@yushaw/sanqian-chat 0.3.0 → 0.3.1

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.
@@ -93,6 +93,9 @@ interface ToolCall {
93
93
  status?: ToolCallStatus;
94
94
  result?: unknown;
95
95
  error?: string;
96
+ actionRequired?: string;
97
+ settingsTab?: string;
98
+ settingsSubTab?: string;
96
99
  }
97
100
  /** Message block for structured rendering */
98
101
  interface MessageBlock {
@@ -533,6 +536,11 @@ type StreamEvent = {
533
536
  type: 'tool_result';
534
537
  tool_call_id: string;
535
538
  result: unknown;
539
+ error?: string;
540
+ status?: 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
541
+ action_required?: string;
542
+ settings_tab?: string;
543
+ settings_sub_tab?: string;
536
544
  } | {
537
545
  type: 'done';
538
546
  conversationId: string;
@@ -93,6 +93,9 @@ interface ToolCall {
93
93
  status?: ToolCallStatus;
94
94
  result?: unknown;
95
95
  error?: string;
96
+ actionRequired?: string;
97
+ settingsTab?: string;
98
+ settingsSubTab?: string;
96
99
  }
97
100
  /** Message block for structured rendering */
98
101
  interface MessageBlock {
@@ -533,6 +536,11 @@ type StreamEvent = {
533
536
  type: 'tool_result';
534
537
  tool_call_id: string;
535
538
  result: unknown;
539
+ error?: string;
540
+ status?: 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
541
+ action_required?: string;
542
+ settings_tab?: string;
543
+ settings_sub_tab?: string;
536
544
  } | {
537
545
  type: 'done';
538
546
  conversationId: string;
@@ -37,6 +37,54 @@ __export(core_exports, {
37
37
  });
38
38
  module.exports = __toCommonJS(core_exports);
39
39
 
40
+ // src/core/tool-status.ts
41
+ function normalizeToolExecutionStatus(status) {
42
+ if (typeof status !== "string") return void 0;
43
+ switch (status.trim().toLowerCase()) {
44
+ case "success":
45
+ case "completed":
46
+ case "ok":
47
+ return "completed";
48
+ case "error":
49
+ case "failed":
50
+ return "error";
51
+ case "cancelled":
52
+ case "canceled":
53
+ return "cancelled";
54
+ case "running":
55
+ return "running";
56
+ case "pending":
57
+ return "pending";
58
+ default:
59
+ return void 0;
60
+ }
61
+ }
62
+ function deriveToolExecutionStatus(options) {
63
+ if (typeof options.actionRequired === "string" && options.actionRequired.length > 0) {
64
+ return "error";
65
+ }
66
+ if (options.error === true) {
67
+ return "error";
68
+ }
69
+ if (typeof options.error === "string" && options.error.trim().length > 0) {
70
+ return "error";
71
+ }
72
+ const normalizedStatus = normalizeToolExecutionStatus(options.status);
73
+ if (normalizedStatus) {
74
+ return normalizedStatus;
75
+ }
76
+ if (options.result !== void 0 && options.result !== null) {
77
+ if (typeof options.result === "string") {
78
+ const normalizedResult = options.result.trimStart();
79
+ if (normalizedResult.startsWith("\u274C") || normalizedResult.startsWith("Error:")) {
80
+ return "error";
81
+ }
82
+ }
83
+ return "completed";
84
+ }
85
+ return options.fallback ?? "running";
86
+ }
87
+
40
88
  // src/core/history.ts
41
89
  function safeParseArgs(value) {
42
90
  if (!value) return void 0;
@@ -67,12 +115,25 @@ function parseToolCalls(toolCalls) {
67
115
  const rawArgs = fn?.arguments ?? tc.args ?? tc.arguments;
68
116
  const args = safeParseArgs(rawArgs);
69
117
  const id = tc.id || tc.tool_call_id || tc.call_id;
118
+ const actionRequired = tc.action_required || tc.actionRequired;
119
+ const settingsTab = tc.settings_tab || tc.settingsTab;
120
+ const settingsSubTab = tc.settings_sub_tab || tc.settingsSubTab;
70
121
  return {
71
122
  id,
72
123
  name,
73
124
  args,
74
- status: "completed",
75
- result: tc.result
125
+ result: tc.result,
126
+ error: typeof tc.error === "string" ? tc.error : void 0,
127
+ actionRequired,
128
+ settingsTab,
129
+ settingsSubTab,
130
+ status: deriveToolExecutionStatus({
131
+ status: tc.status,
132
+ result: tc.result,
133
+ actionRequired,
134
+ error: tc.error,
135
+ fallback: "completed"
136
+ })
76
137
  };
77
138
  }).filter((tc) => tc.name || tc.id);
78
139
  }
@@ -163,7 +224,10 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
163
224
  id: (toolMessage.tool_call_id || void 0) ?? `tool-${idx}`,
164
225
  name: "",
165
226
  args: void 0,
166
- status: "completed",
227
+ status: deriveToolExecutionStatus({
228
+ result: toolMessage.content,
229
+ fallback: "completed"
230
+ }),
167
231
  result: toolMessage.content
168
232
  })) : void 0;
169
233
  const effectiveToolCalls = msgToolCalls && msgToolCalls.length > 0 ? msgToolCalls : fallbackCalls;
@@ -196,6 +260,16 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
196
260
  const toolResultBlocks = [];
197
261
  for (const tc of effectiveToolCalls) {
198
262
  const toolIndex = effectiveToolCalls.indexOf(tc);
263
+ const toolResultById = tc.id ? toolMessages.find((tm) => tm.tool_call_id === tc.id) : void 0;
264
+ const toolResultByIndex = toolMessages[toolIndex];
265
+ const toolResult = toolResultById || toolResultByIndex;
266
+ const toolStatus = deriveToolExecutionStatus({
267
+ status: tc.status,
268
+ result: tc.result ?? toolResult?.content,
269
+ actionRequired: tc.actionRequired,
270
+ error: tc.error,
271
+ fallback: "completed"
272
+ });
199
273
  blocks.push({
200
274
  type: "tool_call",
201
275
  content: "",
@@ -203,12 +277,9 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
203
277
  toolName: tc.name,
204
278
  toolArgs: tc.args,
205
279
  toolCallId: tc.id,
206
- toolStatus: "completed",
280
+ toolStatus,
207
281
  isIntermediate: true
208
282
  });
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
283
  const resultContent = tc.result ?? toolResult?.content;
213
284
  const toolResultId = toolResult?.tool_call_id || void 0;
214
285
  if (resultContent) {
@@ -251,6 +322,13 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
251
322
  );
252
323
  if (matchingToolCall) {
253
324
  matchingToolCall.result = toolMsg.content;
325
+ matchingToolCall.status = deriveToolExecutionStatus({
326
+ status: matchingToolCall.status,
327
+ result: toolMsg.content,
328
+ actionRequired: matchingToolCall.actionRequired,
329
+ error: matchingToolCall.error,
330
+ fallback: "completed"
331
+ });
254
332
  }
255
333
  }
256
334
  const thinkingParts = consecutiveAssistantMsgs.map((m) => m.thinking).filter((t) => t && t.trim());
@@ -568,9 +646,20 @@ function processStreamEvents(stream, onEvent, sdk, setCurrentRunId, streamId) {
568
646
  args: event.args || {}
569
647
  });
570
648
  break;
571
- case "tool_result":
572
- forward({ type: "tool_result", tool_call_id: event.tool_call_id || "", result: event.result });
649
+ case "tool_result": {
650
+ const toolResultEvent = event;
651
+ forward({
652
+ type: "tool_result",
653
+ tool_call_id: toolResultEvent.tool_call_id || "",
654
+ result: toolResultEvent.result,
655
+ error: toolResultEvent.error,
656
+ status: toolResultEvent.status,
657
+ action_required: toolResultEvent.action_required,
658
+ settings_tab: toolResultEvent.settings_tab,
659
+ settings_sub_tab: toolResultEvent.settings_sub_tab
660
+ });
573
661
  break;
662
+ }
574
663
  case "done":
575
664
  forward({ type: "done", conversationId: event.conversationId || "", title: event.title });
576
665
  currentRunId = null;
@@ -1,3 +1,51 @@
1
+ // src/core/tool-status.ts
2
+ function normalizeToolExecutionStatus(status) {
3
+ if (typeof status !== "string") return void 0;
4
+ switch (status.trim().toLowerCase()) {
5
+ case "success":
6
+ case "completed":
7
+ case "ok":
8
+ return "completed";
9
+ case "error":
10
+ case "failed":
11
+ return "error";
12
+ case "cancelled":
13
+ case "canceled":
14
+ return "cancelled";
15
+ case "running":
16
+ return "running";
17
+ case "pending":
18
+ return "pending";
19
+ default:
20
+ return void 0;
21
+ }
22
+ }
23
+ function deriveToolExecutionStatus(options) {
24
+ if (typeof options.actionRequired === "string" && options.actionRequired.length > 0) {
25
+ return "error";
26
+ }
27
+ if (options.error === true) {
28
+ return "error";
29
+ }
30
+ if (typeof options.error === "string" && options.error.trim().length > 0) {
31
+ return "error";
32
+ }
33
+ const normalizedStatus = normalizeToolExecutionStatus(options.status);
34
+ if (normalizedStatus) {
35
+ return normalizedStatus;
36
+ }
37
+ if (options.result !== void 0 && options.result !== null) {
38
+ if (typeof options.result === "string") {
39
+ const normalizedResult = options.result.trimStart();
40
+ if (normalizedResult.startsWith("\u274C") || normalizedResult.startsWith("Error:")) {
41
+ return "error";
42
+ }
43
+ }
44
+ return "completed";
45
+ }
46
+ return options.fallback ?? "running";
47
+ }
48
+
1
49
  // src/core/history.ts
2
50
  function safeParseArgs(value) {
3
51
  if (!value) return void 0;
@@ -28,12 +76,25 @@ function parseToolCalls(toolCalls) {
28
76
  const rawArgs = fn?.arguments ?? tc.args ?? tc.arguments;
29
77
  const args = safeParseArgs(rawArgs);
30
78
  const id = tc.id || tc.tool_call_id || tc.call_id;
79
+ const actionRequired = tc.action_required || tc.actionRequired;
80
+ const settingsTab = tc.settings_tab || tc.settingsTab;
81
+ const settingsSubTab = tc.settings_sub_tab || tc.settingsSubTab;
31
82
  return {
32
83
  id,
33
84
  name,
34
85
  args,
35
- status: "completed",
36
- result: tc.result
86
+ result: tc.result,
87
+ error: typeof tc.error === "string" ? tc.error : void 0,
88
+ actionRequired,
89
+ settingsTab,
90
+ settingsSubTab,
91
+ status: deriveToolExecutionStatus({
92
+ status: tc.status,
93
+ result: tc.result,
94
+ actionRequired,
95
+ error: tc.error,
96
+ fallback: "completed"
97
+ })
37
98
  };
38
99
  }).filter((tc) => tc.name || tc.id);
39
100
  }
@@ -124,7 +185,10 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
124
185
  id: (toolMessage.tool_call_id || void 0) ?? `tool-${idx}`,
125
186
  name: "",
126
187
  args: void 0,
127
- status: "completed",
188
+ status: deriveToolExecutionStatus({
189
+ result: toolMessage.content,
190
+ fallback: "completed"
191
+ }),
128
192
  result: toolMessage.content
129
193
  })) : void 0;
130
194
  const effectiveToolCalls = msgToolCalls && msgToolCalls.length > 0 ? msgToolCalls : fallbackCalls;
@@ -157,6 +221,16 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
157
221
  const toolResultBlocks = [];
158
222
  for (const tc of effectiveToolCalls) {
159
223
  const toolIndex = effectiveToolCalls.indexOf(tc);
224
+ const toolResultById = tc.id ? toolMessages.find((tm) => tm.tool_call_id === tc.id) : void 0;
225
+ const toolResultByIndex = toolMessages[toolIndex];
226
+ const toolResult = toolResultById || toolResultByIndex;
227
+ const toolStatus = deriveToolExecutionStatus({
228
+ status: tc.status,
229
+ result: tc.result ?? toolResult?.content,
230
+ actionRequired: tc.actionRequired,
231
+ error: tc.error,
232
+ fallback: "completed"
233
+ });
160
234
  blocks.push({
161
235
  type: "tool_call",
162
236
  content: "",
@@ -164,12 +238,9 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
164
238
  toolName: tc.name,
165
239
  toolArgs: tc.args,
166
240
  toolCallId: tc.id,
167
- toolStatus: "completed",
241
+ toolStatus,
168
242
  isIntermediate: true
169
243
  });
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
244
  const resultContent = tc.result ?? toolResult?.content;
174
245
  const toolResultId = toolResult?.tool_call_id || void 0;
175
246
  if (resultContent) {
@@ -212,6 +283,13 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
212
283
  );
213
284
  if (matchingToolCall) {
214
285
  matchingToolCall.result = toolMsg.content;
286
+ matchingToolCall.status = deriveToolExecutionStatus({
287
+ status: matchingToolCall.status,
288
+ result: toolMsg.content,
289
+ actionRequired: matchingToolCall.actionRequired,
290
+ error: matchingToolCall.error,
291
+ fallback: "completed"
292
+ });
215
293
  }
216
294
  }
217
295
  const thinkingParts = consecutiveAssistantMsgs.map((m) => m.thinking).filter((t) => t && t.trim());
@@ -529,9 +607,20 @@ function processStreamEvents(stream, onEvent, sdk, setCurrentRunId, streamId) {
529
607
  args: event.args || {}
530
608
  });
531
609
  break;
532
- case "tool_result":
533
- forward({ type: "tool_result", tool_call_id: event.tool_call_id || "", result: event.result });
610
+ case "tool_result": {
611
+ const toolResultEvent = event;
612
+ forward({
613
+ type: "tool_result",
614
+ tool_call_id: toolResultEvent.tool_call_id || "",
615
+ result: toolResultEvent.result,
616
+ error: toolResultEvent.error,
617
+ status: toolResultEvent.status,
618
+ action_required: toolResultEvent.action_required,
619
+ settings_tab: toolResultEvent.settings_tab,
620
+ settings_sub_tab: toolResultEvent.settings_sub_tab
621
+ });
534
622
  break;
623
+ }
535
624
  case "done":
536
625
  forward({ type: "done", conversationId: event.conversationId || "", title: event.title });
537
626
  currentRunId = null;
@@ -617,7 +617,19 @@ 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
+ }
632
+ });
621
633
  break;
622
634
  case "done":
623
635
  sawTerminalEvent = true;
@@ -586,7 +586,19 @@ 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
+ }
601
+ });
590
602
  break;
591
603
  case "done":
592
604
  sawTerminalEvent = true;
@@ -96,6 +96,9 @@ interface ToolCall {
96
96
  status?: ToolCallStatus;
97
97
  result?: unknown;
98
98
  error?: string;
99
+ actionRequired?: string;
100
+ settingsTab?: string;
101
+ settingsSubTab?: string;
99
102
  }
100
103
  /** Message block for structured rendering */
101
104
  interface MessageBlock {
@@ -536,6 +539,11 @@ type StreamEvent = {
536
539
  type: 'tool_result';
537
540
  tool_call_id: string;
538
541
  result: unknown;
542
+ error?: string;
543
+ status?: 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
544
+ action_required?: string;
545
+ settings_tab?: string;
546
+ settings_sub_tab?: string;
539
547
  } | {
540
548
  type: 'done';
541
549
  conversationId: string;
@@ -96,6 +96,9 @@ interface ToolCall {
96
96
  status?: ToolCallStatus;
97
97
  result?: unknown;
98
98
  error?: string;
99
+ actionRequired?: string;
100
+ settingsTab?: string;
101
+ settingsSubTab?: string;
99
102
  }
100
103
  /** Message block for structured rendering */
101
104
  interface MessageBlock {
@@ -536,6 +539,11 @@ type StreamEvent = {
536
539
  type: 'tool_result';
537
540
  tool_call_id: string;
538
541
  result: unknown;
542
+ error?: string;
543
+ status?: 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
544
+ action_required?: string;
545
+ settings_tab?: string;
546
+ settings_sub_tab?: string;
539
547
  } | {
540
548
  type: 'done';
541
549
  conversationId: string;
@@ -88,6 +88,67 @@ module.exports = __toCommonJS(renderer_exports);
88
88
 
89
89
  // src/renderer/hooks/useChat.ts
90
90
  var import_react = require("react");
91
+
92
+ // src/core/tool-status.ts
93
+ function normalizeToolExecutionStatus(status) {
94
+ if (typeof status !== "string") return void 0;
95
+ switch (status.trim().toLowerCase()) {
96
+ case "success":
97
+ case "completed":
98
+ case "ok":
99
+ return "completed";
100
+ case "error":
101
+ case "failed":
102
+ return "error";
103
+ case "cancelled":
104
+ case "canceled":
105
+ return "cancelled";
106
+ case "running":
107
+ return "running";
108
+ case "pending":
109
+ return "pending";
110
+ default:
111
+ return void 0;
112
+ }
113
+ }
114
+ function deriveToolExecutionStatus(options) {
115
+ if (typeof options.actionRequired === "string" && options.actionRequired.length > 0) {
116
+ return "error";
117
+ }
118
+ if (options.error === true) {
119
+ return "error";
120
+ }
121
+ if (typeof options.error === "string" && options.error.trim().length > 0) {
122
+ return "error";
123
+ }
124
+ const normalizedStatus = normalizeToolExecutionStatus(options.status);
125
+ if (normalizedStatus) {
126
+ return normalizedStatus;
127
+ }
128
+ if (options.result !== void 0 && options.result !== null) {
129
+ if (typeof options.result === "string") {
130
+ const normalizedResult = options.result.trimStart();
131
+ if (normalizedResult.startsWith("\u274C") || normalizedResult.startsWith("Error:")) {
132
+ return "error";
133
+ }
134
+ }
135
+ return "completed";
136
+ }
137
+ return options.fallback ?? "running";
138
+ }
139
+ function formatToolResultContent(result, error) {
140
+ const value = result ?? error;
141
+ if (value === void 0 || value === null) return void 0;
142
+ if (typeof value === "string") return value;
143
+ try {
144
+ const serialized = JSON.stringify(value);
145
+ return serialized === void 0 ? String(value) : serialized;
146
+ } catch {
147
+ return String(value);
148
+ }
149
+ }
150
+
151
+ // src/renderer/hooks/useChat.ts
91
152
  var TYPEWRITER_DELAYS = { VERY_FAST: 2, FAST: 5, NORMAL: 10, SLOW: 20 };
92
153
  var TYPEWRITER_THRESHOLDS = { VERY_FAST: 100, FAST: 50, NORMAL: 20 };
93
154
  var MAX_DETACHED_SNAPSHOTS = 30;
@@ -108,6 +169,15 @@ var findLastBlock = (blocks, predicate) => {
108
169
  }
109
170
  return void 0;
110
171
  };
172
+ function getToolResultStatus(event) {
173
+ return deriveToolExecutionStatus({
174
+ status: event.status,
175
+ result: event.result,
176
+ actionRequired: event.action_required,
177
+ error: event.error,
178
+ fallback: "completed"
179
+ });
180
+ }
111
181
  function cloneBlocks(blocks) {
112
182
  return blocks?.map((block) => ({
113
183
  ...block,
@@ -251,7 +321,17 @@ function updateDetachedSnapshotForEvent(streamContext, event) {
251
321
  return true;
252
322
  }
253
323
  case "tool_result": {
254
- const toolCalls = (assistant.toolCalls || []).map((toolCall) => toolCall.id === event.tool_call_id ? { ...toolCall, status: "completed", result: event.result } : toolCall);
324
+ const toolStatus = getToolResultStatus(event);
325
+ const resultContent = formatToolResultContent(event.result, event.error);
326
+ const toolCalls = (assistant.toolCalls || []).map((toolCall) => toolCall.id === event.tool_call_id ? {
327
+ ...toolCall,
328
+ status: toolStatus,
329
+ result: event.result,
330
+ error: toolStatus === "error" ? typeof event.error === "string" && event.error.trim().length > 0 ? event.error : resultContent : void 0,
331
+ actionRequired: event.action_required,
332
+ settingsTab: event.settings_tab,
333
+ settingsSubTab: event.settings_sub_tab
334
+ } : toolCall);
255
335
  const hasRunning = toolCalls.some((toolCall) => toolCall.status === "running");
256
336
  const blocks = cloneBlocks(assistant.blocks) || [];
257
337
  const toolBlockIndex = blocks.findIndex(
@@ -260,17 +340,19 @@ function updateDetachedSnapshotForEvent(streamContext, event) {
260
340
  if (toolBlockIndex !== -1) {
261
341
  blocks[toolBlockIndex] = {
262
342
  ...blocks[toolBlockIndex],
263
- toolStatus: "completed"
343
+ toolStatus
264
344
  };
265
345
  }
266
- blocks.push({
267
- type: "tool_result",
268
- content: typeof event.result === "string" ? event.result : JSON.stringify(event.result),
269
- timestamp: Date.now(),
270
- toolName: toolBlockIndex !== -1 ? blocks[toolBlockIndex].toolName : void 0,
271
- toolCallId: event.tool_call_id,
272
- isIntermediate: true
273
- });
346
+ if (resultContent) {
347
+ blocks.push({
348
+ type: "tool_result",
349
+ content: resultContent,
350
+ timestamp: Date.now(),
351
+ toolName: toolBlockIndex !== -1 ? blocks[toolBlockIndex].toolName : void 0,
352
+ toolCallId: event.tool_call_id,
353
+ isIntermediate: true
354
+ });
355
+ }
274
356
  snapshot[assistantIndex] = {
275
357
  ...assistant,
276
358
  toolCalls,
@@ -992,24 +1074,35 @@ function useChat(options) {
992
1074
  case "tool_result": {
993
1075
  flushTypewriter();
994
1076
  const toolId = event.tool_call_id;
995
- const result = typeof event.result === "string" ? event.result : JSON.stringify(event.result);
1077
+ const toolStatus = getToolResultStatus(event);
1078
+ const resultContent = formatToolResultContent(event.result, event.error);
996
1079
  const blockIdx = currentBlocksRef.current.findIndex((b) => b.type === "tool_call" && b.toolCallId === toolId);
997
1080
  const toolName = blockIdx !== -1 ? currentBlocksRef.current[blockIdx].toolName : void 0;
998
- if (blockIdx !== -1) currentBlocksRef.current[blockIdx].toolStatus = "completed";
999
- currentBlocksRef.current.push({
1000
- type: "tool_result",
1001
- content: result,
1002
- timestamp: Date.now(),
1003
- toolName,
1004
- toolCallId: toolId,
1005
- isIntermediate: true
1006
- });
1081
+ if (blockIdx !== -1) currentBlocksRef.current[blockIdx].toolStatus = toolStatus;
1082
+ if (resultContent) {
1083
+ currentBlocksRef.current.push({
1084
+ type: "tool_result",
1085
+ content: resultContent,
1086
+ timestamp: Date.now(),
1087
+ toolName,
1088
+ toolCallId: toolId,
1089
+ isIntermediate: true
1090
+ });
1091
+ }
1007
1092
  setMessages((prev) => {
1008
1093
  const idx = prev.findIndex((m) => m.id === assistantMessageId);
1009
1094
  if (idx === -1) return prev;
1010
1095
  const msg = prev[idx];
1011
1096
  const updatedCalls = (msg.toolCalls || []).map(
1012
- (t) => t.id === toolId ? { ...t, status: "completed", result: event.result } : t
1097
+ (t) => t.id === toolId ? {
1098
+ ...t,
1099
+ status: toolStatus,
1100
+ result: event.result,
1101
+ error: toolStatus === "error" ? typeof event.error === "string" && event.error.trim().length > 0 ? event.error : resultContent : void 0,
1102
+ actionRequired: event.action_required,
1103
+ settingsTab: event.settings_tab,
1104
+ settingsSubTab: event.settings_sub_tab
1105
+ } : t
1013
1106
  );
1014
1107
  const hasRunning = updatedCalls.some((tc) => tc.status === "running");
1015
1108
  const updated = [...prev];
@@ -6395,12 +6488,25 @@ function parseToolCalls(toolCalls) {
6395
6488
  const rawArgs = fn?.arguments ?? tc.args ?? tc.arguments;
6396
6489
  const args = safeParseArgs(rawArgs);
6397
6490
  const id = tc.id || tc.tool_call_id || tc.call_id;
6491
+ const actionRequired = tc.action_required || tc.actionRequired;
6492
+ const settingsTab = tc.settings_tab || tc.settingsTab;
6493
+ const settingsSubTab = tc.settings_sub_tab || tc.settingsSubTab;
6398
6494
  return {
6399
6495
  id,
6400
6496
  name,
6401
6497
  args,
6402
- status: "completed",
6403
- result: tc.result
6498
+ result: tc.result,
6499
+ error: typeof tc.error === "string" ? tc.error : void 0,
6500
+ actionRequired,
6501
+ settingsTab,
6502
+ settingsSubTab,
6503
+ status: deriveToolExecutionStatus({
6504
+ status: tc.status,
6505
+ result: tc.result,
6506
+ actionRequired,
6507
+ error: tc.error,
6508
+ fallback: "completed"
6509
+ })
6404
6510
  };
6405
6511
  }).filter((tc) => tc.name || tc.id);
6406
6512
  }
@@ -6491,7 +6597,10 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6491
6597
  id: (toolMessage.tool_call_id || void 0) ?? `tool-${idx}`,
6492
6598
  name: "",
6493
6599
  args: void 0,
6494
- status: "completed",
6600
+ status: deriveToolExecutionStatus({
6601
+ result: toolMessage.content,
6602
+ fallback: "completed"
6603
+ }),
6495
6604
  result: toolMessage.content
6496
6605
  })) : void 0;
6497
6606
  const effectiveToolCalls = msgToolCalls && msgToolCalls.length > 0 ? msgToolCalls : fallbackCalls;
@@ -6524,6 +6633,16 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6524
6633
  const toolResultBlocks = [];
6525
6634
  for (const tc of effectiveToolCalls) {
6526
6635
  const toolIndex = effectiveToolCalls.indexOf(tc);
6636
+ const toolResultById = tc.id ? toolMessages.find((tm) => tm.tool_call_id === tc.id) : void 0;
6637
+ const toolResultByIndex = toolMessages[toolIndex];
6638
+ const toolResult = toolResultById || toolResultByIndex;
6639
+ const toolStatus = deriveToolExecutionStatus({
6640
+ status: tc.status,
6641
+ result: tc.result ?? toolResult?.content,
6642
+ actionRequired: tc.actionRequired,
6643
+ error: tc.error,
6644
+ fallback: "completed"
6645
+ });
6527
6646
  blocks.push({
6528
6647
  type: "tool_call",
6529
6648
  content: "",
@@ -6531,12 +6650,9 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6531
6650
  toolName: tc.name,
6532
6651
  toolArgs: tc.args,
6533
6652
  toolCallId: tc.id,
6534
- toolStatus: "completed",
6653
+ toolStatus,
6535
6654
  isIntermediate: true
6536
6655
  });
6537
- const toolResultById = tc.id ? toolMessages.find((tm) => tm.tool_call_id === tc.id) : void 0;
6538
- const toolResultByIndex = toolMessages[toolIndex];
6539
- const toolResult = toolResultById || toolResultByIndex;
6540
6656
  const resultContent = tc.result ?? toolResult?.content;
6541
6657
  const toolResultId = toolResult?.tool_call_id || void 0;
6542
6658
  if (resultContent) {
@@ -6579,6 +6695,13 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6579
6695
  );
6580
6696
  if (matchingToolCall) {
6581
6697
  matchingToolCall.result = toolMsg.content;
6698
+ matchingToolCall.status = deriveToolExecutionStatus({
6699
+ status: matchingToolCall.status,
6700
+ result: toolMsg.content,
6701
+ actionRequired: matchingToolCall.actionRequired,
6702
+ error: matchingToolCall.error,
6703
+ fallback: "completed"
6704
+ });
6582
6705
  }
6583
6706
  }
6584
6707
  const thinkingParts = consecutiveAssistantMsgs.map((m) => m.thinking).filter((t) => t && t.trim());
@@ -7177,9 +7300,20 @@ function processStreamEvents(stream, onEvent, sdk, setCurrentRunId, streamId) {
7177
7300
  args: event.args || {}
7178
7301
  });
7179
7302
  break;
7180
- case "tool_result":
7181
- forward({ type: "tool_result", tool_call_id: event.tool_call_id || "", result: event.result });
7303
+ case "tool_result": {
7304
+ const toolResultEvent = event;
7305
+ forward({
7306
+ type: "tool_result",
7307
+ tool_call_id: toolResultEvent.tool_call_id || "",
7308
+ result: toolResultEvent.result,
7309
+ error: toolResultEvent.error,
7310
+ status: toolResultEvent.status,
7311
+ action_required: toolResultEvent.action_required,
7312
+ settings_tab: toolResultEvent.settings_tab,
7313
+ settings_sub_tab: toolResultEvent.settings_sub_tab
7314
+ });
7182
7315
  break;
7316
+ }
7183
7317
  case "done":
7184
7318
  forward({ type: "done", conversationId: event.conversationId || "", title: event.title });
7185
7319
  currentRunId = null;
@@ -7929,8 +8063,8 @@ function ToolCallItem({ toolCall, toolResult }) {
7929
8063
  style: { color: "var(--chat-text)" },
7930
8064
  children: [
7931
8065
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "font-mono", children: displayName }),
7932
- toolResult && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-green-600 dark:text-green-400", children: TimelineIcons.check }),
7933
- !toolResult && toolCall.toolStatus === "error" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-red-500", children: TimelineIcons.error }),
8066
+ toolCall.toolStatus === "completed" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-green-600 dark:text-green-400", children: TimelineIcons.check }),
8067
+ toolCall.toolStatus === "error" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-red-500", children: TimelineIcons.error }),
7934
8068
  hasArgs && !expanded && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "font-mono", style: { color: "var(--chat-text)", opacity: 0.6, fontSize: "0.75rem" }, children: [
7935
8069
  "(",
7936
8070
  Object.values(toolCall.toolArgs).slice(0, 1).map((v) => typeof v === "string" ? v.length > 18 ? v.slice(0, 18) + "\u2026" : v : "\u2026"),
@@ -7967,7 +8101,8 @@ function StreamingToolCallItem({
7967
8101
  style: { color: "var(--chat-text)" },
7968
8102
  children: [
7969
8103
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "font-mono", children: displayName }),
7970
- toolResult && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-green-600 dark:text-green-400", children: TimelineIcons.check }),
8104
+ toolCall.toolStatus === "completed" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-green-600 dark:text-green-400", children: TimelineIcons.check }),
8105
+ toolCall.toolStatus === "error" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-red-500", children: TimelineIcons.error }),
7971
8106
  isActive && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "ml-0.5 text-amber-500 animate-pulse", children: "\u25C6" }),
7972
8107
  hasArgs && !expanded && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "font-mono", style: { color: "var(--chat-text)", opacity: 0.6, fontSize: "0.75rem" }, children: [
7973
8108
  "(",
@@ -8388,7 +8523,13 @@ function buildFallbackBlocksFromToolCalls(toolCalls) {
8388
8523
  const blocks = [];
8389
8524
  let timestamp = Date.now();
8390
8525
  for (const toolCall of toolCalls) {
8391
- const toolStatus = toolCall.status || (toolCall.result !== void 0 ? "completed" : "running");
8526
+ const toolStatus = deriveToolExecutionStatus({
8527
+ status: toolCall.status,
8528
+ result: toolCall.result,
8529
+ actionRequired: toolCall.actionRequired,
8530
+ error: toolCall.error,
8531
+ fallback: "running"
8532
+ });
8392
8533
  blocks.push({
8393
8534
  type: "tool_call",
8394
8535
  content: "",
@@ -1,5 +1,66 @@
1
1
  // src/renderer/hooks/useChat.ts
2
2
  import { useState, useCallback, useRef, useEffect } from "react";
3
+
4
+ // src/core/tool-status.ts
5
+ function normalizeToolExecutionStatus(status) {
6
+ if (typeof status !== "string") return void 0;
7
+ switch (status.trim().toLowerCase()) {
8
+ case "success":
9
+ case "completed":
10
+ case "ok":
11
+ return "completed";
12
+ case "error":
13
+ case "failed":
14
+ return "error";
15
+ case "cancelled":
16
+ case "canceled":
17
+ return "cancelled";
18
+ case "running":
19
+ return "running";
20
+ case "pending":
21
+ return "pending";
22
+ default:
23
+ return void 0;
24
+ }
25
+ }
26
+ function deriveToolExecutionStatus(options) {
27
+ if (typeof options.actionRequired === "string" && options.actionRequired.length > 0) {
28
+ return "error";
29
+ }
30
+ if (options.error === true) {
31
+ return "error";
32
+ }
33
+ if (typeof options.error === "string" && options.error.trim().length > 0) {
34
+ return "error";
35
+ }
36
+ const normalizedStatus = normalizeToolExecutionStatus(options.status);
37
+ if (normalizedStatus) {
38
+ return normalizedStatus;
39
+ }
40
+ if (options.result !== void 0 && options.result !== null) {
41
+ if (typeof options.result === "string") {
42
+ const normalizedResult = options.result.trimStart();
43
+ if (normalizedResult.startsWith("\u274C") || normalizedResult.startsWith("Error:")) {
44
+ return "error";
45
+ }
46
+ }
47
+ return "completed";
48
+ }
49
+ return options.fallback ?? "running";
50
+ }
51
+ function formatToolResultContent(result, error) {
52
+ const value = result ?? error;
53
+ if (value === void 0 || value === null) return void 0;
54
+ if (typeof value === "string") return value;
55
+ try {
56
+ const serialized = JSON.stringify(value);
57
+ return serialized === void 0 ? String(value) : serialized;
58
+ } catch {
59
+ return String(value);
60
+ }
61
+ }
62
+
63
+ // src/renderer/hooks/useChat.ts
3
64
  var TYPEWRITER_DELAYS = { VERY_FAST: 2, FAST: 5, NORMAL: 10, SLOW: 20 };
4
65
  var TYPEWRITER_THRESHOLDS = { VERY_FAST: 100, FAST: 50, NORMAL: 20 };
5
66
  var MAX_DETACHED_SNAPSHOTS = 30;
@@ -20,6 +81,15 @@ var findLastBlock = (blocks, predicate) => {
20
81
  }
21
82
  return void 0;
22
83
  };
84
+ function getToolResultStatus(event) {
85
+ return deriveToolExecutionStatus({
86
+ status: event.status,
87
+ result: event.result,
88
+ actionRequired: event.action_required,
89
+ error: event.error,
90
+ fallback: "completed"
91
+ });
92
+ }
23
93
  function cloneBlocks(blocks) {
24
94
  return blocks?.map((block) => ({
25
95
  ...block,
@@ -163,7 +233,17 @@ function updateDetachedSnapshotForEvent(streamContext, event) {
163
233
  return true;
164
234
  }
165
235
  case "tool_result": {
166
- const toolCalls = (assistant.toolCalls || []).map((toolCall) => toolCall.id === event.tool_call_id ? { ...toolCall, status: "completed", result: event.result } : toolCall);
236
+ const toolStatus = getToolResultStatus(event);
237
+ const resultContent = formatToolResultContent(event.result, event.error);
238
+ const toolCalls = (assistant.toolCalls || []).map((toolCall) => toolCall.id === event.tool_call_id ? {
239
+ ...toolCall,
240
+ status: toolStatus,
241
+ result: event.result,
242
+ error: toolStatus === "error" ? typeof event.error === "string" && event.error.trim().length > 0 ? event.error : resultContent : void 0,
243
+ actionRequired: event.action_required,
244
+ settingsTab: event.settings_tab,
245
+ settingsSubTab: event.settings_sub_tab
246
+ } : toolCall);
167
247
  const hasRunning = toolCalls.some((toolCall) => toolCall.status === "running");
168
248
  const blocks = cloneBlocks(assistant.blocks) || [];
169
249
  const toolBlockIndex = blocks.findIndex(
@@ -172,17 +252,19 @@ function updateDetachedSnapshotForEvent(streamContext, event) {
172
252
  if (toolBlockIndex !== -1) {
173
253
  blocks[toolBlockIndex] = {
174
254
  ...blocks[toolBlockIndex],
175
- toolStatus: "completed"
255
+ toolStatus
176
256
  };
177
257
  }
178
- blocks.push({
179
- type: "tool_result",
180
- content: typeof event.result === "string" ? event.result : JSON.stringify(event.result),
181
- timestamp: Date.now(),
182
- toolName: toolBlockIndex !== -1 ? blocks[toolBlockIndex].toolName : void 0,
183
- toolCallId: event.tool_call_id,
184
- isIntermediate: true
185
- });
258
+ if (resultContent) {
259
+ blocks.push({
260
+ type: "tool_result",
261
+ content: resultContent,
262
+ timestamp: Date.now(),
263
+ toolName: toolBlockIndex !== -1 ? blocks[toolBlockIndex].toolName : void 0,
264
+ toolCallId: event.tool_call_id,
265
+ isIntermediate: true
266
+ });
267
+ }
186
268
  snapshot[assistantIndex] = {
187
269
  ...assistant,
188
270
  toolCalls,
@@ -904,24 +986,35 @@ function useChat(options) {
904
986
  case "tool_result": {
905
987
  flushTypewriter();
906
988
  const toolId = event.tool_call_id;
907
- const result = typeof event.result === "string" ? event.result : JSON.stringify(event.result);
989
+ const toolStatus = getToolResultStatus(event);
990
+ const resultContent = formatToolResultContent(event.result, event.error);
908
991
  const blockIdx = currentBlocksRef.current.findIndex((b) => b.type === "tool_call" && b.toolCallId === toolId);
909
992
  const toolName = blockIdx !== -1 ? currentBlocksRef.current[blockIdx].toolName : void 0;
910
- if (blockIdx !== -1) currentBlocksRef.current[blockIdx].toolStatus = "completed";
911
- currentBlocksRef.current.push({
912
- type: "tool_result",
913
- content: result,
914
- timestamp: Date.now(),
915
- toolName,
916
- toolCallId: toolId,
917
- isIntermediate: true
918
- });
993
+ if (blockIdx !== -1) currentBlocksRef.current[blockIdx].toolStatus = toolStatus;
994
+ if (resultContent) {
995
+ currentBlocksRef.current.push({
996
+ type: "tool_result",
997
+ content: resultContent,
998
+ timestamp: Date.now(),
999
+ toolName,
1000
+ toolCallId: toolId,
1001
+ isIntermediate: true
1002
+ });
1003
+ }
919
1004
  setMessages((prev) => {
920
1005
  const idx = prev.findIndex((m) => m.id === assistantMessageId);
921
1006
  if (idx === -1) return prev;
922
1007
  const msg = prev[idx];
923
1008
  const updatedCalls = (msg.toolCalls || []).map(
924
- (t) => t.id === toolId ? { ...t, status: "completed", result: event.result } : t
1009
+ (t) => t.id === toolId ? {
1010
+ ...t,
1011
+ status: toolStatus,
1012
+ result: event.result,
1013
+ error: toolStatus === "error" ? typeof event.error === "string" && event.error.trim().length > 0 ? event.error : resultContent : void 0,
1014
+ actionRequired: event.action_required,
1015
+ settingsTab: event.settings_tab,
1016
+ settingsSubTab: event.settings_sub_tab
1017
+ } : t
925
1018
  );
926
1019
  const hasRunning = updatedCalls.some((tc) => tc.status === "running");
927
1020
  const updated = [...prev];
@@ -6307,12 +6400,25 @@ function parseToolCalls(toolCalls) {
6307
6400
  const rawArgs = fn?.arguments ?? tc.args ?? tc.arguments;
6308
6401
  const args = safeParseArgs(rawArgs);
6309
6402
  const id = tc.id || tc.tool_call_id || tc.call_id;
6403
+ const actionRequired = tc.action_required || tc.actionRequired;
6404
+ const settingsTab = tc.settings_tab || tc.settingsTab;
6405
+ const settingsSubTab = tc.settings_sub_tab || tc.settingsSubTab;
6310
6406
  return {
6311
6407
  id,
6312
6408
  name,
6313
6409
  args,
6314
- status: "completed",
6315
- result: tc.result
6410
+ result: tc.result,
6411
+ error: typeof tc.error === "string" ? tc.error : void 0,
6412
+ actionRequired,
6413
+ settingsTab,
6414
+ settingsSubTab,
6415
+ status: deriveToolExecutionStatus({
6416
+ status: tc.status,
6417
+ result: tc.result,
6418
+ actionRequired,
6419
+ error: tc.error,
6420
+ fallback: "completed"
6421
+ })
6316
6422
  };
6317
6423
  }).filter((tc) => tc.name || tc.id);
6318
6424
  }
@@ -6403,7 +6509,10 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6403
6509
  id: (toolMessage.tool_call_id || void 0) ?? `tool-${idx}`,
6404
6510
  name: "",
6405
6511
  args: void 0,
6406
- status: "completed",
6512
+ status: deriveToolExecutionStatus({
6513
+ result: toolMessage.content,
6514
+ fallback: "completed"
6515
+ }),
6407
6516
  result: toolMessage.content
6408
6517
  })) : void 0;
6409
6518
  const effectiveToolCalls = msgToolCalls && msgToolCalls.length > 0 ? msgToolCalls : fallbackCalls;
@@ -6436,6 +6545,16 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6436
6545
  const toolResultBlocks = [];
6437
6546
  for (const tc of effectiveToolCalls) {
6438
6547
  const toolIndex = effectiveToolCalls.indexOf(tc);
6548
+ const toolResultById = tc.id ? toolMessages.find((tm) => tm.tool_call_id === tc.id) : void 0;
6549
+ const toolResultByIndex = toolMessages[toolIndex];
6550
+ const toolResult = toolResultById || toolResultByIndex;
6551
+ const toolStatus = deriveToolExecutionStatus({
6552
+ status: tc.status,
6553
+ result: tc.result ?? toolResult?.content,
6554
+ actionRequired: tc.actionRequired,
6555
+ error: tc.error,
6556
+ fallback: "completed"
6557
+ });
6439
6558
  blocks.push({
6440
6559
  type: "tool_call",
6441
6560
  content: "",
@@ -6443,12 +6562,9 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6443
6562
  toolName: tc.name,
6444
6563
  toolArgs: tc.args,
6445
6564
  toolCallId: tc.id,
6446
- toolStatus: "completed",
6565
+ toolStatus,
6447
6566
  isIntermediate: true
6448
6567
  });
6449
- const toolResultById = tc.id ? toolMessages.find((tm) => tm.tool_call_id === tc.id) : void 0;
6450
- const toolResultByIndex = toolMessages[toolIndex];
6451
- const toolResult = toolResultById || toolResultByIndex;
6452
6568
  const resultContent = tc.result ?? toolResult?.content;
6453
6569
  const toolResultId = toolResult?.tool_call_id || void 0;
6454
6570
  if (resultContent) {
@@ -6491,6 +6607,13 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6491
6607
  );
6492
6608
  if (matchingToolCall) {
6493
6609
  matchingToolCall.result = toolMsg.content;
6610
+ matchingToolCall.status = deriveToolExecutionStatus({
6611
+ status: matchingToolCall.status,
6612
+ result: toolMsg.content,
6613
+ actionRequired: matchingToolCall.actionRequired,
6614
+ error: matchingToolCall.error,
6615
+ fallback: "completed"
6616
+ });
6494
6617
  }
6495
6618
  }
6496
6619
  const thinkingParts = consecutiveAssistantMsgs.map((m) => m.thinking).filter((t) => t && t.trim());
@@ -7089,9 +7212,20 @@ function processStreamEvents(stream, onEvent, sdk, setCurrentRunId, streamId) {
7089
7212
  args: event.args || {}
7090
7213
  });
7091
7214
  break;
7092
- case "tool_result":
7093
- forward({ type: "tool_result", tool_call_id: event.tool_call_id || "", result: event.result });
7215
+ case "tool_result": {
7216
+ const toolResultEvent = event;
7217
+ forward({
7218
+ type: "tool_result",
7219
+ tool_call_id: toolResultEvent.tool_call_id || "",
7220
+ result: toolResultEvent.result,
7221
+ error: toolResultEvent.error,
7222
+ status: toolResultEvent.status,
7223
+ action_required: toolResultEvent.action_required,
7224
+ settings_tab: toolResultEvent.settings_tab,
7225
+ settings_sub_tab: toolResultEvent.settings_sub_tab
7226
+ });
7094
7227
  break;
7228
+ }
7095
7229
  case "done":
7096
7230
  forward({ type: "done", conversationId: event.conversationId || "", title: event.title });
7097
7231
  currentRunId = null;
@@ -7849,8 +7983,8 @@ function ToolCallItem({ toolCall, toolResult }) {
7849
7983
  style: { color: "var(--chat-text)" },
7850
7984
  children: [
7851
7985
  /* @__PURE__ */ jsx8("span", { className: "font-mono", children: displayName }),
7852
- toolResult && /* @__PURE__ */ jsx8("span", { className: "text-green-600 dark:text-green-400", children: TimelineIcons.check }),
7853
- !toolResult && toolCall.toolStatus === "error" && /* @__PURE__ */ jsx8("span", { className: "text-red-500", children: TimelineIcons.error }),
7986
+ toolCall.toolStatus === "completed" && /* @__PURE__ */ jsx8("span", { className: "text-green-600 dark:text-green-400", children: TimelineIcons.check }),
7987
+ toolCall.toolStatus === "error" && /* @__PURE__ */ jsx8("span", { className: "text-red-500", children: TimelineIcons.error }),
7854
7988
  hasArgs && !expanded && /* @__PURE__ */ jsxs3("span", { className: "font-mono", style: { color: "var(--chat-text)", opacity: 0.6, fontSize: "0.75rem" }, children: [
7855
7989
  "(",
7856
7990
  Object.values(toolCall.toolArgs).slice(0, 1).map((v) => typeof v === "string" ? v.length > 18 ? v.slice(0, 18) + "\u2026" : v : "\u2026"),
@@ -7887,7 +8021,8 @@ function StreamingToolCallItem({
7887
8021
  style: { color: "var(--chat-text)" },
7888
8022
  children: [
7889
8023
  /* @__PURE__ */ jsx8("span", { className: "font-mono", children: displayName }),
7890
- toolResult && /* @__PURE__ */ jsx8("span", { className: "text-green-600 dark:text-green-400", children: TimelineIcons.check }),
8024
+ toolCall.toolStatus === "completed" && /* @__PURE__ */ jsx8("span", { className: "text-green-600 dark:text-green-400", children: TimelineIcons.check }),
8025
+ toolCall.toolStatus === "error" && /* @__PURE__ */ jsx8("span", { className: "text-red-500", children: TimelineIcons.error }),
7891
8026
  isActive && /* @__PURE__ */ jsx8("span", { className: "ml-0.5 text-amber-500 animate-pulse", children: "\u25C6" }),
7892
8027
  hasArgs && !expanded && /* @__PURE__ */ jsxs3("span", { className: "font-mono", style: { color: "var(--chat-text)", opacity: 0.6, fontSize: "0.75rem" }, children: [
7893
8028
  "(",
@@ -8308,7 +8443,13 @@ function buildFallbackBlocksFromToolCalls(toolCalls) {
8308
8443
  const blocks = [];
8309
8444
  let timestamp = Date.now();
8310
8445
  for (const toolCall of toolCalls) {
8311
- const toolStatus = toolCall.status || (toolCall.result !== void 0 ? "completed" : "running");
8446
+ const toolStatus = deriveToolExecutionStatus({
8447
+ status: toolCall.status,
8448
+ result: toolCall.result,
8449
+ actionRequired: toolCall.actionRequired,
8450
+ error: toolCall.error,
8451
+ fallback: "running"
8452
+ });
8312
8453
  blocks.push({
8313
8454
  type: "tool_call",
8314
8455
  content: "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yushaw/sanqian-chat",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Floating chat window SDK for Sanqian AI Assistant",
5
5
  "main": "./dist/main/index.js",
6
6
  "types": "./dist/main/index.d.ts",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@streamdown/code": "^1.0.3",
48
- "@yushaw/sanqian-sdk": "^0.3.26",
48
+ "@yushaw/sanqian-sdk": "^0.3.28",
49
49
  "react-virtuoso": "^4.15.0",
50
50
  "rehype-harden": "^1.1.6",
51
51
  "remark-gfm": "^4.0.1",