@falai/agent 2.0.1 → 2.1.1

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 (126) hide show
  1. package/dist/cjs/core/Agent.d.ts +1 -1
  2. package/dist/cjs/core/Agent.js +5 -5
  3. package/dist/cjs/core/Agent.js.map +1 -1
  4. package/dist/cjs/core/AutoChainExecutor.d.ts +2 -2
  5. package/dist/cjs/core/AutoChainExecutor.js +2 -2
  6. package/dist/cjs/core/AutoChainExecutor.js.map +1 -1
  7. package/dist/cjs/core/PromptComposer.d.ts +1 -1
  8. package/dist/cjs/core/PromptComposer.js +2 -2
  9. package/dist/cjs/core/PromptComposer.js.map +1 -1
  10. package/dist/cjs/core/ResponseEngine.d.ts +1 -1
  11. package/dist/cjs/core/ResponseModal.js +6 -6
  12. package/dist/cjs/core/ResponseModal.js.map +1 -1
  13. package/dist/cjs/core/ResponsePipeline.d.ts +2 -2
  14. package/dist/cjs/core/ResponsePipeline.d.ts.map +1 -1
  15. package/dist/cjs/core/ResponsePipeline.js.map +1 -1
  16. package/dist/cjs/core/SignalProcessor.d.ts +3 -3
  17. package/dist/cjs/core/SignalProcessor.d.ts.map +1 -1
  18. package/dist/cjs/core/SignalProcessor.js +1 -1
  19. package/dist/cjs/core/SignalProcessor.js.map +1 -1
  20. package/dist/cjs/core/Step.d.ts +1 -1
  21. package/dist/cjs/core/Step.js +1 -1
  22. package/dist/cjs/core/ToolManager.d.ts +1 -1
  23. package/dist/cjs/core/ToolManager.js +1 -1
  24. package/dist/cjs/core/flow-namespace.d.ts +4 -4
  25. package/dist/cjs/core/flow-namespace.d.ts.map +1 -1
  26. package/dist/cjs/core/flow-namespace.js +14 -25
  27. package/dist/cjs/core/flow-namespace.js.map +1 -1
  28. package/dist/cjs/index.d.ts +3 -3
  29. package/dist/cjs/index.d.ts.map +1 -1
  30. package/dist/cjs/index.js +2 -2
  31. package/dist/cjs/index.js.map +1 -1
  32. package/dist/cjs/types/agent.d.ts +1 -1
  33. package/dist/cjs/types/agent.d.ts.map +1 -1
  34. package/dist/cjs/types/ai.d.ts +1 -1
  35. package/dist/cjs/types/ai.js +1 -1
  36. package/dist/cjs/types/flow.d.ts +33 -25
  37. package/dist/cjs/types/flow.d.ts.map +1 -1
  38. package/dist/cjs/types/index.d.ts +1 -1
  39. package/dist/cjs/types/index.d.ts.map +1 -1
  40. package/dist/cjs/types/index.js.map +1 -1
  41. package/dist/cjs/types/signals.d.ts +4 -5
  42. package/dist/cjs/types/signals.d.ts.map +1 -1
  43. package/dist/cjs/utils/session.d.ts +1 -1
  44. package/dist/cjs/utils/session.js +4 -4
  45. package/dist/cjs/utils/session.js.map +1 -1
  46. package/dist/core/Agent.d.ts +1 -1
  47. package/dist/core/Agent.js +5 -5
  48. package/dist/core/Agent.js.map +1 -1
  49. package/dist/core/AutoChainExecutor.d.ts +2 -2
  50. package/dist/core/AutoChainExecutor.js +2 -2
  51. package/dist/core/AutoChainExecutor.js.map +1 -1
  52. package/dist/core/PromptComposer.d.ts +1 -1
  53. package/dist/core/PromptComposer.js +2 -2
  54. package/dist/core/PromptComposer.js.map +1 -1
  55. package/dist/core/ResponseEngine.d.ts +1 -1
  56. package/dist/core/ResponseModal.js +6 -6
  57. package/dist/core/ResponseModal.js.map +1 -1
  58. package/dist/core/ResponsePipeline.d.ts +2 -2
  59. package/dist/core/ResponsePipeline.d.ts.map +1 -1
  60. package/dist/core/ResponsePipeline.js.map +1 -1
  61. package/dist/core/SignalProcessor.d.ts +3 -3
  62. package/dist/core/SignalProcessor.d.ts.map +1 -1
  63. package/dist/core/SignalProcessor.js +1 -1
  64. package/dist/core/SignalProcessor.js.map +1 -1
  65. package/dist/core/Step.d.ts +1 -1
  66. package/dist/core/Step.js +1 -1
  67. package/dist/core/ToolManager.d.ts +1 -1
  68. package/dist/core/ToolManager.js +1 -1
  69. package/dist/core/flow-namespace.d.ts +4 -4
  70. package/dist/core/flow-namespace.d.ts.map +1 -1
  71. package/dist/core/flow-namespace.js +14 -25
  72. package/dist/core/flow-namespace.js.map +1 -1
  73. package/dist/index.d.ts +3 -3
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +2 -2
  76. package/dist/index.js.map +1 -1
  77. package/dist/types/agent.d.ts +1 -1
  78. package/dist/types/agent.d.ts.map +1 -1
  79. package/dist/types/ai.d.ts +1 -1
  80. package/dist/types/ai.js +1 -1
  81. package/dist/types/flow.d.ts +33 -25
  82. package/dist/types/flow.d.ts.map +1 -1
  83. package/dist/types/index.d.ts +1 -1
  84. package/dist/types/index.d.ts.map +1 -1
  85. package/dist/types/index.js.map +1 -1
  86. package/dist/types/signals.d.ts +4 -5
  87. package/dist/types/signals.d.ts.map +1 -1
  88. package/dist/utils/session.d.ts +1 -1
  89. package/dist/utils/session.js +4 -4
  90. package/dist/utils/session.js.map +1 -1
  91. package/docs/concepts/architecture.md +19 -26
  92. package/docs/concepts/directives.md +53 -84
  93. package/docs/concepts/pipeline.md +4 -4
  94. package/docs/guides/flow-control.md +12 -13
  95. package/docs/guides/streaming.md +1 -1
  96. package/docs/migration/README.md +1 -1
  97. package/docs/migration/v1-to-v2.md +507 -5
  98. package/docs/reference/adapters.md +1 -1
  99. package/docs/reference/directive.md +9 -10
  100. package/docs/reference/flow.md +3 -3
  101. package/docs/reference/instruction.md +1 -1
  102. package/docs/reference/signals.md +3 -4
  103. package/docs/reference/step.md +9 -10
  104. package/docs/reference/tool.md +1 -1
  105. package/docs/start/02-first-agent.md +1 -2
  106. package/examples/tsconfig.json +1 -3
  107. package/package.json +9 -5
  108. package/src/core/Agent.ts +7 -7
  109. package/src/core/AutoChainExecutor.ts +3 -3
  110. package/src/core/PromptComposer.ts +2 -2
  111. package/src/core/ResponseEngine.ts +1 -1
  112. package/src/core/ResponseModal.ts +18 -18
  113. package/src/core/ResponsePipeline.ts +1 -2
  114. package/src/core/SignalProcessor.ts +7 -7
  115. package/src/core/Step.ts +1 -1
  116. package/src/core/ToolManager.ts +1 -1
  117. package/src/core/flow-namespace.ts +32 -53
  118. package/src/index.ts +2 -3
  119. package/src/types/agent.ts +1 -1
  120. package/src/types/ai.ts +1 -1
  121. package/src/types/flow.ts +41 -26
  122. package/src/types/index.ts +0 -1
  123. package/src/types/signals.ts +4 -5
  124. package/src/utils/session.ts +5 -5
  125. package/docs/migration/route-to-flow.md +0 -561
  126. package/docs/reference/pre-directive.md +0 -131
@@ -96,7 +96,7 @@ db.sessions.updateMany({}, { $unset: { pendingTransition: "" } });
96
96
 
97
97
  ### Redis
98
98
 
99
- Extend your migration Lua script (see the [route-to-flow migration](./route-to-flow.md) for the pattern):
99
+ Extend your migration Lua script (see the [Route → Flow rename](#3-route--flow-rename) Redis section for the pattern):
100
100
 
101
101
  ```lua
102
102
  local cursor = "0"
@@ -208,7 +208,7 @@ type StoppedReason =
208
208
  | 'aborted' // { abort: '...' } directive
209
209
  | 'goto' // goTo/goToStep directive short-circuited
210
210
  | 'reset' // { reset: true } directive
211
- | 'halt' // PreDirective halt: true
211
+ | 'halt' // Directive halt: true (pre-LLM only)
212
212
  | 'reply' // Step.reply or directive reply (LLM skipped)
213
213
  | 'max_auto_steps' // auto-step chain cap hit
214
214
  | 'prepare_error' // unchanged
@@ -221,9 +221,512 @@ type StoppedReason =
221
221
 
222
222
  ## 3. Route → Flow Rename
223
223
 
224
- The `Route` domain noun was renamed to `Flow` in a minor breaking bump that ships before v2. v2 assumes `Flow` naming everywhere `Route` / `createRoute` / `session.currentRoute` and the corresponding persistence columns no longer exist.
224
+ The `Route` domain noun has been renamed to `Flow` across the entire `@falai/agent` package. This is a clean break with no compatibility shims, no dual-naming layer, and no runtime fallback for legacy field names. Every public symbol, configuration option, persisted column/field, adapter method, constant, error class, and utility function that referenced "Route" as a noun now uses "Flow". The verb form `route()` and the gerund "routing" (as used in prose and the `routing.ts` module) are preserved — routing-as-an-action remains the correct verb for selecting a flow.
225
+
226
+ ### Symbol Rename Table
227
+
228
+ | Old | New | Layer | Action |
229
+ |-----|-----|-------|--------|
230
+ | `Route` (class) | `Flow` | Core | Update imports and instantiation |
231
+ | `RouteOptions` | `FlowOptions` | Type | Update type annotations |
232
+ | `RouteRef` | `FlowRef` | Type | Update type annotations |
233
+ | `RouteTransitionConfig` | `FlowTransitionConfig` | Type | Update type annotations |
234
+ | `RouteCompletionHandler` | `FlowCompletionHandler` | Type | Update type annotations |
235
+ | `RouteLifecycleHooks` | `FlowLifecycleHooks` | Type | Update type annotations |
236
+ | `RoutingEngine` | `FlowRouter` | Core | Update imports and references |
237
+ | `RoutingEngineOptions` | `FlowRouterOptions` | Type | Update type annotations |
238
+ | `RoutingDecisionOutput` | `FlowRoutingDecisionOutput` | Type | Update type annotations |
239
+ | `RouteConfigurationError` | `FlowConfigurationError` | Error | Update catch blocks |
240
+ | `END_ROUTE` | Removed | Constant | Implicit terminus — remove all references |
241
+ | `END_ROUTE_ID` | Removed | Constant | Implicit terminus — remove all references |
242
+ | `generateRouteId` | `generateFlowId` | Utility | Update calls |
243
+ | `enterRoute` | `enterFlow` | Utility | Update calls |
244
+ | `StepRef.routeId` | `StepRef.flowId` | Type | Update field access |
245
+
246
+ #### Preserved (verb-form carve-outs)
247
+
248
+ These are **not** renamed:
249
+
250
+ - `route()` method on `FlowRouter` (verb form)
251
+ - `RoutingDecision` type (describes the act of routing)
252
+ - `RoutingSchemaOptions` type
253
+ - `buildRoutingPrompt` method
254
+ - `getCandidateStepsWithConditions` method (returns Steps)
255
+ - `src/types/routing.ts` file path
256
+ - All "routing" prose in documentation
257
+
258
+ ### Configuration Rename Table
259
+
260
+ | Old | New | Location |
261
+ |-----|-----|----------|
262
+ | `AgentOptions.routes` | `AgentOptions.flows` | Agent constructor |
263
+ | `AgentOptions.routeSwitchMargin` | `AgentOptions.flowSwitchMargin` | Agent constructor |
264
+ | `agent.routeSwitchMargin` | `agent.flowSwitchMargin` | Getter/setter |
265
+ | `agent.createRoute(...)` | `agent.createFlow(...)` | Method call |
266
+ | `agent.getRoutes()` | `agent.getFlows()` | Method call |
267
+ | `agent.nextStepRoute(...)` | `agent.nextStepFlow(...)` | Method call |
268
+ | `agent.getRoutingEngine()` | `agent.getFlowRouter()` | Method call |
269
+ | `agent.routes` | `agent.flows` | Getter |
270
+ | `Guideline.scope: 'route'` | `Guideline.scope: 'flow'` | Guideline config |
271
+ | `Guideline.route` | `Guideline.flow` | Guideline config |
272
+ | `RoutingDecision.routes` | `RoutingDecision.flows` | Field access |
273
+
274
+ ### Session State Rename Table
275
+
276
+ | Old | New | Notes |
277
+ |-----|-----|-------|
278
+ | `SessionState.currentRoute` | `SessionState.currentFlow` | Runtime shape |
279
+ | `SessionState.routeHistory` | `SessionState.flowHistory` | Runtime shape |
280
+ | `flowHistory[].routeId` | `flowHistory[].flowId` | History item shape |
281
+ | `PendingTransition.targetRouteId` | `PendingTransition.targetFlowId` | Transition config |
282
+ | `PendingTransition.reason: "route_complete"` | `PendingTransition.reason: "flow_complete"` | String literal |
283
+ | `SessionData.currentRoute` | `SessionData.currentFlow` | Persistence shape |
284
+ | `CollectedStateData.routeHistory` | `CollectedStateData.flowHistory` | Persistence shape |
285
+ | `CollectedStateData.currentRouteTitle` | `CollectedStateData.currentFlowTitle` | Persistence shape |
286
+ | `MessageData.route` | `MessageData.flow` | Persistence shape |
287
+ | `SaveMessageOptions.route` | `SaveMessageOptions.flow` | Persistence shape |
288
+
289
+ #### StoppedReason Literals
225
290
 
226
- If you haven't run that migration yet, start there: see **[Route → Flow rename](./route-to-flow.md)** for the full rename table, schema changes per adapter, and Redis/OpenSearch backfill scripts. Once that lands, return here for the rest of the v2 changes.
291
+ | Old | New |
292
+ |-----|-----|
293
+ | `'end_route'` | Removed — use `'last_step'` or `'completed'` |
294
+ | `'route_complete'` | `'last_step'` (no successor) or `'completed'` (explicit directive) |
295
+
296
+ ### Adapter Method Rename Table
297
+
298
+ | Adapter | Old Method | New Method |
299
+ |---------|-----------|------------|
300
+ | `MemoryAdapter` | `updateRouteStep()` | `updateFlowStep()` |
301
+ | `PrismaAdapter` | `updateRouteStep()` | `updateFlowStep()` |
302
+ | `RedisAdapter` | `updateRouteStep()` | `updateFlowStep()` |
303
+ | `MongoAdapter` | `updateRouteStep()` | `updateFlowStep()` |
304
+ | `PostgreSQLAdapter` | `updateRouteStep()` | `updateFlowStep()` |
305
+ | `SQLiteAdapter` | `updateRouteStep()` | `updateFlowStep()` |
306
+ | `OpenSearchAdapter` | `updateRouteStep()` | `updateFlowStep()` |
307
+ | `PersistenceManager` | `updateRouteStep()` | `updateFlowStep()` |
308
+ | `SessionRepository` (interface) | `updateRouteStep()` | `updateFlowStep()` |
309
+
310
+ If you implement a custom adapter, rename your `updateRouteStep` method to `updateFlowStep`.
311
+
312
+ ### Per-Adapter Data Migration
313
+
314
+ The framework no longer reads or writes the legacy field/column names. You must migrate your persisted data before deploying the new version.
315
+
316
+ #### PostgreSQL
317
+
318
+ ```sql
319
+ -- Sessions table
320
+ ALTER TABLE sessions RENAME COLUMN current_route TO current_flow;
321
+
322
+ -- Messages table
323
+ ALTER TABLE messages RENAME COLUMN route TO flow;
324
+ ```
325
+
326
+ #### SQLite
327
+
328
+ SQLite 3.25+ supports `ALTER TABLE ... RENAME COLUMN`:
329
+
330
+ ```sql
331
+ -- Sessions table
332
+ ALTER TABLE sessions RENAME COLUMN current_route TO current_flow;
333
+
334
+ -- Messages table
335
+ ALTER TABLE messages RENAME COLUMN route TO flow;
336
+ ```
337
+
338
+ For SQLite versions older than 3.25, use the copy-and-rename pattern:
339
+
340
+ ```sql
341
+ -- 1. Create new table with correct column names
342
+ CREATE TABLE sessions_new (
343
+ id TEXT PRIMARY KEY,
344
+ user_id TEXT,
345
+ agent_name TEXT,
346
+ status TEXT DEFAULT 'active',
347
+ current_flow TEXT,
348
+ current_step TEXT,
349
+ collected_data TEXT,
350
+ message_count INTEGER DEFAULT 0,
351
+ last_message_at TEXT,
352
+ completed_at TEXT,
353
+ created_at TEXT NOT NULL,
354
+ updated_at TEXT NOT NULL
355
+ );
356
+
357
+ -- 2. Copy data
358
+ INSERT INTO sessions_new SELECT
359
+ id, user_id, agent_name, status,
360
+ current_route AS current_flow,
361
+ current_step, collected_data, message_count,
362
+ last_message_at, completed_at, created_at, updated_at
363
+ FROM sessions;
364
+
365
+ -- 3. Drop old table and rename
366
+ DROP TABLE sessions;
367
+ ALTER TABLE sessions_new RENAME TO sessions;
368
+
369
+ -- Repeat for messages table (route → flow column)
370
+ ```
371
+
372
+ #### Prisma
373
+
374
+ Update your Prisma schema model fields:
375
+
376
+ ```diff
377
+ model Session {
378
+ id String @id
379
+ userId String?
380
+ agentName String?
381
+ status String @default("active")
382
+ - currentRoute String? @map("current_route")
383
+ + currentFlow String? @map("current_flow")
384
+ currentStep String? @map("current_step")
385
+ collectedData Json? @map("collected_data")
386
+ // ...
387
+ }
388
+
389
+ model Message {
390
+ id String @id
391
+ sessionId String @map("session_id")
392
+ // ...
393
+ - route String?
394
+ + flow String?
395
+ step String?
396
+ // ...
397
+ }
398
+ ```
399
+
400
+ Then generate and apply the migration:
401
+
402
+ ```bash
403
+ npx prisma migrate dev --name route-to-flow-rename
404
+ ```
405
+
406
+ If you use a custom `fieldMappings` config in `PrismaAdapter`, update the key from `currentRoute` to `currentFlow`:
407
+
408
+ ```typescript
409
+ // Before
410
+ fieldMappings: { currentRoute: 'currentRoute', ... }
411
+
412
+ // After
413
+ fieldMappings: { currentFlow: 'currentFlow', ... }
414
+ ```
415
+
416
+ #### MongoDB
417
+
418
+ ```javascript
419
+ // Rename session fields
420
+ db.sessions.updateMany({}, {
421
+ $rename: {
422
+ "currentRoute": "currentFlow"
423
+ }
424
+ });
425
+
426
+ // Rename collected state fields (if stored at top level)
427
+ db.sessions.updateMany({}, {
428
+ $rename: {
429
+ "collectedData.routeHistory": "collectedData.flowHistory",
430
+ "collectedData.currentRouteTitle": "collectedData.currentFlowTitle"
431
+ }
432
+ });
433
+
434
+ // Rename message fields
435
+ db.messages.updateMany({}, {
436
+ $rename: {
437
+ "route": "flow"
438
+ }
439
+ });
440
+ ```
441
+
442
+ #### Redis
443
+
444
+ Redis stores sessions as serialized JSON. Use a Lua script to rewrite the payload in-place:
445
+
446
+ ```lua
447
+ -- redis-migrate-route-to-flow.lua
448
+ -- Run with: redis-cli --eval redis-migrate-route-to-flow.lua
449
+
450
+ local cursor = "0"
451
+ repeat
452
+ local result = redis.call("SCAN", cursor, "MATCH", "session:*", "COUNT", 100)
453
+ cursor = result[1]
454
+ local keys = result[2]
455
+ for _, key in ipairs(keys) do
456
+ local val = redis.call("GET", key)
457
+ if val then
458
+ -- Replace field names in JSON payload
459
+ val = val:gsub('"currentRoute"', '"currentFlow"')
460
+ val = val:gsub('"routeHistory"', '"flowHistory"')
461
+ val = val:gsub('"currentRouteTitle"', '"currentFlowTitle"')
462
+ redis.call("SET", key, val)
463
+ end
464
+ end
465
+ until cursor == "0"
466
+
467
+ -- If using hash layout instead of JSON:
468
+ -- Rename hash fields per session key
469
+ local cursor2 = "0"
470
+ repeat
471
+ local result = redis.call("SCAN", cursor2, "MATCH", "session:*", "COUNT", 100)
472
+ cursor2 = result[1]
473
+ local keys = result[2]
474
+ for _, key in ipairs(keys) do
475
+ local typ = redis.call("TYPE", key)["ok"]
476
+ if typ == "hash" then
477
+ local oldVal = redis.call("HGET", key, "currentRoute")
478
+ if oldVal then
479
+ redis.call("HSET", key, "currentFlow", oldVal)
480
+ redis.call("HDEL", key, "currentRoute")
481
+ end
482
+ end
483
+ end
484
+ until cursor2 == "0"
485
+ ```
486
+
487
+ For message keys, apply the same pattern replacing `"route"` with `"flow"` in the JSON payload or hash field.
488
+
489
+ #### OpenSearch
490
+
491
+ Use the `_reindex` API with a painless script to rename fields:
492
+
493
+ ```json
494
+ POST _reindex
495
+ {
496
+ "source": {
497
+ "index": "sessions"
498
+ },
499
+ "dest": {
500
+ "index": "sessions_v2"
501
+ },
502
+ "script": {
503
+ "source": """
504
+ // Rename currentRoute → currentFlow
505
+ if (ctx._source.containsKey('currentRoute')) {
506
+ ctx._source.currentFlow = ctx._source.remove('currentRoute');
507
+ }
508
+ // Rename routeHistory → flowHistory in collectedData
509
+ if (ctx._source.containsKey('collectedData') && ctx._source.collectedData.containsKey('routeHistory')) {
510
+ ctx._source.collectedData.flowHistory = ctx._source.collectedData.remove('routeHistory');
511
+ }
512
+ if (ctx._source.containsKey('collectedData') && ctx._source.collectedData.containsKey('currentRouteTitle')) {
513
+ ctx._source.collectedData.currentFlowTitle = ctx._source.collectedData.remove('currentRouteTitle');
514
+ }
515
+ """,
516
+ "lang": "painless"
517
+ }
518
+ }
519
+ ```
520
+
521
+ Then swap the alias:
522
+
523
+ ```json
524
+ POST _aliases
525
+ {
526
+ "actions": [
527
+ { "remove": { "index": "sessions", "alias": "sessions_active" } },
528
+ { "add": { "index": "sessions_v2", "alias": "sessions_active" } }
529
+ ]
530
+ }
531
+ ```
532
+
533
+ For the messages index, apply the same reindex pattern renaming the `route` field to `flow`:
534
+
535
+ ```json
536
+ POST _reindex
537
+ {
538
+ "source": {
539
+ "index": "messages"
540
+ },
541
+ "dest": {
542
+ "index": "messages_v2"
543
+ },
544
+ "script": {
545
+ "source": """
546
+ if (ctx._source.containsKey('route')) {
547
+ ctx._source.flow = ctx._source.remove('route');
548
+ }
549
+ """,
550
+ "lang": "painless"
551
+ }
552
+ }
553
+ ```
554
+
555
+ ### ID Prefix Migration
556
+
557
+ The `generateFlowId()` function now produces IDs with the prefix `flow_` instead of `route_`. Existing sessions stored under the legacy `route_*` prefix will not be recognized by the framework's flow-matching logic unless migrated.
558
+
559
+ **Run this migration during a maintenance window.** In-flight sessions will lose their step pointer if the rename is not atomic with the adapter restart.
560
+
561
+ #### PostgreSQL / SQLite
562
+
563
+ ```sql
564
+ UPDATE sessions
565
+ SET current_flow = REPLACE(current_flow, 'route_', 'flow_')
566
+ WHERE current_flow LIKE 'route\_%' ESCAPE '\';
567
+ ```
568
+
569
+ If your `collected_data` JSON contains `flowHistory` entries with old IDs (stored as `routeId` before the field rename), update those as well:
570
+
571
+ ```sql
572
+ -- PostgreSQL (JSONB)
573
+ UPDATE sessions
574
+ SET collected_data = REPLACE(collected_data::text, '"route_', '"flow_')::jsonb
575
+ WHERE collected_data::text LIKE '%"route_%';
576
+ ```
577
+
578
+ #### MongoDB
579
+
580
+ ```javascript
581
+ db.sessions.updateMany(
582
+ { currentFlow: { $regex: "^route_" } },
583
+ [{
584
+ $set: {
585
+ currentFlow: {
586
+ $replaceOne: {
587
+ input: "$currentFlow",
588
+ find: "route_",
589
+ replacement: "flow_"
590
+ }
591
+ }
592
+ }
593
+ }]
594
+ );
595
+
596
+ // Also update flowHistory entries
597
+ db.sessions.updateMany(
598
+ { "collectedData.flowHistory.flowId": { $regex: "^route_" } },
599
+ [{
600
+ $set: {
601
+ "collectedData.flowHistory": {
602
+ $map: {
603
+ input: "$collectedData.flowHistory",
604
+ as: "entry",
605
+ in: {
606
+ $mergeObjects: [
607
+ "$$entry",
608
+ {
609
+ flowId: {
610
+ $replaceOne: {
611
+ input: "$$entry.flowId",
612
+ find: "route_",
613
+ replacement: "flow_"
614
+ }
615
+ }
616
+ }
617
+ ]
618
+ }
619
+ }
620
+ }
621
+ }
622
+ }]
623
+ );
624
+ ```
625
+
626
+ #### Redis
627
+
628
+ Extend the Lua script above to also replace ID prefixes in the JSON payload:
629
+
630
+ ```lua
631
+ -- Add to the existing migration script
632
+ val = val:gsub('"route_', '"flow_')
633
+ ```
634
+
635
+ #### OpenSearch
636
+
637
+ Include the prefix replacement in the reindex painless script:
638
+
639
+ ```json
640
+ POST _reindex
641
+ {
642
+ "source": { "index": "sessions_v2" },
643
+ "dest": { "index": "sessions_v3" },
644
+ "script": {
645
+ "source": """
646
+ if (ctx._source.containsKey('currentFlow') && ctx._source.currentFlow != null && ctx._source.currentFlow.startsWith('route_')) {
647
+ ctx._source.currentFlow = 'flow_' + ctx._source.currentFlow.substring(6);
648
+ }
649
+ """,
650
+ "lang": "painless"
651
+ }
652
+ }
653
+ ```
654
+
655
+ ### Code-Side Migration Recipe
656
+
657
+ For downstream TypeScript consumers, here's a sed/codemod summary covering the most common public-API touchpoints:
658
+
659
+ ```bash
660
+ # Symbol renames (imports and references)
661
+ sed -i '' 's/\bRoute\b/Flow/g; s/\bRouteOptions\b/FlowOptions/g; s/\bRouteRef\b/FlowRef/g' src/**/*.ts
662
+ sed -i '' 's/\bRouteTransitionConfig\b/FlowTransitionConfig/g' src/**/*.ts
663
+ sed -i '' 's/\bRouteCompletionHandler\b/FlowCompletionHandler/g' src/**/*.ts
664
+ sed -i '' 's/\bRouteLifecycleHooks\b/FlowLifecycleHooks/g' src/**/*.ts
665
+ sed -i '' 's/\bRouteConfigurationError\b/FlowConfigurationError/g' src/**/*.ts
666
+ sed -i '' 's/\bRoutingEngine\b/FlowRouter/g' src/**/*.ts
667
+
668
+ # Constants (END_ROUTE removed — delete all references)
669
+ sed -i '' '/END_ROUTE/d; /END_FLOW/d' src/**/*.ts
670
+
671
+ # Methods and fields
672
+ sed -i '' 's/\.createRoute(/\.createFlow(/g' src/**/*.ts
673
+ sed -i '' 's/\.getRoutes(/\.getFlows(/g' src/**/*.ts
674
+ sed -i '' 's/\.nextStepRoute(/\.nextStepFlow(/g' src/**/*.ts
675
+ sed -i '' 's/\.getRoutingEngine(/\.getFlowRouter(/g' src/**/*.ts
676
+ sed -i '' 's/\brouteSwitchMargin\b/flowSwitchMargin/g' src/**/*.ts
677
+ sed -i '' 's/\bgenerateRouteId\b/generateFlowId/g' src/**/*.ts
678
+ sed -i '' 's/\benterRoute\b/enterFlow/g' src/**/*.ts
679
+
680
+ # Session state fields
681
+ sed -i '' 's/\.currentRoute/\.currentFlow/g' src/**/*.ts
682
+ sed -i '' 's/\.routeHistory/\.flowHistory/g' src/**/*.ts
683
+ sed -i '' 's/\btargetRouteId\b/targetFlowId/g' src/**/*.ts
684
+
685
+ # String literals
686
+ sed -i '' "s/'end_route'/'last_step'/g" src/**/*.ts
687
+ sed -i '' "s/'route_complete'/'completed'/g" src/**/*.ts
688
+
689
+ # Configuration
690
+ sed -i '' 's/routes:/flows:/g' src/**/*.ts # Be careful — review matches manually
691
+
692
+ # Import paths (if importing from @falai/agent internals)
693
+ sed -i '' 's/core\/Route/core\/Flow/g' src/**/*.ts
694
+ sed -i '' 's/core\/RoutingEngine/core\/FlowRouter/g' src/**/*.ts
695
+ sed -i '' 's/types\/route/types\/flow/g' src/**/*.ts
696
+ ```
697
+
698
+ **Important:** These sed commands are aggressive. Run them, then use `tsc --noEmit` to catch any false positives (e.g., `routes` in an HTTP router context). Review the diff before committing.
699
+
700
+ ### Route → Flow Verification
701
+
702
+ After migrating, confirm no legacy route references remain:
703
+
704
+ ```bash
705
+ rg -n '\b(Route|RouteOptions|RouteRef|RouteConfigurationError|RoutingEngine|END_ROUTE|currentRoute|routeHistory|createRoute|generateRouteId|enterRoute|nextStepRoute|getRoutes|routeSwitchMargin)\b' \
706
+ --glob '**/*.ts' \
707
+ --glob '**/*.tsx' \
708
+ --glob '!node_modules/**' \
709
+ --glob '!dist/**'
710
+ ```
711
+
712
+ Expected output: **zero matches**.
713
+
714
+ ### FAQ
715
+
716
+ **Q: Is there a compatibility shim or deprecation period?**
717
+ No. This is a clean break. The old names are removed entirely.
718
+
719
+ **Q: Do I need to migrate my database before deploying?**
720
+ Yes. The framework no longer reads or writes the legacy column/field names. Deploy the data migration first, then deploy the new code.
721
+
722
+ **Q: What about the `route()` method I see on `FlowRouter`?**
723
+ That's the verb form — it means "to route a message to a flow." It is intentionally preserved.
724
+
725
+ **Q: My tests assert on `'end_route'` or `'route_complete'` — what do I do?**
726
+ `'end_route'` has been removed entirely (implicit terminus replaces it). Update to `'last_step'`. `'route_complete'` becomes `'last_step'` (no successor) or `'completed'` (explicit directive). TypeScript will flag these as type errors if you miss any.
727
+
728
+ **Q: I have custom IDs that don't use the `route_` prefix — do I need to migrate them?**
729
+ Only IDs generated by `generateRouteId()` (now `generateFlowId()`) use the prefix. If you set custom IDs on your flows, they are unaffected by the prefix change.
227
730
 
228
731
 
229
732
  ---
@@ -905,5 +1408,4 @@ If both commands return clean (no matches; exit code 0), your migration is compl
905
1408
 
906
1409
  ## Cross-References
907
1410
 
908
- - [Route → Flow rename](./route-to-flow.md) — the rename pass that ships before v2
909
1411
  - [CHANGELOG](../../CHANGELOG.md) — full v2.0 release notes
@@ -474,7 +474,7 @@ Adapter errors propagate from the underlying driver — the agent does not wrap
474
474
  ## Related
475
475
 
476
476
  - [Persistence](../guides/persistence.md) — recipe for swapping memory for a real adapter
477
- - [Architecture](../concepts/architecture.md) — where the adapter sits among the seven primitives
477
+ - [Architecture](../concepts/architecture.md) — where the adapter sits among the six primitives
478
478
  - [createAgent](./create-agent.md) — the `persistence` and `sessionId` fields
479
479
  - [Directive](./directive.md) — what `pendingDirective` stores
480
480
  - [Signals](./signals.md) — what the `signals` column stores
@@ -9,7 +9,7 @@ order: 6
9
9
 
10
10
  > **Where this is introduced:** [Directives](../concepts/directives.md)
11
11
 
12
- A `Directive<TContext, TData>` is the single shape any tool, hook, or branch returns when it wants to write state, change position, or speak verbatim. It is a **flat object literal** — not a discriminated union, not a class, not a builder. Every field is optional. Every directive is plain JSON-serializable data (PreDirective adds non-serializable extensions; see [PreDirective](./pre-directive.md)).
12
+ A `Directive<TContext, TData>` is the single shape any tool, hook, or branch returns when it wants to write state, change position, or speak verbatim. It is a **flat object literal** — not a discriminated union, not a class, not a builder. Every field is optional. Every directive is plain JSON-serializable data. Pre-LLM-only fields (`appendPrompt`, `injectTools`, `halt`) are one-turn-lifetime and ignored with a WARN log when emitted from post-LLM hooks or persisted.
13
13
 
14
14
  A directive carries up to four orthogonal payloads:
15
15
 
@@ -92,7 +92,7 @@ Shallow-merge means each top-level key is replaced wholesale. To update a nested
92
92
  `reply` co-validates with two fields:
93
93
 
94
94
  - **`abort`** — co-existence is rejected at validation. Aborted conversations cannot deliver a reply.
95
- - **`halt`** (PreDirective only) — when both are set, `reply` becomes the assistant output and the turn ends with `stoppedReason: 'reply'`. When `halt` is set without `reply`, the turn ends with `stoppedReason: 'halt'` and an empty assistant message.
95
+ - **`halt`** — when both are set, `reply` becomes the assistant output and the turn ends with `stoppedReason: 'reply'`. When `halt` is set without `reply`, the turn ends with `stoppedReason: 'halt'` and an empty assistant message.
96
96
 
97
97
  Across multiple emitters this turn, `reply` is **last-wins** — the most recent emission overrides earlier ones, with a `DEBUG` log when more than one emitter set it.
98
98
 
@@ -105,9 +105,9 @@ Directives emitted during one turn — by tools (return value or `ctx.dispatch`)
105
105
  | Position fields (`goTo`, `goToStep`, `complete`, `abort`, `reset`) | **Winner-takes-all by precedence:** `abort > complete > goTo / goToStep > reset`. Among entries of the same precedence, last emission wins. A `DEBUG` log records all losers. |
106
106
  | `reply` | **Last-wins.** Most recent non-empty `reply` overrides earlier ones. `DEBUG` log when more than one was set. |
107
107
  | `dataUpdate`, `contextUpdate` | **Shallow-merged across all emitters.** Last-write-wins on key collision. Always applied — never overridden by position fields. |
108
- | `appendPrompt` (PreDirective) | **Concatenated** in emission order. |
109
- | `injectTools` (PreDirective) | **Concatenated, then deduped by `Tool.id`** (last definition wins). |
110
- | `halt` (PreDirective) | **Logical-OR.** Any emitter setting `halt: true` halts the turn. |
108
+ | `appendPrompt` | **Concatenated** in emission order. |
109
+ | `injectTools` | **Concatenated, then deduped by `Tool.id`** (last definition wins). |
110
+ | `halt` | **Logical-OR.** Any emitter setting `halt: true` halts the turn. |
111
111
 
112
112
  The full algorithm is implemented by `flow.merge(a, b)`. See [`flow` namespace](#flow-namespace) below.
113
113
 
@@ -128,7 +128,7 @@ flow.validate(d); // throws FlowConfigurationError on invalid shape
128
128
  | Helper | Signature | Notes |
129
129
  |--------|-----------|-------|
130
130
  | `flow.isDirective` | `(x: unknown) => x is Directive` | Structural type guard. Returns `true` for any non-null, non-array object. Filters out primitives, null/undefined, arrays, and functions. |
131
- | `flow.merge` | `<T extends Directive>(a: T, b: T) => T` | Merges by Algorithm 4. Position field uses precedence (b wins on tie). `reply` is last-wins. State writes shallow-merge. PreDirective fields concatenate / dedupe / OR per the table above. |
131
+ | `flow.merge` | `<T extends Directive>(a: T, b: T) => T` | Merges by Algorithm 4. Position field uses precedence (b wins on tie). `reply` is last-wins. State writes shallow-merge. Pre-LLM fields concatenate / dedupe / OR per the table above. |
132
132
  | `flow.validate` | `(d: Directive) => void` | Throws `FlowConfigurationError` for: multiple position fields set; `goTo` set as an empty object `{}`; `reply` co-existing with `abort`. |
133
133
 
134
134
  ## Examples
@@ -228,16 +228,15 @@ const step = {
228
228
 
229
229
  Two related runtime behaviors are notable but **not** thrown errors:
230
230
 
231
- - Returning a `PreDirective` (with `appendPrompt` / `injectTools` / `halt`) from a post-LLM hook (`finalize`, `onComplete`) drops those three fields with a `DEBUG` log; the remaining `Directive` portion is honored.
231
+ - Returning a directive with `appendPrompt` / `injectTools` / `halt` from a post-LLM hook (`finalize`, `onComplete`) drops those three fields with a `WARN` log; the remaining `Directive` portion is honored.
232
232
  - A `goTo` / `goToStep` referencing an unknown flow or step throws `FlowConfigurationError` at apply time, not at validation time — the validator does not have the agent's flow registry.
233
233
 
234
234
  ## Related
235
235
 
236
- - [Directives](../concepts/directives.md) — the mental model and inheritance chain `Directive PreDirective → SignalDirective`.
236
+ - [Directives](../concepts/directives.md) — the mental model for `Directive` and `SignalDirective`.
237
237
  - [Turn pipeline](../concepts/pipeline.md) — when the bus runs, and where merge sits in the per-turn sequence.
238
238
  - [Flow control](../guides/flow-control.md) — recipes for redirecting, completing, aborting, or replying from tools and hooks.
239
- - [PreDirective](./pre-directive.md) — pre-LLM extension adding `appendPrompt`, `injectTools`, `halt`.
240
- - [Signals](./signals.md) — `SignalDirective` extends `PreDirective` for signal handlers.
239
+ - [Signals](./signals.md) — `SignalDirective` extends `Directive` for signal handlers.
241
240
  - [Tool](./tool.md) — `ctx.dispatch(directive)` and `ToolResult.directive`.
242
241
  - [Branches](./branches.md) — `then: Directive` as a branch target (note: branches bypass the bus).
243
242
  - [Errors](./errors.md) — `FlowConfigurationError` format contract.
@@ -9,7 +9,7 @@ order: 2
9
9
 
10
10
  > **Where this is introduced:** [Architecture](../concepts/architecture.md)
11
11
 
12
- A `Flow` is one of the seven primitives in `@falai/agent`. It models a single conversational goal — booking a hotel, escalating a complaint, onboarding a teammate — as an ordered set of steps that share the agent's typed `TData` schema. Flows declare what data they need (`requiredFields`), what extra data they can use (`optionalFields`), when they should activate (`when` for AI strings, `if` for code), and what happens when they finish (`onComplete` or `hooks.onComplete`). The router selects exactly one flow per turn; once the active flow's required fields are satisfied, the engine fires its completion path.
12
+ A `Flow` is one of the six primitives in `@falai/agent`. It models a single conversational goal — booking a hotel, escalating a complaint, onboarding a teammate — as an ordered set of steps that share the agent's typed `TData` schema. Flows declare what data they need (`requiredFields`), what extra data they can use (`optionalFields`), when they should activate (`when` for AI strings, `if` for code), and what happens when they finish (`onComplete` or `hooks.onComplete`). The router selects exactly one flow per turn; once the active flow's required fields are satisfied, the engine fires its completion path.
13
13
 
14
14
  ## Signature
15
15
 
@@ -95,7 +95,7 @@ class Flow<TContext = unknown, TData = unknown> {
95
95
 
96
96
  | Hook | Returns | Phase | Notes |
97
97
  | ----------------- | -------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------- |
98
- | `onEnter` | `void \| PreDirective` | pre-LLM | Fires when the flow is entered. May augment the prompt, inject tools, or `halt`. See [PreDirective](./pre-directive.md).|
98
+ | `onEnter` | `void \| Directive` | pre-LLM | Fires when the flow is entered. May augment the prompt, inject tools, or `halt`. Pre-LLM fields honored here.|
99
99
  | `onExit` | `void` | post | Informational. Receives an `ExitReason`; cannot influence flow control. |
100
100
  | `onComplete` | `void \| Directive` | post-LLM | Handler form of completion. Mutually exclusive with top-level `onComplete: string` — setting both throws. |
101
101
  | `onDataUpdate` | `Partial<TData>` | post | Mutate or enrich the data update before it is committed to `session.data`. |
@@ -229,7 +229,7 @@ All `FlowConfigurationError` messages follow the format `[FlowConfigurationError
229
229
 
230
230
  ## Related
231
231
 
232
- - [Architecture](../concepts/architecture.md) — where Flow fits among the seven primitives
232
+ - [Architecture](../concepts/architecture.md) — where Flow fits among the six primitives
233
233
  - [Turn pipeline](../concepts/pipeline.md) — when flows are selected, entered, and completed
234
234
  - [Step](./step.md) — the inner DSL primitive flows are composed of
235
235
  - [Directive](./directive.md) — what `hooks.onComplete` returns
@@ -171,7 +171,7 @@ for (const a of response.appliedInstructions ?? []) {
171
171
  ## Related
172
172
 
173
173
  - [Instructions](../guides/instructions.md) — recipe for shaping behavior with `must` / `never` / `should`
174
- - [Architecture](../concepts/architecture.md) — where Instruction fits among the seven primitives
174
+ - [Architecture](../concepts/architecture.md) — where Instruction fits among the six primitives
175
175
  - [createAgent](./create-agent.md) — `AgentOptions.instructions`
176
176
  - [Flow](./flow.md) — `FlowOptions.instructions`
177
177
  - [Step](./step.md) — `StepOptions.instructions`
@@ -83,7 +83,7 @@ interface SignalContext<TContext = unknown, TData = unknown, TExtract = void> {
83
83
  }
84
84
 
85
85
  interface SignalDirective<TContext = unknown, TData = unknown>
86
- extends PreDirective<TContext, TData> {
86
+ extends Directive<TContext, TData> {
87
87
  stopOtherSignals?: boolean;
88
88
  replyWith?: string | ((ctx: SignalContext<TContext, TData>) => string);
89
89
  }
@@ -176,7 +176,7 @@ Passed to handlers when a signal fires. Symmetric with `ToolContext`.
176
176
 
177
177
  ### `SignalDirective`
178
178
 
179
- Extends [`PreDirective`](./pre-directive.md), which extends [`Directive`](./directive.md). All position fields (`goTo`, `goToStep`, `complete`, `abort`, `reset`), state writes (`dataUpdate`, `contextUpdate`), `reply`, and PreDirective extras (`appendPrompt`, `injectTools`, `halt`) are inherited unchanged.
179
+ Extends [`Directive`](./directive.md). All position fields (`goTo`, `goToStep`, `complete`, `abort`, `reset`), state writes (`dataUpdate`, `contextUpdate`), `reply`, and pre-LLM fields (`appendPrompt`, `injectTools`, `halt`) are inherited unchanged.
180
180
 
181
181
  | Added field | Type | Notes |
182
182
  |-------------|------|-------|
@@ -349,8 +349,7 @@ Soft failures handled in-band (no thrown error, turn continues):
349
349
  ## Related
350
350
 
351
351
  - [Turn pipeline](../concepts/pipeline.md) — where signal phases sit in the turn lifecycle.
352
- - [Directives](../concepts/directives.md) — the inheritance chain Directive PreDirective SignalDirective.
352
+ - [Directives](../concepts/directives.md) — the Directive shape and SignalDirective extension.
353
353
  - [Directive](./directive.md) — base shape for all position and state fields.
354
- - [PreDirective](./pre-directive.md) — pre-LLM extras inherited by `SignalDirective`.
355
354
  - [createAgent](./create-agent.md) — `signals` and `signalBatchSize` options.
356
355
  - [Errors](./errors.md) — `FlowConfigurationError` format contract.