@falai/agent 2.6.1 → 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 +40 -2
- 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 +155 -63
- 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 +26 -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 +40 -2
- 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 +155 -63
- 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 +26 -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 +28 -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 +48 -2
- 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 +200 -74
- 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 +25 -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
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
AgentOptions,
|
|
8
8
|
AgentResponse,
|
|
9
9
|
AgentResponseStreamChunk,
|
|
10
|
+
EndedFlow,
|
|
10
11
|
History,
|
|
11
12
|
SessionState,
|
|
12
13
|
StepRef,
|
|
@@ -37,11 +38,14 @@ import { AutoChainExecutor, type AutoChainResult } from "./AutoChainExecutor";
|
|
|
37
38
|
import { StepLifecycle } from "./StepLifecycle";
|
|
38
39
|
import { SessionFinalizer } from "./SessionFinalizer";
|
|
39
40
|
import { ToolLoopExecutor } from "./ToolLoopExecutor";
|
|
41
|
+
import { flow } from "./flow-namespace";
|
|
40
42
|
import { SignalCoordinator } from "./SignalCoordinator";
|
|
41
43
|
import { ResponseGenerationError } from "./ResponseGenerationError";
|
|
42
|
-
import {
|
|
44
|
+
import { ProviderError, SessionConflictError } from "../types/errors";
|
|
45
|
+
import { cloneDeep, mergeCollected, logger, historyToEvents, completeCurrentFlow, render, userMessage, assistantMessage } from "../utils";
|
|
43
46
|
import { createTemplateContext } from "../utils/template";
|
|
44
47
|
import { StreamingMessageDecoder } from "../utils/streamingMessage";
|
|
48
|
+
import { tryParseJSONResponse } from "../utils/json";
|
|
45
49
|
import type { ToolManager } from "./ToolManager";
|
|
46
50
|
|
|
47
51
|
/**
|
|
@@ -95,6 +99,19 @@ export interface ResponseModalOptions {
|
|
|
95
99
|
*/
|
|
96
100
|
export interface RespondParams<TContext = unknown, TData = unknown> extends Record<string, unknown> {
|
|
97
101
|
history: History;
|
|
102
|
+
/**
|
|
103
|
+
* The user's message for this turn. When set, the engine appends it to the
|
|
104
|
+
* generation history AND to the returned session's history, then appends
|
|
105
|
+
* the assistant reply on top — callers who hold sessions no longer need to
|
|
106
|
+
* maintain history arrays by hand.
|
|
107
|
+
*/
|
|
108
|
+
message?: string;
|
|
109
|
+
/**
|
|
110
|
+
* Restrict this turn's ROUTING candidates to these flow ids/titles.
|
|
111
|
+
* Directive targets (goTo etc.) still resolve against the full registry.
|
|
112
|
+
* Use for entry-pin funnels instead of cloning/filtering agents.
|
|
113
|
+
*/
|
|
114
|
+
allowedFlows?: string[];
|
|
98
115
|
step?: StepRef;
|
|
99
116
|
session?: SessionState<TData>;
|
|
100
117
|
contextOverride?: Partial<TContext>;
|
|
@@ -140,6 +157,10 @@ interface ResponseContext<TContext = unknown, TData = unknown> {
|
|
|
140
157
|
signalHalted?: boolean;
|
|
141
158
|
/** Reply from a halt directive. */
|
|
142
159
|
signalHaltReply?: string;
|
|
160
|
+
/** Flows exited via pendingDirective redirects/resets during routing. */
|
|
161
|
+
endedFlows?: EndedFlow[];
|
|
162
|
+
/** The user turn supplied via params.message (drives history ownership). */
|
|
163
|
+
turnMessage?: string;
|
|
143
164
|
}
|
|
144
165
|
|
|
145
166
|
/**
|
|
@@ -261,6 +282,13 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
261
282
|
if (preTurnSession) {
|
|
262
283
|
this.agent.session.syncSession(preTurnSession);
|
|
263
284
|
}
|
|
285
|
+
// Typed library errors carry their own semantics (ProviderError.code,
|
|
286
|
+
// SessionConflictError) — rethrow bare so consumers can branch on
|
|
287
|
+
// them instead of string-matching. Everything else wraps with the
|
|
288
|
+
// original attached as `cause`.
|
|
289
|
+
if (error instanceof ProviderError || error instanceof SessionConflictError) {
|
|
290
|
+
throw error;
|
|
291
|
+
}
|
|
264
292
|
throw new ResponseGenerationError(
|
|
265
293
|
`[ResponseGenerationError] Response generation failed: ${error instanceof Error ? error.message : String(error)}. ` +
|
|
266
294
|
`Check provider configuration and network connectivity.`,
|
|
@@ -430,6 +458,58 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
430
458
|
return this.agent.tool;
|
|
431
459
|
}
|
|
432
460
|
|
|
461
|
+
/**
|
|
462
|
+
* Repair-parse a JSON-shaped protocol fragment (truncated output,
|
|
463
|
+
* fence-wrapped JSON) into a structured payload. Raw protocol fragments
|
|
464
|
+
* must never surface as the user-visible reply: if the repair parse fails,
|
|
465
|
+
* fail the turn LOUDLY so the caller's rollback/retry path engages instead
|
|
466
|
+
* of leaking `{"message": "…` to an end user. Plain prose (not JSON-shaped
|
|
467
|
+
* at all) never reaches this helper — the call sites gate on the fragment
|
|
468
|
+
* shape and pass it through untouched.
|
|
469
|
+
*/
|
|
470
|
+
private salvageStructuredOutput(
|
|
471
|
+
raw: string,
|
|
472
|
+
surface: "turn" | "stream"
|
|
473
|
+
): AgentStructuredResponse {
|
|
474
|
+
const salvaged = tryParseJSONResponse(raw) as Partial<AgentStructuredResponse> | undefined;
|
|
475
|
+
if (salvaged && typeof salvaged.message === "string") {
|
|
476
|
+
logger.warn(`[ResponseModal] Salvaged malformed structured output from ${surface} via JSON repair parse.`);
|
|
477
|
+
return { ...salvaged, message: salvaged.message };
|
|
478
|
+
}
|
|
479
|
+
throw ResponseGenerationError.fromError(
|
|
480
|
+
new Error(
|
|
481
|
+
"Model returned a schema-mandated response that could not be parsed as JSON. " +
|
|
482
|
+
`The ${surface} was failed instead of delivering raw protocol output to the user.`
|
|
483
|
+
),
|
|
484
|
+
'structured_output_malformed',
|
|
485
|
+
{ responseSchemaName: 'response_output' }
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Tool-emitted directives (ctx.dispatch / `{directive}` returns): state
|
|
491
|
+
* writes apply now; control flow queues for the next turn's
|
|
492
|
+
* pendingDirective applier (same deferred semantics as dispatch()).
|
|
493
|
+
*/
|
|
494
|
+
private async applyToolEmittedDirectives(
|
|
495
|
+
session: SessionState<TData>,
|
|
496
|
+
d: Directive<TContext, TData>
|
|
497
|
+
): Promise<SessionState<TData>> {
|
|
498
|
+
if (d.dataUpdate) {
|
|
499
|
+
session = mergeCollected(session, d.dataUpdate);
|
|
500
|
+
}
|
|
501
|
+
if (d.contextUpdate) {
|
|
502
|
+
await this.agent.updateContext(d.contextUpdate);
|
|
503
|
+
}
|
|
504
|
+
const control = { ...d };
|
|
505
|
+
delete control.dataUpdate;
|
|
506
|
+
delete control.contextUpdate;
|
|
507
|
+
if (Object.keys(control).length > 0) {
|
|
508
|
+
flow.queuePending(session, control);
|
|
509
|
+
}
|
|
510
|
+
return session;
|
|
511
|
+
}
|
|
512
|
+
|
|
433
513
|
/**
|
|
434
514
|
* Collect scoped instructions from agent, flow, and step into a ScopedInstructions value.
|
|
435
515
|
* @private
|
|
@@ -455,21 +535,25 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
455
535
|
*/
|
|
456
536
|
private async prepareUnifiedResponseContext(params: RespondParams<TContext, TData>): Promise<ResponseContext<TContext, TData>> {
|
|
457
537
|
try {
|
|
458
|
-
const { history: simpleHistory, contextOverride, signal } = params;
|
|
538
|
+
const { history: simpleHistory, contextOverride, signal, message: turnMessage, allowedFlows } = params;
|
|
459
539
|
|
|
460
540
|
// Validate input parameters
|
|
461
541
|
if (!simpleHistory) {
|
|
462
542
|
throw new ResponseGenerationError(
|
|
463
543
|
'[ResponseGenerationError] Missing history: history is required for response generation. ' +
|
|
464
|
-
'Pass a valid history array
|
|
544
|
+
'Pass a valid history array (or pass `message` alongside an existing history base).',
|
|
465
545
|
{ params, phase: 'validation' }
|
|
466
546
|
);
|
|
467
547
|
}
|
|
468
548
|
|
|
549
|
+
// `message` is the user turn: appended to what the model sees this
|
|
550
|
+
// turn AND recorded on the returned session's history.
|
|
551
|
+
const history = turnMessage
|
|
552
|
+
? [...simpleHistory, userMessage(turnMessage)]
|
|
553
|
+
: simpleHistory;
|
|
554
|
+
|
|
469
555
|
// Convert HistoryItem[] to Event[] for internal processing
|
|
470
|
-
const historyEvents = historyToEvents(
|
|
471
|
-
// Keep original HistoryItem[] format for external APIs
|
|
472
|
-
const history = simpleHistory;
|
|
556
|
+
const historyEvents = historyToEvents(history);
|
|
473
557
|
|
|
474
558
|
// Use ResponsePipeline for context and session preparation; context
|
|
475
559
|
// and session are passed explicitly — the pipeline holds no state
|
|
@@ -515,9 +599,23 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
515
599
|
}
|
|
516
600
|
}
|
|
517
601
|
|
|
602
|
+
// Record the user turn on the session's own history so the returned
|
|
603
|
+
// session carries the full exchange (respond owns session.history
|
|
604
|
+
// when `message` is used).
|
|
605
|
+
if (turnMessage) {
|
|
606
|
+
session.history = [...(session.history ?? []), userMessage(turnMessage)];
|
|
607
|
+
}
|
|
608
|
+
|
|
518
609
|
// PHASE 1: PREPARE - Execute prepare function if current step has one
|
|
519
610
|
try {
|
|
520
|
-
await this.stepLifecycle.runPrepare(session, effectiveContext);
|
|
611
|
+
const prepareDirective = await this.stepLifecycle.runPrepare(session, effectiveContext);
|
|
612
|
+
// Queue the control-flow directive for THIS turn: routing
|
|
613
|
+
// (handleRoutingAndStepSelection) consumes session.pendingDirective
|
|
614
|
+
// before deciding flow/step, so a prepare-phase goTo/goToStep/
|
|
615
|
+
// reset steers the current turn.
|
|
616
|
+
if (prepareDirective) {
|
|
617
|
+
flow.queuePending(session, prepareDirective);
|
|
618
|
+
}
|
|
521
619
|
} catch (error) {
|
|
522
620
|
throw ResponseGenerationError.fromError(error, 'step_preparation', params, { session, effectiveContext });
|
|
523
621
|
}
|
|
@@ -534,6 +632,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
534
632
|
signalPreDirective?: Directive<TContext, TData>;
|
|
535
633
|
signalHalted?: boolean;
|
|
536
634
|
signalHaltReply?: string;
|
|
635
|
+
endedFlows?: EndedFlow[];
|
|
537
636
|
};
|
|
538
637
|
try {
|
|
539
638
|
routingResult = await this.responsePipeline.routeAndSelectStep({
|
|
@@ -541,6 +640,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
541
640
|
history: historyEvents,
|
|
542
641
|
context: effectiveContext,
|
|
543
642
|
signal,
|
|
643
|
+
allowedFlows,
|
|
544
644
|
});
|
|
545
645
|
} catch (error) {
|
|
546
646
|
throw ResponseGenerationError.fromError(error, 'routing_and_step_selection', params, { session, effectiveContext });
|
|
@@ -550,6 +650,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
550
650
|
effectiveContext,
|
|
551
651
|
session: routingResult.session,
|
|
552
652
|
history,
|
|
653
|
+
turnMessage,
|
|
553
654
|
selectedFlow: routingResult.selectedFlow,
|
|
554
655
|
selectedStep: routingResult.selectedStep,
|
|
555
656
|
responseDirectives: routingResult.responseDirectives,
|
|
@@ -559,6 +660,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
559
660
|
signalPreDirective: routingResult.signalPreDirective,
|
|
560
661
|
signalHalted: routingResult.signalHalted,
|
|
561
662
|
signalHaltReply: routingResult.signalHaltReply,
|
|
663
|
+
endedFlows: routingResult.endedFlows,
|
|
562
664
|
};
|
|
563
665
|
} catch (error) {
|
|
564
666
|
// Re-throw ResponseGenerationError as-is, wrap others
|
|
@@ -748,6 +850,8 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
748
850
|
let isFlowComplete = false;
|
|
749
851
|
let appliedInstructions: AppliedInstruction[] | undefined;
|
|
750
852
|
let runPostPhase = true;
|
|
853
|
+
let tokensUsed: number | undefined;
|
|
854
|
+
const endedFlows: EndedFlow[] = [...(responseContext.endedFlows ?? [])];
|
|
751
855
|
|
|
752
856
|
switch (plan.outcome.kind) {
|
|
753
857
|
case 'halt': {
|
|
@@ -765,6 +869,11 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
765
869
|
});
|
|
766
870
|
isFlowComplete = true;
|
|
767
871
|
stoppedReason = plan.outcome.stoppedReason;
|
|
872
|
+
endedFlows.push({
|
|
873
|
+
flowId: plan.outcome.selectedFlow.id,
|
|
874
|
+
title: plan.outcome.selectedFlow.title,
|
|
875
|
+
reason: plan.outcome.stoppedReason,
|
|
876
|
+
});
|
|
768
877
|
break;
|
|
769
878
|
}
|
|
770
879
|
case 'flowStep': {
|
|
@@ -786,6 +895,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
786
895
|
toolCalls = result.toolCalls;
|
|
787
896
|
session = result.session;
|
|
788
897
|
appliedInstructions = result.appliedInstructions;
|
|
898
|
+
tokensUsed = result.tokensUsed;
|
|
789
899
|
if (plan.outcome.step) {
|
|
790
900
|
executedSteps = [{ id: plan.outcome.step.id, flowId: plan.outcome.selectedFlow.id }];
|
|
791
901
|
}
|
|
@@ -811,6 +921,14 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
811
921
|
session, context: effectiveContext, historyEvents, message, signalFirings, runPostPhase,
|
|
812
922
|
});
|
|
813
923
|
|
|
924
|
+
// History ownership: with params.message the returned session carries
|
|
925
|
+
// the full exchange — the user turn was recorded pre-routing, the
|
|
926
|
+
// assistant tail lands here (post-phase, so overridden replies are the
|
|
927
|
+
// ones recorded). Empty tails are skipped.
|
|
928
|
+
if (responseContext.turnMessage && tail.message) {
|
|
929
|
+
tail.session.history = [...(tail.session.history ?? []), assistantMessage(tail.message)];
|
|
930
|
+
}
|
|
931
|
+
|
|
814
932
|
return {
|
|
815
933
|
message: tail.message,
|
|
816
934
|
session: tail.session,
|
|
@@ -820,6 +938,8 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
820
938
|
stoppedReason,
|
|
821
939
|
appliedInstructions,
|
|
822
940
|
triggeredSignals: tail.triggeredSignals,
|
|
941
|
+
...(tokensUsed !== undefined ? { metadata: { tokensUsed } } : {}),
|
|
942
|
+
...(endedFlows.length > 0 ? { endedFlows } : {}),
|
|
823
943
|
};
|
|
824
944
|
}
|
|
825
945
|
|
|
@@ -852,6 +972,8 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
852
972
|
session: SessionState<TData>;
|
|
853
973
|
appliedInstructions?: AppliedInstruction[];
|
|
854
974
|
stoppedReason?: StoppedReason;
|
|
975
|
+
/** Provider-reported usage for the primary generation call. */
|
|
976
|
+
tokensUsed?: number;
|
|
855
977
|
}> {
|
|
856
978
|
const { selectedFlow, selectedStep, responseDirectives, history, context, historyEvents, signal, transientAppendage, mergedPreDirective } = params;
|
|
857
979
|
let session = params.session;
|
|
@@ -940,17 +1062,25 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
940
1062
|
parameters: responseSchema ? { jsonSchema: responseSchema, schemaName: "response_output" } : undefined,
|
|
941
1063
|
});
|
|
942
1064
|
|
|
943
|
-
let
|
|
944
|
-
let
|
|
1065
|
+
let structuredData = result.structured;
|
|
1066
|
+
let message = structuredData?.message || result.message;
|
|
1067
|
+
const tokensUsed = result.metadata?.tokensUsed;
|
|
1068
|
+
|
|
1069
|
+
// A schema was requested but the provider failed to parse the model's
|
|
1070
|
+
// JSON (truncated output, fence-wrapped fragments). Raw protocol
|
|
1071
|
+
// fragments must never surface as the user-visible reply: attempt one
|
|
1072
|
+
// repair-parse, and if that fails fail the turn LOUDLY so the caller's
|
|
1073
|
+
// rollback/retry path engages instead of leaking `{"message": "…` to
|
|
1074
|
+
// an end user. Plain prose (not JSON-shaped at all) still passes
|
|
1075
|
+
// through — it is not a protocol fragment.
|
|
1076
|
+
if (!structuredData && responseSchema && message && /^\s*(```|{)/.test(message)) {
|
|
1077
|
+
const salvaged = this.salvageStructuredOutput(message, "turn");
|
|
1078
|
+
structuredData = salvaged;
|
|
1079
|
+
message = salvaged.message;
|
|
1080
|
+
}
|
|
945
1081
|
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
hasMessage: !!message,
|
|
949
|
-
messageLength: message?.length || 0,
|
|
950
|
-
hasToolCalls: !!toolCalls,
|
|
951
|
-
toolCallsCount: toolCalls?.length || 0,
|
|
952
|
-
toolNames: toolCalls?.map(tc => tc.toolName) || [],
|
|
953
|
-
});
|
|
1082
|
+
const effectiveResult = structuredData ? { ...result, structured: structuredData } : result;
|
|
1083
|
+
let toolCalls = structuredData?.toolCalls;
|
|
954
1084
|
|
|
955
1085
|
// Execute tools with unified loop handling
|
|
956
1086
|
const toolResult = await this.toolLoopExecutor.runLoop({
|
|
@@ -971,14 +1101,21 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
971
1101
|
message = toolResult.finalMessage;
|
|
972
1102
|
}
|
|
973
1103
|
|
|
1104
|
+
// Tool-emitted directives (ctx.dispatch / `{directive}` returns):
|
|
1105
|
+
// state writes apply now; control flow queues for the next turn's
|
|
1106
|
+
// pendingDirective applier (same deferred semantics as dispatch()).
|
|
1107
|
+
if (toolResult.directives) {
|
|
1108
|
+
session = await this.applyToolEmittedDirectives(session, toolResult.directives);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
974
1111
|
// Collect data from response
|
|
975
1112
|
// Use follow-up structured data from tool loop when available, fall back to original result
|
|
976
1113
|
const dataSource = toolResult.structured
|
|
977
1114
|
? { structured: toolResult.structured }
|
|
978
|
-
:
|
|
1115
|
+
: effectiveResult;
|
|
979
1116
|
session = await this.collectDataFromResponse({ result: dataSource, selectedFlow, nextStep, session });
|
|
980
1117
|
|
|
981
|
-
return { message, toolCalls, session, appliedInstructions };
|
|
1118
|
+
return { message, toolCalls, session, appliedInstructions, tokensUsed };
|
|
982
1119
|
} finally {
|
|
983
1120
|
// Drain the transient appendage at end of turn.
|
|
984
1121
|
// This ensures Directive.appendPrompt does not leak to subsequent
|
|
@@ -1070,6 +1207,14 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
1070
1207
|
const accumulated = tail.message;
|
|
1071
1208
|
const delta = tail.replyOverridden ? accumulated : chunk.delta;
|
|
1072
1209
|
|
|
1210
|
+
// History ownership (parity with the sync tail): with
|
|
1211
|
+
// params.message the returned session carries the full
|
|
1212
|
+
// exchange — the user turn was recorded pre-routing, the
|
|
1213
|
+
// assistant tail lands here, BEFORE finalize persists it.
|
|
1214
|
+
if (responseContext.turnMessage && tail.message) {
|
|
1215
|
+
tail.session.history = [...(tail.session.history ?? []), assistantMessage(tail.message)];
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1073
1218
|
// Single streaming exit: finalize the post-phase session so
|
|
1074
1219
|
// post-signal mutations (e.g. pendingDirective) are persisted.
|
|
1075
1220
|
await this.sessionFinalizer.finalize(tail.session, effectiveContext);
|
|
@@ -1291,6 +1436,15 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
1291
1436
|
session = batchResult.session;
|
|
1292
1437
|
toolCalls = batchResult.toolCalls;
|
|
1293
1438
|
|
|
1439
|
+
// Tool-emitted directives (ctx.dispatch / `{directive}`):
|
|
1440
|
+
// state writes apply now; control flow queues for the next
|
|
1441
|
+
// turn's pendingDirective applier (same deferred semantics
|
|
1442
|
+
// as dispatch()). A verbatim tool reply already replaced the
|
|
1443
|
+
// closing message inside runStreamingBatch.
|
|
1444
|
+
if (batchResult.directives) {
|
|
1445
|
+
session = await this.applyToolEmittedDirectives(session, batchResult.directives);
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1294
1448
|
// Prefer the post-tool follow-up structured for collection and
|
|
1295
1449
|
// emission whenever present — independent of whether a closing
|
|
1296
1450
|
// message was forced — matching the non-streaming path's
|
|
@@ -1309,6 +1463,18 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
1309
1463
|
}
|
|
1310
1464
|
}
|
|
1311
1465
|
|
|
1466
|
+
// Streaming twin of the non-streaming salvage guard: a schema was
|
|
1467
|
+
// requested but no structured payload arrived, and the accumulated
|
|
1468
|
+
// text is JSON-shaped (a protocol fragment) — repair-parse it or
|
|
1469
|
+
// fail the turn rather than leaking raw output to the user.
|
|
1470
|
+
if (chunk.done && !finalStructured && responseSchema && finalAccumulated && /^\s*(```|{)/.test(finalAccumulated)) {
|
|
1471
|
+
const salvaged = this.salvageStructuredOutput(finalAccumulated, "stream");
|
|
1472
|
+
finalStructured = salvaged;
|
|
1473
|
+
finalDelta = salvaged.message.startsWith(finalAccumulated)
|
|
1474
|
+
? salvaged.message.slice(finalAccumulated.length)
|
|
1475
|
+
: salvaged.message;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1312
1478
|
// Collect data on the final chunk for any flow step — flow
|
|
1313
1479
|
// required/optional fields are valid targets even without a step
|
|
1314
1480
|
// `collect` — preferring the post-tool follow-up structured so a
|