@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.
- package/README.md +275 -0
- package/dist/chunk-6VKLWNRE.js +288 -0
- package/dist/index-eud06GDQ.d.ts +446 -0
- package/dist/index.d.ts +4414 -0
- package/dist/index.js +6646 -0
- package/dist/tool/index.d.ts +2 -0
- package/dist/tool/index.js +24 -0
- package/package.json +98 -0
package/dist/index.d.ts
ADDED
|
@@ -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 };
|