Commit Graph
100 Commits
Author SHA1 Message Date
Chinesezjc 2d5256fd91 Merge pull request #1079 from deepseek-harness/feat/code-runtime-multilang-seam
feat(code-runtime): own portable-identifier exclusions at the seam
2026-08-07 13:27:50 +08:00
Chinesezjc 19e21dff52 Merge remote-tracking branch 'origin/master' into feat/code-runtime-multilang-seam
# Conflicts:
#	docs/core-data-structures/code-runtime.i18n.yaml
#	packages/code-runtime/code-runtime/README.i18n.yaml
2026-08-07 13:20:34 +08:00
Chinesezjc a9e040a126 Merge pull request #1080 from deepseek-harness/feat/py-types-code-mode
feat(tools): render a Python SDK and dispatch Code Mode by runtime language
2026-08-07 13:14:41 +08:00
Chinesezjc 596eb686ec Merge pull request #649 from deepseek-harness/feat/todo-multi-in-progress
feat(todo): allow several in_progress todos at once
2026-08-07 11:34:00 +08:00
Chinesezjc 0567727410 Merge remote-tracking branch 'origin/master' into feat/todo-multi-in-progress
# Conflicts:
#	apps/cli/config/base.cordis.yml
#	packages/client/ui-conversation/README.i18n.yaml
2026-08-07 11:27:24 +08:00
Chinesezjc 20651af6a6 Merge branch 'master' into feat/py-types-code-mode 2026-08-07 11:23:41 +08:00
Chinesezjc e15e3db2b1 docs(code-runtime-worker): correct __debug__ attribution in binding-global comment
The inline comment called every dunder entry "the Python backend's" slot,
but `__debug__` is a CPython compile-time constant, not a seeded slot;
align the comment with the seam declaration, which is the single home for
why each entry is reserved.
2026-08-07 11:20:05 +08:00
Chinesezjc efdc78cba3 docs(code-runtime): fix remaining dunder/backend wording and document exports in README
- index.ts: RESERVED_ERROR_MEMBERS JSDoc now says `__x__` (non-empty
  middle), matching DUNDER_MEMBER and the derivative docs; and
  PORTABLE_RESERVED_WORDS is described as covering portable *target*
  languages (Python is a later-PR backend, not yet shipped).
- types.ts + type-equiv docs: CodeBindingNamespace.global points to
  RESERVED_BINDING_GLOBALS by name with an `e.g.` sample rather than
  enumerating the set (single home), no longer implying `__debug__` is a
  seeded slot.
- Agent Note: separate `__debug__` (CPython compile-time constant) from
  the seeded bootstrap globals.
- README (en/zh): document the four exported exclusion sets and the
  portable identifier/naming contract — no cross-package link needed.
- Regenerate cordis services catalog for the shifted source line.
2026-08-07 11:20:05 +08:00
Chinesezjc c05db227d5 docs(code-runtime): sync Agent Note and public JSDoc with the shipped seam
- Agent Note: correct the stale worker-adoption paragraph — the worker
  consumes the seam constants directly by name (no RESERVED_WORDS /
  RESERVED_ERROR_PROPERTIES re-alias) — and describe DUNDER_MEMBER as
  `__x__` (non-empty middle).
- types.ts: document RESERVED_BINDING_GLOBALS on CodeBindingNamespace.global
  (names like `__dsh_main__` pass the identifier rule but are still refused)
  and the non-empty-middle dunder rule on memberNameProperty; propagate to
  the type-equiv derivative docs (both languages, re-recorded).
2026-08-07 11:20:05 +08:00
Chinesezjc 4dc2b197d7 test(code-runtime): correct DUNDER_MEMBER edge cases for the __.+__ pattern
`____` has an empty middle between the two `__` pairs and does not match
`/^__.+__$/`; assert that (not a match) and add `__x__` as the shortest
real dunder form that does.
2026-08-07 11:20:05 +08:00
Chinesezjc 0e6fe760c1 docs(code-runtime): regenerate cordis services catalog for shifted source line 2026-08-07 11:20:05 +08:00
Chinesezjc eb03aa86fe refactor(code-runtime): address seam review — drop worker aliases, tighten dunder
- Worker consumes PORTABLE_RESERVED_WORDS / RESERVED_ERROR_MEMBERS by
  their seam names directly, dropping the local re-alias (symmetry with
  the other two imported constants).
- Split the reserved-vs-duplicate diagnostics: a backend-owned global now
  reports "reserved binding global", not the misleading "duplicate".
- DUNDER_MEMBER uses `__.+__` so a bare `__` (empty middle, not a real
  CPython dunder) is not matched; add coverage.
- Worker misuse tests add `a$b` (second-char `$`) and `lambda` (Python
  keyword) so the identifier narrowing and reserved-word adoption are
  each pinned directly, not only transitively.
- Clarify the seam JSDoc (dunder-vs-explicit-set wording, Python backend
  is a later stack PR) and record in the Agent Note the obligation to
  widen RESERVED_BINDING_GLOBALS when the bootstrap seeds more globals.
2026-08-07 11:20:05 +08:00
Chinesezjc b5578c026e docs(code-runtime): align derived docs with the portable-identifier JSDoc
Update the type-equiv blocks in docs/core-data-structures/code-runtime
(both languages, re-recorded) to mirror the new CodeBindingNamespace /
CodeBindingErrorClass JSDoc, and regenerate the cordis services catalog
for the shifted source line. Keeps the worker-only intro until the
Python backend package exists.
2026-08-07 11:20:05 +08:00
Chinesezjc 5d4cea9dc1 feat(code-runtime): own portable-identifier exclusions at the seam
Move the reserved-word, reserved-global, reserved-error-member, and
dunder exclusion sets from the worker backend up to the code-runtime
seam package, and narrow the portable identifier subset to drop the
JS-only `$`. Every backend now imports one contract so a binding
namespace list valid on one backend is valid on all.

Delivers only the seam extension and the worker's adoption; the Python
backend, py-types renderer, and Code Mode language dispatch are later
PRs in the stack that depend on these exports.
2026-08-07 11:20:05 +08:00
Chinesezjc 90a1c87276 docs(notes): drop the TUI clauses the package removal made stale
ed30088adb deleted packages/ui/tui, so the two present-tense clauses naming
it in notes this branch already rewrites no longer describe HEAD. The
operative claim — the surfaces beyond the web row needed no change — stands
without naming the package. Pair hashes re-recorded.

Also anchor tool-row-styles.spec.ts's rule lookup at a rule boundary, so a
compound selector landing above a base rule fails loud instead of reading
the wrong declaration block.
2026-08-06 14:12:46 +08:00
Chinesezjc 9b01da195a fix(ui-conversation): keep the summary suffix on one line
`flex: none` stops the `+n` box shrinking but not its text wrapping, so a
row too narrow for title + separator + suffix broke the one-line summary in
the exact case the slot exists for. Add `white-space: nowrap` and pin the
declaration as CSS text, since jsdom has no layout.

Also align the README's row illustration with the English rendering (the
same sentence's plan-strip clause already used it), and record the two
deferred review findings in the Agent Note so they survive merge.
2026-08-06 13:56:39 +08:00
Chinesezjc 3915412070 docs(web): say which active item activeContent names
The field JSDoc read as if it searched the active items for a usable one,
which is the skip-forward behavior planSummary deliberately does not do.
2026-08-06 13:43:12 +08:00
Chinesezjc 60153324ce fix(web): align the plan summary's usable-content rule with the tool
planSummary treated whitespace-only content as nameable, so a rejected call
whose args survive verbatim rendered a blank active clause beside a live +N.
The tool's own rule is trimmed non-empty; the row now uses it.

Also hoists the duplicated DSH_SNAPSHOT refresh flag out of the two assembled
snapshot files into their shared assembled-boot scaffolding.
2026-08-06 13:31:40 +08:00
Chinesezjc 5663c9f507 test(web): refresh two stale markdown aria goldens
ea2fc29530 gave the assistant footer separators flanking spaces, so the
accessible text reads `{{clock}} Ran for {{duration}}`. The CJK-strong and
inline-code-link goldens were recorded on a base that predates it and merged
without a re-record; master's push runs skip the snapshot lane, so nothing
caught the drift until a branch merged both. Every other golden already
carries the space.
2026-08-06 13:28:40 +08:00
Chinesezjc b6a6e820f6 Merge remote-tracking branch 'origin/master' into feat/todo-multi-in-progress 2026-08-06 13:18:20 +08:00
Chinesezjc 2e930291f9 docs(note): correct todo dock entry order and projection hook
The web-todo-display note still described the dock entry as registering at
order: -1 and reading snapshot.todos through useSession. TodoPanel.tsx
registers at order: 0 and TodoDock reads the host-computed todos projection
through useProjection.
2026-08-06 13:16:52 +08:00
Chinesezjc ecb17ac3ae Merge remote-tracking branch 'origin/master' into feat/todo-multi-in-progress
# Conflicts:
#	examples/acp-agent/tests/snapshots/todo-write/session.jsonl
#	packages/client/ui-conversation/README.i18n.yaml
2026-08-06 13:04:33 +08:00
Chinesezjc bb18a13fb3 docs(todo): drop stale plan-strip format claims and fix the note's counts
The plan strip header has not emitted `<done>/<total> tasks · <n> in progress`
since #740 replaced it with `·`-joined per-status counts. Four sites still
quoted the old string: plan-summary.ts's module JSDoc, ui-conversation's
README pair, and the web-todo-display note pair.

The parallel-in-progress note's regression example mixed a four-item plan with
an eight-item row reading, and built-boot.snapshot.ts still called itself the
only test loading the built bundles.
2026-08-06 13:00:14 +08:00
Chinesezjc 230752aaa2 test(web): keep the assembled boot entry list module-private
knip rejects it as an unused export: both snapshot files reach the graph
through mountAssembledApp, never through the entry list itself.
2026-08-06 12:48:55 +08:00
Chinesezjc 0cf7420e52 test(web): pin the parallel todo plan in the assembled application
The `+N` active count rides ToolRow's non-shrinking `summarySuffix` slot,
but only jsdom package suites covered it: the ACP snapshots render no web
surface and the built-boot smoke asserts no todo row. Add
`apps/web/tests/todo-row.snapshot.ts`, which boots the built client bundles
against the keyless fixture transport and records `summary`, `suffix`, and
the plan strip's header as separate fields, so folding the count back into
the summary string changes the expected output.

The three assembled-jsdom files now share `apps/web/tests/assembled-boot.ts`
instead of each carrying its own copy of the boot entry list, bundle map,
jsdom stubs, and mount call.

Also: name the policy branch in each `allowParallelInProgress` test title so
no case asserting `true` sits under a `false` describe, reword the stale cap
comment in todo-panel.spec.tsx, and record the plan strip's real header
format in the Agent Note (per-status counts, not `<done>/<total> tasks`).
2026-08-06 12:26:27 +08:00
Chinesezjc 2b9354aab5 docs(todo): state the catalog's policy branch and drop the sidecar census
The tool catalog boots every tool with its default config, but
allowParallelInProgress has no default, so the generator has to choose;
the page now says which branch it shows. The Agent Note's sidecar count
went stale twice inside this PR, so it records the refresh rule instead
of a point-in-time census. The SDK builtin entry pins its config literal
with satisfies like every sibling.
2026-08-06 12:17:10 +08:00
Chinesezjc 8985284f85 Merge remote-tracking branch 'origin/master' into feat/todo-multi-in-progress
# Conflicts:
#	docs/core-data-structures/session.i18n.yaml
#	packages/client/ui-conversation/README.i18n.yaml
2026-08-06 12:00:33 +08:00
Chinesezjc a01d974c60 Merge remote-tracking branch 'origin/master' into feat/todo-multi-in-progress
# Conflicts:
#	.agents/notes/implemented/feature/2026-07-23-web-todo-display.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-23-web-todo-display.md
#	.agents/notes/implemented/feature/2026-07-23-web-todo-display.zh.md
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/client/ui-conversation/README.md
#	packages/client/ui-conversation/README.zh.md
2026-08-06 11:58:06 +08:00
Chinesezjc 3888f367b1 test(todo): refresh snapshot sidecars that landed after this branch
The subagent-* scenarios pin their child tool schemas through their own
tool-schemas.1.expected.json, and the headless fixtures embed the request
header verbatim; all seven still carried the single-in_progress description.
2026-08-06 11:55:49 +08:00
Chinesezjc baed704fc2 test(todo): follow master's Agent shape and load-time config failure 2026-08-06 11:46:22 +08:00
Chinesezjc c337215bae feat(todo): carry the parallel-active count through ToolRow's summarySuffix
Rebuild the todo row's parallel summary on the shared ToolRow that master
introduced: planSummary still returns the active name and the remaining
count separately, and the row hands the count to a new non-shrinking
summarySuffix slot so a narrow row clips the summary text before the count.
An error row drops the suffix, whose collapsed summary is the failure line.

Re-record the ACP todo-write transcript for the parallel prompt, regenerate
the config catalog for the required allowParallelInProgress field, and
re-record the bilingual pairing hashes.
2026-08-06 11:42:31 +08:00
Chinesezjc a1a320cf56 Merge remote-tracking branch 'origin/master' into feat/todo-multi-in-progress 2026-08-06 11:39:09 +08:00
Chinesezjc e0a7f40f04 Merge remote-tracking branch 'origin/master' into feat/todo-multi-in-progress 2026-08-06 11:25:28 +08:00
Chinesezjc 0de132b927 test(tools): drop the quantifier that miscounted its own enumeration
"Two emitted positions" was followed by an enumeration counting
occurrences — the field twice, the class statement once — so the two
halves of the sentence disagreed. The sentence now states what needs the
tables without a count.
2026-08-06 02:33:25 +08:00
Chinesezjc 631d3f930e test(tools): escape U+200C in the new case and name both carriers of the 15.1 requirement
The file's convention is a \uXXXX escape for a character with no visible
width (\u0301, \u1100, \u1161, \ud800 are all written that way) and a
literal only for a visible one; the new case wrote nine raw ZWNJ.

The comment also named only the field as needing 15.1 tables. Two emitted
code positions do: the bare field, once in each class, and the
Tool\u200CbArgs class name. The subscript comment is not one.
2026-08-06 02:22:51 +08:00
Chinesezjc 4f8ba6c190 test(tools): pin the U+200C tail/head split; qualify the identifier-equivalence measurement
The docstring names ZWNJ/ZWJ as a skew instance but nothing checked the
predicate's tail-position accept branch; this is its first test, and it
also covers camelCase's Tool-prefix branch for a head that is
XID_Continue but not XID_Start.

The equivalence sentence pinned its evidence to Node 22.23.1 against
CPython 3.9.6 without saying the samples sit inside those two versions'
shared tables, next to five named characters where that same pair
diverges.
2026-08-06 02:09:27 +08:00
Chinesezjc eb3b635796 docs(tools): widen the ungated language-prose list and correct three JSDoc claims 2026-08-06 01:38:26 +08:00
Chinesezjc 21641ae316 docs(tools): widen the no-runtime reachable set in resolveFlavor 2026-08-06 01:16:09 +08:00
Chinesezjc 665fb987ad docs(tools): mirror the determinism boundary onto the TypeScript renderer 2026-08-06 01:03:03 +08:00
Chinesezjc e19740e7d0 docs(tools): bind wrapped em-dashes, widen the dict degrade note, bound the determinism claim 2026-08-06 00:51:09 +08:00
Chinesezjc 670d6511af docs(tools): reflow the identifier-skew comment paragraphs to the 80-column wrap 2026-08-06 00:37:51 +08:00
Chinesezjc 24cfe8f777 docs(code-runtime): name python in the reference page and complete the ungated-edit list 2026-08-06 00:22:05 +08:00
Chinesezjc b2c1872799 docs(tools): cite per-character Unicode ages and the ungated seam edit 2026-08-06 00:07:10 +08:00
Chinesezjc 05426906b0 docs(tools): count the union member as an edit, and re-scope the runtime guards
The 'adding a language is two table entries plus its renderer' checklist
predates the `CodeSdkLanguage` union and now contradicts the mechanism
sentence beside it: following it literally leaves the union untouched, which
is exactly the excess-property error that sentence promises. It is three
parallel edits, in the note's Decision and Consequences and in the
`SDK_RENDERERS` JSDoc.

Two guard descriptions still claimed work the compiler took over. The
Decision's 'the drift this guards against' now names the `satisfies` pins and
leaves the guards their reachable case, a mounted runtime reporting a language
neither table knows; `resolveFlavor`'s JSDoc drops 'keeps the table coupled to
SDK_RENDERERS' for the same reason. The Consequences said a half-added
language 'cannot arise' for the runtime guards — it can, one PR later at the
consumer's integration point, and never on this base; the claim is now about
timing rather than impossibility.
2026-08-05 23:44:26 +08:00
Chinesezjc 9b3a0982c8 docs: regenerate catalogs and graphs for the shifted source anchors 2026-08-05 23:28:37 +08:00
Chinesezjc e14bcfb08a refactor(tools): pin the two language tables to one union, and name python at the seam
`SDK_RENDERERS` and `RUN_CODE_FLAVORS` had to stay in step by review alone:
the `Object.hasOwn` guards catch drift only once a runtime reporting the
half-added language exists, which is the one case that cannot arise. Both
tables are now `satisfies`-checked against a shared `CodeSdkLanguage` union,
so a missing or extra entry fails `typecheck`. The declared `Record<string, …>`
type stays, since `CodeRuntime.language` is an unconstrained `string`.

The code-runtime seam's own README row and `CodeRuntime.language` JSDoc still
named `'typescript'` as the sole well-known value; both now name `'python'`
too and say only `'typescript'` has a published backend.
2026-08-05 23:25:42 +08:00
Chinesezjc 99218ba41d docs(tools): qualify the last mode-dependent claim, in the spec
`keeps a non-ASCII field name…` asserted that Code Mode omits the native
schemas so nothing else carries a dropped field's name, requiredness, and
type. That holds under `mode: 'code'` only; under `both` the native schemas
ship alongside the SDK, as the module header says. Earlier rounds swept
`py-types.ts` for this family and qualified five sites there; the spec was
never in scope, so this is the family's last unqualified member rather than
residue from those fixes.
2026-08-05 23:09:06 +08:00
Chinesezjc 7d957bc799 docs(tools): name both raw routes, complete the evaluation-floor list
The previous commit's `UNPRINTABLE` sentence said the raw-reach point for all
three characters is `pyScalar`'s `JSON.stringify`, and the test comment said
that route is the only one. Both are exclusive claims and both are false: the
subscript tool-name comment calls `JSON.stringify` itself, and a tool name
carrying NEL, LS, or PS always lands there, none of the three being
`XID_Continue`. `pyScalar`'s own docstring already recorded that inheritance,
so the file contradicted itself. Both sentences now name the two call sites.

The note's evaluation axis was introduced as "the `typing` names the block
spells", which excludes one of its own members (`A | B` is operator syntax)
and omitted PEP 585 builtin generics — `dict[str, Any]` and `list[…]` appear
in nearly every render and need 3.9. The axis is now "the names and syntax the
block would evaluate at definition time", enumerated 3.8 through 3.11.

The test title covered two of the three characters it asserts; NEL is NEXT
LINE, neither a line nor a paragraph separator.
2026-08-05 22:56:32 +08:00
Chinesezjc b869a3b078 docs(tools): close the NEL half of the raw pass-through and reflow
Four non-blocking review suggestions, all prose plus one assertion.

`UNPRINTABLE`'s new sentence named three characters but only two raw-reach
points, leaving "and NEL?" open; it now says all three reach text through
`pyScalar`, and how the description path handles each. `pyScalar`'s
raw-pass-through list already covered NEL under "the C1 controls", and the
test now pins it alongside LS and PS, so the docstring's claim has a
mechanical check for every character it names.

The test title said "paragraph separators" for a pair whose first member is
LINE SEPARATOR. Two docstring paragraphs are reflowed to the file's ~80
columns after the earlier inserts left short lines.

The note's CPython-floor obligation gains a second axis: the `typing` names
the block spells (`TypedDict` 3.8, `NotRequired` 3.11, `A | B` annotations
3.10) are definition-time evaluation floors, not parse floors, so the floor PR
does not read "parseable on the supported range" as "executable on it".
2026-08-05 22:43:42 +08:00
Chinesezjc 05213ec85f Merge remote-tracking branch 'origin/master' into feat/py-types-code-mode
# Conflicts:
#	docs/event-producer-consumer.md
2026-08-05 22:27:13 +08:00
Chinesezjc c5b09c108f docs(tools): record LS/PS as tokenizer non-terminators, with a test
Review read `JSON.stringify`'s raw pass-through of U+0085/U+2028/U+2029 as a
parse hazard: an LS in a `Literal[...]` value or in a `# tools["..."]` comment
would end the physical line and take the SDK block down. Measured on CPython
3.9.6 (Unicode 13.0) and 3.12.13 (15.0): all three are accepted in both a
string literal and a `#` comment, value round-tripping, and only LF and CR
terminate either. The set is the tokenizer's, not `str.splitlines()`'.

Both existing claims were accurate, so nothing changes behaviorally. Name the
distinction where it was assumed: `UNPRINTABLE`'s terminator sentence now says
which set it means, and `pyScalar`'s raw-pass-through list, previously "DEL and
the C1 controls", now also names LS/PS, which are neither. A test pins the raw
form for `const` and `enum` so escaping them later cannot land as a silent
divergence from the TypeScript flavor.
2026-08-05 22:25:45 +08:00
Chinesezjc ab0c275494 style(tools): reflow the UNPRINTABLE paragraph after the qualifier insert
The `mode: 'code'` qualifier left a 109-character line where the rest of the
block wraps at ~80.
2026-08-05 22:07:03 +08:00
Chinesezjc 0891556cb4 docs(tools): qualify the three remaining mode-dependent only-claims
The `mode: 'code'` qualification landed on the module header and
`isBareIdentifier` but not on the other three sites the same reviews
enumerated. `UNPRINTABLE`'s "only declaration of the tools", the open-object
comment's "only signal that extra keys are accepted", and the docstring
comment's "only description of what a tool does" are each false under
`mode: 'both'`, where the native schemas ship alongside the SDK.

Widen the note's predicate-path sentence past head and last position: a
character added to `XID_Continue` passes `IDENTIFIER`'s trailing quantifier
anywhere after the head, the middle of a name included.

Record the ƛ test's table provenance. U+A7DC and the U+019B mapping to it
both arrive in Unicode 16.0, and the engines floor sits exactly there:
Node 22.19.0 reports Unicode 16.0 (ICU 77.1) and produces the mapping.
2026-08-05 21:53:32 +08:00
Chinesezjc be98a0b978 docs(tools): record the case-mapping read point and narrow the class-name quantifier
`camelCase`'s `toUpperCase()` is a fourth reader of the engine's Unicode
tables, on a table distinct from XID membership and with a wider window: a
tool named U+019B passes `isBareIdentifier` and compiles as `async def` on
CPython 3.9.6, but Node maps the head to U+A7DC and the declared
`class ꟜArgs` fails there with `invalid non-printable character`. Record it
alongside the three XID read points in the renderer docs and in the note's
CPython-floor obligation, and pin the derivation with a test.

Correct three over-quantified sentences: a `camelCase`-derived class name is
evaluated for every tool but only reaches emitted text when some object shape
in the schema declares a `TypedDict`. Attribute the `str.isidentifier()`
equivalence to `IDENTIFIER` rather than to the predicate, which is
deliberately stricter, and restore the antecedent the mode qualification
dropped.
2026-08-05 21:33:30 +08:00
Chinesezjc 2914a87eda fix(tools): widen the Unicode-skew obligation past isBareIdentifier
The predicate is not the only reader of the engine's XID tables. camelCase
reads them through its split set and its head test, and the class name it
derives is emitted for EVERY tool -- including one the predicate rejected,
whose TypedDict is still declared and named. A tool named `zz-` + U+1E4D0
never reaches the skew in the predicate, since the `-` rejects it outright,
yet still emits `class Zz<U+1E4D0>xArgs`, which CPython 3.9.6 refuses the
same way. A backend PR executing "pin the predicate against tables for the
floor" literally would leave that path open, so the note and the docstring
now name all three read points.

Two corrections in the same paragraph. The failing direction is a character
added to XID_Start OR XID_Continue -- one added only to the latter passes
the trailing `\p{XID_Continue}*` in a tail position and fails identically.
And the safe direction routes a name to the subscript/`dict[str, Any]`
path: a rejected FIELD name degrades its whole enclosing object rather than
just itself, which the predicate's opening paragraph already said.

Also qualify the module header's "ONLY source" claim, which holds under
`mode: 'code'` but not `both`, where wireSchemas ships every native schema
alongside the SDK section; record the measured str.isidentifier()
equivalence (21 samples, zero divergence, Node 22.23.1 vs CPython 3.9.6)
where the versions it is relative to already live; and attribute the
`FInd` spelling in the ligature test to full case mapping rather than to
the NFKC step, which is the identity there.

Two tests. The fold-collision half of the childClassName fix: sibling joins
that are byte-distinct before NFKC and equal after, so `usedClassNames`
dedupes by raw bytes and the counter only sees the collision because the
join is normalized. And the argument-side oneOf-of-objects branch naming,
which reaches the same childClassName path the output side already pins.
2026-08-05 21:08:24 +08:00
Chinesezjc af51e68875 Merge remote-tracking branch 'origin/master' into feat/py-types-code-mode 2026-08-05 21:01:58 +08:00
Chinesezjc 4a2f371ede Merge remote-tracking branch 'origin/master' into feat/py-types-code-mode 2026-08-05 20:44:45 +08:00
Chinesezjc 8c001d9928 fix(tools): normalize the two class-name joins camelCase's own call misses
camelCase normalized `joined` and then prefixed, so the seam the `Tool`
prefix creates was never covered: `Tool` ends in `l`, a combining-mark
head composes with it, and a name headed by U+0301 was emitted as
`Tool` + U+0301 while CPython compiles `Too` + U+013A. childClassName
has the same shape -- both sides separately NFKC-stable, their join not:
a base ending in a Hangul L jamo or LV syllable composes with a V or T
jamo head. Beyond the declared-name/compiled-symbol mismatch, two
byte-distinct names can fold onto one, and usedClassNames dedupes by raw
bytes, so the collision counter never sees it. Normalize after the
prefix decision and at the join, before the cap. The remaining joins
need nothing: `Args`/`Output` and the digit suffix cannot compose
backwards.

Also record the Unicode-table skew. The predicate reads the engine's
tables (Node 22.23.1: 17.0) and the interpreter reads its own (CPython
3.9.6: 13.0.0), so an interpreter older than the engine takes a bare
name its tokenizer refuses -- U+1C89, U+10570, U+1E290 and U+1E4D0 are
accepted here and rejected there. The other direction only degrades a
legal name to subscript. Closing it needs the CPython floor, which the
backend PR owns; state the asymmetry in the docstring and make the
decision an explicit obligation in the note.
2026-08-05 20:39:11 +08:00
Chinesezjc 2cb0dddb40 docs(tools): stop over-quantifying what String does to a big integral double
The pyScalar paragraph read as a universal over every beyond-safe-range
integral number, and three of its clauses have counterexamples inside
that very domain: String(2 ** 53) and String(1e20) are byte-identical to
BigInt's digits, so the "different integer or no integer literal at all"
split is not exhaustive, "the 16 digits" is 2 ** 60's instance count
rather than the mechanism (shortest round-trip is 1 to 17 significant
digits), and padded digits do name a held integer for 1e20. Say shortest
decimal string then padded to the exponent, give both counts, condition
the no-double-holds-it clause, and state the invariant that makes the
rule unconditional: where String is already exact the two agree, and
where it is not, BigInt is the exact one.

Also align one README.zh.md term: the same file already translates
"exotic names" as 特殊名称 in the SDK-section bullet.
2026-08-05 20:06:06 +08:00
Chinesezjc 5d65686c33 feat(tools): accept Unicode Python identifiers in the Python SDK renderer
The identifier test was ASCII-only, so an object with a `路径` field
degraded to dict[str, Any] -- dropping every sibling field's name,
requiredness and type, with no native schema behind it in Code Mode to
carry them. Python identifiers are `xid_start xid_continue*`, so match
that instead, and widen camelCase's split and head check to the same
sets (naming `_` explicitly in the split, since it is XID_Continue).

NFKC stability is a second and separate condition. CPython normalizes
identifiers at compile time while a JSON key is compared as written, so
a U+FB01 ligature key would be declared and reachable under its ASCII
expansion, a key the tool never accepts, and two keys that normalize
together would collapse
into one declaration. Those names take the subscript path. Generated
class names are normalized instead of rejected -- they are never matched
against a key. Astral characters can now reach the class-name cap, whose
slice counts UTF-16 code units, so drop a split surrogate half.

Also fix two comment claims. The note said one projection reads the
runtime twice per tool; the language-aware getters are installed on
run_code's own definition, so it is twice, both for that schema. And the
182-bracket site's reachability is an array reached from the root
through oneOf arms alone -- a union spine of any depth, not just one
root union; an object ancestor restarts the chain at the 181 site.
2026-08-05 20:03:39 +08:00
Chinesezjc ba634896e0 docs(tools): name the boundary that rejects a padded integer, and what the flavor-guard test actually reads
pyScalar's docstring attributed the rejection of a String-spelled
beyond-safe-range integer to "the Python runtime". No published backend
makes that call on this base. The fact that does not depend on one: the
padded digits name an integer no double holds, and passing it back would
have to cross the argument boundary as a JSON number. Say that, and say
why String rounds at all -- Number::toString is shortest round-trip, so
2 ** 60 emits the 16 digits that re-read to the same double and pads.
Mirror both in the test comment.

The note's Decision sentence said a test covers the flavor guard through
ctx.tools.schemas(). The test reads the definition's getter directly,
under a language absent from both tables; schemas() reaches the same
getter but has no assertion. Name what is read, and record that a
renderer-without-flavor language is drift this guards against rather
than an existing input -- the two key sets are identical today.
2026-08-05 19:44:05 +08:00
Chinesezjc 015bef2f5f docs(tools): widen the 182 reachability shape and finish the note's two language-binding facts
"Reachable only through a raw register() whose parameters is
array-rooted" was too narrow. A root oneOf reaches the same 182: the
union arm propagates listDepth unchanged because `A | B` opens no
bracket, so an array branch starts its chain at 0 exactly as an array
root does. Say "root opens an array chain -- rooted at the array, or at
an array branch of a root oneOf", in the JSDoc and the test comment,
and assert the union shape alongside the array-rooted one.

The note's Decision paragraph said the flavor guard is reached under
"a language that has a renderer but no flavor entry, and a test covers
it". The test uses ruby, absent from both tables, and the mechanism is
that schemas() reaches run_code's getters without passing
requireCodeRuntime -- so any language absent from the flavor table hits
it. State that instead.

The Consequences paragraph recorded the language-binding obligation as
two reads, assembly and execution. Within one projection there are more:
run_code's description and parameters getters each call
resolveFlavor(peekRuntime()) and schemaOf destructures both, so a reload
between them yields one schema whose halves name different languages.
2026-08-05 19:37:11 +08:00
Chinesezjc b44acab888 docs(tools): correct three comment claims about what defineTool and the subscript path do
"defineTool compiles an object root, so the annotation is a bare
TypedDict class name that opens nothing" is a false universal:
parameterSchemaSpecToJsonSchema compiles an OPEN object root, so an
empty parameter table and one with unrepresentable field names both
degrade to dict[str, Any], which opens one bracket. The conclusion the
sentence carries is unaffected -- 1 or 2 against a 182 cap -- so say
"a bare TypedDict class name or dict[str, Any], neither of which
carries a chain", in the JSDoc and the test comment that copied it.

pyScalar's docstring said the subscript tool-name comment quotes
"through the same call". It quotes through its own JSON.stringify call
site in renderToolsSdkPy and never reaches pyScalar, which only takes
const/enum scalars. Same function, different call site.

The mode-'both' test attributed assembly.tools to the public schemas().
That projection is wireSchemas, wired at ctx.systemPrompt.tools.
2026-08-05 19:22:42 +08:00
Chinesezjc 72991bbcdb fix(tools): count kinds of code point, not code points, and cover a hostile tool name
"the two code points CPython refuses" counted classes: NUL is one code
point, unpaired surrogates are the whole 2,048-wide D800-DFFF block. Say
kinds, in both the docstring and the test comment that mirrors it, and
restore the "odd" qualifier the test comment dropped -- an even trailing
backslash run does not eat the closing quote.

The soft-keyword test title still said "only special in statement
position", which the previous commit's own three-way split contradicts
for `case`: `case_block` is a clause head inside a `match` statement, not
a statement.

Add the case the subscript tool-name path lacked. A lone surrogate is
reachable in a name through JSON.parse of MCP wire JSON, and that path
has no UNPRINTABLE / LONE_SURROGATE fallback -- only the same ES2019
well-formed stringification the Literal path leans on.
2026-08-05 19:17:34 +08:00
Chinesezjc f3c8695fd6 test(tools): pin the argument-annotation nesting cap, the worst of the three sites
The 182 the cap is chosen against had no direct case: the existing tests
cover the root chain and the TypedDict field, both of which start one
bracket lower. An array-rooted parameters schema reaches it from a plain
ToolSdkSchema literal, no raw register() needed. Exactly 180 arrays over
a const scalar is the worst case itself -- the root frame starts at
listDepth 0, so every list[ still emits and the innermost Literal[ is
reached rather than degraded; one deeper is where the item degrades.

Name the subscript tool-name comment in pyScalar's docstring: it quotes
through the same JSON.stringify call and inherits the same escapes and
the same pass-throughs.
2026-08-05 19:07:10 +08:00
Chinesezjc dbefb2fa90 fix(tools): complete the Literal parseability attribution and the soft-keyword positions
pyScalar's docstring named only the two code points CPython refuses
anywhere in source. A bare quote, a trailing odd backslash, and a bare
LF/CR break the Literal line just as fatally, and JSON.stringify is what
covers those too. The argument also leaned on an unstated coincidence:
every escape JSON.stringify can emit is a Python escape for the same
character, which is why the emitted text both parses and decodes back to
the declared value. Say both, and assert the second class.

"statement head" does not describe `case`, whose clause block is not a
statement. Split the positions three ways.

Add the mode 'both' by python assembly, pinning the mode-by-language
matrix rather than leaving it to the shared code path.
2026-08-05 19:01:18 +08:00
Chinesezjc 5afcf36ae1 Merge remote-tracking branch 'origin/master' into feat/py-types-code-mode 2026-08-05 18:45:10 +08:00
Chinesezjc 9bba851a62 fix(tools): correct the trim-order claim and check the Literal escape dependency
trim and escape commute for every input, so the new whitespace test does
not pin their order: UNPRINTABLE and LONE_SURROGATE are disjoint from the
set trim() strips, and both escapes emit plain non-whitespace ASCII,
leaving the leading and trailing whitespace runs byte-identical. State that instead of the false causal clause.

pyScalar's Literal path escapes nothing itself -- JSON.stringify is what
keeps it parseable, covering NUL and, under ES2019 well-formed
stringification, unpaired surrogates. Record the dependency and turn it
into a checked invariant. Pin the docstring emission site for a lone
surrogate too, mirroring the NUL case.

Two docstring corrections: describe's caller enumeration omitted the
synthetic { description } wrapper docLines builds, and "special in
statement position" does not describe `_`, which is special in a match
pattern. Both keep the conclusion they support.

Note which of the two table guards fires depends on the entry point.
2026-08-05 18:43:03 +08:00
Chinesezjc 529e69ed87 Merge remote-tracking branch 'origin/master' into feat/py-types-code-mode 2026-08-05 18:21:54 +08:00
Chinesezjc cf85c9a3e4 fix(tools): escape unpaired surrogates and state the Cf boundary by category
U+00AD is 0xAD, so "Cf cannot be addressed by \xNN" was false for the
first example in its own list. The real boundary is the category: one
\xNN form covers Cc exactly, and escaping the single addressable Cf
member would leave a rule that is neither category- nor
addressability-shaped.

A lone surrogate is the NUL case rather than the invisible-character
case -- Python source must be UTF-8-encodable, and compile() raises
UnicodeEncodeError for one in a string literal or a # comment alike
(measured on 3.9). JSON.parse on a wire "\ud800" escape produces them,
so escape them as \uNNNN; the regex's u flag keeps well-formed astral
pairs intact.

Pin the whitespace-plus-surviving-control boundary, which also pins
trim-after-escape.
2026-08-05 18:21:43 +08:00
Chinesezjc 308f5ae0f3 fix(tools): escape the whole C1 control range, not just NEL
Unicode Cc is U+0000-U+001F plus U+007F-U+009F, and no C1 code point is
ECMAScript whitespace, so U+0080-U+009F all survived the collapse and
reached the docstring raw and invisible -- the gap the previous commit
closed for NEL alone. \xNN addresses the whole block, which is the same
reason the set stops at Cc, so widen the class to U+009F and pin
U+009B/U+009C/U+009F.

Windows-1252 bytes 0x80-0x9F decoded as Latin-1 produce exactly these.

Also: required TypedDict fields share the optional fields' listDepth
start, and a description of whitespace plus a surviving control
character is not absent.
2026-08-05 17:59:48 +08:00
Chinesezjc 1b4cb031f0 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.
2026-08-05 17:45:38 +08:00
Chinesezjc bc94431c34 docs(tools): state the Python SDK declarations are static stubs
A TypedDict reads as a constructible class, so a model that writes
FooArgs(field=1) fails with NameError before dispatch: the run request
injects only the tools namespace and ToolCallError. Say so in
SDK_INSTRUCTIONS and require plain dict/list JSON arguments. The TS
flavor needs no counterpart -- interface is visibly a type and its
"runs type-stripped" clause already covers erasure.
2026-08-05 17:28:58 +08:00
Chinesezjc 137a2f4a4f docs(tools): name the underscore family in the Python SDK usage contract 2026-08-05 17:15:35 +08:00
Chinesezjc cb53dbe24a docs(tools): correct the bracket-count sites and the underscore routing rationale 2026-08-05 17:01:51 +08:00
Chinesezjc a2c4aa513b Merge remote-tracking branch 'origin/master' into feat/py-types-code-mode
# Conflicts:
#	packages/code-runtime/README.i18n.yaml
#	packages/code-runtime/README.zh.md
2026-08-05 16:48:01 +08:00
Chinesezjc a525d7d1e2 test(tools): pin underscore-leading tool names to subscript access 2026-08-05 16:46:23 +08:00
Chinesezjc c844bbc8dc Merge remote-tracking branch 'origin/master' into feat/py-types-code-mode 2026-08-05 16:31:09 +08:00
Chinesezjc 581d2ee621 docs(tools): correct the propagated branch-name example to the index-derived 1 2026-08-05 16:31:07 +08:00
Chinesezjc cc6e4d59fc docs(tools): scope the Python SDK validity standard to the grammar
The list-nesting cap guards against a tokenizer SyntaxError, which makes the
text not Python. A long `A | B | …` union is valid at any length and only
defeats CPython's compile-time C recursion (measured: 1,000 branches compile,
5,000 raise RecursionError); nothing compiles this block, and capping would
retire the deep-chain tests pinning the walk's linear time. Records that
boundary at the `oneOf` arm and in the Agent Note (both languages).

Also documents that the context-free degrade marker reads the call's
className rather than the frame's — frames propagate a derived name, so a
per-frame read would declare classes the caller cannot receive — and pins
that path with oneOf-of-objects and array-of-oneOf assertions.
2026-08-05 15:59:15 +08:00
Chinesezjc 0d17baae01 fix(tools): restore the v8 ignore adjacency broken by an inserted comment
The directive must sit on the line before its target; the nesting-cap comment
displaced it onto a comment line, leaving the `?? ''` arm uncovered.
2026-08-05 15:55:42 +08:00
Chinesezjc 95da760696 fix(tools): cap Python SDK list nesting at CPython's bracket limit
A schema nesting arrays past ~200 levels rendered a `list[list[...]]` chain
CPython's tokenizer rejects outright (`too many nested parentheses`), so the
SDK block was not valid Python at all — the failure docstring escaping in the
same file already guards against. The chain now degrades to `Any` at 180
levels; nesting restarts per TypedDict field, since a field annotation is its
own logical line. Unions and nested objects are unaffected: neither
accumulates open brackets.

Also aligns the unreachable SDK_RENDERERS guard message with the two reachable
ones, and corrects a test comment that still said class docstring.
2026-08-05 14:59:52 +08:00
Chinesezjc b83c15b1ed Merge remote-tracking branch 'origin/master' into feat/py-types-code-mode 2026-08-05 14:07:53 +08:00
Chinesezjc a1d7b9a3cd fix(tools): treat a whitespace-only description as absent in the Python SDK
It collapsed to '' rather than undefined, so the renderer emitted an empty
`""""""` docstring or a bare `#   ` line for a node that documents nothing.
2026-08-05 14:05:02 +08:00
Chinesezjc 3f7707e9aa test(tools): satisfy noUncheckedIndexedAccess in the docstring test 2026-08-05 14:03:43 +08:00
Chinesezjc 766ef45bd7 Merge remote-tracking branch 'origin/master' into feat/py-types-code-mode 2026-08-05 14:02:55 +08:00
Chinesezjc 7a178951d6 fix(tools): attach Python SDK docstrings to their own methods
A description was emitted above the `async def`, where Python treats the
first string as the `Tools` class docstring and every later one as a dead
expression — leaving each method undocumented in the model's only source of
tool semantics. Emit it as the first statement of the method body instead.

Also names the known languages in the run_code flavor guard (the reachable
rejection, symmetric with the SDK_RENDERERS guard) and corrects three doc
claims: the code-runtime group README no longer calls the generated SDK
TypeScript, the base Code Mode note states its serial dispatch in past
tense, and the tools README points at the rationale the language-dispatch
note actually carries.
2026-08-05 14:02:47 +08:00
Chinesezjc 975350c7b8 Merge remote-tracking branch 'origin/master' into feat/py-types-code-mode 2026-08-05 13:18:05 +08:00
Chinesezjc 1e202cd28e docs(notes): retire the parallel-native-dispatch TODO claims in the Code Mode note
The rewritten scheduler paragraph states that the native loop already
classifies through isConcurrencySafe, which contradicted two surviving
present-tense claims that parallel native dispatch is an open TODO
blocked on that same metadata. Both now attribute the TODO to decision
time and point at the shipped rolling pool.
2026-08-05 13:15:04 +08:00
Chinesezjc 698fdaea9b fix(tools): emit Python SDK members in one lexicographic stream
The Python renderer partitioned identifier methods ahead of subscript
comments, so a tool set like {a-tool, z} emitted z first — contradicting
the documented lexicographic contract and the TypeScript flavor, which
quotes exotic keys in place. Interleave both kinds in one ordered stream
and track emitted statements for the pass fallback.

Also correct four stale serialization claims in the base Code Mode note
that the live-parallel scheduler superseded.
2026-08-05 12:59:20 +08:00
Chinesezjc 0220e06633 docs(tools): scope the Python snapshot obligation and language-neutral concurrency wording 2026-08-05 11:53:17 +08:00
Chinesezjc c273b4c25b Merge remote-tracking branch 'origin/master' into feat/py-types-code-mode 2026-08-05 11:50:52 +08:00
Chinesezjc a9a12ad720 Merge remote-tracking branch 'origin/master' into feat/py-types-code-mode 2026-08-05 11:46:45 +08:00
Chinesezjc 3a060c5688 Merge pull request #1175 from deepseek-harness/fix/web-sources-scroll
fix(client-web): scroll the web_search source card instead of collapsing
2026-08-04 13:20:16 +08:00
Chinesezjc c67c89911e Merge origin/fix/web-sources-scroll 2026-08-04 13:13:41 +08:00
Chinesezjc 6601738e32 Merge origin/master into fix/web-sources-scroll 2026-08-04 13:11:40 +08:00
Chinesezjc fffd1ca18e Merge branch 'master' into fix/web-sources-scroll 2026-08-04 11:41:15 +08:00
Chinesezjc e6d6e192d5 fix(ui-primitives): keep two-digit source markers out of the scroll clip
Making `.sources` a scroll container turned its `padding-left` from spacing
into a correctness constraint. A scroll container clips inline-start overflow
with no way to scroll it back, and `::marker` is right-aligned to the content
edge, so past nine sources the markers rendered as `0.` and `1.` where `10.`
and `11.` belonged.

`searchMaxResults` is an unbounded positive integer, so size the padding in
`em` against the list's own font to hold a three-digit marker. The browser e2e
measures a `999. ` marker in that inherited font and requires the computed
padding to be at least that wide, pinning the room against the widest marker
rather than one fixture's source count.
2026-08-03 18:58:44 +08:00
Chinesezjc 2b02202ba1 Merge remote-tracking branch 'origin/master' into fix/web-sources-scroll
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-08-03 18:18:25 +08:00
Chinesezjc e5351d3923 test(web): refresh the Markdown image golden for disabled non-tail forks
PR #1003 re-recorded this golden from a base that predated PR #1150, which
disables the branch control on any message that is not a completed turn's
tail. Both landed on master, whose CI skips the snapshot lane, so the
conflict only surfaces on PRs. The first message's control is now disabled
with its explanatory text.
2026-08-03 17:13:14 +08:00