@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/README.md +162 -36
- package/dist/{index-eud06GDQ.d.ts → index-BlSTfS-W.d.ts} +38 -14
- package/dist/index.d.ts +2432 -1037
- package/dist/index.js +1571 -61
- package/dist/tool/index.d.ts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,16 +4,20 @@ Embeddable AI agent infrastructure using Vercel AI SDK. Core building blocks for
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Agent Framework**
|
|
8
|
-
- **Session Management**
|
|
9
|
-
- **LLM Streaming**
|
|
10
|
-
- **Error Resilience**
|
|
11
|
-
- **Context Management**
|
|
12
|
-
- **Tool Framework**
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
7
|
+
- **Agent Framework** — Create AI agents with tool support and streaming responses
|
|
8
|
+
- **Session Management** — Persistent conversation state with branching support
|
|
9
|
+
- **LLM Streaming** — Real-time streaming with proper backpressure handling
|
|
10
|
+
- **Error Resilience** — Automatic retry with exponential backoff
|
|
11
|
+
- **Context Management** — Token counting and automatic context pruning
|
|
12
|
+
- **Tool Framework** — Type-safe tool definitions with Zod schemas
|
|
13
|
+
- **Middleware** — Composable lifecycle hooks for tool interception, prompt injection, logging, and guardrails
|
|
14
|
+
- **Skills** — Modular knowledge packs with progressive disclosure (L1 summary → L2 content → L3 resources)
|
|
15
|
+
- **Approval System** — Configurable tool approval via middleware (rules + interactive prompts)
|
|
16
|
+
- **Checkpoint System** — Undo/restore capabilities for file operations
|
|
17
|
+
- **Model Capabilities** — Runtime model capability detection
|
|
18
|
+
- **MCP Support** — Extend agents with Model Context Protocol servers
|
|
19
|
+
- **Sub-Agents** — Fork agents with inherited config, run tasks in parallel, or let the LLM delegate via `invoke_agent`
|
|
20
|
+
- **Presets** — Reusable agent configurations with tool filtering
|
|
17
21
|
|
|
18
22
|
## Installation
|
|
19
23
|
|
|
@@ -44,22 +48,23 @@ import { openai } from "@ai-sdk/openai";
|
|
|
44
48
|
import { z } from "zod";
|
|
45
49
|
|
|
46
50
|
// Define tools
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
const greet = Tool.define("greet", {
|
|
52
|
+
description: "Greet a user by name",
|
|
53
|
+
parameters: z.object({
|
|
54
|
+
name: z.string().describe("Name to greet"),
|
|
55
|
+
}),
|
|
56
|
+
execute: async ({ name }) => ({
|
|
57
|
+
title: "Greeting",
|
|
58
|
+
output: `Hello, ${name}!`,
|
|
59
|
+
metadata: {},
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
57
62
|
|
|
58
63
|
// Create agent
|
|
59
64
|
const agent = createAgent({
|
|
60
65
|
model: openai("gpt-4o"),
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
tools: [greet],
|
|
67
|
+
systemPrompt: "You are a helpful assistant.",
|
|
63
68
|
});
|
|
64
69
|
|
|
65
70
|
// Stream responses
|
|
@@ -68,7 +73,7 @@ for await (const event of agent.chat("session-1", "Hello!")) {
|
|
|
68
73
|
case "text-delta":
|
|
69
74
|
process.stdout.write(event.text);
|
|
70
75
|
break;
|
|
71
|
-
case "tool-
|
|
76
|
+
case "tool-start":
|
|
72
77
|
console.log(`Calling tool: ${event.toolName}`);
|
|
73
78
|
break;
|
|
74
79
|
case "tool-result":
|
|
@@ -150,26 +155,131 @@ await sessions.createSession({ id: "my-session" });
|
|
|
150
155
|
### Tool Framework
|
|
151
156
|
|
|
152
157
|
```typescript
|
|
153
|
-
import {
|
|
158
|
+
import { Tool } from "@cuylabs/agent-core";
|
|
154
159
|
import { z } from "zod";
|
|
155
160
|
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
description: "Perform a calculation",
|
|
161
|
+
const calculator = Tool.define("calculator", {
|
|
162
|
+
description: "Evaluate a math expression",
|
|
159
163
|
parameters: z.object({
|
|
160
164
|
expression: z.string(),
|
|
161
165
|
}),
|
|
162
166
|
execute: async ({ expression }, ctx) => {
|
|
163
|
-
// ctx provides
|
|
164
|
-
return
|
|
167
|
+
// ctx provides sessionID, cwd, abort signal, host, etc.
|
|
168
|
+
return {
|
|
169
|
+
title: "Calculator",
|
|
170
|
+
output: String(eval(expression)), // Don't actually do this!
|
|
171
|
+
metadata: {},
|
|
172
|
+
};
|
|
165
173
|
},
|
|
166
174
|
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Middleware
|
|
167
178
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
179
|
+
Composable lifecycle hooks for tool interception, prompt injection, logging, and guardrails:
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { createAgent, type AgentMiddleware, approvalMiddleware } from "@cuylabs/agent-core";
|
|
183
|
+
|
|
184
|
+
// Simple logging middleware
|
|
185
|
+
const logger: AgentMiddleware = {
|
|
186
|
+
name: "logger",
|
|
187
|
+
async beforeToolCall(tool, args) {
|
|
188
|
+
console.log(`→ ${tool}`, args);
|
|
189
|
+
return { action: "allow" };
|
|
190
|
+
},
|
|
191
|
+
async afterToolCall(tool, _args, result) {
|
|
192
|
+
console.log(`← ${tool}`, result.title);
|
|
193
|
+
return result;
|
|
194
|
+
},
|
|
195
|
+
async onChatStart(sessionId, message) {
|
|
196
|
+
console.log(`Chat started: ${sessionId}`);
|
|
197
|
+
},
|
|
198
|
+
async onChatEnd(sessionId, { usage, error }) {
|
|
199
|
+
console.log(`Chat ended: ${usage?.totalTokens} tokens`);
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// Guardrail middleware — block dangerous tools
|
|
204
|
+
const guardrail: AgentMiddleware = {
|
|
205
|
+
name: "guardrail",
|
|
206
|
+
async beforeToolCall(tool) {
|
|
207
|
+
if (tool === "bash") {
|
|
208
|
+
return { action: "deny", reason: "Shell commands are disabled." };
|
|
209
|
+
}
|
|
210
|
+
return { action: "allow" };
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const agent = createAgent({
|
|
215
|
+
model: openai("gpt-4o"),
|
|
216
|
+
tools: myTools,
|
|
217
|
+
middleware: [
|
|
218
|
+
logger,
|
|
219
|
+
guardrail,
|
|
220
|
+
approvalMiddleware({
|
|
221
|
+
rules: [{ pattern: "*", tool: "read_file", action: "allow" }],
|
|
222
|
+
onRequest: async (req) => {
|
|
223
|
+
// Prompt user for approval
|
|
224
|
+
return await askUser(req);
|
|
225
|
+
},
|
|
226
|
+
}),
|
|
227
|
+
],
|
|
228
|
+
});
|
|
171
229
|
```
|
|
172
230
|
|
|
231
|
+
Middleware hooks: `beforeToolCall`, `afterToolCall`, `promptSections`, `onEvent`, `onChatStart`, `onChatEnd`. Sub-agents inherit middleware via `fork()`.
|
|
232
|
+
|
|
233
|
+
### Skills
|
|
234
|
+
|
|
235
|
+
Modular knowledge packs with three-level progressive disclosure:
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import { createAgent, createSkillRegistry, createSkillTools } from "@cuylabs/agent-core";
|
|
239
|
+
|
|
240
|
+
// Discover skills from SKILL.md files
|
|
241
|
+
const registry = await createSkillRegistry(process.cwd(), {
|
|
242
|
+
externalDirs: [".agents", ".claude"],
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Create skill tools (skill + skill_resource)
|
|
246
|
+
const skillTools = createSkillTools(registry);
|
|
247
|
+
|
|
248
|
+
// Inject L1 summary into system prompt, give agent skill tools
|
|
249
|
+
const agent = createAgent({
|
|
250
|
+
model: openai("gpt-4o"),
|
|
251
|
+
tools: [...myTools, ...skillTools],
|
|
252
|
+
systemPrompt: `You are a coding assistant.\n\n${registry.formatSummary()}`,
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Skills use the `SKILL.md` format with YAML frontmatter. See [examples/skills/](examples/skills/) for samples.
|
|
257
|
+
|
|
258
|
+
### Built-in Sub-Agent Tools
|
|
259
|
+
|
|
260
|
+
Let the LLM delegate work to specialized sub-agents via tool calls:
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
import { createAgent, createSubAgentTools, Presets } from "@cuylabs/agent-core";
|
|
264
|
+
import type { AgentProfile } from "@cuylabs/agent-core";
|
|
265
|
+
|
|
266
|
+
const profiles: AgentProfile[] = [
|
|
267
|
+
{ name: "explorer", description: "Fast code search", preset: Presets.explore },
|
|
268
|
+
{ name: "reviewer", description: "Thorough code review", preset: Presets.review },
|
|
269
|
+
];
|
|
270
|
+
|
|
271
|
+
const parent = createAgent({ model, tools: codeTools });
|
|
272
|
+
const subTools = createSubAgentTools(parent, { profiles, async: true });
|
|
273
|
+
|
|
274
|
+
const agent = createAgent({
|
|
275
|
+
model,
|
|
276
|
+
tools: [...codeTools, ...subTools],
|
|
277
|
+
});
|
|
278
|
+
// The LLM can now call invoke_agent, wait_agent, and close_agent
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Tools: `invoke_agent` (spawn), `wait_agent` (collect results), `close_agent` (cancel). Supports sync and async modes, depth limiting, and concurrency control. See [docs/subagents.md](docs/subagents.md#built-in-sub-agent-tools-llm-callable).
|
|
282
|
+
|
|
173
283
|
### Error Handling & Retry
|
|
174
284
|
|
|
175
285
|
```typescript
|
|
@@ -262,14 +372,30 @@ The `chat()` method yields these event types:
|
|
|
262
372
|
| Event Type | Description |
|
|
263
373
|
|------------|-------------|
|
|
264
374
|
| `text-delta` | Streaming text chunk |
|
|
265
|
-
| `
|
|
375
|
+
| `text-start` / `text-end` | Text generation boundaries |
|
|
376
|
+
| `tool-start` | Tool invocation started |
|
|
266
377
|
| `tool-result` | Tool execution completed |
|
|
267
|
-
| `
|
|
268
|
-
| `
|
|
378
|
+
| `tool-error` | Tool execution failed |
|
|
379
|
+
| `step-start` / `step-finish` | LLM step boundaries with usage stats |
|
|
380
|
+
| `reasoning-delta` | Model reasoning chunk (if supported) |
|
|
381
|
+
| `status` | Agent state changes (thinking, calling-tool, etc.) |
|
|
382
|
+
| `doom-loop` | Repeated tool call pattern detected |
|
|
383
|
+
| `context-overflow` | Context window exceeded |
|
|
384
|
+
| `message` | Complete user/assistant message |
|
|
269
385
|
| `complete` | Stream finished with usage stats |
|
|
270
|
-
| `error` |
|
|
386
|
+
| `error` | Unrecoverable error |
|
|
387
|
+
|
|
388
|
+
See [examples/03-streaming.ts](examples/03-streaming.ts) for a complete event handling example.
|
|
271
389
|
|
|
272
390
|
## Concurrency Note
|
|
273
391
|
|
|
274
392
|
When using `runConcurrent()` to run multiple tasks in parallel, be aware that all tasks share the same `SessionManager` instance. For independent conversations, create separate `Agent` instances or use distinct session IDs.
|
|
275
393
|
|
|
394
|
+
## Examples
|
|
395
|
+
|
|
396
|
+
See [examples/](examples/) for 17 runnable examples covering every feature — from basic chat to middleware, skills, and Docker execution. The [examples README](examples/README.md) has setup instructions and run commands.
|
|
397
|
+
|
|
398
|
+
## Documentation
|
|
399
|
+
|
|
400
|
+
See [docs/](docs/) for in-depth guides on each subsystem.
|
|
401
|
+
|
|
@@ -203,17 +203,43 @@ interface ToolMetadata {
|
|
|
203
203
|
/**
|
|
204
204
|
* Tool definition namespace for @cuylabs/agent-core
|
|
205
205
|
*
|
|
206
|
-
*
|
|
206
|
+
* Provides consistent tool creation via Tool.define.
|
|
207
207
|
*/
|
|
208
208
|
|
|
209
209
|
/**
|
|
210
|
-
*
|
|
210
|
+
* A schema type compatible with both Zod 3 and Zod 4.
|
|
211
|
+
*
|
|
212
|
+
* Uses structural typing so that schemas from either Zod version satisfy
|
|
213
|
+
* the constraint. This avoids the problem where Zod 3's `ZodType` class
|
|
214
|
+
* and Zod 4's `ZodType` class have incompatible internal shapes, causing
|
|
215
|
+
* type errors when a library compiled against one version is consumed with
|
|
216
|
+
* the other.
|
|
217
|
+
*
|
|
218
|
+
* Both Zod 3 and Zod 4 (classic API) schemas have `parse()` and `_output`.
|
|
219
|
+
*/
|
|
220
|
+
interface CompatibleSchema<T = any> {
|
|
221
|
+
/** Parse and validate input data. Present in both Zod 3 and Zod 4. */
|
|
222
|
+
parse(data: unknown): T;
|
|
223
|
+
/**
|
|
224
|
+
* Type-level output marker used by both Zod versions:
|
|
225
|
+
* - Zod 3: `readonly _output!: T` (definite assignment assertion)
|
|
226
|
+
* - Zod 4 classic: `get _output(): T` (getter)
|
|
227
|
+
*/
|
|
228
|
+
readonly _output: T;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Infer the output type from a compatible schema.
|
|
232
|
+
* Equivalent to `z.infer<T>` but works with both Zod 3 and Zod 4 schemas.
|
|
233
|
+
*/
|
|
234
|
+
type InferSchemaOutput<T extends CompatibleSchema> = T["_output"];
|
|
235
|
+
/**
|
|
236
|
+
* Tool namespace - tool definition utilities
|
|
211
237
|
*/
|
|
212
238
|
declare namespace Tool {
|
|
213
239
|
/**
|
|
214
240
|
* Tool info interface - the shape of a defined tool
|
|
215
241
|
*/
|
|
216
|
-
interface Info<TParams extends
|
|
242
|
+
interface Info<TParams extends CompatibleSchema = any, TMeta extends ToolMetadata = ToolMetadata> {
|
|
217
243
|
/** Unique tool identifier */
|
|
218
244
|
id: string;
|
|
219
245
|
/** Initialize the tool (can be async for dynamic descriptions) */
|
|
@@ -235,13 +261,13 @@ declare namespace Tool {
|
|
|
235
261
|
/**
|
|
236
262
|
* Result of tool initialization
|
|
237
263
|
*/
|
|
238
|
-
interface InitResult<TParams extends
|
|
264
|
+
interface InitResult<TParams extends CompatibleSchema = any, TMeta extends ToolMetadata = ToolMetadata> {
|
|
239
265
|
/** Tool description for the LLM */
|
|
240
266
|
description: string;
|
|
241
267
|
/** Zod schema for parameters */
|
|
242
268
|
parameters: TParams;
|
|
243
269
|
/** Execute the tool */
|
|
244
|
-
execute: (params:
|
|
270
|
+
execute: (params: InferSchemaOutput<TParams>, ctx: ToolContext) => Promise<ExecuteResult<TMeta>>;
|
|
245
271
|
/** Optional custom validation error formatter */
|
|
246
272
|
formatValidationError?: (error: z.ZodError) => string;
|
|
247
273
|
/**
|
|
@@ -272,7 +298,7 @@ declare namespace Tool {
|
|
|
272
298
|
metadata: TMeta;
|
|
273
299
|
}
|
|
274
300
|
/**
|
|
275
|
-
* Define a tool with
|
|
301
|
+
* Define a tool with standard patterns
|
|
276
302
|
*
|
|
277
303
|
* @example
|
|
278
304
|
* ```typescript
|
|
@@ -289,14 +315,14 @@ declare namespace Tool {
|
|
|
289
315
|
* });
|
|
290
316
|
* ```
|
|
291
317
|
*/
|
|
292
|
-
function define<TParams extends
|
|
318
|
+
function define<TParams extends CompatibleSchema, TMeta extends ToolMetadata = ToolMetadata>(id: string, init: Info<TParams, TMeta>["init"] | Awaited<ReturnType<Info<TParams, TMeta>["init"]>>): Info<TParams, TMeta>;
|
|
293
319
|
/**
|
|
294
320
|
* Simple define for static tools (no async init)
|
|
295
321
|
*/
|
|
296
|
-
function defineSimple<TParams extends
|
|
322
|
+
function defineSimple<TParams extends CompatibleSchema, TMeta extends ToolMetadata = ToolMetadata>(id: string, config: {
|
|
297
323
|
description: string;
|
|
298
324
|
parameters: TParams;
|
|
299
|
-
execute: (params:
|
|
325
|
+
execute: (params: InferSchemaOutput<TParams>, ctx: ToolContext) => Promise<ExecuteResult<TMeta>>;
|
|
300
326
|
}): Info<TParams, TMeta>;
|
|
301
327
|
}
|
|
302
328
|
/**
|
|
@@ -304,11 +330,11 @@ declare namespace Tool {
|
|
|
304
330
|
*
|
|
305
331
|
* @deprecated Use Tool.define instead
|
|
306
332
|
*/
|
|
307
|
-
declare function defineTool<TParams extends
|
|
333
|
+
declare function defineTool<TParams extends CompatibleSchema>(definition: {
|
|
308
334
|
id: string;
|
|
309
335
|
description: string;
|
|
310
336
|
parameters: TParams;
|
|
311
|
-
execute: (params:
|
|
337
|
+
execute: (params: InferSchemaOutput<TParams>, ctx: ToolContext) => Promise<ToolResult>;
|
|
312
338
|
}): Tool.Info<TParams>;
|
|
313
339
|
|
|
314
340
|
/**
|
|
@@ -412,8 +438,6 @@ declare const defaultRegistry: ToolRegistry;
|
|
|
412
438
|
|
|
413
439
|
/**
|
|
414
440
|
* Output truncation utilities for @cuylabs/agent-core
|
|
415
|
-
*
|
|
416
|
-
* Based on OpenCode's tool/truncation.ts
|
|
417
441
|
*/
|
|
418
442
|
/** Maximum lines before truncation */
|
|
419
443
|
declare const MAX_LINES = 2000;
|
|
@@ -443,4 +467,4 @@ declare function truncateOutput(output: string, options?: {
|
|
|
443
467
|
*/
|
|
444
468
|
declare function formatSize(bytes: number): string;
|
|
445
469
|
|
|
446
|
-
export { type DirEntry as D, type ExecOptions as E, type FileOperationMeta as F, MAX_BYTES as M, type ToolHost as T,
|
|
470
|
+
export { type CompatibleSchema as C, type DirEntry as D, type ExecOptions as E, type FileOperationMeta as F, type InferSchemaOutput as I, MAX_BYTES as M, type ToolHost as T, type ToolContext as a, Tool as b, type TurnTrackerContext as c, type ExecResult as d, type FileStat as e, MAX_LINES as f, TRUNCATE_DIR as g, TRUNCATE_GLOB as h, type ToolMetadata as i, ToolRegistry as j, type ToolResult as k, type ToolSpec as l, type TruncateResult as m, defaultRegistry as n, defineTool as o, formatSize as p, truncateOutput as t };
|