refactor(tools): restore py-types to the ts-types trusted-after-validation stance
Rounds 6-9 of the bot review kept finding adjacent hostile-getter variants (post-validation cycles, TOCTOU on const/enum/oneOf, self-referential functions) because the renderer had grown per-shape runtime defenses the sibling ts-types renderer does not have. Those inputs are unreachable: the schema is a first-party defineTool object literal that already passed assertSupportedJsonSchema, and per AGENTS.md "Trust TypeScript at typed same-process seams" a typed same-process seam does not add hostile-input handling for values the static interface forbids. renderType now validates the whole tree once and trusts it, wrapping the walk in one try/catch that degrades to Any — byte-for-byte the stance of the ts-types sibling. This removes the cycle-tracking (activeSchemas/hasIdentity), the const/enum/oneOf read snapshots, the isPyScalar re-check, the typing rollback, and the pyScalar null->None re-read handling; the corresponding hostile-getter tests are removed. Behavior fixes that hold for legitimate input are kept: RESERVED soft-keyword exclusion, closed-empty-object TypedDict, class-name cap + per-base collision counter, BigInt digits for beyond-safe integers. py-types.ts stays at 100% per-file coverage. The language-dispatch Agent Note documents the stance and its symmetry with ts-types so the boundary is not re-litigated.
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-07-31-code-mode-language-dispatch.md
|
||||
2026-07-31-code-mode-language-dispatch.md: 23794226c8e236421a79fb2143ccb09095f1a287
|
||||
2026-07-31-code-mode-language-dispatch.zh.md: d2f868215181a99814c19ca4817582e96b396807
|
||||
2026-07-31-code-mode-language-dispatch.md: 9f001b8fad8ca954d9b0c3cdca0e7be4d3b9ce61
|
||||
2026-07-31-code-mode-language-dispatch.zh.md: 525bb8a97e4e6d9e00334d5425acd1491a9b3fc7
|
||||
|
||||
@@ -25,6 +25,8 @@ Both tables are read with `Object.hasOwn` before use so a language named `toStri
|
||||
|
||||
`py-types.ts` renders the same unified tool-schema vocabulary `jsonSchemaToTs` covers, targeting Python: `jsonSchemaToPy` emits a type expression per JSON-schema node, and `renderToolsSdkPy` assembles named `TypedDict`s for each visible tool's arguments and canonical output plus a `tools` object with usage instructions equivalent to the TypeScript flavor. Unsupported raw constructs degrade rather than throwing during assembly, matching the TypeScript renderer's contract. The output is deterministic — lexicographic tool order, byte-identical text for an unchanged tool set — so the prompt stays prefix-cache-friendly.
|
||||
|
||||
`renderType` validates the whole schema once (`assertSupportedJsonSchema`) and then trusts it, wrapping the walk in one `try/catch` that degrades to `Any` — the same trusted-after-validation stance the sibling `ts-types` renderer takes at this typed same-process seam ([Trust TypeScript at typed same-process seams](../../../../AGENTS.md)). It deliberately carries NO defenses against a schema whose accessors mutate between reads (post-validation cycles, TOCTOU on `const`/`enum`, self-referential functions): the input is a first-party `defineTool` object literal that already passed validation, so such inputs are unreachable, and adding per-shape guards here would break symmetry with `ts-types` (which has none) for values the static interface forbids. `jsonSchemaToPy(schema: unknown)` accepts `unknown` and returns `Any` on a malformed schema — the Python counterpart of the TS flavor's `unknown` — but its contract is "degrade an unsupported schema", not "survive an adversarial mutating one".
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **A `language` config field on `ToolRegistry`.** Deployment would then have two places to name the language (the loaded runtime and the tools config) that can disagree; the loaded runtime is the single source of truth, so the registry reads it rather than duplicating it.
|
||||
|
||||
@@ -25,6 +25,8 @@ Code Mode 只生成一种 SDK 形态:TypeScript。`ToolRegistry` 为 `tools:sd
|
||||
|
||||
`py-types.ts` 渲染 `jsonSchemaToTs` 所覆盖的同一套统一工具 schema 词汇,目标为 Python:`jsonSchemaToPy` 为每个 JSON-schema 节点发出一个类型表达式,`renderToolsSdkPy` 为每个可见工具的参数与规范输出装配具名 `TypedDict`,再加一个带用法说明的 `tools` 对象,与 TypeScript 形态等价。不支持的原始构造在装配时降级而非抛错,与 TypeScript 渲染器的契约一致。输出是确定性的——工具按字典序排列,工具集不变时文本逐字节相同——因此 prompt 保持 prefix-cache 友好。
|
||||
|
||||
`renderType` 先用 `assertSupportedJsonSchema` 整树校验一次、随后信任它,用单个 `try/catch` 把整个遍历兜住并降级为 `Any`——与姊妹渲染器 `ts-types` 在这个 typed 同进程 seam 上采取的"校验后信任"姿态一致([Trust TypeScript at typed same-process seams](../../../../AGENTS.md))。它有意不设任何针对"访问器在多次读取间变值"的防御(校验后成环、`const`/`enum` 的 TOCTOU、自引用函数):输入是已通过校验的第一方 `defineTool` 对象字面量,这类输入不可达,而在此加逐形态守卫会为静态接口所禁止的值破坏与 `ts-types`(没有这类守卫)的对称。`jsonSchemaToPy(schema: unknown)` 接受 `unknown` 并对畸形 schema 返回 `Any`——TypeScript 形态 `unknown` 的对应物——但它的契约是"降级不支持的 schema",而非"扛住对抗性的可变 schema"。
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **在 `ToolRegistry` 上加一个 `language` 配置字段。** 那样部署方就会有两处命名语言(所加载的运行时与 tools 配置)且可能相互矛盾;所加载的运行时是唯一真相来源,故注册表读取它而不复制它。
|
||||
|
||||
Reference in New Issue
Block a user