@cuylabs/agent-core 0.2.0 → 0.4.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/dist/index.d.ts CHANGED
@@ -1,211 +1,10 @@
1
1
  import * as ai from 'ai';
2
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';
3
+ import { T as ToolHost, a as ToolContext, b as Tool$1, F as FileOperationMeta, c as TurnTrackerContext } from './index-BlSTfS-W.js';
4
+ export { C as CompatibleSchema, D as DirEntry, E as ExecOptions, d as ExecResult, e as FileStat, I as InferSchemaOutput, M as MAX_BYTES, f as MAX_LINES, g as TRUNCATE_DIR, h as TRUNCATE_GLOB, 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-BlSTfS-W.js';
5
5
  import { ProviderOptions } from '@ai-sdk/provider-utils';
6
6
  import 'zod';
7
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
8
  /**
210
9
  * Message and token types for @cuylabs/agent-core
211
10
  *
@@ -265,7 +64,6 @@ interface ToolMessage extends MessageBase {
265
64
  result: unknown;
266
65
  /**
267
66
  * Timestamp when this tool result was compacted/pruned.
268
- * Matches OpenCode's part.state.time.compacted pattern.
269
67
  * Once set, this message won't be pruned again.
270
68
  */
271
69
  compactedAt?: number;
@@ -316,19 +114,47 @@ interface Session {
316
114
  parentID?: string;
317
115
  }
318
116
 
117
+ /** Agent status for UI display */
118
+ type AgentStatus = "idle" | "processing" | "thinking" | "reasoning" | "calling-tool" | "waiting-approval" | "error";
119
+ /** Approval request for UI */
120
+ interface ApprovalEvent {
121
+ id: string;
122
+ tool: string;
123
+ args: unknown;
124
+ description: string;
125
+ risk: "safe" | "moderate" | "dangerous";
126
+ }
319
127
  /**
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)
128
+ * Events emitted during agent execution
327
129
  *
328
- * This is the single canonical definition — used by both the
329
- * streaming module and custom stream providers.
130
+ * These events are designed for UI consumption:
131
+ * - status: Overall agent state for status indicators
132
+ * - approval-request: User confirmation needed
133
+ * - progress: Step counts for progress bars
330
134
  */
331
- type StreamChunk = {
135
+ type AgentEvent = {
136
+ type: "status";
137
+ status: AgentStatus;
138
+ } | {
139
+ type: "approval-request";
140
+ request: ApprovalEvent;
141
+ } | {
142
+ type: "approval-resolved";
143
+ id: string;
144
+ action: "allow" | "deny" | "remember";
145
+ } | {
146
+ type: "step-start";
147
+ step: number;
148
+ maxSteps: number;
149
+ } | {
150
+ type: "step-finish";
151
+ step: number;
152
+ usage?: TokenUsage;
153
+ finishReason?: string;
154
+ } | {
155
+ type: "message";
156
+ message: Message;
157
+ } | {
332
158
  type: "text-start";
333
159
  } | {
334
160
  type: "text-delta";
@@ -346,7 +172,7 @@ type StreamChunk = {
346
172
  type: "reasoning-end";
347
173
  id: string;
348
174
  } | {
349
- type: "tool-call";
175
+ type: "tool-start";
350
176
  toolName: string;
351
177
  toolCallId: string;
352
178
  input: unknown;
@@ -354,206 +180,12 @@ type StreamChunk = {
354
180
  type: "tool-result";
355
181
  toolName: string;
356
182
  toolCallId: string;
357
- output: unknown;
183
+ result: unknown;
358
184
  } | {
359
185
  type: "tool-error";
360
186
  toolName: string;
361
187
  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;
188
+ error: string;
557
189
  } | {
558
190
  type: "computer-call";
559
191
  callId: string;
@@ -615,304 +247,956 @@ interface StreamInput {
615
247
  }
616
248
 
617
249
  /**
618
- * Reasoning Types & Constants
250
+ * Skill System Types
619
251
  *
620
- * Shared type definitions, level constants, and small helpers
621
- * used across the reasoning subsystem.
252
+ * Skills are modular, on-demand knowledge packs that extend an agent's
253
+ * capabilities with specialized instructions, workflows, and resources.
254
+ *
255
+ * Design principles:
256
+ *
257
+ * 1. **Progressive disclosure** — summaries always in context,
258
+ * full content loaded only when needed. Minimizes baseline token cost.
259
+ *
260
+ * 2. **Scoped priority** — project skills override user skills
261
+ * override global skills. Closest scope wins on name collision.
262
+ *
263
+ * 3. **Agent-initiated loading** — the agent decides when to
264
+ * activate a skill by calling the `skill` tool. No guesswork.
265
+ *
266
+ * 4. **Bundled resources** — skills can ship scripts,
267
+ * references, assets, and examples alongside instructions.
268
+ *
269
+ * 5. **Remote sources** — skills can be fetched from URLs,
270
+ * enabling shared skill repositories across teams.
271
+ *
272
+ * The lifecycle:
273
+ *
274
+ * Discovery → Registry → Summary injection → Agent tool call → Full load
275
+ *
276
+ * @packageDocumentation
622
277
  */
623
278
  /**
624
- * Standard reasoning / thinking levels.
279
+ * Where a skill was discovered, determining its override priority.
625
280
  *
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)|
281
+ * When two skills share the same name, the narrower scope wins:
282
+ * project > user > global > builtin
283
+ *
284
+ * Uses a 4-tier hierarchy with names that are intuitive for
285
+ * library consumers.
634
286
  */
635
- type ReasoningLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
287
+ type SkillScope = "project" | "user" | "global" | "builtin";
636
288
  /**
637
- * Provider-specific reasoning configuration returned by
638
- * {@link getReasoningConfig} / {@link getReasoningConfigSync}.
289
+ * How a skill was obtained — local filesystem or remote URL.
639
290
  */
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;
291
+ type SkillSourceType = "local" | "remote";
292
+ /**
293
+ * Where a skill came from, for provenance tracking.
294
+ */
295
+ interface SkillSource {
296
+ /** Local or remote origin */
297
+ type: SkillSourceType;
298
+ /** Root directory or URL this skill was discovered under */
299
+ root: string;
647
300
  }
648
-
649
301
  /**
650
- * Model Capability Types for @cuylabs/agent-core
302
+ * Classification of bundled resources within a skill directory.
651
303
  *
652
- * Defines the structure for model capabilities that can be sourced from
653
- * static patterns, local cache, or remote APIs.
304
+ * Resource taxonomy:
305
+ * - `script` — executable code for deterministic tasks (can be run without reading)
306
+ * - `reference` — documentation loaded into context on demand
307
+ * - `asset` — templates, images, fonts used in output (not read into context)
308
+ * - `example` — working code samples for the agent to learn from
654
309
  */
310
+ type SkillResourceType = "script" | "reference" | "asset" | "example";
655
311
  /**
656
- * Input modalities a model can accept
312
+ * A file bundled with a skill.
313
+ *
314
+ * Resources are discovered but NOT loaded into memory until the agent
315
+ * explicitly requests them. This is the L3 layer of progressive disclosure.
657
316
  */
658
- type InputModality = "text" | "image" | "audio" | "video" | "pdf";
317
+ interface SkillResource {
318
+ /** Path relative to the skill's base directory */
319
+ relativePath: string;
320
+ /** Classified resource type */
321
+ type: SkillResourceType;
322
+ /** Absolute path on disk (for local resources) */
323
+ absolutePath: string;
324
+ }
659
325
  /**
660
- * Output modalities a model can produce
326
+ * Lightweight skill metadata parsed from SKILL.md YAML frontmatter.
327
+ *
328
+ * This is the **L1 layer** — always present in the system prompt so the
329
+ * agent knows which skills exist. Kept intentionally small (~100 words each)
330
+ * to minimize baseline context cost.
331
+ *
332
+ * Uses YAML frontmatter in a SKILL.md file — the widely-adopted
333
+ * canonical format for skill definitions.
661
334
  */
662
- type OutputModality = "text" | "image" | "audio" | "video";
335
+ interface SkillMetadata {
336
+ /** Unique skill identifier (from frontmatter `name`) */
337
+ name: string;
338
+ /**
339
+ * When to use this skill — the single most important field.
340
+ *
341
+ * This description is what the agent reads to decide whether to
342
+ * activate the skill. Should contain trigger phrases that match
343
+ * task types. Written for another LLM to consume, not humans.
344
+ */
345
+ description: string;
346
+ /** Semantic version (from frontmatter, optional) */
347
+ version?: string;
348
+ /** Scope that determines override priority */
349
+ scope: SkillScope;
350
+ /** How this skill was obtained */
351
+ source: SkillSource;
352
+ /** Absolute path to the SKILL.md file */
353
+ filePath: string;
354
+ /** Directory containing the skill (parent of SKILL.md) */
355
+ baseDir: string;
356
+ /** Optional tags for categorization */
357
+ tags?: string[];
358
+ /**
359
+ * Names of skills this skill depends on.
360
+ * When this skill is loaded, its dependencies are suggested to be loaded too.
361
+ */
362
+ dependencies?: string[];
363
+ }
663
364
  /**
664
- * Comprehensive model capabilities
365
+ * Full skill content including the markdown body and resource listing.
366
+ *
367
+ * This is the **L2 layer** — loaded only when the agent calls the skill
368
+ * tool. Contains the actual instructions plus awareness of bundled resources.
665
369
  */
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;
370
+ interface SkillContent extends SkillMetadata {
371
+ /**
372
+ * The full markdown body of SKILL.md (everything below the frontmatter).
373
+ * This is the core instructional content injected into the conversation.
374
+ */
375
+ body: string;
376
+ /**
377
+ * Bundled resources discovered in the skill directory.
378
+ * Listed so the agent knows what's available but not yet loaded.
379
+ */
380
+ resources: SkillResource[];
784
381
  }
785
382
  /**
786
- * Get current network status
787
- */
788
- declare function getNetworkStatus(): NetworkStatus;
789
-
790
- /**
791
- * Model Capability Resolver for @cuylabs/agent-core
383
+ * Configuration for skill discovery and loading.
792
384
  *
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
385
+ * Controls where skills are found, how deep to scan, and which
386
+ * external conventions to support.
817
387
  *
818
- * Provides a unified API for querying model capabilities with
819
- * automatic fallback through multiple sources.
388
+ * @example
389
+ * ```typescript
390
+ * const skillConfig: SkillConfig = {
391
+ * // Scan standard locations
392
+ * externalDirs: [".agents", ".claude"],
393
+ * // Add custom skill roots
394
+ * roots: ["./company-skills"],
395
+ * // Limit scan depth for performance
396
+ * maxScanDepth: 4,
397
+ * };
398
+ * ```
820
399
  */
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>;
400
+ interface SkillConfig {
841
401
  /**
842
- * Get capabilities for a model
402
+ * Additional directories to scan for skills (absolute or relative to cwd).
403
+ * Each directory is scanned recursively for SKILL.md files.
843
404
  */
844
- getCapabilities(model: LanguageModel): Promise<ModelCapabilities>;
405
+ roots?: string[];
845
406
  /**
846
- * Get provider compatibility settings
407
+ * External skill directory names to scan in project and home directories.
408
+ *
409
+ * These are checked as `<project>/<dir>/skills/` and `~/<dir>/skills/`.
410
+ * Supports `.claude` and `.agents` directory conventions.
411
+ *
412
+ * @default [".agents", ".claude"]
847
413
  */
848
- getCompatibility(model: LanguageModel): Promise<ProviderCompatibility | undefined>;
414
+ externalDirs?: string[];
849
415
  /**
850
- * Force refresh from remote API
416
+ * Maximum directory depth to scan within each root.
417
+ * Higher values find more deeply nested skills but take longer.
418
+ *
419
+ * @default 4
851
420
  */
852
- refreshRemote(): Promise<void>;
421
+ maxScanDepth?: number;
853
422
  /**
854
- * Get current network status
423
+ * Maximum file size for SKILL.md files in bytes.
424
+ * Skills larger than this are skipped with a warning.
425
+ *
426
+ * @default 102400 (100KB)
855
427
  */
856
- getNetworkStatus(): NetworkStatus;
428
+ maxFileSize?: number;
857
429
  /**
858
- * Get resolver statistics
430
+ * Enable fetching skills from remote URLs.
431
+ * When false, `remoteUrls` is ignored.
432
+ *
433
+ * @default false
859
434
  */
860
- getStats(): {
861
- cacheSize: number;
862
- cacheLoaded: boolean;
863
- remoteFetchEnabled: boolean;
864
- networkOnline: boolean;
865
- };
435
+ enableRemote?: boolean;
866
436
  /**
867
- * Clear all cached data
437
+ * Remote skill index URLs to fetch.
438
+ *
439
+ * Each URL should serve a JSON index listing available skills.
440
+ * See `RemoteSkillIndex` for the expected format.
441
+ *
442
+ * @example
443
+ * ```typescript
444
+ * remoteUrls: ["https://skills.example.com/index.json"]
445
+ * ```
868
446
  */
869
- clearCache(): Promise<void>;
447
+ remoteUrls?: string[];
870
448
  /**
871
- * List all available models
872
- * Fetches from remote if cache is empty and remote is enabled
449
+ * Cache directory for remote skills.
450
+ * Defaults to `~/.cache/cuylabs/skills/`.
873
451
  */
874
- listModels(): Promise<ModelEntry[]>;
452
+ remoteCacheDir?: string;
875
453
  /**
876
- * List all available models grouped by provider
454
+ * Include built-in skills bundled with agent-core.
455
+ * Currently reserved for future use.
456
+ *
457
+ * @default true
877
458
  */
878
- listModelsByProvider(): Promise<Record<string, ModelEntry[]>>;
459
+ includeBuiltin?: boolean;
879
460
  }
880
461
  /**
881
- * Get the default resolver instance
462
+ * Schema for remote skill index JSON files.
463
+ *
464
+ * Hosted at a URL like `https://skills.example.com/index.json`.
465
+ * Enables team-wide or organization-wide skill sharing.
466
+ *
467
+ * @example
468
+ * ```json
469
+ * {
470
+ * "version": 1,
471
+ * "skills": [
472
+ * {
473
+ * "name": "testing",
474
+ * "files": ["SKILL.md", "scripts/run-tests.sh", "references/patterns.md"]
475
+ * }
476
+ * ]
477
+ * }
478
+ * ```
882
479
  */
883
- declare function getDefaultResolver(): ModelCapabilityResolver;
480
+ interface RemoteSkillIndex {
481
+ /** Schema version (currently 1) */
482
+ version: number;
483
+ /** List of available skills */
484
+ skills: RemoteSkillEntry[];
485
+ }
884
486
  /**
885
- * Configure the default resolver with custom options
487
+ * A single skill listing in a remote index.
886
488
  */
887
- declare function configureResolver(options: Partial<ResolverOptions>): void;
489
+ interface RemoteSkillEntry {
490
+ /** Skill name (must match the `name` in SKILL.md frontmatter) */
491
+ name: string;
492
+ /** Relative file paths to download (SKILL.md is required) */
493
+ files: string[];
494
+ }
495
+ /**
496
+ * Result of a full skill discovery pass.
497
+ */
498
+ interface SkillDiscoveryResult {
499
+ /** All discovered skills (deduplicated, scope-prioritized) */
500
+ skills: SkillMetadata[];
501
+ /** Skills that failed to load with reasons */
502
+ errors: SkillDiscoveryError[];
503
+ /** Total directories scanned */
504
+ dirsScanned: number;
505
+ /** Discovery duration in milliseconds */
506
+ durationMs: number;
507
+ }
508
+ /**
509
+ * An error encountered during skill discovery.
510
+ */
511
+ interface SkillDiscoveryError {
512
+ /** Path that caused the error */
513
+ path: string;
514
+ /** What went wrong */
515
+ reason: string;
516
+ }
888
517
 
889
518
  /**
890
- * Reasoning Configuration & Option Builders
519
+ * Prompt Pipeline Types
891
520
  *
892
- * Orchestrates capability detection and provider-specific option
893
- * building to produce ready-to-use `providerOptions` for the
894
- * Vercel AI SDK.
521
+ * Types for the layered system prompt architecture.
522
+ * The prompt pipeline composes a system prompt from multiple sources:
895
523
  *
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).
524
+ * Base Template Environment Instructions Custom Sections → Per-Turn
525
+ *
526
+ * Each layer is optional, composable, and can be toggled on/off.
901
527
  */
902
528
 
903
529
  /**
904
- * Get the reasoning configuration for a model.
530
+ * Model family identifier for prompt template selection.
905
531
  *
906
- * Uses the full capability resolver (including network lookups)
907
- * for the most accurate result.
532
+ * Each family gets a base template optimized for its strengths:
533
+ * - `anthropic`: Claude models — structured sections with XML tags
534
+ * - `openai`: GPT/o-series models — clear directives with markdown
535
+ * - `google`: Gemini models — balanced approach
536
+ * - `deepseek`: DeepSeek models — code-focused emphasis
537
+ * - `default`: Generic template for any model
908
538
  */
909
- declare function getReasoningConfig(model: LanguageModel): Promise<ReasoningConfig>;
539
+ type ModelFamily = "anthropic" | "openai" | "google" | "deepseek" | "default";
910
540
  /**
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
- */
541
+ * Runtime environment information injected into the system prompt.
542
+ * Gives the model awareness of the working context.
543
+ */
544
+ interface EnvironmentInfo {
545
+ /** Current working directory */
546
+ cwd: string;
547
+ /** Operating system (e.g. "macOS (darwin arm64)") */
548
+ platform: string;
549
+ /** Current date/time formatted string */
550
+ date: string;
551
+ /** User's shell (e.g. "/bin/zsh") */
552
+ shell?: string;
553
+ /** Active git branch, if inside a repo */
554
+ gitBranch?: string;
555
+ /** Whether the working tree is clean */
556
+ gitClean?: boolean;
557
+ /** Git repo root path */
558
+ gitRoot?: string;
559
+ }
560
+ /**
561
+ * An instruction file discovered on disk (e.g. AGENTS.md).
562
+ *
563
+ * Instruction files provide project-specific or workspace-level guidance
564
+ * that gets injected into every prompt. They're discovered by walking up
565
+ * the directory tree from cwd.
566
+ */
567
+ interface InstructionFile {
568
+ /** Absolute path to the file */
569
+ path: string;
570
+ /** File content (trimmed) */
571
+ content: string;
572
+ /** How the file was discovered */
573
+ source: "project" | "workspace" | "global";
574
+ /** Depth from cwd (0 = same directory) */
575
+ depth: number;
576
+ }
577
+ /**
578
+ * A composable section in the prompt pipeline.
579
+ *
580
+ * Sections are the building blocks of the final system prompt.
581
+ * Each section has a priority that determines its position in the output.
582
+ *
583
+ * Default priority ranges:
584
+ * - 10: Base template
585
+ * - 20: Environment block
586
+ * - 30: Instruction files
587
+ * - 50: Custom sections (default for user-added)
588
+ * - 70: Reserved for future use (e.g. Skills)
589
+ * - 90: Per-turn overrides
590
+ */
591
+ interface PromptSection {
592
+ /** Unique identifier for this section */
593
+ id: string;
594
+ /** Human-readable label (useful for debugging/logging) */
595
+ label: string;
596
+ /** The text content of this section */
597
+ content: string;
598
+ /** Sort priority — lower values appear earlier (default: 50) */
599
+ priority?: number;
600
+ /** Whether this section is active (default: true) */
601
+ enabled?: boolean;
602
+ }
603
+ /**
604
+ * Context passed to the builder for each prompt build.
605
+ *
606
+ * This provides the runtime information needed to compose
607
+ * the system prompt for a specific conversation turn.
608
+ */
609
+ interface PromptBuildContext {
610
+ /** Current working directory */
611
+ cwd: string;
612
+ /** The language model being used */
613
+ model: LanguageModel;
614
+ /** Names of available tools (for template customization) */
615
+ toolNames?: string[];
616
+ /** Per-turn additional instructions (from chat options) */
617
+ override?: string;
618
+ /** Current session ID */
619
+ sessionId?: string;
620
+ }
621
+ /**
622
+ * Configuration for the prompt pipeline.
623
+ *
624
+ * Controls which layers are active and how they behave.
625
+ * All options have sensible defaults — an empty config `{}` gives you
626
+ * the full pipeline with auto-detection.
627
+ *
628
+ * @example
629
+ * ```typescript
630
+ * // Minimal — use all defaults
631
+ * const builder = createPromptBuilder();
632
+ *
633
+ * // Custom base template but keep environment + instructions
634
+ * const builder = createPromptBuilder({
635
+ * baseTemplate: "You are a security auditor...",
636
+ * includeEnvironment: true,
637
+ * includeInstructions: true,
638
+ * });
639
+ *
640
+ * // Fully custom — disable auto features, add your own sections
641
+ * const builder = createPromptBuilder({
642
+ * includeEnvironment: false,
643
+ * includeInstructions: false,
644
+ * sections: [
645
+ * { id: "role", label: "Role", content: "You audit code.", priority: 10 },
646
+ * { id: "rules", label: "Rules", content: "Never modify files.", priority: 20 },
647
+ * ],
648
+ * });
649
+ * ```
650
+ */
651
+ interface PromptConfig {
652
+ /**
653
+ * Override the base template entirely.
654
+ * If set, replaces the model-family-specific template.
655
+ */
656
+ baseTemplate?: string;
657
+ /**
658
+ * Force a specific model family for template selection.
659
+ * Auto-detected from the model if not provided.
660
+ */
661
+ modelFamily?: ModelFamily;
662
+ /**
663
+ * Inject runtime environment info (cwd, platform, git, date).
664
+ * @default true
665
+ */
666
+ includeEnvironment?: boolean;
667
+ /**
668
+ * Discover and include instruction files (AGENTS.md, etc.).
669
+ * @default true
670
+ */
671
+ includeInstructions?: boolean;
672
+ /**
673
+ * File name patterns to search for when discovering instructions.
674
+ * Searched in each directory walking up from cwd.
675
+ *
676
+ * @default ["AGENTS.md", "CLAUDE.md", "COPILOT.md", ".cuylabs/instructions.md"]
677
+ */
678
+ instructionPatterns?: string[];
679
+ /**
680
+ * Absolute paths to global instruction files (always included
681
+ * regardless of cwd). Useful for organization-wide rules.
682
+ */
683
+ globalInstructions?: string[];
684
+ /**
685
+ * Maximum depth to walk up from cwd when searching for instructions.
686
+ * Prevents scanning the entire filesystem.
687
+ * @default 10
688
+ */
689
+ instructionMaxDepth?: number;
690
+ /**
691
+ * Maximum file size in bytes for instruction files.
692
+ * Files larger than this are skipped to avoid bloating the prompt.
693
+ * @default 51200 (50KB)
694
+ */
695
+ instructionMaxSize?: number;
696
+ /**
697
+ * Pre-defined sections to include in every prompt build.
698
+ * These are merged with auto-generated sections (template, environment, etc.).
699
+ */
700
+ sections?: PromptSection[];
701
+ /**
702
+ * Separator between sections in the final composed prompt.
703
+ * @default "\n\n"
704
+ */
705
+ separator?: string;
706
+ /**
707
+ * Skill discovery and loading configuration.
708
+ *
709
+ * When provided, the prompt pipeline will:
710
+ * 1. Discover SKILL.md files from project, user, and global directories
711
+ * 2. Inject skill summaries at PRIORITY_SKILLS (70) in the system prompt
712
+ * 3. Make `skill` and `skill_resource` tools available to the agent
713
+ *
714
+ * Skills use progressive disclosure:
715
+ * - L1 (summary): always in system prompt — names + descriptions
716
+ * - L2 (content): loaded on demand via `skill` tool
717
+ * - L3 (resources): loaded on demand via `skill_resource` tool
718
+ *
719
+ * @example
720
+ * ```typescript
721
+ * const agent = createAgent({
722
+ * model: anthropic("claude-sonnet-4-20250514"),
723
+ * prompt: {
724
+ * skills: {
725
+ * externalDirs: [".agents", ".claude"],
726
+ * roots: ["./company-skills"],
727
+ * },
728
+ * },
729
+ * });
730
+ * ```
731
+ */
732
+ skills?: SkillConfig;
733
+ }
734
+
735
+ /**
736
+ * Stream types for @cuylabs/agent-core
737
+ *
738
+ * Defines the canonical StreamChunk union and related types
739
+ * for both AI SDK native and custom stream providers.
740
+ */
741
+ /**
742
+ * Stream chunk types (AI SDK compatible + custom streams)
743
+ *
744
+ * This is the single canonical definition — used by both the
745
+ * streaming module and custom stream providers.
746
+ */
747
+ type StreamChunk = {
748
+ type: "text-start";
749
+ } | {
750
+ type: "text-delta";
751
+ text: string;
752
+ } | {
753
+ type: "text-end";
754
+ } | {
755
+ type: "reasoning-start";
756
+ id: string;
757
+ } | {
758
+ type: "reasoning-delta";
759
+ id: string;
760
+ text: string;
761
+ } | {
762
+ type: "reasoning-end";
763
+ id: string;
764
+ } | {
765
+ type: "tool-call";
766
+ toolName: string;
767
+ toolCallId: string;
768
+ input: unknown;
769
+ } | {
770
+ type: "tool-result";
771
+ toolName: string;
772
+ toolCallId: string;
773
+ output: unknown;
774
+ } | {
775
+ type: "tool-error";
776
+ toolName: string;
777
+ toolCallId: string;
778
+ error: unknown;
779
+ } | {
780
+ type: "finish-step";
781
+ usage?: {
782
+ inputTokens?: number;
783
+ outputTokens?: number;
784
+ totalTokens?: number;
785
+ };
786
+ finishReason?: string;
787
+ } | {
788
+ type: "finish";
789
+ totalUsage?: {
790
+ inputTokens?: number;
791
+ outputTokens?: number;
792
+ totalTokens?: number;
793
+ };
794
+ } | {
795
+ type: "error";
796
+ error: unknown;
797
+ } | {
798
+ type: "start-step";
799
+ } | {
800
+ type: "start";
801
+ } | {
802
+ type: "abort";
803
+ } | {
804
+ type: "computer-call";
805
+ callId: string;
806
+ action: unknown;
807
+ pendingSafetyChecks?: unknown[];
808
+ } | {
809
+ type: "step-usage";
810
+ usage: {
811
+ inputTokens: number;
812
+ outputTokens: number;
813
+ totalTokens: number;
814
+ };
815
+ };
816
+ /**
817
+ * Custom stream provider function type.
818
+ *
819
+ * This matches the signature needed for agent-core's LLM module,
820
+ * returning a StreamProviderResult-compatible object.
821
+ */
822
+ type StreamProvider = (input: StreamProviderInput) => Promise<StreamProviderResult>;
823
+ /**
824
+ * Input for custom stream providers
825
+ */
826
+ interface StreamProviderInput {
827
+ /** System prompt */
828
+ system: string;
829
+ /** Messages to send */
830
+ messages: Array<{
831
+ role: string;
832
+ content: unknown;
833
+ }>;
834
+ /** Abort signal */
835
+ abortSignal?: AbortSignal;
836
+ /** Max iterations */
837
+ maxSteps?: number;
838
+ }
839
+ /**
840
+ * Result from custom stream providers (AI SDK StreamTextResult compatible)
841
+ */
842
+ interface StreamProviderResult {
843
+ /** Async iterable of stream chunks */
844
+ fullStream: AsyncIterable<StreamChunk>;
845
+ /** Promise resolving to final text */
846
+ text: Promise<string>;
847
+ /** Promise resolving to usage stats */
848
+ usage: Promise<{
849
+ inputTokens: number;
850
+ outputTokens: number;
851
+ totalTokens: number;
852
+ }>;
853
+ /** Promise resolving to finish reason */
854
+ finishReason: Promise<string>;
855
+ }
856
+ /**
857
+ * Configuration for stream provider factory.
858
+ * Contains everything needed to create a stream provider for a specific model.
859
+ */
860
+ interface StreamProviderConfig {
861
+ /** API key to use */
862
+ apiKey?: string;
863
+ /** Display dimensions */
864
+ display?: {
865
+ width: number;
866
+ height: number;
867
+ };
868
+ /** Environment type */
869
+ environment?: string;
870
+ /** Enable debug logging */
871
+ debug?: boolean;
872
+ }
873
+ /**
874
+ * Stream provider factory - creates a stream provider for a given model.
875
+ *
876
+ * This is attached to enhanced tools (like computer tools) to allow
877
+ * the Agent to automatically configure the right stream provider
878
+ * when a model requires custom handling (e.g., OpenAI computer-use-preview).
879
+ */
880
+ type StreamProviderFactory = (modelId: string, config: StreamProviderConfig) => StreamProvider;
881
+ /**
882
+ * Enhanced tools array with additional capabilities.
883
+ *
884
+ * This extends the standard Tool.AnyInfo[] with optional metadata
885
+ * that the Agent can use for automatic configuration.
886
+ */
887
+ interface EnhancedTools extends Array<unknown> {
888
+ /**
889
+ * Factory to create a stream provider for models that need custom streaming.
890
+ * Called by the Agent when it detects a model that requires special handling.
891
+ */
892
+ __streamProviderFactory?: StreamProviderFactory;
893
+ /**
894
+ * Model patterns that require the custom stream provider.
895
+ * Used by the Agent to detect when to use the factory.
896
+ * Default patterns: ["computer-use-preview"]
897
+ */
898
+ __customStreamModels?: string[];
899
+ }
900
+
901
+ /**
902
+ * Reasoning Types & Constants
903
+ *
904
+ * Shared type definitions, level constants, and small helpers
905
+ * used across the reasoning subsystem.
906
+ */
907
+ /**
908
+ * Standard reasoning / thinking levels.
909
+ *
910
+ * | Level | Description |
911
+ * |------------|------------------------------------|
912
+ * | `"off"` | No reasoning (fastest) |
913
+ * | `"minimal"`| Very light reasoning |
914
+ * | `"low"` | Light reasoning |
915
+ * | `"medium"` | Balanced reasoning |
916
+ * | `"high"` | Deep reasoning |
917
+ * | `"xhigh"` | Maximum reasoning (where supported)|
918
+ */
919
+ type ReasoningLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
920
+ /**
921
+ * Provider-specific reasoning configuration returned by
922
+ * {@link getReasoningConfig} / {@link getReasoningConfigSync}.
923
+ */
924
+ interface ReasoningConfig {
925
+ /** Whether the model supports reasoning at all */
926
+ supportsReasoning: boolean;
927
+ /** Reasoning levels available for this model */
928
+ availableLevels: ReasoningLevel[];
929
+ /** Build provider options for a given level */
930
+ getProviderOptions: (level: ReasoningLevel) => Record<string, unknown> | undefined;
931
+ }
932
+
933
+ /**
934
+ * Model Capability Types for @cuylabs/agent-core
935
+ *
936
+ * Defines the structure for model capabilities that can be sourced from
937
+ * static patterns, local cache, or remote APIs.
938
+ */
939
+ /**
940
+ * Input modalities a model can accept
941
+ */
942
+ type InputModality = "text" | "image" | "audio" | "video" | "pdf";
943
+ /**
944
+ * Output modalities a model can produce
945
+ */
946
+ type OutputModality = "text" | "image" | "audio" | "video";
947
+ /**
948
+ * Comprehensive model capabilities
949
+ */
950
+ interface ModelCapabilities {
951
+ /** Model supports extended reasoning/thinking */
952
+ reasoning: boolean;
953
+ /** Model supports function/tool calling */
954
+ toolCalling: boolean;
955
+ /** Model supports temperature adjustment */
956
+ temperature: boolean;
957
+ /** Model supports file attachments */
958
+ attachments: boolean;
959
+ /** Model supports streaming responses */
960
+ streaming: boolean;
961
+ /** Supported input modalities */
962
+ inputModalities: InputModality[];
963
+ /** Supported output modalities */
964
+ outputModalities: OutputModality[];
965
+ /** Maximum context window in tokens */
966
+ contextWindow?: number;
967
+ /** Maximum output tokens */
968
+ maxOutput?: number;
969
+ }
970
+ /**
971
+ * Provider-specific compatibility flags
972
+ * These handle quirks in different provider implementations
973
+ */
974
+ interface ProviderCompatibility {
975
+ /** Supports OpenAI-style reasoning_effort parameter */
976
+ supportsReasoningEffort?: boolean;
977
+ /** Supports developer/system role distinction */
978
+ supportsDeveloperRole?: boolean;
979
+ /** Field name for max tokens (varies by provider) */
980
+ maxTokensField?: "max_tokens" | "max_completion_tokens";
981
+ /** Requires thinking as text tags vs structured */
982
+ requiresThinkingTags?: boolean;
983
+ /** Provider-specific thinking format */
984
+ thinkingFormat?: "openai" | "anthropic" | "google" | "zai";
985
+ }
986
+ /**
987
+ * Complete model entry with metadata
988
+ */
989
+ interface ModelEntry {
990
+ /** Model identifier (e.g., "gpt-4o", "claude-sonnet-4") */
991
+ id: string;
992
+ /** Human-readable model name */
993
+ name: string;
994
+ /** Provider identifier (e.g., "openai", "anthropic") */
995
+ provider: string;
996
+ /** Model capabilities */
997
+ capabilities: ModelCapabilities;
998
+ /** Provider-specific compatibility settings */
999
+ compatibility?: ProviderCompatibility;
1000
+ /** Cost per million tokens (input) */
1001
+ costInput?: number;
1002
+ /** Cost per million tokens (output) */
1003
+ costOutput?: number;
1004
+ /** When this entry was last updated */
1005
+ updatedAt?: string;
1006
+ }
1007
+ /**
1008
+ * Priority levels for capability sources
1009
+ */
1010
+ declare enum SourcePriority {
1011
+ /** User configuration overrides everything */
1012
+ UserConfig = 0,
1013
+ /** Local cache from previous fetch */
1014
+ LocalCache = 1,
1015
+ /** Bundled static data (build-time) */
1016
+ BundledData = 2,
1017
+ /** Pattern-based inference (fallback) */
1018
+ PatternMatch = 3,
1019
+ /** Remote API (if network available) */
1020
+ RemoteAPI = 4
1021
+ }
1022
+ /**
1023
+ * Options for the capability resolver
1024
+ */
1025
+ interface ResolverOptions {
1026
+ /** Enable remote API fetching (default: false) */
1027
+ enableRemoteFetch?: boolean;
1028
+ /** Remote API URL (default: https://models.dev) */
1029
+ remoteApiUrl?: string;
1030
+ /** Cache directory path */
1031
+ cachePath?: string;
1032
+ /** Cache TTL in milliseconds (default: 1 hour) */
1033
+ cacheTtlMs?: number;
1034
+ /** Network timeout in milliseconds (default: 10 seconds) */
1035
+ networkTimeoutMs?: number;
1036
+ /** Custom user overrides for specific models */
1037
+ modelOverrides?: Record<string, Partial<ModelCapabilities>>;
1038
+ }
1039
+
1040
+ /**
1041
+ * Extract a model ID string from a LanguageModel instance.
1042
+ */
1043
+ declare function getModelId(model: LanguageModel): string;
1044
+ /**
1045
+ * Extract a provider identifier from a LanguageModel instance.
1046
+ */
1047
+ declare function getProviderId(model: LanguageModel): string | undefined;
1048
+
1049
+ /**
1050
+ * Remote Capability Fetching for @cuylabs/agent-core
1051
+ *
1052
+ * Handles fetching model capabilities from remote APIs (e.g., models.dev).
1053
+ * Includes network status detection, timeout handling, and retry logic.
1054
+ */
1055
+
1056
+ /**
1057
+ * Network status information
1058
+ */
1059
+ interface NetworkStatus {
1060
+ /** Whether we believe the network is available */
1061
+ online: boolean;
1062
+ /** Last successful fetch timestamp */
1063
+ lastSuccess?: number;
1064
+ /** Last error if any */
1065
+ lastError?: string;
1066
+ /** Number of consecutive failures */
1067
+ failureCount: number;
1068
+ }
1069
+ /**
1070
+ * Get current network status
1071
+ */
1072
+ declare function getNetworkStatus(): NetworkStatus;
1073
+
1074
+ /**
1075
+ * Model Capability Resolver for @cuylabs/agent-core
1076
+ *
1077
+ * Main orchestrator that combines multiple capability sources:
1078
+ * 1. User overrides (highest priority)
1079
+ * 2. Local cache (fast, persisted)
1080
+ * 3. Pattern matching (always available)
1081
+ * 4. Remote API (optional, network-dependent)
1082
+ *
1083
+ * Designed for the Vercel AI SDK v6 ecosystem.
1084
+ */
1085
+
1086
+ /**
1087
+ * Resolution result with source information
1088
+ */
1089
+ interface ResolutionResult {
1090
+ /** The resolved model entry */
1091
+ entry: ModelEntry;
1092
+ /** Which source provided the primary result */
1093
+ source: SourcePriority;
1094
+ /** Whether this is a confident match */
1095
+ confident: boolean;
1096
+ /** Resolution timing in ms */
1097
+ resolveTimeMs: number;
1098
+ }
1099
+ /**
1100
+ * Model Capability Resolver
1101
+ *
1102
+ * Provides a unified API for querying model capabilities with
1103
+ * automatic fallback through multiple sources.
1104
+ */
1105
+ declare class ModelCapabilityResolver {
1106
+ private options;
1107
+ private cache;
1108
+ private sources;
1109
+ private initialized;
1110
+ private initPromise;
1111
+ constructor(options?: Partial<ResolverOptions>);
1112
+ /**
1113
+ * Initialize the resolver (load cache, optionally fetch remote)
1114
+ */
1115
+ initialize(): Promise<void>;
1116
+ /**
1117
+ * Resolve capabilities for a model
1118
+ */
1119
+ resolve(model: LanguageModel): Promise<ResolutionResult>;
1120
+ /**
1121
+ * Quick check if a model supports reasoning
1122
+ * Uses cache/patterns only for speed
1123
+ */
1124
+ supportsReasoning(model: LanguageModel): Promise<boolean>;
1125
+ /**
1126
+ * Get capabilities for a model
1127
+ */
1128
+ getCapabilities(model: LanguageModel): Promise<ModelCapabilities>;
1129
+ /**
1130
+ * Get provider compatibility settings
1131
+ */
1132
+ getCompatibility(model: LanguageModel): Promise<ProviderCompatibility | undefined>;
1133
+ /**
1134
+ * Force refresh from remote API
1135
+ */
1136
+ refreshRemote(): Promise<void>;
1137
+ /**
1138
+ * Get current network status
1139
+ */
1140
+ getNetworkStatus(): NetworkStatus;
1141
+ /**
1142
+ * Get resolver statistics
1143
+ */
1144
+ getStats(): {
1145
+ cacheSize: number;
1146
+ cacheLoaded: boolean;
1147
+ remoteFetchEnabled: boolean;
1148
+ networkOnline: boolean;
1149
+ };
1150
+ /**
1151
+ * Clear all cached data
1152
+ */
1153
+ clearCache(): Promise<void>;
1154
+ /**
1155
+ * List all available models
1156
+ * Fetches from remote if cache is empty and remote is enabled
1157
+ */
1158
+ listModels(): Promise<ModelEntry[]>;
1159
+ /**
1160
+ * List all available models grouped by provider
1161
+ */
1162
+ listModelsByProvider(): Promise<Record<string, ModelEntry[]>>;
1163
+ }
1164
+ /**
1165
+ * Get the default resolver instance
1166
+ */
1167
+ declare function getDefaultResolver(): ModelCapabilityResolver;
1168
+ /**
1169
+ * Configure the default resolver with custom options
1170
+ */
1171
+ declare function configureResolver(options: Partial<ResolverOptions>): void;
1172
+
1173
+ /**
1174
+ * Reasoning Configuration & Option Builders
1175
+ *
1176
+ * Orchestrates capability detection and provider-specific option
1177
+ * building to produce ready-to-use `providerOptions` for the
1178
+ * Vercel AI SDK.
1179
+ *
1180
+ * Two flavours of every function are provided:
1181
+ * - **Async** (`getReasoningConfig`, `buildReasoningOptions`) —
1182
+ * uses the full capability resolver (network + cache).
1183
+ * - **Sync** (`getReasoningConfigSync`, `buildReasoningOptionsSync`) —
1184
+ * uses fast pattern-matching only (no network).
1185
+ */
1186
+
1187
+ /**
1188
+ * Get the reasoning configuration for a model.
1189
+ *
1190
+ * Uses the full capability resolver (including network lookups)
1191
+ * for the most accurate result.
1192
+ */
1193
+ declare function getReasoningConfig(model: LanguageModel): Promise<ReasoningConfig>;
1194
+ /**
1195
+ * Synchronous reasoning config using pattern-matching only.
1196
+ *
1197
+ * Faster but less accurate than {@link getReasoningConfig}.
1198
+ * Good for hot paths where async is impractical.
1199
+ */
916
1200
  declare function getReasoningConfigSync(model: LanguageModel): ReasoningConfig;
917
1201
  /**
918
1202
  * Build `providerOptions` for a reasoning level (async).
@@ -1159,456 +1443,633 @@ declare function httpServer(url: string, options?: Omit<HttpTransportConfig, "tr
1159
1443
  declare function sseServer(url: string, options?: Omit<SseTransportConfig, "transport" | "url">): SseTransportConfig;
1160
1444
 
1161
1445
  /**
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
1446
+ * Agent configuration and state types for @cuylabs/agent-core
1168
1447
  *
1169
- * Each layer is optional, composable, and can be toggled on/off.
1448
+ * Defines AgentConfig (the main config surface), AgentState,
1449
+ * doom-loop types, and compaction configuration.
1170
1450
  */
1171
1451
 
1172
1452
  /**
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.
1453
+ * Agent configuration
1186
1454
  */
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;
1455
+ interface AgentConfig {
1456
+ /** Vercel AI SDK model instance */
1457
+ model: LanguageModel;
1458
+ /** System prompt */
1459
+ systemPrompt?: string;
1460
+ /** Working directory */
1461
+ cwd?: string;
1462
+ /**
1463
+ * Execution environment for tools.
1464
+ *
1465
+ * Controls *where* tools run — local machine, Docker container, SSH host, etc.
1466
+ * When not provided, defaults to `localHost(cwd)` which uses the local
1467
+ * filesystem and shell directly.
1468
+ *
1469
+ * @example
1470
+ * ```typescript
1471
+ * import { localHost } from "@cuylabs/agent-core";
1472
+ *
1473
+ * // Explicit local host (same as default)
1474
+ * const agent = createAgent({ host: localHost("/my/project") });
1475
+ *
1476
+ * // Future: Docker host
1477
+ * // const agent = createAgent({ host: dockerHost("my-container") });
1478
+ * ```
1479
+ */
1480
+ host?: ToolHost;
1481
+ /** Temperature (0-1) */
1482
+ temperature?: number;
1483
+ /** Top-p sampling */
1484
+ topP?: number;
1485
+ /** Max output tokens */
1486
+ maxOutputTokens?: number;
1487
+ /** Maximum steps (tool call iterations) */
1488
+ maxSteps?: number;
1489
+ /** Reasoning/thinking level for models that support it */
1490
+ reasoningLevel?: ReasoningLevel;
1491
+ /** Require approval for dangerous operations */
1492
+ requireApproval?: boolean;
1493
+ /**
1494
+ * Handler for doom loop detection.
1495
+ *
1496
+ * When the agent detects repeated identical tool calls (default: 3 times),
1497
+ * this handler is called to decide what to do.
1498
+ *
1499
+ * If not provided:
1500
+ * - enforceDoomLoop=true (default): throws DoomLoopError
1501
+ * - enforceDoomLoop=false: logs warning and continues
1502
+ *
1503
+ * @see DoomLoopHandler
1504
+ */
1505
+ onDoomLoop?: DoomLoopHandler;
1506
+ /**
1507
+ * Strictly enforce doom loop (throw error).
1508
+ * Only used when onDoomLoop is not provided.
1509
+ * Default: true
1510
+ */
1511
+ enforceDoomLoop?: boolean;
1512
+ /**
1513
+ * Number of identical tool calls before triggering doom loop.
1514
+ * Default: 3
1515
+ */
1516
+ doomLoopThreshold?: number;
1517
+ /**
1518
+ * Context compaction configuration.
1519
+ * Controls automatic management of the context window.
1520
+ *
1521
+ * @see CompactionConfig
1522
+ */
1523
+ compaction?: CompactionConfig;
1524
+ /**
1525
+ * Middleware for agent lifecycle hooks.
1526
+ *
1527
+ * Middleware runs in array order for "before" operations and reverse
1528
+ * order for "after" operations. Each middleware is a plain object with
1529
+ * optional hook methods — no classes or registration required.
1530
+ *
1531
+ * Middleware hooks:
1532
+ * - `beforeToolCall` — intercept tool calls (approve/deny)
1533
+ * - `afterToolCall` — transform tool results
1534
+ * - `promptSections` — inject dynamic prompt content
1535
+ * - `onEvent` — observe streaming events
1536
+ * - `onChatStart` / `onChatEnd` — lifecycle callbacks
1537
+ *
1538
+ * @example
1539
+ * ```typescript
1540
+ * const agent = createAgent({
1541
+ * middleware: [
1542
+ * approvalMiddleware({ onRequest: askUser }),
1543
+ * { name: "logger", onEvent: (e) => console.log(e.type) },
1544
+ * ],
1545
+ * });
1546
+ * ```
1547
+ */
1548
+ middleware?: AgentMiddleware[];
1549
+ /**
1550
+ * Context window size in tokens (for overflow detection).
1551
+ * If not provided, defaults to 128,000 tokens.
1552
+ *
1553
+ * This should match your model's context limit.
1554
+ */
1555
+ contextWindow?: number;
1556
+ /**
1557
+ * Custom stream provider for specialized models (e.g., OpenAI computer use).
1558
+ *
1559
+ * When provided, this function will be called instead of the default
1560
+ * AI SDK streamText(). This allows packages like @cuylabs/computer-agent
1561
+ * to inject custom streaming implementations without circular dependencies.
1562
+ *
1563
+ * The returned object must have a `fullStream` async iterable that yields
1564
+ * AI SDK compatible chunks (text-delta, tool-call, tool-result, etc.).
1565
+ */
1566
+ streamProvider?: StreamProvider;
1567
+ /**
1568
+ * MCP (Model Context Protocol) manager for external tool servers.
1569
+ *
1570
+ * When provided, the agent will connect to configured MCP servers
1571
+ * and make their tools available alongside local tools.
1572
+ *
1573
+ * @example
1574
+ * ```typescript
1575
+ * import { createMCPManager } from "@cuylabs/agent-core";
1576
+ *
1577
+ * const mcp = createMCPManager({
1578
+ * filesystem: {
1579
+ * transport: "stdio",
1580
+ * command: "npx",
1581
+ * args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
1582
+ * },
1583
+ * });
1584
+ *
1585
+ * const agent = createAgent({
1586
+ * model: openai("gpt-4o"),
1587
+ * mcp,
1588
+ * });
1589
+ * ```
1590
+ */
1591
+ mcp?: MCPManager;
1592
+ /**
1593
+ * Prompt pipeline configuration.
1594
+ *
1595
+ * When provided, enables the layered prompt system that automatically
1596
+ * composes system prompts from:
1597
+ * - Model-family-optimized base templates
1598
+ * - Runtime environment info (cwd, platform, git branch)
1599
+ * - Instruction files (AGENTS.md, CLAUDE.md discovered on disk)
1600
+ * - Custom sections you define
1601
+ * - Per-turn overrides from chat options
1602
+ *
1603
+ * When neither `prompt` nor `systemPrompt` is provided, the pipeline
1604
+ * is enabled with sensible defaults.
1605
+ *
1606
+ * When `systemPrompt` is provided without `prompt`, the flat string
1607
+ * is used directly (backward compatible).
1608
+ *
1609
+ * @example
1610
+ * ```typescript
1611
+ * const agent = createAgent({
1612
+ * model: anthropic("claude-sonnet-4-20250514"),
1613
+ * prompt: {
1614
+ * includeEnvironment: true,
1615
+ * includeInstructions: true,
1616
+ * sections: [
1617
+ * { id: "team", label: "Team Rules", content: "Use strict TypeScript." },
1618
+ * ],
1619
+ * },
1620
+ * });
1621
+ * ```
1622
+ */
1623
+ prompt?: PromptConfig;
1202
1624
  }
1203
1625
  /**
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.
1626
+ * Agent state
1209
1627
  */
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;
1628
+ interface AgentState {
1629
+ model: LanguageModel;
1630
+ systemPrompt: string;
1631
+ cwd: string;
1632
+ isStreaming: boolean;
1633
+ /** Current reasoning level */
1634
+ reasoningLevel: ReasoningLevel;
1635
+ error?: Error;
1219
1636
  }
1220
1637
  /**
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.
1638
+ * Doom loop handler response - what to do when a doom loop is detected.
1225
1639
  *
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
1640
+ * Actions:
1641
+ * - "allow" - Allow this once
1642
+ * - "remember" - Allow and remember for this session
1643
+ * - "deny" - Reject and throw error
1233
1644
  */
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
- }
1645
+ type DoomLoopAction = "allow" | "deny" | "remember";
1246
1646
  /**
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.
1647
+ * Doom loop detection request.
1648
+ * Sent to the handler when repeated tool calls are detected.
1251
1649
  */
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;
1650
+ interface DoomLoopRequest {
1651
+ /** The tool being called repeatedly */
1652
+ tool: string;
1653
+ /** How many times it's been called with same input */
1654
+ repeatCount: number;
1655
+ /** The input being passed (for context) */
1656
+ input: unknown;
1657
+ /** Session ID */
1658
+ sessionId: string;
1263
1659
  }
1264
1660
  /**
1265
- * Configuration for the prompt pipeline.
1661
+ * Handler for doom loop situations.
1266
1662
  *
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.
1663
+ * Return:
1664
+ * - "allow": Continue despite the loop
1665
+ * - "deny": Stop execution and throw error
1666
+ * - "remember": Allow and don't ask again for this tool in this session
1270
1667
  *
1271
1668
  * @example
1272
1669
  * ```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
- * ],
1670
+ * const agent = createAgent({
1671
+ * model: anthropic("claude-sonnet-4-20250514"),
1672
+ * onDoomLoop: async (req) => {
1673
+ * // Show UI asking user
1674
+ * const action = await showConfirmDialog(
1675
+ * `${req.tool} called ${req.repeatCount} times with same input. Continue?`
1676
+ * );
1677
+ * return action;
1678
+ * },
1291
1679
  * });
1292
1680
  * ```
1293
1681
  */
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;
1682
+ type DoomLoopHandler = (request: DoomLoopRequest) => Promise<DoomLoopAction>;
1683
+ /**
1684
+ * Configuration for automatic context compaction.
1685
+ *
1686
+ * Features:
1687
+ * - Auto-pruning of old tool outputs
1688
+ * - LLM-based summarization
1689
+ * - Auto-continue after compaction
1690
+ */
1691
+ interface CompactionConfig {
1310
1692
  /**
1311
- * Discover and include instruction files (AGENTS.md, etc.).
1312
- * @default true
1693
+ * Enable automatic compaction when context overflows.
1694
+ * Default: true
1313
1695
  */
1314
- includeInstructions?: boolean;
1696
+ auto?: boolean;
1315
1697
  /**
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"]
1698
+ * Enable pruning of old tool outputs before summarization.
1699
+ * This is a lightweight operation that removes tool result content.
1700
+ * Default: true
1320
1701
  */
1321
- instructionPatterns?: string[];
1702
+ prune?: boolean;
1322
1703
  /**
1323
- * Absolute paths to global instruction files (always included
1324
- * regardless of cwd). Useful for organization-wide rules.
1704
+ * Protect this many recent tokens from pruning.
1705
+ * Default: 40,000
1325
1706
  */
1326
- globalInstructions?: string[];
1707
+ protectedTokens?: number;
1327
1708
  /**
1328
- * Maximum depth to walk up from cwd when searching for instructions.
1329
- * Prevents scanning the entire filesystem.
1330
- * @default 10
1709
+ * Minimum tokens to trigger pruning.
1710
+ * Default: 20,000
1331
1711
  */
1332
- instructionMaxDepth?: number;
1712
+ pruneMinimum?: number;
1333
1713
  /**
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)
1714
+ * Custom summarization prompt for compaction.
1715
+ * If not provided, uses default prompt asking for continuation context.
1337
1716
  */
1338
- instructionMaxSize?: number;
1717
+ summaryPrompt?: string;
1339
1718
  /**
1340
- * Pre-defined sections to include in every prompt build.
1341
- * These are merged with auto-generated sections (template, environment, etc.).
1719
+ * Model to use for summarization (optional).
1720
+ * If not provided, uses the same model as the agent.
1721
+ * You might want to use a cheaper/faster model here.
1342
1722
  */
1343
- sections?: PromptSection[];
1723
+ summaryModel?: LanguageModel;
1344
1724
  /**
1345
- * Separator between sections in the final composed prompt.
1346
- * @default "\n\n"
1725
+ * Auto-continue after compaction with "Continue if you have next steps".
1726
+ * Default: true
1347
1727
  */
1348
- separator?: string;
1728
+ autoContinue?: boolean;
1349
1729
  }
1350
1730
 
1351
1731
  /**
1352
- * Agent configuration and state types for @cuylabs/agent-core
1732
+ * Middleware Types
1353
1733
  *
1354
- * Defines AgentConfig (the main config surface), AgentState,
1355
- * doom-loop types, and compaction configuration.
1734
+ * Defines the composable middleware interface for agent lifecycle hooks.
1735
+ *
1736
+ * Middleware is just a plain object with optional hook methods — no
1737
+ * base classes, no discovery, no installation. Pass it in code:
1738
+ *
1739
+ * ```typescript
1740
+ * const agent = createAgent({
1741
+ * middleware: [myLoggerMiddleware, myApprovalMiddleware],
1742
+ * });
1743
+ * ```
1744
+ *
1745
+ * Hooks run in array order for "before" operations and reverse order
1746
+ * for "after" operations (like middleware stacks everywhere).
1356
1747
  */
1357
1748
 
1358
1749
  /**
1359
- * Agent configuration
1750
+ * Action returned by `beforeToolCall` — determines whether
1751
+ * the tool call proceeds or is blocked.
1360
1752
  */
1361
- interface AgentConfig {
1362
- /** Vercel AI SDK model instance */
1363
- model: LanguageModel;
1364
- /** System prompt */
1365
- systemPrompt?: string;
1366
- /** Working directory */
1367
- cwd?: string;
1753
+ interface ToolCallDecision {
1754
+ /** Whether to allow or deny the tool call */
1755
+ action: "allow" | "deny";
1756
+ /** Reason for denial — returned to the model as the tool output */
1757
+ reason?: string;
1758
+ }
1759
+ /**
1760
+ * Agent middleware — composable lifecycle hooks.
1761
+ *
1762
+ * All methods are optional. Implement only what you need.
1763
+ *
1764
+ * Ordering:
1765
+ * - `beforeToolCall`: runs in array order, first "deny" wins
1766
+ * - `afterToolCall`: runs in reverse order (innermost first)
1767
+ * - `promptSections`: all run, sections merged
1768
+ * - `onEvent`: all run in parallel (non-blocking)
1769
+ * - `onChatStart` / `onChatEnd`: run in array order, awaited sequentially
1770
+ *
1771
+ * @example
1772
+ * ```typescript
1773
+ * // A simple logging middleware
1774
+ * const logger: AgentMiddleware = {
1775
+ * name: "logger",
1776
+ * beforeToolCall: async (tool, args) => {
1777
+ * console.log(`→ ${tool}`, args);
1778
+ * return { action: "allow" };
1779
+ * },
1780
+ * afterToolCall: async (tool, args, result) => {
1781
+ * console.log(`← ${tool}`, result.title);
1782
+ * return result;
1783
+ * },
1784
+ * };
1785
+ * ```
1786
+ */
1787
+ interface AgentMiddleware {
1788
+ /** Middleware name (for logging and debugging) */
1789
+ name: string;
1368
1790
  /**
1369
- * Execution environment for tools.
1791
+ * Intercept a tool call before execution.
1370
1792
  *
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";
1793
+ * Return `{ action: "allow" }` to proceed, or `{ action: "deny", reason }` to
1794
+ * block the call. When denied, `reason` is returned to the model as the tool
1795
+ * output so it can adjust its approach.
1378
1796
  *
1379
- * // Explicit local host (same as default)
1380
- * const agent = createAgent({ host: localHost("/my/project") });
1797
+ * Runs in array order. The first middleware that returns "deny" short-circuits
1798
+ * the chain remaining middleware and the tool itself are skipped.
1381
1799
  *
1382
- * // Future: Docker host
1383
- * // const agent = createAgent({ host: dockerHost("my-container") });
1384
- * ```
1800
+ * @param tool - Tool name (e.g. "bash", "write_file")
1801
+ * @param args - Parsed tool arguments
1802
+ * @param ctx - Tool execution context (cwd, sessionID, host, etc.)
1385
1803
  */
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;
1804
+ beforeToolCall?(tool: string, args: unknown, ctx: ToolContext): Promise<ToolCallDecision>;
1399
1805
  /**
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
1806
+ * Transform or observe a tool result after execution.
1408
1807
  *
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.
1808
+ * Receives the result and must return a result (can be the same object
1809
+ * or a modified copy). Runs in reverse array order so the outermost
1810
+ * middleware sees the final transformed result.
1426
1811
  *
1427
- * @see CompactionConfig
1812
+ * @param tool - Tool name
1813
+ * @param args - Original tool arguments
1814
+ * @param result - Tool execution result
1815
+ * @param ctx - Tool execution context
1428
1816
  */
1429
- compaction?: CompactionConfig;
1817
+ afterToolCall?(tool: string, args: unknown, result: Tool$1.ExecuteResult, ctx: ToolContext): Promise<Tool$1.ExecuteResult>;
1430
1818
  /**
1431
- * Context window size in tokens (for overflow detection).
1432
- * If not provided, defaults to 128,000 tokens.
1819
+ * Inject dynamic prompt sections at build time.
1433
1820
  *
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).
1821
+ * Called during `PromptBuilder.build()` for each middleware. Return one
1822
+ * or more sections to inject into the system prompt. Return `undefined`
1823
+ * or an empty array to inject nothing.
1439
1824
  *
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.
1825
+ * Sections follow the same priority system as static sections use
1826
+ * `priority` to control placement relative to base template (10),
1827
+ * environment (20), instructions (30), custom (50), skills (70),
1828
+ * and per-turn overrides (90).
1443
1829
  *
1444
- * The returned object must have a `fullStream` async iterable that yields
1445
- * AI SDK compatible chunks (text-delta, tool-call, tool-result, etc.).
1830
+ * @param ctx - Build context with cwd, model, toolNames, sessionId
1446
1831
  */
1447
- streamProvider?: StreamProvider;
1832
+ promptSections?(ctx: PromptBuildContext): PromptSection | PromptSection[] | undefined;
1448
1833
  /**
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.
1834
+ * Observe agent events (read-only, non-blocking).
1453
1835
  *
1454
- * @example
1455
- * ```typescript
1456
- * import { createMCPManager } from "@cuylabs/agent-core";
1836
+ * Fires for every event emitted during `chat()`. Errors thrown by
1837
+ * handlers are caught and logged — they never interrupt the stream.
1457
1838
  *
1458
- * const mcp = createMCPManager({
1459
- * filesystem: {
1460
- * transport: "stdio",
1461
- * command: "npx",
1462
- * args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
1463
- * },
1464
- * });
1839
+ * This is intentionally synchronous (void return) to prevent
1840
+ * event observers from blocking the streaming pipeline.
1465
1841
  *
1466
- * const agent = createAgent({
1467
- * model: openai("gpt-4o"),
1468
- * mcp,
1469
- * });
1470
- * ```
1842
+ * @param event - The agent event
1471
1843
  */
1472
- mcp?: MCPManager;
1844
+ onEvent?(event: AgentEvent): void;
1473
1845
  /**
1474
- * Prompt pipeline configuration.
1846
+ * Called when `chat()` starts, before the LLM stream is created.
1475
1847
  *
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
1848
+ * Use this for setup: initializing loggers, recording start time,
1849
+ * resetting per-turn state, etc. Runs in array order, awaited
1850
+ * sequentially.
1483
1851
  *
1484
- * When neither `prompt` nor `systemPrompt` is provided, the pipeline
1485
- * is enabled with sensible defaults.
1852
+ * @param sessionId - Session identifier
1853
+ * @param message - The user message being sent
1854
+ */
1855
+ onChatStart?(sessionId: string, message: string): Promise<void>;
1856
+ /**
1857
+ * Called when `chat()` completes (or errors), after all events
1858
+ * have been yielded.
1486
1859
  *
1487
- * When `systemPrompt` is provided without `prompt`, the flat string
1488
- * is used directly (backward compatible).
1860
+ * Use this for teardown: flushing logs, recording metrics, etc.
1861
+ * Runs in array order, awaited sequentially. Always called, even
1862
+ * if the stream errored.
1489
1863
  *
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
- * ```
1864
+ * @param sessionId - Session identifier
1865
+ * @param result - Completion info (usage stats and optional error)
1503
1866
  */
1504
- prompt?: PromptConfig;
1867
+ onChatEnd?(sessionId: string, result: {
1868
+ usage?: TokenUsage;
1869
+ error?: Error;
1870
+ }): Promise<void>;
1871
+ }
1872
+
1873
+ /**
1874
+ * Storage Types
1875
+ *
1876
+ * Core types for session storage with tree structure support.
1877
+ * Designed for append-only event sourcing with branching.
1878
+ */
1879
+ /**
1880
+ * Base interface for all session entries
1881
+ * Supports tree structure via id/parentId
1882
+ */
1883
+ interface EntryBase {
1884
+ /** Unique entry ID (8-char hex) */
1885
+ id: string;
1886
+ /** Parent entry ID (null for first entry after header) */
1887
+ parentId: string | null;
1888
+ /** ISO timestamp */
1889
+ timestamp: string;
1890
+ }
1891
+ /**
1892
+ * Session header - always first entry in file
1893
+ */
1894
+ interface SessionHeader {
1895
+ type: "header";
1896
+ /** Storage format version */
1897
+ version: number;
1898
+ /** Session ID */
1899
+ id: string;
1900
+ /** Working directory */
1901
+ cwd: string;
1902
+ /** ISO timestamp */
1903
+ timestamp: string;
1904
+ /** Parent session ID (if forked) */
1905
+ parentSessionId?: string;
1906
+ /** Session title */
1907
+ title?: string;
1908
+ }
1909
+ /**
1910
+ * Serialized message (dates as ISO strings)
1911
+ */
1912
+ interface SerializedMessage {
1913
+ id: string;
1914
+ role: "user" | "assistant" | "tool" | "system";
1915
+ content: string;
1916
+ createdAt: string;
1917
+ system?: string;
1918
+ finish?: string;
1919
+ tokens?: {
1920
+ inputTokens?: number;
1921
+ outputTokens?: number;
1922
+ totalTokens?: number;
1923
+ };
1924
+ cost?: number;
1925
+ error?: {
1926
+ name: string;
1927
+ message: string;
1928
+ code?: string;
1929
+ };
1930
+ /** Tool calls for assistant messages with finish=tool-calls */
1931
+ toolCalls?: Array<{
1932
+ toolCallId: string;
1933
+ toolName: string;
1934
+ args: unknown;
1935
+ }>;
1936
+ toolCallId?: string;
1937
+ toolName?: string;
1938
+ result?: unknown;
1939
+ }
1940
+ /**
1941
+ * Message entry
1942
+ */
1943
+ interface MessageEntry extends EntryBase {
1944
+ type: "message";
1945
+ message: SerializedMessage;
1946
+ }
1947
+ /**
1948
+ * Compaction/summarization entry
1949
+ * Replaces old messages with a summary
1950
+ */
1951
+ interface CompactionEntry extends EntryBase {
1952
+ type: "compaction";
1953
+ /** Summary of compacted messages */
1954
+ summary: string;
1955
+ /** ID of first entry that was kept (not compacted) */
1956
+ firstKeptEntryId: string;
1957
+ /** Token count before compaction */
1958
+ tokensBefore: number;
1959
+ /** Token count after compaction */
1960
+ tokensAfter: number;
1961
+ /** Files that were read during compacted messages */
1962
+ readFiles?: string[];
1963
+ /** Files that were modified during compacted messages */
1964
+ modifiedFiles?: string[];
1965
+ }
1966
+ /**
1967
+ * Session info/metadata update
1968
+ */
1969
+ interface MetadataEntry extends EntryBase {
1970
+ type: "metadata";
1971
+ /** Updated title */
1972
+ title?: string;
1973
+ /** User-defined name */
1974
+ name?: string;
1975
+ }
1976
+ /**
1977
+ * Branch marker - indicates a branch point
1978
+ */
1979
+ interface BranchEntry extends EntryBase {
1980
+ type: "branch";
1981
+ /** ID of the entry we're branching from */
1982
+ branchFromId: string;
1983
+ /** Optional summary of the branch */
1984
+ summary?: string;
1505
1985
  }
1506
1986
  /**
1507
- * Agent state
1987
+ * Model/config change entry
1508
1988
  */
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;
1989
+ interface ConfigChangeEntry extends EntryBase {
1990
+ type: "config_change";
1991
+ /** What changed */
1992
+ change: "model" | "reasoning_level" | "temperature" | "other";
1993
+ /** Previous value */
1994
+ from?: string;
1995
+ /** New value */
1996
+ to: string;
1517
1997
  }
1518
1998
  /**
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
1999
+ * All entry types (excluding header)
1525
2000
  */
1526
- type DoomLoopAction = "allow" | "deny" | "remember";
2001
+ type SessionEntry = MessageEntry | CompactionEntry | MetadataEntry | BranchEntry | ConfigChangeEntry;
1527
2002
  /**
1528
- * Doom loop detection request.
1529
- * Sent to the handler when repeated tool calls are detected.
2003
+ * All file entries (including header)
1530
2004
  */
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
- }
2005
+ type FileEntry = SessionHeader | SessionEntry;
1541
2006
  /**
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
- * ```
2007
+ * Session summary info (lightweight, for listing)
1562
2008
  */
1563
- type DoomLoopHandler = (request: DoomLoopRequest) => Promise<DoomLoopAction>;
2009
+ interface SessionInfo {
2010
+ /** Session ID */
2011
+ id: string;
2012
+ /** File path */
2013
+ path: string;
2014
+ /** Working directory */
2015
+ cwd: string;
2016
+ /** Session title */
2017
+ title?: string;
2018
+ /** User-defined name */
2019
+ name?: string;
2020
+ /** Parent session ID (if forked) */
2021
+ parentSessionId?: string;
2022
+ /** Creation time */
2023
+ createdAt: Date;
2024
+ /** Last modified time */
2025
+ updatedAt: Date;
2026
+ /** Number of message entries */
2027
+ messageCount: number;
2028
+ /** First user message (preview) */
2029
+ firstMessage?: string;
2030
+ }
1564
2031
  /**
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
2032
+ * Session storage interface
2033
+ * Pluggable backend for session persistence
1571
2034
  */
1572
- interface CompactionConfig {
2035
+ interface SessionStorage {
1573
2036
  /**
1574
- * Enable automatic compaction when context overflows.
1575
- * Default: true (matches OpenCode's default)
2037
+ * Create a new session
1576
2038
  */
1577
- auto?: boolean;
2039
+ create(header: SessionHeader): Promise<void>;
1578
2040
  /**
1579
- * Enable pruning of old tool outputs before summarization.
1580
- * This is a lightweight operation that removes tool result content.
1581
- * Default: true
2041
+ * Append an entry to a session
1582
2042
  */
1583
- prune?: boolean;
2043
+ append(sessionId: string, entry: SessionEntry): Promise<void>;
1584
2044
  /**
1585
- * Protect this many recent tokens from pruning.
1586
- * Default: 40,000 (like OpenCode's PRUNE_PROTECT)
2045
+ * Append multiple entries atomically
1587
2046
  */
1588
- protectedTokens?: number;
2047
+ appendBatch(sessionId: string, entries: SessionEntry[]): Promise<void>;
1589
2048
  /**
1590
- * Minimum tokens to trigger pruning.
1591
- * Default: 20,000 (like OpenCode's PRUNE_MINIMUM)
2049
+ * Read all entries from a session
1592
2050
  */
1593
- pruneMinimum?: number;
2051
+ read(sessionId: string): Promise<FileEntry[]>;
1594
2052
  /**
1595
- * Custom summarization prompt for compaction.
1596
- * If not provided, uses default prompt asking for continuation context.
2053
+ * Delete a session
1597
2054
  */
1598
- summaryPrompt?: string;
2055
+ delete(sessionId: string): Promise<boolean>;
1599
2056
  /**
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.
2057
+ * Check if session exists
1603
2058
  */
1604
- summaryModel?: LanguageModel;
2059
+ exists(sessionId: string): Promise<boolean>;
1605
2060
  /**
1606
- * Auto-continue after compaction with "Continue if you have next steps".
1607
- * Like OpenCode's behavior.
1608
- * Default: true
2061
+ * List all sessions (lightweight info only)
1609
2062
  */
1610
- autoContinue?: boolean;
2063
+ list(): Promise<SessionInfo[]>;
2064
+ /**
2065
+ * Clear all sessions
2066
+ */
2067
+ clear(): Promise<void>;
1611
2068
  }
2069
+ /**
2070
+ * Current storage format version
2071
+ */
2072
+ declare const STORAGE_VERSION = 1;
1612
2073
 
1613
2074
  /**
1614
2075
  * Session Manager
@@ -1900,7 +2361,7 @@ declare class FileStorage implements SessionStorage {
1900
2361
  * - Windows: %APPDATA%/cuylabs/
1901
2362
  *
1902
2363
  * Project identification:
1903
- * - Uses git root commit hash (like OpenCode) for git repos
2364
+ * - Uses git root commit hash for git repos
1904
2365
  * - Falls back to encoded path for non-git directories
1905
2366
  */
1906
2367
  /**
@@ -2063,6 +2524,8 @@ declare const Presets: {
2063
2524
  readonly review: Preset;
2064
2525
  readonly quick: Preset;
2065
2526
  readonly careful: Preset;
2527
+ readonly code: Preset;
2528
+ readonly watch: Preset;
2066
2529
  };
2067
2530
  /**
2068
2531
  * Create a custom preset with defaults
@@ -2079,8 +2542,6 @@ declare function createPreset(options: Partial<Preset> & {
2079
2542
  * granular per-turn tracking with minimal overhead - only touched files
2080
2543
  * are captured.
2081
2544
  *
2082
- * Inspired by OpenCode's Snapshot system and Codex's TurnDiffTracker.
2083
- *
2084
2545
  * @example
2085
2546
  * ```typescript
2086
2547
  * const tracker = createTurnTracker({ cwd: '/project' });
@@ -2388,9 +2849,9 @@ declare function summarizeEnvironment(info: EnvironmentInfo): string;
2388
2849
  * effectively take precedence in case of conflicting guidance.
2389
2850
  *
2390
2851
  * Compatible with:
2391
- * - OpenCode's AGENTS.md convention
2392
- * - Claude's CLAUDE.md convention
2393
- * - GitHub Copilot's COPILOT.md convention
2852
+ * - AGENTS.md convention
2853
+ * - CLAUDE.md convention
2854
+ * - COPILOT.md convention
2394
2855
  * - CuyLabs' own .cuylabs/instructions.md
2395
2856
  */
2396
2857
 
@@ -2475,6 +2936,242 @@ declare function loadGlobalInstructions(paths: string[], maxSize?: number): Prom
2475
2936
  */
2476
2937
  declare function formatInstructions(files: InstructionFile[], basePath?: string): string;
2477
2938
 
2939
+ /**
2940
+ * Skill Registry — in-memory store for discovered skills
2941
+ *
2942
+ * The registry is the central coordination point for the skill system.
2943
+ * It holds discovered metadata (L1), handles on-demand content loading
2944
+ * (L2/L3), provides the prompt summary for injection, and manages
2945
+ * the lifecycle of skill state.
2946
+ *
2947
+ * ```
2948
+ * discoverSkills() → SkillRegistry → promptSummary (L1 → system prompt)
2949
+ * → loadSkill() (L2 → tool response)
2950
+ * → loadResource (L3 → tool response)
2951
+ * ```
2952
+ *
2953
+ * The registry is designed to be created once per agent and shared
2954
+ * across the agent's lifetime, including across forked sub-agents.
2955
+ *
2956
+ * @example
2957
+ * ```typescript
2958
+ * const registry = await createSkillRegistry("/path/to/project", {
2959
+ * externalDirs: [".agents", ".claude"],
2960
+ * });
2961
+ *
2962
+ * // L1: summary goes into system prompt
2963
+ * const summary = registry.formatSummary();
2964
+ *
2965
+ * // L2: agent calls skill tool → full content loaded
2966
+ * const content = await registry.loadContent("testing");
2967
+ *
2968
+ * // L3: agent reads a bundled reference
2969
+ * const ref = await registry.loadResource("testing", "references/patterns.md");
2970
+ * ```
2971
+ */
2972
+
2973
+ /**
2974
+ * In-memory registry of discovered skills with lazy content loading.
2975
+ *
2976
+ * Holds L1 metadata for all skills. Content (L2) and resources (L3)
2977
+ * are loaded on demand and cached for the registry's lifetime.
2978
+ */
2979
+ declare class SkillRegistry {
2980
+ /** All discovered skill metadata indexed by name */
2981
+ private readonly skills;
2982
+ /** Cached full content for skills that have been loaded */
2983
+ private readonly contentCache;
2984
+ /** Discovery metadata */
2985
+ readonly discoveryResult: SkillDiscoveryResult;
2986
+ constructor(discoveryResult: SkillDiscoveryResult);
2987
+ /** Get a skill's metadata by name. Returns undefined if not found. */
2988
+ get(name: string): SkillMetadata | undefined;
2989
+ /** Check if a skill exists by name. */
2990
+ has(name: string): boolean;
2991
+ /** Get all skill metadata entries. */
2992
+ list(): SkillMetadata[];
2993
+ /** Number of registered skills. */
2994
+ get size(): number;
2995
+ /** Skill names as an array. */
2996
+ get names(): string[];
2997
+ /**
2998
+ * Load a skill's full content (L2: body + resource listing).
2999
+ *
3000
+ * Results are cached — subsequent calls return the cached content
3001
+ * without re-reading the filesystem.
3002
+ *
3003
+ * @param name Skill name
3004
+ * @returns Full skill content, or null if the skill doesn't exist
3005
+ */
3006
+ loadContent(name: string): Promise<SkillContent | null>;
3007
+ /**
3008
+ * Load a specific bundled resource from a skill (L3).
3009
+ *
3010
+ * The skill's content must be loaded first (via `loadContent`)
3011
+ * so the resource listing is available.
3012
+ *
3013
+ * @param skillName Skill name
3014
+ * @param relativePath Relative path to the resource within the skill dir
3015
+ * @returns Resource file content as UTF-8 string
3016
+ * @throws If the skill or resource doesn't exist
3017
+ */
3018
+ loadResource(skillName: string, relativePath: string): Promise<string>;
3019
+ /**
3020
+ * Get the list of resources for a loaded skill.
3021
+ *
3022
+ * @param name Skill name
3023
+ * @returns Resource list, or empty array if skill isn't loaded yet
3024
+ */
3025
+ getResources(name: string): SkillResource[];
3026
+ /**
3027
+ * Format a summary of all available skills for injection into the system prompt.
3028
+ *
3029
+ * This is the L1 layer — the agent sees names and descriptions of all
3030
+ * available skills, enabling it to decide which to activate via tool calls.
3031
+ *
3032
+ * The output format uses XML tags (consistent with the instruction format
3033
+ * in `formatInstructions`) for clear delineation in the prompt.
3034
+ *
3035
+ * Returns empty string if no skills are available.
3036
+ *
3037
+ * @example Output:
3038
+ * ```xml
3039
+ * <available-skills>
3040
+ *
3041
+ * You have access to the following skills. To activate a skill and load its
3042
+ * full instructions, call the `skill` tool with the skill's name.
3043
+ *
3044
+ * <skill name="testing" scope="project">
3045
+ * Write comprehensive test suites with vitest, covering unit, integration,
3046
+ * and snapshot testing patterns.
3047
+ * </skill>
3048
+ *
3049
+ * <skill name="frontend-design" scope="user">
3050
+ * Create distinctive, production-grade frontend interfaces with high design quality.
3051
+ * </skill>
3052
+ *
3053
+ * </available-skills>
3054
+ * ```
3055
+ */
3056
+ formatSummary(): string;
3057
+ /**
3058
+ * Format the full content of a loaded skill for a tool response.
3059
+ *
3060
+ * Wraps the skill body in XML with metadata, and lists bundled resources
3061
+ * so the agent knows what's available at L3.
3062
+ *
3063
+ * @param content Previously loaded skill content
3064
+ * @returns Formatted string for tool response
3065
+ */
3066
+ formatContent(content: SkillContent): string;
3067
+ /** Clear the content cache, forcing reloads on next access. */
3068
+ clearContentCache(): void;
3069
+ /** Check if a skill's content has been loaded and cached. */
3070
+ isContentLoaded(name: string): boolean;
3071
+ }
3072
+ /**
3073
+ * Create a skill registry by discovering all available skills.
3074
+ *
3075
+ * This is the recommended way to initialize the skill system.
3076
+ * It runs discovery, deduplicates by scope priority, and returns
3077
+ * a ready-to-use registry.
3078
+ *
3079
+ * @param cwd Current working directory
3080
+ * @param config Skill configuration
3081
+ * @returns A populated SkillRegistry
3082
+ *
3083
+ * @example
3084
+ * ```typescript
3085
+ * const registry = await createSkillRegistry("/path/to/project", {
3086
+ * externalDirs: [".agents", ".claude"],
3087
+ * roots: ["./company-skills"],
3088
+ * });
3089
+ *
3090
+ * console.log(`Found ${registry.size} skills`);
3091
+ * console.log(registry.formatSummary());
3092
+ * ```
3093
+ */
3094
+ declare function createSkillRegistry(cwd: string, config?: SkillConfig): Promise<SkillRegistry>;
3095
+ /**
3096
+ * Create an empty skill registry (for agents that don't use skills).
3097
+ */
3098
+ declare function emptySkillRegistry(): SkillRegistry;
3099
+
3100
+ /**
3101
+ * Middleware Runner
3102
+ *
3103
+ * Executes middleware hooks in the correct order with proper
3104
+ * error handling and short-circuit semantics.
3105
+ *
3106
+ * This is the internal engine — consumers never see it.
3107
+ * They interact with middleware through AgentConfig.middleware.
3108
+ */
3109
+
3110
+ /**
3111
+ * Middleware runner — holds an ordered list of middleware and
3112
+ * exposes methods to run each hook type with correct semantics.
3113
+ *
3114
+ * Immutable after construction. Fork creates a new runner
3115
+ * (with inherited + additional middleware).
3116
+ */
3117
+ declare class MiddlewareRunner {
3118
+ private readonly stack;
3119
+ constructor(middleware?: AgentMiddleware[]);
3120
+ /** Number of registered middleware */
3121
+ get count(): number;
3122
+ /** Whether any middleware is registered */
3123
+ get hasMiddleware(): boolean;
3124
+ /** Get the middleware list (for fork inheritance) */
3125
+ getMiddleware(): readonly AgentMiddleware[];
3126
+ /**
3127
+ * Run all `beforeToolCall` hooks in order.
3128
+ *
3129
+ * Returns `{ action: "allow" }` if all middleware allow (or have no hook).
3130
+ * Returns `{ action: "deny", reason }` on first denial — remaining
3131
+ * middleware are skipped.
3132
+ */
3133
+ runBeforeToolCall(tool: string, args: unknown, ctx: ToolContext): Promise<ToolCallDecision>;
3134
+ /**
3135
+ * Run all `afterToolCall` hooks in reverse order.
3136
+ *
3137
+ * Each hook receives the result from the previous hook (or the
3138
+ * original tool result for the first hook). Errors are caught
3139
+ * and logged — the original result passes through.
3140
+ */
3141
+ runAfterToolCall(tool: string, args: unknown, result: Tool$1.ExecuteResult, ctx: ToolContext): Promise<Tool$1.ExecuteResult>;
3142
+ /**
3143
+ * Collect prompt sections from all middleware.
3144
+ *
3145
+ * Returns a flat array of sections. Each middleware can return a single
3146
+ * section, an array of sections, or undefined/empty.
3147
+ */
3148
+ collectPromptSections(ctx: PromptBuildContext): PromptSection[];
3149
+ /**
3150
+ * Broadcast an event to all middleware observers.
3151
+ *
3152
+ * Non-blocking — errors are caught and logged. This never
3153
+ * slows down the streaming pipeline.
3154
+ */
3155
+ emitEvent(event: AgentEvent): void;
3156
+ /**
3157
+ * Run all `onChatStart` hooks in order.
3158
+ *
3159
+ * Errors are caught and logged — a broken logger should not
3160
+ * prevent the chat from starting.
3161
+ */
3162
+ runChatStart(sessionId: string, message: string): Promise<void>;
3163
+ /**
3164
+ * Run all `onChatEnd` hooks in order.
3165
+ *
3166
+ * Always called, even when the stream errored. Errors in handlers
3167
+ * are caught and logged.
3168
+ */
3169
+ runChatEnd(sessionId: string, result: {
3170
+ usage?: TokenUsage;
3171
+ error?: Error;
3172
+ }): Promise<void>;
3173
+ }
3174
+
2478
3175
  /**
2479
3176
  * PromptBuilder — The pipeline orchestrator
2480
3177
  *
@@ -2489,7 +3186,7 @@ declare function formatInstructions(files: InstructionFile[], basePath?: string)
2489
3186
  * ├──────────────────┤
2490
3187
  * │ Custom Sections │ priority 50 — user-defined content
2491
3188
  * ├──────────────────┤
2492
- * │ (Skills slot) │ priority 70 — reserved for future skill injection
3189
+ * │ Skills Summary │ priority 70 — L1 skill names + descriptions
2493
3190
  * ├──────────────────┤
2494
3191
  * │ Per-Turn Override │ priority 90 — session-specific context
2495
3192
  * └──────────────────┘
@@ -2522,7 +3219,7 @@ declare const PRIORITY_ENVIRONMENT = 20;
2522
3219
  declare const PRIORITY_INSTRUCTIONS = 30;
2523
3220
  /** Priority for custom user-defined sections (default) */
2524
3221
  declare const PRIORITY_CUSTOM = 50;
2525
- /** Priority reserved for future skill injections */
3222
+ /** Priority for skill summary injection (L1 names + descriptions) */
2526
3223
  declare const PRIORITY_SKILLS = 70;
2527
3224
  /** Priority for per-turn override content */
2528
3225
  declare const PRIORITY_OVERRIDE = 90;
@@ -2562,6 +3259,8 @@ declare class PromptBuilder {
2562
3259
  private envCache?;
2563
3260
  /** Cached instruction files */
2564
3261
  private instructionCache?;
3262
+ /** Cached skill registry */
3263
+ private skillRegistryCache?;
2565
3264
  constructor(config?: PromptConfig);
2566
3265
  /**
2567
3266
  * Build the complete system prompt from all active layers.
@@ -2573,9 +3272,10 @@ declare class PromptBuilder {
2573
3272
  * with the configured separator.
2574
3273
  *
2575
3274
  * @param context - Build context with cwd, model, and optional override
3275
+ * @param middleware - Optional middleware runner to collect dynamic sections from
2576
3276
  * @returns The composed system prompt string
2577
3277
  */
2578
- build(context: PromptBuildContext): Promise<string>;
3278
+ build(context: PromptBuildContext, middleware?: MiddlewareRunner): Promise<string>;
2579
3279
  /**
2580
3280
  * Add or replace a custom section.
2581
3281
  *
@@ -2630,6 +3330,16 @@ declare class PromptBuilder {
2630
3330
  * - You want to force a refresh on the next build()
2631
3331
  */
2632
3332
  clearCache(): void;
3333
+ /**
3334
+ * Get the skill registry for the current working directory.
3335
+ *
3336
+ * Runs skill discovery on first call and caches the result.
3337
+ * Consumers can use this to get the registry for creating skill tools.
3338
+ *
3339
+ * @param cwd Working directory for skill discovery
3340
+ * @returns The skill registry (may be empty if no skills config)
3341
+ */
3342
+ getSkillRegistry(cwd: string): Promise<SkillRegistry>;
2633
3343
  /**
2634
3344
  * Get the model family that would be used for a given model.
2635
3345
  * Useful for debugging template selection.
@@ -2698,8 +3408,8 @@ declare function createPromptBuilder(config?: PromptConfig): PromptBuilder;
2698
3408
  * Uses Vercel AI SDK v6's `prepareStep` hook for zero-overhead
2699
3409
  * integration at step boundaries (between LLM calls in a multi-step turn).
2700
3410
  *
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
3411
+ * Instead of polling a queue after each tool execution, this leverages
3412
+ * the SDK's native step lifecycle. The
2703
3413
  * `prepareStep` callback fires before every LLM call, giving us a
2704
3414
  * natural injection point with no custom loop machinery.
2705
3415
  *
@@ -2933,6 +3643,8 @@ declare class Agent {
2933
3643
  private interventionCtrl;
2934
3644
  /** Execution environment for tool operations */
2935
3645
  private host;
3646
+ /** Middleware runner for lifecycle hooks */
3647
+ private middlewareRunner;
2936
3648
  constructor(config: AgentConfig & {
2937
3649
  tools?: Tool$1.AnyInfo[];
2938
3650
  sessionManager?: SessionManager;
@@ -2999,11 +3711,6 @@ declare class Agent {
2999
3711
  chat(sessionId: string, message: string, options?: {
3000
3712
  abort?: AbortSignal;
3001
3713
  system?: string;
3002
- onApproval?: (request: {
3003
- tool: string;
3004
- args: unknown;
3005
- description: string;
3006
- }) => Promise<"allow" | "deny">;
3007
3714
  }): AsyncGenerator<AgentEvent>;
3008
3715
  /**
3009
3716
  * Ensure a session is loaded or created
@@ -3305,6 +4012,10 @@ declare class Agent {
3305
4012
  reasoningLevel?: ReasoningLevel;
3306
4013
  /** Apply a preset configuration */
3307
4014
  preset?: Preset;
4015
+ /** Override middleware (if not provided, inherits parent's middleware) */
4016
+ middleware?: AgentMiddleware[];
4017
+ /** Additional middleware appended after inherited middleware */
4018
+ additionalMiddleware?: AgentMiddleware[];
3308
4019
  }): Agent;
3309
4020
  /**
3310
4021
  * Create a sub-agent with a preset configuration.
@@ -3628,6 +4339,13 @@ interface LLMStreamInput {
3628
4339
  * Only works with the standard AI SDK path (not custom stream providers).
3629
4340
  */
3630
4341
  intervention?: InterventionController;
4342
+ /**
4343
+ * Middleware runner for lifecycle hooks.
4344
+ *
4345
+ * When provided, tool calls are wrapped with before/after hooks
4346
+ * from registered middleware.
4347
+ */
4348
+ middleware?: MiddlewareRunner;
3631
4349
  }
3632
4350
  /**
3633
4351
  * Step information
@@ -3660,7 +4378,7 @@ declare namespace LLM {
3660
4378
  * @param turnTracker - Optional turn tracker for automatic file baseline capture
3661
4379
  * @param host - Optional execution environment for tools
3662
4380
  */
3663
- function buildToolSet(tools: Record<string, Tool$1.Info>, cwd: string, sessionID: string, messageID: string, abort: AbortSignal, turnTracker?: TurnTrackerContext, host?: ToolHost): Promise<ToolSet>;
4381
+ function buildToolSet(tools: Record<string, Tool$1.Info>, cwd: string, sessionID: string, messageID: string, abort: AbortSignal, turnTracker?: TurnTrackerContext, host?: ToolHost, middleware?: MiddlewareRunner): Promise<ToolSet>;
3664
4382
  /**
3665
4383
  * Create a stream for LLM completion with retry support
3666
4384
  */
@@ -4087,7 +4805,7 @@ declare function findCutPoint(messages: Message[], protectedTokens?: number): nu
4087
4805
  /**
4088
4806
  * Prune old, large tool results from the conversation.
4089
4807
  *
4090
- * This mirrors OpenCode's `SessionCompaction.prune()` pattern:
4808
+ * Prune strategy:
4091
4809
  * - Walks backwards through messages
4092
4810
  * - Protects recent outputs (within {@link protectedTokens})
4093
4811
  * - Skips protected tools (e.g. "skill")
@@ -4208,17 +4926,80 @@ declare class ContextManager {
4208
4926
  /**
4209
4927
  * Get a snapshot of token statistics.
4210
4928
  *
4211
- * Useful for dashboards, logging, or deciding whether to prune.
4929
+ * Useful for dashboards, logging, or deciding whether to prune.
4930
+ */
4931
+ getStats(messages: (Message | ModelMessage)[]): {
4932
+ tokens: number;
4933
+ limit: number;
4934
+ available: number;
4935
+ utilizationPercent: number;
4936
+ isOverflowing: boolean;
4937
+ shouldPrune: boolean;
4938
+ };
4939
+ }
4940
+
4941
+ /**
4942
+ * Approval Middleware
4943
+ *
4944
+ * Wraps the existing approval system as an AgentMiddleware.
4945
+ * This is how approval finally gets wired into the tool execution
4946
+ * pipeline — no more dead code.
4947
+ *
4948
+ * @example
4949
+ * ```typescript
4950
+ * import { createAgent, approvalMiddleware } from "@cuylabs/agent-core";
4951
+ *
4952
+ * const agent = createAgent({
4953
+ * model: anthropic("claude-sonnet-4-20250514"),
4954
+ * tools: [read, write, bash],
4955
+ * middleware: [
4956
+ * approvalMiddleware({
4957
+ * rules: [
4958
+ * { pattern: "src/*", tool: "read_file", action: "allow" },
4959
+ * ],
4960
+ * onRequest: async (req) => {
4961
+ * // Prompt user in your UI
4962
+ * return await askUser(req);
4963
+ * },
4964
+ * }),
4965
+ * ],
4966
+ * });
4967
+ * ```
4968
+ */
4969
+
4970
+ /**
4971
+ * Configuration for the approval middleware.
4972
+ *
4973
+ * Extends the existing ApprovalConfig with middleware-specific options.
4974
+ */
4975
+ interface ApprovalMiddlewareConfig extends ApprovalConfig {
4976
+ /**
4977
+ * Custom risk classification for tools.
4978
+ *
4979
+ * Overrides the built-in risk map. Tools not listed here
4980
+ * fall back to the default classification.
4981
+ *
4982
+ * @example
4983
+ * ```typescript
4984
+ * customRisks: {
4985
+ * "my_deploy_tool": "dangerous",
4986
+ * "my_lint_tool": "safe",
4987
+ * }
4988
+ * ```
4212
4989
  */
4213
- getStats(messages: (Message | ModelMessage)[]): {
4214
- tokens: number;
4215
- limit: number;
4216
- available: number;
4217
- utilizationPercent: number;
4218
- isOverflowing: boolean;
4219
- shouldPrune: boolean;
4220
- };
4990
+ customRisks?: Record<string, RiskLevel>;
4221
4991
  }
4992
+ /**
4993
+ * Create an approval middleware from an ApprovalConfig.
4994
+ *
4995
+ * This bridges the existing `createApprovalHandler` into the middleware
4996
+ * system. The `beforeToolCall` hook checks rules, classifies risk,
4997
+ * and calls the approval handler when needed.
4998
+ *
4999
+ * @param config - Approval configuration (rules, handler, timeout, etc.)
5000
+ * @returns An AgentMiddleware that gates tool execution
5001
+ */
5002
+ declare function approvalMiddleware(config?: ApprovalMiddlewareConfig): AgentMiddleware;
4222
5003
 
4223
5004
  /**
4224
5005
  * LocalHost — executes tools on the local machine.
@@ -4387,6 +5168,620 @@ interface DockerHostOptions {
4387
5168
  */
4388
5169
  declare function dockerHost(options: DockerHostOptions): Promise<ToolHost>;
4389
5170
 
5171
+ /**
5172
+ * Skill Loader — parses SKILL.md files with YAML frontmatter
5173
+ *
5174
+ * Handles the core I/O of reading a SKILL.md file and extracting:
5175
+ * - YAML frontmatter → SkillMetadata (L1)
5176
+ * - Markdown body → SkillContent (L2)
5177
+ * - Directory scan → SkillResource[] (L3)
5178
+ *
5179
+ * The frontmatter format follows the widely-adopted SKILL.md convention:
5180
+ *
5181
+ * ```markdown
5182
+ * ---
5183
+ * name: my-skill
5184
+ * description: When to use this skill...
5185
+ * version: 1.0.0
5186
+ * tags: [testing, typescript]
5187
+ * dependencies: [another-skill]
5188
+ * ---
5189
+ *
5190
+ * # Skill Instructions
5191
+ *
5192
+ * The full markdown body with instructions for the agent...
5193
+ * ```
5194
+ *
5195
+ * We parse YAML manually (no external dependency) since frontmatter
5196
+ * is simple key-value pairs. This keeps agent-core dependency-free.
5197
+ */
5198
+
5199
+ /** The canonical skill definition filename */
5200
+ declare const SKILL_FILENAME = "SKILL.md";
5201
+ /** Maximum file size for SKILL.md (100KB) */
5202
+ declare const DEFAULT_SKILL_MAX_SIZE = 102400;
5203
+ /**
5204
+ * Parse YAML frontmatter from a markdown file content string.
5205
+ *
5206
+ * Handles the `---` delimited frontmatter block at the start of the file.
5207
+ * Returns the parsed key-value pairs and the remaining markdown body.
5208
+ *
5209
+ * We intentionally avoid a YAML library dependency — skill frontmatter
5210
+ * is simple enough that a lightweight parser covers all real-world cases.
5211
+ */
5212
+ declare function parseFrontmatter(raw: string): {
5213
+ data: Record<string, unknown>;
5214
+ body: string;
5215
+ };
5216
+ /**
5217
+ * Load skill metadata from a SKILL.md file path.
5218
+ *
5219
+ * This is the lightweight L1 load — reads just enough to populate the
5220
+ * skill summary that goes into the system prompt. Does NOT load the
5221
+ * full body content or scan for resources.
5222
+ *
5223
+ * @param filePath Absolute path to a SKILL.md file
5224
+ * @param scope The scope this skill belongs to
5225
+ * @param source How this skill was discovered
5226
+ * @param maxSize Maximum file size (skips oversized files)
5227
+ * @returns Parsed metadata, or null if the file is invalid/missing
5228
+ */
5229
+ declare function loadSkillMetadata(filePath: string, scope: SkillScope, source: SkillSource, maxSize?: number): Promise<SkillMetadata | null>;
5230
+ /**
5231
+ * Load the full skill content including body and resources.
5232
+ *
5233
+ * This is the L2+L3 load — called when the agent activates a skill.
5234
+ * Reads the full markdown body and scans for bundled resource files.
5235
+ *
5236
+ * @param metadata Previously loaded skill metadata (L1)
5237
+ * @returns Full skill content with body and resource listing
5238
+ */
5239
+ declare function loadSkillContent(metadata: SkillMetadata): Promise<SkillContent>;
5240
+ /**
5241
+ * Load a specific resource file's content.
5242
+ *
5243
+ * This is the deepest level of progressive disclosure — called only
5244
+ * when the agent explicitly requests a bundled file.
5245
+ *
5246
+ * @param resource The resource to read
5247
+ * @returns File content as UTF-8 string
5248
+ * @throws If the file doesn't exist or can't be read
5249
+ */
5250
+ declare function loadResourceContent(resource: SkillResource): Promise<string>;
5251
+ /**
5252
+ * Infer the resource type from a file's location and extension.
5253
+ *
5254
+ * Used for files that aren't in a typed subdirectory.
5255
+ * Falls back to "reference" for unknown types.
5256
+ */
5257
+ declare function inferResourceType(filePath: string): SkillResourceType;
5258
+
5259
+ /**
5260
+ * Skill Discovery — finds SKILL.md files across the filesystem
5261
+ *
5262
+ * Scans multiple roots with scope-based priority to build a complete
5263
+ * picture of available skills. Combines the best discovery patterns:
5264
+ *
5265
+ * - **Hierarchical walk**: scans `.agents/skills/` directories
5266
+ * between project root and cwd, so subdirectory skills override parents
5267
+ * - **Multi-source approach**: project dirs, user home dirs,
5268
+ * external convention dirs (.claude, .agents), config-specified paths
5269
+ * - **Standard layout**: `skills/<name>/SKILL.md` directory structure
5270
+ *
5271
+ * Discovery roots are scanned in order of increasing priority:
5272
+ *
5273
+ * 1. Built-in skills (lowest priority, reserved for future)
5274
+ * 2. Global config paths (user-specified `roots`)
5275
+ * 3. User home directory (`~/.agents/skills/`, `~/.claude/skills/`)
5276
+ * 4. Project tree walk (from git root to cwd)
5277
+ * 5. Project local (`.agents/skills/`, `.claude/skills/` in cwd)
5278
+ *
5279
+ * When two skills have the same name, the higher-priority root wins.
5280
+ */
5281
+
5282
+ /** Default external dirs to scan for skills (standard conventions) */
5283
+ declare const DEFAULT_EXTERNAL_DIRS: string[];
5284
+ /** Default max scan depth within each root */
5285
+ declare const DEFAULT_MAX_SCAN_DEPTH = 4;
5286
+ /**
5287
+ * Discover all skills from configured roots and conventions.
5288
+ *
5289
+ * This is the main discovery function. It scans all roots, loads
5290
+ * metadata for each SKILL.md found, and returns deduplicated results
5291
+ * with scope-based priority (narrower scope wins).
5292
+ *
5293
+ * @param cwd Current working directory
5294
+ * @param config Skill configuration (roots, external dirs, etc.)
5295
+ * @returns Discovery result with skills, errors, and timing
5296
+ *
5297
+ * @example
5298
+ * ```typescript
5299
+ * const result = await discoverSkills("/path/to/project", {
5300
+ * externalDirs: [".agents", ".claude"],
5301
+ * roots: ["./extra-skills"],
5302
+ * maxScanDepth: 4,
5303
+ * });
5304
+ *
5305
+ * console.log(`Found ${result.skills.length} skills in ${result.durationMs}ms`);
5306
+ * for (const skill of result.skills) {
5307
+ * console.log(` ${skill.name} (${skill.scope}): ${skill.description}`);
5308
+ * }
5309
+ * ```
5310
+ */
5311
+ declare function discoverSkills(cwd: string, config?: SkillConfig): Promise<SkillDiscoveryResult>;
5312
+
5313
+ /**
5314
+ * Skill Tools — built-in tools for agent-initiated skill loading
5315
+ *
5316
+ * Provides two tools that enable the agent to activate skills on demand:
5317
+ *
5318
+ * 1. **`skill`** — Load a skill's full instructions (L2).
5319
+ * The agent calls this when it recognizes a task matching a skill's
5320
+ * description. Returns the complete skill body with resource listings.
5321
+ *
5322
+ * 2. **`skill_resource`** — Read a specific bundled resource (L3).
5323
+ * After loading a skill, the agent can read scripts, references,
5324
+ * examples, or assets bundled with it.
5325
+ *
5326
+ * These tools expose skills as regular tools the agent can call,
5327
+ * using progressive disclosure so context is used efficiently.
5328
+ *
5329
+ * The tool descriptions are dynamic — they list all available skill names
5330
+ * and descriptions so the agent knows what's available without needing
5331
+ * a separate discovery step.
5332
+ *
5333
+ * @example
5334
+ * ```typescript
5335
+ * import { createSkillTools, createSkillRegistry } from "@cuylabs/agent-core";
5336
+ *
5337
+ * const registry = await createSkillRegistry(cwd, skillConfig);
5338
+ * const tools = createSkillTools(registry);
5339
+ *
5340
+ * const agent = createAgent({
5341
+ * model: anthropic("claude-sonnet-4-20250514"),
5342
+ * tools: [...myTools, ...tools],
5343
+ * });
5344
+ * ```
5345
+ */
5346
+
5347
+ /**
5348
+ * Create the built-in `skill` tool that lets the agent load skill instructions.
5349
+ *
5350
+ * The tool's description is dynamically generated to include all available
5351
+ * skill names and summaries, so the agent knows exactly what's available.
5352
+ *
5353
+ * @param registry The skill registry to load from
5354
+ * @returns A Tool.Info for the `skill` tool
5355
+ */
5356
+ declare function createSkillTool(registry: SkillRegistry): Tool$1.AnyInfo;
5357
+ /**
5358
+ * Create the built-in `skill_resource` tool for reading bundled files.
5359
+ *
5360
+ * This is the L3 layer — the agent calls this to read specific scripts,
5361
+ * references, examples, or assets bundled with a skill.
5362
+ *
5363
+ * @param registry The skill registry to load from
5364
+ * @returns A Tool.Info for the `skill_resource` tool
5365
+ */
5366
+ declare function createSkillResourceTool(registry: SkillRegistry): Tool$1.AnyInfo;
5367
+ /**
5368
+ * Create both skill tools (skill + skill_resource) for an agent.
5369
+ *
5370
+ * Returns an empty array if the registry has no skills, so the tools
5371
+ * don't clutter the agent's tool list when skills aren't in use.
5372
+ *
5373
+ * @param registry The skill registry
5374
+ * @returns Array of Tool.AnyInfo (0 or 2 tools)
5375
+ *
5376
+ * @example
5377
+ * ```typescript
5378
+ * const registry = await createSkillRegistry(cwd, config);
5379
+ * const skillTools = createSkillTools(registry);
5380
+ *
5381
+ * const agent = createAgent({
5382
+ * model: myModel,
5383
+ * tools: [...codeTools, ...skillTools],
5384
+ * });
5385
+ * ```
5386
+ */
5387
+ declare function createSkillTools(registry: SkillRegistry): Tool$1.AnyInfo[];
5388
+
5389
+ /**
5390
+ * Sub-Agent Types
5391
+ *
5392
+ * Type definitions for the sub-agent system: profiles, lifecycle status,
5393
+ * handles, and configuration.
5394
+ *
5395
+ * Following PowerShell verb-noun naming (Invoke-Agent, Get-Agent, Stop-Agent).
5396
+ *
5397
+ * Designed for agent-core's composable library architecture.
5398
+ *
5399
+ * Key design decisions:
5400
+ *
5401
+ * 1. **Library-first**: Unlike CLI-embedded tools, these
5402
+ * are importable building blocks. The consumer creates them with
5403
+ * `createSubAgentTools(parent)` and opts in — nothing auto-registered.
5404
+ *
5405
+ * 2. **Agent profiles over role configs**: Instead of TOML files or markdown
5406
+ * agents, we use composable `AgentProfile` objects that wrap existing
5407
+ * `Preset` + extra metadata the LLM can read.
5408
+ *
5409
+ * 3. **Progressive lifecycle**: Start with synchronous `invoke_agent` (simplest),
5410
+ * add `wait_agent`/`close_agent` only when async profiles exist.
5411
+ *
5412
+ * 4. **Depth safety**: Hard limit on nesting with configurable max depth.
5413
+ *
5414
+ * @packageDocumentation
5415
+ */
5416
+
5417
+ /**
5418
+ * An agent profile defines a specialized sub-agent personality.
5419
+ *
5420
+ * Profiles combine a `Preset` (tool filtering, temperature, etc.) with
5421
+ * LLM-facing metadata (name, description, examples) so the model knows
5422
+ * when and how to delegate.
5423
+ *
5424
+ * Profiles define specialized sub-agent personalities with roles
5425
+ * like explorer, worker, planner, and reviewer.
5426
+ *
5427
+ * @example
5428
+ * ```typescript
5429
+ * const explorer: AgentProfile = {
5430
+ * name: "explorer",
5431
+ * description: "Fast, read-only codebase exploration. Use when you need to find files, search code, or understand project structure.",
5432
+ * preset: Presets.explore,
5433
+ * maxSteps: 30,
5434
+ * };
5435
+ * ```
5436
+ */
5437
+ interface AgentProfile {
5438
+ /** Unique identifier used in tool calls (lowercase, no spaces) */
5439
+ name: string;
5440
+ /**
5441
+ * When to use this sub-agent — written for the LLM to consume.
5442
+ *
5443
+ * This is the most important field. The parent LLM reads this to decide
5444
+ * whether to delegate. Include clear trigger phrases.
5445
+ */
5446
+ description: string;
5447
+ /**
5448
+ * Preset to apply (tool filtering, temperature, system prompt, etc.).
5449
+ * If omitted, inherits the parent agent's configuration.
5450
+ */
5451
+ preset?: Preset;
5452
+ /**
5453
+ * Override system prompt for this profile.
5454
+ * Takes precedence over preset's systemPrompt.
5455
+ * Use `{basePrompt}` to include the parent's system prompt.
5456
+ */
5457
+ systemPrompt?: string;
5458
+ /**
5459
+ * Override model for this profile.
5460
+ * Useful for using cheaper/faster models for simple tasks.
5461
+ */
5462
+ model?: LanguageModel;
5463
+ /** Max steps for this sub-agent (default: parent's maxSteps) */
5464
+ maxSteps?: number;
5465
+ /**
5466
+ * Additional middleware appended to the parent's middleware stack.
5467
+ * Useful for adding logging or guardrails specific to this profile.
5468
+ */
5469
+ additionalMiddleware?: AgentMiddleware[];
5470
+ /**
5471
+ * Additional tools beyond what the preset allows.
5472
+ * These are merged with the preset-filtered tools.
5473
+ */
5474
+ additionalTools?: Tool$1.AnyInfo[];
5475
+ /**
5476
+ * Whether this sub-agent can itself spawn further sub-agents.
5477
+ * When false, the `invoke_agent` tool is removed from the child.
5478
+ *
5479
+ * @default false — sub-agents cannot nest by default (safe default)
5480
+ */
5481
+ canSpawn?: boolean;
5482
+ }
5483
+ /**
5484
+ * Lifecycle status of a running or completed sub-agent.
5485
+ *
5486
+ * Simplified lifecycle for the fire-and-forget-with-optional-wait model.
5487
+ */
5488
+ type SubAgentStatus = {
5489
+ state: "running";
5490
+ } | {
5491
+ state: "completed";
5492
+ response: string;
5493
+ usage: SubAgentUsage;
5494
+ } | {
5495
+ state: "errored";
5496
+ error: string;
5497
+ } | {
5498
+ state: "cancelled";
5499
+ };
5500
+ /**
5501
+ * Token usage tracking for a sub-agent run.
5502
+ */
5503
+ interface SubAgentUsage {
5504
+ inputTokens: number;
5505
+ outputTokens: number;
5506
+ totalTokens: number;
5507
+ }
5508
+ /**
5509
+ * A handle to a running or completed sub-agent.
5510
+ *
5511
+ * Returned by async invocations. Allows the parent to wait for completion,
5512
+ * check status, or cancel.
5513
+ */
5514
+ interface SubAgentHandle {
5515
+ /** Unique ID for this sub-agent invocation */
5516
+ id: string;
5517
+ /** Human-readable name (from profile or auto-generated) */
5518
+ name: string;
5519
+ /** Session ID of the child agent's session */
5520
+ sessionId: string;
5521
+ /** Current status */
5522
+ status: SubAgentStatus;
5523
+ /** The promise that resolves when the sub-agent completes */
5524
+ promise: Promise<SubAgentCompletedResult>;
5525
+ /** Abort controller to cancel this sub-agent */
5526
+ abort: AbortController;
5527
+ }
5528
+ /**
5529
+ * The final result of a completed sub-agent.
5530
+ */
5531
+ interface SubAgentCompletedResult {
5532
+ /** Final response text */
5533
+ response: string;
5534
+ /** Session ID (for audit/inspection) */
5535
+ sessionId: string;
5536
+ /** Token usage */
5537
+ usage: SubAgentUsage;
5538
+ /** Tool calls the sub-agent made */
5539
+ toolCalls: Array<{
5540
+ name: string;
5541
+ result: unknown;
5542
+ }>;
5543
+ }
5544
+ /**
5545
+ * Configuration for `createSubAgentTools()`.
5546
+ *
5547
+ * Controls which profiles are available, concurrency limits,
5548
+ * depth limits, and whether async mode is enabled.
5549
+ */
5550
+ interface SubAgentToolConfig {
5551
+ /**
5552
+ * Available sub-agent profiles.
5553
+ * Each profile defines a specialized persona the LLM can invoke.
5554
+ * At least one profile is required.
5555
+ */
5556
+ profiles: AgentProfile[];
5557
+ /**
5558
+ * Maximum concurrent sub-agents (across all profiles).
5559
+ * @default 6
5560
+ */
5561
+ maxConcurrent?: number;
5562
+ /**
5563
+ * Maximum nesting depth for recursive sub-agent spawning.
5564
+ * Depth 0 = no spawning, 1 = one level of sub-agents, etc.
5565
+ *
5566
+ * When a sub-agent reaches the depth limit, the `invoke_agent`
5567
+ * tool is automatically removed from its tool set.
5568
+ *
5569
+ * @default 2
5570
+ */
5571
+ maxDepth?: number;
5572
+ /**
5573
+ * Current depth (internal — incremented when creating child tools).
5574
+ * Users should not set this directly.
5575
+ *
5576
+ * @internal
5577
+ */
5578
+ currentDepth?: number;
5579
+ /**
5580
+ * Enable async mode with `wait_agent` and `close_agent` tools.
5581
+ *
5582
+ * When false (default), `invoke_agent` blocks until the sub-agent
5583
+ * completes and returns the result directly — simpler but sequential.
5584
+ *
5585
+ * When true, `invoke_agent` returns immediately with an agent ID,
5586
+ * and the LLM must call `wait_agent` to collect results. This enables
5587
+ * parallel sub-agent execution.
5588
+ *
5589
+ * @default false
5590
+ */
5591
+ async?: boolean;
5592
+ /**
5593
+ * Title prefix for sub-agent sessions.
5594
+ * @default "Sub-agent"
5595
+ */
5596
+ sessionTitlePrefix?: string;
5597
+ }
5598
+ /** Default maximum concurrent sub-agents */
5599
+ declare const DEFAULT_MAX_CONCURRENT = 6;
5600
+ /** Default maximum nesting depth */
5601
+ declare const DEFAULT_MAX_SPAWN_DEPTH = 2;
5602
+ /** Default session title prefix */
5603
+ declare const DEFAULT_SESSION_TITLE_PREFIX = "Sub-agent";
5604
+
5605
+ /**
5606
+ * Sub-Agent Tracker — in-memory state for active sub-agents
5607
+ *
5608
+ * Manages the lifecycle of running sub-agents: slots, handles,
5609
+ * concurrency limits, and depth enforcement.
5610
+ *
5611
+ * Handles spawn slot reservation and depth checking, designed for
5612
+ * single-process TypeScript/async usage.
5613
+ *
5614
+ * @packageDocumentation
5615
+ */
5616
+
5617
+ /**
5618
+ * Tracks active sub-agent handles and enforces concurrency/depth limits.
5619
+ *
5620
+ * One tracker is created per `createSubAgentTools()` call and shared
5621
+ * across all sub-agent tool instances for that parent agent.
5622
+ */
5623
+ declare class SubAgentTracker {
5624
+ /** Active sub-agent handles by ID */
5625
+ private readonly handles;
5626
+ /** Maximum concurrent sub-agents */
5627
+ readonly maxConcurrent: number;
5628
+ /** Maximum nesting depth */
5629
+ readonly maxDepth: number;
5630
+ /** Current depth in the spawn hierarchy */
5631
+ readonly currentDepth: number;
5632
+ constructor(config?: Partial<SubAgentToolConfig>);
5633
+ /** Number of currently active (running) sub-agents */
5634
+ get activeCount(): number;
5635
+ /** Total tracked handles (including completed) */
5636
+ get totalCount(): number;
5637
+ /**
5638
+ * Check if a new sub-agent can be spawned.
5639
+ * Returns an error message if not, undefined if OK.
5640
+ */
5641
+ canSpawn(): string | undefined;
5642
+ /** Whether the current depth allows further spawning */
5643
+ get canNest(): boolean;
5644
+ /** Register a new sub-agent handle */
5645
+ register(handle: SubAgentHandle): void;
5646
+ /** Get a handle by ID */
5647
+ get(id: string): SubAgentHandle | undefined;
5648
+ /** Update a handle's status */
5649
+ updateStatus(id: string, status: SubAgentStatus): void;
5650
+ /** Get all handles */
5651
+ list(): SubAgentHandle[];
5652
+ /** Get all running handles */
5653
+ running(): SubAgentHandle[];
5654
+ /** Get all completed handles */
5655
+ completed(): SubAgentHandle[];
5656
+ /**
5657
+ * Cancel a running sub-agent.
5658
+ * Signals the abort controller and updates status.
5659
+ */
5660
+ cancel(id: string): boolean;
5661
+ /**
5662
+ * Cancel all running sub-agents.
5663
+ */
5664
+ cancelAll(): void;
5665
+ /**
5666
+ * Wait for a specific sub-agent to complete.
5667
+ * Returns the result or throws if not found.
5668
+ */
5669
+ wait(id: string, timeoutMs?: number): Promise<SubAgentCompletedResult>;
5670
+ /**
5671
+ * Wait for any one of the given sub-agents to complete.
5672
+ * Returns the first result.
5673
+ */
5674
+ waitAny(ids: string[], timeoutMs?: number): Promise<{
5675
+ id: string;
5676
+ result: SubAgentCompletedResult;
5677
+ } | {
5678
+ timedOut: true;
5679
+ }>;
5680
+ /**
5681
+ * Create a child tracker configuration for a nested sub-agent.
5682
+ * Increments the depth counter.
5683
+ */
5684
+ childConfig(): Partial<SubAgentToolConfig>;
5685
+ }
5686
+
5687
+ /**
5688
+ * Sub-Agent Tools — LLM-callable tools for delegating work to specialized agents
5689
+ *
5690
+ * Provides three tools following PowerShell verb-noun naming:
5691
+ *
5692
+ * 1. **`invoke_agent`** — Spawn a sub-agent for a focused task.
5693
+ * In sync mode, blocks until completion and returns the result.
5694
+ * In async mode, returns an agent ID immediately.
5695
+ *
5696
+ * 2. **`wait_agent`** — Wait for one or more async sub-agents to complete.
5697
+ * Supports timeout. Only available in async mode.
5698
+ *
5699
+ * 3. **`close_agent`** — Cancel a running sub-agent.
5700
+ * Only available in async mode.
5701
+ *
5702
+ * These tools bridge agent-core's existing `fork().run()` primitives
5703
+ * into the LLM's tool-calling interface, letting the model itself
5704
+ * decide when to delegate.
5705
+ *
5706
+ * Key differentiators from competitors:
5707
+ * - **Library-first**: Created via `createSubAgentTools(parent)`, not auto-registered
5708
+ * - **Profile-based**: Sub-agent types defined as composable `AgentProfile` objects
5709
+ * - **Depth-safe**: Automatic depth limiting removes `invoke_agent` at max depth
5710
+ * - **Dual-mode**: Sync (simple) or async (parallel) — consumer chooses
5711
+ *
5712
+ * @example
5713
+ * ```typescript
5714
+ * import { createSubAgentTools, createAgent, Presets } from "@cuylabs/agent-core";
5715
+ *
5716
+ * const agent = createAgent({ model, tools: codeTools });
5717
+ * const subAgentTools = createSubAgentTools(agent, {
5718
+ * profiles: [
5719
+ * { name: "explorer", description: "Fast read-only search", preset: Presets.explore },
5720
+ * { name: "reviewer", description: "Thorough code review", preset: Presets.review },
5721
+ * ],
5722
+ * });
5723
+ *
5724
+ * const fullAgent = createAgent({
5725
+ * model,
5726
+ * tools: [...codeTools, ...subAgentTools],
5727
+ * });
5728
+ * ```
5729
+ *
5730
+ * @packageDocumentation
5731
+ */
5732
+
5733
+ /**
5734
+ * Create the sub-agent tools for an agent.
5735
+ *
5736
+ * Returns an array of tools that the LLM can call to delegate tasks:
5737
+ * - `invoke_agent` — always included
5738
+ * - `wait_agent` — only in async mode
5739
+ * - `close_agent` — only in async mode
5740
+ *
5741
+ * These tools use the parent agent's `fork().run()` under the hood,
5742
+ * so sub-agents inherit the parent's storage, host, MCP connections,
5743
+ * and (optionally) middleware.
5744
+ *
5745
+ * @param parent The parent agent that sub-agents will be forked from
5746
+ * @param config Sub-agent configuration (profiles, limits, mode)
5747
+ * @returns Array of Tool.AnyInfo to add to the agent's tools
5748
+ *
5749
+ * @example
5750
+ * ```typescript
5751
+ * import {
5752
+ * createAgent,
5753
+ * createSubAgentTools,
5754
+ * Presets,
5755
+ * } from "@cuylabs/agent-core";
5756
+ *
5757
+ * const parent = createAgent({ model, tools: codeTools });
5758
+ *
5759
+ * // Sync mode (simple — invoke blocks until done)
5760
+ * const subTools = createSubAgentTools(parent, {
5761
+ * profiles: [
5762
+ * { name: "explorer", description: "Read-only search", preset: Presets.explore },
5763
+ * { name: "reviewer", description: "Code review", preset: Presets.review },
5764
+ * ],
5765
+ * });
5766
+ *
5767
+ * // Async mode (parallel — invoke returns ID, wait collects result)
5768
+ * const asyncSubTools = createSubAgentTools(parent, {
5769
+ * profiles: [
5770
+ * { name: "explorer", description: "Read-only search", preset: Presets.explore },
5771
+ * { name: "worker", description: "Implementation", preset: Presets.careful },
5772
+ * ],
5773
+ * async: true,
5774
+ * maxConcurrent: 4,
5775
+ * });
5776
+ *
5777
+ * const agent = createAgent({
5778
+ * model,
5779
+ * tools: [...codeTools, ...subTools],
5780
+ * });
5781
+ * ```
5782
+ */
5783
+ declare function createSubAgentTools(parent: Agent, config: SubAgentToolConfig): Tool$1.AnyInfo[];
5784
+
4390
5785
  type AdapterKind = "openai" | "anthropic" | "google" | "openai-compatible";
4391
5786
  type AdapterSettings = {
4392
5787
  apiKey?: string;
@@ -4416,4 +5811,4 @@ type Resolver = (key: string) => Promise<LanguageModel>;
4416
5811
  type SyncResolver = (key: string) => LanguageModel;
4417
5812
  declare function createResolver(directory: Directory): Resolver;
4418
5813
 
4419
- 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 SyncResolver, 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 };
5814
+ export { type AdapterSettings, Agent, type AgentConfig, type AgentEvent, type AgentMiddleware, type AgentProfile, type AgentState, type AgentStatus, type AnyStreamResult, type AppliedPreset, type ApprovalAction, type ApprovalConfig, ApprovalDeniedError, type ApprovalEvent, type ApprovalHandler, type ApprovalMiddlewareConfig, 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_EXTERNAL_DIRS, DEFAULT_INSTRUCTION_PATTERNS, DEFAULT_MAX_CONCURRENT, DEFAULT_MAX_DEPTH, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_SCAN_DEPTH, DEFAULT_MAX_SPAWN_DEPTH, DEFAULT_RETRY_CONFIG, DEFAULT_SESSION_TITLE_PREFIX, DEFAULT_SKILL_MAX_SIZE, 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, MiddlewareRunner, 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 RemoteSkillEntry, type RemoteSkillIndex, type ResolverOptions, type ResponseHeaders, type RetryConfig, type RetryHandlerOptions, type RetryState, type RiskLevel, SKILL_FILENAME, STORAGE_VERSION, type Session, type SessionContext, type SessionEntry, type SessionHeader, type SessionInfo, SessionManager, type SessionStorage, type SkillConfig, type SkillContent, type SkillDiscoveryError, type SkillDiscoveryResult, type SkillMetadata, SkillRegistry, type SkillResource, type SkillResourceType, type SkillScope, type SkillSource, type SkillSourceType, type SseTransportConfig, type StdioTransportConfig, type StepInfo, type StreamChunk, type StreamInput, type StreamProvider, type StreamProviderConfig, type StreamProviderFactory, type StreamProviderInput, type StreamProviderResult, type SubAgentCompletedResult, type SubAgentHandle, type SubAgentResult, type SubAgentStatus, type SubAgentToolConfig, SubAgentTracker, type SubAgentUsage, type SummarizationOptions, type SyncResolver, type SystemMessage, type TokenUsage, Tool$1 as Tool, type ToolCallDecision, ToolContext, ToolHost, type ToolMessage, type TrackedToolMetadata, TurnChangeTracker, type TurnFileChange, type TurnSummary, type TurnTrackerConfig, TurnTrackerContext, type UndoResult, type UserMessage, applyPreset, approvalMiddleware, buildMessagesFromEntries, buildReasoningOptions, buildReasoningOptionsSync, calculateDelay, clearCheckpoints, configureDefaultSessionManager, configureResolver, createAgent, createApprovalHandler, createCheckpointManager, createMCPManager, createPreset, createPromptBuilder, createResolver, createRetryHandler, createRetryState, createSkillRegistry, createSkillResourceTool, createSkillTool, createSkillTools, createSubAgentTools, createTurnTracker, defineServer, deserializeMessage, detectModelFamily, discoverInstructions, discoverSkills, dockerHost, emptySkillRegistry, 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, inferResourceType, isContextOverflowing, isRetryable, isRetryableCategory, loadGlobalInstructions, loadResourceContent, loadSkillContent, loadSkillMetadata, localHost, mergePresets, parseFrontmatter, parseJSONL, processStream, pruneContext, pruneToolResults, runConcurrent, serializeMessage, shouldCaptureBaseline, shouldPruneContext, shouldRetry, sleep, sseServer, stdioServer, summarizeEnvironment, supportsReasoning, supportsReasoningSync, toJSONL, withFileTracking, withRetry };