Merge remote-tracking branch 'origin/master' into fix/workspace-context-rendered-change-proof

# Conflicts:
#	.agents/notes/implemented/feature/2026-06-24-workspace-context.i18n.yaml
#	packages/context/workspace-context/README.i18n.yaml
#	packages/context/workspace-context/tests/workspace-context.spec.ts
This commit is contained in:
ZiyaZhang
2026-08-06 02:06:16 -07:00
3854 changed files with 218071 additions and 54785 deletions

View File

@@ -6,7 +6,7 @@ Per-session workspace instruction loading for `AGENTS.md`-compatible files. The
## Lifecycle
The baseline is injected at the first `agent/step` of each live session. It reads `$DSH_HOME/AGENTS.md` followed by, in each directory from the project root to `agent.session.header.cwd`, every existing base candidate and then every existing local-overlay candidate. Within one directory, candidates whose content is byte-identical after trimming leading and trailing whitespace collapse to the earliest candidate in configured order, so a `CLAUDE.md` that merely duplicates its sibling `AGENTS.md` is rendered once. The durable sourced `user/message` enters the same request as the claimed prompt.
The first eligible `agent/pre-step` of each live session composes the baseline. When the downstream decision enters a nonempty first-step batch, the plugin folds the baseline into that final batch right after the claimed prompt, so the direct prompt and the durable baseline enter step 1 and reach the first request together. A rejected or empty first-step decision leaves the baseline in the agent's `next-step` inbox for a later wakeup. The loader reads `$DSH_HOME/AGENTS.md` followed by, in each directory from the project root to `agent.session.header.cwd`, every existing base candidate and then every existing local-overlay candidate. Within one directory, candidates whose content is byte-identical after trimming leading and trailing whitespace collapse to the earliest candidate in configured order, so a `CLAUDE.md` that merely duplicates its sibling `AGENTS.md` is rendered once. If a previously queued workspace context is still pending, the plugin removes and replaces that exact inbox item instead of accumulating duplicates.
The plugin also listens on `tools/post-execute` for successful first-party `read`, `write`, and `edit` calls. Each touch checks newly reached descendant scopes and every previously loaded scope. Each configured candidate name is an independent scope in its directory: a newly present file is attached through the result's `additionalContexts`; a changed file appends a replacement; a file that disappears or becomes a per-directory duplicate of an earlier candidate appends a removal notice. Native calls and Code Mode sub-dispatches share this path: `run_code` defers each nested context until its outer result, so the loop still appends updates after tool-call/result adjacency is complete. This follows structured filesystem activity rather than shell `cd`, because each local bash call starts a fresh shell and parsing arbitrary shell syntax would be unreliable.
@@ -48,11 +48,11 @@ The plugin owns the complete `<system-reminder>` framing, and every injected `us
## State And Refresh
Model-visible text contains no hidden state markers. Each baseline or dynamic context event instead carries a typed `workspace-instructions` source with a list of `{ action, scope, path, digest? }` changes; the complete startup or resume baseline also carries `baseline: true`. On every relevant tool touch, the plugin reconstructs loaded state from its visible session events and overlays a short in-memory pending window for context present on the immutable top-level `tools/result` but not yet appended by the loop. A matching durable `user/message` confirms the pending transition. If the owning `step/end` arrives before a matching context reaches the log, the plugin clears the pending transition and its version fast path so the next successful touch can load it again. Nested Code Mode results stage pending changes under the outer execution token for same-run duplicate suppression; the outer result rolls that state back and recommits only contexts that survived outer policy.
Model-visible text contains no hidden state markers. Each baseline or dynamic context event instead carries a typed `workspace-instructions` source with a list of `{ action, scope, path, digest? }` changes; the complete startup or resume baseline also carries `baseline: true`. A matching durable `user/message` confirms a queued baseline and its candidate versions. An entering pre-step folds newly composed context into its final batch immediately after the claimed messages and removes the pending inbox copy; rejection keeps the current context queued. If a listener rewrites away a claimed workspace message without entering its replacement, a later boundary recomposes the current context. On every relevant tool touch, the plugin reconstructs loaded state from its visible session events and overlays a short in-memory pending window for context present on the immutable top-level `tools/result` but not yet appended by the loop. If the owning `step/end` arrives before a matching dynamic context reaches the log, the plugin clears that pending transition and its version fast path so the next successful touch can load it again. Nested Code Mode results stage pending changes under the outer execution token for same-run duplicate suppression; the outer result rolls that state back and recommits only contexts that survived outer policy.
An unchanged path and SHA-1 content digest is not injected again. A per-session, per-scope provider cache stores only `{ path, version, digest, trimmedDigest }`: when the provider's opaque `FsVersion` and the effective visible state both match, reconciliation skips the content read; a changed version triggers a bounded read and SHA-1 confirmation before any model-visible update. The `trimmedDigest` — SHA-1 over the whitespace-trimmed content — is the per-directory duplicate key, so an unchanged file can still be removed when an earlier candidate converges on its content. Resume works because SHA-1 state is persisted in the typed source, while an empty in-memory version cache merely causes one confirming read. Compaction re-arms a scope after its context event leaves the visible surface even when the cached version is unchanged. A removal is a tombstone, so a later candidate reappearance is loaded again. A model-visible change enters the source, pending state, and version cache only when its file-specific section retains at least one content byte, or when its original content is genuinely empty. Partial truncation records the complete-content digest once any content byte survives; truncation to zero remains eligible for a later touch, while a same-digest version refresh updates only the provider cache. A baseline may still publish its budget diagnostic with an empty change list. A dynamic batch with no committed change is not injected at all, and a later touch retries it.
The initial baseline event itself is not rewritten. Its typed changes remain authoritative only while that event is in the visible session surface; the next successful filesystem touch re-adds an unchanged baseline scope after compaction, or appends its replacement or removal. The in-memory scope marker and provider-version cache only select and accelerate probes. A hot plugin remount retains a baseline only when its typed event remains visible, while rebuilding current scope and version tracking; otherwise it injects a current baseline. A resumed loop always recomposes the current baseline and also reconciles still-visible dynamic scopes before its first request. There is no file watcher, so an on-disk change becomes visible at the next successful `read`, `write`, or `edit` touch, or when a resumed loop prepares its baseline.
The initial baseline event itself is not rewritten. Its typed changes remain authoritative only while that event is in the visible session surface; the next successful filesystem touch re-adds an unchanged baseline scope after compaction, or appends its replacement or removal. The in-memory scope marker and provider-version cache only select and accelerate probes. A hot plugin remount retains a baseline only when its typed event remains visible, while rebuilding current scope and version tracking; otherwise it queues a current baseline. A resumed loop always recomposes the current baseline and also reconciles still-visible dynamic scopes at its first pre-step; an entering first request records that context in the same step. There is no file watcher, so an on-disk change becomes visible at the next successful `read`, `write`, or `edit` touch, or when a resumed loop prepares its baseline.
## Configuration