- stateVersion starts at 1: the package is new, no persisted rows predate it
- pendingCalls pairs by own key so a provider callId naming a prototype
property cannot fold toolMs to NaN on an unmatched crash-recovery result
- StatsLine folds the window fallback only when no sessionStats value is
served, and gates the token group on actual token activity instead of
steps, so failed-only sessions drop the zero-token group
- correct the crash-step counting semantics in the README and Agent Note:
recovery closes interrupted steps with a synthetic step/end on reload
- reword the window-scoped alternative as a plain rejected option and name
isTokenDelta's home beside the StreamChunk type
turn/start precedes the first claim and pre-step, a rejected or empty first claim closes a zero-step turn, llm/stream and tools/* are their own domains, and agent/turn-stopping is serial. Regenerates the lifecycle diagram from the same facts and names --patch overlays exactly.
Correct the reworked English wording in the CLI profile section, architecture map, and llm-streaming cross-reference, then bring every edited pair's Chinese side along and re-record the pairing hashes.
The merge conflict resolution kept the master-side snapshot which had
the old DSH_TELEMETRY_DISABLED=1 text. Regenerated to match the current
README.md which uses DSH_TELEMETRY_MODE=FEEDBACK_ONLY/FULL.
Keep both Remote contributions master and this branch add: the mount loop
now carries commandsRemote, goalsRemote, pluginInventoryRemote, and
messageFeedbackRemote, with both new tsconfig references retained.
The declared-provider-catalog note still recorded modalities as closed
because "nothing reads them: context.ts keeps only text blocks" — the
same reasoning this change disproves. Rewrite that fact in place, keep
pricing closed for its own still-current reason, and cross-link both
directions.
The new resolver cases never crossed the config schema, so neither
acceptance path this change adds had an executable gate: config.spec.ts
now refuses an unknown modality at both levels, pins the empty route
list to the namespace validator the settings seam actually runs, and
pins the `[]` materialization the inheritance rule depends on. A
catalog.spec.ts case re-asserts every rung through a written settings
section and `ctx.llm`, which is what the note's Testing section claimed.
Also export PiAiModality from the package entry — it types two public
config fields and consumers could not name it — record the unverified
declaration as a Known Limitation, and stop sharing one fallback array
across a route's models.
A model the installed pi-ai catalog does not describe was reported as
text-only with no way to say otherwise, so a vision model added through
the custom-provider form was refused at every image admission point.
The justification in the source described the DeepSeek chat-completions
serializer, which does reject image blocks; the pi-ai request converter
and every wire protocol it speaks carry images.
Modalities now resolve entry `input` -> installed catalog entry -> route
`defaultInput`, the chain the two capacity fallbacks already use, so the
route value is a fallback and never narrows a catalog model. Its default
stays `[text]`: nothing can interrogate a gateway for its modalities, and
over-claiming admits an image the provider rejects mid-turn, after prompt
admission has already committed the message.
Anchor the max-tokens notice between the closing assistant and the
turn-tail so the tail stays the turn's last chat node and its branch
action survives; forward the ChatNodeDataMap augmentation through the
client entry so built declarations publish the new kind; advance the
fixture's nextTurn past the appended history; drop the engine-unreachable
buildViewNode fallback; deduplicate the runtime README tail sentence.