@falai/agent 2.2.4 → 2.4.2
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 +2 -2
- package/dist/adapters/MemoryAdapter.d.ts.map +1 -1
- package/dist/adapters/MemoryAdapter.js +10 -1
- package/dist/adapters/MemoryAdapter.js.map +1 -1
- package/dist/adapters/MongoAdapter.d.ts.map +1 -1
- package/dist/adapters/MongoAdapter.js +33 -2
- package/dist/adapters/MongoAdapter.js.map +1 -1
- package/dist/adapters/OpenSearchAdapter.d.ts.map +1 -1
- package/dist/adapters/OpenSearchAdapter.js +15 -1
- package/dist/adapters/OpenSearchAdapter.js.map +1 -1
- package/dist/adapters/PostgreSQLAdapter.d.ts.map +1 -1
- package/dist/adapters/PostgreSQLAdapter.js +35 -5
- package/dist/adapters/PostgreSQLAdapter.js.map +1 -1
- package/dist/adapters/PrismaAdapter.d.ts.map +1 -1
- package/dist/adapters/PrismaAdapter.js +58 -15
- package/dist/adapters/PrismaAdapter.js.map +1 -1
- package/dist/adapters/RedisAdapter.d.ts.map +1 -1
- package/dist/adapters/RedisAdapter.js +10 -1
- package/dist/adapters/RedisAdapter.js.map +1 -1
- package/dist/adapters/SQLiteAdapter.d.ts.map +1 -1
- package/dist/adapters/SQLiteAdapter.js +38 -6
- package/dist/adapters/SQLiteAdapter.js.map +1 -1
- package/dist/cjs/adapters/MemoryAdapter.d.ts.map +1 -1
- package/dist/cjs/adapters/MemoryAdapter.js +10 -1
- package/dist/cjs/adapters/MemoryAdapter.js.map +1 -1
- package/dist/cjs/adapters/MongoAdapter.d.ts.map +1 -1
- package/dist/cjs/adapters/MongoAdapter.js +33 -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 +15 -1
- package/dist/cjs/adapters/OpenSearchAdapter.js.map +1 -1
- package/dist/cjs/adapters/PostgreSQLAdapter.d.ts.map +1 -1
- package/dist/cjs/adapters/PostgreSQLAdapter.js +35 -5
- package/dist/cjs/adapters/PostgreSQLAdapter.js.map +1 -1
- package/dist/cjs/adapters/PrismaAdapter.d.ts.map +1 -1
- package/dist/cjs/adapters/PrismaAdapter.js +58 -15
- package/dist/cjs/adapters/PrismaAdapter.js.map +1 -1
- package/dist/cjs/adapters/RedisAdapter.d.ts.map +1 -1
- package/dist/cjs/adapters/RedisAdapter.js +10 -1
- package/dist/cjs/adapters/RedisAdapter.js.map +1 -1
- package/dist/cjs/adapters/SQLiteAdapter.d.ts.map +1 -1
- package/dist/cjs/adapters/SQLiteAdapter.js +38 -6
- package/dist/cjs/adapters/SQLiteAdapter.js.map +1 -1
- package/dist/cjs/core/Agent.d.ts +28 -18
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +58 -68
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/AutoChainExecutor.d.ts.map +1 -1
- package/dist/cjs/core/AutoChainExecutor.js +14 -20
- package/dist/cjs/core/AutoChainExecutor.js.map +1 -1
- package/dist/cjs/core/BranchEvaluator.d.ts +4 -3
- package/dist/cjs/core/BranchEvaluator.d.ts.map +1 -1
- package/dist/cjs/core/BranchEvaluator.js +18 -23
- package/dist/cjs/core/BranchEvaluator.js.map +1 -1
- package/dist/cjs/core/Flow.d.ts +2 -1
- package/dist/cjs/core/Flow.d.ts.map +1 -1
- package/dist/cjs/core/Flow.js +10 -3
- package/dist/cjs/core/Flow.js.map +1 -1
- package/dist/cjs/core/FlowRouter.d.ts +1 -0
- package/dist/cjs/core/FlowRouter.d.ts.map +1 -1
- package/dist/cjs/core/FlowRouter.js +28 -5
- package/dist/cjs/core/FlowRouter.js.map +1 -1
- package/dist/cjs/core/PersistenceManager.d.ts +3 -0
- package/dist/cjs/core/PersistenceManager.d.ts.map +1 -1
- package/dist/cjs/core/PersistenceManager.js +57 -5
- package/dist/cjs/core/PersistenceManager.js.map +1 -1
- package/dist/cjs/core/PromptComposer.d.ts.map +1 -1
- package/dist/cjs/core/PromptComposer.js +24 -10
- package/dist/cjs/core/PromptComposer.js.map +1 -1
- package/dist/cjs/core/ResponseGenerationError.d.ts +30 -0
- package/dist/cjs/core/ResponseGenerationError.d.ts.map +1 -0
- package/dist/cjs/core/ResponseGenerationError.js +37 -0
- package/dist/cjs/core/ResponseGenerationError.js.map +1 -0
- package/dist/cjs/core/ResponseModal.d.ts +43 -102
- package/dist/cjs/core/ResponseModal.d.ts.map +1 -1
- package/dist/cjs/core/ResponseModal.js +178 -1194
- package/dist/cjs/core/ResponseModal.js.map +1 -1
- package/dist/cjs/core/ResponsePipeline.d.ts +58 -152
- package/dist/cjs/core/ResponsePipeline.d.ts.map +1 -1
- package/dist/cjs/core/ResponsePipeline.js +408 -457
- package/dist/cjs/core/ResponsePipeline.js.map +1 -1
- package/dist/cjs/core/SessionFinalizer.d.ts +34 -0
- package/dist/cjs/core/SessionFinalizer.d.ts.map +1 -0
- package/dist/cjs/core/SessionFinalizer.js +61 -0
- package/dist/cjs/core/SessionFinalizer.js.map +1 -0
- package/dist/cjs/core/SessionManager.d.ts +1 -1
- package/dist/cjs/core/SessionManager.d.ts.map +1 -1
- package/dist/cjs/core/SessionManager.js +18 -7
- package/dist/cjs/core/SessionManager.js.map +1 -1
- package/dist/cjs/core/SignalCoordinator.d.ts +103 -0
- package/dist/cjs/core/SignalCoordinator.d.ts.map +1 -0
- package/dist/cjs/core/SignalCoordinator.js +207 -0
- package/dist/cjs/core/SignalCoordinator.js.map +1 -0
- package/dist/cjs/core/SignalEvaluator.d.ts +2 -2
- package/dist/cjs/core/SignalEvaluator.d.ts.map +1 -1
- package/dist/cjs/core/SignalEvaluator.js +11 -26
- package/dist/cjs/core/SignalEvaluator.js.map +1 -1
- package/dist/cjs/core/SignalProcessor.d.ts.map +1 -1
- package/dist/cjs/core/SignalProcessor.js +28 -11
- package/dist/cjs/core/SignalProcessor.js.map +1 -1
- package/dist/cjs/core/Step.d.ts +3 -1
- package/dist/cjs/core/Step.d.ts.map +1 -1
- package/dist/cjs/core/Step.js +10 -3
- package/dist/cjs/core/Step.js.map +1 -1
- package/dist/cjs/core/StepLifecycle.d.ts +33 -0
- package/dist/cjs/core/StepLifecycle.d.ts.map +1 -0
- package/dist/cjs/core/StepLifecycle.js +97 -0
- package/dist/cjs/core/StepLifecycle.js.map +1 -0
- package/dist/cjs/core/ToolLoopExecutor.d.ts +104 -0
- package/dist/cjs/core/ToolLoopExecutor.d.ts.map +1 -0
- package/dist/cjs/core/ToolLoopExecutor.js +391 -0
- package/dist/cjs/core/ToolLoopExecutor.js.map +1 -0
- package/dist/cjs/core/ToolManager.d.ts +1 -1
- package/dist/cjs/core/ToolManager.d.ts.map +1 -1
- package/dist/cjs/core/ToolManager.js.map +1 -1
- package/dist/cjs/index.d.ts +4 -5
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +7 -6
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.d.ts +2 -0
- package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.js +28 -56
- package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
- package/dist/cjs/providers/DeepSeekProvider.d.ts +25 -21
- package/dist/cjs/providers/DeepSeekProvider.d.ts.map +1 -1
- package/dist/cjs/providers/DeepSeekProvider.js +48 -407
- package/dist/cjs/providers/DeepSeekProvider.js.map +1 -1
- package/dist/cjs/providers/GeminiProvider.d.ts +2 -0
- package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/cjs/providers/GeminiProvider.js +27 -56
- package/dist/cjs/providers/GeminiProvider.js.map +1 -1
- package/dist/cjs/providers/OpenAICompatibleProvider.d.ts +129 -0
- package/dist/cjs/providers/OpenAICompatibleProvider.d.ts.map +1 -0
- package/dist/cjs/providers/OpenAICompatibleProvider.js +485 -0
- package/dist/cjs/providers/OpenAICompatibleProvider.js.map +1 -0
- package/dist/cjs/providers/OpenAIProvider.d.ts +9 -28
- package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.js +23 -417
- package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.d.ts +10 -27
- package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.js +28 -417
- package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
- package/dist/cjs/providers/errorClassification.d.ts +61 -0
- package/dist/cjs/providers/errorClassification.d.ts.map +1 -0
- package/dist/cjs/providers/errorClassification.js +123 -0
- package/dist/cjs/providers/errorClassification.js.map +1 -0
- package/dist/cjs/providers/index.d.ts +4 -0
- package/dist/cjs/providers/index.d.ts.map +1 -1
- package/dist/cjs/providers/index.js +7 -1
- package/dist/cjs/providers/index.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +2 -1
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/ai.d.ts +20 -0
- package/dist/cjs/types/ai.d.ts.map +1 -1
- package/dist/cjs/types/errors.d.ts +33 -0
- package/dist/cjs/types/errors.d.ts.map +1 -1
- package/dist/cjs/types/errors.js +37 -1
- package/dist/cjs/types/errors.js.map +1 -1
- package/dist/cjs/types/flow.d.ts +10 -5
- package/dist/cjs/types/flow.d.ts.map +1 -1
- package/dist/cjs/types/history.d.ts +1 -1
- package/dist/cjs/types/history.d.ts.map +1 -1
- package/dist/cjs/types/index.d.ts +5 -4
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/cjs/types/index.js +3 -1
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/cjs/types/persistence.d.ts +43 -2
- package/dist/cjs/types/persistence.d.ts.map +1 -1
- package/dist/cjs/types/session.d.ts +8 -0
- package/dist/cjs/types/session.d.ts.map +1 -1
- package/dist/cjs/types/signals.d.ts +18 -2
- package/dist/cjs/types/signals.d.ts.map +1 -1
- package/dist/cjs/types/template.d.ts +3 -1
- package/dist/cjs/types/template.d.ts.map +1 -1
- package/dist/cjs/types/tool.d.ts +4 -4
- package/dist/cjs/types/tool.d.ts.map +1 -1
- package/dist/cjs/utils/condition.d.ts +19 -0
- package/dist/cjs/utils/condition.d.ts.map +1 -1
- package/dist/cjs/utils/condition.js +86 -15
- package/dist/cjs/utils/condition.js.map +1 -1
- package/dist/cjs/utils/session.d.ts +1 -0
- package/dist/cjs/utils/session.d.ts.map +1 -1
- package/dist/cjs/utils/session.js +1 -0
- package/dist/cjs/utils/session.js.map +1 -1
- package/dist/core/Agent.d.ts +28 -18
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +58 -68
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/AutoChainExecutor.d.ts.map +1 -1
- package/dist/core/AutoChainExecutor.js +14 -20
- package/dist/core/AutoChainExecutor.js.map +1 -1
- package/dist/core/BranchEvaluator.d.ts +4 -3
- package/dist/core/BranchEvaluator.d.ts.map +1 -1
- package/dist/core/BranchEvaluator.js +18 -23
- package/dist/core/BranchEvaluator.js.map +1 -1
- package/dist/core/Flow.d.ts +2 -1
- package/dist/core/Flow.d.ts.map +1 -1
- package/dist/core/Flow.js +10 -3
- package/dist/core/Flow.js.map +1 -1
- package/dist/core/FlowRouter.d.ts +1 -0
- package/dist/core/FlowRouter.d.ts.map +1 -1
- package/dist/core/FlowRouter.js +28 -5
- package/dist/core/FlowRouter.js.map +1 -1
- package/dist/core/PersistenceManager.d.ts +3 -0
- package/dist/core/PersistenceManager.d.ts.map +1 -1
- package/dist/core/PersistenceManager.js +58 -6
- package/dist/core/PersistenceManager.js.map +1 -1
- package/dist/core/PromptComposer.d.ts.map +1 -1
- package/dist/core/PromptComposer.js +24 -10
- package/dist/core/PromptComposer.js.map +1 -1
- package/dist/core/ResponseGenerationError.d.ts +30 -0
- package/dist/core/ResponseGenerationError.d.ts.map +1 -0
- package/dist/core/ResponseGenerationError.js +33 -0
- package/dist/core/ResponseGenerationError.js.map +1 -0
- package/dist/core/ResponseModal.d.ts +43 -102
- package/dist/core/ResponseModal.d.ts.map +1 -1
- package/dist/core/ResponseModal.js +171 -1186
- package/dist/core/ResponseModal.js.map +1 -1
- package/dist/core/ResponsePipeline.d.ts +58 -152
- package/dist/core/ResponsePipeline.d.ts.map +1 -1
- package/dist/core/ResponsePipeline.js +409 -458
- package/dist/core/ResponsePipeline.js.map +1 -1
- package/dist/core/SessionFinalizer.d.ts +34 -0
- package/dist/core/SessionFinalizer.d.ts.map +1 -0
- package/dist/core/SessionFinalizer.js +57 -0
- package/dist/core/SessionFinalizer.js.map +1 -0
- package/dist/core/SessionManager.d.ts +1 -1
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +18 -7
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/SignalCoordinator.d.ts +103 -0
- package/dist/core/SignalCoordinator.d.ts.map +1 -0
- package/dist/core/SignalCoordinator.js +203 -0
- package/dist/core/SignalCoordinator.js.map +1 -0
- package/dist/core/SignalEvaluator.d.ts +2 -2
- package/dist/core/SignalEvaluator.d.ts.map +1 -1
- package/dist/core/SignalEvaluator.js +11 -26
- package/dist/core/SignalEvaluator.js.map +1 -1
- package/dist/core/SignalProcessor.d.ts.map +1 -1
- package/dist/core/SignalProcessor.js +28 -11
- package/dist/core/SignalProcessor.js.map +1 -1
- package/dist/core/Step.d.ts +3 -1
- package/dist/core/Step.d.ts.map +1 -1
- package/dist/core/Step.js +10 -3
- package/dist/core/Step.js.map +1 -1
- package/dist/core/StepLifecycle.d.ts +33 -0
- package/dist/core/StepLifecycle.d.ts.map +1 -0
- package/dist/core/StepLifecycle.js +93 -0
- package/dist/core/StepLifecycle.js.map +1 -0
- package/dist/core/ToolLoopExecutor.d.ts +104 -0
- package/dist/core/ToolLoopExecutor.d.ts.map +1 -0
- package/dist/core/ToolLoopExecutor.js +387 -0
- package/dist/core/ToolLoopExecutor.js.map +1 -0
- package/dist/core/ToolManager.d.ts +1 -1
- package/dist/core/ToolManager.d.ts.map +1 -1
- package/dist/core/ToolManager.js.map +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/providers/AnthropicProvider.d.ts +2 -0
- package/dist/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/providers/AnthropicProvider.js +22 -50
- package/dist/providers/AnthropicProvider.js.map +1 -1
- package/dist/providers/DeepSeekProvider.d.ts +25 -21
- package/dist/providers/DeepSeekProvider.d.ts.map +1 -1
- package/dist/providers/DeepSeekProvider.js +49 -408
- package/dist/providers/DeepSeekProvider.js.map +1 -1
- package/dist/providers/GeminiProvider.d.ts +2 -0
- package/dist/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/GeminiProvider.js +21 -50
- package/dist/providers/GeminiProvider.js.map +1 -1
- package/dist/providers/OpenAICompatibleProvider.d.ts +129 -0
- package/dist/providers/OpenAICompatibleProvider.d.ts.map +1 -0
- package/dist/providers/OpenAICompatibleProvider.js +481 -0
- package/dist/providers/OpenAICompatibleProvider.js.map +1 -0
- package/dist/providers/OpenAIProvider.d.ts +9 -28
- package/dist/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/providers/OpenAIProvider.js +23 -417
- package/dist/providers/OpenAIProvider.js.map +1 -1
- package/dist/providers/OpenRouterProvider.d.ts +10 -27
- package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/providers/OpenRouterProvider.js +28 -417
- package/dist/providers/OpenRouterProvider.js.map +1 -1
- package/dist/providers/errorClassification.d.ts +61 -0
- package/dist/providers/errorClassification.d.ts.map +1 -0
- package/dist/providers/errorClassification.js +116 -0
- package/dist/providers/errorClassification.js.map +1 -0
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +2 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/types/agent.d.ts +2 -1
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/ai.d.ts +20 -0
- package/dist/types/ai.d.ts.map +1 -1
- package/dist/types/errors.d.ts +33 -0
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/errors.js +34 -0
- package/dist/types/errors.js.map +1 -1
- package/dist/types/flow.d.ts +10 -5
- package/dist/types/flow.d.ts.map +1 -1
- package/dist/types/history.d.ts +1 -1
- package/dist/types/history.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/persistence.d.ts +43 -2
- package/dist/types/persistence.d.ts.map +1 -1
- package/dist/types/session.d.ts +8 -0
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/signals.d.ts +18 -2
- package/dist/types/signals.d.ts.map +1 -1
- package/dist/types/template.d.ts +3 -1
- package/dist/types/template.d.ts.map +1 -1
- package/dist/types/tool.d.ts +4 -4
- package/dist/types/tool.d.ts.map +1 -1
- package/dist/utils/condition.d.ts +19 -0
- package/dist/utils/condition.d.ts.map +1 -1
- package/dist/utils/condition.js +84 -15
- package/dist/utils/condition.js.map +1 -1
- package/dist/utils/session.d.ts +1 -0
- package/dist/utils/session.d.ts.map +1 -1
- package/dist/utils/session.js +1 -0
- package/dist/utils/session.js.map +1 -1
- package/docs/README.md +1 -1
- package/docs/guides/branching.md +1 -1
- package/docs/guides/compaction.md +12 -5
- package/docs/guides/conditions.md +15 -2
- package/docs/guides/error-handling.md +59 -9
- package/docs/guides/instructions.md +2 -2
- package/docs/guides/persistence.md +69 -2
- package/docs/guides/streaming.md +2 -0
- package/docs/migration/README.md +5 -1
- package/docs/migration/v2-3-to-v2-4.md +316 -0
- package/docs/reference/adapters.md +55 -7
- package/docs/reference/branches.md +2 -2
- package/docs/reference/create-agent.md +3 -3
- package/docs/reference/errors.md +66 -1
- package/docs/reference/flow.md +1 -1
- package/docs/reference/instruction.md +4 -4
- package/docs/reference/providers.md +100 -3
- package/docs/reference/signals.md +14 -3
- package/docs/reference/step.md +2 -2
- package/docs/reference/tool.md +3 -1
- package/docs/start/05-go-to-production.md +3 -0
- package/package.json +1 -1
- package/src/adapters/MemoryAdapter.ts +15 -1
- package/src/adapters/MongoAdapter.ts +41 -2
- package/src/adapters/OpenSearchAdapter.ts +24 -1
- package/src/adapters/PostgreSQLAdapter.ts +45 -5
- package/src/adapters/PrismaAdapter.ts +82 -16
- package/src/adapters/RedisAdapter.ts +19 -1
- package/src/adapters/SQLiteAdapter.ts +47 -5
- package/src/core/Agent.ts +70 -85
- package/src/core/AutoChainExecutor.ts +27 -57
- package/src/core/BranchEvaluator.ts +24 -30
- package/src/core/Flow.ts +10 -3
- package/src/core/FlowRouter.ts +36 -4
- package/src/core/PersistenceManager.ts +79 -10
- package/src/core/PromptComposer.ts +25 -12
- package/src/core/ResponseGenerationError.ts +56 -0
- package/src/core/ResponseModal.ts +233 -1482
- package/src/core/ResponsePipeline.ts +492 -662
- package/src/core/SessionFinalizer.ts +78 -0
- package/src/core/SessionManager.ts +21 -9
- package/src/core/SignalCoordinator.ts +263 -0
- package/src/core/SignalEvaluator.ts +12 -29
- package/src/core/SignalProcessor.ts +34 -11
- package/src/core/Step.ts +11 -3
- package/src/core/StepLifecycle.ts +139 -0
- package/src/core/ToolLoopExecutor.ts +492 -0
- package/src/core/ToolManager.ts +2 -1
- package/src/index.ts +7 -5
- package/src/providers/AnthropicProvider.ts +30 -72
- package/src/providers/DeepSeekProvider.ts +74 -586
- package/src/providers/GeminiProvider.ts +29 -70
- package/src/providers/OpenAICompatibleProvider.ts +738 -0
- package/src/providers/OpenAIProvider.ts +29 -602
- package/src/providers/OpenRouterProvider.ts +38 -596
- package/src/providers/errorClassification.ts +172 -0
- package/src/providers/index.ts +13 -0
- package/src/types/agent.ts +2 -1
- package/src/types/ai.ts +22 -0
- package/src/types/errors.ts +57 -0
- package/src/types/flow.ts +10 -5
- package/src/types/history.ts +1 -2
- package/src/types/index.ts +5 -1
- package/src/types/persistence.ts +50 -2
- package/src/types/session.ts +9 -0
- package/src/types/signals.ts +20 -2
- package/src/types/template.ts +3 -1
- package/src/types/tool.ts +10 -10
- package/src/utils/condition.ts +115 -18
- package/src/utils/session.ts +2 -0
package/docs/reference/errors.md
CHANGED
|
@@ -31,6 +31,17 @@ class ToolExecutionError extends Error {
|
|
|
31
31
|
cause?: Error;
|
|
32
32
|
}
|
|
33
33
|
class NotImplementedError extends Error { /* name = "NotImplementedError" */ }
|
|
34
|
+
class ProviderError extends Error {
|
|
35
|
+
code: ProviderErrorCode; // 'rate_limited' | 'overloaded' | 'auth' | 'invalid_request'
|
|
36
|
+
// | 'schema_rejected' | 'timeout' | 'network' | 'unknown'
|
|
37
|
+
provider: string; // e.g. "openai"
|
|
38
|
+
cause?: unknown; // original SDK/HTTP error
|
|
39
|
+
}
|
|
40
|
+
class SessionConflictError extends Error {
|
|
41
|
+
sessionId: string;
|
|
42
|
+
expectedVersion: number;
|
|
43
|
+
actualVersion: number | undefined;
|
|
44
|
+
}
|
|
34
45
|
|
|
35
46
|
// Internal — match by `error.name` (not exported from the package barrel)
|
|
36
47
|
class DataValidationError extends Error { errors: ValidationError[] }
|
|
@@ -53,6 +64,8 @@ class ResponseGenerationError extends Error {
|
|
|
53
64
|
| `ToolExecutionError` | A handler throws, all retries fail, or `validateInput` cannot correct invalid args. | `toolId`, `executionContext`, `cause` | Surface a user-friendly message; optionally `agent.dispatch({ goTo: '<recovery-flow>' })`. |
|
|
54
65
|
| `DataValidationError` | `agent.respond(...)` collects values that violate the declared `schema`. | `errors: ValidationError[]` | Re-prompt for the offending fields, then retry. |
|
|
55
66
|
| `ResponseGenerationError` | The provider call fails or the response cannot be parsed. | `details.phase`, `details.originalError` | Retry with backoff, fall back to a different provider, or surface a soft failure to the user. |
|
|
67
|
+
| `ProviderError` | A provider call fails terminally — retries and `backupModels` exhausted. Normalized across all vendors. | `code`, `provider`, `cause` (original SDK error) | Match on `code`: backoff for `rate_limited`/`overloaded`, fix credentials for `auth`, fail fast otherwise. Inside a turn it surfaces on `ResponseGenerationError.details.originalError`. |
|
|
68
|
+
| `SessionConflictError` | A session save carries a stale `version` — another writer persisted the session after this one loaded it (concurrent `respond()` calls, parallel webhooks, two tabs). | `sessionId`, `expectedVersion`, `actualVersion` | Reload the session and retry the operation, or surface the conflict. |
|
|
56
69
|
| `NotImplementedError` | A reserved option is set to a value this version does not support (e.g. `routerMode: 'embedding'` in v2.0). | `message` | Use a supported value. |
|
|
57
70
|
|
|
58
71
|
## Examples
|
|
@@ -87,7 +100,57 @@ try {
|
|
|
87
100
|
}
|
|
88
101
|
```
|
|
89
102
|
|
|
90
|
-
### 2.
|
|
103
|
+
### 2. Matching provider failures by normalized code
|
|
104
|
+
|
|
105
|
+
Terminal provider failures throw `ProviderError` with a vendor-agnostic `code`. Inside a turn, the agent wraps it in `ResponseGenerationError` — unwrap via `details.originalError`.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { ProviderError } from "@falai/agent";
|
|
109
|
+
|
|
110
|
+
function asProviderError(err: unknown): ProviderError | undefined {
|
|
111
|
+
if (err instanceof ProviderError) return err;
|
|
112
|
+
if (err instanceof Error && err.name === "ResponseGenerationError") {
|
|
113
|
+
const original = (err as { details?: { originalError?: unknown } }).details?.originalError;
|
|
114
|
+
if (original instanceof ProviderError) return original;
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const providerError = asProviderError(err);
|
|
120
|
+
if (providerError) {
|
|
121
|
+
switch (providerError.code) {
|
|
122
|
+
case "rate_limited":
|
|
123
|
+
case "overloaded":
|
|
124
|
+
return retryWithBackoff(); // transient — wait and retry
|
|
125
|
+
case "auth":
|
|
126
|
+
throw providerError; // config bug — crash loudly
|
|
127
|
+
default:
|
|
128
|
+
log.error({ cause: providerError.cause }, providerError.message);
|
|
129
|
+
return "I'm having trouble reaching the model. Please retry.";
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 3. Recovering from a session conflict
|
|
135
|
+
|
|
136
|
+
`SessionConflictError` means another writer persisted the session between your load and your save. Reload, then retry.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { SessionConflictError } from "@falai/agent";
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
await agent.respond({ history, session });
|
|
143
|
+
} catch (err) {
|
|
144
|
+
const original = (err as { details?: { originalError?: unknown } }).details?.originalError;
|
|
145
|
+
if (err instanceof SessionConflictError || original instanceof SessionConflictError) {
|
|
146
|
+
const fresh = await agent.session.getOrCreate(sessionId);
|
|
147
|
+
return agent.respond({ history, session: fresh });
|
|
148
|
+
}
|
|
149
|
+
throw err;
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 4. The format contract in practice
|
|
91
154
|
|
|
92
155
|
Every thrown message is parseable. The leading `[<ErrorClass>]` token mirrors the class name, the colon separates `<what>` from `<why>`, and the trailing sentence is `<how to fix>`.
|
|
93
156
|
|
|
@@ -120,3 +183,5 @@ Tool input validation, permission denials, and missing-tool warnings are reporte
|
|
|
120
183
|
- [createAgent](./create-agent.md) — construction-time errors thrown from `new Agent(...)`.
|
|
121
184
|
- [Tool](./tool.md) — handler return shape and the `ToolExecutionError` triggers.
|
|
122
185
|
- [Directive](./directive.md) — the validation rules that surface as `FlowConfigurationError`.
|
|
186
|
+
- [Providers](./providers.md) — the retry/backup pipeline that ends in `ProviderError`.
|
|
187
|
+
- [Persistence adapters](./adapters.md) — the optimistic locking that throws `SessionConflictError`.
|
package/docs/reference/flow.md
CHANGED
|
@@ -77,7 +77,7 @@ class Flow<TContext = unknown, TData = unknown> {
|
|
|
77
77
|
| `id` | `string` | no | derived from `title` | Stable identifier. Auto-generated deterministically from the title when omitted. |
|
|
78
78
|
| `title` | `string` | yes | — | Human-readable name. Shown to the router and used as the default flow id. |
|
|
79
79
|
| `description` | `string` | no | — | One-line summary surfaced to the router prompt. |
|
|
80
|
-
| `when` | `string \| string[]` | no | — | AI-evaluated activation condition(s). Strings only — functions belong on `if`.
|
|
80
|
+
| `when` | `string \| string[]` | no | — | AI-evaluated activation condition(s). Strings only — functions belong on `if`. Non-`!` strings are OR alternatives; `!` strings are OR exclusions where any match inhibits activation. |
|
|
81
81
|
| `if` | `(ctx) => boolean \| Promise<boolean>` or array | no | — | Code-evaluated activation condition(s). Free to evaluate. When both are set, `if` runs first; `when` only evaluates if `if` passes. |
|
|
82
82
|
| `instructions` | `Instruction<TContext, TData>[]` | no | `[]` | Flow-scoped instructions. Apply only while this flow is active. See [Instruction](./instruction.md). |
|
|
83
83
|
| `tools` | `(string \| Tool)[]` | no | `[]` | Tool ids (resolved via the agent's tool registry) or inline `Tool` objects. Available only while this flow is active. |
|
|
@@ -19,7 +19,7 @@ The set of instructions actually rendered into a given turn's prompt is reported
|
|
|
19
19
|
interface Instruction<TContext = unknown, TData = unknown> {
|
|
20
20
|
id?: string;
|
|
21
21
|
kind?: 'must' | 'never' | 'should'; // default: 'should'
|
|
22
|
-
when?: ConditionWhen; // AI
|
|
22
|
+
when?: ConditionWhen; // AI strings: positives OR, ! exclusions inhibit
|
|
23
23
|
if?: ConditionIf<TContext, TData>; // code-evaluated function(s), AND semantics
|
|
24
24
|
prompt: Template<TContext, TData>;
|
|
25
25
|
enabled?: boolean; // default: true
|
|
@@ -48,7 +48,7 @@ interface AppliedInstruction {
|
|
|
48
48
|
|-------|------|----------|---------|-------|
|
|
49
49
|
| `prompt` | `Template<TContext, TData>` | yes | — | Behavioral text rendered into the prompt under the `## Instructions` section. |
|
|
50
50
|
| `kind` | `'must' \| 'never' \| 'should'` | no | `'should'` | Severity. `'must'` = absolute do, `'never'` = absolute don't, `'should'` = conditional nudge. |
|
|
51
|
-
| `when` | `ConditionWhen` | no | — | AI-evaluated activation string
|
|
51
|
+
| `when` | `ConditionWhen` | no | — | AI-evaluated activation string or array. Non-`!` entries are OR alternatives. `!` entries are OR exclusions; any matching exclusion inhibits the instruction. Functions are not allowed here; use `if`. |
|
|
52
52
|
| `if` | `ConditionIf<TContext, TData>` | no | — | Code-evaluated activation function (or array). Free to evaluate. When both `when` and `if` are set, `if` runs first; `when` is only evaluated if `if` passes. |
|
|
53
53
|
| `id` | `string` | no | auto | Stable identifier used in `AppliedInstruction.id`. Auto-generated when omitted. |
|
|
54
54
|
| `enabled` | `boolean` | no | `true` | Set `false` to skip the instruction without removing it from configuration. |
|
|
@@ -74,10 +74,10 @@ The same `Instruction` shape attaches at three positions:
|
|
|
74
74
|
At prompt-build time the composer renders each eligible instruction as a single bullet:
|
|
75
75
|
|
|
76
76
|
```
|
|
77
|
-
- [<kind>] [<scope-caption>] <prompt> (apply only when: <when-clause> OR <when-clause>)
|
|
77
|
+
- [<kind>] [<scope-caption>] <prompt> (apply only when: <when-clause> OR <when-clause>; do not apply when: <exclusion-clause>)
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
The parenthesized condition is omitted when `when` is not set. Code-evaluated `if` predicates run first; a failing predicate removes the entire bullet before the prompt reaches the model.
|
|
80
|
+
The parenthesized condition is omitted when `when` is not set. If `when` contains only `!` exclusions, the suffix uses only `do not apply when: ...`. Code-evaluated `if` predicates run first; a failing predicate removes the entire bullet before the prompt reaches the model.
|
|
81
81
|
|
|
82
82
|
Scope captions are fixed by where the instruction was declared:
|
|
83
83
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Providers"
|
|
3
|
-
description: "Strategy classes that connect an Agent to Gemini, OpenAI, Anthropic, or
|
|
3
|
+
description: "Strategy classes that connect an Agent to Gemini, OpenAI, Anthropic, OpenRouter, or DeepSeek — plus the base class for building your own."
|
|
4
4
|
type: reference
|
|
5
5
|
order: 10
|
|
6
6
|
---
|
|
@@ -21,6 +21,32 @@ Providers are the strategy plug between an `Agent` and a model vendor. Every pro
|
|
|
21
21
|
| OpenRouter | `OpenRouterProvider` | `OpenRouterProviderOptions` | `openai` (compat) |
|
|
22
22
|
| DeepSeek | `DeepSeekProvider` | `DeepSeekProviderOptions` | `openai` (compat) |
|
|
23
23
|
|
|
24
|
+
## Capabilities
|
|
25
|
+
|
|
26
|
+
Every provider declares a required `capabilities: ProviderCapabilities` field — five static flags the engine reads to decide how to drive the vendor (e.g., whether structured output is schema-enforced or prompt-instructed). Custom `AiProvider` implementations **must** declare it.
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
interface ProviderCapabilities {
|
|
30
|
+
supportsTools: boolean; // tool/function calling
|
|
31
|
+
supportsNativeJsonSchema: boolean; // native JSON-schema-enforced output (vs. prompt-based JSON instruction)
|
|
32
|
+
supportsStreaming: boolean; // streaming responses
|
|
33
|
+
supportsStreamingToolCalls: boolean; // tool calls surfaced during streaming
|
|
34
|
+
supportsPromptCaching: boolean; // prompt caching
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The five built-ins:
|
|
39
|
+
|
|
40
|
+
| Capability | Gemini | OpenAI | Anthropic | OpenRouter | DeepSeek |
|
|
41
|
+
|------------|--------|--------|-----------|------------|----------|
|
|
42
|
+
| `supportsTools` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
43
|
+
| `supportsNativeJsonSchema` | ✅ | ✅ | ❌ | ✅ | ✅ |
|
|
44
|
+
| `supportsStreaming` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
45
|
+
| `supportsStreamingToolCalls` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
46
|
+
| `supportsPromptCaching` | ❌ | ❌ | ✅ | ❌ | ❌ |
|
|
47
|
+
|
|
48
|
+
The two asymmetries: Anthropic reports `supportsNativeJsonSchema: false` because its JSON output is enforced via a prompt instruction, not a native schema mode — and it is the only built-in that reports `supportsPromptCaching: true`.
|
|
49
|
+
|
|
24
50
|
## Use with createAgent
|
|
25
51
|
|
|
26
52
|
`createAgent({ provider })` accepts any class that implements `AiProvider`. Swap providers by changing the constructor; nothing else in your agent has to move.
|
|
@@ -250,6 +276,47 @@ const deepseek = new DeepSeekProvider({
|
|
|
250
276
|
});
|
|
251
277
|
```
|
|
252
278
|
|
|
279
|
+
## Building a custom OpenAI-compatible provider
|
|
280
|
+
|
|
281
|
+
Many vendors (Groq, Together, Fireworks, …) expose OpenAI-compatible chat-completions APIs. Instead of implementing `AiProvider` from scratch, subclass the exported `OpenAICompatibleProvider` base class — it handles message/history building, tool-call parsing, streaming chunks, backup-model fallback, retries, schema passthrough, and normalized `ProviderError` wrapping. `OpenAIProvider`, `OpenRouterProvider`, and `DeepSeekProvider` are themselves thin subclasses.
|
|
282
|
+
|
|
283
|
+
A minimal subclass supplies the configured client, naming, and capabilities:
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import OpenAI from "openai";
|
|
287
|
+
import {
|
|
288
|
+
OpenAICompatibleProvider,
|
|
289
|
+
type ProviderCapabilities,
|
|
290
|
+
} from "@falai/agent";
|
|
291
|
+
|
|
292
|
+
export class GroqProvider extends OpenAICompatibleProvider {
|
|
293
|
+
public readonly name = "groq";
|
|
294
|
+
public readonly capabilities: ProviderCapabilities = {
|
|
295
|
+
supportsTools: true,
|
|
296
|
+
supportsNativeJsonSchema: true,
|
|
297
|
+
supportsStreaming: true,
|
|
298
|
+
supportsStreamingToolCalls: true,
|
|
299
|
+
supportsPromptCaching: false,
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
protected readonly logLabel = "GROQ"; // tag in log lines
|
|
303
|
+
protected readonly displayName = "Groq"; // name in retry/error messages
|
|
304
|
+
|
|
305
|
+
constructor(options: { apiKey: string; model: string; backupModels?: string[] }) {
|
|
306
|
+
super({
|
|
307
|
+
client: new OpenAI({
|
|
308
|
+
apiKey: options.apiKey,
|
|
309
|
+
baseURL: "https://api.groq.com/openai/v1",
|
|
310
|
+
}),
|
|
311
|
+
model: options.model,
|
|
312
|
+
backupModels: options.backupModels,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
That is a complete, working provider. For genuinely vendor-specific behavior, override the protected hooks — `DeepSeekProvider` is the reference pattern: it overrides `executeStructuredGenerate` (no `responses.parse` API), `structuredResponseFormat` (native `json_schema` enforcement), `configureStreamParams` (usage in stream chunks), and `onStreamDelta` (reasoning content on the delta).
|
|
319
|
+
|
|
253
320
|
## Errors
|
|
254
321
|
|
|
255
322
|
All five providers share the same construction-time guards and runtime failure modes.
|
|
@@ -259,15 +326,45 @@ All five providers share the same construction-time guards and runtime failure m
|
|
|
259
326
|
| `apiKey` is empty or missing | `Error("<vendor> API key is required")` | Thrown from the constructor. |
|
|
260
327
|
| `model` is empty or missing | `Error("Model is required. ...")` | Thrown from the constructor. |
|
|
261
328
|
| Vendor returns no text and no tool calls | `Error("No response from <vendor>")` | Surfaces as a `ResponseGenerationError` once it bubbles through the agent. |
|
|
262
|
-
| Primary and every backup model fail |
|
|
329
|
+
| Primary and every backup model fail | `ProviderError` with a normalized `code` | After exhausting retries and `backupModels`. The agent wraps it in `ResponseGenerationError`. |
|
|
263
330
|
| Anthropic streaming with `system: undefined` | Vendor 400 | Set `config.system` or rely on history-derived system messages. |
|
|
264
331
|
|
|
265
332
|
The retry/backup logic only kicks in for transient errors: HTTP 429 / 500 / 503 (and 529 for Anthropic), `overloaded`-style codes, or messages containing `overloaded`, `unavailable`, `internal error`, or (OpenRouter only) `capacity`. Other errors fail fast.
|
|
266
333
|
|
|
334
|
+
### `ProviderError`
|
|
335
|
+
|
|
336
|
+
Terminal failures — after retries and backup models are exhausted — throw the exported `ProviderError` with a normalized `code`, so callers handle failures uniformly regardless of which vendor is configured. The original SDK/HTTP error is preserved as `cause`.
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
import { ProviderError } from "@falai/agent";
|
|
340
|
+
|
|
341
|
+
type ProviderErrorCode =
|
|
342
|
+
| 'rate_limited' // 429-style throttling
|
|
343
|
+
| 'overloaded' // capacity / 503 / 529
|
|
344
|
+
| 'auth' // invalid or missing credentials
|
|
345
|
+
| 'invalid_request' // vendor rejected the request shape
|
|
346
|
+
| 'schema_rejected' // structured-output schema rejected
|
|
347
|
+
| 'timeout' // per-attempt timeout exhausted
|
|
348
|
+
| 'network' // connection-level failure
|
|
349
|
+
| 'unknown'; // anything unclassified
|
|
350
|
+
|
|
351
|
+
try {
|
|
352
|
+
await provider.generateMessage(input);
|
|
353
|
+
} catch (err) {
|
|
354
|
+
if (err instanceof ProviderError) {
|
|
355
|
+
console.error(err.provider, err.code); // e.g. "openai" "rate_limited"
|
|
356
|
+
console.error(err.cause); // original SDK error
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
When the failure bubbles through `agent.respond(...)`, it is wrapped in `ResponseGenerationError` like every other turn failure — the `ProviderError` is then on `details.originalError`. See [Errors](./errors.md).
|
|
362
|
+
|
|
267
363
|
## Related
|
|
268
364
|
|
|
269
365
|
- [Install](../start/01-install.md) — provider signup and env keys
|
|
270
366
|
- [Architecture](../concepts/architecture.md) — where the provider sits in the engine
|
|
271
367
|
- [createAgent](./create-agent.md) — the `provider` field
|
|
272
368
|
- [Persistence adapters](./adapters.md) — the other strategy plug
|
|
273
|
-
- [Errors](./errors.md) — `ResponseGenerationError
|
|
369
|
+
- [Errors](./errors.md) — `ProviderError`, `ResponseGenerationError`, and friends
|
|
370
|
+
- [v2.3 → v2.4 migration](../migration/v2-3-to-v2-4.md) — required `capabilities` and the `ProviderError` change
|
|
@@ -88,13 +88,18 @@ interface SignalDirective<TContext = unknown, TData = unknown>
|
|
|
88
88
|
replyWith?: string | ((ctx: SignalContext<TContext, TData>) => string);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
// As reported on the response surface: replyWith already resolved onto reply
|
|
92
|
+
type ResolvedSignalDirective<TContext = unknown, TData = unknown> =
|
|
93
|
+
Directive<TContext, TData> & { stopOtherSignals?: boolean };
|
|
94
|
+
|
|
91
95
|
interface SignalFiring<TContext = unknown, TData = unknown> {
|
|
92
96
|
id: string;
|
|
93
97
|
phase: 'pre' | 'post';
|
|
94
98
|
reason?: string;
|
|
95
99
|
extracted?: unknown;
|
|
96
|
-
directive?:
|
|
100
|
+
directive?: ResolvedSignalDirective<TContext, TData>;
|
|
97
101
|
handlerError?: string;
|
|
102
|
+
extractionError?: string;
|
|
98
103
|
durationMs?: number;
|
|
99
104
|
}
|
|
100
105
|
|
|
@@ -130,7 +135,7 @@ interface AgentOptions<TContext, TData> {
|
|
|
130
135
|
| `id` | `string` | no | auto-generated | Stable identifier within a session. Used for `SignalsState.triggers` keying and on `SignalFiring`. Must be unique across the agent's signals. |
|
|
131
136
|
| `title` | `string` | no | — | Display title shown in logs and traces. |
|
|
132
137
|
| `description` | `string` | no | — | Free-text description; rendered into the classifier prompt. |
|
|
133
|
-
| `when` | `string \| string[]` | no | — | AI-evaluated condition(s). Non-prefixed entries render under "TRIGGER WHEN" (OR semantics — any match can trigger). Entries prefixed with `!` are exclusion conditions rendered under "DO NOT TRIGGER WHEN" (OR semantics — any match inhibits firing). |
|
|
138
|
+
| `when` | `string \| string[]` | no | — | AI-evaluated condition(s), using the same `ConditionWhen` include/exclude syntax as flows, steps, branches, and instructions. Non-prefixed entries render under "TRIGGER WHEN" (OR semantics — any match can trigger). Entries prefixed with `!` are exclusion conditions rendered under "DO NOT TRIGGER WHEN" (OR semantics — any match inhibits firing). |
|
|
134
139
|
| `if` | `SignalPredicate \| SignalPredicate[]` | no | — | Code predicate(s). AND semantics. Free to evaluate. Runs before `when`; if any returns `false`, `when` is skipped (no token cost). |
|
|
135
140
|
| `extract` | `SignalSchema<TExtract>` | no | — | When set, the signal operates in extraction mode. JSON Schema object describing the per-signal `extracted` field merged into the classifier response. The `TExtract` generic carries the resulting type onto `ctx.extracted`. |
|
|
136
141
|
| `phase` | `'pre' \| 'post' \| 'both'` | yes | — | When the signal evaluates. `'pre'` runs in parallel with routing. `'post'` runs after the LLM call, before persistence. `'both'` evaluates in both phases. |
|
|
@@ -185,6 +190,10 @@ Extends [`Directive`](./directive.md). All position fields (`goTo`, `goToStep`,
|
|
|
185
190
|
|
|
186
191
|
**Post-phase drop rules.** When a signal runs in the post-phase, `appendPrompt`, `injectTools`, and `halt` are dropped with a debug warning — they have no meaning after the LLM call has already completed. Position directives in the post-phase set `session.pendingDirective` for the *next* turn (no mid-turn re-entry).
|
|
187
192
|
|
|
193
|
+
### `ResolvedSignalDirective`
|
|
194
|
+
|
|
195
|
+
The directive shape as reported on the response surface (`SignalFiring.directive`). The signal processor resolves `replyWith` onto `reply` and strips it before firings reach `AgentResponse` — so a `ResolvedSignalDirective` is a plain [`Directive`](./directive.md) plus `stopOtherSignals`, with the reply text (if any) always on `reply`. Exported from the package barrel.
|
|
196
|
+
|
|
188
197
|
### `SignalFiring`
|
|
189
198
|
|
|
190
199
|
One entry per signal that fired this turn. Populated in fire order across both phases on `AgentResponse.triggeredSignals` (and on the final chunk of `AgentResponseStreamChunk`). Mirrors the observability framing of `executedSteps` and `appliedInstructions`.
|
|
@@ -195,8 +204,9 @@ One entry per signal that fired this turn. Populated in fire order across both p
|
|
|
195
204
|
| `phase` | `'pre' \| 'post'` | Phase the signal fired in. |
|
|
196
205
|
| `reason` | `string \| undefined` | AI rationale, `'code-only'`, or `'unconditional'`. |
|
|
197
206
|
| `extracted` | `unknown` | Extracted payload when in extraction mode. |
|
|
198
|
-
| `directive` | `
|
|
207
|
+
| `directive` | `ResolvedSignalDirective \| undefined` | The directive returned (or dispatched) by the handler, with `replyWith` already resolved onto `reply`. |
|
|
199
208
|
| `handlerError` | `string \| undefined` | Error message if the handler threw. The turn continues — handler errors never break a turn. |
|
|
209
|
+
| `extractionError` | `string \| undefined` | Set when an extraction-mode signal matched but the classifier returned no `extracted` payload (model omitted the field or returned `null`). The handler still runs with `extracted: undefined`; this makes the miss observable instead of silent. Independent of `handlerError`. |
|
|
200
210
|
| `durationMs` | `number \| undefined` | Wall-clock duration of the handler invocation. |
|
|
201
211
|
|
|
202
212
|
### `SignalsState` and `SignalTriggerState`
|
|
@@ -345,6 +355,7 @@ Soft failures handled in-band (no thrown error, turn continues):
|
|
|
345
355
|
|
|
346
356
|
- **Handler throws** — recorded as `firings[i].handlerError`; iteration continues with the next signal.
|
|
347
357
|
- **Classifier call fails** — all LLM-conditioned signals in that batch are treated as non-match; code-only and unconditional signals continue normally.
|
|
358
|
+
- **Extraction-mode signal matched but no payload returned** — recorded as `firings[i].extractionError` with a WARN log; the handler still runs with `extracted: undefined`. Usually means the `extract` schema used a keyword the provider ignores (e.g. `nullable: true` instead of `type: ['string', 'null']`), so the model omitted the field.
|
|
348
359
|
- **Post-phase emits pre-LLM-only fields** (`halt` / `appendPrompt` / `injectTools`) — dropped with debug warning.
|
|
349
360
|
- **`behavior: 'cooldown'` with no `cooldownMs`** — debug warning at construction; runtime treats as `'always'`.
|
|
350
361
|
|
package/docs/reference/step.md
CHANGED
|
@@ -96,7 +96,7 @@ interface StepLifecycleHooks<TContext = unknown, TData = unknown> {
|
|
|
96
96
|
| `auto` | `boolean` | no | `false` | When `true`, the step runs without an LLM call — only `onEnter`, `prepare`, and `branches` execute. Cannot coexist with `prompt`, `collect`, `tools`, or `finalize`. Counts against `maxAutoStepsPerTurn`. |
|
|
97
97
|
| `collect` | `(keyof TData)[]` | no | `[]` | Schema field keys this step is responsible for extracting from the user message. Every key must exist in the agent's `schema`. The engine skips the step automatically when every listed key is already present in `session.data` (pre-extraction). |
|
|
98
98
|
| `requires` | `(keyof TData)[]` | no | `[]` | Prerequisite field keys. The engine refuses to enter the step until every key is present in `session.data`. When fields covered by `requires` are read inside `branches[].if` predicates, they are guaranteed to be defined. |
|
|
99
|
-
| `when` | `string \| string[]` | no | — | AI-evaluated activation strings
|
|
99
|
+
| `when` | `string \| string[]` | no | — | AI-evaluated activation strings. Non-`!` strings are OR alternatives; `!` strings are OR exclusions where any match inhibits activation. Evaluated by the LLM at routing time. Functions are not allowed here — the constructor throws `FlowConfigurationError` if a function is found. |
|
|
100
100
|
| `if` | `(ctx) => boolean \| Promise<boolean>` or array | no | — | Code-evaluated activation predicates (AND semantics). Evaluated locally — no LLM cost. When both `when` and `if` are set, `if` runs first; `when` is only evaluated if every `if` predicate passes. |
|
|
101
101
|
| `skip` | `(ctx) => boolean \| Promise<boolean>` or array | no | — | Code-evaluated skip predicates (OR semantics). When any predicate returns `true`, the step is bypassed. Only code predicates — no AI strings. |
|
|
102
102
|
| `tools` | `(string \| Tool<TContext, TData>)[]` | no | `[]` | Tools available during this step. Strings are resolved against the agent's tool registry; objects are inline tools. Stacked on top of agent and flow scopes. |
|
|
@@ -130,7 +130,7 @@ or the full `Directive` surface (`appendPrompt`,
|
|
|
130
130
|
|
|
131
131
|
For one step, the engine walks this sequence per turn:
|
|
132
132
|
|
|
133
|
-
1. Evaluate `if` (code, AND) and `when` (AI,
|
|
133
|
+
1. Evaluate `if` (code, AND) and `when` (AI: positive OR, `!` exclusions inhibit) — fails skip the step entirely.
|
|
134
134
|
2. Evaluate `skip` (code, OR) — true means bypass and fall through.
|
|
135
135
|
3. Check `requires` — refuse entry if any required field is missing.
|
|
136
136
|
4. Run `onEnter`, then `prepare` / `hooks.prepare`. May emit a `Directive` (pre-LLM fields honored).
|
package/docs/reference/tool.md
CHANGED
|
@@ -13,10 +13,12 @@ A `Tool` is a function the agent can invoke during a turn. v2 unifies tools into
|
|
|
13
13
|
|
|
14
14
|
`Tool.id` is the sole identifier.
|
|
15
15
|
|
|
16
|
+
Since v2.4 the generic defaults are `unknown` (previously `any`) on `Tool`, `ToolContext`, `ToolResult`, and `ToolHandler`. Pass explicit type parameters — or let inference flow from `createAgent`'s `schema` — to get typed `ctx.data` and `ctx.context`; untyped tool code that relied on implicit `any` needs explicit generics or a type guard.
|
|
17
|
+
|
|
16
18
|
## Signature
|
|
17
19
|
|
|
18
20
|
```typescript
|
|
19
|
-
interface Tool<TContext =
|
|
21
|
+
interface Tool<TContext = unknown, TData = unknown, TResult = unknown> {
|
|
20
22
|
// Identity
|
|
21
23
|
id: string;
|
|
22
24
|
description?: string;
|
|
@@ -54,6 +54,7 @@ model AgentSession {
|
|
|
54
54
|
collectedData Json?
|
|
55
55
|
pendingDirective Json?
|
|
56
56
|
signals Json?
|
|
57
|
+
version Int?
|
|
57
58
|
messageCount Int @default(0)
|
|
58
59
|
lastMessageAt DateTime?
|
|
59
60
|
completedAt DateTime?
|
|
@@ -115,6 +116,8 @@ const response = await agent.respond({
|
|
|
115
116
|
|
|
116
117
|
Unknown ids start fresh against that id; there is no "not found" error path.
|
|
117
118
|
|
|
119
|
+
The `version Int?` column enables optimistic locking: when two writers race on one session (parallel webhooks, a double-send), the stale save throws `SessionConflictError` instead of silently overwriting — reload the session and retry. See [Concurrent writers](../guides/persistence.md#concurrent-writers-optimistic-locking).
|
|
120
|
+
|
|
118
121
|
For the full schema migration story (renaming `pending_transition` to `pendingDirective`, adding the `signals` column) see [persistence adapters reference](../reference/adapters.md#prismaadapter).
|
|
119
122
|
|
|
120
123
|
## 2. Stream responses
|
package/package.json
CHANGED
|
@@ -11,8 +11,10 @@ import type {
|
|
|
11
11
|
SessionData,
|
|
12
12
|
SessionRepository,
|
|
13
13
|
SessionStatus,
|
|
14
|
+
SessionUpdateOptions,
|
|
14
15
|
CreateSessionData,
|
|
15
16
|
} from "../types";
|
|
17
|
+
import { SessionConflictError } from "../types/errors";
|
|
16
18
|
import { cloneDeep } from "../utils/clone";
|
|
17
19
|
import { createSessionId } from "../utils";
|
|
18
20
|
|
|
@@ -92,6 +94,7 @@ class MemorySessionRepository<TData = Record<string, unknown>>
|
|
|
92
94
|
id,
|
|
93
95
|
status: data.status || "active",
|
|
94
96
|
messageCount: data.messageCount || 0,
|
|
97
|
+
version: data.version ?? 1,
|
|
95
98
|
createdAt: now,
|
|
96
99
|
updatedAt: now,
|
|
97
100
|
};
|
|
@@ -126,14 +129,25 @@ class MemorySessionRepository<TData = Record<string, unknown>>
|
|
|
126
129
|
|
|
127
130
|
async update(
|
|
128
131
|
id: string,
|
|
129
|
-
data: Partial<Omit<SessionData<TData>, "id" | "createdAt"
|
|
132
|
+
data: Partial<Omit<SessionData<TData>, "id" | "createdAt">>,
|
|
133
|
+
options?: SessionUpdateOptions
|
|
130
134
|
): Promise<SessionData<TData> | null> {
|
|
131
135
|
const existing = this.sessions.get(id);
|
|
132
136
|
if (!existing) return null;
|
|
133
137
|
|
|
138
|
+
// Compare-and-swap: rows without a stored version (pre-2.4) are accepted
|
|
139
|
+
if (
|
|
140
|
+
options?.expectedVersion !== undefined &&
|
|
141
|
+
existing.version !== undefined &&
|
|
142
|
+
existing.version !== options.expectedVersion
|
|
143
|
+
) {
|
|
144
|
+
throw new SessionConflictError(id, options.expectedVersion, existing.version);
|
|
145
|
+
}
|
|
146
|
+
|
|
134
147
|
const updated: SessionData<TData> = {
|
|
135
148
|
...existing,
|
|
136
149
|
...data,
|
|
150
|
+
version: (existing.version ?? options?.expectedVersion ?? 0) + 1,
|
|
137
151
|
updatedAt: new Date(),
|
|
138
152
|
};
|
|
139
153
|
|
|
@@ -12,7 +12,9 @@ import type {
|
|
|
12
12
|
SessionStatus,
|
|
13
13
|
CollectedStateData,
|
|
14
14
|
CreateSessionData,
|
|
15
|
+
SessionUpdateOptions,
|
|
15
16
|
} from "../types";
|
|
17
|
+
import { SessionConflictError } from "../types/errors";
|
|
16
18
|
import { createSessionId } from "../utils";
|
|
17
19
|
|
|
18
20
|
/**
|
|
@@ -144,6 +146,7 @@ class MongoSessionRepository<TData = Record<string, unknown>>
|
|
|
144
146
|
createSessionId(),
|
|
145
147
|
status: data.status || "active",
|
|
146
148
|
messageCount: data.messageCount || 0,
|
|
149
|
+
version: data.version ?? 1,
|
|
147
150
|
createdAt: now,
|
|
148
151
|
updatedAt: now,
|
|
149
152
|
};
|
|
@@ -173,11 +176,47 @@ class MongoSessionRepository<TData = Record<string, unknown>>
|
|
|
173
176
|
|
|
174
177
|
async update(
|
|
175
178
|
id: string,
|
|
176
|
-
data: Partial<Omit<SessionData<TData>, "id" | "createdAt"
|
|
179
|
+
data: Partial<Omit<SessionData<TData>, "id" | "createdAt">>,
|
|
180
|
+
options?: SessionUpdateOptions
|
|
177
181
|
): Promise<SessionData<TData> | null> {
|
|
182
|
+
if (options?.expectedVersion !== undefined) {
|
|
183
|
+
const expectedVersion = options.expectedVersion;
|
|
184
|
+
|
|
185
|
+
// Compare-and-swap via the filter: docs without a stored version
|
|
186
|
+
// (pre-2.4) are accepted and adopt expectedVersion as their base
|
|
187
|
+
const result = await this.collection.updateOne(
|
|
188
|
+
{
|
|
189
|
+
id,
|
|
190
|
+
$or: [
|
|
191
|
+
{ version: { $exists: false } },
|
|
192
|
+
{ version: null },
|
|
193
|
+
{ version: expectedVersion },
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
{ $set: { ...data, version: expectedVersion + 1, updatedAt: new Date() } }
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
if (result.matchedCount === 0) {
|
|
200
|
+
const existing = await this.collection.findOne({ id });
|
|
201
|
+
if (!existing) return null;
|
|
202
|
+
throw new SessionConflictError(id, expectedVersion, existing.version);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return await this.findById(id);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const existing = await this.collection.findOne({ id });
|
|
209
|
+
if (!existing) return null;
|
|
210
|
+
|
|
178
211
|
const result = await this.collection.updateOne(
|
|
179
212
|
{ id },
|
|
180
|
-
{
|
|
213
|
+
{
|
|
214
|
+
$set: {
|
|
215
|
+
...data,
|
|
216
|
+
version: (existing.version ?? 0) + 1,
|
|
217
|
+
updatedAt: new Date(),
|
|
218
|
+
},
|
|
219
|
+
}
|
|
181
220
|
);
|
|
182
221
|
|
|
183
222
|
if (result.matchedCount === 0) return null;
|
|
@@ -42,7 +42,9 @@ import type {
|
|
|
42
42
|
MessageData,
|
|
43
43
|
CollectedStateData,
|
|
44
44
|
CreateSessionData,
|
|
45
|
+
SessionUpdateOptions,
|
|
45
46
|
} from "../types";
|
|
47
|
+
import { SessionConflictError } from "../types/errors";
|
|
46
48
|
|
|
47
49
|
/**
|
|
48
50
|
* OpenSearch Client interface (minimal typing for the official client)
|
|
@@ -200,6 +202,7 @@ export class OpenSearchAdapter<TData = Record<string, unknown>> implements Persi
|
|
|
200
202
|
pendingDirective: { type: "object", enabled: false },
|
|
201
203
|
signals: { type: "object", enabled: false },
|
|
202
204
|
messageCount: { type: "integer" },
|
|
205
|
+
version: { type: "integer" },
|
|
203
206
|
createdAt: { type: "date" },
|
|
204
207
|
updatedAt: { type: "date" },
|
|
205
208
|
lastMessageAt: { type: "date" },
|
|
@@ -266,6 +269,7 @@ class OpenSearchSessionRepository<TData = Record<string, unknown>>
|
|
|
266
269
|
id,
|
|
267
270
|
status: data.status || "active",
|
|
268
271
|
messageCount: data.messageCount || 0,
|
|
272
|
+
version: data.version ?? 1,
|
|
269
273
|
createdAt: now,
|
|
270
274
|
updatedAt: now,
|
|
271
275
|
};
|
|
@@ -341,10 +345,28 @@ class OpenSearchSessionRepository<TData = Record<string, unknown>>
|
|
|
341
345
|
|
|
342
346
|
async update(
|
|
343
347
|
id: string,
|
|
344
|
-
updates: Partial<Omit<SessionData<TData>, "id" | "createdAt"
|
|
348
|
+
updates: Partial<Omit<SessionData<TData>, "id" | "createdAt">>,
|
|
349
|
+
options?: SessionUpdateOptions
|
|
345
350
|
): Promise<SessionData<TData> | null> {
|
|
351
|
+
// Check-then-set on the stored document — not fully atomic across writers
|
|
352
|
+
const existing = await this.findById(id);
|
|
353
|
+
if (!existing) return null;
|
|
354
|
+
|
|
355
|
+
if (
|
|
356
|
+
options?.expectedVersion !== undefined &&
|
|
357
|
+
existing.version !== undefined &&
|
|
358
|
+
existing.version !== options.expectedVersion
|
|
359
|
+
) {
|
|
360
|
+
throw new SessionConflictError(
|
|
361
|
+
id,
|
|
362
|
+
options.expectedVersion,
|
|
363
|
+
existing.version
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
346
367
|
const doc: Record<string, unknown> = {
|
|
347
368
|
...updates,
|
|
369
|
+
version: (existing.version ?? options?.expectedVersion ?? 0) + 1,
|
|
348
370
|
updatedAt: new Date().toISOString(),
|
|
349
371
|
};
|
|
350
372
|
|
|
@@ -485,6 +507,7 @@ class OpenSearchSessionRepository<TData = Record<string, unknown>>
|
|
|
485
507
|
currentStep: doc.currentStep as string | undefined,
|
|
486
508
|
collectedData: doc.collectedData as CollectedStateData<TData> | undefined,
|
|
487
509
|
messageCount: (doc.messageCount as number) || 0,
|
|
510
|
+
version: (doc.version as number | null) ?? undefined,
|
|
488
511
|
createdAt: new Date(doc.createdAt as string),
|
|
489
512
|
updatedAt: new Date(doc.updatedAt as string),
|
|
490
513
|
lastMessageAt: doc.lastMessageAt
|