From b42d0f6c5aaa24c86e08b45e643dc7fae0243c9f Mon Sep 17 00:00:00 2001 From: Taki Koutsomitis Date: Tue, 15 Sep 2026 14:21:09 -0400 Subject: [PATCH 1/2] Show code execution previews in assistant and notebook activity --- components/AgentChat/ActivityFeed.tsx | 5 + components/AgentChat/CodeExecutionPanel.tsx | 101 ++++++++++++++++++++ types/agentChat.ts | 17 +++- 3 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 components/AgentChat/CodeExecutionPanel.tsx diff --git a/components/AgentChat/ActivityFeed.tsx b/components/AgentChat/ActivityFeed.tsx index 3529fbd79..cdd33c4be 100644 --- a/components/AgentChat/ActivityFeed.tsx +++ b/components/AgentChat/ActivityFeed.tsx @@ -22,6 +22,7 @@ import { import { Loader } from '@/components/ui/Loader'; import { cn } from '@/utils/styles'; import { MarkdownMessage } from './MarkdownMessage'; +import { CodeExecutionPanel } from './CodeExecutionPanel'; import { answerRevealKey, isRevealable, narrationRevealKey } from '@/hooks/useTextReveal'; import type { ActivityCallStatus, @@ -45,6 +46,7 @@ const TOOL_ICONS: Record> = { get_author_works: BookOpen, get_work_fulltext: FileSearch, code_execution: SquareTerminal, + bash_code_execution: SquareTerminal, }; /** @@ -170,6 +172,9 @@ function ToolCallRow({ call }: { readonly call: ChatToolCallActivity }) { {call.detail && (

{call.detail}

)} + {call.code_execution && ( + + )} {call.sources && call.sources.length > 0 && } diff --git a/components/AgentChat/CodeExecutionPanel.tsx b/components/AgentChat/CodeExecutionPanel.tsx new file mode 100644 index 000000000..1e6d07284 --- /dev/null +++ b/components/AgentChat/CodeExecutionPanel.tsx @@ -0,0 +1,101 @@ +'use client'; + +import { useId, useState } from 'react'; +import { ChevronDown, ChevronRight } from 'lucide-react'; +import type { ChatCodeExecution } from '@/types/agentChat'; + +function TextPreview({ + label, + text, + truncated, +}: { + readonly label: string; + readonly text: string; + readonly truncated?: boolean; +}) { + return ( +
+

{label}

+ {/* Treat code and stdout as literal text, including HTML and Markdown. */} +
+        {text}
+      
+ {truncated &&

{label} preview truncated.

} +
+ ); +} + +/** A compact disclosure shared by live and historical activity on both chat surfaces. */ +export function CodeExecutionPanel({ + execution, + tool, +}: { + readonly execution: ChatCodeExecution; + readonly tool: string; +}) { + const [expanded, setExpanded] = useState(false); + const panelId = useId(); + const hasCode = Boolean(execution.code); + const hasOutput = Boolean(execution.output); + const hasReturnCode = execution.return_code != null; + const hasOutputCount = execution.output_count != null; + + if (!hasCode && !hasOutput && !hasReturnCode && !hasOutputCount) return null; + + const label = hasCode + ? hasOutput + ? 'Code and output' + : 'Code' + : hasOutput + ? 'Output' + : 'Execution details'; + + return ( +
+ + +
+ ); +} diff --git a/types/agentChat.ts b/types/agentChat.ts index 33bb4509e..c10e5068f 100644 --- a/types/agentChat.ts +++ b/types/agentChat.ts @@ -50,6 +50,19 @@ export interface ChatThinkingActivity { at: string; } +/** Selected public previews only; raw provider payloads are never part of the feed. */ +export interface ChatCodeExecution { + /** Plain-text code or shell command, bounded by the backend to 12,000 characters. */ + code?: string; + code_truncated?: boolean; + /** Readable stdout, bounded to 2,000 characters. Omitted for encrypted output. */ + output?: string; + output_truncated?: boolean; + return_code?: number; + /** Number of output items; their private file identifiers are not exposed. */ + output_count?: number; +} + export interface ChatToolCallActivity { type: 'tool_call'; /** Machine name (e.g. `web_search`). Only used to pick an icon — new tools appear without notice. */ @@ -59,8 +72,10 @@ export interface ChatToolCallActivity { status: ActivityCallStatus; started_at: string | null; finished_at: string | null; - /** Optional query/name behind the call, ≤200 chars. */ + /** Optional query/name behind the call or a public code execution outcome summary. */ detail?: string | null; + /** Optional code execution preview, available on both assistant and notebook calls. */ + code_execution?: ChatCodeExecution | null; /** Present only on a succeeded `edit_note`: the note version the agent produced. */ note_version_id?: number | null; /** Present only on a succeeded `create_note` (assistant surface): the note it made. */ From d784888bf45bd70eff1c058fefc1e2ba38c61517 Mon Sep 17 00:00:00 2001 From: Taki Koutsomitis Date: Tue, 15 Sep 2026 14:26:23 -0400 Subject: [PATCH 2/2] Simplify preview labels and clarify keyboard accessibility --- components/AgentChat/CodeExecutionPanel.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/components/AgentChat/CodeExecutionPanel.tsx b/components/AgentChat/CodeExecutionPanel.tsx index 1e6d07284..7ce7e3193 100644 --- a/components/AgentChat/CodeExecutionPanel.tsx +++ b/components/AgentChat/CodeExecutionPanel.tsx @@ -4,6 +4,13 @@ import { useId, useState } from 'react'; import { ChevronDown, ChevronRight } from 'lucide-react'; import type { ChatCodeExecution } from '@/types/agentChat'; +function previewLabel(hasCode: boolean, hasOutput: boolean): string { + if (hasCode && hasOutput) return 'Code and output'; + if (hasCode) return 'Code'; + if (hasOutput) return 'Output'; + return 'Execution details'; +} + function TextPreview({ label, text, @@ -18,7 +25,9 @@ function TextPreview({

{label}

{/* Treat code and stdout as literal text, including HTML and Markdown. */}
@@ -46,13 +55,7 @@ export function CodeExecutionPanel({
 
   if (!hasCode && !hasOutput && !hasReturnCode && !hasOutputCount) return null;
 
-  const label = hasCode
-    ? hasOutput
-      ? 'Code and output'
-      : 'Code'
-    : hasOutput
-      ? 'Output'
-      : 'Execution details';
+  const label = previewLabel(hasCode, hasOutput);
 
   return (