From ff64d0ef41befe27ce550d19da15714b7615a0f7 Mon Sep 17 00:00:00 2001 From: Charles Packer Date: Sat, 7 Feb 2026 23:37:18 -0800 Subject: [PATCH] fix: unify subagent model display with footer formatting (#863) --- src/cli/components/SubagentGroupDisplay.tsx | 41 +++++++++++++++++-- src/cli/components/SubagentGroupStatic.tsx | 24 ++++++++++- src/cli/helpers/subagentDisplay.ts | 34 ++++++++++++++++ src/tests/cli/subagentDisplay.test.ts | 44 +++++++++++++++++++++ 4 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 src/tests/cli/subagentDisplay.test.ts diff --git a/src/cli/components/SubagentGroupDisplay.tsx b/src/cli/components/SubagentGroupDisplay.tsx index 8d0532a..4ceb2b3 100644 --- a/src/cli/components/SubagentGroupDisplay.tsx +++ b/src/cli/components/SubagentGroupDisplay.tsx @@ -18,7 +18,11 @@ import { Box, useInput } from "ink"; import { memo, useSyncExternalStore } from "react"; import { useAnimation } from "../contexts/AnimationContext.js"; -import { formatStats, getTreeChars } from "../helpers/subagentDisplay.js"; +import { + formatStats, + getSubagentModelDisplay, + getTreeChars, +} from "../helpers/subagentDisplay.js"; import { getSnapshot, type SubagentState, @@ -78,6 +82,7 @@ const AgentRow = memo( agent.totalTokens, isRunning, ); + const modelDisplay = getSubagentModelDisplay(agent.model); const lastTool = agent.toolCalls[agent.toolCalls.length - 1]; // Condensed mode: simplified view to reduce re-renders when overflowing @@ -101,8 +106,23 @@ const AgentRow = memo( {" · "} {agent.type.toLowerCase()} - {agent.model ? ` · ${agent.model}` : ""} + {modelDisplay && ( + <> + {` · ${modelDisplay.label}`} + {modelDisplay.isByokProvider && ( + + {" ▲"} + + )} + + )} {/* Simple status line */} @@ -142,7 +162,22 @@ const AgentRow = memo( {" · "} {agent.type.toLowerCase()} - {agent.model ? ` · ${agent.model}` : ""} + + {modelDisplay && ( + <> + {` · ${modelDisplay.label}`} + {modelDisplay.isByokProvider && ( + + {" ▲"} + + )} + + )} + {" · "} {stats} diff --git a/src/cli/components/SubagentGroupStatic.tsx b/src/cli/components/SubagentGroupStatic.tsx index 1196bd1..4c836be 100644 --- a/src/cli/components/SubagentGroupStatic.tsx +++ b/src/cli/components/SubagentGroupStatic.tsx @@ -15,7 +15,11 @@ import { Box } from "ink"; import { memo } from "react"; -import { formatStats, getTreeChars } from "../helpers/subagentDisplay.js"; +import { + formatStats, + getSubagentModelDisplay, + getTreeChars, +} from "../helpers/subagentDisplay.js"; import { useTerminalWidth } from "../hooks/useTerminalWidth.js"; import { colors } from "./colors.js"; import { Text } from "./Text"; @@ -59,6 +63,7 @@ const AgentRow = memo(({ agent, isLast }: AgentRowProps) => { const isRunning = agent.status === "running"; const shouldDim = isRunning && !agent.isBackground; const stats = formatStats(agent.toolCount, agent.totalTokens, isRunning); + const modelDisplay = getSubagentModelDisplay(agent.model); return ( @@ -75,7 +80,22 @@ const AgentRow = memo(({ agent, isLast }: AgentRowProps) => { {" · "} {agent.type.toLowerCase()} - {agent.model ? ` · ${agent.model}` : ""} + + {modelDisplay && ( + <> + {` · ${modelDisplay.label}`} + {modelDisplay.isByokProvider && ( + + {" ▲"} + + )} + + )} + {" · "} {stats} diff --git a/src/cli/helpers/subagentDisplay.ts b/src/cli/helpers/subagentDisplay.ts index 72db8f0..32f69e3 100644 --- a/src/cli/helpers/subagentDisplay.ts +++ b/src/cli/helpers/subagentDisplay.ts @@ -3,6 +3,8 @@ * * Used by both SubagentGroupDisplay (live) and SubagentGroupStatic (frozen). */ +import { getModelShortName, resolveModel } from "../../agent/model"; +import { OPENAI_CODEX_PROVIDER_NAME } from "../../providers/openai-codex-provider"; /** * Format tool count and token statistics for display @@ -46,3 +48,35 @@ export function getTreeChars(isLast: boolean): { continueChar: isLast ? " " : "│ ", }; } + +export interface SubagentModelDisplay { + label: string; + isByokProvider: boolean; + isOpenAICodexProvider: boolean; +} + +/** + * Format a subagent model identifier using the same logic as the input footer: + * short label (if known) + provider-based BYOK marker eligibility. + */ +export function getSubagentModelDisplay( + model: string | undefined, +): SubagentModelDisplay | null { + if (!model) return null; + + // Normalize model IDs (e.g. "haiku") to handles before formatting. + const normalized = resolveModel(model) ?? model; + const slashIndex = normalized.indexOf("/"); + const provider = + slashIndex >= 0 ? normalized.slice(0, slashIndex) : normalized; + const isOpenAICodexProvider = provider === OPENAI_CODEX_PROVIDER_NAME; + const isByokProvider = provider.startsWith("lc-") || isOpenAICodexProvider; + const label = + getModelShortName(normalized) ?? normalized.split("/").pop() ?? normalized; + + return { + label, + isByokProvider, + isOpenAICodexProvider, + }; +} diff --git a/src/tests/cli/subagentDisplay.test.ts b/src/tests/cli/subagentDisplay.test.ts new file mode 100644 index 0000000..466f739 --- /dev/null +++ b/src/tests/cli/subagentDisplay.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, test } from "bun:test"; +import { getSubagentModelDisplay } from "../../cli/helpers/subagentDisplay"; + +describe("getSubagentModelDisplay", () => { + test("formats known model IDs using short labels", () => { + const display = getSubagentModelDisplay("haiku"); + expect(display).toEqual({ + label: "Haiku 4.5", + isByokProvider: false, + isOpenAICodexProvider: false, + }); + }); + + test("formats non-BYOK handles using short labels", () => { + const display = getSubagentModelDisplay( + "anthropic/claude-haiku-4-5-20251001", + ); + expect(display).toEqual({ + label: "Haiku 4.5", + isByokProvider: false, + isOpenAICodexProvider: false, + }); + }); + + test("marks lc-* handles as BYOK", () => { + const display = getSubagentModelDisplay( + "lc-anthropic/claude-haiku-4-5-20251001", + ); + expect(display).toEqual({ + label: "claude-haiku-4-5-20251001", + isByokProvider: true, + isOpenAICodexProvider: false, + }); + }); + + test("marks chatgpt-plus-pro handles as BYOK", () => { + const display = getSubagentModelDisplay("chatgpt-plus-pro/gpt-5.2-codex"); + expect(display).toEqual({ + label: "GPT-5.2 Codex", + isByokProvider: true, + isOpenAICodexProvider: true, + }); + }); +});