@ariaflowagents/core 0.8.1 → 0.9.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/dist/capabilities/AutoRetrieveCapability.d.ts +30 -0
- package/dist/capabilities/AutoRetrieveCapability.d.ts.map +1 -0
- package/dist/capabilities/AutoRetrieveCapability.js +36 -0
- package/dist/capabilities/AutoRetrieveCapability.js.map +1 -0
- package/dist/capabilities/ExtractionCapability.d.ts +25 -0
- package/dist/capabilities/ExtractionCapability.d.ts.map +1 -0
- package/dist/capabilities/ExtractionCapability.js +74 -0
- package/dist/capabilities/ExtractionCapability.js.map +1 -0
- package/dist/capabilities/FlowCapability.d.ts +81 -0
- package/dist/capabilities/FlowCapability.d.ts.map +1 -0
- package/dist/capabilities/FlowCapability.js +482 -0
- package/dist/capabilities/FlowCapability.js.map +1 -0
- package/dist/capabilities/GuardrailCapability.d.ts +30 -0
- package/dist/capabilities/GuardrailCapability.d.ts.map +1 -0
- package/dist/capabilities/GuardrailCapability.js +38 -0
- package/dist/capabilities/GuardrailCapability.js.map +1 -0
- package/dist/capabilities/HandoffCapability.d.ts +19 -0
- package/dist/capabilities/HandoffCapability.d.ts.map +1 -0
- package/dist/capabilities/HandoffCapability.js +58 -0
- package/dist/capabilities/HandoffCapability.js.map +1 -0
- package/dist/capabilities/LivePromptAssembler.d.ts +108 -0
- package/dist/capabilities/LivePromptAssembler.d.ts.map +1 -0
- package/dist/capabilities/LivePromptAssembler.js +157 -0
- package/dist/capabilities/LivePromptAssembler.js.map +1 -0
- package/dist/capabilities/TriageCapability.d.ts +16 -0
- package/dist/capabilities/TriageCapability.d.ts.map +1 -0
- package/dist/capabilities/TriageCapability.js +61 -0
- package/dist/capabilities/TriageCapability.js.map +1 -0
- package/dist/capabilities/adapters/ai-sdk.d.ts +14 -0
- package/dist/capabilities/adapters/ai-sdk.d.ts.map +1 -0
- package/dist/capabilities/adapters/ai-sdk.js +29 -0
- package/dist/capabilities/adapters/ai-sdk.js.map +1 -0
- package/dist/capabilities/adapters/gemini.d.ts +15 -0
- package/dist/capabilities/adapters/gemini.d.ts.map +1 -0
- package/dist/capabilities/adapters/gemini.js +40 -0
- package/dist/capabilities/adapters/gemini.js.map +1 -0
- package/dist/capabilities/index.d.ts +154 -0
- package/dist/capabilities/index.d.ts.map +1 -0
- package/dist/capabilities/index.js +128 -0
- package/dist/capabilities/index.js.map +1 -0
- package/dist/eval/EvalRunner.d.ts +12 -0
- package/dist/eval/EvalRunner.d.ts.map +1 -0
- package/dist/eval/EvalRunner.js +64 -0
- package/dist/eval/EvalRunner.js.map +1 -0
- package/dist/eval/scoring.d.ts +15 -0
- package/dist/eval/scoring.d.ts.map +1 -0
- package/dist/eval/scoring.js +152 -0
- package/dist/eval/scoring.js.map +1 -0
- package/dist/eval/types.d.ts +59 -0
- package/dist/eval/types.d.ts.map +1 -0
- package/dist/eval/types.js +2 -0
- package/dist/eval/types.js.map +1 -0
- package/dist/flows/FlowGraph.d.ts +3 -1
- package/dist/flows/FlowGraph.d.ts.map +1 -1
- package/dist/flows/FlowGraph.js +5 -0
- package/dist/flows/FlowGraph.js.map +1 -1
- package/dist/flows/FlowManager.d.ts +60 -1
- package/dist/flows/FlowManager.d.ts.map +1 -1
- package/dist/flows/FlowManager.js +467 -34
- package/dist/flows/FlowManager.js.map +1 -1
- package/dist/flows/extraction.d.ts +16 -1
- package/dist/flows/extraction.d.ts.map +1 -1
- package/dist/flows/extraction.js +34 -0
- package/dist/flows/extraction.js.map +1 -1
- package/dist/flows/index.d.ts +2 -0
- package/dist/flows/index.d.ts.map +1 -1
- package/dist/flows/index.js +1 -0
- package/dist/flows/index.js.map +1 -1
- package/dist/flows/validation.d.ts +1 -1
- package/dist/flows/validation.d.ts.map +1 -1
- package/dist/flows/validation.js +13 -1
- package/dist/flows/validation.js.map +1 -1
- package/dist/hooks/HookRunner.d.ts +3 -1
- package/dist/hooks/HookRunner.d.ts.map +1 -1
- package/dist/hooks/HookRunner.js +3 -0
- package/dist/hooks/HookRunner.js.map +1 -1
- package/dist/hooks/builtin/metrics.d.ts.map +1 -1
- package/dist/hooks/builtin/metrics.js +12 -0
- package/dist/hooks/builtin/metrics.js.map +1 -1
- package/dist/hooks/builtin/observability.d.ts +21 -0
- package/dist/hooks/builtin/observability.d.ts.map +1 -0
- package/dist/hooks/builtin/observability.js +535 -0
- package/dist/hooks/builtin/observability.js.map +1 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/orchestration/DefaultOrchestrationAuthority.d.ts +91 -0
- package/dist/orchestration/DefaultOrchestrationAuthority.d.ts.map +1 -0
- package/dist/orchestration/DefaultOrchestrationAuthority.js +786 -0
- package/dist/orchestration/DefaultOrchestrationAuthority.js.map +1 -0
- package/dist/orchestration/OrchestrationAuthority.d.ts +119 -0
- package/dist/orchestration/OrchestrationAuthority.d.ts.map +1 -0
- package/dist/orchestration/OrchestrationAuthority.js +2 -0
- package/dist/orchestration/OrchestrationAuthority.js.map +1 -0
- package/dist/orchestration/RealtimeExtractionRunner.d.ts +25 -0
- package/dist/orchestration/RealtimeExtractionRunner.d.ts.map +1 -0
- package/dist/orchestration/RealtimeExtractionRunner.js +62 -0
- package/dist/orchestration/RealtimeExtractionRunner.js.map +1 -0
- package/dist/orchestration/index.d.ts +5 -0
- package/dist/orchestration/index.d.ts.map +1 -0
- package/dist/orchestration/index.js +4 -0
- package/dist/orchestration/index.js.map +1 -0
- package/dist/orchestration/types.d.ts +134 -0
- package/dist/orchestration/types.d.ts.map +1 -0
- package/dist/orchestration/types.js +2 -0
- package/dist/orchestration/types.js.map +1 -0
- package/dist/realtime/RealtimeAudioClient.d.ts +105 -0
- package/dist/realtime/RealtimeAudioClient.d.ts.map +1 -0
- package/dist/realtime/RealtimeAudioClient.js +15 -0
- package/dist/realtime/RealtimeAudioClient.js.map +1 -0
- package/dist/realtime/RealtimeRuntime.d.ts +136 -0
- package/dist/realtime/RealtimeRuntime.d.ts.map +1 -0
- package/dist/realtime/RealtimeRuntime.js +270 -0
- package/dist/realtime/RealtimeRuntime.js.map +1 -0
- package/dist/realtime/index.d.ts +4 -0
- package/dist/realtime/index.d.ts.map +1 -0
- package/dist/realtime/index.js +2 -0
- package/dist/realtime/index.js.map +1 -0
- package/dist/runtime/ExtractionEngine.d.ts +2 -1
- package/dist/runtime/ExtractionEngine.d.ts.map +1 -1
- package/dist/runtime/ExtractionEngine.js +11 -0
- package/dist/runtime/ExtractionEngine.js.map +1 -1
- package/dist/runtime/FlowExecutor.d.ts +7 -5
- package/dist/runtime/FlowExecutor.d.ts.map +1 -1
- package/dist/runtime/FlowExecutor.js +71 -12
- package/dist/runtime/FlowExecutor.js.map +1 -1
- package/dist/runtime/Runtime.d.ts +22 -0
- package/dist/runtime/Runtime.d.ts.map +1 -1
- package/dist/runtime/Runtime.js +47 -0
- package/dist/runtime/Runtime.js.map +1 -1
- package/dist/runtime/pipeline/AgentExecuteStage.d.ts.map +1 -1
- package/dist/runtime/pipeline/AgentExecuteStage.js +94 -25
- package/dist/runtime/pipeline/AgentExecuteStage.js.map +1 -1
- package/dist/runtime/pipeline/ContextAssembleStage.js +1 -1
- package/dist/types/index.d.ts +61 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/telemetry.d.ts +107 -0
- package/dist/types/telemetry.d.ts.map +1 -1
- package/package.json +15 -2
|
@@ -0,0 +1,786 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { isExtractionNode } from '../types/index.js';
|
|
3
|
+
import { CapabilityHost, FlowCapability, ExtractionCapability, HandoffCapability, toGeminiDeclarations, DefaultLivePromptAssembler } from '../capabilities/index.js';
|
|
4
|
+
import { HookRunner } from '../hooks/HookRunner.js';
|
|
5
|
+
import { ToolEnforcer } from '../guards/ToolEnforcer.js';
|
|
6
|
+
import { defaultEnforcementRules } from '../guards/rules.js';
|
|
7
|
+
import { DefaultToolExecutor } from '../foundation/DefaultToolExecutor.js';
|
|
8
|
+
import { DefaultConversationState } from '../foundation/DefaultConversationState.js';
|
|
9
|
+
import { DefaultConversationEventLog } from '../foundation/DefaultConversationEventLog.js';
|
|
10
|
+
import { DefaultAgentStateController } from '../foundation/DefaultAgentStateController.js';
|
|
11
|
+
import { ExtractionEngine } from '../runtime/ExtractionEngine.js';
|
|
12
|
+
import { RealtimeExtractionRunner } from './RealtimeExtractionRunner.js';
|
|
13
|
+
import { MemoryStore } from '../session/stores/MemoryStore.js';
|
|
14
|
+
// ─── DefaultOrchestrationAuthority ──────────────────────────────────────────
|
|
15
|
+
/**
|
|
16
|
+
* Default implementation of the OrchestrationAuthority port.
|
|
17
|
+
*
|
|
18
|
+
* Composes Foundation services, HookRunner, CapabilityHost, LivePromptAssembler,
|
|
19
|
+
* ExtractionEngine, and MemoryService into the transport-agnostic orchestration
|
|
20
|
+
* sequence defined in RFC-REALTIME-RUNTIME-AUTHORITY.md.
|
|
21
|
+
*
|
|
22
|
+
* This is the "single brain" that both text Runtime and RealtimeRuntime
|
|
23
|
+
* delegate to for conversational semantics.
|
|
24
|
+
*/
|
|
25
|
+
export class DefaultOrchestrationAuthority {
|
|
26
|
+
agents;
|
|
27
|
+
defaultAgentId;
|
|
28
|
+
hookRunner;
|
|
29
|
+
toolExecutor;
|
|
30
|
+
conversationState;
|
|
31
|
+
eventLog;
|
|
32
|
+
agentState;
|
|
33
|
+
extractionEngine;
|
|
34
|
+
promptAssembler;
|
|
35
|
+
memoryService;
|
|
36
|
+
memoryIngestion;
|
|
37
|
+
maxPersistRetries;
|
|
38
|
+
realtimeExtractionRunner;
|
|
39
|
+
autoRetrieveProvider;
|
|
40
|
+
constructor(config) {
|
|
41
|
+
// Build agent registry
|
|
42
|
+
this.agents = new Map();
|
|
43
|
+
for (const agent of config.agents) {
|
|
44
|
+
this.agents.set(agent.id, agent);
|
|
45
|
+
}
|
|
46
|
+
this.defaultAgentId = config.defaultAgentId;
|
|
47
|
+
const injected = config.injectedServices;
|
|
48
|
+
// Hook infrastructure — use injected HookRunner (shared with Runtime)
|
|
49
|
+
// or create a new one. This is the critical fix for deviation D1:
|
|
50
|
+
// VoiceEngine previously had no access to HookRunner.
|
|
51
|
+
this.hookRunner = injected?.hookRunner ?? new HookRunner(config.hooks);
|
|
52
|
+
// Foundation services — use injected instances (shared with Runtime)
|
|
53
|
+
// or construct new ones from config.
|
|
54
|
+
if (injected?.toolExecutor) {
|
|
55
|
+
this.toolExecutor = injected.toolExecutor;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const sessionStore = config.sessionStore ?? new MemoryStore();
|
|
59
|
+
const enforcer = new ToolEnforcer(config.enforcementRules ?? defaultEnforcementRules);
|
|
60
|
+
this.toolExecutor = new DefaultToolExecutor({
|
|
61
|
+
enforcer,
|
|
62
|
+
hookRunner: this.hookRunner,
|
|
63
|
+
memoryService: config.memoryService,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (injected?.conversationState) {
|
|
67
|
+
this.conversationState = injected.conversationState;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const sessionStore = config.sessionStore ?? new MemoryStore();
|
|
71
|
+
this.conversationState = new DefaultConversationState({
|
|
72
|
+
sessionStore,
|
|
73
|
+
defaultAgentId: config.defaultAgentId,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
this.eventLog = injected?.eventLog ?? new DefaultConversationEventLog({
|
|
77
|
+
sessionStore: config.sessionStore ?? new MemoryStore(),
|
|
78
|
+
});
|
|
79
|
+
this.agentState = injected?.agentState ?? new DefaultAgentStateController();
|
|
80
|
+
// Extraction engine — fixes deviation D3 (no extraction in voice path)
|
|
81
|
+
this.extractionEngine = new ExtractionEngine({
|
|
82
|
+
defaultModel: config.defaultModel,
|
|
83
|
+
telemetry: config.telemetry,
|
|
84
|
+
});
|
|
85
|
+
this.realtimeExtractionRunner = config.extractionModel
|
|
86
|
+
? new RealtimeExtractionRunner({
|
|
87
|
+
model: config.extractionModel,
|
|
88
|
+
telemetry: config.telemetry,
|
|
89
|
+
})
|
|
90
|
+
: undefined;
|
|
91
|
+
// Prompt assembler for realtime session config
|
|
92
|
+
this.promptAssembler = new DefaultLivePromptAssembler();
|
|
93
|
+
// Memory — fixes deviation D4 (no memory ingestion in voice path)
|
|
94
|
+
this.memoryService = config.memoryService;
|
|
95
|
+
this.memoryIngestion = config.memoryIngestion ?? 'manual';
|
|
96
|
+
// Persistence policy
|
|
97
|
+
this.maxPersistRetries = config.maxPersistRetries ?? 3;
|
|
98
|
+
this.autoRetrieveProvider = config.autoRetrieveProvider;
|
|
99
|
+
}
|
|
100
|
+
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
101
|
+
async openSession(params) {
|
|
102
|
+
const agentId = params.agentId ?? this.defaultAgentId;
|
|
103
|
+
const agent = this.agents.get(agentId);
|
|
104
|
+
if (!agent) {
|
|
105
|
+
throw new Error(`[OrchestrationAuthority] Agent "${agentId}" not found`);
|
|
106
|
+
}
|
|
107
|
+
// Load or create session
|
|
108
|
+
const sessionId = params.sessionId ?? crypto.randomUUID();
|
|
109
|
+
const session = await this.conversationState.load(sessionId, params.userId);
|
|
110
|
+
// Set active agent and bump turn
|
|
111
|
+
this.agentState.setActiveAgent(session, agentId);
|
|
112
|
+
this.conversationState.bumpSessionTurn(session);
|
|
113
|
+
// Build RunContext
|
|
114
|
+
const context = {
|
|
115
|
+
session,
|
|
116
|
+
agentId,
|
|
117
|
+
stepCount: 0,
|
|
118
|
+
totalTokens: 0,
|
|
119
|
+
handoffStack: [],
|
|
120
|
+
startTime: Date.now(),
|
|
121
|
+
consecutiveErrors: 0,
|
|
122
|
+
toolCallHistory: [],
|
|
123
|
+
};
|
|
124
|
+
// Build CapabilityHost for this agent
|
|
125
|
+
const { host, flowCapability } = this.buildCapabilityHost(agent, session);
|
|
126
|
+
const basePrompt = resolveAgentPrompt(agent);
|
|
127
|
+
// Fire lifecycle hooks — fixes deviation D1 for realtime
|
|
128
|
+
await this.hookRunner.onStart(context);
|
|
129
|
+
await this.hookRunner.onAgentStart(context, agentId);
|
|
130
|
+
const handle = {
|
|
131
|
+
session,
|
|
132
|
+
context,
|
|
133
|
+
activeAgentId: agentId,
|
|
134
|
+
_internals: { host, flowCapability, mode: params.mode, basePrompt, agent },
|
|
135
|
+
};
|
|
136
|
+
this.syncFlowStateToSession(handle);
|
|
137
|
+
return handle;
|
|
138
|
+
}
|
|
139
|
+
async recordUserInput(handle, text) {
|
|
140
|
+
this.conversationState.appendUserMessage(handle.session, text);
|
|
141
|
+
this.getInternals(handle).flowCapability?.appendUserMessage(text);
|
|
142
|
+
this.syncFlowStateToSession(handle);
|
|
143
|
+
}
|
|
144
|
+
async recordAssistantOutput(handle, text) {
|
|
145
|
+
this.conversationState.appendAssistantMessage(handle.session, text);
|
|
146
|
+
this.getInternals(handle).flowCapability?.appendAssistantMessage(text);
|
|
147
|
+
this.syncFlowStateToSession(handle);
|
|
148
|
+
}
|
|
149
|
+
async prepareRealtimeConfig(handle) {
|
|
150
|
+
const internals = this.getInternals(handle);
|
|
151
|
+
const systemInstruction = await this.promptAssembler.assemble({
|
|
152
|
+
host: internals.host,
|
|
153
|
+
basePrompt: internals.basePrompt,
|
|
154
|
+
session: handle.session,
|
|
155
|
+
memoryService: this.memoryService,
|
|
156
|
+
});
|
|
157
|
+
const tools = toGeminiDeclarations(internals.host.getAllTools());
|
|
158
|
+
internals.host.markConfigured();
|
|
159
|
+
return {
|
|
160
|
+
systemInstruction,
|
|
161
|
+
tools,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
async executeToolCall(handle, call) {
|
|
165
|
+
const internals = this.getInternals(handle);
|
|
166
|
+
const { host, agent } = internals;
|
|
167
|
+
const { session, context } = handle;
|
|
168
|
+
const effectiveArgs = await this.resolveToolArgs(handle, call.name, call.args);
|
|
169
|
+
// 1. Resolve the tool from CapabilityHost (includes capability + regular tools)
|
|
170
|
+
const allTools = host.getAllTools();
|
|
171
|
+
const tool = allTools.find((t) => t.name === call.name);
|
|
172
|
+
if (!tool) {
|
|
173
|
+
// Unknown tool — return error response, fire hook, continue
|
|
174
|
+
const errorOutput = { error: `Unknown tool: ${call.name}` };
|
|
175
|
+
const record = this.buildToolCallRecord(call.id, call.name, effectiveArgs, errorOutput, false);
|
|
176
|
+
context.toolCallHistory.push(record);
|
|
177
|
+
await this.hookRunner.onToolError(context, record, new Error(`Unknown tool: ${call.name}`));
|
|
178
|
+
return {
|
|
179
|
+
action: { type: 'continue' },
|
|
180
|
+
toolResponse: { id: call.id, name: call.name, output: errorOutput },
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
// 2. Record tool call event
|
|
184
|
+
this.eventLog.record(context, {
|
|
185
|
+
type: 'tool-call',
|
|
186
|
+
toolCallId: call.id,
|
|
187
|
+
toolName: call.name,
|
|
188
|
+
args: effectiveArgs,
|
|
189
|
+
});
|
|
190
|
+
// 3. Execute the tool
|
|
191
|
+
let result;
|
|
192
|
+
try {
|
|
193
|
+
const isCapabilityTool = this.isCapabilityTool(call.name, tool, agent);
|
|
194
|
+
if (isCapabilityTool) {
|
|
195
|
+
// Capability tools (flow transitions, extraction) have self-contained execute()
|
|
196
|
+
result = await tool.execute(effectiveArgs);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
// Regular agent tools go through ToolExecutor for enforcement + guards
|
|
200
|
+
result = await this.toolExecutor.execute({
|
|
201
|
+
session,
|
|
202
|
+
userId: session.userId,
|
|
203
|
+
agentId: context.agentId,
|
|
204
|
+
toolName: call.name,
|
|
205
|
+
tool: { execute: tool.execute, description: tool.description },
|
|
206
|
+
input: effectiveArgs,
|
|
207
|
+
toolCallId: call.id,
|
|
208
|
+
toolCallHistory: context.toolCallHistory,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
214
|
+
// Record error
|
|
215
|
+
this.eventLog.record(context, {
|
|
216
|
+
type: 'tool-error',
|
|
217
|
+
toolCallId: call.id,
|
|
218
|
+
toolName: call.name,
|
|
219
|
+
error: errorMsg,
|
|
220
|
+
});
|
|
221
|
+
const record = this.buildToolCallRecord(call.id, call.name, effectiveArgs, undefined, false, error);
|
|
222
|
+
context.toolCallHistory.push(record);
|
|
223
|
+
await this.hookRunner.onToolError(context, record, error);
|
|
224
|
+
return {
|
|
225
|
+
action: { type: 'continue' },
|
|
226
|
+
toolResponse: { id: call.id, name: call.name, output: { error: errorMsg } },
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
// 4. Record successful result
|
|
230
|
+
this.eventLog.record(context, {
|
|
231
|
+
type: 'tool-result',
|
|
232
|
+
toolCallId: call.id,
|
|
233
|
+
toolName: call.name,
|
|
234
|
+
result,
|
|
235
|
+
});
|
|
236
|
+
const record = this.buildToolCallRecord(call.id, call.name, effectiveArgs, result, true);
|
|
237
|
+
context.toolCallHistory.push(record);
|
|
238
|
+
// 5. Fire onToolResult hook — fixes deviation D1
|
|
239
|
+
await this.hookRunner.onToolResult(context, record);
|
|
240
|
+
// 6. Route through CapabilityHost to determine semantic action
|
|
241
|
+
const action = host.processToolResult(call.name, effectiveArgs, result);
|
|
242
|
+
this.syncFlowStateToSession(handle);
|
|
243
|
+
// 7. Persist session (fire-and-forget in hot path) — fixes deviation D2 partially
|
|
244
|
+
this.conversationState.save(session).catch((err) => {
|
|
245
|
+
console.error('[OrchestrationAuthority] Background save failed:', err);
|
|
246
|
+
});
|
|
247
|
+
// 8. Build outcome based on action type
|
|
248
|
+
return this.buildOutcome(handle, call, result, action);
|
|
249
|
+
}
|
|
250
|
+
async completeTurn(handle) {
|
|
251
|
+
const { context, session } = handle;
|
|
252
|
+
const internals = this.getInternals(handle);
|
|
253
|
+
// 1. Post-turn extraction — fixes deviation D3
|
|
254
|
+
await this.runExtraction(internals.agent, context);
|
|
255
|
+
// 1b. Optional realtime extraction refinement for active extraction nodes
|
|
256
|
+
if (internals.mode === 'realtime') {
|
|
257
|
+
await this.runRealtimeExtractionRefinement(handle);
|
|
258
|
+
}
|
|
259
|
+
// 1c. Realtime knowledge + memory preload into working memory (before persist)
|
|
260
|
+
if (internals.mode === 'realtime') {
|
|
261
|
+
await this.runRealtimeKnowledgeRefresh(handle);
|
|
262
|
+
}
|
|
263
|
+
// 2. Persist session with retry — fixes deviation D2
|
|
264
|
+
await this.persistWithRetry(session);
|
|
265
|
+
// 3. Memory ingestion — fixes deviation D4
|
|
266
|
+
await this.runMemoryIngestion(context, session);
|
|
267
|
+
}
|
|
268
|
+
async closeSession(handle, result) {
|
|
269
|
+
const { context, session } = handle;
|
|
270
|
+
// Fire lifecycle hooks — fixes deviation D7
|
|
271
|
+
await this.hookRunner.onAgentEnd(context, context.agentId);
|
|
272
|
+
await this.hookRunner.onEnd(context, result ?? { success: true });
|
|
273
|
+
// Clean up transient state
|
|
274
|
+
this.eventLog.cleanup(session);
|
|
275
|
+
// Final persistence with retry
|
|
276
|
+
await this.persistWithRetry(session);
|
|
277
|
+
await this.hookRunner.onSessionEnd(session, this.buildSessionEndMetadata(session, result ?? { success: true }));
|
|
278
|
+
}
|
|
279
|
+
async failSession(handle, error) {
|
|
280
|
+
const { context, session } = handle;
|
|
281
|
+
// Fire error hooks — fixes deviation D7
|
|
282
|
+
await this.hookRunner.onError(context, error);
|
|
283
|
+
await this.hookRunner.onEnd(context, { success: false, error });
|
|
284
|
+
// Clean up and emergency persist
|
|
285
|
+
this.eventLog.cleanup(session);
|
|
286
|
+
await this.conversationState.save(session).catch((err) => {
|
|
287
|
+
console.error('[OrchestrationAuthority] Emergency save failed:', err);
|
|
288
|
+
});
|
|
289
|
+
await this.hookRunner.onSessionEnd(session, this.buildSessionEndMetadata(session, { success: false, error }));
|
|
290
|
+
}
|
|
291
|
+
// ─── Accessors for facades ──────────────────────────────────────────────────
|
|
292
|
+
/** Expose foundation services for facades that need direct access. */
|
|
293
|
+
get foundation() {
|
|
294
|
+
return {
|
|
295
|
+
toolExecutor: this.toolExecutor,
|
|
296
|
+
conversationState: this.conversationState,
|
|
297
|
+
eventLog: this.eventLog,
|
|
298
|
+
agentState: this.agentState,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
/** Expose hook runner for facades that need to fire transport-specific hooks. */
|
|
302
|
+
get hooks() {
|
|
303
|
+
return this.hookRunner;
|
|
304
|
+
}
|
|
305
|
+
// ─── Private: CapabilityHost construction ─────────────────────────────────
|
|
306
|
+
/**
|
|
307
|
+
* Build a CapabilityHost from agent config.
|
|
308
|
+
* Mirrors the logic in CapabilityCallWorker.buildHost() and
|
|
309
|
+
* AgentExecuteStage's host construction, unified into one place.
|
|
310
|
+
*/
|
|
311
|
+
buildCapabilityHost(agent, session) {
|
|
312
|
+
const host = new CapabilityHost();
|
|
313
|
+
let flowCapability;
|
|
314
|
+
// Flow capability — for flow agents with node graphs
|
|
315
|
+
if (isFlowAgent(agent) && agent.flow) {
|
|
316
|
+
const initialNode = agent.initialNode ?? agent.flow.nodes[0]?.id;
|
|
317
|
+
if (initialNode) {
|
|
318
|
+
flowCapability = new FlowCapability({
|
|
319
|
+
flow: agent.flow,
|
|
320
|
+
initialNode,
|
|
321
|
+
state: this.readPersistedFlowState(session, agent.id),
|
|
322
|
+
});
|
|
323
|
+
if (!flowCapability.isInitialized) {
|
|
324
|
+
flowCapability.initialize();
|
|
325
|
+
}
|
|
326
|
+
host.use(flowCapability);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// Handoff capability — for agents with canHandoffTo
|
|
330
|
+
if (agent.canHandoffTo?.length) {
|
|
331
|
+
const targets = agent.canHandoffTo
|
|
332
|
+
.map(id => this.agents.get(id))
|
|
333
|
+
.filter((a) => Boolean(a))
|
|
334
|
+
.map(a => ({ id: a.id, name: a.name ?? a.id, description: a.description }));
|
|
335
|
+
if (targets.length > 0) {
|
|
336
|
+
host.use(new HandoffCapability(targets));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// Extraction capability (pure capability-based, not ExtractionEngine-based)
|
|
340
|
+
if (agent.extraction) {
|
|
341
|
+
const ex = agent.extraction;
|
|
342
|
+
const usesExtractionEngine = !!(ex.systemPrompt || ex.memoryKey || ex.includeInSystemPrompt !== undefined);
|
|
343
|
+
if (!usesExtractionEngine) {
|
|
344
|
+
host.use(new ExtractionCapability({
|
|
345
|
+
schema: ex.schema,
|
|
346
|
+
requiredFields: ex.requiredFields,
|
|
347
|
+
}));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// Agent's own tools — convert to ToolDeclarations for the host
|
|
351
|
+
if (agent.tools) {
|
|
352
|
+
const toolDecls = Object.entries(agent.tools).map(([name, tool]) => ({
|
|
353
|
+
name,
|
|
354
|
+
description: tool.description ?? '',
|
|
355
|
+
parameters: (tool.inputSchema ?? tool.parameters),
|
|
356
|
+
execute: tool.execute,
|
|
357
|
+
}));
|
|
358
|
+
host.addTools(toolDecls);
|
|
359
|
+
}
|
|
360
|
+
return { host, flowCapability };
|
|
361
|
+
}
|
|
362
|
+
// ─── Private: Outcome construction ────────────────────────────────────────
|
|
363
|
+
async buildOutcome(handle, call, result, action) {
|
|
364
|
+
const outputForModel = action.type === 'continue' && action.toolResponseOverride !== undefined
|
|
365
|
+
? action.toolResponseOverride
|
|
366
|
+
: result;
|
|
367
|
+
const toolResponse = { id: call.id, name: call.name, output: outputForModel };
|
|
368
|
+
const base = { action, toolResponse };
|
|
369
|
+
switch (action.type) {
|
|
370
|
+
case 'reconfigure': {
|
|
371
|
+
// Flow transitioned — rebuild prompt and tools
|
|
372
|
+
const internals = this.getInternals(handle);
|
|
373
|
+
const systemInstruction = await this.promptAssembler.assemble({
|
|
374
|
+
host: internals.host,
|
|
375
|
+
basePrompt: internals.basePrompt,
|
|
376
|
+
session: handle.session,
|
|
377
|
+
memoryService: this.memoryService,
|
|
378
|
+
});
|
|
379
|
+
const tools = internals.host.getAllTools();
|
|
380
|
+
const geminiTools = toGeminiDeclarations(tools);
|
|
381
|
+
internals.host.markConfigured();
|
|
382
|
+
base.nextSystemPrompt = systemInstruction;
|
|
383
|
+
base.nextTools = tools;
|
|
384
|
+
base.nextRealtimeConfig = { systemInstruction, tools: geminiTools };
|
|
385
|
+
return base;
|
|
386
|
+
}
|
|
387
|
+
case 'handoff': {
|
|
388
|
+
// Fire handoff hook
|
|
389
|
+
const reason = action.reason ?? `Handoff to ${action.targetAgent}`;
|
|
390
|
+
await this.hookRunner.onHandoff(handle.context, handle.context.agentId, action.targetAgent, reason);
|
|
391
|
+
return base;
|
|
392
|
+
}
|
|
393
|
+
case 'end': {
|
|
394
|
+
// Fire agent-end hook
|
|
395
|
+
await this.hookRunner.onAgentEnd(handle.context, handle.context.agentId);
|
|
396
|
+
return base;
|
|
397
|
+
}
|
|
398
|
+
case 'continue':
|
|
399
|
+
case 'extraction-complete':
|
|
400
|
+
default:
|
|
401
|
+
return base;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// ─── Private: Extraction ──────────────────────────────────────────────────
|
|
405
|
+
async runExtraction(agent, context) {
|
|
406
|
+
// Only run if agent has extraction config and ExtractionEngine is available
|
|
407
|
+
const extractionConfig = this.extractionEngine.getExtractionConfig(agent, context.session, {
|
|
408
|
+
getFlowState: (session, agentId) => {
|
|
409
|
+
const raw = session.agentStates[agentId]?.state;
|
|
410
|
+
if (!raw || typeof raw !== 'object')
|
|
411
|
+
return undefined;
|
|
412
|
+
return raw;
|
|
413
|
+
},
|
|
414
|
+
getFlowNode: (flowAgent, nodeId) => {
|
|
415
|
+
if (!nodeId)
|
|
416
|
+
return undefined;
|
|
417
|
+
return flowAgent.flow.nodes.find(n => n.id === nodeId);
|
|
418
|
+
},
|
|
419
|
+
isFlowAgent,
|
|
420
|
+
});
|
|
421
|
+
if (!extractionConfig)
|
|
422
|
+
return;
|
|
423
|
+
// Get the last user input for extraction
|
|
424
|
+
const lastUserInput = this.getLastUserInput(context.session);
|
|
425
|
+
if (!lastUserInput)
|
|
426
|
+
return;
|
|
427
|
+
await this.extractionEngine.runTurnExtraction(agent, context, lastUserInput, {
|
|
428
|
+
getFlowState: (session, agentId) => {
|
|
429
|
+
const raw = session.agentStates[agentId]?.state;
|
|
430
|
+
if (!raw || typeof raw !== 'object')
|
|
431
|
+
return undefined;
|
|
432
|
+
return raw;
|
|
433
|
+
},
|
|
434
|
+
setFlowState: (session, agentId, state) => {
|
|
435
|
+
if (!session.agentStates[agentId]) {
|
|
436
|
+
session.agentStates[agentId] = { agentId, state: {}, lastActive: new Date() };
|
|
437
|
+
}
|
|
438
|
+
session.agentStates[agentId].state = state;
|
|
439
|
+
},
|
|
440
|
+
getFlowNode: (flowAgent, nodeId) => {
|
|
441
|
+
if (!nodeId)
|
|
442
|
+
return undefined;
|
|
443
|
+
return flowAgent.flow.nodes.find(n => n.id === nodeId);
|
|
444
|
+
},
|
|
445
|
+
isFlowAgent,
|
|
446
|
+
touchSession: (session) => this.conversationState.touchSession(session),
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
async runRealtimeKnowledgeRefresh(handle) {
|
|
450
|
+
const transcript = this.getLastUserInput(handle.session);
|
|
451
|
+
if (!transcript || transcript.length < 3)
|
|
452
|
+
return;
|
|
453
|
+
if (this.autoRetrieveProvider) {
|
|
454
|
+
try {
|
|
455
|
+
const result = await this.autoRetrieveProvider.run({
|
|
456
|
+
input: transcript,
|
|
457
|
+
context: handle.context,
|
|
458
|
+
});
|
|
459
|
+
if (result?.text) {
|
|
460
|
+
handle.session.workingMemory['knowledge:realtime'] = {
|
|
461
|
+
text: result.text.slice(0, 4000),
|
|
462
|
+
query: transcript,
|
|
463
|
+
retrievedAt: Date.now(),
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
catch (err) {
|
|
468
|
+
console.error('[OrchestrationAuthority] Knowledge refresh failed:', err);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
if (this.memoryService && handle.session.userId) {
|
|
472
|
+
try {
|
|
473
|
+
const { memories } = await this.memoryService.searchMemory({
|
|
474
|
+
userId: handle.session.userId,
|
|
475
|
+
query: transcript,
|
|
476
|
+
limit: 10,
|
|
477
|
+
});
|
|
478
|
+
if (memories.length > 0) {
|
|
479
|
+
handle.session.workingMemory['memory:realtime'] = {
|
|
480
|
+
memories: memories.slice(0, 5),
|
|
481
|
+
retrievedAt: Date.now(),
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
catch (err) {
|
|
486
|
+
console.error('[OrchestrationAuthority] Memory preload failed:', err);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
async runRealtimeExtractionRefinement(handle) {
|
|
491
|
+
if (!this.realtimeExtractionRunner)
|
|
492
|
+
return;
|
|
493
|
+
const internals = this.getInternals(handle);
|
|
494
|
+
const { flowCapability, agent } = internals;
|
|
495
|
+
if (!flowCapability || !isFlowAgent(agent))
|
|
496
|
+
return;
|
|
497
|
+
const node = flowCapability.currentExtractionNode();
|
|
498
|
+
if (!node)
|
|
499
|
+
return;
|
|
500
|
+
const transcript = this.buildExtractionTranscript(handle.session);
|
|
501
|
+
if (!transcript)
|
|
502
|
+
return;
|
|
503
|
+
const result = await this.realtimeExtractionRunner.runPostTurnExtraction({
|
|
504
|
+
schema: node.extractionSchema,
|
|
505
|
+
requiredFields: this.getRequiredExtractionFields(node),
|
|
506
|
+
userTranscript: transcript,
|
|
507
|
+
currentCollectedData: flowCapability.collectedData,
|
|
508
|
+
telemetry: agent.telemetry,
|
|
509
|
+
});
|
|
510
|
+
flowCapability.updateCollectedData(result.extractedFields);
|
|
511
|
+
this.syncFlowStateToSession(handle);
|
|
512
|
+
}
|
|
513
|
+
// ─── Private: Memory ingestion ────────────────────────────────────────────
|
|
514
|
+
async runMemoryIngestion(context, session) {
|
|
515
|
+
if (!this.memoryService)
|
|
516
|
+
return;
|
|
517
|
+
if (!session.userId)
|
|
518
|
+
return;
|
|
519
|
+
if (this.memoryIngestion === 'onEnd') {
|
|
520
|
+
// Automatic ingestion
|
|
521
|
+
try {
|
|
522
|
+
await this.memoryService.addSessionToMemory(session);
|
|
523
|
+
}
|
|
524
|
+
catch (err) {
|
|
525
|
+
console.error('[OrchestrationAuthority] Memory ingestion failed:', err);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
else if (this.memoryIngestion === 'hook') {
|
|
529
|
+
// Hook-controlled ingestion — the onMemoryIngest hook can signal skip
|
|
530
|
+
// by returning false. HookRunner.run() doesn't return the hook value,
|
|
531
|
+
// so we call the hook directly for this special case.
|
|
532
|
+
let shouldIngest = true;
|
|
533
|
+
const hooks = this.hookRunner.getHooks();
|
|
534
|
+
if (hooks.onMemoryIngest) {
|
|
535
|
+
const result = await hooks.onMemoryIngest(context, session);
|
|
536
|
+
if (result === false)
|
|
537
|
+
shouldIngest = false;
|
|
538
|
+
}
|
|
539
|
+
if (shouldIngest) {
|
|
540
|
+
try {
|
|
541
|
+
await this.memoryService.addSessionToMemory(session);
|
|
542
|
+
if (hooks.onMemoryIngested) {
|
|
543
|
+
await hooks.onMemoryIngested(context, session);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
catch (err) {
|
|
547
|
+
console.error('[OrchestrationAuthority] Memory ingestion failed:', err);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
// 'manual' — no automatic ingestion
|
|
552
|
+
}
|
|
553
|
+
// ─── Private: Persistence ─────────────────────────────────────────────────
|
|
554
|
+
async persistWithRetry(session) {
|
|
555
|
+
for (let attempt = 1; attempt <= this.maxPersistRetries; attempt++) {
|
|
556
|
+
try {
|
|
557
|
+
await this.conversationState.save(session);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
catch (error) {
|
|
561
|
+
if (attempt === this.maxPersistRetries) {
|
|
562
|
+
// Fire persistence error hook if available
|
|
563
|
+
await this.hookRunner.run('onPersistenceError', session, error);
|
|
564
|
+
console.error('[OrchestrationAuthority] Persistence failed after retries:', error);
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
// Exponential backoff
|
|
568
|
+
await new Promise(r => setTimeout(r, 100 * Math.pow(2, attempt)));
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
// ─── Private: Helpers ─────────────────────────────────────────────────────
|
|
573
|
+
buildSessionEndMetadata(session, result) {
|
|
574
|
+
const now = Date.now();
|
|
575
|
+
const durationMs = now - session.createdAt.getTime();
|
|
576
|
+
return {
|
|
577
|
+
success: result?.success ?? true,
|
|
578
|
+
endReason: result?.error?.message,
|
|
579
|
+
durationMs,
|
|
580
|
+
turnCount: session.metadata?.totalSteps,
|
|
581
|
+
lastAgentId: session.activeAgentId ?? session.currentAgent,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
getInternals(handle) {
|
|
585
|
+
return handle._internals;
|
|
586
|
+
}
|
|
587
|
+
readPersistedFlowState(session, agentId) {
|
|
588
|
+
const raw = session?.agentStates[agentId]?.state;
|
|
589
|
+
if (!raw || typeof raw !== 'object')
|
|
590
|
+
return undefined;
|
|
591
|
+
const candidate = raw;
|
|
592
|
+
if (typeof candidate.context !== 'object' || candidate.context === null)
|
|
593
|
+
return undefined;
|
|
594
|
+
return {
|
|
595
|
+
context: candidate.context,
|
|
596
|
+
initialized: candidate.initialized === true,
|
|
597
|
+
flowEnded: candidate.flowEnded === true,
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
syncFlowStateToSession(handle) {
|
|
601
|
+
const { flowCapability } = this.getInternals(handle);
|
|
602
|
+
if (!flowCapability)
|
|
603
|
+
return;
|
|
604
|
+
this.agentState.updateAgentState(handle.session, handle.context.agentId, flowCapability.getState());
|
|
605
|
+
this.conversationState.touchSession(handle.session);
|
|
606
|
+
}
|
|
607
|
+
async resolveToolArgs(handle, toolName, args) {
|
|
608
|
+
const verified = await this.verifyExtractionSubmission(handle, toolName, args);
|
|
609
|
+
return verified ?? args;
|
|
610
|
+
}
|
|
611
|
+
async verifyExtractionSubmission(handle, toolName, args) {
|
|
612
|
+
const internals = this.getInternals(handle);
|
|
613
|
+
const flowCapability = internals.flowCapability;
|
|
614
|
+
if (!flowCapability || !isFlowAgent(internals.agent))
|
|
615
|
+
return null;
|
|
616
|
+
const currentNodeId = flowCapability.currentNode;
|
|
617
|
+
if (!currentNodeId)
|
|
618
|
+
return null;
|
|
619
|
+
const node = internals.agent.flow.nodes.find((entry) => entry.id === currentNodeId);
|
|
620
|
+
if (!node || !isExtractionNode(node))
|
|
621
|
+
return null;
|
|
622
|
+
if (toolName !== `submit_${node.id}_data`)
|
|
623
|
+
return null;
|
|
624
|
+
const transcript = this.buildExtractionTranscript(handle.session);
|
|
625
|
+
if (!transcript)
|
|
626
|
+
return {};
|
|
627
|
+
if (this.realtimeExtractionRunner) {
|
|
628
|
+
const result = await this.realtimeExtractionRunner.runPostTurnExtraction({
|
|
629
|
+
schema: node.extractionSchema,
|
|
630
|
+
requiredFields: this.getRequiredExtractionFields(node),
|
|
631
|
+
userTranscript: transcript,
|
|
632
|
+
currentCollectedData: {},
|
|
633
|
+
telemetry: internals.agent.telemetry,
|
|
634
|
+
});
|
|
635
|
+
if (Object.keys(result.extractedFields).length > 0) {
|
|
636
|
+
return result.extractedFields;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return this.filterExtractionArgsAgainstTranscript(args, transcript);
|
|
640
|
+
}
|
|
641
|
+
buildExtractionTranscript(session) {
|
|
642
|
+
const recentUserMessages = session.messages
|
|
643
|
+
.filter((msg) => msg.role === 'user')
|
|
644
|
+
.slice(-6)
|
|
645
|
+
.map((msg) => this.getMessageText(msg.content))
|
|
646
|
+
.filter((text) => typeof text === 'string' && text.trim().length > 0);
|
|
647
|
+
return recentUserMessages.join('\n');
|
|
648
|
+
}
|
|
649
|
+
getMessageText(content) {
|
|
650
|
+
if (typeof content === 'string')
|
|
651
|
+
return content;
|
|
652
|
+
if (!Array.isArray(content))
|
|
653
|
+
return null;
|
|
654
|
+
const text = content
|
|
655
|
+
.filter((part) => typeof part === 'object' && part !== null && 'type' in part)
|
|
656
|
+
.filter((part) => part.type === 'text' && typeof part.text === 'string')
|
|
657
|
+
.map((part) => part.text ?? '')
|
|
658
|
+
.join(' ')
|
|
659
|
+
.trim();
|
|
660
|
+
return text.length > 0 ? text : null;
|
|
661
|
+
}
|
|
662
|
+
toRecord(value) {
|
|
663
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
664
|
+
return {};
|
|
665
|
+
}
|
|
666
|
+
return value;
|
|
667
|
+
}
|
|
668
|
+
getRequiredExtractionFields(node) {
|
|
669
|
+
const requiredFields = node.extractionRequiredFields;
|
|
670
|
+
if (requiredFields && requiredFields.length > 0) {
|
|
671
|
+
return requiredFields;
|
|
672
|
+
}
|
|
673
|
+
const schema = node.extractionSchema;
|
|
674
|
+
return Object.keys(schema.shape ?? {});
|
|
675
|
+
}
|
|
676
|
+
filterExtractionArgsAgainstTranscript(args, transcript) {
|
|
677
|
+
const submitted = this.toRecord(args);
|
|
678
|
+
const accepted = {};
|
|
679
|
+
const normalizedTranscript = normalizeExtractionText(transcript);
|
|
680
|
+
for (const [key, value] of Object.entries(submitted)) {
|
|
681
|
+
if (typeof value === 'string') {
|
|
682
|
+
const normalizedValue = normalizeExtractionText(value);
|
|
683
|
+
if (!normalizedValue)
|
|
684
|
+
continue;
|
|
685
|
+
if (isSuspiciousExtractionPlaceholder(normalizedValue))
|
|
686
|
+
continue;
|
|
687
|
+
if (normalizedTranscript.includes(normalizedValue)
|
|
688
|
+
|| normalizedValue.includes(normalizedTranscript)) {
|
|
689
|
+
accepted[key] = value;
|
|
690
|
+
}
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
694
|
+
const normalizedValue = normalizeExtractionText(String(value));
|
|
695
|
+
if (normalizedTranscript.includes(normalizedValue)) {
|
|
696
|
+
accepted[key] = value;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return accepted;
|
|
701
|
+
}
|
|
702
|
+
buildToolCallRecord(id, name, args, result, success, error) {
|
|
703
|
+
return {
|
|
704
|
+
toolCallId: id,
|
|
705
|
+
toolName: name,
|
|
706
|
+
args,
|
|
707
|
+
result,
|
|
708
|
+
error,
|
|
709
|
+
success,
|
|
710
|
+
timestamp: Date.now(),
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Determine whether a tool is a capability tool (flow transition, extraction)
|
|
715
|
+
* or a regular agent tool. Capability tools have self-contained execute()
|
|
716
|
+
* and bypass ToolExecutor enforcement.
|
|
717
|
+
*/
|
|
718
|
+
isCapabilityTool(name, _tool, agent) {
|
|
719
|
+
// Extraction submit tools follow this naming pattern
|
|
720
|
+
if (name.startsWith('submit_') && name.endsWith('_data'))
|
|
721
|
+
return true;
|
|
722
|
+
// Flow transition tools are not in agent.tools
|
|
723
|
+
if (agent.tools && name in agent.tools)
|
|
724
|
+
return false;
|
|
725
|
+
// Auto-retrieve tools
|
|
726
|
+
if (name === 'search_knowledge' || name === 'auto_retrieve')
|
|
727
|
+
return true;
|
|
728
|
+
// Handoff tools
|
|
729
|
+
if (name === 'transfer_to_agent' || name.startsWith('route_to_'))
|
|
730
|
+
return true;
|
|
731
|
+
// Default: if not in agent.tools, it's likely a capability tool
|
|
732
|
+
return !agent.tools || !(name in agent.tools);
|
|
733
|
+
}
|
|
734
|
+
getLastUserInput(session) {
|
|
735
|
+
for (let i = session.messages.length - 1; i >= 0; i--) {
|
|
736
|
+
const msg = session.messages[i];
|
|
737
|
+
if (msg.role === 'user') {
|
|
738
|
+
// Extract text content from user message
|
|
739
|
+
if (typeof msg.content === 'string')
|
|
740
|
+
return msg.content;
|
|
741
|
+
if (Array.isArray(msg.content)) {
|
|
742
|
+
const textPart = msg.content.find((p) => p.type === 'text');
|
|
743
|
+
if (textPart && typeof textPart.text === 'string')
|
|
744
|
+
return textPart.text;
|
|
745
|
+
}
|
|
746
|
+
return null;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return null;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
753
|
+
/** Resolve agent prompt from AgentConfig.prompt (string or AgentPrompt). */
|
|
754
|
+
function resolveAgentPrompt(agent) {
|
|
755
|
+
const raw = agent.prompt;
|
|
756
|
+
if (!raw)
|
|
757
|
+
return '';
|
|
758
|
+
if (typeof raw === 'string')
|
|
759
|
+
return raw;
|
|
760
|
+
// AgentPrompt with sections
|
|
761
|
+
if ('sections' in raw && Array.isArray(raw.sections)) {
|
|
762
|
+
return raw.sections.map((s) => s.content).join('\n\n');
|
|
763
|
+
}
|
|
764
|
+
return String(raw);
|
|
765
|
+
}
|
|
766
|
+
/** Type guard for flow agents. */
|
|
767
|
+
function isFlowAgent(agent) {
|
|
768
|
+
return agent.type === 'flow' || ('flow' in agent && !!agent.flow);
|
|
769
|
+
}
|
|
770
|
+
function normalizeExtractionText(value) {
|
|
771
|
+
return value
|
|
772
|
+
.toLowerCase()
|
|
773
|
+
.replace(/[^\p{L}\p{N}]+/gu, ' ')
|
|
774
|
+
.replace(/\s+/g, ' ')
|
|
775
|
+
.trim();
|
|
776
|
+
}
|
|
777
|
+
function isSuspiciousExtractionPlaceholder(value) {
|
|
778
|
+
return /^(unknown|not provided|not sure|n a|none|null)$/.test(value)
|
|
779
|
+
|| value.includes('user mentioned')
|
|
780
|
+
|| value.includes('caller mentioned')
|
|
781
|
+
|| value.includes('already provided')
|
|
782
|
+
|| value.includes('previous detail')
|
|
783
|
+
|| value.includes('in sinhala')
|
|
784
|
+
|| value.includes('in tamil');
|
|
785
|
+
}
|
|
786
|
+
//# sourceMappingURL=DefaultOrchestrationAuthority.js.map
|