@falai/agent 0.1.4 → 0.2.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.
Files changed (97) hide show
  1. package/README.md +117 -1
  2. package/dist/cjs/core/Agent.d.ts +11 -0
  3. package/dist/cjs/core/Agent.d.ts.map +1 -1
  4. package/dist/cjs/core/Agent.js +44 -2
  5. package/dist/cjs/core/Agent.js.map +1 -1
  6. package/dist/cjs/core/Events.d.ts +2 -2
  7. package/dist/cjs/core/Events.d.ts.map +1 -1
  8. package/dist/cjs/core/Events.js +4 -4
  9. package/dist/cjs/core/Events.js.map +1 -1
  10. package/dist/cjs/core/Observation.d.ts.map +1 -1
  11. package/dist/cjs/core/Observation.js +3 -2
  12. package/dist/cjs/core/Observation.js.map +1 -1
  13. package/dist/cjs/core/Route.d.ts.map +1 -1
  14. package/dist/cjs/core/Route.js +3 -4
  15. package/dist/cjs/core/Route.js.map +1 -1
  16. package/dist/cjs/core/State.d.ts +1 -1
  17. package/dist/cjs/core/State.d.ts.map +1 -1
  18. package/dist/cjs/core/State.js +4 -3
  19. package/dist/cjs/core/State.js.map +1 -1
  20. package/dist/cjs/core/Tool.d.ts +1 -0
  21. package/dist/cjs/core/Tool.d.ts.map +1 -1
  22. package/dist/cjs/core/Tool.js +3 -2
  23. package/dist/cjs/core/Tool.js.map +1 -1
  24. package/dist/cjs/index.d.ts +2 -1
  25. package/dist/cjs/index.d.ts.map +1 -1
  26. package/dist/cjs/index.js +7 -1
  27. package/dist/cjs/index.js.map +1 -1
  28. package/dist/cjs/types/agent.d.ts +26 -2
  29. package/dist/cjs/types/agent.d.ts.map +1 -1
  30. package/dist/cjs/types/observation.d.ts +2 -0
  31. package/dist/cjs/types/observation.d.ts.map +1 -1
  32. package/dist/cjs/types/route.d.ts +2 -0
  33. package/dist/cjs/types/route.d.ts.map +1 -1
  34. package/dist/cjs/types/tool.d.ts +6 -2
  35. package/dist/cjs/types/tool.d.ts.map +1 -1
  36. package/dist/cjs/utils/id.d.ts +25 -0
  37. package/dist/cjs/utils/id.d.ts.map +1 -0
  38. package/dist/cjs/utils/id.js +71 -0
  39. package/dist/cjs/utils/id.js.map +1 -0
  40. package/dist/core/Agent.d.ts +11 -0
  41. package/dist/core/Agent.d.ts.map +1 -1
  42. package/dist/core/Agent.js +44 -2
  43. package/dist/core/Agent.js.map +1 -1
  44. package/dist/core/Events.d.ts +2 -2
  45. package/dist/core/Events.d.ts.map +1 -1
  46. package/dist/core/Events.js +4 -4
  47. package/dist/core/Events.js.map +1 -1
  48. package/dist/core/Observation.d.ts.map +1 -1
  49. package/dist/core/Observation.js +3 -2
  50. package/dist/core/Observation.js.map +1 -1
  51. package/dist/core/Route.d.ts.map +1 -1
  52. package/dist/core/Route.js +3 -4
  53. package/dist/core/Route.js.map +1 -1
  54. package/dist/core/State.d.ts +1 -1
  55. package/dist/core/State.d.ts.map +1 -1
  56. package/dist/core/State.js +4 -3
  57. package/dist/core/State.js.map +1 -1
  58. package/dist/core/Tool.d.ts +1 -0
  59. package/dist/core/Tool.d.ts.map +1 -1
  60. package/dist/core/Tool.js +3 -2
  61. package/dist/core/Tool.js.map +1 -1
  62. package/dist/index.d.ts +2 -1
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +2 -0
  65. package/dist/index.js.map +1 -1
  66. package/dist/types/agent.d.ts +26 -2
  67. package/dist/types/agent.d.ts.map +1 -1
  68. package/dist/types/observation.d.ts +2 -0
  69. package/dist/types/observation.d.ts.map +1 -1
  70. package/dist/types/route.d.ts +2 -0
  71. package/dist/types/route.d.ts.map +1 -1
  72. package/dist/types/tool.d.ts +6 -2
  73. package/dist/types/tool.d.ts.map +1 -1
  74. package/dist/utils/id.d.ts +25 -0
  75. package/dist/utils/id.d.ts.map +1 -0
  76. package/dist/utils/id.js +65 -0
  77. package/dist/utils/id.js.map +1 -0
  78. package/docs/API_REFERENCE.md +122 -6
  79. package/docs/CONSTRUCTOR_OPTIONS.md +43 -36
  80. package/docs/CONTEXT_MANAGEMENT.md +447 -0
  81. package/docs/GETTING_STARTED.md +2 -0
  82. package/docs/PROVIDERS.md +3 -0
  83. package/examples/declarative-agent.ts +31 -7
  84. package/examples/persistent-onboarding.ts +464 -0
  85. package/package.json +1 -1
  86. package/src/core/Agent.ts +56 -2
  87. package/src/core/Events.ts +6 -4
  88. package/src/core/Observation.ts +3 -3
  89. package/src/core/Route.ts +3 -5
  90. package/src/core/State.ts +5 -4
  91. package/src/core/Tool.ts +4 -3
  92. package/src/index.ts +10 -0
  93. package/src/types/agent.ts +36 -2
  94. package/src/types/observation.ts +2 -0
  95. package/src/types/route.ts +2 -0
  96. package/src/types/tool.ts +6 -2
  97. package/src/utils/id.ts +74 -0
@@ -18,16 +18,16 @@ interface AgentOptions<TContext = unknown> {
18
18
  // Required
19
19
  name: string;
20
20
  ai: AiProvider;
21
-
21
+
22
22
  // Optional metadata
23
23
  description?: string;
24
24
  goal?: string;
25
25
  context?: TContext;
26
-
26
+
27
27
  // Configuration
28
28
  maxEngineIterations?: number;
29
29
  compositionMode?: CompositionMode;
30
-
30
+
31
31
  // Declarative initialization (NEW!)
32
32
  terms?: Term[];
33
33
  guidelines?: Guideline[];
@@ -44,43 +44,47 @@ const agent = new Agent({
44
44
  name: "SupportBot",
45
45
  description: "Helpful customer support",
46
46
  goal: "Resolve issues efficiently",
47
- ai: new GeminiProvider({ apiKey: "..." }),
47
+ ai: new GeminiProvider({ apiKey: "...", model: "..." }),
48
48
  context: { userId: "123" },
49
-
49
+
50
50
  terms: [
51
- { name: "SLA", description: "Service Level Agreement", synonyms: ["response time"] }
51
+ {
52
+ name: "SLA",
53
+ description: "Service Level Agreement",
54
+ synonyms: ["response time"],
55
+ },
52
56
  ],
53
-
57
+
54
58
  guidelines: [
55
59
  {
56
60
  condition: "User is frustrated",
57
61
  action: "Show empathy and offer escalation",
58
62
  tags: ["support"],
59
- enabled: true
60
- }
63
+ enabled: true,
64
+ },
61
65
  ],
62
-
66
+
63
67
  capabilities: [
64
- { title: "Ticket Management", description: "Create and track tickets" }
68
+ { title: "Ticket Management", description: "Create and track tickets" },
65
69
  ],
66
-
70
+
67
71
  routes: [
68
72
  {
69
73
  title: "Create Ticket",
70
74
  description: "Help user create a support ticket",
71
75
  conditions: ["User wants to report an issue"],
72
76
  guidelines: [
73
- { condition: "Issue is urgent", action: "Prioritize immediately" }
74
- ]
75
- }
77
+ { condition: "Issue is urgent", action: "Prioritize immediately" },
78
+ ],
79
+ },
76
80
  ],
77
-
81
+
78
82
  observations: [
79
83
  {
80
84
  description: "User mentions problem but unclear what kind",
81
- routeRefs: ["Create Ticket", "Check Ticket Status"] // By title!
82
- }
83
- ]
85
+ routeRefs: ["Create Ticket", "Check Ticket Status"], // By title!
86
+ },
87
+ ],
84
88
  });
85
89
  ```
86
90
 
@@ -92,7 +96,7 @@ const agent = new Agent({
92
96
  interface RouteOptions {
93
97
  // Required
94
98
  title: string;
95
-
99
+
96
100
  // Optional
97
101
  description?: string;
98
102
  conditions?: string[];
@@ -115,16 +119,16 @@ const agent = new Agent({
115
119
  {
116
120
  condition: "User skips a step",
117
121
  action: "Gently remind them it's important",
118
- tags: ["onboarding"]
122
+ tags: ["onboarding"],
119
123
  },
120
124
  {
121
125
  condition: "User seems confused",
122
126
  action: "Offer a quick tutorial video",
123
- tags: ["help"]
124
- }
125
- ]
126
- }
127
- ]
127
+ tags: ["help"],
128
+ },
129
+ ],
130
+ },
131
+ ],
128
132
  });
129
133
  ```
130
134
 
@@ -187,18 +191,21 @@ obs.disambiguate([route1, route2]);
187
191
  ## 🎨 Best Practices
188
192
 
189
193
  ### Use Declarative When:
194
+
190
195
  - ✅ Configuration is **static** and known upfront
191
196
  - ✅ Loading config from **JSON/YAML files**
192
197
  - ✅ Building **reusable agent templates**
193
198
  - ✅ You want **clean, readable initialization**
194
199
 
195
200
  ### Use Fluent When:
201
+
196
202
  - ✅ Logic is **dynamic** or **conditional**
197
203
  - ✅ Building routes with **complex state machines**
198
204
  - ✅ Adding features **based on runtime conditions**
199
205
  - ✅ You prefer **step-by-step construction**
200
206
 
201
207
  ### Mix Both!
208
+
202
209
  ```typescript
203
210
  // Start with static config
204
211
  const agent = new Agent({
@@ -212,7 +219,7 @@ const agent = new Agent({
212
219
  if (user.isPremium) {
213
220
  agent.createGuideline({
214
221
  condition: "User asks for priority support",
215
- action: "Escalate immediately to premium team"
222
+ action: "Escalate immediately to premium team",
216
223
  });
217
224
  }
218
225
  ```
@@ -221,15 +228,15 @@ if (user.isPremium) {
221
228
 
222
229
  ## 📊 Complete Comparison
223
230
 
224
- | Feature | Declarative (Constructor) | Fluent (Methods) |
225
- |---------|--------------------------|------------------|
226
- | **Terms** | `terms: Term[]` | `agent.createTerm(...)` |
227
- | **Guidelines** | `guidelines: Guideline[]` | `agent.createGuideline(...)` |
228
- | **Capabilities** | `capabilities: Capability[]` | `agent.createCapability(...)` |
229
- | **Routes** | `routes: RouteOptions[]` | `agent.createRoute(...)` |
230
- | **Route Guidelines** | `route.guidelines: Guideline[]` | `route.createGuideline(...)` |
231
- | **Observations** | `observations: ObservationOptions[]` | `agent.createObservation(...)` |
232
- | **Disambiguation** | `routeRefs: string[]` | `obs.disambiguate([...])` |
231
+ | Feature | Declarative (Constructor) | Fluent (Methods) |
232
+ | -------------------- | ------------------------------------ | ------------------------------ |
233
+ | **Terms** | `terms: Term[]` | `agent.createTerm(...)` |
234
+ | **Guidelines** | `guidelines: Guideline[]` | `agent.createGuideline(...)` |
235
+ | **Capabilities** | `capabilities: Capability[]` | `agent.createCapability(...)` |
236
+ | **Routes** | `routes: RouteOptions[]` | `agent.createRoute(...)` |
237
+ | **Route Guidelines** | `route.guidelines: Guideline[]` | `route.createGuideline(...)` |
238
+ | **Observations** | `observations: ObservationOptions[]` | `agent.createObservation(...)` |
239
+ | **Disambiguation** | `routeRefs: string[]` | `obs.disambiguate([...])` |
233
240
 
234
241
  ---
235
242
 
@@ -0,0 +1,447 @@
1
+ # Context Management & Persistence
2
+
3
+ ## Overview
4
+
5
+ The `@falai/agent` framework provides flexible patterns for managing context in both **single-turn** and **multi-turn persistent** conversations. This guide explains how to handle stateful conversations that persist across requests.
6
+
7
+ ---
8
+
9
+ ## 🤔 The Context Lifecycle Problem
10
+
11
+ ### Single-Turn Conversations (Simple)
12
+
13
+ ```typescript
14
+ // ✅ Works great for single-turn conversations
15
+ const agent = new Agent({
16
+ name: "Bot",
17
+ ai: provider,
18
+ context: { userId: "123", preferences: {} },
19
+ });
20
+
21
+ const response = await agent.respond({ history });
22
+ ```
23
+
24
+ **Works because:** Agent is used once and discarded.
25
+
26
+ ### Multi-Turn Conversations (Complex)
27
+
28
+ ```typescript
29
+ // ❌ PROBLEM: Context updates don't persist
30
+ const agent = new Agent({
31
+ name: "Bot",
32
+ ai: provider,
33
+ context: { userId: "123", preferences: {} },
34
+ });
35
+
36
+ // Turn 1
37
+ await agent.respond({ history: [message1] });
38
+ // Tool modifies context in memory...
39
+
40
+ // Turn 2 (new request, agent recreated)
41
+ const agent2 = new Agent({ context: { userId: "123", preferences: {} } });
42
+ await agent2.respond({ history: [message2] });
43
+ // ❌ Lost the context changes from Turn 1!
44
+ ```
45
+
46
+ **Problem:** When agents are recreated (e.g., across HTTP requests), context updates are lost.
47
+
48
+ ---
49
+
50
+ ## 💡 Solution: Context Lifecycle Hooks
51
+
52
+ The framework provides **lifecycle hooks** to integrate with your persistence layer (database, cache, etc.).
53
+
54
+ ### Pattern 1: `beforeRespond` + `onContextUpdate` (Recommended)
55
+
56
+ ```typescript
57
+ import { Agent, type ContextLifecycleHooks } from "@falai/agent";
58
+
59
+ // Define hooks
60
+ const hooks: ContextLifecycleHooks<MyContext> = {
61
+ // Load fresh context before each response
62
+ beforeRespond: async (currentContext) => {
63
+ return await database.loadContext(sessionId);
64
+ },
65
+
66
+ // Persist context after updates
67
+ onContextUpdate: async (newContext, previousContext) => {
68
+ await database.saveContext(sessionId, newContext);
69
+ },
70
+ };
71
+
72
+ // Create agent with hooks
73
+ const agent = new Agent({
74
+ name: "Bot",
75
+ ai: provider,
76
+ context: initialContext,
77
+ hooks, // 🔑 Enable persistence
78
+ });
79
+ ```
80
+
81
+ **How it works:**
82
+
83
+ 1. `beforeRespond` fetches fresh context from your database before `respond()` is called
84
+ 2. Tools can update context using `toolContext.updateContext()` or returning `{ contextUpdate }`
85
+ 3. `onContextUpdate` automatically persists changes to your database
86
+ 4. Next request creates a new agent, `beforeRespond` loads the updated context
87
+
88
+ ---
89
+
90
+ ## 🔧 Tool Context Updates
91
+
92
+ Tools can update context in **two ways**:
93
+
94
+ ### Option A: Return `contextUpdate`
95
+
96
+ ```typescript
97
+ const saveName = defineTool<MyContext, [name: string], boolean>(
98
+ "save_name",
99
+ async (toolContext, name) => {
100
+ return {
101
+ data: true,
102
+ contextUpdate: {
103
+ userName: name,
104
+ updatedAt: new Date(),
105
+ },
106
+ };
107
+ }
108
+ );
109
+ ```
110
+
111
+ **Pros:**
112
+
113
+ - Declarative and functional
114
+ - Easy to test
115
+ - Context update is part of the result
116
+
117
+ ### Option B: Call `updateContext()` directly
118
+
119
+ ```typescript
120
+ const saveName = defineTool<MyContext, [name: string], boolean>(
121
+ "save_name",
122
+ async (toolContext, name) => {
123
+ await toolContext.updateContext({
124
+ userName: name,
125
+ updatedAt: new Date(),
126
+ });
127
+
128
+ return { data: true };
129
+ }
130
+ );
131
+ ```
132
+
133
+ **Pros:**
134
+
135
+ - More imperative and direct
136
+ - Can update context at any point in the tool
137
+ - Useful for complex logic
138
+
139
+ **Both approaches trigger the `onContextUpdate` hook automatically.**
140
+
141
+ ---
142
+
143
+ ## 🌐 Context Provider Pattern
144
+
145
+ For scenarios where context is **always loaded from an external source**, use the `contextProvider` pattern:
146
+
147
+ ```typescript
148
+ const agent = new Agent({
149
+ name: "Bot",
150
+ ai: provider,
151
+
152
+ // Instead of static context:
153
+ contextProvider: async () => {
154
+ // Fetch context fresh on every respond() call
155
+ return await database.loadContext(sessionId);
156
+ },
157
+
158
+ hooks: {
159
+ // Still persist updates
160
+ onContextUpdate: async (newContext) => {
161
+ await database.saveContext(sessionId, newContext);
162
+ },
163
+ },
164
+ });
165
+ ```
166
+
167
+ **When to use:**
168
+
169
+ - Context is always loaded from a database/cache
170
+ - You never have a static starting context
171
+ - You want guaranteed fresh data on every request
172
+
173
+ **Difference from `beforeRespond`:**
174
+
175
+ - `contextProvider`: **Replaces** the context entirely
176
+ - `beforeRespond`: **Updates** the existing context (can access previous state)
177
+
178
+ ---
179
+
180
+ ## 🎯 Complete Example: Multi-Turn Onboarding
181
+
182
+ ```typescript
183
+ import { Agent, defineTool } from "@falai/agent";
184
+
185
+ interface OnboardingContext {
186
+ sessionId: string;
187
+ userId: string;
188
+ businessName?: string;
189
+ industry?: string;
190
+ completedSteps: string[];
191
+ }
192
+
193
+ // Database simulation
194
+ const db = {
195
+ async loadSession(sessionId: string) {
196
+ return await database.findSession(sessionId);
197
+ },
198
+ async saveSession(sessionId: string, data: Partial<OnboardingContext>) {
199
+ await database.updateSession(sessionId, data);
200
+ },
201
+ };
202
+
203
+ // Factory function for creating agents
204
+ async function createOnboardingAgent(sessionId: string) {
205
+ const session = await db.loadSession(sessionId);
206
+
207
+ const agent = new Agent<OnboardingContext>({
208
+ name: "OnboardingBot",
209
+ ai: provider,
210
+ context: {
211
+ sessionId,
212
+ userId: session.userId,
213
+ businessName: session.businessName,
214
+ industry: session.industry,
215
+ completedSteps: session.completedSteps || [],
216
+ },
217
+ hooks: {
218
+ // Load fresh context before responding
219
+ beforeRespond: async (current) => {
220
+ const fresh = await db.loadSession(sessionId);
221
+ return { ...current, ...fresh };
222
+ },
223
+
224
+ // Persist context after updates
225
+ onContextUpdate: async (newContext) => {
226
+ await db.saveSession(sessionId, {
227
+ businessName: newContext.businessName,
228
+ industry: newContext.industry,
229
+ completedSteps: newContext.completedSteps,
230
+ });
231
+ },
232
+ },
233
+ });
234
+
235
+ // Define tools with context updates
236
+ const saveBusinessName = defineTool(
237
+ "save_business_name",
238
+ async (ctx, name: string) => {
239
+ return {
240
+ data: true,
241
+ contextUpdate: {
242
+ businessName: name,
243
+ completedSteps: [...ctx.context.completedSteps, "business_name"],
244
+ },
245
+ };
246
+ }
247
+ );
248
+
249
+ const saveIndustry = defineTool(
250
+ "save_industry",
251
+ async (ctx, industry: string) => {
252
+ // Alternative: use updateContext directly
253
+ await ctx.updateContext({
254
+ industry,
255
+ completedSteps: [...ctx.context.completedSteps, "industry"],
256
+ });
257
+
258
+ return { data: true };
259
+ }
260
+ );
261
+
262
+ // Build conversation routes...
263
+ const route = agent.createRoute({ title: "Onboarding" });
264
+ // ...
265
+
266
+ return agent;
267
+ }
268
+
269
+ // Usage across multiple turns
270
+ async function handleUserMessage(sessionId: string, message: string) {
271
+ // Recreate agent for each turn (context is loaded fresh)
272
+ const agent = await createOnboardingAgent(sessionId);
273
+
274
+ const response = await agent.respond({
275
+ history: [createMessageEvent(EventSource.CUSTOMER, "User", message)],
276
+ });
277
+
278
+ return response.message;
279
+ }
280
+ ```
281
+
282
+ ---
283
+
284
+ ## 📋 Best Practices
285
+
286
+ ### ✅ DO
287
+
288
+ - **Recreate agents** for each request in multi-turn conversations
289
+ - **Use lifecycle hooks** to integrate with your database
290
+ - **Store context** in your database/cache (Redis, PostgreSQL, etc.)
291
+ - **Load fresh context** via `beforeRespond` or `contextProvider`
292
+ - **Test persistence** by simulating multiple turns
293
+ - **Handle errors** in hooks gracefully (fallback to current context)
294
+
295
+ ### ❌ DON'T
296
+
297
+ - **Cache agent instances** across requests (context gets stale)
298
+ - **Mutate context directly** without using `updateContext()` or `contextUpdate`
299
+ - **Rely on in-memory state** for multi-turn conversations
300
+ - **Forget to handle** `onContextUpdate` failures (could lose data)
301
+ - **Mix** `context` and `contextProvider` (will throw an error)
302
+
303
+ ---
304
+
305
+ ## 🔍 Debugging Context Issues
306
+
307
+ ### Problem: Context updates aren't persisting
308
+
309
+ **Check:**
310
+
311
+ 1. Are you using `onContextUpdate` hook?
312
+ 2. Is your database save actually working?
313
+ 3. Are you recreating the agent with fresh context each turn?
314
+
315
+ ```typescript
316
+ // Debug your hooks
317
+ hooks: {
318
+ beforeRespond: async (current) => {
319
+ console.log("📥 Loading context:", current);
320
+ const fresh = await db.load(sessionId);
321
+ console.log("✅ Loaded fresh:", fresh);
322
+ return fresh;
323
+ },
324
+
325
+ onContextUpdate: async (newContext) => {
326
+ console.log("💾 Saving context:", newContext);
327
+ await db.save(sessionId, newContext);
328
+ console.log("✅ Saved successfully");
329
+ },
330
+ },
331
+ ```
332
+
333
+ ### Problem: Context is null/undefined
334
+
335
+ **Check:**
336
+
337
+ 1. Did you provide either `context` or `contextProvider`?
338
+ 2. Is your `contextProvider` returning valid data?
339
+ 3. Is `beforeRespond` returning valid context?
340
+
341
+ ```typescript
342
+ // Validate your contextProvider
343
+ contextProvider: async () => {
344
+ const ctx = await db.load(sessionId);
345
+ if (!ctx) {
346
+ console.error("❌ Context not found!");
347
+ throw new Error("Session not found");
348
+ }
349
+ return ctx;
350
+ },
351
+ ```
352
+
353
+ ### Problem: Tools not updating context
354
+
355
+ **Check:**
356
+
357
+ 1. Are you returning `{ contextUpdate }` or calling `toolContext.updateContext()`?
358
+ 2. Is `onContextUpdate` being called? (Add console.log)
359
+ 3. Are your tools being executed at all?
360
+
361
+ ---
362
+
363
+ ## 🚀 Advanced Patterns
364
+
365
+ ### Pattern: Context Versioning
366
+
367
+ ```typescript
368
+ interface VersionedContext extends MyContext {
369
+ version: number;
370
+ }
371
+
372
+ hooks: {
373
+ onContextUpdate: async (newContext) => {
374
+ await db.save(sessionId, {
375
+ ...newContext,
376
+ version: newContext.version + 1,
377
+ });
378
+ },
379
+
380
+ beforeRespond: async (current) => {
381
+ const fresh = await db.load(sessionId);
382
+ if (fresh.version > current.version) {
383
+ console.log("⚠️ Context changed by another request!");
384
+ }
385
+ return fresh;
386
+ },
387
+ },
388
+ ```
389
+
390
+ ### Pattern: Selective Persistence
391
+
392
+ ```typescript
393
+ // Only persist specific fields
394
+ hooks: {
395
+ onContextUpdate: async (newContext) => {
396
+ // Don't persist temporary/computed fields
397
+ const { tempData, ...persistable } = newContext;
398
+ await db.save(sessionId, persistable);
399
+ },
400
+ },
401
+ ```
402
+
403
+ ### Pattern: Multi-User Context
404
+
405
+ ```typescript
406
+ interface MultiUserContext {
407
+ conversationId: string;
408
+ participants: Map<string, UserData>;
409
+ }
410
+
411
+ hooks: {
412
+ beforeRespond: async (current) => {
413
+ // Load all participant data
414
+ const conversation = await db.loadConversation(conversationId);
415
+ return {
416
+ conversationId,
417
+ participants: new Map(conversation.participants),
418
+ };
419
+ },
420
+ },
421
+ ```
422
+
423
+ ---
424
+
425
+ ## 📚 Related Resources
426
+
427
+ - [Complete Example: Persistent Onboarding](../examples/persistent-onboarding.ts)
428
+ - [API Reference: AgentOptions](./API_REFERENCE.md#agentoptions)
429
+ - [API Reference: ContextLifecycleHooks](./API_REFERENCE.md#contextlifecyclehooks)
430
+ - [Getting Started](./GETTING_STARTED.md)
431
+
432
+ ---
433
+
434
+ ## 🆘 Need Help?
435
+
436
+ If you're still having issues:
437
+
438
+ 1. Check the [examples](../examples/) for working implementations
439
+ 2. Review the [API Reference](./API_REFERENCE.md) for detailed type information
440
+ 3. Open an issue on GitHub with your use case
441
+
442
+ **Remember:** The key to persistent conversations is:
443
+
444
+ 1. **Recreate agents** each turn
445
+ 2. **Load fresh context** via hooks/provider
446
+ 3. **Persist updates** via `onContextUpdate`
447
+ 4. **Never cache** agent instances across requests
@@ -58,6 +58,7 @@ interface MyContext {
58
58
  // Create AI provider
59
59
  const ai = new GeminiProvider({
60
60
  apiKey: process.env.GEMINI_API_KEY!,
61
+ model: "models/gemini-2.5-pro",
61
62
  });
62
63
 
63
64
  // Create your agent
@@ -307,6 +308,7 @@ Increase timeout in provider config:
307
308
  ```typescript
308
309
  new GeminiProvider({
309
310
  apiKey: "...",
311
+ model: "models/gemini-2.5-flash",
310
312
  retryConfig: {
311
313
  timeout: 120000, // 2 minutes
312
314
  retries: 5,
package/docs/PROVIDERS.md CHANGED
@@ -281,6 +281,7 @@ const geminiAgent = new Agent({
281
281
  name: "Gemini Assistant",
282
282
  ai: new GeminiProvider({
283
283
  apiKey: process.env.GEMINI_API_KEY!,
284
+ model: "models/gemini-2.5-flash",
284
285
  }),
285
286
  });
286
287
 
@@ -315,10 +316,12 @@ config();
315
316
 
316
317
  const geminiProvider = new GeminiProvider({
317
318
  apiKey: process.env.GEMINI_API_KEY!,
319
+ model: "models/gemini-2.5-flash",
318
320
  });
319
321
 
320
322
  const openaiProvider = new OpenAIProvider({
321
323
  apiKey: process.env.OPENAI_API_KEY!,
324
+ model: "gpt-5",
322
325
  });
323
326
  ```
324
327