@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/types/flow.ts
CHANGED
|
@@ -59,21 +59,32 @@ export type StoppedReason =
|
|
|
59
59
|
* Result that a prepare/finalize hook may return to issue directives.
|
|
60
60
|
* All fields are optional — returning void is also valid.
|
|
61
61
|
*/
|
|
62
|
-
export interface PrepareResult {
|
|
62
|
+
export interface PrepareResult<TContext = unknown, TData = unknown> {
|
|
63
63
|
/** Partial data to merge into session.data */
|
|
64
|
-
dataUpdate?:
|
|
64
|
+
dataUpdate?: Partial<TData>;
|
|
65
65
|
/** Partial context to merge into the agent context */
|
|
66
|
-
contextUpdate?:
|
|
66
|
+
contextUpdate?: Partial<TContext>;
|
|
67
67
|
/** If true, stop the auto-step chain and end the turn with `reply` */
|
|
68
68
|
halt?: boolean;
|
|
69
69
|
/** Verbatim reply to send when halting */
|
|
70
70
|
reply?: string;
|
|
71
71
|
/** Jump to a step within the current flow */
|
|
72
|
-
goToStep?: string;
|
|
72
|
+
goToStep?: string | { step: string; flow?: string; data?: Partial<TData>; reason?: string; };
|
|
73
73
|
/** Jump to another flow */
|
|
74
|
-
goTo?: string;
|
|
74
|
+
goTo?: string | { flow?: string; step?: string; data?: Partial<TData>; reason?: string; carry?: 'preserve' | 'reset'; };
|
|
75
75
|
/** Mark the current flow as complete */
|
|
76
|
-
complete?:
|
|
76
|
+
complete?: true | { next?: Directive<unknown, unknown>; reason?: string; };
|
|
77
|
+
/**
|
|
78
|
+
* Prompt lines appended to this turn's generation prompt. Pre-LLM
|
|
79
|
+
* augmentation: honored by auto steps' prepare; other step kinds drop it
|
|
80
|
+
* with a loud warning rather than silently ignoring it.
|
|
81
|
+
*/
|
|
82
|
+
appendPrompt?: string[];
|
|
83
|
+
/**
|
|
84
|
+
* Extra tools made available for this turn only. Same auto-step-only
|
|
85
|
+
* honoring as {@link appendPrompt}.
|
|
86
|
+
*/
|
|
87
|
+
injectTools?: Tool[];
|
|
77
88
|
}
|
|
78
89
|
|
|
79
90
|
|
|
@@ -416,46 +427,32 @@ export interface FlowOptions<TContext = unknown, TData = unknown> {
|
|
|
416
427
|
}
|
|
417
428
|
|
|
418
429
|
/**
|
|
419
|
-
* Step lifecycle hooks for managing step-specific behavior.
|
|
430
|
+
* Step lifecycle hooks for managing step-specific behavior. Desugared onto the
|
|
431
|
+
* equivalent top-level `prepare` / `finalize` fields by the Step constructor —
|
|
432
|
+
* both spellings run through the same lifecycle machinery, and declaring both
|
|
433
|
+
* runs them in sequence with their returns merged (Algorithm 4).
|
|
420
434
|
*
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
*
|
|
435
|
+
* `prepare` runs before the AI responds; `finalize` runs after the turn's
|
|
436
|
+
* generation completes (before persistence). Both may return a Directive to
|
|
437
|
+
* write state or redirect flow.
|
|
424
438
|
*/
|
|
425
439
|
export interface StepLifecycleHooks<TContext = unknown, TData = unknown> {
|
|
426
440
|
/**
|
|
427
|
-
* Called
|
|
428
|
-
*
|
|
429
|
-
* Pre-LLM fields (`appendPrompt`, `injectTools`, `halt`) are honored here.
|
|
430
|
-
*/
|
|
431
|
-
onEnter?: (
|
|
432
|
-
ctx: HookContext<TContext, TData>
|
|
433
|
-
) => void | Directive<TContext, TData> | Promise<void | Directive<TContext, TData>>;
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Called when the step is exited. Informational only — cannot influence flow control.
|
|
437
|
-
* Receives the reason the step was exited.
|
|
438
|
-
*/
|
|
439
|
-
onExit?: (
|
|
440
|
-
ctx: HookContext<TContext, TData>,
|
|
441
|
-
reason: ExitReason
|
|
442
|
-
) => void | Promise<void>;
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* Called pre-LLM. May return a Directive to augment the prompt,
|
|
446
|
-
* inject tools, or halt the LLM call.
|
|
447
|
-
* Pre-LLM fields (`appendPrompt`, `injectTools`, `halt`) are honored here.
|
|
441
|
+
* Called pre-LLM (alongside any top-level `prepare`). May return a Directive
|
|
442
|
+
* to write state or redirect flow.
|
|
448
443
|
*/
|
|
449
444
|
prepare?: (
|
|
450
|
-
|
|
445
|
+
context: TContext,
|
|
446
|
+
data?: Partial<TData>
|
|
451
447
|
) => void | Directive<TContext, TData> | Promise<void | Directive<TContext, TData>>;
|
|
452
448
|
|
|
453
449
|
/**
|
|
454
|
-
* Called
|
|
455
|
-
*
|
|
450
|
+
* Called after generation (alongside any top-level `finalize`, before
|
|
451
|
+
* persistence). May return a Directive to redirect flow or write state.
|
|
456
452
|
*/
|
|
457
453
|
finalize?: (
|
|
458
|
-
|
|
454
|
+
context: TContext,
|
|
455
|
+
data?: Partial<TData>
|
|
459
456
|
) => void | Directive<TContext, TData> | Promise<void | Directive<TContext, TData>>;
|
|
460
457
|
}
|
|
461
458
|
|
|
@@ -475,12 +472,12 @@ export interface StepOptions<TContext = unknown, TData = unknown> {
|
|
|
475
472
|
prepare?:
|
|
476
473
|
| string
|
|
477
474
|
| Tool<TContext, TData>
|
|
478
|
-
| ((context: TContext, data?: Partial<TData>) => void | PrepareResult | Promise<void | PrepareResult
|
|
475
|
+
| ((context: TContext, data?: Partial<TData>) => void | PrepareResult<TContext, TData> | Promise<void | PrepareResult<TContext, TData>>);
|
|
479
476
|
/** Programmatic function or tool to run after AI responds */
|
|
480
477
|
finalize?:
|
|
481
478
|
| string
|
|
482
479
|
| Tool<TContext, TData>
|
|
483
|
-
| ((context: TContext, data?: Partial<TData>) => void | PrepareResult | Promise<void | PrepareResult
|
|
480
|
+
| ((context: TContext, data?: Partial<TData>) => void | PrepareResult<TContext, TData> | Promise<void | PrepareResult<TContext, TData>>);
|
|
484
481
|
|
|
485
482
|
/**
|
|
486
483
|
* Fields to collect from the conversation in this step
|
|
@@ -569,8 +566,9 @@ export interface StepOptions<TContext = unknown, TData = unknown> {
|
|
|
569
566
|
branches?: BranchMap<TContext, TData>;
|
|
570
567
|
|
|
571
568
|
/**
|
|
572
|
-
* Step lifecycle hooks
|
|
573
|
-
*
|
|
569
|
+
* Step lifecycle hooks — an alternative spelling of the top-level
|
|
570
|
+
* `prepare` / `finalize` fields. Both spellings run when declared together;
|
|
571
|
+
* returns are merged via Algorithm 4.
|
|
574
572
|
*/
|
|
575
573
|
hooks?: StepLifecycleHooks<TContext, TData>;
|
|
576
574
|
}
|
package/src/types/index.ts
CHANGED
package/src/types/tool.ts
CHANGED
|
@@ -65,6 +65,8 @@ export interface ToolResult<
|
|
|
65
65
|
meta?: Record<string, unknown>;
|
|
66
66
|
/** Emit a directive declaratively (alternative to ctx.dispatch). */
|
|
67
67
|
directive?: Directive<TContext, TData>;
|
|
68
|
+
/** Emit multiple directives at once (alternative to repeated dispatch calls). */
|
|
69
|
+
directives?: Directive<TContext, TData>[];
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
/**
|
package/src/utils/index.ts
CHANGED
|
@@ -15,7 +15,10 @@ export {
|
|
|
15
15
|
export {
|
|
16
16
|
createSession,
|
|
17
17
|
createSessionId,
|
|
18
|
+
boundConversationHistory,
|
|
19
|
+
DEFAULT_MAX_HISTORY_MESSAGES,
|
|
18
20
|
createPersistedState,
|
|
21
|
+
restoreSession,
|
|
19
22
|
enterFlow,
|
|
20
23
|
enterStep,
|
|
21
24
|
completeCurrentFlow,
|
|
@@ -55,8 +58,24 @@ export {
|
|
|
55
58
|
export { LoggerLevel, logger } from "./logger";
|
|
56
59
|
|
|
57
60
|
// Retry utilities
|
|
58
|
-
export type {
|
|
59
|
-
|
|
61
|
+
export type {
|
|
62
|
+
RetryOptions,
|
|
63
|
+
StreamRetryOptions,
|
|
64
|
+
RetryConfig,
|
|
65
|
+
BackupFallbackBase,
|
|
66
|
+
BackupFallbackOptions,
|
|
67
|
+
StreamBackupFallbackOptions,
|
|
68
|
+
} from "./retry";
|
|
69
|
+
export {
|
|
70
|
+
retry,
|
|
71
|
+
withTimeoutAndRetry,
|
|
72
|
+
withStreamRetry,
|
|
73
|
+
resolveRetryConfig,
|
|
74
|
+
combineAbortSignals,
|
|
75
|
+
withBackupFallback,
|
|
76
|
+
streamWithBackupFallback,
|
|
77
|
+
backupFallbackLogging,
|
|
78
|
+
} from "./retry";
|
|
60
79
|
|
|
61
80
|
// Completion helpers
|
|
62
81
|
export { effectiveMessageText, assertUsableCompletion } from "./completion";
|
|
@@ -73,4 +92,4 @@ export {
|
|
|
73
92
|
export { parseJSONResponse, tryParseJSONResponse } from "./json";
|
|
74
93
|
|
|
75
94
|
// Serialization utilities
|
|
76
|
-
export { serializeToolResult } from "./serialize";
|
|
95
|
+
export { serializeToolResult, isToolResultLike, extractResultDirectives } from "./serialize";
|
package/src/utils/retry.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Retry utility with exponential backoff
|
|
3
3
|
*/
|
|
4
4
|
import {logger} from './logger'
|
|
5
|
+
import { getErrorMessage } from '../providers/errorClassification'
|
|
5
6
|
|
|
6
7
|
export interface RetryOptions<T> {
|
|
7
8
|
operation: () => Promise<T>;
|
|
@@ -9,6 +10,13 @@ export interface RetryOptions<T> {
|
|
|
9
10
|
delay: (attempt: number) => number;
|
|
10
11
|
onRetry?: (attempt: number, error: unknown) => void;
|
|
11
12
|
onFailure?: (error: unknown) => boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Predicate deciding whether an error may be retried at all. When it returns
|
|
15
|
+
* `false` the error is rethrown immediately — no backoff, no further attempts.
|
|
16
|
+
* Providers pass a classifier here so deterministic failures (401, 400,
|
|
17
|
+
* caller aborts) fail fast instead of burning the full retry budget.
|
|
18
|
+
*/
|
|
19
|
+
isRetriable?: (error: unknown) => boolean;
|
|
12
20
|
}
|
|
13
21
|
|
|
14
22
|
/** Provider timeout (ms) + retry count, after defaults are applied. */
|
|
@@ -40,7 +48,7 @@ export function resolveRetryConfig(input?: {
|
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
export async function retry<T>(options: RetryOptions<T>): Promise<T> {
|
|
43
|
-
const { operation, maxRetries, delay, onRetry, onFailure } = options;
|
|
51
|
+
const { operation, maxRetries, delay, onRetry, onFailure, isRetriable } = options;
|
|
44
52
|
|
|
45
53
|
let lastError: unknown;
|
|
46
54
|
|
|
@@ -50,6 +58,12 @@ export async function retry<T>(options: RetryOptions<T>): Promise<T> {
|
|
|
50
58
|
} catch (error: unknown) {
|
|
51
59
|
lastError = error;
|
|
52
60
|
|
|
61
|
+
// Unretriable errors (auth, invalid request, caller aborts) fail fast —
|
|
62
|
+
// retrying a deterministic failure only adds latency to the same outcome.
|
|
63
|
+
if (isRetriable && !isRetriable(error)) {
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
|
|
53
67
|
if (attempt === maxRetries) {
|
|
54
68
|
const shouldRethrow = onFailure ? onFailure(lastError) : true;
|
|
55
69
|
if (shouldRethrow) {
|
|
@@ -111,7 +125,8 @@ export const withTimeoutAndRetry = async <T>(
|
|
|
111
125
|
operation: (signal: AbortSignal) => Promise<T>,
|
|
112
126
|
timeoutMs: number = 60000,
|
|
113
127
|
maxRetries: number = 3,
|
|
114
|
-
operationName: string = "AI operation"
|
|
128
|
+
operationName: string = "AI operation",
|
|
129
|
+
isRetriable?: (error: unknown) => boolean
|
|
115
130
|
): Promise<T> => {
|
|
116
131
|
const createTimeoutOperation = () => async (): Promise<T> => {
|
|
117
132
|
const controller = new AbortController();
|
|
@@ -144,6 +159,7 @@ export const withTimeoutAndRetry = async <T>(
|
|
|
144
159
|
operation: createTimeoutOperation(),
|
|
145
160
|
maxRetries,
|
|
146
161
|
delay: defaultBackoff,
|
|
162
|
+
isRetriable,
|
|
147
163
|
onRetry: (attempt: number, error: unknown) => {
|
|
148
164
|
const message = error instanceof Error ? error.message : String(error);
|
|
149
165
|
logger.error(
|
|
@@ -170,6 +186,12 @@ export interface StreamRetryOptions {
|
|
|
170
186
|
delay?: (attempt: number) => number;
|
|
171
187
|
/** Label used in retry logs. */
|
|
172
188
|
operationName?: string;
|
|
189
|
+
/**
|
|
190
|
+
* Predicate deciding whether a pre-first-chunk failure may be retried.
|
|
191
|
+
* When it returns `false` the error propagates immediately. Post-first-chunk
|
|
192
|
+
* failures are never retried regardless (the stream is already committed).
|
|
193
|
+
*/
|
|
194
|
+
isRetriable?: (error: unknown) => boolean;
|
|
173
195
|
/**
|
|
174
196
|
* Max ms to wait for the *first* chunk (time-to-first-token) before treating
|
|
175
197
|
* the attempt as failed. Guards a provider that opens a stream and then
|
|
@@ -221,6 +243,7 @@ export async function* withStreamRetry<T>(
|
|
|
221
243
|
delay = defaultBackoff,
|
|
222
244
|
operationName = "AI stream",
|
|
223
245
|
firstChunkTimeoutMs,
|
|
246
|
+
isRetriable,
|
|
224
247
|
} = options;
|
|
225
248
|
|
|
226
249
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
@@ -246,6 +269,11 @@ export async function* withStreamRetry<T>(
|
|
|
246
269
|
yield result.value;
|
|
247
270
|
}
|
|
248
271
|
} catch (error: unknown) {
|
|
272
|
+
// Unretriable errors (auth, invalid request, caller aborts) propagate
|
|
273
|
+
// immediately — no backoff for a deterministic failure.
|
|
274
|
+
if (isRetriable && !isRetriable(error)) {
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
249
277
|
// Can't retry once deltas are out, and don't retry past the budget.
|
|
250
278
|
if (yielded || attempt === maxRetries) {
|
|
251
279
|
throw error;
|
|
@@ -272,3 +300,224 @@ export async function* withStreamRetry<T>(
|
|
|
272
300
|
}
|
|
273
301
|
}
|
|
274
302
|
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Inputs shared by {@link withBackupFallback} and {@link streamWithBackupFallback}.
|
|
306
|
+
*
|
|
307
|
+
* The walkers own only control flow: which model runs next and when the walk
|
|
308
|
+
* gives up. Logging and terminal-error wrapping stay with the caller (via the
|
|
309
|
+
* callbacks and a try/catch around the call), so each provider keeps its exact
|
|
310
|
+
* log prefixes, classification options and error normalization.
|
|
311
|
+
*/
|
|
312
|
+
export interface BackupFallbackBase {
|
|
313
|
+
/**
|
|
314
|
+
* Models to try in order: `[primaryModel, ...backupModels]`. The first
|
|
315
|
+
* successful attempt wins; every failure advances (or stops) the walk.
|
|
316
|
+
*/
|
|
317
|
+
models: string[];
|
|
318
|
+
/**
|
|
319
|
+
* Predicate deciding whether a failure may fall through to the remaining
|
|
320
|
+
* models. Returning `false` stops the walk and rethrows the error — a
|
|
321
|
+
* deterministic failure (auth, invalid request) would hit identically on
|
|
322
|
+
* every backup. Must be a pure classifier: it may be evaluated more than
|
|
323
|
+
* once per error (the failure callback recomputes it to annotate logs).
|
|
324
|
+
*/
|
|
325
|
+
shouldTryBackup: (error: unknown) => boolean;
|
|
326
|
+
/**
|
|
327
|
+
* Called once per failed model, before the backup decision is applied.
|
|
328
|
+
* `attemptNo` is the 1-based position in `models` (`1` = primary) and
|
|
329
|
+
* `total` is `models.length`, so callers can distinguish the primary
|
|
330
|
+
* failure and tell whether any model remains after this one
|
|
331
|
+
* (`attemptNo < total`).
|
|
332
|
+
*/
|
|
333
|
+
onModelFailed?: (
|
|
334
|
+
model: string,
|
|
335
|
+
error: unknown,
|
|
336
|
+
attemptNo: number,
|
|
337
|
+
total: number
|
|
338
|
+
) => void;
|
|
339
|
+
/**
|
|
340
|
+
* Called before each *backup* attempt (position > 0). `backupNo` and
|
|
341
|
+
* `backupTotal` count backups only (`1..models.length - 1`), matching the
|
|
342
|
+
* historical "Trying backup model 1/2: …" log shape.
|
|
343
|
+
*/
|
|
344
|
+
onBackupStart?: (
|
|
345
|
+
model: string,
|
|
346
|
+
backupNo: number,
|
|
347
|
+
backupTotal: number
|
|
348
|
+
) => void;
|
|
349
|
+
/** Called when a backup succeeds, just before its result is returned/yielded. */
|
|
350
|
+
onBackupSucceeded?: (model: string) => void;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export interface BackupFallbackOptions<T> extends BackupFallbackBase {
|
|
354
|
+
/** Run one model. A rejection marks the model failed; the resolved value wins. */
|
|
355
|
+
attempt: (model: string) => Promise<T>;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export interface StreamBackupFallbackOptions<T> extends BackupFallbackBase {
|
|
359
|
+
/** Produce one model's chunk stream; consumed lazily via `yield*`. */
|
|
360
|
+
attempt: (model: string) => AsyncGenerator<T>;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Try `models[0]` (the primary); on failure, if `shouldTryBackup` allows,
|
|
365
|
+
* walk the remaining backup models in order. Resolves with the first
|
|
366
|
+
* successful result; otherwise rethrows the last error seen. Errors are
|
|
367
|
+
* rethrown untouched — wrap them at the call site.
|
|
368
|
+
*
|
|
369
|
+
* Streaming twin: {@link streamWithBackupFallback}.
|
|
370
|
+
*/
|
|
371
|
+
export async function withBackupFallback<T>(
|
|
372
|
+
opts: BackupFallbackOptions<T>
|
|
373
|
+
): Promise<T> {
|
|
374
|
+
const {
|
|
375
|
+
models,
|
|
376
|
+
attempt,
|
|
377
|
+
shouldTryBackup,
|
|
378
|
+
onModelFailed,
|
|
379
|
+
onBackupStart,
|
|
380
|
+
onBackupSucceeded,
|
|
381
|
+
} = opts;
|
|
382
|
+
|
|
383
|
+
if (models.length === 0) {
|
|
384
|
+
throw new Error(
|
|
385
|
+
"withBackupFallback: `models` must include a primary model"
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
let lastError: unknown;
|
|
390
|
+
for (let i = 0; i < models.length; i++) {
|
|
391
|
+
const model = models[i];
|
|
392
|
+
if (i > 0) onBackupStart?.(model, i, models.length - 1);
|
|
393
|
+
try {
|
|
394
|
+
const result = await attempt(model);
|
|
395
|
+
if (i > 0) onBackupSucceeded?.(model);
|
|
396
|
+
return result;
|
|
397
|
+
} catch (error: unknown) {
|
|
398
|
+
lastError = error;
|
|
399
|
+
onModelFailed?.(model, error, i + 1, models.length);
|
|
400
|
+
// A failure that doesn't qualify for backup ends the walk — the
|
|
401
|
+
// remaining models would hit the same deterministic error. Falling
|
|
402
|
+
// through to the throw covers both this early exit and exhaustion.
|
|
403
|
+
if (!shouldTryBackup(error)) break;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
throw lastError;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Streaming twin of {@link withBackupFallback}: same walk, consuming each
|
|
412
|
+
* model's chunk stream lazily via `yield*` so chunks flow through unchanged.
|
|
413
|
+
* A stream that errors before completion counts as that model's failure and
|
|
414
|
+
* the walk proceeds exactly as in the non-streaming case.
|
|
415
|
+
*/
|
|
416
|
+
export async function* streamWithBackupFallback<T>(
|
|
417
|
+
opts: StreamBackupFallbackOptions<T>
|
|
418
|
+
): AsyncGenerator<T> {
|
|
419
|
+
const {
|
|
420
|
+
models,
|
|
421
|
+
attempt,
|
|
422
|
+
shouldTryBackup,
|
|
423
|
+
onModelFailed,
|
|
424
|
+
onBackupStart,
|
|
425
|
+
onBackupSucceeded,
|
|
426
|
+
} = opts;
|
|
427
|
+
|
|
428
|
+
if (models.length === 0) {
|
|
429
|
+
throw new Error(
|
|
430
|
+
"streamWithBackupFallback: `models` must include a primary model"
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
let lastError: unknown;
|
|
435
|
+
for (let i = 0; i < models.length; i++) {
|
|
436
|
+
const model = models[i];
|
|
437
|
+
if (i > 0) onBackupStart?.(model, i, models.length - 1);
|
|
438
|
+
try {
|
|
439
|
+
yield* attempt(model);
|
|
440
|
+
if (i > 0) onBackupSucceeded?.(model);
|
|
441
|
+
return;
|
|
442
|
+
} catch (error: unknown) {
|
|
443
|
+
lastError = error;
|
|
444
|
+
onModelFailed?.(model, error, i + 1, models.length);
|
|
445
|
+
if (!shouldTryBackup(error)) break;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
throw lastError;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Standard logging observer for the backup-fallback walkers. Holds the
|
|
454
|
+
* per-walk state (primary error message, whether backups were tried) that the
|
|
455
|
+
* terminal "All models failed" log needs, so each provider call site is just
|
|
456
|
+
* `attempt` + `shouldTryBackup` + its own terminal error wrap.
|
|
457
|
+
*
|
|
458
|
+
* ```ts
|
|
459
|
+
* const observer = backupFallbackLogging("[OPENAI]", shouldUseBackupModel);
|
|
460
|
+
* try {
|
|
461
|
+
* return await withBackupFallback({ models, attempt, shouldTryBackup, ...observer.callbacks });
|
|
462
|
+
* } catch (error) {
|
|
463
|
+
* observer.logExhausted(error);
|
|
464
|
+
* throw wrap(error);
|
|
465
|
+
* }
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
468
|
+
export function backupFallbackLogging(
|
|
469
|
+
label: string,
|
|
470
|
+
shouldTryBackup: (error: unknown) => boolean,
|
|
471
|
+
options?: { streaming?: boolean }
|
|
472
|
+
): {
|
|
473
|
+
callbacks: Pick<
|
|
474
|
+
BackupFallbackBase,
|
|
475
|
+
"onModelFailed" | "onBackupStart" | "onBackupSucceeded"
|
|
476
|
+
>;
|
|
477
|
+
logExhausted: (lastError: unknown) => void;
|
|
478
|
+
} {
|
|
479
|
+
let primaryErrMsg = "";
|
|
480
|
+
// Records that the primary failed but qualified for backups — the only path
|
|
481
|
+
// that reaches the "All models failed" terminal log; an ineligible primary
|
|
482
|
+
// rethrows directly.
|
|
483
|
+
let tryingBackups = false;
|
|
484
|
+
const streamSuffix = options?.streaming ? " for streaming" : "";
|
|
485
|
+
|
|
486
|
+
return {
|
|
487
|
+
callbacks: {
|
|
488
|
+
onModelFailed: (model, error, attemptNo, total) => {
|
|
489
|
+
const errMsg = getErrorMessage(error);
|
|
490
|
+
if (attemptNo === 1) {
|
|
491
|
+
primaryErrMsg = errMsg;
|
|
492
|
+
logger.warn(`${label} Primary model ${model} failed: ${errMsg}`);
|
|
493
|
+
if (shouldTryBackup(error)) {
|
|
494
|
+
tryingBackups = true;
|
|
495
|
+
logger.debug(`${label} Trying backup models${streamSuffix}`);
|
|
496
|
+
}
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
logger.warn(`${label} Backup model ${model} failed: ${errMsg}`);
|
|
500
|
+
if (!shouldTryBackup(error) && attemptNo < total) {
|
|
501
|
+
logger.debug(
|
|
502
|
+
`${label} Backup model error doesn't qualify for further attempts`
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
onBackupStart: (model, backupNo, backupTotal) => {
|
|
507
|
+
logger.debug(
|
|
508
|
+
`${label} Trying backup model ${backupNo}/${backupTotal}: ${model}`
|
|
509
|
+
);
|
|
510
|
+
},
|
|
511
|
+
onBackupSucceeded: (model) => {
|
|
512
|
+
logger.debug(`${label} Backup model ${model} succeeded`);
|
|
513
|
+
},
|
|
514
|
+
},
|
|
515
|
+
logExhausted: (lastError) => {
|
|
516
|
+
if (tryingBackups) {
|
|
517
|
+
logger.error(
|
|
518
|
+
`${label} All models failed. Primary: ${primaryErrMsg}, Last backup: ${getErrorMessage(lastError)}`
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
};
|
|
523
|
+
}
|
package/src/utils/serialize.ts
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
2
2
|
* Serialization utilities for tool results and other data
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import type { Directive } from "../types/flow";
|
|
6
|
+
import type { ToolResult } from "../types/tool";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Decide whether a tool handler's return value is a ToolResult-shaped object.
|
|
10
|
+
* Only SEMANTIC markers identify one — bare `{data}` / `{error}` shapes are
|
|
11
|
+
* indistinguishable from ordinary business payloads (e.g. upstream API
|
|
12
|
+
* envelopes) and must be wrapped as raw results instead. Shared by the
|
|
13
|
+
* sequential (ToolManager) and concurrent (StreamingToolExecutor) paths so the
|
|
14
|
+
* definition of "is a ToolResult" can't drift between them.
|
|
15
|
+
*/
|
|
16
|
+
export function isToolResultLike(result: unknown): result is ToolResult {
|
|
17
|
+
return (
|
|
18
|
+
result !== null &&
|
|
19
|
+
typeof result === "object" &&
|
|
20
|
+
(("success" in result &&
|
|
21
|
+
typeof (result as Record<string, unknown>).success === "boolean") ||
|
|
22
|
+
"directive" in result ||
|
|
23
|
+
"directives" in result ||
|
|
24
|
+
"dataUpdate" in result ||
|
|
25
|
+
"contextUpdate" in result)
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Collect a ToolResult's directives — `{directive}` (singular shorthand) and
|
|
31
|
+
* `{directives}` both count. Singular merges FIRST so plural entries win reply
|
|
32
|
+
* ties (last-wins merge), identical on both executor paths.
|
|
33
|
+
*/
|
|
34
|
+
export function extractResultDirectives<TContext = unknown, TData = unknown>(
|
|
35
|
+
result: ToolResult<unknown, TContext, TData>
|
|
36
|
+
): Directive<TContext, TData>[] {
|
|
37
|
+
const directives: Directive<TContext, TData>[] = [];
|
|
38
|
+
if (result.directive) directives.push(result.directive);
|
|
39
|
+
if (Array.isArray(result.directives)) directives.push(...result.directives);
|
|
40
|
+
return directives;
|
|
41
|
+
}
|
|
42
|
+
|
|
5
43
|
/**
|
|
6
44
|
* Serialize a tool execution result into a string suitable for conversation history.
|
|
7
45
|
*
|
package/src/utils/session.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { cloneDeep } from "./clone";
|
|
2
2
|
import type { SessionState } from "../types/session";
|
|
3
|
+
import type { History } from "../types/history";
|
|
3
4
|
import type { Directive } from "../types/flow";
|
|
4
5
|
import type { CollectedStateData } from "../types/persistence";
|
|
5
6
|
import { logger } from "./logger";
|
|
@@ -68,8 +69,44 @@ export function createSession<TData = Record<string, unknown>>(
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
/**
|
|
71
|
-
*
|
|
72
|
+
* Default conversation-history bound applied when the agent configures neither
|
|
73
|
+
* `maxHistoryMessages` nor compaction: generous enough to never bite a real
|
|
74
|
+
* thread, finite so a long-running chat()/stream() session can't grow until
|
|
75
|
+
* the provider context limit bricks it.
|
|
72
76
|
*/
|
|
77
|
+
export const DEFAULT_MAX_HISTORY_MESSAGES = 400;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Bound a conversation history to its most recent `max` entries. The left
|
|
81
|
+
* edge never opens on an orphaned role:'tool' message (whose assistant
|
|
82
|
+
* tool_calls parent was cut) — providers reject such histories. Returns the
|
|
83
|
+
* SAME array reference when no truncation is needed.
|
|
84
|
+
*/
|
|
85
|
+
export function boundConversationHistory(
|
|
86
|
+
history: History,
|
|
87
|
+
max: number
|
|
88
|
+
): History {
|
|
89
|
+
if (!history || history.length <= max || max <= 0) return history;
|
|
90
|
+
let start = history.length - max;
|
|
91
|
+
while (start < history.length && (history[start] as { role?: string }).role === "tool") {
|
|
92
|
+
start++;
|
|
93
|
+
}
|
|
94
|
+
return history.slice(start);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Restore a session from a persisted blob (the named inverse of
|
|
99
|
+
* `createPersistedState`). Restores data + flowHistory verbatim — including a
|
|
100
|
+
* completed-flow blob, whose collected state MUST survive the round trip.
|
|
101
|
+
* Prefer this over the ambiguous `createSession(state)` overload.
|
|
102
|
+
*/
|
|
103
|
+
export function restoreSession<TData = Record<string, unknown>>(
|
|
104
|
+
state: Partial<SessionState<TData>>
|
|
105
|
+
): SessionState<TData> {
|
|
106
|
+
return createSession<TData>(state);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Helper to create a new session ID */
|
|
73
110
|
export function createSessionId(): string {
|
|
74
111
|
return `session_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
75
112
|
}
|
|
@@ -361,9 +398,9 @@ export function sessionDataToStep<TData = Record<string, unknown>>(
|
|
|
361
398
|
* `appendPrompt`, `injectTools`, and `halt` are transient (one-turn lifetime)
|
|
362
399
|
* and must not be serialized. This is a belt-and-suspenders safety net —
|
|
363
400
|
* `Agent.dispatch` already strips before setting `pendingDirective`, and the
|
|
364
|
-
*
|
|
365
|
-
* ensures the persistence layer never writes these fields regardless
|
|
366
|
-
* upstream path.
|
|
401
|
+
* signal pipeline strips pre-LLM-only fields from post-phase directives. This
|
|
402
|
+
* function ensures the persistence layer never writes these fields regardless
|
|
403
|
+
* of the upstream path.
|
|
367
404
|
*/
|
|
368
405
|
function stripPreDirectiveFields<TData>(
|
|
369
406
|
directive: Directive<unknown, TData>
|