@yushaw/sanqian-chat 0.3.0 → 0.3.3

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.
@@ -84,8 +84,18 @@ type MessageRole = 'user' | 'assistant' | 'system' | 'tool';
84
84
  type ToolCallStatus = 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
85
85
  type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
86
86
  type ConnectionErrorCode = 'NOT_FOUND' | 'CONNECTION_FAILED' | 'WEBSOCKET_ERROR' | 'AUTH_ERROR' | 'TIMEOUT' | 'UNKNOWN';
87
+ interface ToolExecutionMetadata {
88
+ commandExitCode?: number;
89
+ durationMs?: number;
90
+ sandboxed?: boolean;
91
+ timedOut?: boolean;
92
+ truncated?: boolean;
93
+ stdoutPath?: string;
94
+ stderrPath?: string;
95
+ presentation?: string;
96
+ }
87
97
  /** Tool call for UI rendering (extended from SDK) */
88
- interface ToolCall {
98
+ interface ToolCall extends ToolExecutionMetadata {
89
99
  id?: string;
90
100
  name: string;
91
101
  args?: Record<string, unknown>;
@@ -93,9 +103,12 @@ interface ToolCall {
93
103
  status?: ToolCallStatus;
94
104
  result?: unknown;
95
105
  error?: string;
106
+ actionRequired?: string;
107
+ settingsTab?: string;
108
+ settingsSubTab?: string;
96
109
  }
97
110
  /** Message block for structured rendering */
98
- interface MessageBlock {
111
+ interface MessageBlock extends ToolExecutionMetadata {
99
112
  type: 'thinking' | 'text' | 'tool_call' | 'tool_result';
100
113
  content: string;
101
114
  timestamp: number;
@@ -104,6 +117,7 @@ interface MessageBlock {
104
117
  toolArgsRaw?: string;
105
118
  toolCallId?: string;
106
119
  toolStatus?: ToolCallStatus;
120
+ actionRequired?: string;
107
121
  isIntermediate?: boolean;
108
122
  fromSubagent?: boolean;
109
123
  }
@@ -533,6 +547,19 @@ type StreamEvent = {
533
547
  type: 'tool_result';
534
548
  tool_call_id: string;
535
549
  result: unknown;
550
+ error?: string;
551
+ status?: 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
552
+ action_required?: string;
553
+ settings_tab?: string;
554
+ settings_sub_tab?: string;
555
+ command_exit_code?: number;
556
+ duration_ms?: number;
557
+ sandboxed?: boolean;
558
+ timed_out?: boolean;
559
+ truncated?: boolean;
560
+ stdout_path?: string;
561
+ stderr_path?: string;
562
+ presentation?: string;
536
563
  } | {
537
564
  type: 'done';
538
565
  conversationId: string;
@@ -699,4 +726,4 @@ declare function parseToolCalls(toolCalls: unknown): ToolCall[] | undefined;
699
726
  */
700
727
  declare function mergeConsecutiveAssistantMessages(rawMessages: ApiMessage[]): ChatMessage[];
701
728
 
702
- export { type ApiMessage, type AttachConfig, type AttachPosition, type AttachState, type AttachedResource, type AttachmentMenuItem, type AttachmentMenuItemType, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, type ChatMessage, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfigSerializable, type ChatUiStrings, type ConnectionErrorCode, type ConnectionStatus, type ContextProviderInfo, type ConversationDetail, type ConversationInfo, type FloatingWindowConfig, type HitlInterruptData, type HitlResponseOutcome, type LinkClickEvent, type LinkHandlerConfig, type Locale, type MessageBlock, type MessageRole, type ResourcePickerItem, type ResourcePickerState, type SdkAdapterConfig, type SendMessage, type SessionResource, type SessionResourceEvent, type StoredSessionResource, type StreamEvent, type ToolCall, type ToolCallStatus, type WindowPosition, createChatAdapter, createSdkAdapter, mergeConsecutiveAssistantMessages, parseToolCalls };
729
+ export { type ApiMessage, type AttachConfig, type AttachPosition, type AttachState, type AttachedResource, type AttachmentMenuItem, type AttachmentMenuItemType, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, type ChatMessage, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfigSerializable, type ChatUiStrings, type ConnectionErrorCode, type ConnectionStatus, type ContextProviderInfo, type ConversationDetail, type ConversationInfo, type FloatingWindowConfig, type HitlInterruptData, type HitlResponseOutcome, type LinkClickEvent, type LinkHandlerConfig, type Locale, type MessageBlock, type MessageRole, type ResourcePickerItem, type ResourcePickerState, type SdkAdapterConfig, type SendMessage, type SessionResource, type SessionResourceEvent, type StoredSessionResource, type StreamEvent, type ToolCall, type ToolCallStatus, type ToolExecutionMetadata, type WindowPosition, createChatAdapter, createSdkAdapter, mergeConsecutiveAssistantMessages, parseToolCalls };
@@ -84,8 +84,18 @@ type MessageRole = 'user' | 'assistant' | 'system' | 'tool';
84
84
  type ToolCallStatus = 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
85
85
  type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
86
86
  type ConnectionErrorCode = 'NOT_FOUND' | 'CONNECTION_FAILED' | 'WEBSOCKET_ERROR' | 'AUTH_ERROR' | 'TIMEOUT' | 'UNKNOWN';
87
+ interface ToolExecutionMetadata {
88
+ commandExitCode?: number;
89
+ durationMs?: number;
90
+ sandboxed?: boolean;
91
+ timedOut?: boolean;
92
+ truncated?: boolean;
93
+ stdoutPath?: string;
94
+ stderrPath?: string;
95
+ presentation?: string;
96
+ }
87
97
  /** Tool call for UI rendering (extended from SDK) */
88
- interface ToolCall {
98
+ interface ToolCall extends ToolExecutionMetadata {
89
99
  id?: string;
90
100
  name: string;
91
101
  args?: Record<string, unknown>;
@@ -93,9 +103,12 @@ interface ToolCall {
93
103
  status?: ToolCallStatus;
94
104
  result?: unknown;
95
105
  error?: string;
106
+ actionRequired?: string;
107
+ settingsTab?: string;
108
+ settingsSubTab?: string;
96
109
  }
97
110
  /** Message block for structured rendering */
98
- interface MessageBlock {
111
+ interface MessageBlock extends ToolExecutionMetadata {
99
112
  type: 'thinking' | 'text' | 'tool_call' | 'tool_result';
100
113
  content: string;
101
114
  timestamp: number;
@@ -104,6 +117,7 @@ interface MessageBlock {
104
117
  toolArgsRaw?: string;
105
118
  toolCallId?: string;
106
119
  toolStatus?: ToolCallStatus;
120
+ actionRequired?: string;
107
121
  isIntermediate?: boolean;
108
122
  fromSubagent?: boolean;
109
123
  }
@@ -533,6 +547,19 @@ type StreamEvent = {
533
547
  type: 'tool_result';
534
548
  tool_call_id: string;
535
549
  result: unknown;
550
+ error?: string;
551
+ status?: 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
552
+ action_required?: string;
553
+ settings_tab?: string;
554
+ settings_sub_tab?: string;
555
+ command_exit_code?: number;
556
+ duration_ms?: number;
557
+ sandboxed?: boolean;
558
+ timed_out?: boolean;
559
+ truncated?: boolean;
560
+ stdout_path?: string;
561
+ stderr_path?: string;
562
+ presentation?: string;
536
563
  } | {
537
564
  type: 'done';
538
565
  conversationId: string;
@@ -699,4 +726,4 @@ declare function parseToolCalls(toolCalls: unknown): ToolCall[] | undefined;
699
726
  */
700
727
  declare function mergeConsecutiveAssistantMessages(rawMessages: ApiMessage[]): ChatMessage[];
701
728
 
702
- export { type ApiMessage, type AttachConfig, type AttachPosition, type AttachState, type AttachedResource, type AttachmentMenuItem, type AttachmentMenuItemType, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, type ChatMessage, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfigSerializable, type ChatUiStrings, type ConnectionErrorCode, type ConnectionStatus, type ContextProviderInfo, type ConversationDetail, type ConversationInfo, type FloatingWindowConfig, type HitlInterruptData, type HitlResponseOutcome, type LinkClickEvent, type LinkHandlerConfig, type Locale, type MessageBlock, type MessageRole, type ResourcePickerItem, type ResourcePickerState, type SdkAdapterConfig, type SendMessage, type SessionResource, type SessionResourceEvent, type StoredSessionResource, type StreamEvent, type ToolCall, type ToolCallStatus, type WindowPosition, createChatAdapter, createSdkAdapter, mergeConsecutiveAssistantMessages, parseToolCalls };
729
+ export { type ApiMessage, type AttachConfig, type AttachPosition, type AttachState, type AttachedResource, type AttachmentMenuItem, type AttachmentMenuItemType, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, type ChatMessage, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfigSerializable, type ChatUiStrings, type ConnectionErrorCode, type ConnectionStatus, type ContextProviderInfo, type ConversationDetail, type ConversationInfo, type FloatingWindowConfig, type HitlInterruptData, type HitlResponseOutcome, type LinkClickEvent, type LinkHandlerConfig, type Locale, type MessageBlock, type MessageRole, type ResourcePickerItem, type ResourcePickerState, type SdkAdapterConfig, type SendMessage, type SessionResource, type SessionResourceEvent, type StoredSessionResource, type StreamEvent, type ToolCall, type ToolCallStatus, type ToolExecutionMetadata, type WindowPosition, createChatAdapter, createSdkAdapter, mergeConsecutiveAssistantMessages, parseToolCalls };
@@ -37,7 +37,77 @@ __export(core_exports, {
37
37
  });
38
38
  module.exports = __toCommonJS(core_exports);
39
39
 
40
+ // src/core/tool-status.ts
41
+ var BLOCKING_ACTION_REQUIRED = /* @__PURE__ */ new Set(["install_git_bash"]);
42
+ function normalizeToolExecutionStatus(status) {
43
+ if (typeof status !== "string") return void 0;
44
+ switch (status.trim().toLowerCase()) {
45
+ case "success":
46
+ case "completed":
47
+ case "ok":
48
+ return "completed";
49
+ case "error":
50
+ case "failed":
51
+ return "error";
52
+ case "cancelled":
53
+ case "canceled":
54
+ return "cancelled";
55
+ case "running":
56
+ return "running";
57
+ case "pending":
58
+ return "pending";
59
+ default:
60
+ return void 0;
61
+ }
62
+ }
63
+ function deriveToolExecutionStatus(options) {
64
+ if (typeof options.actionRequired === "string" && BLOCKING_ACTION_REQUIRED.has(options.actionRequired)) {
65
+ return "error";
66
+ }
67
+ if (options.error === true) {
68
+ return "error";
69
+ }
70
+ if (typeof options.error === "string" && options.error.trim().length > 0) {
71
+ return "error";
72
+ }
73
+ const normalizedStatus = normalizeToolExecutionStatus(options.status);
74
+ if (normalizedStatus) {
75
+ return normalizedStatus;
76
+ }
77
+ if (options.result !== void 0 && options.result !== null) {
78
+ if (typeof options.result === "string") {
79
+ const normalizedResult = options.result.trimStart();
80
+ if (normalizedResult.startsWith("\u274C") || normalizedResult.startsWith("Error:")) {
81
+ return "error";
82
+ }
83
+ }
84
+ return "completed";
85
+ }
86
+ return options.fallback ?? "running";
87
+ }
88
+
40
89
  // src/core/history.ts
90
+ function readToolExecutionNumber(value) {
91
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
92
+ }
93
+ function readToolExecutionBoolean(value) {
94
+ return typeof value === "boolean" ? value : void 0;
95
+ }
96
+ function readToolExecutionString(value) {
97
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
98
+ }
99
+ function readToolExecutionMetadata(tc) {
100
+ return {
101
+ commandExitCode: readToolExecutionNumber(tc.command_exit_code ?? tc.commandExitCode),
102
+ durationMs: readToolExecutionNumber(tc.duration_ms ?? tc.durationMs),
103
+ sandboxed: readToolExecutionBoolean(tc.sandboxed),
104
+ timedOut: readToolExecutionBoolean(tc.timed_out ?? tc.timedOut),
105
+ truncated: readToolExecutionBoolean(tc.truncated),
106
+ stdoutPath: readToolExecutionString(tc.stdout_path ?? tc.stdoutPath),
107
+ stderrPath: readToolExecutionString(tc.stderr_path ?? tc.stderrPath),
108
+ presentation: readToolExecutionString(tc.presentation)
109
+ };
110
+ }
41
111
  function safeParseArgs(value) {
42
112
  if (!value) return void 0;
43
113
  if (typeof value === "object") return value;
@@ -67,12 +137,26 @@ function parseToolCalls(toolCalls) {
67
137
  const rawArgs = fn?.arguments ?? tc.args ?? tc.arguments;
68
138
  const args = safeParseArgs(rawArgs);
69
139
  const id = tc.id || tc.tool_call_id || tc.call_id;
140
+ const actionRequired = tc.action_required || tc.actionRequired;
141
+ const settingsTab = tc.settings_tab || tc.settingsTab;
142
+ const settingsSubTab = tc.settings_sub_tab || tc.settingsSubTab;
70
143
  return {
71
144
  id,
72
145
  name,
73
146
  args,
74
- status: "completed",
75
- result: tc.result
147
+ result: tc.result,
148
+ error: typeof tc.error === "string" ? tc.error : void 0,
149
+ actionRequired,
150
+ settingsTab,
151
+ settingsSubTab,
152
+ status: deriveToolExecutionStatus({
153
+ status: tc.status,
154
+ result: tc.result,
155
+ actionRequired,
156
+ error: tc.error,
157
+ fallback: "completed"
158
+ }),
159
+ ...readToolExecutionMetadata(tc)
76
160
  };
77
161
  }).filter((tc) => tc.name || tc.id);
78
162
  }
@@ -140,6 +224,10 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
140
224
  const consecutiveAssistantMsgs = [msg];
141
225
  const toolMessages = [];
142
226
  let j = i + 1;
227
+ const hasDeclaredToolCalls = (message) => {
228
+ const calls = parseToolCalls(message.toolCalls || message.tool_calls);
229
+ return Boolean(calls && calls.length > 0);
230
+ };
143
231
  while (j < rawMessages.length) {
144
232
  if (rawMessages[j].role === "assistant") {
145
233
  consecutiveAssistantMsgs.push(rawMessages[j]);
@@ -155,15 +243,19 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
155
243
  let blockTime = Date.now();
156
244
  let fallbackToolCalls;
157
245
  let lastEffectiveToolCalls;
246
+ const groupHasDeclaredToolCalls = consecutiveAssistantMsgs.some(hasDeclaredToolCalls);
158
247
  for (let k = 0; k < consecutiveAssistantMsgs.length; k++) {
159
248
  const assistantMsg = consecutiveAssistantMsgs[k];
160
249
  const msgToolCalls = parseToolCalls(assistantMsg.toolCalls || assistantMsg.tool_calls);
161
250
  const isLastAssistant = k === consecutiveAssistantMsgs.length - 1;
162
- const fallbackCalls = isLastAssistant && toolMessages.length > 0 ? toolMessages.map((toolMessage, idx) => ({
251
+ const fallbackCalls = !groupHasDeclaredToolCalls && isLastAssistant && toolMessages.length > 0 ? toolMessages.map((toolMessage, idx) => ({
163
252
  id: (toolMessage.tool_call_id || void 0) ?? `tool-${idx}`,
164
253
  name: "",
165
254
  args: void 0,
166
- status: "completed",
255
+ status: deriveToolExecutionStatus({
256
+ result: toolMessage.content,
257
+ fallback: "completed"
258
+ }),
167
259
  result: toolMessage.content
168
260
  })) : void 0;
169
261
  const effectiveToolCalls = msgToolCalls && msgToolCalls.length > 0 ? msgToolCalls : fallbackCalls;
@@ -196,6 +288,16 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
196
288
  const toolResultBlocks = [];
197
289
  for (const tc of effectiveToolCalls) {
198
290
  const toolIndex = effectiveToolCalls.indexOf(tc);
291
+ const toolResultById = tc.id ? toolMessages.find((tm) => tm.tool_call_id === tc.id) : void 0;
292
+ const toolResultByIndex = toolMessages[toolIndex];
293
+ const toolResult = toolResultById || toolResultByIndex;
294
+ const toolStatus = deriveToolExecutionStatus({
295
+ status: tc.status,
296
+ result: tc.result ?? toolResult?.content,
297
+ actionRequired: tc.actionRequired,
298
+ error: tc.error,
299
+ fallback: "completed"
300
+ });
199
301
  blocks.push({
200
302
  type: "tool_call",
201
303
  content: "",
@@ -203,12 +305,18 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
203
305
  toolName: tc.name,
204
306
  toolArgs: tc.args,
205
307
  toolCallId: tc.id,
206
- toolStatus: "completed",
207
- isIntermediate: true
308
+ toolStatus,
309
+ actionRequired: tc.actionRequired,
310
+ isIntermediate: true,
311
+ commandExitCode: tc.commandExitCode,
312
+ durationMs: tc.durationMs,
313
+ sandboxed: tc.sandboxed,
314
+ timedOut: tc.timedOut,
315
+ truncated: tc.truncated,
316
+ stdoutPath: tc.stdoutPath,
317
+ stderrPath: tc.stderrPath,
318
+ presentation: tc.presentation
208
319
  });
209
- const toolResultById = tc.id ? toolMessages.find((tm) => tm.tool_call_id === tc.id) : void 0;
210
- const toolResultByIndex = toolMessages[toolIndex];
211
- const toolResult = toolResultById || toolResultByIndex;
212
320
  const resultContent = tc.result ?? toolResult?.content;
213
321
  const toolResultId = toolResult?.tool_call_id || void 0;
214
322
  if (resultContent) {
@@ -218,7 +326,16 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
218
326
  timestamp: blockTime++,
219
327
  toolName: tc.name,
220
328
  toolCallId: tc.id || toolResultId,
221
- isIntermediate: true
329
+ actionRequired: tc.actionRequired,
330
+ isIntermediate: true,
331
+ commandExitCode: tc.commandExitCode,
332
+ durationMs: tc.durationMs,
333
+ sandboxed: tc.sandboxed,
334
+ timedOut: tc.timedOut,
335
+ truncated: tc.truncated,
336
+ stdoutPath: tc.stdoutPath,
337
+ stderrPath: tc.stderrPath,
338
+ presentation: tc.presentation
222
339
  });
223
340
  tc.result = resultContent;
224
341
  }
@@ -251,6 +368,13 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
251
368
  );
252
369
  if (matchingToolCall) {
253
370
  matchingToolCall.result = toolMsg.content;
371
+ matchingToolCall.status = deriveToolExecutionStatus({
372
+ status: matchingToolCall.status,
373
+ result: toolMsg.content,
374
+ actionRequired: matchingToolCall.actionRequired,
375
+ error: matchingToolCall.error,
376
+ fallback: "completed"
377
+ });
254
378
  }
255
379
  }
256
380
  const thinkingParts = consecutiveAssistantMsgs.map((m) => m.thinking).filter((t) => t && t.trim());
@@ -568,9 +692,28 @@ function processStreamEvents(stream, onEvent, sdk, setCurrentRunId, streamId) {
568
692
  args: event.args || {}
569
693
  });
570
694
  break;
571
- case "tool_result":
572
- forward({ type: "tool_result", tool_call_id: event.tool_call_id || "", result: event.result });
695
+ case "tool_result": {
696
+ const toolResultEvent = event;
697
+ forward({
698
+ type: "tool_result",
699
+ tool_call_id: toolResultEvent.tool_call_id || "",
700
+ result: toolResultEvent.result,
701
+ error: toolResultEvent.error,
702
+ status: toolResultEvent.status,
703
+ action_required: toolResultEvent.action_required,
704
+ settings_tab: toolResultEvent.settings_tab,
705
+ settings_sub_tab: toolResultEvent.settings_sub_tab,
706
+ command_exit_code: toolResultEvent.command_exit_code,
707
+ duration_ms: toolResultEvent.duration_ms,
708
+ sandboxed: toolResultEvent.sandboxed,
709
+ timed_out: toolResultEvent.timed_out,
710
+ truncated: toolResultEvent.truncated,
711
+ stdout_path: toolResultEvent.stdout_path,
712
+ stderr_path: toolResultEvent.stderr_path,
713
+ presentation: toolResultEvent.presentation
714
+ });
573
715
  break;
716
+ }
574
717
  case "done":
575
718
  forward({ type: "done", conversationId: event.conversationId || "", title: event.title });
576
719
  currentRunId = null;
@@ -1,4 +1,74 @@
1
+ // src/core/tool-status.ts
2
+ var BLOCKING_ACTION_REQUIRED = /* @__PURE__ */ new Set(["install_git_bash"]);
3
+ function normalizeToolExecutionStatus(status) {
4
+ if (typeof status !== "string") return void 0;
5
+ switch (status.trim().toLowerCase()) {
6
+ case "success":
7
+ case "completed":
8
+ case "ok":
9
+ return "completed";
10
+ case "error":
11
+ case "failed":
12
+ return "error";
13
+ case "cancelled":
14
+ case "canceled":
15
+ return "cancelled";
16
+ case "running":
17
+ return "running";
18
+ case "pending":
19
+ return "pending";
20
+ default:
21
+ return void 0;
22
+ }
23
+ }
24
+ function deriveToolExecutionStatus(options) {
25
+ if (typeof options.actionRequired === "string" && BLOCKING_ACTION_REQUIRED.has(options.actionRequired)) {
26
+ return "error";
27
+ }
28
+ if (options.error === true) {
29
+ return "error";
30
+ }
31
+ if (typeof options.error === "string" && options.error.trim().length > 0) {
32
+ return "error";
33
+ }
34
+ const normalizedStatus = normalizeToolExecutionStatus(options.status);
35
+ if (normalizedStatus) {
36
+ return normalizedStatus;
37
+ }
38
+ if (options.result !== void 0 && options.result !== null) {
39
+ if (typeof options.result === "string") {
40
+ const normalizedResult = options.result.trimStart();
41
+ if (normalizedResult.startsWith("\u274C") || normalizedResult.startsWith("Error:")) {
42
+ return "error";
43
+ }
44
+ }
45
+ return "completed";
46
+ }
47
+ return options.fallback ?? "running";
48
+ }
49
+
1
50
  // src/core/history.ts
51
+ function readToolExecutionNumber(value) {
52
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
53
+ }
54
+ function readToolExecutionBoolean(value) {
55
+ return typeof value === "boolean" ? value : void 0;
56
+ }
57
+ function readToolExecutionString(value) {
58
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
59
+ }
60
+ function readToolExecutionMetadata(tc) {
61
+ return {
62
+ commandExitCode: readToolExecutionNumber(tc.command_exit_code ?? tc.commandExitCode),
63
+ durationMs: readToolExecutionNumber(tc.duration_ms ?? tc.durationMs),
64
+ sandboxed: readToolExecutionBoolean(tc.sandboxed),
65
+ timedOut: readToolExecutionBoolean(tc.timed_out ?? tc.timedOut),
66
+ truncated: readToolExecutionBoolean(tc.truncated),
67
+ stdoutPath: readToolExecutionString(tc.stdout_path ?? tc.stdoutPath),
68
+ stderrPath: readToolExecutionString(tc.stderr_path ?? tc.stderrPath),
69
+ presentation: readToolExecutionString(tc.presentation)
70
+ };
71
+ }
2
72
  function safeParseArgs(value) {
3
73
  if (!value) return void 0;
4
74
  if (typeof value === "object") return value;
@@ -28,12 +98,26 @@ function parseToolCalls(toolCalls) {
28
98
  const rawArgs = fn?.arguments ?? tc.args ?? tc.arguments;
29
99
  const args = safeParseArgs(rawArgs);
30
100
  const id = tc.id || tc.tool_call_id || tc.call_id;
101
+ const actionRequired = tc.action_required || tc.actionRequired;
102
+ const settingsTab = tc.settings_tab || tc.settingsTab;
103
+ const settingsSubTab = tc.settings_sub_tab || tc.settingsSubTab;
31
104
  return {
32
105
  id,
33
106
  name,
34
107
  args,
35
- status: "completed",
36
- result: tc.result
108
+ result: tc.result,
109
+ error: typeof tc.error === "string" ? tc.error : void 0,
110
+ actionRequired,
111
+ settingsTab,
112
+ settingsSubTab,
113
+ status: deriveToolExecutionStatus({
114
+ status: tc.status,
115
+ result: tc.result,
116
+ actionRequired,
117
+ error: tc.error,
118
+ fallback: "completed"
119
+ }),
120
+ ...readToolExecutionMetadata(tc)
37
121
  };
38
122
  }).filter((tc) => tc.name || tc.id);
39
123
  }
@@ -101,6 +185,10 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
101
185
  const consecutiveAssistantMsgs = [msg];
102
186
  const toolMessages = [];
103
187
  let j = i + 1;
188
+ const hasDeclaredToolCalls = (message) => {
189
+ const calls = parseToolCalls(message.toolCalls || message.tool_calls);
190
+ return Boolean(calls && calls.length > 0);
191
+ };
104
192
  while (j < rawMessages.length) {
105
193
  if (rawMessages[j].role === "assistant") {
106
194
  consecutiveAssistantMsgs.push(rawMessages[j]);
@@ -116,15 +204,19 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
116
204
  let blockTime = Date.now();
117
205
  let fallbackToolCalls;
118
206
  let lastEffectiveToolCalls;
207
+ const groupHasDeclaredToolCalls = consecutiveAssistantMsgs.some(hasDeclaredToolCalls);
119
208
  for (let k = 0; k < consecutiveAssistantMsgs.length; k++) {
120
209
  const assistantMsg = consecutiveAssistantMsgs[k];
121
210
  const msgToolCalls = parseToolCalls(assistantMsg.toolCalls || assistantMsg.tool_calls);
122
211
  const isLastAssistant = k === consecutiveAssistantMsgs.length - 1;
123
- const fallbackCalls = isLastAssistant && toolMessages.length > 0 ? toolMessages.map((toolMessage, idx) => ({
212
+ const fallbackCalls = !groupHasDeclaredToolCalls && isLastAssistant && toolMessages.length > 0 ? toolMessages.map((toolMessage, idx) => ({
124
213
  id: (toolMessage.tool_call_id || void 0) ?? `tool-${idx}`,
125
214
  name: "",
126
215
  args: void 0,
127
- status: "completed",
216
+ status: deriveToolExecutionStatus({
217
+ result: toolMessage.content,
218
+ fallback: "completed"
219
+ }),
128
220
  result: toolMessage.content
129
221
  })) : void 0;
130
222
  const effectiveToolCalls = msgToolCalls && msgToolCalls.length > 0 ? msgToolCalls : fallbackCalls;
@@ -157,6 +249,16 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
157
249
  const toolResultBlocks = [];
158
250
  for (const tc of effectiveToolCalls) {
159
251
  const toolIndex = effectiveToolCalls.indexOf(tc);
252
+ const toolResultById = tc.id ? toolMessages.find((tm) => tm.tool_call_id === tc.id) : void 0;
253
+ const toolResultByIndex = toolMessages[toolIndex];
254
+ const toolResult = toolResultById || toolResultByIndex;
255
+ const toolStatus = deriveToolExecutionStatus({
256
+ status: tc.status,
257
+ result: tc.result ?? toolResult?.content,
258
+ actionRequired: tc.actionRequired,
259
+ error: tc.error,
260
+ fallback: "completed"
261
+ });
160
262
  blocks.push({
161
263
  type: "tool_call",
162
264
  content: "",
@@ -164,12 +266,18 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
164
266
  toolName: tc.name,
165
267
  toolArgs: tc.args,
166
268
  toolCallId: tc.id,
167
- toolStatus: "completed",
168
- isIntermediate: true
269
+ toolStatus,
270
+ actionRequired: tc.actionRequired,
271
+ isIntermediate: true,
272
+ commandExitCode: tc.commandExitCode,
273
+ durationMs: tc.durationMs,
274
+ sandboxed: tc.sandboxed,
275
+ timedOut: tc.timedOut,
276
+ truncated: tc.truncated,
277
+ stdoutPath: tc.stdoutPath,
278
+ stderrPath: tc.stderrPath,
279
+ presentation: tc.presentation
169
280
  });
170
- const toolResultById = tc.id ? toolMessages.find((tm) => tm.tool_call_id === tc.id) : void 0;
171
- const toolResultByIndex = toolMessages[toolIndex];
172
- const toolResult = toolResultById || toolResultByIndex;
173
281
  const resultContent = tc.result ?? toolResult?.content;
174
282
  const toolResultId = toolResult?.tool_call_id || void 0;
175
283
  if (resultContent) {
@@ -179,7 +287,16 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
179
287
  timestamp: blockTime++,
180
288
  toolName: tc.name,
181
289
  toolCallId: tc.id || toolResultId,
182
- isIntermediate: true
290
+ actionRequired: tc.actionRequired,
291
+ isIntermediate: true,
292
+ commandExitCode: tc.commandExitCode,
293
+ durationMs: tc.durationMs,
294
+ sandboxed: tc.sandboxed,
295
+ timedOut: tc.timedOut,
296
+ truncated: tc.truncated,
297
+ stdoutPath: tc.stdoutPath,
298
+ stderrPath: tc.stderrPath,
299
+ presentation: tc.presentation
183
300
  });
184
301
  tc.result = resultContent;
185
302
  }
@@ -212,6 +329,13 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
212
329
  );
213
330
  if (matchingToolCall) {
214
331
  matchingToolCall.result = toolMsg.content;
332
+ matchingToolCall.status = deriveToolExecutionStatus({
333
+ status: matchingToolCall.status,
334
+ result: toolMsg.content,
335
+ actionRequired: matchingToolCall.actionRequired,
336
+ error: matchingToolCall.error,
337
+ fallback: "completed"
338
+ });
215
339
  }
216
340
  }
217
341
  const thinkingParts = consecutiveAssistantMsgs.map((m) => m.thinking).filter((t) => t && t.trim());
@@ -529,9 +653,28 @@ function processStreamEvents(stream, onEvent, sdk, setCurrentRunId, streamId) {
529
653
  args: event.args || {}
530
654
  });
531
655
  break;
532
- case "tool_result":
533
- forward({ type: "tool_result", tool_call_id: event.tool_call_id || "", result: event.result });
656
+ case "tool_result": {
657
+ const toolResultEvent = event;
658
+ forward({
659
+ type: "tool_result",
660
+ tool_call_id: toolResultEvent.tool_call_id || "",
661
+ result: toolResultEvent.result,
662
+ error: toolResultEvent.error,
663
+ status: toolResultEvent.status,
664
+ action_required: toolResultEvent.action_required,
665
+ settings_tab: toolResultEvent.settings_tab,
666
+ settings_sub_tab: toolResultEvent.settings_sub_tab,
667
+ command_exit_code: toolResultEvent.command_exit_code,
668
+ duration_ms: toolResultEvent.duration_ms,
669
+ sandboxed: toolResultEvent.sandboxed,
670
+ timed_out: toolResultEvent.timed_out,
671
+ truncated: toolResultEvent.truncated,
672
+ stdout_path: toolResultEvent.stdout_path,
673
+ stderr_path: toolResultEvent.stderr_path,
674
+ presentation: toolResultEvent.presentation
675
+ });
534
676
  break;
677
+ }
535
678
  case "done":
536
679
  forward({ type: "done", conversationId: event.conversationId || "", title: event.title });
537
680
  currentRunId = null;
@@ -617,7 +617,27 @@ function registerStreamIpcHandlers(ipcMainHandle, ctx) {
617
617
  });
618
618
  break;
619
619
  case "tool_result":
620
- webContents.send("sanqian-chat:streamEvent", { streamId, event: { type: "tool_result", tool_call_id: evt.tool_call_id, result: evt.result } });
620
+ webContents.send("sanqian-chat:streamEvent", {
621
+ streamId,
622
+ event: {
623
+ type: "tool_result",
624
+ tool_call_id: evt.tool_call_id,
625
+ result: evt.result,
626
+ error: evt.error,
627
+ status: evt.status,
628
+ action_required: evt.action_required,
629
+ settings_tab: evt.settings_tab,
630
+ settings_sub_tab: evt.settings_sub_tab,
631
+ command_exit_code: evt.command_exit_code,
632
+ duration_ms: evt.duration_ms,
633
+ sandboxed: evt.sandboxed,
634
+ timed_out: evt.timed_out,
635
+ truncated: evt.truncated,
636
+ stdout_path: evt.stdout_path,
637
+ stderr_path: evt.stderr_path,
638
+ presentation: evt.presentation
639
+ }
640
+ });
621
641
  break;
622
642
  case "done":
623
643
  sawTerminalEvent = true;
@@ -586,7 +586,27 @@ function registerStreamIpcHandlers(ipcMainHandle, ctx) {
586
586
  });
587
587
  break;
588
588
  case "tool_result":
589
- webContents.send("sanqian-chat:streamEvent", { streamId, event: { type: "tool_result", tool_call_id: evt.tool_call_id, result: evt.result } });
589
+ webContents.send("sanqian-chat:streamEvent", {
590
+ streamId,
591
+ event: {
592
+ type: "tool_result",
593
+ tool_call_id: evt.tool_call_id,
594
+ result: evt.result,
595
+ error: evt.error,
596
+ status: evt.status,
597
+ action_required: evt.action_required,
598
+ settings_tab: evt.settings_tab,
599
+ settings_sub_tab: evt.settings_sub_tab,
600
+ command_exit_code: evt.command_exit_code,
601
+ duration_ms: evt.duration_ms,
602
+ sandboxed: evt.sandboxed,
603
+ timed_out: evt.timed_out,
604
+ truncated: evt.truncated,
605
+ stdout_path: evt.stdout_path,
606
+ stderr_path: evt.stderr_path,
607
+ presentation: evt.presentation
608
+ }
609
+ });
590
610
  break;
591
611
  case "done":
592
612
  sawTerminalEvent = true;