| author | |
| committer | |
| log | daa1d0ed947c276cb74b08025b5903e2ffe31278 |
| tree | 1009f4db3e413f9dfce82b90ae85d1c09fa6824a |
| parent | a92c43b80ba307f112e0ae03bf64d841475cf326 |
| signature | Signed by PGP key B5690EEEBB952194 |
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 @@ |
| 1 | import { scopeProjectRef, scopeThreadRef } from "@t3tools/client-runtime/environment"; | 1 | import { scopeProjectRef, scopeThreadRef } from "@t3tools/client-runtime/environment"; |
| 2 | import { resolveProviderInstanceDisplayName } from "@t3tools/client-runtime/state/provider-instance-display"; | ||
| 2 | import { resolveSubagentMetadata } from "@t3tools/client-runtime/state/subagent-display"; | 3 | import { resolveSubagentMetadata } from "@t3tools/client-runtime/state/subagent-display"; |
| 3 | import type { EnvironmentId, OrchestrationV2Subagent } from "@t3tools/contracts"; | 4 | import type { EnvironmentId, OrchestrationV2Subagent } from "@t3tools/contracts"; |
| 4 | import type { ReactNode } from "react"; | 5 | import 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"; |
| 320 | import { | 321 | import { |
| ... | @@ -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 | <ProviderSubagentBar | 11113 | <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"; |
| 18 | export function ProviderSubagentBar(props: { | 18 | export 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"; |
| 23 | import { MiddleTruncate } from "../ui/middle-truncate"; | 23 | import { MiddleTruncate } from "../ui/middle-truncate"; |
| 24 | import { ProviderInstanceIcon } from "./ProviderInstanceIcon"; | 24 | import { ProviderInstanceIcon } from "./ProviderInstanceIcon"; |
| 25 | import { cn } from "~/lib/utils"; | 25 | import { cn } from "~/lib/utils"; |
| 26 | import { deriveProviderInstanceEntries, shouldShowInstanceBadge } from "../../providerInstances"; | ||
| 26 | 27 | ||
| 27 | /** Geometry and preview limits stay identical in lineage and timeline tooltips. */ | 28 | /** Geometry and preview limits stay identical in lineage and timeline tooltips. */ |
| 28 | export function SubagentTooltipContent(props: { | 29 | export 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 = working | 53 | const StatusIcon = working |
| ... | @@ -57,14 +63,21 @@ export function SubagentTooltipContent(props: { | ... | @@ -57,14 +63,21 @@ export function SubagentTooltipContent(props: { |
| 57 | {driver ? ( | 63 | {driver ? ( |
| 58 | <ProviderInstanceIcon | 64 | <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 | <span | 83 | <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} |