@wrongstack/core 0.1.1 → 0.1.3

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,813 @@
1
+ interface TextBlock {
2
+ type: 'text';
3
+ text: string;
4
+ cache_control?: {
5
+ type: 'ephemeral';
6
+ };
7
+ }
8
+ interface ToolUseBlock {
9
+ type: 'tool_use';
10
+ id: string;
11
+ name: string;
12
+ input: Record<string, unknown>;
13
+ /**
14
+ * Provider-specific opaque metadata captured from the wire response.
15
+ * Echoed back verbatim in the next request so providers that bind
16
+ * extra state to function calls keep working. Example: Gemini's
17
+ * `thoughtSignature` — required for tool-use turns with thinking
18
+ * models, otherwise the next request fails with 400 "Function call
19
+ * is missing a thought_signature in functionCall parts".
20
+ *
21
+ * Keys are namespaced by intent so multiple wires can coexist:
22
+ * - `google.thoughtSignature` — Gemini signed-thought blob
23
+ * Other providers can add their own keys without colliding.
24
+ */
25
+ providerMeta?: Record<string, unknown>;
26
+ }
27
+ interface ToolResultBlock {
28
+ type: 'tool_result';
29
+ tool_use_id: string;
30
+ /**
31
+ * The original tool name. Useful for providers like Google Gemini that
32
+ * need the tool name in `functionResponse.name` — the tool_use_id is
33
+ * only a session-local identifier and is not stable across replays.
34
+ * Always set by ToolExecutor; may be absent on manually-constructed blocks.
35
+ */
36
+ name?: string;
37
+ content: string;
38
+ is_error?: boolean;
39
+ }
40
+ interface ImageBlock {
41
+ type: 'image';
42
+ source: {
43
+ type: 'base64' | 'url';
44
+ media_type?: string;
45
+ data?: string;
46
+ url?: string;
47
+ };
48
+ }
49
+ type ContentBlock = TextBlock | ToolUseBlock | ToolResultBlock | ImageBlock;
50
+ declare function isTextBlock(b: ContentBlock): b is TextBlock;
51
+ declare function isToolUseBlock(b: ContentBlock): b is ToolUseBlock;
52
+ declare function isToolResultBlock(b: ContentBlock): b is ToolResultBlock;
53
+ declare function isImageBlock(b: ContentBlock): b is ImageBlock;
54
+
55
+ type MessageRole = 'user' | 'assistant' | 'system';
56
+ interface Message {
57
+ role: MessageRole;
58
+ content: string | ContentBlock[];
59
+ }
60
+ declare function asBlocks(content: string | ContentBlock[]): ContentBlock[];
61
+ declare function asText(content: string | ContentBlock[]): string;
62
+
63
+ interface SessionMetadata {
64
+ id: string;
65
+ title?: string;
66
+ model?: string;
67
+ provider?: string;
68
+ startedAt: string;
69
+ endedAt?: string;
70
+ }
71
+ type SessionEvent = {
72
+ type: 'session_start';
73
+ ts: string;
74
+ id: string;
75
+ model: string;
76
+ provider: string;
77
+ } | {
78
+ type: 'session_resumed';
79
+ ts: string;
80
+ id: string;
81
+ model: string;
82
+ provider: string;
83
+ } | {
84
+ type: 'user_input';
85
+ ts: string;
86
+ content: string | ContentBlock[];
87
+ } | {
88
+ type: 'llm_request';
89
+ ts: string;
90
+ model: string;
91
+ messageCount: number;
92
+ } | {
93
+ type: 'llm_response';
94
+ ts: string;
95
+ content: ContentBlock[];
96
+ stopReason: string;
97
+ usage: Usage;
98
+ } | {
99
+ type: 'tool_use';
100
+ ts: string;
101
+ name: string;
102
+ id: string;
103
+ input: unknown;
104
+ } | {
105
+ type: 'tool_result';
106
+ ts: string;
107
+ id: string;
108
+ content: unknown;
109
+ isError: boolean;
110
+ } | {
111
+ type: 'compaction';
112
+ ts: string;
113
+ before: number;
114
+ after: number;
115
+ } | {
116
+ type: 'error';
117
+ ts: string;
118
+ message: string;
119
+ phase: string;
120
+ } | {
121
+ type: 'session_end';
122
+ ts: string;
123
+ usage: Usage;
124
+ } | {
125
+ type: 'mode_changed';
126
+ ts: string;
127
+ from: string;
128
+ to: string;
129
+ } | {
130
+ type: 'task_created';
131
+ ts: string;
132
+ taskId: string;
133
+ title: string;
134
+ } | {
135
+ type: 'task_updated';
136
+ ts: string;
137
+ taskId: string;
138
+ status: string;
139
+ } | {
140
+ type: 'task_completed';
141
+ ts: string;
142
+ taskId: string;
143
+ title: string;
144
+ } | {
145
+ type: 'task_failed';
146
+ ts: string;
147
+ taskId: string;
148
+ title: string;
149
+ error: string;
150
+ } | {
151
+ type: 'agent_spawned';
152
+ ts: string;
153
+ agentId: string;
154
+ role: string;
155
+ } | {
156
+ type: 'agent_stopped';
157
+ ts: string;
158
+ agentId: string;
159
+ } | {
160
+ type: 'agent_error';
161
+ ts: string;
162
+ agentId: string;
163
+ error: string;
164
+ } | {
165
+ type: 'spec_parsed';
166
+ ts: string;
167
+ specId: string;
168
+ title: string;
169
+ completeness: number;
170
+ } | {
171
+ type: 'spec_analyzed';
172
+ ts: string;
173
+ specId: string;
174
+ gaps: string[];
175
+ } | {
176
+ type: 'skill_activated';
177
+ ts: string;
178
+ skillName: string;
179
+ } | {
180
+ type: 'skill_deactivated';
181
+ ts: string;
182
+ skillName: string;
183
+ } | {
184
+ type: 'tool_call_start';
185
+ ts: string;
186
+ name: string;
187
+ id: string;
188
+ input: unknown;
189
+ } | {
190
+ type: 'tool_call_end';
191
+ ts: string;
192
+ name: string;
193
+ id: string;
194
+ durationMs: number;
195
+ outputSize: number;
196
+ } | {
197
+ type: 'message_truncated';
198
+ ts: string;
199
+ before: number;
200
+ after: number;
201
+ };
202
+ interface SessionSummary {
203
+ id: string;
204
+ title: string;
205
+ startedAt: string;
206
+ model: string;
207
+ provider: string;
208
+ tokenTotal: number;
209
+ }
210
+ interface SessionData {
211
+ metadata: SessionMetadata;
212
+ events: SessionEvent[];
213
+ messages: Message[];
214
+ usage: Usage;
215
+ }
216
+ interface ResumedSession {
217
+ writer: SessionWriter;
218
+ data: SessionData;
219
+ }
220
+ interface SessionStore {
221
+ create(meta: Omit<SessionMetadata, 'startedAt'>): Promise<SessionWriter>;
222
+ load(id: string): Promise<SessionData>;
223
+ /**
224
+ * Open an existing session for append, returning both a writer that
225
+ * continues writing to the same JSONL file and the replayed state
226
+ * (messages + usage) so the caller can hydrate a Context. A
227
+ * `session_resumed` marker is appended for audit.
228
+ */
229
+ resume(id: string): Promise<ResumedSession>;
230
+ list(limit?: number): Promise<SessionSummary[]>;
231
+ delete(id: string): Promise<void>;
232
+ }
233
+ interface SessionWriter {
234
+ readonly id: string;
235
+ append(event: SessionEvent): Promise<void>;
236
+ close(): Promise<void>;
237
+ }
238
+
239
+ interface CacheStats {
240
+ /** Tokens served from cache (cheaper). */
241
+ readTokens: number;
242
+ /** Tokens written into the cache (more expensive than input on first hit). */
243
+ writeTokens: number;
244
+ /** Hit ratio: cacheRead / (cacheRead + input). 0 when nothing cached. */
245
+ hitRatio: number;
246
+ }
247
+ interface TokenCounter {
248
+ account(usage: Usage, model?: string): void;
249
+ total(): Usage;
250
+ estimateCost(): {
251
+ input: number;
252
+ output: number;
253
+ total: number;
254
+ currency: 'USD';
255
+ };
256
+ cacheStats(): CacheStats;
257
+ reset(): void;
258
+ }
259
+
260
+ /**
261
+ * Immutable run environment — the set-once dependencies for an agent run.
262
+ *
263
+ * `Context` today doubles as both a DI bag (provider, session, tokenCounter,
264
+ * cwd, …) and a mutable state container (messages, todos, meta). That makes
265
+ * it hard to test (every test reconstructs the full bag) and easy to abuse
266
+ * (any tool can swap the provider mid-run).
267
+ *
268
+ * `RunEnv` is the immutable half: a read-only projection that subsystems
269
+ * can hold instead of the whole `Context`. It's a view, not a copy — pulling
270
+ * a `RunEnv` from a `Context` is O(1) and reflects the same underlying
271
+ * references. The opposite direction (set things on Context) still works,
272
+ * and `extractRunEnv` rebuilds the view if you need a snapshot.
273
+ *
274
+ * Migration path: new APIs accept `RunEnv` instead of `Context` when they
275
+ * only need read access. Existing APIs continue to accept `Context` until
276
+ * a full split is scheduled.
277
+ */
278
+ interface RunEnv {
279
+ readonly provider: Provider;
280
+ readonly session: SessionWriter;
281
+ readonly signal: AbortSignal;
282
+ readonly tokenCounter: TokenCounter;
283
+ readonly cwd: string;
284
+ readonly projectRoot: string;
285
+ readonly model: string;
286
+ readonly systemPrompt: readonly TextBlock[];
287
+ readonly tools: readonly Tool[];
288
+ }
289
+ /**
290
+ * Build a `RunEnv` view from a Context. The returned object is a shallow
291
+ * frozen view — mutations to `Context` are visible (it's the same
292
+ * references), but the view itself can't be mutated.
293
+ *
294
+ * Use this in subsystems that want to declare "I only need read access to
295
+ * the env" without rewriting their signature to accept the full Context.
296
+ */
297
+ declare function extractRunEnv(ctx: Context): RunEnv;
298
+
299
+ /**
300
+ * Observable wrapper for the mutable conversation state. Provides snapshot
301
+ * and change-notification semantics on top of the existing `Context`
302
+ * mutable fields (messages, todos, meta), without forcing every call site
303
+ * to migrate.
304
+ *
305
+ * Design notes:
306
+ *
307
+ * - This is a **wrapper**, not a replacement. The underlying `Context`
308
+ * fields are still mutated directly by tools and middleware that don't
309
+ * know about ConversationState. The wrapper observes those mutations by
310
+ * snapshotting on each accessor call, comparing length/identity, and
311
+ * firing onChange. This is intentional during migration: it lets new
312
+ * code subscribe to changes without breaking the unaware-existing code.
313
+ *
314
+ * - For full decoupling (the dev-plan #1 target), every mutation must go
315
+ * through `ConversationState.appendMessage()` etc. instead of
316
+ * `ctx.messages.push(...)`. That's a follow-up refactor — the API shape
317
+ * here is designed to support it.
318
+ *
319
+ * - `meta` is a free-form bag; we shallow-watch its keys. Deep mutations
320
+ * inside `meta.foo` won't trigger onChange. Use immutable replacement
321
+ * (`setMeta('foo', newValue)`) if you need notification.
322
+ */
323
+ type StateChange = {
324
+ kind: 'message_appended';
325
+ message: Message;
326
+ } | {
327
+ kind: 'messages_replaced';
328
+ messages: readonly Message[];
329
+ } | {
330
+ kind: 'todos_replaced';
331
+ todos: readonly TodoItem[];
332
+ } | {
333
+ kind: 'meta_set';
334
+ key: string;
335
+ value: unknown;
336
+ } | {
337
+ kind: 'meta_deleted';
338
+ key: string;
339
+ };
340
+ type StateChangeHandler = (change: StateChange, state: ConversationState) => void;
341
+ interface ReadonlyConversationState {
342
+ readonly messages: readonly Message[];
343
+ readonly todos: readonly TodoItem[];
344
+ readonly meta: Readonly<Record<string, unknown>>;
345
+ }
346
+ declare class ConversationState {
347
+ private readonly ctx;
348
+ private readonly listeners;
349
+ constructor(ctx: Context);
350
+ get messages(): readonly Message[];
351
+ get todos(): readonly TodoItem[];
352
+ get meta(): Readonly<Record<string, unknown>>;
353
+ /**
354
+ * Cheap immutable snapshot. Useful for tests and for compaction passes
355
+ * that need a stable view across an async boundary.
356
+ */
357
+ snapshot(): ReadonlyConversationState;
358
+ appendMessage(message: Message): void;
359
+ replaceMessages(messages: Message[]): void;
360
+ replaceTodos(todos: TodoItem[]): void;
361
+ setMeta(key: string, value: unknown): void;
362
+ deleteMeta(key: string): void;
363
+ /**
364
+ * Subscribe to mutations that go through this wrapper. Note: mutations
365
+ * that bypass the wrapper (e.g. `ctx.messages.push(...)` directly) are
366
+ * NOT observed — by design during migration, since we don't want to
367
+ * monkey-patch arrays. Migrating call sites to use this API is the
368
+ * dev-plan #1 work.
369
+ */
370
+ onChange(listener: StateChangeHandler): () => void;
371
+ private emit;
372
+ }
373
+ /**
374
+ * Convenience constructor — creates a ConversationState bound to the
375
+ * given Context. The wrapper holds a reference, not a copy.
376
+ */
377
+ declare function wrapAsState(ctx: Context): ConversationState;
378
+
379
+ interface TodoItem {
380
+ id: string;
381
+ content: string;
382
+ status: 'pending' | 'in_progress' | 'completed';
383
+ activeForm?: string;
384
+ }
385
+ interface RunOptions {
386
+ signal?: AbortSignal;
387
+ model?: string;
388
+ executionStrategy?: 'parallel' | 'sequential' | 'smart';
389
+ maxIterations?: number;
390
+ }
391
+ interface ContextInit {
392
+ systemPrompt: TextBlock[];
393
+ provider: Provider;
394
+ session: SessionWriter;
395
+ signal: AbortSignal;
396
+ tokenCounter: TokenCounter;
397
+ cwd: string;
398
+ projectRoot: string;
399
+ model: string;
400
+ tools?: Tool[];
401
+ }
402
+ /**
403
+ * L1-A: `Context` is the live agent-run object. Its read-only environment
404
+ * shape is exposed by the `RunEnv` interface (every field below the
405
+ * conversation state) and its mutable shape by `ConversationState` (the
406
+ * `state` accessor). New code should declare the narrower type at its
407
+ * parameter — pass `ctx` for it. Existing tools that accept `Context`
408
+ * still work because `Context` structurally satisfies both.
409
+ */
410
+ declare class Context implements RunEnv {
411
+ messages: Message[];
412
+ todos: TodoItem[];
413
+ readFiles: Set<string>;
414
+ fileMtimes: Map<string, number>;
415
+ systemPrompt: TextBlock[];
416
+ provider: Provider;
417
+ session: SessionWriter;
418
+ signal: AbortSignal;
419
+ tokenCounter: TokenCounter;
420
+ cwd: string;
421
+ projectRoot: string;
422
+ model: string;
423
+ tools: Tool[];
424
+ meta: Record<string, unknown>;
425
+ constructor(init: ContextInit);
426
+ /**
427
+ * Observable wrapper over the mutable conversation state. Lazy so
428
+ * subsystems that don't subscribe pay nothing. Mutations made directly
429
+ * on `ctx.messages` / `ctx.todos` are still visible through this
430
+ * wrapper's read API (it holds a reference, not a copy) but only
431
+ * mutations that go through `state.appendMessage()` etc. fire
432
+ * `onChange`. New code should prefer the wrapper API.
433
+ */
434
+ private _state;
435
+ get state(): ConversationState;
436
+ /**
437
+ * Register a teardown hook tied to the current run's abort signal. The
438
+ * hook fires when the run aborts OR ends normally — Agent.run wires
439
+ * this through a RunController. When no run is active the hook fires
440
+ * immediately so callers don't leak resources.
441
+ *
442
+ * **Scope:** these hooks fire on the **whole agent run's** abort, not on
443
+ * an individual tool call. For per-tool teardown of resources owned by
444
+ * the tool author (child processes, handles), prefer `Tool.cleanup` —
445
+ * see its JSDoc for the full rule.
446
+ */
447
+ private abortHooks;
448
+ registerAbortHook(fn: () => void | Promise<void>): () => void;
449
+ drainAbortHooks(): Promise<void>;
450
+ recordRead(absPath: string, mtimeMs: number): void;
451
+ hasRead(absPath: string): boolean;
452
+ lastReadMtime(absPath: string): number | undefined;
453
+ usage(): Usage;
454
+ }
455
+
456
+ type Permission = 'auto' | 'confirm' | 'deny';
457
+ interface JSONSchema {
458
+ type?: string;
459
+ properties?: Record<string, JSONSchema>;
460
+ required?: string[];
461
+ items?: JSONSchema;
462
+ enum?: unknown[];
463
+ description?: string;
464
+ [k: string]: unknown;
465
+ }
466
+ /**
467
+ * Tool progress event — yielded by `Tool.executeStream` to give the UI
468
+ * something to render while a long-running tool works. The executor
469
+ * publishes each event via EventBus as `tool.progress` so the TUI, logger,
470
+ * and observability layer can consume them uniformly.
471
+ *
472
+ * Keep events small. They are buffered through the EventBus synchronously
473
+ * and rendered on the main thread.
474
+ */
475
+ interface ToolProgressEvent {
476
+ /**
477
+ * - `log` — verbose informational message (e.g. "scanning…")
478
+ * - `warning` — non-fatal issue (e.g. "skipped X due to ENOENT")
479
+ * - `metric` — numeric data (e.g. files scanned so far)
480
+ * - `file_changed` — a tool that mutates the workspace announces a write
481
+ * - `partial_output` — stream of textual output (bash stdout, fetch body)
482
+ */
483
+ type: 'log' | 'warning' | 'metric' | 'file_changed' | 'partial_output';
484
+ text?: string;
485
+ data?: Record<string, unknown>;
486
+ }
487
+ /**
488
+ * Terminal event for `executeStream`. The output must match the tool's
489
+ * declared output type — the executor unwraps `output` and treats it like
490
+ * a normal `execute` return value.
491
+ */
492
+ interface ToolFinalEvent<O> {
493
+ type: 'final';
494
+ output: O;
495
+ }
496
+ type ToolStreamEvent<O = unknown> = ToolProgressEvent | ToolFinalEvent<O>;
497
+ interface Tool<I = unknown, O = unknown> {
498
+ name: string;
499
+ description: string;
500
+ usageHint?: string;
501
+ inputSchema: JSONSchema;
502
+ permission: Permission;
503
+ mutating: boolean;
504
+ maxOutputBytes?: number;
505
+ timeoutMs?: number;
506
+ /**
507
+ * Hint for the TUI spinner — does NOT affect actual timeout enforcement.
508
+ * Use `timeoutMs` for hard limits. Leave undefined when duration varies
509
+ * unpredictably.
510
+ */
511
+ estimatedDurationMs?: number;
512
+ execute(input: I, ctx: Context, opts: {
513
+ signal: AbortSignal;
514
+ }): Promise<O>;
515
+ /**
516
+ * Optional streaming variant. When defined, the executor prefers this
517
+ * over `execute` — yielded events become `tool.progress` EventBus events
518
+ * and the terminal `final` event provides the output. Tools that don't
519
+ * have intermediate state shouldn't implement this; the default `execute`
520
+ * path is more efficient.
521
+ */
522
+ executeStream?(input: I, ctx: Context, opts: {
523
+ signal: AbortSignal;
524
+ }): AsyncIterable<ToolStreamEvent<O>>;
525
+ /**
526
+ * Optional teardown hook fired by the executor when the tool's run is
527
+ * aborted (signal triggered). Errors thrown here are swallowed so they
528
+ * never mask the originating failure.
529
+ *
530
+ * **When to use `cleanup` vs `ctx.registerAbortHook`:**
531
+ *
532
+ * - Use `cleanup` for resources **owned by the tool author** that are
533
+ * established at execute-time: child processes spawned by the tool,
534
+ * file handles opened by the tool, network connections initiated by
535
+ * the tool. The lifecycle is co-located with the tool definition, so
536
+ * readers see the resource and its teardown in one place.
537
+ *
538
+ * ```ts
539
+ * async execute(input, ctx, opts) {
540
+ * const child = spawn(...);
541
+ * // … tool work …
542
+ * },
543
+ * async cleanup(_input, _ctx) {
544
+ * // best-effort kill of any child still running
545
+ * }
546
+ * ```
547
+ *
548
+ * - Use `ctx.registerAbortHook` for **context-scoped teardown** registered
549
+ * dynamically inside `execute`: when the tool delegates to a library
550
+ * that needs cancellation, or when the resource is created lazily
551
+ * somewhere down the call stack and the natural cleanup point isn't
552
+ * at the tool boundary. The hook fires when the **agent run** ends,
553
+ * not when this specific tool call aborts.
554
+ *
555
+ * ```ts
556
+ * async execute(input, ctx, opts) {
557
+ * const handle = openHelper();
558
+ * ctx.registerAbortHook(() => handle.dispose());
559
+ * // … work …
560
+ * }
561
+ * ```
562
+ *
563
+ * If both are registered for the same resource, `cleanup` fires first
564
+ * (on tool abort) and the abort-hook fires after on the wider run abort.
565
+ * Avoid double-free by gating one on the other's effect, or pick a single
566
+ * teardown channel per resource.
567
+ */
568
+ cleanup?(input: I, ctx: Context): Promise<void>;
569
+ }
570
+ interface ToolCallContext {
571
+ tool: Tool;
572
+ input: unknown;
573
+ callId: string;
574
+ ctx: Context;
575
+ signal: AbortSignal;
576
+ }
577
+
578
+ /**
579
+ * WrongStack error hierarchy.
580
+ *
581
+ * Every error thrown by the framework is a `WrongStackError` with a
582
+ * machine-readable `code`, a `subsystem` tag, and a `severity` level.
583
+ * This lets consumers (CLI, TUI, plugins, tests) branch on structured
584
+ * data instead of parsing error messages.
585
+ */
586
+ type ErrorCode = 'PROVIDER_RATE_LIMITED' | 'PROVIDER_AUTH_FAILED' | 'PROVIDER_OVERLOADED' | 'PROVIDER_INVALID_REQUEST' | 'PROVIDER_SERVER_ERROR' | 'PROVIDER_NETWORK_ERROR' | 'PROVIDER_CONTEXT_OVERFLOW' | 'TOOL_NOT_FOUND' | 'TOOL_PERMISSION_DENIED' | 'TOOL_EXECUTION_FAILED' | 'TOOL_TIMEOUT' | 'TOOL_INPUT_INVALID' | 'CONFIG_INVALID' | 'CONFIG_NOT_FOUND' | 'CONFIG_PARSE_FAILED' | 'CONFIG_MIGRATION_NEEDED' | 'PLUGIN_LOAD_FAILED' | 'PLUGIN_API_MISMATCH' | 'PLUGIN_MISSING_DEPENDENCY' | 'AGENT_ITERATION_LIMIT' | 'AGENT_CONTEXT_OVERFLOW' | 'AGENT_ABORTED' | 'AGENT_RUN_FAILED' | 'SESSION_NOT_FOUND' | 'SESSION_CORRUPTED' | 'SESSION_WRITE_FAILED' | 'CONTAINER_TOKEN_ALREADY_BOUND' | 'CONTAINER_TOKEN_NOT_BOUND' | 'REGISTRY_DUPLICATE' | 'REGISTRY_NOT_FOUND' | 'UNKNOWN';
587
+ type ErrorSubsystem = 'provider' | 'tool' | 'config' | 'plugin' | 'agent' | 'session' | 'container' | 'general';
588
+ type ErrorSeverity = 'fatal' | 'error' | 'warning';
589
+ declare class WrongStackError extends Error {
590
+ readonly code: ErrorCode;
591
+ readonly subsystem: ErrorSubsystem;
592
+ readonly severity: ErrorSeverity;
593
+ readonly recoverable: boolean;
594
+ readonly context?: Record<string, unknown>;
595
+ constructor(opts: {
596
+ message: string;
597
+ code: ErrorCode;
598
+ subsystem: ErrorSubsystem;
599
+ severity?: ErrorSeverity;
600
+ recoverable?: boolean;
601
+ context?: Record<string, unknown>;
602
+ cause?: unknown;
603
+ });
604
+ /**
605
+ * Render a one-line user-facing description.
606
+ * Subclasses should override for domain-specific formatting.
607
+ */
608
+ describe(): string;
609
+ }
610
+ /**
611
+ * Tool execution errors — thrown by ToolExecutor and individual tools.
612
+ */
613
+ declare class ToolError extends WrongStackError {
614
+ readonly toolName: string;
615
+ constructor(opts: {
616
+ message: string;
617
+ code: Extract<ErrorCode, 'TOOL_NOT_FOUND' | 'TOOL_PERMISSION_DENIED' | 'TOOL_EXECUTION_FAILED' | 'TOOL_TIMEOUT' | 'TOOL_INPUT_INVALID'>;
618
+ toolName: string;
619
+ recoverable?: boolean;
620
+ context?: Record<string, unknown>;
621
+ cause?: unknown;
622
+ });
623
+ }
624
+ /**
625
+ * Config loading / validation errors.
626
+ */
627
+ declare class ConfigError extends WrongStackError {
628
+ constructor(opts: {
629
+ message: string;
630
+ code: Extract<ErrorCode, 'CONFIG_INVALID' | 'CONFIG_NOT_FOUND' | 'CONFIG_PARSE_FAILED' | 'CONFIG_MIGRATION_NEEDED'>;
631
+ context?: Record<string, unknown>;
632
+ cause?: unknown;
633
+ });
634
+ }
635
+ /**
636
+ * Plugin loading / lifecycle errors.
637
+ */
638
+ declare class PluginError extends WrongStackError {
639
+ readonly pluginName: string;
640
+ constructor(opts: {
641
+ message: string;
642
+ code: Extract<ErrorCode, 'PLUGIN_LOAD_FAILED' | 'PLUGIN_API_MISMATCH' | 'PLUGIN_MISSING_DEPENDENCY'>;
643
+ pluginName: string;
644
+ context?: Record<string, unknown>;
645
+ cause?: unknown;
646
+ });
647
+ }
648
+ /**
649
+ * Agent runtime errors — thrown by Agent.run when a non-WrongStackError
650
+ * escapes the inner loop, so callers always see a structured error.
651
+ */
652
+ declare class AgentError extends WrongStackError {
653
+ constructor(opts: {
654
+ message: string;
655
+ code: Extract<ErrorCode, 'AGENT_ITERATION_LIMIT' | 'AGENT_CONTEXT_OVERFLOW' | 'AGENT_ABORTED' | 'AGENT_RUN_FAILED'>;
656
+ recoverable?: boolean;
657
+ context?: Record<string, unknown>;
658
+ cause?: unknown;
659
+ });
660
+ }
661
+ /**
662
+ * Wrap an arbitrary thrown value into a `WrongStackError` so the caller
663
+ * always gets a structured error. Pass-throughs WrongStackError instances
664
+ * unchanged; raw `Error`s and primitives get an `AGENT_RUN_FAILED` wrapper
665
+ * with the original preserved as `cause`.
666
+ */
667
+ declare function toWrongStackError(err: unknown, code?: Extract<ErrorCode, 'AGENT_RUN_FAILED' | 'AGENT_ABORTED' | 'UNKNOWN'>): WrongStackError;
668
+ /**
669
+ * Session storage errors.
670
+ */
671
+ declare class SessionError extends WrongStackError {
672
+ readonly sessionId?: string;
673
+ constructor(opts: {
674
+ message: string;
675
+ code: Extract<ErrorCode, 'SESSION_NOT_FOUND' | 'SESSION_CORRUPTED' | 'SESSION_WRITE_FAILED'>;
676
+ sessionId?: string;
677
+ context?: Record<string, unknown>;
678
+ cause?: unknown;
679
+ });
680
+ }
681
+ declare function isWrongStackError(err: unknown): err is WrongStackError;
682
+ declare function isToolError(err: unknown): err is ToolError;
683
+ declare function isConfigError(err: unknown): err is ConfigError;
684
+ declare function isPluginError(err: unknown): err is PluginError;
685
+ declare function isSessionError(err: unknown): err is SessionError;
686
+ declare function isAgentError(err: unknown): err is AgentError;
687
+
688
+ interface Usage {
689
+ input: number;
690
+ output: number;
691
+ cacheRead?: number;
692
+ cacheWrite?: number;
693
+ }
694
+ interface Capabilities {
695
+ tools: boolean;
696
+ parallelTools: boolean;
697
+ vision: boolean;
698
+ streaming: boolean;
699
+ promptCache: boolean;
700
+ systemPrompt: boolean;
701
+ jsonMode: boolean;
702
+ maxContext: number;
703
+ cacheControl: 'native' | 'auto' | 'none';
704
+ }
705
+ interface Request {
706
+ model: string;
707
+ system?: TextBlock[];
708
+ messages: Message[];
709
+ tools?: Tool[];
710
+ maxTokens: number;
711
+ temperature?: number;
712
+ topP?: number;
713
+ stopSequences?: string[];
714
+ toolChoice?: 'auto' | 'required' | 'none' | {
715
+ type: 'tool';
716
+ name: string;
717
+ };
718
+ }
719
+ type StopReason = 'end_turn' | 'tool_use' | 'max_tokens' | 'stop_sequence' | 'refusal';
720
+ interface Response {
721
+ content: ContentBlock[];
722
+ stopReason: StopReason;
723
+ usage: Usage;
724
+ model: string;
725
+ }
726
+ type StreamEvent = {
727
+ type: 'message_start';
728
+ model: string;
729
+ } | {
730
+ type: 'content_block_start';
731
+ kind: 'text' | 'tool_use';
732
+ id?: string;
733
+ name?: string;
734
+ } | {
735
+ type: 'content_block_stop';
736
+ index: number;
737
+ } | {
738
+ type: 'text_delta';
739
+ text: string;
740
+ } | {
741
+ type: 'tool_use_start';
742
+ id: string;
743
+ name: string;
744
+ } | {
745
+ type: 'tool_use_input_delta';
746
+ id: string;
747
+ partial: string;
748
+ } | {
749
+ type: 'tool_use_stop';
750
+ id: string;
751
+ input: unknown;
752
+ providerMeta?: Record<string, unknown>;
753
+ } | {
754
+ type: 'message_stop';
755
+ stopReason: StopReason;
756
+ usage: Usage;
757
+ };
758
+ interface Provider {
759
+ readonly id: string;
760
+ readonly capabilities: Capabilities;
761
+ /** Canonical streaming entry point. `complete()` defaults to a wrapper that
762
+ * aggregates this stream — providers may override for non-streaming wires. */
763
+ stream(req: Request, opts: {
764
+ signal: AbortSignal;
765
+ }): AsyncIterable<StreamEvent>;
766
+ complete(req: Request, opts: {
767
+ signal: AbortSignal;
768
+ }): Promise<Response>;
769
+ }
770
+ /**
771
+ * Structured body parsed from a provider's HTTP error response. Populated
772
+ * best-effort: providers return JSON shaped differently (Anthropic uses
773
+ * `{error: {type, message}}`, OpenAI uses `{error: {message, code}}`,
774
+ * Google uses `{error: {status, message}}`), so the fields here are the
775
+ * intersection that's usable for rendering and routing.
776
+ */
777
+ interface ProviderErrorBody {
778
+ /** Provider-specific kind, e.g. "overloaded_error", "rate_limit_error", "invalid_request_error". */
779
+ type?: string;
780
+ /** Human-readable explanation from the provider. */
781
+ message?: string;
782
+ /** Provider request id, when present in the body or headers. */
783
+ requestId?: string;
784
+ /** Parsed Retry-After header (or equivalent body hint) in milliseconds. */
785
+ retryAfterMs?: number;
786
+ /** The raw response body (truncated), kept for debugging. */
787
+ raw?: string;
788
+ }
789
+ declare class ProviderError extends WrongStackError {
790
+ readonly status: number;
791
+ readonly retryable: boolean;
792
+ readonly providerId: string;
793
+ readonly body?: ProviderErrorBody;
794
+ constructor(message: string, status: number, retryable: boolean, providerId: string, opts?: {
795
+ body?: ProviderErrorBody;
796
+ cause?: unknown;
797
+ });
798
+ /**
799
+ * Render a one-line, user-facing description. Designed for the CLI/TUI
800
+ * status line and the agent's retry warning. Avoids dumping raw JSON
801
+ * (which is what users see today when a 529 lands and the log message
802
+ * includes the full `{"type":"error",...}` body).
803
+ *
804
+ * Examples:
805
+ * "minimax-coding-plan overloaded (529): High traffic detected. Upgrade for highspeed model. [req 06534785201de9c0…]"
806
+ * "openai rate limited (429): Retry after 12s"
807
+ * "anthropic invalid request (400): messages.0.role must be one of 'user'|'assistant'"
808
+ * "groq HTTP 500 (server error)"
809
+ */
810
+ describe(): string;
811
+ }
812
+
813
+ export { toWrongStackError as $, AgentError as A, type ToolResultBlock as B, type CacheStats as C, type ToolStreamEvent as D, type ErrorCode as E, type ToolUseBlock as F, asBlocks as G, asText as H, type ImageBlock as I, type JSONSchema as J, isAgentError as K, isConfigError as L, type Message as M, isImageBlock as N, isPluginError as O, type Permission as P, isSessionError as Q, type Request as R, type SessionData as S, type TextBlock as T, type Usage as U, isTextBlock as V, WrongStackError as W, isToolError as X, isToolResultBlock as Y, isToolUseBlock as Z, isWrongStackError as _, type Capabilities as a, Context as a0, type ContextInit as a1, ConversationState as a2, type ReadonlyConversationState as a3, type RunEnv as a4, type RunOptions as a5, type StateChange as a6, type StateChangeHandler as a7, type TodoItem as a8, extractRunEnv as a9, wrapAsState as aa, ConfigError as b, type ContentBlock as c, type ErrorSeverity as d, type ErrorSubsystem as e, type MessageRole as f, PluginError as g, type Provider as h, ProviderError as i, type ProviderErrorBody as j, type Response as k, type ResumedSession as l, SessionError as m, type SessionEvent as n, type SessionMetadata as o, type SessionStore as p, type SessionSummary as q, type SessionWriter as r, type StopReason as s, type StreamEvent as t, type TokenCounter as u, type Tool as v, type ToolCallContext as w, ToolError as x, type ToolFinalEvent as y, type ToolProgressEvent as z };