@falai/agent 1.2.7 → 2.0.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.
- package/README.md +40 -886
- package/dist/adapters/MemoryAdapter.js +2 -2
- package/dist/adapters/MemoryAdapter.js.map +1 -1
- package/dist/adapters/MongoAdapter.js +2 -2
- package/dist/adapters/MongoAdapter.js.map +1 -1
- package/dist/adapters/OpenSearchAdapter.d.ts.map +1 -1
- package/dist/adapters/OpenSearchAdapter.js +9 -7
- package/dist/adapters/OpenSearchAdapter.js.map +1 -1
- package/dist/adapters/PostgreSQLAdapter.d.ts +14 -0
- package/dist/adapters/PostgreSQLAdapter.d.ts.map +1 -1
- package/dist/adapters/PostgreSQLAdapter.js +25 -9
- package/dist/adapters/PostgreSQLAdapter.js.map +1 -1
- package/dist/adapters/PrismaAdapter.js +5 -5
- package/dist/adapters/PrismaAdapter.js.map +1 -1
- package/dist/adapters/RedisAdapter.js +2 -2
- package/dist/adapters/RedisAdapter.js.map +1 -1
- package/dist/adapters/SQLiteAdapter.d.ts +17 -0
- package/dist/adapters/SQLiteAdapter.d.ts.map +1 -1
- package/dist/adapters/SQLiteAdapter.js +30 -11
- package/dist/adapters/SQLiteAdapter.js.map +1 -1
- package/dist/cjs/adapters/MemoryAdapter.js +2 -2
- package/dist/cjs/adapters/MemoryAdapter.js.map +1 -1
- package/dist/cjs/adapters/MongoAdapter.js +2 -2
- package/dist/cjs/adapters/MongoAdapter.js.map +1 -1
- package/dist/cjs/adapters/OpenSearchAdapter.d.ts.map +1 -1
- package/dist/cjs/adapters/OpenSearchAdapter.js +9 -7
- package/dist/cjs/adapters/OpenSearchAdapter.js.map +1 -1
- package/dist/cjs/adapters/PostgreSQLAdapter.d.ts +14 -0
- package/dist/cjs/adapters/PostgreSQLAdapter.d.ts.map +1 -1
- package/dist/cjs/adapters/PostgreSQLAdapter.js +25 -9
- package/dist/cjs/adapters/PostgreSQLAdapter.js.map +1 -1
- package/dist/cjs/adapters/PrismaAdapter.js +5 -5
- package/dist/cjs/adapters/PrismaAdapter.js.map +1 -1
- package/dist/cjs/adapters/RedisAdapter.js +2 -2
- package/dist/cjs/adapters/RedisAdapter.js.map +1 -1
- package/dist/cjs/adapters/SQLiteAdapter.d.ts +17 -0
- package/dist/cjs/adapters/SQLiteAdapter.d.ts.map +1 -1
- package/dist/cjs/adapters/SQLiteAdapter.js +30 -11
- package/dist/cjs/adapters/SQLiteAdapter.js.map +1 -1
- package/dist/cjs/constants/index.d.ts +0 -9
- package/dist/cjs/constants/index.d.ts.map +1 -1
- package/dist/cjs/constants/index.js +2 -11
- package/dist/cjs/constants/index.js.map +1 -1
- package/dist/cjs/core/Agent.d.ts +119 -153
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +471 -324
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/AutoChainExecutor.d.ts +107 -0
- package/dist/cjs/core/AutoChainExecutor.d.ts.map +1 -0
- package/dist/cjs/core/AutoChainExecutor.js +297 -0
- package/dist/cjs/core/AutoChainExecutor.js.map +1 -0
- package/dist/cjs/core/BranchEvaluator.d.ts +54 -0
- package/dist/cjs/core/BranchEvaluator.d.ts.map +1 -0
- package/dist/cjs/core/BranchEvaluator.js +130 -0
- package/dist/cjs/core/BranchEvaluator.js.map +1 -0
- package/dist/cjs/core/DirectiveBus.d.ts +88 -0
- package/dist/cjs/core/DirectiveBus.d.ts.map +1 -0
- package/dist/cjs/core/DirectiveBus.js +196 -0
- package/dist/cjs/core/DirectiveBus.js.map +1 -0
- package/dist/cjs/core/DirectiveChainTracker.d.ts +49 -0
- package/dist/cjs/core/DirectiveChainTracker.d.ts.map +1 -0
- package/dist/cjs/core/DirectiveChainTracker.js +121 -0
- package/dist/cjs/core/DirectiveChainTracker.js.map +1 -0
- package/dist/cjs/core/Flow.d.ts +186 -0
- package/dist/cjs/core/Flow.d.ts.map +1 -0
- package/dist/cjs/core/Flow.js +550 -0
- package/dist/cjs/core/Flow.js.map +1 -0
- package/dist/cjs/core/FlowRouter.d.ts +182 -0
- package/dist/cjs/core/FlowRouter.d.ts.map +1 -0
- package/dist/cjs/core/{RoutingEngine.js → FlowRouter.js} +323 -306
- package/dist/cjs/core/FlowRouter.js.map +1 -0
- package/dist/cjs/core/PersistenceManager.d.ts +2 -2
- package/dist/cjs/core/PersistenceManager.d.ts.map +1 -1
- package/dist/cjs/core/PersistenceManager.js +7 -7
- package/dist/cjs/core/PersistenceManager.js.map +1 -1
- package/dist/cjs/core/PromptComposer.d.ts +21 -8
- package/dist/cjs/core/PromptComposer.d.ts.map +1 -1
- package/dist/cjs/core/PromptComposer.js +182 -105
- package/dist/cjs/core/PromptComposer.js.map +1 -1
- package/dist/cjs/core/PromptSectionCache.d.ts +1 -1
- package/dist/cjs/core/PromptSectionCache.js +1 -1
- package/dist/cjs/core/ResponseEngine.d.ts +18 -8
- package/dist/cjs/core/ResponseEngine.d.ts.map +1 -1
- package/dist/cjs/core/ResponseEngine.js +38 -36
- package/dist/cjs/core/ResponseEngine.js.map +1 -1
- package/dist/cjs/core/ResponseModal.d.ts +73 -56
- package/dist/cjs/core/ResponseModal.d.ts.map +1 -1
- package/dist/cjs/core/ResponseModal.js +1196 -1015
- package/dist/cjs/core/ResponseModal.js.map +1 -1
- package/dist/cjs/core/ResponsePipeline.d.ts +124 -26
- package/dist/cjs/core/ResponsePipeline.d.ts.map +1 -1
- package/dist/cjs/core/ResponsePipeline.js +524 -134
- package/dist/cjs/core/ResponsePipeline.js.map +1 -1
- package/dist/cjs/core/SignalEvaluator.d.ts +86 -0
- package/dist/cjs/core/SignalEvaluator.d.ts.map +1 -0
- package/dist/cjs/core/SignalEvaluator.js +333 -0
- package/dist/cjs/core/SignalEvaluator.js.map +1 -0
- package/dist/cjs/core/SignalProcessor.d.ts +152 -0
- package/dist/cjs/core/SignalProcessor.d.ts.map +1 -0
- package/dist/cjs/core/SignalProcessor.js +562 -0
- package/dist/cjs/core/SignalProcessor.js.map +1 -0
- package/dist/cjs/core/Step.d.ts +43 -32
- package/dist/cjs/core/Step.d.ts.map +1 -1
- package/dist/cjs/core/Step.js +221 -126
- package/dist/cjs/core/Step.js.map +1 -1
- package/dist/cjs/core/StreamingToolExecutor.d.ts +2 -2
- package/dist/cjs/core/StreamingToolExecutor.d.ts.map +1 -1
- package/dist/cjs/core/StreamingToolExecutor.js.map +1 -1
- package/dist/cjs/core/ToolManager.d.ts +44 -13
- package/dist/cjs/core/ToolManager.d.ts.map +1 -1
- package/dist/cjs/core/ToolManager.js +174 -91
- package/dist/cjs/core/ToolManager.js.map +1 -1
- package/dist/cjs/core/createAgent.d.ts +35 -0
- package/dist/cjs/core/createAgent.d.ts.map +1 -0
- package/dist/cjs/core/createAgent.js +39 -0
- package/dist/cjs/core/createAgent.js.map +1 -0
- package/dist/cjs/core/flow-namespace.d.ts +49 -0
- package/dist/cjs/core/flow-namespace.d.ts.map +1 -0
- package/dist/cjs/core/flow-namespace.js +171 -0
- package/dist/cjs/core/flow-namespace.js.map +1 -0
- package/dist/cjs/index.d.ts +11 -14
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +18 -22
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/providers/GeminiProvider.d.ts +3 -3
- package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/cjs/providers/GeminiProvider.js +16 -14
- package/dist/cjs/providers/GeminiProvider.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +183 -54
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/agent.js +0 -6
- package/dist/cjs/types/agent.js.map +1 -1
- package/dist/cjs/types/ai.d.ts +3 -3
- package/dist/cjs/types/ai.d.ts.map +1 -1
- package/dist/cjs/types/errors.d.ts +15 -0
- package/dist/cjs/types/errors.d.ts.map +1 -0
- package/dist/cjs/types/errors.js +22 -0
- package/dist/cjs/types/errors.js.map +1 -0
- package/dist/cjs/types/flow.d.ts +513 -0
- package/dist/cjs/types/flow.d.ts.map +1 -0
- package/dist/cjs/types/{route.js → flow.js} +2 -2
- package/dist/cjs/types/flow.js.map +1 -0
- package/dist/cjs/types/index.d.ts +7 -6
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/cjs/types/index.js +6 -2
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/cjs/types/persistence.d.ts +11 -7
- package/dist/cjs/types/persistence.d.ts.map +1 -1
- package/dist/cjs/types/routing.d.ts +1 -1
- package/dist/cjs/types/routing.d.ts.map +1 -1
- package/dist/cjs/types/session.d.ts +24 -23
- package/dist/cjs/types/session.d.ts.map +1 -1
- package/dist/cjs/types/signals.d.ts +248 -0
- package/dist/cjs/types/signals.d.ts.map +1 -0
- package/dist/cjs/types/signals.js +11 -0
- package/dist/cjs/types/signals.js.map +1 -0
- package/dist/cjs/types/template.d.ts +2 -8
- package/dist/cjs/types/template.d.ts.map +1 -1
- package/dist/cjs/types/tool.d.ts +36 -29
- package/dist/cjs/types/tool.d.ts.map +1 -1
- package/dist/cjs/types/tool.js +1 -1
- package/dist/cjs/types/tool.js.map +1 -1
- package/dist/cjs/utils/condition.d.ts +7 -1
- package/dist/cjs/utils/condition.d.ts.map +1 -1
- package/dist/cjs/utils/condition.js.map +1 -1
- package/dist/cjs/utils/id.d.ts +13 -5
- package/dist/cjs/utils/id.d.ts.map +1 -1
- package/dist/cjs/utils/id.js +24 -10
- package/dist/cjs/utils/id.js.map +1 -1
- package/dist/cjs/utils/index.d.ts +2 -2
- package/dist/cjs/utils/index.d.ts.map +1 -1
- package/dist/cjs/utils/index.js +7 -3
- package/dist/cjs/utils/index.js.map +1 -1
- package/dist/cjs/utils/session.d.ts +44 -5
- package/dist/cjs/utils/session.d.ts.map +1 -1
- package/dist/cjs/utils/session.js +197 -38
- package/dist/cjs/utils/session.js.map +1 -1
- package/dist/constants/index.d.ts +0 -9
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/constants/index.js +3 -9
- package/dist/constants/index.js.map +1 -1
- package/dist/core/Agent.d.ts +119 -153
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +472 -325
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/AutoChainExecutor.d.ts +107 -0
- package/dist/core/AutoChainExecutor.d.ts.map +1 -0
- package/dist/core/AutoChainExecutor.js +293 -0
- package/dist/core/AutoChainExecutor.js.map +1 -0
- package/dist/core/BranchEvaluator.d.ts +54 -0
- package/dist/core/BranchEvaluator.d.ts.map +1 -0
- package/dist/core/BranchEvaluator.js +126 -0
- package/dist/core/BranchEvaluator.js.map +1 -0
- package/dist/core/DirectiveBus.d.ts +88 -0
- package/dist/core/DirectiveBus.d.ts.map +1 -0
- package/dist/core/DirectiveBus.js +192 -0
- package/dist/core/DirectiveBus.js.map +1 -0
- package/dist/core/DirectiveChainTracker.d.ts +49 -0
- package/dist/core/DirectiveChainTracker.d.ts.map +1 -0
- package/dist/core/DirectiveChainTracker.js +117 -0
- package/dist/core/DirectiveChainTracker.js.map +1 -0
- package/dist/core/Flow.d.ts +186 -0
- package/dist/core/Flow.d.ts.map +1 -0
- package/dist/core/Flow.js +546 -0
- package/dist/core/Flow.js.map +1 -0
- package/dist/core/FlowRouter.d.ts +182 -0
- package/dist/core/FlowRouter.d.ts.map +1 -0
- package/dist/core/{RoutingEngine.js → FlowRouter.js} +322 -305
- package/dist/core/FlowRouter.js.map +1 -0
- package/dist/core/PersistenceManager.d.ts +2 -2
- package/dist/core/PersistenceManager.d.ts.map +1 -1
- package/dist/core/PersistenceManager.js +7 -7
- package/dist/core/PersistenceManager.js.map +1 -1
- package/dist/core/PromptComposer.d.ts +21 -8
- package/dist/core/PromptComposer.d.ts.map +1 -1
- package/dist/core/PromptComposer.js +183 -106
- package/dist/core/PromptComposer.js.map +1 -1
- package/dist/core/PromptSectionCache.d.ts +1 -1
- package/dist/core/PromptSectionCache.js +1 -1
- package/dist/core/ResponseEngine.d.ts +18 -8
- package/dist/core/ResponseEngine.d.ts.map +1 -1
- package/dist/core/ResponseEngine.js +38 -36
- package/dist/core/ResponseEngine.js.map +1 -1
- package/dist/core/ResponseModal.d.ts +73 -56
- package/dist/core/ResponseModal.d.ts.map +1 -1
- package/dist/core/ResponseModal.js +1198 -1017
- package/dist/core/ResponseModal.js.map +1 -1
- package/dist/core/ResponsePipeline.d.ts +124 -26
- package/dist/core/ResponsePipeline.d.ts.map +1 -1
- package/dist/core/ResponsePipeline.js +524 -135
- package/dist/core/ResponsePipeline.js.map +1 -1
- package/dist/core/SignalEvaluator.d.ts +86 -0
- package/dist/core/SignalEvaluator.d.ts.map +1 -0
- package/dist/core/SignalEvaluator.js +326 -0
- package/dist/core/SignalEvaluator.js.map +1 -0
- package/dist/core/SignalProcessor.d.ts +152 -0
- package/dist/core/SignalProcessor.d.ts.map +1 -0
- package/dist/core/SignalProcessor.js +555 -0
- package/dist/core/SignalProcessor.js.map +1 -0
- package/dist/core/Step.d.ts +43 -32
- package/dist/core/Step.d.ts.map +1 -1
- package/dist/core/Step.js +220 -126
- package/dist/core/Step.js.map +1 -1
- package/dist/core/StreamingToolExecutor.d.ts +2 -2
- package/dist/core/StreamingToolExecutor.d.ts.map +1 -1
- package/dist/core/StreamingToolExecutor.js.map +1 -1
- package/dist/core/ToolManager.d.ts +44 -13
- package/dist/core/ToolManager.d.ts.map +1 -1
- package/dist/core/ToolManager.js +174 -91
- package/dist/core/ToolManager.js.map +1 -1
- package/dist/core/createAgent.d.ts +35 -0
- package/dist/core/createAgent.d.ts.map +1 -0
- package/dist/core/createAgent.js +36 -0
- package/dist/core/createAgent.js.map +1 -0
- package/dist/core/flow-namespace.d.ts +49 -0
- package/dist/core/flow-namespace.d.ts.map +1 -0
- package/dist/core/flow-namespace.js +168 -0
- package/dist/core/flow-namespace.js.map +1 -0
- package/dist/index.d.ts +11 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -12
- package/dist/index.js.map +1 -1
- package/dist/providers/GeminiProvider.d.ts +3 -3
- package/dist/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/GeminiProvider.js +16 -14
- package/dist/providers/GeminiProvider.js.map +1 -1
- package/dist/types/agent.d.ts +183 -54
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/agent.js +0 -6
- package/dist/types/agent.js.map +1 -1
- package/dist/types/ai.d.ts +3 -3
- package/dist/types/ai.d.ts.map +1 -1
- package/dist/types/errors.d.ts +15 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +18 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/flow.d.ts +513 -0
- package/dist/types/flow.d.ts.map +1 -0
- package/dist/types/flow.js +5 -0
- package/dist/types/flow.js.map +1 -0
- package/dist/types/index.d.ts +7 -6
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +4 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/persistence.d.ts +11 -7
- package/dist/types/persistence.d.ts.map +1 -1
- package/dist/types/routing.d.ts +1 -1
- package/dist/types/routing.d.ts.map +1 -1
- package/dist/types/session.d.ts +24 -23
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/signals.d.ts +248 -0
- package/dist/types/signals.d.ts.map +1 -0
- package/dist/types/signals.js +10 -0
- package/dist/types/signals.js.map +1 -0
- package/dist/types/template.d.ts +2 -8
- package/dist/types/template.d.ts.map +1 -1
- package/dist/types/tool.d.ts +36 -29
- package/dist/types/tool.d.ts.map +1 -1
- package/dist/types/tool.js +1 -1
- package/dist/types/tool.js.map +1 -1
- package/dist/utils/condition.d.ts +7 -1
- package/dist/utils/condition.d.ts.map +1 -1
- package/dist/utils/condition.js.map +1 -1
- package/dist/utils/id.d.ts +13 -5
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +22 -9
- package/dist/utils/id.js.map +1 -1
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -2
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/session.d.ts +44 -5
- package/dist/utils/session.d.ts.map +1 -1
- package/dist/utils/session.js +193 -37
- package/dist/utils/session.js.map +1 -1
- package/docs/README.md +15 -202
- package/docs/concepts/architecture.md +281 -0
- package/docs/concepts/directives.md +400 -0
- package/docs/concepts/pipeline.md +399 -0
- package/docs/guides/branching.md +263 -0
- package/docs/guides/compaction.md +163 -0
- package/docs/guides/conditions.md +167 -0
- package/docs/guides/error-handling.md +176 -0
- package/docs/guides/flow-control.md +409 -0
- package/docs/guides/instructions.md +210 -0
- package/docs/guides/persistence.md +182 -0
- package/docs/guides/streaming.md +137 -0
- package/docs/migration/README.md +15 -0
- package/docs/migration/route-to-flow.md +560 -0
- package/docs/migration/v1-to-v2.md +909 -0
- package/docs/reference/adapters.md +481 -0
- package/docs/reference/branches.md +241 -0
- package/docs/reference/create-agent.md +186 -0
- package/docs/reference/directive.md +243 -0
- package/docs/reference/errors.md +122 -0
- package/docs/reference/flow.md +238 -0
- package/docs/reference/instruction.md +177 -0
- package/docs/reference/pre-directive.md +131 -0
- package/docs/reference/providers.md +227 -0
- package/docs/reference/signals.md +356 -0
- package/docs/reference/step.md +339 -0
- package/docs/reference/tool.md +269 -0
- package/docs/start/01-install.md +81 -0
- package/docs/start/02-first-agent.md +196 -0
- package/docs/start/03-collect-data.md +222 -0
- package/docs/start/04-add-tools.md +276 -0
- package/docs/start/05-go-to-production.md +216 -0
- package/examples/01-quickstart.ts +20 -0
- package/examples/02-data-extraction.ts +90 -0
- package/examples/03-tools.ts +136 -0
- package/examples/04-instructions.ts +100 -0
- package/examples/05-branching.ts +140 -0
- package/examples/06-flow-control.ts +103 -0
- package/examples/07-streaming.ts +69 -0
- package/examples/08-persistence.ts +98 -0
- package/examples/09-signals.ts +144 -0
- package/examples/tsconfig.json +30 -0
- package/package.json +2 -1
- package/src/adapters/MemoryAdapter.ts +3 -3
- package/src/adapters/MongoAdapter.ts +3 -3
- package/src/adapters/OpenSearchAdapter.ts +10 -8
- package/src/adapters/PostgreSQLAdapter.ts +26 -10
- package/src/adapters/PrismaAdapter.ts +6 -6
- package/src/adapters/RedisAdapter.ts +3 -3
- package/src/adapters/SQLiteAdapter.ts +31 -12
- package/src/constants/index.ts +2 -10
- package/src/core/Agent.ts +585 -374
- package/src/core/AutoChainExecutor.ts +440 -0
- package/src/core/BranchEvaluator.ts +167 -0
- package/src/core/DirectiveBus.ts +248 -0
- package/src/core/DirectiveChainTracker.ts +144 -0
- package/src/core/Flow.ts +666 -0
- package/src/core/{RoutingEngine.ts → FlowRouter.ts} +385 -365
- package/src/core/PersistenceManager.ts +8 -8
- package/src/core/PromptComposer.ts +209 -140
- package/src/core/PromptSectionCache.ts +1 -1
- package/src/core/ResponseEngine.ts +61 -46
- package/src/core/ResponseModal.ts +1458 -1241
- package/src/core/ResponsePipeline.ts +675 -173
- package/src/core/SignalEvaluator.ts +420 -0
- package/src/core/SignalProcessor.ts +723 -0
- package/src/core/Step.ts +279 -176
- package/src/core/StreamingToolExecutor.ts +4 -4
- package/src/core/ToolManager.ts +200 -97
- package/src/core/createAgent.ts +40 -0
- package/src/core/flow-namespace.ts +219 -0
- package/src/index.ts +42 -36
- package/src/providers/GeminiProvider.ts +17 -15
- package/src/types/agent.ts +182 -53
- package/src/types/ai.ts +3 -3
- package/src/types/errors.ts +18 -0
- package/src/types/flow.ts +590 -0
- package/src/types/index.ts +43 -16
- package/src/types/persistence.ts +12 -8
- package/src/types/routing.ts +1 -1
- package/src/types/session.ts +26 -23
- package/src/types/signals.ts +321 -0
- package/src/types/template.ts +3 -11
- package/src/types/tool.ts +50 -42
- package/src/utils/condition.ts +13 -4
- package/src/utils/id.ts +27 -9
- package/src/utils/index.ts +6 -2
- package/src/utils/session.ts +238 -42
- package/dist/cjs/core/BatchExecutor.d.ts +0 -359
- package/dist/cjs/core/BatchExecutor.d.ts.map +0 -1
- package/dist/cjs/core/BatchExecutor.js +0 -861
- package/dist/cjs/core/BatchExecutor.js.map +0 -1
- package/dist/cjs/core/BatchPromptBuilder.d.ts +0 -89
- package/dist/cjs/core/BatchPromptBuilder.d.ts.map +0 -1
- package/dist/cjs/core/BatchPromptBuilder.js +0 -223
- package/dist/cjs/core/BatchPromptBuilder.js.map +0 -1
- package/dist/cjs/core/Route.d.ts +0 -180
- package/dist/cjs/core/Route.d.ts.map +0 -1
- package/dist/cjs/core/Route.js +0 -542
- package/dist/cjs/core/Route.js.map +0 -1
- package/dist/cjs/core/RoutingEngine.d.ts +0 -185
- package/dist/cjs/core/RoutingEngine.d.ts.map +0 -1
- package/dist/cjs/core/RoutingEngine.js.map +0 -1
- package/dist/cjs/types/route.d.ts +0 -336
- package/dist/cjs/types/route.d.ts.map +0 -1
- package/dist/cjs/types/route.js.map +0 -1
- package/dist/core/BatchExecutor.d.ts +0 -359
- package/dist/core/BatchExecutor.d.ts.map +0 -1
- package/dist/core/BatchExecutor.js +0 -856
- package/dist/core/BatchExecutor.js.map +0 -1
- package/dist/core/BatchPromptBuilder.d.ts +0 -89
- package/dist/core/BatchPromptBuilder.d.ts.map +0 -1
- package/dist/core/BatchPromptBuilder.js +0 -219
- package/dist/core/BatchPromptBuilder.js.map +0 -1
- package/dist/core/Route.d.ts +0 -180
- package/dist/core/Route.d.ts.map +0 -1
- package/dist/core/Route.js +0 -538
- package/dist/core/Route.js.map +0 -1
- package/dist/core/RoutingEngine.d.ts +0 -185
- package/dist/core/RoutingEngine.d.ts.map +0 -1
- package/dist/core/RoutingEngine.js.map +0 -1
- package/dist/types/route.d.ts +0 -336
- package/dist/types/route.d.ts.map +0 -1
- package/dist/types/route.js +0 -5
- package/dist/types/route.js.map +0 -1
- package/docs/CONTRIBUTING.md +0 -521
- package/docs/api/README.md +0 -3299
- package/docs/api/overview.md +0 -1410
- package/docs/architecture/data-extraction-flow.md +0 -360
- package/docs/architecture/multi-step-execution.md +0 -277
- package/docs/core/agent/README.md +0 -938
- package/docs/core/agent/context-management.md +0 -796
- package/docs/core/agent/rules-and-prohibitions.md +0 -113
- package/docs/core/agent/session-management.md +0 -693
- package/docs/core/ai-integration/prompt-composition.md +0 -355
- package/docs/core/ai-integration/providers.md +0 -515
- package/docs/core/ai-integration/response-processing.md +0 -433
- package/docs/core/conversation-flows/data-collection.md +0 -772
- package/docs/core/conversation-flows/route-dsl.md +0 -509
- package/docs/core/conversation-flows/routes.md +0 -249
- package/docs/core/conversation-flows/step-transitions.md +0 -731
- package/docs/core/conversation-flows/steps.md +0 -268
- package/docs/core/error-handling.md +0 -830
- package/docs/core/persistence/adapters.md +0 -255
- package/docs/core/persistence/session-storage.md +0 -656
- package/docs/core/routing/intelligent-routing.md +0 -470
- package/docs/core/tools/enhanced-tool.md +0 -186
- package/docs/core/tools/streaming-execution.md +0 -161
- package/docs/core/tools/tool-definition.md +0 -970
- package/docs/core/tools/tool-scoping.md +0 -819
- package/docs/guides/advanced-patterns/publishing.md +0 -186
- package/docs/guides/context-compaction.md +0 -96
- package/docs/guides/error-handling-patterns.md +0 -578
- package/docs/guides/getting-started/README.md +0 -795
- package/docs/guides/migration/README.md +0 -101
- package/docs/guides/migration/flexible-routing-conditions.md +0 -375
- package/docs/guides/migration/multi-step-execution.md +0 -393
- package/docs/guides/migration/response-modal-refactor.md +0 -518
- package/docs/guides/prompt-optimization.md +0 -164
- package/examples/advanced-patterns/context-compaction.ts +0 -223
- package/examples/advanced-patterns/knowledge-based-agent.ts +0 -735
- package/examples/advanced-patterns/persistent-onboarding.ts +0 -728
- package/examples/advanced-patterns/route-lifecycle-hooks.ts +0 -556
- package/examples/advanced-patterns/streaming-responses.ts +0 -656
- package/examples/ai-providers/anthropic-integration.ts +0 -388
- package/examples/ai-providers/openai-integration.ts +0 -228
- package/examples/condition-patterns/function-only-conditions.ts +0 -365
- package/examples/condition-patterns/mixed-array-conditions.ts +0 -477
- package/examples/condition-patterns/route-skipif-patterns.ts +0 -468
- package/examples/condition-patterns/step-skipif-patterns.ts +0 -0
- package/examples/condition-patterns/string-only-conditions.ts +0 -296
- package/examples/conversation-flows/completion-transitions.ts +0 -318
- package/examples/core-concepts/basic-agent.ts +0 -503
- package/examples/core-concepts/modern-streaming-api.ts +0 -309
- package/examples/core-concepts/schema-driven-extraction.ts +0 -332
- package/examples/core-concepts/session-management.ts +0 -494
- package/examples/integrations/database-integration.ts +0 -631
- package/examples/integrations/healthcare-integration.ts +0 -595
- package/examples/integrations/search-integration.ts +0 -530
- package/examples/integrations/server-session-management.ts +0 -307
- package/examples/persistence/custom-adapter.ts +0 -526
- package/examples/persistence/database-persistence.ts +0 -583
- package/examples/persistence/memory-sessions.ts +0 -495
- package/examples/persistence/prisma-schema.example.prisma +0 -74
- package/examples/persistence/redis-persistence.ts +0 -488
- package/examples/tools/basic-tools.ts +0 -765
- package/examples/tools/data-enrichment-tools.ts +0 -593
- package/examples/tools/enhanced-tool-metadata.ts +0 -268
- package/examples/tools/streaming-tool-execution.ts +0 -283
- package/src/core/BatchExecutor.ts +0 -1187
- package/src/core/BatchPromptBuilder.ts +0 -299
- package/src/core/Route.ts +0 -678
- package/src/types/route.ts +0 -392
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.FlowRouter = void 0;
|
|
4
4
|
const types_1 = require("../types");
|
|
5
5
|
const utils_1 = require("../utils");
|
|
6
6
|
const PromptComposer_1 = require("./PromptComposer");
|
|
7
|
-
const constants_1 = require("../constants");
|
|
8
7
|
const utils_2 = require("../utils");
|
|
9
|
-
class
|
|
8
|
+
class FlowRouter {
|
|
10
9
|
constructor(options) {
|
|
11
10
|
this.options = options;
|
|
12
11
|
}
|
|
13
12
|
/**
|
|
14
13
|
* Check whether the history contains any user messages.
|
|
15
|
-
* Used to detect "session resume" scenarios where a
|
|
14
|
+
* Used to detect "session resume" scenarios where a flow/step was
|
|
16
15
|
* pre-set programmatically and the conversation starts with only
|
|
17
16
|
* system messages (or no messages at all).
|
|
18
17
|
* @private
|
|
@@ -22,46 +21,46 @@ class RoutingEngine {
|
|
|
22
21
|
}
|
|
23
22
|
/**
|
|
24
23
|
* Handle the "session resume" fast-path: when a session already has a
|
|
25
|
-
* pre-set
|
|
24
|
+
* pre-set currentFlow (and optionally currentStep) and the conversation
|
|
26
25
|
* history contains no user messages, honor the pre-set position instead
|
|
27
|
-
* of running AI
|
|
26
|
+
* of running AI flow/step selection.
|
|
28
27
|
*
|
|
29
28
|
* Returns `undefined` when the fast-path does not apply.
|
|
30
29
|
* @private
|
|
31
30
|
*/
|
|
32
31
|
async handleSessionResume(params) {
|
|
33
|
-
const {
|
|
32
|
+
const { flows, session, history, context } = params;
|
|
34
33
|
// Fast-path only applies when:
|
|
35
|
-
// 1. Session already has a
|
|
34
|
+
// 1. Session already has a currentFlow set
|
|
36
35
|
// 2. There are no user messages in the history (system-only or empty)
|
|
37
|
-
if (!session.
|
|
36
|
+
if (!session.currentFlow || this.hasUserMessages(history)) {
|
|
38
37
|
return undefined;
|
|
39
38
|
}
|
|
40
|
-
// Find the pre-set
|
|
41
|
-
const
|
|
42
|
-
if (!
|
|
43
|
-
utils_2.logger.warn(`[
|
|
39
|
+
// Find the pre-set flow among available flows
|
|
40
|
+
const presetFlow = flows.find((r) => r.id === session.currentFlow.id);
|
|
41
|
+
if (!presetFlow) {
|
|
42
|
+
utils_2.logger.warn(`[FlowConfigurationError] Pre-set flow not found: session references flow "${session.currentFlow.id}" which does not exist among available flows. Falling back to normal routing. Remove the stale session reference or register the missing flow.`);
|
|
44
43
|
return undefined;
|
|
45
44
|
}
|
|
46
|
-
utils_2.logger.debug(`[
|
|
47
|
-
// Enter
|
|
48
|
-
const updatedSession = this.
|
|
49
|
-
// Evaluate cross-
|
|
50
|
-
const
|
|
45
|
+
utils_2.logger.debug(`[FlowRouter] Session resume: honoring pre-set flow '${presetFlow.title}' (no user messages in history)`);
|
|
46
|
+
// Enter flow if needed (merges initialData, no-op if already entered)
|
|
47
|
+
const updatedSession = this.enterFlowIfNeeded(session, presetFlow);
|
|
48
|
+
// Evaluate cross-flow completions
|
|
49
|
+
const completedFlows = this.evaluateFlowCompletions(flows, updatedSession.data || {});
|
|
51
50
|
// If a currentStep is also pre-set, honor it — stay on that step
|
|
52
51
|
if (session.currentStep) {
|
|
53
|
-
const presetStep =
|
|
52
|
+
const presetStep = presetFlow.getStep(session.currentStep.id);
|
|
54
53
|
if (presetStep) {
|
|
55
|
-
utils_2.logger.debug(`[
|
|
54
|
+
utils_2.logger.debug(`[FlowRouter] Session resume: honoring pre-set step '${presetStep.id}'`);
|
|
56
55
|
return {
|
|
57
|
-
|
|
56
|
+
selectedFlow: presetFlow,
|
|
58
57
|
selectedStep: presetStep,
|
|
59
58
|
session: updatedSession,
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
isFlowComplete: false,
|
|
60
|
+
completedFlows,
|
|
62
61
|
};
|
|
63
62
|
}
|
|
64
|
-
utils_2.logger.warn(`[
|
|
63
|
+
utils_2.logger.warn(`[FlowConfigurationError] Pre-set step not found: session references step "${session.currentStep.id}" which does not exist in flow "${presetFlow.title}". Resolving from initial step. Remove the stale step reference or register the missing step.`);
|
|
65
64
|
}
|
|
66
65
|
// No currentStep pre-set (or it wasn't found) — resolve from initialStep
|
|
67
66
|
// using the standard candidate logic (handles skipIf, etc.)
|
|
@@ -71,67 +70,97 @@ class RoutingEngine {
|
|
|
71
70
|
history,
|
|
72
71
|
data: updatedSession.data,
|
|
73
72
|
});
|
|
74
|
-
const candidates = await this.getCandidateStepsWithConditions(
|
|
73
|
+
const candidates = await this.getCandidateStepsWithConditions(presetFlow, undefined, // No current step — start from beginning
|
|
75
74
|
templateContext);
|
|
76
75
|
if (candidates.length === 0) {
|
|
77
|
-
utils_2.logger.warn(`[
|
|
76
|
+
utils_2.logger.warn(`[FlowConfigurationError] No valid steps found: all steps in flow "${presetFlow.title}" are skipped by their conditions. Check skip/when conditions on the flow's steps.`);
|
|
78
77
|
return {
|
|
79
|
-
|
|
78
|
+
selectedFlow: presetFlow,
|
|
80
79
|
selectedStep: undefined,
|
|
81
80
|
session: updatedSession,
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
isFlowComplete: false,
|
|
82
|
+
completedFlows,
|
|
84
83
|
};
|
|
85
84
|
}
|
|
86
85
|
const candidate = candidates[0];
|
|
87
|
-
if (candidate.
|
|
88
|
-
utils_2.logger.debug(`[
|
|
86
|
+
if (candidate.isFlowComplete) {
|
|
87
|
+
utils_2.logger.debug(`[FlowRouter] Session resume: flow '${presetFlow.title}' is already complete`);
|
|
89
88
|
return {
|
|
90
|
-
|
|
89
|
+
selectedFlow: presetFlow,
|
|
91
90
|
selectedStep: undefined,
|
|
92
91
|
session: updatedSession,
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
isFlowComplete: true,
|
|
93
|
+
completedFlows,
|
|
95
94
|
};
|
|
96
95
|
}
|
|
97
|
-
utils_2.logger.debug(`[
|
|
96
|
+
utils_2.logger.debug(`[FlowRouter] Session resume: resolved initial step '${candidate.step.id}'`);
|
|
98
97
|
return {
|
|
99
|
-
|
|
98
|
+
selectedFlow: presetFlow,
|
|
100
99
|
selectedStep: candidate.step,
|
|
101
100
|
session: updatedSession,
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
isFlowComplete: false,
|
|
102
|
+
completedFlows,
|
|
104
103
|
};
|
|
105
104
|
}
|
|
106
105
|
/**
|
|
107
|
-
* Enter a
|
|
106
|
+
* Enter a flow if not already in it, merging initial data.
|
|
107
|
+
*
|
|
108
|
+
* When the flow is `reentrant` and was previously completed in this
|
|
109
|
+
* session, clears every field declared in the flow's `requiredFields`
|
|
110
|
+
* and `optionalFields` before entering — so the flow starts fresh from
|
|
111
|
+
* its initial step instead of being instantly marked complete by stale
|
|
112
|
+
* data. Fields not owned by this flow are preserved.
|
|
113
|
+
*
|
|
108
114
|
* @private
|
|
109
115
|
*/
|
|
110
|
-
|
|
111
|
-
if (!session.
|
|
112
|
-
let
|
|
116
|
+
enterFlowIfNeeded(session, route) {
|
|
117
|
+
if (!session.currentFlow || session.currentFlow.id !== route.id) {
|
|
118
|
+
let workingSession = session;
|
|
119
|
+
// Re-entry into a `reentrant` flow that previously completed:
|
|
120
|
+
// clear owned fields so completion logic doesn't short-circuit on
|
|
121
|
+
// stale data from the prior run.
|
|
122
|
+
const previouslyCompleted = (0, utils_1.isFlowCompletedThisSession)(session, route.id);
|
|
123
|
+
if (previouslyCompleted && route.reentrant) {
|
|
124
|
+
const ownedFields = [
|
|
125
|
+
...(route.requiredFields ?? []),
|
|
126
|
+
...(route.optionalFields ?? []),
|
|
127
|
+
];
|
|
128
|
+
if (ownedFields.length > 0) {
|
|
129
|
+
const owned = new Set(ownedFields);
|
|
130
|
+
const filtered = {};
|
|
131
|
+
for (const key of Object.keys(session.data ?? {})) {
|
|
132
|
+
if (!owned.has(key)) {
|
|
133
|
+
filtered[key] =
|
|
134
|
+
session.data[key];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
workingSession = { ...session, data: filtered };
|
|
138
|
+
utils_2.logger.debug(`[FlowRouter] Re-entering reentrant flow ${route.title}: cleared ${ownedFields.length} owned field(s)`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
let updatedSession = (0, utils_1.enterFlow)(workingSession, route.id, route.title);
|
|
113
142
|
if (route.initialData) {
|
|
114
143
|
updatedSession = (0, utils_1.mergeCollected)(updatedSession, route.initialData);
|
|
115
|
-
utils_2.logger.debug(`[
|
|
144
|
+
utils_2.logger.debug(`[FlowRouter] Merged initial data for flow ${route.title}:`, route.initialData);
|
|
116
145
|
}
|
|
117
|
-
utils_2.logger.debug(`[
|
|
118
|
-
this.options?.
|
|
146
|
+
utils_2.logger.debug(`[FlowRouter] Entered flow: ${route.title}`);
|
|
147
|
+
this.options?.onFlowSwitch?.();
|
|
119
148
|
return updatedSession;
|
|
120
149
|
}
|
|
121
150
|
return session;
|
|
122
151
|
}
|
|
123
152
|
/**
|
|
124
|
-
* Optimized decision for single-
|
|
125
|
-
* Skips
|
|
153
|
+
* Optimized decision for single-flow scenarios
|
|
154
|
+
* Skips flow scoring and only does step selection
|
|
126
155
|
* @private
|
|
127
156
|
*/
|
|
128
|
-
async
|
|
157
|
+
async decideSingleFlowStep(params) {
|
|
129
158
|
const { route, session, history, agentOptions, provider, context, signal } = params;
|
|
130
|
-
const
|
|
131
|
-
// Enter
|
|
132
|
-
const updatedSession = this.
|
|
133
|
-
// Check if this single
|
|
134
|
-
const
|
|
159
|
+
const selectedFlow = route;
|
|
160
|
+
// Enter flow if not already in it (this may merge initial data)
|
|
161
|
+
const updatedSession = this.enterFlowIfNeeded(session, route);
|
|
162
|
+
// Check if this single flow is complete (use updated session data)
|
|
163
|
+
const completedFlows = route.isComplete(updatedSession.data || {}) ? [route] : [];
|
|
135
164
|
// Get candidate steps using new condition evaluation
|
|
136
165
|
const templateContext = (0, utils_2.createTemplateContext)({
|
|
137
166
|
context,
|
|
@@ -144,44 +173,43 @@ class RoutingEngine {
|
|
|
144
173
|
: undefined;
|
|
145
174
|
const candidates = await this.getCandidateStepsWithConditions(route, currentStep, templateContext);
|
|
146
175
|
if (candidates.length === 0) {
|
|
147
|
-
utils_2.logger.warn(`[
|
|
148
|
-
return {
|
|
176
|
+
utils_2.logger.warn(`[FlowConfigurationError] No valid steps found: all candidates in the single-flow agent are skipped. Check step skip/when conditions.`);
|
|
177
|
+
return { selectedFlow, session: updatedSession };
|
|
149
178
|
}
|
|
150
179
|
// If only one candidate, check if it's a completion marker
|
|
151
180
|
if (candidates.length === 1) {
|
|
152
181
|
const candidate = candidates[0];
|
|
153
|
-
if (candidate.
|
|
154
|
-
utils_2.logger.debug(`[
|
|
155
|
-
// Don't return a selectedStep when
|
|
182
|
+
if (candidate.isFlowComplete) {
|
|
183
|
+
utils_2.logger.debug(`[FlowRouter] Single-flow: Flow complete - all required fields collected or last step reached`);
|
|
184
|
+
// Don't return a selectedStep when flow is complete - there's no step to enter
|
|
156
185
|
return {
|
|
157
|
-
|
|
186
|
+
selectedFlow,
|
|
158
187
|
selectedStep: undefined,
|
|
159
188
|
session: updatedSession,
|
|
160
|
-
|
|
161
|
-
|
|
189
|
+
isFlowComplete: true,
|
|
190
|
+
completedFlows,
|
|
162
191
|
};
|
|
163
192
|
}
|
|
164
193
|
else {
|
|
165
|
-
utils_2.logger.debug(`[
|
|
194
|
+
utils_2.logger.debug(`[FlowRouter] Single-flow: Only one valid step: ${candidate.step.id}`);
|
|
166
195
|
return {
|
|
167
|
-
|
|
196
|
+
selectedFlow,
|
|
168
197
|
selectedStep: candidate.step,
|
|
169
198
|
session: updatedSession,
|
|
170
|
-
|
|
171
|
-
|
|
199
|
+
isFlowComplete: false,
|
|
200
|
+
completedFlows,
|
|
172
201
|
};
|
|
173
202
|
}
|
|
174
203
|
}
|
|
175
|
-
// No candidates means
|
|
176
|
-
// Don't mark as complete based on data alone — only END_ROUTE completes a route
|
|
204
|
+
// No candidates means flow has no valid next steps (edge case)
|
|
177
205
|
if (candidates.length === 0) {
|
|
178
|
-
utils_2.logger.debug(`[
|
|
206
|
+
utils_2.logger.debug(`[FlowRouter] Single-flow: No valid candidate steps found`);
|
|
179
207
|
return {
|
|
180
|
-
|
|
208
|
+
selectedFlow,
|
|
181
209
|
selectedStep: undefined,
|
|
182
210
|
session: updatedSession,
|
|
183
|
-
|
|
184
|
-
|
|
211
|
+
isFlowComplete: false,
|
|
212
|
+
completedFlows,
|
|
185
213
|
};
|
|
186
214
|
}
|
|
187
215
|
// Multiple candidates - use AI to select best step
|
|
@@ -192,10 +220,10 @@ class RoutingEngine {
|
|
|
192
220
|
const whenResult = await candidate.step.evaluateWhen(templateContext);
|
|
193
221
|
stepConditionContext.push(...whenResult.aiContextStrings);
|
|
194
222
|
}
|
|
195
|
-
// Check if any candidate is a completion marker (
|
|
196
|
-
const hasCompletionOption = candidates.some(c => c.
|
|
223
|
+
// Check if any candidate is a completion marker (isFlowComplete = true)
|
|
224
|
+
const hasCompletionOption = candidates.some(c => c.isFlowComplete);
|
|
197
225
|
const stepPrompt = await this.buildStepSelectionPrompt({
|
|
198
|
-
route,
|
|
226
|
+
flow: route,
|
|
199
227
|
currentStep,
|
|
200
228
|
candidates,
|
|
201
229
|
data: updatedSession.data || {},
|
|
@@ -205,9 +233,9 @@ class RoutingEngine {
|
|
|
205
233
|
context,
|
|
206
234
|
session: updatedSession,
|
|
207
235
|
stepConditionContext,
|
|
208
|
-
|
|
236
|
+
includeCompletion: hasCompletionOption,
|
|
209
237
|
});
|
|
210
|
-
const stepSchema = this.buildStepSelectionSchema(candidates.filter(c => !c.
|
|
238
|
+
const stepSchema = this.buildStepSelectionSchema(candidates.filter(c => !c.isFlowComplete).map((c) => c.step), hasCompletionOption);
|
|
211
239
|
const stepResult = await provider.generateMessage({
|
|
212
240
|
prompt: stepPrompt,
|
|
213
241
|
history: (0, utils_2.eventsToHistory)(history),
|
|
@@ -219,37 +247,37 @@ class RoutingEngine {
|
|
|
219
247
|
},
|
|
220
248
|
});
|
|
221
249
|
const selectedStepId = stepResult.structured?.selectedStepId;
|
|
222
|
-
// Check if AI selected
|
|
223
|
-
if (selectedStepId ===
|
|
224
|
-
utils_2.logger.debug(`[
|
|
225
|
-
utils_2.logger.debug(`[
|
|
250
|
+
// Check if AI selected flow completion
|
|
251
|
+
if (selectedStepId === '__COMPLETE__') {
|
|
252
|
+
utils_2.logger.debug(`[FlowRouter] Single-flow: AI selected flow completion`);
|
|
253
|
+
utils_2.logger.debug(`[FlowRouter] Single-flow: Reasoning: ${stepResult.structured?.reasoning}`);
|
|
226
254
|
return {
|
|
227
|
-
|
|
255
|
+
selectedFlow,
|
|
228
256
|
selectedStep: undefined,
|
|
229
257
|
responseDirectives: stepResult.structured?.responseDirectives,
|
|
230
258
|
session: updatedSession,
|
|
231
|
-
|
|
232
|
-
|
|
259
|
+
isFlowComplete: true,
|
|
260
|
+
completedFlows,
|
|
233
261
|
};
|
|
234
262
|
}
|
|
235
263
|
const selectedStep = candidates.find((c) => c.step.id === selectedStepId);
|
|
236
264
|
if (selectedStep) {
|
|
237
|
-
utils_2.logger.debug(`[
|
|
238
|
-
utils_2.logger.debug(`[
|
|
265
|
+
utils_2.logger.debug(`[FlowRouter] Single-flow: AI selected step: ${selectedStep.step.id}`);
|
|
266
|
+
utils_2.logger.debug(`[FlowRouter] Single-flow: Reasoning: ${stepResult.structured?.reasoning}`);
|
|
239
267
|
}
|
|
240
268
|
else {
|
|
241
|
-
utils_2.logger.warn(`[
|
|
269
|
+
utils_2.logger.warn(`[FlowConfigurationError] Invalid step ID returned: AI router returned a step ID that does not match any candidate. Falling back to first candidate. Check flow step ids and router configuration.`);
|
|
242
270
|
}
|
|
243
271
|
return {
|
|
244
|
-
|
|
272
|
+
selectedFlow,
|
|
245
273
|
selectedStep: selectedStep?.step || candidates[0].step,
|
|
246
274
|
responseDirectives: stepResult.structured?.responseDirectives,
|
|
247
275
|
session: updatedSession,
|
|
248
|
-
|
|
276
|
+
completedFlows,
|
|
249
277
|
};
|
|
250
278
|
}
|
|
251
279
|
/**
|
|
252
|
-
* Recursively traverse step chain to find first non-skipped step
|
|
280
|
+
* Recursively traverse step chain to find first non-skipped step using new condition evaluation
|
|
253
281
|
* @private
|
|
254
282
|
*/
|
|
255
283
|
async findFirstValidStepRecursiveWithConditions(currentStep, templateContext, visited) {
|
|
@@ -260,76 +288,78 @@ class RoutingEngine {
|
|
|
260
288
|
visited.add(currentStep.id);
|
|
261
289
|
const transitions = currentStep.getTransitions();
|
|
262
290
|
const allAiContextStrings = [];
|
|
291
|
+
// No transitions means implicit terminus — flow is complete
|
|
292
|
+
if (transitions.length === 0) {
|
|
293
|
+
return {
|
|
294
|
+
isFlowComplete: true,
|
|
295
|
+
aiContextStrings: allAiContextStrings,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
263
298
|
for (const transition of transitions) {
|
|
264
299
|
const target = transition;
|
|
265
|
-
// Check for END_ROUTE transition
|
|
266
|
-
if (target && target.id === constants_1.END_ROUTE_ID) {
|
|
267
|
-
// Found END_ROUTE - route is complete
|
|
268
|
-
return {
|
|
269
|
-
isRouteComplete: true,
|
|
270
|
-
aiContextStrings: allAiContextStrings,
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
300
|
if (!target)
|
|
274
301
|
continue;
|
|
275
|
-
// Evaluate
|
|
276
|
-
const skipResult = await target.
|
|
302
|
+
// Evaluate skip condition (code-only, if-shape)
|
|
303
|
+
const skipResult = await target.evaluateSkip(templateContext);
|
|
277
304
|
allAiContextStrings.push(...skipResult.aiContextStrings);
|
|
278
305
|
// If target should NOT be skipped, we found our step
|
|
279
306
|
if (!skipResult.shouldSkip) {
|
|
280
|
-
utils_2.logger.debug(`[
|
|
307
|
+
utils_2.logger.debug(`[FlowRouter] Found valid step after skipping: ${target.id}`);
|
|
281
308
|
return {
|
|
282
309
|
step: target,
|
|
283
|
-
|
|
310
|
+
isFlowComplete: false,
|
|
284
311
|
aiContextStrings: allAiContextStrings,
|
|
285
312
|
};
|
|
286
313
|
}
|
|
287
314
|
// Target should be skipped too - recurse deeper
|
|
288
|
-
utils_2.logger.debug(`[
|
|
315
|
+
utils_2.logger.debug(`[FlowRouter] Skipping step ${target.id} (skipIf condition met), continuing traversal...`);
|
|
289
316
|
const result = await this.findFirstValidStepRecursiveWithConditions(target, templateContext, visited);
|
|
290
317
|
// Collect AI context from recursive call
|
|
291
318
|
if (result.aiContextStrings) {
|
|
292
319
|
allAiContextStrings.push(...result.aiContextStrings);
|
|
293
320
|
}
|
|
294
|
-
// If we found something (a valid step or
|
|
295
|
-
if (result.step || result.
|
|
321
|
+
// If we found something (a valid step or flow complete), return it
|
|
322
|
+
if (result.step || result.isFlowComplete) {
|
|
296
323
|
return {
|
|
297
324
|
...result,
|
|
298
325
|
aiContextStrings: allAiContextStrings,
|
|
299
326
|
};
|
|
300
327
|
}
|
|
301
328
|
}
|
|
302
|
-
// No valid steps
|
|
303
|
-
return {
|
|
329
|
+
// No valid steps found in this branch — all skipped with no further transitions
|
|
330
|
+
return {
|
|
331
|
+
isFlowComplete: true,
|
|
332
|
+
aiContextStrings: allAiContextStrings,
|
|
333
|
+
};
|
|
304
334
|
}
|
|
305
335
|
/**
|
|
306
336
|
* Identify valid next candidate steps using new condition evaluation system
|
|
307
|
-
* Returns step with
|
|
337
|
+
* Returns step with isFlowComplete flag if flow is complete (all steps skipped or no transitions remain)
|
|
308
338
|
*
|
|
309
|
-
*
|
|
339
|
+
* Flow completion is implicit: when the last step has no transitions, the flow is done.
|
|
310
340
|
*/
|
|
311
341
|
async getCandidateStepsWithConditions(route, currentStep, templateContext) {
|
|
312
342
|
const candidates = [];
|
|
313
343
|
if (!currentStep) {
|
|
314
|
-
// Entering
|
|
344
|
+
// Entering flow for the first time — always start the step flow
|
|
315
345
|
const initialStep = route.initialStep;
|
|
316
|
-
const skipResult = await initialStep.
|
|
346
|
+
const skipResult = await initialStep.evaluateSkip(templateContext);
|
|
317
347
|
if (skipResult.shouldSkip) {
|
|
318
|
-
// Initial step should be skipped - recursively traverse to find first non-skipped step
|
|
348
|
+
// Initial step should be skipped - recursively traverse to find first non-skipped step
|
|
319
349
|
const result = await this.findFirstValidStepRecursiveWithConditions(initialStep, templateContext, new Set());
|
|
320
|
-
if (result.
|
|
321
|
-
// All steps are skipped and
|
|
322
|
-
utils_2.logger.debug(`[
|
|
350
|
+
if (result.isFlowComplete) {
|
|
351
|
+
// All steps are skipped and no transitions remain
|
|
352
|
+
utils_2.logger.debug(`[FlowRouter] Flow complete on entry: all steps skipped, no transitions remain`);
|
|
323
353
|
candidates.push({
|
|
324
354
|
step: initialStep,
|
|
325
|
-
|
|
355
|
+
isFlowComplete: true,
|
|
326
356
|
});
|
|
327
357
|
}
|
|
328
358
|
else if (result.step) {
|
|
329
359
|
// Found a non-skipped step
|
|
330
360
|
candidates.push({
|
|
331
361
|
step: result.step,
|
|
332
|
-
|
|
362
|
+
isFlowComplete: result.isFlowComplete || false,
|
|
333
363
|
});
|
|
334
364
|
}
|
|
335
365
|
// If no step found and not complete, fall through to return empty candidates
|
|
@@ -337,87 +367,84 @@ class RoutingEngine {
|
|
|
337
367
|
else {
|
|
338
368
|
candidates.push({
|
|
339
369
|
step: initialStep,
|
|
340
|
-
|
|
370
|
+
isFlowComplete: false,
|
|
341
371
|
});
|
|
342
372
|
}
|
|
343
373
|
return candidates;
|
|
344
374
|
}
|
|
345
|
-
// Continue normal step progression —
|
|
375
|
+
// Continue normal step progression — flows complete when last step has no transitions (implicit terminus)
|
|
346
376
|
const transitions = currentStep.getTransitions();
|
|
347
|
-
|
|
377
|
+
// No transitions means this is the last step — flow is complete
|
|
378
|
+
if (transitions.length === 0) {
|
|
379
|
+
utils_2.logger.debug(`[FlowRouter] Flow complete: current step has no transitions (implicit terminus)`);
|
|
380
|
+
return [
|
|
381
|
+
{
|
|
382
|
+
step: currentStep,
|
|
383
|
+
isFlowComplete: true,
|
|
384
|
+
},
|
|
385
|
+
];
|
|
386
|
+
}
|
|
348
387
|
for (const transition of transitions) {
|
|
349
388
|
const target = transition;
|
|
350
|
-
// Check for END_ROUTE transition (no target step)
|
|
351
|
-
if (target && target.id === constants_1.END_ROUTE_ID) {
|
|
352
|
-
hasEndRoute = true;
|
|
353
|
-
continue;
|
|
354
|
-
}
|
|
355
389
|
if (!target)
|
|
356
390
|
continue;
|
|
357
|
-
const skipResult = await target.
|
|
391
|
+
const skipResult = await target.evaluateSkip(templateContext);
|
|
358
392
|
if (skipResult.shouldSkip) {
|
|
359
|
-
utils_2.logger.debug(`[
|
|
360
|
-
// Recursively traverse to find next valid step
|
|
393
|
+
utils_2.logger.debug(`[FlowRouter] Skipping step ${target.id} (skip condition met)`);
|
|
394
|
+
// Recursively traverse to find next valid step
|
|
361
395
|
const result = await this.findFirstValidStepRecursiveWithConditions(target, templateContext, new Set([currentStep.id]) // Already visited current step
|
|
362
396
|
);
|
|
363
|
-
if (result.
|
|
364
|
-
|
|
397
|
+
if (result.isFlowComplete) {
|
|
398
|
+
// All forward paths lead to terminus
|
|
399
|
+
candidates.push({
|
|
400
|
+
step: currentStep,
|
|
401
|
+
isFlowComplete: true,
|
|
402
|
+
});
|
|
365
403
|
}
|
|
366
404
|
else if (result.step) {
|
|
367
405
|
// Found a non-skipped step deeper in the chain
|
|
368
406
|
candidates.push({
|
|
369
407
|
step: result.step,
|
|
370
|
-
|
|
408
|
+
isFlowComplete: false,
|
|
371
409
|
});
|
|
372
410
|
}
|
|
373
411
|
continue;
|
|
374
412
|
}
|
|
375
413
|
candidates.push({
|
|
376
414
|
step: target,
|
|
377
|
-
|
|
415
|
+
isFlowComplete: false,
|
|
378
416
|
});
|
|
379
417
|
}
|
|
380
|
-
// If no valid candidates found
|
|
418
|
+
// If no valid candidates found after evaluating all transitions
|
|
381
419
|
if (candidates.length === 0) {
|
|
382
|
-
//
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
return [
|
|
387
|
-
{
|
|
388
|
-
step: currentStep,
|
|
389
|
-
isRouteComplete: true,
|
|
390
|
-
},
|
|
391
|
-
];
|
|
392
|
-
}
|
|
393
|
-
// Otherwise, stay in current step if it's still valid
|
|
394
|
-
const currentSkipResult = await currentStep.evaluateSkipIf(templateContext);
|
|
395
|
-
if (!currentSkipResult.shouldSkip) {
|
|
396
|
-
candidates.push({
|
|
420
|
+
// All transitions were skipped — flow is complete
|
|
421
|
+
utils_2.logger.debug(`[FlowRouter] Flow complete: all transitions skipped`);
|
|
422
|
+
return [
|
|
423
|
+
{
|
|
397
424
|
step: currentStep,
|
|
398
|
-
|
|
399
|
-
}
|
|
400
|
-
|
|
425
|
+
isFlowComplete: true,
|
|
426
|
+
},
|
|
427
|
+
];
|
|
401
428
|
}
|
|
402
429
|
return candidates;
|
|
403
430
|
}
|
|
404
431
|
/**
|
|
405
432
|
* Full routing orchestration: builds prompt and schema, calls AI, selects route/step,
|
|
406
|
-
* and updates the session (including initialData merge when entering a new
|
|
433
|
+
* and updates the session (including initialData merge when entering a new flow).
|
|
407
434
|
*
|
|
408
435
|
* OPTIMIZATION: If there's only 1 route, skips route scoring and only does step selection.
|
|
409
|
-
* CROSS-
|
|
436
|
+
* CROSS-FLOW COMPLETION: Evaluates all flows for completion based on collected data.
|
|
410
437
|
*/
|
|
411
|
-
async
|
|
412
|
-
const {
|
|
413
|
-
if (
|
|
438
|
+
async decideFlowAndStep(params) {
|
|
439
|
+
const { flows, session, history, agentOptions, provider, context, signal, } = params;
|
|
440
|
+
if (flows.length === 0) {
|
|
414
441
|
return { session };
|
|
415
442
|
}
|
|
416
|
-
// SESSION RESUME: If the session has a pre-set
|
|
443
|
+
// SESSION RESUME: If the session has a pre-set flow and there are no user
|
|
417
444
|
// messages in the history, honor the pre-set position without AI routing.
|
|
418
445
|
// This supports programmatic session setup and persistence-based resume.
|
|
419
446
|
const resumeResult = await this.handleSessionResume({
|
|
420
|
-
|
|
447
|
+
flows,
|
|
421
448
|
session,
|
|
422
449
|
history,
|
|
423
450
|
context,
|
|
@@ -425,16 +452,24 @@ class RoutingEngine {
|
|
|
425
452
|
if (resumeResult) {
|
|
426
453
|
return resumeResult;
|
|
427
454
|
}
|
|
428
|
-
//
|
|
429
|
-
|
|
430
|
-
//
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
455
|
+
// Exclude flows that completed earlier in this session unless explicitly
|
|
456
|
+
// re-entrant. A completed flow surrenders the conversation back to
|
|
457
|
+
// routing — it cannot be re-selected without `flow.reentrant: true`.
|
|
458
|
+
const reEntryFiltered = flows.filter((f) => !(0, utils_1.isFlowCompletedThisSession)(session, f.id) || f.reentrant === true);
|
|
459
|
+
const excludedFlows = flows.length - reEntryFiltered.length;
|
|
460
|
+
if (excludedFlows > 0) {
|
|
461
|
+
utils_2.logger.debug(`[FlowRouter] Excluded ${excludedFlows} completed (non-reentrant) flow(s) from routing candidates`);
|
|
462
|
+
}
|
|
463
|
+
// CROSS-FLOW COMPLETION EVALUATION: Check all eligible flows for completion
|
|
464
|
+
const completedFlows = this.evaluateFlowCompletions(reEntryFiltered, session.data || {});
|
|
465
|
+
// Log completed flows
|
|
466
|
+
if (completedFlows.length > 0) {
|
|
467
|
+
utils_2.logger.debug(`[FlowRouter] Found ${completedFlows.length} completed routes: ${completedFlows.map(r => r.title).join(', ')}`);
|
|
468
|
+
}
|
|
469
|
+
// OPTIMIZATION: Single flow - skip flow scoring, only do step selection
|
|
470
|
+
if (reEntryFiltered.length === 1) {
|
|
471
|
+
const result = await this.decideSingleFlowStep({
|
|
472
|
+
route: reEntryFiltered[0],
|
|
438
473
|
session,
|
|
439
474
|
history,
|
|
440
475
|
agentOptions,
|
|
@@ -444,9 +479,15 @@ class RoutingEngine {
|
|
|
444
479
|
});
|
|
445
480
|
return {
|
|
446
481
|
...result,
|
|
447
|
-
|
|
482
|
+
completedFlows,
|
|
448
483
|
};
|
|
449
484
|
}
|
|
485
|
+
// No eligible flows after re-entry filtering — caller falls back to the
|
|
486
|
+
// generic non-flow response path.
|
|
487
|
+
if (reEntryFiltered.length === 0) {
|
|
488
|
+
utils_2.logger.debug(`[FlowRouter] All flows completed and none are reentrant — releasing session to fallback`);
|
|
489
|
+
return { session, completedFlows };
|
|
490
|
+
}
|
|
450
491
|
const lastUserMessage = (0, utils_2.getLastMessageFromHistory)(history);
|
|
451
492
|
const templateContext = (0, utils_2.createTemplateContext)({
|
|
452
493
|
context,
|
|
@@ -454,56 +495,55 @@ class RoutingEngine {
|
|
|
454
495
|
history,
|
|
455
496
|
data: session.data
|
|
456
497
|
});
|
|
457
|
-
// Apply
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
// Use filtered routes for further processing
|
|
498
|
+
// Apply flow filtering with new condition evaluation system
|
|
499
|
+
const whenResult = await this.filterFlowsByWhen(reEntryFiltered, templateContext);
|
|
500
|
+
// Use filtered flows for further processing
|
|
461
501
|
const eligibleRoutes = whenResult.eligibleRoutes;
|
|
462
|
-
utils_2.logger.debug(`[
|
|
463
|
-
let
|
|
464
|
-
let
|
|
465
|
-
let
|
|
502
|
+
utils_2.logger.debug(`[FlowRouter] Flow filtering: ${flows.length} total → ${reEntryFiltered.length} after re-entry filter → ${eligibleRoutes.length} after when`);
|
|
503
|
+
let activeFlowSteps;
|
|
504
|
+
let activeFlow;
|
|
505
|
+
let isFlowComplete = false;
|
|
466
506
|
let updatedSession = session;
|
|
467
|
-
if (session.
|
|
468
|
-
|
|
469
|
-
if (
|
|
507
|
+
if (session.currentFlow) {
|
|
508
|
+
activeFlow = eligibleRoutes.find((r) => r.id === session.currentFlow?.id);
|
|
509
|
+
if (activeFlow) {
|
|
470
510
|
const currentStep = session.currentStep
|
|
471
|
-
?
|
|
511
|
+
? activeFlow.getStep(session.currentStep.id)
|
|
472
512
|
: undefined;
|
|
473
513
|
const activeTemplateContext = (0, utils_2.createTemplateContext)({
|
|
474
514
|
...templateContext,
|
|
475
515
|
session: updatedSession,
|
|
476
516
|
data: updatedSession.data
|
|
477
517
|
});
|
|
478
|
-
const candidates = await this.getCandidateStepsWithConditions(
|
|
479
|
-
// Check if
|
|
518
|
+
const candidates = await this.getCandidateStepsWithConditions(activeFlow, currentStep, activeTemplateContext);
|
|
519
|
+
// Check if flow is complete
|
|
480
520
|
// getCandidateStepsWithConditions now automatically handles completion when required fields are collected
|
|
481
|
-
if (candidates.length === 1 && candidates[0].
|
|
482
|
-
|
|
483
|
-
utils_2.logger.debug(`[
|
|
521
|
+
if (candidates.length === 1 && candidates[0].isFlowComplete) {
|
|
522
|
+
isFlowComplete = true;
|
|
523
|
+
utils_2.logger.debug(`[FlowRouter] Flow ${activeFlow.title} is complete - all required fields collected or last step reached`);
|
|
484
524
|
// Don't include steps in routing if route is complete
|
|
485
|
-
|
|
525
|
+
activeFlowSteps = undefined;
|
|
486
526
|
}
|
|
487
527
|
else if (candidates.length === 0) {
|
|
488
|
-
// No candidates available — don't end
|
|
489
|
-
utils_2.logger.debug(`[
|
|
490
|
-
|
|
528
|
+
// No candidates available — don't end flow based on data alone
|
|
529
|
+
utils_2.logger.debug(`[FlowRouter] Flow ${activeFlow.title} has no valid candidate steps`);
|
|
530
|
+
activeFlowSteps = undefined;
|
|
491
531
|
}
|
|
492
532
|
else {
|
|
493
533
|
// Multiple candidates or single non-complete candidate
|
|
494
|
-
|
|
495
|
-
utils_2.logger.debug(`[
|
|
534
|
+
activeFlowSteps = candidates.map((c) => c.step);
|
|
535
|
+
utils_2.logger.debug(`[FlowRouter] Found ${activeFlowSteps.length} candidate steps for active route`);
|
|
496
536
|
}
|
|
497
537
|
}
|
|
498
538
|
}
|
|
499
|
-
const routingSchema = this.
|
|
539
|
+
const routingSchema = this.buildDynamicFlowSchema(eligibleRoutes, undefined, activeFlowSteps);
|
|
500
540
|
const routingPrompt = await this.buildRoutingPrompt({
|
|
501
541
|
history,
|
|
502
|
-
|
|
542
|
+
flows: eligibleRoutes,
|
|
503
543
|
lastMessage: lastUserMessage,
|
|
504
544
|
agentOptions,
|
|
505
545
|
session,
|
|
506
|
-
|
|
546
|
+
activeFlowSteps,
|
|
507
547
|
context,
|
|
508
548
|
});
|
|
509
549
|
const routingResult = await provider.generateMessage({
|
|
@@ -516,115 +556,92 @@ class RoutingEngine {
|
|
|
516
556
|
schemaName: "routing_output",
|
|
517
557
|
},
|
|
518
558
|
});
|
|
519
|
-
let
|
|
559
|
+
let selectedFlow;
|
|
520
560
|
let selectedStep;
|
|
521
561
|
let responseDirectives;
|
|
522
|
-
if (routingResult.structured?.
|
|
523
|
-
// Use cross-
|
|
524
|
-
const optimalRoute = this.
|
|
525
|
-
// If no optimal
|
|
562
|
+
if (routingResult.structured?.flows) {
|
|
563
|
+
// Use cross-flow completion evaluation to select optimal flow
|
|
564
|
+
const optimalRoute = this.selectOptimalFlow(eligibleRoutes, updatedSession.data || {}, routingResult.structured.flows, updatedSession.currentFlow?.id);
|
|
565
|
+
// If no optimal flow found, check why
|
|
526
566
|
if (!optimalRoute) {
|
|
527
567
|
if (eligibleRoutes.length === 0) {
|
|
528
|
-
// No
|
|
529
|
-
utils_2.logger.debug(`[
|
|
530
|
-
|
|
568
|
+
// No flows passed filtering
|
|
569
|
+
utils_2.logger.debug(`[FlowRouter] No eligible flows available - all flows filtered out`);
|
|
570
|
+
selectedFlow = undefined;
|
|
531
571
|
}
|
|
532
572
|
else {
|
|
533
|
-
// Routes exist but
|
|
534
|
-
// This means all
|
|
535
|
-
utils_2.logger.debug(`[
|
|
536
|
-
|
|
573
|
+
// Routes exist but selectOptimalFlow returned undefined
|
|
574
|
+
// This means all flows are 100% complete
|
|
575
|
+
utils_2.logger.debug(`[FlowRouter] No optimal route found - all ${eligibleRoutes.length} eligible routes are complete`);
|
|
576
|
+
selectedFlow = undefined;
|
|
537
577
|
}
|
|
538
578
|
}
|
|
539
579
|
else {
|
|
540
|
-
|
|
580
|
+
selectedFlow = optimalRoute;
|
|
541
581
|
}
|
|
542
582
|
responseDirectives = routingResult.structured.responseDirectives;
|
|
543
|
-
if (
|
|
583
|
+
if (selectedFlow === activeFlow &&
|
|
544
584
|
routingResult.structured.selectedStepId &&
|
|
545
|
-
|
|
546
|
-
selectedStep =
|
|
585
|
+
activeFlow) {
|
|
586
|
+
selectedStep = activeFlow.getStep(routingResult.structured.selectedStepId);
|
|
547
587
|
if (selectedStep) {
|
|
548
|
-
utils_2.logger.debug(`[
|
|
549
|
-
utils_2.logger.debug(`[
|
|
588
|
+
utils_2.logger.debug(`[FlowRouter] AI selected step: ${selectedStep.id} in active route`);
|
|
589
|
+
utils_2.logger.debug(`[FlowRouter] Step reasoning: ${routingResult.structured.stepReasoning}`);
|
|
550
590
|
}
|
|
551
591
|
}
|
|
552
|
-
if (
|
|
553
|
-
utils_2.logger.debug(`[
|
|
554
|
-
updatedSession = this.
|
|
592
|
+
if (selectedFlow) {
|
|
593
|
+
utils_2.logger.debug(`[FlowRouter] Selected route: ${selectedFlow.title}`);
|
|
594
|
+
updatedSession = this.enterFlowIfNeeded(updatedSession, selectedFlow);
|
|
555
595
|
}
|
|
556
596
|
}
|
|
557
597
|
return {
|
|
558
|
-
|
|
598
|
+
selectedFlow,
|
|
559
599
|
selectedStep,
|
|
560
600
|
responseDirectives,
|
|
561
601
|
session: updatedSession,
|
|
562
|
-
|
|
563
|
-
|
|
602
|
+
isFlowComplete,
|
|
603
|
+
completedFlows,
|
|
564
604
|
};
|
|
565
605
|
}
|
|
566
606
|
/**
|
|
567
|
-
* Filter
|
|
568
|
-
* @param routes -
|
|
569
|
-
* @param templateContext - Context for condition evaluation
|
|
570
|
-
* @returns Object with eligible routes and collected AI context strings
|
|
571
|
-
*/
|
|
572
|
-
async filterRoutesBySkipIf(routes, templateContext) {
|
|
573
|
-
const eligibleRoutes = [];
|
|
574
|
-
const aiContextStrings = [];
|
|
575
|
-
for (const route of routes) {
|
|
576
|
-
const skipResult = await route.evaluateSkipIf(templateContext);
|
|
577
|
-
// Collect AI context strings from skipIf conditions
|
|
578
|
-
aiContextStrings.push(...skipResult.aiContextStrings);
|
|
579
|
-
// If route should not be skipped, it's eligible
|
|
580
|
-
if (!skipResult.programmaticResult) {
|
|
581
|
-
eligibleRoutes.push(route);
|
|
582
|
-
}
|
|
583
|
-
else {
|
|
584
|
-
utils_2.logger.debug(`[RoutingEngine] Skipping route ${route.title} (skipIf condition met)`);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
return { eligibleRoutes, aiContextStrings };
|
|
588
|
-
}
|
|
589
|
-
/**
|
|
590
|
-
* Filter routes based on when conditions
|
|
591
|
-
* @param routes - Routes that passed skipIf filtering
|
|
607
|
+
* Filter flows based on when conditions
|
|
608
|
+
* @param routes - Flows that passed skipIf filtering
|
|
592
609
|
* @param templateContext - Context for condition evaluation
|
|
593
|
-
* @returns Object with eligible
|
|
610
|
+
* @returns Object with eligible flows and collected AI context strings
|
|
594
611
|
*/
|
|
595
|
-
async
|
|
612
|
+
async filterFlowsByWhen(routes, templateContext) {
|
|
596
613
|
const eligibleRoutes = [];
|
|
597
614
|
const aiContextStrings = [];
|
|
598
615
|
for (const route of routes) {
|
|
599
616
|
const whenResult = await route.evaluateWhen(templateContext);
|
|
600
617
|
// Collect AI context strings from when conditions
|
|
601
618
|
aiContextStrings.push(...whenResult.aiContextStrings);
|
|
602
|
-
// If
|
|
619
|
+
// If flow has no programmatic conditions or they evaluate to true, it's eligible
|
|
603
620
|
if (!whenResult.hasProgrammaticConditions || whenResult.programmaticResult) {
|
|
604
621
|
eligibleRoutes.push(route);
|
|
605
622
|
}
|
|
606
623
|
else {
|
|
607
|
-
utils_2.logger.debug(`[
|
|
624
|
+
utils_2.logger.debug(`[FlowRouter] Flow ${route.title} not eligible (when condition not met)`);
|
|
608
625
|
}
|
|
609
626
|
}
|
|
610
627
|
return { eligibleRoutes, aiContextStrings };
|
|
611
628
|
}
|
|
612
629
|
/**
|
|
613
|
-
* Evaluate all
|
|
614
|
-
* @param routes - All available
|
|
630
|
+
* Evaluate all flows for completion based on collected data
|
|
631
|
+
* @param routes - All available flows
|
|
615
632
|
* @param data - Currently collected agent-level data
|
|
616
|
-
* @returns Array of
|
|
633
|
+
* @returns Array of flows that are complete
|
|
617
634
|
*/
|
|
618
|
-
|
|
635
|
+
evaluateFlowCompletions(routes, data) {
|
|
619
636
|
return routes.filter(route => route.isComplete(data));
|
|
620
637
|
}
|
|
621
638
|
/**
|
|
622
|
-
* Get completion status for all
|
|
623
|
-
* @param routes - All available
|
|
639
|
+
* Get completion status for all flows
|
|
640
|
+
* @param routes - All available flows
|
|
624
641
|
* @param data - Currently collected agent-level data
|
|
625
|
-
* @returns Map of
|
|
642
|
+
* @returns Map of flow ID to completion progress (0-1)
|
|
626
643
|
*/
|
|
627
|
-
|
|
644
|
+
getFlowCompletionStatus(routes, data) {
|
|
628
645
|
const completionStatus = new Map();
|
|
629
646
|
for (const route of routes) {
|
|
630
647
|
const progress = route.getCompletionProgress(data);
|
|
@@ -633,28 +650,28 @@ class RoutingEngine {
|
|
|
633
650
|
return completionStatus;
|
|
634
651
|
}
|
|
635
652
|
/**
|
|
636
|
-
* Find the best
|
|
637
|
-
* Prioritizes
|
|
638
|
-
* IMPORTANT: Completed
|
|
639
|
-
* @param routes - All available
|
|
653
|
+
* Find the best flow to continue based on completion status and user intent
|
|
654
|
+
* Prioritizes flows that are partially complete but not finished
|
|
655
|
+
* IMPORTANT: Completed flows are excluded to prevent re-entering finished tasks
|
|
656
|
+
* @param routes - All available flows
|
|
640
657
|
* @param data - Currently collected agent-level data
|
|
641
658
|
* @param routeScores - AI-generated route scores from routing decision
|
|
642
|
-
* @returns
|
|
659
|
+
* @returns Flow that should be prioritized for continuation
|
|
643
660
|
*/
|
|
644
|
-
|
|
645
|
-
const completionStatus = this.
|
|
646
|
-
const switchMargin = this.options?.
|
|
661
|
+
selectOptimalFlow(routes, data, routeScores, currentRouteId) {
|
|
662
|
+
const completionStatus = this.getFlowCompletionStatus(routes, data);
|
|
663
|
+
const switchMargin = this.options?.flowSwitchMargin ?? 15;
|
|
647
664
|
// Create weighted scores combining AI intent scores with completion progress
|
|
648
665
|
const weightedScores = [];
|
|
649
666
|
for (const route of routes) {
|
|
650
667
|
const aiScore = routeScores[route.id] || 0;
|
|
651
668
|
const completionProgress = completionStatus.get(route.id) || 0;
|
|
652
|
-
// ALWAYS skip fully completed
|
|
669
|
+
// ALWAYS skip fully completed flows to prevent re-entering finished tasks
|
|
653
670
|
if (completionProgress >= 1.0) {
|
|
654
|
-
utils_2.logger.debug(`[
|
|
671
|
+
utils_2.logger.debug(`[FlowRouter] Excluding completed flow: ${route.title} (100% complete)`);
|
|
655
672
|
continue;
|
|
656
673
|
}
|
|
657
|
-
// Boost partially complete
|
|
674
|
+
// Boost partially complete flows that match user intent
|
|
658
675
|
let weightedScore = aiScore;
|
|
659
676
|
if (completionProgress > 0 && completionProgress < 1.0) {
|
|
660
677
|
weightedScore += (completionProgress * 20); // Up to 20 point boost
|
|
@@ -667,48 +684,48 @@ class RoutingEngine {
|
|
|
667
684
|
return undefined;
|
|
668
685
|
}
|
|
669
686
|
// Apply sticky routing: if there's a current route, only switch if the
|
|
670
|
-
// best alternative exceeds the current
|
|
687
|
+
// best alternative exceeds the current flow's score by the configured margin
|
|
671
688
|
if (currentRouteId) {
|
|
672
689
|
const currentEntry = weightedScores.find(e => e.route.id === currentRouteId);
|
|
673
690
|
const bestEntry = weightedScores[0];
|
|
674
691
|
if (currentEntry && bestEntry.route.id !== currentRouteId) {
|
|
675
692
|
if (bestEntry.score < currentEntry.score + switchMargin) {
|
|
676
|
-
utils_2.logger.debug(`[
|
|
693
|
+
utils_2.logger.debug(`[FlowRouter] Staying on current flow: ${currentEntry.route.title} ` +
|
|
677
694
|
`(current: ${currentEntry.score}, best alternative: ${bestEntry.score}, ` +
|
|
678
695
|
`margin required: ${switchMargin})`);
|
|
679
696
|
return currentEntry.route;
|
|
680
697
|
}
|
|
681
|
-
utils_2.logger.debug(`[
|
|
698
|
+
utils_2.logger.debug(`[FlowRouter] Switching flow: ${currentEntry.route.title} → ${bestEntry.route.title} ` +
|
|
682
699
|
`(current: ${currentEntry.score}, alternative: ${bestEntry.score}, ` +
|
|
683
700
|
`margin: ${switchMargin})`);
|
|
684
701
|
}
|
|
685
702
|
}
|
|
686
|
-
utils_2.logger.debug(`[
|
|
703
|
+
utils_2.logger.debug(`[FlowRouter] Selected optimal route: ${weightedScores[0].route.title} ` +
|
|
687
704
|
`(AI: ${routeScores[weightedScores[0].route.id]}, ` +
|
|
688
705
|
`Completion: ${(completionStatus.get(weightedScores[0].route.id) || 0) * 100}%, ` +
|
|
689
706
|
`Weighted: ${weightedScores[0].score})`);
|
|
690
707
|
return weightedScores[0].route;
|
|
691
708
|
}
|
|
692
709
|
/**
|
|
693
|
-
* Build prompt for step selection within a single
|
|
710
|
+
* Build prompt for step selection within a single flow
|
|
694
711
|
* @private
|
|
695
712
|
*/
|
|
696
713
|
async buildStepSelectionPrompt(params) {
|
|
697
|
-
const { route, currentStep, candidates, data, history, lastMessage, agentOptions, context, session, stepConditionContext,
|
|
714
|
+
const { flow: route, currentStep, candidates, data, history, lastMessage, agentOptions, context, session, stepConditionContext, includeCompletion = false, } = params;
|
|
698
715
|
const templateContext = (0, utils_2.createTemplateContext)({ context, session, history });
|
|
699
716
|
const pc = new PromptComposer_1.PromptComposer(templateContext, this.options?.promptSectionCache);
|
|
700
717
|
// Add agent metadata
|
|
701
718
|
if (agentOptions) {
|
|
702
719
|
await pc.addAgentMeta(agentOptions);
|
|
703
720
|
}
|
|
704
|
-
// Add
|
|
705
|
-
await pc.addInstruction(`Active
|
|
721
|
+
// Add flow context
|
|
722
|
+
await pc.addInstruction(`Active Flow: ${route.title}\nDescription: ${route.description || "N/A"}`);
|
|
706
723
|
// Add current step context
|
|
707
724
|
if (currentStep) {
|
|
708
725
|
await pc.addInstruction(`Current Step: ${currentStep.id}\nDescription: ${currentStep.description || "N/A"}`);
|
|
709
726
|
}
|
|
710
727
|
else {
|
|
711
|
-
await pc.addInstruction("Current Step: None (entering
|
|
728
|
+
await pc.addInstruction("Current Step: None (entering flow)");
|
|
712
729
|
}
|
|
713
730
|
// Add collected data context
|
|
714
731
|
if (Object.keys(data).length > 0) {
|
|
@@ -772,8 +789,8 @@ class RoutingEngine {
|
|
|
772
789
|
"- Choose the step that makes the most sense for moving the conversation forward",
|
|
773
790
|
"- Steps with skipIf conditions that are met have already been filtered out",
|
|
774
791
|
];
|
|
775
|
-
if (
|
|
776
|
-
decisionRules.push("", `- You can select '
|
|
792
|
+
if (includeCompletion) {
|
|
793
|
+
decisionRules.push("", `- You can select '__COMPLETE__' to complete this flow if:`, " * All required data has been collected", " * The user's intent suggests they're done with this task", " * No further steps are needed to fulfill the user's request");
|
|
777
794
|
}
|
|
778
795
|
decisionRules.push("", "Return ONLY JSON matching the provided schema.");
|
|
779
796
|
await pc.addInstruction(decisionRules.join("\n"));
|
|
@@ -783,11 +800,11 @@ class RoutingEngine {
|
|
|
783
800
|
* Build schema for step selection
|
|
784
801
|
* @private
|
|
785
802
|
*/
|
|
786
|
-
buildStepSelectionSchema(validSteps,
|
|
803
|
+
buildStepSelectionSchema(validSteps, includeCompletion = false) {
|
|
787
804
|
const stepIds = validSteps.map((s) => s.id);
|
|
788
|
-
// Add
|
|
789
|
-
if (
|
|
790
|
-
stepIds.push(
|
|
805
|
+
// Add completion option if requested (when required fields are complete)
|
|
806
|
+
if (includeCompletion) {
|
|
807
|
+
stepIds.push('__COMPLETE__');
|
|
791
808
|
}
|
|
792
809
|
return {
|
|
793
810
|
description: "Step transition decision based on conversation context and collected data",
|
|
@@ -801,8 +818,8 @@ class RoutingEngine {
|
|
|
801
818
|
selectedStepId: {
|
|
802
819
|
type: "string",
|
|
803
820
|
nullable: false,
|
|
804
|
-
description:
|
|
805
|
-
?
|
|
821
|
+
description: includeCompletion
|
|
822
|
+
? "The ID of the selected step to transition to, or '__COMPLETE__' to complete the flow"
|
|
806
823
|
: "The ID of the selected step to transition to",
|
|
807
824
|
enum: stepIds,
|
|
808
825
|
},
|
|
@@ -816,20 +833,20 @@ class RoutingEngine {
|
|
|
816
833
|
additionalProperties: false,
|
|
817
834
|
};
|
|
818
835
|
}
|
|
819
|
-
|
|
836
|
+
buildDynamicFlowSchema(routes, extrasSchema, activeFlowSteps) {
|
|
820
837
|
const routeIds = routes.map((r) => r.id);
|
|
821
838
|
const routeProperties = {};
|
|
822
839
|
for (const id of routeIds) {
|
|
823
840
|
routeProperties[id] = {
|
|
824
841
|
type: "number",
|
|
825
842
|
nullable: false,
|
|
826
|
-
description: `Score for
|
|
843
|
+
description: `Score for flow ${id} based on direct evidence, context and semantic fit (0-100)`,
|
|
827
844
|
minimum: 0,
|
|
828
845
|
maximum: 100,
|
|
829
846
|
};
|
|
830
847
|
}
|
|
831
848
|
const base = {
|
|
832
|
-
description: "Full intent analysis: score ALL available
|
|
849
|
+
description: "Full intent analysis: score ALL available flows (0-100) using evidence and context",
|
|
833
850
|
type: "object",
|
|
834
851
|
properties: {
|
|
835
852
|
context: {
|
|
@@ -837,12 +854,12 @@ class RoutingEngine {
|
|
|
837
854
|
nullable: false,
|
|
838
855
|
description: "Brief summary of the user's intent/context",
|
|
839
856
|
},
|
|
840
|
-
|
|
857
|
+
flows: {
|
|
841
858
|
type: "object",
|
|
842
859
|
properties: routeProperties,
|
|
843
860
|
required: routeIds,
|
|
844
861
|
nullable: false,
|
|
845
|
-
description: "Mapping of
|
|
862
|
+
description: "Mapping of flowId to score (0-100)",
|
|
846
863
|
},
|
|
847
864
|
responseDirectives: {
|
|
848
865
|
type: "array",
|
|
@@ -850,17 +867,17 @@ class RoutingEngine {
|
|
|
850
867
|
description: "Optional bullet points the response should address (concise)",
|
|
851
868
|
},
|
|
852
869
|
},
|
|
853
|
-
required: ["context", "
|
|
870
|
+
required: ["context", "flows"],
|
|
854
871
|
additionalProperties: false,
|
|
855
872
|
};
|
|
856
|
-
// Add step selection fields if there's an active
|
|
857
|
-
if (
|
|
873
|
+
// Add step selection fields if there's an active flow with steps
|
|
874
|
+
if (activeFlowSteps && activeFlowSteps.length > 0) {
|
|
858
875
|
base.properties = base.properties || {};
|
|
859
876
|
base.properties.selectedStepId = {
|
|
860
877
|
type: "string",
|
|
861
878
|
nullable: false,
|
|
862
|
-
description: "The step ID to transition to within the active
|
|
863
|
-
enum:
|
|
879
|
+
description: "The step ID to transition to within the active flow (required if continuing in current flow)",
|
|
880
|
+
enum: activeFlowSteps.map((s) => s.id),
|
|
864
881
|
};
|
|
865
882
|
base.properties.stepReasoning = {
|
|
866
883
|
type: "string",
|
|
@@ -880,7 +897,7 @@ class RoutingEngine {
|
|
|
880
897
|
return base;
|
|
881
898
|
}
|
|
882
899
|
async buildRoutingPrompt(params) {
|
|
883
|
-
const { history, routes, lastMessage, agentOptions, session,
|
|
900
|
+
const { history, flows: routes, lastMessage, agentOptions, session, activeFlowSteps, context, } = params;
|
|
884
901
|
const templateContext = (0, utils_2.createTemplateContext)({ context, session, history });
|
|
885
902
|
const pc = new PromptComposer_1.PromptComposer(templateContext, this.options?.promptSectionCache);
|
|
886
903
|
if (agentOptions) {
|
|
@@ -888,10 +905,10 @@ class RoutingEngine {
|
|
|
888
905
|
}
|
|
889
906
|
await pc.addInstruction("Task: Intent analysis and route scoring (0-100). Score ALL listed routes.");
|
|
890
907
|
// Add session context if available
|
|
891
|
-
if (session?.
|
|
908
|
+
if (session?.currentFlow) {
|
|
892
909
|
const sessionInfo = [
|
|
893
910
|
"Current conversation context:",
|
|
894
|
-
`- Active route: ${session.
|
|
911
|
+
`- Active route: ${session.currentFlow.title} (${session.currentFlow.id})`,
|
|
895
912
|
];
|
|
896
913
|
if (session.currentStep) {
|
|
897
914
|
sessionInfo.push(`- Current step: ${session.currentStep.id}`);
|
|
@@ -905,16 +922,16 @@ class RoutingEngine {
|
|
|
905
922
|
sessionInfo.push("Note: User is mid-conversation. They may want to continue current route or switch to a new one based on their intent.");
|
|
906
923
|
await pc.addInstruction(sessionInfo.join("\n"));
|
|
907
924
|
// Add cross-route completion status
|
|
908
|
-
const completionStatus = this.
|
|
909
|
-
const
|
|
925
|
+
const completionStatus = this.getFlowCompletionStatus(routes, session.data || {});
|
|
926
|
+
const completedFlows = this.evaluateFlowCompletions(routes, session.data || {});
|
|
910
927
|
if (completionStatus.size > 0) {
|
|
911
928
|
const statusInfo = [
|
|
912
929
|
"",
|
|
913
|
-
"
|
|
930
|
+
"Flow completion status based on collected data:",
|
|
914
931
|
];
|
|
915
932
|
for (const route of routes) {
|
|
916
933
|
const progress = completionStatus.get(route.id) || 0;
|
|
917
|
-
const isComplete =
|
|
934
|
+
const isComplete = completedFlows.includes(route);
|
|
918
935
|
const progressPercent = Math.round(progress * 100);
|
|
919
936
|
statusInfo.push(`- ${route.title}: ${progressPercent}% complete${isComplete ? ' ✓ COMPLETE' : ''}`);
|
|
920
937
|
if (!isComplete && route.requiredFields) {
|
|
@@ -928,14 +945,14 @@ class RoutingEngine {
|
|
|
928
945
|
await pc.addInstruction(statusInfo.join("\n"));
|
|
929
946
|
}
|
|
930
947
|
// Add available steps for the active route
|
|
931
|
-
if (
|
|
948
|
+
if (activeFlowSteps && activeFlowSteps.length > 0) {
|
|
932
949
|
const stepInfo = [
|
|
933
950
|
"",
|
|
934
951
|
"Available steps in active route (choose one to transition to):",
|
|
935
952
|
];
|
|
936
953
|
const activeStepConditionContext = [];
|
|
937
|
-
for (const step of
|
|
938
|
-
const idx =
|
|
954
|
+
for (const step of activeFlowSteps) {
|
|
955
|
+
const idx = activeFlowSteps.indexOf(step);
|
|
939
956
|
stepInfo.push(`${idx + 1}. Step: ${step.id}`);
|
|
940
957
|
if (step.description) {
|
|
941
958
|
stepInfo.push(` Description: ${step.description}`);
|
|
@@ -979,7 +996,7 @@ class RoutingEngine {
|
|
|
979
996
|
}
|
|
980
997
|
await pc.addInteractionHistory(history);
|
|
981
998
|
await pc.addLastMessage(lastMessage);
|
|
982
|
-
await pc.
|
|
999
|
+
await pc.addFlowOverview(routes);
|
|
983
1000
|
await pc.addInstruction([
|
|
984
1001
|
"Scoring rules:",
|
|
985
1002
|
"- 90-100: explicit keywords + clear intent",
|
|
@@ -992,5 +1009,5 @@ class RoutingEngine {
|
|
|
992
1009
|
return pc.build();
|
|
993
1010
|
}
|
|
994
1011
|
}
|
|
995
|
-
exports.
|
|
996
|
-
//# sourceMappingURL=
|
|
1012
|
+
exports.FlowRouter = FlowRouter;
|
|
1013
|
+
//# sourceMappingURL=FlowRouter.js.map
|