diff --git a/web/src/components/AssistantChat/HappyComposer.tsx b/web/src/components/AssistantChat/HappyComposer.tsx index afdea1a4..5603c3f8 100644 --- a/web/src/components/AssistantChat/HappyComposer.tsx +++ b/web/src/components/AssistantChat/HappyComposer.tsx @@ -269,85 +269,93 @@ export function HappyComposer(props: { haptic('light') }, [onModelModeChange, controlsDisabled, haptic]) + const showPermissionSettings = Boolean(onPermissionModeChange) + const showModelSettings = Boolean(onModelModeChange) const showSettingsButton = Boolean(onPermissionModeChange || onModelModeChange) const showAbortButton = true const overlays = useMemo(() => { - if (showSettings) { + if (showSettings && (showPermissionSettings || showModelSettings)) { return (
-
-
- Permission Mode -
- {PERMISSION_MODES.map((mode) => ( - - ))} -
- -
- -
-
- Model +
+ {permissionMode === mode && ( +
+ )} +
+ + {PERMISSION_MODE_LABELS[mode]} + + + ))}
- {MODEL_MODES.map((mode) => ( - - ))} -
+
+ {modelMode === mode && ( +
+ )} +
+ + {MODEL_MODE_LABELS[mode]} + + + ))} +
+ ) : null}
) @@ -370,6 +378,8 @@ export function HappyComposer(props: { return null }, [ showSettings, + showPermissionSettings, + showModelSettings, suggestions, selectedIndex, controlsDisabled, diff --git a/web/src/components/AssistantChat/messages/ToolMessage.tsx b/web/src/components/AssistantChat/messages/ToolMessage.tsx index f004fae0..24b3b11d 100644 --- a/web/src/components/AssistantChat/messages/ToolMessage.tsx +++ b/web/src/components/AssistantChat/messages/ToolMessage.tsx @@ -2,6 +2,7 @@ import type { ToolCallMessagePartProps } from '@assistant-ui/react' import type { ChatBlock } from '@/chat/types' import type { AgentEvent, ToolCallBlock } from '@/chat/types' import type { MessageStatus } from '@/types/api' +import { CodeBlock } from '@/components/CodeBlock' import { MarkdownRenderer } from '@/components/MarkdownRenderer' import { LazyRainbowText } from '@/components/LazyRainbowText' import { ToolCard } from '@/components/ToolCard/ToolCard' @@ -11,11 +12,28 @@ function isObject(value: unknown): value is Record { return Boolean(value) && typeof value === 'object' } +function safeStringify(value: unknown): string { + if (typeof value === 'string') return value + try { + const stringified = JSON.stringify(value, null, 2) + return typeof stringified === 'string' ? stringified : String(value) + } catch { + return String(value) + } +} + function isToolCallBlock(value: unknown): value is ToolCallBlock { if (!isObject(value)) return false if (value.kind !== 'tool-call') return false if (typeof value.id !== 'string') return false + if (value.localId !== null && typeof value.localId !== 'string') return false + if (typeof value.createdAt !== 'number') return false + if (!Array.isArray(value.children)) return false if (!isObject(value.tool)) return false + if (typeof value.tool.name !== 'string') return false + if (!('input' in value.tool)) return false + if (value.tool.description !== null && typeof value.tool.description !== 'string') return false + if (value.tool.state !== 'pending' && value.tool.state !== 'running' && value.tool.state !== 'completed' && value.tool.state !== 'error') return false return true } @@ -182,10 +200,37 @@ export function HappyToolMessage(props: ToolCallMessagePartProps) { const artifact = props.artifact if (!isToolCallBlock(artifact)) { + const argsText = typeof props.argsText === 'string' ? props.argsText.trim() : '' + const hasArgsText = argsText.length > 0 + const hasResult = props.result !== undefined + const resultText = hasResult ? safeStringify(props.result) : '' + return (
-
- Tool call: {props.toolName} +
+
+
+ Tool: {props.toolName} +
+ {props.isError ? ( + Error + ) : null} + {props.status.type === 'running' && !hasResult ? ( + Running… + ) : null} +
+ + {hasArgsText ? ( +
+ +
+ ) : null} + + {hasResult ? ( +
+ +
+ ) : null}
)