authorgravatar for 76637177+SunkenInTime@users.noreply.github.comDara Adedeji <76637177+SunkenInTime@users.noreply.github.com> 2026-10-04 01:41:52-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-04 02:41:52-03:00
logdaa1d0ed947c276cb74b08025b5903e2ffe31278
tree1009f4db3e413f9dfce82b90ae85d1c09fa6824a
parenta92c43b80ba307f112e0ae03bf64d841475cf326
signature Signed by PGP key B5690EEEBB952194

fix(web): subagent cards name the provider account (#15493)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

6 files changed, 34 insertions(+), 3 deletions(-)

apps/mobile/src/features/threads/SubagentRow.tsx+2-1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1import { scopeProjectRef, scopeThreadRef } from "@t3tools/client-runtime/environment";1import { scopeProjectRef, scopeThreadRef } from "@t3tools/client-runtime/environment";
2import { resolveProviderInstanceDisplayName } from "@t3tools/client-runtime/state/provider-instance-display";
2import { resolveSubagentMetadata } from "@t3tools/client-runtime/state/subagent-display";3import { resolveSubagentMetadata } from "@t3tools/client-runtime/state/subagent-display";
3import type { EnvironmentId, OrchestrationV2Subagent } from "@t3tools/contracts";4import type { EnvironmentId, OrchestrationV2Subagent } from "@t3tools/contracts";
4import type { ReactNode } from "react";5import type { ReactNode } from "react";
...@@ -125,7 +126,7 @@ function SubagentMetadata(props: {...@@ -125,7 +126,7 @@ function SubagentMetadata(props: {
125 size={12}126 size={12}
126 />127 />
127 <Text className="min-w-0 shrink text-xs text-foreground-muted" numberOfLines={1}>128 <Text className="min-w-0 shrink text-xs text-foreground-muted" numberOfLines={1}>
128 {provider?.displayName ? `${provider.displayName} · ` : ""}129 {provider ? `${resolveProviderInstanceDisplayName(provider)} · ` : ""}
129 {modelLabel}130 {modelLabel}
130 </Text>131 </Text>
131 {workspace.map(({ label, value }) => (132 {workspace.map(({ label, value }) => (
apps/web/src/components/ChatView.tsx+8
...@@ -315,6 +315,7 @@ import {...@@ -315,6 +315,7 @@ import {
315 applyProviderInstanceSettings,315 applyProviderInstanceSettings,
316 deriveProviderInstanceEntries,316 deriveProviderInstanceEntries,
317 NO_PROVIDER_MODEL_SELECTION,317 NO_PROVIDER_MODEL_SELECTION,
318 shouldShowInstanceBadge,
318 sortProviderInstanceEntries,319 sortProviderInstanceEntries,
319} from "../providerInstances";320} from "../providerInstances";
320import {321import {
...@@ -11111,6 +11112,13 @@ export default function ChatView(props: ChatViewProps) {...@@ -11111,6 +11112,13 @@ export default function ChatView(props: ChatViewProps) {
11111 {showProviderSubagentBar ? (11112 {showProviderSubagentBar ? (
11112 <ProviderSubagentBar11113 <ProviderSubagentBar
11113 provider={selectedProviderEntry ?? null}11114 provider={selectedProviderEntry ?? null}
11115 showInstanceBadge={
11116 selectedProviderEntry !== undefined &&
11117 shouldShowInstanceBadge(
11118 selectedProviderEntry,
11119 providerInstanceEntries,
11120 )
11121 }
11114 modelLabel={providerSubagentModelLabel}11122 modelLabel={providerSubagentModelLabel}
11115 effortLabel={providerSubagentEffortLabel}11123 effortLabel={providerSubagentEffortLabel}
11116 status={providerSubagentStatus}11124 status={providerSubagentStatus}
apps/web/src/components/chat/ProviderSubagentBar.tsx+4
...@@ -18,6 +18,8 @@ import { ProviderInstanceIcon } from "./ProviderInstanceIcon";...@@ -18,6 +18,8 @@ import { ProviderInstanceIcon } from "./ProviderInstanceIcon";
18export function ProviderSubagentBar(props: {18export function ProviderSubagentBar(props: {
19 /** The provider running the subagent; no icon while its catalog loads. */19 /** The provider running the subagent; no icon while its catalog loads. */
20 readonly provider: ProviderInstanceEntry | null;20 readonly provider: ProviderInstanceEntry | null;
21 /** Several accounts on this provider: badge the glyph, as the composer does. */
22 readonly showInstanceBadge: boolean;
21 readonly modelLabel: string;23 readonly modelLabel: string;
22 /** Reasoning effort as the composer names it, when the subagent has one. */24 /** Reasoning effort as the composer names it, when the subagent has one. */
23 readonly effortLabel: string | null;25 readonly effortLabel: string | null;
...@@ -60,8 +62,10 @@ export function ProviderSubagentBar(props: {...@@ -60,8 +62,10 @@ export function ProviderSubagentBar(props: {
60 accentColor={props.provider.accentColor}62 accentColor={props.provider.accentColor}
61 acpRegistryAgentId={props.provider.acpRegistryAgentId}63 acpRegistryAgentId={props.provider.acpRegistryAgentId}
62 acpRegistryIconUrl={props.provider.acpRegistryIconUrl}64 acpRegistryIconUrl={props.provider.acpRegistryIconUrl}
65 showBadge={props.showInstanceBadge}
63 className="size-4 shrink-0"66 className="size-4 shrink-0"
64 iconClassName="size-4"67 iconClassName="size-4"
68 badgeClassName="right-[-0.125rem] bottom-[-0.125rem] h-3 min-w-3 px-0.5 text-5xs"
65 />69 />
66 ) : null}70 ) : null}
67 <span className="min-w-0 truncate font-medium text-foreground">{props.modelLabel}</span>71 <span className="min-w-0 truncate font-medium text-foreground">{props.modelLabel}</span>
apps/web/src/components/chat/SubagentTooltipContent.tsx+15-2
...@@ -23,12 +23,15 @@ import { ThreadHoverCard } from "../ThreadHoverCard";...@@ -23,12 +23,15 @@ import { ThreadHoverCard } from "../ThreadHoverCard";
23import { MiddleTruncate } from "../ui/middle-truncate";23import { MiddleTruncate } from "../ui/middle-truncate";
24import { ProviderInstanceIcon } from "./ProviderInstanceIcon";24import { ProviderInstanceIcon } from "./ProviderInstanceIcon";
25import { cn } from "~/lib/utils";25import { cn } from "~/lib/utils";
26import { deriveProviderInstanceEntries, shouldShowInstanceBadge } from "../../providerInstances";
2627
27/** Geometry and preview limits stay identical in lineage and timeline tooltips. */28/** Geometry and preview limits stay identical in lineage and timeline tooltips. */
28export function SubagentTooltipContent(props: {29export function SubagentTooltipContent(props: {
29 title: string;30 title: string;
30 model: string | null;31 model: string | null;
31 provider?: ServerProvider | undefined;32 provider?: ServerProvider | undefined;
33 /** The environment's instances; with several accounts on one provider, the card names this one. */
34 providers?: ReadonlyArray<ServerProvider> | undefined;
32 driver?: ProviderDriverKind | undefined;35 driver?: ProviderDriverKind | undefined;
33 elapsed?: ReactNode;36 elapsed?: ReactNode;
34 parentThread?: Pick<OrchestrationV2ThreadShell, "projectId" | "worktreePath"> | undefined;37 parentThread?: Pick<OrchestrationV2ThreadShell, "projectId" | "worktreePath"> | undefined;
...@@ -42,6 +45,9 @@ export function SubagentTooltipContent(props: {...@@ -42,6 +45,9 @@ export function SubagentTooltipContent(props: {
42 const { modelLabel, workspace: metadata } = resolveSubagentMetadata(props);45 const { modelLabel, workspace: metadata } = resolveSubagentMetadata(props);
43 const preview = subagentDetailPreview(props);46 const preview = subagentDetailPreview(props);
44 const driver = props.provider?.driver ?? props.driver;47 const driver = props.provider?.driver ?? props.driver;
48 const entries = deriveProviderInstanceEntries(props.providers ?? []);
49 const entry = entries.find((candidate) => candidate.instanceId === props.provider?.instanceId);
50 const showInstanceBadge = entry !== undefined && shouldShowInstanceBadge(entry, entries);
45 const working = ["running", "in_progress", "pending", "waiting"].includes(props.status);51 const working = ["running", "in_progress", "pending", "waiting"].includes(props.status);
46 const failed = ["failed", "error"].includes(props.status);52 const failed = ["failed", "error"].includes(props.status);
47 const StatusIcon = working53 const StatusIcon = working
...@@ -57,14 +63,21 @@ export function SubagentTooltipContent(props: {...@@ -57,14 +63,21 @@ export function SubagentTooltipContent(props: {
57 {driver ? (63 {driver ? (
58 <ProviderInstanceIcon64 <ProviderInstanceIcon
59 driverKind={driver}65 driverKind={driver}
60 displayName={props.provider?.displayName ?? driver}66 displayName={entry?.displayName ?? props.provider?.displayName ?? driver}
67 accentColor={entry?.accentColor}
61 acpRegistryIconUrl={props.provider?.iconUrl}68 acpRegistryIconUrl={props.provider?.iconUrl}
69 // Same treatment as the sidebar card: accent dot, account in the label.
70 showBadge={showInstanceBadge && entry?.accentColor !== undefined}
71 badgeContent="none"
72 badgeClassName="h-2 min-w-2 px-0"
62 iconClassName="size-3 shrink-0 grayscale opacity-60"73 iconClassName="size-3 shrink-0 grayscale opacity-60"
63 />74 />
64 ) : (75 ) : (
65 <BotIcon className="size-3 shrink-0" />76 <BotIcon className="size-3 shrink-0" />
66 )}77 )}
67 <span className="min-w-0 truncate text-foreground/75">{modelLabel}</span>78 <span className="min-w-0 truncate text-foreground/75">
79 {showInstanceBadge ? `${modelLabel} · ${entry.displayName}` : modelLabel}
80 </span>
68 </div>81 </div>
69 <div className="flex min-w-0 items-center justify-between gap-4">82 <div className="flex min-w-0 items-center justify-between gap-4">
70 <span83 <span
apps/web/src/components/chat/ThreadRelationshipsControl.tsx+1
...@@ -384,6 +384,7 @@ export function ThreadRelationshipsPanel(props: {...@@ -384,6 +384,7 @@ export function ThreadRelationshipsPanel(props: {
384 title={threadTitle}384 title={threadTitle}
385 model={agent.model}385 model={agent.model}
386 provider={provider}386 provider={provider}
387 providers={providers}
387 driver={providerDriver}388 driver={providerDriver}
388 elapsed={<AgentElapsed agent={agent} />}389 elapsed={<AgentElapsed agent={agent} />}
389 status={agent.status}390 status={agent.status}
apps/web/src/components/chat/V2LifecycleRow.tsx+4
...@@ -217,6 +217,7 @@ export function V2LifecycleRow(props: {...@@ -217,6 +217,7 @@ export function V2LifecycleRow(props: {
217 provider={props.providerStatuses.find(217 provider={props.providerStatuses.find(
218 (provider) => provider.instanceId === item.providerInstanceId,218 (provider) => provider.instanceId === item.providerInstanceId,
219 )}219 )}
220 providers={props.providerStatuses}
220 title={formatSubagentDisplayTitle(item.title ?? "Subagent")}221 title={formatSubagentDisplayTitle(item.title ?? "Subagent")}
221 result={item.result}222 result={item.result}
222 progress={item.progress}223 progress={item.progress}
...@@ -374,6 +375,7 @@ export function SubagentNotificationLink(props: {...@@ -374,6 +375,7 @@ export function SubagentNotificationLink(props: {
374 provider={props.providerStatuses.find(375 provider={props.providerStatuses.find(
375 (provider) => provider.instanceId === agent.providerInstanceId,376 (provider) => provider.instanceId === agent.providerInstanceId,
376 )}377 )}
378 providers={props.providerStatuses}
377 title={formatSubagentDisplayTitle(agent.title ?? "Subagent")}379 title={formatSubagentDisplayTitle(agent.title ?? "Subagent")}
378 result={agent.result}380 result={agent.result}
379 progress={agent.progress}381 progress={agent.progress}
...@@ -395,6 +397,7 @@ function SubagentTimelineLink(props: {...@@ -395,6 +397,7 @@ function SubagentTimelineLink(props: {
395 readonly subagentId: NodeId;397 readonly subagentId: NodeId;
396 readonly driver: ProviderDriverKind;398 readonly driver: ProviderDriverKind;
397 readonly provider: ServerProvider | undefined;399 readonly provider: ServerProvider | undefined;
400 readonly providers: ReadonlyArray<ServerProvider>;
398 readonly title: string;401 readonly title: string;
399 readonly result: string | null;402 readonly result: string | null;
400 readonly progress: string | undefined;403 readonly progress: string | undefined;
...@@ -538,6 +541,7 @@ function SubagentTimelineTooltip(...@@ -538,6 +541,7 @@ function SubagentTimelineTooltip(
538 title={formatSubagentDisplayTitle(child?.title ?? props.title)}541 title={formatSubagentDisplayTitle(child?.title ?? props.title)}
539 model={props.model}542 model={props.model}
540 provider={props.provider}543 provider={props.provider}
544 providers={props.providers}
541 driver={props.driver}545 driver={props.driver}
542 elapsed={props.elapsed}546 elapsed={props.elapsed}
543 status={props.status}547 status={props.status}