# Conflicts: # docs/config-catalog.md # docs/cordis-catalog/services.md # docs/module-graph.md # docs/rfc/implemented/feature/2026-07-06-sandbox.md # examples/acp-agent/tests/snapshots/advanced-toolchain/system-prompt.golden.md # examples/acp-agent/tests/snapshots/advanced-toolchain/tool-schemas.golden.json # examples/acp-agent/tests/snapshots/both-mode-turn/system-prompt.golden.md # examples/acp-agent/tests/snapshots/both-mode-turn/tool-schemas.golden.json # examples/acp-agent/tests/snapshots/code-mode-turn/system-prompt.golden.md # examples/acp-agent/tests/snapshots/escalation-approved/session.jsonl # examples/acp-agent/tests/snapshots/escalation-rejected/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/permission-switching/system-prompt.golden.md # examples/acp-agent/tests/snapshots/permission-switching/tool-schemas.golden.json # examples/acp-agent/tests/snapshots/skill-load/tool-schemas.golden.json # examples/acp-agent/tests/snapshots/text-turn/tool-schemas.golden.json # examples/acp-agent/tests/snapshots/workspace-edit/system-prompt.golden.md # examples/acp-agent/tests/snapshots/workspace-edit/tool-schemas.golden.json # packages/bash/bash-sandbox/src/index.ts # packages/bash/bash-sandbox/tests/bwrap.e2e.ts # packages/bash/bash-sandbox/tests/sandbox.spec.ts # packages/bash/bash-sandbox/tests/seatbelt.e2e.ts # packages/bash/bash/src/index.ts # packages/bash/tool-bash/package.json # packages/bash/tool-bash/src/index.ts # packages/bash/tool-bash/src/render.ts # packages/bash/tool-bash/tests/tools.spec.ts # packages/bash/tool-bash/tsconfig.json # pnpm-lock.yaml # scripts/verify-package-readme-model-experience.ts
24 KiB
Cordis Events Catalog
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, and core-data-structures/ 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, 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), 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/created — emit
A fully configured agent and live session were published. Setup is composition-only; agent/session-start is the first startup-driving seam. Synchronous listener failure vetoes publication, while returned-promise rejection is reported. Detach requested during dispatch waits until every creation listener has observed the stable entry.
'agent/created'(this: Scoped<Agent>, agent: Agent): void
Types: Agent
Source: packages/core/agent/src/types.ts:139
agent/disposed — emit
An agent left the registry; AgentLoop emits this after driver quiescence but before session detachment and scoped-registration unwind. Custom registry users own their driver-ordering contract.
'agent/disposed'(this: Scoped<Agent>, agent: Agent): void
Types: Agent
Source: packages/core/agent/src/types.ts:148
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'(this: Scoped<Agent>, agent: Agent, turn: number, step: number, error: Error): void
Types: Agent
Source: packages/core/agent/src/types.ts:283
agent/pre-step — serial
Awaited serial checkpoint for session-surface mutation after prompt assembly and before step/start; appends land outside the pending step. The loop derives history once afterward, so compaction records and replacements are included without rewriting an assembled request. The prompt and prefix are the exact pressure inputs for that request, and signal cancels listener work. Scope-filtered dispatch (@deepseek-ai/dsh-scope): agent-scoped listeners receive only that agent.
'agent/pre-step'(this: Scoped<Agent>, agent: Agent, turn: number, step: number, fullSystemPrompt: string, sessionPrefix: readonly Message[], signal: AbortSignal): Promise<void> | void
Source: packages/core/agent/src/types.ts:202
agent/prompt-submit — waterfall
Allow, rewrite, or block one drained prompt before it becomes a user message. Call next() for the unchanged default.
'agent/prompt-submit'(this: Scoped<Agent>, agent: Agent, content: ContentBlock[], source: MessageSource, next: () => Promise<PromptDecision>): Promise<PromptDecision>
Types: Agent · ContentBlock · MessageSource
Source: packages/core/agent/src/types.ts:212
agent/queued — emit
Detached, frozen content entered the agent's inbox. Source defaults have already been applied, so these are the exact values retained for the log.
'agent/queued'(this: Scoped<Agent>, agent: Agent, content: ContentBlock[], info: { source: MessageSource; steering: boolean }): void
Types: Agent · ContentBlock · MessageSource
Source: packages/core/agent/src/types.ts:167
agent/request — waterfall
Replace the frozen call configuration. Model-visible content must use logged channels; this seam cannot mutate messages. Injection here joins the next request because the current step boundary is already fixed.
'agent/request'(this: Scoped<Agent>, agent: Agent, turn: number, step: number, config: LlmCallConfig, next: () => Promise<LlmCallConfig>): Promise<LlmCallConfig>
Types: Agent · LlmCallConfig
Source: packages/core/agent/src/types.ts:224
agent/session-prefix — waterfall
Compose request-only messages placed before derived history. The frozen result is computed once per loop instance, logged on its anchoring request header, and reused so the provider prefix remains stable. Interrupted composition is discarded. Composition precedes the first agent/pre-step and request boundary, so listener appends join the current request and pressure accounting sees the composed prefix. Changing context belongs in history; contributors should prepend to await next() to preserve registration order. Scope-filtered dispatch (@deepseek-ai/dsh-scope): agent-scoped listeners receive only that agent.
'agent/session-prefix'(this: Scoped<Agent>, agent: Agent, prefix: Message[], signal: AbortSignal, next: () => Promise<Message[]>): Promise<Message[]>
Source: packages/core/agent/src/types.ts:239
agent/session-start — emit
The session lifecycle began, once before the first turn. Use agent.inject() to seed model-facing context. This is a notification, not a veto; disposal requested by a lifecycle owner is rechecked before the driver starts.
'agent/session-start'(this: Scoped<Agent>, agent: Agent, source: SessionStartSource): void
Types: Agent · SessionStartSource
Source: packages/core/agent/src/types.ts:180
agent/status — emit
Agent status changed (idle ⇄ running, or → disposed). send() does not enter running synchronously; drive lifecycle from this event.
'agent/status'(this: Scoped<Agent>, agent: Agent, status: AgentStatus): void
Types: Agent
Source: packages/core/agent/src/types.ts:157
agent/step-result — waterfall
Waterfall: post-process the assembled assistant Message before tool dispatch (validation, content rewriting, …).
'agent/step-result'(this: Scoped<Agent>, agent: Agent, turn: number, step: number, message: Message, next: () => Promise<Message>): Promise<Message>
Source: packages/core/agent/src/types.ts:250
agent/turn-continuation — waterfall
Override whether the turn continues. The default continues after tool calls or steering and stops otherwise; a continue reason becomes steering.
'agent/turn-continuation'(this: Scoped<Agent>, agent: Agent, turn: number, defaultDecision: ContinuationDecision, next: () => Promise<ContinuationDecision>): Promise<ContinuationDecision>
Types: Agent
Source: packages/core/agent/src/types.ts:260
agent/turn-stop — serial
Monotonic terminal-stop checkpoint after continuation and steering are folded; a stop remains authoritative through turn close and flush: steering queued in that window is discarded, while ordinary sends survive.
'agent/turn-stop'(this: Scoped<Agent>, agent: Agent, turn: number): ContinuationStop | undefined
Types: Agent
Source: packages/core/agent/src/types.ts:270
approval/*
approval/request — waterfall
Ask composed answerers for one decision. Return an outcome to claim the request or call next(); failure yields the fail-closed default. Scope-filtered dispatch (@deepseek-ai/dsh-scope): agent-scoped listeners receive only that agent.
'approval/request'(this: Scoped<ApprovalService>, req: ApprovalRequest, next: () => Promise<ApprovalOutcome>): Promise<ApprovalOutcome>
Types: ApprovalOutcome · ApprovalRequest
Source: packages/ui/user-approval/src/index.ts:31
fs/*
fs/edit-intent — waterfall
Single-slot decision for the next FileSystem.editText. Calling next() yields an unconditional edit; the first returned guard wins.
'fs/edit-intent'(target: FsTarget, actor: object | undefined, next: () => { version: FsVersion } | undefined | Promise<{ version: FsVersion } | undefined>): Promise<{ version: FsVersion } | undefined>
Source: packages/fs/fs/src/index.ts:60
fs/observed — emit
Record a successful observation. Listeners must be synchronous recorders: throws fail the tool call and returned promises are not awaited.
'fs/observed'(target: FsTarget, version: FsVersion, actor: object | undefined): void
Source: packages/fs/fs/src/index.ts:69
fs/write-intent — waterfall
Single-slot decision for the next FileSystem.writeText. Calling next() yields the bare provider's unconditional write; the first listener that returns an intent owns the decision rather than composing with peers.
'fs/write-intent'(target: FsTarget, actor: object | undefined, next: () => FsWriteIntent | undefined | Promise<FsWriteIntent | undefined>): Promise<FsWriteIntent | undefined>
Types: FsTarget · FsWriteIntent
Source: packages/fs/fs/src/index.ts:52
llm/*
llm/stream — waterfall
Waterfall around every streaming model call (retry, replay, 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:39
session/*
session/created — emit
Creation announcement during session publication. A synchronous throw vetoes and rolls back with a paired disposal; detach requested during dispatch is deferred. A returned-promise rejection is logged but cannot retroactively veto this synchronous boundary. Scope-filtered dispatch (@deepseek-ai/dsh-scope): agent-scoped listeners receive only sessions entered through that agent's context.
'session/created'(this: Scoped<Session>, session: Session): void
Source: packages/core/session/src/index.ts:47
session/disposed — emit
Emitted once when an announced session leaves the store, including publication rollback, but never for an entry whose creation announcement did not begin. Listener failures are logged and contained. Scope-filtered dispatch (@deepseek-ai/dsh-scope) reuses the owner scope.
'session/disposed'(this: Scoped<Session>, session: Session): void
Source: packages/core/session/src/index.ts:57
session/event — emit
Post-commit, fire-and-forget append feed. The listener snapshot resolves before the log push, but callbacks run after it; observer failures are logged and contained without making the committed append fail. Scope-filtered dispatch (@deepseek-ai/dsh-scope): agent-scoped listeners receive only events from sessions entered through that agent's context.
'session/event'(this: Scoped<Session>, session: Session, event: SessionEvent): void
Types: SessionEvent
Source: packages/core/session/src/index.ts:69
session/flush — parallel
Awaited parallel durability checkpoint: every listener runs and the caller awaits all of them, with no waterfall veto. Dispatch through SessionStore.flush. Scope-filtered dispatch (@deepseek-ai/dsh-scope) reuses the session's owner scope.
'session/flush'(this: Scoped<Session>, session: Session): Promise<void> | void
Source: packages/core/session/src/index.ts:79
subagent/*
subagent/end — emit
A ready child settled. Scope-filtered dispatch uses the same delegating parent carrier as subagent/start, so the lifecycle pair reaches the same scoped audience.
'subagent/end'(this: Scoped<SubagentService>, info: SubagentRunEndInfo): void
Source: packages/subagent/subagent/src/index.ts:108
subagent/provider-added — emit
A provider became resolvable in the registry.
'subagent/provider-added'(provider: SubagentProvider): void
Source: packages/subagent/subagent/src/index.ts:82
subagent/provider-removed — emit
A provider left the registry. Accepted runs remain holder-owned.
'subagent/provider-removed'(name: string): void
Source: packages/subagent/subagent/src/index.ts:88
subagent/start — emit
A provider established a ready child. For in-process providers, ctx.agents.get(info.id) resolves during this notification. Scope-filtered dispatch keys the carrier by the delegating parent, so a parent-scoped listener observes only its own delegations. Paired with subagent/end.
'subagent/start'(this: Scoped<SubagentService>, info: SubagentRunInfo): void
Source: packages/subagent/subagent/src/index.ts:99
system-prompt/*
system-prompt/assemble — waterfall
Expert waterfall over the assembled sections, tools, and variables. Scope-filtered dispatch (@deepseek-ai/dsh-scope): scoped listeners receive only that scope's assemblies. The returned value is authoritative.
'system-prompt/assemble'(this: Scoped<SystemPrompt>, assembly: PromptAssembly, context: AssembleContext, next: () => Promise<PromptAssembly>): Promise<PromptAssembly>
Source: packages/core/system-prompt/src/index.ts:27
system-prompt/change — emit
Emitted when any prompt provider changes. This registry notification is unfiltered because a global change affects every scope.
'system-prompt/change'(): void
Source: packages/core/system-prompt/src/index.ts:33
tools/*
tools/change — emit
A tool was registered or unregistered, or a scoped restriction changed (the available tool set changed — possibly for one scope only). An UNFILTERED registry-subject notification, deliberately not scope-filtered dispatch: a global change concerns every agent's next assembly, so a scoped listener subscribing here sees every change, not just its own scope's.
'tools/change'(): void
Source: packages/core/tools/src/index.ts:116
tools/execute — waterfall
Around-dispatch waterfall for timeout, retry, or metrics. next() returns a normalized result; wrappers may change only exec.signal, while call identity remains immutable. Scope-filtered dispatch (@deepseek-ai/dsh-scope): agent-scoped listeners receive only that agent's calls.
'tools/execute'(this: Scoped<ToolRegistry>, exec: ToolExecution, next: () => Promise<ToolExecutionResult>): Promise<ToolExecutionResult>
Types: ToolExecution · ToolExecutionResult
Source: packages/core/tools/src/index.ts:89
tools/post-execute — waterfall
Accept, replace, enrich, or block a normalized dispatch result. next() accepts it unchanged; thrown tools still reach this seam as errors. Scope-filtered dispatch (@deepseek-ai/dsh-scope): agent-scoped listeners receive only that agent's calls.
'tools/post-execute'(this: Scoped<ToolRegistry>, exec: ToolExecution, result: Readonly<ToolExecutionResult>, next: () => Promise<PostToolDecision>): Promise<PostToolDecision>
Types: ToolExecution · ToolExecutionResult
Source: packages/core/tools/src/index.ts:98
tools/pre-execute — waterfall
Allow, deny, or ask before dispatch. next() delegates to allow; missing approval support turns ask into denial. Scope-filtered dispatch (@deepseek-ai/dsh-scope): agent-scoped listeners receive only that agent's calls.
'tools/pre-execute'(this: Scoped<ToolRegistry>, exec: ToolExecution, next: () => Promise<PreToolDecision>): Promise<PreToolDecision>
Types: ToolExecution
Source: packages/core/tools/src/index.ts:80
tools/result — emit
Observe the frozen, lossless-JSON final outcome. Listener failures are contained. Scope-filtered dispatch (@deepseek-ai/dsh-scope): keyed by exec.agent.
'tools/result'(this: Scoped<ToolRegistry>, exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): undefined
Types: ToolExecution · ToolExecutionResult
Source: packages/core/tools/src/index.ts:106
workflow/*
workflow/agent-end — emit
One agent() call settled (clean result, child failure, or run cancellation). Paired with Events['workflow/agent-start'] by agent.seq, exactly once per started call on every stop path — on an engine termination path (a worker killed past its grace) the end is engine-synthesized with outcome 'cancelled'.
'workflow/agent-end'(info: WorkflowRunInfo, agent: WorkflowAgentEndInfo): void
Source: packages/workflow/workflow/src/index.ts:81
workflow/agent-start — emit
One agent() call established a ready child run. Paired with Events['workflow/agent-end'] by agent.seq. A call that never receives a ready run from the provider emits neither event in this pair.
'workflow/agent-start'(info: WorkflowRunInfo, agent: WorkflowAgentInfo): void
Source: packages/workflow/workflow/src/index.ts:70
workflow/end — emit
A workflow run settled (any stop reason). Fired when WorkflowRun.result resolves. Paired with Events['workflow/start'].
'workflow/end'(info: WorkflowRunInfo, result: WorkflowResultInfo): void
Source: packages/workflow/workflow/src/index.ts:91
workflow/log — emit
The script emitted a narration line (a log(message) call).
'workflow/log'(info: WorkflowRunInfo, message: string): void
Source: packages/workflow/workflow/src/index.ts:60
workflow/phase — emit
The script entered a phase (a phase(title) call) — progress grouping for observers; no execution semantics.
'workflow/phase'(info: WorkflowRunInfo, title: string): void
Source: packages/workflow/workflow/src/index.ts:53
workflow/start — emit
A workflow run started — the script's meta block validated, the body about to execute. Paired with Events['workflow/end'].
'workflow/start'(info: WorkflowRunInfo): void
Source: packages/workflow/workflow/src/index.ts:45
Inherited events (cordis core + loader/hmr/timer)
The framework events every plugin also sees, beyond the harness vocabulary above. This is pinned vendor source (vendoring policy); 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)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)