@falai/agent 0.6.3 → 0.6.4
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 +108 -21
- 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/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 +112 -37
- package/dist/cjs/core/RoutingEngine.js.map +1 -1
- package/dist/cjs/core/State.d.ts +15 -5
- package/dist/cjs/core/State.d.ts.map +1 -1
- package/dist/cjs/core/State.js +24 -5
- 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 +12 -1
- package/dist/cjs/types/session.d.ts.map +1 -1
- package/dist/cjs/types/session.js +26 -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 +108 -21
- 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/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 +112 -37
- package/dist/core/RoutingEngine.js.map +1 -1
- package/dist/core/State.d.ts +15 -5
- package/dist/core/State.d.ts.map +1 -1
- package/dist/core/State.js +25 -6
- 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 +12 -1
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js +26 -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 +135 -21
- package/src/core/Events.ts +73 -10
- package/src/core/Route.ts +8 -4
- package/src/core/RoutingEngine.ts +150 -39
- package/src/core/State.ts +35 -10
- 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 +42 -6
- 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.4",
|
|
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
|
|
@@ -388,13 +409,13 @@ export class Agent<TContext = unknown> {
|
|
|
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
|
|
@@ -464,7 +485,7 @@ export class Agent<TContext = unknown> {
|
|
|
464
485
|
// Merge gathered data into session
|
|
465
486
|
if (Object.keys(gatheredData).length > 0) {
|
|
466
487
|
session = await this.updateExtracted(session, gatheredData);
|
|
467
|
-
|
|
488
|
+
logger.debug(`[Agent] Extracted data:`, gatheredData);
|
|
468
489
|
}
|
|
469
490
|
}
|
|
470
491
|
|
|
@@ -489,19 +510,39 @@ export class Agent<TContext = unknown> {
|
|
|
489
510
|
this.options.persistence?.autoSave !== false
|
|
490
511
|
) {
|
|
491
512
|
await this.persistenceManager.saveSessionState(session.id, session);
|
|
492
|
-
|
|
513
|
+
logger.debug(
|
|
493
514
|
`[Agent] Auto-saved session state to persistence: ${session.id}`
|
|
494
515
|
);
|
|
495
516
|
}
|
|
496
517
|
|
|
518
|
+
// Update current session if we have one
|
|
519
|
+
if (chunk.done && this.currentSession) {
|
|
520
|
+
this.currentSession = session;
|
|
521
|
+
}
|
|
522
|
+
|
|
497
523
|
yield {
|
|
498
524
|
delta: chunk.delta,
|
|
499
525
|
accumulated: chunk.accumulated,
|
|
500
526
|
done: chunk.done,
|
|
501
527
|
session, // Return updated session
|
|
502
528
|
toolCalls,
|
|
529
|
+
isRouteComplete,
|
|
503
530
|
};
|
|
504
531
|
}
|
|
532
|
+
} else if (isRouteComplete && selectedRoute) {
|
|
533
|
+
// Route is complete - set state to END_STATE marker and yield completion signal
|
|
534
|
+
session = enterState(session, END_STATE_ID, "Route completed");
|
|
535
|
+
logger.debug(
|
|
536
|
+
`[Agent] Route ${selectedRoute.title} completed. Entered END_STATE state.`
|
|
537
|
+
);
|
|
538
|
+
yield {
|
|
539
|
+
delta: "",
|
|
540
|
+
accumulated: "",
|
|
541
|
+
done: true,
|
|
542
|
+
session,
|
|
543
|
+
toolCalls: undefined,
|
|
544
|
+
isRouteComplete: true,
|
|
545
|
+
};
|
|
505
546
|
} else {
|
|
506
547
|
// Fallback: No routes defined, stream a simple response
|
|
507
548
|
const fallbackPrompt = new PromptComposer<TContext>()
|
|
@@ -536,12 +577,18 @@ export class Agent<TContext = unknown> {
|
|
|
536
577
|
});
|
|
537
578
|
|
|
538
579
|
for await (const chunk of stream) {
|
|
580
|
+
// Update current session if we have one
|
|
581
|
+
if (chunk.done && this.currentSession) {
|
|
582
|
+
this.currentSession = session;
|
|
583
|
+
}
|
|
584
|
+
|
|
539
585
|
yield {
|
|
540
586
|
delta: chunk.delta,
|
|
541
587
|
accumulated: chunk.accumulated,
|
|
542
588
|
done: chunk.done,
|
|
543
589
|
session, // Return updated session
|
|
544
590
|
toolCalls: undefined,
|
|
591
|
+
isRouteComplete: false,
|
|
545
592
|
};
|
|
546
593
|
}
|
|
547
594
|
}
|
|
@@ -560,6 +607,7 @@ export class Agent<TContext = unknown> {
|
|
|
560
607
|
message: string;
|
|
561
608
|
session?: SessionState;
|
|
562
609
|
toolCalls?: Array<{ toolName: string; arguments: Record<string, unknown> }>;
|
|
610
|
+
isRouteComplete?: boolean;
|
|
563
611
|
}> {
|
|
564
612
|
const { history, contextOverride, signal } = params;
|
|
565
613
|
|
|
@@ -579,8 +627,9 @@ export class Agent<TContext = unknown> {
|
|
|
579
627
|
...(contextOverride as Record<string, unknown>),
|
|
580
628
|
} as TContext;
|
|
581
629
|
|
|
582
|
-
// Initialize or get session
|
|
583
|
-
let session =
|
|
630
|
+
// Initialize or get session (use current session if available)
|
|
631
|
+
let session =
|
|
632
|
+
params.session || this.currentSession || createSession<TContext>();
|
|
584
633
|
|
|
585
634
|
// PHASE 1: TOOL EXECUTION - Execute tools if current state has toolState
|
|
586
635
|
if (session.currentRoute && session.currentState) {
|
|
@@ -619,13 +668,13 @@ export class Agent<TContext = unknown> {
|
|
|
619
668
|
session,
|
|
620
669
|
result.extractedUpdate
|
|
621
670
|
);
|
|
622
|
-
|
|
671
|
+
logger.debug(
|
|
623
672
|
`[Agent] Tool updated extracted data:`,
|
|
624
673
|
result.extractedUpdate
|
|
625
674
|
);
|
|
626
675
|
}
|
|
627
676
|
|
|
628
|
-
|
|
677
|
+
logger.debug(
|
|
629
678
|
`[Agent] Executed tool: ${result.toolName} (success: ${result.success})`
|
|
630
679
|
);
|
|
631
680
|
}
|
|
@@ -637,6 +686,7 @@ export class Agent<TContext = unknown> {
|
|
|
637
686
|
let selectedRoute: Route<TContext> | undefined;
|
|
638
687
|
let responseDirectives: string[] | undefined;
|
|
639
688
|
let selectedState: State<TContext> | undefined;
|
|
689
|
+
let isRouteComplete = false;
|
|
640
690
|
|
|
641
691
|
if (this.routes.length > 0) {
|
|
642
692
|
const orchestration = await this.routingEngine.decideRouteAndState({
|
|
@@ -658,6 +708,14 @@ export class Agent<TContext = unknown> {
|
|
|
658
708
|
selectedState = orchestration.selectedState;
|
|
659
709
|
responseDirectives = orchestration.responseDirectives;
|
|
660
710
|
session = orchestration.session;
|
|
711
|
+
isRouteComplete = orchestration.isRouteComplete || false;
|
|
712
|
+
|
|
713
|
+
// Log if route is complete
|
|
714
|
+
if (isRouteComplete) {
|
|
715
|
+
logger.debug(
|
|
716
|
+
`[Agent] Route complete: all required data collected, END_STATE reached`
|
|
717
|
+
);
|
|
718
|
+
}
|
|
661
719
|
}
|
|
662
720
|
|
|
663
721
|
// PHASE 3: DETERMINE NEXT STATE - Use state from combined decision or get initial state
|
|
@@ -666,7 +724,7 @@ export class Agent<TContext = unknown> {
|
|
|
666
724
|
| Array<{ toolName: string; arguments: Record<string, unknown> }>
|
|
667
725
|
| undefined = undefined;
|
|
668
726
|
|
|
669
|
-
if (selectedRoute) {
|
|
727
|
+
if (selectedRoute && !isRouteComplete) {
|
|
670
728
|
let nextState: State<TContext>;
|
|
671
729
|
|
|
672
730
|
// If we have a selected state from the combined routing decision, use it
|
|
@@ -681,13 +739,13 @@ export class Agent<TContext = unknown> {
|
|
|
681
739
|
);
|
|
682
740
|
if (candidates.length > 0) {
|
|
683
741
|
nextState = candidates[0].state;
|
|
684
|
-
|
|
742
|
+
logger.debug(
|
|
685
743
|
`[Agent] Using first valid state: ${nextState.id} for new route`
|
|
686
744
|
);
|
|
687
745
|
} else {
|
|
688
746
|
// Fallback to initial state even if it should be skipped
|
|
689
747
|
nextState = selectedRoute.initialState;
|
|
690
|
-
|
|
748
|
+
logger.warn(
|
|
691
749
|
`[Agent] No valid states found, using initial state: ${nextState.id}`
|
|
692
750
|
);
|
|
693
751
|
}
|
|
@@ -695,7 +753,7 @@ export class Agent<TContext = unknown> {
|
|
|
695
753
|
|
|
696
754
|
// Update session with next state
|
|
697
755
|
session = enterState(session, nextState.id, nextState.description);
|
|
698
|
-
|
|
756
|
+
logger.debug(`[Agent] Entered state: ${nextState.id}`);
|
|
699
757
|
|
|
700
758
|
// PHASE 4: RESPONSE GENERATION - Generate message using selected route and state
|
|
701
759
|
// Get last user message
|
|
@@ -753,7 +811,7 @@ export class Agent<TContext = unknown> {
|
|
|
753
811
|
// Merge gathered data into session
|
|
754
812
|
if (Object.keys(gatheredData).length > 0) {
|
|
755
813
|
session = mergeExtracted(session, gatheredData);
|
|
756
|
-
|
|
814
|
+
logger.debug(`[Agent] Extracted data:`, gatheredData);
|
|
757
815
|
}
|
|
758
816
|
}
|
|
759
817
|
|
|
@@ -768,6 +826,13 @@ export class Agent<TContext = unknown> {
|
|
|
768
826
|
.contextUpdate as Partial<TContext>
|
|
769
827
|
);
|
|
770
828
|
}
|
|
829
|
+
} else if (isRouteComplete && selectedRoute) {
|
|
830
|
+
// Route is complete - set state to END_STATE marker and return completion signal
|
|
831
|
+
session = enterState(session, END_STATE_ID, "Route completed");
|
|
832
|
+
message = "";
|
|
833
|
+
logger.debug(
|
|
834
|
+
`[Agent] Route ${selectedRoute.title} completed. Entered END_STATE state.`
|
|
835
|
+
);
|
|
771
836
|
} else {
|
|
772
837
|
// Fallback: No routes defined, generate a simple response
|
|
773
838
|
const fallbackPrompt = new PromptComposer<TContext>()
|
|
@@ -811,15 +876,21 @@ export class Agent<TContext = unknown> {
|
|
|
811
876
|
this.options.persistence?.autoSave !== false
|
|
812
877
|
) {
|
|
813
878
|
await this.persistenceManager.saveSessionState(session.id, session);
|
|
814
|
-
|
|
879
|
+
logger.debug(
|
|
815
880
|
`[Agent] Auto-saved session state to persistence: ${session.id}`
|
|
816
881
|
);
|
|
817
882
|
}
|
|
818
883
|
|
|
884
|
+
// Update current session if we have one
|
|
885
|
+
if (this.currentSession) {
|
|
886
|
+
this.currentSession = session;
|
|
887
|
+
}
|
|
888
|
+
|
|
819
889
|
return {
|
|
820
890
|
message,
|
|
821
891
|
session, // Return updated session with route/state info
|
|
822
892
|
toolCalls,
|
|
893
|
+
isRouteComplete: !isRouteComplete, // Indicates if the route has reached END_STATE with all data collected
|
|
823
894
|
};
|
|
824
895
|
}
|
|
825
896
|
|
|
@@ -907,4 +978,47 @@ export class Agent<TContext = unknown> {
|
|
|
907
978
|
const allowedDomains = route.getDomains();
|
|
908
979
|
return this.domainRegistry.getFiltered(allowedDomains);
|
|
909
980
|
}
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Set the current session for convenience methods
|
|
984
|
+
* @param session - Session state to use for subsequent calls
|
|
985
|
+
*/
|
|
986
|
+
setCurrentSession(session: SessionState): void {
|
|
987
|
+
this.currentSession = session;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* Get the current session (if set)
|
|
992
|
+
*/
|
|
993
|
+
getCurrentSession(): SessionState | undefined {
|
|
994
|
+
return this.currentSession;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* Clear the current session
|
|
999
|
+
*/
|
|
1000
|
+
clearCurrentSession(): void {
|
|
1001
|
+
this.currentSession = undefined;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* Get extracted data from current session
|
|
1006
|
+
* @param routeId - Optional route ID to get data for (uses current route if not provided)
|
|
1007
|
+
* @returns The extracted data from the current session
|
|
1008
|
+
*/
|
|
1009
|
+
getExtractedData<TExtracted = unknown>(
|
|
1010
|
+
routeId?: string
|
|
1011
|
+
): Partial<TExtracted> {
|
|
1012
|
+
if (!this.currentSession) {
|
|
1013
|
+
return {} as Partial<TExtracted>;
|
|
1014
|
+
}
|
|
1015
|
+
if (routeId) {
|
|
1016
|
+
return (
|
|
1017
|
+
(this.currentSession.extractedByRoute[
|
|
1018
|
+
routeId
|
|
1019
|
+
] as Partial<TExtracted>) || ({} as Partial<TExtracted>)
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
return this.currentSession.extracted as Partial<TExtracted>;
|
|
1023
|
+
}
|
|
910
1024
|
}
|