@lleverage-ai/agent-sdk 0.0.1
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/LICENSE +21 -0
- package/README.md +2321 -0
- package/dist/agent.d.ts +52 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +2122 -0
- package/dist/agent.js.map +1 -0
- package/dist/backend.d.ts +378 -0
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js +71 -0
- package/dist/backend.js.map +1 -0
- package/dist/backends/composite.d.ts +258 -0
- package/dist/backends/composite.d.ts.map +1 -0
- package/dist/backends/composite.js +437 -0
- package/dist/backends/composite.js.map +1 -0
- package/dist/backends/filesystem.d.ts +268 -0
- package/dist/backends/filesystem.d.ts.map +1 -0
- package/dist/backends/filesystem.js +623 -0
- package/dist/backends/filesystem.js.map +1 -0
- package/dist/backends/index.d.ts +14 -0
- package/dist/backends/index.d.ts.map +1 -0
- package/dist/backends/index.js +14 -0
- package/dist/backends/index.js.map +1 -0
- package/dist/backends/persistent.d.ts +312 -0
- package/dist/backends/persistent.d.ts.map +1 -0
- package/dist/backends/persistent.js +519 -0
- package/dist/backends/persistent.js.map +1 -0
- package/dist/backends/sandbox.d.ts +315 -0
- package/dist/backends/sandbox.d.ts.map +1 -0
- package/dist/backends/sandbox.js +490 -0
- package/dist/backends/sandbox.js.map +1 -0
- package/dist/backends/state.d.ts +225 -0
- package/dist/backends/state.d.ts.map +1 -0
- package/dist/backends/state.js +396 -0
- package/dist/backends/state.js.map +1 -0
- package/dist/checkpointer/file-saver.d.ts +182 -0
- package/dist/checkpointer/file-saver.d.ts.map +1 -0
- package/dist/checkpointer/file-saver.js +298 -0
- package/dist/checkpointer/file-saver.js.map +1 -0
- package/dist/checkpointer/index.d.ts +40 -0
- package/dist/checkpointer/index.d.ts.map +1 -0
- package/dist/checkpointer/index.js +40 -0
- package/dist/checkpointer/index.js.map +1 -0
- package/dist/checkpointer/kv-saver.d.ts +142 -0
- package/dist/checkpointer/kv-saver.d.ts.map +1 -0
- package/dist/checkpointer/kv-saver.js +176 -0
- package/dist/checkpointer/kv-saver.js.map +1 -0
- package/dist/checkpointer/memory-saver.d.ts +158 -0
- package/dist/checkpointer/memory-saver.d.ts.map +1 -0
- package/dist/checkpointer/memory-saver.js +222 -0
- package/dist/checkpointer/memory-saver.js.map +1 -0
- package/dist/checkpointer/types.d.ts +353 -0
- package/dist/checkpointer/types.d.ts.map +1 -0
- package/dist/checkpointer/types.js +159 -0
- package/dist/checkpointer/types.js.map +1 -0
- package/dist/context-manager.d.ts +627 -0
- package/dist/context-manager.d.ts.map +1 -0
- package/dist/context-manager.js +1039 -0
- package/dist/context-manager.js.map +1 -0
- package/dist/context.d.ts +57 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +76 -0
- package/dist/context.js.map +1 -0
- package/dist/errors/index.d.ts +611 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +1023 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/generation-helpers.d.ts +126 -0
- package/dist/generation-helpers.d.ts.map +1 -0
- package/dist/generation-helpers.js +181 -0
- package/dist/generation-helpers.js.map +1 -0
- package/dist/hooks/audit.d.ts +210 -0
- package/dist/hooks/audit.d.ts.map +1 -0
- package/dist/hooks/audit.js +305 -0
- package/dist/hooks/audit.js.map +1 -0
- package/dist/hooks/cache.d.ts +180 -0
- package/dist/hooks/cache.d.ts.map +1 -0
- package/dist/hooks/cache.js +273 -0
- package/dist/hooks/cache.js.map +1 -0
- package/dist/hooks/guardrails.d.ts +145 -0
- package/dist/hooks/guardrails.d.ts.map +1 -0
- package/dist/hooks/guardrails.js +326 -0
- package/dist/hooks/guardrails.js.map +1 -0
- package/dist/hooks/index.d.ts +18 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +32 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/logging.d.ts +193 -0
- package/dist/hooks/logging.d.ts.map +1 -0
- package/dist/hooks/logging.js +345 -0
- package/dist/hooks/logging.js.map +1 -0
- package/dist/hooks/parallel-guardrails.d.ts +268 -0
- package/dist/hooks/parallel-guardrails.d.ts.map +1 -0
- package/dist/hooks/parallel-guardrails.js +416 -0
- package/dist/hooks/parallel-guardrails.js.map +1 -0
- package/dist/hooks/rate-limit.d.ts +305 -0
- package/dist/hooks/rate-limit.d.ts.map +1 -0
- package/dist/hooks/rate-limit.js +372 -0
- package/dist/hooks/rate-limit.js.map +1 -0
- package/dist/hooks/retry.d.ts +144 -0
- package/dist/hooks/retry.d.ts.map +1 -0
- package/dist/hooks/retry.js +210 -0
- package/dist/hooks/retry.js.map +1 -0
- package/dist/hooks/secrets.d.ts +174 -0
- package/dist/hooks/secrets.d.ts.map +1 -0
- package/dist/hooks/secrets.js +306 -0
- package/dist/hooks/secrets.js.map +1 -0
- package/dist/hooks.d.ts +229 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +352 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +97 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +182 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/env.d.ts +25 -0
- package/dist/mcp/env.d.ts.map +1 -0
- package/dist/mcp/env.js +18 -0
- package/dist/mcp/env.js.map +1 -0
- package/dist/mcp/index.d.ts +16 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +17 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/manager.d.ts +184 -0
- package/dist/mcp/manager.d.ts.map +1 -0
- package/dist/mcp/manager.js +446 -0
- package/dist/mcp/manager.js.map +1 -0
- package/dist/mcp/types.d.ts +58 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +7 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/mcp/validation.d.ts +119 -0
- package/dist/mcp/validation.d.ts.map +1 -0
- package/dist/mcp/validation.js +407 -0
- package/dist/mcp/validation.js.map +1 -0
- package/dist/mcp/virtual-server.d.ts +78 -0
- package/dist/mcp/virtual-server.d.ts.map +1 -0
- package/dist/mcp/virtual-server.js +137 -0
- package/dist/mcp/virtual-server.js.map +1 -0
- package/dist/memory/filesystem-store.d.ts +217 -0
- package/dist/memory/filesystem-store.d.ts.map +1 -0
- package/dist/memory/filesystem-store.js +343 -0
- package/dist/memory/filesystem-store.js.map +1 -0
- package/dist/memory/index.d.ts +46 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +46 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/loader.d.ts +396 -0
- package/dist/memory/loader.d.ts.map +1 -0
- package/dist/memory/loader.js +419 -0
- package/dist/memory/loader.js.map +1 -0
- package/dist/memory/permissions.d.ts +282 -0
- package/dist/memory/permissions.d.ts.map +1 -0
- package/dist/memory/permissions.js +297 -0
- package/dist/memory/permissions.js.map +1 -0
- package/dist/memory/rules.d.ts +249 -0
- package/dist/memory/rules.d.ts.map +1 -0
- package/dist/memory/rules.js +362 -0
- package/dist/memory/rules.js.map +1 -0
- package/dist/memory/store.d.ts +286 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +263 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/middleware/apply.d.ts +73 -0
- package/dist/middleware/apply.d.ts.map +1 -0
- package/dist/middleware/apply.js +219 -0
- package/dist/middleware/apply.js.map +1 -0
- package/dist/middleware/context.d.ts +33 -0
- package/dist/middleware/context.d.ts.map +1 -0
- package/dist/middleware/context.js +176 -0
- package/dist/middleware/context.js.map +1 -0
- package/dist/middleware/index.d.ts +31 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +32 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/logging.d.ts +137 -0
- package/dist/middleware/logging.d.ts.map +1 -0
- package/dist/middleware/logging.js +374 -0
- package/dist/middleware/logging.js.map +1 -0
- package/dist/middleware/types.d.ts +183 -0
- package/dist/middleware/types.d.ts.map +1 -0
- package/dist/middleware/types.js +11 -0
- package/dist/middleware/types.js.map +1 -0
- package/dist/observability/events.d.ts +183 -0
- package/dist/observability/events.d.ts.map +1 -0
- package/dist/observability/events.js +305 -0
- package/dist/observability/events.js.map +1 -0
- package/dist/observability/index.d.ts +55 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +87 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/logger.d.ts +318 -0
- package/dist/observability/logger.d.ts.map +1 -0
- package/dist/observability/logger.js +436 -0
- package/dist/observability/logger.js.map +1 -0
- package/dist/observability/metrics.d.ts +341 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +490 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/observability/preset.d.ts +161 -0
- package/dist/observability/preset.d.ts.map +1 -0
- package/dist/observability/preset.js +133 -0
- package/dist/observability/preset.js.map +1 -0
- package/dist/observability/streaming.d.ts +113 -0
- package/dist/observability/streaming.d.ts.map +1 -0
- package/dist/observability/streaming.js +114 -0
- package/dist/observability/streaming.js.map +1 -0
- package/dist/observability/tracing.d.ts +378 -0
- package/dist/observability/tracing.d.ts.map +1 -0
- package/dist/observability/tracing.js +539 -0
- package/dist/observability/tracing.js.map +1 -0
- package/dist/plugins.d.ts +55 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +63 -0
- package/dist/plugins.js.map +1 -0
- package/dist/presets/index.d.ts +7 -0
- package/dist/presets/index.d.ts.map +1 -0
- package/dist/presets/index.js +7 -0
- package/dist/presets/index.js.map +1 -0
- package/dist/presets/production.d.ts +262 -0
- package/dist/presets/production.d.ts.map +1 -0
- package/dist/presets/production.js +295 -0
- package/dist/presets/production.js.map +1 -0
- package/dist/security/index.d.ts +179 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +323 -0
- package/dist/security/index.js.map +1 -0
- package/dist/subagents/advanced.d.ts +413 -0
- package/dist/subagents/advanced.d.ts.map +1 -0
- package/dist/subagents/advanced.js +396 -0
- package/dist/subagents/advanced.js.map +1 -0
- package/dist/subagents/index.d.ts +14 -0
- package/dist/subagents/index.d.ts.map +1 -0
- package/dist/subagents/index.js +15 -0
- package/dist/subagents/index.js.map +1 -0
- package/dist/subagents.d.ts +73 -0
- package/dist/subagents.d.ts.map +1 -0
- package/dist/subagents.js +213 -0
- package/dist/subagents.js.map +1 -0
- package/dist/task-store/file-store.d.ts +76 -0
- package/dist/task-store/file-store.d.ts.map +1 -0
- package/dist/task-store/file-store.js +190 -0
- package/dist/task-store/file-store.js.map +1 -0
- package/dist/task-store/index.d.ts +11 -0
- package/dist/task-store/index.d.ts.map +1 -0
- package/dist/task-store/index.js +10 -0
- package/dist/task-store/index.js.map +1 -0
- package/dist/task-store/kv-store.d.ts +140 -0
- package/dist/task-store/kv-store.d.ts.map +1 -0
- package/dist/task-store/kv-store.js +169 -0
- package/dist/task-store/kv-store.js.map +1 -0
- package/dist/task-store/memory-store.d.ts +66 -0
- package/dist/task-store/memory-store.d.ts.map +1 -0
- package/dist/task-store/memory-store.js +125 -0
- package/dist/task-store/memory-store.js.map +1 -0
- package/dist/task-store/types.d.ts +235 -0
- package/dist/task-store/types.d.ts.map +1 -0
- package/dist/task-store/types.js +110 -0
- package/dist/task-store/types.js.map +1 -0
- package/dist/testing/assertions.d.ts +401 -0
- package/dist/testing/assertions.d.ts.map +1 -0
- package/dist/testing/assertions.js +630 -0
- package/dist/testing/assertions.js.map +1 -0
- package/dist/testing/index.d.ts +343 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +360 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/mock-agent.d.ts +214 -0
- package/dist/testing/mock-agent.d.ts.map +1 -0
- package/dist/testing/mock-agent.js +448 -0
- package/dist/testing/mock-agent.js.map +1 -0
- package/dist/testing/recorder.d.ts +288 -0
- package/dist/testing/recorder.d.ts.map +1 -0
- package/dist/testing/recorder.js +499 -0
- package/dist/testing/recorder.js.map +1 -0
- package/dist/tools/execute.d.ts +104 -0
- package/dist/tools/execute.d.ts.map +1 -0
- package/dist/tools/execute.js +191 -0
- package/dist/tools/execute.js.map +1 -0
- package/dist/tools/factory.d.ts +260 -0
- package/dist/tools/factory.d.ts.map +1 -0
- package/dist/tools/factory.js +241 -0
- package/dist/tools/factory.js.map +1 -0
- package/dist/tools/filesystem.d.ts +215 -0
- package/dist/tools/filesystem.d.ts.map +1 -0
- package/dist/tools/filesystem.js +311 -0
- package/dist/tools/filesystem.js.map +1 -0
- package/dist/tools/index.d.ts +33 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +33 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/search.d.ts +59 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +94 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/skills.d.ts +354 -0
- package/dist/tools/skills.d.ts.map +1 -0
- package/dist/tools/skills.js +413 -0
- package/dist/tools/skills.js.map +1 -0
- package/dist/tools/task.d.ts +272 -0
- package/dist/tools/task.d.ts.map +1 -0
- package/dist/tools/task.js +521 -0
- package/dist/tools/task.js.map +1 -0
- package/dist/tools/todos.d.ts +131 -0
- package/dist/tools/todos.d.ts.map +1 -0
- package/dist/tools/todos.js +120 -0
- package/dist/tools/todos.js.map +1 -0
- package/dist/tools/tool-registry.d.ts +424 -0
- package/dist/tools/tool-registry.d.ts.map +1 -0
- package/dist/tools/tool-registry.js +607 -0
- package/dist/tools/tool-registry.js.map +1 -0
- package/dist/tools/user-interaction.d.ts +116 -0
- package/dist/tools/user-interaction.d.ts.map +1 -0
- package/dist/tools/user-interaction.js +147 -0
- package/dist/tools/user-interaction.js.map +1 -0
- package/dist/tools/utils.d.ts +124 -0
- package/dist/tools/utils.d.ts.map +1 -0
- package/dist/tools/utils.js +189 -0
- package/dist/tools/utils.js.map +1 -0
- package/dist/tools.d.ts +74 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +73 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +2421 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +55 -0
- package/dist/types.js.map +1 -0
- package/package.json +81 -0
package/README.md
ADDED
|
@@ -0,0 +1,2321 @@
|
|
|
1
|
+
# @lleverage-ai/agent-sdk
|
|
2
|
+
|
|
3
|
+
A TypeScript framework for building AI agents using the Vercel AI SDK v6.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [Core Concepts](#core-concepts)
|
|
10
|
+
- [Agents](#agents)
|
|
11
|
+
- [Tools](#tools)
|
|
12
|
+
- [Plugins](#plugins)
|
|
13
|
+
- [Skills](#skills)
|
|
14
|
+
- [Hooks](#hooks)
|
|
15
|
+
- [Tool Loading Strategies](#tool-loading-strategies)
|
|
16
|
+
- [Subagents](#subagents)
|
|
17
|
+
- [Streaming](#streaming)
|
|
18
|
+
- [MCP Integration](#mcp-integration)
|
|
19
|
+
- [Backends](#backends)
|
|
20
|
+
- [Middleware](#middleware)
|
|
21
|
+
- [Observability](#observability)
|
|
22
|
+
- [Memory](#memory)
|
|
23
|
+
- [Checkpointing](#checkpointing)
|
|
24
|
+
- [Context Compaction](#context-compaction)
|
|
25
|
+
- [Error Handling](#error-handling)
|
|
26
|
+
- [API Reference](#api-reference)
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
bun add @lleverage-ai/agent-sdk ai zod
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
You'll also need at least one AI provider:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
bun add @ai-sdk/anthropic # or @ai-sdk/openai
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**React Integration:** If you're building React applications, install the React companion package for UI helpers:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
bun add @lleverage-ai/agent-sdk-react react
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
See the [@lleverage-ai/agent-sdk-react README](../agent-sdk-react/README.md) for documentation on `useApprovalFlow` hook and `ApprovalDialog` component.
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { createAgent } from "@lleverage-ai/agent-sdk";
|
|
52
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
53
|
+
import { tool } from "ai";
|
|
54
|
+
import { z } from "zod";
|
|
55
|
+
|
|
56
|
+
const agent = createAgent({
|
|
57
|
+
model: anthropic("claude-sonnet-4-20250514"),
|
|
58
|
+
systemPrompt: "You are a friendly assistant.",
|
|
59
|
+
tools: {
|
|
60
|
+
greet: tool({
|
|
61
|
+
description: "Greet a person by name",
|
|
62
|
+
inputSchema: z.object({
|
|
63
|
+
name: z.string().describe("The name of the person to greet"),
|
|
64
|
+
}),
|
|
65
|
+
execute: async ({ name }) => `Hello, ${name}!`,
|
|
66
|
+
}),
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const result = await agent.generate({
|
|
71
|
+
prompt: "Say hello to Alice",
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
console.log(result.text);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Core Concepts
|
|
78
|
+
|
|
79
|
+
### Agents
|
|
80
|
+
|
|
81
|
+
Agents combine a language model with tools, plugins, and hooks. Core tools for filesystem operations and task tracking are automatically included.
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
const agent = createAgent({
|
|
85
|
+
model: anthropic("claude-sonnet-4-20250514"),
|
|
86
|
+
systemPrompt: "You are a helpful assistant.",
|
|
87
|
+
maxSteps: 10,
|
|
88
|
+
tools: {
|
|
89
|
+
/* custom tools */
|
|
90
|
+
},
|
|
91
|
+
plugins: [
|
|
92
|
+
/* plugins */
|
|
93
|
+
],
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Disable specific core tools if needed:
|
|
97
|
+
const safeAgent = createAgent({
|
|
98
|
+
model,
|
|
99
|
+
disabledCoreTools: ["bash", "write"], // No shell or write access
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Core tools included:** `read`, `write`, `edit`, `glob`, `grep`, `todo_write`, `bash` (requires sandbox), `search_tools` (when enabled)
|
|
104
|
+
|
|
105
|
+
### Tools
|
|
106
|
+
|
|
107
|
+
Tools use the AI SDK's `tool()` function with `inputSchema`:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import { tool } from "ai";
|
|
111
|
+
import { z } from "zod";
|
|
112
|
+
|
|
113
|
+
const calculator = tool({
|
|
114
|
+
description: "Perform basic math operations",
|
|
115
|
+
inputSchema: z.object({
|
|
116
|
+
operation: z.enum(["add", "subtract", "multiply", "divide"]),
|
|
117
|
+
a: z.number(),
|
|
118
|
+
b: z.number(),
|
|
119
|
+
}),
|
|
120
|
+
execute: async ({ operation, a, b }) => {
|
|
121
|
+
switch (operation) {
|
|
122
|
+
case "add":
|
|
123
|
+
return a + b;
|
|
124
|
+
case "subtract":
|
|
125
|
+
return a - b;
|
|
126
|
+
case "multiply":
|
|
127
|
+
return a * b;
|
|
128
|
+
case "divide":
|
|
129
|
+
return a / b;
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const agent = createAgent({
|
|
135
|
+
model,
|
|
136
|
+
tools: { calculator },
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Plugins
|
|
141
|
+
|
|
142
|
+
Plugins bundle tools, skills, and hooks. Plugin tools are exposed with MCP naming: `mcp__<plugin>__<tool>`.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import { definePlugin } from "@lleverage-ai/agent-sdk";
|
|
146
|
+
|
|
147
|
+
const myPlugin = definePlugin({
|
|
148
|
+
name: "my-plugin",
|
|
149
|
+
description: "A collection of useful tools",
|
|
150
|
+
tools: {
|
|
151
|
+
myTool: tool({
|
|
152
|
+
description: "Does something useful",
|
|
153
|
+
inputSchema: z.object({ input: z.string() }),
|
|
154
|
+
execute: async ({ input }) => `Result: ${input}`,
|
|
155
|
+
}),
|
|
156
|
+
},
|
|
157
|
+
skills: [
|
|
158
|
+
/* skills */
|
|
159
|
+
],
|
|
160
|
+
hooks: [
|
|
161
|
+
/* hooks */
|
|
162
|
+
],
|
|
163
|
+
setup: async (agent) => {
|
|
164
|
+
// Initialization logic
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const agent = createAgent({
|
|
169
|
+
model,
|
|
170
|
+
plugins: [myPlugin],
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Plugin tool available as: mcp__my-plugin__myTool
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Skills
|
|
177
|
+
|
|
178
|
+
Skills provide contextual instructions that guide how the agent behaves. They can be used in several ways:
|
|
179
|
+
|
|
180
|
+
**Tool guidance** - Bundle a skill with plugin tools to explain how to use them:
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
const dataPlugin = definePlugin({
|
|
184
|
+
name: "data-explorer",
|
|
185
|
+
description: "Data exploration tools",
|
|
186
|
+
tools: { getSchema, queryData, createChart },
|
|
187
|
+
skills: [
|
|
188
|
+
defineSkill({
|
|
189
|
+
name: "data-exploration",
|
|
190
|
+
description: "Query and visualize data",
|
|
191
|
+
prompt: `You have access to data exploration tools.
|
|
192
|
+
|
|
193
|
+
Available tables: products, users, sales.
|
|
194
|
+
Always use getSchema first to see column types.`,
|
|
195
|
+
}),
|
|
196
|
+
],
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Instructions only** - A plugin with just a skill loads dynamic instructions (no tools):
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
const codingGuidelinesPlugin = definePlugin({
|
|
204
|
+
name: "coding-guidelines",
|
|
205
|
+
description: "Project coding standards",
|
|
206
|
+
skills: [
|
|
207
|
+
defineSkill({
|
|
208
|
+
name: "guidelines",
|
|
209
|
+
description: "Coding standards for this project",
|
|
210
|
+
prompt: `Follow these coding standards:
|
|
211
|
+
- Use TypeScript strict mode
|
|
212
|
+
- Prefer named exports
|
|
213
|
+
- Write TSDoc for public APIs`,
|
|
214
|
+
}),
|
|
215
|
+
],
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Progressive disclosure** - Skills with tools are loaded on-demand via the `skill` tool:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const analyzeSkill = defineSkill({
|
|
223
|
+
name: "analyze",
|
|
224
|
+
description: "Deep code analysis",
|
|
225
|
+
prompt: "Perform detailed code analysis.",
|
|
226
|
+
tools: {
|
|
227
|
+
lint: tool({
|
|
228
|
+
/* ... */
|
|
229
|
+
}),
|
|
230
|
+
typeCheck: tool({
|
|
231
|
+
/* ... */
|
|
232
|
+
}),
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Unified Hooks
|
|
238
|
+
|
|
239
|
+
Unified hooks allow you to observe and react to agent lifecycle events with a clean, typed API:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
import { createAgent } from "@lleverage-ai/agent-sdk";
|
|
243
|
+
|
|
244
|
+
const agent = createAgent({
|
|
245
|
+
model,
|
|
246
|
+
hooks: {
|
|
247
|
+
PreGenerate: [
|
|
248
|
+
async ({ options }) => {
|
|
249
|
+
console.log("Starting generation...");
|
|
250
|
+
return {};
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
PostGenerate: [
|
|
254
|
+
async ({ result }) => {
|
|
255
|
+
console.log("Generated:", result.text);
|
|
256
|
+
return {};
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
PreToolUse: [
|
|
260
|
+
{
|
|
261
|
+
hooks: [
|
|
262
|
+
async ({ tool_name, tool_input }) => {
|
|
263
|
+
console.log("Tool starting:", tool_name, tool_input);
|
|
264
|
+
return {};
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
PostToolUse: [
|
|
270
|
+
{
|
|
271
|
+
hooks: [
|
|
272
|
+
async ({ tool_name, tool_response }) => {
|
|
273
|
+
console.log("Tool completed:", tool_name, tool_response);
|
|
274
|
+
return {};
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
PostToolUseFailure: [
|
|
280
|
+
{
|
|
281
|
+
hooks: [
|
|
282
|
+
async ({ tool_name, error }) => {
|
|
283
|
+
console.warn("Tool failed:", tool_name, error);
|
|
284
|
+
return {};
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Available hooks:**
|
|
294
|
+
|
|
295
|
+
- `PreGenerate`, `PostGenerate`, `PostGenerateFailure` - Generation lifecycle
|
|
296
|
+
- `PreToolUse`, `PostToolUse`, `PostToolUseFailure` - Tool execution lifecycle
|
|
297
|
+
- `MCPConnectionFailed`, `MCPConnectionRestored` - MCP server connection lifecycle
|
|
298
|
+
|
|
299
|
+
**Hook utilities:**
|
|
300
|
+
|
|
301
|
+
- `createCacheHooks` - Response caching
|
|
302
|
+
- `createRetryHooks` - Automatic retries with backoff
|
|
303
|
+
- `createRateLimitHooks` - Rate limiting
|
|
304
|
+
- `createLoggingHooks` - Structured logging
|
|
305
|
+
- `createGuardrailsHooks` - Content guardrails
|
|
306
|
+
- `createSecretsFilterHooks` - Secrets redaction
|
|
307
|
+
|
|
308
|
+
**MCP Connection Monitoring:**
|
|
309
|
+
|
|
310
|
+
Monitor MCP server connectivity to handle failures and restorations:
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
const agent = createAgent({
|
|
314
|
+
model,
|
|
315
|
+
plugins: [githubPlugin],
|
|
316
|
+
hooks: {
|
|
317
|
+
MCPConnectionFailed: [
|
|
318
|
+
async ({ server_name, config, error, session_id }) => {
|
|
319
|
+
console.error(`MCP server ${server_name} failed:`, error.message);
|
|
320
|
+
// Send alert, log to monitoring system, etc.
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
MCPConnectionRestored: [
|
|
324
|
+
async ({ server_name, tool_count, session_id }) => {
|
|
325
|
+
console.log(
|
|
326
|
+
`MCP server ${server_name} restored with ${tool_count} tools`,
|
|
327
|
+
);
|
|
328
|
+
// Clear alerts, update status, etc.
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
These hooks enable:
|
|
336
|
+
|
|
337
|
+
- **Observability**: Track MCP server health in logs and metrics
|
|
338
|
+
- **Alerting**: Send notifications when servers fail or recover
|
|
339
|
+
- **Graceful degradation**: Disable features that depend on unavailable servers
|
|
340
|
+
- **Debugging**: Understand connectivity issues in production
|
|
341
|
+
|
|
342
|
+
**Retry with Automatic Backoff:**
|
|
343
|
+
|
|
344
|
+
Handle transient failures with automatic retry logic:
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
import { createRetryHooks } from "@lleverage-ai/agent-sdk/hooks";
|
|
348
|
+
|
|
349
|
+
const agent = createAgent({
|
|
350
|
+
model,
|
|
351
|
+
hooks: {
|
|
352
|
+
PostGenerateFailure: [
|
|
353
|
+
createRetryHooks({
|
|
354
|
+
maxRetries: 3,
|
|
355
|
+
baseDelay: 1000, // 1 second
|
|
356
|
+
backoffMultiplier: 2, // Exponential: 1s, 2s, 4s
|
|
357
|
+
jitter: true, // Add randomness to prevent thundering herd
|
|
358
|
+
}),
|
|
359
|
+
],
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
The retry hook automatically handles:
|
|
365
|
+
|
|
366
|
+
- **Rate limiting**: Retries 429 errors with exponential backoff
|
|
367
|
+
- **Server errors**: Retries 5xx errors (500, 502, 503, 504)
|
|
368
|
+
- **Network failures**: Retries ECONNRESET, ECONNREFUSED, ETIMEDOUT
|
|
369
|
+
- **Timeout errors**: Retries timeout failures
|
|
370
|
+
|
|
371
|
+
**Retry statistics tracking:**
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
import { createManagedRetryHooks } from "@lleverage-ai/agent-sdk/hooks";
|
|
375
|
+
|
|
376
|
+
const { hook, getStats } = createManagedRetryHooks({ maxRetries: 5 });
|
|
377
|
+
|
|
378
|
+
const agent = createAgent({
|
|
379
|
+
model,
|
|
380
|
+
hooks: {
|
|
381
|
+
PostGenerateFailure: [hook],
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// After some operations
|
|
386
|
+
const stats = getStats();
|
|
387
|
+
console.log(`Retry rate: ${stats.retries / stats.failures}`);
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Custom retry logic:**
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
const retryHook = createRetryHooks({
|
|
394
|
+
shouldRetry: (error, attempt) => {
|
|
395
|
+
// Only retry rate limits on the first 2 attempts
|
|
396
|
+
return error.message.includes("rate limit") && attempt < 2;
|
|
397
|
+
},
|
|
398
|
+
baseDelay: 2000, // 2 seconds
|
|
399
|
+
});
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
**Note:** Retry hooks work consistently across all agent methods (`generate()`, `stream()`, `streamResponse()`, `streamRaw()`, `streamDataResponse()`).
|
|
403
|
+
|
|
404
|
+
## Tool Loading Strategies
|
|
405
|
+
|
|
406
|
+
The SDK provides multiple mechanisms for loading tools, each optimized for different scenarios:
|
|
407
|
+
|
|
408
|
+
- **Eager Loading** (default): Load all tools upfront - best for small tool sets (< 20 tools)
|
|
409
|
+
- **Lazy Loading** (`use_tools`): Load tools on-demand via a registry - best for 20-100+ tools
|
|
410
|
+
- **Dynamic Discovery** (`search_tools`): Search and load MCP tools dynamically - best for 100+ tools
|
|
411
|
+
- **MCP Loading**: Connect to external MCP servers for standardized tool ecosystems
|
|
412
|
+
|
|
413
|
+
### When to Use Each Approach
|
|
414
|
+
|
|
415
|
+
**Use Eager Loading when:**
|
|
416
|
+
|
|
417
|
+
- Small tool set (< 20 tools) that's always needed
|
|
418
|
+
- Simplest setup with best performance
|
|
419
|
+
- All tools are core to the agent's purpose
|
|
420
|
+
|
|
421
|
+
**Use Lazy Loading when:**
|
|
422
|
+
|
|
423
|
+
- Large tool set (20-100+ tools)
|
|
424
|
+
- Agent only needs a subset per conversation
|
|
425
|
+
- Plugin architecture with domain-specific tools
|
|
426
|
+
- Want to minimize context window usage
|
|
427
|
+
|
|
428
|
+
**Use Dynamic Discovery when:**
|
|
429
|
+
|
|
430
|
+
- Very large tool set (100+ tools)
|
|
431
|
+
- Tool needs are unpredictable
|
|
432
|
+
- Using MCP ecosystem with many servers
|
|
433
|
+
- Want agent to explore capabilities dynamically
|
|
434
|
+
|
|
435
|
+
**Use MCP Loading when:**
|
|
436
|
+
|
|
437
|
+
- Integrating external tool ecosystems (filesystem, databases, APIs)
|
|
438
|
+
- Need standardized tool protocols
|
|
439
|
+
- Building on existing MCP infrastructure
|
|
440
|
+
|
|
441
|
+
### Quick Examples
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
// Eager loading (default)
|
|
445
|
+
const agent = createAgent({
|
|
446
|
+
model,
|
|
447
|
+
tools: { calculator, weather, database }, // All loaded immediately
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Lazy loading with use_tools
|
|
451
|
+
const registry = new ToolRegistry();
|
|
452
|
+
registry.registerPlugin("stripe", stripePlugin.tools);
|
|
453
|
+
registry.registerPlugin("github", githubPlugin.tools);
|
|
454
|
+
|
|
455
|
+
const agent = createAgent({
|
|
456
|
+
model,
|
|
457
|
+
tools: {
|
|
458
|
+
use_tools: createUseToolsTool({ registry }),
|
|
459
|
+
},
|
|
460
|
+
pluginLoading: "lazy",
|
|
461
|
+
});
|
|
462
|
+
// Agent loads tools on demand: use_tools({ plugin: "stripe" })
|
|
463
|
+
// (Tools also become active after agent.loadTools([...]) in lazy mode)
|
|
464
|
+
|
|
465
|
+
// Dynamic discovery with search_tools
|
|
466
|
+
const mcpManager = new MCPManager();
|
|
467
|
+
await mcpManager.connectServer({ name: "filesystem", command: "npx", args: [...] });
|
|
468
|
+
|
|
469
|
+
const agent = createAgent({
|
|
470
|
+
model,
|
|
471
|
+
mcpManager,
|
|
472
|
+
mcpEagerLoad: false,
|
|
473
|
+
tools: {
|
|
474
|
+
search_tools: createSearchToolsTool({ manager: mcpManager, enableLoad: true }),
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
// Agent discovers tools: search_tools({ query: "list files", load: true })
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
For a comprehensive guide with decision trees, common patterns, and best practices, see the **[Tool Loading Guide](../../docs/TOOL_LOADING_GUIDE.md)**.
|
|
481
|
+
|
|
482
|
+
## Production Security
|
|
483
|
+
|
|
484
|
+
⚠️ **Warning**: Default agent settings are permissive to enable rapid development. For production deployments, you should explicitly apply security policies to prevent unauthorized operations.
|
|
485
|
+
|
|
486
|
+
### Quick Production Setup
|
|
487
|
+
|
|
488
|
+
The easiest way to create a production-ready agent is using `createProductionAgent()`, which combines security, observability, and recommended hooks in a single function:
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
import { createProductionAgent } from "@lleverage-ai/agent-sdk";
|
|
492
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
493
|
+
|
|
494
|
+
// One-line production agent setup
|
|
495
|
+
const { agent, observability } = createProductionAgent({
|
|
496
|
+
model: anthropic("claude-sonnet-4-20250514"),
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
// Access observability primitives
|
|
500
|
+
observability.logger?.info("Agent started");
|
|
501
|
+
observability.metrics?.requests.inc();
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
This function automatically configures:
|
|
505
|
+
|
|
506
|
+
- **Security**: Production security preset (sandbox, permissions, tool restrictions)
|
|
507
|
+
- **Observability**: Logging, metrics, and tracing with hooks
|
|
508
|
+
- **Secrets filtering**: Prevents credential leakage in logs and responses
|
|
509
|
+
- **Optional guardrails**: Content filtering for input/output validation
|
|
510
|
+
|
|
511
|
+
**Customization example:**
|
|
512
|
+
|
|
513
|
+
```typescript
|
|
514
|
+
const { agent, observability } = createProductionAgent({
|
|
515
|
+
model: anthropic("claude-sonnet-4-20250514"),
|
|
516
|
+
|
|
517
|
+
// Customize security
|
|
518
|
+
securityPreset: "readonly", // Maximum restrictions
|
|
519
|
+
securityOverrides: {
|
|
520
|
+
permissionMode: "approval-required",
|
|
521
|
+
},
|
|
522
|
+
|
|
523
|
+
// Customize observability
|
|
524
|
+
observabilityOptions: {
|
|
525
|
+
name: "my-agent",
|
|
526
|
+
loggerOptions: { level: "warn" },
|
|
527
|
+
enableTracing: false,
|
|
528
|
+
},
|
|
529
|
+
|
|
530
|
+
// Enable guardrails
|
|
531
|
+
enableGuardrails: true,
|
|
532
|
+
blockedInputPatterns: [/ignore.*instructions/i],
|
|
533
|
+
blockedOutputPatterns: [/\d{3}-\d{2}-\d{4}/], // SSN pattern
|
|
534
|
+
|
|
535
|
+
// Add custom options
|
|
536
|
+
additionalOptions: {
|
|
537
|
+
systemPrompt: "You are a helpful assistant.",
|
|
538
|
+
checkpointer: createMemorySaver(),
|
|
539
|
+
},
|
|
540
|
+
});
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
For more granular control, you can configure security and observability components individually using the following options.
|
|
544
|
+
|
|
545
|
+
### Security Policy Presets
|
|
546
|
+
|
|
547
|
+
The SDK provides four security policy presets that bundle sandbox configuration, permission modes, and tool restrictions:
|
|
548
|
+
|
|
549
|
+
```typescript
|
|
550
|
+
import { createAgent } from "@lleverage-ai/agent-sdk";
|
|
551
|
+
import { applySecurityPolicy } from "@lleverage-ai/agent-sdk/security";
|
|
552
|
+
|
|
553
|
+
// Production preset: balanced security for production deployments
|
|
554
|
+
const agent = createAgent({
|
|
555
|
+
model,
|
|
556
|
+
...applySecurityPolicy("production"),
|
|
557
|
+
});
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**Available presets:**
|
|
561
|
+
|
|
562
|
+
- `"development"` - Permissive settings for rapid iteration (allows all operations)
|
|
563
|
+
- `"ci"` - Restrictive settings for CI/CD (blocks network operations, plan mode only)
|
|
564
|
+
- `"production"` - Balanced settings for production (blocks destructive operations, limited timeouts)
|
|
565
|
+
- `"readonly"` - Maximum restrictions (no writes, no commands, read-only access)
|
|
566
|
+
|
|
567
|
+
### Guardrails Hooks
|
|
568
|
+
|
|
569
|
+
Protect against harmful input and filter sensitive output:
|
|
570
|
+
|
|
571
|
+
```typescript
|
|
572
|
+
import { createGuardrailsHooks } from "@lleverage-ai/agent-sdk/hooks";
|
|
573
|
+
|
|
574
|
+
const agent = createAgent({
|
|
575
|
+
model,
|
|
576
|
+
hooks: createGuardrailsHooks({
|
|
577
|
+
blockedInputPatterns: [
|
|
578
|
+
/ignore\s+previous\s+instructions/i, // Prompt injection
|
|
579
|
+
/system\s+prompt/i, // System prompt extraction
|
|
580
|
+
],
|
|
581
|
+
blockedOutputPatterns: [
|
|
582
|
+
/\d{3}-\d{2}-\d{4}/g, // SSN
|
|
583
|
+
/\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}/g, // Credit card
|
|
584
|
+
],
|
|
585
|
+
blockedInputMessage: "Request blocked by content policy",
|
|
586
|
+
filteredOutputMessage: "[Content filtered]",
|
|
587
|
+
}),
|
|
588
|
+
});
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### Secrets Filtering
|
|
592
|
+
|
|
593
|
+
Prevent credential leakage in logs and responses:
|
|
594
|
+
|
|
595
|
+
```typescript
|
|
596
|
+
import {
|
|
597
|
+
createSecretsFilterHooks,
|
|
598
|
+
COMMON_SECRET_PATTERNS,
|
|
599
|
+
} from "@lleverage-ai/agent-sdk/hooks";
|
|
600
|
+
|
|
601
|
+
const agent = createAgent({
|
|
602
|
+
model,
|
|
603
|
+
hooks: createSecretsFilterHooks({
|
|
604
|
+
patterns: Object.values(COMMON_SECRET_PATTERNS),
|
|
605
|
+
customPatterns: [/my-api-key-[A-Za-z0-9]+/g],
|
|
606
|
+
redactionText: "[REDACTED]",
|
|
607
|
+
onSecretDetected: (type, pattern, context) => {
|
|
608
|
+
console.warn(`Secret detected in ${type}:`, pattern);
|
|
609
|
+
},
|
|
610
|
+
}),
|
|
611
|
+
});
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
**Built-in secret patterns:**
|
|
615
|
+
|
|
616
|
+
- AWS access keys and secrets
|
|
617
|
+
- GitHub tokens (personal access, OAuth)
|
|
618
|
+
- JWT tokens
|
|
619
|
+
- Private keys (PEM format)
|
|
620
|
+
- Slack, Stripe, and other common API keys
|
|
621
|
+
- Password and secret variables
|
|
622
|
+
|
|
623
|
+
### Tool Restrictions
|
|
624
|
+
|
|
625
|
+
Disable dangerous tools in production:
|
|
626
|
+
|
|
627
|
+
```typescript
|
|
628
|
+
const agent = createAgent({
|
|
629
|
+
model,
|
|
630
|
+
// Explicitly disable core tools that allow code execution or file writes
|
|
631
|
+
disabledCoreTools: ["bash", "write", "edit"],
|
|
632
|
+
|
|
633
|
+
// Or use allowlist approach
|
|
634
|
+
allowedTools: ["read", "glob", "grep", "search_tools"],
|
|
635
|
+
});
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
### Permission Mode: acceptEdits with Bash Safety
|
|
639
|
+
|
|
640
|
+
The `acceptEdits` permission mode auto-approves `write` and `edit` tool calls, but **shell commands can still perform file writes** (e.g., `echo > file`, `rm`, `mv`), creating a security gap.
|
|
641
|
+
|
|
642
|
+
To close this gap, use `applySecurityPolicy()` with `acceptEdits` mode - it automatically configures the sandbox to block shell-based file operations:
|
|
643
|
+
|
|
644
|
+
```typescript
|
|
645
|
+
import { applySecurityPolicy } from "@lleverage-ai/agent-sdk/security";
|
|
646
|
+
|
|
647
|
+
// acceptEdits mode with shell file operation blocking (default behavior)
|
|
648
|
+
const agent = createAgent({
|
|
649
|
+
model,
|
|
650
|
+
...applySecurityPolicy("development", {
|
|
651
|
+
permissionMode: "acceptEdits",
|
|
652
|
+
// blockShellFileOps: true is the default
|
|
653
|
+
}),
|
|
654
|
+
});
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
**What gets blocked in acceptEdits mode:**
|
|
658
|
+
|
|
659
|
+
- Output redirection: `echo 'text' > file.txt`, `cat input >> output`
|
|
660
|
+
- File deletion/movement: `rm file.txt`, `mv old new`
|
|
661
|
+
- File creation: `touch file`, `cp source dest`, `mkdir dir`
|
|
662
|
+
- Permission changes: `chmod`, `chown`
|
|
663
|
+
- Package managers: `npm install`, `yarn add`, `pip install`
|
|
664
|
+
|
|
665
|
+
**What remains allowed:**
|
|
666
|
+
|
|
667
|
+
- Read operations: `ls`, `cat`, `grep`, `find`, `head`, `tail`
|
|
668
|
+
- The `write` and `edit` tools (approved by acceptEdits mode)
|
|
669
|
+
|
|
670
|
+
**Manual configuration:**
|
|
671
|
+
|
|
672
|
+
```typescript
|
|
673
|
+
import { getSandboxOptionsForAcceptEdits } from "@lleverage-ai/agent-sdk/security";
|
|
674
|
+
import { LocalSandbox } from "@lleverage-ai/agent-sdk/backends";
|
|
675
|
+
|
|
676
|
+
const agent = createAgent({
|
|
677
|
+
model,
|
|
678
|
+
backend: new LocalSandbox(getSandboxOptionsForAcceptEdits()),
|
|
679
|
+
permissionMode: "acceptEdits",
|
|
680
|
+
});
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
**Disable blocking (not recommended for production):**
|
|
684
|
+
|
|
685
|
+
```typescript
|
|
686
|
+
const agent = createAgent({
|
|
687
|
+
model,
|
|
688
|
+
...applySecurityPolicy("development", {
|
|
689
|
+
permissionMode: "acceptEdits",
|
|
690
|
+
blockShellFileOps: false, // Allow bash file operations
|
|
691
|
+
}),
|
|
692
|
+
});
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
### Complete Production Example
|
|
696
|
+
|
|
697
|
+
Combining security policies, guardrails, and secrets filtering:
|
|
698
|
+
|
|
699
|
+
```typescript
|
|
700
|
+
import { createAgent } from "@lleverage-ai/agent-sdk";
|
|
701
|
+
import { applySecurityPolicy } from "@lleverage-ai/agent-sdk/security";
|
|
702
|
+
import {
|
|
703
|
+
createGuardrailsHooks,
|
|
704
|
+
createSecretsFilterHooks,
|
|
705
|
+
COMMON_SECRET_PATTERNS,
|
|
706
|
+
} from "@lleverage-ai/agent-sdk/hooks";
|
|
707
|
+
|
|
708
|
+
const agent = createAgent({
|
|
709
|
+
model,
|
|
710
|
+
|
|
711
|
+
// Apply production security preset
|
|
712
|
+
...applySecurityPolicy("production"),
|
|
713
|
+
|
|
714
|
+
// Add guardrails and secrets filtering
|
|
715
|
+
hooks: {
|
|
716
|
+
...createGuardrailsHooks({
|
|
717
|
+
blockedInputPatterns: [/ignore\s+previous\s+instructions/i],
|
|
718
|
+
blockedOutputPatterns: [/\d{3}-\d{2}-\d{4}/g],
|
|
719
|
+
}),
|
|
720
|
+
...createSecretsFilterHooks({
|
|
721
|
+
patterns: Object.values(COMMON_SECRET_PATTERNS),
|
|
722
|
+
}),
|
|
723
|
+
},
|
|
724
|
+
|
|
725
|
+
// Additional tool restrictions
|
|
726
|
+
disabledCoreTools: ["bash"],
|
|
727
|
+
});
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
## Subagents
|
|
731
|
+
|
|
732
|
+
Subagents enable task delegation to specialized agents. Define subagents with their own system prompts and tools:
|
|
733
|
+
|
|
734
|
+
```typescript
|
|
735
|
+
import { createSubagent } from "@lleverage-ai/agent-sdk";
|
|
736
|
+
|
|
737
|
+
const researcherSubagent = createSubagent({
|
|
738
|
+
id: "researcher",
|
|
739
|
+
name: "Researcher",
|
|
740
|
+
description: "Researches topics and gathers information",
|
|
741
|
+
systemPrompt:
|
|
742
|
+
"You are a research specialist. Gather comprehensive information.",
|
|
743
|
+
plugins: [webSearchPlugin],
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
const writerSubagent = createSubagent({
|
|
747
|
+
id: "writer",
|
|
748
|
+
name: "Writer",
|
|
749
|
+
description: "Writes content based on research",
|
|
750
|
+
systemPrompt: "You are a content writer. Create clear, engaging content.",
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
// Provide subagents to the parent agent
|
|
754
|
+
const agent = createAgent({
|
|
755
|
+
model,
|
|
756
|
+
systemPrompt: "Coordinate research and writing tasks.",
|
|
757
|
+
subagents: [researcherSubagent, writerSubagent],
|
|
758
|
+
});
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
The parent agent receives a `task` tool to delegate work:
|
|
762
|
+
|
|
763
|
+
```typescript
|
|
764
|
+
// Agent can call: task({ subagent: "researcher", prompt: "Research AI trends" })
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
### Advanced Subagent Execution
|
|
768
|
+
|
|
769
|
+
For programmatic control over subagent execution:
|
|
770
|
+
|
|
771
|
+
```typescript
|
|
772
|
+
import {
|
|
773
|
+
createSubagentContext,
|
|
774
|
+
executeSubagent,
|
|
775
|
+
executeSubagentsParallel,
|
|
776
|
+
} from "@lleverage-ai/agent-sdk";
|
|
777
|
+
|
|
778
|
+
// Create isolated context for subagent
|
|
779
|
+
const context = createSubagentContext({
|
|
780
|
+
parentMessages: messages,
|
|
781
|
+
subagentSystemPrompt: "You are a specialist.",
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
// Execute single subagent
|
|
785
|
+
const result = await executeSubagent({
|
|
786
|
+
subagent: researcherSubagent,
|
|
787
|
+
model,
|
|
788
|
+
context,
|
|
789
|
+
prompt: "Research this topic",
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
// Execute multiple subagents in parallel
|
|
793
|
+
const results = await executeSubagentsParallel({
|
|
794
|
+
subagents: [researcherSubagent, analyzerSubagent],
|
|
795
|
+
model,
|
|
796
|
+
contexts: [context1, context2],
|
|
797
|
+
prompts: ["Research...", "Analyze..."],
|
|
798
|
+
});
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
### Background Tasks and Recovery
|
|
802
|
+
|
|
803
|
+
Background tasks allow subagents to run asynchronously without blocking the parent agent. When persistence is configured, tasks survive process restarts and can be recovered.
|
|
804
|
+
|
|
805
|
+
#### Task Store Configuration
|
|
806
|
+
|
|
807
|
+
```typescript
|
|
808
|
+
import { FileTaskStore } from "@lleverage-ai/agent-sdk/task-store";
|
|
809
|
+
|
|
810
|
+
// Configure persistent task storage
|
|
811
|
+
const taskStore = new FileTaskStore({
|
|
812
|
+
directory: "./task-data",
|
|
813
|
+
expirationMs: 86400000, // 24 hours
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
// Create agent with task store
|
|
817
|
+
const agent = createAgent({
|
|
818
|
+
model,
|
|
819
|
+
subagents: [researcherSubagent],
|
|
820
|
+
taskStore, // Tasks now persist across restarts
|
|
821
|
+
});
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
#### Task Lifecycle
|
|
825
|
+
|
|
826
|
+
Background tasks progress through these states:
|
|
827
|
+
|
|
828
|
+
1. **pending**: Task created, not yet started
|
|
829
|
+
2. **running**: Task is currently executing
|
|
830
|
+
3. **completed**: Task finished successfully
|
|
831
|
+
4. **failed**: Task encountered an error
|
|
832
|
+
|
|
833
|
+
```typescript
|
|
834
|
+
import {
|
|
835
|
+
listBackgroundTasks,
|
|
836
|
+
getBackgroundTask,
|
|
837
|
+
clearCompletedTasks,
|
|
838
|
+
} from "@lleverage-ai/agent-sdk";
|
|
839
|
+
|
|
840
|
+
// List all running tasks
|
|
841
|
+
const runningTasks = await listBackgroundTasks({ status: "running" });
|
|
842
|
+
|
|
843
|
+
// Get a specific task
|
|
844
|
+
const task = await getBackgroundTask("task-123");
|
|
845
|
+
console.log(task.status, task.result, task.error);
|
|
846
|
+
|
|
847
|
+
// Clean up old completed tasks
|
|
848
|
+
const cleaned = await clearCompletedTasks();
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
#### Recovery Patterns
|
|
852
|
+
|
|
853
|
+
The SDK provides utilities for automatic task recovery on agent restart:
|
|
854
|
+
|
|
855
|
+
**1. Recover Interrupted Tasks**
|
|
856
|
+
|
|
857
|
+
When your agent restarts, running tasks are interrupted. Mark them as failed:
|
|
858
|
+
|
|
859
|
+
```typescript
|
|
860
|
+
import { recoverRunningTasks } from "@lleverage-ai/agent-sdk";
|
|
861
|
+
|
|
862
|
+
// On agent startup
|
|
863
|
+
const recovered = await recoverRunningTasks(taskStore);
|
|
864
|
+
console.log(`Recovered ${recovered} interrupted tasks`);
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
**2. Recover Failed Tasks for Retry**
|
|
868
|
+
|
|
869
|
+
Load failed tasks and retry those with transient errors:
|
|
870
|
+
|
|
871
|
+
```typescript
|
|
872
|
+
import {
|
|
873
|
+
recoverFailedTasks,
|
|
874
|
+
updateBackgroundTask,
|
|
875
|
+
} from "@lleverage-ai/agent-sdk";
|
|
876
|
+
|
|
877
|
+
// Load failed tasks
|
|
878
|
+
const failedTasks = await recoverFailedTasks(taskStore, {
|
|
879
|
+
errorPattern: /timeout|network|ECONNREFUSED/,
|
|
880
|
+
minCreatedAt: new Date(Date.now() - 86400000), // Last 24 hours
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
// Retry transient failures
|
|
884
|
+
for (const task of failedTasks) {
|
|
885
|
+
const retryTask = updateBackgroundTask(task, {
|
|
886
|
+
status: "pending",
|
|
887
|
+
error: undefined,
|
|
888
|
+
});
|
|
889
|
+
await taskStore.save(retryTask);
|
|
890
|
+
// Process retryTask through your task execution logic
|
|
891
|
+
}
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
**3. Clean Up Stale Tasks**
|
|
895
|
+
|
|
896
|
+
Prevent unbounded storage growth by removing old tasks:
|
|
897
|
+
|
|
898
|
+
```typescript
|
|
899
|
+
import { cleanupStaleTasks } from "@lleverage-ai/agent-sdk";
|
|
900
|
+
|
|
901
|
+
// Clean up tasks older than 7 days
|
|
902
|
+
const sevenDays = 7 * 24 * 60 * 60 * 1000;
|
|
903
|
+
const cleaned = await cleanupStaleTasks(taskStore, sevenDays);
|
|
904
|
+
console.log(`Cleaned up ${cleaned} stale tasks`);
|
|
905
|
+
|
|
906
|
+
// Schedule periodic cleanup
|
|
907
|
+
setInterval(async () => {
|
|
908
|
+
await cleanupStaleTasks(taskStore, sevenDays);
|
|
909
|
+
}, 86400000); // Once per day
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
**Complete Recovery Pattern**
|
|
913
|
+
|
|
914
|
+
Combine all recovery utilities on agent startup:
|
|
915
|
+
|
|
916
|
+
```typescript
|
|
917
|
+
async function initializeAgent() {
|
|
918
|
+
const taskStore = new FileTaskStore({ directory: "./task-data" });
|
|
919
|
+
|
|
920
|
+
// 1. Recover interrupted tasks
|
|
921
|
+
await recoverRunningTasks(taskStore);
|
|
922
|
+
|
|
923
|
+
// 2. Retry failed tasks with transient errors
|
|
924
|
+
const failedTasks = await recoverFailedTasks(taskStore, {
|
|
925
|
+
errorPattern: /timeout|network/,
|
|
926
|
+
});
|
|
927
|
+
for (const task of failedTasks) {
|
|
928
|
+
const retryTask = updateBackgroundTask(task, {
|
|
929
|
+
status: "pending",
|
|
930
|
+
error: undefined,
|
|
931
|
+
});
|
|
932
|
+
await taskStore.save(retryTask);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// 3. Clean up old tasks
|
|
936
|
+
const sevenDays = 7 * 24 * 60 * 60 * 1000;
|
|
937
|
+
await cleanupStaleTasks(taskStore, sevenDays);
|
|
938
|
+
|
|
939
|
+
// 4. Create agent
|
|
940
|
+
return createAgent({
|
|
941
|
+
model,
|
|
942
|
+
subagents: [researcherSubagent],
|
|
943
|
+
taskStore,
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
## Streaming
|
|
949
|
+
|
|
950
|
+
Agents support streaming responses for real-time output:
|
|
951
|
+
|
|
952
|
+
```typescript
|
|
953
|
+
// AsyncIterator-based streaming
|
|
954
|
+
for await (const part of agent.stream({ prompt: "Tell me a story" })) {
|
|
955
|
+
if (part.type === "text-delta") {
|
|
956
|
+
process.stdout.write(part.text);
|
|
957
|
+
} else if (part.type === "tool-call") {
|
|
958
|
+
console.log("Calling tool:", part.toolName);
|
|
959
|
+
} else if (part.type === "finish") {
|
|
960
|
+
console.log("\nDone:", part.finishReason);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// Next.js API route with useChat compatibility
|
|
965
|
+
export async function POST(req: Request) {
|
|
966
|
+
const { messages } = await req.json();
|
|
967
|
+
return agent.streamResponse({ messages });
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// Raw AI SDK streamText result
|
|
971
|
+
const stream = await agent.streamRaw({ messages });
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
## MCP Integration
|
|
975
|
+
|
|
976
|
+
The SDK provides unified tool management through the Model Context Protocol (MCP).
|
|
977
|
+
|
|
978
|
+
### Plugin-based MCP Tools
|
|
979
|
+
|
|
980
|
+
Plugin tools are automatically registered as virtual MCP servers:
|
|
981
|
+
|
|
982
|
+
```typescript
|
|
983
|
+
const githubPlugin = definePlugin({
|
|
984
|
+
name: "github",
|
|
985
|
+
mcpServer: {
|
|
986
|
+
type: "stdio",
|
|
987
|
+
command: "npx",
|
|
988
|
+
args: ["-y", "@modelcontextprotocol/server-github"],
|
|
989
|
+
env: { GITHUB_TOKEN: "${GITHUB_TOKEN}" }, // Expands from process.env
|
|
990
|
+
},
|
|
991
|
+
});
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
### Tool Search and Deferred Loading
|
|
995
|
+
|
|
996
|
+
When you have many plugin tools, enable tool search to reduce context size:
|
|
997
|
+
|
|
998
|
+
```typescript
|
|
999
|
+
const agent = createAgent({
|
|
1000
|
+
model,
|
|
1001
|
+
plugins: [plugin1, plugin2, plugin3],
|
|
1002
|
+
toolSearch: {
|
|
1003
|
+
enabled: "auto", // "auto" | "always" | "never"
|
|
1004
|
+
threshold: 20, // Defer when plugin tools > 20
|
|
1005
|
+
maxResults: 10, // Max search results
|
|
1006
|
+
},
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
// Agent gets a `search_tools` tool to discover and load tools on-demand
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
### MCP Tool Utilities
|
|
1013
|
+
|
|
1014
|
+
Helper functions for working with MCP tool names:
|
|
1015
|
+
|
|
1016
|
+
```typescript
|
|
1017
|
+
import {
|
|
1018
|
+
mcpTools,
|
|
1019
|
+
mcpToolsFor,
|
|
1020
|
+
toolsFromPlugin,
|
|
1021
|
+
} from "@lleverage-ai/agent-sdk";
|
|
1022
|
+
|
|
1023
|
+
// Get all MCP tool names from an agent
|
|
1024
|
+
const allMcpTools = mcpTools(agent); // ["mcp__github__list_issues", ...]
|
|
1025
|
+
|
|
1026
|
+
// Get tools for a specific plugin
|
|
1027
|
+
const githubTools = mcpToolsFor(agent, "github");
|
|
1028
|
+
|
|
1029
|
+
// Extract tools from a plugin definition
|
|
1030
|
+
const tools = toolsFromPlugin(myPlugin);
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
### MCP Security Best Practices
|
|
1034
|
+
|
|
1035
|
+
When connecting to external MCP servers, apply security controls to protect against malicious or misconfigured tools:
|
|
1036
|
+
|
|
1037
|
+
```typescript
|
|
1038
|
+
import { createAgent } from "@lleverage-ai/agent-sdk";
|
|
1039
|
+
|
|
1040
|
+
const githubPlugin = definePlugin({
|
|
1041
|
+
name: "github",
|
|
1042
|
+
mcpServer: {
|
|
1043
|
+
type: "stdio",
|
|
1044
|
+
command: "npx",
|
|
1045
|
+
args: ["-y", "@modelcontextprotocol/server-github"],
|
|
1046
|
+
env: { GITHUB_TOKEN: "${GITHUB_TOKEN}" },
|
|
1047
|
+
|
|
1048
|
+
// Security: Only allow specific tools from this server
|
|
1049
|
+
allowedTools: ["get_issue", "list_issues", "search_issues"],
|
|
1050
|
+
|
|
1051
|
+
// Security: Validate tool inputs against their JSON Schema
|
|
1052
|
+
validateInputs: true,
|
|
1053
|
+
|
|
1054
|
+
// Security: Reject tools without meaningful schemas
|
|
1055
|
+
requireSchema: true,
|
|
1056
|
+
},
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
const agent = createAgent({
|
|
1060
|
+
model,
|
|
1061
|
+
plugins: [githubPlugin],
|
|
1062
|
+
});
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
#### Security Options
|
|
1066
|
+
|
|
1067
|
+
- **`allowedTools`**: Allowlist of permitted tool names (without `mcp__` prefix). Only tools in this list will be loaded. Useful for restricting access to dangerous operations.
|
|
1068
|
+
|
|
1069
|
+
- **`validateInputs`**: When `true`, tool inputs are validated against their declared JSON Schema before execution. Invalid inputs throw `MCPInputValidationError` instead of being passed to the server. Protects against malformed or malicious inputs.
|
|
1070
|
+
|
|
1071
|
+
- **`requireSchema`**: When `true`, tools without meaningful schemas (empty or minimal schemas) are rejected during connection. Ensures all tools have explicit input validation.
|
|
1072
|
+
|
|
1073
|
+
#### Example: Secure Production MCP Configuration
|
|
1074
|
+
|
|
1075
|
+
```typescript
|
|
1076
|
+
// Production configuration with all security controls
|
|
1077
|
+
const docsPlugin = definePlugin({
|
|
1078
|
+
name: "docs",
|
|
1079
|
+
mcpServer: {
|
|
1080
|
+
type: "http",
|
|
1081
|
+
url: "https://docs-server.internal/mcp",
|
|
1082
|
+
headers: { Authorization: "Bearer ${DOCS_API_TOKEN}" },
|
|
1083
|
+
|
|
1084
|
+
// Only allow read-only operations
|
|
1085
|
+
allowedTools: ["search_docs", "get_document", "list_categories"],
|
|
1086
|
+
|
|
1087
|
+
// Validate all inputs
|
|
1088
|
+
validateInputs: true,
|
|
1089
|
+
|
|
1090
|
+
// Require schemas for all tools
|
|
1091
|
+
requireSchema: true,
|
|
1092
|
+
},
|
|
1093
|
+
});
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
#### Handling Validation Errors
|
|
1097
|
+
|
|
1098
|
+
```typescript
|
|
1099
|
+
import { MCPInputValidationError } from "@lleverage-ai/agent-sdk";
|
|
1100
|
+
|
|
1101
|
+
try {
|
|
1102
|
+
const result = await agent.generate({
|
|
1103
|
+
messages: [{ role: "user", content: "Search the docs" }],
|
|
1104
|
+
});
|
|
1105
|
+
} catch (error) {
|
|
1106
|
+
if (error instanceof MCPInputValidationError) {
|
|
1107
|
+
console.error(`Invalid input for ${error.toolName}:`);
|
|
1108
|
+
console.error(error.errors.join("\n"));
|
|
1109
|
+
// Handle validation error (e.g., log, alert, retry with corrected input)
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
## Backends
|
|
1115
|
+
|
|
1116
|
+
Backends provide filesystem and execution capabilities.
|
|
1117
|
+
|
|
1118
|
+
### FilesystemBackend
|
|
1119
|
+
|
|
1120
|
+
File operations with security protections:
|
|
1121
|
+
|
|
1122
|
+
```typescript
|
|
1123
|
+
import {
|
|
1124
|
+
FilesystemBackend,
|
|
1125
|
+
createFilesystemBackend,
|
|
1126
|
+
} from "@lleverage-ai/agent-sdk";
|
|
1127
|
+
|
|
1128
|
+
const backend = new FilesystemBackend({
|
|
1129
|
+
rootDir: "/project",
|
|
1130
|
+
allowedPaths: ["/project/src", "/project/tests"],
|
|
1131
|
+
maxFileSize: 10 * 1024 * 1024, // 10MB
|
|
1132
|
+
followSymlinks: false,
|
|
1133
|
+
});
|
|
1134
|
+
|
|
1135
|
+
// Or use factory
|
|
1136
|
+
const backend = createFilesystemBackend({ rootDir: "/project" });
|
|
1137
|
+
```
|
|
1138
|
+
|
|
1139
|
+
### StateBackend
|
|
1140
|
+
|
|
1141
|
+
In-memory filesystem for sandboxed operations:
|
|
1142
|
+
|
|
1143
|
+
```typescript
|
|
1144
|
+
import { StateBackend, createAgentState } from "@lleverage-ai/agent-sdk";
|
|
1145
|
+
|
|
1146
|
+
const state = createAgentState();
|
|
1147
|
+
const backend = new StateBackend(state);
|
|
1148
|
+
```
|
|
1149
|
+
|
|
1150
|
+
### LocalSandbox
|
|
1151
|
+
|
|
1152
|
+
Command execution with security controls:
|
|
1153
|
+
|
|
1154
|
+
```typescript
|
|
1155
|
+
import { LocalSandbox, createLocalSandbox } from "@lleverage-ai/agent-sdk";
|
|
1156
|
+
|
|
1157
|
+
const sandbox = new LocalSandbox({
|
|
1158
|
+
cwd: "/project",
|
|
1159
|
+
timeout: 30000,
|
|
1160
|
+
maxOutputSize: 1024 * 1024,
|
|
1161
|
+
blockedCommands: ["rm -rf /"],
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
// Read-only mode
|
|
1165
|
+
const readOnlySandbox = LocalSandbox.readOnly({ cwd: "/project" });
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
## Middleware
|
|
1169
|
+
|
|
1170
|
+
Middleware intercepts and transforms agent operations.
|
|
1171
|
+
|
|
1172
|
+
### Logging Middleware
|
|
1173
|
+
|
|
1174
|
+
```typescript
|
|
1175
|
+
import {
|
|
1176
|
+
createLoggingMiddleware,
|
|
1177
|
+
createTimingMiddleware,
|
|
1178
|
+
} from "@lleverage-ai/agent-sdk";
|
|
1179
|
+
|
|
1180
|
+
const agent = createAgent({
|
|
1181
|
+
model,
|
|
1182
|
+
middleware: [
|
|
1183
|
+
createLoggingMiddleware({ level: "debug" }),
|
|
1184
|
+
createTimingMiddleware(),
|
|
1185
|
+
],
|
|
1186
|
+
});
|
|
1187
|
+
```
|
|
1188
|
+
|
|
1189
|
+
### Caching Middleware
|
|
1190
|
+
|
|
1191
|
+
```typescript
|
|
1192
|
+
import {
|
|
1193
|
+
createCacheMiddleware,
|
|
1194
|
+
InMemoryCacheStore,
|
|
1195
|
+
} from "@lleverage-ai/agent-sdk";
|
|
1196
|
+
|
|
1197
|
+
const cacheStore = new InMemoryCacheStore();
|
|
1198
|
+
|
|
1199
|
+
const agent = createAgent({
|
|
1200
|
+
model,
|
|
1201
|
+
middleware: [
|
|
1202
|
+
createCacheMiddleware({
|
|
1203
|
+
store: cacheStore,
|
|
1204
|
+
ttl: 60000, // 1 minute
|
|
1205
|
+
}),
|
|
1206
|
+
],
|
|
1207
|
+
});
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
### Retry Middleware
|
|
1211
|
+
|
|
1212
|
+
```typescript
|
|
1213
|
+
import {
|
|
1214
|
+
createRetryMiddleware,
|
|
1215
|
+
createCircuitBreakerMiddleware,
|
|
1216
|
+
} from "@lleverage-ai/agent-sdk";
|
|
1217
|
+
|
|
1218
|
+
const agent = createAgent({
|
|
1219
|
+
model,
|
|
1220
|
+
middleware: [
|
|
1221
|
+
createRetryMiddleware({
|
|
1222
|
+
maxRetries: 3,
|
|
1223
|
+
backoff: "exponential",
|
|
1224
|
+
}),
|
|
1225
|
+
createCircuitBreakerMiddleware({
|
|
1226
|
+
threshold: 5,
|
|
1227
|
+
resetTimeout: 60000,
|
|
1228
|
+
}),
|
|
1229
|
+
],
|
|
1230
|
+
});
|
|
1231
|
+
```
|
|
1232
|
+
|
|
1233
|
+
### Guardrails Middleware
|
|
1234
|
+
|
|
1235
|
+
```typescript
|
|
1236
|
+
import {
|
|
1237
|
+
createGuardrailsMiddleware,
|
|
1238
|
+
createContentFilterMiddleware,
|
|
1239
|
+
} from "@lleverage-ai/agent-sdk";
|
|
1240
|
+
|
|
1241
|
+
const agent = createAgent({
|
|
1242
|
+
model,
|
|
1243
|
+
middleware: [
|
|
1244
|
+
createGuardrailsMiddleware({
|
|
1245
|
+
blockedPatterns: [/password/i, /api[_-]?key/i],
|
|
1246
|
+
}),
|
|
1247
|
+
createContentFilterMiddleware({
|
|
1248
|
+
maxLength: 10000,
|
|
1249
|
+
}),
|
|
1250
|
+
],
|
|
1251
|
+
});
|
|
1252
|
+
```
|
|
1253
|
+
|
|
1254
|
+
### Composing Middleware
|
|
1255
|
+
|
|
1256
|
+
```typescript
|
|
1257
|
+
import { composeMiddleware } from "@lleverage-ai/agent-sdk";
|
|
1258
|
+
|
|
1259
|
+
const combined = composeMiddleware([
|
|
1260
|
+
createLoggingMiddleware(),
|
|
1261
|
+
createRetryMiddleware({ maxRetries: 3 }),
|
|
1262
|
+
createCacheMiddleware({ store }),
|
|
1263
|
+
]);
|
|
1264
|
+
```
|
|
1265
|
+
|
|
1266
|
+
## Observability
|
|
1267
|
+
|
|
1268
|
+
Built-in logging, metrics, and tracing support.
|
|
1269
|
+
|
|
1270
|
+
### Logging
|
|
1271
|
+
|
|
1272
|
+
```typescript
|
|
1273
|
+
import {
|
|
1274
|
+
createLogger,
|
|
1275
|
+
createConsoleTransport,
|
|
1276
|
+
createJsonFormatter,
|
|
1277
|
+
} from "@lleverage-ai/agent-sdk";
|
|
1278
|
+
|
|
1279
|
+
const logger = createLogger({
|
|
1280
|
+
level: "info",
|
|
1281
|
+
transports: [
|
|
1282
|
+
createConsoleTransport({
|
|
1283
|
+
formatter: createJsonFormatter(),
|
|
1284
|
+
}),
|
|
1285
|
+
],
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
logger.info("Agent started", { agentId: "123" });
|
|
1289
|
+
```
|
|
1290
|
+
|
|
1291
|
+
### Metrics
|
|
1292
|
+
|
|
1293
|
+
```typescript
|
|
1294
|
+
import {
|
|
1295
|
+
createMetricsRegistry,
|
|
1296
|
+
createAgentMetrics,
|
|
1297
|
+
createConsoleMetricsExporter,
|
|
1298
|
+
} from "@lleverage-ai/agent-sdk";
|
|
1299
|
+
|
|
1300
|
+
const registry = createMetricsRegistry({
|
|
1301
|
+
exporters: [createConsoleMetricsExporter()],
|
|
1302
|
+
});
|
|
1303
|
+
|
|
1304
|
+
const metrics = createAgentMetrics(registry);
|
|
1305
|
+
metrics.requestsTotal.inc({ model: "claude-3" });
|
|
1306
|
+
metrics.latencyHistogram.observe({ model: "claude-3" }, 1500);
|
|
1307
|
+
```
|
|
1308
|
+
|
|
1309
|
+
### Tracing
|
|
1310
|
+
|
|
1311
|
+
```typescript
|
|
1312
|
+
import {
|
|
1313
|
+
createTracer,
|
|
1314
|
+
createConsoleSpanExporter,
|
|
1315
|
+
SemanticAttributes,
|
|
1316
|
+
} from "@lleverage-ai/agent-sdk";
|
|
1317
|
+
|
|
1318
|
+
const tracer = createTracer({
|
|
1319
|
+
serviceName: "my-agent",
|
|
1320
|
+
exporters: [createConsoleSpanExporter()],
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
const span = tracer.startSpan("agent.generate");
|
|
1324
|
+
span.setAttribute(SemanticAttributes.MODEL_NAME, "claude-3");
|
|
1325
|
+
// ... do work
|
|
1326
|
+
span.end();
|
|
1327
|
+
```
|
|
1328
|
+
|
|
1329
|
+
### Cross-Agent Tracing
|
|
1330
|
+
|
|
1331
|
+
When using subagents, you can propagate trace context to maintain distributed tracing across parent and child agents:
|
|
1332
|
+
|
|
1333
|
+
```typescript
|
|
1334
|
+
import {
|
|
1335
|
+
createAgent,
|
|
1336
|
+
createTaskTool,
|
|
1337
|
+
createSubagent,
|
|
1338
|
+
createTracer,
|
|
1339
|
+
SemanticAttributes,
|
|
1340
|
+
} from "@lleverage-ai/agent-sdk";
|
|
1341
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
1342
|
+
|
|
1343
|
+
const tracer = createTracer({
|
|
1344
|
+
name: "my-app",
|
|
1345
|
+
exporters: [createConsoleSpanExporter()],
|
|
1346
|
+
});
|
|
1347
|
+
|
|
1348
|
+
// Create parent agent
|
|
1349
|
+
const parentAgent = createAgent({
|
|
1350
|
+
model: anthropic("claude-sonnet-4-20250514"),
|
|
1351
|
+
});
|
|
1352
|
+
|
|
1353
|
+
// Start a parent span for the request
|
|
1354
|
+
const parentSpan = tracer.startSpan("handle-user-request", {
|
|
1355
|
+
attributes: {
|
|
1356
|
+
[SemanticAttributes.AGENT_NAME]: "parent-agent",
|
|
1357
|
+
},
|
|
1358
|
+
});
|
|
1359
|
+
|
|
1360
|
+
// Define subagent with tracing support
|
|
1361
|
+
const researcherSubagent = {
|
|
1362
|
+
type: "researcher",
|
|
1363
|
+
description: "Researches information",
|
|
1364
|
+
create: (ctx) => {
|
|
1365
|
+
// Create child span linked to parent
|
|
1366
|
+
if (ctx.parentSpanContext) {
|
|
1367
|
+
const span = tracer.startSpan("subagent-research", {
|
|
1368
|
+
parent: ctx.parentSpanContext,
|
|
1369
|
+
attributes: {
|
|
1370
|
+
[SemanticAttributes.SUBAGENT_TYPE]: "researcher",
|
|
1371
|
+
[SemanticAttributes.SUBAGENT_ID]: "researcher-1",
|
|
1372
|
+
},
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
// Track the span for cleanup
|
|
1376
|
+
// In production, use a span manager or store spans in agent state
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
return createSubagent(parentAgent, {
|
|
1380
|
+
name: "researcher",
|
|
1381
|
+
systemPrompt: "You are a research assistant.",
|
|
1382
|
+
});
|
|
1383
|
+
},
|
|
1384
|
+
};
|
|
1385
|
+
|
|
1386
|
+
// Create task tool with parent span context
|
|
1387
|
+
const task = createTaskTool({
|
|
1388
|
+
subagents: [researcherSubagent],
|
|
1389
|
+
defaultModel: anthropic("claude-sonnet-4-20250514"),
|
|
1390
|
+
parentAgent,
|
|
1391
|
+
parentSpanContext: {
|
|
1392
|
+
traceId: parentSpan.traceId,
|
|
1393
|
+
spanId: parentSpan.spanId,
|
|
1394
|
+
},
|
|
1395
|
+
});
|
|
1396
|
+
|
|
1397
|
+
// Use the agent with tracing
|
|
1398
|
+
const result = await parentAgent.generate({
|
|
1399
|
+
prompt: "Research quantum computing",
|
|
1400
|
+
tools: { task },
|
|
1401
|
+
});
|
|
1402
|
+
|
|
1403
|
+
// End the parent span
|
|
1404
|
+
parentSpan.end();
|
|
1405
|
+
await tracer.flush();
|
|
1406
|
+
```
|
|
1407
|
+
|
|
1408
|
+
This creates a distributed trace where:
|
|
1409
|
+
|
|
1410
|
+
- Parent span: `handle-user-request`
|
|
1411
|
+
- Child span: `subagent-research` (linked to parent)
|
|
1412
|
+
|
|
1413
|
+
Both spans share the same `traceId`, allowing you to:
|
|
1414
|
+
|
|
1415
|
+
- Track the full request flow across agents
|
|
1416
|
+
- Measure latency at each level
|
|
1417
|
+
- Correlate logs and errors across parent and child operations
|
|
1418
|
+
- Visualize the call graph in tools like Jaeger or Zipkin
|
|
1419
|
+
|
|
1420
|
+
**Key points:**
|
|
1421
|
+
|
|
1422
|
+
- Pass `parentSpanContext` to `createTaskTool()` to enable trace propagation
|
|
1423
|
+
- Access `ctx.parentSpanContext` in your subagent factory to create child spans
|
|
1424
|
+
- Use `SemanticAttributes.SUBAGENT_*` constants for consistent span metadata
|
|
1425
|
+
- Both parent and child spans will share the same `traceId` for correlation
|
|
1426
|
+
|
|
1427
|
+
### Observability Hooks
|
|
1428
|
+
|
|
1429
|
+
Automatically instrument agents with logging, metrics, and tracing:
|
|
1430
|
+
|
|
1431
|
+
```typescript
|
|
1432
|
+
import {
|
|
1433
|
+
createObservabilityEventStore,
|
|
1434
|
+
createObservabilityEventHooks,
|
|
1435
|
+
} from "@lleverage-ai/agent-sdk";
|
|
1436
|
+
|
|
1437
|
+
const store = createObservabilityEventStore();
|
|
1438
|
+
const hooks = createObservabilityEventHooks(store);
|
|
1439
|
+
|
|
1440
|
+
const agent = createAgent({
|
|
1441
|
+
model,
|
|
1442
|
+
hooks: {
|
|
1443
|
+
MCPConnectionFailed: hooks.MCPConnectionFailed,
|
|
1444
|
+
MCPConnectionRestored: hooks.MCPConnectionRestored,
|
|
1445
|
+
ToolRegistered: hooks.ToolRegistered,
|
|
1446
|
+
ToolLoadError: hooks.ToolLoadError,
|
|
1447
|
+
PreCompact: hooks.PreCompact,
|
|
1448
|
+
PostCompact: hooks.PostCompact,
|
|
1449
|
+
},
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
const events = store.getAll();
|
|
1453
|
+
```
|
|
1454
|
+
|
|
1455
|
+
## Memory
|
|
1456
|
+
|
|
1457
|
+
Persist agent memory across conversations.
|
|
1458
|
+
|
|
1459
|
+
### Filesystem Memory Store
|
|
1460
|
+
|
|
1461
|
+
```typescript
|
|
1462
|
+
import {
|
|
1463
|
+
FilesystemMemoryStore,
|
|
1464
|
+
loadAgentMemory,
|
|
1465
|
+
} from "@lleverage-ai/agent-sdk";
|
|
1466
|
+
|
|
1467
|
+
const store = new FilesystemMemoryStore({
|
|
1468
|
+
rootDir: ".agent/memory",
|
|
1469
|
+
});
|
|
1470
|
+
|
|
1471
|
+
// Load all memory for an agent
|
|
1472
|
+
const memory = await loadAgentMemory({
|
|
1473
|
+
store,
|
|
1474
|
+
agentId: "my-agent",
|
|
1475
|
+
});
|
|
1476
|
+
```
|
|
1477
|
+
|
|
1478
|
+
### In-Memory Store
|
|
1479
|
+
|
|
1480
|
+
```typescript
|
|
1481
|
+
import { InMemoryMemoryStore } from "@lleverage-ai/agent-sdk";
|
|
1482
|
+
|
|
1483
|
+
const store = new InMemoryMemoryStore();
|
|
1484
|
+
|
|
1485
|
+
// Create memory document
|
|
1486
|
+
await store.save({
|
|
1487
|
+
id: "note-1",
|
|
1488
|
+
content: "Important information...",
|
|
1489
|
+
metadata: {
|
|
1490
|
+
tags: ["important"],
|
|
1491
|
+
autoLoad: true,
|
|
1492
|
+
},
|
|
1493
|
+
});
|
|
1494
|
+
```
|
|
1495
|
+
|
|
1496
|
+
### Memory-Aware Middleware
|
|
1497
|
+
|
|
1498
|
+
```typescript
|
|
1499
|
+
import { createAgentMemoryMiddleware } from "@lleverage-ai/agent-sdk";
|
|
1500
|
+
|
|
1501
|
+
const agent = createAgent({
|
|
1502
|
+
model,
|
|
1503
|
+
middleware: [
|
|
1504
|
+
createAgentMemoryMiddleware({
|
|
1505
|
+
store,
|
|
1506
|
+
autoLoad: true,
|
|
1507
|
+
}),
|
|
1508
|
+
],
|
|
1509
|
+
});
|
|
1510
|
+
```
|
|
1511
|
+
|
|
1512
|
+
## Checkpointing
|
|
1513
|
+
|
|
1514
|
+
Save and restore agent state for resumable conversations.
|
|
1515
|
+
|
|
1516
|
+
### MemorySaver
|
|
1517
|
+
|
|
1518
|
+
In-memory checkpoint storage:
|
|
1519
|
+
|
|
1520
|
+
```typescript
|
|
1521
|
+
import { createMemorySaver } from "@lleverage-ai/agent-sdk";
|
|
1522
|
+
|
|
1523
|
+
const checkpointer = createMemorySaver();
|
|
1524
|
+
|
|
1525
|
+
const agent = createAgent({
|
|
1526
|
+
model,
|
|
1527
|
+
checkpointer,
|
|
1528
|
+
});
|
|
1529
|
+
|
|
1530
|
+
// Save checkpoint
|
|
1531
|
+
await checkpointer.save({
|
|
1532
|
+
threadId: "conversation-1",
|
|
1533
|
+
messages,
|
|
1534
|
+
metadata: { userId: "123" },
|
|
1535
|
+
});
|
|
1536
|
+
|
|
1537
|
+
// Load checkpoint
|
|
1538
|
+
const checkpoint = await checkpointer.load("conversation-1");
|
|
1539
|
+
```
|
|
1540
|
+
|
|
1541
|
+
### FileSaver
|
|
1542
|
+
|
|
1543
|
+
Persistent file-based checkpoints:
|
|
1544
|
+
|
|
1545
|
+
```typescript
|
|
1546
|
+
import { createFileSaver } from "@lleverage-ai/agent-sdk";
|
|
1547
|
+
|
|
1548
|
+
const checkpointer = createFileSaver({
|
|
1549
|
+
directory: ".agent/checkpoints",
|
|
1550
|
+
});
|
|
1551
|
+
```
|
|
1552
|
+
|
|
1553
|
+
### KeyValueStoreSaver
|
|
1554
|
+
|
|
1555
|
+
Use any key-value store:
|
|
1556
|
+
|
|
1557
|
+
```typescript
|
|
1558
|
+
import {
|
|
1559
|
+
createKeyValueStoreSaver,
|
|
1560
|
+
InMemoryStore,
|
|
1561
|
+
} from "@lleverage-ai/agent-sdk";
|
|
1562
|
+
|
|
1563
|
+
const store = new InMemoryStore();
|
|
1564
|
+
const checkpointer = createKeyValueStoreSaver({ store });
|
|
1565
|
+
```
|
|
1566
|
+
|
|
1567
|
+
## Context Compaction
|
|
1568
|
+
|
|
1569
|
+
Automatically manage conversation context with intelligent compaction policies.
|
|
1570
|
+
|
|
1571
|
+
### Basic Context Management
|
|
1572
|
+
|
|
1573
|
+
```typescript
|
|
1574
|
+
import { createAgent, createContextManager } from "@lleverage-ai/agent-sdk";
|
|
1575
|
+
|
|
1576
|
+
const contextManager = createContextManager({
|
|
1577
|
+
maxTokens: 100000, // Maximum context window
|
|
1578
|
+
policy: {
|
|
1579
|
+
enabled: true,
|
|
1580
|
+
tokenThreshold: 0.8, // Trigger at 80% capacity
|
|
1581
|
+
hardCapThreshold: 0.95, // Force compact at 95% (safety)
|
|
1582
|
+
enableGrowthRatePrediction: false, // Predict when next message will exceed
|
|
1583
|
+
enableErrorFallback: true, // Auto-compact on context length errors
|
|
1584
|
+
},
|
|
1585
|
+
summarization: {
|
|
1586
|
+
keepMessageCount: 10, // Always keep last 10 messages
|
|
1587
|
+
keepToolResultCount: 5, // Keep recent tool results
|
|
1588
|
+
},
|
|
1589
|
+
});
|
|
1590
|
+
|
|
1591
|
+
const agent = createAgent({
|
|
1592
|
+
model,
|
|
1593
|
+
contextManager,
|
|
1594
|
+
checkpointer, // Required for error fallback
|
|
1595
|
+
});
|
|
1596
|
+
```
|
|
1597
|
+
|
|
1598
|
+
### Compaction Triggers
|
|
1599
|
+
|
|
1600
|
+
The SDK supports multiple compaction triggers:
|
|
1601
|
+
|
|
1602
|
+
1. **Token Threshold** (default: 80%): Triggers when usage exceeds the threshold
|
|
1603
|
+
2. **Hard Cap** (default: 95%): Safety limit - forces compaction to prevent errors
|
|
1604
|
+
3. **Growth Rate**: Predicts if the next message will exceed limits
|
|
1605
|
+
4. **Error Fallback**: Emergency compaction when context length errors occur
|
|
1606
|
+
|
|
1607
|
+
```typescript
|
|
1608
|
+
const contextManager = createContextManager({
|
|
1609
|
+
maxTokens: 100000,
|
|
1610
|
+
policy: {
|
|
1611
|
+
enabled: true,
|
|
1612
|
+
tokenThreshold: 0.75, // Trigger at 75%
|
|
1613
|
+
hardCapThreshold: 0.9, // Force at 90%
|
|
1614
|
+
enableGrowthRatePrediction: true, // Enable predictive compaction
|
|
1615
|
+
enableErrorFallback: true, // Auto-recover from context errors
|
|
1616
|
+
},
|
|
1617
|
+
});
|
|
1618
|
+
```
|
|
1619
|
+
|
|
1620
|
+
### Custom Compaction Policy
|
|
1621
|
+
|
|
1622
|
+
Override the default policy logic with custom rules:
|
|
1623
|
+
|
|
1624
|
+
```typescript
|
|
1625
|
+
const contextManager = createContextManager({
|
|
1626
|
+
maxTokens: 100000,
|
|
1627
|
+
policy: {
|
|
1628
|
+
enabled: true,
|
|
1629
|
+
tokenThreshold: 0.8,
|
|
1630
|
+
hardCapThreshold: 0.95,
|
|
1631
|
+
enableGrowthRatePrediction: false,
|
|
1632
|
+
enableErrorFallback: true,
|
|
1633
|
+
// Custom compaction logic
|
|
1634
|
+
shouldCompact: (budget, messages) => {
|
|
1635
|
+
// Trigger if more than 50 messages
|
|
1636
|
+
if (messages.length > 50) {
|
|
1637
|
+
return { trigger: true, reason: "token_threshold" };
|
|
1638
|
+
}
|
|
1639
|
+
// Trigger if budget usage high
|
|
1640
|
+
if (budget.usage >= 0.85) {
|
|
1641
|
+
return { trigger: true, reason: "hard_cap" };
|
|
1642
|
+
}
|
|
1643
|
+
return { trigger: false };
|
|
1644
|
+
},
|
|
1645
|
+
},
|
|
1646
|
+
});
|
|
1647
|
+
```
|
|
1648
|
+
|
|
1649
|
+
### Observability Hooks
|
|
1650
|
+
|
|
1651
|
+
Monitor compaction events with PreCompact and PostCompact hooks:
|
|
1652
|
+
|
|
1653
|
+
```typescript
|
|
1654
|
+
const agent = createAgent({
|
|
1655
|
+
model,
|
|
1656
|
+
contextManager,
|
|
1657
|
+
hooks: {
|
|
1658
|
+
PreCompact: [
|
|
1659
|
+
async (input) => {
|
|
1660
|
+
console.log(
|
|
1661
|
+
`Compacting ${input.message_count} messages (${input.tokens_before} tokens)`,
|
|
1662
|
+
);
|
|
1663
|
+
return {};
|
|
1664
|
+
},
|
|
1665
|
+
],
|
|
1666
|
+
PostCompact: [
|
|
1667
|
+
async (input) => {
|
|
1668
|
+
console.log(
|
|
1669
|
+
`Compacted ${input.messages_before} → ${input.messages_after} messages`,
|
|
1670
|
+
);
|
|
1671
|
+
console.log(`Saved ${input.tokens_saved} tokens`);
|
|
1672
|
+
return {};
|
|
1673
|
+
},
|
|
1674
|
+
],
|
|
1675
|
+
},
|
|
1676
|
+
});
|
|
1677
|
+
```
|
|
1678
|
+
|
|
1679
|
+
### Error-Triggered Fallback
|
|
1680
|
+
|
|
1681
|
+
When enabled, the SDK automatically attempts emergency compaction if a context length error occurs:
|
|
1682
|
+
|
|
1683
|
+
```typescript
|
|
1684
|
+
const contextManager = createContextManager({
|
|
1685
|
+
maxTokens: 100000,
|
|
1686
|
+
policy: {
|
|
1687
|
+
enableErrorFallback: true, // Enable auto-recovery
|
|
1688
|
+
},
|
|
1689
|
+
});
|
|
1690
|
+
|
|
1691
|
+
const agent = createAgent({
|
|
1692
|
+
model,
|
|
1693
|
+
contextManager,
|
|
1694
|
+
checkpointer, // Required - stores compacted state
|
|
1695
|
+
});
|
|
1696
|
+
|
|
1697
|
+
// If context error occurs, SDK will:
|
|
1698
|
+
// 1. Detect context length error
|
|
1699
|
+
// 2. Compact messages from checkpoint
|
|
1700
|
+
// 3. Save compacted state
|
|
1701
|
+
// 4. Retry the request
|
|
1702
|
+
```
|
|
1703
|
+
|
|
1704
|
+
**Note**: Error fallback requires a checkpointer to store the compacted state. It only triggers once per request to avoid loops.
|
|
1705
|
+
|
|
1706
|
+
### Token Accounting
|
|
1707
|
+
|
|
1708
|
+
The SDK uses a hybrid approach to token tracking for more accurate context management:
|
|
1709
|
+
|
|
1710
|
+
1. **Pre-call Estimates**: Uses token counters to predict usage before API calls
|
|
1711
|
+
2. **Post-call Actuals**: Updates tracking with real usage from model responses
|
|
1712
|
+
3. **Message Caching**: Caches token counts per message to avoid re-counting
|
|
1713
|
+
|
|
1714
|
+
```typescript
|
|
1715
|
+
import {
|
|
1716
|
+
createContextManager,
|
|
1717
|
+
createCustomTokenCounter,
|
|
1718
|
+
} from "@lleverage-ai/agent-sdk";
|
|
1719
|
+
import { encoding_for_model } from "tiktoken";
|
|
1720
|
+
|
|
1721
|
+
// Option 1: Use approximate counter (default, with caching)
|
|
1722
|
+
const contextManager = createContextManager({
|
|
1723
|
+
maxTokens: 100000,
|
|
1724
|
+
// Uses approximate counter by default (4 chars ≈ 1 token)
|
|
1725
|
+
});
|
|
1726
|
+
|
|
1727
|
+
// Option 2: Use model-specific tokenizer (recommended for production)
|
|
1728
|
+
const encoder = encoding_for_model("gpt-4");
|
|
1729
|
+
const contextManager = createContextManager({
|
|
1730
|
+
maxTokens: 100000,
|
|
1731
|
+
tokenCounter: createCustomTokenCounter({
|
|
1732
|
+
countFn: (text) => encoder.encode(text).length,
|
|
1733
|
+
messageOverhead: 4, // Tokens per message for structure
|
|
1734
|
+
}),
|
|
1735
|
+
});
|
|
1736
|
+
|
|
1737
|
+
// The context manager automatically:
|
|
1738
|
+
// - Estimates tokens before generation (for compaction decisions)
|
|
1739
|
+
// - Updates with actual usage after generation (for accurate tracking)
|
|
1740
|
+
// - Caches token counts to avoid re-counting identical messages
|
|
1741
|
+
|
|
1742
|
+
const agent = createAgent({
|
|
1743
|
+
model,
|
|
1744
|
+
contextManager,
|
|
1745
|
+
});
|
|
1746
|
+
|
|
1747
|
+
// After generation, usage is automatically tracked
|
|
1748
|
+
const result = await agent.generate({ prompt: "Hello" });
|
|
1749
|
+
// contextManager now knows the actual token usage from the model
|
|
1750
|
+
```
|
|
1751
|
+
|
|
1752
|
+
**Token Budget Properties**:
|
|
1753
|
+
|
|
1754
|
+
- `currentTokens`: Current token count (actual or estimated)
|
|
1755
|
+
- `maxTokens`: Maximum allowed tokens
|
|
1756
|
+
- `usage`: Usage percentage (0-1)
|
|
1757
|
+
- `remaining`: Tokens remaining
|
|
1758
|
+
- `isActual`: `true` if based on model usage, `false` if estimated
|
|
1759
|
+
|
|
1760
|
+
**Benefits**:
|
|
1761
|
+
|
|
1762
|
+
- More accurate compaction decisions based on real model usage
|
|
1763
|
+
- Reduced computation from message caching (especially with custom tokenizers)
|
|
1764
|
+
- Hybrid approach: estimates for predictions, actuals for accuracy
|
|
1765
|
+
- Automatic integration - no manual updates needed
|
|
1766
|
+
|
|
1767
|
+
### Background Compaction
|
|
1768
|
+
|
|
1769
|
+
Avoid blocking user-facing responses by running compaction in the background:
|
|
1770
|
+
|
|
1771
|
+
```typescript
|
|
1772
|
+
import { createContextManager } from "@lleverage-ai/agent-sdk";
|
|
1773
|
+
|
|
1774
|
+
const contextManager = createContextManager({
|
|
1775
|
+
maxTokens: 100000,
|
|
1776
|
+
policy: {
|
|
1777
|
+
tokenThreshold: 0.8,
|
|
1778
|
+
},
|
|
1779
|
+
scheduler: {
|
|
1780
|
+
enableBackgroundCompaction: true, // Run compaction asynchronously
|
|
1781
|
+
debounceDelayMs: 5000, // Wait 5s before starting compaction
|
|
1782
|
+
maxPendingTasks: 3, // Maximum queued compactions
|
|
1783
|
+
onTaskComplete: (task) => {
|
|
1784
|
+
console.log(
|
|
1785
|
+
`Background compaction saved ${task.result.tokens_saved} tokens`,
|
|
1786
|
+
);
|
|
1787
|
+
},
|
|
1788
|
+
onTaskError: (task) => {
|
|
1789
|
+
console.error(`Compaction failed:`, task.error);
|
|
1790
|
+
},
|
|
1791
|
+
},
|
|
1792
|
+
});
|
|
1793
|
+
|
|
1794
|
+
const agent = createAgent({
|
|
1795
|
+
model,
|
|
1796
|
+
contextManager,
|
|
1797
|
+
});
|
|
1798
|
+
|
|
1799
|
+
// First generation: compaction is scheduled but doesn't block
|
|
1800
|
+
const result1 = await agent.generate({ prompt: "Hello" });
|
|
1801
|
+
// Returns immediately with original messages
|
|
1802
|
+
|
|
1803
|
+
// Background compaction runs after 5s debounce delay
|
|
1804
|
+
|
|
1805
|
+
// Next generation: applies the background compaction result
|
|
1806
|
+
const result2 = await agent.generate({ prompt: "Follow-up" });
|
|
1807
|
+
// Uses compacted messages from background task
|
|
1808
|
+
```
|
|
1809
|
+
|
|
1810
|
+
**How it works**:
|
|
1811
|
+
|
|
1812
|
+
1. **First Call**: When compaction is needed, it's scheduled in the background and original messages are used
|
|
1813
|
+
2. **Background Execution**: After debounce delay, compaction runs asynchronously
|
|
1814
|
+
3. **Next Call**: If background compaction completed, the result is applied automatically
|
|
1815
|
+
4. **Debouncing**: Multiple rapid compaction triggers are coalesced to avoid redundant work
|
|
1816
|
+
|
|
1817
|
+
**Benefits**:
|
|
1818
|
+
|
|
1819
|
+
- No latency impact on user-facing responses
|
|
1820
|
+
- Debouncing prevents excessive compaction during rapid interactions
|
|
1821
|
+
- Queue management prevents memory issues during bursts
|
|
1822
|
+
- Graceful failure handling with callbacks
|
|
1823
|
+
|
|
1824
|
+
**When to use**:
|
|
1825
|
+
|
|
1826
|
+
- Interactive applications where latency matters
|
|
1827
|
+
- High-volume scenarios with many rapid turns
|
|
1828
|
+
- Long-running sessions with periodic compaction needs
|
|
1829
|
+
|
|
1830
|
+
**When to use synchronous compaction** (default behavior):
|
|
1831
|
+
|
|
1832
|
+
- Batch processing where latency isn't critical
|
|
1833
|
+
- Single-request scenarios without follow-up calls
|
|
1834
|
+
- When you need guaranteed compaction before the next generation
|
|
1835
|
+
|
|
1836
|
+
**Access scheduler directly**:
|
|
1837
|
+
|
|
1838
|
+
```typescript
|
|
1839
|
+
// Check pending compactions
|
|
1840
|
+
const pendingTasks = contextManager.scheduler?.getPendingTasks();
|
|
1841
|
+
console.log(`${pendingTasks.length} compactions queued`);
|
|
1842
|
+
|
|
1843
|
+
// Get task details
|
|
1844
|
+
const task = contextManager.scheduler?.getTask(taskId);
|
|
1845
|
+
console.log(`Task status: ${task.status}`);
|
|
1846
|
+
|
|
1847
|
+
// Cancel a pending compaction
|
|
1848
|
+
contextManager.scheduler?.cancel(taskId);
|
|
1849
|
+
|
|
1850
|
+
// Clean up completed tasks
|
|
1851
|
+
contextManager.scheduler?.cleanup();
|
|
1852
|
+
|
|
1853
|
+
// Shutdown scheduler (cancels all pending)
|
|
1854
|
+
contextManager.scheduler?.shutdown();
|
|
1855
|
+
```
|
|
1856
|
+
|
|
1857
|
+
### Advanced Compaction Strategies
|
|
1858
|
+
|
|
1859
|
+
Choose from multiple compaction strategies to optimize context management for your use case.
|
|
1860
|
+
|
|
1861
|
+
#### Strategy Types
|
|
1862
|
+
|
|
1863
|
+
1. **Rollup** (default): Summarize older messages into a single summary
|
|
1864
|
+
2. **Tiered**: Create multiple summary layers (summary of summaries) for long conversations
|
|
1865
|
+
3. **Structured**: Generate structured summaries with distinct sections (decisions, state, questions, references)
|
|
1866
|
+
|
|
1867
|
+
```typescript
|
|
1868
|
+
import { createContextManager } from "@lleverage-ai/agent-sdk";
|
|
1869
|
+
|
|
1870
|
+
// Rollup strategy (default): single summary of old messages
|
|
1871
|
+
const contextManager1 = createContextManager({
|
|
1872
|
+
maxTokens: 100000,
|
|
1873
|
+
summarization: {
|
|
1874
|
+
keepMessageCount: 10,
|
|
1875
|
+
strategy: "rollup", // Simple summary rollup
|
|
1876
|
+
},
|
|
1877
|
+
});
|
|
1878
|
+
|
|
1879
|
+
// Tiered strategy: progressive summarization over time
|
|
1880
|
+
const contextManager2 = createContextManager({
|
|
1881
|
+
maxTokens: 100000,
|
|
1882
|
+
summarization: {
|
|
1883
|
+
keepMessageCount: 10,
|
|
1884
|
+
strategy: "tiered",
|
|
1885
|
+
enableTieredSummaries: true,
|
|
1886
|
+
maxSummaryTiers: 3, // Up to 3 summary levels
|
|
1887
|
+
messagesPerTier: 5, // Combine 5 summaries into next tier
|
|
1888
|
+
},
|
|
1889
|
+
});
|
|
1890
|
+
|
|
1891
|
+
// Structured strategy: summaries with organized sections
|
|
1892
|
+
const contextManager3 = createContextManager({
|
|
1893
|
+
maxTokens: 100000,
|
|
1894
|
+
summarization: {
|
|
1895
|
+
keepMessageCount: 10,
|
|
1896
|
+
strategy: "structured",
|
|
1897
|
+
enableStructuredSummary: true,
|
|
1898
|
+
},
|
|
1899
|
+
});
|
|
1900
|
+
```
|
|
1901
|
+
|
|
1902
|
+
**When to use each strategy**:
|
|
1903
|
+
|
|
1904
|
+
- **Rollup**: Most use cases - simple and effective
|
|
1905
|
+
- **Tiered**: Very long conversations (100+ turns) where you want progressive compression
|
|
1906
|
+
- **Structured**: When you need to parse or query summary content programmatically
|
|
1907
|
+
|
|
1908
|
+
#### Structured Summaries
|
|
1909
|
+
|
|
1910
|
+
Structured summaries provide organized sections for better context organization:
|
|
1911
|
+
|
|
1912
|
+
```typescript
|
|
1913
|
+
// Structured summary format
|
|
1914
|
+
{
|
|
1915
|
+
decisions: ["Chose TypeScript over JavaScript", "Using REST API"],
|
|
1916
|
+
preferences: ["Prefer functional style", "No external dependencies"],
|
|
1917
|
+
currentState: ["Authentication implemented", "Tests passing"],
|
|
1918
|
+
openQuestions: ["How to handle rate limiting?", "Deploy strategy TBD"],
|
|
1919
|
+
references: ["src/auth.ts:45", "API_KEY=abc123", "https://docs.example.com"]
|
|
1920
|
+
}
|
|
1921
|
+
```
|
|
1922
|
+
|
|
1923
|
+
**Benefits**:
|
|
1924
|
+
|
|
1925
|
+
- Easy to parse and extract specific information
|
|
1926
|
+
- Better for long-term context retention
|
|
1927
|
+
- Enables semantic search within summaries
|
|
1928
|
+
|
|
1929
|
+
#### Pinned Messages
|
|
1930
|
+
|
|
1931
|
+
Pin important messages to ensure they're never compacted:
|
|
1932
|
+
|
|
1933
|
+
```typescript
|
|
1934
|
+
import { createContextManager } from "@lleverage-ai/agent-sdk";
|
|
1935
|
+
|
|
1936
|
+
const contextManager = createContextManager({
|
|
1937
|
+
maxTokens: 100000,
|
|
1938
|
+
summarization: {
|
|
1939
|
+
keepMessageCount: 10,
|
|
1940
|
+
strategy: "rollup",
|
|
1941
|
+
},
|
|
1942
|
+
});
|
|
1943
|
+
|
|
1944
|
+
// Pin an important message
|
|
1945
|
+
contextManager.pinMessage(5, "Contains critical user requirements");
|
|
1946
|
+
|
|
1947
|
+
// Check if message is pinned
|
|
1948
|
+
if (contextManager.isPinned(5)) {
|
|
1949
|
+
console.log("Message 5 is protected from compaction");
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
// Unpin when no longer needed
|
|
1953
|
+
contextManager.unpinMessage(5);
|
|
1954
|
+
|
|
1955
|
+
// View all pinned messages
|
|
1956
|
+
console.log("Pinned messages:", contextManager.pinnedMessages);
|
|
1957
|
+
// [{ messageIndex: 5, reason: "Contains critical user requirements", pinnedAt: 1770084888000 }]
|
|
1958
|
+
```
|
|
1959
|
+
|
|
1960
|
+
**Use cases for pinning**:
|
|
1961
|
+
|
|
1962
|
+
- Critical user requirements or constraints
|
|
1963
|
+
- Important decisions that must be preserved
|
|
1964
|
+
- Configuration details referenced throughout the conversation
|
|
1965
|
+
- High-value context that shouldn't be summarized
|
|
1966
|
+
|
|
1967
|
+
**Note**: Pinned messages are always kept in full, regardless of compaction strategy. They appear after the summary and before recent messages in the compacted history.
|
|
1968
|
+
|
|
1969
|
+
#### Tiered Summaries
|
|
1970
|
+
|
|
1971
|
+
For extremely long conversations, tiered summaries create progressive compression:
|
|
1972
|
+
|
|
1973
|
+
```typescript
|
|
1974
|
+
const contextManager = createContextManager({
|
|
1975
|
+
maxTokens: 100000,
|
|
1976
|
+
summarization: {
|
|
1977
|
+
keepMessageCount: 10,
|
|
1978
|
+
strategy: "tiered",
|
|
1979
|
+
enableTieredSummaries: true,
|
|
1980
|
+
maxSummaryTiers: 3,
|
|
1981
|
+
messagesPerTier: 5,
|
|
1982
|
+
},
|
|
1983
|
+
});
|
|
1984
|
+
|
|
1985
|
+
// First compaction: creates Tier 0 summary (summarizes old messages)
|
|
1986
|
+
// After 5 more compactions: creates Tier 1 summary (summarizes 5 Tier 0 summaries)
|
|
1987
|
+
// After 5 more Tier 1 summaries: creates Tier 2 summary (summarizes 5 Tier 1 summaries)
|
|
1988
|
+
```
|
|
1989
|
+
|
|
1990
|
+
**How it works**:
|
|
1991
|
+
|
|
1992
|
+
1. When you have < 5 summaries: creates regular Tier 0 summary
|
|
1993
|
+
2. When you have ≥ 5 summaries: creates Tier 1 summary (consolidates the 5 summaries)
|
|
1994
|
+
3. Continues creating higher tiers as needed up to `maxSummaryTiers`
|
|
1995
|
+
|
|
1996
|
+
**Benefits**:
|
|
1997
|
+
|
|
1998
|
+
- Handles extremely long conversations (1000+ turns)
|
|
1999
|
+
- Maintains important context across many interactions
|
|
2000
|
+
- Logarithmic memory usage as conversation grows
|
|
2001
|
+
|
|
2002
|
+
**Trade-offs**:
|
|
2003
|
+
|
|
2004
|
+
- Slightly higher compaction cost (more summarization calls)
|
|
2005
|
+
- Information loss increases with tier level
|
|
2006
|
+
- Best for conversations where recent context matters most
|
|
2007
|
+
|
|
2008
|
+
### Rich Content Support
|
|
2009
|
+
|
|
2010
|
+
The context manager automatically handles messages with images, files, audio, and video content for token counting and summarization.
|
|
2011
|
+
|
|
2012
|
+
#### Supported Content Types
|
|
2013
|
+
|
|
2014
|
+
The SDK supports all AI SDK content part types:
|
|
2015
|
+
|
|
2016
|
+
- **TextPart**: Standard text messages
|
|
2017
|
+
- **ImagePart**: Images (URLs, base64, data URIs)
|
|
2018
|
+
- **FilePart**: Files (URLs, base64, data URIs)
|
|
2019
|
+
- **ToolCallPart**: Tool invocations
|
|
2020
|
+
- **ToolResultPart**: Tool execution results
|
|
2021
|
+
|
|
2022
|
+
```typescript
|
|
2023
|
+
import { createAgent, createContextManager } from "@lleverage-ai/agent-sdk";
|
|
2024
|
+
|
|
2025
|
+
const agent = createAgent({
|
|
2026
|
+
model,
|
|
2027
|
+
contextManager: createContextManager({
|
|
2028
|
+
maxTokens: 100000,
|
|
2029
|
+
summarization: { keepMessageCount: 10 },
|
|
2030
|
+
}),
|
|
2031
|
+
});
|
|
2032
|
+
|
|
2033
|
+
// Messages with images
|
|
2034
|
+
const result = await agent.generate({
|
|
2035
|
+
messages: [
|
|
2036
|
+
{
|
|
2037
|
+
role: "user",
|
|
2038
|
+
content: [
|
|
2039
|
+
{ type: "text", text: "What's in this screenshot?" },
|
|
2040
|
+
{
|
|
2041
|
+
type: "image",
|
|
2042
|
+
image: new URL("https://example.com/screenshot.png"),
|
|
2043
|
+
},
|
|
2044
|
+
],
|
|
2045
|
+
},
|
|
2046
|
+
],
|
|
2047
|
+
});
|
|
2048
|
+
|
|
2049
|
+
// Messages with files
|
|
2050
|
+
const result2 = await agent.generate({
|
|
2051
|
+
messages: [
|
|
2052
|
+
{
|
|
2053
|
+
role: "user",
|
|
2054
|
+
content: [
|
|
2055
|
+
{ type: "text", text: "Analyze this document" },
|
|
2056
|
+
{
|
|
2057
|
+
type: "file",
|
|
2058
|
+
data: "base64data",
|
|
2059
|
+
mimeType: "application/pdf",
|
|
2060
|
+
},
|
|
2061
|
+
],
|
|
2062
|
+
},
|
|
2063
|
+
],
|
|
2064
|
+
});
|
|
2065
|
+
```
|
|
2066
|
+
|
|
2067
|
+
#### Token Counting for Rich Content
|
|
2068
|
+
|
|
2069
|
+
The token counter automatically accounts for rich content:
|
|
2070
|
+
|
|
2071
|
+
- **Images**: ~1000 tokens per image (approximate vision model cost)
|
|
2072
|
+
- **Files**: ~500 tokens per file (varies by size and type)
|
|
2073
|
+
- **Text**: Character-based approximation or custom tokenizer
|
|
2074
|
+
|
|
2075
|
+
```typescript
|
|
2076
|
+
import {
|
|
2077
|
+
createContextManager,
|
|
2078
|
+
createApproximateTokenCounter,
|
|
2079
|
+
} from "@lleverage-ai/agent-sdk";
|
|
2080
|
+
|
|
2081
|
+
const counter = createApproximateTokenCounter();
|
|
2082
|
+
|
|
2083
|
+
const messages = [
|
|
2084
|
+
{
|
|
2085
|
+
role: "user",
|
|
2086
|
+
content: [
|
|
2087
|
+
{ type: "text", text: "Compare these images" },
|
|
2088
|
+
{ type: "image", image: new URL("https://example.com/before.png") },
|
|
2089
|
+
{ type: "image", image: new URL("https://example.com/after.png") },
|
|
2090
|
+
],
|
|
2091
|
+
},
|
|
2092
|
+
];
|
|
2093
|
+
|
|
2094
|
+
// Counts: text (~5) + 2 images (2000) + overhead (4) = ~2009 tokens
|
|
2095
|
+
const tokenCount = counter.countMessages(messages);
|
|
2096
|
+
|
|
2097
|
+
const contextManager = createContextManager({
|
|
2098
|
+
maxTokens: 10000,
|
|
2099
|
+
tokenCounter: counter, // Optional: use custom counter
|
|
2100
|
+
});
|
|
2101
|
+
```
|
|
2102
|
+
|
|
2103
|
+
#### Rich Content in Summaries
|
|
2104
|
+
|
|
2105
|
+
When compaction occurs, the summarizer extracts metadata from rich content:
|
|
2106
|
+
|
|
2107
|
+
```typescript
|
|
2108
|
+
const contextManager = createContextManager({
|
|
2109
|
+
maxTokens: 50000,
|
|
2110
|
+
summarization: {
|
|
2111
|
+
keepMessageCount: 10,
|
|
2112
|
+
strategy: "structured", // Best for rich content
|
|
2113
|
+
enableStructuredSummary: true,
|
|
2114
|
+
},
|
|
2115
|
+
});
|
|
2116
|
+
|
|
2117
|
+
// After compaction, summaries include:
|
|
2118
|
+
// - Image URLs and descriptions (e.g., "Image: screenshot.png showing login form")
|
|
2119
|
+
// - File references with MIME types (e.g., "File: document.pdf (application/pdf)")
|
|
2120
|
+
// - Context about what the media contained (from model analysis)
|
|
2121
|
+
|
|
2122
|
+
// Example structured summary with rich content:
|
|
2123
|
+
// {
|
|
2124
|
+
// "decisions": ["Use responsive design based on provided mockups"],
|
|
2125
|
+
// "preferences": ["User prefers dark color scheme"],
|
|
2126
|
+
// "currentState": ["Analyzed 3 screenshots showing current UI"],
|
|
2127
|
+
// "openQuestions": ["Should we support mobile tablets?"],
|
|
2128
|
+
// "references": [
|
|
2129
|
+
// "Image: https://example.com/screenshot1.png showing header",
|
|
2130
|
+
// "Image: https://example.com/screenshot2.png showing footer",
|
|
2131
|
+
// "File: spec.pdf containing technical requirements"
|
|
2132
|
+
// ]
|
|
2133
|
+
// }
|
|
2134
|
+
```
|
|
2135
|
+
|
|
2136
|
+
**Best Practices**:
|
|
2137
|
+
|
|
2138
|
+
1. **Use structured summaries** for conversations with many images/files to organize references
|
|
2139
|
+
2. **Pin important media messages** if they contain critical information that shouldn't be summarized
|
|
2140
|
+
3. **Monitor token budgets** - rich content uses significantly more tokens than text
|
|
2141
|
+
4. **Consider background compaction** for image-heavy conversations to avoid blocking
|
|
2142
|
+
5. **Use descriptive text** alongside media to help the summarizer capture context
|
|
2143
|
+
|
|
2144
|
+
**Token Budget Example**:
|
|
2145
|
+
|
|
2146
|
+
```typescript
|
|
2147
|
+
const contextManager = createContextManager({
|
|
2148
|
+
maxTokens: 100000,
|
|
2149
|
+
policy: {
|
|
2150
|
+
tokenThreshold: 0.7, // Lower threshold for image-heavy conversations
|
|
2151
|
+
},
|
|
2152
|
+
});
|
|
2153
|
+
|
|
2154
|
+
// Check budget with rich content
|
|
2155
|
+
const budget = contextManager.getBudget(messages);
|
|
2156
|
+
console.log(`Usage: ${(budget.usage * 100).toFixed(1)}%`);
|
|
2157
|
+
console.log(`Images cost: ~${imageCount * 1000} tokens`);
|
|
2158
|
+
```
|
|
2159
|
+
|
|
2160
|
+
**Note**: Token costs for images and files are approximations. Actual costs depend on:
|
|
2161
|
+
|
|
2162
|
+
- Image resolution and complexity (for vision models)
|
|
2163
|
+
- File size and content type
|
|
2164
|
+
- Model-specific encoding
|
|
2165
|
+
|
|
2166
|
+
For accurate token tracking, use model-specific tokenizers with `createCustomTokenCounter()` and update costs based on your provider's pricing.
|
|
2167
|
+
|
|
2168
|
+
## Error Handling
|
|
2169
|
+
|
|
2170
|
+
Typed errors with recovery utilities.
|
|
2171
|
+
|
|
2172
|
+
### Error Types
|
|
2173
|
+
|
|
2174
|
+
```typescript
|
|
2175
|
+
import {
|
|
2176
|
+
AgentError,
|
|
2177
|
+
ToolExecutionError,
|
|
2178
|
+
ModelError,
|
|
2179
|
+
TimeoutError,
|
|
2180
|
+
RateLimitError,
|
|
2181
|
+
} from "@lleverage-ai/agent-sdk";
|
|
2182
|
+
|
|
2183
|
+
try {
|
|
2184
|
+
await agent.generate({ prompt });
|
|
2185
|
+
} catch (error) {
|
|
2186
|
+
if (error instanceof RateLimitError) {
|
|
2187
|
+
// Wait and retry
|
|
2188
|
+
} else if (error instanceof TimeoutError) {
|
|
2189
|
+
// Handle timeout
|
|
2190
|
+
} else if (error instanceof ToolExecutionError) {
|
|
2191
|
+
console.error("Tool failed:", error.toolName, error.cause);
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
```
|
|
2195
|
+
|
|
2196
|
+
### Error Utilities
|
|
2197
|
+
|
|
2198
|
+
```typescript
|
|
2199
|
+
import {
|
|
2200
|
+
wrapError,
|
|
2201
|
+
isRetryable,
|
|
2202
|
+
getUserMessage,
|
|
2203
|
+
createErrorHandler,
|
|
2204
|
+
} from "@lleverage-ai/agent-sdk";
|
|
2205
|
+
|
|
2206
|
+
// Wrap unknown errors
|
|
2207
|
+
const agentError = wrapError(unknownError, { context: "generation" });
|
|
2208
|
+
|
|
2209
|
+
// Check if error is retryable
|
|
2210
|
+
if (isRetryable(error)) {
|
|
2211
|
+
// Retry logic
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
// Get user-friendly message
|
|
2215
|
+
const message = getUserMessage(error);
|
|
2216
|
+
```
|
|
2217
|
+
|
|
2218
|
+
### Graceful Degradation
|
|
2219
|
+
|
|
2220
|
+
```typescript
|
|
2221
|
+
import {
|
|
2222
|
+
withFallback,
|
|
2223
|
+
tryOperations,
|
|
2224
|
+
createCircuitBreaker,
|
|
2225
|
+
} from "@lleverage-ai/agent-sdk";
|
|
2226
|
+
|
|
2227
|
+
// Single fallback
|
|
2228
|
+
const result = await withFallback(
|
|
2229
|
+
() => primaryOperation(),
|
|
2230
|
+
() => fallbackOperation(),
|
|
2231
|
+
);
|
|
2232
|
+
|
|
2233
|
+
// Try multiple operations
|
|
2234
|
+
const result = await tryOperations([
|
|
2235
|
+
() => attempt1(),
|
|
2236
|
+
() => attempt2(),
|
|
2237
|
+
() => attempt3(),
|
|
2238
|
+
]);
|
|
2239
|
+
|
|
2240
|
+
// Circuit breaker pattern
|
|
2241
|
+
const breaker = createCircuitBreaker({
|
|
2242
|
+
threshold: 5,
|
|
2243
|
+
resetTimeout: 60000,
|
|
2244
|
+
});
|
|
2245
|
+
|
|
2246
|
+
const result = await breaker.execute(() => riskyOperation());
|
|
2247
|
+
```
|
|
2248
|
+
|
|
2249
|
+
## API Reference
|
|
2250
|
+
|
|
2251
|
+
### Agent Creation
|
|
2252
|
+
|
|
2253
|
+
| Function | Description |
|
|
2254
|
+
| ------------------------- | ------------------------------------- |
|
|
2255
|
+
| `createAgent(options)` | Create a new agent instance |
|
|
2256
|
+
| `createSubagent(options)` | Define a subagent for task delegation |
|
|
2257
|
+
|
|
2258
|
+
### Tools
|
|
2259
|
+
|
|
2260
|
+
| Function | Description |
|
|
2261
|
+
| -------------------------------- | ------------------------------- |
|
|
2262
|
+
| `createCoreTools(options)` | Create all core tools |
|
|
2263
|
+
| `createFilesystemTools(backend)` | Create filesystem tools only |
|
|
2264
|
+
| `createBashTool(options)` | Create shell execution tool |
|
|
2265
|
+
| `createTaskTool(options)` | Create subagent delegation tool |
|
|
2266
|
+
| `createSearchToolsTool(options)` | Create MCP tool search |
|
|
2267
|
+
|
|
2268
|
+
### Plugins
|
|
2269
|
+
|
|
2270
|
+
| Function | Description |
|
|
2271
|
+
| ----------------------- | --------------------------------- |
|
|
2272
|
+
| `definePlugin(options)` | Define a plugin |
|
|
2273
|
+
| `defineSkill(options)` | Define a skill with tool guidance |
|
|
2274
|
+
|
|
2275
|
+
### Backends
|
|
2276
|
+
|
|
2277
|
+
| Class | Description |
|
|
2278
|
+
| ------------------- | ------------------------------------- |
|
|
2279
|
+
| `FilesystemBackend` | File operations with security |
|
|
2280
|
+
| `StateBackend` | In-memory filesystem |
|
|
2281
|
+
| `LocalSandbox` | Command execution sandbox |
|
|
2282
|
+
| `CompositeBackend` | Route operations to multiple backends |
|
|
2283
|
+
|
|
2284
|
+
### Middleware
|
|
2285
|
+
|
|
2286
|
+
| Function | Description |
|
|
2287
|
+
| ------------------------------ | --------------------------- |
|
|
2288
|
+
| `createLoggingMiddleware()` | Log requests and responses |
|
|
2289
|
+
| `createCacheMiddleware()` | Cache responses |
|
|
2290
|
+
| `createRetryMiddleware()` | Retry failed requests |
|
|
2291
|
+
| `createGuardrailsMiddleware()` | Content filtering |
|
|
2292
|
+
| `composeMiddleware()` | Combine multiple middleware |
|
|
2293
|
+
|
|
2294
|
+
### Checkpointing
|
|
2295
|
+
|
|
2296
|
+
| Class | Description |
|
|
2297
|
+
| -------------------- | ------------------------- |
|
|
2298
|
+
| `MemorySaver` | In-memory checkpoints |
|
|
2299
|
+
| `FileSaver` | File-based checkpoints |
|
|
2300
|
+
| `KeyValueStoreSaver` | Generic key-value storage |
|
|
2301
|
+
|
|
2302
|
+
### Memory
|
|
2303
|
+
|
|
2304
|
+
| Class | Description |
|
|
2305
|
+
| ----------------------- | ------------------------- |
|
|
2306
|
+
| `FilesystemMemoryStore` | Persistent memory storage |
|
|
2307
|
+
| `InMemoryMemoryStore` | In-memory storage |
|
|
2308
|
+
|
|
2309
|
+
### Observability
|
|
2310
|
+
|
|
2311
|
+
| Function | Description |
|
|
2312
|
+
| --------------------------------- | ---------------------------- |
|
|
2313
|
+
| `createLogger()` | Create structured logger |
|
|
2314
|
+
| `createMetricsRegistry()` | Create metrics collector |
|
|
2315
|
+
| `createTracer()` | Create distributed tracer |
|
|
2316
|
+
| `createObservabilityPreset()` | One-line observability setup |
|
|
2317
|
+
| `createObservabilityEventHooks()` | Collect observability events |
|
|
2318
|
+
|
|
2319
|
+
## License
|
|
2320
|
+
|
|
2321
|
+
MIT
|