@falai/agent 0.6.3 → 0.6.5
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 +89 -29
- package/dist/cjs/constants/index.d.ts +6 -1
- package/dist/cjs/constants/index.d.ts.map +1 -1
- package/dist/cjs/constants/index.js +8 -3
- package/dist/cjs/constants/index.js.map +1 -1
- package/dist/cjs/core/Agent.d.ts +22 -0
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +113 -26
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/Events.d.ts +13 -0
- package/dist/cjs/core/Events.d.ts.map +1 -1
- package/dist/cjs/core/Events.js +28 -14
- package/dist/cjs/core/Events.js.map +1 -1
- package/dist/cjs/core/ResponseEngine.d.ts +1 -1
- package/dist/cjs/core/ResponseEngine.d.ts.map +1 -1
- package/dist/cjs/core/ResponseEngine.js +4 -1
- package/dist/cjs/core/ResponseEngine.js.map +1 -1
- package/dist/cjs/core/Route.d.ts.map +1 -1
- package/dist/cjs/core/Route.js +4 -4
- package/dist/cjs/core/Route.js.map +1 -1
- package/dist/cjs/core/RoutingEngine.d.ts +6 -1
- package/dist/cjs/core/RoutingEngine.d.ts.map +1 -1
- package/dist/cjs/core/RoutingEngine.js +116 -41
- package/dist/cjs/core/RoutingEngine.js.map +1 -1
- package/dist/cjs/core/State.d.ts +18 -6
- package/dist/cjs/core/State.d.ts.map +1 -1
- package/dist/cjs/core/State.js +30 -7
- package/dist/cjs/core/State.js.map +1 -1
- package/dist/cjs/core/Tool.d.ts +8 -1
- package/dist/cjs/core/Tool.d.ts.map +1 -1
- package/dist/cjs/core/Tool.js +25 -28
- package/dist/cjs/core/Tool.js.map +1 -1
- package/dist/cjs/core/Transition.js +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +5 -0
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/agent.js.map +1 -1
- package/dist/cjs/types/route.d.ts +7 -1
- package/dist/cjs/types/route.d.ts.map +1 -1
- package/dist/cjs/types/session.d.ts +13 -2
- package/dist/cjs/types/session.d.ts.map +1 -1
- package/dist/cjs/types/session.js +28 -5
- package/dist/cjs/types/session.js.map +1 -1
- package/dist/cjs/utils/logger.d.ts +10 -0
- package/dist/cjs/utils/logger.d.ts.map +1 -0
- package/dist/cjs/utils/logger.js +23 -0
- package/dist/cjs/utils/logger.js.map +1 -0
- package/dist/constants/index.d.ts +6 -1
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/constants/index.js +6 -1
- package/dist/constants/index.js.map +1 -1
- package/dist/core/Agent.d.ts +22 -0
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +113 -26
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/Events.d.ts +13 -0
- package/dist/core/Events.d.ts.map +1 -1
- package/dist/core/Events.js +28 -14
- package/dist/core/Events.js.map +1 -1
- package/dist/core/ResponseEngine.d.ts +1 -1
- package/dist/core/ResponseEngine.d.ts.map +1 -1
- package/dist/core/ResponseEngine.js +4 -1
- package/dist/core/ResponseEngine.js.map +1 -1
- package/dist/core/Route.d.ts.map +1 -1
- package/dist/core/Route.js +4 -4
- package/dist/core/Route.js.map +1 -1
- package/dist/core/RoutingEngine.d.ts +6 -1
- package/dist/core/RoutingEngine.d.ts.map +1 -1
- package/dist/core/RoutingEngine.js +116 -41
- package/dist/core/RoutingEngine.js.map +1 -1
- package/dist/core/State.d.ts +18 -6
- package/dist/core/State.d.ts.map +1 -1
- package/dist/core/State.js +31 -8
- package/dist/core/State.js.map +1 -1
- package/dist/core/Tool.d.ts +8 -1
- package/dist/core/Tool.d.ts.map +1 -1
- package/dist/core/Tool.js +25 -28
- package/dist/core/Tool.js.map +1 -1
- package/dist/core/Transition.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types/agent.d.ts +5 -0
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/agent.js.map +1 -1
- package/dist/types/route.d.ts +7 -1
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/session.d.ts +13 -2
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js +28 -5
- package/dist/types/session.js.map +1 -1
- package/dist/utils/logger.d.ts +10 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +17 -0
- package/dist/utils/logger.js.map +1 -0
- package/docs/{CONSTRUCTOR_OPTIONS.md → AGENT.md} +79 -7
- package/docs/API_REFERENCE.md +309 -18
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/DOCS.md +46 -22
- package/docs/GETTING_STARTED.md +1 -1
- package/docs/README.md +13 -5
- package/docs/ROUTES.md +743 -0
- package/docs/STATES.md +798 -0
- package/examples/business-onboarding.ts +46 -5
- package/examples/company-qna-agent.ts +107 -1
- package/examples/custom-database-persistence.ts +44 -1
- package/examples/declarative-agent.ts +80 -37
- package/examples/domain-scoping.ts +91 -21
- package/examples/extracted-data-modification.ts +64 -2
- package/examples/healthcare-agent.ts +61 -4
- package/examples/openai-agent.ts +24 -2
- package/examples/opensearch-persistence.ts +26 -1
- package/examples/persistent-onboarding.ts +84 -18
- package/examples/prisma-persistence.ts +90 -16
- package/examples/redis-persistence.ts +89 -17
- package/examples/rules-prohibitions.ts +300 -139
- package/examples/streaming-agent.ts +60 -0
- package/examples/travel-agent.ts +66 -24
- package/package.json +3 -2
- package/src/constants/index.ts +6 -1
- package/src/core/Agent.ts +140 -24
- package/src/core/Events.ts +73 -10
- package/src/core/ResponseEngine.ts +6 -0
- package/src/core/Route.ts +8 -4
- package/src/core/RoutingEngine.ts +154 -43
- package/src/core/State.ts +45 -12
- package/src/core/Tool.ts +67 -10
- package/src/core/Transition.ts +1 -1
- package/src/index.ts +1 -1
- package/src/types/agent.ts +5 -0
- package/src/types/route.ts +10 -1
- package/src/types/session.ts +47 -7
- package/src/utils/logger.ts +19 -0
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
OpenAIProvider,
|
|
15
15
|
GeminiProvider,
|
|
16
16
|
createSession,
|
|
17
|
+
END_STATE,
|
|
17
18
|
} from "../src/index";
|
|
18
19
|
|
|
19
20
|
// Custom context type
|
|
@@ -257,6 +258,37 @@ async function streamingWithRoutes() {
|
|
|
257
258
|
chatState: "Understand the user's product question",
|
|
258
259
|
});
|
|
259
260
|
|
|
261
|
+
// Create a feedback route
|
|
262
|
+
const feedbackRoute = agent.createRoute<{
|
|
263
|
+
rating: number;
|
|
264
|
+
comments: string;
|
|
265
|
+
}>({
|
|
266
|
+
title: "Collect Feedback",
|
|
267
|
+
description: "Collect user feedback on their support experience",
|
|
268
|
+
conditions: ["User wants to provide feedback"],
|
|
269
|
+
extractionSchema: {
|
|
270
|
+
type: "object",
|
|
271
|
+
properties: {
|
|
272
|
+
rating: { type: "number", minimum: 1, maximum: 5 },
|
|
273
|
+
comments: { type: "string" },
|
|
274
|
+
},
|
|
275
|
+
required: ["rating"],
|
|
276
|
+
},
|
|
277
|
+
steps: [
|
|
278
|
+
{
|
|
279
|
+
chatState: "How would you rate your support experience from 1 to 5?",
|
|
280
|
+
gather: ["rating"],
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
chatState: "Thanks for the rating! Any other comments?",
|
|
284
|
+
gather: ["comments"],
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
chatState: "We appreciate your feedback!",
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
});
|
|
291
|
+
|
|
260
292
|
const history = [
|
|
261
293
|
createMessageEvent(
|
|
262
294
|
EventSource.CUSTOMER,
|
|
@@ -285,6 +317,19 @@ async function streamingWithRoutes() {
|
|
|
285
317
|
);
|
|
286
318
|
console.log(` - Extracted:`, chunk.session?.extracted || "None");
|
|
287
319
|
|
|
320
|
+
// Check for route completion
|
|
321
|
+
if (chunk.isRouteComplete) {
|
|
322
|
+
console.log("\n✅ Route complete!");
|
|
323
|
+
if (chunk.session?.currentRoute?.title === "Collect Feedback") {
|
|
324
|
+
await logFeedback(
|
|
325
|
+
agent.getExtractedData(chunk.session?.id) as {
|
|
326
|
+
rating: number;
|
|
327
|
+
comments: string;
|
|
328
|
+
}
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
288
333
|
// Update session with progress
|
|
289
334
|
session = chunk.session!;
|
|
290
335
|
}
|
|
@@ -372,6 +417,21 @@ async function streamingWithAbortSignal() {
|
|
|
372
417
|
}
|
|
373
418
|
}
|
|
374
419
|
|
|
420
|
+
/**
|
|
421
|
+
* Mock function to log feedback.
|
|
422
|
+
* @param data - The feedback data.
|
|
423
|
+
*/
|
|
424
|
+
async function logFeedback(data: { rating: number; comments: string }) {
|
|
425
|
+
console.log("\n" + "=".repeat(60));
|
|
426
|
+
console.log("📝 Logging Feedback...");
|
|
427
|
+
console.log("=".repeat(60));
|
|
428
|
+
console.log("Feedback Details:", JSON.stringify(data, null, 2));
|
|
429
|
+
console.log(` - Rating: ${data.rating}`);
|
|
430
|
+
console.log(` - Comments: ${data.comments}`);
|
|
431
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
432
|
+
console.log("✨ Feedback logged successfully!");
|
|
433
|
+
}
|
|
434
|
+
|
|
375
435
|
async function main() {
|
|
376
436
|
console.log("🚀 Starting Streaming Examples\n");
|
|
377
437
|
console.log("=".repeat(60));
|
package/examples/travel-agent.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
Agent,
|
|
8
8
|
defineTool,
|
|
9
9
|
OpenRouterProvider,
|
|
10
|
-
|
|
10
|
+
END_STATE,
|
|
11
11
|
EventSource,
|
|
12
12
|
createMessageEvent,
|
|
13
13
|
createSession,
|
|
@@ -200,6 +200,7 @@ async function createTravelAgent() {
|
|
|
200
200
|
customerId: "test-123",
|
|
201
201
|
customerName: "Test Customer",
|
|
202
202
|
},
|
|
203
|
+
debug: true,
|
|
203
204
|
});
|
|
204
205
|
|
|
205
206
|
// Add domain glossary
|
|
@@ -328,7 +329,7 @@ async function createTravelAgent() {
|
|
|
328
329
|
});
|
|
329
330
|
|
|
330
331
|
provideConfirmation.transitionTo({
|
|
331
|
-
state:
|
|
332
|
+
state: END_STATE,
|
|
332
333
|
condition: "Customer has confirmation, booking flow complete",
|
|
333
334
|
});
|
|
334
335
|
|
|
@@ -388,7 +389,7 @@ async function createTravelAgent() {
|
|
|
388
389
|
});
|
|
389
390
|
|
|
390
391
|
provideStatus.transitionTo({
|
|
391
|
-
state:
|
|
392
|
+
state: END_STATE,
|
|
392
393
|
condition: "Booking information provided to customer",
|
|
393
394
|
});
|
|
394
395
|
|
|
@@ -431,16 +432,16 @@ async function main() {
|
|
|
431
432
|
),
|
|
432
433
|
];
|
|
433
434
|
|
|
434
|
-
console.
|
|
435
|
-
console.
|
|
435
|
+
console.info("Agent:", agent.name);
|
|
436
|
+
console.info("Description:", agent.description);
|
|
436
437
|
|
|
437
438
|
// Turn 1 - Agent extracts data and starts booking flow
|
|
438
439
|
const response1 = await agent.respond({ history, session });
|
|
439
|
-
console.
|
|
440
|
-
console.
|
|
441
|
-
console.
|
|
442
|
-
console.
|
|
443
|
-
console.
|
|
440
|
+
console.info("\n=== TURN 1 ===");
|
|
441
|
+
console.info("Agent:", response1.message);
|
|
442
|
+
console.info("Route:", response1.session?.currentRoute?.title);
|
|
443
|
+
console.info("State:", response1.session?.currentState?.id);
|
|
444
|
+
console.info("Extracted:", response1.session?.extracted);
|
|
444
445
|
|
|
445
446
|
// Session state updated with progress
|
|
446
447
|
session = response1.session!;
|
|
@@ -454,10 +455,10 @@ async function main() {
|
|
|
454
455
|
];
|
|
455
456
|
|
|
456
457
|
const response2 = await agent.respond({ history: history2, session });
|
|
457
|
-
console.
|
|
458
|
-
console.
|
|
459
|
-
console.
|
|
460
|
-
console.
|
|
458
|
+
console.info("\n=== TURN 2 ===");
|
|
459
|
+
console.info("Agent:", response2.message);
|
|
460
|
+
console.info("Updated extracted:", response2.session?.extracted);
|
|
461
|
+
console.info("Current state:", response2.session?.currentState?.id);
|
|
461
462
|
}
|
|
462
463
|
|
|
463
464
|
// Demonstrate booking status check
|
|
@@ -474,24 +475,65 @@ async function main() {
|
|
|
474
475
|
history: statusHistory,
|
|
475
476
|
session: statusSession,
|
|
476
477
|
});
|
|
477
|
-
console.
|
|
478
|
-
console.
|
|
479
|
-
console.
|
|
480
|
-
console.
|
|
478
|
+
console.info("\n=== BOOKING STATUS CHECK ===");
|
|
479
|
+
console.info("Agent:", statusResponse.message);
|
|
480
|
+
console.info("Route:", statusResponse.session?.currentRoute?.title);
|
|
481
|
+
console.info("Extracted:", statusResponse.session?.extracted);
|
|
481
482
|
|
|
482
483
|
// Show session state management benefits
|
|
483
|
-
console.
|
|
484
|
-
console.
|
|
485
|
-
console.
|
|
486
|
-
console.
|
|
484
|
+
console.info("\n=== SESSION STATE BENEFITS ===");
|
|
485
|
+
console.info("✅ Always-on routing - respects user intent changes");
|
|
486
|
+
console.info("✅ Data persistence - extracted data survives across turns");
|
|
487
|
+
console.info(
|
|
487
488
|
"✅ State progression - intelligent flow based on collected data"
|
|
488
489
|
);
|
|
489
|
-
console.
|
|
490
|
+
console.info("✅ Context awareness - router sees current progress");
|
|
491
|
+
|
|
492
|
+
if (statusResponse.isRouteComplete) {
|
|
493
|
+
console.info("\n✅ Booking status check complete!");
|
|
494
|
+
await logBookingStatusCheck(
|
|
495
|
+
agent.getExtractedData(
|
|
496
|
+
statusResponse.session?.id
|
|
497
|
+
) as unknown as BookingStatusData
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Mock function to send a booking confirmation.
|
|
504
|
+
* @param data - The flight booking data.
|
|
505
|
+
*/
|
|
506
|
+
async function sendBookingConfirmation(data: FlightBookingData) {
|
|
507
|
+
console.info("\n" + "=".repeat(60));
|
|
508
|
+
console.info("🚀 Sending Booking Confirmation...");
|
|
509
|
+
console.info("=".repeat(60));
|
|
510
|
+
console.info("Booking Details:", JSON.stringify(data, null, 2));
|
|
511
|
+
console.info(
|
|
512
|
+
` - Sending confirmation for ${data.passengers} passengers to ${data.destination}.`
|
|
513
|
+
);
|
|
514
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
515
|
+
console.info("✨ Confirmation sent!");
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Mock function to log a booking status check.
|
|
520
|
+
* @param data - The booking status data.
|
|
521
|
+
*/
|
|
522
|
+
async function logBookingStatusCheck(data: BookingStatusData) {
|
|
523
|
+
console.info("\n" + "=".repeat(60));
|
|
524
|
+
console.info("📝 Logging Booking Status Check...");
|
|
525
|
+
console.info("=".repeat(60));
|
|
526
|
+
console.info("Check Details:", JSON.stringify(data, null, 2));
|
|
527
|
+
console.info(
|
|
528
|
+
` - Logging status check for confirmation #${data.confirmationNumber}.`
|
|
529
|
+
);
|
|
530
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
531
|
+
console.info("✨ Status check logged!");
|
|
490
532
|
}
|
|
491
533
|
|
|
492
534
|
// Run if executed directly
|
|
493
535
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
494
|
-
main().catch(console.error);
|
|
536
|
+
main().catch((err) => console.error(err));
|
|
495
537
|
}
|
|
496
538
|
|
|
497
539
|
export { createTravelAgent };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@falai/agent",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "Standalone, strongly-typed AI Agent framework with route DSL and AI provider strategy",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -79,12 +79,13 @@
|
|
|
79
79
|
"dependencies": {
|
|
80
80
|
"@anthropic-ai/sdk": "^0.65.0",
|
|
81
81
|
"@google/genai": "^0.3.0",
|
|
82
|
+
"@types/redis": "^4.0.11",
|
|
83
|
+
"loglevel": "^1.9.2",
|
|
82
84
|
"openai": "^6.3.0"
|
|
83
85
|
},
|
|
84
86
|
"peerDependencies": {
|
|
85
87
|
"@prisma/client": "^6.0.0",
|
|
86
88
|
"ioredis": "^5.7.0",
|
|
87
|
-
"redis": "^4.6.0 || ^5.0.0",
|
|
88
89
|
"mongodb": "^6.0.0 || ^7.0.0",
|
|
89
90
|
"pg": "^8.11.0",
|
|
90
91
|
"mysql2": "^3.2.0",
|
package/src/constants/index.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Special marker to end a route/journey
|
|
3
3
|
*/
|
|
4
|
+
export const END_STATE = Symbol("END_STATE");
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
/**
|
|
7
|
+
* String constant for END_STATE comparisons
|
|
8
|
+
* Use this when checking if currentState.id has reached END_STATE
|
|
9
|
+
*/
|
|
10
|
+
export const END_STATE_ID = "END_STATE";
|
package/src/core/Agent.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type { SessionState } from "../types/session";
|
|
|
10
10
|
import type { AgentStructuredResponse } from "../types/ai";
|
|
11
11
|
import { createSession, enterState, mergeExtracted } from "../types/session";
|
|
12
12
|
import { PromptComposer } from "./PromptComposer";
|
|
13
|
+
import { logger, LoggerLevel } from "../utils/logger";
|
|
13
14
|
|
|
14
15
|
import { Route } from "./Route";
|
|
15
16
|
import { State } from "./State";
|
|
@@ -19,6 +20,7 @@ import { RoutingEngine } from "./RoutingEngine";
|
|
|
19
20
|
import { ResponseEngine } from "./ResponseEngine";
|
|
20
21
|
import { ToolExecutor } from "./ToolExecutor";
|
|
21
22
|
import { getLastMessageFromHistory } from "../utils/event";
|
|
23
|
+
import { END_STATE_ID } from "../constants";
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* Main Agent class with generic context support
|
|
@@ -34,6 +36,7 @@ export class Agent<TContext = unknown> {
|
|
|
34
36
|
private persistenceManager: PersistenceManager | undefined;
|
|
35
37
|
private routingEngine: RoutingEngine<TContext>;
|
|
36
38
|
private responseEngine: ResponseEngine<TContext>;
|
|
39
|
+
private currentSession?: SessionState;
|
|
37
40
|
|
|
38
41
|
/**
|
|
39
42
|
* Dynamic domain property - populated via addDomain
|
|
@@ -41,6 +44,11 @@ export class Agent<TContext = unknown> {
|
|
|
41
44
|
public readonly domain: Record<string, Record<string, unknown>> = {};
|
|
42
45
|
|
|
43
46
|
constructor(private readonly options: AgentOptions<TContext>) {
|
|
47
|
+
// Set log level based on debug option
|
|
48
|
+
if (options.debug) {
|
|
49
|
+
logger.setLevel(LoggerLevel.DEBUG);
|
|
50
|
+
}
|
|
51
|
+
|
|
44
52
|
// Validate context configuration
|
|
45
53
|
if (options.context !== undefined && options.contextProvider) {
|
|
46
54
|
throw new Error(
|
|
@@ -51,6 +59,9 @@ export class Agent<TContext = unknown> {
|
|
|
51
59
|
// Initialize context if provided
|
|
52
60
|
this.context = options.context;
|
|
53
61
|
|
|
62
|
+
// Initialize current session if provided
|
|
63
|
+
this.currentSession = options.session;
|
|
64
|
+
|
|
54
65
|
// Initialize routing and response engines
|
|
55
66
|
this.routingEngine = new RoutingEngine<TContext>({
|
|
56
67
|
maxCandidates: 5,
|
|
@@ -66,7 +77,7 @@ export class Agent<TContext = unknown> {
|
|
|
66
77
|
// Initialize the adapter if it has an initialize method
|
|
67
78
|
if (options.persistence.adapter.initialize) {
|
|
68
79
|
options.persistence.adapter.initialize().catch((error) => {
|
|
69
|
-
|
|
80
|
+
logger.error(
|
|
70
81
|
"[Agent] Persistence adapter initialization failed:",
|
|
71
82
|
error
|
|
72
83
|
);
|
|
@@ -272,6 +283,7 @@ export class Agent<TContext = unknown> {
|
|
|
272
283
|
done: boolean;
|
|
273
284
|
session?: SessionState;
|
|
274
285
|
toolCalls?: Array<{ toolName: string; arguments: Record<string, unknown> }>;
|
|
286
|
+
isRouteComplete?: boolean;
|
|
275
287
|
}> {
|
|
276
288
|
const { history, contextOverride, signal } = params;
|
|
277
289
|
|
|
@@ -291,8 +303,8 @@ export class Agent<TContext = unknown> {
|
|
|
291
303
|
...(contextOverride as Record<string, unknown>),
|
|
292
304
|
} as TContext;
|
|
293
305
|
|
|
294
|
-
// Initialize or get session
|
|
295
|
-
let session = params.session || createSession();
|
|
306
|
+
// Initialize or get session (use current session if available)
|
|
307
|
+
let session = params.session || this.currentSession || createSession();
|
|
296
308
|
|
|
297
309
|
// PHASE 1: TOOL EXECUTION - Execute tools if current state has toolState
|
|
298
310
|
if (session.currentRoute && session.currentState) {
|
|
@@ -331,13 +343,13 @@ export class Agent<TContext = unknown> {
|
|
|
331
343
|
session,
|
|
332
344
|
result.extractedUpdate
|
|
333
345
|
);
|
|
334
|
-
|
|
346
|
+
logger.debug(
|
|
335
347
|
`[Agent] Tool updated extracted data:`,
|
|
336
348
|
result.extractedUpdate
|
|
337
349
|
);
|
|
338
350
|
}
|
|
339
351
|
|
|
340
|
-
|
|
352
|
+
logger.debug(
|
|
341
353
|
`[Agent] Executed tool: ${result.toolName} (success: ${result.success})`
|
|
342
354
|
);
|
|
343
355
|
}
|
|
@@ -349,6 +361,7 @@ export class Agent<TContext = unknown> {
|
|
|
349
361
|
let selectedRoute: Route<TContext> | undefined;
|
|
350
362
|
let responseDirectives: string[] | undefined;
|
|
351
363
|
let selectedState: State<TContext> | undefined;
|
|
364
|
+
let isRouteComplete = false;
|
|
352
365
|
|
|
353
366
|
if (this.routes.length > 0) {
|
|
354
367
|
const orchestration = await this.routingEngine.decideRouteAndState({
|
|
@@ -370,10 +383,18 @@ export class Agent<TContext = unknown> {
|
|
|
370
383
|
selectedState = orchestration.selectedState;
|
|
371
384
|
responseDirectives = orchestration.responseDirectives;
|
|
372
385
|
session = orchestration.session;
|
|
386
|
+
isRouteComplete = orchestration.isRouteComplete || false;
|
|
387
|
+
|
|
388
|
+
// Log if route is complete
|
|
389
|
+
if (isRouteComplete) {
|
|
390
|
+
logger.debug(
|
|
391
|
+
`[Agent] Route complete: all required data collected, END_STATE reached`
|
|
392
|
+
);
|
|
393
|
+
}
|
|
373
394
|
}
|
|
374
395
|
|
|
375
396
|
// PHASE 3: DETERMINE NEXT STATE - Use state from combined decision or get initial state
|
|
376
|
-
if (selectedRoute) {
|
|
397
|
+
if (selectedRoute && !isRouteComplete) {
|
|
377
398
|
let nextState: State<TContext>;
|
|
378
399
|
|
|
379
400
|
// If we have a selected state from the combined routing decision, use it
|
|
@@ -384,17 +405,17 @@ export class Agent<TContext = unknown> {
|
|
|
384
405
|
const candidates = this.routingEngine.getCandidateStates(
|
|
385
406
|
selectedRoute,
|
|
386
407
|
undefined,
|
|
387
|
-
session.extracted
|
|
408
|
+
session.extracted || {}
|
|
388
409
|
);
|
|
389
410
|
if (candidates.length > 0) {
|
|
390
411
|
nextState = candidates[0].state;
|
|
391
|
-
|
|
412
|
+
logger.debug(
|
|
392
413
|
`[Agent] Using first valid state: ${nextState.id} for new route`
|
|
393
414
|
);
|
|
394
415
|
} else {
|
|
395
416
|
// Fallback to initial state even if it should be skipped
|
|
396
417
|
nextState = selectedRoute.initialState;
|
|
397
|
-
|
|
418
|
+
logger.warn(
|
|
398
419
|
`[Agent] No valid states found, using initial state: ${nextState.id}`
|
|
399
420
|
);
|
|
400
421
|
}
|
|
@@ -402,7 +423,7 @@ export class Agent<TContext = unknown> {
|
|
|
402
423
|
|
|
403
424
|
// Update session with next state
|
|
404
425
|
session = enterState(session, nextState.id, nextState.description);
|
|
405
|
-
|
|
426
|
+
logger.debug(`[Agent] Entered state: ${nextState.id}`);
|
|
406
427
|
|
|
407
428
|
// PHASE 4: RESPONSE GENERATION - Stream message using selected route and state
|
|
408
429
|
// Get last user message
|
|
@@ -417,6 +438,7 @@ export class Agent<TContext = unknown> {
|
|
|
417
438
|
// Build response prompt
|
|
418
439
|
const responsePrompt = this.responseEngine.buildResponsePrompt(
|
|
419
440
|
selectedRoute,
|
|
441
|
+
nextState,
|
|
420
442
|
selectedRoute.getRules(),
|
|
421
443
|
selectedRoute.getProhibitions(),
|
|
422
444
|
responseDirectives,
|
|
@@ -464,7 +486,7 @@ export class Agent<TContext = unknown> {
|
|
|
464
486
|
// Merge gathered data into session
|
|
465
487
|
if (Object.keys(gatheredData).length > 0) {
|
|
466
488
|
session = await this.updateExtracted(session, gatheredData);
|
|
467
|
-
|
|
489
|
+
logger.debug(`[Agent] Extracted data:`, gatheredData);
|
|
468
490
|
}
|
|
469
491
|
}
|
|
470
492
|
|
|
@@ -489,19 +511,39 @@ export class Agent<TContext = unknown> {
|
|
|
489
511
|
this.options.persistence?.autoSave !== false
|
|
490
512
|
) {
|
|
491
513
|
await this.persistenceManager.saveSessionState(session.id, session);
|
|
492
|
-
|
|
514
|
+
logger.debug(
|
|
493
515
|
`[Agent] Auto-saved session state to persistence: ${session.id}`
|
|
494
516
|
);
|
|
495
517
|
}
|
|
496
518
|
|
|
519
|
+
// Update current session if we have one
|
|
520
|
+
if (chunk.done && this.currentSession) {
|
|
521
|
+
this.currentSession = session;
|
|
522
|
+
}
|
|
523
|
+
|
|
497
524
|
yield {
|
|
498
525
|
delta: chunk.delta,
|
|
499
526
|
accumulated: chunk.accumulated,
|
|
500
527
|
done: chunk.done,
|
|
501
528
|
session, // Return updated session
|
|
502
529
|
toolCalls,
|
|
530
|
+
isRouteComplete,
|
|
503
531
|
};
|
|
504
532
|
}
|
|
533
|
+
} else if (isRouteComplete && selectedRoute) {
|
|
534
|
+
// Route is complete - set state to END_STATE marker and yield completion signal
|
|
535
|
+
session = enterState(session, END_STATE_ID, "Route completed");
|
|
536
|
+
logger.debug(
|
|
537
|
+
`[Agent] Route ${selectedRoute.title} completed. Entered END_STATE state.`
|
|
538
|
+
);
|
|
539
|
+
yield {
|
|
540
|
+
delta: "",
|
|
541
|
+
accumulated: "",
|
|
542
|
+
done: true,
|
|
543
|
+
session,
|
|
544
|
+
toolCalls: undefined,
|
|
545
|
+
isRouteComplete: true,
|
|
546
|
+
};
|
|
505
547
|
} else {
|
|
506
548
|
// Fallback: No routes defined, stream a simple response
|
|
507
549
|
const fallbackPrompt = new PromptComposer<TContext>()
|
|
@@ -536,12 +578,18 @@ export class Agent<TContext = unknown> {
|
|
|
536
578
|
});
|
|
537
579
|
|
|
538
580
|
for await (const chunk of stream) {
|
|
581
|
+
// Update current session if we have one
|
|
582
|
+
if (chunk.done && this.currentSession) {
|
|
583
|
+
this.currentSession = session;
|
|
584
|
+
}
|
|
585
|
+
|
|
539
586
|
yield {
|
|
540
587
|
delta: chunk.delta,
|
|
541
588
|
accumulated: chunk.accumulated,
|
|
542
589
|
done: chunk.done,
|
|
543
590
|
session, // Return updated session
|
|
544
591
|
toolCalls: undefined,
|
|
592
|
+
isRouteComplete: false,
|
|
545
593
|
};
|
|
546
594
|
}
|
|
547
595
|
}
|
|
@@ -560,6 +608,7 @@ export class Agent<TContext = unknown> {
|
|
|
560
608
|
message: string;
|
|
561
609
|
session?: SessionState;
|
|
562
610
|
toolCalls?: Array<{ toolName: string; arguments: Record<string, unknown> }>;
|
|
611
|
+
isRouteComplete?: boolean;
|
|
563
612
|
}> {
|
|
564
613
|
const { history, contextOverride, signal } = params;
|
|
565
614
|
|
|
@@ -579,8 +628,9 @@ export class Agent<TContext = unknown> {
|
|
|
579
628
|
...(contextOverride as Record<string, unknown>),
|
|
580
629
|
} as TContext;
|
|
581
630
|
|
|
582
|
-
// Initialize or get session
|
|
583
|
-
let session =
|
|
631
|
+
// Initialize or get session (use current session if available)
|
|
632
|
+
let session =
|
|
633
|
+
params.session || this.currentSession || createSession<TContext>();
|
|
584
634
|
|
|
585
635
|
// PHASE 1: TOOL EXECUTION - Execute tools if current state has toolState
|
|
586
636
|
if (session.currentRoute && session.currentState) {
|
|
@@ -619,13 +669,13 @@ export class Agent<TContext = unknown> {
|
|
|
619
669
|
session,
|
|
620
670
|
result.extractedUpdate
|
|
621
671
|
);
|
|
622
|
-
|
|
672
|
+
logger.debug(
|
|
623
673
|
`[Agent] Tool updated extracted data:`,
|
|
624
674
|
result.extractedUpdate
|
|
625
675
|
);
|
|
626
676
|
}
|
|
627
677
|
|
|
628
|
-
|
|
678
|
+
logger.debug(
|
|
629
679
|
`[Agent] Executed tool: ${result.toolName} (success: ${result.success})`
|
|
630
680
|
);
|
|
631
681
|
}
|
|
@@ -637,6 +687,7 @@ export class Agent<TContext = unknown> {
|
|
|
637
687
|
let selectedRoute: Route<TContext> | undefined;
|
|
638
688
|
let responseDirectives: string[] | undefined;
|
|
639
689
|
let selectedState: State<TContext> | undefined;
|
|
690
|
+
let isRouteComplete = false;
|
|
640
691
|
|
|
641
692
|
if (this.routes.length > 0) {
|
|
642
693
|
const orchestration = await this.routingEngine.decideRouteAndState({
|
|
@@ -658,6 +709,14 @@ export class Agent<TContext = unknown> {
|
|
|
658
709
|
selectedState = orchestration.selectedState;
|
|
659
710
|
responseDirectives = orchestration.responseDirectives;
|
|
660
711
|
session = orchestration.session;
|
|
712
|
+
isRouteComplete = orchestration.isRouteComplete || false;
|
|
713
|
+
|
|
714
|
+
// Log if route is complete
|
|
715
|
+
if (isRouteComplete) {
|
|
716
|
+
logger.debug(
|
|
717
|
+
`[Agent] Route complete: all required data collected, END_STATE reached`
|
|
718
|
+
);
|
|
719
|
+
}
|
|
661
720
|
}
|
|
662
721
|
|
|
663
722
|
// PHASE 3: DETERMINE NEXT STATE - Use state from combined decision or get initial state
|
|
@@ -666,7 +725,7 @@ export class Agent<TContext = unknown> {
|
|
|
666
725
|
| Array<{ toolName: string; arguments: Record<string, unknown> }>
|
|
667
726
|
| undefined = undefined;
|
|
668
727
|
|
|
669
|
-
if (selectedRoute) {
|
|
728
|
+
if (selectedRoute && !isRouteComplete) {
|
|
670
729
|
let nextState: State<TContext>;
|
|
671
730
|
|
|
672
731
|
// If we have a selected state from the combined routing decision, use it
|
|
@@ -677,17 +736,17 @@ export class Agent<TContext = unknown> {
|
|
|
677
736
|
const candidates = this.routingEngine.getCandidateStates(
|
|
678
737
|
selectedRoute,
|
|
679
738
|
undefined,
|
|
680
|
-
session.extracted
|
|
739
|
+
session.extracted || {}
|
|
681
740
|
);
|
|
682
741
|
if (candidates.length > 0) {
|
|
683
742
|
nextState = candidates[0].state;
|
|
684
|
-
|
|
743
|
+
logger.debug(
|
|
685
744
|
`[Agent] Using first valid state: ${nextState.id} for new route`
|
|
686
745
|
);
|
|
687
746
|
} else {
|
|
688
747
|
// Fallback to initial state even if it should be skipped
|
|
689
748
|
nextState = selectedRoute.initialState;
|
|
690
|
-
|
|
749
|
+
logger.warn(
|
|
691
750
|
`[Agent] No valid states found, using initial state: ${nextState.id}`
|
|
692
751
|
);
|
|
693
752
|
}
|
|
@@ -695,7 +754,7 @@ export class Agent<TContext = unknown> {
|
|
|
695
754
|
|
|
696
755
|
// Update session with next state
|
|
697
756
|
session = enterState(session, nextState.id, nextState.description);
|
|
698
|
-
|
|
757
|
+
logger.debug(`[Agent] Entered state: ${nextState.id}`);
|
|
699
758
|
|
|
700
759
|
// PHASE 4: RESPONSE GENERATION - Generate message using selected route and state
|
|
701
760
|
// Get last user message
|
|
@@ -710,6 +769,7 @@ export class Agent<TContext = unknown> {
|
|
|
710
769
|
// Build response prompt
|
|
711
770
|
const responsePrompt = this.responseEngine.buildResponsePrompt(
|
|
712
771
|
selectedRoute,
|
|
772
|
+
nextState,
|
|
713
773
|
selectedRoute.getRules(),
|
|
714
774
|
selectedRoute.getProhibitions(),
|
|
715
775
|
responseDirectives,
|
|
@@ -752,8 +812,8 @@ export class Agent<TContext = unknown> {
|
|
|
752
812
|
|
|
753
813
|
// Merge gathered data into session
|
|
754
814
|
if (Object.keys(gatheredData).length > 0) {
|
|
755
|
-
session =
|
|
756
|
-
|
|
815
|
+
session = await this.updateExtracted(session, gatheredData);
|
|
816
|
+
logger.debug(`[Agent] Extracted data:`, gatheredData);
|
|
757
817
|
}
|
|
758
818
|
}
|
|
759
819
|
|
|
@@ -768,6 +828,13 @@ export class Agent<TContext = unknown> {
|
|
|
768
828
|
.contextUpdate as Partial<TContext>
|
|
769
829
|
);
|
|
770
830
|
}
|
|
831
|
+
} else if (isRouteComplete && selectedRoute) {
|
|
832
|
+
// Route is complete - set state to END_STATE marker and return completion signal
|
|
833
|
+
session = enterState(session, END_STATE_ID, "Route completed");
|
|
834
|
+
message = "";
|
|
835
|
+
logger.debug(
|
|
836
|
+
`[Agent] Route ${selectedRoute.title} completed. Entered END_STATE state.`
|
|
837
|
+
);
|
|
771
838
|
} else {
|
|
772
839
|
// Fallback: No routes defined, generate a simple response
|
|
773
840
|
const fallbackPrompt = new PromptComposer<TContext>()
|
|
@@ -811,15 +878,21 @@ export class Agent<TContext = unknown> {
|
|
|
811
878
|
this.options.persistence?.autoSave !== false
|
|
812
879
|
) {
|
|
813
880
|
await this.persistenceManager.saveSessionState(session.id, session);
|
|
814
|
-
|
|
881
|
+
logger.debug(
|
|
815
882
|
`[Agent] Auto-saved session state to persistence: ${session.id}`
|
|
816
883
|
);
|
|
817
884
|
}
|
|
818
885
|
|
|
886
|
+
// Update current session if we have one
|
|
887
|
+
if (this.currentSession) {
|
|
888
|
+
this.currentSession = session;
|
|
889
|
+
}
|
|
890
|
+
|
|
819
891
|
return {
|
|
820
892
|
message,
|
|
821
893
|
session, // Return updated session with route/state info
|
|
822
894
|
toolCalls,
|
|
895
|
+
isRouteComplete: !isRouteComplete, // Indicates if the route has reached END_STATE with all data collected
|
|
823
896
|
};
|
|
824
897
|
}
|
|
825
898
|
|
|
@@ -907,4 +980,47 @@ export class Agent<TContext = unknown> {
|
|
|
907
980
|
const allowedDomains = route.getDomains();
|
|
908
981
|
return this.domainRegistry.getFiltered(allowedDomains);
|
|
909
982
|
}
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* Set the current session for convenience methods
|
|
986
|
+
* @param session - Session state to use for subsequent calls
|
|
987
|
+
*/
|
|
988
|
+
setCurrentSession(session: SessionState): void {
|
|
989
|
+
this.currentSession = session;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
/**
|
|
993
|
+
* Get the current session (if set)
|
|
994
|
+
*/
|
|
995
|
+
getCurrentSession(): SessionState | undefined {
|
|
996
|
+
return this.currentSession;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Clear the current session
|
|
1001
|
+
*/
|
|
1002
|
+
clearCurrentSession(): void {
|
|
1003
|
+
this.currentSession = undefined;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* Get extracted data from current session
|
|
1008
|
+
* @param routeId - Optional route ID to get data for (uses current route if not provided)
|
|
1009
|
+
* @returns The extracted data from the current session
|
|
1010
|
+
*/
|
|
1011
|
+
getExtractedData<TExtracted = unknown>(
|
|
1012
|
+
routeId?: string
|
|
1013
|
+
): Partial<TExtracted> {
|
|
1014
|
+
if (!this.currentSession) {
|
|
1015
|
+
return {} as Partial<TExtracted>;
|
|
1016
|
+
}
|
|
1017
|
+
if (routeId) {
|
|
1018
|
+
return (
|
|
1019
|
+
(this.currentSession.extractedByRoute?.[
|
|
1020
|
+
routeId
|
|
1021
|
+
] as Partial<TExtracted>) || ({} as Partial<TExtracted>)
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
return (this.currentSession.extracted as Partial<TExtracted>) || {};
|
|
1025
|
+
}
|
|
910
1026
|
}
|