feat(spill): bound the durable copy of Code Mode sub-dispatch results

New tools/code-dispatch-log waterfall (run via registry.shapeDispatchLog,
contained — a throwing listener falls back to the unshaped content) lets
listeners reshape the tool/code-dispatch event's content before the
bridge appends it. dsh-spill-policy registers a second arm sharing the
model-facing arm's exact replacement pipeline (same maxInlineBytes cap,
preview + locator, within-cap invariant, best-effort fallbacks), with
artifacts labeled dispatch under the sub-call id. The program's value is
untouched; read sub-calls ARE bounded (a log copy is not model context,
and read produces the biggest logs). Resolves the tools README's
uncapped-dispatch-log Known Limitation.
This commit is contained in:
Tianyi Cui
2026-07-26 09:01:03 +08:00
parent 366419862f
commit 4987261d55
16 changed files with 415 additions and 75 deletions

View File

@@ -331,19 +331,29 @@ export function createRunCodeTool(registry: ToolRegistry, requireRuntime: () =>
for (const context of result.additionalContexts ?? []) {
exec.deferContext(context)
}
exec.agent?.session.append('tool/code-dispatch', {
parentCallId: exec.callId,
subCallId,
name,
// The SIBLING parse of the dispatched value: byte-identical JSON,
// but a separate object — a tool mutating its args cannot desync
// this record from what it actually received.
arguments: normalized.logged,
isError: result.isError,
// The registry deep-froze this projection at result finalization;
// append snapshots it again, so the log copy stays detached.
content: result.content,
})
if (exec.agent !== undefined) {
// The durable copy may be reshaped (e.g. spilled to a preview +
// locator) by the log-shaping waterfall; the program's value and
// the model contract are untouched.
const logged = await registry.shapeDispatchLog({
exec, agent: exec.agent, subCallId, name, isError: result.isError,
// The registry deep-froze this projection at result
// finalization; append snapshots the final copy again, so the
// log stays detached.
content: result.content,
})
exec.agent.session.append('tool/code-dispatch', {
parentCallId: exec.callId,
subCallId,
name,
// The SIBLING parse of the dispatched value: byte-identical JSON,
// but a separate object — a tool mutating its args cannot desync
// this record from what it actually received.
arguments: normalized.logged,
isError: result.isError,
content: logged,
})
}
resolve(result.isError
? { isError: true, message: result.error.message }
: { isError: false, value: result.value })