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.
@@ -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 |