@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
|
@@ -9,7 +9,7 @@ order: 5
|
|
|
9
9
|
|
|
10
10
|
By default, `createAgent` runs against an in-process `MemoryAdapter`. That is the right choice while you are building — zero setup, instant resets between tests — but it forgets every conversation the moment the process exits. Production needs storage that outlives a deploy, scales to multiple replicas, and lets a session resume by id from any machine.
|
|
11
11
|
|
|
12
|
-
This guide covers the swap. You will pick an adapter, wire it through `persistence`, resume sessions by `sessionId`, and run the v1 → v2 schema migration if you are upgrading an existing store.
|
|
12
|
+
This guide covers the swap. You will pick an adapter, wire it through `persistence`, resume sessions by `sessionId`, handle concurrent writers with optimistic locking, version your session schema, and run the v1 → v2 schema migration if you are upgrading an existing store.
|
|
13
13
|
|
|
14
14
|
## The seven adapters
|
|
15
15
|
|
|
@@ -39,7 +39,7 @@ bun add -d prisma
|
|
|
39
39
|
bunx prisma init
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
**2. Declare the session model.** Two columns matter most: `pendingDirective` and `signals`. The agent serializes its [`Directive`](../reference/directive.md) and signals state into them at the end of every turn and reads them back at the start of the next. Both are required on every adapter's session schema in v2.
|
|
42
|
+
**2. Declare the session model.** Two columns matter most: `pendingDirective` and `signals`. The agent serializes its [`Directive`](../reference/directive.md) and signals state into them at the end of every turn and reads them back at the start of the next. Both are required on every adapter's session schema in v2. The `version` column is optional but recommended — it enables [optimistic locking](#concurrent-writers-optimistic-locking); without it the adapter degrades gracefully and locking stays inactive.
|
|
43
43
|
|
|
44
44
|
```prisma
|
|
45
45
|
model AgentSession {
|
|
@@ -51,6 +51,7 @@ model AgentSession {
|
|
|
51
51
|
collectedData Json?
|
|
52
52
|
pendingDirective Json?
|
|
53
53
|
signals Json?
|
|
54
|
+
version Int?
|
|
54
55
|
messageCount Int @default(0)
|
|
55
56
|
lastMessageAt DateTime?
|
|
56
57
|
completedAt DateTime?
|
|
@@ -156,6 +157,72 @@ A few things worth noting:
|
|
|
156
157
|
|
|
157
158
|
If you need an archive of every session and message (audit logs, search, analytics), pair Redis with a second adapter on a slower path, or pick a durable adapter from the table above directly.
|
|
158
159
|
|
|
160
|
+
## Concurrent writers: optimistic locking
|
|
161
|
+
|
|
162
|
+
Once sessions span processes, two writers can race on one id — parallel webhooks, a double-send from a chat widget, two browser tabs. Every save is a compare-and-swap on the session's `version` (incremented on each save): the loser's save throws `SessionConflictError` instead of silently overwriting the winner's state. The error carries `sessionId`, `expectedVersion`, and `actualVersion`; the recovery is mechanical — reload, retry.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { SessionConflictError } from "@falai/agent";
|
|
166
|
+
|
|
167
|
+
function isSessionConflict(err: unknown): boolean {
|
|
168
|
+
if (err instanceof SessionConflictError) return true;
|
|
169
|
+
if (err instanceof Error && err.name === "ResponseGenerationError") {
|
|
170
|
+
const original = (err as { details?: { originalError?: unknown } }).details?.originalError;
|
|
171
|
+
return original instanceof SessionConflictError;
|
|
172
|
+
}
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
return await agent.respond({ history, session });
|
|
178
|
+
} catch (err) {
|
|
179
|
+
if (isSessionConflict(err)) {
|
|
180
|
+
const fresh = await agent.session.getOrCreate(sessionId); // reload the winning state
|
|
181
|
+
return agent.respond({ history, session: fresh });
|
|
182
|
+
}
|
|
183
|
+
throw err;
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Three things you do **not** have to worry about:
|
|
188
|
+
|
|
189
|
+
- **Same-process concurrency.** Concurrent saves of one session from a single process are serialized through a per-session queue — they never conflict with each other. Conflicts only fire between genuinely independent copies (two processes, or two separately loaded sessions).
|
|
190
|
+
- **Existing rows.** Sessions written by pre-2.4 versions have no stored `version` and are accepted without conflict; the first save stamps them. Memory, Mongo, Redis, and OpenSearch need no schema change at all, and the SQLite/PostgreSQL adapters auto-add the `version` column in `initialize()`.
|
|
191
|
+
- **Opting out.** Prisma users who skip the `version Int?` column simply run without locking — the adapter detects the missing column and degrades gracefully.
|
|
192
|
+
|
|
193
|
+
The per-adapter storage details live in [persistence adapters](../reference/adapters.md#optimistic-locking); the retry pattern is also covered in [Errors](./error-handling.md).
|
|
194
|
+
|
|
195
|
+
## Schema versioning: migrate old sessions on load
|
|
196
|
+
|
|
197
|
+
The locking `version` guards *who* writes; `schemaVersion` guards *what shape* they write. When you rename a schema field or restructure collected data, sessions persisted by the previous deploy still carry the old shape. Declare a `schemaVersion` and a `migrateSession` function, and the agent upgrades stale state at load time:
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
const agent = createAgent({
|
|
201
|
+
schema, provider, flows,
|
|
202
|
+
persistence: {
|
|
203
|
+
adapter,
|
|
204
|
+
schemaVersion: 2,
|
|
205
|
+
migrateSession: (collected, fromVersion) => {
|
|
206
|
+
// v1 stored `destination`; v2 renamed it to `city`
|
|
207
|
+
if ((fromVersion ?? 1) < 2) {
|
|
208
|
+
const { destination, ...rest } = collected.data as { destination?: string };
|
|
209
|
+
return { ...collected, data: { ...rest, city: destination } };
|
|
210
|
+
}
|
|
211
|
+
return collected;
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
How it behaves:
|
|
218
|
+
|
|
219
|
+
- Every save stamps the configured `schemaVersion` onto the persisted state.
|
|
220
|
+
- On load, when the stored version differs from the configured one (or is missing — pre-versioning rows pass `fromVersion: undefined`), `migrateSession` runs and its return value is used for the turn. The new stamp persists on the next save.
|
|
221
|
+
- The migrator may be async, and must return state valid for the current `schemaVersion`.
|
|
222
|
+
- With a version mismatch but **no** `migrateSession`, the agent logs a warning and loads the state as-is.
|
|
223
|
+
|
|
224
|
+
Bump `schemaVersion` with every breaking change to your collected-data shape and keep the migrator's old-version branches around — a long-idle session might skip several versions and arrive with any historical `fromVersion`.
|
|
225
|
+
|
|
159
226
|
## Schema migration: v1 → v2
|
|
160
227
|
|
|
161
228
|
If you are upgrading an existing v1 store, the session schema needs two new columns before v2 runs against it:
|
package/docs/guides/streaming.md
CHANGED
|
@@ -134,4 +134,6 @@ When the signal aborts, the loop exits cleanly — no exception is thrown by the
|
|
|
134
134
|
|
|
135
135
|
For a Stop button, store the `controller` reference for the active stream on the UI side and call `controller.abort()` from the click handler. For server-side hard ceilings, wrap `respondStream` with an `AbortController` whose `setTimeout` fires at your SLO budget.
|
|
136
136
|
|
|
137
|
+
If the turn fails — the generator surfaces an error chunk — it has no lasting effect: the in-memory session rolls back to its pre-turn snapshot (the user message added by `stream()` before the turn is retained), and persisted state is whatever the previous turn saved. Retrying is always safe.
|
|
138
|
+
|
|
137
139
|
**Next:** [Errors](./error-handling.md)
|
package/docs/migration/README.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Migration"
|
|
3
|
-
description: "Migration guides for upgrading @falai/agent
|
|
3
|
+
description: "Migration guides for upgrading @falai/agent between major and minor versions."
|
|
4
4
|
type: overview
|
|
5
5
|
order: 99
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Migration
|
|
9
9
|
|
|
10
|
+
Upgrading from `2.3.x`? The v2.4 guide covers the concurrency-safety and provider-layer changes — required `AiProvider.capabilities`, normalized `ProviderError`, optimistic session locking with `SessionConflictError`, the `unknown` generic defaults, and the internals removed from the public barrel — with before/after code and per-adapter notes.
|
|
11
|
+
|
|
12
|
+
[Read the v2.3 → v2.4 migration guide](./v2-3-to-v2-4.md)
|
|
13
|
+
|
|
10
14
|
Upgrading from `1.x`? The consolidated migration guide covers every breaking change in v2 — including the Route → Flow rename, the Instruction unification, the Tool merge, and the Directive collapse — with rename tables, per-adapter schema migrations, and before/after code for each section.
|
|
11
15
|
|
|
12
16
|
[Read the v1 → v2 migration guide](./v1-to-v2.md)
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "v2.3 → v2.4 migration"
|
|
3
|
+
description: "Every breaking change in v2.4 with before/after code: provider capabilities, ProviderError, optimistic session locking, and the unknown generic defaults."
|
|
4
|
+
type: migration
|
|
5
|
+
order: 2
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# v2.3 → v2.4 Migration
|
|
9
|
+
|
|
10
|
+
**Version:** 2.4.0 — Architecture hardening (concurrency safety, consolidated provider layer, stricter types)
|
|
11
|
+
|
|
12
|
+
## Summary
|
|
13
|
+
|
|
14
|
+
v2.4 hardens three surfaces. Sessions gain optimistic locking (`version` + `SessionConflictError`) and user-defined schema versioning (`schemaVersion` + `migrateSession`). The provider layer is consolidated: every `AiProvider` must declare `capabilities`, terminal failures are normalized into `ProviderError`, and the new `OpenAICompatibleProvider` base class is exported. The type surface tightens: generic defaults move from `any` to `unknown`, and a handful of internals leave the public barrel.
|
|
15
|
+
|
|
16
|
+
If you only use the built-in providers and adapters, the upgrade is usually zero-code: the new `version` column is added automatically (SQLite/PostgreSQL) or needs no schema at all (Memory, Mongo, Redis, OpenSearch), and pre-2.4 rows are accepted without conflict. The sections below cover the cases that do need a change.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Table of Contents
|
|
21
|
+
|
|
22
|
+
1. [Custom providers must declare `capabilities`](#1-custom-providers-must-declare-capabilities)
|
|
23
|
+
2. [Provider terminal errors are now `ProviderError`](#2-provider-terminal-errors-are-now-providererror)
|
|
24
|
+
3. [Optimistic session locking](#3-optimistic-session-locking)
|
|
25
|
+
4. [Custom `SessionRepository`: `update()` gains `expectedVersion`](#4-custom-sessionrepository-update-gains-expectedversion)
|
|
26
|
+
5. [Generic defaults: `any` → `unknown`](#5-generic-defaults-any--unknown)
|
|
27
|
+
6. [`SignalFiring.directive` is now `ResolvedSignalDirective`](#6-signalfiringdirective-is-now-resolvedsignaldirective)
|
|
28
|
+
7. [Removed from the public barrel](#7-removed-from-the-public-barrel)
|
|
29
|
+
8. [`ResponsePipeline` stored-state API removed](#8-responsepipeline-stored-state-api-removed)
|
|
30
|
+
9. [Behavioral changes to be aware of](#9-behavioral-changes-to-be-aware-of)
|
|
31
|
+
10. [Verification](#verification)
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. Custom providers must declare `capabilities`
|
|
36
|
+
|
|
37
|
+
`AiProvider.capabilities: ProviderCapabilities` is now a **required** member. Every custom provider must declare its five static capability flags; the built-ins already do.
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import type { AiProvider, ProviderCapabilities } from "@falai/agent";
|
|
41
|
+
|
|
42
|
+
class MyProvider implements AiProvider {
|
|
43
|
+
readonly name = "my-provider";
|
|
44
|
+
|
|
45
|
+
// New in v2.4 — required
|
|
46
|
+
readonly capabilities: ProviderCapabilities = {
|
|
47
|
+
supportsTools: true,
|
|
48
|
+
supportsNativeJsonSchema: true,
|
|
49
|
+
supportsStreaming: true,
|
|
50
|
+
supportsStreamingToolCalls: true,
|
|
51
|
+
supportsPromptCaching: false,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// generateMessage / generateMessageStream as before
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
If your provider wraps an OpenAI-compatible API (Groq, Together, Fireworks, …), consider subclassing the new exported [`OpenAICompatibleProvider`](../reference/providers.md#building-a-custom-openai-compatible-provider) base class instead of implementing `AiProvider` from scratch — it supplies message building, tool-call parsing, streaming, retries, backup models, and error normalization.
|
|
59
|
+
|
|
60
|
+
The capability values for the five built-in providers are documented in the [providers reference](../reference/providers.md#capabilities).
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 2. Provider terminal errors are now `ProviderError`
|
|
65
|
+
|
|
66
|
+
Terminal provider failures — i.e. after retries and backup models (if any) are exhausted — now throw `ProviderError` (exported) with a normalized `code` instead of rethrowing the raw SDK error. The original SDK/HTTP error is preserved as `cause`.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
type ProviderErrorCode =
|
|
70
|
+
| 'rate_limited'
|
|
71
|
+
| 'overloaded'
|
|
72
|
+
| 'auth'
|
|
73
|
+
| 'invalid_request'
|
|
74
|
+
| 'schema_rejected'
|
|
75
|
+
| 'timeout'
|
|
76
|
+
| 'network'
|
|
77
|
+
| 'unknown';
|
|
78
|
+
|
|
79
|
+
class ProviderError extends Error {
|
|
80
|
+
readonly code: ProviderErrorCode;
|
|
81
|
+
readonly provider: string; // e.g. "openai"
|
|
82
|
+
readonly cause?: unknown; // original SDK error
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Before / After
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// ─── v2.3: match on raw SDK error shapes ───
|
|
90
|
+
try {
|
|
91
|
+
await agent.respond(message);
|
|
92
|
+
} catch (err) {
|
|
93
|
+
if ((err as { status?: number }).status === 429) { /* rate limited */ }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ─── v2.4: match on the normalized code ───
|
|
97
|
+
import { ProviderError } from "@falai/agent";
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
await agent.respond(message);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
if (err instanceof ProviderError) {
|
|
103
|
+
if (err.code === "rate_limited" || err.code === "overloaded") {
|
|
104
|
+
// backoff and retry
|
|
105
|
+
}
|
|
106
|
+
console.error(err.provider, err.code, err.cause); // original SDK error on cause
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
> **Note:** when the failure surfaces through `agent.respond(...)`, it is wrapped in `ResponseGenerationError` like every other turn failure — the `ProviderError` is then on `details.originalError`. Code calling a provider directly sees the `ProviderError` itself.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 3. Optimistic session locking
|
|
116
|
+
|
|
117
|
+
`SessionState` / `SessionData` carry a `version` number, incremented on every save. A save with a stale version — another writer persisted the session after this one loaded it (concurrent `respond()` calls, parallel webhooks, two tabs) — throws the new `SessionConflictError` instead of silently overwriting state.
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import { SessionConflictError } from "@falai/agent";
|
|
121
|
+
|
|
122
|
+
function isSessionConflict(err: unknown): boolean {
|
|
123
|
+
if (err instanceof SessionConflictError) return true;
|
|
124
|
+
// respond() wraps turn failures in ResponseGenerationError —
|
|
125
|
+
// the conflict is then on details.originalError
|
|
126
|
+
if (err instanceof Error && err.name === "ResponseGenerationError") {
|
|
127
|
+
const details = (err as { details?: { originalError?: unknown } }).details;
|
|
128
|
+
return details?.originalError instanceof SessionConflictError;
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
await agent.respond({ history, session });
|
|
135
|
+
} catch (err) {
|
|
136
|
+
if (isSessionConflict(err)) {
|
|
137
|
+
// Reload the session and retry — another writer won the race.
|
|
138
|
+
const fresh = await agent.session.getOrCreate(sessionId);
|
|
139
|
+
return agent.respond({ history, session: fresh });
|
|
140
|
+
}
|
|
141
|
+
throw err;
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`SessionConflictError` carries `sessionId`, `expectedVersion`, and `actualVersion`. Recommended handling: reload the session, retry the operation.
|
|
146
|
+
|
|
147
|
+
### What you need to migrate, per adapter
|
|
148
|
+
|
|
149
|
+
| Adapter | Action |
|
|
150
|
+
|---------|--------|
|
|
151
|
+
| `MemoryAdapter` | Nothing. |
|
|
152
|
+
| `MongoAdapter` | Nothing — documents gain `version` on next save. |
|
|
153
|
+
| `RedisAdapter` | Nothing — `version` rides inside the JSON value. |
|
|
154
|
+
| `OpenSearchAdapter` | Nothing — `version` is a document field. |
|
|
155
|
+
| `SQLiteAdapter` | Nothing — `initialize()` auto-adds the `version` column. |
|
|
156
|
+
| `PostgreSQLAdapter` | Nothing — `initialize()` auto-adds the `version` column. |
|
|
157
|
+
| `PrismaAdapter` | Add `version Int?` to your session model (see below). |
|
|
158
|
+
|
|
159
|
+
Rows written by pre-2.4 versions have no stored `version` and are **accepted without conflict** — there is no backfill to run. The first v2.4 save stamps them.
|
|
160
|
+
|
|
161
|
+
### Prisma
|
|
162
|
+
|
|
163
|
+
```diff
|
|
164
|
+
model AgentSession {
|
|
165
|
+
id String @id
|
|
166
|
+
// ...
|
|
167
|
+
pendingDirective Json?
|
|
168
|
+
signals Json?
|
|
169
|
+
+ version Int?
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Run `npx prisma migrate dev --name v2-4-session-version`. Without the column, the adapter detects its absence on the first write and degrades gracefully — everything works, but optimistic locking stays **inactive** until you add it.
|
|
174
|
+
|
|
175
|
+
### Same-process concurrency
|
|
176
|
+
|
|
177
|
+
Concurrent saves of one session from the same process are serialized through a per-session queue and never conflict with each other. `SessionConflictError` only fires for genuinely independent copies — two processes, or two separately loaded sessions.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 4. Custom `SessionRepository`: `update()` gains `expectedVersion`
|
|
182
|
+
|
|
183
|
+
The `SessionRepository.update()` signature gained an optional third parameter:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// ─── v2.3 ───
|
|
187
|
+
update(id: string, data: Partial<...>): Promise<SessionData<TData> | null>;
|
|
188
|
+
|
|
189
|
+
// ─── v2.4 ───
|
|
190
|
+
update(
|
|
191
|
+
id: string,
|
|
192
|
+
data: Partial<Omit<SessionData<TData>, "id" | "createdAt">>,
|
|
193
|
+
options?: { expectedVersion?: number } // SessionUpdateOptions
|
|
194
|
+
): Promise<SessionData<TData> | null>;
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Two valid implementations:
|
|
198
|
+
|
|
199
|
+
- **Compare-and-swap (recommended).** When `options.expectedVersion` is provided, throw `SessionConflictError` if the stored `version` differs (rows with no stored version are accepted), and increment `version` by one on every successful update. See `MemoryAdapter` for the reference implementation.
|
|
200
|
+
- **Ignore it.** Don't read `options` at all — your store simply opts out of optimistic locking, exactly like pre-2.4 behavior.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 5. Generic defaults: `any` → `unknown`
|
|
205
|
+
|
|
206
|
+
The default type parameters on `Agent`, `Tool`, `ToolContext`, `ToolResult`, and `ToolHandler` changed from `any` to `unknown`. `ToolHistoryItem.content` is also `unknown` (was `any`).
|
|
207
|
+
|
|
208
|
+
Typed code is unaffected — if you pass explicit generics or let inference flow from `schema`, nothing changes. Untyped tool code that relied on implicit `any` may now need explicit type parameters or a type guard:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
// ─── v2.3: compiled because TData defaulted to any ───
|
|
212
|
+
const tool: Tool = {
|
|
213
|
+
id: "lookup",
|
|
214
|
+
handler: async (ctx) => {
|
|
215
|
+
return ctx.data.orderId.trim(); // ctx.data was any
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// ─── v2.4: declare the generics… ───
|
|
220
|
+
const tool: Tool<MyContext, MyData> = {
|
|
221
|
+
id: "lookup",
|
|
222
|
+
handler: async (ctx) => {
|
|
223
|
+
return ctx.data.orderId?.trim();
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// ─── …or narrow the unknown ───
|
|
228
|
+
handler: async (ctx) => {
|
|
229
|
+
const data = ctx.data as Partial<MyData>;
|
|
230
|
+
return data.orderId?.trim();
|
|
231
|
+
},
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## 6. `SignalFiring.directive` is now `ResolvedSignalDirective`
|
|
237
|
+
|
|
238
|
+
`SignalFiring.directive` was typed `SignalDirective`; it is now `ResolvedSignalDirective` (exported). The difference: `replyWith` has already been resolved onto `reply` and stripped by the time a firing reaches the response surface — which was already the runtime behavior; the type now says so.
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
// ─── v2.3 ───
|
|
242
|
+
for (const firing of response.triggeredSignals ?? []) {
|
|
243
|
+
firing.directive?.replyWith; // typed as present, never was at runtime
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ─── v2.4 ───
|
|
247
|
+
for (const firing of response.triggeredSignals ?? []) {
|
|
248
|
+
firing.directive?.reply; // resolved reply text, if any
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## 7. Removed from the public barrel
|
|
255
|
+
|
|
256
|
+
These internals are no longer exported. They locked the architecture into semver and had no supported external use:
|
|
257
|
+
|
|
258
|
+
| Removed export | Kind | Replacement |
|
|
259
|
+
|---|---|---|
|
|
260
|
+
| `DirectiveChainTracker` | class | Internal — remove direct imports. |
|
|
261
|
+
| `DirectiveChainEntry` | type | Internal — remove direct imports. |
|
|
262
|
+
| `StreamingToolExecutor` | class | Internal — remove direct imports. |
|
|
263
|
+
|
|
264
|
+
If you imported any of these, the conversation-control surface you want is `Directive`, `agent.dispatch()`, and the documented hooks.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## 8. `ResponsePipeline` stored-state API removed
|
|
269
|
+
|
|
270
|
+
`ResponsePipeline` (internal, but reachable in v2.3 via subclassing tricks) no longer holds mutable turn state. Removed:
|
|
271
|
+
|
|
272
|
+
- `setContext()` / `setCurrentSession()` / `getStoredContext()` / `getCurrentSession()`
|
|
273
|
+
- `updateDataFlow()`
|
|
274
|
+
|
|
275
|
+
Context and session are now passed explicitly through the pipeline; `determineNextStep` takes a required `context` parameter. If you depended on these, pass state explicitly instead of reading it back from the pipeline.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## 9. Behavioral changes to be aware of
|
|
280
|
+
|
|
281
|
+
Not breaking in the type sense, but observable at runtime:
|
|
282
|
+
|
|
283
|
+
- **`session.data` is the single source of truth for collected data.** The bidirectional sync between the Agent's internal copy and the session is gone. `agent.getCollectedData()` / `agent.getData()` read from the live session; `agent.updateCollectedData()` writes into it. Data set before any session exists (including `initialData`) is staged and seeds the first created session; loading an existing session discards staged data in favor of the stored state.
|
|
284
|
+
- **Passing an explicit `session` to `respond()` no longer merges the managed session's data into it.** That was cross-session state leakage; sessions you pass in are now used as-is.
|
|
285
|
+
- **Failed-turn rollback.** If `respond()` / `stream()` throws mid-turn, the in-memory session is restored to its pre-turn snapshot (the user message added by `chat()` / `stream()` before the turn is retained). Persisted state is from the previous turn — a failed turn no longer leaves a partially mutated session.
|
|
286
|
+
- **Deterministic compaction.** When `compaction` is configured, it now runs at end-of-turn finalize on every `respond()` / `chat()` / `stream()`. Previously it only ran inside `session.addMessage()`, so respond-only integrations grew history unboundedly.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Verification
|
|
291
|
+
|
|
292
|
+
After migrating, confirm no legacy references remain. Run from your repo root:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
rg -n '\b(DirectiveChainTracker|DirectiveChainEntry|StreamingToolExecutor|updateDataFlow|getStoredContext)\b' \
|
|
296
|
+
--glob '**/*.ts' \
|
|
297
|
+
--glob '!node_modules/**' \
|
|
298
|
+
--glob '!dist/**'
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Expected output: **zero matches**. Then run the type checker — it will flag missing `capabilities` on custom providers, the new `update()` signature on custom repositories, and any implicit-`any` tool code:
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
npx tsc --noEmit
|
|
305
|
+
# or
|
|
306
|
+
bun run typecheck
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
For Prisma users: confirm the `version Int?` column exists if you want locking active.
|
|
310
|
+
|
|
311
|
+
## Cross-References
|
|
312
|
+
|
|
313
|
+
- [CHANGELOG](../../CHANGELOG.md) — full v2.4 release notes
|
|
314
|
+
- [Persistence](../guides/persistence.md) — locking and schema-versioning recipes
|
|
315
|
+
- [Providers](../reference/providers.md) — capabilities matrix and `OpenAICompatibleProvider`
|
|
316
|
+
- [Errors](../reference/errors.md) — `ProviderError` and `SessionConflictError`
|
|
@@ -37,8 +37,13 @@ interface PersistenceAdapter<TData> {
|
|
|
37
37
|
|
|
38
38
|
interface PersistenceConfig<TData> {
|
|
39
39
|
adapter: PersistenceAdapter<TData>;
|
|
40
|
-
autoSave?: boolean;
|
|
41
|
-
userId?: string;
|
|
40
|
+
autoSave?: boolean; // default: true
|
|
41
|
+
userId?: string; // attached to created sessions/messages
|
|
42
|
+
schemaVersion?: number; // stamps persisted state; see "Schema versioning"
|
|
43
|
+
migrateSession?: ( // upgrades state written under an older schemaVersion
|
|
44
|
+
collectedData: CollectedStateData<TData>,
|
|
45
|
+
fromVersion: number | undefined
|
|
46
|
+
) => CollectedStateData<TData> | Promise<CollectedStateData<TData>>;
|
|
42
47
|
}
|
|
43
48
|
```
|
|
44
49
|
|
|
@@ -46,6 +51,8 @@ The `SessionRepository` write methods accept a `CollectedStateData` payload that
|
|
|
46
51
|
|
|
47
52
|
```typescript
|
|
48
53
|
interface CollectedStateData<TData> {
|
|
54
|
+
/** User-defined schema version of the agent that wrote this state. */
|
|
55
|
+
schemaVersion?: number;
|
|
49
56
|
data: Partial<TData>;
|
|
50
57
|
flowHistory: SessionState<TData>["flowHistory"];
|
|
51
58
|
history?: SessionState<TData>["history"];
|
|
@@ -64,6 +71,43 @@ Two columns deserve special attention:
|
|
|
64
71
|
|
|
65
72
|
Both are **required columns** on every adapter's session schema. v1 schemas had a `pending_transition` column instead; v2 replaces it. See the per-adapter migration notes below and the consolidated [v1 → v2 migration](../migration/v1-to-v2.md).
|
|
66
73
|
|
|
74
|
+
## Optimistic locking
|
|
75
|
+
|
|
76
|
+
Every session row carries a `version: number` (on `SessionData` / `SessionState`), incremented by the repository on every update. `SessionRepository.update()` takes an optional compare-and-swap guard:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
interface SessionUpdateOptions {
|
|
80
|
+
/** Reject the update with SessionConflictError if the stored `version` differs. */
|
|
81
|
+
expectedVersion?: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
update(
|
|
85
|
+
id: string,
|
|
86
|
+
data: Partial<Omit<SessionData<TData>, "id" | "createdAt">>,
|
|
87
|
+
options?: SessionUpdateOptions
|
|
88
|
+
): Promise<SessionData<TData> | null>;
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
When the agent saves a session and another writer bumped the stored `version` since this copy was loaded (concurrent `respond()` calls from two processes, parallel webhooks, two tabs), the save throws the exported [`SessionConflictError`](./errors.md) — carrying `sessionId`, `expectedVersion`, and `actualVersion` — instead of silently overwriting the winner's state. Recommended handling: reload the session and retry. Same-process concurrent saves of one session are serialized through a per-session queue and never conflict with each other.
|
|
92
|
+
|
|
93
|
+
What each adapter needs:
|
|
94
|
+
|
|
95
|
+
| Adapter | `version` storage | Migration from pre-2.4 |
|
|
96
|
+
|---------|-------------------|------------------------|
|
|
97
|
+
| `MemoryAdapter` | in-memory field | None. |
|
|
98
|
+
| `MongoAdapter` | document field | None — added on next save. |
|
|
99
|
+
| `RedisAdapter` | inside the JSON value | None. |
|
|
100
|
+
| `OpenSearchAdapter` | document field | None. |
|
|
101
|
+
| `SQLiteAdapter` | `version INTEGER` column | None — `initialize()` auto-adds the column. |
|
|
102
|
+
| `PostgreSQLAdapter` | `version INTEGER` column | None — `initialize()` auto-adds the column. |
|
|
103
|
+
| `PrismaAdapter` | your model's `version Int?` | Add `version Int?` to your session model. Without it the adapter detects the missing column and degrades gracefully — locking stays inactive. |
|
|
104
|
+
|
|
105
|
+
Rows written by pre-2.4 versions have no stored `version` and are **accepted without conflict**; the first v2.4 save stamps them. If you implement a custom `SessionRepository`, honor `options.expectedVersion` as a compare-and-swap (see `MemoryAdapter` for the reference implementation) or ignore the parameter to opt out of locking.
|
|
106
|
+
|
|
107
|
+
## Schema versioning
|
|
108
|
+
|
|
109
|
+
Independent of the locking `version`, `CollectedStateData.schemaVersion` tracks the **user-defined shape** of your persisted state. Configure `persistence.schemaVersion` and the agent stamps it on every save; on load, a session written under a different (or missing) version is passed through `persistence.migrateSession` before use. See the [persistence guide](../guides/persistence.md#schema-versioning-migrate-old-sessions-on-load) for the recipe.
|
|
110
|
+
|
|
67
111
|
### Wiring with `sessionId`
|
|
68
112
|
|
|
69
113
|
Pass `sessionId` to the agent constructor or directly to a `respond` call to resume a conversation by id:
|
|
@@ -148,7 +192,7 @@ interface PrismaAdapterOptions {
|
|
|
148
192
|
|
|
149
193
|
### Schema requirements
|
|
150
194
|
|
|
151
|
-
Your `Session` model must declare `pendingDirective` and `signals` JSON columns alongside the standard fields:
|
|
195
|
+
Your `Session` model must declare `pendingDirective` and `signals` JSON columns alongside the standard fields. Add `version Int?` to enable [optimistic locking](#optimistic-locking):
|
|
152
196
|
|
|
153
197
|
```prisma
|
|
154
198
|
model AgentSession {
|
|
@@ -161,6 +205,7 @@ model AgentSession {
|
|
|
161
205
|
collectedData Json?
|
|
162
206
|
pendingDirective Json?
|
|
163
207
|
signals Json?
|
|
208
|
+
version Int?
|
|
164
209
|
messageCount Int @default(0)
|
|
165
210
|
lastMessageAt DateTime?
|
|
166
211
|
completedAt DateTime?
|
|
@@ -169,7 +214,7 @@ model AgentSession {
|
|
|
169
214
|
}
|
|
170
215
|
```
|
|
171
216
|
|
|
172
|
-
|
|
217
|
+
`version` is optional — the adapter detects a missing column on the first write and degrades gracefully, leaving locking inactive. The other columns are required; if your existing schema lacks them, see [v1 → v2 migration](../migration/v1-to-v2.md) for the column rename and DDL.
|
|
173
218
|
|
|
174
219
|
### Examples
|
|
175
220
|
|
|
@@ -318,7 +363,7 @@ interface PostgreSQLAdapterOptions {
|
|
|
318
363
|
|
|
319
364
|
### Schema requirements
|
|
320
365
|
|
|
321
|
-
`initialize()` creates tables with the v2 columns. Migrating from v1:
|
|
366
|
+
`initialize()` creates tables with the v2 columns and auto-adds the `version` column to tables created by pre-2.4 versions — no manual DDL for the locking upgrade. Migrating from v1:
|
|
322
367
|
|
|
323
368
|
```sql
|
|
324
369
|
ALTER TABLE agent_sessions DROP COLUMN IF EXISTS pending_transition;
|
|
@@ -369,7 +414,7 @@ interface SQLiteAdapterOptions {
|
|
|
369
414
|
|
|
370
415
|
### Schema requirements
|
|
371
416
|
|
|
372
|
-
`initialize()` creates the v2 tables. Migrating from v1 (SQLite 3.35+):
|
|
417
|
+
`initialize()` creates the v2 tables and auto-adds the `version` column to tables created by pre-2.4 versions — no manual DDL for the locking upgrade. Migrating from v1 (SQLite 3.35+):
|
|
373
418
|
|
|
374
419
|
```sql
|
|
375
420
|
ALTER TABLE agent_sessions DROP COLUMN pending_transition;
|
|
@@ -462,10 +507,11 @@ const agent = createAgent({
|
|
|
462
507
|
|
|
463
508
|
## Errors
|
|
464
509
|
|
|
465
|
-
Adapter errors propagate from the underlying driver — the agent does not wrap them. Your `try`/`catch` sees the native vendor error (e.g., `PrismaClientKnownRequestError`, `MongoServerError`, `ioredis.ReplyError`).
|
|
510
|
+
Adapter errors propagate from the underlying driver — the agent does not wrap them. Your `try`/`catch` sees the native vendor error (e.g., `PrismaClientKnownRequestError`, `MongoServerError`, `ioredis.ReplyError`). The one framework-owned exception is `SessionConflictError`.
|
|
466
511
|
|
|
467
512
|
| When | Error | Why |
|
|
468
513
|
|------|-------|-----|
|
|
514
|
+
| A save carries a stale `version` — a concurrent writer persisted the session first | `SessionConflictError` (exported) | Reload the session and retry. See [Optimistic locking](#optimistic-locking). |
|
|
469
515
|
| `pendingDirective` or `signals` column missing on a v1 schema | Driver-specific column-not-found error | Run the v2 migration shown above for your adapter. |
|
|
470
516
|
| `findById` returns `null` for a `sessionId` you passed to `createAgent` | None — a new session is created with that id | Treat unknown ids as "first turn." |
|
|
471
517
|
| `initialize()` not called on Postgres / SQLite / OpenSearch | Driver-specific table/index-not-found error on first write | Call `await adapter.initialize()` once on boot, or run the equivalent DDL yourself. |
|
|
@@ -478,4 +524,6 @@ Adapter errors propagate from the underlying driver — the agent does not wrap
|
|
|
478
524
|
- [createAgent](./create-agent.md) — the `persistence` and `sessionId` fields
|
|
479
525
|
- [Directive](./directive.md) — what `pendingDirective` stores
|
|
480
526
|
- [Signals](./signals.md) — what the `signals` column stores
|
|
527
|
+
- [Errors](./errors.md) — `SessionConflictError` fields and recovery
|
|
481
528
|
- [v1 → v2 migration](../migration/v1-to-v2.md) — column renames and DDL diffs
|
|
529
|
+
- [v2.3 → v2.4 migration](../migration/v2-3-to-v2-4.md) — the `version` column and `update()` signature change
|
|
@@ -19,7 +19,7 @@ Branches resolve **after** the step's post-LLM phase (tool execution, `finalize`
|
|
|
19
19
|
|
|
20
20
|
```typescript
|
|
21
21
|
interface BranchEntry<TContext = unknown, TData = unknown> {
|
|
22
|
-
/** AI
|
|
22
|
+
/** AI condition: positives OR, ! exclusions inhibit. */
|
|
23
23
|
when?: string | string[];
|
|
24
24
|
|
|
25
25
|
/** Code predicate. Function or array of functions (AND semantics). */
|
|
@@ -64,7 +64,7 @@ interface BranchPredicateContext<TContext = unknown, TData = unknown> {
|
|
|
64
64
|
|
|
65
65
|
| Field | Type | Required | Default | Notes |
|
|
66
66
|
|-------|------|----------|---------|-------|
|
|
67
|
-
| `when` | `string \| string[]` | no | — | AI-evaluated condition.
|
|
67
|
+
| `when` | `string \| string[]` | no | — | AI-evaluated condition. Non-`!` strings are OR alternatives; `!` strings are OR exclusions where any match inhibits the branch. Reuses the same machinery as `step.when`. Only evaluated if `if` passes (or is absent). Costs LLM tokens. |
|
|
68
68
|
| `if` | `BranchPredicate \| BranchPredicate[]` | no | — | Code predicate. Free to evaluate. When both `when` and `if` are set, `if` runs first; `when` is only evaluated if all `if` predicates pass. |
|
|
69
69
|
| `then` | `string \| Directive` | yes | — | Target. See [Resolution of `then`](#resolution-of-then) below. |
|
|
70
70
|
| `label` | `string` | no | — | Optional label surfaced in event traces and flow visualization. |
|
|
@@ -59,9 +59,9 @@ interface AgentOptions<TContext = unknown, TData = unknown> {
|
|
|
59
59
|
| `name` | `string` | yes | — | Display name surfaced in logs and prompt sections. |
|
|
60
60
|
| `goal` | `string` | no | — | One-line objective rendered into the system prompt. |
|
|
61
61
|
| `persona` | `Template<TContext>` | no | — | Who the agent is and how it communicates — role, tone, and self-concept. Rendered into the system prompt. |
|
|
62
|
-
| `provider` | `AiProvider` | yes | — | Strategy instance: `GeminiProvider`, `OpenAIProvider`, `AnthropicProvider`, or `
|
|
62
|
+
| `provider` | `AiProvider` | yes | — | Strategy instance: `GeminiProvider`, `OpenAIProvider`, `AnthropicProvider`, `OpenRouterProvider`, `DeepSeekProvider`, or your own (must declare `capabilities`). |
|
|
63
63
|
| `schema` | `StructuredSchema` | no | — | Single source of truth for `TData`. Every `collect` key in every step must reference a property defined here. |
|
|
64
|
-
| `initialData` | `Partial<TData>` | no | — |
|
|
64
|
+
| `initialData` | `Partial<TData>` | no | — | Staged at construction and seeds `session.data` when the first session is created. Loading an existing session keeps the stored data instead. |
|
|
65
65
|
| `flows` | `FlowOptions<TContext, TData>[]` | no | `[]` | Conversation flows. May also be added later via `agent.createFlow(...)`. |
|
|
66
66
|
| `tools` | `Tool<TContext, TData, unknown>[]` | no | `[]` | Agent-scoped tools. Each tool's `Tool.id` must be unique. |
|
|
67
67
|
| `instructions` | `Instruction<TContext, TData>[]` | no | `[]` | Behavioral statements discriminated by `kind: 'must' \| 'never' \| 'should'`. Scoped at agent, flow, or step level. |
|
|
@@ -71,7 +71,7 @@ interface AgentOptions<TContext = unknown, TData = unknown> {
|
|
|
71
71
|
| `context` | `TContext` | no | — | Static ambient data (user info, env, etc.) available to every hook and tool. |
|
|
72
72
|
| `contextProvider` | `ContextProvider<TContext>` | no | — | Async context loader. Use instead of `context` when ambient data must be fetched per turn. |
|
|
73
73
|
| `hooks` | `ContextLifecycleHooks<TContext, TData>` | no | — | `beforeRespond`, `onContextUpdate`, `onDataUpdate` — fire around every turn. |
|
|
74
|
-
| `persistence` | `PersistenceConfig<TData>` | no | in-memory | Session storage. Omit for `MemoryAdapter`. |
|
|
74
|
+
| `persistence` | `PersistenceConfig<TData>` | no | in-memory | Session storage: `adapter`, `autoSave`, `userId`, plus `schemaVersion` / `migrateSession` for upgrading state written by older deployments. Omit for `MemoryAdapter`. |
|
|
75
75
|
| `knowledgeBase` | `Record<string, unknown>` | no | — | Arbitrary JSON inlined into the prompt as background knowledge. |
|
|
76
76
|
| `flowSwitchMargin` | `number` | no | `15` | Margin (0–100) the best alternative flow must exceed the current flow's score by before switching. Higher values make the agent stickier. |
|
|
77
77
|
| `maxAutoStepsPerTurn` | `number` | no | `10` | Cap on consecutive `auto: true` steps per turn. Throws `FlowConfigurationError` when exceeded. |
|