@framers/agentos 0.1.48 → 0.1.50

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.
Files changed (93) hide show
  1. package/dist/api/AgentOS.d.ts +1 -53
  2. package/dist/api/AgentOS.d.ts.map +1 -1
  3. package/dist/api/AgentOS.js +5 -256
  4. package/dist/api/AgentOS.js.map +1 -1
  5. package/dist/api/AgentOSOrchestrator.d.ts +5 -276
  6. package/dist/api/AgentOSOrchestrator.d.ts.map +1 -1
  7. package/dist/api/AgentOSOrchestrator.js +107 -772
  8. package/dist/api/AgentOSOrchestrator.js.map +1 -1
  9. package/dist/api/StreamChunkEmitter.d.ts +32 -0
  10. package/dist/api/StreamChunkEmitter.d.ts.map +1 -0
  11. package/dist/api/StreamChunkEmitter.js +141 -0
  12. package/dist/api/StreamChunkEmitter.js.map +1 -0
  13. package/dist/api/TaskOutcomeTelemetryManager.d.ts +107 -0
  14. package/dist/api/TaskOutcomeTelemetryManager.d.ts.map +1 -0
  15. package/dist/api/TaskOutcomeTelemetryManager.js +331 -0
  16. package/dist/api/TaskOutcomeTelemetryManager.js.map +1 -0
  17. package/dist/api/errors.d.ts +17 -0
  18. package/dist/api/errors.d.ts.map +1 -0
  19. package/dist/api/errors.js +25 -0
  20. package/dist/api/errors.js.map +1 -0
  21. package/dist/api/turn-phases/conversation-history.d.ts +22 -0
  22. package/dist/api/turn-phases/conversation-history.d.ts.map +1 -0
  23. package/dist/api/turn-phases/conversation-history.js +55 -0
  24. package/dist/api/turn-phases/conversation-history.js.map +1 -0
  25. package/dist/api/turn-phases/index.d.ts +5 -0
  26. package/dist/api/turn-phases/index.d.ts.map +1 -0
  27. package/dist/api/turn-phases/index.js +5 -0
  28. package/dist/api/turn-phases/index.js.map +1 -0
  29. package/dist/api/turn-phases/long-term-memory.d.ts +35 -0
  30. package/dist/api/turn-phases/long-term-memory.d.ts.map +1 -0
  31. package/dist/api/turn-phases/long-term-memory.js +82 -0
  32. package/dist/api/turn-phases/long-term-memory.js.map +1 -0
  33. package/dist/api/turn-phases/prompt-profile.d.ts +22 -0
  34. package/dist/api/turn-phases/prompt-profile.d.ts.map +1 -0
  35. package/dist/api/turn-phases/prompt-profile.js +34 -0
  36. package/dist/api/turn-phases/prompt-profile.js.map +1 -0
  37. package/dist/api/turn-phases/rolling-summary.d.ts +28 -0
  38. package/dist/api/turn-phases/rolling-summary.d.ts.map +1 -0
  39. package/dist/api/turn-phases/rolling-summary.js +87 -0
  40. package/dist/api/turn-phases/rolling-summary.js.map +1 -0
  41. package/dist/api/types/OrchestratorConfig.d.ts +89 -0
  42. package/dist/api/types/OrchestratorConfig.d.ts.map +1 -0
  43. package/dist/api/types/OrchestratorConfig.js +6 -0
  44. package/dist/api/types/OrchestratorConfig.js.map +1 -0
  45. package/dist/core/agents/index.d.ts +7 -0
  46. package/dist/core/agents/index.d.ts.map +1 -0
  47. package/dist/core/agents/index.js +7 -0
  48. package/dist/core/agents/index.js.map +1 -0
  49. package/dist/core/ai_utilities/HybridUtilityAI.d.ts +54 -0
  50. package/dist/core/ai_utilities/HybridUtilityAI.d.ts.map +1 -1
  51. package/dist/core/ai_utilities/HybridUtilityAI.js +119 -1
  52. package/dist/core/ai_utilities/HybridUtilityAI.js.map +1 -1
  53. package/dist/core/ai_utilities/index.d.ts +5 -0
  54. package/dist/core/ai_utilities/index.d.ts.map +1 -0
  55. package/dist/core/ai_utilities/index.js +5 -0
  56. package/dist/core/ai_utilities/index.js.map +1 -0
  57. package/dist/core/audio/index.d.ts +5 -0
  58. package/dist/core/audio/index.d.ts.map +1 -0
  59. package/dist/core/audio/index.js +5 -0
  60. package/dist/core/audio/index.js.map +1 -0
  61. package/dist/core/conversation/index.d.ts +8 -0
  62. package/dist/core/conversation/index.d.ts.map +1 -0
  63. package/dist/core/conversation/index.js +8 -0
  64. package/dist/core/conversation/index.js.map +1 -0
  65. package/dist/core/index.d.ts +45 -0
  66. package/dist/core/index.d.ts.map +1 -0
  67. package/dist/core/index.js +66 -0
  68. package/dist/core/index.js.map +1 -0
  69. package/dist/core/orchestration/index.d.ts +6 -0
  70. package/dist/core/orchestration/index.d.ts.map +1 -0
  71. package/dist/core/orchestration/index.js +6 -0
  72. package/dist/core/orchestration/index.js.map +1 -0
  73. package/dist/core/streaming/AsyncStreamClientBridge.d.ts +29 -0
  74. package/dist/core/streaming/AsyncStreamClientBridge.d.ts.map +1 -0
  75. package/dist/core/streaming/AsyncStreamClientBridge.js +130 -0
  76. package/dist/core/streaming/AsyncStreamClientBridge.js.map +1 -0
  77. package/dist/core/streaming/index.d.ts +4 -0
  78. package/dist/core/streaming/index.d.ts.map +1 -0
  79. package/dist/core/streaming/index.js +4 -0
  80. package/dist/core/streaming/index.js.map +1 -0
  81. package/dist/core/tools/index.d.ts +13 -0
  82. package/dist/core/tools/index.d.ts.map +1 -0
  83. package/dist/core/tools/index.js +8 -0
  84. package/dist/core/tools/index.js.map +1 -0
  85. package/dist/rag/EmbeddingManager.d.ts.map +1 -1
  86. package/dist/rag/EmbeddingManager.js +5 -7
  87. package/dist/rag/EmbeddingManager.js.map +1 -1
  88. package/dist/rag/HydeRetriever.d.ts +1 -0
  89. package/dist/rag/HydeRetriever.d.ts.map +1 -1
  90. package/dist/rag/HydeRetriever.js +41 -5
  91. package/dist/rag/HydeRetriever.js.map +1 -1
  92. package/dist/speech/providers/BuiltInAdaptiveVadProvider.d.ts +1 -1
  93. 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, selectPromptProfile, } from '../core/prompting/PromptProfileRouter.js';
19
- import { DEFAULT_ROLLING_SUMMARY_COMPACTION_CONFIG, maybeCompactConversationMessages, } from '../core/conversation/RollingSummaryCompactor.js';
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 { getActiveTraceMetadata, recordAgentOSToolResultMetrics, recordAgentOSTurnMetrics, recordExceptionOnActiveSpan, runWithSpanContext, shouldIncludeTraceInAgentOSResponses, startAgentOSSpan, withAgentOSSpan, } from '../core/observability/otel.js';
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
- function normalizeTaskOutcomeOverride(customFlags) {
86
- if (!customFlags)
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
- if (dependencies.taskOutcomeTelemetryStore && this.config.taskOutcomeTelemetry.enabled) {
350
- try {
351
- const persisted = await dependencies.taskOutcomeTelemetryStore.loadWindows();
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
- resolveTaskOutcomeScopeKey(args) {
407
- const scope = this.config.taskOutcomeTelemetry.scope;
408
- const org = normalizeOrganizationId(args.organizationId) ?? 'none';
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.pushChunkToStream(streamId, AgentOSResponseChunkType.WORKFLOW_UPDATE, gmiId, context.personaId, false, {
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.pushErrorChunk(agentOSStreamId, input.selectedPersonaId || 'unknown_persona', 'orchestrator_critical', GMIErrorCode.INTERNAL_SERVER_ERROR, `A critical orchestration error occurred: ${criticalError.message}`, { name: criticalError.name, stack: criticalError.stack });
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.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.SYSTEM_PROGRESS, gmiInstanceIdForChunks, currentPersonaId, false, { message: `Initializing persona ${currentPersonaId}... GMI: ${gmiInstanceIdForChunks}`, progressPercentage: 10 });
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.maybeApplyAdaptiveExecutionPolicy({
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.emitExecutionLifecycleUpdate({
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.emitExecutionLifecycleUpdate({
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 (text + JSON metadata) ---
1001
- let rollingSummaryResult = null;
1002
- let rollingSummaryProfileId = null;
1003
- let rollingSummaryConfigForTurn = this.config.rollingSummaryCompactionConfig;
1004
- let rollingSummarySystemPromptForTurn = this.config.rollingSummarySystemPrompt;
1005
- if (this.config.rollingSummaryCompactionProfilesConfig) {
1006
- const profilesConfig = this.config.rollingSummaryCompactionProfilesConfig;
1007
- const picked = pickByMode(profilesConfig.defaultProfileByMode, modeForRouting) ??
1008
- profilesConfig.defaultProfileId;
1009
- rollingSummaryProfileId = picked;
1010
- const profile = profilesConfig.profiles?.[picked];
1011
- if (profile?.config) {
1012
- rollingSummaryConfigForTurn = profile.config;
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 (concise/deep/planner/reviewer) ---
1075
- let promptProfileSelection = null;
1076
- if (conversationContext && this.config.promptProfileConfig) {
1077
- try {
1078
- const rawPrev = conversationContext.getMetadata('promptProfileState');
1079
- const previousState = rawPrev && typeof rawPrev === 'object' && typeof rawPrev.presetId === 'string'
1080
- ? rawPrev
1081
- : null;
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 (user/persona/org) ---
1108
- let longTermMemoryContextText = null;
1109
- let longTermMemoryRetrievalDiagnostics;
1110
- let longTermMemoryShouldReview = false;
1111
- let longTermMemoryReviewReason = null;
1112
- if (conversationContext &&
1113
- this.dependencies.longTermMemoryRetriever &&
1114
- Boolean(longTermMemoryPolicy?.enabled) &&
1115
- (Boolean(longTermMemoryPolicy?.scopes?.user) ||
1116
- Boolean(longTermMemoryPolicy?.scopes?.persona) ||
1117
- Boolean(longTermMemoryPolicy?.scopes?.organization))) {
1118
- try {
1119
- const queryText = gmiInput.type === GMIInteractionType.TEXT && typeof gmiInput.content === 'string'
1120
- ? gmiInput.content.trim()
1121
- : gmiInput.type === GMIInteractionType.MULTIMODAL_CONTENT
1122
- ? JSON.stringify(gmiInput.content).trim()
1123
- : '';
1124
- const userTurnCount = conversationContext.getAllMessages().filter((m) => m?.role === MessageRole.USER).length;
1125
- const recallConfig = this.config.longTermMemoryRecall;
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
- // Provide a durable history snapshot for prompt construction so persona switches share memory.
1189
- // When rolling-summary compaction is enabled and a summary exists, trim history to:
1190
- // - keep the configured head messages verbatim
1191
- // - keep only messages after `summaryUptoTimestamp` (unsummarized tail)
1192
- if (conversationContext) {
1193
- const excludeRoles = new Set([MessageRole.ERROR, MessageRole.THOUGHT]);
1194
- const useTrimmedHistory = rollingSummaryEnabled &&
1195
- typeof rollingSummaryResult?.summaryUptoTimestamp === 'number' &&
1196
- rollingSummaryText.length > 0;
1197
- const rawHistory = useTrimmedHistory
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.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.METADATA_UPDATE, gmiInstanceIdForChunks, currentPersonaId, false, {
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.emitExecutionLifecycleUpdate({
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.pushErrorChunk(agentOSStreamId, currentPersonaId, gmiInstanceIdForChunks, GMIErrorCode.RATE_LIMIT_EXCEEDED, // Or a more specific code
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.updateTaskOutcomeKpi({
773
+ const taskOutcomeKpi = this.telemetry.updateKpi({
1439
774
  outcome: taskOutcome,
1440
775
  organizationId: organizationIdForMemory,
1441
776
  personaId: currentPersonaId,
1442
777
  });
1443
- const taskOutcomeAlert = this.maybeBuildTaskOutcomeAlert(taskOutcomeKpi);
1444
- await this.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.METADATA_UPDATE, gmiInstanceIdForChunks, currentPersonaId, false, {
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.emitExecutionLifecycleUpdate({
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.emitExecutionLifecycleUpdate({
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.emitExecutionLifecycleUpdate({
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.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.FINAL_RESPONSE, gmiInstanceIdForChunks, currentPersonaId, true, {
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.updateTaskOutcomeKpi({
884
+ const taskOutcomeKpi = this.telemetry.updateKpi({
1550
885
  outcome: turnMetricsTaskOutcome,
1551
886
  organizationId: organizationIdForMemory,
1552
887
  personaId: currentPersonaId,
1553
888
  });
1554
- const taskOutcomeAlert = this.maybeBuildTaskOutcomeAlert(taskOutcomeKpi);
889
+ const taskOutcomeAlert = this.telemetry.maybeBuildAlert(taskOutcomeKpi);
1555
890
  console.error(`AgentOSOrchestrator: Error during _processTurnInternal for stream ${agentOSStreamId}:`, gmiErr);
1556
- await this.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.METADATA_UPDATE, gmiInstanceIdForChunks, currentPersonaId ?? 'unknown_persona', false, {
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.emitExecutionLifecycleUpdate({
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.pushErrorChunk(agentOSStreamId, currentPersonaId ?? 'unknown_persona', gmiInstanceIdForChunks, gmiErr.code, gmiErr.message, gmiErr.details);
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.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.TOOL_RESULT_EMISSION, gmiInstanceIdForChunks, personaId, false, { toolCallId, toolName, toolResult: toolOutput, isSuccess, errorMessage });
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.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.TOOL_CALL_REQUEST, gmiInstanceIdForChunks, personaId, false, // Not final, more interaction expected
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.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.FINAL_RESPONSE, gmiInstanceIdForChunks, personaId, true, {
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.pushErrorChunk(agentOSStreamId, personaId, gmiInstanceIdForChunks, gmiErr.code, gmiErr.message, gmiErr.details);
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.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.TEXT_DELTA, gmiInstanceIdForChunks, personaId, false, // text delta is not final by itself
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.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.UI_COMMAND, gmiInstanceIdForChunks, personaId, false, { uiCommands: gmiOutput.uiCommands });
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.pushErrorChunk(agentOSStreamId, personaId, gmiInstanceIdForChunks, gmiOutput.error.code, gmiOutput.error.message, gmiOutput.error.details);
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.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.FINAL_RESPONSE, gmiInstanceIdForChunks, personaId, true, {
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.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.TEXT_DELTA, gmiInstanceIdForChunks, personaId, gmiChunk.isFinal ?? false, { textDelta: gmiChunk.content });
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.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.SYSTEM_PROGRESS, gmiInstanceIdForChunks, personaId, gmiChunk.isFinal ?? false, progressContent);
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.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.TOOL_CALL_REQUEST, gmiInstanceIdForChunks, personaId, false, // Tool call request is not final for the AgentOS turn
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.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.UI_COMMAND, gmiInstanceIdForChunks, personaId, gmiChunk.isFinal ?? false, { uiCommands: gmiChunk.content });
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.pushErrorChunk(agentOSStreamId, personaId, gmiInstanceIdForChunks, errDetails.code || GMIErrorCode.GMI_PROCESSING_ERROR, errDetails.message || String(gmiChunk.content) || 'Unknown GMI processing error.', errDetails.details || errDetails);
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.taskOutcomeKpiWindows.clear();
1939
- this.taskOutcomeAlertState.clear();
1273
+ this.telemetry?.kpiWindows.clear();
1274
+ this.telemetry?.alertState.clear();
1940
1275
  this.initialized = false;
1941
1276
  console.log('AgentOSOrchestrator: Shutdown complete.');
1942
1277
  }