@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.
- package/dist/agent/global-state.d.ts +13 -0
- package/dist/agent/global-state.d.ts.map +1 -1
- package/dist/agent/global-state.js +13 -0
- package/dist/agent/global-state.js.map +1 -1
- package/dist/agent/ledger/artifact-ledger.d.ts +84 -0
- package/dist/agent/ledger/artifact-ledger.d.ts.map +1 -0
- package/dist/agent/ledger/artifact-ledger.js +421 -0
- package/dist/agent/ledger/artifact-ledger.js.map +1 -0
- package/dist/agent/ledger/index.d.ts +1 -0
- package/dist/agent/ledger/index.d.ts.map +1 -1
- package/dist/agent/ledger/index.js +1 -0
- package/dist/agent/ledger/index.js.map +1 -1
- package/dist/agent/runtime/adapter-contracts.d.ts.map +1 -1
- package/dist/agent/runtime/adapter-contracts.js +33 -0
- package/dist/agent/runtime/adapter-contracts.js.map +1 -1
- package/dist/agent/runtime/catalog.d.ts +97 -0
- package/dist/agent/runtime/catalog.d.ts.map +1 -0
- package/dist/agent/runtime/catalog.js +261 -0
- package/dist/agent/runtime/catalog.js.map +1 -0
- package/dist/agent/runtime/compatibility.d.ts.map +1 -1
- package/dist/agent/runtime/compatibility.js +13 -11
- package/dist/agent/runtime/compatibility.js.map +1 -1
- package/dist/agent/runtime/detection.d.ts.map +1 -1
- package/dist/agent/runtime/detection.js +66 -3
- package/dist/agent/runtime/detection.js.map +1 -1
- package/dist/agent/runtime/extension-manifest.d.ts +115 -0
- package/dist/agent/runtime/extension-manifest.d.ts.map +1 -0
- package/dist/agent/runtime/extension-manifest.js +383 -0
- package/dist/agent/runtime/extension-manifest.js.map +1 -0
- package/dist/agent/runtime/index.d.ts +2 -0
- package/dist/agent/runtime/index.d.ts.map +1 -1
- package/dist/agent/runtime/index.js +2 -0
- package/dist/agent/runtime/index.js.map +1 -1
- package/dist/agent/runtime/registry.d.ts +38 -3
- package/dist/agent/runtime/registry.d.ts.map +1 -1
- package/dist/agent/runtime/registry.js +16 -5
- package/dist/agent/runtime/registry.js.map +1 -1
- package/dist/agent/turn/index.d.ts +18 -2
- package/dist/agent/turn/index.d.ts.map +1 -1
- package/dist/agent/turn/index.js +45 -1
- package/dist/agent/turn/index.js.map +1 -1
- package/dist/protocols/acp/agent-descriptors.d.ts +18 -1
- package/dist/protocols/acp/agent-descriptors.d.ts.map +1 -1
- package/dist/protocols/acp/agent-descriptors.js +132 -16
- package/dist/protocols/acp/agent-descriptors.js.map +1 -1
- package/dist/protocols/acp/detect-local.d.ts.map +1 -1
- package/dist/protocols/acp/detect-local.js +146 -2
- package/dist/protocols/acp/detect-local.js.map +1 -1
- package/dist/protocols/acp/index.d.ts +5 -3
- package/dist/protocols/acp/index.d.ts.map +1 -1
- package/dist/protocols/acp/index.js +26 -24
- package/dist/protocols/acp/index.js.map +1 -1
- package/dist/protocols/acp/mcp-session-inheritance.d.ts +49 -0
- package/dist/protocols/acp/mcp-session-inheritance.d.ts.map +1 -0
- package/dist/protocols/acp/mcp-session-inheritance.js +162 -0
- package/dist/protocols/acp/mcp-session-inheritance.js.map +1 -0
- package/dist/protocols/acp/session.d.ts +9 -1
- package/dist/protocols/acp/session.d.ts.map +1 -1
- package/dist/protocols/acp/session.js +441 -13
- package/dist/protocols/acp/session.js.map +1 -1
- package/dist/protocols/acp/subprocess.d.ts +3 -1
- package/dist/protocols/acp/subprocess.d.ts.map +1 -1
- package/dist/protocols/acp/subprocess.js +67 -2
- package/dist/protocols/acp/subprocess.js.map +1 -1
- package/dist/protocols/acp/types.d.ts +111 -13
- package/dist/protocols/acp/types.d.ts.map +1 -1
- package/dist/protocols/acp/types.js.map +1 -1
- package/dist/server/contract.d.ts.map +1 -1
- package/dist/server/contract.js +2 -0
- package/dist/server/contract.js.map +1 -1
- package/dist/server/handlers/acp.d.ts +3 -1
- package/dist/server/handlers/acp.d.ts.map +1 -1
- package/dist/server/handlers/acp.js +4 -1
- package/dist/server/handlers/acp.js.map +1 -1
- package/dist/server/handlers/agent-runtimes.d.ts.map +1 -1
- package/dist/server/handlers/agent-runtimes.js +7 -2
- package/dist/server/handlers/agent-runtimes.js.map +1 -1
- package/dist/server/handlers/agent-turn.d.ts +5 -0
- package/dist/server/handlers/agent-turn.d.ts.map +1 -1
- package/dist/server/handlers/agent-turn.js +47 -0
- package/dist/server/handlers/agent-turn.js.map +1 -1
- package/dist/server/handlers/runtime-adapter-projections.d.ts +90 -0
- package/dist/server/handlers/runtime-adapter-projections.d.ts.map +1 -0
- package/dist/server/handlers/runtime-adapter-projections.js +329 -0
- package/dist/server/handlers/runtime-adapter-projections.js.map +1 -0
- package/dist/server/handlers/runtime-artifact-projections.d.ts +14 -0
- package/dist/server/handlers/runtime-artifact-projections.d.ts.map +1 -1
- package/dist/server/handlers/runtime-artifact-projections.js +30 -7
- package/dist/server/handlers/runtime-artifact-projections.js.map +1 -1
- package/dist/server/handlers/runtime-readiness.d.ts +7 -4
- package/dist/server/handlers/runtime-readiness.d.ts.map +1 -1
- package/dist/server/handlers/runtime-readiness.js +113 -1
- package/dist/server/handlers/runtime-readiness.js.map +1 -1
- package/dist/server/handlers/runtime-session-projections.d.ts +87 -0
- package/dist/server/handlers/runtime-session-projections.d.ts.map +1 -0
- package/dist/server/handlers/runtime-session-projections.js +290 -0
- package/dist/server/handlers/runtime-session-projections.js.map +1 -0
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +18 -2
- package/dist/server/http.js.map +1 -1
- package/dist/server/index.d.ts +3 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/route-ownership.d.ts.map +1 -1
- package/dist/server/route-ownership.js +2 -0
- package/dist/server/route-ownership.js.map +1 -1
- 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
|
-
|
|
290
|
-
|
|
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:
|
|
425
|
-
rawOutput:
|
|
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:
|
|
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) {
|