refactor(runtime): name the tool-call data boundary and share the call-field recipe - #4937
refactor(runtime): name the tool-call data boundary and share the call-field recipe#4937Adarsh-Me wants to merge 2 commits into
Conversation
…l-field recipe executeTool() spelled out the argument-projection policy inline: raw snapshot, permission projection on a private clone, the Computer Use persisted/model views, and two handwritten common-field recipes for the tool_start event and the persisted tool_call message — all inside a method that also owns admission, dispatch identity, T1/T2 and publication (apache#4908, slice of the A06 direction in apache#4726). The call-data rules move to tool-call-snapshot.ts: the recursive snapshot helpers, schema validation, one named buildToolCallArgs() operation that produces the four argument views with the existing guards (direct-only rejection skips validation and projection entirely; an unavailable sandbox-boundary surface defers validation; projection failures come back as permissionArgsError for the refusal path instead of throwing), and one buildToolCallCommonFields() recipe invoked once per record so the event and the message each receive privately owned clones of args and providerOptions. The consolidated recipes were verified to fail the argument-ownership isolation test when a single shared args object leaked across the two records — the exact break the duplication used to make impossible. No public API, event, message or durable format changes. The four argument-view consumers (managed mutation admission, durable preparation, result projection, telemetry/artifacts) read the same named views; their algorithms are untouched.
…message shape The shared common-field recipe emitted toolUseId as its own key. The tool_start event carries that field, but the persisted tool_call message names the same value `id` and its stored-message schema rejects unknown keys — appendMessages threw 'Invalid stored message schema', the Computer Use history guard generalized it to 'Operation failed', and the remote TUI turn failed before its fixture tool could run (tui-mcp-remote-integration). The recipe no longer emits the call id: the input still carries it, and each record names it at its own site — `toolUseId` on the event, `id` on the message. Reproduced locally through the failing integration test (failed on the refactored build, passed on the pre-refactor module); passes again after the fix along with the four argument-ownership, settlement, model-loop and privacy suites (21/21).
|
The CI failure on The shared common-field recipe emitted The fix keeps the shared recipe but stops it from emitting the call id: the recipe's input still takes Re-verified locally: the previously failing |
hqhq1025
left a comment
There was a problem hiding this comment.
复核 c84067161d553509646afbc885571fd0bdf97ce4,未发现有充分证据支持的 P0–P3 问题。技术检查通过;是否接受这次重构的代码量与边界取舍,仍需维护者决定。
实际改动是把参数快照、声明式校验和权限/模型参数投影提取到 tool-call-snapshot.ts,并把事件与存储消息的公共字段构造从两处合并为一处。原始参数仍在首次等待前冻结并完成步骤准入登记(tool-runtime.ts:1123);直接调用限制、普通步骤拒绝和不可用沙箱界面的校验例外保持在原有控制路径(tool-runtime.ts:1133、tool-call-snapshot.ts:212)。Computer Use 的模型投影仍调用原来的转换函数(tool-call-snapshot.ts:230)。
公共字段构造器每次独立克隆 args 和 providerOptions(tool-call-snapshot.ts:291),事件和消息分别调用它(tool-runtime.ts:1235、:1256)。事件独有的 toolUseId 没有混入持久化消息,后者继续使用 id,符合 packages/core/src/session.ts:1191、:1489 的严格解码形状。数据库 schema 未改变,不涉及 migration。
实测规模为 tool-runtime.ts 4192→4078 行,executeTool 1096→1049 行,新文件 310 行,两文件合计净增 196 行。可验证的收益是消除一份重复字段构造,并提取已有数据边界;这不是净删代码,也没有证明性能收益。
验证通过:干净安装与 build:test、Runtime typecheck、两文件 lint/format、diff 检查,以及 123 项相关回归。回归包含真实 SQLite 提交/读取、持久化拒绝路径、参数校验、沙箱边界和 Computer Use 模型/隐私路径。另外通过生产 settleToolCall 入口,用可控 Promise 暂停验证了校验期间调用者修改原参数、执行期间修改投影,以及消息/事件互改参数和提供方元数据后的隔离。
边界说明:displayName: '' 现在会被保留,原代码会省略;持久化解码允许空串,已检查的正常生产工具生成器不会产生该值,主 UI 仍回退到工具名,因此未将其定为可达产品缺陷。此次没有运行原生 Computer Use 桌面操作或全仓测试;不能把上述结果扩展为任意外部自定义工具的全面兼容证明。对当前 main 492ff80f0f4abf3a917ce8f2c41b32fa9e3fc60c 的 Git 合并树检查无冲突,但未在合并树上运行测试。提交审查前该 head 的 hosted test 为 SUCCESS;没有可沿用的既有 review。
Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.
Summary
Implements #4908 — the call-data slice of the A06 direction in #4726. Behavior-preserving extraction; no public API, event, message, or durable-format changes.
executeTool()spelled out the argument-projection policy inline: raw snapshot, permission projection on a private clone, the Computer Use persisted/model views, and two handwritten common-field recipes for thetool_startevent and the persistedtool_callmessage — all inside a method that also owns admission, dispatch identity, T1/T2 and publication.The call-data rules now live in
packages/runtime/src/tool-call-snapshot.ts:snapshotToolArgs/snapshotJsonValue— the recursive, frozen, cycle-rejecting snapshot, moved verbatim.validateDeclaredToolArgs— schema validation, moved verbatim.buildToolCallArgs()— one named operation producing the four argument views (executionArgs,permissionArgs,persistedArgs,modelFacingArgs) with the existing guards: direct-only rejection skips validation and projection entirely; an unavailable sandbox-boundary surface defers validation while every other rule still applies; validation/projection failures are returned aspermissionArgsErrorfor the existing refusal path instead of thrown. The Computer Use history-regression rationale ([A06] Extract an immutable tool-call snapshot and centralize argument projections #4908's window_id/windowId record) moved into this function's documentation.buildToolCallCommonFields()— the one shared recipe for the fields both call records assemble. It is invoked once per record: the event and the message each receive privately owned clones ofargsandproviderOptions, preserving the isolation the duplicated recipes guaranteed.The 43 downstream uses of the views (loop gates, managed mutation admission, durable preparation, result projection, sandbox-denial keys, telemetry/artifacts) read the same named values as before; their algorithms are untouched. Synchronous-before-first-await registration is preserved: only the projection point moved into the helper, and admission ordering around it is unchanged.
Measurements (per #4908's required table)
buildToolCallCommonFields), invoked once per recordbuildToolCallArgs); no duplicated projectionThe net addition is documentation and named types: the moved rules kept their explanatory comments, which the issue required preserving, and the module adds the interface declarations that make the four views explicit. No behavior changed, so no new tests were required; the existing isolation suite covers the boundary.
Verification
tool-runtime-argument-ownership(1/1),tool-runtime-settlement(12/12),computer-use-model-loop(3/3),computer-use-privacy-boundary(5/5) — 21/21 pass. All test files unmodified.argsacross event and storage, andtool-runtime-argument-ownershipfailed immediately. The per-record invocation fixes it; that failure mode is exactly what the duplicated recipes used to prevent, and the shared recipe now documents it.@maka/runtimebuilds clean (tsc -p tsconfig.json+ filesystem worker, 0 errors);biome format/lintclean on both files.AI use
Select exactly one:
Tool(s) and scope:
Checklist
Does this PR entail a change in behavior?