diff --git a/AGENTS.md b/AGENTS.md index 044507a9fe5..a6b0c478d20 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,6 +55,10 @@ 的术语照用,不自造译法;表里没有或拿不准的,在 `i18n/glossary.json` 加 `status: "proposed"` 条目再讨论。门禁为 `pnpm check:i18n-glossary`,规则见 `docs/dev-rules/engineering-conventions.md` §5.1。 +- 文案里出现**任务 / 对话 / 消息**这几个词时,必须先读 + `docs/product-rules/task-and-conversation-naming.md`:`session` 面向用户叫「任务」, + 「对话」只用于任务内的交流过程与内容,单条往来叫「消息」;**「任务」与 `task` 同句出现 + 时必须消解歧义**。这三个词的边界拿不准会直接做出用户能看见的不一致。 - 所有新增或修改的 UI 必须同时**实现** Light 与 Dark 两种模式(颜色一律走语义 token, 禁止只适配一种模式的硬编码或条件补丁);只实现一种模式视为未完成。**两种模式的实机 目检不是硬性门槛**——能目检更好,做不到时如实写明哪种模式未验证,不得把「复用了 themed diff --git a/apps/desktop/help-knowledge/collaboration.md b/apps/desktop/help-knowledge/collaboration.md index efc1962b071..259cd4ed40e 100644 --- a/apps/desktop/help-knowledge/collaboration.md +++ b/apps/desktop/help-knowledge/collaboration.md @@ -19,7 +19,7 @@ Collaboration ("协同" / Collab) lets one session — the **lead** — bring in **While collaborating:** - Each worker is a full session with its own history, tools, working directory, and model, and runs with permissions bypassed so it doesn't stop to ask. It inherits the lead's working directory by default (or shares the worktree when the lead uses one). -- A worker's model defaults to your last "New Chat" choice for that vendor, falling back to the lead's model. +- A worker's model defaults to your last new-session choice for that vendor, falling back to the lead's model. - Only one worker is **focused** (shown) at a time — switch between them with the worker tabs / dropdown. You can intervene in any worker directly, and archive workers individually. **Turning it off:** diff --git a/apps/desktop/help-knowledge/import-sessions.md b/apps/desktop/help-knowledge/import-sessions.md index 56a516c9696..d1fb144a1dd 100644 --- a/apps/desktop/help-knowledge/import-sessions.md +++ b/apps/desktop/help-knowledge/import-sessions.md @@ -12,7 +12,7 @@ If you already have Codex or Claude Code conversations on disk (from the CLIs), - Click **Scan** to discover sessions in the standard Codex / Claude Code locations on your machine. - Filter the results: - by **source** — Codex or Claude Code; - - by **placement** — by project (sessions inside a specific working directory) or by dialogue (free-form conversations not tied to a project). + - by **placement** — by project (sessions inside a specific working directory) or by chat (free-form sessions not tied to a project). - Tick the sessions or projects you want, then click **Import**. - They appear in the left sidebar. diff --git a/apps/desktop/help-knowledge/sessions-and-chat.md b/apps/desktop/help-knowledge/sessions-and-chat.md index 497ce3493a5..994b18c925c 100644 --- a/apps/desktop/help-knowledge/sessions-and-chat.md +++ b/apps/desktop/help-knowledge/sessions-and-chat.md @@ -17,7 +17,7 @@ Each **session** is a persistent chat thread driven by **one agent** (Claude Cod - While the agent is replying, **Send** becomes **Stop** — click it to interrupt the current turn cleanly. - You can keep typing while the agent is busy — follow-ups are **queued** in order and sent one after another when the previous turn finishes. -**Conversation controls:** +**Chat controls:** - **`/clear`** — clears the current session's conversation context in place (resets messages and state without creating or switching to a new session). The session itself stays in the sidebar; only its content is wiped. - **`/compact`** — ask the agent to summarize and compress earlier turns into a shorter context (Claude Code). diff --git a/apps/desktop/help-knowledge/sidebar.md b/apps/desktop/help-knowledge/sidebar.md index 3692bf9f109..6a743c16397 100644 --- a/apps/desktop/help-knowledge/sidebar.md +++ b/apps/desktop/help-knowledge/sidebar.md @@ -31,8 +31,8 @@ The left sidebar lists your sessions. It's the main way to navigate and organize **Right-click actions:** -- On a regular session: **Pin / Unpin**, **Rename**, **Move to project** (submenu), **Copy conversation link** (a `cindy://session/` deep link), **Open in new window**, **Archive**, **Delete**. -- On an archived session: **Rename**, **Unarchive**, **Copy conversation link**, **Delete**. +- On a regular session: **Pin / Unpin**, **Rename**, **Move to project** (submenu), **Copy session link** (a `cindy://session/` deep link), **Open in new window**, **Archive**, **Delete**. +- On an archived session: **Rename**, **Unarchive**, **Copy session link**, **Delete**. **Session statuses:** diff --git a/apps/desktop/native/agent-island/macos-agent-island-helper.swift b/apps/desktop/native/agent-island/macos-agent-island-helper.swift index 125fe50d812..b3f476faae4 100644 --- a/apps/desktop/native/agent-island/macos-agent-island-helper.swift +++ b/apps/desktop/native/agent-island/macos-agent-island-helper.swift @@ -1441,8 +1441,8 @@ struct AgentIslandStrings: Codable, Equatable { static let fallback = AgentIslandStrings( appName: "Cindy", - newConversationTitle: "New Maker", - newConversationHint: "Start a new conversation", + newConversationTitle: "New Session", + newConversationHint: "Start a new session", muteSound: "Mute Agent Island", enableSound: "Enable Agent Island sound", settings: "Agent Island settings", diff --git a/apps/desktop/src/main/__tests__/notificationService.test.ts b/apps/desktop/src/main/__tests__/notificationService.test.ts index bc75469d88a..88d9f27e91d 100644 --- a/apps/desktop/src/main/__tests__/notificationService.test.ts +++ b/apps/desktop/src/main/__tests__/notificationService.test.ts @@ -395,7 +395,7 @@ describe('notificationService — channels 分发', () => { // 文案断言 — 锁住对外可见的飞书消息格式,后续如要改文案需主动调整测试。 expect(feishuIm.sendMarkdownText).toHaveBeenCalledWith( 'ou_owner', - 'Cindy · 会话「Hello」需要你回复', + 'Cindy · 任务「Hello」需要你回复', ); }); @@ -415,7 +415,7 @@ describe('notificationService — channels 分发', () => { expect(feishuIm.sendMarkdownText).toHaveBeenCalledTimes(1); expect(feishuIm.sendMarkdownText).toHaveBeenCalledWith( 'ou_owner', - 'Cindy · 会话「Hello」已完成 ✓', + 'Cindy · 任务「Hello」已完成 ✓', ); }); @@ -439,7 +439,7 @@ describe('notificationService — channels 分发', () => { ); expect(feishuIm.sendMarkdownText).toHaveBeenCalledWith( 'ou_owner', - 'Cindy · 会话「Broken model」执行失败', + 'Cindy · 任务「Broken model」执行失败', ); }); diff --git a/apps/desktop/src/main/agent-island/__tests__/service.test.ts b/apps/desktop/src/main/agent-island/__tests__/service.test.ts index af31783d671..2bba1508abc 100644 --- a/apps/desktop/src/main/agent-island/__tests__/service.test.ts +++ b/apps/desktop/src/main/agent-island/__tests__/service.test.ts @@ -1282,7 +1282,7 @@ describe('AgentIslandService native publishing', () => { setMainLocale('zh-CN'); service.refreshLocalization(); await vi.waitFor(() => expect(publish.mock.calls.at(-1)?.[0].sessions[0]).toMatchObject({ - title: '未命名对话', + title: '未命名任务', })); setMainLocale('en'); service.refreshLocalization(); diff --git a/apps/desktop/src/main/agent-island/__tests__/state.test.ts b/apps/desktop/src/main/agent-island/__tests__/state.test.ts index ea3fbebb2c0..51697b70259 100644 --- a/apps/desktop/src/main/agent-island/__tests__/state.test.ts +++ b/apps/desktop/src/main/agent-island/__tests__/state.test.ts @@ -647,7 +647,7 @@ describe('Agent Island display state', () => { // review 曾担心 priorityCompactTitle 会把原始哨兵带给 native(Swift 侧的 compactTitle // 优先读这个字段)。事实是取值链第一步 meaningfulSessionTitle 就按大小写无关过滤掉 // 'new maker' / 'untitled' / 'codex' 等泛化串,pill 因此回落到项目名 —— 小尺寸胶囊里 - // 项目名本来也比「未命名对话」有信息量。这条断言把该事实钉住,免得日后有人放宽过滤 + // 项目名本来也比「未命名任务」有信息量。这条断言把该事实钉住,免得日后有人放宽过滤 // 又把哨兵漏给 native(PR #1031 review 第 13 轮)。 const state = createAgentIslandState(); diff --git a/apps/desktop/src/main/applicationMenuLabels.ts b/apps/desktop/src/main/applicationMenuLabels.ts index 43afc58b131..3e8f0785f40 100644 --- a/apps/desktop/src/main/applicationMenuLabels.ts +++ b/apps/desktop/src/main/applicationMenuLabels.ts @@ -40,7 +40,7 @@ export const APPLICATION_MENU_LABELS: Record { expect(again).toEqual({ committed: 0, skipped: 1, failed: 0 }); expect(db.select().from(schema.mediaRefs).all()).toHaveLength(1); - // 同图发到另一个会话 → 各挂各的引用 + // 同图发到另一个任务 → 各挂各的引用 const other = await chatAttachments.commitChatImageUrls( { sessionId: 'sess-2', urls: [PNG_URL] }, db, diff --git a/apps/desktop/src/main/commands/builtins.ts b/apps/desktop/src/main/commands/builtins.ts index 4720956cda0..8300864b70b 100644 --- a/apps/desktop/src/main/commands/builtins.ts +++ b/apps/desktop/src/main/commands/builtins.ts @@ -343,8 +343,11 @@ export function registerBuiltinDesktopCommands( registry.register({ name: 'clear', + // 实现是 renderer 收到 DESKTOP_COMMAND_TRIGGERED 后调 clearSession() → + // clearSessionAfterGuard:**原地**清空当前任务的对话上下文,不新建、也不切走 + // (help-knowledge/{commands,sessions-and-chat}.md 写的才是对的)。 description: - 'Equivalent of the sidebar\'s "New Chat" button — opens a fresh empty draft and switches to it. Discards the current conversation context.', + 'Clears the current session context in place — wipes its messages and state without creating or switching to a new session. The session stays in the sidebar.', execute: (ctx) => broadcastDesktopCommand(buildPayload('clear', ctx)), }); @@ -573,7 +576,7 @@ export function registerBuiltinDesktopCommands( registry.register({ name: 'jump-session', - description: '输入 sessionId 后直接跳转到该会话。', + description: '输入 sessionId 后直接跳转到该任务。', // 实际执行在 renderer 本地拦截(navigationCommands.ts);这里仅负责让命令出现在 // `/` 菜单并提供描述,不走 executeDesktopCommand broadcast。 execute: () => {}, diff --git a/apps/desktop/src/main/device-link/__tests__/mirrorCachePurgeQueue.test.ts b/apps/desktop/src/main/device-link/__tests__/mirrorCachePurgeQueue.test.ts index 38129269b39..d575ccec0c9 100644 --- a/apps/desktop/src/main/device-link/__tests__/mirrorCachePurgeQueue.test.ts +++ b/apps/desktop/src/main/device-link/__tests__/mirrorCachePurgeQueue.test.ts @@ -339,7 +339,7 @@ describe('文件级条目(clearDevice 删不掉时用)', () => { const pending = path.join(`${root}.control`, 'pending'); await fsp.mkdir(cleared, { recursive: true }); await fsp.mkdir(pending, { recursive: true }); - // 20 个会话计数 + 20 个墓碑,远超单条目的 key 上限(16)。 + // 20 个任务计数 + 20 个墓碑,远超单条目的 key 上限(16)。 for (let i = 0; i < 20; i += 1) { await fsp.writeFile(path.join(cleared, `sess-${i}`), '1', 'utf8'); await fsp.writeFile(path.join(pending, `sess-${i}`), '1', 'utf8'); diff --git a/apps/desktop/src/main/device-link/ipc.ts b/apps/desktop/src/main/device-link/ipc.ts index 2dd8a0c4af7..168a35faa36 100644 --- a/apps/desktop/src/main/device-link/ipc.ts +++ b/apps/desktop/src/main/device-link/ipc.ts @@ -458,13 +458,13 @@ export async function handleInvoke( if (capability.error.code === 'CHANNEL_NOT_ALLOWED') { throwIpcError( 'SESSION_REFERENCE_UNSUPPORTED', - '目标设备版本不支持会话引用,请升级目标设备后重试', + '目标设备版本不支持任务引用,请升级目标设备后重试', ); } if (capability.error.code === 'IPC_ERROR') { throwIpcError( 'SESSION_REFERENCE_UNAVAILABLE', - '目标设备仍在启动,会话引用暂不可用,请稍后重试', + '目标设备仍在启动,任务引用暂不可用,请稍后重试', ); } throwIpcError( @@ -489,7 +489,7 @@ export async function handleInvoke( ) { throwIpcError( 'SESSION_REFERENCE_UNSUPPORTED', - '目标设备版本不支持会话引用,请升级目标设备后重试', + '目标设备版本不支持任务引用,请升级目标设备后重试', ); } } diff --git a/apps/desktop/src/main/hook-control/__tests__/dispatcher.test.ts b/apps/desktop/src/main/hook-control/__tests__/dispatcher.test.ts index 493eeb704b1..5bf6646f8ed 100644 --- a/apps/desktop/src/main/hook-control/__tests__/dispatcher.test.ts +++ b/apps/desktop/src/main/hook-control/__tests__/dispatcher.test.ts @@ -471,7 +471,7 @@ describe('dispatcher 核心语义', () => { fr.finish({ finalText: '新对话的回答' }); await tick(); const finalText = c.last('turn.end')!.payload.finalText; - expect(finalText).toContain('原对话已不在可用的工作目录里'); + expect(finalText).toContain('原任务已不在可用的工作目录里'); expect(finalText).toContain('把它所在的目录加进来'); expect(finalText).toContain('新对话的回答'); }); @@ -818,7 +818,7 @@ describe('dispatcher 核心语义', () => { fr.finish({ finalText: '新会话的回答' }); await tick(); const finalText = c.last('turn.end')!.payload.finalText; - expect(finalText).toContain('原对话已不在可用的工作目录里'); + expect(finalText).toContain('原任务已不在可用的工作目录里'); expect(finalText).toContain('新会话的回答'); }); @@ -867,7 +867,7 @@ describe('dispatcher 核心语义', () => { fr.finish({ finalText: '新的回答' }); await tick(); // 措辞留余地: inspect 的 null 也可能是读库瞬时失败, 不能一口咬定会话没了 - expect(c.last('turn.end')!.payload.finalText).toContain('原对话现在读不到'); + expect(c.last('turn.end')!.payload.finalText).toContain('原任务现在读不到'); }); it('切账号期间异步定位失败也不回写旧代 rejected ack', async () => { @@ -2004,7 +2004,7 @@ describe('turn.reopen: 失败任务在桌面端被续跑后接回原消息', () undispatchListeners.add(listener); return () => undispatchListeners.delete(listener); }) as NonNullable, - /** 模拟"桌面端在这个会话里做了与续跑无关的事"。 */ + /** 模拟"桌面端在这个任务里做了与续跑无关的事"。 */ intervene: (sessionId: string) => { for (const l of [...interveners]) l(sessionId); }, @@ -2139,7 +2139,7 @@ describe('turn.reopen: 失败任务在桌面端被续跑后接回原消息', () it('同 session 又来了新的 hook 任务 -> 撤销在观察的续跑并作废记账', async () => { const { cr, sig, c, d, sessionId } = await failOneTask(); - // 让后续 dispatch 沿 binding 落回同一个会话(否则 inspect 查不到会重建)。 + // 让后续 dispatch 沿 binding 落回同一个任务(否则 inspect 查不到会重建)。 cr.sessions[sessionId] = { workingDir: WS_DIR, usable: true }; sig.retry(sessionId); await tick(); @@ -2358,7 +2358,7 @@ describe('turn.reopen: 失败任务在桌面端被续跑后接回原消息', () expect(cr.watches).toHaveLength(0); expect(c.ofType('turn.reopen')).toHaveLength(0); - // 而且它同时作废了意图与记账: 与 enqueue 侧同一条规则 —— 这个会话被无关内容推进过, + // 而且它同时作废了意图与记账: 与 enqueue 侧同一条规则 —— 这个任务被无关内容推进过, // 就不再把任何结果接回那条旧消息。哪怕目标那条随后真的 dispatch 也不接。 // 取舍是明确的: 这里判错的代价是"渠道消息停在失败上"(本能力之前的状态), 反过来 // 放行则是"把无关输出写进用户那条消息", 后者是真的错。 @@ -2667,7 +2667,7 @@ describe('turn.reopen: 失败任务在桌面端被续跑后接回原消息', () await tick(); // 同一 externalKey 的新 hook 任务跑起来, 并同样以失败收口 -> 记账换成它的 requestId。 - // (让 inspect 看见这个 session, 第二条派发才会落到同一个会话上。) + // (让 inspect 看见这个 session, 第二条派发才会落到同一个任务上。) cr.sessions[sessionId] = { workingDir: WS_DIR, usable: true }; d.handleDispatch('conn-1', dispatch({ requestId: 'req-take-over', prompt: '新任务' }), c.send); await tick(); diff --git a/apps/desktop/src/main/hook-control/__tests__/interactions.test.ts b/apps/desktop/src/main/hook-control/__tests__/interactions.test.ts index 3e2e7404b19..fc20b98f925 100644 --- a/apps/desktop/src/main/hook-control/__tests__/interactions.test.ts +++ b/apps/desktop/src/main/hook-control/__tests__/interactions.test.ts @@ -85,7 +85,7 @@ describe('composeInteractionCard', () => { }); }); - it('permission: 三按钮卡(允许一次/本对话总是允许/拒绝), 超时默认拒绝', () => { + it('permission: 三按钮卡(允许一次/本任务总是允许/拒绝), 超时默认拒绝', () => { const composed = composeInteractionCard({ kind: 'permission', requestId: 'int-6', @@ -99,12 +99,12 @@ describe('composeInteractionCard', () => { expect(composed.card.body).toContain('rm -rf dist'); expect(composed.card.buttons).toEqual([ { id: 'perm:allow', label: '允许一次', style: 'primary' }, - { id: 'perm:always', label: '本对话总是允许', style: 'default' }, + { id: 'perm:always', label: '本任务总是允许', style: 'default' }, { id: 'perm:deny', label: '拒绝', style: 'danger' }, ]); expect(composed.decisions.get('perm:allow')).toEqual({ kind: 'permission', behavior: 'allow' }); - // 「本对话总是允许」带会话级 addRules(claude 直接消费, codex 非空即会话放行) - // 注:按钮文案用「对话」(产品术语,与应用内 permissions.alwaysAllowForSession 一致), + // 「本任务总是允许」带会话级 addRules(claude 直接消费, codex 非空即会话放行) + // 注:按钮文案用「任务」(产品术语,与应用内 permissions.alwaysAllowForSession 一致), // 但 addRules 的作用域仍是技术意义上的 agent session,两者刻意不同名。 expect(composed.decisions.get('perm:always')).toEqual({ kind: 'permission', diff --git a/apps/desktop/src/main/hook-control/dispatcher.ts b/apps/desktop/src/main/hook-control/dispatcher.ts index 34199746cdd..2fb5295523b 100644 --- a/apps/desktop/src/main/hook-control/dispatcher.ts +++ b/apps/desktop/src/main/hook-control/dispatcher.ts @@ -372,16 +372,16 @@ const MAX_PENDING_REOPENS = 200; * 对话选择重新指定原对话才接得回来 —— 两步缺一不可。 */ const NOTICE_SESSION_RECREATED = - 'ℹ️ 原对话已不在可用的工作目录里,这条消息起换用了新对话,原对话的上下文不会带过来。' + - '想接回原对话:先到 Cindy 的 设置 → 远程连接 → 工作目录映射 把它所在的目录加进来,' + - '再在这里用对话选择重新指定它。'; + 'ℹ️ 原任务已不在可用的工作目录里,这条消息起换用了新任务,原任务的上下文不会带过来。' + + '想接回原任务:先到 Cindy 的 设置 → 远程连接 → 工作目录映射 把它所在的目录加进来,' + + '再在这里选择任务重新指定它。'; /** * 查不到原对话时的说明。措辞刻意留了余地: inspect 返回 null 是多义的 —— * 会话真的没了是 null, meta / DB 读取瞬时失败也被吞成 null(session-runner * 两路都 catch)。一口咬定"已被归档或删除"会在读库抖动时误导用户 * (PR #733 review 指出)。 */ -const NOTICE_SESSION_GONE = 'ℹ️ 原对话现在读不到(可能已被归档或删除),这条消息起换用了新对话。'; +const NOTICE_SESSION_GONE = 'ℹ️ 原任务现在读不到(可能已被归档或删除),这条消息起换用了新任务。'; /** 标题里消息摘要的最大长度(字符), 超出截断加省略号。 */ const TITLE_SNIPPET_MAX = 24; @@ -1139,7 +1139,7 @@ export function createHookDispatcher(deps: HookDispatcherDeps): HookDispatcher { status: 'error', finalText: '', errorMessage: - '这个对话所在的目录已不在工作目录映射里,本条消息没有执行。把它所在的目录加进 设置 → 远程连接 → 工作目录映射 后再发一次。', + '这个任务所在的目录已不在工作目录映射里,本条消息没有执行。把它所在的目录加进 设置 → 远程连接 → 工作目录映射 后再发一次。', durationMs: 0, }; } else { diff --git a/apps/desktop/src/main/hook-control/interactions.ts b/apps/desktop/src/main/hook-control/interactions.ts index a71e78a4bac..cd827f1bd73 100644 --- a/apps/desktop/src/main/hook-control/interactions.ts +++ b/apps/desktop/src/main/hook-control/interactions.ts @@ -71,7 +71,7 @@ export interface ComposedInteractionCard { * InteractionRequest -> 卡片 + 决策映射。 * 返回 null 表示该 kind 不出卡(未来未知 kind), 由调用方按 kind 安全默认 * 就地自决。permission 自 Slack 按目录权限偏好上线起出卡: 非 bypass 会话的 - * 权限请求渲染成「允许一次 / 本对话总是允许 / 拒绝」三按钮卡, 超时安全默认 + * 权限请求渲染成「允许一次 / 本任务总是允许 / 拒绝」三按钮卡, 超时安全默认 * **拒绝**(用户显式选了收紧档, 放行才是意外)。 */ export function composeInteractionCard(req: InteractionRequest): ComposedInteractionCard | null { @@ -180,7 +180,7 @@ export function composeInteractionCard(req: InteractionRequest): ComposedInterac body, buttons: [ { id: 'perm:allow', label: '允许一次', style: 'primary' }, - { id: 'perm:always', label: '本对话总是允许', style: 'default' }, + { id: 'perm:always', label: '本任务总是允许', style: 'default' }, { id: 'perm:deny', label: '拒绝', style: 'danger' }, ], }, diff --git a/apps/desktop/src/main/hook-control/session-runner.ts b/apps/desktop/src/main/hook-control/session-runner.ts index 498c1a71ecf..42848826da0 100644 --- a/apps/desktop/src/main/hook-control/session-runner.ts +++ b/apps/desktop/src/main/hook-control/session-runner.ts @@ -524,7 +524,7 @@ export function createMakerHookSessionRunner(deps: { `hook run aborted: live session ${req.sessionId} runs in a directory that is no longer in the workspace map`, ); return fail( - '这个对话正在一个已不在工作目录映射里的目录中运行,本条消息没有执行。把该目录加进 设置 → 远程连接 → 工作目录映射,或在桌面端关掉这个对话后重发。', + '这个任务正在一个已不在工作目录映射里的目录中运行,本条消息没有执行。把该目录加进 设置 → 远程连接 → 工作目录映射,或在桌面端关掉这个任务后重发。', ); } diff --git a/apps/desktop/src/main/im/dingtalk/uiText.ts b/apps/desktop/src/main/im/dingtalk/uiText.ts index 1730c8cb8c8..d90af340359 100644 --- a/apps/desktop/src/main/im/dingtalk/uiText.ts +++ b/apps/desktop/src/main/im/dingtalk/uiText.ts @@ -15,7 +15,7 @@ export const ui = { help: `🤖 我能帮你做这些: /new 开个新对话(清掉当前上下文) -/stop 停止当前任务,并撤掉排队消息 +/stop 中止当前执行,并撤掉排队消息 /help 查看可用命令 模型、权限和远程接管请在 Cindy 桌面端调整。`, diff --git a/apps/desktop/src/main/im/discord/uiText.ts b/apps/desktop/src/main/im/discord/uiText.ts index b57adc1e7cf..3c386d13c70 100644 --- a/apps/desktop/src/main/im/discord/uiText.ts +++ b/apps/desktop/src/main/im/discord/uiText.ts @@ -21,18 +21,18 @@ export const ui = { /new 开个新存档(清掉当前对话上下文) /model 换个模型上场 /permission 调一下权限模式(auto / bypass / ask 等) -/ctr 远程接管 desktop 上的某个工作区(接管中再发可直接换会话) +/ctr 远程接管 desktop 上的某个工作区(接管中再发可直接换任务) /exctr 结束接管,回到我们俩的 Discord 私聊 /help 看看我会啥 -任务跑着想让它停?直接发 \`!stop\`,我会立刻中止当前任务、把排队的消息也撤掉,会话保留可继续。 +有活儿在跑想让它停?直接发 \`!stop\`,我会立刻中止当前执行、把排队的消息也撤掉,任务保留可继续。 平时直接发消息就行,我在听~`, unknownCommand: (cmd: string) => `没认出 \`${cmd}\` 这个命令 🤔\n我能听懂的: /new、/model、/permission、/ctr、/exctr、/help`, detachedBySlash: '🚪 接管结束,咱们回到 Discord 私聊。下次想远程操控 desktop 再 /ctr', detachedByRevoke: '⚠️ 你在 desktop 那边把接管收回去了,后续消息回到我们俩的 Discord 私聊。', - notAttached: '🤷 你现在没在接管任何会话,/exctr 闲着也没事可干。', + notAttached: '🤷 你现在没在接管任何任务,/exctr 闲着也没事可干。', }, agent: { @@ -51,10 +51,10 @@ export const ui = { : missing === 'provider-disconnected' ? '未连接或连接已失效' : `需要先登录 ${agentKind} 凭证`; - const message = `⚠️ 当前 Discord 会话使用供应商「${provider}」(${model}),${reason}。`; + const message = `⚠️ 当前 Discord 对话使用供应商「${provider}」(${model}),${reason}。`; return attached ? `${message}\n请在 desktop 的 Settings → 模型供应商中修复认证后,直接继续发送消息。` - : `${message}\n“新会话配置”只影响新会话;修改后请发送 \`/new\`,再继续聊天。`; + : `${message}\n“新对话配置”只影响新对话;修改后请发送 \`/new\`,再继续聊天。`; }, controlInProgress: '🎮 你 /ctr 还在选择中呢 — 先把上面那张卡片操作完(或点 🚪 退出),再来发别的~', @@ -64,14 +64,14 @@ export const ui = { position <= 1 ? '⏳ 上一轮还在跑,这条先排队——跑完自动接上。想直接叫停就发 `!stop`' : `⏳ 上一轮还在跑,这条排在第 ${position} 位——会按顺序发给 agent。想直接叫停就发 \`!stop\``, - /** `!stop` 生效 — 当前任务已中止, 会话保留可继续。 */ + /** `!stop` 生效 — 当前执行已中止, 任务保留可继续。 */ stopDone: (droppedQueued: number) => droppedQueued > 0 - ? `⏹ 已叫停当前任务,排队中的 ${droppedQueued} 条消息也一并撤了。会话还在——想继续随时发新指令~` - : '⏹ 已叫停当前任务。会话还在——想继续随时发新指令~', + ? `⏹ 已叫停当前执行,排队中的 ${droppedQueued} 条消息也一并撤了。任务还在——想继续随时发新指令~` + : '⏹ 已叫停当前执行。任务还在——想继续随时发新指令~', /** `!stop` 时没有任务在跑。 */ stopIdle: '🤷 现在没有正在跑的任务,`!stop` 落了个空。等有活儿要停的时候再喊我~', - scheduledTaskHeader: (name: string | null) => (name ? `🤖 自动任务「${name}」` : '🤖 自动任务'), + scheduledTaskHeader: (name: string | null) => (name ? `🤖 自动化「${name}」` : '🤖 自动化'), unsupportedOnly: (entries: IMUnsupportedEntry[]) => `🙏 这条消息我吞不下:\n${entries.map((e) => `• ${e.label}`).join('\n')}\n\n` + `我能消化的: 文本、图片(jpg/png/gif/webp)、PDF、代码与配置类文本文件。`, @@ -131,24 +131,24 @@ export const ui = { }, control: { title: '🎮 挑个工作区上号', - emptyBody: '_暂时还没有可接管的工作区~ 在 desktop 端打开/创建一个会话再来_', + emptyBody: '_暂时还没有可接管的工作区~ 在 desktop 端打开/创建一个任务再来_', hint: '点工作区往下走;点 🚪 退出 取消这次', attachedSwitchHint: (sessionTitle: string) => - `🎮 当前接管中:**${sessionTitle}**\n选个新会话直接换乘;点 🚪 退出则保持现状`, + `🎮 当前接管中:**${sessionTitle}**\n选个新任务直接换乘;点 🚪 退出则保持现状`, btnExit: '🚪 撤了', resolvedExit: '🚪 走了,下次想远程操控再 /ctr', sessionPickerTitle: (displayName: string) => `🎮 ${displayName} 里的存档`, - sessionPickerHint: '挑个会话继续打 · ➕ 新建 开新存档 · ↩️ 后退 换工作区 · 🚪 退出 取消', + sessionPickerHint: '挑个任务继续打 · ➕ 新建 开新存档 · ↩️ 后退 换工作区 · 🚪 退出 取消', sessionPickerEmptyBody: (displayName: string) => - `_工作区 **${displayName}** 这边还没有 active 会话~ 不如点 ➕ 新建 开一个?_`, + `_工作区 **${displayName}** 这边还没有 active 任务~ 不如点 ➕ 新建 开一个?_`, btnNew: '➕ 新建', btnBack: '↩️ 后退', resolvedSessionPick: (sessionTitle: string, workspaceName: string) => - `🎯 上号了:**${sessionTitle}**(${workspaceName})\n接下来你发消息我就直接进这个会话;想撤随时 \`/exctr\``, + `🎯 上号了:**${sessionTitle}**(${workspaceName})\n接下来你发消息我就直接进这个任务;想撤随时 \`/exctr\``, resolvedNewSession: (workspaceName: string) => `✨ 新存档已建 + 接管完成(在 **${workspaceName}** 里)\n直接发指令开聊;想退就 \`/exctr\``, attachFailed: (reason: string) => `❌ 没接上:${reason}`, - sessionBusyOldCardPlaceholder: '⏳ 那个会话还在跑——下方给你刷了张新卡片,重选一下吧', + sessionBusyOldCardPlaceholder: '⏳ 那个任务还在跑——下方给你刷了张新卡片,重选一下吧', sessionBusyPrompts: [ (sessionTitle: string) => `⏳ **${sessionTitle}** 这会儿正在 BOSS 战~\nagent 还在思考/敲代码,等它这把打完再 \`/ctr\` 上号`, diff --git a/apps/desktop/src/main/im/feishu/uiText.ts b/apps/desktop/src/main/im/feishu/uiText.ts index c68dbf67d1e..723559f5add 100644 --- a/apps/desktop/src/main/im/feishu/uiText.ts +++ b/apps/desktop/src/main/im/feishu/uiText.ts @@ -24,18 +24,18 @@ export const ui = { /new 开个新存档(清掉当前对话上下文) /model 换个模型上场 /permission 调一下权限模式(auto / bypass / ask 等) -/ctr 远程接管 desktop 上的某个工作区(接管中再发可直接换会话) +/ctr 远程接管 desktop 上的某个工作区(接管中再发可直接换任务) /exctr 结束接管,回到我们俩的私聊 /help 看看我会啥 -任务跑着想让它停?直接发 \`!stop\`,我会立刻中止当前任务、把排队的消息也撤掉,会话保留可继续。 +有活儿在跑想让它停?直接发 \`!stop\`,我会立刻中止当前执行、把排队的消息也撤掉,任务保留可继续。 平时直接发消息就行,我在听~`, unknownCommand: (cmd: string) => `没认出 \`${cmd}\` 这个命令 🤔\n我能听懂的: /new、/model、/permission、/ctr、/exctr、/help`, detachedBySlash: '🚪 接管结束,咱们回到私聊频道。下次想远程操控 desktop 再 /ctr', detachedByRevoke: '⚠️ 你在 desktop 那边把接管收回去了,后续消息回到我们俩的私聊。', - notAttached: '🤷 你现在没在接管任何会话,/exctr 闲着也没事可干。', + notAttached: '🤷 你现在没在接管任何任务,/exctr 闲着也没事可干。', }, // ── agent runtime feedback ───────────────────────────────────────────────── @@ -55,10 +55,10 @@ export const ui = { : missing === 'provider-disconnected' ? '未连接或连接已失效' : `需要先登录 ${agentKind} 凭证`; - const message = `⚠️ 当前会话使用供应商「${provider}」(${model}),${reason}。`; + const message = `⚠️ 当前对话使用供应商「${provider}」(${model}),${reason}。`; return attached ? `${message}\n请在 desktop 的 Settings → 模型供应商中修复认证后,直接继续发送消息。` - : `${message}\n“新会话配置”只影响新会话;修改后请发送 \`/new\`,再继续聊天。`; + : `${message}\n“新对话配置”只影响新对话;修改后请发送 \`/new\`,再继续聊天。`; }, controlInProgress: '🎮 你 /ctr 还在选择中呢 — 先把上面那张卡片操作完(或点 🚪 退出),再来发别的~', @@ -69,15 +69,15 @@ export const ui = { position <= 1 ? '⏳ 上一轮还在跑,这条先排队——跑完自动接上。想直接叫停就发 `!stop`' : `⏳ 上一轮还在跑,这条排在第 ${position} 位——会按顺序发给 agent。想直接叫停就发 \`!stop\``, - /** `!stop` 生效 — 当前任务已中止, 会话保留可继续。 */ + /** `!stop` 生效 — 当前执行已中止, 任务保留可继续。 */ stopDone: (droppedQueued: number) => droppedQueued > 0 - ? `⏹ 已叫停当前任务,排队中的 ${droppedQueued} 条消息也一并撤了。会话还在——想继续随时发新指令~` - : '⏹ 已叫停当前任务。会话还在——想继续随时发新指令~', + ? `⏹ 已叫停当前执行,排队中的 ${droppedQueued} 条消息也一并撤了。任务还在——想继续随时发新指令~` + : '⏹ 已叫停当前执行。任务还在——想继续随时发新指令~', /** `!stop` 时没有任务在跑。 */ stopIdle: '🤷 现在没有正在跑的任务,`!stop` 落了个空。等有活儿要停的时候再喊我~', - /** 远程控制时转播自动任务 turn 的卡片头(系统自动发起,非用户)。 */ - scheduledTaskHeader: (name: string | null) => (name ? `🤖 自动任务「${name}」` : '🤖 自动任务'), + /** 远程控制时转播自动化 turn 的卡片头(系统自动发起,非用户)。 */ + scheduledTaskHeader: (name: string | null) => (name ? `🤖 自动化「${name}」` : '🤖 自动化'), /** 用户发的内容**全部**模型都处理不了——单独发,不进 Agent。 */ unsupportedOnly: (entries: IMUnsupportedEntry[]) => `🙏 这条消息我吞不下:\n${entries.map((e) => `• ${e.label}`).join('\n')}\n\n` + @@ -141,27 +141,27 @@ export const ui = { }, control: { title: '🎮 挑个工作区上号', - emptyBody: '_暂时还没有可接管的工作区~ 在 desktop 端打开/创建一个会话再来_', + emptyBody: '_暂时还没有可接管的工作区~ 在 desktop 端打开/创建一个任务再来_', hint: '点工作区往下走;点 🚪 退出 取消这次', - /** 接管态下重发 /ctr — picker 顶部提示当前接管中的会话, 选新的直接换乘。 */ + /** 接管态下重发 /ctr — picker 顶部提示当前接管中的任务, 选新的直接换乘。 */ attachedSwitchHint: (sessionTitle: string) => - `🎮 当前接管中:**${sessionTitle}**\n选个新会话直接换乘;点 🚪 退出则保持现状`, + `🎮 当前接管中:**${sessionTitle}**\n选个新任务直接换乘;点 🚪 退出则保持现状`, btnExit: '🚪 撤了', resolvedExit: '🚪 走了,下次想远程操控再 /ctr', sessionPickerTitle: (displayName: string) => `🎮 ${displayName} 里的存档`, - sessionPickerHint: '挑个会话继续打 · ➕ 新建 开新存档 · ↩️ 后退 换工作区 · 🚪 退出 取消', + sessionPickerHint: '挑个任务继续打 · ➕ 新建 开新存档 · ↩️ 后退 换工作区 · 🚪 退出 取消', sessionPickerEmptyBody: (displayName: string) => - `_工作区 **${displayName}** 这边还没有 active 会话~ 不如点 ➕ 新建 开一个?_`, + `_工作区 **${displayName}** 这边还没有 active 任务~ 不如点 ➕ 新建 开一个?_`, btnNew: '➕ 新建', btnBack: '↩️ 后退', resolvedSessionPick: (sessionTitle: string, workspaceName: string) => - `🎯 上号了:**${sessionTitle}**(${workspaceName})\n接下来你发消息我就直接进这个会话;想撤随时 \`/exctr\``, + `🎯 上号了:**${sessionTitle}**(${workspaceName})\n接下来你发消息我就直接进这个任务;想撤随时 \`/exctr\``, resolvedNewSession: (workspaceName: string) => `✨ 新存档已建 + 接管完成(在 **${workspaceName}** 里)\n直接发指令开聊;想退就 \`/exctr\``, attachFailed: (reason: string) => `❌ 没接上:${reason}`, /** 旧卡片(被点了 busy session 那张)被 freeze 时的占位文字, 让用户知道这张 * 卡片不再活跃, 下面会新发一张可重选的卡片。 */ - sessionBusyOldCardPlaceholder: '⏳ 那个会话还在跑——下方给你刷了张新卡片,重选一下吧', + sessionBusyOldCardPlaceholder: '⏳ 那个任务还在跑——下方给你刷了张新卡片,重选一下吧', // ── /ctr session-pick: 目标 session 正在跑 turn — 4 套有温度的提示 ── // 共同核心: agent 在干活, 强行接管会乱; 等会儿再来。话术尽量像队友说话。 sessionBusyPrompts: [ diff --git a/apps/desktop/src/main/im/shared/__tests__/authCheck.test.ts b/apps/desktop/src/main/im/shared/__tests__/authCheck.test.ts index 7be31988444..6e6f231b50e 100644 --- a/apps/desktop/src/main/im/shared/__tests__/authCheck.test.ts +++ b/apps/desktop/src/main/im/shared/__tests__/authCheck.test.ts @@ -244,7 +244,7 @@ describe('checkImRouteAuth', () => { describe('resolveEffectiveProvider', () => { // issue #882 第 3 点(2026-07 review):同一 model id 在不同来源上 mode 不一致时, // 鉴权解析不能只看"这个来源提供这个 id",还要看这个来源上这个 id 是不是聊天模型 - // ——否则会去校验一个非聊天来源的凭证,校验"通过"也不代表这个会话真能发聊天请求。 + // ——否则会去校验一个非聊天来源的凭证,校验"通过"也不代表这个任务真能发聊天请求。 it('rejects an explicit providerId whose copy of the model is non-chat, even though the id exists there', () => { const nonChat = provider({ id: 'xd', diff --git a/apps/desktop/src/main/im/shared/cardBuilders.ts b/apps/desktop/src/main/im/shared/cardBuilders.ts index 72ea720decf..e1603e6a240 100644 --- a/apps/desktop/src/main/im/shared/cardBuilders.ts +++ b/apps/desktop/src/main/im/shared/cardBuilders.ts @@ -548,7 +548,7 @@ export function buildScheduleDoneCard( ? [ { id: 'schedule:open-session', - label: '查看会话', + label: '查看任务', type: 'primary', payload: { requestId: `schedule-open:${run.id}`, diff --git a/apps/desktop/src/main/im/telegram/uiText.ts b/apps/desktop/src/main/im/telegram/uiText.ts index 3e2bde802c8..a68bad4c57e 100644 --- a/apps/desktop/src/main/im/telegram/uiText.ts +++ b/apps/desktop/src/main/im/telegram/uiText.ts @@ -22,22 +22,22 @@ export const ui = { help: `🤖 我能帮你做这些: /new 开个新对话(清掉当前上下文;群里发就重置这个群话题的上下文) -/project 切到某个项目目录干活(bot 原生会话,不接管 desktop) -/session 列最近的 desktop 会话,点一个直接接管续聊 +/project 切到某个项目目录干活(在 bot 这边跑,不接管 desktop) +/session 列最近的 desktop 任务,点一个直接接管续聊 /model 换个模型上场 /permission 调一下权限模式(auto / bypass / ask 等) -/ctr 远程接管 desktop 上的某个工作区(接管中再发可直接换会话) +/ctr 远程接管 desktop 上的某个工作区(接管中再发可直接换任务) /exctr 结束接管,回到 Telegram 对话 /help 看看我会啥 -任务跑着想让它停?发 /stop(或 \`!stop\`),我会立刻中止当前任务、把排队的消息也撤掉,会话保留可继续。 +有活儿在跑想让它停?发 /stop(或 \`!stop\`),我会立刻中止当前执行、把排队的消息也撤掉,任务保留可继续。 私聊直接发消息就行;群里 @ 我或回复我的消息才会触发,我也会记住群里最近的聊天做上下文~`, unknownCommand: (cmd: string) => `没认出 \`${cmd}\` 这个命令 🤔\n我能听懂的: /new、/session、/project、/model、/permission、/ctr、/exctr、/stop、/help`, detachedBySlash: '🚪 接管结束,咱们回到 Telegram 对话。下次想远程操控 desktop 再 /ctr', detachedByRevoke: '⚠️ 你在 desktop 那边把接管收回去了,后续消息回到 Telegram 对话。', - notAttached: '🤷 你现在没在接管任何会话,/exctr 闲着也没事可干。', + notAttached: '🤷 你现在没在接管任何任务,/exctr 闲着也没事可干。', }, agent: { @@ -56,10 +56,10 @@ export const ui = { : missing === 'provider-disconnected' ? '未连接或连接已失效' : `需要先登录 ${agentKind} 凭证`; - const message = `⚠️ 当前 Telegram 会话使用供应商「${provider}」(${model}),${reason}。`; + const message = `⚠️ 当前 Telegram 对话使用供应商「${provider}」(${model}),${reason}。`; return attached ? `${message}\n请在 desktop 的 Settings → 模型供应商中修复认证后,直接继续发送消息。` - : `${message}\n“新会话配置”只影响新会话;修改后请发送 \`/new\`,再继续聊天。`; + : `${message}\n“新对话配置”只影响新对话;修改后请发送 \`/new\`,再继续聊天。`; }, controlInProgress: '🎮 你 /ctr 还在选择中呢 — 先把上面那张卡片操作完(或点 🚪 退出),再来发别的~', @@ -69,14 +69,14 @@ export const ui = { position <= 1 ? '⏳ 上一轮还在跑,这条先排队——跑完自动接上。想直接叫停就发 `!stop`' : `⏳ 上一轮还在跑,这条排在第 ${position} 位——会按顺序发给 agent。想直接叫停就发 \`!stop\``, - /** `!stop` 生效 — 当前任务已中止, 会话保留可继续。 */ + /** `!stop` 生效 — 当前执行已中止, 任务保留可继续。 */ stopDone: (droppedQueued: number) => droppedQueued > 0 - ? `⏹ 已叫停当前任务,排队中的 ${droppedQueued} 条消息也一并撤了。会话还在——想继续随时发新指令~` - : '⏹ 已叫停当前任务。会话还在——想继续随时发新指令~', + ? `⏹ 已叫停当前执行,排队中的 ${droppedQueued} 条消息也一并撤了。任务还在——想继续随时发新指令~` + : '⏹ 已叫停当前执行。任务还在——想继续随时发新指令~', /** `!stop` 时没有任务在跑。 */ stopIdle: '🤷 现在没有正在跑的任务,`!stop` 落了个空。等有活儿要停的时候再喊我~', - scheduledTaskHeader: (name: string | null) => (name ? `🤖 自动任务「${name}」` : '🤖 自动任务'), + scheduledTaskHeader: (name: string | null) => (name ? `🤖 自动化「${name}」` : '🤖 自动化'), unsupportedOnly: (entries: IMUnsupportedEntry[]) => `🙏 这条消息我吞不下:\n${entries.map((e) => `• ${e.label}`).join('\n')}\n\n` + `我能消化的: 文本、图片(jpg/png/gif/webp)、PDF、代码与配置类文本文件。`, @@ -148,36 +148,36 @@ export const ui = { resolvedCancel: '🚪 没切,保持现状', switchFailed: (reason: string) => `❌ 没切过去:${reason}(目录可能已被移动或删除)`, attachedUnsupported: - '🎮 你正在 /ctr 接管一个 desktop 会话——接管期间消息直接进那个会话,不需要 /project。想切项目先 `/exctr` 退出接管', + '🎮 你正在 /ctr 接管一个 desktop 任务——接管期间消息直接进那个任务,不需要 /project。想切项目先 `/exctr` 退出接管', dialogueName: '对话(托管目录)', }, control: { title: '🎮 挑个工作区上号', - emptyBody: '_暂时还没有可接管的工作区~ 在 desktop 端打开/创建一个会话再来_', + emptyBody: '_暂时还没有可接管的工作区~ 在 desktop 端打开/创建一个任务再来_', recentSessions: { - title: '🕑 最近的会话', + title: '🕑 最近的任务', hint: '点一个直接接管续聊(跨工作区按时间排)· 想按工作区挑用 /ctr · 🚪 退出取消', - emptyBody: '_desktop 端还没有活跃会话~ 先在 desktop 里开一个再来_', + emptyBody: '_desktop 端还没有活跃任务~ 先在 desktop 里开一个再来_', optionLabel: (title: string, workspaceName: string | null) => workspaceName ? `${title} · ${workspaceName}` : title, }, hint: '点工作区往下走;点 🚪 退出 取消这次', attachedSwitchHint: (sessionTitle: string) => - `🎮 当前接管中:**${sessionTitle}**\n选个新会话直接换乘;点 🚪 退出则保持现状`, + `🎮 当前接管中:**${sessionTitle}**\n选个新任务直接换乘;点 🚪 退出则保持现状`, btnExit: '🚪 撤了', resolvedExit: '🚪 走了,下次想远程操控再 /ctr', - sessionPickerTitle: (displayName: string) => `🎮 ${displayName} 里的会话`, - sessionPickerHint: '挑个会话继续 · ➕ 新建 开新会话 · ↩️ 后退 换工作区 · 🚪 退出 取消', + sessionPickerTitle: (displayName: string) => `🎮 ${displayName} 里的任务`, + sessionPickerHint: '挑个任务继续 · ➕ 新建 开新任务 · ↩️ 后退 换工作区 · 🚪 退出 取消', sessionPickerEmptyBody: (displayName: string) => - `_工作区 **${displayName}** 这边还没有 active 会话~ 不如点 ➕ 新建 开一个?_`, + `_工作区 **${displayName}** 这边还没有 active 任务~ 不如点 ➕ 新建 开一个?_`, btnNew: '➕ 新建', btnBack: '↩️ 后退', resolvedSessionPick: (sessionTitle: string, workspaceName: string) => - `🎯 上号了:**${sessionTitle}**(${workspaceName})\n接下来你发消息我就直接进这个会话;想撤随时 \`/exctr\``, + `🎯 上号了:**${sessionTitle}**(${workspaceName})\n接下来你发消息我就直接进这个任务;想撤随时 \`/exctr\``, resolvedNewSession: (workspaceName: string) => - `✨ 新会话已建 + 接管完成(在 **${workspaceName}** 里)\n直接发指令开聊;想退就 \`/exctr\``, + `✨ 新任务已建 + 接管完成(在 **${workspaceName}** 里)\n直接发指令开聊;想退就 \`/exctr\``, attachFailed: (reason: string) => `❌ 没接上:${reason}`, - sessionBusyOldCardPlaceholder: '⏳ 那个会话还在跑——下方给你刷了张新卡片,重选一下吧', + sessionBusyOldCardPlaceholder: '⏳ 那个任务还在跑——下方给你刷了张新卡片,重选一下吧', sessionBusyPrompts: [ (sessionTitle: string) => `⏳ **${sessionTitle}** 这会儿正忙~\nagent 还在思考/敲代码,等它这轮跑完再 \`/ctr\` 上号`, @@ -190,7 +190,7 @@ export const ui = { ], takeoverLoadingPrompts: [ (sessionTitle: string) => - `⏳ 正在让你接管 **${sessionTitle}**…\n_📦 加载会话元数据_ · _🔌 接通 agent 通道_ · _🧠 复盘上一轮进展_\n_三两秒就好_`, + `⏳ 正在让你接管 **${sessionTitle}**…\n_📦 加载任务元数据_ · _🔌 接通 agent 通道_ · _🧠 复盘上一轮进展_\n_三两秒就好_`, (sessionTitle: string) => `🎮 接入 **${sessionTitle}** 中…\n_正在把上下文搬过来,马上就好_`, (sessionTitle: string) => @@ -208,9 +208,9 @@ export const ui = { (workspaceName: string) => `🎯 接管 + 新建 一气呵成——**${workspaceName}** 已就位,agent 待命中。\n发第一条指令开聊;想撤随时 \`/exctr\``, (workspaceName: string) => - `✨ 新会话已建——工作目录 **${workspaceName}** 已经备好,agent 等你发话。\n第一条消息开聊;\`/exctr\` 随时退出`, + `✨ 新任务已建——工作目录 **${workspaceName}** 已经备好,agent 等你发话。\n第一条消息开聊;\`/exctr\` 随时退出`, (workspaceName: string) => - `🎮 准备就绪——在 **${workspaceName}** 给你开了个全新会话。\n要做啥告诉我,agent 在线~ \`/exctr\` 收手`, + `🎮 准备就绪——在 **${workspaceName}** 给你开了个全新任务。\n要做啥告诉我,agent 在线~ \`/exctr\` 收手`, (workspaceName: string) => `🆕 全新开局——**${workspaceName}** 工作目录已经 ready。\n第一条指令开聊;不想继续就 \`/exctr\``, ], diff --git a/apps/desktop/src/main/im/wechat/WechatIM.ts b/apps/desktop/src/main/im/wechat/WechatIM.ts index 791be00baae..67cd634dc08 100644 --- a/apps/desktop/src/main/im/wechat/WechatIM.ts +++ b/apps/desktop/src/main/im/wechat/WechatIM.ts @@ -1207,7 +1207,7 @@ export class WechatIM extends BaseIM implements RichChannelIM { case '/help': await this.#commitSimpleReply( task, - '可用命令:/new 新对话;/stop 停止当前任务;/stop all 停止全部任务;/status 查看状态;/help 查看帮助。', + '可用命令:/new 新对话;/stop 中止当前执行;/stop all 中止全部执行;/status 查看状态;/help 查看帮助。', ); return; case '/status': @@ -1290,7 +1290,7 @@ export class WechatIM extends BaseIM implements RichChannelIM { const text = reason.includes('TURN_PERMISSION_POLICY_UNSUPPORTED') || reason.includes('unsupported_turn_permission') - ? '当前会话使用“完全访问”权限,个人微信暂不支持该模式。请在 Cindy 中改为“自动”或“每次询问”。' + ? '当前任务使用“完全访问”权限,个人微信暂不支持该模式。请在 Cindy 中改为“自动”或“每次询问”。' : reason === 'missing_auth' ? '当前 Agent 尚未完成授权,请先在 Cindy 中连接模型服务。' : '这条消息暂时无法启动,请稍后重试。'; diff --git a/apps/desktop/src/main/im/wechat/uiText.ts b/apps/desktop/src/main/im/wechat/uiText.ts index fe712758ee3..8803bc46800 100644 --- a/apps/desktop/src/main/im/wechat/uiText.ts +++ b/apps/desktop/src/main/im/wechat/uiText.ts @@ -14,18 +14,18 @@ export const ui = { /new 开个新存档(清掉当前对话上下文) /model 换个模型上场 /permission 调一下权限模式(auto / bypass / ask 等) -/ctr 远程接管 desktop 上的某个工作区(接管中再发可直接换会话) +/ctr 远程接管 desktop 上的某个工作区(接管中再发可直接换任务) /exctr 结束接管,回到我们俩的微信私聊 /help 看看我会啥 -任务跑着想让它停?直接发 \`!stop\`,我会立刻中止当前任务、把排队的消息也撤掉,会话保留可继续。 +有活儿在跑想让它停?直接发 \`!stop\`,我会立刻中止当前执行、把排队的消息也撤掉,任务保留可继续。 平时直接发消息就行,我在听~`, unknownCommand: (cmd: string) => `没认出 \`${cmd}\` 这个命令 🤔\n我能听懂的: /new、/model、/permission、/ctr、/exctr、/help`, detachedBySlash: '🚪 接管结束,咱们回到微信私聊。下次想远程操控 desktop 再 /ctr', detachedByRevoke: '⚠️ 你在 desktop 那边把接管收回去了,后续消息回到我们俩的微信私聊。', - notAttached: '🤷 你现在没在接管任何会话,/exctr 闲着也没事可干。', + notAttached: '🤷 你现在没在接管任何任务,/exctr 闲着也没事可干。', }, agent: { completedNoText: '✅ 跑完啦(这一轮 agent 没说话)', @@ -43,10 +43,10 @@ export const ui = { : missing === 'provider-disconnected' ? '未连接或连接已失效' : `需要先登录 ${agentKind} 凭证`; - const message = `⚠️ 当前微信会话使用供应商「${provider}」(${model}),${reason}。`; + const message = `⚠️ 当前微信对话使用供应商「${provider}」(${model}),${reason}。`; return attached ? `${message}\n请在 desktop 的 Settings → 模型供应商中修复认证后,直接继续发送消息。` - : `${message}\n“新会话配置”只影响新会话;修改后请发送 \`/new\`,再继续聊天。`; + : `${message}\n“新对话配置”只影响新对话;修改后请发送 \`/new\`,再继续聊天。`; }, controlInProgress: '🎮 你 /ctr 还在选择中呢 — 先把上面那张卡片操作完(或点 🚪 退出),再来发别的~', @@ -58,10 +58,10 @@ export const ui = { : `⏳ 上一轮还在跑,这条排在第 ${position} 位——会按顺序发给 agent。想直接叫停就发 \`!stop\``, stopDone: (droppedQueued: number) => droppedQueued > 0 - ? `⏹ 已叫停当前任务,排队中的 ${droppedQueued} 条消息也一并撤了。会话还在——想继续随时发新指令~` - : '⏹ 已叫停当前任务。会话还在——想继续随时发新指令~', + ? `⏹ 已叫停当前执行,排队中的 ${droppedQueued} 条消息也一并撤了。任务还在——想继续随时发新指令~` + : '⏹ 已叫停当前执行。任务还在——想继续随时发新指令~', stopIdle: '🤷 现在没有正在跑的任务,`!stop` 落了个空。等有活儿要停的时候再喊我~', - scheduledTaskHeader: (name: string | null) => (name ? `🤖 自动任务「${name}」` : '🤖 自动任务'), + scheduledTaskHeader: (name: string | null) => (name ? `🤖 自动化「${name}」` : '🤖 自动化'), unsupportedOnly: (entries: IMUnsupportedEntry[]) => `🙏 这条消息我吞不下:\n${entries.map((entry) => `• ${entry.label}`).join('\n')}\n\n` + '我能消化的: 文本、图片(jpg/png/gif/webp)、PDF、代码与配置类文本文件。', @@ -121,24 +121,24 @@ export const ui = { }, control: { title: '🎮 挑个工作区上号', - emptyBody: '_暂时还没有可接管的工作区~ 在 desktop 端打开/创建一个会话再来_', + emptyBody: '_暂时还没有可接管的工作区~ 在 desktop 端打开/创建一个任务再来_', hint: '点工作区往下走;点 🚪 退出 取消这次', attachedSwitchHint: (sessionTitle: string) => - `🎮 当前接管中:**${sessionTitle}**\n选个新会话直接换乘;点 🚪 退出则保持现状`, + `🎮 当前接管中:**${sessionTitle}**\n选个新任务直接换乘;点 🚪 退出则保持现状`, btnExit: '🚪 撤了', resolvedExit: '🚪 走了,下次想远程操控再 /ctr', sessionPickerTitle: (displayName: string) => `🎮 ${displayName} 里的存档`, - sessionPickerHint: '挑个会话继续打 · ➕ 新建 开新存档 · ↩️ 后退 换工作区 · 🚪 退出 取消', + sessionPickerHint: '挑个任务继续打 · ➕ 新建 开新存档 · ↩️ 后退 换工作区 · 🚪 退出 取消', sessionPickerEmptyBody: (displayName: string) => - `_工作区 **${displayName}** 这边还没有 active 会话~ 不如点 ➕ 新建 开一个?_`, + `_工作区 **${displayName}** 这边还没有 active 任务~ 不如点 ➕ 新建 开一个?_`, btnNew: '➕ 新建', btnBack: '↩️ 后退', resolvedSessionPick: (sessionTitle: string, workspaceName: string) => - `🎯 上号了:**${sessionTitle}**(${workspaceName})\n接下来你发消息我就直接进这个会话;想撤随时 \`/exctr\``, + `🎯 上号了:**${sessionTitle}**(${workspaceName})\n接下来你发消息我就直接进这个任务;想撤随时 \`/exctr\``, resolvedNewSession: (workspaceName: string) => `✨ 新存档已建 + 接管完成(在 **${workspaceName}** 里)\n直接发指令开聊;想退就 \`/exctr\``, attachFailed: (reason: string) => `❌ 没接上:${reason}`, - sessionBusyOldCardPlaceholder: '⏳ 那个会话还在跑——下方给你刷了张新卡片,重选一下吧', + sessionBusyOldCardPlaceholder: '⏳ 那个任务还在跑——下方给你刷了张新卡片,重选一下吧', sessionBusyPrompts: [ (sessionTitle: string) => `⏳ **${sessionTitle}** 这会儿正在 BOSS 战~\nagent 还在思考/敲代码,等它这把打完再 \`/ctr\` 上号`, diff --git a/apps/desktop/src/main/localDb/__tests__/conversationSearchIpc.test.ts b/apps/desktop/src/main/localDb/__tests__/conversationSearchIpc.test.ts index 9802cb49646..dcf43d650bf 100644 --- a/apps/desktop/src/main/localDb/__tests__/conversationSearchIpc.test.ts +++ b/apps/desktop/src/main/localDb/__tests__/conversationSearchIpc.test.ts @@ -45,10 +45,10 @@ beforeEach(() => { describe('local-db:conversations:search — unnamedLabel 透传', () => { it('把 renderer 的已解析文案原样带给 searchConversations', async () => { - await handler()(null, { query: 'needle', unnamedLabel: '未命名对话' }); + await handler()(null, { query: 'needle', unnamedLabel: '未命名任务' }); expect(h.searchConversations).toHaveBeenCalledWith( - expect.objectContaining({ query: 'needle', unnamedLabel: '未命名对话' }), + expect.objectContaining({ query: 'needle', unnamedLabel: '未命名任务' }), ); }); diff --git a/apps/desktop/src/main/maker-host/__tests__/claudeAutoPermissionFallback.test.ts b/apps/desktop/src/main/maker-host/__tests__/claudeAutoPermissionFallback.test.ts index 8cd033eb2f7..6aa3ab4d0de 100644 --- a/apps/desktop/src/main/maker-host/__tests__/claudeAutoPermissionFallback.test.ts +++ b/apps/desktop/src/main/maker-host/__tests__/claudeAutoPermissionFallback.test.ts @@ -268,7 +268,7 @@ describe('createClaudeAutoClassifierFailureObserver', () => { { now: () => t }, ); - // 两个会话各自累计 2 段,交错到达 —— 谁都不该被对方推过阈值。 + // 两个任务各自累计 2 段,交错到达 —— 谁都不该被对方推过阈值。 for (let i = 0; i < 2; i += 1) { observer(ctx({ status: 429 })); observer(ctx({ status: 429, requestHeaders: { 'x-claude-code-session-id': 'sdk-2' } })); diff --git a/apps/desktop/src/main/maker-ipc/__tests__/interruptedTurnAutoResume.test.ts b/apps/desktop/src/main/maker-ipc/__tests__/interruptedTurnAutoResume.test.ts index f1f79c3af93..7eea4919e2f 100644 --- a/apps/desktop/src/main/maker-ipc/__tests__/interruptedTurnAutoResume.test.ts +++ b/apps/desktop/src/main/maker-ipc/__tests__/interruptedTurnAutoResume.test.ts @@ -305,7 +305,7 @@ describe('InterruptedTurnAutoResumeGuard', () => { expect(decision.action).toBe('resume'); if (decision.action === 'resume') { expect(decision.attempt, '本轮计数已归零').toBe(1); - // 会话累计不重置 —— 它是"这个会话到底重连过多少次"的展示值。 + // 会话累计不重置 —— 它是"这个任务到底重连过多少次"的展示值。 expect(decision.sessionTotal).toBe(INTERRUPTED_TURN_MAX_CONSECUTIVE_ATTEMPTS + 1); } }); @@ -421,7 +421,7 @@ describe('InterruptedTurnAutoResumeGuard', () => { g.guard.onInterruptedTurn(SID, runInterruptedTurn(g)); } expect(g.guard.onInterruptedTurn(SID, runInterruptedTurn(g)).action).toBe('exhausted'); - // 另一个会话自己的账:第一次照常放行。 + // 另一个任务自己的账:第一次照常放行。 const otherDecision = g.guard.onInterruptedTurn(other, g.now()); expect(otherDecision.action).toBe('resume'); if (otherDecision.action === 'resume') expect(otherDecision.sessionTotal).toBe(1); diff --git a/apps/desktop/src/main/maker-ipc/__tests__/orcaLifecycleService.test.ts b/apps/desktop/src/main/maker-ipc/__tests__/orcaLifecycleService.test.ts index 1e880c9a11b..9cedf4438d8 100644 --- a/apps/desktop/src/main/maker-ipc/__tests__/orcaLifecycleService.test.ts +++ b/apps/desktop/src/main/maker-ipc/__tests__/orcaLifecycleService.test.ts @@ -297,7 +297,7 @@ describe('OrcaLifecycleService', () => { entrypoint: 'create_worker', context: 'create_worker/worker-session-1/worker-ready-placeholder', }); - expect(ORCA_WORKER_READY_MESSAGE).toBe('[系统] Orca Worker 会话已就绪,等待 Lead 分配任务。'); + expect(ORCA_WORKER_READY_MESSAGE).toBe('[系统] Orca Worker 已就绪,等待 Lead 分配任务。'); expect(calls).toEqual([ 'createWorkerInTeam:team-existing:reviewer', 'sendWorkerReadyPlaceholder:create_worker:create_worker/worker-session-1/worker-ready-placeholder', diff --git a/apps/desktop/src/main/maker-ipc/helpKnowledge.generated.ts b/apps/desktop/src/main/maker-ipc/helpKnowledge.generated.ts index bc8f3831765..28e23d14dd3 100644 --- a/apps/desktop/src/main/maker-ipc/helpKnowledge.generated.ts +++ b/apps/desktop/src/main/maker-ipc/helpKnowledge.generated.ts @@ -28,7 +28,7 @@ export const HELP_KNOWLEDGE: HelpKnowledgeDoc[] = [ id: "collaboration", title: "Collaboration mode (lead + workers)", summary: "Turn on Collab to bring one or more worker agents in beside your session and delegate tasks to them.", - content: "Collaboration (\"协同\" / Collab) lets one session — the **lead** — bring in one or more **worker** agents to run alongside it (a \"team\"). The lead delegates tasks to workers through a built-in MCP, and you can watch and steer each worker.\n\n**Turning it on:**\n\n- In a project session — local or SSH remote — click the **Collab** pill in the composer toolbar. Both Claude Code and Codex sessions can be the lead, on either side.\n- Pick the first worker's agent (Claude Code or Codex), and optionally its role / model / effort / an initial task.\n- Start. This creates the team plus that first worker.\n\n**Adding more workers:**\n\n- From the collaboration tab's worker list, use **Create new worker** (+) to add more.\n- Default limits are a **soft cap of 5** and a **hard cap of 8** workers per lead — you'll see a warning past the soft cap, and creation is blocked at the hard cap. Both are adjustable (1–20) in the Collaboration section under **Settings > General**.\n\n**While collaborating:**\n\n- Each worker is a full session with its own history, tools, working directory, and model, and runs with permissions bypassed so it doesn't stop to ask. It inherits the lead's working directory by default (or shares the worktree when the lead uses one).\n- A worker's model defaults to your last \"New Chat\" choice for that vendor, falling back to the lead's model.\n- Only one worker is **focused** (shown) at a time — switch between them with the worker tabs / dropdown. You can intervene in any worker directly, and archive workers individually.\n\n**Turning it off:**\n\n- Closing collab **confirms with you first** (in-progress work is lost), then ends the workflow and archives **all** the team's workers. The conversation history stays searchable.\n\n**Notes:**\n\n- The lead must be a Claude Code / Codex **project** session — local **or SSH remote**. On a remote lead, workers are spawned on the **same remote host**, and the collaboration MCP reaches the remote side over an SSH remote-forward to the local bridge (with a persistent bearer token). A worker can't start its own sub-collab (no nesting).\n- A lead can have only **one active collaboration workflow** at a time. If you have two clients open on the same account (e.g. dev + release) and start collab on the same session in both, the second fails with \"开启协同失败\" because a workflow is already active — close one client or end the existing collab.", + content: "Collaboration (\"协同\" / Collab) lets one session — the **lead** — bring in one or more **worker** agents to run alongside it (a \"team\"). The lead delegates tasks to workers through a built-in MCP, and you can watch and steer each worker.\n\n**Turning it on:**\n\n- In a project session — local or SSH remote — click the **Collab** pill in the composer toolbar. Both Claude Code and Codex sessions can be the lead, on either side.\n- Pick the first worker's agent (Claude Code or Codex), and optionally its role / model / effort / an initial task.\n- Start. This creates the team plus that first worker.\n\n**Adding more workers:**\n\n- From the collaboration tab's worker list, use **Create new worker** (+) to add more.\n- Default limits are a **soft cap of 5** and a **hard cap of 8** workers per lead — you'll see a warning past the soft cap, and creation is blocked at the hard cap. Both are adjustable (1–20) in the Collaboration section under **Settings > General**.\n\n**While collaborating:**\n\n- Each worker is a full session with its own history, tools, working directory, and model, and runs with permissions bypassed so it doesn't stop to ask. It inherits the lead's working directory by default (or shares the worktree when the lead uses one).\n- A worker's model defaults to your last new-session choice for that vendor, falling back to the lead's model.\n- Only one worker is **focused** (shown) at a time — switch between them with the worker tabs / dropdown. You can intervene in any worker directly, and archive workers individually.\n\n**Turning it off:**\n\n- Closing collab **confirms with you first** (in-progress work is lost), then ends the workflow and archives **all** the team's workers. The conversation history stays searchable.\n\n**Notes:**\n\n- The lead must be a Claude Code / Codex **project** session — local **or SSH remote**. On a remote lead, workers are spawned on the **same remote host**, and the collaboration MCP reaches the remote side over an SSH remote-forward to the local bridge (with a persistent bearer token). A worker can't start its own sub-collab (no nesting).\n- A lead can have only **one active collaboration workflow** at a time. If you have two clients open on the same account (e.g. dev + release) and start collab on the same session in both, the second fails with \"开启协同失败\" because a workflow is already active — close one client or end the existing collab.", }, { id: "commands", @@ -60,7 +60,7 @@ export const HELP_KNOWLEDGE: HelpKnowledgeDoc[] = [ title: "Importing Codex and Claude conversations", summary: "Scan and import existing Codex or Claude Code chat history from Settings > Session Import.", tab: "import", - content: "If you already have Codex or Claude Code conversations on disk (from the CLIs), Import brings them into Cindy so they show up in the sidebar alongside your other sessions.\n\n**Importing:**\n\n- Open Settings > Session Import.\n- Click **Scan** to discover sessions in the standard Codex / Claude Code locations on your machine.\n- Filter the results:\n - by **source** — Codex or Claude Code;\n - by **placement** — by project (sessions inside a specific working directory) or by dialogue (free-form conversations not tied to a project).\n- Tick the sessions or projects you want, then click **Import**.\n- They appear in the left sidebar.\n\n**Notes:**\n\n- Importing is **one-way and non-destructive** — the originals on disk are left in place; nothing is synced back. Re-importing the same session is a no-op.\n- Imported threads are read-only history at first; you can continue them just like any other Cindy session.\n- This is for bringing in past conversations — it's unrelated to archiving / deleting sessions (see the Sidebar topic for those).", + content: "If you already have Codex or Claude Code conversations on disk (from the CLIs), Import brings them into Cindy so they show up in the sidebar alongside your other sessions.\n\n**Importing:**\n\n- Open Settings > Session Import.\n- Click **Scan** to discover sessions in the standard Codex / Claude Code locations on your machine.\n- Filter the results:\n - by **source** — Codex or Claude Code;\n - by **placement** — by project (sessions inside a specific working directory) or by chat (free-form sessions not tied to a project).\n- Tick the sessions or projects you want, then click **Import**.\n- They appear in the left sidebar.\n\n**Notes:**\n\n- Importing is **one-way and non-destructive** — the originals on disk are left in place; nothing is synced back. Re-importing the same session is a no-op.\n- Imported threads are read-only history at first; you can continue them just like any other Cindy session.\n- This is for bringing in past conversations — it's unrelated to archiving / deleting sessions (see the Sidebar topic for those).", }, { id: "integrations", @@ -132,13 +132,13 @@ export const HELP_KNOWLEDGE: HelpKnowledgeDoc[] = [ id: "sessions-and-chat", title: "Sessions and the chat thread", summary: "Create sessions, send / stop / queue messages, clear context, and switch between sessions in the sidebar.", - content: "Each **session** is a persistent chat thread driven by **one agent** (Claude Code or Codex). All your chats live as sessions in the left sidebar.\n\n**Starting a session:**\n\n- Click **+ New** at the top of the sidebar.\n- Pick the agent (Claude Code or Codex) and — for code work — a working directory.\n- Pick a model in the composer toolbar if you want something other than the default.\n- Type your first message and send.\n\n**During a turn:**\n\n- While the agent is replying, **Send** becomes **Stop** — click it to interrupt the current turn cleanly.\n- You can keep typing while the agent is busy — follow-ups are **queued** in order and sent one after another when the previous turn finishes.\n\n**Conversation controls:**\n\n- **`/clear`** — clears the current session's conversation context in place (resets messages and state without creating or switching to a new session). The session itself stays in the sidebar; only its content is wiped.\n- **`/compact`** — ask the agent to summarize and compress earlier turns into a shorter context (Claude Code).\n- **Edit** — you can edit your **last** user message. Doing so **rewinds** the conversation: it drops that message and everything after it, then resends your edited message. In sessions with file-rewind support (local git working directories with savepoints), file changes are also rolled back. In sessions without savepoints (remote sessions, non-git directories, or Codex sessions that haven't created savepoints), only the conversation is rewound — filesystem changes remain.\n\n**Switching between sessions:**\n\n- Click any session in the sidebar to switch to it. State is persisted — each session remembers its agent, model, working directory, attachments, etc.\n- Keep **unrelated tasks in separate sessions** — sharing one session for everything dilutes the agent's context and confuses it.\n\n**Notes:**\n\n- Sessions are **per local user** — they live in the local SQLite DB at `/cindy-.db` (installs migrated from the legacy app get their sessions copied from the old `xdt-maker-.db` on first login). They don't sync to other machines automatically.\n- If you have two desktop instances open (e.g. dev + release) on the same user account, both read/write the same DB and will see each other's sessions.", + content: "Each **session** is a persistent chat thread driven by **one agent** (Claude Code or Codex). All your chats live as sessions in the left sidebar.\n\n**Starting a session:**\n\n- Click **+ New** at the top of the sidebar.\n- Pick the agent (Claude Code or Codex) and — for code work — a working directory.\n- Pick a model in the composer toolbar if you want something other than the default.\n- Type your first message and send.\n\n**During a turn:**\n\n- While the agent is replying, **Send** becomes **Stop** — click it to interrupt the current turn cleanly.\n- You can keep typing while the agent is busy — follow-ups are **queued** in order and sent one after another when the previous turn finishes.\n\n**Chat controls:**\n\n- **`/clear`** — clears the current session's conversation context in place (resets messages and state without creating or switching to a new session). The session itself stays in the sidebar; only its content is wiped.\n- **`/compact`** — ask the agent to summarize and compress earlier turns into a shorter context (Claude Code).\n- **Edit** — you can edit your **last** user message. Doing so **rewinds** the conversation: it drops that message and everything after it, then resends your edited message. In sessions with file-rewind support (local git working directories with savepoints), file changes are also rolled back. In sessions without savepoints (remote sessions, non-git directories, or Codex sessions that haven't created savepoints), only the conversation is rewound — filesystem changes remain.\n\n**Switching between sessions:**\n\n- Click any session in the sidebar to switch to it. State is persisted — each session remembers its agent, model, working directory, attachments, etc.\n- Keep **unrelated tasks in separate sessions** — sharing one session for everything dilutes the agent's context and confuses it.\n\n**Notes:**\n\n- Sessions are **per local user** — they live in the local SQLite DB at `/cindy-.db` (installs migrated from the legacy app get their sessions copied from the old `xdt-maker-.db` on first login). They don't sync to other machines automatically.\n- If you have two desktop instances open (e.g. dev + release) on the same user account, both read/write the same DB and will see each other's sessions.", }, { id: "sidebar", title: "Finding and organizing sessions", summary: "Browse, group, filter, search, and pin sessions or projects in the left sidebar.", - content: "The left sidebar lists your sessions. It's the main way to navigate and organize.\n\n**Grouping:**\n\n- Switch grouping between **by project** (working directory) and **by date** — those are the two options.\n\n**Filtering:**\n\n- **Status** — active / archived / all.\n- **Project** — multi-select working directories, or \"all\".\n- **Agent** — Claude Code / Codex / all.\n- **Recent activity** — last 1 day / 3 days / 7 days / 30 days / all.\n\n**Pinning:**\n\n- Right-click a session and pick **Pin** to keep it at the top. There's no cap on the number of pinned sessions; a newly pinned session goes to the **front** of the pin order (most recent pin first).\n- Right-click a pinned session and pick **Unpin** to remove the pin.\n- Open a project's overflow menu and pick **Pin project** to move the whole project into **Pinned**. Project pins are independent from session pins, remain available after a restart or project rename, and can be expanded or collapsed inside **Pinned**.\n- Pinned projects and sessions share one draggable order. Newly pinned items move to the front, while the other pinned items keep their relative order. Use **Unpin project** to return a project to its position under the current project sorting mode.\n- The **Pinned** section supports the same **Text**, **List**, and **Card** display modes for both sessions and projects.\n\n**Searching:**\n\n- The search box matches against the **session title only** — not message content and not the working directory path. If you can't remember a session's title, browse via project / date / agent filters instead.\n\n**Right-click actions:**\n\n- On a regular session: **Pin / Unpin**, **Rename**, **Move to project** (submenu), **Copy conversation link** (a `cindy://session/` deep link), **Open in new window**, **Archive**, **Delete**.\n- On an archived session: **Rename**, **Unarchive**, **Copy conversation link**, **Delete**.\n\n**Session statuses:**\n\n- Sessions are **active**, **archived**, or **deleted** — there are no other states. Archiving hides a session from the default view without removing it; delete is unrecoverable from the UI.\n\n**Notes:**\n\n- Click the sidebar collapse arrow to shrink it to an icon-only strip — that's purely visual, you don't lose any features.\n- **Pin state and order** are persisted in `/sidebar-settings.json` and are shared across windows. **Filter settings** are stored per-window (in that window's local storage), so they don't sync between, say, a dev window and the installed app.", + content: "The left sidebar lists your sessions. It's the main way to navigate and organize.\n\n**Grouping:**\n\n- Switch grouping between **by project** (working directory) and **by date** — those are the two options.\n\n**Filtering:**\n\n- **Status** — active / archived / all.\n- **Project** — multi-select working directories, or \"all\".\n- **Agent** — Claude Code / Codex / all.\n- **Recent activity** — last 1 day / 3 days / 7 days / 30 days / all.\n\n**Pinning:**\n\n- Right-click a session and pick **Pin** to keep it at the top. There's no cap on the number of pinned sessions; a newly pinned session goes to the **front** of the pin order (most recent pin first).\n- Right-click a pinned session and pick **Unpin** to remove the pin.\n- Open a project's overflow menu and pick **Pin project** to move the whole project into **Pinned**. Project pins are independent from session pins, remain available after a restart or project rename, and can be expanded or collapsed inside **Pinned**.\n- Pinned projects and sessions share one draggable order. Newly pinned items move to the front, while the other pinned items keep their relative order. Use **Unpin project** to return a project to its position under the current project sorting mode.\n- The **Pinned** section supports the same **Text**, **List**, and **Card** display modes for both sessions and projects.\n\n**Searching:**\n\n- The search box matches against the **session title only** — not message content and not the working directory path. If you can't remember a session's title, browse via project / date / agent filters instead.\n\n**Right-click actions:**\n\n- On a regular session: **Pin / Unpin**, **Rename**, **Move to project** (submenu), **Copy session link** (a `cindy://session/` deep link), **Open in new window**, **Archive**, **Delete**.\n- On an archived session: **Rename**, **Unarchive**, **Copy session link**, **Delete**.\n\n**Session statuses:**\n\n- Sessions are **active**, **archived**, or **deleted** — there are no other states. Archiving hides a session from the default view without removing it; delete is unrecoverable from the UI.\n\n**Notes:**\n\n- Click the sidebar collapse arrow to shrink it to an icon-only strip — that's purely visual, you don't lose any features.\n- **Pin state and order** are persisted in `/sidebar-settings.json` and are shared across windows. **Filter settings** are stored per-window (in that window's local storage), so they don't sync between, say, a dev window and the installed app.", }, { id: "skills", diff --git a/apps/desktop/src/main/maker-ipc/orcaLifecycleService.ts b/apps/desktop/src/main/maker-ipc/orcaLifecycleService.ts index cd063301d69..462ad2c542b 100644 --- a/apps/desktop/src/main/maker-ipc/orcaLifecycleService.ts +++ b/apps/desktop/src/main/maker-ipc/orcaLifecycleService.ts @@ -10,7 +10,7 @@ import type { OrcaWorkerCreationResult, } from './orcaWorkerCreationService.js'; -export const ORCA_WORKER_READY_MESSAGE = '[系统] Orca Worker 会话已就绪,等待 Lead 分配任务。'; +export const ORCA_WORKER_READY_MESSAGE = '[系统] Orca Worker 已就绪,等待 Lead 分配任务。'; /** 开启协同时的一次性入参;负责把 UI/MCP 的 worker 偏好归一到 worker 创建内核。 */ export interface OrcaEnableTeamParams { diff --git a/apps/desktop/src/main/maker-ipc/register.ts b/apps/desktop/src/main/maker-ipc/register.ts index 5a2aa07d7cd..5ff66be916d 100644 --- a/apps/desktop/src/main/maker-ipc/register.ts +++ b/apps/desktop/src/main/maker-ipc/register.ts @@ -1346,7 +1346,7 @@ const renameSessionsConfirmBridge = new RenameSessionsConfirmBridge({ broadcast: (channel, payload) => broadcastToAllWindows(channel, payload), logger: log, onDesktopOnlyConfirmPending: (sessionId) => - desktopConfirmImNotifier(sessionId, '「批量重命名会话」的确认卡'), + desktopConfirmImNotifier(sessionId, '「批量重命名任务」的确认卡'), }); /** @@ -5328,7 +5328,7 @@ export function registerMakerIpc(maker: Maker, options: RegisterMakerIpcOptions) return { ok: false, errorCode: 'INVALID_ARGS', - message: 'working_dir 暂不支持远程会话(远端路径无法在本机校验)', + message: 'working_dir 暂不支持远程任务(远端路径无法在本机校验)', }; } const checked = await validateHandoffWorkingDir(workingDirOverride); @@ -5911,7 +5911,7 @@ export function registerMakerIpc(maker: Maker, options: RegisterMakerIpcOptions) return { ok: false, errorCode: 'NO_PRIOR_ASSISTANT', - message: '原会话还没有可用于分叉的 Agent 回复', + message: '原任务还没有可用于分叉的 Agent 回复', }; } diff --git a/apps/desktop/src/main/maker-ipc/sessionReferenceResolver.ts b/apps/desktop/src/main/maker-ipc/sessionReferenceResolver.ts index 6cd1dcdfbdf..dad510051f4 100644 --- a/apps/desktop/src/main/maker-ipc/sessionReferenceResolver.ts +++ b/apps/desktop/src/main/maker-ipc/sessionReferenceResolver.ts @@ -210,7 +210,7 @@ function fitSerializedPayloadBudget( else { throw new SessionReferenceError( 'SESSION_REFERENCE_INVALID', - '会话引用元数据超过整次请求预算', + '任务引用元数据超过整次请求预算', ); } } @@ -365,7 +365,7 @@ async function resolveLocal( .from(sessions) .where(eq(sessions.id, ref.sessionId)) .limit(1); - if (!session) throw new SessionReferenceError('SESSION_REFERENCE_NOT_FOUND', `找不到本机会话 ${ref.sessionId}`); + if (!session) throw new SessionReferenceError('SESSION_REFERENCE_NOT_FOUND', `找不到本机任务 ${ref.sessionId}`); const loaded = await readLocalRows(ref, messageLimit, session.clearedAt ?? null); // A recent local snapshot may legitimately end at a partial assistant // message when the turn failed. Preserve only a safe status marker; never @@ -385,7 +385,7 @@ async function resolveLocal( .filter((entry): entry is { message: AgentInputSessionReferenceMessage; isAnchor: boolean } => entry.message !== null); const mapped = mappedWithAnchor.map((entry) => entry.message); if (mapped.length === 0) { - throw new SessionReferenceError('SESSION_REFERENCE_NOT_FOUND', `会话 ${ref.sessionId} 没有可引用的可见消息`); + throw new SessionReferenceError('SESSION_REFERENCE_NOT_FOUND', `任务 ${ref.sessionId} 没有可引用的可见消息`); } const anchorIndex = ref.messageClientId ? mappedWithAnchor.findIndex((entry) => entry.isAnchor) @@ -417,7 +417,7 @@ function remoteFailure(ref: AgentInputSessionRef, response: Awaited; if (!Array.isArray(page.items) || typeof page.hasMore !== 'boolean') { - throw new SessionReferenceError('SESSION_REFERENCE_NOT_FOUND', `来源设备返回了无效的会话历史`); + throw new SessionReferenceError('SESSION_REFERENCE_NOT_FOUND', `来源设备返回了无效的对话记录`); } return { items: page.items.filter((row): row is Record => @@ -485,7 +485,7 @@ function parseRemoteHistoryPage(ref: AgentInputSessionRef, value: unknown): Remo ) { throw new SessionReferenceError( 'SESSION_REFERENCE_NOT_FOUND', - `鏉ユ簮璁惧杩斿洖浜嗘棤鏁堢殑浼氳瘇鍘嗗彶`, + `来源设备返回了无效的对话记录`, ); } const cursor = page.nextCursor as Record; @@ -498,7 +498,7 @@ function parseRemoteHistoryPage(ref: AgentInputSessionRef, value: unknown): Remo ) { throw new SessionReferenceError( 'SESSION_REFERENCE_NOT_FOUND', - `鏉ユ簮璁惧杩斿洖浜嗘棤鏁堢殑浼氳瘇鍘嗗彶`, + `来源设备返回了无效的对话记录`, ); } return { @@ -598,7 +598,7 @@ async function resolveRemote( tokenBudget: number, ): Promise<{ context: AgentInputSessionReferenceContext; usedTokens: number }> { if (!ref.deviceId) { - throw new SessionReferenceError('SESSION_REFERENCE_NOT_FOUND', `会话 ${ref.sessionId} 没有在线来源设备`); + throw new SessionReferenceError('SESSION_REFERENCE_NOT_FOUND', `任务 ${ref.sessionId} 没有在线来源设备`); } let sessionResponse: Awaited>; try { @@ -611,7 +611,7 @@ async function resolveRemote( } if (sessionResponse.ok !== true) remoteFailure(ref, sessionResponse); if (!sessionResponse.result || typeof sessionResponse.result !== 'object') { - throw new SessionReferenceError('SESSION_REFERENCE_NOT_FOUND', `来源设备上不存在会话 ${ref.sessionId}`); + throw new SessionReferenceError('SESSION_REFERENCE_NOT_FOUND', `来源设备上不存在任务 ${ref.sessionId}`); } const remoteSession = sessionResponse.result as Record; const remoteClearedAt = timestampToMs(remoteSession.clearedAt); @@ -695,7 +695,7 @@ async function resolveRemote( ); } if (mapped.length === 0) { - throw new SessionReferenceError('SESSION_REFERENCE_NOT_FOUND', `会话 ${ref.sessionId} 没有可引用的可见消息`); + throw new SessionReferenceError('SESSION_REFERENCE_NOT_FOUND', `任务 ${ref.sessionId} 没有可引用的可见消息`); } const fitted = fitMessagesToTokenBudget(mapped, tokenBudget, anchorIndex); return { @@ -725,7 +725,7 @@ export async function resolveSessionReferences( if (refs.length > MAX_SESSION_REFERENCES) { throw new SessionReferenceError( 'SESSION_REFERENCE_INVALID', - `单条消息最多引用 ${MAX_SESSION_REFERENCES} 个会话,当前为 ${refs.length} 个`, + `单条消息最多引用 ${MAX_SESSION_REFERENCES} 个任务,当前为 ${refs.length} 个`, ); } const contexts: AgentInputSessionReferenceContext[] = []; @@ -734,14 +734,14 @@ export async function resolveSessionReferences( for (let index = 0; index < refs.length; index++) { const ref = refs[index]; if (!ref?.sessionId.trim()) { - throw new SessionReferenceError('SESSION_REFERENCE_INVALID', '会话引用缺少 sessionId'); + throw new SessionReferenceError('SESSION_REFERENCE_INVALID', '任务引用缺少 sessionId'); } if ( ref.sessionId.length > MAX_REFERENCE_ID_LENGTH || (ref.deviceId?.length ?? 0) > MAX_REFERENCE_ID_LENGTH || (ref.messageClientId?.length ?? 0) > MAX_REFERENCE_ID_LENGTH ) { - throw new SessionReferenceError('SESSION_REFERENCE_INVALID', '会话引用标识过长'); + throw new SessionReferenceError('SESSION_REFERENCE_INVALID', '任务引用标识过长'); } const refsLeft = refs.length - index; const messageLimit = Math.max(1, Math.floor(remainingMessages / refsLeft)); diff --git a/apps/desktop/src/main/notificationService.ts b/apps/desktop/src/main/notificationService.ts index 324261326de..a119e5a5500 100644 --- a/apps/desktop/src/main/notificationService.ts +++ b/apps/desktop/src/main/notificationService.ts @@ -79,9 +79,9 @@ interface ShowSessionEventPayload { } /** - * Toast 文案分两层:title 同时标识 Cindy 与会话,body 放结构化终态。 + * Toast 文案分两层:title 同时标识 Cindy 与任务,body 放结构化终态。 * 不能只依赖 Windows AUMID / macOS bundle 元数据标识来源:不同系统和通知中心 - * 展示的 app 元数据并不一致,只显示会话名时容易被误认成同名插件主动发出的通知。 + * 展示的 app 元数据并不一致,只显示任务名时容易被误认成同名插件主动发出的通知。 */ function buildBody(kind: SessionEventKind): string { switch (kind) { @@ -105,7 +105,7 @@ function buildFeishuText(title: string, kind: SessionEventKind): string { : kind === 'error' ? '执行失败' : '已完成 ✓'; - return `${CLIENT_NOTIFICATION_NAME} · 会话「${title}」${status}`; + return `${CLIENT_NOTIFICATION_NAME} · 任务「${title}」${status}`; } // 防 GC:Electron Notification 实例如果不持引用,JS 引擎可能在 toast 还在显示 @@ -147,7 +147,7 @@ export function showDesktopSessionEvent( ): void { const { sessionId, title, kind } = payload; if (sessionId) markSessionNeedsAttention(sessionId); - const safeTitle = title?.trim() || sessionId.slice(0, 8) || '会话'; + const safeTitle = title?.trim() || sessionId.slice(0, 8) || '任务'; showDesktopToast(safeTitle, kind, () => focusWindow(getWindow, sessionId)); } diff --git a/apps/desktop/src/main/scheduler-host/__tests__/runnerQueuedDispatch.test.ts b/apps/desktop/src/main/scheduler-host/__tests__/runnerQueuedDispatch.test.ts index 0af45864e26..c98d75d2cd2 100644 --- a/apps/desktop/src/main/scheduler-host/__tests__/runnerQueuedDispatch.test.ts +++ b/apps/desktop/src/main/scheduler-host/__tests__/runnerQueuedDispatch.test.ts @@ -781,7 +781,7 @@ describe('MakerScheduleRunner queued dispatch (busy bound session)', () => { }); // ── 排队等待:不占执行槽 + 有上限 ──────────────────────────────────────────── -// 2026-07-29 事故:目标会话长时间不空闲(用户在长对话里 / 那个会话自己卡死),队列 +// 2026-07-29 事故:目标会话长时间不空闲(用户在长对话里 / 那个任务自己卡死),队列 // 永不 drain,`await dispatchGate` 永不 settle —— 4 个心跳 run 各挂 3.5 小时,占满 // 全部执行槽,其余定时任务全部停摆。 describe('MakerScheduleRunner queued dispatch: slot accounting and wait cap', () => { diff --git a/apps/desktop/src/main/voice-input/__tests__/globalShortcut.test.ts b/apps/desktop/src/main/voice-input/__tests__/globalShortcut.test.ts index 35b99ea4efa..6988ecdab22 100644 --- a/apps/desktop/src/main/voice-input/__tests__/globalShortcut.test.ts +++ b/apps/desktop/src/main/voice-input/__tests__/globalShortcut.test.ts @@ -8,7 +8,7 @@ import type { GlobalVoiceInputIpcDeps } from '../global.js'; const mocks = vi.hoisted(() => { const handlers = new Map unknown>(); const registeredShortcuts = new Map void>(); - // 录制/挂起那两条 IPC 的合法发起方,同时也是 keys 转发的收件人 —— 建模成一个会话副窗口 + // 录制/挂起那两条 IPC 的合法发起方,同时也是 keys 转发的收件人 —— 建模成一个任务副窗口 // (设置页在副窗口里照样打得开)。所以它要有顶层 frame 与 once,好过得了那道授权闸。 const focusedWindowMainFrame = { url: 'http://localhost:5173/index.html' }; const focusedWindow = { @@ -473,7 +473,7 @@ describe('voice input global shortcut registration', () => { // 存盘就等于宣布「当前快捷键是这个新的」,那旧的必须同时失效。旧 accelerator 的注销 // 只写在成功路径上,缺权限时在那之前就返回了 —— 于是设置页显示「右 Option 待授权」, - // 而按 F16 整个会话里仍会触发语音输入。 + // 而按 F16 整个任务里仍会触发语音输入。 it('deactivates the previously registered accelerator when persisting a pending shortcut', async () => { setPlatform('darwin'); const { registerGlobalVoiceInputIpc } = await import('../global.js'); @@ -1577,7 +1577,7 @@ describe('voice input global shortcut registration', () => { }); // 按住说话的会话可能在录制框打开**之前**就已经 start 了; 挂起/替换 listener 会调 - // endActiveTriggerIfNeeded() 补发一次 end。把这个 end 也丢掉, 那个会话就永远停不下来 —— + // endActiveTriggerIfNeeded() 补发一次 end。把这个 end 也丢掉, 那个任务就永远停不下来 —— // listener 已经停了, 它还在录。所以只挡新激活。 it('still delivers end while recording but drops new activations', async () => { setPlatform('darwin'); @@ -1615,7 +1615,7 @@ describe('voice input global shortcut registration', () => { ); // 而先前那次按住的 end 必须送到 —— 挂起会调 endActiveTriggerIfNeeded() 补发它, 丢掉 - // 那个会话就永远停不下来。 + // 那个任务就永远停不下来。 mocks.listenerOptions.onTrigger?.('end'); expect(mocks.focusedWindow.webContents.send).toHaveBeenCalledWith( 'voice-input:global-shortcut-trigger', diff --git a/apps/desktop/src/renderer/__tests__/PermissionPrompt.i18n.test.tsx b/apps/desktop/src/renderer/__tests__/PermissionPrompt.i18n.test.tsx index 31e9b142475..2a14776a838 100644 --- a/apps/desktop/src/renderer/__tests__/PermissionPrompt.i18n.test.tsx +++ b/apps/desktop/src/renderer/__tests__/PermissionPrompt.i18n.test.tsx @@ -35,7 +35,7 @@ describe('PermissionPrompt i18n', () => { expect(screen.getByText('允许 PowerShell?')).toBeTruthy(); expect(screen.getByText('拒绝')).toBeTruthy(); - expect(screen.getByText('本对话总是允许')).toBeTruthy(); + expect(screen.getByText('本任务总是允许')).toBeTruthy(); expect(screen.getByText('允许一次')).toBeTruthy(); expect(screen.queryByText('Allow Codex to run this command?')).toBeNull(); expect(screen.getByText('Provider-supplied reason stays verbatim.')).toBeTruthy(); diff --git a/apps/desktop/src/renderer/__tests__/automationGeneratedSessions.test.ts b/apps/desktop/src/renderer/__tests__/automationGeneratedSessions.test.ts index 5ca269b42f8..3154de63231 100644 --- a/apps/desktop/src/renderer/__tests__/automationGeneratedSessions.test.ts +++ b/apps/desktop/src/renderer/__tests__/automationGeneratedSessions.test.ts @@ -972,12 +972,12 @@ describe('automation-generated sessions', () => { expect(runHistoryCardSource).toContain("run.costAttribution === 'legacy'"); expect(zh.scheduler.cell.totalCost).toBe('开销 {{cost}}'); expect(zh.scheduler.cell.totalValue).toBe('价值 {{value}}'); - expect(zh.scheduler.runs.sessionCost).toBe('对话开销 {{cost}}'); - expect(zh.scheduler.runs.sessionValue).toBe('对话价值 {{value}}'); + expect(zh.scheduler.runs.sessionCost).toBe('任务开销 {{cost}}'); + expect(zh.scheduler.runs.sessionValue).toBe('任务价值 {{value}}'); expect(zh.scheduler.runs.runCost).toBe('本次开销 {{cost}}'); expect(zh.scheduler.runs.legacyCostUnavailable).toBe('历史费用无法拆分'); expect(zh.scheduler.runs.persistentSessionGroup).toBe( - '持续对话 {{session}} · {{count}} 次运行', + '持续任务 {{session}} · {{count}} 次运行', ); expect(zh.scheduler.runs.expandRemainingRuns).toBe('展开另外 {{count}} 次'); }); diff --git a/apps/desktop/src/renderer/__tests__/builtinToolsCollabDescriptionI18n.test.ts b/apps/desktop/src/renderer/__tests__/builtinToolsCollabDescriptionI18n.test.ts index cdccd5b6a3e..010d157d4b7 100644 --- a/apps/desktop/src/renderer/__tests__/builtinToolsCollabDescriptionI18n.test.ts +++ b/apps/desktop/src/renderer/__tests__/builtinToolsCollabDescriptionI18n.test.ts @@ -4,7 +4,7 @@ import { resolve } from 'node:path'; import { describe, expect, it } from 'vitest'; const expectedDescriptions = { - 'zh-CN': '关闭后仅影响后续新建的对话,不会中止当前 Worker;用于多 Worker 团队编排', + 'zh-CN': '关闭后仅影响后续新建的任务,不会中止当前 Worker;用于多 Worker 团队编排', en: 'Turning it off only affects newly created sessions and does not stop active workers; supports multi-worker team orchestration', ja: 'オフにしても今後作成するセッションにのみ反映され、現在の Worker は停止しません。マルチ Worker のチーム編成に使用します', ko: '끄면 이후 새로 만드는 세션에만 적용되며 현재 Worker는 중지되지 않습니다. 멀티 Worker 팀 편성에 사용합니다', diff --git a/apps/desktop/src/renderer/__tests__/conversationSearchBox.test.ts b/apps/desktop/src/renderer/__tests__/conversationSearchBox.test.ts index d5e6c02008e..d76c8111b09 100644 --- a/apps/desktop/src/renderer/__tests__/conversationSearchBox.test.ts +++ b/apps/desktop/src/renderer/__tests__/conversationSearchBox.test.ts @@ -310,7 +310,7 @@ describe('ConversationSearchBox live search', () => { }); // 搜索结果行是「列表之外」的第二个标题出口:本 PR 早前只投影了侧边栏 / 会话头 / tab, - // 结果行仍渲染原始 title,于是列表写「未命名对话」、搜索结果写 "New Maker"。 + // 结果行仍渲染原始 title,于是列表写「未命名任务」、搜索结果写 "New Maker"。 // 匹配串(main 算下标)与渲染串(这里)必须是同一个 conversationSearchTitle 的结果。 it('未起名会话在结果行里显示兜底文案,并把 unnamedLabel 带进请求', async () => { vi.mocked(searchConversations).mockResolvedValue({ diff --git a/apps/desktop/src/renderer/__tests__/makerChatStoreAutoName.test.ts b/apps/desktop/src/renderer/__tests__/makerChatStoreAutoName.test.ts index c1de632d8f6..c61046a3b16 100644 --- a/apps/desktop/src/renderer/__tests__/makerChatStoreAutoName.test.ts +++ b/apps/desktop/src/renderer/__tests__/makerChatStoreAutoName.test.ts @@ -265,7 +265,7 @@ describe('makerChatStore auto-name — 本机会话', () => { it('较早那次尝试失败时不撤回预览 —— 更晚的尝试仍在飞', async () => { // 用户在首次 auto-title 返回前连发两条:两次都通过 autoNameSettled 检查各起一次尝试。 // 早的那次失败若按 sessionId 直接撤回,会把仍在飞的那次的预览一起revoke,标题白闪 - // 一次「未命名对话」。 + // 一次「未命名任务」。 let failFirst: (v: { applied: boolean; done: boolean }) => void = () => {}; autoTitle.mockImplementationOnce(() => new Promise((resolve) => { failFirst = resolve; diff --git a/apps/desktop/src/renderer/__tests__/makerTransportRouting.test.ts b/apps/desktop/src/renderer/__tests__/makerTransportRouting.test.ts index fbbc0fe05a7..d322b43827d 100644 --- a/apps/desktop/src/renderer/__tests__/makerTransportRouting.test.ts +++ b/apps/desktop/src/renderer/__tests__/makerTransportRouting.test.ts @@ -55,7 +55,7 @@ function stubElectron() { const sess = (id: string): Session => ({ id }) as unknown as Session; describe('makerApiFor 路由(完整对等会话级操作)', () => { - it('远程 device-link 会话:每个会话级操作命中对应隧道 channel + 原样转发 args', async () => { + it('远程 device-link 会话:每个任务级操作命中对应隧道 channel + 原样转发 args', async () => { const { invoke } = stubElectron(); const { makerApiFor } = await import('@/lib/makerTransport'); const { remoteProjectsStore } = await import('@/features/device-link/remoteProjectsStore'); diff --git a/apps/desktop/src/renderer/__tests__/modelSelectorXhighSSoT.test.ts b/apps/desktop/src/renderer/__tests__/modelSelectorXhighSSoT.test.ts index 85e84cec1bb..cbc6b7c0bf8 100644 --- a/apps/desktop/src/renderer/__tests__/modelSelectorXhighSSoT.test.ts +++ b/apps/desktop/src/renderer/__tests__/modelSelectorXhighSSoT.test.ts @@ -13,7 +13,7 @@ * onXxxDidChange 上抛,由父组件 merge 精确值让 props 重新流下来——单一可信源。 * * 二、draft-effort-server-coupling(2026-07-17): - * 现象:登录令牌失效(401)后,草稿(未创建会话)里推理强度点了没反应。 + * 现象:登录令牌失效(401)后,草稿(未创建任务)里推理强度点了没反应。 * 根因:草稿分支 `await updatePreferences(服务端 PATCH)` 成功后才调 onEffortDidChange, * 服务端失败被 catch 静默吞掉 → UI 永不刷新。 * 修复:默认模型/档位偏好全量本地化(newMakerDraft.lastByVendor 按 agent 分槽 + diff --git a/apps/desktop/src/renderer/__tests__/newMakerProjectPicker.test.ts b/apps/desktop/src/renderer/__tests__/newMakerProjectPicker.test.ts index d6895de377f..9d036b6becf 100644 --- a/apps/desktop/src/renderer/__tests__/newMakerProjectPicker.test.ts +++ b/apps/desktop/src/renderer/__tests__/newMakerProjectPicker.test.ts @@ -734,7 +734,7 @@ describe('Shared create project picker', () => { * * refreshRemoteDeviceSessions 对瞬态错误退避重试,窗口最长约 6.75 秒。原来 handoff `await` 它才 * 返回,于是这段时间里应用被关掉 → 对端**已经**有了新会话,而用户的首条消息(或目标弹窗里刚写的 - * 内容)还没被 setPending / setPendingGoal 记录下来 → 重开再试就在对端建出第二个会话,第一个空着 + * 内容)还没被 setPending / setPendingGoal 记录下来 → 重开再试就在对端建出第二个任务,第一个空着 * 滞留;建目标那条还会连同只存在于弹窗内存里的编辑一起丢。 * * 交接本来就不需要等权威快照 —— 临时行(带对端真正分配的 workDir)已经足够让 SessionView 的 diff --git a/apps/desktop/src/renderer/__tests__/remoteHistoryOriginReconcile.test.ts b/apps/desktop/src/renderer/__tests__/remoteHistoryOriginReconcile.test.ts index b3c36c32437..65166b5c734 100644 --- a/apps/desktop/src/renderer/__tests__/remoteHistoryOriginReconcile.test.ts +++ b/apps/desktop/src/renderer/__tests__/remoteHistoryOriginReconcile.test.ts @@ -6,7 +6,7 @@ * 控制端启动时路由可能**先于** remote-projects bootstrap 恢复到某远程会话:此刻 * getSessionDeviceId 仍是 undefined → ensureInitialMessages 误命中控制端本机空库 * (被控端 row 不在本地)→ 拿到空历史且 historyLoaded=true 卡死,即使随后 mapping - * 注入也不再重试。修复:makerChatStore 记录每个会话「按哪个 origin 加载」, + * 注入也不再重试。修复:makerChatStore 记录每个任务「按哪个 origin 加载」, * remoteProjectsStore 注入 / 变更来源后 reconcileOpenSessionOrigins 检测漂移并经隧道重载。 * * 本测试直接调用导出的 reconcileOpenSessionOrigins(生产由 initGlobalListeners 的 diff --git a/apps/desktop/src/renderer/__tests__/sessionLinkPasteTitleProjection.test.ts b/apps/desktop/src/renderer/__tests__/sessionLinkPasteTitleProjection.test.ts index 4ed9d92d304..8f2ce544873 100644 --- a/apps/desktop/src/renderer/__tests__/sessionLinkPasteTitleProjection.test.ts +++ b/apps/desktop/src/renderer/__tests__/sessionLinkPasteTitleProjection.test.ts @@ -11,7 +11,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { DEFAULT_DRAFT_SESSION_TITLE } from '@cindy/maker-shared/session-title'; -const UNNAMED = '未命名对话'; +const UNNAMED = '未命名任务'; const sessionGet = vi.fn(); vi.mock('@/lib/sessionService', () => ({ diff --git a/apps/desktop/src/renderer/__tests__/sessionsStoreAutoTitlePreview.test.ts b/apps/desktop/src/renderer/__tests__/sessionsStoreAutoTitlePreview.test.ts index 20dbd23c083..41e382c2393 100644 --- a/apps/desktop/src/renderer/__tests__/sessionsStoreAutoTitlePreview.test.ts +++ b/apps/desktop/src/renderer/__tests__/sessionsStoreAutoTitlePreview.test.ts @@ -107,7 +107,7 @@ describe('sessionsStore — 预览必须活过全量刷新', () => { it('新建会话触发的 forceRefreshAll 不会把预览冲回哨兵', async () => { // 真实时序:createSession → 预览 → sessions:created push → forceRefreshAll, // 而那次重拉从 DB 拿回的行**仍带哨兵**(权威标题要等 auto-title 落库)。 - // 只写缓存会被冲掉 —— 标题先显示用户那句话、又退回「未命名对话」(review P1)。 + // 只写缓存会被冲掉 —— 标题先显示用户那句话、又退回「未命名任务」(review P1)。 await seed(session()); emitAutoTitlePreview(SESSION_ID, '帮我排查登录失败'); expect(currentTitle()).toBe('帮我排查登录失败'); @@ -208,7 +208,7 @@ describe('sessionsStore — 预览必须活过全量刷新', () => { it('权威标题晚于在飞的 list 请求写入 → 旧快照不许把它冲回哨兵', async () => { // 真实时序:sessions:created push → forceRefreshAll 起飞(DB 快照里还是哨兵) // → main 写完占位、sessions:patched 落进缓存 → 那个更早的请求才回来。 - // 没有版本化 override 的话,整桶覆盖会把权威标题冲掉,界面退到「未命名对话」 + // 没有版本化 override 的话,整桶覆盖会把权威标题冲掉,界面退到「未命名任务」 // 直到下一次刷新;乐观预览此刻已按「权威值到达即回收」的规则让位(review P1)。 await seed(session()); diff --git a/apps/desktop/src/renderer/__tests__/userMessageLinkify.test.ts b/apps/desktop/src/renderer/__tests__/userMessageLinkify.test.ts index bd8664c68ae..0bcf7250153 100644 --- a/apps/desktop/src/renderer/__tests__/userMessageLinkify.test.ts +++ b/apps/desktop/src/renderer/__tests__/userMessageLinkify.test.ts @@ -291,7 +291,7 @@ describe('userMessageLinkify', () => { it('matches markdown-form session links with an explicit label', () => { const sessionUrl = 'xdt-maker://session/03e0c22d-19db-4ac5-814f-1ea04040b471'; const md = `[修复语音输入白屏](${sessionUrl})`; - const matches = findLinkifyMatches(`帮我看下 ${md} 这个会话`); + const matches = findLinkifyMatches(`帮我看下 ${md} 这个任务`); expect(matches).toEqual([ { kind: 'session', diff --git a/apps/desktop/src/renderer/features/cc-agent/NewMakerDraftRoute.tsx b/apps/desktop/src/renderer/features/cc-agent/NewMakerDraftRoute.tsx index f96606873e5..bce05e52c3e 100644 --- a/apps/desktop/src/renderer/features/cc-agent/NewMakerDraftRoute.tsx +++ b/apps/desktop/src/renderer/features/cc-agent/NewMakerDraftRoute.tsx @@ -256,7 +256,7 @@ const DRAFT_IMAGE_URL_PREFIX = `xdt-image://${NEW_MAKER_DRAFT_KEY}/`; * **只对能证明一致的纯文本消息放行**。带 @mention / 附件 / 会话引用时,权威占位由 * `deriveAutoTitleSeed` 另行推导(剔除 mention 的 wire token、拿文件名合成描述),这里 * 算不出同一个串 —— 预览会先显示 A 再跳成 B,比短暂显示占位更糟。这类消息交给显示层的 - * 「未命名对话」兜底,等权威标题回流。 + * 「未命名任务」兜底,等权威标题回流。 * * 纯文本时两侧一致是可证明的:无 reference / 未编码引用标记时 `projectLiteralUserText` * 退化为 `text.trim()`,无 mention 时 `stripMentionTokens` 同样只做 trim,而 diff --git a/apps/desktop/src/renderer/features/cc-agent/SessionContentHeader.tsx b/apps/desktop/src/renderer/features/cc-agent/SessionContentHeader.tsx index 30e4700bfd9..ca21ff69535 100644 --- a/apps/desktop/src/renderer/features/cc-agent/SessionContentHeader.tsx +++ b/apps/desktop/src/renderer/features/cc-agent/SessionContentHeader.tsx @@ -200,9 +200,9 @@ export function SessionContentHeader({ const trimmed = raw.trim(); setIsEditing(false); // 「没改」= 与原始标题相同,**或**与预填的显示标题相同。后者不可省:未起名的 - // 会话预填的是本地化兜底文案(「未命名对话」),它不等于库里的英文哨兵 —— 只比 + // 会话预填的是本地化兜底文案(「未命名任务」),它不等于库里的英文哨兵 —— 只比 // session.title 的话,用户双击后原样回车就会把兜底文案写进库、把哨兵冲掉, - // 自动起名从此永久跳过这个会话(标题永远停在「未命名对话」)。 + // 自动起名从此永久跳过这个会话(标题永远停在「未命名任务」)。 const unchanged = !trimmed || trimmed === session.title || trimmed === displayTitle; if (remoteWritesBlocked) { if (!unchanged) showRemoteWriteBlockedToast(); @@ -263,7 +263,7 @@ export function SessionContentHeader({ t, ]); - /* ---- 复制对话链接(cindy://session/ 深链,与 SessionItem 同语义; + /* ---- 复制任务链接(cindy://session/ 深链,与 SessionItem 同语义; 远程会话把归属设备冻进 ?device=) ---- */ const handleCopyDeepLink = useCallback(async () => { try { diff --git a/apps/desktop/src/renderer/features/cc-agent/lib/__tests__/sessionDisplayTitle.test.ts b/apps/desktop/src/renderer/features/cc-agent/lib/__tests__/sessionDisplayTitle.test.ts index ecc2db31943..0e8824d9bf5 100644 --- a/apps/desktop/src/renderer/features/cc-agent/lib/__tests__/sessionDisplayTitle.test.ts +++ b/apps/desktop/src/renderer/features/cc-agent/lib/__tests__/sessionDisplayTitle.test.ts @@ -11,7 +11,7 @@ import { toStoredSessionTitle, } from '../sessionDisplayTitle'; -const UNNAMED = '未命名对话'; +const UNNAMED = '未命名任务'; function session(over: Partial = {}): Session { return { diff --git a/apps/desktop/src/renderer/features/cc-agent/lib/sessionDisplayTitle.ts b/apps/desktop/src/renderer/features/cc-agent/lib/sessionDisplayTitle.ts index e235536813a..790934d0afe 100644 --- a/apps/desktop/src/renderer/features/cc-agent/lib/sessionDisplayTitle.ts +++ b/apps/desktop/src/renderer/features/cc-agent/lib/sessionDisplayTitle.ts @@ -85,7 +85,7 @@ export function toStoredSessionTitle(session: Session, editedTitle: string): str * 就会错位、把高亮画到别的字上。两种情况必须关掉高亮: * * - `[Schedule] xxx` 前缀被剥掉(既有 case); - * - 哨兵标题被换成本地化的「未命名对话」(本次新增,同一个坑)。 + * - 哨兵标题被换成本地化的「未命名任务」(本次新增,同一个坑)。 */ export function canHighlightSessionDisplayTitle(session: Session): boolean { return !isScheduledSession(session) && !isDefaultDraftSessionTitle(session.title); diff --git a/apps/desktop/src/renderer/features/cc-agent/sidebar/SessionCard.tsx b/apps/desktop/src/renderer/features/cc-agent/sidebar/SessionCard.tsx index 3ae97f56257..069dbd7d02f 100644 --- a/apps/desktop/src/renderer/features/cc-agent/sidebar/SessionCard.tsx +++ b/apps/desktop/src/renderer/features/cc-agent/sidebar/SessionCard.tsx @@ -383,7 +383,7 @@ export function SessionCard({ } }, [session.deviceLinkDeviceId, session.id, t]); - // 单项「复制对话链接」:直接复制 cindy://session/ 深链。原「复制会话 ID」 + // 单项「复制任务链接」:直接复制 cindy://session/ 深链。原「复制会话 ID」 // 二级菜单(深度链接 / 仅 ID / Agent)已按产品决策收敛为这一项;不自带分隔线, // 分组由各使用点决定,避免菜单被切得过碎。 const copySessionIdSubmenu = ( diff --git a/apps/desktop/src/renderer/features/cc-agent/sidebar/SessionItem.tsx b/apps/desktop/src/renderer/features/cc-agent/sidebar/SessionItem.tsx index dd5d95fd9ff..bf5569cf4da 100644 --- a/apps/desktop/src/renderer/features/cc-agent/sidebar/SessionItem.tsx +++ b/apps/desktop/src/renderer/features/cc-agent/sidebar/SessionItem.tsx @@ -550,7 +550,7 @@ export const SessionItem = memo(function SessionItem({ } }, [session.deviceLinkDeviceId, session.id, t]); - // 单项「复制对话链接」:直接复制 cindy://session/ 深链(可粘贴到聊天里 + // 单项「复制任务链接」:直接复制 cindy://session/ 深链(可粘贴到聊天里 // 渲染成会话 chip)。原「复制会话 ID」二级菜单(深度链接 / 仅 ID / Agent)已按 // 产品决策收敛为这一项;不自带分隔线,分组由各使用点决定,避免菜单被切得过碎。 const copySessionIdSubmenu = ( diff --git a/apps/desktop/src/renderer/features/cc-agent/sidebar/__tests__/SessionCard.visual.test.ts b/apps/desktop/src/renderer/features/cc-agent/sidebar/__tests__/SessionCard.visual.test.ts index 7f685cdcebe..b0b440fe23c 100644 --- a/apps/desktop/src/renderer/features/cc-agent/sidebar/__tests__/SessionCard.visual.test.ts +++ b/apps/desktop/src/renderer/features/cc-agent/sidebar/__tests__/SessionCard.visual.test.ts @@ -53,7 +53,7 @@ vi.mock('react-i18next', () => ({ 'ccAgent.sidebar.sessionMenu.pin': '置顶', 'ccAgent.sidebar.sessionMenu.openInNewWindow': '新窗口打开', 'ccAgent.sidebar.sessionMenu.archived': '归档', - 'ccAgent.sidebar.sessionMenu.copySessionLink': '复制对话链接', + 'ccAgent.sidebar.sessionMenu.copySessionLink': '复制任务链接', }; return dict[key] ?? key; }, diff --git a/apps/desktop/src/renderer/features/cc-agent/sidebar/__tests__/sessionRowRenderIsolation.test.ts b/apps/desktop/src/renderer/features/cc-agent/sidebar/__tests__/sessionRowRenderIsolation.test.ts index 9b0c2dba519..876ff636698 100644 --- a/apps/desktop/src/renderer/features/cc-agent/sidebar/__tests__/sessionRowRenderIsolation.test.ts +++ b/apps/desktop/src/renderer/features/cc-agent/sidebar/__tests__/sessionRowRenderIsolation.test.ts @@ -5,11 +5,11 @@ * --------------------------------------------------------------------------- * 背景:2026-07 切换会话卡顿,实测左侧列表整栏重画单次 80-96ms、每次切换连跑 3 遍。 * 根源是行内订阅了"整张表"快照(attention Map / urgency Set 每次广播换新引用), - * 且 SessionItem 无 memo —— 任何一个会话的状态变化都让几百行全部重渲染。 + * 且 SessionItem 无 memo —— 任何一个任务的状态变化都让几百行全部重渲染。 * * 本测试钉住修复后的三条不变量(谁改坏了这里就红): * 1. SessionItem 必须保持 React.memo 包裹; - * 2. 某个会话的 attention 变化只重渲染它自己那一行,其它行不动; + * 2. 某个任务的 attention 变化只重渲染它自己那一行,其它行不动; * 3. urgency 集合内容不变时(即便上游产了新 Set 引用)任何行都不重渲染, * 变化时只重渲染受影响的行。 * diff --git a/apps/desktop/src/renderer/features/cc-agent/sidebar/sections/UnclassifiedSection.tsx b/apps/desktop/src/renderer/features/cc-agent/sidebar/sections/UnclassifiedSection.tsx index 1939a547b40..50fbe186e50 100644 --- a/apps/desktop/src/renderer/features/cc-agent/sidebar/sections/UnclassifiedSection.tsx +++ b/apps/desktop/src/renderer/features/cc-agent/sidebar/sections/UnclassifiedSection.tsx @@ -15,7 +15,7 @@ * 未绑定"的视觉壳。这里的实现:直接复用 SessionItem,让交互一致;外层不再叠加 * Draft 样式,因为未起名草稿的视觉与设计稿一致(普通 32px 行)。设计稿 italic * 的灰字仅用于纯文字提示态——本期所有 unclassified 都是真实 Session(包括未起名 - * 草稿),照普通行渲染即可,标题由 `lib/sessionDisplayTitle` 兜底成「未命名对话」。 + * 草稿),照普通行渲染即可,标题由 `lib/sessionDisplayTitle` 兜底成「未命名任务」。 */ import { SessionEntryList } from '../SessionEntryList'; diff --git a/apps/desktop/src/renderer/features/right-sidebar/__tests__/RightSidebarShell.test.ts b/apps/desktop/src/renderer/features/right-sidebar/__tests__/RightSidebarShell.test.ts index 620c346e6b3..2bc33eead54 100644 --- a/apps/desktop/src/renderer/features/right-sidebar/__tests__/RightSidebarShell.test.ts +++ b/apps/desktop/src/renderer/features/right-sidebar/__tests__/RightSidebarShell.test.ts @@ -907,7 +907,7 @@ describe('RightSidebarShell 钉住面板粘性展开(切会话把收着的侧栏 }); setGhostTabPinned('pr-signoff', true); // 无条目 = 不算钉住,先钉上 tabsIpc = makeRightSidebarTabsIpc(); - // 两个会话都带同一块钉住面板页签(补挂在真环境由注册表驱动,单测无插件 + // 两个任务都带同一块钉住面板页签(补挂在真环境由注册表驱动,单测无插件 // 注册,直接让存档里就有)。 tabsIpc.list.mockImplementation(async () => ({ tabs: [{ id: 'tab-ghost', kind: GHOST_KIND, state: null }], @@ -997,7 +997,7 @@ describe('RightSidebarShell 钉住面板粘性展开(切会话把收着的侧栏 expect(requests).toHaveLength(0); }); - it('首帧(启动进第一个会话)不算切换 → 不弹', async () => { + it('首帧(启动进第一个任务)不算切换 → 不弹', async () => { writePanelCollapsed('right-tabs', { sessionId: 's1' }, true); setLastFocusedPinnedGhostKind(GHOST_KIND); diff --git a/apps/desktop/src/renderer/i18n/locales/en/common.json b/apps/desktop/src/renderer/i18n/locales/en/common.json index 839e10e8d40..76abc5bda1c 100644 --- a/apps/desktop/src/renderer/i18n/locales/en/common.json +++ b/apps/desktop/src/renderer/i18n/locales/en/common.json @@ -52,8 +52,8 @@ }, "agentIsland": { "native": { - "newConversationTitle": "New Chat", - "newConversationHint": "Start a new conversation", + "newConversationTitle": "New Session", + "newConversationHint": "Start a new session", "muteSound": "Mute Agent Island", "enableSound": "Enable Agent Island sound", "settings": "Agent Island settings", @@ -659,7 +659,7 @@ }, "qnaTitle": "Ask directly", "qnaDescription": "If the overview does not cover your question, ask what you want to do and I will point you to the right path.", - "qnaPlaceholder": "For example: how do I import a Codex conversation?", + "qnaPlaceholder": "For example: how do I import a Codex session?", "qnaSend": "Send", "qnaPending": "Preparing an answer…", "qnaNoAnswer": "I do not have a reliable answer yet. Try asking again with a different wording.", @@ -678,7 +678,7 @@ "openAssistant": "Ask Help Assistant", "panelTitle": "Help Assistant", "panelAriaLabel": "Help Assistant panel", - "newSession": "New chat", + "newSession": "New session", "threadEmptyTitle": "Ask me anything", "threadEmptyHint": "Quick answers to common questions — you can follow up to correct me" }, @@ -1081,8 +1081,8 @@ "totalHint": "Selectable for import", "projects": "Project sessions", "projectsHint": "Sessions that will appear under project groups", - "dialogue": "Dialogues", - "dialogueHint": "Codex projectless conversations", + "dialogue": "Chat", + "dialogueHint": "Codex projectless sessions", "filtered": "Skipped", "filteredHint": "Internal or already in app" }, @@ -1093,7 +1093,7 @@ "codex": "Codex", "claude": "Claude", "project": "Existing projects", - "dialogue": "Dialogues" + "dialogue": "Chat" }, "toast": { "scanFailed": "Scan failed", @@ -1195,7 +1195,7 @@ }, "providers": { "title": "Providers", - "subtitle": "The source used when picking a model. Each source offers its own models and billing, switchable per conversation.", + "subtitle": "The source used when picking a model. Each source offers its own models and billing, switchable per session.", "pill": { "connected": "Connected", "suspended": "Disabled" @@ -1654,7 +1654,7 @@ }, "intro": "After connecting, private messages sent to this WeChat connection enter Cindy on this computer. Tencent provides the authorization page, which may display OpenClaw.", "errorNote": "The connection was not completed. Check your network and scan again. Cindy never stores plaintext credentials after a failed connection.", - "disabledNote": "Personal WeChat is paused in this version by the compatibility policy. Saved conversations and settings are not removed.", + "disabledNote": "Personal WeChat is paused in this version by the compatibility policy. Saved sessions and settings are not removed.", "permission": { "label": "Permission mode", "hint": "Applies to new context after /new. Risky actions still require confirmation in Cindy Desktop.", @@ -1690,7 +1690,7 @@ "connected": "WeChat private messages go directly to Cindy on this computer and are not relayed through Cindy servers.", "reconnecting": "This computer is restoring the WeChat connection. No new scan is needed.", "needs_reauth": "The WeChat authorization expired or was replaced by another connection. Scan again.", - "disabled_by_policy": "The connection is paused by this version's compatibility policy; credentials and conversations remain saved.", + "disabled_by_policy": "The connection is paused by this version's compatibility policy; credentials and sessions remain saved.", "error": "The connection is temporarily unavailable. Scan again or unbind and retry later." } }, @@ -1708,7 +1708,7 @@ }, "unbind": { "title": "Unbind personal WeChat?", - "description": "Unbinding stops messaging, removes local WeChat credentials, and clears unfinished tasks. Existing Cindy conversations, history, and saved attachments remain.", + "description": "Unbinding stops messaging, removes local WeChat credentials, and clears unfinished tasks. Existing Cindy sessions, history, and saved attachments remain.", "confirm": "Unbind", "cancel": "Cancel" }, @@ -1852,7 +1852,7 @@ }, "commandMenu": { "start": "Welcome and usage", - "new": "Start a new session", + "new": "Start a new chat", "help": "Show command help", "stop": "Stop the current task", "session": "Recent sessions, pick one to continue", @@ -1968,7 +1968,7 @@ }, "memory": { "title": "Memory", - "description": "Powers workspace-scoped memory. After changing settings you usually need to start a new Session for it to take effect. If memory misbehaves, click Reset or turn it off.", + "description": "Powers workspace-scoped memory. After changing settings you usually need to start a new agent session for it to take effect. If memory misbehaves, click Reset or turn it off.", "maker": { "label": "Cindy", "description": "Memory is managed by the agent entry, not by each model. Claude Code and Codex sessions can share memory within the same working directory.", @@ -2006,7 +2006,7 @@ "toast": { "enabled": "{{label}} memory enabled", "disabled": "{{label}} memory disabled", - "takesEffectSuffix": " — takes effect in new chats", + "takesEffectSuffix": " — takes effect on the next agent session", "toggleFailed": "Failed to update memory setting", "resetSuccess_one": "{{label}} memories reset — removed {{count}} entry", "resetSuccess_other": "{{label}} memories reset — removed {{count}} entries", @@ -2176,7 +2176,7 @@ "title": "Browser automation", "description": "A dedicated browser for agents — separate from your everyday Chrome", "toggleAria": "Toggle browser automation", - "toggleHint": "This toggle only applies to agent sessions started afterward; it does not affect a conversation that is already running.", + "toggleHint": "This toggle only applies to agent sessions started afterward; it does not affect a session that is already running.", "noProjectHint": "Open Settings from a project session to toggle browser automation for that project.", "detected": "Detected {{browser}}", "notDetected": "No local browser detected. Install Chrome to use browser automation.", @@ -2211,7 +2211,7 @@ "description": "Let agents inspect and operate a connected Android device over ADB", "toggleAria": "Toggle Android automation", "refresh": "Recheck", - "toggleHint": "This machine-wide toggle only applies to agent sessions started afterward; it does not affect a conversation that is already running.", + "toggleHint": "This machine-wide toggle only applies to agent sessions started afterward; it does not affect a session that is already running.", "status": { "checking": "Checking ADB status…", "preparing": "Preparing ADB…", @@ -2348,7 +2348,7 @@ "windowsHint": "Windows does not require extra system permissions. Keep the target app visible on the active desktop while the agent works.", "linuxHint": "Linux does not use macOS TCC permissions; make sure the current desktop session allows window inspection and input automation." }, - "toggleHint": "This toggle only applies to agent sessions started afterward; it does not affect a conversation that is already running.", + "toggleHint": "This toggle only applies to agent sessions started afterward; it does not affect a session that is already running.", "noProjectHint": "Open Settings from a project session to toggle direct computer control for that project.", "update": { "available": "New version v{{version}} available. You may need to re-grant system permissions after updating.", @@ -2377,21 +2377,21 @@ }, "builtinTools": { "title": "Built-in Tools", - "description": "Set defaults for new conversations or override them for a project. Project settings take priority.", + "description": "Set defaults for new sessions or override them for a project. Project settings take priority.", "empty": "No tools available.", "toggleAria": "Toggle {{name}} tool", "toggleHint": "Changes apply only to new or restarted agent sessions. Running sessions are not affected.", "loadFailed": "Failed to load tool list", "scopePicker": { "ariaLabel": "Switch the scope of built-in tool settings", - "userDefault": "New conversation defaults", + "userDefault": "New session defaults", "userDefaultDescription": "Used by new sessions without a project override" }, "source": { "project": "project", "projectTooltip": "Overridden by project settings ({{dir}})", "user": "user default", - "userTooltip": "Inherited from new conversation defaults", + "userTooltip": "Inherited from new session defaults", "product": "product default", "productTooltip": "Inherited from Cindy product defaults" }, @@ -2759,8 +2759,8 @@ "description": "Open the settings page" }, "new-maker": { - "label": "New chat", - "description": "Start a new chat" + "label": "New session", + "description": "Start a new session" }, "close-tab-or-window": { "label": "Close tab or window", @@ -2953,7 +2953,7 @@ "projectSwitchLockedTip": "This plugin is disabled globally; enable it in the Global view first", "projectBanner": { "title": "Configuring project \"{{name}}\"", - "desc": "Switches here only affect this project — turned-off plugins won't appear in its new sessions", + "desc": "Switches here only affect this project — turned-off plugins are unavailable in it", "backToGlobal": "Back to global" }, "scopePicker": { @@ -2984,7 +2984,7 @@ "updated": "\"{{name}}\" updated to v{{version}}", "exported": "Exported the .cindy file for \"{{name}}\"", "exportFailed": "Couldn't export \"{{name}}\" — try again", - "projectDisabled": "Turned off \"{{name}}\" for this project (applies to new sessions)", + "projectDisabled": "Turned off \"{{name}}\" for this project", "projectEnabled": "\"{{name}}\" now follows the global setting in this project" }, "panelError": { @@ -3855,8 +3855,8 @@ "addAria": "Add tab", "addFailed": "Failed to add tab", "tabCloseAria": "Close tab", - "pinAria": "Pin — keep in all conversations", - "unpinAria": "Unpin — keep only in this conversation", + "pinAria": "Pin — keep in all sessions", + "unpinAria": "Unpin — keep only in this session", "menu": { "addLabel": "Add tab", "comingSoon": "Coming soon", @@ -4522,7 +4522,7 @@ "search": "Search", "local": "Local", "projects": "Projects", - "conversations": "Conversations" + "conversations": "Sessions" } }, "addRemoteProject": { @@ -4715,8 +4715,8 @@ "recent": "Recent", "browseFolder": "Choose a different folder", "browseProjectFolder": "Choose another project folder", - "dialogue": "Dialogue", - "dialogueOrSelectProject": "Dialogue or select project", + "dialogue": "Chat", + "dialogueOrSelectProject": "Chat or select project", "removeFromList": "Remove from list", "missingDir": "Folder not found" }, @@ -4770,7 +4770,7 @@ "disabledHint": "Collaboration is disabled for this project. Enable it in project settings.", "unavailableHint": "Unable to verify this project's collaboration setting. Try again shortly.", "unsupportedRemoteHint": "The controlled device is too old to support collaboration.", - "remoteContinueNotice": " This message is still being sent as a single conversation.", + "remoteContinueNotice": " This message is still being sent as a single session.", "handoffRecoveredMessage": "Collaboration startup was interrupted last time. Your unsent message has been restored to the composer.", "handoffRecoveredGoal": "Collaboration startup was interrupted last time. The goal was not started; its text has been restored to the composer.", "loadingHint": "Still checking this project's collaboration setting. Try again shortly.", @@ -5043,7 +5043,7 @@ "outputFile": "Output: {{path}}" }, "forkOrigin": { - "label": "Created from another conversation" + "label": "Created from another session" }, "imageMissing": { "title": "Image missing" @@ -5057,17 +5057,17 @@ "copy": "Copy message", "linkCopied": "Link copied", "moreActions": "More message actions", - "copyLink": "Copy current conversation link", + "copyLink": "Copy current message link", "copyLinkFailed": "Couldn't copy the link", "rewind": "Back to here", - "fork": "Continue in a new conversation", - "forkConfirmTitle": "Start a new conversation from here?", - "forkConfirmDescription": "A separate conversation will be created using the context at this point. The original conversation stays unchanged, and future messages in either conversation won't affect the other.", - "forkConfirm": "Start new conversation", + "fork": "Start a new session", + "forkConfirmTitle": "Start a new session from here?", + "forkConfirmDescription": "A separate session will be created using the context at this point. The original session stays unchanged, and future messages in either session won't affect the other.", + "forkConfirm": "Start new session", "forkCancel": "Cancel", "delete": "Delete this message", "deleteConfirmTitle": "Delete this message?", - "deleteConfirmDescription": "A user message is removed on its own. For an AI message, the entire AI output after the preceding user input is removed. Later conversation stays in place. On the next send, the AI context is rebuilt from the remaining history.", + "deleteConfirmDescription": "A user message is removed on its own. For an AI message, the entire AI output after the preceding user input is removed. Later messages stay in place. On the next send, the AI context is rebuilt from the remaining history.", "deleteConfirm": "Delete message", "deleteCancel": "Cancel", "deleteBusy": "Wait for the current response to finish before deleting a message.", @@ -5089,9 +5089,9 @@ "codexThreadStale": "Codex auth mode changed — this session can't continue. Start a new session to apply it.", "claudeGatewayOpusPlanMismatch": "This Opus request went through XD Gateway, not your Claude.ai subscription. That gateway route rejected the model. Switch to your Claude.ai subscription and retry, or choose another model.", "switchClaudeSubscription": "Switch to Claude.ai & Retry", - "switchClaudeSubscriptionTitle": "Switch this conversation to the Claude.ai subscription and retry", + "switchClaudeSubscriptionTitle": "Switch this session to the Claude.ai subscription and retry", "switchingClaudeSubscription": "Switching to Claude.ai…", - "claudeSubscriptionSwitchFailed": "Couldn’t switch to Claude.ai or retry this conversation: {{msg}}", + "claudeSubscriptionSwitchFailed": "Couldn’t switch to Claude.ai or retry this session: {{msg}}", "budgetModelHint": "If this keeps failing, try switching to the standard GPT model.", "codexAuthMissing": "Remote Codex hasn't been signed in yet (401 Unauthorized). Click \"Sync auth\" to push your local Codex credentials to the remote Cindy Codex home, then retry.", "codexAuthMissingLocal": "Codex credentials missing or expired (401 Unauthorized). Re-login in {{appName}} Settings → Codex (the app-server will auto-restart on login), or run `codex login` in your terminal and toggle the auth mode in Settings once to restart the app-server. Then start a new session.", @@ -5430,7 +5430,7 @@ "savedTokens": "saved {{tokens}} tokens", "manual": "Manual compact", "auto": "Auto compact", - "aria": "Conversation compacted" + "aria": "Chat compacted" }, "autoResume": { "label": "Reconnected", @@ -5439,7 +5439,7 @@ "detail": { "reason": "Interruption reason", "attempt": "Attempt {{attempt}}/{{total}}", - "sessionTotal": "{{count}} reconnects in this conversation" + "sessionTotal": "{{count}} reconnects in this session" } }, "autoResumeSeparator": { @@ -5520,7 +5520,7 @@ "messageCount_other": "{{count}} messages", "truncated": "truncated" }, - "forkBusy": "Wait for the current response to finish before starting a new conversation from here", + "forkBusy": "Wait for the current response to finish before starting a new session from here", "editStopTimeout": "Timed out waiting for the task to stop — please try again", "editQueueNotEmpty": "This session has queued messages — send or remove them before editing", "editNotLast": "A newer message exists in this session — this one is no longer the latest and cannot be edited", @@ -5529,11 +5529,11 @@ "editSend": "Send", "editRollbackHint": "Sending will undo changes to {{count}} file(s) from the last turn", "forkErrors": { - "unsupportedHistory": "The historical agent session for this point is unavailable, so a new conversation can't be started from here", - "noPriorAssistant": "Start a new conversation from a message that follows an AI reply", - "sourceNeverRan": "The original conversation hasn't run yet, so a new conversation can't be started", - "codexStateUnavailable": "The Codex conversation state is unavailable, so a new conversation can't be started. {{detail}}", - "generic": "Couldn't start a new conversation. Try again." + "unsupportedHistory": "The historical agent session for this point is unavailable, so a new session can't be started from here", + "noPriorAssistant": "Start a new session from a message that follows an AI reply", + "sourceNeverRan": "The original session hasn't run yet, so a new session can't be started", + "codexStateUnavailable": "The Codex session state is unavailable, so a new session can't be started. {{detail}}", + "generic": "Couldn't start a new session. Try again." } }, "threadContext": { @@ -5541,7 +5541,7 @@ "viewThread": "View thread context ({{count}})" }, "rewind": { - "noFilesToRollback": "No files to roll back, only conversation history will be truncated", + "noFilesToRollback": "No files to roll back, only chat history will be truncated", "errors": { "noPriorAssistantPreview": "Please rewind from a question that follows an AI reply (this one has no prior assistant reply)", "sessionRunning": "The task did not stop in time — retry the rewind", @@ -5556,10 +5556,10 @@ "errorDescription": "File checkpointing is not enabled for this Session — files modified by tools cannot be rolled back.", "calculating": "Calculating rewind impact…", "noChanges": "No file changes to roll back", - "continueWillTruncate": "Continuing will still truncate later conversation", + "continueWillTruncate": "Continuing will still truncate subsequent messages", "noPerFileDiff": "This SDK version does not support per-file diff preview", "summaryDefault": "Subsequent messages after this will be discarded, and {{count}} files in the working directory will be rolled back to that moment.", - "summaryRunning": "The current task will stop. Later conversation and corresponding file changes will be rewound.", + "summaryRunning": "The current task will stop. Subsequent messages and the corresponding file changes will be rewound.", "summaryEmpty": "Subsequent messages will be discarded. No files were changed; only history is truncated.", "summaryEmptyWithNote": "Subsequent messages will be discarded.", "runningNotice": "The task is still running. Confirming will stop it before recalculating and applying the rewind.", @@ -5765,11 +5765,11 @@ "compactHint": "Click to compact context", "codexAutoHint": "Codex compacts automatically on the server side; manual compaction is not supported", "confirmTitle": "Compact context?", - "confirmDescription": "Current usage: {{used}} / {{total}} ({{pct}}%). This silently compacts context — the agent summarizes conversation history to free up context space.", + "confirmDescription": "Current usage: {{used}} / {{total}} ({{pct}}%). This silently compacts context — the agent summarizes chat history to free up context space.", "confirmAction": "Compact", "confirmCancel": "Cancel" }, - "dialogueLabel": "Dialogue", + "dialogueLabel": "Chat", "backToAutomations": "Back to Automations", "waitForReply": "Waiting for your reply in the document area…", "modelSwitchedFastModeOff": "Model switched, Fast Mode disabled" @@ -5809,7 +5809,7 @@ "worktreeRepoHint": "Try re-selecting the working directory and wait for the folder badge to refresh, then retry", "sessionInactive": "Session is no longer active", "remoteProjectBanner": "New session in {{project}} on {{device}}", - "remoteDialogueBanner": "New dialogue on {{device}} (no project)" + "remoteDialogueBanner": "New chat on {{device}} (no project)" }, "jumpSession": { "emptyId": "Enter a sessionId to jump to", @@ -5842,14 +5842,14 @@ "awaitingPluginSetup": "Awaiting plugin setup" }, "showAllSessions": "Show all {{count}}", - "dialogues": "Dialogues", - "newDialogue": "New dialogue", - "dialoguesToggleExpand": "Expand dialogues", - "dialoguesToggleCollapse": "Collapse dialogues", + "dialogues": "Chat", + "newDialogue": "New session", + "dialoguesToggleExpand": "Expand Chat", + "dialoguesToggleCollapse": "Collapse Chat", "loadingDialogues": "Loading…", - "noDialogues": "No dialogues", - "dialogueSettings": "Organize dialogues", - "dialogueSettingsAria": "Organize dialogues (Sort by: {{sortBy}})", + "noDialogues": "No sessions", + "dialogueSettings": "Organize Chat", + "dialogueSettingsAria": "Organize Chat (Sort by: {{sortBy}})", "dialogueSortHeading": "Sort by", "dialogueSort": { "recency": "Recent first", @@ -6036,18 +6036,18 @@ "moveToProject": "Move to project", "moveToProjectHeading": "Select project", "browseProjectFolder": "Choose project folder", - "moveToDialogue": "Move to Dialogues", + "moveToDialogue": "Move to Chat", "noProjects": "No recent projects", "moveToProjectDone": "Moved to project", "moveToProjectFailed": "Failed to move to project", - "moveToDialogueDone": "Moved to Dialogues", - "moveToDialogueFailed": "Failed to move to Dialogues", + "moveToDialogueDone": "Moved to Chat", + "moveToDialogueFailed": "Failed to move to Chat", "moveToProjectRunningBlocked": "This session is running. Move it after it finishes.", "moveToProjectAttachedBlocked": "This session is being controlled remotely. Move it after control ends.", "moveToProjectRemoteUnsupported": "Moving remote sessions is not supported yet", "openInNewWindow": "Open in new window", "exportShare": "Export session…", - "copySessionLink": "Copy chat link" + "copySessionLink": "Copy session link" }, "projectAction": { "rename": "Rename project", @@ -6282,11 +6282,11 @@ "open": "Search", "placeholder": "Search titles or content…", "clear": "Clear search", - "empty": "Type to search chat titles and content", + "empty": "Type to search session titles and content", "searching": "Searching…", "failed": "Search failed", - "noResults": "No matching chats", - "jumpFailed": "Opened the chat, but could not jump to the matching message", + "noResults": "No matching sessions", + "jumpFailed": "Opened the session, but could not jump to the matching message", "moreHits": "{{count}} more related", "kind": { "title": "Title", @@ -6732,28 +6732,28 @@ "deleteDialog": { "title": "Delete automation \"{{name}}\"?", "titleFallback": "Delete automation?", - "description": "Future triggers will stop. Choose what to do with conversations already generated by this automation.", - "sessionCountPending": "Counting generated conversations…", - "sessionCount": "{{count}} generated conversation(s)", + "description": "Future triggers will stop. Choose what to do with sessions already generated by this automation.", + "sessionCountPending": "Counting generated sessions…", + "sessionCount": "{{count}} generated session(s)", "inflightCount": "{{count}} execution(s) are currently running and will be stopped", - "optionAria": "Choose how to handle generated conversations", + "optionAria": "Choose how to handle generated sessions", "option": { "keep": { - "title": "Keep conversations", - "description": "Delete only the automation task. Conversations stay in the sidebar." + "title": "Keep sessions", + "description": "Delete only the automation task. Sessions stay in the sidebar." }, "archive": { - "title": "Archive conversations", - "description": "Delete the automation task and move generated conversations to Archive." + "title": "Archive sessions", + "description": "Delete the automation task and move generated sessions to Archive." }, "delete": { - "title": "Delete conversations", - "description": "Delete the automation task and its generated conversations. This cannot be undone." + "title": "Delete sessions", + "description": "Delete the automation task and its generated sessions. This cannot be undone." } }, "confirm": "Delete automation", - "confirmDeleteSessions": "Delete automation and conversations", - "sessionUpdatePartial": "Automation deleted, but {{fail}} conversation(s) could not be updated" + "confirmDeleteSessions": "Delete automation and sessions", + "sessionUpdatePartial": "Automation deleted, but {{fail}} session(s) could not be updated" }, "confirm": { "delete": { @@ -6853,7 +6853,7 @@ "refresh": "Refresh", "emptyTitle": "No issues submitted yet", "emptyTitleUnverified": "Couldn't find your issues right now", - "startIssueChat": "Start a new chat with /issue", + "startIssueChat": "Start a new session with /issue", "statusUnknown": "Status unknown", "sourceCindy": "Submitted from Cindy", "sourceGithub": "GitHub account", @@ -7703,7 +7703,7 @@ "migrationPartial": "Migration done: {{success}} succeeded / {{skipped}} skipped / {{failed}} failed (total {{total}})", "migrationFailed": "Migration failed, please retry later", "fetchUpdateNoticeFailed": "Failed to fetch update notice", - "corruptionRestored": "Database restored from snapshot at {{time}}; recent chats may be lost", + "corruptionRestored": "Database restored from snapshot at {{time}}; recent chat history may be lost", "schemaDriftWarning": "A local database version record is inconsistent. Update to the latest version; if this warning keeps appearing, contact support. The app will not delete local data automatically.", "telegramBotConnected": "Telegram bot configuration saved", "telegramBotConnectFailed": "Telegram bot connection failed; check the token and your network", @@ -7922,7 +7922,7 @@ }, "toast": { "usage": "Usage: /goal ", - "noSession": "Open or start a conversation first to set a goal", + "noSession": "Open or start a session first to set a goal", "failed": "Failed to set goal", "set": "Goal set — working toward it", "cleared": "Goal cleared" @@ -7945,7 +7945,7 @@ "failed": "Learning failed", "cancel": "Cancel", "review": "Review proposal", - "fromConversation": "Distilled from the current conversation", + "fromConversation": "Distilled from the current session", "openSession": "Open learning session", "continueHint": "Keep chatting in this session to revise the proposal; apply it via \"Review proposal\" when satisfied" }, @@ -8004,7 +8004,7 @@ "confirmExcludeMedia": "Export without media", "oversizeHint": "Content exceeds the size limit; media files account for about {{mediaMb}} MB. You can export again without media (text and context are kept).", "tooLarge": "The session exceeds the {{limitMb}} MB export limit even without media, so it cannot be exported.", - "doneFull": "Exported. The recipient can continue the conversation seamlessly.", + "doneFull": "Exported. The recipient can continue the chat seamlessly.", "donePartial": "Exported. Some transcript history is missing (still resumable).", "doneDbOnly": "Exported with chat history only (the recipient cannot seamlessly continue).", "failed": "Export failed" @@ -8029,7 +8029,7 @@ "passwordPlaceholder": "Password", "unlock": "Unlock", "previewMeta": "{{agent}} · {{count}} messages · exported {{exportedAt}}", - "riskWarning": "This file was shared from outside — make sure you trust its source. The imported history may influence the AI's future behavior, and if you continue the conversation, the AI can still read and write local files and run commands.", + "riskWarning": "This file was shared from outside — make sure you trust its source. The imported history may influence the AI's future behavior, and if you continue the chat, the AI can still read and write local files and run commands.", "workdirHint": "The original working directory was {{originalDir}}. Choose the matching directory on this machine.", "workdirEmpty": "No directory selected", "pickWorkdir": "Choose directory", @@ -8052,8 +8052,8 @@ "db-only": "Chat history only: readable, but cannot seamlessly continue" }, "fidelityResult": { - "full": "Context is complete. You can continue the conversation right away.", - "partial": "You can continue the conversation; rewinding to earlier fork points may fail.", + "full": "Context is complete. You can continue the chat right away.", + "partial": "You can continue the chat; rewinding to earlier fork points may fail.", "db-only": "Only chat history was imported: Claude sessions need fresh context; Codex sessions restore as plain-text history (reasoning and tool details are lost)." }, "note": { diff --git a/apps/desktop/src/renderer/i18n/locales/ja/common.json b/apps/desktop/src/renderer/i18n/locales/ja/common.json index dd6d20f8718..9f70505bd21 100644 --- a/apps/desktop/src/renderer/i18n/locales/ja/common.json +++ b/apps/desktop/src/renderer/i18n/locales/ja/common.json @@ -52,8 +52,8 @@ }, "agentIsland": { "native": { - "newConversationTitle": "新しい会話", - "newConversationHint": "新しい会話を開始", + "newConversationTitle": "新しいセッション", + "newConversationHint": "新しいセッションを開始", "muteSound": "Agent Island の音をミュート", "enableSound": "Agent Island の音を有効化", "settings": "Agent Island 設定", @@ -659,7 +659,7 @@ }, "qnaTitle": "そのまま質問する", "qnaDescription": "overview に載っていない内容でも、やりたいことを聞けば次に開く場所や進め方を案内します。", - "qnaPlaceholder": "例:Codex の会話をインポートするには?", + "qnaPlaceholder": "例:Codex のセッションをインポートするには?", "qnaSend": "送信", "qnaPending": "回答を準備しています…", "qnaNoAnswer": "まだ確実な回答がありません。表現を変えてもう一度聞いてみてください。", @@ -678,7 +678,7 @@ "openAssistant": "そのまま質問する", "panelTitle": "ヘルプアシスタント", "panelAriaLabel": "ヘルプアシスタントパネル", - "newSession": "新しい会話", + "newSession": "新しいセッション", "threadEmptyTitle": "気軽に質問してください", "threadEmptyHint": "よくある質問にすぐ回答します。追加で質問して修正もできます" }, @@ -1195,7 +1195,7 @@ }, "providers": { "title": "モデルプロバイダー", - "subtitle": "モデルを選ぶときのソースです。各ソースは対応するモデルと課金方式を提供し、会話ごとに切り替えられます。", + "subtitle": "モデルを選ぶときのソースです。各ソースは対応するモデルと課金方式を提供し、セッションごとに切り替えられます。", "pill": { "connected": "接続済み", "suspended": "無効" @@ -1653,7 +1653,7 @@ }, "intro": "接続後、この WeChat 接続へのプライベートメッセージは、このコンピューターの Cindy に届きます。認証ページは Tencent が提供し、OpenClaw と表示される場合があります。", "errorNote": "接続を完了できませんでした。ネットワークを確認してもう一度スキャンしてください。失敗時に Cindy が認証情報を平文で保存することはありません。", - "disabledNote": "互換性ポリシーにより、このバージョンでは個人 WeChat 接続が一時停止されています。保存済みの会話と設定は削除されません。", + "disabledNote": "互換性ポリシーにより、このバージョンでは個人 WeChat 接続が一時停止されています。保存済みのセッションと設定は削除されません。", "permission": { "label": "権限モード", "hint": "/new 後の新しいコンテキストに適用されます。高リスク操作は引き続き Cindy Desktop での確認が必要です。", @@ -1689,7 +1689,7 @@ "connected": "WeChat のプライベートメッセージはこのコンピューターの Cindy に直接届き、Cindy サーバーを経由しません。", "reconnecting": "WeChat 接続を復旧しています。再スキャンは不要です。", "needs_reauth": "WeChat 認証が期限切れか、別の接続に置き換えられました。もう一度スキャンしてください。", - "disabled_by_policy": "互換性ポリシーにより接続は一時停止中です。認証情報と会話は保持されています。", + "disabled_by_policy": "互換性ポリシーにより接続は一時停止中です。認証情報とセッションは保持されています。", "error": "接続を一時的に利用できません。再スキャンするか、連携解除後にもう一度お試しください。" } }, @@ -1707,7 +1707,7 @@ }, "unbind": { "title": "個人 WeChat の連携を解除しますか?", - "description": "連携を解除するとメッセージ送受信を停止し、ローカルの WeChat 認証情報と未完了タスクを削除します。既存の Cindy 会話、履歴、保存済み添付ファイルは残ります。", + "description": "連携を解除するとメッセージ送受信を停止し、ローカルの WeChat 認証情報と未完了タスクを削除します。既存の Cindy セッション、履歴、保存済み添付ファイルは残ります。", "confirm": "連携解除", "cancel": "キャンセル" }, @@ -1851,7 +1851,7 @@ }, "commandMenu": { "start": "ようこそと使い方を表示", - "new": "新しいセッションを開始", + "new": "新しい会話を開始", "help": "コマンドヘルプを表示", "stop": "現在のタスクを停止", "session": "最近のセッション一覧から続行", @@ -1967,7 +1967,7 @@ }, "memory": { "title": "メモリ", - "description": "ワークスペース単位のメモリを実現します。設定を変更したら、新しい Session を開始してから反映されます。メモリに問題があれば Reset するか OFF にしてください。", + "description": "ワークスペース単位のメモリを実現します。設定を変更したら、新しい Agent セッションを開始してから反映されます。メモリに問題があれば Reset するか OFF にしてください。", "maker": { "label": "Cindy", "description": "メモリはモデルごとではなく Agent の入口ごとに管理されます。同じ作業ディレクトリでは、Claude Code と Codex の入口から開始した Session がメモリを共有します", @@ -2005,7 +2005,7 @@ "toast": { "enabled": "{{label}} メモリを有効化しました", "disabled": "{{label}} メモリを無効化しました", - "takesEffectSuffix": " — 新しいチャットで反映されます", + "takesEffectSuffix": " — 次回 Agent セッションから反映されます", "toggleFailed": "メモリ設定の更新に失敗しました", "resetSuccess_one": "{{label}} メモリをリセットしました — {{count}} 件削除", "resetSuccess_other": "{{label}} メモリをリセットしました — {{count}} 件削除", @@ -2175,7 +2175,7 @@ "title": "自動操作ブラウザ", "description": "エージェント専用の独立したブラウザ。普段の Chrome とは分離されています", "toggleAria": "ブラウザ自動操作を切り替える", - "toggleHint": "この切り替えは、以降に開始した Agent セッションにのみ反映されます。実行中の会話には影響しません。", + "toggleHint": "この切り替えは、以降に開始した Agent セッションにのみ反映されます。実行中のセッションには影響しません。", "noProjectHint": "プロジェクトのセッションから「設定」を開くと、そのプロジェクトのブラウザ自動操作を切り替えられます。", "detected": "{{browser}} を検出しました", "notDetected": "ローカルブラウザが見つかりません。Chrome をインストールするとブラウザ自動操作を利用できます。", @@ -2210,7 +2210,7 @@ "description": "エージェントが ADB 経由で接続済み Android デバイスを確認し、操作できます", "toggleAria": "Android 自動操作を切り替える", "refresh": "再確認", - "toggleHint": "このマシン全体の切り替えは、以降に開始した Agent セッションにのみ反映されます。実行中の会話には影響しません。", + "toggleHint": "このマシン全体の切り替えは、以降に開始した Agent セッションにのみ反映されます。実行中のセッションには影響しません。", "status": { "checking": "ADB の状態を確認しています…", "preparing": "ADB を準備しています…", @@ -2347,7 +2347,7 @@ "windowsHint": "Windows では追加のシステム権限は不要です。Agent が作業している間は、対象アプリを現在のアクティブなデスクトップ上で表示したままにしてください。", "linuxHint": "Linux では macOS の TCC 権限は使いません。現在のデスクトップセッションでウィンドウ取得と入力自動化が許可されていることを確認してください。" }, - "toggleHint": "この切り替えは、以降に開始した Agent セッションにのみ反映されます。実行中の会話には影響しません。", + "toggleHint": "この切り替えは、以降に開始した Agent セッションにのみ反映されます。実行中のセッションには影響しません。", "noProjectHint": "プロジェクトのセッションから「設定」を開くと、そのプロジェクトのコンピュータ直接操作を切り替えられます。", "update": { "available": "新しいバージョン v{{version}} が利用可能です。更新後、システム権限の再許可が必要になる場合があります。", @@ -2376,21 +2376,21 @@ }, "builtinTools": { "title": "組み込みツール", - "description": "新しい会話のデフォルトを設定するか、プロジェクトごとに上書きします。プロジェクト設定が優先されます。", + "description": "新しいセッションのデフォルトを設定するか、プロジェクトごとに上書きします。プロジェクト設定が優先されます。", "empty": "利用可能なツールはありません。", "toggleAria": "{{name}} ツールを切り替え", "toggleHint": "変更は新規または再起動した Agent セッションにのみ反映され、実行中のセッションには影響しません。", "loadFailed": "ツール一覧の読み込みに失敗しました", "scopePicker": { "ariaLabel": "組み込みツール設定の適用範囲を切り替え", - "userDefault": "新しい会話のデフォルト", + "userDefault": "新しいセッションのデフォルト", "userDefaultDescription": "プロジェクト上書きのない新規セッションに適用" }, "source": { "project": "プロジェクト", "projectTooltip": "プロジェクト設定で上書きされています ({{dir}})", "user": "ユーザーデフォルト", - "userTooltip": "新しい会話のデフォルトを継承", + "userTooltip": "新しいセッションのデフォルトを継承", "product": "製品デフォルト", "productTooltip": "Cindy の製品デフォルトを継承" }, @@ -2758,8 +2758,8 @@ "description": "設定ページを開きます" }, "new-maker": { - "label": "新しいチャット", - "description": "新しいチャットを開始します" + "label": "新しいセッション", + "description": "新しいセッションを開始します" }, "close-tab-or-window": { "label": "タブまたはウィンドウを閉じる", @@ -2952,7 +2952,7 @@ "projectSwitchLockedTip": "このプラグインはグローバルで無効です。先に「グローバル」ビューで有効にしてください", "projectBanner": { "title": "プロジェクト「{{name}}」を設定中", - "desc": "ここのスイッチはこのプロジェクトのみに適用されます。オフにしたプラグインは新しいセッションに現れません", + "desc": "ここのスイッチはこのプロジェクトのみに適用されます。オフにしたプラグインはこのプロジェクトでは使用できません", "backToGlobal": "グローバルに戻る" }, "scopePicker": { @@ -2983,7 +2983,7 @@ "updated": "「{{name}}」を v{{version}} に更新しました", "exported": "「{{name}}」の .cindy ファイルを書き出しました", "exportFailed": "「{{name}}」を書き出せませんでした。もう一度お試しください", - "projectDisabled": "「{{name}}」をこのプロジェクトで無効にしました(新しいセッションから適用)", + "projectDisabled": "「{{name}}」をこのプロジェクトで無効にしました", "projectEnabled": "「{{name}}」はこのプロジェクトでグローバル設定に従います" }, "panelError": { @@ -4521,7 +4521,7 @@ "search": "検索", "local": "ローカル", "projects": "プロジェクト", - "conversations": "会話" + "conversations": "セッション" } }, "addRemoteProject": { @@ -4769,7 +4769,7 @@ "disabledHint": "このプロジェクトでは協同モードが無効です。プロジェクト設定で有効にしてください。", "unavailableHint": "このプロジェクトの協同設定を確認できません。しばらくしてから再試行してください。", "unsupportedRemoteHint": "被操作デバイスのバージョンが古いため、協同を利用できません。", - "remoteContinueNotice": "このメッセージは単一の対話として送信されます。", + "remoteContinueNotice": "このメッセージは単一セッションとして送信されます。", "handoffRecoveredMessage": "前回は協同の開始が中断されました。送信されなかったメッセージを入力欄に復元しました。", "handoffRecoveredGoal": "前回は協同の開始が中断されました。ゴールは開始されておらず、その内容を入力欄に復元しました。", "loadingHint": "このプロジェクトの協同設定を確認中です。しばらくしてから再試行してください。", @@ -5042,7 +5042,7 @@ "outputFile": "出力: {{path}}" }, "forkOrigin": { - "label": "元の会話から作成" + "label": "元のセッションから作成" }, "imageMissing": { "title": "画像が失われました" @@ -5056,13 +5056,13 @@ "copy": "メッセージをコピー", "linkCopied": "リンクをコピーしました", "moreActions": "その他のメッセージ操作", - "copyLink": "現在の会話リンクをコピー", + "copyLink": "現在のメッセージリンクをコピー", "copyLinkFailed": "リンクをコピーできませんでした", "rewind": "ここまで戻る", - "fork": "新しい会話で続ける", - "forkConfirmTitle": "ここから新しい会話を始めますか?", - "forkConfirmDescription": "この時点の会話コンテキストを使って、独立した新しい会話を作成します。元の会話は変更されず、以後のメッセージは互いに影響しません。", - "forkConfirm": "新しい会話を始める", + "fork": "新しいセッションで続ける", + "forkConfirmTitle": "ここから新しいセッションを始めますか?", + "forkConfirmDescription": "この時点の会話コンテキストを使って、独立した新しいセッションを作成します。元のセッションは変更されず、以後のメッセージは互いに影響しません。", + "forkConfirm": "新しいセッションを始める", "forkCancel": "キャンセル", "delete": "このメッセージを削除", "deleteConfirmTitle": "このメッセージを削除しますか?", @@ -5084,16 +5084,16 @@ "retry": "再試行", "continueAfterReset": "リセット後に続行", "continueAfterResetTitle": "利用上限のリセット後にこのタスクを続行する一回限りの自動化を作成", - "credentialSwitchBusy": "他の Codex タスクが実行中のため、この会話で選択したプロバイダーへまだ切り替えられません(共有 Codex プロセスの再起動が必要です)。他のタスクの終了後に「再試行」を押してください。", - "codexThreadStale": "Codex の認証モードが切り替わりました。この会話は続行できません。新しい会話を開始してください。", + "credentialSwitchBusy": "他の Codex タスクが実行中のため、このセッションで選択したプロバイダーへまだ切り替えられません(共有 Codex プロセスの再起動が必要です)。他のタスクの終了後に「再試行」を押してください。", + "codexThreadStale": "Codex の認証モードが切り替わりました。このセッションは続行できません。新しいセッションを開始してください。", "claudeGatewayOpusPlanMismatch": "この Opus リクエストは Claude.ai サブスクリプションではなく、XD Gateway 経由で送信され、このモデルが拒否されました。Claude.ai サブスクリプションに切り替えて再試行するか、別のモデルを選択してください。", "switchClaudeSubscription": "Claude.ai に切り替えて再試行", - "switchClaudeSubscriptionTitle": "この会話を Claude.ai サブスクリプションに切り替えて再試行", + "switchClaudeSubscriptionTitle": "このセッションを Claude.ai サブスクリプションに切り替えて再試行", "switchingClaudeSubscription": "Claude.ai に切り替え中…", - "claudeSubscriptionSwitchFailed": "Claude.ai への切り替えまたはこの会話の再試行に失敗しました:{{msg}}", + "claudeSubscriptionSwitchFailed": "Claude.ai への切り替えまたはこのセッションの再試行に失敗しました:{{msg}}", "budgetModelHint": "繰り返し失敗する場合は、通常版 GPT モデルに切り替えてお試しください。", "codexAuthMissing": "リモートの Codex がまだサインインしていません(401 Unauthorized)。「認証情報を同期」をクリックしてローカルの Codex 認証情報をリモートの Cindy Codex home にプッシュし、再試行してください。", - "codexAuthMissingLocal": "Codex 認証情報がないか期限切れです(401 Unauthorized)。{{appName}} Settings → Codex から再ログインする(ログイン時に app-server が自動再起動)か、ターミナルで `codex login` 実行後 Settings で認証モードを一度切り替えて app-server を再起動してから、新しい会話を開始してください。", + "codexAuthMissingLocal": "Codex 認証情報がないか期限切れです(401 Unauthorized)。{{appName}} Settings → Codex から再ログインする(ログイン時に app-server が自動再起動)か、ターミナルで `codex login` 実行後 Settings で認証モードを一度切り替えて app-server を再起動してから、新しいセッションを開始してください。", "codexSessionExpired": "OpenAI 接続の更新が必要です。ChatGPT に再接続すると続行できます。{{appName}} がシステムの Codex CLI からサインアウトしたり、他のモデルソースへの再接続を求めたりすることはありません。", "codexSessionExpiredDialog": { "title": "OpenAI 接続の更新が必要です", @@ -5438,7 +5438,7 @@ "detail": { "reason": "中断の理由", "attempt": "今回の再試行 {{attempt}}/{{total}}", - "sessionTotal": "この会話での再接続 {{count}} 回" + "sessionTotal": "このセッションでの再接続 {{count}} 回" } }, "autoResumeSeparator": { @@ -5519,7 +5519,7 @@ "messageCount_other": "{{count}} 件のメッセージ", "truncated": "一部省略" }, - "forkBusy": "現在の応答が終了してから、ここから新しい会話を始めてください", + "forkBusy": "現在の応答が終了してから、ここから新しいセッションを始めてください", "editStopTimeout": "タスクの停止待ちがタイムアウトしました。もう一度お試しください", "editQueueNotEmpty": "このセッションには送信待ちのメッセージがあります。先に送信または削除してから編集してください", "editNotLast": "セッションに新しいメッセージがあるため、これは最新ではなくなり編集して再送信できません", @@ -5528,11 +5528,11 @@ "editSend": "送信", "editRollbackHint": "送信すると前のターンで変更された {{count}} 個のファイルが元に戻ります", "forkErrors": { - "unsupportedHistory": "この位置に対応する過去のエージェントセッションを利用できないため、ここから新しい会話を始められません", - "noPriorAssistant": "AI の返信より後のメッセージから新しい会話を始めてください", - "sourceNeverRan": "元の会話がまだ実行されていないため、新しい会話を始められません", - "codexStateUnavailable": "Codex 会話の状態を利用できないため、新しい会話を始められません。{{detail}}", - "generic": "新しい会話を始められませんでした。再試行してください" + "unsupportedHistory": "この位置に対応する過去のエージェントセッションを利用できないため、ここから新しいセッションを始められません", + "noPriorAssistant": "AI の返信より後のメッセージから新しいセッションを始めてください", + "sourceNeverRan": "元のセッションがまだ実行されていないため、新しいセッションを始められません", + "codexStateUnavailable": "Codex セッションの状態を利用できないため、新しいセッションを始められません。{{detail}}", + "generic": "新しいセッションを始められませんでした。再試行してください" } }, "threadContext": { @@ -5833,11 +5833,11 @@ }, "showAllSessions": "すべて表示 {{count}} 件", "dialogues": "会話", - "newDialogue": "新規会話", + "newDialogue": "新規セッション", "dialoguesToggleExpand": "会話を展開", "dialoguesToggleCollapse": "会話を折りたたむ", "loadingDialogues": "読み込み中…", - "noDialogues": "会話はありません", + "noDialogues": "セッションはありません", "dialogueSettings": "会話を整理", "dialogueSettingsAria": "会話を整理(並び替え:{{sortBy}})", "dialogueSortHeading": "並び替え", @@ -5851,8 +5851,8 @@ "createProjectFailed": "プロジェクトの作成に失敗しました", "projectsSectionToggleExpand": "プロジェクトを展開", "projectsSectionToggleCollapse": "プロジェクトを折りたたむ", - "projectsToggleExpand": "すべてのプロジェクト会話を展開", - "projectsToggleCollapse": "すべてのプロジェクト会話を折りたたむ", + "projectsToggleExpand": "すべてのプロジェクトセッションを展開", + "projectsToggleCollapse": "すべてのプロジェクトセッションを折りたたむ", "filterAria": "サイドバー整理(ステータス:{{status}}、Agent:{{vendor}}、最終アクティビティ:{{lastActivity}}、グループ:{{groupBy}}、並び替え:{{sortBy}}、プロジェクト:{{projects}})", "organizeSidebar": "サイドバー整理", "filterAllText": "すべて", @@ -6037,7 +6037,7 @@ "moveToProjectRemoteUnsupported": "リモートセッションの移動はまだサポートされていません", "openInNewWindow": "新しいウィンドウで開く", "exportShare": "セッションをエクスポート…", - "copySessionLink": "チャットリンクをコピー" + "copySessionLink": "セッションリンクをコピー" }, "projectAction": { "rename": "プロジェクト名を変更", @@ -6067,7 +6067,7 @@ "projects": "プロジェクト", "dialogues": "チャット" }, - "railNavCount": "{{count}} 件の会話" + "railNavCount": "{{count}} 件のセッション" }, "rename": { "aiRename": "AIで名前を再生成", @@ -6272,11 +6272,11 @@ "open": "検索", "placeholder": "タイトルまたは内容を検索…", "clear": "検索をクリア", - "empty": "キーワードを入力して会話のタイトルと内容を検索", + "empty": "キーワードを入力してセッションのタイトルと内容を検索", "searching": "検索中…", "failed": "検索に失敗しました", - "noResults": "一致する会話がありません", - "jumpFailed": "会話を開きましたが、一致したメッセージへ移動できませんでした", + "noResults": "一致するセッションがありません", + "jumpFailed": "セッションを開きましたが、一致したメッセージへ移動できませんでした", "moreHits": "ほか {{count}} 件の関連", "kind": { "title": "タイトル", @@ -6722,28 +6722,28 @@ "deleteDialog": { "title": "自動化「{{name}}」を削除しますか?", "titleFallback": "自動化を削除しますか?", - "description": "今後のトリガーは停止されます。この自動化がすでに生成した会話の扱いを選択してください。", - "sessionCountPending": "生成済みの会話を確認しています…", - "sessionCount": "生成済みの会話 {{count}} 件", + "description": "今後のトリガーは停止されます。この自動化がすでに生成したセッションの扱いを選択してください。", + "sessionCountPending": "生成済みのセッションを確認しています…", + "sessionCount": "生成済みのセッション {{count}} 件", "inflightCount": "現在 {{count}} 件の実行中タスクがあり、削除すると停止されます", - "optionAria": "生成済み会話の扱いを選択", + "optionAria": "生成済みセッションの扱いを選択", "option": { "keep": { - "title": "会話を保持", - "description": "自動化タスクのみ削除し、会話はサイドバーに残します。" + "title": "セッションを保持", + "description": "自動化タスクのみ削除し、セッションはサイドバーに残します。" }, "archive": { - "title": "会話をアーカイブ", - "description": "自動化タスクを削除し、生成された会話をアーカイブへ移動します。" + "title": "セッションをアーカイブ", + "description": "自動化タスクを削除し、生成されたセッションをアーカイブへ移動します。" }, "delete": { - "title": "会話を削除", - "description": "自動化タスクと生成された会話を削除します。この操作は元に戻せません。" + "title": "セッションを削除", + "description": "自動化タスクと生成されたセッションを削除します。この操作は元に戻せません。" } }, "confirm": "自動化を削除", - "confirmDeleteSessions": "自動化と会話を削除", - "sessionUpdatePartial": "自動化は削除されましたが、{{fail}} 件の会話を処理できませんでした" + "confirmDeleteSessions": "自動化とセッションを削除", + "sessionUpdatePartial": "自動化は削除されましたが、{{fail}} 件のセッションを処理できませんでした" }, "confirm": { "delete": { @@ -6843,7 +6843,7 @@ "refresh": "更新", "emptyTitle": "送信した Issue はまだありません", "emptyTitleUnverified": "現在 Issue を取得できません", - "startIssueChat": "/issue で新しいチャットを開始", + "startIssueChat": "/issue で新しいセッションを開始", "statusUnknown": "ステータス不明", "sourceCindy": "Cindy から送信", "sourceGithub": "GitHub アカウント", @@ -7079,9 +7079,9 @@ }, "renameSessions": { "confirm": { - "title": "会話タイトルの一括変更を確認", - "description": "実際に書き込まれるタイトルを確認してください。承認すると {{count}} 件の会話タイトルを更新します。", - "count": "{{count}} 件の会話", + "title": "セッションタイトルの一括変更を確認", + "description": "実際に書き込まれるタイトルを確認してください。承認すると {{count}} 件のセッションタイトルを更新します。", + "count": "{{count}} 件のセッション", "from": "変更前:", "to": "変更後:", "untitled": "無題", @@ -7692,7 +7692,7 @@ "migrationPartial": "移行完了:成功 {{success}} / スキップ {{skipped}} / 失敗 {{failed}}(合計 {{total}})", "migrationFailed": "移行に失敗しました。後でもう一度お試しください", "fetchUpdateNoticeFailed": "更新通知の取得に失敗しました", - "corruptionRestored": "{{time}} のスナップショットからデータベースを復元しました。期間中のチャットが失われた可能性があります", + "corruptionRestored": "{{time}} のスナップショットからデータベースを復元しました。期間中の対話履歴が失われた可能性があります", "schemaDriftWarning": "ローカルデータベースのバージョン記録に不整合があります。最新版に更新し、更新後もこの警告が繰り返し表示される場合はサポートに連絡してください。アプリがローカルデータを自動的に削除することはありません。", "telegramBotConnected": "Telegram ボット設定を保存しました", "telegramBotConnectFailed": "Telegram ボットの接続に失敗しました。Token とネットワークを確認してください", @@ -7910,7 +7910,7 @@ }, "toast": { "usage": "使い方:/goal <目標の条件>", - "noSession": "目標を設定するには、先に会話を開くか開始してください", + "noSession": "目標を設定するには、先にセッションを開くか開始してください", "failed": "目標の設定に失敗しました", "set": "目標を設定しました。自動で進めます", "cleared": "目標をクリアしました" @@ -7933,7 +7933,7 @@ "failed": "学習に失敗しました", "cancel": "キャンセル", "review": "提案を確認", - "fromConversation": "現在の会話から蒸留", + "fromConversation": "現在のセッションから蒸留", "openSession": "学習セッションを開く", "continueHint": "このセッションで会話を続ければ提案を修正できます。満足したら「提案を確認」から適用してください" }, diff --git a/apps/desktop/src/renderer/i18n/locales/ko/common.json b/apps/desktop/src/renderer/i18n/locales/ko/common.json index 64c3a7fb5bd..0810941acd9 100644 --- a/apps/desktop/src/renderer/i18n/locales/ko/common.json +++ b/apps/desktop/src/renderer/i18n/locales/ko/common.json @@ -52,8 +52,8 @@ }, "agentIsland": { "native": { - "newConversationTitle": "새 대화", - "newConversationHint": "새 대화 시작", + "newConversationTitle": "새 세션", + "newConversationHint": "새 세션 시작", "muteSound": "Agent Island 소리 끄기", "enableSound": "Agent Island 소리 켜기", "settings": "Agent Island 설정", @@ -659,7 +659,7 @@ }, "qnaTitle": "바로 질문하기", "qnaDescription": "overview 에 없는 내용이라도 하고 싶은 일을 물어보면 다음에 열어야 할 곳이나 진행 방법을 안내합니다.", - "qnaPlaceholder": "예: Codex 대화를 어떻게 가져오나요?", + "qnaPlaceholder": "예: Codex 세션을 어떻게 가져오나요?", "qnaSend": "보내기", "qnaPending": "답변을 준비하는 중…", "qnaNoAnswer": "아직 확실한 답을 찾지 못했습니다. 표현을 바꿔 다시 물어보세요.", @@ -678,7 +678,7 @@ "openAssistant": "바로 질문하기", "panelTitle": "도움말 어시스턴트", "panelAriaLabel": "도움말 어시스턴트 패널", - "newSession": "새 대화", + "newSession": "새 세션", "threadEmptyTitle": "무엇이든 물어보세요", "threadEmptyHint": "자주 묻는 질문에 빠르게 답하고, 이어서 질문해 바로잡을 수 있어요" }, @@ -1195,7 +1195,7 @@ }, "providers": { "title": "모델 제공자", - "subtitle": "모델을 선택할 때의 소스입니다. 각 소스는 지원하는 모델과 과금 방식을 제공하며, 대화별로 전환할 수 있습니다.", + "subtitle": "모델을 선택할 때의 소스입니다. 각 소스는 지원하는 모델과 과금 방식을 제공하며, 세션별로 전환할 수 있습니다.", "pill": { "connected": "연결됨", "suspended": "비활성화됨" @@ -1653,7 +1653,7 @@ }, "intro": "연결하면 이 WeChat 연결로 보낸 개인 메시지가 이 컴퓨터의 Cindy로 전달됩니다. 인증 페이지는 Tencent가 제공하며 OpenClaw로 표시될 수 있습니다.", "errorNote": "연결이 완료되지 않았습니다. 네트워크를 확인하고 다시 스캔하세요. 연결 실패 시 Cindy는 인증 정보를 평문으로 저장하지 않습니다.", - "disabledNote": "호환성 정책에 따라 이 버전에서는 개인 WeChat 연결이 일시 중지되었습니다. 저장된 대화와 설정은 삭제되지 않습니다.", + "disabledNote": "호환성 정책에 따라 이 버전에서는 개인 WeChat 연결이 일시 중지되었습니다. 저장된 세션과 설정은 삭제되지 않습니다.", "permission": { "label": "권한 모드", "hint": "/new 이후의 새 컨텍스트에 적용됩니다. 위험한 작업은 계속 Cindy Desktop에서 확인해야 합니다.", @@ -1689,7 +1689,7 @@ "connected": "WeChat 개인 메시지는 이 컴퓨터의 Cindy로 직접 전달되며 Cindy 서버를 경유하지 않습니다.", "reconnecting": "WeChat 연결을 복구하고 있습니다. 다시 스캔할 필요가 없습니다.", "needs_reauth": "WeChat 인증이 만료되었거나 다른 연결로 교체되었습니다. 다시 스캔하세요.", - "disabled_by_policy": "호환성 정책으로 연결이 일시 중지되었습니다. 인증 정보와 대화는 유지됩니다.", + "disabled_by_policy": "호환성 정책으로 연결이 일시 중지되었습니다. 인증 정보와 세션은 유지됩니다.", "error": "연결을 일시적으로 사용할 수 없습니다. 다시 스캔하거나 연동 해제 후 나중에 다시 시도하세요." } }, @@ -1707,7 +1707,7 @@ }, "unbind": { "title": "개인 WeChat 연동을 해제하시겠습니까?", - "description": "연동을 해제하면 메시지 송수신을 중지하고 로컬 WeChat 인증 정보와 미완료 작업을 삭제합니다. 기존 Cindy 대화, 기록, 저장된 첨부 파일은 유지됩니다.", + "description": "연동을 해제하면 메시지 송수신을 중지하고 로컬 WeChat 인증 정보와 미완료 작업을 삭제합니다. 기존 Cindy 세션, 기록, 저장된 첨부 파일은 유지됩니다.", "confirm": "연동 해제", "cancel": "취소" }, @@ -1851,7 +1851,7 @@ }, "commandMenu": { "start": "환영 메시지와 사용법 보기", - "new": "새 세션 시작", + "new": "새 대화 시작", "help": "명령어 도움말 보기", "stop": "현재 작업 중지", "session": "최근 세션 목록에서 이어서 대화", @@ -1967,7 +1967,7 @@ }, "memory": { "title": "메모리", - "description": "워크스페이스 단위 메모리를 구현합니다. 설정 변경 후에는 일반적으로 새 Session을 시작해야 적용됩니다. 메모리에 문제가 있다면 Reset하거나 끄세요.", + "description": "워크스페이스 단위 메모리를 구현합니다. 설정 변경 후에는 일반적으로 새 Agent 세션을 시작해야 적용됩니다. 메모리에 문제가 있다면 Reset하거나 끄세요.", "maker": { "label": "Cindy", "description": "메모리는 개별 모델이 아니라 Agent 진입점별로 관리됩니다. 동일한 작업 디렉터리에서 Claude Code와 Codex 진입점으로 시작한 Session은 메모리를 공유합니다", @@ -2005,7 +2005,7 @@ "toast": { "enabled": "{{label}} 메모리가 활성화되었습니다", "disabled": "{{label}} 메모리가 비활성화되었습니다", - "takesEffectSuffix": " — 새 채팅에서 적용됩니다", + "takesEffectSuffix": " — 다음 Agent 세션부터 적용됩니다", "toggleFailed": "메모리 설정 업데이트 실패", "resetSuccess_one": "{{label}} 메모리가 재설정되었습니다 — {{count}}개 항목 제거됨", "resetSuccess_other": "{{label}} 메모리가 재설정되었습니다 — {{count}}개 항목 제거됨", @@ -2175,7 +2175,7 @@ "title": "자동 조작 브라우저", "description": "에이전트 전용 독립 브라우저 — 평소 쓰는 Chrome과 분리됩니다", "toggleAria": "브라우저 자동화 전환", - "toggleHint": "이 토글은 이후 시작하는 Agent 세션에만 적용되며, 진행 중인 대화에는 영향을 주지 않습니다.", + "toggleHint": "이 토글은 이후 시작하는 Agent 세션에만 적용되며, 진행 중인 세션에는 영향을 주지 않습니다.", "noProjectHint": "프로젝트 세션에서 설정을 열면 해당 프로젝트의 브라우저 자동화를 전환할 수 있습니다.", "detected": "{{browser}} 감지됨", "notDetected": "로컬 브라우저를 찾을 수 없습니다. Chrome을 설치하면 브라우저 자동화를 사용할 수 있습니다.", @@ -2210,7 +2210,7 @@ "description": "에이전트가 ADB를 통해 연결된 Android 기기를 검사하고 조작할 수 있습니다", "toggleAria": "Android 자동화 전환", "refresh": "다시 확인", - "toggleHint": "이 시스템 전체 토글은 이후 시작하는 Agent 세션에만 적용되며, 진행 중인 대화에는 영향을 주지 않습니다.", + "toggleHint": "이 시스템 전체 토글은 이후 시작하는 Agent 세션에만 적용되며, 진행 중인 세션에는 영향을 주지 않습니다.", "status": { "checking": "ADB 상태 확인 중…", "preparing": "ADB 준비 중…", @@ -2347,7 +2347,7 @@ "windowsHint": "Windows에서는 추가 시스템 권한이 필요하지 않습니다. 에이전트가 작업하는 동안 대상 앱을 현재 활성 데스크톱에 보이도록 유지하세요.", "linuxHint": "Linux에서는 macOS TCC 권한을 사용하지 않습니다. 현재 데스크톱 세션에서 창 검사와 입력 자동화가 허용되는지 확인하세요." }, - "toggleHint": "이 토글은 이후 시작하는 Agent 세션에만 적용되며, 진행 중인 대화에는 영향을 주지 않습니다.", + "toggleHint": "이 토글은 이후 시작하는 Agent 세션에만 적용되며, 진행 중인 세션에는 영향을 주지 않습니다.", "noProjectHint": "프로젝트 세션에서 설정을 열면 해당 프로젝트의 컴퓨터 직접 제어를 전환할 수 있습니다.", "update": { "available": "새 버전 v{{version}}을(를) 사용할 수 있습니다. 업데이트 후 시스템 권한을 다시 허용해야 할 수 있습니다.", @@ -2376,21 +2376,21 @@ }, "builtinTools": { "title": "기본 제공 도구", - "description": "새 대화의 기본값을 설정하거나 프로젝트별로 재정의합니다. 프로젝트 설정이 우선합니다.", + "description": "새 세션의 기본값을 설정하거나 프로젝트별로 재정의합니다. 프로젝트 설정이 우선합니다.", "empty": "사용 가능한 도구가 없습니다.", "toggleAria": "{{name}} 도구 전환", "toggleHint": "변경 사항은 새로 만들거나 다시 시작한 Agent 세션에만 적용되며, 실행 중인 세션에는 영향을 주지 않습니다.", "loadFailed": "도구 목록을 불러오지 못했습니다", "scopePicker": { "ariaLabel": "기본 제공 도구 설정 범위 전환", - "userDefault": "새 대화 기본값", + "userDefault": "새 세션 기본값", "userDefaultDescription": "프로젝트 재정의가 없는 새 세션에 적용" }, "source": { "project": "프로젝트", "projectTooltip": "프로젝트 설정으로 재정의됨 ({{dir}})", "user": "사용자 기본값", - "userTooltip": "새 대화 기본값을 따름", + "userTooltip": "새 세션 기본값을 따름", "product": "제품 기본값", "productTooltip": "Cindy 제품 기본값을 따름" }, @@ -2758,8 +2758,8 @@ "description": "설정 페이지를 엽니다" }, "new-maker": { - "label": "새 대화", - "description": "새 대화를 시작합니다" + "label": "새 세션", + "description": "새 세션을 시작합니다" }, "close-tab-or-window": { "label": "탭 또는 창 닫기", @@ -2952,7 +2952,7 @@ "projectSwitchLockedTip": "이 플러그인은 전역에서 비활성 상태입니다. 먼저 '전역' 보기에서 활성화해 주세요", "projectBanner": { "title": "프로젝트 \"{{name}}\" 설정 중", - "desc": "여기 스위치는 이 프로젝트에만 적용됩니다. 끈 플러그인은 새 세션에 나타나지 않습니다", + "desc": "여기 스위치는 이 프로젝트에만 적용됩니다. 끈 플러그인은 이 프로젝트에서 사용할 수 없습니다", "backToGlobal": "전역으로 돌아가기" }, "scopePicker": { @@ -2983,7 +2983,7 @@ "updated": "\"{{name}}\"이(가) v{{version}}(으)로 업데이트되었습니다", "exported": "\"{{name}}\"의 .cindy 파일을 내보냈습니다", "exportFailed": "\"{{name}}\"을(를) 내보내지 못했습니다. 다시 시도하세요", - "projectDisabled": "이 프로젝트에서 \"{{name}}\"을(를) 껐습니다(새 세션부터 적용)", + "projectDisabled": "이 프로젝트에서 \"{{name}}\"을(를) 껐습니다", "projectEnabled": "\"{{name}}\"이(가) 이 프로젝트에서 전역 설정을 따릅니다" }, "panelError": { @@ -4521,7 +4521,7 @@ "search": "검색", "local": "로컬", "projects": "프로젝트", - "conversations": "대화" + "conversations": "세션" } }, "addRemoteProject": { @@ -4769,7 +4769,7 @@ "disabledHint": "이 프로젝트에서는 협업 모드가 비활성화되어 있습니다. 프로젝트 설정에서 활성화하세요.", "unavailableHint": "이 프로젝트의 협업 설정을 확인할 수 없습니다. 잠시 후 다시 시도하세요.", "unsupportedRemoteHint": "제어 대상 기기의 버전이 오래되어 협업을 사용할 수 없습니다.", - "remoteContinueNotice": " 이 메시지는 단일 대화로 계속 전송됩니다.", + "remoteContinueNotice": " 이 메시지는 단일 세션으로 계속 전송됩니다.", "handoffRecoveredMessage": "지난번 협업 시작이 중단되었습니다. 전송되지 않은 메시지를 입력창에 복원했습니다.", "handoffRecoveredGoal": "지난번 협업 시작이 중단되었습니다. 목표가 시작되지 않아 내용을 입력창에 복원했습니다.", "loadingHint": "이 프로젝트의 협업 설정을 확인하는 중입니다. 잠시 후 다시 시도하세요.", @@ -5042,7 +5042,7 @@ "outputFile": "출력: {{path}}" }, "forkOrigin": { - "label": "원래 대화에서 생성됨" + "label": "원래 세션에서 생성됨" }, "imageMissing": { "title": "이미지가 손실되었습니다" @@ -5056,13 +5056,13 @@ "copy": "메시지 복사", "linkCopied": "링크가 복사되었습니다", "moreActions": "메시지 추가 작업", - "copyLink": "현재 대화 링크 복사", + "copyLink": "현재 메시지 링크 복사", "copyLinkFailed": "링크를 복사하지 못했습니다", "rewind": "여기로 돌아가기", - "fork": "새 대화에서 계속하기", - "forkConfirmTitle": "여기서 새 대화를 시작할까요?", - "forkConfirmDescription": "이 지점의 대화 맥락을 사용해 독립된 새 대화를 만듭니다. 원래 대화는 변경되지 않으며, 이후 두 대화의 메시지는 서로에게 영향을 주지 않습니다.", - "forkConfirm": "새 대화 시작", + "fork": "새 세션에서 계속하기", + "forkConfirmTitle": "여기서 새 세션을 시작할까요?", + "forkConfirmDescription": "이 지점의 대화 맥락을 사용해 독립된 새 세션을 만듭니다. 원래 세션은 변경되지 않으며, 이후 두 세션의 메시지는 서로에게 영향을 주지 않습니다.", + "forkConfirm": "새 세션 시작", "forkCancel": "취소", "delete": "이 메시지 삭제", "deleteConfirmTitle": "이 메시지를 삭제할까요?", @@ -5084,16 +5084,16 @@ "retry": "다시 시도", "continueAfterReset": "재설정 후 계속", "continueAfterResetTitle": "사용량 제한 재설정 후 현재 작업을 계속할 일회성 자동화 만들기", - "credentialSwitchBusy": "다른 Codex 작업이 실행 중이어서 이 대화에서 선택한 제공자로 아직 전환할 수 없습니다(공유 Codex 프로세스 재시작 필요). 다른 작업이 끝난 후 ‘재시도’를 눌러 주세요.", - "codexThreadStale": "Codex 인증 모드가 변경되었습니다. 이 대화는 계속할 수 없습니다. 새 대화를 시작하세요.", + "credentialSwitchBusy": "다른 Codex 작업이 실행 중이어서 이 세션에서 선택한 제공자로 아직 전환할 수 없습니다(공유 Codex 프로세스 재시작 필요). 다른 작업이 끝난 후 ‘재시도’를 눌러 주세요.", + "codexThreadStale": "Codex 인증 모드가 변경되었습니다. 이 세션은 계속할 수 없습니다. 새 세션을 시작하세요.", "claudeGatewayOpusPlanMismatch": "이 Opus 요청은 Claude.ai 구독이 아니라 XD Gateway를 통해 전송되었고, 해당 게이트웨이 경로에서 모델을 거부했습니다. Claude.ai 구독으로 전환해 다시 시도하거나 다른 모델을 선택하세요.", "switchClaudeSubscription": "Claude.ai로 전환 후 재시도", - "switchClaudeSubscriptionTitle": "이 대화를 Claude.ai 구독으로 전환하고 다시 시도", + "switchClaudeSubscriptionTitle": "이 세션을 Claude.ai 구독으로 전환하고 다시 시도", "switchingClaudeSubscription": "Claude.ai로 전환하는 중…", - "claudeSubscriptionSwitchFailed": "Claude.ai로 전환하거나 이 대화를 다시 시도할 수 없습니다: {{msg}}", + "claudeSubscriptionSwitchFailed": "Claude.ai로 전환하거나 이 세션을 다시 시도할 수 없습니다: {{msg}}", "budgetModelHint": "계속 실패하면 일반 GPT 모델로 전환해 보세요.", "codexAuthMissing": "원격 Codex가 아직 로그인되지 않았습니다(401 Unauthorized). 「인증 동기화」를 클릭하여 로컬 Codex 자격 증명을 원격 Cindy Codex home으로 푸시한 후 다시 시도하세요.", - "codexAuthMissingLocal": "Codex 자격 증명이 없거나 만료되었습니다(401 Unauthorized). {{appName}} Settings → Codex에서 다시 로그인하거나(로그인 시 app-server가 자동 재시작) 터미널에서 `codex login` 실행 후 Settings에서 인증 모드를 한 번 전환하여 app-server를 재시작한 다음 새 대화를 시작하세요.", + "codexAuthMissingLocal": "Codex 자격 증명이 없거나 만료되었습니다(401 Unauthorized). {{appName}} Settings → Codex에서 다시 로그인하거나(로그인 시 app-server가 자동 재시작) 터미널에서 `codex login` 실행 후 Settings에서 인증 모드를 한 번 전환하여 app-server를 재시작한 다음 새 세션을 시작하세요.", "codexSessionExpired": "OpenAI 연결을 업데이트해야 합니다. ChatGPT를 다시 연결하면 계속할 수 있습니다. {{appName}}에서 시스템 Codex CLI를 로그아웃하거나 다른 모델 소스의 재연결을 요구하지 않습니다.", "codexSessionExpiredDialog": { "title": "OpenAI 연결 업데이트 필요", @@ -5438,7 +5438,7 @@ "detail": { "reason": "중단 원인", "attempt": "이번 재시도 {{attempt}}/{{total}}", - "sessionTotal": "이 대화에서 재연결 {{count}}회" + "sessionTotal": "이 세션에서 재연결 {{count}}회" } }, "autoResumeSeparator": { @@ -5519,7 +5519,7 @@ "messageCount_other": "메시지 {{count}}개", "truncated": "일부 생략" }, - "forkBusy": "현재 응답이 종료된 후 여기서 새 대화를 시작해 주세요", + "forkBusy": "현재 응답이 종료된 후 여기서 새 세션을 시작해 주세요", "editStopTimeout": "작업 중지 대기 시간이 초과되었습니다. 다시 시도하세요", "editQueueNotEmpty": "이 세션에 대기 중인 메시지가 있습니다. 먼저 전송하거나 제거한 후 편집하세요", "editNotLast": "세션에 더 새로운 메시지가 있어 이 메시지는 더 이상 최신이 아니므로 편집하여 재전송할 수 없습니다", @@ -5528,11 +5528,11 @@ "editSend": "전송", "editRollbackHint": "전송하면 이전 턴에서 변경된 파일 {{count}}개가 되돌려집니다", "forkErrors": { - "unsupportedHistory": "이 지점에 해당하는 이전 에이전트 세션을 사용할 수 없어 여기서 새 대화를 시작할 수 없습니다", - "noPriorAssistant": "AI 응답 뒤의 메시지에서 새 대화를 시작해 주세요", - "sourceNeverRan": "원래 대화가 아직 실행되지 않아 새 대화를 시작할 수 없습니다", - "codexStateUnavailable": "Codex 대화 상태를 사용할 수 없어 새 대화를 시작할 수 없습니다. {{detail}}", - "generic": "새 대화를 시작하지 못했습니다. 다시 시도해 주세요" + "unsupportedHistory": "이 지점에 해당하는 이전 에이전트 세션을 사용할 수 없어 여기서 새 세션을 시작할 수 없습니다", + "noPriorAssistant": "AI 응답 뒤의 메시지에서 새 세션을 시작해 주세요", + "sourceNeverRan": "원래 세션이 아직 실행되지 않아 새 세션을 시작할 수 없습니다", + "codexStateUnavailable": "Codex 세션 상태를 사용할 수 없어 새 세션을 시작할 수 없습니다. {{detail}}", + "generic": "새 세션을 시작하지 못했습니다. 다시 시도해 주세요" } }, "threadContext": { @@ -5833,11 +5833,11 @@ }, "showAllSessions": "모두 표시 {{count}}개", "dialogues": "대화", - "newDialogue": "새 대화", + "newDialogue": "새 세션", "dialoguesToggleExpand": "대화 펼치기", "dialoguesToggleCollapse": "대화 접기", "loadingDialogues": "불러오는 중…", - "noDialogues": "대화 없음", + "noDialogues": "세션 없음", "dialogueSettings": "대화 정리", "dialogueSettingsAria": "대화 정리(정렬: {{sortBy}})", "dialogueSortHeading": "정렬", @@ -6037,7 +6037,7 @@ "moveToProjectRemoteUnsupported": "원격 세션 이동은 아직 지원하지 않습니다", "openInNewWindow": "새 창에서 열기", "exportShare": "세션 내보내기…", - "copySessionLink": "대화 링크 복사" + "copySessionLink": "세션 링크 복사" }, "projectAction": { "rename": "프로젝트 이름 변경", @@ -6272,11 +6272,11 @@ "open": "검색", "placeholder": "제목 또는 내용 검색…", "clear": "검색 지우기", - "empty": "키워드를 입력해 대화 제목과 내용을 검색하세요", + "empty": "키워드를 입력해 세션 제목과 내용을 검색하세요", "searching": "검색 중…", "failed": "검색 실패", - "noResults": "일치하는 대화가 없습니다", - "jumpFailed": "대화를 열었지만 일치한 메시지로 이동하지 못했습니다", + "noResults": "일치하는 세션이 없습니다", + "jumpFailed": "세션을 열었지만 일치한 메시지로 이동하지 못했습니다", "moreHits": "관련 {{count}}곳 더 있음", "kind": { "title": "제목", @@ -6722,28 +6722,28 @@ "deleteDialog": { "title": "자동화 \"{{name}}\"을(를) 삭제하시겠습니까?", "titleFallback": "자동화를 삭제하시겠습니까?", - "description": "향후 트리거가 중지됩니다. 이 자동화가 이미 생성한 대화를 어떻게 처리할지 선택하세요.", - "sessionCountPending": "생성된 대화를 확인하는 중…", - "sessionCount": "생성된 대화 {{count}}개", + "description": "향후 트리거가 중지됩니다. 이 자동화가 이미 생성한 세션을 어떻게 처리할지 선택하세요.", + "sessionCountPending": "생성된 세션을 확인하는 중…", + "sessionCount": "생성된 세션 {{count}}개", "inflightCount": "현재 {{count}}건의 실행이 진행 중이며 삭제하면 중지됩니다", - "optionAria": "생성된 대화 처리 방식 선택", + "optionAria": "생성된 세션 처리 방식 선택", "option": { "keep": { - "title": "대화 유지", - "description": "자동화 작업만 삭제하고 대화는 사이드바에 유지합니다." + "title": "세션 유지", + "description": "자동화 작업만 삭제하고 세션은 사이드바에 유지합니다." }, "archive": { - "title": "대화 보관", - "description": "자동화 작업을 삭제하고 생성된 대화를 보관함으로 이동합니다." + "title": "세션 보관", + "description": "자동화 작업을 삭제하고 생성된 세션을 보관함으로 이동합니다." }, "delete": { - "title": "대화 삭제", - "description": "자동화 작업과 생성된 대화를 삭제합니다. 이 작업은 되돌릴 수 없습니다." + "title": "세션 삭제", + "description": "자동화 작업과 생성된 세션을 삭제합니다. 이 작업은 되돌릴 수 없습니다." } }, "confirm": "자동화 삭제", - "confirmDeleteSessions": "자동화와 대화 삭제", - "sessionUpdatePartial": "자동화는 삭제되었지만 {{fail}}개 대화를 처리하지 못했습니다" + "confirmDeleteSessions": "자동화와 세션 삭제", + "sessionUpdatePartial": "자동화는 삭제되었지만 {{fail}}개 세션을 처리하지 못했습니다" }, "confirm": { "delete": { @@ -6843,7 +6843,7 @@ "refresh": "새로 고침", "emptyTitle": "아직 제출한 이슈가 없습니다", "emptyTitleUnverified": "현재 이슈를 가져올 수 없습니다", - "startIssueChat": "/issue 로 새 채팅 시작", + "startIssueChat": "/issue 로 새 세션 시작", "statusUnknown": "상태 알 수 없음", "sourceCindy": "Cindy에서 제출", "sourceGithub": "GitHub 계정", @@ -7692,7 +7692,7 @@ "migrationPartial": "마이그레이션 완료: 성공 {{success}} / 건너뜀 {{skipped}} / 실패 {{failed}} (총 {{total}})", "migrationFailed": "마이그레이션 실패. 잠시 후 다시 시도하세요", "fetchUpdateNoticeFailed": "업데이트 알림 가져오기 실패", - "corruptionRestored": "{{time}}의 스냅샷에서 데이터베이스가 복원됨. 그 사이의 채팅이 손실되었을 수 있습니다", + "corruptionRestored": "{{time}}의 스냅샷에서 데이터베이스가 복원됨. 그 사이의 대화 기록이 손실되었을 수 있습니다", "schemaDriftWarning": "로컬 데이터베이스 버전 기록이 일치하지 않습니다. 최신 버전으로 업데이트한 뒤에도 이 경고가 계속 표시되면 지원팀에 문의해 주세요. 앱은 로컬 데이터를 자동으로 삭제하지 않습니다.", "telegramBotConnected": "Telegram 봇 구성이 저장됨", "telegramBotConnectFailed": "Telegram 봇 연결 실패. Token과 네트워크를 확인하세요", @@ -7910,7 +7910,7 @@ }, "toast": { "usage": "사용법: /goal <목표 조건>", - "noSession": "목표를 설정하려면 먼저 대화를 열거나 시작하세요", + "noSession": "목표를 설정하려면 먼저 세션을 열거나 시작하세요", "failed": "목표 설정에 실패했습니다", "set": "목표를 설정했습니다 — 자동으로 진행합니다", "cleared": "목표를 지웠습니다" @@ -7933,7 +7933,7 @@ "failed": "학습에 실패했습니다", "cancel": "취소", "review": "제안 검토", - "fromConversation": "현재 대화에서 증류", + "fromConversation": "현재 세션에서 증류", "openSession": "학습 세션 열기", "continueHint": "이 세션에서 계속 대화하면 제안을 수정할 수 있습니다. 만족하면 \"제안 검토\"로 적용하세요" }, diff --git a/apps/desktop/src/renderer/i18n/locales/zh-CN/common.json b/apps/desktop/src/renderer/i18n/locales/zh-CN/common.json index c8a143d5408..6165d82c11b 100644 --- a/apps/desktop/src/renderer/i18n/locales/zh-CN/common.json +++ b/apps/desktop/src/renderer/i18n/locales/zh-CN/common.json @@ -1,8 +1,8 @@ { "ipcError": { - "SESSION_RUNNING": "当前对话正在执行任务,请稍后再试。", - "CREDENTIAL_SWITCH_BUSY": "其他 Codex 任务正在运行,本次来源切换未生效(对话仍使用原来源)。等其他任务结束后再试。", - "REMOTE_MODEL_SWITCH_ROUTE_CHANGE": "目标模型或来源需要不同的远端路由,请关闭并重新创建远程对话后生效。", + "SESSION_RUNNING": "当前任务正在运行,请稍后再试。", + "CREDENTIAL_SWITCH_BUSY": "Codex 正在其它任务中运行,本次来源切换未生效(本任务仍使用原来源)。等它结束后再试。", + "REMOTE_MODEL_SWITCH_ROUTE_CHANGE": "目标模型或来源需要不同的远端路由,请关闭并重新创建远程任务后生效。", "REMOTE_PROVIDER_UPDATING": "供应商凭证正在更新,请稍后重试", "REMOTE_PROVIDER_UNSUPPORTED": "该供应商不支持 SSH 远程会话,请更换来源", "REMOTE_NATIVE_OAUTH_UNAVAILABLE": "未连接 Claude.ai 订阅,请先连接或选择网关模型", @@ -52,8 +52,8 @@ }, "agentIsland": { "native": { - "newConversationTitle": "新建对话", - "newConversationHint": "开始一个新的对话", + "newConversationTitle": "新建任务", + "newConversationHint": "开始一个新的任务", "muteSound": "关闭灵动岛声音", "enableSound": "开启灵动岛声音", "settings": "灵动岛设置", @@ -73,7 +73,7 @@ "permissionPromptTitle": "需要确认权限", "permissionPromptTitleWithTool": "允许 {{toolName}}?", "allowOnce": "允许一次", - "alwaysAllowForSession": "本对话总是允许", + "alwaysAllowForSession": "本任务总是允许", "deny": "拒绝" } }, @@ -94,7 +94,7 @@ "voiceInput": "语音输入", "shortcuts": "键盘快捷键", "agentIsland": "灵动岛", - "import": "对话导入", + "import": "任务导入", "connections": "第三方平台", "providers": "模型供应商", "remoteControl": "远程连接", @@ -129,7 +129,7 @@ "linkOpen": "链接打开方式", "compatMode": "小技巧", "voiceInput": "语音输入", - "import": "对话导入", + "import": "任务导入", "providers": "模型供应商", "remote": "远端机器", "devices": "设备互联", @@ -321,15 +321,15 @@ "agentProxyEnvPending": "等待应用环境变量 → {{url}}(主机连接后自动生效)" }, "startSession": { - "title": "在这台远端开新 Codex 对话", + "title": "在这台远端开新 Codex 任务", "hint": "新建一个绑定到这台机器的 Maker。Codex 跑在远端,你在这里和它聊。工作目录支持 ~ 展开,不存在会确认后创建。", "start": "开始", - "created": "已在 {{hostId}} 上创建远端对话", - "createFailed": "创建远端对话失败", + "created": "已在 {{hostId}} 上创建远端任务", + "createFailed": "创建远端任务失败", "errorWorkdirEmpty": "请输入远端工作目录", "errorWorkdirIsFile": "{{path}} 已存在但不是目录,换一个路径", "confirmCreateTitle": "目录不存在,创建吗?", - "confirmCreateDescription": "{{path}} 在 {{hostId}} 上不存在。点确认会在远端跑 mkdir -p 创建它,然后进入对话。", + "confirmCreateDescription": "{{path}} 在 {{hostId}} 上不存在。点确认会在远端跑 mkdir -p 创建它,然后进入任务。", "confirmCreateOk": "创建并继续" }, "codexSync": { @@ -455,19 +455,19 @@ }, "sidebarCardMode": { "label": "侧边栏卡片模式", - "hint": "置顶对话以卡片形式展示,含任务现状摘要,卡片高度随摘要长短错落;侧栏拉宽时自动分 2~3 列。", + "hint": "置顶任务以卡片形式展示,含执行状态摘要,卡片高度随摘要长短错落;侧栏拉宽时自动分 2~3 列。", "aria": "切换侧边栏卡片模式" } }, "notifications": { "title": "通知", - "sessionDoneLabel": "对话完成时弹出系统通知", + "sessionDoneLabel": "任务完成时弹出系统通知", "sessionDoneHint": "仅在窗口失焦时弹出,避免前台打扰。", - "sessionDoneAria": "切换对话完成的系统通知", - "feishuLabel": "对话完成或需要回复时发送飞书 / Lark 消息", + "sessionDoneAria": "切换任务完成的系统通知", + "feishuLabel": "任务完成或需要回复时发送飞书 / Lark 消息", "feishuHint": "通过已绑定的飞书或 Lark 机器人,向你私聊发送一条消息;仅在窗口失焦时发送。", "feishuDisabledHint": "需要先在「飞书 / Lark 机器人」中配置 bot 并向它发送一条消息以完成绑定。", - "feishuAria": "切换对话完成的飞书通知" + "feishuAria": "切换任务完成的飞书通知" }, "windowBehavior": { "title": "应用行为", @@ -639,27 +639,27 @@ "description": "这里是 {{appName}} 的 overview,用来说明进入应用后如何开始,以及更推荐的工作方式。", "gettingStarted": { "title": "快速开始", - "step1": "先新建一个对话,告诉 Claude Code 或 Codex 你要完成的目标。", + "step1": "先新建一个任务,告诉 Claude Code 或 Codex 你要完成的目标。", "step2": "如果你的工作和项目代码有关,先选择工作目录,再让它读文件、解释代码或修改实现。", "step3": "把对话区当作持续推进工作的地方:提出任务、查看结果、根据结果继续推进。" }, "workflows": { "title": "推荐工作流", - "description": "{{appName}} 适合把不同任务拆成不同对话,并在侧边栏之间快速切换。", + "description": "{{appName}} 适合把不同目标拆成不同任务,并在侧边栏之间快速切换。", "item1": "探索代码:先让 Agent 解释项目结构、关键文件和约束,再开始修改。", "item2": "处理改动:让 Agent 先给出小步修改,再逐轮验证、修正并收敛。", - "item3": "并行任务:需要隔离上下文时,创建新对话分别处理不同目标。" + "item3": "并行处理:需要隔离上下文时,创建新任务分别处理不同目标。" }, "recommendedWays": { "title": "推荐的使用方式", "description": "先描述目标和上下文,再给出你希望的结果形式,通常比只发一句短命令更高效。", "item1": "先说清楚你要完成什么,再补充相关目录、文件或限制条件。", "item2": "把大任务拆成多轮:先理解,再修改,再验证。", - "item3": "当你要处理独立问题时,创建新对话而不是把所有事情混在一个对话里。" + "item3": "当你要处理独立问题时,创建新任务而不是把所有事情混在一个任务里。" }, "qnaTitle": "直接提问", "qnaDescription": "如果 overview 里没有覆盖到你的问题,可以直接问我该怎么做。", - "qnaPlaceholder": "例如:如何导入 codex 对话", + "qnaPlaceholder": "例如:如何导入 codex 任务", "qnaSend": "发送", "qnaPending": "正在整理答案…", "qnaNoAnswer": "暂时还没有可靠答案,你可以换个说法再问一次。", @@ -678,7 +678,7 @@ "openAssistant": "直接提问", "panelTitle": "帮助助手", "panelAriaLabel": "帮助问答面板", - "newSession": "新对话", + "newSession": "新任务", "threadEmptyTitle": "有问题尽管问", "threadEmptyHint": "覆盖常见问题的快速答案,可以追问纠正" }, @@ -732,7 +732,7 @@ "statsFailed": "统计暂不可用", "legacyUsage": "旧版本数据(只读,不参与清理):{{size}}", "cleanupLabel": "磁盘清理", - "cleanupDescription": "扫描没有被任何对话引用的媒体文件、超限缓存与废弃目录。先报数,确认后才删除;输入框草稿和排队消息里的图片会被自动保护。", + "cleanupDescription": "扫描没有被任何任务引用的媒体文件、超限缓存与废弃目录。先报数,确认后才删除;输入框草稿和排队消息里的图片会被自动保护。", "scanButton": "扫描", "scanFailed": "扫描失败", "reportZeroRef": "无引用文件 {{count}} 个({{size}})", @@ -1059,32 +1059,32 @@ } }, "sessionImport": { - "title": "本地对话导入", - "description": "只读扫描本机 Claude Code 与 Codex 历史。只有列表里勾选的对话会被导入;已跳过的内部对话不会展示,也不会导入。", + "title": "本地任务导入", + "description": "只读扫描本机 Claude Code 与 Codex 历史。只有列表里勾选的任务会被导入;已跳过的内部 Agent 会话不会展示,也不会导入。", "scan": "扫描", "scanning": "扫描中", "emptyTitle": "先扫描本地历史", "emptyDescription": "扫描不会写入数据库,也不会改变侧边栏。", - "noCandidates": "当前筛选下没有可导入的对话。", + "noCandidates": "当前筛选下没有可导入的任务。", "collapse": "收起分组", "expand": "展开分组", "selectGroup": "选择整个分组", - "sessions": "条对话", + "sessions": "条任务", "selected": "已选择 {{count}} 条", "importSelected": "导入选中", "importing": "导入中", "archived": "已归档", - "untitled": "未命名对话", + "untitled": "未命名任务", "noWorkingDir": "无工作目录", "summary": { "total": "列表中", "totalHint": "可以勾选导入", - "projects": "项目对话", - "projectsHint": "导入后进入项目分组的对话", + "projects": "项目任务", + "projectsHint": "导入后进入项目分组的任务", "dialogue": "对话", - "dialogueHint": "Codex 标记为不属于项目的对话", + "dialogueHint": "Codex 标记为不属于项目的任务", "filtered": "已跳过", - "filteredHint": "内部对话或已在应用内" + "filteredHint": "内部 Agent 会话或已在应用内" }, "filters": { "source": "来源", @@ -1140,7 +1140,7 @@ "xai": { "logoutConfirm": { "title": "断开 xAI(SuperGrok)?", - "description": "断开会清除本机保存的 xAI 登录凭证;已选 Grok 模型的对话将无法调用,直到重新授权。", + "description": "断开会清除本机保存的 xAI 登录凭证;已选 Grok 模型的任务将无法调用,直到重新授权。", "confirm": "断开", "cancel": "取消" }, @@ -1195,7 +1195,7 @@ }, "providers": { "title": "模型供应商", - "subtitle": "选择模型时的来源。每个来源提供它支持的模型与计费方式,可按对话分别切换。", + "subtitle": "选择模型时的来源。每个来源提供它支持的模型与计费方式,可按任务分别切换。", "pill": { "connected": "已连接", "suspended": "已停用" @@ -1252,7 +1252,7 @@ }, "rotateConfirm": { "title": "轮换 Cindy AI 密钥?", - "description": "将撤销当前密钥并签发新密钥。旧密钥立即失效:使用旧密钥的进行中对话、远程对话与手机语音需要重新发起。", + "description": "将撤销当前密钥并签发新密钥。旧密钥立即失效:使用旧密钥的进行中任务、远程任务与手机语音需要重新发起。", "confirm": "轮换" }, "rotateSuccess": "密钥已轮换", @@ -1327,7 +1327,7 @@ }, "deleteConfirm": { "title": "删除自定义供应商?", - "description": "将删除「{{name}}」的配置和本机保存的密钥,正在使用它的对话会回退到默认来源。", + "description": "将删除「{{name}}」的配置和本机保存的密钥,正在使用它的任务会回退到默认来源。", "confirm": "删除", "cancel": "取消" }, @@ -1653,7 +1653,7 @@ }, "intro": "连接后,你发给该微信连接的私聊消息会进入本机 Cindy。授权网页由腾讯提供,页面中可能显示 OpenClaw。", "errorNote": "本次连接未完成。请确认网络正常后重新扫码;Cindy 不会在连接失败时保存明文凭证。", - "disabledNote": "此版本的个人微信连接已因兼容性策略暂停。已保存的对话和设置不会被删除。", + "disabledNote": "此版本的个人微信连接已因兼容性策略暂停。已保存的任务和设置不会被删除。", "permission": { "label": "权限模式", "hint": "仅影响 /new 后的新上下文。高风险操作仍会要求你回到 Cindy 桌面端确认。", @@ -1689,7 +1689,7 @@ "connected": "微信私聊消息会直接进入本机 Cindy,消息不经过 Cindy 服务端转发。", "reconnecting": "本机正在恢复与微信的连接,无需重新扫码。", "needs_reauth": "微信授权已失效或被新的连接替换,请重新扫码。", - "disabled_by_policy": "连接因当前版本的兼容性策略暂停;凭证和对话仍保留。", + "disabled_by_policy": "连接因当前版本的兼容性策略暂停;凭证和任务仍保留。", "error": "连接暂时失败。可以重新扫码,或解绑后稍后再试。" } }, @@ -1707,7 +1707,7 @@ }, "unbind": { "title": "解绑个人微信?", - "description": "解绑会停止收发、删除本机微信凭证并清理未完成任务。已创建的 Cindy 对话、历史消息和已保存附件会保留。", + "description": "解绑会停止收发、删除本机微信凭证并清理未完成的执行。已创建的 Cindy 任务、历史消息和已保存附件会保留。", "confirm": "解绑", "cancel": "取消" }, @@ -1853,12 +1853,12 @@ "start": "查看欢迎信息和用法", "new": "开启新对话", "help": "查看命令帮助", - "stop": "停止当前任务", - "session": "最近对话列表,选中即续聊", + "stop": "中止当前执行", + "session": "最近任务列表,选中即续聊", "project": "切换项目工作区", "model": "查看或切换模型", "permission": "查看或切换权限模式", - "ctr": "接管桌面端的对话", + "ctr": "接管桌面端的任务", "exctr": "退出远程控制" }, "behavior": { @@ -1967,10 +1967,10 @@ }, "memory": { "title": "记忆", - "description": "用于帮助用户实现基于工作区的记忆,改变设置之后理论上需要新建对话才会生效,如果觉得记忆系统出现问题可以点击 Reset 或者关闭。", + "description": "用于帮助用户实现基于工作区的记忆,改变设置之后理论上需要新建 Agent 会话才会生效,如果觉得记忆系统出现问题可以点击 Reset 或者关闭。", "maker": { "label": "Cindy", - "description": "记忆按 Agent 入口管理,而不是按具体模型管理;同一工作目录下,Claude Code 和 Codex 入口的对话可以共享记忆", + "description": "记忆按 Agent 入口管理,而不是按具体模型管理;同一工作目录下,Claude Code 和 Codex 入口的任务可以共享记忆", "toggleAria": "切换 Cindy 记忆", "resetAria": "重置 Cindy 记忆", "resetConfirm": { @@ -1982,7 +1982,7 @@ "toast": { "enabled": "Cindy 记忆已启用", "disabled": "Cindy 记忆已禁用", - "deferredSuffix": " — 运行中的 Codex 对话将在当前任务结束后应用", + "deferredSuffix": " — 运行中的 Codex 任务将在当前执行结束后应用", "toggleFailed": "切换 Cindy 记忆失败", "resetSuccess_one": "Cindy 记忆已重置 — 已移除 {{count}} 个工作目录", "resetSuccess_other": "Cindy 记忆已重置 — 已移除 {{count}} 个工作目录", @@ -2005,7 +2005,7 @@ "toast": { "enabled": "{{label}} 记忆已启用", "disabled": "{{label}} 记忆已禁用", - "takesEffectSuffix": " — 在新对话中生效", + "takesEffectSuffix": " — 下次 Agent 会话生效", "toggleFailed": "更新记忆设置失败", "resetSuccess_one": "{{label}} 记忆已重置 — 已移除 {{count}} 条", "resetSuccess_other": "{{label}} 记忆已重置 — 已移除 {{count}} 条", @@ -2043,7 +2043,7 @@ }, "toast": { "saved": "Subagent 设置已保存", - "deferredSuffix": " — 运行中的 Codex 对话将在当前任务结束后应用" + "deferredSuffix": " — 运行中的 Codex 任务将在当前执行结束后应用" } }, "compaction": { @@ -2108,14 +2108,14 @@ "hint": "LiteLLM 服务端跨客户端月度累计 vs max_budget" }, "session": { - "label": "对话 — 当前对话累计", - "hint": "当前对话终身累计 USD,跨 resume 持久化" + "label": "任务 — 当前任务累计", + "hint": "当前任务终身累计 USD,跨 resume 持久化" } } }, "compatMode": { "title": "小技巧", - "description": "一些功能性开关,部分需要新建对话才会生效(具体见各 cell 说明)。" + "description": "一些功能性开关,部分需要新建任务才会生效(具体见各 cell 说明)。" }, "chatEmbedding": { "cell": { @@ -2175,8 +2175,8 @@ "title": "自动操作浏览器", "description": "一个 Agent 专用的独立浏览器,与你日常的 Chrome 互不影响", "toggleAria": "切换浏览器自动化", - "toggleHint": "这个开关只对之后新建的 Agent 会话生效;已在运行的对话不受影响。", - "noProjectHint": "从项目对话中打开「设置」可针对该项目开关浏览器自动化。", + "toggleHint": "这个开关只对之后新建的 Agent 会话生效;已在运行的 Agent 会话不受影响。", + "noProjectHint": "从项目任务中打开「设置」可针对该项目开关浏览器自动化。", "detected": "已检测到 {{browser}}", "notDetected": "未检测到本机浏览器。安装 Chrome 后即可使用浏览器自动化。", "download": "下载 Chrome", @@ -2210,7 +2210,7 @@ "description": "让 Agent 通过 ADB 检查并操作已连接的 Android 设备", "toggleAria": "切换 Android 自动化", "refresh": "重新检查", - "toggleHint": "这个全局开关只对之后新建的 Agent 会话生效;已在运行的对话不受影响。", + "toggleHint": "这个全局开关只对之后新建的 Agent 会话生效;已在运行的 Agent 会话不受影响。", "status": { "checking": "正在检查 ADB 状态…", "preparing": "正在准备 ADB…", @@ -2347,8 +2347,8 @@ "windowsHint": "Windows 不需要额外系统授权。使用时请保持目标应用在当前活动桌面中可见,Agent 会接管前台操作。", "linuxHint": "Linux 不需要 macOS 的 TCC 授权;请确保当前桌面会话允许窗口读取和输入自动化。" }, - "toggleHint": "这个开关只对之后新建的 Agent 会话生效;已在运行的对话不受影响。", - "noProjectHint": "从项目对话中打开「设置」可针对该项目开关直接电脑控制。", + "toggleHint": "这个开关只对之后新建的 Agent 会话生效;已在运行的 Agent 会话不受影响。", + "noProjectHint": "从项目任务中打开「设置」可针对该项目开关直接电脑控制。", "update": { "available": "发现新版本 v{{version}},更新后如系统权限失效需重新授权。", "action": "更新", @@ -2376,21 +2376,21 @@ }, "builtinTools": { "title": "内置工具", - "description": "设置新对话默认值,或为指定项目添加覆盖。项目设置优先于用户默认值。", + "description": "设置 Agent 会话默认值,或为指定项目添加覆盖。项目设置优先于用户默认值。", "empty": "暂无可用的工具。", "toggleAria": "切换 {{name}} 工具", "toggleHint": "变更仅对新建或重启的 Agent 会话生效,正在运行的 Agent 会话不受影响。", "loadFailed": "加载工具列表失败", "scopePicker": { "ariaLabel": "切换内置工具设置的作用域", - "userDefault": "新对话默认值", - "userDefaultDescription": "适用于没有项目覆盖的新对话" + "userDefault": "Agent 会话默认值", + "userDefaultDescription": "适用于没有项目覆盖的 Agent 会话" }, "source": { "project": "项目", "projectTooltip": "由项目设置覆盖 ({{dir}})", "user": "用户默认", - "userTooltip": "沿用新对话默认值", + "userTooltip": "沿用 Agent 会话默认值", "product": "产品默认", "productTooltip": "沿用 Cindy 产品默认值" }, @@ -2419,7 +2419,7 @@ }, "memory": { "name": "Cindy 记忆", - "description": "跨 Agent 的长期记忆,让上下文在对话之间持续" + "description": "跨 Agent 的长期记忆,让上下文在任务之间持续" }, "xdt_helper": { "name": "Cindy Helper", @@ -2427,7 +2427,7 @@ }, "collab": { "name": "协同模式", - "description": "关闭后仅影响后续新建的对话,不会中止当前 Worker;用于多 Worker 团队编排" + "description": "关闭后仅影响后续新建的任务,不会中止当前 Worker;用于多 Worker 团队编排" }, "lsp": { "name": "LSP", @@ -2438,7 +2438,7 @@ "silentEncryptedRetry": { "cell": { "label": "静默丢弃协议加密内容并重试", - "description": "切换供应商时,自动丢弃历史中上一供应商的加密推理内容并静默重发,避免对话卡死;首次触发后到下次切换模型前持续生效。代价:被丢弃的轮次模型会重新推理、略多花 token。默认开启。" + "description": "切换供应商时,自动丢弃历史中上一供应商的加密推理内容并静默重发,避免任务卡死;首次触发后到下次切换模型前持续生效。代价:被丢弃的那几轮模型会重新推理、略多花 token。默认开启。" }, "toggleAria": "切换\"静默丢弃协议加密内容并重试\"", "toast": { @@ -2623,7 +2623,7 @@ "tina": { "chat": { "title": "对话", - "description": "不落任何仓库的纯对话;在 Slack、Telegram 或 X 里选择 chat 即可使用,每条对话 lane 独立复用 session。" + "description": "不落任何仓库的纯对话;在 Slack、Telegram 或 X 里选择 chat 即可使用,每条对话 lane 独立对应一个任务。" }, "prefs": { "agentLabel": "Agent", @@ -2698,7 +2698,7 @@ "deleteFailed": "删除失败", "disconnected": "已断开所有远程连接", "disconnectFailed": "断开远程连接失败", - "remoteSessionEnded": "被控设备已结束该远程对话" + "remoteSessionEnded": "被控设备已结束该远程任务" }, "control": "控制" }, @@ -2751,15 +2751,15 @@ "items": { "toggle-sidebar": { "label": "切换侧边栏", - "description": "收起或展开左侧对话列表" + "description": "收起或展开左侧任务列表" }, "open-settings": { "label": "打开设置", "description": "打开设置页面" }, "new-maker": { - "label": "新对话", - "description": "创建一个新的对话" + "label": "新任务", + "description": "创建一个新的任务" }, "close-tab-or-window": { "label": "关闭标签页或窗口", @@ -2815,47 +2815,47 @@ }, "cycle-permission-mode": { "label": "切换权限模式", - "description": "输入框聚焦时循环切换对话的权限模式" + "description": "输入框聚焦时循环切换任务的权限模式" }, "open-terminal": { "label": "打开命令行", "description": "在右侧栏打开终端(已打开则聚焦)" }, "switch-session-1": { - "label": "切换到第 1 个对话", - "description": "跳转到侧边栏第 1 个可见对话" + "label": "切换到第 1 个任务", + "description": "跳转到侧边栏第 1 个可见任务" }, "switch-session-2": { - "label": "切换到第 2 个对话", - "description": "跳转到侧边栏第 2 个可见对话" + "label": "切换到第 2 个任务", + "description": "跳转到侧边栏第 2 个可见任务" }, "switch-session-3": { - "label": "切换到第 3 个对话", - "description": "跳转到侧边栏第 3 个可见对话" + "label": "切换到第 3 个任务", + "description": "跳转到侧边栏第 3 个可见任务" }, "switch-session-4": { - "label": "切换到第 4 个对话", - "description": "跳转到侧边栏第 4 个可见对话" + "label": "切换到第 4 个任务", + "description": "跳转到侧边栏第 4 个可见任务" }, "switch-session-5": { - "label": "切换到第 5 个对话", - "description": "跳转到侧边栏第 5 个可见对话" + "label": "切换到第 5 个任务", + "description": "跳转到侧边栏第 5 个可见任务" }, "switch-session-6": { - "label": "切换到第 6 个对话", - "description": "跳转到侧边栏第 6 个可见对话" + "label": "切换到第 6 个任务", + "description": "跳转到侧边栏第 6 个可见任务" }, "switch-session-7": { - "label": "切换到第 7 个对话", - "description": "跳转到侧边栏第 7 个可见对话" + "label": "切换到第 7 个任务", + "description": "跳转到侧边栏第 7 个可见任务" }, "switch-session-8": { - "label": "切换到第 8 个对话", - "description": "跳转到侧边栏第 8 个可见对话" + "label": "切换到第 8 个任务", + "description": "跳转到侧边栏第 8 个可见任务" }, "switch-session-9": { - "label": "切换到第 9 个对话", - "description": "跳转到侧边栏第 9 个可见对话" + "label": "切换到第 9 个任务", + "description": "跳转到侧边栏第 9 个可见任务" } }, "none": "未设置", @@ -2952,7 +2952,7 @@ "projectSwitchLockedTip": "该插件已在全局停用;请先切回「全局」视图开启", "projectBanner": { "title": "正在配置项目「{{name}}」", - "desc": "开关只影响此项目:关闭的插件不会出现在该项目的新对话里", + "desc": "开关只影响此项目:关闭的插件在该项目中不可用", "backToGlobal": "返回全局" }, "scopePicker": { @@ -2983,7 +2983,7 @@ "updated": "「{{name}}」已更新到 v{{version}}", "exported": "已导出「{{name}}」的 .cindy 文件", "exportFailed": "导出「{{name}}」失败,请重试", - "projectDisabled": "已在此项目停用「{{name}}」(新对话生效)", + "projectDisabled": "已在此项目停用「{{name}}」", "projectEnabled": "「{{name}}」在此项目恢复跟随全局" }, "panelError": { @@ -3031,11 +3031,11 @@ "cindyTextOneshot": "可向 Cindy 的快速通道提问并获得文字回答", "cindyTextOneshotDetail": "走系统的轻量任务模型链(与自动起标题同一条通道),不动用 Agent、碰不到你的文件,可能产生模型费用。", "agentUserAction": "用户点击插件卡片后,可让 Agent 开始新一轮工作", - "agentUserActionDetail": "每次点击只允许启动一次,并且只能使用这张卡所属的对话。", + "agentUserActionDetail": "每次点击只允许启动一次,并且只能使用这张卡所属的任务。", "agentBackground": "可在后台自动让 Agent 开始新一轮工作", - "agentBackgroundDetail": "不需要当次点击,但只能使用你曾为这个插件建立过关联的对话,可能产生模型费用。", + "agentBackgroundDetail": "不需要当次点击,但只能使用你曾为这个插件建立过关联的任务,可能产生模型费用。", "agentErrand": "可把任务交给 Cindy Agent 代办并取回结果", - "agentErrandDetail": "任务在这个插件的专属对话里进行,你随时可以在侧边栏看到并叫停;默认只能读不能改,要放开需你在插件配置里亲手调整,可能产生模型费用。", + "agentErrandDetail": "这些代办会在这个插件的专属任务里进行,你随时可以在侧边栏看到并叫停;默认只能读不能改,要放开需你在插件配置里亲手调整,可能产生模型费用。", "nodeRuntime": "可在本机运行随插件附带的 Node 程序", "nodeMcp": "可在本机运行随插件附带的 stdio MCP", "nodeRuntimeDetail": "高风险:它和你本人一样,可以读取或修改你账号能访问的本机文件、访问网络并启动其它程序。Cindy 不能用浏览器沙箱限制它。", @@ -3051,7 +3051,7 @@ "codeDetail": "在独立沙箱中运行,无法访问你的文件与网络。", "resident": "常驻运行(生效后即启动,不调用也保持后台进程)", "residentDetail": "空闲时也占用少量内存;停用或卸载后进程立即关闭。", - "subscribeTopics": "旁听你的对话动态(轮次与对话事件,不含消息内容)", + "subscribeTopics": "旁听你的任务动态(每轮往来与任务事件,不含消息内容)", "subscribeHooks": "可读取、拦截或改写你发出的消息", "subscribeHooksDetail": "发送前先经它审阅:可拦下消息,也可能改写正文后再发出;被拦的消息可编辑后重发。", "subscribeAssistantReply": "可读取并重写 AI 的回复", @@ -3064,7 +3064,7 @@ "confirm": "可弹出确认框征求你同意", "confirmDetail": "它动手前可以弹一个和 Cindy 同款的确认框来问你,问句由它提供、框上会标注它的名字;同意还是取消都由你亲手点,它自己点不了。同时最多一个确认框,弹得太频会被拦下。", "fsWrite": "可写入文件(创建/修改)", - "fsWriteDetail": "它自己的专属数据目录随时可写;当前对话的工作目录跟随对话的权限模式(免批模式直接写,逐条确认模式先征求你同意);其它目录每次都需要你确认。文件始终由主机代写,插件本身无法直接访问你的文件系统。", + "fsWriteDetail": "它自己的专属数据目录随时可写;当前任务的工作目录跟随任务的权限模式(免批模式直接写,逐条确认模式先征求你同意);其它目录每次都需要你确认。文件始终由主机代写,插件本身无法直接访问你的文件系统。", "networkHost": "访问网络域名 {{host}}", "networkSecret": "需要你提供凭证「{{name}}」", "networkSecretGhostInputDetail": "凭证由这个插件自己的设置界面收集,录入时会经过插件页面;随后一次性交给主机加密保管,存入后插件无法读回,主机只在请求它声明的域名时注入。", @@ -3082,10 +3082,10 @@ "noChange": "权限无变化。", "pick": "可请求你选择一个文件夹交给它", "pickDetail": "它发起请求时,Cindy 会弹出系统的选择文件夹窗口;选哪个、给不给,都由你亲手决定,取消则它什么都拿不到。", - "workspace": "可请求为项目文件夹在侧边栏创建对话入口", - "workspaceDetail": "它发起请求时,由你亲手选择文件夹,或在确认卡上看到完整路径后再决定;它正在处理你发起的调用时,当前对话工作目录内的子文件夹可直接使用,不再单独确认。文件夹已有对话时直接复用,不重复创建。创建的只是空对话,不会自动开始任何任务。", - "sessionContext": "可获知当前对话正在使用的项目文件夹位置", - "sessionContextDetail": "只在它为当前对话干活时告知位置信息(对话编号与项目文件夹路径),不包含文件内容,也不因此获得文件访问权。", + "workspace": "可请求为项目文件夹在侧边栏创建任务入口", + "workspaceDetail": "它发起请求时,由你亲手选择文件夹,或在确认卡上看到完整路径后再决定;它正在处理你发起的调用时,当前任务工作目录内的子文件夹可直接使用,不再单独确认。文件夹已有任务时直接复用,不重复创建。创建的只是空任务,不会自动开始运行。", + "sessionContext": "可获知当前任务正在使用的项目文件夹位置", + "sessionContextDetail": "只在它为当前任务干活时告知位置信息(任务编号与项目文件夹路径),不包含文件内容,也不因此获得文件访问权。", "preview": "可在右侧栏浏览器中打开以下网站的预览页面", "previewDetail": "只能打开下方列出的网站;每次打开都会提示是哪个插件打开的。", "nodeChildSpawn": "Node 程序可请宿主再启动包内申报过的子进程", @@ -3196,7 +3196,7 @@ "slotNotify": "系统提示", "slotFs": "写文件", "slotSkill": "Agent 技能", - "slotWorkspace": "工作区对话" + "slotWorkspace": "工作区任务" }, "commandPaletteTag": "插件指令", "updateConfirm": { @@ -3214,7 +3214,7 @@ }, "workspace": { "dialogTitle": "选择项目文件夹", - "dialogMessage": "插件「{{name}}」请求你选择一个项目文件夹,用于在侧边栏创建对话" + "dialogMessage": "插件「{{name}}」请求你选择一个项目文件夹,用于在侧边栏创建任务" } }, "mcp": { @@ -3231,7 +3231,7 @@ "dialog": { "createTitle": "添加 MCP 服务器", "editTitle": "编辑 MCP 服务器", - "desc": "仅支持远程 http / sse 型 MCP。保存后新建的对话即可调用,已在进行中的对话不受影响。" + "desc": "仅支持远程 http / sse 型 MCP。保存后新建或重启的 Agent 会话即可调用,正在运行的 Agent 会话不受影响。" }, "fields": { "name": "显示名称", @@ -3263,7 +3263,7 @@ }, "deleteConfirm": { "title": "删除 “{{name}}”?", - "message": "删除后新建的对话将不再加载该 MCP。", + "message": "删除后新建或重启的 Agent 会话将不再加载该 MCP。", "confirm": "删除" } }, @@ -3346,7 +3346,7 @@ "toast": { "enabled": "智能通讯录已启用", "disabled": "智能通讯录已关闭", - "codexRefreshDeferred": "开关已保存,但 Codex 正忙:将在重启应用后对 Codex 对话生效", + "codexRefreshDeferred": "开关已保存,但 Codex 正忙:将在重启应用后对 Codex 任务生效", "toggleFailed": "切换智能通讯录失败", "created": "已创建「{{name}}」", "createFailed": "创建失败", @@ -3432,8 +3432,8 @@ "connecting": "连接中…", "connected": "已连接", "connectError": "连接失败:{{reason}}", - "noSessions": "对方设备暂无对话", - "selectSession": "选择一个对话开始", + "noSessions": "对方设备暂无任务", + "selectSession": "选择一个任务开始", "inputPlaceholder": "给对方设备的 Maker 发消息…", "modelLabel": "模型", "send": "发送", @@ -3687,16 +3687,16 @@ "dailyLimitLabel": "今日 {{spend}}/{{limit}}", "monthlyLimitLabel": "本周期 {{spend}}/{{limit}}", "creditLabel": "{{used}}/{{total}}", - "sessionCostLabel": "本对话 {{cost}}", + "sessionCostLabel": "本任务 {{cost}}", "codex": { "waiting": "Codex --", "noUsageLabel": "暂无用量", - "noUsageDetail": "本对话尚未产生用量。", + "noUsageDetail": "本任务尚未产生用量。", "unavailableLabel": "用量暂不可用", "unavailableDetail": "用量数据暂时不可用。", "waitingDetail": "Codex 用量会在下一轮完成后更新。", - "sessionTokensLine": "本对话已用 {{tokens}} token", - "sessionValueLabel": "本对话价值 {{cost}}", + "sessionTokensLine": "本任务已用 {{tokens}} token", + "sessionValueLabel": "本任务价值 {{cost}}", "planCreditsLine": "{{plan}} · 剩余 {{credits}} credits", "planLine": "套餐 {{plan}}", "creditsLine": "剩余 {{credits}} credits", @@ -3720,7 +3720,7 @@ "windowSegment": "{{label}} 剩余 {{remaining}}", "resetAt": "{{at}} 重置", "windowLine": "{{label}} 剩余 {{remaining}}(已用 {{used}})", - "sessionValueLabel": "本对话价值 {{cost}}", + "sessionValueLabel": "本任务价值 {{cost}}", "planLine": "套餐 {{plan}}", "limitWarning": "接近套餐限额", "limitRejected": "已触发套餐限额,请求可能被拒绝", @@ -3730,7 +3730,7 @@ }, "tooltip": { "todayUsedSoftLimit": "今日已用 ${{today}} / 软限额 ${{limit}} ({{pct}}%)", - "sessionUsed": "本对话已用 {{cost}}", + "sessionUsed": "本任务已用 {{cost}}", "creditUsed": "账号已用 {{used}} · 总额 {{total}}", "monthlyUnavailable": "⚠️ 月度配额暂时不可用", "dailyUnavailable": "⚠️ 今日用量明细暂时不可用", @@ -3805,7 +3805,7 @@ }, "closeConfirm": { "title": "确认要离开吗?", - "description": "当前有正在进行的对话,关闭会中断并丢失未完成的 turn。", + "description": "当前有正在进行的任务,关闭会中断,未完成的这一轮会丢失。", "confirm": "离开", "cancel": "取消" }, @@ -3846,7 +3846,7 @@ "window": { "title": "侧边栏", "mergeBack": "合并回主窗口", - "followPlaceholder": "侧边栏跟随主窗口的当前对话" + "followPlaceholder": "侧边栏跟随主窗口的当前任务" }, "emptyTitle": "预留区域", "emptyDesc": "右侧边栏内容将在后续迭代加入", @@ -3854,8 +3854,8 @@ "addAria": "添加标签", "addFailed": "添加标签失败", "tabCloseAria": "关闭标签", - "pinAria": "钉住,在所有对话中保留", - "unpinAria": "取消钉住,仅保留在当前对话", + "pinAria": "钉住,在所有任务中保留", + "unpinAria": "取消钉住,仅保留在当前任务", "menu": { "addLabel": "添加标签", "comingSoon": "即将支持", @@ -3907,7 +3907,7 @@ "placeholderHint": "{{kind}} — 内容将在后续阶段加入" }, "fileBrowser": { - "noWorkdir": "当前对话未关联本地工作目录,文件浏览器不可用", + "noWorkdir": "当前任务未关联本地工作目录,文件浏览器不可用", "filterPlaceholder": "筛选文件…", "filterClear": "清除筛选", "filterIndexing": "正在索引项目文件…", @@ -3975,12 +3975,12 @@ "fileCount_one": "{{count}} 个文件", "fileCount_other": "{{count}} 个文件", "emptyTitle": "暂无改动", - "emptyDesc": "本次对话还没有任何文件改动。让 Agent 编辑或创建文件后,会在这里出现。", + "emptyDesc": "本任务还没有任何文件改动。让 Agent 编辑或创建文件后,会在这里出现。", "emptyDescGit": "当前 git worktree 没有 staged 或 unstaged 改动。", "filterEmptyTitle": "当前过滤下暂无改动", "filterEmptyDesc": "当前筛选或设置下没有可显示的 git 改动。", - "noSessionTitle": "暂无对话", - "noSessionDesc": "选择一个本机对话后可查看 git worktree 改动。", + "noSessionTitle": "暂无任务", + "noSessionDesc": "选择一个本机任务后可查看 git worktree 改动。", "loadingTitle": "正在读取 git 改动", "loadingDesc": "面板会保留上一次内容,读取完成后刷新。", "errorTitle": "读取失败", @@ -4036,7 +4036,7 @@ "staged": "已暂存", "commit": "提交", "branch": "分支", - "lastTurn": "上轮对话" + "lastTurn": "上一轮" }, "sourceDropdownAria": "选择审查来源", "lastTurnEmpty": { @@ -4220,19 +4220,19 @@ "disabled": { "default": { "title": "暂不可用", - "desc": "当前对话无法读取 git worktree。" + "desc": "当前任务无法读取 git worktree。" }, "remote-session": { - "title": "远端对话暂不支持", + "title": "远端任务暂不支持", "desc": "远端 worktree 的 git 审查会在后续版本接入。" }, "no-session": { - "title": "对话不存在", - "desc": "找不到当前对话记录。" + "title": "任务不存在", + "desc": "找不到当前任务记录。" }, "no-workdir": { "title": "没有工作目录", - "desc": "当前对话没有可审查的本地工作目录。" + "desc": "当前任务没有可审查的本地工作目录。" }, "non-git": { "title": "不是 git 仓库", @@ -4244,11 +4244,11 @@ }, "invalid-worktree": { "title": "worktree 无效", - "desc": "当前对话绑定的 worktree 不可用。" + "desc": "当前任务绑定的 worktree 不可用。" }, "unknown": { "title": "暂不可用", - "desc": "无法读取当前对话的 git 状态。" + "desc": "无法读取当前任务的 git 状态。" } }, "revealInFolder": "打开文件所在位置", @@ -4260,7 +4260,7 @@ "backgroundTasks": { "running": "运行中", "completed": "已完成", - "empty": "本对话暂无后台任务", + "empty": "本任务暂无后台作业", "stop": "停止", "back": "返回", "progressSummary": "{{done}}/{{total}} Agent", @@ -4426,7 +4426,7 @@ }, "sourceDisconnected": { "title": "模型来源已断开", - "description": "此对话选择的来源已断开连接,暂时无法发送消息。请在模型选择器里换一个已连接的来源,或去设置重新连接。", + "description": "此任务选择的来源已断开连接,暂时无法发送消息。请在模型选择器里换一个已连接的来源,或去设置重新连接。", "connect": "去设置", "sendBlocked": "来源已断开,无法发送 — 请切换或重新连接来源" }, @@ -4460,7 +4460,7 @@ "INLINE_INVALID": "配置内容无效,请检查后重试。", "INLINE_UNAVAILABLE": "当前版本无法在对话中保存此配置,请更新 Cindy 后重试。", "SAVE_FAILED": "未能保存配置,请重试。", - "WINDOW_CLOSED": "发起设置的窗口已关闭,请回到该对话重试。", + "WINDOW_CLOSED": "发起设置的窗口已关闭,请回到该任务重试。", "TARGET_UNAVAILABLE": "插件已停用、卸载或不可用,请检查插件状态后重试。", "ASSESSMENT_FAILED": "暂时无法刷新配置状态,请重试。", "TIMEOUT": "等待设置超时,请重试。" @@ -4521,7 +4521,7 @@ "search": "搜索", "local": "本机", "projects": "项目", - "conversations": "对话" + "conversations": "任务" } }, "addRemoteProject": { @@ -4682,7 +4682,7 @@ "channels": { "feishu": "飞书" }, - "heading": "{{user}} 正在从{{channel}}远程接管这个对话", + "heading": "{{user}} 正在从{{channel}}远程接管这个任务", "subtitle": "输入暂时交给他~ 你也可以随时把指挥权\"收回\"", "revokeAria": "收回接管", "revoke": "收回", @@ -4730,7 +4730,7 @@ "showLess": "收起", "noTextContent": "(无文本内容)", "orcaRowAria": "来自协同 {{sender}} 的排队消息", - "schedulerRowAria": "自动化任务「{{sender}}」的排队消息", + "schedulerRowAria": "自动化「{{sender}}」的排队消息", "dragAria": "拖动第 {{index}} 条待发送消息排序", "steerAction": "插话", "steerRunningTip": "插话到当前任务,不打断正在执行的工作,当前动作完成后优先处理这条消息", @@ -4750,7 +4750,7 @@ "newChatView": { "missing": { "repoRoot": "当前目录的仓库根(仓库探测未完成或失败)", - "sessionId": "对话 ID(请稍后再试)", + "sessionId": "任务 ID(请稍后再试)", "separator": "、", "message": "无法开 worktree:缺少 {{missing}}", "hint": "可尝试重新选一次工作目录,等右上角文件夹标识刷新后再试" @@ -4763,13 +4763,13 @@ "popoverTitle": "召集 Worker 协作", "startButton": "开启协同", "startFailed": "开启协同失败", - "startFailedContinue": "开启协同失败,已改为单对话继续", + "startFailedContinue": "开启协同失败,已改为单任务继续", "createWorkerRefreshFailed": "Worker 状态刷新失败,请稍后重试", "openHint": "召集 Worker 协作", "disabledHint": "当前项目已关闭协同模式,请在项目设置中重新启用", "unavailableHint": "暂时无法确认当前项目的协同设置,请稍后重试", "unsupportedRemoteHint": "被控设备的版本过旧,暂不支持协同", - "remoteContinueNotice": "本条消息仍会以单对话继续发送。", + "remoteContinueNotice": "本条消息仍会以单任务继续发送。", "handoffRecoveredMessage": "上次开启协同时被中断,未发出的消息已恢复到输入框", "handoffRecoveredGoal": "上次开启协同时被中断,目标未启动,内容已恢复到输入框", "loadingHint": "正在确认当前项目的协同设置,请稍后重试", @@ -4788,20 +4788,20 @@ "archiveWorkerConfirmCancel": "取消", "errors": { "INVALID_PARAMS": "所选模型或协同配置已不可用,请重新选择后重试。", - "INVALID_PARAMS_CONTINUE": "所选模型或协同配置已不可用;协同未开启,已改为单对话继续。请重新选择后重试。", + "INVALID_PARAMS_CONTINUE": "所选模型或协同配置已不可用;协同未开启,已改为单任务继续。请重新选择后重试。", "INVALID_PARAMS_REMOTE": "被控设备不支持所选模型或协同配置。请改选该设备支持的模型后重试。", "NO_PROVIDER_FOR_AGENT": "当前设备没有为该 Agent 连接可用的模型供应商。请先在设置中完成连接。", - "NO_PROVIDER_FOR_AGENT_CONTINUE": "当前设备没有为该 Agent 连接可用的模型供应商;协同未开启,已改为单对话继续。请先在设置中完成连接。", + "NO_PROVIDER_FOR_AGENT_CONTINUE": "当前设备没有为该 Agent 连接可用的模型供应商;协同未开启,已改为单任务继续。请先在设置中完成连接。", "NO_PROVIDER_FOR_AGENT_REMOTE": "被控设备没有为该 Agent 连接可用的模型供应商。请在被控设备上完成连接后重试。", "PROVIDER_ROUTE_UNAVAILABLE": "所选模型与供应商的精确路由当前不可用,请改选可用的模型或供应商后重试。", - "PROVIDER_ROUTE_UNAVAILABLE_CONTINUE": "所选模型与供应商的精确路由当前不可用;协同未开启,已改为单对话继续。请改选可用的模型或供应商后重试。", + "PROVIDER_ROUTE_UNAVAILABLE_CONTINUE": "所选模型与供应商的精确路由当前不可用;协同未开启,已改为单任务继续。请改选可用的模型或供应商后重试。", "PROVIDER_ROUTE_UNAVAILABLE_REMOTE": "被控设备上的所选模型与供应商精确路由当前不可用,请改选该设备可用的路由后重试。", "BUDGET_MODEL_REQUIRES_API_MODE": "此 Codex 模型需要先在设置中配置 API key 才能使用。请配置后重试。", - "BUDGET_MODEL_REQUIRES_API_MODE_CONTINUE": "此 Codex 模型需要先在设置中配置 API key;协同未开启,已改为单对话继续。请配置 API key 后重试。", + "BUDGET_MODEL_REQUIRES_API_MODE_CONTINUE": "此 Codex 模型需要先在设置中配置 API key;协同未开启,已改为单任务继续。请配置 API key 后重试。", "BUDGET_MODEL_REQUIRES_API_MODE_REMOTE": "被控设备尚未配置此 Codex 模型所需的 API key。请在被控设备上配置后重试。", - "PRECONDITION_FAILED": "当前项目已关闭协同模式,或当前对话不支持协同。", - "PRECONDITION_FAILED_CONTINUE": "当前项目已关闭协同模式,已改为单对话继续。", - "PRECONDITION_FAILED_REMOTE": "被控对话不支持协同。" + "PRECONDITION_FAILED": "当前项目已关闭协同模式,或当前任务不支持协同。", + "PRECONDITION_FAILED_CONTINUE": "当前项目已关闭协同模式,已改为单任务继续。", + "PRECONDITION_FAILED_REMOTE": "被控任务不支持协同。" } }, "chatInput": { @@ -4836,8 +4836,8 @@ "credentialSwitchApplied": "模型来源切换已生效。", "modelSwitchContextGuard": { "title": "目标模型的上下文窗口较小", - "warnToast": "当前对话已使用 {{used}} 上下文,切换后约占目标模型窗口({{total}})的 {{pct}}%", - "overflowDescription": "当前对话已使用 {{used}} 上下文,超出目标模型窗口({{total}},约 {{pct}}%)。切换后回复和压缩都可能失败,建议先点击上下文圆环压缩对话,或新建对话使用该模型。仍要切换吗?", + "warnToast": "当前任务已使用 {{used}} 上下文,切换后约占目标模型窗口({{total}})的 {{pct}}%", + "overflowDescription": "当前任务已使用 {{used}} 上下文,超出目标模型窗口({{total}},约 {{pct}}%)。切换后回复和压缩都可能失败,建议先点击上下文圆环压缩对话,或新建任务使用该模型。仍要切换吗?", "confirmSwitch": "仍然切换模型", "cancelSwitch": "取消切换" }, @@ -4889,9 +4889,9 @@ "saveEdit": "保存文本" }, "permissionPrompt": { - "alwaysAllowScoped": "本对话都允许 {{scope}}", - "alwaysAllowScopedHint": "作用于整个对话,且只限这里列出的范围。", - "alwaysAllowSessionHint": "作用于整个对话,不只是这一次请求。", + "alwaysAllowScoped": "本任务都允许 {{scope}}", + "alwaysAllowScopedHint": "作用于整个任务,且只限这里列出的范围。", + "alwaysAllowSessionHint": "作用于整个任务,不只是这一次请求。", "ruleSeparator": "、" }, "deviceSwitcher": { @@ -5042,7 +5042,7 @@ "outputFile": "输出:{{path}}" }, "forkOrigin": { - "label": "基于原对话创建" + "label": "基于原任务创建" }, "imageMissing": { "title": "图片已丢失" @@ -5056,18 +5056,18 @@ "copy": "复制消息", "linkCopied": "链接已复制", "moreActions": "更多消息操作", - "copyLink": "复制当前对话链接", + "copyLink": "复制当前消息链接", "copyLinkFailed": "无法复制链接", "rewind": "回到此处", - "fork": "在新对话中继续", - "forkConfirmTitle": "从这里开启一个新对话?", - "forkConfirmDescription": "系统会根据这里的对话上下文创建一个独立的新对话。原对话不会改变,之后两边的消息互不影响。", - "forkConfirm": "开启新对话", + "fork": "开启一个新任务", + "forkConfirmTitle": "从这里开启一个新任务?", + "forkConfirmDescription": "系统会根据这里的对话上下文创建一个独立的新任务。原任务不会改变,之后两边的消息互不影响。", + "forkConfirm": "开启新任务", "forkCancel": "取消", - "delete": "删除本条对话", - "deleteConfirmTitle": "删除本条对话?", - "deleteConfirmDescription": "用户消息只删除本条;AI 消息会删除上一次用户输入之后产生的整轮输出。之后的对话继续保留。下次发送时,AI 上下文会从剩余记录重建。", - "deleteConfirm": "删除本条对话", + "delete": "删除本条消息", + "deleteConfirmTitle": "删除本条消息?", + "deleteConfirmDescription": "用户消息只删除本条;AI 消息会删除上一次用户输入之后产生的整轮输出。之后的消息继续保留。下次发送时,AI 上下文会从剩余记录重建。", + "deleteConfirm": "删除本条消息", "deleteCancel": "取消", "deleteBusy": "请等当前回复结束后再删除消息", "deleteFailed": "无法删除这条消息,请重试", @@ -5084,16 +5084,16 @@ "retry": "重试", "continueAfterReset": "恢复后继续", "continueAfterResetTitle": "创建一次性自动化,在用量恢复后继续当前任务", - "credentialSwitchBusy": "其他 Codex 任务正在运行,暂时无法按本对话选择的模型来源切换(需要重启共享的 Codex 后台进程)。等其他任务结束后点「重试」即可。", - "codexThreadStale": "Codex 鉴权模式已切换,当前对话无法继续 —— 请新建对话后生效。", + "credentialSwitchBusy": "Codex 正在其它任务中运行,暂时无法按本任务选择的模型来源切换(需要重启共享的 Codex 后台进程)。等它结束后点「重试」即可。", + "codexThreadStale": "Codex 鉴权模式已切换,当前任务无法继续 —— 请新建任务后生效。", "claudeGatewayOpusPlanMismatch": "这次 Opus 请求实际走的是 XD Gateway,不是你的 Claude.ai 订阅;该网关路由拒绝了此模型。可切换到 Claude.ai 订阅后重试,或换一个模型。", "switchClaudeSubscription": "切换到 Claude.ai 并重试", - "switchClaudeSubscriptionTitle": "把当前对话切换到 Claude.ai 订阅并重试", + "switchClaudeSubscriptionTitle": "把当前任务切换到 Claude.ai 订阅并重试", "switchingClaudeSubscription": "正在切换到 Claude.ai…", - "claudeSubscriptionSwitchFailed": "无法切换到 Claude.ai 或重试当前对话:{{msg}}", + "claudeSubscriptionSwitchFailed": "无法切换到 Claude.ai 或重试当前任务:{{msg}}", "budgetModelHint": "如果反复失败,可以切换到普通版 GPT 再试试。", "codexAuthMissing": "远端 Codex 还没同步登录态(401 Unauthorized)。点击右侧「同步登录态」把本机 Codex 凭证推到远端 Cindy Codex home,然后重试发送。", - "codexAuthMissingLocal": "Codex 凭证缺失或失效(401 Unauthorized)。请在 {{appName}} Settings → Codex 重新登录(登录后会自动重启 Codex app-server),或在终端跑 `codex login` + 在 Settings 切换一次鉴权模式来重启 app-server,然后新建对话再发。", + "codexAuthMissingLocal": "Codex 凭证缺失或失效(401 Unauthorized)。请在 {{appName}} Settings → Codex 重新登录(登录后会自动重启 Codex app-server),或在终端跑 `codex login` + 在 Settings 切换一次鉴权模式来重启 app-server,然后新建任务再发。", "codexSessionExpired": "OpenAI 连接需要更新。重新连接 ChatGPT 后即可继续;{{appName}} 不会退出系统 Codex CLI,也不会要求你重新连接其他模型来源。", "codexSessionExpiredDialog": { "title": "OpenAI 连接需要更新", @@ -5108,10 +5108,10 @@ "syncAuthSuccess": "已同步,请点击「重试」重新发送", "syncAuthFailed": "同步失败:{{msg}}", "syncAuthDaemonRestartFailed": "凭证已推送到远端,但旧 daemon 没重启 — 请重连远端后再点重试", - "invalidEncryptedContent": "由于更换供应商导致加密不可用,本对话只能在原供应商下使用,或点此 Fork 丢弃协议加密内容并在新对话继续。", + "invalidEncryptedContent": "由于更换供应商导致加密不可用,本任务只能在原供应商下使用,或点此 Fork 丢弃协议加密内容并在新任务继续。", "forkStripEncrypted": "Fork 并剥离", "forkStripEncryptedRunning": "Fork 中…", - "forkStripEncryptedTitle": "Fork 并剥离出新对话(去掉加密推理内容)", + "forkStripEncryptedTitle": "Fork 并剥离出新任务(去掉加密推理内容)", "codexSessionExpiredLogin": "重新连接 ChatGPT", "codexSessionReconnected": "ChatGPT 账号已重新连接。点击「重试」继续。", "networkUnreachable": "网络异常,服务暂时不可达,可点击「重试」。", @@ -5131,8 +5131,8 @@ "bashStatus_one": "后台任务运行中({{count}} 个)…", "bashStatus_other": "后台任务运行中({{count}} 个)…", "stopAll": "全部停止", - "stopAllTitle": "关闭该对话的后台进程,停止全部后台子任务(对话可继续使用)", - "stopBashTitle": "停止该对话仍在运行的后台任务(不关闭会话进程)", + "stopAllTitle": "关闭该任务的后台进程,停止全部后台作业(任务可继续使用)", + "stopBashTitle": "停止该任务仍在运行的后台作业(不关闭会话进程)", "stopping": "停止中…" }, "interruptedBanner": { @@ -5143,8 +5143,8 @@ "dismissTitle": "关闭此中断提示" }, "worktreeRestoreBanner": { - "text": "该对话的 worktree 已被回收(目录不存在)。", - "textPendingSnapshot": "该对话有一份未应用的更改快照。", + "text": "该任务的 worktree 已被回收(目录不存在)。", + "textPendingSnapshot": "该任务有一份未应用的更改快照。", "restoreAction": "恢复 worktree", "applySnapshotAction": "恢复更改", "restoring": "恢复中…", @@ -5154,7 +5154,7 @@ "failed": "恢复 worktree 失败:{{message}}", "failures": { "gone": "恢复分支已不存在,无法在产品内恢复", - "noWorktree": "该对话没有可恢复的 worktree", + "noWorktree": "该任务没有可恢复的 worktree", "gitError": "Git 无法重建该 worktree", "unknown": "未知错误" }, @@ -5256,7 +5256,7 @@ "fetching": "正在从远端获取文件…", "fetchFailed": "从远端获取文件失败", "notFound": "远端文件不存在", - "outsideWorkdir": "该文件在对话工作目录之外,远程对话暂不支持访问", + "outsideWorkdir": "该文件在任务工作目录之外,远程任务暂不支持访问", "staleCopy": "远端连接不可用,已显示本地历史副本(可能不是最新版本)", "staleBadge": "历史副本", "revealLocalCopy": "下载并定位本地副本" @@ -5438,7 +5438,7 @@ "detail": { "reason": "中断原因", "attempt": "本次重试 {{attempt}}/{{total}}", - "sessionTotal": "本对话累计重连 {{count}} 次" + "sessionTotal": "本任务累计重连 {{count}} 次" } }, "autoResumeSeparator": { @@ -5453,7 +5453,7 @@ "toggleHint": "查看交接内容", "handoffTitle": "交接内容——切换时发送给新引擎的上下文摘要(不计入对话记录,仅在此展示)", "handoffTitleEnglishSource": "交接内容——切换时发送给新引擎的上下文摘要,原文为英文(不计入对话记录,仅在此展示)", - "resumedBadge": "已续接原对话" + "resumedBadge": "已续接原任务" } }, "markdownRenderer": { @@ -5475,7 +5475,7 @@ "openInSidebarBrowser": "在侧边栏浏览器中打开", "openInSidebarFileBrowser": "在侧边栏文件浏览器中打开", "openInSidebarFileBrowserFailed": "在侧边栏文件浏览器中打开失败", - "openInSidebarFileBrowserOutsideWorkdir": "该文件不在当前对话工作目录内,无法在侧边栏文件浏览器中定位", + "openInSidebarFileBrowserOutsideWorkdir": "该文件不在当前任务工作目录内,无法在侧边栏文件浏览器中定位", "openInDefaultBrowser": "在默认浏览器中打开", "copyLink": "复制链接", "linkCopied": "链接已复制", @@ -5507,9 +5507,9 @@ "attachmentSaveForbidden": "无法保存附件:源文件路径不受信任", "attachmentSourceMissing": "无法保存附件:源文件已不存在", "attachmentSaveFailed": "无法保存附件,请检查目标目录权限后重试", - "automationSent": "由自动化任务发送", - "automationSentNamed": "由自动化任务「{{name}}」发送", - "automationViewTask": "查看自动化任务", + "automationSent": "由自动化发送", + "automationSentNamed": "由自动化「{{name}}」发送", + "automationViewTask": "查看自动化", "expandLongMessage": "展开全文", "collapseLongMessage": "收起", "sessionReference": { @@ -5519,20 +5519,20 @@ "messageCount_other": "{{count}} 条消息", "truncated": "已截断" }, - "forkBusy": "当前对话正在运行,等结束后再从这里开启新对话", + "forkBusy": "当前任务正在运行,等结束后再从这里开启新任务", "editStopTimeout": "等待任务停止超时,请稍后重试", - "editQueueNotEmpty": "对话有排队中的消息,先发送或移除队列内容后再编辑", - "editNotLast": "对话里已有更新的消息,这条不再是最后一条,无法编辑重发", + "editQueueNotEmpty": "任务有排队中的消息,先发送或移除队列内容后再编辑", + "editNotLast": "任务里已有更新的消息,这条不再是最后一条,无法编辑重发", "editFailed": "编辑重发失败,请重试", "editCancel": "取消", "editSend": "发送", "editRollbackHint": "发送将撤销上一轮对 {{count}} 个文件的改动", "forkErrors": { - "unsupportedHistory": "目标位置对应的历史引擎会话已不可用,无法从这里开启新对话", - "noPriorAssistant": "请在 AI 回复之后的消息上开启新对话", - "sourceNeverRan": "原对话尚未运行,无法开启新对话", - "codexStateUnavailable": "Codex 对话状态不可用,无法开启新对话。{{detail}}", - "generic": "无法开启新对话,请重试" + "unsupportedHistory": "目标位置对应的历史引擎会话已不可用,无法从这里开启新任务", + "noPriorAssistant": "请在 AI 回复之后的消息上开启新任务", + "sourceNeverRan": "原任务尚未运行,无法开启新任务", + "codexStateUnavailable": "Codex Agent 会话状态不可用,无法开启新任务。{{detail}}", + "generic": "无法开启新任务,请重试" } }, "threadContext": { @@ -5544,7 +5544,7 @@ "errors": { "noPriorAssistantPreview": "请在 AI 回复之后的提问上 rewind(这条之前没有 assistant 回复)", "sessionRunning": "等待任务停止超时,请重新回退", - "noLiveQuery": "对话未激活——请先发送任意一条消息(哪怕一句\"ok\")让 SDK 起来再 rewind", + "noLiveQuery": "Agent 会话未激活——请先发送任意一条消息(哪怕一句\"ok\")让 SDK 起来再 rewind", "noPriorAssistantCommit": "请在 AI 回复之后的提问上 rewind", "commitGeneric": "rewind 失败,请重试", "remoteNotSupported": "远端 Claude Code 会话暂不支持 rewind" @@ -5552,20 +5552,20 @@ "dialog": { "title": "回退到这条消息?", "titleError": "无法回退到这条消息", - "errorDescription": "这个对话的文件 checkpointing 未启用,无法回退被工具改动过的文件。", + "errorDescription": "这个任务的文件 checkpointing 未启用,无法回退被工具改动过的文件。", "calculating": "正在计算回退影响…", "noChanges": "没有需要回滚的文件改动", - "continueWillTruncate": "继续回退仍会截断后续对话", + "continueWillTruncate": "继续回退仍会截断后续消息", "noPerFileDiff": "该 SDK 版本不支持单文件 diff 预览", - "summaryDefault": "这条消息后续对话会被丢弃,工作目录里的 {{count}} 个文件会回退到那一刻。", - "summaryRunning": "当前任务会被停止;这条消息之后的对话和对应文件改动会被回退。", - "summaryEmpty": "这条消息后续对话会被丢弃。没有文件被改动,仅截断历史。", - "summaryEmptyWithNote": "这条消息后续对话会被丢弃。", + "summaryDefault": "后续消息会被丢弃,工作目录里的 {{count}} 个文件会回退到那一刻。", + "summaryRunning": "当前执行会被停止;后续消息和对应文件改动会被回退。", + "summaryEmpty": "后续消息会被丢弃。没有文件被改动,仅截断历史。", + "summaryEmptyWithNote": "后续消息会被丢弃。", "runningNotice": "任务仍在运行。确认后会先停止任务,再按最新状态执行回退。", "runningQueueNotice": "排队消息会保留并暂停,不会自动发送。", "summaryPrefix": "共 {{count}} 个文件,", "linesSuffix": "行", - "errorHint": "提示:可以 fork 一个新对话继续编辑。", + "errorHint": "提示:可以 fork 一个新任务继续编辑。", "cancel": "取消", "confirm": "确认回退", "confirmRunning": "停止并回退", @@ -5575,17 +5575,17 @@ } }, "upgradeBanner": { - "description": "远端 {{hostId}} 的 cc-manager 有新版本可用 — 升级会中断该机器上当前所有 Claude Code 远端对话", + "description": "远端 {{hostId}} 的 cc-manager 有新版本可用 — 升级会中断该机器上当前所有 Claude Code 远端任务", "upgrading": "正在升级远端 {{hostId}} 的 cc-manager…", "upgrade": "立即升级", - "upgradeTitle": "下载并启动新版本 cc-manager(会中断当前对话)", + "upgradeTitle": "下载并启动新版本 cc-manager(会中断当前任务)", "dismissTitle": "暂不升级(下次启动会再次提示)", "failed": "升级失败:{{message}}", "daemonNotReady": "cc-manager 升级完成,但远端 daemon 暂未启动 — 请手动重新发送消息" }, "remoteError": { "REMOTE_DAEMON_CLOSED": "远端连接已中断(daemon 可能正在升级或重启)。本轮已结束 — 请重新发送。", - "REMOTE_LOCAL_ATTACHMENT_UNSUPPORTED": "远端对话无法访问本地文件/图片附件。请直接粘贴内容。", + "REMOTE_LOCAL_ATTACHMENT_UNSUPPORTED": "远端任务无法访问本地文件/图片附件。请直接粘贴内容。", "REMOTE_COMPAT_MODE_UNSUPPORTED": "远端 Claude Code 会话无法通过本地兼容 Proxy 路由(内部错误,请反馈)。", "REMOTE_GATEWAY_ENDPOINT_UNAVAILABLE": "Cindy AI 凭据尚未就绪(登录后由服务端自动下发)。请稍候重试;若持续出现,请到 设置 → 模型供应商 检查 Cindy AI 连接状态。", "DEVICE_LINK_CONTROL_DISABLED": "已关闭对该设备的控制,消息未发送。", @@ -5625,8 +5625,8 @@ } }, "credentialSwitchWait": { - "waiting": "其他 Codex 任务正在使用不同的模型来源运行,结束后会自动按本对话选择的来源发送这条消息。", - "waitingForNamed": "「{{titles}}」正在使用不同的模型来源运行,结束后会自动按本对话选择的来源发送这条消息。", + "waiting": "Codex 正在其它任务中以不同的模型来源运行,结束后会自动按本任务选择的来源发送这条消息。", + "waitingForNamed": "「{{titles}}」正在使用不同的模型来源运行,结束后会自动按本任务选择的来源发送这条消息。", "cancel": "取消", "cancelTitle": "取消等待并移除这条消息" }, @@ -5681,9 +5681,9 @@ "remoteSession": { "reconnecting": "与被控设备的连接中断,正在重连…", "hostOffline": "被控设备已离线,暂时无法同步", - "actionsUnavailable": "被控设备暂不可用,恢复连接后才能修改该对话", + "actionsUnavailable": "被控设备暂不可用,恢复连接后才能修改该任务", "resync": "重新同步", - "loading": "正在从被控设备加载对话…", + "loading": "正在从被控设备加载任务…", "suspectStall": "本轮长时间无更新,可能已断流", "finalizeStuck": "结束本轮", "issue": { @@ -5699,9 +5699,9 @@ "openFailedWith": "打开失败:{{error}}", "openFolderFailedWith": "打开文件夹失败:{{error}}", "unknownError": "未知错误", - "unnamedSession": "未命名对话", + "unnamedSession": "未命名任务", "moreActions": "更多操作", - "newSession": "新建对话", + "newSession": "新建任务", "loading": "加载中…" }, "time": { @@ -5777,7 +5777,7 @@ "days_other": "{{count}} 天前活跃过" }, "neverActive": "从未收到用户消息", - "unnamedSession": "未命名对话", + "unnamedSession": "未命名任务", "wake": { "resumed": "RESUMED", "alreadyActive": "ALREADY ACTIVE", @@ -5790,20 +5790,20 @@ } }, "draft": { - "createSessionFailed": "创建对话失败,请重试", + "createSessionFailed": "创建任务失败,请重试", "remoteProviderUpdating": "供应商凭证正在更新,请稍后重试", "remoteProviderUnsupported": "该供应商不支持 SSH 远程会话,请更换来源", "remoteNativeOauthUnavailable": "未连接 Claude.ai 订阅,请先连接或选择网关模型", "deviceStillLoading": "远程设备仍在加载中,请稍候", "worktreeMissingRepo": "无法开 worktree:缺少当前目录的仓库根", "worktreeRepoHint": "可尝试重新选一次工作目录,等右下角文件夹标识刷新后再试", - "sessionInactive": "对话已不再处于活跃状态", - "remoteProjectBanner": "在 {{device}} 的 {{project}} 中新建对话", + "sessionInactive": "任务已不再处于活跃状态", + "remoteProjectBanner": "在 {{device}} 的 {{project}} 中新建任务", "remoteDialogueBanner": "在 {{device}} 上新建对话(不绑定项目)" }, "jumpSession": { - "emptyId": "请输入要跳转的对话 ID", - "notFound": "未找到该对话" + "emptyId": "请输入要跳转的任务 ID", + "notFound": "未找到该任务" }, "sidebar": { "machineSwitcher": { @@ -5833,11 +5833,11 @@ }, "showAllSessions": "显示全部 {{count}} 项", "dialogues": "对话", - "newDialogue": "新对话", + "newDialogue": "新任务", "dialoguesToggleExpand": "展开对话", "dialoguesToggleCollapse": "收起对话", "loadingDialogues": "加载中…", - "noDialogues": "暂无对话", + "noDialogues": "暂无任务", "dialogueSettings": "整理对话", "dialogueSettingsAria": "整理对话(排序:{{sortBy}})", "dialogueSortHeading": "排序", @@ -5851,8 +5851,8 @@ "createProjectFailed": "新建项目失败", "projectsSectionToggleExpand": "展开项目", "projectsSectionToggleCollapse": "收起项目", - "projectsToggleExpand": "展开所有项目对话", - "projectsToggleCollapse": "收起所有项目对话", + "projectsToggleExpand": "展开所有项目任务", + "projectsToggleCollapse": "收起所有项目任务", "filterAria": "整理侧边栏(状态:{{status}},Agent:{{vendor}},最近活跃:{{lastActivity}},分组:{{groupBy}},排序:{{sortBy}},项目:{{projects}})", "organizeSidebar": "整理侧边栏", "filterAllText": "全部", @@ -5895,9 +5895,9 @@ "today": "今天", "yesterday": "昨天", "older": "更早", - "empty": "没有对话" + "empty": "没有任务" }, - "browseEmpty": "该目录下还没有任何对话,先创建一个对话", + "browseEmpty": "该目录下还没有任何任务,先创建一个任务", "automationGenerated": "由自动化创建", "hasDraft": "有未发送的草稿", "hasPausedQueue": "有暂停未发送的消息", @@ -5908,8 +5908,8 @@ "error": "任务出错 — 点击查看" }, "scheduleBinding": { - "label": "已绑定自动化任务", - "viewTask": "查看自动化任务", + "label": "已绑定自动化", + "viewTask": "查看自动化", "tooltipName": "任务:{{name}}", "tooltipFrequency": "频率:{{frequency}}", "pausedSuffix": "(已暂停)" @@ -5919,7 +5919,7 @@ "attentionCount": "{{count}} 待处理", "hiddenCount": "{{count}} 已收起", "stopped": "已停止", - "aria": "{{title}} 自动化对话,{{count}} 次运行,{{attention}} 个待处理", + "aria": "{{title}} 自动化生成的任务,{{count}} 次运行,{{attention}} 个待处理", "menu": { "runNow": "立即运行", "more": "更多操作", @@ -5939,78 +5939,78 @@ "archiveFailed": "归档失败,请重试", "unarchiveFailed": "恢复失败,请重试", "archiveBlocked": { - "running": "当前对话正在执行,请先停止后再归档", - "attached": "此对话正在被远程接管,请先收回后再归档" + "running": "当前任务正在运行,请先停止后再归档", + "attached": "此任务正在被远程接管,请先收回后再归档" }, "confirmDelete": { - "title": "删除对话", - "description": "确定要删除这个对话吗?删除后无法恢复。", - "dirtyWorktreeWarning": "该对话的 worktree 有未提交的更改;删除前会自动保存为 git 快照(分支保留,可用 git 找回)。", + "title": "删除任务", + "description": "确定要删除这个任务吗?删除后无法恢复。", + "dirtyWorktreeWarning": "该任务的 worktree 有未提交的更改;删除前会自动保存为 git 快照(分支保留,可用 git 找回)。", "confirm": "确认", "cancel": "取消" }, "confirmArchive": { - "title": "归档对话", - "description": "确定要归档这个对话吗?归档后将从列表中移除。", - "dirtyWorktreeWarning": "该对话的 worktree 有未提交的更改;归档前会自动保存为 git 快照,之后可在对话内一键恢复。", + "title": "归档任务", + "description": "确定要归档这个任务吗?归档后将从列表中移除。", + "dirtyWorktreeWarning": "该任务的 worktree 有未提交的更改;归档前会自动保存为 git 快照,之后可在任务内一键恢复。", "confirm": "确认", "cancel": "取消" }, "archiveAll": { - "title": "归档全部对话", + "title": "归档全部任务", "confirm": "归档全部", "cancel": "取消", - "description": "将归档该项目下全部 {{count}} 个对话。归档后将从列表中移除。", - "descriptionWithSkip": "将归档该项目下 {{count}} 个对话,{{skip}}的对话会被跳过。归档后将从列表中移除。", + "description": "将归档该项目下全部 {{count}} 个任务。归档后将从列表中移除。", + "descriptionWithSkip": "将归档该项目下 {{count}} 个任务,{{skip}}的任务会被跳过。归档后将从列表中移除。", "skipRunning": "{{count}} 个运行中", "skipPinned": "{{count}} 个已置顶", "skipSeparator": "、", - "archived_one": "已归档 {{count}} 个对话", - "archived_other": "已归档 {{count}} 个对话", + "archived_one": "已归档 {{count}} 个任务", + "archived_other": "已归档 {{count}} 个任务", "partialFailure": "已归档 {{ok}} 个,{{fail}} 个失败,请重试", - "allPinnedOrRunning": "该项目下对话全部已置顶或在运行中,无可归档项", - "allPinned": "该项目下对话全部已置顶,不会归档", - "allRunning": "该项目下所有对话都在运行中,请先停止后再归档", - "empty": "该项目下没有可归档的对话" + "allPinnedOrRunning": "该项目下任务全部已置顶或在运行中,无可归档项", + "allPinned": "该项目下任务全部已置顶,不会归档", + "allRunning": "该项目下所有任务都在运行中,请先停止后再归档", + "empty": "该项目下没有可归档的任务" }, "unarchiveAll": { - "title": "取消归档全部对话", + "title": "取消归档全部任务", "confirm": "全部取消归档", "cancel": "取消", - "description": "将恢复该项目下 {{count}} 个已归档对话。恢复后将从“已归档”列表移除,并回到“活跃”列表。", - "unarchived_one": "已恢复 {{count}} 个对话", - "unarchived_other": "已恢复 {{count}} 个对话", + "description": "将恢复该项目下 {{count}} 个已归档任务。恢复后将从“已归档”列表移除,并回到“活跃”列表。", + "unarchived_one": "已恢复 {{count}} 个任务", + "unarchived_other": "已恢复 {{count}} 个任务", "partialFailure": "已恢复 {{ok}} 个,{{fail}} 个失败,请重试", - "empty": "该项目下没有可取消归档的对话" + "empty": "该项目下没有可取消归档的任务" }, "bulkSelection": { "selected": "已选 {{count}} 个", - "archive": "归档所选对话", - "delete": "删除所选对话", + "archive": "归档所选任务", + "delete": "删除所选任务", "clear": "清除选择", - "archiveNone": "所选对话没有可归档项", + "archiveNone": "所选任务没有可归档项", "skipRunning": "{{count}} 个运行中", "skipAttached": "{{count}} 个远程接管中", "skipNotActive": "{{count}} 个非活跃状态", "skipSeparator": "、", - "archived_one": "已归档 {{count}} 个对话", - "archived_other": "已归档 {{count}} 个对话", - "deleted_one": "已删除 {{count}} 个对话", - "deleted_other": "已删除 {{count}} 个对话", + "archived_one": "已归档 {{count}} 个任务", + "archived_other": "已归档 {{count}} 个任务", + "deleted_one": "已删除 {{count}} 个任务", + "deleted_other": "已删除 {{count}} 个任务", "partialArchiveFailure": "已归档 {{ok}} 个,{{fail}} 个失败,请重试", "partialDeleteFailure": "已删除 {{ok}} 个,{{fail}} 个失败,请重试", "confirmDelete": { - "title": "删除所选对话", - "description": "将删除 {{count}} 个已选对话。删除后无法恢复。", - "dirtyWorktreeWarning": "其中 {{count}} 个对话的 worktree 有未提交更改,删除前会自动保存为 git 快照。", + "title": "删除所选任务", + "description": "将删除 {{count}} 个已选任务。删除后无法恢复。", + "dirtyWorktreeWarning": "其中 {{count}} 个任务的 worktree 有未提交更改,删除前会自动保存为 git 快照。", "confirm": "删除", "cancel": "取消" }, "confirmArchive": { - "title": "归档所选对话", - "description": "将归档 {{count}} 个已选对话。归档后将从列表中移除。", - "descriptionWithSkip": "将归档 {{count}} 个已选对话,{{skip}}的对话会被跳过。归档后将从列表中移除。", - "dirtyWorktreeWarning": "其中 {{count}} 个对话的 worktree 有未提交更改,归档前会自动保存为 git 快照,之后可在对话内一键恢复。", + "title": "归档所选任务", + "description": "将归档 {{count}} 个已选任务。归档后将从列表中移除。", + "descriptionWithSkip": "将归档 {{count}} 个已选任务,{{skip}}的任务会被跳过。归档后将从列表中移除。", + "dirtyWorktreeWarning": "其中 {{count}} 个任务的 worktree 有未提交更改,归档前会自动保存为 git 快照,之后可在任务内一键恢复。", "confirm": "归档", "cancel": "取消" } @@ -6032,12 +6032,12 @@ "moveToProjectFailed": "移动到项目失败", "moveToDialogueDone": "已移到对话", "moveToDialogueFailed": "移到对话失败", - "moveToProjectRunningBlocked": "对话正在运行,结束后再移动", - "moveToProjectAttachedBlocked": "对话正在被接管,结束接管后再移动", - "moveToProjectRemoteUnsupported": "暂不支持移动远程对话", + "moveToProjectRunningBlocked": "任务正在运行,结束后再移动", + "moveToProjectAttachedBlocked": "任务正在被接管,结束接管后再移动", + "moveToProjectRemoteUnsupported": "暂不支持移动远程任务", "openInNewWindow": "在新窗口打开", - "exportShare": "导出对话…", - "copySessionLink": "复制对话链接" + "exportShare": "导出任务…", + "copySessionLink": "复制任务链接" }, "projectAction": { "rename": "重命名项目", @@ -6047,14 +6047,14 @@ "archivedAll": "全部归档", "unarchiveAll": "全部取消归档", "openInExplorer": "在文件管理器中打开", - "newInDirectory": "在此目录新建对话", + "newInDirectory": "在此目录新建任务", "copyDeepLink": "复制深度链接", - "syncCodex": "同步此项目的 Codex 对话", + "syncCodex": "同步此项目的 Codex 任务", "syncCodexDone": "Codex 同步完成:链接 {{inserted}} 条,更新 {{updated}} 条", - "syncCodexAlreadyLinked": "此项目的 Codex 对话已经链接过了", - "syncCodexNone": "没有找到此项目的 Codex 对话", + "syncCodexAlreadyLinked": "此项目的 Codex 任务已经链接过了", + "syncCodexNone": "没有找到此项目的 Codex 任务", "syncCodexFailed": "Codex 同步失败:{{error}}", - "search": "搜索对话" + "search": "搜索任务" }, "deepLink": { "copied": "已复制深度链接", @@ -6067,7 +6067,7 @@ "projects": "项目", "dialogues": "对话" }, - "railNavCount": "{{count}} 个对话" + "railNavCount": "{{count}} 个任务" }, "rename": { "aiRename": "用 AI 重新命名", @@ -6080,7 +6080,7 @@ "file": "文件" }, "sessionHeader": { - "untitled": "未命名对话", + "untitled": "未命名任务", "moreActions": "更多操作" }, "gitContext": { @@ -6106,8 +6106,8 @@ "workdirBrowse": { "backToProjects": "返回项目", "switchProject": "切换项目", - "activeSessionCount_one": "{{count}} 个活跃对话", - "activeSessionCount_other": "{{count}} 个活跃对话", + "activeSessionCount_one": "{{count}} 个活跃任务", + "activeSessionCount_other": "{{count}} 个活跃任务", "treeEmpty": "此目录为空", "treeLoading": "正在加载文件…", "treeAction": { @@ -6180,8 +6180,8 @@ "unrenderable": { "modifiedAt": "修改于 {{time}}", "notSupported": "此文件类型不支持在应用内预览", - "remoteNotSupported": "远程对话的文件在远端机器上,此类型暂不支持预览", - "remoteOversize": "文件过大,远程对话暂不支持预览", + "remoteNotSupported": "远程任务的文件在远端机器上,此类型暂不支持预览", + "remoteOversize": "文件过大,远程任务暂不支持预览", "cachedLocally": "已取回到本地,可用系统应用打开", "openInSystem": "在系统中打开", "revealFolder": "显示所在文件夹" @@ -6225,21 +6225,21 @@ "cachedTruncatedNotice": "缓存副本较大,已截断显示前 32 MB —— 在系统中打开查看完整内容", "fetchingRemote": "正在下载 {{percent}}%({{received}} / {{total}} · {{speed}})", "readErrorOffline": "与远程设备的连接已断开,此文件还没有本地缓存。恢复连接后再试", - "readErrorRemoteFsUnavailable": "远端环境尚未初始化,请先在对话中发送一条消息", + "readErrorRemoteFsUnavailable": "远端环境尚未初始化,请先在任务中发送一条消息", "fetchingRemoteUpload": "远端准备中(上传至中转 {{percent}}%,{{received}} / {{total}} · {{speed}})" }, "sessionTabs": { - "newSession": "新建对话", + "newSession": "新建任务", "closeSession": "关闭 {{title}}" }, "archiveSession": { - "title": "归档对话", + "title": "归档任务", "description": "「{{title}}」将被归档,从列表中移除。可在归档视图中恢复。", "confirm": "归档", "cancel": "取消", "failed": "归档失败,请重试", - "runningBlocked": "当前对话正在执行,请先停止后再归档", - "attachedBlocked": "此对话正在被远程接管,请先收回后再归档" + "runningBlocked": "当前任务正在运行,请先停止后再归档", + "attachedBlocked": "此任务正在被远程接管,请先收回后再归档" }, "confirmSwitchAway": { "title": "当前文件有未保存的修改", @@ -6272,11 +6272,11 @@ "open": "搜索", "placeholder": "搜索标题或内容…", "clear": "清空搜索", - "empty": "输入关键词搜索对话标题和内容", + "empty": "输入关键词搜索任务标题和内容", "searching": "搜索中…", "failed": "搜索失败", - "noResults": "没有匹配的对话", - "jumpFailed": "已打开对话,但未能定位到命中消息", + "noResults": "没有匹配的任务", + "jumpFailed": "已打开任务,但未能定位到命中消息", "moreHits": "还有 {{count}} 处相关", "kind": { "title": "标题", @@ -6322,8 +6322,8 @@ "loadFailed": "加载失败:{{error}}" }, "empty": { - "promptHeading": "暂无自动化任务", - "promptDescription": "创建一个 cron 自动化任务,让 Claude Code 或 Codex 按计划执行你的 Prompt。" + "promptHeading": "暂无自动化", + "promptDescription": "创建一个 cron 自动化,让 Claude Code 或 Codex 按计划执行你的 Prompt。" }, "usageLimitRecovery": { "name": "用量恢复后继续任务", @@ -6377,7 +6377,7 @@ "section": { "otherGroup": "其他", "createProject": "在此项目下新建自动化", - "createProjectAria": "在该项目下新建自动化任务(预填项目目录)", + "createProjectAria": "在该项目下新建自动化(预填项目目录)", "openConfig": "打开配置文件", "openConfigAria": "打开项目自动化配置文件", "openConfigFailed": "打开文件失败", @@ -6410,7 +6410,7 @@ "cell": { "subtitlePaused": "已暂停", "subtitleWaitingForResources": "等待执行资源(当前 {{inFlight}}/{{max}})", - "subtitleQueuedForSession": "排队等待对话空闲", + "subtitleQueuedForSession": "排队等待任务空闲", "subtitleManual": "手动", "subtitleOnce": "一次", "tagOnce": "一次", @@ -6449,17 +6449,17 @@ }, "statusAria": "状态:{{status}}", "took": "用时 {{duration}}", - "openSession": "打开对话", - "sessionDeleted": "对话已删除", - "sessionUnavailable": "暂时无法确认对话状态,请稍后重试", + "openSession": "打开任务", + "sessionDeleted": "任务已删除", + "sessionUnavailable": "暂时无法确认任务状态,请稍后重试", "markReadTitle": "点击标记为已读", - "sessionCost": "对话开销 {{cost}}", - "sessionValue": "对话价值 {{value}}", + "sessionCost": "任务开销 {{cost}}", + "sessionValue": "任务价值 {{value}}", "runCost": "本次开销 {{cost}}", "runValue": "本次估算价值 {{value}}", "costUnavailable": "费用不可用", "legacyCostUnavailable": "历史费用无法拆分", - "persistentSessionGroup": "持续对话 {{session}} · {{count}} 次运行", + "persistentSessionGroup": "持续任务 {{session}} · {{count}} 次运行", "expandRemainingRuns": "展开另外 {{count}} 次", "expandSessionRuns": "展开 {{count}} 次运行", "collapseSessionRuns": "收起历史记录", @@ -6503,7 +6503,7 @@ "create": "创建", "saveAria": "保存自动化", "createAria": "创建自动化", - "namePlaceholder": "为这个自动化任务命名", + "namePlaceholder": "为这个自动化命名", "promptPlaceholder": "示例:在 Sentry 中查找新崩溃并总结关键回归。" }, "executionMode": { @@ -6522,7 +6522,7 @@ "capabilities": { "jira_read": "读取 Jira", "jira_comment": "评论 Jira", - "sessions_dispatch": "派发对话", + "sessions_dispatch": "派发任务", "feishu_read": "读取飞书消息" }, "project": "项目目录", @@ -6533,7 +6533,7 @@ "capabilityDescs": { "jira_read": "查询与读取 Jira 单(按 key 读取 / JQL 搜索)", "jira_comment": "向 Jira 单添加评论", - "sessions_dispatch": "创建或唤醒 {{appName}} 对话并投递消息", + "sessions_dispatch": "创建或唤醒 {{appName}} 任务并投递消息", "feishu_read": "列最近活跃对话;拉取指定对话(群/单聊)的最近消息" }, "capabilityWarn": { @@ -6549,7 +6549,7 @@ "once": "一次", "automatic": "自动", "manually": "手动", - "session": "对话", + "session": "任务", "silentRun": "静默运行", "silentRunTooltip": "开启后,任务成功完成时默认不提醒。若任务说明中要求在特定情况提醒,AI 会在满足条件时主动上报;失败和异常仍会提醒。", "silentRunInfoAria": "查看静默运行规则", @@ -6557,28 +6557,28 @@ "configFeishu": "配置飞书机器人" }, "runSession": { - "label": "运行对话", + "label": "运行任务", "fresh": "每次新建", - "persistent": "持续对话", - "bound": "绑定已有对话", + "persistent": "持续任务", + "bound": "绑定已有任务", "card": { - "open": "打开对话", + "open": "打开任务", "unbind": "解除绑定", - "persistentNote": "对话归档后会自动重建并续绑", - "deleted": "绑定对话已删除", - "deletedPersistentNote": "下次运行时会自动创建新对话并续绑", - "fallbackTitle": "对话 {{id}}" + "persistentNote": "任务归档后会自动重建并续绑", + "deleted": "绑定任务已删除", + "deletedPersistentNote": "下次运行时会自动创建新任务并续绑", + "fallbackTitle": "任务 {{id}}" }, - "switchModelHint": "下次触发时会将绑定对话切换到此模型" + "switchModelHint": "下次触发时会将绑定任务切换到此模型" }, "thread": { - "label": "目标对话", - "loading": "加载对话中…", + "label": "目标任务", + "loading": "加载任务中…", "loadFailed": "加载失败:{{error}}", - "empty": "暂无可用的活跃对话", - "deletedBinding": "绑定对话已删除,请重新选择", - "archivedBinding": "绑定对话已归档,请恢复该对话后再保存,或重新选择", - "selectPlaceholder": "— 选择一个对话 —" + "empty": "暂无可用的活跃任务", + "deletedBinding": "绑定任务已删除,请重新选择", + "archivedBinding": "绑定任务已归档,请恢复该任务后再保存,或重新选择", + "selectPlaceholder": "— 选择一个任务 —" }, "preRunHook": { "label": "前置检查", @@ -6630,7 +6630,7 @@ "scriptWorkspaceRequired": "仅运行脚本需要本地项目目录", "scheduleRequired": "请填写计划", "timezoneRequired": "请填写时区", - "selectThread": "请选择目标对话", + "selectThread": "请选择目标任务", "selectProject": "请选择项目目录", "modelUnavailable": "模型 {{model}} 已不可用,请重新选择", "reasoningInvalid": "Reasoning 必须是 {{values}} 之一", @@ -6644,7 +6644,7 @@ "browseFolder": "选择其他文件夹", "advanced": "高级", "worktree": "worktree", - "worktreeHint": "任务触发时从项目当前分支创建临时 worktree;持续对话会复用该目录", + "worktreeHint": "自动化触发时从项目当前分支创建临时 worktree;持续任务会复用该目录", "advancedAria": "高级设置(worktree)", "useWorktreeAria": "启用 worktree 隔离", "branchHints": { @@ -6700,7 +6700,7 @@ }, "effortHeading": "推理强度", "effortDefault": "默认", - "followSession": "跟随对话", + "followSession": "跟随任务", "modelListAria": "模型列表", "modelSelectorAria": "模型选择器", "categoryListAria": "{{category}} 模型", @@ -6722,28 +6722,28 @@ "deleteDialog": { "title": "删除自动化「{{name}}」?", "titleFallback": "删除自动化?", - "description": "删除后未来的触发会停止。请选择这个自动化已经生成的对话要如何处理。", - "sessionCountPending": "正在统计已生成的对话…", - "sessionCount": "已关联 {{count}} 个对话", + "description": "删除后未来的触发会停止。请选择这个自动化已经生成的任务要如何处理。", + "sessionCountPending": "正在统计已生成的任务…", + "sessionCount": "已关联 {{count}} 个任务", "inflightCount": "当前有 {{count}} 次执行正在进行,删除会立即停止它们", - "optionAria": "选择已生成对话的处理方式", + "optionAria": "选择已生成任务的处理方式", "option": { "keep": { - "title": "保留对话", - "description": "只删除自动化任务,对话继续留在左侧列表中。" + "title": "保留任务", + "description": "只删除自动化,任务继续留在左侧列表中。" }, "archive": { - "title": "归档对话", - "description": "删除自动化任务,并把它生成的对话移入归档。" + "title": "归档任务", + "description": "删除自动化,并把它生成的任务移入归档。" }, "delete": { - "title": "删除对话", - "description": "删除自动化任务,并删除它生成的对话。此操作不可撤销。" + "title": "删除任务", + "description": "删除自动化,并删除它生成的任务。此操作不可撤销。" } }, "confirm": "确认删除自动化", - "confirmDeleteSessions": "删除自动化和对话", - "sessionUpdatePartial": "自动化已删除,但 {{fail}} 个对话处理失败" + "confirmDeleteSessions": "删除自动化和任务", + "sessionUpdatePartial": "自动化已删除,但 {{fail}} 个任务处理失败" }, "confirm": { "delete": { @@ -6843,7 +6843,7 @@ "refresh": "刷新", "emptyTitle": "还没有提交过 Issue", "emptyTitleUnverified": "暂时查不到你的 Issue", - "startIssueChat": "用 /issue 开始新对话", + "startIssueChat": "用 /issue 开始新任务", "statusUnknown": "状态未知", "sourceCindy": "由 Cindy 提交", "sourceGithub": "GitHub 账号", @@ -7079,9 +7079,9 @@ }, "renameSessions": { "confirm": { - "title": "确认批量重命名对话", - "description": "请核对即将写入的标题。确认后会更新 {{count}} 个对话标题。", - "count": "{{count}} 个对话", + "title": "确认批量重命名任务", + "description": "请核对即将写入的标题。确认后会更新 {{count}} 个任务标题。", + "count": "{{count}} 个任务", "from": "原标题:", "to": "新标题:", "untitled": "无标题", @@ -7094,14 +7094,14 @@ "confirm": { "title": "「{{name}}」请求使用你的文件", "count": "{{count}} 个文件", - "descriptionAttachments": "以下文件在当前对话的工作目录之外。允许后,这些文件会交给该插件处理(它只拿到内容指纹,拿不到路径)。", - "descriptionDir": "以下目录/文件在当前对话的工作目录之外。允许后,其中的文件会交给该插件上传(它只拿到一次性票据,拿不到路径)。", - "descriptionSaveDir": "以下目录在当前对话的工作目录之外。允许后,该插件可以把下载的文件存入其中(不会覆盖已有文件)。", - "descriptionFsWrite": "该插件请求在当前对话的工作目录写入以下文件。允许后,本对话内它写同一目录不再询问。", - "descriptionWorkspace": "该文件夹在当前对话的工作目录之外。允许后,Cindy 会以它为工作目录在侧边栏创建一个空对话(已有对话时直接复用),不会读取其中的文件。", + "descriptionAttachments": "以下文件在当前任务的工作目录之外。允许后,这些文件会交给该插件处理(它只拿到内容指纹,拿不到路径)。", + "descriptionDir": "以下目录/文件在当前任务的工作目录之外。允许后,其中的文件会交给该插件上传(它只拿到一次性票据,拿不到路径)。", + "descriptionSaveDir": "以下目录在当前任务的工作目录之外。允许后,该插件可以把下载的文件存入其中(不会覆盖已有文件)。", + "descriptionFsWrite": "该插件请求在当前任务的工作目录写入以下文件。允许后,本任务内它写同一目录不再询问。", + "descriptionWorkspace": "该文件夹在当前任务的工作目录之外。允许后,Cindy 会以它为工作目录在侧边栏创建一个空任务(已有任务时直接复用),不会读取其中的文件。", "dirMeta": "{{count}} 个文件 · {{size}}", "directory": "目录", - "allowDirs": "同时允许这些文件所在目录(本对话内同目录媒体文件不再询问)", + "allowDirs": "同时允许这些文件所在目录(本任务内同目录媒体文件不再询问)", "allow": "允许", "deny": "拒绝" } @@ -7171,11 +7171,11 @@ "usageFailed": "统计失败:{{message}}", "usageEmpty": "最近 30 天还没有本机使用记录", "usageDiagnose": "诊断这个 Skill", - "usageDiagnosisStarting": "创建诊断对话…", - "usageDiagnosisFailed": "创建诊断对话失败:{{message}}", - "usageDiagnosisCreateSessionFailed": "创建诊断对话失败,请重试", + "usageDiagnosisStarting": "创建诊断任务…", + "usageDiagnosisFailed": "创建诊断任务失败:{{message}}", + "usageDiagnosisCreateSessionFailed": "创建诊断任务失败,请重试", "usageDiagnosisAgentTitle": "选择诊断 Agent", - "usageDiagnosisAgentDescription": "会创建一个新对话,并把统计信息和对话索引填入输入框,不会自动发送。", + "usageDiagnosisAgentDescription": "会创建一个新任务,并把统计信息和对话索引填入输入框,不会自动发送。", "usageDiagnosisClaude": "Claude", "usageDiagnosisCodex": "Codex", "usageDiagnosisCancel": "取消", @@ -7186,7 +7186,7 @@ "usageDocumentSize": "文档体积", "usageDocumentSizeValue": "{{size}} / 约 {{tokens}} tokens", "usageReadObservation": "重复读取", - "usageReadObservationTooltip": "统计 30 分钟内读取 3 次及以上的对话占比。它只是注意信号,不直接代表文档有问题。", + "usageReadObservationTooltip": "统计 30 分钟内读取 3 次及以上的任务占比。它只是注意信号,不直接代表文档有问题。", "usageNoData": "无数据", "usageVersionComparisonTitle": "版本对比", "usageVersionNoCurrent": "当前版暂无使用", @@ -7647,20 +7647,20 @@ "effortLabel": "强度:", "permissionLabel": "权限:", "warningHint": "⚠ 标记的模式(ask/default/plan/acceptEdits)一阶段未接 UI 审批弹窗,实验页 hook 自动 deny 工具调用 —— 仅 auto / bypassPermissions 推荐用于真实测试。", - "createSession": "创建对话", - "closeSession": "关闭对话", - "sessionField": "对话:", + "createSession": "创建任务", + "closeSession": "关闭任务", + "sessionField": "任务:", "agentField": "Agent:", "statusField": "状态:", "errorField": "错误:", - "sendNotAccepted": "发送未被接受,turn 没有启动。请检查工作目录或对话状态后重试。", + "sendNotAccepted": "发送未被接受,本轮没有启动。请检查工作目录或任务状态后重试。", "messagePlaceholder": "输入消息…", "imagePathPlaceholder": "Image absolute path (D5 - 测试 Codex 图片)", "send": "发送", "abort": "中止", "clear": "清空", "eventsHeading": "事件 ({{count}})", - "noEvents": "尚无事件。创建对话并发送消息以查看流。" + "noEvents": "尚无事件。创建任务并发送消息以查看流。" }, "logic": { "toasts": { @@ -7692,7 +7692,7 @@ "migrationPartial": "迁移完成:成功 {{success}} / 跳过 {{skipped}} / 失败 {{failed}}(共 {{total}})", "migrationFailed": "迁移失败,请稍后重试", "fetchUpdateNoticeFailed": "获取更新公告失败", - "corruptionRestored": "数据库已从 {{time}} 的快照恢复,期间的对话可能丢失", + "corruptionRestored": "数据库已从 {{time}} 的快照恢复,期间的对话记录可能丢失", "schemaDriftWarning": "本地数据库版本记录不一致。请升级到最新版;如果更新后仍反复出现,请联系支持。应用不会自动删除本地数据", "telegramBotConnected": "Telegram 机器人配置已保存", "telegramBotConnectFailed": "Telegram 机器人连接失败,请检查 Token 和网络", @@ -7757,11 +7757,11 @@ "codexBinaryMissingDescription": "Codex 组件尚未准备好,暂时无法使用这个功能。请稍后重试,或前往「模型供应商」检查 Codex 连接状态。", "goToSettings": "前往设置", "remoteCodexBinaryMissingTitle": "被控端 Codex 组件未就绪", - "remoteAuthDescription": "需要在被控设备「{{device}}」上完成配置后,才能在这里新建并运行对话。请在该设备上配置后重试。", + "remoteAuthDescription": "需要在被控设备「{{device}}」上完成配置后,才能在这里新建并运行任务。请在该设备上配置后重试。", "remoteCodexNoSourceTitle": "被控端 Codex 暂无可用来源", - "remoteCodexNoSourceDescription": "被控设备「{{device}}」尚未连接 Codex 可用的模型来源,无法在这里新建并运行对话。请在该设备上打开 {{appName}},在「设置 → 模型供应商」登录 OpenAI 或连接其他来源后重试。", + "remoteCodexNoSourceDescription": "被控设备「{{device}}」尚未连接 Codex 可用的模型来源,无法在这里新建并运行任务。请在该设备上打开 {{appName}},在「设置 → 模型供应商」登录 OpenAI 或连接其他来源后重试。", "remoteCcNoSourceTitle": "被控端 Claude 暂无可用来源", - "remoteCcNoSourceDescription": "被控设备「{{device}}」尚未连接可用的 Claude 模型来源,无法在这里新建并运行对话。请在该设备上打开 {{appName}},在「设置 → 模型供应商」连接来源后重试。", + "remoteCcNoSourceDescription": "被控设备「{{device}}」尚未连接可用的 Claude 模型来源,无法在这里新建并运行任务。请在该设备上打开 {{appName}},在「设置 → 模型供应商」连接来源后重试。", "gotIt": "知道了" }, "fileTypes": { @@ -7829,7 +7829,7 @@ "split": { "leadLabel": "Lead ({{agent}})", "workerLabel": "Worker ({{agent}})", - "leadNotFound": "找不到 Orca lead 对话", + "leadNotFound": "找不到 Orca lead 任务", "waitingForWorker": "等待 Worker", "closeAria": "关闭" } @@ -7850,7 +7850,7 @@ }, "commands": { "toast": { - "remoteUnsupported": "对方设备的应用版本过旧,暂不支持在远程对话使用此命令,请先升级被控设备的 {{appName}}" + "remoteUnsupported": "对方设备的应用版本过旧,暂不支持在远程任务使用此命令,请先升级被控设备的 {{appName}}" } }, "goal": { @@ -7910,7 +7910,7 @@ }, "toast": { "usage": "用法:/goal <目标条件>", - "noSession": "请先打开或开始一个对话再设目标", + "noSession": "请先打开或开始一个任务再设目标", "failed": "设置目标失败", "set": "目标已设置,开始自动推进", "cleared": "目标已清除" @@ -7933,9 +7933,9 @@ "failed": "学习失败", "cancel": "取消", "review": "查看提案", - "fromConversation": "从当前对话蒸馏", - "openSession": "打开学习对话", - "continueHint": "在本对话中继续发消息即可修改提案,满意后点「查看提案」应用" + "fromConversation": "从当前任务蒸馏", + "openSession": "打开学习任务", + "continueHint": "在本任务中继续发消息即可修改提案,满意后点「查看提案」应用" }, "review": { "ariaLabel": "技能提案审查面板", @@ -7951,7 +7951,7 @@ "discarding": "放弃中…", "discardFailed": "放弃失败,请重试", "discardedToast": "已放弃该技能提案", - "reviseHint": "想修改?直接在学习对话里说即可" + "reviseHint": "想修改?直接在学习任务里说即可" }, "ipcError": { "LEARN_BUSY": "已有一个学习任务在进行中", @@ -7974,12 +7974,12 @@ }, "globalDrop": { "cindyHint": "松开安装插件", - "cshareHint": "松开导入分享的对话" + "cshareHint": "松开导入分享的任务" }, "sessionShare": { "export": { - "title": "导出对话", - "sensitiveWarning": "导出文件包含此对话的完整对话记录与工具输出(可能含文件内容、命令结果等敏感信息),请确认接收方可信。", + "title": "导出任务", + "sensitiveWarning": "导出文件包含此任务的完整对话记录与工具输出(可能含文件内容、命令结果等敏感信息),请确认接收方可信。", "encryptLabel": "使用密码加密导出文件", "passwordPlaceholder": "密码(至少 4 位)", "passwordConfirmPlaceholder": "再次输入密码", @@ -7991,45 +7991,45 @@ "confirm": "导出", "confirmExcludeMedia": "排除媒体后导出", "oversizeHint": "内容超出体积上限,其中媒体文件约 {{mediaMb}} MB。可排除媒体后重新导出(保留文字与上下文)。", - "tooLarge": "对话内容超出 {{limitMb}} MB 导出上限,排除媒体也无法缩小,暂时无法导出。", + "tooLarge": "该任务超出 {{limitMb}} MB 导出上限,排除媒体也无法缩小,暂时无法导出。", "doneFull": "已导出,接收方可完整续聊", "donePartial": "已导出,部分历史转录缺失(仍可续聊)", "doneDbOnly": "已导出,仅包含对话记录(接收方无法无缝续聊)", "failed": "导出失败" }, "error": { - "NOT_FOUND": "对话不存在", - "PRECONDITION_FAILED": "该对话不支持导出", + "NOT_FOUND": "任务不存在", + "PRECONDITION_FAILED": "该任务不支持导出", "SHARE_EXPORT_FAILED": "导出失败", "SHARE_FILE_INVALID": "文件无效或已损坏", "SHARE_PASSWORD_REQUIRED": "该文件已加密,需要密码", "SHARE_PASSWORD_WRONG": "密码错误或文件已损坏", "SHARE_VERSION_UNSUPPORTED": "文件由更新版本的 {{appName}} 导出,请先升级", - "SHARE_CONFLICT": "该对话已导入过(相同的对话标识已存在)", + "SHARE_CONFLICT": "该任务已导入过(相同的任务标识已存在)", "SHARE_IMPORT_FAILED": "导入失败,已回滚", "SHARE_WORKTREE_NOT_GIT": "所选目录不是 Git 仓库,无法在 worktree 中创建", "SHARE_WORKTREE_FAILED": "Worktree 创建失败,导入已中止" }, "import": { "entryButton": "从 .cshare 文件导入…", - "title": "导入分享的对话", + "title": "导入分享的任务", "passwordPrompt": "该文件已加密,请输入分享者提供的密码。", "passwordPlaceholder": "密码", "unlock": "解锁", "previewMeta": "{{agent}} · {{count}} 条消息 · 导出于 {{exportedAt}}", "riskWarning": "该文件来自外部分享,请确认来源可信。导入的历史内容可能影响 AI 的后续行为;继续对话时,AI 仍可读写本机文件、执行命令。", - "workdirHint": "原对话的工作目录是 {{originalDir}},请选择你机器上对应的目录。", + "workdirHint": "原任务的工作目录是 {{originalDir}},请选择你机器上对应的目录。", "workdirEmpty": "尚未选择目录", "pickWorkdir": "选择目录", "useWorktree": "在 worktree 中创建", - "useWorktreeHint": "在所选项目上新建一个独立 worktree,导入的对话将在其中工作,不影响主工作区。", + "useWorktreeHint": "在所选项目上新建一个独立 worktree,导入的任务将在其中工作,不影响主工作区。", "confirm": "导入", "cancel": "取消", "close": "关闭", - "openSession": "打开对话", + "openSession": "打开任务", "doneTitle": "导入完成", - "conflictTitle": "该对话已存在", - "conflictBody": "本机已存在相同标识的对话(此前导入过,或原对话就在本机)。重新导入将覆盖已有对话,替换为此分享包中的内容。", + "conflictTitle": "该任务已存在", + "conflictBody": "本机已存在相同标识的任务(此前导入过,或原任务就在本机)。重新导入将覆盖已有任务,替换为此分享包中的内容。", "conflictOverwrite": "覆盖导入", "conflictCancel": "取消", "failed": "导入失败" @@ -8042,10 +8042,10 @@ "fidelityResult": { "full": "上下文完整,可直接继续对话。", "partial": "可继续对话;回溯到较早的分叉点可能失败。", - "db-only": "仅导入了对话记录:Claude 对话需要重建上下文;Codex 对话将以纯文本历史恢复(丢失推理与工具细节)。" + "db-only": "仅导入了对话记录:Claude 任务需要重建上下文;Codex 任务将以纯文本历史恢复(丢失推理与工具细节)。" }, "note": { - "codexStateSkipped": "本机尚未初始化 Codex 环境,对话状态未完全恢复;先运行一次 Codex 对话后即可正常续聊。", + "codexStateSkipped": "本机尚未初始化 Codex 环境,任务状态未完全恢复;先运行一次 Codex 任务后即可正常续聊。", "workdirKeyInexact": "所选目录路径过长,无法放置历史转录,已降级为仅对话记录。" } }, diff --git a/apps/desktop/src/renderer/lib/makerChatStore.ts b/apps/desktop/src/renderer/lib/makerChatStore.ts index 45a8a2cb4ce..02ce733f318 100644 --- a/apps/desktop/src/renderer/lib/makerChatStore.ts +++ b/apps/desktop/src/renderer/lib/makerChatStore.ts @@ -6981,7 +6981,7 @@ const autoNameSettled = new Set(); * * 用户在首次 `maker:auto-title` 返回前连着发两条文字时,两次都会通过 `autoNameSettled` * 检查、各起一次尝试。若**较早**那次失败,它的撤回不能动预览 —— 更晚的尝试仍在飞, - * 预览还有主人,撤回会让标题白闪一次「未命名对话」(PR #1031 review P1)。 + * 预览还有主人,撤回会让标题白闪一次「未命名任务」(PR #1031 review P1)。 * 与 SessionMenuSheet 的 `renameSeqRef`、搜索框的 `requestSeqRef` 同款守卫。 */ const autoNameAttempts = new Map(); @@ -7085,7 +7085,7 @@ function scheduleAutoName( * * 预览是「马上会有权威标题回流」的赌注,它的失效条件是权威标题落地。起名没写成时 * 那个条件永远不成立:叠加层会在每次全量刷新后继续顶着 DB 里的哨兵,会话永久显示一个 - * **库里并不存在**的标题(重启后又变回「未命名对话」,同一会话两种标题)。宁可退回可 + * **库里并不存在**的标题(重启后又变回「未命名任务」,同一会话两种标题)。宁可退回可 * 解释的兜底文案 —— 而且没登记 `autoNameSettled`,下一条带文字的消息会重试起名。 * * 万一 IPC 是「写库成功、响应丢了」,撤回也不会造成错误状态:main 已经广播过 diff --git a/apps/desktop/src/renderer/lib/sessionsBus.ts b/apps/desktop/src/renderer/lib/sessionsBus.ts index a772b2777d2..6662a2f9c4e 100644 --- a/apps/desktop/src/renderer/lib/sessionsBus.ts +++ b/apps/desktop/src/renderer/lib/sessionsBus.ts @@ -78,7 +78,7 @@ export function emitAutoTitlePreview(sessionId: string, title: string): void { * * 为什么必须有这条:预览是叠加层,它的失效条件是「权威标题落地」。起名彻底失败时那个 * 条件永远不成立,叠加层会在每次全量刷新后继续顶着 DB 里的哨兵,会话就永久显示一个 - * **库里并不存在**的标题(重启后又变回「未命名对话」)。宁可退回可解释的兜底文案: + * **库里并不存在**的标题(重启后又变回「未命名任务」)。宁可退回可解释的兜底文案: * 下一条带文字的消息会重试起名(`autoNameSettled` 未登记)。 */ export function emitAutoTitlePreviewCleared(sessionId: string): void { diff --git a/apps/desktop/src/renderer/lib/sessionsStore.ts b/apps/desktop/src/renderer/lib/sessionsStore.ts index b89165e94c5..a730169792e 100644 --- a/apps/desktop/src/renderer/lib/sessionsStore.ts +++ b/apps/desktop/src/renderer/lib/sessionsStore.ts @@ -112,7 +112,7 @@ function mergeSession(prev: Session, patch: Partial): Session { * 为什么必须是叠加层、而不是一次性 patchLocal:新建会话的 `sessions:created` push 会 * 触发 `forceRefreshAll`,那次重拉从 DB 拿回的行**仍带默认哨兵**(权威标题要等 * `maker:auto-title` 落库才有),会把只写进缓存的乐观标题冲掉 —— 表现为标题先显示 - * 用户那句话、又退回「未命名对话」,直到 IPC 回来(PR #1031 review P1)。 + * 用户那句话、又退回「未命名任务」,直到 IPC 回来(PR #1031 review P1)。 * * 语义与 device-link 远程侧的 `remoteProjectsStore.pendingTitlePreview` 对称:只在 * **权威标题仍是哨兵**时顶替显示,权威标题一到就自动让位并回收条目,不需要显式失效。 @@ -157,7 +157,7 @@ function applyAutoTitlePreviews(list: Session[]): Session[] { * * 典型时序(新建会话):`sessions:created` push → `forceRefreshAll()` 起飞(快照里是 * 哨兵)→ main 写完占位 → `sessions:patched` 落进缓存 → 那个更早的请求才回来,把哨兵 - * 写回去,界面退到「未命名对话」直到下一次刷新。乐观预览叠加层此刻已按权威值到达的 + * 写回去,界面退到「未命名任务」直到下一次刷新。乐观预览叠加层此刻已按权威值到达的 * 规则回收,没人再替它顶回来(PR #1031 review P1)。 * * 方向与 {@link autoTitlePreviews} 互补:那一层管「请求发起于乐观写入**之后**、DB 里还 @@ -361,7 +361,7 @@ export const sessionsStore = { // **包括「权威标题与预览逐字相同」的常见情形**(两端共用 normalizeAutoTitle,占位本来 // 就该一样)。曾经在这里放过 `preview !== patch.title` 的例外,结果是缓存里那个串到底 // 是「叠加上去的乐观值」还是「已落库的权威值」再也分不出来 —— 随后的失败撤回会把 - // **已经落库**的标题打回哨兵、界面退到「未命名对话」并与 DB 不一致(PR #1031 review P1)。 + // **已经落库**的标题打回哨兵、界面退到「未命名任务」并与 DB 不一致(PR #1031 review P1)。 // 语义上也该无条件回收:DB 已经有值,叠加层的唯一用途(盖住仍是哨兵的行)已经消失。 // // 乐观预览不走这个门(见 {@link applyOptimisticTitle}),所以这里见到的标题一律是 diff --git a/apps/desktop/src/renderer/vite-env.d.ts b/apps/desktop/src/renderer/vite-env.d.ts index 1e935c234c1..546c73e4042 100644 --- a/apps/desktop/src/renderer/vite-env.d.ts +++ b/apps/desktop/src/renderer/vite-env.d.ts @@ -2103,10 +2103,10 @@ interface ElectronAPI { // ── 系统级通知(CC Agent session 状态变更)── /** * 弹一条桌面通知。kind 决定文案: - * - 'done' — 真完成,文案 "会话「xxx」已完成" - * - 'error' — 执行失败,文案 "会话「xxx」执行失败" + * - 'done' — 真完成,文案 "任务「xxx」已完成" + * - 'error' — 执行失败,文案 "任务「xxx」执行失败" * - 'needs-reply' — 等用户回复(ask-user / permission / plan-review), - * 文案 "会话「xxx」需要你回复" + * 文案 "任务「xxx」需要你回复" */ notificationShowSessionEvent: (payload: { sessionId: string; diff --git a/apps/desktop/src/shared/__tests__/agentIslandCompactBadgeWidth.test.ts b/apps/desktop/src/shared/__tests__/agentIslandCompactBadgeWidth.test.ts index 255f3c6d46d..042ee45a27b 100644 --- a/apps/desktop/src/shared/__tests__/agentIslandCompactBadgeWidth.test.ts +++ b/apps/desktop/src/shared/__tests__/agentIslandCompactBadgeWidth.test.ts @@ -34,7 +34,7 @@ const NATIVE_COMPACT_BADGE_WIDTH: Array<{ renderedWidth: number; }> = [ { label: '单会话', pillSnapshot: { activeSessionCount: 0, sessionCount: 1 }, layoutWidth: 22, renderedWidth: 22 }, - { label: '12 个会话', pillSnapshot: { activeSessionCount: 0, sessionCount: 12 }, layoutWidth: 22, renderedWidth: 22 }, + { label: '12 个任务', pillSnapshot: { activeSessionCount: 0, sessionCount: 12 }, layoutWidth: 22, renderedWidth: 22 }, { label: '1/2', pillSnapshot: { activeSessionCount: 1, sessionCount: 2 }, layoutWidth: 30, renderedWidth: 30 }, { label: '9/9', pillSnapshot: { activeSessionCount: 9, sessionCount: 9 }, layoutWidth: 30, renderedWidth: 30 }, { label: '1/12', pillSnapshot: { activeSessionCount: 1, sessionCount: 12 }, layoutWidth: 37, renderedWidth: 33 }, diff --git a/apps/desktop/src/shared/__tests__/autoTitleSeed.test.ts b/apps/desktop/src/shared/__tests__/autoTitleSeed.test.ts index c59e4b4fe75..36f10701c42 100644 --- a/apps/desktop/src/shared/__tests__/autoTitleSeed.test.ts +++ b/apps/desktop/src/shared/__tests__/autoTitleSeed.test.ts @@ -384,7 +384,7 @@ describe('deriveAutoTitleSeed — 用户一个字没写', () => { describe('deriveAutoTitleSeed — 会话/项目引用', () => { const HREF = 'cindy://session/src-1'; - it('只拖一个会话引用 → 用被引用会话的标题,而不是 [Referenced conversation] 机器块', () => { + it('只拖一个任务引用 → 用被引用会话的标题,而不是 [Referenced conversation] 机器块', () => { const seed = deriveAutoTitleSeed( queued({ text: HREF, diff --git a/apps/desktop/src/shared/__tests__/conversationSearchTitle.test.ts b/apps/desktop/src/shared/__tests__/conversationSearchTitle.test.ts index bc5cd1f1c21..c5a625f405e 100644 --- a/apps/desktop/src/shared/__tests__/conversationSearchTitle.test.ts +++ b/apps/desktop/src/shared/__tests__/conversationSearchTitle.test.ts @@ -13,11 +13,11 @@ import { conversationSearchTitle } from '../conversationSearch'; describe('conversationSearchTitle', () => { it('哨兵标题换成调用方给的本地化文案', () => { - expect(conversationSearchTitle(DEFAULT_DRAFT_SESSION_TITLE, '未命名对话')).toBe('未命名对话'); + expect(conversationSearchTitle(DEFAULT_DRAFT_SESSION_TITLE, '未命名任务')).toBe('未命名任务'); }); it('普通标题原样返回(不受 label 影响)', () => { - expect(conversationSearchTitle('修 Orca 心跳', '未命名对话')).toBe('修 Orca 心跳'); + expect(conversationSearchTitle('修 Orca 心跳', '未命名任务')).toBe('修 Orca 心跳'); }); it('没传 label(旧 renderer 构建)时退回原始标题,不静默变成空串', () => { diff --git a/apps/desktop/src/shared/__tests__/deepLinkSchemes.test.ts b/apps/desktop/src/shared/__tests__/deepLinkSchemes.test.ts index d0d32d26489..69e9ba2bc96 100644 --- a/apps/desktop/src/shared/__tests__/deepLinkSchemes.test.ts +++ b/apps/desktop/src/shared/__tests__/deepLinkSchemes.test.ts @@ -65,7 +65,7 @@ describe('isDeepLinkProtocol', () => { describe('textContainsDeepLink', () => { it('detects either scheme anywhere in free text', () => { - expect(textContainsDeepLink('见 cindy://session/abc 这个会话')).toBe(true); + expect(textContainsDeepLink('见 cindy://session/abc 这个任务')).toBe(true); expect(textContainsDeepLink('老链接 xdt-maker://session/abc 仍可点')).toBe(true); expect(textContainsDeepLink('普通文本 https://example.com')).toBe(false); }); diff --git a/apps/desktop/src/shared/agentIsland.ts b/apps/desktop/src/shared/agentIsland.ts index bef38a32a4b..b07f9638d45 100644 --- a/apps/desktop/src/shared/agentIsland.ts +++ b/apps/desktop/src/shared/agentIsland.ts @@ -426,8 +426,8 @@ export interface AgentIslandStrings { export const DEFAULT_AGENT_ISLAND_STRINGS: AgentIslandStrings = { appName: BRAND_NAME, - newConversationTitle: 'New Chat', - newConversationHint: 'Start a new conversation', + newConversationTitle: 'New Session', + newConversationHint: 'Start a new session', muteSound: 'Mute Agent Island', enableSound: 'Enable Agent Island sound', settings: 'Agent Island settings', diff --git a/apps/mobile/app/devices/index.tsx b/apps/mobile/app/devices/index.tsx index 505e0d6537b..4ce6d8af29d 100644 --- a/apps/mobile/app/devices/index.tsx +++ b/apps/mobile/app/devices/index.tsx @@ -1046,7 +1046,7 @@ export default function HomeScreen() { if (action === 'delete') { // 菜单不再展示会话标题(2026-07-07 产品反馈),删除确认在这里带上标题作上下文。 // 哨兵先过投影,与列表行显示同一个串:否则确认框里写着 "New Maker",用户在列表上 - // 看到的却是「未命名对话」,对不上自己要删的是哪条。 + // 看到的却是「未命名任务」,对不上自己要删的是哪条。 const title = projectDraftSessionTitle(session.title, t('session.menu.unnamedTitle')).trim() || t('devices.list.untitled'); Alert.alert(t('devices.list.alert.deleteTitle'), t('devices.list.alert.deleteMessage', { title }), [ diff --git a/apps/mobile/app/sessions/[sessionId].tsx b/apps/mobile/app/sessions/[sessionId].tsx index 62e8d0d2141..cbdd39972f7 100644 --- a/apps/mobile/app/sessions/[sessionId].tsx +++ b/apps/mobile/app/sessions/[sessionId].tsx @@ -7154,15 +7154,20 @@ export default function SessionScreen() { const forkAtMessage = useCallback((clientId: string, draft?: MobileMessageDraft) => { if (!deviceId || messageActionBusy) return; + // 文案走 i18n(与本页其它 Alert 同规):mobile 支持 en / ja / ko,硬编码中文会让 + // 非中文环境看到中文弹窗。四语措辞与 desktop 的 chat.messageActionBar.fork* 对齐。 Alert.alert( - '从这里开启一个新对话?', - '系统会根据这里的对话上下文创建一个独立的新对话。原对话不会改变,之后两边的消息互不影响。', + t('session.screen.forkConfirmTitle'), + t('session.screen.forkConfirmDescription'), [ - { text: '取消', style: 'cancel' }, - { text: '开启新对话', onPress: () => void performForkAtMessage(clientId, draft) }, + { text: t('session.screen.forkCancel'), style: 'cancel' }, + { + text: t('session.screen.forkConfirm'), + onPress: () => void performForkAtMessage(clientId, draft), + }, ], ); - }, [deviceId, messageActionBusy, performForkAtMessage]); + }, [deviceId, messageActionBusy, performForkAtMessage, t]); const openForkOrigin = useCallback(() => { const parentSessionId = currentSession?.parentSessionId; diff --git a/apps/mobile/app/sessions/new.tsx b/apps/mobile/app/sessions/new.tsx index 9e087bea215..0285f5cdd03 100644 --- a/apps/mobile/app/sessions/new.tsx +++ b/apps/mobile/app/sessions/new.tsx @@ -688,7 +688,7 @@ export default function NewRemoteSessionScreen() { }; }, [draft.agentKind, draft.permissionMode, newSessionPreferences, newSessionPreferencesLoaded]); - // 新建对话默认运行配置 = 跟随最近一次会话(整套 agent+model+effort,按所选设备 scope);没有最近会话则用 + // 新建任务默认运行配置 = 跟随最近一次会话(整套 agent+model+effort,按所选设备 scope);没有最近会话则用 // 模型列表最上面那个(列表异步就绪后再设)。一旦用户手动选过模型即不再覆盖;切设备(未手动选过)按新设备重算。 // 决策逻辑全在纯函数 resolveNewSessionAutoDefault 里(便于单测);此 effect 只负责"调纯函数 → setDraft + 更新 ref"。 // 最近会话路径同步可得(sessions 在内存);列表最上面依赖 providers 异步,故 modelRows 就绪后此 effect 再触发。 diff --git a/apps/mobile/src/__tests__/collaboration.test.ts b/apps/mobile/src/__tests__/collaboration.test.ts index 873a2d50970..10fced92970 100644 --- a/apps/mobile/src/__tests__/collaboration.test.ts +++ b/apps/mobile/src/__tests__/collaboration.test.ts @@ -66,7 +66,7 @@ describe('mobile collaboration session fallback', () => { expect(sessionCollaborationLabel(custom)).toBe('协作 reviewer'); expect(sessionCollaborationNotice(custom)).toContain('协作编排操作请先在电脑端完成'); - expect(sessionCollaborationReadOnlyReason(custom)).toContain('会话写操作请先在电脑端完成'); + expect(sessionCollaborationReadOnlyReason(custom)).toContain('任务写操作请先在电脑端完成'); expect(sessionCollaborationLabel(session({ orcaRole: null }))).toBeNull(); expect(isCollaborationSession(session({ orcaRole: null }))).toBe(false); expect(sessionCollaborationReadOnlyReason(session({ orcaRole: null }))).toBeNull(); diff --git a/apps/mobile/src/__tests__/desktopSlashCommands.test.ts b/apps/mobile/src/__tests__/desktopSlashCommands.test.ts index 98dd6a7d912..832b50280f7 100644 --- a/apps/mobile/src/__tests__/desktopSlashCommands.test.ts +++ b/apps/mobile/src/__tests__/desktopSlashCommands.test.ts @@ -23,9 +23,9 @@ describe('filterMobileDesktopCommands', () => { describe('parseMobileDesktopCommand', () => { it('命中白名单命令并抽出参数', () => { - expect(parseMobileDesktopCommand('/learn 总结这个会话的调试技巧')).toEqual({ + expect(parseMobileDesktopCommand('/learn 总结这个任务的调试技巧')).toEqual({ name: 'learn', - args: '总结这个会话的调试技巧', + args: '总结这个任务的调试技巧', }); }); diff --git a/apps/mobile/src/__tests__/devices.test.ts b/apps/mobile/src/__tests__/devices.test.ts index 0212f62eb9f..749e419e7b1 100644 --- a/apps/mobile/src/__tests__/devices.test.ts +++ b/apps/mobile/src/__tests__/devices.test.ts @@ -73,7 +73,7 @@ describe('mobile controllable device filter', () => { ['offline', 'offline', false], ]); expect(items.find((item) => item.device.deviceId === 'ready')?.statusDetail).toBe('已允许远程控制'); - expect(items.find((item) => item.device.deviceId === 'busy')?.statusDetail).toBe('电脑端正在处理会话'); + expect(items.find((item) => item.device.deviceId === 'busy')?.statusDetail).toBe('电脑端正在处理任务'); expect(items.find((item) => item.device.deviceId === 'revoked')?.statusDetail).toContain('恢复'); expect(items.find((item) => item.device.deviceId === 'disabled')?.statusDetail).toContain('打开允许远程控制'); expect(items.find((item) => item.device.deviceId === 'offline')?.statusDetail).toContain('10 分钟前在线'); diff --git a/apps/mobile/src/__tests__/interactionModel.test.ts b/apps/mobile/src/__tests__/interactionModel.test.ts index 9ab92d77abe..4990977ee1f 100644 --- a/apps/mobile/src/__tests__/interactionModel.test.ts +++ b/apps/mobile/src/__tests__/interactionModel.test.ts @@ -97,7 +97,7 @@ describe('interactionModel', () => { riskSummary: null, canAlwaysAllow: true, })).toEqual({ - title: '可以只允许一次,也可以本会话总是允许', + title: '可以只允许一次,也可以本任务总是允许', detail: '工具: Bash', }); expect(buildPermissionDecisionSummary({ diff --git a/apps/mobile/src/__tests__/invokeTimeouts.test.ts b/apps/mobile/src/__tests__/invokeTimeouts.test.ts index a69ad157f2a..b1fc1958495 100644 --- a/apps/mobile/src/__tests__/invokeTimeouts.test.ts +++ b/apps/mobile/src/__tests__/invokeTimeouts.test.ts @@ -33,7 +33,7 @@ describe('resolveMobileInvokeTimeoutMs', () => { // regenerate-title:OAuth 刷新(~10s)+ 标题请求自身 12s,合法总预算 ~22s。 expect(resolveMobileInvokeTimeoutMs('maker:regenerate-title')).toBe(30_000); // create-session:冷启动 app-server / agent 拉起无更短 deadline;goal 路径 - // 无稳定客户端会话 id,误超时重试会建出第二个会话。 + // 无稳定客户端会话 id,误超时重试会建出第二个任务。 expect(resolveMobileInvokeTimeoutMs('maker:create-session')).toBe(30_000); // goal set/resume:restoreSessionForGoal 同样 await createSession 重启持久化 // agent;两者有真实副作用,误超时重试会改动/重启已在跑的 goal。 diff --git a/apps/mobile/src/__tests__/messageActionMenu.test.ts b/apps/mobile/src/__tests__/messageActionMenu.test.ts index 2afe3259ad0..7d39cccd75c 100644 --- a/apps/mobile/src/__tests__/messageActionMenu.test.ts +++ b/apps/mobile/src/__tests__/messageActionMenu.test.ts @@ -1,8 +1,14 @@ import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; -import { describe, expect, it } from 'vitest'; +import { beforeAll, describe, expect, it } from 'vitest'; +import { i18n } from '@/i18n'; import { buildMobileMessageMenu } from '@/session/messageActionMenu'; +// 文案已 i18n 化;固定 zh-CN 让字面量断言与语言环境解耦(全局 mock 默认 en-US)。 +beforeAll(async () => { + await i18n.changeLanguage('zh-CN'); +}); + describe('mobile message action menu', () => { it('matches desktop order, natural language, and destructive grouping', () => { expect(buildMobileMessageMenu({ @@ -12,11 +18,11 @@ describe('mobile message action menu', () => { canFork: true, canRewind: true, })).toEqual([ - { id: 'fork', label: '在新对话中继续' }, + { id: 'fork', label: '开启一个新任务' }, { id: 'add-to-chat', label: '添加到对话' }, - { id: 'copy-link', label: '复制当前对话链接' }, + { id: 'copy-link', label: '复制当前消息链接' }, { id: 'rewind', label: '回到此处' }, - { id: 'delete', label: '删除本条对话', destructive: true, separatorBefore: true }, + { id: 'delete', label: '删除本条消息', destructive: true, separatorBefore: true }, ]); }); diff --git a/apps/mobile/src/__tests__/mobileRemoteFlowSmoke.test.ts b/apps/mobile/src/__tests__/mobileRemoteFlowSmoke.test.ts index d2d65973da3..e6b4f0d8c79 100644 --- a/apps/mobile/src/__tests__/mobileRemoteFlowSmoke.test.ts +++ b/apps/mobile/src/__tests__/mobileRemoteFlowSmoke.test.ts @@ -709,9 +709,9 @@ describe('mobile remote-control headless UI flow smoke', () => { )).toMatchObject({ title: '移动端巡检', unreadCount: 1, - detail: 'cron 0 9 * * * · 新会话 · Claude · xdt-maker', + detail: 'cron 0 9 * * * · 新任务 · Claude · xdt-maker', runSessionDetail: null, - runSessionLabel: '新会话', + runSessionLabel: '新任务', }); await maker.schedule.markScheduleRunsRead('sched-1'); expect(countUnreadRuns(normalizeScheduleRuns(await maker.schedule.listRuns('sched-1', 50)))).toBe(0); diff --git a/apps/mobile/src/__tests__/mobileSessionMessageCache.test.ts b/apps/mobile/src/__tests__/mobileSessionMessageCache.test.ts index e27d77768b4..2062e5a9a23 100644 --- a/apps/mobile/src/__tests__/mobileSessionMessageCache.test.ts +++ b/apps/mobile/src/__tests__/mobileSessionMessageCache.test.ts @@ -100,7 +100,7 @@ describe('mobileSessionMessageCache', () => { const fresh = [ { ...makeMessage({ id: 'u1', clientId: 'c-u1', role: 'user', createdAt: isoAt(1) }), - content: JSON.stringify({ text: '看下这个会话', images: [], files: [] }), + content: JSON.stringify({ text: '看下这个任务', images: [], files: [] }), updatedAt: isoAt(1), userId: 'user-1', }, diff --git a/apps/mobile/src/__tests__/newSession.test.ts b/apps/mobile/src/__tests__/newSession.test.ts index ab303fdf245..6e17fab64df 100644 --- a/apps/mobile/src/__tests__/newSession.test.ts +++ b/apps/mobile/src/__tests__/newSession.test.ts @@ -333,7 +333,7 @@ describe('pickNewSessionDefaultDevice', () => { describe('new session default device follows the home device filter', () => { it('sends the deviceExplicit flag only when the home list is filtered to one device', () => { const homeSource = readTextLf(resolve(process.cwd(), 'app/devices/index.tsx'), 'utf8'); - // 筛选某台电脑时带显式标记;"所有对话"(selectedDeviceId=null)不带,保留记忆回落。 + // 筛选某台电脑时带显式标记;"所有任务"(selectedDeviceId=null)不带,保留记忆回落。 expect(homeSource).toContain("...(selectedDeviceId ? { deviceExplicit: '1' } : {})"); }); @@ -546,7 +546,7 @@ describe('new session model', () => { model: 'claude-sonnet-4-6', }, 'Carol Mac')).toMatchObject({ title: '准备创建并发送', - subtitle: '确认后会在被控设备创建对话,并把首条消息加入队列。', + subtitle: '确认后会在被控设备创建任务,并把首条消息加入队列。', details: [ '设备:Carol Mac', '位置:对话工作区', @@ -773,7 +773,7 @@ describe('new session composer surface', () => { const voiceButtonEnd = newSource.indexOf('// 切 agent:', voiceButtonStart); const voiceButtonSource = newSource.slice(voiceButtonStart, voiceButtonEnd); const storedAgentStart = newSource.indexOf('const storedAgentKind = newSessionPreferences?.agentKind;'); - const storedAgentEnd = newSource.indexOf('// 新建对话默认运行配置', storedAgentStart); + const storedAgentEnd = newSource.indexOf('// 新建任务默认运行配置', storedAgentStart); const storedAgentSource = newSource.slice(storedAgentStart, storedAgentEnd); const selectDeviceStart = newSource.indexOf('const selectDevice = useCallback((option: NewSessionDeviceOption) => {'); const selectDeviceEnd = newSource.indexOf('// 切 agent:', selectDeviceStart); diff --git a/apps/mobile/src/__tests__/remoteSessionStore.test.ts b/apps/mobile/src/__tests__/remoteSessionStore.test.ts index 2bf414486cc..78c4ed68064 100644 --- a/apps/mobile/src/__tests__/remoteSessionStore.test.ts +++ b/apps/mobile/src/__tests__/remoteSessionStore.test.ts @@ -2379,7 +2379,7 @@ describe('setDeviceSessions 对在途乐观创建行(pendingLocalCreation)的保 remoteSessionStore.setDeviceSessions('dev-1', 'Mac', [session('s-old')]); remoteSessionStore.upsertDeviceSession('dev-1', 'Mac', session('s-new', { pendingLocalCreation: true })); - // 首页 in-flight 的 sessions:list 响应此刻返回(发出时被控端还没建这个会话)。 + // 首页 in-flight 的 sessions:list 响应此刻返回(发出时被控端还没建这个任务)。 remoteSessionStore.setDeviceSessions('dev-1', 'Mac', [session('s-old')]); const rows = remoteSessionStore.getSessions(); expect(rows.map((s) => s.id)).toContain('s-new'); diff --git a/apps/mobile/src/__tests__/scheduleDelete.test.ts b/apps/mobile/src/__tests__/scheduleDelete.test.ts index d8ba79dfef4..0b189ac7627 100644 --- a/apps/mobile/src/__tests__/scheduleDelete.test.ts +++ b/apps/mobile/src/__tests__/scheduleDelete.test.ts @@ -48,7 +48,7 @@ describe('mobile schedule delete model', () => { sessionCount: 2, inflightCount: 1, }); - expect(describeScheduleDeletePreview(preview)).toBe('找到 2 个由它生成的会话,还有 1 次执行正在进行'); + expect(describeScheduleDeletePreview(preview)).toBe('找到 2 个由它生成的任务,还有 1 次执行正在进行'); }); it('maps deletion dispositions to desktop-compatible session patches', () => { diff --git a/apps/mobile/src/__tests__/scheduleModel.test.ts b/apps/mobile/src/__tests__/scheduleModel.test.ts index a2fd625bf2e..0f04ab85bd7 100644 --- a/apps/mobile/src/__tests__/scheduleModel.test.ts +++ b/apps/mobile/src/__tests__/scheduleModel.test.ts @@ -94,9 +94,9 @@ describe('schedule model', () => { expect(summary).toMatchObject({ title: '巡检 xdt-maker', subtitle: '上次 1 小时前 · 2 小时后', - detail: 'cron 0 9 * * * · 新会话 · Claude · xdt-maker', + detail: 'cron 0 9 * * * · 新任务 · Claude · xdt-maker', runSessionDetail: null, - runSessionLabel: '新会话', + runSessionLabel: '新任务', statusLabel: '运行中', unreadCount: 1, }); @@ -107,9 +107,9 @@ describe('schedule model', () => { persistentSession: true, targetSessionId: 'session-persistent-123', }), [], NOW)).toMatchObject({ - detail: 'cron 0 9 * * * · 持续会话 · Claude · xdt-maker', - runSessionDetail: '持续会话 session-', - runSessionLabel: '持续会话', + detail: 'cron 0 9 * * * · 持续任务 · Claude · xdt-maker', + runSessionDetail: '持续任务 session-', + runSessionLabel: '持续任务', }); expect(summarizeSchedule(schedule({ @@ -117,9 +117,9 @@ describe('schedule model', () => { workingDir: '', useWorktree: true, }), [], NOW)).toMatchObject({ - detail: 'cron 0 9 * * * · 绑定会话 · Claude · 未设置目录', + detail: 'cron 0 9 * * * · 绑定任务 · Claude · 未设置目录', runSessionDetail: '绑定到 session-', - runSessionLabel: '绑定会话', + runSessionLabel: '绑定任务', }); }); @@ -168,7 +168,7 @@ describe('schedule model', () => { expect(summarizeRun(runs[1], NOW)).toMatchObject({ title: '失败', detail: 'boom', - meta: '耗时未知 · 未创建会话', + meta: '耗时未知 · 未创建任务', canDelete: true, canMarkRead: true, canOpenSession: false, @@ -186,9 +186,9 @@ describe('schedule model', () => { }), NOW)).toMatchObject({ title: '执行中', detail: null, - meta: '已运行 1 分 30 秒 · 会话 session-', + meta: '已运行 1 分 30 秒 · 任务 session-', openSessionLabel: '打开', - sessionDetail: '会话 session-', + sessionDetail: '任务 session-', canDelete: false, canMarkRead: false, canOpenSession: true, diff --git a/apps/mobile/src/__tests__/sessionComposerLayout.test.ts b/apps/mobile/src/__tests__/sessionComposerLayout.test.ts index 44a081e5621..a6e2042e984 100644 --- a/apps/mobile/src/__tests__/sessionComposerLayout.test.ts +++ b/apps/mobile/src/__tests__/sessionComposerLayout.test.ts @@ -276,7 +276,7 @@ describe('sessionComposerLayout', () => { label: '停止', visible: true, }); - expect(runningWithDraft.guidanceText).toBe('点发送后会进入桌面端队列,按当前会话设置执行。'); + expect(runningWithDraft.guidanceText).toBe('点发送后会进入桌面端队列,按当前任务设置执行。'); }); it('marks busy operations without changing the primary layout shape', () => { @@ -400,6 +400,6 @@ describe('sessionComposerLayout', () => { queueBusy: false, sending: false, voiceState: 'idle', - }).guidanceText).toBe('点发送后会进入桌面端队列,按当前会话设置执行。'); + }).guidanceText).toBe('点发送后会进入桌面端队列,按当前任务设置执行。'); }); }); diff --git a/apps/mobile/src/__tests__/sessionControls.test.ts b/apps/mobile/src/__tests__/sessionControls.test.ts index 51f4a7fbe7d..1136083fc6a 100644 --- a/apps/mobile/src/__tests__/sessionControls.test.ts +++ b/apps/mobile/src/__tests__/sessionControls.test.ts @@ -165,13 +165,13 @@ describe('sessionControls', () => { contextWindow: 200000, }))).toEqual({ title: 'Session spend', - detail: '本会话 $0.03 · 12.5k tokens · 上下文 6k / 200k · 3%', + detail: '本任务 $0.03 · 12.5k tokens · 上下文 6k / 200k · 3%', available: true, }); expect(summarizeSessionSpend(session())).toEqual({ title: 'Session spend', - detail: '暂无会话用量', + detail: '暂无任务用量', available: false, }); }); diff --git a/apps/mobile/src/__tests__/sessionList.test.ts b/apps/mobile/src/__tests__/sessionList.test.ts index 7ca3490aa6f..fb8eca8879c 100644 --- a/apps/mobile/src/__tests__/sessionList.test.ts +++ b/apps/mobile/src/__tests__/sessionList.test.ts @@ -101,7 +101,7 @@ describe('sessionList', () => { }), new Date('2026-01-01T00:10:00.000Z').getTime()); expect(item).toMatchObject({ - title: '未命名会话', + title: '未命名任务', subtitle: 'Codex · gpt-5.4 · dialogue', detail: '活跃 · 5 分钟前 · 12 条消息', messagePreview: null, @@ -245,8 +245,8 @@ describe('sessionList', () => { const group = rows.find((item) => item.automationGroup); expect(group).toMatchObject({ title: '移动端巡检', - subtitle: '自动化任务 · 2 个会话 · Claude Code · claude-sonnet-4-6', - detail: '2 个会话 · 4 分钟前 · 自动化执行中 · 1 个自动化未读', + subtitle: '自动化 · 2 个任务 · Claude Code · claude-sonnet-4-6', + detail: '2 个任务 · 4 分钟前 · 自动化执行中 · 1 个自动化未读', automationGroup: { key: 'schedule:sched-1', sessionIds: ['running', 'old'], @@ -443,7 +443,7 @@ describe('sessionList', () => { statusFilter: 'active', })).toMatchObject({ title: '搜索结果', - detail: '1 个匹配会话 · 活跃 2 · 项目分组', + detail: '1 个匹配任务 · 活跃 2 · 项目分组', hint: '搜索范围包含标题、项目路径、模型、自动化名称和消息预览。', resultCount: 1, rowCount: 1, @@ -474,9 +474,9 @@ describe('sessionList', () => { sections: automationSections, statusFilter: 'automation', })).toMatchObject({ - title: '自动化会话', - detail: '2 个会话 · 1 行 · 自动化 2 · 项目分组', - hint: '自动化会话会按计划聚合,展开后可以进入单次运行。', + title: '自动化生成的任务', + detail: '2 个任务 · 1 行 · 自动化 2 · 项目分组', + hint: '自动化生成的任务会按计划聚合,展开后可以进入单次运行。', resultCount: 2, rowCount: 1, }); @@ -484,19 +484,19 @@ describe('sessionList', () => { it('keeps mobile device-detail empty states specific to search and filters', () => { expect(deviceSessionEmptyState('active', 'billing')).toMatchObject({ - title: '没有匹配的会话', + title: '没有匹配的任务', }); expect(deviceSessionEmptyState('waiting', '')).toMatchObject({ title: '没有待处理请求', }); expect(deviceSessionEmptyState('automation', '')).toMatchObject({ - title: '没有自动化会话', + title: '没有自动化生成的任务', }); expect(deviceSessionEmptyState('archived', '')).toMatchObject({ - title: '没有归档会话', + title: '没有归档任务', }); expect(deviceSessionEmptyState('active', '')).toMatchObject({ - title: '这台电脑暂无活动会话', + title: '这台电脑暂无活动任务', }); }); diff --git a/apps/mobile/src/__tests__/sessionMainLayerDesktopFirst.test.ts b/apps/mobile/src/__tests__/sessionMainLayerDesktopFirst.test.ts index 02938b18f59..24ad1030dc3 100644 --- a/apps/mobile/src/__tests__/sessionMainLayerDesktopFirst.test.ts +++ b/apps/mobile/src/__tests__/sessionMainLayerDesktopFirst.test.ts @@ -11,7 +11,7 @@ describe('mobile session main layer desktop-first noise budget', () => { expect(emptySource).toContain('message.renderer.emptyMessages'); expect(emptySource).not.toContain('这台电脑暂无活动消息'); - expect(emptySource).not.toContain('先在桌面端创建或继续一个会话'); + expect(emptySource).not.toContain('先在桌面端创建或继续一个任务'); expect(emptySource).not.toContain('emptyText'); }); diff --git a/apps/mobile/src/__tests__/sessionMenu.test.ts b/apps/mobile/src/__tests__/sessionMenu.test.ts index 57f5d39ffc2..3a3f27eee59 100644 --- a/apps/mobile/src/__tests__/sessionMenu.test.ts +++ b/apps/mobile/src/__tests__/sessionMenu.test.ts @@ -59,13 +59,13 @@ describe('sessionMenu header', () => { it('falls back to workspace name when the session has no title', () => { expect(buildSessionMenuHeader(session({ title: '' }), {}).title).toBe('xdt-maker'); - expect(buildSessionMenuHeader(session({ title: '', workingDir: null }), {}).title).toBe('远程对话'); + expect(buildSessionMenuHeader(session({ title: '', workingDir: null }), {}).title).toBe('远程任务'); }); it('未起名会话显示本地化兜底,不回落工作目录名', () => { // 哨兵若按「无标题」处理会回落 workspaceName 显示目录名,与 desktop 的 - // 「未命名对话」不一致(PR #1031 review P1)。 - expect(buildSessionMenuHeader(session({ title: 'New Maker' }), {}).title).toBe('未命名对话'); + // 「未命名任务」不一致(PR #1031 review P1)。 + expect(buildSessionMenuHeader(session({ title: 'New Maker' }), {}).title).toBe('未命名任务'); }); it('prefers the worktree name in the meta line', () => { @@ -100,7 +100,7 @@ describe('sessionMenu actions', () => { writeDisabled: false, }); expect(actions.map((action) => action.id)).toEqual(['rename', 'copyLink', 'pin', 'archive', 'delete']); - expect(actions.find((action) => action.id === 'copyLink')?.label).toBe('复制对话链接'); + expect(actions.find((action) => action.id === 'copyLink')?.label).toBe('复制任务链接'); expect(actions.find((action) => action.id === 'pin')?.label).toBe('置顶'); expect(actions.find((action) => action.id === 'archive')?.testID).toBe('session.archiveButton'); expect(actions.find((action) => action.id === 'delete')?.tone).toBe('danger'); diff --git a/apps/mobile/src/__tests__/sessionOperationLayout.test.ts b/apps/mobile/src/__tests__/sessionOperationLayout.test.ts index dab82a83199..0af5f3b084f 100644 --- a/apps/mobile/src/__tests__/sessionOperationLayout.test.ts +++ b/apps/mobile/src/__tests__/sessionOperationLayout.test.ts @@ -10,7 +10,7 @@ describe('session operation layout', () => { hasActivePendingInteraction: true, })).toEqual({ canUseComposer: false, - composerDisabledReason: '当前会话还没有同步完成。', + composerDisabledReason: '当前任务还没有同步完成。', composerDisabledReasonSource: 'session-syncing', composerSlot: 'missing-session', messageHistoryMode: 'hidden', diff --git a/apps/mobile/src/__tests__/sessionOverview.test.ts b/apps/mobile/src/__tests__/sessionOverview.test.ts index 47bef077dad..f2ceab68bee 100644 --- a/apps/mobile/src/__tests__/sessionOverview.test.ts +++ b/apps/mobile/src/__tests__/sessionOverview.test.ts @@ -102,7 +102,7 @@ describe('sessionOverview', () => { }); expect(overview.attentionLabel).toBe('已归档'); - expect(overview.actionCopy).toBe('会话已归档,恢复后才能继续发送。'); + expect(overview.actionCopy).toBe('任务已归档,恢复后才能继续发送。'); expect(overview.stateChips).toEqual([ { id: 'status', label: '已归档', strong: true }, ]); diff --git a/apps/mobile/src/__tests__/sessionSelection.test.ts b/apps/mobile/src/__tests__/sessionSelection.test.ts index 58c724373b6..1a089bb8aba 100644 --- a/apps/mobile/src/__tests__/sessionSelection.test.ts +++ b/apps/mobile/src/__tests__/sessionSelection.test.ts @@ -92,7 +92,7 @@ describe('mobile session selection', () => { expect(summary.candidates.map((item) => item.id)).toEqual(['active']); expect(summary.skippedCount).toBe(2); - expect(summary.description).toContain('将跳过 2 个不适用的会话'); + expect(summary.description).toContain('将跳过 2 个不适用的任务'); expect(isMobileSessionBulkActionAvailable(summary)).toBe(true); expect(mobileSessionBulkActionButtonLabel(summary)).toBe('归档 1'); expect(mobileSessionBulkPatch('archive')).toEqual({ status: 'archived', pinnedAt: null }); @@ -119,7 +119,7 @@ describe('mobile session selection', () => { expect(summary.candidates.map((item) => item.id)).toEqual(['archived']); expect(summary.skippedCount).toBe(2); - expect(summary.description).toContain('将跳过 2 个不适用的会话'); + expect(summary.description).toContain('将跳过 2 个不适用的任务'); expect(mobileSessionBulkPatch('restore')).toEqual({ status: 'active' }); }); diff --git a/apps/mobile/src/__tests__/sessionSwipe.test.ts b/apps/mobile/src/__tests__/sessionSwipe.test.ts index 2b4e1443d34..08ae7e31ba1 100644 --- a/apps/mobile/src/__tests__/sessionSwipe.test.ts +++ b/apps/mobile/src/__tests__/sessionSwipe.test.ts @@ -446,7 +446,7 @@ describe('buildSessionActionMenu', () => { it('菜单顺序固定:重命名 → 置顶切换 → 归档 → 删除,删除标 destructive', () => { const menu = buildSessionActionMenu(null); expect(menu.map((item) => item.action)).toEqual(['rename', 'pin', 'archive', 'delete']); - expect(menu[3]).toMatchObject({ label: '删除对话', destructive: true }); + expect(menu[3]).toMatchObject({ label: '删除任务', destructive: true }); expect(menu.filter((item) => item.destructive)).toHaveLength(1); }); diff --git a/apps/mobile/src/__tests__/sessionTailBannerModel.test.ts b/apps/mobile/src/__tests__/sessionTailBannerModel.test.ts index c3e11ed6fa2..2653f6698f5 100644 --- a/apps/mobile/src/__tests__/sessionTailBannerModel.test.ts +++ b/apps/mobile/src/__tests__/sessionTailBannerModel.test.ts @@ -88,7 +88,7 @@ describe('resolveSessionTailBanner — error-tail', () => { })], })); expect(stale).toMatchObject({ kind: 'error-tail', retryable: false }); - expect((stale as { text: string }).text).toContain('新建对话'); + expect((stale as { text: string }).text).toContain('新建任务'); const encrypted = resolveSessionTailBanner(baseInput({ messages: [errorRow('e2', '2026-01-01T00:00:02.000Z', { diff --git a/apps/mobile/src/__tests__/sessionTitleProjection.test.ts b/apps/mobile/src/__tests__/sessionTitleProjection.test.ts index d1ccc72b8e0..a6773f1dfe2 100644 --- a/apps/mobile/src/__tests__/sessionTitleProjection.test.ts +++ b/apps/mobile/src/__tests__/sessionTitleProjection.test.ts @@ -1,10 +1,10 @@ /** - * 手机端「未起名会话标题」投影的出口清单。 + * 手机端「未起名任务标题」投影的出口清单。 * - * 不变量:用户能看到的每个会话标题出口都必须过 projectDraftSessionTitle,内部哨兵 + * 不变量:用户能看到的每个任务标题出口都必须过 projectDraftSessionTitle,内部哨兵 * `New Maker` 一处都不许原样渲染;而重命名输入框预填投影值后,**必须**有一条 * 「没改就不落库」的判据同时比原始标题与投影值 —— 否则用户双击后原样确定就会把兜底 - * 文案写进 DB,哨兵被毁、自动起名永久跳过该会话(PR #1031)。 + * 文案写进 DB,哨兵被毁、自动起名永久跳过该任务(PR #1031)。 * * 这些出口散在大型屏幕组件里(RN 组件在本仓没有渲染测试基座),用源码不变量钉住: * 定位精确、不依赖 UI 基座,后续谁改动这几行都会在这里失败。 diff --git a/apps/mobile/src/__tests__/systemCard.test.ts b/apps/mobile/src/__tests__/systemCard.test.ts index 5c0bf12f764..04958215fd3 100644 --- a/apps/mobile/src/__tests__/systemCard.test.ts +++ b/apps/mobile/src/__tests__/systemCard.test.ts @@ -58,10 +58,10 @@ describe('systemCard', () => { { kind: 'agent-builtin', name: 'doctor', description: 'remote doctor' }, ]).map((command) => [command.name, command.description])).toEqual([ ['help', '显示手机端和远程 agent 命令'], - ['context', '查看当前会话上下文用量'], - ['cost', '查看当前会话消耗'], + ['context', '查看当前任务上下文用量'], + ['cost', '查看当前任务消耗'], ['pwd', '显示当前远程工作目录'], - ['status', '显示当前会话状态'], + ['status', '显示当前任务状态'], ['doctor', 'remote doctor'], ]); }); @@ -143,10 +143,10 @@ describe('formatMobileSystemCard — goal 续跑卡按原因分说法', () => { describe('formatMobileSystemCard — 中断自动重连状态', () => { const info = { error: 'socket hang up', attempt: 2, maxAttempts: 5, sessionTotal: 3 }; - it('shows live progress, reason, current attempt, and conversation total', () => { + it('shows live progress, reason, current attempt, and session total', () => { expect(formatMobileSystemCard('auto-resume', { ...info, live: true })).toMatchObject({ title: 'Reconnecting 2/5…', - subtitle: 'Attempt 2/5 · 3 reconnects in this conversation', + subtitle: 'Attempt 2/5 · 3 reconnects in this session', body: 'socket hang up', }); }); diff --git a/apps/mobile/src/i18n/locales/en/devices.json b/apps/mobile/src/i18n/locales/en/devices.json index c781cebb224..1a875015a65 100644 --- a/apps/mobile/src/i18n/locales/en/devices.json +++ b/apps/mobile/src/i18n/locales/en/devices.json @@ -9,38 +9,38 @@ }, "list": { "thisComputer": "This device", - "allConversations": "All Conversations", + "allConversations": "All Sessions", "syncFailed": "Sync failed", "requestFailed": "Request failed", "loading": "Loading available devices", - "untitled": "Untitled conversation", + "untitled": "Untitled session", "connection": { "connecting": "Connecting", "disconnected": "Disconnected" }, "error": { - "sessionDeviceNotFound": "Can't find the device this conversation belongs to. Sync again and retry.", + "sessionDeviceNotFound": "Can't find the device this session belongs to. Sync again and retry.", "taskDeviceNotFound": "Can't find the device this task belongs to. Sync again and retry.", "noDevice": "No device available. Turn on remote control on the desktop, then sync again." }, "alert": { "actionFailed": "Action failed", "renameFailed": "Rename failed", - "deleteTitle": "Delete conversation?", + "deleteTitle": "Delete session?", "deleteMessage": "This deletes \"{{title}}\" and can't be undone." }, "a11y": { - "selectScope": "Select conversation scope", + "selectScope": "Select session scope", "openSettings": "Open settings", "syncing": "Syncing", "sync": "Sync again", - "pinnedConversations": "{{count}} pinned conversations", - "newRemoteConversation": "New remote conversation", + "pinnedConversations": "{{count}} pinned sessions", + "newRemoteConversation": "New remote session", "renameDevice": "Rename {{label}}", "project": "Project {{title}}", "openAutomationLatest": "Open the latest session of automation {{title}}", "automationTask": "Automation {{title}}", - "openConversation": "Open conversation {{title}}", + "openConversation": "Open session {{title}}", "collapse": "Collapse {{title}}", "expand": "Expand {{title}}", "taskError": "Task failed", @@ -60,17 +60,17 @@ "saveA11y": "Save device name" }, "renameSession": { - "title": "Rename Conversation", - "placeholder": "Conversation title", - "savingA11y": "Saving conversation title", - "saveA11y": "Save conversation title" + "title": "Rename Session", + "placeholder": "Session title", + "savingA11y": "Saving session title", + "saveA11y": "Save session title" }, "revoked": { "title": "Access revoked", "body": "\"{{deviceName}}\" revoked this device's remote access. Re-authorize this device in {{appName}} settings on that computer, then tap \"Retry Access.\"", "retry": "Retry Access" }, - "viewAllConversations": "View all {{count}} conversations", + "viewAllConversations": "View all {{count}} sessions", "viewAllRuns": "View all {{count}} runs", "preview": { "needAttention": "{{count}} need attention", @@ -86,7 +86,7 @@ "automation": "Automation", "archived": "Archived", "all": "All", - "a11y": "Filter {{label}} conversations" + "a11y": "Filter {{label}} sessions" }, "group": { "project": "Project", @@ -98,15 +98,15 @@ "active": "Active", "waiting": "Pending", "automation": "Automation", - "activeA11y": "Filter active conversations", - "waitingA11y": "Filter pending conversations", - "automationA11y": "Filter automation conversations" + "activeA11y": "Filter active sessions", + "waitingA11y": "Filter pending sessions", + "automationA11y": "Filter automation sessions" }, "plan": "Schedule", "automationsButtonA11y": "Open remote automations", "search": { - "openA11y": "Search remote conversations", - "closeA11y": "Close remote conversation search", + "openA11y": "Search remote sessions", + "closeA11y": "Close remote session search", "label": "Search", "placeholder": "Search titles, projects, models, messages", "clearA11y": "Clear search", @@ -115,15 +115,15 @@ "close": "Close" }, "filters": { - "collapseA11y": "Collapse conversation filters", - "expandA11y": "Expand conversation filters", + "collapseA11y": "Collapse session filters", + "expandA11y": "Expand session filters", "label": "Filter" }, "selection": { - "count": "{{count}} conversations selected", + "count": "{{count}} sessions selected", "canAct": "Actions available", "cannotAct": "No actions available", - "clearA11y": "Clear conversation selection" + "clearA11y": "Clear session selection" }, "bulk": { "pinning": "Pinning", @@ -131,19 +131,19 @@ "archiving": "Archiving", "restoring": "Restoring", "deleting": "Deleting", - "pinA11y": "Pin selected conversations", - "unpinA11y": "Unpin selected conversations", - "archiveA11y": "Archive selected conversations", - "restoreA11y": "Restore selected conversations", - "deleteA11y": "Delete selected conversations", + "pinA11y": "Pin selected sessions", + "unpinA11y": "Unpin selected sessions", + "archiveA11y": "Archive selected sessions", + "restoreA11y": "Restore selected sessions", + "deleteA11y": "Delete selected sessions", "cancelA11y": "Cancel bulk action", "partialFailure": "Processed {{done}}, {{failed}} failed (restored, you can retry).", - "noArchivable": "No archivable active conversations in the selection.", - "noPinnable": "No pinnable active conversations in the selection.", - "noRestorable": "No restorable archived conversations in the selection.", - "noUnpinnable": "No pinned conversations in the selection.", - "noDeletable": "No deletable conversations in the selection.", - "noneModifiable": "None of the selected conversations can be changed." + "noArchivable": "No archivable active sessions in the selection.", + "noPinnable": "No pinnable active sessions in the selection.", + "noRestorable": "No restorable archived sessions in the selection.", + "noUnpinnable": "No pinned sessions in the selection.", + "noDeletable": "No deletable sessions in the selection.", + "noneModifiable": "None of the selected sessions can be changed." }, "automationScope": { "eyebrow": "Automation", @@ -153,16 +153,16 @@ }, "projectScope": { "eyebrow": "Project", - "emptyCopy": "Start a new conversation in this project.", - "emptyTitle": "This project has no conversations yet" + "emptyCopy": "Start a new session in this project.", + "emptyTitle": "This project has no sessions yet" }, "subtitle": { - "deviceActive": "{{deviceName}} · {{count}} active conversations", - "activeAndProjects": "{{count}} active conversations · {{projects}} projects" + "deviceActive": "{{deviceName}} · {{count}} active sessions", + "activeAndProjects": "{{count}} active sessions · {{projects}} projects" }, "row": { "a11yAutomationGroup": "Automation group {{title}}", - "a11ySession": "Conversation {{title}}", + "a11ySession": "Session {{title}}", "a11yCollapse": "Collapse {{title}}", "a11yExpand": "Expand {{title}}", "collapse": "Collapse", @@ -171,20 +171,20 @@ "badge": { "waiting": "Pending {{count}}", "automationPrefix": "Automation", - "sessionCount": "{{count}} conversations", + "sessionCount": "{{count}} sessions", "aggregate": "Grouped", "running": "Running", "unread": "Unread {{count}}" }, "child": { - "openA11y": "Open automation conversation {{title}}" + "openA11y": "Open automation session {{title}}" }, "viewAllRuns": "View all {{count}} runs" }, "automations": { "error": { "projectMissingConfig": "This project automation is missing projectConfigId. Delete it on the desktop.", - "deletePartial": "Automation deleted, but {{count}} generated conversations couldn't be updated. Check them on the desktop." + "deletePartial": "Automation deleted, but {{count}} generated sessions couldn't be updated. Check them on the desktop." }, "subtitle": "{{active}} running · {{total}} tasks", "metric": { @@ -245,7 +245,7 @@ "intervalPlaceholder": "Leave empty to use cron", "workspace": { "project": "Project", - "dialogue": "Conversation" + "dialogue": "Chat" }, "useWorktree": "Use worktree", "modelPlaceholderBound": "Follow session", @@ -257,19 +257,19 @@ "cancelA11y": "Cancel editing automation" }, "delete": { - "counting": "Counting the conversations this automation generated…", - "confirmDeleteBoth": "Delete Task and Conversations", - "confirmArchive": "Delete Task and Archive Conversations", + "counting": "Counting the sessions this automation generated…", + "confirmDeleteBoth": "Delete Task and Sessions", + "confirmArchive": "Delete Task and Archive Sessions", "confirmTaskOnly": "Delete Task Only", "title": "Delete {{name}}", - "copy": "Deleting an automation task doesn't interrupt runs already in progress. Generated conversations are handled by the option below, matching the desktop.", - "optionsA11y": "Choose how to handle generated conversations", - "keepDesc": "Delete only this automation task; past conversations stay in their original list.", - "keepLabel": "Keep Generated Conversations", - "archiveDesc": "After deleting the task, move its generated conversations to the archive, still searchable later.", - "archiveLabel": "Archive Generated Conversations", - "deleteDesc": "After deleting the task, also mark its generated conversations as deleted. This can't be undone from your phone.", - "deleteLabel": "Delete Generated Conversations", + "copy": "Deleting an automation task doesn't interrupt runs already in progress. Generated sessions are handled by the option below, matching the desktop.", + "optionsA11y": "Choose how to handle generated sessions", + "keepDesc": "Delete only this automation task; past sessions stay in their original list.", + "keepLabel": "Keep Generated Sessions", + "archiveDesc": "After deleting the task, move its generated sessions to the archive, still searchable later.", + "archiveLabel": "Archive Generated Sessions", + "deleteDesc": "After deleting the task, also mark its generated sessions as deleted. This can't be undone from your phone.", + "deleteLabel": "Delete Generated Sessions", "cancelA11y": "Cancel deleting automation", "confirmA11y": "Confirm deleting automation" }, @@ -284,7 +284,7 @@ }, "runDelete": { "title": "Delete this run record", - "copy": "This deletes only this automation run record. It won't delete the conversations it opened, and it won't affect future automatic triggers.", + "copy": "This deletes only this automation run record. It won't delete the sessions it opened, and it won't affect future automatic triggers.", "confirmA11y": "Confirm deleting run record", "confirm": "Delete Record", "cancelA11y": "Cancel deleting run record" diff --git a/apps/mobile/src/i18n/locales/en/interaction.json b/apps/mobile/src/i18n/locales/en/interaction.json index 086dd45b14b..44e47c1e44d 100644 --- a/apps/mobile/src/i18n/locales/en/interaction.json +++ b/apps/mobile/src/i18n/locales/en/interaction.json @@ -115,7 +115,7 @@ }, "rewind": { "readError": "Couldn't read the rewind preview. Sync again and retry.", - "noFilesNote": "No files to roll back; only the conversation after this message will be truncated.", + "noFilesNote": "No files to roll back; only the chat after this message will be truncated.", "moreFiles": "{{count}} more files", "stats": "{{count}} files · +{{insertions}} / -{{deletions}} lines", "gotIt": "Got it", @@ -125,11 +125,11 @@ "confirming": "Rewinding", "titleLoading": "Checking rewind impact", "titleDefault": "Rewind to this message?", - "titleEmpty": "Rewind conversation only", + "titleEmpty": "Rewind chat only", "titleError": "Can't rewind", "detailLoading": "Reading the file rewind preview from your computer.", - "detailDefault": "This rolls back the files below and truncates the conversation after this message.", - "detailEmpty": "No files to roll back; confirming truncates only the conversation after this message.", + "detailDefault": "This rolls back the files below and truncates the chat after this message.", + "detailEmpty": "No files to roll back; confirming truncates only the chat after this message.", "detailError": "Your computer declined this rewind." }, "collab": { diff --git a/apps/mobile/src/i18n/locales/en/message.json b/apps/mobile/src/i18n/locales/en/message.json index d13ec8f5f69..cdb80cadd14 100644 --- a/apps/mobile/src/i18n/locales/en/message.json +++ b/apps/mobile/src/i18n/locales/en/message.json @@ -110,7 +110,7 @@ "mediaDeviceLinkHint": "The mobile app will request the remote device to upload the media via device-link before opening it.", "controlDelete": "Delete message", "controlRewind": "Rewind to here", - "controlFork": "Fork conversation", + "controlFork": "Fork session", "markdownDocAriaLabel": "Message body", "toolOutputTitle": "{{label}} output", "mermaidEmpty": "Empty Mermaid diagram.", @@ -178,7 +178,7 @@ "neutral": "Reconnect", "pending": "Reconnecting…", "pendingWithProgress": "Reconnecting {{attempt}}/{{total}}…", - "details": "Attempt {{attempt}}/{{total}} · {{count}} reconnects in this conversation" + "details": "Attempt {{attempt}}/{{total}} · {{count}} reconnects in this session" }, "modelLabel": "Model", "sessionLabel": "Session", diff --git a/apps/mobile/src/i18n/locales/en/session.json b/apps/mobile/src/i18n/locales/en/session.json index b938ccf8c96..176da259460 100644 --- a/apps/mobile/src/i18n/locales/en/session.json +++ b/apps/mobile/src/i18n/locales/en/session.json @@ -29,7 +29,7 @@ "networkReconnecting": "Reconnecting…", "modelBusyRetrying": "Model service busy — retrying…", "composerSyncing": "Syncing session data; you can send once it finishes.", - "composerSessionNotSynced": "This conversation has not finished syncing yet.", + "composerSessionNotSynced": "This session has not finished syncing yet.", "composerCreating": "Creating the session; you can keep sending after the first message goes out.", "nextAgentSwitch": "Next switches to {{agent}} · {{model}}", "createFailedTitle": "Session Creation Failed", @@ -39,7 +39,7 @@ "retry": "Retry", "firstMessageNotSent": "The first message wasn't sent; its content has been restored to the input box.", "attachmentsNotCarriedBack": "{{count}} attachment(s) couldn't be brought back — please add them again.", - "commandWaitsForSession": "The conversation is still being created — this command needs the session to be ready. Please try again shortly.", + "commandWaitsForSession": "The session is still being created — this command needs the session to be ready. Please try again shortly.", "locateMessageNotFound": "Couldn't find the message to jump to.", "shareNoLocalImage": "Couldn't get the local image file", "shareFailedTitle": "Share Failed", @@ -94,12 +94,16 @@ "readOnlyMode": "Read-only", "accessRevokedShort": "Access revoked", "sessionNotSynced": "Session not synced", - "conversationFallback": "Conversation", + "conversationFallback": "Session", "accessRevoked": "The device has revoked access", "syncing": "Syncing", "pendingCount": "{{num}} pending", "archived": "Archived", - "queuePausedNotice": "Queue paused" + "queuePausedNotice": "Queue paused", + "forkConfirmTitle": "Start a new session from here?", + "forkConfirmDescription": "A separate session will be created using the context at this point. The original session stays unchanged, and future messages in either session won't affect the other.", + "forkConfirm": "Start new session", + "forkCancel": "Cancel" }, "new": { "selectDevice": "Select a device", @@ -163,14 +167,14 @@ "chipReadOnly": "Read-only", "titleFallback": "Remote session", "unnamedTitle": "Untitled session", - "renameAction": "Rename Chat", - "copyLinkAction": "Copy Chat Link", + "renameAction": "Rename Session", + "copyLinkAction": "Copy Session Link", "unpin": "Unpin", "pin": "Pin", "restore": "Restore", "archive": "Archive", "deleteAction": "Delete Session", - "deleteConversation": "Delete Chat", + "deleteConversation": "Delete Session", "workingDir": "Working directory", "aiRenameUnsupported": "The device's version is too old to support auto-naming yet.", "aiRenameOffline": "The device is offline; try again later.", @@ -219,6 +223,13 @@ "cancelRename": "Cancel rename", "backToMenu": "Back to session menu" }, + "messageMenu": { + "fork": "Start a new session", + "addToChat": "Add to chat", + "copyLink": "Copy current message link", + "rewind": "Back to here", + "deleteOne": "Delete this message" + }, "tail": { "interrupted": "The app exited mid-task, so it was interrupted.", "continueTask": "Continue Task", @@ -229,7 +240,7 @@ }, "row": { "moreOptions": "More options", - "archiveConversation": "Archive chat", + "archiveConversation": "Archive session", "closeActionMenu": "Close session actions menu", "referenceAroundAnchor": "Around the link", "referenceRecent": "Recent messages", diff --git a/apps/mobile/src/i18n/locales/ja/devices.json b/apps/mobile/src/i18n/locales/ja/devices.json index ccbe1924f15..462a0976933 100644 --- a/apps/mobile/src/i18n/locales/ja/devices.json +++ b/apps/mobile/src/i18n/locales/ja/devices.json @@ -9,38 +9,38 @@ }, "list": { "thisComputer": "このデバイス", - "allConversations": "すべての会話", + "allConversations": "すべてのセッション", "syncFailed": "同期に失敗しました", "requestFailed": "リクエストに失敗しました", "loading": "操作可能なデバイスを読み込み中", - "untitled": "無題の会話", + "untitled": "無題のセッション", "connection": { "connecting": "接続中", "disconnected": "切断されました" }, "error": { - "sessionDeviceNotFound": "この会話が属するデバイスが見つかりません。再同期してからもう一度お試しください。", + "sessionDeviceNotFound": "このセッションが属するデバイスが見つかりません。再同期してからもう一度お試しください。", "taskDeviceNotFound": "このタスクが属するデバイスが見つかりません。再同期してからもう一度お試しください。", "noDevice": "利用できるデバイスがありません。デスクトップでリモート操作をオンにしてから、再同期してください。" }, "alert": { "actionFailed": "操作に失敗しました", "renameFailed": "名前の変更に失敗しました", - "deleteTitle": "会話を削除しますか?", + "deleteTitle": "セッションを削除しますか?", "deleteMessage": "「{{title}}」を削除します。削除すると元に戻せません。" }, "a11y": { - "selectScope": "会話の範囲を選択", + "selectScope": "セッションの範囲を選択", "openSettings": "設定を開く", "syncing": "再同期中", "sync": "再同期", - "pinnedConversations": "ピン留めした会話 {{count}} 件", - "newRemoteConversation": "新しいリモート会話", + "pinnedConversations": "ピン留めしたセッション {{count}} 件", + "newRemoteConversation": "新しいリモートセッション", "renameDevice": "{{label}} の名前を変更", "project": "プロジェクト {{title}}", "openAutomationLatest": "自動化タスク {{title}} の最新セッションを開く", "automationTask": "自動化タスク {{title}}", - "openConversation": "会話 {{title}} を開く", + "openConversation": "セッション {{title}} を開く", "collapse": "{{title}} を折りたたむ", "expand": "{{title}} を展開", "taskError": "タスクでエラー", @@ -60,17 +60,17 @@ "saveA11y": "デバイス名を保存" }, "renameSession": { - "title": "会話の名前を変更", - "placeholder": "会話のタイトル", - "savingA11y": "会話のタイトルを保存中", - "saveA11y": "会話のタイトルを保存" + "title": "セッションの名前を変更", + "placeholder": "セッションのタイトル", + "savingA11y": "セッションのタイトルを保存中", + "saveA11y": "セッションのタイトルを保存" }, "revoked": { "title": "アクセス権が取り消されました", "body": "「{{deviceName}}」がこの端末のリモートアクセスを取り消しました。そのパソコンの {{appName}} 設定でこの端末を再度承認し、「再試行」をタップしてください。", "retry": "再試行" }, - "viewAllConversations": "すべての会話 {{count}} 件を表示", + "viewAllConversations": "すべてのセッション {{count}} 件を表示", "viewAllRuns": "すべての実行 {{count}} 回を表示", "preview": { "needAttention": "要対応 {{count}} 件", @@ -86,7 +86,7 @@ "automation": "自動化", "archived": "アーカイブ", "all": "すべて", - "a11y": "{{label}} の会話で絞り込み" + "a11y": "{{label}} のセッションで絞り込み" }, "group": { "project": "プロジェクト", @@ -98,15 +98,15 @@ "active": "アクティブ", "waiting": "対応待ち", "automation": "自動化", - "activeA11y": "アクティブな会話で絞り込み", - "waitingA11y": "対応待ちの会話で絞り込み", - "automationA11y": "自動化の会話で絞り込み" + "activeA11y": "アクティブなセッションで絞り込み", + "waitingA11y": "対応待ちのセッションで絞り込み", + "automationA11y": "自動化のセッションで絞り込み" }, "plan": "スケジュール", "automationsButtonA11y": "リモート自動化を開く", "search": { - "openA11y": "リモート会話を検索", - "closeA11y": "リモート会話の検索を閉じる", + "openA11y": "リモートセッションを検索", + "closeA11y": "リモートセッションの検索を閉じる", "label": "検索", "placeholder": "タイトル、プロジェクト、モデル、メッセージを検索", "clearA11y": "検索をクリア", @@ -115,15 +115,15 @@ "close": "閉じる" }, "filters": { - "collapseA11y": "会話の絞り込みを折りたたむ", - "expandA11y": "会話の絞り込みを展開", + "collapseA11y": "セッションの絞り込みを折りたたむ", + "expandA11y": "セッションの絞り込みを展開", "label": "絞り込み" }, "selection": { - "count": "{{count}} 件の会話を選択中", + "count": "{{count}} 件のセッションを選択中", "canAct": "実行できる操作あり", "cannotAct": "実行できる操作なし", - "clearA11y": "会話の選択を解除" + "clearA11y": "セッションの選択を解除" }, "bulk": { "pinning": "ピン留め中", @@ -131,19 +131,19 @@ "archiving": "アーカイブ中", "restoring": "復元中", "deleting": "削除中", - "pinA11y": "選択した会話を一括でピン留め", - "unpinA11y": "選択した会話のピン留めを一括で解除", - "archiveA11y": "選択した会話を一括でアーカイブ", - "restoreA11y": "選択した会話を一括で復元", - "deleteA11y": "選択した会話を一括で削除", + "pinA11y": "選択したセッションを一括でピン留め", + "unpinA11y": "選択したセッションのピン留めを一括で解除", + "archiveA11y": "選択したセッションを一括でアーカイブ", + "restoreA11y": "選択したセッションを一括で復元", + "deleteA11y": "選択したセッションを一括で削除", "cancelA11y": "一括操作をキャンセル", "partialFailure": "{{done}} 件を処理し、{{failed}} 件が失敗しました(復元済みで再試行できます)。", - "noArchivable": "選択の中にアーカイブできるアクティブな会話がありません。", - "noPinnable": "選択の中にピン留めできるアクティブな会話がありません。", - "noRestorable": "選択の中に復元できるアーカイブ済み会話がありません。", - "noUnpinnable": "選択の中にピン留め済みの会話がありません。", - "noDeletable": "選択の中に削除できる会話がありません。", - "noneModifiable": "選択した会話はいずれも変更できません。" + "noArchivable": "選択の中にアーカイブできるアクティブなセッションがありません。", + "noPinnable": "選択の中にピン留めできるアクティブなセッションがありません。", + "noRestorable": "選択の中に復元できるアーカイブ済みセッションがありません。", + "noUnpinnable": "選択の中にピン留め済みのセッションがありません。", + "noDeletable": "選択の中に削除できるセッションがありません。", + "noneModifiable": "選択したセッションはいずれも変更できません。" }, "automationScope": { "eyebrow": "自動化タスク", @@ -153,16 +153,16 @@ }, "projectScope": { "eyebrow": "プロジェクト", - "emptyCopy": "このプロジェクトで新しい会話を始めましょう。", - "emptyTitle": "このプロジェクトにはまだ会話がありません" + "emptyCopy": "このプロジェクトで新しいセッションを始めましょう。", + "emptyTitle": "このプロジェクトにはまだセッションがありません" }, "subtitle": { - "deviceActive": "{{deviceName}} · アクティブな会話 {{count}} 件", - "activeAndProjects": "アクティブな会話 {{count}} 件 · プロジェクト {{projects}} 件" + "deviceActive": "{{deviceName}} · アクティブなセッション {{count}} 件", + "activeAndProjects": "アクティブなセッション {{count}} 件 · プロジェクト {{projects}} 件" }, "row": { "a11yAutomationGroup": "自動化会話グループ {{title}}", - "a11ySession": "会話 {{title}}", + "a11ySession": "セッション {{title}}", "a11yCollapse": "{{title}} を折りたたむ", "a11yExpand": "{{title}} を展開", "collapse": "折りたたむ", @@ -171,20 +171,20 @@ "badge": { "waiting": "対応待ち {{count}}", "automationPrefix": "自動化", - "sessionCount": "{{count}} 件の会話", + "sessionCount": "{{count}} 件のセッション", "aggregate": "集約", "running": "実行中", "unread": "未読 {{count}}" }, "child": { - "openA11y": "自動化会話 {{title}} を開く" + "openA11y": "自動化セッション {{title}} を開く" }, "viewAllRuns": "すべての実行 {{count}} 回を表示" }, "automations": { "error": { "projectMissingConfig": "プロジェクト自動化に projectConfigId がありません。デスクトップで削除してください。", - "deletePartial": "自動化を削除しましたが、{{count}} 件の生成された会話の状態を更新できませんでした。デスクトップでこれらの会話を確認してください。" + "deletePartial": "自動化を削除しましたが、{{count}} 件の生成されたセッションの状態を更新できませんでした。デスクトップでこれらのセッションを確認してください。" }, "subtitle": "実行中 {{active}} 件 · タスク {{total}} 件", "metric": { @@ -257,19 +257,19 @@ "cancelA11y": "自動化の編集をキャンセル" }, "delete": { - "counting": "この自動化が生成した会話を集計中…", - "confirmDeleteBoth": "タスクと会話を削除", - "confirmArchive": "タスクを削除して会話をアーカイブ", + "counting": "この自動化が生成したセッションを集計中…", + "confirmDeleteBoth": "タスクとセッションを削除", + "confirmArchive": "タスクを削除してセッションをアーカイブ", "confirmTaskOnly": "タスクのみ削除", "title": "{{name}} を削除", - "copy": "自動化タスクを削除しても、すでに開始された実行は中断されません。生成された会話は下のオプションに従って処理され、デスクトップと同じ挙動になります。", - "optionsA11y": "生成された会話の処理方法を選択", - "keepDesc": "この自動化タスクだけを削除し、過去の会話は元のリストに残します。", - "keepLabel": "生成された会話を残す", - "archiveDesc": "タスクを削除した後、生成された会話をアーカイブに移動します。後で検索できます。", - "archiveLabel": "生成された会話をアーカイブ", - "deleteDesc": "タスクを削除した後、生成された会話も削除済みとして扱います。この操作はスマホからは元に戻せません。", - "deleteLabel": "生成された会話を削除", + "copy": "自動化タスクを削除しても、すでに開始された実行は中断されません。生成されたセッションは下のオプションに従って処理され、デスクトップと同じ挙動になります。", + "optionsA11y": "生成されたセッションの処理方法を選択", + "keepDesc": "この自動化タスクだけを削除し、過去のセッションは元のリストに残します。", + "keepLabel": "生成されたセッションを残す", + "archiveDesc": "タスクを削除した後、生成されたセッションをアーカイブに移動します。後で検索できます。", + "archiveLabel": "生成されたセッションをアーカイブ", + "deleteDesc": "タスクを削除した後、生成されたセッションも削除済みとして扱います。この操作はスマホからは元に戻せません。", + "deleteLabel": "生成されたセッションを削除", "cancelA11y": "自動化の削除をキャンセル", "confirmA11y": "自動化の削除を確定" }, @@ -284,7 +284,7 @@ }, "runDelete": { "title": "この実行記録を削除", - "copy": "この自動化の実行記録のみを削除します。開いた会話は削除されず、以降の自動トリガーにも影響しません。", + "copy": "この自動化の実行記録のみを削除します。開いたセッションは削除されず、以降の自動トリガーにも影響しません。", "confirmA11y": "実行記録の削除を確定", "confirm": "記録を削除", "cancelA11y": "実行記録の削除をキャンセル" diff --git a/apps/mobile/src/i18n/locales/ja/message.json b/apps/mobile/src/i18n/locales/ja/message.json index 1d9ffef8f91..a71138d1b8b 100644 --- a/apps/mobile/src/i18n/locales/ja/message.json +++ b/apps/mobile/src/i18n/locales/ja/message.json @@ -110,7 +110,7 @@ "mediaDeviceLinkHint": "モバイルは device-link 経由でリモートデバイスにメディアのアップロードを要求してから開きます。", "controlDelete": "メッセージを削除", "controlRewind": "ここまで戻す", - "controlFork": "会話を分岐", + "controlFork": "セッションを分岐", "markdownDocAriaLabel": "メッセージ本文", "toolOutputTitle": "{{label}} の出力", "mermaidEmpty": "空の Mermaid 図。", @@ -178,7 +178,7 @@ "neutral": "再接続", "pending": "再接続中…", "pendingWithProgress": "再接続中 {{attempt}}/{{total}}…", - "details": "今回の再試行 {{attempt}}/{{total}} · この会話での再接続 {{count}} 回" + "details": "今回の再試行 {{attempt}}/{{total}} · このセッションでの再接続 {{count}} 回" }, "modelLabel": "モデル", "sessionLabel": "セッション", diff --git a/apps/mobile/src/i18n/locales/ja/session.json b/apps/mobile/src/i18n/locales/ja/session.json index 1d5034c0721..7eb51645fb1 100644 --- a/apps/mobile/src/i18n/locales/ja/session.json +++ b/apps/mobile/src/i18n/locales/ja/session.json @@ -29,7 +29,7 @@ "networkReconnecting": "再接続中…", "modelBusyRetrying": "モデルサービスが混雑、再試行中…", "composerSyncing": "セッションデータを同期中です。完了すると送信できます。", - "composerSessionNotSynced": "この会話はまだ同期が完了していません。", + "composerSessionNotSynced": "このセッションはまだ同期が完了していません。", "composerCreating": "セッションを作成中です。最初のメッセージ送信後に続けて送信できます。", "nextAgentSwitch": "次から {{agent}} · {{model}} に切り替え", "createFailedTitle": "セッションの作成に失敗しました", @@ -39,7 +39,7 @@ "retry": "再試行", "firstMessageNotSent": "最初のメッセージは送信されませんでした。内容は入力欄に復元されました。", "attachmentsNotCarriedBack": "{{count}} 件の添付は復元できませんでした。もう一度追加してください。", - "commandWaitsForSession": "会話の作成中です。このコマンドはセッションの準備が必要なため、少し待ってから送信してください。", + "commandWaitsForSession": "セッションの作成中です。このコマンドはセッションの準備が必要なため、少し待ってから送信してください。", "locateMessageNotFound": "移動先のメッセージが見つかりませんでした。", "shareNoLocalImage": "ローカルの画像ファイルを取得できませんでした", "shareFailedTitle": "共有に失敗しました", @@ -94,12 +94,16 @@ "readOnlyMode": "読み取り専用", "accessRevokedShort": "アクセスは取り消されました", "sessionNotSynced": "セッション未同期", - "conversationFallback": "会話", + "conversationFallback": "セッション", "accessRevoked": "デバイスによってアクセス権が取り消されました", "syncing": "同期中", "pendingCount": "保留中 {{num}} 件", "archived": "アーカイブ済み", - "queuePausedNotice": "キュー一時停止中" + "queuePausedNotice": "キュー一時停止中", + "forkConfirmTitle": "ここから新しいセッションを始めますか?", + "forkConfirmDescription": "この時点の会話コンテキストを使って、独立した新しいセッションを作成します。元のセッションは変更されず、以後のメッセージは互いに影響しません。", + "forkConfirm": "新しいセッションを始める", + "forkCancel": "キャンセル" }, "new": { "selectDevice": "デバイスを選択", @@ -162,15 +166,15 @@ "chipArchived": "アーカイブ済み", "chipReadOnly": "読み取り専用", "titleFallback": "リモートセッション", - "unnamedTitle": "名称未設定の会話", - "renameAction": "会話の名前を変更", - "copyLinkAction": "会話リンクをコピー", + "unnamedTitle": "名称未設定のセッション", + "renameAction": "セッションの名前を変更", + "copyLinkAction": "セッションリンクをコピー", "unpin": "ピン留めを解除", "pin": "ピン留め", "restore": "復元", "archive": "アーカイブ", "deleteAction": "セッションを削除", - "deleteConversation": "会話を削除", + "deleteConversation": "セッションを削除", "workingDir": "作業ディレクトリ", "aiRenameUnsupported": "デバイス側のバージョンが古く、自動命名にまだ対応していません。", "aiRenameOffline": "デバイスがオフラインです。後で再試行してください。", @@ -219,6 +223,13 @@ "cancelRename": "名前の変更をキャンセル", "backToMenu": "セッションメニューに戻る" }, + "messageMenu": { + "fork": "新しいセッションで続ける", + "addToChat": "会話に追加", + "copyLink": "現在のメッセージリンクをコピー", + "rewind": "ここまで戻る", + "deleteOne": "このメッセージを削除" + }, "tail": { "interrupted": "タスクの実行中にアプリが終了したため、中断されました。", "continueTask": "タスクを続ける", @@ -229,7 +240,7 @@ }, "row": { "moreOptions": "その他のオプション", - "archiveConversation": "会話をアーカイブ", + "archiveConversation": "セッションをアーカイブ", "closeActionMenu": "セッション操作メニューを閉じる", "referenceAroundAnchor": "リンク付近", "referenceRecent": "最近のメッセージ", diff --git a/apps/mobile/src/i18n/locales/ko/devices.json b/apps/mobile/src/i18n/locales/ko/devices.json index 53284831e7e..b355d5c1682 100644 --- a/apps/mobile/src/i18n/locales/ko/devices.json +++ b/apps/mobile/src/i18n/locales/ko/devices.json @@ -9,38 +9,38 @@ }, "list": { "thisComputer": "이 기기", - "allConversations": "모든 대화", + "allConversations": "모든 세션", "syncFailed": "동기화 실패", "requestFailed": "요청 실패", "loading": "제어 가능한 기기를 불러오는 중", - "untitled": "제목 없는 대화", + "untitled": "제목 없는 세션", "connection": { "connecting": "연결 중", "disconnected": "연결 끊김" }, "error": { - "sessionDeviceNotFound": "이 대화가 속한 기기를 찾을 수 없습니다. 다시 동기화한 후 시도하세요.", + "sessionDeviceNotFound": "이 세션이 속한 기기를 찾을 수 없습니다. 다시 동기화한 후 시도하세요.", "taskDeviceNotFound": "이 작업이 속한 기기를 찾을 수 없습니다. 다시 동기화한 후 시도하세요.", "noDevice": "사용할 수 있는 기기가 없습니다. 데스크톱에서 원격 제어를 켠 후 다시 동기화하세요." }, "alert": { "actionFailed": "작업 실패", "renameFailed": "이름 변경 실패", - "deleteTitle": "대화를 삭제할까요?", + "deleteTitle": "세션을 삭제할까요?", "deleteMessage": "「{{title}}」을(를) 삭제합니다. 삭제하면 복구할 수 없습니다." }, "a11y": { - "selectScope": "대화 범위 선택", + "selectScope": "세션 범위 선택", "openSettings": "설정 열기", "syncing": "다시 동기화하는 중", "sync": "다시 동기화", - "pinnedConversations": "고정된 대화 {{count}}개", - "newRemoteConversation": "새 원격 대화", + "pinnedConversations": "고정된 세션 {{count}}개", + "newRemoteConversation": "새 원격 세션", "renameDevice": "{{label}} 이름 변경", "project": "프로젝트 {{title}}", "openAutomationLatest": "자동화 작업 {{title}}의 최근 세션 열기", "automationTask": "자동화 작업 {{title}}", - "openConversation": "대화 {{title}} 열기", + "openConversation": "세션 {{title}} 열기", "collapse": "{{title}} 접기", "expand": "{{title}} 펼치기", "taskError": "작업 오류", @@ -60,17 +60,17 @@ "saveA11y": "기기 이름 저장" }, "renameSession": { - "title": "대화 이름 변경", - "placeholder": "대화 제목", - "savingA11y": "대화 제목 저장 중", - "saveA11y": "대화 제목 저장" + "title": "세션 이름 변경", + "placeholder": "세션 제목", + "savingA11y": "세션 제목 저장 중", + "saveA11y": "세션 제목 저장" }, "revoked": { "title": "액세스 권한이 취소됨", "body": "「{{deviceName}}」이(가) 이 기기의 원격 액세스를 취소했습니다. 해당 컴퓨터의 {{appName}} 설정에서 이 기기를 다시 승인한 후 「다시 시도」를 누르세요.", "retry": "다시 시도" }, - "viewAllConversations": "대화 {{count}}개 모두 보기", + "viewAllConversations": "세션 {{count}}개 모두 보기", "viewAllRuns": "실행 {{count}}회 모두 보기", "preview": { "needAttention": "확인 필요 {{count}}개", @@ -86,7 +86,7 @@ "automation": "자동화", "archived": "보관됨", "all": "전체", - "a11y": "{{label}} 대화로 필터링" + "a11y": "{{label}} 세션으로 필터링" }, "group": { "project": "프로젝트", @@ -98,15 +98,15 @@ "active": "활동", "waiting": "대기 중", "automation": "자동화", - "activeA11y": "활동 중인 대화로 필터링", - "waitingA11y": "대기 중인 대화로 필터링", - "automationA11y": "자동화 대화로 필터링" + "activeA11y": "활동 중인 세션으로 필터링", + "waitingA11y": "대기 중인 세션으로 필터링", + "automationA11y": "자동화 세션으로 필터링" }, "plan": "스케줄", "automationsButtonA11y": "원격 자동화 열기", "search": { - "openA11y": "원격 대화 검색", - "closeA11y": "원격 대화 검색 닫기", + "openA11y": "원격 세션 검색", + "closeA11y": "원격 세션 검색 닫기", "label": "검색", "placeholder": "제목, 프로젝트, 모델, 메시지 검색", "clearA11y": "검색 지우기", @@ -115,15 +115,15 @@ "close": "닫기" }, "filters": { - "collapseA11y": "대화 필터 접기", - "expandA11y": "대화 필터 펼치기", + "collapseA11y": "세션 필터 접기", + "expandA11y": "세션 필터 펼치기", "label": "필터" }, "selection": { - "count": "대화 {{count}}개 선택됨", + "count": "세션 {{count}}개 선택됨", "canAct": "실행 가능한 작업 있음", "cannotAct": "실행 가능한 작업 없음", - "clearA11y": "대화 선택 해제" + "clearA11y": "세션 선택 해제" }, "bulk": { "pinning": "고정 중", @@ -131,19 +131,19 @@ "archiving": "보관 중", "restoring": "복원 중", "deleting": "삭제 중", - "pinA11y": "선택한 대화 일괄 고정", - "unpinA11y": "선택한 대화 고정 일괄 해제", - "archiveA11y": "선택한 대화 일괄 보관", - "restoreA11y": "선택한 대화 일괄 복원", - "deleteA11y": "선택한 대화 일괄 삭제", + "pinA11y": "선택한 세션 일괄 고정", + "unpinA11y": "선택한 세션 고정 일괄 해제", + "archiveA11y": "선택한 세션 일괄 보관", + "restoreA11y": "선택한 세션 일괄 복원", + "deleteA11y": "선택한 세션 일괄 삭제", "cancelA11y": "일괄 작업 취소", "partialFailure": "{{done}}개를 처리했고 {{failed}}개가 실패했습니다(복원됨, 다시 시도할 수 있습니다).", - "noArchivable": "선택 항목에 보관할 수 있는 활성 대화가 없습니다.", - "noPinnable": "선택 항목에 고정할 수 있는 활성 대화가 없습니다.", - "noRestorable": "선택 항목에 복원할 수 있는 보관된 대화가 없습니다.", - "noUnpinnable": "선택 항목에 고정된 대화가 없습니다.", - "noDeletable": "선택 항목에 삭제할 수 있는 대화가 없습니다.", - "noneModifiable": "선택한 대화는 모두 변경할 수 없습니다." + "noArchivable": "선택 항목에 보관할 수 있는 활성 세션이 없습니다.", + "noPinnable": "선택 항목에 고정할 수 있는 활성 세션이 없습니다.", + "noRestorable": "선택 항목에 복원할 수 있는 보관된 세션이 없습니다.", + "noUnpinnable": "선택 항목에 고정된 세션이 없습니다.", + "noDeletable": "선택 항목에 삭제할 수 있는 세션이 없습니다.", + "noneModifiable": "선택한 세션은 모두 변경할 수 없습니다." }, "automationScope": { "eyebrow": "자동화 작업", @@ -153,16 +153,16 @@ }, "projectScope": { "eyebrow": "프로젝트", - "emptyCopy": "이 프로젝트에서 새 대화를 시작하세요.", - "emptyTitle": "이 프로젝트에는 아직 대화가 없습니다" + "emptyCopy": "이 프로젝트에서 새 세션을 시작하세요.", + "emptyTitle": "이 프로젝트에는 아직 세션이 없습니다" }, "subtitle": { - "deviceActive": "{{deviceName}} · 활동 중인 대화 {{count}}개", - "activeAndProjects": "활동 중인 대화 {{count}}개 · 프로젝트 {{projects}}개" + "deviceActive": "{{deviceName}} · 활동 중인 세션 {{count}}개", + "activeAndProjects": "활동 중인 세션 {{count}}개 · 프로젝트 {{projects}}개" }, "row": { "a11yAutomationGroup": "자동화 대화 그룹 {{title}}", - "a11ySession": "대화 {{title}}", + "a11ySession": "세션 {{title}}", "a11yCollapse": "{{title}} 접기", "a11yExpand": "{{title}} 펼치기", "collapse": "접기", @@ -171,20 +171,20 @@ "badge": { "waiting": "대기 중 {{count}}", "automationPrefix": "자동화", - "sessionCount": "대화 {{count}}개", + "sessionCount": "세션 {{count}}개", "aggregate": "집계", "running": "실행 중", "unread": "읽지 않음 {{count}}" }, "child": { - "openA11y": "자동화 대화 {{title}} 열기" + "openA11y": "자동화 세션 {{title}} 열기" }, "viewAllRuns": "실행 {{count}}회 모두 보기" }, "automations": { "error": { "projectMissingConfig": "프로젝트 자동화에 projectConfigId가 없습니다. 데스크톱에서 삭제하세요.", - "deletePartial": "자동화를 삭제했지만 생성된 대화 {{count}}개의 상태를 업데이트하지 못했습니다. 데스크톱에서 이 대화들을 확인하세요." + "deletePartial": "자동화를 삭제했지만 생성된 세션 {{count}}개의 상태를 업데이트하지 못했습니다. 데스크톱에서 이 세션들을 확인하세요." }, "subtitle": "실행 중 {{active}}개 · 작업 {{total}}개", "metric": { @@ -257,19 +257,19 @@ "cancelA11y": "자동화 편집 취소" }, "delete": { - "counting": "이 자동화가 생성한 대화를 집계하는 중…", - "confirmDeleteBoth": "작업과 대화 삭제", - "confirmArchive": "작업 삭제 후 대화 보관", + "counting": "이 자동화가 생성한 세션을 집계하는 중…", + "confirmDeleteBoth": "작업과 세션 삭제", + "confirmArchive": "작업 삭제 후 세션 보관", "confirmTaskOnly": "작업만 삭제", "title": "{{name}} 삭제", - "copy": "자동화 작업을 삭제해도 이미 시작된 실행은 중단되지 않습니다. 생성된 대화는 아래 옵션에 따라 처리되며 데스크톱과 동일하게 동작합니다.", - "optionsA11y": "생성된 대화 처리 방식 선택", - "keepDesc": "이 자동화 작업만 삭제하고, 이전 대화는 원래 목록에 그대로 둡니다.", - "keepLabel": "생성된 대화 유지", - "archiveDesc": "작업을 삭제한 후 생성된 대화를 보관함으로 옮깁니다. 이후에도 검색할 수 있습니다.", - "archiveLabel": "생성된 대화 보관", - "deleteDesc": "작업을 삭제한 후 생성된 대화도 삭제됨으로 표시합니다. 이 작업은 휴대폰에서 되돌릴 수 없습니다.", - "deleteLabel": "생성된 대화 삭제", + "copy": "자동화 작업을 삭제해도 이미 시작된 실행은 중단되지 않습니다. 생성된 세션은 아래 옵션에 따라 처리되며 데스크톱과 동일하게 동작합니다.", + "optionsA11y": "생성된 세션 처리 방식 선택", + "keepDesc": "이 자동화 작업만 삭제하고, 이전 세션은 원래 목록에 그대로 둡니다.", + "keepLabel": "생성된 세션 유지", + "archiveDesc": "작업을 삭제한 후 생성된 세션을 보관함으로 옮깁니다. 이후에도 검색할 수 있습니다.", + "archiveLabel": "생성된 세션 보관", + "deleteDesc": "작업을 삭제한 후 생성된 세션도 삭제됨으로 표시합니다. 이 작업은 휴대폰에서 되돌릴 수 없습니다.", + "deleteLabel": "생성된 세션 삭제", "cancelA11y": "자동화 삭제 취소", "confirmA11y": "자동화 삭제 확인" }, @@ -284,7 +284,7 @@ }, "runDelete": { "title": "이 실행 기록 삭제", - "copy": "이 자동화 실행 기록만 삭제합니다. 열었던 대화는 삭제되지 않으며 이후 자동 트리거에도 영향을 주지 않습니다.", + "copy": "이 자동화 실행 기록만 삭제합니다. 열었던 세션은 삭제되지 않으며 이후 자동 트리거에도 영향을 주지 않습니다.", "confirmA11y": "실행 기록 삭제 확인", "confirm": "기록 삭제", "cancelA11y": "실행 기록 삭제 취소" diff --git a/apps/mobile/src/i18n/locales/ko/message.json b/apps/mobile/src/i18n/locales/ko/message.json index 7330bcc4a7a..aabe3ec70df 100644 --- a/apps/mobile/src/i18n/locales/ko/message.json +++ b/apps/mobile/src/i18n/locales/ko/message.json @@ -110,7 +110,7 @@ "mediaDeviceLinkHint": "모바일은 device-link를 통해 원격 기기에 미디어 업로드를 요청한 후 엽니다.", "controlDelete": "메시지 삭제", "controlRewind": "여기로 되돌리기", - "controlFork": "대화 분기", + "controlFork": "세션 분기", "markdownDocAriaLabel": "메시지 본문", "toolOutputTitle": "{{label}} 출력", "mermaidEmpty": "빈 Mermaid 다이어그램.", @@ -178,7 +178,7 @@ "neutral": "다시 연결", "pending": "다시 연결하는 중…", "pendingWithProgress": "다시 연결하는 중 {{attempt}}/{{total}}…", - "details": "이번 재시도 {{attempt}}/{{total}} · 이 대화에서 재연결 {{count}}회" + "details": "이번 재시도 {{attempt}}/{{total}} · 이 세션에서 재연결 {{count}}회" }, "modelLabel": "모델", "sessionLabel": "세션", diff --git a/apps/mobile/src/i18n/locales/ko/session.json b/apps/mobile/src/i18n/locales/ko/session.json index b245d16b8ac..da51c3e810a 100644 --- a/apps/mobile/src/i18n/locales/ko/session.json +++ b/apps/mobile/src/i18n/locales/ko/session.json @@ -29,7 +29,7 @@ "networkReconnecting": "다시 연결하는 중…", "modelBusyRetrying": "모델 서비스 혼잡, 다시 시도 중…", "composerSyncing": "세션 데이터를 동기화 중입니다. 완료되면 보낼 수 있습니다.", - "composerSessionNotSynced": "이 대화는 아직 동기화가 끝나지 않았습니다.", + "composerSessionNotSynced": "이 세션은 아직 동기화가 끝나지 않았습니다.", "composerCreating": "세션을 만드는 중입니다. 첫 메시지가 전송되면 계속 보낼 수 있습니다.", "nextAgentSwitch": "다음부터 {{agent}} · {{model}}(으)로 전환", "createFailedTitle": "세션 생성 실패", @@ -39,7 +39,7 @@ "retry": "다시 시도", "firstMessageNotSent": "첫 메시지가 전송되지 않았습니다. 내용이 입력창에 복원되었습니다.", "attachmentsNotCarriedBack": "첨부 {{count}}개는 되돌리지 못했습니다. 다시 추가해 주세요.", - "commandWaitsForSession": "대화를 만들고 있습니다. 이 명령은 세션이 준비된 뒤에 사용할 수 있습니다. 잠시 후 다시 보내 주세요.", + "commandWaitsForSession": "세션을 만들고 있습니다. 이 명령은 세션이 준비된 뒤에 사용할 수 있습니다. 잠시 후 다시 보내 주세요.", "locateMessageNotFound": "이동할 메시지를 찾을 수 없습니다.", "shareNoLocalImage": "로컬 이미지 파일을 가져올 수 없습니다", "shareFailedTitle": "공유 실패", @@ -94,12 +94,16 @@ "readOnlyMode": "읽기 전용", "accessRevokedShort": "액세스 취소됨", "sessionNotSynced": "세션 미동기화", - "conversationFallback": "대화", + "conversationFallback": "세션", "accessRevoked": "기기에서 액세스 권한을 취소했습니다", "syncing": "동기화 중", "pendingCount": "대기 {{num}}건", "archived": "보관됨", - "queuePausedNotice": "대기열 일시 중지됨" + "queuePausedNotice": "대기열 일시 중지됨", + "forkConfirmTitle": "여기서 새 세션을 시작할까요?", + "forkConfirmDescription": "이 지점의 대화 맥락을 사용해 독립된 새 세션을 만듭니다. 원래 세션은 변경되지 않으며, 이후 두 세션의 메시지는 서로에게 영향을 주지 않습니다.", + "forkConfirm": "새 세션 시작", + "forkCancel": "취소" }, "new": { "selectDevice": "기기 선택", @@ -162,15 +166,15 @@ "chipArchived": "보관됨", "chipReadOnly": "읽기 전용", "titleFallback": "원격 세션", - "unnamedTitle": "이름 없는 대화", - "renameAction": "대화 이름 변경", - "copyLinkAction": "대화 링크 복사", + "unnamedTitle": "이름 없는 세션", + "renameAction": "세션 이름 변경", + "copyLinkAction": "세션 링크 복사", "unpin": "고정 해제", "pin": "고정", "restore": "복원", "archive": "보관", "deleteAction": "세션 삭제", - "deleteConversation": "대화 삭제", + "deleteConversation": "세션 삭제", "workingDir": "작업 디렉터리", "aiRenameUnsupported": "기기 버전이 오래되어 자동 이름 짓기를 아직 지원하지 않습니다.", "aiRenameOffline": "기기가 오프라인입니다. 나중에 다시 시도하세요.", @@ -219,6 +223,13 @@ "cancelRename": "이름 변경 취소", "backToMenu": "세션 메뉴로 돌아가기" }, + "messageMenu": { + "fork": "새 세션에서 계속하기", + "addToChat": "대화에 추가", + "copyLink": "현재 메시지 링크 복사", + "rewind": "여기로 돌아가기", + "deleteOne": "이 메시지 삭제" + }, "tail": { "interrupted": "작업 실행 도중 앱이 종료되어 중단되었습니다.", "continueTask": "작업 계속", @@ -229,7 +240,7 @@ }, "row": { "moreOptions": "기타 옵션", - "archiveConversation": "대화 보관", + "archiveConversation": "세션 보관", "closeActionMenu": "세션 작업 메뉴 닫기", "referenceAroundAnchor": "링크 부근", "referenceRecent": "최근 메시지", diff --git a/apps/mobile/src/i18n/locales/zh-CN/devices.json b/apps/mobile/src/i18n/locales/zh-CN/devices.json index 8071367a0b8..9985d4be2d1 100644 --- a/apps/mobile/src/i18n/locales/zh-CN/devices.json +++ b/apps/mobile/src/i18n/locales/zh-CN/devices.json @@ -9,38 +9,38 @@ }, "list": { "thisComputer": "这台设备", - "allConversations": "所有对话", + "allConversations": "所有任务", "syncFailed": "同步失败", "requestFailed": "请求失败", "loading": "正在读取可控制设备", - "untitled": "未命名对话", + "untitled": "未命名任务", "connection": { "connecting": "连接中", "disconnected": "连接断开" }, "error": { - "sessionDeviceNotFound": "找不到这个对话所属设备,请重新同步后再试。", + "sessionDeviceNotFound": "找不到这个任务所属设备,请重新同步后再试。", "taskDeviceNotFound": "找不到这个任务所属设备,请重新同步后再试。", "noDevice": "没有可用设备。请先打开桌面端远程控制,然后重新同步。" }, "alert": { "actionFailed": "操作失败", "renameFailed": "重命名失败", - "deleteTitle": "删除对话?", + "deleteTitle": "删除任务?", "deleteMessage": "将删除「{{title}}」,删除后不可恢复。" }, "a11y": { - "selectScope": "选择对话范围", + "selectScope": "选择任务范围", "openSettings": "打开设置", "syncing": "正在重新同步", "sync": "重新同步", - "pinnedConversations": "置顶 {{count}} 条对话", - "newRemoteConversation": "新建远程对话", + "pinnedConversations": "置顶 {{count}} 条任务", + "newRemoteConversation": "新建远程任务", "renameDevice": "重命名 {{label}}", "project": "项目 {{title}}", - "openAutomationLatest": "打开自动化任务 {{title}} 的最新对话", - "automationTask": "自动化任务 {{title}}", - "openConversation": "打开对话 {{title}}", + "openAutomationLatest": "打开自动化 {{title}} 的最新任务", + "automationTask": "自动化 {{title}}", + "openConversation": "打开任务 {{title}}", "collapse": "收起 {{title}}", "expand": "展开 {{title}}", "taskError": "任务出错", @@ -60,17 +60,17 @@ "saveA11y": "保存设备名" }, "renameSession": { - "title": "重命名对话", - "placeholder": "对话标题", - "savingA11y": "正在保存对话标题", - "saveA11y": "保存对话标题" + "title": "重命名任务", + "placeholder": "任务标题", + "savingA11y": "正在保存任务标题", + "saveA11y": "保存任务标题" }, "revoked": { "title": "已撤销访问权限", "body": "「{{deviceName}}」撤销了本机的远程访问权限。请在该电脑的 {{appName}} 设置里重新授权本设备,然后点「重试访问」。", "retry": "重试访问" }, - "viewAllConversations": "查看全部 {{count}} 条对话", + "viewAllConversations": "查看全部 {{count}} 条任务", "viewAllRuns": "查看全部 {{count}} 次运行", "preview": { "needAttention": "{{count}} 个需关注", @@ -86,7 +86,7 @@ "automation": "自动化", "archived": "归档", "all": "全部", - "a11y": "筛选{{label}}对话" + "a11y": "筛选{{label}}任务" }, "group": { "project": "项目", @@ -98,15 +98,15 @@ "active": "活跃", "waiting": "待处理", "automation": "自动化", - "activeA11y": "筛选活跃对话", - "waitingA11y": "筛选待处理对话", - "automationA11y": "筛选自动化对话" + "activeA11y": "筛选活跃任务", + "waitingA11y": "筛选待处理任务", + "automationA11y": "筛选自动化生成的任务" }, "plan": "计划", "automationsButtonA11y": "打开远程自动化", "search": { - "openA11y": "搜索远程对话", - "closeA11y": "关闭搜索远程对话", + "openA11y": "搜索远程任务", + "closeA11y": "关闭搜索远程任务", "label": "搜索", "placeholder": "搜索标题、项目、模型、消息", "clearA11y": "清除搜索", @@ -115,15 +115,15 @@ "close": "关闭" }, "filters": { - "collapseA11y": "收起对话筛选", - "expandA11y": "展开对话筛选", + "collapseA11y": "收起任务筛选", + "expandA11y": "展开任务筛选", "label": "筛选" }, "selection": { - "count": "已选择 {{count}} 个对话", + "count": "已选择 {{count}} 个任务", "canAct": "可执行操作", "cannotAct": "没有可执行操作", - "clearA11y": "取消选择对话" + "clearA11y": "取消选择任务" }, "bulk": { "pinning": "置顶中", @@ -131,38 +131,38 @@ "archiving": "归档中", "restoring": "恢复中", "deleting": "删除中", - "pinA11y": "批量置顶选中对话", - "unpinA11y": "批量取消置顶选中对话", - "archiveA11y": "批量归档选中对话", - "restoreA11y": "批量恢复选中对话", - "deleteA11y": "批量删除选中对话", + "pinA11y": "批量置顶选中任务", + "unpinA11y": "批量取消置顶选中任务", + "archiveA11y": "批量归档选中任务", + "restoreA11y": "批量恢复选中任务", + "deleteA11y": "批量删除选中任务", "cancelA11y": "取消批量操作", "partialFailure": "已处理 {{done}} 个,{{failed}} 个失败(已还原,可重试)。", - "noArchivable": "当前选择里没有可归档的活跃对话。", - "noPinnable": "当前选择里没有可置顶的活跃对话。", - "noRestorable": "当前选择里没有可恢复的归档对话。", - "noUnpinnable": "当前选择里没有已置顶的对话。", - "noDeletable": "当前选择里没有可删除的对话。", - "noneModifiable": "当前选择的对话都不能修改。" + "noArchivable": "当前选择里没有可归档的活跃任务。", + "noPinnable": "当前选择里没有可置顶的活跃任务。", + "noRestorable": "当前选择里没有可恢复的归档任务。", + "noUnpinnable": "当前选择里没有已置顶的任务。", + "noDeletable": "当前选择里没有可删除的任务。", + "noneModifiable": "当前选择的任务都不能修改。" }, "automationScope": { - "eyebrow": "自动化任务", - "title": "自动化任务", - "emptyCopy": "任务运行后,每次运行的对话都会出现在这里。", - "emptyTitle": "这个任务还没有运行记录" + "eyebrow": "自动化", + "title": "自动化", + "emptyCopy": "自动化运行后,每次运行的任务都会出现在这里。", + "emptyTitle": "这个自动化还没有运行记录" }, "projectScope": { "eyebrow": "项目", - "emptyCopy": "在这个项目里开始一个新对话。", - "emptyTitle": "这个项目还没有对话" + "emptyCopy": "在这个项目里开始一个新任务。", + "emptyTitle": "这个项目还没有任务" }, "subtitle": { - "deviceActive": "{{deviceName}} · {{count}} 个活跃对话", - "activeAndProjects": "{{count}} 个活跃对话 · {{projects}} 个项目" + "deviceActive": "{{deviceName}} · {{count}} 个活跃任务", + "activeAndProjects": "{{count}} 个活跃任务 · {{projects}} 个项目" }, "row": { - "a11yAutomationGroup": "自动化对话组 {{title}}", - "a11ySession": "对话 {{title}}", + "a11yAutomationGroup": "自动化组 {{title}}", + "a11ySession": "任务 {{title}}", "a11yCollapse": "收起 {{title}}", "a11yExpand": "展开 {{title}}", "collapse": "收起", @@ -171,33 +171,33 @@ "badge": { "waiting": "待处理 {{count}}", "automationPrefix": "自动化", - "sessionCount": "{{count}} 个对话", + "sessionCount": "{{count}} 个任务", "aggregate": "聚合", "running": "运行中", "unread": "未读 {{count}}" }, "child": { - "openA11y": "打开自动化对话 {{title}}" + "openA11y": "打开自动化生成的任务 {{title}}" }, "viewAllRuns": "查看全部 {{count}} 次运行" }, "automations": { "error": { "projectMissingConfig": "项目自动化缺少 projectConfigId,请在桌面端删除", - "deletePartial": "自动化已删除,但 {{count}} 个生成对话未能更新状态。请在桌面端检查这些对话。" + "deletePartial": "自动化已删除,但 {{count}} 个生成任务未能更新状态。请在桌面端检查这些任务。" }, - "subtitle": "{{active}} 个运行中 · {{total}} 个任务", + "subtitle": "{{active}} 个运行中 · {{total}} 个自动化", "metric": { "running": "运行中", "unreadResults": "未读结果", "executing": "执行中" }, "summary": { - "paused": "{{count}} 个任务已暂停。选择任务后可以运行、暂停、编辑或打开最近对话。", - "default": "选择任务后可以运行、暂停、编辑或打开最近对话。" + "paused": "{{count}} 个自动化已暂停。选择后可以运行、暂停、编辑或打开最近任务。", + "default": "选择自动化后可以运行、暂停、编辑或打开最近任务。" }, "empty": { - "copy": "可以直接从手机创建第一条自动化任务,或在桌面端创建后同步到这里。", + "copy": "可以直接从手机创建第一条自动化,或在桌面端创建后同步到这里。", "title": "这台设备暂无自动化" }, "createAutomation": "新建自动化", @@ -212,16 +212,16 @@ "edit": "编辑自动化" }, "sessionMode": { - "bound": "绑定已有对话后,目录、worktree、Agent 和 Fast 模式跟随该对话。", - "persistent": "首次触发会创建持续对话,后续触发继续接在同一对话里。", - "fresh": "每次触发都会创建新的对话。" + "bound": "绑定已有任务后,目录、worktree、Agent 和 Fast 模式跟随该任务。", + "persistent": "首次触发会创建持续任务,后续触发继续接在同一任务里。", + "fresh": "每次触发都会创建新的任务。" }, "field": { "name": "名称", "prompt": "提示词", "trigger": "触发方式", - "runSession": "运行对话", - "boundSession": "绑定对话", + "runSession": "运行任务", + "boundSession": "绑定任务", "intervalMinutes": "间隔分钟", "workspace": "工作区", "projectDir": "项目目录", @@ -230,25 +230,25 @@ "timezone": "时区" }, "namePlaceholder": "例如:每日项目巡检", - "scriptHint": "这是一个仅运行脚本任务,脚本配置只能在桌面端编辑;此处可以改动名称、触发方式、通知等其它字段。", + "scriptHint": "这是一个仅运行脚本的自动化,脚本配置只能在桌面端编辑;此处可以改动名称、触发方式、通知等其它字段。", "promptPlaceholder": "写清楚每次自动执行要让 {{appName}} 做什么", "trigger": { "recurring": "周期", "manual": "手动" }, "session": { - "fresh": "新对话", + "fresh": "新任务", "persistent": "持续", "bound": "绑定" }, - "boundSessionA11y": "绑定对话 {{name}}", + "boundSessionA11y": "绑定任务 {{name}}", "intervalPlaceholder": "留空则使用 cron", "workspace": { "project": "项目", "dialogue": "对话" }, "useWorktree": "使用 worktree", - "modelPlaceholderBound": "跟随对话", + "modelPlaceholderBound": "跟随任务", "fastMode": "Fast 模式", "notifyDesktop": "桌面通知", "notifyFeishu": "飞书通知", @@ -257,19 +257,19 @@ "cancelA11y": "取消编辑自动化" }, "delete": { - "counting": "正在统计由这条自动化生成的对话…", - "confirmDeleteBoth": "删除任务和对话", - "confirmArchive": "删除任务并归档对话", - "confirmTaskOnly": "只删除任务", + "counting": "正在统计由这条自动化生成的任务…", + "confirmDeleteBoth": "删除自动化和任务", + "confirmArchive": "删除自动化并归档任务", + "confirmTaskOnly": "只删除自动化", "title": "删除 {{name}}", - "copy": "删除自动化任务不会中断已经开始的执行。生成过的对话按下面的选项处理,和桌面端保持一致。", - "optionsA11y": "选择生成对话处理方式", - "keepDesc": "只删除这条自动化任务,历史对话继续保留在原列表里。", - "keepLabel": "保留生成对话", - "archiveDesc": "删除任务后,把由它生成的对话移到归档,后续仍可搜索。", - "archiveLabel": "归档生成对话", - "deleteDesc": "删除任务后,把由它生成的对话也标记为删除。这个操作不可从手机端撤销。", - "deleteLabel": "删除生成对话", + "copy": "删除自动化不会中断已经开始的执行。生成过的任务按下面的选项处理,和桌面端保持一致。", + "optionsA11y": "选择生成任务处理方式", + "keepDesc": "只删除这条自动化,历史任务继续保留在原列表里。", + "keepLabel": "保留生成任务", + "archiveDesc": "删除自动化后,把由它生成的任务移到归档,后续仍可搜索。", + "archiveLabel": "归档生成任务", + "deleteDesc": "删除自动化后,把由它生成的任务也标记为删除。这个操作不可从手机端撤销。", + "deleteLabel": "删除生成任务", "cancelA11y": "取消删除自动化", "confirmA11y": "确认删除自动化" }, @@ -284,7 +284,7 @@ }, "runDelete": { "title": "删除这条执行记录", - "copy": "只会删除这条自动化执行记录,不会删除它打开过的对话,也不会影响后续自动触发。", + "copy": "只会删除这条自动化执行记录,不会删除它打开过的任务,也不会影响后续自动触发。", "confirmA11y": "确认删除执行记录", "confirm": "删除记录", "cancelA11y": "取消删除执行记录" @@ -298,10 +298,10 @@ "cardA11y": "自动化模板 {{name}}", "manualConfig": "手动配置" }, - "scheduleRowA11y": "自动化任务 {{title}}", + "scheduleRowA11y": "自动化 {{title}}", "detail": { - "promptScriptFallback": "仅运行脚本任务,配置只能在桌面端编辑", - "promptEmpty": "没有保存任务提示词", + "promptScriptFallback": "仅运行脚本的自动化,配置只能在桌面端编辑", + "promptEmpty": "没有保存自动化提示词", "deleteA11y": "删除自动化", "runNowA11y": "立即执行自动化", "running": "运行中", @@ -313,9 +313,9 @@ "edit": "编辑" }, "run": { - "openSessionA11y": "打开自动化对话", + "openSessionA11y": "打开自动化生成的任务", "opening": "打开中", - "session": "对话", + "session": "任务", "restartA11y": "重新执行自动化", "restarting": "重跑中", "restart": "重跑", diff --git a/apps/mobile/src/i18n/locales/zh-CN/files.json b/apps/mobile/src/i18n/locales/zh-CN/files.json index 89849f1528b..e90653906a8 100644 --- a/apps/mobile/src/i18n/locales/zh-CN/files.json +++ b/apps/mobile/src/i18n/locales/zh-CN/files.json @@ -5,7 +5,7 @@ "footerOnline": "已连接 · 实时同步", "footerConnecting": "连接中…", "folderEmpty": "文件夹为空。", - "noWorkdir": "该对话没有工作目录,无法浏览文件。", + "noWorkdir": "该任务没有工作目录,无法浏览文件。", "rootDir": "(根目录)", "copyPath": "复制路径", "sendToSession": "发送到对话", diff --git a/apps/mobile/src/i18n/locales/zh-CN/interaction.json b/apps/mobile/src/i18n/locales/zh-CN/interaction.json index c980b03a7b8..9b9f6a6e5c6 100644 --- a/apps/mobile/src/i18n/locales/zh-CN/interaction.json +++ b/apps/mobile/src/i18n/locales/zh-CN/interaction.json @@ -13,9 +13,9 @@ "kind": "权限确认", "deny": "拒绝", "denyAccessibility": "拒绝权限请求", - "alwaysAllow": "本对话总是允许", - "alwaysAllowAccessibility": "本对话总是允许权限请求", - "alwaysAllowConfirm": "确认本对话允许", + "alwaysAllow": "本任务总是允许", + "alwaysAllowAccessibility": "本任务总是允许权限请求", + "alwaysAllowConfirm": "确认本任务允许", "allowOnce": "允许一次", "allowOnceAccessibility": "允许一次权限请求", "allowOnceConfirm": "确认允许一次", @@ -206,7 +206,7 @@ "INLINE_INVALID": "配置内容无效,请检查后重试。", "INLINE_UNAVAILABLE": "当前版本无法在对话中保存此配置,请更新 Cindy 后重试。", "SAVE_FAILED": "未能保存配置,请重试。", - "WINDOW_CLOSED": "发起设置的窗口已关闭,请回到该对话重试。", + "WINDOW_CLOSED": "发起设置的窗口已关闭,请回到该任务重试。", "TARGET_UNAVAILABLE": "插件已停用、卸载或不可用,请检查插件状态后重试。", "ASSESSMENT_FAILED": "暂时无法刷新配置状态,请重试。", "TIMEOUT": "等待设置超时,请重试。" diff --git a/apps/mobile/src/i18n/locales/zh-CN/message.json b/apps/mobile/src/i18n/locales/zh-CN/message.json index 60620230f55..f23a50e7bab 100644 --- a/apps/mobile/src/i18n/locales/zh-CN/message.json +++ b/apps/mobile/src/i18n/locales/zh-CN/message.json @@ -19,8 +19,8 @@ "expandMessage": "展开消息", "collapse": "收起", "expand": "展开", - "automationOriginNamed": "由自动化任务「{{name}}」发送", - "automationOrigin": "由自动化任务发送", + "automationOriginNamed": "由自动化「{{name}}」发送", + "automationOrigin": "由自动化发送", "modelMismatch": "本轮由 {{actual}} 响应,而非所选的 {{selected}}(服务端临时降级)", "copyStateCopying": "复制中", "copyStateCopied": "已复制", @@ -54,11 +54,11 @@ "subagentSummary": "Subagent 小结", "agentSwitchLabel": "已从 {{from}} 切换到 {{to}}", "agentSwitchViewHandoff": "{{label}},查看交接内容", - "sessionResumedPill": "已续接原对话", + "sessionResumedPill": "已续接原任务", "handoffContentDesc": "交接内容——切换时发送给新引擎的上下文摘要(不计入对话记录,仅在此展示)", "handoffContentDescEnglishSource": "交接内容——切换时发送给新引擎的上下文摘要,原文为英文(不计入对话记录,仅在此展示)", "openDiagramDetail": "打开图表详情", - "sessionChipFallback": "对话 {{id}}", + "sessionChipFallback": "任务 {{id}}", "imageFallbackTitle": "图片", "mermaidExportFailedTitle": "导出失败", "mermaidRenderIncompleteBody": "图表尚未渲染完成或导出出错,请稍后重试。", @@ -110,7 +110,7 @@ "mediaDeviceLinkHint": "移动端会通过 device-link 请求远程设备上传媒体后再打开。", "controlDelete": "删除消息", "controlRewind": "回退到这里", - "controlFork": "分叉对话", + "controlFork": "分叉任务", "markdownDocAriaLabel": "消息正文", "toolOutputTitle": "{{label}} 输出", "mermaidEmpty": "空 Mermaid 图表。", @@ -178,10 +178,10 @@ "neutral": "重新连接", "pending": "重新连接中…", "pendingWithProgress": "重新连接中 {{attempt}}/{{total}}…", - "details": "本次重试 {{attempt}}/{{total}} · 本对话累计重连 {{count}} 次" + "details": "本次重试 {{attempt}}/{{total}} · 本任务累计重连 {{count}} 次" }, "modelLabel": "模型", - "sessionLabel": "对话", + "sessionLabel": "任务", "sessionResumed": "已续接", "agentSwitch": "已从 {{from}} 切换到 {{to}}", "learnStarted": "技能学习已启动", diff --git a/apps/mobile/src/i18n/locales/zh-CN/models.json b/apps/mobile/src/i18n/locales/zh-CN/models.json index f6fa4d55443..476820a1510 100644 --- a/apps/mobile/src/i18n/locales/zh-CN/models.json +++ b/apps/mobile/src/i18n/locales/zh-CN/models.json @@ -30,7 +30,7 @@ "agentSwitch": { "browseAccessibility": "浏览 {{agent}} 模型", "confirmTitle": "切换到 {{agent}}?", - "confirmMessage": "下一条消息发送时,{{appName}} 会把当前对话交接给 {{agent}}。两个 Agent 对上下文和工具的理解可能不同,请确认后再选择目标模型。", + "confirmMessage": "下一条消息发送时,{{appName}} 会把当前任务交接给 {{agent}}。两个 Agent 对上下文和工具的理解可能不同,请确认后再选择目标模型。", "cancel": "取消", "confirm": "继续选择" } diff --git a/apps/mobile/src/i18n/locales/zh-CN/session.json b/apps/mobile/src/i18n/locales/zh-CN/session.json index 1ec5b93bd4a..9e200132cf2 100644 --- a/apps/mobile/src/i18n/locales/zh-CN/session.json +++ b/apps/mobile/src/i18n/locales/zh-CN/session.json @@ -28,18 +28,18 @@ "thinking": "思考中…", "networkReconnecting": "正在重新连接…", "modelBusyRetrying": "模型服务繁忙,正在重试…", - "composerSyncing": "正在同步对话数据,同步完成后即可发送。", - "composerSessionNotSynced": "当前对话还没有同步完成。", - "composerCreating": "对话创建中,首条消息发出后即可继续发送。", + "composerSyncing": "正在同步任务数据,同步完成后即可发送。", + "composerSessionNotSynced": "当前任务还没有同步完成。", + "composerCreating": "任务创建中,首条消息发出后即可继续发送。", "nextAgentSwitch": "下条切到 {{agent}} · {{model}}", - "createFailedTitle": "对话创建失败", - "createFailedDefault": "对话创建失败。", - "createFailedNotice": "对话创建失败:{{message}}", + "createFailedTitle": "任务创建失败", + "createFailedDefault": "任务创建失败。", + "createFailedNotice": "任务创建失败:{{message}}", "backToEdit": "返回编辑", "retry": "重试", "firstMessageNotSent": "首条消息没有发出,内容已还原到输入框。", "attachmentsNotCarriedBack": "有 {{count}} 个附件没能一起带回,需要重新添加。", - "commandWaitsForSession": "对话还在创建中,这个命令要等对话就绪后才能用,请稍后再发。", + "commandWaitsForSession": "任务还在创建中,这个命令要等任务就绪后才能用,请稍后再发。", "locateMessageNotFound": "未找到要定位的消息。", "shareNoLocalImage": "无法获取本地图片文件", "shareFailedTitle": "分享失败", @@ -50,15 +50,15 @@ "voiceStopAndSend": "结束语音输入并发送", "sendMessage": "发送远程消息", "voiceStartInput": "开始语音输入", - "stopSession": "停止远程对话", - "sessionNotFoundResync": "未找到当前远程对话,请重新同步后再发送。", - "missingWorkingDir": "当前对话缺少工作目录,不能发送消息。", + "stopSession": "停止远程任务", + "sessionNotFoundResync": "未找到当前远程任务,请重新同步后再发送。", + "missingWorkingDir": "当前任务缺少工作目录,不能发送消息。", "queueMessageGone": "这条排队消息已不在队列里,修改未保存。", "editQueueUnsupported": "电脑端版本过旧,还不支持安全修改排队引用、附件或清空文本。请升级电脑端,或仅修改普通文字。", - "sessionNotSyncedRetry": "对话尚未同步完成,请稍后再试。", + "sessionNotSyncedRetry": "任务尚未同步完成,请稍后再试。", "savingPreviousQueueEdit": "正在保存上一条排队消息的修改,完成后再编辑。", "attachmentsUploadingBeforeEdit": "还有附件正在上传,等上传完成后再编辑排队消息。", - "goalAlreadyActive": "该对话已有进行中的目标,请先在此处理它。", + "goalAlreadyActive": "该任务已有进行中的目标,请先在此处理它。", "resetReconfirmTitle": "请重新确认", "resetOfferExpired": "重置凭证已过期,请确认刷新后的配额信息。", "resetOutcomeReset": "Codex 用量窗口已重置。", @@ -69,37 +69,41 @@ "resetResultTitle": "重置结果", "resetFailedTitle": "重置失败", "operationFailed": "操作失败", - "sessionDeviceNotFound": "未找到这个对话所属的设备,请确认对应设备已连接后重试。", - "deleteMessageTitle": "删除本条对话?", - "deleteMessageBody": "用户消息只删除本条;AI 消息会删除上一次用户输入之后产生的整轮输出。之后的对话继续保留。下次发送时,AI 上下文会从剩余记录重建。", - "deleteMessageAction": "删除本条对话", + "sessionDeviceNotFound": "未找到这个任务所属的设备,请确认对应设备已连接后重试。", + "deleteMessageTitle": "删除本条消息?", + "deleteMessageBody": "用户消息只删除本条;AI 消息会删除上一次用户输入之后产生的整轮输出。之后的消息继续保留。下次发送时,AI 上下文会从剩余记录重建。", + "deleteMessageAction": "删除本条消息", "readingCapabilities": "正在读取 {{agent}} 运行能力…", "editingQueueMessageNumbered": "正在编辑第 {{index}} 条排队消息", "editingQueueMessage": "正在编辑排队消息", "discardQueueEdit": "放弃编辑排队消息", "savingHint": "正在保存修改,完成后再操作。", "composerPlaceholder": "输入远程消息", - "openSessionMenu": "打开对话菜单", - "searchTitle": "搜索当前对话", - "searchPlaceholder": "搜索当前对话消息", + "openSessionMenu": "打开任务菜单", + "searchTitle": "搜索当前任务", + "searchPlaceholder": "搜索当前任务的消息", "searchEnterKeyword": "输入关键词", "searchNoMatch": "没有匹配的消息", "searchPrevious": "上一条搜索结果", "searchNext": "下一条搜索结果", - "syncingSession": "正在同步对话", - "awaitingSync": "等待对话同步", - "resync": "重新同步远程对话", + "syncingSession": "正在同步任务", + "awaitingSync": "等待任务同步", + "resync": "重新同步远程任务", "collapseHistory": "收起历史消息", "expandHistory": "查看历史消息", "readOnlyMode": "只读模式", "accessRevokedShort": "访问已撤销", - "sessionNotSynced": "对话未同步", - "conversationFallback": "对话", + "sessionNotSynced": "任务未同步", + "conversationFallback": "任务", "accessRevoked": "访问权限已被被控设备撤销", "syncing": "正在同步", "pendingCount": "待处理 {{num}}", "archived": "已归档", - "queuePausedNotice": "队列已暂停" + "queuePausedNotice": "队列已暂停", + "forkConfirmTitle": "从这里开启一个新任务?", + "forkConfirmDescription": "系统会根据这里的对话上下文创建一个独立的新任务。原任务不会改变,之后两边的消息互不影响。", + "forkConfirm": "开启新任务", + "forkCancel": "取消" }, "new": { "selectDevice": "选择设备", @@ -108,18 +112,18 @@ "selectProject": "选择项目", "composerPlaceholder": "今天我们做点什么呢~", "voiceSelectDeviceFirst": "请选择设备后再使用语音输入。", - "creatingSession": "正在创建对话", - "createAndSend": "创建对话并发送首条消息", + "creatingSession": "正在创建任务", + "createAndSend": "创建任务并发送首条消息", "modelAccessibility": "模型:{{model}}", "voiceInput": "语音输入", "selectDeviceError": "请选择设备。", "enterProjectPath": "请输入电脑端项目路径。", "enterModel": "请输入模型。", "enterFirstMessageOrAttachment": "请输入首条消息或添加附件。", - "noSessionIdReturned": "被控端没有返回新对话 id。", - "sessionIdNotAdopted": "被控端未采用预生成对话 id(电脑端版本异常),请更新电脑端后重试。", - "firstMessageMissingConfirm": "首条消息没有发出,内容已还原到输入框;发送前请确认对话里没有这条消息。", - "firstMessageUnconfirmed": "首条消息发送状态未确认,内容已还原到输入框,请先确认对话里是否已有这条消息再发送。", + "noSessionIdReturned": "被控端没有返回新任务 id。", + "sessionIdNotAdopted": "被控端未采用预生成任务 id(电脑端版本异常),请更新电脑端后重试。", + "firstMessageMissingConfirm": "首条消息没有发出,内容已还原到输入框;发送前请确认任务里没有这条消息。", + "firstMessageUnconfirmed": "首条消息发送状态未确认,内容已还原到输入框,请先确认任务里是否已有这条消息再发送。", "selectControlledDevice": "选择被控设备", "selectDeviceNamed": "选择设备 {{name}}", "selectAgent": "选择 Agent", @@ -155,22 +159,22 @@ "previewAttachments": "附件:{{num}} 个", "previewReadyTitle": "准备创建并发送", "previewNotReadyTitle": "还不能创建", - "previewReadySubtitle": "确认后会在被控设备创建对话,并把首条消息加入队列。" + "previewReadySubtitle": "确认后会在被控设备创建任务,并把首条消息加入队列。" }, "menu": { "chipPinned": "已置顶", "chipArchived": "已归档", "chipReadOnly": "只读", - "titleFallback": "远程对话", - "unnamedTitle": "未命名对话", - "renameAction": "重命名对话", - "copyLinkAction": "复制对话链接", + "titleFallback": "远程任务", + "unnamedTitle": "未命名任务", + "renameAction": "重命名任务", + "copyLinkAction": "复制任务链接", "unpin": "取消置顶", "pin": "置顶", "restore": "恢复", "archive": "归档", - "deleteAction": "删除对话", - "deleteConversation": "删除对话", + "deleteAction": "删除任务", + "deleteConversation": "删除任务", "workingDir": "工作目录", "aiRenameUnsupported": "被控设备版本过旧,暂不支持自动起名。", "aiRenameOffline": "被控设备不在线,稍后再试。", @@ -178,22 +182,22 @@ "contextUsageSummary": "上下文 {{percent}}%", "copied": "已复制", "copyFailed": "复制失败", - "deleteConfirmTitle": "删除对话?", + "deleteConfirmTitle": "删除任务?", "deleteConfirmBody": "删除后不可恢复。", "dirAlreadyAdded": "这个目录已经在列表里。", "resetCodexTitle": "重置 Codex 用量?", "resetCodexBody": "将为 {{account}} 消耗 1 次重置。配额窗口会立即重开,此操作不能撤销。", "currentCodexAccount": "当前 Codex 账号", "resetConsumeOnce": "消耗 1 次重置", - "titlePlaceholder": "对话标题", + "titlePlaceholder": "任务标题", "aiRenameLabel": "AI 自动起名", - "viewUsageDetail": "查看对话用量详情", + "viewUsageDetail": "查看任务用量详情", "linkCopied": "链接已复制", - "sessionInfo": "对话信息", + "sessionInfo": "任务信息", "usageSection": "用量", "refreshContextUsage": "刷新上下文用量", - "currentSessionSpend": "本次对话", - "noSessionSpend": "暂无对话用量", + "currentSessionSpend": "本次任务", + "noSessionSpend": "暂无任务用量", "contextLabel": "上下文", "accountLimits": "账号限额", "quotaExhaustedResettable": "当前配额已耗尽。重置会立即消耗 1 次,且不能撤销。", @@ -217,20 +221,27 @@ "confirmRename": "确认重命名", "confirm": "确定", "cancelRename": "取消重命名", - "backToMenu": "返回对话菜单" + "backToMenu": "返回任务菜单" + }, + "messageMenu": { + "fork": "开启一个新任务", + "addToChat": "添加到对话", + "copyLink": "复制当前消息链接", + "rewind": "回到此处", + "deleteOne": "删除本条消息" }, "tail": { "interrupted": "任务执行到一半时应用退出,已被中断。", "continueTask": "继续任务", "retry": "重试", "ignore": "忽略", - "codexThreadLost": "Codex 鉴权模式已切换,当前对话无法继续 —— 请新建对话后生效。", - "encryptedContentInvalid": "由于更换供应商导致加密不可用,本对话只能在原供应商下使用;请回到电脑端处理(可 Fork 丢弃协议加密内容后在新对话继续)。" + "codexThreadLost": "Codex 鉴权模式已切换,当前任务无法继续 —— 请新建任务后生效。", + "encryptedContentInvalid": "由于更换供应商导致加密不可用,本任务只能在原供应商下使用;请回到电脑端处理(可 Fork 丢弃协议加密内容后在新任务继续)。" }, "row": { "moreOptions": "更多选项", - "archiveConversation": "归档对话", - "closeActionMenu": "关闭对话操作菜单", + "archiveConversation": "归档任务", + "closeActionMenu": "关闭任务操作菜单", "referenceAroundAnchor": "链接附近", "referenceRecent": "最近消息", "referenceCount": "{{num}} 条", diff --git a/apps/mobile/src/i18n/locales/zh-CN/settings.json b/apps/mobile/src/i18n/locales/zh-CN/settings.json index 0c3865a5248..cb4c9d834fb 100644 --- a/apps/mobile/src/i18n/locales/zh-CN/settings.json +++ b/apps/mobile/src/i18n/locales/zh-CN/settings.json @@ -58,8 +58,8 @@ "connecting": "正在连接 Relay", "stopped": "Relay 未连接", "lastSynced": "上次同步 {{time}}", - "canSync": "可以同步远程对话。", - "reconnectHint": "网络恢复后会自动重新订阅远程对话。", + "canSync": "可以同步远程任务。", + "reconnectHint": "网络恢复后会自动重新订阅远程任务。", "resumeHint": "回到前台或重新登录后会恢复连接。" }, "header": { @@ -106,7 +106,7 @@ "analyticsResetAction": "恢复" }, "account": { - "logoutHint": "退出只会清除这台手机上的登录态和本地远程镜像,不会影响电脑端对话。", + "logoutHint": "退出只会清除这台手机上的登录态和本地远程镜像,不会影响电脑端任务。", "logout": "退出登录", "loggingOut": "退出中", "loggingOutAccessibility": "正在退出登录" diff --git a/apps/mobile/src/session/MessageRenderer.tsx b/apps/mobile/src/session/MessageRenderer.tsx index 3f66ed342b1..17999478716 100644 --- a/apps/mobile/src/session/MessageRenderer.tsx +++ b/apps/mobile/src/session/MessageRenderer.tsx @@ -1475,7 +1475,7 @@ function MessageBubble({ }) { const styles = useThemedStyles(makeStyles); const { colors } = useTheme(); - const { t } = useTranslation(); + const { t, i18n: i18nInstance } = useTranslation(); const [copyState, setCopyState] = useState('idle'); const [actionSheetOpen, setActionSheetOpen] = useState(false); // chat-text-quote:只解析持久化 quotesEncoded 明确标记的产品引用消息,避免 @@ -1649,13 +1649,15 @@ function MessageBubble({ }, [collapseResolved, collapseLatched, displayBubbleBody]); const shouldCollapseLongMessage = (collapseMeasureEnabled && collapseLatched) || collapseResolved; const longMessageCollapsed = shouldCollapseLongMessage && !longMessageExpanded; + // label 走 i18n.t,所以语言必须进依赖:否则用户在任务页挂载期间切语言,菜单会一直 + // 停在切换前的语言,直到 capability 变化或组件重挂。 const messageMenu = useMemo(() => buildMobileMessageMenu({ canAddToChat, canCopyLink, canDelete, canFork, canRewind, - }), [canAddToChat, canCopyLink, canDelete, canFork, canRewind]); + }), [canAddToChat, canCopyLink, canDelete, canFork, canRewind, i18nInstance.language]); const actionBar = useMemo(() => buildMessageActionBarPresentation({ align: isUser ? 'user' : 'agent', canCopy, diff --git a/apps/mobile/src/session/messageActionMenu.ts b/apps/mobile/src/session/messageActionMenu.ts index 2aa5656526a..a5d1415e887 100644 --- a/apps/mobile/src/session/messageActionMenu.ts +++ b/apps/mobile/src/session/messageActionMenu.ts @@ -1,4 +1,12 @@ -/** Pure menu model shared by the message action sheet and tests. */ +/** + * Pure menu model shared by the message action sheet and tests. + * + * 文案走 i18n(与同目录 `sessionMenu.ts` 同规):这些 label 会直接渲染成 + * `MessageActionSheet` 的可视文本与无障碍标签,mobile 支持 en / ja / ko, + * 塞硬编码中文会让非中文语言环境看到中文菜单。 + */ +import { i18n } from '@/i18n'; + export type MobileMessageMenuActionId = 'fork' | 'add-to-chat' | 'copy-link' | 'rewind' | 'delete'; export interface MobileMessageMenuItem { @@ -16,14 +24,21 @@ export function buildMobileMessageMenu(input: { canRewind: boolean; }): MobileMessageMenuItem[] { const items: MobileMessageMenuItem[] = []; - if (input.canFork) items.push({ id: 'fork', label: '在新对话中继续' }); - if (input.canAddToChat) items.push({ id: 'add-to-chat', label: '添加到对话' }); - if (input.canCopyLink) items.push({ id: 'copy-link', label: '复制当前对话链接' }); - if (input.canRewind) items.push({ id: 'rewind', label: '回到此处' }); + // 语义与 desktop 的 chat.messageActionBar.* 一一对应:fork 产出的是一条新任务; + // copy-link 复制的是带 messageClientId 的消息深链;delete 删的是单条消息。 + // 「添加到对话」指发进当前对话流,按 naming 规则保持「对话」。 + if (input.canFork) items.push({ id: 'fork', label: i18n.t('session.messageMenu.fork') }); + if (input.canAddToChat) { + items.push({ id: 'add-to-chat', label: i18n.t('session.messageMenu.addToChat') }); + } + if (input.canCopyLink) { + items.push({ id: 'copy-link', label: i18n.t('session.messageMenu.copyLink') }); + } + if (input.canRewind) items.push({ id: 'rewind', label: i18n.t('session.messageMenu.rewind') }); if (input.canDelete) { items.push({ id: 'delete', - label: '删除本条对话', + label: i18n.t('session.messageMenu.deleteOne'), destructive: true, separatorBefore: items.length > 0, }); diff --git a/apps/mobile/src/session/sessionMenu.ts b/apps/mobile/src/session/sessionMenu.ts index 656763300fb..4bab0f9f381 100644 --- a/apps/mobile/src/session/sessionMenu.ts +++ b/apps/mobile/src/session/sessionMenu.ts @@ -59,7 +59,7 @@ export function buildSessionMenuHeader( return { // 哨兵(尚未起名)直接给本地化兜底,**不回落 workspaceName** —— 回落会把目录名当标题, - // 与 desktop 的「未命名对话」不一致(PR #1031 review P1)。文案走 i18n:本模块四语言 + // 与 desktop 的「未命名任务」不一致(PR #1031 review P1)。文案走 i18n:本模块四语言 // 都在用 i18n.t,不能塞硬编码中文(mobile 支持 en / ja / ko)。 title: isDefaultDraftSessionTitle(session.title) ? i18n.t('session.menu.unnamedTitle') diff --git a/docs/product-rules/README.md b/docs/product-rules/README.md index e434410cdcf..a03bd44f634 100644 --- a/docs/product-rules/README.md +++ b/docs/product-rules/README.md @@ -15,3 +15,5 @@ Core 边界,以及 Agent、Skill、插件的产品分工。 - [`region-and-editions.md`](region-and-editions.md):Global 与中国大陆版的关系、 身份命名与默认值方向、区域相关 UI 标注与对外口径。 +- [`task-and-conversation-naming.md`](task-and-conversation-naming.md):`session` 的中文 + 叫「任务」,任务 / 对话 / 消息的分层定义与判定规则,以及「任务」与 `task` 的歧义处理。 diff --git a/docs/product-rules/task-and-conversation-naming.md b/docs/product-rules/task-and-conversation-naming.md new file mode 100644 index 00000000000..6a628455804 --- /dev/null +++ b/docs/product-rules/task-and-conversation-naming.md @@ -0,0 +1,711 @@ +# 任务、对话、消息:面向用户的命名 + +> **状态**:权威产品规则(authoritative)。已随本次改名落地,机器口径见 +> `i18n/glossary.json` 的 `session` / `agent-session` / `chat` / `task` / `message` / `turn` 条目。 +> **最容易译错的一条**:英文 `session` 在本仓是**五个**不同概念,只有中文必须把它们分开—— +> 动手前先读 §6.0.2。 +> **适用范围**:所有面向用户的中文文案、帮助内容与对外表述 +> **读取时机**:新增或修改涉及「任务 / 对话 / 消息」的 UI 文案之前 + +## 1. 为什么要改 + +改名前中文把 `session` 译作「对话」。这个词无法表达 Cindy 最重要的一个能力:**同时跑 +多个互不干扰的工作**。「对话」听起来是一次问答,用户看不出左侧列表里的每一条其实 +是一个有自己空间、可以并行推进的独立工作。 + +改称「任务」就是为了让这件事在命名上自明。 + +但这**不是**把所有「对话」替换成「任务」。用户在每个任务里做的事**确实是在跟 AI 对话**, +产品里也确实还有对话框。所以需要先把概念分层定清楚。 + +## 2. 概念模型 + +面向用户只有**三层**,各有明确所指,互不重叠: + +``` +任务 (Session) 左侧列表里的一个条目 +│ 有自己的独立空间,可以同时跑多个 +│ +└── 对话 (Chat) 你在这个任务里跟 AI 交互的过程 + │ 「对话区」「对话正文」「对话记录」都指这个 + │ + └── 消息 (Message) 对话里的一条往来,用户看到的最小单元 +``` + +**「轮次」不是第四层。** 一轮(`turn`)指一次提问到一次回答结束,而 AI 在这一轮里做的 +所有事——思考、调用工具、多次输出——最终**折叠成一条消息**呈现。所以从用户视角, +消息和轮次是同一个粒度的两种切法,不存在包含关系。 + +现有产品行为就是按这个口径实现的:删除时「用户消息只删除本条;**AI 消息会删除上一次 +用户输入之后产生的整轮输出**」——AI 侧的一条消息就等于一轮输出。 + +因此**面向用户一律说「消息」**。`turn` 是内部概念,只在确实要强调"一次完整往返"时才 +译作「一轮」(如"上一轮"),不要引入「轮次」这个词作为界面术语。 + +另有一类与上面**完全无关**、不得混用: + +- **弹窗 (Dialog)** —— 界面上的模态框。中文用「弹窗」或「对话框」,不受本文约束。 + +### 2.1 判定规则 + +拿不准某处该用哪个词时,按顺序问: + +1. **它能被单独打开、删除、重命名、分享、导出吗?** → 能,就是**任务** +2. **它指的是"人和 AI 来回交流"这件事本身,或者"没有项目归属"这一类吗?** → 是,就是 + **对话**(后一种见 §2.3) +3. **它指某一条具体往来吗?** → 是,就是**消息**(不要说「轮次」) +4. **它是界面上弹出来的框吗?** → 是,用**弹窗**,与本文无关 + +### 2.2 典型对照 + +| 场景 | 改前 | 改后 | +|---|---|---| +| 侧栏新建入口 | 新建对话 | 新建任务 | +| 列表为空 | 暂无对话 | 暂无任务 | +| 导出整条记录 | 导出对话 | 导出任务 | +| 找不到那条记录 | 对话不存在 | 任务不存在 | +| 远程接管 | 接管这个对话 | 接管这个任务 | +| 删除一条往来 | 删除本条对话 | 删除本条消息 | +| 上一次问答 | 上轮对话 | 上一轮 | +| 聊天区正文 | 对话正文 | 对话正文(不变) | +| 模态框 | 关闭此对话框 | 关闭此对话框(不变) | + +第 6、7 行是**既有译名错误**,与本次改名无关,本来就该修(见 §6)。 + +### 2.3 「对话」还兼任一个分类名:没有项目归属的那一类 + +任务可以绑一个项目(工作目录),也可以不绑。UI 上需要一个词称呼**不绑项目**的那一类, +这个词就是「对话」——纯聊天,不落到某个项目上。 + +**这不改变单条仍然叫「任务」。** 分类名说的是「这一桶装的是什么」,不是「其中每一条叫 +什么」。所以「对话」这一桶里装着若干条任务,两句话同时成立。 + +代码里这个二分就是 `workspaceKind: 'dialogue' | 'project'`。凡是它驱动的文案都按这个 +口径,英文用 `Chat`: + +| 位置 | 分类名 | +|---|---| +| 侧栏「对话」分组标题、导航项、展开/收起、整理 | 对话 | +| 把某条任务移出项目 → 归到该分组 | 移到对话 | +| 任务顶部的归属 chip(`workspaceKind === 'dialogue'` 时) | 对话 `<编号>` | +| 新建时的项目选择器(chip + popover 标题 + 选项) | 对话 / 对话或选择项目 | +| 手机端新建的工作区二选一、位置预览 | 对话 / 对话工作区 | +| 远端新建 banner | 在 X 上新建对话(不绑定项目) | +| 设置 → 任务导入的「位置」筛选、条目归属标签与统计 | 已有项目 / 对话 | +| 定时任务列表按项目分组 | 对话(`DIALOGUE_GROUP_KEY` 那一组) | +| 自动化表单的工作区二选一 | 项目 / 对话 | + +**但同一栏里指向条目的文案仍用「任务」**,这条界线要划清: + +| 文案 | 用词 | 为什么 | +|---|---|---| +| 该分组的 + 按钮 | 新任务 | 动作产物是一条任务 | +| 该分组空态 | 暂无任务 | 说的是这栏里没有条目 | +| 该分组条目计数 | {{count}} 个任务 | 数的是条目 | + +判定时问:**这个词指的是一批任务的归属类别,还是其中某一条?** 前者用「对话」。 + +落地时这一类被漏判了 21 处(最初全改成「任务」),最刺眼的是项目选择器上出现 +「任务或选择项目」——把一个**归属分类**说成了单条条目,用户根本读不出它在选什么。 +是实机目检截图时发现的,静态检查和测试都没拦住。 + +### 2.4 那个自动分配的目录叫「工作目录」,不要另起名字 + +不绑项目的任务也有工作目录:Cindy 在 `userData/dialogues/<日期>//` 下自动开一个 +空目录当 cwd,Agent 产出的文件落在那里。**对话消息存在数据库里,与这个目录无关。** + +它对外一律叫**工作目录**(`Working directory`,现状 43 处唯一译法),**不要为它单独造词** +——「独立任务空间」「任务空间」「工作空间」这类说法一并禁用(2026-07-31 裁决)。同一个东西 +攒出多套称呼,用户就不知道点开会看到什么。 + +**但任务顶部那个 chip 不写「工作目录」**,仍按 §2.3 写归属分类「对话」——权衡过一轮后维持 +原样(2026-07-31):chip 上还跟着 `sessionId` 前 8 位用来区分同时开着的多条任务 +(`inter-session distinguishability`,见 `CCAgentSessionView.tsx` 注释),「对话 5dc71f0f」比 +「工作目录 5dc71f0f」更能说明这条任务的归属,而「点开能看到文件」这件事由 chip 的点击行为 +承担,不必写进标签。 + +本条约束的是**称呼那个目录本身**的场合(提示、说明、aria、设置项),不是 chip 标签。 + +门禁已就位:`glossary.json` 的 `working-directory` 条目把「任务空间」「工作空间」列入 +`forbidden`。禁用是**子串匹配**,所以「独立任务空间」「专属任务空间」这类前缀变体都会被 +「任务空间」一条拦下,不需要逐个枚举(已实测:注入 `独立任务空间` 后门禁报错并指回本条目)。「工作区」**不在**禁用列表——那是 `Workspace` 的合法译法(见 `worktree` 条目 +note,那里早已裁定「工作区」只留给 Workspace、working directory 用「工作目录」;本条把该 +裁决提为独立条目并加上门禁)。 + +## 3. 「任务」与 `task` 的冲突 + +这是落地时最大的坑。 + +「任务」原先已被 `task` 占用——指 **Agent 正在执行的那件活**。撞得最直接的一句: + +``` +"当前对话正在执行任务,请稍后再试。" + ← "This session is currently running a task." +``` + +改名后会变成「当前任务正在执行任务」。 + +### 3.1 处理方式:同句出现时必须消解歧义 + +**同一句里同时出现 `session` 与 `task` 时,task 一侧必须改写。** 优先动词化,确需名词时 +用「执行」或「作业」: + +| 英文 | 不要 | 应该 | +|---|---|---| +| This session is currently running a task | 当前任务正在执行任务 | 当前任务正在运行 | +| Another Codex task is still running | 其他 Codex 任务正在运行(+本任务…) | Codex 正在其它任务中运行 | +| stop all background tasks (session can continue) | 停止全部后台任务(任务可继续) | 停止全部后台作业(任务可继续) | +| Pinned sessions with task-status summaries | 置顶任务…含任务现状摘要 | 置顶任务…含执行状态摘要 | + +不同句、语境清晰时,`task` 沿用「任务」(现状 104 处)——落地时刻意没有全量改写, +因为「后台任务」「定时任务」这类有限定词的说法本身不歧义,强行统一反而生硬。 + +**`automation` 不带「任务」二字。** 原先中文叫「自动化任务」,与 session 撞在同一句里会 +出现「删除任务和任务」这种读不通的文案。英文侧本来就以 `automation`(38 处)为主、 +只有 4 处叫 `automation task`,所以中文统一为「自动化」: + +- 「只删除自动化,任务继续留在左侧列表中。」 +- 「删除自动化并归档任务」 + +例外:**定时任务**(`schedule`)保留「任务」二字,有「定时」前缀限定,不歧义。 + +## 4. 英文侧已一并收敛 + +中文的混乱有一半来自英文侧本身不统一——改名前同一个东西有**五种英文写法**,所以只改 +中文治不了根:写文案的人看到 `Dialogue` 仍会译成「对话」。 + +下表按**当前 HEAD 实测**(en locale,desktop + mobile;口径见本节末的复现命令)。**「改前」列是 +本次改名的 merge base,不是历史某个快照**——本仓很活跃,隔几天重跑数字会变。 + +| 英文 | 改前 | 现在 | 去向 | +|---|---|---|---| +| `Session` | 365 | **476** | **唯一正式写法**,指那个可打开可删除的条目 | +| `Conversation` | 136 | **19** | 101 → `Session`、12 → `Chat`、4 改写成别的说法;**剩下 19 处刻意保留** | +| `Dialogue` | 16 | **0** | 14 → `Chat`、2 → `Session`;英文侧不再使用 | +| `Chat` | 131 | 144 | **基本保留**;只把 14 处明确指条目的改成 `Session`(`New chat` / `Chats` / `chat titles` / `chat link` / `Rename Chat` / `Archive chat` 等) | +| `Thread` | 11 | 11 | 一处没动,按 `glossary.json` 的 thread 条目分语境处理 | + +⚠️ **不要把这张表读成「`Conversation` 已清零」。** 它没有清零,也不该清零——保留的 19 处是 +按 §4.1 逐条判过的三类(IM 平台自己的会话、`conversation lane` 这种复合概念、隐私与合规 +文案),删掉它们会制造新的错误。同理 `Chat` 的 144 处绝大多数都必须留着。 + +新写英文文案时:**指条目用 `Session`,指交流过程用 `Chat`**,不要再引入 +`Conversation` / `Dialogue`;但改动既有 `Conversation` / `Chat` 之前,先用 §4.1 的分类过一遍。 + +复现「现在」那一列(要拿「改前」列,把读文件换成 `git show :`): + +```js +// 统计的是「值里含该词的 key 数」,不是出现次数,也不看 key 名。 +// 存成 /tmp/count-en.mjs 后 `node /tmp/count-en.mjs`(换 locale 改路径即可)。 +import { execFileSync } from 'node:child_process'; +import fs from 'node:fs'; +const walk = (o, out = []) => { + for (const v of Object.values(o)) (v && typeof v === 'object') ? walk(v, out) : out.push(String(v)); + return out; +}; +const files = execFileSync('git', ['ls-files', + 'apps/desktop/src/renderer/i18n/locales/en', 'apps/mobile/src/i18n/locales/en', +], { encoding: 'utf8' }).split('\n').filter((f) => f.endsWith('.json')); +const vals = files.flatMap((f) => walk(JSON.parse(fs.readFileSync(f, 'utf8')))); +for (const w of ['session', 'conversation', 'dialogue', 'chat', 'thread']) { + console.log(w.padEnd(13), vals.filter((v) => new RegExp(w, 'i').test(v)).length); +} +``` + +### 4.1 三个词都不能批量替换(踩过的坑) + +`dialogue` 在本产品里只有一种用法。**`chat` 和 `conversation` 都不行。** + +`chat` 同时承载至少五种互不相干的含义,机器分不开: + +| 用法 | 例子 | 能不能改 | +|---|---|---| +| OpenAI 协议名 | `Chat Completions (Cindy bridge)` | **绝对不能**,是外部协议名 | +| 模型类型 | `chat model picker` / `chat model endpoint` | 不能,指对话类模型 | +| 外部平台概念 | `group chats` / `private chat` / 飞书 `recently-active chats` | 不能,那是微信 / Telegram / 飞书自己的概念 | +| 动词 | `you chat from here` / `people I chat with` | 不能,改了语法就坏 | +| 界面区域 | `chat input` / `chat width` / `the chat on the left` | 不能,指对话区 | +| 那个条目 | `New chat` / `Chats` / `search chat titles` | 可以 → `Session` | + +落地时先用正则批量替换过一遍,结果把 `Chat Completions` 和 `you chat from here` 都改坏了, +是 review diff 时发现的。**改 `chat` 必须用白名单,不要用黑名单或 lookahead。** + +`conversation` 起初被判定为"只有一种用法、可整体替换",**这个判断是错的**。它至少还有 +三种用法不能动: + +| 用法 | 例子 | 后果 | +|---|---|---| +| IM 平台自己的会话 | `New Feishu conversations` / `Send /new in an existing conversation` | 改后与同组的 `in an existing DM` / `in an existing chat` 自相矛盾 | +| 通道等复合概念 | `one session per conversation lane` | 改成 `one session per session lane`,重复且丢义 | +| 隐私与合规文案 | `never conversations, files, or prompts` | 改成 `never sessions` 后承诺范围变了;这类措辞一律不在术语 PR 里动 | + +判断 `conversation` 该不该改,看它**属于谁**:属于 Cindy 的条目 → `Session`;属于对方 +平台,或指交流内容本身 → 不动。 + +### 4.2 中英必须成对判断 + +最有效的自检:**逐条比对中英去向**。中文侧逐条人工判过,可以当基准——凡「中文保持 +了『对话』而英文改成了 `session`」的,都是英文侧判错了。落地时这条检查抓出 13 处 +不一致,其中 2 处是隐私承诺文案。 + +反过来也要看:中文改成「任务」而英文还是 `conversation` 的,要么英文漏改,要么中文改 +错了。 + +## 5. 不改的东西 + +- **代码标识符**:`session`、`sessionId`、`SessionView`、IPC channel 名、数据库字段、 + 协议字段一律不动。术语表现有条目已写明「代码与内部标识仍用 Session」,本文延续。 +- **弹窗**:见 §2。 + +日文与韩文**一并收敛了**,理由见 §5.1。 + +### 5.1 ja / ko 为什么也要改(原本以为不用) + +最初的判断是「`セッション` / `세션` 是音译,不存在中文这种一词多义问题,不跟改」。这句话 +**只对 task/session 撞车那部分成立**,管不到另一件事:同一个概念在 ja / ko 里本来就有两种写法。 + +实际分布(**口径:key 名含 `session` 的条目**,如 `ccAgent.sidebar.sessionMenu.*`;这批 key 就是 +描述条目的。desktop + mobile 合计): + +| | | 主流 | 少数派 | +|---|---|---|---| +| ja | 改名前 → 现在 | `セッション` 124 → **143** | `会話` 23 → **4** | +| ko | 改名前 → 现在 | `세션` 127 → **143** | `대화` 25 → **8** | + +也就是说 ja / ko 本来就在混用,而且主流早已是音译,那 23 / 25 处是**违反术语表自身声明**的既有 +债务。英文侧从 `Chat` / `Conversation` 收敛到 `Session` 之后,这类不一致会直接暴露成 +`New Session` / `新しい会話` 这种四语打架的组合,所以 2026-07-31 一并收敛到音译。剩下的 +4 / 8 处是下面那三类刻意保留。 + +**这不是新裁决,是向既有主流靠回去**:术语表 `session` 条目本来就声明 ja = `セッション`、 +ko = `세션`。 + +三类刻意保留(逐条判断出来的,占候选的 5%): + +| 保留什么 | 例子 | 为什么 | +|---|---|---| +| 交流过程(动词性) | `このセッションで会話を続ければ` / `이 세션에서 계속 대화하면` | 对应 `keep chatting`,改了语法就坏 | +| `chat history` | `チャット履歴` / `채팅 기록` | 对应的是 chat 不是 session | +| 复合概念 | `会話 lane`(conversation lane)、`会話コンテキスト`(the context)、`会話インデックス`(transcript indexes) | 英文本身就不是 session | + +**`タスク` / `작업`(task)不用像中文那样改写成「自动化」**——日韩音译天然区分 +`セッション` / `タスク`,没有中文那种撞车。这恰好是最初判断里成立的那半。 + +## 6. 落地记录 + +一次性完成,四种语言同步收敛(`ja` / `ko` 的取舍见 §5.1)。 + +**口径**:以本次改名的 merge base 为基线,按 en / zh-CN / ja / ko 四个 locale(desktop + +mobile)的 key 逐条比对当前 HEAD。**数字会随 rebase 与 review 期间的回退漂移**——本表按最后 +一次实测更新,需要重新盘点时重跑 §4 末尾与本节的脚本,不要拿旧数字对账。 + +| 项 | 数量 | +|---|---| +| 含「对话」的中文文案条目 | 565(**逐条人工判断**:改 466 / 原样保留 99) | +| 英文 `conversation`/`dialogue` → `Session` | 103(conversation 101 + dialogue 2) | +| 英文 `conversation`/`dialogue` → `Chat`(§2.3 归属分类 + 交流过程) | 26(conversation 12 + dialogue 14) | +| 英文 `conversation` → 改写成别的说法(不再出现这三个词) | 4 | +| 英文 `conversation` **保留不动**(IM 平台/复合概念/隐私文案,见 §4.1) | 19 | +| 英文 `chat` → `Session`(白名单,仅指条目的) | 14 | +| 「自动化任务」→「自动化」 | 23 | +| `ja` 由 `会話` 收敛到 `セッション` | 118(按**全部 key** 计仍含 `会話` 的 60 处,多数与 session 无关,如 `会話コンテキスト`;只看 key 名含 `session` 的那批,残留 4 处,见 §5.1) | +| `ko` 由 `대화` 收敛到 `세션` | 115(同上口径:全部 key 残留 96,key 名含 `session` 的残留 8) | +| 术语表条目 | 7 个:新增 4(`task` / `message` / `turn` / `working-directory`)+ 改译 3(`session` / `chat` / `thread`) | + +### 6.0 落地方法:脚本只用来定位,判断必须人工 + +**这一条是踩出来的,后来者别再犯。** 最初的做法是让脚本按正则规则自动判断并批量替换, +人工事后 review。结果: + +- 把代码标识符改了(`buildDialogueWorkspaceDir` → `buildSessionWorkspaceDir`,靠 typecheck 才暴露) +- 把 OpenAI 协议名 `Chat Completions` 改了 +- 把动词改坏了(`you chat from here` → `you session from here`) +- 把飞书的「群/单聊」改成了「任务」(`List recently-active chats`) +- 把 `Subsequent messages will be discarded` 改成「后续**任务**会被丢弃」,而且 5 条同类文案 + 改得互相不一致 + +抽查命中率约 **9%**(前 142 条查出 13 处错),也就是说事后 review 一旦看漏,错的文案就直接 +到用户眼前。 + +**正确流程**:脚本只负责**列出候选**(key + 英文原文 + 当前中文),逐条人工判断要不要转、 +转成什么,只写入确认的那些。**默认不转。** 最终 565 条里有 87 条判定为保持「对话」—— +这 87 条正是自动规则最容易改错的地方。 + +即便如此仍有漏判,而且**逐条看也没能全部拦住**:§2.3 那类归属分类名(21 处)是实机跑 +起来目检截图才发现的。所以除了逐条判断,落地必须再叠三层: + +1. **中英成对比对**(§4.2)——抓出 13 处 +2. **同一区块的兄弟条目一起看**——IM 那 5 条 channelDescriptions 只有并排看才能发现 + 「已有私聊发送 /new」证明主语是平台侧 +3. **实机目检**——分类名这类错误在代码和测试里都是合法字符串,只有跑起来才看得出 + +顺带被这几层抓出两处**句子截断**(改写时丢了「使用该模型。仍要切换吗?」和「默认开启。」), +与术语无关,纯属编辑事故。检测手段是:把新旧值里的术语词都替换成占位符后比对,剩余 +部分不同的就是夹带了非术语改动。 + +同时修掉了两批**既有译名错误**(与改名无关,本来就该修): + +- **42 处**英文是 `message`、中文却译成「对话」(`"Delete this message"` → 「删除本条对话」) +- **7 处**英文是 `turn`、中文也译成「对话」 + +**没有改的**:代码标识符、`SSH 远程会话` / `Agent 进程会话` / `登录绑定会话` / +`操作系统桌面会话`(那是另一类 session,与产品概念无关,见 §6.0.2)。 + +#### 6.0.1 只扫 locale 文件会漏掉一整类:代码里的硬编码文案 + +改术语时最容易漏的不是判断错,是**根本没扫到**。本仓有一批面向用户的中文写在代码里、 +不走 i18n,只扫 `locales/**` 完全看不见它们。落地时漏了 **11 处**,全靠 review 才发现: + +| 位置 | 漏掉的文案 | 后果 | +|---|---|---| +| `apps/mobile/src/session/messageActionMenu.ts` | 「在新对话中继续」「复制当前对话链接」「删除本条对话」 | 手机长按消息的操作表整套还是旧术语 | +| `apps/mobile/app/sessions/[sessionId].tsx` | Fork 确认框三句 | 同一流程里桌面说「任务」、手机说「对话」 | +| `apps/desktop/src/main/applicationMenuLabels.ts` | 影子 catalog 的 `newMaker`(四语) | macOS File 菜单与 renderer 入口直接冲突 | +| `apps/desktop/src/main/hook-control/interactions.ts` | 「本对话总是允许」 | 同一个权限请求,App 里说「任务」、Slack 卡片里说「对话」 | +| `apps/desktop/src/main/hook-control/dispatcher.ts` | 两条 IM 通知 | 换任务的说明还在说「原对话」 | +| `apps/desktop/src/main/im/{telegram,wechat}/*` | `/new` 反馈与命令帮助 | IM 侧术语与桌面端不一致 | +| `packages/maker-core/src/agents/codex/index.ts` | 记忆 feature 的 description | 设置页里的说明还是旧术语 | + +#### 不变量与文案来源的全部对称路径 + +一句话不变量:**面向用户的中文里,指「那个能单独打开/删除/归档/重命名的东西」时一律说 +「任务」。** 落地时它被违反了三轮,每轮都是因为**漏了一条来源路径**,不是判断错。所以把 +来源枚举完整,改术语时逐条过: + +| # | 来源 | 覆盖办法 | +|---|---|---| +| 1 | `apps/desktop/src/renderer/i18n/locales/**` | diff locale | +| 2 | `apps/mobile/src/i18n/locales/**` | diff locale | +| 3 | `apps/desktop/src/main/**` 硬编码 | 代码搜索(IM 通知、菜单影子 catalog、权限卡、IPC 错误) | +| 4 | `apps/mobile/{src,app}/**` 硬编码 | 代码搜索(操作表、Alert 确认框) | +| 5 | **`packages/maker-shared/**` 硬编码** | 代码搜索。**最容易漏**:mobile 首页与设备详情的空态/筛选/副标题全在这里,不在 locale | +| 6 | `packages/maker-core/**` 硬编码 | 代码搜索(agent capability 的 displayName / description) | +| 7 | `packages/lizi-mcps/**` | **不改**:MCP 元数据是给 LLM 读的,不是 UI | + +**判据要同时搜两个词**:「对话」和「**会话**」。只搜「对话」会整片漏掉——共享层里那批空态 +写的是「还没有会话」「活跃会话」「未命名会话」,一个「对话」都没有。(「会话」是 `session` +条目的 forbidden 译法,出现在产品语境就是问题;属于 §6.0.2 那几类另一种 session 时才保留。) + +**搜索模式也要覆盖拼接与模板字符串**。第一次只搜 `xxx: '…会话…'` 形式,漏掉了 +`` `${n} 个会话` ``、`` `持续会话 ${id}` `` 这类,靠测试断言里的实际值才发现: + +```bash +# 宽口径:先不限模式看全量,再逐条判断 +grep -rn '会话\|对话' packages/maker-shared/src/*.ts | grep -vE ':[0-9]+:\s*(\*|//)' +``` + +**所以候选清单必须包含代码搜索**,不能只 diff locale 文件: + +```bash +# 面向用户的硬编码中文(排除注释与测试) +grep -rnE "(label|text|title|message|placeholder|subtitle|desc|Description):\s*'[^']*<词>" \ + --include='*.ts' --include='*.tsx' apps/ packages/ | grep -vE '__tests__|\.test\.' +``` + +同时留意**影子 catalog**:为了不依赖 renderer i18n 而单独维护一份文案的文件,天然会和 i18n +漂移。本仓已知**两处**,改术语时都要过: + +| 影子 catalog | 为什么存在 | 漂移后的后果 | +|---|---|---| +| `apps/desktop/src/main/applicationMenuLabels.ts` | macOS 原生菜单在 renderer 起来前就要有文案,四语自带 | File 菜单与 renderer 入口叫法冲突 | +| `apps/desktop/src/shared/agentIsland.ts` 的 `DEFAULT_AGENT_ISLAND_STRINGS` + `native/agent-island/macos-agent-island-helper.swift` 的 `AgentIslandStrings.fallback` | Agent Island 是独立 helper 进程,冷启动时还没收到 main 的本地化状态 | **冷启动/回退态**露出旧名。落地时这两处一直停在 `New Chat` 和 `New Maker`(后者是更早的品牌名),只有拿到 main 状态后才显示新名 | + +穷尽这一类的办法是**在 locale 之外搜英文旧标签**(而不是搜代码里的中文): + +```bash +grep -rniE "'(New Chat|New Maker|New Conversation|Copy conversation link)'" \ + apps packages --include='*.ts' --include='*.tsx' --include='*.swift' | grep -v i18n/locales +``` + +命中结果里要分清两类:**影子 catalog 的默认值必须改**;而 `'New Maker'` 作为**数据库默认 +标题的哨兵**(`deviceLinkCreateArgs.ts` 写入、`isDefaultDraftSessionTitle` 判定、swift 侧 +`normalized != "new maker"`)**绝对不能改**——`projectDraftSessionTitle` 那一整套机制的存在 +意义就是把这个哨兵挡在用户视线之外,改了它等于让哨兵检测全部失效。 + +#### 6.0.2 英文的 `session` 在本仓是五个概念,中文只有它们必须分开 + +**这是本次改名一半返工的根因,先读这一节再动任何含 session 的文案。** + +英文用一个 `session` 指五件不同的事,`ja` / `ko` 跟英文一样一词通用——**只有中文被迫二分**, +所以只有中文会译错,而且英文源里没有可供机器判别的信号: + +| 概念 | 中文 | 术语表条目 | 例子 | +|---|---|---|---| +| 产品条目(可单独打开/删除/分享) | **任务** | `session` | 侧栏那一行、`/session` 列表、fork 产物 | +| **Agent / 引擎运行时会话**(SDK Query 生命周期) | **Agent 会话** | **`agent-session`** | 设置生效范围、`Session is inactive`、`Codex session state` | +| 登录/绑定校验会话 | 会话 | `session` 的 `exempt` | `The verification session expired` | +| SSH 远程与传输会话 | 会话 | 同上 | `not supported in SSH remote sessions` | +| 操作系统桌面会话 / mobile 语音连接 | 会话 | 同上 | `the current desktop session`、`voice.sessionNotConnected` | + +**改名前这五个概念挤在一个 `session` 条目 + 一条 39 项路径白名单里**,白名单只写了「这里允许 +出现『会话』」,没写「这里指的是哪一种」——于是逐条判断时没有可对照的名字,落地过程里 +反复把运行时会话译成「任务」(`memory` / `computerUse` / `mcp` / `forkErrors` / `rewind` 各一轮)。 +2026-08-01 把最大的那一类单独立成 **`agent-session`** 条目(占白名单 22/39,也是唯一一类 +译错会**改变用户行为**的),并加了机器规则。 + +**门禁能拦住的部分**:`agent-session` 的 `forbidden` 是「英文源字面写了 `agent session` 时, +中文不许出现『任务』」——精确、当前零误报(唯一的合法混用 `forkErrors.unsupportedHistory` +已登记 exempt)。已实测:把 `computerUse.*.toggleHint` 改回犯错时的写法,门禁报 3 处违规。 + +**门禁拦不住的部分**(必须人读英文源):英文只写 `session` 的那批——`The Codex session state`、 +`Session is inactive`、`in-flight sessions are unaffected`。**英语本身不区分这两个意思, +同一句里两个所指并存还是合法的**,任何词形规则都会误伤。这类只能靠下面三条判据。 + +判据一:**这句话会不会让用户去做一个多余的动作?** 会(「新建任务才生效」)→ 说 Agent 会话。 + +这条判据是踩出来的:落地时把 `settings.memory.agent.toast.takesEffectSuffix` 改成了「在新任务中生效」,但代码 +写着 `setMemory 只更新 memoryOverride, 影响下次 buildQuery (新 session / rewind 重启)`: +**同一条任务里 rewind 也会生效**,说「新任务」会让用户白开一个任务、还割裂了上下文。同页 +同机制的兄弟文案(`lspMode.toast.*`、`builtinTools.toast.*`)早就写着「Agent 会话」并登记 +在 `session` 条目的 `exempt` 里——一致性证据就在隔壁,却没去看。 + +**先确认延迟是否真的存在。** 判据一不是把所有英文的 `new session` 机械翻成「Agent 会话」: +必须沿实现追到设置的读取时机。只在 `buildQuery`/SDK Query 启动时读取的设置,才说「下一个 +Agent 会话」;调用时现查的设置会立即生效,不该要求用户重启任何会话。项目插件开关属于后者: +renderer 的 `$` 指令展开、`ghost_list` 与 `ghost_call` 都按工作目录现查;旧 Agent 会话即使仍有 +工具描述里的花名册快照,实际调用也会被 `GHOST_DISABLED_IN_WORKDIR` 拒绝。因此 UI 只说 +「已停用/不可用」,不写「新任务生效」,也不写「新 Agent 会话生效」。英文源若把生效时机写错, +同样应按实现修正四语,不能把错误事实忠实翻译过去。 + +判据二:**同一句里前后两半必须在同一个概念上**——比第一条更容易踩。落地时把 +`computerUse.{browser,android,directControl}.toggleHint` 写成「只对之后新建的 **Agent 会话** +生效;已在运行的**任务**不受影响」——前半句说运行时、后半句跳到产品条目,用户读完不知道 +是该重启当前任务还是新建一个。对照 en / ja / ko:三语的两个半句都用同一个 session +(`does not affect a session that is already running` / `実行中のセッション` / `진행 중인 세션`), +只有 zh 在句中换了概念。已改回「已在运行的 **Agent 会话**不受影响」。 + +自查办法:把 `session` 条目 `exempt` 列表里的 zh 值全打出来,看有没有同句既含「会话」又含 +「任务」的。**同句混用不等于错**——`chat.backgroundActivity.stopBashTitle`(「停止该**任务**仍在 +运行的后台作业(不关闭**会话**进程)」)和 `forkErrors.unsupportedHistory`(「历史引擎**会话** +已不可用,无法从这里开启新**任务**」)都是两个词各指其物、句意清晰,刻意保留。要看的是 +**同一个所指被换了两种叫法**。 + +判据三:**两个词都不合适时,把名词绕开。** `maker-core` 两个 agent 的 memory +`description` 上,reviewer 给出过相反建议——一边说「新会话」违反 forbidden 译法、该写 +「新任务」,一边说写「新任务」会让用户以为必须新建任务。两边都对:这个 capability 描述的是 +「记忆之后会被自动召回」,而它落在 `maker-core` 里、**不是 locale 文件,没法登记 +`exempt`**。所以改成「从对话中生成新记忆并在**后续对话**中召回」——用户视角真实、不含禁用 +词、也不暗示任何多余动作。**遇到 reviewer 意见相反时,先看是不是这个名词本身选错了。** + +#### 6.0.3 `/new` 不产生新任务:按落库行为定名,不按命令名字 + +IM 渠道的 `/new` 看着像「新建」,实际走 `im/shared/sessionRepo.ts::resetSessionToDefaults` —— +`db.update(sessions).where(eq(sessions.id, …))`:**保留同一条 session 行**,只清 `sdkSessionId`、 +按渠道默认重置工作目录与上下文。侧边栏里的条目数不变,也没有新条目可以单独打开。 + +所以凡是描述这条路径的中文,一律说**「新对话」**。说「新任务」会让用户去侧边栏找一条根本不 +存在的新条目。这条不变量的全部对称路径: + +| 触点 | 底层 | 说法 | +|---|---|---| +| IM 平台**首次**私聊 | `INSERT` 新 session 行 | 确实是新任务 | +| 已有私聊发 `/new` | `UPDATE` 原行 | **不是**新任务 | +| 消息操作菜单 fork | `INSERT` 新 session 行 | 确实是新任务(「开启一个新任务」) | +| `/ctr` → ➕ 新建 | `INSERT` 新 session 行 | 确实是新任务 | + +前两条**共用同一份设置、同一句文案**,所以文案必须取两者都成立的说法 = 「新对话」。落地时 +这一族被指出过三轮,每轮漏一处:`settings.imBot.defaults.*`(设置项标题)→ +`settings.wechatBot.workingDir.*` 与 `toasts.workingDir*`(工作目录那组)→ +`settings.telegramBot.commandMenu.new`(Telegram 斜杠菜单说明)。**判据一律回到落库行为。** + +英文侧同理:`Start a new session` 也不准确,已一并改成 `Start a new chat`(ja「新しい会話を +開始」/ ko「새 대화 시작」)。en 的 `session` 词义比中文「任务」宽、读起来不刺眼,但既然中文 +已按落库行为定名,四语就该说同一件事(§4.2)。 + +#### 6.0.4 兄弟渠道必须成组改:一处 IM 文案改动 = 四个包一起过 + +`apps/desktop/src/main/im/{telegram,discord,feishu,wechat}/uiText.ts` 是四份**近亲文案包** +(结构同形、大量句子逐字相同;刻意不共享,以免渠道措辞互相串味)。落地时只改了 telegram, +于是后面每轮 review 点出一个兄弟包——典型的逐条打补丁。 + +**规则:改任一渠道的 IM 文案,必须同一轮把四个包一起过。** 判据不是「reviewer 点了哪个」, +而是「这句话在别的包里有没有同形副本」。 + +配套要区分**不跟改**的「会话」。本轮枚举全仓硬编码字符串共 102 处含「会话」,按读者归类: + +| 类别 | 例子 | 处理 | +|---|---|---| +| 渠道直接发给用户 | 四个 `uiText.ts` | **改**(已清零) | +| main 抛给 renderer 的 `Error` message | 见下方「一句一句追展示路径」——**按 code 映射 i18n 的不改,原样展示的必须改**,不能按文件一刀切 | +| 插件 / MCP / LLM 读的字符串 | `cindy-brain/*Slot.ts`、`mcp-integrations/ghost.ts` | **不改**:不是 UI 文案,且多数确实指 Agent 进程会话(§6.0.2) | +| Orca 协同术语 | 架构代码与内部标识继续用 session;`packages/maker-shared/src/sessionIdentity.ts` 返回的手机端只读提示会直接展示给用户 | **按展示路径分开**:架构术语不改,用户可见的 Orca Lead / Worker / 协作条目仍叫「任务」(含「协作子任务」「任务写操作」) | +| 测试 fixture | `packages/maker-shared/src/fixtures.ts` | **不改** | + +另有一类**刻意保留的渠道人格用词**:discord / feishu / wechat 把 session 叫「存档」、把接管 +叫「上号」(`feishu/uiText.ts` 文件头写明了这个基调:「偶尔带点游戏味的隐喻(上号、存档、 +副本、AFK 之类)」)。那不是错译而是渠道语气,本次只清 forbidden 词「会话」,不动「存档」。 + +#### 一句一句追展示路径,别按文件归类 + +上面那张表最初把「main 抛给 renderer 的 `Error` message」整类判成不改,理由是 fork / rewind +确实按 error code 渲染 i18n(`chat.userMessage.forkErrors.*` / `chat.rewind.errors.*`),main 侧 +那串只进日志。**这个推广是错的**:`maker-ipc/sessionReferenceResolver.ts` 抛的 +`SessionReferenceError` 经 `agent-input-coordinator.ts::resolveReferenceContexts` 直接落进 +`projection.error`,`CCAgentSessionView` 的 `ErrorBanner` **原样展示**(同文件测试 +`expect(latestProjection(...).error).toContain('snapshot is missing')` 就钉着这条链路)。于是 +用户粘贴「任务链接」后失败时看到的是「找不到本机会话 …」「最多引用 N 个会话」。 + +**判据只有一条:这句话会不会原样出现在用户眼前。** 回答它必须追一次展示路径,不能靠文件目录 +推断。落地时连续四轮返工,每轮都是漏了一条**展示路径**(不是漏了一个目录): + +| 轮次 | 漏掉的路径 | 为什么没扫到 | +|---|---|---| +| 1 | `packages/maker-shared/**` | 只 diff 了 locale 文件 | +| 2 | IM 渠道 `uiText.ts` | 共享层扫完就以为齐了 | +| 3 | discord / feishu / wechat(telegram 的同形副本) | 按 reviewer 点到的那个文件改 | +| 4 | main 抛错 → `projection.error` → `ErrorBanner` | 按文件类别一刀切,没追这一条的实际渲染 | +| 5 | `notificationService.buildFeishuText` → 飞书私聊 | 前四轮都在追「报错」,没想到**成功态通知**也是一条 | +| 6 | `dispatcher.ts` / `session-runner.ts` 的映射守卫 → `turn.end.errorMessage` → 渠道 | 出口清单按**函数名**枚举,看不见「文案当返回值逐层传出」;且反向扫描时只搜了「会话」,没搜「对话」 | +| 7 | `apps/desktop/help-knowledge/*.md` → Help Assistant | 只按 reviewer 点到的那一行改(先是 `collaboration.md`,再是 `sidebar.md`),没把 24 篇一次扫完 | + +**帮助内容(`help-knowledge/*.md` → `helpKnowledge.generated.ts`)要单独扫一遍。** 它是 +Help Assistant 的语料,改术语时最容易忘,而且它大量**引用界面标签原文**——标签改了、帮助 +没改,用户就被指向一个不存在的菜单项。改完必须 `pnpm --filter desktop gen:help-kb` 重新生成, +再跑 `gen:help-kb:check` 确认同步。 + +```bash +# 找出帮助里引用了旧标签的地方(逐条对照当前 en locale 的真实标签) +grep -rniE 'conversation|dialogue' apps/desktop/help-knowledge/*.md +``` + +判据仍是「它引用的是**界面标签**还是**交流内容**」:`Copy conversation link` / +`by dialogue`(都是筛选项与菜单项的旧名)必须改;`The conversation history stays searchable` / +`Memory across conversations` / `rewinds the conversation` 指内容,保持不动。 + +所以改术语时,对每一处候选串问:**它从哪里被渲染出来?** 三种答案对应三种处理—— +renderer 按 code 取 i18n(不改这串,改 i18n key)/原样进 UI(必须改)/只进日志或给 +LLM(不改)。拿不准就搜一次调用链,代价比多一轮 review 低得多。 + +##### main 侧「直接送到用户眼前」的出口清单 + +连续五轮都是同一族返工,所以第 5 轮做了检查点:不再逐条等 reviewer 点,把 main 侧的出口 +(sink)一次枚举干净。判据不是文件在哪个目录,而是**这个函数把字符串送去了哪里**。 + +| 出口 | 送到哪 | 本次处理 | +|---|---|---| +| `im/{telegram,discord,feishu,wechat}/uiText.ts` | 渠道消息 / 卡片 | 四包已清零(§6.0.4) | +| `notificationService.buildFeishuText()` | 飞书私聊完成通知 | 「会话「X」」→「任务「X」」;测试断言同步 | +| `notificationService.showDesktopSessionEvent()` 的 `safeTitle` 兜底 | 系统 toast 标题 | `'会话'` → `'任务'` | +| `im/desktopConfirmNotice.buildDesktopConfirmNoticeText(what)` | 飞书私聊确认提醒 | 3 个调用点,只有「批量重命名会话」含术语词 → 改 | +| `hook-control/interactions.ts` · `dispatcher.ts` | Slack / IM 权限卡与通知 | 已改(§6.0.1) | +| `SessionReferenceError` → `projection.error` → `ErrorBanner` | 对话区错误横幅 | 14 处已改 | +| `device-link/ipc.ts` 的 `throwIpcError(SESSION_REFERENCE_*)` | 同上(`ipcError.*` 里**没有**这两个 code 的 i18n,所以原样展示) | 3 处已改 | +| `ORCA_WORKER_READY_MESSAGE` | 注入 worker 对话流的系统消息 | 「Orca Worker 会话已就绪」→「Orca Worker 已就绪」(去掉冗余名词,不动 Orca 自己的 Worker / Lead 术语) | +| `scheduler-host/notifier.ts` | 自动化飞书卡片 | 无术语词,不动 | +| `cindy-brain/*Slot.ts` · `mcp-integrations/ghost.ts` · `maker-ipc/ghostErrandRunner.ts` | 插件 / LLM 读 | 不动(§6.0.2) | +| `maker-orchestration/{fork,rewind}.ts` | renderer 按 error code 取 i18n | 不动(改的是 i18n key) | +| `device-link/mediaFetch.ts` · `file-browser/ssh-media.ts` | SSH 连接会话,另一种 session | 不动(§6.0.2) | + +复现这份清单的办法(改术语时先跑一遍,比等 review 便宜): + +```bash +# 1. 找出 main 侧所有「送给用户」的出口调用点 +grep -rnE 'sendMarkdownText|sendFeishuText|desktopConfirmImNotifier|showDesktopToast|new Notification' \ + apps/desktop/src/main --include='*.ts' | grep -v __tests__ +# 2. 反向:列出所有含术语词的字符串字面量,逐条回答"它从哪个出口出去" +# ⚠️ 两个词都要搜。只搜「会话」会漏掉「这个对话所在的目录…」这类同样指条目的错句。 +grep -rnE '会话|对话' apps/desktop/src/main apps/mobile/src packages/*/src \ + --include='*.ts' --include='*.tsx' | grep -vE '__tests__|:[0-9]+:\s*(//|\*)' +``` + +**第 1 步(按出口函数名 grep)单独用不够**:有些文案不是被出口函数直接调用,而是**当返回值 +逐层传出去**的。`hook-control/dispatcher.ts` 的映射守卫把文案塞进 `outcome.errorMessage`, +经 `turn.end.errorMessage` 才到渠道用户;`session-runner.ts` 的 `fail(...)` 同理。grep 出口 +函数名永远看不到它们——只有第 2 步的反向扫描能。**两步是互补的,不能只做一步。** + +**顺带修掉一处 `origin/main` 既有乱码**:`sessionReferenceResolver.ts` 有两句 +`来源设备返回了无效的会话历史` 被存成了 UTF-8 字节按 GBK 解过一遍的形态 +(`鏉ユ簮璁惧杩斿洖浜嗘棤鏁堢殑浼氳瘇鍘嗗彶`),真触发时用户看到的是乱码。既然本轮要改这一组 +文案,一并修正为「来源设备返回了无效的对话记录」。 + +**禁用词的判定要看条件,不要顺手断言原因。** `sessionActionStrip.ts` 的 +`filesDisabledReason` 原文是「Dialogue 会话没有远程工作目录」,改名时顺势写成「不绑项目的 +任务没有…」,但实际判据只是 `!!session.workingDir`:旧数据或异常行同样会缺 workingDir,被 +这句话解释成「因为任务类型」。文案只陈述状态(「这个任务没有远程工作目录」),不断言原因。 + +#### 6.0.5 注释与测试常量里「陈述现有文案」的地方必须跟改 + +§8 声明内部注释与代码标识符刻意不动,但有一类例外,判据是**这句注释有没有断言「用户看到的 +是 X」**:改名让 X 变了,这类注释就变成了错的,比不改更坏——后来人照注释去「修正」代码, +方向正好相反。落地时被 review 连点两轮(每轮列出不同实例),做检查点后一次扫清 41 处: + +| 类型 | 例子 | 处理 | +|---|---|---| +| 注释断言兜底文案 | `sessionList.ts`「与 desktop 侧的『未命名对话』不一致」 | **改**:实际值已是「未命名任务」(`ccAgent.common.unnamedSession` / `sessionHeader.untitled` / mobile `devices.list.untitled` 实测) | +| 注释断言按钮文案 | `interactions.test.ts`「注:按钮文案用『对话』」,而按钮已是「本任务总是允许」 | **改** | +| 测试常量当兜底文案的替身 | `const UNNAMED = '未命名对话'` | **改**:值是注入的、改了不影响行为,但留着会变成下一个人「修正」代码的依据 | +| 泛指 session 的普通注释 | 「idle 会话直接显示」「worktree 会话按 base repo 归组」 | **不改**(§8) | + +**退役渠道的 fixture 整体不动。** `im/shared/__tests__/threadUiFixture.ts` 保存的是 2026-07-17 +退役的 SlackIM 文案包原件(文件头写明「不进产品包」),它的价值正是「忠实副本」。落地时误改了 +其中 4 句,把「开一个新会话」和「同一个任务」并列进同一句;已整体回退到 `origin/main`。 + +### 6.1 门禁怎么表达「同一个词分场合」 + +**不能简单把「对话」加进 `Session` 的 `forbidden`**——那会把「对话区」「对话正文」这些 +合法用法一并拦下。用 `alsoAllowed` + `when` 按语境豁免(写法参照 `glossary.json` 里 +`thread` 条目,它按语境分了四类): + +```jsonc +{ + "id": "session", + "translations": { "zh-CN": "任务" }, + "alsoAllowed": { + "zh-CN": [ + { "text": "对话", "when": "指任务内人与 AI 交流的过程或其内容(对话区、对话正文、对话记录、继续对话)" } + ] + }, + "forbidden": { "zh-CN": ["会话"] } +} +``` + +`Message` 的 forbidden 同样收窄到**精确的错译形式**「条对话」,而不是笼统禁「对话」—— +一句话里可能既有 `message` 又有合法的「对话X」,机器分不清哪个词对应哪个,笼统禁会误报。 + +门禁设计不到位会天天误报,最终没人看——这一层的成本最容易被低估。 + +## 7. 已知代价 + +**两个月内第二次大规模改名。** `glossary.json` 里 `Session` 条目原先的备注记录着上一次 +裁决:「落地时把 **440 处「会话」+ 22 处「聊天」统一改为「对话」**」(2026-07)。 + +再改一次,影响的不只是代码——已发布的截图、教程、帮助内容,以及用户已经形成的认知都要 +跟着变。这是本次改名唯一需要产品明确承担的成本,已确认接受。 + +## 8. 遗留 + +- **侧栏全局「新建」入口不在本次范围**:`ccAgent.layout.new` 仍是「新建」/`New`。改成 + 「新任务」/`New Task` 由 PR #951 承担(它同时重构该入口的快捷卡片与一键启动),本文只 + 确认命名方向一致,不抢那个 key。若 #951 迟迟不合,这个按钮会和周围已改的「新任务」措辞 + 不齐,届时再单独补 +- **`task` 未全量改写**:不同句、语境清晰时仍用「任务」(104 处)。只在与 session 同句时 + 强制消解,见 §3.1。后续若发现新的歧义句,按同一原则改写即可 +- **IM 渠道的「存档 / 上号」人格用词未统一**:discord / feishu / wechat 仍把 session 叫 + 「存档」(见 §6.0.4)。本次只清了 forbidden 词「会话」,因此这三个渠道会同时出现「任务」 + 与「存档」。要不要把「存档」也收敛成「任务」是语气取舍而非错译,留给产品单独裁决 +- **共享层的部分中文仍是硬编码**(reviewer 在本次 review 里点了 4 轮,这里把清单记全): + + | 位置 | 谁直接渲染它 | + |---|---| + | `maker-shared/src/sessionList.ts` 的 `remoteSessionListTitle` / `deviceSessionEmptyState` | mobile 设备详情页 `app/devices/[deviceId].tsx` | + | `maker-shared/src/sessionSelection.ts` 的 `summarizeMobileSessionBulkAction`(title / description / confirmText) | 同上,批量操作确认卡 | + | `maker-shared/src/scheduleModel.ts` 的 `summarizeRun`(按钮与状态 label) | mobile `app/automations/[deviceId].tsx` | + + 这些函数在业务代码里直接返回中文串,而调用它们的页面其它字段都走 i18n,于是 en / ja / ko + 环境下会出现「一半翻译一半中文」。**这是 `origin/main` 既有状态**,也正是 §6.0.1 那份来源 + 清单第 5 条要专门代码搜索的原因。 + + **修法不是改用词,而是把共享层改成不产出文案**(调用方传 `t` / strings,或函数只返回计数与 + 结构化数据)。那要动 3 个模块 + 各自调用点 + 测试,属独立重构,不在本次改名范围;本 PR 只 + 保证这些中文串与桌面端同款 key 用词一致。同目录的 + `apps/mobile/src/session/messageActionMenu.ts` 与 `app/sessions/[sessionId].tsx` 的 fork 弹窗 + 已在本 PR 内迁到 i18n——它们是**页面自己的文案**、改法就是照抄隔壁,与共享层这一类不同。 + (`apps/mobile/src/session/messageActionMenu.ts` 原本也在此列,本次已改为走 + `i18n.t('session.messageMenu.*')`——同目录 `sessionMenu.ts` 早就是这个写法,它是唯一的例外) +- **mobile 的 locale 没有 key 对齐门禁**:`pnpm check:i18n` 只校验 + `apps/desktop/src/renderer/i18n/locales//common.json`(见脚本头部注释), + `apps/mobile/src/i18n/locales/**` 的 15 个 json 不在其中。本次新增 mobile key 时是手工核对 + 四语一致的(各 235 个 key)。把门禁扩到 mobile 是独立改动,可能会暴露一批既有不齐 +- **代码标识符与内部注释仍以 Session 为主**:刻意不动。仅当中文注释被测试当作源码锚点、 + 且新旧术语冲突时才跟进(本次有 1 处:`apps/mobile/app/sessions/new.tsx` 的 + 「新建任务默认运行配置」) diff --git a/i18n/GLOSSARY.md b/i18n/GLOSSARY.md index 92c6eaaa653..a145b3e2a26 100644 --- a/i18n/GLOSSARY.md +++ b/i18n/GLOSSARY.md @@ -27,6 +27,7 @@ | --- | --- | --- | --- | --- | | **Active** | 活跃 | アクティブ | 활성 | zh-CN: 活动(仅当英文含 Active) | | **Agent** | `Agent`(保留英文) | `Agent`(保留英文) | `Agent`(保留英文) | zh-CN: 智能体;zh-CN: 代理(仅当英文含 Agent) | +| **Agent session** | Agent 会话 | Agent セッション | Agent 세션 | zh-CN: 任务(仅当英文含 agent session) | | **Author** | 作者 | 作成者 | 작성자 | zh-CN: 提交者(仅当英文含 Author);zh-CN: 提出者(仅当英文含 Author) | | **Automation** | 自动化 | 自動化 | 자동화 | ja: オートメーション;ko: 오토메이션 | | **Balance** | 余额 | 残高 | 잔액 | zh-CN: 额度(仅当英文含 Balance) | @@ -38,19 +39,23 @@ | **Dismiss** | 关闭 | 閉じる | 닫기 | zh-CN: 忽略(仅当英文含 Dismiss);zh-CN: 知道了(仅当英文含 Dismiss) | | **Effort** | 推理强度 | 推論強度 | 추론 강도 | zh-CN: 思考强度;zh-CN: 思考深度;zh-CN: 推理深度;zh-CN: 努力程度;zh-CN: 推理力度;ja: 推論レベル;ja: 推論の強度 | | **Issue** | `Issue`(保留英文) | `Issue`(保留英文) | 이슈 | zh-CN: 工单;zh-CN: 议题;ja: 課題(仅当英文含 Issue) | +| **Message** | 消息 | メッセージ | 메시지 | zh-CN: 条对话(仅当英文含 Message) | | **Plugin** | 插件 | プラグイン | 플러그인 | zh-CN: Plugin;zh-CN: plugin | | **Project** | 项目 | プロジェクト | 프로젝트 | zh-CN: Project | | **Provider** | 供应商 | プロバイダー | 제공자 | zh-CN: 提供方;zh-CN: 提供商;ja: 提供元(仅当英文含 Provider);ja: 事業者(仅当英文含 Provider);ko: 공급자;ko: 제공업체 | | **Proxy** | `Proxy`(保留英文) | プロキシ | 프록시 | zh-CN: 代理(仅当英文含 Proxy) | | **Quota** | 配额 | クォータ | 할당량 | ja: 残量;zh-CN: 额度(仅当英文含 Quota) | | **Running** | 运行中 | 実行中 | 실행 중 | zh-CN: 执行中(仅当英文含 Running);zh-CN: 进行中(仅当英文含 Running) | -| **Session** | 对话 | セッション | 세션 | zh-CN: 会话 | +| **Session** | 任务 | セッション | 세션 | zh-CN: 会话 | | **Skill** | 技能 | スキル | 스킬 | ja: 技能;ko: 기술 | | **SkillHub** | `SkillHub`(保留英文) | `SkillHub`(保留英文) | `SkillHub`(保留英文) | ja: スキルハブ;ko: 스킬 허브 | | **Subagent** | `Subagent`(保留英文) | サブエージェント | 하위 에이전트 | zh-CN: 代理(仅当英文含 Subagent) | -| **Thread** | 对话 | スレッド | 스레드 | zh-CN: 线程 | +| **Task** | 任务 | タスク | 작업 | — | +| **Thread** | 任务 | スレッド | 스레드 | zh-CN: 线程 | +| **Turn** | 轮 | ターン | 턴 | — | | **Usage** | 用量 | 使用量 | 사용량 | zh-CN: 使用情况;zh-CN: 使用表现;ja: 使用状況;ja: 利用状況;ko: 사용 현황 | | **Worker** | `Worker`(保留英文) | `Worker`(保留英文) | `Worker`(保留英文) | — | +| **Working directory** | 工作目录 | 作業ディレクトリ | 작업 디렉터리 | zh-CN: 任务空间;zh-CN: 工作空间 | | **Worktree** | worktree | worktree | worktree | zh-CN: 工作树(仅当英文含 Worktree);zh-CN: 工作区(仅当英文含 Worktree) | ### 分场合译法 @@ -62,8 +67,6 @@ | **Active** | zh-CN | 活跃 | 默认 | | Active | zh-CN | 生效中 | 计费语境("no active subscription"→没有生效中的订阅),指订阅状态而非活跃度 | | **Automation** | zh-CN | 自动化 | 默认 | -| Automation | zh-CN | 自动任务 | 指单条自动化实例(一个定时跑的任务)时 | -| Automation | zh-CN | 自动化任务 | 需要与模块名区分、强调它是一条任务时 | | Automation | zh-CN | 自动操作 | Computer Use 语境——「自动操作电脑 / 浏览器」,指代操控设备而非定时任务 | | **Credits** | zh-CN | 点数 | 默认 | | Credits | zh-CN | credits | 指第三方服务自身的 credits(如 Codex / ChatGPT 订阅余量),保留其原生叫法 | @@ -72,9 +75,11 @@ | Provider | zh-CN | 模型供应商 | 首次出现或需要消歧时用全称,如设置页标题 | | **Running** | zh-CN | 运行中 | 默认 | | Running | zh-CN | 正在运行 | 完整句子里作谓语时("Agent 正在运行"),不是状态标签 | +| **Session** | zh-CN | 任务 | 默认 | +| Session | zh-CN | 对话 | 指任务内人与 AI 交流的过程或其内容(对话区、对话正文、对话记录、继续对话) | | **Skill** | zh-CN | 技能 | 默认 | | Skill | zh-CN | Skill | 技术语境下指代具体的 skill 文件 / 目录结构时,可保留英文 | -| **Thread** | zh-CN | 对话 | 默认 | +| **Thread** | zh-CN | 任务 | 默认 | | Thread | zh-CN | thread | 指 Codex 的 thread 这一外部系统概念时(导入历史、thread 上下文),保留英文 | | Thread | zh-CN | 评论 | 指 GitHub PR 的 review thread 时,用「review 评论」 | @@ -84,11 +89,13 @@ - 豁免范围:`desktop:settings.computerUse.directControl.permissions.windowsHint` - **Agent** — 产品一级概念,四语统一保留英文原词。zh-CN 现状 44:5 已是主流;ja / ko 实测拉丁 Agent 同样是主流(59:38 / 58:39),故三语一致(2026-07 修正:此前误按抽样声明为片假名 エージェント / 한글 에이전트,那是少数派)。「代理」一词在中文 UI 里整体废弃——Agent 与 Proxy 都保留英文,不共用这个译名;「代理」按英文源分别登记在 agent / subagent / proxy 三条下(条件禁用),不再统一挂 proxy——统一登记会让自动替换无法确定目标:2026-07 一次批量重放就把 "Agent"→「代理」的 4 处和 "Subagent models" 的 4 处一并换成了 Proxy,产出「子 Proxy 模型」这种错译。引入术语表时全仓仅 9 处「代理」,已全部清理为 Agent / Subagent / Proxy。豁免 settings.remote 整段:那里的 agent 指 ssh-agent(英文原文即 "SSH agent" / "probe agent"),是与产品 Agent 同形的另一个概念。 - 豁免范围:`desktop:settings.remote.` +- **Agent session** — Agent / 引擎的**运行时**会话(SDK Query 的生命周期),不是 session 条目说的那个产品条目。中文必须叫「Agent 会话」而不是「任务」——两者的区别是用户会不会因此做一个多余的动作:记忆、MCP、内置工具、LSP、权限开关这类设置都在「下一个 Agent 会话」生效,而同一条任务里 /clear、rewind、重启都会开新的 Agent 会话,说「新建任务才生效」会让用户白开一条任务、还割裂上下文。forbidden 规则只覆盖英文源字面写了 agent session 的 key(精确、无误报);英文只写 session 的那批(`The Codex session state` / `Session is inactive` / `in-flight sessions`)**机器判不出来**——英语本身不区分这两个意思,同一句里两个所指并存也是合法的(`The historical agent session ... so a new session can't be started`:前半指运行时、后半指条目,已登记 exempt)。所以这批只能靠人读英文源判断,判据与全部踩坑记录见 docs/product-rules/task-and-conversation-naming.md §6.0.2。session 条目的 exempt 里另有三类同样叫 session 的东西:登录/绑定校验会话、SSH 远程与传输会话、操作系统桌面会话,以及 mobile 的语音连接会话——它们各自稳定、极少改动,故未单独立条目。 + - 豁免范围:`desktop:chat.userMessage.forkErrors.unsupportedHistory` - **Author** — 中文用「作者」(现状 9:4)。典型的同模块自相矛盾:issueTracker 同一个页面里,assignee.labelAuthor 写「提交者」、list.filterAuthor 写「提出者」、visibility.onlyAuthorCanChange 写「作者」——同一个字段三种叫法。条件禁用避免误伤 Committer / Reporter 等其它英文词的正确译法。 -- **Automation** — 模块名 / 能力名用「自动化」(现状 88 处主流);指单条实例时用「自动任务」,这是刻意保留的场景差异——一刀切会写出「查看你的自动化」这类别扭中文。特别注意 Automation 在本产品里指两个不同概念:scheduler 的定时任务,以及 settings.computerUse 的操控电脑能力;后者用「自动操作电脑 / 浏览器」,改成「自动化电脑」反而不通。因此不设 forbidden。(2026-07 修正:ja 原声明 オートメーション 仅 37/121,主流是 自動化;ko 的 자동화 75/121 本就正确。)豁免通讯录授权提示的两个 key:那里指的是 macOS 系统设置里的面板名,日文官方标签是「オートメーション」而非产品术语「自動化」——照产品术语改会让用户按提示在系统设置里找不到对应项,授权恢复路径直接断掉。zh-CN 的「自动化」与 ko 的「자동화」恰好就是各自的 macOS 官方标签,不受影响。 +- **Automation** — scheduler 的模块名、单条实例与能力名统一用「自动化」;不再把「自动任务」「自动化任务」登记为用户可见译法,避免与产品条目 Session(同样叫「任务」)在『删除自动化和任务』等文案里相撞。Computer Use 是另一个概念,用「自动操作电脑 / 浏览器」,改成「自动化电脑」反而不通。因此 zh-CN 只保留「自动操作」这一语境豁免。术语门禁只扫描 locale,不扫描源码注释;历史注释与测试 fixture 的旧称按 docs/product-rules/task-and-conversation-naming.md §6.0.5 逐条判断,不借术语改名机械扩大代码改动。(2026-07 修正:ja 原声明 オートメーション 仅 37/121,主流是 自動化;ko 的 자동화 75/121 本就正确。)豁免通讯录授权提示的两个 key:那里指的是 macOS 系统设置里的面板名,日文官方标签是「オートメーション」而非产品术语「自動化」——照产品术语改会让用户按提示在系统设置里找不到对应项,授权恢复路径直接断掉。zh-CN 的「自动化」与 ko 的「자동화」恰好就是各自的 macOS 官方标签,不受影响。 - 豁免范围:`desktop:settings.contacts.import.permissionDenied`、`desktop:settings.contacts.ipcError.PERMISSION_DENIED` - **Balance** — 账户余额用「余额」。原先被并入「额度」(可用额度 / 额度更新于 / 剩余额度),与 Credits、Quota 混同。Balance 是「账上还剩多少」,Credits 是「可购买消耗的计数单位」,Quota 是「周期内上限」——三者是不同层面的量,中文必须分开。 -- **Chat** — 在本产品里 Chat 与 Session 指同一个东西,中文统一叫「对话」(2026-07 裁决),不再区分「聊天 / 会话 / 对话」三套说法。原先 22 处「聊天」(聊天正文、聊天区、聊天记录…)已一并改为「对话」。ja / ko 保留各自既有译法,两语没有中文这种三词混用问题。 +- **Chat** — Chat 专指任务内人与 AI 交流的过程与内容(对话区 / 对话正文 / 对话记录 / 继续对话),中文「对话」。指那个可打开可删除的条目时用 Session(中文「任务」)——此前 Chat 与 Session 混用同一个中文词的问题已随 2026-07-31 的改名解决:英文侧指容器的 Chat 已改为 Session。「聊天」一律禁用。ja / ko 保留各自既有译法。 - **Collaboration** — Orca 多 Agent 功能名,中文用「协同」(现状 21:2)。可见 bug:设置导航项写「协作」,点进去页面标题写「协同」。条件禁用:settings.contacts 里的「协作经历」是 collaboration history,属另一个语义,英文源不含独立 Collaboration 一词时不判违规。 - 豁免范围:`desktop:settings.contacts.` - **Collapse** — 中文用「收起」(现状 29:7),与反义词「展开」(Expand,29/30 已统一)配对。ccAgent 与 rightSidebar 内部各自「收起 / 折叠」混用,是同模块自相矛盾。条件禁用:「折叠」在别处可能是正常中文,只在英文源含 Collapse 时判违规。 @@ -98,6 +105,7 @@ - **Effort** — 模型的推理投入档位(low/medium/high/…)。用「推理强度」有三重依据:①同类工具 i18n 里断层第一(GitHub 代码搜索共现量级 1452,第二名「思考强度」431);②OpenAI 简中帮助中心即用此词;③跨 provider 中立——Cindy 的 create_worker 用一个 effort 同时映射 Codex reasoning effort 与 Claude thinking 预算,而 Anthropic 官方简中叫「努力程度」、OpenAI 叫「推理强度」,需要不偏向任一方的词。禁用项各有硬理由:「思考深度」「推理深度」会与国内产品普遍的「深度思考」开关撞车,且语义错误(低 effort 是想得少,不是想得浅);「努力程度」在中文里强烈指向人的绩效评价;「推理等级/级别」未入禁用但不推荐——Cindy 已有 model tier 概念,「等级」易被读成模型档位。 - **Issue** — 保留英文(2026-07 裁决)。现状「工单」32 /「议题」4 是既有 bug 的来源:列表按钮写「新建议题」、点进去页面标题写「新建工单」。「问题」不入 forbidden——它在「修复问题和失败」这类泛指语境下是正常中文,不是 Issue 的误译。checkCase 关闭:issue 同时是常用英语单词,prompt 模板里的 "fix the issue" 是正常用法,做大小写约束会制造 9 处假阳性。(2026-07 修正:ko 原声明 Issue 仅 3 处,实测 이슈 45 处才是主流,改随本语言主流;ja 的 Issue 43:2 保持不变。)ja 的「課題」同样是多义词(问题 / 课题),按 whenEn=Issue 条件禁用——插件创建 prompt 里的 "define the problem" 用「課題」是正确的。豁免两个 Jira capability:那里指的是 Jira 自己的记录类型,日文 UI 的既定叫法是「課題」,保留英文反而与用户实际看到的 Jira 界面对不上。产品自身的 Issue 不受影响。 菜单入口(titleBar.menuItems.issues 与系统菜单 labels.issues)刻意不用术语:那里回答的是「点这里能干什么」,邻居全是动作短语(帮助 / 检查更新 / 最新更新介绍),夹一个英文名词既断风格,也让不熟悉 GitHub 的用户不知道该不该点 —— zh-CN 用「问题反馈」、ja 用「フィードバック」(ja 同级项同样全是日文,原先的 Issue 一样孤立)。这不是把 Issue 译成那些词:指代该类对象的位置(页面标题、正文、提示)仍写 Issue,因为点进去就跳 GitHub,名字必须对得上 —— 判据与 ja 的 Jira 豁免同源(跟用户实际看到的外部界面一致)。en 保持 Issues、ko 保持 이슈(各自在本语言里本就是自然的入口词)。 - 豁免范围:`desktop:scheduler.editor.script.capabilityDescs.jira_comment`、`desktop:scheduler.editor.script.capabilityDescs.jira_read` +- **Message** — 对话里的一条往来,用户看到的最小单元。此前有 42 处英文是 message、中文却译成「对话」(如 "Delete this message" → 「删除本条对话」),2026-07-31 一并修正。AI 在一轮里的思考与工具调用最终折叠成一条消息呈现,所以消息与轮次是同粒度的两种切法,不是包含关系。(门禁只精确拦「条对话」这种错译形式,不拦「对话历史 / 对话上下文」——同句里既有 message 又有合法「对话X」时机器无法区分对应关系。) - **Plugin** — 中文一律「插件」,不保留英文原词(现状 94:4)。历史上 Ghost / Plugin 两种叫法并存,对外统一为「插件」。 - **Project** — 中文一律「项目」(现状 119:15)。同一个侧边栏里筛选叫「项目」、批量操作提示叫「Project」是既有 bug。小写 project 不入 forbidden——它常出现在路径与文件名里(project.json)。 - **Provider** — 中文用「供应商」(现状 36:18 领先简称)。全称「模型供应商」在需要消歧的位置仍可用。「提供商」「提供方」是同义漂移,禁用——设置页标题写「模型供应商」、它自己的表单写「提供商」是既有 bug。豁免 ssoVerificationSubtitle:那里的「身份提供方」是 SSO / SAML 领域 Identity Provider 的标准中文术语,与模型供应商是两个概念。该文案在 desktop locale 与 mobile 影子 catalog 各有一份副本,两处都要豁免。(2026-07 修正:ko 原声明 제공업체 仅 6 处,实测 제공자 32 处为主流,改随主流。)ja 的「提供元」是多义词——同时表示 "source"(来源),因此按 whenEn=Provider 条件禁用;无条件禁会把「共有元の提供元を信頼できるか」这类 source 语境一并换掉。豁免 creditParity 两条:那里的 third-party providers 指支付渠道/收单机构,不是模型供应商——ja 用「事業者」、zh-CN 用「第三方渠道」都对,套上「プロバイダー」反而会让用户以为是模型服务商在收钱。(2026-07 更新:billing 弃用「点数」概念改用金额/余额,creditParityCny/Usd 两条文案已删除,对应豁免一并移除。) @@ -105,16 +113,19 @@ - **Proxy** — 网络代理,中文保留英文原词(2026-07 裁决:Agent 与 Proxy 都不叫「代理」)。本条只管英文源为 Proxy 的那些——Agent / Subagent 的「代理」误译各自登记在自己条目下。ja / ko 沿用既有音译。whenEn 的匹配走 makeSourceTermMatcher,复数按英语真实形态展开:Proxy 的复数是 proxies,原先只认「加 s」,英文源写 system proxies 时这条条件禁用会整个跳过。 - **Quota** — 周期内的使用上限用「配额」(现状已有 3 处这么写)。与 credits 条目一起裁决:「额度」原先同时表示 Balance / Credits / Quota,三个概念在计费与限额提示里互相污染。「额度」的 forbidden 统一登记在 credits 条目下,不在这里重复——一个词只归属一个术语,否则同一处违规会被两个术语各报一次。(2026-07 修正:ko 原声明 쿼터 在全仓零出现,属凭空造词;ja 原声明 クォータ 仅 desktop 用、mobile 用 残量。定为 ja クォータ / ko 할당량——残量 语义偏 Balance 会与 残高 混淆,而 ko 的 사용량 正是 Usage 的标准词,让 Quota 占用它会重演 zh「额度」三义合一的错误。) - **Running** — 状态标签用「运行中」(现状 16:9)。条件禁用是必需的——「进行中」是 In Progress 的正确译法、「执行中」在别处也可能对,无条件禁会大面积误伤。mobile/devices 的自动化面板上「运行中 / 执行中 / 重跑中」三种写法同屏出现。 -- **Session** — 面向用户一律叫「对话」——「会话」太生硬难懂(2026-07 裁决)。代码与内部标识仍用 Session,本条只约束 UI 文案。Chat 合并到同一个词,见 chat 条目。落地时把 440 处「会话」+ 22 处「聊天」统一改为「对话」。ja / ko 沿用既有音译,不受本条约束。豁免的 6 处是同形异义的技术 session(登录态、WebSocket 语音连接),与产品对话无关——同类的还有 Linux Computer Use 提示里的「桌面会话」——那是 OS 桌面会话(窗口读取与输入自动化的授权范围)。注意 login.errors 的「绑定会话」指账号绑定态,scheduler 的「绑定对话」才是产品对话,两者同名不同义,靠 key 精确区分。还豁免 archiveWorkerConfirmDesc:归档 Worker 时 orcaTeamService.archiveWorker() 先关掉它的 SDK 运行时 session,再归档存下来的产品对话——一句话里是两个生命周期,统称「对话」会让用户以为聊天记录也被停掉了。还豁免 stopBashTitle:它停后台任务但显式保留底层 session 进程,「对话进程」是个不存在的东西,还会模糊它与相邻的「关闭后台进程」的区别。另豁免 4 个 toggleHint 与 forkErrors.unsupportedHistory:英文原文明确区分 agent session(Agent 运行实例)与 conversation(产品对话),两者合并成「对话」会把语义说错——开关其实对「同一对话内重启的 Agent 会话」也生效,写成「只对新建对话生效」等于告诉用户要新开对话。unsupportedHistory 失效的是历史引擎 session,产品对话仍在。内置工具的两条 toast 同理豁免:英文是 takes effect on next session,指下一个 Agent 会话;写成「下次对话生效」会与紧邻的 toggleHint(说重启 Agent 会话即可)自相矛盾,让用户以为必须新开一个对话。personalization 说明也豁免:那三处英文全是 runtime session——设置在 Agent 会话创建时被快照,而终端直跑 / 其他 IDE 里的 Claude Code 根本不是 Cindy 的产品对话,受 SessionStart hook 管。称它们「对话」等于凭空造出 Cindy 之外的产品对话。「Claude Code 会话」四处同样豁免:英文是 Claude Code sessions,指 CC 子进程 / SDK 运行实例(调试日志由 XDT_CC_DEBUG_NET 在下一个 CC 会话启动时生效),不是存下来的产品对话。称它「对话」会让用户以为要新开一个对话才生效。subagentModels.hint 同样豁免:英文是 apply to new sessions,指 Agent 运行实例——runtime-configs 用 live getter 暴露该设置、buildClaudeEnv() 在 Agent 会话启动时快照,所以在同一个对话里重启 runtime 就能生效。写成「从新建对话开始生效」会让用户以为必须新开对话。切换 Agent 引擎的确认文案与 Codex 延迟生效提示同样豁免:英文分别是 the original engine’s session 与 new Codex sessions,指引擎的运行实例——切回原引擎时续接的是它的 runtime session,存下来的产品对话从没变过;Codex 那条重启后对下一个 Codex 运行实例生效,同一对话里重启也算。统称「对话」会让用户以为每个引擎各有一个对话、或必须新开对话。stopConfirmDesc 与 lspMode 的两条 toast 同样豁免:英文源分别是 The Worker session will be destroyed 与 takes effect in new sessions,指的都是 Agent 运行实例——结束协同时 disableOrcaInternal() 关掉 Worker 的 SDK 运行时后会把它存下来的对话标为 archived、聊天记录仍在,LSP 开关则在下一个 Agent 会话启动时才被读到。称它们「对话」会让用户以为聊天记录被销毁、或必须新开一个对话,与紧邻的归档确认文案自相矛盾。 - - 豁免范围:`desktop:chat.backgroundActivity.stopBashTitle`、`desktop:chat.remoteError.REMOTE_COMPAT_MODE_UNSUPPORTED`、`desktop:chat.rewind.errors.remoteNotSupported`、`desktop:chat.systemCard.context.noLiveSession`、`desktop:chat.userMessage.forkErrors.unsupportedHistory`、`desktop:login.errors.INVALID_BIND_TICKET`、`desktop:login.errors.INVALID_LOGIN_TICKET`、`desktop:newChat.chatInput.agentSwitch.confirmation.description`、`desktop:newChat.collaboration.archiveWorkerConfirmDesc`、`desktop:newChat.collaboration.stopConfirmDesc`、`desktop:settings.about.debugLogDescription`、`desktop:settings.builtinTools.toast.disabled`、`desktop:settings.builtinTools.toast.enabled`、`desktop:settings.builtinTools.toggleHint`、`desktop:settings.computerUse.android.toggleHint`、`desktop:settings.computerUse.browser.toggleHint`、`desktop:settings.computerUse.codexRefreshDeferred`、`desktop:settings.computerUse.directControl.permissions.linuxHint`、`desktop:settings.computerUse.directControl.toggleHint`、`desktop:settings.lspMode.toast.disabled`、`desktop:settings.lspMode.toast.enabled`、`desktop:settings.personalization.placeholder`、`desktop:settings.subagentModels.hint`、`desktop:newChat.modelSelector.subscriptionDirectDisabled.chatgpt`、`desktop:newChat.modelSelector.subscriptionDirectDisabled.xai`、`desktop:newChat.modelSelector.subscriptionDirectDisabled.generic`、`desktop:ccAgent.draft.remoteProviderUnsupported`、`desktop:ipcError.REMOTE_PROVIDER_UNSUPPORTED`、`mobile/composer:voice.invalidSession`、`mobile/composer:voice.missingConnectionProvider`、`mobile/composer:voice.missingRefinerTargetProvider`、`mobile/composer:voice.sessionExpiredOrForbidden`、`mobile/composer:voice.sessionNotConnected` +- **Session** — 面向用户叫「任务」——它是左侧列表里可单独打开/删除/分享的条目,有自己的独立空间、可以同时跑多个;「对话」表达不出这个并行性(2026-07-31 裁决,替代此前的「对话」)。任务内人与 AI 交流的过程与内容仍叫「对话」,见 alsoAllowed 与 chat 条目。「会话」继续禁用于产品概念。但英文的 session 在本仓实际承载**五个**不同概念,其中最容易译错、也最影响用户行为的是 Agent 运行时会话——已单独立为 agent-session 条目,中文叫「Agent 会话」,务必先读它再改这类文案;余下三类(登录/绑定校验、SSH 远程与传输、操作系统桌面)与 mobile 语音连接会话都与产品无关,存量译法不在本条约束内,见本条 exempt。代码与内部标识仍用 Session,本条只约束 UI 文案。ja / ko 用音译 `セッション` / `세션`,本次一并收敛:改名前这两种语言就在混用(音译 271 / 277 处 vs `会話`・`대화` 16 / 11 处),改完英文后不一致放大到 133 处,2026-07-31 逐条判断后统一到音译(详见 docs/product-rules/task-and-conversation-naming.md §5.1)。日韩不存在中文那种一词多义问题,所以 `タスク` / `작업`(task)无需像中文那样改写成「自动化」。交流过程仍用 `会話` / `대화`,chat history 仍用 `チャット履歴` / `채팅 기록`。概念分层与判定规则见 docs/product-rules/task-and-conversation-naming.md。 + - 豁免范围:`desktop:chat.backgroundActivity.stopBashTitle`、`desktop:chat.remoteError.REMOTE_COMPAT_MODE_UNSUPPORTED`、`desktop:chat.rewind.errors.remoteNotSupported`、`desktop:chat.systemCard.context.noLiveSession`、`desktop:chat.userMessage.forkErrors.codexStateUnavailable`、`desktop:chat.rewind.errors.noLiveQuery`、`desktop:chat.userMessage.forkErrors.unsupportedHistory`、`desktop:login.errors.INVALID_BIND_TICKET`、`desktop:login.errors.INVALID_LOGIN_TICKET`、`desktop:newChat.chatInput.agentSwitch.confirmation.description`、`desktop:newChat.collaboration.archiveWorkerConfirmDesc`、`desktop:newChat.collaboration.stopConfirmDesc`、`desktop:settings.about.debugLogDescription`、`desktop:settings.builtinTools.toast.disabled`、`desktop:settings.builtinTools.toast.enabled`、`desktop:settings.builtinTools.toggleHint`、`desktop:settings.builtinTools.description`、`desktop:settings.builtinTools.scopePicker.userDefault`、`desktop:settings.builtinTools.scopePicker.userDefaultDescription`、`desktop:settings.builtinTools.source.userTooltip`、`desktop:settings.computerUse.android.toggleHint`、`desktop:settings.computerUse.browser.toggleHint`、`desktop:settings.computerUse.codexRefreshDeferred`、`desktop:settings.computerUse.directControl.permissions.linuxHint`、`desktop:settings.computerUse.directControl.toggleHint`、`desktop:settings.lspMode.toast.disabled`、`desktop:settings.memory.agent.toast.takesEffectSuffix`、`desktop:settings.sessionImport.description`、`desktop:settings.sessionImport.summary.filteredHint`、`desktop:settings.mcp.dialog.desc`、`desktop:settings.mcp.deleteConfirm.message`、`desktop:settings.memory.description`、`desktop:settings.lspMode.toast.enabled`、`desktop:settings.personalization.placeholder`、`desktop:settings.subagentModels.hint`、`desktop:newChat.modelSelector.subscriptionDirectDisabled.chatgpt`、`desktop:newChat.modelSelector.subscriptionDirectDisabled.xai`、`desktop:newChat.modelSelector.subscriptionDirectDisabled.generic`、`desktop:ccAgent.draft.remoteProviderUnsupported`、`desktop:ipcError.REMOTE_PROVIDER_UNSUPPORTED`、`mobile/composer:voice.invalidSession`、`mobile/composer:voice.missingConnectionProvider`、`mobile/composer:voice.missingRefinerTargetProvider`、`mobile/composer:voice.sessionExpiredOrForbidden`、`mobile/composer:voice.sessionNotConnected` - **Skill** — 中文用「技能」(现状 32:26 领先),偶尔保留英文 Skill 也允许,故 zh-CN 不设 forbidden——硬性禁用会逼出别扭的中文。ja / ko 则禁用 技能 / 기술:那是与 スキル / 스킬 并存的同义漂移,且只出现在 skillhub 内部(3 / 3 处)。ko 的 기술 是多义词(技术 / 记述 / 技能),两处非 Skill 义已按 key 豁免。SkillHub 作为产品名单独登记在 skillhub 条目。(2026-07 补充:alsoAllowed 允许保留英文,那就必须是规范形态 Skill——caseStandardFor 会把这类条目一并纳入大小写检查,skillhub 里原有 10 处小写 skill 已统一。) - 豁免范围:`desktop:localDbFatal.details`、`desktop:issueTracker.create.descriptionPlaceholder` - **SkillHub** — 产品名,四语一律保留英文原样(暂定保留,未来若做中文名再改)。注意与 skill 条目区分:侧边栏 tab 现在叫「技能」但实际指 SkillHub,创建面板的「技能」指 Skills——这是既有 bug,两者不是一个东西不该同名,清理存量时需一并处理。 - **Subagent** — Agent 的派生概念,中文跟随 Agent 保留英文原词,不译「子代理」。「代理」在此条下按 whenEn=Subagent 登记:同一个中文词在 Agent / Subagent / Proxy 三处各有来源,只有按英文源区分,自动替换才知道该换成哪个词。ja / ko 沿用既有音译。 -- **Thread** — 全仓仅 8 处,按语境分四类,是「同一个词必须分场合翻译」的典型:①指产品内的对话 → 跟随 session 叫「对话」(已落地 3 处);②Codex 外部概念 → 保留 thread;③GitHub PR review thread →「review 评论」;④比喻用法 "persistent work thread" → 原译「工作线程」,中文有 OS 线程歧义,已改写为「持续推进的工作对话」。「线程」一律禁用。 +- **Task** — 指 Agent 正在执行的一件活,与 Session(也叫「任务」)同名。**同一句里同时出现两者时必须消解歧义**:task 一侧优先动词化("当前任务正在运行" / "执行完成"),确需名词时用「执行」或「作业」——"当前任务正在执行任务" 这类写法一律不可接受。不同句、语境清晰时沿用「任务」(现状 104 处)。定时任务(Schedule)与后台作业各有限定词,不歧义。判定规则见 docs/product-rules/task-and-conversation-naming.md §3。 +- **Thread** — 按语境分四类:①指产品内那个条目 → 跟随 session 叫「任务」(2026-07-31 随 session 改名一并更新);②Codex 外部概念 → 保留 thread;③GitHub PR review thread →「review 评论」;④比喻用法 "persistent work thread" → 改写为「持续推进的工作」。「线程」一律禁用。 +- **Turn** — 一次提问到一次回答结束。**内部概念,不作为界面术语**:面向用户一律说「消息」,只在确实要强调一次完整往返时说「一轮」(如「上一轮」)。不要引入「轮次」作为界面用词。 - **Usage** — 中文用「用量」(现状 37:6 压倒性)。「使用情况」「使用表现」是同义漂移,禁用。注意 usage 在英文里也有「用法」义(如 CLI usage),那属于另一个概念,不在本条约束范围。豁免遥测说明两处:那里的 usage 指「启动与留存情况」这类使用行为,不是计费口径的用量指标。硬套成「用量」会写出「启动与留存用量」这种不通的话——zh-CN 用的是「留存情况」+「使用环境」,ja/ko 的「継続利用状況」「지속 사용 현황」同理。 - 豁免范围:`desktop:settings.about.analyticsDescription`、`mobile/settings:legal.analyticsHint` - **Worker** — Orca 协同角色名,四语统一保留英文。这里只钉大小写形态:首字母大写 Worker。小写 worker 由 guard 的大小写规则单独覆盖(desktop 26:13 混用、mobile 一律小写)。 +- **Working directory** — Agent 干活所在的那个目录,中文一律「工作目录」(现状 43:0,四语唯一译法)。**不绑项目的任务也有工作目录**:Cindy 在 userData/dialogues/<日期>// 下自动分配一个空目录,用来存 Agent 产出的文件;对话消息存在数据库里,与该目录无关。这个目录不要另起名字——「独立任务空间」「任务空间」「工作空间」一类新造说法一并禁用(2026-07-31 裁决),免得同一个东西攒出多套称呼。「工作区」不入 forbidden:它是 Workspace 的合法译法,见 worktree 条目 note(那里已裁定「工作区」只留给 Workspace、working directory 用「工作目录」,本条把该裁决提为独立条目并加上门禁)。 - **Worktree** — Git worktree 是外部工具的既定概念,四语一律保留英文小写原词(现状 21 处最多)。desktop:chat 一个模块里就有 worktree / 工作区 / 工作树 三种写法。特别要紧的是「工作区」——它同时被用于 worktree、Workspace、working tree 三个不同英文概念,必须让出来只表示 Workspace;working directory 用「工作目录」。条件禁用只在英文源含 Worktree 时生效,不影响 Workspace 的正常翻译。豁免导入提示那一句:英文原文同时出现 worktree 与 main workspace,条件禁用是按整句英文判定的,无法区分句内两个概念——那里的「主工作区」正是 Workspace,必须保留,否则会把「不影响主工作区」这条安全边界说成「不影响主 worktree」。 - 豁免范围:`desktop:sessionShare.import.useWorktreeHint` diff --git a/i18n/glossary.json b/i18n/glossary.json index e46f72027b6..4e356920efa 100644 --- a/i18n/glossary.json +++ b/i18n/glossary.json @@ -281,14 +281,6 @@ }, "alsoAllowed": { "zh-CN": [ - { - "text": "自动任务", - "when": "指单条自动化实例(一个定时跑的任务)时" - }, - { - "text": "自动化任务", - "when": "需要与模块名区分、强调它是一条任务时" - }, { "text": "自动操作", "when": "Computer Use 语境——「自动操作电脑 / 浏览器」,指代操控设备而非定时任务" @@ -303,7 +295,7 @@ "오토메이션" ] }, - "note": "模块名 / 能力名用「自动化」(现状 88 处主流);指单条实例时用「自动任务」,这是刻意保留的场景差异——一刀切会写出「查看你的自动化」这类别扭中文。特别注意 Automation 在本产品里指两个不同概念:scheduler 的定时任务,以及 settings.computerUse 的操控电脑能力;后者用「自动操作电脑 / 浏览器」,改成「自动化电脑」反而不通。因此不设 forbidden。(2026-07 修正:ja 原声明 オートメーション 仅 37/121,主流是 自動化;ko 的 자동화 75/121 本就正确。)豁免通讯录授权提示的两个 key:那里指的是 macOS 系统设置里的面板名,日文官方标签是「オートメーション」而非产品术语「自動化」——照产品术语改会让用户按提示在系统设置里找不到对应项,授权恢复路径直接断掉。zh-CN 的「自动化」与 ko 的「자동화」恰好就是各自的 macOS 官方标签,不受影响。", + "note": "scheduler 的模块名、单条实例与能力名统一用「自动化」;不再把「自动任务」「自动化任务」登记为用户可见译法,避免与产品条目 Session(同样叫「任务」)在『删除自动化和任务』等文案里相撞。Computer Use 是另一个概念,用「自动操作电脑 / 浏览器」,改成「自动化电脑」反而不通。因此 zh-CN 只保留「自动操作」这一语境豁免。术语门禁只扫描 locale,不扫描源码注释;历史注释与测试 fixture 的旧称按 docs/product-rules/task-and-conversation-naming.md §6.0.5 逐条判断,不借术语改名机械扩大代码改动。(2026-07 修正:ja 原声明 オートメーション 仅 37/121,主流是 自動化;ko 的 자동화 75/121 本就正确。)豁免通讯录授权提示的两个 key:那里指的是 macOS 系统设置里的面板名,日文官方标签是「オートメーション」而非产品术语「自動化」——照产品术语改会让用户按提示在系统设置里找不到对应项,授权恢复路径直接断掉。zh-CN 的「自动化」与 ko 的「자동화」恰好就是各自的 macOS 官方标签,不受影响。", "exempt": [ "desktop:settings.contacts.import.permissionDenied", "desktop:settings.contacts.ipcError.PERMISSION_DENIED" @@ -344,7 +336,7 @@ "聊天" ] }, - "note": "在本产品里 Chat 与 Session 指同一个东西,中文统一叫「对话」(2026-07 裁决),不再区分「聊天 / 会话 / 对话」三套说法。原先 22 处「聊天」(聊天正文、聊天区、聊天记录…)已一并改为「对话」。ja / ko 保留各自既有译法,两语没有中文这种三词混用问题。" + "note": "Chat 专指任务内人与 AI 交流的过程与内容(对话区 / 对话正文 / 对话记录 / 继续对话),中文「对话」。指那个可打开可删除的条目时用 Session(中文「任务」)——此前 Chat 与 Session 混用同一个中文词的问题已随 2026-07-31 的改名解决:英文侧指容器的 Chat 已改为 Session。「聊天」一律禁用。ja / ko 保留各自既有译法。" }, { "id": "collaboration", @@ -784,7 +776,7 @@ "status": "decided", "en": "Session", "translations": { - "zh-CN": "对话", + "zh-CN": "任务", "ja": "セッション", "ko": "세션" }, @@ -794,12 +786,14 @@ "会话" ] }, - "note": "面向用户一律叫「对话」——「会话」太生硬难懂(2026-07 裁决)。代码与内部标识仍用 Session,本条只约束 UI 文案。Chat 合并到同一个词,见 chat 条目。落地时把 440 处「会话」+ 22 处「聊天」统一改为「对话」。ja / ko 沿用既有音译,不受本条约束。豁免的 6 处是同形异义的技术 session(登录态、WebSocket 语音连接),与产品对话无关——同类的还有 Linux Computer Use 提示里的「桌面会话」——那是 OS 桌面会话(窗口读取与输入自动化的授权范围)。注意 login.errors 的「绑定会话」指账号绑定态,scheduler 的「绑定对话」才是产品对话,两者同名不同义,靠 key 精确区分。还豁免 archiveWorkerConfirmDesc:归档 Worker 时 orcaTeamService.archiveWorker() 先关掉它的 SDK 运行时 session,再归档存下来的产品对话——一句话里是两个生命周期,统称「对话」会让用户以为聊天记录也被停掉了。还豁免 stopBashTitle:它停后台任务但显式保留底层 session 进程,「对话进程」是个不存在的东西,还会模糊它与相邻的「关闭后台进程」的区别。另豁免 4 个 toggleHint 与 forkErrors.unsupportedHistory:英文原文明确区分 agent session(Agent 运行实例)与 conversation(产品对话),两者合并成「对话」会把语义说错——开关其实对「同一对话内重启的 Agent 会话」也生效,写成「只对新建对话生效」等于告诉用户要新开对话。unsupportedHistory 失效的是历史引擎 session,产品对话仍在。内置工具的两条 toast 同理豁免:英文是 takes effect on next session,指下一个 Agent 会话;写成「下次对话生效」会与紧邻的 toggleHint(说重启 Agent 会话即可)自相矛盾,让用户以为必须新开一个对话。personalization 说明也豁免:那三处英文全是 runtime session——设置在 Agent 会话创建时被快照,而终端直跑 / 其他 IDE 里的 Claude Code 根本不是 Cindy 的产品对话,受 SessionStart hook 管。称它们「对话」等于凭空造出 Cindy 之外的产品对话。「Claude Code 会话」四处同样豁免:英文是 Claude Code sessions,指 CC 子进程 / SDK 运行实例(调试日志由 XDT_CC_DEBUG_NET 在下一个 CC 会话启动时生效),不是存下来的产品对话。称它「对话」会让用户以为要新开一个对话才生效。subagentModels.hint 同样豁免:英文是 apply to new sessions,指 Agent 运行实例——runtime-configs 用 live getter 暴露该设置、buildClaudeEnv() 在 Agent 会话启动时快照,所以在同一个对话里重启 runtime 就能生效。写成「从新建对话开始生效」会让用户以为必须新开对话。切换 Agent 引擎的确认文案与 Codex 延迟生效提示同样豁免:英文分别是 the original engine’s session 与 new Codex sessions,指引擎的运行实例——切回原引擎时续接的是它的 runtime session,存下来的产品对话从没变过;Codex 那条重启后对下一个 Codex 运行实例生效,同一对话里重启也算。统称「对话」会让用户以为每个引擎各有一个对话、或必须新开对话。stopConfirmDesc 与 lspMode 的两条 toast 同样豁免:英文源分别是 The Worker session will be destroyed 与 takes effect in new sessions,指的都是 Agent 运行实例——结束协同时 disableOrcaInternal() 关掉 Worker 的 SDK 运行时后会把它存下来的对话标为 archived、聊天记录仍在,LSP 开关则在下一个 Agent 会话启动时才被读到。称它们「对话」会让用户以为聊天记录被销毁、或必须新开一个对话,与紧邻的归档确认文案自相矛盾。", + "note": "面向用户叫「任务」——它是左侧列表里可单独打开/删除/分享的条目,有自己的独立空间、可以同时跑多个;「对话」表达不出这个并行性(2026-07-31 裁决,替代此前的「对话」)。任务内人与 AI 交流的过程与内容仍叫「对话」,见 alsoAllowed 与 chat 条目。「会话」继续禁用于产品概念。但英文的 session 在本仓实际承载**五个**不同概念,其中最容易译错、也最影响用户行为的是 Agent 运行时会话——已单独立为 agent-session 条目,中文叫「Agent 会话」,务必先读它再改这类文案;余下三类(登录/绑定校验、SSH 远程与传输、操作系统桌面)与 mobile 语音连接会话都与产品无关,存量译法不在本条约束内,见本条 exempt。代码与内部标识仍用 Session,本条只约束 UI 文案。ja / ko 用音译 `セッション` / `세션`,本次一并收敛:改名前这两种语言就在混用(音译 271 / 277 处 vs `会話`・`대화` 16 / 11 处),改完英文后不一致放大到 133 处,2026-07-31 逐条判断后统一到音译(详见 docs/product-rules/task-and-conversation-naming.md §5.1)。日韩不存在中文那种一词多义问题,所以 `タスク` / `작업`(task)无需像中文那样改写成「自动化」。交流过程仍用 `会話` / `대화`,chat history 仍用 `チャット履歴` / `채팅 기록`。概念分层与判定规则见 docs/product-rules/task-and-conversation-naming.md。", "exempt": [ "desktop:chat.backgroundActivity.stopBashTitle", "desktop:chat.remoteError.REMOTE_COMPAT_MODE_UNSUPPORTED", "desktop:chat.rewind.errors.remoteNotSupported", "desktop:chat.systemCard.context.noLiveSession", + "desktop:chat.userMessage.forkErrors.codexStateUnavailable", + "desktop:chat.rewind.errors.noLiveQuery", "desktop:chat.userMessage.forkErrors.unsupportedHistory", "desktop:login.errors.INVALID_BIND_TICKET", "desktop:login.errors.INVALID_LOGIN_TICKET", @@ -810,12 +804,22 @@ "desktop:settings.builtinTools.toast.disabled", "desktop:settings.builtinTools.toast.enabled", "desktop:settings.builtinTools.toggleHint", + "desktop:settings.builtinTools.description", + "desktop:settings.builtinTools.scopePicker.userDefault", + "desktop:settings.builtinTools.scopePicker.userDefaultDescription", + "desktop:settings.builtinTools.source.userTooltip", "desktop:settings.computerUse.android.toggleHint", "desktop:settings.computerUse.browser.toggleHint", "desktop:settings.computerUse.codexRefreshDeferred", "desktop:settings.computerUse.directControl.permissions.linuxHint", "desktop:settings.computerUse.directControl.toggleHint", "desktop:settings.lspMode.toast.disabled", + "desktop:settings.memory.agent.toast.takesEffectSuffix", + "desktop:settings.sessionImport.description", + "desktop:settings.sessionImport.summary.filteredHint", + "desktop:settings.mcp.dialog.desc", + "desktop:settings.mcp.deleteConfirm.message", + "desktop:settings.memory.description", "desktop:settings.lspMode.toast.enabled", "desktop:settings.personalization.placeholder", "desktop:settings.subagentModels.hint", @@ -829,7 +833,38 @@ "mobile/composer:voice.missingRefinerTargetProvider", "mobile/composer:voice.sessionExpiredOrForbidden", "mobile/composer:voice.sessionNotConnected" - ] + ], + "alsoAllowed": { + "zh-CN": [ + { + "text": "对话", + "when": "指任务内人与 AI 交流的过程或其内容(对话区、对话正文、对话记录、继续对话)" + } + ] + } + }, + { + "id": "agent-session", + "status": "decided", + "en": "Agent session", + "translations": { + "zh-CN": "Agent 会话", + "ja": "Agent セッション", + "ko": "Agent 세션" + }, + "checkCase": false, + "forbidden": { + "zh-CN": [ + { + "text": "任务", + "whenEn": "agent session" + } + ] + }, + "exempt": [ + "desktop:chat.userMessage.forkErrors.unsupportedHistory" + ], + "note": "Agent / 引擎的**运行时**会话(SDK Query 的生命周期),不是 session 条目说的那个产品条目。中文必须叫「Agent 会话」而不是「任务」——两者的区别是用户会不会因此做一个多余的动作:记忆、MCP、内置工具、LSP、权限开关这类设置都在「下一个 Agent 会话」生效,而同一条任务里 /clear、rewind、重启都会开新的 Agent 会话,说「新建任务才生效」会让用户白开一条任务、还割裂上下文。forbidden 规则只覆盖英文源字面写了 agent session 的 key(精确、无误报);英文只写 session 的那批(`The Codex session state` / `Session is inactive` / `in-flight sessions`)**机器判不出来**——英语本身不区分这两个意思,同一句里两个所指并存也是合法的(`The historical agent session ... so a new session can't be started`:前半指运行时、后半指条目,已登记 exempt)。所以这批只能靠人读英文源判断,判据与全部踩坑记录见 docs/product-rules/task-and-conversation-naming.md §6.0.2。session 条目的 exempt 里另有三类同样叫 session 的东西:登录/绑定校验会话、SSH 远程与传输会话、操作系统桌面会话,以及 mobile 的语音连接会话——它们各自稳定、极少改动,故未单独立条目。" }, { "id": "shortcut-listener-permission", @@ -937,7 +972,7 @@ "status": "decided", "en": "Thread", "translations": { - "zh-CN": "对话", + "zh-CN": "任务", "ja": "スレッド", "ko": "스레드" }, @@ -959,7 +994,7 @@ "线程" ] }, - "note": "全仓仅 8 处,按语境分四类,是「同一个词必须分场合翻译」的典型:①指产品内的对话 → 跟随 session 叫「对话」(已落地 3 处);②Codex 外部概念 → 保留 thread;③GitHub PR review thread →「review 评论」;④比喻用法 \"persistent work thread\" → 原译「工作线程」,中文有 OS 线程歧义,已改写为「持续推进的工作对话」。「线程」一律禁用。" + "note": "按语境分四类:①指产品内那个条目 → 跟随 session 叫「任务」(2026-07-31 随 session 改名一并更新);②Codex 外部概念 → 保留 thread;③GitHub PR review thread →「review 评论」;④比喻用法 \"persistent work thread\" → 改写为「持续推进的工作」。「线程」一律禁用。" }, { "id": "usage", @@ -1012,6 +1047,24 @@ "forbidden": {}, "note": "Orca 协同角色名,四语统一保留英文。这里只钉大小写形态:首字母大写 Worker。小写 worker 由 guard 的大小写规则单独覆盖(desktop 26:13 混用、mobile 一律小写)。" }, + { + "id": "working-directory", + "status": "decided", + "en": "Working directory", + "translations": { + "zh-CN": "工作目录", + "ja": "作業ディレクトリ", + "ko": "작업 디렉터리" + }, + "checkCase": false, + "forbidden": { + "zh-CN": [ + "任务空间", + "工作空间" + ] + }, + "note": "Agent 干活所在的那个目录,中文一律「工作目录」(现状 43:0,四语唯一译法)。**不绑项目的任务也有工作目录**:Cindy 在 userData/dialogues/<日期>// 下自动分配一个空目录,用来存 Agent 产出的文件;对话消息存在数据库里,与该目录无关。这个目录不要另起名字——「独立任务空间」「任务空间」「工作空间」一类新造说法一并禁用(2026-07-31 裁决),免得同一个东西攒出多套称呼。「工作区」不入 forbidden:它是 Workspace 的合法译法,见 worktree 条目 note(那里已裁定「工作区」只留给 Workspace、working directory 用「工作目录」,本条把该裁决提为独立条目并加上门禁)。" + }, { "id": "worktree", "status": "decided", @@ -1104,6 +1157,50 @@ "ko": "컨텍스트 윈도우" }, "note": "模型一次请求可容纳的 token 上限。自定义 Provider 的窗口编辑字段(#386)与用量/压缩相关文案使用;空间紧的 placeholder 可缩写为「上下文 / Context / コンテキスト / 컨텍스트」+ (tokens)。" + }, + { + "id": "task", + "status": "decided", + "en": "Task", + "translations": { + "zh-CN": "任务", + "ja": "タスク", + "ko": "작업" + }, + "checkCase": false, + "note": "指 Agent 正在执行的一件活,与 Session(也叫「任务」)同名。**同一句里同时出现两者时必须消解歧义**:task 一侧优先动词化(\"当前任务正在运行\" / \"执行完成\"),确需名词时用「执行」或「作业」——\"当前任务正在执行任务\" 这类写法一律不可接受。不同句、语境清晰时沿用「任务」(现状 104 处)。定时任务(Schedule)与后台作业各有限定词,不歧义。判定规则见 docs/product-rules/task-and-conversation-naming.md §3。" + }, + { + "id": "message", + "status": "decided", + "en": "Message", + "translations": { + "zh-CN": "消息", + "ja": "メッセージ", + "ko": "메시지" + }, + "checkCase": false, + "forbidden": { + "zh-CN": [ + { + "text": "条对话", + "whenEn": "Message" + } + ] + }, + "note": "对话里的一条往来,用户看到的最小单元。此前有 42 处英文是 message、中文却译成「对话」(如 \"Delete this message\" → 「删除本条对话」),2026-07-31 一并修正。AI 在一轮里的思考与工具调用最终折叠成一条消息呈现,所以消息与轮次是同粒度的两种切法,不是包含关系。(门禁只精确拦「条对话」这种错译形式,不拦「对话历史 / 对话上下文」——同句里既有 message 又有合法「对话X」时机器无法区分对应关系。)" + }, + { + "id": "turn", + "status": "decided", + "en": "Turn", + "translations": { + "zh-CN": "轮", + "ja": "ターン", + "ko": "턴" + }, + "checkCase": false, + "note": "一次提问到一次回答结束。**内部概念,不作为界面术语**:面向用户一律说「消息」,只在确实要强调一次完整往返时说「一轮」(如「上一轮」)。不要引入「轮次」作为界面用词。" } ] } diff --git a/packages/device-link/src/__tests__/allowlist.test.ts b/packages/device-link/src/__tests__/allowlist.test.ts index 97f46188390..ce68ed9e348 100644 --- a/packages/device-link/src/__tests__/allowlist.test.ts +++ b/packages/device-link/src/__tests__/allowlist.test.ts @@ -253,7 +253,7 @@ describe('REMOTE_INVOKE_ALLOWLIST', () => { { re: /updater|release-notes|session-import|migration/, why: 'updater / 导入 / 迁移' }, ]; // 显式豁免: - // - `maker:goal:set` 是 per-session 域动作(入参带 sessionId,只影响单个会话的 + // - `maker:goal:set` 是 per-session 域动作(入参带 sessionId,只影响单个任务的 // 目标状态机),与 compat-mode:set / memory:set 这类全局设置写不同类,同类的 // maker:set-permission-mode 本就放行;仅命名撞上 `:set$` 模式。 // - `maker:api-key:present` 是 presence-only 探测:只回 { present: boolean }, diff --git a/packages/device-link/src/__tests__/topics.test.ts b/packages/device-link/src/__tests__/topics.test.ts index 52480b7bfdc..6841b437986 100644 --- a/packages/device-link/src/__tests__/topics.test.ts +++ b/packages/device-link/src/__tests__/topics.test.ts @@ -35,7 +35,7 @@ describe('topicForPush', () => { ); }); - it('learn:event → sessions(账号级:run 关联触发/蒸馏两个会话,单 sessionId 路由会漏)', () => { + it('learn:event → sessions(账号级:run 关联触发/蒸馏两个任务,单 sessionId 路由会漏)', () => { expect( topicForPush('learn:event', { type: 'state-changed', run: { runId: 'r1', status: 'distilling' } }), ).toBe('sessions'); diff --git a/packages/maker-core/src/agents/claude-code/index.ts b/packages/maker-core/src/agents/claude-code/index.ts index 07b4446d189..0555d51d45b 100644 --- a/packages/maker-core/src/agents/claude-code/index.ts +++ b/packages/maker-core/src/agents/claude-code/index.ts @@ -560,7 +560,7 @@ const CAPABILITIES: Capabilities = { memory: { supported: { supported: true }, displayName: 'Auto Memory', - description: '自动从对话中沉淀长期记忆并在新会话中召回 (后台 auto-dream 一并联动)', + description: '自动从对话中沉淀长期记忆并在后续对话中召回 (后台 auto-dream 一并联动)', stage: 'stable', defaultEnabled: true, resettable: true, diff --git a/packages/maker-core/src/agents/codex/index.ts b/packages/maker-core/src/agents/codex/index.ts index 44cafabeadd..1baf1845e3d 100644 --- a/packages/maker-core/src/agents/codex/index.ts +++ b/packages/maker-core/src/agents/codex/index.ts @@ -892,7 +892,7 @@ const CAPABILITIES: Capabilities = { memory: { supported: { supported: true }, displayName: '记忆 (实验性)', - description: '从聊天中生成新记忆并在新对话中召回 (Codex Feature::MemoryTool, 默认关)', + description: '从对话中生成新记忆并在后续对话中召回 (Codex Feature::MemoryTool, 默认关)', stage: 'experimental', defaultEnabled: false, resettable: true, diff --git a/packages/maker-shared/src/__tests__/deviceLinkContract.test.ts b/packages/maker-shared/src/__tests__/deviceLinkContract.test.ts index e165c056f54..ad913676dcb 100644 --- a/packages/maker-shared/src/__tests__/deviceLinkContract.test.ts +++ b/packages/maker-shared/src/__tests__/deviceLinkContract.test.ts @@ -132,8 +132,8 @@ describe('device-link shared contract', () => { expect(humanizeRemoteError(Object.assign(new Error('revoked'), { code: 'ACCESS_REVOKED' }))) .toContain('撤销手机访问权限'); // 识别不了的错误原文透出(含普通 Error 与非 Error 值) - expect(humanizeRemoteError(new Error('找不到这个会话所属电脑,请重新同步后再试。'))) - .toBe('找不到这个会话所属电脑,请重新同步后再试。'); + expect(humanizeRemoteError(new Error('找不到这个任务所属电脑,请重新同步后再试。'))) + .toBe('找不到这个任务所属电脑,请重新同步后再试。'); expect(humanizeRemoteError('plain failure')).toBe('plain failure'); }); diff --git a/packages/maker-shared/src/__tests__/deviceList.test.ts b/packages/maker-shared/src/__tests__/deviceList.test.ts index 92c5cc5d3e9..2a893c82acb 100644 --- a/packages/maker-shared/src/__tests__/deviceList.test.ts +++ b/packages/maker-shared/src/__tests__/deviceList.test.ts @@ -73,7 +73,7 @@ describe('shared device list presentation model', () => { ['offline', 'offline', false], ]); expect(items.find((item) => item.device.deviceId === 'ready')?.statusDetail).toBe('已允许远程控制'); - expect(items.find((item) => item.device.deviceId === 'busy')?.statusDetail).toBe('电脑端正在处理会话'); + expect(items.find((item) => item.device.deviceId === 'busy')?.statusDetail).toBe('电脑端正在处理任务'); expect(items.find((item) => item.device.deviceId === 'revoked')?.statusDetail).toContain('恢复'); expect(items.find((item) => item.device.deviceId === 'disabled')?.statusDetail).toContain('打开允许远程控制'); expect(items.find((item) => item.device.deviceId === 'offline')?.statusDetail).toContain('10 分钟前在线'); diff --git a/packages/maker-shared/src/__tests__/fixtures.test.ts b/packages/maker-shared/src/__tests__/fixtures.test.ts index 2d046e1d7b3..32dad71f8dc 100644 --- a/packages/maker-shared/src/__tests__/fixtures.test.ts +++ b/packages/maker-shared/src/__tests__/fixtures.test.ts @@ -53,9 +53,9 @@ describe('shared remote-control fixture baseline', () => { Date.parse('2026-06-17T08:00:00.000Z'), )).toMatchObject({ title: '每日移动端远控巡检', - detail: 'cron 0 9 * * * · 新会话 · Claude · xdt-maker', + detail: 'cron 0 9 * * * · 新任务 · Claude · xdt-maker', runSessionDetail: null, - runSessionLabel: '新会话', + runSessionLabel: '新任务', unreadCount: 1, }); diff --git a/packages/maker-shared/src/__tests__/interaction.test.ts b/packages/maker-shared/src/__tests__/interaction.test.ts index 1d5f6be8702..655e5081814 100644 --- a/packages/maker-shared/src/__tests__/interaction.test.ts +++ b/packages/maker-shared/src/__tests__/interaction.test.ts @@ -106,7 +106,7 @@ describe('interaction shared model', () => { riskSummary: null, canAlwaysAllow: true, })).toEqual({ - title: '可以只允许一次,也可以本会话总是允许', + title: '可以只允许一次,也可以本任务总是允许', detail: '工具: Bash', }); expect(buildPermissionDecisionSummary({ diff --git a/packages/maker-shared/src/__tests__/mobileHome.test.ts b/packages/maker-shared/src/__tests__/mobileHome.test.ts index 5169a5f48f3..467dfaa4c65 100644 --- a/packages/maker-shared/src/__tests__/mobileHome.test.ts +++ b/packages/maker-shared/src/__tests__/mobileHome.test.ts @@ -430,7 +430,7 @@ describe('mobileHome', () => { deviceLinkDeviceId: 'mac-a', workspaceKind: 'dialogue', workingDir: null, - title: '另一个会话', + title: '另一个任务', preview: '部署文档已更新', }), ]; diff --git a/packages/maker-shared/src/__tests__/scheduleDelete.test.ts b/packages/maker-shared/src/__tests__/scheduleDelete.test.ts index 865840df34e..59e9bb95be0 100644 --- a/packages/maker-shared/src/__tests__/scheduleDelete.test.ts +++ b/packages/maker-shared/src/__tests__/scheduleDelete.test.ts @@ -48,7 +48,7 @@ describe('mobile schedule delete model', () => { sessionCount: 2, inflightCount: 1, }); - expect(describeScheduleDeletePreview(preview)).toBe('找到 2 个由它生成的会话,还有 1 次执行正在进行'); + expect(describeScheduleDeletePreview(preview)).toBe('找到 2 个由它生成的任务,还有 1 次执行正在进行'); }); it('buildScheduleDeletePreview excludes the hand-bound targetSessionId from the mobile preview', () => { @@ -121,7 +121,7 @@ describe('hand-bound targetSessionId exclusion', () => { expect(preview.sessionIds).not.toContain(boundId); expect(preview.sessionCount).toBe(2); - expect(describeScheduleDeletePreview(preview)).toBe('找到 2 个由它生成的会话'); + expect(describeScheduleDeletePreview(preview)).toBe('找到 2 个由它生成的任务'); }); it('keeps all generated sessions when no bound id is given (backward compat)', () => { diff --git a/packages/maker-shared/src/__tests__/scheduleModel.test.ts b/packages/maker-shared/src/__tests__/scheduleModel.test.ts index 849f6d98b13..78239c75dcb 100644 --- a/packages/maker-shared/src/__tests__/scheduleModel.test.ts +++ b/packages/maker-shared/src/__tests__/scheduleModel.test.ts @@ -116,9 +116,9 @@ describe('schedule model', () => { expect(summary).toMatchObject({ title: '巡检 xdt-maker', subtitle: '上次 1 小时前 · 2 小时后', - detail: 'cron 0 9 * * * · 新会话 · Claude · xdt-maker', + detail: 'cron 0 9 * * * · 新任务 · Claude · xdt-maker', runSessionDetail: null, - runSessionLabel: '新会话', + runSessionLabel: '新任务', statusLabel: '运行中', unreadCount: 1, }); @@ -129,9 +129,9 @@ describe('schedule model', () => { persistentSession: true, targetSessionId: 'session-persistent-123', }), [], NOW)).toMatchObject({ - detail: 'cron 0 9 * * * · 持续会话 · Claude · xdt-maker', - runSessionDetail: '持续会话 session-', - runSessionLabel: '持续会话', + detail: 'cron 0 9 * * * · 持续任务 · Claude · xdt-maker', + runSessionDetail: '持续任务 session-', + runSessionLabel: '持续任务', }); expect(summarizeSchedule(schedule({ @@ -139,9 +139,9 @@ describe('schedule model', () => { workingDir: '', useWorktree: true, }), [], NOW)).toMatchObject({ - detail: 'cron 0 9 * * * · 绑定会话 · Claude · 未设置目录', + detail: 'cron 0 9 * * * · 绑定任务 · Claude · 未设置目录', runSessionDetail: '绑定到 session-', - runSessionLabel: '绑定会话', + runSessionLabel: '绑定任务', }); }); @@ -190,7 +190,7 @@ describe('schedule model', () => { expect(summarizeRun(runs[1], NOW)).toMatchObject({ title: '失败', detail: 'boom', - meta: '耗时未知 · 未创建会话', + meta: '耗时未知 · 未创建任务', canDelete: true, canMarkRead: true, canOpenSession: false, @@ -208,9 +208,9 @@ describe('schedule model', () => { }), NOW)).toMatchObject({ title: '执行中', detail: null, - meta: '已运行 1 分 30 秒 · 会话 session-', + meta: '已运行 1 分 30 秒 · 任务 session-', openSessionLabel: '打开', - sessionDetail: '会话 session-', + sessionDetail: '任务 session-', canDelete: false, canMarkRead: false, canOpenSession: true, diff --git a/packages/maker-shared/src/__tests__/sessionActionStrip.test.ts b/packages/maker-shared/src/__tests__/sessionActionStrip.test.ts index 8cdea8f1dcf..48e766a4013 100644 --- a/packages/maker-shared/src/__tests__/sessionActionStrip.test.ts +++ b/packages/maker-shared/src/__tests__/sessionActionStrip.test.ts @@ -95,9 +95,11 @@ describe('shared session action strip model', () => { { id: 'read-only', label: '只读', strong: true }, ]); expect(overview.runtimeSubtitle).toBe('协作 Worker · Worktree app-worker · Codex · claude-sonnet-4-6 · plan · Fast'); + // 这条 fixture 恰好证明禁用原因不能断言「因为不绑项目」: 它是 Orca Worker、 + // 有 worktreePath, 只是 workingDir 为 null —— 判据只看 workingDir。 expect(overview.actions.find((action) => action.id === 'files')).toMatchObject({ disabled: true, - disabledReason: 'Dialogue 会话没有远程工作目录,不能浏览文件。', + disabledReason: '这个任务没有远程工作目录,不能浏览文件。', }); expect(overview.actions.find((action) => action.id === 'search')).toMatchObject({ disabled: true, diff --git a/packages/maker-shared/src/__tests__/sessionControls.test.ts b/packages/maker-shared/src/__tests__/sessionControls.test.ts index d234ba12808..7a64dc2997b 100644 --- a/packages/maker-shared/src/__tests__/sessionControls.test.ts +++ b/packages/maker-shared/src/__tests__/sessionControls.test.ts @@ -20,13 +20,13 @@ describe('shared session usage summaries', () => { totalTokenUsage: 42000, })).toEqual({ available: true, - detail: '本会话 $0.02 · 42k tokens · 上下文 16k / 200k · 8%', + detail: '本任务 $0.02 · 42k tokens · 上下文 16k / 200k · 8%', title: 'Session spend', }); expect(summarizeSessionSpend(null)).toEqual({ available: false, - detail: '暂无会话用量', + detail: '暂无任务用量', title: 'Session spend', }); }); diff --git a/packages/maker-shared/src/__tests__/sessionList.test.ts b/packages/maker-shared/src/__tests__/sessionList.test.ts index 9dbea3b9562..86f6582e10d 100644 --- a/packages/maker-shared/src/__tests__/sessionList.test.ts +++ b/packages/maker-shared/src/__tests__/sessionList.test.ts @@ -110,7 +110,7 @@ describe('sessionList', () => { it('未起名会话用调用方给的本地化文案,不回落工作目录路径', () => { // 哨兵若按「无标题」处理,会回落 workingDir 把完整路径当标题显示,与 desktop 的 - // 「未命名对话」不一致(PR #1031 review P1)。文案由调用方按当前 locale 传入 —— + // 「未命名任务」不一致(PR #1031 review P1)。文案由调用方按当前 locale 传入 —— // 共享层不能兜中文串,mobile 还支持 en / ja / ko。 const now = new Date('2026-01-01T00:10:00.000Z').getTime(); const sentinel = session('s-sentinel', { @@ -119,8 +119,8 @@ describe('sessionList', () => { }); expect( - toRemoteSessionListItem(sentinel, now, undefined, 0, null, null, '未命名对话').title, - ).toBe('未命名对话'); + toRemoteSessionListItem(sentinel, now, undefined, 0, null, null, '未命名任务').title, + ).toBe('未命名任务'); // 同一条会话换 locale → 换文案(锁住「不在共享层写死中文」)。 expect( toRemoteSessionListItem(sentinel, now, undefined, 0, null, null, 'Untitled session').title, @@ -136,7 +136,7 @@ describe('sessionList', () => { 0, null, null, - '未命名对话', + '未命名任务', ); expect(emptyTitle.title).toBe('/Users/dash/Code/Cindy/cindy'); }); @@ -201,7 +201,7 @@ describe('sessionList', () => { }), new Date('2026-01-01T00:10:00.000Z').getTime()); expect(item).toMatchObject({ - title: '未命名会话', + title: '未命名任务', subtitle: 'Codex · gpt-5.4 · dialogue', detail: '活跃 · 5 分钟前 · 12 条消息', messagePreview: null, @@ -423,8 +423,8 @@ describe('sessionList', () => { const group = rows.find((item) => item.automationGroup); expect(group).toMatchObject({ title: '移动端巡检', - subtitle: '自动化任务 · 2 个会话 · Claude Code · claude-sonnet-4-6', - detail: '2 个会话 · 4 分钟前 · 自动化执行中 · 1 个自动化未读', + subtitle: '自动化 · 2 个任务 · Claude Code · claude-sonnet-4-6', + detail: '2 个任务 · 4 分钟前 · 自动化执行中 · 1 个自动化未读', automationGroup: { key: 'schedule:sched-1', sessionIds: ['running', 'old'], @@ -659,7 +659,7 @@ describe('sessionList', () => { statusFilter: 'active', })).toMatchObject({ title: '搜索结果', - detail: '1 个匹配会话 · 活跃 2 · 项目分组', + detail: '1 个匹配任务 · 活跃 2 · 项目分组', hint: '搜索范围包含标题、项目路径、模型、自动化名称和消息预览。', resultCount: 1, rowCount: 1, @@ -690,9 +690,9 @@ describe('sessionList', () => { sections: automationSections, statusFilter: 'automation', })).toMatchObject({ - title: '自动化会话', - detail: '2 个会话 · 1 行 · 自动化 2 · 项目分组', - hint: '自动化会话会按计划聚合,展开后可以进入单次运行。', + title: '自动化生成的任务', + detail: '2 个任务 · 1 行 · 自动化 2 · 项目分组', + hint: '自动化生成的任务会按计划聚合,展开后可以进入单次运行。', resultCount: 2, rowCount: 1, }); @@ -700,19 +700,19 @@ describe('sessionList', () => { it('keeps mobile device-detail empty states specific to search and filters', () => { expect(deviceSessionEmptyState('active', 'billing')).toMatchObject({ - title: '没有匹配的会话', + title: '没有匹配的任务', }); expect(deviceSessionEmptyState('waiting', '')).toMatchObject({ title: '没有待处理请求', }); expect(deviceSessionEmptyState('automation', '')).toMatchObject({ - title: '没有自动化会话', + title: '没有自动化生成的任务', }); expect(deviceSessionEmptyState('archived', '')).toMatchObject({ - title: '没有归档会话', + title: '没有归档任务', }); expect(deviceSessionEmptyState('active', '')).toMatchObject({ - title: '这台电脑暂无活动会话', + title: '这台电脑暂无活动任务', }); }); diff --git a/packages/maker-shared/src/__tests__/sessionOperation.test.ts b/packages/maker-shared/src/__tests__/sessionOperation.test.ts index 7f5cc5b5c1d..50b2a7b0f7a 100644 --- a/packages/maker-shared/src/__tests__/sessionOperation.test.ts +++ b/packages/maker-shared/src/__tests__/sessionOperation.test.ts @@ -12,7 +12,7 @@ describe('shared session operation model', () => { hasActivePendingInteraction: true, })).toEqual({ canUseComposer: false, - composerDisabledReason: '当前会话还没有同步完成。', + composerDisabledReason: '当前任务还没有同步完成。', composerDisabledReasonSource: 'session-syncing', composerSlot: 'missing-session', messageHistoryMode: 'hidden', @@ -282,7 +282,7 @@ describe('shared session composer action model', () => { label: '停止', visible: true, }); - expect(runningWithDraft.guidanceText).toBe('点发送后会进入桌面端队列,按当前会话设置执行。'); + expect(runningWithDraft.guidanceText).toBe('点发送后会进入桌面端队列,按当前任务设置执行。'); }); it('keeps the send slot present and usable through the whole voice lifecycle', () => { diff --git a/packages/maker-shared/src/__tests__/sessionSelection.test.ts b/packages/maker-shared/src/__tests__/sessionSelection.test.ts index 212dabc4405..cf6733f8f8c 100644 --- a/packages/maker-shared/src/__tests__/sessionSelection.test.ts +++ b/packages/maker-shared/src/__tests__/sessionSelection.test.ts @@ -91,7 +91,7 @@ describe('mobile session selection', () => { expect(summary.candidates.map((item) => item.id)).toEqual(['active']); expect(summary.skippedCount).toBe(2); - expect(summary.description).toContain('将跳过 2 个不适用的会话'); + expect(summary.description).toContain('将跳过 2 个不适用的任务'); expect(isMobileSessionBulkActionAvailable(summary)).toBe(true); expect(mobileSessionBulkActionButtonLabel(summary)).toBe('归档 1'); expect(mobileSessionBulkPatch('archive')).toEqual({ status: 'archived', pinnedAt: null }); @@ -118,7 +118,7 @@ describe('mobile session selection', () => { expect(summary.candidates.map((item) => item.id)).toEqual(['archived']); expect(summary.skippedCount).toBe(2); - expect(summary.description).toContain('将跳过 2 个不适用的会话'); + expect(summary.description).toContain('将跳过 2 个不适用的任务'); expect(mobileSessionBulkPatch('restore')).toEqual({ status: 'active' }); }); diff --git a/packages/maker-shared/src/__tests__/sessionTitle.test.ts b/packages/maker-shared/src/__tests__/sessionTitle.test.ts index 7b439678f66..5bebaa1e274 100644 --- a/packages/maker-shared/src/__tests__/sessionTitle.test.ts +++ b/packages/maker-shared/src/__tests__/sessionTitle.test.ts @@ -55,14 +55,14 @@ describe('isDefaultDraftSessionTitle', () => { describe('projectDraftSessionTitle', () => { it('哨兵 + 已解析文案 → 换成那个文案', () => { - expect(projectDraftSessionTitle(DEFAULT_DRAFT_SESSION_TITLE, '未命名对话')).toBe('未命名对话'); + expect(projectDraftSessionTitle(DEFAULT_DRAFT_SESSION_TITLE, '未命名任务')).toBe('未命名任务'); expect(projectDraftSessionTitle(DEFAULT_DRAFT_SESSION_TITLE, 'Untitled session')).toBe('Untitled session'); }); it('非哨兵标题原样返回,不被 label 顶掉', () => { - expect(projectDraftSessionTitle('修 Orca 心跳', '未命名对话')).toBe('修 Orca 心跳'); + expect(projectDraftSessionTitle('修 Orca 心跳', '未命名任务')).toBe('修 Orca 心跳'); // 用户手动改成近似串仍是合法自定义标题(判据不做归一,见上)。 - expect(projectDraftSessionTitle('new maker', '未命名对话')).toBe('new maker'); + expect(projectDraftSessionTitle('new maker', '未命名任务')).toBe('new maker'); }); it('没给 label 时退回原串,由调用方自己接兜底链', () => { @@ -72,14 +72,14 @@ describe('projectDraftSessionTitle', () => { }); it('空标题归一成空串,方便调用方直接接 `||` 兜底', () => { - expect(projectDraftSessionTitle(null, '未命名对话')).toBe(''); - expect(projectDraftSessionTitle(undefined, '未命名对话')).toBe(''); - expect(projectDraftSessionTitle('', '未命名对话')).toBe(''); + expect(projectDraftSessionTitle(null, '未命名任务')).toBe(''); + expect(projectDraftSessionTitle(undefined, '未命名任务')).toBe(''); + expect(projectDraftSessionTitle('', '未命名任务')).toBe(''); }); it('幂等:对投影结果再投影一次不变', () => { // 渲染与搜索 haystack 各自调一次,两次都必须收敛到同一个串。 - const once = projectDraftSessionTitle(DEFAULT_DRAFT_SESSION_TITLE, '未命名对话'); - expect(projectDraftSessionTitle(once, '未命名对话')).toBe(once); + const once = projectDraftSessionTitle(DEFAULT_DRAFT_SESSION_TITLE, '未命名任务'); + expect(projectDraftSessionTitle(once, '未命名任务')).toBe(once); }); }); diff --git a/packages/maker-shared/src/__tests__/systemCard.test.ts b/packages/maker-shared/src/__tests__/systemCard.test.ts index bfcd0dcae34..c698d289abe 100644 --- a/packages/maker-shared/src/__tests__/systemCard.test.ts +++ b/packages/maker-shared/src/__tests__/systemCard.test.ts @@ -20,10 +20,10 @@ describe('shared system card presentation model', () => { { kind: 'agent-builtin', name: 'doctor', description: 'remote doctor' }, ]).map((command) => [command.name, command.description])).toEqual([ ['help', '显示手机端和远程 agent 命令'], - ['context', '查看当前会话上下文用量'], - ['cost', '查看当前会话消耗'], + ['context', '查看当前任务上下文用量'], + ['cost', '查看当前任务消耗'], ['pwd', '显示当前远程工作目录'], - ['status', '显示当前会话状态'], + ['status', '显示当前任务状态'], ['doctor', 'remote doctor'], ]); }); diff --git a/packages/maker-shared/src/agentCapabilities.ts b/packages/maker-shared/src/agentCapabilities.ts index cd8741ee511..47ed64f33b2 100644 --- a/packages/maker-shared/src/agentCapabilities.ts +++ b/packages/maker-shared/src/agentCapabilities.ts @@ -239,7 +239,7 @@ export function buildMobileModelSwitchConfirmation({ toLabel, targetModelId, title: `切换到 ${toLabel} 模型?`, - description: `这个会话已有历史消息。${fromLabel} 和 ${toLabel} 的消息格式可能不兼容,切换后旧上下文可能无法继续使用。`, + description: `这个任务已有历史消息。${fromLabel} 和 ${toLabel} 的消息格式可能不兼容,切换后旧上下文可能无法继续使用。`, }; } diff --git a/packages/maker-shared/src/deviceLinkContract.ts b/packages/maker-shared/src/deviceLinkContract.ts index 1931b005c67..e6a4d8c166a 100644 --- a/packages/maker-shared/src/deviceLinkContract.ts +++ b/packages/maker-shared/src/deviceLinkContract.ts @@ -407,9 +407,9 @@ export function relayStatusLabel(status: DeviceLinkRelayStatus): string { export function relayStatusHint(status: DeviceLinkRelayStatus, lastSyncedAt: number | null): string { if (status === 'online') { - return lastSyncedAt ? `上次同步 ${formatClock(lastSyncedAt)}` : '可以同步远程会话。'; + return lastSyncedAt ? `上次同步 ${formatClock(lastSyncedAt)}` : '可以同步远程任务。'; } - if (status === 'connecting') return '网络恢复后会自动重新订阅远程会话。'; + if (status === 'connecting') return '网络恢复后会自动重新订阅远程任务。'; return '回到前台或重新登录后会恢复连接。'; } diff --git a/packages/maker-shared/src/deviceList.ts b/packages/maker-shared/src/deviceList.ts index ca64958fff1..3fe2babf7f8 100644 --- a/packages/maker-shared/src/deviceList.ts +++ b/packages/maker-shared/src/deviceList.ts @@ -192,7 +192,7 @@ function deviceStatusDetail(device: DeviceListDeviceLike, state: DeviceAccessSta if (state === 'offline') return formatLastSeen(device.lastSeenAt, now); if (state === 'remote_disabled') return '在电脑端设置里打开允许远程控制'; if (state === 'self') return '当前手机'; - if (state === 'busy') return '电脑端正在处理会话'; + if (state === 'busy') return '电脑端正在处理任务'; return '已允许远程控制'; } diff --git a/packages/maker-shared/src/interaction.ts b/packages/maker-shared/src/interaction.ts index fee090d1c52..a22439e3098 100644 --- a/packages/maker-shared/src/interaction.ts +++ b/packages/maker-shared/src/interaction.ts @@ -182,7 +182,7 @@ export function buildPermissionDecisionSummary(input: { } if (input.canAlwaysAllow) { return { - title: '可以只允许一次,也可以本会话总是允许', + title: '可以只允许一次,也可以本任务总是允许', detail: `工具: ${input.toolName}`, }; } @@ -368,7 +368,7 @@ export function interactionDisplayTitle(kind: string): string { export function interactionDisplayHint(kind: string, readOnly = false): string { if (readOnly) return '协作只读模式,仅展示电脑端请求。'; if (kind === 'plan_review') return '先看计划,必要时反馈修改,确认后电脑端才继续执行。'; - if (kind === 'permission') return '只把本次决定回传给当前电脑端会话。'; + if (kind === 'permission') return '只把本次决定回传给当前电脑端任务。'; if (kind === 'ask_user_question') return '回答会保存草稿,提交后电脑端继续。'; if (kind === 'issue_confirm') return '确认标题和正文后再提交。'; if (kind === 'plugin_setup') return '配置要在电脑端完成,这里可以取消这次请求。'; @@ -804,7 +804,7 @@ export function permissionRiskSummary(request: InteractionRequestLike): string | const command = typeof input.command === 'string' ? input.command.trim() : ''; if (!command) return null; if (!isHighRiskShellCommand(command)) return null; - return '这个命令可能修改系统、仓库或外部服务状态。允许前请确认你信任当前会话和命令内容。'; + return '这个命令可能修改系统、仓库或外部服务状态。允许前请确认你信任当前任务和命令内容。'; } export function sessionScopedPermissionSuggestions(suggestions: unknown): unknown[] { diff --git a/packages/maker-shared/src/mobileHome.ts b/packages/maker-shared/src/mobileHome.ts index 9beb2b97639..60df3ae183b 100644 --- a/packages/maker-shared/src/mobileHome.ts +++ b/packages/maker-shared/src/mobileHome.ts @@ -568,7 +568,7 @@ function fallbackScheduleInfo(session: RemoteSessionListSessionLike): RemoteSess latestRunAt: 0, running: false, scheduleId: '', - scheduleName: title || '自动化任务', + scheduleName: title || '自动化', allSchedulesStopped: false, unreadCount: 0, unreadRunIds: [], @@ -654,7 +654,7 @@ function mobileHomeEmptyState({ kind: 'search', noDevice: null, title: '没有匹配结果', - copy: '换一个项目名、对话标题、电脑名或消息关键词再试。', + copy: '换一个项目名、任务标题、电脑名或消息关键词再试。', }; } if (deviceCount === 0) { @@ -675,23 +675,23 @@ function mobileHomeEmptyState({ return { kind: 'automation', noDevice: null, - title: '没有自动化会话', - copy: '自动化任务运行后,对应会话会按项目聚合到这里。', + title: '没有自动化生成的任务', + copy: '自动化运行后,对应任务会按项目聚合到这里。', }; } if (statusFilter === 'archived') { return { kind: 'archived', noDevice: null, - title: '没有归档会话', - copy: '切到全部或活跃筛选,继续查看当前项目和对话。', + title: '没有归档任务', + copy: '切到全部或活跃筛选,继续查看当前项目和任务。', }; } return { kind: 'noSession', noDevice: null, - title: '还没有会话', - copy: '先在桌面端创建或继续一个会话,手机端会直接显示对应项目和对话。', + title: '还没有任务', + copy: '先在桌面端创建或继续一个任务,手机端会直接显示对应项目和任务。', }; } diff --git a/packages/maker-shared/src/scheduleDelete.ts b/packages/maker-shared/src/scheduleDelete.ts index 6b7be9fbfe2..4ea9e8fb707 100644 --- a/packages/maker-shared/src/scheduleDelete.ts +++ b/packages/maker-shared/src/scheduleDelete.ts @@ -93,8 +93,8 @@ export function isProjectAutomationSchedule(target: ScheduleDeleteTarget): boole export function describeScheduleDeletePreview(preview: ScheduleDeletePreview): string { const sessionPart = preview.sessionCount === 0 - ? '没有找到由它生成的会话' - : `找到 ${preview.sessionCount} 个由它生成的会话`; + ? '没有找到由它生成的任务' + : `找到 ${preview.sessionCount} 个由它生成的任务`; if (preview.inflightCount > 0) { return `${sessionPart},还有 ${preview.inflightCount} 次执行正在进行`; } diff --git a/packages/maker-shared/src/scheduleForm.ts b/packages/maker-shared/src/scheduleForm.ts index 74c7e7f44c5..e1778d9c80d 100644 --- a/packages/maker-shared/src/scheduleForm.ts +++ b/packages/maker-shared/src/scheduleForm.ts @@ -203,7 +203,7 @@ export function validateMobileScheduleDraft( } if (!draft.timezone.trim()) return { field: 'timezone', message: '请输入时区' }; if (draft.targetSessionId.trim() === MOBILE_SCHEDULE_PENDING_SESSION_ID) { - return { field: 'targetSessionId', message: '请选择要绑定的会话' }; + return { field: 'targetSessionId', message: '请选择要绑定的任务' }; } if (draft.runMode === 'recurring') { if (!draft.cronExpr.trim()) return { field: 'cronExpr', message: '请输入 cron 表达式' }; diff --git a/packages/maker-shared/src/scheduleModel.ts b/packages/maker-shared/src/scheduleModel.ts index 7987637767f..84f53f2d841 100644 --- a/packages/maker-shared/src/scheduleModel.ts +++ b/packages/maker-shared/src/scheduleModel.ts @@ -189,7 +189,7 @@ export function summarizeRun(run: RemoteScheduleRun, now = Date.now()): RunSumma const subtitle = finished ? `${fired} - ${finished}` : fired; const error = run.errorMsg?.trim(); const result = run.resultText?.trim(); - const sessionDetail = run.sessionId?.trim() ? `会话 ${shortSessionId(run.sessionId)}` : null; + const sessionDetail = run.sessionId?.trim() ? `任务 ${shortSessionId(run.sessionId)}` : null; const isLegacySessionRun = run.id.startsWith(LEGACY_SESSION_RUN_ID_PREFIX); const unread = isUnreadRun(run, now); const canDelete = !isLegacySessionRun && run.status !== 'running'; @@ -210,7 +210,7 @@ export function summarizeRun(run: RemoteScheduleRun, now = Date.now()): RunSumma markReadLabel: canMarkRead ? '已读' : null, meta: [ describeRunTiming(run, now), - sessionDetail ?? (canRestart ? '可重新执行' : '未创建会话'), + sessionDetail ?? (canRestart ? '可重新执行' : '未创建任务'), ].filter(Boolean).join(' · '), openSessionLabel: sessionDetail ? '打开' : null, restartLabel: canRestart ? '重跑' : null, @@ -314,18 +314,18 @@ function describeDestination(schedule: RemoteSchedule): string { function describeRunSessionLabel( schedule: Pick, ): string { - if (schedule.persistentSession) return '持续会话'; - if (schedule.targetSessionId?.trim()) return '绑定会话'; - return '新会话'; + if (schedule.persistentSession) return '持续任务'; + if (schedule.targetSessionId?.trim()) return '绑定任务'; + return '新任务'; } function describeRunSessionDetail( schedule: Pick, ): string | null { if (schedule.persistentSession && schedule.targetSessionId?.trim()) { - return `持续会话 ${shortSessionId(schedule.targetSessionId)}`; + return `持续任务 ${shortSessionId(schedule.targetSessionId)}`; } - if (schedule.persistentSession) return '首次触发后持续复用同一会话'; + if (schedule.persistentSession) return '首次触发后持续复用同一任务'; if (schedule.targetSessionId?.trim()) return `绑定到 ${shortSessionId(schedule.targetSessionId)}`; return null; } diff --git a/packages/maker-shared/src/sessionActionStrip.ts b/packages/maker-shared/src/sessionActionStrip.ts index 129842ed711..1bdec3022ce 100644 --- a/packages/maker-shared/src/sessionActionStrip.ts +++ b/packages/maker-shared/src/sessionActionStrip.ts @@ -127,10 +127,12 @@ function buildActionStripActions( ): SessionActionStripAction[] { const queueDisabledReason = queueActionDisabledReason(input); const searchDisabledReason = input.messageCount > 0 ? null : '当前没有可搜索的消息。'; - const filesDisabledReason = filesEnabled ? null : 'Dialogue 会话没有远程工作目录,不能浏览文件。'; + // 判据只是 `!!session.workingDir`(见 filesEnabled) —— 不绑项目的任务是最常见的 + // 命中场景, 但旧数据/异常行同样会缺 workingDir, 所以文案只陈述状态、不断言原因。 + const filesDisabledReason = filesEnabled ? null : '这个任务没有远程工作目录,不能浏览文件。'; return [ { - accessibilityLabel: '打开会话设置', + accessibilityLabel: '打开任务设置', active: false, attention: !!input.readOnlyReason, disabled: false, @@ -140,7 +142,7 @@ function buildActionStripActions( testID: 'session.controlsToggle', }, { - accessibilityLabel: '查看会话用量', + accessibilityLabel: '查看任务用量', active: false, attention: false, disabled: false, @@ -207,8 +209,8 @@ function overviewActionCopy(input: { if (input.remoteUnavailableReason) return input.remoteUnavailableReason; if (input.pendingCount > 0) return '先处理待处理请求,处理后输入区会恢复。'; if (input.readOnlyReason) return input.readOnlyReason; - if (input.sessionStatus === 'archived') return '会话已归档,恢复后才能继续发送。'; - if (input.sessionStatus === 'deleted') return '会话已删除,不能继续操作。'; + if (input.sessionStatus === 'archived') return '任务已归档,恢复后才能继续发送。'; + if (input.sessionStatus === 'deleted') return '任务已删除,不能继续操作。'; if (input.queuePaused) return '队列已暂停,可在队列面板继续执行。'; return null; } diff --git a/packages/maker-shared/src/sessionControls.ts b/packages/maker-shared/src/sessionControls.ts index 92518e4eb6b..8cc00a21671 100644 --- a/packages/maker-shared/src/sessionControls.ts +++ b/packages/maker-shared/src/sessionControls.ts @@ -27,7 +27,7 @@ export function summarizeSessionSpend(session: Pick< available: boolean; } { if (!session) { - return { title: 'Session spend', detail: '暂无会话用量', available: false }; + return { title: 'Session spend', detail: '暂无任务用量', available: false }; } const totalCostUsd = readNumber(session.totalCostUsd); @@ -37,7 +37,7 @@ export function summarizeSessionSpend(session: Pick< const parts: string[] = []; if (totalCostUsd !== null && totalCostUsd > 0) { - parts.push(`本会话 ${formatUsd(totalCostUsd)}`); + parts.push(`本任务 ${formatUsd(totalCostUsd)}`); } if (totalTokenUsage !== null && totalTokenUsage > 0) { parts.push(`${formatCompactNumber(totalTokenUsage)} tokens`); @@ -52,7 +52,7 @@ export function summarizeSessionSpend(session: Pick< } if (parts.length === 0) { - return { title: 'Session spend', detail: '暂无会话用量', available: false }; + return { title: 'Session spend', detail: '暂无任务用量', available: false }; } return { title: 'Session spend', detail: parts.join(' · '), available: true }; } diff --git a/packages/maker-shared/src/sessionIdentity.ts b/packages/maker-shared/src/sessionIdentity.ts index 73b625fc24d..0816ce8fd78 100644 --- a/packages/maker-shared/src/sessionIdentity.ts +++ b/packages/maker-shared/src/sessionIdentity.ts @@ -29,13 +29,13 @@ export function sessionCollaborationLabel(session: SessionCollaborationLike): st export function sessionCollaborationNotice(session: SessionCollaborationLike | null): string | null { if (!session) return null; if (session.orcaRole === 'lead') { - return '这是桌面端 Orca Lead 会话。可以在手机上继续发消息聊天;创建 worker、切换 focus、fork/rewind、队列编辑和会话设置等编排操作请在电脑端完成。'; + return '这是桌面端 Orca Lead 任务。可以在手机上继续发消息聊天;创建 worker、切换 focus、fork/rewind、队列编辑和任务设置等编排操作请在电脑端完成。'; } if (session.orcaRole === 'worker') { - return '这是桌面端 Orca Worker 会话。手机版第一版只做安全查看,暂不提供发送消息、worker focus、归档 worker 或停止协作。'; + return '这是桌面端 Orca Worker 任务。手机版第一版只做安全查看,暂不提供发送消息、worker focus、归档 worker 或停止协作。'; } return sessionCollaborationLabel(session) - ? '这是桌面端协作会话。手机版第一版只做安全查看,协作编排操作请先在电脑端完成。' + ? '这是桌面端协作任务。手机版第一版只做安全查看,协作编排操作请先在电脑端完成。' : null; } @@ -44,23 +44,23 @@ export function isCollaborationSession(session: SessionCollaborationLike | null) } /** - * 写编排操作的只读 reason:对所有协作会话(lead + worker + 其它角色)返回 reason,驱动 fork/rewind、 - * 队列编辑、会话设置写、pending interaction 等高风险写操作的 gating(这些操作 lead 也仍留在电脑端)。 + * 写编排操作的只读 reason:对所有协作任务(lead + worker + 其它角色)返回 reason,驱动 fork/rewind、 + * 队列编辑、任务设置写、pending interaction 等高风险写操作的 gating(这些操作 lead 也仍留在电脑端)。 */ export function sessionCollaborationReadOnlyReason(session: SessionCollaborationLike | null): string | null { if (!isCollaborationSession(session)) return null; - return '协作模式手机版第一版为只读安全降级。处理确认、队列编辑和会话写操作请先在电脑端完成。'; + return '协作模式手机版第一版为只读安全降级。处理确认、队列编辑和任务写操作请先在电脑端完成。'; } /** - * 输入框(发消息)是否只读:只对**非 lead** 的协作会话(worker / 其它角色)返回 reason;对 Lead 会话 - * 返回 null —— Lead 允许在手机上发文字消息(集中在原对话里继续聊)。普通(非协作)会话也返回 null。 + * 输入框(发消息)是否只读:只对**非 lead** 的协作任务(worker / 其它角色)返回 reason;对 Lead 任务 + * 返回 null —— Lead 允许在手机上发文字消息(集中在原对话里继续聊)。普通(非协作)任务也返回 null。 * 与 sessionCollaborationReadOnlyReason(写编排只读)区分:composer 只读 ≠ 写编排只读。 */ export function sessionCollaborationComposerReadOnlyReason(session: SessionCollaborationLike | null): string | null { if (!isCollaborationSession(session)) return null; if (session?.orcaRole === 'lead') return null; - return '该协作子会话手机版为只读,发送消息请在电脑端完成。'; + return '该协作子任务手机版为只读,发送消息请在电脑端完成。'; } export function sessionWorktreeInfo(session: SessionWorktreeLike): SessionWorktreeInfo | null { diff --git a/packages/maker-shared/src/sessionList.ts b/packages/maker-shared/src/sessionList.ts index 93c00d46938..76fe3819980 100644 --- a/packages/maker-shared/src/sessionList.ts +++ b/packages/maker-shared/src/sessionList.ts @@ -159,7 +159,7 @@ export interface RemoteSessionListOptions { */ groupAutomations?: boolean; /** - * 「尚未起名」会话的显示文案(已解析的 i18n 值)。不传则回落 workingDir → 「未命名会话」; + * 「尚未起名」任务的显示文案(已解析的 i18n 值)。不传则回落 workingDir → 「未命名任务」; * 共享层刻意不兜中文串,见 {@link remoteSessionDisplayTitle}。 */ unnamedLabel?: string; @@ -298,9 +298,9 @@ export function buildRemoteSessionListContext({ const resultCount = sections.reduce((sum, section) => sum + section.data.reduce((sectionSum, item) => sectionSum + (item.automationGroup?.sessionCount ?? 1), 0), 0); const resultCopy = resultCount > 0 - ? `${resultCount} 个${normalizedQuery ? '匹配' : ''}会话` + ? `${resultCount} 个${normalizedQuery ? '匹配' : ''}任务` : normalizedQuery - ? '没有匹配会话' + ? '没有匹配任务' : '当前筛选无结果'; const rowCopy = rowCount > 0 && rowCount !== resultCount ? ` · ${rowCount} 行` : ''; return { @@ -318,7 +318,7 @@ export function remoteSessionOverviewCopy(overview: RemoteSessionOverview): stri overview.projectCount > 0 ? `${overview.projectCount} 个项目` : null, overview.runningAutomation > 0 ? `${overview.runningAutomation} 个自动化执行中` : null, ].filter(Boolean); - return parts.length > 0 ? parts.join(' · ') : '当前电脑暂无可汇总的会话活动。'; + return parts.length > 0 ? parts.join(' · ') : '当前电脑暂无可汇总的任务活动。'; } export function deviceSessionEmptyState( @@ -327,7 +327,7 @@ export function deviceSessionEmptyState( ): { title: string; copy: string } { if (searchQuery.trim()) { return { - title: '没有匹配的会话', + title: '没有匹配的任务', copy: '换一个标题、项目路径、模型或消息关键词再试。', }; } @@ -339,37 +339,37 @@ export function deviceSessionEmptyState( } if (statusFilter === 'automation') { return { - title: '没有自动化会话', - copy: '自动化任务运行后,对应会话会聚合到这里。', + title: '没有自动化生成的任务', + copy: '自动化运行后,对应任务会聚合到这里。', }; } if (statusFilter === 'archived') { return { - title: '没有归档会话', - copy: '长按会话可以批量归档或删除。', + title: '没有归档任务', + copy: '长按任务可以批量归档或删除。', }; } return { - title: '这台电脑暂无活动会话', - copy: '先在桌面端创建或继续一个会话,手机端会把它镜像到这里。', + title: '这台电脑暂无活动任务', + copy: '先在桌面端创建或继续一个任务,手机端会把它镜像到这里。', }; } function remoteSessionListTitle(statusFilter: RemoteSessionStatusFilter): string { - if (statusFilter === 'active') return '活跃会话'; - if (statusFilter === 'waiting') return '待处理会话'; - if (statusFilter === 'automation') return '自动化会话'; - if (statusFilter === 'archived') return '归档会话'; - return '全部会话'; + if (statusFilter === 'active') return '活跃任务'; + if (statusFilter === 'waiting') return '待处理任务'; + if (statusFilter === 'automation') return '自动化生成的任务'; + if (statusFilter === 'archived') return '归档任务'; + return '全部任务'; } function remoteSessionListHint(statusFilter: RemoteSessionStatusFilter, searchQuery: string): string { if (searchQuery) return '搜索范围包含标题、项目路径、模型、自动化名称和消息预览。'; if (statusFilter === 'waiting') return '这里集中显示需要在手机端确认的权限、计划和问题。'; - if (statusFilter === 'automation') return '自动化会话会按计划聚合,展开后可以进入单次运行。'; - if (statusFilter === 'archived') return '长按归档会话可进入批量选择,然后恢复或删除。'; - if (statusFilter === 'all') return '活跃、归档和自动化会话会一起显示,适合快速查找。'; - return '点开会话继续控制,长按会话进入批量选择。'; + if (statusFilter === 'automation') return '自动化生成的任务会按计划聚合,展开后可以进入单次运行。'; + if (statusFilter === 'archived') return '长按归档任务可进入批量选择,然后恢复或删除。'; + if (statusFilter === 'all') return '活跃、归档和自动化生成的任务会一起显示,适合快速查找。'; + return '点开任务继续控制,长按任务进入批量选择。'; } function buildProjectSections( @@ -426,7 +426,7 @@ function buildDateSections( * 远程会话在列表 / 菜单上应显示的标题。 * * 「尚未起名」的哨兵**直接用调用方给的本地化兜底、不回落 workingDir**:回落会把完整 - * 工作目录路径当标题显示,与 desktop 侧的「未命名对话」不一致(PR #1031 review P1)。 + * 工作目录路径当标题显示,与 desktop 侧的「未命名任务」不一致(PR #1031 review P1)。 * 哨兵本身是 locale-independent 的英文字面量(见 ./sessionTitle.ts),原样显示会露出 * "New Maker"。 * @@ -434,17 +434,17 @@ function buildDateSections( * 导出中文 UI 串),与 desktop 的 `getSessionDisplayTitle(session, unnamedLabel)` 同款 * 签名。 * - * 空标题(非哨兵)保持既有兜底链 workingDir → 「未命名会话」不变。 + * 空标题(非哨兵)保持既有兜底链 workingDir → 「未命名任务」不变。 */ export function remoteSessionDisplayTitle(session: RemoteSession, unnamedLabel?: string): string { if (isDefaultDraftSessionTitle(session.title)) { // 调用方给了**已解析的 i18n 文案**就用它;没给则按「无标题」走既有兜底链 - // (workingDir → '未命名会话'),即本 PR 之前的行为 —— 但无论如何都不把英文哨兵 + // (workingDir → '未命名任务'),即本 PR 之前的行为 —— 但无论如何都不把英文哨兵 // 原样显示出去。刻意不在共享层兜一个中文串:mobile 支持 en / ja / ko,那会让未接 // i18n 的调用方悄悄显示中文(PR #1031 review P1)。 - return unnamedLabel || session.workingDir || '未命名会话'; + return unnamedLabel || session.workingDir || '未命名任务'; } - return automationDisplayTitle(session) || session.workingDir || '未命名会话'; + return automationDisplayTitle(session) || session.workingDir || '未命名任务'; } export function toRemoteSessionListItem( @@ -454,7 +454,7 @@ export function toRemoteSessionListItem( pendingInteractionCount = 0, messagePreview: string | null = null, liveActivity: RemoteSessionLiveActivity | null = null, - /** 「尚未起名」会话的显示文案,由调用方传已解析的 i18n 值(见 remoteSessionDisplayTitle)。 */ + /** 「尚未起名」任务的显示文案,由调用方传已解析的 i18n 值(见 remoteSessionDisplayTitle)。 */ unnamedLabel?: string, ): RemoteSessionListItem { const lastActivityAt = session.userSendAt ?? session.updatedAt ?? session.createdAt; @@ -793,7 +793,7 @@ function automationDisplayTitle(session: RemoteSession): string { function fallbackScheduleInfo(session: RemoteSession): RemoteSessionScheduleInfo | null { if (!isAutomationGeneratedSession(session)) return null; - const title = automationDisplayTitle(session) || '自动化任务'; + const title = automationDisplayTitle(session) || '自动化'; return { scheduleId: '', scheduleName: title, @@ -881,14 +881,14 @@ function toAutomationGroupListItem( lastActivityAt: latestActivityAt, title: scheduleInfo?.scheduleName ?? primary.title, subtitle: [ - '自动化任务', - `${group.length} 个会话`, + '自动化', + `${group.length} 个任务`, primary.worktreeLabel, agentLabel(primary.session.agentKind), primary.session.model, ].filter(Boolean).join(' · '), detail: [ - `${group.length} 个会话`, + `${group.length} 个任务`, relativeTime(latestActivityAt, now), scheduleInfo?.running ? '自动化执行中' : null, scheduleInfo && scheduleInfo.unreadCount > 0 ? `${scheduleInfo.unreadCount} 个自动化未读` : null, diff --git a/packages/maker-shared/src/sessionOperation.ts b/packages/maker-shared/src/sessionOperation.ts index 0348755f657..13fee67d734 100644 --- a/packages/maker-shared/src/sessionOperation.ts +++ b/packages/maker-shared/src/sessionOperation.ts @@ -267,7 +267,7 @@ export function buildSessionOperationLayout(input: SessionOperationLayoutInput): if (!input.hasCurrentSession) { return { canUseComposer: false, - composerDisabledReason: '当前会话还没有同步完成。', + composerDisabledReason: '当前任务还没有同步完成。', composerDisabledReasonSource: 'session-syncing', composerSlot: 'missing-session', messageHistoryMode: 'hidden', @@ -370,7 +370,7 @@ function buildComposerGuidanceText(input: SessionComposerLayoutInput): string { if (hasAttachments && hasDraft) return `将发送 ${input.attachmentCount} 个附件和输入框里的文字。`; if (hasAttachments) return `将只发送 ${input.attachmentCount} 个附件,也可以补充说明后再发送。`; if (input.attachmentPickerOpen) return '可以添加手机上的照片、截图或文件。'; - if (hasDraft) return '点发送后会进入桌面端队列,按当前会话设置执行。'; + if (hasDraft) return '点发送后会进入桌面端队列,按当前任务设置执行。'; if (hasQuotes) return `将发送 ${input.quoteCount} 处引用,也可以补充说明后再发送。`; if (input.canStop) return '电脑端正在执行;可继续排队输入,或点停止保留当前队列。'; return '输入文字开始,使用 / 调命令,使用 @ 引用项目资源。'; diff --git a/packages/maker-shared/src/sessionSelection.ts b/packages/maker-shared/src/sessionSelection.ts index 5ba22a9fe57..10218247fce 100644 --- a/packages/maker-shared/src/sessionSelection.ts +++ b/packages/maker-shared/src/sessionSelection.ts @@ -65,13 +65,13 @@ export function summarizeMobileSessionBulkAction const candidates = sessions.filter((session) => isBulkActionCandidate(session, action)); const skippedCount = sessions.length - candidates.length; const actionLabel = bulkActionLabel(action); - const skipped = skippedCount > 0 ? `,将跳过 ${skippedCount} 个不适用的会话` : ''; + const skipped = skippedCount > 0 ? `,将跳过 ${skippedCount} 个不适用的任务` : ''; return { action, candidates, skippedCount, - title: `${actionLabel} ${candidates.length} 个会话`, - description: `将对当前选中的 ${candidates.length} 个会话执行${actionLabel}${skipped}。`, + title: `${actionLabel} ${candidates.length} 个任务`, + description: `将对当前选中的 ${candidates.length} 个任务执行${actionLabel}${skipped}。`, confirmText: actionLabel, }; } diff --git a/packages/maker-shared/src/systemCard.ts b/packages/maker-shared/src/systemCard.ts index 0506b14a1a2..64a4c1b992d 100644 --- a/packages/maker-shared/src/systemCard.ts +++ b/packages/maker-shared/src/systemCard.ts @@ -44,10 +44,10 @@ export interface SystemCardPresentation { export const DEFAULT_LOCAL_SYSTEM_COMMANDS: SystemCardSlashCommandLike[] = [ { kind: 'agent-builtin', name: 'help', description: '显示手机端和远程 agent 命令' }, - { kind: 'agent-builtin', name: 'context', description: '查看当前会话上下文用量' }, - { kind: 'agent-builtin', name: 'cost', description: '查看当前会话消耗' }, + { kind: 'agent-builtin', name: 'context', description: '查看当前任务上下文用量' }, + { kind: 'agent-builtin', name: 'cost', description: '查看当前任务消耗' }, { kind: 'agent-builtin', name: 'pwd', description: '显示当前远程工作目录' }, - { kind: 'agent-builtin', name: 'status', description: '显示当前会话状态' }, + { kind: 'agent-builtin', name: 'status', description: '显示当前任务状态' }, ]; export function parseLocalSystemCommand( @@ -155,7 +155,7 @@ export function formatSystemCard( return { title: 'Session spend', rows: [], - body: readString(record.detail) ?? '暂无会话用量', + body: readString(record.detail) ?? '暂无任务用量', }; } if (type === 'pwd') { @@ -171,7 +171,7 @@ export function formatSystemCard( body: readString(record.detail) ?? readString(record.summary) ?? readString(record.message) - ?? '会话压缩已完成。', + ?? '对话压缩已完成。', }; } if (type === 'cmd') { @@ -189,7 +189,7 @@ export function formatSystemCard( return { title: 'Session Status', rows: [ - { label: 'title', value: readString(record.title) || '未命名会话' }, + { label: 'title', value: readString(record.title) || '未命名任务' }, { label: 'status', value: readString(record.sessionStatus) || 'unknown' }, { label: 'agent', value: readString(record.agent) || 'unknown' }, { label: 'model', value: readString(record.model) || 'unknown' },