Repository navigation
Replies: 1 comment
A plugin now covers this:
|
| Where | What |
|---|---|
dsh-ptc-runtime-node |
setTimeout(… controller.abort('execution deadline reached'), spec.timeoutMs); the run then reports { kind: 'timeout', message: 'execution deadline reached (<n>ms)' } |
dsh-tools (ptc.ts) |
that becomes CodeRunFailedError → CODE_RUN_FAILED, message code run failed (timeout): execution deadline reached (120000ms) |
dsh-user-questions |
the nested ask settles as ASK_ABORTED — abortedQuestion(): "ask_user_question was aborted before the user answered" |
The model only ever reads the second. The first and third are what say why, and the ask tool's own contract sentence — "A timeout is never approval" — is exactly what the surviving text fails to convey. Your control measured this cleanly: the same question asked as a direct tool call is not on any program clock, so it waited over four minutes for a real answer.
What the plugin does
It wraps the public tools/execute waterfall — the one seam that also sees a program's nested sub-dispatches, because they go through the same registry pipeline — and keeps a ledger keyed by the call tree's rootCallId:
- a settled dispatch of
ask_user_questionthat failed withASK_ABORTEDis recorded against its tree; - a settled
run_codethat failed withCODE_RUN_FAILEDand whose message matchesexecution deadline reachedtakes that record and callsexec.deferContext(…)with one user-role advisory: the question is ABORTED, not answered; a deadline is never approval, and is nobody's decision; do not perform the action it was gating; ask it again as a directask_user_questioncall outside the program, where no deadline is running; if the work cannot proceed, stop and hand it to the user.
Nothing else changes — no request is re-sent, no sandbox is widened, no session event is rewritten, and the transcript of the timed-out run stays exactly as the harness recorded it.
npm install @argszero/cordis-plugin-ptc-ask-timeout-advisor- insert:
- id: ptc-ask-timeout-advisor
name: '@argszero/cordis-plugin-ptc-ask-timeout-advisor'
config:
mode: advisory # or: warn (log only), off (register nothing)What it deliberately refuses
Every gate narrows, because a false alarm here tells a model to re-ask a question the user already answered:
- a program that failed with its own exception (
code run failed (exception): …) — its own diagnosis; - a different
timeoutof the runtime's own, worded differently (e.g.compute budget exhausted) — only the run deadline is this plugin's business; - a deadline with no aborted ask behind it;
- an ask the user answered inside the budget; and one that ended
ASK_TIMED_OUT(a different outcome, whose answer channel stays open); - a non-program call that fails with the same code and wording (
bash, a composite tool) — the program-name gate; - a second program in a tree that already spent its record — the record is taken, never read.
Evidence
test/seam.spec.mjs mounts the real dsh-tools registry, the real run_code transport and the real CodeRunFailedError, and drives a program backend that calls the very bindings ptc.ts built — so the plugin observes genuine scheduler.prepare → dispatch → finalize traffic and proves the reported sequence end to end, including that the nested settlement is visible before the program settles. Each refusal above has an arm of its own. 43 tests; npm run test:inject mutates the source and requires every mutation to be caught by a named arm (26 CAUGHT, 1 named EQUIVALENT, 0 SILENT, 0 skipped); npm run test:probe-lines installs one line per || segment of the peer range and runs the whole suite there (0.1.7-rc.2 and 0.2.0-rc.2, 43/43 on each).
Honest boundary
This is a stopgap and it settles nothing: it does not stop the deadline, restore the program, or answer the question. It removes one specific lie — that "a program failed" is the whole story. The preferred fix remains upstream, and there are two clean shapes for it: do not count a human wait against a program's execution budget, or surface the pending state in the program's own result, so the model learns it from the transport instead of from a plugin. If the runtime ever rewords execution deadline reached, this plugin stops matching and goes silent — the safe direction — rather than guessing that some other failure was a budget cut.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
PTC 执行期限把人工等待当成程序执行时间:未作答的 ask_user_question 会被 120s 截止切断,模型继续自行执行
提交渠道:GitHub Discussion → Q&A(该仓库
has_issues: false;现有分类为 Announcements / General / Ideas / Polls / Q&A / Show Your Plugins,没有 Bug Reports 分类)已提交:#8368
产品:DeepSeek Harness Desktop 0.2.0-rc.2(Windows x64,打包安装版)
组件:
@deepseek-ai/dsh-ptc-runtime-node(与@deepseek-ai/dsh-tools的 PTC 模式、@deepseek-ai/dsh-user-questions、@deepseek-ai/dsh-tool-ask-user交互)严重度:高(在用户未作答的情况下让 Agent 继续执行,等于把"超时"当成了许可)
摘要
PTC 模式下模型只能调用
run_code,它在程序里通过绑定调用tools.ask_user_question(...)。该运行时的执行期限(默认timeoutMs: 120000)会把这次人工等待计入程序执行时间:用户没在 120 秒内作答时,期限到期会UserQuestionError: ASK_ABORTED),run_code整体判为失败(code run failed (timeout): execution deadline reached (120000ms)),而模型只看到一个
run_code失败结果,看不到"问题仍然可回答、这是待答而非跳过"的信号,于是在未获得任何回答的情况下继续往下执行。环境
0.2.0-rc.2(Electron 打包版,resources/app.asar)ptc(PTC 模式)dsh-ptc-runtime-node:timeoutMs: 120000,maxTimeoutMs: 600000ask_user_question模式复现
在 PTC 预设的会话里,让模型把提问写进程序,例如:
然后什么都不点,等待 120 秒。
实际行为(会话日志原文)
来自本机会话日志(
~/.dsh/sessions/<project>/<session>/session.v4.jsonl.zstd,zstd 多帧),已隐去会话 id:同一模式在另一会话复现(seq 367-371):同样是
ASK_ABORTED+execution deadline reached (120000ms)。对照:同一台机器上
ask_user_question作为顶层工具直接调用时(legacy 阻塞模式,schema 无timeout参数)等待时间没有上限——实测有 4.6 分钟后正常拿到回答的用例,不会自动继续。问题只出现在 PTC 嵌套调用里。期望行为
人或审批的等待不应计入"程序执行时间",或至少不应让模型在未作答时继续执行:
run_code的执行期限遇到需要人工作答的绑定调用时暂停计时(或把这类绑定排除在期限之外)。人是不可压缩的延迟,"超时"在这里没有任何安全含义。{pending: true, callId}(问题仍可回答、不得视为许可)与真正的程序超时失败。当前run_code只返回CODE_RUN_FAILED,模型只能猜。packages/.../ptc-runtime-node/README.md的 Known Limitations 写明 "Execution is one-shot — no yield/wait API",Dev Note 指向architecture/2026-09-11-sandboxed-node-ptc-runtime.md#deferred-timeout-design,其中明确列出 "approval wait accounting" 为待定项。本 issue 提供该待定项的真实伤害证据。为什么危险
dsh-tool-ask-userREADME 明确写 "A timeout is never approval";但 PTC 路径下模型甚至拿不到"超时"这一事实,只拿到一个程序失败。临时规避(用户侧)
cordis.patch.yml里给ptc-runtime行加配置,把预算提到上限:tool-ask-user行设mode: timed并让模型对"没有答案就不能继续"的提问传timeout: -1(askTimed走ask(),不设 deadline)。这需要模型每次都判断正确。run_code(改用 standard 预设)。复现所需材料
安装版
app.asar无法用 shell 读取,以上全部证据取自本机会话日志(JSONL 中的tool/call、tool/ptc-dispatch、tool/result事件),字段与代码符号可直接在0.2.0-rc.2的构建产物里检索:@deepseek-ai/dsh-ptc-runtime-node/lib/index.js:controller.abort("execution deadline reached")、`execution deadline reached (${spec.timeoutMs}ms)`、timeoutMs: z.number().default(12e4)、maxTimeoutMs: z.number().default(6e5)@deepseek-ai/dsh-user-questions/lib/index.js:askTimed、ASK_TIMED_OUT→{ pending: true, callId }@deepseek-ai/dsh-tool-ask-user/lib/index.js:pendingNotice("No answer batch arrived before the timeout. This is pending, not a skipped answer. Continue useful independent work. … Do not treat this as permission.")All reactions