@falai/agent 0.6.7 → 0.6.9
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 +162 -30
- package/dist/cjs/core/Agent.d.ts +18 -0
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +218 -15
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/Route.d.ts +12 -1
- package/dist/cjs/core/Route.d.ts.map +1 -1
- package/dist/cjs/core/Route.js +38 -1
- package/dist/cjs/core/Route.js.map +1 -1
- package/dist/cjs/core/RoutingEngine.d.ts.map +1 -1
- package/dist/cjs/core/RoutingEngine.js +3 -1
- package/dist/cjs/core/RoutingEngine.js.map +1 -1
- package/dist/cjs/core/State.js +1 -1
- package/dist/cjs/core/State.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/route.d.ts +52 -1
- package/dist/cjs/types/route.d.ts.map +1 -1
- package/dist/cjs/types/session.d.ts +17 -1
- package/dist/cjs/types/session.d.ts.map +1 -1
- package/dist/cjs/types/session.js.map +1 -1
- package/dist/core/Agent.d.ts +18 -0
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +219 -16
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/Route.d.ts +12 -1
- package/dist/core/Route.d.ts.map +1 -1
- package/dist/core/Route.js +38 -1
- package/dist/core/Route.js.map +1 -1
- package/dist/core/RoutingEngine.d.ts.map +1 -1
- package/dist/core/RoutingEngine.js +3 -1
- package/dist/core/RoutingEngine.js.map +1 -1
- package/dist/core/State.js +1 -1
- package/dist/core/State.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types/route.d.ts +52 -1
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/session.d.ts +17 -1
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js.map +1 -1
- package/docs/EXAMPLES.md +51 -2
- package/docs/ROUTES.md +345 -1
- package/docs/STATES.md +97 -7
- package/examples/business-onboarding.ts +10 -12
- package/examples/company-qna-agent.ts +4 -5
- package/examples/healthcare-agent.ts +63 -0
- package/examples/persistent-onboarding.ts +6 -8
- package/examples/route-transitions.ts +242 -0
- package/examples/travel-agent.ts +60 -0
- package/package.json +1 -1
- package/src/core/Agent.ts +338 -16
- package/src/core/Route.ts +53 -1
- package/src/core/RoutingEngine.ts +3 -1
- package/src/core/State.ts +1 -1
- package/src/index.ts +3 -1
- package/src/types/route.ts +58 -1
- package/src/types/session.ts +20 -2
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
# 🤖 @falai/agent
|
|
4
4
|
|
|
5
|
-
###
|
|
5
|
+
### Type-Safe AI Conversational Agents That Actually Work in Production
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**Schema-driven data extraction • Predictable conversations • Enterprise-ready**
|
|
8
8
|
|
|
9
9
|
[](https://www.typescriptlang.org/)
|
|
10
10
|
[](./LICENSE)
|
|
@@ -17,6 +17,46 @@
|
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
+
## ⚡ The @falai/agent Difference
|
|
21
|
+
|
|
22
|
+
### Traditional AI Chat:
|
|
23
|
+
```typescript
|
|
24
|
+
// User: "I want to book the Grand Hotel for 2 people"
|
|
25
|
+
// AI: "Sure! Which hotel would you like?" // 😠 Asked already!
|
|
26
|
+
// User: "Grand Hotel"
|
|
27
|
+
// AI: "How many guests?" // 😠 You just told me!
|
|
28
|
+
// User: "2 people"
|
|
29
|
+
// AI: "What date?" // Finally...
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### With @falai/agent:
|
|
33
|
+
```typescript
|
|
34
|
+
// User: "I want to book the Grand Hotel for 2 people"
|
|
35
|
+
// AI: "Sure! For what date would you like to book?" // ✅ Skips known info
|
|
36
|
+
// User: "Next Friday"
|
|
37
|
+
// AI: "Booking confirmed for 2 guests at Grand Hotel on Friday!" // ✅ All data extracted
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**No more repetitive questions. No more guessing what the AI will ask next.**
|
|
41
|
+
|
|
42
|
+
Schema-first extraction means the AI automatically captures what you've already said, and only asks for what's missing.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 🤔 Why @falai/agent?
|
|
47
|
+
|
|
48
|
+
After building production AI applications, we found existing solutions either:
|
|
49
|
+
|
|
50
|
+
- **Too unpredictable** - AI decides everything, including which tools to call (unreliable in production)
|
|
51
|
+
- **Too complex** - Heavy Python frameworks with massive dependencies
|
|
52
|
+
- **Too basic** - No structured data extraction or state management
|
|
53
|
+
|
|
54
|
+
@falai/agent gives you **predictable AI** - the creativity of LLMs with the reliability of code.
|
|
55
|
+
|
|
56
|
+
**The key insight:** Let AI do what it's good at (understanding intent, generating responses, extracting data), and let TypeScript handle the rest (state logic, tool execution, validation).
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
20
60
|
## 🌟 Features
|
|
21
61
|
|
|
22
62
|
<table>
|
|
@@ -51,6 +91,7 @@
|
|
|
51
91
|
- **Text-Based Conditions** - Human-readable transition conditions for the AI to evaluate
|
|
52
92
|
- **Code-Based Logic** - Deterministic state progression with `skipIf` and `requiredData`
|
|
53
93
|
- **Always-On Routing** - Context-aware routing respects user intent changes
|
|
94
|
+
- **Route Transitions** - Automatic transitions between routes with `onComplete` for seamless workflows
|
|
54
95
|
|
|
55
96
|
</td>
|
|
56
97
|
<td width="50%">
|
|
@@ -112,7 +153,50 @@ yarn add @falai/agent
|
|
|
112
153
|
|
|
113
154
|
## 🚀 Quick Start
|
|
114
155
|
|
|
115
|
-
|
|
156
|
+
### Level 1: Your First Agent (30 seconds)
|
|
157
|
+
|
|
158
|
+
Create a minimal conversational agent:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { Agent, GeminiProvider } from "@falai/agent";
|
|
162
|
+
|
|
163
|
+
// Create your agent
|
|
164
|
+
const agent = new Agent({
|
|
165
|
+
name: "Assistant",
|
|
166
|
+
description: "A helpful assistant",
|
|
167
|
+
ai: new GeminiProvider({
|
|
168
|
+
apiKey: process.env.GEMINI_API_KEY!,
|
|
169
|
+
model: "models/gemini-2.0-flash-exp",
|
|
170
|
+
}),
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Create a simple route
|
|
174
|
+
agent.createRoute({
|
|
175
|
+
title: "General Help",
|
|
176
|
+
description: "Answers user questions",
|
|
177
|
+
conditions: ["User needs help or asks a question"],
|
|
178
|
+
initialState: {
|
|
179
|
+
chatState: "Answer the user's question helpfully",
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Start chatting
|
|
184
|
+
const response = await agent.respond({
|
|
185
|
+
history: [
|
|
186
|
+
{ source: "customer", name: "Alice", content: "What can you do?" },
|
|
187
|
+
],
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
console.log(response.message);
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**That's it!** You now have a working conversational AI agent.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### Level 2: Data Extraction (The Real Power)
|
|
198
|
+
|
|
199
|
+
Now let's build an agent that intelligently gathers structured data:
|
|
116
200
|
|
|
117
201
|
```typescript
|
|
118
202
|
import {
|
|
@@ -168,6 +252,9 @@ const bookingRoute = agent.createRoute<HotelBookingData>({
|
|
|
168
252
|
},
|
|
169
253
|
required: ["hotelName", "date", "guests"],
|
|
170
254
|
},
|
|
255
|
+
endState: {
|
|
256
|
+
chatState: "Confirm the booking details warmly and thank the user",
|
|
257
|
+
},
|
|
171
258
|
});
|
|
172
259
|
|
|
173
260
|
// 5️⃣ Build the flow to gather data step-by-step
|
|
@@ -243,7 +330,7 @@ for await (const chunk of agent.respondStream({ history })) {
|
|
|
243
330
|
```typescript
|
|
244
331
|
let session = createSession<MyData>();
|
|
245
332
|
const response = await agent.respond({ history, session });
|
|
246
|
-
session = response.session!; // Tracks progress across turns
|
|
333
|
+
session = response.session!; // Tracks progress across turns, you can use it to save the current state in your database
|
|
247
334
|
```
|
|
248
335
|
|
|
249
336
|
**Database persistence** with any adapter:
|
|
@@ -282,45 +369,78 @@ const agent = new Agent({
|
|
|
282
369
|
|
|
283
370
|
---
|
|
284
371
|
|
|
285
|
-
## 🎯 Examples
|
|
372
|
+
## 🎯 Examples - Pick Your Use Case
|
|
286
373
|
|
|
287
|
-
|
|
374
|
+
### 🤖 Conversational Flows
|
|
375
|
+
Build intelligent data-gathering conversations:
|
|
288
376
|
|
|
289
|
-
- 🏢 **[Business Onboarding](./examples/business-onboarding.ts)** -
|
|
290
|
-
- ✈️ **[Travel Agent](./examples/travel-agent.ts)** -
|
|
291
|
-
- 🏥 **[Healthcare Assistant](./examples/healthcare-agent.ts)** - Appointment scheduling & lab
|
|
292
|
-
|
|
293
|
-
|
|
377
|
+
- 🏢 **[Business Onboarding](./examples/business-onboarding.ts)** - Multi-step company setup with conditional branching
|
|
378
|
+
- ✈️ **[Travel Agent](./examples/travel-agent.ts)** - Flight & hotel booking with session state
|
|
379
|
+
- 🏥 **[Healthcare Assistant](./examples/healthcare-agent.ts)** - Appointment scheduling & lab result delivery
|
|
380
|
+
|
|
381
|
+
### 🏢 Production Patterns
|
|
382
|
+
Enterprise-ready features:
|
|
294
383
|
|
|
295
|
-
**Persistence
|
|
384
|
+
- 💾 **[Prisma Persistence](./examples/prisma-persistence.ts)** - Auto-save sessions with Prisma ORM
|
|
385
|
+
- ⚡ **[Redis Persistence](./examples/redis-persistence.ts)** - High-performance in-memory sessions
|
|
386
|
+
- 🔐 **[Domain Scoping](./examples/domain-scoping.ts)** - Tool security & access control
|
|
296
387
|
|
|
297
|
-
|
|
298
|
-
-
|
|
299
|
-
|
|
388
|
+
### ⚡ Advanced Techniques
|
|
389
|
+
Power-user features:
|
|
390
|
+
|
|
391
|
+
- 📋 **[Declarative Agent](./examples/declarative-agent.ts)** - Full constructor-based configuration
|
|
392
|
+
- ⚡ **[Streaming Responses](./examples/streaming-agent.ts)** - Real-time response streaming
|
|
300
393
|
- 📜 **[Rules & Prohibitions](./examples/rules-prohibitions.ts)** - Fine-grained behavior control
|
|
301
394
|
|
|
302
|
-
📖 **[See all examples with
|
|
395
|
+
📖 **[See all examples with detailed explanations →](./docs/EXAMPLES.md)**
|
|
303
396
|
|
|
304
397
|
---
|
|
305
398
|
|
|
306
399
|
## 🏗️ How It Works
|
|
307
400
|
|
|
308
|
-
`@falai/agent` uses a **state machine-driven architecture
|
|
401
|
+
`@falai/agent` uses a **schema-first, state machine-driven architecture**:
|
|
309
402
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
403
|
+
```
|
|
404
|
+
User Message
|
|
405
|
+
↓
|
|
406
|
+
┌─────────────────────────────────────────┐
|
|
407
|
+
│ 1. PREPARATION (Tools) │
|
|
408
|
+
│ • Execute tools for current state │
|
|
409
|
+
│ • Update context with results │
|
|
410
|
+
│ • Enrich extracted data │
|
|
411
|
+
└─────────────────────────────────────────┘
|
|
412
|
+
↓
|
|
413
|
+
┌─────────────────────────────────────────┐
|
|
414
|
+
│ 2. ROUTING (AI-Driven) │
|
|
415
|
+
│ • Evaluate all routes │
|
|
416
|
+
│ • Consider session context │
|
|
417
|
+
│ • Select best route (0-100 score) │
|
|
418
|
+
└─────────────────────────────────────────┘
|
|
419
|
+
↓
|
|
420
|
+
┌─────────────────────────────────────────┐
|
|
421
|
+
│ 3. STATE SELECTION (Code + AI) │
|
|
422
|
+
│ • Filter states with skipIf (code) │
|
|
423
|
+
│ • AI picks best from valid states │
|
|
424
|
+
│ • Update session state │
|
|
425
|
+
└─────────────────────────────────────────┘
|
|
426
|
+
↓
|
|
427
|
+
┌─────────────────────────────────────────┐
|
|
428
|
+
│ 4. RESPONSE (AI + Schema) │
|
|
429
|
+
│ • Extract data via JSON Schema │
|
|
430
|
+
│ • Generate natural message │
|
|
431
|
+
│ • Update session with new data │
|
|
432
|
+
└─────────────────────────────────────────┘
|
|
433
|
+
↓
|
|
434
|
+
Response with Structured Data
|
|
435
|
+
```
|
|
315
436
|
|
|
316
|
-
|
|
437
|
+
### Key Principles:
|
|
317
438
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
- Always-on routing lets users change direction mid-conversation
|
|
439
|
+
✅ **AI decides:** Route selection, state selection (from valid options), message generation, data extraction
|
|
440
|
+
✅ **Code decides:** Tool execution, state filtering (`skipIf`), data validation, flow control
|
|
441
|
+
✅ **Result:** Predictable, testable agents with natural conversations
|
|
322
442
|
|
|
323
|
-
This
|
|
443
|
+
**This architecture delivers 1-2 LLM calls per turn** (vs 3-5 in traditional approaches) while maintaining complete type safety.
|
|
324
444
|
|
|
325
445
|
📖 **[Read the full architecture guide →](./docs/ARCHITECTURE.md)**
|
|
326
446
|
|
|
@@ -349,10 +469,22 @@ MIT © 2025
|
|
|
349
469
|
|
|
350
470
|
<div align="center">
|
|
351
471
|
|
|
352
|
-
|
|
472
|
+
## 🚀 Ready to Build?
|
|
473
|
+
|
|
474
|
+
**Choose your path:**
|
|
475
|
+
|
|
476
|
+
👶 **New to AI agents?** → [5-minute tutorial](./docs/GETTING_STARTED.md)
|
|
477
|
+
🏗️ **Building production app?** → [Architecture guide](./docs/ARCHITECTURE.md)
|
|
478
|
+
💡 **Have questions?** → [Open a discussion](https://github.com/falai-dev/agent/discussions)
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
### ⭐ Star us on [GitHub](https://github.com/falai-dev/agent)
|
|
483
|
+
|
|
484
|
+
**Help us reach more developers building production AI!**
|
|
353
485
|
|
|
354
486
|
[Report Bug](https://github.com/falai-dev/agent/issues) • [Request Feature](https://github.com/falai-dev/agent/issues) • [Contribute](https://github.com/falai-dev/agent/pulls)
|
|
355
487
|
|
|
356
|
-
|
|
488
|
+
**Made with ❤️ for the community**
|
|
357
489
|
|
|
358
490
|
</div>
|
package/dist/cjs/core/Agent.d.ts
CHANGED
|
@@ -175,5 +175,23 @@ export declare class Agent<TContext = unknown> {
|
|
|
175
175
|
* @returns The extracted data from the current session
|
|
176
176
|
*/
|
|
177
177
|
getExtractedData<TExtracted = unknown>(routeId?: string): Partial<TExtracted>;
|
|
178
|
+
/**
|
|
179
|
+
* Manually transition to a different route
|
|
180
|
+
* Sets a pending transition that will be executed on the next respond() call
|
|
181
|
+
*
|
|
182
|
+
* @param routeIdOrTitle - Route ID or title to transition to
|
|
183
|
+
* @param session - Session state to update (uses current session if not provided)
|
|
184
|
+
* @param condition - Optional AI-evaluated condition for the transition
|
|
185
|
+
* @returns Updated session with pending transition
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* // After route completes
|
|
189
|
+
* if (response.isRouteComplete && response.session) {
|
|
190
|
+
* const updatedSession = agent.transitionToRoute("feedback-collection", response.session);
|
|
191
|
+
* // Next respond() call will automatically transition to feedback route
|
|
192
|
+
* const nextResponse = await agent.respond({ history, session: updatedSession });
|
|
193
|
+
* }
|
|
194
|
+
*/
|
|
195
|
+
transitionToRoute(routeIdOrTitle: string, session?: SessionState, condition?: string): SessionState;
|
|
178
196
|
}
|
|
179
197
|
//# sourceMappingURL=Agent.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Agent.d.ts","sourceRoot":"","sources":["../../../src/core/Agent.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAMrD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAO1D;;GAEG;AACH,qBAAa,KAAK,CAAC,QAAQ,GAAG,OAAO;IAkBvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAjBpC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,YAAY,CAAoB;IAExC,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,kBAAkB,CAAiC;IAC3D,OAAO,CAAC,aAAa,CAA0B;IAC/C,OAAO,CAAC,cAAc,CAA2B;IACjD,OAAO,CAAC,cAAc,CAAC,CAAe;IAEtC;;OAEG;IACH,SAAgB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAM;gBAExC,OAAO,EAAE,YAAY,CAAC,QAAQ,CAAC;IAkE5D;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,GAAG,SAAS,CAEpC;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;IAED;;;OAGG;IACH,WAAW,CAAC,UAAU,GAAG,OAAO,EAC9B,OAAO,EAAE,YAAY,CAAC,UAAU,CAAC,GAChC,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC;IAM9B;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAK5B;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAU3C;;OAEG;IACH,gBAAgB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI;IAS9C;;;OAGG;IACH,SAAS,CAAC,KAAK,SAAS,MAAM,EAAE,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrE,IAAI,EAAE,KAAK,EACX,YAAY,EAAE,OAAO,GACpB,IAAI;IAuBP;;;OAGG;IACG,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAe9D;;;;OAIG;YACW,eAAe;
|
|
1
|
+
{"version":3,"file":"Agent.d.ts","sourceRoot":"","sources":["../../../src/core/Agent.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/core/Agent.js
CHANGED
|
@@ -8,6 +8,7 @@ const session_1 = require("../types/session");
|
|
|
8
8
|
const PromptComposer_1 = require("./PromptComposer");
|
|
9
9
|
const logger_1 = require("../utils/logger");
|
|
10
10
|
const Route_1 = require("./Route");
|
|
11
|
+
const State_1 = require("./State");
|
|
11
12
|
const DomainRegistry_1 = require("./DomainRegistry");
|
|
12
13
|
const PersistenceManager_1 = require("./PersistenceManager");
|
|
13
14
|
const RoutingEngine_1 = require("./RoutingEngine");
|
|
@@ -190,8 +191,7 @@ class Agent {
|
|
|
190
191
|
};
|
|
191
192
|
// Trigger lifecycle hook if configured
|
|
192
193
|
if (this.options.hooks?.onExtractedUpdate) {
|
|
193
|
-
|
|
194
|
-
newExtracted = updatedExtracted;
|
|
194
|
+
newExtracted = (await this.options.hooks.onExtractedUpdate(newExtracted, previousExtracted));
|
|
195
195
|
}
|
|
196
196
|
// Return updated session
|
|
197
197
|
return (0, session_1.mergeExtracted)(session, newExtracted);
|
|
@@ -260,7 +260,34 @@ class Agent {
|
|
|
260
260
|
let responseDirectives;
|
|
261
261
|
let selectedState;
|
|
262
262
|
let isRouteComplete = false;
|
|
263
|
-
|
|
263
|
+
// Check for pending transition from previous route completion
|
|
264
|
+
if (session.pendingTransition) {
|
|
265
|
+
const targetRoute = this.routes.find((r) => r.id === session.pendingTransition?.targetRouteId);
|
|
266
|
+
if (targetRoute) {
|
|
267
|
+
logger_1.logger.debug(`[Agent] Auto-transitioning from pending transition to route: ${targetRoute.title}`);
|
|
268
|
+
// Clear pending transition and enter new route
|
|
269
|
+
session = {
|
|
270
|
+
...session,
|
|
271
|
+
pendingTransition: undefined,
|
|
272
|
+
};
|
|
273
|
+
session = (0, session_1.enterRoute)(session, targetRoute.id, targetRoute.title);
|
|
274
|
+
// Merge initial data if available
|
|
275
|
+
if (targetRoute.initialData) {
|
|
276
|
+
session = (0, session_1.mergeExtracted)(session, targetRoute.initialData);
|
|
277
|
+
}
|
|
278
|
+
selectedRoute = targetRoute;
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
logger_1.logger.warn(`[Agent] Pending transition target route not found: ${session.pendingTransition.targetRouteId}`);
|
|
282
|
+
// Clear invalid transition
|
|
283
|
+
session = {
|
|
284
|
+
...session,
|
|
285
|
+
pendingTransition: undefined,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// If no pending transition or transition handled, do normal routing
|
|
290
|
+
if (this.routes.length > 0 && !selectedRoute) {
|
|
264
291
|
const orchestration = await this.routingEngine.decideRouteAndState({
|
|
265
292
|
routes: this.routes,
|
|
266
293
|
session,
|
|
@@ -381,17 +408,74 @@ class Agent {
|
|
|
381
408
|
}
|
|
382
409
|
}
|
|
383
410
|
else if (isRouteComplete && selectedRoute) {
|
|
384
|
-
// Route is complete -
|
|
411
|
+
// Route is complete - generate completion message then check for onComplete transition
|
|
412
|
+
const lastUserMessage = (0, event_1.getLastMessageFromHistory)(history);
|
|
413
|
+
// Get endState spec from route
|
|
414
|
+
const endStateSpec = selectedRoute.endStateSpec;
|
|
415
|
+
// Create a temporary state for completion message generation using endState configuration
|
|
416
|
+
const completionState = new State_1.State(selectedRoute.id, endStateSpec.chatState || "Summarize what was accomplished and confirm completion", endStateSpec.id || constants_1.END_STATE_ID, endStateSpec.gather, undefined, endStateSpec.requiredData, endStateSpec.chatState || "Summarize what was accomplished and confirm completion based on the conversation history and collected data");
|
|
417
|
+
// Build response schema for completion
|
|
418
|
+
const responseSchema = this.responseEngine.responseSchemaForRoute(selectedRoute, completionState);
|
|
419
|
+
// Build completion response prompt
|
|
420
|
+
const completionPrompt = this.responseEngine.buildResponsePrompt(selectedRoute, completionState, selectedRoute.getRules(), selectedRoute.getProhibitions(), undefined, // No directives for completion
|
|
421
|
+
history, lastUserMessage, {
|
|
422
|
+
name: this.options.name,
|
|
423
|
+
goal: this.options.goal,
|
|
424
|
+
description: this.options.description,
|
|
425
|
+
personality: this.options.personality,
|
|
426
|
+
});
|
|
427
|
+
// Stream completion message using AI provider
|
|
428
|
+
const stream = this.options.ai.generateMessageStream({
|
|
429
|
+
prompt: completionPrompt,
|
|
430
|
+
history,
|
|
431
|
+
context: effectiveContext,
|
|
432
|
+
signal,
|
|
433
|
+
parameters: {
|
|
434
|
+
jsonSchema: responseSchema,
|
|
435
|
+
schemaName: "completion_message_stream",
|
|
436
|
+
},
|
|
437
|
+
});
|
|
438
|
+
logger_1.logger.debug(`[Agent] Streaming completion message for route: ${selectedRoute.title}`);
|
|
439
|
+
// Check for onComplete transition
|
|
440
|
+
const transitionConfig = await selectedRoute.evaluateOnComplete({ extracted: session.extracted }, effectiveContext);
|
|
441
|
+
if (transitionConfig) {
|
|
442
|
+
// Find target route by ID or title
|
|
443
|
+
const targetRoute = this.routes.find((r) => r.id === transitionConfig.transitionTo ||
|
|
444
|
+
r.title === transitionConfig.transitionTo);
|
|
445
|
+
if (targetRoute) {
|
|
446
|
+
// Set pending transition in session
|
|
447
|
+
session = {
|
|
448
|
+
...session,
|
|
449
|
+
pendingTransition: {
|
|
450
|
+
targetRouteId: targetRoute.id,
|
|
451
|
+
condition: transitionConfig.condition,
|
|
452
|
+
reason: "route_complete",
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
logger_1.logger.debug(`[Agent] Route ${selectedRoute.title} completed with pending transition to: ${targetRoute.title}`);
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
logger_1.logger.warn(`[Agent] Route ${selectedRoute.title} completed but target route not found: ${transitionConfig.transitionTo}`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
// Set state to END_STATE marker
|
|
385
462
|
session = (0, session_1.enterState)(session, constants_1.END_STATE_ID, "Route completed");
|
|
386
463
|
logger_1.logger.debug(`[Agent] Route ${selectedRoute.title} completed. Entered END_STATE state.`);
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
done
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
464
|
+
// Stream completion chunks
|
|
465
|
+
for await (const chunk of stream) {
|
|
466
|
+
// Update current session if we have one
|
|
467
|
+
if (chunk.done && this.currentSession) {
|
|
468
|
+
this.currentSession = session;
|
|
469
|
+
}
|
|
470
|
+
yield {
|
|
471
|
+
delta: chunk.delta,
|
|
472
|
+
accumulated: chunk.accumulated,
|
|
473
|
+
done: chunk.done,
|
|
474
|
+
session,
|
|
475
|
+
toolCalls: undefined,
|
|
476
|
+
isRouteComplete: true,
|
|
477
|
+
};
|
|
478
|
+
}
|
|
395
479
|
}
|
|
396
480
|
else {
|
|
397
481
|
// Fallback: No routes defined, stream a simple response
|
|
@@ -492,7 +576,34 @@ class Agent {
|
|
|
492
576
|
let responseDirectives;
|
|
493
577
|
let selectedState;
|
|
494
578
|
let isRouteComplete = false;
|
|
495
|
-
|
|
579
|
+
// Check for pending transition from previous route completion
|
|
580
|
+
if (session.pendingTransition) {
|
|
581
|
+
const targetRoute = this.routes.find((r) => r.id === session.pendingTransition?.targetRouteId);
|
|
582
|
+
if (targetRoute) {
|
|
583
|
+
logger_1.logger.debug(`[Agent] Auto-transitioning from pending transition to route: ${targetRoute.title}`);
|
|
584
|
+
// Clear pending transition and enter new route
|
|
585
|
+
session = {
|
|
586
|
+
...session,
|
|
587
|
+
pendingTransition: undefined,
|
|
588
|
+
};
|
|
589
|
+
session = (0, session_1.enterRoute)(session, targetRoute.id, targetRoute.title);
|
|
590
|
+
// Merge initial data if available
|
|
591
|
+
if (targetRoute.initialData) {
|
|
592
|
+
session = (0, session_1.mergeExtracted)(session, targetRoute.initialData);
|
|
593
|
+
}
|
|
594
|
+
selectedRoute = targetRoute;
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
logger_1.logger.warn(`[Agent] Pending transition target route not found: ${session.pendingTransition.targetRouteId}`);
|
|
598
|
+
// Clear invalid transition
|
|
599
|
+
session = {
|
|
600
|
+
...session,
|
|
601
|
+
pendingTransition: undefined,
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
// If no pending transition or transition handled, do normal routing
|
|
606
|
+
if (this.routes.length > 0 && !selectedRoute) {
|
|
496
607
|
const orchestration = await this.routingEngine.decideRouteAndState({
|
|
497
608
|
routes: this.routes,
|
|
498
609
|
session,
|
|
@@ -591,9 +702,59 @@ class Agent {
|
|
|
591
702
|
}
|
|
592
703
|
}
|
|
593
704
|
else if (isRouteComplete && selectedRoute) {
|
|
594
|
-
// Route is complete -
|
|
705
|
+
// Route is complete - generate completion message then check for onComplete transition
|
|
706
|
+
const lastUserMessage = (0, event_1.getLastMessageFromHistory)(history);
|
|
707
|
+
// Get endState spec from route
|
|
708
|
+
const endStateSpec = selectedRoute.endStateSpec;
|
|
709
|
+
// Create a temporary state for completion message generation using endState configuration
|
|
710
|
+
const completionState = new State_1.State(selectedRoute.id, endStateSpec.chatState || "Summarize what was accomplished and confirm completion", endStateSpec.id || constants_1.END_STATE_ID, endStateSpec.gather, undefined, endStateSpec.requiredData, endStateSpec.chatState || "Summarize what was accomplished and confirm completion based on the conversation history and collected data");
|
|
711
|
+
// Build response schema for completion
|
|
712
|
+
const responseSchema = this.responseEngine.responseSchemaForRoute(selectedRoute, completionState);
|
|
713
|
+
// Build completion response prompt
|
|
714
|
+
const completionPrompt = this.responseEngine.buildResponsePrompt(selectedRoute, completionState, selectedRoute.getRules(), selectedRoute.getProhibitions(), undefined, // No directives for completion
|
|
715
|
+
history, lastUserMessage, {
|
|
716
|
+
name: this.options.name,
|
|
717
|
+
goal: this.options.goal,
|
|
718
|
+
description: this.options.description,
|
|
719
|
+
personality: this.options.personality,
|
|
720
|
+
});
|
|
721
|
+
// Generate completion message using AI provider
|
|
722
|
+
const completionResult = await this.options.ai.generateMessage({
|
|
723
|
+
prompt: completionPrompt,
|
|
724
|
+
history,
|
|
725
|
+
context: effectiveContext,
|
|
726
|
+
signal,
|
|
727
|
+
parameters: {
|
|
728
|
+
jsonSchema: responseSchema,
|
|
729
|
+
schemaName: "completion_message",
|
|
730
|
+
},
|
|
731
|
+
});
|
|
732
|
+
message = completionResult.structured?.message || completionResult.message;
|
|
733
|
+
logger_1.logger.debug(`[Agent] Generated completion message for route: ${selectedRoute.title}`);
|
|
734
|
+
// Check for onComplete transition
|
|
735
|
+
const transitionConfig = await selectedRoute.evaluateOnComplete({ extracted: session.extracted }, effectiveContext);
|
|
736
|
+
if (transitionConfig) {
|
|
737
|
+
// Find target route by ID or title
|
|
738
|
+
const targetRoute = this.routes.find((r) => r.id === transitionConfig.transitionTo ||
|
|
739
|
+
r.title === transitionConfig.transitionTo);
|
|
740
|
+
if (targetRoute) {
|
|
741
|
+
// Set pending transition in session
|
|
742
|
+
session = {
|
|
743
|
+
...session,
|
|
744
|
+
pendingTransition: {
|
|
745
|
+
targetRouteId: targetRoute.id,
|
|
746
|
+
condition: transitionConfig.condition,
|
|
747
|
+
reason: "route_complete",
|
|
748
|
+
},
|
|
749
|
+
};
|
|
750
|
+
logger_1.logger.debug(`[Agent] Route ${selectedRoute.title} completed with pending transition to: ${targetRoute.title}`);
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
logger_1.logger.warn(`[Agent] Route ${selectedRoute.title} completed but target route not found: ${transitionConfig.transitionTo}`);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
// Set state to END_STATE marker
|
|
595
757
|
session = (0, session_1.enterState)(session, constants_1.END_STATE_ID, "Route completed");
|
|
596
|
-
message = "";
|
|
597
758
|
logger_1.logger.debug(`[Agent] Route ${selectedRoute.title} completed. Entered END_STATE state.`);
|
|
598
759
|
}
|
|
599
760
|
else {
|
|
@@ -750,6 +911,48 @@ class Agent {
|
|
|
750
911
|
}
|
|
751
912
|
return this.currentSession.extracted || {};
|
|
752
913
|
}
|
|
914
|
+
/**
|
|
915
|
+
* Manually transition to a different route
|
|
916
|
+
* Sets a pending transition that will be executed on the next respond() call
|
|
917
|
+
*
|
|
918
|
+
* @param routeIdOrTitle - Route ID or title to transition to
|
|
919
|
+
* @param session - Session state to update (uses current session if not provided)
|
|
920
|
+
* @param condition - Optional AI-evaluated condition for the transition
|
|
921
|
+
* @returns Updated session with pending transition
|
|
922
|
+
*
|
|
923
|
+
* @example
|
|
924
|
+
* // After route completes
|
|
925
|
+
* if (response.isRouteComplete && response.session) {
|
|
926
|
+
* const updatedSession = agent.transitionToRoute("feedback-collection", response.session);
|
|
927
|
+
* // Next respond() call will automatically transition to feedback route
|
|
928
|
+
* const nextResponse = await agent.respond({ history, session: updatedSession });
|
|
929
|
+
* }
|
|
930
|
+
*/
|
|
931
|
+
transitionToRoute(routeIdOrTitle, session, condition) {
|
|
932
|
+
const targetSession = session || this.currentSession;
|
|
933
|
+
if (!targetSession) {
|
|
934
|
+
throw new Error("No session provided and no current session available. Please provide a session to transition.");
|
|
935
|
+
}
|
|
936
|
+
// Find target route by ID or title
|
|
937
|
+
const targetRoute = this.routes.find((r) => r.id === routeIdOrTitle || r.title === routeIdOrTitle);
|
|
938
|
+
if (!targetRoute) {
|
|
939
|
+
throw new Error(`Route not found: ${routeIdOrTitle}. Available routes: ${this.routes.map((r) => r.title).join(", ")}`);
|
|
940
|
+
}
|
|
941
|
+
const updatedSession = {
|
|
942
|
+
...targetSession,
|
|
943
|
+
pendingTransition: {
|
|
944
|
+
targetRouteId: targetRoute.id,
|
|
945
|
+
condition,
|
|
946
|
+
reason: "manual",
|
|
947
|
+
},
|
|
948
|
+
};
|
|
949
|
+
// Update current session if using it
|
|
950
|
+
if (!session && this.currentSession) {
|
|
951
|
+
this.currentSession = updatedSession;
|
|
952
|
+
}
|
|
953
|
+
logger_1.logger.debug(`[Agent] Set pending manual transition to route: ${targetRoute.title}`);
|
|
954
|
+
return updatedSession;
|
|
955
|
+
}
|
|
753
956
|
}
|
|
754
957
|
exports.Agent = Agent;
|
|
755
958
|
//# sourceMappingURL=Agent.js.map
|