fix(tools): name the two bound SDK names and escape NEL

The static-stub sentence over-generalized: `tools` and `ToolCallError`
ARE bound at run time, and a model reading "everything below is a stub"
could stop catching `ToolCallError`. State the boundary and pin both
halves in the fixed-instruction assertions.

UNPRINTABLE missed U+0085: it is Cc but not ECMAScript whitespace, so
it survived the collapse and reached the docstring raw and invisible.
Add it and scope the docstring to Cc, since the `\xNN` escape cannot
address the Cf formatting characters that pass through by design.

Record the backend PR's two runtime contracts -- inject only `tools`
and `ToolCallError`, and bind the assembly-time language to the
request -- in the Agent Note and at requireCodeRuntime.
This commit is contained in:
Chinesezjc
2026-08-05 17:45:38 +08:00
parent bc94431c34
commit 1b4cb031f0
6 changed files with 46 additions and 6 deletions

View File

@@ -106,6 +106,12 @@ describe('renderToolsSdkPy', () => {
expect(text).toContain('# tools["class"](args: dict[str, Any]) -> str')
// Fixed instruction lines the model relies on.
expect(text).toContain('top-level `await`')
// The binding boundary: `tools`/`ToolCallError` are bound, the TypedDicts
// are not. Both halves are pinned — dropping either one turns a correct
// contract into a wrong one (a model that reads only "STATIC STUB" would
// stop catching `ToolCallError`).
expect(text).toContain('exactly two of the names declared below are bound: `tools` and `ToolCallError`')
expect(text).toContain('never `FooArgs(field=1)`, which raises `NameError`')
expect(text).toContain('ToolCallError')
expect(text).toContain('class ToolCallError(Exception):')
expect(text).toContain('MAY overlap under `asyncio.gather`')
@@ -732,5 +738,17 @@ describe('renderToolsSdkPy', () => {
const others = renderToolsSdkPy([make('bell\u0007esc\u001bdel\u007f')])
expect(others).toContain(String.raw`bell\x07esc\x1bdel\x7f`)
expect(renderToolsSdkPy([make('tab\tnewline\ncr\r')])).toContain('"""tab newline cr"""')
// NEL is the one `Cc` code point the collapse does NOT fold: ECMAScript
// whitespace is TAB/VT/FF/SP/NBSP/ZWNBSP/Zs plus LF/CR/LS/PS, and U+0085 is
// in none of them, so without the escape it would reach the docstring raw
// and be invisible there. NBSP, which IS whitespace, folds instead.
const nel = renderToolsSdkPy([make('a\u0085b')])
expect(nel).not.toContain('\u0085')
expect(nel).toContain(String.raw`# a\x85b`)
expect(renderToolsSdkPy([make('nb\u00a0sp')])).toContain('"""nb sp"""')
// `Cf` formatting characters pass through by design: `\xNN` cannot address
// them, and they terminate neither a Python string literal nor a `#`
// comment, so the block stays parseable with the code point intact.
expect(renderToolsSdkPy([make('zero\u200bwidth')])).toContain('"""zero\u200bwidth"""')
})
})