import { ExecutionContext } from '@theokit/http'; import { c as AgentOptions, T as ToolOptions, P as ProjectContextOptions, H as HumanInTheLoopOptions, R as ReasoningEffort, M as McpServersMap } from './types-DdltPCj2.js'; import { C as CompiledAgentOptions, A as AgentDefinition, a as CompiledTool, G as Guardrail, m as SkillsSelection, s as SubagentAgentDefinition, H as HookApprovalGate, t as CodePlugin } from './agent-compiler-BMpuJFAI.js'; import { SkillsSettings, ContextSettings, SystemPromptResolver, ModelSelection, PluginsSettings, Plugin, ProviderRoutingSettings, AgentDefinition as AgentDefinition$1, BudgetTracker, CustomTool, RunEventSink, MemorySettings, TelemetrySettings, PermissionRule } from '@theokit/sdk'; import { S as StreamEvent, f as ApprovalRequiredEvent, a5 as MaskError } from './delegation-scoring-CURe4oSL.js'; import { RetryOptions } from '@theokit/sdk/retry'; import { SandboxPosture } from '@theokit/sdk/sandbox'; import { z } from 'zod'; import { H as HookHandlers } from './hook-handlers-Cw2FsnE5.js'; import { S as SettingSourcesSelection } from './setting-sources-gate-BJJiKq33.js'; import { WireChunk } from '@theokit/presenter/wire'; import { TheokitAgentError } from '@theokit/sdk/errors'; /** * AgentExecutionContext — extends http-decorators' ExecutionContext with agent-specific methods. * * Per ADR D2 (LSP): any guard that accepts ExecutionContext works unchanged with * AgentExecutionContext. Agent-specific guards can narrow via isAgentContext(). */ interface AgentRunInfo { id: string; startedAt: Date; } interface AgentExecutionContext extends ExecutionContext { /** The agent's scalar configuration. */ getAgent(): AgentOptions; /** The current run information. */ getRun(): AgentRunInfo; /** The tool being called, or null if in the main agent handler. */ getToolCall(): ToolOptions | null; /** Type guard — always true for AgentExecutionContext. */ isAgentContext(): true; } /** Create an AgentExecutionContext from a base ExecutionContext + agent state. */ declare function createAgentExecutionContext(base: ExecutionContext, agent: AgentOptions, run: AgentRunInfo, toolCall?: ToolOptions): AgentExecutionContext; /** Narrow an ExecutionContext to AgentExecutionContext if it is one. */ declare function isAgentContext(ctx: ExecutionContext): ctx is AgentExecutionContext; /** * M8-3 — compile `@Skills` metadata into the SDK's `SkillsSettings`. * * Per sdk-runtime.md: the bridge COMPILES decorator metadata into a format the * SDK accepts; the SDK is the runtime. `Agent.create({ skills })` natively * discovers `.theokit/skills//SKILL.md` files and injects the `` * block — so giving `@Skills` runtime is a pure shape translation, not a * reimplementation of discovery. * * Mapping (ADR D4): * - `{ include }` → `{ enabled: include, autoInject: true }` * - `{ autoDiscover: true }` → `{ autoInject: true }` (enabled omitted ⇒ the SDK * enables every discovered skill) */ /** * M53 — the options shape lives WITH its conversion now. It used to be declared on the decorator * that is being deleted, which would have taken a type the compiler needs down with it. */ interface SkillsOptions { /** Skill names to include (resolved from `.theokit/skills//SKILL.md`). */ include: string[]; /** Auto-discover every skill under `.theokit/skills/` (default: false). */ autoDiscover?: boolean; } declare function compileSkills(options: SkillsOptions): SkillsSettings; /** * M8-1 — compile `@ContextWindow` metadata into the SDK's `ContextSettings`. * * Per sdk-runtime.md, the SDK owns session/transcript storage + compaction. * The bridge therefore maps the one knob the SDK natively exposes as a budget — * `maxTokens` → `ContextSettings.maxTokens` — and reports the strategy knobs that * have NO native `AgentOptions` equivalent (`compactionStrategy`, `preserveLastN`, * `preserveToolResults`, `preserveSystemPrompt`). Those are surfaced as * `metadataOnlyKnobs` so the walk can emit an honest `metadata-only` warning * (ADR D2) instead of silently dropping them (G10 — honest enforcement). */ /** * M53 — declared here, with its conversion, instead of on the decorator being deleted. */ interface ContextWindowOptions { /** Maximum tokens before compaction triggers. */ maxTokens?: number; /** * Per-file truncation cap in CHARACTERS (SDK default 40 000, ~10k tokens). Larger files are * truncated head/tail with a marker. * * Reachable here because the surface that turns instruction discovery on is the same surface that * decides when an instruction file gets cut: a 60 000-character `CLAUDE.md` was silently * truncated to 40 000, and the only knob on offer was named after tokens. */ maxBytesPerFile?: number; /** * Aggregate cap across all context files in CHARACTERS (SDK default 120 000). Past it, * lower-priority sources are DROPPED — not truncated. */ maxBytesTotal?: number; } interface CompiledContextWindow { /** SDK-shaped context budget passed to `Agent.create({ context })`. */ context: ContextSettings; /** Declared knobs the SDK does not expose — reported, never silently dropped. */ metadataOnlyKnobs: string[]; } declare function compileContextWindow(options: ContextWindowOptions): CompiledContextWindow; /** * M8-2 — compile `@ProjectContext` metadata into an SDK `SystemPromptResolver`. * * There is no native `AgentOptions` field that carries a repo map, so the bridge * composes the documented `systemPrompt` seam (`string | SystemPromptResolver`): * the resolver prepends `buildEnvContext` + `buildRepoMap` (`@theokit/sdk-tools`) * and the nearest `THEO.md` (`@theokit/sdk/project#readProjectInstructions`) to * the agent's base prompt (ADR D3). The SDK primitives are dynamically imported so they stay out of * the eager module graph — only loaded when a `@ProjectContext` agent actually sends. (The original * reason given here was "so `@theokit/sdk-tools` stays an OPTIONAL peer"; it is a hard dependency of * this package, and `tools-entry.ts` re-exports it statically. The laziness is still worth keeping, * for the reason above.) * * Knobs with no primitive mapping (`indexStrategy`, `relevanceStrategy`, * `maxFilesInContext`, `includeExtensions`, `rootMarkers`) are reported via * {@link projectContextMetadataOnlyKnobs} so the walk warns honestly (G10). * Only `ignorePatterns` is forwarded (to `buildRepoMap`). */ declare function projectContextMetadataOnlyKnobs(options: ProjectContextOptions): string[]; /** * Build a `SystemPromptResolver` that prepends env + repo map + project * instructions to `base`. `base` may itself be a {@link SystemPromptResolver} * (V4-L.1, ADR D2): it is resolved once with the same `promptCtx` and composed * (resolve-then-prepend). A failing base resolver propagates (fail-loud). When the * SDK provides no `cwd`, the resolver returns the resolved base unchanged (no * filesystem guess — keeps `packages/agents/src` free of direct Node `process` * access per G8; the repo map needs a real cwd). */ declare function compileProjectContext(options: ProjectContextOptions, base?: string | SystemPromptResolver): SystemPromptResolver; /** * Auto-generate HTTP routes from @Agent metadata — Web Standard. * * Per ADR D5: an agent's `route` auto-generates two endpoints: * - POST {route}/chat — send message, receive SSE stream * - GET {route}/runs/:runId — get run status/result * * Routes are convention-based, generated at registration time. */ interface AgentRoute { method: 'POST' | 'GET'; path: string; handler: (request: Request) => Promise; } interface AgentRouteContext { /** * M53 — only the mounting `route` was ever read from the walk, so the generator declares that * instead of the whole `AgentWalkResult` (which chained it to the decorator metadata walk). * `AgentWalkResult` satisfies this structurally, so the decorator path is unchanged. */ walkResult: { route: string; }; compiledOptions: CompiledAgentOptions; createRun: (message: string, sessionId: string) => AsyncIterable; getRun?: (runId: string) => Promise<{ id: string; status: string; result?: string; } | null>; } /** Generate HTTP routes for a single agent. Returns Web Standard handlers. */ declare function generateAgentRoutes(ctx: AgentRouteContext): AgentRoute[]; /** * M4 (theokit-ai-first) — the HITL producer: a `pre_tool_call` plugin that pauses the SDK run * for a human-in-the-loop tool approval. * * ADR 0038: this is the ADAPTER seam — it makes the shipped-but-dead `@HumanInTheLoop` decorator * FUNCTIONAL by wiring the SDK's OWN async `pre_tool_call` veto hook (which the SDK loop `await`s, * so returning a pending Promise genuinely PAUSES the run) to a human approval. It calls no LLM, * dispatches no tool, and runs no second loop — the SDK owns all of that. * * Flow: gated tool about to run → emit an `ApprovalRequiredEvent` (which the translator maps to * the ai-sdk `tool-approval-request` chunk) → `await awaitApproval(approvalId)` (resolved by the * out-of-band approve route) → allow (`undefined`) or veto (`{ block, message }`, which the SDK * surfaces as a tool result the model self-corrects on). */ /** * M20 — a settled HITL decision. Structural mirror of the harness's `ApprovalDecision` (the agents * package must not import from `theokit` — dependency direction). `awaitApproval` may resolve a bare * boolean (legacy) OR this object; the plugin normalizes both. */ interface HitlDecision { approved: boolean; reason?: string; payload?: unknown; /** * What settled this, when it was not a person (usetheokit/theokit#393). * * Mirrors `ApprovalDecision.settledBy` in `theokit`, for the reason the comment above this * interface gives: this package must not import from `theokit`. Absent means the decision came * through the approve route; `'timeout'` means the window closed with nobody deciding. */ settledBy?: 'timeout'; } /** Injected wiring — the harness (mount-agent) supplies these; the plugin stays pure. */ interface HitlWiring { /** Tool name → its `@HumanInTheLoop` config. A tool absent here is NOT gated. */ gated: Map; /** Push the approval-required event into the agent stream (the translator emits the chunk). */ emit: (event: ApprovalRequiredEvent) => void; /** * Await the human decision for `approvalId`; resolves approve/deny (or timeout). M20 — may resolve * a bare boolean (legacy) OR a {@link HitlDecision} carrying an approver `reason` + `payload`. */ awaitApproval: (approvalId: string, opts: HumanInTheLoopOptions, toolName: string) => Promise; /** * OPTIONAL — reach the owner off the stream (usetheokit/theokit#458). * * `emit` above puts the pause into the run's own event stream, and that was the only egress: a * caller not currently consuming it never learned the run was waiting, so the framework's * asynchronous promise held exactly as long as someone was watching. * * This receives the same facts the stream carries and does whatever the APPLICATION does — Slack, * email, a row in a table. Deliberately not a `@theokit/gateway` dependency: this package must not * import from it, and picking a channel is a policy decision the framework does not get to make. * * Fire-and-forget by contract. The run does not wait for it and does not fail on it: a dispatch * outage must not decide whether a gated tool runs, and must not delay the pause it announces. */ onApprovalRequired?: (request: ApprovalRequest) => void | Promise; } /** What an application is told when a gated tool pauses. The stream's facts, off the stream. */ interface ApprovalRequest { approvalId: string; toolName: string; question: string; input: unknown; /** Relative, exactly as the stream carries it — the app knows its own base URL; this does not. */ callbackUrl: string; timeoutMs: number; } /** * M96 U1 — the approval posture: what a surface does when a gated tool asks for approval. * * ## Why a closed type, and why mandatory * * The defect this module closes was one of TYPE, not of behaviour. The "no HITL" posture was not * representable as a value, so it was expressed as ABSENCE — and an absence has no exhaustive * `match`, appears in no log, and fails no test. `toAgentFactory` compiled the `compiled.hitl` map * that `.approvals({…})` produces and threw it away, with the discard admitted in writing in the * JSDoc, while the sibling bridge REFUSED for the same definition. * * The permissive posture remains entirely expressible — as a NAMED VALUE, with a written reason. * What stops existing is the omission. * * ## Os peers * * `codex` models the posture as a four-variant `enum AskForApproval` in the protocol crate, and * makes forgetting it a compile error through the `ToolRuntime: Approvable + Sandboxable` supertrait; * a mandatory field is the closest this type system gets to that. * * Omission resolves in the safe direction — never auto-approved — and the human's stand-in is named * rather than inferred from the absence of one. */ /** The map of gated tools that `compileAgentDefinition` produces (`compiled.hitl`). */ type GatedTools = ReadonlyMap; /** * What this surface does when a gated tool asks for approval. Four variants, none of them * "omission" — each with a concrete consumer and a written reason. */ type ApprovalPosture = { /** There is a human: the request is EMITTED and execution pauses until the decision arrives. */ kind: 'interactive'; /** * Pushes the `ApprovalRequiredEvent` into the surface's stream. MANDATORY: `toAgentFactory` * returns a raw handle, with no stream and no translator, so the sink comes from whoever passes * the posture — which is precisely the surface that owns the stream. A no-op default would * bring the silent discard back through the back door: the request would be "emitted" nowhere. */ emit: (event: ApprovalRequiredEvent) => void; /** Resolves the human decision; execution stays paused until this settles. */ awaitApproval: (approvalId: string, opts: HumanInTheLoopOptions, toolName: string) => Promise; } | { /** Nobody asks and the tool runs. Legitimate only when something else confines the execution. */ kind: 'auto-approve'; /** * M77 — the EVIDENCE that something else confines the execution. Not optional, and not a * string. * * This is the most consequential decision a coding agent makes — "run commands without * asking" — and until M77 its type asked only for a `reason: string`. A string is * unverifiable at the seam: nothing could tell "confined by bwrap, kernel-enforced" from * "I'm sure it's fine". So the consumer implemented the refusal itself, TWICE * (`shouldAutoApprove` in the TUI, `resolveHeadlessApproval` in the headless path), with the * same rule in both — an absent posture counts as unconfined. A security rule duplicated * across two call sites is a rule that will eventually disagree with itself (G12). * * `SandboxPosture` is the SDK's own honest answer to "am I kernel-enforced right now?", and * it carries `detail` so a refusal can say WHY rather than just "unconfined". Requiring it * makes the unconfined case unrepresentable at the type level; {@link applyPosture} refuses * it at runtime for the caller who casts past the type. */ confinedBy: SandboxPosture; reason: string; } | { /** Nobody asks and the tool does NOT run — the safe reading of omission (codex `GranularApprovalConfig`). */ kind: 'auto-reject'; reason: string; } | { /** * The gate is driven by the SERVING SURFACE (the ACP client, through the protocol itself), so * the layer installs no plugin at all. It is a bypass — but a NAMED bypass, with a written * reason, that shows up in the `match`, shows up in logs and can be counted by a gate. What it * replaces is worse: today the same effect happens by omission, with none of that. */ kind: 'owned-by-surface'; reason: string; }; /** * Writes the plugins the posture requires into `extra` — the object `toAgentFactory` spreads AFTER * `m8` into the `Agent.create` options, and which therefore wins. Plugins already present (from the * agent, via `m8`, and from an override, via `extra`) are preserved: running them over is the M14 * regression that `buildExtraCreateOptions` already carries a guard against. * * The legacy `plugins` shape (an `{ enabled }` object) cannot carry both, so a posture that needs to * install a plugin alongside it is refused LOUD AND CLEAR, rather than resolved by dropping one of * them: silently dropping the approval gate is exactly the class of defect this milestone closes * (Rule 8, fail-closed). */ declare function applyPosture(extra: Record, m8: { plugins?: unknown; }, posturePolicy: ApprovalPosture, gated: GatedTools | undefined): void; /** * A definition, or a THUNK that produces one per session. * * M91 — the `apiKey` line just below has accepted a thunk since M74, added for **exactly** this * reason. The asymmetry was one line wide and cost a lot: with the object shape, trust, hooks, skills * and MCP are frozen at module load. In a `theokit acp` process an IDE keeps open for hours, that * reintroduces the staleness M67 removed by moving construction to the entry point. */ type DefinitionOrThunk = AgentDefinition | ((sessionId: string) => AgentDefinition | Promise); /** * SDK Adapter — bridges @theokit/agents decorators → @theokit/sdk runtime. * * Per rule sdk-runtime.md (UNBREAKABLE): @theokit/sdk is the ONLY agent runtime. * This adapter replaces llm-runner.ts (which called OpenRouter API directly). * * Flow: @Agent decorator → compileAgent() → createSdkAgentStream() → SDK Agent.create() → Run.stream() */ /** * Per-request overrides forwarded into `Agent.create` (V4-L.2 + V4-L.3). Bundled into * one object (rather than positional params) so the per-request surface can grow without * a parameter explosion. Each field is Axis-A SWAP — a value the app holds at call time. */ /** * A multimodal image for a send turn — the SDK's `SDKImage` shape, typed locally because `@theokit/sdk` * is an optional peer (dynamic import). Either a URL or inline base64 `{ data, mimeType }`. */ type BridgeImage = { url: string; } | { data: string; mimeType: string; }; interface RuntimeOverrides { /** Overrides the model for this call (`?? compiled.model ?? default`). */ model?: string | ModelSelection; /** * M35 (multimodal) — images to send alongside the text. When present, the SDK send switches from the * plain-string form to the structured `{ text, images }` form (`SDKUserMessage`). Absent ⇒ the * string path is byte-unchanged (back-compat). */ images?: readonly BridgeImage[]; /** * Per-run extended-thinking effort (`?? compiled.reasoningEffort`). Mapped to the SDK * `ModelSelection.params` so the provider produces reasoning (surfaced as `thinking` StreamEvents). */ reasoningEffort?: ReasoningEffort; /** * Per-run opt-in (`?? compiled.parseThinkTags`): when true, wrap the event stream with the M2 * ``-tag extractor so inline `…` text becomes `thinking` StreamEvents. */ parseThinkTags?: boolean; /** * Per-run opt-in (`?? compiled.stripToolDialect`): when true, strip a leaked Hermes * `` tool-call dialect out of the assistant text stream (theocode#32). */ stripToolDialect?: boolean; /** * Per-run opt-in (`?? compiled.recoverLeakedToolCalls`): when true, enable the SDK chat route's * `extractToolCallsFromContent` so a leaked Hermes `` dialect is recovered * and EXECUTED (theokit#58). Sibling of {@link stripToolDialect}; has effect only when {@link providers} * routes a provider. */ recoverLeakedToolCalls?: boolean; /** Per-run cwd → `Agent.create({ local: { cwd } })` → `SystemPromptContext.cwd`. */ cwd?: string; /** * M7 — per-run run-context override (`?? compiled.runContext`). Injected into every tool * handler's `ctx.context` by `buildSdkTools`. Lets a single request override the agent-level * `defineAgent({ context })` (mirrors `model`/`cwd`). */ runContext?: Record; /** * Per-run plugins (e.g. permission gate selected by request mode). Accepts EITHER * named-plugin discovery settings (`{ enabled: [...] }`) OR an array of code `Plugin` * objects, forwarded RAW through the duck-typed `Agent.create` bridge (mirrors the * @theokit/sdk `AgentOptions.plugins` widen). */ plugins?: PluginsSettings | readonly Plugin[]; /** Per-run provider routing. */ providers?: ProviderRoutingSettings; /** Per-run sub-agent definitions (opts-only; `compiled.agents` stays deferred — ADR D3). */ agents?: Record; /** Per-run SDK budget tracker (inner tool-loop cap; distinct from the loop's USD `budget`). */ budgetTracker?: BudgetTracker; /** * SDK 4.0 (SE40) — root of the native Claude-shaped `.jsonl` session transcript the SDK writes * automatically (`/projects//.jsonl`). The framework threads the * app's project root here (see `mount-agent`) so sessions persist per-app; unset ⇒ SDK default * (`~/.theokit`). Replaces the removed pluggable `conversationStorage` — persistence is now the * SDK's native transcript, not a swappable adapter. * * Forwarded to the SDK as `local.baseDir`, which that package deprecated in its own #301 in favour * of `sessionDir`. It stays `baseDir` on purpose: `sessionDir` does not exist at `^4.52.1`, the * floor this package declares, so switching would type-error on the floor and — worse — silently * send the transcript to the SDK default `~/.theokit` for any consumer resolving there, losing * per-app persistence. Migrating the name is a floor bump, not a rename, and the deprecated field * is still honoured. Attempted and reverted rather than assumed: the typecheck gate caught it. * * **Setting this also relocates the agent's durable MEMORY**, which the SDK's own docstring for * the field denies ("Only transcripts are written here"). `MEMORY.md` moves from * `/.theokit/memory/` to `/projects//memory/`, while `.index/` and * `sessions/` stay behind — so the store looks alive and the capture looks broken. Since the * mount sets this unconditionally, it applies to every agent the framework serves. * * Measured against `@theokit/sdk` 4.62.0 and filed as usetheokit/theokit-sdk#463; the downstream * report is usetheokit/theokit#557. Documented here rather than worked around because one SDK * field governs both roots — there is no way to place them separately from this side. */ baseDir?: string; /** * V4-Q: pre-built SDK `CustomTool[]` forwarded RAW to `Agent.create.tools` (appended after the * compiled tools), bypassing `defineTool` (which requires a Zod schema). Lets an app whose tools * come from imperative SDK factories supply them without the `@Tool` compile path. */ sdkTools?: readonly CustomTool[]; /** * theokit#132 — sink for the SDK's typed `RunEvent`s (`tool_progress`, `rate_limit`, * `permission_denied`, `task_*`, `compact_boundary`, `tripwire`, `completion_check`), threaded * straight to `SendOptions.onRunEvent`. * * Threaded, NOT multiplexed into the UIMessage stream. These are run diagnostics for an observer — * theokit-studio's event inspector is the demand that opened the issue — and folding them into the * message protocol would force every consumer of the chat stream to know about frames it has no * use for. Threading leaves the chat path byte-identical for anyone who does not opt in, which is * why the key is omitted entirely rather than set to `undefined` when absent. */ onRunEvent?: RunEventSink; /** * theokit#474 — per-turn transient retry, opt-in. * * When set, the START of the turn (the SDK handshake plus its first event, before anything is * yielded) is wrapped in the SDK `Retry`, so a 429/5xx/network blip that kills the turn before it * produced anything is recovered instead of ending it. Absent ⇒ a single attempt, and the key is * omitted from every downstream call, so the stream is byte-identical to before. * * See `turn-retry.ts` for why a rejection-shaped retry would have been inert on this path, and * for the invariant that closes the retry window on the first event. */ retry?: RetryOptions; /** * theokit#475 — expose the run's REAL token usage to tool handlers as `ctx.usage`, opt-in. * * When true, the adapter installs a {@link RunUsageMeter} as the run's `budgetTracker` (wrapping * the caller's own, when there is one) and hands every tool handler a snapshot of what the * provider has reported so far. A tool can then answer "how much context is left?" from a * measurement instead of a character-count estimate. See `usage/run-usage.ts`. * * Opt-in rather than always-on because installing a `budgetTracker` changes what `Agent.create` * receives for every run that never asked for it, and the back-compat floor here is a floor, not * a preference. Absent ⇒ handlers are wrapped exactly as before (`ctx.usage` does not exist) and * the SDK receives no tracker it was not already given. */ exposeUsageToTools?: boolean; } declare function createSdkAgentStream(compiled: CompiledAgentOptions, compiledTools: CompiledTool[], apiKey: string | (() => string | Promise), overrides?: RuntimeOverrides): ((message: string, sessionId: string, factoryOpts?: { disableTools?: boolean; }) => AsyncIterable) & { resolvedModel: string | ModelSelection; }; /** * The minimal `SDKAgent` surface a serving host needs (ACP checks `agentId: string` + `send: fn`). * `Agent.getOrCreate` returns the real SDK agent — this alias just types what {@link toAgentFactory} * hands back without re-exporting the full `@theokit/sdk` `Agent` type. */ /** * The minimum a turn returns. Structural on purpose (M91's ADR-2). * * Re-exporting the SDK's type would tie this layer's public signature to theirs — the opposite of * what the boundary exists to do, and the same reason `SdkAgentHandle` was already an alias rather * than a * re-export. */ interface SdkTurnHandle { wait: () => Promise<{ result?: string; usage?: { totalTokens?: number; }; }>; } /** Per-turn options. Open for now — the SDK accepts more than the layer needs to declare. */ type SdkSendOptions = Record; interface SdkAgentHandle { readonly agentId: string; /** * B-062 — a tool-using run that returns schema-validated output. * * The SDK's `agent.generate(input, { output })` "runs the agent's NORMAL tool loop (the user's * tools run first) and then coerces the final answer into a Zod schema". It existed and this * handle did not declare it, so a consumer of `@theokit/agents` could reach it only by importing * `@theokit/sdk` directly — the one thing this layer's doctrine forbids. * * The survey that filed this concluded the capability was absent, having measured `generateObject` * — which is the TOOLLESS path by design: it builds a transient agent whose only tool is the * synthetic output tool. Both paths exist and answer different questions. * * OPTIONAL, because a caller-injected handle (tests, a custom transport) need not implement it, * and requiring it would break every such double to add a method most of them never call. * * BOTH wrappers below forward it. A wrapper that rebuilds the handle silently removes every method * it does not name, and the consumer meets the loss at runtime as "the handle has no `generate`" * after the types said it had one. Each forward is pinned by its own mutation. * * It is NOT put through the output guards, stated rather than assumed: `generate` resolves to a * validated OBJECT and those guards moderate TEXT, so running them over a serialised object would * moderate a shape no guard was written against. Guarding the structured path needs its own * decision about what a redaction means to a schema, and inventing one would be the gate that * reports without gating this module already refuses to build. */ readonly generate?: (message: string, options: { output: unknown; [key: string]: unknown; }) => Promise; /** * M91 — era `(msg: string, opts?: unknown) => unknown`. * * The `unknown` return cost the consumer a whole module: `agents/lib/goal/runner-facade.ts`, 38 * lines whose only job was to re-narrow this return into the `send → wait` contract the goal loop * requires. That module's docstring records that, before it, the caller wrote `as never` — and that * **it was under that cover that the goal surface diverged from the real agent for several * milestones**. The layer always knew the shape; it simply did not declare it. * * The shape is **asynchronous**: `SDKAgent.send` returns `Promise`, and the SDK's * `GoalLoopAgent` declares `send(prompt): Promise<{ wait(): Promise<…> }>`. This milestone's first * attempt typed it as synchronous and the consumer's `tsc` would not have caught it — the facade's * `as never` absorbed the difference. It is literally the divergence the facade's docstring * described, met again while trying to remove it. */ send: (msg: string, opts?: SdkSendOptions) => Promise; dispose: () => Promise; } /** * #12 — bridge a builder/`defineAgent` {@link TheokitAgentDefinition} to a real `SDKAgent` FACTORY, * so surfaces that require an `SDKAgent` (or `(sessionId) => SDKAgent`) — notably `theokit acp`, * whose entry default-export must be one — can serve an agent defined with the `AgentBuilder.create()` chain. * * The factory reuses the SAME projection the streaming path uses (`compileAgentDefinition` → tools / * model / `assembleM8CreateOptions`), so the served agent has identical tools, model, system prompt, * skills, and `mcpServers`. Keyed per `sessionId` via `Agent.getOrCreate` (matches the run path). * * M96 — the surface DECLARES its {@link ApprovalPosture} and the factory installs the plugin the * variant requires. Before, the `compiled.hitl` map was compiled and DISCARDED here. Rationale in * `./approval-posture.ts`. */ declare function toAgentFactory(def: DefinitionOrThunk, opts: { apiKey: string | (() => string | Promise); approvals: ApprovalPosture; overrides?: RuntimeOverrides; }): (sessionId: string) => Promise; /** * T2.1 — "may I auto-approve this tool right now?", as a symbol a surface can call. * * ## Why this exists next to `approval-posture.ts` rather than inside it * * {@link applyPosture} answers a different question: *"construct this agent with a gate?"*. It runs * once, at factory time, mutating an options bag. A surface asks per EVENT, before it decides * whether to render a prompt, and it has no options bag in hand. * * Until this function existed the only reachable half of the rule was the type. `approval-posture.ts` * says in writing that the consumer implemented the refusal twice and calls that a G12 violation — * and it was still true when this was written, because the enforcement stayed private. A rule that * lives in an unexported function lives once for the framework and zero times for everyone else. * * ## The invariant, stated once * * **Nothing auto-approves without positive evidence of enforced confinement**, or the operator's * explicit `danger-full-access` waiver (#939). Two consumer defects reduce to it: * * - *B-006* — an absent posture counts as unconfined. Absence of evidence is not evidence of * confinement, and defaulting the other way silently disables the guard anywhere the posture has * not been threaded through. * - *B-021* — the headless path made the posture a required argument precisely because omitting it * returned "approved" for `full-auto`, skipping the refusal the function exists for. * * `posture` stays OPTIONAL in the type so a surface mid-migration still compiles; it just gets * `false`. Making it required would be the stricter type and the worse outcome — a caller who cannot * yet supply it writes its own predicate instead, which is how the duplication started. * * An omitted posture resolves to the conservative answer rather than to a permissive one. */ /** * What a surface does about asking, as the three modes a coding agent actually offers. * * Not a framework invention: these are the values the only real consumer put in front of users, and * naming them here is what lets its two copies of the rule become one call. */ declare const APPROVAL_MODES: readonly ["suggest", "auto-edit", "full-auto"]; type ApprovalMode = (typeof APPROVAL_MODES)[number]; /** * Tools whose writes are bounded by their own write root rather than by the kernel. * * A CATALOG, not a policy. The names are the SDK factories' defaults (`apply-patch.ts:51`, * `edit-file.ts:155`, `write-file.ts:86`), so this answers "does this tool confine its own writes?" * — a fact about the SDK. It does NOT answer "may this tool run without asking a human?", which is * the product's to decide and is why {@link shouldAutoApprove} does not read this by default. See * the `auto-edit` branch there for what conflating the two cost. * * Genuinely immutable, not merely typed that way. `ReadonlySet` is erased at runtime, and one * `as Set` on an approval gate reachable from every consumer of this package would widen * what auto-approves everywhere, with no diff in the module that owns the rule. `Object.freeze` * alone does not do it: a Set keeps its entries in internal slots rather than own properties, so * freezing leaves `add` fully functional — the mutators have to be replaced. */ declare const WRITE_SCOPED_TOOLS: ReadonlySet; interface ShouldAutoApproveOptions { /** * Which tools this PRODUCT lets run without asking, in `auto-edit`. * * Required in practice: absent, `auto-edit` approves nothing. {@link WRITE_SCOPED_TOOLS} is * available for a product that wants every write-scoped tool, but passing it is a decision the * product makes, not a default it inherits. */ writeScopedTools?: ReadonlySet; } /** * Whether a gated tool may run without asking a human. * * Pure over its arguments and holds no state, so a surface can call it per event. * * @param mode - what the user chose. * @param toolName - the name the model sees, which is also the approval key. * @param posture - the sandbox's own answer to "am I kernel-enforced right now?". Absent or * unenforced means NOT confined, and nothing auto-approves, unless its `mode` is * `danger-full-access`, the operator's explicit request to run without confinement. */ declare function shouldAutoApprove(mode: ApprovalMode, toolName: string, posture?: Pick & { readonly mode?: string; }, options?: ShouldAutoApproveOptions): boolean; /** * Model-selection mapping (M1 reasoning-visibility) — the single site that turns a * provider-agnostic `ReasoningEffort` into the SDK `ModelSelection`. Kept in its own small module * (not inside `sdk-adapter.ts`) so the adapter stays under the G6 500-LoC budget and the helper has * a focused home (per the plan's T1.1 "or a small sibling"). */ /** * Build the SDK `ModelSelection` for a model id + optional reasoning effort. With no (or empty) * effort it returns the bare `{ id }` — byte-identical to the prior behavior (backward-compat). With * an effort it adds the canonical reasoning param `{ id: PARAM_THINKING, value: effort }`. Pure. */ declare function buildModelSelection(model: string | ModelSelection, effort?: ReasoningEffort): ModelSelection; /** * Read the reasoning effort back out of a model selection — the inverse of * {@link buildModelSelection}, and the reason this module owns the param key. * * Returns `undefined` when there is no effort to read: a bare-string model id (no params to carry * one), a selection without `params`, or params that do not include the reasoning key. Absence is a * DECLARED state, not an exceptional one, so none of those throw. * * A present-but-unrecognised value is returned VERBATIM. Validating it belongs to the caller (which * already has its own parser); hijacking that here would widen the layer's responsibility with * nobody asking. Pure. * * The return is `string`, not {@link ReasoningEffort}, precisely BECAUSE the value is unvalidated: * whatever is in the document comes back, and typing it as the semi-closed union would promise a * check this function deliberately does not perform. Assigning the result to a `ReasoningEffort` is * the caller's decision, after the caller's own parse. */ declare function reasoningEffortOf(model: string | ModelSelection): string | undefined; /** * ``-tag reasoning extractor (M2 reasoning-visibility) — bridge middleware. * * Converts inline `…` in the assistant TEXT stream into `thinking` segments, so * models that emit reasoning as inline tags (qwen3-coder / deepseek-class) surface reasoning the * same way native-reasoning providers do (M1's `reasoningEffort`). Two exports: * - `createThinkTagExtractor` — the pure incremental splitter (handles a tag straddling a chunk * boundary); the testable core. * - `extractThinkTagStream` — a StreamEvent transform that applies the extractor to `text_delta` * events and passes every other event through unchanged (Phase 2). * * Opt-in: wired into `createSdkAgentStream` only when `parseThinkTags` is set (Phase 3) — default * off, because a code assistant can legitimately emit literal `` in answer/code text. * * Reference patterns: Aider `reasoning_tags.py`, Vercel AI SDK `extract-reasoning-middleware.ts` * (blueprint `code-assistant-reasoning-ux` ADR-3). */ /** A typed slice of the text stream: ordinary `text` vs extracted `thinking`. */ interface Segment { kind: 'text' | 'thinking'; content: string; } /** * A pure, stateful, incremental `` splitter. Feed it text chunks via `write`; it returns the * segments it can resolve so far, holding back only a tail that is still a viable prefix of the * active delimiter (so a tag split across two chunks is recognized). Call `end()` once the stream * is done to flush any buffered tail (a truncated `` with no close is flushed as `thinking`, * so reasoning is never silently dropped). * * One instance per stream (per round); never shared — there is no cross-instance state. */ declare function createThinkTagExtractor(): { write: (chunk: string) => Segment[]; end: () => Segment[]; }; /** * Stream transform: convert inline `…` carried in `text_delta` events into `thinking` * events, passing every other event (native `thinking`, `tool_call`, `done`, `error`, …) through * unchanged. A fresh extractor per call ⇒ per-stream (per-round) state; the extractor's mode persists * across interleaved non-text events (a reasoning block split by a tool call is not corrupted). On * source end, the extractor is flushed so a truncated `` is still surfaced (as `thinking`). * * Non-string `text_delta.content` is passed through untouched (defensive — never throws). The * `end()` flush runs in a `finally`, so a buffered unclosed `` is surfaced as `thinking` * even when the source errors mid-stream (the flushed segments are delivered before the error * re-propagates) — never silently dropped (Unbreakable Rule 8: fail loud, lose nothing). */ declare function extractThinkTagStream(source: AsyncIterable): AsyncGenerator; /** * Translates @theokit/sdk SDKMessage events → TheoKit AgentStreamEvent. * * The SDK yields SDKMessage (system/assistant/tool_call/thinking/status). * TheoKit devtools + SSE handler expect AgentStreamEvent (run_started/text_delta/tool_call/done). * This module bridges the two — Adapter pattern (per sdk-integration-blueprint ADR-D2). */ /** Minimal SDK message shape — duck-typed to avoid hard import of @theokit/sdk types. */ interface SdkMessage { type: string; [key: string]: unknown; } /** * Translate a single SDK message to zero or more TheoKit stream events. * Returns an array because one SDK message may map to multiple TheoKit events. */ declare function translateSdkEvent(msg: SdkMessage, runId: string): StreamEvent[]; /** * M8 — `AgentBuilder.create()`, the fluent agent builder with accumulative **type-state**. * * `AgentBuilder.create().context().tool(t).model(id).system(s).build()` accumulates type parameters the way * the most-loved TS DX does (tRPC `t.procedure.input().query()`, Zod, Hono) and resolves to the * **SAME branded `AgentDefinition`** that {@link defineAgent} produces — one runtime, N syntaxes * (ADR-B1). The value is entirely at the type level; `.build()` delegates to `defineAgent`, so * convergence with the `defineAgent` / `@Agent` surfaces is by construction. * * Compile-time guarantees (proven by `tests/type/agent-builder.test-d.ts`): * - `.build()` is a compile error when `.model()` was never called (UnsetMarker technique — tRPC * `utils.ts:2-4` / `procedureBuilder.ts:362-379`). * - `.tool(t)` is a compile error when the tool declares a required run-context ⊄ the agent's `C`. * - tool names accumulate into a union type parameter (`TTools`). * * PURE metadata (sdk-runtime.md / G2): the builder describes an agent, it NEVER calls an LLM. */ /** * A required-but-unset builder field. Branded (a literal intersected with a unique brand) so no * ordinary value can satisfy it — the terminal-method guards below key off it. tRPC's UnsetMarker. */ type UnsetMarker = 'theokit.unset' & { readonly __brand: 'theokit.unset'; }; /** * Compile-error carriers. When a guarded method's precondition fails, its signature demands an * argument of one of these types, which the caller cannot supply — the labeled tuple element * surfaces the reason in the error. (Idiomatic gate for zero-arg terminal methods.) */ interface MissingModelError { readonly __theokitError: 'call .model(id) before .build()'; } interface ToolContextError { readonly __theokitError: 'this tool requires a run-context not provided via .context()'; readonly required: TRequired; } /** The agent context before `.context()` is called — satisfies only tools with no requirement. */ type EmptyContext = Record; /** * The union of literal tool names in a tuple of {@link ContextualTool}s. * * Written as a distributed conditional rather than `TList[number]['name']` so an EMPTY list yields * `never` (which unions away, leaving `TTools` untouched) instead of `string` (which would widen the * accumulated union and silently destroy the type-state for the rest of the chain). */ type ToolNamesOf = TList extends readonly (infer TElement)[] ? TElement extends ContextualTool ? TName : never : never; /** * The combined run-context every tool in the tuple requires. * * The singular `.tool()` guards one `TRequired` against the agent's `TContext`; a list has to guard * ALL of them, so this intersects them. Without it, `.tools([...])` would be a hole in the exact * check `.tool()` performs — a batch API that quietly accepts what the single-item API rejects is * worse than no batch API. * * Intersection, not union: satisfying every element means the agent's context must have what each * one needs. `unknown` (a plain tool) intersects away, so a list of plain tools stays unconstrained. */ type ToolContextOf = (TList extends readonly (infer TElement)[] ? TElement extends ContextualTool ? (required: TRequired) => void : never : never) extends (required: infer TIntersection) => void ? TIntersection : never; /** * A tool that MAY declare, at the type level, the run-context shape it needs. A plain * {@link CustomTool} (`TRequired = unknown`) is satisfied by any agent context. Build one that * carries a literal name + optional required-context via {@link ContextualTool.of}. */ interface ContextualTool extends CustomTool { readonly name: TName; /** Phantom — never present at runtime; carries the required run-context type for `.tool()`. */ readonly __requiredContext?: TRequired; } /** * Static factory for {@link ContextualTool}. M57: OO surface (`ContextualTool.of(...)`) aligned with * the SDK's `X.create()` shape — was the free `contextualTool(...)` function. `ContextualTool` is a * TYPE (the interface above) and a VALUE (this const) at once, the same dual-space pattern the SDK * uses for `Tool`/`Agent`. No instance state, so the factory is `static`-style (`of`), not `create`. */ declare const ContextualTool: { /** * Tag a {@link CustomTool} with a literal name (so `.tool()` can accumulate the tool-name union) * and, optionally, a required run-context type. The `requiredContext` argument is a type-only * witness — pass `undefined as C` or a sample value; it is never read at runtime. */ of(tool: CustomTool & { name: TName; }, _requiredContext?: TRequired): ContextualTool; }; /** * The fluent builder. Each method returns a NEW builder type with the relevant type parameter * advanced (immutable chaining). Runtime state is a plain {@link DefineAgentConfig} accumulator. */ interface AgentBuilder { /** Set the request schema (lifted into the typed client via {@link AgentDefinition}). */ input(schema: S): AgentBuilder; /** * Set the model. Required before `.build()`. COMPILE ERROR when called twice — the argument * type collapses to `never` once the model is set (tRPC's set-once technique). * * M94 — accepts a `ModelSelection` as well as the raw id. The SDK implementation **always** * accepted it (`agent-builder.ts:50`: `model(m: string | ModelSelection)`); it was this facade that * narrowed to `string`, and the narrowing made every field of the selection unreachable — including * the `contextWindow` the SDK had started publishing. A facade diverging from the implementation is * the same class of defect M91 paid two patches to fix. */ model(id: TModel extends UnsetMarker ? string | ModelSelection : never): AgentBuilder; /** Set the static system prompt. */ system(prompt: string): AgentBuilder; /** Set the extended-thinking effort. */ reasoningEffort(effort: ReasoningEffort): AgentBuilder; /** * theokit#363 — cap the agent's tool-calling turns within ONE run. A positive integer; throws at * `.build()` otherwise. Unset ⇒ the SDK's own ceiling (8) applies, unchanged. * * This is the chain the scaffold writes, and until now it had no way to say "stop after N steps": * the declaration existed on the decorator path only, and even there nothing on the served path * read it. See {@link DefineAgentConfig.maxIterations} for why the name is `maxIterations`. */ maxIterations(limit: number): AgentBuilder; /** Set the run-context (M7) — the object every tool handler receives as `ctx.context`. */ context>(value: C): AgentBuilder; /** * Add a tool. Accumulates its name into `TTools`. COMPILE ERROR when the tool declares a * required run-context (`ContextualTool<_, TRequired>`) that the agent's `TContext` does not * satisfy — set it first with `.context()`. */ tool(tool: ContextualTool, ...guard: TContext extends TRequired ? [] : [error: ToolContextError]): AgentBuilder; /** * M69 — add MANY tools at once, accumulating the union of their names. * * The chain only had the singular `.tool()`, so a tool set computed at runtime — the normal case, * since which tools an agent gets depends on sandbox mode, surface profile and trust — could not * be expressed in the chain. The measured workaround was a fold outside it: * * allTools.reduce((acc, tool) => acc.tool(tool), chain) * * That works and loses the type-state: the accumulated name union collapses, so * `InferAgentToolNames` stops seeing the literal names the generated client is built from. The * escape hatch cost exactly the guarantee the builder exists for. * * An empty list is a typed no-op — `never` unions away, so `TTools` is untouched rather than * widened. */ tools(list: TList, ...guard: TContext extends ToolContextOf ? [] : [error: ToolContextError>]): AgentBuilder>; /** * M69 — apply a sub-chain only when `condition` holds, preserving the type-state either way. * * `.use(preset)` composes a whole sub-chain but cannot skip a link in the MIDDLE of one, which is * what a conditional element needs. * * The condition is a plain `boolean`, already computed — never a predicate with access to * context. A predicate would make this a door for business logic inside the authoring chain (the * milestone's named risk); a boolean keeps the decision where the caller made it. * * The returned type is the same on both branches, because the condition is a runtime value and a * type cannot depend on it: the union is what the branch COULD add. That is what lets `.when` sit * mid-chain without collapsing what came before. */ when>(condition: boolean, apply: (builder: AgentBuilder) => TResult): TResult; /** M9 — add one input/output guardrail (appends). Runs at the framework boundary before the SDK. */ guardrail(g: Guardrail): AgentBuilder; /** M9 — set the full guardrail list (replaces any previously added). */ guardrails(gs: readonly Guardrail[]): AgentBuilder; /** M14 — gate one tool behind a HITL approval (merges into the approvals map, keyed by tool name). */ approval(toolName: TTools extends never ? string : TTools, options: HumanInTheLoopOptions): AgentBuilder; /** M14 — set the full approvals map (replaces any previously added). */ approvals(map: Record): AgentBuilder; /** * M13 — the skills the agent can consult mid-turn: a static list OR a per-request * resolver `(ctx) => string[]`. Each entry is a `createSkill(...)` object OR a * filesystem skill NAME (a string) — mix freely. * * Progressive disclosure (cheap by default): every turn the SDK lists each skill's * name + description in a `` block (so the model KNOWS the skill exists) AND * auto-provisions a `skill_read` tool the model calls to load the full body ON DEMAND * (so a long procedure only enters the prompt when it is actually needed). */ skills(selection: SkillsSelection): AgentBuilder; /** * #825 — subagents this agent carries, by name, WITH whatever the caller applied to them. * * Distinct from `settingSources`, which DISCOVERS them: the framework reads the file and takes the * prompt straight from it, so an enriched definition — a `MEMORY.md` folded in by * `applySubagentMemory` is the measured case — had nowhere to go and was silently discarded. */ subagents(map: Record): AgentBuilder; /** * theokit-file-based-config — opt into `.theokit/` file-based config (skills, subagents, hooks, * MCP, context, cron), discovered by the SDK from the app root (`project` = `/.theokit/`, * `user` = `~/.theokit/`). Unset ⇒ inline (code) config only. * * SECURITY (M68): takes a {@link SettingSourcesSelection}, not a string array. `project` reads * `.theokit/hooks.json`, which **executes shell**, so it requires a `TrustPosture` as evidence; * `user` is a plain boolean because `~/.theokit/` is the operator's own machine. The previous * signature called `project` "opt-in because `.theokit/` is your own repo" — true for a web app * whose `cwd` is its own deploy, false for an agent pointed at a repository someone else wrote. */ settingSources(selection: SettingSourcesSelection): AgentBuilder; /** * #686 — decide whether a hook declared in a config root is spawned AT ALL, before it runs. * * The fluent twin of `defineAgent({ hookApproval })`. It exists because a consumer that builds * through this chain had no way to reach the gate: `.use()` composes presets rather than sinking * capabilities, and the definition reaches `streamAgentTurnInProcess` with `local` already * assembled, so there was no downstream place to inject it either. * * Distinct from {@link AgentBuilder.hooks} below, which ATTACHES lifecycle hooks. This one decides * whether hooks somebody ELSE declared — in `.theokit/`, or in a foreign dialect imported through * `settingSources` — are allowed to run. * * Requires `@theokit/sdk >= 5.4.0`. Declaring it against an older SDK is refused at assembly * rather than forwarded: a gate that silently does not gate is worse than none. */ hookApproval(gate: HookApprovalGate): AgentBuilder; /** * M49 — enable the SDK's durable memory for this agent (`.theokit/memory/` in the run cwd: * `Remember:` capture with secret redaction, auto-injected recall, memory tools). Takes the SDK's * `MemorySettings` verbatim — `{ enabled: true }` is the minimal opt-in. */ memory(settings: MemorySettings): AgentBuilder; /** * B-072 — emit OpenTelemetry spans for this agent's runs (`agent.send`, `llm.call`, `tool.call`, * `memory.search`). Takes the SDK's `TelemetrySettings` verbatim; `{ enabled: true }` is the * minimal opt-in, and content (prompts, responses, tool args) is omitted unless * `includeContent: true` is set. * * Distinct from a diagnostics sink, which answers "what did this run LOG". A trace answers "where * did this run SPEND its time, and which call was the slow one" — two questions, and the sink * could never answer the second. */ telemetry(settings: TelemetrySettings): AgentBuilder; /** * Attach LIFECYCLE HOOKS in code, keyed by `HookName` — the builder-chain seam for intercepting * the agent loop. `pre_tool_call` may VETO a tool by returning `{ block: true, message }` before * it runs; the other events are observational. * * ```ts * AgentBuilder.create().hooks({ * pre_tool_call: (c) => guard(c.name) ? undefined : { block: true, message: 'not allowed' }, * on_session_start: () => log('session up'), * }) * ``` * * A hook is delivered internally as a code plugin, but that is TRANSPORT, not the contract — use * this instead of hand-wrapping a plugin, so the caller expresses interception rather than * assembling plumbing. Call once — a later call replaces the map. Composes with * {@link AgentBuilder.plugins}: hooks and plugins are additive, not exclusive. */ hooks(map: HookHandlers | Readonly>): AgentBuilder; /** * Register code `Plugin` objects for this agent — the builder-chain equivalent of * `Agent.create({ plugins })`. A plugin is an EXTENSION UNIT: it can register tools and commands, * or supply a model provider / memory adapter (`kind: 'general' | 'model-provider' | 'memory'`). * * For lifecycle interception prefer {@link AgentBuilder.hooks} — a hook needs a plugin only as its * transport, and `plugins()` makes the caller assemble that transport by hand. Reach for this when * you genuinely have a plugin (a provider, a memory adapter, a tool-registering extension). * * NOT the Claude Code filesystem-bundle form (B-055). `[{ type: 'local', path: './p' }]` used to * typecheck here against `readonly unknown[]` and do nothing — the typecheck that should have * caught it was what let it through. A bundle is declared by living in a `plugins/` directory * under `.theokit/` or `.claude/`, where its `skills/` and `agents/` are discovered. */ plugins(list: readonly CodePlugin[]): AgentBuilder; /** * Declare MCP (Model Context Protocol) servers available to this agent — the builder-chain * equivalent of the `@MCP` class decorator. Each key is a server name; the value is its config * (`command` / `args` / `env` / `cwd`). Forwarded to `Agent.create({ mcpServers })`; the SDK owns * MCP execution. Call once — a later call replaces the map. */ mcp(servers: McpServersMap): AgentBuilder; /** * Apply a reusable partial chain (Spring-Boot-style composition). `preset` receives the current * builder and returns an advanced one; its accumulated type-state flows through. */ use(preset: (builder: AgentBuilder) => TResult): TResult; /** * Resolve to the branded {@link AgentDefinition} — the SAME value `defineAgent` returns. * COMPILE ERROR when `.model()` was never called. */ build(...guard: TModel extends UnsetMarker ? [error: MissingModelError] : []): AgentDefinition; /** Phantom — lets type tests read the accumulated tool-name union. Never present at runtime. */ readonly __toolNames?: TTools; } /** * Static entry point for the fluent builder. M57: OO surface (`AgentBuilder.create()`) aligned with * the SDK's `X.create()` shape — was the free `agent()` function. `AgentBuilder` is a TYPE (the * generic interface above) and a VALUE (this const) at once; in type position the interface wins, in * value position the const wins — no collision, the same dual-space pattern the SDK uses for `Agent`. */ declare const AgentBuilder: { /** * Start a fluent agent definition. Chain `.model()` (required) + `.context()` / `.system()` / * `.input()` / `.tool()` / `.use()`, then `.build()` to get the branded {@link AgentDefinition}. */ create(): AgentBuilder; }; /** Thrown when an `agents/` file default-exports neither a `defineAgent` value nor an `@Agent` class. */ /** * M80 — extends {@link TheokitAgentError}, not plain `Error`. * * `isTransientError` is defined over `TheokitAgentError`, so a class outside that hierarchy is * INVISIBLE to it and the only recourse left to a consumer is matching on message text. `code` is * stable across a rename; `isRetryable` is DECLARED, because a default would be a retry policy * nobody chose. */ declare class AgentDefinitionError extends TheokitAgentError { readonly name = "AgentDefinitionError"; constructor(source: string); } /** Unwrap a module namespace `{ default: X }` to `X`; pass a bare value through. */ /** * What {@link compileAgentModule} accepts: an agent definition, a capability-built options object, * or the module object that default-exports either of those. * * The compiled arm mirrors `isCompiledAgentOptions` — `tools` plus `agents` — rather than the fuller * `CompiledAgentOptions`, whose required `stream` the runtime guard never asks for. A parameter type * STRICTER than the guard would refuse shapes that work today, which is a different defect from the * one being fixed (usetheokit/theokit#663). * * The parameter was `unknown` until #663. The runtime was never wrong — it refused a bad shape and * threw `AgentDefinitionError` — but `unknown` moved the refusal to the first turn, and a consumer * shipped two releases in which no turn could run, with every static check green. */ type AgentModule = AgentDefinition | AcceptedCompiledOptions | { readonly default: AgentDefinition | AcceptedCompiledOptions; }; /** * `CompiledAgentOptions` as `isCompiledAgentOptions` actually accepts it: an array under `tools` and * an object under `agents`. The guard inspects neither element type nor `stream`, so neither is * demanded here — a parameter that demanded them would reject modules the runtime compiles today, * which is a new defect rather than a fix for #663. */ type AcceptedCompiledOptions = Omit, 'tools' | 'agents'> & { readonly tools: readonly unknown[]; readonly agents: Readonly>; }; declare function compileAgentModule(mod: AgentModule, source?: string): CompiledAgentOptions; /** * Compile a module whose shape the typechecker CANNOT know — one that arrived from a dynamic * `import()` of a path discovered at runtime, which is what every HTTP / CLI / MCP entry point in * this framework receives. * * Identical to {@link compileAgentModule} at runtime. The separate name exists so `unknown` cannot * quietly re-enter the typed entry points: a genuine disk boundary says so by calling this, and * anything else has to satisfy {@link AgentModule}. Before #663 both cases shared one `unknown` * parameter, so the boundary that had a reason was indistinguishable from the one that did not. */ declare function compileLoadedAgentModule(mod: unknown, source?: string): CompiledAgentOptions; /** HITL wiring supplied by the harness (mount-agent): the gated-tool map + the approval resolver. */ interface StreamHitlOptions { gated: HitlWiring['gated']; awaitApproval: HitlWiring['awaitApproval']; } interface StreamAgentOptions { message: string; sessionId: string; /** * What the browser is told a failure was (usetheokit/theokit#390). * * Absent ⇒ masked to a fixed string. The server's raw error text — a driver's message, an HTTP * client's, a filesystem call's — used to reach the client verbatim, and `ai@7` on the same * protocol masks by default for the reason its own comment gives. The full text still reaches * the logs and the `agent.run` span; what stops is it reaching a browser unless a host decides. */ onError?: MaskError; /** M35 (multimodal) — images to send alongside the text. Absent ⇒ the string send path is unchanged. */ images?: RuntimeOverrides['images']; /** Enable human-in-the-loop tool approval (M4). Absent ⇒ the M2 non-HITL path, byte-unchanged. */ hitl?: StreamHitlOptions; /** * theokit-file-based-config (EC-1) — the app root `cwd` the SDK resolves `.theokit/` against when * `settingSources` is active. The framework boundary (`mountAgent`) threads its resolved * `projectRoot` here so discovery points at the app root, NOT `process.cwd()`. Absent ⇒ no `local.cwd`. */ cwd?: RuntimeOverrides['cwd']; /** * SDK 4.0 (SE40) — root of the native `.jsonl` session transcript. The framework boundary * (`mountAgent`) threads the resolved app root here so sessions persist per-app; absent ⇒ the SDK * default (`~/.theokit`). */ baseDir?: RuntimeOverrides['baseDir']; /** * The request's abort signal (M4). On client disconnect, the HITL merge queue is closed so the * detached SDK pump stops buffering (bounded memory) and the client stream terminates at once. * The paused SDK run itself is released by the approval timeout, not instantly — a durable-store * follow-up. The non-HITL path is pull-based and already tears down on the consumer's return. */ signal?: AbortSignal; /** * theokit#132 — observe the SDK's typed `RunEvent`s for this run (`tool_progress`, `rate_limit`, * `permission_denied`, `task_*`, `compact_boundary`, `tripwire`, `completion_check`). * * The seam theokit-studio's M1 event inspector needs: without it the run endpoint can only stream * `{kind:"message"}`, and the inspector degrades to showing nothing about the run itself. * * Deliberately a SINK rather than extra chunks on the returned generator. The generator's contract * is the UIMessage protocol a chat consumer renders; run diagnostics are a different audience, and * multiplexing them would make every chat consumer step over frames it has no use for. Absent ⇒ * the stream is byte-identical to before. */ onRunEvent?: RuntimeOverrides['onRunEvent']; /** * theokit#474 — per-turn transient retry. * * `AgentRunnerRunOptions.retry` has existed since V4-P, and it belongs to the OTHER runtime: the * reflective loop, whose round factory is allowed to throw. This entry point runs one SDK turn, * and the SDK reports a provider failure as the run's terminal `error` EVENT rather than as a * rejection — so the option could not simply be forwarded, and a wrapper that only caught throws * would have been inert. See `turn-retry.ts`. * * Absent ⇒ the key is omitted from the SDK call entirely and the turn is a single attempt, exactly * as before. */ retry?: RuntimeOverrides['retry']; /** * theokit#475 — expose the run's REAL token usage to tool handlers as `ctx.usage`. * * The seam a `get_context_remaining`-style tool needs: without it the only figure reachable from * inside a handler is a character-count estimate over `ctx.messages`. Read it with `readRunUsage` * from `@theokit/agents/usage`. * * Absent ⇒ handlers receive exactly the ctx they did before and the SDK receives no tracker it * was not already given. */ exposeUsageToTools?: RuntimeOverrides['exposeUsageToTools']; } /** * Run a compiled agent and yield the M0/M1 `UIMessageStream` chunks. `apiKey` is resolved by the * caller (`resolveProvider`). One `textId` per run (G8: `crypto.randomUUID`). When `hitl` is * supplied (M4), a HITL `pre_tool_call` plugin pauses the run for gated tools — the pause is the * SDK's own awaited hook, never a second loop (ADR 0038). */ declare function streamAgentUIMessages(compiled: CompiledAgentOptions, apiKey: string, input: StreamAgentOptions): AsyncGenerator; /** * M10 (theokit-ai-first) — createToolHooksPlugin: `beforeToolCall` / `afterToolCall` observability. * * ADR-0040 § D2: a HOME/BOUNDARY plugin over the SDK's OWN `pre_tool_call` / `post_tool_call` / * `pre_llm_call` / `post_llm_call` / `pre_user_send` hooks (mirrors {@link createHitlPlugin}). It * calls no LLM, dispatches no tool, runs no second loop — the SDK owns the run. `beforeToolCall` * may VETO a call; `afterToolCall` observes the result. * * M19 — `processInput` completes the input side of the processor pipeline, wired to the SDK * `pre_user_send` hook. NOTE (honest ceiling): the SDK does NOT let a plugin mutate the raw * prompt/stream; `pre_user_send` lets a handler INJECT derived context (`recalledContext`) BEFORE * the model. So `processInput` receives the prompt and returns an optional string, which the SDK * injects as a `` block ahead of the prompt. The api-error side of M19 lives in * `./api-error-handler` (a sibling factory — the SDK owns retry and exposes no error hook). */ /** A tool call about to run (mirrored from the SDK `pre_tool_call` context — type-only). */ interface BeforeToolCallContext { name: string; args: Record; } /** A veto returned from `beforeToolCall` — blocks the tool; the SDK surfaces `message` to the model. */ interface ToolCallVeto { block: true; message: string; } /** A completed tool call (mirrored from the SDK `post_tool_call` context — type-only). */ interface AfterToolCallContext { name: string; result: unknown; } interface ToolHooks { /** Observe (and optionally VETO) every tool call before it runs. Return a veto or nothing. */ beforeToolCall?: (ctx: BeforeToolCallContext) => ToolCallVeto | undefined | Promise; /** Observe every tool call's result after it runs. */ afterToolCall?: (ctx: AfterToolCallContext) => void | Promise; /** * M10 — observe every LLM turn BEFORE it runs (SDK `pre_llm_call`). Observability only — the * SDK's LLM-call context carries `{ agentId, runId, iteration }`, not the mutable request body. */ beforeLLMCall?: (ctx: LLMCallContext) => void | Promise; /** M10 — observe every LLM turn AFTER it completes (SDK `post_llm_call`). Observability only. */ afterLLMCall?: (ctx: LLMCallContext) => void | Promise; /** * M19 — pre-process the user input BEFORE the model runs (SDK `pre_user_send`). Receives the * prompt; return a string to INJECT as derived context ahead of it (the SDK wraps it in a * `` block), or `undefined` to inject nothing. The SDK does not expose raw-prompt * mutation to plugins, so this is additive, not a rewrite of the caller's stream. */ processInput?: (ctx: ProcessInputContext) => string | undefined | Promise; } /** Context of the input seam (mirrors the SDK `PreUserSendContext`). */ interface ProcessInputContext { prompt: string; agentId: string; runId: string; } /** Context of an LLM turn (mirrors the SDK `LlmCallContext` for `pre_llm_call`/`post_llm_call`). */ interface LLMCallContext { agentId: string; runId: string; /** 0-based iteration index of the current turn, when available. */ iteration?: number; } /** * Minimal SDK hook context (type-only — no runtime import, keeps the SDK peer optional). Tool hooks * carry `name`/`args`/`result`; LLM hooks carry `iteration`. All optional so one shape covers both. */ interface ToolHookRawContext { agentId: string; runId: string; name?: string; args?: Record; result?: unknown; iteration?: number; /** M19 — the user prompt, present on the `pre_user_send` seam. */ prompt?: string; } interface ToolHooksPluginContext { on(hook: string, handler: (ctx: ToolHookRawContext) => unknown): void; } interface ToolHooksPlugin { name: string; /** SDK `BasePlugin.version` — required by the `@theokit/sdk` Plugin contract. */ version: string; /** * SDK Plugin discriminator. MUST be `'general'` — the SDK's `isCodePlugin()` gate filters out any * plugin object lacking `kind: 'general'` (a `register()`-only object is silently dropped and its * hooks NEVER fire). Regression guard for the M10/M19 latent E2E bug. */ kind: 'general'; register(ctx: ToolHooksPluginContext): void; } /** * Build the tool-hooks plugin. Registers ONLY the hooks provided (inert when none) so it adds no * overhead unless used. The returned object is a structural `@theokit/sdk` `Plugin`. */ declare function createToolHooksPlugin(hooks: ToolHooks): ToolHooksPlugin; /** * What an `ask` verdict resolves to, and who decides it. * * MANDATORY, and that is the whole of the #826 fix rather than a detail of it. `PermissionEngine` * answers `ask` for a tool no rule matches, and `PermissionPlugin.create` turns that into a hard * block when no gate is supplied: * * opts.onAsk ? opts.onAsk(name) : { block: true, message: `requires approval: ${name}` } * * So an absent gate is not "no opinion" — it is the most restrictive opinion available, applied to * every tool the operator did not enumerate, and applied silently. * * The absence being unrepresentable is the point. `approval-posture.ts`, in this same package, * already refuses this shape in writing: "the 'no HITL' posture was not representable as a value, so * it was expressed as ABSENCE — and an absence has no exhaustive match, appears in no log, and fails * no test." A caller that wants everything unmatched to run says so with a gate that allows; what * stops existing is the omission. */ interface PermissionsPluginOptions { /** * Consulted ONLY on an `ask` verdict — an explicit `allow` never reaches it, and an explicit * `deny` is immune to it. May be asynchronous, so a surface with a human can await the decision. */ readonly onAsk: (toolName: string, input: unknown) => { readonly behavior: 'allow'; } | { readonly behavior: 'deny'; readonly message?: string; } | Promise<{ readonly behavior: 'allow'; } | { readonly behavior: 'deny'; readonly message?: string; }>; } /** * The `permissions` block of a settings file, as a plugin the run enforces. * * ## Why this exists * * `permissionRulesFromSettings` has rendered that block into `PermissionRule[]` since it was written, * and nothing consumed the result. Measured on 2026-09-17 across the consumer: the field is produced * in `settings-json.ts` and read in no other file, so an operator's `deny` was parsed, reported as * translated, and dropped. * * The cost was measured the same day, beside Claude Code on byte-identical configuration: it refused * a `Read(./off-limits.txt)` deny rule, and this ecosystem answered with the file's contents. * * ## What wiring it cost before the gate was mandatory * * Enforcing the rules without deciding the `ask` verdict traded one defect for a worse one. Measured * on the built binary the same day, with a single project rule allowing `Read(./src/**)`: `skill_read` * came back "Plugin blocked this tool call: requires approval: `skill_read`" for a skill that loads * correctly, and adding `"allow":["skill_read"]` made the identical call return the skill body. * Delegation died the same way — which is why the symptom was first filed as a delegation defect * (#828) and as a plugin-bundled-skill defect (#826), and was neither. * * An allow-list had silently become a whitelist-or-die: the moment an operator wrote any `permissions` * block, every tool they had not enumerated stopped working, with no diagnostic naming the cause. * * ## Why here rather than in the consumer * * `PermissionEngine` and `PermissionPlugin` are SDK classes, and a consumer that names them takes a * dependency on the SDK's shape for a capability it should be able to ask for by name. * `createToolHooksPlugin`, next door, set that precedent. * * ## Why `undefined` rather than an empty plugin * * A plugin over an empty rule set is a gate that can only answer yes. Returning `undefined` lets the * caller omit it, which keeps "no policy was configured" distinguishable from "a policy that permits * everything" — and a diagnostic reading a live gate would report the second as a control in force. */ declare function createPermissionsPlugin(rules: readonly PermissionRule[], options: PermissionsPluginOptions): Plugin | undefined; /** * M19 (theokit-ai-first) — `processApiError`: app-level fallback around an agent run. * * ADR-0040 § D2 + sdk-runtime.md: the `@theokit/sdk` runtime OWNS the LLM call AND its own * retry/backoff, and exposes NO api-error hook to plugins. This module is therefore NOT a plugin — * it is a thin app-boundary wrapper (the M19 DoD explicitly allows "a sibling factory"). It * RE-INVOKES the caller's run thunk on error; it never calls an LLM, never dispatches a tool, never * reimplements retry inside the loop (G2). The SDK's internal retry runs first; this handles what * survives it — a second, app-owned fallback layer (the M19 top-risk note: "app-level fallback, not * a second retry loop" inside the SDK). */ /** What the app decides after seeing an API error. */ interface ApiErrorDecision { /** Re-invoke the run thunk once more. Ignored past `maxAttempts`. */ retry?: boolean; /** * A value to return INSTEAD of rethrowing, when `retry` is false/absent. Lets the app degrade * gracefully (e.g. a canned response) rather than surface the raw provider error. */ fallback?: R; } /** Context handed to the app's error handler for each failed attempt (1-based). */ interface ApiErrorContext { error: unknown; /** 1-based attempt number that just failed. */ attempt: number; } interface ApiErrorPolicy { /** Called once per failed attempt. Return `{ retry: true }` to try again, or a `{ fallback }`. */ processApiError: (ctx: ApiErrorContext) => ApiErrorDecision | Promise>; /** * Hard cap on total attempts (defaults to 3). A backstop so a handler that always returns * `{ retry: true }` cannot loop forever (mirrors the SDK's own bounded retry philosophy). */ maxAttempts?: number; } /** * Run `thunk`, applying the app's `processApiError` policy on failure. Returns the run's value on * success, the policy's `fallback` when it declines to retry with one, or rethrows the last error. * * `thunk` is typically `() => agent.send(msg).then((r) => r.wait())` — a whole SDK run. On retry the * thunk is re-invoked from scratch (a fresh run), never resumed mid-loop. */ declare function runWithApiErrorHandling(thunk: () => Promise, policy: ApiErrorPolicy): Promise; /** * Build a reusable guard bound to one policy. `const guard = createApiErrorHandler({ ... })` then * `guard(() => agent.send(m).then((r) => r.wait()))` — the same policy applied to many runs. */ declare function createApiErrorHandler(policy: ApiErrorPolicy): (thunk: () => Promise) => Promise; /** * M81 — the two gaps around a delegation's LIFE, as opposed to its execution. * * The execution engine was supplied and works. What was missing sits either side of it: * * - **a clock cap.** `budget` is money only, and a delegation that HANGS burns clock, not dollars. * So a consumer wrote its own timeout race with its own typed error — mechanism, re-derived. * - **disposal with an owner.** `delegate()` never creates disposable agents, so every site that * does — a squad, an ephemeral reviewer — hand-wrote acquire/dispose. Both of those files carried * a bug-fix comment about `finally` semantics, which is what "we got this wrong once" looks like * when the correct version lives nowhere in particular. */ /** * Raised when a delegation exceeds its wall-clock cap. * * A distinct class from the budget error on purpose: "you spent your dollars" and "you ran out of * time" call for different responses, and a caller that cannot tell them apart retries the one that * will hang again. */ declare class DelegationTimeoutError extends TheokitAgentError { readonly name = "DelegationTimeoutError"; constructor(subAgent: string, timeoutMs: number); } /** * Race a delegation against a wall clock. * * The timer is cleared on both paths: a pending `setTimeout` keeps the event loop alive, and a CLI * that finishes its work and then sits for thirty seconds looks broken in a way nobody connects back * to a delegation cap. */ declare function withClockCap(promise: Promise, timeoutMs: number, subAgent: string): Promise; /** An agent that must be released, and the release. */ interface EphemeralAgent { readonly agent: TAgent; readonly dispose: () => void | Promise; } /** * Run `body` against a freshly created agent and dispose it afterwards, whatever happens. * * ## Why the disposal cannot mask the result * * `Promise.allSettled` semantics, named by the milestone: the body's outcome — value OR error — is * what the caller receives, and a failing `dispose` never replaces it. * * A cleanup that threw over a successful run would report a teardown error for work that actually * succeeded. Worse, a cleanup that threw over a FAILED run would replace the real failure with the * teardown one, and the diagnosis the caller needed is gone. That asymmetry is precisely what the * two hand-written call sites got wrong, each with its own bug-fix comment about `finally`. * * The disposal error is not silently swallowed either — it is reported through `onDisposeError`, so * a leak is observable without competing with the result. */ declare function withEphemeralAgent(create: () => EphemeralAgent | Promise>, body: (agent: TAgent) => Promise, options?: { readonly onDisposeError?: (error: unknown) => void; }): Promise; /** * M24 (ADR-0041) — MCP follow-ups, framework-side layer over the `@MCP` config (ADR-0040 § D2). * * Three home/boundary helpers that compose with the existing `@MCP` / `defineAgent` surfaces — the * SDK still owns MCP server EXECUTION (`Agent.create({ mcpServers })`); these only shape the config: * - `resolveMcpServers` — per-request resolver so multi-tenant apps hand different MCP creds to * different callers (mirrors the M13 skills resolver); * - `mcpRegistry` — build a server config for a known MCP registry (Composio / mcp.run); * - `mcpToolApprovals` — mark MCP tools for approval, producing the M14 `approvals` entries so a * gated MCP tool routes through the (E2E-proven) HITL flow. */ /** The request context handed to an MCP resolver (the M7 run-context — opaque per-request data). */ type McpRequestContext = Record; /** * How the MCP server set is chosen: * - a map — static `@MCP`-style config. * - a function — resolved per request from the {@link McpRequestContext} (sync or async). */ type McpSelection = McpServersMap | ((ctx: McpRequestContext) => McpServersMap | Promise); /** * Resolve the MCP servers for a request. Returns `undefined` when no selection is given. Fails fast * if a resolver returns a non-object (error-handling.md). Multi-tenant apps pass a function that * reads per-user credentials from `ctx`. */ declare function resolveMcpServers(selection: McpSelection | undefined, ctx: McpRequestContext): Promise; /** Config for {@link mcpRegistry}. `registry` selects a known provider. */ interface McpRegistryConfig { registry: 'composio' | 'mcp.run'; /** The registry API key (kept in the server's env, never logged). */ apiKey: string; /** Composio: the app toolkits to expose (e.g. `['github', 'slack']`). */ apps?: string[]; /** mcp.run: the profile to connect. */ profile?: string; } /** * Build an {@link McpServersMap} for a known MCP registry. Emits an `npx`-launched server config with * the API key in `env`. Documented registries: **Composio** (`@composio/mcp`) and **mcp.run** * (`@mcp.run/cli`). Other registries are wired manually via a raw `@MCP` entry. */ declare function mcpRegistry(config: McpRegistryConfig): McpServersMap; /** An MCP tool's approval spec: full {@link HumanInTheLoopOptions} or a bare question string. */ type McpApprovalSpec = HumanInTheLoopOptions | string; /** * Mark MCP tools for human approval (`requireToolApproval`). Returns the `Record` shape the M14 `defineAgent({ approvals })` map consumes — so a gated MCP * tool routes through the same (E2E-proven) HITL flow as a native gated tool. A bare string is * shorthand for `{ question }`. */ declare function mcpToolApprovals(specs: Record): Record; /** * The channel by which a skipped entry is NAMED. Without it, narrowing the blast radius would turn * into fail-open. * * **`onWarn` is optional; the WARNING is not.** Omitted, warnings go to `stderr` — never nowhere. * The M112 review measured why that matters: the only production caller (the agent-builder's * `agents/chat.ts:530`) did NOT pass `onWarn`, and the whole defence against `error-handling.md § 2` * — repeated across four artifacts — rested on the phrase *"the error stays visible on the channel"*. * With no subscriber it did not: the entry was dropped in total silence, and the user saw "the tool * disappeared" and nothing else. */ interface LoadMcpJsonOptions { onWarn?: (warning: string) => void; /** * The environment `${VAR}` references resolve against. Injected rather than read from * `process.env` so a test can prove the expansion without mutating the process it runs in — * the convention `diagnostic-sink.ts` and `transcript-root-hint.ts` already follow. */ env?: Record; } /** * Raised when `/.mcp.json` exists but cannot be read, is not valid JSON, or does not match the * expected shape — never swallowed. * * It descends from {@link TheokitAgentError} rather than bare `Error` on purpose: `isTransientError` * requires that hierarchy, so a parallel one would make the predicate that separates recoverable * from unrecoverable useless for this error. `isRetryable` stays `false` — a malformed config file * does not get better by trying again. */ declare class McpFileError extends TheokitAgentError { readonly name = "McpFileError"; constructor(message: string); } /** * Load the MCP servers declared in `/.mcp.json`. * * Returns an empty map when the file is absent (MCP is opt-in) and when the document is a valid JSON * object without an `mcpServers` key (a project that declares no server). Throws {@link McpFileError} * on a read failure, invalid JSON, or any shape violation. * * Reading is explicit — this module has no import-time side effect. */ declare function loadMcpJson(cwd: string, opts?: LoadMcpJsonOptions): McpServersMap; /** * The MCP servers an operator registered for themselves, from `~/.claude.json`. * * `.mcp.json` is a PROJECT's declaration and {@link loadMcpJson} has read it since B-043. This is * the other half: the servers someone registered for their own account, under the same * `mcpServers` key, which nothing here was reading. The surfaces table listed them "not read yet, * and in scope", justified with "the personal scope measures 0" — re-measured 2026-09-15 on the * machine that wrote that note: 2 servers, neither reaching an agent. * * ## Only `mcpServers`, and the rest is deliberately left alone * * That file is two things under one name. Its OAuth state and UI toggles are a specific CLI writing * about its own session; a library reaching into another program's login state would be taking * something it neither owns nor can refresh. The parser already ignores every other key, and a test * pins it by putting a token beside the servers and asserting the result carries nothing but them. * * ## Why this never throws * * {@link loadMcpJson} throws on a malformed project file, because that file is the project's own * declaration and an author who wrote it wants to know. This one is in the operator's home and is * shared with another product: one stray comma there would otherwise break every project on the * machine, for a file this project did not write. It reports and returns nothing instead — the same * trade `readLayer` makes in `config/settings-file.ts`, for the same reason. */ declare function loadPersonalMcpServers(home: string, opts?: LoadMcpJsonOptions): McpServersMap; /** * Agent manifest generator — build-time JSON describing all agents, tools, guards, policies. * * Feeds: theokit agents list/inspect, TheoCloud deploy, UI agent consoles. */ /** * M53 — the manifest's OWN input contract, listing exactly the members it reads. It used to take an * `AgentWalkResult`, which chained the manifest to the decorator metadata walk that M53 deletes. * * `AgentWalkResult` satisfies this structurally, so the decorator path keeps working unchanged while * the migration is in flight; once the walk is gone, the capability path builds this directly. The * point is that the manifest never needed the whole walk — only these members. */ interface AgentManifestSource { readonly agentConfig: { name: string; model?: string; stream?: boolean; }; readonly route: string; readonly mainLoop: { propertyKey: string | symbol; strategy: string; }; readonly guards: readonly { name: string; }[]; readonly interceptors: readonly { name: string; }[]; readonly toolboxes: readonly { namespace?: string; tools: readonly { config: { name: string; description: string; risk?: string; }; approval?: unknown; capabilities?: string[]; trace: boolean; audit: boolean; }[]; }[]; readonly gateway?: { platforms: string[]; sessionStrategy?: string; }; readonly subAgentClasses: readonly { name: string; }[]; readonly memory?: { provider?: string; embeddings?: boolean; fts?: boolean; scope?: string; }; readonly skills?: { include?: string[]; }; readonly mcpServers?: Record; } interface AgentManifest { version: '1.0'; generatedAt: string; agents: AgentManifestEntry[]; } interface AgentManifestEntry { name: string; route: string; model?: string; stream: boolean; mainLoop: { method: string; strategy: string; }; guards: string[]; interceptors: string[]; tools: AgentManifestTool[]; gateway?: { platforms: string[]; sessionStrategy: string; }; subAgents: string[]; memory?: { provider: string; embeddings: boolean; fts: boolean; scope: string; }; skills?: string[]; mcpServers?: string[]; } interface AgentManifestTool { name: string; description: string; risk?: string; approval: boolean; capabilities?: string[]; trace: boolean; audit: boolean; } /** * Generate a serializable agent manifest from an {@link AgentManifestSource} per agent. * All Function references are converted to string names for JSON safety. */ declare function generateAgentManifest(sources: AgentManifestSource[]): AgentManifest; /** * agentsPlugin() — TheoKit dev-server plugin for agent routes. * * Mirrors httpDecoratorsPlugin() pattern. Registers agent HTTP endpoints * (POST /chat, GET /runs/:id) via the TheoKit plugin hook system. * * Per ADR D6: structural { name, register } shape (no compile-time theokit dep). */ /** * An agent the plugin mounts: its name, route and already-compiled options (from `applyCapabilities`). */ interface PluginAgentEntry { readonly name: string; readonly route: string; readonly compiled: CompiledAgentOptions; } interface AgentsPluginOptions { /** Agents to mount: prepared entries built by `applyCapabilities`. */ agents: PluginAgentEntry[]; /** Factory that creates agent runs — bridges to SDK Agent.create() + agent.send(). */ createRunFactory?: (compiled: CompiledAgentOptions) => (message: string, sessionId: string) => AsyncIterable; } interface PluginApp { addHook(name: string, fn: (ctx: { request: Request; }) => Promise): void; } /** * Create a TheoKit plugin that mounts agent routes. * Web Standard Request/Response — runtime-agnostic. */ declare function agentsPlugin(opts: AgentsPluginOptions): { name: string; register(app: PluginApp): void; }; export { compileContextWindow as $, type AgentManifestEntry as A, type BeforeToolCallContext as B, type ContextWindowOptions as C, type DefinitionOrThunk as D, type EphemeralAgent as E, type McpSelection as F, type ProcessInputContext as G, type HitlDecision as H, type SdkAgentHandle as I, type SdkMessage as J, type SdkSendOptions as K, type LLMCallContext as L, type McpApprovalSpec as M, type SdkTurnHandle as N, type Segment as O, type PermissionsPluginOptions as P, type ShouldAutoApproveOptions as Q, type RuntimeOverrides as R, type SkillsOptions as S, type ToolCallVeto as T, type ToolHooks as U, type ToolHooksPlugin as V, WRITE_SCOPED_TOOLS as W, agentsPlugin as X, applyPosture as Y, buildModelSelection as Z, compileAgentModule as _, type ApprovalPosture as a, compileLoadedAgentModule as a0, compileProjectContext as a1, compileSkills as a2, createAgentExecutionContext as a3, createApiErrorHandler as a4, createPermissionsPlugin as a5, createSdkAgentStream as a6, createThinkTagExtractor as a7, createToolHooksPlugin as a8, extractThinkTagStream as a9, generateAgentManifest as aa, generateAgentRoutes as ab, isAgentContext as ac, loadMcpJson as ad, loadPersonalMcpServers as ae, mcpRegistry as af, mcpToolApprovals as ag, projectContextMetadataOnlyKnobs as ah, reasoningEffortOf as ai, resolveMcpServers as aj, runWithApiErrorHandling as ak, shouldAutoApprove as al, toAgentFactory as am, translateSdkEvent as an, withClockCap as ao, withEphemeralAgent as ap, type AgentModule as b, APPROVAL_MODES as c, type AfterToolCallContext as d, AgentBuilder as e, AgentDefinitionError as f, type AgentExecutionContext as g, type AgentManifest as h, type AgentManifestSource as i, type AgentManifestTool as j, type AgentRoute as k, type AgentRouteContext as l, type AgentRunInfo as m, type AgentsPluginOptions as n, type ApiErrorContext as o, type ApiErrorDecision as p, type ApiErrorPolicy as q, type ApprovalMode as r, streamAgentUIMessages as s, type ApprovalRequest as t, type CompiledContextWindow as u, ContextualTool as v, DelegationTimeoutError as w, McpFileError as x, type McpRegistryConfig as y, type McpRequestContext as z };