Every ctx.<key> service a plugin can call: the exact public interface plus the class JSDoc. This is one axis of the wiring reference a plugin author works against — the events a plugin listens to are the sibling events catalog, and core-data-structures/ catalogs the data structures these signatures move around. An abstract seam (e.g. ctx.bash) is implemented by a separate package; the interface is what consumers code against.
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 ctx surface a plugin also sees — pinned vendor source, summarized tersely.
ctx.agentLoop — AgentLoopThe 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 = {}, meta: Pick<SessionHeader, 'cwd'> = {}): ReactLoopAgent
async createAgent(options: CreateAgentOptions): Promise<AgentHandle>
async resume(options: ResumeAgentOptions): Promise<AgentHandle>
Source: packages/core/agent-loop/src/index.ts:71
ctx.agents — AgentRegistryAgent 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 (@deepseek-ai/dsh-agent-loop), registered via setFactory.
setFactory(factory: AgentFactory): () => Promise<void> | void
async create(options: CreateAgentOptions): Promise<AgentHandle>
async resume(options: ResumeAgentOptions): Promise<AgentHandle>
register(agent: Agent): () => Promise<void> | void
enter(agent: Agent): () => void
announce(agent: Agent): void
get(id: AgentId): Agent | undefined
list(): Agent[]
Types: Agent
Source: packages/core/agent/src/index.ts:168
ctx.approval — ApprovalServiceThe ctx.approval service: dispatches ApprovalRequests to the approval/request waterfall and audits every ask/outcome pair to the requesting agent's session log. Stateless between requests — grants are returned to the caller, never stored here.
Owns the policy tier too (effective = fold(the session's 'approval/policy' events) ?? config.policy): request() resolves 'never' to 'rejected' before dispatching any interactive answerer, a per-agent prompt section states a 'never' policy (and only that one in prose — an 'ask' promise could overclaim an answerer that headless compositions do not have), and an agent/pre-step narrator injects at most one coalesced notice when a session's effective policy moved past what the model was last told.
async request(req: ApprovalRequest): Promise<ApprovalOutcome>
Types: ApprovalOutcome · ApprovalRequest
Source: packages/ui/user-approval/src/index.ts:287
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:
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()).
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:62
ctx.codeRuntime — CodeRuntime (abstract seam)Abstract code-execution service. Subclass, implement run and the two descriptors, and load the subclass as a plugin — it registers as ctx.codeRuntime (one implementation per context; loading a second throws, cordis' standard duplicate-service behavior).
Semantics every implementation must honor:
Disposal reaches quiescence: in-flight runs are terminated AND awaited before the service's own teardown completes (no orphan substrate survives fiber.dispose()).
abstract run(request: CodeRunRequest): Promise<CodeRunResult>
Types: CodeRunRequest · CodeRunResult
Source: packages/code-runtime/code-runtime/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:
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.
abstract compactIfNeeded( agent: CompactAgentContext, fullSystemPrompt: string, sessionPrefix: readonly Message[], signal: AbortSignal, ): Promise<CompactionResult | null>
abstract compactRegion( session: Session, start: number, end: number, agent: CompactAgentContext, signal?: AbortSignal, ): Promise<CompactionResult>
Types: Message
Source: packages/compact/compact/src/index.ts:65
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:
targetKey so stale guards and target lookup agree across paths (e.g. through symlinks).undefined when the target is absent.FS_NOT_TEXT.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.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).
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 · FsEditRequest · FsInfo · FsTarget · FsVersion · FsWriteIntent · FsWriteOutcome
Source: packages/fs/fs/src/index.ts:172
ctx.llm — LlmServiceThe 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:88
ctx.sandbox — SandboxProvider (abstract seam)Abstract process-sandbox service. Subclass, implement confine, and load the subclass as a plugin — it registers as ctx.sandbox (one implementation per context; loading a second throws, cordis' standard duplicate-service behavior).
Semantics every implementation must honor:
confine time with SandboxUnavailableError (no backend for this host), or at EXECUTION time by the runner itself refusing to run the command (exiting without exec'ing it, identified by ConfinedArgv.runnerFailureSignatures). A silent unconfined passthrough is never a legal outcome on either path.confine itself spawns nothing beyond that one-time probing.The returned ConfinedArgv.enforcement states the backend's actual completeness for THIS host; partial is reported, never silently upgraded to full.
abstract confine(argv: readonly string[], policy: SandboxPolicy): ConfinedArgv
Types: ConfinedArgv · SandboxPolicy
Source: packages/sandbox/sandbox/src/index.ts:180
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):
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.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).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).
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:102
ctx.sessions — SessionStoreIn-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
async flush(session: Session): Promise<void>
get(id: SessionId): Session | undefined
list(): Session[]
fork(source: SessionForkSource, boundary?: number, childSessionId?: SessionId): Session
Source: packages/core/session/src/index.ts:427
ctx.skills — SkillServiceRegistry of skill providers. It merges provider catalogs with stable first-wins duplicate handling, exposes sorted model-visible summaries, and loads full skill bodies on demand.
registerProvider(provider: SkillProvider): () => Promise<void> | void
register(skill: SkillRegistration): () => Promise<void> | void
async list(options: SkillLookupOptions = {}): Promise<SkillSummary[]>
async get(name: string, options: SkillLookupOptions = {}): Promise<SkillDefinition | undefined>
Source: packages/skill/skill/src/index.ts:157
ctx.subagents — SubagentServiceThe subagents service: a registry of named SubagentProviders and a capability-checked start surface.
registerProvider(provider: SubagentProvider): () => Promise<void> | void
getProvider(name: string): SubagentProvider | undefined
list(): string[]
start(name: string, request: SubagentStartRequest): SubagentRun
Source: packages/subagent/subagent/src/index.ts:155
ctx.systemPrompt — SystemPromptRegistry service (ctx.systemPrompt): plugins contribute ordered text sections, tool-schema providers, named prompt variables, and authoritative contribution protections; the agent loop calls assemble(context) once per step. Registers the harness-owned harness:identity and deployment:persona sections itself (see Config.persona).
section(section: PromptSection): () => Promise<void> | void
tools(provider: (context: AssembleContext) => ToolProviderResult): () => Promise<void> | void
variable(name: string, provider: (context: AssembleContext) => string | undefined): () => Promise<void> | void
protect(protection: PromptProtection): () => Promise<void> | void
async assemble(context: AssembleContext = {}): Promise<PromptAssembly>
Source: packages/core/system-prompt/src/index.ts:379
ctx.tools — ToolRegistryTool registry (ctx.tools): tool plugins register definitions; the agent loop executes calls through the tools/pre-execute → guards → tools/execute → tools/post-execute → tools/result pipeline. The registry contributes its schemas into the system-prompt assembly — WHICH schemas is governed by its mode config (see Config.mode); under a non-native mode it also owns the reserved run_code presentation transport and the tools:sdk prompt section.
Two registration layers (@deepseek-ai/dsh-scope): a registration through a plain plugin context is GLOBAL (visible to every agent); one through a scoped context (agent.ctx) is filed in that scope's layer — visible to that agent alone, disposed with the scope, and SHADOWING a global tool of the same name for that agent (most-specific-wins; within one layer a duplicate name still throws). restrict masks the global layer per scope. One visibility function (visible) feeds prompt assembly, get, and execute — and, under a non-native mode, the SDK section and run_code's bindings — so what the model is shown, what a presenter renders, what a program can call, and what dispatches can never disagree.
register(definition: ToolDefinition): () => Promise<void> | void
restrict(filter: ToolRestriction): () => Promise<void> | void
guard(guard: ToolGuard): () => Promise<void> | void
visible(scope?: ScopeKey): ToolDefinition[]
get(name: string, scope?: ScopeKey): ToolDefinition | undefined
schemas(scope?: ScopeKey): ToolSchema[]
knownNames(scope?: ScopeKey): string[]
async execute(exec: ToolExecutionInput): Promise<ToolExecutionResult>
Types: ToolDefinition · ToolExecutionInput · ToolExecutionResult
Source: packages/core/tools/src/index.ts:478
ctx.userInteraction — UserInteractionServicectx.userInteraction: one active UI provider plus an ask() surface.
registerProvider(provider: UserInteractionProvider): () => void
async ask(request: AskUserQuestionRequest): Promise<AskUserQuestionAnswer>
Source: packages/ui/user-interaction/src/index.ts:82
ctx.web — WebServiceThe web access service. Registered as ctx.web (one instance per context).
Selection semantics (resolved at execution time, never order-dependent):
status().available → that provider.WEB_PROVIDER_CONFIGURED_MISSING.WEB_PROVIDER_CONFIGURED_UNAVAILABLE.WEB_PROVIDER_AMBIGUOUS.No id configured, no usable provider → WEB_PROVIDER_UNAVAILABLE.
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
ctx.workflows — WorkflowService (abstract seam)Abstract workflow execution service. Subclass, implement start, and load the subclass as a plugin — it registers as ctx.workflows (one implementation per context; loading a second throws, cordis' standard duplicate-service behavior).
Semantics every implementation must honor:
result NEVER rejects — every failure resolves with stopReason: 'error' (or 'cancelled') — and once the run is cancelled, result SETTLES within the implementation's bounded grace even if the script itself never settles (a consumer awaiting result must never be wedged past a cancellation).workflow/* events fire through emitWorkflowEvent (data snapshots, per-listener containment); workflow/end fires exactly once per started run, after result is settled or as it settles.dispose() reaches quiescence within a bounded grace: it cancels, waits for the script to settle AND its started children to finish disposing, and abandons whatever is left rather than hanging its caller (the engine documents what abandonment leaves behind).Runs are HOLDER-OWNED: the engine hands control (cancel/dispose) to the start() caller and does not track its live runs — disposing the engine's own fiber mid-run deliberately leaves those runs to their holders' teardown, so an engine reload cannot yank a run out from under the consumer awaiting it.
abstract start(request: WorkflowStartRequest): WorkflowRun
Source: packages/workflow/workflow/src/index.ts:210
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); 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.
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-bail / 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. The timer key 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)