formagent-sdk 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/cli/index.js +290 -86
- package/dist/index.js +259 -83
- 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
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# Session Storage
|
|
2
|
+
|
|
3
|
+
Session storage enables persistent conversation state across process restarts. This is essential for applications that need to maintain conversation context over time.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The SDK provides two built-in storage implementations:
|
|
8
|
+
|
|
9
|
+
| Storage Type | Persistence | Use Case |
|
|
10
|
+
|-------------|-------------|----------|
|
|
11
|
+
| `MemorySessionStorage` | Process lifetime only | Development, testing, single-use sessions |
|
|
12
|
+
| `FileSessionStorage` | Disk-based, survives restarts | Production, multi-session applications |
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
### Using FileSessionStorage
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { createSession, FileSessionStorage, builtinTools } from "formagent-sdk"
|
|
20
|
+
|
|
21
|
+
// Create a persistent storage instance
|
|
22
|
+
const storage = new FileSessionStorage("./sessions")
|
|
23
|
+
|
|
24
|
+
// Create session with persistent storage
|
|
25
|
+
const session = await createSession({
|
|
26
|
+
model: "claude-sonnet-4-20250514",
|
|
27
|
+
tools: builtinTools,
|
|
28
|
+
sessionStorage: storage,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// Save the session ID for later resumption
|
|
32
|
+
const sessionId = session.id
|
|
33
|
+
console.log(`Session created: ${sessionId}`)
|
|
34
|
+
|
|
35
|
+
// ... use the session ...
|
|
36
|
+
|
|
37
|
+
await session.close()
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Resuming a Session
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { createSession, FileSessionStorage, builtinTools } from "formagent-sdk"
|
|
44
|
+
|
|
45
|
+
const storage = new FileSessionStorage("./sessions")
|
|
46
|
+
|
|
47
|
+
// Resume from a previous session
|
|
48
|
+
const session = await createSession({
|
|
49
|
+
model: "claude-sonnet-4-20250514",
|
|
50
|
+
tools: builtinTools,
|
|
51
|
+
sessionStorage: storage,
|
|
52
|
+
resume: "previous-session-id", // The session ID from before
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// Continue the conversation with full context
|
|
56
|
+
await session.send("What were we discussing?")
|
|
57
|
+
|
|
58
|
+
for await (const event of session.receive()) {
|
|
59
|
+
if (event.type === "text") {
|
|
60
|
+
process.stdout.write(event.text)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Global Storage Configuration
|
|
66
|
+
|
|
67
|
+
For applications that use a single storage backend, you can set a default storage:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { setDefaultStorage, FileSessionStorage, createSession } from "formagent-sdk"
|
|
71
|
+
|
|
72
|
+
// Set once at application startup
|
|
73
|
+
setDefaultStorage(new FileSessionStorage("./sessions"))
|
|
74
|
+
|
|
75
|
+
// All sessions now use file storage by default
|
|
76
|
+
const session1 = await createSession({ model: "claude-sonnet-4-20250514" })
|
|
77
|
+
const session2 = await createSession({ model: "claude-sonnet-4-20250514" })
|
|
78
|
+
|
|
79
|
+
// Both sessions are persisted to ./sessions/
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Storage Interface
|
|
83
|
+
|
|
84
|
+
You can implement custom storage backends by implementing the `SessionStorage` interface:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
interface SessionStorage {
|
|
88
|
+
/** Save session state */
|
|
89
|
+
save(state: SessionState): Promise<void>
|
|
90
|
+
|
|
91
|
+
/** Load session state by ID */
|
|
92
|
+
load(sessionId: string): Promise<SessionState | undefined>
|
|
93
|
+
|
|
94
|
+
/** Delete session state */
|
|
95
|
+
delete(sessionId: string): Promise<void>
|
|
96
|
+
|
|
97
|
+
/** List all stored session IDs */
|
|
98
|
+
list(): Promise<string[]>
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### SessionState Structure
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
interface SessionState {
|
|
106
|
+
/** Unique session identifier */
|
|
107
|
+
id: string
|
|
108
|
+
/** Conversation messages */
|
|
109
|
+
messages: SDKMessage[]
|
|
110
|
+
/** Accumulated usage statistics */
|
|
111
|
+
usage: ExtendedUsageInfo
|
|
112
|
+
/** Session metadata */
|
|
113
|
+
metadata: Record<string, unknown>
|
|
114
|
+
/** Creation timestamp */
|
|
115
|
+
createdAt: number
|
|
116
|
+
/** Last update timestamp */
|
|
117
|
+
updatedAt: number
|
|
118
|
+
/** Parent session ID (if forked) */
|
|
119
|
+
parentId?: string
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Built-in Implementations
|
|
124
|
+
|
|
125
|
+
### MemorySessionStorage
|
|
126
|
+
|
|
127
|
+
In-memory storage that exists only for the process lifetime.
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { MemorySessionStorage } from "formagent-sdk"
|
|
131
|
+
|
|
132
|
+
const storage = new MemorySessionStorage()
|
|
133
|
+
|
|
134
|
+
// Additional methods
|
|
135
|
+
storage.clear() // Clear all sessions
|
|
136
|
+
storage.size() // Get number of stored sessions
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### FileSessionStorage
|
|
140
|
+
|
|
141
|
+
File-based storage that persists sessions as JSON files.
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import { FileSessionStorage } from "formagent-sdk"
|
|
145
|
+
|
|
146
|
+
// Sessions stored as ./sessions/{session-id}.json
|
|
147
|
+
const storage = new FileSessionStorage("./sessions")
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**File Structure:**
|
|
151
|
+
```
|
|
152
|
+
./sessions/
|
|
153
|
+
├── sess_abc123.json
|
|
154
|
+
├── sess_def456.json
|
|
155
|
+
└── sess_ghi789.json
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Custom Storage Examples
|
|
159
|
+
|
|
160
|
+
### Redis Storage
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { createClient } from "redis"
|
|
164
|
+
import type { SessionStorage, SessionState } from "formagent-sdk"
|
|
165
|
+
|
|
166
|
+
class RedisSessionStorage implements SessionStorage {
|
|
167
|
+
private client: ReturnType<typeof createClient>
|
|
168
|
+
private prefix: string
|
|
169
|
+
|
|
170
|
+
constructor(redisUrl: string, prefix = "session:") {
|
|
171
|
+
this.client = createClient({ url: redisUrl })
|
|
172
|
+
this.prefix = prefix
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async save(state: SessionState): Promise<void> {
|
|
176
|
+
await this.client.set(
|
|
177
|
+
this.prefix + state.id,
|
|
178
|
+
JSON.stringify(state)
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async load(sessionId: string): Promise<SessionState | undefined> {
|
|
183
|
+
const data = await this.client.get(this.prefix + sessionId)
|
|
184
|
+
return data ? JSON.parse(data) : undefined
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async delete(sessionId: string): Promise<void> {
|
|
188
|
+
await this.client.del(this.prefix + sessionId)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async list(): Promise<string[]> {
|
|
192
|
+
const keys = await this.client.keys(this.prefix + "*")
|
|
193
|
+
return keys.map(k => k.slice(this.prefix.length))
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### SQLite Storage
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import Database from "better-sqlite3"
|
|
202
|
+
import type { SessionStorage, SessionState } from "formagent-sdk"
|
|
203
|
+
|
|
204
|
+
class SQLiteSessionStorage implements SessionStorage {
|
|
205
|
+
private db: Database.Database
|
|
206
|
+
|
|
207
|
+
constructor(dbPath: string) {
|
|
208
|
+
this.db = new Database(dbPath)
|
|
209
|
+
this.db.exec(`
|
|
210
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
211
|
+
id TEXT PRIMARY KEY,
|
|
212
|
+
state TEXT NOT NULL,
|
|
213
|
+
updated_at INTEGER NOT NULL
|
|
214
|
+
)
|
|
215
|
+
`)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async save(state: SessionState): Promise<void> {
|
|
219
|
+
this.db.prepare(`
|
|
220
|
+
INSERT OR REPLACE INTO sessions (id, state, updated_at)
|
|
221
|
+
VALUES (?, ?, ?)
|
|
222
|
+
`).run(state.id, JSON.stringify(state), Date.now())
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async load(sessionId: string): Promise<SessionState | undefined> {
|
|
226
|
+
const row = this.db.prepare(
|
|
227
|
+
"SELECT state FROM sessions WHERE id = ?"
|
|
228
|
+
).get(sessionId) as { state: string } | undefined
|
|
229
|
+
return row ? JSON.parse(row.state) : undefined
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async delete(sessionId: string): Promise<void> {
|
|
233
|
+
this.db.prepare("DELETE FROM sessions WHERE id = ?").run(sessionId)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async list(): Promise<string[]> {
|
|
237
|
+
const rows = this.db.prepare("SELECT id FROM sessions").all() as { id: string }[]
|
|
238
|
+
return rows.map(r => r.id)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Best Practices
|
|
244
|
+
|
|
245
|
+
### 1. Use Shared Storage Instance
|
|
246
|
+
|
|
247
|
+
Create a single storage instance and reuse it:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// storage.ts
|
|
251
|
+
import { FileSessionStorage } from "formagent-sdk"
|
|
252
|
+
|
|
253
|
+
export const sessionStorage = new FileSessionStorage("./data/sessions")
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
// app.ts
|
|
258
|
+
import { sessionStorage } from "./storage"
|
|
259
|
+
import { createSession } from "formagent-sdk"
|
|
260
|
+
|
|
261
|
+
const session = await createSession({
|
|
262
|
+
sessionStorage,
|
|
263
|
+
// ...
|
|
264
|
+
})
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### 2. Handle Missing Sessions
|
|
268
|
+
|
|
269
|
+
When resuming, handle the case where the session doesn't exist:
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
try {
|
|
273
|
+
const session = await createSession({
|
|
274
|
+
sessionStorage,
|
|
275
|
+
resume: sessionId,
|
|
276
|
+
})
|
|
277
|
+
} catch (error) {
|
|
278
|
+
if (error.message.includes("Session not found")) {
|
|
279
|
+
// Start a fresh session instead
|
|
280
|
+
const session = await createSession({ sessionStorage })
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 3. Clean Up Old Sessions
|
|
286
|
+
|
|
287
|
+
Implement session cleanup for long-running applications:
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
async function cleanupOldSessions(storage: FileSessionStorage, maxAgeDays: number) {
|
|
291
|
+
const sessionIds = await storage.list()
|
|
292
|
+
const maxAge = maxAgeDays * 24 * 60 * 60 * 1000
|
|
293
|
+
|
|
294
|
+
for (const id of sessionIds) {
|
|
295
|
+
const state = await storage.load(id)
|
|
296
|
+
if (state && Date.now() - state.updatedAt > maxAge) {
|
|
297
|
+
await storage.delete(id)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## API Reference
|
|
304
|
+
|
|
305
|
+
### createSession Options
|
|
306
|
+
|
|
307
|
+
| Option | Type | Description |
|
|
308
|
+
|--------|------|-------------|
|
|
309
|
+
| `sessionStorage` | `SessionStorage` | Storage backend for persistence |
|
|
310
|
+
| `resume` | `string` | Session ID to resume from |
|
|
311
|
+
| `fork` | `string` | Session ID to fork (create branch) |
|
|
312
|
+
|
|
313
|
+
### Functions
|
|
314
|
+
|
|
315
|
+
| Function | Description |
|
|
316
|
+
|----------|-------------|
|
|
317
|
+
| `setDefaultStorage(storage)` | Set global default storage |
|
|
318
|
+
| `createSessionStorage(type, options)` | Create storage instance |
|
|
319
|
+
| `resumeSession(sessionId, options)` | Resume existing session |
|
|
320
|
+
| `forkSession(sessionId, options)` | Fork existing session |
|