@deepseek-ai/dsh-tool-call-timeout-policy 0.1.1-rc.2 → 0.1.2-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/guard/timeout-policy/README.md
5
- README.md: 4b0ea4e541f0740289623f5e81d79e168240a402
6
- README.zh.md: 36bf8150e5b095c713cb71b76bd8dcc43857afc8
5
+ README.md: 6e6d403e19a35eecd13442c4da0f2fa5d44e0365
6
+ README.zh.md: c3941d14ae3144b383aba38ccbaa1aedd6594f4e
package/README.md CHANGED
@@ -1,47 +1,108 @@
1
- # dsh-tool-call-timeout-policy
1
+ ---
2
+ description: "Cooperative time limit for cancellation-aware tool calls, mapping a settled timeout to a clear model error for users and maintainers choosing or debugging the plugin."
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-tool-call-timeout-policy
2
7
 
3
8
  English | [中文](README.zh.md)
4
9
 
5
- Tool-call timeout enforcer: a single `tools/execute` around-dispatch listener that arms a per-call cooperative deadline on `exec.signal` for a tool declaring `timeoutMs` on its `ToolDefinition` and returns a structured `TOOL_TIMEOUT` result when that deadline wins. The budget is read from the tool's own declaration (`ToolDefinition.timeoutMs`, set by the owning tool plugin), so this plugin is **zero-config**. It is the reference `tools/execute` wrapper and the enforcement home for model-facing tool-call budgets ([timeout-library Agent Note](../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md)).
10
+ ## Summary
11
+
12
+ A tool call can hang for a long time — a slow web fetch, a search that never returns — and without a limit the model waits indefinitely, stalling the whole session. `dsh-tool-call-timeout-policy` arms a cooperative deadline for calls that declare a limit: it asks the tool to stop through `exec.signal`, then maps a settled cancellation to a clear `Error: tool call timed out after <ms>ms` result. A tool that ignores or slowly handles cancellation keeps the caller waiting until it settles; the plugin never hard-stops downstream work. The limit comes from each tool's own configuration, so the plugin itself is zero-config, and it ships enabled in the `dsh` base bundle.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ The common path is one line: add the plugin to the composition — the `dsh` base bundle already has it. Tools that have a limit configured are protected automatically; every other tool is untouched.
29
+
30
+ ### When to choose it
6
31
 
7
- ## Plugin (namespace: `timeout-policy`)
32
+ Choose it when the model calls tools that can take a long time, those tools honor `exec.signal`, and you want a predictable timed-out answer after cancellation settles. Avoid it when a tool must be hard-stopped at its limit — the plugin can only ask a tool to stop, so a tool that ignores cancellation keeps running and keeps the caller waiting — and when you want one default limit for every tool, because each tool's limit comes from that tool's own configuration.
8
33
 
9
- A function/namespace plugin (`name` / `inject` / `apply`), not a service. It registers no tool and takes no config — it consumes `ctx.tools`'s `tools/execute` waterfall (which the `dsh-tools` registry always provides) and reads each dispatched tool's declared `timeoutMs` from the registry (`ctx.tools.get(exec.name)`).
34
+ ### Setting it up
35
+
36
+ Mount the plugin with no configuration:
10
37
 
11
38
  ```yaml
12
- - id: timeout-policy
13
- name: '@deepseek-ai/dsh-tool-call-timeout-policy'
39
+ - name: '@deepseek-ai/dsh-tool-call-timeout-policy'
14
40
  ```
15
41
 
16
- The per-tool budget is declared by the tool plugin (e.g. `dsh-tool-web`'s `fetchTimeoutMs`/`searchTimeoutMs` config, attached as `ToolDefinition.timeoutMs`); this plugin only enforces it, so a mistyped tool name is not possible.
42
+ The limit is set where the tool is configured. For example, `dsh-tool-web`'s `fetchTimeoutMs`/`searchTimeoutMs` settings (default 30,000 ms) put the limit on `web_fetch` and `web_search`. Tools without a limit the shipped `bash`, `read`, `write`, and `edit` — are never cut off. The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-tool-web) lists the tool settings that produce limits.
43
+
44
+ ### What you get
45
+
46
+ When the deadline fires, the plugin aborts the derived `exec.signal`. After downstream code honors cancellation and `next()` settles, the model receives `Error: tool call timed out after <ms>ms` as an error result, so it can decide to retry, adjust, or give up. A tool that ignores or slowly handles the signal keeps the caller waiting and produces no timeout result until it settles; calls that finish in time are unchanged.
47
+
48
+ -----
49
+
50
+ <a id="understand-the-implementation"></a>
51
+ ## Understand the implementation
52
+
53
+ <details>
54
+ <summary>Implementation internals — click to expand</summary>
55
+
56
+ This section explains how the plugin arms a deadline around each dispatch and maps it to the `TOOL_TIMEOUT` result, and points at the code that realizes it; the observable behavior is fully covered in [Use this package](#use-this-package).
17
57
 
18
- ### Behavior
58
+ ### Design philosophy
19
59
 
20
- For a tool that **declares a `timeoutMs`** the listener:
60
+ The wrapper is built on four commitments:
21
61
 
22
- 1. Reads the budget from the tool's own declaration in the registry (`ctx.tools.get(exec.name)?.timeoutMs`) and arms `deadline(exec.signal, timeoutMs, 'TOOL_TIMEOUT')` one signal fusing the caller's abort with this plugin's timer (`@deepseek-ai/dsh-timeout`).
23
- 2. Swaps that derived signal onto `exec` for the downstream dispatch, then restores the caller's own signal afterward (cordis `next()` ignores passed arguments, so the wrapper mutates the shared `exec` in place; restoring keeps `tools/post-execute` seeing the caller's signal).
24
- 3. After dispatch, if `timeoutOf(d.signal, 'TOOL_TIMEOUT')` matches this plugin's own timer fired replaces the result with a structured `TOOL_TIMEOUT` tool result: `{ isError: true, error: { message, info: { name: 'ToolTimeoutError', code: 'TOOL_TIMEOUT' } }, content: 'Error: tool call timed out after <ms>ms' }`.
62
+ - **Enforcement home, not a library.** `dsh-timeout` owns timing and classification (`deadline`, `timeoutOf`); this plugin owns the per-call wiring over `tools/execute`; each capability owns termination. The split is recorded in the [timeout-deadline-library Agent Note](../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md).
63
+ - **The tool declares its own budget.** `timeoutMs` lives on the tool's `ToolDefinition`, read from the registry (`ctx.tools.get(exec.name, exec.agent)?.timeoutMs`), so a mistyped tool name is impossible and undeclared tools delegate untouched.
64
+ - **Scoped classification.** `TOOL_TIMEOUT` serves as both the internal `deadline` classification code and the structured error `code`; scoping `timeoutOf` to it keeps a nested outer deadline (another wrapper's timer that fired first) from being misread as this plugin's timeout it reads as an ordinary upstream cancel.
65
+ - **Signal swap, then restore.** Cordis `next()` ignores passed arguments, so the wrapper mutates the shared `exec` in place: it swaps the derived deadline signal onto `exec` for dispatch and restores the caller's signal in a `finally`, so `tools/post-execute` listeners never see this plugin's possibly-aborted signal.
25
66
 
26
- A tool that **declares no budget** delegates untouched (no deadline).
67
+ ### How a deadline is armed and mapped
27
68
 
28
- The base `next()` of `tools/execute` is the registry's dispatch-with-normalization thunk, so when the timeout signal reaches a provider that throws its own upstream-abort error, dispatch first turns it into a normal error result, and this wrapper then replaces that with `TOOL_TIMEOUT`. That ordering is why the replacement is keyed off the signal (`timeoutOf`), not off the dispatched result's shape.
69
+ One `tools/execute` listener reads the dispatched tool's declared limit from the registry (`ctx.tools.get(exec.name, exec.agent)?.timeoutMs`); a tool without a limit delegates untouched. For a limited tool, `deadline(exec.signal, timeoutMs, TOOL_TIMEOUT)` builds a fused signal that the wrapper swaps onto `exec` for dispatch and restores in a `finally`, so `tools/post-execute` listeners never see the derived signal. When the wrapper's own timer fired `timeoutOf(d.signal, 'TOOL_TIMEOUT')` scoped by the code, so a nested outer deadline reads as an ordinary upstream cancel — the dispatched result, already normalized into an error result by dispatch, is replaced with the structured result: `isError: true`, content `Error: tool call timed out after <ms>ms`, and error info `{ name: 'ToolTimeoutError', code: 'TOOL_TIMEOUT' }`.
29
70
 
30
- ### Cooperative, not a hard kill
71
+ ### Composing with other wrappers
31
72
 
32
- The derived signal only **notifies**; termination stays with the tool and the capability it forwards `exec.signal` to (the `dsh-timeout` library owns no kill). **Declaring `timeoutMs` therefore means "cooperative with `exec.signal`"**: a tool that ignores the signal will not stop on timeout. Only signal-forwarding tools should declare it — the shipped `web_fetch`/`web_search` (which forward through `ctx.web` to providers) are the reference. `TOOL_TIMEOUT` needs no session event for reconstructability: it is the final model-facing `tool/result`, already logged by the loop.
73
+ Multiple `tools/execute` listeners compose by Cordis registration order, which chooses the semantics: the timeout registered outer covers a whole retry operation, the timeout registered inner covers each attempt.
33
74
 
34
- ### Composing with other `tools/execute` wrappers
75
+ ### Source map
35
76
 
36
- Multiple `tools/execute` listeners compose by cordis registration order. Combined with a future retry/sandbox/metrics wrapper, registration order chooses the semantics — "timeout covers the whole retry operation" (timeout registered outer) versus "timeout covers each attempt" (timeout registered inner).
77
+ | File | Role |
78
+ |---|---|
79
+ | [`src/index.ts`](src/index.ts) | Plugin entry: `TOOL_TIMEOUT`, `name`/`inject`/`apply`, the `tools/execute` wrapper |
80
+ | [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant: the stateless wrapper owns no package-local event history) |
37
81
 
82
+ </details>
83
+
84
+ -----
85
+
86
+ <a id="further-exploration"></a>
87
+ ## Further Exploration
88
+
89
+ Read these pages when the package-level contract is not enough. They move from the tool-call pipeline to the timeout-library split, the enforced limits, and the guard group map.
90
+
91
+ - [Tools subsystem reference](../../../docs/subsystems/tools.md) — the `tools/execute` waterfall and decision shapes this wrapper hooks.
92
+ - [Timeout deadline library Agent Note](../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md) — the timing/termination split and why the deadline only notifies.
93
+ - [Generated configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-tool-web) — `dsh-tool-web`'s `fetchTimeoutMs`/`searchTimeoutMs` budgets the policy enforces.
94
+ - [guard group map](../README.md) — the sibling guard packages and the loop-hygiene family.
95
+
96
+ -----
97
+
98
+ <a id="model-experience"></a>
38
99
  ## Model Experience
39
100
 
40
101
  ### Conditional tool result
41
102
 
42
103
  #### What the model sees
43
104
 
44
- This plugin adds no prompt or schema. If a declared deadline wins, it replaces the provider's outcome with `Error: tool call timed out after <ms>ms` plus structured `TOOL_TIMEOUT`; otherwise the original result passes through unchanged.
105
+ This plugin adds no prompt or schema. If a declared deadline wins and downstream cancellation settles, it replaces the provider's outcome with `Error: tool call timed out after <ms>ms` plus the structured `TOOL_TIMEOUT` error; otherwise the original result passes through unchanged. A downstream call that never settles cannot produce a timeout result.
45
106
 
46
107
  #### Token effect
47
108
 
@@ -53,5 +114,22 @@ Append-only; newly visible content follows the reusable request prefix and does
53
114
 
54
115
  ## Known Limitations and Deferred Work
55
116
 
56
- - **Cooperative, never a hard kill** — the deadline only notifies via `exec.signal`; a tool that ignores the signal does not stop on timeout (see § Cooperative, not a hard kill).
57
- - **No blanket budget** — only tools that declare `timeoutMs` on their `ToolDefinition` get a deadline; there is no registry-wide default for undeclared tools (the shipped `bash`/`read`/`write`/`edit` deliberately declare none).
117
+ <a id="known-limitations-and-deferred-work"></a>
118
+
119
+
120
+ These limits define when the policy is a poor fit. They are current package constraints, not a task backlog.
121
+
122
+ - **Cooperative, never a hard kill** — the deadline only notifies via `exec.signal`; a tool that ignores the signal does not stop on timeout, the wrapper remains inside `await next()`, and the model receives no timeout result until downstream settles.
123
+ - **No blanket budget** — only tools that declare `timeoutMs` on their `ToolDefinition` get a deadline; undeclared tools (the shipped `bash`, `read`, `write`, and `edit` declare none) have no registry-wide default.
124
+
125
+ <a id="dev-note"></a>
126
+ ### Dev Note
127
+
128
+ <details>
129
+ <summary>Working context for maintainers — click to expand</summary>
130
+
131
+ This Dev Note is working context for maintainers: open questions and directions that are not decided. It is explicitly non-authoritative — shipped behavior, limits, and accepted rationale live in the sections above, the package code, and the linked Agent Notes.
132
+
133
+ The `src/index.ts` FIXME asks to settle a `@deepseek-ai/dsh-timeout-guard` rename; the [naming ledger](../../../.agents/notes/implemented/architecture/2026-08-11-repository-naming-contract-and-rename-ledger.md) already records `@deepseek-ai/dsh-tool-call-timeout-policy` as the decided name, so the FIXME is stale pending a code cleanup.
134
+
135
+ </details>
package/README.zh.md CHANGED
@@ -1,47 +1,108 @@
1
- # dsh-tool-call-timeout-policy
1
+ ---
2
+ description: "为配合取消的工具调用设置协作式时间上限,并在超时流程完成后映射为清晰的模型错误,供选择或排查此插件的用户与维护者阅读。"
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-tool-call-timeout-policy
2
7
 
3
8
  [English](README.md) | 中文
4
9
 
5
- 工具调用超时强制执行器:单个 `tools/execute` 环绕分发监听器,会在 `exec.signal` 上设置单次调用的协作式截止时间;适用于声明了 `timeoutMs` 且声明位于其 `ToolDefinition` 上的工具。该截止时间先到时,它返回结构化 `TOOL_TIMEOUT` 结果。预算从工具自身的声明中读取(`ToolDefinition.timeoutMs`,由拥有该工具的插件设置),因此此插件是**零配置**的。它是 `tools/execute` 包装层的参考实现,也是面向模型工具调用预算的强制执行归属地([超时库 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.zh.md))。
10
+ ## 概述
11
+
12
+ 工具调用可能会长时间挂起——缓慢的网页抓取、永不返回的搜索——没有上限时模型会无限期等待,拖住整个会话。`dsh-tool-call-timeout-policy` 为声明了限时的调用设置协作式截止时间:它通过 `exec.signal` 请求工具停止,再把已经完成的取消映射为清晰的 `Error: tool call timed out after <ms>ms` 结果。忽略或缓慢处理取消的工具会让调用方继续等待,直到自身完成;本插件绝不会硬性停止下游工作。限时来自每个工具自身的配置,因此插件本身零配置,并随 `dsh` base 组合默认启用。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 常用路径只有一行:把插件加入组合——`dsh` base 组合已经包含它。配置了限时的工具会被自动保护;其余工具完全不受影响。
29
+
30
+ ### 何时选择
6
31
 
7
- ## 插件(命名空间:`timeout-policy`)
32
+ 当模型会调用耗时很长的工具、这些工具会遵守 `exec.signal`,且你希望在取消完成后得到可预期的超时答复时,选择它。当工具必须在到达限时后被硬性停止时——插件只能请求工具停止,因此忽略取消的工具会继续运行并让调用方继续等待——以及当你希望为所有工具设置一个统一默认限时时(因为每个工具的限时来自该工具自身的配置),避免使用它。
8
33
 
9
- 它是函数/命名空间插件(`name`/`inject`/`apply`),而非服务。它不注册工具,也不接受配置;它消费 `ctx.tools` 的 `tools/execute` waterfall(瀑布式事件)(由 `dsh-tools` 注册表始终提供),并读取每个已分发工具声明的 `timeoutMs`;该声明来自注册表(`ctx.tools.get(exec.name)`)。
34
+ ### 设置
35
+
36
+ 无需任何配置即可挂载插件:
10
37
 
11
38
  ```yaml
12
- - id: timeout-policy
13
- name: '@deepseek-ai/dsh-tool-call-timeout-policy'
39
+ - name: '@deepseek-ai/dsh-tool-call-timeout-policy'
14
40
  ```
15
41
 
16
- 每工具预算由工具插件声明(例如 `dsh-tool-web` 的 `fetchTimeoutMs`/`searchTimeoutMs` 配置,会附加为 `ToolDefinition.timeoutMs`);此插件只负责强制执行,因此不可能拼错工具名。
42
+ 限时在配置工具的位置设置。例如,`dsh-tool-web` 的 `fetchTimeoutMs`/`searchTimeoutMs` 设置(默认 30,000 ms)把限时放到 `web_fetch` 与 `web_search` 上。没有限时的工具——随附的 `bash`、`read`、`write`、`edit`——绝不会被切断。生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-tool-web)列出会产生限时的工具设置。
43
+
44
+ ### 你会得到什么
45
+
46
+ 截止时间触发时,插件会中止派生的 `exec.signal`。下游代码遵守取消且 `next()` 完成后,模型会收到标记为错误的 `Error: tool call timed out after <ms>ms` 工具结果,从而决定重试、调整或放弃。忽略或缓慢处理该信号的工具会让调用方继续等待,并且在自身完成前不会产生超时结果;按时完成的调用保持不变。
47
+
48
+ -----
49
+
50
+ <a id="understand-the-implementation"></a>
51
+ ## 理解实现
52
+
53
+ <details>
54
+ <summary>实现细节——点击展开</summary>
55
+
56
+ 本节解释插件如何在每次分发周围设置截止时间并将其映射为 `TOOL_TIMEOUT` 结果,并指出实现它的代码位置;可观察行为已在[使用本包](#use-this-package)中完整说明。
17
57
 
18
- ### 行为
58
+ ### 设计理念
19
59
 
20
- 对 **声明了 `timeoutMs` 的工具**,监听器会:
60
+ 包装层建立在四项承诺之上:
21
61
 
22
- 1. 从注册表中的工具自身声明(`ctx.tools.get(exec.name)?.timeoutMs`)读取预算,并设置 `deadline(exec.signal, timeoutMs, 'TOOL_TIMEOUT')`:一个将调用方中止与此插件计时器融合的信号(`@deepseek-ai/dsh-timeout`)。
23
- 2. 将该派生信号替换到 `exec` 上用于下游分发,然后恢复调用方自身的信号(Cordis `next()` 忽略传入的参数,因此包装层会原地修改共享 `exec`;恢复可使 `tools/post-execute` 看到调用方的信号)。
24
- 3. 分发后,如果 `timeoutOf(d.signal, 'TOOL_TIMEOUT')` 检测到此插件自身的计时器已触发,则将结果替换为结构化 `TOOL_TIMEOUT` 工具结果:`{ isError: true, error: { message, info: { name: 'ToolTimeoutError', code: 'TOOL_TIMEOUT' } }, content: 'Error: tool call timed out after <ms>ms' }`。
62
+ - **强制执行归属,而非库。** `dsh-timeout` 负责时序与分类(`deadline`、`timeoutOf`);本插件负责 `tools/execute` 上的单次调用接线;各能力负责终止。该拆分记录在[超时截止时间库 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.zh.md) 中。
63
+ - **工具声明自己的预算。** `timeoutMs` 位于工具的 `ToolDefinition` 上,从注册表读取(`ctx.tools.get(exec.name, exec.agent)?.timeoutMs`),因此不可能拼错工具名,未声明工具原样委派。
64
+ - **作用域分类。** `TOOL_TIMEOUT` 同时用作内部 `deadline` 分类码与结构化错误 `code`;把 `timeoutOf` 限定到它,可避免嵌套的外层截止时间(先触发的另一包装层计时器)被误读为本插件的超时——它读作普通的上游取消。
65
+ - **先交换信号,再恢复。** Cordis `next()` 忽略传入参数,因此包装层原地修改共享 `exec`:分发时把派生的截止时间信号换到 `exec` 上,并在 `finally` 中恢复调用方信号,使 `tools/post-execute` 监听器永远看不到本插件可能已中止的信号。
25
66
 
26
- **未声明预算的工具** 会原样委托(不启动截止时间)。
67
+ ### 截止时间如何设置与映射
27
68
 
28
- 基础 `next()` 是注册表为 `tools/execute` 提供的、带规范化处理的分发 thunk,因此当超时信号到达抛出自身上游中止错误的提供方时,分发会先将其转换为普通错误结果,再由此包装层替换为 `TOOL_TIMEOUT`。这一顺序就是替换依据信号(`timeoutOf`)而非已分发结果形状的原因。
69
+ 一个 `tools/execute` 监听器从注册表读取已分发工具声明的限时(`ctx.tools.get(exec.name, exec.agent)?.timeoutMs`);没有限时的工具原样委派。对有限时的工具,`deadline(exec.signal, timeoutMs, TOOL_TIMEOUT)` 构建融合信号,包装层在分发时把它换到 `exec` 上并在 `finally` 中恢复,使 `tools/post-execute` 监听器永远看不到派生信号。当包装层自己的计时器触发时——`timeoutOf(d.signal, 'TOOL_TIMEOUT')` 以代码限定作用域,因此嵌套的外层截止时间读作普通的上游取消——已被分发规范化为错误结果的分发结果会被替换为结构化结果:`isError: true`、内容 `Error: tool call timed out after <ms>ms`、错误信息 `{ name: 'ToolTimeoutError', code: 'TOOL_TIMEOUT' }`。
29
70
 
30
- ### 协作式,而非硬终止
71
+ ### 与其他包装层组合
31
72
 
32
- 派生信号只会**通知**;是否终止仍取决于工具及其将 `exec.signal` 转发到的能力(`dsh-timeout` 库本身不负责硬终止)。**因此,声明 `timeoutMs` 意味着「与 `exec.signal` 协作」**:忽略该信号的工具不会在超时时停止。只有转发信号的工具才应声明该字段;已交付的 `web_fetch`/`web_search`(通过 `ctx.web` 转发给提供方)是参考实现。`TOOL_TIMEOUT` 无需会话事件以满足可重建性:它是最终面向模型的 `tool/result`,已由循环记录。
73
+ 多个 `tools/execute` 监听器按 Cordis 注册顺序组合,注册顺序决定语义:超时注册在外层时覆盖整个重试操作,注册在内层时覆盖每次尝试。
33
74
 
34
- ### 与其他 `tools/execute` 包装层组合
75
+ ### 源码地图
35
76
 
36
- 多个 `tools/execute` 监听器按 Cordis 注册顺序组合。与未来的重试/沙箱/指标包装层一起使用时,注册顺序决定语义:「超时覆盖整个重试操作」(超时注册在外层),或「超时覆盖每次尝试」(超时注册在内层)。
77
+ | 文件 | 职责 |
78
+ |---|---|
79
+ | [`src/index.ts`](src/index.ts) | 插件入口:`TOOL_TIMEOUT`、`name`/`inject`/`apply`、`tools/execute` 包装层 |
80
+ | [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式:无状态包装层不拥有包级事件历史) |
37
81
 
82
+ </details>
83
+
84
+ -----
85
+
86
+ <a id="further-exploration"></a>
87
+ ## 进一步探索
88
+
89
+ 当包级约定不够用时阅读以下页面。它们从工具调用流水线逐步进入超时库拆分、被执行的限时与 guard 组映射。
90
+
91
+ - [工具子系统参考](../../../docs/subsystems/tools.zh.md)——本包装层挂钩的 `tools/execute` waterfall 与决策形态。
92
+ - [超时截止时间库 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.zh.md)——时序/终止拆分以及截止时间为何只通知。
93
+ - [生成配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-tool-web)——策略所执行的 `dsh-tool-web` 的 `fetchTimeoutMs`/`searchTimeoutMs` 预算。
94
+ - [guard 组映射](../README.zh.md)——同组的 guard 包与循环卫生家族。
95
+
96
+ -----
97
+
98
+ <a id="model-experience"></a>
38
99
  ## 模型体验
39
100
 
40
101
  ### 条件工具结果
41
102
 
42
- #### 模型看到的内容
103
+ #### 模型看到什么
43
104
 
44
- 此插件不添加提示词或 schema。如果已声明的截止时间先到,它会将提供方结果替换为 `Error: tool call timed out after <ms>ms` 与结构化 `TOOL_TIMEOUT`;否则原结果保持不变。
105
+ 此插件不添加提示词或 schema。如果已声明的截止时间先到且下游取消完成,它会用 `Error: tool call timed out after <ms>ms` 与结构化 `TOOL_TIMEOUT` 错误替换提供方结果;否则原结果保持不变。永不完成的下游调用无法产生超时结果。
45
106
 
46
107
  #### Token 影响
47
108
 
@@ -51,7 +112,24 @@
51
112
 
52
113
  仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
53
114
 
54
- ## 已知限制与暂缓事项
115
+ ## 已知限制与延期工作
116
+
117
+ <a id="known-limitations-and-deferred-work"></a>
118
+
119
+
120
+ 这些限制说明策略何时不合适。它们是当前包约束,不是任务积压。
121
+
122
+ - **协作式,绝不是硬终止**——截止时间只通过 `exec.signal` 通知;忽略该信号的工具不会在超时时停止,包装层仍停留在 `await next()` 内,模型要等下游完成后才可能收到超时结果。
123
+ - **没有统一预算**——只有声明 `timeoutMs` 并将其放在 `ToolDefinition` 上的工具才会获得截止时间;未声明工具(随附的 `bash`、`read`、`write`、`edit` 有意不声明)没有注册表级默认值。
124
+
125
+ <a id="dev-note"></a>
126
+ ### 开发备注
127
+
128
+ <details>
129
+ <summary>维护者的工作上下文——点击展开</summary>
130
+
131
+ 本开发备注是维护者的工作上下文:开放问题与尚未决定的探索方向。它明确不具权威性——已交付的行为、限制与既定理由以上文、包代码和相关 Agent Note 为准。
132
+
133
+ `src/index.ts` 中的 FIXME 要求确定 `@deepseek-ai/dsh-timeout-guard` 改名;[改名台账](../../../.agents/notes/implemented/architecture/2026-08-11-repository-naming-contract-and-rename-ledger.zh.md) 已把 `@deepseek-ai/dsh-tool-call-timeout-policy` 记录为既定名称,因此该 FIXME 已陈旧,待代码清理。
55
134
 
56
- - **协作式,绝不是硬终止**:截止时间只通过 `exec.signal` 通知;忽略该信号的工具不会在超时时停止(参见「协作式,而非硬终止」一节)。
57
- - **没有统一预算**:只有声明 `timeoutMs` 并将其放在 `ToolDefinition` 上的工具才会获得截止时间;未声明工具没有注册表级默认值(已交付的 `bash`/`read`/`write`/`edit` 有意不声明)。
135
+ </details>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-tool-call-timeout-policy",
3
3
  "description": "Tool-call timeout policy: a tools/execute wrapper that arms a per-tool deadline on exec.signal and returns TOOL_TIMEOUT when it wins",
4
- "version": "0.1.1-rc.2",
4
+ "version": "0.1.2-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -32,17 +32,17 @@
32
32
  ],
33
33
  "license": "MIT",
34
34
  "peerDependencies": {
35
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
36
- "@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
37
- "@deepseek-ai/dsh-timeout": "^0.1.1-rc.2",
38
- "@deepseek-ai/dsh-tools": "^0.1.1-rc.2",
39
- "@deepseek-ai/cordis": "^4.0.1"
35
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
36
+ "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.2",
37
+ "@deepseek-ai/dsh-tools": "^0.1.2-alpha.2",
38
+ "@deepseek-ai/cordis": "^4.0.2",
39
+ "@deepseek-ai/dsh-llm": "^0.1.2-alpha.2"
40
40
  },
41
41
  "devDependencies": {
42
- "@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
43
- "@deepseek-ai/dsh-timeout": "^0.1.1-rc.2",
44
- "@deepseek-ai/dsh-tools": "^0.1.1-rc.2",
45
- "@deepseek-ai/cordis": "^4.0.1",
46
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2"
42
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
43
+ "@deepseek-ai/dsh-llm": "^0.1.2-alpha.2",
44
+ "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.2",
45
+ "@deepseek-ai/dsh-tools": "^0.1.2-alpha.2",
46
+ "@deepseek-ai/cordis": "^4.0.2"
47
47
  }
48
48
  }