Replies: 9 comments
|
@luisnomad 这个把「流式 runaway 参数」的链路断得非常清楚——而且你点的 一、
|
|
@luisnomad 闭环一下——这个缺口已经有一个插件形态的实现兜底了,你提的「插件应该先兜」我直接就做了。已在 npm 发布 它做什么:包一层
为什么它能真正防止执行(不只是记日志):我把 边界诚实说明:「不要执行这个超大调用」是由 core 的 error-finish 路径提供的,本插件负责检测 + 切断。如果以后 harness 需要把这转换成一条结构化 assistant 消息(而不是抛/重试 用法: - insert:
- id: llm-tool-call-guard
name: '@argszero/cordis-plugin-llm-tool-call-guard'默认 12/12 测试通过(含「per-block-index 独立计数」「越界切断 + error finish」「observe-only 发 stop finish」「文本/reasoning delta 不计入预算」)。如果作者觉得默认预算或行为需要调整,直接在仓库开 issue 或 @我。 |
|
Thank you—this confirms the seam and ownership split very clearly. Yes, I would be happy to test the plugin against the same local model and failure mode. I can also provide a redacted recorded-stream excerpt containing the runaway The plugin-first split makes sense to me: stop and close the upstream stream at |
|
@luisnomad 太好了——作者愿意实测 + 提供录制流是最理想的一步。关于 fixture 格式,给出一个最省事的选项: 插件测试消费的 chunk 协议
你那份录制流怎么给最有价值最理想是只给触发越界的那个序段,去掉 carrier/连接层:
交付形态建议可以给两种,任选其一即可:
拿到之后我会把它做成一个固定的回归用例,加进 |
|
@luisnomad @everyone 小更新—— 新增:
|
|
I pulled the real failing stream from the parent session log and redacted the payload. It has a slightly different (and useful) shape from the expected >24 KiB case:
So the current 24 KiB default does not catch our actual failure. This is a fragment-count runaway, rather than a large-argument runaway. A 4,096-byte setting catches it only at fragment 3,583; a 1,024-fragment cap would catch it at 1,173 bytes. Here is a content-redacted, boundary-exact fixture. The gzip/base64 string encodes the ordered byte length of every recorded delta ( import { gunzipSync } from 'node:zlib'
const encodedLengths =
'H4sIAAAAAAAAE+3TsQ0AIAgF0Zkg7j+btdECo8WFHM1rr/jEiMw4Xa7yIDSIuGeRHUJDGUCCyDuEhqZfR2i4gNDQHEJD11r5ywRCIhgn6g8AAA=='
const deltaLengths = [
...gunzipSync(Buffer.from(encodedLengths, 'base64')).toString('ascii'),
].map(Number)
export const recordedRunaway = [
...deltaLengths.map((length) => ({
type: 'tool-call-delta' as const,
index: 0,
id: 'call-redacted',
name: 'job_output',
argumentsDelta: 'x'.repeat(length),
})),
{ type: 'finish' as const, reason: { kind: 'max-tokens' as const } },
]
console.assert(deltaLengths.length === 4_074)
console.assert(deltaLengths.reduce((a, b) => a + b, 0) === 4_658)This suggests a complementary One small independent observation while checking the fixture: the current implementation adds No project text, paths, call IDs, or job IDs are included above; only the recorded protocol shape and sizes. |
|
@luisnomad @everyone 闭环——你那份录制流 fixture 直接促成了 v0.1.2。三个改动: 1. 修正 UTF-8 字节计数(你发现的独立 bug)你点得非常准:v0.1.1 用 2. 新增
|
|
@luisnomad 更正一条我上一条公告里的实质错误 —— 你按我说的装 v0.1.2 是装不上的。请直接上 v0.1.3。 我错在哪上一条我写「peer range 根因:semver 里
而 npm 上 v0.1.3 的正确写法一条 comparator 对应一条元组线: 实测对比(同法、不加
功能代码( 对你那份 fixture 的结论没变三个字段协议( 同一个坑我把同族插件也一并修了这不是单个包的手误 —— 我复核了自己已发布的其余插件,
如果你在自己的环境里也写 抱歉让你多绕一步 —— 稿子发出去时这个 range 我自己没跑过 |
|
Thank you for the careful follow-through, especially for checking the prerelease range across both active Harness release lines and correcting it so transparently. We will test v0.1.3 against the recorded failure case. We are also working on a few Harness plugins of our own. The available plugin seams—and exchanges like this—make the project genuinely exciting to build on. We appreciate the work and are happy to support the ecosystem as it develops! |
Uh oh!
There was an error while loading. Please reload this page.
Summary
A local OpenAI-compatible model produced a runaway
job_outputtool argument after a continuable subagent started. The Harness allowed the malformed argument to stream until the model exhausted its entire output allowance, then surfaced only the genericOutput token limit reachedmessage.This is ultimately a model-generation error, but the Harness can contain it much earlier and report the actual failure.
Environment
@deepseek-ai/dsh:0.1.1-rc.2subagentand generic job toolsThe model and endpoint remained healthy throughout the incident.
Observed sequence
subagentsuccessfully.started subagent a72944e6-6129-468c-8ab6-6a717af96a11.job_outputcall. (That is already the wrong control for a continuable subagent id; its settlement notice should be used instead.){"job_id":"a72944e6-6129-468c-8ab6-6a717af96a717af96a717af96a..."}stopReason: "length", and amax-tokensturn end. The assembled assistant message contained no usable content.The durable stream made the cause visible: a
tool-call-deltasequence forjob_outputaccumulated the runaway argument untilblock-end. The Web UI only showedOutput token limit reached, which made this initially look like an ordinary response-length problem.Expected behavior
The Harness should stop consuming a response once a tool-call argument exceeds a defensible configured bound or becomes impossible for the target tool, preserve a diagnostic attempt, and report a specific malformed/oversized-tool-call failure. It must not execute the malformed call.
Increasing
maxTokensis not a fix; it only makes this failure slower and more expensive.Suggested ownership and behavior
A stream guard on the existing
llm/streamextension point seems preferable to changing the agent loop. Possible design:tool-call-deltachunks independently for each call index.TOOL_CALL_ARGUMENTS_TOO_LARGE.For the specific subagent confusion, the model-facing result or guidance could additionally state that
started subagent <id>is a continuable child id and must not be passed tojob_output;job_outputis only forstarted background ... job <id>acknowledgements. That clarification is useful, but it does not replace the generic streaming guard.Local mitigation
I switched council delegation to one-shot/foreground-returning subagents. Independent calls issued in one assistant response still run concurrently, while their results return directly and require no id polling. This avoids the trigger for this local model, but I am not proposing that continuable subagents be disabled upstream.
I can provide the redacted session-event excerpt or test a proposed guard against the same local model if useful.
All reactions