claude-code-rust 0.12.0 → 0.12.2
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/README.md +10 -6
- package/agent-sdk/README.md +1 -1
- package/agent-sdk/dist/bridge/account_metadata.js +44 -0
- package/agent-sdk/dist/bridge/available_commands.js +129 -0
- package/agent-sdk/dist/bridge/commands.js +58 -36
- package/agent-sdk/dist/bridge/error_classification.js +20 -7
- package/agent-sdk/dist/bridge/events.js +18 -0
- package/agent-sdk/dist/bridge/history.js +183 -11
- package/agent-sdk/dist/bridge/logger.js +3 -0
- package/agent-sdk/dist/bridge/mcp.js +49 -79
- package/agent-sdk/dist/bridge/mcp_metadata.js +369 -0
- package/agent-sdk/dist/bridge/message_handlers.js +401 -57
- package/agent-sdk/dist/bridge/model_metadata.js +228 -0
- package/agent-sdk/dist/bridge/session_lifecycle.js +197 -326
- package/agent-sdk/dist/bridge/state_parsing.js +7 -1
- package/agent-sdk/dist/bridge/task_links.js +34 -0
- package/agent-sdk/dist/bridge/tasks.js +862 -0
- package/agent-sdk/dist/bridge/tool_calls.js +88 -31
- package/agent-sdk/dist/bridge/tooling.js +1262 -32
- package/agent-sdk/dist/bridge.js +95 -43
- package/agent-sdk/dist/bridge.test.js +3680 -269
- package/package.json +2 -2
|
@@ -2,13 +2,17 @@ import { asRecordOrNull } from "./shared.js";
|
|
|
2
2
|
import { toPermissionMode, buildModeState, refreshSupportedModesForSession } from "./commands.js";
|
|
3
3
|
import { writeEvent, emitSessionUpdate, emitConnectEvent, emitSessionReplacedEvent, } from "./events.js";
|
|
4
4
|
import { TOOL_RESULT_TYPES, isToolSearchToolName, isToolSearchToolResultType, unwrapToolUseResult, } from "./tooling.js";
|
|
5
|
-
import { emitToolCall, emitToolCallUpdate,
|
|
5
|
+
import { emitToolCall, emitToolCallUpdate, emitToolResultUpdate, finalizeOpenToolCalls, emitToolProgressUpdate, emitToolSummaryUpdate, ensureToolCallVisible, resolveTaskToolUseId, defersTaskNotificationCompletion, toolAcceptsTaskLifecycle, taskProgressText, taskUpdatedFields, } from "./tool_calls.js";
|
|
6
|
+
import { applyTaskLifecycleState } from "./tasks.js";
|
|
7
|
+
import { linkTaskToolUse, unlinkTaskToolUse } from "./task_links.js";
|
|
6
8
|
import { emitAuthRequired, classifyTurnErrorKind, emitFastModeUpdateIfChanged } from "./error_classification.js";
|
|
7
9
|
import { mapAvailableAgentsFromNames, emitAvailableAgentsIfChanged, refreshAvailableAgents } from "./agents.js";
|
|
8
|
-
import {
|
|
10
|
+
import { mapInitSlashCommands, mapSdkSlashCommands, updateAvailableCommands, } from "./available_commands.js";
|
|
11
|
+
import { buildApiRetryUpdate, buildRateLimitUpdate, normalizeSettingsParseErrors, numberField, parseApiRetryError, parseRuntimeSessionState, } from "./state_parsing.js";
|
|
9
12
|
import { looksLikeAuthRequired } from "./auth.js";
|
|
10
13
|
import { emitCurrentModelUpdate, refreshCurrentModel, updateSessionId } from "./session_lifecycle.js";
|
|
11
14
|
import { bridgeLogger, LOG_TARGETS } from "./logger.js";
|
|
15
|
+
import { emitMcpSnapshotFromStatuses } from "./mcp.js";
|
|
12
16
|
export function textFromPrompt(command) {
|
|
13
17
|
const chunks = command.chunks ?? [];
|
|
14
18
|
return chunks
|
|
@@ -30,6 +34,68 @@ const SUPPORTED_IMAGE_MIME_TYPES = new Set([
|
|
|
30
34
|
"image/gif",
|
|
31
35
|
"image/webp",
|
|
32
36
|
]);
|
|
37
|
+
function sdkCorrelationMetadata(msg) {
|
|
38
|
+
return {
|
|
39
|
+
requestId: typeof msg.request_id === "string" ? msg.request_id : undefined,
|
|
40
|
+
subagentType: typeof msg.subagent_type === "string" ? msg.subagent_type : undefined,
|
|
41
|
+
taskDescription: typeof msg.task_description === "string" ? msg.task_description : undefined,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function sdkTaskMetadata(msg) {
|
|
45
|
+
const metadata = sdkCorrelationMetadata(msg);
|
|
46
|
+
const taskType = typeof msg.task_type === "string" && msg.task_type.length > 0 ? msg.task_type : undefined;
|
|
47
|
+
const workflowName = typeof msg.workflow_name === "string" && msg.workflow_name.length > 0 ? msg.workflow_name : undefined;
|
|
48
|
+
const prompt = typeof msg.prompt === "string" && msg.prompt.length > 0 ? msg.prompt : undefined;
|
|
49
|
+
const outputFile = typeof msg.output_file === "string" && msg.output_file.length > 0 ? msg.output_file : undefined;
|
|
50
|
+
const status = typeof msg.status === "string" && msg.status.length > 0 ? msg.status : undefined;
|
|
51
|
+
const summary = status && typeof msg.summary === "string" && msg.summary.length > 0 ? msg.summary : undefined;
|
|
52
|
+
const taskMetadata = {
|
|
53
|
+
...(metadata.requestId ? { request_id: metadata.requestId } : {}),
|
|
54
|
+
...(metadata.subagentType ? { subagent_type: metadata.subagentType } : {}),
|
|
55
|
+
...(metadata.taskDescription ? { task_description: metadata.taskDescription } : {}),
|
|
56
|
+
...(taskType ? { task_type: taskType } : {}),
|
|
57
|
+
...(workflowName ? { workflow_name: workflowName } : {}),
|
|
58
|
+
...(prompt ? { prompt } : {}),
|
|
59
|
+
...(outputFile ? { output_file: outputFile } : {}),
|
|
60
|
+
...(summary ? { summary } : {}),
|
|
61
|
+
...(status ? { terminal_status: status } : {}),
|
|
62
|
+
};
|
|
63
|
+
return Object.keys(taskMetadata).length > 0 ? taskMetadata : undefined;
|
|
64
|
+
}
|
|
65
|
+
function sdkMessageOriginKind(msg) {
|
|
66
|
+
const origin = msg.origin && typeof msg.origin === "object" ? msg.origin : null;
|
|
67
|
+
return typeof origin?.kind === "string" ? origin.kind : undefined;
|
|
68
|
+
}
|
|
69
|
+
function logSdkMessageOrigin(session, msg) {
|
|
70
|
+
const originKind = sdkMessageOriginKind(msg);
|
|
71
|
+
if (!originKind) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
bridgeLogger.debug({
|
|
75
|
+
target: LOG_TARGETS.APP_SESSION,
|
|
76
|
+
eventName: "sdk_message_origin_observed",
|
|
77
|
+
message: "SDK message origin observed",
|
|
78
|
+
outcome: originKind === "auto-continuation" ? "accepted" : "observed",
|
|
79
|
+
sessionId: session.sessionId,
|
|
80
|
+
fields: {
|
|
81
|
+
message_type: typeof msg.type === "string" ? msg.type : undefined,
|
|
82
|
+
origin_kind: originKind,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function emitSystemNoticeUpdate(session, severity, message) {
|
|
87
|
+
const trimmed = message.trim();
|
|
88
|
+
if (!trimmed) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
emitSessionUpdate(session.sessionId, { type: "system_notice_update", severity, message: trimmed });
|
|
92
|
+
}
|
|
93
|
+
function notificationSeverity(priority) {
|
|
94
|
+
return priority === "high" || priority === "immediate" ? "warning" : "info";
|
|
95
|
+
}
|
|
96
|
+
function isTerminalToolStatus(status) {
|
|
97
|
+
return status === "completed" || status === "failed" || status === "killed";
|
|
98
|
+
}
|
|
33
99
|
/** Fast check that a string looks like valid base64 (non-empty, correct charset & padding). */
|
|
34
100
|
function isValidBase64(data) {
|
|
35
101
|
if (!data)
|
|
@@ -98,12 +164,13 @@ export function handleTaskSystemMessage(session, subtype, msg) {
|
|
|
98
164
|
subtype !== "task_progress" &&
|
|
99
165
|
subtype !== "task_updated" &&
|
|
100
166
|
subtype !== "task_notification") {
|
|
101
|
-
return;
|
|
167
|
+
return false;
|
|
102
168
|
}
|
|
103
169
|
const taskId = typeof msg.task_id === "string" ? msg.task_id : "";
|
|
104
170
|
const explicitToolUseId = typeof msg.tool_use_id === "string" ? msg.tool_use_id : "";
|
|
171
|
+
const messageTaskMetadata = sdkTaskMetadata(msg);
|
|
105
172
|
if (taskId && explicitToolUseId) {
|
|
106
|
-
session
|
|
173
|
+
linkTaskToolUse(session, taskId, explicitToolUseId);
|
|
107
174
|
}
|
|
108
175
|
const toolUseId = resolveTaskToolUseId(session, msg);
|
|
109
176
|
bridgeLogger.debug({
|
|
@@ -142,6 +209,7 @@ export function handleTaskSystemMessage(session, subtype, msg) {
|
|
|
142
209
|
});
|
|
143
210
|
}
|
|
144
211
|
if (!toolUseId) {
|
|
212
|
+
applyTaskLifecycleState(session, subtype, msg);
|
|
145
213
|
if (subtype === "task_updated" && taskId) {
|
|
146
214
|
bridgeLogger.debug({
|
|
147
215
|
target: LOG_TARGETS.APP_TOOL,
|
|
@@ -152,46 +220,54 @@ export function handleTaskSystemMessage(session, subtype, msg) {
|
|
|
152
220
|
fields: { task_id: taskId, subtype },
|
|
153
221
|
});
|
|
154
222
|
}
|
|
155
|
-
return;
|
|
223
|
+
return true;
|
|
156
224
|
}
|
|
157
225
|
const toolCall = ensureToolCallVisible(session, toolUseId, "Agent", {});
|
|
158
226
|
if (!toolAcceptsTaskLifecycle(toolCall)) {
|
|
159
227
|
if (taskId) {
|
|
160
|
-
session
|
|
228
|
+
unlinkTaskToolUse(session, taskId);
|
|
161
229
|
}
|
|
162
|
-
return;
|
|
230
|
+
return true;
|
|
163
231
|
}
|
|
232
|
+
applyTaskLifecycleState(session, subtype, msg);
|
|
164
233
|
if (toolCall.status === "pending") {
|
|
165
234
|
emitToolCallUpdate(session, toolUseId, { status: "in_progress" }, "progress");
|
|
166
235
|
}
|
|
167
236
|
if (subtype === "task_started") {
|
|
168
237
|
const description = typeof msg.description === "string" ? msg.description : "";
|
|
169
238
|
if (!description) {
|
|
170
|
-
return;
|
|
239
|
+
return true;
|
|
171
240
|
}
|
|
172
|
-
|
|
241
|
+
const fields = {
|
|
173
242
|
status: "in_progress",
|
|
174
243
|
raw_output: description,
|
|
175
244
|
content: [{ type: "content", content: { type: "text", text: description } }],
|
|
176
|
-
|
|
177
|
-
|
|
245
|
+
...(messageTaskMetadata ? { task_metadata: messageTaskMetadata } : {}),
|
|
246
|
+
};
|
|
247
|
+
emitToolCallUpdate(session, toolUseId, fields, "task_started");
|
|
248
|
+
return true;
|
|
178
249
|
}
|
|
179
250
|
if (subtype === "task_progress") {
|
|
180
251
|
const progress = taskProgressText(msg);
|
|
181
252
|
if (!progress) {
|
|
182
|
-
return;
|
|
253
|
+
return true;
|
|
183
254
|
}
|
|
184
|
-
|
|
255
|
+
const fields = {
|
|
185
256
|
status: "in_progress",
|
|
186
257
|
raw_output: progress,
|
|
187
258
|
content: [{ type: "content", content: { type: "text", text: progress } }],
|
|
188
|
-
|
|
189
|
-
|
|
259
|
+
...(messageTaskMetadata ? { task_metadata: messageTaskMetadata } : {}),
|
|
260
|
+
};
|
|
261
|
+
emitToolCallUpdate(session, toolUseId, fields, "task_progress");
|
|
262
|
+
return true;
|
|
190
263
|
}
|
|
191
264
|
if (subtype === "task_updated") {
|
|
192
265
|
const fields = taskUpdatedFields(msg);
|
|
266
|
+
if (messageTaskMetadata) {
|
|
267
|
+
fields.task_metadata = { ...(fields.task_metadata ?? {}), ...messageTaskMetadata };
|
|
268
|
+
}
|
|
193
269
|
if (Object.keys(fields).length === 0) {
|
|
194
|
-
return;
|
|
270
|
+
return true;
|
|
195
271
|
}
|
|
196
272
|
bridgeLogger.debug({
|
|
197
273
|
target: LOG_TARGETS.APP_TOOL,
|
|
@@ -209,20 +285,117 @@ export function handleTaskSystemMessage(session, subtype, msg) {
|
|
|
209
285
|
},
|
|
210
286
|
});
|
|
211
287
|
emitToolCallUpdate(session, toolUseId, fields, "task_updated");
|
|
212
|
-
|
|
288
|
+
if (taskId && isTerminalToolStatus(fields.status)) {
|
|
289
|
+
unlinkTaskToolUse(session, taskId);
|
|
290
|
+
}
|
|
291
|
+
return true;
|
|
213
292
|
}
|
|
214
293
|
const status = typeof msg.status === "string" ? msg.status : "";
|
|
215
294
|
const summary = typeof msg.summary === "string" ? msg.summary : "";
|
|
216
295
|
const finalStatus = status === "completed" ? "completed" : status === "stopped" ? "killed" : "failed";
|
|
217
|
-
const
|
|
296
|
+
const deferCompletion = finalStatus === "completed" && defersTaskNotificationCompletion(toolCall);
|
|
297
|
+
const fields = deferCompletion ? {} : { status: finalStatus };
|
|
298
|
+
if (messageTaskMetadata) {
|
|
299
|
+
fields.task_metadata = messageTaskMetadata;
|
|
300
|
+
}
|
|
218
301
|
if (summary) {
|
|
219
302
|
fields.raw_output = summary;
|
|
220
303
|
fields.content = [{ type: "content", content: { type: "text", text: summary } }];
|
|
221
304
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
305
|
+
if (Object.keys(fields).length > 0) {
|
|
306
|
+
emitToolCallUpdate(session, toolUseId, fields, "task_notification");
|
|
307
|
+
}
|
|
308
|
+
if (taskId && !deferCompletion) {
|
|
309
|
+
unlinkTaskToolUse(session, taskId);
|
|
310
|
+
}
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
function stringField(msg, field) {
|
|
314
|
+
const value = msg[field];
|
|
315
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
316
|
+
}
|
|
317
|
+
function nullableStringField(msg, field) {
|
|
318
|
+
const value = msg[field];
|
|
319
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
320
|
+
}
|
|
321
|
+
function sourceMessageUuid(msg) {
|
|
322
|
+
return stringField(msg, "uuid");
|
|
323
|
+
}
|
|
324
|
+
function dedupeMessageUuids(value) {
|
|
325
|
+
if (!Array.isArray(value)) {
|
|
326
|
+
return [];
|
|
327
|
+
}
|
|
328
|
+
const seen = new Set();
|
|
329
|
+
const uuids = [];
|
|
330
|
+
for (const entry of value) {
|
|
331
|
+
if (typeof entry !== "string" || !entry) {
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
if (seen.has(entry)) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
seen.add(entry);
|
|
338
|
+
uuids.push(entry);
|
|
339
|
+
}
|
|
340
|
+
return uuids;
|
|
341
|
+
}
|
|
342
|
+
function emitTranscriptRetraction(session, messageUuids, reason, metadata = {}) {
|
|
343
|
+
const deduped = dedupeMessageUuids(messageUuids);
|
|
344
|
+
if (deduped.length === 0) {
|
|
345
|
+
return false;
|
|
225
346
|
}
|
|
347
|
+
emitSessionUpdate(session.sessionId, {
|
|
348
|
+
type: "transcript_retraction",
|
|
349
|
+
message_uuids: deduped,
|
|
350
|
+
reason,
|
|
351
|
+
...(metadata.requestId ? { request_id: metadata.requestId } : {}),
|
|
352
|
+
...(metadata.trigger ? { trigger: metadata.trigger } : {}),
|
|
353
|
+
...(metadata.direction ? { direction: metadata.direction } : {}),
|
|
354
|
+
...(metadata.originalModel ? { original_model: metadata.originalModel } : {}),
|
|
355
|
+
...(metadata.fallbackModel ? { fallback_model: metadata.fallbackModel } : {}),
|
|
356
|
+
...(metadata.apiRefusalCategory ? { api_refusal_category: metadata.apiRefusalCategory } : {}),
|
|
357
|
+
...(metadata.apiRefusalExplanation ? { api_refusal_explanation: metadata.apiRefusalExplanation } : {}),
|
|
358
|
+
...(metadata.content ? { content: metadata.content } : {}),
|
|
359
|
+
});
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
function handleFallbackRetractionMessage(session, subtype, msg) {
|
|
363
|
+
if (subtype !== "model_refusal_fallback" && subtype !== "model_fallback") {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
const reason = subtype === "model_fallback" ? "model_fallback" : "model_refusal_fallback";
|
|
367
|
+
const messageUuids = dedupeMessageUuids(msg.retracted_message_uuids);
|
|
368
|
+
const metadata = {
|
|
369
|
+
requestId: nullableStringField(msg, "request_id"),
|
|
370
|
+
trigger: stringField(msg, "trigger"),
|
|
371
|
+
direction: stringField(msg, "direction"),
|
|
372
|
+
originalModel: stringField(msg, "original_model"),
|
|
373
|
+
fallbackModel: stringField(msg, "fallback_model"),
|
|
374
|
+
apiRefusalCategory: nullableStringField(msg, "api_refusal_category"),
|
|
375
|
+
apiRefusalExplanation: nullableStringField(msg, "api_refusal_explanation"),
|
|
376
|
+
content: stringField(msg, "content"),
|
|
377
|
+
};
|
|
378
|
+
const emitted = emitTranscriptRetraction(session, messageUuids, reason, metadata);
|
|
379
|
+
bridgeLogger.info({
|
|
380
|
+
target: LOG_TARGETS.APP_SESSION,
|
|
381
|
+
eventName: "sdk_model_fallback_received",
|
|
382
|
+
message: "SDK model fallback retraction received",
|
|
383
|
+
outcome: emitted ? "success" : "observed",
|
|
384
|
+
sessionId: session.sessionId,
|
|
385
|
+
requestId: metadata.requestId,
|
|
386
|
+
count: messageUuids.length,
|
|
387
|
+
fields: {
|
|
388
|
+
sdk_subtype: subtype,
|
|
389
|
+
trigger: metadata.trigger,
|
|
390
|
+
direction: metadata.direction,
|
|
391
|
+
original_model: metadata.originalModel,
|
|
392
|
+
fallback_model: metadata.fallbackModel,
|
|
393
|
+
api_refusal_category: metadata.apiRefusalCategory,
|
|
394
|
+
has_api_refusal_explanation: metadata.apiRefusalExplanation !== undefined,
|
|
395
|
+
has_content: metadata.content !== undefined,
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
return true;
|
|
226
399
|
}
|
|
227
400
|
function logContentBlockLinkage(session, blockType, toolUseId, toolName, linkage) {
|
|
228
401
|
if (!toolUseId && !linkage?.parentToolUseId) {
|
|
@@ -274,14 +447,22 @@ export function handleContentBlock(session, block, linkage) {
|
|
|
274
447
|
if (blockType === "text") {
|
|
275
448
|
const text = typeof block.text === "string" ? block.text : "";
|
|
276
449
|
if (text) {
|
|
277
|
-
emitSessionUpdate(session.sessionId, {
|
|
450
|
+
emitSessionUpdate(session.sessionId, {
|
|
451
|
+
type: "agent_message_chunk",
|
|
452
|
+
content: { type: "text", text },
|
|
453
|
+
...(linkage?.sourceMessageUuid ? { source_message_uuid: linkage.sourceMessageUuid } : {}),
|
|
454
|
+
});
|
|
278
455
|
}
|
|
279
456
|
return;
|
|
280
457
|
}
|
|
281
458
|
if (blockType === "thinking") {
|
|
282
459
|
const text = typeof block.thinking === "string" ? block.thinking : "";
|
|
283
460
|
if (text) {
|
|
284
|
-
emitSessionUpdate(session.sessionId, {
|
|
461
|
+
emitSessionUpdate(session.sessionId, {
|
|
462
|
+
type: "agent_thought_chunk",
|
|
463
|
+
content: { type: "text", text },
|
|
464
|
+
...(linkage?.sourceMessageUuid ? { source_message_uuid: linkage.sourceMessageUuid } : {}),
|
|
465
|
+
});
|
|
285
466
|
}
|
|
286
467
|
return;
|
|
287
468
|
}
|
|
@@ -296,8 +477,7 @@ export function handleContentBlock(session, block, linkage) {
|
|
|
296
477
|
return;
|
|
297
478
|
}
|
|
298
479
|
logContentBlockLinkage(session, blockType, toolUseId, name, linkage);
|
|
299
|
-
|
|
300
|
-
emitToolCall(session, toolUseId, name, input, linkage?.parentToolUseId ?? null);
|
|
480
|
+
emitToolCall(session, toolUseId, name, input, linkage?.parentToolUseId ?? null, linkage?.metadata, linkage?.sourceMessageUuid);
|
|
301
481
|
return;
|
|
302
482
|
}
|
|
303
483
|
if (TOOL_RESULT_TYPES.has(blockType)) {
|
|
@@ -310,16 +490,17 @@ export function handleContentBlock(session, block, linkage) {
|
|
|
310
490
|
}
|
|
311
491
|
logContentBlockLinkage(session, blockType, toolUseId, undefined, linkage);
|
|
312
492
|
const isError = Boolean(block.is_error);
|
|
313
|
-
emitToolResultUpdate(session, toolUseId, isError, block.content, block);
|
|
493
|
+
emitToolResultUpdate(session, toolUseId, isError, block.content, block, linkage?.sourceMessageUuid);
|
|
314
494
|
}
|
|
315
495
|
}
|
|
316
|
-
export function handleStreamEvent(session, event, parentToolUseId) {
|
|
496
|
+
export function handleStreamEvent(session, event, parentToolUseId, sourceMessageUuid) {
|
|
317
497
|
const eventType = typeof event.type === "string" ? event.type : "";
|
|
318
498
|
if (eventType === "content_block_start") {
|
|
319
499
|
if (event.content_block && typeof event.content_block === "object") {
|
|
320
500
|
handleContentBlock(session, event.content_block, {
|
|
321
501
|
source: "stream_event",
|
|
322
502
|
parentToolUseId,
|
|
503
|
+
sourceMessageUuid,
|
|
323
504
|
});
|
|
324
505
|
}
|
|
325
506
|
return;
|
|
@@ -333,22 +514,33 @@ export function handleStreamEvent(session, event, parentToolUseId) {
|
|
|
333
514
|
if (deltaType === "text_delta") {
|
|
334
515
|
const text = typeof delta.text === "string" ? delta.text : "";
|
|
335
516
|
if (text) {
|
|
336
|
-
emitSessionUpdate(session.sessionId, {
|
|
517
|
+
emitSessionUpdate(session.sessionId, {
|
|
518
|
+
type: "agent_message_chunk",
|
|
519
|
+
content: { type: "text", text },
|
|
520
|
+
...(sourceMessageUuid ? { source_message_uuid: sourceMessageUuid } : {}),
|
|
521
|
+
});
|
|
337
522
|
}
|
|
338
523
|
}
|
|
339
524
|
else if (deltaType === "thinking_delta") {
|
|
340
525
|
const text = typeof delta.thinking === "string" ? delta.thinking : "";
|
|
341
526
|
if (text) {
|
|
342
|
-
emitSessionUpdate(session.sessionId, {
|
|
527
|
+
emitSessionUpdate(session.sessionId, {
|
|
528
|
+
type: "agent_thought_chunk",
|
|
529
|
+
content: { type: "text", text },
|
|
530
|
+
...(sourceMessageUuid ? { source_message_uuid: sourceMessageUuid } : {}),
|
|
531
|
+
});
|
|
343
532
|
}
|
|
344
533
|
}
|
|
345
534
|
}
|
|
346
535
|
}
|
|
347
536
|
export function handleAssistantMessage(session, message) {
|
|
537
|
+
const assistantMessageUuid = sourceMessageUuid(message);
|
|
538
|
+
emitTranscriptRetraction(session, dedupeMessageUuids(message.supersedes), "assistant_supersedes");
|
|
348
539
|
const assistantError = typeof message.error === "string" ? message.error : "";
|
|
349
540
|
if (assistantError.length > 0) {
|
|
350
|
-
session.lastAssistantError = assistantError;
|
|
541
|
+
session.lastAssistantError = parseApiRetryError(assistantError);
|
|
351
542
|
}
|
|
543
|
+
const metadata = sdkCorrelationMetadata(message);
|
|
352
544
|
const messageObject = message.message && typeof message.message === "object"
|
|
353
545
|
? message.message
|
|
354
546
|
: null;
|
|
@@ -367,18 +559,33 @@ export function handleAssistantMessage(session, message) {
|
|
|
367
559
|
blockType === "mcp_tool_use" ||
|
|
368
560
|
TOOL_RESULT_TYPES.has(blockType)) {
|
|
369
561
|
const parentToolUseId = typeof message.parent_tool_use_id === "string" ? message.parent_tool_use_id : undefined;
|
|
370
|
-
handleContentBlock(session, blockRecord, {
|
|
562
|
+
handleContentBlock(session, blockRecord, {
|
|
563
|
+
source: "assistant",
|
|
564
|
+
parentToolUseId,
|
|
565
|
+
metadata,
|
|
566
|
+
sourceMessageUuid: assistantMessageUuid,
|
|
567
|
+
});
|
|
371
568
|
}
|
|
372
569
|
}
|
|
373
570
|
}
|
|
571
|
+
function messageToolUseResult(message) {
|
|
572
|
+
if (Object.hasOwn(message, "toolUseResult")) {
|
|
573
|
+
return message.toolUseResult;
|
|
574
|
+
}
|
|
575
|
+
if (Object.hasOwn(message, "tool_use_result")) {
|
|
576
|
+
return message.tool_use_result;
|
|
577
|
+
}
|
|
578
|
+
return undefined;
|
|
579
|
+
}
|
|
374
580
|
export function handleUserToolResultBlocks(session, message) {
|
|
375
581
|
const messageObject = message.message && typeof message.message === "object"
|
|
376
582
|
? message.message
|
|
377
583
|
: null;
|
|
378
584
|
if (!messageObject) {
|
|
379
|
-
return;
|
|
585
|
+
return false;
|
|
380
586
|
}
|
|
381
587
|
const content = Array.isArray(messageObject.content) ? messageObject.content : [];
|
|
588
|
+
let handled = false;
|
|
382
589
|
for (const block of content) {
|
|
383
590
|
if (!block || typeof block !== "object") {
|
|
384
591
|
continue;
|
|
@@ -387,9 +594,23 @@ export function handleUserToolResultBlocks(session, message) {
|
|
|
387
594
|
const blockType = typeof blockRecord.type === "string" ? blockRecord.type : "";
|
|
388
595
|
if (TOOL_RESULT_TYPES.has(blockType)) {
|
|
389
596
|
const parentToolUseId = typeof message.parent_tool_use_id === "string" ? message.parent_tool_use_id : undefined;
|
|
390
|
-
|
|
597
|
+
const toolUseId = typeof blockRecord.tool_use_id === "string" ? blockRecord.tool_use_id : "";
|
|
598
|
+
if (!toolUseId) {
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
handled = true;
|
|
602
|
+
if (isHiddenToolResult(session, toolUseId, blockType)) {
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
logContentBlockLinkage(session, blockType, toolUseId, undefined, {
|
|
606
|
+
source: "user",
|
|
607
|
+
parentToolUseId,
|
|
608
|
+
sourceMessageUuid: sourceMessageUuid(message),
|
|
609
|
+
});
|
|
610
|
+
emitToolResultUpdate(session, toolUseId, Boolean(blockRecord.is_error), blockRecord.content, messageToolUseResult(message) ?? blockRecord, sourceMessageUuid(message));
|
|
391
611
|
}
|
|
392
612
|
}
|
|
613
|
+
return handled;
|
|
393
614
|
}
|
|
394
615
|
export function handleResultMessage(session, message) {
|
|
395
616
|
emitFastModeUpdateIfChanged(session, message.fast_mode_state);
|
|
@@ -419,6 +640,7 @@ export function handleResultMessage(session, message) {
|
|
|
419
640
|
finalizeOpenToolCalls(session, "failed");
|
|
420
641
|
const errorKind = classifyTurnErrorKind(subtype, errors, assistantError);
|
|
421
642
|
const fallback = subtype ? `turn failed: ${subtype}` : "turn failed";
|
|
643
|
+
const apiErrorStatus = numberField(message, "api_error_status", "apiErrorStatus");
|
|
422
644
|
writeEvent({
|
|
423
645
|
event: "turn_error",
|
|
424
646
|
session_id: session.sessionId,
|
|
@@ -426,6 +648,7 @@ export function handleResultMessage(session, message) {
|
|
|
426
648
|
error_kind: errorKind,
|
|
427
649
|
...(subtype ? { sdk_result_subtype: subtype } : {}),
|
|
428
650
|
...(assistantError ? { assistant_error: assistantError } : {}),
|
|
651
|
+
...(apiErrorStatus !== undefined ? { api_error_status: apiErrorStatus } : {}),
|
|
429
652
|
...(terminalReason ? { terminal_reason: terminalReason } : {}),
|
|
430
653
|
});
|
|
431
654
|
session.lastAssistantError = undefined;
|
|
@@ -452,8 +675,96 @@ function terminalReasonFromValue(value) {
|
|
|
452
675
|
export function handleSdkMessage(session, message) {
|
|
453
676
|
const msg = message;
|
|
454
677
|
const type = typeof msg.type === "string" ? msg.type : "";
|
|
678
|
+
logSdkMessageOrigin(session, msg);
|
|
455
679
|
if (type === "system") {
|
|
456
680
|
const subtype = typeof msg.subtype === "string" ? msg.subtype : "";
|
|
681
|
+
if (handleFallbackRetractionMessage(session, subtype, msg)) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
if (subtype === "commands_changed") {
|
|
685
|
+
updateAvailableCommands(session, "commands_changed", mapSdkSlashCommands(msg.commands));
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
if (subtype === "notification") {
|
|
689
|
+
const text = typeof msg.text === "string" ? msg.text : "";
|
|
690
|
+
emitSystemNoticeUpdate(session, notificationSeverity(msg.priority), text);
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
if (subtype === "mirror_error") {
|
|
694
|
+
const error = typeof msg.error === "string" ? msg.error : "";
|
|
695
|
+
const key = asRecordOrNull(msg.key);
|
|
696
|
+
bridgeLogger.warn({
|
|
697
|
+
target: LOG_TARGETS.APP_SESSION,
|
|
698
|
+
eventName: "sdk_mirror_error_received",
|
|
699
|
+
message: "SDK transcript mirror error received",
|
|
700
|
+
outcome: "failure",
|
|
701
|
+
sessionId: session.sessionId,
|
|
702
|
+
fields: {
|
|
703
|
+
error_message: error || undefined,
|
|
704
|
+
project_key: typeof key?.projectKey === "string" ? key.projectKey : undefined,
|
|
705
|
+
mirror_session_id: typeof key?.sessionId === "string" ? key.sessionId : undefined,
|
|
706
|
+
subpath: typeof key?.subpath === "string" ? key.subpath : undefined,
|
|
707
|
+
},
|
|
708
|
+
});
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
if (subtype === "plugin_install") {
|
|
712
|
+
const status = typeof msg.status === "string" ? msg.status : "";
|
|
713
|
+
const name = typeof msg.name === "string" ? msg.name : "";
|
|
714
|
+
const error = typeof msg.error === "string" ? msg.error : "";
|
|
715
|
+
bridgeLogger.info({
|
|
716
|
+
target: LOG_TARGETS.BRIDGE_SDK,
|
|
717
|
+
eventName: "sdk_plugin_install_received",
|
|
718
|
+
message: "SDK plugin install event received",
|
|
719
|
+
outcome: status === "failed" ? "failure" : status || "observed",
|
|
720
|
+
sessionId: session.sessionId,
|
|
721
|
+
fields: {
|
|
722
|
+
plugin_status: status || undefined,
|
|
723
|
+
plugin_name: name || undefined,
|
|
724
|
+
error_message: error || undefined,
|
|
725
|
+
},
|
|
726
|
+
});
|
|
727
|
+
if (status === "failed") {
|
|
728
|
+
const subject = name ? ` ${name}` : "";
|
|
729
|
+
const suffix = error ? `: ${error}` : ".";
|
|
730
|
+
emitSystemNoticeUpdate(session, "warning", `Plugin install failed${subject}${suffix}`);
|
|
731
|
+
}
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
if (subtype === "permission_denied") {
|
|
735
|
+
bridgeLogger.info({
|
|
736
|
+
target: LOG_TARGETS.BRIDGE_PERMISSION,
|
|
737
|
+
eventName: "sdk_permission_denied_received",
|
|
738
|
+
message: "SDK permission denied event received",
|
|
739
|
+
outcome: "denied",
|
|
740
|
+
sessionId: session.sessionId,
|
|
741
|
+
toolCallId: typeof msg.tool_use_id === "string" ? msg.tool_use_id : undefined,
|
|
742
|
+
fields: {
|
|
743
|
+
tool_name: typeof msg.tool_name === "string" ? msg.tool_name : undefined,
|
|
744
|
+
agent_id: typeof msg.agent_id === "string" ? msg.agent_id : undefined,
|
|
745
|
+
decision_reason_type: typeof msg.decision_reason_type === "string" ? msg.decision_reason_type : undefined,
|
|
746
|
+
decision_reason: typeof msg.decision_reason === "string" ? msg.decision_reason : undefined,
|
|
747
|
+
denial_message: typeof msg.message === "string" ? msg.message : undefined,
|
|
748
|
+
},
|
|
749
|
+
});
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
if (subtype === "memory_recall" || subtype === "thinking_tokens") {
|
|
753
|
+
bridgeLogger.debug({
|
|
754
|
+
target: LOG_TARGETS.BRIDGE_SDK,
|
|
755
|
+
eventName: "sdk_system_message_log_only",
|
|
756
|
+
message: "SDK system message handled with log-only policy",
|
|
757
|
+
outcome: "ignored",
|
|
758
|
+
sessionId: session.sessionId,
|
|
759
|
+
fields: {
|
|
760
|
+
sdk_subtype: subtype,
|
|
761
|
+
memory_count: Array.isArray(msg.memories) ? msg.memories.length : undefined,
|
|
762
|
+
estimated_tokens: typeof msg.estimated_tokens === "number" ? msg.estimated_tokens : undefined,
|
|
763
|
+
estimated_tokens_delta: typeof msg.estimated_tokens_delta === "number" ? msg.estimated_tokens_delta : undefined,
|
|
764
|
+
},
|
|
765
|
+
});
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
457
768
|
if (subtype === "api_retry") {
|
|
458
769
|
const update = buildApiRetryUpdate(msg);
|
|
459
770
|
if (update) {
|
|
@@ -502,12 +813,10 @@ export function handleSdkMessage(session, message) {
|
|
|
502
813
|
}
|
|
503
814
|
}
|
|
504
815
|
if (Array.isArray(msg.slash_commands)) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
emitSessionUpdate(session.sessionId, { type: "available_commands_update", commands });
|
|
510
|
-
}
|
|
816
|
+
updateAvailableCommands(session, "init_slash_commands", mapInitSlashCommands(msg.slash_commands));
|
|
817
|
+
}
|
|
818
|
+
if (Array.isArray(msg.mcp_servers)) {
|
|
819
|
+
emitMcpSnapshotFromStatuses(session, msg.mcp_servers, "init");
|
|
511
820
|
}
|
|
512
821
|
if (session.lastAvailableAgentsSignature === undefined && Array.isArray(msg.agents)) {
|
|
513
822
|
emitAvailableAgentsIfChanged(session, mapAvailableAgentsFromNames(msg.agents));
|
|
@@ -515,12 +824,8 @@ export function handleSdkMessage(session, message) {
|
|
|
515
824
|
void session.query
|
|
516
825
|
.supportedCommands()
|
|
517
826
|
.then((commands) => {
|
|
518
|
-
const mapped = commands
|
|
519
|
-
|
|
520
|
-
description: command.description ?? "",
|
|
521
|
-
input_hint: command.argumentHint ?? undefined,
|
|
522
|
-
}));
|
|
523
|
-
emitSessionUpdate(session.sessionId, { type: "available_commands_update", commands: mapped });
|
|
827
|
+
const mapped = mapSdkSlashCommands(commands);
|
|
828
|
+
updateAvailableCommands(session, "supportedCommands", mapped);
|
|
524
829
|
})
|
|
525
830
|
.catch(() => {
|
|
526
831
|
// Best-effort only; slash commands from init were already emitted.
|
|
@@ -544,6 +849,9 @@ export function handleSdkMessage(session, message) {
|
|
|
544
849
|
if (msg.status === "compacting") {
|
|
545
850
|
emitSessionUpdate(session.sessionId, { type: "session_status_update", status: "compacting" });
|
|
546
851
|
}
|
|
852
|
+
else if (msg.status === "requesting") {
|
|
853
|
+
emitSessionUpdate(session.sessionId, { type: "session_status_update", status: "requesting" });
|
|
854
|
+
}
|
|
547
855
|
else if (msg.status === null) {
|
|
548
856
|
emitSessionUpdate(session.sessionId, { type: "session_status_update", status: "idle" });
|
|
549
857
|
}
|
|
@@ -572,6 +880,7 @@ export function handleSdkMessage(session, message) {
|
|
|
572
880
|
emitSessionUpdate(session.sessionId, {
|
|
573
881
|
type: "agent_message_chunk",
|
|
574
882
|
content: { type: "text", text: content },
|
|
883
|
+
...(sourceMessageUuid(msg) ? { source_message_uuid: sourceMessageUuid(msg) } : {}),
|
|
575
884
|
});
|
|
576
885
|
}
|
|
577
886
|
return;
|
|
@@ -591,7 +900,19 @@ export function handleSdkMessage(session, message) {
|
|
|
591
900
|
});
|
|
592
901
|
return;
|
|
593
902
|
}
|
|
594
|
-
handleTaskSystemMessage(session, subtype, msg)
|
|
903
|
+
if (handleTaskSystemMessage(session, subtype, msg)) {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
bridgeLogger.debug({
|
|
907
|
+
target: LOG_TARGETS.BRIDGE_SDK,
|
|
908
|
+
eventName: "sdk_system_message_unhandled",
|
|
909
|
+
message: "SDK system message ignored by explicit fallback policy",
|
|
910
|
+
outcome: "ignored",
|
|
911
|
+
sessionId: session.sessionId,
|
|
912
|
+
fields: {
|
|
913
|
+
sdk_subtype: subtype || undefined,
|
|
914
|
+
},
|
|
915
|
+
});
|
|
595
916
|
return;
|
|
596
917
|
}
|
|
597
918
|
if (type === "prompt_suggestion") {
|
|
@@ -624,14 +945,17 @@ export function handleSdkMessage(session, message) {
|
|
|
624
945
|
if (type === "stream_event") {
|
|
625
946
|
if (msg.event && typeof msg.event === "object") {
|
|
626
947
|
const parentToolUseId = typeof msg.parent_tool_use_id === "string" ? msg.parent_tool_use_id : undefined;
|
|
627
|
-
handleStreamEvent(session, msg.event, parentToolUseId);
|
|
948
|
+
handleStreamEvent(session, msg.event, parentToolUseId, sourceMessageUuid(msg));
|
|
628
949
|
}
|
|
629
950
|
return;
|
|
630
951
|
}
|
|
631
952
|
if (type === "tool_progress") {
|
|
632
953
|
const toolUseId = typeof msg.tool_use_id === "string" ? msg.tool_use_id : "";
|
|
633
954
|
const toolName = typeof msg.tool_name === "string" ? msg.tool_name : "Tool";
|
|
634
|
-
|
|
955
|
+
const taskId = typeof msg.task_id === "string" ? msg.task_id : "";
|
|
956
|
+
const taskToolUseId = taskId ? session.taskToolUseIds.get(taskId) ?? "" : "";
|
|
957
|
+
const resolvedToolUseId = taskToolUseId || toolUseId;
|
|
958
|
+
if (isHiddenToolUse(session, resolvedToolUseId, toolName)) {
|
|
635
959
|
return;
|
|
636
960
|
}
|
|
637
961
|
bridgeLogger.debug({
|
|
@@ -640,16 +964,17 @@ export function handleSdkMessage(session, message) {
|
|
|
640
964
|
message: "SDK tool progress linkage observed",
|
|
641
965
|
outcome: typeof msg.parent_tool_use_id === "string" ? "child" : "root_or_unknown",
|
|
642
966
|
sessionId: session.sessionId,
|
|
643
|
-
toolCallId:
|
|
967
|
+
toolCallId: resolvedToolUseId || undefined,
|
|
644
968
|
fields: {
|
|
645
969
|
tool_name: toolName,
|
|
646
970
|
tool_use_id: toolUseId || undefined,
|
|
647
971
|
parent_tool_use_id: typeof msg.parent_tool_use_id === "string" ? msg.parent_tool_use_id : undefined,
|
|
648
|
-
task_id:
|
|
972
|
+
task_id: taskId || undefined,
|
|
973
|
+
task_resolved_tool_use_id: taskToolUseId || undefined,
|
|
649
974
|
},
|
|
650
975
|
});
|
|
651
|
-
if (
|
|
652
|
-
emitToolProgressUpdate(session,
|
|
976
|
+
if (resolvedToolUseId) {
|
|
977
|
+
emitToolProgressUpdate(session, resolvedToolUseId, toolName);
|
|
653
978
|
}
|
|
654
979
|
return;
|
|
655
980
|
}
|
|
@@ -671,6 +996,23 @@ export function handleSdkMessage(session, message) {
|
|
|
671
996
|
if (type === "rate_limit_event") {
|
|
672
997
|
const rateLimitInfo = asRecordOrNull(msg.rate_limit_info);
|
|
673
998
|
const update = buildRateLimitUpdate(msg.rate_limit_info);
|
|
999
|
+
const rawIsUsingOverage = typeof rateLimitInfo?.isUsingOverage === "boolean" ? rateLimitInfo.isUsingOverage : undefined;
|
|
1000
|
+
const rawOverageInUse = typeof rateLimitInfo?.overageInUse === "boolean" ? rateLimitInfo.overageInUse : undefined;
|
|
1001
|
+
if (rawIsUsingOverage !== undefined &&
|
|
1002
|
+
rawOverageInUse !== undefined &&
|
|
1003
|
+
rawIsUsingOverage !== rawOverageInUse) {
|
|
1004
|
+
bridgeLogger.warn({
|
|
1005
|
+
target: LOG_TARGETS.APP_SESSION,
|
|
1006
|
+
eventName: "sdk_rate_limit_overage_spelling_conflict",
|
|
1007
|
+
message: "SDK rate limit overage booleans conflict",
|
|
1008
|
+
outcome: "using_overageInUse",
|
|
1009
|
+
sessionId: session.sessionId,
|
|
1010
|
+
fields: {
|
|
1011
|
+
raw_is_using_overage: rawIsUsingOverage,
|
|
1012
|
+
raw_overage_in_use: rawOverageInUse,
|
|
1013
|
+
},
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
674
1016
|
bridgeLogger.debug({
|
|
675
1017
|
target: LOG_TARGETS.APP_SESSION,
|
|
676
1018
|
eventName: "sdk_rate_limit_event_received",
|
|
@@ -684,7 +1026,8 @@ export function handleSdkMessage(session, message) {
|
|
|
684
1026
|
raw_resets_at: numberField(rateLimitInfo ?? {}, "resetsAt"),
|
|
685
1027
|
raw_overage_status: typeof rateLimitInfo?.overageStatus === "string" ? rateLimitInfo.overageStatus : undefined,
|
|
686
1028
|
raw_overage_resets_at: numberField(rateLimitInfo ?? {}, "overageResetsAt"),
|
|
687
|
-
raw_is_using_overage:
|
|
1029
|
+
raw_is_using_overage: rawIsUsingOverage,
|
|
1030
|
+
raw_overage_in_use: rawOverageInUse,
|
|
688
1031
|
raw_surpassed_threshold: numberField(rateLimitInfo ?? {}, "surpassedThreshold"),
|
|
689
1032
|
parsed_status: update?.status,
|
|
690
1033
|
parsed_rate_limit_type: update?.rate_limit_type,
|
|
@@ -712,14 +1055,15 @@ export function handleSdkMessage(session, message) {
|
|
|
712
1055
|
return;
|
|
713
1056
|
}
|
|
714
1057
|
if (type === "user") {
|
|
715
|
-
handleUserToolResultBlocks(session, msg);
|
|
1058
|
+
const handledBlocks = handleUserToolResultBlocks(session, msg);
|
|
716
1059
|
const toolUseId = typeof msg.parent_tool_use_id === "string" ? msg.parent_tool_use_id : "";
|
|
717
|
-
|
|
1060
|
+
const rawToolUseResult = messageToolUseResult(msg);
|
|
1061
|
+
if (!handledBlocks && toolUseId && rawToolUseResult !== undefined) {
|
|
718
1062
|
if (session.hiddenToolUseIds.has(toolUseId)) {
|
|
719
1063
|
return;
|
|
720
1064
|
}
|
|
721
|
-
const parsed = unwrapToolUseResult(
|
|
722
|
-
emitToolResultUpdate(session, toolUseId, parsed.isError, parsed.content, msg
|
|
1065
|
+
const parsed = unwrapToolUseResult(rawToolUseResult);
|
|
1066
|
+
emitToolResultUpdate(session, toolUseId, parsed.isError, parsed.content, rawToolUseResult, sourceMessageUuid(msg));
|
|
723
1067
|
}
|
|
724
1068
|
return;
|
|
725
1069
|
}
|