@framers/agentos 0.1.49 → 0.1.51
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/api/AgentOS.d.ts +1 -53
- package/dist/api/AgentOS.d.ts.map +1 -1
- package/dist/api/AgentOS.js +5 -256
- package/dist/api/AgentOS.js.map +1 -1
- package/dist/api/AgentOSOrchestrator.d.ts +5 -276
- package/dist/api/AgentOSOrchestrator.d.ts.map +1 -1
- package/dist/api/AgentOSOrchestrator.js +107 -772
- package/dist/api/AgentOSOrchestrator.js.map +1 -1
- package/dist/api/StreamChunkEmitter.d.ts +32 -0
- package/dist/api/StreamChunkEmitter.d.ts.map +1 -0
- package/dist/api/StreamChunkEmitter.js +141 -0
- package/dist/api/StreamChunkEmitter.js.map +1 -0
- package/dist/api/TaskOutcomeTelemetryManager.d.ts +107 -0
- package/dist/api/TaskOutcomeTelemetryManager.d.ts.map +1 -0
- package/dist/api/TaskOutcomeTelemetryManager.js +331 -0
- package/dist/api/TaskOutcomeTelemetryManager.js.map +1 -0
- package/dist/api/errors.d.ts +17 -0
- package/dist/api/errors.d.ts.map +1 -0
- package/dist/api/errors.js +25 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/turn-phases/conversation-history.d.ts +22 -0
- package/dist/api/turn-phases/conversation-history.d.ts.map +1 -0
- package/dist/api/turn-phases/conversation-history.js +55 -0
- package/dist/api/turn-phases/conversation-history.js.map +1 -0
- package/dist/api/turn-phases/index.d.ts +5 -0
- package/dist/api/turn-phases/index.d.ts.map +1 -0
- package/dist/api/turn-phases/index.js +5 -0
- package/dist/api/turn-phases/index.js.map +1 -0
- package/dist/api/turn-phases/long-term-memory.d.ts +35 -0
- package/dist/api/turn-phases/long-term-memory.d.ts.map +1 -0
- package/dist/api/turn-phases/long-term-memory.js +82 -0
- package/dist/api/turn-phases/long-term-memory.js.map +1 -0
- package/dist/api/turn-phases/prompt-profile.d.ts +22 -0
- package/dist/api/turn-phases/prompt-profile.d.ts.map +1 -0
- package/dist/api/turn-phases/prompt-profile.js +34 -0
- package/dist/api/turn-phases/prompt-profile.js.map +1 -0
- package/dist/api/turn-phases/rolling-summary.d.ts +28 -0
- package/dist/api/turn-phases/rolling-summary.d.ts.map +1 -0
- package/dist/api/turn-phases/rolling-summary.js +87 -0
- package/dist/api/turn-phases/rolling-summary.js.map +1 -0
- package/dist/api/types/OrchestratorConfig.d.ts +89 -0
- package/dist/api/types/OrchestratorConfig.d.ts.map +1 -0
- package/dist/api/types/OrchestratorConfig.js +6 -0
- package/dist/api/types/OrchestratorConfig.js.map +1 -0
- package/dist/channels/adapters/TelegramChannelAdapter.d.ts +12 -0
- package/dist/channels/adapters/TelegramChannelAdapter.d.ts.map +1 -1
- package/dist/channels/adapters/TelegramChannelAdapter.js +57 -2
- package/dist/channels/adapters/TelegramChannelAdapter.js.map +1 -1
- package/dist/core/agents/index.d.ts +7 -0
- package/dist/core/agents/index.d.ts.map +1 -0
- package/dist/core/agents/index.js +7 -0
- package/dist/core/agents/index.js.map +1 -0
- package/dist/core/ai_utilities/HybridUtilityAI.d.ts +54 -0
- package/dist/core/ai_utilities/HybridUtilityAI.d.ts.map +1 -1
- package/dist/core/ai_utilities/HybridUtilityAI.js +119 -1
- package/dist/core/ai_utilities/HybridUtilityAI.js.map +1 -1
- package/dist/core/ai_utilities/index.d.ts +5 -0
- package/dist/core/ai_utilities/index.d.ts.map +1 -0
- package/dist/core/ai_utilities/index.js +5 -0
- package/dist/core/ai_utilities/index.js.map +1 -0
- package/dist/core/audio/index.d.ts +5 -0
- package/dist/core/audio/index.d.ts.map +1 -0
- package/dist/core/audio/index.js +5 -0
- package/dist/core/audio/index.js.map +1 -0
- package/dist/core/conversation/index.d.ts +8 -0
- package/dist/core/conversation/index.d.ts.map +1 -0
- package/dist/core/conversation/index.js +8 -0
- package/dist/core/conversation/index.js.map +1 -0
- package/dist/core/index.d.ts +45 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +66 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/orchestration/index.d.ts +6 -0
- package/dist/core/orchestration/index.d.ts.map +1 -0
- package/dist/core/orchestration/index.js +6 -0
- package/dist/core/orchestration/index.js.map +1 -0
- package/dist/core/streaming/AsyncStreamClientBridge.d.ts +29 -0
- package/dist/core/streaming/AsyncStreamClientBridge.d.ts.map +1 -0
- package/dist/core/streaming/AsyncStreamClientBridge.js +130 -0
- package/dist/core/streaming/AsyncStreamClientBridge.js.map +1 -0
- package/dist/core/streaming/index.d.ts +4 -0
- package/dist/core/streaming/index.d.ts.map +1 -0
- package/dist/core/streaming/index.js +4 -0
- package/dist/core/streaming/index.js.map +1 -0
- package/dist/core/tools/index.d.ts +13 -0
- package/dist/core/tools/index.d.ts.map +1 -0
- package/dist/core/tools/index.js +8 -0
- package/dist/core/tools/index.js.map +1 -0
- package/dist/speech/providers/BuiltInAdaptiveVadProvider.d.ts +1 -1
- package/package.json +1 -1
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
* coordinating tool execution and result feedback.
|
|
8
8
|
* @module backend/agentos/api/AgentOSOrchestrator
|
|
9
9
|
*/
|
|
10
|
+
import { TaskOutcomeTelemetryManager, evaluateTaskOutcome, resolveTaskOutcomeTelemetryConfig, resolveAdaptiveExecutionConfig, } from './TaskOutcomeTelemetryManager.js';
|
|
11
|
+
import { StreamChunkEmitter } from './StreamChunkEmitter.js';
|
|
12
|
+
import { executeRollingSummaryPhase } from './turn-phases/rolling-summary.js';
|
|
13
|
+
import { executePromptProfilePhase } from './turn-phases/prompt-profile.js';
|
|
14
|
+
import { executeLongTermMemoryPhase } from './turn-phases/long-term-memory.js';
|
|
15
|
+
import { assembleConversationHistory } from './turn-phases/conversation-history.js';
|
|
10
16
|
import { AgentOSResponseChunkType, } from './types/AgentOSResponse.js';
|
|
11
17
|
import { GMIInteractionType, // Added for GMITurnInput
|
|
12
18
|
GMIOutputChunkType, // Added for comparisons
|
|
@@ -15,10 +21,13 @@ import { MessageRole } from '../core/conversation/ConversationMessage.js';
|
|
|
15
21
|
import { uuidv4 } from '../utils/uuid.js';
|
|
16
22
|
import { GMIError, GMIErrorCode } from '../utils/errors.js';
|
|
17
23
|
import { normalizeUsage, snapshotPersonaDetails } from '../core/orchestration/helpers.js';
|
|
18
|
-
import { DEFAULT_PROMPT_PROFILE_CONFIG,
|
|
19
|
-
import { DEFAULT_ROLLING_SUMMARY_COMPACTION_CONFIG,
|
|
24
|
+
import { DEFAULT_PROMPT_PROFILE_CONFIG, } from '../core/prompting/PromptProfileRouter.js';
|
|
25
|
+
import { DEFAULT_ROLLING_SUMMARY_COMPACTION_CONFIG, } from '../core/conversation/RollingSummaryCompactor.js';
|
|
20
26
|
import { DEFAULT_LONG_TERM_MEMORY_POLICY, hasAnyLongTermMemoryScope, LONG_TERM_MEMORY_POLICY_METADATA_KEY, resolveLongTermMemoryPolicy, } from '../core/conversation/LongTermMemoryPolicy.js';
|
|
21
|
-
import {
|
|
27
|
+
import { recordAgentOSToolResultMetrics, recordAgentOSTurnMetrics, recordExceptionOnActiveSpan, runWithSpanContext, startAgentOSSpan, withAgentOSSpan, } from '../core/observability/otel.js';
|
|
28
|
+
// TaskOutcomeKpiWindowEntry imported from types/OrchestratorConfig.ts
|
|
29
|
+
// AdaptiveExecutionDecision imported from TaskOutcomeTelemetryManager
|
|
30
|
+
// ITaskOutcomeTelemetryStore imported from types/OrchestratorConfig.ts
|
|
22
31
|
const RECALL_PROFILE_DEFAULTS = {
|
|
23
32
|
aggressive: {
|
|
24
33
|
cadenceTurns: 2,
|
|
@@ -39,22 +48,6 @@ const RECALL_PROFILE_DEFAULTS = {
|
|
|
39
48
|
topKByScope: { user: 4, persona: 4, organization: 4 },
|
|
40
49
|
},
|
|
41
50
|
};
|
|
42
|
-
function normalizeMode(value) {
|
|
43
|
-
return (value || '').trim().toLowerCase();
|
|
44
|
-
}
|
|
45
|
-
function pickByMode(map, mode) {
|
|
46
|
-
if (!map || Object.keys(map).length === 0)
|
|
47
|
-
return null;
|
|
48
|
-
const modeNorm = normalizeMode(mode);
|
|
49
|
-
const exact = map[modeNorm];
|
|
50
|
-
if (exact)
|
|
51
|
-
return exact;
|
|
52
|
-
const patternMatch = Object.entries(map)
|
|
53
|
-
.map(([key, value]) => ({ key: normalizeMode(key), value }))
|
|
54
|
-
.filter(({ key }) => key && (modeNorm === key || modeNorm.startsWith(key) || modeNorm.includes(key)))
|
|
55
|
-
.sort((a, b) => b.key.length - a.key.length)[0];
|
|
56
|
-
return patternMatch?.value ?? null;
|
|
57
|
-
}
|
|
58
51
|
function renderPlainText(markdown) {
|
|
59
52
|
let text = String(markdown ?? '');
|
|
60
53
|
if (!text.trim())
|
|
@@ -82,92 +75,8 @@ function renderPlainText(markdown) {
|
|
|
82
75
|
text = text.replace(/\n{3,}/g, '\n\n');
|
|
83
76
|
return text.trim();
|
|
84
77
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return null;
|
|
88
|
-
const read = (keys) => {
|
|
89
|
-
for (const key of keys) {
|
|
90
|
-
if (Object.prototype.hasOwnProperty.call(customFlags, key))
|
|
91
|
-
return customFlags[key];
|
|
92
|
-
}
|
|
93
|
-
return undefined;
|
|
94
|
-
};
|
|
95
|
-
const raw = read(['taskOutcome', 'task_outcome', 'taskSuccess', 'task_success']);
|
|
96
|
-
if (typeof raw === 'boolean') {
|
|
97
|
-
return {
|
|
98
|
-
status: raw ? 'success' : 'failed',
|
|
99
|
-
score: raw ? 1 : 0,
|
|
100
|
-
reason: raw ? 'Caller marked task successful.' : 'Caller marked task failed.',
|
|
101
|
-
source: 'request_override',
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
if (typeof raw === 'number' && Number.isFinite(raw)) {
|
|
105
|
-
const score = Math.max(0, Math.min(1, raw));
|
|
106
|
-
return {
|
|
107
|
-
status: score >= 0.8 ? 'success' : score >= 0.4 ? 'partial' : 'failed',
|
|
108
|
-
score,
|
|
109
|
-
reason: 'Caller supplied numeric task outcome score.',
|
|
110
|
-
source: 'request_override',
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
if (typeof raw !== 'string' || !raw.trim())
|
|
114
|
-
return null;
|
|
115
|
-
const normalized = raw.trim().toLowerCase().replace(/\s+/g, '_').replace(/-/g, '_');
|
|
116
|
-
if (normalized === 'success' ||
|
|
117
|
-
normalized === 'succeeded' ||
|
|
118
|
-
normalized === 'done' ||
|
|
119
|
-
normalized === 'completed' ||
|
|
120
|
-
normalized === 'true') {
|
|
121
|
-
return {
|
|
122
|
-
status: 'success',
|
|
123
|
-
score: 1,
|
|
124
|
-
reason: 'Caller marked task successful.',
|
|
125
|
-
source: 'request_override',
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
if (normalized === 'partial' ||
|
|
129
|
-
normalized === 'incomplete' ||
|
|
130
|
-
normalized === 'needs_followup') {
|
|
131
|
-
return {
|
|
132
|
-
status: 'partial',
|
|
133
|
-
score: 0.5,
|
|
134
|
-
reason: 'Caller marked task partially completed.',
|
|
135
|
-
source: 'request_override',
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
if (normalized === 'failed' ||
|
|
139
|
-
normalized === 'failure' ||
|
|
140
|
-
normalized === 'error' ||
|
|
141
|
-
normalized === 'false') {
|
|
142
|
-
return {
|
|
143
|
-
status: 'failed',
|
|
144
|
-
score: 0,
|
|
145
|
-
reason: 'Caller marked task failed.',
|
|
146
|
-
source: 'request_override',
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
function normalizeRequestedToolFailureMode(customFlags) {
|
|
152
|
-
if (!customFlags)
|
|
153
|
-
return null;
|
|
154
|
-
const read = (keys) => {
|
|
155
|
-
for (const key of keys) {
|
|
156
|
-
if (Object.prototype.hasOwnProperty.call(customFlags, key))
|
|
157
|
-
return customFlags[key];
|
|
158
|
-
}
|
|
159
|
-
return undefined;
|
|
160
|
-
};
|
|
161
|
-
const raw = read(['toolFailureMode', 'tool_failure_mode', 'failureMode', 'failMode']);
|
|
162
|
-
if (typeof raw !== 'string')
|
|
163
|
-
return null;
|
|
164
|
-
const normalized = raw.trim().toLowerCase().replace(/\s+/g, '_').replace(/-/g, '_');
|
|
165
|
-
if (normalized === 'fail_open' || normalized === 'open')
|
|
166
|
-
return 'fail_open';
|
|
167
|
-
if (normalized === 'fail_closed' || normalized === 'closed')
|
|
168
|
-
return 'fail_closed';
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
78
|
+
// normalizeTaskOutcomeOverride, normalizeRequestedToolFailureMode
|
|
79
|
+
// moved to TaskOutcomeTelemetryManager
|
|
171
80
|
function clampInteger(value, fallback, min, max) {
|
|
172
81
|
const num = Number(value);
|
|
173
82
|
if (!Number.isFinite(num))
|
|
@@ -206,84 +115,6 @@ function resolveTenantRoutingConfig(config) {
|
|
|
206
115
|
strictOrganizationIsolation: Boolean(config?.strictOrganizationIsolation),
|
|
207
116
|
};
|
|
208
117
|
}
|
|
209
|
-
function resolveTaskOutcomeTelemetryConfig(config) {
|
|
210
|
-
const scope = config?.scope === 'global' || config?.scope === 'organization'
|
|
211
|
-
? config.scope
|
|
212
|
-
: 'organization_persona';
|
|
213
|
-
return {
|
|
214
|
-
enabled: config?.enabled !== false,
|
|
215
|
-
rollingWindowSize: clampInteger(config?.rollingWindowSize, 100, 5, 5000),
|
|
216
|
-
scope,
|
|
217
|
-
emitAlerts: config?.emitAlerts !== false,
|
|
218
|
-
alertBelowWeightedSuccessRate: Math.max(0, Math.min(1, Number(config?.alertBelowWeightedSuccessRate ?? 0.55))),
|
|
219
|
-
alertMinSamples: clampInteger(config?.alertMinSamples, 8, 1, 10000),
|
|
220
|
-
alertCooldownMs: clampInteger(config?.alertCooldownMs, 60000, 0, 86400000),
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
function resolveAdaptiveExecutionConfig(config) {
|
|
224
|
-
return {
|
|
225
|
-
enabled: config?.enabled !== false,
|
|
226
|
-
minSamples: clampInteger(config?.minSamples, 5, 1, 1000),
|
|
227
|
-
minWeightedSuccessRate: Math.max(0, Math.min(1, Number(config?.minWeightedSuccessRate ?? 0.7))),
|
|
228
|
-
forceAllToolsWhenDegraded: config?.forceAllToolsWhenDegraded !== false,
|
|
229
|
-
forceFailOpenWhenDegraded: config?.forceFailOpenWhenDegraded !== false,
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
function sanitizeKpiEntry(raw) {
|
|
233
|
-
const status = raw?.status;
|
|
234
|
-
const validStatus = status === 'success' || status === 'partial' || status === 'failed' ? status : null;
|
|
235
|
-
if (!validStatus)
|
|
236
|
-
return null;
|
|
237
|
-
const scoreNum = Number(raw?.score);
|
|
238
|
-
const timestampNum = Number(raw?.timestamp);
|
|
239
|
-
if (!Number.isFinite(scoreNum) || !Number.isFinite(timestampNum))
|
|
240
|
-
return null;
|
|
241
|
-
return {
|
|
242
|
-
status: validStatus,
|
|
243
|
-
score: Math.max(0, Math.min(1, scoreNum)),
|
|
244
|
-
timestamp: Math.max(0, Math.trunc(timestampNum)),
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
function evaluateTaskOutcome(args) {
|
|
248
|
-
const override = normalizeTaskOutcomeOverride(args.customFlags);
|
|
249
|
-
if (override)
|
|
250
|
-
return override;
|
|
251
|
-
if (args.didForceTerminate || args.finalOutput.error) {
|
|
252
|
-
return {
|
|
253
|
-
status: 'failed',
|
|
254
|
-
score: 0,
|
|
255
|
-
reason: args.didForceTerminate
|
|
256
|
-
? 'Turn force-terminated due to iteration cap.'
|
|
257
|
-
: 'Final response contains an error payload.',
|
|
258
|
-
source: 'heuristic',
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
const text = typeof args.finalOutput.responseText === 'string'
|
|
262
|
-
? args.finalOutput.responseText.trim()
|
|
263
|
-
: '';
|
|
264
|
-
if (text.length >= 48) {
|
|
265
|
-
return {
|
|
266
|
-
status: 'success',
|
|
267
|
-
score: args.degraded ? 0.85 : 0.95,
|
|
268
|
-
reason: 'Final response was produced without terminal errors.',
|
|
269
|
-
source: 'heuristic',
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
if (text.length > 0 || (args.finalOutput.toolCalls?.length ?? 0) > 0) {
|
|
273
|
-
return {
|
|
274
|
-
status: 'partial',
|
|
275
|
-
score: args.degraded ? 0.5 : 0.6,
|
|
276
|
-
reason: 'Turn completed but produced a limited final response.',
|
|
277
|
-
source: 'heuristic',
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
return {
|
|
281
|
-
status: 'failed',
|
|
282
|
-
score: 0.1,
|
|
283
|
-
reason: 'No usable final response was produced.',
|
|
284
|
-
source: 'heuristic',
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
118
|
/**
|
|
288
119
|
* @class AgentOSOrchestrator
|
|
289
120
|
* @description
|
|
@@ -296,8 +127,6 @@ function evaluateTaskOutcome(args) {
|
|
|
296
127
|
export class AgentOSOrchestrator {
|
|
297
128
|
constructor() {
|
|
298
129
|
this.initialized = false;
|
|
299
|
-
this.taskOutcomeKpiWindows = new Map();
|
|
300
|
-
this.taskOutcomeAlertState = new Map();
|
|
301
130
|
/**
|
|
302
131
|
* A map to hold ongoing stream contexts.
|
|
303
132
|
* Key: streamId (generated by orchestrator for this interaction flow).
|
|
@@ -343,30 +172,10 @@ export class AgentOSOrchestrator {
|
|
|
343
172
|
taskOutcomeTelemetry: resolveTaskOutcomeTelemetryConfig(config.taskOutcomeTelemetry),
|
|
344
173
|
adaptiveExecution: resolveAdaptiveExecutionConfig(config.adaptiveExecution),
|
|
345
174
|
};
|
|
346
|
-
this.taskOutcomeKpiWindows.clear();
|
|
347
|
-
this.taskOutcomeAlertState.clear();
|
|
348
175
|
this.dependencies = dependencies;
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
const cap = this.config.taskOutcomeTelemetry.rollingWindowSize;
|
|
353
|
-
for (const [scopeKey, rawEntries] of Object.entries(persisted ?? {})) {
|
|
354
|
-
if (!Array.isArray(rawEntries))
|
|
355
|
-
continue;
|
|
356
|
-
const normalized = rawEntries
|
|
357
|
-
.map((entry) => sanitizeKpiEntry(entry))
|
|
358
|
-
.filter((entry) => Boolean(entry))
|
|
359
|
-
.sort((a, b) => a.timestamp - b.timestamp);
|
|
360
|
-
if (normalized.length === 0)
|
|
361
|
-
continue;
|
|
362
|
-
const trimmed = normalized.slice(Math.max(0, normalized.length - cap));
|
|
363
|
-
this.taskOutcomeKpiWindows.set(scopeKey, trimmed);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
catch (error) {
|
|
367
|
-
console.warn('AgentOSOrchestrator: Failed to load persisted task outcome telemetry windows; continuing with empty windows.', error);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
176
|
+
this.chunks = new StreamChunkEmitter(dependencies.streamingManager, this.activeStreamContexts);
|
|
177
|
+
this.telemetry = new TaskOutcomeTelemetryManager(this.config.taskOutcomeTelemetry, this.config.adaptiveExecution, dependencies.taskOutcomeTelemetryStore);
|
|
178
|
+
await this.telemetry.loadPersistedWindows();
|
|
370
179
|
this.initialized = true;
|
|
371
180
|
console.log('AgentOSOrchestrator initialized.');
|
|
372
181
|
}
|
|
@@ -403,304 +212,9 @@ export class AgentOSOrchestrator {
|
|
|
403
212
|
}
|
|
404
213
|
return inbound;
|
|
405
214
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
const persona = normalizeOrganizationId(args.personaId) ?? 'unknown';
|
|
410
|
-
if (scope === 'global')
|
|
411
|
-
return 'global';
|
|
412
|
-
if (scope === 'organization')
|
|
413
|
-
return `org:${org}`;
|
|
414
|
-
return `org:${org}|persona:${persona}`;
|
|
415
|
-
}
|
|
416
|
-
updateTaskOutcomeKpi(args) {
|
|
417
|
-
const telemetry = this.config.taskOutcomeTelemetry;
|
|
418
|
-
if (!telemetry.enabled)
|
|
419
|
-
return null;
|
|
420
|
-
const scopeKey = this.resolveTaskOutcomeScopeKey({
|
|
421
|
-
organizationId: args.organizationId,
|
|
422
|
-
personaId: args.personaId,
|
|
423
|
-
});
|
|
424
|
-
const now = Date.now();
|
|
425
|
-
const window = this.taskOutcomeKpiWindows.get(scopeKey) ?? [];
|
|
426
|
-
window.push({
|
|
427
|
-
status: args.outcome.status,
|
|
428
|
-
score: Math.max(0, Math.min(1, Number(args.outcome.score) || 0)),
|
|
429
|
-
timestamp: now,
|
|
430
|
-
});
|
|
431
|
-
const cap = telemetry.rollingWindowSize;
|
|
432
|
-
if (window.length > cap) {
|
|
433
|
-
window.splice(0, window.length - cap);
|
|
434
|
-
}
|
|
435
|
-
this.taskOutcomeKpiWindows.set(scopeKey, window);
|
|
436
|
-
if (this.dependencies.taskOutcomeTelemetryStore) {
|
|
437
|
-
const snapshot = window.map((entry) => ({ ...entry }));
|
|
438
|
-
void this.dependencies.taskOutcomeTelemetryStore
|
|
439
|
-
.saveWindow(scopeKey, snapshot)
|
|
440
|
-
.catch((error) => {
|
|
441
|
-
console.warn(`AgentOSOrchestrator: Failed to persist task outcome telemetry window for scope '${scopeKey}'.`, error);
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
return this.summarizeTaskOutcomeWindow(scopeKey);
|
|
445
|
-
}
|
|
446
|
-
getCurrentTaskOutcomeKpi(args) {
|
|
447
|
-
if (!this.config.taskOutcomeTelemetry.enabled)
|
|
448
|
-
return null;
|
|
449
|
-
const scopeKey = this.resolveTaskOutcomeScopeKey({
|
|
450
|
-
organizationId: args.organizationId,
|
|
451
|
-
personaId: args.personaId,
|
|
452
|
-
});
|
|
453
|
-
return this.summarizeTaskOutcomeWindow(scopeKey);
|
|
454
|
-
}
|
|
455
|
-
summarizeTaskOutcomeWindow(scopeKey) {
|
|
456
|
-
const telemetry = this.config.taskOutcomeTelemetry;
|
|
457
|
-
const window = this.taskOutcomeKpiWindows.get(scopeKey) ?? [];
|
|
458
|
-
if (window.length === 0)
|
|
459
|
-
return null;
|
|
460
|
-
const now = Date.now();
|
|
461
|
-
let successCount = 0;
|
|
462
|
-
let partialCount = 0;
|
|
463
|
-
let failedCount = 0;
|
|
464
|
-
let scoreSum = 0;
|
|
465
|
-
for (const entry of window) {
|
|
466
|
-
if (entry.status === 'success')
|
|
467
|
-
successCount += 1;
|
|
468
|
-
else if (entry.status === 'partial')
|
|
469
|
-
partialCount += 1;
|
|
470
|
-
else
|
|
471
|
-
failedCount += 1;
|
|
472
|
-
scoreSum += entry.score;
|
|
473
|
-
}
|
|
474
|
-
const sampleCount = window.length;
|
|
475
|
-
const successRate = sampleCount > 0 ? successCount / sampleCount : 0;
|
|
476
|
-
const averageScore = sampleCount > 0 ? scoreSum / sampleCount : 0;
|
|
477
|
-
return {
|
|
478
|
-
scopeKey,
|
|
479
|
-
scopeMode: telemetry.scope,
|
|
480
|
-
windowSize: telemetry.rollingWindowSize,
|
|
481
|
-
sampleCount,
|
|
482
|
-
successCount,
|
|
483
|
-
partialCount,
|
|
484
|
-
failedCount,
|
|
485
|
-
successRate,
|
|
486
|
-
averageScore,
|
|
487
|
-
weightedSuccessRate: averageScore,
|
|
488
|
-
timestamp: new Date(now).toISOString(),
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
maybeApplyAdaptiveExecutionPolicy(args) {
|
|
492
|
-
const adaptive = this.config.adaptiveExecution;
|
|
493
|
-
if (!adaptive.enabled || !args.turnPlan)
|
|
494
|
-
return { applied: false };
|
|
495
|
-
const kpi = this.getCurrentTaskOutcomeKpi({
|
|
496
|
-
organizationId: args.organizationId,
|
|
497
|
-
personaId: args.personaId,
|
|
498
|
-
});
|
|
499
|
-
if (!kpi)
|
|
500
|
-
return { applied: false, kpi };
|
|
501
|
-
if (kpi.sampleCount < adaptive.minSamples)
|
|
502
|
-
return { applied: false, kpi };
|
|
503
|
-
if (kpi.weightedSuccessRate >= adaptive.minWeightedSuccessRate)
|
|
504
|
-
return { applied: false, kpi };
|
|
505
|
-
const reasons = [
|
|
506
|
-
`weightedSuccessRate=${kpi.weightedSuccessRate.toFixed(3)} below threshold=${adaptive.minWeightedSuccessRate.toFixed(3)}`,
|
|
507
|
-
];
|
|
508
|
-
let forcedToolSelectionMode = false;
|
|
509
|
-
let forcedToolFailureMode = false;
|
|
510
|
-
let preservedRequestedFailClosed = false;
|
|
511
|
-
if (adaptive.forceAllToolsWhenDegraded &&
|
|
512
|
-
args.turnPlan.policy.toolSelectionMode === 'discovered') {
|
|
513
|
-
args.turnPlan.policy.toolSelectionMode = 'all';
|
|
514
|
-
forcedToolSelectionMode = true;
|
|
515
|
-
reasons.push('toolSelectionMode switched discovered -> all');
|
|
516
|
-
}
|
|
517
|
-
if (adaptive.forceFailOpenWhenDegraded && args.turnPlan.policy.toolFailureMode !== 'fail_open') {
|
|
518
|
-
const requestedFailureMode = normalizeRequestedToolFailureMode(args.requestCustomFlags);
|
|
519
|
-
if (requestedFailureMode === 'fail_closed') {
|
|
520
|
-
preservedRequestedFailClosed = true;
|
|
521
|
-
reasons.push('preserved explicit request override toolFailureMode=fail_closed');
|
|
522
|
-
}
|
|
523
|
-
else {
|
|
524
|
-
const before = args.turnPlan.policy.toolFailureMode;
|
|
525
|
-
args.turnPlan.policy.toolFailureMode = 'fail_open';
|
|
526
|
-
forcedToolFailureMode = true;
|
|
527
|
-
reasons.push(`toolFailureMode switched ${before} -> fail_open`);
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
if (!forcedToolSelectionMode && !forcedToolFailureMode) {
|
|
531
|
-
return {
|
|
532
|
-
applied: false,
|
|
533
|
-
reason: preservedRequestedFailClosed
|
|
534
|
-
? 'Adaptive execution detected degraded KPI but preserved explicit fail-closed request override.'
|
|
535
|
-
: undefined,
|
|
536
|
-
kpi,
|
|
537
|
-
actions: preservedRequestedFailClosed
|
|
538
|
-
? {
|
|
539
|
-
preservedRequestedFailClosed: true,
|
|
540
|
-
}
|
|
541
|
-
: undefined,
|
|
542
|
-
};
|
|
543
|
-
}
|
|
544
|
-
args.turnPlan.capability.fallbackApplied = true;
|
|
545
|
-
args.turnPlan.capability.fallbackReason = `Adaptive fallback applied: ${reasons.join('; ')}.`;
|
|
546
|
-
args.turnPlan.diagnostics.usedFallback = true;
|
|
547
|
-
return {
|
|
548
|
-
applied: true,
|
|
549
|
-
reason: args.turnPlan.capability.fallbackReason,
|
|
550
|
-
kpi,
|
|
551
|
-
actions: {
|
|
552
|
-
forcedToolSelectionMode,
|
|
553
|
-
forcedToolFailureMode,
|
|
554
|
-
preservedRequestedFailClosed: preservedRequestedFailClosed || undefined,
|
|
555
|
-
},
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
maybeBuildTaskOutcomeAlert(kpi) {
|
|
559
|
-
const telemetry = this.config.taskOutcomeTelemetry;
|
|
560
|
-
if (!telemetry.enabled || !telemetry.emitAlerts || !kpi)
|
|
561
|
-
return null;
|
|
562
|
-
if (kpi.sampleCount < telemetry.alertMinSamples)
|
|
563
|
-
return null;
|
|
564
|
-
if (kpi.weightedSuccessRate >= telemetry.alertBelowWeightedSuccessRate)
|
|
565
|
-
return null;
|
|
566
|
-
const now = Date.now();
|
|
567
|
-
const lastAlertAt = this.taskOutcomeAlertState.get(kpi.scopeKey) ?? 0;
|
|
568
|
-
if (telemetry.alertCooldownMs > 0 && now - lastAlertAt < telemetry.alertCooldownMs) {
|
|
569
|
-
return null;
|
|
570
|
-
}
|
|
571
|
-
this.taskOutcomeAlertState.set(kpi.scopeKey, now);
|
|
572
|
-
const severity = kpi.weightedSuccessRate < telemetry.alertBelowWeightedSuccessRate * 0.6
|
|
573
|
-
? 'critical'
|
|
574
|
-
: 'warning';
|
|
575
|
-
return {
|
|
576
|
-
scopeKey: kpi.scopeKey,
|
|
577
|
-
severity,
|
|
578
|
-
reason: `Weighted success rate ${kpi.weightedSuccessRate.toFixed(3)} below alert threshold ` +
|
|
579
|
-
`${telemetry.alertBelowWeightedSuccessRate.toFixed(3)}.`,
|
|
580
|
-
threshold: telemetry.alertBelowWeightedSuccessRate,
|
|
581
|
-
value: kpi.weightedSuccessRate,
|
|
582
|
-
sampleCount: kpi.sampleCount,
|
|
583
|
-
timestamp: new Date(now).toISOString(),
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
/**
|
|
587
|
-
* Helper method to create and push response chunks via StreamingManager.
|
|
588
|
-
* @private
|
|
589
|
-
*/
|
|
590
|
-
async pushChunkToStream(streamId, type, gmiInstanceId, personaId, isFinal, data) {
|
|
591
|
-
const baseChunk = {
|
|
592
|
-
type,
|
|
593
|
-
streamId,
|
|
594
|
-
gmiInstanceId,
|
|
595
|
-
personaId,
|
|
596
|
-
isFinal,
|
|
597
|
-
timestamp: new Date().toISOString(),
|
|
598
|
-
};
|
|
599
|
-
if (data && typeof data === 'object' && 'metadata' in data && data.metadata) {
|
|
600
|
-
baseChunk.metadata = data.metadata;
|
|
601
|
-
}
|
|
602
|
-
const ctx = this.activeStreamContexts.get(streamId);
|
|
603
|
-
if (ctx?.languageNegotiation) {
|
|
604
|
-
baseChunk.metadata = baseChunk.metadata || {};
|
|
605
|
-
if (!baseChunk.metadata.language)
|
|
606
|
-
baseChunk.metadata.language = ctx.languageNegotiation;
|
|
607
|
-
}
|
|
608
|
-
if (shouldIncludeTraceInAgentOSResponses() &&
|
|
609
|
-
(type === AgentOSResponseChunkType.METADATA_UPDATE ||
|
|
610
|
-
type === AgentOSResponseChunkType.FINAL_RESPONSE ||
|
|
611
|
-
type === AgentOSResponseChunkType.ERROR)) {
|
|
612
|
-
const traceMeta = getActiveTraceMetadata();
|
|
613
|
-
if (traceMeta) {
|
|
614
|
-
baseChunk.metadata = baseChunk.metadata || {};
|
|
615
|
-
baseChunk.metadata.trace = traceMeta;
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
let chunk;
|
|
619
|
-
switch (type) {
|
|
620
|
-
case AgentOSResponseChunkType.TEXT_DELTA:
|
|
621
|
-
chunk = { ...baseChunk, textDelta: data.textDelta };
|
|
622
|
-
break;
|
|
623
|
-
case AgentOSResponseChunkType.SYSTEM_PROGRESS:
|
|
624
|
-
chunk = {
|
|
625
|
-
...baseChunk,
|
|
626
|
-
message: data.message,
|
|
627
|
-
progressPercentage: data.progressPercentage,
|
|
628
|
-
statusCode: data.statusCode,
|
|
629
|
-
};
|
|
630
|
-
break;
|
|
631
|
-
case AgentOSResponseChunkType.TOOL_CALL_REQUEST:
|
|
632
|
-
chunk = {
|
|
633
|
-
...baseChunk,
|
|
634
|
-
toolCalls: data.toolCalls,
|
|
635
|
-
rationale: data.rationale,
|
|
636
|
-
};
|
|
637
|
-
break;
|
|
638
|
-
case AgentOSResponseChunkType.TOOL_RESULT_EMISSION:
|
|
639
|
-
chunk = {
|
|
640
|
-
...baseChunk,
|
|
641
|
-
toolCallId: data.toolCallId,
|
|
642
|
-
toolName: data.toolName,
|
|
643
|
-
toolResult: data.toolResult,
|
|
644
|
-
isSuccess: data.isSuccess,
|
|
645
|
-
errorMessage: data.errorMessage,
|
|
646
|
-
};
|
|
647
|
-
break;
|
|
648
|
-
case AgentOSResponseChunkType.UI_COMMAND:
|
|
649
|
-
chunk = { ...baseChunk, uiCommands: data.uiCommands };
|
|
650
|
-
break;
|
|
651
|
-
case AgentOSResponseChunkType.ERROR:
|
|
652
|
-
chunk = {
|
|
653
|
-
...baseChunk,
|
|
654
|
-
code: data.code,
|
|
655
|
-
message: data.message,
|
|
656
|
-
details: data.details,
|
|
657
|
-
};
|
|
658
|
-
break;
|
|
659
|
-
case AgentOSResponseChunkType.FINAL_RESPONSE:
|
|
660
|
-
chunk = {
|
|
661
|
-
...baseChunk,
|
|
662
|
-
finalResponseText: data.finalResponseText,
|
|
663
|
-
finalToolCalls: data.finalToolCalls,
|
|
664
|
-
finalUiCommands: data.finalUiCommands,
|
|
665
|
-
audioOutput: data.audioOutput,
|
|
666
|
-
imageOutput: data.imageOutput,
|
|
667
|
-
usage: normalizeUsage(data.usage),
|
|
668
|
-
reasoningTrace: data.reasoningTrace,
|
|
669
|
-
error: data.error,
|
|
670
|
-
updatedConversationContext: data.updatedConversationContext,
|
|
671
|
-
activePersonaDetails: data.activePersonaDetails,
|
|
672
|
-
};
|
|
673
|
-
break;
|
|
674
|
-
case AgentOSResponseChunkType.WORKFLOW_UPDATE:
|
|
675
|
-
chunk = {
|
|
676
|
-
...baseChunk,
|
|
677
|
-
workflow: data.workflow,
|
|
678
|
-
};
|
|
679
|
-
break;
|
|
680
|
-
case AgentOSResponseChunkType.METADATA_UPDATE:
|
|
681
|
-
chunk = {
|
|
682
|
-
...baseChunk,
|
|
683
|
-
updates: data.updates,
|
|
684
|
-
};
|
|
685
|
-
break;
|
|
686
|
-
default:
|
|
687
|
-
console.error(`AgentOSOrchestrator: Unknown chunk type encountered in pushChunkToStream: ${type}`);
|
|
688
|
-
chunk = {
|
|
689
|
-
...baseChunk,
|
|
690
|
-
type: AgentOSResponseChunkType.ERROR,
|
|
691
|
-
code: GMIErrorCode.INTERNAL_SERVER_ERROR,
|
|
692
|
-
message: `Unknown chunk type: ${type}`,
|
|
693
|
-
details: data,
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
try {
|
|
697
|
-
await this.dependencies.streamingManager.pushChunk(streamId, chunk);
|
|
698
|
-
}
|
|
699
|
-
catch (pushError) {
|
|
700
|
-
// Gracefully handle attempts to push after a stream is closed or missing
|
|
701
|
-
console.error(`AgentOSOrchestrator: Failed to push chunk to stream ${streamId}. Type: ${type}. Error: ${pushError?.message}`, pushError);
|
|
702
|
-
}
|
|
703
|
-
}
|
|
215
|
+
// Task outcome telemetry methods delegated to this.telemetry (TaskOutcomeTelemetryManager)
|
|
216
|
+
// pushChunkToStream, pushErrorChunk, emitExecutionLifecycleUpdate
|
|
217
|
+
// delegated to this.chunks (StreamChunkEmitter)
|
|
704
218
|
async broadcastWorkflowUpdate(update) {
|
|
705
219
|
this.ensureInitialized();
|
|
706
220
|
const targets = [];
|
|
@@ -726,32 +240,12 @@ export class AgentOSOrchestrator {
|
|
|
726
240
|
conversationId: update.workflow.conversationId,
|
|
727
241
|
status: update.workflow.status,
|
|
728
242
|
};
|
|
729
|
-
await this.
|
|
243
|
+
await this.chunks.pushChunk(streamId, AgentOSResponseChunkType.WORKFLOW_UPDATE, gmiId, context.personaId, false, {
|
|
730
244
|
workflow: update,
|
|
731
245
|
metadata,
|
|
732
246
|
});
|
|
733
247
|
}));
|
|
734
248
|
}
|
|
735
|
-
/**
|
|
736
|
-
* Helper method to create and push error chunks.
|
|
737
|
-
* @private
|
|
738
|
-
*/
|
|
739
|
-
async pushErrorChunk(streamId, personaId, gmiInstanceId = 'unknown_gmi_instance', code, message, details) {
|
|
740
|
-
await this.pushChunkToStream(streamId, AgentOSResponseChunkType.ERROR, gmiInstanceId, personaId, true, // Errors are usually final for the current operation
|
|
741
|
-
{ code: code.toString(), message, details });
|
|
742
|
-
}
|
|
743
|
-
async emitExecutionLifecycleUpdate(args) {
|
|
744
|
-
await this.pushChunkToStream(args.streamId, AgentOSResponseChunkType.METADATA_UPDATE, args.gmiInstanceId, args.personaId, false, {
|
|
745
|
-
updates: {
|
|
746
|
-
executionLifecycle: {
|
|
747
|
-
phase: args.phase,
|
|
748
|
-
status: args.status,
|
|
749
|
-
timestamp: new Date().toISOString(),
|
|
750
|
-
...(args.details ? { details: args.details } : null),
|
|
751
|
-
},
|
|
752
|
-
},
|
|
753
|
-
});
|
|
754
|
-
}
|
|
755
249
|
/**
|
|
756
250
|
* Orchestrates a full logical turn for a user request.
|
|
757
251
|
* This involves managing GMI interaction, tool calls, and streaming responses.
|
|
@@ -792,7 +286,7 @@ export class AgentOSOrchestrator {
|
|
|
792
286
|
}
|
|
793
287
|
console.error(`AgentOSOrchestrator: Critical unhandled error in _processTurnInternal for stream ${agentOSStreamId}:`, criticalError);
|
|
794
288
|
try {
|
|
795
|
-
await this.
|
|
289
|
+
await this.chunks.pushError(agentOSStreamId, input.selectedPersonaId || 'unknown_persona', 'orchestrator_critical', GMIErrorCode.INTERNAL_SERVER_ERROR, `A critical orchestration error occurred: ${criticalError.message}`, { name: criticalError.name, stack: criticalError.stack });
|
|
796
290
|
await this.dependencies.streamingManager.closeStream(agentOSStreamId, 'Critical orchestrator error');
|
|
797
291
|
}
|
|
798
292
|
catch (cleanupError) {
|
|
@@ -855,7 +349,7 @@ export class AgentOSOrchestrator {
|
|
|
855
349
|
conversationContext, userApiKeys: input.userApiKeys, processingOptions: input.options
|
|
856
350
|
};
|
|
857
351
|
this.activeStreamContexts.set(agentOSStreamId, streamContext);
|
|
858
|
-
await this.
|
|
352
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.SYSTEM_PROGRESS, gmiInstanceIdForChunks, currentPersonaId, false, { message: `Initializing persona ${currentPersonaId}... GMI: ${gmiInstanceIdForChunks}`, progressPercentage: 10 });
|
|
859
353
|
const gmiInput = this.constructGMITurnInput(agentOSStreamId, input, streamContext);
|
|
860
354
|
let turnPlan = null;
|
|
861
355
|
const resolvedOrganizationId = this.resolveOrganizationContext(input.organizationId);
|
|
@@ -880,7 +374,7 @@ export class AgentOSOrchestrator {
|
|
|
880
374
|
throw new GMIError(`Turn planning failed before execution: ${planningError?.message || String(planningError)}`, GMIErrorCode.PROCESSING_ERROR, { streamId: agentOSStreamId, planningError });
|
|
881
375
|
}
|
|
882
376
|
}
|
|
883
|
-
const adaptiveExecution = this.
|
|
377
|
+
const adaptiveExecution = this.telemetry.maybeApplyAdaptivePolicy({
|
|
884
378
|
turnPlan,
|
|
885
379
|
organizationId: resolvedOrganizationId,
|
|
886
380
|
personaId: currentPersonaId,
|
|
@@ -894,7 +388,7 @@ export class AgentOSOrchestrator {
|
|
|
894
388
|
actions: adaptiveExecution.actions,
|
|
895
389
|
}
|
|
896
390
|
: undefined;
|
|
897
|
-
await this.
|
|
391
|
+
await this.chunks.emitLifecycleUpdate({
|
|
898
392
|
streamId: agentOSStreamId,
|
|
899
393
|
gmiInstanceId: gmiInstanceIdForChunks,
|
|
900
394
|
personaId: currentPersonaId,
|
|
@@ -911,7 +405,7 @@ export class AgentOSOrchestrator {
|
|
|
911
405
|
});
|
|
912
406
|
if (turnPlan?.capability.fallbackApplied || adaptiveExecution.applied) {
|
|
913
407
|
lifecycleDegraded = true;
|
|
914
|
-
await this.
|
|
408
|
+
await this.chunks.emitLifecycleUpdate({
|
|
915
409
|
streamId: agentOSStreamId,
|
|
916
410
|
gmiInstanceId: gmiInstanceIdForChunks,
|
|
917
411
|
personaId: currentPersonaId,
|
|
@@ -997,106 +491,35 @@ export class AgentOSOrchestrator {
|
|
|
997
491
|
const modeForRouting = typeof input.options?.customFlags?.mode === 'string' && input.options.customFlags.mode.trim()
|
|
998
492
|
? input.options.customFlags.mode.trim()
|
|
999
493
|
: currentPersonaId;
|
|
1000
|
-
// --- Rolling summary compaction (
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
if (profile?.systemPrompt) {
|
|
1015
|
-
rollingSummarySystemPromptForTurn = profile.systemPrompt;
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
if (conversationContext && rollingSummaryConfigForTurn) {
|
|
1019
|
-
try {
|
|
1020
|
-
const llmCaller = async (call) => {
|
|
1021
|
-
const providerIdResolved = call.providerId ||
|
|
1022
|
-
this.dependencies.modelProviderManager.getProviderForModel(call.modelId)?.providerId ||
|
|
1023
|
-
this.dependencies.modelProviderManager.getDefaultProvider()?.providerId;
|
|
1024
|
-
if (!providerIdResolved) {
|
|
1025
|
-
throw new Error(`No provider resolved for rolling-summary model '${call.modelId}'.`);
|
|
1026
|
-
}
|
|
1027
|
-
const provider = this.dependencies.modelProviderManager.getProvider(providerIdResolved);
|
|
1028
|
-
if (!provider) {
|
|
1029
|
-
throw new Error(`Provider '${providerIdResolved}' not found for rolling-summary compaction.`);
|
|
1030
|
-
}
|
|
1031
|
-
const response = await provider.generateCompletion(call.modelId, call.messages, call.options);
|
|
1032
|
-
const choice = response?.choices?.[0];
|
|
1033
|
-
const responseContent = choice?.message?.content ?? choice?.text ?? '';
|
|
1034
|
-
if (typeof responseContent === 'string')
|
|
1035
|
-
return responseContent.trim();
|
|
1036
|
-
if (Array.isArray(responseContent)) {
|
|
1037
|
-
return responseContent
|
|
1038
|
-
.map((part) => (typeof part?.text === 'string' ? part.text : ''))
|
|
1039
|
-
.filter(Boolean)
|
|
1040
|
-
.join('\n')
|
|
1041
|
-
.trim();
|
|
1042
|
-
}
|
|
1043
|
-
return String(responseContent ?? '').trim();
|
|
1044
|
-
};
|
|
1045
|
-
const stateKey = this.config.rollingSummaryStateKey;
|
|
1046
|
-
const compaction = await maybeCompactConversationMessages({
|
|
1047
|
-
messages: conversationContext.getAllMessages(),
|
|
1048
|
-
sessionMetadata: conversationContext.getAllMetadata(),
|
|
1049
|
-
config: rollingSummaryConfigForTurn,
|
|
1050
|
-
llmCaller: ({ providerId, modelId, messages, options }) => llmCaller({ providerId, modelId, messages, options }),
|
|
1051
|
-
systemPrompt: rollingSummarySystemPromptForTurn,
|
|
1052
|
-
stateKey,
|
|
1053
|
-
});
|
|
1054
|
-
rollingSummaryResult = compaction;
|
|
1055
|
-
if (compaction.updatedSessionMetadata && Object.prototype.hasOwnProperty.call(compaction.updatedSessionMetadata, stateKey)) {
|
|
1056
|
-
conversationContext.setMetadata(stateKey, compaction.updatedSessionMetadata[stateKey]);
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
catch (compactionError) {
|
|
1060
|
-
console.warn(`AgentOSOrchestrator: Rolling summary compaction failed for stream ${agentOSStreamId} (continuing without it).`, compactionError);
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
494
|
+
// --- Rolling summary compaction (delegated to turn-phases/rolling-summary) ---
|
|
495
|
+
const rollingSummaryPhase = await executeRollingSummaryPhase({
|
|
496
|
+
conversationContext,
|
|
497
|
+
modeForRouting,
|
|
498
|
+
streamId: agentOSStreamId,
|
|
499
|
+
rollingSummaryCompactionConfig: this.config.rollingSummaryCompactionConfig,
|
|
500
|
+
rollingSummaryCompactionProfilesConfig: this.config.rollingSummaryCompactionProfilesConfig,
|
|
501
|
+
rollingSummarySystemPrompt: this.config.rollingSummarySystemPrompt,
|
|
502
|
+
rollingSummaryStateKey: this.config.rollingSummaryStateKey,
|
|
503
|
+
modelProviderManager: this.dependencies.modelProviderManager,
|
|
504
|
+
});
|
|
505
|
+
const { result: rollingSummaryResult, profileId: rollingSummaryProfileId, configForTurn: rollingSummaryConfigForTurn } = rollingSummaryPhase;
|
|
506
|
+
const rollingSummaryEnabled = rollingSummaryPhase.enabled;
|
|
507
|
+
const rollingSummaryText = rollingSummaryPhase.summaryText;
|
|
1063
508
|
if (!gmiInput.metadata) {
|
|
1064
509
|
gmiInput.metadata = {};
|
|
1065
510
|
}
|
|
1066
|
-
const rollingSummaryEnabled = Boolean(rollingSummaryConfigForTurn?.enabled);
|
|
1067
|
-
const rollingSummaryText = rollingSummaryEnabled && typeof rollingSummaryResult?.summaryText === 'string'
|
|
1068
|
-
? rollingSummaryResult.summaryText.trim()
|
|
1069
|
-
: '';
|
|
1070
511
|
gmiInput.metadata.rollingSummary =
|
|
1071
512
|
rollingSummaryEnabled && rollingSummaryText
|
|
1072
513
|
? { text: rollingSummaryText, json: rollingSummaryResult?.summaryJson ?? undefined }
|
|
1073
514
|
: null;
|
|
1074
|
-
// --- Prompt-profile routing (
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
const userMessageForRouting = gmiInput.type === GMIInteractionType.TEXT && typeof gmiInput.content === 'string'
|
|
1083
|
-
? gmiInput.content
|
|
1084
|
-
: gmiInput.type === GMIInteractionType.MULTIMODAL_CONTENT
|
|
1085
|
-
? JSON.stringify(gmiInput.content)
|
|
1086
|
-
: '';
|
|
1087
|
-
const selection = selectPromptProfile(this.config.promptProfileConfig, {
|
|
1088
|
-
conversationId: conversationContext.sessionId,
|
|
1089
|
-
mode: modeForRouting,
|
|
1090
|
-
userMessage: userMessageForRouting,
|
|
1091
|
-
didCompact: Boolean(rollingSummaryResult?.didCompact),
|
|
1092
|
-
}, previousState);
|
|
1093
|
-
promptProfileSelection = selection.result;
|
|
1094
|
-
conversationContext.setMetadata('promptProfileState', selection.nextState);
|
|
1095
|
-
}
|
|
1096
|
-
catch (routerError) {
|
|
1097
|
-
console.warn(`AgentOSOrchestrator: Prompt-profile routing failed for stream ${agentOSStreamId} (continuing without it).`, routerError);
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
515
|
+
// --- Prompt-profile routing (delegated to turn-phases/prompt-profile) ---
|
|
516
|
+
const promptProfileSelection = executePromptProfilePhase({
|
|
517
|
+
conversationContext,
|
|
518
|
+
promptProfileConfig: this.config.promptProfileConfig,
|
|
519
|
+
modeForRouting,
|
|
520
|
+
gmiInput,
|
|
521
|
+
didCompact: Boolean(rollingSummaryResult?.didCompact),
|
|
522
|
+
});
|
|
1100
523
|
gmiInput.metadata.promptProfile = promptProfileSelection
|
|
1101
524
|
? {
|
|
1102
525
|
id: promptProfileSelection.presetId,
|
|
@@ -1104,127 +527,39 @@ export class AgentOSOrchestrator {
|
|
|
1104
527
|
reason: promptProfileSelection.reason,
|
|
1105
528
|
}
|
|
1106
529
|
: null;
|
|
1107
|
-
// --- Long-term memory retrieval (
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
const cadenceTurns = recallConfig.cadenceTurns;
|
|
1127
|
-
const forceOnCompaction = recallConfig.forceOnCompaction;
|
|
1128
|
-
const rawState = conversationContext.getMetadata('longTermMemoryRetrievalState');
|
|
1129
|
-
const prevState = rawState &&
|
|
1130
|
-
typeof rawState === 'object' &&
|
|
1131
|
-
typeof rawState.lastReviewedUserTurn === 'number'
|
|
1132
|
-
? rawState
|
|
1133
|
-
: null;
|
|
1134
|
-
const turnsSinceReview = prevState
|
|
1135
|
-
? Math.max(0, userTurnCount - prevState.lastReviewedUserTurn)
|
|
1136
|
-
: Number.POSITIVE_INFINITY;
|
|
1137
|
-
const dueToCadence = !prevState || turnsSinceReview >= cadenceTurns;
|
|
1138
|
-
const dueToCompaction = forceOnCompaction && Boolean(rollingSummaryResult?.didCompact);
|
|
1139
|
-
const shouldReview = dueToCadence || dueToCompaction;
|
|
1140
|
-
longTermMemoryShouldReview = shouldReview;
|
|
1141
|
-
if (shouldReview) {
|
|
1142
|
-
longTermMemoryReviewReason = !prevState
|
|
1143
|
-
? 'initial_review'
|
|
1144
|
-
: dueToCompaction
|
|
1145
|
-
? 'forced_on_compaction'
|
|
1146
|
-
: 'cadence_due';
|
|
1147
|
-
}
|
|
1148
|
-
else {
|
|
1149
|
-
longTermMemoryReviewReason = 'cadence_not_due';
|
|
1150
|
-
}
|
|
1151
|
-
if (shouldReview && queryText.length > 0) {
|
|
1152
|
-
const retrievalResult = await this.dependencies.longTermMemoryRetriever.retrieveLongTermMemory({
|
|
1153
|
-
userId: streamContext.userId,
|
|
1154
|
-
organizationId: organizationIdForMemory,
|
|
1155
|
-
conversationId: streamContext.conversationId,
|
|
1156
|
-
personaId: currentPersonaId,
|
|
1157
|
-
mode: modeForRouting,
|
|
1158
|
-
queryText,
|
|
1159
|
-
memoryPolicy: longTermMemoryPolicy ?? DEFAULT_LONG_TERM_MEMORY_POLICY,
|
|
1160
|
-
maxContextChars: recallConfig.maxContextChars,
|
|
1161
|
-
topKByScope: recallConfig.topKByScope,
|
|
1162
|
-
});
|
|
1163
|
-
if (retrievalResult?.contextText && retrievalResult.contextText.trim()) {
|
|
1164
|
-
longTermMemoryContextText = retrievalResult.contextText.trim();
|
|
1165
|
-
longTermMemoryRetrievalDiagnostics = retrievalResult.diagnostics;
|
|
1166
|
-
}
|
|
1167
|
-
conversationContext.setMetadata('longTermMemoryRetrievalState', {
|
|
1168
|
-
lastReviewedUserTurn: userTurnCount,
|
|
1169
|
-
lastReviewedAt: Date.now(),
|
|
1170
|
-
});
|
|
1171
|
-
}
|
|
1172
|
-
else if (shouldReview && queryText.length === 0) {
|
|
1173
|
-
longTermMemoryReviewReason = 'empty_query';
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
catch (retrievalError) {
|
|
1177
|
-
console.warn(`AgentOSOrchestrator: Long-term memory retrieval failed for stream ${agentOSStreamId} (continuing without it).`, retrievalError);
|
|
1178
|
-
longTermMemoryReviewReason = 'retrieval_error';
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
else {
|
|
1182
|
-
longTermMemoryReviewReason = 'retriever_not_applicable';
|
|
1183
|
-
}
|
|
530
|
+
// --- Long-term memory retrieval (delegated to turn-phases/long-term-memory) ---
|
|
531
|
+
const longTermMemoryPhase = await executeLongTermMemoryPhase({
|
|
532
|
+
conversationContext,
|
|
533
|
+
longTermMemoryRetriever: this.dependencies.longTermMemoryRetriever,
|
|
534
|
+
longTermMemoryPolicy,
|
|
535
|
+
gmiInput,
|
|
536
|
+
streamId: agentOSStreamId,
|
|
537
|
+
userId: streamContext.userId,
|
|
538
|
+
organizationId: organizationIdForMemory,
|
|
539
|
+
conversationId: streamContext.conversationId,
|
|
540
|
+
personaId: currentPersonaId,
|
|
541
|
+
modeForRouting,
|
|
542
|
+
recallConfig: this.config.longTermMemoryRecall,
|
|
543
|
+
didCompact: Boolean(rollingSummaryResult?.didCompact),
|
|
544
|
+
});
|
|
545
|
+
const longTermMemoryContextText = longTermMemoryPhase.contextText;
|
|
546
|
+
const longTermMemoryRetrievalDiagnostics = longTermMemoryPhase.diagnostics;
|
|
547
|
+
const longTermMemoryShouldReview = longTermMemoryPhase.shouldReview;
|
|
548
|
+
const longTermMemoryReviewReason = longTermMemoryPhase.reviewReason;
|
|
1184
549
|
gmiInput.metadata.longTermMemoryContext =
|
|
1185
550
|
typeof longTermMemoryContextText === 'string' && longTermMemoryContextText.length > 0
|
|
1186
551
|
? longTermMemoryContextText
|
|
1187
552
|
: null;
|
|
1188
|
-
//
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
? conversationContext.getAllMessages()
|
|
1199
|
-
: conversationContext.getHistory(undefined, [MessageRole.ERROR, MessageRole.THOUGHT]);
|
|
1200
|
-
let historyForPrompt = rawHistory.filter((m) => m && !excludeRoles.has(m.role));
|
|
1201
|
-
const last = historyForPrompt[historyForPrompt.length - 1];
|
|
1202
|
-
if (last?.role === MessageRole.USER) {
|
|
1203
|
-
const content = typeof last.content === 'string' ? last.content.trim() : '';
|
|
1204
|
-
const inbound = gmiInput.type === GMIInteractionType.TEXT && typeof gmiInput.content === 'string'
|
|
1205
|
-
? gmiInput.content.trim()
|
|
1206
|
-
: gmiInput.type === GMIInteractionType.MULTIMODAL_CONTENT
|
|
1207
|
-
? JSON.stringify(gmiInput.content).trim()
|
|
1208
|
-
: '';
|
|
1209
|
-
if (content && inbound && content === inbound) {
|
|
1210
|
-
historyForPrompt = historyForPrompt.slice(0, -1);
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
if (useTrimmedHistory) {
|
|
1214
|
-
const headCount = Math.max(0, rollingSummaryConfigForTurn?.headMessagesToKeep ?? 0);
|
|
1215
|
-
const head = historyForPrompt.slice(0, Math.min(headCount, historyForPrompt.length));
|
|
1216
|
-
const afterSummary = historyForPrompt.filter((m) => m && m.timestamp > rollingSummaryResult.summaryUptoTimestamp);
|
|
1217
|
-
const merged = [];
|
|
1218
|
-
const seen = new Set();
|
|
1219
|
-
for (const msg of [...head, ...afterSummary]) {
|
|
1220
|
-
const id = typeof msg?.id === 'string' ? msg.id : '';
|
|
1221
|
-
if (!id || seen.has(id))
|
|
1222
|
-
continue;
|
|
1223
|
-
seen.add(id);
|
|
1224
|
-
merged.push(msg);
|
|
1225
|
-
}
|
|
1226
|
-
historyForPrompt = merged;
|
|
1227
|
-
}
|
|
553
|
+
// --- Conversation history assembly (delegated to turn-phases/conversation-history) ---
|
|
554
|
+
const historyForPrompt = assembleConversationHistory({
|
|
555
|
+
conversationContext,
|
|
556
|
+
gmiInput,
|
|
557
|
+
rollingSummaryEnabled,
|
|
558
|
+
rollingSummaryResult,
|
|
559
|
+
rollingSummaryText,
|
|
560
|
+
rollingSummaryConfigForTurn,
|
|
561
|
+
});
|
|
562
|
+
if (historyForPrompt) {
|
|
1228
563
|
gmiInput.metadata.conversationHistoryForPrompt = historyForPrompt;
|
|
1229
564
|
}
|
|
1230
565
|
// Persist any compaction/router metadata updates prior to the main LLM call.
|
|
@@ -1269,7 +604,7 @@ export class AgentOSOrchestrator {
|
|
|
1269
604
|
});
|
|
1270
605
|
}
|
|
1271
606
|
// Emit routing + memory metadata as a first-class chunk for clients.
|
|
1272
|
-
await this.
|
|
607
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.METADATA_UPDATE, gmiInstanceIdForChunks, currentPersonaId, false, {
|
|
1273
608
|
updates: {
|
|
1274
609
|
promptProfile: promptProfileSelection,
|
|
1275
610
|
organizationId: organizationIdForMemory ?? null,
|
|
@@ -1328,7 +663,7 @@ export class AgentOSOrchestrator {
|
|
|
1328
663
|
let currentToolCallIteration = 0;
|
|
1329
664
|
let continueProcessing = true;
|
|
1330
665
|
let lastGMIOutput; // To store the result from handleToolResult or final processTurnStream result
|
|
1331
|
-
await this.
|
|
666
|
+
await this.chunks.emitLifecycleUpdate({
|
|
1332
667
|
streamId: agentOSStreamId,
|
|
1333
668
|
gmiInstanceId: gmiInstanceIdForChunks,
|
|
1334
669
|
personaId: currentPersonaId,
|
|
@@ -1401,7 +736,7 @@ export class AgentOSOrchestrator {
|
|
|
1401
736
|
if (currentToolCallIteration >= this.config.maxToolCallIterations && continueProcessing) {
|
|
1402
737
|
console.warn(`AgentOSOrchestrator: Max tool call iterations reached for stream ${agentOSStreamId}. Forcing termination.`);
|
|
1403
738
|
didForceTerminate = true;
|
|
1404
|
-
await this.
|
|
739
|
+
await this.chunks.pushError(agentOSStreamId, currentPersonaId, gmiInstanceIdForChunks, GMIErrorCode.RATE_LIMIT_EXCEEDED, // Or a more specific code
|
|
1405
740
|
'Agent reached maximum tool call iterations.', { maxIterations: this.config.maxToolCallIterations });
|
|
1406
741
|
}
|
|
1407
742
|
// Final processing at the end of the turn or if no more continuation.
|
|
@@ -1435,13 +770,13 @@ export class AgentOSOrchestrator {
|
|
|
1435
770
|
customFlags: input.options?.customFlags,
|
|
1436
771
|
});
|
|
1437
772
|
turnMetricsTaskOutcome = taskOutcome;
|
|
1438
|
-
const taskOutcomeKpi = this.
|
|
773
|
+
const taskOutcomeKpi = this.telemetry.updateKpi({
|
|
1439
774
|
outcome: taskOutcome,
|
|
1440
775
|
organizationId: organizationIdForMemory,
|
|
1441
776
|
personaId: currentPersonaId,
|
|
1442
777
|
});
|
|
1443
|
-
const taskOutcomeAlert = this.
|
|
1444
|
-
await this.
|
|
778
|
+
const taskOutcomeAlert = this.telemetry.maybeBuildAlert(taskOutcomeKpi);
|
|
779
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.METADATA_UPDATE, gmiInstanceIdForChunks, currentPersonaId, false, {
|
|
1445
780
|
updates: {
|
|
1446
781
|
taskOutcome,
|
|
1447
782
|
taskOutcomeKpi,
|
|
@@ -1449,7 +784,7 @@ export class AgentOSOrchestrator {
|
|
|
1449
784
|
},
|
|
1450
785
|
});
|
|
1451
786
|
if (turnMetricsStatus === 'error') {
|
|
1452
|
-
await this.
|
|
787
|
+
await this.chunks.emitLifecycleUpdate({
|
|
1453
788
|
streamId: agentOSStreamId,
|
|
1454
789
|
gmiInstanceId: gmiInstanceIdForChunks,
|
|
1455
790
|
personaId: currentPersonaId,
|
|
@@ -1465,7 +800,7 @@ export class AgentOSOrchestrator {
|
|
|
1465
800
|
}
|
|
1466
801
|
else {
|
|
1467
802
|
if (lifecycleDegraded) {
|
|
1468
|
-
await this.
|
|
803
|
+
await this.chunks.emitLifecycleUpdate({
|
|
1469
804
|
streamId: agentOSStreamId,
|
|
1470
805
|
gmiInstanceId: gmiInstanceIdForChunks,
|
|
1471
806
|
personaId: currentPersonaId,
|
|
@@ -1476,7 +811,7 @@ export class AgentOSOrchestrator {
|
|
|
1476
811
|
},
|
|
1477
812
|
});
|
|
1478
813
|
}
|
|
1479
|
-
await this.
|
|
814
|
+
await this.chunks.emitLifecycleUpdate({
|
|
1480
815
|
streamId: agentOSStreamId,
|
|
1481
816
|
gmiInstanceId: gmiInstanceIdForChunks,
|
|
1482
817
|
personaId: currentPersonaId,
|
|
@@ -1518,7 +853,7 @@ export class AgentOSOrchestrator {
|
|
|
1518
853
|
console.warn(`AgentOSOrchestrator: Failed to persist assistant output to ConversationContext for stream ${agentOSStreamId}.`, persistError);
|
|
1519
854
|
}
|
|
1520
855
|
}
|
|
1521
|
-
await this.
|
|
856
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.FINAL_RESPONSE, gmiInstanceIdForChunks, currentPersonaId, true, {
|
|
1522
857
|
finalResponseText: finalGMIStateForResponse.responseText ?? null,
|
|
1523
858
|
finalResponseTextPlain: typeof finalGMIStateForResponse.responseText === 'string'
|
|
1524
859
|
? renderPlainText(finalGMIStateForResponse.responseText)
|
|
@@ -1546,21 +881,21 @@ export class AgentOSOrchestrator {
|
|
|
1546
881
|
reason: `Exception before completion: ${gmiErr.code}`,
|
|
1547
882
|
source: 'heuristic',
|
|
1548
883
|
};
|
|
1549
|
-
const taskOutcomeKpi = this.
|
|
884
|
+
const taskOutcomeKpi = this.telemetry.updateKpi({
|
|
1550
885
|
outcome: turnMetricsTaskOutcome,
|
|
1551
886
|
organizationId: organizationIdForMemory,
|
|
1552
887
|
personaId: currentPersonaId,
|
|
1553
888
|
});
|
|
1554
|
-
const taskOutcomeAlert = this.
|
|
889
|
+
const taskOutcomeAlert = this.telemetry.maybeBuildAlert(taskOutcomeKpi);
|
|
1555
890
|
console.error(`AgentOSOrchestrator: Error during _processTurnInternal for stream ${agentOSStreamId}:`, gmiErr);
|
|
1556
|
-
await this.
|
|
891
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.METADATA_UPDATE, gmiInstanceIdForChunks, currentPersonaId ?? 'unknown_persona', false, {
|
|
1557
892
|
updates: {
|
|
1558
893
|
taskOutcome: turnMetricsTaskOutcome,
|
|
1559
894
|
taskOutcomeKpi,
|
|
1560
895
|
taskOutcomeAlert,
|
|
1561
896
|
},
|
|
1562
897
|
});
|
|
1563
|
-
await this.
|
|
898
|
+
await this.chunks.emitLifecycleUpdate({
|
|
1564
899
|
streamId: agentOSStreamId,
|
|
1565
900
|
gmiInstanceId: gmiInstanceIdForChunks,
|
|
1566
901
|
personaId: currentPersonaId ?? 'unknown_persona',
|
|
@@ -1573,7 +908,7 @@ export class AgentOSOrchestrator {
|
|
|
1573
908
|
taskOutcomeScore: turnMetricsTaskOutcome.score,
|
|
1574
909
|
},
|
|
1575
910
|
});
|
|
1576
|
-
await this.
|
|
911
|
+
await this.chunks.pushError(agentOSStreamId, currentPersonaId ?? 'unknown_persona', gmiInstanceIdForChunks, gmiErr.code, gmiErr.message, gmiErr.details);
|
|
1577
912
|
await this.dependencies.streamingManager.closeStream(agentOSStreamId, "Error during turn processing.");
|
|
1578
913
|
}
|
|
1579
914
|
finally {
|
|
@@ -1632,7 +967,7 @@ export class AgentOSOrchestrator {
|
|
|
1632
967
|
span?.setAttribute('agentos.tool_success', isSuccess);
|
|
1633
968
|
try {
|
|
1634
969
|
// Emit the tool result itself as a chunk
|
|
1635
|
-
await this.
|
|
970
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.TOOL_RESULT_EMISSION, gmiInstanceIdForChunks, personaId, false, { toolCallId, toolName, toolResult: toolOutput, isSuccess, errorMessage });
|
|
1636
971
|
// Persist tool result into ConversationContext for durable memory / prompt reconstruction.
|
|
1637
972
|
if (this.config.enableConversationalPersistence && conversationContext) {
|
|
1638
973
|
try {
|
|
@@ -1665,7 +1000,7 @@ export class AgentOSOrchestrator {
|
|
|
1665
1000
|
await this.processGMIOutput(agentOSStreamId, streamContext, gmiOutputAfterTool, false);
|
|
1666
1001
|
// If GMIOutput indicates further tool calls are needed by the GMI
|
|
1667
1002
|
if (gmiOutputAfterTool.toolCalls && gmiOutputAfterTool.toolCalls.length > 0) {
|
|
1668
|
-
await this.
|
|
1003
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.TOOL_CALL_REQUEST, gmiInstanceIdForChunks, personaId, false, // Not final, more interaction expected
|
|
1669
1004
|
{
|
|
1670
1005
|
toolCalls: gmiOutputAfterTool.toolCalls,
|
|
1671
1006
|
rationale: gmiOutputAfterTool.responseText || 'Agent requires further tool execution.',
|
|
@@ -1703,7 +1038,7 @@ export class AgentOSOrchestrator {
|
|
|
1703
1038
|
}
|
|
1704
1039
|
// If it's final and no more tool calls, the interaction for this GMI processing cycle might be done.
|
|
1705
1040
|
// Push a final response marker or the already pushed final data from processGMIOutput takes precedence.
|
|
1706
|
-
await this.
|
|
1041
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.FINAL_RESPONSE, gmiInstanceIdForChunks, personaId, true, {
|
|
1707
1042
|
finalResponseText: gmiOutputAfterTool.responseText,
|
|
1708
1043
|
finalToolCalls: gmiOutputAfterTool.toolCalls,
|
|
1709
1044
|
finalUiCommands: gmiOutputAfterTool.uiCommands,
|
|
@@ -1725,7 +1060,7 @@ export class AgentOSOrchestrator {
|
|
|
1725
1060
|
const gmiErr = GMIError.wrap?.(error, GMIErrorCode.TOOL_ERROR, `Error in orchestrateToolResult for stream ${agentOSStreamId}`) ||
|
|
1726
1061
|
new GMIError(`Error in orchestrateToolResult for stream ${agentOSStreamId}: ${error.message}`, GMIErrorCode.TOOL_ERROR, error);
|
|
1727
1062
|
console.error(`AgentOSOrchestrator: Critical error processing tool result for stream ${agentOSStreamId}:`, gmiErr);
|
|
1728
|
-
await this.
|
|
1063
|
+
await this.chunks.pushError(agentOSStreamId, personaId, gmiInstanceIdForChunks, gmiErr.code, gmiErr.message, gmiErr.details);
|
|
1729
1064
|
this.activeStreamContexts.delete(agentOSStreamId);
|
|
1730
1065
|
await this.dependencies.streamingManager.closeStream(agentOSStreamId, 'Critical error during tool result processing.');
|
|
1731
1066
|
throw gmiErr; // Re-throw to signal failure to caller if necessary
|
|
@@ -1758,14 +1093,14 @@ export class AgentOSOrchestrator {
|
|
|
1758
1093
|
const { gmi, personaId, conversationContext } = streamContext;
|
|
1759
1094
|
const gmiInstanceIdForChunks = gmi.getGMIId();
|
|
1760
1095
|
if (gmiOutput.responseText) {
|
|
1761
|
-
await this.
|
|
1096
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.TEXT_DELTA, gmiInstanceIdForChunks, personaId, false, // text delta is not final by itself
|
|
1762
1097
|
{ textDelta: gmiOutput.responseText });
|
|
1763
1098
|
}
|
|
1764
1099
|
if (gmiOutput.uiCommands && gmiOutput.uiCommands.length > 0) {
|
|
1765
|
-
await this.
|
|
1100
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.UI_COMMAND, gmiInstanceIdForChunks, personaId, false, { uiCommands: gmiOutput.uiCommands });
|
|
1766
1101
|
}
|
|
1767
1102
|
if (gmiOutput.error) {
|
|
1768
|
-
await this.
|
|
1103
|
+
await this.chunks.pushError(agentOSStreamId, personaId, gmiInstanceIdForChunks, gmiOutput.error.code, gmiOutput.error.message, gmiOutput.error.details);
|
|
1769
1104
|
// If an error occurs in GMIOutput, it's usually final for this interaction path
|
|
1770
1105
|
if (gmiOutput.isFinal) {
|
|
1771
1106
|
this.activeStreamContexts.delete(agentOSStreamId);
|
|
@@ -1784,7 +1119,7 @@ export class AgentOSOrchestrator {
|
|
|
1784
1119
|
});
|
|
1785
1120
|
}
|
|
1786
1121
|
// This is a final response without further tool calls
|
|
1787
|
-
await this.
|
|
1122
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.FINAL_RESPONSE, gmiInstanceIdForChunks, personaId, true, {
|
|
1788
1123
|
finalResponseText: gmiOutput.responseText,
|
|
1789
1124
|
finalToolCalls: gmiOutput.toolCalls, // Should be empty or undefined here
|
|
1790
1125
|
finalUiCommands: gmiOutput.uiCommands,
|
|
@@ -1810,30 +1145,30 @@ export class AgentOSOrchestrator {
|
|
|
1810
1145
|
switch (gmiChunk.type) {
|
|
1811
1146
|
case GMIOutputChunkType.TEXT_DELTA:
|
|
1812
1147
|
if (gmiChunk.content && typeof gmiChunk.content === 'string') {
|
|
1813
|
-
await this.
|
|
1148
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.TEXT_DELTA, gmiInstanceIdForChunks, personaId, gmiChunk.isFinal ?? false, { textDelta: gmiChunk.content });
|
|
1814
1149
|
}
|
|
1815
1150
|
break;
|
|
1816
1151
|
case GMIOutputChunkType.SYSTEM_MESSAGE: // Was SystemProgress
|
|
1817
1152
|
if (gmiChunk.content && typeof gmiChunk.content === 'object') {
|
|
1818
1153
|
const progressContent = gmiChunk.content;
|
|
1819
|
-
await this.
|
|
1154
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.SYSTEM_PROGRESS, gmiInstanceIdForChunks, personaId, gmiChunk.isFinal ?? false, progressContent);
|
|
1820
1155
|
}
|
|
1821
1156
|
break;
|
|
1822
1157
|
case GMIOutputChunkType.TOOL_CALL_REQUEST:
|
|
1823
1158
|
if (gmiChunk.content && Array.isArray(gmiChunk.content)) {
|
|
1824
1159
|
const toolCalls = gmiChunk.content;
|
|
1825
|
-
await this.
|
|
1160
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.TOOL_CALL_REQUEST, gmiInstanceIdForChunks, personaId, false, // Tool call request is not final for the AgentOS turn
|
|
1826
1161
|
{ toolCalls, rationale: gmiChunk.metadata?.rationale || "Agent requires tool execution." });
|
|
1827
1162
|
}
|
|
1828
1163
|
break;
|
|
1829
1164
|
case GMIOutputChunkType.UI_COMMAND:
|
|
1830
1165
|
if (gmiChunk.content && Array.isArray(gmiChunk.content)) {
|
|
1831
|
-
await this.
|
|
1166
|
+
await this.chunks.pushChunk(agentOSStreamId, AgentOSResponseChunkType.UI_COMMAND, gmiInstanceIdForChunks, personaId, gmiChunk.isFinal ?? false, { uiCommands: gmiChunk.content });
|
|
1832
1167
|
}
|
|
1833
1168
|
break;
|
|
1834
1169
|
case GMIOutputChunkType.ERROR: {
|
|
1835
1170
|
const errDetails = gmiChunk.errorDetails || { message: gmiChunk.content };
|
|
1836
|
-
await this.
|
|
1171
|
+
await this.chunks.pushError(agentOSStreamId, personaId, gmiInstanceIdForChunks, errDetails.code || GMIErrorCode.GMI_PROCESSING_ERROR, errDetails.message || String(gmiChunk.content) || 'Unknown GMI processing error.', errDetails.details || errDetails);
|
|
1837
1172
|
// If GMI sends an error chunk that it considers final for its operation
|
|
1838
1173
|
if (gmiChunk.isFinal) {
|
|
1839
1174
|
this.activeStreamContexts.delete(agentOSStreamId);
|
|
@@ -1935,8 +1270,8 @@ export class AgentOSOrchestrator {
|
|
|
1935
1270
|
}
|
|
1936
1271
|
}
|
|
1937
1272
|
this.activeStreamContexts.clear();
|
|
1938
|
-
this.
|
|
1939
|
-
this.
|
|
1273
|
+
this.telemetry?.kpiWindows.clear();
|
|
1274
|
+
this.telemetry?.alertState.clear();
|
|
1940
1275
|
this.initialized = false;
|
|
1941
1276
|
console.log('AgentOSOrchestrator: Shutdown complete.');
|
|
1942
1277
|
}
|