@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.
@@ -87,8 +87,18 @@ type MessageRole = 'user' | 'assistant' | 'system' | 'tool';
87
87
  type ToolCallStatus = 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
88
88
  type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
89
89
  type ConnectionErrorCode = 'NOT_FOUND' | 'CONNECTION_FAILED' | 'WEBSOCKET_ERROR' | 'AUTH_ERROR' | 'TIMEOUT' | 'UNKNOWN';
90
+ interface ToolExecutionMetadata {
91
+ commandExitCode?: number;
92
+ durationMs?: number;
93
+ sandboxed?: boolean;
94
+ timedOut?: boolean;
95
+ truncated?: boolean;
96
+ stdoutPath?: string;
97
+ stderrPath?: string;
98
+ presentation?: string;
99
+ }
90
100
  /** Tool call for UI rendering (extended from SDK) */
91
- interface ToolCall {
101
+ interface ToolCall extends ToolExecutionMetadata {
92
102
  id?: string;
93
103
  name: string;
94
104
  args?: Record<string, unknown>;
@@ -96,9 +106,12 @@ interface ToolCall {
96
106
  status?: ToolCallStatus;
97
107
  result?: unknown;
98
108
  error?: string;
109
+ actionRequired?: string;
110
+ settingsTab?: string;
111
+ settingsSubTab?: string;
99
112
  }
100
113
  /** Message block for structured rendering */
101
- interface MessageBlock {
114
+ interface MessageBlock extends ToolExecutionMetadata {
102
115
  type: 'thinking' | 'text' | 'tool_call' | 'tool_result';
103
116
  content: string;
104
117
  timestamp: number;
@@ -107,6 +120,7 @@ interface MessageBlock {
107
120
  toolArgsRaw?: string;
108
121
  toolCallId?: string;
109
122
  toolStatus?: ToolCallStatus;
123
+ actionRequired?: string;
110
124
  isIntermediate?: boolean;
111
125
  fromSubagent?: boolean;
112
126
  }
@@ -536,6 +550,19 @@ type StreamEvent = {
536
550
  type: 'tool_result';
537
551
  tool_call_id: string;
538
552
  result: unknown;
553
+ error?: string;
554
+ status?: 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
555
+ action_required?: string;
556
+ settings_tab?: string;
557
+ settings_sub_tab?: string;
558
+ command_exit_code?: number;
559
+ duration_ms?: number;
560
+ sandboxed?: boolean;
561
+ timed_out?: boolean;
562
+ truncated?: boolean;
563
+ stdout_path?: string;
564
+ stderr_path?: string;
565
+ presentation?: string;
539
566
  } | {
540
567
  type: 'done';
541
568
  conversationId: string;
@@ -1741,4 +1768,4 @@ declare function ensureChatBaseStyles(): void;
1741
1768
  */
1742
1769
  declare function ensureFullChatStyles(): void;
1743
1770
 
1744
- export { AddResourceButton, type AddResourceButtonProps, type AlertAction, AlertBanner, type AlertBannerProps, type AlertConfig, type AlertType, AttachButton, type AttachButtonProps, type AttachConfig, type AttachPosition, type AttachState, type AttachedResource, AttachedResourceTags, type AttachedResourceTagsProps, type AttachmentMenuItem, type AttachmentMenuItemType, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, ChatInput, type ChatInputHandle, type ChatInputProps, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfig, type ChatUiConfigSerializable, type ChatUiStrings, CompactChat, type CompactChatController, type CompactChatHistoryConfig, type CompactChatProps, type CompactChatStateSnapshot, type ConnectionErrorCode, type ConnectionStatus, type ContextProviderInfo, type ConversationChangeMeta, type ConversationDetail, type ConversationInfo, type ConversationSwitchOptions, FloatingChat, type FloatingChatProps, type FloatingWindowConfig, HistoryList, type HistoryListProps, HistoryModal, type HistoryModalProps, HitlCard, type HitlCardProps, type HitlInterruptData, I18nProvider, type I18nProviderProps, IntermediateSteps, type IntermediateStepsProps, type LinkClickEvent, type LinkHandlerConfig, type Locale, MarkdownRenderer, type MarkdownRendererProps, type MessageBlock, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, type MessageRole, ModeToggleButton, type ModeToggleButtonProps, PanelControls, type PanelControlsProps, PanelHeaderButtons, PanelResizer, Resizer, type ResizerProps, type ResolvedTheme, ResourceChip, ResourceChipList, type ResourceChipListProps, type ResourceChipProps, ResourcePicker, type ResourcePickerItem, type ResourcePickerProps, type ResourcePickerState, SanqianChat, SanqianChatMessage, type SanqianChatMessageProps, type SanqianChatProps, SanqianMessageList, type SanqianMessageListHandle, type SanqianMessageListProps, type SdkAdapterConfig, type SendMessage, type SendMessageOptions, type SessionResource, type SessionResourceEvent, type StoredSessionResource, type StreamEvent, StreamingTimeline, type StreamingTimelineProps, type ThemeMode, ThemeProvider, type ThemeProviderProps, ThinkingSection, type ThinkingSectionProps, ToolArgumentsDisplay, type ToolArgumentsDisplayProps, type ToolCall, type ToolCallStatus, type Translations, type UseAttachStateReturn, type UseChatCapabilities, type UseChatOptions, type UseChatPanelReturn, type UseChatReturn, type UseConnectionOptions, type UseConnectionReturn, type UseConversationsOptions, type UseConversationsReturn, type UseFocusPersistenceOptions, type UseResourcePickerOptions, type UseResourcePickerReturn, type WindowPosition, createChatAdapter, createIpcAdapter, createSdkAdapter, ensureChatBaseStyles, ensureFullChatStyles, getTranslations, resolveChatStrings, useAttachState, useChat, useChatPanel, useChatStyles, useConnection, useConversations, useFocusPersistence, useI18n, useIpcUiConfig, useResolvedUiConfig, useResourcePicker, useStandaloneI18n, useStandaloneTheme, useTheme, useWindowDragLock };
1771
+ export { AddResourceButton, type AddResourceButtonProps, type AlertAction, AlertBanner, type AlertBannerProps, type AlertConfig, type AlertType, AttachButton, type AttachButtonProps, type AttachConfig, type AttachPosition, type AttachState, type AttachedResource, AttachedResourceTags, type AttachedResourceTagsProps, type AttachmentMenuItem, type AttachmentMenuItemType, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, ChatInput, type ChatInputHandle, type ChatInputProps, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfig, type ChatUiConfigSerializable, type ChatUiStrings, CompactChat, type CompactChatController, type CompactChatHistoryConfig, type CompactChatProps, type CompactChatStateSnapshot, type ConnectionErrorCode, type ConnectionStatus, type ContextProviderInfo, type ConversationChangeMeta, type ConversationDetail, type ConversationInfo, type ConversationSwitchOptions, FloatingChat, type FloatingChatProps, type FloatingWindowConfig, HistoryList, type HistoryListProps, HistoryModal, type HistoryModalProps, HitlCard, type HitlCardProps, type HitlInterruptData, I18nProvider, type I18nProviderProps, IntermediateSteps, type IntermediateStepsProps, type LinkClickEvent, type LinkHandlerConfig, type Locale, MarkdownRenderer, type MarkdownRendererProps, type MessageBlock, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, type MessageRole, ModeToggleButton, type ModeToggleButtonProps, PanelControls, type PanelControlsProps, PanelHeaderButtons, PanelResizer, Resizer, type ResizerProps, type ResolvedTheme, ResourceChip, ResourceChipList, type ResourceChipListProps, type ResourceChipProps, ResourcePicker, type ResourcePickerItem, type ResourcePickerProps, type ResourcePickerState, SanqianChat, SanqianChatMessage, type SanqianChatMessageProps, type SanqianChatProps, SanqianMessageList, type SanqianMessageListHandle, type SanqianMessageListProps, type SdkAdapterConfig, type SendMessage, type SendMessageOptions, type SessionResource, type SessionResourceEvent, type StoredSessionResource, type StreamEvent, StreamingTimeline, type StreamingTimelineProps, type ThemeMode, ThemeProvider, type ThemeProviderProps, ThinkingSection, type ThinkingSectionProps, ToolArgumentsDisplay, type ToolArgumentsDisplayProps, type ToolCall, type ToolCallStatus, type ToolExecutionMetadata, type Translations, type UseAttachStateReturn, type UseChatCapabilities, type UseChatOptions, type UseChatPanelReturn, type UseChatReturn, type UseConnectionOptions, type UseConnectionReturn, type UseConversationsOptions, type UseConversationsReturn, type UseFocusPersistenceOptions, type UseResourcePickerOptions, type UseResourcePickerReturn, type WindowPosition, createChatAdapter, createIpcAdapter, createSdkAdapter, ensureChatBaseStyles, ensureFullChatStyles, getTranslations, resolveChatStrings, useAttachState, useChat, useChatPanel, useChatStyles, useConnection, useConversations, useFocusPersistence, useI18n, useIpcUiConfig, useResolvedUiConfig, useResourcePicker, useStandaloneI18n, useStandaloneTheme, useTheme, useWindowDragLock };
@@ -87,8 +87,18 @@ type MessageRole = 'user' | 'assistant' | 'system' | 'tool';
87
87
  type ToolCallStatus = 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
88
88
  type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
89
89
  type ConnectionErrorCode = 'NOT_FOUND' | 'CONNECTION_FAILED' | 'WEBSOCKET_ERROR' | 'AUTH_ERROR' | 'TIMEOUT' | 'UNKNOWN';
90
+ interface ToolExecutionMetadata {
91
+ commandExitCode?: number;
92
+ durationMs?: number;
93
+ sandboxed?: boolean;
94
+ timedOut?: boolean;
95
+ truncated?: boolean;
96
+ stdoutPath?: string;
97
+ stderrPath?: string;
98
+ presentation?: string;
99
+ }
90
100
  /** Tool call for UI rendering (extended from SDK) */
91
- interface ToolCall {
101
+ interface ToolCall extends ToolExecutionMetadata {
92
102
  id?: string;
93
103
  name: string;
94
104
  args?: Record<string, unknown>;
@@ -96,9 +106,12 @@ interface ToolCall {
96
106
  status?: ToolCallStatus;
97
107
  result?: unknown;
98
108
  error?: string;
109
+ actionRequired?: string;
110
+ settingsTab?: string;
111
+ settingsSubTab?: string;
99
112
  }
100
113
  /** Message block for structured rendering */
101
- interface MessageBlock {
114
+ interface MessageBlock extends ToolExecutionMetadata {
102
115
  type: 'thinking' | 'text' | 'tool_call' | 'tool_result';
103
116
  content: string;
104
117
  timestamp: number;
@@ -107,6 +120,7 @@ interface MessageBlock {
107
120
  toolArgsRaw?: string;
108
121
  toolCallId?: string;
109
122
  toolStatus?: ToolCallStatus;
123
+ actionRequired?: string;
110
124
  isIntermediate?: boolean;
111
125
  fromSubagent?: boolean;
112
126
  }
@@ -536,6 +550,19 @@ type StreamEvent = {
536
550
  type: 'tool_result';
537
551
  tool_call_id: string;
538
552
  result: unknown;
553
+ error?: string;
554
+ status?: 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
555
+ action_required?: string;
556
+ settings_tab?: string;
557
+ settings_sub_tab?: string;
558
+ command_exit_code?: number;
559
+ duration_ms?: number;
560
+ sandboxed?: boolean;
561
+ timed_out?: boolean;
562
+ truncated?: boolean;
563
+ stdout_path?: string;
564
+ stderr_path?: string;
565
+ presentation?: string;
539
566
  } | {
540
567
  type: 'done';
541
568
  conversationId: string;
@@ -1741,4 +1768,4 @@ declare function ensureChatBaseStyles(): void;
1741
1768
  */
1742
1769
  declare function ensureFullChatStyles(): void;
1743
1770
 
1744
- export { AddResourceButton, type AddResourceButtonProps, type AlertAction, AlertBanner, type AlertBannerProps, type AlertConfig, type AlertType, AttachButton, type AttachButtonProps, type AttachConfig, type AttachPosition, type AttachState, type AttachedResource, AttachedResourceTags, type AttachedResourceTagsProps, type AttachmentMenuItem, type AttachmentMenuItemType, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, ChatInput, type ChatInputHandle, type ChatInputProps, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfig, type ChatUiConfigSerializable, type ChatUiStrings, CompactChat, type CompactChatController, type CompactChatHistoryConfig, type CompactChatProps, type CompactChatStateSnapshot, type ConnectionErrorCode, type ConnectionStatus, type ContextProviderInfo, type ConversationChangeMeta, type ConversationDetail, type ConversationInfo, type ConversationSwitchOptions, FloatingChat, type FloatingChatProps, type FloatingWindowConfig, HistoryList, type HistoryListProps, HistoryModal, type HistoryModalProps, HitlCard, type HitlCardProps, type HitlInterruptData, I18nProvider, type I18nProviderProps, IntermediateSteps, type IntermediateStepsProps, type LinkClickEvent, type LinkHandlerConfig, type Locale, MarkdownRenderer, type MarkdownRendererProps, type MessageBlock, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, type MessageRole, ModeToggleButton, type ModeToggleButtonProps, PanelControls, type PanelControlsProps, PanelHeaderButtons, PanelResizer, Resizer, type ResizerProps, type ResolvedTheme, ResourceChip, ResourceChipList, type ResourceChipListProps, type ResourceChipProps, ResourcePicker, type ResourcePickerItem, type ResourcePickerProps, type ResourcePickerState, SanqianChat, SanqianChatMessage, type SanqianChatMessageProps, type SanqianChatProps, SanqianMessageList, type SanqianMessageListHandle, type SanqianMessageListProps, type SdkAdapterConfig, type SendMessage, type SendMessageOptions, type SessionResource, type SessionResourceEvent, type StoredSessionResource, type StreamEvent, StreamingTimeline, type StreamingTimelineProps, type ThemeMode, ThemeProvider, type ThemeProviderProps, ThinkingSection, type ThinkingSectionProps, ToolArgumentsDisplay, type ToolArgumentsDisplayProps, type ToolCall, type ToolCallStatus, type Translations, type UseAttachStateReturn, type UseChatCapabilities, type UseChatOptions, type UseChatPanelReturn, type UseChatReturn, type UseConnectionOptions, type UseConnectionReturn, type UseConversationsOptions, type UseConversationsReturn, type UseFocusPersistenceOptions, type UseResourcePickerOptions, type UseResourcePickerReturn, type WindowPosition, createChatAdapter, createIpcAdapter, createSdkAdapter, ensureChatBaseStyles, ensureFullChatStyles, getTranslations, resolveChatStrings, useAttachState, useChat, useChatPanel, useChatStyles, useConnection, useConversations, useFocusPersistence, useI18n, useIpcUiConfig, useResolvedUiConfig, useResourcePicker, useStandaloneI18n, useStandaloneTheme, useTheme, useWindowDragLock };
1771
+ export { AddResourceButton, type AddResourceButtonProps, type AlertAction, AlertBanner, type AlertBannerProps, type AlertConfig, type AlertType, AttachButton, type AttachButtonProps, type AttachConfig, type AttachPosition, type AttachState, type AttachedResource, AttachedResourceTags, type AttachedResourceTagsProps, type AttachmentMenuItem, type AttachmentMenuItemType, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, ChatInput, type ChatInputHandle, type ChatInputProps, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfig, type ChatUiConfigSerializable, type ChatUiStrings, CompactChat, type CompactChatController, type CompactChatHistoryConfig, type CompactChatProps, type CompactChatStateSnapshot, type ConnectionErrorCode, type ConnectionStatus, type ContextProviderInfo, type ConversationChangeMeta, type ConversationDetail, type ConversationInfo, type ConversationSwitchOptions, FloatingChat, type FloatingChatProps, type FloatingWindowConfig, HistoryList, type HistoryListProps, HistoryModal, type HistoryModalProps, HitlCard, type HitlCardProps, type HitlInterruptData, I18nProvider, type I18nProviderProps, IntermediateSteps, type IntermediateStepsProps, type LinkClickEvent, type LinkHandlerConfig, type Locale, MarkdownRenderer, type MarkdownRendererProps, type MessageBlock, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, type MessageRole, ModeToggleButton, type ModeToggleButtonProps, PanelControls, type PanelControlsProps, PanelHeaderButtons, PanelResizer, Resizer, type ResizerProps, type ResolvedTheme, ResourceChip, ResourceChipList, type ResourceChipListProps, type ResourceChipProps, ResourcePicker, type ResourcePickerItem, type ResourcePickerProps, type ResourcePickerState, SanqianChat, SanqianChatMessage, type SanqianChatMessageProps, type SanqianChatProps, SanqianMessageList, type SanqianMessageListHandle, type SanqianMessageListProps, type SdkAdapterConfig, type SendMessage, type SendMessageOptions, type SessionResource, type SessionResourceEvent, type StoredSessionResource, type StreamEvent, StreamingTimeline, type StreamingTimelineProps, type ThemeMode, ThemeProvider, type ThemeProviderProps, ThinkingSection, type ThinkingSectionProps, ToolArgumentsDisplay, type ToolArgumentsDisplayProps, type ToolCall, type ToolCallStatus, type ToolExecutionMetadata, type Translations, type UseAttachStateReturn, type UseChatCapabilities, type UseChatOptions, type UseChatPanelReturn, type UseChatReturn, type UseConnectionOptions, type UseConnectionReturn, type UseConversationsOptions, type UseConversationsReturn, type UseFocusPersistenceOptions, type UseResourcePickerOptions, type UseResourcePickerReturn, type WindowPosition, createChatAdapter, createIpcAdapter, createSdkAdapter, ensureChatBaseStyles, ensureFullChatStyles, getTranslations, resolveChatStrings, useAttachState, useChat, useChatPanel, useChatStyles, useConnection, useConversations, useFocusPersistence, useI18n, useIpcUiConfig, useResolvedUiConfig, useResourcePicker, useStandaloneI18n, useStandaloneTheme, useTheme, useWindowDragLock };
@@ -88,6 +88,68 @@ 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
+ var BLOCKING_ACTION_REQUIRED = /* @__PURE__ */ new Set(["install_git_bash"]);
94
+ function normalizeToolExecutionStatus(status) {
95
+ if (typeof status !== "string") return void 0;
96
+ switch (status.trim().toLowerCase()) {
97
+ case "success":
98
+ case "completed":
99
+ case "ok":
100
+ return "completed";
101
+ case "error":
102
+ case "failed":
103
+ return "error";
104
+ case "cancelled":
105
+ case "canceled":
106
+ return "cancelled";
107
+ case "running":
108
+ return "running";
109
+ case "pending":
110
+ return "pending";
111
+ default:
112
+ return void 0;
113
+ }
114
+ }
115
+ function deriveToolExecutionStatus(options) {
116
+ if (typeof options.actionRequired === "string" && BLOCKING_ACTION_REQUIRED.has(options.actionRequired)) {
117
+ return "error";
118
+ }
119
+ if (options.error === true) {
120
+ return "error";
121
+ }
122
+ if (typeof options.error === "string" && options.error.trim().length > 0) {
123
+ return "error";
124
+ }
125
+ const normalizedStatus = normalizeToolExecutionStatus(options.status);
126
+ if (normalizedStatus) {
127
+ return normalizedStatus;
128
+ }
129
+ if (options.result !== void 0 && options.result !== null) {
130
+ if (typeof options.result === "string") {
131
+ const normalizedResult = options.result.trimStart();
132
+ if (normalizedResult.startsWith("\u274C") || normalizedResult.startsWith("Error:")) {
133
+ return "error";
134
+ }
135
+ }
136
+ return "completed";
137
+ }
138
+ return options.fallback ?? "running";
139
+ }
140
+ function formatToolResultContent(result, error) {
141
+ const value = result ?? error;
142
+ if (value === void 0 || value === null) return void 0;
143
+ if (typeof value === "string") return value;
144
+ try {
145
+ const serialized = JSON.stringify(value);
146
+ return serialized === void 0 ? String(value) : serialized;
147
+ } catch {
148
+ return String(value);
149
+ }
150
+ }
151
+
152
+ // src/renderer/hooks/useChat.ts
91
153
  var TYPEWRITER_DELAYS = { VERY_FAST: 2, FAST: 5, NORMAL: 10, SLOW: 20 };
92
154
  var TYPEWRITER_THRESHOLDS = { VERY_FAST: 100, FAST: 50, NORMAL: 20 };
93
155
  var MAX_DETACHED_SNAPSHOTS = 30;
@@ -108,6 +170,36 @@ var findLastBlock = (blocks, predicate) => {
108
170
  }
109
171
  return void 0;
110
172
  };
173
+ function getToolResultStatus(event) {
174
+ return deriveToolExecutionStatus({
175
+ status: event.status,
176
+ result: event.result,
177
+ actionRequired: event.action_required,
178
+ error: event.error,
179
+ fallback: "completed"
180
+ });
181
+ }
182
+ function readToolExecutionNumber(value) {
183
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
184
+ }
185
+ function readToolExecutionBoolean(value) {
186
+ return typeof value === "boolean" ? value : void 0;
187
+ }
188
+ function readToolExecutionString(value) {
189
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
190
+ }
191
+ function getToolExecutionMetadata(event) {
192
+ return {
193
+ commandExitCode: readToolExecutionNumber(event.command_exit_code),
194
+ durationMs: readToolExecutionNumber(event.duration_ms),
195
+ sandboxed: readToolExecutionBoolean(event.sandboxed),
196
+ timedOut: readToolExecutionBoolean(event.timed_out),
197
+ truncated: readToolExecutionBoolean(event.truncated),
198
+ stdoutPath: readToolExecutionString(event.stdout_path),
199
+ stderrPath: readToolExecutionString(event.stderr_path),
200
+ presentation: readToolExecutionString(event.presentation)
201
+ };
202
+ }
111
203
  function cloneBlocks(blocks) {
112
204
  return blocks?.map((block) => ({
113
205
  ...block,
@@ -251,7 +343,19 @@ function updateDetachedSnapshotForEvent(streamContext, event) {
251
343
  return true;
252
344
  }
253
345
  case "tool_result": {
254
- const toolCalls = (assistant.toolCalls || []).map((toolCall) => toolCall.id === event.tool_call_id ? { ...toolCall, status: "completed", result: event.result } : toolCall);
346
+ const toolStatus = getToolResultStatus(event);
347
+ const resultContent = formatToolResultContent(event.result, event.error);
348
+ const toolExecutionMetadata = getToolExecutionMetadata(event);
349
+ const toolCalls = (assistant.toolCalls || []).map((toolCall) => toolCall.id === event.tool_call_id ? {
350
+ ...toolCall,
351
+ status: toolStatus,
352
+ result: event.result,
353
+ error: toolStatus === "error" ? typeof event.error === "string" && event.error.trim().length > 0 ? event.error : resultContent : void 0,
354
+ actionRequired: event.action_required,
355
+ settingsTab: event.settings_tab,
356
+ settingsSubTab: event.settings_sub_tab,
357
+ ...toolExecutionMetadata
358
+ } : toolCall);
255
359
  const hasRunning = toolCalls.some((toolCall) => toolCall.status === "running");
256
360
  const blocks = cloneBlocks(assistant.blocks) || [];
257
361
  const toolBlockIndex = blocks.findIndex(
@@ -260,17 +364,23 @@ function updateDetachedSnapshotForEvent(streamContext, event) {
260
364
  if (toolBlockIndex !== -1) {
261
365
  blocks[toolBlockIndex] = {
262
366
  ...blocks[toolBlockIndex],
263
- toolStatus: "completed"
367
+ toolStatus,
368
+ actionRequired: event.action_required,
369
+ ...toolExecutionMetadata
264
370
  };
265
371
  }
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
- });
372
+ if (resultContent) {
373
+ blocks.push({
374
+ type: "tool_result",
375
+ content: resultContent,
376
+ timestamp: Date.now(),
377
+ toolName: toolBlockIndex !== -1 ? blocks[toolBlockIndex].toolName : void 0,
378
+ toolCallId: event.tool_call_id,
379
+ actionRequired: event.action_required,
380
+ isIntermediate: true,
381
+ ...toolExecutionMetadata
382
+ });
383
+ }
274
384
  snapshot[assistantIndex] = {
275
385
  ...assistant,
276
386
  toolCalls,
@@ -393,16 +503,18 @@ function useChat(options) {
393
503
  }, [conversationId]);
394
504
  (0, import_react.useEffect)(() => {
395
505
  isMountedRef.current = true;
506
+ const streamContexts = streamContextsRef.current;
507
+ const conversationStreamTokens = conversationStreamTokensRef.current;
396
508
  return () => {
397
509
  isMountedRef.current = false;
398
510
  cancelRef.current?.();
399
- streamContextsRef.current.forEach((context) => {
511
+ streamContexts.forEach((context) => {
400
512
  context.suppressed = true;
401
513
  context.terminal = true;
402
514
  context.cancel?.();
403
515
  });
404
- streamContextsRef.current.clear();
405
- conversationStreamTokensRef.current.clear();
516
+ streamContexts.clear();
517
+ conversationStreamTokens.clear();
406
518
  if (typewriterIntervalRef.current) clearTimeout(typewriterIntervalRef.current);
407
519
  };
408
520
  }, []);
@@ -992,24 +1104,46 @@ function useChat(options) {
992
1104
  case "tool_result": {
993
1105
  flushTypewriter();
994
1106
  const toolId = event.tool_call_id;
995
- const result = typeof event.result === "string" ? event.result : JSON.stringify(event.result);
1107
+ const toolStatus = getToolResultStatus(event);
1108
+ const resultContent = formatToolResultContent(event.result, event.error);
1109
+ const toolExecutionMetadata = getToolExecutionMetadata(event);
996
1110
  const blockIdx = currentBlocksRef.current.findIndex((b) => b.type === "tool_call" && b.toolCallId === toolId);
997
1111
  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
- });
1112
+ if (blockIdx !== -1) {
1113
+ currentBlocksRef.current[blockIdx] = {
1114
+ ...currentBlocksRef.current[blockIdx],
1115
+ toolStatus,
1116
+ actionRequired: event.action_required,
1117
+ ...toolExecutionMetadata
1118
+ };
1119
+ }
1120
+ if (resultContent) {
1121
+ currentBlocksRef.current.push({
1122
+ type: "tool_result",
1123
+ content: resultContent,
1124
+ timestamp: Date.now(),
1125
+ toolName,
1126
+ toolCallId: toolId,
1127
+ actionRequired: event.action_required,
1128
+ isIntermediate: true,
1129
+ ...toolExecutionMetadata
1130
+ });
1131
+ }
1007
1132
  setMessages((prev) => {
1008
1133
  const idx = prev.findIndex((m) => m.id === assistantMessageId);
1009
1134
  if (idx === -1) return prev;
1010
1135
  const msg = prev[idx];
1011
1136
  const updatedCalls = (msg.toolCalls || []).map(
1012
- (t) => t.id === toolId ? { ...t, status: "completed", result: event.result } : t
1137
+ (t) => t.id === toolId ? {
1138
+ ...t,
1139
+ status: toolStatus,
1140
+ result: event.result,
1141
+ error: toolStatus === "error" ? typeof event.error === "string" && event.error.trim().length > 0 ? event.error : resultContent : void 0,
1142
+ actionRequired: event.action_required,
1143
+ settingsTab: event.settings_tab,
1144
+ settingsSubTab: event.settings_sub_tab,
1145
+ ...toolExecutionMetadata
1146
+ } : t
1013
1147
  );
1014
1148
  const hasRunning = updatedCalls.some((tc) => tc.status === "running");
1015
1149
  const updated = [...prev];
@@ -1919,7 +2053,9 @@ function useConnection(options) {
1919
2053
  const [errorCode, setErrorCode] = (0, import_react4.useState)();
1920
2054
  const isMountedRef = (0, import_react4.useRef)(true);
1921
2055
  const onStatusChangeRef = (0, import_react4.useRef)(onStatusChange);
1922
- onStatusChangeRef.current = onStatusChange;
2056
+ (0, import_react4.useEffect)(() => {
2057
+ onStatusChangeRef.current = onStatusChange;
2058
+ }, [onStatusChange]);
1923
2059
  const reconnectAttemptsRef = (0, import_react4.useRef)(0);
1924
2060
  (0, import_react4.useEffect)(() => {
1925
2061
  isMountedRef.current = true;
@@ -6366,6 +6502,27 @@ function useWindowDragLock(active) {
6366
6502
  }
6367
6503
 
6368
6504
  // src/core/history.ts
6505
+ function readToolExecutionNumber2(value) {
6506
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
6507
+ }
6508
+ function readToolExecutionBoolean2(value) {
6509
+ return typeof value === "boolean" ? value : void 0;
6510
+ }
6511
+ function readToolExecutionString2(value) {
6512
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
6513
+ }
6514
+ function readToolExecutionMetadata(tc) {
6515
+ return {
6516
+ commandExitCode: readToolExecutionNumber2(tc.command_exit_code ?? tc.commandExitCode),
6517
+ durationMs: readToolExecutionNumber2(tc.duration_ms ?? tc.durationMs),
6518
+ sandboxed: readToolExecutionBoolean2(tc.sandboxed),
6519
+ timedOut: readToolExecutionBoolean2(tc.timed_out ?? tc.timedOut),
6520
+ truncated: readToolExecutionBoolean2(tc.truncated),
6521
+ stdoutPath: readToolExecutionString2(tc.stdout_path ?? tc.stdoutPath),
6522
+ stderrPath: readToolExecutionString2(tc.stderr_path ?? tc.stderrPath),
6523
+ presentation: readToolExecutionString2(tc.presentation)
6524
+ };
6525
+ }
6369
6526
  function safeParseArgs(value) {
6370
6527
  if (!value) return void 0;
6371
6528
  if (typeof value === "object") return value;
@@ -6395,12 +6552,26 @@ function parseToolCalls(toolCalls) {
6395
6552
  const rawArgs = fn?.arguments ?? tc.args ?? tc.arguments;
6396
6553
  const args = safeParseArgs(rawArgs);
6397
6554
  const id = tc.id || tc.tool_call_id || tc.call_id;
6555
+ const actionRequired = tc.action_required || tc.actionRequired;
6556
+ const settingsTab = tc.settings_tab || tc.settingsTab;
6557
+ const settingsSubTab = tc.settings_sub_tab || tc.settingsSubTab;
6398
6558
  return {
6399
6559
  id,
6400
6560
  name,
6401
6561
  args,
6402
- status: "completed",
6403
- result: tc.result
6562
+ result: tc.result,
6563
+ error: typeof tc.error === "string" ? tc.error : void 0,
6564
+ actionRequired,
6565
+ settingsTab,
6566
+ settingsSubTab,
6567
+ status: deriveToolExecutionStatus({
6568
+ status: tc.status,
6569
+ result: tc.result,
6570
+ actionRequired,
6571
+ error: tc.error,
6572
+ fallback: "completed"
6573
+ }),
6574
+ ...readToolExecutionMetadata(tc)
6404
6575
  };
6405
6576
  }).filter((tc) => tc.name || tc.id);
6406
6577
  }
@@ -6468,6 +6639,10 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6468
6639
  const consecutiveAssistantMsgs = [msg];
6469
6640
  const toolMessages = [];
6470
6641
  let j = i + 1;
6642
+ const hasDeclaredToolCalls = (message) => {
6643
+ const calls = parseToolCalls(message.toolCalls || message.tool_calls);
6644
+ return Boolean(calls && calls.length > 0);
6645
+ };
6471
6646
  while (j < rawMessages.length) {
6472
6647
  if (rawMessages[j].role === "assistant") {
6473
6648
  consecutiveAssistantMsgs.push(rawMessages[j]);
@@ -6483,15 +6658,19 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6483
6658
  let blockTime = Date.now();
6484
6659
  let fallbackToolCalls;
6485
6660
  let lastEffectiveToolCalls;
6661
+ const groupHasDeclaredToolCalls = consecutiveAssistantMsgs.some(hasDeclaredToolCalls);
6486
6662
  for (let k = 0; k < consecutiveAssistantMsgs.length; k++) {
6487
6663
  const assistantMsg = consecutiveAssistantMsgs[k];
6488
6664
  const msgToolCalls = parseToolCalls(assistantMsg.toolCalls || assistantMsg.tool_calls);
6489
6665
  const isLastAssistant = k === consecutiveAssistantMsgs.length - 1;
6490
- const fallbackCalls = isLastAssistant && toolMessages.length > 0 ? toolMessages.map((toolMessage, idx) => ({
6666
+ const fallbackCalls = !groupHasDeclaredToolCalls && isLastAssistant && toolMessages.length > 0 ? toolMessages.map((toolMessage, idx) => ({
6491
6667
  id: (toolMessage.tool_call_id || void 0) ?? `tool-${idx}`,
6492
6668
  name: "",
6493
6669
  args: void 0,
6494
- status: "completed",
6670
+ status: deriveToolExecutionStatus({
6671
+ result: toolMessage.content,
6672
+ fallback: "completed"
6673
+ }),
6495
6674
  result: toolMessage.content
6496
6675
  })) : void 0;
6497
6676
  const effectiveToolCalls = msgToolCalls && msgToolCalls.length > 0 ? msgToolCalls : fallbackCalls;
@@ -6524,6 +6703,16 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6524
6703
  const toolResultBlocks = [];
6525
6704
  for (const tc of effectiveToolCalls) {
6526
6705
  const toolIndex = effectiveToolCalls.indexOf(tc);
6706
+ const toolResultById = tc.id ? toolMessages.find((tm) => tm.tool_call_id === tc.id) : void 0;
6707
+ const toolResultByIndex = toolMessages[toolIndex];
6708
+ const toolResult = toolResultById || toolResultByIndex;
6709
+ const toolStatus = deriveToolExecutionStatus({
6710
+ status: tc.status,
6711
+ result: tc.result ?? toolResult?.content,
6712
+ actionRequired: tc.actionRequired,
6713
+ error: tc.error,
6714
+ fallback: "completed"
6715
+ });
6527
6716
  blocks.push({
6528
6717
  type: "tool_call",
6529
6718
  content: "",
@@ -6531,12 +6720,18 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6531
6720
  toolName: tc.name,
6532
6721
  toolArgs: tc.args,
6533
6722
  toolCallId: tc.id,
6534
- toolStatus: "completed",
6535
- isIntermediate: true
6723
+ toolStatus,
6724
+ actionRequired: tc.actionRequired,
6725
+ isIntermediate: true,
6726
+ commandExitCode: tc.commandExitCode,
6727
+ durationMs: tc.durationMs,
6728
+ sandboxed: tc.sandboxed,
6729
+ timedOut: tc.timedOut,
6730
+ truncated: tc.truncated,
6731
+ stdoutPath: tc.stdoutPath,
6732
+ stderrPath: tc.stderrPath,
6733
+ presentation: tc.presentation
6536
6734
  });
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
6735
  const resultContent = tc.result ?? toolResult?.content;
6541
6736
  const toolResultId = toolResult?.tool_call_id || void 0;
6542
6737
  if (resultContent) {
@@ -6546,7 +6741,16 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6546
6741
  timestamp: blockTime++,
6547
6742
  toolName: tc.name,
6548
6743
  toolCallId: tc.id || toolResultId,
6549
- isIntermediate: true
6744
+ actionRequired: tc.actionRequired,
6745
+ isIntermediate: true,
6746
+ commandExitCode: tc.commandExitCode,
6747
+ durationMs: tc.durationMs,
6748
+ sandboxed: tc.sandboxed,
6749
+ timedOut: tc.timedOut,
6750
+ truncated: tc.truncated,
6751
+ stdoutPath: tc.stdoutPath,
6752
+ stderrPath: tc.stderrPath,
6753
+ presentation: tc.presentation
6550
6754
  });
6551
6755
  tc.result = resultContent;
6552
6756
  }
@@ -6579,6 +6783,13 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
6579
6783
  );
6580
6784
  if (matchingToolCall) {
6581
6785
  matchingToolCall.result = toolMsg.content;
6786
+ matchingToolCall.status = deriveToolExecutionStatus({
6787
+ status: matchingToolCall.status,
6788
+ result: toolMsg.content,
6789
+ actionRequired: matchingToolCall.actionRequired,
6790
+ error: matchingToolCall.error,
6791
+ fallback: "completed"
6792
+ });
6582
6793
  }
6583
6794
  }
6584
6795
  const thinkingParts = consecutiveAssistantMsgs.map((m) => m.thinking).filter((t) => t && t.trim());
@@ -7177,9 +7388,28 @@ function processStreamEvents(stream, onEvent, sdk, setCurrentRunId, streamId) {
7177
7388
  args: event.args || {}
7178
7389
  });
7179
7390
  break;
7180
- case "tool_result":
7181
- forward({ type: "tool_result", tool_call_id: event.tool_call_id || "", result: event.result });
7391
+ case "tool_result": {
7392
+ const toolResultEvent = event;
7393
+ forward({
7394
+ type: "tool_result",
7395
+ tool_call_id: toolResultEvent.tool_call_id || "",
7396
+ result: toolResultEvent.result,
7397
+ error: toolResultEvent.error,
7398
+ status: toolResultEvent.status,
7399
+ action_required: toolResultEvent.action_required,
7400
+ settings_tab: toolResultEvent.settings_tab,
7401
+ settings_sub_tab: toolResultEvent.settings_sub_tab,
7402
+ command_exit_code: toolResultEvent.command_exit_code,
7403
+ duration_ms: toolResultEvent.duration_ms,
7404
+ sandboxed: toolResultEvent.sandboxed,
7405
+ timed_out: toolResultEvent.timed_out,
7406
+ truncated: toolResultEvent.truncated,
7407
+ stdout_path: toolResultEvent.stdout_path,
7408
+ stderr_path: toolResultEvent.stderr_path,
7409
+ presentation: toolResultEvent.presentation
7410
+ });
7182
7411
  break;
7412
+ }
7183
7413
  case "done":
7184
7414
  forward({ type: "done", conversationId: event.conversationId || "", title: event.title });
7185
7415
  currentRunId = null;
@@ -7929,8 +8159,8 @@ function ToolCallItem({ toolCall, toolResult }) {
7929
8159
  style: { color: "var(--chat-text)" },
7930
8160
  children: [
7931
8161
  /* @__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 }),
8162
+ toolCall.toolStatus === "completed" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-green-600 dark:text-green-400", children: TimelineIcons.check }),
8163
+ toolCall.toolStatus === "error" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-red-500", children: TimelineIcons.error }),
7934
8164
  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
8165
  "(",
7936
8166
  Object.values(toolCall.toolArgs).slice(0, 1).map((v) => typeof v === "string" ? v.length > 18 ? v.slice(0, 18) + "\u2026" : v : "\u2026"),
@@ -7967,7 +8197,8 @@ function StreamingToolCallItem({
7967
8197
  style: { color: "var(--chat-text)" },
7968
8198
  children: [
7969
8199
  /* @__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 }),
8200
+ toolCall.toolStatus === "completed" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-green-600 dark:text-green-400", children: TimelineIcons.check }),
8201
+ toolCall.toolStatus === "error" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-red-500", children: TimelineIcons.error }),
7971
8202
  isActive && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "ml-0.5 text-amber-500 animate-pulse", children: "\u25C6" }),
7972
8203
  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
8204
  "(",
@@ -8388,7 +8619,13 @@ function buildFallbackBlocksFromToolCalls(toolCalls) {
8388
8619
  const blocks = [];
8389
8620
  let timestamp = Date.now();
8390
8621
  for (const toolCall of toolCalls) {
8391
- const toolStatus = toolCall.status || (toolCall.result !== void 0 ? "completed" : "running");
8622
+ const toolStatus = deriveToolExecutionStatus({
8623
+ status: toolCall.status,
8624
+ result: toolCall.result,
8625
+ actionRequired: toolCall.actionRequired,
8626
+ error: toolCall.error,
8627
+ fallback: "running"
8628
+ });
8392
8629
  blocks.push({
8393
8630
  type: "tool_call",
8394
8631
  content: "",
@@ -8397,6 +8634,7 @@ function buildFallbackBlocksFromToolCalls(toolCalls) {
8397
8634
  toolArgs: toolCall.args,
8398
8635
  toolCallId: toolCall.id,
8399
8636
  toolStatus,
8637
+ actionRequired: toolCall.actionRequired,
8400
8638
  isIntermediate: true
8401
8639
  });
8402
8640
  const resultContent = normalizeToolResult(toolCall.result);
@@ -8407,6 +8645,7 @@ function buildFallbackBlocksFromToolCalls(toolCalls) {
8407
8645
  timestamp: timestamp++,
8408
8646
  toolName: toolCall.name,
8409
8647
  toolCallId: toolCall.id,
8648
+ actionRequired: toolCall.actionRequired,
8410
8649
  isIntermediate: true
8411
8650
  });
8412
8651
  }
@@ -8927,9 +9166,13 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8927
9166
  const riskLevel = interrupt.risk_level || "medium";
8928
9167
  const riskStyle = riskColors[riskLevel];
8929
9168
  const onSubmitRef = (0, import_react24.useRef)(onSubmit);
8930
- onSubmitRef.current = onSubmit;
8931
9169
  const onCancelRef = (0, import_react24.useRef)(onCancel);
8932
- onCancelRef.current = onCancel;
9170
+ (0, import_react24.useEffect)(() => {
9171
+ onSubmitRef.current = onSubmit;
9172
+ }, [onSubmit]);
9173
+ (0, import_react24.useEffect)(() => {
9174
+ onCancelRef.current = onCancel;
9175
+ }, [onCancel]);
8933
9176
  (0, import_react24.useEffect)(() => {
8934
9177
  if (isUserInput) {
8935
9178
  if (!interrupt.options || interrupt.options.length === 0) {