|
|
@@ -1,21 +1,19 @@
|
|
|
<!-- Generated by scripts/gen-cordis-catalog.ts — do not edit by hand.
|
|
|
Run `pnpm run gen-cordis-catalog` to regenerate. -->
|
|
|
|
|
|
-# Cordis Events & Services Catalog
|
|
|
+# Cordis Events Catalog
|
|
|
|
|
|
-An index reference to the **wiring** a plugin author works against: every cordis event you can listen to (exact signature + dispatch mode) and every `ctx.<key>` service you can call (exact public interface). It complements [core-data-structures/](../core-data-structures/core.md), which catalogs the *data structures* these signatures move around — this page is the verbs, that page is the nouns.
|
|
|
+Every cordis event a plugin can listen to: exact signature, dispatch mode, and the declaration's JSDoc. This is one axis of the **wiring** reference a plugin author works against — the callable `ctx.<key>` surface is the sibling [services catalog](services.md), and [core-data-structures/](../core-data-structures/core.md) catalogs the *data structures* these signatures move around.
|
|
|
|
|
|
This file is GENERATED from source (`scripts/gen-cordis-catalog.ts`) and verified fresh by `pnpm run verify-cordis-catalog` (part of `doc-sync`) — do not edit it by hand. Signature blocks use a `ts cordis-catalog` fence (skipped by doc-typecheck, since a bare signature is not standalone-compilable). Type names in a signature link to the page that documents them.
|
|
|
|
|
|
-The **harness tier** below (the `@deepseek-ai/dsh-*` packages) is the vocabulary this repo owns. The **inherited tier** at the end is the cordis-core + loader/hmr/timer surface a plugin also sees — pinned vendor source, summarized tersely.
|
|
|
-
|
|
|
-## Events
|
|
|
+The **harness tier** below (the `@deepseek-ai/dsh-*` packages) is the vocabulary this repo owns, grouped by scope. The **inherited tier** at the end is the cordis-core + loader/hmr/timer event surface a plugin also sees — pinned vendor source, summarized tersely.
|
|
|
|
|
|
Dispatch modes: **emit** (fire-and-forget), **waterfall** (each listener gets `next()` and may transform or veto — see [waterfall semantics](../architecture.md#cordis-waterfall-semantics-important)), **parallel** (awaited fan-out; all listeners run), **serial** (awaited in registration order until one returns a bail value — anything other than `null`, `false`, or `undefined`).
|
|
|
|
|
|
-### `agent/*`
|
|
|
+## `agent/*`
|
|
|
|
|
|
-#### `agent/created` — emit
|
|
|
+### `agent/created` — emit
|
|
|
|
|
|
An agent was registered in the AgentRegistry and is ready to receive messages.
|
|
|
|
|
|
@@ -27,7 +25,7 @@ Types: [Agent](../core-data-structures/core.md)
|
|
|
|
|
|
Source: [`packages/core/agent/src/types.ts:234`](../../packages/core/agent/src/types.ts)
|
|
|
|
|
|
-#### `agent/disposed` — emit
|
|
|
+### `agent/disposed` — emit
|
|
|
|
|
|
An agent was disposed and removed from the registry; its fiber and any in-flight turn have been torn down.
|
|
|
|
|
|
@@ -39,7 +37,7 @@ Types: [Agent](../core-data-structures/core.md)
|
|
|
|
|
|
Source: [`packages/core/agent/src/types.ts:241`](../../packages/core/agent/src/types.ts)
|
|
|
|
|
|
-#### `agent/error` — emit
|
|
|
+### `agent/error` — emit
|
|
|
|
|
|
A step or turn errored. The loop reports a failure here (plus the logger) even when the error has no in-turn position for a session `error` event.
|
|
|
|
|
|
@@ -51,7 +49,7 @@ Types: [Agent](../core-data-structures/core.md)
|
|
|
|
|
|
Source: [`packages/core/agent/src/types.ts:380`](../../packages/core/agent/src/types.ts)
|
|
|
|
|
|
-#### `agent/pre-step` — serial
|
|
|
+### `agent/pre-step` — serial
|
|
|
|
|
|
Awaited pre-step surface-mutation checkpoint, fired once per step AFTER `turn/start` (and after the prior step closed) but BEFORE this step's `step/start` — so anything a listener appends lands OUTSIDE the step, between `turn/start`/`step/end` and the upcoming `step/start`. `step` is the number of the step about to start. The loop awaits `ctx.serial('agent/pre-step', …)` after assembling the system prompt, then opens the step and derives the request history ONCE from whatever the surface now holds. This is where compaction belongs: it mutates the session surface in place (shadowing an older range with a summary node) with its log-only `compact/*` records cleanly outside any step, and the single subsequent derive reflects the mutation — so there is no double-derive and no listener can see (or be expected to act on) an assembled `messages` array that does not exist yet.
|
|
|
|
|
|
@@ -65,7 +63,7 @@ Types: [Agent](../core-data-structures/core.md)
|
|
|
|
|
|
Source: [`packages/core/agent/src/types.ts:319`](../../packages/core/agent/src/types.ts)
|
|
|
|
|
|
-#### `agent/prompt-submit` — waterfall
|
|
|
+### `agent/prompt-submit` — waterfall
|
|
|
|
|
|
Waterfall: decide what happens to ONE drained queued message before it becomes a `user/message` — allow (optionally rewriting the prompt bytes or attaching `additionalContext`) or block it. Fires inside the already-open turn, per drained message. Maps onto Claude Code's `UserPromptSubmit` hook. Call `next()` to delegate to the default (allow unchanged), or return a PromptDecision without calling `next()` to short-circuit.
|
|
|
|
|
|
@@ -77,7 +75,7 @@ Types: [Agent](../core-data-structures/core.md) · [ContentBlock](../core-data-s
|
|
|
|
|
|
Source: [`packages/core/agent/src/types.ts:332`](../../packages/core/agent/src/types.ts)
|
|
|
|
|
|
-#### `agent/queued` — emit
|
|
|
+### `agent/queued` — emit
|
|
|
|
|
|
A message entered the agent's inbox (queued or steering). `source` is the resolved source (defaults applied), not the caller's raw options.
|
|
|
|
|
|
@@ -89,7 +87,7 @@ Types: [Agent](../core-data-structures/core.md) · [ContentBlock](../core-data-s
|
|
|
|
|
|
Source: [`packages/core/agent/src/types.ts:259`](../../packages/core/agent/src/types.ts)
|
|
|
|
|
|
-#### `agent/request` — waterfall
|
|
|
+### `agent/request` — waterfall
|
|
|
|
|
|
Waterfall: mutate the fully-assembled GenerateOptions before the model call (hooks, model switching, tool filtering, …). Call `next()` to delegate, or return without it to short-circuit. For surface mutation that must precede history derivation (compaction), use agent/pre-step instead — by the time this fires, `options.messages` is already derived.
|
|
|
|
|
|
@@ -101,7 +99,7 @@ Types: [Agent](../core-data-structures/core.md) · [GenerateOptions](../core-dat
|
|
|
|
|
|
Source: [`packages/core/agent/src/types.ts:345`](../../packages/core/agent/src/types.ts)
|
|
|
|
|
|
-#### `agent/session-start` — emit
|
|
|
+### `agent/session-start` — emit
|
|
|
|
|
|
The agent's session lifecycle began, fired once before its first turn. `source` says why (SessionStartSource: fresh startup, a resumed persisted session, …). A pure NOTIFICATION (emit, not waterfall): it carries no veto — a session-start listener that wants to seed context does so via `agent.inject()` (a `context/message` the first request sees), not by returning a decision. Cannot block the session from starting; that gap is deliberate (a bridge logs/injects, it does not gate startup).
|
|
|
|
|
|
@@ -113,7 +111,7 @@ Types: [Agent](../core-data-structures/core.md)
|
|
|
|
|
|
Source: [`packages/core/agent/src/types.ts:274`](../../packages/core/agent/src/types.ts)
|
|
|
|
|
|
-#### `agent/status` — emit
|
|
|
+### `agent/status` — emit
|
|
|
|
|
|
Agent status changed (`idle` ⇄ `running`, or → `disposed`). Drive lifecycle off this transition, never off a status you just requested — `send()` does not flip status to `running` before it returns.
|
|
|
|
|
|
@@ -125,7 +123,7 @@ Types: [Agent](../core-data-structures/core.md)
|
|
|
|
|
|
Source: [`packages/core/agent/src/types.ts:250`](../../packages/core/agent/src/types.ts)
|
|
|
|
|
|
-#### `agent/step-result` — waterfall
|
|
|
+### `agent/step-result` — waterfall
|
|
|
|
|
|
Waterfall: post-process the assembled assistant Message before tool dispatch (validation, content rewriting, …).
|
|
|
|
|
|
@@ -137,7 +135,7 @@ Types: [Agent](../core-data-structures/core.md) · [Message](../core-data-struct
|
|
|
|
|
|
Source: [`packages/core/agent/src/types.ts:355`](../../packages/core/agent/src/types.ts)
|
|
|
|
|
|
-#### `agent/turn-continuation` — waterfall
|
|
|
+### `agent/turn-continuation` — waterfall
|
|
|
|
|
|
Waterfall: override the turn-continuation decision via a typed ContinuationDecision. The loop's `defaultDecision` is `continue` when the step had tool calls or steering was injected, else `stop`. Listeners force-continue (`/goal`, `/loop` — optionally attaching a `reason` recorded as next-step steering) or force-stop (budget guards). Call `next()` to delegate to the default, or return a decision to override.
|
|
|
|
|
|
@@ -149,9 +147,9 @@ Types: [Agent](../core-data-structures/core.md)
|
|
|
|
|
|
Source: [`packages/core/agent/src/types.ts:368`](../../packages/core/agent/src/types.ts)
|
|
|
|
|
|
-### `fs/*`
|
|
|
+## `fs/*`
|
|
|
|
|
|
-#### `fs/edit-intent` — waterfall
|
|
|
+### `fs/edit-intent` — waterfall
|
|
|
|
|
|
Single-slot decision: produce the optional version guard for the next FileSystem.editText. The tool dispatches this as an unbound waterfall and supplies a default thunk returning `undefined` (unconditional edit of the current content — the bare provider; no `stat`). The `@deepseek-ai/dsh-fs-policy` policy listener returns `{ version: vObserved }`, or throws `FS_NOT_OBSERVED` if the actor is unset or has not observed the target. Does NOT call `next()`: one decision, first-wins (see Events.'fs/write-intent').
|
|
|
|
|
|
@@ -163,7 +161,7 @@ Types: [FsTarget](../core-data-structures/filesystem.md) · [FsVersion](../core-
|
|
|
|
|
|
Source: [`packages/fs/fs/src/index.ts:123`](../../packages/fs/fs/src/index.ts)
|
|
|
|
|
|
-#### `fs/observed` — emit
|
|
|
+### `fs/observed` — emit
|
|
|
|
|
|
Record that an actor observed a target at a version, after a successful read/write/edit. Fire-and-forget (plain `emit`). A listener MUST be a synchronous, side-effect-only recorder (`@deepseek-ai/dsh-fs-policy`'s is a `WeakMap.set`): the tool does not guard the emit, so a listener that throws surfaces as the tool's `isError` result, and cordis `emit` does not await listener promises — async or fallible audit/telemetry does not belong here. No listener ⇒ nothing recorded. `actor` is the opaque tool-execution context.
|
|
|
|
|
|
@@ -175,7 +173,7 @@ Types: [FsTarget](../core-data-structures/filesystem.md) · [FsVersion](../core-
|
|
|
|
|
|
Source: [`packages/fs/fs/src/index.ts:138`](../../packages/fs/fs/src/index.ts)
|
|
|
|
|
|
-#### `fs/write-intent` — waterfall
|
|
|
+### `fs/write-intent` — waterfall
|
|
|
|
|
|
Single-slot decision: produce the write intent for the next FileSystem.writeText. The tool dispatches this as an unbound waterfall (no `this`) and supplies a default thunk returning `undefined` (unconditional create-or-overwrite — the bare provider). The `@deepseek-ai/dsh-fs-policy` policy listener returns `createIfAbsent` (unobserved actor) or `{ kind: 'replaceIfVersion', version: vObserved }` (observed) and does NOT call `next()` — one decision, not a composable chain. The slot is first-wins: the first non-`next()` decider (registration order, or `prepend`) occupies it; a second decider is a misconfiguration, not layering. `actor` is the opaque tool-execution context, never read here.
|
|
|
|
|
|
@@ -187,9 +185,9 @@ Types: [FsTarget](../core-data-structures/filesystem.md) · [FsWriteIntent](../c
|
|
|
|
|
|
Source: [`packages/fs/fs/src/index.ts:109`](../../packages/fs/fs/src/index.ts)
|
|
|
|
|
|
-### `llm/*`
|
|
|
+## `llm/*`
|
|
|
|
|
|
-#### `llm/stream` — waterfall
|
|
|
+### `llm/stream` — waterfall
|
|
|
|
|
|
Waterfall around every streaming model call (retry, caching, routing). Bound to the LlmService; call `next()` to reach the resolved adapter's stream, or yield your own chunks to short-circuit.
|
|
|
|
|
|
@@ -201,9 +199,9 @@ Types: [GenerateOptions](../core-data-structures/core.md) · [StreamChunk](../co
|
|
|
|
|
|
Source: [`packages/llm/llm/src/index.ts:33`](../../packages/llm/llm/src/index.ts)
|
|
|
|
|
|
-### `session/*`
|
|
|
+## `session/*`
|
|
|
|
|
|
-#### `session/created` — emit
|
|
|
+### `session/created` — emit
|
|
|
|
|
|
A session was created in the store.
|
|
|
|
|
|
@@ -213,7 +211,7 @@ A session was created in the store.
|
|
|
|
|
|
Source: [`packages/core/session/src/index.ts:36`](../../packages/core/session/src/index.ts)
|
|
|
|
|
|
-#### `session/event` — emit
|
|
|
+### `session/event` — emit
|
|
|
|
|
|
An event was appended to a session log (sync, fire-and-forget). This is the per-append feed a UI or invariant plugin tails.
|
|
|
|
|
|
@@ -225,7 +223,7 @@ Types: [SessionEvent](../core-data-structures/core.md)
|
|
|
|
|
|
Source: [`packages/core/session/src/index.ts:44`](../../packages/core/session/src/index.ts)
|
|
|
|
|
|
-#### `session/flush` — parallel
|
|
|
+### `session/flush` — parallel
|
|
|
|
|
|
Awaited durability checkpoint. The agent loop awaits `ctx.parallel('session/flush', session)` at every turn end; persistence plugins (JSONL, SQLite) drain their write-behind buffers here and on fiber dispose. Awaited (parallel), not a waterfall: every listener runs and the loop waits for all of them, but none can veto.
|
|
|
|
|
|
@@ -235,9 +233,9 @@ Awaited durability checkpoint. The agent loop awaits `ctx.parallel('session/flus
|
|
|
|
|
|
Source: [`packages/core/session/src/index.ts:54`](../../packages/core/session/src/index.ts)
|
|
|
|
|
|
-### `subagent/*`
|
|
|
+## `subagent/*`
|
|
|
|
|
|
-#### `subagent/end` — emit
|
|
|
+### `subagent/end` — emit
|
|
|
|
|
|
A subagent run settled — emitted when SubagentRun.result resolves (any stop reason). Paired with Events['subagent/start'].
|
|
|
|
|
|
@@ -247,7 +245,7 @@ A subagent run settled — emitted when SubagentRun.result resolves (any stop re
|
|
|
|
|
|
Source: [`packages/subagent/subagent/src/index.ts:77`](../../packages/subagent/subagent/src/index.ts)
|
|
|
|
|
|
-#### `subagent/start` — emit
|
|
|
+### `subagent/start` — emit
|
|
|
|
|
|
A subagent run started — emitted after the provider is resolved and its capabilities validated, as the child run begins. Paired with Events['subagent/end'].
|
|
|
|
|
|
@@ -257,9 +255,9 @@ A subagent run started — emitted after the provider is resolved and its capabi
|
|
|
|
|
|
Source: [`packages/subagent/subagent/src/index.ts:70`](../../packages/subagent/subagent/src/index.ts)
|
|
|
|
|
|
-### `system-prompt/*`
|
|
|
+## `system-prompt/*`
|
|
|
|
|
|
-#### `system-prompt/assemble` — waterfall
|
|
|
+### `system-prompt/assemble` — waterfall
|
|
|
|
|
|
Waterfall around prompt assembly — mutate or extend the PromptAssembly (sections + tool schemas) before it is rendered. Bound to the SystemPrompt service; call `next()` to delegate.
|
|
|
|
|
|
@@ -269,7 +267,7 @@ Waterfall around prompt assembly — mutate or extend the PromptAssembly (sectio
|
|
|
|
|
|
Source: [`packages/core/system-prompt/src/index.ts:26`](../../packages/core/system-prompt/src/index.ts)
|
|
|
|
|
|
-#### `system-prompt/change` — emit
|
|
|
+### `system-prompt/change` — emit
|
|
|
|
|
|
A section or tool provider was registered or unregistered (the assembly inputs changed).
|
|
|
|
|
|
@@ -279,9 +277,9 @@ A section or tool provider was registered or unregistered (the assembly inputs c
|
|
|
|
|
|
Source: [`packages/core/system-prompt/src/index.ts:32`](../../packages/core/system-prompt/src/index.ts)
|
|
|
|
|
|
-### `tools/*`
|
|
|
+## `tools/*`
|
|
|
|
|
|
-#### `tools/change` — emit
|
|
|
+### `tools/change` — emit
|
|
|
|
|
|
A tool was registered or unregistered (the available tool set changed).
|
|
|
|
|
|
@@ -291,7 +289,7 @@ A tool was registered or unregistered (the available tool set changed).
|
|
|
|
|
|
Source: [`packages/core/tools/src/index.ts:87`](../../packages/core/tools/src/index.ts)
|
|
|
|
|
|
-#### `tools/post-execute` — waterfall
|
|
|
+### `tools/post-execute` — waterfall
|
|
|
|
|
|
Waterfall AFTER a tool runs — where hook plugins inspect the result and accept it (optionally REPLACING the model-facing content, and/or attaching `additionalContext` for the next request) or block it with corrective `feedback` (Claude Code's `PostToolUse`). Listeners receive `(exec, result, next)`: call `next()` to delegate to the default (accept unchanged), or return a PostToolDecision to override. The core tool dispatch sits between the two waterfalls as plain code, all inside `execute`'s outer try/catch (and the tool body keeps its own inner try/catch, so a thrown tool still reaches `post-execute` as an `isError` result).
|
|
|
|
|
|
@@ -303,7 +301,7 @@ Types: [ToolExecution](../core-data-structures/tools.md) · [ToolExecutionResult
|
|
|
|
|
|
Source: [`packages/core/tools/src/index.ts:82`](../../packages/core/tools/src/index.ts)
|
|
|
|
|
|
-#### `tools/pre-execute` — waterfall
|
|
|
+### `tools/pre-execute` — waterfall
|
|
|
|
|
|
Waterfall BEFORE a tool runs — the gate where sandbox, permission, and hook plugins allow or deny a call (Claude Code's `PreToolUse`). Listeners receive `(exec, next)`: call `next()` to delegate to the default (allow), or return a PreToolDecision without calling `next()` to short-circuit. A `deny` skips dispatch and yields an `isError` result; the tool body never runs. Input rewrite is deliberately NOT offered here (see PreToolDecision); `ask` degrades to deny until the permission system lands (`FIXME(permissions)`).
|
|
|
|
|
|
@@ -315,233 +313,9 @@ Types: [ToolExecution](../core-data-structures/tools.md)
|
|
|
|
|
|
Source: [`packages/core/tools/src/index.ts:66`](../../packages/core/tools/src/index.ts)
|
|
|
|
|
|
-## Services
|
|
|
-
|
|
|
-The `ctx.<key>` services the harness provides. An abstract seam (e.g. `ctx.bash`) is implemented by a separate package; the interface is what consumers code against.
|
|
|
-
|
|
|
-### `ctx.agentLoop` — `AgentLoop`
|
|
|
-
|
|
|
-The agent-loop plugin (`ctx.agentLoop`): creates ReactLoopAgents, runs their loops, and registers them in `ctx.agents`. Also implements the AgentFactory seam, so plugins create/resume agents through `ctx.agents` (the interface) without depending on this concrete package.
|
|
|
-
|
|
|
-The loop itself is deliberately thin — every behavior beyond "call the model, run the tools, repeat" belongs to plugins listening on the event taxonomy declared in @deepseek-ai/dsh-agent.
|
|
|
-
|
|
|
-```ts cordis-catalog
|
|
|
-create(id: AgentId, options: AgentOptions = {}): ReactLoopAgent
|
|
|
-createAgent(options: CreateAgentOptions): AgentHandle
|
|
|
-async resume(options: ResumeAgentOptions): Promise<AgentHandle>
|
|
|
-```
|
|
|
-
|
|
|
-Source: [`packages/core/agent-loop/src/index.ts:63`](../../packages/core/agent-loop/src/index.ts)
|
|
|
-
|
|
|
-### `ctx.agents` — `AgentRegistry`
|
|
|
-
|
|
|
-Agent registry (`ctx.agents`): tracks live agents so UI, hook, and orchestrator plugins can find them without depending on the concrete loop package. Agent *creation* is provided by whichever plugin implements the AgentFactory (phase 1: `@deepseek-ai/dsh-agent-loop`), registered via setFactory.
|
|
|
-
|
|
|
-```ts cordis-catalog
|
|
|
-setFactory(factory: AgentFactory): () => void
|
|
|
-create(options: CreateAgentOptions): AgentHandle
|
|
|
-async resume(options: ResumeAgentOptions): Promise<AgentHandle>
|
|
|
-register(agent: Agent): () => void
|
|
|
-get(id: AgentId): Agent | undefined
|
|
|
-list(): Agent[]
|
|
|
-```
|
|
|
-
|
|
|
-Types: [Agent](../core-data-structures/core.md)
|
|
|
-
|
|
|
-Source: [`packages/core/agent/src/index.ts:117`](../../packages/core/agent/src/index.ts)
|
|
|
-
|
|
|
-### `ctx.bash` — `BashExecutor` (abstract seam)
|
|
|
-
|
|
|
-Abstract bash execution service. Subclass, implement the abstract methods, and load the subclass as a plugin — it registers as `ctx.bash` (one implementation per context; loading a second throws, which is cordis' standard duplicate-service behavior).
|
|
|
-
|
|
|
-Semantics every implementation must honor:
|
|
|
-
|
|
|
-- run REJECTS only for infrastructure failures (unusable workdir, missing shell, pre-aborted signal). Nonzero exits, timeout kills, and abort kills RESOLVE with a descriptive BashRunResult — reporting a failed command is the tool layer's job, not an exception.
|
|
|
-- start returns immediately; no timeout applies to background tasks (callers stop them via kill or the spec's AbortSignal). Completion must fire the onTaskDone listeners exactly once per task, and must NOT fire after the service is disposed.
|
|
|
-- readOutput is incremental: consecutive reads never re-deliver output. Implementations bound their buffers; reads that lost data flag `lossy` and point at full-stream spill files when available.
|
|
|
-- Disposal kills every running task and awaits their exit (no orphan processes survive `fiber.dispose()`).
|
|
|
-
|
|
|
-```ts cordis-catalog
|
|
|
-abstract resolve(request: BashExecRequest): BashExecSpec
|
|
|
-abstract run(spec: BashExecSpec): Promise<BashRunResult>
|
|
|
-abstract start(spec: BashExecSpec): BashTask
|
|
|
-abstract get(id: BashTaskId): BashTask | undefined
|
|
|
-abstract ownerOf(id: BashTaskId): OwnerToken | undefined
|
|
|
-abstract list(): BashTask[]
|
|
|
-abstract readOutput(id: BashTaskId): BashTaskRead
|
|
|
-abstract kill(id: BashTaskId): boolean
|
|
|
-onTaskDone(listener: BashTaskListener): () => void
|
|
|
-```
|
|
|
-
|
|
|
-Types: [BashExecRequest](../core-data-structures/bash.md) · [BashExecSpec](../core-data-structures/bash.md) · [BashRunResult](../core-data-structures/bash.md) · [BashTask](../core-data-structures/bash.md) · [BashTaskRead](../core-data-structures/bash.md)
|
|
|
+## Inherited events (cordis core + loader/hmr/timer)
|
|
|
|
|
|
-Source: [`packages/bash/bash/src/index.ts:59`](../../packages/bash/bash/src/index.ts)
|
|
|
-
|
|
|
-### `ctx.compact` — `CompactService` (abstract seam)
|
|
|
-
|
|
|
-Abstract compaction service. Subclass implement the two abstract methods, and load the subclass as a plugin — it registers as `ctx.compact` (one implementation per context; loading a second throws, which is cordis' standard duplicate-service behavior).
|
|
|
-
|
|
|
-Both core methods are abstract: the contract states WHAT compaction does, while the entire strategy — token estimation, retention policy, event sequencing, summarization — is a HOW decision owned by the implementation.
|
|
|
-
|
|
|
-Implementations MUST honor:
|
|
|
-
|
|
|
-- **Surface contract**: a successful compaction shadows the compacted surface nodes with a SINGLE replacement node carrying the summary. Because `SurfaceEventType` is a closed union, that node is a `user/message` with `surfaceOp: { op:'replace', start, end }`; the `compact/*` events are log-only (lock + provenance).
|
|
|
-- **Blocking**: no compaction begins while another is in progress for the same session. The recommended mechanism is the log-recorded lock — append `compact/start` before the slow work and `compact/end` after (even on failure) — so the lock is visible to replay and crash recovery.
|
|
|
-
|
|
|
-```ts cordis-catalog
|
|
|
-abstract compactIfNeeded( agent: CompactAgentContext, turn: number, step: number, fullSystemPrompt: string, signal: AbortSignal, ): Promise<CompactionResult | null>
|
|
|
-abstract compactRegion( session: Session, start: number, end: number, agent: CompactAgentContext, turn: number, step: number, signal?: AbortSignal, ): Promise<CompactionResult>
|
|
|
-```
|
|
|
-
|
|
|
-Source: [`packages/compact/compact/src/index.ts:63`](../../packages/compact/compact/src/index.ts)
|
|
|
-
|
|
|
-### `ctx.fs` — `FileSystem` (abstract seam)
|
|
|
-
|
|
|
-Abstract filesystem provider service. Subclass, implement the seven storage primitives, and load the subclass as a plugin — it registers as `ctx.fs` (one implementation per context; loading a second throws, cordis' standard duplicate-service behavior).
|
|
|
-
|
|
|
-Semantics every backend must honor:
|
|
|
-
|
|
|
-- resolve returns a stable FsTarget; the same underlying file reached by different input paths must yield the same `targetKey` so stale guards and target lookup agree across paths (e.g. through symlinks).
|
|
|
-- stat returns FsInfo metadata (never content) or `undefined` when the target is absent.
|
|
|
-- readText/streamText read the whole regular text file (the stream for large files); both own regular-file checks, UTF-8 decoding, binary/NUL rejection, and `FS_NOT_TEXT`.
|
|
|
-- listDir returns direct children of a directory in stable name order with resolved child targets and cheap metadata only. It never reads file contents. Missing targets throw `FS_NOT_FOUND`, non-directories throw `FS_NOT_DIRECTORY`, permission failures throw `FS_PERMISSION_DENIED`, and other backend I/O failures throw `FS_IO_ERROR`.
|
|
|
-- writeText is atomic temp-file + rename. `expected` is OPTIONAL: omit it for an unconditional create-or-overwrite (the bare-provider default), or supply a FsWriteIntent to guard the write.
|
|
|
-- editText verifies `expected.version` BEFORE literal matching (so a stale edit reports `FS_STALE_VERSION`, not `FS_EDIT_NOT_FOUND`/ `FS_AMBIGUOUS_EDIT` against newer content), then applies literal replacement and writes atomically — all inside one mutation critical section. `expected` is OPTIONAL: omit it for an unconditional edit of the current content (a missing target still reports `FS_STALE_VERSION`).
|
|
|
-
|
|
|
-```ts cordis-catalog
|
|
|
-abstract resolve(path: string, opts?: { cwd?: string }): Promise<FsTarget>
|
|
|
-abstract stat(target: FsTarget, signal?: AbortSignal): Promise<FsInfo | undefined>
|
|
|
-abstract readText(target: FsTarget, signal?: AbortSignal): Promise<string>
|
|
|
-abstract streamText(target: FsTarget, signal?: AbortSignal): Promise<AsyncIterable<string>>
|
|
|
-abstract listDir(target: FsTarget, signal?: AbortSignal): Promise<FsDirEntry[]>
|
|
|
-abstract writeText(target: FsTarget, content: string, expected?: FsWriteIntent, signal?: AbortSignal): Promise<FsWriteOutcome>
|
|
|
-abstract editText(target: FsTarget, edit: FsEditRequest, expected?: { version: FsVersion }, signal?: AbortSignal): Promise<FsEditOutcome>
|
|
|
-```
|
|
|
-
|
|
|
-Types: [FsEditOutcome](../core-data-structures/filesystem.md) · [FsEditRequest](../core-data-structures/filesystem.md) · [FsInfo](../core-data-structures/filesystem.md) · [FsTarget](../core-data-structures/filesystem.md) · [FsVersion](../core-data-structures/filesystem.md) · [FsWriteIntent](../core-data-structures/filesystem.md) · [FsWriteOutcome](../core-data-structures/filesystem.md)
|
|
|
-
|
|
|
-Source: [`packages/fs/fs/src/index.ts:172`](../../packages/fs/fs/src/index.ts)
|
|
|
-
|
|
|
-### `ctx.llm` — `LlmService`
|
|
|
-
|
|
|
-The abstract `llm` service: an adapter registry plus a streaming model-call surface, interceptable via the `llm/stream` waterfall.
|
|
|
-
|
|
|
-```ts cordis-catalog
|
|
|
-registerAdapter(models: string[], adapter: LlmAdapter): () => void
|
|
|
-models(): string[]
|
|
|
-stream(options: GenerateOptions): AsyncIterable<StreamChunk>
|
|
|
-```
|
|
|
-
|
|
|
-Types: [GenerateOptions](../core-data-structures/core.md) · [StreamChunk](../core-data-structures/llm-streaming.md)
|
|
|
-
|
|
|
-Source: [`packages/llm/llm/src/index.ts:78`](../../packages/llm/llm/src/index.ts)
|
|
|
-
|
|
|
-### `ctx.sessionPersistence` — `SessionPersistence` (abstract seam)
|
|
|
-
|
|
|
-Abstract durable session-persistence service. Subclass, implement the abstract methods, and load the subclass as a plugin — it registers as `ctx.sessionPersistence` (one implementation per context; loading a second throws, cordis' standard duplicate-service behavior).
|
|
|
-
|
|
|
-Contracts every implementation MUST honor (a DB backend asserts them inside a transaction; a file backend appends at EOF):
|
|
|
-
|
|
|
-- **Append-only; a crashed turn is closed, not truncated.** Committed events — those at or below a flushed `turn/end` — are never rewritten. A crash can leave an unclosed final turn whose events are real (and possibly large); load preserves them and closes the orphaned turn with synthetic boundary events (see load). Only a never-fully-written torn tail fragment is discarded.
|
|
|
-- **Contiguous seq.** A persisted log is contiguous: `events[i].seq === i`. load rejects a parse error or a `seq` gap in the COMMITTED region (unloadable); append's first event `seq` MUST equal the backend's stored next-seq (after `load` has balanced any interrupted turn).
|
|
|
-- **JSON-serializable data.** `SessionEventMap` is merge-extensible and `event.data` is typed only as `SessionEventMap[K]`, so append REJECTS non-JSON-serializable data with an error naming the offending event type. A backend snapshots (serializes/clones) each event when it buffers, since `session.events` hands out the live mutable object.
|
|
|
-- **Durability.** append returns only once the batch is durable (the file backend fsyncs; a DB commits). create MAY defer the physical write until the first append (lazy materialization).
|
|
|
-
|
|
|
-```ts cordis-catalog
|
|
|
-abstract create(meta: SessionHeader): Promise<void>
|
|
|
-abstract append(id: SessionId, events: readonly SessionEvent[]): Promise<void>
|
|
|
-abstract load(id: SessionId): Promise<{ meta: SessionHeader; events: SessionEvent[] }>
|
|
|
-abstract list(): Promise<SessionHeader[]>
|
|
|
-```
|
|
|
-
|
|
|
-Types: [SessionEvent](../core-data-structures/core.md)
|
|
|
-
|
|
|
-Source: [`packages/session-persistence/session-persistence/src/index.ts:98`](../../packages/session-persistence/session-persistence/src/index.ts)
|
|
|
-
|
|
|
-### `ctx.sessions` — `SessionStore`
|
|
|
-
|
|
|
-In-memory session store (`ctx.sessions`).
|
|
|
-
|
|
|
-Persistence is intentionally not implemented here — persistence plugins subscribe to `session/event` and flush on `session/flush` / dispose.
|
|
|
-
|
|
|
-```ts cordis-catalog
|
|
|
-create(id?: SessionId, options?: CreateSessionOptions): Session
|
|
|
-prepare(id?: SessionId, options?: CreateSessionOptions): Session
|
|
|
-enter(session: Session): () => void
|
|
|
-announce(session: Session): void
|
|
|
-get(id: SessionId): Session | undefined
|
|
|
-list(): Session[]
|
|
|
-```
|
|
|
-
|
|
|
-Source: [`packages/core/session/src/index.ts:327`](../../packages/core/session/src/index.ts)
|
|
|
-
|
|
|
-### `ctx.subagents` — `SubagentService`
|
|
|
-
|
|
|
-The `subagents` service: a registry of named SubagentProviders and a capability-checked start surface.
|
|
|
-
|
|
|
-```ts cordis-catalog
|
|
|
-registerProvider(provider: SubagentProvider): () => void
|
|
|
-getProvider(name: string): SubagentProvider | undefined
|
|
|
-list(): string[]
|
|
|
-start(name: string, request: SubagentStartRequest): SubagentRun
|
|
|
-```
|
|
|
-
|
|
|
-Source: [`packages/subagent/subagent/src/index.ts:123`](../../packages/subagent/subagent/src/index.ts)
|
|
|
-
|
|
|
-### `ctx.systemPrompt` — `SystemPrompt`
|
|
|
-
|
|
|
-Registry service (`ctx.systemPrompt`): plugins contribute ordered text sections and tool-schema providers; the agent loop calls `assemble()` once per step.
|
|
|
-
|
|
|
-```ts cordis-catalog
|
|
|
-section(section: PromptSection): () => void
|
|
|
-tools(provider: () => ToolSchema[]): () => void
|
|
|
-assemble(): Promise<PromptAssembly>
|
|
|
-```
|
|
|
-
|
|
|
-Source: [`packages/core/system-prompt/src/index.ts:73`](../../packages/core/system-prompt/src/index.ts)
|
|
|
-
|
|
|
-### `ctx.tools` — `ToolRegistry`
|
|
|
-
|
|
|
-Tool registry (`ctx.tools`): tool plugins register definitions; the agent loop executes calls through the `tools/pre-execute` → dispatch → `tools/post-execute` pipeline. The registry contributes its schemas into the system-prompt assembly.
|
|
|
-
|
|
|
-```ts cordis-catalog
|
|
|
-register(definition: ToolDefinition): () => void
|
|
|
-get(name: string): ToolDefinition | undefined
|
|
|
-schemas(): ToolSchema[]
|
|
|
-async execute(exec: ToolExecution): Promise<ToolExecutionResult>
|
|
|
-```
|
|
|
-
|
|
|
-Types: [ToolDefinition](../core-data-structures/tools.md) · [ToolExecution](../core-data-structures/tools.md) · [ToolExecutionResult](../core-data-structures/tools.md)
|
|
|
-
|
|
|
-Source: [`packages/core/tools/src/index.ts:268`](../../packages/core/tools/src/index.ts)
|
|
|
-
|
|
|
-### `ctx.web` — `WebService`
|
|
|
-
|
|
|
-The web access service. Registered as `ctx.web` (one instance per context).
|
|
|
-
|
|
|
-Selection semantics (resolved at execution time, never order-dependent):
|
|
|
-
|
|
|
-- A configured id that is registered and `status().available` → that provider.
|
|
|
-- A configured id not registered → `WEB_PROVIDER_CONFIGURED_MISSING`.
|
|
|
-- A configured id registered but unavailable → `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`.
|
|
|
-- No id configured, exactly one registered usable provider → that provider.
|
|
|
-- No id configured, multiple usable providers → `WEB_PROVIDER_AMBIGUOUS`.
|
|
|
-- No id configured, no usable provider → `WEB_PROVIDER_UNAVAILABLE`.
|
|
|
-
|
|
|
-```ts cordis-catalog
|
|
|
-registerSearchProvider(provider: WebSearchProvider): () => void
|
|
|
-registerFetchProvider(provider: WebFetchProvider): () => void
|
|
|
-async search(request: WebSearchRequest, exec?: WebExecContext): Promise<WebSearchResult>
|
|
|
-async fetch(request: WebFetchRequest, exec?: WebExecContext): Promise<WebFetchResult>
|
|
|
-```
|
|
|
-
|
|
|
-Source: [`packages/web/web/src/index.ts:87`](../../packages/web/web/src/index.ts)
|
|
|
-
|
|
|
-## Inherited tier (cordis core + loader/hmr/timer)
|
|
|
-
|
|
|
-The framework surface every plugin inherits, beyond the harness vocabulary above. This is pinned vendor source ([vendoring policy](../../vendor/README.md)); it is summarized here so the catalog is a complete picture of what `ctx` and the event bus offer, without elevating framework internals to the harness tier's prominence.
|
|
|
-
|
|
|
-### Inherited events
|
|
|
+The framework events every plugin also sees, beyond the harness vocabulary above. This is pinned vendor source ([vendoring policy](../../vendor/README.md)); it is summarized here so the page is a complete picture of the event bus, without elevating framework internals to the harness tier's prominence.
|
|
|
|
|
|
- `internal/plugin` — A plugin fiber was created. ([`vendor/cordis/src/events.ts:197`](../../vendor/cordis/src/events.ts))
|
|
|
- `internal/status` — A fiber changed lifecycle state. ([`vendor/cordis/src/events.ts:198`](../../vendor/cordis/src/events.ts))
|
|
|
@@ -558,16 +332,3 @@ The framework surface every plugin inherits, beyond the harness vocabulary above
|
|
|
- `loader/entry-init` — A config entry is being initialized. ([`vendor/loader/src/index.ts:25`](../../vendor/loader/src/index.ts))
|
|
|
- `loader/partial-dispose` — An entry is being partially disposed on reload. ([`vendor/loader/src/index.ts:26`](../../vendor/loader/src/index.ts))
|
|
|
- `loader/patch-context` — A context is being patched during a reload. ([`vendor/loader/src/index.ts:27`](../../vendor/loader/src/index.ts))
|
|
|
-
|
|
|
-### Inherited `ctx` members
|
|
|
-
|
|
|
-- `ctx.on / ctx.once` — Register an event listener (disposable). ([`vendor/cordis/src/events.ts:29`](../../vendor/cordis/src/events.ts))
|
|
|
-- `ctx.emit / ctx.parallel / ctx.serial / ctx.bail / ctx.waterfall` — Dispatch an event (sync / awaited / first-bail / veto-chain). ([`vendor/cordis/src/events.ts:29`](../../vendor/cordis/src/events.ts))
|
|
|
-- `ctx.plugin / ctx.inject` — Load a plugin / declare required services. ([`vendor/cordis/src/registry.ts:144`](../../vendor/cordis/src/registry.ts))
|
|
|
-- `ctx.effect` — Register a disposable side effect tied to the fiber. ([`vendor/cordis/src/fiber.ts:9`](../../vendor/cordis/src/fiber.ts))
|
|
|
-- `ctx.get / ctx.set / ctx.provide / ctx.accessor / ctx.mixin` — Low-level service-store access and binding. ([`vendor/cordis/src/reflect.ts:7`](../../vendor/cordis/src/reflect.ts))
|
|
|
-- `ctx.extend / ctx.isolate / ctx.intercept` — Derive a child context (scoped services / isolation / interception). ([`vendor/cordis/src/context.ts:35`](../../vendor/cordis/src/context.ts))
|
|
|
-- `ctx.root / ctx.scope / ctx.fiber / ctx.registry / ctx.reflect / ctx.events / ctx.logger` — Ambient handles onto the running context graph. ([`vendor/cordis/src/context.ts:16`](../../vendor/cordis/src/context.ts))
|
|
|
-- `ctx.timer (+ interval / timeout / throttle / debounce / setTimeout / setInterval)` — Disposable timer helpers. The `timer` key is provided at runtime; the six helpers are mixed onto ctx directly (declared via Pick). ([`vendor/timer/src/index.ts:4`](../../vendor/timer/src/index.ts))
|
|
|
-- `ctx.loader` — The config Loader that booted the app (present under the loader). ([`vendor/loader/src/index.ts:30`](../../vendor/loader/src/index.ts))
|
|
|
-- `ctx.hmr` — The hot-module-reload watcher (present under the hmr plugin). ([`vendor/hmr/src/index.ts:15`](../../vendor/hmr/src/index.ts))
|