@cuylabs/agent-core 0.1.0

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,4414 @@
1
+ import * as ai from 'ai';
2
+ import { LanguageModel, Tool, StreamTextResult, ToolSet, Output, ModelMessage } from 'ai';
3
+ import { T as ToolHost, a as Tool$1, F as FileOperationMeta, b as TurnTrackerContext } from './index-eud06GDQ.js';
4
+ export { D as DirEntry, E as ExecOptions, c as ExecResult, d as FileStat, M as MAX_BYTES, e as MAX_LINES, f as TRUNCATE_DIR, g as TRUNCATE_GLOB, h as ToolContext, i as ToolMetadata, j as ToolRegistry, k as ToolResult, l as ToolSpec, m as TruncateResult, n as defaultRegistry, o as defineTool, p as formatSize, t as truncateOutput } from './index-eud06GDQ.js';
5
+ import { ProviderOptions } from '@ai-sdk/provider-utils';
6
+ import 'zod';
7
+
8
+ /**
9
+ * Storage Types
10
+ *
11
+ * Core types for session storage with tree structure support.
12
+ * Designed for append-only event sourcing with branching.
13
+ */
14
+ /**
15
+ * Base interface for all session entries
16
+ * Supports tree structure via id/parentId
17
+ */
18
+ interface EntryBase {
19
+ /** Unique entry ID (8-char hex) */
20
+ id: string;
21
+ /** Parent entry ID (null for first entry after header) */
22
+ parentId: string | null;
23
+ /** ISO timestamp */
24
+ timestamp: string;
25
+ }
26
+ /**
27
+ * Session header - always first entry in file
28
+ */
29
+ interface SessionHeader {
30
+ type: "header";
31
+ /** Storage format version */
32
+ version: number;
33
+ /** Session ID */
34
+ id: string;
35
+ /** Working directory */
36
+ cwd: string;
37
+ /** ISO timestamp */
38
+ timestamp: string;
39
+ /** Parent session ID (if forked) */
40
+ parentSessionId?: string;
41
+ /** Session title */
42
+ title?: string;
43
+ }
44
+ /**
45
+ * Serialized message (dates as ISO strings)
46
+ */
47
+ interface SerializedMessage {
48
+ id: string;
49
+ role: "user" | "assistant" | "tool" | "system";
50
+ content: string;
51
+ createdAt: string;
52
+ system?: string;
53
+ finish?: string;
54
+ tokens?: {
55
+ inputTokens?: number;
56
+ outputTokens?: number;
57
+ totalTokens?: number;
58
+ };
59
+ cost?: number;
60
+ error?: {
61
+ name: string;
62
+ message: string;
63
+ code?: string;
64
+ };
65
+ /** Tool calls for assistant messages with finish=tool-calls */
66
+ toolCalls?: Array<{
67
+ toolCallId: string;
68
+ toolName: string;
69
+ args: unknown;
70
+ }>;
71
+ toolCallId?: string;
72
+ toolName?: string;
73
+ result?: unknown;
74
+ }
75
+ /**
76
+ * Message entry
77
+ */
78
+ interface MessageEntry extends EntryBase {
79
+ type: "message";
80
+ message: SerializedMessage;
81
+ }
82
+ /**
83
+ * Compaction/summarization entry
84
+ * Replaces old messages with a summary
85
+ */
86
+ interface CompactionEntry extends EntryBase {
87
+ type: "compaction";
88
+ /** Summary of compacted messages */
89
+ summary: string;
90
+ /** ID of first entry that was kept (not compacted) */
91
+ firstKeptEntryId: string;
92
+ /** Token count before compaction */
93
+ tokensBefore: number;
94
+ /** Token count after compaction */
95
+ tokensAfter: number;
96
+ /** Files that were read during compacted messages */
97
+ readFiles?: string[];
98
+ /** Files that were modified during compacted messages */
99
+ modifiedFiles?: string[];
100
+ }
101
+ /**
102
+ * Session info/metadata update
103
+ */
104
+ interface MetadataEntry extends EntryBase {
105
+ type: "metadata";
106
+ /** Updated title */
107
+ title?: string;
108
+ /** User-defined name */
109
+ name?: string;
110
+ }
111
+ /**
112
+ * Branch marker - indicates a branch point
113
+ */
114
+ interface BranchEntry extends EntryBase {
115
+ type: "branch";
116
+ /** ID of the entry we're branching from */
117
+ branchFromId: string;
118
+ /** Optional summary of the branch */
119
+ summary?: string;
120
+ }
121
+ /**
122
+ * Model/config change entry
123
+ */
124
+ interface ConfigChangeEntry extends EntryBase {
125
+ type: "config_change";
126
+ /** What changed */
127
+ change: "model" | "reasoning_level" | "temperature" | "other";
128
+ /** Previous value */
129
+ from?: string;
130
+ /** New value */
131
+ to: string;
132
+ }
133
+ /**
134
+ * All entry types (excluding header)
135
+ */
136
+ type SessionEntry = MessageEntry | CompactionEntry | MetadataEntry | BranchEntry | ConfigChangeEntry;
137
+ /**
138
+ * All file entries (including header)
139
+ */
140
+ type FileEntry = SessionHeader | SessionEntry;
141
+ /**
142
+ * Session summary info (lightweight, for listing)
143
+ */
144
+ interface SessionInfo {
145
+ /** Session ID */
146
+ id: string;
147
+ /** File path */
148
+ path: string;
149
+ /** Working directory */
150
+ cwd: string;
151
+ /** Session title */
152
+ title?: string;
153
+ /** User-defined name */
154
+ name?: string;
155
+ /** Parent session ID (if forked) */
156
+ parentSessionId?: string;
157
+ /** Creation time */
158
+ createdAt: Date;
159
+ /** Last modified time */
160
+ updatedAt: Date;
161
+ /** Number of message entries */
162
+ messageCount: number;
163
+ /** First user message (preview) */
164
+ firstMessage?: string;
165
+ }
166
+ /**
167
+ * Session storage interface
168
+ * Pluggable backend for session persistence
169
+ */
170
+ interface SessionStorage {
171
+ /**
172
+ * Create a new session
173
+ */
174
+ create(header: SessionHeader): Promise<void>;
175
+ /**
176
+ * Append an entry to a session
177
+ */
178
+ append(sessionId: string, entry: SessionEntry): Promise<void>;
179
+ /**
180
+ * Append multiple entries atomically
181
+ */
182
+ appendBatch(sessionId: string, entries: SessionEntry[]): Promise<void>;
183
+ /**
184
+ * Read all entries from a session
185
+ */
186
+ read(sessionId: string): Promise<FileEntry[]>;
187
+ /**
188
+ * Delete a session
189
+ */
190
+ delete(sessionId: string): Promise<boolean>;
191
+ /**
192
+ * Check if session exists
193
+ */
194
+ exists(sessionId: string): Promise<boolean>;
195
+ /**
196
+ * List all sessions (lightweight info only)
197
+ */
198
+ list(): Promise<SessionInfo[]>;
199
+ /**
200
+ * Clear all sessions
201
+ */
202
+ clear(): Promise<void>;
203
+ }
204
+ /**
205
+ * Current storage format version
206
+ */
207
+ declare const STORAGE_VERSION = 1;
208
+
209
+ /**
210
+ * Message and token types for @cuylabs/agent-core
211
+ *
212
+ * Defines the message model used across conversations,
213
+ * including all role variants and token usage tracking.
214
+ */
215
+ /**
216
+ * Message roles
217
+ */
218
+ type MessageRole = "user" | "assistant" | "tool" | "system";
219
+ /**
220
+ * Base message interface
221
+ */
222
+ interface MessageBase {
223
+ id: string;
224
+ role: MessageRole;
225
+ createdAt: Date;
226
+ }
227
+ /**
228
+ * User message
229
+ */
230
+ interface UserMessage extends MessageBase {
231
+ role: "user";
232
+ content: string;
233
+ /** Optional custom system prompt for this message */
234
+ system?: string;
235
+ }
236
+ /**
237
+ * Assistant message
238
+ */
239
+ interface AssistantMessage extends MessageBase {
240
+ role: "assistant";
241
+ content: string;
242
+ /** Finish reason */
243
+ finish?: "stop" | "length" | "tool-calls" | "content-filter" | "error" | "unknown";
244
+ /** Token usage for this response */
245
+ tokens?: TokenUsage;
246
+ /** Cost in USD */
247
+ cost?: number;
248
+ /** Error if any */
249
+ error?: MessageError;
250
+ /** Tool calls made by the assistant (when finish === "tool-calls") */
251
+ toolCalls?: Array<{
252
+ toolCallId: string;
253
+ toolName: string;
254
+ args: unknown;
255
+ }>;
256
+ }
257
+ /**
258
+ * Tool result message
259
+ */
260
+ interface ToolMessage extends MessageBase {
261
+ role: "tool";
262
+ content: string;
263
+ toolCallId: string;
264
+ toolName: string;
265
+ result: unknown;
266
+ /**
267
+ * Timestamp when this tool result was compacted/pruned.
268
+ * Matches OpenCode's part.state.time.compacted pattern.
269
+ * Once set, this message won't be pruned again.
270
+ */
271
+ compactedAt?: number;
272
+ }
273
+ /**
274
+ * System message
275
+ */
276
+ interface SystemMessage extends MessageBase {
277
+ role: "system";
278
+ content: string;
279
+ }
280
+ /**
281
+ * Union of all message types
282
+ */
283
+ type Message = UserMessage | AssistantMessage | ToolMessage | SystemMessage;
284
+ /**
285
+ * Error in a message
286
+ */
287
+ interface MessageError {
288
+ name: string;
289
+ message: string;
290
+ code?: string;
291
+ status?: number;
292
+ }
293
+ /**
294
+ * Token usage statistics
295
+ */
296
+ interface TokenUsage {
297
+ inputTokens?: number;
298
+ outputTokens?: number;
299
+ totalTokens?: number;
300
+ /** Cache read tokens (if supported) */
301
+ cacheReadTokens?: number;
302
+ /** Cache write tokens (if supported) */
303
+ cacheWriteTokens?: number;
304
+ }
305
+ /**
306
+ * Session state
307
+ */
308
+ interface Session {
309
+ id: string;
310
+ messages: Message[];
311
+ createdAt: Date;
312
+ updatedAt: Date;
313
+ /** Optional title */
314
+ title?: string;
315
+ /** Parent session ID for forking */
316
+ parentID?: string;
317
+ }
318
+
319
+ /**
320
+ * Stream types for @cuylabs/agent-core
321
+ *
322
+ * Defines the canonical StreamChunk union and related types
323
+ * for both AI SDK native and custom stream providers.
324
+ */
325
+ /**
326
+ * Stream chunk types (AI SDK compatible + custom streams)
327
+ *
328
+ * This is the single canonical definition — used by both the
329
+ * streaming module and custom stream providers.
330
+ */
331
+ type StreamChunk = {
332
+ type: "text-start";
333
+ } | {
334
+ type: "text-delta";
335
+ text: string;
336
+ } | {
337
+ type: "text-end";
338
+ } | {
339
+ type: "reasoning-start";
340
+ id: string;
341
+ } | {
342
+ type: "reasoning-delta";
343
+ id: string;
344
+ text: string;
345
+ } | {
346
+ type: "reasoning-end";
347
+ id: string;
348
+ } | {
349
+ type: "tool-call";
350
+ toolName: string;
351
+ toolCallId: string;
352
+ input: unknown;
353
+ } | {
354
+ type: "tool-result";
355
+ toolName: string;
356
+ toolCallId: string;
357
+ output: unknown;
358
+ } | {
359
+ type: "tool-error";
360
+ toolName: string;
361
+ toolCallId: string;
362
+ error: unknown;
363
+ } | {
364
+ type: "finish-step";
365
+ usage?: {
366
+ inputTokens?: number;
367
+ outputTokens?: number;
368
+ totalTokens?: number;
369
+ };
370
+ finishReason?: string;
371
+ } | {
372
+ type: "finish";
373
+ totalUsage?: {
374
+ inputTokens?: number;
375
+ outputTokens?: number;
376
+ totalTokens?: number;
377
+ };
378
+ } | {
379
+ type: "error";
380
+ error: unknown;
381
+ } | {
382
+ type: "start-step";
383
+ } | {
384
+ type: "start";
385
+ } | {
386
+ type: "abort";
387
+ } | {
388
+ type: "computer-call";
389
+ callId: string;
390
+ action: unknown;
391
+ pendingSafetyChecks?: unknown[];
392
+ } | {
393
+ type: "step-usage";
394
+ usage: {
395
+ inputTokens: number;
396
+ outputTokens: number;
397
+ totalTokens: number;
398
+ };
399
+ };
400
+ /**
401
+ * Custom stream provider function type.
402
+ *
403
+ * This matches the signature needed for agent-core's LLM module,
404
+ * returning a StreamProviderResult-compatible object.
405
+ */
406
+ type StreamProvider = (input: StreamProviderInput) => Promise<StreamProviderResult>;
407
+ /**
408
+ * Input for custom stream providers
409
+ */
410
+ interface StreamProviderInput {
411
+ /** System prompt */
412
+ system: string;
413
+ /** Messages to send */
414
+ messages: Array<{
415
+ role: string;
416
+ content: unknown;
417
+ }>;
418
+ /** Abort signal */
419
+ abortSignal?: AbortSignal;
420
+ /** Max iterations */
421
+ maxSteps?: number;
422
+ }
423
+ /**
424
+ * Result from custom stream providers (AI SDK StreamTextResult compatible)
425
+ */
426
+ interface StreamProviderResult {
427
+ /** Async iterable of stream chunks */
428
+ fullStream: AsyncIterable<StreamChunk>;
429
+ /** Promise resolving to final text */
430
+ text: Promise<string>;
431
+ /** Promise resolving to usage stats */
432
+ usage: Promise<{
433
+ inputTokens: number;
434
+ outputTokens: number;
435
+ totalTokens: number;
436
+ }>;
437
+ /** Promise resolving to finish reason */
438
+ finishReason: Promise<string>;
439
+ }
440
+ /**
441
+ * Configuration for stream provider factory.
442
+ * Contains everything needed to create a stream provider for a specific model.
443
+ */
444
+ interface StreamProviderConfig {
445
+ /** API key to use */
446
+ apiKey?: string;
447
+ /** Display dimensions */
448
+ display?: {
449
+ width: number;
450
+ height: number;
451
+ };
452
+ /** Environment type */
453
+ environment?: string;
454
+ /** Enable debug logging */
455
+ debug?: boolean;
456
+ }
457
+ /**
458
+ * Stream provider factory - creates a stream provider for a given model.
459
+ *
460
+ * This is attached to enhanced tools (like computer tools) to allow
461
+ * the Agent to automatically configure the right stream provider
462
+ * when a model requires custom handling (e.g., OpenAI computer-use-preview).
463
+ */
464
+ type StreamProviderFactory = (modelId: string, config: StreamProviderConfig) => StreamProvider;
465
+ /**
466
+ * Enhanced tools array with additional capabilities.
467
+ *
468
+ * This extends the standard Tool.AnyInfo[] with optional metadata
469
+ * that the Agent can use for automatic configuration.
470
+ */
471
+ interface EnhancedTools extends Array<unknown> {
472
+ /**
473
+ * Factory to create a stream provider for models that need custom streaming.
474
+ * Called by the Agent when it detects a model that requires special handling.
475
+ */
476
+ __streamProviderFactory?: StreamProviderFactory;
477
+ /**
478
+ * Model patterns that require the custom stream provider.
479
+ * Used by the Agent to detect when to use the factory.
480
+ * Default patterns: ["computer-use-preview"]
481
+ */
482
+ __customStreamModels?: string[];
483
+ }
484
+
485
+ /** Agent status for UI display */
486
+ type AgentStatus = "idle" | "processing" | "thinking" | "reasoning" | "calling-tool" | "waiting-approval" | "error";
487
+ /** Approval request for UI */
488
+ interface ApprovalEvent {
489
+ id: string;
490
+ tool: string;
491
+ args: unknown;
492
+ description: string;
493
+ risk: "safe" | "moderate" | "dangerous";
494
+ }
495
+ /**
496
+ * Events emitted during agent execution
497
+ *
498
+ * These events are designed for UI consumption:
499
+ * - status: Overall agent state for status indicators
500
+ * - approval-request: User confirmation needed
501
+ * - progress: Step counts for progress bars
502
+ */
503
+ type AgentEvent = {
504
+ type: "status";
505
+ status: AgentStatus;
506
+ } | {
507
+ type: "approval-request";
508
+ request: ApprovalEvent;
509
+ } | {
510
+ type: "approval-resolved";
511
+ id: string;
512
+ action: "allow" | "deny" | "remember";
513
+ } | {
514
+ type: "step-start";
515
+ step: number;
516
+ maxSteps: number;
517
+ } | {
518
+ type: "step-finish";
519
+ step: number;
520
+ usage?: TokenUsage;
521
+ finishReason?: string;
522
+ } | {
523
+ type: "message";
524
+ message: Message;
525
+ } | {
526
+ type: "text-start";
527
+ } | {
528
+ type: "text-delta";
529
+ text: string;
530
+ } | {
531
+ type: "text-end";
532
+ } | {
533
+ type: "reasoning-start";
534
+ id: string;
535
+ } | {
536
+ type: "reasoning-delta";
537
+ id: string;
538
+ text: string;
539
+ } | {
540
+ type: "reasoning-end";
541
+ id: string;
542
+ } | {
543
+ type: "tool-start";
544
+ toolName: string;
545
+ toolCallId: string;
546
+ input: unknown;
547
+ } | {
548
+ type: "tool-result";
549
+ toolName: string;
550
+ toolCallId: string;
551
+ result: unknown;
552
+ } | {
553
+ type: "tool-error";
554
+ toolName: string;
555
+ toolCallId: string;
556
+ error: string;
557
+ } | {
558
+ type: "computer-call";
559
+ callId: string;
560
+ action: unknown;
561
+ pendingSafetyChecks?: unknown[];
562
+ } | {
563
+ type: "computer-result";
564
+ callId: string;
565
+ result: unknown;
566
+ } | {
567
+ type: "intervention-applied";
568
+ id: string;
569
+ message: string;
570
+ } | {
571
+ type: "doom-loop";
572
+ toolName: string;
573
+ repeatCount: number;
574
+ } | {
575
+ type: "context-overflow";
576
+ inputTokens: number;
577
+ limit: number;
578
+ } | {
579
+ type: "turn-summary";
580
+ turnId: string;
581
+ files: Array<{
582
+ path: string;
583
+ type: "created" | "modified" | "deleted" | "unchanged";
584
+ additions: number;
585
+ deletions: number;
586
+ }>;
587
+ additions: number;
588
+ deletions: number;
589
+ } | {
590
+ type: "retry";
591
+ attempt: number;
592
+ delayMs: number;
593
+ error: Error;
594
+ } | {
595
+ type: "error";
596
+ error: Error;
597
+ } | {
598
+ type: "complete";
599
+ usage?: TokenUsage;
600
+ };
601
+ /**
602
+ * Processor result - what happens after processing a turn
603
+ */
604
+ type ProcessorResult = "continue" | "stop" | "compact";
605
+ /**
606
+ * Stream input for the LLM
607
+ */
608
+ interface StreamInput {
609
+ sessionID: string;
610
+ model: ai.LanguageModel;
611
+ system: string[];
612
+ messages: ai.ModelMessage[];
613
+ abort: AbortSignal;
614
+ tools: Record<string, unknown>;
615
+ }
616
+
617
+ /**
618
+ * Reasoning Types & Constants
619
+ *
620
+ * Shared type definitions, level constants, and small helpers
621
+ * used across the reasoning subsystem.
622
+ */
623
+ /**
624
+ * Standard reasoning / thinking levels.
625
+ *
626
+ * | Level | Description |
627
+ * |------------|------------------------------------|
628
+ * | `"off"` | No reasoning (fastest) |
629
+ * | `"minimal"`| Very light reasoning |
630
+ * | `"low"` | Light reasoning |
631
+ * | `"medium"` | Balanced reasoning |
632
+ * | `"high"` | Deep reasoning |
633
+ * | `"xhigh"` | Maximum reasoning (where supported)|
634
+ */
635
+ type ReasoningLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
636
+ /**
637
+ * Provider-specific reasoning configuration returned by
638
+ * {@link getReasoningConfig} / {@link getReasoningConfigSync}.
639
+ */
640
+ interface ReasoningConfig {
641
+ /** Whether the model supports reasoning at all */
642
+ supportsReasoning: boolean;
643
+ /** Reasoning levels available for this model */
644
+ availableLevels: ReasoningLevel[];
645
+ /** Build provider options for a given level */
646
+ getProviderOptions: (level: ReasoningLevel) => Record<string, unknown> | undefined;
647
+ }
648
+
649
+ /**
650
+ * Model Capability Types for @cuylabs/agent-core
651
+ *
652
+ * Defines the structure for model capabilities that can be sourced from
653
+ * static patterns, local cache, or remote APIs.
654
+ */
655
+ /**
656
+ * Input modalities a model can accept
657
+ */
658
+ type InputModality = "text" | "image" | "audio" | "video" | "pdf";
659
+ /**
660
+ * Output modalities a model can produce
661
+ */
662
+ type OutputModality = "text" | "image" | "audio" | "video";
663
+ /**
664
+ * Comprehensive model capabilities
665
+ */
666
+ interface ModelCapabilities {
667
+ /** Model supports extended reasoning/thinking */
668
+ reasoning: boolean;
669
+ /** Model supports function/tool calling */
670
+ toolCalling: boolean;
671
+ /** Model supports temperature adjustment */
672
+ temperature: boolean;
673
+ /** Model supports file attachments */
674
+ attachments: boolean;
675
+ /** Model supports streaming responses */
676
+ streaming: boolean;
677
+ /** Supported input modalities */
678
+ inputModalities: InputModality[];
679
+ /** Supported output modalities */
680
+ outputModalities: OutputModality[];
681
+ /** Maximum context window in tokens */
682
+ contextWindow?: number;
683
+ /** Maximum output tokens */
684
+ maxOutput?: number;
685
+ }
686
+ /**
687
+ * Provider-specific compatibility flags
688
+ * These handle quirks in different provider implementations
689
+ */
690
+ interface ProviderCompatibility {
691
+ /** Supports OpenAI-style reasoning_effort parameter */
692
+ supportsReasoningEffort?: boolean;
693
+ /** Supports developer/system role distinction */
694
+ supportsDeveloperRole?: boolean;
695
+ /** Field name for max tokens (varies by provider) */
696
+ maxTokensField?: "max_tokens" | "max_completion_tokens";
697
+ /** Requires thinking as text tags vs structured */
698
+ requiresThinkingTags?: boolean;
699
+ /** Provider-specific thinking format */
700
+ thinkingFormat?: "openai" | "anthropic" | "google" | "zai";
701
+ }
702
+ /**
703
+ * Complete model entry with metadata
704
+ */
705
+ interface ModelEntry {
706
+ /** Model identifier (e.g., "gpt-4o", "claude-sonnet-4") */
707
+ id: string;
708
+ /** Human-readable model name */
709
+ name: string;
710
+ /** Provider identifier (e.g., "openai", "anthropic") */
711
+ provider: string;
712
+ /** Model capabilities */
713
+ capabilities: ModelCapabilities;
714
+ /** Provider-specific compatibility settings */
715
+ compatibility?: ProviderCompatibility;
716
+ /** Cost per million tokens (input) */
717
+ costInput?: number;
718
+ /** Cost per million tokens (output) */
719
+ costOutput?: number;
720
+ /** When this entry was last updated */
721
+ updatedAt?: string;
722
+ }
723
+ /**
724
+ * Priority levels for capability sources
725
+ */
726
+ declare enum SourcePriority {
727
+ /** User configuration overrides everything */
728
+ UserConfig = 0,
729
+ /** Local cache from previous fetch */
730
+ LocalCache = 1,
731
+ /** Bundled static data (build-time) */
732
+ BundledData = 2,
733
+ /** Pattern-based inference (fallback) */
734
+ PatternMatch = 3,
735
+ /** Remote API (if network available) */
736
+ RemoteAPI = 4
737
+ }
738
+ /**
739
+ * Options for the capability resolver
740
+ */
741
+ interface ResolverOptions {
742
+ /** Enable remote API fetching (default: false) */
743
+ enableRemoteFetch?: boolean;
744
+ /** Remote API URL (default: https://models.dev) */
745
+ remoteApiUrl?: string;
746
+ /** Cache directory path */
747
+ cachePath?: string;
748
+ /** Cache TTL in milliseconds (default: 1 hour) */
749
+ cacheTtlMs?: number;
750
+ /** Network timeout in milliseconds (default: 10 seconds) */
751
+ networkTimeoutMs?: number;
752
+ /** Custom user overrides for specific models */
753
+ modelOverrides?: Record<string, Partial<ModelCapabilities>>;
754
+ }
755
+
756
+ /**
757
+ * Extract a model ID string from a LanguageModel instance.
758
+ */
759
+ declare function getModelId(model: LanguageModel): string;
760
+ /**
761
+ * Extract a provider identifier from a LanguageModel instance.
762
+ */
763
+ declare function getProviderId(model: LanguageModel): string | undefined;
764
+
765
+ /**
766
+ * Remote Capability Fetching for @cuylabs/agent-core
767
+ *
768
+ * Handles fetching model capabilities from remote APIs (e.g., models.dev).
769
+ * Includes network status detection, timeout handling, and retry logic.
770
+ */
771
+
772
+ /**
773
+ * Network status information
774
+ */
775
+ interface NetworkStatus {
776
+ /** Whether we believe the network is available */
777
+ online: boolean;
778
+ /** Last successful fetch timestamp */
779
+ lastSuccess?: number;
780
+ /** Last error if any */
781
+ lastError?: string;
782
+ /** Number of consecutive failures */
783
+ failureCount: number;
784
+ }
785
+ /**
786
+ * Get current network status
787
+ */
788
+ declare function getNetworkStatus(): NetworkStatus;
789
+
790
+ /**
791
+ * Model Capability Resolver for @cuylabs/agent-core
792
+ *
793
+ * Main orchestrator that combines multiple capability sources:
794
+ * 1. User overrides (highest priority)
795
+ * 2. Local cache (fast, persisted)
796
+ * 3. Pattern matching (always available)
797
+ * 4. Remote API (optional, network-dependent)
798
+ *
799
+ * Designed for the Vercel AI SDK v6 ecosystem.
800
+ */
801
+
802
+ /**
803
+ * Resolution result with source information
804
+ */
805
+ interface ResolutionResult {
806
+ /** The resolved model entry */
807
+ entry: ModelEntry;
808
+ /** Which source provided the primary result */
809
+ source: SourcePriority;
810
+ /** Whether this is a confident match */
811
+ confident: boolean;
812
+ /** Resolution timing in ms */
813
+ resolveTimeMs: number;
814
+ }
815
+ /**
816
+ * Model Capability Resolver
817
+ *
818
+ * Provides a unified API for querying model capabilities with
819
+ * automatic fallback through multiple sources.
820
+ */
821
+ declare class ModelCapabilityResolver {
822
+ private options;
823
+ private cache;
824
+ private sources;
825
+ private initialized;
826
+ private initPromise;
827
+ constructor(options?: Partial<ResolverOptions>);
828
+ /**
829
+ * Initialize the resolver (load cache, optionally fetch remote)
830
+ */
831
+ initialize(): Promise<void>;
832
+ /**
833
+ * Resolve capabilities for a model
834
+ */
835
+ resolve(model: LanguageModel): Promise<ResolutionResult>;
836
+ /**
837
+ * Quick check if a model supports reasoning
838
+ * Uses cache/patterns only for speed
839
+ */
840
+ supportsReasoning(model: LanguageModel): Promise<boolean>;
841
+ /**
842
+ * Get capabilities for a model
843
+ */
844
+ getCapabilities(model: LanguageModel): Promise<ModelCapabilities>;
845
+ /**
846
+ * Get provider compatibility settings
847
+ */
848
+ getCompatibility(model: LanguageModel): Promise<ProviderCompatibility | undefined>;
849
+ /**
850
+ * Force refresh from remote API
851
+ */
852
+ refreshRemote(): Promise<void>;
853
+ /**
854
+ * Get current network status
855
+ */
856
+ getNetworkStatus(): NetworkStatus;
857
+ /**
858
+ * Get resolver statistics
859
+ */
860
+ getStats(): {
861
+ cacheSize: number;
862
+ cacheLoaded: boolean;
863
+ remoteFetchEnabled: boolean;
864
+ networkOnline: boolean;
865
+ };
866
+ /**
867
+ * Clear all cached data
868
+ */
869
+ clearCache(): Promise<void>;
870
+ /**
871
+ * List all available models
872
+ * Fetches from remote if cache is empty and remote is enabled
873
+ */
874
+ listModels(): Promise<ModelEntry[]>;
875
+ /**
876
+ * List all available models grouped by provider
877
+ */
878
+ listModelsByProvider(): Promise<Record<string, ModelEntry[]>>;
879
+ }
880
+ /**
881
+ * Get the default resolver instance
882
+ */
883
+ declare function getDefaultResolver(): ModelCapabilityResolver;
884
+ /**
885
+ * Configure the default resolver with custom options
886
+ */
887
+ declare function configureResolver(options: Partial<ResolverOptions>): void;
888
+
889
+ /**
890
+ * Reasoning Configuration & Option Builders
891
+ *
892
+ * Orchestrates capability detection and provider-specific option
893
+ * building to produce ready-to-use `providerOptions` for the
894
+ * Vercel AI SDK.
895
+ *
896
+ * Two flavours of every function are provided:
897
+ * - **Async** (`getReasoningConfig`, `buildReasoningOptions`) —
898
+ * uses the full capability resolver (network + cache).
899
+ * - **Sync** (`getReasoningConfigSync`, `buildReasoningOptionsSync`) —
900
+ * uses fast pattern-matching only (no network).
901
+ */
902
+
903
+ /**
904
+ * Get the reasoning configuration for a model.
905
+ *
906
+ * Uses the full capability resolver (including network lookups)
907
+ * for the most accurate result.
908
+ */
909
+ declare function getReasoningConfig(model: LanguageModel): Promise<ReasoningConfig>;
910
+ /**
911
+ * Synchronous reasoning config using pattern-matching only.
912
+ *
913
+ * Faster but less accurate than {@link getReasoningConfig}.
914
+ * Good for hot paths where async is impractical.
915
+ */
916
+ declare function getReasoningConfigSync(model: LanguageModel): ReasoningConfig;
917
+ /**
918
+ * Build `providerOptions` for a reasoning level (async).
919
+ *
920
+ * Returns `undefined` when reasoning is off or unsupported.
921
+ */
922
+ declare function buildReasoningOptions(model: LanguageModel, level: ReasoningLevel): Promise<ProviderOptions | undefined>;
923
+ /**
924
+ * Build `providerOptions` for a reasoning level (sync / pattern-only).
925
+ */
926
+ declare function buildReasoningOptionsSync(model: LanguageModel, level: ReasoningLevel): ProviderOptions | undefined;
927
+ /**
928
+ * Check whether a model supports reasoning (async, full resolver).
929
+ */
930
+ declare function supportsReasoning(model: LanguageModel): Promise<boolean>;
931
+ /**
932
+ * Synchronous check using pattern-matching only.
933
+ */
934
+ declare function supportsReasoningSync(model: LanguageModel): boolean;
935
+
936
+ /**
937
+ * MCP (Model Context Protocol) Integration for @cuylabs/agent-core
938
+ *
939
+ * Provides seamless integration with MCP servers, allowing agents to
940
+ * use tools, resources, and prompts from external MCP-compatible services.
941
+ *
942
+ * Built on top of @ai-sdk/mcp for the core client functionality.
943
+ *
944
+ * @example
945
+ * ```typescript
946
+ * import { createAgent, createMCPManager } from "@cuylabs/agent-core";
947
+ *
948
+ * const mcp = createMCPManager({
949
+ * filesystem: {
950
+ * transport: "stdio",
951
+ * command: "npx",
952
+ * args: ["-y", "@modelcontextprotocol/server-filesystem", "/path"],
953
+ * },
954
+ * weather: {
955
+ * transport: "http",
956
+ * url: "https://weather-mcp.example.com/mcp",
957
+ * },
958
+ * });
959
+ *
960
+ * const agent = createAgent({
961
+ * model: openai("gpt-4o"),
962
+ * mcp,
963
+ * });
964
+ * ```
965
+ */
966
+
967
+ /**
968
+ * Stdio transport configuration for local MCP servers
969
+ */
970
+ interface StdioTransportConfig {
971
+ transport: "stdio";
972
+ /** Command to execute */
973
+ command: string;
974
+ /** Command arguments */
975
+ args?: string[];
976
+ /** Environment variables */
977
+ env?: Record<string, string>;
978
+ /** Working directory */
979
+ cwd?: string;
980
+ }
981
+ /**
982
+ * HTTP transport configuration for remote MCP servers
983
+ */
984
+ interface HttpTransportConfig {
985
+ transport: "http";
986
+ /** Server URL */
987
+ url: string;
988
+ /** HTTP headers (e.g., Authorization) */
989
+ headers?: Record<string, string>;
990
+ }
991
+ /**
992
+ * SSE transport configuration for remote MCP servers
993
+ */
994
+ interface SseTransportConfig {
995
+ transport: "sse";
996
+ /** Server URL */
997
+ url: string;
998
+ /** HTTP headers (e.g., Authorization) */
999
+ headers?: Record<string, string>;
1000
+ }
1001
+ /**
1002
+ * MCP server configuration
1003
+ */
1004
+ type MCPServerConfig = (StdioTransportConfig | HttpTransportConfig | SseTransportConfig) & {
1005
+ /** Whether this server is enabled (default: true) */
1006
+ enabled?: boolean;
1007
+ /** Connection timeout in ms (default: 30000) */
1008
+ timeout?: number;
1009
+ /** Human-readable name for the server */
1010
+ name?: string;
1011
+ };
1012
+ /**
1013
+ * MCP manager configuration - map of server name to config
1014
+ */
1015
+ type MCPConfig = Record<string, MCPServerConfig>;
1016
+ /**
1017
+ * Server connection status
1018
+ */
1019
+ type MCPServerStatus = {
1020
+ status: "disconnected";
1021
+ } | {
1022
+ status: "connecting";
1023
+ } | {
1024
+ status: "connected";
1025
+ toolCount: number;
1026
+ } | {
1027
+ status: "error";
1028
+ error: string;
1029
+ } | {
1030
+ status: "disabled";
1031
+ };
1032
+ /**
1033
+ * MCP resource (from server)
1034
+ */
1035
+ interface MCPResource {
1036
+ uri: string;
1037
+ name: string;
1038
+ description?: string;
1039
+ mimeType?: string;
1040
+ server: string;
1041
+ }
1042
+ /**
1043
+ * MCP prompt (from server)
1044
+ */
1045
+ interface MCPPrompt {
1046
+ name: string;
1047
+ description?: string;
1048
+ arguments?: Array<{
1049
+ name: string;
1050
+ description?: string;
1051
+ required?: boolean;
1052
+ }>;
1053
+ server: string;
1054
+ }
1055
+ /**
1056
+ * MCP Manager - handles connections to multiple MCP servers
1057
+ */
1058
+ interface MCPManager {
1059
+ /**
1060
+ * Connect to all configured servers
1061
+ * @returns Map of server names to their status
1062
+ */
1063
+ connect(): Promise<Map<string, MCPServerStatus>>;
1064
+ /**
1065
+ * Get all tools from connected servers
1066
+ * Returns AI SDK compatible tools ready for use with streamText/generateText
1067
+ */
1068
+ getTools(): Promise<Record<string, Tool>>;
1069
+ /**
1070
+ * Get status of a specific server
1071
+ */
1072
+ getStatus(serverName: string): MCPServerStatus;
1073
+ /**
1074
+ * Get status of all servers
1075
+ */
1076
+ getAllStatus(): Map<string, MCPServerStatus>;
1077
+ /**
1078
+ * List available resources from all connected servers
1079
+ */
1080
+ listResources(): Promise<MCPResource[]>;
1081
+ /**
1082
+ * Read a resource by URI
1083
+ */
1084
+ readResource(uri: string): Promise<{
1085
+ contents: Array<{
1086
+ uri: string;
1087
+ text?: string;
1088
+ blob?: string;
1089
+ mimeType?: string;
1090
+ }>;
1091
+ }>;
1092
+ /**
1093
+ * List available prompts from all connected servers
1094
+ */
1095
+ listPrompts(): Promise<MCPPrompt[]>;
1096
+ /**
1097
+ * Get a prompt with optional arguments
1098
+ */
1099
+ getPrompt(serverName: string, promptName: string, args?: Record<string, unknown>): Promise<{
1100
+ description?: string;
1101
+ messages: Array<{
1102
+ role: string;
1103
+ content: unknown;
1104
+ }>;
1105
+ }>;
1106
+ /**
1107
+ * Close all connections
1108
+ */
1109
+ close(): Promise<void>;
1110
+ /**
1111
+ * Check if manager has any connected servers
1112
+ */
1113
+ isConnected(): boolean;
1114
+ }
1115
+ /**
1116
+ * Create an MCP manager for connecting to multiple MCP servers
1117
+ *
1118
+ * @example
1119
+ * ```typescript
1120
+ * const mcp = createMCPManager({
1121
+ * // Local filesystem server via stdio
1122
+ * filesystem: {
1123
+ * transport: "stdio",
1124
+ * command: "npx",
1125
+ * args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user"],
1126
+ * },
1127
+ * // Remote weather API via HTTP
1128
+ * weather: {
1129
+ * transport: "http",
1130
+ * url: "https://weather.example.com/mcp",
1131
+ * headers: { Authorization: "Bearer xxx" },
1132
+ * },
1133
+ * });
1134
+ *
1135
+ * // Connect to all servers
1136
+ * await mcp.connect();
1137
+ *
1138
+ * // Get all tools for use with AI SDK
1139
+ * const tools = await mcp.getTools();
1140
+ * ```
1141
+ */
1142
+ declare function createMCPManager(config: MCPConfig): MCPManager;
1143
+ /**
1144
+ * Helper to define a single MCP server config
1145
+ * Useful for building configs programmatically
1146
+ */
1147
+ declare function defineServer(config: MCPServerConfig): MCPServerConfig;
1148
+ /**
1149
+ * Helper to create a stdio server config
1150
+ */
1151
+ declare function stdioServer(command: string, args?: string[], options?: Omit<StdioTransportConfig, "transport" | "command" | "args">): StdioTransportConfig;
1152
+ /**
1153
+ * Helper to create an HTTP server config
1154
+ */
1155
+ declare function httpServer(url: string, options?: Omit<HttpTransportConfig, "transport" | "url">): HttpTransportConfig;
1156
+ /**
1157
+ * Helper to create an SSE server config
1158
+ */
1159
+ declare function sseServer(url: string, options?: Omit<SseTransportConfig, "transport" | "url">): SseTransportConfig;
1160
+
1161
+ /**
1162
+ * Prompt Pipeline Types
1163
+ *
1164
+ * Types for the layered system prompt architecture.
1165
+ * The prompt pipeline composes a system prompt from multiple sources:
1166
+ *
1167
+ * Base Template → Environment → Instructions → Custom Sections → Per-Turn
1168
+ *
1169
+ * Each layer is optional, composable, and can be toggled on/off.
1170
+ */
1171
+
1172
+ /**
1173
+ * Model family identifier for prompt template selection.
1174
+ *
1175
+ * Each family gets a base template optimized for its strengths:
1176
+ * - `anthropic`: Claude models — structured sections with XML tags
1177
+ * - `openai`: GPT/o-series models — clear directives with markdown
1178
+ * - `google`: Gemini models — balanced approach
1179
+ * - `deepseek`: DeepSeek models — code-focused emphasis
1180
+ * - `default`: Generic template for any model
1181
+ */
1182
+ type ModelFamily = "anthropic" | "openai" | "google" | "deepseek" | "default";
1183
+ /**
1184
+ * Runtime environment information injected into the system prompt.
1185
+ * Gives the model awareness of the working context.
1186
+ */
1187
+ interface EnvironmentInfo {
1188
+ /** Current working directory */
1189
+ cwd: string;
1190
+ /** Operating system (e.g. "macOS (darwin arm64)") */
1191
+ platform: string;
1192
+ /** Current date/time formatted string */
1193
+ date: string;
1194
+ /** User's shell (e.g. "/bin/zsh") */
1195
+ shell?: string;
1196
+ /** Active git branch, if inside a repo */
1197
+ gitBranch?: string;
1198
+ /** Whether the working tree is clean */
1199
+ gitClean?: boolean;
1200
+ /** Git repo root path */
1201
+ gitRoot?: string;
1202
+ }
1203
+ /**
1204
+ * An instruction file discovered on disk (e.g. AGENTS.md).
1205
+ *
1206
+ * Instruction files provide project-specific or workspace-level guidance
1207
+ * that gets injected into every prompt. They're discovered by walking up
1208
+ * the directory tree from cwd.
1209
+ */
1210
+ interface InstructionFile {
1211
+ /** Absolute path to the file */
1212
+ path: string;
1213
+ /** File content (trimmed) */
1214
+ content: string;
1215
+ /** How the file was discovered */
1216
+ source: "project" | "workspace" | "global";
1217
+ /** Depth from cwd (0 = same directory) */
1218
+ depth: number;
1219
+ }
1220
+ /**
1221
+ * A composable section in the prompt pipeline.
1222
+ *
1223
+ * Sections are the building blocks of the final system prompt.
1224
+ * Each section has a priority that determines its position in the output.
1225
+ *
1226
+ * Default priority ranges:
1227
+ * - 10: Base template
1228
+ * - 20: Environment block
1229
+ * - 30: Instruction files
1230
+ * - 50: Custom sections (default for user-added)
1231
+ * - 70: Reserved for future use (e.g. Skills)
1232
+ * - 90: Per-turn overrides
1233
+ */
1234
+ interface PromptSection {
1235
+ /** Unique identifier for this section */
1236
+ id: string;
1237
+ /** Human-readable label (useful for debugging/logging) */
1238
+ label: string;
1239
+ /** The text content of this section */
1240
+ content: string;
1241
+ /** Sort priority — lower values appear earlier (default: 50) */
1242
+ priority?: number;
1243
+ /** Whether this section is active (default: true) */
1244
+ enabled?: boolean;
1245
+ }
1246
+ /**
1247
+ * Context passed to the builder for each prompt build.
1248
+ *
1249
+ * This provides the runtime information needed to compose
1250
+ * the system prompt for a specific conversation turn.
1251
+ */
1252
+ interface PromptBuildContext {
1253
+ /** Current working directory */
1254
+ cwd: string;
1255
+ /** The language model being used */
1256
+ model: LanguageModel;
1257
+ /** Names of available tools (for template customization) */
1258
+ toolNames?: string[];
1259
+ /** Per-turn additional instructions (from chat options) */
1260
+ override?: string;
1261
+ /** Current session ID */
1262
+ sessionId?: string;
1263
+ }
1264
+ /**
1265
+ * Configuration for the prompt pipeline.
1266
+ *
1267
+ * Controls which layers are active and how they behave.
1268
+ * All options have sensible defaults — an empty config `{}` gives you
1269
+ * the full pipeline with auto-detection.
1270
+ *
1271
+ * @example
1272
+ * ```typescript
1273
+ * // Minimal — use all defaults
1274
+ * const builder = createPromptBuilder();
1275
+ *
1276
+ * // Custom base template but keep environment + instructions
1277
+ * const builder = createPromptBuilder({
1278
+ * baseTemplate: "You are a security auditor...",
1279
+ * includeEnvironment: true,
1280
+ * includeInstructions: true,
1281
+ * });
1282
+ *
1283
+ * // Fully custom — disable auto features, add your own sections
1284
+ * const builder = createPromptBuilder({
1285
+ * includeEnvironment: false,
1286
+ * includeInstructions: false,
1287
+ * sections: [
1288
+ * { id: "role", label: "Role", content: "You audit code.", priority: 10 },
1289
+ * { id: "rules", label: "Rules", content: "Never modify files.", priority: 20 },
1290
+ * ],
1291
+ * });
1292
+ * ```
1293
+ */
1294
+ interface PromptConfig {
1295
+ /**
1296
+ * Override the base template entirely.
1297
+ * If set, replaces the model-family-specific template.
1298
+ */
1299
+ baseTemplate?: string;
1300
+ /**
1301
+ * Force a specific model family for template selection.
1302
+ * Auto-detected from the model if not provided.
1303
+ */
1304
+ modelFamily?: ModelFamily;
1305
+ /**
1306
+ * Inject runtime environment info (cwd, platform, git, date).
1307
+ * @default true
1308
+ */
1309
+ includeEnvironment?: boolean;
1310
+ /**
1311
+ * Discover and include instruction files (AGENTS.md, etc.).
1312
+ * @default true
1313
+ */
1314
+ includeInstructions?: boolean;
1315
+ /**
1316
+ * File name patterns to search for when discovering instructions.
1317
+ * Searched in each directory walking up from cwd.
1318
+ *
1319
+ * @default ["AGENTS.md", "CLAUDE.md", "COPILOT.md", ".cuylabs/instructions.md"]
1320
+ */
1321
+ instructionPatterns?: string[];
1322
+ /**
1323
+ * Absolute paths to global instruction files (always included
1324
+ * regardless of cwd). Useful for organization-wide rules.
1325
+ */
1326
+ globalInstructions?: string[];
1327
+ /**
1328
+ * Maximum depth to walk up from cwd when searching for instructions.
1329
+ * Prevents scanning the entire filesystem.
1330
+ * @default 10
1331
+ */
1332
+ instructionMaxDepth?: number;
1333
+ /**
1334
+ * Maximum file size in bytes for instruction files.
1335
+ * Files larger than this are skipped to avoid bloating the prompt.
1336
+ * @default 51200 (50KB)
1337
+ */
1338
+ instructionMaxSize?: number;
1339
+ /**
1340
+ * Pre-defined sections to include in every prompt build.
1341
+ * These are merged with auto-generated sections (template, environment, etc.).
1342
+ */
1343
+ sections?: PromptSection[];
1344
+ /**
1345
+ * Separator between sections in the final composed prompt.
1346
+ * @default "\n\n"
1347
+ */
1348
+ separator?: string;
1349
+ }
1350
+
1351
+ /**
1352
+ * Agent configuration and state types for @cuylabs/agent-core
1353
+ *
1354
+ * Defines AgentConfig (the main config surface), AgentState,
1355
+ * doom-loop types, and compaction configuration.
1356
+ */
1357
+
1358
+ /**
1359
+ * Agent configuration
1360
+ */
1361
+ interface AgentConfig {
1362
+ /** Vercel AI SDK model instance */
1363
+ model: LanguageModel;
1364
+ /** System prompt */
1365
+ systemPrompt?: string;
1366
+ /** Working directory */
1367
+ cwd?: string;
1368
+ /**
1369
+ * Execution environment for tools.
1370
+ *
1371
+ * Controls *where* tools run — local machine, Docker container, SSH host, etc.
1372
+ * When not provided, defaults to `localHost(cwd)` which uses the local
1373
+ * filesystem and shell directly.
1374
+ *
1375
+ * @example
1376
+ * ```typescript
1377
+ * import { localHost } from "@cuylabs/agent-core";
1378
+ *
1379
+ * // Explicit local host (same as default)
1380
+ * const agent = createAgent({ host: localHost("/my/project") });
1381
+ *
1382
+ * // Future: Docker host
1383
+ * // const agent = createAgent({ host: dockerHost("my-container") });
1384
+ * ```
1385
+ */
1386
+ host?: ToolHost;
1387
+ /** Temperature (0-1) */
1388
+ temperature?: number;
1389
+ /** Top-p sampling */
1390
+ topP?: number;
1391
+ /** Max output tokens */
1392
+ maxOutputTokens?: number;
1393
+ /** Maximum steps (tool call iterations) */
1394
+ maxSteps?: number;
1395
+ /** Reasoning/thinking level for models that support it */
1396
+ reasoningLevel?: ReasoningLevel;
1397
+ /** Require approval for dangerous operations */
1398
+ requireApproval?: boolean;
1399
+ /**
1400
+ * Handler for doom loop detection.
1401
+ *
1402
+ * When the agent detects repeated identical tool calls (default: 3 times),
1403
+ * this handler is called to decide what to do.
1404
+ *
1405
+ * If not provided:
1406
+ * - enforceDoomLoop=true (default): throws DoomLoopError
1407
+ * - enforceDoomLoop=false: logs warning and continues
1408
+ *
1409
+ * @see DoomLoopHandler
1410
+ */
1411
+ onDoomLoop?: DoomLoopHandler;
1412
+ /**
1413
+ * Strictly enforce doom loop (throw error).
1414
+ * Only used when onDoomLoop is not provided.
1415
+ * Default: true
1416
+ */
1417
+ enforceDoomLoop?: boolean;
1418
+ /**
1419
+ * Number of identical tool calls before triggering doom loop.
1420
+ * Default: 3 (like OpenCode's DOOM_LOOP_THRESHOLD)
1421
+ */
1422
+ doomLoopThreshold?: number;
1423
+ /**
1424
+ * Context compaction configuration.
1425
+ * Controls automatic management of the context window.
1426
+ *
1427
+ * @see CompactionConfig
1428
+ */
1429
+ compaction?: CompactionConfig;
1430
+ /**
1431
+ * Context window size in tokens (for overflow detection).
1432
+ * If not provided, defaults to 128,000 tokens.
1433
+ *
1434
+ * This should match your model's context limit.
1435
+ */
1436
+ contextWindow?: number;
1437
+ /**
1438
+ * Custom stream provider for specialized models (e.g., OpenAI computer use).
1439
+ *
1440
+ * When provided, this function will be called instead of the default
1441
+ * AI SDK streamText(). This allows packages like @cuylabs/computer-agent
1442
+ * to inject custom streaming implementations without circular dependencies.
1443
+ *
1444
+ * The returned object must have a `fullStream` async iterable that yields
1445
+ * AI SDK compatible chunks (text-delta, tool-call, tool-result, etc.).
1446
+ */
1447
+ streamProvider?: StreamProvider;
1448
+ /**
1449
+ * MCP (Model Context Protocol) manager for external tool servers.
1450
+ *
1451
+ * When provided, the agent will connect to configured MCP servers
1452
+ * and make their tools available alongside local tools.
1453
+ *
1454
+ * @example
1455
+ * ```typescript
1456
+ * import { createMCPManager } from "@cuylabs/agent-core";
1457
+ *
1458
+ * const mcp = createMCPManager({
1459
+ * filesystem: {
1460
+ * transport: "stdio",
1461
+ * command: "npx",
1462
+ * args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
1463
+ * },
1464
+ * });
1465
+ *
1466
+ * const agent = createAgent({
1467
+ * model: openai("gpt-4o"),
1468
+ * mcp,
1469
+ * });
1470
+ * ```
1471
+ */
1472
+ mcp?: MCPManager;
1473
+ /**
1474
+ * Prompt pipeline configuration.
1475
+ *
1476
+ * When provided, enables the layered prompt system that automatically
1477
+ * composes system prompts from:
1478
+ * - Model-family-optimized base templates
1479
+ * - Runtime environment info (cwd, platform, git branch)
1480
+ * - Instruction files (AGENTS.md, CLAUDE.md discovered on disk)
1481
+ * - Custom sections you define
1482
+ * - Per-turn overrides from chat options
1483
+ *
1484
+ * When neither `prompt` nor `systemPrompt` is provided, the pipeline
1485
+ * is enabled with sensible defaults.
1486
+ *
1487
+ * When `systemPrompt` is provided without `prompt`, the flat string
1488
+ * is used directly (backward compatible).
1489
+ *
1490
+ * @example
1491
+ * ```typescript
1492
+ * const agent = createAgent({
1493
+ * model: anthropic("claude-sonnet-4-20250514"),
1494
+ * prompt: {
1495
+ * includeEnvironment: true,
1496
+ * includeInstructions: true,
1497
+ * sections: [
1498
+ * { id: "team", label: "Team Rules", content: "Use strict TypeScript." },
1499
+ * ],
1500
+ * },
1501
+ * });
1502
+ * ```
1503
+ */
1504
+ prompt?: PromptConfig;
1505
+ }
1506
+ /**
1507
+ * Agent state
1508
+ */
1509
+ interface AgentState {
1510
+ model: LanguageModel;
1511
+ systemPrompt: string;
1512
+ cwd: string;
1513
+ isStreaming: boolean;
1514
+ /** Current reasoning level */
1515
+ reasoningLevel: ReasoningLevel;
1516
+ error?: Error;
1517
+ }
1518
+ /**
1519
+ * Doom loop handler response - what to do when a doom loop is detected.
1520
+ *
1521
+ * Aligned with OpenCode's PermissionNext.Reply:
1522
+ * - "once" (OpenCode) → "allow" - Allow this once
1523
+ * - "always" (OpenCode) → "remember" - Allow and remember for this session
1524
+ * - "reject" (OpenCode) → "deny" - Reject and throw error
1525
+ */
1526
+ type DoomLoopAction = "allow" | "deny" | "remember";
1527
+ /**
1528
+ * Doom loop detection request.
1529
+ * Sent to the handler when repeated tool calls are detected.
1530
+ */
1531
+ interface DoomLoopRequest {
1532
+ /** The tool being called repeatedly */
1533
+ tool: string;
1534
+ /** How many times it's been called with same input */
1535
+ repeatCount: number;
1536
+ /** The input being passed (for context) */
1537
+ input: unknown;
1538
+ /** Session ID */
1539
+ sessionId: string;
1540
+ }
1541
+ /**
1542
+ * Handler for doom loop situations.
1543
+ *
1544
+ * Return:
1545
+ * - "allow": Continue despite the loop (like OpenCode's "once")
1546
+ * - "deny": Stop execution and throw error (like OpenCode's "reject")
1547
+ * - "remember": Allow and don't ask again for this tool in this session (like OpenCode's "always")
1548
+ *
1549
+ * @example
1550
+ * ```typescript
1551
+ * const agent = createAgent({
1552
+ * model: anthropic("claude-sonnet-4-20250514"),
1553
+ * onDoomLoop: async (req) => {
1554
+ * // Show UI asking user
1555
+ * const action = await showConfirmDialog(
1556
+ * `${req.tool} called ${req.repeatCount} times with same input. Continue?`
1557
+ * );
1558
+ * return action;
1559
+ * },
1560
+ * });
1561
+ * ```
1562
+ */
1563
+ type DoomLoopHandler = (request: DoomLoopRequest) => Promise<DoomLoopAction>;
1564
+ /**
1565
+ * Configuration for automatic context compaction.
1566
+ *
1567
+ * Aligned with OpenCode's SessionCompaction:
1568
+ * - Auto-pruning of old tool outputs
1569
+ * - LLM-based summarization
1570
+ * - Auto-continue after compaction
1571
+ */
1572
+ interface CompactionConfig {
1573
+ /**
1574
+ * Enable automatic compaction when context overflows.
1575
+ * Default: true (matches OpenCode's default)
1576
+ */
1577
+ auto?: boolean;
1578
+ /**
1579
+ * Enable pruning of old tool outputs before summarization.
1580
+ * This is a lightweight operation that removes tool result content.
1581
+ * Default: true
1582
+ */
1583
+ prune?: boolean;
1584
+ /**
1585
+ * Protect this many recent tokens from pruning.
1586
+ * Default: 40,000 (like OpenCode's PRUNE_PROTECT)
1587
+ */
1588
+ protectedTokens?: number;
1589
+ /**
1590
+ * Minimum tokens to trigger pruning.
1591
+ * Default: 20,000 (like OpenCode's PRUNE_MINIMUM)
1592
+ */
1593
+ pruneMinimum?: number;
1594
+ /**
1595
+ * Custom summarization prompt for compaction.
1596
+ * If not provided, uses default prompt asking for continuation context.
1597
+ */
1598
+ summaryPrompt?: string;
1599
+ /**
1600
+ * Model to use for summarization (optional).
1601
+ * If not provided, uses the same model as the agent.
1602
+ * You might want to use a cheaper/faster model here.
1603
+ */
1604
+ summaryModel?: LanguageModel;
1605
+ /**
1606
+ * Auto-continue after compaction with "Continue if you have next steps".
1607
+ * Like OpenCode's behavior.
1608
+ * Default: true
1609
+ */
1610
+ autoContinue?: boolean;
1611
+ }
1612
+
1613
+ /**
1614
+ * Session Manager
1615
+ *
1616
+ * High-level API for session management with tree structure support.
1617
+ * Uses pluggable storage backend.
1618
+ */
1619
+
1620
+ /**
1621
+ * Options for creating a new session
1622
+ */
1623
+ interface CreateSessionOptions {
1624
+ /** Custom session ID (auto-generated if not provided) */
1625
+ id?: string;
1626
+ /** Working directory */
1627
+ cwd: string;
1628
+ /** Session title */
1629
+ title?: string;
1630
+ /** Parent session ID (if forking) */
1631
+ parentSessionId?: string;
1632
+ }
1633
+ /**
1634
+ * Session context for LLM
1635
+ */
1636
+ interface SessionContext {
1637
+ /** Messages for LLM context */
1638
+ messages: Message[];
1639
+ /** Current leaf entry ID */
1640
+ leafId: string | null;
1641
+ /** Session metadata */
1642
+ metadata: {
1643
+ id: string;
1644
+ cwd: string;
1645
+ title?: string;
1646
+ name?: string;
1647
+ };
1648
+ }
1649
+ /**
1650
+ * Session Manager
1651
+ *
1652
+ * Manages session lifecycle with tree-structured entries.
1653
+ * Supports branching, compaction, and pluggable storage.
1654
+ */
1655
+ declare class SessionManager {
1656
+ private storage;
1657
+ private currentSessionId;
1658
+ private currentLeafId;
1659
+ private entriesCache;
1660
+ private idsCache;
1661
+ constructor(storage?: SessionStorage);
1662
+ /**
1663
+ * Create a new session
1664
+ */
1665
+ create(options: CreateSessionOptions): Promise<string>;
1666
+ /**
1667
+ * Load an existing session
1668
+ */
1669
+ load(sessionId: string): Promise<void>;
1670
+ /**
1671
+ * Get current session ID
1672
+ */
1673
+ getSessionId(): string | null;
1674
+ /**
1675
+ * Get current leaf ID
1676
+ */
1677
+ getLeafId(): string | null;
1678
+ /**
1679
+ * Add a message to the session
1680
+ */
1681
+ addMessage(message: Message): Promise<string>;
1682
+ /**
1683
+ * Add multiple messages atomically
1684
+ */
1685
+ addMessages(messages: Message[]): Promise<string[]>;
1686
+ /**
1687
+ * Get messages for LLM context
1688
+ */
1689
+ getMessages(leafId?: string): Message[];
1690
+ /**
1691
+ * Add a compaction entry (after pruning context)
1692
+ */
1693
+ addCompaction(options: {
1694
+ summary: string;
1695
+ firstKeptEntryId: string;
1696
+ tokensBefore: number;
1697
+ tokensAfter: number;
1698
+ readFiles?: string[];
1699
+ modifiedFiles?: string[];
1700
+ }): Promise<string>;
1701
+ /**
1702
+ * Branch from a specific entry
1703
+ * Sets the leaf to that entry so new messages continue from there
1704
+ */
1705
+ branch(fromEntryId: string, summary?: string): Promise<string>;
1706
+ /**
1707
+ * Switch to a different leaf (navigate to a branch)
1708
+ */
1709
+ switchToLeaf(leafId: string): void;
1710
+ /**
1711
+ * Update session metadata
1712
+ */
1713
+ updateMetadata(updates: {
1714
+ title?: string;
1715
+ name?: string;
1716
+ }): Promise<void>;
1717
+ /**
1718
+ * Get full session context for LLM
1719
+ */
1720
+ getContext(): SessionContext;
1721
+ /**
1722
+ * Get all entries (for debugging/inspection)
1723
+ */
1724
+ getEntries(): FileEntry[];
1725
+ /**
1726
+ * Get header
1727
+ */
1728
+ getHeader(): SessionHeader | null;
1729
+ /**
1730
+ * List all sessions
1731
+ */
1732
+ listSessions(): Promise<SessionInfo[]>;
1733
+ /**
1734
+ * Delete a session
1735
+ */
1736
+ deleteSession(sessionId: string): Promise<boolean>;
1737
+ /**
1738
+ * Check if session exists
1739
+ */
1740
+ sessionExists(sessionId: string): Promise<boolean>;
1741
+ /**
1742
+ * Get underlying storage
1743
+ */
1744
+ getStorage(): SessionStorage;
1745
+ }
1746
+ /**
1747
+ * Get default session manager instance
1748
+ */
1749
+ declare function getDefaultSessionManager(): SessionManager;
1750
+ /**
1751
+ * Configure default session manager with custom storage
1752
+ */
1753
+ declare function configureDefaultSessionManager(storage: SessionStorage): SessionManager;
1754
+
1755
+ /**
1756
+ * Storage Utilities
1757
+ *
1758
+ * Helper functions for session storage operations.
1759
+ */
1760
+
1761
+ /**
1762
+ * Generate a unique 8-character hex ID
1763
+ * Collision-checked against existing IDs
1764
+ */
1765
+ declare function generateEntryId(existingIds?: Set<string>): string;
1766
+ /**
1767
+ * Parse JSONL content into entries
1768
+ * Handles malformed lines gracefully
1769
+ */
1770
+ declare function parseJSONL<T>(content: string): T[];
1771
+ /**
1772
+ * Serialize entry to JSONL line
1773
+ */
1774
+ declare function toJSONL(entry: FileEntry): string;
1775
+ /**
1776
+ * Serialize a Message to storage format
1777
+ */
1778
+ declare function serializeMessage(message: Message): SerializedMessage;
1779
+ /**
1780
+ * Deserialize a stored message back to Message
1781
+ */
1782
+ declare function deserializeMessage(serialized: SerializedMessage): Message;
1783
+ /**
1784
+ * Get leaf entry ID (last entry in the chain)
1785
+ */
1786
+ declare function getLeafId(entries: FileEntry[]): string | null;
1787
+ /**
1788
+ * Build messages array from entries for a specific leaf
1789
+ * Handles compaction summaries
1790
+ */
1791
+ declare function buildMessagesFromEntries(entries: FileEntry[], leafId?: string): Message[];
1792
+
1793
+ /**
1794
+ * Memory Session Storage
1795
+ *
1796
+ * In-memory implementation of SessionStorage.
1797
+ * Useful for testing and ephemeral sessions.
1798
+ */
1799
+
1800
+ /**
1801
+ * In-memory session storage
1802
+ */
1803
+ declare class MemoryStorage implements SessionStorage {
1804
+ private sessions;
1805
+ create(header: SessionHeader): Promise<void>;
1806
+ append(sessionId: string, entry: SessionEntry): Promise<void>;
1807
+ appendBatch(sessionId: string, entries: SessionEntry[]): Promise<void>;
1808
+ read(sessionId: string): Promise<FileEntry[]>;
1809
+ delete(sessionId: string): Promise<boolean>;
1810
+ exists(sessionId: string): Promise<boolean>;
1811
+ list(): Promise<SessionInfo[]>;
1812
+ clear(): Promise<void>;
1813
+ }
1814
+
1815
+ /**
1816
+ * File Session Storage
1817
+ *
1818
+ * JSONL file-based implementation of SessionStorage.
1819
+ * Each session is stored as a separate .jsonl file with append-only writes.
1820
+ *
1821
+ * Features:
1822
+ * - Async I/O (non-blocking)
1823
+ * - Append-only writes (crash-safe)
1824
+ * - In-memory cache for fast reads
1825
+ * - Tree structure support via id/parentId
1826
+ */
1827
+
1828
+ /**
1829
+ * Options for file storage
1830
+ */
1831
+ interface FileStorageOptions {
1832
+ /** Directory to store session files */
1833
+ directory: string;
1834
+ /** File extension (default: .jsonl) */
1835
+ extension?: string;
1836
+ /** Enable in-memory cache (default: true) */
1837
+ cache?: boolean;
1838
+ }
1839
+ /**
1840
+ * JSONL file-based session storage
1841
+ */
1842
+ declare class FileStorage implements SessionStorage {
1843
+ private readonly directory;
1844
+ private readonly extension;
1845
+ private readonly useCache;
1846
+ private cache;
1847
+ private initialized;
1848
+ constructor(options: FileStorageOptions);
1849
+ /**
1850
+ * Ensure directory exists
1851
+ */
1852
+ private ensureDir;
1853
+ /**
1854
+ * Get file path for a session
1855
+ */
1856
+ private getPath;
1857
+ /**
1858
+ * Load entries from disk
1859
+ */
1860
+ private loadFromDisk;
1861
+ /**
1862
+ * Get entries (from cache or disk)
1863
+ */
1864
+ private getEntries;
1865
+ create(header: SessionHeader): Promise<void>;
1866
+ append(sessionId: string, entry: SessionEntry): Promise<void>;
1867
+ appendBatch(sessionId: string, entries: SessionEntry[]): Promise<void>;
1868
+ read(sessionId: string): Promise<FileEntry[]>;
1869
+ delete(sessionId: string): Promise<boolean>;
1870
+ exists(sessionId: string): Promise<boolean>;
1871
+ list(): Promise<SessionInfo[]>;
1872
+ clear(): Promise<void>;
1873
+ /**
1874
+ * Flush cache (force reload from disk on next read)
1875
+ */
1876
+ flushCache(): void;
1877
+ /**
1878
+ * Reload a session from disk (bypass cache)
1879
+ */
1880
+ reload(sessionId: string): Promise<FileEntry[]>;
1881
+ /**
1882
+ * Get session IDs without loading full data
1883
+ */
1884
+ listIds(): Promise<string[]>;
1885
+ /**
1886
+ * Get file stats for a session
1887
+ */
1888
+ getStats(sessionId: string): Promise<{
1889
+ size: number;
1890
+ mtime: Date;
1891
+ } | null>;
1892
+ }
1893
+
1894
+ /**
1895
+ * Default paths for storage
1896
+ *
1897
+ * Follows platform conventions:
1898
+ * - macOS: ~/.cuylabs/
1899
+ * - Linux: $XDG_DATA_HOME/cuylabs/ or ~/.local/share/cuylabs/
1900
+ * - Windows: %APPDATA%/cuylabs/
1901
+ *
1902
+ * Project identification:
1903
+ * - Uses git root commit hash (like OpenCode) for git repos
1904
+ * - Falls back to encoded path for non-git directories
1905
+ */
1906
+ /**
1907
+ * Get the default data directory for cuylabs
1908
+ */
1909
+ declare function getDataDir(appName?: string): string;
1910
+ /**
1911
+ * Get the default sessions directory
1912
+ */
1913
+ declare function getSessionsDir(appName?: string): string;
1914
+ /**
1915
+ * Get sessions directory for a specific project/cwd
1916
+ */
1917
+ declare function getProjectSessionsDir(cwd: string, appName?: string): string;
1918
+
1919
+ /**
1920
+ * Agent Presets — Reusable Configuration Profiles
1921
+ *
1922
+ * Presets are domain-agnostic agent configurations that can be applied
1923
+ * to `fork()` calls. They control tool selection, system prompt,
1924
+ * temperature, and other parameters.
1925
+ *
1926
+ * Design principles:
1927
+ * 1. **Composable** — Merge multiple presets: `mergePresets(explore, careful)`
1928
+ * 2. **Pattern-based** — Filter tools with glob patterns: `allowTools: ["read*"]`
1929
+ * 3. **Embeddable** — No coupling to CLI, file system, or any specific domain
1930
+ *
1931
+ * @example
1932
+ * ```typescript
1933
+ * import { Presets, applyPreset } from "@cuylabs/agent-core";
1934
+ *
1935
+ * // Apply a built-in preset
1936
+ * const explorer = agent.fork(applyPreset(Presets.explore, agent.getTools()));
1937
+ * const result = await explorer.run({ message: "Find relevant components" });
1938
+ *
1939
+ * // Create a custom preset
1940
+ * const myPreset: Preset = {
1941
+ * name: "analyst",
1942
+ * description: "Read-only analytical mode",
1943
+ * allowTools: ["read*", "grep*", "search*"],
1944
+ * denyTools: ["write*", "exec*"],
1945
+ * systemPrompt: "Analyse thoroughly and report findings.",
1946
+ * temperature: 0.1,
1947
+ * };
1948
+ * ```
1949
+ */
1950
+
1951
+ /**
1952
+ * Agent preset - a reusable configuration profile
1953
+ *
1954
+ * Presets define specialized behavior without coupling to specific tools.
1955
+ * Tool filtering uses glob-like patterns that match at runtime.
1956
+ */
1957
+ interface Preset {
1958
+ /** Unique identifier for this preset */
1959
+ name: string;
1960
+ /** Human-readable description */
1961
+ description: string;
1962
+ /**
1963
+ * Tool allow patterns (glob-like).
1964
+ * If provided, only tools matching these patterns are allowed.
1965
+ * Patterns support: `*` (any chars), `?` (single char)
1966
+ *
1967
+ * @example ["read*", "search*", "glob*"] - Read-only tools
1968
+ * @example ["*"] - All tools
1969
+ */
1970
+ allowTools?: string[];
1971
+ /**
1972
+ * Tool deny patterns (glob-like).
1973
+ * Tools matching these patterns are excluded (applied AFTER allow).
1974
+ *
1975
+ * @example ["bash*", "write*", "delete*"] - Deny destructive tools
1976
+ */
1977
+ denyTools?: string[];
1978
+ /**
1979
+ * Override system prompt.
1980
+ * Use `{basePrompt}` placeholder to include parent's system prompt.
1981
+ *
1982
+ * @example "{basePrompt}\n\nYou are in exploration mode. Be thorough."
1983
+ */
1984
+ systemPrompt?: string;
1985
+ /**
1986
+ * Override temperature (0-1).
1987
+ * Lower = more deterministic, higher = more creative.
1988
+ */
1989
+ temperature?: number;
1990
+ /**
1991
+ * Override max steps.
1992
+ */
1993
+ maxSteps?: number;
1994
+ /**
1995
+ * Override reasoning level.
1996
+ */
1997
+ reasoningLevel?: ReasoningLevel;
1998
+ /**
1999
+ * Override model.
2000
+ * Useful for using cheaper models for simple tasks.
2001
+ */
2002
+ model?: LanguageModel;
2003
+ }
2004
+ /**
2005
+ * Result of applying a preset - ready for fork()
2006
+ */
2007
+ interface AppliedPreset {
2008
+ name: string;
2009
+ systemPrompt?: string;
2010
+ tools?: Tool$1.AnyInfo[];
2011
+ maxSteps?: number;
2012
+ reasoningLevel?: ReasoningLevel;
2013
+ model?: LanguageModel;
2014
+ }
2015
+ /**
2016
+ * Filter tools based on allow/deny patterns
2017
+ *
2018
+ * Patterns match against tool IDs (the first argument to Tool.define).
2019
+ */
2020
+ declare function filterTools(tools: Tool$1.AnyInfo[], options: {
2021
+ allow?: string[];
2022
+ deny?: string[];
2023
+ }): Tool$1.AnyInfo[];
2024
+ /**
2025
+ * Apply a preset to create fork() options
2026
+ *
2027
+ * Takes a preset definition and the available tools, returns options
2028
+ * ready for agent.fork().
2029
+ *
2030
+ * @param preset - The preset configuration
2031
+ * @param availableTools - All tools from the parent agent
2032
+ * @param baseSystemPrompt - Parent's system prompt (for {basePrompt} substitution)
2033
+ * @returns Options object for agent.fork()
2034
+ *
2035
+ * @example
2036
+ * ```typescript
2037
+ * // Apply the explore preset
2038
+ * const options = applyPreset(Presets.explore, agent.getTools(), agent.systemPrompt);
2039
+ * const explorer = agent.fork(options);
2040
+ * ```
2041
+ */
2042
+ declare function applyPreset(preset: Preset, availableTools: Tool$1.AnyInfo[], baseSystemPrompt?: string): AppliedPreset;
2043
+ /**
2044
+ * Merge multiple presets (later presets override earlier ones)
2045
+ *
2046
+ * Tool patterns are combined:
2047
+ * - allowTools: intersection (only tools matching ALL presets)
2048
+ * - denyTools: union (deny if ANY preset denies)
2049
+ *
2050
+ * @example
2051
+ * ```typescript
2052
+ * // Combine explore (read-only) with careful (low temp)
2053
+ * const merged = mergePresets(Presets.explore, Presets.careful);
2054
+ * ```
2055
+ */
2056
+ declare function mergePresets(...presets: Preset[]): Preset;
2057
+ /**
2058
+ * All built-in presets
2059
+ */
2060
+ declare const Presets: {
2061
+ readonly explore: Preset;
2062
+ readonly plan: Preset;
2063
+ readonly review: Preset;
2064
+ readonly quick: Preset;
2065
+ readonly careful: Preset;
2066
+ };
2067
+ /**
2068
+ * Create a custom preset with defaults
2069
+ */
2070
+ declare function createPreset(options: Partial<Preset> & {
2071
+ name: string;
2072
+ }): Preset;
2073
+
2074
+ /**
2075
+ * Turn Change Tracker - Per-turn file modification tracking
2076
+ *
2077
+ * Tracks file changes within a single agent turn for undo/diff capabilities.
2078
+ * Unlike checkpoints (which are session-level snapshots), this provides
2079
+ * granular per-turn tracking with minimal overhead - only touched files
2080
+ * are captured.
2081
+ *
2082
+ * Inspired by OpenCode's Snapshot system and Codex's TurnDiffTracker.
2083
+ *
2084
+ * @example
2085
+ * ```typescript
2086
+ * const tracker = createTurnTracker({ cwd: '/project' });
2087
+ *
2088
+ * // Start tracking a turn
2089
+ * tracker.startTurn('turn-1');
2090
+ *
2091
+ * // Before modifying a file (auto-called by wrapped tools)
2092
+ * await tracker.beforeWrite('/project/src/foo.ts');
2093
+ *
2094
+ * // ... tool makes changes ...
2095
+ *
2096
+ * // End turn and get summary
2097
+ * const summary = await tracker.endTurn();
2098
+ * console.log(summary.diff); // Unified diff of all changes
2099
+ *
2100
+ * // Or undo all changes from this turn
2101
+ * await tracker.undoTurn();
2102
+ * ```
2103
+ */
2104
+ /** Configuration for turn tracker */
2105
+ interface TurnTrackerConfig {
2106
+ /** Working directory (absolute path) - all paths resolved relative to this */
2107
+ cwd: string;
2108
+ /** Whether to use git for diffs (requires git repo). Default: auto-detect */
2109
+ useGit?: boolean;
2110
+ /** Maximum files to track per turn (prevents memory issues). Default: 100 */
2111
+ maxTrackedFiles?: number;
2112
+ }
2113
+ /** File baseline captured before modification */
2114
+ interface FileBaseline {
2115
+ /** Absolute path */
2116
+ path: string;
2117
+ /** Content before modification (null if file didn't exist) */
2118
+ content: string | null;
2119
+ /** File mode (permissions) */
2120
+ mode: number | null;
2121
+ /** Hash of original content for quick comparison */
2122
+ hash: string | null;
2123
+ /** When baseline was captured */
2124
+ capturedAt: Date;
2125
+ }
2126
+ /** Single file change within a turn */
2127
+ interface TurnFileChange {
2128
+ /** Relative path (from cwd) */
2129
+ path: string;
2130
+ /** Type of change */
2131
+ type: "created" | "modified" | "deleted" | "unchanged";
2132
+ /** Lines added */
2133
+ additions: number;
2134
+ /** Lines removed */
2135
+ deletions: number;
2136
+ /** Unified diff for this file */
2137
+ diff?: string;
2138
+ }
2139
+ /** Summary of a completed turn */
2140
+ interface TurnSummary {
2141
+ /** Turn identifier */
2142
+ turnId: string;
2143
+ /** Files changed */
2144
+ files: TurnFileChange[];
2145
+ /** Total files tracked */
2146
+ totalTracked: number;
2147
+ /** Total additions across all files */
2148
+ additions: number;
2149
+ /** Total deletions across all files */
2150
+ deletions: number;
2151
+ /** Combined unified diff for all changes */
2152
+ diff: string | null;
2153
+ /** Turn duration in ms */
2154
+ duration: number;
2155
+ }
2156
+ /** Result of undo operation */
2157
+ interface UndoResult {
2158
+ /** Files restored */
2159
+ restored: string[];
2160
+ /** Files that couldn't be restored (with reasons) */
2161
+ failed: Array<{
2162
+ path: string;
2163
+ reason: string;
2164
+ }>;
2165
+ }
2166
+
2167
+ /** Extended tool metadata with file operation info */
2168
+ interface TrackedToolMetadata {
2169
+ /** File operation metadata for auto-tracking */
2170
+ fileOps?: FileOperationMeta;
2171
+ /** Other metadata */
2172
+ [key: string]: unknown;
2173
+ }
2174
+ /**
2175
+ * Turn Change Tracker
2176
+ *
2177
+ * Maintains in-memory baselines of files before they're modified,
2178
+ * enabling diffs and undo for the current turn.
2179
+ */
2180
+ declare class TurnChangeTracker {
2181
+ private config;
2182
+ private currentTurn;
2183
+ private gitRoot;
2184
+ private gitDetected;
2185
+ constructor(config: TurnTrackerConfig);
2186
+ /**
2187
+ * Start tracking a new turn.
2188
+ * Clears any baselines from previous turn.
2189
+ */
2190
+ startTurn(turnId: string): void;
2191
+ /**
2192
+ * End the current turn and get a summary of changes.
2193
+ * After this, baselines are kept until next startTurn().
2194
+ */
2195
+ endTurn(): Promise<TurnSummary>;
2196
+ /**
2197
+ * Check if currently in an active turn
2198
+ */
2199
+ isInTurn(): boolean;
2200
+ /**
2201
+ * Get current turn ID (if active)
2202
+ */
2203
+ getCurrentTurnId(): string | null;
2204
+ /**
2205
+ * Capture baseline for a file BEFORE it's modified.
2206
+ * Call this before any write/delete operation.
2207
+ * Returns true if baseline was captured, false if already tracked or limit reached.
2208
+ */
2209
+ beforeWrite(filePath: string): Promise<boolean>;
2210
+ /**
2211
+ * Get list of files being tracked in current turn
2212
+ */
2213
+ getTrackedFiles(): string[];
2214
+ /**
2215
+ * Check if a specific file is being tracked
2216
+ */
2217
+ isTracking(filePath: string): boolean;
2218
+ /**
2219
+ * Get unified diff for all changes in current turn.
2220
+ * Can be called before endTurn() for live preview.
2221
+ */
2222
+ getDiff(): Promise<string | null>;
2223
+ /**
2224
+ * Get diff for a single file
2225
+ */
2226
+ getFileDiff(filePath: string): Promise<string | null>;
2227
+ /**
2228
+ * Undo all changes from current turn - restore all files to baseline state
2229
+ */
2230
+ undoTurn(): Promise<UndoResult>;
2231
+ /**
2232
+ * Undo changes to specific files only
2233
+ */
2234
+ undoFiles(filePaths: string[]): Promise<UndoResult>;
2235
+ /** Capture current state of a file as baseline */
2236
+ private captureBaseline;
2237
+ /** Restore a file to its baseline state */
2238
+ private restoreFile;
2239
+ /** Compute change info for a single file */
2240
+ private computeFileChange;
2241
+ /** Generate unified diff between old and new content */
2242
+ private generateDiff;
2243
+ /** Simple unified diff implementation */
2244
+ private simpleDiff;
2245
+ /**
2246
+ * Generate diff using git (placeholder for future enhancement)
2247
+ *
2248
+ * Note: git diff --no-index requires two files on disk, which makes
2249
+ * it tricky to use with in-memory content. For now, we always use
2250
+ * the simple diff implementation. A future enhancement could write
2251
+ * temp files or use a proper diff library like 'diff' from npm.
2252
+ */
2253
+ private gitDiff;
2254
+ /** Check if we're in a git repository */
2255
+ private isInGitRepo;
2256
+ /** Count additions and deletions from a diff string */
2257
+ private countDiffStats;
2258
+ /** Hash content for quick comparison */
2259
+ private hashContent;
2260
+ }
2261
+ /**
2262
+ * Create a new turn change tracker
2263
+ */
2264
+ declare function createTurnTracker(config: TurnTrackerConfig): TurnChangeTracker;
2265
+ /**
2266
+ * Extract file paths from tool arguments based on metadata
2267
+ */
2268
+ declare function extractFilePathsFromArgs(args: Record<string, unknown>, meta: FileOperationMeta): string[];
2269
+ /**
2270
+ * Check if a tool operation should trigger baseline capture
2271
+ */
2272
+ declare function shouldCaptureBaseline(meta: FileOperationMeta): boolean;
2273
+ /**
2274
+ * Wrap a tool execute function with automatic file tracking
2275
+ */
2276
+ declare function withFileTracking<T extends (...args: unknown[]) => Promise<unknown>>(tracker: TurnChangeTracker, meta: FileOperationMeta, execute: T): T;
2277
+
2278
+ /**
2279
+ * Prompt Templates — Base system prompts per model family
2280
+ *
2281
+ * agent-core provides **generic, domain-agnostic** templates.
2282
+ * These establish the agent's general approach and tool-use discipline
2283
+ * without assuming a specific domain (coding, browsing, etc.).
2284
+ *
2285
+ * Domain-specific templates belong in consumer packages:
2286
+ * - agent-code → coding templates ("read files before editing", etc.)
2287
+ * - computer-agent → computer-use templates
2288
+ * - your-custom-agent → whatever you need
2289
+ *
2290
+ * Consumer packages inject their templates via:
2291
+ * createPromptBuilder({ baseTemplate: MY_CODING_TEMPLATE })
2292
+ * builder.addSection({ id: "coding", ... })
2293
+ *
2294
+ * This file also contains model family detection, which IS infrastructure
2295
+ * and belongs here — any consumer benefits from knowing the model family
2296
+ * to optimize prompt formatting.
2297
+ */
2298
+
2299
+ /**
2300
+ * Detect which model family a LanguageModel belongs to.
2301
+ *
2302
+ * Uses the provider string first (most reliable), then falls back
2303
+ * to pattern matching on the model ID.
2304
+ *
2305
+ * @example
2306
+ * ```typescript
2307
+ * import { anthropic } from "@ai-sdk/anthropic";
2308
+ * detectModelFamily(anthropic("claude-sonnet-4-20250514")); // "anthropic"
2309
+ *
2310
+ * import { openai } from "@ai-sdk/openai";
2311
+ * detectModelFamily(openai("gpt-4o")); // "openai"
2312
+ * ```
2313
+ */
2314
+ declare function detectModelFamily(model: LanguageModel): ModelFamily;
2315
+ /**
2316
+ * Get the base template for a model family.
2317
+ *
2318
+ * @param family - Model family identifier
2319
+ * @returns The generic system prompt template
2320
+ */
2321
+ declare function getTemplate(family: ModelFamily): string;
2322
+ /**
2323
+ * Get all available template families.
2324
+ * Useful for debugging or letting users choose.
2325
+ */
2326
+ declare function getAvailableFamilies(): ModelFamily[];
2327
+
2328
+ /**
2329
+ * Environment Block — Runtime context for the system prompt
2330
+ *
2331
+ * Gathers information about the working environment (cwd, platform,
2332
+ * git status, date, shell) and formats it as a structured block
2333
+ * that gets injected into the prompt at priority 20.
2334
+ *
2335
+ * This gives the model awareness of:
2336
+ * - Where it's working (directory, project)
2337
+ * - What platform it's on (affects path separators, commands)
2338
+ * - Current time (useful for time-sensitive tasks)
2339
+ * - Git state (branch, clean/dirty)
2340
+ */
2341
+
2342
+ /**
2343
+ * Gather runtime environment information.
2344
+ *
2345
+ * Collects cwd, platform, git status, date, and shell.
2346
+ * Git commands use a short timeout to avoid hanging on slow repos.
2347
+ *
2348
+ * @param cwd - Working directory to gather info for
2349
+ * @returns Environment info object
2350
+ */
2351
+ declare function gatherEnvironment(cwd: string): EnvironmentInfo;
2352
+ /**
2353
+ * Format environment info as a prompt-friendly block.
2354
+ *
2355
+ * Output example:
2356
+ * ```
2357
+ * <workspace>
2358
+ * Directory: /Users/dev/my-project
2359
+ * Project: my-project
2360
+ * Platform: macOS (darwin arm64)
2361
+ * Date: Saturday, February 8, 2026
2362
+ * Shell: /bin/zsh
2363
+ * Git: main (clean)
2364
+ * </workspace>
2365
+ * ```
2366
+ */
2367
+ declare function formatEnvironment(info: EnvironmentInfo): string;
2368
+ /**
2369
+ * Create a short one-line summary of the environment.
2370
+ * Useful for compact contexts or logging.
2371
+ */
2372
+ declare function summarizeEnvironment(info: EnvironmentInfo): string;
2373
+
2374
+ /**
2375
+ * Instruction File Discovery — Project rules from the filesystem
2376
+ *
2377
+ * Discovers instruction files (AGENTS.md, CLAUDE.md, COPILOT.md, etc.)
2378
+ * by walking up the directory tree from cwd. These files provide
2379
+ * project-specific or workspace-level guidance to the agent.
2380
+ *
2381
+ * Discovery order (general → specific):
2382
+ * 1. Global instruction files (from config)
2383
+ * 2. Workspace-level files (found near git root or higher directories)
2384
+ * 3. Project-level files (found in or near cwd)
2385
+ *
2386
+ * All discovered files are included, ordered from most general to most
2387
+ * specific. This means project-level instructions appear last and
2388
+ * effectively take precedence in case of conflicting guidance.
2389
+ *
2390
+ * Compatible with:
2391
+ * - OpenCode's AGENTS.md convention
2392
+ * - Claude's CLAUDE.md convention
2393
+ * - GitHub Copilot's COPILOT.md convention
2394
+ * - CuyLabs' own .cuylabs/instructions.md
2395
+ */
2396
+
2397
+ /**
2398
+ * Default file patterns to search for.
2399
+ * Checked in each directory when walking up from cwd.
2400
+ */
2401
+ declare const DEFAULT_INSTRUCTION_PATTERNS: string[];
2402
+ /**
2403
+ * Maximum file size for instruction files (50KB).
2404
+ * Files larger than this are skipped to prevent prompt bloat.
2405
+ */
2406
+ declare const DEFAULT_MAX_FILE_SIZE = 51200;
2407
+ /**
2408
+ * Maximum directories to walk up from cwd.
2409
+ */
2410
+ declare const DEFAULT_MAX_DEPTH = 10;
2411
+ /**
2412
+ * Discover instruction files by walking up the directory tree from cwd.
2413
+ *
2414
+ * Searches for files matching the given patterns in each directory,
2415
+ * starting from `cwd` and walking up to the filesystem root (or until
2416
+ * `maxDepth` is reached).
2417
+ *
2418
+ * Results are ordered from **most general** (shallowest/root-level) to
2419
+ * **most specific** (deepest/cwd-level), so that specific project rules
2420
+ * appear last and take natural precedence.
2421
+ *
2422
+ * @param cwd - Starting directory for the search
2423
+ * @param patterns - File name patterns to look for (e.g. ["AGENTS.md"])
2424
+ * @param options - Discovery options
2425
+ * @returns Array of discovered instruction files, general → specific
2426
+ *
2427
+ * @example
2428
+ * ```typescript
2429
+ * const files = await discoverInstructions("/Users/dev/project/src", [
2430
+ * "AGENTS.md",
2431
+ * "CLAUDE.md",
2432
+ * ]);
2433
+ *
2434
+ * // Might find:
2435
+ * // 1. /Users/dev/project/AGENTS.md (depth: 1, workspace)
2436
+ * // 2. /Users/dev/project/src/AGENTS.md (depth: 0, project)
2437
+ * ```
2438
+ */
2439
+ declare function discoverInstructions(cwd: string, patterns?: string[], options?: {
2440
+ maxDepth?: number;
2441
+ maxFileSize?: number;
2442
+ stopAtGitRoot?: boolean;
2443
+ }): Promise<InstructionFile[]>;
2444
+ /**
2445
+ * Load global instruction files from explicit paths.
2446
+ *
2447
+ * Global instructions are always included regardless of cwd.
2448
+ * Useful for organization-wide coding standards or team rules.
2449
+ *
2450
+ * @param paths - Absolute paths to global instruction files
2451
+ * @param maxSize - Maximum file size in bytes
2452
+ * @returns Array of loaded instruction files
2453
+ */
2454
+ declare function loadGlobalInstructions(paths: string[], maxSize?: number): Promise<InstructionFile[]>;
2455
+ /**
2456
+ * Format discovered instruction files into a prompt block.
2457
+ *
2458
+ * Each file is wrapped in a tagged block with its source path
2459
+ * for transparency. Files are already ordered general → specific.
2460
+ *
2461
+ * Output example:
2462
+ * ```
2463
+ * <project-instructions>
2464
+ *
2465
+ * <instructions source="AGENTS.md" scope="workspace">
2466
+ * ... workspace-level content ...
2467
+ * </instructions>
2468
+ *
2469
+ * <instructions source="src/AGENTS.md" scope="project">
2470
+ * ... project-specific content ...
2471
+ * </instructions>
2472
+ *
2473
+ * </project-instructions>
2474
+ * ```
2475
+ */
2476
+ declare function formatInstructions(files: InstructionFile[], basePath?: string): string;
2477
+
2478
+ /**
2479
+ * PromptBuilder — The pipeline orchestrator
2480
+ *
2481
+ * Assembles a system prompt from composable layers:
2482
+ *
2483
+ * ┌──────────────────┐
2484
+ * │ Base Template │ priority 10 — model-family-specific guidelines
2485
+ * ├──────────────────┤
2486
+ * │ Environment │ priority 20 — cwd, platform, git, date
2487
+ * ├──────────────────┤
2488
+ * │ Instructions │ priority 30 — AGENTS.md, CLAUDE.md files
2489
+ * ├──────────────────┤
2490
+ * │ Custom Sections │ priority 50 — user-defined content
2491
+ * ├──────────────────┤
2492
+ * │ (Skills slot) │ priority 70 — reserved for future skill injection
2493
+ * ├──────────────────┤
2494
+ * │ Per-Turn Override │ priority 90 — session-specific context
2495
+ * └──────────────────┘
2496
+ *
2497
+ * Each layer is optional. Sections are sorted by priority (lower = earlier)
2498
+ * and composed into a single string.
2499
+ *
2500
+ * @example
2501
+ * ```typescript
2502
+ * import { createPromptBuilder } from "@cuylabs/agent-core";
2503
+ * import { anthropic } from "@ai-sdk/anthropic";
2504
+ *
2505
+ * const builder = createPromptBuilder({
2506
+ * includeEnvironment: true,
2507
+ * includeInstructions: true,
2508
+ * });
2509
+ *
2510
+ * const prompt = await builder.build({
2511
+ * cwd: "/path/to/project",
2512
+ * model: anthropic("claude-sonnet-4-20250514"),
2513
+ * });
2514
+ * ```
2515
+ */
2516
+
2517
+ /** Priority for the base template section */
2518
+ declare const PRIORITY_BASE = 10;
2519
+ /** Priority for the environment block section */
2520
+ declare const PRIORITY_ENVIRONMENT = 20;
2521
+ /** Priority for instruction files section */
2522
+ declare const PRIORITY_INSTRUCTIONS = 30;
2523
+ /** Priority for custom user-defined sections (default) */
2524
+ declare const PRIORITY_CUSTOM = 50;
2525
+ /** Priority reserved for future skill injections */
2526
+ declare const PRIORITY_SKILLS = 70;
2527
+ /** Priority for per-turn override content */
2528
+ declare const PRIORITY_OVERRIDE = 90;
2529
+ /**
2530
+ * PromptBuilder composes system prompts from layered sections.
2531
+ *
2532
+ * It handles:
2533
+ * - Automatic model family detection and template selection
2534
+ * - Runtime environment injection (cwd, git, platform)
2535
+ * - Instruction file discovery (AGENTS.md, etc.)
2536
+ * - Custom section management (add/remove/toggle)
2537
+ * - Caching to avoid repeated filesystem I/O
2538
+ *
2539
+ * @example
2540
+ * ```typescript
2541
+ * const builder = new PromptBuilder();
2542
+ *
2543
+ * // Add a custom section
2544
+ * builder.addSection({
2545
+ * id: "safety",
2546
+ * label: "Safety Rules",
2547
+ * content: "Never delete files without explicit confirmation.",
2548
+ * priority: 40,
2549
+ * });
2550
+ *
2551
+ * const prompt = await builder.build({
2552
+ * cwd: "/my/project",
2553
+ * model: myModel,
2554
+ * });
2555
+ * ```
2556
+ */
2557
+ declare class PromptBuilder {
2558
+ private readonly config;
2559
+ /** User-added sections (persist across builds) */
2560
+ private customSections;
2561
+ /** Cached environment info */
2562
+ private envCache?;
2563
+ /** Cached instruction files */
2564
+ private instructionCache?;
2565
+ constructor(config?: PromptConfig);
2566
+ /**
2567
+ * Build the complete system prompt from all active layers.
2568
+ *
2569
+ * This is the main entry point. Call it before each LLM request
2570
+ * to get a fully composed system prompt with all context.
2571
+ *
2572
+ * Sections are sorted by priority (lower = earlier) and joined
2573
+ * with the configured separator.
2574
+ *
2575
+ * @param context - Build context with cwd, model, and optional override
2576
+ * @returns The composed system prompt string
2577
+ */
2578
+ build(context: PromptBuildContext): Promise<string>;
2579
+ /**
2580
+ * Add or replace a custom section.
2581
+ *
2582
+ * Custom sections persist across builds. Use this to inject
2583
+ * context that should be present in every prompt (e.g. team rules,
2584
+ * project constraints, skill content).
2585
+ *
2586
+ * @param section - The section to add
2587
+ *
2588
+ * @example
2589
+ * ```typescript
2590
+ * builder.addSection({
2591
+ * id: "no-delete",
2592
+ * label: "File Safety",
2593
+ * content: "Never delete files without asking the user first.",
2594
+ * priority: 40,
2595
+ * });
2596
+ * ```
2597
+ */
2598
+ addSection(section: PromptSection): void;
2599
+ /**
2600
+ * Remove a custom section by its ID.
2601
+ *
2602
+ * @param id - Section ID to remove
2603
+ * @returns true if a section was removed
2604
+ */
2605
+ removeSection(id: string): boolean;
2606
+ /**
2607
+ * Toggle a custom section on or off without removing it.
2608
+ *
2609
+ * @param id - Section ID to toggle
2610
+ * @param enabled - Whether the section should be active
2611
+ */
2612
+ toggleSection(id: string, enabled: boolean): void;
2613
+ /**
2614
+ * Get all currently registered sections (including auto-generated labels).
2615
+ * Useful for debugging or building UIs that show prompt composition.
2616
+ *
2617
+ * @returns Array of all custom sections
2618
+ */
2619
+ getSections(): PromptSection[];
2620
+ /**
2621
+ * Check if a section with the given ID exists.
2622
+ */
2623
+ hasSection(id: string): boolean;
2624
+ /**
2625
+ * Clear all cached data (environment info, discovered instructions).
2626
+ *
2627
+ * Call this when:
2628
+ * - The working directory changes
2629
+ * - Instruction files are modified
2630
+ * - You want to force a refresh on the next build()
2631
+ */
2632
+ clearCache(): void;
2633
+ /**
2634
+ * Get the model family that would be used for a given model.
2635
+ * Useful for debugging template selection.
2636
+ */
2637
+ getModelFamily(model: PromptBuildContext["model"]): ModelFamily;
2638
+ /**
2639
+ * Get the base template that would be used for a given model.
2640
+ */
2641
+ getBaseTemplate(model: PromptBuildContext["model"]): string;
2642
+ /**
2643
+ * Preview the sections that would be generated for a given context.
2644
+ * Returns section metadata without the full content (for logging/debugging).
2645
+ */
2646
+ preview(context: PromptBuildContext): Promise<Array<{
2647
+ id: string;
2648
+ label: string;
2649
+ priority: number;
2650
+ size: number;
2651
+ }>>;
2652
+ /**
2653
+ * Get environment info, using cache if available for the same cwd.
2654
+ */
2655
+ private getEnvironment;
2656
+ /**
2657
+ * Get instruction files, using cache if available for the same cwd.
2658
+ */
2659
+ private getInstructions;
2660
+ }
2661
+ /**
2662
+ * Create a new PromptBuilder instance.
2663
+ *
2664
+ * This is the recommended way to create a builder. It accepts
2665
+ * an optional configuration and returns a ready-to-use instance.
2666
+ *
2667
+ * @param config - Optional pipeline configuration
2668
+ * @returns A new PromptBuilder
2669
+ *
2670
+ * @example
2671
+ * ```typescript
2672
+ * // Full pipeline with defaults
2673
+ * const builder = createPromptBuilder();
2674
+ *
2675
+ * // Custom configuration
2676
+ * const builder = createPromptBuilder({
2677
+ * includeEnvironment: true,
2678
+ * includeInstructions: true,
2679
+ * modelFamily: "anthropic",
2680
+ * sections: [
2681
+ * { id: "team", label: "Team Rules", content: "Use TypeScript strict mode." },
2682
+ * ],
2683
+ * });
2684
+ *
2685
+ * // Build a prompt
2686
+ * const systemPrompt = await builder.build({
2687
+ * cwd: process.cwd(),
2688
+ * model: myModel,
2689
+ * });
2690
+ * ```
2691
+ */
2692
+ declare function createPromptBuilder(config?: PromptConfig): PromptBuilder;
2693
+
2694
+ /**
2695
+ * Intervention Controller for @cuylabs/agent-core
2696
+ *
2697
+ * Manages mid-turn message injection into running agent turns.
2698
+ * Uses Vercel AI SDK v6's `prepareStep` hook for zero-overhead
2699
+ * integration at step boundaries (between LLM calls in a multi-step turn).
2700
+ *
2701
+ * Instead of polling a queue after each tool execution (like pi-mono's
2702
+ * "steering"), this leverages the SDK's native step lifecycle. The
2703
+ * `prepareStep` callback fires before every LLM call, giving us a
2704
+ * natural injection point with no custom loop machinery.
2705
+ *
2706
+ * Two injection modes:
2707
+ * - **Immediate**: `intervene(msg)` — injected at the next step boundary
2708
+ * - **Deferred**: `queueNext(msg)` — held until the turn completes
2709
+ */
2710
+ /** A queued intervention message waiting to be applied */
2711
+ interface PendingIntervention {
2712
+ /** Unique identifier for tracking */
2713
+ readonly id: string;
2714
+ /** The user message to inject */
2715
+ readonly message: string;
2716
+ /** When the intervention was created */
2717
+ readonly createdAt: Date;
2718
+ }
2719
+ /**
2720
+ * Callback fired when an intervention is applied to a step.
2721
+ *
2722
+ * The agent uses this to push `intervention-applied` events into
2723
+ * the streaming event queue so they reach the consumer in the
2724
+ * correct order relative to other events.
2725
+ */
2726
+ type OnInterventionApplied = (intervention: PendingIntervention) => void;
2727
+ /**
2728
+ * Manages mid-turn message injection for agents.
2729
+ *
2730
+ * This is the core primitive that connects user redirection intent
2731
+ * to the AI SDK's multi-step loop. The flow:
2732
+ *
2733
+ * 1. Consumer calls `agent.intervene("focus on auth.ts")` from any async context
2734
+ * 2. The message is queued as a `PendingIntervention`
2735
+ * 3. At the next step boundary, `prepareStep` drains the queue
2736
+ * 4. The message is appended to the LLM's input messages
2737
+ * 5. The LLM responds to the redirect naturally
2738
+ *
2739
+ * Thread safety: All operations are synchronous and run on the
2740
+ * same event loop. No locks needed — Node.js guarantees ordering.
2741
+ *
2742
+ * @example
2743
+ * ```typescript
2744
+ * const ctrl = new InterventionController();
2745
+ *
2746
+ * // Queue an intervention (from a UI handler, WebSocket, etc.)
2747
+ * const id = ctrl.intervene("stop refactoring, fix the failing test first");
2748
+ *
2749
+ * // Later, in prepareStep:
2750
+ * const pending = ctrl.drainImmediate();
2751
+ * // → [{ id, message: "stop refactoring...", createdAt }]
2752
+ * ```
2753
+ */
2754
+ declare class InterventionController {
2755
+ /** Immediate interventions — applied at the next step boundary */
2756
+ private immediate;
2757
+ /** Deferred messages — held until the turn completes */
2758
+ private deferred;
2759
+ /**
2760
+ * Callback fired when an intervention is wired into a step.
2761
+ * Set by the Agent before starting a chat turn, cleared after.
2762
+ */
2763
+ onApplied?: OnInterventionApplied;
2764
+ /**
2765
+ * Inject a message at the next LLM step boundary.
2766
+ *
2767
+ * The message is appended as a user message to the conversation
2768
+ * before the next LLM call in the current multi-step turn. The
2769
+ * LLM will see it and can adjust its behavior accordingly.
2770
+ *
2771
+ * Safe to call from any async context while `chat()` is running.
2772
+ * If called when no turn is active, the message will be picked up
2773
+ * by the first step of the next `chat()` call.
2774
+ *
2775
+ * @param message - The user message to inject
2776
+ * @returns Intervention ID for tracking
2777
+ */
2778
+ intervene(message: string): string;
2779
+ /**
2780
+ * Drain and return all pending immediate interventions.
2781
+ * The internal queue is cleared atomically.
2782
+ *
2783
+ * @internal Called by the LLM stream's `prepareStep` hook
2784
+ */
2785
+ drainImmediate(): PendingIntervention[];
2786
+ /** Whether there are pending immediate interventions */
2787
+ get hasPending(): boolean;
2788
+ /** Number of pending immediate interventions */
2789
+ get pendingCount(): number;
2790
+ /**
2791
+ * Queue a message for after the current turn completes.
2792
+ *
2793
+ * Unlike `intervene()`, this does **not** inject mid-turn. The
2794
+ * message is held and available via `drainDeferred()` after
2795
+ * `chat()` finishes. The consumer decides whether to send it
2796
+ * as a new turn.
2797
+ *
2798
+ * @param message - The message to queue
2799
+ * @returns Intervention ID for tracking
2800
+ */
2801
+ queueNext(message: string): string;
2802
+ /**
2803
+ * Drain and return all deferred messages.
2804
+ * The internal queue is cleared atomically.
2805
+ */
2806
+ drainDeferred(): PendingIntervention[];
2807
+ /** Whether there are deferred messages */
2808
+ get hasDeferred(): boolean;
2809
+ /** Number of deferred messages */
2810
+ get deferredCount(): number;
2811
+ /** Clear all queues (immediate + deferred) */
2812
+ clear(): void;
2813
+ /** Reset the controller for a new turn (clears onApplied, keeps queues) */
2814
+ resetCallbacks(): void;
2815
+ }
2816
+
2817
+ /**
2818
+ * Sub-agent run result
2819
+ */
2820
+ interface SubAgentResult {
2821
+ /** Final response text */
2822
+ response: string;
2823
+ /** Sub-agent session ID */
2824
+ sessionId: string;
2825
+ /** Token usage */
2826
+ usage: TokenUsage;
2827
+ /** Tool calls made */
2828
+ toolCalls: Array<{
2829
+ name: string;
2830
+ result: unknown;
2831
+ }>;
2832
+ }
2833
+ /**
2834
+ * Create an agent instance
2835
+ */
2836
+ declare function createAgent(config: AgentConfig & {
2837
+ tools?: Tool$1.AnyInfo[];
2838
+ sessionManager?: SessionManager;
2839
+ }): Agent;
2840
+ /**
2841
+ * Run multiple sub-agent tasks concurrently.
2842
+ *
2843
+ * Useful for parallelizing independent research or analysis tasks.
2844
+ * Results are returned in the same order as tasks.
2845
+ *
2846
+ * **IMPORTANT: Concurrency Considerations**
2847
+ *
2848
+ * When multiple tasks use the same `Agent` instance, they share the same
2849
+ * `SessionManager`. To avoid state conflicts:
2850
+ *
2851
+ * 1. Use distinct `parentSessionId` values for each task
2852
+ * 2. Or create separate `Agent` instances via `agent.fork()` for each task
2853
+ * 3. Or ensure tasks use unique session IDs (auto-generated if not specified)
2854
+ *
2855
+ * Tasks modifying the same session concurrently may cause race conditions.
2856
+ *
2857
+ * @example
2858
+ * ```typescript
2859
+ * const explorer = agent.fork({ name: "explore", ... });
2860
+ *
2861
+ * const results = await runConcurrent([
2862
+ * { agent: explorer, message: "Find all API endpoints" },
2863
+ * { agent: explorer, message: "Find all database queries" },
2864
+ * { agent: explorer, message: "Find all auth middleware" },
2865
+ * ], { maxConcurrency: 3 });
2866
+ * ```
2867
+ */
2868
+ declare function runConcurrent(tasks: Array<{
2869
+ agent: Agent;
2870
+ message: string;
2871
+ parentSessionId?: string;
2872
+ title?: string;
2873
+ }>, options?: {
2874
+ /** Maximum concurrent tasks (default: 5) */
2875
+ maxConcurrency?: number;
2876
+ /** Abort signal for all tasks */
2877
+ abort?: AbortSignal;
2878
+ }): Promise<SubAgentResult[]>;
2879
+ /**
2880
+ * Embeddable AI agent with tool support.
2881
+ *
2882
+ * The `Agent` class is the main entry-point for consumers. It manages:
2883
+ * - **Sessions** — persistent conversation history
2884
+ * - **Streaming** — real-time event generation via `chat()`
2885
+ * - **Tools** — registry + execution context
2886
+ * - **Context management** — overflow detection & auto-compaction
2887
+ * - **Reasoning** — configurable thinking levels per model
2888
+ * - **Sub-agents** — `fork()` / `run()` for parallel or specialised tasks
2889
+ *
2890
+ * @example
2891
+ * ```typescript
2892
+ * import { createAgent } from "@cuylabs/agent-core";
2893
+ * import { anthropic } from "@ai-sdk/anthropic";
2894
+ *
2895
+ * const agent = createAgent({
2896
+ * model: anthropic("claude-sonnet-4-20250514"),
2897
+ * cwd: "/path/to/project",
2898
+ * });
2899
+ *
2900
+ * // Streaming
2901
+ * for await (const event of agent.chat("session-1", "List all TypeScript files")) {
2902
+ * if (event.type === "text-delta") process.stdout.write(event.text);
2903
+ * }
2904
+ *
2905
+ * // Non-streaming (convenience)
2906
+ * const result = await agent.send("session-1", "Fix the bug in utils.ts");
2907
+ * console.log(result.response);
2908
+ * ```
2909
+ */
2910
+ declare class Agent {
2911
+ private config;
2912
+ private tools;
2913
+ private sessions;
2914
+ private loadedSessions;
2915
+ private state;
2916
+ /** Context manager for overflow detection and compaction */
2917
+ private contextManager;
2918
+ /** Tools that user said "remember" for doom loop (don't ask again) */
2919
+ private rememberedDoomLoopTools;
2920
+ /** Turn change tracker for file modification tracking */
2921
+ private turnTracker;
2922
+ /** Counter for turn IDs */
2923
+ private turnCounter;
2924
+ /** MCP manager for external tool servers */
2925
+ private mcpManager?;
2926
+ /** Whether MCP has been connected (lazy init) */
2927
+ private mcpConnected;
2928
+ /** Cached MCP tools (refreshed on connect) */
2929
+ private mcpToolsCache?;
2930
+ /** Prompt pipeline builder (when using layered prompt mode) */
2931
+ private promptBuilder?;
2932
+ /** Intervention controller for mid-turn message injection */
2933
+ private interventionCtrl;
2934
+ /** Execution environment for tool operations */
2935
+ private host;
2936
+ constructor(config: AgentConfig & {
2937
+ tools?: Tool$1.AnyInfo[];
2938
+ sessionManager?: SessionManager;
2939
+ });
2940
+ /** Working directory for file operations */
2941
+ get cwd(): string;
2942
+ /** Current model */
2943
+ get model(): LanguageModel;
2944
+ /** System prompt */
2945
+ get systemPrompt(): string;
2946
+ /** Is currently streaming */
2947
+ get isStreaming(): boolean;
2948
+ /** Current reasoning level */
2949
+ get reasoningLevel(): ReasoningLevel;
2950
+ /**
2951
+ * Set reasoning level
2952
+ * Will be clamped to available levels for the current model
2953
+ */
2954
+ setReasoningLevel(level: ReasoningLevel): void;
2955
+ /**
2956
+ * Get available reasoning levels for the current model
2957
+ */
2958
+ getAvailableReasoningLevels(): ReasoningLevel[];
2959
+ /**
2960
+ * Check if current model supports reasoning
2961
+ */
2962
+ supportsReasoning(): boolean;
2963
+ /**
2964
+ * Ensure MCP is connected and return tools
2965
+ * Lazy initialization - only connects on first use
2966
+ */
2967
+ private ensureMCPConnected;
2968
+ /**
2969
+ * Repair session history by synthesising missing tool-result messages.
2970
+ *
2971
+ * When a tool `execute()` throws, the AI SDK emits `tool-error` instead
2972
+ * of `tool-result`. If the error event wasn't persisted (e.g. because
2973
+ * the fix above wasn't in place), subsequent turns will fail with
2974
+ * `MissingToolResultsError`. This method detects orphaned tool-call IDs
2975
+ * and adds placeholder `tool` messages so the history is valid again.
2976
+ */
2977
+ private repairOrphanedToolCalls;
2978
+ /**
2979
+ * Convert internal {@link Message} array to Vercel AI SDK {@link ModelMessage} format.
2980
+ *
2981
+ * Handles the role-specific mappings:
2982
+ * - `user` / `system` → pass-through
2983
+ * - `assistant` with tool calls → `ToolCallPart[]` content
2984
+ * - `tool` → `tool-result` content part
2985
+ */
2986
+ private toModelMessages;
2987
+ /**
2988
+ * Stream a chat response.
2989
+ *
2990
+ * Sends a user message and yields real-time {@link AgentEvent}s as the
2991
+ * model responds, calls tools, and finishes. Conversation state is
2992
+ * persisted to the session automatically.
2993
+ *
2994
+ * @param sessionId - Session identifier for conversation history
2995
+ * @param message - User message to send
2996
+ * @param options - Abort signal, system prompt override, and approval handler
2997
+ * @yields {AgentEvent} Events as they occur during processing
2998
+ */
2999
+ chat(sessionId: string, message: string, options?: {
3000
+ abort?: AbortSignal;
3001
+ system?: string;
3002
+ onApproval?: (request: {
3003
+ tool: string;
3004
+ args: unknown;
3005
+ description: string;
3006
+ }) => Promise<"allow" | "deny">;
3007
+ }): AsyncGenerator<AgentEvent>;
3008
+ /**
3009
+ * Ensure a session is loaded or created
3010
+ */
3011
+ private ensureSession;
3012
+ /**
3013
+ * Send a message and wait for the complete response (non-streaming).
3014
+ *
3015
+ * This is a convenience wrapper around {@link chat} that buffers all
3016
+ * events internally and returns the final response, usage, and tool
3017
+ * call results. Prefer `chat()` when you need real-time streaming.
3018
+ *
3019
+ * @param sessionId - Session identifier
3020
+ * @param message - User message
3021
+ * @param options - Abort signal and optional system prompt override
3022
+ */
3023
+ send(sessionId: string, message: string, options?: {
3024
+ abort?: AbortSignal;
3025
+ system?: string;
3026
+ }): Promise<{
3027
+ response: string;
3028
+ usage: TokenUsage;
3029
+ toolCalls: Array<{
3030
+ name: string;
3031
+ result: unknown;
3032
+ }>;
3033
+ }>;
3034
+ /** Add a tool at runtime */
3035
+ addTool(tool: Tool$1.Info): void;
3036
+ /** Remove a tool by ID */
3037
+ removeTool(toolId: string): boolean;
3038
+ /** Get all tool IDs */
3039
+ getToolIds(): string[];
3040
+ /** Get all tools */
3041
+ getTools(): Tool$1.AnyInfo[];
3042
+ /** Check if a tool exists */
3043
+ hasTool(toolId: string): boolean;
3044
+ /** Get session manager */
3045
+ getSessionManager(): SessionManager;
3046
+ /** Get current session context */
3047
+ getSessionContext(): SessionContext;
3048
+ /** Get messages from current session */
3049
+ getMessages(): Message[];
3050
+ /** Delete a session */
3051
+ deleteSession(sessionId: string): Promise<boolean>;
3052
+ /** List all sessions */
3053
+ listSessions(): Promise<SessionInfo[]>;
3054
+ /** Branch from current point */
3055
+ branch(summary?: string): Promise<string>;
3056
+ /**
3057
+ * Get context window statistics.
3058
+ *
3059
+ * Useful for UI to display context utilization like:
3060
+ * - Progress bar showing how full the context is
3061
+ * - Warning when approaching limit
3062
+ * - Info about when compaction will trigger
3063
+ *
3064
+ * @example
3065
+ * ```typescript
3066
+ * const stats = agent.getContextStats();
3067
+ * console.log(`Context: ${stats.utilizationPercent}% used`);
3068
+ * if (stats.shouldPrune) {
3069
+ * console.log('Context will be compacted soon');
3070
+ * }
3071
+ * ```
3072
+ */
3073
+ getContextStats(): {
3074
+ tokens: number;
3075
+ limit: number;
3076
+ available: number;
3077
+ utilizationPercent: number;
3078
+ isOverflowing: boolean;
3079
+ shouldPrune: boolean;
3080
+ };
3081
+ /**
3082
+ * Manually trigger context compaction.
3083
+ *
3084
+ * Usually auto-compaction handles this, but you can trigger it manually
3085
+ * if needed (e.g., before a long operation).
3086
+ *
3087
+ * @returns Pruning result with details about what was removed/summarized
3088
+ */
3089
+ compactContext(): Promise<{
3090
+ removedCount: number;
3091
+ tokensRemoved: number;
3092
+ summarized: boolean;
3093
+ summary?: string;
3094
+ }>;
3095
+ /**
3096
+ * Clear remembered doom loop tools.
3097
+ *
3098
+ * When a user says "remember" for a doom loop, that tool is added
3099
+ * to a set that skips future doom loop checks. Call this to reset.
3100
+ */
3101
+ clearRememberedDoomLoopTools(): void;
3102
+ /**
3103
+ * Inject a message at the next step boundary of a running turn.
3104
+ *
3105
+ * This is the primary redirection mechanism. When the agent is
3106
+ * streaming a multi-step response (calling tools, reasoning, etc.),
3107
+ * this injects a user message before the next LLM call. The LLM
3108
+ * sees the intervention and can adjust its behavior.
3109
+ *
3110
+ * Uses Vercel AI SDK v6's `prepareStep` hook — no polling, no
3111
+ * custom loop code. The SDK handles the injection naturally.
3112
+ *
3113
+ * Safe to call from any async context (UI event handlers, WebSocket
3114
+ * callbacks, timers, etc.) while `chat()` is running.
3115
+ *
3116
+ * If called when no turn is active, the message will be picked up
3117
+ * by the first step of the next `chat()` call.
3118
+ *
3119
+ * @param message - The user message to inject mid-turn
3120
+ * @returns Intervention ID for tracking
3121
+ *
3122
+ * @example
3123
+ * ```typescript
3124
+ * // Start a streaming turn
3125
+ * const stream = agent.chat("session-1", "refactor the auth module");
3126
+ *
3127
+ * // From a UI handler (another async context):
3128
+ * agent.intervene("stop, focus only on JWT validation in auth.ts");
3129
+ *
3130
+ * // The stream will yield an intervention-applied event
3131
+ * for await (const event of stream) {
3132
+ * if (event.type === "intervention-applied") {
3133
+ * console.log(`Redirected: ${event.message}`);
3134
+ * }
3135
+ * }
3136
+ * ```
3137
+ */
3138
+ intervene(message: string): string;
3139
+ /**
3140
+ * Queue a message for after the current turn completes.
3141
+ *
3142
+ * Unlike `intervene()`, this does **not** interrupt the current turn.
3143
+ * The message is held and available via `drainQueuedNext()` after
3144
+ * `chat()` finishes. The consumer decides whether to send it as a
3145
+ * new turn.
3146
+ *
3147
+ * @param message - The message to queue
3148
+ * @returns Intervention ID for tracking
3149
+ *
3150
+ * @example
3151
+ * ```typescript
3152
+ * agent.queueNext("now run the test suite");
3153
+ *
3154
+ * for await (const event of agent.chat("s1", "fix the bug")) {
3155
+ * // ... handle events
3156
+ * }
3157
+ *
3158
+ * // After turn completes, check for queued messages
3159
+ * if (agent.hasQueuedNext()) {
3160
+ * const next = agent.drainQueuedNext();
3161
+ * for (const item of next) {
3162
+ * // Send as a new turn
3163
+ * for await (const ev of agent.chat("s1", item.message)) { ... }
3164
+ * }
3165
+ * }
3166
+ * ```
3167
+ */
3168
+ queueNext(message: string): string;
3169
+ /** Whether there are deferred messages queued for after the turn */
3170
+ hasQueuedNext(): boolean;
3171
+ /** Drain and return all deferred messages (clears the queue) */
3172
+ drainQueuedNext(): PendingIntervention[];
3173
+ /**
3174
+ * Get the raw intervention controller for advanced use cases.
3175
+ *
3176
+ * Use this when you need fine-grained control over the intervention
3177
+ * lifecycle (e.g., checking pending count, clearing queues, setting
3178
+ * custom `onApplied` callbacks).
3179
+ *
3180
+ * @internal
3181
+ */
3182
+ getInterventionController(): InterventionController;
3183
+ /**
3184
+ * Get the unified diff for all file changes in the current/last turn.
3185
+ *
3186
+ * @example
3187
+ * ```typescript
3188
+ * const diff = await agent.getTurnDiff();
3189
+ * if (diff) {
3190
+ * console.log('Changes this turn:\n', diff);
3191
+ * }
3192
+ * ```
3193
+ */
3194
+ getTurnDiff(): Promise<string | null>;
3195
+ /**
3196
+ * Get list of files being tracked in the current turn.
3197
+ */
3198
+ getTrackedFiles(): string[];
3199
+ /**
3200
+ * Undo all file changes from the current turn.
3201
+ * Restores files to their state before the turn started.
3202
+ *
3203
+ * @example
3204
+ * ```typescript
3205
+ * const result = await agent.undoTurn();
3206
+ * console.log(`Restored ${result.restored.length} files`);
3207
+ * if (result.failed.length > 0) {
3208
+ * console.log('Failed to restore:', result.failed);
3209
+ * }
3210
+ * ```
3211
+ */
3212
+ undoTurn(): Promise<UndoResult>;
3213
+ /**
3214
+ * Undo changes to specific files only.
3215
+ *
3216
+ * @param files - Array of file paths to restore (relative to cwd)
3217
+ */
3218
+ undoFiles(files: string[]): Promise<UndoResult>;
3219
+ /**
3220
+ * Check if currently tracking a turn.
3221
+ */
3222
+ isTrackingTurn(): boolean;
3223
+ /**
3224
+ * Get access to the raw turn tracker for advanced use cases.
3225
+ *
3226
+ * @internal
3227
+ */
3228
+ getTurnTracker(): TurnChangeTracker;
3229
+ /**
3230
+ * Update system prompt.
3231
+ *
3232
+ * If the agent is using the prompt pipeline, calling this switches
3233
+ * to flat string mode (disabling the pipeline). To modify the pipeline
3234
+ * instead, use `getPromptBuilder()`.
3235
+ */
3236
+ setSystemPrompt(prompt: string): void;
3237
+ /** Update working directory */
3238
+ setCwd(cwd: string): void;
3239
+ /** Update model */
3240
+ setModel(model: LanguageModel): void;
3241
+ /**
3242
+ * Get the prompt builder (when using pipeline mode).
3243
+ * Returns undefined if using flat string mode.
3244
+ *
3245
+ * Use this to add/remove sections, clear caches, or inspect
3246
+ * the prompt composition without switching to flat mode.
3247
+ *
3248
+ * @example
3249
+ * ```typescript
3250
+ * const builder = agent.getPromptBuilder();
3251
+ * if (builder) {
3252
+ * builder.addSection({
3253
+ * id: "task-context",
3254
+ * label: "Task Context",
3255
+ * content: "Focus on fixing authentication bugs.",
3256
+ * priority: 60,
3257
+ * });
3258
+ * }
3259
+ * ```
3260
+ */
3261
+ getPromptBuilder(): PromptBuilder | undefined;
3262
+ /**
3263
+ * Check if the agent is using the prompt pipeline.
3264
+ */
3265
+ isUsingPromptPipeline(): boolean;
3266
+ /**
3267
+ * Fork this agent to create a sub-agent with modified configuration.
3268
+ *
3269
+ * Sub-agents share the same session manager but can have different:
3270
+ * - System prompts (specialized for tasks)
3271
+ * - Tools (restricted subset)
3272
+ * - Models (cheaper/faster for simple tasks)
3273
+ * - Reasoning levels
3274
+ *
3275
+ * @example
3276
+ * ```typescript
3277
+ * // Create an exploration sub-agent with read-only tools
3278
+ * const explorer = agent.fork({
3279
+ * name: "explore",
3280
+ * systemPrompt: "You explore codebases. Only use read and search tools.",
3281
+ * tools: [readTool, grepTool, globTool],
3282
+ * });
3283
+ *
3284
+ * // Run the sub-agent
3285
+ * const result = await explorer.run({
3286
+ * parentSessionId: "main-session",
3287
+ * message: "Find all API endpoints",
3288
+ * });
3289
+ * ```
3290
+ */
3291
+ fork(options?: {
3292
+ /** Name for the sub-agent (used in session titles) */
3293
+ name?: string;
3294
+ /** Override system prompt (switches to flat string mode) */
3295
+ systemPrompt?: string;
3296
+ /** Override prompt pipeline configuration */
3297
+ prompt?: PromptConfig;
3298
+ /** Override model */
3299
+ model?: LanguageModel;
3300
+ /** Override tools (if not provided, inherits parent's tools) */
3301
+ tools?: Tool$1.AnyInfo[];
3302
+ /** Override max steps */
3303
+ maxSteps?: number;
3304
+ /** Override reasoning level */
3305
+ reasoningLevel?: ReasoningLevel;
3306
+ /** Apply a preset configuration */
3307
+ preset?: Preset;
3308
+ }): Agent;
3309
+ /**
3310
+ * Create a sub-agent with a preset configuration.
3311
+ *
3312
+ * Convenience method that applies a preset and returns a forked agent.
3313
+ *
3314
+ * @example
3315
+ * ```typescript
3316
+ * import { Presets } from "@cuylabs/agent-core";
3317
+ *
3318
+ * // Create an exploration sub-agent
3319
+ * const explorer = agent.withPreset(Presets.explore);
3320
+ * const result = await explorer.run({
3321
+ * message: "Find all API routes in this project",
3322
+ * });
3323
+ *
3324
+ * // Create a careful review sub-agent
3325
+ * const reviewer = agent.withPreset(Presets.review);
3326
+ * const review = await reviewer.run({
3327
+ * message: "Review src/auth.ts for security issues",
3328
+ * });
3329
+ * ```
3330
+ */
3331
+ withPreset(preset: Preset): Agent;
3332
+ /**
3333
+ * Run a task in an isolated sub-agent session.
3334
+ *
3335
+ * Creates a new session linked to the parent, runs the task to completion,
3336
+ * and returns the result. The sub-agent session is preserved for inspection.
3337
+ *
3338
+ * @example
3339
+ * ```typescript
3340
+ * const result = await agent.run({
3341
+ * parentSessionId: "main-session",
3342
+ * message: "Review this code for security issues",
3343
+ * title: "Security Review",
3344
+ * });
3345
+ *
3346
+ * console.log(result.response);
3347
+ * console.log(`Sub-agent session: ${result.sessionId}`);
3348
+ * ```
3349
+ */
3350
+ run(options: {
3351
+ /** Parent session ID (for linking) */
3352
+ parentSessionId?: string;
3353
+ /** The task/message to execute */
3354
+ message: string;
3355
+ /** Title for the sub-agent session */
3356
+ title?: string;
3357
+ /** Abort signal */
3358
+ abort?: AbortSignal;
3359
+ }): Promise<{
3360
+ /** Final response text */
3361
+ response: string;
3362
+ /** Sub-agent session ID */
3363
+ sessionId: string;
3364
+ /** Token usage */
3365
+ usage: TokenUsage;
3366
+ /** Tool calls made */
3367
+ toolCalls: Array<{
3368
+ name: string;
3369
+ result: unknown;
3370
+ }>;
3371
+ }>;
3372
+ /**
3373
+ * Get the MCP manager (if configured)
3374
+ */
3375
+ getMCP(): MCPManager | undefined;
3376
+ /**
3377
+ * Check if MCP is configured
3378
+ */
3379
+ hasMCP(): boolean;
3380
+ /**
3381
+ * Reconnect to MCP servers
3382
+ *
3383
+ * Use this after network issues or when server availability changes.
3384
+ * Clears tool cache and reconnects all servers.
3385
+ */
3386
+ reconnectMCP(): Promise<void>;
3387
+ /**
3388
+ * Close the agent and clean up resources
3389
+ *
3390
+ * Closes MCP connections if configured.
3391
+ * After calling close(), the agent should not be used.
3392
+ */
3393
+ close(): Promise<void>;
3394
+ }
3395
+
3396
+ /**
3397
+ * Error classification and handling for @cuylabs/agent-core
3398
+ *
3399
+ * Provides structured error types with retryability detection,
3400
+ * header parsing, and error categorization.
3401
+ */
3402
+ /**
3403
+ * Categories of errors that can occur during LLM operations
3404
+ */
3405
+ type ErrorCategory = "rate_limit" | "overloaded" | "auth" | "invalid_request" | "context_overflow" | "content_filter" | "network" | "timeout" | "cancelled" | "unknown";
3406
+ /**
3407
+ * Determines if an error category is typically retryable
3408
+ */
3409
+ declare function isRetryableCategory(category: ErrorCategory): boolean;
3410
+ /**
3411
+ * Headers from an API response
3412
+ */
3413
+ interface ResponseHeaders {
3414
+ "retry-after"?: string;
3415
+ "retry-after-ms"?: string;
3416
+ "x-ratelimit-remaining"?: string;
3417
+ "x-ratelimit-reset"?: string;
3418
+ [key: string]: string | undefined;
3419
+ }
3420
+ /**
3421
+ * Options for creating an LLMError
3422
+ */
3423
+ interface LLMErrorOptions {
3424
+ message: string;
3425
+ category?: ErrorCategory;
3426
+ status?: number;
3427
+ headers?: ResponseHeaders;
3428
+ cause?: Error;
3429
+ provider?: string;
3430
+ model?: string;
3431
+ }
3432
+ /**
3433
+ * Structured error for LLM operations
3434
+ */
3435
+ declare class LLMError extends Error {
3436
+ readonly category: ErrorCategory;
3437
+ readonly status?: number;
3438
+ readonly headers?: ResponseHeaders;
3439
+ readonly provider?: string;
3440
+ readonly model?: string;
3441
+ readonly isRetryable: boolean;
3442
+ readonly retryDelayMs?: number;
3443
+ constructor(options: LLMErrorOptions);
3444
+ /**
3445
+ * Create from an unknown error
3446
+ */
3447
+ static from(error: unknown, context?: Partial<LLMErrorOptions>): LLMError;
3448
+ /**
3449
+ * Human-readable description
3450
+ */
3451
+ get description(): string;
3452
+ }
3453
+ /**
3454
+ * Check if an error is retryable
3455
+ */
3456
+ declare function isRetryable(error: unknown): boolean;
3457
+ /**
3458
+ * Get retry delay for an error (returns undefined if not retryable)
3459
+ */
3460
+ declare function getRetryDelay(error: unknown): number | undefined;
3461
+ /**
3462
+ * Get error category
3463
+ */
3464
+ declare function getErrorCategory(error: unknown): ErrorCategory;
3465
+
3466
+ /**
3467
+ * Retry logic with exponential backoff for @cuylabs/agent-core
3468
+ *
3469
+ * Provides configurable retry behavior with header-aware delays,
3470
+ * exponential backoff, and abort signal support.
3471
+ */
3472
+
3473
+ /**
3474
+ * Retry configuration
3475
+ */
3476
+ interface RetryConfig {
3477
+ /** Maximum number of retry attempts (default: 3) */
3478
+ maxAttempts?: number;
3479
+ /** Initial delay in ms (default: 2000) */
3480
+ initialDelayMs?: number;
3481
+ /** Backoff multiplier (default: 2) */
3482
+ backoffFactor?: number;
3483
+ /** Maximum delay without headers in ms (default: 30000) */
3484
+ maxDelayMs?: number;
3485
+ /** Whether to jitter delays (default: true) */
3486
+ jitter?: boolean;
3487
+ /** Callback when retrying */
3488
+ onRetry?: (attempt: number, delayMs: number, error: LLMError) => void;
3489
+ }
3490
+ /**
3491
+ * Default retry configuration
3492
+ */
3493
+ declare const DEFAULT_RETRY_CONFIG: Required<Omit<RetryConfig, "onRetry">>;
3494
+ /**
3495
+ * Tracks retry state across attempts
3496
+ */
3497
+ interface RetryState {
3498
+ /** Current attempt number (1-indexed) */
3499
+ attempt: number;
3500
+ /** Total errors encountered */
3501
+ errors: LLMError[];
3502
+ /** Whether more retries are available */
3503
+ canRetry: boolean;
3504
+ /** Delay until next retry (if applicable) */
3505
+ nextDelayMs?: number;
3506
+ }
3507
+ /**
3508
+ * Creates initial retry state
3509
+ */
3510
+ declare function createRetryState(): RetryState;
3511
+ /**
3512
+ * Calculate delay for a retry attempt
3513
+ */
3514
+ declare function calculateDelay(attempt: number, error: LLMError | undefined, config: Required<Omit<RetryConfig, "onRetry">>): number;
3515
+ /**
3516
+ * Sleep for a duration, respecting abort signal
3517
+ */
3518
+ declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
3519
+ /**
3520
+ * Execute a function with retry logic
3521
+ */
3522
+ declare function withRetry<T>(fn: (attempt: number) => Promise<T>, config?: RetryConfig, signal?: AbortSignal): Promise<T>;
3523
+ /**
3524
+ * Options for retry handler
3525
+ */
3526
+ interface RetryHandlerOptions extends RetryConfig {
3527
+ /** Abort signal */
3528
+ signal?: AbortSignal;
3529
+ }
3530
+ /**
3531
+ * Creates a retry handler that wraps stream creation
3532
+ */
3533
+ declare function createRetryHandler(options?: RetryHandlerOptions): <T>(createStream: (attempt: number) => Promise<T>) => Promise<T>;
3534
+ /**
3535
+ * Checks if more retries should be attempted
3536
+ */
3537
+ declare function shouldRetry(error: unknown, attempt: number, maxAttempts?: number): boolean;
3538
+
3539
+ /**
3540
+ * LLM integration for @cuylabs/agent-core
3541
+ *
3542
+ * Handles the actual Vercel AI SDK streamText calls with
3543
+ * integrated retry logic and error handling.
3544
+ */
3545
+
3546
+ /** Stream result type - uses default Output for text streaming */
3547
+ type LLMStreamResult = StreamTextResult<ToolSet, Output.Output<string, string, never>>;
3548
+
3549
+ /**
3550
+ * Custom stream result type - compatible shape from external providers.
3551
+ * This is what packages like computer-agent return for OpenAI computer use.
3552
+ */
3553
+ type CustomStreamResult = {
3554
+ fullStream: AsyncIterable<StreamChunk>;
3555
+ text: Promise<string>;
3556
+ usage: Promise<{
3557
+ inputTokens: number;
3558
+ outputTokens: number;
3559
+ totalTokens: number;
3560
+ }>;
3561
+ finishReason: Promise<string>;
3562
+ };
3563
+ /** Union type for stream results - either AI SDK or custom */
3564
+ type AnyStreamResult = LLMStreamResult | CustomStreamResult;
3565
+ /** Default max output tokens */
3566
+ declare const OUTPUT_TOKEN_MAX = 32000;
3567
+ /**
3568
+ * @deprecated Use StreamProvider from types instead
3569
+ */
3570
+ type CustomStreamProvider = StreamProvider;
3571
+ /**
3572
+ * Input for LLM stream
3573
+ */
3574
+ interface LLMStreamInput {
3575
+ /** Session ID */
3576
+ sessionID: string;
3577
+ /** Model to use */
3578
+ model: LanguageModel;
3579
+ /** System prompt parts */
3580
+ system: string[];
3581
+ /** Messages to send */
3582
+ messages: ModelMessage[];
3583
+ /** Abort signal */
3584
+ abort: AbortSignal;
3585
+ /** Tools to use */
3586
+ tools: Record<string, Tool$1.Info>;
3587
+ /** Working directory */
3588
+ cwd: string;
3589
+ /** Execution environment for tools. When provided, passed into ToolContext. */
3590
+ host?: ToolHost;
3591
+ /** Temperature */
3592
+ temperature?: number;
3593
+ /** Top-p */
3594
+ topP?: number;
3595
+ /** Max output tokens */
3596
+ maxOutputTokens?: number;
3597
+ /** Max steps (tool iterations) */
3598
+ maxSteps?: number;
3599
+ /** Reasoning level for extended thinking models */
3600
+ reasoningLevel?: ReasoningLevel;
3601
+ /** Retry configuration */
3602
+ retry?: RetryConfig;
3603
+ /** Callback for step completion */
3604
+ onStepFinish?: (step: StepInfo) => void | Promise<void>;
3605
+ /**
3606
+ * Custom stream provider - when set, bypasses AI SDK streamText.
3607
+ * Used for OpenAI computer use and other specialized models.
3608
+ */
3609
+ customStreamProvider?: StreamProvider;
3610
+ /**
3611
+ * Turn tracker for automatic file baseline capture.
3612
+ * When provided, tools that declare fileOps will have
3613
+ * their file modifications tracked for undo/diff.
3614
+ */
3615
+ turnTracker?: TurnTrackerContext;
3616
+ /**
3617
+ * Pre-built MCP tools (already in AI SDK format).
3618
+ * These are merged with local tools before passing to streamText.
3619
+ */
3620
+ mcpTools?: ToolSet;
3621
+ /**
3622
+ * Intervention controller for mid-turn message injection.
3623
+ *
3624
+ * When provided, a `prepareStep` hook is registered with `streamText()`
3625
+ * that checks for pending interventions before each LLM call. Pending
3626
+ * messages are appended as user messages to the step's conversation.
3627
+ *
3628
+ * Only works with the standard AI SDK path (not custom stream providers).
3629
+ */
3630
+ intervention?: InterventionController;
3631
+ }
3632
+ /**
3633
+ * Step information
3634
+ */
3635
+ interface StepInfo {
3636
+ toolResults?: Array<{
3637
+ toolName: string;
3638
+ toolCallId: string;
3639
+ output: unknown;
3640
+ }>;
3641
+ usage?: {
3642
+ inputTokens?: number;
3643
+ outputTokens?: number;
3644
+ totalTokens?: number;
3645
+ };
3646
+ finishReason?: string;
3647
+ }
3648
+ /**
3649
+ * LLM module - handles Vercel AI SDK calls
3650
+ */
3651
+ declare namespace LLM {
3652
+ /**
3653
+ * Build Vercel AI SDK ToolSet from Tool.Info definitions
3654
+ *
3655
+ * @param tools - Tool definitions to build
3656
+ * @param cwd - Working directory for file operations
3657
+ * @param sessionID - Current session ID
3658
+ * @param messageID - Current message ID
3659
+ * @param abort - Abort signal for cancellation
3660
+ * @param turnTracker - Optional turn tracker for automatic file baseline capture
3661
+ * @param host - Optional execution environment for tools
3662
+ */
3663
+ function buildToolSet(tools: Record<string, Tool$1.Info>, cwd: string, sessionID: string, messageID: string, abort: AbortSignal, turnTracker?: TurnTrackerContext, host?: ToolHost): Promise<ToolSet>;
3664
+ /**
3665
+ * Create a stream for LLM completion with retry support
3666
+ */
3667
+ function stream(input: LLMStreamInput): Promise<AnyStreamResult>;
3668
+ /**
3669
+ * Create a stream without retry (for when caller handles retry externally)
3670
+ */
3671
+ function streamOnce(input: LLMStreamInput): Promise<AnyStreamResult>;
3672
+ }
3673
+
3674
+ /**
3675
+ * Stream processor for @cuylabs/agent-core
3676
+ *
3677
+ * Processes the LLM stream and emits events.
3678
+ * Includes doom loop detection and context overflow monitoring.
3679
+ */
3680
+
3681
+ /**
3682
+ * Doom loop error thrown when repeated tool calls are detected
3683
+ */
3684
+ declare class DoomLoopError extends Error {
3685
+ readonly toolName: string;
3686
+ readonly repeatCount: number;
3687
+ readonly input: unknown;
3688
+ constructor(toolName: string, repeatCount: number, input: unknown);
3689
+ }
3690
+ /**
3691
+ * Context overflow error
3692
+ */
3693
+ declare class ContextOverflowError extends Error {
3694
+ readonly inputTokens: number;
3695
+ readonly limit: number;
3696
+ constructor(inputTokens: number, limit: number);
3697
+ }
3698
+ /**
3699
+ * Processor options
3700
+ */
3701
+ interface ProcessorOptions {
3702
+ /** Session ID (optional - for tracking purposes) */
3703
+ sessionID?: string;
3704
+ /** Abort signal */
3705
+ abort: AbortSignal;
3706
+ /** Event callback */
3707
+ onEvent: (event: AgentEvent) => void | Promise<void>;
3708
+ /** Doom loop threshold (default: 3) */
3709
+ doomLoopThreshold?: number;
3710
+ /** Enforce doom loop (throw error vs just warn) - only used when onDoomLoop is not provided */
3711
+ enforceDoomLoop?: boolean;
3712
+ /**
3713
+ * Handler for doom loop detection.
3714
+ * When provided, this is called instead of throwing DoomLoopError.
3715
+ * Allows user to choose: allow (continue), deny (stop), or remember (allow + skip future).
3716
+ */
3717
+ onDoomLoop?: DoomLoopHandler;
3718
+ /** Tools that are "remembered" (allowed without asking) */
3719
+ rememberedDoomLoopTools?: Set<string>;
3720
+ /** Token limit for context overflow detection */
3721
+ contextTokenLimit?: number;
3722
+ /** Callback for context overflow (allows custom handling) */
3723
+ onContextOverflow?: (tokens: number, limit: number) => void | Promise<void>;
3724
+ /** Current step number (for progress events) */
3725
+ currentStep?: number;
3726
+ /** Max steps (for progress events) */
3727
+ maxSteps?: number;
3728
+ }
3729
+ /**
3730
+ * Processor result with collected data
3731
+ */
3732
+ interface ProcessorOutput {
3733
+ /** Processing result */
3734
+ result: ProcessorResult;
3735
+ /** Accumulated text */
3736
+ text: string;
3737
+ /** Tool results */
3738
+ toolResults: Array<{
3739
+ toolName: string;
3740
+ toolCallId: string;
3741
+ result: unknown;
3742
+ }>;
3743
+ /** Final usage */
3744
+ usage?: TokenUsage;
3745
+ /** Error if any */
3746
+ error?: Error;
3747
+ }
3748
+ /**
3749
+ * Process an LLM stream and emit events
3750
+ *
3751
+ * AI SDK 5 fullStream events:
3752
+ * - text-start, text-delta, text-end
3753
+ * - reasoning-start, reasoning-delta, reasoning-end
3754
+ * - tool-call, tool-result, tool-error
3755
+ * - start-step, finish-step
3756
+ * - start, finish, abort, error
3757
+ *
3758
+ * Also supports custom stream results from providers like OpenAI computer use.
3759
+ */
3760
+ declare function processStream(stream: AnyStreamResult, options: ProcessorOptions): Promise<ProcessorOutput>;
3761
+
3762
+ /**
3763
+ * Approval System
3764
+ *
3765
+ * Handles user confirmations before potentially dangerous operations.
3766
+ * UI applications can intercept approval requests and prompt users.
3767
+ *
3768
+ * Design principles:
3769
+ * - Simple: 3 actions (allow, deny, remember)
3770
+ * - Safe: Default deny for unknown operations
3771
+ * - Async: Promise-based for UI integration
3772
+ * - Typed: Full type safety for tool arguments
3773
+ */
3774
+ /** Risk level for operations */
3775
+ type RiskLevel = "safe" | "moderate" | "dangerous";
3776
+ /** User's response to an approval request */
3777
+ type ApprovalAction = "allow" | "deny" | "remember";
3778
+ /** Approval request sent to the UI/handler */
3779
+ interface ApprovalRequest {
3780
+ /** Unique request ID */
3781
+ id: string;
3782
+ /** Session ID */
3783
+ sessionId: string;
3784
+ /** Tool name */
3785
+ tool: string;
3786
+ /** Tool arguments */
3787
+ args: unknown;
3788
+ /** Human-readable description */
3789
+ description: string;
3790
+ /** Risk level */
3791
+ risk: RiskLevel;
3792
+ /** Patterns that would be remembered if "remember" is chosen */
3793
+ patterns: string[];
3794
+ /** Timestamp */
3795
+ timestamp: number;
3796
+ }
3797
+ /** Rule for auto-approving/denying operations */
3798
+ interface ApprovalRule {
3799
+ /** Pattern to match (glob-style) */
3800
+ pattern: string;
3801
+ /** Tool to match (* for all) */
3802
+ tool: string;
3803
+ /** Action to take */
3804
+ action: "allow" | "deny";
3805
+ }
3806
+ /** Configuration for the approval handler */
3807
+ interface ApprovalConfig {
3808
+ /** Default action when no rule matches (default: "ask") */
3809
+ defaultAction?: "allow" | "deny" | "ask";
3810
+ /** Pre-configured rules */
3811
+ rules?: ApprovalRule[];
3812
+ /** Handler for approval requests */
3813
+ onRequest?: (request: ApprovalRequest) => Promise<ApprovalAction>;
3814
+ /** Timeout for approval requests in ms (default: 5 minutes) */
3815
+ timeout?: number;
3816
+ }
3817
+ /**
3818
+ * Get risk level for a tool
3819
+ */
3820
+ declare function getToolRisk(tool: string, customRisks?: Record<string, RiskLevel>): RiskLevel;
3821
+ /** Thrown when user denies an operation */
3822
+ declare class ApprovalDeniedError extends Error {
3823
+ readonly tool: string;
3824
+ readonly args: unknown;
3825
+ constructor(tool: string, args: unknown, message?: string);
3826
+ }
3827
+ /** Thrown when approval request times out */
3828
+ declare class ApprovalTimeoutError extends Error {
3829
+ readonly tool: string;
3830
+ readonly timeoutMs: number;
3831
+ constructor(tool: string, timeoutMs: number);
3832
+ }
3833
+ /**
3834
+ * Creates an approval handler for managing tool execution permissions.
3835
+ *
3836
+ * @example
3837
+ * ```typescript
3838
+ * const approvals = createApprovalHandler({
3839
+ * rules: [
3840
+ * { pattern: "src/*", tool: "edit_file", action: "allow" },
3841
+ * { pattern: "*", tool: "bash", action: "deny" },
3842
+ * ],
3843
+ * onRequest: async (req) => {
3844
+ * // Show UI dialog
3845
+ * const userChoice = await showConfirmDialog(req);
3846
+ * return userChoice; // "allow" | "deny" | "remember"
3847
+ * },
3848
+ * });
3849
+ * ```
3850
+ */
3851
+ declare function createApprovalHandler(config?: ApprovalConfig): {
3852
+ request: (sessionId: string, tool: string, args: unknown, customRisks?: Record<string, RiskLevel>) => Promise<void>;
3853
+ cancelAll: (reason?: string) => void;
3854
+ addRule: (rule: ApprovalRule) => void;
3855
+ getRules: () => readonly ApprovalRule[];
3856
+ clearSessionRules: () => void;
3857
+ };
3858
+ /** Type for the approval handler */
3859
+ type ApprovalHandler = ReturnType<typeof createApprovalHandler>;
3860
+
3861
+ /**
3862
+ * Checkpoint System - Git-based file state tracking and recovery
3863
+ *
3864
+ * Provides undo/restore capabilities for agent file operations by maintaining
3865
+ * an isolated git repository that tracks working directory changes.
3866
+ *
3867
+ * @example
3868
+ * ```typescript
3869
+ * const checkpoints = await createCheckpointManager({ workDir: '/project' });
3870
+ *
3871
+ * // Save state before risky operation
3872
+ * const id = await checkpoints.save('before refactoring');
3873
+ *
3874
+ * // After operation completes, see what changed
3875
+ * const changes = await checkpoints.changes(id);
3876
+ * console.log('Modified files:', changes.files);
3877
+ *
3878
+ * // If something went wrong, restore
3879
+ * await checkpoints.restore(id);
3880
+ *
3881
+ * // Or undo just specific files
3882
+ * await checkpoints.undoFiles(id, ['src/broken.ts']);
3883
+ * ```
3884
+ */
3885
+ /** Configuration for creating a checkpoint manager */
3886
+ interface CheckpointConfig {
3887
+ /** Working directory to track (absolute path) */
3888
+ workDir: string;
3889
+ /** Optional custom storage directory (defaults to ~/.cuylabs/checkpoints/{project-hash}/) */
3890
+ storageDir?: string;
3891
+ /** Whether to track file contents (enables restore). Default: true */
3892
+ trackContent?: boolean;
3893
+ /** Patterns to exclude from tracking (gitignore format) */
3894
+ exclude?: string[];
3895
+ /** Maximum number of checkpoints to retain. Default: 100 */
3896
+ maxCheckpoints?: number;
3897
+ }
3898
+ /** A saved checkpoint representing a point-in-time state */
3899
+ interface Checkpoint {
3900
+ /** Unique checkpoint identifier (git tree hash) */
3901
+ id: string;
3902
+ /** Human-readable label */
3903
+ label: string;
3904
+ /** When the checkpoint was created */
3905
+ createdAt: Date;
3906
+ /** Optional metadata */
3907
+ metadata?: Record<string, unknown>;
3908
+ }
3909
+ /** File change information between checkpoints */
3910
+ interface FileChange {
3911
+ /** Relative file path */
3912
+ path: string;
3913
+ /** Type of change */
3914
+ type: 'added' | 'modified' | 'deleted';
3915
+ /** Lines added (for text files) */
3916
+ additions?: number;
3917
+ /** Lines removed (for text files) */
3918
+ deletions?: number;
3919
+ }
3920
+ /** Changes detected between a checkpoint and current state */
3921
+ interface ChangeSet {
3922
+ /** Checkpoint being compared from */
3923
+ fromCheckpoint: string;
3924
+ /** List of changed files */
3925
+ files: FileChange[];
3926
+ /** Unified diff output */
3927
+ diff: string;
3928
+ }
3929
+ /** Checkpoint manager interface */
3930
+ interface CheckpointManager {
3931
+ /** Save current state as a checkpoint */
3932
+ save(label?: string, metadata?: Record<string, unknown>): Promise<Checkpoint>;
3933
+ /** Restore all files to a checkpoint state */
3934
+ restore(checkpointId: string): Promise<void>;
3935
+ /** Get changes since a checkpoint */
3936
+ changes(checkpointId: string): Promise<ChangeSet>;
3937
+ /** Undo changes to specific files (restore them from checkpoint) */
3938
+ undoFiles(checkpointId: string, files: string[]): Promise<void>;
3939
+ /** List all checkpoints (newest first) */
3940
+ list(): Promise<Checkpoint[]>;
3941
+ /** Delete a specific checkpoint */
3942
+ remove(checkpointId: string): Promise<void>;
3943
+ /** Prune old checkpoints beyond maxCheckpoints limit */
3944
+ prune(): Promise<number>;
3945
+ /** Get the latest checkpoint */
3946
+ latest(): Promise<Checkpoint | null>;
3947
+ /** Get file content at a specific checkpoint */
3948
+ getFileAt(checkpointId: string, filePath: string): Promise<string | null>;
3949
+ /** Check if manager is initialized */
3950
+ isInitialized(): boolean;
3951
+ /** Close and cleanup resources */
3952
+ close(): Promise<void>;
3953
+ }
3954
+ /**
3955
+ * Creates a checkpoint manager for tracking file state changes
3956
+ *
3957
+ * @param config - Configuration options
3958
+ * @returns Initialized checkpoint manager
3959
+ */
3960
+ declare function createCheckpointManager(config: CheckpointConfig): Promise<CheckpointManager>;
3961
+ /**
3962
+ * Clear all checkpoint data for a working directory
3963
+ *
3964
+ * @param workDir - Working directory whose checkpoints to clear
3965
+ */
3966
+ declare function clearCheckpoints(workDir: string): Promise<void>;
3967
+
3968
+ /**
3969
+ * Token Estimation Utilities
3970
+ *
3971
+ * Provides lightweight heuristic-based token counting for messages
3972
+ * and conversations. Uses the chars/4 approximation — simple but
3973
+ * effective for context-window planning decisions.
3974
+ *
3975
+ * These are *estimates*, not exact counts. For billing or hard limits,
3976
+ * use the provider's native tokeniser instead.
3977
+ */
3978
+
3979
+ /**
3980
+ * Estimate token count for a plain string.
3981
+ *
3982
+ * Uses the widely-accepted `chars / 4` heuristic.
3983
+ *
3984
+ * @param text - Text to estimate
3985
+ * @returns Estimated token count (always ≥ 1 for non-empty input)
3986
+ */
3987
+ declare function estimateTokens(text: string): number;
3988
+ /**
3989
+ * Estimate token count for a single message.
3990
+ *
3991
+ * Handles:
3992
+ * - Plain string content
3993
+ * - Multi-part / multimodal arrays (text parts + images)
3994
+ *
3995
+ * @param message - A `Message` (internal) or `ModelMessage` (AI SDK)
3996
+ * @returns Estimated token count
3997
+ */
3998
+ declare function estimateMessageTokens(message: Message | ModelMessage): number;
3999
+ /**
4000
+ * Estimate total tokens for an entire conversation.
4001
+ *
4002
+ * Adds a small per-message overhead (≈ 4 tokens) for message
4003
+ * framing (`role`, delimiters, etc.).
4004
+ *
4005
+ * @param messages - Array of messages to estimate
4006
+ * @returns Estimated total token count
4007
+ */
4008
+ declare function estimateConversationTokens(messages: (Message | ModelMessage)[]): number;
4009
+
4010
+ /**
4011
+ * Context Overflow Detection & Pruning
4012
+ *
4013
+ * Detects when a conversation is approaching the model's context-window
4014
+ * limit and prunes old / redundant content to stay within bounds.
4015
+ *
4016
+ * Two pruning strategies are available:
4017
+ * 1. **Tool-result pruning** — replaces large, stale tool outputs with
4018
+ * compact placeholders (lightweight, no model call).
4019
+ * 2. **Conversation cutting** — identifies a safe cut-point and removes
4020
+ * older messages entirely (optionally summarised by the manager).
4021
+ */
4022
+
4023
+ /**
4024
+ * Context limits configuration.
4025
+ *
4026
+ * All values are in *estimated* tokens (see {@link estimateTokens}).
4027
+ */
4028
+ interface ContextLimits {
4029
+ /** Maximum context window size in tokens */
4030
+ contextWindow: number;
4031
+ /** Reserve tokens for output generation */
4032
+ reserveTokens: number;
4033
+ /** Protect this many recent tokens from pruning */
4034
+ protectedTokens: number;
4035
+ /** Minimum tokens to trigger pruning (avoid pruning tiny contexts) */
4036
+ pruneMinimum: number;
4037
+ }
4038
+ /**
4039
+ * Default context limits.
4040
+ * Based on typical 128 k context-window models.
4041
+ */
4042
+ declare const DEFAULT_CONTEXT_LIMITS: ContextLimits;
4043
+ /**
4044
+ * Check whether the context is overflowing.
4045
+ *
4046
+ * @param tokens - Current estimated token count
4047
+ * @param limits - Context limits (defaults to 128 k window)
4048
+ * @returns `true` if context exceeds the safe threshold
4049
+ */
4050
+ declare function isContextOverflowing(tokens: number, limits?: ContextLimits): boolean;
4051
+ /**
4052
+ * Check whether pruning should be triggered.
4053
+ *
4054
+ * Returns `false` when the conversation is still small enough that
4055
+ * pruning would be wasteful (below {@link ContextLimits.pruneMinimum}).
4056
+ */
4057
+ declare function shouldPruneContext(tokens: number, limits?: ContextLimits): boolean;
4058
+ /**
4059
+ * Result of a pruning operation.
4060
+ */
4061
+ interface PruneResult {
4062
+ /** Messages after pruning (may include a summary message) */
4063
+ messages: Message[];
4064
+ /** Number of messages removed */
4065
+ removedCount: number;
4066
+ /** Estimated tokens removed */
4067
+ tokensRemoved: number;
4068
+ /** Whether summarisation was used */
4069
+ summarized: boolean;
4070
+ /** The summary content, if generated */
4071
+ summary?: string;
4072
+ }
4073
+ /**
4074
+ * Find a safe index at which the conversation can be "cut".
4075
+ *
4076
+ * Rules:
4077
+ * - Never cut in the middle of a tool-call sequence
4078
+ * - Prefer cutting after assistant / user messages
4079
+ * - Keep at least {@link protectedTokens} tokens at the end
4080
+ *
4081
+ * @param messages - Full message array
4082
+ * @param protectedTokens - Tokens to preserve at the end
4083
+ * @returns Cut index (exclusive — remove messages *before* this index).
4084
+ * Returns `0` when no safe cut-point exists.
4085
+ */
4086
+ declare function findCutPoint(messages: Message[], protectedTokens?: number): number;
4087
+ /**
4088
+ * Prune old, large tool results from the conversation.
4089
+ *
4090
+ * This mirrors OpenCode's `SessionCompaction.prune()` pattern:
4091
+ * - Walks backwards through messages
4092
+ * - Protects recent outputs (within {@link protectedTokens})
4093
+ * - Skips protected tools (e.g. "skill")
4094
+ * - Stamps pruned messages with a `compactedAt` timestamp
4095
+ *
4096
+ * @param messages - Messages to prune
4097
+ * @param protectedTokens - Don't prune tool results in the last N tokens
4098
+ * @param options - Extra tool names to protect
4099
+ * @returns New message array with large tool outputs replaced
4100
+ */
4101
+ declare function pruneToolResults(messages: Message[], protectedTokens?: number, options?: {
4102
+ /** Additional tools to protect from pruning */
4103
+ protectedTools?: string[];
4104
+ }): Message[];
4105
+
4106
+ /**
4107
+ * Context Summarisation
4108
+ *
4109
+ * When tool-result pruning alone isn't enough to stay within the
4110
+ * context window, the manager can *cut* the conversation and replace
4111
+ * the removed portion with an LLM-generated summary.
4112
+ *
4113
+ * This module provides the summary-generation logic and the
4114
+ * top-level `pruneContext()` orchestration function.
4115
+ */
4116
+
4117
+ /**
4118
+ * Options for summary generation.
4119
+ */
4120
+ interface SummarizationOptions {
4121
+ /** Model used to generate the summary */
4122
+ model: LanguageModel;
4123
+ /** Max tokens for the summary output */
4124
+ maxTokens?: number;
4125
+ /** Custom summarisation system prompt */
4126
+ customPrompt?: string;
4127
+ }
4128
+ /**
4129
+ * Options for {@link pruneContext}.
4130
+ */
4131
+ interface PruneContextOptions {
4132
+ /** Model for summarisation (omit to skip summary generation) */
4133
+ model?: LanguageModel;
4134
+ /** Context limits to enforce */
4135
+ limits?: ContextLimits;
4136
+ /** Custom summarisation prompt */
4137
+ summaryPrompt?: string;
4138
+ }
4139
+ /**
4140
+ * Generate a natural-language summary of a message sequence.
4141
+ *
4142
+ * @param messages - Messages to summarise
4143
+ * @param options - Model & prompt configuration
4144
+ * @returns Summary text
4145
+ */
4146
+ declare function generateSummary(messages: Message[], options: SummarizationOptions): Promise<string>;
4147
+ /**
4148
+ * Prune a conversation to fit within context-window limits.
4149
+ *
4150
+ * Strategy (in order):
4151
+ * 1. Prune old tool outputs (lightweight, no model call)
4152
+ * 2. If still overflowing, find a safe cut-point and remove
4153
+ * older messages — optionally generating an LLM summary.
4154
+ *
4155
+ * @param messages - Current message array
4156
+ * @param options - Limits, model, and prompt overrides
4157
+ * @returns A {@link PruneResult} with the trimmed messages
4158
+ */
4159
+ declare function pruneContext(messages: Message[], options?: PruneContextOptions): Promise<PruneResult>;
4160
+
4161
+ /**
4162
+ * Context Manager
4163
+ *
4164
+ * Stateful wrapper around the context-management primitives.
4165
+ * Tracks per-session context limits and provides a single entry
4166
+ * point for token estimation, overflow detection, and pruning.
4167
+ */
4168
+
4169
+ /**
4170
+ * Per-session context manager.
4171
+ *
4172
+ * Holds the active context-window limits and provides convenience
4173
+ * methods for checking, reporting, and pruning context.
4174
+ *
4175
+ * @example
4176
+ * ```typescript
4177
+ * const ctx = new ContextManager({ limits: { contextWindow: 200_000 } });
4178
+ *
4179
+ * if (ctx.shouldPrune(messages)) {
4180
+ * const result = await ctx.prune(messages);
4181
+ * console.log(`Removed ${result.removedCount} messages`);
4182
+ * }
4183
+ * ```
4184
+ */
4185
+ declare class ContextManager {
4186
+ private limits;
4187
+ private model?;
4188
+ private summaryPrompt?;
4189
+ constructor(options?: {
4190
+ limits?: Partial<ContextLimits>;
4191
+ model?: LanguageModel;
4192
+ summaryPrompt?: string;
4193
+ });
4194
+ /** Get a copy of the current context limits. */
4195
+ getLimits(): ContextLimits;
4196
+ /** Update context limits (e.g. when switching models). */
4197
+ setLimits(limits: Partial<ContextLimits>): void;
4198
+ /** Set the model used for summarisation. */
4199
+ setModel(model: LanguageModel): void;
4200
+ /** Estimate total tokens for a message array. */
4201
+ estimateTokens(messages: (Message | ModelMessage)[]): number;
4202
+ /** Check whether the context is overflowing. */
4203
+ isOverflowing(messages: (Message | ModelMessage)[]): boolean;
4204
+ /** Check whether pruning should be triggered. */
4205
+ shouldPrune(messages: (Message | ModelMessage)[]): boolean;
4206
+ /** Prune context to fit within limits. */
4207
+ prune(messages: Message[]): Promise<PruneResult>;
4208
+ /**
4209
+ * Get a snapshot of token statistics.
4210
+ *
4211
+ * Useful for dashboards, logging, or deciding whether to prune.
4212
+ */
4213
+ getStats(messages: (Message | ModelMessage)[]): {
4214
+ tokens: number;
4215
+ limit: number;
4216
+ available: number;
4217
+ utilizationPercent: number;
4218
+ isOverflowing: boolean;
4219
+ shouldPrune: boolean;
4220
+ };
4221
+ }
4222
+
4223
+ /**
4224
+ * LocalHost — executes tools on the local machine.
4225
+ *
4226
+ * Default ToolHost implementation. Uses Node's `child_process.spawn`
4227
+ * for commands and `node:fs/promises` for file operations.
4228
+ */
4229
+
4230
+ /**
4231
+ * Create a ToolHost that runs everything on the local machine.
4232
+ *
4233
+ * @param defaultCwd Working directory for commands when none is specified.
4234
+ * Defaults to `process.cwd()`.
4235
+ */
4236
+ declare function localHost(defaultCwd?: string): ToolHost;
4237
+
4238
+ /**
4239
+ * DockerHost — executes tools inside a Docker container.
4240
+ *
4241
+ * Requires `dockerode` as a peer dependency. Install it with:
4242
+ *
4243
+ * npm install dockerode
4244
+ *
4245
+ * All file operations and process execution happen inside the container
4246
+ * via `docker exec`. File reads/writes use shell commands piped through
4247
+ * the Docker API — no volumes or bind mounts required.
4248
+ *
4249
+ * ### Quick start — build a sandbox and wire it up
4250
+ *
4251
+ * ```typescript
4252
+ * import Dockerode from "dockerode";
4253
+ * import { dockerHost, createAgent, Tool } from "@cuylabs/agent-core";
4254
+ * import { openai } from "@ai-sdk/openai";
4255
+ * import { z } from "zod";
4256
+ *
4257
+ * // 1. Build a lightweight sandbox container
4258
+ * const docker = new Dockerode();
4259
+ * await new Promise<void>((res, rej) =>
4260
+ * docker.pull("alpine:3.19", {}, (e, s) =>
4261
+ * e ? rej(e) : docker.modem.followProgress(s, (err) => (err ? rej(err) : res())),
4262
+ * ),
4263
+ * );
4264
+ * const container = await docker.createContainer({
4265
+ * Image: "alpine:3.19",
4266
+ * name: "agent-sandbox",
4267
+ * Cmd: [
4268
+ * "sh", "-c",
4269
+ * "apk add --no-cache bash coreutils python3 && tail -f /dev/null",
4270
+ * ],
4271
+ * WorkingDir: "/workspace",
4272
+ * });
4273
+ * await container.start();
4274
+ *
4275
+ * // 2. Create a dockerHost pointed at the running container
4276
+ * const host = await dockerHost({
4277
+ * container: "agent-sandbox",
4278
+ * workdir: "/workspace",
4279
+ * });
4280
+ *
4281
+ * // 3. Define tools — they call ctx.host.exec(), ctx.host.writeFile(), etc.
4282
+ * // The SAME tool code works on localHost() or dockerHost().
4283
+ * const runCmd = Tool.define("run", {
4284
+ * description: "Run a shell command",
4285
+ * parameters: z.object({ command: z.string() }),
4286
+ * execute: async ({ command }, ctx) => {
4287
+ * const r = await ctx.host!.exec(command);
4288
+ * return { title: command, output: r.stdout + r.stderr, metadata: {} };
4289
+ * },
4290
+ * });
4291
+ *
4292
+ * // 4. Create agent — all tools execute inside the container
4293
+ * const agent = createAgent({ model: openai("gpt-4o-mini"), host, tools: [runCmd] });
4294
+ * for await (const ev of agent.chat("s1", "Run: python3 -c 'print(42)'")) {
4295
+ * if (ev.type === "text-delta") process.stdout.write(ev.text);
4296
+ * }
4297
+ *
4298
+ * // 5. Clean up
4299
+ * await container.stop();
4300
+ * await container.remove();
4301
+ * ```
4302
+ *
4303
+ * ### Passing a Container object directly
4304
+ *
4305
+ * ```typescript
4306
+ * import Dockerode from "dockerode";
4307
+ * const docker = new Dockerode();
4308
+ * const host = await dockerHost({
4309
+ * container: docker.getContainer("my-container"),
4310
+ * user: "root",
4311
+ * workdir: "/workspace",
4312
+ * });
4313
+ * ```
4314
+ *
4315
+ * See `examples/14-docker-host.ts` for a fully runnable version.
4316
+ */
4317
+
4318
+ /**
4319
+ * Configuration for creating a Docker ToolHost.
4320
+ */
4321
+ interface DockerHostOptions {
4322
+ /**
4323
+ * The container to connect to. Either:
4324
+ * - A string container name or ID
4325
+ * - A Dockerode Container object (from `docker.getContainer(...)`)
4326
+ */
4327
+ container: string | {
4328
+ id: string;
4329
+ };
4330
+ /** User to run commands as inside the container (default: container default). */
4331
+ user?: string;
4332
+ /** Default working directory inside the container (default: "/"). */
4333
+ workdir?: string;
4334
+ /**
4335
+ * Dockerode constructor options. Only used when `container` is a string.
4336
+ * Passed directly to `new Dockerode(options)`.
4337
+ *
4338
+ * @example
4339
+ * ```typescript
4340
+ * { socketPath: "/var/run/docker.sock" }
4341
+ * // or
4342
+ * { host: "192.168.1.10", port: 2376 }
4343
+ * ```
4344
+ */
4345
+ dockerOptions?: Record<string, unknown>;
4346
+ }
4347
+ /**
4348
+ * Create a ToolHost that runs everything inside a Docker container.
4349
+ *
4350
+ * Requires `dockerode` to be installed as a peer dependency.
4351
+ * The container **must already be running** — use `container.start()`
4352
+ * before calling this factory.
4353
+ *
4354
+ * @param options Container configuration.
4355
+ * @param options.container Container name/ID string, or a Dockerode `Container` object.
4356
+ * @param options.user User to run commands as (default: container's default user).
4357
+ * @param options.workdir Default working directory (default: "/").
4358
+ * @param options.dockerOptions Dockerode constructor options (socket path, remote host, etc.).
4359
+ * Only used when `container` is a string.
4360
+ * @returns A ToolHost whose `exec`, `readFile`, `writeFile`, etc. all operate
4361
+ * inside the container.
4362
+ *
4363
+ * @throws If `dockerode` is not installed.
4364
+ * @throws If the container does not exist or is not running (on first operation).
4365
+ *
4366
+ * @example Build a container, wire it up, clean up
4367
+ * ```typescript
4368
+ * import Dockerode from "dockerode";
4369
+ * import { dockerHost, createAgent } from "@cuylabs/agent-core";
4370
+ *
4371
+ * const docker = new Dockerode();
4372
+ * const ctr = await docker.createContainer({
4373
+ * Image: "node:22-alpine",
4374
+ * Cmd: ["tail", "-f", "/dev/null"],
4375
+ * WorkingDir: "/app",
4376
+ * });
4377
+ * await ctr.start();
4378
+ *
4379
+ * const host = await dockerHost({ container: ctr, workdir: "/app" });
4380
+ * const agent = createAgent({ host, model: openai("gpt-4o") });
4381
+ *
4382
+ * // ... use agent ...
4383
+ *
4384
+ * await ctr.stop();
4385
+ * await ctr.remove();
4386
+ * ```
4387
+ */
4388
+ declare function dockerHost(options: DockerHostOptions): Promise<ToolHost>;
4389
+
4390
+ type AdapterKind = "openai" | "anthropic" | "google" | "openai-compatible";
4391
+ type AdapterSettings = {
4392
+ apiKey?: string;
4393
+ baseUrl?: string;
4394
+ headers?: Record<string, string>;
4395
+ extra?: Record<string, unknown>;
4396
+ };
4397
+ type EngineSpec = {
4398
+ adapter: AdapterKind | string;
4399
+ settings?: AdapterSettings;
4400
+ build?: (modelId: string, settings: AdapterSettings) => LanguageModel;
4401
+ };
4402
+ type ModelSpec = {
4403
+ engine: string;
4404
+ id: string;
4405
+ settings?: AdapterSettings;
4406
+ };
4407
+ type Directory = {
4408
+ engines: Record<string, EngineSpec>;
4409
+ entries?: Record<string, ModelSpec>;
4410
+ };
4411
+ type Resolver = (key: string) => LanguageModel;
4412
+ declare function createResolver(directory: Directory): Resolver;
4413
+
4414
+ export { type AdapterSettings, Agent, type AgentConfig, type AgentEvent, type AgentState, type AgentStatus, type AnyStreamResult, type AppliedPreset, type ApprovalAction, type ApprovalConfig, ApprovalDeniedError, type ApprovalEvent, type ApprovalHandler, type ApprovalRequest, type ApprovalRule, ApprovalTimeoutError, type AssistantMessage, type BranchEntry, type ChangeSet, type Checkpoint, type CheckpointConfig, type CheckpointManager, type CompactionConfig, type CompactionEntry, type ContextLimits, ContextManager, ContextOverflowError, type CreateSessionOptions, type CustomStreamProvider, type CustomStreamResult, DEFAULT_CONTEXT_LIMITS, DEFAULT_INSTRUCTION_PATTERNS, DEFAULT_MAX_DEPTH, DEFAULT_MAX_FILE_SIZE, DEFAULT_RETRY_CONFIG, type DockerHostOptions, type DoomLoopAction, DoomLoopError, type DoomLoopHandler, type DoomLoopRequest, type EngineSpec, type EnhancedTools, type EnvironmentInfo, type ErrorCategory, type FileBaseline, type FileChange, type FileEntry, FileOperationMeta, FileStorage, type FileStorageOptions, type HttpTransportConfig, type InstructionFile, InterventionController, LLM, LLMError, type LLMErrorOptions, type LLMStreamInput, type LLMStreamResult, type MCPConfig, type MCPManager, type MCPPrompt, type MCPResource, type MCPServerConfig, type MCPServerStatus, MemoryStorage, type Message, type MessageBase, type MessageEntry, type MessageError, type MessageRole, type MetadataEntry, type ModelCapabilities, ModelCapabilityResolver, type Directory as ModelDirectory, type ModelEntry, type ModelFamily, type Resolver as ModelResolver, type ModelSpec, type NetworkStatus, OUTPUT_TOKEN_MAX, type OnInterventionApplied, PRIORITY_BASE, PRIORITY_CUSTOM, PRIORITY_ENVIRONMENT, PRIORITY_INSTRUCTIONS, PRIORITY_OVERRIDE, PRIORITY_SKILLS, type PendingIntervention, type Preset, Presets, type ProcessorOptions, type ProcessorOutput, type ProcessorResult, type PromptBuildContext, PromptBuilder, type PromptConfig, type PromptSection, type ProviderCompatibility, type PruneContextOptions, type PruneResult, type ReasoningConfig, type ReasoningLevel, type ResolverOptions, type ResponseHeaders, type RetryConfig, type RetryHandlerOptions, type RetryState, type RiskLevel, STORAGE_VERSION, type Session, type SessionContext, type SessionEntry, type SessionHeader, type SessionInfo, SessionManager, type SessionStorage, type SseTransportConfig, type StdioTransportConfig, type StepInfo, type StreamChunk, type StreamInput, type StreamProvider, type StreamProviderConfig, type StreamProviderFactory, type StreamProviderInput, type StreamProviderResult, type SubAgentResult, type SummarizationOptions, type SystemMessage, type TokenUsage, Tool$1 as Tool, ToolHost, type ToolMessage, type TrackedToolMetadata, TurnChangeTracker, type TurnFileChange, type TurnSummary, type TurnTrackerConfig, TurnTrackerContext, type UndoResult, type UserMessage, applyPreset, buildMessagesFromEntries, buildReasoningOptions, buildReasoningOptionsSync, calculateDelay, clearCheckpoints, configureDefaultSessionManager, configureResolver, createAgent, createApprovalHandler, createCheckpointManager, createMCPManager, createPreset, createPromptBuilder, createResolver, createRetryHandler, createRetryState, createTurnTracker, defineServer, deserializeMessage, detectModelFamily, discoverInstructions, dockerHost, estimateConversationTokens, estimateMessageTokens, estimateTokens, extractFilePathsFromArgs, filterTools, findCutPoint, formatEnvironment, formatInstructions, gatherEnvironment, generateEntryId, generateSummary, getAvailableFamilies, getDataDir, getDefaultResolver, getDefaultSessionManager, getErrorCategory, getLeafId, getModelId, getNetworkStatus, getProjectSessionsDir, getProviderId, getReasoningConfig, getReasoningConfigSync, getRetryDelay, getSessionsDir, getTemplate, getToolRisk, httpServer, isContextOverflowing, isRetryable, isRetryableCategory, loadGlobalInstructions, localHost, mergePresets, parseJSONL, processStream, pruneContext, pruneToolResults, runConcurrent, serializeMessage, shouldCaptureBaseline, shouldPruneContext, shouldRetry, sleep, sseServer, stdioServer, summarizeEnvironment, supportsReasoning, supportsReasoningSync, toJSONL, withFileTracking, withRetry };