@falai/agent 0.5.3 → 0.5.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 CHANGED
@@ -70,6 +70,7 @@
70
70
  - **Session State Integration** - Automatic saving of extracted data & conversation progress
71
71
  - **Provider Pattern** - Simple API like AI providers
72
72
  - **Multiple Adapters** - Prisma, Redis, MongoDB, PostgreSQL, SQLite, OpenSearch, Memory
73
+ - **Custom Database Support** - Manual session state management for existing schemas
73
74
  - **Auto-save** - Automatic session state & message persistence
74
75
  - **Type-Safe** - Full TypeScript support with generics
75
76
  - **Extensible** - Create adapters for any database
@@ -972,6 +973,16 @@ Full-text search and analytics-powered persistence:
972
973
  - 🌐 Compatible with Elasticsearch 7.x
973
974
  - ☁️ AWS OpenSearch Service ready
974
975
 
976
+ ### 🗄️ [Custom Database Integration](./examples/custom-database-persistence.ts)
977
+
978
+ Manual session state management for existing database schemas:
979
+
980
+ - 🔧 Full control over database operations
981
+ - 📦 Works with any database (no adapter needed)
982
+ - 🔄 Manual session state save/restore
983
+ - 🎯 Perfect for integrating with existing schemas
984
+ - ✅ Complete example with validation hooks
985
+
975
986
  ---
976
987
 
977
988
  ## 💾 Database Adapters
@@ -46,7 +46,8 @@ class State {
46
46
  return this.createStateRefWithTransition(spec.state);
47
47
  }
48
48
  // Create new target state for chatState or toolState
49
- const targetState = new State(this.routeId, spec.chatState, undefined, spec.gather, spec.skipIf, spec.requiredData);
49
+ const targetState = new State(this.routeId, spec.chatState, spec.id, // Use custom ID if provided
50
+ spec.gather, spec.skipIf, spec.requiredData);
50
51
  const transition = new Transition_1.Transition(this.getRef(), spec, condition);
51
52
  transition.setTarget(targetState);
52
53
  this.transitions.push(transition);
@@ -1 +1 @@
1
- {"version":3,"file":"State.js","sourceRoot":"","sources":["../../../src/core/State.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"State.js","sourceRoot":"","sources":["../../../src/core/State.ts"],"names":[],"mappings":"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"}
@@ -64,6 +64,8 @@ export interface RouteOptions<TExtracted = unknown> {
64
64
  * Specification for a state transition
65
65
  */
66
66
  export interface TransitionSpec<TContext = unknown, TExtracted = unknown> {
67
+ /** Custom ID for this state (optional - will generate deterministic ID if not provided) */
68
+ id?: string;
67
69
  /** Transition to a chat state with this description */
68
70
  chatState?: string;
69
71
  /** Transition to execute a tool */
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../src/types/route.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,UAAU,GAAG,OAAO;IAChD,qGAAqG;IACrG,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,wCAAwC;IACxC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,4EAA4E;IAC5E,mBAAmB,CAAC,EAAE,gBAAgB,CAAC;IACvC,6EAA6E;IAC7E,oBAAoB,CAAC,EAAE,gBAAgB,CAAC;IACxC;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO;IACtE,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IAEnC,SAAS,CAAC,EAAE,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;IAC1C,mDAAmD;IACnD,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;IACrD;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,CACxE,SAAQ,QAAQ;IAChB,iCAAiC;IACjC,YAAY,EAAE,CACZ,IAAI,EAAE,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,EAC1C,SAAS,CAAC,EAAE,MAAM,KACf,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;CAC7C"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../src/types/route.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,UAAU,GAAG,OAAO;IAChD,qGAAqG;IACrG,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,wCAAwC;IACxC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,4EAA4E;IAC5E,mBAAmB,CAAC,EAAE,gBAAgB,CAAC;IACvC,6EAA6E;IAC7E,oBAAoB,CAAC,EAAE,gBAAgB,CAAC;IACxC;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO;IACtE,2FAA2F;IAC3F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IAEnC,SAAS,CAAC,EAAE,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;IAC1C,mDAAmD;IACnD,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;IACrD;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,CACxE,SAAQ,QAAQ;IAChB,iCAAiC;IACjC,YAAY,EAAE,CACZ,IAAI,EAAE,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,EAC1C,SAAS,CAAC,EAAE,MAAM,KACf,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;CAC7C"}
@@ -43,7 +43,8 @@ export class State {
43
43
  return this.createStateRefWithTransition(spec.state);
44
44
  }
45
45
  // Create new target state for chatState or toolState
46
- const targetState = new State(this.routeId, spec.chatState, undefined, spec.gather, spec.skipIf, spec.requiredData);
46
+ const targetState = new State(this.routeId, spec.chatState, spec.id, // Use custom ID if provided
47
+ spec.gather, spec.skipIf, spec.requiredData);
47
48
  const transition = new Transition(this.getRef(), spec, condition);
48
49
  transition.setTarget(targetState);
49
50
  this.transitions.push(transition);
@@ -1 +1 @@
1
- {"version":3,"file":"State.js","sourceRoot":"","sources":["../../src/core/State.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"State.js","sourceRoot":"","sources":["../../src/core/State.ts"],"names":[],"mappings":"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"}
@@ -64,6 +64,8 @@ export interface RouteOptions<TExtracted = unknown> {
64
64
  * Specification for a state transition
65
65
  */
66
66
  export interface TransitionSpec<TContext = unknown, TExtracted = unknown> {
67
+ /** Custom ID for this state (optional - will generate deterministic ID if not provided) */
68
+ id?: string;
67
69
  /** Transition to a chat state with this description */
68
70
  chatState?: string;
69
71
  /** Transition to execute a tool */
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/types/route.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,UAAU,GAAG,OAAO;IAChD,qGAAqG;IACrG,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,wCAAwC;IACxC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,4EAA4E;IAC5E,mBAAmB,CAAC,EAAE,gBAAgB,CAAC;IACvC,6EAA6E;IAC7E,oBAAoB,CAAC,EAAE,gBAAgB,CAAC;IACxC;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO;IACtE,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IAEnC,SAAS,CAAC,EAAE,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;IAC1C,mDAAmD;IACnD,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;IACrD;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,CACxE,SAAQ,QAAQ;IAChB,iCAAiC;IACjC,YAAY,EAAE,CACZ,IAAI,EAAE,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,EAC1C,SAAS,CAAC,EAAE,MAAM,KACf,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;CAC7C"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/types/route.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,UAAU,GAAG,OAAO;IAChD,qGAAqG;IACrG,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,wCAAwC;IACxC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,4EAA4E;IAC5E,mBAAmB,CAAC,EAAE,gBAAgB,CAAC;IACvC,6EAA6E;IAC7E,oBAAoB,CAAC,EAAE,gBAAgB,CAAC;IACxC;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO;IACtE,2FAA2F;IAC3F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IAEnC,SAAS,CAAC,EAAE,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;IAC1C,mDAAmD;IACnD,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;IACrD;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,CACxE,SAAQ,QAAQ;IAChB,iCAAiC;IACjC,YAAY,EAAE,CACZ,IAAI,EAAE,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,EAC1C,SAAS,CAAC,EAAE,MAAM,KACf,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;CAC7C"}
@@ -87,29 +87,99 @@ interface RespondOutput {
87
87
  - Enables "I changed my mind" scenarios with context-aware routing
88
88
  - Automatically merges new extracted data with existing session data
89
89
 
90
- **Example:**
90
+ **Example with Persistence Adapters:**
91
91
 
92
92
  ```typescript
93
+ import { createSession } from "@falai/agent";
94
+
95
+ // Using built-in persistence adapters
96
+ const { sessionData, sessionState } =
97
+ await persistence.createSessionWithState<FlightData>({
98
+ userId: "user_123",
99
+ agentName: "Travel Agent",
100
+ });
101
+
93
102
  const response = await agent.respond({
94
- history: conversationHistory,
103
+ history,
104
+ session: sessionState, // Auto-saves if autoSave: true
95
105
  });
106
+ ```
96
107
 
97
- // Save to database
98
- await db.agentMessages.create({
99
- sessionId: session.id,
108
+ **Example with Custom Database (Manual):**
109
+
110
+ ```typescript
111
+ import { createSession, SessionState } from "@falai/agent";
112
+
113
+ // Load from your custom database
114
+ const dbSession = await yourDb.sessions.findOne({ id: sessionId });
115
+
116
+ // Restore or create session state
117
+ let agentSession: SessionState<YourDataType>;
118
+
119
+ if (dbSession && dbSession.currentRoute && dbSession.collectedData) {
120
+ // Restore existing session from database
121
+ agentSession = {
122
+ currentRoute: {
123
+ id: dbSession.currentRoute,
124
+ title:
125
+ dbSession.collectedData?.currentRouteTitle || dbSession.currentRoute,
126
+ enteredAt: new Date(),
127
+ },
128
+ currentState: dbSession.currentState
129
+ ? {
130
+ id: dbSession.currentState,
131
+ description: dbSession.collectedData?.currentStateDescription,
132
+ enteredAt: new Date(),
133
+ }
134
+ : undefined,
135
+ extracted: dbSession.collectedData?.extracted || {},
136
+ routeHistory: dbSession.collectedData?.routeHistory || [],
137
+ metadata: {
138
+ sessionId: dbSession.id,
139
+ createdAt: dbSession.createdAt,
140
+ lastUpdatedAt: new Date(),
141
+ },
142
+ };
143
+ } else {
144
+ // Create new session
145
+ agentSession = createSession<YourDataType>({
146
+ sessionId: dbSession?.id || "new-session-id",
147
+ });
148
+ }
149
+
150
+ // Use session in conversation
151
+ const response = await agent.respond({
152
+ history,
153
+ session: agentSession,
154
+ });
155
+
156
+ // Manually save to your database
157
+ await yourDb.sessions.update({
158
+ id: dbSession.id,
159
+ currentRoute: response.session?.currentRoute?.id,
160
+ currentState: response.session?.currentState?.id,
161
+ collectedData: {
162
+ extracted: response.session?.extracted,
163
+ routeHistory: response.session?.routeHistory,
164
+ currentRouteTitle: response.session?.currentRoute?.title,
165
+ currentStateDescription: response.session?.currentState?.description,
166
+ metadata: response.session?.metadata,
167
+ },
168
+ lastMessageAt: new Date(),
169
+ });
170
+
171
+ // Save message
172
+ await yourDb.messages.create({
173
+ sessionId: dbSession.id,
100
174
  role: "agent",
101
175
  content: response.message,
102
- route: response.route?.title,
103
- state: response.state?.description,
104
- toolCalls: response.toolCalls || [],
176
+ route: response.session?.currentRoute?.id,
177
+ state: response.session?.currentState?.id,
105
178
  });
106
-
107
- // Check if conversation is complete
108
- if (response.route?.title === END_ROUTE) {
109
- await markSessionComplete(session.id);
110
- }
111
179
  ```
112
180
 
181
+ See also: [Custom Database Integration Example](../examples/custom-database-persistence.ts)
182
+
113
183
  ##### `respondStream(input: RespondInput<TContext>): AsyncGenerator<StreamChunk>`
114
184
 
115
185
  Generates an AI response as a real-time stream for better user experience. Provides the same structured output as `respond()` but delivers it incrementally.
@@ -58,7 +58,11 @@ import {
58
58
  mergeExtracted,
59
59
  } from "@falai/agent";
60
60
 
61
- let session = createSession<FlightData>();
61
+ // Create session with optional metadata including session ID
62
+ let session = createSession<FlightData>(sessionId, {
63
+ userId: "user_456",
64
+ createdAt: new Date(),
65
+ });
62
66
 
63
67
  // Turn 1 - Extract data
64
68
  const response1 = await agent.respond({ history, session });
@@ -67,6 +71,31 @@ session = response1.session!; // Updated with extracted data
67
71
  // Turn 2 - User changes mind (always-on routing)
68
72
  const response2 = await agent.respond({ history, session: response1.session });
69
73
  session = response2.session!; // Route/state updated if user changed direction
74
+
75
+ // Access session metadata
76
+ console.log(session.metadata?.sessionId); // "unique-session-123"
77
+ ```
78
+
79
+ **Session with Persistence:**
80
+
81
+ When using persistence adapters, set the session ID from the database:
82
+
83
+ ```typescript
84
+ // Create database session and in-memory session state
85
+ const { sessionData, sessionState } =
86
+ await persistence.createSessionWithState<FlightData>({
87
+ userId: "user_123",
88
+ agentName: "Travel Agent",
89
+ });
90
+
91
+ // sessionState.metadata.sessionId is automatically set to sessionData.id
92
+ console.log(sessionState.metadata?.sessionId); // "cuid_from_database"
93
+
94
+ // Use it in conversation
95
+ const response = await agent.respond({
96
+ history,
97
+ session: sessionState, // Auto-saves to database!
98
+ });
70
99
  ```
71
100
 
72
101
  **Why?** Session state enables:
@@ -75,6 +104,7 @@ session = response2.session!; // Route/state updated if user changed direction
75
104
  - **Context Awareness** - Router sees current progress and extracted data
76
105
  - **Data Persistence** - Extracted data survives across turns
77
106
  - **State Recovery** - Resume conversations from any point
107
+ - **Session Tracking** - Track conversations via session ID in database
78
108
 
79
109
  ### 3. 🔧 Code-Based State Logic
80
110
 
@@ -83,12 +113,14 @@ Use TypeScript functions instead of LLM conditions for deterministic flow:
83
113
  ```typescript
84
114
  // State with smart bypassing based on extracted data
85
115
  const askDestination = route.initialState.transitionTo({
116
+ id: "ask_destination", // Optional: custom state ID
86
117
  chatState: "Ask where they want to fly",
87
118
  gather: ["destination"],
88
119
  skipIf: (extracted) => !!extracted.destination, // Code-based condition!
89
120
  });
90
121
 
91
122
  const askDate = askDestination.transitionTo({
123
+ id: "ask_date", // Optional: custom state ID for easier tracking
92
124
  chatState: "Ask about travel dates",
93
125
  gather: ["departureDate"],
94
126
  skipIf: (extracted) => !!extracted.departureDate,
@@ -96,12 +128,27 @@ const askDate = askDestination.transitionTo({
96
128
  });
97
129
  ```
98
130
 
131
+ **Custom State IDs:**
132
+
133
+ You can optionally provide custom IDs for states to make them easier to track and reference:
134
+
135
+ ```typescript
136
+ const confirmBooking = askDate.transitionTo({
137
+ id: "confirm_booking", // ✅ Custom ID instead of auto-generated
138
+ chatState: "Confirm all booking details",
139
+ requiredData: ["destination", "departureDate", "passengers"],
140
+ });
141
+ ```
142
+
143
+ If you don't provide an ID, one is automatically generated from the route ID and state description.
144
+
99
145
  **Why?** Code-based logic provides:
100
146
 
101
147
  - **Predictability** - No fuzzy LLM interpretation of conditions
102
148
  - **Performance** - No extra LLM calls for condition checking
103
149
  - **Debugging** - Clear logic flow you can trace
104
150
  - **Type Safety** - Full TypeScript support for data validation
151
+ - **Custom IDs** - Easier tracking and debugging with meaningful state identifiers
105
152
 
106
153
  ### 4. 🛠️ Tools with Data Access
107
154
 
@@ -121,11 +121,18 @@ const bookingRoute = agent.createRoute<BookingData>({
121
121
  },
122
122
  });
123
123
 
124
- // Define states with smart data gathering
125
- bookingRoute.initialState.transitionTo({
126
- chatState: "Collect booking details",
127
- gather: ["destination", "date", "passengers"],
128
- });
124
+ // Define states with smart data gathering and custom IDs
125
+ bookingRoute.initialState
126
+ .transitionTo({
127
+ id: "collect_details", // ✅ Custom state ID for easier tracking
128
+ chatState: "Collect booking details",
129
+ gather: ["destination", "date", "passengers"],
130
+ })
131
+ .transitionTo({
132
+ id: "confirm_booking", // ✅ Custom state ID
133
+ chatState: "Confirm all details",
134
+ requiredData: ["destination", "date", "passengers"],
135
+ });
129
136
 
130
137
  // Access persistence methods
131
138
  const persistence = agent.getPersistenceManager();
@@ -137,17 +144,22 @@ const { sessionData, sessionState } =
137
144
  agentName: "My Agent",
138
145
  });
139
146
 
147
+ // Session ID is automatically set in metadata
148
+ console.log("Session ID:", sessionState.metadata?.sessionId);
149
+ // Outputs: sessionData.id (e.g., "cuid_abc123")
150
+
140
151
  // Load history
141
152
  const history = await persistence.loadSessionHistory(sessionData.id);
142
153
 
143
154
  // Generate response with session state
144
155
  const response = await agent.respond({
145
156
  history,
146
- session: sessionState, // Pass session state
157
+ session: sessionState, // Pass session state with ID
147
158
  });
148
159
 
149
160
  // Session state is auto-saved! ✨
150
161
  console.log("Extracted data:", response.session?.extracted);
162
+ console.log("Current state ID:", response.session?.currentState?.id); // Custom or auto-generated ID
151
163
 
152
164
  // Save message
153
165
  await persistence.saveMessage({
@@ -0,0 +1,533 @@
1
+ /**
2
+ * Example: Custom Database Integration (Manual Session State Management)
3
+ *
4
+ * This example shows how to manually manage session state when using your own
5
+ * database structure instead of the built-in persistence adapters.
6
+ *
7
+ * Use this approach if you:
8
+ * - Have an existing database schema you want to integrate with
9
+ * - Need custom data structures beyond what adapters provide
10
+ * - Want full control over database operations
11
+ */
12
+
13
+ import {
14
+ Agent,
15
+ GeminiProvider,
16
+ createMessageEvent,
17
+ EventSource,
18
+ createSession,
19
+ SessionState,
20
+ MessageEventData,
21
+ Event,
22
+ } from "../src/index";
23
+
24
+ /**
25
+ * Example: Your existing database structure
26
+ * This could be any ORM (Prisma, TypeORM, Drizzle) or query builder
27
+ */
28
+ interface CustomDatabaseSession {
29
+ id: string;
30
+ userId: string;
31
+ currentRoute?: string;
32
+ currentState?: string;
33
+ collectedData?: {
34
+ extracted?: Record<string, unknown>;
35
+ routeHistory?: unknown[];
36
+ currentRouteTitle?: string;
37
+ currentStateDescription?: string;
38
+ metadata?: Record<string, unknown>;
39
+ };
40
+ createdAt: Date;
41
+ updatedAt: Date;
42
+ }
43
+
44
+ interface CustomDatabaseMessage {
45
+ id: string;
46
+ sessionId: string;
47
+ userId: string;
48
+ role: "user" | "agent" | "system";
49
+ content: string;
50
+ route?: string;
51
+ state?: string;
52
+ createdAt: Date;
53
+ }
54
+
55
+ // Mock database - replace with your actual database
56
+ class CustomDatabase {
57
+ private sessions: Map<string, CustomDatabaseSession> = new Map();
58
+ private messages: Map<string, CustomDatabaseMessage[]> = new Map();
59
+
60
+ async findSession(id: string): Promise<CustomDatabaseSession | null> {
61
+ return this.sessions.get(id) || null;
62
+ }
63
+
64
+ async createSession(
65
+ data: Omit<CustomDatabaseSession, "id" | "createdAt" | "updatedAt">
66
+ ): Promise<CustomDatabaseSession> {
67
+ const session: CustomDatabaseSession = {
68
+ id: `session_${Date.now()}`,
69
+ ...data,
70
+ createdAt: new Date(),
71
+ updatedAt: new Date(),
72
+ };
73
+ this.sessions.set(session.id, session);
74
+ return session;
75
+ }
76
+
77
+ async updateSession(
78
+ id: string,
79
+ data: Partial<CustomDatabaseSession>
80
+ ): Promise<CustomDatabaseSession | null> {
81
+ const session = this.sessions.get(id);
82
+ if (!session) return null;
83
+
84
+ const updated = {
85
+ ...session,
86
+ ...data,
87
+ updatedAt: new Date(),
88
+ };
89
+ this.sessions.set(id, updated);
90
+ return updated;
91
+ }
92
+
93
+ async createMessage(
94
+ data: Omit<CustomDatabaseMessage, "id" | "createdAt">
95
+ ): Promise<CustomDatabaseMessage> {
96
+ const message: CustomDatabaseMessage = {
97
+ id: `msg_${Date.now()}`,
98
+ ...data,
99
+ createdAt: new Date(),
100
+ };
101
+
102
+ const messages = this.messages.get(data.sessionId) || [];
103
+ messages.push(message);
104
+ this.messages.set(data.sessionId, messages);
105
+
106
+ return message;
107
+ }
108
+
109
+ async getSessionMessages(
110
+ sessionId: string
111
+ ): Promise<CustomDatabaseMessage[]> {
112
+ return this.messages.get(sessionId) || [];
113
+ }
114
+ }
115
+
116
+ // Example data type for a customer onboarding route
117
+ interface OnboardingData {
118
+ fullName: string;
119
+ email: string;
120
+ companyName: string;
121
+ phoneNumber?: string;
122
+ industry?: string;
123
+ }
124
+
125
+ async function example() {
126
+ const db = new CustomDatabase();
127
+ const userId = "user_123";
128
+
129
+ // Create agent
130
+ const agent = new Agent({
131
+ name: "Onboarding Assistant",
132
+ description: "Help new customers get started",
133
+ goal: "Collect customer information efficiently",
134
+ ai: new GeminiProvider({
135
+ apiKey: process.env.GEMINI_API_KEY!,
136
+ model: "models/gemini-2.0-flash-exp",
137
+ }),
138
+ // NOTE: No persistence adapter - we handle it manually!
139
+ });
140
+
141
+ // Create onboarding route
142
+ const onboardingRoute = agent.createRoute<OnboardingData>({
143
+ title: "Customer Onboarding",
144
+ description: "Collect customer information",
145
+ conditions: [
146
+ "User is a new customer",
147
+ "User needs to set up their account",
148
+ ],
149
+ gatherSchema: {
150
+ type: "object",
151
+ properties: {
152
+ fullName: { type: "string" },
153
+ email: { type: "string" },
154
+ companyName: { type: "string" },
155
+ phoneNumber: { type: "string" },
156
+ industry: { type: "string" },
157
+ },
158
+ required: ["fullName", "email", "companyName"],
159
+ },
160
+ });
161
+
162
+ // Define states with custom IDs
163
+ onboardingRoute.initialState
164
+ .transitionTo({
165
+ id: "ask_name",
166
+ chatState: "Ask for full name",
167
+ gather: ["fullName"],
168
+ skipIf: (data) => !!data.fullName,
169
+ })
170
+ .transitionTo({
171
+ id: "ask_email",
172
+ chatState: "Ask for email address",
173
+ gather: ["email"],
174
+ skipIf: (data) => !!data.email,
175
+ })
176
+ .transitionTo({
177
+ id: "ask_company",
178
+ chatState: "Ask for company name",
179
+ gather: ["companyName"],
180
+ skipIf: (data) => !!data.companyName,
181
+ })
182
+ .transitionTo({
183
+ id: "ask_phone",
184
+ chatState: "Ask for phone number (optional)",
185
+ gather: ["phoneNumber"],
186
+ })
187
+ .transitionTo({
188
+ id: "ask_industry",
189
+ chatState: "Ask for industry",
190
+ gather: ["industry"],
191
+ })
192
+ .transitionTo({
193
+ id: "confirm_details",
194
+ chatState: "Confirm all details",
195
+ requiredData: ["fullName", "email", "companyName"],
196
+ });
197
+
198
+ /**
199
+ * Create or load session from your custom database
200
+ */
201
+ let dbSession = await db.findSession("existing_session_id");
202
+
203
+ if (!dbSession) {
204
+ // Create new session in your database
205
+ dbSession = await db.createSession({
206
+ userId,
207
+ collectedData: {},
208
+ });
209
+ console.log("✨ Created new database session:", dbSession.id);
210
+ }
211
+
212
+ /**
213
+ * Convert database session to agent SessionState
214
+ */
215
+ let agentSession: SessionState<OnboardingData>;
216
+
217
+ if (dbSession.currentRoute && dbSession.collectedData) {
218
+ // Restore existing session from database
219
+ console.log("📥 Restoring session from database...");
220
+
221
+ agentSession = {
222
+ currentRoute: {
223
+ id: dbSession.currentRoute,
224
+ title:
225
+ dbSession.collectedData?.currentRouteTitle || dbSession.currentRoute,
226
+ enteredAt: new Date(),
227
+ },
228
+ currentState: dbSession.currentState
229
+ ? {
230
+ id: dbSession.currentState,
231
+ description: dbSession.collectedData?.currentStateDescription,
232
+ enteredAt: new Date(),
233
+ }
234
+ : undefined,
235
+ extracted:
236
+ (dbSession.collectedData?.extracted as Partial<OnboardingData>) || {},
237
+ routeHistory:
238
+ (dbSession.collectedData
239
+ ?.routeHistory as SessionState<OnboardingData>["routeHistory"]) || [],
240
+ metadata: {
241
+ sessionId: dbSession.id,
242
+ userId,
243
+ createdAt: dbSession.createdAt,
244
+ lastUpdatedAt: dbSession.updatedAt,
245
+ ...(dbSession.collectedData?.metadata as Record<string, unknown>),
246
+ },
247
+ };
248
+
249
+ console.log("✅ Session restored:", {
250
+ sessionId: agentSession.metadata?.sessionId,
251
+ currentRoute: agentSession.currentRoute?.title,
252
+ currentState: agentSession.currentState?.id,
253
+ extracted: agentSession.extracted,
254
+ });
255
+ } else {
256
+ // Create new session state
257
+ console.log("🆕 Creating new session state...");
258
+
259
+ agentSession = createSession<OnboardingData>(dbSession.id, {
260
+ sessionId: dbSession.id,
261
+ userId,
262
+ createdAt: dbSession.createdAt,
263
+ });
264
+ }
265
+
266
+ /**
267
+ * Simulate conversation
268
+ */
269
+ const history: Event<MessageEventData>[] = [];
270
+
271
+ // Turn 1: User provides name and email
272
+ console.log("\n--- Turn 1 ---");
273
+ const userMessage1 = createMessageEvent(
274
+ EventSource.CUSTOMER,
275
+ "User",
276
+ "Hi! I'm John Smith and my email is john@acme.com"
277
+ );
278
+ history.push(userMessage1);
279
+
280
+ // Save user message to database
281
+ await db.createMessage({
282
+ sessionId: dbSession.id,
283
+ userId,
284
+ role: "user",
285
+ content: userMessage1.data.message,
286
+ });
287
+
288
+ const response1 = await agent.respond({
289
+ history,
290
+ session: agentSession,
291
+ });
292
+
293
+ console.log("🤖 Agent:", response1.message);
294
+ console.log("📊 Extracted so far:", response1.session?.extracted);
295
+
296
+ // Save agent message to database
297
+ await db.createMessage({
298
+ sessionId: dbSession.id,
299
+ userId,
300
+ role: "agent",
301
+ content: response1.message,
302
+ route: response1.session?.currentRoute?.id,
303
+ state: response1.session?.currentState?.id,
304
+ });
305
+
306
+ // Manually save session state back to database
307
+ await db.updateSession(dbSession.id, {
308
+ currentRoute: response1.session?.currentRoute?.id,
309
+ currentState: response1.session?.currentState?.id,
310
+ collectedData: {
311
+ extracted: response1.session?.extracted,
312
+ routeHistory: response1.session?.routeHistory,
313
+ currentRouteTitle: response1.session?.currentRoute?.title,
314
+ currentStateDescription: response1.session?.currentState?.description,
315
+ metadata: response1.session?.metadata,
316
+ },
317
+ });
318
+
319
+ console.log("💾 Session saved to database");
320
+
321
+ // Update session for next turn
322
+ agentSession = response1.session!;
323
+
324
+ // Turn 2: User provides company
325
+ console.log("\n--- Turn 2 ---");
326
+ history.push(
327
+ createMessageEvent(EventSource.AI_AGENT, "Agent", response1.message)
328
+ );
329
+
330
+ const userMessage2 = createMessageEvent(
331
+ EventSource.CUSTOMER,
332
+ "User",
333
+ "I work for Acme Corporation"
334
+ );
335
+ history.push(userMessage2);
336
+
337
+ await db.createMessage({
338
+ sessionId: dbSession.id,
339
+ userId,
340
+ role: "user",
341
+ content: userMessage2.data.message,
342
+ });
343
+
344
+ const response2 = await agent.respond({
345
+ history,
346
+ session: agentSession,
347
+ });
348
+
349
+ console.log("🤖 Agent:", response2.message);
350
+ console.log("📊 Extracted so far:", response2.session?.extracted);
351
+
352
+ await db.createMessage({
353
+ sessionId: dbSession.id,
354
+ userId,
355
+ role: "agent",
356
+ content: response2.message,
357
+ route: response2.session?.currentRoute?.id,
358
+ state: response2.session?.currentState?.id,
359
+ });
360
+
361
+ // Save session state
362
+ await db.updateSession(dbSession.id, {
363
+ currentRoute: response2.session?.currentRoute?.id,
364
+ currentState: response2.session?.currentState?.id,
365
+ collectedData: {
366
+ extracted: response2.session?.extracted,
367
+ routeHistory: response2.session?.routeHistory,
368
+ currentRouteTitle: response2.session?.currentRoute?.title,
369
+ currentStateDescription: response2.session?.currentState?.description,
370
+ metadata: response2.session?.metadata,
371
+ },
372
+ });
373
+
374
+ console.log("💾 Session saved to database");
375
+
376
+ /**
377
+ * Demonstrate session recovery
378
+ */
379
+ console.log("\n--- Session Recovery ---");
380
+ console.log("Simulating app restart...\n");
381
+
382
+ // Load session from database again
383
+ const reloadedDbSession = await db.findSession(dbSession.id);
384
+ if (!reloadedDbSession) throw new Error("Session not found");
385
+
386
+ // Reconstruct session state
387
+ const recoveredSession: SessionState<OnboardingData> = {
388
+ currentRoute: reloadedDbSession.currentRoute
389
+ ? {
390
+ id: reloadedDbSession.currentRoute,
391
+ title:
392
+ reloadedDbSession.collectedData?.currentRouteTitle ||
393
+ reloadedDbSession.currentRoute,
394
+ enteredAt: new Date(),
395
+ }
396
+ : undefined,
397
+ currentState: reloadedDbSession.currentState
398
+ ? {
399
+ id: reloadedDbSession.currentState,
400
+ description: reloadedDbSession.collectedData?.currentStateDescription,
401
+ enteredAt: new Date(),
402
+ }
403
+ : undefined,
404
+ extracted:
405
+ (reloadedDbSession.collectedData?.extracted as Partial<OnboardingData>) ||
406
+ {},
407
+ routeHistory:
408
+ (reloadedDbSession.collectedData
409
+ ?.routeHistory as SessionState<OnboardingData>["routeHistory"]) || [],
410
+ metadata: {
411
+ sessionId: reloadedDbSession.id,
412
+ userId,
413
+ createdAt: reloadedDbSession.createdAt,
414
+ lastUpdatedAt: reloadedDbSession.updatedAt,
415
+ },
416
+ };
417
+
418
+ console.log("✅ Session recovered from database:", {
419
+ sessionId: recoveredSession.metadata?.sessionId,
420
+ currentRoute: recoveredSession.currentRoute?.title,
421
+ currentState: recoveredSession.currentState?.id,
422
+ extracted: recoveredSession.extracted,
423
+ });
424
+
425
+ // Load message history
426
+ const messages = await db.getSessionMessages(dbSession.id);
427
+ console.log(`📜 Loaded ${messages.length} messages from history`);
428
+
429
+ console.log("\n✅ Example complete!");
430
+ }
431
+
432
+ /**
433
+ * Advanced Example: With validation hooks
434
+ */
435
+ async function advancedExample() {
436
+ const db = new CustomDatabase();
437
+ const userId = "user_456";
438
+
439
+ const agent = new Agent({
440
+ name: "Smart Onboarding",
441
+ ai: new GeminiProvider({
442
+ apiKey: process.env.GEMINI_API_KEY!,
443
+ model: "models/gemini-2.0-flash-exp",
444
+ }),
445
+ hooks: {
446
+ // Validate and enrich extracted data
447
+ onExtractedUpdate: async (extracted, previous) => {
448
+ console.log("🔄 Data extracted, validating...");
449
+
450
+ // Normalize email
451
+ if (extracted.email) {
452
+ extracted.email = extracted.email.toLowerCase().trim();
453
+ }
454
+
455
+ // Normalize phone
456
+ if (extracted.phoneNumber) {
457
+ extracted.phoneNumber = extracted.phoneNumber.replace(/\D/g, "");
458
+ }
459
+
460
+ return extracted;
461
+ },
462
+ },
463
+ });
464
+
465
+ const route = agent.createRoute<OnboardingData>({
466
+ title: "Onboarding",
467
+ gatherSchema: {
468
+ type: "object",
469
+ properties: {
470
+ fullName: { type: "string" },
471
+ email: { type: "string" },
472
+ companyName: { type: "string" },
473
+ phoneNumber: { type: "string" },
474
+ },
475
+ required: ["fullName", "email", "companyName"],
476
+ },
477
+ });
478
+
479
+ route.initialState.transitionTo({
480
+ id: "collect_all",
481
+ chatState: "Collect all information",
482
+ gather: ["fullName", "email", "companyName", "phoneNumber"],
483
+ });
484
+
485
+ // Create database session
486
+ const dbSession = await db.createSession({
487
+ userId,
488
+ collectedData: {},
489
+ });
490
+
491
+ // Create agent session
492
+ let agentSession = createSession<OnboardingData>(dbSession.id, {
493
+ userId,
494
+ });
495
+
496
+ // Simulate conversation
497
+ const response = await agent.respond({
498
+ history: [
499
+ createMessageEvent(
500
+ EventSource.CUSTOMER,
501
+ "User",
502
+ "I'm Alice Johnson, alice@EXAMPLE.COM, working for TechCorp. Phone: (555) 123-4567"
503
+ ),
504
+ ],
505
+ session: agentSession,
506
+ });
507
+
508
+ console.log("🤖 Agent:", response.message);
509
+ console.log("📊 Normalized data:", response.session?.extracted);
510
+ // Shows: { email: "alice@example.com", phoneNumber: "5551234567", ... }
511
+
512
+ // Save to database
513
+ await db.updateSession(dbSession.id, {
514
+ currentRoute: response.session?.currentRoute?.id,
515
+ currentState: response.session?.currentState?.id,
516
+ collectedData: {
517
+ extracted: response.session?.extracted,
518
+ routeHistory: response.session?.routeHistory,
519
+ currentRouteTitle: response.session?.currentRoute?.title,
520
+ currentStateDescription: response.session?.currentState?.description,
521
+ metadata: response.session?.metadata,
522
+ },
523
+ });
524
+
525
+ console.log("✅ Validated data saved to custom database!");
526
+ }
527
+
528
+ // Run the example
529
+ if (require.main === module) {
530
+ example().catch(console.error);
531
+ }
532
+
533
+ export { example, advancedExample };
@@ -128,14 +128,16 @@ async function example() {
128
128
  },
129
129
  });
130
130
 
131
- // State flow with smart data gathering
131
+ // State flow with smart data gathering and custom IDs
132
132
  const askDestination = flightRoute.initialState.transitionTo({
133
+ id: "ask_destination", // Custom state ID for easier tracking
133
134
  chatState: "Ask where they want to fly",
134
135
  gather: ["destination"],
135
136
  skipIf: (extracted) => !!extracted.destination,
136
137
  });
137
138
 
138
139
  const askDates = askDestination.transitionTo({
140
+ id: "ask_dates", // Custom state ID
139
141
  chatState: "Ask about travel dates",
140
142
  gather: ["departureDate", "returnDate"],
141
143
  skipIf: (extracted) => !!extracted.departureDate,
@@ -143,6 +145,7 @@ async function example() {
143
145
  });
144
146
 
145
147
  const askPassengers = askDates.transitionTo({
148
+ id: "ask_passengers", // Custom state ID
146
149
  chatState: "Ask how many passengers",
147
150
  gather: ["passengers"],
148
151
  skipIf: (extracted) => !!extracted.passengers,
@@ -150,6 +153,7 @@ async function example() {
150
153
  });
151
154
 
152
155
  const askCabinClass = askPassengers.transitionTo({
156
+ id: "ask_cabin_class", // Custom state ID
153
157
  chatState: "Ask about cabin class preference",
154
158
  gather: ["cabinClass"],
155
159
  skipIf: (extracted) => !!extracted.cabinClass,
@@ -157,6 +161,7 @@ async function example() {
157
161
  });
158
162
 
159
163
  const confirmBooking = askCabinClass.transitionTo({
164
+ id: "confirm_booking", // Custom state ID
160
165
  chatState: "Present options and confirm booking details",
161
166
  requiredData: ["destination", "departureDate", "passengers", "cabinClass"],
162
167
  });
@@ -186,6 +191,10 @@ async function example() {
186
191
  const dbSessionId = sessionResult.sessionData.id;
187
192
 
188
193
  console.log("✨ Created new session:", dbSessionId);
194
+ console.log("📊 Session metadata:", {
195
+ sessionId: session.metadata?.sessionId, // Same as dbSessionId
196
+ createdAt: session.metadata?.createdAt,
197
+ });
189
198
  console.log("📊 Initial session state:", {
190
199
  currentRoute: session.currentRoute,
191
200
  extracted: session.extracted,
@@ -216,8 +225,10 @@ async function example() {
216
225
 
217
226
  console.log("🤖 Agent:", response1.message);
218
227
  console.log("📊 Session state after turn 1:", {
228
+ sessionId: response1.session?.metadata?.sessionId,
219
229
  currentRoute: response1.session?.currentRoute?.title,
220
- currentState: response1.session?.currentState?.id,
230
+ currentStateId: response1.session?.currentState?.id, // Custom ID like "ask_destination"
231
+ currentStateDescription: response1.session?.currentState?.description,
221
232
  extracted: response1.session?.extracted,
222
233
  });
223
234
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@falai/agent",
3
- "version": "0.5.3",
3
+ "version": "0.5.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",
package/src/core/State.ts CHANGED
@@ -83,7 +83,7 @@ export class State<TContext = unknown, TExtracted = unknown> {
83
83
  const targetState = new State<TContext, TExtracted>(
84
84
  this.routeId,
85
85
  spec.chatState,
86
- undefined,
86
+ spec.id, // Use custom ID if provided
87
87
  spec.gather,
88
88
  spec.skipIf,
89
89
  spec.requiredData
@@ -70,6 +70,8 @@ export interface RouteOptions<TExtracted = unknown> {
70
70
  * Specification for a state transition
71
71
  */
72
72
  export interface TransitionSpec<TContext = unknown, TExtracted = unknown> {
73
+ /** Custom ID for this state (optional - will generate deterministic ID if not provided) */
74
+ id?: string;
73
75
  /** Transition to a chat state with this description */
74
76
  chatState?: string;
75
77
  /** Transition to execute a tool */