Cache README (EN/ZH): the medium is the session_projcache domain in
per-record layout (one version-stamped document per session under the
json backend root), reads are synchronous from the domain's in-memory
tables, and the storage stack rides in base. storage-json README
documents both layouts and their contracts; web-app README notes that
storage and the projection cache live in the shared base. Regenerated:
session-projection subsystem catalog (sync cachedSnapshot, domain
medium), config-catalog (Config.root gone), module-graph (cache now
depends on storage-domain, not session-persistence), cli composition
(storage rows in base), and the projection-cache Agent Note — which now
records the file-root revision and its revert as rejected alternatives.
ProjectionDefinition lost its persist?: boolean opt-in when every
projection unit became uniformly checkpointed, but the subsystem doc's
type-equiv paste still carried the field, so verify-type-equiv drifted
from packages/session/session-projection/src/index.ts. Remove the field
and its JSDoc from both language sides and re-record the bilingual
pairing record.
- subsystem docs: drop the removed persist flag from the ProjectionDefinition
type block (both languages).
- config-catalog regenerated (cache requires sessionProjections/sessions,
config gains root) and the zh side synced by hand; doc graphs regenerated.
- Agent Notes: the per-session cache note records the owned root tree and
no-persistence design; the storage-root proposal's link to it is corrected
(two levels up).
Replace the single global session_projcache domain with a per-session
cache file inside the session's own persistence directory, resolved
through sessionPersistence.locate(meta) — the persistence backend owns
the session-directory layout, the cache service keeps every checkpoint
and cold-read responsibility.
- cachedSnapshot(meta) becomes async (one file read per session);
coldSnapshot takes the session header so it can locate the file, with
the stored log header remaining the identity witness.
- Backends without a per-session directory (sqlite) disable the durable
cache: writes no-op and cold reads fall to the full-log rung. An
obsolete global cache is never read — derived data refolds on first
cold read (no migration).
- writeAtomic is exported from dsh-storage-json as the shared atomic
whole-file replace primitive; api-proxy listing and subagent cold
reads await the now-async cachedSnapshot.
- READMEs and a new Agent Note document the per-session medium.
Drop the persist?: boolean opt-in: every unit's state — client-visible and
host-only alike — is now written to the projection cache. A unit can no
longer silently skip the durable cache, host-only units no longer need an
explicit flag to participate in cold restore, and the persist-sharing
conflict check disappears with the field.
- ProjectionDefinition/ErasedDefinition lose persist; register overloads
simplify; checkpoint/restoreFloor/restore fold every registered unit.
- Registry and cache tests drop the persist:true fixtures and the
persistence-policy sharing test.
- READMEs and the state-and-client-views note record the uniform rule;
cordis API catalog and subsystem signatures regenerated.
An image with a side above the deployed routes' 2000px many-image bound
could be durably committed by read_image, ride every later request, and
permanently fail the session with provider 400s. Admission now enforces a
configurable maxImageDimension (default 2000) during the full decode, so
read_image surfaces a recoverable tool error naming the limit instead of
poisoning durable history; the Web composer gets dedicated copy for the
new IMAGE_DIMENSION_TOO_LARGE reason.
Fixes#2626
master introduced AttachmentStore.saveImages as the batch admission
(count/aggregate-byte/media-type limits, validate-all-before-save,
ordered commit). admitEncodedImages narrows to the shared wire entry:
canonical-base64 enforcement plus delegation to saveImages, keeping one
home for batch policy while both wire endpoints (prompt RPC and the
command executor) still call one function. Test doubles gain saveImages;
batch-limit error texts follow saveImages' wording.
A claimed slash command consumed only the text half of the composer
submission: /goal with reference images executed, cleared the draft, and
silently stranded the images in the rail. Model-visible attachment intent
had no route through the command plane.
The submission envelope is now modeled end to end. CommandDefinition
input.images declares acceptance; the declaration rides the descriptor to
every client, onto the minted CommandClaim, and into the input machine's
claim snapshot. commands.execute carries the submission's base64 images
and enforces the declaration in the executor: non-declaring commands, a
missing attachment store, and exceeded batch limits settle as logged
error results before the handler runs. Admission reuses the attachment
package's new admitEncodedImages, extracted from api-proxy's prompt path
so both wire endpoints share one limits/validation/commit sequence.
Producers own model visibility: /goal submits one user followup (image
blocks + a fixed reference line) after a successful create/edit so goal
rounds read the images from session history; /plan folds them into its
steered message. Grammar misfits (/goal pause, bare /plan, /plan off)
return direct errors and the composer keeps the images.
On the client, enter adjudication carries a SubmitEnvelope and every
command route that cannot consume images throws a localized refusal that
renders as one composer notice with draft and images retained; the
claimed pre-gate applies the same copy. An accepting claim serializes the
draft images, forwards them to commands.execute, and clears plus releases
them only on a success outcome.
The assembled web test roster gains the ui-input-trigger and ui-commands
plugins, mirroring the shipped composition, so slash submissions exercise
the command plane; a new keyless snapshot pins the refusal banner and the
accepting /goal flow over the built client graph.
Replace the legacy reference.* API Proxy domain with @Remote methods on the
owning services, following the typert gateway design master adopted on
2026-08-02 (message-feedback and plugin-inventory precedents):
- FileReferenceService and SessionReferenceResolver extend TypertRemoteService;
fileReferences/list and sessionReferenceResolver/candidates are unary Remote
methods cancelled through the reserved trailing signal, and the candidates
face attaches each candidate's canonical mention under the configured limit
- move the wire types to type-only ./types subpaths (FileReferenceCandidate,
SessionReferenceMentionCandidate) and export ./typert plus ./remote artifacts
- mount both contributions in the api-remotes client assembly; ui-reference
consumes ctx.remote instead of connection.api.references and registers zh/en
locale dictionaries for its sections and labels
- delete the reference.* routes, schemas, map rows, client stubs, and fixtures;
the connection fixture serves the Remote endpoints instead
- release deliverPrompt admission listeners when the agent is disposed with the
prepared prompt still pending, and cover the reference-* RpcError codes in
the schema spec
- add the missing tsconfig paths for the /grammar and /types subpaths (clean-
tree vitest could not resolve @deepseek-ai/dsh-file-reference/grammar)
- regenerate the cordis catalog, capability seams, and event matrix; update the
owning bilingual READMEs, Agent Notes, and the reference-composer golden