Revert "feat: support agent skill"

This reverts commit 991116990a.
This commit is contained in:
fatelei 2026-07-22 12:48:29 +08:00
parent 991116990a
commit 865a618fd5
No known key found for this signature in database
GPG Key ID: 2F91DA05646F4EED
91 changed files with 1067 additions and 21141 deletions

View File

@ -141,7 +141,6 @@ from .workspace import (
models,
plugin,
rbac,
skills,
snippets,
tool_providers,
trigger_providers,
@ -221,7 +220,6 @@ __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", "skill", ""] = Field("", description="Tag type filter")
type: Literal["knowledge", "app", "snippet", ""] = Field("", description="Tag type filter")
keyword: str | None = Field(None, description="Search keyword")

View File

@ -1,848 +0,0 @@
"""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,7 +23,6 @@ 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)
@ -37,7 +36,6 @@ __all__ = [
"_mail",
"_plugin",
"_runtime_credentials",
"_skills",
"_workspace",
"api",
"bp",

View File

@ -1,54 +0,0 @@
"""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

@ -1,115 +0,0 @@
"""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

@ -112,7 +112,6 @@ 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
@ -171,7 +170,6 @@ __all__ = [
"AgentRuntimeSessionOwnerType",
"AgentRuntimeSessionStatus",
"AgentScope",
"AgentSkillBinding",
"AgentSource",
"AgentStatus",
"ApiRequest",
@ -245,11 +243,6 @@ __all__ = [
"RecommendedApp",
"SavedMessage",
"Site",
"Skill",
"SkillDraftFile",
"SkillFileKind",
"SkillFileStorage",
"SkillVersion",
"SnippetType",
"Tag",
"TagBinding",

View File

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

View File

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

View File

@ -1,165 +0,0 @@
"""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,7 +46,6 @@ 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):
@ -99,7 +98,6 @@ class ConfigPushPayload(BaseModel):
@dataclass(slots=True)
class AgentConfigTarget:
tenant_id: str
agent_id: str
version_id: str
kind: AgentConfigVersionKind
@ -148,7 +146,6 @@ 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,
@ -194,7 +191,7 @@ class AgentConfigService:
return {
"agent_id": target.agent_id,
"config_version": self._config_version_payload(target),
"items": self._skill_items_for_target(target),
"items": [self._serialize_skill_item(skill) for skill in target.agent_soul.config_skills],
}
def list_files(
@ -236,26 +233,9 @@ class AgentConfigService:
config_version_kind=config_version_kind,
user_id=user_id,
)
try:
skill = self._require_skill(target.agent_soul, name=name)
payload, mime_type = self._load_tool_file_bytes(tenant_id=tenant_id, file_id=skill.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
skill = self._require_skill(target.agent_soul, name=name)
payload, mime_type = self._load_tool_file_bytes(tenant_id=tenant_id, file_id=skill.file_id)
return ConfigDownload(filename=f"{skill.name}.zip", mime_type=mime_type or "application/zip", payload=payload)
def download_skill_url(
self,
@ -297,44 +277,8 @@ class AgentConfigService:
config_version_kind=config_version_kind,
user_id=user_id,
)
try:
skill = self._require_skill(target.agent_soul, name=name)
archive_bytes, _mime_type = self._load_tool_file_bytes(tenant_id=tenant_id, file_id=skill.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",
},
)
skill = self._require_skill(target.agent_soul, name=name)
archive_bytes, _mime_type = self._load_tool_file_bytes(tenant_id=tenant_id, file_id=skill.file_id)
try:
archive_items, skill_md = self._inspect_skill_archive(archive_bytes)
except (OSError, ValueError, zipfile.BadZipFile) as exc:
@ -344,7 +288,7 @@ class AgentConfigService:
status_code=500,
) from exc
return {
**skill_item,
**self._serialize_skill_item(skill),
"source": "config_skill_zip",
"files": archive_items,
"skill_md": skill_md,
@ -886,7 +830,6 @@ class AgentConfigService:
status_code=404,
)
return AgentConfigTarget(
tenant_id=tenant_id,
agent_id=agent_id,
version_id=version.id,
kind=config_version_kind,
@ -1181,7 +1124,9 @@ class AgentConfigService:
return {
"agent_id": target.agent_id,
"config_version": AgentConfigService._config_version_payload(target),
"skills": {"items": AgentConfigService._skill_items_for_target(target)},
"skills": {
"items": [AgentConfigService._serialize_skill_item(skill) for skill in target.agent_soul.config_skills]
},
"files": {
"items": [
AgentConfigService._serialize_file_item(file_ref) for file_ref in target.agent_soul.config_files
@ -1191,20 +1136,6 @@ 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,7 +12,6 @@ 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
@ -283,13 +282,5 @@ 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

@ -1,394 +0,0 @@
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

@ -62,7 +62,6 @@ def _target(
) -> AgentConfigTarget:
agent_soul = soul or _soul()
return AgentConfigTarget(
tenant_id=TENANT,
agent_id=AGENT,
version_id=version_id,
kind=kind,
@ -494,9 +493,7 @@ def test_manifest_uses_items_shape_without_download_urls() -> None:
),
)
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)
manifest = AgentConfigService._manifest_for_target(target)
assert manifest == {
"agent_id": AGENT,
@ -535,44 +532,6 @@ def test_manifest_uses_items_shape_without_download_urls() -> 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' | 'skill' | 'snippet'
export type TagType = 'app' | 'knowledge' | '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', 'skill', 'snippet'])
export const zTagType = z.enum(['app', 'knowledge', 'snippet'])
/**
* TagBindingPayload

View File

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

View File

@ -29,7 +29,7 @@ export const zTagUpdateRequestPayload = z.object({
*
* Tag type
*/
export const zTagType = z.enum(['app', 'knowledge', 'skill', 'snippet'])
export const zTagType = z.enum(['app', 'knowledge', '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', 'skill', 'snippet']).optional().default(''),
type: z.enum(['', 'app', 'knowledge', 'snippet']).optional().default(''),
})
/**

File diff suppressed because it is too large Load Diff

View File

@ -31,16 +31,6 @@ 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
@ -626,193 +616,6 @@ 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 = {
@ -1220,21 +1023,6 @@ 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
@ -1675,60 +1463,6 @@ 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
@ -2205,10 +1939,6 @@ export type PermissionCatalogItem = {
name: string
}
export type SkillFileKind = 'directory' | 'file'
export type SkillFileStorage = 'text' | 'tool_file'
export type ToolParameter = {
auto_generate?: PluginParameterAutoGenerate | null
default?:
@ -2703,38 +2433,6 @@ 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
@ -4878,369 +4576,6 @@ 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,13 +12,6 @@ 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
*
@ -431,186 +424,6 @@ 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
*/
@ -830,33 +643,6 @@ 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
*
@ -1397,132 +1183,6 @@ 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
*/
@ -2375,42 +2035,6 @@ 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
*/
@ -3735,26 +3359,6 @@ 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(),
@ -5024,234 +4628,6 @@ 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,7 +18,6 @@ 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

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

View File

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

View File

@ -42,50 +42,14 @@ describe('AgentRosterResponseContent', () => {
fireEvent.click(screen.getByRole('button', { name: /workFinished/i }))
expect(processToggle).toHaveAttribute('aria-expanded', 'true')
await waitFor(
() => {
expect(screen.getByText('history answer')).toBeInTheDocument()
},
{ timeout: 5000 },
)
await waitFor(() => {
expect(screen.getByTestId('agent-roster-response-content')).toHaveTextContent('history answer')
})
expect(screen.queryByText('internal thought should not render')).not.toBeInTheDocument()
})
it('should preserve historical answer whitespace when rendering markdown', async () => {
const user = userEvent.setup()
const item = {
id: 'answer-history-code',
content: '',
isAnswer: true,
agent_thoughts: [
{
id: 'thought-history-code',
thought: 'internal thought should not render',
answer: ' const answer = 42',
tool: '',
tool_input: '',
observation: '',
message_id: 'answer-history-code',
conversation_id: 'conversation-history-code',
position: 1,
},
],
} satisfies ChatItem
render(<AgentRosterResponseContent item={item} />)
await user.click(screen.getByRole('button', { name: 'Thinking' }))
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 () => {
it('should render new agent response parts in event order when thoughts and messages interleave', async () => {
const item = {
id: 'answer-1',
content: 'first answer second answer',

View File

@ -1,32 +1,46 @@
import type { FC, ReactNode } from 'react'
import type { DefaultModel, FormValue, Model, ModelParameterRule } from '../declarations'
import type {
FC,
ReactNode,
} from 'react'
import type {
DefaultModel,
FormValue,
ModelParameterRule,
} from '../declarations'
import type { ParameterValue } from './parameter-item'
import type { TriggerProps } from './trigger'
import type { Node, NodeOutPutVar } from '@/app/components/workflow/types'
import type {
Node,
NodeOutPutVar,
} from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
import { Popover, PopoverClose, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { useMemo, useState } from 'react'
import {
Popover,
PopoverClose,
PopoverContent,
PopoverTrigger,
} from '@langgenius/dify-ui/popover'
import { useMemo, useRef, 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'
import { useTextGenerationCurrentProviderAndModelAndModelList } from '../hooks'
import {
useTextGenerationCurrentProviderAndModelAndModelList,
} from '../hooks'
import ModelSelector from '../model-selector'
import ParameterItem from './parameter-item'
import PresetsParameter from './presets-parameter'
import { getSupportedPresetConfig } from './presets-parameter-utils'
import Trigger from './trigger'
export type ModelParameterModalProps = {
popupClassName?: string
isAdvancedMode: boolean
modelId: string
provider: string
setModel: (model: {
modelId: string
provider: string
mode?: string
features?: string[]
}) => void
setModel: (model: { modelId: string, provider: string, mode?: string, features?: string[] }) => void
completionParams: FormValue
onCompletionParamsChange: (newParams: FormValue) => void
hideDebugWithMultipleModel?: boolean
@ -35,7 +49,6 @@ export type ModelParameterModalProps = {
renderTrigger?: (v: TriggerProps) => ReactNode
readonly?: boolean
isInWorkflow?: boolean
modelList?: Model[]
scope?: string
nodesOutputVars?: NodeOutPutVar[]
availableNodes?: Node[]
@ -55,29 +68,31 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
renderTrigger,
readonly,
isInWorkflow,
modelList,
nodesOutputVars,
availableNodes,
}) => {
const { t } = useTranslation()
const [open, setOpen] = useState(false)
const { data: parameterRulesData, isLoading } = useModelParameterRules(provider, modelId)
const settingsIconRef = useRef<HTMLDivElement>(null)
const {
data: parameterRulesData,
isLoading,
} = useModelParameterRules(provider, modelId)
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 {
currentProvider,
currentModel,
activeTextGenerationModelList,
} = useTextGenerationCurrentProviderAndModelAndModelList(
{ provider, model: modelId },
)
const parameterRules: ModelParameterRule[] = useMemo(() => {
return parameterRulesData?.data || []
}, [parameterRulesData])
const supportedPresetParameterNames = useMemo(() => {
return parameterRules.map((parameterRule) => parameterRule.name)
return parameterRules.map(parameterRule => parameterRule.name)
}, [parameterRules])
const hasSelectedModel = !!provider && !!modelId
const handleParamChange = (key: string, value: ParameterValue) => {
onCompletionParamsChange({
@ -87,10 +102,8 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
}
const handleChangeModel = ({ provider, model }: DefaultModel) => {
const targetProvider = availableTextGenerationModelList.find(
(modelItem) => modelItem.provider === provider,
)
const targetModelItem = targetProvider?.models?.find((modelItem) => modelItem.model === model)
const targetProvider = activeTextGenerationModelList.find(modelItem => modelItem.provider === provider)
const targetModelItem = targetProvider?.models.find(modelItem => modelItem.model === model)
setModel({
modelId: model,
provider,
@ -98,10 +111,6 @@ 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) {
@ -129,60 +138,42 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
<Popover
open={open}
onOpenChange={(newOpen) => {
if (readonly) return
if (readonly)
return
setOpen(newOpen)
}}
>
{renderTrigger ? (
<PopoverTrigger
render={
<button
type="button"
className="block w-full border-none bg-transparent p-0 text-left text-inherit [font:inherit]"
>
{renderTrigger({
open,
currentProvider: selectedProvider,
currentModel: selectedModel,
providerName: provider,
modelId,
})}
</button>
}
/>
) : (
<div className="flex h-8 min-w-[296px] items-center gap-px overflow-hidden rounded-lg">
<div className="min-w-0 flex-1">
<ModelSelector
defaultModel={provider || modelId ? { provider, model: modelId } : undefined}
modelList={availableTextGenerationModelList}
readonly={readonly}
triggerClassName={cn(
'h-8! w-full rounded-r-none!',
isInWorkflow &&
'border border-workflow-block-parma-bg bg-workflow-block-parma-bg hover:bg-workflow-block-parma-bg',
)}
onSelect={handleChangeModel}
onOpenProviderSettings={handleOpenModelSettings}
/>
</div>
<PopoverTrigger
aria-label={t(($) => $['modelProvider.modelSettings'], { ns: 'common' })}
disabled={readonly || !hasSelectedModel}
className={cn(
'flex size-8 shrink-0 items-center justify-center rounded-l-none rounded-r-lg border-0 bg-components-button-tertiary-bg p-0 text-text-tertiary outline-hidden hover:bg-components-button-tertiary-bg-hover hover:text-text-secondary focus-visible:ring-2 focus-visible:ring-state-accent-solid disabled:cursor-not-allowed disabled:text-text-disabled',
isInWorkflow &&
'border border-workflow-block-parma-bg bg-workflow-block-parma-bg hover:bg-workflow-block-parma-bg',
)}
>
<span aria-hidden className="i-ri-equalizer-2-line size-4" />
</PopoverTrigger>
</div>
)}
<PopoverTrigger
render={(
<button type="button" className="block w-full border-none bg-transparent p-0 text-left text-inherit [font:inherit]">
{
renderTrigger
? renderTrigger({
open,
currentProvider,
currentModel,
providerName: provider,
modelId,
})
: (
<Trigger
isInWorkflow={isInWorkflow}
currentProvider={currentProvider}
currentModel={currentModel}
providerName={provider}
modelId={modelId}
settingsRef={settingsIconRef}
/>
)
}
</button>
)}
/>
<PopoverContent
placement={isInWorkflow ? 'left' : renderTrigger ? 'bottom-end' : 'left-start'}
placement={isInWorkflow ? 'left' : (renderTrigger ? 'bottom-end' : 'left-start')}
sideOffset={4}
popupClassName={cn(popupClassName, 'w-[400px] rounded-2xl')}
positionerProps={!renderTrigger ? { anchor: settingsIconRef } : undefined}
>
<div className="relative px-3 pt-3.5 pb-1">
<div className="pr-8 pl-1 system-xl-semibold text-text-primary">
@ -193,71 +184,69 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
</PopoverClose>
</div>
<div className="max-h-[420px] overflow-y-auto">
{renderTrigger && (
<div className="px-4 pt-2 pb-4">
<ModelSelector
defaultModel={hasSelectedModel ? { provider, model: modelId } : undefined}
modelList={availableTextGenerationModelList}
onSelect={handleChangeModel}
onOpenProviderSettings={handleOpenModelSettings}
onHide={() => setOpen(false)}
/>
</div>
)}
{!!parameterRules.length && (
<div
className={cn(
'flex flex-col gap-2 px-4 pt-3 pb-4',
renderTrigger && 'border-t border-divider-subtle',
)}
>
<div className="flex items-center gap-1">
<div className="flex flex-1 items-center system-sm-semibold-uppercase text-text-secondary">
{t(($) => $['modelProvider.parameters'], { ns: 'common' })}
<div className="px-4 pt-2 pb-4">
<ModelSelector
defaultModel={(provider || modelId) ? { provider, model: modelId } : undefined}
modelList={activeTextGenerationModelList}
onSelect={handleChangeModel}
onHide={() => setOpen(false)}
/>
</div>
{
!!parameterRules.length && (
<div className="flex flex-col gap-2 border-t border-divider-subtle px-4 pt-3 pb-4">
<div className="flex items-center gap-1">
<div className="flex flex-1 items-center system-sm-semibold-uppercase text-text-secondary">{t('modelProvider.parameters', { ns: 'common' })}</div>
{
PROVIDER_WITH_PRESET_TONE.includes(provider) && (
<PresetsParameter
onSelect={handleSelectPresetParameter}
supportedParameterNames={supportedPresetParameterNames}
/>
)
}
</div>
{PROVIDER_WITH_PRESET_TONE.includes(provider) && (
<PresetsParameter
onSelect={handleSelectPresetParameter}
supportedParameterNames={supportedPresetParameterNames}
/>
)}
{
isRulesLoading
? <div className="py-5"><Loading /></div>
: (
[
...parameterRules,
...(isAdvancedMode ? [STOP_PARAMETER_RULE] : []),
].map(parameter => (
<ParameterItem
key={`${modelId}-${parameter.name}`}
parameterRule={parameter}
value={completionParams?.[parameter.name]}
onChange={v => handleParamChange(parameter.name, v)}
onSwitch={(checked, assignValue) => handleSwitch(parameter.name, checked, assignValue)}
isInWorkflow={isInWorkflow}
nodesOutputVars={nodesOutputVars}
availableNodes={availableNodes}
/>
))
)
}
</div>
{[...parameterRules, ...(isAdvancedMode ? [STOP_PARAMETER_RULE] : [])].map(
(parameter) => (
<ParameterItem
key={`${modelId}-${parameter.name}`}
parameterRule={parameter}
value={completionParams?.[parameter.name]}
onChange={(v) => handleParamChange(parameter.name, v)}
onSwitch={(checked, assignValue) =>
handleSwitch(parameter.name, checked, assignValue)
}
isInWorkflow={isInWorkflow}
nodesOutputVars={nodesOutputVars}
availableNodes={availableNodes}
/>
),
)}
</div>
)}
{!parameterRules.length && isRulesLoading && (
<div className="px-4 py-5">
<Loading />
</div>
)}
)
}
{
!parameterRules.length && isRulesLoading && (
<div className="px-4 py-5"><Loading /></div>
)
}
</div>
{!hideDebugWithMultipleModel && (
<div
className="flex h-[50px] cursor-pointer items-center justify-between rounded-b-xl border-t border-t-divider-subtle px-4 system-sm-regular text-text-accent"
onClick={() => onDebugWithMultipleModelChange?.()}
>
{debugWithMultipleModel
? t(($) => $.debugAsSingleModel, { ns: 'appDebug' })
: t(($) => $.debugAsMultipleModel, { ns: 'appDebug' })}
<span
aria-hidden
className="i-custom-vender-line-arrows-arrow-narrow-left size-3 rotate-180"
/>
{
debugWithMultipleModel
? t('debugAsSingleModel', { ns: 'appDebug' })
: t('debugAsMultipleModel', { ns: 'appDebug' })
}
<ArrowNarrowLeft className="size-3 rotate-180" />
</div>
)}
</PopoverContent>

View File

@ -14,7 +14,8 @@ import { getModelSelectorValueLabel, isSameModelSelectorValue } from './types'
const getModelProviderPluginId = (provider: string) => {
const [organization, pluginName] = provider.split('/').filter(Boolean)
if (organization && pluginName) return `${organization}/${pluginName}`
if (organization && pluginName)
return `${organization}/${pluginName}`
return provider ? `langgenius/${provider}` : ''
}
@ -33,7 +34,6 @@ type ModelSelectorProps = {
hideProviderSettingsFooter?: boolean
onConfigureEmptyState?: () => void
onOpenMarketplace?: () => void
onOpenProviderSettings?: () => void
providerSettingsSource?: 'agent'
showModelMeta?: boolean
modelPredicate?: ModelSelectorModelPredicate
@ -53,7 +53,6 @@ function ModelSelector({
hideProviderSettingsFooter,
onConfigureEmptyState,
onOpenMarketplace,
onOpenProviderSettings,
providerSettingsSource,
showModelMeta,
modelPredicate,
@ -62,9 +61,16 @@ function ModelSelector({
const { t } = useTranslation()
const [open, setOpen] = useState(false)
const [inputValue, setInputValue] = useState('')
const { currentProvider, currentModel } = useCurrentProviderAndModel(modelList, defaultModel)
const {
currentProvider,
currentModel,
} = useCurrentProviderAndModel(
modelList,
defaultModel,
)
const currentValue = useMemo<ModelSelectorValue | null>(() => {
if (!currentProvider || !currentModel) return null
if (!currentProvider || !currentModel)
return null
return {
provider: currentProvider.provider,
@ -72,53 +78,47 @@ function ModelSelector({
}
}, [currentModel, currentProvider])
const handleOpenChange = useCallback(
(newOpen: boolean) => {
if (readonly) return
const handleOpenChange = useCallback((newOpen: boolean) => {
if (readonly)
return
setOpen(newOpen)
if (!newOpen) setInputValue('')
},
[readonly],
)
const handleSelect = useCallback(
(provider: string, model: ModelItem) => {
setOpen(false)
setOpen(newOpen)
if (!newOpen)
setInputValue('')
}, [readonly])
if (onSelect) {
onSelect({
provider,
model: model.model,
plugin_id: getModelProviderPluginId(provider),
})
}
},
[onSelect],
)
const handleSelect = useCallback((provider: string, model: ModelItem) => {
setOpen(false)
setInputValue('')
const handleValueChange = useCallback(
(value: ModelSelectorValue | null) => {
if (!value) return
if (onSelect) {
onSelect({
provider,
model: model.model,
plugin_id: getModelProviderPluginId(provider),
})
}
}, [onSelect])
const provider = modelList.find((model) => model.provider === value.provider)
const model = provider?.models.find((model) => model.model === value.model)
const handleValueChange = useCallback((value: ModelSelectorValue | null) => {
if (!value)
return
if (!provider || !model) return
if (model.status !== ModelStatusEnum.active) return
const provider = modelList.find(model => model.provider === value.provider)
const model = provider?.models.find(model => model.model === value.model)
handleSelect(provider.provider, model)
},
[handleSelect, modelList],
)
if (!provider || !model)
return
if (model.status !== ModelStatusEnum.active)
return
const handleInputValueChange = useCallback(
(inputValue: string, details: ComboboxRootChangeEventDetails) => {
if (details.reason !== 'item-press') setInputValue(inputValue)
},
[],
)
handleSelect(provider.provider, model)
}, [handleSelect, modelList])
const handleInputValueChange = useCallback((inputValue: string, details: ComboboxRootChangeEventDetails) => {
if (details.reason !== 'item-press')
setInputValue(inputValue)
}, [])
const handleHide = useCallback(() => {
setOpen(false)
@ -159,11 +159,7 @@ function ModelSelector({
deprecatedClassName={deprecatedClassName}
showDeprecatedWarnIcon={showDeprecatedWarnIcon}
showModelMeta={showModelMeta}
isModelCompatible={
currentProvider && currentModel
? modelPredicate?.(currentProvider, currentModel)
: undefined
}
isModelCompatible={currentProvider && currentModel ? modelPredicate?.(currentProvider, currentModel) : undefined}
/>
</ComboboxTrigger>
<ComboboxContent
@ -182,7 +178,6 @@ function ModelSelector({
modelSuggestionPredicate={modelSuggestionPredicate}
onConfigureEmptyState={onConfigureEmptyState ? handleConfigureEmptyState : undefined}
onOpenMarketplace={onOpenMarketplace}
onOpenProviderSettings={onOpenProviderSettings}
onInputValueChange={setInputValue}
onHide={handleHide}
/>

View File

@ -9,6 +9,7 @@ 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'
@ -54,14 +55,15 @@ function PopupItem({
const { modelProviders } = useProviderContext()
const updateModelList = useUpdateModelList()
const updateModelProviders = useUpdateModelProviders()
const currentProvider = modelProviders.find((provider) => provider.provider === model.provider)
const currentProvider = modelProviders.find(provider => provider.provider === model.provider)
const { canUseCredential, canCreateCredential, canManageCredential } = useCredentialPermissions()
const canOpenCredentialDropdown =
!!currentProvider && (canUseCredential || canCreateCredential || canManageCredential)
const canOpenCredentialDropdown = canUseCredential || canCreateCredential || canManageCredential
const handleOpenModelModal = () => {
if (!canCreateCredential) return
if (!canCreateCredential)
return
if (!currentProvider) return
if (!currentProvider)
return
setShowModelModal({
payload: {
currentProvider,
@ -72,37 +74,33 @@ function PopupItem({
const modelType = model.models[0]!.model_type
if (modelType) updateModelList(modelType)
if (modelType)
updateModelList(modelType)
},
})
}
// 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 state = useCredentialPanelState(currentProvider)
const { isChangingPriority, handleChangePriority } = useChangeProviderPriority(currentProvider)
const groupItems = useMemo(
() =>
model.models
.filter((modelItem) => modelItem.status !== ModelStatusEnum.noConfigure)
.map((modelItem) => ({
provider: model.provider,
model: modelItem.model,
})),
[model.models, model.provider],
)
const groupItems = useMemo(() => model.models
.filter(modelItem => modelItem.status !== ModelStatusEnum.noConfigure)
.map(modelItem => ({
provider: model.provider,
model: modelItem.model,
})), [model.models, model.provider])
const isUsingCredits = credentialPanelState.priority === 'credits'
const hasCredits = !credentialPanelState.isCreditsExhausted
const isApiKeyActive =
credentialPanelState.variant === 'api-active' || credentialPanelState.variant === 'api-fallback'
const { credentialName } = credentialPanelState
const isUsingCredits = state.priority === 'credits'
const hasCredits = !state.isCreditsExhausted
const isApiKeyActive = state.variant === 'api-active' || state.variant === 'api-fallback'
const { credentialName } = state
const handleCloseDropdown = useCallback(() => {
setDropdownOpen(false)
onHide()
}, [onHide])
if (!currentProvider) return null
if (!currentProvider)
return null
return (
<ComboboxGroup className="mb-1" items={groupItems}>
@ -110,131 +108,117 @@ function PopupItem({
<button
type="button"
className="flex min-w-0 cursor-pointer items-center border-0 bg-transparent p-0 text-left"
onClick={() => setCollapsed((prev) => !prev)}
onClick={() => setCollapsed(prev => !prev)}
>
<span className="truncate">{model.label[language] || model.label.en_US}</span>
<span
className={cn(
'i-custom-vender-solid-general-arrow-down-round-fill size-4 shrink-0 text-text-quaternary',
collapsed && '-rotate-90',
)}
/>
<span className={cn('i-custom-vender-solid-general-arrow-down-round-fill size-4 shrink-0 text-text-quaternary', collapsed && '-rotate-90')} />
</button>
<Popover open={dropdownOpen} onOpenChange={setDropdownOpen}>
<PopoverTrigger
disabled={!canOpenCredentialDropdown}
render={
<button
type="button"
className="flex max-w-[50%] min-w-0 shrink-0 cursor-pointer items-center rounded-md px-1.5 py-1 system-xs-medium text-text-tertiary hover:bg-components-button-ghost-bg-hover"
>
{isUsingCredits ? (
hasCredits ? (
<>
<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>
</>
) : (
<>
<span className="i-ri-alert-fill size-3 shrink-0 text-text-warning-secondary" />
<span className="ml-1 truncate text-text-warning">
{t(($) => $['modelProvider.selector.creditsExhausted'], { ns: 'common' })}
</span>
</>
)
) : credentialName ? (
<>
<StatusDot size="small" status={isApiKeyActive ? 'success' : 'error'} />
<span className="ml-1 truncate text-text-tertiary">{credentialName}</span>
</>
) : (
<>
<StatusDot size="small" status="disabled" />
<span className="ml-1 truncate text-text-tertiary">
{t(($) => $['modelProvider.selector.configureRequired'], { ns: 'common' })}
</span>
</>
)}
{canOpenCredentialDropdown && (
<span className="i-ri-arrow-down-s-line size-3.5! shrink-0 translate-y-px text-text-tertiary" />
)}
render={(
<button type="button" className="flex max-w-[50%] min-w-0 shrink-0 cursor-pointer items-center rounded-md px-1.5 py-1 system-xs-medium text-text-tertiary hover:bg-components-button-ghost-bg-hover">
{isUsingCredits
? (
hasCredits
? (
<>
<CreditsCoin className="size-3" />
<span className="ml-1 truncate">{t('modelProvider.selector.aiCredits', { ns: 'common' })}</span>
</>
)
: (
<>
<span className="i-ri-alert-fill size-3 shrink-0 text-text-warning-secondary" />
<span className="ml-1 truncate text-text-warning">{t('modelProvider.selector.creditsExhausted', { ns: 'common' })}</span>
</>
)
)
: credentialName
? (
<>
<StatusDot size="small" status={isApiKeyActive ? 'success' : 'error'} />
<span className="ml-1 truncate text-text-tertiary">{credentialName}</span>
</>
)
: (
<>
<StatusDot size="small" status="disabled" />
<span className="ml-1 truncate text-text-tertiary">{t('modelProvider.selector.configureRequired', { ns: 'common' })}</span>
</>
)}
{canOpenCredentialDropdown && <span className="i-ri-arrow-down-s-line size-3.5! shrink-0 translate-y-px text-text-tertiary" />}
</button>
}
)}
/>
{currentProvider && (
<PopoverContent placement="bottom-end">
<DropdownContent
provider={currentProvider}
state={credentialPanelState}
isChangingPriority={isChangingPriority}
onChangePriority={handleChangePriority}
onClose={handleCloseDropdown}
/>
</PopoverContent>
)}
<PopoverContent placement="bottom-end">
<DropdownContent
provider={currentProvider}
state={state}
isChangingPriority={isChangingPriority}
onChangePriority={handleChangePriority}
onClose={handleCloseDropdown}
/>
</PopoverContent>
</Popover>
</div>
{!collapsed &&
model.models.map((modelItem) => {
const isModelCompatible = modelPredicate?.(model, modelItem) ?? true
const isModelSuggested = modelSuggestionPredicate?.(model, modelItem) ?? false
const rowClassName = cn(
'group relative mx-1 flex h-8 min-w-0 items-center gap-1 rounded-lg px-3 py-1.5 text-left',
modelItem.status === ModelStatusEnum.active
? 'cursor-pointer hover:bg-state-base-hover'
: 'cursor-not-allowed hover:bg-state-base-hover-alt',
)
const rowContent = (
<>
<div className="flex min-w-0 items-center gap-2">
<ModelIcon
className={cn('size-5 shrink-0')}
provider={model}
modelName={modelItem.model}
/>
<ModelName
className={cn(
'system-sm-medium text-text-secondary',
!isModelCompatible && 'text-text-quaternary',
modelItem.status !== ModelStatusEnum.active && 'opacity-60',
)}
modelItem={modelItem}
nameClassName={modelItem.deprecated ? 'line-through' : undefined}
>
{isModelSuggested && (
<Tooltip>
<TooltipTrigger
render={
<span
aria-label={suggestionTip}
className="i-ri-shield-star-line size-3.5 shrink-0 text-text-accent-secondary"
/>
}
/>
<TooltipContent placement="top">{suggestionTip}</TooltipContent>
</Tooltip>
)}
</ModelName>
</div>
{defaultModel?.model === modelItem.model &&
defaultModel.provider === model.provider && (
<ComboboxItemIndicator className="shrink-0 text-text-accent">
<span
className="i-custom-vender-line-general-check size-4"
aria-hidden="true"
/>
</ComboboxItemIndicator>
{!collapsed && model.models.map((modelItem) => {
const isModelCompatible = modelPredicate?.(model, modelItem) ?? true
const isModelSuggested = modelSuggestionPredicate?.(model, modelItem) ?? false
const rowClassName = cn(
'group relative mx-1 flex h-8 min-w-0 items-center gap-1 rounded-lg px-3 py-1.5 text-left',
modelItem.status === ModelStatusEnum.active ? 'cursor-pointer hover:bg-state-base-hover' : 'cursor-not-allowed hover:bg-state-base-hover-alt',
)
const rowContent = (
<>
<div className="flex min-w-0 items-center gap-2">
<ModelIcon
className={cn('size-5 shrink-0')}
provider={model}
modelName={modelItem.model}
/>
<ModelName
className={cn(
'system-sm-medium text-text-secondary',
!isModelCompatible && 'text-text-quaternary',
modelItem.status !== ModelStatusEnum.active && 'opacity-60',
)}
</>
)
const itemRender =
modelItem.status === ModelStatusEnum.noConfigure ? (
<div className={rowClassName} aria-disabled="true" onPointerDown={onPreviewCardClose}>
modelItem={modelItem}
nameClassName={modelItem.deprecated ? 'line-through' : undefined}
>
{isModelSuggested && (
<Tooltip>
<TooltipTrigger
render={(
<span
aria-label={suggestionTip}
className="i-ri-shield-star-line size-3.5 shrink-0 text-text-accent-secondary"
/>
)}
/>
<TooltipContent placement="top">
{suggestionTip}
</TooltipContent>
</Tooltip>
)}
</ModelName>
</div>
{
defaultModel?.model === modelItem.model && defaultModel.provider === currentProvider.provider && (
<ComboboxItemIndicator className="shrink-0 text-text-accent">
<span className="i-custom-vender-line-general-check size-4" aria-hidden="true" />
</ComboboxItemIndicator>
)
}
</>
)
const itemRender = modelItem.status === ModelStatusEnum.noConfigure
? (
<div
className={rowClassName}
aria-disabled="true"
onPointerDown={onPreviewCardClose}
>
{rowContent}
{canCreateCredential && (
<button
@ -246,7 +230,8 @@ function PopupItem({
</button>
)}
</div>
) : (
)
: (
<ComboboxItem
value={{
provider: model.provider,
@ -260,17 +245,17 @@ function PopupItem({
</ComboboxItem>
)
return (
<PreviewCardTrigger
key={modelItem.model}
delay={150}
closeDelay={150}
handle={previewCardHandle}
payload={{ provider: model, modelItem }}
render={itemRender}
/>
)
})}
return (
<PreviewCardTrigger
key={modelItem.model}
delay={150}
closeDelay={150}
handle={previewCardHandle}
payload={{ provider: model, modelItem }}
render={itemRender}
/>
)
})}
</ComboboxGroup>
)
}

View File

@ -3,19 +3,12 @@ import type { ModelSelectorPreviewPayload } from './popup-item'
import type { ModelSelectorModelPredicate } from './types'
import type { ModelProviderQuotaGetPaid } from '@/types/model-provider'
import { ComboboxList } from '@langgenius/dify-ui/combobox'
import {
createPreviewCardHandle,
PreviewCard,
PreviewCardContent,
} from '@langgenius/dify-ui/preview-card'
import { createPreviewCardHandle, PreviewCard, PreviewCardContent } from '@langgenius/dify-ui/preview-card'
import { useQuery, useSuspenseQuery } from '@tanstack/react-query'
import { useTheme } from 'next-themes'
import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
ACCOUNT_SETTING_MODAL_ACTION,
ACCOUNT_SETTING_TAB,
} from '@/app/components/header/account-setting/constants'
import { ACCOUNT_SETTING_MODAL_ACTION, ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
import { useIntegrationsSetting } from '@/app/components/header/account-setting/use-integrations-setting'
import checkTaskStatus from '@/app/components/plugins/install-plugin/base/check-task-status'
import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list'
@ -26,37 +19,20 @@ import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { useSearchParams } from '@/next/navigation'
import { consoleQuery } from '@/service/client'
import { useInstallPackageFromMarketPlace } from '@/service/use-plugins'
import {
CustomConfigurationStatusEnum,
ModelFeatureEnum,
ModelStatusEnum,
ModelTypeEnum,
} from '../declarations'
import { CustomConfigurationStatusEnum, ModelFeatureEnum, ModelStatusEnum, ModelTypeEnum } from '../declarations'
import { useLanguage, useMarketplaceAllPlugins } from '../hooks'
import ModelBadge from '../model-badge'
import ModelIcon from '../model-icon'
import CreditsExhaustedAlert from '../provider-added-card/model-auth-dropdown/credits-exhausted-alert'
import { useTrialCredits } from '../provider-added-card/use-trial-credits'
import { providerSupportsCredits } from '../supports-credits'
import {
MODEL_PROVIDER_QUOTA_GET_PAID,
modelTypeFormat,
providerKeyToPluginId,
sizeFormat,
} from '../utils'
import { MODEL_PROVIDER_QUOTA_GET_PAID, modelTypeFormat, providerKeyToPluginId, sizeFormat } from '../utils'
import FeatureIcon from './feature-icon'
import MarketplaceSection from './marketplace-section'
import { createModelSelectorSearchIndex, filterModelSelectorModels } from './model-search'
import ModelSelectorEmptyState from './popup-empty-state'
import PopupItem from './popup-item'
import {
CompatibleModelsNotice,
ModelProviderSettingsFooter,
ModelSelectorPopupFrame,
ModelSelectorScrollBody,
ModelSelectorSearchHeader,
ShowIncompatibleModelsButton,
} from './popup-layout'
import { CompatibleModelsNotice, ModelProviderSettingsFooter, ModelSelectorPopupFrame, ModelSelectorScrollBody, ModelSelectorSearchHeader, ShowIncompatibleModelsButton } from './popup-layout'
export type PopupProps = {
defaultModel?: DefaultModel
@ -70,7 +46,6 @@ export type PopupProps = {
onConfigureEmptyState?: () => void
onInputValueChange: (value: string) => void
onOpenMarketplace?: () => void
onOpenProviderSettings?: () => void
onHide: () => void
}
function Popup({
@ -85,132 +60,104 @@ function Popup({
onConfigureEmptyState,
onInputValueChange,
onOpenMarketplace,
onOpenProviderSettings,
onHide,
}: PopupProps) {
const { t } = useTranslation()
const searchParams = useSearchParams()
const { theme } = useTheme()
const language = useLanguage()
const previewCardHandle = useMemo(
() => createPreviewCardHandle<ModelSelectorPreviewPayload>(),
[],
)
const previewCardHandle = useMemo(() => createPreviewCardHandle<ModelSelectorPreviewPayload>(), [])
const [marketplaceCollapsed, setMarketplaceCollapsed] = useState(false)
const [showIncompatibleModels, setShowIncompatibleModels] = useState(false)
const openIntegrationsSetting = useIntegrationsSetting()
const { modelProviders } = useProviderContext()
const { data: enableMarketplace } = useSuspenseQuery({
...systemFeaturesQueryOptions(),
select: (systemFeatures) => systemFeatures.enable_marketplace,
select: systemFeatures => systemFeatures.enable_marketplace,
})
const { plugins: allPlugins, isLoading: isMarketplacePluginsLoading } = useMarketplaceAllPlugins(
modelProviders,
'',
enableMarketplace,
)
const {
plugins: allPlugins,
isLoading: isMarketplacePluginsLoading,
} = useMarketplaceAllPlugins(modelProviders, '', enableMarketplace)
const { mutateAsync: installPackageFromMarketPlace } = useInstallPackageFromMarketPlace()
const { refreshPluginList } = useRefreshPluginList()
const { canInstallPlugin } = useWorkspacePluginInstallPermission()
const [installingProvider, setInstallingProvider] = useState<ModelProviderQuotaGetPaid | null>(
null,
)
const [installingProvider, setInstallingProvider] = useState<ModelProviderQuotaGetPaid | null>(null)
const { isExhausted: isCreditsExhausted } = useTrialCredits()
const { data: trialModels = [] } = useQuery(
consoleQuery.trialModels.get.queryOptions({
enabled: IS_CLOUD_EDITION,
select: (data) => data.trial_models,
}),
)
const installedProviderMap = useMemo(
() => new Map(modelProviders.map((provider) => [provider.provider, provider])),
[modelProviders],
)
const { data: trialModels = [] } = useQuery(consoleQuery.trialModels.get.queryOptions({
enabled: IS_CLOUD_EDITION,
select: data => data.trial_models,
}))
const installedProviderMap = useMemo(() => new Map(
modelProviders.map(provider => [provider.provider, provider]),
), [modelProviders])
const aiCreditVisibleProviders = useMemo(() => {
if (!enableMarketplace || isCreditsExhausted) return new Set<string>()
if (!enableMarketplace || isCreditsExhausted)
return new Set<string>()
return new Set(
modelProviders
.filter((provider) => providerSupportsCredits(provider, trialModels))
.map((provider) => provider.provider),
.filter(provider => providerSupportsCredits(provider, trialModels))
.map(provider => provider.provider),
)
}, [enableMarketplace, isCreditsExhausted, modelProviders, trialModels])
const showCreditsExhaustedAlert =
enableMarketplace &&
isCreditsExhausted &&
modelProviders.some((provider) => providerSupportsCredits(provider, trialModels))
const showCreditsExhaustedAlert = enableMarketplace
&& isCreditsExhausted
&& modelProviders.some(provider => providerSupportsCredits(provider, trialModels))
const hasApiKeyFallback = modelProviders.some((provider) => {
const isApiKeyActive =
provider.custom_configuration?.status === CustomConfigurationStatusEnum.active
const isApiKeyActive = provider.custom_configuration?.status === CustomConfigurationStatusEnum.active
return isApiKeyActive && providerSupportsCredits(provider, trialModels)
})
const handleInstallPlugin = useCallback(
async (key: ModelProviderQuotaGetPaid) => {
if (
!enableMarketplace ||
!canInstallPlugin ||
!allPlugins ||
isMarketplacePluginsLoading ||
installingProvider
)
return
const pluginId = providerKeyToPluginId[key]
const plugin = allPlugins.find((p) => p.plugin_id === pluginId)
if (!plugin) return
const handleInstallPlugin = useCallback(async (key: ModelProviderQuotaGetPaid) => {
if (!enableMarketplace || !canInstallPlugin || !allPlugins || isMarketplacePluginsLoading || installingProvider)
return
const pluginId = providerKeyToPluginId[key]
const plugin = allPlugins.find(p => p.plugin_id === pluginId)
if (!plugin)
return
const uniqueIdentifier = plugin.latest_package_identifier
setInstallingProvider(key)
try {
const { all_installed, task_id } = await installPackageFromMarketPlace(uniqueIdentifier)
if (!all_installed) {
const { check } = checkTaskStatus()
await check({ taskId: task_id, pluginUniqueIdentifier: uniqueIdentifier })
}
refreshPluginList(plugin)
} catch {
} finally {
setInstallingProvider(null)
const uniqueIdentifier = plugin.latest_package_identifier
setInstallingProvider(key)
try {
const { all_installed, task_id } = await installPackageFromMarketPlace(uniqueIdentifier)
if (!all_installed) {
const { check } = checkTaskStatus()
await check({ taskId: task_id, pluginUniqueIdentifier: uniqueIdentifier })
}
},
[
allPlugins,
enableMarketplace,
canInstallPlugin,
installPackageFromMarketPlace,
installingProvider,
isMarketplacePluginsLoading,
refreshPluginList,
],
)
refreshPluginList(plugin)
}
catch { }
finally {
setInstallingProvider(null)
}
}, [allPlugins, enableMarketplace, canInstallPlugin, installPackageFromMarketPlace, installingProvider, isMarketplacePluginsLoading, refreshPluginList])
const installedModelList = useMemo(() => {
const modelMap = new Map(modelList.map((model) => [model.provider, model]))
const modelMap = new Map(modelList.map(model => [model.provider, model]))
const installedMarketplaceModels = MODEL_PROVIDER_QUOTA_GET_PAID.flatMap((providerKey) => {
const installedProvider = installedProviderMap.get(providerKey)
if (!installedProvider) return []
if (!installedProvider)
return []
const matchedModel = modelMap.get(providerKey)
if (matchedModel) return [matchedModel]
if (matchedModel)
return [matchedModel]
if (!aiCreditVisibleProviders.has(providerKey)) return []
if (!aiCreditVisibleProviders.has(providerKey))
return []
return [
{
provider: installedProvider.provider,
icon_small: installedProvider.icon_small,
icon_small_dark: installedProvider.icon_small_dark,
label: installedProvider.label,
models: [],
status: ModelStatusEnum.active,
},
]
return [{
provider: installedProvider.provider,
icon_small: installedProvider.icon_small,
icon_small_dark: installedProvider.icon_small_dark,
label: installedProvider.label,
models: [],
status: ModelStatusEnum.active,
}]
})
const otherModels = modelList.filter(
(model) =>
!MODEL_PROVIDER_QUOTA_GET_PAID.includes(model.provider as ModelProviderQuotaGetPaid),
)
const otherModels = modelList.filter(model => !MODEL_PROVIDER_QUOTA_GET_PAID.includes(model.provider as ModelProviderQuotaGetPaid))
return [...installedMarketplaceModels, ...otherModels]
}, [aiCreditVisibleProviders, installedProviderMap, modelList])
@ -219,96 +166,82 @@ function Popup({
() => createModelSelectorSearchIndex(installedModelList, language),
[installedModelList, language],
)
const filteredModelList = useMemo(
() =>
filterModelSelectorModels({
aiCreditVisibleProviders,
defaultModel,
inputValue,
installedModelList,
modelPredicate: showIncompatibleModels ? undefined : modelPredicate,
scopeFeatures,
searchIndex,
}),
[
aiCreditVisibleProviders,
defaultModel,
inputValue,
installedModelList,
modelPredicate,
scopeFeatures,
searchIndex,
showIncompatibleModels,
],
)
const filteredModelList = useMemo(() => filterModelSelectorModels({
aiCreditVisibleProviders,
defaultModel,
inputValue,
installedModelList,
modelPredicate: showIncompatibleModels ? undefined : modelPredicate,
scopeFeatures,
searchIndex,
}), [aiCreditVisibleProviders, defaultModel, inputValue, installedModelList, modelPredicate, scopeFeatures, searchIndex, showIncompatibleModels])
const shouldShowModelPredicateReveal = !!modelPredicate
const marketplaceProviders = useMemo(() => {
if (!enableMarketplace) return []
if (!enableMarketplace)
return []
const installedProviders = new Set(modelProviders.map((provider) => provider.provider))
return MODEL_PROVIDER_QUOTA_GET_PAID.filter((key) => !installedProviders.has(key))
const installedProviders = new Set(modelProviders.map(provider => provider.provider))
return MODEL_PROVIDER_QUOTA_GET_PAID.filter(key => !installedProviders.has(key))
}, [enableMarketplace, modelProviders])
const handleOpenSettings = useCallback(() => {
onHide()
if (onOpenProviderSettings) {
onOpenProviderSettings()
return
}
openIntegrationsSetting({
payload: ACCOUNT_SETTING_TAB.PROVIDER,
source: providerSettingsSource,
})
}, [onHide, onOpenProviderSettings, openIntegrationsSetting, providerSettingsSource])
openIntegrationsSetting({ payload: ACCOUNT_SETTING_TAB.PROVIDER, source: providerSettingsSource })
}, [onHide, openIntegrationsSetting, providerSettingsSource])
const handleClosePreviewCard = useCallback(() => {
previewCardHandle.close()
}, [previewCardHandle])
const isProviderSettingsCurrentPage =
searchParams?.get('action') === ACCOUNT_SETTING_MODAL_ACTION &&
searchParams?.get('tab') === ACCOUNT_SETTING_TAB.PROVIDER
const handleConfigureEmptyState =
onConfigureEmptyState ?? (isProviderSettingsCurrentPage ? onHide : handleOpenSettings)
const isProviderSettingsCurrentPage = searchParams?.get('action') === ACCOUNT_SETTING_MODAL_ACTION
&& searchParams?.get('tab') === ACCOUNT_SETTING_TAB.PROVIDER
const handleConfigureEmptyState = onConfigureEmptyState ?? (isProviderSettingsCurrentPage ? onHide : handleOpenSettings)
return (
<ModelSelectorPopupFrame>
<ModelSelectorSearchHeader inputValue={inputValue} onInputValueChange={onInputValueChange} />
{showCreditsExhaustedAlert && <CreditsExhaustedAlert hasApiKeyFallback={hasApiKeyFallback} />}
<ModelSelectorSearchHeader
inputValue={inputValue}
onInputValueChange={onInputValueChange}
/>
{showCreditsExhaustedAlert && (
<CreditsExhaustedAlert hasApiKeyFallback={hasApiKeyFallback} />
)}
<ModelSelectorScrollBody label={t('modelProvider.models', { ns: 'common' })}>
<ComboboxList className="max-h-none overflow-visible p-0">
<div className="pb-1">
{filteredModelList.map((model) => (
<PopupItem
key={model.provider}
defaultModel={defaultModel}
model={model}
modelPredicate={modelPredicate}
modelSuggestionPredicate={modelSuggestionPredicate}
previewCardHandle={previewCardHandle}
onPreviewCardClose={handleClosePreviewCard}
onHide={onHide}
/>
))}
{
filteredModelList.map(model => (
<PopupItem
key={model.provider}
defaultModel={defaultModel}
model={model}
modelPredicate={modelPredicate}
modelSuggestionPredicate={modelSuggestionPredicate}
previewCardHandle={previewCardHandle}
onPreviewCardClose={handleClosePreviewCard}
onHide={onHide}
/>
))
}
</div>
</ComboboxList>
<div className="pb-1">
{!filteredModelList.length && !installedModelList.length && (
<ModelSelectorEmptyState onConfigure={handleConfigureEmptyState} />
<ModelSelectorEmptyState
onConfigure={handleConfigureEmptyState}
/>
)}
{!filteredModelList.length && installedModelList.length > 0 && (
<div className="px-3 py-1.5 text-center text-xs/4.5 break-all text-text-tertiary">
{t('modelProvider.selector.noModelFoundForSearch', {
ns: 'common',
query: inputValue,
})}
{t('modelProvider.selector.noModelFoundForSearch', { ns: 'common', query: inputValue })}
</div>
)}
{scopeFeatures.length > 0 && <CompatibleModelsNotice />}
{scopeFeatures.length > 0 && (
<CompatibleModelsNotice />
)}
{shouldShowModelPredicateReveal && (
<ShowIncompatibleModelsButton
showIncompatibleModels={showIncompatibleModels}
onClick={() => setShowIncompatibleModels((value) => !value)}
onClick={() => setShowIncompatibleModels(value => !value)}
/>
)}
{enableMarketplace && (
@ -353,7 +286,8 @@ function ModelSelectorPreviewCard({
language,
payload,
}: ModelSelectorPreviewCardProps) {
if (!payload) return null
if (!payload)
return null
const { provider, modelItem } = payload
@ -364,14 +298,18 @@ function ModelSelectorPreviewCard({
>
<div className="flex flex-col gap-1">
<div className="flex flex-col items-start gap-2">
<ModelIcon className="size-5 shrink-0" provider={provider} modelName={modelItem.model} />
<div className="system-md-medium text-wrap wrap-break-word text-text-primary">
{modelItem.label[language] || modelItem.label.en_US}
</div>
<ModelIcon
className="size-5 shrink-0"
provider={provider}
modelName={modelItem.model}
/>
<div className="system-md-medium text-wrap wrap-break-word text-text-primary">{modelItem.label[language] || modelItem.label.en_US}</div>
</div>
<div className="flex flex-wrap gap-1">
{!!modelItem.model_type && (
<ModelBadge>{modelTypeFormat(modelItem.model_type)}</ModelBadge>
<ModelBadge>
{modelTypeFormat(modelItem.model_type)}
</ModelBadge>
)}
{!!modelItem.model_properties.mode && (
<ModelBadge>
@ -379,27 +317,23 @@ function ModelSelectorPreviewCard({
</ModelBadge>
)}
{!!modelItem.model_properties.context_size && (
<ModelBadge>{sizeFormat(modelItem.model_properties.context_size as number)}</ModelBadge>
<ModelBadge>
{sizeFormat(modelItem.model_properties.context_size as number)}
</ModelBadge>
)}
</div>
{[ModelTypeEnum.textGeneration, ModelTypeEnum.textEmbedding, ModelTypeEnum.rerank].includes(
modelItem.model_type as ModelTypeEnum,
) &&
modelItem.features?.some((feature) =>
[
ModelFeatureEnum.vision,
ModelFeatureEnum.audio,
ModelFeatureEnum.video,
ModelFeatureEnum.document,
].includes(feature),
) && (
{[ModelTypeEnum.textGeneration, ModelTypeEnum.textEmbedding, ModelTypeEnum.rerank].includes(modelItem.model_type as ModelTypeEnum)
&& modelItem.features?.some(feature => [ModelFeatureEnum.vision, ModelFeatureEnum.audio, ModelFeatureEnum.video, ModelFeatureEnum.document].includes(feature))
&& (
<div className="pt-2">
<div className="mb-1 system-2xs-medium-uppercase text-text-tertiary">
{capabilitiesLabel}
</div>
<div className="mb-1 system-2xs-medium-uppercase text-text-tertiary">{capabilitiesLabel}</div>
<div className="flex flex-wrap gap-1">
{modelItem.features?.map((feature) => (
<FeatureIcon key={feature} feature={feature} showFeaturesLabel />
{modelItem.features?.map(feature => (
<FeatureIcon
key={feature}
feature={feature}
showFeaturesLabel
/>
))}
</div>
</div>

View File

@ -14,7 +14,10 @@ export type MainNavRouteConfig = {
activeIcon: string
visibility: MainNavRouteVisibility
feature?: 'agentV2' | 'marketplace'
} & ({ label: string; labelKey?: never } | { label?: never; labelKey: string })
} & (
| { label: string, labelKey?: never }
| { label?: never, labelKey: string }
)
export type MainNavRouteVisibilityOptions = {
agentV2Enabled: boolean
@ -46,10 +49,7 @@ export const MAIN_NAV_ROUTES = [
key: 'apps',
href: '/apps',
labelKey: 'menus.apps',
active: (path: string) =>
isPathUnderRoute(path, '/apps') ||
isPathUnderRoute(path, '/app') ||
isPathUnderRoute(path, '/snippets'),
active: (path: string) => isPathUnderRoute(path, '/apps') || isPathUnderRoute(path, '/app') || isPathUnderRoute(path, '/snippets'),
icon: 'i-custom-vender-main-nav-studio',
activeIcon: 'i-custom-vender-main-nav-studio-active',
visibility: 'all',
@ -64,16 +64,6 @@ 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',
@ -87,8 +77,7 @@ export const MAIN_NAV_ROUTES = [
key: 'integrations',
href: buildIntegrationPath('provider'),
labelKey: 'mainNav.integrations',
active: (path: string) =>
isPathUnderRoute(path, '/integrations') || isPathUnderRoute(path, '/tools'),
active: (path: string) => isPathUnderRoute(path, '/integrations') || isPathUnderRoute(path, '/tools'),
icon: 'i-custom-vender-main-nav-integrations',
activeIcon: 'i-custom-vender-main-nav-integrations-active',
visibility: 'all',
@ -97,8 +86,7 @@ export const MAIN_NAV_ROUTES = [
key: 'marketplace',
href: '/marketplace',
labelKey: 'mainNav.marketplace',
active: (path: string) =>
isPathUnderRoute(path, '/marketplace') || isPathUnderRoute(path, '/plugins'),
active: (path: string) => isPathUnderRoute(path, '/marketplace') || isPathUnderRoute(path, '/plugins'),
icon: 'i-custom-vender-main-nav-marketplace',
activeIcon: 'i-custom-vender-main-nav-marketplace-active',
visibility: 'all',
@ -115,17 +103,18 @@ export const MAIN_NAV_ROUTES = [
},
] as const satisfies readonly MainNavRouteConfig[]
export function isMainNavRouteVisible(
route: MainNavRouteConfig,
options: MainNavRouteVisibilityOptions,
) {
if (route.feature === 'agentV2' && !options.agentV2Enabled) return false
export function isMainNavRouteVisible(route: MainNavRouteConfig, options: MainNavRouteVisibilityOptions) {
if (route.feature === 'agentV2' && !options.agentV2Enabled)
return false
if (route.feature === 'marketplace' && !options.marketplaceEnabled) return false
if (route.feature === 'marketplace' && !options.marketplaceEnabled)
return false
if (route.visibility === 'all') return true
if (route.visibility === 'all')
return true
if (route.visibility === 'notDatasetOperator') return !options.isCurrentWorkspaceDatasetOperator
if (route.visibility === 'notDatasetOperator')
return !options.isCurrentWorkspaceDatasetOperator
return options.canUseAppDeploy
}
@ -137,9 +126,11 @@ function isAppDetailPathname(pathname: string) {
function isDatasetDetailPathname(pathname: string) {
const [section, datasetId, subSection, action] = pathname.split('/').filter(Boolean)
if (section !== 'datasets' || !datasetId) return false
if (section !== 'datasets' || !datasetId)
return false
if (DATASET_COLLECTION_ROUTES.has(datasetId)) return false
if (DATASET_COLLECTION_ROUTES.has(datasetId))
return false
if (subSection === 'documents' && action && DATASET_DOCUMENT_CREATION_ROUTES.has(action))
return false
@ -156,9 +147,7 @@ function isAgentDetailPathname(pathname: string) {
function isDeploymentDetailPathname(pathname: string) {
const [section, appInstanceId] = pathname.split('/').filter(Boolean)
return (
section === 'deployments' && !!appInstanceId && !DEPLOYMENT_COLLECTION_ROUTES.has(appInstanceId)
)
return section === 'deployments' && !!appInstanceId && !DEPLOYMENT_COLLECTION_ROUTES.has(appInstanceId)
}
function isSnippetDetailPathname(pathname: string) {
@ -168,13 +157,17 @@ function isSnippetDetailPathname(pathname: string) {
}
export function shouldUseDetailSidebar(pathname: string, options: DetailSidebarVisibilityOptions) {
if (isDatasetDetailPathname(pathname) || isSnippetDetailPathname(pathname)) return true
if (isDatasetDetailPathname(pathname) || isSnippetDetailPathname(pathname))
return true
if (options.isCurrentWorkspaceDatasetOperator) return false
if (options.isCurrentWorkspaceDatasetOperator)
return false
if (isAppDetailPathname(pathname)) return true
if (isAppDetailPathname(pathname))
return true
if (options.agentV2Enabled && isAgentDetailPathname(pathname)) return true
if (options.agentV2Enabled && isAgentDetailPathname(pathname))
return true
return options.canUseAppDeploy && isDeploymentDetailPathname(pathname)
}

View File

@ -22,14 +22,10 @@ vi.mock('@/hooks/use-timestamp', () => ({
}))
vi.mock('@/app/components/workflow/nodes/_base/components/editor/code-editor', () => ({
default: ({ value }: { value?: string }) => (
<pre data-testid="conversation-code-editor">{value}</pre>
),
default: ({ value }: { value?: string }) => <pre data-testid="conversation-code-editor">{value}</pre>,
}))
const mockFetchCurrentValueOfConversationVariable = vi.mocked(
fetchCurrentValueOfConversationVariable,
)
const mockFetchCurrentValueOfConversationVariable = vi.mocked(fetchCurrentValueOfConversationVariable)
const mockCopy = vi.mocked(copy)
const createConversationVariable = (
@ -44,9 +40,7 @@ const createConversationVariable = (
})
const createConversationVariableResponse = (
data: Array<
Awaited<ReturnType<typeof fetchCurrentValueOfConversationVariable>>['data'][number]
> = [],
data: Array<Awaited<ReturnType<typeof fetchCurrentValueOfConversationVariable>>['data'][number]> = [],
): Awaited<ReturnType<typeof fetchCurrentValueOfConversationVariable>> => ({
data,
has_more: false,
@ -58,9 +52,7 @@ const createConversationVariableResponse = (
describe('ConversationVariableModal', () => {
beforeEach(() => {
vi.clearAllMocks()
mockFetchCurrentValueOfConversationVariable.mockResolvedValue(
createConversationVariableResponse(),
)
mockFetchCurrentValueOfConversationVariable.mockResolvedValue(createConversationVariableResponse())
})
afterEach(() => {
@ -70,31 +62,32 @@ describe('ConversationVariableModal', () => {
it('loads latest values, switches the active variable, and closes the modal', async () => {
const user = userEvent.setup()
const onHide = vi.fn()
mockFetchCurrentValueOfConversationVariable.mockResolvedValue(
createConversationVariableResponse([
{
...createConversationVariable({
id: 'var-1',
value: '{"latest":1}',
}),
updated_at: 100,
created_at: 50,
},
{
...createConversationVariable({
id: 'var-2',
name: 'summary',
value_type: ChatVarType.String,
value: 'latest text',
}),
updated_at: 200,
created_at: 150,
},
]),
)
mockFetchCurrentValueOfConversationVariable.mockResolvedValue(createConversationVariableResponse([
{
...createConversationVariable({
id: 'var-1',
value: '{"latest":1}',
}),
updated_at: 100,
created_at: 50,
},
{
...createConversationVariable({
id: 'var-2',
name: 'summary',
value_type: ChatVarType.String,
value: 'latest text',
}),
updated_at: 200,
created_at: 150,
},
]))
renderWorkflowComponent(
<ConversationVariableModal conversationID="conversation-1" onHide={onHide} />,
<ConversationVariableModal
conversationID="conversation-1"
onHide={onHide}
/>,
{
initialStoreState: {
appId: 'app-1',
@ -119,10 +112,8 @@ describe('ConversationVariableModal', () => {
})
expect(screen.getAllByText('session_state')).toHaveLength(2)
expect(
await screen.findByText((content) => content.includes('formatted-100')),
).toBeInTheDocument()
expect(await screen.findByTestId('conversation-code-editor')).toHaveTextContent('{"latest":1}')
expect(screen.getByText(content => content.includes('formatted-100'))).toBeInTheDocument()
expect(screen.getByTestId('conversation-code-editor')).toHaveTextContent('{"latest":1}')
await user.click(screen.getByText('summary'))
expect(screen.getByText('latest text')).toBeInTheDocument()
@ -136,7 +127,10 @@ describe('ConversationVariableModal', () => {
vi.useFakeTimers()
renderWorkflowComponent(
<ConversationVariableModal conversationID="conversation-1" onHide={vi.fn()} />,
<ConversationVariableModal
conversationID="conversation-1"
onHide={vi.fn()}
/>,
{
initialStoreState: {
appId: 'app-1',
@ -145,9 +139,7 @@ describe('ConversationVariableModal', () => {
},
)
const copyTrigger = document.querySelector(
'.flex.items-center.p-1 svg.cursor-pointer',
) as HTMLElement
const copyTrigger = document.querySelector('.flex.items-center.p-1 svg.cursor-pointer') as HTMLElement
act(() => {
fireEvent.click(copyTrigger)

View File

@ -1,4 +1,3 @@
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'
@ -40,16 +39,7 @@ 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 ?? [],
})),
deleteSkillMutationFn: vi.fn(async (_input: unknown) => ({ removed_names: ['Tender Analyzer'], result: 'success' })),
uploadSkillMutationFn: vi.fn(async (_input: unknown) => ({
config_version: { id: 'draft-1', kind: 'draft', writable: true },
skill: {
@ -66,8 +56,6 @@ 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(),
}))
@ -168,65 +156,18 @@ 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 })
return <pre data-testid="config-snapshot-probe">{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,
}
return (
<pre data-testid="config-snapshot-probe">
{JSON.stringify(configSnapshot)}
</pre>
)
}
function renderAgentSkills({
@ -272,22 +213,6 @@ 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 () => ({
@ -342,38 +267,6 @@ 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 delete a configured skill by config name', async () => {
@ -405,7 +298,7 @@ describe('AgentSkills', () => {
const user = userEvent.setup()
renderAgentSkills({ initialDraft: defaultAgentSoulConfigFormState })
await openUploadSkillDialog(user)
await user.click(screen.getByRole('button', { name: /agentV2\.agentDetail\.configure\.skills\.add/i }))
const input = await waitFor(() => {
const element = document.querySelector('input[type="file"]')
@ -414,9 +307,7 @@ describe('AgentSkills', () => {
})
const file = new File(['skill'], 'invoice-helper.skill', { type: 'application/zip' })
await user.upload(input, file)
await user.click(
screen.getByRole('button', { name: /agentDetail\.configure\.skills\.upload\.action/i }),
)
await user.click(screen.getByRole('button', { name: /agentDetail\.configure\.skills\.upload\.action/i }))
await waitFor(() => {
expect(mocks.uploadSkillMutationFn).toHaveBeenCalled()
@ -449,426 +340,12 @@ describe('AgentSkills', () => {
expect(toast.success).toHaveBeenCalled()
})
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'),
).not.toBeInTheDocument()
})
it('should show skill package guidance after failure and hide it when retrying', async () => {
const user = userEvent.setup()
mocks.uploadSkillMutationFn
.mockRejectedValueOnce(new Error('Backend upload error'))
.mockImplementationOnce(() => new Promise<never>(() => undefined))
renderAgentSkills({ initialDraft: defaultAgentSoulConfigFormState })
await openUploadSkillDialog(user)
const input = await waitFor(() => {
const element = document.querySelector('input[type="file"]')
expect(element).not.toBeNull()
return element as HTMLInputElement
})
await user.upload(
input,
new File(['skill'], 'invoice-helper.skill', { type: 'application/zip' }),
)
const uploadButton = screen.getByRole('button', {
name: /agentDetail\.configure\.skills\.upload\.action/i,
})
await user.click(uploadButton)
expect(
await screen.findByText('agentV2.agentDetail.configure.skills.upload.warning.files'),
).toBeInTheDocument()
expect(
screen.getByText('agentV2.agentDetail.configure.skills.upload.warning.specification'),
).toBeInTheDocument()
await user.click(uploadButton)
await waitFor(() => {
expect(
screen.queryByText('agentV2.agentDetail.configure.skills.upload.warning.files'),
).not.toBeInTheDocument()
})
})
it('should not show the frontend fallback error when skill upload fails', async () => {
const user = userEvent.setup()
mocks.uploadSkillMutationFn.mockRejectedValueOnce(new Error('Backend upload error'))
renderAgentSkills({ initialDraft: defaultAgentSoulConfigFormState })
await openUploadSkillDialog(user)
await user.click(screen.getByRole('button', { name: /agentV2\.agentDetail\.configure\.skills\.add/i }))
const input = await waitFor(() => {
const element = document.querySelector('input[type="file"]')
@ -877,17 +354,13 @@ describe('AgentSkills', () => {
})
const file = new File(['skill'], 'invoice-helper.skill', { type: 'application/zip' })
await user.upload(input, file)
await user.click(
screen.getByRole('button', { name: /agentDetail\.configure\.skills\.upload\.action/i }),
)
await user.click(screen.getByRole('button', { name: /agentDetail\.configure\.skills\.upload\.action/i }))
await waitFor(() => {
expect(mocks.uploadSkillMutationFn).toHaveBeenCalled()
})
expect(toast.error).not.toHaveBeenCalledWith(
'agentV2.agentDetail.configure.skills.upload.failed',
)
expect(toast.error).not.toHaveBeenCalledWith('agentV2.agentDetail.configure.skills.upload.failed')
})
it('should use workflow config skill endpoints with node_id for uploads and skill member queries', async () => {
@ -904,7 +377,7 @@ describe('AgentSkills', () => {
},
})
await openUploadSkillDialog(user)
await user.click(screen.getByRole('button', { name: /agentV2\.agentDetail\.configure\.skills\.add/i }))
const input = await waitFor(() => {
const element = document.querySelector('input[type="file"]')
expect(element).not.toBeNull()
@ -912,9 +385,7 @@ describe('AgentSkills', () => {
})
const file = new File(['skill'], 'invoice-helper.skill', { type: 'application/zip' })
await user.upload(input, file)
await user.click(
screen.getByRole('button', { name: /agentDetail\.configure\.skills\.upload\.action/i }),
)
await user.click(screen.getByRole('button', { name: /agentDetail\.configure\.skills\.upload\.action/i }))
await waitFor(() => {
expect(mocks.uploadSkillMutationFn.mock.calls[0]?.[0]).toEqual({
@ -935,21 +406,19 @@ describe('AgentSkills', () => {
await user.click(screen.getByText('Tender Analyzer').closest('button')!)
await waitFor(() => {
expect(mocks.inspectQueryOptions).toHaveBeenCalledWith(
expect.objectContaining({
input: expect.objectContaining({
params: {
app_id: 'app-1',
name: 'Tender Analyzer',
},
query: {
draft_type: 'draft',
node_id: 'node-1',
version_id: 'draft-1',
},
}),
expect(mocks.inspectQueryOptions).toHaveBeenCalledWith(expect.objectContaining({
input: expect.objectContaining({
params: {
app_id: 'app-1',
name: 'Tender Analyzer',
},
query: {
draft_type: 'draft',
node_id: 'node-1',
version_id: 'draft-1',
},
}),
)
}))
})
})
@ -957,27 +426,23 @@ describe('AgentSkills', () => {
const user = userEvent.setup()
renderAgentSkills()
await user.click(
screen.getByRole('button', {
name: /common\.operation\.download.*Tender Analyzer/,
}),
)
await user.click(screen.getByRole('button', {
name: /common\.operation\.download.*Tender Analyzer/,
}))
await waitFor(() => {
expect(mocks.skillDownloadQueryOptions).toHaveBeenCalledWith(
expect.objectContaining({
input: expect.objectContaining({
params: {
agent_id: 'agent-1',
name: 'Tender Analyzer',
},
query: {
draft_type: 'draft',
version_id: undefined,
},
}),
expect(mocks.skillDownloadQueryOptions).toHaveBeenCalledWith(expect.objectContaining({
input: expect.objectContaining({
params: {
agent_id: 'agent-1',
name: 'Tender Analyzer',
},
query: {
draft_type: 'draft',
version_id: undefined,
},
}),
)
}))
})
expect(mocks.downloadUrl).toHaveBeenCalledWith({
url: 'https://example.com/Tender Analyzer.skill',
@ -999,28 +464,24 @@ describe('AgentSkills', () => {
},
})
await user.click(
screen.getByRole('button', {
name: /common\.operation\.download.*Tender Analyzer/,
}),
)
await user.click(screen.getByRole('button', {
name: /common\.operation\.download.*Tender Analyzer/,
}))
await waitFor(() => {
expect(mocks.skillDownloadQueryOptions).toHaveBeenCalledWith(
expect.objectContaining({
input: expect.objectContaining({
params: {
app_id: 'app-1',
name: 'Tender Analyzer',
},
query: {
draft_type: 'draft',
node_id: 'node-1',
version_id: 'draft-1',
},
}),
expect(mocks.skillDownloadQueryOptions).toHaveBeenCalledWith(expect.objectContaining({
input: expect.objectContaining({
params: {
app_id: 'app-1',
name: 'Tender Analyzer',
},
query: {
draft_type: 'draft',
node_id: 'node-1',
version_id: 'draft-1',
},
}),
)
}))
})
})
@ -1031,35 +492,31 @@ describe('AgentSkills', () => {
await user.click(screen.getByText('Tender Analyzer').closest('button')!)
await waitFor(() => {
expect(mocks.inspectQueryOptions).toHaveBeenCalledWith(
expect.objectContaining({
input: expect.objectContaining({
params: {
agent_id: 'agent-1',
name: 'Tender Analyzer',
},
}),
expect(mocks.inspectQueryOptions).toHaveBeenCalledWith(expect.objectContaining({
input: expect.objectContaining({
params: {
agent_id: 'agent-1',
name: 'Tender Analyzer',
},
}),
)
}))
})
await user.click(screen.getByText('references').closest('button')!)
await user.click(screen.getByText('guide.md').closest('button')!)
await waitFor(() => {
expect(mocks.previewQueryOptions).toHaveBeenCalledWith(
expect.objectContaining({
input: expect.objectContaining({
params: {
agent_id: 'agent-1',
name: 'Tender Analyzer',
},
query: expect.objectContaining({
path: 'references/guide.md',
}),
expect(mocks.previewQueryOptions).toHaveBeenCalledWith(expect.objectContaining({
input: expect.objectContaining({
params: {
agent_id: 'agent-1',
name: 'Tender Analyzer',
},
query: expect.objectContaining({
path: 'references/guide.md',
}),
}),
)
}))
})
})
@ -1085,26 +542,22 @@ describe('AgentSkills', () => {
await user.click(screen.getByText('Tender Analyzer').closest('button')!)
await user.click(await screen.findByText('references'))
await user.click(screen.getByText('guide.md').closest('button')!)
await user.click(
screen.getByRole('button', {
name: /common\.operation\.download.*guide\.md/,
}),
)
await user.click(screen.getByRole('button', {
name: /common\.operation\.download.*guide\.md/,
}))
await waitFor(() => {
expect(mocks.downloadQueryOptions).toHaveBeenCalledWith(
expect.objectContaining({
input: expect.objectContaining({
params: {
agent_id: 'agent-1',
name: 'Tender Analyzer',
},
query: expect.objectContaining({
path: 'references/guide.md',
}),
expect(mocks.downloadQueryOptions).toHaveBeenCalledWith(expect.objectContaining({
input: expect.objectContaining({
params: {
agent_id: 'agent-1',
name: 'Tender Analyzer',
},
query: expect.objectContaining({
path: 'references/guide.md',
}),
}),
)
}))
})
expect(mocks.downloadUrl).toHaveBeenCalledWith({
url: 'https://example.com/references/guide.md',
@ -1117,11 +570,9 @@ describe('AgentSkills', () => {
renderAgentSkills()
await user.click(screen.getByText('Tender Analyzer').closest('button')!)
await user.click(
await screen.findByRole('button', {
name: /common\.operation\.download.*SKILL\.md/,
}),
)
await user.click(await screen.findByRole('button', {
name: /common\.operation\.download.*SKILL\.md/,
}))
expect(mocks.downloadBlob).toHaveBeenCalledWith({
data: expect.any(Blob),
@ -1129,23 +580,19 @@ describe('AgentSkills', () => {
})
const blob = mocks.downloadBlob.mock.calls[0]?.[0].data as Blob
await expect(blob.text()).resolves.toBe('# Skill\n')
expect(mocks.downloadQueryOptions).not.toHaveBeenCalledWith(
expect.objectContaining({
input: expect.objectContaining({
query: expect.objectContaining({
path: 'SKILL.md',
}),
expect(mocks.downloadQueryOptions).not.toHaveBeenCalledWith(expect.objectContaining({
input: expect.objectContaining({
query: expect.objectContaining({
path: 'SKILL.md',
}),
}),
)
}))
})
it('should disable add and remove actions when the section is read only', () => {
const { container } = renderAgentSkills({ readOnly: true })
expect(
screen.queryByRole('button', { name: /agentV2\.agentDetail\.configure\.skills\.add/i }),
).not.toBeInTheDocument()
expect(screen.queryByRole('button', { name: /agentV2\.agentDetail\.configure\.skills\.add/i })).not.toBeInTheDocument()
expect(container.querySelector('[data-agent-skill-remove-button]')).toBeNull()
})
})

View File

@ -1,450 +1,38 @@
'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 { 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 { useMutation } from '@tanstack/react-query'
import { useAtomValue, useSetAtom } from 'jotai'
import { useCallback, useMemo, useRef, useState } from 'react'
import { useCallback, 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 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()
const skills = useAtomValue(agentComposerSkillsAtom)
const upsertAgentSkill = useSetAtom(upsertAgentSkillAtom)
const removeAgentSkill = useSetAtom(removeAgentSkillAtom)
const { mutate: deleteAgentSkill } = useMutation(
consoleQuery.agent.byAgentId.config.skills.byName.delete.mutationOptions(),
)
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 { mutate: deleteAgentSkill } = useMutation(consoleQuery.agent.byAgentId.config.skills.byName.delete.mutationOptions())
const { mutate: deleteAppSkill } = useMutation(consoleQuery.apps.byAppId.agent.config.skills.byName.delete.mutationOptions())
const handleOpenUpload = useCallback((options?: AgentOrchestrateAddActionOptions) => {
promptAddCallbackRef.current = options?.onAdded
@ -452,101 +40,52 @@ 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)
promptAddCallbackRef.current?.(skill)
promptAddCallbackRef.current = undefined
},
[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 handleUploaded = useCallback((skill: AgentSkill) => {
upsertAgentSkill(skill)
promptAddCallbackRef.current?.(skill)
promptAddCallbackRef.current = undefined
}, [upsertAgentSkill])
const handleUploadOpenChange = useCallback((open: boolean) => {
if (!open) promptAddCallbackRef.current = undefined
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']))
const handleRemoveSkill = useCallback((skillId: string) => {
const skill = skills.find(item => item.id === skillId)
if (!skill)
return
const onSuccess = () => {
removeAgentSkill(skillId)
}
if (apiContext.workflow) {
deleteAppSkill({
params: {
app_id: apiContext.workflow.appId,
name: skill.name,
},
)
},
[boundSkillIds, replaceWorkspaceSkillBindings, t],
)
const handleRemoveSkill = useCallback(
(skillId: string) => {
const skill = skills.find((item) => item.id === skillId)
if (!skill) return
const onSuccess = () => {
removeAgentSkill(skillId)
}
if (apiContext.workflow) {
deleteAppSkill(
{
params: {
app_id: apiContext.workflow.appId,
name: skill.name,
},
query: {
node_id: apiContext.workflow.nodeId,
draft_type: apiContext.draftType,
version_id: apiContext.versionId,
},
},
{ onSuccess },
)
return
}
deleteAgentSkill(
{
params: {
agent_id: apiContext.agentId,
name: skill.name,
},
query: {
draft_type: apiContext.draftType,
version_id: apiContext.versionId,
},
query: {
node_id: apiContext.workflow.nodeId,
draft_type: apiContext.draftType,
version_id: apiContext.versionId,
},
{ onSuccess },
)
},
[apiContext, deleteAgentSkill, deleteAppSkill, removeAgentSkill, skills],
)
}, { onSuccess })
return
}
deleteAgentSkill({
params: {
agent_id: apiContext.agentId,
name: skill.name,
},
query: {
draft_type: apiContext.draftType,
version_id: apiContext.versionId,
},
}, { onSuccess })
}, [apiContext, deleteAgentSkill, deleteAppSkill, removeAgentSkill, skills])
return (
<>
@ -559,92 +98,23 @@ export function AgentSkills() {
tipAriaLabel={skillsTip}
rootClassName="border-b border-divider-subtle pt-4"
panelContentClassName="flex flex-col gap-1 pb-4"
actions={
!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>
)
}
>
{!hasSkills ? (
<ConfigureSectionEmpty
title={t(($) => $['agentDetail.configure.skills.empty.title'])}
description={t(($) => $['agentDetail.configure.skills.empty.description'])}
actions={(
<ConfigureSectionAddButton
ariaLabel={t('agentDetail.configure.skills.add')}
onClick={() => handleOpenUpload()}
/>
) : (
<>
{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}
/>
))}
</>
)}
>
{skills.length === 0
? (
<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} />
))}
</ConfigureSection>
<AgentSkillUploadDialog
apiContext={apiContext}

View File

@ -1,15 +0,0 @@
# 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

@ -1,860 +0,0 @@
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

@ -1,378 +0,0 @@
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

@ -1,180 +0,0 @@
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

@ -1,700 +0,0 @@
'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

@ -1,12 +0,0 @@
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,9 +15,8 @@ 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, keep the query stale
* so a server-side fallback does not hide API-enabled features until the shared
* query cache expires.
* 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.
*/
export const systemFeaturesQueryOptions = () => {
const queryKey = consoleQuery.systemFeatures.get.queryKey()
@ -32,11 +31,11 @@ export const systemFeaturesQueryOptions = () => {
return queryOptions<GetSystemFeaturesResponse>({
queryKey,
staleTime: 0,
queryFn: async () => {
try {
return await consoleClient.systemFeatures.get()
} catch (err) {
}
catch (err) {
console.error('[systemFeatures] fetch failed, using defaults', err)
return defaultSystemFeatures
}

View File

@ -21,13 +21,13 @@ export const serverSystemFeaturesQueryOptions = () => {
return queryOptions<GetSystemFeaturesResponse>({
queryKey,
staleTime: 0,
queryFn: async () => {
try {
return await serverConsoleClient.systemFeatures.get(undefined, {
context: await getServerConsoleClientContext(),
})
} catch (err) {
}
catch (err) {
console.error('[systemFeatures] server fetch failed', err)
return defaultSystemFeatures
}

View File

@ -9,15 +9,15 @@ 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 (branding.enabled) {
titleStr = `${prefix}${branding.application_title}`
favicon = branding.favicon
} else {
if (systemFeatures.branding.enabled) {
titleStr = `${prefix}${systemFeatures.branding.application_title}`
favicon = systemFeatures.branding.favicon
}
else {
titleStr = `${prefix}Dify`
favicon = `${basePath}/favicon.ico`
}
@ -25,22 +25,22 @@ export default function useDocumentTitle(title: string) {
useTitle(titleStr)
useEffect(() => {
let apple: HTMLLinkElement | null = null
if (branding.favicon) {
if (systemFeatures.branding.favicon) {
document
.querySelectorAll(
"link[rel='icon'], link[rel='shortcut icon'], link[rel='apple-touch-icon'], link[rel='mask-icon']",
'link[rel=\'icon\'], link[rel=\'shortcut icon\'], link[rel=\'apple-touch-icon\'], link[rel=\'mask-icon\']',
)
.forEach((n) => n.parentNode?.removeChild(n))
.forEach(n => n.parentNode?.removeChild(n))
apple = document.createElement('link')
apple.rel = 'apple-touch-icon'
apple.href = branding.favicon
apple.href = systemFeatures.branding.favicon
document.head.appendChild(apple)
}
return () => {
apple?.remove()
}
}, [branding.favicon])
}, [systemFeatures.branding.favicon])
useFavicon(favicon)
}

View File

@ -14,11 +14,10 @@
"agentDetail.access.title": "نقطة الوصول",
"agentDetail.access.toggleSurface": "تبديل وصول {{name}}",
"agentDetail.access.webApp.accessUrl": "رابط الوصول",
"agentDetail.access.webApp.actions.accessControl": "التحكم في الوصول",
"agentDetail.access.webApp.actions.customize": "واجهة أمامية مخصصة",
"agentDetail.access.webApp.actions.customize": "تخصيص",
"agentDetail.access.webApp.actions.embedded": "مضمّن",
"agentDetail.access.webApp.actions.launch": "تشغيل",
"agentDetail.access.webApp.actions.settings": "العلامة التجارية",
"agentDetail.access.webApp.actions.settings": "الإعدادات",
"agentDetail.access.webApp.refreshUrl": "تحديث رابط الوصول",
"agentDetail.access.webApp.showQrCode": "عرض رمز QR",
"agentDetail.access.webApp.ssoEnabled": "SSO ممكّن",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "الإعدادات المتقدمة",
"agentDetail.configure.advancedSettings.toggle": "تبديل الإعدادات المتقدمة",
"agentDetail.configure.answer.activity.ranCommands": "تم تشغيل الأوامر",
"agentDetail.configure.answer.activity.runningCommands": "جارٍ تشغيل الأوامر",
"agentDetail.configure.answer.duration.minute": "{{count}} د",
"agentDetail.configure.answer.duration.second": "{{count}} ث",
"agentDetail.configure.answer.thinking": "جارٍ التفكير",
"agentDetail.configure.answer.workFinished": "اكتمل العمل",
"agentDetail.configure.answer.workedFor": "عمل لمدة {{duration}}",
"agentDetail.configure.answer.workingFor": "يعمل منذ {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "ميزات الدردشة",
"agentDetail.configure.clearSessionConfirm.description": "سيؤدي هذا إلى مسح الجلسة الحالية والتخلي عن تغييرات إعدادات Agent التي لم يتم تطبيقها بعد.",
"agentDetail.configure.clearSessionConfirm.title": "مسح الجلسة والتخلي عن التغييرات؟",
"agentDetail.configure.communityEditionIsolationTip": "إخلاء مسؤولية: في Community Edition، تعمل بيئة العزل كمستخدم غير جذر ضمن إعداد الإمكانات الافتراضي لـ Docker، ولا توفر سوى حماية محدودة تعتمد على Landlock لملفات الوكيل الخاصة وملفات الجلسة. يتشارك الخادم وجميع العمليات الفرعية للصدفة مساحة أسماء PID نفسها وحدّ الإمكانات على مستوى الحاوية، لذلك لا ينبغي اعتبارها بيئة عزل أمني متعددة الطبقات ومعزولة بقوة.",
"agentDetail.configure.communityEditionIsolationTip": "لا توفر Community Edition عزلاً صارماً لنظام الملفات بين المستخدمين النهائيين أو بين عمليات التشغيل. لا تعرض وكيل CE نفسه لعدة مستخدمين نهائيين مستقلين عندما يكون عزل البيانات أو الامتثال الصارم مطلوباً.",
"agentDetail.configure.files.add": "إضافة ملف",
"agentDetail.configure.files.buildNote.generated": "تم إنشاؤه",
"agentDetail.configure.files.buildNote.richTooltip": "سجل الوكيل لما أعدّه في وضع Build. يقرأه في بداية كل محادثة إلى جانب Prompt الخاص بك. <docLink>معرفة المزيد</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "قم بتحميل المستندات التي يمكن للوكيل قراءتها، مثل المواصفات أو القوالب أو الإرشادات",
"agentDetail.configure.files.empty.title": "لا توجد ملفات بعد",
"agentDetail.configure.files.label": "الملفات",
"agentDetail.configure.files.missing": "الملف غير موجود",
"agentDetail.configure.files.preview.empty": "لا يوجد محتوى معاينة.",
"agentDetail.configure.files.preview.failed": "فشل تحميل المعاينة.",
"agentDetail.configure.files.preview.unsupported": "هذا الملف لا يدعم المعاينة.",
@ -203,22 +198,14 @@
"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": "تبديل المهارات",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "قم بتحميل ملف .zip أو .skill.",
"agentDetail.configure.skills.upload.success": "تم تحميل المهارة.",
"agentDetail.configure.skills.upload.title": "تحميل مهارة",
"agentDetail.configure.skills.upload.warning.files": "إذا كنت تحتاج فقط إلى استخدام ملفات Markdown، فحمّلها إلى قسم الملفات وأشر إليها في الموجّه الخاص بك.",
"agentDetail.configure.skills.upload.warning.specification": "يجب أن تتوافق المهارات التي يتم تحميلها مع <specificationLink>مواصفات Agent Skills</specificationLink>.",
"agentDetail.configure.title": "تكوين",
"agentDetail.configure.tools.add": "إضافة أداة",
"agentDetail.configure.tools.addMenu.cliTool.badge": "للمطورين",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "خيارات الفرز",
"roster.sort.recentlyCreated": "الأحدث إنشاءً",
"roster.updateSuccess": "تم تحديث الوكيل.",
"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"
"roster.usageStatus.draft": "مسودة"
}

View File

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

View File

@ -14,11 +14,10 @@
"agentDetail.access.title": "Zugangspunkt",
"agentDetail.access.toggleSurface": "Zugriff von {{name}} umschalten",
"agentDetail.access.webApp.accessUrl": "Zugriffs-URL",
"agentDetail.access.webApp.actions.accessControl": "Zugriffskontrolle",
"agentDetail.access.webApp.actions.customize": "Benutzerdefiniertes Frontend",
"agentDetail.access.webApp.actions.customize": "Anpassen",
"agentDetail.access.webApp.actions.embedded": "Eingebettet",
"agentDetail.access.webApp.actions.launch": "Starten",
"agentDetail.access.webApp.actions.settings": "Branding",
"agentDetail.access.webApp.actions.settings": "Einstellungen",
"agentDetail.access.webApp.refreshUrl": "Zugriffs-URL aktualisieren",
"agentDetail.access.webApp.showQrCode": "QR-Code anzeigen",
"agentDetail.access.webApp.ssoEnabled": "SSO aktiviert",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Erweiterte Einstellungen",
"agentDetail.configure.advancedSettings.toggle": "Erweiterte Einstellungen umschalten",
"agentDetail.configure.answer.activity.ranCommands": "Befehle ausgeführt",
"agentDetail.configure.answer.activity.runningCommands": "Befehle werden ausgeführt",
"agentDetail.configure.answer.duration.minute": "{{count}} Min.",
"agentDetail.configure.answer.duration.second": "{{count}} s",
"agentDetail.configure.answer.thinking": "Denkt nach",
"agentDetail.configure.answer.workFinished": "Arbeit abgeschlossen",
"agentDetail.configure.answer.workedFor": "Gearbeitet für {{duration}}",
"agentDetail.configure.answer.workingFor": "Arbeitet seit {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Chat-Funktionen",
"agentDetail.configure.clearSessionConfirm.description": "Dadurch wird die aktuelle Sitzung geleert und nicht angewendete Agent-Konfigurationsänderungen werden verworfen.",
"agentDetail.configure.clearSessionConfirm.title": "Sitzung leeren und Änderungen verwerfen?",
"agentDetail.configure.communityEditionIsolationTip": "Haftungsausschluss: In der Community Edition wird die Sandbox als Nicht-Root-Benutzer unter der standardmäßigen Capability-Konfiguration von Docker ausgeführt und bietet nur begrenzten Landlock-basierten Schutz für die eigenen Dateien des Agenten und Sitzungsdateien. Der Server und alle Shell-Unterprozesse teilen denselben PID-Namespace und dieselbe Capability-Grenze auf Containerebene. Daher sollte sie nicht als stark isolierte, mehrschichtige Sicherheits-Sandbox betrachtet werden.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition bietet keine harte Dateisystemisolierung zwischen Endbenutzern oder Ausführungen. Stellen Sie denselben CE-Agenten nicht mehreren voneinander unabhängigen Endbenutzern bereit, wenn Datenisolierung oder strenge Compliance erforderlich ist.",
"agentDetail.configure.files.add": "Datei hinzufügen",
"agentDetail.configure.files.buildNote.generated": "Generiert",
"agentDetail.configure.files.buildNote.richTooltip": "Die Aufzeichnung des Agenten darüber, was er im Build-Modus eingerichtet hat. Er liest sie zu Beginn jeder Unterhaltung zusammen mit Ihrem Prompt. <docLink>Mehr erfahren</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Laden Sie Dokumente hoch, die der Agent lesen kann, z. B. Spezifikationen, Vorlagen oder Richtlinien",
"agentDetail.configure.files.empty.title": "Noch keine Dateien",
"agentDetail.configure.files.label": "Dateien",
"agentDetail.configure.files.missing": "Datei nicht gefunden",
"agentDetail.configure.files.preview.empty": "Kein Vorschauinhalt.",
"agentDetail.configure.files.preview.failed": "Vorschau konnte nicht geladen werden.",
"agentDetail.configure.files.preview.unsupported": "Für diese Datei wird keine Vorschau unterstützt.",
@ -203,22 +198,14 @@
"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",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Bitte eine .zip- oder .skill-Datei hochladen.",
"agentDetail.configure.skills.upload.success": "Skill hochgeladen.",
"agentDetail.configure.skills.upload.title": "Skill hochladen",
"agentDetail.configure.skills.upload.warning.files": "Wenn du nur Markdown-Dateien verwenden möchtest, lade sie unter „Dateien“ hoch und verweise in deinem Prompt darauf.",
"agentDetail.configure.skills.upload.warning.specification": "Hochgeladene Skills müssen der <specificationLink>Agent-Skills-Spezifikation</specificationLink> entsprechen.",
"agentDetail.configure.title": "Konfigurieren",
"agentDetail.configure.tools.add": "Tool hinzufügen",
"agentDetail.configure.tools.addMenu.cliTool.badge": "Für Entwickler",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "Sortieroptionen",
"roster.sort.recentlyCreated": "Zuletzt erstellt",
"roster.updateSuccess": "Agent aktualisiert.",
"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"
"roster.usageStatus.draft": "Entwurf"
}

View File

@ -201,7 +201,6 @@
"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",
@ -487,7 +486,6 @@
"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

@ -14,11 +14,10 @@
"agentDetail.access.title": "Access Point",
"agentDetail.access.toggleSurface": "Toggle {{name}} access",
"agentDetail.access.webApp.accessUrl": "Access URL",
"agentDetail.access.webApp.actions.accessControl": "Access Control",
"agentDetail.access.webApp.actions.customize": "Custom Frontend",
"agentDetail.access.webApp.actions.customize": "Customize",
"agentDetail.access.webApp.actions.embedded": "Embedded",
"agentDetail.access.webApp.actions.launch": "Launch",
"agentDetail.access.webApp.actions.settings": "Branding",
"agentDetail.access.webApp.actions.settings": "Settings",
"agentDetail.access.webApp.refreshUrl": "Refresh access URL",
"agentDetail.access.webApp.showQrCode": "Show QR code",
"agentDetail.access.webApp.ssoEnabled": "SSO Enabled",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Advanced Settings",
"agentDetail.configure.advancedSettings.toggle": "Toggle advanced settings",
"agentDetail.configure.answer.activity.ranCommands": "Ran commands",
"agentDetail.configure.answer.activity.runningCommands": "Running commands",
"agentDetail.configure.answer.duration.minute": "{{count}}m",
"agentDetail.configure.answer.duration.second": "{{count}}s",
"agentDetail.configure.answer.thinking": "Thinking",
"agentDetail.configure.answer.workFinished": "Work finished",
"agentDetail.configure.answer.workedFor": "Worked for {{duration}}",
"agentDetail.configure.answer.workingFor": "Working for {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Chat Features",
"agentDetail.configure.clearSessionConfirm.description": "This will clear the current session and discard unapplied Agent configuration changes.",
"agentDetail.configure.clearSessionConfirm.title": "Clear session and discard changes?",
"agentDetail.configure.communityEditionIsolationTip": "Disclaimer: In Community Edition, sandbox runs as a non-root user under Dockers default capability configuration and only provides limited Landlock-based protection for the agents own files and session files. The server and all shell subprocesses share the same PID namespace and container-level capability boundary, so it should not be considered a strongly isolated multi-layer security sandbox.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition does not provide hard file system isolation between end users or runs. Do not expose the same CE agent to multiple independent end users where data isolation or strict compliance is required.",
"agentDetail.configure.files.add": "Add file",
"agentDetail.configure.files.buildNote.generated": "Generated",
"agentDetail.configure.files.buildNote.richTooltip": "The agent's record of what it set up in Build mode. It reads this at the start of every conversation, alongside your Prompt. <docLink>Learn more</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Upload docs the agent can read, like specs, templates, or guidelines",
"agentDetail.configure.files.empty.title": "No files yet",
"agentDetail.configure.files.label": "Files",
"agentDetail.configure.files.missing": "File not found",
"agentDetail.configure.files.preview.empty": "No preview content.",
"agentDetail.configure.files.preview.failed": "Failed to load preview.",
"agentDetail.configure.files.preview.unsupported": "Preview is not supported for this file.",
@ -203,24 +198,14 @@
"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",
@ -233,17 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Upload a .zip or .skill file.",
"agentDetail.configure.skills.upload.success": "Skill uploaded.",
"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",
@ -437,178 +411,5 @@
"roster.sort.optionsLabel": "Sort options",
"roster.sort.recentlyCreated": "Recently created",
"roster.updateSuccess": "Agent updated.",
"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"
"roster.usageStatus.draft": "Draft"
}

View File

@ -201,7 +201,6 @@
"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",
@ -487,7 +486,6 @@
"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

@ -14,11 +14,10 @@
"agentDetail.access.title": "Punto de acceso",
"agentDetail.access.toggleSurface": "Alternar acceso de {{name}}",
"agentDetail.access.webApp.accessUrl": "URL de acceso",
"agentDetail.access.webApp.actions.accessControl": "Control de Acceso",
"agentDetail.access.webApp.actions.customize": "Frontend personalizado",
"agentDetail.access.webApp.actions.customize": "Personalizar",
"agentDetail.access.webApp.actions.embedded": "Incrustado",
"agentDetail.access.webApp.actions.launch": "Iniciar",
"agentDetail.access.webApp.actions.settings": "Marca",
"agentDetail.access.webApp.actions.settings": "Configuración",
"agentDetail.access.webApp.refreshUrl": "Actualizar URL de acceso",
"agentDetail.access.webApp.showQrCode": "Mostrar código QR",
"agentDetail.access.webApp.ssoEnabled": "SSO habilitado",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Configuración avanzada",
"agentDetail.configure.advancedSettings.toggle": "Alternar configuración avanzada",
"agentDetail.configure.answer.activity.ranCommands": "Comandos ejecutados",
"agentDetail.configure.answer.activity.runningCommands": "Ejecutando comandos",
"agentDetail.configure.answer.duration.minute": "{{count}} min",
"agentDetail.configure.answer.duration.second": "{{count}} s",
"agentDetail.configure.answer.thinking": "Pensando",
"agentDetail.configure.answer.workFinished": "Trabajo finalizado",
"agentDetail.configure.answer.workedFor": "Trabajó durante {{duration}}",
"agentDetail.configure.answer.workingFor": "Trabajando durante {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Funciones de chat",
"agentDetail.configure.clearSessionConfirm.description": "Esto borrará la sesión actual y descartará los cambios de configuración del Agent que aún no se hayan aplicado.",
"agentDetail.configure.clearSessionConfirm.title": "¿Borrar la sesión y descartar los cambios?",
"agentDetail.configure.communityEditionIsolationTip": "Descargo de responsabilidad: En Community Edition, el sandbox se ejecuta como un usuario no root con la configuración de capacidades predeterminada de Docker y solo proporciona protección limitada basada en Landlock para los archivos propios del agente y los archivos de sesión. El servidor y todos los subprocesos del shell comparten el mismo espacio de nombres PID y el mismo límite de capacidades a nivel de contenedor, por lo que no debe considerarse un sandbox de seguridad multicapa con aislamiento sólido.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition no proporciona aislamiento estricto del sistema de archivos entre usuarios finales ni entre ejecuciones. No expongas el mismo agente CE a varios usuarios finales independientes cuando se requiera aislamiento de datos o cumplimiento estricto.",
"agentDetail.configure.files.add": "Agregar archivo",
"agentDetail.configure.files.buildNote.generated": "Generado",
"agentDetail.configure.files.buildNote.richTooltip": "El registro del agente sobre lo que configuró en modo Build. Lo lee al inicio de cada conversación, junto con tu Prompt. <docLink>Más información</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Sube documentos que el agente pueda leer, como especificaciones, plantillas o guías",
"agentDetail.configure.files.empty.title": "Aún no hay archivos",
"agentDetail.configure.files.label": "Archivos",
"agentDetail.configure.files.missing": "Archivo no encontrado",
"agentDetail.configure.files.preview.empty": "Sin contenido de vista previa.",
"agentDetail.configure.files.preview.failed": "Error al cargar la vista previa.",
"agentDetail.configure.files.preview.unsupported": "Este archivo no admite vista previa.",
@ -203,22 +198,14 @@
"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",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Sube un archivo .zip o .skill.",
"agentDetail.configure.skills.upload.success": "Habilidad subida.",
"agentDetail.configure.skills.upload.title": "Subir habilidad",
"agentDetail.configure.skills.upload.warning.files": "Si solo necesitas usar archivos Markdown, súbelos a Archivos y haz referencia a ellos en tu prompt.",
"agentDetail.configure.skills.upload.warning.specification": "Las habilidades subidas deben cumplir la <specificationLink>especificación de Agent Skills</specificationLink>.",
"agentDetail.configure.title": "Configurar",
"agentDetail.configure.tools.add": "Agregar herramienta",
"agentDetail.configure.tools.addMenu.cliTool.badge": "Para desarrolladores",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "Opciones de ordenación",
"roster.sort.recentlyCreated": "Creados recientemente",
"roster.updateSuccess": "Agente actualizado.",
"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"
"roster.usageStatus.draft": "Borrador"
}

View File

@ -201,7 +201,6 @@
"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",
@ -487,7 +486,6 @@
"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

@ -14,11 +14,10 @@
"agentDetail.access.title": "نقطه دسترسی",
"agentDetail.access.toggleSurface": "تغییر دسترسی {{name}}",
"agentDetail.access.webApp.accessUrl": "URL دسترسی",
"agentDetail.access.webApp.actions.accessControl": "کنترل دسترسی",
"agentDetail.access.webApp.actions.customize": "فرانت‌اند سفارشی",
"agentDetail.access.webApp.actions.customize": "سفارشی‌سازی",
"agentDetail.access.webApp.actions.embedded": "تعبیه‌شده",
"agentDetail.access.webApp.actions.launch": "راه‌اندازی",
"agentDetail.access.webApp.actions.settings": "برندسازی",
"agentDetail.access.webApp.actions.settings": "تنظیمات",
"agentDetail.access.webApp.refreshUrl": "تازه‌سازی URL دسترسی",
"agentDetail.access.webApp.showQrCode": "نمایش کد QR",
"agentDetail.access.webApp.ssoEnabled": "SSO فعال",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "تنظیمات پیشرفته",
"agentDetail.configure.advancedSettings.toggle": "تغییر تنظیمات پیشرفته",
"agentDetail.configure.answer.activity.ranCommands": "دستورات اجرا شدند",
"agentDetail.configure.answer.activity.runningCommands": "در حال اجرای دستورات",
"agentDetail.configure.answer.duration.minute": "{{count}} دقیقه",
"agentDetail.configure.answer.duration.second": "{{count}} ثانیه",
"agentDetail.configure.answer.thinking": "در حال فکر کردن",
"agentDetail.configure.answer.workFinished": "کار تمام شد",
"agentDetail.configure.answer.workedFor": "به مدت {{duration}} کار کرد",
"agentDetail.configure.answer.workingFor": "در حال کار به مدت {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "ویژگی‌های چت",
"agentDetail.configure.clearSessionConfirm.description": "این کار نشست فعلی را پاک می‌کند و تغییرات پیکربندی Agent را که هنوز اعمال نشده‌اند کنار می‌گذارد.",
"agentDetail.configure.clearSessionConfirm.title": "نشست پاک شود و تغییرات کنار گذاشته شوند؟",
"agentDetail.configure.communityEditionIsolationTip": "سلب مسئولیت: در Community Edition، محیط sandbox به‌عنوان کاربر غیر root و تحت پیکربندی پیش‌فرض قابلیت‌های Docker اجرا می‌شود و فقط محافظت محدود مبتنی بر Landlock را برای فایل‌های خود عامل و فایل‌های نشست فراهم می‌کند. سرور و همه زیرفرایندهای shell فضای نام PID و مرز قابلیت در سطح کانتینر یکسانی را به اشتراک می‌گذارند؛ بنابراین نباید آن را یک sandbox امنیتی چندلایه با جداسازی قوی در نظر گرفت.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition جداسازی سخت‌گیرانهٔ سیستم فایل را بین کاربران نهایی یا اجراها فراهم نمی‌کند. در جایی که جداسازی داده یا رعایت الزامات سخت‌گیرانه لازم است، همان عامل CE را در اختیار چند کاربر نهایی مستقل قرار ندهید.",
"agentDetail.configure.files.add": "افزودن فایل",
"agentDetail.configure.files.buildNote.generated": "تولید شده",
"agentDetail.configure.files.buildNote.richTooltip": "رکورد عامل از چیزهایی که در حالت Build تنظیم کرده است. در آغاز هر گفتگو، آن را همراه با Prompt شما می‌خواند. <docLink>بیشتر بدانید</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "اسنادی را که عامل می‌تواند بخواند بارگذاری کنید، مانند مشخصات، قالب‌ها یا دستورالعمل‌ها",
"agentDetail.configure.files.empty.title": "هنوز فایلی وجود ندارد",
"agentDetail.configure.files.label": "فایل‌ها",
"agentDetail.configure.files.missing": "فایل یافت نشد",
"agentDetail.configure.files.preview.empty": "محتوای پیش‌نمایش وجود ندارد.",
"agentDetail.configure.files.preview.failed": "بارگذاری پیش‌نمایش ناموفق بود.",
"agentDetail.configure.files.preview.unsupported": "این فایل از پیش‌نمایش پشتیبانی نمی‌کند.",
@ -203,22 +198,14 @@
"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": "تغییر مهارت‌ها",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "یک فایل .zip یا .skill بارگذاری کنید.",
"agentDetail.configure.skills.upload.success": "مهارت بارگذاری شد.",
"agentDetail.configure.skills.upload.title": "بارگذاری مهارت",
"agentDetail.configure.skills.upload.warning.files": "اگر فقط می‌خواهید از فایل‌های Markdown استفاده کنید، آن‌ها را در بخش فایل‌ها بارگذاری و در پرامپت خود ارجاع دهید.",
"agentDetail.configure.skills.upload.warning.specification": "مهارت‌های بارگذاری‌شده باید از <specificationLink>مشخصات Agent Skills</specificationLink> پیروی کنند.",
"agentDetail.configure.title": "پیکربندی",
"agentDetail.configure.tools.add": "افزودن ابزار",
"agentDetail.configure.tools.addMenu.cliTool.badge": "برای توسعه‌دهندگان",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "گزینه‌های مرتب‌سازی",
"roster.sort.recentlyCreated": "تازه‌ترین ایجاد",
"roster.updateSuccess": "عامل به‌روزرسانی شد.",
"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"
"roster.usageStatus.draft": "پیش‌نویس"
}

View File

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

View File

@ -14,11 +14,10 @@
"agentDetail.access.title": "Point daccès",
"agentDetail.access.toggleSurface": "Basculer laccès de {{name}}",
"agentDetail.access.webApp.accessUrl": "URL daccès",
"agentDetail.access.webApp.actions.accessControl": "Contrôle d'accès",
"agentDetail.access.webApp.actions.customize": "Frontend personnalisé",
"agentDetail.access.webApp.actions.customize": "Personnaliser",
"agentDetail.access.webApp.actions.embedded": "Intégré",
"agentDetail.access.webApp.actions.launch": "Lancer",
"agentDetail.access.webApp.actions.settings": "Image de marque",
"agentDetail.access.webApp.actions.settings": "Paramètres",
"agentDetail.access.webApp.refreshUrl": "Actualiser lURL daccès",
"agentDetail.access.webApp.showQrCode": "Afficher le code QR",
"agentDetail.access.webApp.ssoEnabled": "SSO activé",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Paramètres avancés",
"agentDetail.configure.advancedSettings.toggle": "Basculer les paramètres avancés",
"agentDetail.configure.answer.activity.ranCommands": "Commandes exécutées",
"agentDetail.configure.answer.activity.runningCommands": "Exécution des commandes",
"agentDetail.configure.answer.duration.minute": "{{count}} min",
"agentDetail.configure.answer.duration.second": "{{count}} s",
"agentDetail.configure.answer.thinking": "Réflexion en cours",
"agentDetail.configure.answer.workFinished": "Travail terminé",
"agentDetail.configure.answer.workedFor": "A travaillé pendant {{duration}}",
"agentDetail.configure.answer.workingFor": "Travaille depuis {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Fonctionnalités de chat",
"agentDetail.configure.clearSessionConfirm.description": "Cette action effacera la session actuelle et ignorera les modifications de configuration de lAgent non encore appliquées.",
"agentDetail.configure.clearSessionConfirm.title": "Effacer la session et ignorer les modifications ?",
"agentDetail.configure.communityEditionIsolationTip": "Avertissement : Dans Community Edition, le bac à sable sexécute sous un utilisateur non root avec la configuration de capacités par défaut de Docker et fournit uniquement une protection limitée basée sur Landlock pour les fichiers propres de lagent et les fichiers de session. Le serveur et tous les sous-processus shell partagent le même espace de noms PID et la même limite de capacités au niveau du conteneur ; il ne doit donc pas être considéré comme un bac à sable de sécurité multicouche fortement isolé.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition ne fournit pas disolation stricte du système de fichiers entre les utilisateurs finaux ni entre les exécutions. Nexposez pas le même agent CE à plusieurs utilisateurs finaux indépendants lorsque lisolation des données ou une conformité stricte est requise.",
"agentDetail.configure.files.add": "Ajouter un fichier",
"agentDetail.configure.files.buildNote.generated": "Généré",
"agentDetail.configure.files.buildNote.richTooltip": "Le registre de l'agent sur ce qu'il a configuré en mode Build. Il le lit au début de chaque conversation, avec votre Prompt. <docLink>En savoir plus</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Téléchargez des documents que lagent peut lire, comme des spécifications, des modèles ou des directives",
"agentDetail.configure.files.empty.title": "Pas encore de fichiers",
"agentDetail.configure.files.label": "Fichiers",
"agentDetail.configure.files.missing": "Fichier introuvable",
"agentDetail.configure.files.preview.empty": "Aucun contenu daperçu.",
"agentDetail.configure.files.preview.failed": "Échec du chargement de laperçu.",
"agentDetail.configure.files.preview.unsupported": "Ce fichier ne prend pas en charge laperçu.",
@ -203,22 +198,14 @@
"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",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Téléversez un fichier .zip ou .skill.",
"agentDetail.configure.skills.upload.success": "Compétence téléversée.",
"agentDetail.configure.skills.upload.title": "Téléverser une compétence",
"agentDetail.configure.skills.upload.warning.files": "Si vous souhaitez uniquement utiliser des fichiers Markdown, téléversez-les dans Fichiers et référencez-les dans votre prompt.",
"agentDetail.configure.skills.upload.warning.specification": "Les compétences téléversées doivent respecter la <specificationLink>spécification Agent Skills</specificationLink>.",
"agentDetail.configure.title": "Configurer",
"agentDetail.configure.tools.add": "Ajouter un outil",
"agentDetail.configure.tools.addMenu.cliTool.badge": "Pour les développeurs",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "Options de tri",
"roster.sort.recentlyCreated": "Récemment créés",
"roster.updateSuccess": "Agent mis à jour.",
"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"
"roster.usageStatus.draft": "Brouillon"
}

View File

@ -201,7 +201,6 @@
"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",
@ -487,7 +486,6 @@
"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

@ -14,11 +14,10 @@
"agentDetail.access.title": "एक्सेस पॉइंट",
"agentDetail.access.toggleSurface": "{{name}} एक्सेस टॉगल करें",
"agentDetail.access.webApp.accessUrl": "एक्सेस URL",
"agentDetail.access.webApp.actions.accessControl": "पहुँच नियंत्रण",
"agentDetail.access.webApp.actions.customize": "कस्टम फ्रंटएंड",
"agentDetail.access.webApp.actions.customize": "अनुकूलित करें",
"agentDetail.access.webApp.actions.embedded": "एम्बेडेड",
"agentDetail.access.webApp.actions.launch": "लॉन्च करें",
"agentDetail.access.webApp.actions.settings": "ब्रांडिंग",
"agentDetail.access.webApp.actions.settings": "सेटिंग्स",
"agentDetail.access.webApp.refreshUrl": "एक्सेस URL रीफ़्रेश करें",
"agentDetail.access.webApp.showQrCode": "QR कोड दिखाएँ",
"agentDetail.access.webApp.ssoEnabled": "SSO सक्षम",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "उन्नत सेटिंग्स",
"agentDetail.configure.advancedSettings.toggle": "उन्नत सेटिंग्स टॉगल करें",
"agentDetail.configure.answer.activity.ranCommands": "कमांड चलाए गए",
"agentDetail.configure.answer.activity.runningCommands": "कमांड चलाए जा रहे हैं",
"agentDetail.configure.answer.duration.minute": "{{count}} मिनट",
"agentDetail.configure.answer.duration.second": "{{count}} सेकंड",
"agentDetail.configure.answer.thinking": "सोच रहा है",
"agentDetail.configure.answer.workFinished": "काम पूरा हुआ",
"agentDetail.configure.answer.workedFor": "{{duration}} तक काम किया",
"agentDetail.configure.answer.workingFor": "{{duration}} से काम कर रहा है",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "चैट सुविधाएँ",
"agentDetail.configure.clearSessionConfirm.description": "यह मौजूदा सेशन साफ़ कर देगा और Agent कॉन्फ़िगरेशन के वे बदलाव छोड़ देगा जो अभी लागू नहीं हुए हैं।",
"agentDetail.configure.clearSessionConfirm.title": "सेशन साफ़ करके बदलाव छोड़ें?",
"agentDetail.configure.communityEditionIsolationTip": "अस्वीकरण: Community Edition में sandbox, Docker के डिफ़ॉल्ट capability configuration के अंतर्गत non-root उपयोगकर्ता के रूप में चलता है और Agent की अपनी फ़ाइलों तथा session files के लिए केवल सीमित Landlock-आधारित सुरक्षा प्रदान करता है। सर्वर और सभी shell subprocesses एक ही PID namespace और container-level capability boundary साझा करते हैं, इसलिए इसे मज़बूत रूप से पृथक multi-layer security sandbox नहीं माना जाना चाहिए।",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition अंतिम उपयोगकर्ताओं या रन के बीच सख्त फ़ाइल सिस्टम आइसोलेशन प्रदान नहीं करता है। जहाँ डेटा आइसोलेशन या कड़े अनुपालन की आवश्यकता हो, वहाँ एक ही CE एजेंट को कई स्वतंत्र अंतिम उपयोगकर्ताओं के लिए उजागर न करें।",
"agentDetail.configure.files.add": "फ़ाइल जोड़ें",
"agentDetail.configure.files.buildNote.generated": "जनरेट किया गया",
"agentDetail.configure.files.buildNote.richTooltip": "Build mode में एजेंट ने जो सेट अप किया उसका रिकॉर्ड। हर बातचीत की शुरुआत में यह इसे आपके Prompt के साथ पढ़ता है। <docLink>और जानें</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "ऐसे दस्तावेज़ अपलोड करें जिन्हें एजेंट पढ़ सके, जैसे विनिर्देश, टेम्पलेट या दिशानिर्देश",
"agentDetail.configure.files.empty.title": "अभी तक कोई फ़ाइल नहीं",
"agentDetail.configure.files.label": "फ़ाइलें",
"agentDetail.configure.files.missing": "फ़ाइल नहीं मिली",
"agentDetail.configure.files.preview.empty": "कोई पूर्वावलोकन सामग्री नहीं।",
"agentDetail.configure.files.preview.failed": "पूर्वावलोकन लोड करने में विफल।",
"agentDetail.configure.files.preview.unsupported": "यह फ़ाइल पूर्वावलोकन का समर्थन नहीं करती।",
@ -203,22 +198,14 @@
"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": "कौशल टॉगल करें",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "एक .zip या .skill फ़ाइल अपलोड करें।",
"agentDetail.configure.skills.upload.success": "कौशल अपलोड हो गया।",
"agentDetail.configure.skills.upload.title": "कौशल अपलोड करें",
"agentDetail.configure.skills.upload.warning.files": "यदि आपको केवल Markdown फ़ाइलों का उपयोग करना है, तो उन्हें फ़ाइलें में अपलोड करें और अपने प्रॉम्प्ट में उनका संदर्भ दें।",
"agentDetail.configure.skills.upload.warning.specification": "अपलोड किए गए कौशल को <specificationLink>Agent Skills विनिर्देश</specificationLink> का पालन करना चाहिए।",
"agentDetail.configure.title": "कॉन्फ़िगर करें",
"agentDetail.configure.tools.add": "उपकरण जोड़ें",
"agentDetail.configure.tools.addMenu.cliTool.badge": "डेवलपर्स के लिए",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "क्रमबद्ध विकल्प",
"roster.sort.recentlyCreated": "हाल ही में बनाए गए",
"roster.updateSuccess": "एजेंट अपडेट हो गया।",
"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"
"roster.usageStatus.draft": "ड्राफ्ट"
}

View File

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

View File

@ -14,11 +14,10 @@
"agentDetail.access.title": "Titik Akses",
"agentDetail.access.toggleSurface": "Alihkan akses {{name}}",
"agentDetail.access.webApp.accessUrl": "URL Akses",
"agentDetail.access.webApp.actions.accessControl": "Kontrol Akses",
"agentDetail.access.webApp.actions.customize": "Frontend Kustom",
"agentDetail.access.webApp.actions.customize": "Sesuaikan",
"agentDetail.access.webApp.actions.embedded": "Tertanam",
"agentDetail.access.webApp.actions.launch": "Luncurkan",
"agentDetail.access.webApp.actions.settings": "Branding",
"agentDetail.access.webApp.actions.settings": "Pengaturan",
"agentDetail.access.webApp.refreshUrl": "Segarkan URL akses",
"agentDetail.access.webApp.showQrCode": "Tampilkan kode QR",
"agentDetail.access.webApp.ssoEnabled": "SSO Diaktifkan",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Pengaturan Lanjutan",
"agentDetail.configure.advancedSettings.toggle": "Alihkan pengaturan lanjutan",
"agentDetail.configure.answer.activity.ranCommands": "Perintah dijalankan",
"agentDetail.configure.answer.activity.runningCommands": "Menjalankan perintah",
"agentDetail.configure.answer.duration.minute": "{{count}} mnt",
"agentDetail.configure.answer.duration.second": "{{count}} dtk",
"agentDetail.configure.answer.thinking": "Sedang berpikir",
"agentDetail.configure.answer.workFinished": "Pekerjaan selesai",
"agentDetail.configure.answer.workedFor": "Bekerja selama {{duration}}",
"agentDetail.configure.answer.workingFor": "Bekerja selama {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Fitur Chat",
"agentDetail.configure.clearSessionConfirm.description": "Tindakan ini akan menghapus sesi saat ini dan membuang perubahan konfigurasi Agent yang belum diterapkan.",
"agentDetail.configure.clearSessionConfirm.title": "Hapus sesi dan buang perubahan?",
"agentDetail.configure.communityEditionIsolationTip": "Penafian: Di Community Edition, sandbox berjalan sebagai pengguna non-root dengan konfigurasi kapabilitas default Docker dan hanya menyediakan perlindungan terbatas berbasis Landlock untuk file milik agen dan file sesi. Server dan semua subproses shell menggunakan namespace PID dan batas kapabilitas tingkat kontainer yang sama, sehingga sandbox ini tidak boleh dianggap sebagai sandbox keamanan berlapis dengan isolasi yang kuat.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition tidak menyediakan isolasi sistem file yang ketat antar pengguna akhir atau antar eksekusi. Jangan mengekspos agen CE yang sama kepada beberapa pengguna akhir independen ketika isolasi data atau kepatuhan ketat diperlukan.",
"agentDetail.configure.files.add": "Tambahkan file",
"agentDetail.configure.files.buildNote.generated": "Dihasilkan",
"agentDetail.configure.files.buildNote.richTooltip": "Catatan agen tentang apa yang disiapkannya dalam mode Build. Agen membaca ini di awal setiap percakapan, bersama Prompt Anda. <docLink>Pelajari selengkapnya</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Unggah dokumen yang dapat dibaca agen, seperti spesifikasi, templat, atau pedoman",
"agentDetail.configure.files.empty.title": "Belum ada file",
"agentDetail.configure.files.label": "File",
"agentDetail.configure.files.missing": "File tidak ditemukan",
"agentDetail.configure.files.preview.empty": "Tidak ada konten pratinjau.",
"agentDetail.configure.files.preview.failed": "Gagal memuat pratinjau.",
"agentDetail.configure.files.preview.unsupported": "File ini tidak mendukung pratinjau.",
@ -203,22 +198,14 @@
"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",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Unggah file .zip atau .skill.",
"agentDetail.configure.skills.upload.success": "Keterampilan diunggah.",
"agentDetail.configure.skills.upload.title": "Unggah keterampilan",
"agentDetail.configure.skills.upload.warning.files": "Jika Anda hanya perlu menggunakan file Markdown, unggah ke File dan rujuk file tersebut dalam prompt Anda.",
"agentDetail.configure.skills.upload.warning.specification": "Keterampilan yang diunggah harus mengikuti <specificationLink>spesifikasi Agent Skills</specificationLink>.",
"agentDetail.configure.title": "Konfigurasi",
"agentDetail.configure.tools.add": "Tambahkan alat",
"agentDetail.configure.tools.addMenu.cliTool.badge": "Untuk pengembang",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "Opsi pengurutan",
"roster.sort.recentlyCreated": "Baru dibuat",
"roster.updateSuccess": "Agen diperbarui.",
"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"
"roster.usageStatus.draft": "Draf"
}

View File

@ -201,7 +201,6 @@
"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",
@ -487,7 +486,6 @@
"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

@ -14,11 +14,10 @@
"agentDetail.access.title": "Punto di accesso",
"agentDetail.access.toggleSurface": "Attiva/disattiva accesso di {{name}}",
"agentDetail.access.webApp.accessUrl": "URL di accesso",
"agentDetail.access.webApp.actions.accessControl": "Controllo di accesso",
"agentDetail.access.webApp.actions.customize": "Frontend personalizzato",
"agentDetail.access.webApp.actions.customize": "Personalizza",
"agentDetail.access.webApp.actions.embedded": "Incorporato",
"agentDetail.access.webApp.actions.launch": "Avvia",
"agentDetail.access.webApp.actions.settings": "Branding",
"agentDetail.access.webApp.actions.settings": "Impostazioni",
"agentDetail.access.webApp.refreshUrl": "Aggiorna URL di accesso",
"agentDetail.access.webApp.showQrCode": "Mostra codice QR",
"agentDetail.access.webApp.ssoEnabled": "SSO abilitato",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Impostazioni avanzate",
"agentDetail.configure.advancedSettings.toggle": "Attiva/disattiva impostazioni avanzate",
"agentDetail.configure.answer.activity.ranCommands": "Comandi eseguiti",
"agentDetail.configure.answer.activity.runningCommands": "Esecuzione dei comandi",
"agentDetail.configure.answer.duration.minute": "{{count}} min",
"agentDetail.configure.answer.duration.second": "{{count}} s",
"agentDetail.configure.answer.thinking": "Sto pensando",
"agentDetail.configure.answer.workFinished": "Lavoro terminato",
"agentDetail.configure.answer.workedFor": "Ha lavorato per {{duration}}",
"agentDetail.configure.answer.workingFor": "Sta lavorando da {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Funzionalità chat",
"agentDetail.configure.clearSessionConfirm.description": "Questa operazione cancellerà la sessione corrente e scarterà le modifiche alla configurazione dellAgent non ancora applicate.",
"agentDetail.configure.clearSessionConfirm.title": "Cancellare la sessione e scartare le modifiche?",
"agentDetail.configure.communityEditionIsolationTip": "Esclusione di responsabilità: In Community Edition, la sandbox viene eseguita come utente non root con la configurazione predefinita delle capability di Docker e fornisce solo una protezione limitata basata su Landlock per i file dellagente e i file di sessione. Il server e tutti i sottoprocessi della shell condividono lo stesso namespace PID e lo stesso limite di capability a livello di container, quindi non deve essere considerata una sandbox di sicurezza multilivello con isolamento forte.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition non fornisce un isolamento rigido del file system tra utenti finali o esecuzioni. Non esporre lo stesso agente CE a più utenti finali indipendenti quando sono richiesti isolamento dei dati o conformità rigorosa.",
"agentDetail.configure.files.add": "Aggiungi file",
"agentDetail.configure.files.buildNote.generated": "Generato",
"agentDetail.configure.files.buildNote.richTooltip": "Il registro dell'agente di ciò che ha configurato in modalità Build. Lo legge all'inizio di ogni conversazione, insieme al tuo Prompt. <docLink>Scopri di più</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Carica documenti che lagente possa leggere, come specifiche, modelli o linee guida",
"agentDetail.configure.files.empty.title": "Nessun file al momento",
"agentDetail.configure.files.label": "File",
"agentDetail.configure.files.missing": "File non trovato",
"agentDetail.configure.files.preview.empty": "Nessun contenuto in anteprima.",
"agentDetail.configure.files.preview.failed": "Impossibile caricare lanteprima.",
"agentDetail.configure.files.preview.unsupported": "Questo file non supporta lanteprima.",
@ -203,22 +198,14 @@
"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à",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Carica un file .zip o .skill.",
"agentDetail.configure.skills.upload.success": "Abilità caricata.",
"agentDetail.configure.skills.upload.title": "Carica abilità",
"agentDetail.configure.skills.upload.warning.files": "Se devi usare solo file Markdown, caricali in File e richiamali nel tuo prompt.",
"agentDetail.configure.skills.upload.warning.specification": "Le abilità caricate devono rispettare la <specificationLink>specifica Agent Skills</specificationLink>.",
"agentDetail.configure.title": "Configura",
"agentDetail.configure.tools.add": "Aggiungi strumento",
"agentDetail.configure.tools.addMenu.cliTool.badge": "Per sviluppatori",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "Opzioni di ordinamento",
"roster.sort.recentlyCreated": "Creati di recente",
"roster.updateSuccess": "Agente aggiornato.",
"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"
"roster.usageStatus.draft": "Bozza"
}

View File

@ -201,7 +201,6 @@
"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",
@ -487,7 +486,6 @@
"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

@ -14,11 +14,10 @@
"agentDetail.access.title": "アクセスポイント",
"agentDetail.access.toggleSurface": "{{name}} のアクセスを切り替え",
"agentDetail.access.webApp.accessUrl": "アクセス URL",
"agentDetail.access.webApp.actions.accessControl": "アクセス制御",
"agentDetail.access.webApp.actions.customize": "カスタムフロントエンド",
"agentDetail.access.webApp.actions.customize": "カスタマイズ",
"agentDetail.access.webApp.actions.embedded": "埋め込み",
"agentDetail.access.webApp.actions.launch": "起動",
"agentDetail.access.webApp.actions.settings": "ブランディング",
"agentDetail.access.webApp.actions.settings": "設定",
"agentDetail.access.webApp.refreshUrl": "アクセス URL を更新",
"agentDetail.access.webApp.showQrCode": "QR コードを表示",
"agentDetail.access.webApp.ssoEnabled": "SSO 有効",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "詳細設定",
"agentDetail.configure.advancedSettings.toggle": "詳細設定の表示を切り替え",
"agentDetail.configure.answer.activity.ranCommands": "コマンドを実行しました",
"agentDetail.configure.answer.activity.runningCommands": "コマンドを実行中",
"agentDetail.configure.answer.duration.minute": "{{count}}分",
"agentDetail.configure.answer.duration.second": "{{count}}秒",
"agentDetail.configure.answer.thinking": "思考中",
"agentDetail.configure.answer.workFinished": "作業が完了しました",
"agentDetail.configure.answer.workedFor": "{{duration}} 実行しました",
"agentDetail.configure.answer.workingFor": "{{duration}} 実行中",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "チャット機能",
"agentDetail.configure.clearSessionConfirm.description": "現在のセッションをクリアし、まだ適用されていない Agent の設定変更を破棄します。",
"agentDetail.configure.clearSessionConfirm.title": "セッションをクリアして変更を破棄しますか?",
"agentDetail.configure.communityEditionIsolationTip": "免責事項Community Edition では、サンドボックスは Docker のデフォルトのケイパビリティ構成下で非 root ユーザーとして実行され、エージェント自身のファイルとセッションファイルに対して Landlock ベースの限定的な保護のみを提供します。サーバーとすべてのシェルサブプロセスは同じ PID 名前空間とコンテナレベルのケイパビリティ境界を共有するため、強力に分離された多層セキュリティサンドボックスとみなすべきではありません。",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition では、エンドユーザー間または実行間で厳密なファイルシステム分離は提供されません。データ分離や厳格なコンプライアンスが必要な場合は、同じ CE エージェントを複数の独立したエンドユーザーに公開しないでください。",
"agentDetail.configure.files.add": "ファイルを追加",
"agentDetail.configure.files.buildNote.generated": "生成済み",
"agentDetail.configure.files.buildNote.richTooltip": "Build モードでエージェントが設定した内容の記録です。各会話の開始時に、Prompt と一緒にこれを読み取ります。<docLink>詳しく見る</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "仕様、テンプレート、ガイドラインなど、エージェントが読めるドキュメントをアップロード",
"agentDetail.configure.files.empty.title": "ファイルはまだありません",
"agentDetail.configure.files.label": "ファイル",
"agentDetail.configure.files.missing": "ファイルが見つかりません",
"agentDetail.configure.files.preview.empty": "プレビュー内容はありません。",
"agentDetail.configure.files.preview.failed": "プレビューの読み込みに失敗しました。",
"agentDetail.configure.files.preview.unsupported": "ファイルはプレビューに対応していません。",
@ -203,22 +198,14 @@
"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": "スキルの表示を切り替え",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": ".zip または .skill ファイルをアップロードしてください。",
"agentDetail.configure.skills.upload.success": "スキルをアップロードしました。",
"agentDetail.configure.skills.upload.title": "スキルをアップロード",
"agentDetail.configure.skills.upload.warning.files": "Markdown ファイルのみを使用する場合は、「ファイル」にアップロードし、プロンプト内で参照してください。",
"agentDetail.configure.skills.upload.warning.specification": "アップロードするスキルは <specificationLink>Agent Skills 仕様</specificationLink>に準拠する必要があります。",
"agentDetail.configure.title": "設定",
"agentDetail.configure.tools.add": "ツールを追加",
"agentDetail.configure.tools.addMenu.cliTool.badge": "開発者向け",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "並び替えオプション",
"roster.sort.recentlyCreated": "最近作成",
"roster.updateSuccess": "エージェントを更新しました。",
"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"
"roster.usageStatus.draft": "ドラフト"
}

View File

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

View File

@ -14,11 +14,10 @@
"agentDetail.access.title": "액세스 지점",
"agentDetail.access.toggleSurface": "{{name}} 액세스 전환",
"agentDetail.access.webApp.accessUrl": "액세스 URL",
"agentDetail.access.webApp.actions.accessControl": "접근 제어",
"agentDetail.access.webApp.actions.customize": "커스텀 프런트엔드",
"agentDetail.access.webApp.actions.customize": "사용자 지정",
"agentDetail.access.webApp.actions.embedded": "임베드",
"agentDetail.access.webApp.actions.launch": "실행",
"agentDetail.access.webApp.actions.settings": "브랜딩",
"agentDetail.access.webApp.actions.settings": "설정",
"agentDetail.access.webApp.refreshUrl": "액세스 URL 새로 고침",
"agentDetail.access.webApp.showQrCode": "QR 코드 표시",
"agentDetail.access.webApp.ssoEnabled": "SSO 사용 가능",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "고급 설정",
"agentDetail.configure.advancedSettings.toggle": "고급 설정 전환",
"agentDetail.configure.answer.activity.ranCommands": "명령 실행됨",
"agentDetail.configure.answer.activity.runningCommands": "명령 실행 중",
"agentDetail.configure.answer.duration.minute": "{{count}}분",
"agentDetail.configure.answer.duration.second": "{{count}}초",
"agentDetail.configure.answer.thinking": "생각 중",
"agentDetail.configure.answer.workFinished": "작업 완료",
"agentDetail.configure.answer.workedFor": "{{duration}} 동안 작업함",
"agentDetail.configure.answer.workingFor": "{{duration}} 동안 작업 중",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "채팅 기능",
"agentDetail.configure.clearSessionConfirm.description": "현재 세션이 지워지고 아직 적용되지 않은 Agent 구성 변경 사항이 폐기됩니다.",
"agentDetail.configure.clearSessionConfirm.title": "세션을 지우고 변경 사항을 폐기할까요?",
"agentDetail.configure.communityEditionIsolationTip": "면책 조항: Community Edition에서 sandbox는 Docker의 기본 capability 구성 아래 non-root 사용자로 실행되며 Agent 자체 파일과 세션 파일에 대해 제한적인 Landlock 기반 보호만 제공합니다. 서버와 모든 shell 하위 프로세스는 동일한 PID namespace와 컨테이너 수준 capability 경계를 공유하므로, 강력하게 격리된 다계층 보안 sandbox로 간주해서는 안 됩니다.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition은 최종 사용자 간 또는 실행 간에 강력한 파일 시스템 격리를 제공하지 않습니다. 데이터 격리나 엄격한 규정 준수가 필요한 경우 동일한 CE 에이전트를 여러 독립 최종 사용자에게 노출하지 마세요.",
"agentDetail.configure.files.add": "파일 추가",
"agentDetail.configure.files.buildNote.generated": "생성됨",
"agentDetail.configure.files.buildNote.richTooltip": "에이전트가 Build mode에서 설정한 내용의 기록입니다. 모든 대화 시작 시 Prompt와 함께 이 기록을 읽습니다. <docLink>자세히 알아보기</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "사양, 템플릿, 가이드라인 등 에이전트가 읽을 수 있는 문서를 업로드하세요",
"agentDetail.configure.files.empty.title": "아직 파일이 없습니다",
"agentDetail.configure.files.label": "파일",
"agentDetail.configure.files.missing": "파일을 찾을 수 없음",
"agentDetail.configure.files.preview.empty": "미리보기 내용이 없습니다.",
"agentDetail.configure.files.preview.failed": "미리보기를 불러오지 못했습니다.",
"agentDetail.configure.files.preview.unsupported": "이 파일은 미리보기를 지원하지 않습니다.",
@ -203,22 +198,14 @@
"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": "스킬 전환",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": ".zip 또는 .skill 파일을 업로드하세요.",
"agentDetail.configure.skills.upload.success": "스킬을 업로드했습니다.",
"agentDetail.configure.skills.upload.title": "스킬 업로드",
"agentDetail.configure.skills.upload.warning.files": "Markdown 파일만 사용하려면 파일에 업로드하고 프롬프트에서 참조하세요.",
"agentDetail.configure.skills.upload.warning.specification": "업로드한 스킬은 <specificationLink>Agent Skills 사양</specificationLink>을 준수해야 합니다.",
"agentDetail.configure.title": "구성",
"agentDetail.configure.tools.add": "도구 추가",
"agentDetail.configure.tools.addMenu.cliTool.badge": "개발자용",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "정렬 옵션",
"roster.sort.recentlyCreated": "최근 생성",
"roster.updateSuccess": "에이전트가 업데이트되었습니다.",
"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"
"roster.usageStatus.draft": "초안"
}

View File

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

View File

@ -14,11 +14,10 @@
"agentDetail.access.title": "Toegangspunt",
"agentDetail.access.toggleSurface": "Toegang van {{name}} schakelen",
"agentDetail.access.webApp.accessUrl": "Toegangs-URL",
"agentDetail.access.webApp.actions.accessControl": "Toegangsbeheer",
"agentDetail.access.webApp.actions.customize": "Aangepaste frontend",
"agentDetail.access.webApp.actions.customize": "Aanpassen",
"agentDetail.access.webApp.actions.embedded": "Ingesloten",
"agentDetail.access.webApp.actions.launch": "Starten",
"agentDetail.access.webApp.actions.settings": "Branding",
"agentDetail.access.webApp.actions.settings": "Instellingen",
"agentDetail.access.webApp.refreshUrl": "Toegangs-URL vernieuwen",
"agentDetail.access.webApp.showQrCode": "QR-code tonen",
"agentDetail.access.webApp.ssoEnabled": "SSO ingeschakeld",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Geavanceerde instellingen",
"agentDetail.configure.advancedSettings.toggle": "Geavanceerde instellingen in/uit",
"agentDetail.configure.answer.activity.ranCommands": "Opdrachten uitgevoerd",
"agentDetail.configure.answer.activity.runningCommands": "Opdrachten uitvoeren",
"agentDetail.configure.answer.duration.minute": "{{count}} min",
"agentDetail.configure.answer.duration.second": "{{count}} s",
"agentDetail.configure.answer.thinking": "Aan het nadenken",
"agentDetail.configure.answer.workFinished": "Werk voltooid",
"agentDetail.configure.answer.workedFor": "Gewerkt gedurende {{duration}}",
"agentDetail.configure.answer.workingFor": "Bezig gedurende {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Chatfuncties",
"agentDetail.configure.clearSessionConfirm.description": "Dit wist de huidige sessie en negeert nog niet toegepaste configuratiewijzigingen van de Agent.",
"agentDetail.configure.clearSessionConfirm.title": "Sessie wissen en wijzigingen negeren?",
"agentDetail.configure.communityEditionIsolationTip": "Disclaimer: In Community Edition draait de sandbox als een niet-rootgebruiker onder de standaard capabilityconfiguratie van Docker en biedt deze alleen beperkte, op Landlock gebaseerde bescherming voor de eigen bestanden van de agent en sessiebestanden. De server en alle shellsubprocessen delen dezelfde PID-namespace en capabilitygrens op containerniveau. Daarom mag dit niet worden beschouwd als een sterk geïsoleerde, meerlaagse beveiligingssandbox.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition biedt geen harde bestandssysteemisolatie tussen eindgebruikers of runs. Stel dezelfde CE-agent niet beschikbaar aan meerdere onafhankelijke eindgebruikers wanneer gegevensisolatie of strikte compliance vereist is.",
"agentDetail.configure.files.add": "Bestand toevoegen",
"agentDetail.configure.files.buildNote.generated": "Gegenereerd",
"agentDetail.configure.files.buildNote.richTooltip": "Het verslag van de agent van wat hij in de Build-modus heeft ingesteld. Hij leest dit aan het begin van elk gesprek, samen met uw Prompt. <docLink>Meer informatie</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Upload documenten die de agent kan lezen, zoals specificaties, sjablonen of richtlijnen",
"agentDetail.configure.files.empty.title": "Nog geen bestanden",
"agentDetail.configure.files.label": "Bestanden",
"agentDetail.configure.files.missing": "Bestand niet gevonden",
"agentDetail.configure.files.preview.empty": "Geen voorbeeldinhoud.",
"agentDetail.configure.files.preview.failed": "Laden van voorbeeld mislukt.",
"agentDetail.configure.files.preview.unsupported": "Dit bestand ondersteunt geen voorbeeldweergave.",
@ -203,22 +198,14 @@
"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",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Upload een .zip- of .skill-bestand.",
"agentDetail.configure.skills.upload.success": "Skill geüpload.",
"agentDetail.configure.skills.upload.title": "Skill uploaden",
"agentDetail.configure.skills.upload.warning.files": "Als je alleen Markdown-bestanden wilt gebruiken, upload ze dan naar Bestanden en verwijs ernaar in je prompt.",
"agentDetail.configure.skills.upload.warning.specification": "Geüploade skills moeten voldoen aan de <specificationLink>Agent Skills-specificatie</specificationLink>.",
"agentDetail.configure.title": "Configureren",
"agentDetail.configure.tools.add": "Tool toevoegen",
"agentDetail.configure.tools.addMenu.cliTool.badge": "Voor ontwikkelaars",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "Sorteeropties",
"roster.sort.recentlyCreated": "Recent aangemaakt",
"roster.updateSuccess": "Agent bijgewerkt.",
"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"
"roster.usageStatus.draft": "Concept"
}

View File

@ -201,7 +201,6 @@
"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",
@ -487,7 +486,6 @@
"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

@ -14,11 +14,10 @@
"agentDetail.access.title": "Punkt dostępu",
"agentDetail.access.toggleSurface": "Przełącz dostęp {{name}}",
"agentDetail.access.webApp.accessUrl": "URL dostępu",
"agentDetail.access.webApp.actions.accessControl": "Kontrola dostępu",
"agentDetail.access.webApp.actions.customize": "Niestandardowy frontend",
"agentDetail.access.webApp.actions.customize": "Dostosuj",
"agentDetail.access.webApp.actions.embedded": "Osadzony",
"agentDetail.access.webApp.actions.launch": "Uruchom",
"agentDetail.access.webApp.actions.settings": "Branding",
"agentDetail.access.webApp.actions.settings": "Ustawienia",
"agentDetail.access.webApp.refreshUrl": "Odśwież URL dostępu",
"agentDetail.access.webApp.showQrCode": "Pokaż kod QR",
"agentDetail.access.webApp.ssoEnabled": "SSO włączone",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Ustawienia zaawansowane",
"agentDetail.configure.advancedSettings.toggle": "Przełącz ustawienia zaawansowane",
"agentDetail.configure.answer.activity.ranCommands": "Uruchomiono polecenia",
"agentDetail.configure.answer.activity.runningCommands": "Uruchamianie poleceń",
"agentDetail.configure.answer.duration.minute": "{{count}} min",
"agentDetail.configure.answer.duration.second": "{{count}} s",
"agentDetail.configure.answer.thinking": "Myśli",
"agentDetail.configure.answer.workFinished": "Praca zakończona",
"agentDetail.configure.answer.workedFor": "Pracował przez {{duration}}",
"agentDetail.configure.answer.workingFor": "Pracuje od {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Funkcje czatu",
"agentDetail.configure.clearSessionConfirm.description": "Spowoduje to wyczyszczenie bieżącej sesji i odrzucenie niezastosowanych zmian konfiguracji Agent.",
"agentDetail.configure.clearSessionConfirm.title": "Wyczyścić sesję i odrzucić zmiany?",
"agentDetail.configure.communityEditionIsolationTip": "Zastrzeżenie: W Community Edition piaskownica działa jako użytkownik bez uprawnień root przy domyślnej konfiguracji capabilities platformy Docker i zapewnia jedynie ograniczoną ochronę opartą na Landlock dla własnych plików agenta oraz plików sesji. Serwer i wszystkie podprocesy powłoki współdzielą tę samą przestrzeń nazw PID oraz granicę capabilities na poziomie kontenera, dlatego nie należy jej uznawać za silnie izolowaną, wielowarstwową piaskownicę bezpieczeństwa.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition nie zapewnia twardej izolacji systemu plików między użytkownikami końcowymi ani uruchomieniami. Nie udostępniaj tego samego agenta CE wielu niezależnym użytkownikom końcowym, gdy wymagana jest izolacja danych lub ścisła zgodność.",
"agentDetail.configure.files.add": "Dodaj plik",
"agentDetail.configure.files.buildNote.generated": "Wygenerowano",
"agentDetail.configure.files.buildNote.richTooltip": "Zapis agenta tego, co skonfigurował w trybie Build. Odczytuje go na początku każdej rozmowy razem z Twoim Promptem. <docLink>Dowiedz się więcej</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Prześlij dokumenty, które agent może czytać, np. specyfikacje, szablony lub wytyczne",
"agentDetail.configure.files.empty.title": "Brak plików",
"agentDetail.configure.files.label": "Pliki",
"agentDetail.configure.files.missing": "Nie znaleziono pliku",
"agentDetail.configure.files.preview.empty": "Brak treści podglądu.",
"agentDetail.configure.files.preview.failed": "Nie udało się załadować podglądu.",
"agentDetail.configure.files.preview.unsupported": "Ten plik nie obsługuje podglądu.",
@ -203,22 +198,14 @@
"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",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Prześlij plik .zip lub .skill.",
"agentDetail.configure.skills.upload.success": "Umiejętność przesłana.",
"agentDetail.configure.skills.upload.title": "Prześlij umiejętność",
"agentDetail.configure.skills.upload.warning.files": "Jeśli chcesz używać tylko plików Markdown, prześlij je do sekcji Pliki i odwołaj się do nich w swoim prompcie.",
"agentDetail.configure.skills.upload.warning.specification": "Przesyłane umiejętności muszą być zgodne ze <specificationLink>specyfikacją Agent Skills</specificationLink>.",
"agentDetail.configure.title": "Konfiguruj",
"agentDetail.configure.tools.add": "Dodaj narzędzie",
"agentDetail.configure.tools.addMenu.cliTool.badge": "Dla deweloperów",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "Opcje sortowania",
"roster.sort.recentlyCreated": "Ostatnio utworzone",
"roster.updateSuccess": "Agent zaktualizowany.",
"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"
"roster.usageStatus.draft": "Wersja robocza"
}

View File

@ -201,7 +201,6 @@
"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",
@ -487,7 +486,6 @@
"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

@ -14,11 +14,10 @@
"agentDetail.access.title": "Ponto de acesso",
"agentDetail.access.toggleSurface": "Alternar acesso de {{name}}",
"agentDetail.access.webApp.accessUrl": "URL de acesso",
"agentDetail.access.webApp.actions.accessControl": "Controle de Acesso",
"agentDetail.access.webApp.actions.customize": "Frontend personalizado",
"agentDetail.access.webApp.actions.customize": "Personalizar",
"agentDetail.access.webApp.actions.embedded": "Incorporado",
"agentDetail.access.webApp.actions.launch": "Iniciar",
"agentDetail.access.webApp.actions.settings": "Marca",
"agentDetail.access.webApp.actions.settings": "Configurações",
"agentDetail.access.webApp.refreshUrl": "Atualizar URL de acesso",
"agentDetail.access.webApp.showQrCode": "Mostrar QR code",
"agentDetail.access.webApp.ssoEnabled": "SSO ativado",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Configurações avançadas",
"agentDetail.configure.advancedSettings.toggle": "Alternar configurações avançadas",
"agentDetail.configure.answer.activity.ranCommands": "Comandos executados",
"agentDetail.configure.answer.activity.runningCommands": "Executando comandos",
"agentDetail.configure.answer.duration.minute": "{{count}} min",
"agentDetail.configure.answer.duration.second": "{{count}} s",
"agentDetail.configure.answer.thinking": "Pensando",
"agentDetail.configure.answer.workFinished": "Trabalho concluído",
"agentDetail.configure.answer.workedFor": "Trabalhou por {{duration}}",
"agentDetail.configure.answer.workingFor": "Trabalhando há {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Recursos de chat",
"agentDetail.configure.clearSessionConfirm.description": "Isso limpará a sessão atual e descartará as alterações de configuração do Agent que ainda não foram aplicadas.",
"agentDetail.configure.clearSessionConfirm.title": "Limpar a sessão e descartar alterações?",
"agentDetail.configure.communityEditionIsolationTip": "Aviso: Na Community Edition, o sandbox é executado como um usuário não root com a configuração padrão de capabilities do Docker e fornece apenas proteção limitada baseada em Landlock para os próprios arquivos do agente e os arquivos da sessão. O servidor e todos os subprocessos de shell compartilham o mesmo namespace de PID e o mesmo limite de capabilities no nível do contêiner; portanto, ele não deve ser considerado um sandbox de segurança multicamada com isolamento forte.",
"agentDetail.configure.communityEditionIsolationTip": "A Community Edition não fornece isolamento rígido do sistema de arquivos entre usuários finais ou execuções. Não exponha o mesmo agente CE a vários usuários finais independentes quando isolamento de dados ou conformidade rigorosa forem necessários.",
"agentDetail.configure.files.add": "Adicionar arquivo",
"agentDetail.configure.files.buildNote.generated": "Gerado",
"agentDetail.configure.files.buildNote.richTooltip": "O registro do agente do que ele configurou no modo Build. Ele lê isso no início de cada conversa, junto com seu Prompt. <docLink>Saiba mais</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Envie documentos que o agente possa ler, como especificações, modelos ou diretrizes",
"agentDetail.configure.files.empty.title": "Ainda não há arquivos",
"agentDetail.configure.files.label": "Arquivos",
"agentDetail.configure.files.missing": "Arquivo não encontrado",
"agentDetail.configure.files.preview.empty": "Sem conteúdo de pré-visualização.",
"agentDetail.configure.files.preview.failed": "Falha ao carregar a pré-visualização.",
"agentDetail.configure.files.preview.unsupported": "Este arquivo não oferece suporte à pré-visualização.",
@ -203,22 +198,14 @@
"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",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Envie um arquivo .zip ou .skill.",
"agentDetail.configure.skills.upload.success": "Habilidade enviada.",
"agentDetail.configure.skills.upload.title": "Enviar habilidade",
"agentDetail.configure.skills.upload.warning.files": "Se você só precisa usar arquivos Markdown, envie-os para Arquivos e faça referência a eles no seu prompt.",
"agentDetail.configure.skills.upload.warning.specification": "As habilidades enviadas devem seguir a <specificationLink>especificação Agent Skills</specificationLink>.",
"agentDetail.configure.title": "Configurar",
"agentDetail.configure.tools.add": "Adicionar ferramenta",
"agentDetail.configure.tools.addMenu.cliTool.badge": "Para desenvolvedores",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "Opções de ordenação",
"roster.sort.recentlyCreated": "Criados recentemente",
"roster.updateSuccess": "Agente atualizado.",
"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"
"roster.usageStatus.draft": "Rascunho"
}

View File

@ -201,7 +201,6 @@
"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",
@ -487,7 +486,6 @@
"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

@ -14,11 +14,10 @@
"agentDetail.access.title": "Punct de acces",
"agentDetail.access.toggleSurface": "Comută accesul pentru {{name}}",
"agentDetail.access.webApp.accessUrl": "URL de acces",
"agentDetail.access.webApp.actions.accessControl": "Controlul Accesului",
"agentDetail.access.webApp.actions.customize": "Frontend personalizat",
"agentDetail.access.webApp.actions.customize": "Personalizează",
"agentDetail.access.webApp.actions.embedded": "Încorporat",
"agentDetail.access.webApp.actions.launch": "Lansează",
"agentDetail.access.webApp.actions.settings": "Branding",
"agentDetail.access.webApp.actions.settings": "Setări",
"agentDetail.access.webApp.refreshUrl": "Reîmprospătează URL-ul de acces",
"agentDetail.access.webApp.showQrCode": "Afișează codul QR",
"agentDetail.access.webApp.ssoEnabled": "SSO activat",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Setări avansate",
"agentDetail.configure.advancedSettings.toggle": "Comută setările avansate",
"agentDetail.configure.answer.activity.ranCommands": "Comenzi executate",
"agentDetail.configure.answer.activity.runningCommands": "Se execută comenzile",
"agentDetail.configure.answer.duration.minute": "{{count}} min",
"agentDetail.configure.answer.duration.second": "{{count}} s",
"agentDetail.configure.answer.thinking": "Se gândește",
"agentDetail.configure.answer.workFinished": "Lucrare finalizată",
"agentDetail.configure.answer.workedFor": "A lucrat timp de {{duration}}",
"agentDetail.configure.answer.workingFor": "Lucrează de {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Funcții de chat",
"agentDetail.configure.clearSessionConfirm.description": "Această acțiune va goli sesiunea curentă și va renunța la modificările neaplicate ale configurării Agent.",
"agentDetail.configure.clearSessionConfirm.title": "Golești sesiunea și renunți la modificări?",
"agentDetail.configure.communityEditionIsolationTip": "Declinarea răspunderii: În Community Edition, sandbox-ul rulează ca utilizator non-root cu configurația implicită de capabilități Docker și oferă doar protecție limitată bazată pe Landlock pentru fișierele proprii ale agentului și fișierele de sesiune. Serverul și toate subprocesele shell partajează același spațiu de nume PID și aceeași limită de capabilități la nivel de container, deci nu trebuie considerat un sandbox de securitate multistrat puternic izolat.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition nu oferă izolare strictă a sistemului de fișiere între utilizatorii finali sau între rulări. Nu expune același agent CE către mai mulți utilizatori finali independenți atunci când este necesară izolarea datelor sau conformitatea strictă.",
"agentDetail.configure.files.add": "Adaugă fișier",
"agentDetail.configure.files.buildNote.generated": "Generat",
"agentDetail.configure.files.buildNote.richTooltip": "Înregistrarea agentului despre ce a configurat în modul Build. O citește la începutul fiecărei conversații, împreună cu Promptul dvs. <docLink>Aflați mai multe</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Încarcă documente pe care agentul le poate citi, precum specificații, șabloane sau ghiduri",
"agentDetail.configure.files.empty.title": "Niciun fișier încă",
"agentDetail.configure.files.label": "Fișiere",
"agentDetail.configure.files.missing": "Fișierul nu a fost găsit",
"agentDetail.configure.files.preview.empty": "Niciun conținut de previzualizat.",
"agentDetail.configure.files.preview.failed": "Încărcarea previzualizării a eșuat.",
"agentDetail.configure.files.preview.unsupported": "Acest fișier nu acceptă previzualizarea.",
@ -203,22 +198,14 @@
"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",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Încărcați un fișier .zip sau .skill.",
"agentDetail.configure.skills.upload.success": "Abilitate încărcată.",
"agentDetail.configure.skills.upload.title": "Încarcă abilitate",
"agentDetail.configure.skills.upload.warning.files": "Dacă trebuie doar să folosești fișiere Markdown, încarcă-le în Fișiere și menționează-le în promptul tău.",
"agentDetail.configure.skills.upload.warning.specification": "Abilitățile încărcate trebuie să respecte <specificationLink>specificația Agent Skills</specificationLink>.",
"agentDetail.configure.title": "Configurare",
"agentDetail.configure.tools.add": "Adaugă instrument",
"agentDetail.configure.tools.addMenu.cliTool.badge": "Pentru dezvoltatori",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "Opțiuni de sortare",
"roster.sort.recentlyCreated": "Create recent",
"roster.updateSuccess": "Agent actualizat.",
"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"
"roster.usageStatus.draft": "Ciornă"
}

View File

@ -201,7 +201,6 @@
"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",
@ -487,7 +486,6 @@
"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

@ -14,11 +14,10 @@
"agentDetail.access.title": "Точка доступа",
"agentDetail.access.toggleSurface": "Переключить доступ {{name}}",
"agentDetail.access.webApp.accessUrl": "URL доступа",
"agentDetail.access.webApp.actions.accessControl": "Управление доступом",
"agentDetail.access.webApp.actions.customize": "Пользовательский фронтенд",
"agentDetail.access.webApp.actions.customize": "Настроить",
"agentDetail.access.webApp.actions.embedded": "Встроить",
"agentDetail.access.webApp.actions.launch": "Запустить",
"agentDetail.access.webApp.actions.settings": "Брендинг",
"agentDetail.access.webApp.actions.settings": "Настройки",
"agentDetail.access.webApp.refreshUrl": "Обновить URL доступа",
"agentDetail.access.webApp.showQrCode": "Показать QR-код",
"agentDetail.access.webApp.ssoEnabled": "SSO включено",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Расширенные настройки",
"agentDetail.configure.advancedSettings.toggle": "Переключить расширенные настройки",
"agentDetail.configure.answer.activity.ranCommands": "Команды выполнены",
"agentDetail.configure.answer.activity.runningCommands": "Выполнение команд",
"agentDetail.configure.answer.duration.minute": "{{count}} мин",
"agentDetail.configure.answer.duration.second": "{{count}} с",
"agentDetail.configure.answer.thinking": "Размышляет",
"agentDetail.configure.answer.workFinished": "Работа завершена",
"agentDetail.configure.answer.workedFor": "Работал {{duration}}",
"agentDetail.configure.answer.workingFor": "Работает {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Функции чата",
"agentDetail.configure.clearSessionConfirm.description": "Это очистит текущий сеанс и отменит непримененные изменения конфигурации Agent.",
"agentDetail.configure.clearSessionConfirm.title": "Очистить сеанс и отменить изменения?",
"agentDetail.configure.communityEditionIsolationTip": "Отказ от ответственности: В Community Edition песочница запускается от имени пользователя без прав root с конфигурацией возможностей Docker по умолчанию и обеспечивает лишь ограниченную защиту на основе Landlock для собственных файлов агента и файлов сеанса. Сервер и все дочерние процессы оболочки используют одно и то же пространство имен PID и общую границу возможностей на уровне контейнера, поэтому эту среду не следует считать надежно изолированной многоуровневой песочницей безопасности.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition не обеспечивает жесткую изоляцию файловой системы между конечными пользователями или запусками. Не предоставляйте один и тот же CE-агент нескольким независимым конечным пользователям, если требуется изоляция данных или строгое соответствие требованиям.",
"agentDetail.configure.files.add": "Добавить файл",
"agentDetail.configure.files.buildNote.generated": "Сгенерировано",
"agentDetail.configure.files.buildNote.richTooltip": "Запись агента о том, что он настроил в режиме Build. Он читает ее в начале каждого разговора вместе с вашим Prompt. <docLink>Подробнее</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Загрузите документы, которые может прочитать агент, например спецификации, шаблоны или руководства",
"agentDetail.configure.files.empty.title": "Пока нет файлов",
"agentDetail.configure.files.label": "Файлы",
"agentDetail.configure.files.missing": "Файл не найден",
"agentDetail.configure.files.preview.empty": "Нет содержимого для предпросмотра.",
"agentDetail.configure.files.preview.failed": "Не удалось загрузить предпросмотр.",
"agentDetail.configure.files.preview.unsupported": "Этот файл не поддерживает предварительный просмотр.",
@ -203,22 +198,14 @@
"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": "Переключить навыки",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Загрузите файл .zip или .skill.",
"agentDetail.configure.skills.upload.success": "Навык загружен.",
"agentDetail.configure.skills.upload.title": "Загрузить навык",
"agentDetail.configure.skills.upload.warning.files": "Если вам нужно использовать только файлы Markdown, загрузите их в раздел «Файлы» и укажите ссылки на них в своем промпте.",
"agentDetail.configure.skills.upload.warning.specification": "Загружаемые навыки должны соответствовать <specificationLink>спецификации Agent Skills</specificationLink>.",
"agentDetail.configure.title": "Настроить",
"agentDetail.configure.tools.add": "Добавить инструмент",
"agentDetail.configure.tools.addMenu.cliTool.badge": "Для разработчиков",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "Параметры сортировки",
"roster.sort.recentlyCreated": "Недавно созданные",
"roster.updateSuccess": "Агент обновлён.",
"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"
"roster.usageStatus.draft": "Черновик"
}

View File

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

View File

@ -14,11 +14,10 @@
"agentDetail.access.title": "Dostopna točka",
"agentDetail.access.toggleSurface": "Preklopi dostop {{name}}",
"agentDetail.access.webApp.accessUrl": "URL dostopa",
"agentDetail.access.webApp.actions.accessControl": "Nadzor dostopa",
"agentDetail.access.webApp.actions.customize": "Frontend po meri",
"agentDetail.access.webApp.actions.customize": "Prilagodi",
"agentDetail.access.webApp.actions.embedded": "Vgrajeno",
"agentDetail.access.webApp.actions.launch": "Zaženi",
"agentDetail.access.webApp.actions.settings": "Znamčenje",
"agentDetail.access.webApp.actions.settings": "Nastavitve",
"agentDetail.access.webApp.refreshUrl": "Osveži URL dostopa",
"agentDetail.access.webApp.showQrCode": "Pokaži QR kodo",
"agentDetail.access.webApp.ssoEnabled": "SSO omogočeno",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Napredne nastavitve",
"agentDetail.configure.advancedSettings.toggle": "Preklopi napredne nastavitve",
"agentDetail.configure.answer.activity.ranCommands": "Ukazi izvedeni",
"agentDetail.configure.answer.activity.runningCommands": "Izvajanje ukazov",
"agentDetail.configure.answer.duration.minute": "{{count}} min",
"agentDetail.configure.answer.duration.second": "{{count}} s",
"agentDetail.configure.answer.thinking": "Razmišlja",
"agentDetail.configure.answer.workFinished": "Delo končano",
"agentDetail.configure.answer.workedFor": "Delal {{duration}}",
"agentDetail.configure.answer.workingFor": "Dela {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Funkcije klepeta",
"agentDetail.configure.clearSessionConfirm.description": "To bo počistilo trenutno sejo in zavrglo še neuveljavljene spremembe konfiguracije Agent.",
"agentDetail.configure.clearSessionConfirm.title": "Počisti sejo in zavrzi spremembe?",
"agentDetail.configure.communityEditionIsolationTip": "Zavrnitev odgovornosti: V Community Edition se peskovnik izvaja kot uporabnik brez pravic root s privzeto konfiguracijo zmogljivosti Docker in zagotavlja le omejeno zaščito na osnovi Landlock za agentove lastne datoteke in datoteke seje. Strežnik in vsi podprocesi lupine si delijo isti imenski prostor PID in isto mejo zmogljivosti na ravni vsebnika, zato ga ne smemo obravnavati kot močno izoliran večplastni varnostni peskovnik.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition ne zagotavlja stroge izolacije datotečnega sistema med končnimi uporabniki ali zagoni. Istega agenta CE ne izpostavljajte več neodvisnim končnim uporabnikom, kadar sta potrebni izolacija podatkov ali stroga skladnost.",
"agentDetail.configure.files.add": "Dodaj datoteko",
"agentDetail.configure.files.buildNote.generated": "Ustvarjeno",
"agentDetail.configure.files.buildNote.richTooltip": "Agentov zapis tega, kar je nastavil v načinu Build. Prebere ga na začetku vsakega pogovora skupaj z vašim Promptom. <docLink>Več informacij</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Naložite dokumente, ki jih lahko agent bere, npr. specifikacije, predloge ali smernice",
"agentDetail.configure.files.empty.title": "Še ni datotek",
"agentDetail.configure.files.label": "Datoteke",
"agentDetail.configure.files.missing": "Datoteka ni bila najdena",
"agentDetail.configure.files.preview.empty": "Ni vsebine za predogled.",
"agentDetail.configure.files.preview.failed": "Predogleda ni bilo mogoče naložiti.",
"agentDetail.configure.files.preview.unsupported": "Ta datoteka ne podpira predogleda.",
@ -203,22 +198,14 @@
"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",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Naložite datoteko .zip ali .skill.",
"agentDetail.configure.skills.upload.success": "Veščina naložena.",
"agentDetail.configure.skills.upload.title": "Naloži veščino",
"agentDetail.configure.skills.upload.warning.files": "Če želite uporabljati samo datoteke Markdown, jih naložite v razdelek Datoteke in se nanje sklicujte v svojem pozivu.",
"agentDetail.configure.skills.upload.warning.specification": "Naložene veščine morajo upoštevati <specificationLink>specifikacijo Agent Skills</specificationLink>.",
"agentDetail.configure.title": "Konfiguriraj",
"agentDetail.configure.tools.add": "Dodaj orodje",
"agentDetail.configure.tools.addMenu.cliTool.badge": "Za razvijalce",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "Možnosti razvrščanja",
"roster.sort.recentlyCreated": "Nedavno ustvarjeno",
"roster.updateSuccess": "Agent posodobljen.",
"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"
"roster.usageStatus.draft": "Osnutek"
}

View File

@ -201,7 +201,6 @@
"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",
@ -487,7 +486,6 @@
"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

@ -14,11 +14,10 @@
"agentDetail.access.title": "จุดเข้าถึง",
"agentDetail.access.toggleSurface": "สลับการเข้าถึง {{name}}",
"agentDetail.access.webApp.accessUrl": "URL การเข้าถึง",
"agentDetail.access.webApp.actions.accessControl": "การควบคุมการเข้าถึง",
"agentDetail.access.webApp.actions.customize": "ฟรอนต์เอนด์ที่กำหนดเอง",
"agentDetail.access.webApp.actions.customize": "ปรับแต่ง",
"agentDetail.access.webApp.actions.embedded": "ฝัง",
"agentDetail.access.webApp.actions.launch": "เปิดใช้",
"agentDetail.access.webApp.actions.settings": "การสร้างแบรนด์",
"agentDetail.access.webApp.actions.settings": "ตั้งค่า",
"agentDetail.access.webApp.refreshUrl": "รีเฟรช URL การเข้าถึง",
"agentDetail.access.webApp.showQrCode": "แสดง QR Code",
"agentDetail.access.webApp.ssoEnabled": "เปิดใช้งาน SSO",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "การตั้งค่าขั้นสูง",
"agentDetail.configure.advancedSettings.toggle": "สลับการตั้งค่าขั้นสูง",
"agentDetail.configure.answer.activity.ranCommands": "เรียกใช้คำสั่งแล้ว",
"agentDetail.configure.answer.activity.runningCommands": "กำลังเรียกใช้คำสั่ง",
"agentDetail.configure.answer.duration.minute": "{{count}} นาที",
"agentDetail.configure.answer.duration.second": "{{count}} วินาที",
"agentDetail.configure.answer.thinking": "กำลังคิด",
"agentDetail.configure.answer.workFinished": "งานเสร็จสิ้น",
"agentDetail.configure.answer.workedFor": "ทำงานเป็นเวลา {{duration}}",
"agentDetail.configure.answer.workingFor": "กำลังทำงานเป็นเวลา {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "ฟีเจอร์แชท",
"agentDetail.configure.clearSessionConfirm.description": "การดำเนินการนี้จะล้างเซสชันปัจจุบันและทิ้งการเปลี่ยนแปลงการกำหนดค่าของ Agent ที่ยังไม่ได้ใช้",
"agentDetail.configure.clearSessionConfirm.title": "ล้างเซสชันและทิ้งการเปลี่ยนแปลงหรือไม่?",
"agentDetail.configure.communityEditionIsolationTip": "ข้อจำกัดความรับผิดชอบ: ใน Community Edition แซนด์บ็อกซ์ทำงานในฐานะผู้ใช้ที่ไม่ใช่ root ภายใต้การกำหนดค่า capability เริ่มต้นของ Docker และให้การป้องกันแบบ Landlock ที่จำกัดเฉพาะไฟล์ของเอเจนต์เองและไฟล์เซสชันเท่านั้น เซิร์ฟเวอร์และโพรเซสย่อยของ shell ทั้งหมดใช้ PID namespace และขอบเขต capability ระดับคอนเทนเนอร์ร่วมกัน ดังนั้นจึงไม่ควรถือว่าเป็นแซนด์บ็อกซ์ความปลอดภัยหลายชั้นที่แยกอย่างเข้มแข็ง",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition ไม่มีการแยกระบบไฟล์อย่างเข้มงวดระหว่างผู้ใช้ปลายทางหรือระหว่างการรัน อย่าเปิดเผยเอเจนต์ CE ตัวเดียวกันให้กับผู้ใช้ปลายทางอิสระหลายรายเมื่อจำเป็นต้องมีการแยกข้อมูลหรือการปฏิบัติตามข้อกำหนดอย่างเข้มงวด",
"agentDetail.configure.files.add": "เพิ่มไฟล์",
"agentDetail.configure.files.buildNote.generated": "สร้างแล้ว",
"agentDetail.configure.files.buildNote.richTooltip": "บันทึกของ agent เกี่ยวกับสิ่งที่ตั้งค่าไว้ในโหมด Build โดยจะอ่านสิ่งนี้ตอนเริ่มทุกบทสนทนา พร้อมกับ Prompt ของคุณ <docLink>เรียนรู้เพิ่มเติม</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "อัปโหลดเอกสารที่ตัวแทนสามารถอ่านได้ เช่น ข้อกำหนด เทมเพลต หรือแนวทาง",
"agentDetail.configure.files.empty.title": "ยังไม่มีไฟล์",
"agentDetail.configure.files.label": "ไฟล์",
"agentDetail.configure.files.missing": "ไม่พบไฟล์",
"agentDetail.configure.files.preview.empty": "ไม่มีเนื้อหาสำหรับแสดงตัวอย่าง",
"agentDetail.configure.files.preview.failed": "โหลดตัวอย่างไม่สำเร็จ",
"agentDetail.configure.files.preview.unsupported": "ไฟล์นี้ไม่รองรับการแสดงตัวอย่าง",
@ -203,22 +198,14 @@
"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": "สลับทักษะ",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "อัปโหลดไฟล์ .zip หรือ .skill",
"agentDetail.configure.skills.upload.success": "อัปโหลดทักษะแล้ว",
"agentDetail.configure.skills.upload.title": "อัปโหลดทักษะ",
"agentDetail.configure.skills.upload.warning.files": "หากต้องการใช้เฉพาะไฟล์ Markdown ให้อัปโหลดไปยังส่วนไฟล์และอ้างอิงไฟล์เหล่านั้นในพรอมต์ของคุณ",
"agentDetail.configure.skills.upload.warning.specification": "ทักษะที่อัปโหลดต้องเป็นไปตาม<specificationLink>ข้อกำหนด Agent Skills</specificationLink>",
"agentDetail.configure.title": "กำหนดค่า",
"agentDetail.configure.tools.add": "เพิ่มเครื่องมือ",
"agentDetail.configure.tools.addMenu.cliTool.badge": "สำหรับนักพัฒนา",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "ตัวเลือกการเรียง",
"roster.sort.recentlyCreated": "สร้างล่าสุด",
"roster.updateSuccess": "อัปเดตตัวแทนแล้ว",
"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"
"roster.usageStatus.draft": "ฉบับร่าง"
}

View File

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

View File

@ -14,11 +14,10 @@
"agentDetail.access.title": "Erişim Noktası",
"agentDetail.access.toggleSurface": "{{name}} erişimini değiştir",
"agentDetail.access.webApp.accessUrl": "Erişim URL'si",
"agentDetail.access.webApp.actions.accessControl": "Erişim Kontrolü",
"agentDetail.access.webApp.actions.customize": "Özel Ön Yüz",
"agentDetail.access.webApp.actions.customize": "Özelleştir",
"agentDetail.access.webApp.actions.embedded": "Gömülü",
"agentDetail.access.webApp.actions.launch": "Başlat",
"agentDetail.access.webApp.actions.settings": "Markalama",
"agentDetail.access.webApp.actions.settings": "Ayarlar",
"agentDetail.access.webApp.refreshUrl": "Erişim URL'sini yenile",
"agentDetail.access.webApp.showQrCode": "QR kodunu göster",
"agentDetail.access.webApp.ssoEnabled": "SSO Etkin",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Gelişmiş Ayarlar",
"agentDetail.configure.advancedSettings.toggle": "Gelişmiş ayarları değiştir",
"agentDetail.configure.answer.activity.ranCommands": "Komutlar çalıştırıldı",
"agentDetail.configure.answer.activity.runningCommands": "Komutlar çalıştırılıyor",
"agentDetail.configure.answer.duration.minute": "{{count}} dk",
"agentDetail.configure.answer.duration.second": "{{count}} sn",
"agentDetail.configure.answer.thinking": "Düşünüyor",
"agentDetail.configure.answer.workFinished": "İş tamamlandı",
"agentDetail.configure.answer.workedFor": "{{duration}} çalıştı",
"agentDetail.configure.answer.workingFor": "{{duration}} çalışıyor",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Sohbet Özellikleri",
"agentDetail.configure.clearSessionConfirm.description": "Bu işlem geçerli oturumu temizler ve henüz uygulanmamış Agent yapılandırma değişikliklerini vazgeçer.",
"agentDetail.configure.clearSessionConfirm.title": "Oturumu temizleyip değişikliklerden vazgeçilsin mi?",
"agentDetail.configure.communityEditionIsolationTip": "Sorumluluk reddi: Community Edition'da sandbox, Docker'ın varsayılan capability yapılandırması altında root olmayan bir kullanıcı olarak çalışır ve yalnızca ajanın kendi dosyaları ile oturum dosyaları için sınırlı, Landlock tabanlı koruma sağlar. Sunucu ve tüm shell alt süreçleri aynı PID namespace'ini ve container düzeyindeki capability sınırını paylaşır; bu nedenle güçlü biçimde yalıtılmış, çok katmanlı bir güvenlik sandbox'ı olarak değerlendirilmemelidir.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition, son kullanıcılar veya çalıştırmalar arasında katı dosya sistemi yalıtımı sağlamaz. Veri yalıtımı veya sıkı uyumluluk gerektiğinde aynı CE ajanını birden fazla bağımsız son kullanıcıya açmayın.",
"agentDetail.configure.files.add": "Dosya ekle",
"agentDetail.configure.files.buildNote.generated": "Oluşturuldu",
"agentDetail.configure.files.buildNote.richTooltip": "Agent'ın Build mode'da kurduğu şeylerin kaydı. Her konuşmanın başında bunu Prompt'unuzla birlikte okur. <docLink>Daha fazla bilgi</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Ajanın okuyabileceği belgeleri yükleyin, örneğin spesifikasyonlar, şablonlar veya yönergeler",
"agentDetail.configure.files.empty.title": "Henüz dosya yok",
"agentDetail.configure.files.label": "Dosyalar",
"agentDetail.configure.files.missing": "Dosya bulunamadı",
"agentDetail.configure.files.preview.empty": "Önizleme içeriği yok.",
"agentDetail.configure.files.preview.failed": "Önizleme yüklenemedi.",
"agentDetail.configure.files.preview.unsupported": "Bu dosya önizlemeyi desteklemiyor.",
@ -203,22 +198,14 @@
"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",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Bir .zip veya .skill dosyası yükleyin.",
"agentDetail.configure.skills.upload.success": "Beceri yüklendi.",
"agentDetail.configure.skills.upload.title": "Beceri yükle",
"agentDetail.configure.skills.upload.warning.files": "Yalnızca Markdown dosyalarını kullanmanız gerekiyorsa bunları Dosyalar bölümüne yükleyin ve isteminizde referans verin.",
"agentDetail.configure.skills.upload.warning.specification": "Yüklenen beceriler <specificationLink>Agent Skills spesifikasyonuna</specificationLink> uygun olmalıdır.",
"agentDetail.configure.title": "Yapılandır",
"agentDetail.configure.tools.add": "Araç ekle",
"agentDetail.configure.tools.addMenu.cliTool.badge": "Geliştiriciler için",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "Sıralama seçenekleri",
"roster.sort.recentlyCreated": "Yeni oluşturulanlar",
"roster.updateSuccess": "Ajan güncellendi.",
"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"
"roster.usageStatus.draft": "Taslak"
}

View File

@ -201,7 +201,6 @@
"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",
@ -487,7 +486,6 @@
"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

@ -14,11 +14,10 @@
"agentDetail.access.title": "Точка доступу",
"agentDetail.access.toggleSurface": "Перемкнути доступ {{name}}",
"agentDetail.access.webApp.accessUrl": "URL доступу",
"agentDetail.access.webApp.actions.accessControl": "Контроль доступу",
"agentDetail.access.webApp.actions.customize": "Користувацький фронтенд",
"agentDetail.access.webApp.actions.customize": "Налаштувати",
"agentDetail.access.webApp.actions.embedded": "Вбудувати",
"agentDetail.access.webApp.actions.launch": "Запустити",
"agentDetail.access.webApp.actions.settings": "Брендинг",
"agentDetail.access.webApp.actions.settings": "Налаштування",
"agentDetail.access.webApp.refreshUrl": "Оновити URL доступу",
"agentDetail.access.webApp.showQrCode": "Показати QR-код",
"agentDetail.access.webApp.ssoEnabled": "SSO увімкнено",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Розширені налаштування",
"agentDetail.configure.advancedSettings.toggle": "Перемкнути розширені налаштування",
"agentDetail.configure.answer.activity.ranCommands": "Команди виконано",
"agentDetail.configure.answer.activity.runningCommands": "Виконання команд",
"agentDetail.configure.answer.duration.minute": "{{count}} хв",
"agentDetail.configure.answer.duration.second": "{{count}} с",
"agentDetail.configure.answer.thinking": "Розмірковує",
"agentDetail.configure.answer.workFinished": "Роботу завершено",
"agentDetail.configure.answer.workedFor": "Працював {{duration}}",
"agentDetail.configure.answer.workingFor": "Працює {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Функції чату",
"agentDetail.configure.clearSessionConfirm.description": "Ця дія очистить поточний сеанс і відхилить незастосовані зміни конфігурації Agent.",
"agentDetail.configure.clearSessionConfirm.title": "Очистити сеанс і відхилити зміни?",
"agentDetail.configure.communityEditionIsolationTip": "Відмова від відповідальності: У Community Edition пісочниця запускається від імені користувача без прав root із конфігурацією можливостей Docker за замовчуванням і забезпечує лише обмежений захист на основі Landlock для власних файлів агента та файлів сеансу. Сервер і всі дочірні процеси оболонки використовують один простір імен PID та спільну межу можливостей на рівні контейнера, тому цю пісочницю не слід вважати надійно ізольованим багаторівневим середовищем безпеки.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition не забезпечує жорсткої ізоляції файлової системи між кінцевими користувачами або запусками. Не надавайте один і той самий CE-агент кільком незалежним кінцевим користувачам, якщо потрібна ізоляція даних або сувора відповідність вимогам.",
"agentDetail.configure.files.add": "Додати файл",
"agentDetail.configure.files.buildNote.generated": "Згенеровано",
"agentDetail.configure.files.buildNote.richTooltip": "Запис агента про те, що він налаштував у режимі Build. Він читає його на початку кожної розмови разом із вашим Prompt. <docLink>Докладніше</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Завантажте документи, які може читати агент, наприклад специфікації, шаблони чи інструкції",
"agentDetail.configure.files.empty.title": "Файлів ще немає",
"agentDetail.configure.files.label": "Файли",
"agentDetail.configure.files.missing": "Файл не знайдено",
"agentDetail.configure.files.preview.empty": "Немає вмісту для перегляду.",
"agentDetail.configure.files.preview.failed": "Не вдалося завантажити перегляд.",
"agentDetail.configure.files.preview.unsupported": "Попередній перегляд цього файлу не підтримується.",
@ -203,22 +198,14 @@
"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": "Перемкнути навички",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Завантажте файл .zip або .skill.",
"agentDetail.configure.skills.upload.success": "Навичку завантажено.",
"agentDetail.configure.skills.upload.title": "Завантажити навичку",
"agentDetail.configure.skills.upload.warning.files": "Якщо потрібно використовувати лише файли Markdown, завантажте їх у розділ «Файли» та посилайтеся на них у своєму промпті.",
"agentDetail.configure.skills.upload.warning.specification": "Завантажувані навички мають відповідати <specificationLink>специфікації Agent Skills</specificationLink>.",
"agentDetail.configure.title": "Налаштувати",
"agentDetail.configure.tools.add": "Додати інструмент",
"agentDetail.configure.tools.addMenu.cliTool.badge": "Для розробників",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "Параметри сортування",
"roster.sort.recentlyCreated": "Нещодавно створені",
"roster.updateSuccess": "Агента оновлено.",
"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"
"roster.usageStatus.draft": "Чернетка"
}

View File

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

View File

@ -14,11 +14,10 @@
"agentDetail.access.title": "Điểm truy cập",
"agentDetail.access.toggleSurface": "Bật/tắt truy cập {{name}}",
"agentDetail.access.webApp.accessUrl": "URL truy cập",
"agentDetail.access.webApp.actions.accessControl": "Kiểm soát truy cập",
"agentDetail.access.webApp.actions.customize": "Giao diện tùy chỉnh",
"agentDetail.access.webApp.actions.customize": "Tùy chỉnh",
"agentDetail.access.webApp.actions.embedded": "Nhúng",
"agentDetail.access.webApp.actions.launch": "Khởi chạy",
"agentDetail.access.webApp.actions.settings": "Thương hiệu",
"agentDetail.access.webApp.actions.settings": "Cài đặt",
"agentDetail.access.webApp.refreshUrl": "Làm mới URL truy cập",
"agentDetail.access.webApp.showQrCode": "Hiển thị mã QR",
"agentDetail.access.webApp.ssoEnabled": "SSO đã bật",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "Cài đặt nâng cao",
"agentDetail.configure.advancedSettings.toggle": "Bật/tắt cài đặt nâng cao",
"agentDetail.configure.answer.activity.ranCommands": "Đã chạy lệnh",
"agentDetail.configure.answer.activity.runningCommands": "Đang chạy lệnh",
"agentDetail.configure.answer.duration.minute": "{{count}} phút",
"agentDetail.configure.answer.duration.second": "{{count}} giây",
"agentDetail.configure.answer.thinking": "Đang suy nghĩ",
"agentDetail.configure.answer.workFinished": "Công việc đã hoàn tất",
"agentDetail.configure.answer.workedFor": "Đã làm việc trong {{duration}}",
"agentDetail.configure.answer.workingFor": "Đang làm việc trong {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Tính năng trò chuyện",
"agentDetail.configure.clearSessionConfirm.description": "Thao tác này sẽ xóa phiên hiện tại và hủy các thay đổi cấu hình Agent chưa được áp dụng.",
"agentDetail.configure.clearSessionConfirm.title": "Xóa phiên và hủy thay đổi?",
"agentDetail.configure.communityEditionIsolationTip": "Tuyên bố miễn trừ trách nhiệm: Trong Community Edition, sandbox chạy dưới dạng người dùng không phải root với cấu hình capability mặc định của Docker và chỉ cung cấp khả năng bảo vệ hạn chế dựa trên Landlock cho các tệp riêng của agent và tệp phiên. Máy chủ và tất cả tiến trình con của shell dùng chung PID namespace và ranh giới capability cấp container, vì vậy không nên xem đây là sandbox bảo mật nhiều lớp được cách ly mạnh.",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition không cung cấp cách ly hệ thống tệp cứng giữa người dùng cuối hoặc giữa các lần chạy. Không cung cấp cùng một tác nhân CE cho nhiều người dùng cuối độc lập khi cần cách ly dữ liệu hoặc tuân thủ nghiêm ngặt.",
"agentDetail.configure.files.add": "Thêm tệp",
"agentDetail.configure.files.buildNote.generated": "Đã tạo",
"agentDetail.configure.files.buildNote.richTooltip": "Bản ghi của tác nhân về những gì nó đã thiết lập trong chế độ Build. Nó đọc bản ghi này ở đầu mỗi cuộc trò chuyện, cùng với Prompt của bạn. <docLink>Tìm hiểu thêm</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "Tải lên tài liệu mà tác nhân có thể đọc, như đặc tả, mẫu hoặc hướng dẫn",
"agentDetail.configure.files.empty.title": "Chưa có tệp nào",
"agentDetail.configure.files.label": "Tệp",
"agentDetail.configure.files.missing": "Không tìm thấy tệp",
"agentDetail.configure.files.preview.empty": "Không có nội dung xem trước.",
"agentDetail.configure.files.preview.failed": "Tải xem trước thất bại.",
"agentDetail.configure.files.preview.unsupported": "Tệp này không hỗ trợ xem trước.",
@ -203,22 +198,14 @@
"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",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "Tải lên một tệp .zip hoặc .skill.",
"agentDetail.configure.skills.upload.success": "Đã tải lên kỹ năng.",
"agentDetail.configure.skills.upload.title": "Tải lên kỹ năng",
"agentDetail.configure.skills.upload.warning.files": "Nếu bạn chỉ cần sử dụng tệp Markdown, hãy tải chúng lên Tệp và tham chiếu chúng trong prompt của bạn.",
"agentDetail.configure.skills.upload.warning.specification": "Kỹ năng được tải lên phải tuân theo <specificationLink>đặc tả Agent Skills</specificationLink>.",
"agentDetail.configure.title": "Cấu hình",
"agentDetail.configure.tools.add": "Thêm công cụ",
"agentDetail.configure.tools.addMenu.cliTool.badge": "Dành cho nhà phát triển",
@ -426,117 +411,5 @@
"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",
"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"
"roster.usageStatus.draft": "Bản nháp"
}

View File

@ -201,7 +201,6 @@
"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",
@ -487,7 +486,6 @@
"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

@ -14,11 +14,10 @@
"agentDetail.access.title": "访问点",
"agentDetail.access.toggleSurface": "切换 {{name}} 访问状态",
"agentDetail.access.webApp.accessUrl": "访问 URL",
"agentDetail.access.webApp.actions.accessControl": "访问控制",
"agentDetail.access.webApp.actions.customize": "自定义前端",
"agentDetail.access.webApp.actions.customize": "自定义",
"agentDetail.access.webApp.actions.embedded": "嵌入",
"agentDetail.access.webApp.actions.launch": "启动",
"agentDetail.access.webApp.actions.settings": "品牌设置",
"agentDetail.access.webApp.actions.settings": "设置",
"agentDetail.access.webApp.refreshUrl": "刷新访问 URL",
"agentDetail.access.webApp.showQrCode": "显示二维码",
"agentDetail.access.webApp.ssoEnabled": "已启用 SSO",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "高级设置",
"agentDetail.configure.advancedSettings.toggle": "展开或收起高级设置",
"agentDetail.configure.answer.activity.ranCommands": "已运行命令",
"agentDetail.configure.answer.activity.runningCommands": "正在运行命令",
"agentDetail.configure.answer.duration.minute": "{{count}}分",
"agentDetail.configure.answer.duration.second": "{{count}}秒",
"agentDetail.configure.answer.thinking": "思考过程",
"agentDetail.configure.answer.workFinished": "工作已完成",
"agentDetail.configure.answer.workedFor": "工作了 {{duration}}",
"agentDetail.configure.answer.workingFor": "工作中 {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Chat 功能",
"agentDetail.configure.clearSessionConfirm.description": "这将清空当前会话,并丢弃尚未应用的 Agent 配置更改。",
"agentDetail.configure.clearSessionConfirm.title": "清空会话并放弃改动?",
"agentDetail.configure.communityEditionIsolationTip": "免责声明:在 Community Edition 中sandbox 以非 root 用户身份运行,采用 Docker 默认 capability 配置,并且只通过 Landlock 为 Agent 自有文件和会话文件提供有限保护。服务器和所有 shell 子进程共享同一个 PID namespace 和容器级 capability 边界,因此不应将其视为具备强隔离能力的多层安全 sandbox。",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition 不在最终用户之间或不同运行之间提供严格的文件系统隔离。如果需要数据隔离或严格合规,请勿将同一个 CE Agent 暴露给多个相互独立的最终用户。",
"agentDetail.configure.files.add": "添加文件",
"agentDetail.configure.files.buildNote.generated": "已生成",
"agentDetail.configure.files.buildNote.richTooltip": "Agent 在构建模式中完成的设置都记录在这里。每次对话开始时,它会连同提示词一起读取这份记录。<docLink>了解更多</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "上传 Agent 可读取的文档,例如规格、模板或指南",
"agentDetail.configure.files.empty.title": "暂无文件",
"agentDetail.configure.files.label": "文件",
"agentDetail.configure.files.missing": "未找到文件",
"agentDetail.configure.files.preview.empty": "暂无预览内容。",
"agentDetail.configure.files.preview.failed": "预览加载失败。",
"agentDetail.configure.files.preview.unsupported": "该文件不支持预览。",
@ -203,24 +198,14 @@
"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",
@ -233,17 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "请上传 .zip 或 .skill 文件。",
"agentDetail.configure.skills.upload.success": "Skill 已上传。",
"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": "开发者适用",
@ -437,178 +411,5 @@
"roster.sort.optionsLabel": "排序选项",
"roster.sort.recentlyCreated": "最近创建",
"roster.updateSuccess": "Agent 已更新。",
"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"
"roster.usageStatus.draft": "草稿"
}

View File

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

View File

@ -14,11 +14,10 @@
"agentDetail.access.title": "存取點",
"agentDetail.access.toggleSurface": "切換 {{name}} 存取狀態",
"agentDetail.access.webApp.accessUrl": "存取 URL",
"agentDetail.access.webApp.actions.accessControl": "存取控制",
"agentDetail.access.webApp.actions.customize": "自訂前端",
"agentDetail.access.webApp.actions.customize": "自訂",
"agentDetail.access.webApp.actions.embedded": "嵌入",
"agentDetail.access.webApp.actions.launch": "啟動",
"agentDetail.access.webApp.actions.settings": "品牌設定",
"agentDetail.access.webApp.actions.settings": "設定",
"agentDetail.access.webApp.refreshUrl": "重新整理存取 URL",
"agentDetail.access.webApp.showQrCode": "顯示 QR 碼",
"agentDetail.access.webApp.ssoEnabled": "已啟用 SSO",
@ -64,11 +63,8 @@
"agentDetail.configure.advancedSettings.envEditor.valuePlaceholder": "Value",
"agentDetail.configure.advancedSettings.label": "進階設定",
"agentDetail.configure.advancedSettings.toggle": "展開或收合進階設定",
"agentDetail.configure.answer.activity.ranCommands": "已執行命令",
"agentDetail.configure.answer.activity.runningCommands": "正在執行命令",
"agentDetail.configure.answer.duration.minute": "{{count}}分",
"agentDetail.configure.answer.duration.second": "{{count}}秒",
"agentDetail.configure.answer.thinking": "思考過程",
"agentDetail.configure.answer.workFinished": "工作已完成",
"agentDetail.configure.answer.workedFor": "工作了 {{duration}}",
"agentDetail.configure.answer.workingFor": "工作中 {{duration}}",
@ -90,7 +86,7 @@
"agentDetail.configure.chatFeatures.title": "Chat 功能",
"agentDetail.configure.clearSessionConfirm.description": "這會清空目前會話,並丟棄尚未套用的 Agent 設定變更。",
"agentDetail.configure.clearSessionConfirm.title": "清空會話並放棄變更?",
"agentDetail.configure.communityEditionIsolationTip": "免責聲明:在 Community Edition 中sandbox 以非 root 使用者身分執行,採用 Docker 預設 capability 設定,並且只透過 Landlock 為 Agent 自有檔案和工作階段檔案提供有限保護。伺服器和所有 shell 子程序共用同一個 PID namespace 和容器層級 capability 邊界,因此不應將其視為具備強隔離能力的多層安全 sandbox。",
"agentDetail.configure.communityEditionIsolationTip": "Community Edition 不會在最終使用者之間或不同執行之間提供嚴格的檔案系統隔離。如果需要資料隔離或嚴格合規,請勿將同一個 CE Agent 暴露給多個相互獨立的最終使用者。",
"agentDetail.configure.files.add": "新增檔案",
"agentDetail.configure.files.buildNote.generated": "已生成",
"agentDetail.configure.files.buildNote.richTooltip": "Agent 在建置模式中完成的設定都記錄在這裡。每次對話開始時,它會連同提示詞一起讀取這份記錄。<docLink>了解更多</docLink>",
@ -99,7 +95,6 @@
"agentDetail.configure.files.empty.description": "上傳 Agent 可讀取的文件,例如規格、範本或指南",
"agentDetail.configure.files.empty.title": "暫無檔案",
"agentDetail.configure.files.label": "檔案",
"agentDetail.configure.files.missing": "找不到檔案",
"agentDetail.configure.files.preview.empty": "暫無預覽內容。",
"agentDetail.configure.files.preview.failed": "預覽載入失敗。",
"agentDetail.configure.files.preview.unsupported": "此檔案不支援預覽。",
@ -203,22 +198,14 @@
"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",
@ -231,8 +218,6 @@
"agentDetail.configure.skills.upload.invalidFile": "請上傳 .zip 或 .skill 檔案。",
"agentDetail.configure.skills.upload.success": "Skill 已上傳。",
"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.title": "設定",
"agentDetail.configure.tools.add": "新增工具",
"agentDetail.configure.tools.addMenu.cliTool.badge": "開發者適用",
@ -426,117 +411,5 @@
"roster.sort.optionsLabel": "排序選項",
"roster.sort.recentlyCreated": "最近建立",
"roster.updateSuccess": "Agent 已更新。",
"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"
"roster.usageStatus.draft": "草稿"
}

View File

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