From baba54d6b65d90351d25baba9a7abc74604f4908 Mon Sep 17 00:00:00 2001 From: Taylor Ho Date: Sun, 14 Jun 2026 23:21:05 -0700 Subject: [PATCH] feat(agents): add raw ACP activity view toggle - Add a Raw switch to the agent activity panel header so users can swap between the formatted transcript and raw ACP JSON-RPC payloads. - Update the activity title to reflect raw mode and render the raw feed as the full panel content instead of a nested dark card. - Keep the existing transcript view unchanged by default and scope the raw-mode reset to agent/channel changes. --- .../agents/ui/ManagedAgentSessionPanel.tsx | 26 +++++++++- .../src/features/agents/ui/RawEventRail.tsx | 44 +++++----------- .../channels/ui/AgentSessionThreadPanel.tsx | 50 +++++++++++++++++-- 3 files changed, 84 insertions(+), 36 deletions(-) diff --git a/desktop/src/features/agents/ui/ManagedAgentSessionPanel.tsx b/desktop/src/features/agents/ui/ManagedAgentSessionPanel.tsx index f27e734fc..8f67ca870 100644 --- a/desktop/src/features/agents/ui/ManagedAgentSessionPanel.tsx +++ b/desktop/src/features/agents/ui/ManagedAgentSessionPanel.tsx @@ -31,6 +31,7 @@ type ManagedAgentSessionPanelProps = { compact?: boolean; emptyDescription?: string; isWorking?: boolean; + rawLayout?: "responsive" | "exclusive"; showHeader?: boolean; showInterventionHint?: boolean; showRaw?: boolean; @@ -44,6 +45,7 @@ export function ManagedAgentSessionPanel({ compact = false, emptyDescription = "Mention this agent in a channel to watch the next turn.", isWorking = false, + rawLayout = "responsive", showHeader = true, showInterventionHint = false, showRaw = true, @@ -108,6 +110,7 @@ export function ManagedAgentSessionPanel({ hasObserver={hasObserver} isWorking={isWorking} profiles={profiles} + rawLayout={rawLayout} showInterventionHint={showInterventionHint} showRaw={showRaw} transcript={scopedTranscript} @@ -161,6 +164,7 @@ function SessionBody({ hasObserver, isWorking, profiles, + rawLayout, showInterventionHint, showRaw, transcript, @@ -174,10 +178,26 @@ function SessionBody({ hasObserver: boolean; isWorking: boolean; profiles?: UserProfileLookup; + rawLayout: "responsive" | "exclusive"; showInterventionHint: boolean; showRaw: boolean; transcript: TranscriptItem[]; }) { + if (showRaw && rawLayout === "exclusive") { + return ( + <> + + + {errorMessage ? ( +

+ + {errorMessage} +

+ ) : null} + + ); + } + return ( <> {!hasObserver ? ( @@ -187,7 +207,7 @@ function SessionBody({ ) : (
- {showRaw ? : null} + {showRaw && rawLayout === "responsive" ? ( + + ) : null}
)} diff --git a/desktop/src/features/agents/ui/RawEventRail.tsx b/desktop/src/features/agents/ui/RawEventRail.tsx index 74a655b77..89d2ac3a2 100644 --- a/desktop/src/features/agents/ui/RawEventRail.tsx +++ b/desktop/src/features/agents/ui/RawEventRail.tsx @@ -1,46 +1,28 @@ -import * as React from "react"; - -import { Button } from "@/shared/ui/button"; import type { ObserverEvent } from "./agentSessionTypes"; import { describeRawEvent } from "./agentSessionTranscript"; export function RawEventRail({ events }: { events: ObserverEvent[] }) { - const [expanded, setExpanded] = React.useState(false); - const visible = expanded ? events : events.slice(-18); - return ( - + ); } diff --git a/desktop/src/features/channels/ui/AgentSessionThreadPanel.tsx b/desktop/src/features/channels/ui/AgentSessionThreadPanel.tsx index 180b4c032..d349b35f3 100644 --- a/desktop/src/features/channels/ui/AgentSessionThreadPanel.tsx +++ b/desktop/src/features/channels/ui/AgentSessionThreadPanel.tsx @@ -1,4 +1,5 @@ -import { ArrowLeft, CircleDot, Octagon, X } from "lucide-react"; +import * as React from "react"; +import { ArrowLeft, CircleDot, Octagon, TerminalSquare, X } from "lucide-react"; import { toast } from "sonner"; import { ManagedAgentSessionPanel } from "@/features/agents/ui/ManagedAgentSessionPanel"; @@ -25,6 +26,7 @@ import { PANEL_SINGLE_COLUMN_HEADER_LAYER_CLASS, } from "@/shared/ui/OverlayPanelBackdrop"; import { THREAD_PANEL_MIN_WIDTH_PX } from "@/shared/hooks/useThreadPanelWidth"; +import { Switch } from "@/shared/ui/switch"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip"; import type { ChannelAgentSessionAgent } from "./useChannelAgentSessions"; @@ -60,6 +62,19 @@ export function AgentSessionThreadPanel({ useEscapeKey(onClose, isOverlay || isSinglePanelView); const { ref: scrollRef, onScroll } = useStickToBottom(); + const rawFeedScopeKey = `${agent.pubkey}:${channel.id}`; + const [rawFeedState, setRawFeedState] = React.useState(() => ({ + scopeKey: rawFeedScopeKey, + show: false, + })); + const showRawFeed = + rawFeedState.scopeKey === rawFeedScopeKey && rawFeedState.show; + const handleRawFeedChange = React.useCallback( + (checked: boolean) => { + setRawFeedState({ scopeKey: rawFeedScopeKey, show: checked }); + }, + [rawFeedScopeKey], + ); async function handleInterruptTurn() { try { @@ -87,6 +102,32 @@ export function AgentSessionThreadPanel({ Live ) : null} + {isLive ? ( +
+ + +
+ ) : null} {isLive && isWorking ? ( @@ -140,7 +181,9 @@ export function AgentSessionThreadPanel({ > - Activity + + {showRawFeed ? "Raw ACP Activity" : "Activity"} + {agentHeaderActions} @@ -164,9 +207,10 @@ export function AgentSessionThreadPanel({ emptyDescription={`Mention ${agent.name} in the channel to see its work here.`} isWorking={isWorking} profiles={profiles} + rawLayout="exclusive" showHeader={false} showInterventionHint={canInterruptTurn} - showRaw={false} + showRaw={showRawFeed} /> );