Files
deepseek-harness/docs/cordis-catalog/events-and-services.md
Tianyi Cui a29bbe1453 Add JSDoc completeness gate for the cordis surface
gen-cordis-catalog now hard-errors (aggregated, not fail-fast) when an
event lacks description prose or a payload @param, or a public service
method lacks JSDoc, a @param per parameter, a @returns on a non-void
result, or an explicit return type annotation. The this receiver and the
trailing waterfall next are exempt on events (mode machinery owned by
@mode); a stale @param naming no real parameter errors, mirroring the
@mode contradiction check. parseJsDoc now ends prose at the first block
tag (standard JSDoc semantics), so the tags never change the rendered
catalog — only Source: line pointers moved.

Fills the ~139 gaps found across the 15 surface files, extends the spec
with negative-path fixtures for every new guard plus the exemptions,
records the decision as an implemented process RFC, and extends the
AGENTS.md typed-events bullet with the authoring rule. Runs inside
verify-cordis-catalog -> doc-sync, so CI and pre-push enforce it with
zero new wiring.
2026-07-04 19:06:35 +08:00

36 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; 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

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:234

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:241

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:389

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.

Serial (awaited in registration order), not a waterfall: a listener mutates the surface as a side effect; there is nothing to transform, but the loop must wait for the mutation to complete before opening the step and deriving. Cordis serial bails early if a listener returns a bail value; this event is typed and documented as void, so listeners must not return a semantic veto value. fullSystemPrompt is the assembled prompt a listener needs to measure pressure (the system prompt counts toward the budget). signal cancels any in-flight work a listener starts (e.g. a summarization model call).

'agent/pre-step'(agent: Agent, turn: number, step: number, fullSystemPrompt: string, signal: AbortSignal): Promise<void> | void

Types: Agent

Source: packages/core/agent/src/types.ts:319

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.

'agent/prompt-submit'(agent: Agent, content: ContentBlock[], source: MessageSource, next: () => Promise<PromptDecision>): Promise<PromptDecision>

Types: Agent · ContentBlock · MessageSource

Source: packages/core/agent/src/types.ts:332

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:259

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.

'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:345

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).

'agent/session-start'(agent: Agent, source: SessionStartSource): void

Types: Agent

Source: packages/core/agent/src/types.ts:274

agent/status — emit

Agent status changed (idlerunning, 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:250

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:379

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>

Types: Agent · Message

Source: packages/core/agent/src/types.ts:355

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.

'agent/turn-continuation'(agent: Agent, turn: number, defaultDecision: ContinuationDecision, next: () => Promise<ContinuationDecision>): Promise<ContinuationDecision>

Types: Agent

Source: packages/core/agent/src/types.ts:368

fs/*

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').

'fs/edit-intent'(target: FsTarget, actor: object | undefined, next: () => { version: FsVersion } | undefined | Promise<{ version: FsVersion } | undefined>): Promise<{ version: FsVersion } | undefined>

Types: FsTarget · FsVersion

Source: packages/fs/fs/src/index.ts:123

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.

'fs/observed'(target: FsTarget, version: FsVersion, actor: object | undefined): void

Types: FsTarget · FsVersion

Source: packages/fs/fs/src/index.ts:138

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.

'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:109

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:32

session/*

session/created — emit

A session was created in the store.

'session/created'(session: Session): void

Source: packages/core/session/src/index.ts:36

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:44

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:54

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:77

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:70

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:26

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:32

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:87

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).

'tools/post-execute'(this: ToolRegistry, exec: ToolExecution, result: ToolExecutionResult, next: () => Promise<PostToolDecision>): Promise<PostToolDecision>

Types: ToolExecution · ToolExecutionResult

Source: packages/core/tools/src/index.ts:82

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)).

'tools/pre-execute'(this: ToolRegistry, exec: ToolExecution, next: () => Promise<PreToolDecision>): Promise<PreToolDecision>

Types: ToolExecution

Source: packages/core/tools/src/index.ts:66

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:65

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.agentLoopAgentLoop

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.agentsAgentRegistry

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.bashBashExecutor (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()).
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.compactCompactService (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.
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

ctx.fsFileSystem (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).
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.llmLlmService

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:70

ctx.sessionPersistenceSessionPersistence (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).
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.sessionsSessionStore

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:327

ctx.subagentsSubagentService

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:123

ctx.systemPromptSystemPrompt

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:73

ctx.toolsToolRegistry

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.

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:268

ctx.webWebService

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:105

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

Inherited ctx members