Merge branch 'worktree/agent-execution-context-rfc' into worktree/explicit-turn-signal

This commit is contained in:
Yichen Jiang
2026-07-18 21:33:17 +08:00
683 changed files with 37770 additions and 8712 deletions

View File

@@ -23,7 +23,7 @@ A fully configured agent and live session were published. Setup is composition-o
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:206`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:221`](../../packages/core/agent/src/types.ts)
### `agent/disposed` — emit
@@ -35,7 +35,7 @@ An agent left the registry; AgentLoop emits this after driver quiescence but bef
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:215`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:230`](../../packages/core/agent/src/types.ts)
### `agent/error` — emit
@@ -47,7 +47,7 @@ A step or turn errored. The loop reports a failure here (plus the logger) even w
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:357`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:372`](../../packages/core/agent/src/types.ts)
### `agent/pre-step` — serial
@@ -59,7 +59,7 @@ Awaited serial checkpoint for session-surface mutation after prompt assembly and
Types: [Agent](../core-data-structures/core.md) · [Message](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:269`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:284`](../../packages/core/agent/src/types.ts)
### `agent/prompt-submit` — waterfall
@@ -71,7 +71,7 @@ Allow, rewrite, or block one drained prompt before it becomes a user message. Ca
Types: [Agent](../core-data-structures/core.md) · [ContentBlock](../core-data-structures/core.md) · [MessageSource](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:282`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:297`](../../packages/core/agent/src/types.ts)
### `agent/queued` — emit
@@ -83,7 +83,7 @@ Detached, frozen content entered the agent's inbox. Source defaults have already
Types: [Agent](../core-data-structures/core.md) · [ContentBlock](../core-data-structures/core.md) · [MessageSource](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:234`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:249`](../../packages/core/agent/src/types.ts)
### `agent/request` — waterfall
@@ -95,7 +95,7 @@ Replace the frozen call configuration. Model-visible content must use logged cha
Types: [Agent](../core-data-structures/core.md) · [LlmCallConfig](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:295`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:310`](../../packages/core/agent/src/types.ts)
### `agent/session-prefix` — waterfall
@@ -107,7 +107,7 @@ Compose request-only messages placed before derived history. The frozen result i
Types: [Agent](../core-data-structures/core.md) · [Message](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:310`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:325`](../../packages/core/agent/src/types.ts)
### `agent/session-start` — emit
@@ -119,7 +119,7 @@ The session lifecycle began, once before the first turn. Use `agent.inject()` to
Types: [Agent](../core-data-structures/core.md) · [SessionStartSource](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:247`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:262`](../../packages/core/agent/src/types.ts)
### `agent/status` — emit
@@ -131,7 +131,7 @@ Agent status changed (`idle` ⇄ `running`, or → `disposed`). `send()` does no
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:224`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:239`](../../packages/core/agent/src/types.ts)
### `agent/step-result` — waterfall
@@ -143,7 +143,7 @@ Waterfall: post-process the assembled assistant Message before tool dispatch (va
Types: [Agent](../core-data-structures/core.md) · [Message](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:322`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:337`](../../packages/core/agent/src/types.ts)
### `agent/turn-continuation` — waterfall
@@ -155,7 +155,7 @@ Override whether the turn continues. The default continues after tool calls or s
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:333`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:348`](../../packages/core/agent/src/types.ts)
### `agent/turn-stop` — serial
@@ -167,7 +167,7 @@ Monotonic terminal-stop checkpoint after continuation and steering are folded; a
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:344`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:359`](../../packages/core/agent/src/types.ts)
## `approval/*`
@@ -195,7 +195,7 @@ Single-slot decision for the next FileSystem.editText. Calling `next()` yields a
Types: [FsTarget](../core-data-structures/filesystem.md) · [FsVersion](../core-data-structures/filesystem.md)
Source: [`packages/fs/fs/src/index.ts:59`](../../packages/fs/fs/src/index.ts)
Source: [`packages/fs/fs/src/index.ts:61`](../../packages/fs/fs/src/index.ts)
### `fs/observed` — emit
@@ -207,7 +207,7 @@ Record a successful observation. Listeners must be synchronous recorders: throws
Types: [FsTarget](../core-data-structures/filesystem.md) · [FsVersion](../core-data-structures/filesystem.md)
Source: [`packages/fs/fs/src/index.ts:68`](../../packages/fs/fs/src/index.ts)
Source: [`packages/fs/fs/src/index.ts:70`](../../packages/fs/fs/src/index.ts)
### `fs/write-intent` — waterfall
@@ -219,7 +219,7 @@ Single-slot decision for the next FileSystem.writeText. Calling `next()` yields
Types: [FsTarget](../core-data-structures/filesystem.md) · [FsWriteIntent](../core-data-structures/filesystem.md)
Source: [`packages/fs/fs/src/index.ts:51`](../../packages/fs/fs/src/index.ts)
Source: [`packages/fs/fs/src/index.ts:53`](../../packages/fs/fs/src/index.ts)
## `llm/*`
@@ -233,7 +233,7 @@ Waterfall around every streaming model call (retry, replay, routing). Bound to t
Types: [GenerateOptions](../core-data-structures/core.md) · [StreamChunk](../core-data-structures/llm-streaming.md)
Source: [`packages/llm/llm/src/index.ts:39`](../../packages/llm/llm/src/index.ts)
Source: [`packages/llm/llm/src/index.ts:40`](../../packages/llm/llm/src/index.ts)
## `session/*`
@@ -245,7 +245,7 @@ Creation announcement during session publication. A synchronous throw vetoes and
'session/created'(this: Scoped<Session>, session: Session): void
```
Source: [`packages/core/session/src/index.ts:47`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:46`](../../packages/core/session/src/index.ts)
### `session/disposed` — emit
@@ -255,7 +255,7 @@ Emitted once when an announced session leaves the store, including publication r
'session/disposed'(this: Scoped<Session>, session: Session): void
```
Source: [`packages/core/session/src/index.ts:57`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:56`](../../packages/core/session/src/index.ts)
### `session/event` — emit
@@ -267,7 +267,7 @@ Post-commit, fire-and-forget append feed. The listener snapshot resolves before
Types: [SessionEvent](../core-data-structures/core.md)
Source: [`packages/core/session/src/index.ts:69`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:68`](../../packages/core/session/src/index.ts)
### `session/flush` — parallel
@@ -277,7 +277,7 @@ Awaited parallel durability checkpoint: every listener runs and the caller await
'session/flush'(this: Scoped<Session>, session: Session): Promise<void> | void
```
Source: [`packages/core/session/src/index.ts:79`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:78`](../../packages/core/session/src/index.ts)
## `subagent/*`
@@ -469,14 +469,14 @@ Source: [`packages/workflow/workflow/src/index.ts:45`](../../packages/workflow/w
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))
- `internal/service` — Interception hook for a service binding (no core producer). ([`vendor/cordis/src/events.ts:199`](../../vendor/cordis/src/events.ts))
- `internal/update` — Waterfall: a fiber config update is being applied. ([`vendor/cordis/src/events.ts:200`](../../vendor/cordis/src/events.ts))
- `internal/get` — Waterfall: a service is being read from the store. ([`vendor/cordis/src/events.ts:201`](../../vendor/cordis/src/events.ts))
- `internal/set` — Waterfall: a service is being written to the store. ([`vendor/cordis/src/events.ts:202`](../../vendor/cordis/src/events.ts))
- `internal/listener` — A listener was registered. ([`vendor/cordis/src/events.ts:203`](../../vendor/cordis/src/events.ts))
- `internal/dispatch` — An event is being dispatched to listeners. ([`vendor/cordis/src/events.ts:204`](../../vendor/cordis/src/events.ts))
- `internal/plugin` — A plugin fiber was created. ([`vendor/cordis/src/events.ts:328`](../../vendor/cordis/src/events.ts))
- `internal/status` — A fiber changed lifecycle state. ([`vendor/cordis/src/events.ts:330`](../../vendor/cordis/src/events.ts))
- `internal/service` — Interception hook for a service binding (no core producer). ([`vendor/cordis/src/events.ts:332`](../../vendor/cordis/src/events.ts))
- `internal/update` — Waterfall: a fiber config update is being applied. ([`vendor/cordis/src/events.ts:334`](../../vendor/cordis/src/events.ts))
- `internal/get` — Waterfall: a service is being read from the store. ([`vendor/cordis/src/events.ts:336`](../../vendor/cordis/src/events.ts))
- `internal/set` — Waterfall: a service is being written to the store. ([`vendor/cordis/src/events.ts:338`](../../vendor/cordis/src/events.ts))
- `internal/listener` — A listener was registered. ([`vendor/cordis/src/events.ts:340`](../../vendor/cordis/src/events.ts))
- `internal/dispatch` — An event is being dispatched to listeners. ([`vendor/cordis/src/events.ts:342`](../../vendor/cordis/src/events.ts))
- `hmr/change` — A watched source file changed on disk. ([`vendor/hmr/src/index.ts:20`](../../vendor/hmr/src/index.ts))
- `hmr/reload` — Plugins are being reloaded after a change. ([`vendor/hmr/src/index.ts:21`](../../vendor/hmr/src/index.ts))
- `exit` — The process is exiting on a signal. ([`vendor/loader/src/index.ts:23`](../../vendor/loader/src/index.ts))

View File

@@ -85,7 +85,21 @@ abstract start(spec: BashExecSpec): BashProcess
Types: [BashExecRequest](../core-data-structures/bash.md) · [BashExecSpec](../core-data-structures/bash.md) · [BashRunResult](../core-data-structures/bash.md)
Source: [`packages/bash/bash/src/index.ts:46`](../../packages/bash/bash/src/index.ts)
Source: [`packages/bash/bash/src/index.ts:49`](../../packages/bash/bash/src/index.ts)
## `ctx.bashEnv` — `BashEnvRegistry`
Registry (`ctx.bashEnv`) for trusted, per-execution `DSH_*` variables. The namespace is rebuilt for every model bash call: ambient `DSH_*` values are discarded by the executor, then the registry's current snapshot is injected. Built-in shell facts remain owned by the registry itself while plugins can register additional, enumerable facts with effect-scoped disposal.
```ts cordis-catalog
register(contributor: BashEnvContributor): () => void
collect(execution: ToolExecution): DshEnvironment
list(): BashEnvVariableInfo[]
```
Types: [ToolExecution](../core-data-structures/tools.md)
Source: [`packages/bash/tool-bash/src/index.ts:102`](../../packages/bash/tool-bash/src/index.ts)
## `ctx.codeRuntime` — `CodeRuntime` (abstract seam)
@@ -101,7 +115,7 @@ Source: [`packages/code-runtime/code-runtime/src/index.ts:30`](../../packages/co
## `ctx.compact` — `CompactService` (abstract seam)
Abstract compaction service. Implementations own token estimation, retention, and summarization, but a successful run must replace the selected surface span with one summary node and prevent concurrent compaction of the same session. Load one implementation per context as `ctx.compact`.
Abstract compaction service. Implementations own trigger policy, retention, and summarization, and may consume a separate measurement service. A successful run replaces the selected surface span with one summary node and prevents concurrent compaction of the same session. Load one implementation per context as `ctx.compact`.
```ts cordis-catalog
abstract compactIfNeeded( agent: CompactAgentContext, fullSystemPrompt: string, sessionPrefix: readonly Message[], signal: AbortSignal, ): Promise<CompactionResult | null>
@@ -110,15 +124,16 @@ abstract compactRegion( session: Session, start: number, end: number, agent: Com
Types: [Message](../core-data-structures/core.md)
Source: [`packages/compact/compact/src/index.ts:36`](../../packages/compact/compact/src/index.ts)
Source: [`packages/compact/compact/src/index.ts:38`](../../packages/compact/compact/src/index.ts)
## `ctx.fs` — `FileSystem` (abstract seam)
Abstract filesystem provider. Targets must preserve identity across aliases; reads expose regular UTF-8 text or typed errors, listings are stable and content-free, and mutations are atomic. Optional guards add stale protection without changing the unguarded provider contract.
```ts cordis-catalog
abstract resolve(path: string, opts?: { cwd?: string }): Promise<FsTarget>
abstract resolve(path: string, opts?: { cwd?: string; signal?: AbortSignal }): Promise<FsTarget>
abstract stat(target: FsTarget, signal?: AbortSignal): Promise<FsInfo | undefined>
abstract lstat(path: string, opts?: { cwd?: string }, signal?: AbortSignal): Promise<FsPathInfo | 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[]>
@@ -128,21 +143,22 @@ abstract editText(target: FsTarget, edit: FsEditRequest, expected?: { version: F
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:78`](../../packages/fs/fs/src/index.ts)
Source: [`packages/fs/fs/src/index.ts:80`](../../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[]
registerAdapter(providers: string[], adapter: LlmAdapter): () => void
listProviders(): LlmProviderInfo[]
async listModels(provider: string): Promise<LlmModelInfo[]>
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:75`](../../packages/llm/llm/src/index.ts)
Source: [`packages/llm/llm/src/index.ts:96`](../../packages/llm/llm/src/index.ts)
## `ctx.permission` — `PermissionService`
@@ -176,6 +192,7 @@ Source: [`packages/sandbox/sandbox/src/index.ts:111`](../../packages/sandbox/san
Durable append-only session storage. Implementations preserve contiguous, losslessly JSON-serializable events; append resolves only after durability, and load balances a complete interrupted tail without rewriting committed events.
```ts cordis-catalog
abstract locate(meta: SessionHeader): SessionLocation | undefined
abstract create(meta: SessionHeader): Promise<void>
abstract append(id: SessionId, events: readonly SessionEvent[]): Promise<void>
abstract load(id: SessionId): Promise<{ meta: SessionHeader; events: SessionEvent[] }>
@@ -184,19 +201,21 @@ abstract list(): Promise<SessionHeader[]>
Types: [SessionEvent](../core-data-structures/core.md)
Source: [`packages/session-persistence/session-persistence/src/index.ts:30`](../../packages/session-persistence/session-persistence/src/index.ts)
Source: [`packages/session-persistence/session-persistence/src/index.ts:42`](../../packages/session-persistence/session-persistence/src/index.ts)
## `ctx.sessionQuery` — `SessionQueryService`
Live-preferred logical-corpus and exact-event read service.
Live-preferred logical-corpus exact-read and relationship-tracing service.
```ts cordis-catalog
listSessions(): Promise<SessionRecord[]>
async listEvents(sessionId: SessionId): Promise<SessionEventRecord[]>
async traceSession(sessionId: SessionId): Promise<SessionLineageTrace>
async traceEvent(request: SessionEventTraceRequest): Promise<SessionEventTrace>
async readEvent(request: SessionEventReadRequest): Promise<SessionEventWindow>
```
Source: [`packages/session-query/session-query/src/index.ts:35`](../../packages/session-query/session-query/src/index.ts)
Source: [`packages/session-query/session-query/src/index.ts:38`](../../packages/session-query/session-query/src/index.ts)
## `ctx.sessions` — `SessionStore`
@@ -215,7 +234,7 @@ list(): Session[]
fork(source: SessionForkSource, boundary?: number, childSessionId?: SessionId): Session
```
Source: [`packages/core/session/src/index.ts:564`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:580`](../../packages/core/session/src/index.ts)
## `ctx.skills` — `SkillService`
@@ -230,6 +249,22 @@ async get(name: string, options: SkillLookupOptions = {}): Promise<SkillDefiniti
Source: [`packages/skill/skill/src/index.ts:141`](../../packages/skill/skill/src/index.ts)
## `ctx.spillStore` — `SpillStore` (abstract seam)
Abstract spill storage service. Subclass, implement saveText, and load the subclass as a plugin — it registers as `ctx.spillStore` (one implementation per context; loading a second throws, cordis' standard duplicate-service behavior).
Semantics every implementation must honor:
- saveText persists the FULL `content` verbatim and returns an opaque locator, exact byte length, and model-facing retrieval guidance.
- Storage is scoped by the request's SaveTextSpill.owner session; the backend chooses a private (not world-readable) location and a collision-free name derived from — never equal to — the caller's `suggestedName`.
- `saveText` REJECTS on a real storage failure (permissions, ENOSPC, backend unavailable); the caller decides how to degrade (the spill policy treats a rejection as best-effort and keeps the inline result).
```ts cordis-catalog
abstract saveText(input: SaveTextSpill): Promise<SpillRef>
```
Source: [`packages/spill/spill/src/index.ts:45`](../../packages/spill/spill/src/index.ts)
## `ctx.subagents` — `SubagentService`
Named provider registry and capability-checked start surface.
@@ -275,6 +310,19 @@ Types: [Agent](../core-data-structures/core.md)
Source: [`packages/tasks/tasks/src/index.ts:76`](../../packages/tasks/tasks/src/index.ts)
## `ctx.tokenMeter` — `TokenMeterService`
Replay owner for one service-wide estimator and isolated per-session folds.
```ts cordis-catalog
measure(session: Session, requestHeader?: EpochHeader): TokenMeasurement
estimateMessage(message: Message): number
```
Types: [Message](../core-data-structures/core.md)
Source: [`packages/llm/token-meter/src/index.ts:106`](../../packages/llm/token-meter/src/index.ts)
## `ctx.tools` — `ToolRegistry`
Tool registry and execution pipeline. Scoped registrations shadow globals; one visibility resolver feeds presentation, lookup, and dispatch.
@@ -290,7 +338,7 @@ async execute(exec: ToolExecutionInput): Promise<ToolExecutionResult>
Types: [ToolDefinition](../core-data-structures/tools.md) · [ToolExecutionInput](../core-data-structures/tools.md) · [ToolExecutionResult](../core-data-structures/tools.md)
Source: [`packages/core/tools/src/index.ts:363`](../../packages/core/tools/src/index.ts)
Source: [`packages/core/tools/src/index.ts:378`](../../packages/core/tools/src/index.ts)
## `ctx.userInteraction` — `UserInteractionService`
@@ -339,12 +387,12 @@ Source: [`packages/workflow/workflow/src/index.ts:159`](../../packages/workflow/
The framework `ctx` surface every plugin also sees, beyond the harness services above. This is pinned vendor source ([vendoring policy](../../vendor/README.md)); 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`](../../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.on / ctx.once` — Register an event listener (disposable). ([`vendor/cordis/src/events.ts:34`](../../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:34`](../../vendor/cordis/src/events.ts))
- `ctx.plugin / ctx.inject` — Load a plugin / declare required services. ([`vendor/cordis/src/registry.ts:164`](../../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.extend / ctx.isolate / ctx.intercept` — Derive a child context (scoped services / isolation / interception). ([`vendor/cordis/src/context.ts:42`](../../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))