Register the five web packages in the root tsconfig.json project graph (master's typecheck moved to `tsc -b tsconfig.json` and dropped the separate tsconfig.typecheck.json), and regenerate the module graph and cordis catalog so they reflect the web packages on master.
27 KiB
Cordis Events & Services 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/, which catalogs the data structures these signatures move around — this page is the verbs, that page is the nouns.
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
Dispatch modes: emit (fire-and-forget), waterfall (each listener gets next() and may transform or veto — see waterfall semantics), parallel (awaited fan-out, no veto). The harness declares 25 events across 7 scopes.
agent/*
agent/created — emit
An agent was registered in the AgentRegistry and is ready to receive messages.
'agent/created'(agent: Agent): void
Types: Agent
Source: packages/core/agent/src/types.ts:137
agent/disposed — emit
An agent was disposed and removed from the registry; its fiber and any in-flight turn have been torn down.
'agent/disposed'(agent: Agent): void
Types: Agent
Source: packages/core/agent/src/types.ts:143
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.
'agent/error'(agent: Agent, turn: number, step: number, error: Error): void
Types: Agent
Source: packages/core/agent/src/types.ts:220
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.
'agent/queued'(agent: Agent, content: ContentBlock[], info: { source: MessageSource; steering: boolean }): void
Types: Agent · ContentBlock · MessageSource
Source: packages/core/agent/src/types.ts:156
agent/request — waterfall
Waterfall: mutate the fully-assembled GenerateOptions before the model call (hooks, compaction, model switching, tool filtering, …). Call next() to delegate, or return without it to short-circuit.
'agent/request'(agent: Agent, turn: number, step: number, options: GenerateOptions, next: () => Promise<GenerateOptions>): Promise<GenerateOptions>
Types: Agent · GenerateOptions
Source: packages/core/agent/src/types.ts:189
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.
'agent/status'(agent: Agent, status: AgentStatus): void
Types: Agent
Source: packages/core/agent/src/types.ts:150
agent/steering — emit
Steering content was injected into a running turn.
'agent/steering'(agent: Agent, turn: number, content: ContentBlock[], source: MessageSource): void
Types: Agent · ContentBlock · MessageSource
Source: packages/core/agent/src/types.ts:214
agent/step-end — emit
A step ended.
'agent/step-end'(agent: Agent, turn: number, step: number): void
Types: Agent
Source: packages/core/agent/src/types.ts:180
agent/step-result — waterfall
Waterfall: post-process the assembled assistant Message before tool dispatch (validation, content rewriting, …).
'agent/step-result'(agent: Agent, turn: number, step: number, message: Message, next: () => Promise<Message>): Promise<Message>
Source: packages/core/agent/src/types.ts:195
agent/step-start — emit
A step (one model call plus its tool dispatch) began. step is 1-based within the turn; a turn runs one or more steps.
'agent/step-start'(agent: Agent, turn: number, step: number): void
Types: Agent
Source: packages/core/agent/src/types.ts:175
agent/stream-chunk — emit
A raw StreamChunk arrived from the model (token-level UI/log feed).
'agent/stream-chunk'(agent: Agent, turn: number, step: number, chunk: StreamChunk): void
Types: Agent · StreamChunk
Source: packages/core/agent/src/types.ts:209
agent/turn-continuation — waterfall
Waterfall: override the turn-continuation decision. The default (computed by the loop) is hadToolCalls || steeringInjected. Listeners can force-continue (/goal, /loop) or force-stop (budget guards).
'agent/turn-continuation'(agent: Agent, turn: number, defaultDecision: boolean, next: () => Promise<boolean>): Promise<boolean>
Types: Agent
Source: packages/core/agent/src/types.ts:202
agent/turn-end — emit
A turn ended. reason distinguishes a clean stop from a truncated or aborted one (completed | aborted | error | disposed | max-tokens).
'agent/turn-end'(agent: Agent, turn: number, reason: TurnEndReason): void
Types: Agent · TurnEndReason
Source: packages/core/agent/src/types.ts:169
agent/turn-start — emit
A turn began. turn is the 1-based turn number within the session.
'agent/turn-start'(agent: Agent, turn: number): void
Types: Agent
Source: packages/core/agent/src/types.ts:163
llm/*
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.
'llm/stream'(this: LlmService, options: GenerateOptions, next: () => AsyncIterable<StreamChunk>): AsyncIterable<StreamChunk>
Types: GenerateOptions · StreamChunk
Source: packages/llm/llm/src/index.ts:31
session/*
session/created — emit
A session was created in the store.
'session/created'(session: Session): void
Source: packages/core/session/src/index.ts:33
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.
'session/event'(session: Session, event: SessionEvent): void
Types: SessionEvent
Source: packages/core/session/src/index.ts:39
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.
'session/flush'(session: Session): Promise<void> | void
Source: packages/core/session/src/index.ts:48
subagent/*
subagent/end — emit
A subagent run settled — emitted when SubagentRun.result resolves (any stop reason). Paired with Events['subagent/start'].
'subagent/end'(info: SubagentRunEndInfo): void
Source: packages/subagent/subagent/src/index.ts:65
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'].
'subagent/start'(info: SubagentRunInfo): void
Source: packages/subagent/subagent/src/index.ts:59
system-prompt/*
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.
'system-prompt/assemble'(this: SystemPrompt, assembly: PromptAssembly, next: () => Promise<PromptAssembly>): Promise<PromptAssembly>
Source: packages/core/system-prompt/src/index.ts:24
system-prompt/change — emit
A section or tool provider was registered or unregistered (the assembly inputs changed).
'system-prompt/change'(): void
Source: packages/core/system-prompt/src/index.ts:30
tools/*
tools/change — emit
A tool was registered or unregistered (the available tool set changed).
'tools/change'(): void
Source: packages/core/tools/src/index.ts:48
tools/execute — waterfall
Waterfall around every tool execution — the single seam where sandbox, permission, hook, and plan-mode plugins wrap or veto a call. Listeners receive (exec, next): call next() to proceed (possibly around your own logic), or return a ToolExecutionResult without calling next() to short-circuit (veto).
'tools/execute'(this: ToolRegistry, exec: ToolExecution, next: () => Promise<ToolExecutionResult>): Promise<ToolExecutionResult>
Types: ToolExecution · ToolExecutionResult
Source: packages/core/tools/src/index.ts:43
web/*
web/providers-change — emit
Fired after the provider registry changes — a search or fetch provider was registered or disposed. Carries no payload and no capability graph: it means only "the provider registry changed; observers may recompute status from ctx.web". searchStatus() / fetchStatus() stay derived, not stored.
'web/providers-change'(this: WebService): void
Source: packages/web/web/src/index.ts:66
Services
The 11 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.
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
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.
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
Source: packages/core/agent/src/index.ts:117
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
lossyand point at full-stream spill files when available. - Disposal kills every running task and awaits their exit (no orphan processes survive
fiber.dispose()).
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 · BashExecSpec · BashRunResult · BashTask · BashTaskRead
Source: packages/bash/bash/src/index.ts:59
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
SurfaceEventTypeis a closed union, that node is auser/messagewithsurfaceOp: { op:'replace', start, end }; thecompact/*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/startbefore the slow work andcompact/endafter (even on failure) — so the lock is visible to replay and crash recovery.
abstract compactIfNeeded( session: Session, systemPrompt?: string, model?: string, signal?: AbortSignal, ): Promise<CompactionResult | null>
abstract compactRegion( session: Session, start: number, end: number, model: string, signal?: AbortSignal, ): Promise<CompactionResult>
Source: packages/compact/compact/src/index.ts:57
ctx.llm — LlmService
The abstract llm service: an adapter registry plus a streaming model-call surface, interceptable via the llm/stream waterfall.
registerAdapter(models: string[], adapter: LlmAdapter): () => void
models(): string[]
stream(options: GenerateOptions): AsyncIterable<StreamChunk>
Types: GenerateOptions · StreamChunk
Source: packages/llm/llm/src/index.ts:69
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 aseqgap in the COMMITTED region (unloadable); append's first eventseqMUST equal the backend's stored next-seq (afterloadhas balanced any interrupted turn). - JSON-serializable data.
SessionEventMapis merge-extensible andevent.datais typed only asSessionEventMap[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, sincesession.eventshands 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).
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
Source: packages/session-persistence/session-persistence/src/index.ts:98
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.
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:321
ctx.subagents — SubagentService
The subagents service: a registry of named SubagentProviders and a capability-checked start surface.
registerProvider(provider: SubagentProvider): () => void
getProvider(name: string): SubagentProvider | undefined
list(): string[]
start(name: string, request: SubagentStartRequest): SubagentRun
Source: packages/subagent/subagent/src/index.ts:103
ctx.systemPrompt — SystemPrompt
Registry service (ctx.systemPrompt): plugins contribute ordered text sections and tool-schema providers; the agent loop calls assemble() once per step.
section(section: PromptSection): () => void
tools(provider: () => ToolSchema[]): () => void
assemble(): Promise<PromptAssembly>
Source: packages/core/system-prompt/src/index.ts:71
ctx.tools — ToolRegistry
Tool registry (ctx.tools): tool plugins register definitions; the agent loop executes calls through the tools/execute waterfall. The registry contributes its schemas into the system-prompt assembly.
register(definition: ToolDefinition): () => void
get(name: string): ToolDefinition | undefined
schemas(): ToolSchema[]
async execute(exec: ToolExecution): Promise<ToolExecutionResult>
Types: ToolDefinition · ToolExecution · ToolExecutionResult
Source: packages/core/tools/src/index.ts:277
ctx.web — WebService
The web access service. Registered as ctx.web (one instance per context).
Selection semantics (identical for status and execution, never order- dependent):
- A configured id that is registered and
status().available→ that provider. - A configured id not registered →
configured-missing/WEB_PROVIDER_CONFIGURED_MISSING. - A configured id registered but unavailable →
configured-unavailable/WEB_PROVIDER_CONFIGURED_UNAVAILABLE. - No id configured, exactly one registered usable provider → that provider.
- No id configured, multiple usable providers →
ambiguous/WEB_PROVIDER_AMBIGUOUS. - No id configured, no usable provider →
none/WEB_PROVIDER_UNAVAILABLE.
registerSearchProvider(provider: WebSearchProvider): () => void
registerFetchProvider(provider: WebFetchProvider): () => void
searchStatus(): WebCapabilityStatus
fetchStatus(): WebCapabilityStatus
async search(request: WebSearchRequest, exec?: WebExecContext): Promise<WebSearchResult>
async fetch(request: WebFetchRequest, exec?: WebExecContext): Promise<WebFetchResult>
Source: packages/web/web/src/index.ts:106
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); 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
internal/plugin— A plugin fiber was created. (vendor/cordis/src/events.ts:197)internal/status— A fiber changed lifecycle state. (vendor/cordis/src/events.ts:198)internal/service— Interception hook for a service binding (no core producer). (vendor/cordis/src/events.ts:199)internal/update— Waterfall: a fiber config update is being applied. (vendor/cordis/src/events.ts:200)internal/get— Waterfall: a service is being read from the store. (vendor/cordis/src/events.ts:201)internal/set— Waterfall: a service is being written to the store. (vendor/cordis/src/events.ts:202)internal/listener— A listener was registered. (vendor/cordis/src/events.ts:203)internal/dispatch— An event is being dispatched to listeners. (vendor/cordis/src/events.ts:204)hmr/change— A watched source file changed on disk. (vendor/hmr/src/index.ts:20)hmr/reload— Plugins are being reloaded after a change. (vendor/hmr/src/index.ts:21)exit— The process is exiting on a signal. (vendor/loader/src/index.ts:23)loader/config-update— The loader config tree changed. (vendor/loader/src/index.ts:24)loader/entry-init— A config entry is being initialized. (vendor/loader/src/index.ts:25)loader/partial-dispose— An entry is being partially disposed on reload. (vendor/loader/src/index.ts:26)loader/patch-context— A context is being patched during a reload. (vendor/loader/src/index.ts:27)
Inherited ctx members
ctx.on / ctx.once— Register an event listener (disposable). (vendor/cordis/src/events.ts:29)ctx.emit / ctx.parallel / ctx.serial / ctx.bail / ctx.waterfall— Dispatch an event (sync / awaited / first-non-nullish / veto-chain). (vendor/cordis/src/events.ts:29)ctx.plugin / ctx.inject— Load a plugin / declare required services. (vendor/cordis/src/registry.ts:144)ctx.effect— Register a disposable side effect tied to the fiber. (vendor/cordis/src/fiber.ts:9)ctx.get / ctx.set / ctx.provide / ctx.accessor / ctx.mixin— Low-level service-store access and binding. (vendor/cordis/src/reflect.ts:7)ctx.extend / ctx.isolate / ctx.intercept— Derive a child context (scoped services / isolation / interception). (vendor/cordis/src/context.ts:35)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)ctx.timer (+ interval / timeout / throttle / debounce / setTimeout / setInterval)— Disposable timer helpers. Thetimerkey is provided at runtime; the six helpers are mixed onto ctx directly (declared via Pick). (vendor/timer/src/index.ts:4)ctx.loader— The config Loader that booted the app (present under the loader). (vendor/loader/src/index.ts:30)ctx.hmr— The hot-module-reload watcher (present under the hmr plugin). (vendor/hmr/src/index.ts:15)