前言¶
在 DeepSeek Harness(dsh)里跑长任务时,常见一种现象:界面上的 turn 一直显示 running,日志和输出里却没有任何报错。模型侧有流式 idle 超时(DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300_000,由 idleWatchdog 按 chunk 重载),因此「长时间无输出」往往指向工具调用卡住,而不是模型卡住。社区讨论 #7339 描述的就是这类 long-horizon 任务静默挂起。
根因之一在于工具定义:ToolDefinition.timeoutMs 是可选字段,说明为「Cooperative tool-call timeout budget in milliseconds. Omit for no deadline」。核心策略包 @deepseek-ai/dsh-tool-call-timeout-policy 在 timeoutMs === undefined 时直接 return next(),不会挂定时器、不会发信号、也不会写诊断。在 dsh v0.1.6-alpha.2 中,调用 defineTool( 的 40 个非测试源文件里,有 26 个完全没有 timeoutMs,其中包括 tool-subagent、tool-terminal、mcp-resources 以及若干 fs 与工作流相关工具——恰恰是委托子进程、跨网络 MCP 等容易长时间阻塞的路径。
下面介绍社区 Cordis 插件 @argszero/cordis-plugin-tool-deadline-guard(GitHub:argszero/cordis-plugin-tool-deadline-guard),它在 tools/execute 与 tools/pre-execute 瀑布上为「未自行声明 deadline」的调用补上可配置的部署级预算,超时后把静默挂起变成模型可见的结构化错误,便于重试或改策略。
这是什么¶
@argszero/cordis-plugin-tool-deadline-guard 由维护者 argszero 发布,分类属于 DSH 生态中的工作流向插件(SkillHub 目录:argszero/cordis-plugin-tool-deadline-guard)。它不替换工具自身实现的 timeoutMs,而是在 harness 未武装任何 deadline 时为工具执行体与派发前阶段分别计时;到期则返回插件自有错误码,与核心策略的 TOOL_TIMEOUT 区分开,便于区分「工具声明了预算却超时」与「部署策略补上的预算超时」。
核心功能¶
执行体阶段(tools/execute)¶
插件挂在与核心 timeout policy 相同的公开 seam tools/execute 上。对每个 dispatch:
- 通过
ctx.tools.get(exec.name, exec.agent)解析工具定义,决定是否武装 deadline;未知工具仍由 registry 处理(UNKNOWN_TOOL)。 - 武装一个 timer 与一个
AbortController,经exec.signal向工具提供AbortSignal.any([caller, own]),与核心 policy 的做法一致。 - 在
finally中恢复调用方 signal,避免后续 wrapper 看到本 mount 可能已 abort 的 signal。 - 仅当本 mount 的 timer 先触发时才替换结果;在预算内正常结束则原样返回工具结果,不二次 dispatch。
- 超时结果形态为
isError: true,且error.info.code === 'TOOL_DEADLINE_EXCEEDED'(非核心的TOOL_TIMEOUT),模型侧可据此行动。README 中的示例如:对subagent的调用在超过600000ms且工具未声明timeoutMs时,返回明确说明 deadline 来源的错误文本。
默认情况下,凡未在工具定义里写 timeoutMs 的调用,会被 bound 在 10 分钟(600000 ms);已声明 timeoutMs 的工具在默认 applyTo: undeclared 下仍交给核心 policy,本插件不改动。
派发前阶段(tools/pre-execute,0.2.0)¶
timeoutMs 只约束工具体;tools/pre-execute 瀑布在核心 tools/execute wrapper 之前完成。若某 listener 在此阶段 await 无界工作,整个 session 的工具调用都会堵在后面,而只挂在 tools/execute 上的 deadline structurally 看不到该阶段。README 以 @deepseek-ai/dsh-workspace-changes 在 pre-execute 中等待 turn recorder 快照队列为例,并指向讨论 #9515。
0.2.0 起,同一插件也挂载 tools/pre-execute。该阶段超时使用不同错误码 TOOL_PRE_EXECUTE_DEADLINE_EXCEEDED,形态为 deny(调用未被 dispatch),与执行体超时区分:便于判断是「跑太久」还是「根本没跑起来」。
配置项¶
无配置时:对所有未声明 timeoutMs 的工具使用 10 分钟默认预算,其余调用原样放行。
| field | default | meaning |
|---|---|---|
mode |
auto |
auto 武装 deadline;observe 只报告将会 bound 的内容并原样 dispatch;off 不注册 |
defaultTimeoutMs |
600000 |
未声明 timeout 的工具的预算(10 分钟) |
perTool |
{} |
按解析后的工具名覆盖预算,优先于 defaultTimeoutMs |
exempt |
[] |
永不 bound 的工具名 |
applyTo |
undeclared |
undeclared 不碰已声明 timeoutMs 的工具;all 对所有已知工具 bound |
onExpiry |
cooperative |
到期行为;README 指向 Honest limits 小节 |
preExecute |
true |
是否 bound 派发前阶段 |
preExecuteTimeoutMs |
defaultTimeoutMs |
派发前阶段预算 |
warnLimit |
5 |
每个 mount 发出的诊断条数;0 关闭 |
预算须为正有限数且 ≤ 2147483647(setTimeout 饱和上限);更大值会立即触发,因此「无 deadline」应通过 exempt 或 mode: off 表达,而非设极大数值。
安装与启用¶
先在项目中安装 npm 包(版本以 registry 为准;package.json 中当前为 0.2.0):
npm install @argszero/cordis-plugin-tool-deadline-guard
在 cordis.yml 或 profile bundle 中增加一行(也可合并包内自带的 cordis.patch.yml,内容等价):
- insert:
- id: tool-deadline-guard
name: '@argszero/cordis-plugin-tool-deadline-guard'
安装后无需额外配置即可生效:未声明 timeoutMs 的工具默认 10 分钟上限。需要更长或更短等待时,在 Cordis 插件配置里调整 defaultTimeoutMs 或 perTool;对必须允许长时间阻塞的工具(例如特定 MCP),将其名加入 exempt。
上线前可用 mode: observe 观察本 mount 将会 bound 哪些调用,确认名单后再切回 auto。
典型用法示例¶
仅收紧 subagent 与 terminal,其余仍用默认 10 分钟:
在插件实例上配置(字段名以 README 为准):
defaultTimeoutMs: 600000
perTool:
subagent: 900000
terminal: 300000
exempt:
- ask-user
关闭派发前阶段 bound(只 guard 执行体):
preExecute: false
对某第三方 MCP 工具完全豁免:
exempt:
- mcp_some_server_long_poll
超时后模型会看到类似 README 中的 tool_result [error],其中说明 exceeded 的毫秒数与 deadline 来自 tool-deadline-guard mount,而不是工具自带的 timeoutMs。
适用场景与注意¶
适合谁: 在生产或长时间自动化任务中使用 dsh,且依赖 subagent、终端、MCP、工作流类工具;希望 turn 不会因单个 wedged 调用无限 running,又需要与核心 TOOL_TIMEOUT 区分运维语义(部署策略 vs 工具自声明预算)的团队。
协作式默认: onExpiry 默认为 cooperative;README 强调插件不会强行抛弃工具本身不愿结束的调用,并在可选 replace 模式下明确说明限制。需要更激进行为时应阅读 README 的 Honest limits。
权限与安全: 插件随当前 dsh 进程权限运行,挂载在 harness 公开 waterfall 上,不单独降权。安装前请阅读 GitHub 仓库 源码与 MIT 许可证,确认与您的合规要求一致。peer 依赖范围在 package 说明中审计为 dsh 0.1.2-rc.1 至 0.2.1-alpha.2 的 npm/pnpm prerelease 读法;与您实际 dsh 版本不匹配时应在测试环境验证。
生态说明: DSH 采用「一切皆插件」的 Cordis 组合方式;SkillHub 等社区目录独立于 DeepSeek / 幻方,并非官方应用商店。本插件星标数为 0(截至目录抓取),选用时以 README 与 issue 跟踪维护状态即可。
结尾¶
tool-deadline-guard 解决的是 harness 对「未声明 timeoutMs」工具默认不设 deadline 的空白:把静默 wedged 的工具调用转成 TOOL_DEADLINE_EXCEEDED 或 TOOL_PRE_EXECUTE_DEADLINE_EXCEEDED,让模型和运维都能看见原因。目录页:https://www.skillhub.cn/plugins/argszero/cordis-plugin-tool-deadline-guard;源码与完整配置说明:https://github.com/argszero/cordis-plugin-tool-deadline-guard。