claude-code-rust 0.14.2 → 0.14.4

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.
@@ -1,16 +1,16 @@
1
1
  import { asRecordOrNull } from "./shared.js";
2
- import { toPermissionMode, buildModeState, refreshSupportedModesForSession } from "./commands.js";
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, parseToolNonExecutionMetadata, } from "./tooling.js";
5
5
  import { emitToolCall, emitToolCallUpdate, emitToolResultUpdate, finalizeOpenToolCalls, emitToolProgressUpdate, emitToolSummaryUpdate, ensureToolCallVisible, resolveTaskToolUseId, defersTaskNotificationCompletion, toolAcceptsTaskLifecycle, taskProgressText, taskUpdatedFields, } from "./tool_calls.js";
6
- import { applyBackgroundTasksChanged, applyTaskLifecycleState } from "./tasks.js";
6
+ import { applyBackgroundTasksChanged, applyTaskLifecycleState, } from "./tasks.js";
7
7
  import { linkTaskToolUse, unlinkTaskToolUse } from "./task_links.js";
8
8
  import { emitAuthRequired, classifyTurnErrorKind, emitFastModeUpdate, emitFastModeUpdateIfChanged, setFastModeSnapshotIfChanged, } from "./error_classification.js";
9
- import { mapAvailableAgentsFromNames, emitAvailableAgentsIfChanged, refreshAvailableAgents } from "./agents.js";
9
+ import { mapAvailableAgentsFromNames, emitAvailableAgentsIfChanged, refreshAvailableAgents, } from "./agents.js";
10
10
  import { mapInitSlashCommands, mapSdkSlashCommands, updateAvailableCommands, } from "./available_commands.js";
11
- import { buildApiRetryUpdate, buildRateLimitUpdate, buildSubagentRetryUpdate, normalizeSettingsParseErrors, numberField, parseApiRetryError, parseRuntimeSessionState, } from "./state_parsing.js";
11
+ import { buildApiRetryUpdate, buildRateLimitUpdate, buildSubagentRetryUpdate, normalizeSettingsParseErrors, nonNegativeIntegerField, numberField, parseApiRetryError, parseRuntimeSessionState, } from "./state_parsing.js";
12
12
  import { looksLikeAuthRequired } from "./auth.js";
13
- import { emitCurrentModelUpdate, refreshCurrentModel, updateSessionId } from "./session_lifecycle.js";
13
+ import { emitCurrentModelUpdate, refreshCurrentModel, updateSessionId, } from "./session_lifecycle.js";
14
14
  import { bridgeLogger, LOG_TARGETS } from "./logger.js";
15
15
  import { emitMcpSnapshotFromStatuses } from "./mcp.js";
16
16
  export function textFromPrompt(command) {
@@ -38,23 +38,41 @@ function sdkCorrelationMetadata(msg) {
38
38
  return {
39
39
  requestId: typeof msg.request_id === "string" ? msg.request_id : undefined,
40
40
  subagentType: typeof msg.subagent_type === "string" ? msg.subagent_type : undefined,
41
- taskDescription: typeof msg.task_description === "string" ? msg.task_description : undefined,
41
+ taskDescription: typeof msg.task_description === "string"
42
+ ? msg.task_description
43
+ : undefined,
42
44
  parentAgentId: typeof msg.parent_agent_id === "string" ? msg.parent_agent_id : undefined,
43
45
  };
44
46
  }
45
47
  function sdkTaskMetadata(msg) {
46
48
  const metadata = sdkCorrelationMetadata(msg);
47
- const taskType = typeof msg.task_type === "string" && msg.task_type.length > 0 ? msg.task_type : undefined;
48
- const workflowName = typeof msg.workflow_name === "string" && msg.workflow_name.length > 0 ? msg.workflow_name : undefined;
49
- const prompt = typeof msg.prompt === "string" && msg.prompt.length > 0 ? msg.prompt : undefined;
50
- const outputFile = typeof msg.output_file === "string" && msg.output_file.length > 0 ? msg.output_file : undefined;
51
- const status = typeof msg.status === "string" && msg.status.length > 0 ? msg.status : undefined;
52
- const summary = status && typeof msg.summary === "string" && msg.summary.length > 0 ? msg.summary : undefined;
49
+ const taskType = typeof msg.task_type === "string" && msg.task_type.length > 0
50
+ ? msg.task_type
51
+ : undefined;
52
+ const workflowName = typeof msg.workflow_name === "string" && msg.workflow_name.length > 0
53
+ ? msg.workflow_name
54
+ : undefined;
55
+ const prompt = typeof msg.prompt === "string" && msg.prompt.length > 0
56
+ ? msg.prompt
57
+ : undefined;
58
+ const outputFile = typeof msg.output_file === "string" && msg.output_file.length > 0
59
+ ? msg.output_file
60
+ : undefined;
61
+ const status = typeof msg.status === "string" && msg.status.length > 0
62
+ ? msg.status
63
+ : undefined;
64
+ const summary = status && typeof msg.summary === "string" && msg.summary.length > 0
65
+ ? msg.summary
66
+ : undefined;
53
67
  const taskMetadata = {
54
68
  ...(metadata.requestId ? { request_id: metadata.requestId } : {}),
55
69
  ...(metadata.subagentType ? { subagent_type: metadata.subagentType } : {}),
56
- ...(metadata.taskDescription ? { task_description: metadata.taskDescription } : {}),
57
- ...(metadata.parentAgentId ? { parent_agent_id: metadata.parentAgentId } : {}),
70
+ ...(metadata.taskDescription
71
+ ? { task_description: metadata.taskDescription }
72
+ : {}),
73
+ ...(metadata.parentAgentId
74
+ ? { parent_agent_id: metadata.parentAgentId }
75
+ : {}),
58
76
  ...(taskType ? { task_type: taskType } : {}),
59
77
  ...(workflowName ? { workflow_name: workflowName } : {}),
60
78
  ...(prompt ? { prompt } : {}),
@@ -66,10 +84,67 @@ function sdkTaskMetadata(msg) {
66
84
  return Object.keys(taskMetadata).length > 0 ? taskMetadata : undefined;
67
85
  }
68
86
  function sdkMessageOriginKind(msg) {
69
- const origin = msg.origin && typeof msg.origin === "object" ? msg.origin : null;
87
+ const origin = msg.origin && typeof msg.origin === "object"
88
+ ? msg.origin
89
+ : null;
70
90
  return typeof origin?.kind === "string" ? origin.kind : undefined;
71
91
  }
92
+ function externalMessageUpdateFromSdkUser(msg) {
93
+ const origin = asRecordOrNull(msg.origin);
94
+ if (!origin) {
95
+ return undefined;
96
+ }
97
+ const kind = typeof origin?.kind === "string" ? origin.kind : undefined;
98
+ if (kind !== "peer" && kind !== "task-notification") {
99
+ return undefined;
100
+ }
101
+ const payload = asRecordOrNull(msg.message);
102
+ const content = payload?.content;
103
+ const contentText = typeof content === "string"
104
+ ? content
105
+ : Array.isArray(content)
106
+ ? content
107
+ .flatMap((block) => {
108
+ const record = asRecordOrNull(block);
109
+ return record?.type === "text" && typeof record.text === "string"
110
+ ? [record.text]
111
+ : [];
112
+ })
113
+ .join("\n")
114
+ : "";
115
+ const text = typeof origin.body === "string" ? origin.body : contentText;
116
+ if (!text.trim()) {
117
+ return undefined;
118
+ }
119
+ return {
120
+ type: "external_message_update",
121
+ content: text,
122
+ ...(sourceMessageUuid(msg)
123
+ ? { source_message_uuid: sourceMessageUuid(msg) }
124
+ : {}),
125
+ origin: {
126
+ kind,
127
+ ...(typeof origin.subkind === "string"
128
+ ? { subkind: origin.subkind }
129
+ : {}),
130
+ ...(typeof origin.from === "string" ? { from: origin.from } : {}),
131
+ ...(typeof origin.name === "string" ? { name: origin.name } : {}),
132
+ ...(typeof origin.fromSession === "string"
133
+ ? { from_session: origin.fromSession }
134
+ : {}),
135
+ ...(typeof origin.senderTaskId === "string"
136
+ ? { sender_task_id: origin.senderTaskId }
137
+ : {}),
138
+ ...(typeof origin.verifiedPeerPid === "number" &&
139
+ Number.isSafeInteger(origin.verifiedPeerPid) &&
140
+ origin.verifiedPeerPid >= 0
141
+ ? { verified_peer_pid: origin.verifiedPeerPid }
142
+ : {}),
143
+ },
144
+ };
145
+ }
72
146
  function logSdkMessageOrigin(session, msg) {
147
+ const origin = asRecordOrNull(msg.origin);
73
148
  const originKind = sdkMessageOriginKind(msg);
74
149
  if (!originKind) {
75
150
  return;
@@ -83,6 +158,16 @@ function logSdkMessageOrigin(session, msg) {
83
158
  fields: {
84
159
  message_type: typeof msg.type === "string" ? msg.type : undefined,
85
160
  origin_kind: originKind,
161
+ origin_subkind: typeof origin?.subkind === "string" ? origin.subkind : undefined,
162
+ origin_from_session: typeof origin?.fromSession === "string"
163
+ ? origin.fromSession
164
+ : undefined,
165
+ origin_sender_task_id: typeof origin?.senderTaskId === "string"
166
+ ? origin.senderTaskId
167
+ : undefined,
168
+ origin_verified_peer_pid: typeof origin?.verifiedPeerPid === "number"
169
+ ? origin.verifiedPeerPid
170
+ : undefined,
86
171
  },
87
172
  });
88
173
  }
@@ -91,7 +176,11 @@ function emitSystemNoticeUpdate(session, severity, message) {
91
176
  if (!trimmed) {
92
177
  return;
93
178
  }
94
- emitSessionUpdate(session.sessionId, { type: "system_notice_update", severity, message: trimmed });
179
+ emitSessionUpdate(session.sessionId, {
180
+ type: "system_notice_update",
181
+ severity,
182
+ message: trimmed,
183
+ });
95
184
  }
96
185
  const MAX_INFORMATIONAL_DEDUP_KEYS = 256;
97
186
  function shouldEmitInformationalMessage(session, level, content, toolUseId) {
@@ -203,7 +292,9 @@ function handleModelRefusalNoFallbackMessage(session, msg) {
203
292
  }
204
293
  function workerShutdownMessage(reason) {
205
294
  const trimmed = reason.trim();
206
- return trimmed ? `Claude worker is shutting down: ${trimmed}` : "Claude worker is shutting down.";
295
+ return trimmed
296
+ ? `Claude worker is shutting down: ${trimmed}`
297
+ : "Claude worker is shutting down.";
207
298
  }
208
299
  function handleWorkerShuttingDownSystemMessage(session, msg) {
209
300
  const reason = typeof msg.reason === "string" ? msg.reason.trim() : "";
@@ -280,7 +371,9 @@ export function contentFromPrompt(command) {
280
371
  }
281
372
  }
282
373
  else if (chunk.kind === "image") {
283
- const val = chunk.value && typeof chunk.value === "object" ? chunk.value : null;
374
+ const val = chunk.value && typeof chunk.value === "object"
375
+ ? chunk.value
376
+ : null;
284
377
  if (!val)
285
378
  continue;
286
379
  const data = typeof val.data === "string" ? val.data : "";
@@ -400,7 +493,9 @@ export function handleTaskSystemMessage(session, subtype, msg) {
400
493
  const fields = {
401
494
  status: "in_progress",
402
495
  raw_output: description,
403
- content: [{ type: "content", content: { type: "text", text: description } }],
496
+ content: [
497
+ { type: "content", content: { type: "text", text: description } },
498
+ ],
404
499
  ...(messageTaskMetadata ? { task_metadata: messageTaskMetadata } : {}),
405
500
  };
406
501
  emitToolCallUpdate(session, toolUseId, fields, "task_started");
@@ -423,7 +518,10 @@ export function handleTaskSystemMessage(session, subtype, msg) {
423
518
  if (subtype === "task_updated") {
424
519
  const fields = taskUpdatedFields(msg);
425
520
  if (messageTaskMetadata) {
426
- fields.task_metadata = { ...(fields.task_metadata ?? {}), ...messageTaskMetadata };
521
+ fields.task_metadata = {
522
+ ...(fields.task_metadata ?? {}),
523
+ ...messageTaskMetadata,
524
+ };
427
525
  }
428
526
  if (Object.keys(fields).length === 0) {
429
527
  return true;
@@ -451,15 +549,23 @@ export function handleTaskSystemMessage(session, subtype, msg) {
451
549
  }
452
550
  const status = typeof msg.status === "string" ? msg.status : "";
453
551
  const summary = typeof msg.summary === "string" ? msg.summary : "";
454
- const finalStatus = status === "completed" ? "completed" : status === "stopped" ? "killed" : "failed";
552
+ const finalStatus = status === "completed"
553
+ ? "completed"
554
+ : status === "stopped"
555
+ ? "killed"
556
+ : "failed";
455
557
  const deferCompletion = finalStatus === "completed" && defersTaskNotificationCompletion(toolCall);
456
- const fields = deferCompletion ? {} : { status: finalStatus };
558
+ const fields = deferCompletion
559
+ ? {}
560
+ : { status: finalStatus };
457
561
  if (messageTaskMetadata) {
458
562
  fields.task_metadata = messageTaskMetadata;
459
563
  }
460
564
  if (summary) {
461
565
  fields.raw_output = summary;
462
- fields.content = [{ type: "content", content: { type: "text", text: summary } }];
566
+ fields.content = [
567
+ { type: "content", content: { type: "text", text: summary } },
568
+ ];
463
569
  }
464
570
  if (Object.keys(fields).length > 0) {
465
571
  emitToolCallUpdate(session, toolUseId, fields, "task_notification");
@@ -510,10 +616,19 @@ function emitTranscriptRetraction(session, messageUuids, reason, metadata = {})
510
616
  ...(metadata.requestId ? { request_id: metadata.requestId } : {}),
511
617
  ...(metadata.trigger ? { trigger: metadata.trigger } : {}),
512
618
  ...(metadata.direction ? { direction: metadata.direction } : {}),
513
- ...(metadata.originalModel ? { original_model: metadata.originalModel } : {}),
514
- ...(metadata.fallbackModel ? { fallback_model: metadata.fallbackModel } : {}),
515
- ...(metadata.apiRefusalCategory ? { api_refusal_category: metadata.apiRefusalCategory } : {}),
516
- ...(metadata.apiRefusalExplanation ? { api_refusal_explanation: metadata.apiRefusalExplanation } : {}),
619
+ ...(metadata.originalModel
620
+ ? { original_model: metadata.originalModel }
621
+ : {}),
622
+ ...(metadata.fallbackModel
623
+ ? { fallback_model: metadata.fallbackModel }
624
+ : {}),
625
+ ...(metadata.scope ? { scope: metadata.scope } : {}),
626
+ ...(metadata.apiRefusalCategory
627
+ ? { api_refusal_category: metadata.apiRefusalCategory }
628
+ : {}),
629
+ ...(metadata.apiRefusalExplanation
630
+ ? { api_refusal_explanation: metadata.apiRefusalExplanation }
631
+ : {}),
517
632
  ...(metadata.content ? { content: metadata.content } : {}),
518
633
  });
519
634
  return true;
@@ -530,6 +645,7 @@ function handleFallbackRetractionMessage(session, subtype, msg) {
530
645
  direction: stringField(msg, "direction"),
531
646
  originalModel: stringField(msg, "original_model"),
532
647
  fallbackModel: stringField(msg, "fallback_model"),
648
+ scope: stringField(msg, "scope") ?? "session",
533
649
  apiRefusalCategory: nullableStringField(msg, "api_refusal_category"),
534
650
  apiRefusalExplanation: nullableStringField(msg, "api_refusal_explanation"),
535
651
  content: stringField(msg, "content"),
@@ -549,6 +665,7 @@ function handleFallbackRetractionMessage(session, subtype, msg) {
549
665
  direction: metadata.direction,
550
666
  original_model: metadata.originalModel,
551
667
  fallback_model: metadata.fallbackModel,
668
+ scope: metadata.scope,
552
669
  api_refusal_category: metadata.apiRefusalCategory,
553
670
  has_api_refusal_explanation: metadata.apiRefusalExplanation !== undefined,
554
671
  has_content: metadata.content !== undefined,
@@ -576,6 +693,18 @@ function logContentBlockLinkage(session, blockType, toolUseId, toolName, linkage
576
693
  },
577
694
  });
578
695
  }
696
+ function resolveToolProgressTarget(session, taskToolUseId, toolUseId, parentToolUseId) {
697
+ if (taskToolUseId && session.toolCalls.has(taskToolUseId)) {
698
+ return { toolUseId: taskToolUseId, source: "task" };
699
+ }
700
+ if (toolUseId && session.toolCalls.has(toolUseId)) {
701
+ return { toolUseId, source: "tool" };
702
+ }
703
+ if (parentToolUseId && session.toolCalls.has(parentToolUseId)) {
704
+ return { toolUseId: parentToolUseId, source: "parent" };
705
+ }
706
+ return null;
707
+ }
579
708
  function hideToolUse(session, toolUseId) {
580
709
  if (toolUseId) {
581
710
  session.hiddenToolUseIds.add(toolUseId);
@@ -609,7 +738,9 @@ export function handleContentBlock(session, block, linkage) {
609
738
  emitSessionUpdate(session.sessionId, {
610
739
  type: "agent_message_chunk",
611
740
  content: { type: "text", text },
612
- ...(linkage?.sourceMessageUuid ? { source_message_uuid: linkage.sourceMessageUuid } : {}),
741
+ ...(linkage?.sourceMessageUuid
742
+ ? { source_message_uuid: linkage.sourceMessageUuid }
743
+ : {}),
613
744
  });
614
745
  }
615
746
  return;
@@ -620,15 +751,21 @@ export function handleContentBlock(session, block, linkage) {
620
751
  emitSessionUpdate(session.sessionId, {
621
752
  type: "agent_thought_chunk",
622
753
  content: { type: "text", text },
623
- ...(linkage?.sourceMessageUuid ? { source_message_uuid: linkage.sourceMessageUuid } : {}),
754
+ ...(linkage?.sourceMessageUuid
755
+ ? { source_message_uuid: linkage.sourceMessageUuid }
756
+ : {}),
624
757
  });
625
758
  }
626
759
  return;
627
760
  }
628
- if (blockType === "tool_use" || blockType === "server_tool_use" || blockType === "mcp_tool_use") {
761
+ if (blockType === "tool_use" ||
762
+ blockType === "server_tool_use" ||
763
+ blockType === "mcp_tool_use") {
629
764
  const toolUseId = typeof block.id === "string" ? block.id : "";
630
765
  const name = typeof block.name === "string" ? block.name : "Tool";
631
- const input = block.input && typeof block.input === "object" ? block.input : {};
766
+ const input = block.input && typeof block.input === "object"
767
+ ? block.input
768
+ : {};
632
769
  if (!toolUseId) {
633
770
  return;
634
771
  }
@@ -676,7 +813,9 @@ export function handleStreamEvent(session, event, parentToolUseId, sourceMessage
676
813
  emitSessionUpdate(session.sessionId, {
677
814
  type: "agent_message_chunk",
678
815
  content: { type: "text", text },
679
- ...(sourceMessageUuid ? { source_message_uuid: sourceMessageUuid } : {}),
816
+ ...(sourceMessageUuid
817
+ ? { source_message_uuid: sourceMessageUuid }
818
+ : {}),
680
819
  });
681
820
  }
682
821
  }
@@ -686,7 +825,9 @@ export function handleStreamEvent(session, event, parentToolUseId, sourceMessage
686
825
  emitSessionUpdate(session.sessionId, {
687
826
  type: "agent_thought_chunk",
688
827
  content: { type: "text", text },
689
- ...(sourceMessageUuid ? { source_message_uuid: sourceMessageUuid } : {}),
828
+ ...(sourceMessageUuid
829
+ ? { source_message_uuid: sourceMessageUuid }
830
+ : {}),
690
831
  });
691
832
  }
692
833
  }
@@ -706,7 +847,9 @@ export function handleAssistantMessage(session, message) {
706
847
  if (!messageObject) {
707
848
  return;
708
849
  }
709
- const content = Array.isArray(messageObject.content) ? messageObject.content : [];
850
+ const content = Array.isArray(messageObject.content)
851
+ ? messageObject.content
852
+ : [];
710
853
  for (const block of content) {
711
854
  if (!block || typeof block !== "object") {
712
855
  continue;
@@ -717,7 +860,9 @@ export function handleAssistantMessage(session, message) {
717
860
  blockType === "server_tool_use" ||
718
861
  blockType === "mcp_tool_use" ||
719
862
  TOOL_RESULT_TYPES.has(blockType)) {
720
- const parentToolUseId = typeof message.parent_tool_use_id === "string" ? message.parent_tool_use_id : undefined;
863
+ const parentToolUseId = typeof message.parent_tool_use_id === "string"
864
+ ? message.parent_tool_use_id
865
+ : undefined;
721
866
  handleContentBlock(session, blockRecord, {
722
867
  source: "assistant",
723
868
  parentToolUseId,
@@ -743,7 +888,9 @@ export function handleUserToolResultBlocks(session, message) {
743
888
  if (!messageObject) {
744
889
  return false;
745
890
  }
746
- const content = Array.isArray(messageObject.content) ? messageObject.content : [];
891
+ const content = Array.isArray(messageObject.content)
892
+ ? messageObject.content
893
+ : [];
747
894
  const nonExecutionByToolUseId = parseToolNonExecutionMetadata(message.tool_result_meta);
748
895
  let handled = false;
749
896
  for (const block of content) {
@@ -753,8 +900,12 @@ export function handleUserToolResultBlocks(session, message) {
753
900
  const blockRecord = block;
754
901
  const blockType = typeof blockRecord.type === "string" ? blockRecord.type : "";
755
902
  if (TOOL_RESULT_TYPES.has(blockType)) {
756
- const parentToolUseId = typeof message.parent_tool_use_id === "string" ? message.parent_tool_use_id : undefined;
757
- const toolUseId = typeof blockRecord.tool_use_id === "string" ? blockRecord.tool_use_id : "";
903
+ const parentToolUseId = typeof message.parent_tool_use_id === "string"
904
+ ? message.parent_tool_use_id
905
+ : undefined;
906
+ const toolUseId = typeof blockRecord.tool_use_id === "string"
907
+ ? blockRecord.tool_use_id
908
+ : "";
758
909
  if (!toolUseId) {
759
910
  continue;
760
911
  }
@@ -786,9 +937,30 @@ export function handleResultMessage(session, message) {
786
937
  });
787
938
  return;
788
939
  }
789
- const errors = Array.isArray(message.errors) && message.errors.every((entry) => typeof entry === "string")
940
+ const errors = Array.isArray(message.errors) &&
941
+ message.errors.every((entry) => typeof entry === "string")
790
942
  ? message.errors
791
943
  : [];
944
+ const resumeDropsTurnRefusal = errors.find((entry) => entry.startsWith("Resume rejected by --resume-drops-turn:"));
945
+ if (session.deferConnect &&
946
+ session.resumeDropsTurn &&
947
+ resumeDropsTurnRefusal) {
948
+ session.initializationReady = false;
949
+ session.initializationError = resumeDropsTurnRefusal;
950
+ bridgeLogger.warn({
951
+ target: LOG_TARGETS.APP_SESSION,
952
+ eventName: "session_resume_drops_turn_rejected",
953
+ message: "guarded resume candidate rejected because the source session changed",
954
+ outcome: "failure",
955
+ sessionId: session.sessionId,
956
+ fields: {
957
+ drop_turn_id: session.resumeDropsTurn,
958
+ validation_fence_complete: session.resumeGuardFenceComplete === true,
959
+ error_message: resumeDropsTurnRefusal,
960
+ },
961
+ });
962
+ return;
963
+ }
792
964
  const assistantError = session.lastAssistantError;
793
965
  const authHint = errors.find((entry) => looksLikeAuthRequired(entry));
794
966
  if (authHint) {
@@ -808,7 +980,9 @@ export function handleResultMessage(session, message) {
808
980
  error_kind: errorKind,
809
981
  ...(subtype ? { sdk_result_subtype: subtype } : {}),
810
982
  ...(assistantError ? { assistant_error: assistantError } : {}),
811
- ...(apiErrorStatus !== undefined ? { api_error_status: apiErrorStatus } : {}),
983
+ ...(apiErrorStatus !== undefined
984
+ ? { api_error_status: apiErrorStatus }
985
+ : {}),
812
986
  ...(terminalReason ? { terminal_reason: terminalReason } : {}),
813
987
  });
814
988
  session.lastAssistantError = undefined;
@@ -928,8 +1102,12 @@ export function handleSdkMessage(session, message) {
928
1102
  fields: {
929
1103
  tool_name: typeof msg.tool_name === "string" ? msg.tool_name : undefined,
930
1104
  agent_id: typeof msg.agent_id === "string" ? msg.agent_id : undefined,
931
- decision_reason_type: typeof msg.decision_reason_type === "string" ? msg.decision_reason_type : undefined,
932
- decision_reason: typeof msg.decision_reason === "string" ? msg.decision_reason : undefined,
1105
+ decision_reason_type: typeof msg.decision_reason_type === "string"
1106
+ ? msg.decision_reason_type
1107
+ : undefined,
1108
+ decision_reason: typeof msg.decision_reason === "string"
1109
+ ? msg.decision_reason
1110
+ : undefined,
933
1111
  denial_message: typeof msg.message === "string" ? msg.message : undefined,
934
1112
  },
935
1113
  });
@@ -944,9 +1122,15 @@ export function handleSdkMessage(session, message) {
944
1122
  sessionId: session.sessionId,
945
1123
  fields: {
946
1124
  sdk_subtype: subtype,
947
- memory_count: Array.isArray(msg.memories) ? msg.memories.length : undefined,
948
- estimated_tokens: typeof msg.estimated_tokens === "number" ? msg.estimated_tokens : undefined,
949
- estimated_tokens_delta: typeof msg.estimated_tokens_delta === "number" ? msg.estimated_tokens_delta : undefined,
1125
+ memory_count: Array.isArray(msg.memories)
1126
+ ? msg.memories.length
1127
+ : undefined,
1128
+ estimated_tokens: typeof msg.estimated_tokens === "number"
1129
+ ? msg.estimated_tokens
1130
+ : undefined,
1131
+ estimated_tokens_delta: typeof msg.estimated_tokens_delta === "number"
1132
+ ? msg.estimated_tokens_delta
1133
+ : undefined,
950
1134
  },
951
1135
  });
952
1136
  return;
@@ -975,7 +1159,9 @@ export function handleSdkMessage(session, message) {
975
1159
  const modelName = typeof msg.model === "string" ? msg.model : session.model;
976
1160
  session.model = modelName;
977
1161
  const currentModelChanged = refreshCurrentModel(session, false);
978
- const incomingMode = typeof msg.permissionMode === "string" ? toPermissionMode(msg.permissionMode) : null;
1162
+ const incomingMode = typeof msg.permissionMode === "string"
1163
+ ? toPermissionMode(msg.permissionMode)
1164
+ : null;
979
1165
  if (incomingMode) {
980
1166
  session.mode = incomingMode;
981
1167
  }
@@ -1007,7 +1193,8 @@ export function handleSdkMessage(session, message) {
1007
1193
  if (Array.isArray(msg.mcp_servers)) {
1008
1194
  emitMcpSnapshotFromStatuses(session, msg.mcp_servers, "init");
1009
1195
  }
1010
- if (session.lastAvailableAgentsSignature === undefined && Array.isArray(msg.agents)) {
1196
+ if (session.lastAvailableAgentsSignature === undefined &&
1197
+ Array.isArray(msg.agents)) {
1011
1198
  emitAvailableAgentsIfChanged(session, mapAvailableAgentsFromNames(msg.agents));
1012
1199
  }
1013
1200
  void session.query
@@ -1029,20 +1216,54 @@ export function handleSdkMessage(session, message) {
1029
1216
  return;
1030
1217
  }
1031
1218
  if (subtype === "status") {
1032
- const mode = typeof msg.permissionMode === "string" ? toPermissionMode(msg.permissionMode) : null;
1219
+ const mode = typeof msg.permissionMode === "string"
1220
+ ? toPermissionMode(msg.permissionMode)
1221
+ : null;
1033
1222
  if (mode) {
1034
1223
  session.mode = mode;
1035
1224
  refreshSupportedModesForSession(session);
1036
- emitSessionUpdate(session.sessionId, { type: "current_mode_update", current_mode_id: mode });
1225
+ emitSessionUpdate(session.sessionId, {
1226
+ type: "current_mode_update",
1227
+ current_mode_id: mode,
1228
+ });
1037
1229
  }
1038
1230
  if (msg.status === "compacting") {
1039
- emitSessionUpdate(session.sessionId, { type: "session_status_update", status: "compacting" });
1231
+ emitSessionUpdate(session.sessionId, {
1232
+ type: "compaction_update",
1233
+ phase: "started",
1234
+ });
1040
1235
  }
1041
1236
  else if (msg.status === "requesting") {
1042
- emitSessionUpdate(session.sessionId, { type: "session_status_update", status: "requesting" });
1237
+ emitSessionUpdate(session.sessionId, {
1238
+ type: "session_status_update",
1239
+ status: "requesting",
1240
+ });
1043
1241
  }
1044
1242
  else if (msg.status === null) {
1045
- emitSessionUpdate(session.sessionId, { type: "session_status_update", status: "idle" });
1243
+ if (msg.compact_result === "success") {
1244
+ emitSessionUpdate(session.sessionId, {
1245
+ type: "compaction_update",
1246
+ phase: "finished",
1247
+ result: "success",
1248
+ });
1249
+ }
1250
+ else if (msg.compact_result === "failed") {
1251
+ const compactError = typeof msg.compact_error === "string" &&
1252
+ msg.compact_error.trim().length > 0
1253
+ ? msg.compact_error.trim()
1254
+ : undefined;
1255
+ emitSessionUpdate(session.sessionId, {
1256
+ type: "compaction_update",
1257
+ phase: "finished",
1258
+ result: "failed",
1259
+ error_code: compactError === "too_few_groups" ? "too_few_groups" : "unknown",
1260
+ ...(compactError ? { error: compactError } : {}),
1261
+ });
1262
+ }
1263
+ emitSessionUpdate(session.sessionId, {
1264
+ type: "session_status_update",
1265
+ status: "idle",
1266
+ });
1046
1267
  }
1047
1268
  emitFastModeUpdateIfChanged(session, msg.fast_mode_state, msg.fast_mode_disabled_reason);
1048
1269
  return;
@@ -1053,12 +1274,18 @@ export function handleSdkMessage(session, message) {
1053
1274
  return;
1054
1275
  }
1055
1276
  const trigger = compactMetadata.trigger;
1056
- const preTokens = numberField(compactMetadata, "pre_tokens", "preTokens");
1057
- if ((trigger === "manual" || trigger === "auto") && preTokens !== undefined) {
1277
+ const preTokens = nonNegativeIntegerField(compactMetadata, "pre_tokens", "preTokens");
1278
+ const postTokens = nonNegativeIntegerField(compactMetadata, "post_tokens", "postTokens");
1279
+ const durationMs = nonNegativeIntegerField(compactMetadata, "duration_ms", "durationMs");
1280
+ if ((trigger === "manual" || trigger === "auto") &&
1281
+ preTokens !== undefined) {
1058
1282
  emitSessionUpdate(session.sessionId, {
1059
- type: "compaction_boundary",
1283
+ type: "compaction_update",
1284
+ phase: "boundary",
1060
1285
  trigger,
1061
1286
  pre_tokens: preTokens,
1287
+ ...(postTokens !== undefined ? { post_tokens: postTokens } : {}),
1288
+ ...(durationMs !== undefined ? { duration_ms: durationMs } : {}),
1062
1289
  });
1063
1290
  }
1064
1291
  return;
@@ -1069,7 +1296,9 @@ export function handleSdkMessage(session, message) {
1069
1296
  emitSessionUpdate(session.sessionId, {
1070
1297
  type: "agent_message_chunk",
1071
1298
  content: { type: "text", text: content },
1072
- ...(sourceMessageUuid(msg) ? { source_message_uuid: sourceMessageUuid(msg) } : {}),
1299
+ ...(sourceMessageUuid(msg)
1300
+ ? { source_message_uuid: sourceMessageUuid(msg) }
1301
+ : {}),
1073
1302
  });
1074
1303
  }
1075
1304
  return;
@@ -1084,7 +1313,9 @@ export function handleSdkMessage(session, message) {
1084
1313
  session_id: session.sessionId,
1085
1314
  completion: {
1086
1315
  elicitation_id: elicitationId,
1087
- ...(typeof msg.mcp_server_name === "string" ? { server_name: msg.mcp_server_name } : {}),
1316
+ ...(typeof msg.mcp_server_name === "string"
1317
+ ? { server_name: msg.mcp_server_name }
1318
+ : {}),
1088
1319
  },
1089
1320
  });
1090
1321
  return;
@@ -1107,7 +1338,10 @@ export function handleSdkMessage(session, message) {
1107
1338
  if (type === "prompt_suggestion") {
1108
1339
  const suggestion = typeof msg.suggestion === "string" ? msg.suggestion.trim() : "";
1109
1340
  if (suggestion) {
1110
- emitSessionUpdate(session.sessionId, { type: "prompt_suggestion_update", suggestion });
1341
+ emitSessionUpdate(session.sessionId, {
1342
+ type: "prompt_suggestion_update",
1343
+ suggestion,
1344
+ });
1111
1345
  }
1112
1346
  return;
1113
1347
  }
@@ -1122,10 +1356,14 @@ export function handleSdkMessage(session, message) {
1122
1356
  }
1123
1357
  if (type === "auth_status") {
1124
1358
  const output = Array.isArray(msg.output)
1125
- ? msg.output.filter((entry) => typeof entry === "string").join("\n")
1359
+ ? msg.output
1360
+ .filter((entry) => typeof entry === "string")
1361
+ .join("\n")
1126
1362
  : "";
1127
1363
  const errorText = typeof msg.error === "string" ? msg.error : "";
1128
- const combined = [errorText, output].filter((entry) => entry.length > 0).join("\n");
1364
+ const combined = [errorText, output]
1365
+ .filter((entry) => entry.length > 0)
1366
+ .join("\n");
1129
1367
  if (combined && looksLikeAuthRequired(combined)) {
1130
1368
  emitAuthRequired(session, combined);
1131
1369
  }
@@ -1133,7 +1371,9 @@ export function handleSdkMessage(session, message) {
1133
1371
  }
1134
1372
  if (type === "stream_event") {
1135
1373
  if (msg.event && typeof msg.event === "object") {
1136
- const parentToolUseId = typeof msg.parent_tool_use_id === "string" ? msg.parent_tool_use_id : undefined;
1374
+ const parentToolUseId = typeof msg.parent_tool_use_id === "string"
1375
+ ? msg.parent_tool_use_id
1376
+ : undefined;
1137
1377
  handleStreamEvent(session, msg.event, parentToolUseId, sourceMessageUuid(msg));
1138
1378
  }
1139
1379
  return;
@@ -1141,30 +1381,38 @@ export function handleSdkMessage(session, message) {
1141
1381
  if (type === "tool_progress") {
1142
1382
  const toolUseId = typeof msg.tool_use_id === "string" ? msg.tool_use_id : "";
1143
1383
  const toolName = typeof msg.tool_name === "string" ? msg.tool_name : "Tool";
1384
+ const parentToolUseId = typeof msg.parent_tool_use_id === "string" ? msg.parent_tool_use_id : "";
1144
1385
  const taskId = typeof msg.task_id === "string" ? msg.task_id : "";
1145
- const taskToolUseId = taskId ? session.taskToolUseIds.get(taskId) ?? "" : "";
1146
- const resolvedToolUseId = taskToolUseId || toolUseId;
1147
- if (isHiddenToolUse(session, resolvedToolUseId, toolName)) {
1386
+ const taskToolUseId = taskId
1387
+ ? (session.taskToolUseIds.get(taskId) ?? "")
1388
+ : "";
1389
+ if (isHiddenToolUse(session, toolUseId, toolName)) {
1148
1390
  return;
1149
1391
  }
1392
+ const progressTarget = resolveToolProgressTarget(session, taskToolUseId, toolUseId, parentToolUseId);
1393
+ const resolvedToolUseId = progressTarget?.toolUseId ?? "";
1150
1394
  bridgeLogger.debug({
1151
1395
  target: LOG_TARGETS.APP_TOOL,
1152
1396
  eventName: "sdk_tool_progress_linkage_observed",
1153
1397
  message: "SDK tool progress linkage observed",
1154
- outcome: typeof msg.parent_tool_use_id === "string" ? "child" : "root_or_unknown",
1398
+ outcome: progressTarget?.source ?? "orphaned",
1155
1399
  sessionId: session.sessionId,
1156
- toolCallId: resolvedToolUseId || undefined,
1400
+ toolCallId: resolvedToolUseId || toolUseId || undefined,
1157
1401
  fields: {
1158
1402
  tool_name: toolName,
1159
1403
  tool_use_id: toolUseId || undefined,
1160
- parent_tool_use_id: typeof msg.parent_tool_use_id === "string" ? msg.parent_tool_use_id : undefined,
1404
+ parent_tool_use_id: parentToolUseId || undefined,
1161
1405
  task_id: taskId || undefined,
1162
1406
  task_resolved_tool_use_id: taskToolUseId || undefined,
1407
+ resolved_tool_use_id: resolvedToolUseId || undefined,
1408
+ correlation_source: progressTarget?.source,
1163
1409
  },
1164
1410
  });
1165
1411
  if (resolvedToolUseId) {
1166
1412
  const hasSubagentRetry = Object.hasOwn(msg, "subagent_retry");
1167
- const subagentRetry = hasSubagentRetry ? buildSubagentRetryUpdate(msg) : null;
1413
+ const subagentRetry = hasSubagentRetry
1414
+ ? buildSubagentRetryUpdate(msg)
1415
+ : null;
1168
1416
  if (hasSubagentRetry && !subagentRetry) {
1169
1417
  bridgeLogger.warn({
1170
1418
  target: LOG_TARGETS.APP_TOOL,
@@ -1178,7 +1426,7 @@ export function handleSdkMessage(session, message) {
1178
1426
  const subagentType = typeof msg.subagent_type === "string" && msg.subagent_type.trim()
1179
1427
  ? msg.subagent_type.trim()
1180
1428
  : undefined;
1181
- emitToolProgressUpdate(session, resolvedToolUseId, toolName, {
1429
+ emitToolProgressUpdate(session, resolvedToolUseId, {
1182
1430
  ...(subagentRetry
1183
1431
  ? { subagentRetry }
1184
1432
  : hasSubagentRetry
@@ -1207,8 +1455,12 @@ export function handleSdkMessage(session, message) {
1207
1455
  if (type === "rate_limit_event") {
1208
1456
  const rateLimitInfo = asRecordOrNull(msg.rate_limit_info);
1209
1457
  const update = buildRateLimitUpdate(msg.rate_limit_info);
1210
- const rawIsUsingOverage = typeof rateLimitInfo?.isUsingOverage === "boolean" ? rateLimitInfo.isUsingOverage : undefined;
1211
- const rawOverageInUse = typeof rateLimitInfo?.overageInUse === "boolean" ? rateLimitInfo.overageInUse : undefined;
1458
+ const rawIsUsingOverage = typeof rateLimitInfo?.isUsingOverage === "boolean"
1459
+ ? rateLimitInfo.isUsingOverage
1460
+ : undefined;
1461
+ const rawOverageInUse = typeof rateLimitInfo?.overageInUse === "boolean"
1462
+ ? rateLimitInfo.overageInUse
1463
+ : undefined;
1212
1464
  if (rawIsUsingOverage !== undefined &&
1213
1465
  rawOverageInUse !== undefined &&
1214
1466
  rawIsUsingOverage !== rawOverageInUse) {
@@ -1231,11 +1483,17 @@ export function handleSdkMessage(session, message) {
1231
1483
  outcome: update ? "success" : "dropped",
1232
1484
  sessionId: session.sessionId,
1233
1485
  fields: {
1234
- raw_status: typeof rateLimitInfo?.status === "string" ? rateLimitInfo.status : undefined,
1235
- raw_rate_limit_type: typeof rateLimitInfo?.rateLimitType === "string" ? rateLimitInfo.rateLimitType : undefined,
1486
+ raw_status: typeof rateLimitInfo?.status === "string"
1487
+ ? rateLimitInfo.status
1488
+ : undefined,
1489
+ raw_rate_limit_type: typeof rateLimitInfo?.rateLimitType === "string"
1490
+ ? rateLimitInfo.rateLimitType
1491
+ : undefined,
1236
1492
  raw_utilization: numberField(rateLimitInfo ?? {}, "utilization"),
1237
1493
  raw_resets_at: numberField(rateLimitInfo ?? {}, "resetsAt"),
1238
- raw_overage_status: typeof rateLimitInfo?.overageStatus === "string" ? rateLimitInfo.overageStatus : undefined,
1494
+ raw_overage_status: typeof rateLimitInfo?.overageStatus === "string"
1495
+ ? rateLimitInfo.overageStatus
1496
+ : undefined,
1239
1497
  raw_overage_resets_at: numberField(rateLimitInfo ?? {}, "overageResetsAt"),
1240
1498
  raw_is_using_overage: rawIsUsingOverage,
1241
1499
  raw_overage_in_use: rawOverageInUse,
@@ -1266,6 +1524,10 @@ export function handleSdkMessage(session, message) {
1266
1524
  return;
1267
1525
  }
1268
1526
  if (type === "user") {
1527
+ const externalMessageUpdate = externalMessageUpdateFromSdkUser(msg);
1528
+ if (externalMessageUpdate) {
1529
+ emitSessionUpdate(session.sessionId, externalMessageUpdate);
1530
+ }
1269
1531
  const handledBlocks = handleUserToolResultBlocks(session, msg);
1270
1532
  const toolUseId = typeof msg.parent_tool_use_id === "string" ? msg.parent_tool_use_id : "";
1271
1533
  const rawToolUseResult = messageToolUseResult(msg);