import { useLayoutEffect, useState, type ReactNode } from "preact/compat"; import { activeAgentRunSummary } from "../display/agent"; import { executionState } from "../display/state"; import type { AgentDisplayItem, AgentRunSummary, AgentRunSummaryAgent, DisplayItem, TaskProgressDisplayItem, ToolDisplayItem, } from "../display/types"; import { agentActivityMetaLabel, agentMessagePreview, agentRunSummaryLabel, taskStatusMarker } from "../display/labels"; import { activeTurnId, type ChatTurnLifecycleState } from "../chat-state"; import { createWorkMessageClassName } from "./work-message"; import { shortThreadId, truncate } from "../../../utils"; const AGENT_ROW_MESSAGE_PREVIEW_LIMIT = 120; const AGENT_ACTIVITY_PROMPT_PREVIEW_LIMIT = 96; type ReasoningDisplayItem = ToolDisplayItem & { kind: "reasoning" }; export type WorkItemDisplayItem = TaskProgressDisplayItem | AgentDisplayItem | ReasoningDisplayItem; export interface WorkItemContext { turnLifecycle: ChatTurnLifecycleState; displayItems: readonly DisplayItem[]; openDetails: ReadonlySet; onDetailsToggle?: (key: string, open: boolean) => void; } export function workItemsActiveTurnId(context: Pick): string | null { return activeTurnId(context); } export function activeAgentRunSummaryBlock(context: WorkItemContext): AgentRunSummary | null { return activeAgentRunSummary(context.displayItems, workItemsActiveTurnId(context)); } export function agentRunSummaryNode(summary: AgentRunSummary): ReactNode { return ; } export function workItemNode(item: WorkItemDisplayItem, context: WorkItemContext): ReactNode { if (item.kind === "taskProgress") return ; if (item.kind === "agent") return ; return ; } function AgentRunSummaryItem({ summary }: { summary: AgentRunSummary }): ReactNode { return ( 0 ? "failed" : "running"}>
{agentRunSummaryLabel(summary)}
); } function TaskProgressItem({ item }: { item: TaskProgressDisplayItem }): ReactNode { return ( {item.explanation ?
{item.explanation}
: null} {item.steps.length === 0 ? (
Plan updated
) : (
    {item.steps.map((step) => (
  • {taskStatusMarker(step.status)} {step.step}
  • ))}
)}
); } function AgentItem({ item, context }: { item: AgentDisplayItem; context: WorkItemContext }): ReactNode { const detailsKey = `${item.id}:agent-details`; const [detailsOpen, setDetailsOpen] = useState(context.openDetails.has(detailsKey)); useLayoutEffect(() => { setDetailsOpen(context.openDetails.has(detailsKey)); }, [context.openDetails, detailsKey]); return (
{agentSummaryText(item)}
{ const nextOpen = event.currentTarget.open; setDetailsOpen(nextOpen); context.onDetailsToggle?.(detailsKey, nextOpen); }} > Details
{item.receiverThreadIds.length > 0 ? : null} {item.model ? : null} {item.reasoningEffort ? : null}
{item.prompt ? (
Prompt
{item.prompt}
) : null} {item.agents.length > 0 ? (
    {item.agents.map((agent) => (
  • {shortThreadId(agent.threadId)} {agentStatusLabel(agent.status, agent.message)}
  • ))}
) : null} {item.agents.map((agent) => agent.message && isLongAgentMessage(agent.message) ? (
Agent output {shortThreadId(agent.threadId)}
{agent.message}
) : null, )}
); } function ReasoningItem({ item, context }: { item: ReasoningDisplayItem; context: WorkItemContext }): ReactNode { const active = isReasoningActive(item, context); return (
{active ? "reasoning" : "thought"}
{item.text || (active ? "Reasoning" : "Thought")} {active ? ( . . . ) : null}
); } function WorkMessage({ label, className, state, children, }: { label: string; className: string; state: ReturnType; children: ReactNode; }): ReactNode { const classes = [createWorkMessageClassName(className), state ? `codex-panel__execution codex-panel__execution--${state}` : ""] .filter(Boolean) .join(" "); return (
{label}
{children}
); } function MetaPair({ name, value }: { name: string; value: string }): ReactNode { return ( <>
{name}
{value}
); } function AgentSummaryRows({ summary }: { summary: AgentRunSummary }): ReactNode { if (summary.agents.length === 0 && summary.additionalAgents === 0) return null; return (
    {summary.agents.map((agent) => (
  • {shortThreadId(agent.threadId)} {agentSummaryStatusLabel(agent)}
  • ))} {summary.additionalAgents > 0 ? (
  • +{String(summary.additionalAgents)} more
  • ) : null}
); } function agentSummaryText(item: AgentDisplayItem): string { const target = item.receiverThreadIds.length === 0 ? "" : ` ${item.receiverThreadIds.map(shortThreadId).join(", ")}`; const promptPreview = agentPromptPreview(item.prompt); return `${agentActivityMetaLabel(item.tool)}${target}${promptPreview ? `: ${promptPreview}` : ""} (${item.status})`; } function agentPromptPreview(prompt: string | null): string | null { if (!prompt) return null; const normalized = prompt.trim().replace(/\s+/g, " "); return normalized ? truncate(normalized, AGENT_ACTIVITY_PROMPT_PREVIEW_LIMIT) : null; } function agentStatusLabel(status: string, message: string | null): string { const preview = agentMessagePreview(message, AGENT_ROW_MESSAGE_PREVIEW_LIMIT); return preview ? `${status}: ${preview}` : status; } function agentSummaryStatusLabel(agent: AgentRunSummaryAgent): string { return agent.messagePreview ? `${agent.status}: ${agent.messagePreview}` : agent.status; } function isLongAgentMessage(message: string): boolean { return message.length > AGENT_ROW_MESSAGE_PREVIEW_LIMIT || message.includes("\n"); } function isReasoningActive(item: ReasoningDisplayItem, context: WorkItemContext): boolean { const activeTurn = workItemsActiveTurnId(context); if (!activeTurn || item.turnId !== activeTurn) return false; if (executionState(item) === "completed") return false; const latestActiveTurnItem = [...context.displayItems].reverse().find((candidate) => candidate.turnId === activeTurn); return latestActiveTurnItem?.id === item.id; }