@geminilight/mindos 1.1.50 → 1.1.52

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 (108) hide show
  1. package/dist/agent/global-state.d.ts +13 -0
  2. package/dist/agent/global-state.d.ts.map +1 -1
  3. package/dist/agent/global-state.js +13 -0
  4. package/dist/agent/global-state.js.map +1 -1
  5. package/dist/agent/ledger/artifact-ledger.d.ts +84 -0
  6. package/dist/agent/ledger/artifact-ledger.d.ts.map +1 -0
  7. package/dist/agent/ledger/artifact-ledger.js +421 -0
  8. package/dist/agent/ledger/artifact-ledger.js.map +1 -0
  9. package/dist/agent/ledger/index.d.ts +1 -0
  10. package/dist/agent/ledger/index.d.ts.map +1 -1
  11. package/dist/agent/ledger/index.js +1 -0
  12. package/dist/agent/ledger/index.js.map +1 -1
  13. package/dist/agent/runtime/adapter-contracts.d.ts.map +1 -1
  14. package/dist/agent/runtime/adapter-contracts.js +33 -0
  15. package/dist/agent/runtime/adapter-contracts.js.map +1 -1
  16. package/dist/agent/runtime/catalog.d.ts +97 -0
  17. package/dist/agent/runtime/catalog.d.ts.map +1 -0
  18. package/dist/agent/runtime/catalog.js +261 -0
  19. package/dist/agent/runtime/catalog.js.map +1 -0
  20. package/dist/agent/runtime/compatibility.d.ts.map +1 -1
  21. package/dist/agent/runtime/compatibility.js +13 -11
  22. package/dist/agent/runtime/compatibility.js.map +1 -1
  23. package/dist/agent/runtime/detection.d.ts.map +1 -1
  24. package/dist/agent/runtime/detection.js +66 -3
  25. package/dist/agent/runtime/detection.js.map +1 -1
  26. package/dist/agent/runtime/extension-manifest.d.ts +115 -0
  27. package/dist/agent/runtime/extension-manifest.d.ts.map +1 -0
  28. package/dist/agent/runtime/extension-manifest.js +383 -0
  29. package/dist/agent/runtime/extension-manifest.js.map +1 -0
  30. package/dist/agent/runtime/index.d.ts +2 -0
  31. package/dist/agent/runtime/index.d.ts.map +1 -1
  32. package/dist/agent/runtime/index.js +2 -0
  33. package/dist/agent/runtime/index.js.map +1 -1
  34. package/dist/agent/runtime/registry.d.ts +38 -3
  35. package/dist/agent/runtime/registry.d.ts.map +1 -1
  36. package/dist/agent/runtime/registry.js +16 -5
  37. package/dist/agent/runtime/registry.js.map +1 -1
  38. package/dist/agent/turn/index.d.ts +18 -2
  39. package/dist/agent/turn/index.d.ts.map +1 -1
  40. package/dist/agent/turn/index.js +45 -1
  41. package/dist/agent/turn/index.js.map +1 -1
  42. package/dist/protocols/acp/agent-descriptors.d.ts +18 -1
  43. package/dist/protocols/acp/agent-descriptors.d.ts.map +1 -1
  44. package/dist/protocols/acp/agent-descriptors.js +132 -16
  45. package/dist/protocols/acp/agent-descriptors.js.map +1 -1
  46. package/dist/protocols/acp/detect-local.d.ts.map +1 -1
  47. package/dist/protocols/acp/detect-local.js +146 -2
  48. package/dist/protocols/acp/detect-local.js.map +1 -1
  49. package/dist/protocols/acp/index.d.ts +5 -3
  50. package/dist/protocols/acp/index.d.ts.map +1 -1
  51. package/dist/protocols/acp/index.js +26 -24
  52. package/dist/protocols/acp/index.js.map +1 -1
  53. package/dist/protocols/acp/mcp-session-inheritance.d.ts +49 -0
  54. package/dist/protocols/acp/mcp-session-inheritance.d.ts.map +1 -0
  55. package/dist/protocols/acp/mcp-session-inheritance.js +162 -0
  56. package/dist/protocols/acp/mcp-session-inheritance.js.map +1 -0
  57. package/dist/protocols/acp/session.d.ts +9 -1
  58. package/dist/protocols/acp/session.d.ts.map +1 -1
  59. package/dist/protocols/acp/session.js +441 -13
  60. package/dist/protocols/acp/session.js.map +1 -1
  61. package/dist/protocols/acp/subprocess.d.ts +3 -1
  62. package/dist/protocols/acp/subprocess.d.ts.map +1 -1
  63. package/dist/protocols/acp/subprocess.js +67 -2
  64. package/dist/protocols/acp/subprocess.js.map +1 -1
  65. package/dist/protocols/acp/types.d.ts +111 -13
  66. package/dist/protocols/acp/types.d.ts.map +1 -1
  67. package/dist/protocols/acp/types.js.map +1 -1
  68. package/dist/server/contract.d.ts.map +1 -1
  69. package/dist/server/contract.js +2 -0
  70. package/dist/server/contract.js.map +1 -1
  71. package/dist/server/handlers/acp.d.ts +3 -1
  72. package/dist/server/handlers/acp.d.ts.map +1 -1
  73. package/dist/server/handlers/acp.js +4 -1
  74. package/dist/server/handlers/acp.js.map +1 -1
  75. package/dist/server/handlers/agent-runtimes.d.ts.map +1 -1
  76. package/dist/server/handlers/agent-runtimes.js +7 -2
  77. package/dist/server/handlers/agent-runtimes.js.map +1 -1
  78. package/dist/server/handlers/agent-turn.d.ts +5 -0
  79. package/dist/server/handlers/agent-turn.d.ts.map +1 -1
  80. package/dist/server/handlers/agent-turn.js +47 -0
  81. package/dist/server/handlers/agent-turn.js.map +1 -1
  82. package/dist/server/handlers/runtime-adapter-projections.d.ts +90 -0
  83. package/dist/server/handlers/runtime-adapter-projections.d.ts.map +1 -0
  84. package/dist/server/handlers/runtime-adapter-projections.js +329 -0
  85. package/dist/server/handlers/runtime-adapter-projections.js.map +1 -0
  86. package/dist/server/handlers/runtime-artifact-projections.d.ts +14 -0
  87. package/dist/server/handlers/runtime-artifact-projections.d.ts.map +1 -1
  88. package/dist/server/handlers/runtime-artifact-projections.js +30 -7
  89. package/dist/server/handlers/runtime-artifact-projections.js.map +1 -1
  90. package/dist/server/handlers/runtime-readiness.d.ts +7 -4
  91. package/dist/server/handlers/runtime-readiness.d.ts.map +1 -1
  92. package/dist/server/handlers/runtime-readiness.js +113 -1
  93. package/dist/server/handlers/runtime-readiness.js.map +1 -1
  94. package/dist/server/handlers/runtime-session-projections.d.ts +87 -0
  95. package/dist/server/handlers/runtime-session-projections.d.ts.map +1 -0
  96. package/dist/server/handlers/runtime-session-projections.js +290 -0
  97. package/dist/server/handlers/runtime-session-projections.js.map +1 -0
  98. package/dist/server/http.d.ts.map +1 -1
  99. package/dist/server/http.js +18 -2
  100. package/dist/server/http.js.map +1 -1
  101. package/dist/server/index.d.ts +3 -1
  102. package/dist/server/index.d.ts.map +1 -1
  103. package/dist/server/index.js +3 -1
  104. package/dist/server/index.js.map +1 -1
  105. package/dist/server/route-ownership.d.ts.map +1 -1
  106. package/dist/server/route-ownership.js +2 -0
  107. package/dist/server/route-ownership.js.map +1 -1
  108. package/package.json +9 -9
@@ -6,6 +6,9 @@
6
6
  import { spawnAndConnect, killAgent, } from './subprocess.js';
7
7
  import { findAcpAgent } from './registry.js';
8
8
  import { resolveConfiguredAcpAgentEntry } from './agent-descriptors.js';
9
+ import { buildAcpSessionMcpInheritancePlan, } from './mcp-session-inheritance.js';
10
+ import { recordArtifactsFromAcpToolCall } from '../../agent/ledger/artifact-ledger.js';
11
+ import { redactSensitiveText } from '../../agent/redaction.js';
9
12
  function withTimeout(promise, timeoutMs, message) {
10
13
  return new Promise((resolve, reject) => {
11
14
  const timer = setTimeout(() => reject(new Error(message)), timeoutMs);
@@ -53,6 +56,32 @@ function clientCapabilitiesForPermissionMode(mode) {
53
56
  terminal: !readonly,
54
57
  };
55
58
  }
59
+ function resolveSessionMcpInheritance(options, agentCapabilities) {
60
+ if (options?.mcpServers) {
61
+ return {
62
+ servers: options.mcpServers,
63
+ summaries: options.mcpServers.map((server) => ({
64
+ name: server.name,
65
+ type: 'type' in server && server.type === 'http'
66
+ ? 'http'
67
+ : 'type' in server && server.type === 'sse'
68
+ ? 'sse'
69
+ : 'stdio',
70
+ })),
71
+ };
72
+ }
73
+ if (options?.inheritMcpServers === false || !options?.mcpConfig) {
74
+ return { servers: [], summaries: [] };
75
+ }
76
+ const plan = buildAcpSessionMcpInheritancePlan({
77
+ config: options.mcpConfig,
78
+ agentCapabilities,
79
+ });
80
+ return {
81
+ servers: plan.servers,
82
+ summaries: plan.summaries,
83
+ };
84
+ }
56
85
  /* ── State ─────────────────────────────────────────────────────────────── */
57
86
  const sessions = new Map();
58
87
  const sessionConnections = new Map();
@@ -109,17 +138,21 @@ export async function createSessionFromEntry(entry, options) {
109
138
  // Phase 3: session/new
110
139
  let modes;
111
140
  let configOptions;
141
+ let currentModeId;
112
142
  let agentSessionId;
143
+ const mcpInheritance = resolveSessionMcpInheritance(options, agentCapabilities);
113
144
  try {
114
145
  const newResult = await conn.connection.newSession({
115
146
  cwd: sessionCwd,
116
- mcpServers: [],
147
+ mcpServers: mcpInheritance.servers,
117
148
  });
118
149
  if (typeof newResult.sessionId === 'string') {
119
150
  agentSessionId = newResult.sessionId;
120
151
  }
121
152
  modes = parseModes(newResult.modes);
153
+ currentModeId = parseCurrentModeId(newResult.modes);
122
154
  configOptions = parseConfigOptions(newResult.configOptions);
155
+ currentModeId ??= currentModeFromConfig(configOptions);
123
156
  }
124
157
  catch (sessionErr) {
125
158
  const msg = sessionErr.message ?? '';
@@ -139,7 +172,9 @@ export async function createSessionFromEntry(entry, options) {
139
172
  agentCapabilities,
140
173
  modes,
141
174
  configOptions,
175
+ currentModeId,
142
176
  authMethods,
177
+ mcpServers: mcpInheritance.summaries,
143
178
  };
144
179
  sessions.set(sessionId, session);
145
180
  sessionConnections.set(sessionId, conn);
@@ -177,14 +212,18 @@ export async function loadSession(agentId, existingSessionId, options) {
177
212
  }
178
213
  let modes;
179
214
  let configOptions;
215
+ let currentModeId;
216
+ const mcpInheritance = resolveSessionMcpInheritance(options, agentCapabilities);
180
217
  try {
181
218
  const loadResult = await conn.connection.loadSession({
182
219
  sessionId: existingSessionId,
183
220
  cwd: loadCwd,
184
- mcpServers: [],
221
+ mcpServers: mcpInheritance.servers,
185
222
  });
186
223
  modes = parseModes(loadResult.modes);
224
+ currentModeId = parseCurrentModeId(loadResult.modes);
187
225
  configOptions = parseConfigOptions(loadResult.configOptions);
226
+ currentModeId ??= currentModeFromConfig(configOptions);
188
227
  }
189
228
  catch (err) {
190
229
  killAgent(conn.process);
@@ -201,6 +240,8 @@ export async function loadSession(agentId, existingSessionId, options) {
201
240
  agentCapabilities,
202
241
  modes,
203
242
  configOptions,
243
+ currentModeId,
244
+ mcpServers: mcpInheritance.summaries,
204
245
  };
205
246
  sessions.set(existingSessionId, session);
206
247
  sessionConnections.set(existingSessionId, conn);
@@ -230,6 +271,9 @@ export async function listSessions(sessionId, options) {
230
271
  }
231
272
  /* ── Public API — Prompt ──────────────────────────────────────────────── */
232
273
  const PROMPT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
274
+ const TOOL_RAW_TEXT_LIMIT = 4000;
275
+ const INLINE_IMAGE_RESULT_LIMIT = 64 * 1024;
276
+ const INLINE_IMAGE_PREFIX_RE = /^(?:data:image\/|iVBORw0KGgo|\/9j\/|UklGR)/;
233
277
  /**
234
278
  * Send a prompt and collect the full response.
235
279
  * Text arrives via session/update notifications (handled by SDK → Client.sessionUpdate).
@@ -244,10 +288,17 @@ export async function prompt(sessionId, text) {
244
288
  let notificationText = '';
245
289
  conn.callbacks.onSessionUpdate = (params) => {
246
290
  const update = sdkNotificationToUpdate(sessionId, params);
291
+ applySessionUpdate(session, update);
247
292
  if ((update.type === 'agent_message_chunk' || update.type === 'text') && update.text) {
248
293
  notificationText += update.text;
249
294
  }
250
295
  };
296
+ conn.callbacks.onPermissionRequest = (event) => {
297
+ applySessionUpdate(session, { sessionId, type: 'permission_request', permission: event });
298
+ };
299
+ conn.callbacks.onPermissionResolved = (event) => {
300
+ applySessionUpdate(session, { sessionId, type: 'permission_resolved', permission: event });
301
+ };
251
302
  try {
252
303
  const result = await withTimeout(conn.connection.prompt({
253
304
  sessionId: wireSessionId,
@@ -267,6 +318,8 @@ export async function prompt(sessionId, text) {
267
318
  }
268
319
  finally {
269
320
  conn.callbacks.onSessionUpdate = undefined;
321
+ conn.callbacks.onPermissionRequest = undefined;
322
+ conn.callbacks.onPermissionResolved = undefined;
270
323
  }
271
324
  }
272
325
  /**
@@ -282,13 +335,21 @@ export async function promptStream(sessionId, text, onUpdate) {
282
335
  let aggregatedText = '';
283
336
  conn.callbacks.onSessionUpdate = (params) => {
284
337
  const update = sdkNotificationToUpdate(sessionId, params);
338
+ applySessionUpdate(session, update);
285
339
  onUpdate(update);
286
340
  if ((update.type === 'agent_message_chunk' || update.type === 'text') && update.text) {
287
341
  aggregatedText += update.text;
288
342
  }
289
- if (update.type === 'config_option_update' && update.configOptions) {
290
- session.configOptions = update.configOptions;
291
- }
343
+ };
344
+ conn.callbacks.onPermissionRequest = (event) => {
345
+ const update = { sessionId, type: 'permission_request', permission: event };
346
+ applySessionUpdate(session, update);
347
+ onUpdate(update);
348
+ };
349
+ conn.callbacks.onPermissionResolved = (event) => {
350
+ const update = { sessionId, type: 'permission_resolved', permission: event };
351
+ applySessionUpdate(session, update);
352
+ onUpdate(update);
292
353
  };
293
354
  try {
294
355
  const result = await withTimeout(conn.connection.prompt({
@@ -310,6 +371,8 @@ export async function promptStream(sessionId, text, onUpdate) {
310
371
  }
311
372
  finally {
312
373
  conn.callbacks.onSessionUpdate = undefined;
374
+ conn.callbacks.onPermissionRequest = undefined;
375
+ conn.callbacks.onPermissionResolved = undefined;
313
376
  }
314
377
  }
315
378
  /* ── Public API — Session Control ─────────────────────────────────────── */
@@ -330,6 +393,7 @@ export async function setMode(sessionId, modeId) {
330
393
  const { session, conn } = getSessionAndConn(sessionId);
331
394
  const wireSessionId = session.agentSessionId ?? sessionId;
332
395
  await conn.connection.setSessionMode({ sessionId: wireSessionId, modeId });
396
+ session.currentModeId = modeId;
333
397
  session.lastActivityAt = new Date().toISOString();
334
398
  }
335
399
  export async function setConfigOption(sessionId, configId, value) {
@@ -341,8 +405,10 @@ export async function setConfigOption(sessionId, configId, value) {
341
405
  value,
342
406
  });
343
407
  const configOptions = parseConfigOptions(result.configOptions);
344
- if (configOptions)
408
+ if (configOptions) {
345
409
  session.configOptions = configOptions;
410
+ session.currentModeId = currentModeFromConfig(configOptions) ?? session.currentModeId;
411
+ }
346
412
  session.lastActivityAt = new Date().toISOString();
347
413
  return session.configOptions ?? [];
348
414
  }
@@ -370,6 +436,14 @@ export function getActiveSessions() {
370
436
  reapStaleSessions();
371
437
  return [...sessions.values()];
372
438
  }
439
+ export function getSessionSnapshot(sessionId) {
440
+ const session = sessions.get(sessionId);
441
+ return session ? buildAcpSessionSnapshot(session) : undefined;
442
+ }
443
+ export function getActiveSessionSnapshots() {
444
+ reapStaleSessions();
445
+ return [...sessions.values()].map(buildAcpSessionSnapshot);
446
+ }
373
447
  export async function closeAllSessions() {
374
448
  const ids = [...sessions.keys()];
375
449
  await Promise.allSettled(ids.map(id => closeSession(id)));
@@ -390,6 +464,76 @@ function updateSessionState(session, state) {
390
464
  session.state = state;
391
465
  session.lastActivityAt = new Date().toISOString();
392
466
  }
467
+ export function buildAcpSessionSnapshot(session) {
468
+ const modes = session.modes ?? [];
469
+ const configOptions = session.configOptions ?? [];
470
+ const toolCalls = session.toolCalls ?? [];
471
+ const permissionEvents = session.permissionEvents ?? [];
472
+ return {
473
+ schemaVersion: 1,
474
+ sessionId: session.id,
475
+ agentId: session.agentId,
476
+ ...(session.agentSessionId ? { agentSessionId: session.agentSessionId } : {}),
477
+ state: session.state,
478
+ ...(session.cwd ? { cwd: session.cwd } : {}),
479
+ createdAt: session.createdAt,
480
+ lastActivityAt: session.lastActivityAt,
481
+ ...(session.agentCapabilities ? { agentCapabilities: session.agentCapabilities } : {}),
482
+ authMethods: session.authMethods ?? [],
483
+ modes,
484
+ ...(session.currentModeId ? { currentModeId: session.currentModeId } : {}),
485
+ configOptions,
486
+ controls: {
487
+ model: buildControlSnapshot(configOptions, 'model'),
488
+ mode: buildModeControlSnapshot(configOptions, modes, session.currentModeId),
489
+ thoughtLevel: buildControlSnapshot(configOptions, 'thought_level'),
490
+ },
491
+ availableCommands: session.availableCommands ?? [],
492
+ toolCalls,
493
+ toolSummary: summarizeToolCalls(toolCalls),
494
+ permissionEvents,
495
+ pendingPermissions: permissionEvents.filter((event) => event.status === 'pending'),
496
+ ...(session.sessionInfo ? { sessionInfo: session.sessionInfo } : {}),
497
+ mcpServers: session.mcpServers ?? [],
498
+ };
499
+ }
500
+ function applySessionUpdate(session, update) {
501
+ session.lastActivityAt = new Date().toISOString();
502
+ if (update.type === 'available_commands_update') {
503
+ session.availableCommands = parseAvailableCommands(update.availableCommands);
504
+ return;
505
+ }
506
+ if (update.type === 'current_mode_update' && update.currentModeId) {
507
+ session.currentModeId = update.currentModeId;
508
+ return;
509
+ }
510
+ if (update.type === 'config_option_update' && update.configOptions) {
511
+ session.configOptions = update.configOptions;
512
+ session.currentModeId = currentModeFromConfig(update.configOptions) ?? session.currentModeId;
513
+ return;
514
+ }
515
+ if ((update.type === 'tool_call' || update.type === 'tool_call_update') && update.toolCall) {
516
+ session.toolCalls = upsertToolCall(session.toolCalls, update.toolCall);
517
+ recordArtifactsFromAcpToolCall({
518
+ runtimeId: session.agentId,
519
+ sessionId: session.id,
520
+ ...(session.agentSessionId ? { externalSessionId: session.agentSessionId } : {}),
521
+ ...(session.cwd ? { cwd: session.cwd } : {}),
522
+ toolCall: update.toolCall,
523
+ });
524
+ return;
525
+ }
526
+ if (update.type === 'session_info_update' && update.sessionInfo) {
527
+ session.sessionInfo = {
528
+ ...session.sessionInfo,
529
+ ...update.sessionInfo,
530
+ };
531
+ return;
532
+ }
533
+ if ((update.type === 'permission_request' || update.type === 'permission_resolved') && update.permission) {
534
+ session.permissionEvents = upsertPermissionEvent(session.permissionEvents, update.permission);
535
+ }
536
+ }
393
537
  /* ── Internal — SDK notification → MindOS update ──────────────────────── */
394
538
  /**
395
539
  * Convert SDK SessionNotification to MindOS AcpSessionUpdate.
@@ -416,13 +560,20 @@ function sdkNotificationToUpdate(sessionId, params) {
416
560
  case 'tool_call':
417
561
  case 'tool_call_update': {
418
562
  const tc = update;
563
+ const rawOutputPointers = extractRawOutputPointers(tc.rawOutput ?? tc.raw_output);
564
+ const locations = [
565
+ ...parseToolCallLocations(tc.locations),
566
+ ...rawOutputPointers.locations,
567
+ ];
419
568
  base.toolCall = {
420
569
  toolCallId: String(tc.toolCallId ?? ''),
421
570
  title: typeof tc.title === 'string' ? tc.title : undefined,
422
571
  status: tc.status ?? 'pending',
423
572
  kind: tc.kind,
424
- rawInput: typeof tc.rawInput === 'string' ? tc.rawInput : undefined,
425
- rawOutput: typeof tc.rawOutput === 'string' ? tc.rawOutput : undefined,
573
+ rawInput: safeToolRawText(tc.rawInput),
574
+ rawOutput: safeToolRawText(tc.rawOutput ?? tc.raw_output),
575
+ content: parseToolCallContent(tc.content),
576
+ ...(locations.length > 0 ? { locations } : {}),
426
577
  };
427
578
  break;
428
579
  }
@@ -457,6 +608,110 @@ function sdkNotificationToUpdate(sessionId, params) {
457
608
  }
458
609
  return base;
459
610
  }
611
+ function safeToolRawText(value) {
612
+ if (typeof value !== 'string')
613
+ return undefined;
614
+ const trimmed = value.trim();
615
+ if (trimmed.length > INLINE_IMAGE_RESULT_LIMIT
616
+ && INLINE_IMAGE_PREFIX_RE.test(trimmed)) {
617
+ return undefined;
618
+ }
619
+ const redacted = redactSensitiveText(trimmed);
620
+ return redacted.length > TOOL_RAW_TEXT_LIMIT
621
+ ? `${redacted.slice(0, TOOL_RAW_TEXT_LIMIT)}...`
622
+ : redacted;
623
+ }
624
+ function parseToolCallLocations(value) {
625
+ if (!Array.isArray(value))
626
+ return [];
627
+ const locations = [];
628
+ const seen = new Set();
629
+ for (const item of value) {
630
+ if (!item || typeof item !== 'object' || Array.isArray(item))
631
+ continue;
632
+ const record = item;
633
+ const rawPath = typeof record.path === 'string'
634
+ ? record.path
635
+ : typeof record.uri === 'string' && record.uri.startsWith('file://')
636
+ ? record.uri.slice('file://'.length)
637
+ : '';
638
+ const path = rawPath.trim();
639
+ if (!path)
640
+ continue;
641
+ const line = typeof record.line === 'number' && Number.isFinite(record.line)
642
+ ? Math.max(1, Math.floor(record.line))
643
+ : undefined;
644
+ const key = `${path}:${line ?? ''}`;
645
+ if (seen.has(key))
646
+ continue;
647
+ seen.add(key);
648
+ locations.push({ path, ...(line ? { line } : {}) });
649
+ if (locations.length >= 50)
650
+ break;
651
+ }
652
+ return locations;
653
+ }
654
+ function parseToolCallContent(value) {
655
+ if (!Array.isArray(value))
656
+ return undefined;
657
+ const blocks = [];
658
+ for (const item of value) {
659
+ const block = parseToolCallContentBlock(item);
660
+ if (block)
661
+ blocks.push(block);
662
+ if (blocks.length >= 50)
663
+ break;
664
+ }
665
+ return blocks.length > 0 ? blocks : undefined;
666
+ }
667
+ function parseToolCallContentBlock(value) {
668
+ if (!value || typeof value !== 'object' || Array.isArray(value))
669
+ return null;
670
+ const record = value;
671
+ if (record.type === 'resource_link' && typeof record.uri === 'string') {
672
+ return {
673
+ type: 'resource_link',
674
+ uri: record.uri,
675
+ name: typeof record.name === 'string' && record.name.trim() ? record.name : 'resource',
676
+ };
677
+ }
678
+ if (record.type === 'resource') {
679
+ const resource = record.resource;
680
+ if (!resource || typeof resource !== 'object' || Array.isArray(resource))
681
+ return null;
682
+ const resourceRecord = resource;
683
+ if (typeof resourceRecord.uri !== 'string')
684
+ return null;
685
+ return {
686
+ type: 'resource',
687
+ resource: {
688
+ uri: resourceRecord.uri,
689
+ ...(typeof resourceRecord.text === 'string' ? { text: safeToolRawText(resourceRecord.text) ?? '' } : {}),
690
+ },
691
+ };
692
+ }
693
+ return null;
694
+ }
695
+ function extractRawOutputPointers(value) {
696
+ if (!value || typeof value !== 'object' || Array.isArray(value))
697
+ return { locations: [] };
698
+ const record = value;
699
+ const candidates = [
700
+ record.saved_path,
701
+ record.savedPath,
702
+ readNestedString(record.image, 'path'),
703
+ readNestedString(record.artifact, 'path'),
704
+ ];
705
+ const locations = candidates
706
+ .filter((candidate) => typeof candidate === 'string' && candidate.trim().length > 0)
707
+ .map((candidate) => ({ path: candidate.trim() }));
708
+ return { locations };
709
+ }
710
+ function readNestedString(value, key) {
711
+ return value && typeof value === 'object' && !Array.isArray(value) && typeof value[key] === 'string'
712
+ ? value[key]
713
+ : undefined;
714
+ }
460
715
  /* ── Internal — Parsers ───────────────────────────────────────────────── */
461
716
  function parseAgentCapabilities(raw) {
462
717
  if (!raw || typeof raw !== 'object')
@@ -499,6 +754,20 @@ function parseModes(raw) {
499
754
  }))
500
755
  .filter(m => m.id && m.name);
501
756
  }
757
+ function parseCurrentModeId(raw) {
758
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
759
+ return undefined;
760
+ const obj = raw;
761
+ if (typeof obj.currentModeId === 'string' && obj.currentModeId.trim())
762
+ return obj.currentModeId.trim();
763
+ const currentMode = obj.currentMode;
764
+ if (currentMode && typeof currentMode === 'object' && !Array.isArray(currentMode)) {
765
+ const id = currentMode.id;
766
+ if (typeof id === 'string' && id.trim())
767
+ return id.trim();
768
+ }
769
+ return undefined;
770
+ }
502
771
  function parseConfigOptions(raw) {
503
772
  if (!Array.isArray(raw) || raw.length === 0)
504
773
  return undefined;
@@ -508,15 +777,174 @@ function parseConfigOptions(raw) {
508
777
  type: 'select',
509
778
  configId: String(o.configId ?? o.id ?? ''),
510
779
  category: String(o.category ?? 'other'),
511
- label: typeof o.label === 'string' ? o.label : undefined,
780
+ label: typeof o.label === 'string' ? o.label : typeof o.name === 'string' ? o.name : undefined,
512
781
  currentValue: String(o.currentValue ?? ''),
513
- options: Array.isArray(o.options) ? o.options.map((opt) => {
514
- const optObj = opt;
515
- return { id: String(optObj.id ?? ''), label: String(optObj.label ?? '') };
516
- }) : [],
782
+ options: parseConfigOptionEntries(o.options),
517
783
  }))
518
784
  .filter(o => o.configId);
519
785
  }
786
+ function parseConfigOptionEntries(raw) {
787
+ if (!Array.isArray(raw))
788
+ return [];
789
+ const entries = [];
790
+ const pushEntry = (option) => {
791
+ if (!option || typeof option !== 'object' || Array.isArray(option))
792
+ return;
793
+ const record = option;
794
+ const id = String(record.id ?? record.value ?? '').trim();
795
+ const label = String(record.label ?? record.name ?? id).trim();
796
+ if (id)
797
+ entries.push({ id, label: label || id });
798
+ };
799
+ for (const item of raw) {
800
+ if (item && typeof item === 'object' && !Array.isArray(item) && Array.isArray(item.options)) {
801
+ for (const nested of item.options) {
802
+ pushEntry(nested);
803
+ }
804
+ continue;
805
+ }
806
+ pushEntry(item);
807
+ }
808
+ return entries;
809
+ }
810
+ function parseAvailableCommands(raw) {
811
+ if (!Array.isArray(raw))
812
+ return [];
813
+ const seen = new Set();
814
+ const commands = [];
815
+ for (const entry of raw) {
816
+ const command = normalizeAvailableCommand(entry);
817
+ if (!command || seen.has(command.id))
818
+ continue;
819
+ seen.add(command.id);
820
+ commands.push(command);
821
+ if (commands.length >= 100)
822
+ break;
823
+ }
824
+ return commands;
825
+ }
826
+ function normalizeAvailableCommand(entry) {
827
+ if (typeof entry === 'string') {
828
+ const name = entry.trim().replace(/^\//, '');
829
+ return name ? { id: name, name } : null;
830
+ }
831
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry))
832
+ return null;
833
+ const record = entry;
834
+ const rawName = record.name ?? record.id ?? record.command ?? record.title;
835
+ if (typeof rawName !== 'string')
836
+ return null;
837
+ const name = rawName.trim().replace(/^\//, '');
838
+ if (!name)
839
+ return null;
840
+ const id = typeof record.id === 'string' && record.id.trim()
841
+ ? record.id.trim().replace(/^\//, '')
842
+ : name;
843
+ const description = typeof record.description === 'string' && record.description.trim()
844
+ ? record.description.trim().slice(0, 300)
845
+ : undefined;
846
+ return {
847
+ id,
848
+ name,
849
+ ...(description ? { description } : {}),
850
+ };
851
+ }
852
+ function currentModeFromConfig(configOptions) {
853
+ const option = findConfigOption(configOptions ?? [], 'mode');
854
+ return option?.currentValue?.trim() || undefined;
855
+ }
856
+ function buildControlSnapshot(configOptions, category) {
857
+ const option = findConfigOption(configOptions, category);
858
+ if (!option) {
859
+ return {
860
+ status: 'unavailable',
861
+ source: 'unavailable',
862
+ options: [],
863
+ };
864
+ }
865
+ return {
866
+ status: 'available',
867
+ source: 'observed',
868
+ configId: option.configId,
869
+ currentValue: option.currentValue,
870
+ options: option.options,
871
+ };
872
+ }
873
+ function buildModeControlSnapshot(configOptions, modes, currentModeId) {
874
+ const option = findConfigOption(configOptions, 'mode');
875
+ if (option) {
876
+ return {
877
+ status: 'available',
878
+ source: 'observed',
879
+ configId: option.configId,
880
+ ...(currentModeId ?? option.currentValue ? { currentValue: currentModeId ?? option.currentValue } : {}),
881
+ options: option.options,
882
+ };
883
+ }
884
+ if (modes.length === 0) {
885
+ return {
886
+ status: 'unavailable',
887
+ source: 'unavailable',
888
+ options: [],
889
+ };
890
+ }
891
+ return {
892
+ status: 'available',
893
+ source: currentModeId ? 'observed' : 'declared',
894
+ ...(currentModeId ? { currentValue: currentModeId } : {}),
895
+ options: modes.map((mode) => ({ id: mode.id, label: mode.name })),
896
+ };
897
+ }
898
+ function findConfigOption(configOptions, category) {
899
+ return configOptions.find((option) => {
900
+ const optionCategory = option.category.toLowerCase();
901
+ const configId = option.configId.toLowerCase();
902
+ if (category === 'thought_level') {
903
+ return optionCategory === 'thought_level'
904
+ || optionCategory === 'reasoning'
905
+ || configId === 'thought_level'
906
+ || configId === 'thinking'
907
+ || configId === 'reasoning_effort';
908
+ }
909
+ return optionCategory === category || configId === category;
910
+ });
911
+ }
912
+ function upsertToolCall(existing, update) {
913
+ if (!update.toolCallId)
914
+ return existing ?? [];
915
+ const next = [...(existing ?? [])];
916
+ const index = next.findIndex((toolCall) => toolCall.toolCallId === update.toolCallId);
917
+ if (index === -1)
918
+ return [...next, update].slice(-100);
919
+ next[index] = {
920
+ ...next[index],
921
+ ...update,
922
+ status: update.status ?? next[index].status,
923
+ };
924
+ return next;
925
+ }
926
+ function summarizeToolCalls(toolCalls) {
927
+ return {
928
+ total: toolCalls.length,
929
+ pending: toolCalls.filter((toolCall) => toolCall.status === 'pending').length,
930
+ inProgress: toolCalls.filter((toolCall) => toolCall.status === 'in_progress').length,
931
+ completed: toolCalls.filter((toolCall) => toolCall.status === 'completed').length,
932
+ failed: toolCalls.filter((toolCall) => toolCall.status === 'failed').length,
933
+ };
934
+ }
935
+ function upsertPermissionEvent(existing, update) {
936
+ const next = [...(existing ?? [])];
937
+ const index = next.findIndex((event) => event.requestId === update.requestId);
938
+ if (index === -1)
939
+ return [...next, update].slice(-100);
940
+ next[index] = {
941
+ ...next[index],
942
+ ...update,
943
+ options: update.options.length > 0 ? update.options : next[index].options,
944
+ requestedAt: next[index].requestedAt || update.requestedAt,
945
+ };
946
+ return next;
947
+ }
520
948
  /* ── Internal — Session limits ─────────────────────────────────────────── */
521
949
  function checkSessionLimits(agentId) {
522
950
  if (sessions.size >= MAX_TOTAL_SESSIONS) {