@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.
@@ -1,5 +1,67 @@
1
1
  // src/renderer/hooks/useChat.ts
2
2
  import { useState, useCallback, useRef, useEffect } from "react";
3
+
4
+ // src/core/tool-status.ts
5
+ var BLOCKING_ACTION_REQUIRED = /* @__PURE__ */ new Set(["install_git_bash"]);
6
+ function normalizeToolExecutionStatus(status) {
7
+ if (typeof status !== "string") return void 0;
8
+ switch (status.trim().toLowerCase()) {
9
+ case "success":
10
+ case "completed":
11
+ case "ok":
12
+ return "completed";
13
+ case "error":
14
+ case "failed":
15
+ return "error";
16
+ case "cancelled":
17
+ case "canceled":
18
+ return "cancelled";
19
+ case "running":
20
+ return "running";
21
+ case "pending":
22
+ return "pending";
23
+ default:
24
+ return void 0;
25
+ }
26
+ }
27
+ function deriveToolExecutionStatus(options) {
28
+ if (typeof options.actionRequired === "string" && BLOCKING_ACTION_REQUIRED.has(options.actionRequired)) {
29
+ return "error";
30
+ }
31
+ if (options.error === true) {
32
+ return "error";
33
+ }
34
+ if (typeof options.error === "string" && options.error.trim().length > 0) {
35
+ return "error";
36
+ }
37
+ const normalizedStatus = normalizeToolExecutionStatus(options.status);
38
+ if (normalizedStatus) {
39
+ return normalizedStatus;
40
+ }
41
+ if (options.result !== void 0 && options.result !== null) {
42
+ if (typeof options.result === "string") {
43
+ const normalizedResult = options.result.trimStart();
44
+ if (normalizedResult.startsWith("\u274C") || normalizedResult.startsWith("Error:")) {
45
+ return "error";
46
+ }
47
+ }
48
+ return "completed";
49
+ }
50
+ return options.fallback ?? "running";
51
+ }
52
+ function formatToolResultContent(result, error) {
53
+ const value = result ?? error;
54
+ if (value === void 0 || value === null) return void 0;
55
+ if (typeof value === "string") return value;
56
+ try {
57
+ const serialized = JSON.stringify(value);
58
+ return serialized === void 0 ? String(value) : serialized;
59
+ } catch {
60
+ return String(value);
61
+ }
62
+ }
63
+
64
+ // src/renderer/hooks/useChat.ts
3
65
  var TYPEWRITER_DELAYS = { VERY_FAST: 2, FAST: 5, NORMAL: 10, SLOW: 20 };
4
66
  var TYPEWRITER_THRESHOLDS = { VERY_FAST: 100, FAST: 50, NORMAL: 20 };
5
67
  var MAX_DETACHED_SNAPSHOTS = 30;
@@ -20,6 +82,36 @@ var findLastBlock = (blocks, predicate) => {
20
82
  }
21
83
  return void 0;
22
84
  };
85
+ function getToolResultStatus(event) {
86
+ return deriveToolExecutionStatus({
87
+ status: event.status,
88
+ result: event.result,
89
+ actionRequired: event.action_required,
90
+ error: event.error,
91
+ fallback: "completed"
92
+ });
93
+ }
94
+ function readToolExecutionNumber(value) {
95
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
96
+ }
97
+ function readToolExecutionBoolean(value) {
98
+ return typeof value === "boolean" ? value : void 0;
99
+ }
100
+ function readToolExecutionString(value) {
101
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
102
+ }
103
+ function getToolExecutionMetadata(event) {
104
+ return {
105
+ commandExitCode: readToolExecutionNumber(event.command_exit_code),
106
+ durationMs: readToolExecutionNumber(event.duration_ms),
107
+ sandboxed: readToolExecutionBoolean(event.sandboxed),
108
+ timedOut: readToolExecutionBoolean(event.timed_out),
109
+ truncated: readToolExecutionBoolean(event.truncated),
110
+ stdoutPath: readToolExecutionString(event.stdout_path),
111
+ stderrPath: readToolExecutionString(event.stderr_path),
112
+ presentation: readToolExecutionString(event.presentation)
113
+ };
114
+ }
23
115
  function cloneBlocks(blocks) {
24
116
  return blocks?.map((block) => ({
25
117
  ...block,
@@ -163,7 +255,19 @@ function updateDetachedSnapshotForEvent(streamContext, event) {
163
255
  return true;
164
256
  }
165
257
  case "tool_result": {
166
- const toolCalls = (assistant.toolCalls || []).map((toolCall) => toolCall.id === event.tool_call_id ? { ...toolCall, status: "completed", result: event.result } : toolCall);
258
+ const toolStatus = getToolResultStatus(event);
259
+ const resultContent = formatToolResultContent(event.result, event.error);
260
+ const toolExecutionMetadata = getToolExecutionMetadata(event);
261
+ const toolCalls = (assistant.toolCalls || []).map((toolCall) => toolCall.id === event.tool_call_id ? {
262
+ ...toolCall,
263
+ status: toolStatus,
264
+ result: event.result,
265
+ error: toolStatus === "error" ? typeof event.error === "string" && event.error.trim().length > 0 ? event.error : resultContent : void 0,
266
+ actionRequired: event.action_required,
267
+ settingsTab: event.settings_tab,
268
+ settingsSubTab: event.settings_sub_tab,
269
+ ...toolExecutionMetadata
270
+ } : toolCall);
167
271
  const hasRunning = toolCalls.some((toolCall) => toolCall.status === "running");
168
272
  const blocks = cloneBlocks(assistant.blocks) || [];
169
273
  const toolBlockIndex = blocks.findIndex(
@@ -172,17 +276,23 @@ function updateDetachedSnapshotForEvent(streamContext, event) {
172
276
  if (toolBlockIndex !== -1) {
173
277
  blocks[toolBlockIndex] = {
174
278
  ...blocks[toolBlockIndex],
175
- toolStatus: "completed"
279
+ toolStatus,
280
+ actionRequired: event.action_required,
281
+ ...toolExecutionMetadata
176
282
  };
177
283
  }
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
- });
284
+ if (resultContent) {
285
+ blocks.push({
286
+ type: "tool_result",
287
+ content: resultContent,
288
+ timestamp: Date.now(),
289
+ toolName: toolBlockIndex !== -1 ? blocks[toolBlockIndex].toolName : void 0,
290
+ toolCallId: event.tool_call_id,
291
+ actionRequired: event.action_required,
292
+ isIntermediate: true,
293
+ ...toolExecutionMetadata
294
+ });
295
+ }
186
296
  snapshot[assistantIndex] = {
187
297
  ...assistant,
188
298
  toolCalls,
@@ -305,16 +415,18 @@ function useChat(options) {
305
415
  }, [conversationId]);
306
416
  useEffect(() => {
307
417
  isMountedRef.current = true;
418
+ const streamContexts = streamContextsRef.current;
419
+ const conversationStreamTokens = conversationStreamTokensRef.current;
308
420
  return () => {
309
421
  isMountedRef.current = false;
310
422
  cancelRef.current?.();
311
- streamContextsRef.current.forEach((context) => {
423
+ streamContexts.forEach((context) => {
312
424
  context.suppressed = true;
313
425
  context.terminal = true;
314
426
  context.cancel?.();
315
427
  });
316
- streamContextsRef.current.clear();
317
- conversationStreamTokensRef.current.clear();
428
+ streamContexts.clear();
429
+ conversationStreamTokens.clear();
318
430
  if (typewriterIntervalRef.current) clearTimeout(typewriterIntervalRef.current);
319
431
  };
320
432
  }, []);
@@ -904,24 +1016,46 @@ function useChat(options) {
904
1016
  case "tool_result": {
905
1017
  flushTypewriter();
906
1018
  const toolId = event.tool_call_id;
907
- const result = typeof event.result === "string" ? event.result : JSON.stringify(event.result);
1019
+ const toolStatus = getToolResultStatus(event);
1020
+ const resultContent = formatToolResultContent(event.result, event.error);
1021
+ const toolExecutionMetadata = getToolExecutionMetadata(event);
908
1022
  const blockIdx = currentBlocksRef.current.findIndex((b) => b.type === "tool_call" && b.toolCallId === toolId);
909
1023
  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
- });
1024
+ if (blockIdx !== -1) {
1025
+ currentBlocksRef.current[blockIdx] = {
1026
+ ...currentBlocksRef.current[blockIdx],
1027
+ toolStatus,
1028
+ actionRequired: event.action_required,
1029
+ ...toolExecutionMetadata
1030
+ };
1031
+ }
1032
+ if (resultContent) {
1033
+ currentBlocksRef.current.push({
1034
+ type: "tool_result",
1035
+ content: resultContent,
1036
+ timestamp: Date.now(),
1037
+ toolName,
1038
+ toolCallId: toolId,
1039
+ actionRequired: event.action_required,
1040
+ isIntermediate: true,
1041
+ ...toolExecutionMetadata
1042
+ });
1043
+ }
919
1044
  setMessages((prev) => {
920
1045
  const idx = prev.findIndex((m) => m.id === assistantMessageId);
921
1046
  if (idx === -1) return prev;
922
1047
  const msg = prev[idx];
923
1048
  const updatedCalls = (msg.toolCalls || []).map(
924
- (t) => t.id === toolId ? { ...t, status: "completed", result: event.result } : t
1049
+ (t) => t.id === toolId ? {
1050
+ ...t,
1051
+ status: toolStatus,
1052
+ result: event.result,
1053
+ error: toolStatus === "error" ? typeof event.error === "string" && event.error.trim().length > 0 ? event.error : resultContent : void 0,
1054
+ actionRequired: event.action_required,
1055
+ settingsTab: event.settings_tab,
1056
+ settingsSubTab: event.settings_sub_tab,
1057
+ ...toolExecutionMetadata
1058
+ } : t
925
1059
  );
926
1060
  const hasRunning = updatedCalls.some((tc) => tc.status === "running");
927
1061
  const updated = [...prev];
@@ -1831,7 +1965,9 @@ function useConnection(options) {
1831
1965
  const [errorCode, setErrorCode] = useState4();
1832
1966
  const isMountedRef = useRef2(true);
1833
1967
  const onStatusChangeRef = useRef2(onStatusChange);
1834
- onStatusChangeRef.current = onStatusChange;
1968
+ useEffect3(() => {
1969
+ onStatusChangeRef.current = onStatusChange;
1970
+ }, [onStatusChange]);
1835
1971
  const reconnectAttemptsRef = useRef2(0);
1836
1972
  useEffect3(() => {
1837
1973
  isMountedRef.current = true;
@@ -6278,6 +6414,27 @@ function useWindowDragLock(active) {
6278
6414
  }
6279
6415
 
6280
6416
  // src/core/history.ts
6417
+ function readToolExecutionNumber2(value) {
6418
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
6419
+ }
6420
+ function readToolExecutionBoolean2(value) {
6421
+ return typeof value === "boolean" ? value : void 0;
6422
+ }
6423
+ function readToolExecutionString2(value) {
6424
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
6425
+ }
6426
+ function readToolExecutionMetadata(tc) {
6427
+ return {
6428
+ commandExitCode: readToolExecutionNumber2(tc.command_exit_code ?? tc.commandExitCode),
6429
+ durationMs: readToolExecutionNumber2(tc.duration_ms ?? tc.durationMs),
6430
+ sandboxed: readToolExecutionBoolean2(tc.sandboxed),
6431
+ timedOut: readToolExecutionBoolean2(tc.timed_out ?? tc.timedOut),
6432
+ truncated: readToolExecutionBoolean2(tc.truncated),
6433
+ stdoutPath: readToolExecutionString2(tc.stdout_path ?? tc.stdoutPath),
6434
+ stderrPath: readToolExecutionString2(tc.stderr_path ?? tc.stderrPath),
6435
+ presentation: readToolExecutionString2(tc.presentation)
6436
+ };
6437
+ }
6281
6438
  function safeParseArgs(value) {
6282
6439
  if (!value) return void 0;
6283
6440
  if (typeof value === "object") return value;
@@ -6307,12 +6464,26 @@ function parseToolCalls(toolCalls) {
6307
6464
  const rawArgs = fn?.arguments ?? tc.args ?? tc.arguments;
6308
6465
  const args = safeParseArgs(rawArgs);
6309
6466
  const id = tc.id || tc.tool_call_id || tc.call_id;
6467
+ const actionRequired = tc.action_required || tc.actionRequired;
6468
+ const settingsTab = tc.settings_tab || tc.settingsTab;
6469
+ const settingsSubTab = tc.settings_sub_tab || tc.settingsSubTab;
6310
6470
  return {
6311
6471
  id,
6312
6472
  name,
6313
6473
  args,
6314
- status: "completed",
6315
- result: tc.result
6474
+ result: tc.result,
6475
+ error: typeof tc.error === "string" ? tc.error : void 0,
6476
+ actionRequired,
6477
+ settingsTab,
6478
+ settingsSubTab,
6479
+ status: deriveToolExecutionStatus({
6480
+ status: tc.status,
6481
+ result: tc.result,
6482
+ actionRequired,
6483
+ error: tc.error,
6484
+ fallback: "completed"
6485
+ }),
6486
+ ...readToolExecutionMetadata(tc)
6316
6487
  };
6317
6488
  }).filter((tc) => tc.name || tc.id);
6318
6489
  }
@@ -6380,6 +6551,10 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6380
6551
  const consecutiveAssistantMsgs = [msg];
6381
6552
  const toolMessages = [];
6382
6553
  let j = i + 1;
6554
+ const hasDeclaredToolCalls = (message) => {
6555
+ const calls = parseToolCalls(message.toolCalls || message.tool_calls);
6556
+ return Boolean(calls && calls.length > 0);
6557
+ };
6383
6558
  while (j < rawMessages.length) {
6384
6559
  if (rawMessages[j].role === "assistant") {
6385
6560
  consecutiveAssistantMsgs.push(rawMessages[j]);
@@ -6395,15 +6570,19 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6395
6570
  let blockTime = Date.now();
6396
6571
  let fallbackToolCalls;
6397
6572
  let lastEffectiveToolCalls;
6573
+ const groupHasDeclaredToolCalls = consecutiveAssistantMsgs.some(hasDeclaredToolCalls);
6398
6574
  for (let k = 0; k < consecutiveAssistantMsgs.length; k++) {
6399
6575
  const assistantMsg = consecutiveAssistantMsgs[k];
6400
6576
  const msgToolCalls = parseToolCalls(assistantMsg.toolCalls || assistantMsg.tool_calls);
6401
6577
  const isLastAssistant = k === consecutiveAssistantMsgs.length - 1;
6402
- const fallbackCalls = isLastAssistant && toolMessages.length > 0 ? toolMessages.map((toolMessage, idx) => ({
6578
+ const fallbackCalls = !groupHasDeclaredToolCalls && isLastAssistant && toolMessages.length > 0 ? toolMessages.map((toolMessage, idx) => ({
6403
6579
  id: (toolMessage.tool_call_id || void 0) ?? `tool-${idx}`,
6404
6580
  name: "",
6405
6581
  args: void 0,
6406
- status: "completed",
6582
+ status: deriveToolExecutionStatus({
6583
+ result: toolMessage.content,
6584
+ fallback: "completed"
6585
+ }),
6407
6586
  result: toolMessage.content
6408
6587
  })) : void 0;
6409
6588
  const effectiveToolCalls = msgToolCalls && msgToolCalls.length > 0 ? msgToolCalls : fallbackCalls;
@@ -6436,6 +6615,16 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6436
6615
  const toolResultBlocks = [];
6437
6616
  for (const tc of effectiveToolCalls) {
6438
6617
  const toolIndex = effectiveToolCalls.indexOf(tc);
6618
+ const toolResultById = tc.id ? toolMessages.find((tm) => tm.tool_call_id === tc.id) : void 0;
6619
+ const toolResultByIndex = toolMessages[toolIndex];
6620
+ const toolResult = toolResultById || toolResultByIndex;
6621
+ const toolStatus = deriveToolExecutionStatus({
6622
+ status: tc.status,
6623
+ result: tc.result ?? toolResult?.content,
6624
+ actionRequired: tc.actionRequired,
6625
+ error: tc.error,
6626
+ fallback: "completed"
6627
+ });
6439
6628
  blocks.push({
6440
6629
  type: "tool_call",
6441
6630
  content: "",
@@ -6443,12 +6632,18 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6443
6632
  toolName: tc.name,
6444
6633
  toolArgs: tc.args,
6445
6634
  toolCallId: tc.id,
6446
- toolStatus: "completed",
6447
- isIntermediate: true
6635
+ toolStatus,
6636
+ actionRequired: tc.actionRequired,
6637
+ isIntermediate: true,
6638
+ commandExitCode: tc.commandExitCode,
6639
+ durationMs: tc.durationMs,
6640
+ sandboxed: tc.sandboxed,
6641
+ timedOut: tc.timedOut,
6642
+ truncated: tc.truncated,
6643
+ stdoutPath: tc.stdoutPath,
6644
+ stderrPath: tc.stderrPath,
6645
+ presentation: tc.presentation
6448
6646
  });
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
6647
  const resultContent = tc.result ?? toolResult?.content;
6453
6648
  const toolResultId = toolResult?.tool_call_id || void 0;
6454
6649
  if (resultContent) {
@@ -6458,7 +6653,16 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6458
6653
  timestamp: blockTime++,
6459
6654
  toolName: tc.name,
6460
6655
  toolCallId: tc.id || toolResultId,
6461
- isIntermediate: true
6656
+ actionRequired: tc.actionRequired,
6657
+ isIntermediate: true,
6658
+ commandExitCode: tc.commandExitCode,
6659
+ durationMs: tc.durationMs,
6660
+ sandboxed: tc.sandboxed,
6661
+ timedOut: tc.timedOut,
6662
+ truncated: tc.truncated,
6663
+ stdoutPath: tc.stdoutPath,
6664
+ stderrPath: tc.stderrPath,
6665
+ presentation: tc.presentation
6462
6666
  });
6463
6667
  tc.result = resultContent;
6464
6668
  }
@@ -6491,6 +6695,13 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6491
6695
  );
6492
6696
  if (matchingToolCall) {
6493
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
+ });
6494
6705
  }
6495
6706
  }
6496
6707
  const thinkingParts = consecutiveAssistantMsgs.map((m) => m.thinking).filter((t) => t && t.trim());
@@ -7089,9 +7300,28 @@ function processStreamEvents(stream, onEvent, sdk, setCurrentRunId, streamId) {
7089
7300
  args: event.args || {}
7090
7301
  });
7091
7302
  break;
7092
- case "tool_result":
7093
- 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
+ command_exit_code: toolResultEvent.command_exit_code,
7315
+ duration_ms: toolResultEvent.duration_ms,
7316
+ sandboxed: toolResultEvent.sandboxed,
7317
+ timed_out: toolResultEvent.timed_out,
7318
+ truncated: toolResultEvent.truncated,
7319
+ stdout_path: toolResultEvent.stdout_path,
7320
+ stderr_path: toolResultEvent.stderr_path,
7321
+ presentation: toolResultEvent.presentation
7322
+ });
7094
7323
  break;
7324
+ }
7095
7325
  case "done":
7096
7326
  forward({ type: "done", conversationId: event.conversationId || "", title: event.title });
7097
7327
  currentRunId = null;
@@ -7352,7 +7582,7 @@ var MessageBubble = memo2(function MessageBubble2({
7352
7582
  });
7353
7583
 
7354
7584
  // src/renderer/components/SanqianChat.tsx
7355
- import { memo as memo13, useCallback as useCallback18, useEffect as useEffect21, useMemo as useMemo8, useRef as useRef13 } from "react";
7585
+ import { memo as memo13, useCallback as useCallback17, useEffect as useEffect21, useMemo as useMemo8, useRef as useRef13 } from "react";
7356
7586
 
7357
7587
  // src/renderer/utils/chatConfig.ts
7358
7588
  import { useEffect as useEffect13, useMemo as useMemo3, useState as useState10 } from "react";
@@ -7849,8 +8079,8 @@ function ToolCallItem({ toolCall, toolResult }) {
7849
8079
  style: { color: "var(--chat-text)" },
7850
8080
  children: [
7851
8081
  /* @__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 }),
8082
+ toolCall.toolStatus === "completed" && /* @__PURE__ */ jsx8("span", { className: "text-green-600 dark:text-green-400", children: TimelineIcons.check }),
8083
+ toolCall.toolStatus === "error" && /* @__PURE__ */ jsx8("span", { className: "text-red-500", children: TimelineIcons.error }),
7854
8084
  hasArgs && !expanded && /* @__PURE__ */ jsxs3("span", { className: "font-mono", style: { color: "var(--chat-text)", opacity: 0.6, fontSize: "0.75rem" }, children: [
7855
8085
  "(",
7856
8086
  Object.values(toolCall.toolArgs).slice(0, 1).map((v) => typeof v === "string" ? v.length > 18 ? v.slice(0, 18) + "\u2026" : v : "\u2026"),
@@ -7887,7 +8117,8 @@ function StreamingToolCallItem({
7887
8117
  style: { color: "var(--chat-text)" },
7888
8118
  children: [
7889
8119
  /* @__PURE__ */ jsx8("span", { className: "font-mono", children: displayName }),
7890
- toolResult && /* @__PURE__ */ jsx8("span", { className: "text-green-600 dark:text-green-400", children: TimelineIcons.check }),
8120
+ toolCall.toolStatus === "completed" && /* @__PURE__ */ jsx8("span", { className: "text-green-600 dark:text-green-400", children: TimelineIcons.check }),
8121
+ toolCall.toolStatus === "error" && /* @__PURE__ */ jsx8("span", { className: "text-red-500", children: TimelineIcons.error }),
7891
8122
  isActive && /* @__PURE__ */ jsx8("span", { className: "ml-0.5 text-amber-500 animate-pulse", children: "\u25C6" }),
7892
8123
  hasArgs && !expanded && /* @__PURE__ */ jsxs3("span", { className: "font-mono", style: { color: "var(--chat-text)", opacity: 0.6, fontSize: "0.75rem" }, children: [
7893
8124
  "(",
@@ -8308,7 +8539,13 @@ function buildFallbackBlocksFromToolCalls(toolCalls) {
8308
8539
  const blocks = [];
8309
8540
  let timestamp = Date.now();
8310
8541
  for (const toolCall of toolCalls) {
8311
- const toolStatus = toolCall.status || (toolCall.result !== void 0 ? "completed" : "running");
8542
+ const toolStatus = deriveToolExecutionStatus({
8543
+ status: toolCall.status,
8544
+ result: toolCall.result,
8545
+ actionRequired: toolCall.actionRequired,
8546
+ error: toolCall.error,
8547
+ fallback: "running"
8548
+ });
8312
8549
  blocks.push({
8313
8550
  type: "tool_call",
8314
8551
  content: "",
@@ -8317,6 +8554,7 @@ function buildFallbackBlocksFromToolCalls(toolCalls) {
8317
8554
  toolArgs: toolCall.args,
8318
8555
  toolCallId: toolCall.id,
8319
8556
  toolStatus,
8557
+ actionRequired: toolCall.actionRequired,
8320
8558
  isIntermediate: true
8321
8559
  });
8322
8560
  const resultContent = normalizeToolResult(toolCall.result);
@@ -8327,6 +8565,7 @@ function buildFallbackBlocksFromToolCalls(toolCalls) {
8327
8565
  timestamp: timestamp++,
8328
8566
  toolName: toolCall.name,
8329
8567
  toolCallId: toolCall.id,
8568
+ actionRequired: toolCall.actionRequired,
8330
8569
  isIntermediate: true
8331
8570
  });
8332
8571
  }
@@ -8855,9 +9094,13 @@ var HitlCard = memo8(function HitlCard2({
8855
9094
  const riskLevel = interrupt.risk_level || "medium";
8856
9095
  const riskStyle = riskColors[riskLevel];
8857
9096
  const onSubmitRef = useRef10(onSubmit);
8858
- onSubmitRef.current = onSubmit;
8859
9097
  const onCancelRef = useRef10(onCancel);
8860
- onCancelRef.current = onCancel;
9098
+ useEffect18(() => {
9099
+ onSubmitRef.current = onSubmit;
9100
+ }, [onSubmit]);
9101
+ useEffect18(() => {
9102
+ onCancelRef.current = onCancel;
9103
+ }, [onCancel]);
8861
9104
  useEffect18(() => {
8862
9105
  if (isUserInput) {
8863
9106
  if (!interrupt.options || interrupt.options.length === 0) {
@@ -9219,10 +9462,10 @@ var AlertBanner = memo9(function AlertBanner2({
9219
9462
  });
9220
9463
 
9221
9464
  // src/renderer/components/AddResourceButton.tsx
9222
- import { memo as memo11, useState as useState18, useRef as useRef12, useEffect as useEffect20, useCallback as useCallback17 } from "react";
9465
+ import { memo as memo11, useState as useState18, useRef as useRef12, useEffect as useEffect20, useCallback as useCallback16 } from "react";
9223
9466
 
9224
9467
  // src/renderer/components/ResourcePicker.tsx
9225
- import { memo as memo10, useState as useState17, useCallback as useCallback16, useRef as useRef11, useEffect as useEffect19 } from "react";
9468
+ import { memo as memo10, useState as useState17, useCallback as useCallback15, useRef as useRef11, useEffect as useEffect19 } from "react";
9226
9469
  import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
9227
9470
  var STRINGS = {
9228
9471
  en: {
@@ -9323,7 +9566,7 @@ var ResourcePicker = memo10(function ResourcePicker2({
9323
9566
  }
9324
9567
  scrollRestoredRef.current = selectedProviderId;
9325
9568
  }, [isOpen, selectedProviderId, resources.length]);
9326
- const handleListScroll = useCallback16((e) => {
9569
+ const handleListScroll = useCallback15((e) => {
9327
9570
  if (!selectedProviderId) return;
9328
9571
  const scrollTop = e.currentTarget.scrollTop;
9329
9572
  sessionStorage.setItem(getScrollKey(selectedProviderId), String(scrollTop));
@@ -9352,13 +9595,13 @@ var ResourcePicker = memo10(function ResourcePicker2({
9352
9595
  observer.observe(sentinelRef.current);
9353
9596
  return () => observer.disconnect();
9354
9597
  }, [hasMore, isLoadingResources, onLoadMore]);
9355
- const handleTabClick = useCallback16((providerId) => {
9598
+ const handleTabClick = useCallback15((providerId) => {
9356
9599
  if (providerId !== selectedProviderId) {
9357
9600
  onSearchChange("");
9358
9601
  onSelectProvider(providerId);
9359
9602
  }
9360
9603
  }, [selectedProviderId, onSelectProvider, onSearchChange]);
9361
- const handleResourceClick = useCallback16((item) => {
9604
+ const handleResourceClick = useCallback15((item) => {
9362
9605
  onAttachResource(item);
9363
9606
  }, [onAttachResource]);
9364
9607
  if (!isOpen) return null;
@@ -9664,7 +9907,7 @@ var AddResourceButton = memo11(function AddResourceButton2({
9664
9907
  document.addEventListener("keydown", handleKeyDown);
9665
9908
  return () => document.removeEventListener("keydown", handleKeyDown);
9666
9909
  }, [isMenuOpen]);
9667
- const handleButtonClick = useCallback17(() => {
9910
+ const handleButtonClick = useCallback16(() => {
9668
9911
  if (isPickerOpen) {
9669
9912
  setIsPickerOpen(false);
9670
9913
  onSelectProvider(null);
@@ -9678,31 +9921,31 @@ var AddResourceButton = memo11(function AddResourceButton2({
9678
9921
  }
9679
9922
  setIsMenuOpen(!isMenuOpen);
9680
9923
  }, [isMenuOpen, isPickerOpen, onSelectProvider, providers, onUploadFile, onUploadImage]);
9681
- const handleUploadFile = useCallback17(() => {
9924
+ const handleUploadFile = useCallback16(() => {
9682
9925
  setIsMenuOpen(false);
9683
9926
  onUploadFile?.();
9684
9927
  }, [onUploadFile]);
9685
- const handleUploadImage = useCallback17(() => {
9928
+ const handleUploadImage = useCallback16(() => {
9686
9929
  setIsMenuOpen(false);
9687
9930
  onUploadImage?.();
9688
9931
  }, [onUploadImage]);
9689
- const handleProviderClick = useCallback17((providerId) => {
9932
+ const handleProviderClick = useCallback16((providerId) => {
9690
9933
  setIsMenuOpen(false);
9691
9934
  onSelectProvider(providerId);
9692
9935
  setIsPickerOpen(true);
9693
9936
  }, [onSelectProvider]);
9694
- const handlePickerClose = useCallback17(() => {
9937
+ const handlePickerClose = useCallback16(() => {
9695
9938
  setIsPickerOpen(false);
9696
9939
  onSelectProvider(null);
9697
9940
  }, [onSelectProvider]);
9698
- const handlePickerSelectProvider = useCallback17((providerId) => {
9941
+ const handlePickerSelectProvider = useCallback16((providerId) => {
9699
9942
  if (providerId === null) {
9700
9943
  setIsPickerOpen(false);
9701
9944
  setIsMenuOpen(true);
9702
9945
  }
9703
9946
  onSelectProvider(providerId);
9704
9947
  }, [onSelectProvider]);
9705
- const handleAttachResource = useCallback17((item) => {
9948
+ const handleAttachResource = useCallback16((item) => {
9706
9949
  onAttachResource(item);
9707
9950
  setIsPickerOpen(false);
9708
9951
  onSelectProvider(null);
@@ -10004,7 +10247,7 @@ var SanqianChat = memo13(function SanqianChat2({
10004
10247
  chat.submitHitlInput({ cancelled: true });
10005
10248
  }
10006
10249
  };
10007
- const handleSendMessage = useCallback18(async (content) => {
10250
+ const handleSendMessage = useCallback17(async (content) => {
10008
10251
  const attachedResources = resourcePicker.attachedResources.length > 0 ? [...resourcePicker.attachedResources] : void 0;
10009
10252
  const sent = await chat.trySendMessage(content, {
10010
10253
  attachedResources
@@ -10161,7 +10404,7 @@ var SanqianChat = memo13(function SanqianChat2({
10161
10404
  });
10162
10405
 
10163
10406
  // src/renderer/components/FloatingChat.tsx
10164
- import { memo as memo15, useCallback as useCallback20, useMemo as useMemo11, useRef as useRef15 } from "react";
10407
+ import { memo as memo15, useCallback as useCallback19, useMemo as useMemo11, useRef as useRef15 } from "react";
10165
10408
 
10166
10409
  // src/renderer/components/ModeToggleButton.tsx
10167
10410
  import { useMemo as useMemo9 } from "react";
@@ -10238,7 +10481,7 @@ function AttachButton({ className, locale = "en" }) {
10238
10481
  }
10239
10482
 
10240
10483
  // src/renderer/components/Resizer.tsx
10241
- import { useCallback as useCallback19, useRef as useRef14, useEffect as useEffect22, useLayoutEffect, useState as useState19 } from "react";
10484
+ import { useCallback as useCallback18, useRef as useRef14, useEffect as useEffect22, useLayoutEffect, useState as useState19 } from "react";
10242
10485
  import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
10243
10486
  function useRafThrottle(fn) {
10244
10487
  const rafRef = useRef14(null);
@@ -10247,7 +10490,7 @@ function useRafThrottle(fn) {
10247
10490
  useLayoutEffect(() => {
10248
10491
  fnRef.current = fn;
10249
10492
  });
10250
- const throttledFn = useCallback19((...args) => {
10493
+ const throttledFn = useCallback18((...args) => {
10251
10494
  argsRef.current = args;
10252
10495
  if (rafRef.current === null) {
10253
10496
  rafRef.current = requestAnimationFrame(() => {
@@ -10285,10 +10528,10 @@ function Resizer({
10285
10528
  widthRef.current = width;
10286
10529
  }, [width]);
10287
10530
  const throttledSetWidth = useRafThrottle(setWidth);
10288
- const handleDoubleClick = useCallback19(() => {
10531
+ const handleDoubleClick = useCallback18(() => {
10289
10532
  setWidth(defaultWidth, true);
10290
10533
  }, [defaultWidth, setWidth]);
10291
- const handleMouseDown = useCallback19((e) => {
10534
+ const handleMouseDown = useCallback18((e) => {
10292
10535
  e.preventDefault();
10293
10536
  setIsDragging(true);
10294
10537
  startX.current = e.screenX;
@@ -10614,7 +10857,7 @@ var FloatingChat = memo15(function FloatingChat2({
10614
10857
  ] })
10615
10858
  ] });
10616
10859
  const resolvedHeader = header ?? (showHeader ? defaultHeader : null);
10617
- const defaultRenderMessage = useCallback20(
10860
+ const defaultRenderMessage = useCallback19(
10618
10861
  (message) => {
10619
10862
  if (message.role === "tool") return null;
10620
10863
  const isUser = message.role === "user";
@@ -10628,7 +10871,7 @@ var FloatingChat = memo15(function FloatingChat2({
10628
10871
  },
10629
10872
  [renderContent]
10630
10873
  );
10631
- const defaultRenderHitl = useCallback20(
10874
+ const defaultRenderHitl = useCallback19(
10632
10875
  (interrupt, onApprove, onReject) => /* @__PURE__ */ jsxs16("div", { className: "p-4 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg m-2", children: [
10633
10876
  /* @__PURE__ */ jsx22("p", { className: "font-medium mb-2", children: interrupt.type === "approval_request" ? resolvedStrings.hitlApprovalRequired : resolvedStrings.hitlInputRequired }),
10634
10877
  interrupt.tool && /* @__PURE__ */ jsxs16("p", { className: "text-sm mb-2", children: [
@@ -10956,7 +11199,7 @@ var HistoryList = memo16(function HistoryList2({
10956
11199
  });
10957
11200
 
10958
11201
  // src/renderer/components/HistoryModal.tsx
10959
- import { memo as memo17, useEffect as useEffect25, useCallback as useCallback21, useRef as useRef17, useState as useState21 } from "react";
11202
+ import { memo as memo17, useEffect as useEffect25, useCallback as useCallback20, useRef as useRef17, useState as useState21 } from "react";
10960
11203
  import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
10961
11204
  var ANIMATION_DURATION = 120;
10962
11205
  var HistoryModal = memo17(function HistoryModal2({
@@ -10971,7 +11214,7 @@ var HistoryModal = memo17(function HistoryModal2({
10971
11214
  const closeTimerRef = useRef17(null);
10972
11215
  const [isClosing, setIsClosing] = useState21(false);
10973
11216
  const [shouldRender, setShouldRender] = useState21(isOpen);
10974
- const clearCloseTimer = useCallback21(() => {
11217
+ const clearCloseTimer = useCallback20(() => {
10975
11218
  if (closeTimerRef.current === null) return;
10976
11219
  clearTimeout(closeTimerRef.current);
10977
11220
  closeTimerRef.current = null;
@@ -10997,7 +11240,7 @@ var HistoryModal = memo17(function HistoryModal2({
10997
11240
  return clearCloseTimer;
10998
11241
  }, [isOpen, shouldRender, clearCloseTimer]);
10999
11242
  useEffect25(() => clearCloseTimer, [clearCloseTimer]);
11000
- const handleCloseRequest = useCallback21(() => {
11243
+ const handleCloseRequest = useCallback20(() => {
11001
11244
  if (!isOpen || isClosing) return;
11002
11245
  onClose();
11003
11246
  }, [isOpen, isClosing, onClose]);
@@ -11016,7 +11259,7 @@ var HistoryModal = memo17(function HistoryModal2({
11016
11259
  if (!isOpen || !modalRef.current) return;
11017
11260
  modalRef.current.focus();
11018
11261
  }, [isOpen]);
11019
- const handleBackdropClick = useCallback21(
11262
+ const handleBackdropClick = useCallback20(
11020
11263
  (e) => {
11021
11264
  if (e.target === e.currentTarget && !isClosing) {
11022
11265
  handleCloseRequest();
@@ -11156,7 +11399,7 @@ var HistoryModal = memo17(function HistoryModal2({
11156
11399
  });
11157
11400
 
11158
11401
  // src/renderer/components/CompactChat.tsx
11159
- import { memo as memo18, useRef as useRef18, useState as useState22, useEffect as useEffect26, useCallback as useCallback22, useMemo as useMemo12 } from "react";
11402
+ import { memo as memo18, useRef as useRef18, useState as useState22, useEffect as useEffect26, useCallback as useCallback21, useMemo as useMemo12 } from "react";
11160
11403
  import { createPortal } from "react-dom";
11161
11404
 
11162
11405
  // src/renderer/components/PanelControls.tsx
@@ -11307,14 +11550,14 @@ var CompactChat = memo18(function CompactChat2({
11307
11550
  () => resourcePicker.providers.filter((p) => p.hasGetList),
11308
11551
  [resourcePicker.providers]
11309
11552
  );
11310
- const handleHitlCancel = useCallback22(() => {
11553
+ const handleHitlCancel = useCallback21(() => {
11311
11554
  if (chat.pendingInterrupt?.type === "approval_request") {
11312
11555
  chat.rejectHitl();
11313
11556
  } else {
11314
11557
  chat.submitHitlInput({ cancelled: true });
11315
11558
  }
11316
11559
  }, [chat.pendingInterrupt?.type, chat.rejectHitl, chat.submitHitlInput]);
11317
- const handleSendWithResources = useCallback22(async (content) => {
11560
+ const handleSendWithResources = useCallback21(async (content) => {
11318
11561
  const resources = [...resourcePicker.attachedResources];
11319
11562
  const sent = await chat.trySendMessage(
11320
11563
  content,
@@ -11355,7 +11598,7 @@ var CompactChat = memo18(function CompactChat2({
11355
11598
  parentSetTextRef.current = (text) => chatInputRef.current?.setValue(text);
11356
11599
  }
11357
11600
  }, [parentSetTextRef]);
11358
- const getStateSnapshot = useCallback22(() => ({
11601
+ const getStateSnapshot = useCallback21(() => ({
11359
11602
  messages: chat.messages,
11360
11603
  isLoading: chat.isLoading,
11361
11604
  isStreaming: chat.isStreaming,
@@ -11453,14 +11696,14 @@ var CompactChat = memo18(function CompactChat2({
11453
11696
  conversations.loadConversations();
11454
11697
  }
11455
11698
  }, [showHistory, connection.isConnected]);
11456
- const handleSelectConversation = useCallback22(
11699
+ const handleSelectConversation = useCallback21(
11457
11700
  async (id) => {
11458
11701
  setShowHistory(false);
11459
11702
  await chat.loadConversation(id);
11460
11703
  },
11461
11704
  [chat]
11462
11705
  );
11463
- const handleDeleteConversation = useCallback22(
11706
+ const handleDeleteConversation = useCallback21(
11464
11707
  async (id) => {
11465
11708
  await conversations.deleteConversation(id);
11466
11709
  onConversationDeleted?.(id);
@@ -11470,7 +11713,7 @@ var CompactChat = memo18(function CompactChat2({
11470
11713
  },
11471
11714
  [conversations, chat, onConversationDeleted]
11472
11715
  );
11473
- const handleNewChat = useCallback22(() => {
11716
+ const handleNewChat = useCallback21(() => {
11474
11717
  chat.newConversation();
11475
11718
  setShowHistory(false);
11476
11719
  setTimeout(() => {
@@ -11491,7 +11734,7 @@ var CompactChat = memo18(function CompactChat2({
11491
11734
  }, [handleNewChat]);
11492
11735
  const isMac = typeof navigator !== "undefined" && navigator.platform.includes("Mac");
11493
11736
  const shortcutKey = isMac ? "\u2318N" : "Ctrl+N";
11494
- const defaultRenderMessage = useCallback22((message) => {
11737
+ const defaultRenderMessage = useCallback21((message) => {
11495
11738
  if (message.role === "tool") return null;
11496
11739
  const isUser = message.role === "user";
11497
11740
  const {