ソースを参照

docs(tools): document the abort message and the background call card; pin both with tests

Huanqi Cao 1 ヶ月 前
コミット
efcee43c7d

+ 2 - 2
packages/bash/pwsh-local/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/bash/pwsh-local/README.md
-README.md: bb5d941b0cc98fb14e0b47d5bbe0bffb5192edd8
-README.zh.md: efd8aac2b8766d1082b85905fce9867151b77310
+README.md: c5e0cc524e32d2218e734539c74adcddb663d4f3
+README.zh.md: 7906d8a511ee72ecdffaddaf88ddebe195a20857

+ 1 - 1
packages/bash/pwsh-local/README.md

@@ -50,6 +50,6 @@ No direct invalidation; the named consumer owns any request-prefix changes.
 - **The command string is PowerShell text** — the `-Command` domain has no shell-quoting layer, but a model-facing command is parsed by PowerShell itself, so PowerShell syntax errors are command failures, not launch failures.
 - **A background spawn-failure note is single-delivery** — the subprocess service buffers no output for a process that never ran, so the executor injects `spawn failed: …` into exactly one `readOutput()` delta; a reader that discards that delta cannot recover it.
 - **Windows termination reports no signal** — a force-killed process settles as exit 1 with `signal: null`, so signal-based status classification (POSIX `killed`) does not apply on Windows; `kill()`-initiated stops still stamp `killed` directly.
-- **The encoding preamble precedes the command** — PowerShell requires `param(...)` and `using namespace`/`using assembly` statements at the very top of a script, so a command whose first statement is one of those cannot run under the UTF-8 output preamble; prefix such scripts with a no-op statement (or `& { … }`) first.
+- **The encoding preamble precedes the command** — PowerShell requires `param(...)`, `#requires`, and `using namespace`/`using assembly` statements at the very top of a script, so a command whose first statement is one of those cannot run under the UTF-8 output preamble; prefix such scripts with a no-op statement (or `& { … }`) first.
 
 Scrub-heuristic and spill-retention caveats live with [`dsh-subprocess-local`](../../subprocess/subprocess-local/README.md), which owns those mechanics.

+ 1 - 1
packages/bash/pwsh-local/README.zh.md

@@ -50,6 +50,6 @@
 - **命令字符串是 PowerShell 文本**——`-Command` 域没有 shell 引号层,但面向模型的命令由 PowerShell 自己解析,因此 PowerShell 语法错误是命令失败,而非启动失败。
 - **后台 spawn 失败提示只投递一次**——subprocess 服务不会为从未运行的进程缓冲输出,因此执行器只把 `spawn failed: …` 注入一次 `readOutput()` 增量;丢弃该增量的读取方无法恢复它。
 - **Windows 终止不报告信号**——被强制终止的进程以退出码 1、`signal: null` 结束,因此基于信号的状态分类(POSIX `killed`)在 Windows 上不适用;`kill()` 发起的停止仍会直接盖上 `killed`。
-- **编码 preamble 位于命令之前**——PowerShell 要求 `param(...)` 与 `using namespace`/`using assembly` 语句位于脚本最顶部,因此以其中一种开头的命令无法在 UTF-8 输出 preamble 下运行;请先用无操作语句(或 `& { … }`)开头。
+- **编码 preamble 位于命令之前**——PowerShell 要求 `param(...)`、`#requires` 与 `using namespace`/`using assembly` 语句位于脚本最顶部,因此以其中一种开头的命令无法在 UTF-8 输出 preamble 下运行;请先用无操作语句(或 `& { … }`)开头。
 
 清理启发式与 spill 保留的注意事项由 [`dsh-subprocess-local`](../../subprocess/subprocess-local/README.md) 持有,它拥有这些机制。

+ 2 - 2
packages/bash/tool-bash/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/bash/tool-bash/README.md
-README.md: 29b9fba369e1fc6a4b8bb7bdd6543b7678df627d
-README.zh.md: 31f691f7bfb8d2cb905751663151c3f6a6bc6c57
+README.md: e3c8c445c8959b7f49705bb59af2bf61e0e716c8
+README.zh.md: fc5b3794187dcfd1f8b382bc805595c8ae887c55

+ 1 - 1
packages/bash/tool-bash/README.md

@@ -141,7 +141,7 @@ Append-only; newly visible content follows the reusable request prefix and does
 
 #### What the model sees
 
-Validation and policy failures are normalized as `Error: <message>`. This package's stable messages are `invalid command: expected a non-empty string`, `invalid description: expected a non-empty string`, `invalid timeoutMs: expected a positive number, got <value>`, `invalid escalation: sandbox_permissions requires a justification`, `invalid escalation: justification is only valid together with sandbox_permissions`, `invalid justification: expected a non-empty sentence`, `background execution is disabled for this bash tool`, `background tasks unavailable: load @deepseek-ai/dsh-tasks and @deepseek-ai/dsh-tool-tasks`, `sandbox_permissions is not available in this composition (no sandboxing executor to escalate)`, `sandbox escalation to "<mode>" is not strictly wider than this call's current "<mode>" mode`, the approval-availability/rejection/cancellation variants, and `command aborted`.
+Validation and policy failures are normalized as `Error: <message>`. This package's stable messages are `invalid command: expected a non-empty string`, `invalid description: expected a non-empty string`, `invalid timeoutMs: expected a positive number, got <value>`, `invalid escalation: sandbox_permissions requires a justification`, `invalid escalation: justification is only valid together with sandbox_permissions`, `invalid justification: expected a non-empty sentence`, `background execution is disabled for this bash tool`, `background tasks unavailable: load @deepseek-ai/dsh-tasks and @deepseek-ai/dsh-tool-tasks`, `sandbox_permissions is not available in this composition (no sandboxing executor to escalate)`, `sandbox escalation to "<mode>" is not strictly wider than this call's current "<mode>" mode`, the approval-availability/rejection/cancellation variants, and `tool call aborted`.
 
 #### Token effect
 

+ 1 - 1
packages/bash/tool-bash/README.zh.md

@@ -141,7 +141,7 @@ renderer 先输出依数据而定的 stdout 尾部,再输出可选的 `[stderr
 
 #### 模型看到的内容
 
-验证和策略失败统一为 `Error: <message>`。此包的稳定消息包括 `invalid command: expected a non-empty string`、`invalid description: expected a non-empty string`、`invalid timeoutMs: expected a positive number, got <value>`、`invalid escalation: sandbox_permissions requires a justification`、`invalid escalation: justification is only valid together with sandbox_permissions`、`invalid justification: expected a non-empty sentence`、`background execution is disabled for this bash tool`、`background tasks unavailable: load @deepseek-ai/dsh-tasks and @deepseek-ai/dsh-tool-tasks`、`sandbox_permissions is not available in this composition (no sandboxing executor to escalate)`、`sandbox escalation to "<mode>" is not strictly wider than this call's current "<mode>" mode`、审批不可用/拒绝/取消变体,以及 `command aborted`。
+验证和策略失败统一为 `Error: <message>`。此包的稳定消息包括 `invalid command: expected a non-empty string`、`invalid description: expected a non-empty string`、`invalid timeoutMs: expected a positive number, got <value>`、`invalid escalation: sandbox_permissions requires a justification`、`invalid escalation: justification is only valid together with sandbox_permissions`、`invalid justification: expected a non-empty sentence`、`background execution is disabled for this bash tool`、`background tasks unavailable: load @deepseek-ai/dsh-tasks and @deepseek-ai/dsh-tool-tasks`、`sandbox_permissions is not available in this composition (no sandboxing executor to escalate)`、`sandbox escalation to "<mode>" is not strictly wider than this call's current "<mode>" mode`、审批不可用/拒绝/取消变体,以及 `tool call aborted`。
 
 #### Token 影响
 

+ 5 - 2
packages/bash/tool-bash/tests/tools.spec.ts

@@ -305,7 +305,7 @@ describe('bash tool', () => {
     expect(text(result)).toMatch(/ENOENT/)
   })
 
-  it('surfaces foreground aborts as isError', async () => {
+  it('surfaces foreground aborts as the structured TOOL_ABORTED error', async () => {
     const ctx = await setup()
     const controller = new AbortController()
     const pending = ctx.tools.execute({
@@ -317,7 +317,10 @@ describe('bash tool', () => {
     setTimeout(() => { controller.abort() }, 50)
     const result = await pending
     expect(result.isError).toBe(true)
-    expect(text(result)).toMatch(/aborted/)
+    expect(result.error).toMatchObject({
+      message: 'tool call aborted',
+      info: { name: 'AbortError', code: TOOL_ABORTED },
+    })
   })
 
   // Type and required-key violations are rejected by the harness

+ 2 - 2
packages/bash/tool-pwsh/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/bash/tool-pwsh/README.md
-README.md: b5acc73a68d3b309860554d4c1e8d979eb8d1eec
-README.zh.md: 4d678c42194b78da8b4b10e01f8b9e666d6236d8
+README.md: dfe26a63684d61dcdd6f969c2c2261dac79325c7
+README.zh.md: 2344f8477e5b15f2c4d366dd82b46358eacbc1b7

+ 1 - 1
packages/bash/tool-pwsh/README.md

@@ -36,7 +36,7 @@ When `run_in_background` is true, this plugin preflights `ctx.tasks.start()` bef
 
 ## UI presentation
 
-The tool owns its `presentCall`/`presentResult` render intent. A call is a `terminal` card carrying command, description, and optional cwd; a completed result is a `generic` card with the rendered output in a `console` fence. The bash tool's terminal card with its parsed exit-status pill has no pwsh counterpart yet — a PowerShell-aware presentation is roadmap work. These presenters are pure and replay-safe.
+The tool owns its `presentCall`/`presentResult` render intent. A foreground call is a `terminal` card carrying command, description, and optional cwd; a `run_in_background` call is a `generic` card with the raw command, mirroring the bash tool's background presentation. A completed result is a `generic` card with the rendered output in a `console` fence. The bash tool's terminal card with its parsed exit-status pill has no pwsh counterpart yet — a PowerShell-aware presentation is roadmap work. These presenters are pure and replay-safe.
 
 ## Model Experience
 

+ 1 - 1
packages/bash/tool-pwsh/README.zh.md

@@ -36,7 +36,7 @@
 
 ## UI presentation
 
-工具拥有自己的 `presentCall`/`presentResult` 呈现意图。调用是携带命令、描述与可选 cwd 的 `terminal` 卡;完成的结果是以 `console` 围栏包裹渲染输出的 `generic` 卡。bash 工具那种带解析退出状态 pill 的 terminal 卡在 pwsh 侧暂无对应——PowerShell 感知的呈现属于路线图工作。这些 presenter 是纯函数且可重放。
+工具拥有自己的 `presentCall`/`presentResult` 呈现意图。前台调用是携带命令、描述与可选 cwd 的 `terminal` 卡;`run_in_background` 调用是携带原始命令的 `generic` 卡,镜像 bash 工具的后台呈现。完成的结果是以 `console` 围栏包裹渲染输出的 `generic` 卡。bash 工具那种带解析退出状态 pill 的 terminal 卡在 pwsh 侧暂无对应——PowerShell 感知的呈现属于路线图工作。这些 presenter 是纯函数且可重放。
 
 ## Model Experience
 

+ 16 - 0
packages/bash/tool-pwsh/tests/tools.spec.ts

@@ -537,6 +537,22 @@ describe('UI presentation', () => {
       .toMatchObject({ cwd: 'C:\\work' })
   })
 
+  it('a background pending call renders the generic card like the bash tool', async () => {
+    const { ctx } = await setup()
+    const definition = ctx.tools.get('pwsh')
+    expect(definition?.presentCall?.({
+      command: 'Start-Sleep -Seconds 60',
+      description: 'long wait',
+      run_in_background: true,
+    })).toEqual({
+      card: 'generic',
+      title: 'Start-Sleep -Seconds 60',
+      kind: 'execute',
+      rawInput: 'Start-Sleep -Seconds 60',
+      content: [{ type: 'text', text: 'long wait' }],
+    })
+  })
+
   it('presentResult falls back to undefined for multi-block or non-text content', async () => {
     const { ctx } = await setup()
     const definition = ctx.tools.get('pwsh')