formagent-sdk 0.3.0 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/cli/index.js +43 -7
- package/dist/index.js +43 -7
- package/docs/README.md +126 -0
- package/docs/api-reference.md +677 -0
- package/docs/getting-started.md +273 -0
- package/docs/mcp-servers.md +465 -0
- package/docs/session-storage.md +320 -0
- package/docs/tools.md +501 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -228,6 +228,8 @@ for await (const event of session.receive()) {
|
|
|
228
228
|
|
|
229
229
|
## Session Management
|
|
230
230
|
|
|
231
|
+
**Default Behavior:** Sessions use in-memory storage by default. Conversation history is lost when the process exits. Use `FileSessionStorage` for persistence across restarts.
|
|
232
|
+
|
|
231
233
|
### Persistent Sessions
|
|
232
234
|
|
|
233
235
|
Enable session persistence with `FileSessionStorage`:
|
package/dist/cli/index.js
CHANGED
|
@@ -1735,7 +1735,8 @@ class SessionImpl {
|
|
|
1735
1735
|
const toolResult = await tool.execute(toolInput, context);
|
|
1736
1736
|
let content = typeof toolResult.content === "string" ? toolResult.content : JSON.stringify(toolResult.content);
|
|
1737
1737
|
if (needsTruncation(content)) {
|
|
1738
|
-
|
|
1738
|
+
const truncationConfig = this.config.tempDir ? { tempDir: this.config.tempDir } : undefined;
|
|
1739
|
+
content = await truncateToolOutput(content, truncationConfig);
|
|
1739
1740
|
}
|
|
1740
1741
|
toolResponse = toolResult;
|
|
1741
1742
|
result = {
|
|
@@ -4262,6 +4263,7 @@ function checkDirAccess(dirPath, options) {
|
|
|
4262
4263
|
|
|
4263
4264
|
// src/tools/builtin/bash.ts
|
|
4264
4265
|
var DEFAULT_TIMEOUT = 120000;
|
|
4266
|
+
var DEFAULT_IDLE_TIMEOUT = 30000;
|
|
4265
4267
|
var MAX_OUTPUT_LENGTH = 1e5;
|
|
4266
4268
|
var DEFAULT_BLOCKED_PATTERNS = [
|
|
4267
4269
|
"\\bsudo\\b",
|
|
@@ -4328,21 +4330,39 @@ function createBashTool(options = {}) {
|
|
|
4328
4330
|
}
|
|
4329
4331
|
}
|
|
4330
4332
|
const actualTimeout = Math.min(timeout, 600000);
|
|
4333
|
+
const idleTimeout = options.idleTimeout ?? DEFAULT_IDLE_TIMEOUT;
|
|
4331
4334
|
return new Promise((resolve2) => {
|
|
4332
4335
|
let stdout = "";
|
|
4333
4336
|
let stderr = "";
|
|
4334
4337
|
let killed = false;
|
|
4338
|
+
let killedReason = null;
|
|
4339
|
+
let lastOutputTime = Date.now();
|
|
4335
4340
|
const proc = spawn("bash", ["-c", command], {
|
|
4336
4341
|
cwd: cwdAccess.resolved,
|
|
4337
4342
|
env: process.env,
|
|
4338
|
-
shell: false
|
|
4343
|
+
shell: false,
|
|
4344
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
4339
4345
|
});
|
|
4340
4346
|
const timer = setTimeout(() => {
|
|
4341
4347
|
killed = true;
|
|
4348
|
+
killedReason = "timeout";
|
|
4342
4349
|
proc.kill("SIGTERM");
|
|
4343
4350
|
setTimeout(() => proc.kill("SIGKILL"), 1000);
|
|
4344
4351
|
}, actualTimeout);
|
|
4352
|
+
const idleChecker = setInterval(() => {
|
|
4353
|
+
const idleTime = Date.now() - lastOutputTime;
|
|
4354
|
+
if (idleTime >= idleTimeout && !killed) {
|
|
4355
|
+
killed = true;
|
|
4356
|
+
killedReason = "idle";
|
|
4357
|
+
proc.kill("SIGTERM");
|
|
4358
|
+
setTimeout(() => proc.kill("SIGKILL"), 1000);
|
|
4359
|
+
}
|
|
4360
|
+
}, 1000);
|
|
4361
|
+
const updateLastOutputTime = () => {
|
|
4362
|
+
lastOutputTime = Date.now();
|
|
4363
|
+
};
|
|
4345
4364
|
proc.stdout?.on("data", (data) => {
|
|
4365
|
+
updateLastOutputTime();
|
|
4346
4366
|
stdout += data.toString();
|
|
4347
4367
|
if (stdout.length > MAX_OUTPUT_LENGTH) {
|
|
4348
4368
|
stdout = stdout.slice(0, MAX_OUTPUT_LENGTH) + `
|
|
@@ -4351,6 +4371,7 @@ function createBashTool(options = {}) {
|
|
|
4351
4371
|
}
|
|
4352
4372
|
});
|
|
4353
4373
|
proc.stderr?.on("data", (data) => {
|
|
4374
|
+
updateLastOutputTime();
|
|
4354
4375
|
stderr += data.toString();
|
|
4355
4376
|
if (stderr.length > MAX_OUTPUT_LENGTH) {
|
|
4356
4377
|
stderr = stderr.slice(0, MAX_OUTPUT_LENGTH) + `
|
|
@@ -4359,17 +4380,31 @@ function createBashTool(options = {}) {
|
|
|
4359
4380
|
});
|
|
4360
4381
|
proc.on("close", (code) => {
|
|
4361
4382
|
clearTimeout(timer);
|
|
4383
|
+
clearInterval(idleChecker);
|
|
4362
4384
|
if (killed) {
|
|
4363
|
-
|
|
4364
|
-
|
|
4385
|
+
if (killedReason === "idle") {
|
|
4386
|
+
resolve2({
|
|
4387
|
+
content: `Command terminated: no output for ${idleTimeout / 1000} seconds (likely waiting for input)
|
|
4365
4388
|
|
|
4366
4389
|
Partial output:
|
|
4367
4390
|
${stdout}
|
|
4368
4391
|
|
|
4369
4392
|
Stderr:
|
|
4370
4393
|
${stderr}`,
|
|
4371
|
-
|
|
4372
|
-
|
|
4394
|
+
isError: true
|
|
4395
|
+
});
|
|
4396
|
+
} else {
|
|
4397
|
+
resolve2({
|
|
4398
|
+
content: `Command timed out after ${actualTimeout}ms
|
|
4399
|
+
|
|
4400
|
+
Partial output:
|
|
4401
|
+
${stdout}
|
|
4402
|
+
|
|
4403
|
+
Stderr:
|
|
4404
|
+
${stderr}`,
|
|
4405
|
+
isError: true
|
|
4406
|
+
});
|
|
4407
|
+
}
|
|
4373
4408
|
return;
|
|
4374
4409
|
}
|
|
4375
4410
|
const output = stdout + (stderr ? `
|
|
@@ -4390,6 +4425,7 @@ ${output}`,
|
|
|
4390
4425
|
});
|
|
4391
4426
|
proc.on("error", (error) => {
|
|
4392
4427
|
clearTimeout(timer);
|
|
4428
|
+
clearInterval(idleChecker);
|
|
4393
4429
|
resolve2({
|
|
4394
4430
|
content: `Failed to execute command: ${error.message}`,
|
|
4395
4431
|
isError: true
|
|
@@ -6029,7 +6065,7 @@ ${responseText}`;
|
|
|
6029
6065
|
metadata: {
|
|
6030
6066
|
status: response.status,
|
|
6031
6067
|
statusText: response.statusText,
|
|
6032
|
-
headers: Object.fromEntries(response.headers
|
|
6068
|
+
headers: Object.fromEntries(response.headers),
|
|
6033
6069
|
body: responseBody
|
|
6034
6070
|
}
|
|
6035
6071
|
};
|
package/dist/index.js
CHANGED
|
@@ -2129,7 +2129,8 @@ class SessionImpl {
|
|
|
2129
2129
|
const toolResult = await tool2.execute(toolInput, context);
|
|
2130
2130
|
let content = typeof toolResult.content === "string" ? toolResult.content : JSON.stringify(toolResult.content);
|
|
2131
2131
|
if (needsTruncation(content)) {
|
|
2132
|
-
|
|
2132
|
+
const truncationConfig = this.config.tempDir ? { tempDir: this.config.tempDir } : undefined;
|
|
2133
|
+
content = await truncateToolOutput(content, truncationConfig);
|
|
2133
2134
|
}
|
|
2134
2135
|
toolResponse = toolResult;
|
|
2135
2136
|
result = {
|
|
@@ -5312,6 +5313,7 @@ function checkDirAccess(dirPath, options2) {
|
|
|
5312
5313
|
|
|
5313
5314
|
// src/tools/builtin/bash.ts
|
|
5314
5315
|
var DEFAULT_TIMEOUT = 120000;
|
|
5316
|
+
var DEFAULT_IDLE_TIMEOUT = 30000;
|
|
5315
5317
|
var MAX_OUTPUT_LENGTH = 1e5;
|
|
5316
5318
|
var DEFAULT_BLOCKED_PATTERNS = [
|
|
5317
5319
|
"\\bsudo\\b",
|
|
@@ -5378,21 +5380,39 @@ function createBashTool(options2 = {}) {
|
|
|
5378
5380
|
}
|
|
5379
5381
|
}
|
|
5380
5382
|
const actualTimeout = Math.min(timeout, 600000);
|
|
5383
|
+
const idleTimeout = options2.idleTimeout ?? DEFAULT_IDLE_TIMEOUT;
|
|
5381
5384
|
return new Promise((resolve2) => {
|
|
5382
5385
|
let stdout = "";
|
|
5383
5386
|
let stderr = "";
|
|
5384
5387
|
let killed = false;
|
|
5388
|
+
let killedReason = null;
|
|
5389
|
+
let lastOutputTime = Date.now();
|
|
5385
5390
|
const proc = spawn("bash", ["-c", command], {
|
|
5386
5391
|
cwd: cwdAccess.resolved,
|
|
5387
5392
|
env: process.env,
|
|
5388
|
-
shell: false
|
|
5393
|
+
shell: false,
|
|
5394
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
5389
5395
|
});
|
|
5390
5396
|
const timer = setTimeout(() => {
|
|
5391
5397
|
killed = true;
|
|
5398
|
+
killedReason = "timeout";
|
|
5392
5399
|
proc.kill("SIGTERM");
|
|
5393
5400
|
setTimeout(() => proc.kill("SIGKILL"), 1000);
|
|
5394
5401
|
}, actualTimeout);
|
|
5402
|
+
const idleChecker = setInterval(() => {
|
|
5403
|
+
const idleTime = Date.now() - lastOutputTime;
|
|
5404
|
+
if (idleTime >= idleTimeout && !killed) {
|
|
5405
|
+
killed = true;
|
|
5406
|
+
killedReason = "idle";
|
|
5407
|
+
proc.kill("SIGTERM");
|
|
5408
|
+
setTimeout(() => proc.kill("SIGKILL"), 1000);
|
|
5409
|
+
}
|
|
5410
|
+
}, 1000);
|
|
5411
|
+
const updateLastOutputTime = () => {
|
|
5412
|
+
lastOutputTime = Date.now();
|
|
5413
|
+
};
|
|
5395
5414
|
proc.stdout?.on("data", (data) => {
|
|
5415
|
+
updateLastOutputTime();
|
|
5396
5416
|
stdout += data.toString();
|
|
5397
5417
|
if (stdout.length > MAX_OUTPUT_LENGTH) {
|
|
5398
5418
|
stdout = stdout.slice(0, MAX_OUTPUT_LENGTH) + `
|
|
@@ -5401,6 +5421,7 @@ function createBashTool(options2 = {}) {
|
|
|
5401
5421
|
}
|
|
5402
5422
|
});
|
|
5403
5423
|
proc.stderr?.on("data", (data) => {
|
|
5424
|
+
updateLastOutputTime();
|
|
5404
5425
|
stderr += data.toString();
|
|
5405
5426
|
if (stderr.length > MAX_OUTPUT_LENGTH) {
|
|
5406
5427
|
stderr = stderr.slice(0, MAX_OUTPUT_LENGTH) + `
|
|
@@ -5409,17 +5430,31 @@ function createBashTool(options2 = {}) {
|
|
|
5409
5430
|
});
|
|
5410
5431
|
proc.on("close", (code) => {
|
|
5411
5432
|
clearTimeout(timer);
|
|
5433
|
+
clearInterval(idleChecker);
|
|
5412
5434
|
if (killed) {
|
|
5413
|
-
|
|
5414
|
-
|
|
5435
|
+
if (killedReason === "idle") {
|
|
5436
|
+
resolve2({
|
|
5437
|
+
content: `Command terminated: no output for ${idleTimeout / 1000} seconds (likely waiting for input)
|
|
5415
5438
|
|
|
5416
5439
|
Partial output:
|
|
5417
5440
|
${stdout}
|
|
5418
5441
|
|
|
5419
5442
|
Stderr:
|
|
5420
5443
|
${stderr}`,
|
|
5421
|
-
|
|
5422
|
-
|
|
5444
|
+
isError: true
|
|
5445
|
+
});
|
|
5446
|
+
} else {
|
|
5447
|
+
resolve2({
|
|
5448
|
+
content: `Command timed out after ${actualTimeout}ms
|
|
5449
|
+
|
|
5450
|
+
Partial output:
|
|
5451
|
+
${stdout}
|
|
5452
|
+
|
|
5453
|
+
Stderr:
|
|
5454
|
+
${stderr}`,
|
|
5455
|
+
isError: true
|
|
5456
|
+
});
|
|
5457
|
+
}
|
|
5423
5458
|
return;
|
|
5424
5459
|
}
|
|
5425
5460
|
const output = stdout + (stderr ? `
|
|
@@ -5440,6 +5475,7 @@ ${output}`,
|
|
|
5440
5475
|
});
|
|
5441
5476
|
proc.on("error", (error) => {
|
|
5442
5477
|
clearTimeout(timer);
|
|
5478
|
+
clearInterval(idleChecker);
|
|
5443
5479
|
resolve2({
|
|
5444
5480
|
content: `Failed to execute command: ${error.message}`,
|
|
5445
5481
|
isError: true
|
|
@@ -7091,7 +7127,7 @@ ${responseText}`;
|
|
|
7091
7127
|
metadata: {
|
|
7092
7128
|
status: response.status,
|
|
7093
7129
|
statusText: response.statusText,
|
|
7094
|
-
headers: Object.fromEntries(response.headers
|
|
7130
|
+
headers: Object.fromEntries(response.headers),
|
|
7095
7131
|
body: responseBody
|
|
7096
7132
|
}
|
|
7097
7133
|
};
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# formagent-sdk Documentation
|
|
2
|
+
|
|
3
|
+
Welcome to the documentation for `formagent-sdk`, a Claude Agent SDK compatible framework for building AI agents with streaming support, tool execution, and skill management.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Getting Started](./getting-started.md) - Quick start guide
|
|
8
|
+
- [API Reference](./api-reference.md) - Complete API documentation
|
|
9
|
+
- [Session Storage](./session-storage.md) - Persistent session management
|
|
10
|
+
- [Built-in Tools](./tools.md) - File operations, bash, and more
|
|
11
|
+
- [MCP Servers](./mcp-servers.md) - Model Context Protocol integration
|
|
12
|
+
|
|
13
|
+
## Quick Links
|
|
14
|
+
|
|
15
|
+
### Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install formagent-sdk
|
|
19
|
+
# or
|
|
20
|
+
bun add formagent-sdk
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Environment Setup
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
export ANTHROPIC_API_KEY=your-api-key
|
|
27
|
+
# Optional: Custom endpoint
|
|
28
|
+
export ANTHROPIC_BASE_URL=https://your-proxy.com
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Minimal Example
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { createSession, builtinTools } from "formagent-sdk"
|
|
35
|
+
|
|
36
|
+
const session = await createSession({
|
|
37
|
+
model: "claude-sonnet-4-20250514",
|
|
38
|
+
tools: builtinTools,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
await session.send("List files in the current directory")
|
|
42
|
+
|
|
43
|
+
for await (const event of session.receive()) {
|
|
44
|
+
if (event.type === "text") {
|
|
45
|
+
process.stdout.write(event.text)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
await session.close()
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Core Concepts
|
|
53
|
+
|
|
54
|
+
### Sessions
|
|
55
|
+
|
|
56
|
+
Sessions manage conversations with Claude. They handle message history, tool execution, and streaming responses.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const session = await createSession({ model: "claude-sonnet-4-20250514" })
|
|
60
|
+
await session.send("Hello!")
|
|
61
|
+
for await (const event of session.receive()) { /* ... */ }
|
|
62
|
+
await session.close()
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Tools
|
|
66
|
+
|
|
67
|
+
Tools extend Claude's capabilities. The SDK provides built-in tools and supports custom tool definitions.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { builtinTools, tool } from "formagent-sdk"
|
|
71
|
+
import { z } from "zod"
|
|
72
|
+
|
|
73
|
+
// Use built-in tools
|
|
74
|
+
const session = await createSession({ tools: builtinTools })
|
|
75
|
+
|
|
76
|
+
// Or create custom tools
|
|
77
|
+
const myTool = tool({
|
|
78
|
+
name: "my_tool",
|
|
79
|
+
description: "Does something useful",
|
|
80
|
+
schema: z.object({ input: z.string() }),
|
|
81
|
+
execute: async ({ input }) => `Result: ${input}`,
|
|
82
|
+
})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### MCP Servers
|
|
86
|
+
|
|
87
|
+
MCP (Model Context Protocol) servers provide a standardized way to expose tools.
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { createSdkMcpServer, tool } from "formagent-sdk"
|
|
91
|
+
|
|
92
|
+
const server = createSdkMcpServer({
|
|
93
|
+
name: "my-server",
|
|
94
|
+
version: "1.0.0",
|
|
95
|
+
tools: [myTool],
|
|
96
|
+
})
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Architecture
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
formagent-sdk
|
|
103
|
+
├── Session API # Conversation management
|
|
104
|
+
│ ├── createSession() # Create new sessions
|
|
105
|
+
│ ├── send() # Send messages
|
|
106
|
+
│ └── receive() # Stream responses
|
|
107
|
+
├── Session Storage # Persistence layer
|
|
108
|
+
│ ├── MemorySessionStorage # In-memory (default, non-persistent)
|
|
109
|
+
│ └── FileSessionStorage # File-based persistence
|
|
110
|
+
├── Tool System # Tool execution
|
|
111
|
+
│ ├── builtinTools # Built-in tools (Bash, Read, Write, etc.)
|
|
112
|
+
│ ├── tool() # Tool definition helper
|
|
113
|
+
│ └── ToolManager # Tool registration and execution
|
|
114
|
+
├── MCP Integration # Model Context Protocol
|
|
115
|
+
│ ├── createSdkMcpServer()
|
|
116
|
+
│ └── MCPServerManager
|
|
117
|
+
└── Providers # LLM providers
|
|
118
|
+
├── AnthropicProvider
|
|
119
|
+
├── OpenAIProvider
|
|
120
|
+
└── GeminiProvider
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Support
|
|
124
|
+
|
|
125
|
+
- [GitHub Issues](https://github.com/anthropics/claude-code/issues)
|
|
126
|
+
- [Examples](../examples/)
|