diff --git a/packages/app/e2e/browser/compact-workspace-sheet.spec.ts b/packages/app/e2e/browser/compact-workspace-sheet.spec.ts index 77a2a0882b..990d490d9b 100644 --- a/packages/app/e2e/browser/compact-workspace-sheet.spec.ts +++ b/packages/app/e2e/browser/compact-workspace-sheet.spec.ts @@ -71,6 +71,14 @@ test("compact opens files and subagents in a sheet, leaving the tabs for agents" await closeSheet(page, "linked.ts"); }); + await test.step("a subagent's row shows the facts its sheet shows", async () => { + await page.getByTestId("subagents-track-header").click(); + await expect(page.getByTestId(`subagents-track-row-facts-${child.id}`)).toHaveText( + "Ten second stream · Low · Load test", + ); + await tapSheetBackdrop(page); + }); + await test.step("a subagent opens in a sheet over the subagents list", async () => { await openSubagentFromCompactTrack(page, child.id); diff --git a/packages/app/src/panels/agent-facts-line.ts b/packages/app/src/panels/agent-facts-line.ts index 7d3cd80332..7c01e5ce42 100644 --- a/packages/app/src/panels/agent-facts-line.ts +++ b/packages/app/src/panels/agent-facts-line.ts @@ -1,7 +1,10 @@ import type { AgentMode, AgentModelDefinition } from "@getpaseo/protocol/agent-types"; +import { useShallow } from "zustand/shallow"; import { formatAgentModeLabel } from "@/agent-controls/labels"; import { formatTokenCount } from "@/components/context-window-meter.utils"; import { resolveAgentModelSelection } from "@/composer/agent-controls/utils"; +import { useProvidersSnapshot } from "@/hooks/use-providers-snapshot"; +import { useSessionStore } from "@/stores/session-store"; export interface AgentFactsInput { models: AgentModelDefinition[] | null; @@ -33,6 +36,32 @@ export function buildAgentFactsLine(input: AgentFactsInput): string | null { return parts.join(" · "); } +/** The facts line for a Paseo agent in the session store, kept current as the agent reports. */ +export function useAgentFactsLine(serverId: string, agentId: string): string | null { + const agent = useSessionStore( + useShallow((state) => { + const current = state.sessions[serverId]?.agents.get(agentId); + return current + ? { + provider: current.provider, + cwd: current.cwd, + runtimeModelId: current.runtimeInfo?.model ?? null, + configuredModelId: current.model ?? null, + runtimeThinkingOptionId: current.runtimeInfo?.thinkingOptionId ?? null, + explicitThinkingOptionId: current.thinkingOptionId ?? null, + availableModes: current.availableModes, + currentModeId: current.currentModeId, + contextWindowUsedTokens: current.lastUsage?.contextWindowUsedTokens ?? null, + contextWindowMaxTokens: current.lastUsage?.contextWindowMaxTokens ?? null, + } + : null; + }), + ); + const { entries } = useProvidersSnapshot(serverId, { cwd: agent?.cwd }); + const models = entries?.find((entry) => entry.provider === agent?.provider)?.models ?? null; + return agent ? buildAgentFactsLine({ ...agent, models }) : null; +} + /** The mode control shows the current mode, or the provider's first one until the agent reports. */ function resolveModeLabel(modes: AgentMode[], currentModeId: string | null | undefined) { const mode = modes.find((candidate) => candidate.id === currentModeId) ?? modes[0]; diff --git a/packages/app/src/panels/agent-panel.tsx b/packages/app/src/panels/agent-panel.tsx index a9e11df570..7ae7d0c470 100644 --- a/packages/app/src/panels/agent-panel.tsx +++ b/packages/app/src/panels/agent-panel.tsx @@ -60,9 +60,8 @@ import { useContainerWidthBelow } from "@/hooks/use-container-width"; import { reconcileMissingAgentStateWithPresentAgent } from "@/panels/agent-panel-load-state"; import { TimelineSyncStatus } from "@/timeline/sync-status"; import { usePaneContext, usePaneFocus } from "@/panels/pane-context"; -import { buildAgentFactsLine } from "@/panels/agent-facts-line"; +import { useAgentFactsLine } from "@/panels/agent-facts-line"; import { PaneSubtitle } from "@/panels/pane-subtitle"; -import { useProvidersSnapshot } from "@/hooks/use-providers-snapshot"; import { definePanel, type PanelDescriptor } from "@/panels/panel-registry"; import { RenderProfile } from "@/utils/render-profiler"; import { useHasPluginComposerPills } from "@/plugins"; @@ -1384,28 +1383,7 @@ function TimelineSyncErrorCallout({ * without its composer. */ function SheetAgentFacts({ serverId, agentId }: { serverId: string; agentId: string }) { - const agent = useSessionStore( - useShallow((state) => { - const current = state.sessions[serverId]?.agents.get(agentId); - return current - ? { - provider: current.provider, - cwd: current.cwd, - runtimeModelId: current.runtimeInfo?.model ?? null, - configuredModelId: current.model ?? null, - runtimeThinkingOptionId: current.runtimeInfo?.thinkingOptionId ?? null, - explicitThinkingOptionId: current.thinkingOptionId ?? null, - availableModes: current.availableModes, - currentModeId: current.currentModeId, - contextWindowUsedTokens: current.lastUsage?.contextWindowUsedTokens ?? null, - contextWindowMaxTokens: current.lastUsage?.contextWindowMaxTokens ?? null, - } - : null; - }), - ); - const { entries } = useProvidersSnapshot(serverId, { cwd: agent?.cwd }); - const models = entries?.find((entry) => entry.provider === agent?.provider)?.models ?? null; - const text = agent ? buildAgentFactsLine({ ...agent, models }) : null; + const text = useAgentFactsLine(serverId, agentId); return text ? : null; } diff --git a/packages/app/src/subagents/track.tsx b/packages/app/src/subagents/track.tsx index c1834587d2..1914993700 100644 --- a/packages/app/src/subagents/track.tsx +++ b/packages/app/src/subagents/track.tsx @@ -13,6 +13,7 @@ import { type WorkspaceTabPresentation, } from "@/screens/workspace/workspace-tab-presentation"; import type { Theme } from "@/styles/theme"; +import { useAgentFactsLine } from "@/panels/agent-facts-line"; import { getPanelManifest } from "@/panels/panel-manifest"; import type { SubagentRow } from "./select"; import type { ArchiveFinishedStatus } from "./use-archive-finished"; @@ -219,14 +220,16 @@ function SubagentsTrackRow({ ({ active }: { active: boolean }) => ( <> - - {displayLabel} - - {presentation.subtitle ? ( - - {presentation.subtitle} + + + {displayLabel} - ) : null} + {row.kind === "paseo" ? ( + + ) : ( + + )} + {row.kind === "paseo" ? ( ; +} + +function SubagentFacts({ rowId, text }: { rowId: string; text: string | null }) { + if (!text) return null; + return ( + + {text} + + ); +} + function SubagentRowActions({ rowId, displayLabel, @@ -360,9 +379,24 @@ const styles = StyleSheet.create((theme) => ({ fontSize: theme.fontSize.base, color: theme.colors.foreground, }, - // Trailing metadata — provider context on a subagent row, progress on the archive row. No width - // cap: the panel's own ceiling bounds it. It shrinks twice as fast as the label, so a wordy - // provider subtitle gives way first instead of squeezing the thing that names the row. + // Title over facts. The facts sit under the title rather than beside it, so a long task name + // keeps the row's full width and neither line truncates the other. + rowText: { + flexGrow: 1, + flexShrink: 1, + flexBasis: "auto", + minWidth: 0, + }, + rowTitle: { + fontSize: theme.fontSize.base, + color: theme.colors.foreground, + }, + rowFacts: { + fontSize: theme.fontSize.sm, + color: theme.colors.foregroundMuted, + }, + // Progress on the archive row. It shrinks twice as fast as the label, so a long retry count + // gives way first instead of squeezing the thing that names the row. rowTrailing: { flexShrink: 2, minWidth: 0,