@theokit/sdk 4.19.1 → 4.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1589 @@
1
+ import * as zod from 'zod';
2
+
3
+ /**
4
+ * `PermissionEngine` — first-match permission rules for tool invocations.
5
+ *
6
+ * Evaluates a tool name (and optional arguments, #55) against an ordered list
7
+ * of rules. First matching rule wins; when no rule matches the `defaultAction`
8
+ * is returned. #55 — the default is now `"ask"` (FAIL-CLOSED): a permission
9
+ * engine that cannot positively allow must not silently allow. Opt back into
10
+ * the previous fail-open behavior with `{ defaultAction: "allow" }`.
11
+ */
12
+ type PermissionAction = "allow" | "deny" | "ask";
13
+ /**
14
+ * SE1 — a per-run permission MODE that adjusts the rule-engine verdict globally.
15
+ * A PURE post-processor of the verdict (no tool-safety metadata needed, so it fits
16
+ * a bring-your-own-tools runtime). Grounded in a peer project (plan agent = deny-all,
17
+ * `dangerously-skip-permissions`) + Codex (`AskForApproval`: `OnRequest` default,
18
+ * `Never`, `UnlessTrusted`). See {@link applyMode} for the exact table.
19
+ *
20
+ * - `default` — verdict as-is (rules decide; unmatched ⇒ `ask`, fail-closed).
21
+ * - `plan` — read-only: `allow` rules pass, everything else ⇒ `deny` (mutations blocked).
22
+ * NOTE: `plan` gates on the resolved verdict, so an engine configured with
23
+ * `{ defaultAction: "allow" }` still yields `allow` for UNMATCHED calls under
24
+ * `plan` — pair `plan` with the default fail-closed engine (`defaultAction: "ask"`)
25
+ * for full read-only behavior.
26
+ * - `acceptEdits` — auto-approve the UNMATCHED verdict, but STILL honor an explicit
27
+ * `ask` rule (a caller gates a risky tool with an ask rule). Codex `UnlessTrusted`.
28
+ * - `bypass` (alias `bypassPermissions`, the Anthropic-exact name) — everything ⇒
29
+ * `allow` EXCEPT an explicit `deny` rule. Never asks. a peer project
30
+ * `dangerously-skip-permissions` / Codex `Never` / Anthropic `bypassPermissions`.
31
+ */
32
+ type PermissionMode = "default" | "plan" | "acceptEdits" | "bypass" | "bypassPermissions";
33
+ /**
34
+ * SE1 — apply a {@link PermissionMode} to a rule-engine verdict. Pure.
35
+ *
36
+ * `explicit` is `true` when the verdict came from a rule that matched by name (and
37
+ * args), `false` when it is the fail-closed default for an unmatched call. The flag
38
+ * is load-bearing for `acceptEdits`, which auto-approves the unmatched default but
39
+ * keeps honoring an explicit `ask` rule (unlike `bypass`, which allows even that).
40
+ *
41
+ * INVARIANT (both a peer project + Codex): an explicit `deny` is immune to EVERY
42
+ * auto-approve mode — `bypass`/`acceptEdits` never un-deny.
43
+ */
44
+ declare function applyMode(verdict: PermissionAction, mode: PermissionMode, explicit: boolean): PermissionAction;
45
+ /**
46
+ * #55 — an argument matcher. A rule with `args` gates on the tool's argument
47
+ * VALUES, not just its name: an exact string, a RegExp (tested against the
48
+ * stringified value), or a predicate. Every declared arg must match for the
49
+ * rule to apply — so `{ tool: "shell", args: { command: /rm\s+-rf/ } }` denies
50
+ * a destructive shell call while leaving `ls` to fall through.
51
+ */
52
+ type ArgMatcher = string | RegExp | ((value: unknown) => boolean);
53
+ interface PermissionRule {
54
+ /** Tool name (exact string) or pattern (RegExp). */
55
+ tool: string | RegExp;
56
+ /**
57
+ * #55 — optional per-argument matchers. When present, the rule matches only
58
+ * if the tool name matches AND every declared arg predicate matches the
59
+ * corresponding call argument. A missing/undefined arg fails its predicate
60
+ * (the rule does not match) — never throws.
61
+ */
62
+ args?: Record<string, ArgMatcher>;
63
+ /** Action to take when rule matches. */
64
+ action: PermissionAction;
65
+ }
66
+ /** Options for {@link PermissionEngine}. */
67
+ interface PermissionEngineOptions {
68
+ /**
69
+ * Action when no rule matches. #55 — default is now `"ask"` (fail-closed): a
70
+ * permission engine that cannot positively allow must not silently allow.
71
+ * Pass `"allow"` to restore the previous fail-open behavior.
72
+ */
73
+ readonly defaultAction?: PermissionAction;
74
+ }
75
+ declare class PermissionEngine {
76
+ #private;
77
+ private readonly rules;
78
+ private readonly defaultAction;
79
+ constructor(rules: PermissionRule[], options?: PermissionEngineOptions);
80
+ /**
81
+ * Evaluate a tool name (and optional arguments) against the rules. First
82
+ * match wins; falls back to the configured `defaultAction` (default `"ask"`,
83
+ * fail-closed) when no rule matches. #55 — a rule with `args` gates on the
84
+ * argument values, so the same tool name can resolve to different actions
85
+ * depending on what it is asked to do.
86
+ */
87
+ evaluate(toolName: string, args?: Record<string, unknown>, mode?: PermissionMode): PermissionAction;
88
+ }
89
+
90
+ /**
91
+ * SE2 — typed runtime EVENT stream, ADDITIVE to the `SDKMessage` content stream.
92
+ *
93
+ * `Run.stream()` yields `SDKMessage`s (the conversation content). `RunEvent`s are
94
+ * out-of-band runtime-OBSERVABILITY signals — the model's content is unaffected —
95
+ * delivered opt-in via `SendOptions.onRunEvent`. Discriminate on `type`. Mirrors
96
+ * the Anthropic `SDKMessage`-union approach (rate-limit, permission-denied, task
97
+ * lifecycle, compaction boundary).
98
+ *
99
+ * The union is the forward-compatible CONTRACT (discriminate exhaustively). The
100
+ * runtime EMITS every variant end-to-end: `tool_progress` + `permission_denied`
101
+ * (agent-loop tool-dispatch seam), `rate_limit` (pool-aware LLM client 429 retry),
102
+ * `compact_boundary` (session auto-compaction), and `task_*` (opt-in bridge from a
103
+ * `Task.submit({ onRunEvent })` task's lifecycle). A consumer switching on `type`
104
+ * sees the real signal.
105
+ *
106
+ * @public
107
+ */
108
+ type RunEvent = RunToolProgressEvent | RunRateLimitEvent | RunPermissionDeniedEvent | RunTaskStartedEvent | RunTaskUpdatedEvent | RunTaskCompletedEvent | RunCompactBoundaryEvent | RunTripwireEvent | RunCompletionCheckEvent;
109
+ /**
110
+ * SE24 — a guardrail processor called `abort()`; the run stops with a tripwire.
111
+ * Delivered via {@link SendOptions.onRunEvent} (mirrors the `RunResult.tripwire`
112
+ * surfaced on `wait()`).
113
+ */
114
+ interface RunTripwireEvent {
115
+ readonly type: "tripwire";
116
+ readonly reason: string;
117
+ readonly processorId: string;
118
+ }
119
+ /** A tool call is being dispatched (before its result). */
120
+ interface RunToolProgressEvent {
121
+ readonly type: "tool_progress";
122
+ readonly toolName: string;
123
+ readonly toolCallId: string;
124
+ }
125
+ /** The provider returned a rate-limit (HTTP 429); the loop will back off + retry. */
126
+ interface RunRateLimitEvent {
127
+ readonly type: "rate_limit";
128
+ /** Retry attempt number (1-based) about to be delayed. */
129
+ readonly attempt: number;
130
+ /** Delay in ms before the retry, when the provider/policy supplied one. */
131
+ readonly retryAfterMs?: number;
132
+ }
133
+ /**
134
+ * A tool call was DENIED before dispatch — by the permission gate/plugin (SE1),
135
+ * an operator file-hook `preToolUse`, or the fork tool-whitelist. `source`
136
+ * discriminates which. `toolCallId` joins the event to the tool-call log.
137
+ */
138
+ interface RunPermissionDeniedEvent {
139
+ readonly type: "permission_denied";
140
+ readonly toolName: string;
141
+ readonly toolCallId: string;
142
+ /** Which layer blocked the call. */
143
+ readonly source: "plugin" | "file_hook" | "fork_whitelist";
144
+ /** The rejection message surfaced to the model. */
145
+ readonly message: string;
146
+ }
147
+ /** A background task/subagent started. */
148
+ interface RunTaskStartedEvent {
149
+ readonly type: "task_started";
150
+ readonly taskId: string;
151
+ readonly description?: string;
152
+ }
153
+ /** A background task/subagent changed state. */
154
+ interface RunTaskUpdatedEvent {
155
+ readonly type: "task_updated";
156
+ readonly taskId: string;
157
+ readonly status: string;
158
+ }
159
+ /** A background task/subagent finished. */
160
+ interface RunTaskCompletedEvent {
161
+ readonly type: "task_completed";
162
+ readonly taskId: string;
163
+ readonly status: "completed" | "failed" | "stopped";
164
+ }
165
+ /**
166
+ * SE34 — the per-send completion check (`isTaskComplete`) produced a verdict.
167
+ * Emitted once, after a finished run's reply is judged against
168
+ * {@link SendOptions.completionCheck}. Distinct from `task_completed` (which is
169
+ * background-task/subagent lifecycle). Mirrors {@link RunResult.completionCheck}.
170
+ */
171
+ interface RunCompletionCheckEvent {
172
+ readonly type: "completion_check";
173
+ readonly complete: boolean;
174
+ readonly reason: string;
175
+ }
176
+ /** The conversation crossed a compaction boundary (history was summarized). */
177
+ interface RunCompactBoundaryEvent {
178
+ readonly type: "compact_boundary";
179
+ readonly trigger: "manual" | "auto";
180
+ /** Token count before compaction, when known. */
181
+ readonly preTokens?: number;
182
+ }
183
+ /**
184
+ * SE2 — the opt-in sink for {@link RunEvent}s. Supplied via `SendOptions.onRunEvent`.
185
+ * Synchronous + best-effort: a throwing sink must never break the run (the emitter
186
+ * try-catches it), so keep it fast (push to a queue, don't await).
187
+ */
188
+ type RunEventSink = (event: RunEvent) => void;
189
+ /**
190
+ * SE2 — emit a {@link RunEvent} to an optional sink, swallowing any sink error so
191
+ * observability can never break the run (fail-safe, mirrors the EventBus EC-2
192
+ * contract). No-op when the sink is absent.
193
+ */
194
+ declare function emitRunEvent(sink: RunEventSink | undefined, event: RunEvent): void;
195
+
196
+ /**
197
+ * SE24 — guardrail processor pipeline. A `Processor` inspects/transforms/blocks
198
+ * the user message (input) or the model's final text (output). Processors run in
199
+ * order; each may rewrite its payload, `abort(reason)` to stop the run (surfaced
200
+ * as {@link RunResult.tripwire} + a `tripwire` run-event), or `warn()` to report
201
+ * a non-blocking violation. The pipeline is provider-agnostic and carries NO LLM
202
+ * — an LLM-classifier processor is the consumer's (delegated, see the guardrails
203
+ * ADR). Mirrors a peer framework's `inputProcessors` / `outputProcessors`.
204
+ *
205
+ * @public
206
+ */
207
+ /**
208
+ * A policy violation surfaced to a processor's {@link Processor.onViolation}
209
+ * callback — on `abort()` (blocking) AND on `warn()` (non-blocking).
210
+ *
211
+ * @public
212
+ */
213
+ interface ProcessorViolation {
214
+ processorId: string;
215
+ message: string;
216
+ detail?: unknown;
217
+ }
218
+ /**
219
+ * Controls available to a processor while it runs: `abort()` stops the run with
220
+ * a tripwire; `warn()` reports a non-blocking violation and continues.
221
+ *
222
+ * @public
223
+ */
224
+ interface ProcessorControls {
225
+ /** Stop the run immediately with a tripwire. Throws — code after it never runs. */
226
+ abort(reason: string): never;
227
+ /** Report a non-blocking violation (fires `onViolation`); the run continues. */
228
+ warn(message: string, detail?: unknown): void;
229
+ }
230
+ /** Context passed to {@link Processor.processInput}. @public */
231
+ interface InputProcessorContext extends ProcessorControls {
232
+ /** The user message text for this send. */
233
+ message: string;
234
+ agentId: string;
235
+ }
236
+ /** Context passed to {@link Processor.processOutput}. @public */
237
+ interface OutputProcessorContext extends ProcessorControls {
238
+ /** The model's final assistant text for this run. */
239
+ text: string;
240
+ agentId: string;
241
+ }
242
+ /**
243
+ * A guardrail processor. Provide `processInput` (runs before the LLM) and/or
244
+ * `processOutput` (runs on the final text). A processor's `strategy` (block /
245
+ * rewrite / redact / warn) is expressed via the {@link ProcessorControls}:
246
+ * return the transformed payload to rewrite/redact, `abort()` to block, `warn()`
247
+ * to report without blocking. The core ships no strategy enum — strategies are a
248
+ * processor-level convention over these primitives.
249
+ *
250
+ * @public
251
+ */
252
+ interface Processor {
253
+ /** Stable id — surfaced on {@link ProcessorViolation} and {@link RunResult.tripwire}. */
254
+ id: string;
255
+ /**
256
+ * Transform or block the user message before it reaches the model. Return the
257
+ * (possibly rewritten) text; returning nothing (void) preserves the message
258
+ * unchanged. May be `async`.
259
+ */
260
+ processInput?(ctx: InputProcessorContext): string | Promise<string> | void;
261
+ /**
262
+ * Transform or block the model's final text before it reaches the caller.
263
+ * Return the (possibly redacted) text; returning nothing (void) preserves the
264
+ * text unchanged. May be `async`.
265
+ */
266
+ processOutput?(ctx: OutputProcessorContext): string | Promise<string> | void;
267
+ /** Fires on `abort()` and `warn()`. Errors thrown here are swallowed (never break the pipeline). */
268
+ onViolation?(violation: ProcessorViolation): void;
269
+ }
270
+ /**
271
+ * The tripwire detail attached to {@link RunResult.tripwire} when a processor
272
+ * aborts, and carried by the `tripwire` run-event.
273
+ *
274
+ * @public
275
+ */
276
+ interface ProcessorTripwire {
277
+ reason: string;
278
+ processorId: string;
279
+ }
280
+
281
+ /**
282
+ * Leaf module for content-block types shared by `messages.ts` (assistant/user
283
+ * content) and `agent-prims.ts` (`CustomTool` handler results). Kept
284
+ * import-free so both can depend on it WITHOUT the `agent-prims ↔ messages`
285
+ * cycle (#7).
286
+ *
287
+ * @public
288
+ */
289
+ /**
290
+ * Plain text content block emitted by the assistant or user, or returned by a
291
+ * tool.
292
+ *
293
+ * @public
294
+ */
295
+ interface TextBlock {
296
+ type: "text";
297
+ text: string;
298
+ }
299
+ /**
300
+ * SE7 — a base64-encoded image block a tool can hand back as (part of) its
301
+ * result or its `ToolError`. `media_type` is a MIME type (e.g. `"image/png"`);
302
+ * `data` is the base64 payload without a data-URL prefix.
303
+ *
304
+ * Note: when a tool builds this from model- or user-influenced input, treat
305
+ * `media_type` as UNTRUSTED — validate/allow-list it before rendering it in a
306
+ * log or UI (it could carry newlines / control chars). The SDK only forwards it
307
+ * (JSON-serialized onto the wire) and never executes or path-joins it.
308
+ *
309
+ * @public
310
+ */
311
+ interface ImageBlock {
312
+ type: "image";
313
+ source: {
314
+ type: "base64";
315
+ media_type: string;
316
+ data: string;
317
+ };
318
+ }
319
+ /**
320
+ * SE7 — structured content a tool result may carry: text and/or images. A tool
321
+ * `handler` may return this (success) and a `ToolError` may carry it (failure).
322
+ * Block-capable provider wires forward it natively; string-only provider wires
323
+ * flatten text and fail fast on an image.
324
+ *
325
+ * @public
326
+ */
327
+ type ToolResultContentBlock = TextBlock | ImageBlock;
328
+
329
+ /**
330
+ * Type-leaf — primitives shared between `agent.ts`, `run.ts`, and
331
+ * `messages.ts`. Extracted to break LOW type-only cycles #5 and #7
332
+ * (audit `architecture-output/final_report.md`) per plan
333
+ * arch-review-fixes-2026-06-06 § Phase 4 / T4.1 (D438).
334
+ *
335
+ * Public surface unchanged — `types/agent.ts` re-exports these from this
336
+ * leaf so `import type { ModelSelection, CustomTool } from "@theokit/sdk"`
337
+ * keeps resolving.
338
+ *
339
+ * @public
340
+ */
341
+ /**
342
+ * One slot in a {@link ModelSelection.params} array.
343
+ *
344
+ * @public
345
+ */
346
+ interface ModelParameterValue {
347
+ id: string;
348
+ value: string;
349
+ }
350
+ /**
351
+ * Identifies a model plus optional per-model parameters (e.g. reasoning effort).
352
+ *
353
+ * Use `Theokit.models.list()` to discover valid ids and parameter definitions.
354
+ *
355
+ * @public
356
+ */
357
+ interface ModelSelection {
358
+ id: string;
359
+ params?: ModelParameterValue[];
360
+ }
361
+ /**
362
+ * SE12 — a read-only, text-only projection of one turn of the run's conversation,
363
+ * exposed to a tool handler via `ctx.messages`. Content is flattened to text; tool
364
+ * calls / results and non-text parts are dropped, and turns that project to empty
365
+ * text are omitted. Consumed by `defineSubAgent`'s `messageFilter` to forward
366
+ * (a subset of) the supervisor transcript to a subagent.
367
+ *
368
+ * `role` includes `"system"` for type-completeness with the wire message shape,
369
+ * but the system prompt travels on a separate request field — a `"system"` entry
370
+ * does NOT appear in the current projection.
371
+ *
372
+ * @public
373
+ */
374
+ interface ToolContextMessage {
375
+ role: "system" | "user" | "assistant";
376
+ content: string;
377
+ }
378
+ /**
379
+ * Local function tool declared per-agent via {@link AgentOptions.tools}. The
380
+ * handler runs in-process; no MCP server is involved. The SDK serializes
381
+ * `name`, `description`, and `inputSchema` into the model's tool catalog.
382
+ *
383
+ * Handlers MUST be re-passed on `Agent.resume()` because closures cannot be
384
+ * persisted. The tool catalog (name + description + schema) is NOT serialized.
385
+ *
386
+ * @public
387
+ */
388
+ interface CustomTool {
389
+ /**
390
+ * Tool name surfaced to the LLM. Must match `^[a-zA-Z][a-zA-Z0-9_-]{0,63}$`
391
+ * and must not collide with `shell`, `memory_search`, `memory_get`, or any
392
+ * `mcp_*` prefix (reserved for the SDK's built-in tools).
393
+ */
394
+ name: string;
395
+ /** Description surfaced to the LLM. Required — drives tool-selection accuracy. */
396
+ description: string;
397
+ /** JSON Schema (Draft-7 subset) describing the `input` argument. Must be `type: "object"`. */
398
+ inputSchema: Record<string, unknown>;
399
+ /**
400
+ * Local handler invoked when the model emits `tool_use` for this tool.
401
+ * Returns a string OR structured content blocks (SE7 — text + image, e.g. a
402
+ * screenshot) that become the `tool_result.content` surfaced back to the
403
+ * model. Throws → SDK converts to `tool_result` with `isError: true` and the
404
+ * error `message` as content; throw a `ToolError` to carry a clean message or
405
+ * multimodal error content. #65 — an optional 2nd `ToolContext` argument
406
+ * carries the run's `AbortSignal`; single-argument handlers are unaffected. M7
407
+ * — the same `ctx` also carries an optional user `context` (provided once via
408
+ * `SendOptions.context`), so shared config like a `projectRoot` is read by
409
+ * every tool instead of baked into each factory. SE12 — `ctx.messages` is a
410
+ * read-only, text-only projection of the current turn's transcript (see
411
+ * {@link ToolContextMessage}); `defineSubAgent`'s `messageFilter` consumes it.
412
+ * #119 — `ctx.threadId` is the run's session identity (the key passed to
413
+ * `Agent.getOrCreate(sessionId, …)`, or the agent's own id), so a stateful tool
414
+ * shared across sessions can scope its state per session instead of leaking it.
415
+ */
416
+ handler: (input: Record<string, unknown>, ctx?: {
417
+ signal?: AbortSignal;
418
+ context?: unknown;
419
+ messages?: readonly ToolContextMessage[];
420
+ threadId?: string;
421
+ }) => string | ToolResultContentBlock[] | Promise<string | ToolResultContentBlock[]>;
422
+ }
423
+
424
+ /**
425
+ * Type-leaf — base message types shared between `conversation.ts` and
426
+ * `updates.ts`. Extracted to break LOW type-only cycle #6 (audit
427
+ * `architecture-output/final_report.md`) per plan arch-review-fixes-2026-06-06
428
+ * § Phase 4 / T4.1 (D438).
429
+ *
430
+ * Public surface unchanged — `types/conversation.ts` re-exports `UserMessage`
431
+ * from this leaf so `import type { UserMessage } from "@theokit/sdk"` keeps
432
+ * resolving.
433
+ *
434
+ * @public
435
+ */
436
+ /**
437
+ * User-authored message in a conversation history.
438
+ *
439
+ * @public
440
+ */
441
+ interface UserMessage {
442
+ text: string;
443
+ }
444
+
445
+ /**
446
+ * Single tool call event. The internal `args` and `result` shapes are NOT stable.
447
+ *
448
+ * @public
449
+ */
450
+ interface ToolCall {
451
+ callId: string;
452
+ name: string;
453
+ args?: unknown;
454
+ result?: unknown;
455
+ }
456
+ /**
457
+ * Incremental text token from the assistant.
458
+ *
459
+ * @public
460
+ */
461
+ interface TextDeltaUpdate {
462
+ type: "text-delta";
463
+ text: string;
464
+ }
465
+ /**
466
+ * Incremental reasoning token.
467
+ *
468
+ * @public
469
+ */
470
+ interface ThinkingDeltaUpdate {
471
+ type: "thinking-delta";
472
+ text: string;
473
+ }
474
+ /**
475
+ * Emitted when a reasoning block completes.
476
+ *
477
+ * @public
478
+ */
479
+ interface ThinkingCompletedUpdate {
480
+ type: "thinking-completed";
481
+ thinkingDurationMs: number;
482
+ }
483
+ /**
484
+ * Tool call started — args committed.
485
+ *
486
+ * @public
487
+ */
488
+ interface ToolCallStartedUpdate {
489
+ type: "tool-call-started";
490
+ callId: string;
491
+ toolCall: ToolCall;
492
+ modelCallId: string;
493
+ }
494
+ /**
495
+ * Tool call arguments streaming in incrementally.
496
+ *
497
+ * @public
498
+ */
499
+ interface PartialToolCallUpdate {
500
+ type: "partial-tool-call";
501
+ callId: string;
502
+ toolCall: ToolCall;
503
+ modelCallId: string;
504
+ }
505
+ /**
506
+ * Tool call completed.
507
+ *
508
+ * @public
509
+ */
510
+ interface ToolCallCompletedUpdate {
511
+ type: "tool-call-completed";
512
+ callId: string;
513
+ toolCall: ToolCall;
514
+ modelCallId: string;
515
+ }
516
+ /**
517
+ * Token count delta for usage tracking.
518
+ *
519
+ * @public
520
+ */
521
+ interface TokenDeltaUpdate {
522
+ type: "token-delta";
523
+ tokens: number;
524
+ }
525
+ /**
526
+ * Conversation step started.
527
+ *
528
+ * @public
529
+ */
530
+ interface StepStartedUpdate {
531
+ type: "step-started";
532
+ stepId: number;
533
+ }
534
+ /**
535
+ * Conversation step completed.
536
+ *
537
+ * @public
538
+ */
539
+ interface StepCompletedUpdate {
540
+ type: "step-completed";
541
+ stepId: number;
542
+ stepDurationMs: number;
543
+ }
544
+ /**
545
+ * Turn ended with usage summary.
546
+ *
547
+ * @public
548
+ */
549
+ interface TurnEndedUpdate {
550
+ type: "turn-ended";
551
+ usage?: {
552
+ inputTokens: number;
553
+ outputTokens: number;
554
+ cacheReadTokens: number;
555
+ cacheWriteTokens: number;
556
+ };
557
+ }
558
+ /**
559
+ * User message appended to the conversation.
560
+ *
561
+ * @public
562
+ */
563
+ interface UserMessageAppendedUpdate {
564
+ type: "user-message-appended";
565
+ userMessage: UserMessage;
566
+ }
567
+ /** @public */
568
+ interface SummaryUpdate {
569
+ type: "summary";
570
+ summary: string;
571
+ }
572
+ /** @public */
573
+ interface SummaryStartedUpdate {
574
+ type: "summary-started";
575
+ }
576
+ /** @public */
577
+ interface SummaryCompletedUpdate {
578
+ type: "summary-completed";
579
+ }
580
+ /** @public */
581
+ interface ShellOutputDeltaUpdate {
582
+ type: "shell-output-delta";
583
+ event: Record<string, unknown>;
584
+ }
585
+ /**
586
+ * Lowest-level raw update from a run. Pass `onDelta` to `agent.send()` to
587
+ * consume these. Finer-grained than `SDKMessage` events.
588
+ *
589
+ * @public
590
+ */
591
+ type InteractionUpdate = TextDeltaUpdate | ThinkingDeltaUpdate | ThinkingCompletedUpdate | ToolCallStartedUpdate | ToolCallCompletedUpdate | PartialToolCallUpdate | TokenDeltaUpdate | StepStartedUpdate | StepCompletedUpdate | TurnEndedUpdate | UserMessageAppendedUpdate | SummaryUpdate | SummaryStartedUpdate | SummaryCompletedUpdate | ShellOutputDeltaUpdate;
592
+
593
+ /**
594
+ * Plain assistant message in a conversation history.
595
+ *
596
+ * @public
597
+ */
598
+ interface AssistantMessage {
599
+ text: string;
600
+ }
601
+ /**
602
+ * Reasoning step in a conversation history.
603
+ *
604
+ * @public
605
+ */
606
+ interface ThinkingMessage {
607
+ text: string;
608
+ thinkingDurationMs?: number;
609
+ }
610
+ /**
611
+ * Shell command executed during a run.
612
+ *
613
+ * @public
614
+ */
615
+ interface ShellCommand {
616
+ command: string;
617
+ workingDirectory?: string;
618
+ }
619
+ /**
620
+ * Output of a shell command.
621
+ *
622
+ * @public
623
+ */
624
+ interface ShellOutput {
625
+ stdout: string;
626
+ stderr: string;
627
+ exitCode: number;
628
+ }
629
+ /**
630
+ * Single step inside an agent turn.
631
+ *
632
+ * @public
633
+ */
634
+ /**
635
+ * Result of a tool invocation. Pairs with the preceding `toolCall` step
636
+ * by `callId`. `isError: true` when the tool returned a failure result.
637
+ *
638
+ * T2.3 — added so `Run.conversation()` surfaces the full interaction
639
+ * including tool results (parity with OpenAI Agents `RunResult.new_items`).
640
+ *
641
+ * @public
642
+ */
643
+ interface ToolResult {
644
+ callId: string;
645
+ name: string;
646
+ result: string;
647
+ isError: boolean;
648
+ }
649
+ type ConversationStep = {
650
+ type: "assistantMessage";
651
+ message: AssistantMessage;
652
+ } | {
653
+ type: "toolCall";
654
+ message: ToolCall;
655
+ } | {
656
+ type: "toolResult";
657
+ message: ToolResult;
658
+ } | {
659
+ type: "thinkingMessage";
660
+ message: ThinkingMessage;
661
+ };
662
+ /**
663
+ * Agent turn: user message + assistant/tool/thinking steps.
664
+ *
665
+ * @public
666
+ */
667
+ interface AgentConversationTurn {
668
+ userMessage?: UserMessage;
669
+ steps: ConversationStep[];
670
+ }
671
+ /**
672
+ * Shell turn: a command and its output.
673
+ *
674
+ * @public
675
+ */
676
+ interface ShellConversationTurn {
677
+ shellCommand?: ShellCommand;
678
+ shellOutput?: ShellOutput;
679
+ }
680
+ /**
681
+ * Structured per-turn view of a run.
682
+ *
683
+ * @public
684
+ */
685
+ type ConversationTurn = {
686
+ type: "agentConversationTurn";
687
+ turn: AgentConversationTurn;
688
+ } | {
689
+ type: "shellConversationTurn";
690
+ turn: ShellConversationTurn;
691
+ };
692
+
693
+ /**
694
+ * Child-process environment policy contract (#54).
695
+ *
696
+ * The DIP-correct home for the `EnvPolicy` contract type: the domain `types/`
697
+ * layer owns the contract, and the application-layer implementation
698
+ * (`internal/runtime/lifecycle/env-policy.ts`) re-exports it for back-compat
699
+ * while owning the runtime logic (`resolveChildEnv`, secret patterns, …).
700
+ *
701
+ * Modes:
702
+ * - `inherit-scrubbed` (DEFAULT) — inherit all parent vars EXCEPT secret-like
703
+ * names (`*KEY*`, `*SECRET*`, `*TOKEN*`, `*PASSWORD*`, `*_AUTH*`). Non-breaking:
704
+ * existing spawns keep every non-secret var; only secrets stop leaking.
705
+ * - `core` — inherit ONLY a safe base allowlist (PATH/HOME/…); strongest scrub.
706
+ * - `all` — explicit opt-out: inherit everything, secrets included.
707
+ *
708
+ * @public
709
+ */
710
+ type EnvPolicy = "inherit-scrubbed" | "core" | "all";
711
+
712
+ /**
713
+ * MCP server configuration accepted by `Agent.create()` and `agent.send()`.
714
+ *
715
+ * @public
716
+ */
717
+ type McpStdioServerConfig = {
718
+ type?: "stdio";
719
+ command: string;
720
+ args?: string[];
721
+ env?: Record<string, string>;
722
+ /** Local agents only. Cloud rejects this field. */
723
+ cwd?: string;
724
+ /**
725
+ * #59 — per-request timeout in ms. A request that gets no reply within this
726
+ * window rejects with a typed `NetworkError` (`code: "mcp_timeout"`) instead
727
+ * of hanging the agent loop forever. Default 30_000.
728
+ */
729
+ requestTimeoutMs?: number;
730
+ /**
731
+ * #54 — env inherit/scrub policy for the spawned MCP server process. Defaults
732
+ * to `"inherit-scrubbed"` (drop secret-like host vars: `*KEY*`/`*SECRET*`/
733
+ * `*TOKEN*`/`*PASSWORD*`/`*_AUTH*`/…) so a third-party MCP server binary cannot
734
+ * exfiltrate host secrets via the environment. `env` above is merged AFTER the
735
+ * policy and always wins. Pass `"all"` to restore full inheritance.
736
+ */
737
+ envPolicy?: EnvPolicy;
738
+ };
739
+ /**
740
+ * OAuth-style auth bundle for HTTP/SSE MCP servers.
741
+ *
742
+ * @public
743
+ */
744
+ interface McpAuthConfig {
745
+ CLIENT_ID: string;
746
+ CLIENT_SECRET?: string;
747
+ scopes?: string[];
748
+ /**
749
+ * OAuth 2.1 PKCE flow configuration (ADR D41, v1.2+). When present, the
750
+ * SDK runs the PKCE flow on first use and stores tokens via keychain or
751
+ * file. Without this, the SDK relies on `CLIENT_SECRET` + manual headers.
752
+ */
753
+ oauth?: McpOAuthConfig;
754
+ }
755
+ /**
756
+ * OAuth 2.1 PKCE flow descriptor. See ADR D41.
757
+ *
758
+ * @public
759
+ */
760
+ interface McpOAuthConfig {
761
+ /** Authorization endpoint (e.g. https://api.notion.com/v1/oauth/authorize). */
762
+ authorizationEndpoint: string;
763
+ /** Token endpoint (e.g. https://api.notion.com/v1/oauth/token). */
764
+ tokenEndpoint: string;
765
+ /** Where the OAuth `code` is received. */
766
+ redirectMode: "manual" | "localhost";
767
+ /** Localhost callback port (0 = random free port, default). */
768
+ localhostPort?: number;
769
+ /** Flow timeout in ms (default 300_000 = 5min). */
770
+ timeoutMs?: number;
771
+ }
772
+ /**
773
+ * HTTP or SSE MCP server.
774
+ *
775
+ * @public
776
+ */
777
+ type McpHttpServerConfig = {
778
+ type?: "http" | "sse";
779
+ url: string;
780
+ /** Passed through. `Authorization` works here. */
781
+ headers?: Record<string, string>;
782
+ auth?: McpAuthConfig;
783
+ /**
784
+ * #59 — per-request timeout in ms, enforced via `AbortSignal.timeout`. A
785
+ * fetch that does not respond within this window rejects with a typed
786
+ * `NetworkError` (`code: "mcp_timeout"`). Default 30_000.
787
+ */
788
+ requestTimeoutMs?: number;
789
+ };
790
+ /**
791
+ * Union of MCP server configs.
792
+ *
793
+ * @public
794
+ */
795
+ type McpServerConfig = McpStdioServerConfig | McpHttpServerConfig;
796
+
797
+ /**
798
+ * Tool invocation block emitted by the assistant.
799
+ *
800
+ * @public
801
+ */
802
+ interface ToolUseBlock {
803
+ type: "tool_use";
804
+ id: string;
805
+ name: string;
806
+ /** Tool args are not part of the stable schema. Treat as unknown and parse defensively. */
807
+ input: unknown;
808
+ }
809
+ /**
810
+ * Init metadata. Emitted once at the start of a run.
811
+ *
812
+ * @public
813
+ */
814
+ interface SDKSystemMessage {
815
+ type: "system";
816
+ subtype?: "init";
817
+ agent_id: string;
818
+ run_id: string;
819
+ model?: ModelSelection;
820
+ tools?: string[];
821
+ }
822
+ /**
823
+ * Echo of the user prompt for this run.
824
+ *
825
+ * @public
826
+ */
827
+ interface SDKUserMessageEvent {
828
+ type: "user";
829
+ agent_id: string;
830
+ run_id: string;
831
+ message: {
832
+ role: "user";
833
+ content: TextBlock[];
834
+ };
835
+ }
836
+ /**
837
+ * Model text output for this run.
838
+ *
839
+ * @public
840
+ */
841
+ interface SDKAssistantMessage {
842
+ type: "assistant";
843
+ agent_id: string;
844
+ run_id: string;
845
+ message: {
846
+ role: "assistant";
847
+ content: Array<TextBlock | ToolUseBlock>;
848
+ };
849
+ }
850
+ /**
851
+ * Reasoning content.
852
+ *
853
+ * @public
854
+ */
855
+ interface SDKThinkingMessage {
856
+ type: "thinking";
857
+ agent_id: string;
858
+ run_id: string;
859
+ text: string;
860
+ thinking_duration_ms?: number;
861
+ }
862
+ /**
863
+ * Tool invocation lifecycle event. Emitted at start with `args`, then again on
864
+ * completion with `result`.
865
+ *
866
+ * Tool `args` and `result` are NOT part of the stable schema — treat as unknown.
867
+ *
868
+ * @public
869
+ */
870
+ interface SDKToolUseMessage {
871
+ type: "tool_call";
872
+ agent_id: string;
873
+ run_id: string;
874
+ call_id: string;
875
+ name: string;
876
+ status: "running" | "completed" | "error";
877
+ args?: unknown;
878
+ result?: unknown;
879
+ truncated?: {
880
+ args?: boolean;
881
+ result?: boolean;
882
+ };
883
+ }
884
+ /**
885
+ * Cloud run lifecycle transitions.
886
+ *
887
+ * @public
888
+ */
889
+ interface SDKStatusMessage {
890
+ type: "status";
891
+ agent_id: string;
892
+ run_id: string;
893
+ status: "CREATING" | "RUNNING" | "FINISHED" | "ERROR" | "CANCELLED" | "EXPIRED";
894
+ message?: string;
895
+ }
896
+ /**
897
+ * Task-level milestones and summaries.
898
+ *
899
+ * @public
900
+ */
901
+ interface SDKTaskMessage {
902
+ type: "task";
903
+ agent_id: string;
904
+ run_id: string;
905
+ status?: string;
906
+ text?: string;
907
+ }
908
+ /**
909
+ * Awaiting user input or approval.
910
+ *
911
+ * @public
912
+ */
913
+ interface SDKRequestMessage {
914
+ type: "request";
915
+ agent_id: string;
916
+ run_id: string;
917
+ request_id: string;
918
+ }
919
+ /**
920
+ * Partial object emitted during `Agent.streamObject<T>` streaming (ADR D45).
921
+ * `partial` is `DeepPartial<z.infer<T>>` at the typed iterator level but
922
+ * erased to `unknown` here because SDKMessage union is non-generic.
923
+ *
924
+ * @public
925
+ */
926
+ interface SDKObjectDelta {
927
+ type: "object_delta";
928
+ agent_id: string;
929
+ run_id: string;
930
+ partial: unknown;
931
+ attempt: number;
932
+ }
933
+ /**
934
+ * Discriminated union of all stream events. Discriminate on `type`.
935
+ *
936
+ * All events include `agent_id` and `run_id`.
937
+ *
938
+ * @public
939
+ */
940
+ type SDKMessage = SDKSystemMessage | SDKUserMessageEvent | SDKAssistantMessage | SDKThinkingMessage | SDKToolUseMessage | SDKStatusMessage | SDKTaskMessage | SDKRequestMessage | SDKObjectDelta;
941
+
942
+ /**
943
+ * Public type contract for token usage + cost tracking (ADRs D376-D379).
944
+ *
945
+ * Surfaces via `RunResult.usage` + `RunResult.cost` after every Run.
946
+ * Re-exported through the package barrel; consumers import from
947
+ * `@theokit/sdk`.
948
+ *
949
+ * @public
950
+ */
951
+ /**
952
+ * Token usage observed during a Run. 5 closed buckets (D376) cover
953
+ * 100% of providers in 2026: OpenAI Chat / OpenAI Responses (o-series
954
+ * with reasoning) / Anthropic Messages (with prompt caching).
955
+ *
956
+ * `totalTokens` is derived (`inputTokens + outputTokens`); EC-10
957
+ * invariant: SDK must never emit `totalTokens !== inputTokens + outputTokens`.
958
+ *
959
+ * `requests[]` is the per-request breakdown (mirror a peer SDK)
960
+ * — populated only when the run made > 1 LLM call.
961
+ */
962
+ interface TokenUsage {
963
+ readonly inputTokens: number;
964
+ readonly outputTokens: number;
965
+ readonly cacheReadTokens?: number;
966
+ readonly cacheWriteTokens?: number;
967
+ readonly reasoningTokens?: number;
968
+ readonly totalTokens: number;
969
+ /** Per-request breakdown; absent when the run made a single LLM call. */
970
+ readonly requests?: ReadonlyArray<Omit<TokenUsage, "requests">>;
971
+ }
972
+ /**
973
+ * Cost confidence level (D377).
974
+ * - `actual`: returned by a provider billing API (e.g. OpenRouter `/generation`).
975
+ * - `estimated`: computed from bundled pricing snapshot.
976
+ * - `included`: subscription-included route (Codex CLI, Claude Pro).
977
+ * - `unknown`: pricing data unavailable; `amountUsd` is undefined.
978
+ */
979
+ type CostStatus = "actual" | "estimated" | "included" | "unknown";
980
+ /** Source of the cost figure for caller-side audit. */
981
+ type CostSource = "openrouter_api" | "litellm_snapshot" | "user_override" | "subscription_included" | "unknown";
982
+ /**
983
+ * Cost breakdown attached to `RunResult.cost`. When `status === "unknown"`,
984
+ * `amountUsd` is `undefined` — DO NOT default to 0 (mentira). UI exibe
985
+ * `n/a` para unknown, `~$1.23` para estimated, `$1.23` para actual.
986
+ */
987
+ interface CostBreakdown {
988
+ readonly amountUsd: number | undefined;
989
+ readonly status: CostStatus;
990
+ readonly currency: "USD";
991
+ readonly source: CostSource;
992
+ readonly pricingVersion: string | undefined;
993
+ readonly notes?: ReadonlyArray<string>;
994
+ /** Per-bucket detail (in USD) for caller analytics. */
995
+ readonly detail?: {
996
+ readonly input?: number;
997
+ readonly output?: number;
998
+ readonly cacheRead?: number;
999
+ readonly cacheWrite?: number;
1000
+ readonly reasoning?: number;
1001
+ };
1002
+ }
1003
+
1004
+ /**
1005
+ * Options for the tool-result guard (spotlighting + PII redaction on tool
1006
+ * output). Contract type owned by `types/` (SE45 / D435) — the implementation
1007
+ * lives in `internal/agent-loop/tool-result-guard.ts`, which re-exports this.
1008
+ *
1009
+ * @public
1010
+ */
1011
+ interface ToolResultGuardOptions {
1012
+ /** Wrap tool-result content in explicit data boundaries (spotlighting). */
1013
+ delimit?: boolean;
1014
+ /** Redact common PII patterns (email, phone) in tool-result content. */
1015
+ redactPii?: boolean;
1016
+ }
1017
+ /**
1018
+ * Lifecycle status of a {@link Run}.
1019
+ *
1020
+ * @public
1021
+ */
1022
+ type RunStatus = "running" | "finished" | "error" | "cancelled";
1023
+ /**
1024
+ * Operations that may or may not be supported on a given {@link Run}, or on
1025
+ * its parent agent.
1026
+ *
1027
+ * Runtime-specific availability — query at runtime with `run.supports(op)` and
1028
+ * read the human reason via `run.unsupportedReason(op)`.
1029
+ *
1030
+ * @public
1031
+ */
1032
+ type RunOperation = "stream" | "wait" | "cancel" | "conversation" | "listArtifacts" | "downloadArtifact" | "runUntil" | "runToCompletion" | "streamToCompletion" | "fork" | "usePersonality" | "workflow";
1033
+ /**
1034
+ * Git metadata attached to cloud runs.
1035
+ *
1036
+ * @public
1037
+ */
1038
+ interface RunGitInfo {
1039
+ branches: Array<{
1040
+ repoUrl: string;
1041
+ branch?: string;
1042
+ prUrl?: string;
1043
+ }>;
1044
+ }
1045
+ /**
1046
+ * SE3 — provenance of the turn that produced a run: WHO triggered it. Stamped in
1047
+ * the multi-agent path (Squad / a2a / handoff / background-delegation) and
1048
+ * forwarded onto {@link RunResult.origin} — so consumers can attribute/route
1049
+ * turns by their trigger. Metadata-only; discriminate on `kind`. Mirrors the
1050
+ * Anthropic Agent SDK's `origin` shape.
1051
+ *
1052
+ * Encoding note (distinct from absence): an ABSENT origin (`undefined`) means the
1053
+ * provenance was NOT stamped — the default for a plain `agent.send()`. The explicit
1054
+ * `{ kind: "human" }` is a positive marker a HOST stamps to say "this turn is
1055
+ * definitely from a human" (e.g. to distinguish a real user message from an
1056
+ * un-attributed one in an audit UI). The two are not interchangeable: `undefined`
1057
+ * = unknown/unstamped; `{ kind: "human" }` = explicitly human. Consumers writing an
1058
+ * exhaustive `switch (origin?.kind)` therefore handle `undefined` (unstamped) and
1059
+ * `"human"` (explicit) as separate, meaningful cases.
1060
+ *
1061
+ * @public
1062
+ */
1063
+ /**
1064
+ * SE3 — provenance of a turn (metadata-only; never changes routing/dispatch).
1065
+ * Producers: `peer` (Squad step / a2a envelope), `coordinator` (subagent
1066
+ * delegation), `auto-continuation` (the run-to-completion / stream-to-completion
1067
+ * driver's continuation rounds) are stamped BY the SDK. `human` and
1068
+ * `task-notification` are positive markers a HOST stamps (e.g. re-sending an
1069
+ * agent after a background task completed). Absent origin ⇒ `undefined`.
1070
+ *
1071
+ * @public
1072
+ */
1073
+ type MessageOrigin =
1074
+ /** Explicitly a human-triggered turn — a positive marker a host stamps (NOT the
1075
+ * same as an absent/unstamped origin, which is `undefined`). */
1076
+ {
1077
+ readonly kind: "human";
1078
+ }
1079
+ /** Another agent (a Squad peer or an a2a sender) triggered this turn. */
1080
+ | {
1081
+ readonly kind: "peer";
1082
+ readonly from: string;
1083
+ }
1084
+ /** A background task's completion re-entered the agent as a follow-up turn. */
1085
+ | {
1086
+ readonly kind: "task-notification";
1087
+ }
1088
+ /** A delegating/handoff coordinator triggered this turn. `from` is the coordinator's
1089
+ * id when known, omitted for an anonymous coordinator. */
1090
+ | {
1091
+ readonly kind: "coordinator";
1092
+ readonly from?: string;
1093
+ }
1094
+ /** The loop's continuation driver re-sent to continue truncated work. */
1095
+ | {
1096
+ readonly kind: "auto-continuation";
1097
+ };
1098
+ /**
1099
+ * Terminal result of a {@link Run}.
1100
+ *
1101
+ * @public
1102
+ */
1103
+ interface RunResult {
1104
+ id: string;
1105
+ status: "finished" | "error" | "cancelled";
1106
+ result?: string;
1107
+ /**
1108
+ * SE24 — set when a guardrail processor called `abort()`. The run stops
1109
+ * (`status: "cancelled"`) and `tripwire` carries the blocking reason +
1110
+ * processor id. `undefined` on every non-guardrail outcome. A
1111
+ * `throwOnError: true` agent resolves normally on a tripwire (status is
1112
+ * `"cancelled"`, not `"error"`). On an OUTPUT block the model already ran, so
1113
+ * `usage`/`cost` survive (billing) while `result` is suppressed; an INPUT
1114
+ * block never reaches the model, so `usage` is absent.
1115
+ *
1116
+ * @public
1117
+ */
1118
+ tripwire?: ProcessorTripwire;
1119
+ model?: ModelSelection;
1120
+ durationMs?: number;
1121
+ git?: RunGitInfo;
1122
+ /**
1123
+ * SE3 — provenance forwarded from {@link SendOptions.origin}: who triggered
1124
+ * this turn (human / peer / task-notification / coordinator / auto-continuation).
1125
+ * `undefined` means the provenance was not stamped (a plain `agent.send()`).
1126
+ * Metadata-only — never affects routing.
1127
+ *
1128
+ * @public
1129
+ */
1130
+ origin?: MessageOrigin;
1131
+ /**
1132
+ * Structured error detail, populated when `status === "error"`. Surfaces
1133
+ * the diagnostic that emit-error-event pushes into the stream so callers
1134
+ * that don't drain `run.stream()` still get the cause via `run.wait()`.
1135
+ *
1136
+ * For successful runs (`status: "finished"`) this is undefined.
1137
+ *
1138
+ * @public
1139
+ */
1140
+ error?: RunErrorDetail;
1141
+ /**
1142
+ * Token usage observed for this run (ADR D376). Populated in every
1143
+ * status where ≥1 LLM call completed — including partial-failure
1144
+ * runs (EC-5). `undefined` only when zero LLM calls executed (e.g.,
1145
+ * abort before send).
1146
+ *
1147
+ * @public
1148
+ */
1149
+ usage?: TokenUsage;
1150
+ /**
1151
+ * Estimated/actual USD cost for this run (ADR D377). Always paired
1152
+ * with `usage` when populated. `cost.status` tells caller how to
1153
+ * trust the figure.
1154
+ *
1155
+ * @public
1156
+ */
1157
+ cost?: CostBreakdown;
1158
+ /**
1159
+ * M1-2: `true` when the run stopped because the agent loop hit its iteration
1160
+ * ceiling (`SendOptions.maxIterations` or the default of 8) while the model
1161
+ * still wanted to call tools — i.e. the work was silently truncated rather
1162
+ * than finished. `undefined`/absent on a clean finish. A continuation driver
1163
+ * (or a careful caller) inspects this to decide whether to send again.
1164
+ *
1165
+ * @public
1166
+ */
1167
+ stoppedAtIterationLimit?: boolean;
1168
+ /**
1169
+ * `true` when the run stopped because the **doom-loop guard** detected the model repeating
1170
+ * IDENTICAL tool calls (same name + same input) to the hard threshold — making no progress (e.g.
1171
+ * a tool that keeps failing and is retried unchanged). `undefined`/absent otherwise. Through the
1172
+ * continuation driver this surfaces as `terminal: "no_progress"` (a controlled stop, NOT a
1173
+ * truncation to re-send). Tune or disable via {@link SendOptions.doomLoop}.
1174
+ *
1175
+ * @public
1176
+ */
1177
+ stoppedByDoomLoop?: boolean;
1178
+ /**
1179
+ * SE34 — the per-send completion verdict, populated when
1180
+ * {@link SendOptions.completionCheck} was set AND the run finished. Absent on
1181
+ * non-finished runs and when no completion check was requested. Reuses the
1182
+ * shipped LLM-as-judge (same one `runUntil` drives). @public
1183
+ */
1184
+ completionCheck?: CompletionCheckResult;
1185
+ }
1186
+ /**
1187
+ * Doom-loop guard thresholds (see {@link SendOptions.doomLoop}). Both are counts of CONSECUTIVE
1188
+ * identical tool calls: `softThreshold` injects a one-time guidance nudge; `hardThreshold` stops.
1189
+ *
1190
+ * @public
1191
+ */
1192
+ interface DoomLoopThresholds {
1193
+ /** Consecutive-identical count at which a one-time guidance nudge is injected. Default 3. */
1194
+ softThreshold?: number;
1195
+ /** Consecutive-identical count at which the run stops (`no_progress`). Default 5. */
1196
+ hardThreshold?: number;
1197
+ }
1198
+ /**
1199
+ * Options for {@link SDKAgent.runToCompletion} (M1 Phase 3 — continuation driver).
1200
+ *
1201
+ * @public
1202
+ */
1203
+ interface RunToCompletionOptions {
1204
+ /**
1205
+ * Maximum number of continuation rounds (re-sends) before giving up with
1206
+ * `terminal: "step_limit"`. Default 5. A hard ceiling that prevents a
1207
+ * runaway loop when the model keeps truncating.
1208
+ */
1209
+ maxRounds?: number;
1210
+ /**
1211
+ * The short prompt re-sent after a truncated round to make the (stateful)
1212
+ * agent resume. Defaults to a generic "continue" instruction. The original
1213
+ * conversation is preserved by the agent's session, so this need not repeat
1214
+ * the task.
1215
+ */
1216
+ continuationPrompt?: string;
1217
+ /** Called once per truncated round that triggers a re-send (for metrics/logging). */
1218
+ onTruncated?: (event: {
1219
+ round: number;
1220
+ }) => void | Promise<void>;
1221
+ /** Abort signal; checked between rounds — once aborted, no further round starts. */
1222
+ signal?: AbortSignal;
1223
+ /** Per-send options forwarded to each underlying `send()` (e.g. `maxIterations`). */
1224
+ sendOptions?: SendOptions;
1225
+ }
1226
+ /**
1227
+ * Result of {@link SDKAgent.runToCompletion}.
1228
+ *
1229
+ * @public
1230
+ */
1231
+ interface RunToCompletionResult {
1232
+ /**
1233
+ * Why the driver stopped:
1234
+ * - `"done"` — a round finished without truncating (the model is done).
1235
+ * - `"step_limit"` — `maxRounds` exhausted (or aborted) while still truncating.
1236
+ * - `"no_progress"` — two consecutive rounds produced empty output.
1237
+ */
1238
+ terminal: "done" | "step_limit" | "no_progress";
1239
+ /**
1240
+ * Index of the final round. Round 0 is the initial `send`; rounds ≥ 1 are
1241
+ * continuation re-sends. So `terminal: "done"` with `rounds: 0` means the
1242
+ * first send finished without truncating; `rounds: N` means N continuation
1243
+ * re-sends happened. For `step_limit`, `rounds` equals `maxRounds`.
1244
+ */
1245
+ rounds: number;
1246
+ /** The `RunResult` of the final round. */
1247
+ lastResult: RunResult;
1248
+ /** Token usage summed across all rounds; `undefined` when no round reported usage. */
1249
+ usage?: TokenUsage;
1250
+ }
1251
+ /**
1252
+ * Result of {@link SDKAgent.streamToCompletion} (V3-4 — the STREAMING continuation
1253
+ * driver). Same shape + terminal semantics as {@link RunToCompletionResult}; it is
1254
+ * the generator's RETURN value (read it via a manual `gen.next()` loop — a plain
1255
+ * `for await...of` consumes the yielded `SDKMessage`s but discards this return value).
1256
+ *
1257
+ * @public
1258
+ */
1259
+ type StreamToCompletionResult = RunToCompletionResult;
1260
+ /**
1261
+ * Structured error attached to a {@link RunResult} when the underlying run
1262
+ * transitioned to `"error"` status. `message` is always present; `code` is
1263
+ * a stable identifier suitable for branching (e.g. `"llm_4xx"`,
1264
+ * `"tool_dispatch_failed"`, `"mcp_init_failed"`); `cause` is the raw error
1265
+ * for further inspection when available.
1266
+ *
1267
+ * @public
1268
+ */
1269
+ interface RunErrorDetail {
1270
+ message: string;
1271
+ code?: string;
1272
+ cause?: unknown;
1273
+ }
1274
+ /**
1275
+ * Dimensions of an inline image attachment.
1276
+ *
1277
+ * @public
1278
+ */
1279
+ interface SDKImageDimension {
1280
+ width: number;
1281
+ height: number;
1282
+ }
1283
+ /**
1284
+ * Either a remote URL or inline base64 payload.
1285
+ *
1286
+ * @public
1287
+ */
1288
+ type SDKImage = {
1289
+ url: string;
1290
+ dimension?: SDKImageDimension;
1291
+ } | {
1292
+ data: string;
1293
+ mimeType: string;
1294
+ dimension?: SDKImageDimension;
1295
+ };
1296
+ /**
1297
+ * Structured form of `agent.send()`'s message argument. Use it to send images
1298
+ * alongside text.
1299
+ *
1300
+ * @public
1301
+ */
1302
+ interface SDKUserMessage {
1303
+ text: string;
1304
+ images?: SDKImage[];
1305
+ }
1306
+ /**
1307
+ * Per-send overrides and callbacks.
1308
+ *
1309
+ * @public
1310
+ */
1311
+ interface SendOptions {
1312
+ /**
1313
+ * Per-send model override. SE8 — accepts a bare-string id shorthand
1314
+ * (`"openai/gpt-4o-mini"`, normalized to `{ id }`) OR a {@link ModelSelection}
1315
+ * object (use the object form to pass `params`).
1316
+ */
1317
+ model?: string | ModelSelection;
1318
+ /**
1319
+ * SE3 — provenance of this turn (who triggered it). Stamped by the multi-agent
1320
+ * path (Squad peer, a2a sender, coordinator/handoff, background task-notification)
1321
+ * and forwarded onto {@link RunResult.origin}. Metadata-only — the value never
1322
+ * changes routing or dispatch. Omit to leave the turn un-attributed; a host may
1323
+ * pass `{ kind: "human" }` to positively mark a human turn.
1324
+ */
1325
+ origin?: MessageOrigin;
1326
+ /**
1327
+ * SE2 — opt-in typed runtime-EVENT sink. Receives out-of-band `RunEvent`s
1328
+ * (permission_denied, tool_progress, rate_limit, task_*, compact_boundary) for
1329
+ * observability, ADDITIVE to the `SDKMessage` content stream. Best-effort: a
1330
+ * throwing sink never breaks the run. Discriminate on `event.type`.
1331
+ */
1332
+ onRunEvent?: RunEventSink;
1333
+ /**
1334
+ * Doom-loop guard config. The loop stops (with `terminal: "no_progress"`, `RunResult.stoppedByDoomLoop`)
1335
+ * when the model repeats IDENTICAL tool calls to the hard threshold. On by default with generous
1336
+ * thresholds (soft 3 / hard 5). Set `false` to disable, or an object to tune the thresholds.
1337
+ */
1338
+ doomLoop?: false | DoomLoopThresholds;
1339
+ /**
1340
+ * Per-call system prompt override. Wins over `AgentOptions.systemPrompt`.
1341
+ * String only — for dynamic resolvers, configure on `AgentOptions`. An
1342
+ * empty string is honoured (it explicitly clears the system context).
1343
+ */
1344
+ systemPrompt?: string;
1345
+ /** Fully replaces creation-time servers for this run (not merged). */
1346
+ mcpServers?: Record<string, McpServerConfig>;
1347
+ /**
1348
+ * Per-call inline custom tools. Fully replaces `AgentOptions.tools` for
1349
+ * this run (not merged). Local runtime only — cloud agents reject any
1350
+ * non-empty per-call tools array with the same error code as creation
1351
+ * (`cloud_custom_tools_rejected`). Semantics:
1352
+ * - `undefined` → fall back to `AgentOptions.tools`
1353
+ * - `[]` → explicitly clear (no custom tools for this run)
1354
+ * - `[t1, t2]` → use exactly these tools for this run
1355
+ */
1356
+ tools?: CustomTool[];
1357
+ /**
1358
+ * The set of repo-relative file paths in scope for THIS send (local runtime).
1359
+ * Declares "which files am I working on" so path-scoped rule files
1360
+ * (`.theokit/rules/*.md` with `paths:`/`globs:`, and `.cursor/rules/*.mdc`
1361
+ * globs) activate when a pattern matches one of these paths. `alwaysApply`
1362
+ * rules load regardless. Omit ⇒ only unconditional rules load (the create-time
1363
+ * snapshot is untouched). Matching is glob-based (`**`, `*`, `?`).
1364
+ */
1365
+ contextPaths?: readonly string[];
1366
+ /**
1367
+ * SE1 — the permission mode for THIS run, threaded to a registered
1368
+ * `PermissionPlugin`'s pre-tool gate. Precedence: this per-send value wins over
1369
+ * `AgentOptions.permissionMode` (creation-time default). Modes: `default` (rules
1370
+ * decide; unmatched ⇒ fail-closed ask), `plan` (read-only — allow rules pass,
1371
+ * everything else denied), `acceptEdits` (auto-approve unmatched, honor explicit
1372
+ * ask rules), `bypass` / `bypassPermissions` (allow all except an explicit deny).
1373
+ * Absent ⇒ the plugin's own construction-time mode applies. Local runtime.
1374
+ */
1375
+ permissionMode?: PermissionMode;
1376
+ onStep?: (args: {
1377
+ step: ConversationStep;
1378
+ }) => void | Promise<void>;
1379
+ onDelta?: (args: {
1380
+ update: InteractionUpdate;
1381
+ }) => void | Promise<void>;
1382
+ /**
1383
+ * Per-call tool gate (OpenAI/OpenRouter `tool_choice`). `"none"` forces a text answer even when
1384
+ * the agent has tools registered — used by an agent loop to force a closing summary at its step
1385
+ * ceiling (a cached agent's tools cannot be un-registered, so the gate is per-send). `"required"`
1386
+ * forces a tool call; `"auto"` (or omitted) is the default. Local runtime; OpenAI-compat providers.
1387
+ */
1388
+ toolChoice?: "auto" | "none" | "required";
1389
+ /**
1390
+ * SE18 — per-send runtime tool subset (**local runtime only**; cloud agents
1391
+ * ignore it). When set, only tools whose name is in this list may be called for
1392
+ * this send; a call to any other registered tool is vetoed at dispatch (the same
1393
+ * `withToolWhitelist` path `Agent.fork`'s `allowedTools` uses — NOT
1394
+ * `PermissionEngine`). Composes with `toolChoice`: `activeTools` narrows the set,
1395
+ * `toolChoice` gates calling within it. Absent ⇒ the full toolset is available.
1396
+ *
1397
+ * Matching is EXACT against the tool's registered name — the same name the
1398
+ * dispatch sees after tool-name repair (mirrors `Agent.fork`'s `allowedTools`;
1399
+ * it is NOT lowercased for you, so a mixed-case registered tool needs its exact
1400
+ * registered name here). An empty list (`[]`) vetoes EVERY tool (fail-closed —
1401
+ * "restrict to the empty set"); pass `undefined` (or omit) for no restriction.
1402
+ * A subagent invoked within this send inherits the whitelist via async-context
1403
+ * propagation unless it declares its own tool scope.
1404
+ */
1405
+ activeTools?: string[];
1406
+ /** Local agents only. Expire a stuck active run before starting this message. */
1407
+ local?: {
1408
+ force?: boolean;
1409
+ };
1410
+ /**
1411
+ * Optional `AbortSignal` propagated to memory adapter `pre_user_send`
1412
+ * hooks (EC-H). Note: the LLM HTTP call itself is NOT cancellable
1413
+ * mid-stream — same constraint as `Agent.batch` (ADR D140).
1414
+ *
1415
+ * @public
1416
+ */
1417
+ signal?: AbortSignal;
1418
+ /**
1419
+ * M7 — an opaque user `context` forwarded to every tool handler's `ctx.context`
1420
+ * for this run. Set shared config (e.g. `projectRoot`) once here instead of
1421
+ * baking it into each tool factory (mirrors a framework `experimental_context`,
1422
+ * a peer framework `RuntimeContext`, a peer SDK `RunContext`).
1423
+ *
1424
+ * @public
1425
+ */
1426
+ context?: unknown;
1427
+ /**
1428
+ * #58 — per-tool execution timeout in ms. Each tool call is bounded by this
1429
+ * deadline (merged with `signal`); a hung tool yields a typed timeout result
1430
+ * (exit 124) instead of wedging the run. Undefined = no per-tool timeout.
1431
+ *
1432
+ * @public
1433
+ */
1434
+ perToolTimeoutMs?: number;
1435
+ /**
1436
+ * #57 — opt-in tool-result content guard applied before tool output reaches
1437
+ * the LLM. `{ delimit: true }` frames untrusted tool output as data
1438
+ * (prompt-injection mitigation); `{ redactPii: true }` redacts email/phone.
1439
+ * Undefined = no guard.
1440
+ *
1441
+ * @public
1442
+ */
1443
+ toolResultGuard?: ToolResultGuardOptions;
1444
+ /**
1445
+ * Opt-in task wrapping (ADRs D363, D374). When truthy, the entire
1446
+ * run is registered as a `Task` in the SDK's observable registry —
1447
+ * caller can list / inspect / cancel / subscribe via the `Task`
1448
+ * namespace. Default behavior (no `task` option) is byte-identical
1449
+ * to v1.1 (no Task overhead).
1450
+ *
1451
+ * Accepts:
1452
+ * - `true` — auto-generate task id; no extra metadata.
1453
+ * - `{ id, meta }` — user-supplied id (D368 grammar enforced) and/or
1454
+ * metadata attached to the handle's `meta` field.
1455
+ *
1456
+ * The work-fn `signal` is **merged** with `options.signal` (whichever
1457
+ * aborts first wins). Local agents only — CloudAgent throws
1458
+ * `UnsupportedTaskOperationError` (D370).
1459
+ *
1460
+ * @public
1461
+ */
1462
+ task?: true | {
1463
+ id?: string;
1464
+ meta?: Record<string, unknown>;
1465
+ };
1466
+ /**
1467
+ * Per-send ceiling on the agent loop's tool-calling turns (M1-2). Raises (or
1468
+ * lowers) the default cap of 8 for this single send — useful when one heavy
1469
+ * task needs more rounds than the agent's default. Must be a positive
1470
+ * integer; invalid values throw `ConfigurationError` at the boundary. When
1471
+ * unset, the loop uses the default of 8.
1472
+ *
1473
+ * @public
1474
+ */
1475
+ maxIterations?: number;
1476
+ /**
1477
+ * SE34 — per-send completion check (`isTaskComplete`). After this single
1478
+ * `send()` reaches a terminal `finished` state, the shipped LLM-as-judge
1479
+ * scores the final reply against `criteria` and surfaces the verdict on
1480
+ * {@link RunResult.completionCheck} + a `completion_check` run-event. This is
1481
+ * the finer-grained, single-`send()` gate (contrast `runUntil`, which judges
1482
+ * the FULL response BETWEEN sends). Opt-in — absent ⇒ the send is byte-identical
1483
+ * to today (no extra judge call). Non-finished runs skip the check. The judge
1484
+ * runs when `wait()` is called on the returned `Run`; a stream-only consumer
1485
+ * must call `wait()` to trigger the verdict + the `completion_check` event.
1486
+ *
1487
+ * @public
1488
+ */
1489
+ completionCheck?: CompletionCheck;
1490
+ }
1491
+ /**
1492
+ * SE34 — the per-send completion criterion (see {@link SendOptions.completionCheck}).
1493
+ *
1494
+ * @public
1495
+ */
1496
+ interface CompletionCheck {
1497
+ /** What "complete" means for this send — fed to the judge as the goal. */
1498
+ criteria: string;
1499
+ /** Judge model identifier. Default `"openai/gpt-4o-mini"`. */
1500
+ judgeModel?: string;
1501
+ /** Override env for the judge auxiliary agent. Default `OPENROUTER_API_KEY`. */
1502
+ apiKey?: string;
1503
+ }
1504
+ /**
1505
+ * SE34 — the resolved per-send completion verdict (see {@link RunResult.completionCheck}).
1506
+ *
1507
+ * @public
1508
+ */
1509
+ interface CompletionCheckResult {
1510
+ /** `true` when the judge ruled the send's reply satisfies the criteria. */
1511
+ complete: boolean;
1512
+ /** The judge's stated reason. */
1513
+ reason: string;
1514
+ /** `true` when the judge output could not be parsed — fail-safe `complete: false`. */
1515
+ parseFailed: boolean;
1516
+ }
1517
+ /**
1518
+ * Handle to a single prompt submission.
1519
+ *
1520
+ * @public
1521
+ */
1522
+ interface Run {
1523
+ readonly id: string;
1524
+ readonly agentId: string;
1525
+ readonly status: RunStatus;
1526
+ readonly result?: string;
1527
+ readonly model?: ModelSelection;
1528
+ readonly durationMs?: number;
1529
+ readonly git?: RunGitInfo;
1530
+ readonly createdAt?: number;
1531
+ /** AsyncGenerator of normalized stream events. Discriminate on `event.type`. */
1532
+ stream(): AsyncGenerator<SDKMessage, void>;
1533
+ /** Resolves to the terminal {@link RunResult}. */
1534
+ wait(): Promise<RunResult>;
1535
+ /** Move status to `"cancelled"`, abort the stream, stop in-flight tool calls. */
1536
+ cancel(): Promise<void>;
1537
+ /** Structured per-turn view of the conversation. */
1538
+ conversation(): Promise<ConversationTurn[]>;
1539
+ /** Whether the given operation is available on this run's runtime. */
1540
+ supports(operation: RunOperation): boolean;
1541
+ /** Human-readable reason that `supports(operation)` returned `false`. */
1542
+ unsupportedReason(operation: RunOperation): string | undefined;
1543
+ /** Subscribe to status changes. Returns an unsubscribe function. */
1544
+ onDidChangeStatus(listener: (status: RunStatus) => void): () => void;
1545
+ }
1546
+ /**
1547
+ * SE9 — options for the integrated structured-output method `agent.generate`: the
1548
+ * {@link SendOptions} that drive the tool loop (phase 1) plus the required `output`
1549
+ * Zod schema and structuring knobs (phase 2). Co-located with `SendOptions` /
1550
+ * `RunResult` (which they extend/use) so the public `SDKAgent` interface does not
1551
+ * import the runtime `agent-generate` module (breaks the type cycle).
1552
+ *
1553
+ * @public
1554
+ */
1555
+ interface GenerateOptions<T extends zod.ZodType> extends SendOptions {
1556
+ /** Zod schema the final answer is coerced into (the structuring contract). */
1557
+ output: T;
1558
+ /** Retry budget on the structuring phase's parse-failures (reused from generateObject). Default 1. */
1559
+ maxRetries?: number;
1560
+ /**
1561
+ * What the STRUCTURING phase (phase 2) does when the model's output still fails
1562
+ * Zod validation after retries. Default `"throw"`. `"return-partial"` / `"return-raw"`
1563
+ * salvage the object; the salvaged/raw value is in {@link GenerateRunResult.raw}
1564
+ * (the pre-parse structuring input) — NOT the phase-1 text answer, which is in
1565
+ * `result.result`.
1566
+ */
1567
+ errorStrategy?: "throw" | "return-partial" | "return-raw";
1568
+ }
1569
+ /**
1570
+ * SE9 — result of `agent.generate`: the validated typed object plus the underlying
1571
+ * tool-loop {@link RunResult} (status / usage / model) and the raw pre-parse input.
1572
+ *
1573
+ * @public
1574
+ */
1575
+ interface GenerateRunResult<O> {
1576
+ /** The validated object — inferred type from the `output` schema. */
1577
+ object: O;
1578
+ /** The underlying tool-loop run (phase 1). */
1579
+ result: RunResult;
1580
+ /** Raw model input to the synthetic `output` tool, before the Zod parse. */
1581
+ raw: unknown;
1582
+ /** Combined token usage of the structuring phase. */
1583
+ usage: {
1584
+ inputTokens: number;
1585
+ outputTokens: number;
1586
+ };
1587
+ }
1588
+
1589
+ export { type RunToCompletionResult as $, type AgentConversationTurn as A, type ProcessorControls as B, type CustomTool as C, type DoomLoopThresholds as D, type ProcessorTripwire as E, type ProcessorViolation as F, type GenerateOptions as G, type RunCompactBoundaryEvent as H, type ImageBlock as I, type RunCompletionCheckEvent as J, type RunErrorDetail as K, type RunEvent as L, type ModelSelection as M, type RunGitInfo as N, type OutputProcessorContext as O, type Processor as P, type RunOperation as Q, type RunResult as R, type SDKMessage as S, type ToolResultContentBlock as T, type RunPermissionDeniedEvent as U, type RunRateLimitEvent as V, type RunStatus as W, type RunTaskCompletedEvent as X, type RunTaskStartedEvent as Y, type RunTaskUpdatedEvent as Z, type RunToCompletionOptions as _, type McpServerConfig as a, type RunToolProgressEvent as a0, type RunTripwireEvent as a1, type SDKAssistantMessage as a2, type SDKImage as a3, type SDKImageDimension as a4, type SDKObjectDelta as a5, type SDKRequestMessage as a6, type SDKStatusMessage as a7, type SDKSystemMessage as a8, type SDKTaskMessage as a9, type ToolResult as aA, type ToolResultGuardOptions as aB, type ToolUseBlock as aC, type TurnEndedUpdate as aD, type UserMessage as aE, type UserMessageAppendedUpdate as aF, applyMode as aG, emitRunEvent as aH, type SDKThinkingMessage as aa, type SDKToolUseMessage as ab, type SDKUserMessage as ac, type SDKUserMessageEvent as ad, type SendOptions as ae, type ShellCommand as af, type ShellConversationTurn as ag, type ShellOutput as ah, type ShellOutputDeltaUpdate as ai, type StepCompletedUpdate as aj, type StepStartedUpdate as ak, type StreamToCompletionResult as al, type SummaryCompletedUpdate as am, type SummaryStartedUpdate as an, type SummaryUpdate as ao, type TextBlock as ap, type TextDeltaUpdate as aq, type ThinkingCompletedUpdate as ar, type ThinkingDeltaUpdate as as, type ThinkingMessage as at, type TokenDeltaUpdate as au, type TokenUsage as av, type ToolCall as aw, type ToolCallCompletedUpdate as ax, type ToolCallStartedUpdate as ay, type ToolContextMessage as az, type Run as b, type PermissionMode as c, PermissionEngine as d, type RunEventSink as e, type AssistantMessage as f, type CompletionCheck as g, type CompletionCheckResult as h, type ConversationStep as i, type ConversationTurn as j, type CostBreakdown as k, type CostSource as l, type CostStatus as m, type GenerateRunResult as n, type InputProcessorContext as o, type InteractionUpdate as p, type McpAuthConfig as q, type McpHttpServerConfig as r, type McpOAuthConfig as s, type McpStdioServerConfig as t, type MessageOrigin as u, type ModelParameterValue as v, type PartialToolCallUpdate as w, type PermissionAction as x, type PermissionEngineOptions as y, type PermissionRule as z };