@falai/agent 0.5.5 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/cjs/core/ResponseEngine.js +2 -2
  2. package/dist/cjs/core/ResponseEngine.js.map +1 -1
  3. package/dist/cjs/core/Route.d.ts +6 -1
  4. package/dist/cjs/core/Route.d.ts.map +1 -1
  5. package/dist/cjs/core/Route.js +19 -1
  6. package/dist/cjs/core/Route.js.map +1 -1
  7. package/dist/cjs/core/State.d.ts +1 -2
  8. package/dist/cjs/core/State.d.ts.map +1 -1
  9. package/dist/cjs/core/State.js +5 -6
  10. package/dist/cjs/core/State.js.map +1 -1
  11. package/dist/cjs/core/Transition.d.ts +2 -2
  12. package/dist/cjs/core/Transition.d.ts.map +1 -1
  13. package/dist/cjs/core/Transition.js +3 -2
  14. package/dist/cjs/core/Transition.js.map +1 -1
  15. package/dist/cjs/types/route.d.ts +15 -4
  16. package/dist/cjs/types/route.d.ts.map +1 -1
  17. package/dist/core/ResponseEngine.js +2 -2
  18. package/dist/core/ResponseEngine.js.map +1 -1
  19. package/dist/core/Route.d.ts +6 -1
  20. package/dist/core/Route.d.ts.map +1 -1
  21. package/dist/core/Route.js +19 -1
  22. package/dist/core/Route.js.map +1 -1
  23. package/dist/core/State.d.ts +1 -2
  24. package/dist/core/State.d.ts.map +1 -1
  25. package/dist/core/State.js +5 -6
  26. package/dist/core/State.js.map +1 -1
  27. package/dist/core/Transition.d.ts +2 -2
  28. package/dist/core/Transition.d.ts.map +1 -1
  29. package/dist/core/Transition.js +3 -2
  30. package/dist/core/Transition.js.map +1 -1
  31. package/dist/types/route.d.ts +15 -4
  32. package/dist/types/route.d.ts.map +1 -1
  33. package/docs/ADAPTERS.md +1 -1
  34. package/docs/API_REFERENCE.md +22 -25
  35. package/docs/ARCHITECTURE.md +18 -22
  36. package/docs/CONSTRUCTOR_OPTIONS.md +2 -2
  37. package/docs/CONTEXT_MANAGEMENT.md +1 -1
  38. package/docs/GETTING_STARTED.md +1 -1
  39. package/docs/PERSISTENCE.md +3 -3
  40. package/examples/business-onboarding.ts +86 -70
  41. package/examples/company-qna-agent.ts +4 -4
  42. package/examples/custom-database-persistence.ts +2 -2
  43. package/examples/declarative-agent.ts +3 -3
  44. package/examples/extracted-data-modification.ts +1 -1
  45. package/examples/healthcare-agent.ts +24 -30
  46. package/examples/openai-agent.ts +1 -1
  47. package/examples/opensearch-persistence.ts +2 -2
  48. package/examples/persistent-onboarding.ts +2 -2
  49. package/examples/prisma-persistence.ts +3 -3
  50. package/examples/redis-persistence.ts +3 -3
  51. package/examples/travel-agent.ts +73 -96
  52. package/package.json +1 -1
  53. package/src/core/ResponseEngine.ts +2 -2
  54. package/src/core/Route.ts +34 -3
  55. package/src/core/State.ts +6 -13
  56. package/src/core/Transition.ts +6 -3
  57. package/src/types/route.ts +15 -5
@@ -130,7 +130,7 @@ async function createHealthcareAgent() {
130
130
  title: "Schedule an Appointment",
131
131
  description: "Helps the patient find a time for their appointment.",
132
132
  conditions: ["The patient wants to schedule an appointment"],
133
- gatherSchema: {
133
+ extractionSchema: {
134
134
  type: "object",
135
135
  properties: {
136
136
  appointmentReason: {
@@ -161,25 +161,21 @@ async function createHealthcareAgent() {
161
161
  });
162
162
 
163
163
  // State 1: Gather appointment reason
164
- const gatherReason = schedulingRoute.initialState.transitionTo(
165
- {
166
- chatState: "Ask what the patient needs an appointment for",
167
- gather: ["appointmentReason"],
168
- skipIf: (extracted) => !!extracted.appointmentReason,
169
- },
170
- "Patient hasn't specified reason for appointment yet"
171
- );
164
+ const gatherReason = schedulingRoute.initialState.transitionTo({
165
+ chatState: "Ask what the patient needs an appointment for",
166
+ gather: ["appointmentReason"],
167
+ skipIf: (extracted) => !!extracted.appointmentReason,
168
+ condition: "Patient hasn't specified reason for appointment yet",
169
+ });
172
170
 
173
171
  // State 2: Check urgency and show available slots
174
- const checkUrgency = gatherReason.transitionTo(
175
- {
176
- chatState: "Check if this is urgent and show available slots",
177
- gather: ["urgency"],
178
- skipIf: (extracted) => !!extracted.urgency,
179
- requiredData: ["appointmentReason"],
180
- },
181
- "Reason provided, now assess urgency level"
182
- );
172
+ const checkUrgency = gatherReason.transitionTo({
173
+ chatState: "Check if this is urgent and show available slots",
174
+ gather: ["urgency"],
175
+ skipIf: (extracted) => !!extracted.urgency,
176
+ requiredData: ["appointmentReason"],
177
+ condition: "Reason provided, now assess urgency level",
178
+ });
183
179
 
184
180
  const showSlots = checkUrgency.transitionTo({
185
181
  toolState: getUpcomingSlots,
@@ -206,22 +202,20 @@ async function createHealthcareAgent() {
206
202
  requiredData: ["appointmentReason", "preferredTime", "preferredDate"],
207
203
  });
208
204
 
209
- const schedule = confirmDetails.transitionTo(
210
- {
211
- toolState: scheduleAppointment,
212
- requiredData: ["appointmentReason", "preferredTime", "preferredDate"],
213
- },
214
- "All details confirmed, book the appointment"
215
- );
205
+ const schedule = confirmDetails.transitionTo({
206
+ toolState: scheduleAppointment,
207
+ requiredData: ["appointmentReason", "preferredTime", "preferredDate"],
208
+ condition: "All details confirmed, book the appointment",
209
+ });
216
210
 
217
211
  const confirmation = schedule.transitionTo({
218
212
  chatState: "Confirm the appointment has been scheduled",
219
213
  });
220
214
 
221
- confirmation.transitionTo(
222
- { state: END_ROUTE },
223
- "Appointment booked successfully"
224
- );
215
+ confirmation.transitionTo({
216
+ state: END_ROUTE,
217
+ condition: "Appointment booked successfully",
218
+ });
225
219
 
226
220
  // Alternative path: no times work - show later slots
227
221
  const laterSlots = presentTimes.transitionTo({
@@ -250,7 +244,7 @@ async function createHealthcareAgent() {
250
244
  title: "Lab Results",
251
245
  description: "Retrieves the patient's lab results and explains them.",
252
246
  conditions: ["The patient wants to see their lab results"],
253
- gatherSchema: {
247
+ extractionSchema: {
254
248
  type: "object",
255
249
  properties: {
256
250
  testType: {
@@ -112,7 +112,7 @@ async function main() {
112
112
  title: "Check Weather",
113
113
  description: "Help user check weather for a location",
114
114
  conditions: ["User wants to know the weather"],
115
- gatherSchema: {
115
+ extractionSchema: {
116
116
  type: "object",
117
117
  properties: {
118
118
  location: {
@@ -105,7 +105,7 @@ async function example() {
105
105
  "User reports an issue or problem",
106
106
  "User is dissatisfied",
107
107
  ],
108
- gatherSchema: {
108
+ extractionSchema: {
109
109
  type: "object",
110
110
  properties: {
111
111
  category: {
@@ -337,7 +337,7 @@ async function analyticsExample() {
337
337
 
338
338
  const ticketRoute = agent.createRoute<TicketData>({
339
339
  title: "Analyze Support Ticket",
340
- gatherSchema: {
340
+ extractionSchema: {
341
341
  type: "object",
342
342
  properties: {
343
343
  ticketType: { type: "string" },
@@ -244,7 +244,7 @@ async function createPersistentOnboardingAgent(sessionId: string) {
244
244
  title: "Business Onboarding",
245
245
  description: "Guide user through business information collection",
246
246
  conditions: ["User is onboarding their business"],
247
- gatherSchema: {
247
+ extractionSchema: {
248
248
  type: "object",
249
249
  properties: {
250
250
  businessName: {
@@ -503,7 +503,7 @@ async function main() {
503
503
  * - Always-on routing respects intent changes
504
504
  *
505
505
  * ✅ PATTERN 2: Schema-First Data Extraction
506
- * - gatherSchema: Define data contracts upfront
506
+ * - extractionSchema: Define data contracts upfront
507
507
  * - Type-safe extraction throughout conversation
508
508
  * - skipIf functions for deterministic state logic
509
509
  * - requiredData arrays for prerequisites
@@ -96,7 +96,7 @@ async function example() {
96
96
  "User wants to book a flight",
97
97
  "User mentions travel, flying, or booking tickets",
98
98
  ],
99
- gatherSchema: {
99
+ extractionSchema: {
100
100
  type: "object",
101
101
  properties: {
102
102
  destination: {
@@ -413,7 +413,7 @@ async function advancedExample() {
413
413
  const onboardingRoute = agent.createRoute<OnboardingData>({
414
414
  title: "User Onboarding",
415
415
  description: "Collect user information for account setup",
416
- gatherSchema: {
416
+ extractionSchema: {
417
417
  type: "object",
418
418
  properties: {
419
419
  fullName: { type: "string" },
@@ -514,7 +514,7 @@ async function quickStart() {
514
514
  // Create a simple contact form route
515
515
  const contactRoute = agent.createRoute<ContactFormData>({
516
516
  title: "Contact Form",
517
- gatherSchema: {
517
+ extractionSchema: {
518
518
  type: "object",
519
519
  properties: {
520
520
  name: { type: "string" },
@@ -88,7 +88,7 @@ async function example() {
88
88
  "User wants to report a problem",
89
89
  "User mentions support, help, or issue",
90
90
  ],
91
- gatherSchema: {
91
+ extractionSchema: {
92
92
  type: "object",
93
93
  properties: {
94
94
  issue: {
@@ -286,7 +286,7 @@ async function highThroughputExample() {
286
286
  // Simple chat route that extracts topic and sentiment
287
287
  const chatRoute = agent.createRoute<QuickChatData>({
288
288
  title: "General Chat",
289
- gatherSchema: {
289
+ extractionSchema: {
290
290
  type: "object",
291
291
  properties: {
292
292
  topic: {
@@ -364,7 +364,7 @@ async function sessionRecoveryExample() {
364
364
 
365
365
  const orderRoute = agent.createRoute<OrderData>({
366
366
  title: "Place Order",
367
- gatherSchema: {
367
+ extractionSchema: {
368
368
  type: "object",
369
369
  properties: {
370
370
  productId: { type: "string" },
@@ -223,7 +223,7 @@ async function createTravelAgent() {
223
223
  description:
224
224
  "Helps the customer find and book a flight to their desired destination.",
225
225
  conditions: ["The customer wants to book a flight"],
226
- gatherSchema: {
226
+ extractionSchema: {
227
227
  type: "object",
228
228
  properties: {
229
229
  destination: {
@@ -270,85 +270,67 @@ async function createTravelAgent() {
270
270
  });
271
271
 
272
272
  // Build the route flow with data extraction and smart state progression
273
- const askDestination = flightBookingRoute.initialState.transitionTo(
274
- {
275
- chatState: "Ask about the destination",
276
- gather: ["destination"],
277
- skipIf: (extracted) => !!extracted.destination,
278
- },
279
- "Customer needs to specify their travel destination"
280
- );
273
+ const askDestination = flightBookingRoute.initialState.transitionTo({
274
+ chatState: "Ask about the destination",
275
+ gather: ["destination"],
276
+ skipIf: (extracted) => !!extracted.destination,
277
+ condition: "Customer needs to specify their travel destination",
278
+ });
281
279
 
282
- const enrichDestination = askDestination.transitionTo(
283
- {
284
- toolState: lookupDestinationCode,
285
- requiredData: ["destination"],
286
- },
287
- "Destination provided, lookup airport code"
288
- );
280
+ const enrichDestination = askDestination.transitionTo({
281
+ toolState: lookupDestinationCode,
282
+ requiredData: ["destination"],
283
+ condition: "Destination provided, lookup airport code",
284
+ });
289
285
 
290
- const askDates = enrichDestination.transitionTo(
291
- {
292
- chatState: "Ask about preferred travel dates",
293
- gather: ["departureDate"],
294
- skipIf: (extracted) => !!extracted.departureDate,
295
- requiredData: ["destination"],
296
- },
297
- "Destination confirmed, need travel dates"
298
- );
286
+ const askDates = enrichDestination.transitionTo({
287
+ chatState: "Ask about preferred travel dates",
288
+ gather: ["departureDate"],
289
+ skipIf: (extracted) => !!extracted.departureDate,
290
+ requiredData: ["destination"],
291
+ condition: "Destination confirmed, need travel dates",
292
+ });
299
293
 
300
- const askPassengers = askDates.transitionTo(
301
- {
302
- chatState: "Ask for number of passengers",
303
- gather: ["passengers"],
304
- skipIf: (extracted) => !!extracted.passengers,
305
- requiredData: ["destination", "departureDate"],
306
- },
307
- "Dates confirmed, need passenger count"
308
- );
294
+ const askPassengers = askDates.transitionTo({
295
+ chatState: "Ask for number of passengers",
296
+ gather: ["passengers"],
297
+ skipIf: (extracted) => !!extracted.passengers,
298
+ requiredData: ["destination", "departureDate"],
299
+ condition: "Dates confirmed, need passenger count",
300
+ });
309
301
 
310
- const searchFlightsState = askPassengers.transitionTo(
311
- {
312
- toolState: searchFlights,
313
- // Triggered when shouldSearchFlights flag is set by hook
314
- },
315
- "All basic info gathered, search for available flights"
316
- );
302
+ const searchFlightsState = askPassengers.transitionTo({
303
+ toolState: searchFlights,
304
+ // Triggered when shouldSearchFlights flag is set by hook
305
+ condition: "All basic info gathered, search for available flights",
306
+ });
317
307
 
318
- const presentFlights = searchFlightsState.transitionTo(
319
- {
320
- chatState: "Present available flights and ask which one works for them",
321
- },
322
- "Flight search complete, present options to customer"
323
- );
308
+ const presentFlights = searchFlightsState.transitionTo({
309
+ chatState: "Present available flights and ask which one works for them",
310
+ condition: "Flight search complete, present options to customer",
311
+ });
324
312
 
325
313
  // Happy path: customer selects a flight
326
- const confirmBooking = presentFlights.transitionTo(
327
- {
328
- chatState: "Confirm booking details before proceeding",
329
- gather: ["cabinClass", "urgency"], // Additional optional data
330
- },
331
- "Customer interested in a flight, confirm booking details"
332
- );
314
+ const confirmBooking = presentFlights.transitionTo({
315
+ chatState: "Confirm booking details before proceeding",
316
+ gather: ["cabinClass", "urgency"], // Additional optional data
317
+ condition: "Customer interested in a flight, confirm booking details",
318
+ });
333
319
 
334
- const bookFlightState = confirmBooking.transitionTo(
335
- {
336
- toolState: bookFlight,
337
- },
338
- "Customer confirmed, proceed with booking"
339
- );
320
+ const bookFlightState = confirmBooking.transitionTo({
321
+ toolState: bookFlight,
322
+ condition: "Customer confirmed, proceed with booking",
323
+ });
340
324
 
341
- const provideConfirmation = bookFlightState.transitionTo(
342
- {
343
- chatState: "Provide confirmation number and booking summary",
344
- },
345
- "Booking completed successfully"
346
- );
325
+ const provideConfirmation = bookFlightState.transitionTo({
326
+ chatState: "Provide confirmation number and booking summary",
327
+ condition: "Booking completed successfully",
328
+ });
347
329
 
348
- provideConfirmation.transitionTo(
349
- { state: END_ROUTE },
350
- "Customer has confirmation, booking flow complete"
351
- );
330
+ provideConfirmation.transitionTo({
331
+ state: END_ROUTE,
332
+ condition: "Customer has confirmation, booking flow complete",
333
+ });
352
334
 
353
335
  // Add route-specific guidelines
354
336
  flightBookingRoute.createGuideline({
@@ -370,7 +352,7 @@ async function createTravelAgent() {
370
352
  description:
371
353
  "Retrieves the customer's booking status and provides relevant information.",
372
354
  conditions: ["The customer wants to check their booking status"],
373
- gatherSchema: {
355
+ extractionSchema: {
374
356
  type: "object",
375
357
  properties: {
376
358
  confirmationNumber: {
@@ -386,34 +368,29 @@ async function createTravelAgent() {
386
368
  },
387
369
  });
388
370
 
389
- const askConfirmation = bookingStatusRoute.initialState.transitionTo(
390
- {
391
- chatState: "Ask for the confirmation number or booking reference",
392
- gather: ["confirmationNumber"],
393
- skipIf: (extracted) => !!extracted.confirmationNumber,
394
- },
395
- "Customer wants to check booking status but hasn't provided confirmation number"
396
- );
371
+ const askConfirmation = bookingStatusRoute.initialState.transitionTo({
372
+ chatState: "Ask for the confirmation number or booking reference",
373
+ gather: ["confirmationNumber"],
374
+ skipIf: (extracted) => !!extracted.confirmationNumber,
375
+ condition:
376
+ "Customer wants to check booking status but hasn't provided confirmation number",
377
+ });
397
378
 
398
- const checkStatus = askConfirmation.transitionTo(
399
- {
400
- toolState: getBookingStatus,
401
- requiredData: ["confirmationNumber"],
402
- },
403
- "Confirmation number provided, look up booking details"
404
- );
379
+ const checkStatus = askConfirmation.transitionTo({
380
+ toolState: getBookingStatus,
381
+ requiredData: ["confirmationNumber"],
382
+ condition: "Confirmation number provided, look up booking details",
383
+ });
405
384
 
406
- const provideStatus = checkStatus.transitionTo(
407
- {
408
- chatState: "Provide booking status and relevant information",
409
- },
410
- "Booking status retrieved successfully"
411
- );
385
+ const provideStatus = checkStatus.transitionTo({
386
+ chatState: "Provide booking status and relevant information",
387
+ condition: "Booking status retrieved successfully",
388
+ });
412
389
 
413
- provideStatus.transitionTo(
414
- { state: END_ROUTE },
415
- "Booking information provided to customer"
416
- );
390
+ provideStatus.transitionTo({
391
+ state: END_ROUTE,
392
+ condition: "Booking information provided to customer",
393
+ });
417
394
 
418
395
  // Global guidelines
419
396
  agent.createGuideline({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@falai/agent",
3
- "version": "0.5.5",
3
+ "version": "0.6.0",
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",
@@ -30,9 +30,9 @@ export class ResponseEngine<TContext = unknown> {
30
30
  }
31
31
 
32
32
  // Add gather fields from current state
33
- if (currentState?.gatherFields && route.gatherSchema?.properties) {
33
+ if (currentState?.gatherFields && route.extractionSchema?.properties) {
34
34
  for (const field of currentState.gatherFields) {
35
- const fieldSchema = route.gatherSchema.properties[field];
35
+ const fieldSchema = route.extractionSchema.properties[field];
36
36
  if (fieldSchema) {
37
37
  base.properties![field] = fieldSchema;
38
38
  }
package/src/core/Route.ts CHANGED
@@ -2,7 +2,12 @@
2
2
  * Route (Journey) DSL implementation
3
3
  */
4
4
 
5
- import type { RouteOptions, RouteRef } from "../types/route";
5
+ import type {
6
+ RouteOptions,
7
+ RouteRef,
8
+ TransitionSpec,
9
+ TransitionResult,
10
+ } from "../types/route";
6
11
  import type { StructuredSchema } from "../types/schema";
7
12
  import type { Guideline } from "../types/agent";
8
13
 
@@ -22,7 +27,7 @@ export class Route<TContext = unknown, TExtracted = unknown> {
22
27
  public readonly prohibitions: string[];
23
28
  public readonly initialState: State<TContext, TExtracted>;
24
29
  public readonly responseOutputSchema?: StructuredSchema;
25
- public readonly gatherSchema?: StructuredSchema;
30
+ public readonly extractionSchema?: StructuredSchema;
26
31
  public readonly initialData?: Partial<TExtracted>;
27
32
  private routingExtrasSchema?: StructuredSchema;
28
33
  private guidelines: Guideline[] = [];
@@ -42,7 +47,7 @@ export class Route<TContext = unknown, TExtracted = unknown> {
42
47
  );
43
48
  this.routingExtrasSchema = options.routingExtrasSchema;
44
49
  this.responseOutputSchema = options.responseOutputSchema;
45
- this.gatherSchema = options.gatherSchema;
50
+ this.extractionSchema = options.extractionSchema;
46
51
  this.initialData = options.initialData;
47
52
 
48
53
  // Initialize guidelines from options
@@ -51,6 +56,32 @@ export class Route<TContext = unknown, TExtracted = unknown> {
51
56
  this.createGuideline(guideline);
52
57
  });
53
58
  }
59
+
60
+ // Build sequential steps if provided
61
+ if (options.steps && options.steps.length > 0) {
62
+ this.buildSequentialSteps(options.steps);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Build a sequential state machine from an array of steps
68
+ * @private
69
+ */
70
+ private buildSequentialSteps(
71
+ steps: Array<TransitionSpec<TContext, TExtracted>>
72
+ ): void {
73
+ // Import END_ROUTE dynamically to avoid circular dependency
74
+ const END_ROUTE = Symbol.for("END_ROUTE");
75
+
76
+ let currentState: TransitionResult<TContext, TExtracted> =
77
+ this.initialState;
78
+
79
+ for (const step of steps) {
80
+ currentState = currentState.transitionTo(step);
81
+ }
82
+
83
+ // End the route
84
+ currentState.transitionTo({ state: END_ROUTE });
54
85
  }
55
86
 
56
87
  /**
package/src/core/State.ts CHANGED
@@ -43,12 +43,10 @@ export class State<TContext = unknown, TExtracted = unknown> {
43
43
  * Create a transition from this state to another
44
44
  *
45
45
  * @param spec - Transition specification (chatState, toolState, or direct state)
46
- * @param condition - Optional condition for this transition
47
46
  * @returns TransitionResult that supports chaining
48
47
  */
49
48
  transitionTo(
50
- spec: TransitionSpec<TContext, TExtracted>,
51
- condition?: string
49
+ spec: TransitionSpec<TContext, TExtracted>
52
50
  ): TransitionResult<TContext, TExtracted> {
53
51
  // Handle END_ROUTE
54
52
  if (
@@ -58,8 +56,7 @@ export class State<TContext = unknown, TExtracted = unknown> {
58
56
  ) {
59
57
  const endTransition = new Transition<TContext, TExtracted>(
60
58
  this.getRef(),
61
- { state: END_ROUTE },
62
- condition
59
+ { state: END_ROUTE, condition: spec.condition }
63
60
  );
64
61
  this.transitions.push(endTransition);
65
62
 
@@ -71,8 +68,7 @@ export class State<TContext = unknown, TExtracted = unknown> {
71
68
  if (spec.state && typeof spec.state !== "symbol") {
72
69
  const transition = new Transition<TContext, TExtracted>(
73
70
  this.getRef(),
74
- spec,
75
- condition
71
+ spec
76
72
  );
77
73
  this.transitions.push(transition);
78
74
 
@@ -90,8 +86,7 @@ export class State<TContext = unknown, TExtracted = unknown> {
90
86
  );
91
87
  const transition = new Transition<TContext, TExtracted>(
92
88
  this.getRef(),
93
- spec,
94
- condition
89
+ spec
95
90
  );
96
91
  transition.setTarget(targetState);
97
92
 
@@ -160,10 +155,8 @@ export class State<TContext = unknown, TExtracted = unknown> {
160
155
 
161
156
  return {
162
157
  ...ref,
163
- transitionTo: (
164
- spec: TransitionSpec<TContext, TExtracted>,
165
- condition?: string
166
- ) => stateInstance.transitionTo(spec, condition),
158
+ transitionTo: (spec: TransitionSpec<TContext, TExtracted>) =>
159
+ stateInstance.transitionTo(spec),
167
160
  };
168
161
  }
169
162
 
@@ -10,12 +10,15 @@ import type { State } from "./State";
10
10
  */
11
11
  export class Transition<TContext = unknown, TExtracted = unknown> {
12
12
  private target?: State<TContext, TExtracted>;
13
+ public readonly condition?: string;
13
14
 
14
15
  constructor(
15
16
  public readonly source: StateRef,
16
- public readonly spec: TransitionSpec<TContext, TExtracted>,
17
- public readonly condition?: string
18
- ) {}
17
+ public readonly spec: TransitionSpec<TContext, TExtracted>
18
+ ) {
19
+ // Extract condition from spec for convenience
20
+ this.condition = spec.condition;
21
+ }
19
22
 
20
23
  /**
21
24
  * Set the target state for this transition
@@ -30,7 +30,7 @@ import type { Guideline } from "./agent";
30
30
 
31
31
  /**
32
32
  * Options for creating a route
33
- * @template TExtracted - Type of data extracted throughout the route (inferred from gatherSchema)
33
+ * @template TExtracted - Type of data extracted throughout the route (inferred from extractionSchema)
34
34
  */
35
35
  export interface RouteOptions<TExtracted = unknown> {
36
36
  /** Custom ID for the route (optional - will generate deterministic ID from title if not provided) */
@@ -57,13 +57,19 @@ export interface RouteOptions<TExtracted = unknown> {
57
57
  * NEW: Schema defining data to extract throughout this route
58
58
  * This creates a type-safe contract for what data the route collects
59
59
  */
60
- gatherSchema?: StructuredSchema;
60
+ extractionSchema?: StructuredSchema;
61
61
  /**
62
62
  * NEW: Initial data to pre-populate when entering this route
63
63
  * Useful for restoring sessions or pre-filling known information
64
64
  * States with skipIf conditions will be automatically bypassed if data is present
65
65
  */
66
66
  initialData?: Partial<TExtracted>;
67
+ /**
68
+ * NEW: Sequential steps for simple linear flows
69
+ * If provided, automatically chains the steps from initialState to END_ROUTE
70
+ * For complex flows with branching, build the state machine manually instead
71
+ */
72
+ steps?: TransitionSpec<unknown, TExtracted>[];
67
73
  }
68
74
 
69
75
  /**
@@ -81,7 +87,7 @@ export interface TransitionSpec<TContext = unknown, TExtracted = unknown> {
81
87
  state?: StateRef | symbol;
82
88
  /**
83
89
  * NEW: Fields to gather from the conversation in this state
84
- * These should match keys in the route's gatherSchema
90
+ * These should match keys in the route's extractionSchema
85
91
  */
86
92
  gather?: string[];
87
93
  /**
@@ -97,6 +103,11 @@ export interface TransitionSpec<TContext = unknown, TExtracted = unknown> {
97
103
  * Uses string[] for developer-friendly usage (same as gather)
98
104
  */
99
105
  requiredData?: string[];
106
+ /**
107
+ * Optional condition for this transition
108
+ * Description of when this transition should be taken
109
+ */
110
+ condition?: string;
100
111
  }
101
112
 
102
113
  /**
@@ -107,7 +118,6 @@ export interface TransitionResult<TContext = unknown, TExtracted = unknown>
107
118
  extends StateRef {
108
119
  /** Allow chaining transitions */
109
120
  transitionTo: (
110
- spec: TransitionSpec<TContext, TExtracted>,
111
- condition?: string
121
+ spec: TransitionSpec<TContext, TExtracted>
112
122
  ) => TransitionResult<TContext, TExtracted>;
113
123
  }