diff --git a/docs/tool-catalog.i18n.yaml b/docs/tool-catalog.i18n.yaml index 2c07846e9d..a23f1382dd 100644 --- a/docs/tool-catalog.i18n.yaml +++ b/docs/tool-catalog.i18n.yaml @@ -129,8 +129,8 @@ en: 07d595ce1f1a5184 zh: 07d595ce1f1a5184 /tool-schema-catalog/deepseek-ai-dsh-tool-fs/apply-patch: - en: 882f2fa9fac236d6 - zh: e8320796a5cb038f + en: 50b4939c9a1d96ac + zh: e733cc1156e2853c /tool-schema-catalog/deepseek-ai-dsh-tool-fs/edit: en: 5b6a252adde51af6 zh: 7aa172a5d8b9cbd2 diff --git a/docs/tool-catalog.md b/docs/tool-catalog.md index c6a06cef4b..29cd20a38c 100644 --- a/docs/tool-catalog.md +++ b/docs/tool-catalog.md @@ -1045,7 +1045,7 @@ Standalone view/create/unique literal replace/line insert tool over the filesyst ### `apply_patch` -Create, update, delete, or move UTF-8 text files with one Codex-style patch; prefer it over multiple edit calls when a change spans several files or separated regions. +Create, update, delete, or move UTF-8 text files with one patch; prefer it over multiple edit calls when a change spans several files or separated regions. ```json { @@ -1053,7 +1053,7 @@ Create, update, delete, or move UTF-8 text files with one Codex-style patch; pre "properties": { "input": { "type": "string", - "description": "Codex patch text: start with *** Begin Patch and end with *** End Patch. Use *** Add File: path with + lines; *** Update File: path, optionally followed by *** Move to: path, with @@ or @@ context and space-prefixed context, + added, or - removed lines; *** End of File anchors the last hunk to the file end. Use *** Delete File: path to delete. Paths are relative to the Session working directory." + "description": "Patch text: start with *** Begin Patch and end with *** End Patch. Use *** Add File: path with + lines; *** Update File: path, optionally followed by *** Move to: path, with @@ or @@ context and space-prefixed context, + added, or - removed lines; *** End of File anchors the last hunk to the file end. Use *** Delete File: path to delete. Paths are relative to the Session working directory." } }, "required": [ diff --git a/docs/tool-catalog.zh.md b/docs/tool-catalog.zh.md index ef5d06c5c1..8c17c2add3 100644 --- a/docs/tool-catalog.zh.md +++ b/docs/tool-catalog.zh.md @@ -1051,7 +1051,7 @@ pwsh 工具是 Windows 组合中 bash 执行器 seam 的 PowerShell 方言消费 ### `apply_patch` -用一个 Codex 风格补丁新增、更新、删除或移动 UTF-8 文本文件;变更跨越多个文件或分离区域时,优先于多次 edit 调用。 +用一个补丁新增、更新、删除或移动 UTF-8 文本文件;变更跨越多个文件或分离区域时,优先于多次 edit 调用。 ```json { @@ -1059,7 +1059,7 @@ pwsh 工具是 Windows 组合中 bash 执行器 seam 的 PowerShell 方言消费 "properties": { "input": { "type": "string", - "description": "Codex patch text: start with *** Begin Patch and end with *** End Patch. Use *** Add File: path with + lines; *** Update File: path, optionally followed by *** Move to: path, with @@ or @@ context and space-prefixed context, + added, or - removed lines; *** End of File anchors the last hunk to the file end. Use *** Delete File: path to delete. Paths are relative to the Session working directory." + "description": "Patch text: start with *** Begin Patch and end with *** End Patch. Use *** Add File: path with + lines; *** Update File: path, optionally followed by *** Move to: path, with @@ or @@ context and space-prefixed context, + added, or - removed lines; *** End of File anchors the last hunk to the file end. Use *** Delete File: path to delete. Paths are relative to the Session working directory." } }, "required": [ diff --git a/packages/client/tsdown.client.ts b/packages/client/tsdown.client.ts index 5eaf0b2a35..8895b1dce9 100644 --- a/packages/client/tsdown.client.ts +++ b/packages/client/tsdown.client.ts @@ -82,7 +82,7 @@ function styleInjectionModule( * Everything else under @deepseek-ai/* is either a module-table entry * (external) or a leak the purity gate rejects. */ -export const INLINE_SAFE = /^(?:@deepseek-ai\/dsh-(?:file-reference|session|llm|tools|brand|official-bundles|deque|output-retention|typert-protocol|util-crypto|util-time|util-values|util-workspace-path)(?:\/|$)|@deepseek-ai\/dsh-util-file-drag$|@deepseek-ai\/dsh-token-meter\/client$|@deepseek-ai\/dsh-commands\/builtin$|@deepseek-ai\/dsh-native-command\/types$|@deepseek-ai\/dsh-host-open-in-app\/shared$|@deepseek-ai\/dsh-plugin-manager\/registry$|@deepseek-ai\/dsh-agent-preset-registry\/display$|@deepseek-ai\/dsh-api-workspace-controller\/default-workspace$|@deepseek-ai\/dsh-spill-policy\/notice$)/ +export const INLINE_SAFE = /^(?:@deepseek-ai\/dsh-(?:file-reference|fs-patch|session|llm|tools|brand|official-bundles|deque|output-retention|typert-protocol|util-crypto|util-time|util-values|util-workspace-path)(?:\/|$)|@deepseek-ai\/dsh-util-file-drag$|@deepseek-ai\/dsh-token-meter\/client$|@deepseek-ai\/dsh-commands\/builtin$|@deepseek-ai\/dsh-native-command\/types$|@deepseek-ai\/dsh-host-open-in-app\/shared$|@deepseek-ai\/dsh-plugin-manager\/registry$|@deepseek-ai\/dsh-agent-preset-registry\/display$|@deepseek-ai\/dsh-api-workspace-controller\/default-workspace$|@deepseek-ai\/dsh-spill-policy\/notice$)/ /** * Vendored framework libraries: rescoped into @deepseek-ai, so the gate below diff --git a/packages/client/ui-conversation/src/client/locales.ts b/packages/client/ui-conversation/src/client/locales.ts index d89b2e91c4..9da780b793 100644 --- a/packages/client/ui-conversation/src/client/locales.ts +++ b/packages/client/ui-conversation/src/client/locales.ts @@ -329,7 +329,7 @@ export const zh = { 'tool.title.bash': '运行命令', 'tool.title.write': '写入', 'tool.title.edit': '编辑', - 'tool.title.applyPatch': '应用补丁', + 'tool.title.applyPatch': '编辑', 'tool.title.code': '代码', 'tool.title.generic': '工具调用', 'tool.title.inspect': '查询 Cordis 环境', @@ -720,7 +720,7 @@ export const en = { 'tool.title.bash': 'Bash', 'tool.title.write': 'Write', 'tool.title.edit': 'Edit', - 'tool.title.applyPatch': 'Apply patch', + 'tool.title.applyPatch': 'Edit', 'tool.title.code': 'Code', 'tool.title.generic': 'Tool call', 'tool.title.inspect': 'Query Cordis environment', @@ -1110,7 +1110,7 @@ export const ja = { 'tool.title.bash': 'Bash', 'tool.title.write': '書き込み', 'tool.title.edit': '編集', - 'tool.title.applyPatch': 'パッチを適用', + 'tool.title.applyPatch': '編集', 'tool.title.code': 'コード', 'tool.title.generic': 'ツール呼び出し', 'tool.title.inspect': 'Cordis 環境を照会', @@ -1493,7 +1493,7 @@ export const es = { 'tool.title.bash': 'golpe', 'tool.title.write': 'escribir', 'tool.title.edit': 'Editar', - 'tool.title.applyPatch': 'Aplicar parche', + 'tool.title.applyPatch': 'Editar', 'tool.title.code': 'Código', 'tool.title.generic': 'llamada de herramienta', 'tool.title.inspect': 'Consulta entorno Cordis', @@ -1883,7 +1883,7 @@ export const fr = { 'tool.title.bash': 'Frapper', 'tool.title.write': 'Écrire', 'tool.title.edit': 'Modifier', - 'tool.title.applyPatch': 'Appliquer le patch', + 'tool.title.applyPatch': 'Modifier', 'tool.title.code': 'Coder', 'tool.title.generic': "Appel d'outil", 'tool.title.inspect': "Interroger l'environnement Cordis", @@ -2273,7 +2273,7 @@ export const de = { 'tool.title.bash': 'Bash', 'tool.title.write': 'Schreiben', 'tool.title.edit': 'Bearbeiten', - 'tool.title.applyPatch': 'Patch anwenden', + 'tool.title.applyPatch': 'Bearbeiten', 'tool.title.code': 'Code', 'tool.title.generic': 'Werkzeugaufruf', 'tool.title.inspect': 'Fragen Sie die Cordis-Umgebung ab', @@ -2663,7 +2663,7 @@ export const ru = { 'tool.title.bash': 'Баш', 'tool.title.write': 'Написать', 'tool.title.edit': 'Редактировать', - 'tool.title.applyPatch': 'Применить патч', + 'tool.title.applyPatch': 'Редактировать', 'tool.title.code': 'Код', 'tool.title.generic': 'Вызов инструмента', 'tool.title.inspect': 'Запрос среды Cordis', @@ -3053,7 +3053,7 @@ export const pt = { 'tool.title.bash': 'Bash', 'tool.title.write': 'Escreva', 'tool.title.edit': 'Editar', - 'tool.title.applyPatch': 'Aplicar patch', + 'tool.title.applyPatch': 'Editar', 'tool.title.code': 'Código', 'tool.title.generic': 'Chamada de ferramenta', 'tool.title.inspect': 'Consultar ambiente Cordis', diff --git a/packages/client/ui-tool/README.i18n.yaml b/packages/client/ui-tool/README.i18n.yaml index b2d8ee1464..00c7ad6832 100644 --- a/packages/client/ui-tool/README.i18n.yaml +++ b/packages/client/ui-tool/README.i18n.yaml @@ -15,14 +15,14 @@ en: d152484eb41ac6b4 zh: 09388d293f9be9cb /deepseek-ai-dsh-client-ui-tool/use-this-package: - en: 2419beff9bcd8139 - zh: 5aafe9b168e571a5 + en: 813b9f2e3438eed2 + zh: ac7616097d45991a /deepseek-ai-dsh-client-ui-tool/use-this-package/registering-a-business-tool-view: en: e458aff61e634190 zh: 650e417f450b3edf /deepseek-ai-dsh-client-ui-tool/use-this-package/built-in-views: - en: 153498fd97c03524 - zh: 210731b119db29a5 + en: 4f141937224a0390 + zh: b3b8a22df4936742 /deepseek-ai-dsh-client-ui-tool/understand-the-implementation: en: 00bc7b0c84edc603 zh: 1cfcf5b435248a91 diff --git a/packages/client/ui-tool/README.md b/packages/client/ui-tool/README.md index a55e8ab7ed..61fb410cef 100644 --- a/packages/client/ui-tool/README.md +++ b/packages/client/ui-tool/README.md @@ -33,7 +33,7 @@ Before dispatch, a named model call appears as one non-expandable row with its t `subagent` and `subagent_fork` creation rows stream the first line of `description`, retaining it during execution, after creation, and when expanded. After preparation, calls without a description fall back to the first nonempty `prompt` line; missing arguments leave only the tool title. Both tools and `list_subagent_models` use the existing Users icon; `subagent_fork` uses the distinct title “Fork subagent”. Creation receipts show the started state, the full `description` as the agent task description, the recorded agent Session id, and complete task instructions. Current receipts read `kind: activation` and `subagentId` from result metadata; only historical results without metadata use text parsing. Malformed metadata keeps the generic result view. Synchronous agent responses remain in the result body; errors retain their failure summary. -File and URL toolview Slots obtain `useContextMenu` directly and supply bound `fileContextMenuProps` or `urlContextMenuProps` to `ToolRow`. File registrations inject a gesture-time path resolver for the originating Session’s working directory, falling back to its workspace root. `ToolCallTree` supplies an explicit file adapter to its plain `GenericToolCard` fallback, including nested calls. No Markdown provider is required. Mouse and keyboard gestures retain the captured path, line, URL, and Session without expanding the row or opening the file. +File and URL toolview Slots obtain `useContextMenu` directly and supply bound `fileContextMenuProps` or `urlContextMenuProps` to `ToolRow`; file rows also supply `fileTargetContextMenuProps` for each per-file patch link. File registrations inject a gesture-time path resolver for the originating Session’s working directory, falling back to its workspace root. `ToolCallTree` supplies an explicit file adapter to its plain `GenericToolCard` fallback, including nested calls. No Markdown provider is required. Mouse and keyboard gestures retain the captured path, line, URL, and Session without expanding the row or opening the file. ### Registering a business tool view @@ -55,7 +55,7 @@ Every registered view receives the explicit `preparing`, `start`, and `result` p File-tool rows prefer a complete `file_path`; other generic rows, including search and unregistered tools, prefer a non-empty `description` before their variant-specific argument fallback. Read `args` during rendering or view construction: a preparing reader grows in place, and only previously observed answer changes trigger subsequent block publication. -This package owns the generic fallback and the built-in shell/pwsh, read, read_image, write/edit, `apply_patch`, running `str_replace_editor` `create`/`str_replace`, grep/glob, web, todo, question, and PTC dispatch presentations. The `apply_patch` row shares the write/edit component: it shows its title without a single-file summary, derives no diff while running, shows the applied hunks from settled `meta.diffs`, and a result whose metadata is marked stopped renders as an error with its failure text beside the known diffs. Structured cards derive directly from first-party raw event fields; Host `presentCall` and `presentResult` values never enter the Client. Running atomic rows share one left-to-right highlight across their titles, separators, summaries, and suffixes, while settled rows stay static. Running and settled foreground standard `bash`/`pwsh` and `terminal_send` calls use terminal cards at the root and in PTC dispatch children, subject to the same argument, result, and error checks. Persistent `bash`/`pwsh` calls use terminal cards only while running. Shell output ending in a recognized spill-policy notice uses expandable generic output in shell rows and generic output in Details; a displaced or omitted exit marker cannot establish success. Settled persistent-shell results stay generic because reset and partial-output diagnostics do not always describe one process exit status; root persistent results are expandable. Valid explicit background `bash`/`pwsh` calls use the terminal Start presentation; a successful launch acknowledgement retains the command and cwd in a static command-only card without output or execution status. Launch failures retain generic arguments and errors, while background `terminal_send` stays generic. Inspect preserves the original arguments and acknowledgement; later job output is not attached to this card ([decision](../../../.agents/notes/implemented/bug-fix/2026-10-10-background-shell-command-display.md)). Expanded terminal commands and background inputs wrap to the available width while preserving authored line breaks and selectable text; terminal output keeps its column-preserving layout. Only horizontal overflow adds a named keyboard focus target. Inspect is available when provided. The acknowledgement does not report the job's exit status. A native or PTC dispatch failure carrying `AUTO_REVIEW_DENIED` shows the Auto review verdict in its collapsed row and one normalized not-executed reason when expanded; a missing or whitespace-only reason uses localized fallback text. `ui-skill` demonstrates a business-owned registration for `skill`. +This package owns the generic fallback and the built-in shell/pwsh, read, read_image, write/edit, `apply_patch`, running `str_replace_editor` `create`/`str_replace`, grep/glob, web, todo, question, and PTC dispatch presentations. The `apply_patch` row shares the write/edit component and the Edit title. Once `input` completes, its summary lists the files that `dsh-fs-patch` `parsePatch` reads from the patch, with a move as `source → destination`; a malformed patch has no summary. A lone added or updated file is one openable path. After a successful result, several files or a move render one link per added, updated, or moved-to file, while move sources and deleted files stay plain text; running and failed rows show the plain list. The row derives no diff while running, shows the applied hunks from settled `meta.diffs`, and a result whose metadata is marked stopped renders as an error with its failure text beside the known diffs. Structured cards derive directly from first-party raw event fields; Host `presentCall` and `presentResult` values never enter the Client. Running atomic rows share one left-to-right highlight across their titles, separators, summaries, and suffixes, while settled rows stay static. Running and settled foreground standard `bash`/`pwsh` and `terminal_send` calls use terminal cards at the root and in PTC dispatch children, subject to the same argument, result, and error checks. Persistent `bash`/`pwsh` calls use terminal cards only while running. Shell output ending in a recognized spill-policy notice uses expandable generic output in shell rows and generic output in Details; a displaced or omitted exit marker cannot establish success. Settled persistent-shell results stay generic because reset and partial-output diagnostics do not always describe one process exit status; root persistent results are expandable. Valid explicit background `bash`/`pwsh` calls use the terminal Start presentation; a successful launch acknowledgement retains the command and cwd in a static command-only card without output or execution status. Launch failures retain generic arguments and errors, while background `terminal_send` stays generic. Inspect preserves the original arguments and acknowledgement; later job output is not attached to this card ([decision](../../../.agents/notes/implemented/bug-fix/2026-10-10-background-shell-command-display.md)). Expanded terminal commands and background inputs wrap to the available width while preserving authored line breaks and selectable text; terminal output keeps its column-preserving layout. Only horizontal overflow adds a named keyboard focus target. Inspect is available when provided. The acknowledgement does not report the job's exit status. A native or PTC dispatch failure carrying `AUTO_REVIEW_DENIED` shows the Auto review verdict in its collapsed row and one normalized not-executed reason when expanded; a missing or whitespace-only reason uses localized fallback text. `ui-skill` demonstrates a business-owned registration for `skill`. Question rows pair answers with questions by id, using the tool result or the `userQuestions` projection for late replies. An answerable call has a button to reopen its panel. Once a late reply is in the agent Inbox, the row shows its submitted answers; “View answers” expands its read-only transcript while admission is pending. A settled call with answers opens the panel read-only. Closed calls without answers and invalid records offer no button. Cancelled and interrupted rows show their verdict without inventing answers. If no panel provider accepts the button action, it expands the row. Unsupported or ambiguous input falls back to flattened Tool text. diff --git a/packages/client/ui-tool/README.zh.md b/packages/client/ui-tool/README.zh.md index 55e1fa9ad1..851266138d 100644 --- a/packages/client/ui-tool/README.zh.md +++ b/packages/client/ui-tool/README.zh.md @@ -33,7 +33,7 @@ kind: "package-reference" `subagent` 与 `subagent_fork` 的创建行逐步显示 `description` 的首行,执行中、创建完成后和展开时均保留。准备阶段结束后,缺少描述的调用回退到 `prompt` 的首个非空行;参数不可用时仅显示工具标题。两个工具与 `list_subagent_models` 均使用现有的 Users 图标;`subagent_fork` 使用独立标题“派生子智能体”。创建回执显示已启动状态、作为智能体任务描述的完整 `description`、记录的智能体 Session id,以及完整任务指令。当前回执从结果 metadata 读取 `kind: activation` 与 `subagentId`;仅缺少 metadata 的历史结果使用文本解析。metadata 无效时保留通用结果视图。同步返回的 agent 回复仍放在结果正文中;失败时保留错误摘要。 -文件和 URL 工具视图 Slot 直接获取 `useContextMenu`,向 `ToolRow` 提供已绑定的 `fileContextMenuProps` 或 `urlContextMenuProps`。文件注册注入在触发时读取原始 Session 工作目录的路径解析器,工作目录缺失时使用工作区根目录。`ToolCallTree` 向普通 `GenericToolCard` fallback 显式提供文件适配函数,包含嵌套调用。无需 Markdown Provider。鼠标和键盘手势保留捕获的路径、行号、URL 与 Session,不会展开工具行或打开文件。 +文件和 URL 工具视图 Slot 直接获取 `useContextMenu`,向 `ToolRow` 提供已绑定的 `fileContextMenuProps` 或 `urlContextMenuProps`;文件行还为补丁的每个文件链接提供 `fileTargetContextMenuProps`。文件注册注入在触发时读取原始 Session 工作目录的路径解析器,工作目录缺失时使用工作区根目录。`ToolCallTree` 向普通 `GenericToolCard` fallback 显式提供文件适配函数,包含嵌套调用。无需 Markdown Provider。鼠标和键盘手势保留捕获的路径、行号、URL 与 Session,不会展开工具行或打开文件。 ### 注册业务工具视图 @@ -55,7 +55,7 @@ owner 载荷为 `ToolCallOwnerProps`:`callId`、`toolName`、`phase` 判别字 文件工具行优先使用完整的 `file_path`;其他通用行(包括搜索和未注册的工具)优先使用非空 `description`,再回退到各自类别的参数摘要。应在渲染或构造视图时读取 `args`:准备态读器原地增长,后续仅在已观察答案变化时重新发布块。 -本包拥有 generic fallback,以及 shell/pwsh、read、read_image、write/edit、`apply_patch`、运行中的 `str_replace_editor` `create`/`str_replace`、grep/glob、web、todo、question 与 PTC dispatch 的内置展示。`apply_patch` 行复用 write/edit 组件:显示工具标题而不给出单文件摘要,运行态不推导 diff,完成态展示 `meta.diffs` 中已应用的 hunk,元数据标记为停止的结果按错误呈现,并在已知 diff 旁保留失败文本。结构化卡片直接从第一方原始 event 字段派生;Host `presentCall` 与 `presentResult` 值不会进入 Client。运行中的原子工具行以同一道高光从左到右扫过标题、分隔符、摘要与后缀;已完成行保持静态。运行中与已完成的前台标准 `bash`/`pwsh` 和 `terminal_send` 调用,无论位于根还是 PTC dispatch 子调用中,都在通过相同的参数、结果和错误检查后使用 terminal 卡片。持久 `bash`/`pwsh` 调用仅在运行中使用 terminal 卡片。以已识别的 spill 策略提示结尾的 shell 输出,在 shell 行中使用可展开的 generic 输出,在 Details 中使用 generic 输出;位置被改变或被省略的退出标记无法证明成功。已完成的持久 shell 结果保持 generic 展示,因为 reset 与部分输出诊断不一定描述单个进程的退出状态;根调用的持久 shell 结果可展开。合法的主动后台 `bash`/`pwsh` 调用使用 terminal Start 展示;成功启动回执后,静态仅命令卡片保留 command 和 cwd,不显示输出或执行状态。启动失败保留通用参数与错误展示,后台 `terminal_send` 仍为 generic。Inspect 保留原始参数及回执,后续 job 输出不接回此卡片([决定](../../../.agents/notes/implemented/bug-fix/2026-10-10-background-shell-command-display.zh.md))。展开的终端命令与后台输入按可用宽度自动折行,保留原有换行和可选文本;终端输出保持按列对齐的排版。仅横向溢出时添加具名的键盘聚焦目标。提供 Inspect 时也可使用。回执不表示后台任务的退出状态。带有 `AUTO_REVIEW_DENIED` 的原生或 PTC dispatch 失败会在折叠行显示 Auto review 裁决,展开时显示一行归一化后的“未执行”原因;原因缺失或只有空白时使用本地化 fallback 文案。`ui-skill` 展示了业务包自行拥有的 `skill` 注册项。 +本包拥有 generic fallback,以及 shell/pwsh、read、read_image、write/edit、`apply_patch`、运行中的 `str_replace_editor` `create`/`str_replace`、grep/glob、web、todo、question 与 PTC dispatch 的内置展示。`apply_patch` 行复用 write/edit 组件与“编辑”标题。`input` 完整到达后,摘要列出 `dsh-fs-patch` 的 `parsePatch` 从补丁读出的文件,移动显示为 `源 → 目标`;格式不合法的补丁没有摘要。单个新增或修改的文件是一个可打开路径。成功结果中有多个文件或含移动时,每个新增、修改或移动目标文件各是一个链接,移动源与被删除文件保持纯文本;运行中与失败的行显示纯文本列表。该行运行态不推导 diff,完成态展示 `meta.diffs` 中已应用的 hunk,元数据标记为停止的结果按错误呈现,并在已知 diff 旁保留失败文本。结构化卡片直接从第一方原始 event 字段派生;Host `presentCall` 与 `presentResult` 值不会进入 Client。运行中的原子工具行以同一道高光从左到右扫过标题、分隔符、摘要与后缀;已完成行保持静态。运行中与已完成的前台标准 `bash`/`pwsh` 和 `terminal_send` 调用,无论位于根还是 PTC dispatch 子调用中,都在通过相同的参数、结果和错误检查后使用 terminal 卡片。持久 `bash`/`pwsh` 调用仅在运行中使用 terminal 卡片。以已识别的 spill 策略提示结尾的 shell 输出,在 shell 行中使用可展开的 generic 输出,在 Details 中使用 generic 输出;位置被改变或被省略的退出标记无法证明成功。已完成的持久 shell 结果保持 generic 展示,因为 reset 与部分输出诊断不一定描述单个进程的退出状态;根调用的持久 shell 结果可展开。合法的主动后台 `bash`/`pwsh` 调用使用 terminal Start 展示;成功启动回执后,静态仅命令卡片保留 command 和 cwd,不显示输出或执行状态。启动失败保留通用参数与错误展示,后台 `terminal_send` 仍为 generic。Inspect 保留原始参数及回执,后续 job 输出不接回此卡片([决定](../../../.agents/notes/implemented/bug-fix/2026-10-10-background-shell-command-display.zh.md))。展开的终端命令与后台输入按可用宽度自动折行,保留原有换行和可选文本;终端输出保持按列对齐的排版。仅横向溢出时添加具名的键盘聚焦目标。提供 Inspect 时也可使用。回执不表示后台任务的退出状态。带有 `AUTO_REVIEW_DENIED` 的原生或 PTC dispatch 失败会在折叠行显示 Auto review 裁决,展开时显示一行归一化后的“未执行”原因;原因缺失或只有空白时使用本地化 fallback 文案。`ui-skill` 展示了业务包自行拥有的 `skill` 注册项。 问题行按 id 配对问题与答案;答案来自工具结果,迟到回答则来自 `userQuestions` 投影。可回答的调用提供按钮以重新打开面板。迟到回复进入 agent Inbox 后,问题行显示已提交的答案;等待准入期间,「查看回答」展开只读的行内记录。带有答案的已结算调用则以只读方式打开面板。没有答案的已关闭调用和无效记录不提供按钮。已取消和已中断的行显示裁决,不虚构答案。若面板提供方不接受按钮操作,点击会展开该行。不受支持或含糊的输入回退为压平的工具文本。 diff --git a/packages/client/ui-tool/package.json b/packages/client/ui-tool/package.json index feb4aa74bc..c46fbdedb2 100644 --- a/packages/client/ui-tool/package.json +++ b/packages/client/ui-tool/package.json @@ -48,6 +48,7 @@ }, "devDependencies": { "@deepseek-ai/dsh-spill-policy": "workspace:*", + "@deepseek-ai/dsh-fs-patch": "workspace:*", "@deepseek-ai/dsh-agent": "workspace:*", "@deepseek-ai/dsh-llm": "workspace:*", "@deepseek-ai/dsh-session": "workspace:*", diff --git a/packages/client/ui-tool/src/client/tool/components/ToolRow.module.css b/packages/client/ui-tool/src/client/tool/components/ToolRow.module.css index e7f99f96c4..7d5bd8a6a6 100644 --- a/packages/client/ui-tool/src/client/tool/components/ToolRow.module.css +++ b/packages/client/ui-tool/src/client/tool/components/ToolRow.module.css @@ -108,6 +108,36 @@ font-size: var(--dsh-content-font-size-secondary, 13px); line-height: calc(24px + var(--dsh-content-font-delta, 0px)); color: inherit; +} + +/* Multi-file patch summary: one ellipsized line of plain separators and per-file + links. Shrink-to-fit like .fileLink, so the row's empty remainder stays the + expand/collapse toggle. */ +.fileLinks { + flex: 0 1 auto; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: var(--dsh-content-font-size-secondary, 13px); + line-height: calc(24px + var(--dsh-content-font-delta, 0px)); +} + +/* One file inside .fileLinks: .fileLink's affordance without its own box, so the + line keeps flowing and the parent's ellipsis applies. */ +.fileTargetLink { + display: inline; + margin: 0; + padding: 0; + border: none; + background: none; + font: inherit; + color: inherit; +} + +/* Path link affordance shared by the whole-summary and per-file links. */ +.fileLink, +.fileTargetLink { text-decoration: underline dotted; text-decoration-color: currentColor; text-decoration-thickness: 1px; @@ -115,6 +145,23 @@ cursor: pointer; } +/* Pointing at one file of a multi-file patch brightens that file alone: the + row keeps its idle color and diff totals instead of the whole-row hover. */ +@media (hover: hover) { + .row:has(.fileTargetLink:hover) { + color: var(--dsw-alias-label-tertiary); + } + + .row:not([aria-expanded='true']):has(.fileTargetLink:hover) .diffAdded, + .row:not([aria-expanded='true']):has(.fileTargetLink:hover) .diffRemoved { + color: inherit; + } + + .fileTargetLink:hover { + color: var(--dsw-alias-label-secondary); + } +} + /* Error row's collapsed summary: the failure's first line in the error color. */ .errorSummary { color: var(--dsw-alias-state-error-primary); diff --git a/packages/client/ui-tool/src/client/tool/components/ToolRow.tsx b/packages/client/ui-tool/src/client/tool/components/ToolRow.tsx index 851a151226..9985614f80 100644 --- a/packages/client/ui-tool/src/client/tool/components/ToolRow.tsx +++ b/packages/client/ui-tool/src/client/tool/components/ToolRow.tsx @@ -1,4 +1,4 @@ -import { memo, useCallback, useMemo, type KeyboardEvent, type MouseEvent, type ReactNode } from 'react' +import { Fragment, memo, useCallback, useMemo, type KeyboardEvent, type MouseEvent, type ReactNode } from 'react' import clsx from 'clsx' import { CodeBlock, DiffBlock, DisclosureRow, IconInspectOutlineRegular, ReadBlock, SearchBlock, @@ -21,7 +21,7 @@ import { } from '../models/primitive-labels.ts' import type { AskQuestionCardModel } from '../models/ask-question-card-model.ts' import { - formatToolBody, type ToolRowState, type ToolRowVariant, + FILE_TARGET_MOVE, FILE_TARGET_SEPARATOR, formatToolBody, type ToolRowFileTarget, type ToolRowState, type ToolRowVariant, } from '../models/tool-call-model.ts' import type { WebCardModelProps } from '../models/web-card-model.ts' import { AskQuestionCard } from './AskQuestionCard.tsx' @@ -93,6 +93,14 @@ export interface ToolRowProps { onOpenFile?: ((path: string, options?: OpenFileOptions) => void) | undefined /** File gesture bindings supplied by the owning tool view. */ fileContextMenuProps?: ContextMenuTriggerProps | undefined + /** + * Files a patch row names. When onOpenFile is set, the row state is `ok`, and + * at least one target has a `filePath`, each such target renders as its own + * link in place of the summary text; otherwise the row shows `summary`. + */ + fileTargets?: readonly ToolRowFileTarget[] | undefined + /** File gesture bindings for one `fileTargets` path, supplied by the owning tool view. */ + fileTargetContextMenuProps?: ((path: string) => ContextMenuTriggerProps) | undefined /** URL gesture bindings supplied by the owning tool view. */ urlContextMenuProps?: ContextMenuTriggerProps | undefined /** Safe http(s) URL from tool args; when set, the summary renders as a link that opens it in a new tab. */ @@ -109,6 +117,35 @@ function stopLinkClick(event: MouseEvent): void { event.stopPropagation() } +interface FileTargetLinkProps { + label: string + path: string + onOpenFile: (path: string, options?: OpenFileOptions) => void + contextMenuProps: ContextMenuTriggerProps | undefined + onLinkKeyDown: (event: KeyboardEvent) => void +} + +/** One openable file inside a multi-file summary; activating it never toggles the row. */ +function FileTargetLink({ label, path, onOpenFile, contextMenuProps, onLinkKeyDown }: FileTargetLinkProps) { + return ( + + ) +} + /** Visually hidden run-state label for color-only running and settlement cues. */ function stateStatus(state: ToolRowState, t: TranslateNS<'conversation'>): string | null { switch (state) { @@ -151,7 +188,7 @@ export const ToolRow = memo(function ToolRow({ filePath, filePathLine, onOpenFile, - fileContextMenuProps, urlContextMenuProps, + fileContextMenuProps, urlContextMenuProps, fileTargets, fileTargetContextMenuProps, href, inspect, useDisclosure, @@ -203,6 +240,10 @@ export const ToolRow = memo(function ToolRow({ } : undefined, [filePath, filePathLine, onOpenFile, settledWithCue]) const linkHref = settledWithCue ? undefined : href + // Per-file links appear only on a successful result: TextShimmer accepts no + // buttons, so running and failed rows keep the plain summary text. + const openTarget = state === 'ok' && fileTargets?.some(target => target.filePath !== undefined) === true + ? onOpenFile : undefined // Keep Enter/Space on the focused path or URL link from bubbling to the row's // keydown handler, which would preventDefault() the key and toggle expand // instead of activating the link — the keyboard analogue of the click @@ -219,7 +260,25 @@ export const ToolRow = memo(function ToolRow({ its title shows no trailing dot). */ <> - {exitLabel === null && openFile !== undefined ? ( + {exitLabel === null && fileTargets !== undefined && openTarget !== undefined ? ( + + {fileTargets.map((target, index) => ( + + {index > 0 && FILE_TARGET_SEPARATOR} + {target.from !== undefined && `${target.from}${FILE_TARGET_MOVE}`} + {target.filePath === undefined ? target.label : ( + + )} + + ))} + + ) : exitLabel === null && openFile !== undefined ? (