@falai/agent 2.6.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/adapters/MemoryAdapter.js +29 -33
- package/dist/adapters/MemoryAdapter.js.map +1 -1
- package/dist/adapters/PostgreSQLAdapter.d.ts.map +1 -1
- package/dist/adapters/PostgreSQLAdapter.js +16 -11
- package/dist/adapters/PostgreSQLAdapter.js.map +1 -1
- package/dist/adapters/RedisAdapter.d.ts +1 -0
- package/dist/adapters/RedisAdapter.d.ts.map +1 -1
- package/dist/adapters/RedisAdapter.js +75 -25
- package/dist/adapters/RedisAdapter.js.map +1 -1
- package/dist/adapters/SQLiteAdapter.d.ts.map +1 -1
- package/dist/adapters/SQLiteAdapter.js +7 -29
- package/dist/adapters/SQLiteAdapter.js.map +1 -1
- package/dist/adapters/sessionRow.d.ts +22 -0
- package/dist/adapters/sessionRow.d.ts.map +1 -0
- package/dist/adapters/sessionRow.js +48 -0
- package/dist/adapters/sessionRow.js.map +1 -0
- package/dist/cjs/adapters/MemoryAdapter.js +29 -33
- package/dist/cjs/adapters/MemoryAdapter.js.map +1 -1
- package/dist/cjs/adapters/PostgreSQLAdapter.d.ts.map +1 -1
- package/dist/cjs/adapters/PostgreSQLAdapter.js +16 -11
- package/dist/cjs/adapters/PostgreSQLAdapter.js.map +1 -1
- package/dist/cjs/adapters/RedisAdapter.d.ts +1 -0
- package/dist/cjs/adapters/RedisAdapter.d.ts.map +1 -1
- package/dist/cjs/adapters/RedisAdapter.js +75 -25
- 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 +7 -29
- package/dist/cjs/adapters/SQLiteAdapter.js.map +1 -1
- package/dist/cjs/adapters/sessionRow.d.ts +22 -0
- package/dist/cjs/adapters/sessionRow.d.ts.map +1 -0
- package/dist/cjs/adapters/sessionRow.js +52 -0
- package/dist/cjs/adapters/sessionRow.js.map +1 -0
- package/dist/cjs/core/Agent.d.ts +8 -0
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +43 -3
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/AutoChainExecutor.d.ts +8 -18
- package/dist/cjs/core/AutoChainExecutor.d.ts.map +1 -1
- package/dist/cjs/core/AutoChainExecutor.js +23 -26
- package/dist/cjs/core/AutoChainExecutor.js.map +1 -1
- package/dist/cjs/core/CompactionEngine.d.ts +14 -1
- package/dist/cjs/core/CompactionEngine.d.ts.map +1 -1
- package/dist/cjs/core/CompactionEngine.js +30 -6
- package/dist/cjs/core/CompactionEngine.js.map +1 -1
- package/dist/cjs/core/FlowRouter.d.ts.map +1 -1
- package/dist/cjs/core/FlowRouter.js +32 -10
- package/dist/cjs/core/FlowRouter.js.map +1 -1
- package/dist/cjs/core/ResponseGenerationError.d.ts.map +1 -1
- package/dist/cjs/core/ResponseGenerationError.js +3 -5
- package/dist/cjs/core/ResponseGenerationError.js.map +1 -1
- package/dist/cjs/core/ResponseModal.d.ts +29 -0
- package/dist/cjs/core/ResponseModal.d.ts.map +1 -1
- package/dist/cjs/core/ResponseModal.js +141 -18
- package/dist/cjs/core/ResponseModal.js.map +1 -1
- package/dist/cjs/core/ResponsePipeline.d.ts +36 -6
- package/dist/cjs/core/ResponsePipeline.d.ts.map +1 -1
- package/dist/cjs/core/ResponsePipeline.js +208 -73
- package/dist/cjs/core/ResponsePipeline.js.map +1 -1
- package/dist/cjs/core/SessionFinalizer.d.ts.map +1 -1
- package/dist/cjs/core/SessionFinalizer.js +30 -3
- package/dist/cjs/core/SessionFinalizer.js.map +1 -1
- package/dist/cjs/core/SessionManager.d.ts +10 -1
- package/dist/cjs/core/SessionManager.d.ts.map +1 -1
- package/dist/cjs/core/SessionManager.js +43 -16
- package/dist/cjs/core/SessionManager.js.map +1 -1
- package/dist/cjs/core/SignalProcessor.d.ts.map +1 -1
- package/dist/cjs/core/SignalProcessor.js +5 -77
- package/dist/cjs/core/SignalProcessor.js.map +1 -1
- package/dist/cjs/core/Step.d.ts.map +1 -1
- package/dist/cjs/core/Step.js +50 -2
- package/dist/cjs/core/Step.js.map +1 -1
- package/dist/cjs/core/StepLifecycle.d.ts +16 -6
- package/dist/cjs/core/StepLifecycle.d.ts.map +1 -1
- package/dist/cjs/core/StepLifecycle.js +97 -14
- package/dist/cjs/core/StepLifecycle.js.map +1 -1
- package/dist/cjs/core/StreamingToolExecutor.d.ts.map +1 -1
- package/dist/cjs/core/StreamingToolExecutor.js +28 -4
- package/dist/cjs/core/StreamingToolExecutor.js.map +1 -1
- package/dist/cjs/core/ToolLoopExecutor.d.ts +5 -1
- package/dist/cjs/core/ToolLoopExecutor.d.ts.map +1 -1
- package/dist/cjs/core/ToolLoopExecutor.js +156 -64
- package/dist/cjs/core/ToolLoopExecutor.js.map +1 -1
- 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 +40 -17
- package/dist/cjs/core/ToolManager.js.map +1 -1
- package/dist/cjs/core/flow-namespace.d.ts +15 -0
- package/dist/cjs/core/flow-namespace.d.ts.map +1 -1
- package/dist/cjs/core/flow-namespace.js +22 -0
- package/dist/cjs/core/flow-namespace.js.map +1 -1
- package/dist/cjs/index.d.ts +4 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +5 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.d.ts +10 -3
- package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.js +54 -73
- package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
- package/dist/cjs/providers/GeminiProvider.d.ts +9 -3
- package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/cjs/providers/GeminiProvider.js +24 -68
- package/dist/cjs/providers/GeminiProvider.js.map +1 -1
- package/dist/cjs/providers/OpenAICompatibleProvider.d.ts +9 -0
- package/dist/cjs/providers/OpenAICompatibleProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenAICompatibleProvider.js +101 -74
- package/dist/cjs/providers/OpenAICompatibleProvider.js.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.d.ts +1 -1
- package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.js +1 -1
- package/dist/cjs/providers/errorClassification.d.ts +7 -0
- package/dist/cjs/providers/errorClassification.d.ts.map +1 -1
- package/dist/cjs/providers/errorClassification.js +27 -0
- package/dist/cjs/providers/errorClassification.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +35 -0
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/flow.d.ts +49 -32
- package/dist/cjs/types/flow.d.ts.map +1 -1
- package/dist/cjs/types/index.d.ts +1 -1
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/cjs/types/tool.d.ts +2 -0
- package/dist/cjs/types/tool.d.ts.map +1 -1
- package/dist/cjs/types/tool.js.map +1 -1
- package/dist/cjs/utils/index.d.ts +4 -4
- package/dist/cjs/utils/index.d.ts.map +1 -1
- package/dist/cjs/utils/index.js +10 -1
- package/dist/cjs/utils/index.js.map +1 -1
- package/dist/cjs/utils/retry.d.ts +99 -1
- package/dist/cjs/utils/retry.d.ts.map +1 -1
- package/dist/cjs/utils/retry.js +140 -3
- package/dist/cjs/utils/retry.js.map +1 -1
- package/dist/cjs/utils/serialize.d.ts +17 -0
- package/dist/cjs/utils/serialize.d.ts.map +1 -1
- package/dist/cjs/utils/serialize.js +33 -0
- package/dist/cjs/utils/serialize.js.map +1 -1
- package/dist/cjs/utils/session.d.ts +21 -1
- package/dist/cjs/utils/session.d.ts.map +1 -1
- package/dist/cjs/utils/session.js +36 -4
- package/dist/cjs/utils/session.js.map +1 -1
- package/dist/core/Agent.d.ts +8 -0
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +43 -3
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/AutoChainExecutor.d.ts +8 -18
- package/dist/core/AutoChainExecutor.d.ts.map +1 -1
- package/dist/core/AutoChainExecutor.js +23 -26
- package/dist/core/AutoChainExecutor.js.map +1 -1
- package/dist/core/CompactionEngine.d.ts +14 -1
- package/dist/core/CompactionEngine.d.ts.map +1 -1
- package/dist/core/CompactionEngine.js +30 -6
- package/dist/core/CompactionEngine.js.map +1 -1
- package/dist/core/FlowRouter.d.ts.map +1 -1
- package/dist/core/FlowRouter.js +32 -10
- package/dist/core/FlowRouter.js.map +1 -1
- package/dist/core/ResponseGenerationError.d.ts.map +1 -1
- package/dist/core/ResponseGenerationError.js +3 -5
- package/dist/core/ResponseGenerationError.js.map +1 -1
- package/dist/core/ResponseModal.d.ts +29 -0
- package/dist/core/ResponseModal.d.ts.map +1 -1
- package/dist/core/ResponseModal.js +142 -19
- package/dist/core/ResponseModal.js.map +1 -1
- package/dist/core/ResponsePipeline.d.ts +36 -6
- package/dist/core/ResponsePipeline.d.ts.map +1 -1
- package/dist/core/ResponsePipeline.js +208 -73
- package/dist/core/ResponsePipeline.js.map +1 -1
- package/dist/core/SessionFinalizer.d.ts.map +1 -1
- package/dist/core/SessionFinalizer.js +31 -4
- package/dist/core/SessionFinalizer.js.map +1 -1
- package/dist/core/SessionManager.d.ts +10 -1
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +44 -17
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/SignalProcessor.d.ts.map +1 -1
- package/dist/core/SignalProcessor.js +5 -77
- package/dist/core/SignalProcessor.js.map +1 -1
- package/dist/core/Step.d.ts.map +1 -1
- package/dist/core/Step.js +50 -2
- package/dist/core/Step.js.map +1 -1
- package/dist/core/StepLifecycle.d.ts +16 -6
- package/dist/core/StepLifecycle.d.ts.map +1 -1
- package/dist/core/StepLifecycle.js +97 -14
- package/dist/core/StepLifecycle.js.map +1 -1
- package/dist/core/StreamingToolExecutor.d.ts.map +1 -1
- package/dist/core/StreamingToolExecutor.js +28 -4
- package/dist/core/StreamingToolExecutor.js.map +1 -1
- package/dist/core/ToolLoopExecutor.d.ts +5 -1
- package/dist/core/ToolLoopExecutor.d.ts.map +1 -1
- package/dist/core/ToolLoopExecutor.js +156 -64
- package/dist/core/ToolLoopExecutor.js.map +1 -1
- package/dist/core/ToolManager.d.ts +1 -1
- package/dist/core/ToolManager.d.ts.map +1 -1
- package/dist/core/ToolManager.js +41 -18
- package/dist/core/ToolManager.js.map +1 -1
- package/dist/core/flow-namespace.d.ts +15 -0
- package/dist/core/flow-namespace.d.ts.map +1 -1
- package/dist/core/flow-namespace.js +22 -0
- package/dist/core/flow-namespace.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/AnthropicProvider.d.ts +10 -3
- package/dist/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/providers/AnthropicProvider.js +56 -75
- package/dist/providers/AnthropicProvider.js.map +1 -1
- package/dist/providers/GeminiProvider.d.ts +9 -3
- package/dist/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/GeminiProvider.js +26 -70
- package/dist/providers/GeminiProvider.js.map +1 -1
- package/dist/providers/OpenAICompatibleProvider.d.ts +9 -0
- package/dist/providers/OpenAICompatibleProvider.d.ts.map +1 -1
- package/dist/providers/OpenAICompatibleProvider.js +103 -76
- package/dist/providers/OpenAICompatibleProvider.js.map +1 -1
- package/dist/providers/OpenAIProvider.d.ts +1 -1
- package/dist/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/providers/OpenAIProvider.js +1 -1
- package/dist/providers/errorClassification.d.ts +7 -0
- package/dist/providers/errorClassification.d.ts.map +1 -1
- package/dist/providers/errorClassification.js +26 -0
- package/dist/providers/errorClassification.js.map +1 -1
- package/dist/types/agent.d.ts +35 -0
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/flow.d.ts +49 -32
- package/dist/types/flow.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/tool.d.ts +2 -0
- package/dist/types/tool.d.ts.map +1 -1
- package/dist/types/tool.js.map +1 -1
- package/dist/utils/index.d.ts +4 -4
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -3
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/retry.d.ts +99 -1
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +137 -3
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/serialize.d.ts +17 -0
- package/dist/utils/serialize.d.ts.map +1 -1
- package/dist/utils/serialize.js +31 -0
- package/dist/utils/serialize.js.map +1 -1
- package/dist/utils/session.d.ts +21 -1
- package/dist/utils/session.d.ts.map +1 -1
- package/dist/utils/session.js +33 -4
- package/dist/utils/session.js.map +1 -1
- package/docs/concepts/architecture.md +3 -3
- package/docs/concepts/directives.md +1 -1
- package/docs/guides/error-handling.md +46 -45
- package/docs/guides/flow-control.md +8 -1
- package/docs/guides/instructions.md +15 -6
- package/docs/guides/persistence.md +12 -5
- package/docs/guides/streaming.md +10 -0
- package/docs/migration/README.md +4 -0
- package/docs/migration/v2-3-to-v2-4.md +4 -0
- package/docs/migration/v2-6-to-v2-7.md +246 -0
- package/docs/reference/adapters.md +15 -1
- package/docs/reference/create-agent.md +30 -0
- package/docs/reference/directive.md +1 -1
- package/docs/reference/errors.md +29 -31
- package/docs/reference/providers.md +23 -19
- package/docs/reference/step.md +28 -21
- package/docs/reference/tool.md +14 -5
- package/docs/start/02-first-agent.md +8 -4
- package/docs/start/03-collect-data.md +19 -10
- package/examples/01-quickstart.ts +1 -1
- package/examples/02-data-extraction.ts +1 -1
- package/examples/03-tools.ts +1 -1
- package/examples/04-instructions.ts +1 -1
- package/examples/05-branching.ts +1 -1
- package/examples/06-flow-control.ts +3 -3
- package/examples/07-streaming.ts +1 -1
- package/examples/08-persistence.ts +1 -1
- package/examples/09-signals.ts +1 -1
- package/package.json +2 -2
- package/src/adapters/MemoryAdapter.ts +29 -33
- package/src/adapters/PostgreSQLAdapter.ts +23 -18
- package/src/adapters/RedisAdapter.ts +81 -34
- package/src/adapters/SQLiteAdapter.ts +10 -31
- package/src/adapters/sessionRow.ts +57 -0
- package/src/core/Agent.ts +51 -3
- package/src/core/AutoChainExecutor.ts +37 -50
- package/src/core/CompactionEngine.ts +40 -6
- package/src/core/FlowRouter.ts +38 -12
- package/src/core/ResponseGenerationError.ts +3 -6
- package/src/core/ResponseModal.ts +185 -19
- package/src/core/ResponsePipeline.ts +259 -85
- package/src/core/SessionFinalizer.ts +35 -5
- package/src/core/SessionManager.ts +55 -21
- package/src/core/SignalProcessor.ts +5 -85
- package/src/core/Step.ts +79 -2
- package/src/core/StepLifecycle.ts +125 -22
- package/src/core/StreamingToolExecutor.ts +31 -6
- package/src/core/ToolLoopExecutor.ts +201 -75
- package/src/core/ToolManager.ts +45 -18
- package/src/core/flow-namespace.ts +29 -0
- package/src/index.ts +4 -1
- package/src/providers/AnthropicProvider.ts +83 -131
- package/src/providers/GeminiProvider.ts +42 -116
- package/src/providers/OpenAICompatibleProvider.ts +132 -133
- package/src/providers/OpenAIProvider.ts +2 -2
- package/src/providers/errorClassification.ts +34 -0
- package/src/types/agent.ts +34 -0
- package/src/types/flow.ts +37 -39
- package/src/types/index.ts +1 -0
- package/src/types/tool.ts +2 -0
- package/src/utils/index.ts +22 -3
- package/src/utils/retry.ts +251 -2
- package/src/utils/serialize.ts +38 -0
- package/src/utils/session.ts +41 -4
- package/dist/cjs/core/DirectiveBus.d.ts +0 -88
- package/dist/cjs/core/DirectiveBus.d.ts.map +0 -1
- package/dist/cjs/core/DirectiveBus.js +0 -196
- package/dist/cjs/core/DirectiveBus.js.map +0 -1
- package/dist/core/DirectiveBus.d.ts +0 -88
- package/dist/core/DirectiveBus.d.ts.map +0 -1
- package/dist/core/DirectiveBus.js +0 -192
- package/dist/core/DirectiveBus.js.map +0 -1
- package/src/core/DirectiveBus.ts +0 -248
package/src/core/ToolManager.ts
CHANGED
|
@@ -18,7 +18,7 @@ import type {
|
|
|
18
18
|
} from "../types";
|
|
19
19
|
import type { Directive } from "../types/flow";
|
|
20
20
|
import { ToolScope } from "../types";
|
|
21
|
-
import { logger } from "../utils";
|
|
21
|
+
import { logger, isToolResultLike, extractResultDirectives } from "../utils";
|
|
22
22
|
// Type-only import: ToolManager must not create a runtime cycle with Agent
|
|
23
23
|
import type { Agent } from "./Agent";
|
|
24
24
|
import { Flow } from "./Flow";
|
|
@@ -81,7 +81,7 @@ export class ToolManager<TContext = unknown, TData = unknown> {
|
|
|
81
81
|
* Set the transient tool layer for the current turn.
|
|
82
82
|
*
|
|
83
83
|
* Accepts the merged `injectTools` array from pre-LLM emitters
|
|
84
|
-
* (
|
|
84
|
+
* (concatenated in outer-to-inner order).
|
|
85
85
|
* Deduplicates by tool `id` with last-definition-wins.
|
|
86
86
|
*
|
|
87
87
|
* Must be paired with `clearTransientTools()` in a try/finally guard
|
|
@@ -678,6 +678,7 @@ export class ToolManager<TContext = unknown, TData = unknown> {
|
|
|
678
678
|
}): Promise<ToolExecutionResult> {
|
|
679
679
|
const { tool, context, updateContext, updateData, history, data, toolArguments } = params;
|
|
680
680
|
const startTime = Date.now();
|
|
681
|
+
const executionTimeout = 30000; // 30 seconds default timeout
|
|
681
682
|
|
|
682
683
|
try {
|
|
683
684
|
// Validate tool before execution
|
|
@@ -725,24 +726,40 @@ export class ToolManager<TContext = unknown, TData = unknown> {
|
|
|
725
726
|
return gateDenial;
|
|
726
727
|
}
|
|
727
728
|
|
|
728
|
-
// Execute tool with timeout protection
|
|
729
|
-
|
|
729
|
+
// Execute tool with timeout protection. The timer is cleared when the
|
|
730
|
+
// race settles AND unref'd so a pending timer never delays process
|
|
731
|
+
// shutdown; a handler that loses the race keeps running detached (its
|
|
732
|
+
// result is discarded), which we surface in a warn.
|
|
733
|
+
let timeoutTimer: ReturnType<typeof setTimeout> | undefined;
|
|
730
734
|
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
731
|
-
|
|
735
|
+
timeoutTimer = setTimeout(
|
|
736
|
+
() => reject(new Error(`Tool execution timeout after ${executionTimeout}ms`)),
|
|
737
|
+
executionTimeout
|
|
738
|
+
);
|
|
739
|
+
timeoutTimer.unref?.();
|
|
732
740
|
});
|
|
733
741
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
742
|
+
let result: unknown;
|
|
743
|
+
try {
|
|
744
|
+
// Attach a no-op catch to the losing handler promise so a post-timeout
|
|
745
|
+
// rejection can't surface as an unhandled rejection and crash the process.
|
|
746
|
+
const handlerPromise = Promise.resolve(tool.handler(toolContext, toolArguments));
|
|
747
|
+
handlerPromise.catch(() => undefined);
|
|
748
|
+
result = await Promise.race([handlerPromise, timeoutPromise]);
|
|
749
|
+
} finally {
|
|
750
|
+
clearTimeout(timeoutTimer);
|
|
751
|
+
}
|
|
738
752
|
|
|
739
753
|
const executionTime = Date.now() - startTime;
|
|
740
754
|
logger.debug(`[ToolManager] Tool ${tool.id} completed in ${executionTime}ms`);
|
|
741
755
|
|
|
742
|
-
// Handle different result types
|
|
756
|
+
// Handle different result types. Only SEMANTIC markers identify a
|
|
757
|
+
// ToolResult — bare `{data}` / `{error}` shapes are indistinguishable
|
|
758
|
+
// from ordinary business payloads (e.g. upstream API envelopes) and are
|
|
759
|
+
// wrapped as raw results instead.
|
|
743
760
|
let toolResult: ToolResult<unknown, TContext, TData>;
|
|
744
761
|
|
|
745
|
-
if (
|
|
762
|
+
if (isToolResultLike(result)) {
|
|
746
763
|
// It's already a ToolResult-like object
|
|
747
764
|
toolResult = result as ToolResult<unknown, TContext, TData>;
|
|
748
765
|
} else {
|
|
@@ -753,10 +770,9 @@ export class ToolManager<TContext = unknown, TData = unknown> {
|
|
|
753
770
|
};
|
|
754
771
|
}
|
|
755
772
|
|
|
756
|
-
// Collect
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
}
|
|
773
|
+
// Collect directives from the result — singular and plural forms both
|
|
774
|
+
// count, ordered identically to the concurrent executor.
|
|
775
|
+
collectedDirectives.push(...extractResultDirectives(toolResult));
|
|
760
776
|
|
|
761
777
|
// Apply data updates from tool result with validation
|
|
762
778
|
if (toolResult.dataUpdate) {
|
|
@@ -813,6 +829,15 @@ export class ToolManager<TContext = unknown, TData = unknown> {
|
|
|
813
829
|
const executionTime = Date.now() - startTime;
|
|
814
830
|
|
|
815
831
|
logger.error(`[ToolManager] Tool execution error for ${tool.id} after ${executionTime}ms:`, error);
|
|
832
|
+
if (error instanceof Error && error.message.includes("Tool execution timeout")) {
|
|
833
|
+
// The losing handler may still be running with captured callbacks.
|
|
834
|
+
// Its writes are NOT rolled back here — tools should stay idempotent
|
|
835
|
+
// or accept their own cancellation via interruptBehavior/abort.
|
|
836
|
+
logger.warn(
|
|
837
|
+
`[ToolManager] Handler for "${tool.id}" exceeded ${executionTimeout}ms and was abandoned while still running. ` +
|
|
838
|
+
`Its eventual side effects are not cancelled or rolled back.`
|
|
839
|
+
);
|
|
840
|
+
}
|
|
816
841
|
|
|
817
842
|
// Re-throw the error so callers can handle it
|
|
818
843
|
throw error;
|
|
@@ -871,9 +896,11 @@ export class ToolManager<TContext = unknown, TData = unknown> {
|
|
|
871
896
|
return data != null && key in (data as Record<string, unknown>);
|
|
872
897
|
},
|
|
873
898
|
dispatch: (_directive: Directive<TContext, TData>): void => {
|
|
874
|
-
//
|
|
875
|
-
//
|
|
876
|
-
//
|
|
899
|
+
// Handlers never see THIS context — StreamingToolExecutor.executeTool
|
|
900
|
+
// wraps each call in a per-call context whose dispatch attributes the
|
|
901
|
+
// directive to that tool. Reaching here means a gate or future caller
|
|
902
|
+
// dispatched outside a tool execution; surface it instead of dropping.
|
|
903
|
+
logger.warn("[ToolManager] ctx.dispatch called outside tool execution has no turn to attach to; directive dropped.");
|
|
877
904
|
},
|
|
878
905
|
};
|
|
879
906
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { Directive } from "../types/flow";
|
|
9
|
+
import type { SessionState } from "../types/session";
|
|
9
10
|
import { FlowConfigurationError } from "./Step";
|
|
10
11
|
|
|
11
12
|
// ─── Position field metadata ─────────────────────────────────────────────────
|
|
@@ -181,6 +182,32 @@ function validate<TContext, TData>(d: Directive<TContext, TData>): void {
|
|
|
181
182
|
}
|
|
182
183
|
}
|
|
183
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Reduce an array of directives into one via the canonical Algorithm 4 merge
|
|
187
|
+
* (position precedence, reply last-wins, state shallow-merge, halt OR).
|
|
188
|
+
* Empty arrays yield undefined.
|
|
189
|
+
*/
|
|
190
|
+
function mergeAll<TContext, TData>(
|
|
191
|
+
directives: Directive<TContext, TData>[]
|
|
192
|
+
): Directive<TContext, TData> | undefined {
|
|
193
|
+
if (directives.length === 0) return undefined;
|
|
194
|
+
return directives.reduce((a, b) => merge(a, b));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Queue a control directive on the session for the pendingDirective applier at
|
|
199
|
+
* the START of the next turn, merging with anything already queued via the
|
|
200
|
+
* canonical Algorithm 4 merge.
|
|
201
|
+
*/
|
|
202
|
+
function queuePending<TData>(
|
|
203
|
+
session: SessionState<TData>,
|
|
204
|
+
directive: Directive<unknown, TData>
|
|
205
|
+
): void {
|
|
206
|
+
session.pendingDirective = session.pendingDirective
|
|
207
|
+
? merge(session.pendingDirective, directive)
|
|
208
|
+
: directive;
|
|
209
|
+
}
|
|
210
|
+
|
|
184
211
|
/**
|
|
185
212
|
* The `flow` namespace object. Exported as a single const for ergonomic usage:
|
|
186
213
|
*
|
|
@@ -194,5 +221,7 @@ function validate<TContext, TData>(d: Directive<TContext, TData>): void {
|
|
|
194
221
|
export const flow = {
|
|
195
222
|
isDirective,
|
|
196
223
|
merge,
|
|
224
|
+
mergeAll,
|
|
225
|
+
queuePending,
|
|
197
226
|
validate,
|
|
198
227
|
} as const;
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
// Core
|
|
8
8
|
export { Agent } from "./core/Agent";
|
|
9
|
+
export type { RespondParams, StreamOptions, GenerateOptions } from "./core/ResponseModal";
|
|
10
|
+
export { ResponseGenerationError } from "./core/ResponseGenerationError";
|
|
11
|
+
export type { EndedFlow } from "./types/agent";
|
|
9
12
|
export { createAgent } from "./core/createAgent";
|
|
10
13
|
export { Flow } from "./core/Flow";
|
|
11
14
|
export { Step, FlowConfigurationError } from "./core/Step";
|
|
@@ -188,4 +191,4 @@ export type {
|
|
|
188
191
|
ConditionWhen,
|
|
189
192
|
} from "./types";
|
|
190
193
|
export { EventKind, MessageRole } from "./types";
|
|
191
|
-
export { createSession, createSessionId, createPersistedState, enterFlow, enterStep, completeCurrentFlow, isFlowCompletedThisSession, mergeCollected } from "./utils";
|
|
194
|
+
export { restoreSession, createSession, createSessionId, createPersistedState, enterFlow, enterStep, completeCurrentFlow, isFlowCompletedThisSession, mergeCollected } from "./utils";
|
|
@@ -17,11 +17,11 @@ import type {
|
|
|
17
17
|
} from "../types";
|
|
18
18
|
import type { ProviderCapabilities } from "../types/ai";
|
|
19
19
|
import type { HistoryItem } from "../types/history";
|
|
20
|
-
import { withTimeoutAndRetry, withStreamRetry, resolveRetryConfig, logger, assertUsableCompletion, combineAbortSignals } from "../utils";
|
|
20
|
+
import { withTimeoutAndRetry, withStreamRetry, withBackupFallback, streamWithBackupFallback, backupFallbackLogging, resolveRetryConfig, logger, assertUsableCompletion, combineAbortSignals } from "../utils";
|
|
21
21
|
import {
|
|
22
22
|
classifyProviderError,
|
|
23
|
-
getErrorMessage,
|
|
24
23
|
isBackupEligible,
|
|
24
|
+
isRetriableProviderError,
|
|
25
25
|
toProviderError,
|
|
26
26
|
type ErrorClassificationOptions,
|
|
27
27
|
} from "./errorClassification";
|
|
@@ -31,9 +31,9 @@ import {
|
|
|
31
31
|
* Uses types from @anthropic-ai/sdk package
|
|
32
32
|
*/
|
|
33
33
|
export interface AnthropicProviderOptions {
|
|
34
|
-
/** Anthropic API key */
|
|
35
|
-
apiKey
|
|
36
|
-
/** Model to use (required) - e.g., "claude-sonnet-
|
|
34
|
+
/** Anthropic API key. Optional when `client` is injected (tests). */
|
|
35
|
+
apiKey?: string;
|
|
36
|
+
/** Model to use (required) - e.g., "claude-sonnet-5", "claude-opus-5" */
|
|
37
37
|
model: string;
|
|
38
38
|
/** Backup models to try if primary fails (default: []) */
|
|
39
39
|
backupModels?: string[];
|
|
@@ -44,6 +44,12 @@ export interface AnthropicProviderOptions {
|
|
|
44
44
|
timeout?: number;
|
|
45
45
|
retries?: number;
|
|
46
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* Pre-configured SDK client. Overrides the internally-constructed one.
|
|
49
|
+
* Intended for tests injecting scripted transports; production callers
|
|
50
|
+
* should pass `apiKey` instead.
|
|
51
|
+
*/
|
|
52
|
+
client?: Anthropic;
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
/**
|
|
@@ -73,7 +79,10 @@ export class AnthropicProvider implements AiProvider {
|
|
|
73
79
|
supportsNativeJsonSchema: false, // JSON output is enforced via a prompt instruction, not a native schema mode
|
|
74
80
|
supportsStreaming: true,
|
|
75
81
|
supportsStreamingToolCalls: true,
|
|
76
|
-
|
|
82
|
+
// No cache_control is attached to any request yet — reporting true would
|
|
83
|
+
// make consumers believe repeated system prompts are cached when they are
|
|
84
|
+
// not. Flip this when caching is actually implemented.
|
|
85
|
+
supportsPromptCaching: false,
|
|
77
86
|
};
|
|
78
87
|
private client: Anthropic;
|
|
79
88
|
private primaryModel: string;
|
|
@@ -84,17 +93,17 @@ export class AnthropicProvider implements AiProvider {
|
|
|
84
93
|
private retryConfig: { timeout: number; retries: number };
|
|
85
94
|
|
|
86
95
|
constructor(options: AnthropicProviderOptions) {
|
|
87
|
-
const { apiKey, model, backupModels = [], config, retryConfig } = options;
|
|
96
|
+
const { apiKey, model, backupModels = [], config, retryConfig, client } = options;
|
|
88
97
|
|
|
89
|
-
if (!apiKey) {
|
|
98
|
+
if (!client && !apiKey) {
|
|
90
99
|
throw new Error("Anthropic API key is required");
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
if (!model) {
|
|
94
|
-
throw new Error("Model is required. Example: 'claude-sonnet-
|
|
103
|
+
throw new Error("Model is required. Example: 'claude-sonnet-5'");
|
|
95
104
|
}
|
|
96
105
|
|
|
97
|
-
this.client = new Anthropic({
|
|
106
|
+
this.client = client ?? new Anthropic({
|
|
98
107
|
apiKey,
|
|
99
108
|
});
|
|
100
109
|
this.primaryModel = model;
|
|
@@ -188,64 +197,19 @@ export class AnthropicProvider implements AiProvider {
|
|
|
188
197
|
>(
|
|
189
198
|
input: GenerateMessageInput<TContext>
|
|
190
199
|
): Promise<GenerateMessageOutput<TStructured>> {
|
|
191
|
-
|
|
192
|
-
try {
|
|
193
|
-
return await this.generateWithModel<TContext, TStructured>(
|
|
194
|
-
this.primaryModel,
|
|
195
|
-
input
|
|
196
|
-
);
|
|
197
|
-
} catch (primaryError: unknown) {
|
|
198
|
-
const primaryErrMsg = getErrorMessage(primaryError);
|
|
199
|
-
logger.warn(
|
|
200
|
-
`[ANTHROPIC] Primary model ${this.primaryModel} failed: ${primaryErrMsg}`
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
if (!shouldUseBackupModel(primaryError)) {
|
|
204
|
-
throw toProviderError(primaryError, this.name, CLASSIFICATION_OPTIONS);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
logger.debug(`[ANTHROPIC] Trying backup models`);
|
|
208
|
-
|
|
209
|
-
let lastBackupError: unknown = primaryError;
|
|
210
|
-
|
|
211
|
-
for (let i = 0; i < this.backupModels.length; i++) {
|
|
212
|
-
const backupModel = this.backupModels[i];
|
|
213
|
-
logger.debug(
|
|
214
|
-
`[ANTHROPIC] Trying backup model ${i + 1}/${this.backupModels.length
|
|
215
|
-
}: ${backupModel}`
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
try {
|
|
219
|
-
const result = await this.generateWithModel<TContext, TStructured>(
|
|
220
|
-
backupModel,
|
|
221
|
-
input
|
|
222
|
-
);
|
|
223
|
-
logger.debug(`[ANTHROPIC] Backup model ${backupModel} succeeded`);
|
|
224
|
-
return result;
|
|
225
|
-
} catch (backupError: unknown) {
|
|
226
|
-
const backupErrMsg = getErrorMessage(backupError);
|
|
227
|
-
logger.warn(
|
|
228
|
-
`[ANTHROPIC] Backup model ${backupModel} failed: ${backupErrMsg}`
|
|
229
|
-
);
|
|
230
|
-
lastBackupError = backupError;
|
|
231
|
-
|
|
232
|
-
if (
|
|
233
|
-
!shouldUseBackupModel(backupError) &&
|
|
234
|
-
i < this.backupModels.length - 1
|
|
235
|
-
) {
|
|
236
|
-
logger.debug(
|
|
237
|
-
`[ANTHROPIC] Backup model error doesn't qualify for further attempts`
|
|
238
|
-
);
|
|
239
|
-
break;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
200
|
+
const observer = backupFallbackLogging("[ANTHROPIC]", shouldUseBackupModel);
|
|
243
201
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
202
|
+
try {
|
|
203
|
+
return await withBackupFallback({
|
|
204
|
+
models: [this.primaryModel, ...this.backupModels],
|
|
205
|
+
attempt: (model) =>
|
|
206
|
+
this.generateWithModel<TContext, TStructured>(model, input),
|
|
207
|
+
shouldTryBackup: shouldUseBackupModel,
|
|
208
|
+
...observer.callbacks,
|
|
209
|
+
});
|
|
210
|
+
} catch (error: unknown) {
|
|
211
|
+
observer.logExhausted(error);
|
|
212
|
+
throw toProviderError(error, this.name, CLASSIFICATION_OPTIONS);
|
|
249
213
|
}
|
|
250
214
|
}
|
|
251
215
|
|
|
@@ -394,7 +358,8 @@ export class AnthropicProvider implements AiProvider {
|
|
|
394
358
|
operation,
|
|
395
359
|
this.retryConfig.timeout,
|
|
396
360
|
this.retryConfig.retries,
|
|
397
|
-
`Anthropic ${model}
|
|
361
|
+
`Anthropic ${model}`,
|
|
362
|
+
(error) => isRetriableProviderError(error, CLASSIFICATION_OPTIONS)
|
|
398
363
|
) as Promise<GenerateMessageOutput<TStructured>>;
|
|
399
364
|
}
|
|
400
365
|
|
|
@@ -404,64 +369,22 @@ export class AnthropicProvider implements AiProvider {
|
|
|
404
369
|
>(
|
|
405
370
|
input: GenerateMessageInput<TContext>
|
|
406
371
|
): AsyncGenerator<GenerateMessageStreamChunk<TStructured>> {
|
|
407
|
-
|
|
408
|
-
try {
|
|
409
|
-
yield* withStreamRetry(
|
|
410
|
-
(signal) => this.generateStreamWithModel<TContext, TStructured>(this.primaryModel, input, signal),
|
|
411
|
-
{ maxRetries: this.retryConfig.retries, firstChunkTimeoutMs: this.retryConfig.timeout, operationName: `Anthropic ${this.primaryModel} stream` }
|
|
412
|
-
);
|
|
413
|
-
} catch (primaryError: unknown) {
|
|
414
|
-
const primaryErrMsg = getErrorMessage(primaryError);
|
|
415
|
-
logger.warn(
|
|
416
|
-
`[ANTHROPIC] Primary model ${this.primaryModel} failed: ${primaryErrMsg}`
|
|
417
|
-
);
|
|
418
|
-
|
|
419
|
-
if (!shouldUseBackupModel(primaryError)) {
|
|
420
|
-
throw toProviderError(primaryError, this.name, CLASSIFICATION_OPTIONS);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
logger.debug(`[ANTHROPIC] Trying backup models for streaming`);
|
|
424
|
-
|
|
425
|
-
let lastBackupError: unknown = primaryError;
|
|
426
|
-
|
|
427
|
-
for (let i = 0; i < this.backupModels.length; i++) {
|
|
428
|
-
const backupModel = this.backupModels[i];
|
|
429
|
-
logger.debug(
|
|
430
|
-
`[ANTHROPIC] Trying backup model ${i + 1}/${this.backupModels.length
|
|
431
|
-
}: ${backupModel}`
|
|
432
|
-
);
|
|
372
|
+
const observer = backupFallbackLogging("[ANTHROPIC]", shouldUseBackupModel, { streaming: true });
|
|
433
373
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
if (
|
|
449
|
-
!shouldUseBackupModel(backupError) &&
|
|
450
|
-
i < this.backupModels.length - 1
|
|
451
|
-
) {
|
|
452
|
-
logger.debug(
|
|
453
|
-
`[ANTHROPIC] Backup model error doesn't qualify for further attempts`
|
|
454
|
-
);
|
|
455
|
-
break;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
const lastBackupErrMsg = getErrorMessage(lastBackupError);
|
|
461
|
-
logger.error(
|
|
462
|
-
`[ANTHROPIC] All models failed. Primary: ${primaryErrMsg}, Last backup: ${lastBackupErrMsg}`
|
|
463
|
-
);
|
|
464
|
-
throw toProviderError(lastBackupError, this.name, CLASSIFICATION_OPTIONS);
|
|
374
|
+
try {
|
|
375
|
+
yield* streamWithBackupFallback({
|
|
376
|
+
models: [this.primaryModel, ...this.backupModels],
|
|
377
|
+
attempt: (model) =>
|
|
378
|
+
withStreamRetry(
|
|
379
|
+
(signal) => this.generateStreamWithModel<TContext, TStructured>(model, input, signal),
|
|
380
|
+
{ maxRetries: this.retryConfig.retries, firstChunkTimeoutMs: this.retryConfig.timeout, operationName: `Anthropic ${model} stream`, isRetriable: (error) => isRetriableProviderError(error, CLASSIFICATION_OPTIONS) }
|
|
381
|
+
),
|
|
382
|
+
shouldTryBackup: shouldUseBackupModel,
|
|
383
|
+
...observer.callbacks,
|
|
384
|
+
});
|
|
385
|
+
} catch (error: unknown) {
|
|
386
|
+
observer.logExhausted(error);
|
|
387
|
+
throw toProviderError(error, this.name, CLASSIFICATION_OPTIONS);
|
|
465
388
|
}
|
|
466
389
|
}
|
|
467
390
|
|
|
@@ -545,10 +468,13 @@ export class AnthropicProvider implements AiProvider {
|
|
|
545
468
|
let stopReason: string | undefined;
|
|
546
469
|
let inputTokens = 0;
|
|
547
470
|
let outputTokens = 0;
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
471
|
+
// Tool_use blocks arrive empty: content_block_start carries `input: {}`,
|
|
472
|
+
// the real arguments stream in later as input_json_delta partial_json
|
|
473
|
+
// fragments. Accumulate per block index and parse once at stream end.
|
|
474
|
+
const pendingToolBlocks = new Map<
|
|
475
|
+
number,
|
|
476
|
+
{ toolName: string; argumentsBuffer: string }
|
|
477
|
+
>();
|
|
552
478
|
|
|
553
479
|
for await (const chunk of stream) {
|
|
554
480
|
if (chunk.type === "message_start") {
|
|
@@ -556,9 +482,9 @@ export class AnthropicProvider implements AiProvider {
|
|
|
556
482
|
inputTokens = chunk.message.usage.input_tokens;
|
|
557
483
|
} else if (chunk.type === "content_block_start") {
|
|
558
484
|
if (chunk.content_block.type === "tool_use") {
|
|
559
|
-
|
|
485
|
+
pendingToolBlocks.set(chunk.index, {
|
|
560
486
|
toolName: chunk.content_block.name,
|
|
561
|
-
|
|
487
|
+
argumentsBuffer: "",
|
|
562
488
|
});
|
|
563
489
|
}
|
|
564
490
|
} else if (chunk.type === "content_block_delta") {
|
|
@@ -570,6 +496,11 @@ export class AnthropicProvider implements AiProvider {
|
|
|
570
496
|
accumulated,
|
|
571
497
|
done: false,
|
|
572
498
|
} as GenerateMessageStreamChunk<TStructured>;
|
|
499
|
+
} else if (chunk.delta.type === "input_json_delta") {
|
|
500
|
+
const block = pendingToolBlocks.get(chunk.index);
|
|
501
|
+
if (block) {
|
|
502
|
+
block.argumentsBuffer += chunk.delta.partial_json;
|
|
503
|
+
}
|
|
573
504
|
}
|
|
574
505
|
} else if (chunk.type === "message_delta") {
|
|
575
506
|
stopReason = chunk.delta.stop_reason || undefined;
|
|
@@ -577,6 +508,27 @@ export class AnthropicProvider implements AiProvider {
|
|
|
577
508
|
}
|
|
578
509
|
}
|
|
579
510
|
|
|
511
|
+
const toolCalls: Array<{
|
|
512
|
+
toolName: string;
|
|
513
|
+
arguments: Record<string, unknown>;
|
|
514
|
+
}> = [];
|
|
515
|
+
for (const [, block] of [...pendingToolBlocks.entries()].sort(
|
|
516
|
+
([a], [b]) => a - b
|
|
517
|
+
)) {
|
|
518
|
+
let args: Record<string, unknown> = {};
|
|
519
|
+
if (block.argumentsBuffer) {
|
|
520
|
+
try {
|
|
521
|
+
args = JSON.parse(block.argumentsBuffer) as Record<string, unknown>;
|
|
522
|
+
} catch (error) {
|
|
523
|
+
logger.warn(
|
|
524
|
+
"[ANTHROPIC] Failed to parse streamed tool call arguments:",
|
|
525
|
+
error
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
toolCalls.push({ toolName: block.toolName, arguments: args });
|
|
530
|
+
}
|
|
531
|
+
|
|
580
532
|
// Parse JSON response if schema was provided
|
|
581
533
|
let structured: AgentStructuredResponse | undefined;
|
|
582
534
|
if (input.parameters?.jsonSchema && accumulated) {
|