Merge remote-tracking branch 'origin/master' into xtr/agent-loop-message-machine

# Conflicts:
#	.agents/notes/implemented/feature/2026-06-14-acp-agent-client-protocol.md
#	.agents/notes/implemented/feature/2026-06-14-acp-agent-client-protocol.zh.md
#	.agents/notes/implemented/feature/2026-06-30-hook-bridges.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-30-interception-seams.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-06-sandbox.md
#	.agents/notes/implemented/feature/2026-07-06-sandbox.zh.md
#	.agents/notes/implemented/feature/2026-07-19-model-facing-goal-tools.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-19-same-session-goal-round-driver.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-25-workspace-ui-product-flow.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-02-remove-stream-chunk-mirror.i18n.yaml
#	docs/architecture.i18n.yaml
#	docs/cookbook/adding-a-tool.i18n.yaml
#	docs/cookbook/extension-cookbook.i18n.yaml
#	docs/core-data-structures/llm-streaming.i18n.yaml
#	docs/core-data-structures/session.i18n.yaml
#	docs/core-data-structures/tools.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/persistence-catalog.md
#	examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/stdout.expected.jsonl
#	examples/acp-agent/tests/snapshots/escalation-approved/session.jsonl
#	examples/acp-agent/tests/snapshots/escalation-rejected/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-escalation-approved/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-pretool-ask/session.jsonl
#	examples/acp-agent/tests/snapshots/permission-switching/session.jsonl
#	examples/acp-agent/tests/snapshots/plan-mode-reject/session.jsonl
#	examples/acp-agent/tests/snapshots/plan-mode/session.jsonl
#	examples/acp-agent/tests/snapshots/session-sandbox-root/session.jsonl
#	packages/context/session-reference/README.md
#	packages/core/agent-loop/tests/agent.spec.ts
#	packages/hooks/hooks-claude/tests/coverage-cases.ts
#	packages/host/runtime/tests/host-runtime.spec.ts
#	packages/llm/llm-retry/tests/retry.spec.ts
#	packages/session-persistence/session-persistence/src/coordinator.ts
#	packages/support/acp-snapshot/README.md
#	packages/support/acp-snapshot/src/normalize.ts
#	packages/ui/acp/acp-feature-support.md
#	packages/ui/acp/src/codec.ts
#	packages/ui/acp/src/index.ts
#	packages/ui/acp/tests/bridge.spec.ts
#	packages/ui/acp/tests/codec.spec.ts
#	packages/ui/acp/tests/config-options.spec.ts
#	packages/ui/acp/tests/dispose.spec.ts
#	packages/ui/acp/tests/edges.spec.ts
#	packages/ui/acp/tests/stream-update.spec.ts
#	packages/ui/acp/tests/turns.spec.ts
This commit is contained in:
_Kerman
2026-07-26 14:05:33 +08:00
1062 changed files with 33621 additions and 30750 deletions

View File

@@ -412,6 +412,26 @@ A command was registered or unregistered. This is an unfiltered registry notific
Source: [`packages/ui/commands/src/index.ts:103`](../../packages/ui/commands/src/index.ts)
## `domain/*`
### `domain/changed` — emit
A domain record or the global singleton changed, emitted once per write strictly after the backend acknowledged durability. Events of one domain arrive in its write-chain order.
```ts cordis-catalog
/**
* A domain record or the global singleton changed, emitted once per write
* strictly after the backend acknowledged durability. Events of one
* domain arrive in its write-chain order.
* @param change - domain, table (`''` for global), key (`''` for global),
* operation discriminant, and on `put` the new snapshot.
* @mode emit
*/
'domain/changed'(change: DomainChanged): void
```
Source: [`packages/storage/storage-domain/src/events.ts:46`](../../packages/storage/storage-domain/src/events.ts)
## `fs/*`
### `fs/edit-intent` — waterfall

View File

@@ -319,6 +319,50 @@ Types: [DshEnvironment](../core-data-structures/bash.md) · [ToolExecution](../c
Source: [`packages/bash/tool-bash/src/index.ts:104`](../../packages/bash/tool-bash/src/index.ts)
## `ctx.clientModuleHost` — `ClientModuleHostService`
The web plugin table service: incremental dshClient scan + wire composition + bundle route + index tap. Construction runs the activation scan synchronously — a malformed declaration or missing bundle among the already-loaded entries aggregates into one loud throw (FAILED fiber; the boot sweep reports it).
```ts cordis-catalog
/**
* Current composed entry graph (stable object between changes).
* @returns the graph served as `window.__DSH_BOOT__`.
*/
graph(): WebBootGraph
/**
* Absolute path of an entry's client bundle.
* @param id - entry id (package name).
* @returns the path, or undefined for an unknown id.
*/
clientPath(id: string): string | undefined
/**
* Re-hash one bundle (the HMR watch's registration hook — the only entry
* point through which bundle content changes reach the graph).
* @param id - entry id (package name).
* @returns the new rev, or undefined for an unknown id.
*/
rebuilt(id: string): string | undefined
/**
* Subscribe to bundle rebuilds; fires only when the re-hash changed the rev.
* @param listener - receives the entry id and its new bundle rev.
* @returns the unsubscriber.
*/
onRebuilt(listener: (id: string, rev: string) => void): () => void
/**
* Fires after any flush that recomposed the graph (row added/removed, or a
* rebuilt rev change). Pull model: listeners re-read {@link graph}.
* @param listener - notified with no payload.
* @returns the unsubscriber.
*/
onGraphChanged(listener: () => void): () => void
```
Source: [`packages/client/modules/src/index.ts:143`](../../packages/client/modules/src/index.ts)
## `ctx.codeRuntime` — `CodeRuntime` (abstract seam)
Registers one `ctx.codeRuntime` implementation. Program, budget, abort, and substrate failures resolve in CodeRunResult; only seam misuse rejects. Implementations bridge structured-cloneable bindings, materialize each declared namespace rejection class, treat programs as hostile peers, isolate runs from one another, and terminate and await in-flight runs during disposal.
@@ -614,6 +658,30 @@ Types: [Agent](../core-data-structures/core.md) · [CreateGoalRequest](../core-d
Source: [`packages/goal/goal/src/index.ts:134`](../../packages/goal/goal/src/index.ts)
## `ctx.httpServer` — `HttpServerService`
The web-shape HTTP carrier service. Activation listens immediately (route registration order carries no request-facing semantics: named routes are composed to be disjoint, and the static dist fallback answers anything not yet claimed during the boot window). A listen failure throws out of init — a FAILED fiber the boot's fail-loud sweep reports.
```ts cordis-catalog
/**
* Register a named route. Duplicate (kind, path) throws — route patterns are
* a composition-level contract, so a collision is a misconfiguration.
* @param route - kind, path, and the owning handler.
* @returns the disposer removing the route.
*/
register(route: WebRoute): () => void
/**
* Register an index.html transform, applied to every index response in
* registration order.
* @param transform - pure html-to-html function.
* @returns the disposer removing the transform.
*/
tapIndex(transform: (html: string) => string): () => void
```
Source: [`packages/host/webserver/src/index.ts:55`](../../packages/host/webserver/src/index.ts)
## `ctx.invariants` — `InvariantService`
Package-owned invariant registry with global and regex-based selection.
@@ -759,7 +827,7 @@ set(agent: Agent, active: boolean): void
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/plan/plan-mode/src/index.ts:142`](../../packages/plan/plan-mode/src/index.ts)
Source: [`packages/plan/plan-mode/src/index.ts:141`](../../packages/plan/plan-mode/src/index.ts)
## `ctx.pty` — `PtyService`
@@ -941,15 +1009,17 @@ abstract load(id: SessionId): Promise<{ meta: SessionHeader; events: SessionEven
* This read is serialized with writes for the same id and returns detached
* values, so observers cannot mutate backend-owned state.
* @param id - the persisted session to inspect.
* @param signal - optional cancellation for queued and backend read work.
* @returns the header and valid stored event prefix exactly as observed.
*/
abstract inspect(id: SessionId): Promise<{ meta: SessionHeader; events: SessionEvent[] }>
abstract inspect(id: SessionId, signal?: AbortSignal): Promise<{ meta: SessionHeader; events: SessionEvent[] }>
/**
* Lightweight listing from metadata, without a full-log parse.
* @param signal - optional cancellation for backend listing work.
* @returns one header per materialized session.
*/
abstract list(): Promise<SessionHeader[]>
abstract list(signal?: AbortSignal): Promise<SessionHeader[]>
/**
* List materialized sessions with cheap per-log change tokens.
@@ -958,9 +1028,10 @@ abstract list(): Promise<SessionHeader[]>
* successful mutating {@link load} repair changes the next listed revision.
* Revisions also distinguish independently backed stores so backend-local
* counters cannot compare equal across different persistence sources.
* @param signal - optional cancellation for backend snapshot-listing work.
* @returns one header and opaque revision per materialized session without loading full logs.
*/
abstract listSnapshots(): Promise<SessionPersistenceSnapshot[]>
abstract listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot[]>
```
Types: [SessionEvent](../core-data-structures/core.md) · [SessionHeader](../core-data-structures/persistence.md) · [SessionId](../core-data-structures/core.md) · [SessionLocation](../core-data-structures/persistence.md) · [SessionPersistenceSnapshot](../core-data-structures/persistence.md)
@@ -986,15 +1057,16 @@ abstract searchSessions( request: SessionSearchRequest, exec?: SessionSearchExec
* Search events within one live-preferred logical session.
* @param request - target session, query text, filters, page size, and cursor.
* @param exec - optional cancellation control.
* @returns matching event hits in deterministic relevance order.
* @returns matching event hits and their target header from one indexed generation.
*/
abstract searchEvents( request: SessionEventSearchRequest, exec?: SessionSearchExecContext, ): Promise<SessionSearchPage<SessionEventSearchHit>>
abstract searchEvents( request: SessionEventSearchRequest, exec?: SessionSearchExecContext, ): Promise<SessionEventSearchPage>
/**
* List the complete logical corpus using live-preferred records.
* @param signal - optional cancellation for persistence listing.
* @returns deterministic newest-first cloned session records.
*/
listSessions(): Promise<SessionRecord[]>
listSessions(signal?: AbortSignal): Promise<SessionRecord[]>
/**
* Read and replay-validate one complete logical session log without making it live.
@@ -1007,16 +1079,37 @@ async readSession(sessionId: SessionId): Promise<SessionLogSnapshot>
/**
* Filter the complete logical corpus with provider-independent predicates.
* @param filters - ANDed session metadata and availability clauses.
* @param signal - optional cancellation for persistence listing.
* @returns matching cloned records in deterministic newest-first order.
*/
async filterSessions(filters: readonly SessionResultFilter[]): Promise<SessionRecord[]>
async filterSessions( filters: readonly SessionResultFilter[], signal?: AbortSignal, ): Promise<SessionRecord[]>
/**
* Fold the latest log-backed title from one live-preferred logical session.
* @param sessionId - live or persisted session id to read.
* @param signal - optional cancellation for source resolution and title folding.
* @returns latest title snapshot, or `undefined` when the log has no title event.
*/
async readTitle(sessionId: SessionId): Promise<SessionTitleSnapshot | undefined>
async readTitle( sessionId: SessionId, signal?: AbortSignal, ): Promise<SessionTitleSnapshot | undefined>
/**
* Fold the latest title and return its source header from one corpus observation.
* @param sessionId - live or persisted session id to read.
* @param signal - optional cancellation for source resolution and title folding.
* @returns cloned source header and optional latest title snapshot.
*/
async readTitleSnapshot( sessionId: SessionId, signal?: AbortSignal, ): Promise<SessionTitleObservation>
/**
* Fold titles for unique sessions from one cancellable corpus observation.
*
* Results preserve first-occurrence input order. Operational failures stay
* isolated per session, while cancellation rejects the complete operation.
* @param sessionIds - live or persisted session ids to observe.
* @param signal - optional cancellation shared by all source reads.
* @returns one fulfilled or rejected result per unique requested id.
*/
async readTitleSnapshots( sessionIds: readonly SessionId[], signal?: AbortSignal, ): Promise<SessionTitleObservationResult[]>
/**
* List lightweight raw-log event records for one logical session.
@@ -1044,30 +1137,33 @@ async readSurface(sessionId: SessionId): Promise<SessionSurfaceSnapshot>
/**
* Trace known ancestry and descendants from one corpus observation.
* @param sessionId - logical session id to trace.
* @param signal - optional cancellation for persistence listing.
* @returns a complete lineage or an explicit unresolved parent boundary.
* @throws when corpus resolution fails, the target is absent, or its known ancestry cycles.
*/
async traceSession(sessionId: SessionId): Promise<SessionLineageTrace>
async traceSession(sessionId: SessionId, signal?: AbortSignal): Promise<SessionLineageTrace>
/**
* Trace one event's direct positional and provenance relationships.
* @param request - target session id and event seq.
* @returns direct links plus the target's positional replacement chain.
* @param signal - optional cancellation for persisted source resolution.
* @returns source header, direct links, and the target's positional replacement chain.
* @throws when source resolution fails, the target is absent, or surface/provenance validation fails.
*/
async traceEvent(request: SessionEventTraceRequest): Promise<SessionEventTrace>
async traceEvent(request: SessionEventTraceRequest, signal?: AbortSignal): Promise<SessionEventTraceObservation>
/**
* Read one full event plus a bounded raw-log context window.
* @param request - target session/seq and context sizes.
* @param signal - optional cancellation for persisted source resolution.
* @returns cloned target and neighboring events.
*/
async readEvent(request: SessionEventReadRequest): Promise<SessionEventWindow>
async readEvent(request: SessionEventReadRequest, signal?: AbortSignal): Promise<SessionEventWindow>
```
Types: [SessionEventReadRequest](../core-data-structures/session-query.md) · [SessionEventRecord](../core-data-structures/session-query.md) · [SessionEventResultFilter](../core-data-structures/session-query.md) · [SessionEventSearchDocument](../core-data-structures/session-query.md) · [SessionEventSearchHit](../core-data-structures/session-query.md) · [SessionEventSearchRequest](../core-data-structures/session-query.md) · [SessionEventTrace](../core-data-structures/session-query.md) · [SessionEventTraceRequest](../core-data-structures/session-query.md) · [SessionEventWindow](../core-data-structures/session-query.md) · [SessionId](../core-data-structures/core.md) · [SessionLineageTrace](../core-data-structures/session-query.md) · [SessionLogSnapshot](../core-data-structures/session-query.md) · [SessionRecord](../core-data-structures/session-query.md) · [SessionResultFilter](../core-data-structures/session-query.md) · [SessionSearchExecContext](../core-data-structures/session-query.md) · [SessionSearchHit](../core-data-structures/session-query.md) · [SessionSearchPage](../core-data-structures/session-query.md) · [SessionSearchRequest](../core-data-structures/session-query.md) · [SessionSurfaceSnapshot](../core-data-structures/session-query.md) · [SessionTitleSnapshot](../core-data-structures/session-title.md)
Types: [SessionEventReadRequest](../core-data-structures/session-query.md) · [SessionEventRecord](../core-data-structures/session-query.md) · [SessionEventResultFilter](../core-data-structures/session-query.md) · [SessionEventSearchDocument](../core-data-structures/session-query.md) · [SessionEventSearchPage](../core-data-structures/session-query.md) · [SessionEventSearchRequest](../core-data-structures/session-query.md) · [SessionEventTraceObservation](../core-data-structures/session-query.md) · [SessionEventTraceRequest](../core-data-structures/session-query.md) · [SessionEventWindow](../core-data-structures/session-query.md) · [SessionId](../core-data-structures/core.md) · [SessionLineageTrace](../core-data-structures/session-query.md) · [SessionLogSnapshot](../core-data-structures/session-query.md) · [SessionRecord](../core-data-structures/session-query.md) · [SessionResultFilter](../core-data-structures/session-query.md) · [SessionSearchExecContext](../core-data-structures/session-query.md) · [SessionSearchHit](../core-data-structures/session-query.md) · [SessionSearchPage](../core-data-structures/session-query.md) · [SessionSearchRequest](../core-data-structures/session-query.md) · [SessionSurfaceSnapshot](../core-data-structures/session-query.md) · [SessionTitleObservation](../core-data-structures/session-query.md) · [SessionTitleObservationResult](../core-data-structures/session-query.md) · [SessionTitleSnapshot](../core-data-structures/session-title.md)
Source: [`packages/session-query/session-query/src/index.ts:74`](../../packages/session-query/session-query/src/index.ts)
Source: [`packages/session-query/session-query/src/index.ts:81`](../../packages/session-query/session-query/src/index.ts)
## `ctx.sessionReferences` — `SessionReferenceService`
@@ -1353,6 +1449,73 @@ Types: [SaveTextSpill](../core-data-structures/spill.md) · [SpillRef](../core-d
Source: [`packages/spill/spill/src/index.ts:45`](../../packages/spill/spill/src/index.ts)
## `ctx.storage` — `Storage`
The storage hub service. Backends register under `backend`; data forms mount under their `StorageForms` key and are reached as `ctx.storage.<form>`.
```ts cordis-catalog
/**
* Mount a data-form facility on the hub. Mounting is an effect: the
* returned disposer unmounts the form.
* @param form - Form key declared in {@link StorageForms}.
* @param facility - The facility instance to expose.
* @returns the disposer that unmounts the form.
*/
mount<K extends keyof StorageForms>(form: K, facility: StorageForms[K]): () => void
/**
* Resolve a mounted data form.
* @param form - Form key declared in {@link StorageForms}.
* @returns the mounted facility.
*/
form<K extends keyof StorageForms>(form: K): StorageForms[K]
```
Source: [`packages/storage/storage/src/index.ts:47`](../../packages/storage/storage/src/index.ts)
## `ctx.storageDomain` — `DomainFacility`
The mounted domain facility. Opens declared domains over routed backends; one facility instance owns the open-domain table and enforces single-open per domain name.
```ts cordis-catalog
/**
* Open one declared domain. Steps, each failing the whole call: reject a
* name that is already open (`already-open`); resolve the backend route
* (`backend-not-found` passes through from the hub); require its `kv` facet
* (`facet-unsupported`); open the unit projected from the spec (backend
* `version-mismatch`/`malformed-medium` pass through); load and validate
* every stored record against the spec's zod schemas (`invalid-record`
* with the offending table and key); construct the domain.
*
* Lifecycle: the CALLER owns the returned handle and closes it via
* `Domain.close()` (typically as its own `ctx.effect` disposer) — the
* facility does not tie the domain to any consumer fiber. Domains still
* open when the facility unmounts are closed by the plugin disposer.
* @param spec - The domain declaration, typically from `defineDomain`.
* @returns the opened domain handle, typed by the spec.
*/
async open<S extends DomainSpec>(spec: S): Promise<Domain<S>>
/**
* Look up an open domain by name, untyped. Diagnostic surface (the package
* invariant cross-checks change events against live domain state); typed
* consumers hold the handle returned by {@link open}.
* @param name - Domain name.
* @returns the open domain runtime, or `undefined` when not open.
*/
get(name: string): DomainImpl | undefined
/**
* Close every domain still open on this facility. The unmount path for
* consumers that never called `Domain.close()` themselves; closing is
* idempotent, so double-closing an already-closed domain is harmless.
* @returns resolution after every unit is released.
*/
async closeAll(): Promise<void>
```
Source: [`packages/storage/storage-domain/src/index.ts:69`](../../packages/storage/storage-domain/src/index.ts)
## `ctx.subagents` — `SubagentService`
Named provider registry and capability-checked start surface.
@@ -1813,6 +1976,62 @@ Types: [WorkflowRun](../core-data-structures/workflow.md) · [WorkflowStartReque
Source: [`packages/workflow/workflow/src/index.ts:159`](../../packages/workflow/workflow/src/index.ts)
## `ctx.workspace` — `WorkspaceRegistry`
Durable workspace registry. Startup waits for `sessionPersistence`, builds one canonical-cwd header index, and completes the one-time history bootstrap before the service becomes active. The persistence dependency is mandatory so an unavailable peer can never be mistaken for an empty history and commit the initialized marker.
```ts cordis-catalog
/**
* Create or reuse a workspace for an existing directory. The path is
* canonicalized through `fs.realpath`; a nonexistent path rejects with the
* original error and a non-directory rejects. Repeated calls for the same
* canonical path return the existing entity without changing its title.
* A newly created workspace is prepended to the durable registry order.
* A different canonical path cannot create a duplicate display title.
* @param path - Existing directory to own, in any path spelling.
* @param title - Display title used only when a new record is created.
* @returns the existing or newly durable workspace.
*/
async create(path: string, title?: string): Promise<Workspace>
/**
* Look up a workspace by id.
* @param id - Workspace id.
* @returns the workspace, or `undefined` when unknown.
*/
get(id: WorkspaceId): Workspace | undefined
/**
* Synchronous workspace projection in durable registry order. Every
* entity's `sessionIds` getter is already filtered by the startup/live
* canonical-cwd header index; this method performs no persistence reads.
* @returns a fresh ordered array of workspace entities.
*/
list(): Workspace[]
/**
* Move one accounted, cwd-validated session to the front of its workspace.
* Ungrouped sessions and candidates filtered by the header check are
* no-ops. The owning workspace's relative position never changes.
* @param sessionId - Session whose activity was observed.
* @returns resolution after the possible record write.
*/
async touchSession(sessionId: SessionId): Promise<void>
/**
* Resolve by canonical directory path without creating or mutating a
* workspace. A missing path rejects during `realpath`; an existing unowned
* directory returns `undefined`.
* @param path - Existing directory path in any spelling.
* @returns the workspace owning the canonical path, when one exists.
*/
async resolveByPath(path: string): Promise<Workspace | undefined>
```
Types: [SessionId](../core-data-structures/core.md)
Source: [`packages/workspace/workspace/src/index.ts:75`](../../packages/workspace/workspace/src/index.ts)
## Inherited `ctx` members (cordis core + loader/hmr/timer)
The framework `ctx` surface every plugin also sees, beyond the harness services above. This is pinned vendor source ([vendoring policy](../../vendor/README.md)); it is summarized here so the page is a complete picture of what `ctx` offers, without elevating framework internals to the harness tier's prominence.