- 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.
- 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).
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.
None of these change behavior; each said something that was not true.
`SessionCwdConflict`'s doc block had been left stranded above the
`AgentPresetConflict` inserted under it, so one class carried a comment
about the other and the second carried none.
The roster comment named a `.system` directory that does not exist; the
shipped root is `config/agent-presets/`, and `system` is the trust its
entries carry.
The real-composition test attributed the disabled `api-gateway` row to
"side effects outside this process" alongside the port and the exporter.
It is disabled for a different reason — the api-proxy cannot mount in
this layer at all — and hiding that behind the same phrase would leave a
later layer unable to tell whether the line can come out.
One test claimed to refuse an adoption while asserting only that the
header records the preset; it now says what it checks.
`PERSONA_SECTION`/`PERSONA_ORDER` existed twice, once in the registry
that declares the slot and once restated in the row that replaces it —
a drift that would land a preset's persona beside the deployment's
instead of shadowing it. The registry exports them now.
The preset conflict message read "already runs agent preset undefined"
for a session that records none, which is the shape a deployment with no
roster produces; it names that case instead, with the regression that
reaches it through the gateway.
Finally, `PresetTree.write()` drops the `loader/config-update` the
inherited method emits — recorded on the override, since a future
edit-while-running flow needs its own persistence path.
`session.create` takes an optional `agentPreset`, and the host resolves it,
mounts it during pre-publication setup, and records the resolved id on the
session header so a later resume rebuilds the same agent.
Resolution happens BEFORE the session exists, not inside setup: the session
boundary snapshots `meta` before asynchronous setup begins, so an id discovered
during setup could never reach the header. Mounting still happens in setup,
where a failure rolls the whole creation back rather than publishing a session
whose capabilities are half-installed.
Resume ignores whatever the request names and rebuilds from the stored id. A
resumed session's history was produced under that composition; restoring a
different one would replay tool calls the model can no longer make.
`dsh-agent-presets` now throws `UnknownPresetError` / `PresetMountError` so
the host can tell a bad request from a broken preset — they become
`agent-preset-not-found` and `agent-preset-invalid`.
Ships the two built-in compositions (`standard`, `core-web`) and the persona
row that lets them differ in identity. Nothing mounts them yet: no roster is
configured, so `composeAgent` finds no service and every session keeps the host
composition. Wiring the roster and moving base's agent-plane rows behind it is
the next commit, so the switch happens atomically with a real-composition test.
- the cached-identity rung gains a finality gate: the identity value
carries its descriptor seq and a cached row is served only when that
seq lands in the child's own suffix, so a fork seed's replayed
ancestor identity can never outrank the authoritative refold
(stateVersion bumped for the state-shape change)
- the cold preparation validates the inspected header against the
enumerated candidate's lifecycle witness; a republished id degrades to
that child's corrupt diagnostic instead of leaking the new owner's log
- the new projection registration proves HMR disposal; companion notes
qualify the superseded decision text and record the deliberate
error-face asymmetry
- the subagent projection's empty state becomes a serializable null
sentinel (undefined fields vanish in JSON push frames, leaving stale
identities in clients); consumers treat null and a missing key alike,
and cached sentinels defer to the authoritative refold
- the tool catalog generator mounts the projection registry for
list_agents; the shared unavailable-error copy goes catalog-neutral
- the design note's verification section records the new
subagent-diagnostic snapshot honestly, the 2026-07-22 note's test
inventory is rewritten to the current mechanism, and the session-store
error code is documented
Cold children consult the optional session-projection-cache checkpoint
before paying a preparation recompute: the identity is immutable once
appended, so a cached value is definitive regardless of its watermark.
The cache stays a read-only accelerator (absent service or any rung-two
fault falls through silently; verdicts stay with the authoritative
refold), and the note plus core-data-structures pages describe the
three-rung ladder.
- listChildren reads the session store via strict ctx.get (property proxy
is caller-scoped), orders candidates branchlessly, narrows the cold-read
return type, and pins the cost model and store/registry composition gaps
with tests; per-file coverage restored
- acp-agent and headless-agent compositions mount session-projection; a
keyless snapshot pins the descriptor-less diagnostic row
- api-proxy cold spec pins header-origin ownership and the legacy
descriptor-only opt-out
- design note ships as implemented with its English pairing; companion
notes and core-data-structures pages synced
Address review feedback on PR #1738:
- ReactLoopAgent builds its AgentEventDispatch once in the constructor and
routes every emit/serial/waterfall through it, so hot-path dispatches no
longer allocate a carrier and dispatcher per call; the public carrier
field is gone (fused dispatcher is private).
- agentEvents accepts an optional prebuilt carrier.
- The fused payload builder spreads the payload before the injected agent
so a structurally acceptable payload carrying an agent field can never
override the subject.
- Regenerate doc graphs; re-record core + architecture + affected Agent
Note translation pairs; add payload-object event contract Agent Note.
All agent/* and agent-loop/config-start-failed events take one payload
object carrying the agent subject; waterfall/serial payloads require a
signal and keep next as the final argument. PreStepContext and
RequestFailureContext are unfolded into payloads and retired.
goal/changed follows the same shape so agentEvents keeps its listener
error containment. ReactLoopAgent builds its scope carrier once in the
constructor. Regenerates scope resolvers, tool-cordis api catalog, and
docs catalogs; updates all affected listeners, tests, and the
core-data-structures docs (en + zh).
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.
llm.discoverModels was reachable from any declared trusted host. The
method takes a caller-supplied baseURL and makes the host issue a GET to
it, then reports the status or the parsed body — so on a LAN deployment
an anonymous caller had a probe for whatever the host can reach and the
browser cannot, plus a path that carries a draft credential. The
PRIVILEGED_METHODS doc already states the rule this broke: trustedHosts
is a DNS-rebinding fence, not authentication, so the configuration plane
stays loopback-same-origin. It is in that set now, asserted both against
the hand-built fence and over real HTTP beside the catalog reads that
deliberately stay reachable.
supportsDiscovery and listModelDiscoveryNamespaces are gone. The field
was required on the wire and read by nobody: its own contract said a
surface should offer the action "instead of naming an adapter family it
would have to hardcode", while the surface hardcodes llm-pi-ai in two
places and gates the button on whether there is anything to probe. Its
shape did not fit the second caller either — the create card has no row
to read a per-row field from. Keeping a required field alive for a
consumer that may never arrive costs every producer and fixture a value
nobody consults, which is exactly how the fixtures drifted. The registry
that fed it had no other production consumer, so registration and
disposal are now observed through the offer itself.
The Agent Note claimed the key is never logged, which the wire schema
beside it already contradicts, and predated both the provider field and
the catalog-answer path. The two new public types pointed at core.md
without a type-equiv block or manifest entry, so the generated service
catalog named documentation that did not exist.