feat: support agent skill

This commit is contained in:
fatelei 2026-07-09 15:23:58 +08:00
parent d5e5227c6e
commit eb5d1da0e8
No known key found for this signature in database
GPG Key ID: 2F91DA05646F4EED
91 changed files with 20086 additions and 400 deletions

View File

@ -144,6 +144,7 @@ from .workspace import (
models,
plugin,
rbac,
skills,
snippets,
tool_providers,
trigger_providers,
@ -225,6 +226,7 @@ __all__ = [
"saved_message",
"setup",
"site",
"skills",
"snippet_workflow",
"snippet_workflow_draft_variable",
"snippets",

View File

@ -59,7 +59,7 @@ class TagBindingRemovePayload(BaseModel):
class TagListQueryParam(BaseModel):
type: Literal["knowledge", "app", "snippet", ""] = Field("", description="Tag type filter")
type: Literal["knowledge", "app", "snippet", "skill", ""] = Field("", description="Tag type filter")
keyword: str | None = Field(None, description="Search keyword")

View File

@ -0,0 +1,848 @@
"""Console API for workspace-level Skill Management."""
from __future__ import annotations
import io
from flask import request, send_file
from flask_restx import Resource
from pydantic import BaseModel, ConfigDict, Field, ValidationError
from controllers.common.fields import BinaryFileResponse
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
from controllers.console import console_ns
from controllers.console.wraps import (
account_initialization_required,
edit_permission_required,
setup_required,
with_current_tenant_id,
with_current_user,
)
from core.app.entities.app_invoke_entities import InvokeFrom
from extensions.ext_database import db
from fields.base import ResponseModel
from libs import helper
from libs.helper import dump_response
from libs.login import login_required
from models.account import Account
from models.model import App
from services.app_generate_service import AppGenerateService
from services.skill_management_service import (
SkillAssistMessagePayload,
SkillCreatePayload,
SkillDraftFileOperationPayload,
SkillDraftTreePayload,
SkillImportPayload,
SkillManagementService,
SkillManagementServiceError,
SkillMetadataPayload,
SkillPublishPayload,
SkillRestorePayload,
SkillVersionUpdatePayload,
)
_FILE_UPLOAD_PARAMS = {
"file": {
"description": "Skill draft file payload",
"in": "formData",
"type": "file",
"required": True,
},
}
class WorkspaceSkillsQuery(BaseModel):
keyword: str | None = Field(default=None, description="Search keyword matching skill name or description.")
page: int = Field(default=1, ge=1, le=99999, description="Page number.")
limit: int = Field(default=20, ge=1, le=100, description="Number of items per page.")
tag: list[str] = Field(
default_factory=list,
description="Skill tag filters. Repeat the parameter for multiple tags.",
)
class SkillDeletePayload(BaseModel):
model_config = ConfigDict(extra="forbid")
confirmation_name: str | None = Field(
default=None,
description="Required when deleting a referenced Skill. Must match the Skill name.",
)
class AgentSkillBindingsPayload(BaseModel):
model_config = ConfigDict(extra="forbid")
skill_ids: list[str] = Field(default_factory=list, description="Ordered Skill IDs bound to the Agent.")
class SkillFileQuery(BaseModel):
path: str = Field(description="Skill file path relative to the Skill root.")
version_id: str | None = Field(default=None, description="Optional published version ID. Omit for current draft.")
class SkillResponse(ResponseModel):
id: str
name: str
display_name: str
icon: str
description: str
tags: list[str] = Field(default_factory=list)
name_manually_edited: bool = False
visibility: str
latest_published_version_id: str | None = None
reference_count: int = 0
created_by: str | None = None
created_by_name: str | None = None
updated_by: str | None = None
updated_by_name: str | None = None
created_at: int
updated_at: int
class SkillFileResponse(ResponseModel):
id: str | None = None
path: str
kind: str
storage: str | None = None
mime_type: str | None = None
content: str | None = None
tool_file_id: str | None = None
size: int | None = None
hash: str | None = None
class SkillFilePreviewResponse(ResponseModel):
path: str
mime_type: str
content: str
size: int
hash: str
class SkillFileUploadResponse(ResponseModel):
id: str
name: str
mime_type: str
size: int
hash: str
class SkillDetailResponse(SkillResponse):
files: list[SkillFileResponse] = Field(default_factory=list)
class SkillListResponse(ResponseModel):
data: list[SkillResponse] = Field(default_factory=list)
has_more: bool = False
limit: int = 20
page: int = 1
total: int = 0
class SkillTagResponse(ResponseModel):
tag: str
count: int
class SkillTagListResponse(ResponseModel):
data: list[SkillTagResponse] = Field(default_factory=list)
class SkillVersionResponse(ResponseModel):
id: str
skill_id: str
version_number: int
version_name: str
publish_note: str
hash_code: str
archive_size: int
published_by: str | None = None
published_by_name: str | None = None
is_latest: bool = False
created_at: int
class SkillVersionListResponse(ResponseModel):
data: list[SkillVersionResponse] = Field(default_factory=list)
class SkillVersionDetailResponse(SkillVersionResponse):
files: list[SkillFileResponse] = Field(default_factory=list)
class SkillVersionDeleteResponse(ResponseModel):
id: str
deleted: bool
latest_published_version_id: str | None = None
class SkillReferenceResponse(ResponseModel):
type: str
agent_id: str
agent_icon: str | None = None
agent_icon_background: str | None = None
agent_icon_type: str | None = None
app_id: str | None = None
name: str
display_name: str
workflow_id: str | None = None
workflow_name: str | None = None
workflow_icon: str | None = None
workflow_icon_background: str | None = None
workflow_icon_type: str | None = None
workflow_version: str | None = None
node_id: str | None = None
node_name: str | None = None
class SkillReferenceListResponse(ResponseModel):
data: list[SkillReferenceResponse] = Field(default_factory=list)
class SkillDeleteResponse(ResponseModel):
id: str
deleted: bool
class AgentSkillBindingItemResponse(ResponseModel):
id: str
priority: int
name: str
display_name: str
icon: str
description: str
tags: list[str] = Field(default_factory=list)
status: str
file_count: int
latest_published_version_id: str | None = None
latest_published_at: int | None = None
updated_at: int
class AgentSkillBindingsResponse(ResponseModel):
agent_id: str
skill_ids: list[str] = Field(default_factory=list)
data: list[AgentSkillBindingItemResponse] = Field(default_factory=list)
register_schema_models(
console_ns,
WorkspaceSkillsQuery,
SkillCreatePayload,
SkillAssistMessagePayload,
SkillMetadataPayload,
SkillDraftFileOperationPayload,
SkillDraftTreePayload,
SkillPublishPayload,
SkillRestorePayload,
SkillVersionUpdatePayload,
SkillDeletePayload,
SkillFileQuery,
AgentSkillBindingsPayload,
)
register_response_schema_models(
console_ns,
SkillResponse,
SkillFileResponse,
SkillFilePreviewResponse,
SkillFileUploadResponse,
SkillDetailResponse,
SkillListResponse,
SkillTagResponse,
SkillTagListResponse,
SkillVersionResponse,
SkillVersionListResponse,
SkillVersionDetailResponse,
SkillVersionDeleteResponse,
SkillReferenceResponse,
SkillReferenceListResponse,
SkillDeleteResponse,
AgentSkillBindingItemResponse,
AgentSkillBindingsResponse,
BinaryFileResponse,
)
def _error_response(exc: SkillManagementServiceError) -> tuple[dict[str, str], int]:
body: dict[str, object] = {"code": exc.code, "message": exc.message}
if exc.details:
body["details"] = exc.details
return body, exc.status_code
@console_ns.route("/workspaces/current/skills")
class WorkspaceSkillsApi(Resource):
@console_ns.doc(params=query_params_from_model(WorkspaceSkillsQuery))
@console_ns.response(200, "Workspace skills", console_ns.models[SkillListResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@with_current_tenant_id
def get(self, current_tenant_id: str):
query_input: dict[str, object] = {
"keyword": request.args.get("keyword"),
"tag": request.args.getlist("tag"),
}
if "limit" in request.args:
query_input["limit"] = request.args.get("limit")
if "page" in request.args:
query_input["page"] = request.args.get("page")
query = WorkspaceSkillsQuery.model_validate(query_input)
result = SkillManagementService().list_skills(
tenant_id=current_tenant_id,
keyword=query.keyword,
page=query.page,
limit=query.limit,
tags=[tag for tag in query.tag if tag],
)
return dump_response(SkillListResponse, result)
@console_ns.expect(console_ns.models[SkillCreatePayload.__name__])
@console_ns.response(201, "Skill created", console_ns.models[SkillDetailResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@with_current_user
@with_current_tenant_id
def post(self, current_tenant_id: str, current_user: Account):
try:
payload = SkillCreatePayload.model_validate(console_ns.payload or {})
result = SkillManagementService().create_skill(
tenant_id=current_tenant_id,
user_id=current_user.id,
payload=payload,
)
return dump_response(SkillDetailResponse, result), 201
except ValidationError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except ValueError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.route("/workspaces/current/skills/files/upload")
class WorkspaceSkillFileUploadApi(Resource):
@console_ns.doc(consumes=["multipart/form-data"], params=_FILE_UPLOAD_PARAMS)
@console_ns.response(201, "Skill draft file uploaded", console_ns.models[SkillFileUploadResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@with_current_user
@with_current_tenant_id
def post(self, current_tenant_id: str, current_user: Account):
if "file" not in request.files:
return {"code": "no_file_uploaded", "message": "no file uploaded"}, 400
file = request.files["file"]
if not file.filename:
return {"code": "filename_missing", "message": "filename is required"}, 400
result = SkillManagementService().upload_file(
tenant_id=current_tenant_id,
user_id=current_user.id,
filename=file.filename,
content=file.stream.read(),
mime_type=file.mimetype,
)
return dump_response(SkillFileUploadResponse, result), 201
@console_ns.route("/workspaces/current/skills/tags")
class WorkspaceSkillTagsApi(Resource):
@console_ns.response(200, "Workspace Skill tags", console_ns.models[SkillTagListResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@with_current_tenant_id
def get(self, current_tenant_id: str):
result = SkillManagementService().list_tags(tenant_id=current_tenant_id)
return dump_response(SkillTagListResponse, result)
@console_ns.route("/workspaces/current/skills/import")
class WorkspaceSkillImportApi(Resource):
@console_ns.doc(description="Import a Skill zip package from multipart form field `file`.")
@console_ns.response(201, "Skill imported", console_ns.models[SkillDetailResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@with_current_user
@with_current_tenant_id
def post(self, current_tenant_id: str, current_user: Account):
upload = request.files.get("file")
if upload is None:
return {"code": "invalid_request", "message": "file is required"}, 400
try:
payload = SkillImportPayload(content=upload.read(), filename=upload.filename or "skill.zip")
result = SkillManagementService().import_skill(
tenant_id=current_tenant_id,
user_id=current_user.id,
payload=payload,
)
return dump_response(SkillDetailResponse, result), 201
except (ValidationError, ValueError) as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.route("/workspaces/current/skills/<string:skill_id>")
class WorkspaceSkillApi(Resource):
@console_ns.response(200, "Skill detail", console_ns.models[SkillDetailResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@with_current_tenant_id
def get(self, current_tenant_id: str, skill_id: str):
try:
result = SkillManagementService().get_skill(tenant_id=current_tenant_id, skill_id=skill_id)
return dump_response(SkillDetailResponse, result)
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.expect(console_ns.models[SkillMetadataPayload.__name__])
@console_ns.response(200, "Skill updated", console_ns.models[SkillResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@with_current_user
@with_current_tenant_id
def patch(self, current_tenant_id: str, current_user: Account, skill_id: str):
try:
payload = SkillMetadataPayload.model_validate(console_ns.payload or {})
result = SkillManagementService().update_metadata(
tenant_id=current_tenant_id,
user_id=current_user.id,
skill_id=skill_id,
payload=payload,
)
return dump_response(SkillResponse, result)
except ValidationError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except ValueError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.expect(console_ns.models[SkillDeletePayload.__name__])
@console_ns.response(200, "Skill deleted", console_ns.models[SkillDeleteResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@with_current_tenant_id
def delete(self, current_tenant_id: str, skill_id: str):
try:
payload = SkillDeletePayload.model_validate(console_ns.payload or {})
result = SkillManagementService().delete_skill(
tenant_id=current_tenant_id,
skill_id=skill_id,
confirmation_name=payload.confirmation_name,
)
return dump_response(SkillDeleteResponse, result)
except ValidationError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.route("/workspaces/current/skills/<string:skill_id>/duplicate")
class WorkspaceSkillDuplicateApi(Resource):
@console_ns.response(201, "Skill duplicated", console_ns.models[SkillDetailResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@with_current_user
@with_current_tenant_id
def post(self, current_tenant_id: str, current_user: Account, skill_id: str):
try:
result = SkillManagementService().duplicate_skill(
tenant_id=current_tenant_id,
user_id=current_user.id,
skill_id=skill_id,
)
return dump_response(SkillDetailResponse, result), 201
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.route("/workspaces/current/skills/<string:skill_id>/export")
class WorkspaceSkillExportApi(Resource):
@console_ns.response(200, "Published Skill zip archive")
@setup_required
@login_required
@account_initialization_required
@with_current_tenant_id
def get(self, current_tenant_id: str, skill_id: str):
try:
result = SkillManagementService().pull_published_archive(tenant_id=current_tenant_id, skill_id=skill_id)
return send_file(
io.BytesIO(result.payload),
mimetype=result.mime_type,
as_attachment=True,
download_name=result.filename,
)
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.route("/workspaces/current/skills/<string:skill_id>/assist/messages")
class WorkspaceSkillAssistMessageApi(Resource):
"""Stream read-only Skill Authoring suggestions from the default workspace model."""
@console_ns.expect(console_ns.models[SkillAssistMessagePayload.__name__])
@console_ns.response(200, "Skill Authoring assistant event stream")
@setup_required
@login_required
@account_initialization_required
@with_current_user
@with_current_tenant_id
def post(self, current_tenant_id: str, current_user: Account, skill_id: str):
try:
payload = SkillAssistMessagePayload.model_validate(console_ns.payload or {})
assistant_app, query = SkillManagementService().get_or_create_assistant_app(
tenant_id=current_tenant_id,
skill_id=skill_id,
user_id=current_user.id,
message=payload.message,
attachments=payload.attachments,
model_payload=payload.model,
)
except ValidationError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except SkillManagementServiceError as exc:
return _error_response(exc)
app_model = db.session().get(App, assistant_app.id)
if app_model is None:
return {"code": "skill_assistant_unavailable", "message": "Skill Authoring Agent is unavailable"}, 503
response = AppGenerateService.generate(
session=db.session(),
app_model=app_model,
user=current_user,
args={"inputs": {}, "query": query, "auto_generate_name": False},
invoke_from=InvokeFrom.DEBUGGER,
streaming=True,
)
return helper.compact_generate_response(response)
@console_ns.route("/workspaces/current/skills/<string:skill_id>/files")
class WorkspaceSkillFilesApi(Resource):
@console_ns.expect(console_ns.models[SkillDraftFileOperationPayload.__name__])
@console_ns.response(200, "Draft file operation applied", console_ns.models[SkillDetailResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@with_current_user
@with_current_tenant_id
def patch(self, current_tenant_id: str, current_user: Account, skill_id: str):
try:
payload = SkillDraftFileOperationPayload.model_validate(console_ns.payload or {})
result = SkillManagementService().apply_draft_file_operation(
tenant_id=current_tenant_id,
user_id=current_user.id,
skill_id=skill_id,
payload=payload,
)
return dump_response(SkillDetailResponse, result)
except ValidationError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except ValueError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.expect(console_ns.models[SkillDraftTreePayload.__name__])
@console_ns.response(200, "Draft files replaced", console_ns.models[SkillDetailResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@with_current_user
@with_current_tenant_id
def put(self, current_tenant_id: str, current_user: Account, skill_id: str):
try:
payload = SkillDraftTreePayload.model_validate(console_ns.payload or {})
result = SkillManagementService().replace_draft_tree(
tenant_id=current_tenant_id,
user_id=current_user.id,
skill_id=skill_id,
payload=payload,
)
return dump_response(SkillDetailResponse, result)
except ValidationError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except ValueError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.route("/workspaces/current/skills/<string:skill_id>/files/preview")
class WorkspaceSkillFilePreviewApi(Resource):
@console_ns.doc(params=query_params_from_model(SkillFileQuery))
@console_ns.response(200, "Skill file text preview", console_ns.models[SkillFilePreviewResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@with_current_tenant_id
def get(self, current_tenant_id: str, skill_id: str):
try:
query = SkillFileQuery.model_validate(
{
"path": request.args.get("path"),
"version_id": request.args.get("version_id"),
}
)
result = SkillManagementService().preview_file(
tenant_id=current_tenant_id,
skill_id=skill_id,
path=query.path,
version_id=query.version_id,
)
return dump_response(SkillFilePreviewResponse, result)
except ValidationError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except ValueError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.route("/workspaces/current/skills/<string:skill_id>/files/content")
class WorkspaceSkillFileContentApi(Resource):
@console_ns.doc(params={**query_params_from_model(SkillFileQuery), "download": "Return as an attachment when 1."})
@console_ns.response(200, "Skill file content", console_ns.models[BinaryFileResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@with_current_tenant_id
def get(self, current_tenant_id: str, skill_id: str):
try:
query = SkillFileQuery.model_validate(
{
"path": request.args.get("path"),
"version_id": request.args.get("version_id"),
}
)
result = SkillManagementService().pull_file(
tenant_id=current_tenant_id,
skill_id=skill_id,
path=query.path,
version_id=query.version_id,
)
return send_file(
io.BytesIO(result.payload),
mimetype=result.mime_type,
as_attachment=request.args.get("download") == "1",
download_name=result.filename,
)
except ValidationError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except ValueError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.route("/workspaces/current/skills/<string:skill_id>/publish")
class WorkspaceSkillPublishApi(Resource):
@console_ns.expect(console_ns.models[SkillPublishPayload.__name__])
@console_ns.response(200, "Skill published", console_ns.models[SkillVersionResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@with_current_user
@with_current_tenant_id
def post(self, current_tenant_id: str, current_user: Account, skill_id: str):
try:
payload = SkillPublishPayload.model_validate(console_ns.payload or {})
result = SkillManagementService().publish_skill(
tenant_id=current_tenant_id,
user_id=current_user.id,
skill_id=skill_id,
payload=payload,
)
return dump_response(SkillVersionResponse, result)
except ValidationError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.route("/workspaces/current/skills/<string:skill_id>/restore")
class WorkspaceSkillRestoreApi(Resource):
@console_ns.expect(console_ns.models[SkillRestorePayload.__name__])
@console_ns.response(200, "Skill version restored", console_ns.models[SkillVersionResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@with_current_user
@with_current_tenant_id
def post(self, current_tenant_id: str, current_user: Account, skill_id: str):
try:
payload = SkillRestorePayload.model_validate(console_ns.payload or {})
result = SkillManagementService().restore_version(
tenant_id=current_tenant_id,
user_id=current_user.id,
skill_id=skill_id,
payload=payload,
)
return dump_response(SkillVersionResponse, result)
except ValidationError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.route("/workspaces/current/skills/<string:skill_id>/references")
class WorkspaceSkillReferencesApi(Resource):
@console_ns.response(200, "Skill references", console_ns.models[SkillReferenceListResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@with_current_tenant_id
def get(self, current_tenant_id: str, skill_id: str):
try:
result = SkillManagementService().list_skill_references(tenant_id=current_tenant_id, skill_id=skill_id)
return dump_response(SkillReferenceListResponse, result)
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.route("/workspaces/current/skills/<string:skill_id>/versions")
class WorkspaceSkillVersionsApi(Resource):
@console_ns.response(200, "Skill versions", console_ns.models[SkillVersionListResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@with_current_tenant_id
def get(self, current_tenant_id: str, skill_id: str):
try:
result = SkillManagementService().list_versions(tenant_id=current_tenant_id, skill_id=skill_id)
return dump_response(SkillVersionListResponse, result)
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.route("/workspaces/current/skills/<string:skill_id>/versions/<string:version_id>")
class WorkspaceSkillVersionApi(Resource):
@console_ns.response(200, "Skill version detail", console_ns.models[SkillVersionDetailResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@with_current_tenant_id
def get(self, current_tenant_id: str, skill_id: str, version_id: str):
try:
result = SkillManagementService().get_version(
tenant_id=current_tenant_id,
skill_id=skill_id,
version_id=version_id,
)
return dump_response(SkillVersionDetailResponse, result)
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.expect(console_ns.models[SkillVersionUpdatePayload.__name__])
@console_ns.response(200, "Skill version updated", console_ns.models[SkillVersionResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@with_current_tenant_id
def patch(self, current_tenant_id: str, skill_id: str, version_id: str):
try:
payload = SkillVersionUpdatePayload.model_validate(console_ns.payload or {})
result = SkillManagementService().update_version(
tenant_id=current_tenant_id,
skill_id=skill_id,
version_id=version_id,
payload=payload,
)
return dump_response(SkillVersionResponse, result)
except ValidationError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.response(200, "Skill version deleted", console_ns.models[SkillVersionDeleteResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@with_current_user
@with_current_tenant_id
def delete(self, current_tenant_id: str, current_user: Account, skill_id: str, version_id: str):
try:
result = SkillManagementService().delete_version(
tenant_id=current_tenant_id,
user_id=current_user.id,
skill_id=skill_id,
version_id=version_id,
)
return dump_response(SkillVersionDeleteResponse, result)
except SkillManagementServiceError as exc:
return _error_response(exc)
@console_ns.route("/workspaces/current/agents/<string:agent_id>/skills")
class WorkspaceAgentSkillBindingsApi(Resource):
@console_ns.response(200, "Agent Skill bindings", console_ns.models[AgentSkillBindingsResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@with_current_tenant_id
def get(self, current_tenant_id: str, agent_id: str):
result = SkillManagementService().list_agent_bindings(tenant_id=current_tenant_id, agent_id=agent_id)
return dump_response(AgentSkillBindingsResponse, result)
@console_ns.expect(console_ns.models[AgentSkillBindingsPayload.__name__])
@console_ns.response(200, "Agent Skill bindings replaced", console_ns.models[AgentSkillBindingsResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@with_current_user
@with_current_tenant_id
def put(self, current_tenant_id: str, current_user: Account, agent_id: str):
try:
payload = AgentSkillBindingsPayload.model_validate(console_ns.payload or {})
result = SkillManagementService().replace_agent_bindings(
tenant_id=current_tenant_id,
user_id=current_user.id,
agent_id=agent_id,
skill_ids=payload.skill_ids,
)
return dump_response(AgentSkillBindingsResponse, result)
except ValidationError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except SkillManagementServiceError as exc:
return _error_response(exc)
__all__ = [
"WorkspaceAgentSkillBindingsApi",
"WorkspaceSkillApi",
"WorkspaceSkillDuplicateApi",
"WorkspaceSkillExportApi",
"WorkspaceSkillFilesApi",
"WorkspaceSkillImportApi",
"WorkspaceSkillPublishApi",
"WorkspaceSkillReferencesApi",
"WorkspaceSkillRestoreApi",
"WorkspaceSkillTagsApi",
"WorkspaceSkillVersionApi",
"WorkspaceSkillVersionsApi",
"WorkspaceSkillsApi",
]

View File

@ -23,6 +23,7 @@ from .knowledge import retrieval as _knowledge_retrieval
from .plugin import agent_config as _agent_config
from .plugin import agent_drive as _agent_drive
from .plugin import plugin as _plugin
from .plugin import skills as _skills
from .workspace import workspace as _workspace
api.add_namespace(inner_api_ns)
@ -36,6 +37,7 @@ __all__ = [
"_mail",
"_plugin",
"_runtime_credentials",
"_skills",
"_workspace",
"api",
"bp",

View File

@ -0,0 +1,54 @@
"""Inner API for published workspace Skills.
These endpoints are called by trusted runtime services. They expose only
published Skill artifacts, never draft files or editable metadata.
"""
from __future__ import annotations
import io
from flask import request, send_file
from flask_restx import Resource
from pydantic import BaseModel, ValidationError
from controllers.console.wraps import setup_required
from controllers.inner_api import inner_api_ns
from controllers.inner_api.wraps import plugin_inner_api_only
from services.skill_management_service import SkillManagementService, SkillManagementServiceError
class _SkillTargetQuery(BaseModel):
tenant_id: str
def _target_query_from_request() -> _SkillTargetQuery:
return _SkillTargetQuery.model_validate({"tenant_id": request.args.get("tenant_id")})
def _error_response(exc: SkillManagementServiceError) -> tuple[dict[str, str], int]:
return {"code": exc.code, "message": exc.message}, exc.status_code
@inner_api_ns.route("/skills/<string:skill_id>/pull")
class PublishedSkillPullApi(Resource):
@setup_required
@plugin_inner_api_only
@inner_api_ns.doc("published_skill_pull")
def get(self, skill_id: str):
try:
query = _target_query_from_request()
result = SkillManagementService().pull_published_archive(tenant_id=query.tenant_id, skill_id=skill_id)
return send_file(
io.BytesIO(result.payload),
mimetype=result.mime_type,
as_attachment=True,
download_name=result.filename,
)
except ValidationError as exc:
return {"code": "invalid_request", "message": str(exc)}, 400
except SkillManagementServiceError as exc:
return _error_response(exc)
__all__ = ["PublishedSkillPullApi"]

View File

@ -0,0 +1,115 @@
"""add workspace skill management
Revision ID: a4f8d2c9e1b0
Revises: c3d4e5f6a7b8
Create Date: 2026-07-09 12:00:00.000000
"""
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import mysql
from models.types import StringUUID
# revision identifiers, used by Alembic.
revision = "a4f8d2c9e1b0"
down_revision = "c3d4e5f6a7b8"
branch_labels = None
depends_on = None
def _uuid_column(name: str, *, nullable: bool = False) -> sa.Column:
return sa.Column(name, StringUUID(), nullable=nullable)
def _long_text() -> sa.types.TypeEngine:
return sa.Text().with_variant(mysql.LONGTEXT(), "mysql")
def upgrade() -> None:
op.create_table(
"skills",
_uuid_column("id"),
_uuid_column("tenant_id"),
sa.Column("name", sa.String(length=64), nullable=False),
sa.Column("display_name", sa.String(length=128), nullable=False),
sa.Column("icon", sa.String(length=16), nullable=False, server_default="📄"),
sa.Column("description", sa.String(length=1024), nullable=False, server_default=""),
sa.Column("tags", _long_text(), nullable=False, server_default="[]"),
sa.Column("name_manually_edited", sa.Boolean(), nullable=False, server_default=sa.false()),
sa.Column("visibility", sa.String(length=32), nullable=False, server_default="workspace"),
_uuid_column("latest_published_version_id", nullable=True),
_uuid_column("created_by", nullable=True),
_uuid_column("updated_by", nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()),
sa.Column("updated_at", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()),
sa.PrimaryKeyConstraint("id", name="skill_pkey"),
sa.UniqueConstraint("tenant_id", "name", name="skill_tenant_name_unique"),
)
op.create_index("skills_tenant_updated_at_idx", "skills", ["tenant_id", "updated_at"])
op.create_table(
"skill_draft_files",
_uuid_column("id"),
_uuid_column("skill_id"),
sa.Column("path", sa.String(length=512), nullable=False),
sa.Column("kind", sa.String(length=32), nullable=False),
sa.Column("storage", sa.String(length=32), nullable=True),
sa.Column("mime_type", sa.String(length=255), nullable=True),
sa.Column("content_text", _long_text(), nullable=True),
_uuid_column("tool_file_id", nullable=True),
sa.Column("size", sa.BigInteger(), nullable=True),
sa.Column("hash", sa.String(length=255), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()),
sa.Column("updated_at", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()),
sa.PrimaryKeyConstraint("id", name="skill_draft_file_pkey"),
sa.UniqueConstraint("skill_id", "path", name="skill_draft_file_skill_path_unique"),
)
op.create_index("skill_draft_files_skill_path_idx", "skill_draft_files", ["skill_id", "path"])
op.create_table(
"skill_versions",
_uuid_column("id"),
_uuid_column("skill_id"),
sa.Column("version_number", sa.Integer(), nullable=False),
sa.Column("version_name", sa.String(length=128), nullable=False, server_default=""),
sa.Column("publish_note", sa.String(length=1024), nullable=False, server_default=""),
sa.Column("manifest", _long_text(), nullable=False),
_uuid_column("archive_tool_file_id"),
sa.Column("hash_code", sa.String(length=255), nullable=False),
sa.Column("archive_size", sa.BigInteger(), nullable=False),
_uuid_column("published_by", nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()),
sa.Column("updated_at", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()),
sa.PrimaryKeyConstraint("id", name="skill_version_pkey"),
sa.UniqueConstraint("skill_id", "version_number", name="skill_version_skill_number_unique"),
)
op.create_index("skill_versions_skill_created_at_idx", "skill_versions", ["skill_id", "created_at"])
op.create_table(
"agent_skill_bindings",
_uuid_column("id"),
_uuid_column("tenant_id"),
_uuid_column("agent_id"),
_uuid_column("skill_id"),
sa.Column("priority", sa.Integer(), nullable=False),
_uuid_column("created_by", nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()),
sa.Column("updated_at", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()),
sa.PrimaryKeyConstraint("id", name="agent_skill_binding_pkey"),
sa.UniqueConstraint("tenant_id", "agent_id", "skill_id", name="agent_skill_binding_unique"),
sa.UniqueConstraint("tenant_id", "agent_id", "priority", name="agent_skill_binding_priority_unique"),
)
op.create_index("agent_skill_bindings_skill_idx", "agent_skill_bindings", ["tenant_id", "skill_id"])
def downgrade() -> None:
op.drop_index("agent_skill_bindings_skill_idx", table_name="agent_skill_bindings")
op.drop_table("agent_skill_bindings")
op.drop_index("skill_versions_skill_created_at_idx", table_name="skill_versions")
op.drop_table("skill_versions")
op.drop_index("skill_draft_files_skill_path_idx", table_name="skill_draft_files")
op.drop_table("skill_draft_files")
op.drop_index("skills_tenant_updated_at_idx", table_name="skills")
op.drop_table("skills")

View File

@ -113,6 +113,7 @@ from .provider import (
TenantDefaultModel,
TenantPreferredModelProvider,
)
from .skill import AgentSkillBinding, Skill, SkillDraftFile, SkillFileKind, SkillFileStorage, SkillVersion
from .snippet import CustomizedSnippet, SnippetType
from .source import DataSourceApiKeyAuthBinding, DataSourceOauthBinding
from .task import CeleryTask, CeleryTaskSet
@ -173,6 +174,7 @@ __all__ = [
"AgentRuntimeSessionOwnerType",
"AgentRuntimeSessionStatus",
"AgentScope",
"AgentSkillBinding",
"AgentSource",
"AgentStatus",
"ApiRequest",
@ -246,6 +248,11 @@ __all__ = [
"RecommendedApp",
"SavedMessage",
"Site",
"Skill",
"SkillDraftFile",
"SkillFileKind",
"SkillFileStorage",
"SkillVersion",
"SnippetType",
"Tag",
"TagBinding",

View File

@ -249,6 +249,7 @@ class TagType(StrEnum):
KNOWLEDGE = "knowledge"
APP = "app"
SNIPPET = "snippet"
SKILL = "skill"
class DatasetMetadataType(StrEnum):

View File

@ -2646,7 +2646,7 @@ class Tag(TypeBase):
sa.Index("tag_name_idx", "name"),
)
TAG_TYPE_LIST = ["knowledge", "app", "snippet"]
TAG_TYPE_LIST = ["knowledge", "app", "snippet", "skill"]
id: Mapped[str] = mapped_column(
StringUUID, insert_default=lambda: str(uuid4()), default_factory=lambda: str(uuid4()), init=False

165
api/models/skill.py Normal file
View File

@ -0,0 +1,165 @@
"""Workspace-level Skill Management models.
These tables are the source of truth for reusable workspace Skills. Agent Soul
``config_skills`` and Agent Drive skill rows remain per-agent runtime/config
assets; they may consume a published Skill snapshot but do not own the Skill's
draft, metadata, version history, or Agent binding priority.
"""
from enum import StrEnum
import sqlalchemy as sa
from pydantic import BaseModel, ConfigDict
from sqlalchemy import Index, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column
from models.base import Base, DefaultFieldsMixin
from models.types import EnumText, JSONModelColumn, LongText, StringUUID
class SkillFileKind(StrEnum):
"""Draft file entry kind."""
FILE = "file"
DIRECTORY = "directory"
class SkillFileStorage(StrEnum):
"""How a draft file's content is stored."""
TEXT = "text"
TOOL_FILE = "tool_file"
class SkillVersionManifestFile(BaseModel):
"""One file entry captured in a published Skill snapshot manifest."""
path: str
mime_type: str | None = None
size: int
hash: str
model_config = ConfigDict(extra="forbid")
class SkillVersionManifest(BaseModel):
"""Published Skill snapshot file index."""
files: list[SkillVersionManifestFile]
model_config = ConfigDict(extra="forbid")
class Skill(DefaultFieldsMixin, Base):
"""Workspace-level reusable Skill metadata and draft status."""
__tablename__ = "skills"
__table_args__ = (
sa.PrimaryKeyConstraint("id", name="skill_pkey"),
UniqueConstraint("tenant_id", "name", name="skill_tenant_name_unique"),
Index("skills_tenant_updated_at_idx", "tenant_id", "updated_at"),
)
tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
name: Mapped[str] = mapped_column(sa.String(64), nullable=False)
display_name: Mapped[str] = mapped_column(sa.String(128), nullable=False)
icon: Mapped[str] = mapped_column(sa.String(16), nullable=False, default="📄", server_default="📄")
description: Mapped[str] = mapped_column(sa.String(1024), nullable=False, default="", server_default="")
tags: Mapped[str] = mapped_column(LongText, nullable=False, default="[]", server_default="[]")
name_manually_edited: Mapped[bool] = mapped_column(
sa.Boolean,
nullable=False,
default=False,
server_default=sa.false(),
)
visibility: Mapped[str] = mapped_column(
sa.String(32),
nullable=False,
default="workspace",
server_default="workspace",
)
latest_published_version_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True)
created_by: Mapped[str | None] = mapped_column(StringUUID, nullable=True)
updated_by: Mapped[str | None] = mapped_column(StringUUID, nullable=True)
class SkillDraftFile(DefaultFieldsMixin, Base):
"""One draft file or directory in a workspace Skill."""
__tablename__ = "skill_draft_files"
__table_args__ = (
sa.PrimaryKeyConstraint("id", name="skill_draft_file_pkey"),
UniqueConstraint("skill_id", "path", name="skill_draft_file_skill_path_unique"),
Index("skill_draft_files_skill_path_idx", "skill_id", "path"),
)
skill_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
path: Mapped[str] = mapped_column(sa.String(512), nullable=False)
kind: Mapped[SkillFileKind] = mapped_column(EnumText(SkillFileKind, length=32), nullable=False)
storage: Mapped[SkillFileStorage | None] = mapped_column(EnumText(SkillFileStorage, length=32), nullable=True)
mime_type: Mapped[str | None] = mapped_column(sa.String(255), nullable=True)
content_text: Mapped[str | None] = mapped_column(LongText, nullable=True)
tool_file_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True)
size: Mapped[int | None] = mapped_column(sa.BigInteger, nullable=True)
hash: Mapped[str | None] = mapped_column(sa.String(255), nullable=True)
class SkillVersion(DefaultFieldsMixin, Base):
"""Immutable published Skill snapshot.
``hash_code`` uniquely identifies a published version for downstream
execution audit. It includes Skill identity, version number, and archive
content digest instead of being only the archive content hash.
"""
__tablename__ = "skill_versions"
__table_args__ = (
sa.PrimaryKeyConstraint("id", name="skill_version_pkey"),
UniqueConstraint("skill_id", "version_number", name="skill_version_skill_number_unique"),
Index("skill_versions_skill_created_at_idx", "skill_id", "created_at"),
)
skill_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
version_number: Mapped[int] = mapped_column(sa.Integer, nullable=False)
version_name: Mapped[str] = mapped_column(sa.String(128), nullable=False, default="", server_default="")
publish_note: Mapped[str] = mapped_column(sa.String(1024), nullable=False, default="", server_default="")
manifest: Mapped[SkillVersionManifest] = mapped_column(JSONModelColumn(SkillVersionManifest), nullable=False)
archive_tool_file_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
hash_code: Mapped[str] = mapped_column(sa.String(255), nullable=False)
archive_size: Mapped[int] = mapped_column(sa.BigInteger, nullable=False)
published_by: Mapped[str | None] = mapped_column(StringUUID, nullable=True)
class AgentSkillBinding(DefaultFieldsMixin, Base):
"""Direct Agent-to-workspace-Skill binding.
``priority`` is retained as an internal ordering column for the current
schema constraints. Runtime Skill selection is Agent-driven and must not
treat it as a matching priority.
"""
__tablename__ = "agent_skill_bindings"
__table_args__ = (
sa.PrimaryKeyConstraint("id", name="agent_skill_binding_pkey"),
UniqueConstraint("tenant_id", "agent_id", "skill_id", name="agent_skill_binding_unique"),
UniqueConstraint("tenant_id", "agent_id", "priority", name="agent_skill_binding_priority_unique"),
Index("agent_skill_bindings_skill_idx", "tenant_id", "skill_id"),
)
tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
agent_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
skill_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
priority: Mapped[int] = mapped_column(sa.Integer, nullable=False)
created_by: Mapped[str | None] = mapped_column(StringUUID, nullable=True)
__all__ = [
"AgentSkillBinding",
"Skill",
"SkillDraftFile",
"SkillFileKind",
"SkillFileStorage",
"SkillVersion",
"SkillVersionManifest",
"SkillVersionManifestFile",
]

View File

@ -46,6 +46,7 @@ from models.tools import ToolFile
from services.agent.config_skill_normalize_service import ConfigSkillNormalizeService
from services.agent.skill_package_service import SkillPackageError
from services.agent_drive_service import DriveFileRef
from services.skill_management_service import SkillManagementService, SkillManagementServiceError
class AgentConfigVersionKind(StrEnum):
@ -98,6 +99,7 @@ class ConfigPushPayload(BaseModel):
@dataclass(slots=True)
class AgentConfigTarget:
tenant_id: str
agent_id: str
version_id: str
kind: AgentConfigVersionKind
@ -146,6 +148,7 @@ class AgentConfigService:
user_id=user_id,
)
return AgentConfigTarget(
tenant_id=tenant_id,
agent_id=target.agent_id,
version_id=target.version_id,
kind=target.kind,
@ -191,7 +194,7 @@ class AgentConfigService:
return {
"agent_id": target.agent_id,
"config_version": self._config_version_payload(target),
"items": [self._serialize_skill_item(skill) for skill in target.agent_soul.config_skills],
"items": self._skill_items_for_target(target),
}
def list_files(
@ -233,10 +236,27 @@ class AgentConfigService:
config_version_kind=config_version_kind,
user_id=user_id,
)
skill = self._require_skill(target.agent_soul, name=name)
file_id = self._available_skill_file_id(skill)
payload, mime_type = self._load_tool_file_bytes(tenant_id=tenant_id, file_id=file_id)
return ConfigDownload(filename=f"{skill.name}.zip", mime_type=mime_type or "application/zip", payload=payload)
try:
skill = self._require_skill(target.agent_soul, name=name)
file_id = self._available_skill_file_id(skill)
payload, mime_type = self._load_tool_file_bytes(tenant_id=tenant_id, file_id=file_id)
return ConfigDownload(
filename=f"{skill.name}.zip",
mime_type=mime_type or "application/zip",
payload=payload,
)
except AgentConfigServiceError as exc:
if exc.code != "config_skill_not_found":
raise
try:
result = SkillManagementService().pull_runtime_agent_skill(
tenant_id=tenant_id,
agent_id=agent_id,
name=name,
)
return ConfigDownload(filename=result.filename, mime_type=result.mime_type, payload=result.payload)
except SkillManagementServiceError as exc:
raise AgentConfigServiceError("config_skill_not_found", "config skill not found", status_code=404) from exc
def download_skill_url(
self,
@ -279,9 +299,45 @@ class AgentConfigService:
config_version_kind=config_version_kind,
user_id=user_id,
)
skill = self._require_skill(target.agent_soul, name=name)
file_id = self._available_skill_file_id(skill)
archive_bytes, _mime_type = self._load_tool_file_bytes(tenant_id=tenant_id, file_id=file_id)
try:
skill = self._require_skill(target.agent_soul, name=name)
file_id = self._available_skill_file_id(skill)
archive_bytes, _mime_type = self._load_tool_file_bytes(tenant_id=tenant_id, file_id=file_id)
skill_item = self._serialize_skill_item(skill)
except AgentConfigServiceError as exc:
if exc.code != "config_skill_not_found":
raise
try:
workspace_archive = SkillManagementService().pull_runtime_agent_skill(
tenant_id=tenant_id,
agent_id=agent_id,
name=name,
)
except SkillManagementServiceError as skill_exc:
raise AgentConfigServiceError(
"config_skill_not_found",
"config skill not found",
status_code=404,
) from skill_exc
archive_bytes = workspace_archive.payload
skill_item = next(
(
item
for item in SkillManagementService().list_runtime_agent_skills(
tenant_id=tenant_id,
agent_id=agent_id,
)
if item["name"] == name
),
{
"id": name,
"name": name,
"description": "",
"size": None,
"hash": None,
"mime_type": "application/zip",
},
)
try:
archive_items, skill_md = self._inspect_skill_archive(archive_bytes)
except (OSError, ValueError, zipfile.BadZipFile) as exc:
@ -291,7 +347,7 @@ class AgentConfigService:
status_code=500,
) from exc
return {
**self._serialize_skill_item(skill),
**skill_item,
"source": "config_skill_zip",
"files": archive_items,
"skill_md": skill_md,
@ -839,6 +895,7 @@ class AgentConfigService:
status_code=404,
)
return AgentConfigTarget(
tenant_id=tenant_id,
agent_id=agent_id,
version_id=version.id,
kind=config_version_kind,
@ -1133,9 +1190,7 @@ class AgentConfigService:
return {
"agent_id": target.agent_id,
"config_version": AgentConfigService._config_version_payload(target),
"skills": {
"items": [AgentConfigService._serialize_skill_item(skill) for skill in target.agent_soul.config_skills]
},
"skills": {"items": AgentConfigService._skill_items_for_target(target)},
"files": {
"items": [
AgentConfigService._serialize_file_item(file_ref) for file_ref in target.agent_soul.config_files
@ -1145,6 +1200,20 @@ class AgentConfigService:
"note": target.agent_soul.config_note,
}
@staticmethod
def _skill_items_for_target(target: AgentConfigTarget) -> list[dict[str, object]]:
items = [AgentConfigService._serialize_skill_item(skill) for skill in target.agent_soul.config_skills]
seen_names = {str(item["name"]) for item in items}
for item in SkillManagementService().list_runtime_agent_skills(
tenant_id=target.tenant_id,
agent_id=target.agent_id,
):
if item["name"] in seen_names:
continue
seen_names.add(str(item["name"]))
items.append(item)
return items
@staticmethod
def _config_version_payload(target: AgentConfigTarget) -> dict[str, object]:
return {

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@ from werkzeug.exceptions import NotFound
from models.dataset import Dataset
from models.enums import TagType
from models.model import App, Tag, TagBinding
from models.skill import Skill
from models.snippet import CustomizedSnippet
type _TagTypeLike = TagType | str
@ -282,5 +283,13 @@ class TagService:
)
if not snippet:
raise NotFound("Snippet not found")
elif type == "skill":
skill = session.scalar(
select(Skill)
.where(Skill.tenant_id == current_user.current_tenant_id, Skill.id == target_id)
.limit(1)
)
if not skill:
raise NotFound("Skill not found")
else:
raise NotFound("Invalid binding type")

View File

@ -0,0 +1,394 @@
from __future__ import annotations
from inspect import unwrap
from unittest.mock import MagicMock, PropertyMock, patch
import pytest
from flask import Flask
from controllers.console import console_ns
from controllers.console.workspace.skills import (
WorkspaceAgentSkillBindingsApi,
WorkspaceSkillAssistMessageApi,
WorkspaceSkillFilesApi,
WorkspaceSkillsApi,
WorkspaceSkillTagsApi,
WorkspaceSkillVersionApi,
)
from models.account import Account
from services.skill_management_service import SkillAssistAttachmentPayload, SkillManagementServiceError
@pytest.fixture
def app() -> Flask:
flask_app = Flask("test_workspace_skills")
flask_app.config["TESTING"] = True
return flask_app
@pytest.fixture
def current_user() -> Account:
user = Account(name="Test User", email="test@example.com")
user.id = "user-1"
return user
def _skill_detail() -> dict:
return {
"id": "skill-1",
"name": "finance-sop",
"display_name": "Finance SOP",
"icon": "📄",
"description": "",
"tags": [],
"name_manually_edited": False,
"visibility": "workspace",
"latest_published_version_id": None,
"reference_count": 0,
"created_by": "user-1",
"created_by_name": "Test User",
"updated_by": "user-1",
"updated_by_name": "Test User",
"created_at": 1,
"updated_at": 1,
"files": [
{
"id": "file-1",
"path": "SKILL.md",
"kind": "file",
"storage": "text",
"mime_type": "text/markdown",
"content": "---\nname: finance-sop\n---\n# Body",
"tool_file_id": None,
"size": 32,
"hash": "hash",
}
],
}
def test_create_skill_validates_payload_and_returns_detail(app: Flask, current_user: Account) -> None:
api = WorkspaceSkillsApi()
method = unwrap(api.post)
service = MagicMock()
service.create_skill.return_value = _skill_detail()
with (
app.test_request_context("/", method="POST"),
patch.object(type(console_ns), "payload", new_callable=PropertyMock, return_value={}),
patch("controllers.console.workspace.skills.SkillManagementService", return_value=service),
):
payload, status = method(api, "tenant-1", current_user)
assert status == 201
assert payload["id"] == "skill-1"
assert payload["files"][0]["path"] == "SKILL.md"
service.create_skill.assert_called_once()
assert service.create_skill.call_args.kwargs["tenant_id"] == "tenant-1"
assert service.create_skill.call_args.kwargs["user_id"] == "user-1"
def test_list_skills_uses_default_pagination_when_query_omits_page_and_limit(app: Flask) -> None:
api = WorkspaceSkillsApi()
method = unwrap(api.get)
service = MagicMock()
service.list_skills.return_value = {
"data": [],
"has_more": False,
"limit": 20,
"page": 1,
"total": 0,
}
with (
app.test_request_context("/?keyword=finance&tag=ops&tag=", method="GET"),
patch("controllers.console.workspace.skills.SkillManagementService", return_value=service),
):
payload = method(api, "tenant-1")
assert payload == {
"data": [],
"has_more": False,
"limit": 20,
"page": 1,
"total": 0,
}
service.list_skills.assert_called_once_with(
tenant_id="tenant-1",
keyword="finance",
page=1,
limit=20,
tags=["ops"],
)
def test_get_agent_skill_bindings_returns_card_data(app: Flask) -> None:
api = WorkspaceAgentSkillBindingsApi()
method = unwrap(api.get)
service = MagicMock()
service.list_agent_bindings.return_value = {
"agent_id": "agent-1",
"skill_ids": ["skill-1"],
"data": [
{
"id": "skill-1",
"priority": 0,
"name": "finance-sop",
"display_name": "Finance SOP",
"icon": "📄",
"description": "Handle finance.",
"tags": ["Finance"],
"status": "published",
"file_count": 2,
"latest_published_version_id": "version-1",
"latest_published_at": 123,
"updated_at": 124,
}
],
}
with (
app.test_request_context("/", method="GET"),
patch("controllers.console.workspace.skills.SkillManagementService", return_value=service),
):
payload = method(api, "tenant-1", "agent-1")
assert payload["skill_ids"] == ["skill-1"]
assert payload["data"][0]["display_name"] == "Finance SOP"
assert payload["data"][0]["file_count"] == 2
service.list_agent_bindings.assert_called_once_with(tenant_id="tenant-1", agent_id="agent-1")
def test_patch_skill_file_operation_validates_payload_and_returns_detail(app: Flask, current_user: Account) -> None:
api = WorkspaceSkillFilesApi()
method = unwrap(api.patch)
service = MagicMock()
service.apply_draft_file_operation.return_value = _skill_detail()
request_payload = {
"operation": "upsert_text",
"path": "references/policy.md",
"content": "Policy",
}
with (
app.test_request_context("/", method="PATCH"),
patch.object(type(console_ns), "payload", new_callable=PropertyMock, return_value=request_payload),
patch("controllers.console.workspace.skills.SkillManagementService", return_value=service),
):
payload = method(api, "tenant-1", current_user, "skill-1")
assert payload["id"] == "skill-1"
service.apply_draft_file_operation.assert_called_once()
call = service.apply_draft_file_operation.call_args.kwargs
assert call["tenant_id"] == "tenant-1"
assert call["user_id"] == "user-1"
assert call["skill_id"] == "skill-1"
assert call["payload"].operation == "upsert_text"
def test_patch_skill_file_operation_returns_error_details(app: Flask, current_user: Account) -> None:
api = WorkspaceSkillFilesApi()
method = unwrap(api.patch)
service = MagicMock()
service.apply_draft_file_operation.side_effect = SkillManagementServiceError(
"missing_skill_name",
"SKILL.md frontmatter name is required",
details={"path": "SKILL.md", "field": "name", "line": 2},
)
with (
app.test_request_context("/", method="PATCH"),
patch.object(
type(console_ns),
"payload",
new_callable=PropertyMock,
return_value={"operation": "delete", "path": "SKILL.md"},
),
patch("controllers.console.workspace.skills.SkillManagementService", return_value=service),
):
payload, status = method(api, "tenant-1", current_user, "skill-1")
assert status == 400
assert payload == {
"code": "missing_skill_name",
"message": "SKILL.md frontmatter name is required",
"details": {"path": "SKILL.md", "field": "name", "line": 2},
}
def test_list_skill_tags_returns_filter_options(app: Flask) -> None:
api = WorkspaceSkillTagsApi()
method = unwrap(api.get)
service = MagicMock()
service.list_tags.return_value = {"data": [{"tag": "finance", "count": 2}]}
with (
app.test_request_context("/", method="GET"),
patch("controllers.console.workspace.skills.SkillManagementService", return_value=service),
):
payload = method(api, "tenant-1")
assert payload == {"data": [{"tag": "finance", "count": 2}]}
service.list_tags.assert_called_once_with(tenant_id="tenant-1")
def test_get_skill_version_returns_version_detail(app: Flask) -> None:
api = WorkspaceSkillVersionApi()
method = unwrap(api.get)
service = MagicMock()
service.get_version.return_value = {
"id": "version-1",
"skill_id": "skill-1",
"version_number": 1,
"version_name": "Initial finance policy",
"publish_note": "Initial finance policy",
"hash_code": "hash-code",
"archive_size": 123,
"published_by": "user-1",
"published_by_name": "Li Wei",
"is_latest": True,
"created_at": 1,
"files": [
{
"id": None,
"path": "SKILL.md",
"kind": "file",
"storage": "text",
"mime_type": "text/markdown",
"content": "# Version",
"tool_file_id": None,
"size": 9,
"hash": "file-hash",
}
],
}
with (
app.test_request_context("/", method="GET"),
patch("controllers.console.workspace.skills.SkillManagementService", return_value=service),
):
payload = method(api, "tenant-1", "skill-1", "version-1")
assert payload["files"][0]["content"] == "# Version"
service.get_version.assert_called_once_with(
tenant_id="tenant-1",
skill_id="skill-1",
version_id="version-1",
)
def test_patch_skill_version_renames_version(app: Flask) -> None:
api = WorkspaceSkillVersionApi()
method = unwrap(api.patch)
service = MagicMock()
service.update_version.return_value = {
"id": "version-1",
"skill_id": "skill-1",
"version_number": 1,
"version_name": "Approval threshold",
"publish_note": "",
"hash_code": "hash-code",
"archive_size": 123,
"published_by": "user-1",
"published_by_name": "Li Wei",
"is_latest": True,
"created_at": 1,
}
with (
app.test_request_context("/", method="PATCH"),
patch.object(
type(console_ns),
"payload",
new_callable=PropertyMock,
return_value={"version_name": "Approval threshold"},
),
patch("controllers.console.workspace.skills.SkillManagementService", return_value=service),
):
payload = method(api, "tenant-1", "skill-1", "version-1")
assert payload["version_name"] == "Approval threshold"
service.update_version.assert_called_once()
assert service.update_version.call_args.kwargs["payload"].version_name == "Approval threshold"
def test_delete_skill_version_returns_new_latest(app: Flask, current_user: Account) -> None:
api = WorkspaceSkillVersionApi()
method = unwrap(api.delete)
service = MagicMock()
service.delete_version.return_value = {
"id": "version-2",
"deleted": True,
"latest_published_version_id": "version-1",
}
with (
app.test_request_context("/", method="DELETE"),
patch("controllers.console.workspace.skills.SkillManagementService", return_value=service),
):
payload = method(api, "tenant-1", current_user, "skill-1", "version-2")
assert payload == {"id": "version-2", "deleted": True, "latest_published_version_id": "version-1"}
service.delete_version.assert_called_once_with(
tenant_id="tenant-1",
user_id="user-1",
skill_id="skill-1",
version_id="version-2",
)
def test_skill_assistant_runs_agent_app_stream(app: Flask, current_user: Account) -> None:
api = WorkspaceSkillAssistMessageApi()
method = unwrap(api.post)
service = MagicMock()
assistant_app = MagicMock()
assistant_app.id = "assistant-app-1"
service.get_or_create_assistant_app.return_value = (assistant_app, "<skill_draft>draft</skill_draft>")
app_model = MagicMock()
app_response = MagicMock()
compact_response = MagicMock()
with (
app.test_request_context("/", method="POST"),
patch.object(
type(console_ns),
"payload",
new_callable=PropertyMock,
return_value={
"attachments": [
{
"tool_file_id": "tool-file-1",
"name": "requirements.md",
"mime_type": "text/markdown",
"size": 128,
}
],
"message": "Create an approval checklist.",
},
),
patch("controllers.console.workspace.skills.SkillManagementService", return_value=service),
patch(
"controllers.console.workspace.skills.db.session",
return_value=MagicMock(get=MagicMock(return_value=app_model)),
),
patch("controllers.console.workspace.skills.AppGenerateService.generate", return_value=app_response),
patch("controllers.console.workspace.skills.helper.compact_generate_response", return_value=compact_response),
):
response = method(api, "tenant-1", current_user, "skill-1")
assert response is compact_response
service.get_or_create_assistant_app.assert_called_once_with(
tenant_id="tenant-1",
skill_id="skill-1",
user_id="user-1",
attachments=[
SkillAssistAttachmentPayload(
tool_file_id="tool-file-1",
name="requirements.md",
mime_type="text/markdown",
size=128,
)
],
message="Create an approval checklist.",
model_payload=None,
)

View File

@ -63,6 +63,7 @@ def _target(
) -> AgentConfigTarget:
agent_soul = soul or _soul()
return AgentConfigTarget(
tenant_id=TENANT,
agent_id=AGENT,
version_id=version_id,
kind=kind,
@ -508,7 +509,9 @@ def test_manifest_uses_items_shape_without_download_urls() -> None:
),
)
manifest = AgentConfigService._manifest_for_target(target)
with patch(f"{MODULE}.SkillManagementService") as skill_management_service:
skill_management_service.return_value.list_runtime_agent_skills.return_value = []
manifest = AgentConfigService._manifest_for_target(target)
assert manifest == {
"agent_id": AGENT,
@ -557,7 +560,9 @@ def test_manifest_preserves_missing_config_assets_and_pull_rejects_them() -> Non
target = _target(kind=AgentConfigVersionKind.DRAFT, writable=False, soul=soul)
service = AgentConfigService()
manifest = service._manifest_for_target(target)
with patch(f"{MODULE}.SkillManagementService") as skill_management_service:
skill_management_service.return_value.list_runtime_agent_skills.return_value = []
manifest = service._manifest_for_target(target)
assert manifest["skills"]["items"][0]["is_missing"] is True # type: ignore[index]
assert manifest["files"]["items"][0]["is_missing"] is True # type: ignore[index]
@ -606,6 +611,44 @@ def test_config_asset_refs_require_file_id_unless_marked_missing() -> None:
)
def test_manifest_appends_published_workspace_skills() -> None:
target = _target(
kind=AgentConfigVersionKind.DRAFT,
writable=False,
soul=_soul(
config_skills=[
AgentConfigSkillRefConfig(name="alpha", description="Alpha skill", file_id="tool-file-1")
]
),
)
with patch(f"{MODULE}.SkillManagementService") as skill_management_service:
skill_management_service.return_value.list_runtime_agent_skills.return_value = [
{
"id": "workspace-skill-id",
"name": "beta",
"file_id": "tool-file-2",
"description": "Beta workspace skill",
"size": 123,
"hash": "sha256:beta",
"mime_type": "application/zip",
},
{
"id": "duplicate",
"name": "alpha",
"file_id": "tool-file-ignored",
"description": "Duplicate workspace skill",
"size": 456,
"hash": "sha256:ignored",
"mime_type": "application/zip",
},
]
manifest = AgentConfigService._manifest_for_target(target)
assert [item["name"] for item in manifest["skills"]["items"]] == ["alpha", "beta"]
assert manifest["skills"]["items"][1]["file_id"] == "tool-file-2"
def test_preview_skill_file_returns_text_preview() -> None:
service = AgentConfigService()
target = _target(

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,7 @@ export type TagBindingRemovePayload = {
type: TagType
}
export type TagType = 'app' | 'knowledge' | 'snippet'
export type TagType = 'app' | 'knowledge' | 'skill' | 'snippet'
export type PostTagBindingsData = {
body: TagBindingPayload

View File

@ -14,7 +14,7 @@ export const zSimpleResultResponse = z.object({
*
* Tag type
*/
export const zTagType = z.enum(['app', 'knowledge', 'snippet'])
export const zTagType = z.enum(['app', 'knowledge', 'skill', 'snippet'])
/**
* TagBindingPayload

View File

@ -22,14 +22,14 @@ export type TagUpdateRequestPayload = {
name: string
}
export type TagType = 'app' | 'knowledge' | 'snippet'
export type TagType = 'app' | 'knowledge' | 'skill' | 'snippet'
export type GetTagsData = {
body?: never
path?: never
query?: {
keyword?: string
type?: '' | 'app' | 'knowledge' | 'snippet'
type?: '' | 'app' | 'knowledge' | 'skill' | 'snippet'
}
url: '/tags'
}

View File

@ -29,7 +29,7 @@ export const zTagUpdateRequestPayload = z.object({
*
* Tag type
*/
export const zTagType = z.enum(['app', 'knowledge', 'snippet'])
export const zTagType = z.enum(['app', 'knowledge', 'skill', 'snippet'])
/**
* TagBasePayload
@ -41,7 +41,7 @@ export const zTagBasePayload = z.object({
export const zGetTagsQuery = z.object({
keyword: z.string().optional(),
type: z.enum(['', 'app', 'knowledge', 'snippet']).optional().default(''),
type: z.enum(['', 'app', 'knowledge', 'skill', 'snippet']).optional().default(''),
})
/**

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,16 @@ export type AgentProviderListResponse = Array<{
[key: string]: unknown
}>
export type AgentSkillBindingsResponse = {
agent_id: string
data?: Array<AgentSkillBindingItemResponse>
skill_ids?: Array<string>
}
export type AgentSkillBindingsPayload = {
skill_ids?: Array<string>
}
export type SnippetPaginationResponse = {
data: Array<SnippetListItemResponse>
has_more: boolean
@ -635,6 +645,193 @@ export type WorkspaceAccessMatrix = {
pagination?: Pagination | null
}
export type SkillListResponse = {
data?: Array<SkillResponse>
has_more?: boolean
limit?: number
page?: number
total?: number
}
export type SkillCreatePayload = {
description?: string
display_name?: string | null
icon?: string
name?: string | null
tags?: Array<string>
}
export type SkillDetailResponse = {
created_at: number
created_by?: string | null
created_by_name?: string | null
description: string
display_name: string
files?: Array<SkillFileResponse>
icon: string
id: string
latest_published_version_id?: string | null
name: string
name_manually_edited?: boolean
reference_count?: number
tags?: Array<string>
updated_at: number
updated_by?: string | null
updated_by_name?: string | null
visibility: string
}
export type SkillFileUploadResponse = {
hash: string
id: string
mime_type: string
name: string
size: number
}
export type SkillTagListResponse = {
data?: Array<SkillTagResponse>
}
export type SkillDeletePayload = {
confirmation_name?: string | null
}
export type SkillDeleteResponse = {
deleted: boolean
id: string
}
export type SkillMetadataPayload = {
display_name?: string | null
expected_updated_at?: number | null
icon?: string | null
tags?: Array<string> | null
}
export type SkillResponse = {
created_at: number
created_by?: string | null
created_by_name?: string | null
description: string
display_name: string
icon: string
id: string
latest_published_version_id?: string | null
name: string
name_manually_edited?: boolean
reference_count?: number
tags?: Array<string>
updated_at: number
updated_by?: string | null
updated_by_name?: string | null
visibility: string
}
export type SkillAssistModelPayload = {
model: string
model_settings?: { [key: string]: unknown } | null
plugin_id?: string | null
provider: string
}
export type SkillAssistAttachmentPayload = {
mime_type?: string | null
name: string
size?: number | null
tool_file_id: string
}
export type SkillAssistMessagePayload = {
attachments?: Array<SkillAssistAttachmentPayload>
message: string
model?: SkillAssistModelPayload | null
}
export type SkillDraftFileOperationPayload = {
content?: string | null
expected_updated_at?: number | null
hash?: string | null
mime_type?: string | null
operation: SkillDraftFileOperation
path: string
size?: number | null
target_path?: string | null
tool_file_id?: string | null
}
export type SkillDraftTreePayload = {
expected_updated_at?: number | null
files?: Array<SkillDraftTreeItemPayload>
}
export type SkillFilePreviewResponse = {
content: string
hash: string
mime_type: string
path: string
size: number
}
export type SkillPublishPayload = {
publish_note?: string
version_name?: string | null
}
export type SkillVersionResponse = {
archive_size: number
created_at: number
hash_code: string
id: string
is_latest?: boolean
publish_note: string
published_by?: string | null
published_by_name?: string | null
skill_id: string
version_name: string
version_number: number
}
export type SkillReferenceListResponse = {
data?: Array<SkillReferenceResponse>
}
export type SkillRestorePayload = {
publish_note?: string
version_id: string
version_name?: string | null
}
export type SkillVersionListResponse = {
data?: Array<SkillVersionResponse>
}
export type SkillVersionDeleteResponse = {
deleted: boolean
id: string
latest_published_version_id?: string | null
}
export type SkillVersionDetailResponse = {
archive_size: number
created_at: number
files?: Array<SkillFileResponse>
hash_code: string
id: string
is_latest?: boolean
publish_note: string
published_by?: string | null
published_by_name?: string | null
skill_id: string
version_name: string
version_number: number
}
export type SkillVersionUpdatePayload = {
publish_note?: string
version_name?: string | null
}
export type ToolLabelListResponse = Array<ToolLabel>
export type ApiToolProviderAddPayload = {
@ -1042,6 +1239,21 @@ export type WorkspaceCustomConfigResponse = {
replace_webapp_logo?: string | null
}
export type AgentSkillBindingItemResponse = {
description: string
display_name: string
file_count: number
icon: string
id: string
latest_published_at?: number | null
latest_published_version_id?: string | null
name: string
priority: number
status: string
tags?: Array<string>
updated_at: number
}
export type SnippetListItemResponse = {
author_name: string | null
created_at: number
@ -1502,6 +1714,60 @@ export type AccessPolicyRole = {
role_tag?: string
}
export type SkillFileResponse = {
content?: string | null
hash?: string | null
id?: string | null
kind: string
mime_type?: string | null
path: string
size?: number | null
storage?: string | null
tool_file_id?: string | null
}
export type SkillTagResponse = {
count: number
tag: string
}
export type SkillDraftFileOperation =
| 'delete'
| 'mkdir'
| 'rename'
| 'upsert_text'
| 'upsert_tool_file'
export type SkillDraftTreeItemPayload = {
content?: string | null
hash?: string | null
kind?: SkillFileKind
mime_type?: string | null
path: string
size?: number | null
storage?: SkillFileStorage | null
tool_file_id?: string | null
}
export type SkillReferenceResponse = {
agent_id: string
agent_icon?: string | null
agent_icon_background?: string | null
agent_icon_type?: string | null
app_id?: string | null
display_name: string
name: string
node_id?: string | null
node_name?: string | null
type: string
workflow_icon?: string | null
workflow_icon_background?: string | null
workflow_icon_type?: string | null
workflow_id?: string | null
workflow_name?: string | null
workflow_version?: string | null
}
export type ToolLabel = {
icon: string
label: I18nObject
@ -1978,6 +2244,10 @@ export type PermissionCatalogItem = {
name: string
}
export type SkillFileKind = 'directory' | 'file'
export type SkillFileStorage = 'text' | 'tool_file'
export type ToolParameter = {
auto_generate?: PluginParameterAutoGenerate | null
default?:
@ -2472,6 +2742,38 @@ export type GetWorkspacesCurrentAgentProvidersResponses = {
export type GetWorkspacesCurrentAgentProvidersResponse =
GetWorkspacesCurrentAgentProvidersResponses[keyof GetWorkspacesCurrentAgentProvidersResponses]
export type GetWorkspacesCurrentAgentsByAgentIdSkillsData = {
body?: never
path: {
agent_id: string
}
query?: never
url: '/workspaces/current/agents/{agent_id}/skills'
}
export type GetWorkspacesCurrentAgentsByAgentIdSkillsResponses = {
200: AgentSkillBindingsResponse
}
export type GetWorkspacesCurrentAgentsByAgentIdSkillsResponse =
GetWorkspacesCurrentAgentsByAgentIdSkillsResponses[keyof GetWorkspacesCurrentAgentsByAgentIdSkillsResponses]
export type PutWorkspacesCurrentAgentsByAgentIdSkillsData = {
body: AgentSkillBindingsPayload
path: {
agent_id: string
}
query?: never
url: '/workspaces/current/agents/{agent_id}/skills'
}
export type PutWorkspacesCurrentAgentsByAgentIdSkillsResponses = {
200: AgentSkillBindingsResponse
}
export type PutWorkspacesCurrentAgentsByAgentIdSkillsResponse =
PutWorkspacesCurrentAgentsByAgentIdSkillsResponses[keyof PutWorkspacesCurrentAgentsByAgentIdSkillsResponses]
export type GetWorkspacesCurrentCustomizedSnippetsData = {
body?: never
path?: never
@ -4622,6 +4924,369 @@ export type GetWorkspacesCurrentRbacWorkspaceDatasetsAccessPolicyResponses = {
export type GetWorkspacesCurrentRbacWorkspaceDatasetsAccessPolicyResponse =
GetWorkspacesCurrentRbacWorkspaceDatasetsAccessPolicyResponses[keyof GetWorkspacesCurrentRbacWorkspaceDatasetsAccessPolicyResponses]
export type GetWorkspacesCurrentSkillsData = {
body?: never
path?: never
query?: {
keyword?: string
limit?: number
page?: number
tag?: Array<string>
}
url: '/workspaces/current/skills'
}
export type GetWorkspacesCurrentSkillsResponses = {
200: SkillListResponse
}
export type GetWorkspacesCurrentSkillsResponse =
GetWorkspacesCurrentSkillsResponses[keyof GetWorkspacesCurrentSkillsResponses]
export type PostWorkspacesCurrentSkillsData = {
body: SkillCreatePayload
path?: never
query?: never
url: '/workspaces/current/skills'
}
export type PostWorkspacesCurrentSkillsResponses = {
201: SkillDetailResponse
}
export type PostWorkspacesCurrentSkillsResponse =
PostWorkspacesCurrentSkillsResponses[keyof PostWorkspacesCurrentSkillsResponses]
export type PostWorkspacesCurrentSkillsFilesUploadData = {
body: {
file: Blob | File
}
path?: never
query?: never
url: '/workspaces/current/skills/files/upload'
}
export type PostWorkspacesCurrentSkillsFilesUploadResponses = {
201: SkillFileUploadResponse
}
export type PostWorkspacesCurrentSkillsFilesUploadResponse =
PostWorkspacesCurrentSkillsFilesUploadResponses[keyof PostWorkspacesCurrentSkillsFilesUploadResponses]
export type PostWorkspacesCurrentSkillsImportData = {
body?: never
path?: never
query?: never
url: '/workspaces/current/skills/import'
}
export type PostWorkspacesCurrentSkillsImportResponses = {
201: SkillDetailResponse
}
export type PostWorkspacesCurrentSkillsImportResponse =
PostWorkspacesCurrentSkillsImportResponses[keyof PostWorkspacesCurrentSkillsImportResponses]
export type GetWorkspacesCurrentSkillsTagsData = {
body?: never
path?: never
query?: never
url: '/workspaces/current/skills/tags'
}
export type GetWorkspacesCurrentSkillsTagsResponses = {
200: SkillTagListResponse
}
export type GetWorkspacesCurrentSkillsTagsResponse =
GetWorkspacesCurrentSkillsTagsResponses[keyof GetWorkspacesCurrentSkillsTagsResponses]
export type DeleteWorkspacesCurrentSkillsBySkillIdData = {
body: SkillDeletePayload
path: {
skill_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}'
}
export type DeleteWorkspacesCurrentSkillsBySkillIdResponses = {
200: SkillDeleteResponse
}
export type DeleteWorkspacesCurrentSkillsBySkillIdResponse =
DeleteWorkspacesCurrentSkillsBySkillIdResponses[keyof DeleteWorkspacesCurrentSkillsBySkillIdResponses]
export type GetWorkspacesCurrentSkillsBySkillIdData = {
body?: never
path: {
skill_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}'
}
export type GetWorkspacesCurrentSkillsBySkillIdResponses = {
200: SkillDetailResponse
}
export type GetWorkspacesCurrentSkillsBySkillIdResponse =
GetWorkspacesCurrentSkillsBySkillIdResponses[keyof GetWorkspacesCurrentSkillsBySkillIdResponses]
export type PatchWorkspacesCurrentSkillsBySkillIdData = {
body: SkillMetadataPayload
path: {
skill_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}'
}
export type PatchWorkspacesCurrentSkillsBySkillIdResponses = {
200: SkillResponse
}
export type PatchWorkspacesCurrentSkillsBySkillIdResponse =
PatchWorkspacesCurrentSkillsBySkillIdResponses[keyof PatchWorkspacesCurrentSkillsBySkillIdResponses]
export type PostWorkspacesCurrentSkillsBySkillIdAssistMessagesData = {
body: SkillAssistMessagePayload
path: {
skill_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}/assist/messages'
}
export type PostWorkspacesCurrentSkillsBySkillIdAssistMessagesResponses = {
200: {
[key: string]: unknown
}
}
export type PostWorkspacesCurrentSkillsBySkillIdAssistMessagesResponse =
PostWorkspacesCurrentSkillsBySkillIdAssistMessagesResponses[keyof PostWorkspacesCurrentSkillsBySkillIdAssistMessagesResponses]
export type PostWorkspacesCurrentSkillsBySkillIdDuplicateData = {
body?: never
path: {
skill_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}/duplicate'
}
export type PostWorkspacesCurrentSkillsBySkillIdDuplicateResponses = {
201: SkillDetailResponse
}
export type PostWorkspacesCurrentSkillsBySkillIdDuplicateResponse =
PostWorkspacesCurrentSkillsBySkillIdDuplicateResponses[keyof PostWorkspacesCurrentSkillsBySkillIdDuplicateResponses]
export type GetWorkspacesCurrentSkillsBySkillIdExportData = {
body?: never
path: {
skill_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}/export'
}
export type GetWorkspacesCurrentSkillsBySkillIdExportResponses = {
200: {
[key: string]: unknown
}
}
export type GetWorkspacesCurrentSkillsBySkillIdExportResponse =
GetWorkspacesCurrentSkillsBySkillIdExportResponses[keyof GetWorkspacesCurrentSkillsBySkillIdExportResponses]
export type PatchWorkspacesCurrentSkillsBySkillIdFilesData = {
body: SkillDraftFileOperationPayload
path: {
skill_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}/files'
}
export type PatchWorkspacesCurrentSkillsBySkillIdFilesResponses = {
200: SkillDetailResponse
}
export type PatchWorkspacesCurrentSkillsBySkillIdFilesResponse =
PatchWorkspacesCurrentSkillsBySkillIdFilesResponses[keyof PatchWorkspacesCurrentSkillsBySkillIdFilesResponses]
export type PutWorkspacesCurrentSkillsBySkillIdFilesData = {
body: SkillDraftTreePayload
path: {
skill_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}/files'
}
export type PutWorkspacesCurrentSkillsBySkillIdFilesResponses = {
200: SkillDetailResponse
}
export type PutWorkspacesCurrentSkillsBySkillIdFilesResponse =
PutWorkspacesCurrentSkillsBySkillIdFilesResponses[keyof PutWorkspacesCurrentSkillsBySkillIdFilesResponses]
export type GetWorkspacesCurrentSkillsBySkillIdFilesContentData = {
body?: never
path: {
skill_id: string
}
query: {
download?: string
path: string
version_id?: string
}
url: '/workspaces/current/skills/{skill_id}/files/content'
}
export type GetWorkspacesCurrentSkillsBySkillIdFilesContentResponses = {
200: BinaryFileResponse
}
export type GetWorkspacesCurrentSkillsBySkillIdFilesContentResponse =
GetWorkspacesCurrentSkillsBySkillIdFilesContentResponses[keyof GetWorkspacesCurrentSkillsBySkillIdFilesContentResponses]
export type GetWorkspacesCurrentSkillsBySkillIdFilesPreviewData = {
body?: never
path: {
skill_id: string
}
query: {
path: string
version_id?: string
}
url: '/workspaces/current/skills/{skill_id}/files/preview'
}
export type GetWorkspacesCurrentSkillsBySkillIdFilesPreviewResponses = {
200: SkillFilePreviewResponse
}
export type GetWorkspacesCurrentSkillsBySkillIdFilesPreviewResponse =
GetWorkspacesCurrentSkillsBySkillIdFilesPreviewResponses[keyof GetWorkspacesCurrentSkillsBySkillIdFilesPreviewResponses]
export type PostWorkspacesCurrentSkillsBySkillIdPublishData = {
body: SkillPublishPayload
path: {
skill_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}/publish'
}
export type PostWorkspacesCurrentSkillsBySkillIdPublishResponses = {
200: SkillVersionResponse
}
export type PostWorkspacesCurrentSkillsBySkillIdPublishResponse =
PostWorkspacesCurrentSkillsBySkillIdPublishResponses[keyof PostWorkspacesCurrentSkillsBySkillIdPublishResponses]
export type GetWorkspacesCurrentSkillsBySkillIdReferencesData = {
body?: never
path: {
skill_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}/references'
}
export type GetWorkspacesCurrentSkillsBySkillIdReferencesResponses = {
200: SkillReferenceListResponse
}
export type GetWorkspacesCurrentSkillsBySkillIdReferencesResponse =
GetWorkspacesCurrentSkillsBySkillIdReferencesResponses[keyof GetWorkspacesCurrentSkillsBySkillIdReferencesResponses]
export type PostWorkspacesCurrentSkillsBySkillIdRestoreData = {
body: SkillRestorePayload
path: {
skill_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}/restore'
}
export type PostWorkspacesCurrentSkillsBySkillIdRestoreResponses = {
200: SkillVersionResponse
}
export type PostWorkspacesCurrentSkillsBySkillIdRestoreResponse =
PostWorkspacesCurrentSkillsBySkillIdRestoreResponses[keyof PostWorkspacesCurrentSkillsBySkillIdRestoreResponses]
export type GetWorkspacesCurrentSkillsBySkillIdVersionsData = {
body?: never
path: {
skill_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}/versions'
}
export type GetWorkspacesCurrentSkillsBySkillIdVersionsResponses = {
200: SkillVersionListResponse
}
export type GetWorkspacesCurrentSkillsBySkillIdVersionsResponse =
GetWorkspacesCurrentSkillsBySkillIdVersionsResponses[keyof GetWorkspacesCurrentSkillsBySkillIdVersionsResponses]
export type DeleteWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdData = {
body?: never
path: {
skill_id: string
version_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}/versions/{version_id}'
}
export type DeleteWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponses = {
200: SkillVersionDeleteResponse
}
export type DeleteWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponse =
DeleteWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponses[keyof DeleteWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponses]
export type GetWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdData = {
body?: never
path: {
skill_id: string
version_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}/versions/{version_id}'
}
export type GetWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponses = {
200: SkillVersionDetailResponse
}
export type GetWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponse =
GetWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponses[keyof GetWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponses]
export type PatchWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdData = {
body: SkillVersionUpdatePayload
path: {
skill_id: string
version_id: string
}
query?: never
url: '/workspaces/current/skills/{skill_id}/versions/{version_id}'
}
export type PatchWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponses = {
200: SkillVersionResponse
}
export type PatchWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponse =
PatchWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponses[keyof PatchWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponses]
export type GetWorkspacesCurrentToolLabelsData = {
body?: never
path?: never

View File

@ -12,6 +12,13 @@ export const zAgentProviderResponse = z.record(z.string(), z.unknown())
*/
export const zAgentProviderListResponse = z.array(z.record(z.string(), z.unknown()))
/**
* AgentSkillBindingsPayload
*/
export const zAgentSkillBindingsPayload = z.object({
skill_ids: z.array(z.string()).optional(),
})
/**
* SnippetImportPayload
*
@ -434,6 +441,186 @@ export const zReplaceBindingsRequest = z.object({
role_ids: z.array(z.string()).optional(),
})
/**
* SkillCreatePayload
*/
export const zSkillCreatePayload = z.object({
description: z.string().optional().default(''),
display_name: z.string().nullish(),
icon: z.string().optional().default('📄'),
name: z.string().nullish(),
tags: z.array(z.string()).optional(),
})
/**
* SkillFileUploadResponse
*/
export const zSkillFileUploadResponse = z.object({
hash: z.string(),
id: z.string(),
mime_type: z.string(),
name: z.string(),
size: z.int(),
})
/**
* SkillDeletePayload
*/
export const zSkillDeletePayload = z.object({
confirmation_name: z.string().nullish(),
})
/**
* SkillDeleteResponse
*/
export const zSkillDeleteResponse = z.object({
deleted: z.boolean(),
id: z.string(),
})
/**
* SkillMetadataPayload
*/
export const zSkillMetadataPayload = z.object({
display_name: z.string().nullish(),
expected_updated_at: z.int().nullish(),
icon: z.string().nullish(),
tags: z.array(z.string()).nullish(),
})
/**
* SkillResponse
*/
export const zSkillResponse = z.object({
created_at: z.int(),
created_by: z.string().nullish(),
created_by_name: z.string().nullish(),
description: z.string(),
display_name: z.string(),
icon: z.string(),
id: z.string(),
latest_published_version_id: z.string().nullish(),
name: z.string(),
name_manually_edited: z.boolean().optional().default(false),
reference_count: z.int().optional().default(0),
tags: z.array(z.string()).optional(),
updated_at: z.int(),
updated_by: z.string().nullish(),
updated_by_name: z.string().nullish(),
visibility: z.string(),
})
/**
* SkillListResponse
*/
export const zSkillListResponse = z.object({
data: z.array(zSkillResponse).optional(),
has_more: z.boolean().optional(),
limit: z.number().optional(),
page: z.number().optional(),
total: z.number().optional(),
})
/**
* SkillAssistModelPayload
*/
export const zSkillAssistModelPayload = z.object({
model: z.string().min(1).max(255),
model_settings: z.record(z.string(), z.unknown()).nullable().optional(),
plugin_id: z.string().min(1).max(255).nullable().optional(),
provider: z.string().min(1).max(255),
})
/**
* SkillAssistAttachmentPayload
*/
export const zSkillAssistAttachmentPayload = z.object({
mime_type: z.string().min(1).max(255).nullable().optional(),
name: z.string().min(1).max(255),
size: z.number().gte(0).nullable().optional(),
tool_file_id: z.string().min(1),
})
/**
* SkillAssistMessagePayload
*
* One user message and optional uploaded context for the read-only Skill Authoring assistant.
*/
export const zSkillAssistMessagePayload = z.object({
attachments: z.array(zSkillAssistAttachmentPayload).max(10).optional(),
message: z.string().min(1).max(8000),
model: zSkillAssistModelPayload.nullable().optional(),
})
/**
* SkillFilePreviewResponse
*/
export const zSkillFilePreviewResponse = z.object({
content: z.string(),
hash: z.string(),
mime_type: z.string(),
path: z.string(),
size: z.int(),
})
/**
* SkillPublishPayload
*/
export const zSkillPublishPayload = z.object({
publish_note: z.string().max(1024).optional().default(''),
version_name: z.string().max(128).nullish(),
})
/**
* SkillVersionResponse
*/
export const zSkillVersionResponse = z.object({
archive_size: z.int(),
created_at: z.int(),
hash_code: z.string(),
id: z.string(),
is_latest: z.boolean().optional().default(false),
publish_note: z.string(),
published_by: z.string().nullish(),
published_by_name: z.string().nullish(),
skill_id: z.string(),
version_name: z.string(),
version_number: z.int(),
})
/**
* SkillRestorePayload
*/
export const zSkillRestorePayload = z.object({
publish_note: z.string().max(1024).optional().default(''),
version_id: z.string(),
version_name: z.string().max(128).nullish(),
})
/**
* SkillVersionListResponse
*/
export const zSkillVersionListResponse = z.object({
data: z.array(zSkillVersionResponse).optional(),
})
/**
* SkillVersionDeleteResponse
*/
export const zSkillVersionDeleteResponse = z.object({
deleted: z.boolean(),
id: z.string(),
latest_published_version_id: z.string().nullish(),
})
/**
* SkillVersionUpdatePayload
*/
export const zSkillVersionUpdatePayload = z.object({
publish_note: z.string().max(1024).optional().default(''),
version_name: z.string().max(128).nullish(),
})
/**
* ApiToolProviderDeletePayload
*/
@ -654,6 +841,33 @@ export const zSwitchWorkspaceResponse = z.object({
result: z.string(),
})
/**
* AgentSkillBindingItemResponse
*/
export const zAgentSkillBindingItemResponse = z.object({
description: z.string(),
display_name: z.string(),
file_count: z.int(),
icon: z.string(),
id: z.string(),
latest_published_at: z.int().nullish(),
latest_published_version_id: z.string().nullish(),
name: z.string(),
priority: z.int(),
status: z.string(),
tags: z.array(z.string()).optional(),
updated_at: z.int(),
})
/**
* AgentSkillBindingsResponse
*/
export const zAgentSkillBindingsResponse = z.object({
agent_id: z.string(),
data: z.array(zAgentSkillBindingItemResponse).optional(),
skill_ids: z.array(z.string()).optional(),
})
/**
* IconInfo
*
@ -1242,6 +1456,132 @@ export const zWorkspaceAccessMatrix = z.object({
pagination: zPagination.nullish(),
})
/**
* SkillFileResponse
*/
export const zSkillFileResponse = z.object({
content: z.string().nullish(),
hash: z.string().nullish(),
id: z.string().nullish(),
kind: z.string(),
mime_type: z.string().nullish(),
path: z.string(),
size: z.int().nullish(),
storage: z.string().nullish(),
tool_file_id: z.string().nullish(),
})
/**
* SkillDetailResponse
*/
export const zSkillDetailResponse = z.object({
created_at: z.int(),
created_by: z.string().nullish(),
created_by_name: z.string().nullish(),
description: z.string(),
display_name: z.string(),
files: z.array(zSkillFileResponse).optional(),
icon: z.string(),
id: z.string(),
latest_published_version_id: z.string().nullish(),
name: z.string(),
name_manually_edited: z.boolean().optional().default(false),
reference_count: z.int().optional().default(0),
tags: z.array(z.string()).optional(),
updated_at: z.int(),
updated_by: z.string().nullish(),
updated_by_name: z.string().nullish(),
visibility: z.string(),
})
/**
* SkillVersionDetailResponse
*/
export const zSkillVersionDetailResponse = z.object({
archive_size: z.int(),
created_at: z.int(),
files: z.array(zSkillFileResponse).optional(),
hash_code: z.string(),
id: z.string(),
is_latest: z.boolean().optional().default(false),
publish_note: z.string(),
published_by: z.string().nullish(),
published_by_name: z.string().nullish(),
skill_id: z.string(),
version_name: z.string(),
version_number: z.int(),
})
/**
* SkillTagResponse
*/
export const zSkillTagResponse = z.object({
count: z.int(),
tag: z.string(),
})
/**
* SkillTagListResponse
*/
export const zSkillTagListResponse = z.object({
data: z.array(zSkillTagResponse).optional(),
})
/**
* SkillDraftFileOperation
*/
export const zSkillDraftFileOperation = z.enum([
'delete',
'mkdir',
'rename',
'upsert_text',
'upsert_tool_file',
])
/**
* SkillDraftFileOperationPayload
*/
export const zSkillDraftFileOperationPayload = z.object({
content: z.string().nullish(),
expected_updated_at: z.int().nullish(),
hash: z.string().nullish(),
mime_type: z.string().nullish(),
operation: zSkillDraftFileOperation,
path: z.string(),
size: z.int().gte(0).nullish(),
target_path: z.string().nullish(),
tool_file_id: z.string().nullish(),
})
/**
* SkillReferenceResponse
*/
export const zSkillReferenceResponse = z.object({
agent_id: z.string(),
agent_icon: z.string().nullish(),
agent_icon_background: z.string().nullish(),
agent_icon_type: z.string().nullish(),
app_id: z.string().nullish(),
display_name: z.string(),
name: z.string(),
node_id: z.string().nullish(),
node_name: z.string().nullish(),
type: z.string(),
workflow_icon: z.string().nullish(),
workflow_icon_background: z.string().nullish(),
workflow_icon_type: z.string().nullish(),
workflow_id: z.string().nullish(),
workflow_name: z.string().nullish(),
workflow_version: z.string().nullish(),
})
/**
* SkillReferenceListResponse
*/
export const zSkillReferenceListResponse = z.object({
data: z.array(zSkillReferenceResponse).optional(),
})
/**
* ToolEmojiIcon
*/
@ -2094,6 +2434,42 @@ export const zPermissionCatalogResponse = z.object({
groups: z.array(zPermissionCatalogGroup).optional(),
})
/**
* SkillFileKind
*
* Draft file entry kind.
*/
export const zSkillFileKind = z.enum(['directory', 'file'])
/**
* SkillFileStorage
*
* How a draft file's content is stored.
*/
export const zSkillFileStorage = z.enum(['text', 'tool_file'])
/**
* SkillDraftTreeItemPayload
*/
export const zSkillDraftTreeItemPayload = z.object({
content: z.string().nullish(),
hash: z.string().nullish(),
kind: zSkillFileKind.optional().default('file'),
mime_type: z.string().nullish(),
path: z.string(),
size: z.int().gte(0).nullish(),
storage: zSkillFileStorage.nullish(),
tool_file_id: z.string().nullish(),
})
/**
* SkillDraftTreePayload
*/
export const zSkillDraftTreePayload = z.object({
expected_updated_at: z.int().nullish(),
files: z.array(zSkillDraftTreeItemPayload).optional(),
})
/**
* Option
*/
@ -3418,6 +3794,26 @@ export const zGetWorkspacesCurrentAgentProviderByProviderNameResponse = zAgentPr
*/
export const zGetWorkspacesCurrentAgentProvidersResponse = zAgentProviderListResponse
export const zGetWorkspacesCurrentAgentsByAgentIdSkillsPath = z.object({
agent_id: z.string(),
})
/**
* Agent Skill bindings
*/
export const zGetWorkspacesCurrentAgentsByAgentIdSkillsResponse = zAgentSkillBindingsResponse
export const zPutWorkspacesCurrentAgentsByAgentIdSkillsBody = zAgentSkillBindingsPayload
export const zPutWorkspacesCurrentAgentsByAgentIdSkillsPath = z.object({
agent_id: z.string(),
})
/**
* Agent Skill bindings replaced
*/
export const zPutWorkspacesCurrentAgentsByAgentIdSkillsResponse = zAgentSkillBindingsResponse
export const zGetWorkspacesCurrentCustomizedSnippetsQuery = z.object({
creators: z.array(z.string()).optional(),
is_published: z.boolean().optional(),
@ -4687,6 +5083,234 @@ export const zGetWorkspacesCurrentRbacWorkspaceDatasetsAccessPoliciesByPolicyIdR
*/
export const zGetWorkspacesCurrentRbacWorkspaceDatasetsAccessPolicyResponse = zWorkspaceAccessMatrix
export const zGetWorkspacesCurrentSkillsQuery = z.object({
keyword: z.string().optional(),
limit: z.number().optional(),
page: z.number().optional(),
tag: z.array(z.string()).optional(),
})
/**
* Workspace skills
*/
export const zGetWorkspacesCurrentSkillsResponse = zSkillListResponse
export const zPostWorkspacesCurrentSkillsBody = zSkillCreatePayload
/**
* Skill created
*/
export const zPostWorkspacesCurrentSkillsResponse = zSkillDetailResponse
export const zPostWorkspacesCurrentSkillsFilesUploadBody = z.object({
file: z.custom<Blob | File>(),
})
/**
* Skill draft file uploaded
*/
export const zPostWorkspacesCurrentSkillsFilesUploadResponse = zSkillFileUploadResponse
/**
* Skill imported
*/
export const zPostWorkspacesCurrentSkillsImportResponse = zSkillDetailResponse
/**
* Workspace Skill tags
*/
export const zGetWorkspacesCurrentSkillsTagsResponse = zSkillTagListResponse
export const zDeleteWorkspacesCurrentSkillsBySkillIdBody = zSkillDeletePayload
export const zDeleteWorkspacesCurrentSkillsBySkillIdPath = z.object({
skill_id: z.string(),
})
/**
* Skill deleted
*/
export const zDeleteWorkspacesCurrentSkillsBySkillIdResponse = zSkillDeleteResponse
export const zGetWorkspacesCurrentSkillsBySkillIdPath = z.object({
skill_id: z.string(),
})
/**
* Skill detail
*/
export const zGetWorkspacesCurrentSkillsBySkillIdResponse = zSkillDetailResponse
export const zPatchWorkspacesCurrentSkillsBySkillIdBody = zSkillMetadataPayload
export const zPatchWorkspacesCurrentSkillsBySkillIdPath = z.object({
skill_id: z.string(),
})
/**
* Skill updated
*/
export const zPatchWorkspacesCurrentSkillsBySkillIdResponse = zSkillResponse
export const zPostWorkspacesCurrentSkillsBySkillIdAssistMessagesBody = zSkillAssistMessagePayload
export const zPostWorkspacesCurrentSkillsBySkillIdAssistMessagesPath = z.object({
skill_id: z.string(),
})
/**
* Skill Authoring assistant event stream
*/
export const zPostWorkspacesCurrentSkillsBySkillIdAssistMessagesResponse = z.record(
z.string(),
z.unknown(),
)
export const zPostWorkspacesCurrentSkillsBySkillIdDuplicatePath = z.object({
skill_id: z.string(),
})
/**
* Skill duplicated
*/
export const zPostWorkspacesCurrentSkillsBySkillIdDuplicateResponse = zSkillDetailResponse
export const zGetWorkspacesCurrentSkillsBySkillIdExportPath = z.object({
skill_id: z.string(),
})
/**
* Published Skill zip archive
*/
export const zGetWorkspacesCurrentSkillsBySkillIdExportResponse = z.record(z.string(), z.unknown())
export const zPatchWorkspacesCurrentSkillsBySkillIdFilesBody = zSkillDraftFileOperationPayload
export const zPatchWorkspacesCurrentSkillsBySkillIdFilesPath = z.object({
skill_id: z.string(),
})
/**
* Draft file operation applied
*/
export const zPatchWorkspacesCurrentSkillsBySkillIdFilesResponse = zSkillDetailResponse
export const zPutWorkspacesCurrentSkillsBySkillIdFilesBody = zSkillDraftTreePayload
export const zPutWorkspacesCurrentSkillsBySkillIdFilesPath = z.object({
skill_id: z.string(),
})
/**
* Draft files replaced
*/
export const zPutWorkspacesCurrentSkillsBySkillIdFilesResponse = zSkillDetailResponse
export const zGetWorkspacesCurrentSkillsBySkillIdFilesContentPath = z.object({
skill_id: z.string(),
})
export const zGetWorkspacesCurrentSkillsBySkillIdFilesContentQuery = z.object({
download: z.string().optional(),
path: z.string(),
version_id: z.string().optional(),
})
/**
* Skill file content
*/
export const zGetWorkspacesCurrentSkillsBySkillIdFilesContentResponse = zBinaryFileResponse
export const zGetWorkspacesCurrentSkillsBySkillIdFilesPreviewPath = z.object({
skill_id: z.string(),
})
export const zGetWorkspacesCurrentSkillsBySkillIdFilesPreviewQuery = z.object({
path: z.string(),
version_id: z.string().optional(),
})
/**
* Skill file text preview
*/
export const zGetWorkspacesCurrentSkillsBySkillIdFilesPreviewResponse = zSkillFilePreviewResponse
export const zPostWorkspacesCurrentSkillsBySkillIdPublishBody = zSkillPublishPayload
export const zPostWorkspacesCurrentSkillsBySkillIdPublishPath = z.object({
skill_id: z.string(),
})
/**
* Skill published
*/
export const zPostWorkspacesCurrentSkillsBySkillIdPublishResponse = zSkillVersionResponse
export const zGetWorkspacesCurrentSkillsBySkillIdReferencesPath = z.object({
skill_id: z.string(),
})
/**
* Skill references
*/
export const zGetWorkspacesCurrentSkillsBySkillIdReferencesResponse = zSkillReferenceListResponse
export const zPostWorkspacesCurrentSkillsBySkillIdRestoreBody = zSkillRestorePayload
export const zPostWorkspacesCurrentSkillsBySkillIdRestorePath = z.object({
skill_id: z.string(),
})
/**
* Skill version restored
*/
export const zPostWorkspacesCurrentSkillsBySkillIdRestoreResponse = zSkillVersionResponse
export const zGetWorkspacesCurrentSkillsBySkillIdVersionsPath = z.object({
skill_id: z.string(),
})
/**
* Skill versions
*/
export const zGetWorkspacesCurrentSkillsBySkillIdVersionsResponse = zSkillVersionListResponse
export const zDeleteWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdPath = z.object({
skill_id: z.string(),
version_id: z.string(),
})
/**
* Skill version deleted
*/
export const zDeleteWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponse =
zSkillVersionDeleteResponse
export const zGetWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdPath = z.object({
skill_id: z.string(),
version_id: z.string(),
})
/**
* Skill version detail
*/
export const zGetWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponse =
zSkillVersionDetailResponse
export const zPatchWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdBody =
zSkillVersionUpdatePayload
export const zPatchWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdPath = z.object({
skill_id: z.string(),
version_id: z.string(),
})
/**
* Skill version updated
*/
export const zPatchWorkspacesCurrentSkillsBySkillIdVersionsByVersionIdResponse =
zSkillVersionResponse
/**
* Tool labels retrieved successfully
*/

View File

@ -18,6 +18,7 @@ The current Flask-RESTX generator still emits these response entries under `appl
| service | GET | `/files/{file_id}/preview` | Original file MIME type, optionally attachment | `BinaryFileResponse` |
| console | GET | `/workspaces/current/plugin/icon` | Plugin asset MIME type | `BinaryFileResponse` |
| console | GET | `/workspaces/current/plugin/asset` | `application/octet-stream` | `BinaryFileResponse` |
| console | GET | `/workspaces/current/skills/{skill_id}/files/content` | Skill file MIME type, optionally attachment | `BinaryFileResponse` |
| console | GET | `/workspaces/current/tool-provider/builtin/{provider}/icon` | Tool icon MIME type | `BinaryFileResponse` |
| console | GET | `/workspaces/current/trigger-provider/{provider}/icon` | Trigger icon response | `BinaryFileResponse` |
| console | GET | `/workspaces/{tenant_id}/model-providers/{provider}/{icon_type}/{lang}` | Model provider icon MIME type | `BinaryFileResponse` |

View File

@ -0,0 +1,5 @@
import SkillDetailPage from '@/features/skills/detail-page'
export default function Page() {
return <SkillDetailPage />
}

View File

@ -0,0 +1,5 @@
import SkillsPage from '@/features/skills/page'
export default function Page() {
return <SkillsPage />
}

View File

@ -91,9 +91,12 @@ describe('AgentRosterResponseContent', () => {
await user.click(processToggle)
expect(processToggle).toHaveAttribute('aria-expanded', 'true')
await waitFor(() => {
expect(screen.getByText('history answer')).toBeInTheDocument()
})
await waitFor(
() => {
expect(screen.getByText('history answer')).toBeInTheDocument()
},
{ timeout: 5000 },
)
expect(screen.queryByText('internal thought should not render')).not.toBeInTheDocument()
})
@ -122,9 +125,12 @@ describe('AgentRosterResponseContent', () => {
render(<AgentRosterResponseContent item={item} />)
await user.click(screen.getByRole('button', { name: 'Thinking' }))
await waitFor(() => {
expect(screen.getByText('const answer = 42').tagName).toBe('CODE')
})
await waitFor(
() => {
expect(screen.getByText('const answer = 42').tagName).toBe('CODE')
},
{ timeout: 5000 },
)
})
it('should keep one collapsible thinking timeline while response parts interleave', async () => {

View File

@ -1,5 +1,5 @@
import type { FC, ReactNode } from 'react'
import type { DefaultModel, FormValue, ModelParameterRule } from '../declarations'
import type { DefaultModel, FormValue, Model, ModelParameterRule } from '../declarations'
import type { ParameterValue } from './parameter-item'
import type { TriggerProps } from './types'
import type { Node, NodeOutPutVar } from '@/app/components/workflow/types'
@ -7,7 +7,6 @@ import { cn } from '@langgenius/dify-ui/cn'
import { Popover, PopoverClose, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
import Loading from '@/app/components/base/loading'
import { PROVIDER_WITH_PRESET_TONE, STOP_PARAMETER_RULE } from '@/config'
import { useModelParameterRules } from '@/service/use-common'
@ -36,6 +35,7 @@ export type ModelParameterModalProps = {
renderTrigger?: (v: TriggerProps) => ReactNode
readonly?: boolean
isInWorkflow?: boolean
modelList?: Model[]
scope?: string
nodesOutputVars?: NodeOutPutVar[]
availableNodes?: Node[]
@ -55,6 +55,7 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
renderTrigger,
readonly,
isInWorkflow,
modelList,
nodesOutputVars,
availableNodes,
}) => {
@ -64,6 +65,11 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
const isRulesLoading = !!provider && !!modelId && isLoading
const { currentProvider, currentModel, activeTextGenerationModelList } =
useTextGenerationCurrentProviderAndModelAndModelList({ provider, model: modelId })
const availableTextGenerationModelList = modelList ?? activeTextGenerationModelList
const selectedProvider =
modelList?.find((modelItem) => modelItem.provider === provider) ?? currentProvider
const selectedModel =
selectedProvider?.models?.find((modelItem) => modelItem.model === modelId) ?? currentModel
const parameterRules: ModelParameterRule[] = useMemo(() => {
return parameterRulesData?.data || []
@ -71,6 +77,7 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
const supportedPresetParameterNames = useMemo(() => {
return parameterRules.map((parameterRule) => parameterRule.name)
}, [parameterRules])
const hasSelectedModel = !!provider && !!modelId
const handleParamChange = (key: string, value: ParameterValue) => {
onCompletionParamsChange({
@ -80,10 +87,10 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
}
const handleChangeModel = ({ provider, model }: DefaultModel) => {
const targetProvider = activeTextGenerationModelList.find(
const targetProvider = availableTextGenerationModelList.find(
(modelItem) => modelItem.provider === provider,
)
const targetModelItem = targetProvider?.models.find((modelItem) => modelItem.model === model)
const targetModelItem = targetProvider?.models?.find((modelItem) => modelItem.model === model)
setModel({
modelId: model,
provider,
@ -91,6 +98,10 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
features: targetModelItem?.features || [],
})
}
const handleOpenModelSettings = () => {
if (readonly || !hasSelectedModel) return
setOpen(true)
}
const handleSwitch = (key: string, value: boolean, assignValue: ParameterValue) => {
if (!value) {
@ -114,8 +125,6 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
})
}
const hasSelectedModel = !!provider && !!modelId
return (
<Popover
open={open}
@ -133,8 +142,8 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
>
{renderTrigger({
open,
currentProvider,
currentModel,
currentProvider: selectedProvider,
currentModel: selectedModel,
providerName: provider,
modelId,
})}
@ -146,7 +155,7 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
<div className="min-w-0 flex-1">
<ModelSelector
defaultModel={provider || modelId ? { provider, model: modelId } : undefined}
modelList={activeTextGenerationModelList}
modelList={availableTextGenerationModelList}
readonly={readonly}
triggerClassName={cn(
'h-8! w-full rounded-r-none!',
@ -154,6 +163,7 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
'border border-workflow-block-parma-bg bg-workflow-block-parma-bg hover:bg-workflow-block-parma-bg',
)}
onSelect={handleChangeModel}
onOpenProviderSettings={handleOpenModelSettings}
/>
</div>
<PopoverTrigger
@ -187,8 +197,9 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
<div className="px-4 pt-2 pb-4">
<ModelSelector
defaultModel={hasSelectedModel ? { provider, model: modelId } : undefined}
modelList={activeTextGenerationModelList}
modelList={availableTextGenerationModelList}
onSelect={handleChangeModel}
onOpenProviderSettings={handleOpenModelSettings}
onHide={() => setOpen(false)}
/>
</div>
@ -249,7 +260,10 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
{debugWithMultipleModel
? t(($) => $.debugAsSingleModel, { ns: 'appDebug' })
: t(($) => $.debugAsMultipleModel, { ns: 'appDebug' })}
<ArrowNarrowLeft className="size-3 rotate-180" />
<span
aria-hidden
className="i-custom-vender-line-arrows-arrow-narrow-left size-3 rotate-180"
/>
</div>
)}
</PopoverContent>

View File

@ -33,6 +33,7 @@ type ModelSelectorProps = {
hideProviderSettingsFooter?: boolean
onConfigureEmptyState?: () => void
onOpenMarketplace?: () => void
onOpenProviderSettings?: () => void
providerSettingsSource?: 'agent'
showModelMeta?: boolean
modelPredicate?: ModelSelectorModelPredicate
@ -52,6 +53,7 @@ function ModelSelector({
hideProviderSettingsFooter,
onConfigureEmptyState,
onOpenMarketplace,
onOpenProviderSettings,
providerSettingsSource,
showModelMeta,
modelPredicate,
@ -180,6 +182,7 @@ function ModelSelector({
modelSuggestionPredicate={modelSuggestionPredicate}
onConfigureEmptyState={onConfigureEmptyState ? handleConfigureEmptyState : undefined}
onOpenMarketplace={onOpenMarketplace}
onOpenProviderSettings={onOpenProviderSettings}
onInputValueChange={setInputValue}
onHide={handleHide}
/>

View File

@ -9,7 +9,6 @@ import { StatusDot } from '@langgenius/dify-ui/status-dot'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { CreditsCoin } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
import { useModalContext } from '@/context/modal-context'
import { useProviderContext } from '@/context/provider-context'
import { useCredentialPermissions } from '@/hooks/use-credential-permissions'
@ -57,7 +56,8 @@ function PopupItem({
const updateModelProviders = useUpdateModelProviders()
const currentProvider = modelProviders.find((provider) => provider.provider === model.provider)
const { canUseCredential, canCreateCredential, canManageCredential } = useCredentialPermissions()
const canOpenCredentialDropdown = canUseCredential || canCreateCredential || canManageCredential
const canOpenCredentialDropdown =
!!currentProvider && (canUseCredential || canCreateCredential || canManageCredential)
const handleOpenModelModal = () => {
if (!canCreateCredential) return
@ -77,7 +77,8 @@ function PopupItem({
})
}
const state = useCredentialPanelState(currentProvider)
// oxlint-disable-next-line eslint-react/use-state -- This domain hook returns credential panel state, not a React useState tuple.
const credentialPanelState = useCredentialPanelState(currentProvider)
const { isChangingPriority, handleChangePriority } = useChangeProviderPriority(currentProvider)
const groupItems = useMemo(
() =>
@ -90,10 +91,11 @@ function PopupItem({
[model.models, model.provider],
)
const isUsingCredits = state.priority === 'credits'
const hasCredits = !state.isCreditsExhausted
const isApiKeyActive = state.variant === 'api-active' || state.variant === 'api-fallback'
const { credentialName } = state
const isUsingCredits = credentialPanelState.priority === 'credits'
const hasCredits = !credentialPanelState.isCreditsExhausted
const isApiKeyActive =
credentialPanelState.variant === 'api-active' || credentialPanelState.variant === 'api-fallback'
const { credentialName } = credentialPanelState
const handleCloseDropdown = useCallback(() => {
setDropdownOpen(false)
@ -129,7 +131,10 @@ function PopupItem({
{isUsingCredits ? (
hasCredits ? (
<>
<CreditsCoin className="size-3" />
<span
aria-hidden
className="i-custom-vender-line-financeandecommerce-credits-coin size-3"
/>
<span className="ml-1 truncate">
{t(($) => $['modelProvider.selector.aiCredits'], { ns: 'common' })}
</span>
@ -161,15 +166,17 @@ function PopupItem({
</button>
}
/>
<PopoverContent placement="bottom-end">
<DropdownContent
provider={currentProvider}
state={state}
isChangingPriority={isChangingPriority}
onChangePriority={handleChangePriority}
onClose={handleCloseDropdown}
/>
</PopoverContent>
{currentProvider && (
<PopoverContent placement="bottom-end">
<DropdownContent
provider={currentProvider}
state={credentialPanelState}
isChangingPriority={isChangingPriority}
onChangePriority={handleChangePriority}
onClose={handleCloseDropdown}
/>
</PopoverContent>
)}
</Popover>
</div>
{!collapsed &&
@ -215,7 +222,7 @@ function PopupItem({
</ModelName>
</div>
{defaultModel?.model === modelItem.model &&
defaultModel.provider === currentProvider.provider && (
defaultModel.provider === model.provider && (
<ComboboxItemIndicator className="shrink-0 text-text-accent">
<span
className="i-custom-vender-line-general-check size-4"

View File

@ -70,6 +70,7 @@ export type PopupProps = {
onConfigureEmptyState?: () => void
onInputValueChange: (value: string) => void
onOpenMarketplace?: () => void
onOpenProviderSettings?: () => void
onHide: () => void
}
function Popup({
@ -84,6 +85,7 @@ function Popup({
onConfigureEmptyState,
onInputValueChange,
onOpenMarketplace,
onOpenProviderSettings,
onHide,
}: PopupProps) {
const { t } = useTranslation()
@ -250,11 +252,16 @@ function Popup({
const handleOpenSettings = useCallback(() => {
onHide()
if (onOpenProviderSettings) {
onOpenProviderSettings()
return
}
openIntegrationsSetting({
payload: ACCOUNT_SETTING_TAB.PROVIDER,
source: providerSettingsSource,
})
}, [onHide, openIntegrationsSetting, providerSettingsSource])
}, [onHide, onOpenProviderSettings, openIntegrationsSetting, providerSettingsSource])
const handleClosePreviewCard = useCallback(() => {
previewCardHandle.close()
}, [previewCardHandle])

View File

@ -64,6 +64,16 @@ export const MAIN_NAV_ROUTES = [
visibility: 'notDatasetOperator',
feature: 'agentV2',
},
{
key: 'skills',
href: '/skills',
labelKey: 'mainNav.skills',
active: (path: string) => isPathUnderRoute(path, '/skills'),
icon: 'i-ri-box-3-line',
activeIcon: 'i-ri-box-3-fill',
visibility: 'notDatasetOperator',
feature: 'agentV2',
},
{
key: 'datasets',
href: '/datasets',

View File

@ -119,8 +119,10 @@ describe('ConversationVariableModal', () => {
})
expect(screen.getAllByText('session_state')).toHaveLength(2)
expect(screen.getByText((content) => content.includes('formatted-100'))).toBeInTheDocument()
expect(screen.getByTestId('conversation-code-editor')).toHaveTextContent('{"latest":1}')
expect(
await screen.findByText((content) => content.includes('formatted-100')),
).toBeInTheDocument()
expect(await screen.findByTestId('conversation-code-editor')).toHaveTextContent('{"latest":1}')
await user.click(screen.getByText('summary'))
expect(screen.getByText('latest text')).toBeInTheDocument()

View File

@ -1,3 +1,4 @@
import type { SkillResponse } from '@dify/contracts/api/console/workspaces/types.gen'
import type { AgentConfigApiContext } from '../../config-context'
import type { AgentSoulConfigFormState } from '@/features/agent-v2/agent-composer/form-state'
import { toast } from '@langgenius/dify-ui/toast'
@ -39,10 +40,16 @@ type ConfigSkillDownloadQueryOptionsInput = {
}
const mocks = vi.hoisted(() => ({
agentSkillBindingsKey: vi.fn((_options: unknown): unknown[] => ['workspace-agent-skills']),
agentSkillBindingsQueryOptions: vi.fn((_options: unknown) => ({})),
deleteSkillMutationFn: vi.fn(async (_input: unknown) => ({
removed_names: ['Tender Analyzer'],
result: 'success',
})),
replaceAgentSkillBindingsMutationFn: vi.fn(async (input: { body: { skill_ids?: string[] } }) => ({
agent_id: 'agent-1',
skill_ids: input.body.skill_ids ?? [],
})),
uploadSkillMutationFn: vi.fn(async (_input: unknown) => ({
config_version: { id: 'draft-1', kind: 'draft', writable: true },
skill: {
@ -59,6 +66,8 @@ const mocks = vi.hoisted(() => ({
inspectQueryOptions: vi.fn((_options: ConfigSkillInspectQueryOptionsInput) => ({})),
previewQueryOptions: vi.fn((_options: ConfigSkillFileQueryOptionsInput) => ({})),
downloadQueryOptions: vi.fn((_options: ConfigSkillFileQueryOptionsInput) => ({})),
workspaceSkillsQueryOptions: vi.fn((_options: unknown) => ({})),
workspaceSkillsInfiniteOptions: vi.fn((_options: unknown) => ({})),
downloadBlob: vi.fn(),
downloadUrl: vi.fn(),
}))
@ -159,9 +168,43 @@ vi.mock('@/service/client', () => ({
},
},
},
workspaces: {
current: {
agents: {
byAgentId: {
skills: {
get: {
key: mocks.agentSkillBindingsKey,
queryOptions: mocks.agentSkillBindingsQueryOptions,
},
put: {
mutationOptions: () => ({ mutationFn: mocks.replaceAgentSkillBindingsMutationFn }),
},
},
},
},
skills: {
get: {
queryOptions: mocks.workspaceSkillsQueryOptions,
infiniteOptions: mocks.workspaceSkillsInfiniteOptions,
},
},
},
},
},
}))
async function openUploadSkillDialog(user: ReturnType<typeof userEvent.setup>) {
await user.click(
screen.getByRole('button', { name: /agentV2\.agentDetail\.configure\.skills\.add/i }),
)
await user.click(
screen.getByRole('button', {
name: /agentV2\.agentDetail\.configure\.skills\.addMenu\.upload\.label/i,
}),
)
}
function ConfigSnapshotProbe() {
const draft = useAtomValue(agentComposerDraftAtom)
const configSnapshot = formStateToAgentSoulConfig({ formState: draft })
@ -169,6 +212,23 @@ function ConfigSnapshotProbe() {
return <pre aria-label="config snapshot">{JSON.stringify(configSnapshot)}</pre>
}
function createWorkspaceSkill(overrides: Partial<SkillResponse> = {}): SkillResponse {
return {
id: 'workspace-skill-1',
name: 'refund-approval',
display_name: 'Refund approval',
description: 'Handle refund requests.',
icon: '💳',
latest_published_version_id: 'version-1',
reference_count: 0,
tags: [],
visibility: 'workspace',
created_at: 1,
updated_at: 1,
...overrides,
}
}
function renderAgentSkills({
initialDraft = {
...defaultAgentSoulConfigFormState,
@ -212,6 +272,22 @@ function renderAgentSkills({
describe('AgentSkills', () => {
beforeEach(() => {
vi.clearAllMocks()
mocks.agentSkillBindingsKey.mockImplementation((options) => {
const { input } = options as { input: { params: { agent_id: string } } }
return ['workspace-agent-skills', input]
})
mocks.agentSkillBindingsQueryOptions.mockImplementation((options) => {
const { input } = options as { input: { params: { agent_id: string } } }
return {
queryKey: ['workspace-agent-skills', input],
queryFn: async () => ({
agent_id: input.params.agent_id,
skill_ids: [],
data: [],
}),
}
})
mocks.inspectQueryOptions.mockImplementation(({ input }) => ({
queryKey: ['inspect-skill', input],
queryFn: async () => ({
@ -266,6 +342,38 @@ describe('AgentSkills', () => {
url: `https://example.com/${input.params.name}.skill`,
}),
}))
mocks.workspaceSkillsQueryOptions.mockImplementation((options) => {
const { input } = options as { input: { query?: { keyword?: string } } }
return {
queryKey: ['workspace-skills', input],
queryFn: async () => ({
data: [],
}),
}
})
mocks.workspaceSkillsInfiniteOptions.mockImplementation((options) => {
const { input, getNextPageParam, initialPageParam } = options as {
input: (pageParam: number) => {
query?: { keyword?: string; limit?: number; page?: number }
}
getNextPageParam: (lastPage: { has_more?: boolean; page?: number }) => number | undefined
initialPageParam: number
}
return {
queryKey: ['workspace-skills', input(initialPageParam)],
queryFn: async ({ pageParam = initialPageParam }: { pageParam?: number }) => ({
data: [],
has_more: false,
limit: input(pageParam).query?.limit ?? 20,
page: pageParam,
total: 0,
}),
getNextPageParam,
initialPageParam,
}
})
})
it('should prevent missing skills from being previewed or downloaded', async () => {
@ -340,9 +448,7 @@ describe('AgentSkills', () => {
const user = userEvent.setup()
renderAgentSkills({ initialDraft: defaultAgentSoulConfigFormState })
await user.click(
screen.getByRole('button', { name: /agentV2\.agentDetail\.configure\.skills\.add/i }),
)
await openUploadSkillDialog(user)
const input = await waitFor(() => {
const element = document.querySelector('input[type="file"]')
@ -386,13 +492,375 @@ describe('AgentSkills', () => {
expect(toast.success).toHaveBeenCalled()
})
it('should hide skill package guidance before an upload fails', async () => {
it('should bind workspace skills without adding them to inline config skills', async () => {
const user = userEvent.setup()
mocks.workspaceSkillsInfiniteOptions.mockImplementation((options) => {
const { input, getNextPageParam, initialPageParam } = options as {
input: (pageParam: number) => {
query?: { keyword?: string; limit?: number; page?: number }
}
getNextPageParam: (lastPage: { has_more?: boolean; page?: number }) => number | undefined
initialPageParam: number
}
return {
queryKey: ['workspace-skills', input(initialPageParam)],
queryFn: async ({ pageParam = initialPageParam }: { pageParam?: number }) => ({
data: [
{
id: 'workspace-skill-1',
name: 'refund-approval',
display_name: 'Refund approval',
description: 'Handle refund requests.',
icon: '💳',
latest_published_version_id: 'version-1',
reference_count: 0,
tags: [],
visibility: 'workspace',
created_at: 1,
updated_at: 1,
},
],
has_more: false,
limit: 20,
page: pageParam,
total: 1,
}),
getNextPageParam,
initialPageParam,
}
})
renderAgentSkills({ initialDraft: defaultAgentSoulConfigFormState })
await user.click(
screen.getByRole('button', { name: /agentV2\.agentDetail\.configure\.skills\.add/i }),
)
await user.click(
screen.getByRole('button', {
name: /agentV2\.agentDetail\.configure\.skills\.addMenu\.workspace\.label/i,
}),
)
await user.click(await screen.findByRole('button', { name: /Refund approval/ }))
await waitFor(() => {
expect(mocks.replaceAgentSkillBindingsMutationFn.mock.calls[0]?.[0]).toEqual({
params: {
agent_id: 'agent-1',
},
body: {
skill_ids: ['workspace-skill-1'],
},
})
})
const snapshot = JSON.parse(screen.getByTestId('config-snapshot-probe').textContent ?? '{}')
expect(snapshot.config_skills).toEqual([])
})
it('should allow workflow agent nodes to bind workspace skills', async () => {
const user = userEvent.setup()
mocks.workspaceSkillsInfiniteOptions.mockImplementation((options) => {
const { input, getNextPageParam, initialPageParam } = options as {
input: (pageParam: number) => {
query?: { keyword?: string; limit?: number; page?: number }
}
getNextPageParam: (lastPage: { has_more?: boolean; page?: number }) => number | undefined
initialPageParam: number
}
return {
queryKey: ['workspace-skills', input(initialPageParam)],
queryFn: async ({ pageParam = initialPageParam }: { pageParam?: number }) => ({
data: [
{
id: 'workspace-skill-1',
name: 'refund-approval',
display_name: 'Refund approval',
description: 'Handle refund requests.',
icon: '💳',
latest_published_version_id: 'version-1',
reference_count: 0,
tags: [],
visibility: 'workspace',
created_at: 1,
updated_at: 1,
},
],
has_more: false,
limit: 20,
page: pageParam,
total: 1,
}),
getNextPageParam,
initialPageParam,
}
})
renderAgentSkills({
initialDraft: defaultAgentSoulConfigFormState,
apiContext: {
agentId: 'workflow-agent-1',
draftType: 'draft',
workflow: {
appId: 'workflow-app-1',
nodeId: 'agent-node-1',
},
},
})
await user.click(
screen.getByRole('button', { name: /agentV2\.agentDetail\.configure\.skills\.add/i }),
)
const workspaceMenuItem = screen.getByRole('button', {
name: /agentV2\.agentDetail\.configure\.skills\.addMenu\.workspace\.label/i,
})
expect(workspaceMenuItem).not.toBeDisabled()
await user.click(workspaceMenuItem)
await user.click(await screen.findByRole('button', { name: /Refund approval/ }))
await waitFor(() => {
expect(mocks.replaceAgentSkillBindingsMutationFn.mock.calls[0]?.[0]).toEqual({
params: {
agent_id: 'workflow-agent-1',
},
body: {
skill_ids: ['workspace-skill-1'],
},
})
})
})
it('should mark already bound workspace skills as added and prevent duplicate binding', async () => {
const user = userEvent.setup()
mocks.agentSkillBindingsQueryOptions.mockImplementation((options) => {
const { input } = options as { input: { params: { agent_id: string } } }
return {
queryKey: ['workspace-agent-skills', input],
queryFn: async () => ({
agent_id: input.params.agent_id,
skill_ids: ['workspace-skill-1'],
data: [
{
...createWorkspaceSkill(),
priority: 0,
status: 'published',
file_count: 1,
latest_published_at: 1,
},
],
}),
}
})
mocks.workspaceSkillsInfiniteOptions.mockImplementation((options) => {
const { input, getNextPageParam, initialPageParam } = options as {
input: (pageParam: number) => {
query?: { keyword?: string; limit?: number; page?: number }
}
getNextPageParam: (lastPage: { has_more?: boolean; page?: number }) => number | undefined
initialPageParam: number
}
return {
queryKey: ['workspace-skills', input(initialPageParam)],
queryFn: async ({ pageParam = initialPageParam }: { pageParam?: number }) => ({
data: [
createWorkspaceSkill(),
createWorkspaceSkill({
id: 'draft-skill',
name: 'draft-skill',
display_name: 'Draft skill',
latest_published_version_id: null,
}),
],
has_more: false,
limit: 20,
page: pageParam,
total: 2,
}),
getNextPageParam,
initialPageParam,
}
})
renderAgentSkills({ initialDraft: defaultAgentSoulConfigFormState })
await user.click(
screen.getByRole('button', { name: /agentV2\.agentDetail\.configure\.skills\.add/i }),
)
await user.click(
screen.getByRole('button', {
name: /agentV2\.agentDetail\.configure\.skills\.addMenu\.workspace\.label/i,
}),
)
expect(
await screen.findByText('agentV2.agentDetail.configure.skills.workspaceSelector.added'),
).toBeInTheDocument()
expect(
screen.getByText('agentV2.agentDetail.configure.skills.workspaceSelector.draft'),
).toBeInTheDocument()
const addedSkillButton = screen
.getByText('agentV2.agentDetail.configure.skills.workspaceSelector.added')
.closest('button')
const draftSkillButton = screen
.getByText('agentV2.agentDetail.configure.skills.workspaceSelector.draft')
.closest('button')
expect(addedSkillButton).toBeDisabled()
expect(draftSkillButton).toBeDisabled()
expect(mocks.replaceAgentSkillBindingsMutationFn).not.toHaveBeenCalled()
})
it('should fetch the next workspace skill page when scrolling the selector', async () => {
const user = userEvent.setup()
mocks.workspaceSkillsInfiniteOptions.mockImplementation((options) => {
const { input, getNextPageParam, initialPageParam } = options as {
input: (pageParam: number) => {
query?: { keyword?: string; limit?: number; page?: number }
}
getNextPageParam: (lastPage: { has_more?: boolean; page?: number }) => number | undefined
initialPageParam: number
}
return {
queryKey: ['workspace-skills', input(initialPageParam)],
queryFn: async ({ pageParam = initialPageParam }: { pageParam?: number }) => ({
data:
pageParam === 1
? [createWorkspaceSkill()]
: [
createWorkspaceSkill({
id: 'workspace-skill-2',
name: 'sales-follow-up',
display_name: 'Sales follow-up',
}),
],
has_more: pageParam === 1,
limit: 20,
page: pageParam,
total: 2,
}),
getNextPageParam,
initialPageParam,
}
})
renderAgentSkills({ initialDraft: defaultAgentSoulConfigFormState })
await user.click(
screen.getByRole('button', { name: /agentV2\.agentDetail\.configure\.skills\.add/i }),
)
await user.click(
screen.getByRole('button', {
name: /agentV2\.agentDetail\.configure\.skills\.addMenu\.workspace\.label/i,
}),
)
await waitFor(() => {
expect(screen.getAllByText('Refund approval').length).toBeGreaterThan(1)
})
const scrollContainer = document.querySelector('.overflow-y-auto')
expect(scrollContainer).not.toBeNull()
Object.defineProperties(scrollContainer!, {
clientHeight: { configurable: true, value: 100 },
scrollHeight: { configurable: true, value: 160 },
scrollTop: { configurable: true, value: 80 },
})
fireEvent.scroll(scrollContainer!)
expect(await screen.findByText('Sales follow-up')).toBeInTheDocument()
})
it('should remove workspace skill bindings from the configured agent', async () => {
const user = userEvent.setup()
mocks.agentSkillBindingsQueryOptions.mockImplementation((options) => {
const { input } = options as { input: { params: { agent_id: string } } }
return {
queryKey: ['workspace-agent-skills', input],
queryFn: async () => ({
agent_id: input.params.agent_id,
skill_ids: ['workspace-skill-1'],
data: [
{
...createWorkspaceSkill(),
priority: 0,
status: 'published',
file_count: 1,
latest_published_at: 1,
},
],
}),
}
})
renderAgentSkills({ initialDraft: defaultAgentSoulConfigFormState })
await user.click(
await screen.findByRole('button', {
name: 'agentV2.agentDetail.configure.skills.moreActions:{"name":"Refund approval"}',
}),
)
await user.click(await screen.findByText('agentV2.agentDetail.configure.skills.removeAction'))
await waitFor(() => {
expect(mocks.replaceAgentSkillBindingsMutationFn.mock.calls[0]?.[0]).toEqual({
params: {
agent_id: 'agent-1',
},
body: {
skill_ids: [],
},
})
})
expect(toast.success).toHaveBeenCalledWith(
'agentV2.agentDetail.configure.skills.workspaceSelector.removeSuccess',
)
})
it('should open workspace skill details in a new tab from the row menu', async () => {
const user = userEvent.setup()
const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null)
mocks.agentSkillBindingsQueryOptions.mockImplementation((options) => {
const { input } = options as { input: { params: { agent_id: string } } }
return {
queryKey: ['workspace-agent-skills', input],
queryFn: async () => ({
agent_id: input.params.agent_id,
skill_ids: ['workspace-skill-1'],
data: [
{
...createWorkspaceSkill(),
priority: 0,
status: 'published',
file_count: 1,
latest_published_at: 1,
},
],
}),
}
})
renderAgentSkills({ initialDraft: defaultAgentSoulConfigFormState })
await user.click(
await screen.findByRole('button', {
name: 'agentV2.agentDetail.configure.skills.moreActions:{"name":"Refund approval"}',
}),
)
await user.click(await screen.findByText('agentV2.agentDetail.configure.skills.openInLibrary'))
expect(openSpy).toHaveBeenCalledWith(
'/skills/workspace-skill-1',
'_blank',
'noopener,noreferrer',
)
})
it('should hide skill package guidance before an upload fails', async () => {
const user = userEvent.setup()
renderAgentSkills({ initialDraft: defaultAgentSoulConfigFormState })
await openUploadSkillDialog(user)
expect(
screen.queryByText('agentV2.agentDetail.configure.skills.upload.warning.specification'),
@ -406,9 +874,7 @@ describe('AgentSkills', () => {
.mockImplementationOnce(() => new Promise<never>(() => undefined))
renderAgentSkills({ initialDraft: defaultAgentSoulConfigFormState })
await user.click(
screen.getByRole('button', { name: /agentV2\.agentDetail\.configure\.skills\.add/i }),
)
await openUploadSkillDialog(user)
const input = await waitFor(() => {
const element = document.querySelector('input[type="file"]')
expect(element).not.toBeNull()
@ -445,9 +911,7 @@ describe('AgentSkills', () => {
mocks.uploadSkillMutationFn.mockRejectedValueOnce(new Error('Backend upload error'))
renderAgentSkills({ initialDraft: defaultAgentSoulConfigFormState })
await user.click(
screen.getByRole('button', { name: /agentV2\.agentDetail\.configure\.skills\.add/i }),
)
await openUploadSkillDialog(user)
const input = await waitFor(() => {
const element = document.querySelector('input[type="file"]')
@ -483,9 +947,7 @@ describe('AgentSkills', () => {
},
})
await user.click(
screen.getByRole('button', { name: /agentV2\.agentDetail\.configure\.skills\.add/i }),
)
await openUploadSkillDialog(user)
const input = await waitFor(() => {
const element = document.querySelector('input[type="file"]')
expect(element).not.toBeNull()

View File

@ -1,30 +1,384 @@
'use client'
import type {
AgentSkillBindingItemResponse,
SkillResponse,
} from '@dify/contracts/api/console/workspaces/types.gen'
import type { UIEvent } from 'react'
import type { AgentOrchestrateAddActionOptions } from '../add-actions-context'
import type { AgentSkill } from '@/features/agent-v2/agent-composer/form-state'
import { useMutation } from '@tanstack/react-query'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@langgenius/dify-ui/dropdown-menu'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { toast } from '@langgenius/dify-ui/toast'
import {
keepPreviousData,
useInfiniteQuery,
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/react-query'
import { useDebounce } from 'ahooks'
import { useAtomValue, useSetAtom } from 'jotai'
import { useCallback, useRef, useState } from 'react'
import { useCallback, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { SearchInput } from '@/app/components/base/search-input'
import { SkeletonRectangle } from '@/app/components/base/skeleton'
import {
agentComposerSkillsAtom,
removeAgentSkillAtom,
upsertAgentSkillAtom,
} from '@/features/agent-v2/agent-composer/store-modules/skills'
import Link from '@/next/link'
import { consoleQuery } from '@/service/client'
import { useRegisterAgentOrchestrateAddAction } from '../add-actions-context'
import { ConfigureSectionAddButton } from '../common/add-button'
import { ConfigureSectionEmpty } from '../common/empty'
import { ConfigureSection } from '../common/section'
import { AgentConfigureTipContent } from '../common/tip-content'
import { useAgentConfigApiContext } from '../config-context'
import { useAgentOrchestrateReadOnly } from '../read-only-context'
import { AgentSkillItem } from './item'
import { AgentSkillUploadDialog } from './upload-dialog'
const WORKSPACE_SKILLS_PAGE_SIZE = 20
function AgentSkillAddMenuItem({
badge,
description,
disabled,
iconClassName,
label,
onClick,
}: {
badge?: string
description: string
disabled?: boolean
iconClassName: string
label: string
onClick: () => void
}) {
return (
<button
type="button"
disabled={disabled}
onClick={onClick}
className="flex w-full min-w-0 items-start gap-3 rounded-lg px-2 py-2 text-left outline-hidden hover:not-disabled:bg-state-base-hover focus-visible:bg-state-base-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid disabled:cursor-not-allowed disabled:opacity-50"
>
<span
aria-hidden
className={cn('mt-0.5 size-4 shrink-0 text-text-tertiary', iconClassName)}
/>
<span className="flex min-w-0 flex-1 flex-col gap-0.5">
<span className="flex min-w-0 items-center gap-2">
<span className="truncate system-sm-medium text-text-secondary">{label}</span>
{badge && (
<span className="shrink-0 rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-1.5 py-0.5 system-2xs-medium-uppercase text-text-tertiary">
{badge}
</span>
)}
</span>
<span className="line-clamp-2 system-xs-regular text-text-tertiary">{description}</span>
</span>
</button>
)
}
function WorkspaceSkillIcon({ icon }: { icon?: string }) {
return (
<span className="flex size-5 shrink-0 items-center justify-center rounded-md border-[0.5px] border-divider-subtle bg-background-default-dodge">
{icon ? (
<span className="text-[12px] leading-none">{icon}</span>
) : (
<span aria-hidden className="i-ri-box-3-line size-3.5 text-text-tertiary" />
)}
</span>
)
}
function WorkspaceSkillRow({
disabled,
isAdded,
isPending,
onSelect,
onPreview,
selected,
skill,
}: {
disabled: boolean
isAdded: boolean
isPending: boolean
onSelect: (skill: SkillResponse) => void
onPreview: (skill: SkillResponse) => void
selected: boolean
skill: SkillResponse
}) {
const { t } = useTranslation('agentV2')
return (
<button
type="button"
disabled={disabled || isAdded || isPending}
onClick={() => onSelect(skill)}
onFocus={() => onPreview(skill)}
onMouseEnter={() => onPreview(skill)}
className={cn(
'flex h-12 w-full min-w-0 items-center gap-2 rounded-lg px-2 text-left outline-hidden hover:not-disabled:bg-state-base-hover focus-visible:bg-state-base-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid disabled:cursor-default disabled:opacity-60',
selected && 'bg-state-base-hover',
)}
>
<WorkspaceSkillIcon icon={skill.icon} />
<span className="flex w-0 min-w-0 flex-1 flex-col gap-0.5">
<span className="truncate system-sm-medium text-text-secondary">{skill.display_name}</span>
<span className="truncate system-xs-regular text-text-tertiary">{skill.name}</span>
</span>
{isAdded && (
<span className="shrink-0 system-xs-medium text-text-tertiary">
{t(($) => $['agentDetail.configure.skills.workspaceSelector.added'])}
</span>
)}
{!isAdded && disabled && (
<span className="shrink-0 system-xs-medium text-text-tertiary">
{t(($) => $['agentDetail.configure.skills.workspaceSelector.draft'])}
</span>
)}
</button>
)
}
function WorkspaceSkillPreview({ skill }: { skill?: SkillResponse }) {
const { t } = useTranslation('agentV2')
if (!skill) {
return (
<div className="flex h-full items-center justify-center px-6 text-center system-xs-regular text-text-tertiary">
{t(($) => $['agentDetail.configure.skills.workspaceSelector.empty'])}
</div>
)
}
return (
<div className="flex h-full flex-col gap-4 p-4">
<div className="flex min-w-0 items-start gap-3">
<WorkspaceSkillIcon icon={skill.icon} />
<div className="min-w-0 flex-1">
<div className="truncate system-md-semibold text-text-primary">{skill.display_name}</div>
<div className="mt-0.5 truncate system-xs-regular text-text-tertiary">{skill.name}</div>
</div>
</div>
{!!skill.tags?.length && (
<div className="flex flex-wrap gap-1">
{skill.tags.slice(0, 5).map((tag) => (
<span
key={tag}
className="rounded-[5px] border border-divider-subtle bg-components-badge-bg-dimm px-1.5 py-0.5 system-2xs-medium-uppercase text-text-tertiary"
>
{tag}
</span>
))}
</div>
)}
<p className="line-clamp-6 system-sm-regular text-text-secondary">{skill.description}</p>
{(skill.updated_by_name || skill.created_by_name) && (
<div className="mt-auto system-xs-regular text-text-tertiary">
{skill.updated_by_name || skill.created_by_name}
</div>
)}
</div>
)
}
function WorkspaceSkillSelector({
boundSkillIds,
isBindingPending,
onSelect,
}: {
boundSkillIds: string[]
isBindingPending: boolean
onSelect: (skill: SkillResponse) => void
}) {
const { t } = useTranslation('agentV2')
const [keyword, setKeyword] = useState('')
const [previewSkillId, setPreviewSkillId] = useState<string | undefined>(undefined)
const debouncedKeyword = useDebounce(keyword.trim(), { wait: 300 })
const skillsQuery = useInfiniteQuery({
...consoleQuery.workspaces.current.skills.get.infiniteOptions({
input: (pageParam) => ({
query: {
limit: WORKSPACE_SKILLS_PAGE_SIZE,
page: Number(pageParam),
...(debouncedKeyword ? { keyword: debouncedKeyword } : {}),
},
}),
getNextPageParam: (lastPage) => (lastPage.has_more ? (lastPage.page ?? 1) + 1 : undefined),
initialPageParam: 1,
placeholderData: keepPreviousData,
}),
})
const boundSkillIdSet = useMemo(() => new Set(boundSkillIds), [boundSkillIds])
const skills = skillsQuery.data?.pages.flatMap((page) => page.data ?? []) ?? []
const previewSkill = skills.find((skill) => skill.id === previewSkillId) ?? skills[0]
const hasNextPage = skillsQuery.hasNextPage ?? false
const isFetchingNextPage = skillsQuery.isFetchingNextPage
const fetchNextPage = skillsQuery.fetchNextPage
const handleListScroll = useCallback(
(event: UIEvent<HTMLDivElement>) => {
const target = event.currentTarget
const scrollBottom = target.scrollHeight - target.scrollTop - target.clientHeight
if (scrollBottom < 80 && hasNextPage && !isFetchingNextPage) void fetchNextPage()
},
[fetchNextPage, hasNextPage, isFetchingNextPage],
)
return (
<div className="flex h-[520px] w-[560px] overflow-hidden rounded-xl border border-divider-regular bg-components-panel-bg shadow-lg">
<div className="flex min-w-0 flex-1 flex-col border-r border-divider-subtle">
<div className="border-b border-divider-subtle p-3">
<div className="relative">
<SearchInput
value={keyword}
onValueChange={setKeyword}
placeholder={t(($) => $['agentDetail.configure.skills.workspaceSelector.search'])}
/>
<span
aria-hidden
className="pointer-events-none absolute top-1/2 right-8 i-ri-price-tag-3-line size-4 -translate-y-1/2 text-text-tertiary"
/>
</div>
</div>
<div className="min-h-0 flex-1 overflow-y-auto p-1.5" onScroll={handleListScroll}>
{skillsQuery.isPending && (
<div className="space-y-2 p-1">
<SkeletonRectangle className="h-10 rounded-lg" />
<SkeletonRectangle className="h-10 rounded-lg" />
<SkeletonRectangle className="h-10 rounded-lg" />
</div>
)}
{!skillsQuery.isPending && skills.length === 0 && (
<div className="flex h-full items-center justify-center px-4 text-center system-xs-regular text-text-tertiary">
{t(($) => $['agentDetail.configure.skills.workspaceSelector.empty'])}
</div>
)}
{!skillsQuery.isPending &&
skills.map((skill) => (
<WorkspaceSkillRow
key={skill.id}
disabled={!skill.latest_published_version_id}
isAdded={boundSkillIdSet.has(skill.id)}
isPending={isBindingPending}
selected={previewSkill?.id === skill.id}
skill={skill}
onPreview={(skill) => setPreviewSkillId(skill.id)}
onSelect={onSelect}
/>
))}
{skillsQuery.isFetchingNextPage && (
<div className="space-y-2 p-1">
<SkeletonRectangle className="h-10 rounded-lg" />
<SkeletonRectangle className="h-10 rounded-lg" />
</div>
)}
</div>
<Link
href="/skills"
className="flex h-10 items-center justify-between border-t border-divider-subtle px-3 system-sm-medium text-text-secondary outline-hidden hover:bg-state-base-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid"
>
<span>{t(($) => $['agentDetail.configure.skills.workspaceSelector.manage'])}</span>
<span aria-hidden className="i-ri-arrow-right-up-line size-4 text-text-tertiary" />
</Link>
</div>
<div className="w-[240px] shrink-0 bg-background-default">
<WorkspaceSkillPreview skill={previewSkill} />
</div>
</div>
)
}
function WorkspaceAgentSkillItem({
skill,
onRemove,
}: {
skill: AgentSkillBindingItemResponse
onRemove: (skillId: string) => void
}) {
const { t } = useTranslation('agentV2')
const readOnly = useAgentOrchestrateReadOnly()
const displayName = skill.display_name || skill.name
const handleOpenInLibrary = useCallback(() => {
window.open(`/skills/${skill.id}`, '_blank', 'noopener,noreferrer')
}, [skill.id])
return (
<div className="group relative h-8 overflow-hidden rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg shadow-xs shadow-shadow-shadow-3 hover:bg-components-panel-on-panel-item-bg-hover hover:shadow-sm">
<Link
href={`/skills/${skill.id}`}
target="_blank"
rel="noreferrer"
className="flex h-full w-full min-w-0 cursor-pointer items-center gap-1 rounded-lg py-1 pr-8 pl-2 text-left outline-hidden select-none focus-visible:inset-ring-2 focus-visible:inset-ring-state-accent-solid"
>
<WorkspaceSkillIcon icon={skill.icon} />
<span className="flex w-0 min-w-0 flex-1 items-center gap-1">
<span className="min-w-0 truncate system-sm-medium text-text-secondary">
{displayName}
</span>
<span
aria-hidden
className="i-ri-arrow-right-up-line size-3.5 shrink-0 text-text-quaternary opacity-0 group-focus-within:opacity-100 group-hover:opacity-100"
/>
</span>
<span
className={cn(
'shrink-0 system-xs-regular text-text-tertiary',
!readOnly && 'group-focus-within:opacity-0 group-hover:opacity-0',
)}
>
{skill.name}
</span>
</Link>
<DropdownMenu modal={false}>
<DropdownMenuTrigger
aria-label={t(($) => $['agentDetail.configure.skills.moreActions'], {
name: displayName,
})}
className="absolute top-1/2 right-1 z-10 flex size-6 -translate-y-1/2 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden data-popup-open:bg-state-base-hover data-popup-open:text-text-secondary"
onClick={(event) => event.stopPropagation()}
>
<span aria-hidden className="i-ri-more-fill size-4" />
</DropdownMenuTrigger>
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-48">
<DropdownMenuItem className="gap-2" onClick={handleOpenInLibrary}>
<span aria-hidden className="i-ri-arrow-right-up-line size-4 shrink-0" />
<span>{t(($) => $['agentDetail.configure.skills.openInLibrary'])}</span>
</DropdownMenuItem>
<DropdownMenuItem
variant="destructive"
className="gap-2"
onClick={() => onRemove(skill.id)}
>
<span aria-hidden className="i-ri-delete-bin-line size-4 shrink-0" />
<span>{t(($) => $['agentDetail.configure.skills.removeAction'])}</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
)
}
export function AgentSkills() {
const { t } = useTranslation('agentV2')
const { t: tCommon } = useTranslation('common')
const skillsTip = t(($) => $['agentDetail.configure.skills.tip'])
const skillsListId = 'agent-configure-skills-list'
const queryClient = useQueryClient()
const readOnly = useAgentOrchestrateReadOnly()
const [addMenuOpen, setAddMenuOpen] = useState(false)
const [addMenuView, setAddMenuView] = useState<'menu' | 'workspace-selector'>('menu')
const [isUploadOpen, setIsUploadOpen] = useState(false)
const promptAddCallbackRef = useRef<AgentOrchestrateAddActionOptions['onAdded']>(undefined)
const apiContext = useAgentConfigApiContext()
@ -37,6 +391,60 @@ export function AgentSkills() {
const { mutate: deleteAppSkill } = useMutation(
consoleQuery.apps.byAppId.agent.config.skills.byName.delete.mutationOptions(),
)
const agentSkillBindingsQueryOptions =
consoleQuery.workspaces.current.agents.byAgentId.skills.get.queryOptions({
input: {
params: {
agent_id: apiContext.agentId,
},
},
})
const agentSkillBindingsQuery = useQuery({
...agentSkillBindingsQueryOptions,
})
const { isPending: isReplacingAgentSkillBindings, mutate: replaceAgentSkillBindings } =
useMutation(consoleQuery.workspaces.current.agents.byAgentId.skills.put.mutationOptions())
const workspaceSkills = agentSkillBindingsQuery.data?.data ?? []
const boundSkillIds =
agentSkillBindingsQuery.data?.skill_ids ?? workspaceSkills.map((skill) => skill.id)
const hasSkills = skills.length > 0 || workspaceSkills.length > 0
const invalidateAgentSkillBindings = useCallback(() => {
void queryClient.invalidateQueries({
queryKey: consoleQuery.workspaces.current.agents.byAgentId.skills.get.key({
type: 'query',
input: {
params: {
agent_id: apiContext.agentId,
},
},
}),
})
}, [apiContext.agentId, queryClient])
const replaceWorkspaceSkillBindings = useCallback(
(skillIds: string[], onSuccess?: () => void) => {
replaceAgentSkillBindings(
{
params: {
agent_id: apiContext.agentId,
},
body: {
skill_ids: skillIds,
},
},
{
onError: () => {
toast.error(t(($) => $['agentDetail.configure.skills.workspaceSelector.saveFailed']))
},
onSuccess: () => {
invalidateAgentSkillBindings()
onSuccess?.()
},
},
)
},
[apiContext.agentId, invalidateAgentSkillBindings, replaceAgentSkillBindings, t],
)
const handleOpenUpload = useCallback((options?: AgentOrchestrateAddActionOptions) => {
promptAddCallbackRef.current = options?.onAdded
@ -44,6 +452,20 @@ export function AgentSkills() {
}, [])
useRegisterAgentOrchestrateAddAction('skills', handleOpenUpload)
const handleAddMenuOpenChange = useCallback((open: boolean) => {
setAddMenuOpen(open)
if (!open) setAddMenuView('menu')
}, [])
const handleOpenWorkspaceSelector = useCallback(() => {
setAddMenuView('workspace-selector')
}, [])
const handleOpenUploadFromMenu = useCallback(() => {
setAddMenuOpen(false)
handleOpenUpload()
}, [handleOpenUpload])
const handleUploaded = useCallback(
(skill: AgentSkill) => {
upsertAgentSkill(skill)
@ -53,11 +475,36 @@ export function AgentSkills() {
[upsertAgentSkill],
)
const handleSelectWorkspaceSkill = useCallback(
(skill: SkillResponse) => {
if (!skill.latest_published_version_id || boundSkillIds.includes(skill.id)) return
replaceWorkspaceSkillBindings([...boundSkillIds, skill.id], () => {
toast.success(t(($) => $['agentDetail.configure.skills.workspaceSelector.addSuccess']))
setAddMenuOpen(false)
setAddMenuView('menu')
})
},
[boundSkillIds, replaceWorkspaceSkillBindings, t],
)
const handleUploadOpenChange = useCallback((open: boolean) => {
if (!open) promptAddCallbackRef.current = undefined
setIsUploadOpen(open)
}, [])
const handleRemoveWorkspaceSkill = useCallback(
(skillId: string) => {
replaceWorkspaceSkillBindings(
boundSkillIds.filter((item) => item !== skillId),
() => {
toast.success(t(($) => $['agentDetail.configure.skills.workspaceSelector.removeSuccess']))
},
)
},
[boundSkillIds, replaceWorkspaceSkillBindings, t],
)
const handleRemoveSkill = useCallback(
(skillId: string) => {
const skill = skills.find((item) => item.id === skillId)
@ -113,26 +560,90 @@ export function AgentSkills() {
rootClassName="border-b border-divider-subtle pt-4"
panelContentClassName="flex flex-col gap-1 pb-4"
actions={
<ConfigureSectionAddButton
ariaLabel={t(($) => $['agentDetail.configure.skills.add'])}
onClick={() => handleOpenUpload()}
/>
!readOnly && (
<Popover open={addMenuOpen} onOpenChange={handleAddMenuOpenChange}>
<PopoverTrigger
render={
<Button
aria-label={t(($) => $['agentDetail.configure.skills.add'])}
variant="ghost"
size="small"
className="shrink-0 gap-1 px-2"
>
<span aria-hidden className="i-ri-add-line size-3.5" />
<span>{tCommon(($) => $['operation.add'])}</span>
</Button>
}
/>
<PopoverContent
placement="bottom-end"
sideOffset={4}
popupClassName={
addMenuView === 'menu'
? 'w-[320px] bg-components-panel-bg-blur p-1 shadow-lg backdrop-blur-[5px]'
: 'w-[560px] overflow-hidden border-none bg-transparent p-0 shadow-none'
}
>
{addMenuView === 'menu' ? (
<>
<AgentSkillAddMenuItem
iconClassName="i-custom-public-agent-building-blocks"
label={t(($) => $['agentDetail.configure.skills.addMenu.workspace.label'])}
description={t(
($) => $['agentDetail.configure.skills.addMenu.workspace.description'],
)}
onClick={handleOpenWorkspaceSelector}
/>
<AgentSkillAddMenuItem
badge={t(($) => $['agentDetail.configure.skills.addMenu.upload.badge'])}
iconClassName="i-ri-upload-cloud-2-line"
label={t(($) => $['agentDetail.configure.skills.addMenu.upload.label'])}
description={t(
($) => $['agentDetail.configure.skills.addMenu.upload.description'],
)}
onClick={handleOpenUploadFromMenu}
/>
</>
) : (
<WorkspaceSkillSelector
boundSkillIds={boundSkillIds}
isBindingPending={isReplacingAgentSkillBindings}
onSelect={handleSelectWorkspaceSkill}
/>
)}
</PopoverContent>
</Popover>
)
}
>
{skills.length === 0 ? (
{!hasSkills ? (
<ConfigureSectionEmpty
title={t(($) => $['agentDetail.configure.skills.empty.title'])}
description={t(($) => $['agentDetail.configure.skills.empty.description'])}
/>
) : (
skills.map((skill) => (
<AgentSkillItem
key={skill.id}
apiContext={apiContext}
skill={skill}
onRemove={handleRemoveSkill}
/>
))
<>
{workspaceSkills.length > 0 && (
<div className="px-1 pt-1 pb-0.5 system-xs-medium-uppercase text-text-tertiary">
{t(($) => $['agentDetail.configure.skills.fromSkillLibrary'])}
</div>
)}
{workspaceSkills.map((skill) => (
<WorkspaceAgentSkillItem
key={skill.id}
skill={skill}
onRemove={handleRemoveWorkspaceSkill}
/>
))}
{skills.map((skill) => (
<AgentSkillItem
key={skill.id}
apiContext={apiContext}
skill={skill}
onRemove={handleRemoveSkill}
/>
))}
</>
)}
</ConfigureSection>
<AgentSkillUploadDialog

View File

@ -0,0 +1,15 @@
# Skills
Workspace Skill management UI. This module owns the Skills list, filters, and list-level actions.
## Internal Modules
None.
## External Modules
- app/components/base/search-input
- app/components/base/skeleton
- app/components/base/tooltip
- hooks/use-document-title
- hooks/use-timestamp

View File

@ -0,0 +1,860 @@
import type {
SkillDetailResponse,
SkillReferenceResponse,
SkillVersionResponse,
} from '@dify/contracts/api/console/workspaces/types.gen'
import type { ReactNode } from 'react'
import { toast } from '@langgenius/dify-ui/toast'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import SkillDetailPage from '../detail-page'
const mocks = vi.hoisted(() => ({
fetchSkillFileBlob: vi.fn(),
publishSkillMutationFn: vi.fn(),
restoreSkillMutationFn: vi.fn(),
saveDraftFileMutationFn: vi.fn(),
sendSkillAssistMessage: vi.fn(),
skillDetail: undefined as SkillDetailResponse | undefined,
skillDetailKey: vi.fn((_options: unknown): unknown[] => ['skill-detail']),
skillDetailQueryOptions: vi.fn((_options: unknown) => ({})),
skillListKey: vi.fn((_options: unknown): unknown[] => ['skills']),
skillMetadataMutationFn: vi.fn(),
skillReferencesQueryOptions: vi.fn((_options: unknown) => ({})),
skillTagsKey: vi.fn((_options: unknown): unknown[] => ['skill-tags']),
skillVersionsKey: vi.fn((_options: unknown): unknown[] => ['skill-versions']),
skillVersionsQueryOptions: vi.fn((_options: unknown) => ({})),
skillVersionDetailQueryOptions: vi.fn((_options: unknown) => ({})),
uploadSkillFile: vi.fn(),
versionDeleteMutationFn: vi.fn(),
versionPatchMutationFn: vi.fn(),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
error: vi.fn(),
info: vi.fn(),
success: vi.fn(),
},
}))
vi.mock('@/app/components/base/markdown', () => ({
Markdown: ({ content }: { content: string }) => <div>{content}</div>,
}))
vi.mock('@/app/components/base/app-icon', () => ({
default: ({ icon }: { icon?: string }) => <span>{icon}</span>,
}))
vi.mock('@/app/components/header/account-setting/model-provider-page/hooks', () => ({
useDefaultModel: () => ({
data: {
provider: {
provider: 'langgenius/openai/openai',
},
model: 'gpt-5.5',
},
}),
useModelList: () => ({
data: [
{
provider: 'langgenius/openai/openai',
status: 'active',
models: [
{
model: 'gpt-5.5',
status: 'active',
},
],
},
],
isLoading: false,
}),
}))
vi.mock(
'@/app/components/header/account-setting/model-provider-page/model-parameter-modal',
() => ({
default: () => <button type="button">model-settings</button>,
}),
)
vi.mock('@/app/components/workflow/nodes/_base/components/editor/code-editor', () => ({
default: ({ onChange, value }: { onChange?: (value: string) => void; value: string }) => (
<textarea
aria-label="code-editor"
value={value}
onChange={(event) => onChange?.(event.target.value)}
/>
),
}))
vi.mock('@/hooks/use-document-title', () => ({
default: vi.fn(),
}))
vi.mock('@/hooks/use-format-time-from-now', () => ({
useFormatTimeFromNow: () => ({
formatTimeFromNow: () => 'just now',
}),
}))
vi.mock('@/hooks/use-timestamp', () => ({
default: () => ({
formatTime: () => '2026-07-21 12:00',
}),
}))
vi.mock('@/next/link', () => ({
default: ({ children, href, ...props }: { children: ReactNode; href: string }) => (
<a href={href} {...props}>
{children}
</a>
),
}))
vi.mock('@/next/navigation', () => ({
useParams: () => ({
skillId: 'skill-1',
}),
}))
vi.mock('@/service/client', () => ({
consoleQuery: {
workspaces: {
current: {
skills: {
get: {
key: mocks.skillListKey,
},
tags: {
get: {
key: mocks.skillTagsKey,
},
},
bySkillId: {
get: {
key: mocks.skillDetailKey,
queryOptions: mocks.skillDetailQueryOptions,
},
patch: {
mutationOptions: () => ({ mutationFn: mocks.skillMetadataMutationFn }),
},
publish: {
post: {
mutationOptions: () => ({ mutationFn: mocks.publishSkillMutationFn }),
},
},
references: {
get: {
queryOptions: mocks.skillReferencesQueryOptions,
},
},
restore: {
post: {
mutationOptions: () => ({ mutationFn: mocks.restoreSkillMutationFn }),
},
},
files: {
patch: {
mutationOptions: () => ({ mutationFn: mocks.saveDraftFileMutationFn }),
},
},
versions: {
get: {
key: mocks.skillVersionsKey,
queryOptions: mocks.skillVersionsQueryOptions,
},
byVersionId: {
get: {
queryOptions: mocks.skillVersionDetailQueryOptions,
},
patch: {
mutationOptions: () => ({ mutationFn: mocks.versionPatchMutationFn }),
},
delete: {
mutationOptions: () => ({ mutationFn: mocks.versionDeleteMutationFn }),
},
},
},
},
},
},
},
},
}))
vi.mock('../client', () => ({
fetchSkillFileBlob: mocks.fetchSkillFileBlob,
sendSkillAssistMessage: mocks.sendSkillAssistMessage,
uploadSkillFile: mocks.uploadSkillFile,
}))
function createSkillDetail(overrides: Partial<SkillDetailResponse> = {}): SkillDetailResponse {
return {
id: 'skill-1',
name: 'github-actions-failure-debugging',
display_name: 'Untitled skill',
icon: '📄',
description: 'Guide for debugging failing GitHub Actions workflows.',
tags: [],
name_manually_edited: true,
visibility: 'workspace',
latest_published_version_id: 'version-1',
reference_count: 0,
created_by: 'user-1',
created_by_name: 'Fate',
updated_by: 'user-1',
updated_by_name: 'Fate',
created_at: 1784631405,
updated_at: 1784638487,
files: [
{
id: 'file-1',
path: 'SKILL.md',
kind: 'file',
storage: 'text',
mime_type: 'text/markdown',
content:
'---\nname: github-actions-failure-debugging\ndescription: Guide for debugging failing GitHub Actions workflows.\nmetadata:\n display-name: Untitled skill\n---\n# GitHub Actions Failure Debugging\n',
tool_file_id: null,
size: 180,
hash: 'hash-1',
},
],
...overrides,
}
}
function createSkillVersion(overrides: Partial<SkillVersionResponse> = {}): SkillVersionResponse {
return {
id: 'version-1',
skill_id: 'skill-1',
version_number: 1,
version_name: 'Initial version',
publish_note: 'Original instructions',
hash_code: 'hash-code-1',
archive_size: 180,
published_by: 'user-1',
published_by_name: 'Fate',
created_at: 1784638400,
is_latest: false,
...overrides,
}
}
function createAgentReference(
overrides: Partial<SkillReferenceResponse> = {},
): SkillReferenceResponse {
return {
agent_id: 'agent-1',
agent_icon: '🤖',
agent_icon_background: '#EFF6FF',
agent_icon_type: 'emoji',
app_id: 'app-1',
display_name: 'Support Agent',
name: 'support-agent',
type: 'agent',
...overrides,
}
}
function renderSkillDetailPage() {
const queryClient = new QueryClient({
defaultOptions: {
queries: { retry: false },
mutations: { retry: false },
},
})
return render(
<QueryClientProvider client={queryClient}>
<SkillDetailPage />
</QueryClientProvider>,
)
}
function getBuilderAttachmentInput(container: HTMLElement) {
const inputs = Array.from(container.querySelectorAll<HTMLInputElement>('input[type="file"]'))
return inputs.at(-1) ?? null
}
function getSourceEditor() {
const editors = screen.getAllByRole('textbox')
const sourceEditor = editors.find(
(editor): editor is HTMLTextAreaElement =>
editor instanceof HTMLTextAreaElement &&
editor.value.includes('name: github-actions-failure-debugging'),
)
if (!sourceEditor) throw new Error('source editor not found')
return sourceEditor
}
function getFileTreeItem(path: string) {
const fileButton = document.querySelector(`[title="${path}"]`)
const treeItem = fileButton?.closest('[data-skill-file-tree-item]')
if (!(treeItem instanceof HTMLElement)) throw new Error(`file tree item not found: ${path}`)
return treeItem
}
async function openFileTreeActions(user: ReturnType<typeof userEvent.setup>, path: string) {
const treeItem = getFileTreeItem(path)
await user.click(within(treeItem).getByRole('button', { name: 'common.operation.more' }))
}
async function openRootCreateMenu(user: ReturnType<typeof userEvent.setup>) {
const triggers = Array.from(document.querySelectorAll('aside .i-ri-add-line'))
.map((icon) => icon.closest('button'))
.filter((button): button is HTMLButtonElement => button instanceof HTMLButtonElement)
const trigger = triggers.at(-1)
if (!(trigger instanceof HTMLButtonElement)) throw new Error('root create menu trigger not found')
await user.click(trigger)
}
async function openVersionRowActions(
user: ReturnType<typeof userEvent.setup>,
versionName: string,
) {
const versionText = await screen.findByText(versionName)
const versionRow = versionText.closest('li')
if (!(versionRow instanceof HTMLElement)) throw new Error(`version row not found: ${versionName}`)
const buttons = within(versionRow).getAllByRole('button')
const actionButton = buttons.at(-1)
if (!actionButton) throw new Error(`version row action not found: ${versionName}`)
await user.click(actionButton)
}
describe('SkillDetailPage', () => {
beforeEach(() => {
vi.clearAllMocks()
mocks.skillDetail = createSkillDetail()
mocks.skillDetailKey.mockImplementation((options) => ['skill-detail', options])
mocks.skillVersionsKey.mockImplementation((options) => ['skill-versions', options])
mocks.skillListKey.mockImplementation((options) => ['skills', options])
mocks.skillTagsKey.mockImplementation((options) => ['skill-tags', options])
mocks.skillDetailQueryOptions.mockImplementation((options) => ({
queryKey: ['skill-detail', options],
queryFn: async () => mocks.skillDetail,
}))
mocks.skillVersionsQueryOptions.mockImplementation((options) => ({
queryKey: ['skill-versions', options],
queryFn: async () => ({
data: [],
}),
}))
mocks.skillVersionDetailQueryOptions.mockImplementation((options) => ({
queryKey: ['skill-version-detail', options],
queryFn: async () => ({
...mocks.skillDetail,
files: [],
}),
}))
mocks.skillReferencesQueryOptions.mockImplementation((options) => ({
queryKey: ['skill-references', options],
queryFn: async () => ({
data: [],
}),
}))
mocks.saveDraftFileMutationFn.mockImplementation(
async (input: { body: { content?: string; operation: string; path: string } }) => {
if (input.body.operation !== 'upsert_text') {
const nextDetail = createSkillDetail({
updated_at: 1784638490,
})
mocks.skillDetail = {
...nextDetail,
files: mocks.skillDetail?.files ?? nextDetail.files,
}
return mocks.skillDetail
}
const nextDetail = createSkillDetail({
display_name: input.body.content?.includes('display-name: 333333333')
? '333333333'
: 'Untitled skill',
updated_at: 1784638490,
})
const nextFiles = nextDetail.files ?? []
nextFiles[0] = {
...nextFiles[0]!,
content: input.body.content ?? '',
}
nextDetail.files = nextFiles
mocks.skillDetail = nextDetail
return nextDetail
},
)
mocks.skillMetadataMutationFn.mockImplementation(
async (input: { body: { display_name?: string } }) => {
const nextDetail = createSkillDetail({
display_name: input.body.display_name ?? 'Untitled skill',
updated_at: 1784638491,
})
mocks.skillDetail = {
...nextDetail,
files: mocks.skillDetail?.files ?? nextDetail.files,
}
return nextDetail
},
)
mocks.publishSkillMutationFn.mockResolvedValue({
id: 'version-2',
version_number: 2,
version_name: '',
publish_note: '',
hash_code: 'hash-code',
archive_size: 180,
published_by: 'user-1',
published_by_name: 'Fate',
created_at: 1784638492,
is_latest: true,
})
mocks.restoreSkillMutationFn.mockResolvedValue({})
mocks.versionPatchMutationFn.mockResolvedValue({})
mocks.versionDeleteMutationFn.mockResolvedValue({})
mocks.sendSkillAssistMessage.mockResolvedValue(undefined)
mocks.uploadSkillFile.mockResolvedValue({
id: 'tool-file-1',
name: 'guide.md',
mime_type: 'text/markdown',
size: 10,
})
})
it('saves the live display name into SKILL.md before publishing', async () => {
const user = userEvent.setup()
renderSkillDetailPage()
const displayNameInput = await screen.findByDisplayValue('Untitled skill')
await user.clear(displayNameInput)
await user.type(displayNameInput, '333333333')
await user.click(screen.getByRole('button', { name: 'agentV2.skillManagement.detail.publish' }))
await waitFor(() => {
expect(mocks.saveDraftFileMutationFn).toHaveBeenCalled()
})
expect(mocks.saveDraftFileMutationFn).toHaveBeenCalledWith(
expect.objectContaining({
body: expect.objectContaining({
content: expect.stringContaining('display-name: 333333333'),
operation: 'upsert_text',
path: 'SKILL.md',
}),
}),
expect.anything(),
)
await waitFor(() => {
expect(mocks.publishSkillMutationFn).toHaveBeenCalled()
})
})
it('adds custom metadata from the value field Enter key and saves it on publish', async () => {
const user = userEvent.setup()
renderSkillDetailPage()
await user.click(
await screen.findByRole('button', {
name: 'agentV2.skillManagement.detail.addMetadata',
}),
)
await user.type(
screen.getByPlaceholderText('agentV2.skillManagement.detail.metadataKey'),
'owner',
)
await user.type(
screen.getByPlaceholderText('agentV2.skillManagement.detail.metadataValue'),
'support{Enter}',
)
expect(await screen.findByText('owner')).toBeInTheDocument()
expect(screen.getByText('support')).toBeInTheDocument()
await user.click(screen.getByRole('button', { name: 'agentV2.skillManagement.detail.publish' }))
await waitFor(() => {
expect(mocks.saveDraftFileMutationFn).toHaveBeenCalledWith(
expect.objectContaining({
body: expect.objectContaining({
content: expect.stringContaining(' owner: support'),
}),
}),
expect.anything(),
)
})
})
it('sends uploaded Skill Builder attachments without requiring typed text', async () => {
const user = userEvent.setup()
const { container } = renderSkillDetailPage()
await screen.findByText('agentV2.skillManagement.detail.builder.title')
const attachmentInput = getBuilderAttachmentInput(container)
expect(attachmentInput).not.toBeNull()
await user.upload(
attachmentInput!,
new File(['# Guide'], 'guide.md', {
type: 'text/markdown',
}),
)
expect(await screen.findByText('guide.md')).toBeInTheDocument()
await user.click(
screen.getByRole('button', {
name: 'agentV2.skillManagement.detail.builder.send',
}),
)
await waitFor(() => {
expect(mocks.sendSkillAssistMessage).toHaveBeenCalledWith(
expect.objectContaining({
skillId: 'skill-1',
message: 'agentV2.skillManagement.detail.builder.attachmentOnlyMessage',
attachments: [
{
mime_type: 'text/markdown',
name: 'guide.md',
size: 10,
tool_file_id: 'tool-file-1',
},
],
}),
)
})
})
it('rejects image attachments in Skill Builder before uploading', async () => {
const user = userEvent.setup({ applyAccept: false })
const { container } = renderSkillDetailPage()
await screen.findByText('agentV2.skillManagement.detail.builder.title')
const attachmentInput = getBuilderAttachmentInput(container)
expect(attachmentInput).not.toBeNull()
await user.upload(
attachmentInput!,
new File(['image'], 'image.png', {
type: 'image/png',
}),
)
expect(mocks.uploadSkillFile).not.toHaveBeenCalled()
expect(toast.error).toHaveBeenCalledWith(
'agentV2.skillManagement.detail.builder.attachUnsupported',
)
})
it('shows a publish confirmation for referenced skills before publishing updates', async () => {
const user = userEvent.setup()
mocks.skillDetail = createSkillDetail({ reference_count: 1 })
mocks.skillReferencesQueryOptions.mockImplementation((options) => ({
queryKey: ['skill-references', options],
queryFn: async () => ({
data: [createAgentReference()],
}),
}))
renderSkillDetailPage()
await user.click(
await screen.findByRole('button', { name: 'agentV2.skillManagement.detail.publish' }),
)
expect(
await screen.findByText('agentV2.skillManagement.detail.publishReferencesTitle'),
).toBeInTheDocument()
expect(await screen.findByText('Support Agent')).toBeInTheDocument()
expect(mocks.publishSkillMutationFn).not.toHaveBeenCalled()
await user.click(
screen.getByRole('button', { name: 'agentV2.skillManagement.detail.publishUpdate' }),
)
await waitFor(() => {
expect(mocks.publishSkillMutationFn).toHaveBeenCalled()
})
})
it('renders selected version files in read-only mode and restores that version', async () => {
const user = userEvent.setup()
const version = createSkillVersion({
id: 'version-1',
version_name: 'Rollback target',
})
mocks.skillVersionsQueryOptions.mockImplementation((options) => ({
queryKey: ['skill-versions', options],
queryFn: async () => ({
data: [version],
}),
}))
mocks.skillVersionDetailQueryOptions.mockImplementation((options) => ({
queryKey: ['skill-version-detail', options],
queryFn: async () => ({
...version,
files: [
{
id: 'version-file-1',
path: 'SKILL.md',
kind: 'file',
storage: 'text',
mime_type: 'text/markdown',
content:
'---\nname: github-actions-failure-debugging\ndescription: Old description.\nmetadata:\n display-name: Rollback skill\n---\n# Rollback instructions\n',
tool_file_id: null,
size: 140,
hash: 'version-hash-1',
},
],
}),
}))
renderSkillDetailPage()
await user.click(
await screen.findByRole('button', { name: 'agentV2.skillManagement.detail.versionHistory' }),
)
await user.click(await screen.findByRole('button', { name: /Rollback target/ }))
expect(await screen.findByText(/Rollback instructions/)).toBeInTheDocument()
await user.click(
screen.getByRole('button', { name: 'agentV2.skillManagement.detail.restoreVersion' }),
)
await waitFor(() => {
expect(mocks.restoreSkillMutationFn).toHaveBeenCalledWith(
expect.objectContaining({
body: {
version_id: 'version-1',
version_name: 'Rollback target',
},
}),
expect.anything(),
)
})
})
it('inserts a reference file from source editor slash picker', async () => {
const user = userEvent.setup()
mocks.skillDetail = createSkillDetail({
files: [
...createSkillDetail().files!,
{
id: 'file-2',
path: 'docs/guide.md',
kind: 'file',
storage: 'text',
mime_type: 'text/markdown',
content: '# Guide',
tool_file_id: null,
size: 7,
hash: 'hash-2',
},
],
})
renderSkillDetailPage()
await user.click(
await screen.findByRole('button', {
name: 'agentV2.skillManagement.detail.markdownSourceMode',
}),
)
const sourceEditor = getSourceEditor()
sourceEditor.focus()
sourceEditor.setSelectionRange(sourceEditor.value.length, sourceEditor.value.length)
await user.keyboard('/')
expect(
await screen.findByText('agentV2.skillManagement.detail.referenceFiles.title'),
).toBeInTheDocument()
await user.keyboard('{ArrowRight}{Enter}')
await waitFor(() => {
expect(sourceEditor.value).toContain('[guide.md](<docs/guide.md>)')
})
})
it('sends suggestion chips as Builder messages and blocks concurrent sends', async () => {
const user = userEvent.setup()
mocks.sendSkillAssistMessage.mockImplementation(() => new Promise<void>(() => undefined))
renderSkillDetailPage()
await user.click(
await screen.findByRole('button', {
name: 'agentV2.skillManagement.detail.builder.exampleIssueTriage',
}),
)
await waitFor(() => {
expect(mocks.sendSkillAssistMessage).toHaveBeenCalledWith(
expect.objectContaining({
message: 'agentV2.skillManagement.detail.builder.exampleIssueTriage',
}),
)
})
expect(
await screen.findByPlaceholderText(
'agentV2.skillManagement.detail.builder.modifyPlaceholder',
),
).toBeDisabled()
await user.click(
screen.getByRole('button', {
name: 'agentV2.skillManagement.detail.builder.followUpDisplayName',
}),
)
expect(mocks.sendSkillAssistMessage).toHaveBeenCalledTimes(1)
})
it('creates a folder from the root file menu', async () => {
const user = userEvent.setup()
renderSkillDetailPage()
await waitFor(() => {
expect(getFileTreeItem('SKILL.md')).toBeInTheDocument()
})
await openRootCreateMenu(user)
await user.click(await screen.findByText('agentV2.skillManagement.detail.createFolderMenu'))
const dialog = await screen.findByRole('dialog')
await user.clear(within(dialog).getByDisplayValue('new-folder'))
await user.type(within(dialog).getByRole('textbox'), 'references')
await user.click(within(dialog).getByRole('button', { name: 'common.operation.save' }))
await waitFor(() => {
expect(mocks.saveDraftFileMutationFn).toHaveBeenCalledWith(
expect.objectContaining({
body: expect.objectContaining({
expected_updated_at: 1784638487,
operation: 'mkdir',
path: 'references',
}),
}),
expect.anything(),
)
})
})
it('deletes a file through the file tree action menu', async () => {
const user = userEvent.setup()
renderSkillDetailPage()
await waitFor(() => {
expect(getFileTreeItem('SKILL.md')).toBeInTheDocument()
})
await openFileTreeActions(user, 'SKILL.md')
await user.click(await screen.findByText('common.operation.delete'))
const dialog = await screen.findByRole('alertdialog')
await user.click(within(dialog).getByRole('button', { name: 'common.operation.delete' }))
await waitFor(() => {
expect(mocks.saveDraftFileMutationFn).toHaveBeenCalledWith(
expect.objectContaining({
body: expect.objectContaining({
expected_updated_at: 1784638487,
operation: 'delete',
path: 'SKILL.md',
}),
}),
expect.anything(),
)
})
})
it('renames a version title and publish note from the version menu', async () => {
const user = userEvent.setup()
const version = createSkillVersion({
id: 'version-1',
publish_note: 'Initial note',
version_name: 'Initial version',
})
mocks.skillVersionsQueryOptions.mockImplementation((options) => ({
queryKey: ['skill-versions', options],
queryFn: async () => ({
data: [version],
}),
}))
renderSkillDetailPage()
await user.click(
await screen.findByRole('button', { name: 'agentV2.skillManagement.detail.versionHistory' }),
)
await openVersionRowActions(user, 'Initial version')
await user.click(await screen.findByText('agentV2.skillManagement.detail.nameThisVersion'))
const dialog = await screen.findByRole('dialog')
const [titleInput, noteInput] = within(dialog).getAllByRole('textbox')
if (!titleInput || !noteInput) throw new Error('version info inputs not found')
await user.clear(titleInput)
await user.type(titleInput, 'Named version')
await user.clear(noteInput)
await user.type(noteInput, 'Release note')
await user.click(
within(dialog).getByRole('button', { name: 'agentV2.skillManagement.detail.publish' }),
)
await waitFor(() => {
expect(mocks.versionPatchMutationFn).toHaveBeenCalledWith(
expect.objectContaining({
body: {
publish_note: 'Release note',
version_name: 'Named version',
},
params: {
skill_id: 'skill-1',
version_id: 'version-1',
},
}),
expect.anything(),
)
})
})
it('deletes a non-latest version from the version menu', async () => {
const user = userEvent.setup()
const version = createSkillVersion({
id: 'version-1',
is_latest: false,
version_name: 'Old version',
})
mocks.skillVersionsQueryOptions.mockImplementation((options) => ({
queryKey: ['skill-versions', options],
queryFn: async () => ({
data: [version],
}),
}))
renderSkillDetailPage()
await user.click(
await screen.findByRole('button', { name: 'agentV2.skillManagement.detail.versionHistory' }),
)
await openVersionRowActions(user, 'Old version')
await user.click(await screen.findByText('common.operation.delete'))
const dialog = await screen.findByRole('alertdialog')
await user.click(within(dialog).getByRole('button', { name: 'common.operation.delete' }))
await waitFor(() => {
expect(mocks.versionDeleteMutationFn).toHaveBeenCalledWith(
{
params: {
skill_id: 'skill-1',
version_id: 'version-1',
},
},
expect.anything(),
)
})
})
})

View File

@ -0,0 +1,378 @@
import type {
SkillResponse,
SkillTagResponse,
} from '@dify/contracts/api/console/workspaces/types.gen'
import type { ReactNode } from 'react'
import { toast } from '@langgenius/dify-ui/toast'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import SkillsPage from '../page'
const mocks = vi.hoisted(() => ({
createSkillMutationFn: vi.fn(),
deleteSkillMutationFn: vi.fn(),
duplicateSkillMutationFn: vi.fn(),
importSkillMutationFn: vi.fn(),
push: vi.fn(),
queryState: {
keyword: '',
tag: [] as string[],
},
skills: [] as SkillResponse[],
skillsKey: vi.fn((_options: unknown): unknown[] => ['skills']),
skillsQueryOptions: vi.fn((_options: unknown) => ({})),
tags: [] as SkillTagResponse[],
tagsKey: vi.fn((_options: unknown): unknown[] => ['skill-tags']),
tagsQueryOptions: vi.fn((_options: unknown) => ({})),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
error: vi.fn(),
success: vi.fn(),
},
}))
vi.mock('ahooks', () => ({
useDebounce: (value: unknown) => value,
}))
vi.mock('nuqs', async () => {
const React = await import('react')
const listeners = new Map<'keyword' | 'tag', Set<() => void>>()
const createParser = () => ({
withDefault: () => ({
withOptions: () => ({}),
}),
})
return {
debounce: () => undefined,
parseAsArrayOf: () => ({
withDefault: () => ({}),
}),
parseAsString: createParser(),
useQueryState: (name: 'keyword' | 'tag') => {
const [value, setValue] = React.useState(mocks.queryState[name])
React.useEffect(() => {
const nameListeners = listeners.get(name) ?? new Set<() => void>()
listeners.set(name, nameListeners)
const listener = () => setValue(mocks.queryState[name])
nameListeners.add(listener)
return () => {
nameListeners.delete(listener)
}
}, [name])
const setQueryValue = (nextValue: string | string[]) => {
mocks.queryState[name] = nextValue as never
setValue(nextValue as never)
listeners.get(name)?.forEach((listener) => listener())
return Promise.resolve(new URLSearchParams())
}
return [value, setQueryValue] as const
},
}
})
vi.mock('@/hooks/use-document-title', () => ({
default: vi.fn(),
}))
vi.mock('@/hooks/use-timestamp', () => ({
default: () => ({
formatTime: () => '2026-07-22 10:00',
}),
}))
vi.mock('@/next/link', () => ({
default: ({ children, href, ...props }: { children: ReactNode; href: string }) => (
<a href={href} {...props}>
{children}
</a>
),
}))
vi.mock('@/next/navigation', () => ({
useRouter: () => ({
push: mocks.push,
}),
}))
vi.mock('@/service/client', () => ({
consoleQuery: {
workspaces: {
current: {
skills: {
get: {
key: mocks.skillsKey,
queryOptions: mocks.skillsQueryOptions,
},
post: {
mutationOptions: () => ({ mutationFn: mocks.createSkillMutationFn }),
},
import: {
post: {
mutationOptions: () => ({ mutationFn: mocks.importSkillMutationFn }),
},
},
tags: {
get: {
key: mocks.tagsKey,
queryOptions: mocks.tagsQueryOptions,
},
},
bySkillId: {
delete: {
mutationOptions: () => ({ mutationFn: mocks.deleteSkillMutationFn }),
},
duplicate: {
post: {
mutationOptions: () => ({ mutationFn: mocks.duplicateSkillMutationFn }),
},
},
},
},
},
},
},
}))
function createSkill(overrides: Partial<SkillResponse> = {}): SkillResponse {
return {
id: 'skill-1',
name: 'refund-approval',
display_name: 'Refund approval',
icon: '💳',
description: 'Handle refund requests.',
tags: ['support'],
visibility: 'workspace',
latest_published_version_id: 'version-1',
reference_count: 2,
created_at: 1784631405,
updated_at: 1784638487,
...overrides,
}
}
function renderSkillsPage() {
const queryClient = new QueryClient({
defaultOptions: {
mutations: { retry: false },
queries: { retry: false },
},
})
return render(
<QueryClientProvider client={queryClient}>
<SkillsPage />
</QueryClientProvider>,
)
}
describe('SkillsPage', () => {
beforeEach(() => {
vi.clearAllMocks()
mocks.queryState.keyword = ''
mocks.queryState.tag = []
mocks.skills = [createSkill()]
mocks.tags = [
{ count: 2, tag: 'support' },
{ count: 1, tag: 'sales' },
]
mocks.skillsKey.mockImplementation((options) => ['skills', options])
mocks.tagsKey.mockImplementation((options) => ['skill-tags', options])
mocks.skillsQueryOptions.mockImplementation((options) => ({
queryKey: ['skills', options],
queryFn: async () => ({
data: mocks.skills,
has_more: false,
page: 1,
total: mocks.skills.length,
}),
}))
mocks.tagsQueryOptions.mockImplementation((options) => ({
queryKey: ['skill-tags', options],
queryFn: async () => ({
data: mocks.tags,
}),
}))
mocks.createSkillMutationFn.mockResolvedValue(createSkill({ id: 'created-skill' }))
mocks.importSkillMutationFn.mockResolvedValue(createSkill({ id: 'imported-skill' }))
mocks.duplicateSkillMutationFn.mockResolvedValue(createSkill({ id: 'duplicated-skill' }))
mocks.deleteSkillMutationFn.mockResolvedValue({
deleted: true,
id: 'skill-1',
})
})
it('renders skills with tags, reference count, and detail links', async () => {
renderSkillsPage()
const skillLink = await screen.findByRole('link', { name: /Refund approval/ })
expect(skillLink).toHaveAttribute('href', '/skills/skill-1')
expect(screen.getByText('refund-approval')).toBeInTheDocument()
expect(screen.getByText('Handle refund requests.')).toBeInTheDocument()
expect(screen.getByText('support')).toBeInTheDocument()
expect(
screen.getByText('agentV2.skillManagement.referenceCount:{"count":2}'),
).toBeInTheDocument()
})
it('passes keyword and selected tags to the list query', async () => {
const user = userEvent.setup()
renderSkillsPage()
await user.type(
await screen.findByRole('searchbox', {
name: 'agentV2.skillManagement.searchLabel',
}),
'refund',
)
await waitFor(() => {
expect(mocks.skillsQueryOptions).toHaveBeenLastCalledWith(
expect.objectContaining({
input: {
query: {
keyword: 'refund',
},
},
}),
)
})
await user.click(screen.getByRole('button', { name: 'agentV2.skillManagement.tags' }))
await waitFor(() => {
expect(screen.getAllByText('support').length).toBeGreaterThan(1)
})
await user.click(screen.getAllByText('support').at(-1)!)
await waitFor(() => {
expect(mocks.skillsQueryOptions).toHaveBeenLastCalledWith(
expect.objectContaining({
input: {
query: {
keyword: 'refund',
tag: ['support'],
},
},
}),
)
})
})
it('creates a placeholder skill and navigates to its detail page', async () => {
const user = userEvent.setup()
renderSkillsPage()
await user.click(await screen.findByRole('button', { name: 'agentV2.skillManagement.create' }))
await waitFor(() => {
expect(mocks.createSkillMutationFn).toHaveBeenCalledWith(
{
body: {},
},
expect.anything(),
)
})
expect(toast.success).toHaveBeenCalledWith('agentV2.skillManagement.createSuccess')
expect(mocks.push).toHaveBeenCalledWith('/skills/created-skill')
})
it('imports a package file and navigates to the imported skill', async () => {
const user = userEvent.setup()
const { container } = renderSkillsPage()
const fileInput = container.querySelector<HTMLInputElement>('input[type="file"]')
expect(fileInput).not.toBeNull()
const file = new File(['skill'], 'refund.skill', { type: 'application/zip' })
await user.upload(fileInput!, file)
await waitFor(() => {
expect(mocks.importSkillMutationFn).toHaveBeenCalledWith(
{
body: {
file,
},
},
expect.anything(),
)
})
expect(toast.success).toHaveBeenCalledWith('agentV2.skillManagement.importSuccess')
expect(mocks.push).toHaveBeenCalledWith('/skills/imported-skill')
})
it('duplicates a skill from the card action menu', async () => {
const user = userEvent.setup()
renderSkillsPage()
await user.click(
await screen.findByRole('button', {
name: 'agentV2.skillManagement.moreActions:{"name":"Refund approval"}',
}),
)
await user.click(await screen.findByText('common.operation.duplicate'))
await waitFor(() => {
expect(mocks.duplicateSkillMutationFn).toHaveBeenCalledWith(
{
params: {
skill_id: 'skill-1',
},
},
expect.anything(),
)
})
expect(toast.success).toHaveBeenCalledWith('agentV2.skillManagement.duplicateSuccess')
})
it('confirms deletion with the skill name and refreshes list data', async () => {
const user = userEvent.setup()
renderSkillsPage()
await user.click(
await screen.findByRole('button', {
name: 'agentV2.skillManagement.moreActions:{"name":"Refund approval"}',
}),
)
await user.click(await screen.findByText('common.operation.delete'))
const dialog = await screen.findByRole('alertdialog')
await user.click(within(dialog).getByRole('button', { name: 'common.operation.delete' }))
await waitFor(() => {
expect(mocks.deleteSkillMutationFn).toHaveBeenCalledWith(
{
body: {
confirmation_name: 'refund-approval',
},
params: {
skill_id: 'skill-1',
},
},
expect.anything(),
)
})
expect(toast.success).toHaveBeenCalledWith('agentV2.skillManagement.deleteSuccess')
})
it('shows the empty-search state without create or import actions', async () => {
mocks.queryState.keyword = 'missing'
mocks.skills = []
renderSkillsPage()
expect(await screen.findByText('agentV2.skillManagement.emptySearch')).toBeInTheDocument()
expect(
screen.queryByText('agentV2.skillManagement.emptyAction.createTitle'),
).not.toBeInTheDocument()
expect(
screen.queryByText('agentV2.skillManagement.emptyAction.importTitle'),
).not.toBeInTheDocument()
})
})

View File

@ -0,0 +1,180 @@
import type {
SkillAssistAttachmentPayload,
SkillFileUploadResponse,
} from '@dify/contracts/api/console/workspaces/types.gen'
import type {
DefaultModel,
FormValue,
} from '@/app/components/header/account-setting/model-provider-page/declarations'
// oxlint-disable-next-line no-restricted-imports
import type { IOnCompleted, IOnData, IOnError } from '@/service/base'
// oxlint-disable-next-line no-restricted-imports
import { get, post, ssePost, upload } from '@/service/base'
function parseSkillUploadErrorMessage(message: string) {
const trimmedMessage = message.trim()
if (!trimmedMessage.startsWith('{')) return trimmedMessage
try {
const parsed: unknown = JSON.parse(trimmedMessage)
if (parsed && typeof parsed === 'object') {
const parsedMessage = (parsed as Record<string, unknown>).message
if (typeof parsedMessage === 'string' && parsedMessage.trim()) return parsedMessage.trim()
}
} catch {
return trimmedMessage
}
return trimmedMessage
}
function readSkillUploadErrorMessage(
error: unknown,
visited = new Set<unknown>(),
): string | undefined {
if (!error || visited.has(error)) return undefined
if (typeof error === 'string') return parseSkillUploadErrorMessage(error)
if (typeof error !== 'object') return undefined
visited.add(error)
const record = error as Record<string, unknown>
for (const key of ['data', 'body', 'error', 'cause', 'response']) {
const nestedMessage = readSkillUploadErrorMessage(record[key], visited)
if (nestedMessage) return nestedMessage
}
const message = record.message
if (typeof message === 'string' && message.trim()) return parseSkillUploadErrorMessage(message)
return undefined
}
async function getSkillUploadResponseErrorMessage(response: Response) {
try {
const data: unknown = await response.clone().json()
return readSkillUploadErrorMessage(data)
} catch {
try {
const text = await response.clone().text()
if (text.trim()) return parseSkillUploadErrorMessage(text)
} catch {}
}
}
export async function uploadSkillFile(
file: File,
options?: {
onProgress?: (progress: number) => void
},
) {
const body = new FormData()
body.append('file', file)
try {
if (options?.onProgress) {
const onProgress = (event: ProgressEvent) => {
if (!event.lengthComputable) return
options.onProgress?.(Math.floor((event.loaded / event.total) * 100))
}
const response = await upload(
{
xhr: new XMLHttpRequest(),
data: body,
onprogress: onProgress,
},
false,
'/workspaces/current/skills/files/upload',
)
return response as SkillFileUploadResponse
}
return await post<SkillFileUploadResponse>(
'/workspaces/current/skills/files/upload',
{ body },
{
bodyStringify: false,
deleteContentType: true,
silent: true,
},
)
} catch (error) {
const message =
error instanceof Response
? await getSkillUploadResponseErrorMessage(error)
: readSkillUploadErrorMessage(error)
if (message) {
const normalizedError = new Error(message)
normalizedError.cause = error
throw normalizedError
}
throw error
}
}
export async function fetchSkillFileBlob({
download = false,
path,
skillId,
versionId,
}: {
download?: boolean
path: string
skillId: string
versionId: string | null
}) {
const params = new URLSearchParams({ path })
if (versionId) params.set('version_id', versionId)
if (download) params.set('download', '1')
const response = await get<Response>(
`/workspaces/current/skills/${encodeURIComponent(skillId)}/files/content?${params.toString()}`,
{},
{ needAllResponseContent: true },
)
return response.blob()
}
export function sendSkillAssistMessage({
attachments,
getAbortController,
message,
model,
onCompleted,
onData,
onError,
skillId,
}: {
attachments?: SkillAssistAttachmentPayload[]
getAbortController?: (abortController: AbortController) => void
message: string
model?: DefaultModel & {
model_settings?: FormValue
}
onCompleted?: IOnCompleted
onData?: IOnData
onError?: IOnError
skillId: string
}) {
return ssePost(
`/workspaces/current/skills/${encodeURIComponent(skillId)}/assist/messages`,
{
body: {
attachments,
message,
model,
},
},
{
getAbortController,
onCompleted,
onData,
onError,
},
)
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,700 @@
'use client'
import type { SkillResponse } from '@dify/contracts/api/console/workspaces/types.gen'
import {
AlertDialog,
AlertDialogActions,
AlertDialogCancelButton,
AlertDialogConfirmButton,
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
} from '@langgenius/dify-ui/alert-dialog'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@langgenius/dify-ui/dropdown-menu'
import {
ScrollAreaContent,
ScrollAreaRoot,
ScrollAreaScrollbar,
ScrollAreaThumb,
ScrollAreaViewport,
} from '@langgenius/dify-ui/scroll-area'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useDebounce } from 'ahooks'
import { useQueryState } from 'nuqs'
import { useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { SearchInput } from '@/app/components/base/search-input'
import { SkeletonRectangle } from '@/app/components/base/skeleton'
import useDocumentTitle from '@/hooks/use-document-title'
import useTimestamp from '@/hooks/use-timestamp'
import Link from '@/next/link'
import { useRouter } from '@/next/navigation'
import { consoleQuery } from '@/service/client'
import { skillKeywordQueryParser, skillQueryParamNames, skillTagQueryParser } from './query-params'
const placeholderCardIds = Array.from(
{ length: 16 },
(_, index) => `skill-placeholder-card-${index}`,
)
const skeletonRows = ['primary', 'secondary', 'tertiary'] as const
function skillsListQueryKey() {
return consoleQuery.workspaces.current.skills.get.key({ type: 'query' })
}
function SkillIcon({ icon }: { icon?: string }) {
return (
<div className="flex size-10 shrink-0 items-center justify-center rounded-[10px] border-[0.5px] border-divider-regular bg-background-default-dodge">
{icon ? (
<span className="system-lg-medium text-text-secondary">{icon}</span>
) : (
<span aria-hidden className="i-ri-box-3-line size-5 text-text-tertiary" />
)}
</div>
)
}
function SkillTagBadge({ tag }: { tag: string }) {
return (
<span className="flex min-w-4 shrink-0 items-center justify-center rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-1.5 py-0.5 system-2xs-medium-uppercase text-text-tertiary">
<span className="max-w-28 truncate">{tag}</span>
</span>
)
}
function SkillCardSkeleton() {
return (
<>
{skeletonRows.map((row) => (
<div
key={row}
className="relative h-42 rounded-xl border-[0.5px] border-components-card-border bg-components-card-bg shadow-xs shadow-shadow-shadow-3"
>
<div className="flex items-center gap-3 px-4 pt-4 pb-2">
<SkeletonRectangle className="my-0 size-10 shrink-0 rounded-[10px] opacity-20" />
<div className="flex min-w-0 flex-1 flex-col gap-1.5">
<SkeletonRectangle className="my-0 h-3 w-36 max-w-full rounded-md opacity-20" />
<SkeletonRectangle className="my-0 h-2 w-24 max-w-full rounded-md opacity-12" />
</div>
</div>
<div className="px-4 py-1">
<SkeletonRectangle className="my-0 h-2 w-full rounded-md opacity-12" />
<SkeletonRectangle className="my-0 mt-2 h-2 w-3/4 rounded-md opacity-10" />
</div>
<div className="flex gap-1 px-4 pt-2">
<SkeletonRectangle className="my-0 h-5 w-14 rounded-md opacity-12" />
<SkeletonRectangle className="my-0 h-5 w-20 rounded-md opacity-10" />
</div>
</div>
))}
</>
)
}
function SkillPlaceholderState({
creating,
importing,
isEmptySearch,
onCreate,
onImport,
title,
}: {
creating?: boolean
importing?: boolean
isEmptySearch?: boolean
onCreate?: () => void
onImport?: () => void
title: string
}) {
const { t } = useTranslation('agentV2')
return (
<section
aria-labelledby="skill-placeholder-title"
className="relative col-span-full min-h-[calc(100vh-142px)] overflow-hidden"
>
<div className="pointer-events-none absolute inset-0 grid grid-cols-[repeat(auto-fill,minmax(296px,1fr))] grid-rows-4 gap-3">
{placeholderCardIds.map((id) => (
<div key={id} className="rounded-xl bg-background-default-lighter opacity-75" />
))}
</div>
<div className="pointer-events-none absolute inset-0 bg-linear-to-b from-background-body/0 to-background-body" />
<div className="absolute inset-0 flex items-center justify-center overflow-hidden p-2">
<div className="flex w-[420px] max-w-full flex-col items-center gap-3">
<div className="flex size-14 items-center justify-center rounded-[10px]">
<div className="flex size-full min-w-px items-center justify-center overflow-hidden rounded-xl border border-dashed border-divider-regular bg-components-card-bg p-1 backdrop-blur-md">
<span aria-hidden className="i-ri-box-3-line size-6 text-text-tertiary" />
</div>
</div>
<h2
id="skill-placeholder-title"
className="system-sm-regular whitespace-nowrap text-text-tertiary"
>
{title}
</h2>
{!isEmptySearch && (
<div className="mt-2 flex w-full flex-col gap-2">
<button
type="button"
disabled={creating || importing}
className="flex h-11 w-full cursor-pointer items-center gap-3 rounded-xl bg-components-card-bg px-4 text-left shadow-xs outline-hidden hover:bg-state-base-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid disabled:cursor-not-allowed disabled:opacity-50"
onClick={onCreate}
>
<span
aria-hidden
className={cn(
'size-4 shrink-0 text-text-tertiary',
creating ? 'i-ri-loader-4-line animate-spin' : 'i-ri-sparkling-2-line',
)}
/>
<span className="min-w-0 flex-1">
<span className="block truncate system-sm-medium text-text-secondary">
{t(($) => $['skillManagement.emptyAction.createTitle'])}
</span>
<span className="block truncate system-xs-regular text-text-tertiary">
{t(($) => $['skillManagement.emptyAction.createDescription'])}
</span>
</span>
</button>
<button
type="button"
disabled={creating || importing}
className="flex h-11 w-full cursor-pointer items-center gap-3 rounded-xl bg-components-card-bg px-4 text-left shadow-xs outline-hidden hover:bg-state-base-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid disabled:cursor-not-allowed disabled:opacity-50"
onClick={onImport}
>
<span
aria-hidden
className={cn(
'size-4 shrink-0 text-text-tertiary',
importing ? 'i-ri-loader-4-line animate-spin' : 'i-ri-upload-line',
)}
/>
<span className="min-w-0 flex-1">
<span className="block truncate system-sm-medium text-text-secondary">
{t(($) => $['skillManagement.emptyAction.importTitle'])}
</span>
<span className="block truncate system-xs-regular text-text-tertiary">
{t(($) => $['skillManagement.emptyAction.importDescription'])}
</span>
</span>
</button>
</div>
)}
</div>
</div>
</section>
)
}
function DeleteSkillDialog({
open,
skill,
onOpenChange,
}: {
open: boolean
skill: SkillResponse
onOpenChange: (open: boolean) => void
}) {
const { t } = useTranslation('agentV2')
const { t: tCommon } = useTranslation('common')
const queryClient = useQueryClient()
const deleteMutation = useMutation(
consoleQuery.workspaces.current.skills.bySkillId.delete.mutationOptions(),
)
const handleDelete = () => {
if (deleteMutation.isPending) return
deleteMutation.mutate(
{
params: {
skill_id: skill.id,
},
body: {
confirmation_name: skill.name,
},
},
{
onSuccess: () => {
toast.success(t(($) => $['skillManagement.deleteSuccess']))
void queryClient.invalidateQueries({ queryKey: skillsListQueryKey() })
void queryClient.invalidateQueries({
queryKey: consoleQuery.workspaces.current.skills.tags.get.key({ type: 'query' }),
})
onOpenChange(false)
},
onError: () => {
toast.error(t(($) => $['skillManagement.deleteFailed']))
},
},
)
}
return (
<AlertDialog open={open} onOpenChange={onOpenChange}>
<AlertDialogContent className="p-6">
<AlertDialogTitle className="truncate title-2xl-semi-bold text-text-primary">
{t(($) => $['skillManagement.deleteDialog.title'], { name: skill.display_name })}
</AlertDialogTitle>
<AlertDialogDescription className="mt-2 system-md-regular wrap-break-word whitespace-pre-wrap text-text-tertiary">
{t(($) => $['skillManagement.deleteDialog.description'])}
</AlertDialogDescription>
<AlertDialogActions className="p-0 pt-6">
<AlertDialogCancelButton disabled={deleteMutation.isPending}>
{tCommon(($) => $['operation.cancel'])}
</AlertDialogCancelButton>
<AlertDialogConfirmButton
tone="destructive"
loading={deleteMutation.isPending}
onClick={handleDelete}
>
{tCommon(($) => $['operation.delete'])}
</AlertDialogConfirmButton>
</AlertDialogActions>
</AlertDialogContent>
</AlertDialog>
)
}
function SkillCard({ skill }: { skill: SkillResponse }) {
const { t } = useTranslation('agentV2')
const { t: tCommon } = useTranslation('common')
const { formatTime } = useTimestamp()
const queryClient = useQueryClient()
const [isDeleteOpen, setIsDeleteOpen] = useState(false)
const duplicateMutation = useMutation(
consoleQuery.workspaces.current.skills.bySkillId.duplicate.post.mutationOptions(),
)
const tags = skill.tags ?? []
const isDraft = !skill.latest_published_version_id
const updatedAt = formatTime(
skill.updated_at,
t(($) => $['skillManagement.dateTimeFormat']),
)
const handleDuplicate = () => {
if (duplicateMutation.isPending) return
duplicateMutation.mutate(
{
params: {
skill_id: skill.id,
},
},
{
onSuccess: () => {
toast.success(t(($) => $['skillManagement.duplicateSuccess']))
void queryClient.invalidateQueries({ queryKey: skillsListQueryKey() })
void queryClient.invalidateQueries({
queryKey: consoleQuery.workspaces.current.skills.tags.get.key({ type: 'query' }),
})
},
onError: () => {
toast.error(t(($) => $['skillManagement.duplicateFailed']))
},
},
)
}
return (
<article className="group relative col-span-1 h-42 min-w-0 overflow-hidden rounded-xl border-[0.5px] border-solid border-components-card-border bg-components-card-bg shadow-xs shadow-shadow-shadow-3 transition-shadow duration-200 ease-in-out hover:shadow-lg">
<div className="flex h-full min-w-0 flex-col">
<Link
href={`/skills/${skill.id}`}
className="block min-w-0 shrink-0 cursor-pointer outline-hidden"
>
<div className="flex items-center gap-3 px-4 pt-4 pb-2">
<SkillIcon icon={skill.icon} />
<div className="flex min-w-0 flex-1 flex-col gap-0.5 py-px">
<h2 className="truncate system-md-semibold text-text-secondary">
{skill.display_name}
</h2>
<p className="truncate system-xs-regular text-text-tertiary">{skill.name}</p>
</div>
</div>
<div className="px-4 py-1 system-xs-regular text-text-tertiary">
<div className="line-clamp-2 min-h-8">{skill.description}</div>
</div>
</Link>
<div className="relative h-6 shrink-0 px-3">
{tags.length > 0 && (
<div className="flex min-w-0 gap-1 overflow-hidden p-1">
{tags.slice(0, 4).map((tag) => (
<SkillTagBadge key={tag} tag={tag} />
))}
</div>
)}
<div className="pointer-events-none absolute top-0 right-0 bottom-0 w-14 bg-linear-to-r from-components-card-bg-transparent to-components-card-bg" />
</div>
<div className="flex min-w-0 shrink-0 items-center px-4 pt-2 pb-3 system-xs-regular text-text-tertiary">
<div className="flex min-w-0 flex-1 items-center gap-1">
<span className="shrink-0">
{t(($) => $['skillManagement.referenceCount'], {
count: skill.reference_count ?? 0,
})}
</span>
<span aria-hidden className="shrink-0 text-text-quaternary">
·
</span>
<span className="min-w-0 truncate">
{isDraft
? t(($) => $['skillManagement.editedAt'], { time: updatedAt })
: t(($) => $['skillManagement.publishedAt'], { time: updatedAt })}
</span>
</div>
</div>
</div>
{isDraft && (
<div className="absolute top-[-0.5px] right-0 flex h-5 items-start overflow-hidden">
<div className="h-5 w-3 bg-background-section-burn [clip-path:polygon(0_0,100%_0,100%_100%)]" />
<div className="flex h-5 items-center bg-background-section-burn pr-2 pl-0.5 system-2xs-medium-uppercase text-text-tertiary">
{t(($) => $['skillManagement.draft'])}
</div>
</div>
)}
<div className="pointer-events-none absolute top-2 right-2 z-20 flex items-center overflow-hidden rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 opacity-0 shadow-lg backdrop-blur-xs transition-opacity group-focus-within:pointer-events-auto group-focus-within:opacity-100 group-hover:pointer-events-auto group-hover:opacity-100 has-data-popup-open:pointer-events-auto has-data-popup-open:opacity-100">
<DropdownMenu modal={false}>
<DropdownMenuTrigger
aria-label={t(($) => $['skillManagement.moreActions'], { name: skill.display_name })}
className="flex size-8 cursor-pointer items-center justify-center rounded-lg p-1.5 hover:bg-state-base-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden data-popup-open:bg-state-base-hover"
onClick={(event) => event.stopPropagation()}
>
<span className="sr-only">
{t(($) => $['skillManagement.moreActions'], { name: skill.display_name })}
</span>
<span aria-hidden className="i-ri-more-fill size-4.5 text-text-tertiary" />
</DropdownMenuTrigger>
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-40">
<DropdownMenuItem className="gap-2" onClick={handleDuplicate}>
<span
aria-hidden
className="i-ri-file-copy-line size-4 shrink-0 text-text-tertiary"
/>
<span>{tCommon(($) => $['operation.duplicate'])}</span>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
variant="destructive"
className="gap-2"
onClick={() => setIsDeleteOpen(true)}
>
<span aria-hidden className="i-ri-delete-bin-line size-4 shrink-0" />
<span>{tCommon(($) => $['operation.delete'])}</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<DeleteSkillDialog skill={skill} open={isDeleteOpen} onOpenChange={setIsDeleteOpen} />
</article>
)
}
function SkillTagFilter({ tags }: { tags: string[] }) {
const { t } = useTranslation('agentV2')
const [selectedTags, setSelectedTags] = useQueryState(
skillQueryParamNames.tag,
skillTagQueryParser,
)
const selectedTagSet = new Set(selectedTags)
const toggleTag = (tag: string) => {
const nextTags = selectedTagSet.has(tag)
? selectedTags.filter((item) => item !== tag)
: [...selectedTags, tag]
void setSelectedTags(nextTags)
}
return (
<DropdownMenu>
<DropdownMenuTrigger
className={cn(
'flex h-8 shrink-0 cursor-pointer items-center gap-1 rounded-lg bg-components-input-bg-normal px-2 py-1 system-sm-regular text-text-tertiary hover:bg-state-base-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden',
selectedTags.length > 0 && 'text-text-secondary',
)}
>
<span>{t(($) => $['skillManagement.tags'])}</span>
{selectedTags.length > 0 && (
<span className="flex min-w-4 shrink-0 items-center justify-center rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-1 py-0.5 system-2xs-medium-uppercase text-text-tertiary tabular-nums">
{selectedTags.length}
</span>
)}
<span aria-hidden className="i-ri-arrow-down-s-line size-4 text-text-tertiary" />
</DropdownMenuTrigger>
<DropdownMenuContent placement="bottom-start" sideOffset={4} popupClassName="w-52">
{tags.length === 0 ? (
<DropdownMenuItem disabled>{t(($) => $['skillManagement.noTags'])}</DropdownMenuItem>
) : (
tags.map((tag) => (
<DropdownMenuItem key={tag} className="gap-2" onClick={() => toggleTag(tag)}>
<span
aria-hidden
className={cn(
'i-ri-check-line size-4 shrink-0',
selectedTagSet.has(tag) ? 'text-text-accent' : 'text-transparent',
)}
/>
<span className="min-w-0 flex-1 truncate">{tag}</span>
</DropdownMenuItem>
))
)}
{selectedTags.length > 0 && (
<>
<DropdownMenuSeparator />
<DropdownMenuItem className="gap-2" onClick={() => setSelectedTags([])}>
<span aria-hidden className="i-ri-close-line size-4 shrink-0 text-text-tertiary" />
<span>{t(($) => $['skillManagement.clearTags'])}</span>
</DropdownMenuItem>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
)
}
function SkillsToolbar({
creating,
importing,
onCreate,
onImport,
tags,
}: {
creating: boolean
importing: boolean
onCreate: () => void
onImport: () => void
tags: string[]
}) {
const { t } = useTranslation('agentV2')
const [keyword, setKeyword] = useQueryState(skillQueryParamNames.keyword, skillKeywordQueryParser)
const isMutating = creating || importing
return (
<div className="flex min-w-0 items-center gap-2">
<SkillTagFilter tags={tags} />
<SearchInput
aria-label={t(($) => $['skillManagement.searchLabel'])}
className="h-8 w-50 min-w-0 shrink"
placeholder={t(($) => $['skillManagement.searchPlaceholder'])}
value={keyword}
onValueChange={(value) => {
void setKeyword(value)
}}
/>
<div className="ml-auto flex shrink-0 items-center gap-2">
<Button
className="h-8 gap-1 px-3"
disabled={isMutating}
loading={importing}
onClick={onImport}
>
<span aria-hidden className="i-ri-upload-line size-4" />
<span className="px-0.5 system-sm-medium">{t(($) => $['skillManagement.import'])}</span>
</Button>
<Button
variant="primary"
className="h-8 gap-0.5 px-3"
disabled={isMutating}
loading={creating}
onClick={onCreate}
>
<span aria-hidden className="i-ri-add-line size-4" />
<span className="px-0.5 system-sm-medium">{t(($) => $['skillManagement.create'])}</span>
</Button>
</div>
</div>
)
}
function SkillGrid({
creating,
importing,
isEmptySearch,
isError,
isFetching,
isPending,
onCreate,
onImport,
skills,
}: {
creating: boolean
importing: boolean
isEmptySearch: boolean
isError: boolean
isFetching: boolean
isPending: boolean
onCreate: () => void
onImport: () => void
skills: SkillResponse[]
}) {
const { t } = useTranslation('agentV2')
return (
<section
aria-label={t(($) => $['skillManagement.listLabel'])}
className="grid grid-cols-[repeat(auto-fill,minmax(296px,1fr))] gap-2.5"
aria-busy={isFetching || undefined}
>
{isPending && <SkillCardSkeleton />}
{!isPending && isError && (
<SkillPlaceholderState title={t(($) => $['skillManagement.loadingError'])} />
)}
{!isPending && !isError && skills.length === 0 && (
<SkillPlaceholderState
creating={creating}
importing={importing}
isEmptySearch={isEmptySearch}
onCreate={onCreate}
onImport={onImport}
title={
isEmptySearch
? t(($) => $['skillManagement.emptySearch'])
: t(($) => $['skillManagement.empty'])
}
/>
)}
{!isPending && !isError && skills.map((skill) => <SkillCard key={skill.id} skill={skill} />)}
</section>
)
}
export default function SkillsPage() {
const { t } = useTranslation('agentV2')
const router = useRouter()
const queryClient = useQueryClient()
const importInputRef = useRef<HTMLInputElement>(null)
const [keyword] = useQueryState(skillQueryParamNames.keyword, skillKeywordQueryParser)
const [selectedTags] = useQueryState(skillQueryParamNames.tag, skillTagQueryParser)
const debouncedKeyword = useDebounce(keyword.trim(), { wait: 300 })
const createMutation = useMutation(consoleQuery.workspaces.current.skills.post.mutationOptions())
const importMutation = useMutation(
consoleQuery.workspaces.current.skills.import.post.mutationOptions(),
)
const skillsQuery = useQuery(
consoleQuery.workspaces.current.skills.get.queryOptions({
input: {
query: {
...(debouncedKeyword ? { keyword: debouncedKeyword } : {}),
...(selectedTags.length > 0 ? { tag: selectedTags } : {}),
},
},
}),
)
const tagsQuery = useQuery(consoleQuery.workspaces.current.skills.tags.get.queryOptions())
const skills = skillsQuery.data?.data ?? []
const tags = (tagsQuery.data?.data ?? []).map((tag) => tag.tag)
useDocumentTitle(t(($) => $['skillManagement.title']))
const invalidateSkills = () => {
void queryClient.invalidateQueries({ queryKey: skillsListQueryKey() })
void queryClient.invalidateQueries({
queryKey: consoleQuery.workspaces.current.skills.tags.get.key({ type: 'query' }),
})
}
const handleCreate = () => {
if (createMutation.isPending) return
createMutation.mutate(
{
body: {},
},
{
onSuccess: (skill) => {
toast.success(t(($) => $['skillManagement.createSuccess']))
invalidateSkills()
router.push(`/skills/${skill.id}`)
},
onError: () => {
toast.error(t(($) => $['skillManagement.createFailed']))
},
},
)
}
const handleFileChange = (file: File | undefined) => {
if (!file || importMutation.isPending) return
importMutation.mutate(
{
body: {
file,
},
},
{
onSuccess: (skill) => {
toast.success(t(($) => $['skillManagement.importSuccess']))
invalidateSkills()
router.push(`/skills/${skill.id}`)
},
onError: () => {
toast.error(t(($) => $['skillManagement.importFailed']))
},
onSettled: () => {
if (importInputRef.current) importInputRef.current.value = ''
},
},
)
}
return (
<div className="flex h-0 min-w-0 grow flex-col overflow-hidden bg-background-body">
<div className="shrink-0 bg-background-body px-8 pt-4 pb-2">
<div className="flex h-6 min-w-0 items-center justify-between gap-4">
<h1 className="min-w-0 flex-1 truncate text-[18px]/[21.6px] font-semibold text-text-primary">
{t(($) => $['skillManagement.title'])}
</h1>
</div>
<div className="mt-3.5">
<input
ref={importInputRef}
type="file"
accept=".zip,.skill,application/zip"
className="hidden"
onChange={(event) => handleFileChange(event.currentTarget.files?.[0])}
/>
<SkillsToolbar
creating={createMutation.isPending}
importing={importMutation.isPending}
onCreate={handleCreate}
onImport={() => importInputRef.current?.click()}
tags={tags}
/>
</div>
</div>
<div className="min-h-0 flex-1">
<ScrollAreaRoot className="relative h-full min-h-0 min-w-0 overflow-hidden">
<ScrollAreaViewport tabIndex={-1} className="overscroll-contain">
<ScrollAreaContent className="min-h-full px-8 pt-2 pb-8">
<SkillGrid
creating={createMutation.isPending}
importing={importMutation.isPending}
skills={skills}
isEmptySearch={!!debouncedKeyword || selectedTags.length > 0}
isError={skillsQuery.isError}
isFetching={skillsQuery.isFetching}
isPending={skillsQuery.isPending}
onCreate={handleCreate}
onImport={() => importInputRef.current?.click()}
/>
</ScrollAreaContent>
</ScrollAreaViewport>
<ScrollAreaScrollbar>
<ScrollAreaThumb />
</ScrollAreaScrollbar>
</ScrollAreaRoot>
</div>
</div>
)
}

View File

@ -0,0 +1,12 @@
import { debounce, parseAsArrayOf, parseAsString } from 'nuqs'
export const skillQueryParamNames = {
keyword: 'keyword',
tag: 'tag',
} as const
export const skillKeywordQueryParser = parseAsString.withDefault('').withOptions({
limitUrlUpdates: debounce(300),
})
export const skillTagQueryParser = parseAsArrayOf(parseAsString, ';').withDefault([])

View File

@ -15,8 +15,9 @@ import { cloudSystemFeatures, defaultSystemFeatures } from './config'
*
* For Cloud, this query is intentionally local-only and uses `staleTime:
* 'static'`: the payload comes from frontend config/defaults, so invalidation
* should not re-run the same local merge. For non-Cloud, do not override
* `staleTime`: inherit the 5-minute default from query-client-server.ts.
* should not re-run the same local merge. For non-Cloud, keep the query stale
* so a server-side fallback does not hide API-enabled features until the shared
* query cache expires.
*/
export const systemFeaturesQueryOptions = () => {
const queryKey = consoleQuery.systemFeatures.get.queryKey()
@ -31,6 +32,7 @@ export const systemFeaturesQueryOptions = () => {
return queryOptions<GetSystemFeaturesResponse>({
queryKey,
staleTime: 0,
queryFn: async () => {
try {
return await consoleClient.systemFeatures.get()

View File

@ -21,6 +21,7 @@ export const serverSystemFeaturesQueryOptions = () => {
return queryOptions<GetSystemFeaturesResponse>({
queryKey,
staleTime: 0,
queryFn: async () => {
try {
return await serverConsoleClient.systemFeatures.get(undefined, {

View File

@ -9,13 +9,14 @@ import { basePath } from '@/utils/var'
export default function useDocumentTitle(title: string) {
const { data, isPending } = useQuery(systemFeaturesQueryOptions())
const systemFeatures = data ?? defaultSystemFeatures
const branding = systemFeatures.branding ?? defaultSystemFeatures.branding
const prefix = title ? `${title} - ` : ''
let titleStr = ''
let favicon = ''
if (isPending === false) {
if (systemFeatures.branding.enabled) {
titleStr = `${prefix}${systemFeatures.branding.application_title}`
favicon = systemFeatures.branding.favicon
if (branding.enabled) {
titleStr = `${prefix}${branding.application_title}`
favicon = branding.favicon
} else {
titleStr = `${prefix}Dify`
favicon = `${basePath}/favicon.ico`
@ -24,7 +25,7 @@ export default function useDocumentTitle(title: string) {
useTitle(titleStr)
useEffect(() => {
let apple: HTMLLinkElement | null = null
if (systemFeatures.branding.favicon) {
if (branding.favicon) {
document
.querySelectorAll(
"link[rel='icon'], link[rel='shortcut icon'], link[rel='apple-touch-icon'], link[rel='mask-icon']",
@ -33,13 +34,13 @@ export default function useDocumentTitle(title: string) {
apple = document.createElement('link')
apple.rel = 'apple-touch-icon'
apple.href = systemFeatures.branding.favicon
apple.href = branding.favicon
document.head.appendChild(apple)
}
return () => {
apple?.remove()
}
}, [systemFeatures.branding.favicon])
}, [branding.favicon])
useFavicon(favicon)
}

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "يشغل Preview الوكيل المكتمل كما سيراه المستخدمون، مع ردود واضحة وميزات الدردشة.",
"agentDetail.configure.rightPanel.previewTipTitle": "عاين وكيلك",
"agentDetail.configure.skills.add": "إضافة مهارة",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "محتوى تفاصيل المهارة",
"agentDetail.configure.skills.detail.fileCount": "{{count}} ملفات",
"agentDetail.configure.skills.detail.files": "الملفات",
"agentDetail.configure.skills.empty.description": "تمنح المهارات الوكيل خبرة قابلة لإعادة الاستخدام يمكنه استدعاؤها أثناء العمل",
"agentDetail.configure.skills.empty.title": "لا توجد مهارات بعد",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "مهارة",
"agentDetail.configure.skills.label": "المهارات",
"agentDetail.configure.skills.missing": "المهارة غير موجودة",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "إزالة {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "اجمع التعليمات والملفات والبرامج النصية لمهمة متكررة في Skill. أشر إليها باستخدام / في Prompt. <docLink>معرفة المزيد</docLink>\n\nفي وضع Build، يمكن للوكيل إعدادها لك.",
"agentDetail.configure.skills.tip": "اجمع التعليمات والملفات والبرامج النصية لمهمة متكررة في Skill. أشر إليها باستخدام / في Prompt. معرفة المزيد\n\nفي وضع Build، يمكن للوكيل إعدادها لك.",
"agentDetail.configure.skills.toggle": "تبديل المهارات",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "خيارات الفرز",
"roster.sort.recentlyCreated": "الأحدث إنشاءً",
"roster.updateSuccess": "تم تحديث الوكيل.",
"roster.usageStatus.draft": "مسودة"
"roster.usageStatus.draft": "مسودة",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "طي الشريط الجانبي",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "توسيع الشريط الجانبي",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "لا توجد ملفات مطابقة.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "البحث عن الملفات",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -201,6 +201,7 @@
"mainNav.home": "الرئيسية",
"mainNav.integrations": "التكاملات",
"mainNav.marketplace": "سوق الإضافات",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "لم يتم العثور على تطبيقات ويب",
"mainNav.webApps.openApp": "فتح تطبيق الويب {{name}}",
"mainNav.webApps.searchPlaceholder": "البحث في تطبيقات الويب",
@ -503,6 +504,7 @@
"operation.sure": "أنا متأكد",
"operation.toggleFullscreen": "تبديل ملء الشاشة",
"operation.toggleMute": "تبديل كتم الصوت",
"operation.upload": "Upload",
"operation.view": "عرض",
"operation.viewDetails": "عرض التفاصيل",
"operation.viewMore": "عرض المزيد",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview führt den fertigen Agenten so aus, wie deine Benutzer ihn sehen, mit klaren Antworten und Chat-Funktionen.",
"agentDetail.configure.rightPanel.previewTipTitle": "Agenten vorschauen",
"agentDetail.configure.skills.add": "Skill hinzufügen",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Skill-Detailinhalt",
"agentDetail.configure.skills.detail.fileCount": "{{count}} DATEIEN",
"agentDetail.configure.skills.detail.files": "Dateien",
"agentDetail.configure.skills.empty.description": "Skills geben dem Agenten wiederverwendbare Fachkenntnisse, die er bei der Arbeit nutzen kann",
"agentDetail.configure.skills.empty.title": "Noch keine Skills",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Skill",
"agentDetail.configure.skills.label": "Skills",
"agentDetail.configure.skills.missing": "Skill nicht gefunden",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "{{name}} entfernen",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Bündeln Sie Anweisungen, Dateien und Skripte für eine wiederkehrende Aufgabe in einer Skill. Verweisen Sie im Prompt mit / darauf. <docLink>Mehr erfahren</docLink>\n\nIm Build-Modus kann der Agent diese Einrichtung für Sie übernehmen.",
"agentDetail.configure.skills.tip": "Bündeln Sie Anweisungen, Dateien und Skripte für eine wiederkehrende Aufgabe in einer Skill. Verweisen Sie im Prompt mit / darauf. Mehr erfahren\n\nIm Build-Modus kann der Agent diese Einrichtung für Sie übernehmen.",
"agentDetail.configure.skills.toggle": "Skills umschalten",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "Sortieroptionen",
"roster.sort.recentlyCreated": "Zuletzt erstellt",
"roster.updateSuccess": "Agent aktualisiert.",
"roster.usageStatus.draft": "Entwurf"
"roster.usageStatus.draft": "Entwurf",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "Seitenleiste einklappen",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "Seitenleiste ausklappen",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "Keine passenden Dateien.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Dateien suchen",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "Startseite",
"mainNav.integrations": "Integrationen",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "Keine Web-Apps gefunden",
"mainNav.webApps.openApp": "Web-App {{name}} öffnen",
"mainNav.webApps.searchPlaceholder": "Web-Apps suchen",
@ -504,6 +505,7 @@
"operation.sure": "Ich bin sicher",
"operation.toggleFullscreen": "Vollbild umschalten",
"operation.toggleMute": "Stummschaltung umschalten",
"operation.upload": "Upload",
"operation.view": "Ansehen",
"operation.viewDetails": "Details anzeigen",
"operation.viewMore": "MEHR SEHEN",

View File

@ -203,15 +203,24 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview runs the finished agent the way your users will see it, with clean replies and chat features.",
"agentDetail.configure.rightPanel.previewTipTitle": "Preview your agent",
"agentDetail.configure.skills.add": "Add skill",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.description": "A .zip containing SKILL.md. It's embedded in this app and won't update with the library.",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.description": "Reuses a shared skill and follows its published updates.",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Skill detail content",
"agentDetail.configure.skills.detail.fileCount": "{{count}} FILES",
"agentDetail.configure.skills.detail.files": "Files",
"agentDetail.configure.skills.empty.description": "Skills give the agent reusable expertise it can call while working",
"agentDetail.configure.skills.empty.title": "No skills yet",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Skill",
"agentDetail.configure.skills.label": "Skills",
"agentDetail.configure.skills.missing": "Skill not found",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "Remove {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Package the instructions, files, and scripts for a recurring task into a skill. Reference with / in the Prompt. <docLink>Learn more</docLink>\n\nIn Build mode, the agent can set these up for you.",
"agentDetail.configure.skills.tip": "Package the instructions, files, and scripts for a recurring task into a skill. Reference with / in the Prompt. Learn more\n\nIn Build mode, the agent can set these up for you.",
"agentDetail.configure.skills.toggle": "Toggle skills",
@ -226,6 +235,15 @@
"agentDetail.configure.skills.upload.title": "Upload skill",
"agentDetail.configure.skills.upload.warning.files": "If you only need to use Markdown files, upload them to Files and reference them in your prompt.",
"agentDetail.configure.skills.upload.warning.specification": "Uploaded skills must follow the <specificationLink>Agent Skills specification</specificationLink>.",
"agentDetail.configure.skills.workspaceItemType": "Workspace",
"agentDetail.configure.skills.workspaceSelector.addSuccess": "Workspace skill added.",
"agentDetail.configure.skills.workspaceSelector.added": "Added",
"agentDetail.configure.skills.workspaceSelector.draft": "Draft",
"agentDetail.configure.skills.workspaceSelector.empty": "No workspace skills found.",
"agentDetail.configure.skills.workspaceSelector.manage": "Manage in Skills",
"agentDetail.configure.skills.workspaceSelector.removeSuccess": "Workspace skill removed.",
"agentDetail.configure.skills.workspaceSelector.saveFailed": "Failed to update workspace skills.",
"agentDetail.configure.skills.workspaceSelector.search": "Search skills...",
"agentDetail.configure.title": "Configure",
"agentDetail.configure.tools.add": "Add tool",
"agentDetail.configure.tools.addMenu.cliTool.badge": "For developers",
@ -419,5 +437,178 @@
"roster.sort.optionsLabel": "Sort options",
"roster.sort.recentlyCreated": "Recently created",
"roster.updateSuccess": "Agent updated.",
"roster.usageStatus.draft": "Draft"
"roster.usageStatus.draft": "Draft",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.builder.attach": "Attach file",
"skillManagement.detail.builder.attachFailed": "Failed to attach file.",
"skillManagement.detail.builder.attachUnsupported": "Only text and document files can be attached.",
"skillManagement.detail.builder.attachmentOnlyMessage": "Use the attached file to help improve this Skill.",
"skillManagement.detail.builder.close": "Close Skill Builder",
"skillManagement.detail.builder.compatibleModelsOnly": "Only compatible models are shown",
"skillManagement.detail.builder.exampleIssueTriage": "Customer issue triage",
"skillManagement.detail.builder.exampleOnboarding": "New hire onboarding guide",
"skillManagement.detail.builder.exampleSalesFollowUp": "Sales lead follow-up strategy",
"skillManagement.detail.builder.followUpDisplayName": "Use Refund approval as the display name",
"skillManagement.detail.builder.followUpNameIcon": "Apply the suggested name and icon",
"skillManagement.detail.builder.fromMarketplace": "From Marketplace",
"skillManagement.detail.builder.model": "GPT-4o",
"skillManagement.detail.builder.modelCredits.all": "All credits",
"skillManagement.detail.builder.modelCredits.configure": "Configure required",
"skillManagement.detail.builder.modelCredits.exhausted": "Credits exhausted",
"skillManagement.detail.builder.modelProviderSettings": "Model Provider Settings",
"skillManagement.detail.builder.modelSearch": "Search models...",
"skillManagement.detail.builder.modifyPlaceholder": "Ask AI to modify this skill...",
"skillManagement.detail.builder.open": "Open Skill Builder",
"skillManagement.detail.builder.placeholder": "Describe the scenario...",
"skillManagement.detail.builder.promptDescription": "Describe it and a draft appears in the editor, with steps and files included.",
"skillManagement.detail.builder.promptTitle": "What should this Skill handle?",
"skillManagement.detail.builder.removeAttachment": "Remove {{name}}",
"skillManagement.detail.builder.restart": "Restart builder",
"skillManagement.detail.builder.send": "Send message",
"skillManagement.detail.builder.sendFailed": "Skill Builder failed to respond.",
"skillManagement.detail.builder.title": "Skill Builder",
"skillManagement.detail.builder.tryExample": "Try an example",
"skillManagement.detail.builder.voice": "Voice input",
"skillManagement.detail.builder.voiceUnavailable": "Voice input is not available yet.",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.closeVersions": "Close versions",
"skillManagement.detail.collapseSidebar": "Collapse sidebar",
"skillManagement.detail.copyFile": "Copy",
"skillManagement.detail.copyFileSuccess": "File copied to clipboard.",
"skillManagement.detail.copyVersionId": "Copy ID",
"skillManagement.detail.copyVersionIdSuccess": "ID copied to clipboard.",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.cutFile": "Cut",
"skillManagement.detail.cutFileSuccess": "File cut to clipboard.",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.editVersionInfo": "Edit version info",
"skillManagement.detail.exitVersions": "Exit versions",
"skillManagement.detail.expandSidebar": "Expand sidebar",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.nameThisVersion": "Name this version",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "No matching files.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.pasteFile": "Paste",
"skillManagement.detail.pasteFileSuccess": "File pasted.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishReferencesDescription": "Publishing will make this draft active for {{count}} references using this Skill.",
"skillManagement.detail.publishReferencesTitle": "Publish skill",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.publishUpdate": "Publish update",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Search files",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesFailedStatus": "{{count}} file uploads failed.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.uploadFilesProgress": "Uploading {{completed}}/{{total}}",
"skillManagement.detail.uploadFilesResult": "{{uploaded}} uploaded · {{failed}} failed",
"skillManagement.detail.uploadFilesStatus": "Upload Status",
"skillManagement.detail.uploadStatusDismiss": "Dismiss upload status",
"skillManagement.detail.versionHistory": "Open version history",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versionPublishNote": "Release notes",
"skillManagement.detail.versionPublishNotePlaceholder": "Describe this change",
"skillManagement.detail.versionPublishedMeta": "{{time}} · {{name}}",
"skillManagement.detail.versionTitle": "Title",
"skillManagement.detail.versions": "Versions",
"skillManagement.detail.viewOnly": "View only",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptyAction.createDescription": "Describe a scenario in plain words and get a draft skill to review.",
"skillManagement.emptyAction.createTitle": "Create with Skill Builder",
"skillManagement.emptyAction.importDescription": "Bring a .zip with SKILL.md, in the agentskills.io format.",
"skillManagement.emptyAction.importTitle": "Import a skill package",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "Home",
"mainNav.integrations": "Integrations",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "No web apps found",
"mainNav.webApps.openApp": "Open {{name}} web app",
"mainNav.webApps.searchPlaceholder": "Search web apps",
@ -504,6 +505,7 @@
"operation.sure": "I'm sure",
"operation.toggleFullscreen": "Toggle fullscreen",
"operation.toggleMute": "Toggle mute",
"operation.upload": "Upload",
"operation.view": "View",
"operation.viewDetails": "View Details",
"operation.viewMore": "VIEW MORE",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview ejecuta el agente terminado como lo verán tus usuarios, con respuestas claras y funciones de chat.",
"agentDetail.configure.rightPanel.previewTipTitle": "Previsualiza tu agente",
"agentDetail.configure.skills.add": "Agregar habilidad",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Contenido de los detalles de la habilidad",
"agentDetail.configure.skills.detail.fileCount": "{{count}} ARCHIVOS",
"agentDetail.configure.skills.detail.files": "Archivos",
"agentDetail.configure.skills.empty.description": "Las habilidades le dan al agente experiencia reutilizable que puede invocar mientras trabaja",
"agentDetail.configure.skills.empty.title": "Aún no hay habilidades",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Habilidad",
"agentDetail.configure.skills.label": "Habilidades",
"agentDetail.configure.skills.missing": "Habilidad no encontrada",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "Eliminar {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Empaqueta las instrucciones, archivos y scripts de una tarea recurrente en una skill. Haz referencia con / en el Prompt. <docLink>Más información</docLink>\n\nEn modo Build, el agente puede configurarlas por ti.",
"agentDetail.configure.skills.tip": "Empaqueta las instrucciones, archivos y scripts de una tarea recurrente en una skill. Haz referencia con / en el Prompt. Más información\n\nEn modo Build, el agente puede configurarlas por ti.",
"agentDetail.configure.skills.toggle": "Alternar habilidades",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "Opciones de ordenación",
"roster.sort.recentlyCreated": "Creados recientemente",
"roster.updateSuccess": "Agente actualizado.",
"roster.usageStatus.draft": "Borrador"
"roster.usageStatus.draft": "Borrador",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "Contraer barra lateral",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "Expandir barra lateral",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "No hay archivos coincidentes.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Buscar archivos",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "Inicio",
"mainNav.integrations": "Integraciones",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "No se encontraron aplicaciones web",
"mainNav.webApps.openApp": "Abrir la aplicación web {{name}}",
"mainNav.webApps.searchPlaceholder": "Buscar aplicaciones web",
@ -504,6 +505,7 @@
"operation.sure": "Estoy seguro",
"operation.toggleFullscreen": "Alternar pantalla completa",
"operation.toggleMute": "Alternar silencio",
"operation.upload": "Upload",
"operation.view": "Vista",
"operation.viewDetails": "Ver detalles",
"operation.viewMore": "VER MÁS",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview عامل تکمیل‌شده را همان‌طور که کاربران می‌بینند اجرا می‌کند، با پاسخ‌های تمیز و قابلیت‌های گفتگو.",
"agentDetail.configure.rightPanel.previewTipTitle": "پیش‌نمایش عامل",
"agentDetail.configure.skills.add": "افزودن مهارت",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "محتوای جزئیات مهارت",
"agentDetail.configure.skills.detail.fileCount": "{{count}} فایل",
"agentDetail.configure.skills.detail.files": "فایل‌ها",
"agentDetail.configure.skills.empty.description": "مهارت‌ها به عامل تخصص قابل استفاده مجدد می‌دهند که هنگام کار می‌تواند فراخوانی کند",
"agentDetail.configure.skills.empty.title": "هنوز مهارتی وجود ندارد",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "مهارت",
"agentDetail.configure.skills.label": "مهارت‌ها",
"agentDetail.configure.skills.missing": "مهارت یافت نشد",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "حذف {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "دستورالعمل‌ها، فایل‌ها و اسکریپت‌های یک کار تکرارشونده را در یک Skill بسته‌بندی کنید. در Prompt با / به آن ارجاع دهید. <docLink>بیشتر بدانید</docLink>\n\nدر حالت Build، عامل می‌تواند این موارد را برای شما تنظیم کند.",
"agentDetail.configure.skills.tip": "دستورالعمل‌ها، فایل‌ها و اسکریپت‌های یک کار تکرارشونده را در یک Skill بسته‌بندی کنید. در Prompt با / به آن ارجاع دهید. بیشتر بدانید\n\nدر حالت Build، عامل می‌تواند این موارد را برای شما تنظیم کند.",
"agentDetail.configure.skills.toggle": "تغییر مهارت‌ها",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "گزینه‌های مرتب‌سازی",
"roster.sort.recentlyCreated": "تازه‌ترین ایجاد",
"roster.updateSuccess": "عامل به‌روزرسانی شد.",
"roster.usageStatus.draft": "پیش‌نویس"
"roster.usageStatus.draft": "پیش‌نویس",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "جمع کردن نوار کناری",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "باز کردن نوار کناری",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "فایل منطبقی نیست.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "جستجوی فایل‌ها",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "خانه",
"mainNav.integrations": "یکپارچه‌سازی‌ها",
"mainNav.marketplace": "بازارچه",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "هیچ برنامه وبی یافت نشد",
"mainNav.webApps.openApp": "باز کردن برنامه وب {{name}}",
"mainNav.webApps.searchPlaceholder": "جستجوی برنامه‌های وب",
@ -504,6 +505,7 @@
"operation.sure": "مطمئن هستم",
"operation.toggleFullscreen": "تغییر حالت تمام‌صفحه",
"operation.toggleMute": "تغییر حالت بی‌صدا",
"operation.upload": "Upload",
"operation.view": "مشاهده",
"operation.viewDetails": "دیدن جزئیات",
"operation.viewMore": "بیشتر ببینید",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview exécute lagent final comme vos utilisateurs le verront, avec des réponses claires et les fonctions de chat.",
"agentDetail.configure.rightPanel.previewTipTitle": "Prévisualiser votre agent",
"agentDetail.configure.skills.add": "Ajouter une compétence",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Contenu des détails de la compétence",
"agentDetail.configure.skills.detail.fileCount": "{{count}} FICHIERS",
"agentDetail.configure.skills.detail.files": "Fichiers",
"agentDetail.configure.skills.empty.description": "Les compétences offrent à lagent une expertise réutilisable quil peut invoquer pendant son travail",
"agentDetail.configure.skills.empty.title": "Pas encore de compétences",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Compétence",
"agentDetail.configure.skills.label": "Compétences",
"agentDetail.configure.skills.missing": "Compétence introuvable",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "Supprimer {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Regroupez les instructions, fichiers et scripts d'une tâche récurrente dans une skill. Référencez-la avec / dans le Prompt. <docLink>En savoir plus</docLink>\n\nEn mode Build, l'agent peut les configurer pour vous.",
"agentDetail.configure.skills.tip": "Regroupez les instructions, fichiers et scripts d'une tâche récurrente dans une skill. Référencez-la avec / dans le Prompt. En savoir plus\n\nEn mode Build, l'agent peut les configurer pour vous.",
"agentDetail.configure.skills.toggle": "Basculer les compétences",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "Options de tri",
"roster.sort.recentlyCreated": "Récemment créés",
"roster.updateSuccess": "Agent mis à jour.",
"roster.usageStatus.draft": "Brouillon"
"roster.usageStatus.draft": "Brouillon",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "Réduire la barre latérale",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "Développer la barre latérale",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "Aucun fichier correspondant.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Rechercher des fichiers",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "Accueil",
"mainNav.integrations": "Intégrations",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "Aucune application web trouvée",
"mainNav.webApps.openApp": "Ouvrir lapplication web {{name}}",
"mainNav.webApps.searchPlaceholder": "Rechercher des applications web",
@ -504,6 +505,7 @@
"operation.sure": "Je suis sûr",
"operation.toggleFullscreen": "Basculer en plein écran",
"operation.toggleMute": "Activer/désactiver le son",
"operation.upload": "Upload",
"operation.view": "Vue",
"operation.viewDetails": "Voir les détails",
"operation.viewMore": "VOIR PLUS",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview तैयार agent को वैसे चलाता है जैसे आपके users उसे देखेंगे, साफ replies और chat features के साथ।",
"agentDetail.configure.rightPanel.previewTipTitle": "अपने agent का preview करें",
"agentDetail.configure.skills.add": "कौशल जोड़ें",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "कौशल विवरण सामग्री",
"agentDetail.configure.skills.detail.fileCount": "{{count}} फ़ाइलें",
"agentDetail.configure.skills.detail.files": "फ़ाइलें",
"agentDetail.configure.skills.empty.description": "कौशल एजेंट को पुनः उपयोग योग्य विशेषज्ञता देते हैं जिसे वह काम करते समय कॉल कर सकता है",
"agentDetail.configure.skills.empty.title": "अभी तक कोई कौशल नहीं",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "कौशल",
"agentDetail.configure.skills.label": "कौशल",
"agentDetail.configure.skills.missing": "कौशल नहीं मिला",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "{{name}} हटाएँ",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "किसी दोहराए जाने वाले कार्य के निर्देशों, फ़ाइलों और स्क्रिप्ट को एक Skill में पैकेज करें। Prompt में / से संदर्भ दें। <docLink>और जानें</docLink>\n\nBuild mode में, एजेंट इन्हें आपके लिए सेट कर सकता है।",
"agentDetail.configure.skills.tip": "किसी दोहराए जाने वाले कार्य के निर्देशों, फ़ाइलों और स्क्रिप्ट को एक Skill में पैकेज करें। Prompt में / से संदर्भ दें। और जानें\n\nBuild mode में, एजेंट इन्हें आपके लिए सेट कर सकता है।",
"agentDetail.configure.skills.toggle": "कौशल टॉगल करें",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "क्रमबद्ध विकल्प",
"roster.sort.recentlyCreated": "हाल ही में बनाए गए",
"roster.updateSuccess": "एजेंट अपडेट हो गया।",
"roster.usageStatus.draft": "ड्राफ्ट"
"roster.usageStatus.draft": "ड्राफ्ट",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "साइडबार समेटें",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "साइडबार फैलाएं",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "कोई मेल खाती फ़ाइल नहीं.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "फ़ाइलें खोजें",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "होम",
"mainNav.integrations": "इंटीग्रेशन",
"mainNav.marketplace": "मार्केटप्लेस",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "कोई वेब ऐप नहीं मिला",
"mainNav.webApps.openApp": "{{name}} वेब ऐप खोलें",
"mainNav.webApps.searchPlaceholder": "वेब ऐप खोजें",
@ -504,6 +505,7 @@
"operation.sure": "मुझे यकीन है",
"operation.toggleFullscreen": "फ़ुलस्क्रीन टॉगल करें",
"operation.toggleMute": "म्यूट टॉगल करें",
"operation.upload": "Upload",
"operation.view": "देखना",
"operation.viewDetails": "विवरण देखें",
"operation.viewMore": "और देखें",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview menjalankan agent yang sudah selesai seperti yang akan dilihat pengguna, dengan balasan rapi dan fitur chat.",
"agentDetail.configure.rightPanel.previewTipTitle": "Pratinjau agen Anda",
"agentDetail.configure.skills.add": "Tambahkan keterampilan",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Konten detail keterampilan",
"agentDetail.configure.skills.detail.fileCount": "{{count}} FILE",
"agentDetail.configure.skills.detail.files": "File",
"agentDetail.configure.skills.empty.description": "Keterampilan memberi agen keahlian yang dapat digunakan kembali yang bisa dipanggil saat bekerja",
"agentDetail.configure.skills.empty.title": "Belum ada keterampilan",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Keterampilan",
"agentDetail.configure.skills.label": "Keterampilan",
"agentDetail.configure.skills.missing": "Keterampilan tidak ditemukan",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "Hapus {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Kemas instruksi, file, dan skrip untuk tugas berulang menjadi skill. Referensikan dengan / di Prompt. <docLink>Pelajari selengkapnya</docLink>\n\nDalam mode Build, agen dapat menyiapkannya untuk Anda.",
"agentDetail.configure.skills.tip": "Kemas instruksi, file, dan skrip untuk tugas berulang menjadi skill. Referensikan dengan / di Prompt. Pelajari selengkapnya\n\nDalam mode Build, agen dapat menyiapkannya untuk Anda.",
"agentDetail.configure.skills.toggle": "Alihkan keterampilan",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "Opsi pengurutan",
"roster.sort.recentlyCreated": "Baru dibuat",
"roster.updateSuccess": "Agen diperbarui.",
"roster.usageStatus.draft": "Draf"
"roster.usageStatus.draft": "Draf",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "Ciutkan sidebar",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "Perluas sidebar",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "Tidak ada file yang cocok.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Cari file",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -201,6 +201,7 @@
"mainNav.home": "Beranda",
"mainNav.integrations": "Integrasi",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "Tidak ada aplikasi web ditemukan",
"mainNav.webApps.openApp": "Buka aplikasi web {{name}}",
"mainNav.webApps.searchPlaceholder": "Cari aplikasi web",
@ -503,6 +504,7 @@
"operation.sure": "Saya yakin",
"operation.toggleFullscreen": "Alihkan layar penuh",
"operation.toggleMute": "Alihkan bisu",
"operation.upload": "Upload",
"operation.view": "Lihat",
"operation.viewDetails": "Lihat Detail",
"operation.viewMore": "LIHAT LEBIH BANYAK",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview esegue lagente completato come lo vedranno gli utenti, con risposte pulite e funzionalità di chat.",
"agentDetail.configure.rightPanel.previewTipTitle": "Anteprima del tuo agente",
"agentDetail.configure.skills.add": "Aggiungi abilità",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Contenuto dei dettagli dellabilità",
"agentDetail.configure.skills.detail.fileCount": "{{count}} FILE",
"agentDetail.configure.skills.detail.files": "File",
"agentDetail.configure.skills.empty.description": "Le abilità forniscono allagente competenze riutilizzabili che può richiamare durante il lavoro",
"agentDetail.configure.skills.empty.title": "Nessuna abilità al momento",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Abilità",
"agentDetail.configure.skills.label": "Abilità",
"agentDetail.configure.skills.missing": "Abilità non trovata",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "Rimuovi {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Raggruppa istruzioni, file e script per un'attività ricorrente in una skill. Fai riferimento con / nel Prompt. <docLink>Scopri di più</docLink>\n\nIn modalità Build, l'agente può configurarli per te.",
"agentDetail.configure.skills.tip": "Raggruppa istruzioni, file e script per un'attività ricorrente in una skill. Fai riferimento con / nel Prompt. Scopri di più\n\nIn modalità Build, l'agente può configurarli per te.",
"agentDetail.configure.skills.toggle": "Attiva/disattiva abilità",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "Opzioni di ordinamento",
"roster.sort.recentlyCreated": "Creati di recente",
"roster.updateSuccess": "Agente aggiornato.",
"roster.usageStatus.draft": "Bozza"
"roster.usageStatus.draft": "Bozza",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "Comprimi barra laterale",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "Espandi barra laterale",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "Nessun file corrispondente.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Cerca file",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "Home",
"mainNav.integrations": "Integrazioni",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "Nessuna app web trovata",
"mainNav.webApps.openApp": "Apri l'app web {{name}}",
"mainNav.webApps.searchPlaceholder": "Cerca app web",
@ -504,6 +505,7 @@
"operation.sure": "Sono sicuro",
"operation.toggleFullscreen": "Attiva/disattiva schermo intero",
"operation.toggleMute": "Attiva/disattiva muto",
"operation.upload": "Upload",
"operation.view": "Vista",
"operation.viewDetails": "Visualizza dettagli",
"operation.viewMore": "SCOPRI DI PIÙ",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview は完成したエージェントをユーザーに見える形で実行し、整った返信とチャット機能を確認できます。",
"agentDetail.configure.rightPanel.previewTipTitle": "エージェントをプレビュー",
"agentDetail.configure.skills.add": "スキルを追加",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "スキル詳細コンテンツ",
"agentDetail.configure.skills.detail.fileCount": "{{count}} 個のファイル",
"agentDetail.configure.skills.detail.files": "ファイル",
"agentDetail.configure.skills.empty.description": "スキルはエージェントが作業中に呼び出せる再利用可能な専門知識を提供します",
"agentDetail.configure.skills.empty.title": "スキルはまだありません",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "スキル",
"agentDetail.configure.skills.label": "スキル",
"agentDetail.configure.skills.missing": "スキルが見つかりません",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "{{name}} を削除",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "繰り返し行うタスクに必要な指示、ファイル、スクリプトを Skill にまとめます。Prompt で / を使って参照します。<docLink>詳しく見る</docLink>\n\nBuild モードでは、エージェントがこれらを設定できます。",
"agentDetail.configure.skills.tip": "繰り返し行うタスクに必要な指示、ファイル、スクリプトを Skill にまとめます。Prompt で / を使って参照します。詳しく見る\n\nBuild モードでは、エージェントがこれらを設定できます。",
"agentDetail.configure.skills.toggle": "スキルの表示を切り替え",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "並び替えオプション",
"roster.sort.recentlyCreated": "最近作成",
"roster.updateSuccess": "エージェントを更新しました。",
"roster.usageStatus.draft": "ドラフト"
"roster.usageStatus.draft": "ドラフト",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "サイドバーを折りたたむ",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "サイドバーを展開",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "一致するファイルはありません。",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "ファイルを検索",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "ホーム",
"mainNav.integrations": "連携",
"mainNav.marketplace": "マーケットプレイス",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "Webアプリが見つかりません",
"mainNav.webApps.openApp": "{{name}} のWebアプリを開く",
"mainNav.webApps.searchPlaceholder": "Webアプリを検索",
@ -504,6 +505,7 @@
"operation.sure": "確認済み",
"operation.toggleFullscreen": "全画面表示を切り替え",
"operation.toggleMute": "ミュートを切り替え",
"operation.upload": "Upload",
"operation.view": "表示",
"operation.viewDetails": "詳細を見る",
"operation.viewMore": "さらに表示",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview는 사용자가 보게 될 방식으로 완성된 에이전트를 실행하며, 깔끔한 답변과 채팅 기능을 보여줍니다.",
"agentDetail.configure.rightPanel.previewTipTitle": "에이전트 미리보기",
"agentDetail.configure.skills.add": "스킬 추가",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "스킬 상세 내용",
"agentDetail.configure.skills.detail.fileCount": "파일 {{count}}개",
"agentDetail.configure.skills.detail.files": "파일",
"agentDetail.configure.skills.empty.description": "스킬은 에이전트가 작업하면서 호출할 수 있는 재사용 가능한 전문 능력을 제공합니다",
"agentDetail.configure.skills.empty.title": "아직 스킬이 없습니다",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "스킬",
"agentDetail.configure.skills.label": "스킬",
"agentDetail.configure.skills.missing": "스킬을 찾을 수 없음",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "{{name}} 제거",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "반복 작업에 필요한 지침, 파일, 스크립트를 Skill로 패키징하세요. Prompt에서 /로 참조하세요. <docLink>자세히 알아보기</docLink>\n\nBuild mode에서는 에이전트가 이를 대신 설정할 수 있습니다.",
"agentDetail.configure.skills.tip": "반복 작업에 필요한 지침, 파일, 스크립트를 Skill로 패키징하세요. Prompt에서 /로 참조하세요. 자세히 알아보기\n\nBuild mode에서는 에이전트가 이를 대신 설정할 수 있습니다.",
"agentDetail.configure.skills.toggle": "스킬 전환",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "정렬 옵션",
"roster.sort.recentlyCreated": "최근 생성",
"roster.updateSuccess": "에이전트가 업데이트되었습니다.",
"roster.usageStatus.draft": "초안"
"roster.usageStatus.draft": "초안",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "사이드바 접기",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "사이드바 펼치기",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "일치하는 파일이 없습니다.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "파일 검색",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "홈",
"mainNav.integrations": "연동",
"mainNav.marketplace": "마켓플레이스",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "웹 앱을 찾을 수 없습니다",
"mainNav.webApps.openApp": "{{name}} 웹 앱 열기",
"mainNav.webApps.searchPlaceholder": "웹 앱 검색",
@ -504,6 +505,7 @@
"operation.sure": "확인",
"operation.toggleFullscreen": "전체 화면 전환",
"operation.toggleMute": "음소거 전환",
"operation.upload": "Upload",
"operation.view": "보기",
"operation.viewDetails": "세부 정보보기",
"operation.viewMore": "더보기",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview voert de voltooide agent uit zoals je gebruikers die zien, met duidelijke antwoorden en chatfuncties.",
"agentDetail.configure.rightPanel.previewTipTitle": "Voorbeeld van je agent",
"agentDetail.configure.skills.add": "Skill toevoegen",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Skill-detailinhoud",
"agentDetail.configure.skills.detail.fileCount": "{{count}} BESTANDEN",
"agentDetail.configure.skills.detail.files": "Bestanden",
"agentDetail.configure.skills.empty.description": "Skills geven de agent herbruikbare expertise die hij tijdens het werken kan inzetten",
"agentDetail.configure.skills.empty.title": "Nog geen skills",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Skill",
"agentDetail.configure.skills.label": "Skills",
"agentDetail.configure.skills.missing": "Skill niet gevonden",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "{{name}} verwijderen",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Bundel de instructies, bestanden en scripts voor een terugkerende taak in een skill. Verwijs ernaar met / in de Prompt. <docLink>Meer informatie</docLink>\n\nIn de Build-modus kan de agent dit voor u instellen.",
"agentDetail.configure.skills.tip": "Bundel de instructies, bestanden en scripts voor een terugkerende taak in een skill. Verwijs ernaar met / in de Prompt. Meer informatie\n\nIn de Build-modus kan de agent dit voor u instellen.",
"agentDetail.configure.skills.toggle": "Skills in/uit",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "Sorteeropties",
"roster.sort.recentlyCreated": "Recent aangemaakt",
"roster.updateSuccess": "Agent bijgewerkt.",
"roster.usageStatus.draft": "Concept"
"roster.usageStatus.draft": "Concept",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "Zijbalk inklappen",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "Zijbalk uitklappen",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "Geen overeenkomende bestanden.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Bestanden zoeken",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -201,6 +201,7 @@
"mainNav.home": "Start",
"mainNav.integrations": "Integraties",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "Geen webapps gevonden",
"mainNav.webApps.openApp": "Webapp {{name}} openen",
"mainNav.webApps.searchPlaceholder": "Webapps zoeken",
@ -503,6 +504,7 @@
"operation.sure": "I'm sure",
"operation.toggleFullscreen": "Volledig scherm schakelen",
"operation.toggleMute": "Dempen schakelen",
"operation.upload": "Upload",
"operation.view": "View",
"operation.viewDetails": "View Details",
"operation.viewMore": "VIEW MORE",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview uruchamia gotowego agenta tak, jak zobaczą go użytkownicy, z przejrzystymi odpowiedziami i funkcjami czatu.",
"agentDetail.configure.rightPanel.previewTipTitle": "Podgląd agenta",
"agentDetail.configure.skills.add": "Dodaj umiejętność",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Zawartość szczegółów umiejętności",
"agentDetail.configure.skills.detail.fileCount": "{{count}} PLIKÓW",
"agentDetail.configure.skills.detail.files": "Pliki",
"agentDetail.configure.skills.empty.description": "Umiejętności dają agentowi reużywalną wiedzę, którą może wywołać podczas pracy",
"agentDetail.configure.skills.empty.title": "Brak umiejętności",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Umiejętność",
"agentDetail.configure.skills.label": "Umiejętności",
"agentDetail.configure.skills.missing": "Nie znaleziono umiejętności",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "Usuń {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Spakuj instrukcje, pliki i skrypty dla powtarzalnego zadania w skill. Odwołuj się za pomocą / w Prompt. <docLink>Dowiedz się więcej</docLink>\n\nW trybie Build agent może skonfigurować je za Ciebie.",
"agentDetail.configure.skills.tip": "Spakuj instrukcje, pliki i skrypty dla powtarzalnego zadania w skill. Odwołuj się za pomocą / w Prompt. Dowiedz się więcej\n\nW trybie Build agent może skonfigurować je za Ciebie.",
"agentDetail.configure.skills.toggle": "Przełącz umiejętności",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "Opcje sortowania",
"roster.sort.recentlyCreated": "Ostatnio utworzone",
"roster.updateSuccess": "Agent zaktualizowany.",
"roster.usageStatus.draft": "Wersja robocza"
"roster.usageStatus.draft": "Wersja robocza",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "Zwiń pasek boczny",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "Rozwiń pasek boczny",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "Brak pasujących plików.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Szukaj plików",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "Strona główna",
"mainNav.integrations": "Integracje",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "Nie znaleziono aplikacji webowych",
"mainNav.webApps.openApp": "Otwórz aplikację webową {{name}}",
"mainNav.webApps.searchPlaceholder": "Szukaj aplikacji webowych",
@ -504,6 +505,7 @@
"operation.sure": "Jestem pewien",
"operation.toggleFullscreen": "Przełącz pełny ekran",
"operation.toggleMute": "Przełącz wyciszenie",
"operation.upload": "Upload",
"operation.view": "Widok",
"operation.viewDetails": "Wyświetl szczegóły",
"operation.viewMore": "ZOBACZ WIĘCEJ",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview executa o agente finalizado como seus usuários o verão, com respostas claras e recursos de chat.",
"agentDetail.configure.rightPanel.previewTipTitle": "Pré-visualize seu agente",
"agentDetail.configure.skills.add": "Adicionar habilidade",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Conteúdo dos detalhes da habilidade",
"agentDetail.configure.skills.detail.fileCount": "{{count}} ARQUIVOS",
"agentDetail.configure.skills.detail.files": "Arquivos",
"agentDetail.configure.skills.empty.description": "As habilidades dão ao agente expertise reutilizável que ele pode invocar enquanto trabalha",
"agentDetail.configure.skills.empty.title": "Ainda não há habilidades",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Habilidade",
"agentDetail.configure.skills.label": "Habilidades",
"agentDetail.configure.skills.missing": "Habilidade não encontrada",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "Remover {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Empacote as instruções, arquivos e scripts de uma tarefa recorrente em uma skill. Referencie com / no Prompt. <docLink>Saiba mais</docLink>\n\nNo modo Build, o agente pode configurar isso para você.",
"agentDetail.configure.skills.tip": "Empacote as instruções, arquivos e scripts de uma tarefa recorrente em uma skill. Referencie com / no Prompt. Saiba mais\n\nNo modo Build, o agente pode configurar isso para você.",
"agentDetail.configure.skills.toggle": "Alternar habilidades",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "Opções de ordenação",
"roster.sort.recentlyCreated": "Criados recentemente",
"roster.updateSuccess": "Agente atualizado.",
"roster.usageStatus.draft": "Rascunho"
"roster.usageStatus.draft": "Rascunho",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "Recolher barra lateral",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "Expandir barra lateral",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "Nenhum arquivo correspondente.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Buscar arquivos",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "Início",
"mainNav.integrations": "Integrações",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "Nenhum app web encontrado",
"mainNav.webApps.openApp": "Abrir o app web {{name}}",
"mainNav.webApps.searchPlaceholder": "Pesquisar apps web",
@ -504,6 +505,7 @@
"operation.sure": "Tenho certeza",
"operation.toggleFullscreen": "Alternar tela cheia",
"operation.toggleMute": "Alternar mudo",
"operation.upload": "Upload",
"operation.view": "Vista",
"operation.viewDetails": "Ver detalhes",
"operation.viewMore": "VER MAIS",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview rulează agentul finalizat așa cum îl vor vedea utilizatorii, cu răspunsuri clare și funcții de chat.",
"agentDetail.configure.rightPanel.previewTipTitle": "Previzualizează agentul",
"agentDetail.configure.skills.add": "Adaugă abilitate",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Conținutul detaliilor abilității",
"agentDetail.configure.skills.detail.fileCount": "{{count}} FIȘIERE",
"agentDetail.configure.skills.detail.files": "Fișiere",
"agentDetail.configure.skills.empty.description": "Abilitățile oferă agentului expertiză reutilizabilă pe care o poate apela în timpul lucrului",
"agentDetail.configure.skills.empty.title": "Nicio abilitate încă",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Abilitate",
"agentDetail.configure.skills.label": "Abilități",
"agentDetail.configure.skills.missing": "Abilitatea nu a fost găsită",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "Elimină {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Împachetați instrucțiunile, fișierele și scripturile pentru o sarcină recurentă într-un skill. Faceți referire cu / în Prompt. <docLink>Aflați mai multe</docLink>\n\nÎn modul Build, agentul le poate configura pentru dvs.",
"agentDetail.configure.skills.tip": "Împachetați instrucțiunile, fișierele și scripturile pentru o sarcină recurentă într-un skill. Faceți referire cu / în Prompt. Aflați mai multe\n\nÎn modul Build, agentul le poate configura pentru dvs.",
"agentDetail.configure.skills.toggle": "Comută abilitățile",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "Opțiuni de sortare",
"roster.sort.recentlyCreated": "Create recent",
"roster.updateSuccess": "Agent actualizat.",
"roster.usageStatus.draft": "Ciornă"
"roster.usageStatus.draft": "Ciornă",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "Restrânge bara laterală",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "Extinde bara laterală",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "Nu există fișiere potrivite.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Caută fișiere",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "Acasă",
"mainNav.integrations": "Integrări",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "Nu s-au găsit aplicații web",
"mainNav.webApps.openApp": "Deschide aplicația web {{name}}",
"mainNav.webApps.searchPlaceholder": "Caută aplicații web",
@ -504,6 +505,7 @@
"operation.sure": "Sunt sigur",
"operation.toggleFullscreen": "Comută ecran complet",
"operation.toggleMute": "Comută sunetul",
"operation.upload": "Upload",
"operation.view": "Vedere",
"operation.viewDetails": "Vezi detalii",
"operation.viewMore": "VEZI MAI MULT",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview запускает готового агента так, как его увидят пользователи, с чистыми ответами и функциями чата.",
"agentDetail.configure.rightPanel.previewTipTitle": "Предпросмотр агента",
"agentDetail.configure.skills.add": "Добавить навык",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Содержимое деталей навыка",
"agentDetail.configure.skills.detail.fileCount": "{{count}} ФАЙЛОВ",
"agentDetail.configure.skills.detail.files": "Файлы",
"agentDetail.configure.skills.empty.description": "Навыки дают агенту переиспользуемую экспертизу, которую он может вызывать в работе",
"agentDetail.configure.skills.empty.title": "Пока нет навыков",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Навык",
"agentDetail.configure.skills.label": "Навыки",
"agentDetail.configure.skills.missing": "Навык не найден",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "Удалить {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Упакуйте инструкции, файлы и скрипты для повторяющейся задачи в Skill. Ссылайтесь на него через / в Prompt. <docLink>Подробнее</docLink>\n\nВ режиме Build агент может настроить это за вас.",
"agentDetail.configure.skills.tip": "Упакуйте инструкции, файлы и скрипты для повторяющейся задачи в Skill. Ссылайтесь на него через / в Prompt. Подробнее\n\nВ режиме Build агент может настроить это за вас.",
"agentDetail.configure.skills.toggle": "Переключить навыки",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "Параметры сортировки",
"roster.sort.recentlyCreated": "Недавно созданные",
"roster.updateSuccess": "Агент обновлён.",
"roster.usageStatus.draft": "Черновик"
"roster.usageStatus.draft": "Черновик",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "Свернуть боковую панель",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "Развернуть боковую панель",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "Нет подходящих файлов.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Поиск файлов",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "Главная",
"mainNav.integrations": "Интеграции",
"mainNav.marketplace": "Маркетплейс",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "Веб-приложения не найдены",
"mainNav.webApps.openApp": "Открыть веб-приложение {{name}}",
"mainNav.webApps.searchPlaceholder": "Поиск веб-приложений",
@ -504,6 +505,7 @@
"operation.sure": "Я уверен",
"operation.toggleFullscreen": "Переключить полноэкранный режим",
"operation.toggleMute": "Переключить звук",
"operation.upload": "Upload",
"operation.view": "Вид",
"operation.viewDetails": "Подробнее",
"operation.viewMore": "ПОДРОБНЕЕ",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview zažene dokončanega agenta tako, kot ga bodo videli uporabniki, z jasnimi odgovori in funkcijami klepeta.",
"agentDetail.configure.rightPanel.previewTipTitle": "Predogled agenta",
"agentDetail.configure.skills.add": "Dodaj veščino",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Vsebina podrobnosti veščine",
"agentDetail.configure.skills.detail.fileCount": "{{count}} DATOTEK",
"agentDetail.configure.skills.detail.files": "Datoteke",
"agentDetail.configure.skills.empty.description": "Veščine agentu dajejo ponovno uporabno strokovnost, ki jo lahko kliče med delom",
"agentDetail.configure.skills.empty.title": "Še ni veščin",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Veščina",
"agentDetail.configure.skills.label": "Veščine",
"agentDetail.configure.skills.missing": "Veščina ni bila najdena",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "Odstrani {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Navodila, datoteke in skripte za ponavljajočo se nalogo zapakirajte v skill. Sklicujte se z / v Promptu. <docLink>Več informacij</docLink>\n\nV načinu Build jih lahko agent nastavi namesto vas.",
"agentDetail.configure.skills.tip": "Navodila, datoteke in skripte za ponavljajočo se nalogo zapakirajte v skill. Sklicujte se z / v Promptu. Več informacij\n\nV načinu Build jih lahko agent nastavi namesto vas.",
"agentDetail.configure.skills.toggle": "Preklopi veščine",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "Možnosti razvrščanja",
"roster.sort.recentlyCreated": "Nedavno ustvarjeno",
"roster.updateSuccess": "Agent posodobljen.",
"roster.usageStatus.draft": "Osnutek"
"roster.usageStatus.draft": "Osnutek",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "Strni stransko vrstico",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "Razširi stransko vrstico",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "Ni ujemajočih se datotek.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Išči datoteke",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -201,6 +201,7 @@
"mainNav.home": "Domov",
"mainNav.integrations": "Integracije",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "Ni najdenih spletnih aplikacij",
"mainNav.webApps.openApp": "Odpri spletno aplikacijo {{name}}",
"mainNav.webApps.searchPlaceholder": "Išči spletne aplikacije",
@ -503,6 +504,7 @@
"operation.sure": "Prepričan sem",
"operation.toggleFullscreen": "Preklopi celozaslonski način",
"operation.toggleMute": "Preklopi utišanje",
"operation.upload": "Upload",
"operation.view": "Pogled",
"operation.viewDetails": "Poglej podrobnosti",
"operation.viewMore": "POGLEJ VEČ",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview เรียกใช้เอเจนต์ที่เสร็จแล้วในแบบที่ผู้ใช้จะเห็น พร้อมคำตอบที่ชัดเจนและฟีเจอร์แชท",
"agentDetail.configure.rightPanel.previewTipTitle": "ดูตัวอย่างเอเจนต์",
"agentDetail.configure.skills.add": "เพิ่มทักษะ",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "เนื้อหารายละเอียดทักษะ",
"agentDetail.configure.skills.detail.fileCount": "{{count}} ไฟล์",
"agentDetail.configure.skills.detail.files": "ไฟล์",
"agentDetail.configure.skills.empty.description": "ทักษะให้ตัวแทนมีความเชี่ยวชาญที่นำกลับมาใช้ใหม่ได้ซึ่งสามารถเรียกใช้ได้ขณะทำงาน",
"agentDetail.configure.skills.empty.title": "ยังไม่มีทักษะ",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "ทักษะ",
"agentDetail.configure.skills.label": "ทักษะ",
"agentDetail.configure.skills.missing": "ไม่พบทักษะ",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "ลบ {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "แพ็กเกจคำสั่ง ไฟล์ และสคริปต์สำหรับงานที่ทำซ้ำเป็น Skill อ้างอิงด้วย / ใน Prompt <docLink>เรียนรู้เพิ่มเติม</docLink>\n\nในโหมด Build agent สามารถตั้งค่าสิ่งเหล่านี้ให้คุณได้",
"agentDetail.configure.skills.tip": "แพ็กเกจคำสั่ง ไฟล์ และสคริปต์สำหรับงานที่ทำซ้ำเป็น Skill อ้างอิงด้วย / ใน Prompt เรียนรู้เพิ่มเติม\n\nในโหมด Build agent สามารถตั้งค่าสิ่งเหล่านี้ให้คุณได้",
"agentDetail.configure.skills.toggle": "สลับทักษะ",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "ตัวเลือกการเรียง",
"roster.sort.recentlyCreated": "สร้างล่าสุด",
"roster.updateSuccess": "อัปเดตตัวแทนแล้ว",
"roster.usageStatus.draft": "ฉบับร่าง"
"roster.usageStatus.draft": "ฉบับร่าง",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "ยุบแถบด้านข้าง",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "ขยายแถบด้านข้าง",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "ไม่พบไฟล์ที่ตรงกัน",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "ค้นหาไฟล์",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "หน้าแรก",
"mainNav.integrations": "การผสานรวม",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "ไม่พบเว็บแอป",
"mainNav.webApps.openApp": "เปิดเว็บแอป {{name}}",
"mainNav.webApps.searchPlaceholder": "ค้นหาเว็บแอป",
@ -504,6 +505,7 @@
"operation.sure": "ฉันแน่ใจ",
"operation.toggleFullscreen": "สลับเต็มหน้าจอ",
"operation.toggleMute": "สลับปิดเสียง",
"operation.upload": "Upload",
"operation.view": "ทิวทัศน์",
"operation.viewDetails": "ดูรายละเอียด",
"operation.viewMore": "ดูเพิ่มเติม",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview, tamamlanan ajanı kullanıcılarınızın göreceği şekilde, temiz yanıtlar ve sohbet özellikleriyle çalıştırır.",
"agentDetail.configure.rightPanel.previewTipTitle": "Ajanınızı önizleyin",
"agentDetail.configure.skills.add": "Beceri ekle",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Beceri detay içeriği",
"agentDetail.configure.skills.detail.fileCount": "{{count}} DOSYA",
"agentDetail.configure.skills.detail.files": "Dosyalar",
"agentDetail.configure.skills.empty.description": "Beceriler, ajana çalışırken çağırabileceği yeniden kullanılabilir uzmanlık sağlar",
"agentDetail.configure.skills.empty.title": "Henüz beceri yok",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Beceri",
"agentDetail.configure.skills.label": "Beceriler",
"agentDetail.configure.skills.missing": "Beceri bulunamadı",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "{{name}} kaldır",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Tekrarlanan bir görev için gereken talimatları, dosyaları ve betikleri bir Skill olarak paketleyin. Prompt içinde / ile referans verin. <docLink>Daha fazla bilgi</docLink>\n\nBuild mode'da agent bunları sizin için ayarlayabilir.",
"agentDetail.configure.skills.tip": "Tekrarlanan bir görev için gereken talimatları, dosyaları ve betikleri bir Skill olarak paketleyin. Prompt içinde / ile referans verin. Daha fazla bilgi\n\nBuild mode'da agent bunları sizin için ayarlayabilir.",
"agentDetail.configure.skills.toggle": "Becerileri değiştir",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "Sıralama seçenekleri",
"roster.sort.recentlyCreated": "Yeni oluşturulanlar",
"roster.updateSuccess": "Ajan güncellendi.",
"roster.usageStatus.draft": "Taslak"
"roster.usageStatus.draft": "Taslak",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "Kenar çubuğunu daralt",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "Kenar çubuğunu genişlet",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "Eşleşen dosya yok.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Dosya ara",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -201,6 +201,7 @@
"mainNav.home": "Ana sayfa",
"mainNav.integrations": "Entegrasyonlar",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "Web uygulaması bulunamadı",
"mainNav.webApps.openApp": "{{name}} web uygulamasını aç",
"mainNav.webApps.searchPlaceholder": "Web uygulamalarında ara",
@ -503,6 +504,7 @@
"operation.sure": "Eminim",
"operation.toggleFullscreen": "Tam ekranı aç/kapat",
"operation.toggleMute": "Sessize al/aç",
"operation.upload": "Upload",
"operation.view": "Görüntüle",
"operation.viewDetails": "Detayları Görüntüle",
"operation.viewMore": "DAHA FAZLA GÖSTER",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview запускає готового агента так, як його побачать користувачі, з чистими відповідями та функціями чату.",
"agentDetail.configure.rightPanel.previewTipTitle": "Попередній перегляд агента",
"agentDetail.configure.skills.add": "Додати навичку",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Вміст деталей навички",
"agentDetail.configure.skills.detail.fileCount": "{{count}} ФАЙЛІВ",
"agentDetail.configure.skills.detail.files": "Файли",
"agentDetail.configure.skills.empty.description": "Навички дають агенту перевикористовну експертизу, яку він може викликати під час роботи",
"agentDetail.configure.skills.empty.title": "Поки що немає навичок",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Навичка",
"agentDetail.configure.skills.label": "Навички",
"agentDetail.configure.skills.missing": "Навичку не знайдено",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "Видалити {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Запакуйте інструкції, файли та скрипти для повторюваного завдання в Skill. Посилайтеся на нього через / у Prompt. <docLink>Докладніше</docLink>\n\nУ режимі Build агент може налаштувати це за вас.",
"agentDetail.configure.skills.tip": "Запакуйте інструкції, файли та скрипти для повторюваного завдання в Skill. Посилайтеся на нього через / у Prompt. Докладніше\n\nУ режимі Build агент може налаштувати це за вас.",
"agentDetail.configure.skills.toggle": "Перемкнути навички",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "Параметри сортування",
"roster.sort.recentlyCreated": "Нещодавно створені",
"roster.updateSuccess": "Агента оновлено.",
"roster.usageStatus.draft": "Чернетка"
"roster.usageStatus.draft": "Чернетка",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "Згорнути бічну панель",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "Розгорнути бічну панель",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "Немає відповідних файлів.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Пошук файлів",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "Головна",
"mainNav.integrations": "Інтеграції",
"mainNav.marketplace": "Маркетплейс",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "Вебзастосунки не знайдено",
"mainNav.webApps.openApp": "Відкрити вебзастосунок {{name}}",
"mainNav.webApps.searchPlaceholder": "Пошук вебзастосунків",
@ -504,6 +505,7 @@
"operation.sure": "Я впевнений",
"operation.toggleFullscreen": "Перемкнути повноекранний режим",
"operation.toggleMute": "Перемкнути звук",
"operation.upload": "Upload",
"operation.view": "Вид",
"operation.viewDetails": "Перегляд докладних відомостей",
"operation.viewMore": "ДИВИТИСЬ БІЛЬШЕ",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "Preview chạy agent hoàn chỉnh theo cách người dùng sẽ thấy, với câu trả lời rõ ràng và các tính năng chat.",
"agentDetail.configure.rightPanel.previewTipTitle": "Xem trước agent",
"agentDetail.configure.skills.add": "Thêm kỹ năng",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Nội dung chi tiết kỹ năng",
"agentDetail.configure.skills.detail.fileCount": "{{count}} TỆP",
"agentDetail.configure.skills.detail.files": "Tệp",
"agentDetail.configure.skills.empty.description": "Kỹ năng mang đến cho tác nhân chuyên môn có thể tái sử dụng mà nó có thể gọi khi làm việc",
"agentDetail.configure.skills.empty.title": "Chưa có kỹ năng nào",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Kỹ năng",
"agentDetail.configure.skills.label": "Kỹ năng",
"agentDetail.configure.skills.missing": "Không tìm thấy kỹ năng",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "Xóa {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "Đóng gói hướng dẫn, tệp và script cho một tác vụ lặp lại thành skill. Tham chiếu bằng / trong Prompt. <docLink>Tìm hiểu thêm</docLink>\n\nỞ chế độ Build, tác nhân có thể thiết lập những mục này cho bạn.",
"agentDetail.configure.skills.tip": "Đóng gói hướng dẫn, tệp và script cho một tác vụ lặp lại thành skill. Tham chiếu bằng / trong Prompt. Tìm hiểu thêm\n\nỞ chế độ Build, tác nhân có thể thiết lập những mục này cho bạn.",
"agentDetail.configure.skills.toggle": "Bật/tắt kỹ năng",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "Tùy chọn sắp xếp",
"roster.sort.recentlyCreated": "Mới tạo gần đây",
"roster.updateSuccess": "Đã cập nhật tác nhân.",
"roster.usageStatus.draft": "Bản nháp"
"roster.usageStatus.draft": "Bản nháp",
"skillManagement.clearTags": "Clear tags",
"skillManagement.create": "Create",
"skillManagement.createFailed": "Failed to create skill.",
"skillManagement.createSuccess": "Skill created.",
"skillManagement.dateTimeFormat": "MMM D, YYYY HH:mm",
"skillManagement.deleteDialog.description": "This skill will be removed from the workspace. Agents that reference it may lose access to this capability.",
"skillManagement.deleteDialog.title": "Delete {{name}}?",
"skillManagement.deleteFailed": "Failed to delete skill.",
"skillManagement.deleteSuccess": "Skill deleted.",
"skillManagement.detail.addMetadata": "Add metadata",
"skillManagement.detail.addMetadataDescription": "Add a frontmatter field to this Markdown file.",
"skillManagement.detail.addTag": "Add tag",
"skillManagement.detail.addTagDescription": "Create or bind a tag to this Skill. New tags are saved with the Skill metadata.",
"skillManagement.detail.addTagSuccess": "Tag added.",
"skillManagement.detail.back": "Back to Skills",
"skillManagement.detail.cancelAddMetadata": "Cancel adding metadata",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "Thu gọn thanh bên",
"skillManagement.detail.createFile": "New file",
"skillManagement.detail.createFileDescription": "Enter a file path relative to the skill root.",
"skillManagement.detail.createFileMenu": "New file...",
"skillManagement.detail.createFileSuccess": "File created.",
"skillManagement.detail.createFolder": "New folder",
"skillManagement.detail.createFolderDescription": "Enter a folder path relative to the skill root.",
"skillManagement.detail.createFolderMenu": "New folder...",
"skillManagement.detail.createFolderSuccess": "Folder created.",
"skillManagement.detail.createdBy": "Created by {{name}}",
"skillManagement.detail.currentDraft": "Current draft",
"skillManagement.detail.deleteFileConfirm": "Delete this file?",
"skillManagement.detail.deleteFileSuccess": "File deleted.",
"skillManagement.detail.deleteVersionConfirm": "Delete this version?",
"skillManagement.detail.deleteVersionFailed": "Failed to delete version.",
"skillManagement.detail.deleteVersionSuccess": "Version deleted.",
"skillManagement.detail.downloadFile": "Download file",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "Mở rộng thanh bên",
"skillManagement.detail.fileCount": "{{count}} FILES",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} bytes",
"skillManagement.detail.fileMissing": "File not found.",
"skillManagement.detail.fileOperationFailed": "File operation failed.",
"skillManagement.detail.files": "Files",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Failed to load skill.",
"skillManagement.detail.markdownLiveMode": "Live preview",
"skillManagement.detail.markdownSourceMode": "Source editor",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "File moved.",
"skillManagement.detail.moveFilesSuccess": "Files moved.",
"skillManagement.detail.noFileSelected": "Select a file to preview.",
"skillManagement.detail.noFiles": "No files.",
"skillManagement.detail.noSearchResults": "Không có tệp phù hợp.",
"skillManagement.detail.noVersions": "No published versions yet.",
"skillManagement.detail.previewUnsupported": "Preview is not supported for this file.",
"skillManagement.detail.publish": "Publish",
"skillManagement.detail.publishFailed": "Failed to publish skill.",
"skillManagement.detail.publishSuccess": "Skill published.",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "Read only",
"skillManagement.detail.referenceFiles.confirm": "Enter Confirm",
"skillManagement.detail.referenceFiles.empty": "No files available.",
"skillManagement.detail.referenceFiles.livePlaceholder": "Write instructions for the agent, type / to reference files",
"skillManagement.detail.referenceFiles.navigate": "↑↓ Navigate",
"skillManagement.detail.referenceFiles.title": "Reference files",
"skillManagement.detail.referencedBy": "Referenced by {{count}} Apps",
"skillManagement.detail.removeMetadata": "Delete {{name}} metadata",
"skillManagement.detail.removeTag": "Remove {{tag}}",
"skillManagement.detail.removeTagSuccess": "Tag removed.",
"skillManagement.detail.renameFile": "Rename file",
"skillManagement.detail.renameFileDescription": "Enter the new path relative to the skill root.",
"skillManagement.detail.renameFileSuccess": "File renamed.",
"skillManagement.detail.renameVersion": "Rename",
"skillManagement.detail.renameVersionFailed": "Failed to rename version.",
"skillManagement.detail.renameVersionPrompt": "Version name",
"skillManagement.detail.renameVersionSuccess": "Version renamed.",
"skillManagement.detail.restoreVersion": "Restore",
"skillManagement.detail.restoreVersionFailed": "Failed to restore version.",
"skillManagement.detail.restoreVersionSuccess": "Version restored to draft.",
"skillManagement.detail.save": "Save",
"skillManagement.detail.saveFailed": "Failed to save file.",
"skillManagement.detail.saveSuccess": "File saved.",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "Tìm kiếm tệp",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "Failed to update tags.",
"skillManagement.detail.uploadFile": "Upload file",
"skillManagement.detail.uploadFileFailed": "File upload failed.",
"skillManagement.detail.uploadFileSuccess": "File uploaded.",
"skillManagement.detail.uploadFilesMenu": "Upload files...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "Versions",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Failed to duplicate skill.",
"skillManagement.duplicateSuccess": "Skill duplicated.",
"skillManagement.editedAt": "Edited {{time}}",
"skillManagement.empty": "No skills yet",
"skillManagement.emptySearch": "No skills found",
"skillManagement.import": "Import",
"skillManagement.importFailed": "Failed to import skill.",
"skillManagement.importSuccess": "Skill imported.",
"skillManagement.listLabel": "Workspace skills",
"skillManagement.loadingError": "Failed to load skills",
"skillManagement.moreActions": "More actions for {{name}}",
"skillManagement.noTags": "No tags",
"skillManagement.publishedAt": "Published {{time}}",
"skillManagement.referenceCount": "{{count}} Refs",
"skillManagement.searchLabel": "Search skills",
"skillManagement.searchPlaceholder": "Search",
"skillManagement.tags": "Tags",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "Trang chủ",
"mainNav.integrations": "Tích hợp",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "Không tìm thấy ứng dụng web",
"mainNav.webApps.openApp": "Mở ứng dụng web {{name}}",
"mainNav.webApps.searchPlaceholder": "Tìm kiếm ứng dụng web",
@ -504,6 +505,7 @@
"operation.sure": "Tôi chắc chắn",
"operation.toggleFullscreen": "Chuyển đổi toàn màn hình",
"operation.toggleMute": "Bật/tắt tiếng",
"operation.upload": "Upload",
"operation.view": "Cảnh",
"operation.viewDetails": "Xem chi tiết",
"operation.viewMore": "XEM THÊM",

View File

@ -203,15 +203,24 @@
"agentDetail.configure.rightPanel.previewTipBody": "像用户一样测试它。你的交互不会影响 Agent 后续的行为。",
"agentDetail.configure.rightPanel.previewTipTitle": "预览你的 Agent",
"agentDetail.configure.skills.add": "添加 Skill",
"agentDetail.configure.skills.addMenu.upload.badge": "内嵌",
"agentDetail.configure.skills.addMenu.upload.description": "包含 SKILL.md 的 .zip 包。它会嵌入当前应用,不会跟随 Skill 库更新。",
"agentDetail.configure.skills.addMenu.upload.label": "上传包",
"agentDetail.configure.skills.addMenu.workspace.description": "复用共享 Skill并跟随其发布版本更新。",
"agentDetail.configure.skills.addMenu.workspace.label": "从 Skill 库选择",
"agentDetail.configure.skills.detail.contentRegion": "Skill 详情内容",
"agentDetail.configure.skills.detail.fileCount": "{{count}} 个文件",
"agentDetail.configure.skills.detail.files": "文件",
"agentDetail.configure.skills.empty.description": "Skill 为 Agent 提供工作时可调用的可复用专业能力",
"agentDetail.configure.skills.empty.title": "暂无 Skill",
"agentDetail.configure.skills.fromSkillLibrary": "从 Skill 库选择",
"agentDetail.configure.skills.itemType": "Skill",
"agentDetail.configure.skills.label": "Skill",
"agentDetail.configure.skills.missing": "未找到 Skill",
"agentDetail.configure.skills.moreActions": "{{name}} 的更多操作",
"agentDetail.configure.skills.openInLibrary": "在 Skill 库中打开",
"agentDetail.configure.skills.remove": "移除 {{name}}",
"agentDetail.configure.skills.removeAction": "移除",
"agentDetail.configure.skills.richTip": "将重复任务所需的指令、文件和脚本打包成 Skill。在提示词中用 / 引用。<docLink>了解更多</docLink>\n\n在构建模式中Agent 可帮你完成这些设置。",
"agentDetail.configure.skills.tip": "将重复任务所需的指令、文件和脚本打包成 Skill。在提示词中用 / 引用。了解更多\n\n在构建模式中Agent 可帮你完成这些设置。",
"agentDetail.configure.skills.toggle": "展开或收起 Skill",
@ -226,6 +235,15 @@
"agentDetail.configure.skills.upload.title": "上传 Skill",
"agentDetail.configure.skills.upload.warning.files": "如果只需要使用 Markdown 文件,请将它们上传到「文件」,并在你的提示词中引用。",
"agentDetail.configure.skills.upload.warning.specification": "上传的 Skill 需遵循 <specificationLink>Agent Skills 规范</specificationLink>。",
"agentDetail.configure.skills.workspaceItemType": "Workspace",
"agentDetail.configure.skills.workspaceSelector.addSuccess": "Workspace Skill 已添加。",
"agentDetail.configure.skills.workspaceSelector.added": "已添加",
"agentDetail.configure.skills.workspaceSelector.draft": "Draft",
"agentDetail.configure.skills.workspaceSelector.empty": "未找到 Workspace Skill。",
"agentDetail.configure.skills.workspaceSelector.manage": "在 Skills 中管理",
"agentDetail.configure.skills.workspaceSelector.removeSuccess": "Workspace Skill 已移除。",
"agentDetail.configure.skills.workspaceSelector.saveFailed": "Workspace Skill 更新失败。",
"agentDetail.configure.skills.workspaceSelector.search": "搜索 Skill...",
"agentDetail.configure.title": "配置",
"agentDetail.configure.tools.add": "添加工具",
"agentDetail.configure.tools.addMenu.cliTool.badge": "开发者适用",
@ -419,5 +437,178 @@
"roster.sort.optionsLabel": "排序选项",
"roster.sort.recentlyCreated": "最近创建",
"roster.updateSuccess": "Agent 已更新。",
"roster.usageStatus.draft": "草稿"
"roster.usageStatus.draft": "草稿",
"skillManagement.clearTags": "清除标签",
"skillManagement.create": "创建",
"skillManagement.createFailed": "Skill 创建失败。",
"skillManagement.createSuccess": "Skill 已创建。",
"skillManagement.dateTimeFormat": "YYYY-MM-DD HH:mm",
"skillManagement.deleteDialog.description": "该 Skill 将从 workspace 中移除。引用它的 Agent 可能失去对应能力。",
"skillManagement.deleteDialog.title": "删除 {{name}}",
"skillManagement.deleteFailed": "Skill 删除失败。",
"skillManagement.deleteSuccess": "Skill 已删除。",
"skillManagement.detail.addMetadata": "添加元数据",
"skillManagement.detail.addMetadataDescription": "为这个 Markdown 文件添加一个 frontmatter 字段。",
"skillManagement.detail.addTag": "添加标签",
"skillManagement.detail.addTagDescription": "创建或绑定一个标签到这个 Skill。新标签会随 Skill 元信息保存。",
"skillManagement.detail.addTagSuccess": "标签已添加。",
"skillManagement.detail.back": "返回 Skills",
"skillManagement.detail.builder.attach": "添加文件",
"skillManagement.detail.builder.attachFailed": "添加文件失败。",
"skillManagement.detail.builder.attachUnsupported": "只能添加文本和文档文件。",
"skillManagement.detail.builder.attachmentOnlyMessage": "根据附件内容帮助完善这个 Skill。",
"skillManagement.detail.builder.close": "关闭 Skill Builder",
"skillManagement.detail.builder.compatibleModelsOnly": "仅显示兼容模型",
"skillManagement.detail.builder.exampleIssueTriage": "客户问题分级处理",
"skillManagement.detail.builder.exampleOnboarding": "新员工入职引导",
"skillManagement.detail.builder.exampleSalesFollowUp": "销售线索跟进策略",
"skillManagement.detail.builder.followUpDisplayName": "使用 Refund approval 作为显示名称",
"skillManagement.detail.builder.followUpNameIcon": "应用建议的名称和图标",
"skillManagement.detail.builder.fromMarketplace": "来自 Marketplace",
"skillManagement.detail.builder.model": "GPT-4o",
"skillManagement.detail.builder.modelCredits.all": "全部额度",
"skillManagement.detail.builder.modelCredits.configure": "需要配置",
"skillManagement.detail.builder.modelCredits.exhausted": "额度已用尽",
"skillManagement.detail.builder.modelProviderSettings": "模型供应商设置",
"skillManagement.detail.builder.modelSearch": "搜索模型...",
"skillManagement.detail.builder.modifyPlaceholder": "让 AI 修改这个 Skill...",
"skillManagement.detail.builder.open": "打开 Skill Builder",
"skillManagement.detail.builder.placeholder": "描述场景...",
"skillManagement.detail.builder.promptDescription": "描述它,草稿会出现在编辑器中,并包含步骤和文件。",
"skillManagement.detail.builder.promptTitle": "这个 Skill 应该处理什么?",
"skillManagement.detail.builder.removeAttachment": "移除 {{name}}",
"skillManagement.detail.builder.restart": "重新开始 Builder",
"skillManagement.detail.builder.send": "发送消息",
"skillManagement.detail.builder.sendFailed": "Skill Builder 响应失败。",
"skillManagement.detail.builder.title": "Skill Builder",
"skillManagement.detail.builder.tryExample": "试试示例",
"skillManagement.detail.builder.voice": "语音输入",
"skillManagement.detail.builder.voiceUnavailable": "暂不支持语音输入。",
"skillManagement.detail.cancelAddMetadata": "取消添加元数据",
"skillManagement.detail.closeFileTab": "关闭 {{name}}",
"skillManagement.detail.closeVersions": "关闭版本面板",
"skillManagement.detail.collapseSidebar": "折叠侧边栏",
"skillManagement.detail.copyFile": "复制",
"skillManagement.detail.copyFileSuccess": "文件已复制到剪贴板。",
"skillManagement.detail.copyVersionId": "复制 ID",
"skillManagement.detail.copyVersionIdSuccess": "ID 已复制到剪贴板。",
"skillManagement.detail.createFile": "新建文件",
"skillManagement.detail.createFileDescription": "输入相对于 Skill 根目录的文件路径。",
"skillManagement.detail.createFileMenu": "新建文件...",
"skillManagement.detail.createFileSuccess": "文件已创建。",
"skillManagement.detail.createFolder": "新建文件夹",
"skillManagement.detail.createFolderDescription": "输入相对于 Skill 根目录的文件夹路径。",
"skillManagement.detail.createFolderMenu": "新建文件夹...",
"skillManagement.detail.createFolderSuccess": "文件夹已创建。",
"skillManagement.detail.createdBy": "由 {{name}} 创建",
"skillManagement.detail.currentDraft": "当前草稿",
"skillManagement.detail.cutFile": "剪切",
"skillManagement.detail.cutFileSuccess": "文件已剪切到剪贴板。",
"skillManagement.detail.deleteFileConfirm": "删除这个文件?",
"skillManagement.detail.deleteFileSuccess": "文件已删除。",
"skillManagement.detail.deleteVersionConfirm": "删除这个版本?",
"skillManagement.detail.deleteVersionFailed": "版本删除失败。",
"skillManagement.detail.deleteVersionSuccess": "版本已删除。",
"skillManagement.detail.downloadFile": "下载文件",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.editVersionInfo": "编辑版本信息",
"skillManagement.detail.exitVersions": "退出版本",
"skillManagement.detail.expandSidebar": "展开侧边栏",
"skillManagement.detail.fileCount": "{{count}} 个文件",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} 字节",
"skillManagement.detail.fileMissing": "文件不存在。",
"skillManagement.detail.fileOperationFailed": "文件操作失败。",
"skillManagement.detail.files": "文件",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Skill 加载失败。",
"skillManagement.detail.markdownLiveMode": "实时预览",
"skillManagement.detail.markdownSourceMode": "源码编辑",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "文件已移动。",
"skillManagement.detail.moveFilesSuccess": "文件已移动。",
"skillManagement.detail.nameThisVersion": "命名这个版本",
"skillManagement.detail.noFileSelected": "选择一个文件进行预览。",
"skillManagement.detail.noFiles": "暂无文件。",
"skillManagement.detail.noSearchResults": "没有匹配的文件。",
"skillManagement.detail.noVersions": "暂无发布版本。",
"skillManagement.detail.pasteFile": "粘贴",
"skillManagement.detail.pasteFileSuccess": "文件已粘贴。",
"skillManagement.detail.previewUnsupported": "该文件暂不支持预览。",
"skillManagement.detail.publish": "发布",
"skillManagement.detail.publishFailed": "Skill 发布失败。",
"skillManagement.detail.publishReferencesDescription": "发布后,这个草稿会对 {{count}} 个正在使用此 Skill 的引用生效。",
"skillManagement.detail.publishReferencesTitle": "发布 Skill",
"skillManagement.detail.publishSuccess": "Skill 已发布。",
"skillManagement.detail.publishUpdate": "发布更新",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "只读",
"skillManagement.detail.referenceFiles.confirm": "Enter 确认",
"skillManagement.detail.referenceFiles.empty": "暂无可引用的文件。",
"skillManagement.detail.referenceFiles.livePlaceholder": "编写给 Agent 的说明,输入 / 引用文件",
"skillManagement.detail.referenceFiles.navigate": "↑↓ 导航",
"skillManagement.detail.referenceFiles.title": "引用文件",
"skillManagement.detail.referencedBy": "被 {{count}} 个应用引用",
"skillManagement.detail.removeMetadata": "删除 {{name}} 元数据",
"skillManagement.detail.removeTag": "移除 {{tag}}",
"skillManagement.detail.removeTagSuccess": "标签已移除。",
"skillManagement.detail.renameFile": "重命名文件",
"skillManagement.detail.renameFileDescription": "输入相对于 Skill 根目录的新路径。",
"skillManagement.detail.renameFileSuccess": "文件已重命名。",
"skillManagement.detail.renameVersion": "重命名",
"skillManagement.detail.renameVersionFailed": "版本重命名失败。",
"skillManagement.detail.renameVersionPrompt": "版本名称",
"skillManagement.detail.renameVersionSuccess": "版本已重命名。",
"skillManagement.detail.restoreVersion": "恢复",
"skillManagement.detail.restoreVersionFailed": "版本恢复失败。",
"skillManagement.detail.restoreVersionSuccess": "版本已恢复到草稿。",
"skillManagement.detail.save": "保存",
"skillManagement.detail.saveFailed": "文件保存失败。",
"skillManagement.detail.saveSuccess": "文件已保存。",
"skillManagement.detail.saved": "已保存",
"skillManagement.detail.savedAt": "{{time}}已保存",
"skillManagement.detail.saving": "保存中...",
"skillManagement.detail.searchFiles": "查找文件",
"skillManagement.detail.unsavedChanges": "有未保存的更改",
"skillManagement.detail.updateTagsFailed": "标签更新失败。",
"skillManagement.detail.uploadFile": "上传文件",
"skillManagement.detail.uploadFileFailed": "文件上传失败。",
"skillManagement.detail.uploadFileSuccess": "文件已上传。",
"skillManagement.detail.uploadFilesFailedStatus": "{{count}} 个文件上传失败。",
"skillManagement.detail.uploadFilesMenu": "上传文件...",
"skillManagement.detail.uploadFilesProgress": "上传中 {{completed}}/{{total}}",
"skillManagement.detail.uploadFilesResult": "{{uploaded}} 个已上传 · {{failed}} 个失败",
"skillManagement.detail.uploadFilesStatus": "上传状态",
"skillManagement.detail.uploadStatusDismiss": "关闭上传状态",
"skillManagement.detail.versionHistory": "打开版本历史",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versionPublishNote": "发布说明",
"skillManagement.detail.versionPublishNotePlaceholder": "请描述变更",
"skillManagement.detail.versionPublishedMeta": "{{time}} · {{name}}",
"skillManagement.detail.versionTitle": "标题",
"skillManagement.detail.versions": "版本",
"skillManagement.detail.viewOnly": "仅查看",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Skill 复制失败。",
"skillManagement.duplicateSuccess": "Skill 已复制。",
"skillManagement.editedAt": "编辑于 {{time}}",
"skillManagement.empty": "暂无 Skill",
"skillManagement.emptyAction.createDescription": "用自然语言描述场景,生成一个可 review 的 Skill 草稿。",
"skillManagement.emptyAction.createTitle": "使用 Skill Builder 创建",
"skillManagement.emptyAction.importDescription": "导入包含 SKILL.md 且符合 agentskills.io 格式的 .zip 包。",
"skillManagement.emptyAction.importTitle": "导入 Skill 包",
"skillManagement.emptySearch": "未找到 Skill",
"skillManagement.import": "导入",
"skillManagement.importFailed": "Skill 导入失败。",
"skillManagement.importSuccess": "Skill 已导入。",
"skillManagement.listLabel": "Workspace Skills",
"skillManagement.loadingError": "Skill 加载失败",
"skillManagement.moreActions": "{{name}} 的更多操作",
"skillManagement.noTags": "暂无标签",
"skillManagement.publishedAt": "发布于 {{time}}",
"skillManagement.referenceCount": "{{count}} 个引用",
"skillManagement.searchLabel": "搜索 Skill",
"skillManagement.searchPlaceholder": "搜索",
"skillManagement.tags": "标签",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "主页",
"mainNav.integrations": "集成",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "未找到 Web 应用",
"mainNav.webApps.openApp": "打开 {{name}} Web 应用",
"mainNav.webApps.searchPlaceholder": "搜索 Web 应用",
@ -504,6 +505,7 @@
"operation.sure": "我确定",
"operation.toggleFullscreen": "切换全屏",
"operation.toggleMute": "切换静音",
"operation.upload": "上传",
"operation.view": "查看",
"operation.viewDetails": "查看详情",
"operation.viewMore": "查看更多",

View File

@ -203,15 +203,22 @@
"agentDetail.configure.rightPanel.previewTipBody": "像使用者一樣測試它。你的互動不會影響 Agent 後續的行為。",
"agentDetail.configure.rightPanel.previewTipTitle": "預覽你的 Agent",
"agentDetail.configure.skills.add": "新增 Skill",
"agentDetail.configure.skills.addMenu.upload.badge": "EMBEDDED",
"agentDetail.configure.skills.addMenu.upload.label": "Upload package",
"agentDetail.configure.skills.addMenu.workspace.label": "From skill library",
"agentDetail.configure.skills.detail.contentRegion": "Skill 詳情內容",
"agentDetail.configure.skills.detail.fileCount": "{{count}} 個檔案",
"agentDetail.configure.skills.detail.files": "檔案",
"agentDetail.configure.skills.empty.description": "Skill 為 Agent 提供工作時可呼叫的可複用專業能力",
"agentDetail.configure.skills.empty.title": "暫無 Skill",
"agentDetail.configure.skills.fromSkillLibrary": "From skill library",
"agentDetail.configure.skills.itemType": "Skill",
"agentDetail.configure.skills.label": "Skill",
"agentDetail.configure.skills.missing": "找不到 Skill",
"agentDetail.configure.skills.moreActions": "More actions for {{name}}",
"agentDetail.configure.skills.openInLibrary": "Open in Skill library",
"agentDetail.configure.skills.remove": "移除 {{name}}",
"agentDetail.configure.skills.removeAction": "Remove",
"agentDetail.configure.skills.richTip": "將重複任務所需的指令、檔案和指令碼打包成 Skill。在提示詞中用 / 引用。<docLink>了解更多</docLink>\n\n在建置模式中Agent 可幫你完成這些設定。",
"agentDetail.configure.skills.tip": "將重複任務所需的指令、檔案和指令碼打包成 Skill。在提示詞中用 / 引用。了解更多\n\n在建置模式中Agent 可幫你完成這些設定。",
"agentDetail.configure.skills.toggle": "展開或收合 Skill",
@ -419,5 +426,117 @@
"roster.sort.optionsLabel": "排序選項",
"roster.sort.recentlyCreated": "最近建立",
"roster.updateSuccess": "Agent 已更新。",
"roster.usageStatus.draft": "草稿"
"roster.usageStatus.draft": "草稿",
"skillManagement.clearTags": "清除標籤",
"skillManagement.create": "创建",
"skillManagement.createFailed": "Skill 建立失敗。",
"skillManagement.createSuccess": "Skill 已建立。",
"skillManagement.dateTimeFormat": "YYYY-MM-DD HH:mm",
"skillManagement.deleteDialog.description": "該 Skill 將從 workspace 中移除。引用它的 Agent 可能失去對應能力。",
"skillManagement.deleteDialog.title": "刪除 {{name}}",
"skillManagement.deleteFailed": "Skill 刪除失敗。",
"skillManagement.deleteSuccess": "Skill 已刪除。",
"skillManagement.detail.addMetadata": "新增元資料",
"skillManagement.detail.addMetadataDescription": "為這個 Markdown 檔案新增一個 frontmatter 欄位。",
"skillManagement.detail.addTag": "新增標籤",
"skillManagement.detail.addTagDescription": "建立或綁定一個標籤到這個 Skill。新標籤會隨 Skill 中繼資料保存。",
"skillManagement.detail.addTagSuccess": "標籤已新增。",
"skillManagement.detail.back": "返回 Skills",
"skillManagement.detail.cancelAddMetadata": "取消新增元資料",
"skillManagement.detail.closeFileTab": "Close {{name}}",
"skillManagement.detail.collapseSidebar": "摺疊側邊欄",
"skillManagement.detail.createFile": "新增檔案",
"skillManagement.detail.createFileDescription": "輸入相對於 Skill 根目錄的檔案路徑。",
"skillManagement.detail.createFileMenu": "新增檔案...",
"skillManagement.detail.createFileSuccess": "檔案已建立。",
"skillManagement.detail.createFolder": "新增資料夾",
"skillManagement.detail.createFolderDescription": "輸入相對於 Skill 根目錄的資料夾路徑。",
"skillManagement.detail.createFolderMenu": "新增資料夾...",
"skillManagement.detail.createFolderSuccess": "資料夾已建立。",
"skillManagement.detail.createdBy": "由 {{name}} 建立",
"skillManagement.detail.currentDraft": "目前草稿",
"skillManagement.detail.deleteFileConfirm": "刪除這個檔案?",
"skillManagement.detail.deleteFileSuccess": "檔案已刪除。",
"skillManagement.detail.deleteVersionConfirm": "刪除這個版本?",
"skillManagement.detail.deleteVersionFailed": "版本刪除失敗。",
"skillManagement.detail.deleteVersionSuccess": "版本已刪除。",
"skillManagement.detail.downloadFile": "下载文件",
"skillManagement.detail.draft": "Draft",
"skillManagement.detail.expandSidebar": "展開側邊欄",
"skillManagement.detail.fileCount": "{{count}} 個檔案",
"skillManagement.detail.fileMeta": "{{type}} · {{size}} 位元組",
"skillManagement.detail.fileMissing": "檔案不存在。",
"skillManagement.detail.fileOperationFailed": "檔案操作失敗。",
"skillManagement.detail.files": "檔案",
"skillManagement.detail.latest": "Latest",
"skillManagement.detail.loadFailed": "Skill 載入失敗。",
"skillManagement.detail.markdownLiveMode": "即時預覽",
"skillManagement.detail.markdownSourceMode": "原始碼編輯",
"skillManagement.detail.metadataKey": "name",
"skillManagement.detail.metadataValue": "value",
"skillManagement.detail.moveFileSuccess": "檔案已移動。",
"skillManagement.detail.moveFilesSuccess": "檔案已移動。",
"skillManagement.detail.noFileSelected": "選擇一個檔案進行預覽。",
"skillManagement.detail.noFiles": "暫無檔案。",
"skillManagement.detail.noSearchResults": "沒有符合的檔案。",
"skillManagement.detail.noVersions": "暫無發布版本。",
"skillManagement.detail.previewUnsupported": "該檔案暫不支援預覽。",
"skillManagement.detail.publish": "發布",
"skillManagement.detail.publishFailed": "Skill 發布失敗。",
"skillManagement.detail.publishSuccess": "Skill 已發布。",
"skillManagement.detail.published": "Published",
"skillManagement.detail.readonly": "唯讀",
"skillManagement.detail.referenceFiles.confirm": "Enter 確認",
"skillManagement.detail.referenceFiles.empty": "暫無可引用的檔案。",
"skillManagement.detail.referenceFiles.livePlaceholder": "編寫給 Agent 的說明,輸入 / 引用檔案",
"skillManagement.detail.referenceFiles.navigate": "↑↓ 導覽",
"skillManagement.detail.referenceFiles.title": "引用檔案",
"skillManagement.detail.referencedBy": "被 {{count}} 個應用引用",
"skillManagement.detail.removeMetadata": "刪除 {{name}} 元資料",
"skillManagement.detail.removeTag": "移除 {{tag}}",
"skillManagement.detail.removeTagSuccess": "標籤已移除。",
"skillManagement.detail.renameFile": "重新命名檔案",
"skillManagement.detail.renameFileDescription": "輸入相對於 Skill 根目錄的新路徑。",
"skillManagement.detail.renameFileSuccess": "檔案已重新命名。",
"skillManagement.detail.renameVersion": "重新命名",
"skillManagement.detail.renameVersionFailed": "版本重新命名失敗。",
"skillManagement.detail.renameVersionPrompt": "版本名称",
"skillManagement.detail.renameVersionSuccess": "版本已重新命名。",
"skillManagement.detail.restoreVersion": "還原",
"skillManagement.detail.restoreVersionFailed": "版本還原失敗。",
"skillManagement.detail.restoreVersionSuccess": "版本已還原到草稿。",
"skillManagement.detail.save": "儲存",
"skillManagement.detail.saveFailed": "檔案儲存失敗。",
"skillManagement.detail.saveSuccess": "檔案已儲存。",
"skillManagement.detail.saved": "Saved",
"skillManagement.detail.savedAt": "Saved {{time}}",
"skillManagement.detail.saving": "Saving...",
"skillManagement.detail.searchFiles": "搜尋檔案",
"skillManagement.detail.unsavedChanges": "Unsaved changes",
"skillManagement.detail.updateTagsFailed": "標籤更新失敗。",
"skillManagement.detail.uploadFile": "上傳檔案",
"skillManagement.detail.uploadFileFailed": "檔案上傳失敗。",
"skillManagement.detail.uploadFileSuccess": "檔案已上傳。",
"skillManagement.detail.uploadFilesMenu": "上傳檔案...",
"skillManagement.detail.versionMeta": "{{hash}} · {{time}}",
"skillManagement.detail.versions": "版本",
"skillManagement.draft": "Draft",
"skillManagement.duplicateFailed": "Skill 複製失敗。",
"skillManagement.duplicateSuccess": "Skill 已複製。",
"skillManagement.editedAt": "編輯於 {{time}}",
"skillManagement.empty": "暫無 Skill",
"skillManagement.emptySearch": "未找到 Skill",
"skillManagement.import": "匯入",
"skillManagement.importFailed": "Skill 匯入失敗。",
"skillManagement.importSuccess": "Skill 已匯入。",
"skillManagement.listLabel": "Workspace Skills",
"skillManagement.loadingError": "Skill 載入失敗",
"skillManagement.moreActions": "{{name}} 的更多操作",
"skillManagement.noTags": "暫無標籤",
"skillManagement.publishedAt": "發布於 {{time}}",
"skillManagement.referenceCount": "{{count}} 個引用",
"skillManagement.searchLabel": "搜尋 Skill",
"skillManagement.searchPlaceholder": "搜索",
"skillManagement.tags": "標籤",
"skillManagement.title": "Skills"
}

View File

@ -202,6 +202,7 @@
"mainNav.home": "首頁",
"mainNav.integrations": "集成",
"mainNav.marketplace": "Marketplace",
"mainNav.skills": "Skills",
"mainNav.webApps.noResults": "未找到 Web 應用",
"mainNav.webApps.openApp": "開啟 {{name}} Web 應用",
"mainNav.webApps.searchPlaceholder": "搜尋 Web 應用",
@ -504,6 +505,7 @@
"operation.sure": "我確定",
"operation.toggleFullscreen": "切換全螢幕",
"operation.toggleMute": "切換靜音",
"operation.upload": "上傳",
"operation.view": "視圖",
"operation.viewDetails": "查看詳情",
"operation.viewMore": "查看更多",