diff --git a/apps/web/src/routes/agent/lifecycle/kind-fork-dialog.tsx b/apps/web/src/routes/agent/lifecycle/kind-fork-dialog.tsx index b8a9efdb..252d03cc 100644 --- a/apps/web/src/routes/agent/lifecycle/kind-fork-dialog.tsx +++ b/apps/web/src/routes/agent/lifecycle/kind-fork-dialog.tsx @@ -13,14 +13,9 @@ import { import type { AgentKind } from "../agent.types"; -const KIND_LABELS: Record = { - pet: { title: "Assistant Agent", icon: Bot }, - cattle: { title: "Task Agent", icon: Zap }, -}; - -const KIND_TAGLINE_KEYS: Record = { - pet: "agentLifecycle.kindPetTagline", - cattle: "agentLifecycle.kindCattleTagline", +const KIND_COPY: Record = { + pet: { titleKey: "agent.assistantAgent", taglineKey: "agent.kindAssistantTagline", icon: Bot }, + cattle: { titleKey: "agent.taskAgent", taglineKey: "agent.kindTaskTagline", icon: Zap }, }; const CARRIED_OVER = [ @@ -60,12 +55,14 @@ export function KindForkDialog({ onConfirm: () => void; }) { const { t } = useTranslation(); - const current = KIND_LABELS[currentKind]; - const target = KIND_LABELS[targetKind]; + const current = KIND_COPY[currentKind]; + const target = KIND_COPY[targetKind]; const CurrentIcon = current.icon; const TargetIcon = target.icon; - const currentTagline = t(KIND_TAGLINE_KEYS[currentKind]); - const targetTagline = t(KIND_TAGLINE_KEYS[targetKind]); + const currentTitle = t(current.titleKey); + const targetTitle = t(target.titleKey); + const currentTagline = t(current.taglineKey); + const targetTagline = t(target.taglineKey); const isPetToCattle = currentKind === "pet" && targetKind === "cattle"; const dropped = isPetToCattle ? DROPPED_PET_TO_CATTLE.map((key) => t(key)) : []; const added = !isPetToCattle ? ADDED_CATTLE_TO_PET.map((key) => t(key)) : []; @@ -76,15 +73,15 @@ export function KindForkDialog({ {t("agentLifecycle.forkTitle")} - {t("agentLifecycle.forkDescription", { agentName, target: target.title })} + {t("agentLifecycle.forkDescription", { agentName, target: targetTitle })}
- + - +
@@ -127,9 +124,7 @@ export function KindForkDialog({ {t("common.cancel")} diff --git a/apps/web/src/shared/i18n/translations/en.json b/apps/web/src/shared/i18n/translations/en.json index bc022b3a..d3eaa0a8 100644 --- a/apps/web/src/shared/i18n/translations/en.json +++ b/apps/web/src/shared/i18n/translations/en.json @@ -1114,6 +1114,7 @@ "apiTokenSettingsLocation": "Create tokens in Settings → Access tokens.", "applyChanges": "Apply changes", "applying": "Applying…", + "assistantSandboxState": "Assistant sandbox state", "checkpointedPathsRestored": "checkpointed paths restored", "copiedToClipboard": "Copied to clipboard", "copyInstructionDescription": "Copy instructions for this Agent.", @@ -1125,12 +1126,18 @@ "fieldsEdited": "{{count}} fields edited", "fieldsInChange": "Fields in this change", "forkAdded": "Added to fork", + "forkAddedSandbox": "Dedicated stable sandbox", "forkAs": "Fork as {{kind}}", + "forkCarriedEnvVars": "Environment variables", + "forkCarriedManifestFields": "Name, description, model, and system prompt", + "forkCarriedMcpBindings": "MCP server bindings", "forkCarriedOver": "Carried over", "forkDescription": "Create a new Agent with this type.", "forkDropped": "Dropped", "forkNotWiredNotice": "Fork action is not wired yet.", "forkStaysHere": "Stays on this Agent", + "forkStaysRuntimeLogs": "Runtime logs", + "forkStaysSessions": "Sessions and transcripts", "forkTitle": "Change Agent type", "forking": "Forking…", "kindHintAssistant": "Assistant Agent", diff --git a/apps/web/src/shared/i18n/translations/ja.json b/apps/web/src/shared/i18n/translations/ja.json index 145c8677..acb068a2 100644 --- a/apps/web/src/shared/i18n/translations/ja.json +++ b/apps/web/src/shared/i18n/translations/ja.json @@ -1114,6 +1114,7 @@ "apiTokenSettingsLocation": "設定 → アクセストークンでトークンを作成します。", "applyChanges": "変更を適用", "applying": "適用中…", + "assistantSandboxState": "アシスタントのサンドボックス状態", "checkpointedPathsRestored": "チェックポイントのパスを復元", "copiedToClipboard": "クリップボードにコピーしました", "copyInstructionDescription": "このエージェントの手順をコピーします。", @@ -1125,12 +1126,18 @@ "fieldsEdited": "{{count}} 個のフィールドを編集", "fieldsInChange": "この変更のフィールド", "forkAdded": "フォークに追加", + "forkAddedSandbox": "専用の安定サンドボックス", "forkAs": "{{kind}} としてフォーク", + "forkCarriedEnvVars": "環境変数", + "forkCarriedManifestFields": "名前、説明、モデル、システムプロンプト", + "forkCarriedMcpBindings": "MCP サーバーのバインディング", "forkCarriedOver": "引き継ぎ", "forkDescription": "このタイプで新しいエージェントを作成します。", "forkDropped": "破棄", "forkNotWiredNotice": "フォーク操作はまだ接続されていません。", "forkStaysHere": "このエージェントに残る", + "forkStaysRuntimeLogs": "ランタイムログ", + "forkStaysSessions": "セッションとトランスクリプト", "forkTitle": "エージェントタイプを変更", "forking": "フォーク中…", "kindHintAssistant": "アシスタントエージェント", diff --git a/apps/web/src/shared/i18n/translations/zh-CN.json b/apps/web/src/shared/i18n/translations/zh-CN.json index dea51819..be6e2b93 100644 --- a/apps/web/src/shared/i18n/translations/zh-CN.json +++ b/apps/web/src/shared/i18n/translations/zh-CN.json @@ -1114,6 +1114,7 @@ "apiTokenSettingsLocation": "在设置 → 访问令牌中创建令牌。", "applyChanges": "应用更改", "applying": "应用中…", + "assistantSandboxState": "助理沙箱状态", "checkpointedPathsRestored": "已恢复检查点路径", "copiedToClipboard": "已复制到剪贴板", "copyInstructionDescription": "复制此智能体的使用说明。", @@ -1125,12 +1126,18 @@ "fieldsEdited": "已编辑 {{count}} 个字段", "fieldsInChange": "此次更改中的字段", "forkAdded": "已添加到派生智能体", + "forkAddedSandbox": "专属稳定沙箱", "forkAs": "派生为 {{kind}}", + "forkCarriedEnvVars": "环境变量", + "forkCarriedManifestFields": "名称、描述、模型和系统提示词", + "forkCarriedMcpBindings": "MCP 服务器绑定", "forkCarriedOver": "已保留", "forkDescription": "使用此类型创建新的智能体。", "forkDropped": "已丢弃", "forkNotWiredNotice": "派生操作尚未接入。", "forkStaysHere": "保留在此智能体", + "forkStaysRuntimeLogs": "运行时日志", + "forkStaysSessions": "会话和记录", "forkTitle": "更改智能体类型", "forking": "派生中…", "kindHintAssistant": "助手智能体", diff --git a/apps/web/src/shared/i18n/translations/zh-TW.json b/apps/web/src/shared/i18n/translations/zh-TW.json index 134a3a05..b62cb82d 100644 --- a/apps/web/src/shared/i18n/translations/zh-TW.json +++ b/apps/web/src/shared/i18n/translations/zh-TW.json @@ -1114,6 +1114,7 @@ "apiTokenSettingsLocation": "在設定 → 存取權杖中建立權杖。", "applyChanges": "套用變更", "applying": "套用中…", + "assistantSandboxState": "助理沙箱狀態", "checkpointedPathsRestored": "已還原檢查點路徑", "copiedToClipboard": "已複製到剪貼簿", "copyInstructionDescription": "複製此智慧體的使用說明。", @@ -1125,12 +1126,18 @@ "fieldsEdited": "已編輯 {{count}} 個欄位", "fieldsInChange": "此變更中的欄位", "forkAdded": "已新增至分支智慧體", + "forkAddedSandbox": "專屬穩定沙箱", "forkAs": "分支為 {{kind}}", + "forkCarriedEnvVars": "環境變數", + "forkCarriedManifestFields": "名稱、描述、模型和系統提示", + "forkCarriedMcpBindings": "MCP 伺服器綁定", "forkCarriedOver": "已沿用", "forkDescription": "使用此類型建立新的智慧體。", "forkDropped": "已捨棄", "forkNotWiredNotice": "分支操作尚未接入。", "forkStaysHere": "保留在此智慧體", + "forkStaysRuntimeLogs": "執行日誌", + "forkStaysSessions": "工作階段和對話紀錄", "forkTitle": "變更智慧體類型", "forking": "分支中…", "kindHintAssistant": "助理智慧體", diff --git a/apps/web/src/shared/ui/dialog.tsx b/apps/web/src/shared/ui/dialog.tsx index ef50a22d..b9c9f65a 100644 --- a/apps/web/src/shared/ui/dialog.tsx +++ b/apps/web/src/shared/ui/dialog.tsx @@ -42,7 +42,10 @@ function DialogContent({ { } }); + test("resolves every Agent kind fork dialog key in every locale", () => { + const source = readSource("../src/routes/agent/lifecycle/kind-fork-dialog.tsx"); + const referencedKeys = [ + ...source.matchAll(/"((?:agent|agentLifecycle|environments|common)\.[A-Za-z0-9]+)"/gu), + ].map((match) => match[1] ?? ""); + + expect(referencedKeys.length).toBeGreaterThan(0); + + for (const catalog of [en, zhCN, zhTW, ja]) { + for (const key of referencedKeys) { + const value = lookup(catalog, key); + expect(typeof value).toBe("string"); + expect(value).not.toBe(key); + } + } + }); + + test("passes the {{kind}} variable the fork confirm label interpolates", () => { + const source = readSource("../src/routes/agent/lifecycle/kind-fork-dialog.tsx"); + + expect(source).toContain('t("agentLifecycle.forkAs", { kind:'); + for (const catalog of [en, zhCN, zhTW, ja]) { + expect(lookup(catalog, "agentLifecycle.forkAs")).toContain("{{kind}}"); + } + }); + + test("keeps dialog grid columns shrinkable so content cannot outgrow the dialog", () => { + const source = readSource("../src/shared/ui/dialog.tsx"); + + expect(source).toContain("grid-cols-[minmax(0,1fr)]"); + }); + test("does not render contract-owned Agent kind English copy directly", () => { const source = readSource("../src/routes/agent/components/kind-selector.tsx");