@falai/agent 0.3.10 → 0.3.12

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 (131) hide show
  1. package/README.md +192 -16
  2. package/dist/adapters/PrismaAdapter.d.ts +115 -0
  3. package/dist/adapters/PrismaAdapter.d.ts.map +1 -0
  4. package/dist/adapters/PrismaAdapter.js +331 -0
  5. package/dist/adapters/PrismaAdapter.js.map +1 -0
  6. package/dist/adapters/index.d.ts +6 -0
  7. package/dist/adapters/index.d.ts.map +1 -0
  8. package/dist/adapters/index.js +5 -0
  9. package/dist/adapters/index.js.map +1 -0
  10. package/dist/cjs/adapters/PrismaAdapter.d.ts +115 -0
  11. package/dist/cjs/adapters/PrismaAdapter.d.ts.map +1 -0
  12. package/dist/cjs/adapters/PrismaAdapter.js +335 -0
  13. package/dist/cjs/adapters/PrismaAdapter.js.map +1 -0
  14. package/dist/cjs/adapters/index.d.ts +6 -0
  15. package/dist/cjs/adapters/index.d.ts.map +1 -0
  16. package/dist/cjs/adapters/index.js +9 -0
  17. package/dist/cjs/adapters/index.js.map +1 -0
  18. package/dist/cjs/core/Agent.d.ts +35 -0
  19. package/dist/cjs/core/Agent.d.ts.map +1 -1
  20. package/dist/cjs/core/Agent.js +153 -0
  21. package/dist/cjs/core/Agent.js.map +1 -1
  22. package/dist/cjs/core/PersistenceManager.d.ts +77 -0
  23. package/dist/cjs/core/PersistenceManager.d.ts.map +1 -0
  24. package/dist/cjs/core/PersistenceManager.js +153 -0
  25. package/dist/cjs/core/PersistenceManager.js.map +1 -0
  26. package/dist/cjs/index.d.ts +6 -0
  27. package/dist/cjs/index.d.ts.map +1 -1
  28. package/dist/cjs/index.js +8 -1
  29. package/dist/cjs/index.js.map +1 -1
  30. package/dist/cjs/providers/AnthropicProvider.d.ts +43 -0
  31. package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -0
  32. package/dist/cjs/providers/AnthropicProvider.js +328 -0
  33. package/dist/cjs/providers/AnthropicProvider.js.map +1 -0
  34. package/dist/cjs/providers/GeminiProvider.d.ts +4 -1
  35. package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
  36. package/dist/cjs/providers/GeminiProvider.js +96 -0
  37. package/dist/cjs/providers/GeminiProvider.js.map +1 -1
  38. package/dist/cjs/providers/OpenAIProvider.d.ts +4 -1
  39. package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
  40. package/dist/cjs/providers/OpenAIProvider.js +115 -0
  41. package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
  42. package/dist/cjs/providers/OpenRouterProvider.d.ts +4 -1
  43. package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
  44. package/dist/cjs/providers/OpenRouterProvider.js +115 -0
  45. package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
  46. package/dist/cjs/providers/index.d.ts +13 -0
  47. package/dist/cjs/providers/index.d.ts.map +1 -0
  48. package/dist/cjs/providers/index.js +16 -0
  49. package/dist/cjs/providers/index.js.map +1 -0
  50. package/dist/cjs/types/agent.d.ts +3 -0
  51. package/dist/cjs/types/agent.d.ts.map +1 -1
  52. package/dist/cjs/types/agent.js.map +1 -1
  53. package/dist/cjs/types/ai.d.ts +28 -0
  54. package/dist/cjs/types/ai.d.ts.map +1 -1
  55. package/dist/cjs/types/index.d.ts +1 -0
  56. package/dist/cjs/types/index.d.ts.map +1 -1
  57. package/dist/cjs/types/persistence.d.ts +194 -0
  58. package/dist/cjs/types/persistence.d.ts.map +1 -0
  59. package/dist/cjs/types/persistence.js +7 -0
  60. package/dist/cjs/types/persistence.js.map +1 -0
  61. package/dist/core/Agent.d.ts +35 -0
  62. package/dist/core/Agent.d.ts.map +1 -1
  63. package/dist/core/Agent.js +153 -0
  64. package/dist/core/Agent.js.map +1 -1
  65. package/dist/core/PersistenceManager.d.ts +77 -0
  66. package/dist/core/PersistenceManager.d.ts.map +1 -0
  67. package/dist/core/PersistenceManager.js +149 -0
  68. package/dist/core/PersistenceManager.js.map +1 -0
  69. package/dist/index.d.ts +6 -0
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +4 -0
  72. package/dist/index.js.map +1 -1
  73. package/dist/providers/AnthropicProvider.d.ts +43 -0
  74. package/dist/providers/AnthropicProvider.d.ts.map +1 -0
  75. package/dist/providers/AnthropicProvider.js +321 -0
  76. package/dist/providers/AnthropicProvider.js.map +1 -0
  77. package/dist/providers/GeminiProvider.d.ts +4 -1
  78. package/dist/providers/GeminiProvider.d.ts.map +1 -1
  79. package/dist/providers/GeminiProvider.js +96 -0
  80. package/dist/providers/GeminiProvider.js.map +1 -1
  81. package/dist/providers/OpenAIProvider.d.ts +4 -1
  82. package/dist/providers/OpenAIProvider.d.ts.map +1 -1
  83. package/dist/providers/OpenAIProvider.js +115 -0
  84. package/dist/providers/OpenAIProvider.js.map +1 -1
  85. package/dist/providers/OpenRouterProvider.d.ts +4 -1
  86. package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
  87. package/dist/providers/OpenRouterProvider.js +115 -0
  88. package/dist/providers/OpenRouterProvider.js.map +1 -1
  89. package/dist/providers/index.d.ts +13 -0
  90. package/dist/providers/index.d.ts.map +1 -0
  91. package/dist/providers/index.js +9 -0
  92. package/dist/providers/index.js.map +1 -0
  93. package/dist/types/agent.d.ts +3 -0
  94. package/dist/types/agent.d.ts.map +1 -1
  95. package/dist/types/agent.js.map +1 -1
  96. package/dist/types/ai.d.ts +28 -0
  97. package/dist/types/ai.d.ts.map +1 -1
  98. package/dist/types/index.d.ts +1 -0
  99. package/dist/types/index.d.ts.map +1 -1
  100. package/dist/types/persistence.d.ts +194 -0
  101. package/dist/types/persistence.d.ts.map +1 -0
  102. package/dist/types/persistence.js +6 -0
  103. package/dist/types/persistence.js.map +1 -0
  104. package/docs/API_REFERENCE.md +260 -2
  105. package/docs/PERSISTENCE.md +419 -0
  106. package/docs/PROVIDERS.md +139 -2
  107. package/examples/business-onboarding.ts +5 -4
  108. package/examples/declarative-agent.ts +1 -1
  109. package/examples/domain-scoping.ts +5 -4
  110. package/examples/healthcare-agent.ts +4 -4
  111. package/examples/openai-agent.ts +6 -4
  112. package/examples/prisma-persistence.ts +313 -0
  113. package/examples/prisma-schema.example.prisma +74 -0
  114. package/examples/rules-prohibitions.ts +4 -4
  115. package/examples/streaming-agent.ts +371 -0
  116. package/examples/travel-agent.ts +7 -4
  117. package/package.json +10 -1
  118. package/src/adapters/PrismaAdapter.ts +510 -0
  119. package/src/adapters/index.ts +10 -0
  120. package/src/core/Agent.ts +205 -0
  121. package/src/core/PersistenceManager.ts +222 -0
  122. package/src/index.ts +23 -0
  123. package/src/providers/AnthropicProvider.ts +467 -0
  124. package/src/providers/GeminiProvider.ts +135 -0
  125. package/src/providers/OpenAIProvider.ts +157 -0
  126. package/src/providers/OpenRouterProvider.ts +157 -0
  127. package/src/providers/index.ts +16 -0
  128. package/src/types/agent.ts +3 -0
  129. package/src/types/ai.ts +32 -0
  130. package/src/types/index.ts +14 -0
  131. package/src/types/persistence.ts +234 -0
@@ -109,6 +109,107 @@ if (response.route?.title === END_ROUTE) {
109
109
  }
110
110
  ```
111
111
 
112
+ ##### `respondStream(input: RespondInput<TContext>): AsyncGenerator<StreamChunk>`
113
+
114
+ Generates an AI response as a real-time stream for better user experience. Provides the same structured output as `respond()` but delivers it incrementally.
115
+
116
+ ```typescript
117
+ interface StreamChunk {
118
+ /** The incremental text delta */
119
+ delta: string;
120
+ /** Full accumulated text so far */
121
+ accumulated: string;
122
+ /** Whether this is the final chunk */
123
+ done: boolean;
124
+ /** Route chosen by the agent (only in final chunk) */
125
+ route?: { id: string; title: string };
126
+ /** Current state within the route (only in final chunk) */
127
+ state?: { id: string; description?: string };
128
+ /** Tool calls requested by the agent (only in final chunk) */
129
+ toolCalls?: Array<{
130
+ toolName: string;
131
+ arguments: Record<string, unknown>;
132
+ }>;
133
+ }
134
+ ```
135
+
136
+ **Key Features:**
137
+
138
+ - 🌊 Real-time streaming for better perceived performance
139
+ - 📊 Access to route, state, and tool information in final chunk
140
+ - 🛑 Cancellable with AbortSignal
141
+ - ✅ Supported by all providers (Anthropic, OpenAI, Gemini, OpenRouter)
142
+
143
+ **Example:**
144
+
145
+ ```typescript
146
+ // Basic streaming
147
+ for await (const chunk of agent.respondStream({ history })) {
148
+ if (chunk.delta) {
149
+ // Display incremental text to user
150
+ process.stdout.write(chunk.delta);
151
+ }
152
+
153
+ if (chunk.done) {
154
+ console.log("\n✅ Complete!");
155
+ // Access final metadata
156
+ if (chunk.route) {
157
+ console.log("Route:", chunk.route.title);
158
+ }
159
+ if (chunk.toolCalls) {
160
+ console.log("Tool calls:", chunk.toolCalls.length);
161
+ }
162
+ }
163
+ }
164
+ ```
165
+
166
+ **With Cancellation:**
167
+
168
+ ```typescript
169
+ const abortController = new AbortController();
170
+
171
+ // Cancel after 5 seconds
172
+ setTimeout(() => abortController.abort(), 5000);
173
+
174
+ try {
175
+ for await (const chunk of agent.respondStream({
176
+ history,
177
+ signal: abortController.signal,
178
+ })) {
179
+ console.log(chunk.delta);
180
+ }
181
+ } catch (error) {
182
+ if (error.name === "AbortError") {
183
+ console.log("Stream cancelled");
184
+ }
185
+ }
186
+ ```
187
+
188
+ **Collecting Full Response:**
189
+
190
+ ```typescript
191
+ let fullMessage = "";
192
+ let finalChunk;
193
+
194
+ for await (const chunk of agent.respondStream({ history })) {
195
+ fullMessage += chunk.delta;
196
+ if (chunk.done) {
197
+ finalChunk = chunk;
198
+ }
199
+ }
200
+
201
+ // Save to database
202
+ await db.agentMessages.create({
203
+ sessionId: session.id,
204
+ role: "agent",
205
+ content: fullMessage,
206
+ route: finalChunk.route?.title,
207
+ toolCalls: finalChunk.toolCalls || [],
208
+ });
209
+ ```
210
+
211
+ **See Also:** [streaming-agent.ts](../examples/streaming-agent.ts) for comprehensive examples.
212
+
112
213
  #### Properties
113
214
 
114
215
  ##### `name: string`
@@ -366,11 +467,15 @@ Returns filtered domains by names. If `allowedNames` is `undefined`, returns all
366
467
  const registry = new DomainRegistry();
367
468
 
368
469
  registry.register("payment", {
369
- processPayment: async (amount: number) => { /* ... */ },
470
+ processPayment: async (amount: number) => {
471
+ /* ... */
472
+ },
370
473
  });
371
474
 
372
475
  registry.register("shipping", {
373
- calculateShipping: (zipCode: string) => { /* ... */ },
476
+ calculateShipping: (zipCode: string) => {
477
+ /* ... */
478
+ },
374
479
  });
375
480
 
376
481
  // Get specific domains
@@ -386,6 +491,61 @@ Returns list of all registered domain names.
386
491
 
387
492
  ---
388
493
 
494
+ ### `AnthropicProvider`
495
+
496
+ AI provider implementation for Anthropic (Claude models).
497
+
498
+ #### Constructor
499
+
500
+ ```typescript
501
+ new AnthropicProvider(options: AnthropicProviderOptions)
502
+
503
+ interface AnthropicProviderOptions {
504
+ apiKey: string;
505
+ model: string; // Required: e.g., "claude-sonnet-4-5"
506
+ backupModels?: string[]; // Fallback models
507
+ config?: Partial<Omit<MessageCreateParamsNonStreaming, "model" | "messages" | "max_tokens">>;
508
+ retryConfig?: {
509
+ timeout?: number; // Default: 60000ms
510
+ retries?: number; // Default: 3
511
+ };
512
+ }
513
+ ```
514
+
515
+ **Note:** `model` is required for AnthropicProvider. Available models include:
516
+
517
+ - `claude-sonnet-4-5` - Latest Claude Sonnet 4.5 (most capable)
518
+ - `claude-opus-4-1` - Claude Opus 4.1 (powerful for complex tasks)
519
+ - `claude-sonnet-4-0` - Claude Sonnet 4.0 (stable, production-ready)
520
+
521
+ #### Methods
522
+
523
+ ##### `generateMessage<TContext>(input: GenerateMessageInput<TContext>): Promise<GenerateMessageOutput>`
524
+
525
+ Generates a message with retry logic and backup models using Anthropic's API.
526
+
527
+ **Example:**
528
+
529
+ ```typescript
530
+ import { AnthropicProvider } from "@falai/agent";
531
+
532
+ const provider = new AnthropicProvider({
533
+ apiKey: process.env.ANTHROPIC_API_KEY!,
534
+ model: "claude-sonnet-4-5",
535
+ backupModels: ["claude-opus-4-1", "claude-sonnet-4-0"],
536
+ config: {
537
+ temperature: 0.7,
538
+ top_p: 0.9,
539
+ },
540
+ retryConfig: {
541
+ timeout: 60000,
542
+ retries: 3,
543
+ },
544
+ });
545
+ ```
546
+
547
+ ---
548
+
389
549
  ### `GeminiProvider`
390
550
 
391
551
  AI provider implementation for Google Gemini.
@@ -414,6 +574,104 @@ Generates a message with retry logic and backup models.
414
574
 
415
575
  ---
416
576
 
577
+ ### `OpenAIProvider`
578
+
579
+ AI provider implementation for OpenAI (GPT models).
580
+
581
+ #### Constructor
582
+
583
+ ```typescript
584
+ new OpenAIProvider(options: OpenAIProviderOptions)
585
+
586
+ interface OpenAIProviderOptions {
587
+ apiKey: string;
588
+ model: string; // Required: e.g., "gpt-4o", "gpt-5"
589
+ backupModels?: string[]; // Fallback models
590
+ retryConfig?: {
591
+ timeout?: number; // Default: 60000ms
592
+ retries?: number; // Default: 3
593
+ };
594
+ }
595
+ ```
596
+
597
+ **Note:** Unlike GeminiProvider, `model` is required for OpenAIProvider. Choose from available OpenAI models like "gpt-4o", "gpt-5", "gpt-4-turbo", etc.
598
+
599
+ #### Methods
600
+
601
+ ##### `generateMessage<TContext>(input: GenerateMessageInput<TContext>): Promise<GenerateMessageOutput>`
602
+
603
+ Generates a message with retry logic and backup models using OpenAI's API.
604
+
605
+ **Example:**
606
+
607
+ ```typescript
608
+ import { OpenAIProvider } from "@falai/agent";
609
+
610
+ const provider = new OpenAIProvider({
611
+ apiKey: process.env.OPENAI_API_KEY!,
612
+ model: "gpt-5",
613
+ backupModels: ["gpt-4o", "gpt-4-turbo"],
614
+ retryConfig: {
615
+ timeout: 60000,
616
+ retries: 3,
617
+ },
618
+ });
619
+ ```
620
+
621
+ ---
622
+
623
+ ### `OpenRouterProvider`
624
+
625
+ AI provider implementation for OpenRouter (access to 200+ models).
626
+
627
+ #### Constructor
628
+
629
+ ```typescript
630
+ new OpenRouterProvider(options: OpenRouterProviderOptions)
631
+
632
+ interface OpenRouterProviderOptions {
633
+ apiKey: string;
634
+ model: string; // Required: e.g., "openai/gpt-5", "anthropic/claude-sonnet-4-5"
635
+ backupModels?: string[]; // Fallback models
636
+ siteUrl?: string; // Optional: your app URL for OpenRouter rankings
637
+ siteName?: string; // Optional: your app name for OpenRouter rankings
638
+ retryConfig?: {
639
+ timeout?: number; // Default: 60000ms
640
+ retries?: number; // Default: 3
641
+ };
642
+ }
643
+ ```
644
+
645
+ **Note:** OpenRouter provides access to models from multiple providers. Model names follow the format `provider/model-name` (e.g., "openai/gpt-5", "anthropic/claude-sonnet-4-5", "google/gemini-pro").
646
+
647
+ #### Methods
648
+
649
+ ##### `generateMessage<TContext>(input: GenerateMessageInput<TContext>): Promise<GenerateMessageOutput>`
650
+
651
+ Generates a message with retry logic and backup models using OpenRouter's API.
652
+
653
+ **Example:**
654
+
655
+ ```typescript
656
+ import { OpenRouterProvider } from "@falai/agent";
657
+
658
+ const provider = new OpenRouterProvider({
659
+ apiKey: process.env.OPENROUTER_API_KEY!,
660
+ model: "openai/gpt-5",
661
+ backupModels: ["anthropic/claude-sonnet-4-5", "google/gemini-pro"],
662
+ siteUrl: "https://yourapp.com",
663
+ siteName: "Your App Name",
664
+ retryConfig: {
665
+ timeout: 60000,
666
+ retries: 3,
667
+ },
668
+ });
669
+ ```
670
+
671
+ **See Also:** [Providers Guide](./PROVIDERS.md) for detailed provider comparison and configuration examples.
672
+
673
+ ---
674
+
417
675
  ### `PromptBuilder`
418
676
 
419
677
  Constructs prompts for AI generation.
@@ -0,0 +1,419 @@
1
+ # Persistence with @falai/agent
2
+
3
+ The `@falai/agent` framework provides optional, flexible persistence for automatically saving conversation sessions and messages to your database.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Optional** - Persistence is completely optional
8
+ - ✅ **Provider Pattern** - Simple API like AI providers (`new PrismaAdapter({ prisma })`)
9
+ - ✅ **Type-safe** - Full TypeScript support
10
+ - ✅ **Prisma Ready** - Built-in Prisma ORM adapter
11
+ - ✅ **Extensible** - Create adapters for any database
12
+ - ✅ **Auto-save** - Automatic message persistence
13
+
14
+ ## Quick Start with Prisma
15
+
16
+ ### 1. Install Dependencies
17
+
18
+ ```bash
19
+ npm install @prisma/client prisma
20
+ npx prisma init
21
+ ```
22
+
23
+ ### 2. Set Up Schema
24
+
25
+ Copy this schema to `prisma/schema.prisma`:
26
+
27
+ ```prisma
28
+ datasource db {
29
+ provider = "postgresql"
30
+ url = env("DATABASE_URL")
31
+ }
32
+
33
+ generator client {
34
+ provider = "prisma-client-js"
35
+ }
36
+
37
+ model AgentSession {
38
+ id String @id @default(cuid())
39
+ userId String? @map("user_id")
40
+ agentName String? @map("agent_name")
41
+ status String @default("active")
42
+ currentRoute String? @map("current_route")
43
+ currentState String? @map("current_state")
44
+ collectedData Json? @map("collected_data")
45
+ messageCount Int @default(0) @map("message_count")
46
+ lastMessageAt DateTime? @map("last_message_at")
47
+ completedAt DateTime? @map("completed_at")
48
+ createdAt DateTime @default(now()) @map("created_at")
49
+ updatedAt DateTime @updatedAt @map("updated_at")
50
+
51
+ messages AgentMessage[]
52
+
53
+ @@index([userId, status])
54
+ @@map("agent_sessions")
55
+ }
56
+
57
+ model AgentMessage {
58
+ id String @id @default(cuid())
59
+ sessionId String @map("session_id")
60
+ userId String? @map("user_id")
61
+ role String
62
+ content String @db.Text
63
+ route String?
64
+ state String?
65
+ toolCalls Json? @map("tool_calls")
66
+ event Json?
67
+ createdAt DateTime @default(now()) @map("created_at")
68
+
69
+ session AgentSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
70
+
71
+ @@index([sessionId])
72
+ @@map("agent_messages")
73
+ }
74
+ ```
75
+
76
+ ### 3. Generate and Migrate
77
+
78
+ ```bash
79
+ npx prisma generate
80
+ npx prisma migrate dev --name init
81
+ ```
82
+
83
+ ### 4. Use in Your Agent (That's it!)
84
+
85
+ ```typescript
86
+ import { Agent, PrismaAdapter, GeminiProvider } from "@falai/agent";
87
+ import { PrismaClient } from "@prisma/client";
88
+
89
+ const prisma = new PrismaClient();
90
+
91
+ const agent = new Agent({
92
+ name: "My Agent",
93
+ ai: new GeminiProvider({ apiKey: "..." }),
94
+ // ✨ Just add this!
95
+ persistence: {
96
+ adapter: new PrismaAdapter({ prisma }),
97
+ userId: "user_123",
98
+ },
99
+ });
100
+
101
+ // Access persistence methods
102
+ const persistence = agent.getPersistenceManager();
103
+
104
+ // Create a session
105
+ const session = await persistence.createSession({
106
+ userId: "user_123",
107
+ agentName: "My Agent",
108
+ });
109
+
110
+ // Load history
111
+ const history = await persistence.loadSessionHistory(session.id);
112
+
113
+ // Generate response
114
+ const response = await agent.respond({ history });
115
+
116
+ // Save message (auto-saved if configured)
117
+ await persistence.saveMessage({
118
+ sessionId: session.id,
119
+ role: "agent",
120
+ content: response.message,
121
+ });
122
+ ```
123
+
124
+ ## Configuration Options
125
+
126
+ ### Basic Configuration
127
+
128
+ ```typescript
129
+ new PrismaAdapter({
130
+ prisma: prismaClient, // Required: Your Prisma client
131
+ });
132
+ ```
133
+
134
+ ### Custom Table Names
135
+
136
+ ```typescript
137
+ new PrismaAdapter({
138
+ prisma,
139
+ tables: {
140
+ sessions: "myCustomSessions",
141
+ messages: "myCustomMessages",
142
+ },
143
+ });
144
+ ```
145
+
146
+ ### Custom Field Mappings
147
+
148
+ If your database uses different field names:
149
+
150
+ ```typescript
151
+ new PrismaAdapter({
152
+ prisma,
153
+ fieldMappings: {
154
+ sessions: {
155
+ userId: "user_id",
156
+ createdAt: "created_at",
157
+ updatedAt: "updated_at",
158
+ },
159
+ messages: {
160
+ sessionId: "session_id",
161
+ createdAt: "created_at",
162
+ },
163
+ },
164
+ });
165
+ ```
166
+
167
+ ## Auto-Save Messages
168
+
169
+ Enable automatic message persistence:
170
+
171
+ ```typescript
172
+ const agent = new Agent({
173
+ name: "My Agent",
174
+ ai: provider,
175
+ persistence: {
176
+ adapter: new PrismaAdapter({ prisma }),
177
+ autoSave: true, // Default: true
178
+ userId: "user_123",
179
+ },
180
+ });
181
+ ```
182
+
183
+ ## Using with Lifecycle Hooks
184
+
185
+ The most powerful pattern - auto-sync context with database:
186
+
187
+ ```typescript
188
+ const agent = new Agent({
189
+ name: "Smart Assistant",
190
+ ai: provider,
191
+ context: {
192
+ userId: "user_123",
193
+ sessionId: session.id,
194
+ preferences: { theme: "light" },
195
+ },
196
+ hooks: {
197
+ // Load fresh context before each response
198
+ beforeRespond: async (ctx) => {
199
+ const persistence = agent.getPersistenceManager();
200
+ const session = await persistence?.getSession(ctx.sessionId);
201
+ return {
202
+ ...ctx,
203
+ preferences: session?.collectedData?.preferences || ctx.preferences,
204
+ };
205
+ },
206
+ // Auto-save context updates
207
+ onContextUpdate: async (ctx) => {
208
+ const persistence = agent.getPersistenceManager();
209
+ await persistence?.updateCollectedData(ctx.sessionId, {
210
+ preferences: ctx.preferences,
211
+ });
212
+ },
213
+ },
214
+ persistence: {
215
+ adapter: new PrismaAdapter({ prisma }),
216
+ },
217
+ });
218
+
219
+ // Now context updates are automatically persisted!
220
+ await agent.updateContext({ preferences: { theme: "dark" } });
221
+ ```
222
+
223
+ ## PersistenceManager API
224
+
225
+ Access via `agent.getPersistenceManager()`:
226
+
227
+ ### Session Methods
228
+
229
+ ```typescript
230
+ // Create session
231
+ await persistence.createSession({
232
+ userId: "user_123",
233
+ agentName: "My Agent",
234
+ initialData: { key: "value" },
235
+ });
236
+
237
+ // Get session
238
+ await persistence.getSession(sessionId);
239
+
240
+ // Find active session for user
241
+ await persistence.findActiveSession(userId);
242
+
243
+ // Get all user sessions
244
+ await persistence.getUserSessions(userId);
245
+
246
+ // Update session
247
+ await persistence.updateSessionStatus(sessionId, "completed");
248
+ await persistence.updateCollectedData(sessionId, { key: "value" });
249
+ await persistence.updateRouteState(sessionId, routeId, stateId);
250
+
251
+ // Complete/abandon session
252
+ await persistence.completeSession(sessionId);
253
+ await persistence.abandonSession(sessionId);
254
+
255
+ // Delete session
256
+ await persistence.deleteSession(sessionId);
257
+ ```
258
+
259
+ ### Message Methods
260
+
261
+ ```typescript
262
+ // Save message
263
+ await persistence.saveMessage({
264
+ sessionId: session.id,
265
+ userId: "user_123",
266
+ role: "user" | "agent" | "system",
267
+ content: "Hello!",
268
+ route: "route_id",
269
+ state: "state_id",
270
+ toolCalls: [...],
271
+ });
272
+
273
+ // Get messages
274
+ await persistence.getSessionMessages(sessionId);
275
+ await persistence.getUserMessages(userId);
276
+
277
+ // Load as Event history
278
+ await persistence.loadSessionHistory(sessionId);
279
+ ```
280
+
281
+ ## Creating Custom Adapters
282
+
283
+ Create adapters for any database:
284
+
285
+ ```typescript
286
+ import { PersistenceAdapter, SessionRepository, MessageRepository } from "@falai/agent";
287
+
288
+ class MyDatabaseAdapter implements PersistenceAdapter {
289
+ public readonly sessionRepository: SessionRepository;
290
+ public readonly messageRepository: MessageRepository;
291
+
292
+ constructor(config: MyConfig) {
293
+ this.sessionRepository = new MySessionRepository(config);
294
+ this.messageRepository = new MyMessageRepository(config);
295
+ }
296
+
297
+ async initialize?(): Promise<void> {
298
+ // Optional: Setup tables/indexes
299
+ }
300
+
301
+ async disconnect?(): Promise<void> {
302
+ // Optional: Cleanup
303
+ }
304
+ }
305
+
306
+ // Use it
307
+ const agent = new Agent({
308
+ persistence: {
309
+ adapter: new MyDatabaseAdapter({ ... }),
310
+ },
311
+ });
312
+ ```
313
+
314
+ ## Examples
315
+
316
+ ### Complete Example
317
+
318
+ See `examples/prisma-persistence.ts` for a full working example.
319
+
320
+ ### Minimal Example
321
+
322
+ ```typescript
323
+ import { Agent, PrismaAdapter } from "@falai/agent";
324
+ import { PrismaClient } from "@prisma/client";
325
+
326
+ const prisma = new PrismaClient();
327
+ const agent = new Agent({
328
+ name: "Assistant",
329
+ ai: provider,
330
+ persistence: {
331
+ adapter: new PrismaAdapter({ prisma }),
332
+ },
333
+ });
334
+
335
+ const persistence = agent.getPersistenceManager();
336
+ const session = await persistence.createSession({ userId: "user_123" });
337
+ const history = await persistence.loadSessionHistory(session.id);
338
+ const response = await agent.respond({ history });
339
+ await persistence.saveMessage({
340
+ sessionId: session.id,
341
+ role: "agent",
342
+ content: response.message,
343
+ });
344
+ ```
345
+
346
+ ## Database Schema Details
347
+
348
+ ### SessionData Fields
349
+
350
+ - `id`: Unique session identifier
351
+ - `userId`: Optional user identifier
352
+ - `agentName`: Name of the agent
353
+ - `status`: `"active" | "completed" | "abandoned"`
354
+ - `currentRoute`: Current route ID
355
+ - `currentState`: Current state ID
356
+ - `collectedData`: JSON object for custom data
357
+ - `messageCount`: Number of messages in session
358
+ - `lastMessageAt`: Timestamp of last message
359
+ - `completedAt`: When session was completed
360
+ - `createdAt`: Session creation time
361
+ - `updatedAt`: Last update time
362
+
363
+ ### MessageData Fields
364
+
365
+ - `id`: Unique message identifier
366
+ - `sessionId`: Reference to session
367
+ - `userId`: Optional user identifier
368
+ - `role`: `"user" | "agent" | "system"`
369
+ - `content`: Message text
370
+ - `route`: Route ID when message was sent
371
+ - `state`: State ID when message was sent
372
+ - `toolCalls`: Array of tool calls (if any)
373
+ - `event`: Full event data (optional)
374
+ - `createdAt`: Message creation time
375
+
376
+ ## Best Practices
377
+
378
+ 1. ✅ Use lifecycle hooks for automatic context persistence
379
+ 2. ✅ Enable `autoSave` to track message counts automatically
380
+ 3. ✅ Store minimal data in `collectedData`
381
+ 4. ✅ Index frequently queried fields
382
+ 5. ✅ Use cascading deletes for cleanup
383
+ 6. ✅ Handle errors gracefully
384
+ 7. ✅ Complete or abandon sessions when done
385
+
386
+ ## Troubleshooting
387
+
388
+ ### "Cannot find module '@prisma/client'"
389
+
390
+ Install Prisma:
391
+
392
+ ```bash
393
+ npm install @prisma/client prisma
394
+ npx prisma generate
395
+ ```
396
+
397
+ ### "Table not found"
398
+
399
+ Run migrations:
400
+
401
+ ```bash
402
+ npx prisma migrate dev
403
+ ```
404
+
405
+ ### Custom schema not working
406
+
407
+ Check your field mappings match your actual database schema.
408
+
409
+ ## Other Databases
410
+
411
+ The adapter pattern works with any database. Examples:
412
+
413
+ - **MongoDB**: Create `MongoAdapter`
414
+ - **PostgreSQL (raw)**: Create `PostgresAdapter`
415
+ - **MySQL**: Create `MySQLAdapter`
416
+ - **Redis**: Create `RedisAdapter`
417
+ - **Elasticsearch**: Create `ElasticsearchAdapter`
418
+
419
+ Just implement the `PersistenceAdapter` interface!