@@ -2,10 +2,15 @@ import * as Dialog from "../../components/ui/Dialog";
22import { useAtomValue } from "@effect/atom-react" ;
33import { scopeThreadRef } from "@t3tools/client-runtime/environment" ;
44import { resolveHandoffEndpoints , type HandoffEndpoints } from "@t3tools/client-runtime/handoff" ;
5- import { resolveProviderInstanceDisplayName } from "@t3tools/client-runtime/state/provider-instance-display" ;
5+ import {
6+ normalizeProviderAccentColor ,
7+ resolveProviderInstanceDisplayName ,
8+ shouldShowInstanceBadge ,
9+ } from "@t3tools/client-runtime/state/provider-instance-display" ;
610import type {
711 EnvironmentId ,
812 OrchestrationV2ProjectedTurnItem ,
13+ ProviderInstanceConfig ,
914 ProviderInstanceId ,
1015 ServerProvider ,
1116} from "@t3tools/contracts" ;
@@ -14,7 +19,7 @@ import { useMemo } from "react";
1419import { Pressable , View , type ColorValue } from "react-native" ;
1520import { AppText as Text } from "../../components/ui/AppText" ;
1621import { SymbolView } from "../../components/ui/AppSymbol" ;
17- import { ProviderIcon } from "../../components/ProviderIcon" ;
22+ import { ProviderIcon , ProviderInstanceIcon } from "../../components/ProviderIcon" ;
1823import { environmentThreadDetails } from "../../state/threads" ;
1924import { serverEnvironment } from "../../state/server" ;
2025import { ThreadContextDivider } from "./thread-context-divider" ;
@@ -39,6 +44,7 @@ export function ThreadHandoffRow(props: {
3944 environmentId : EnvironmentId ;
4045 projectedItem : OrchestrationV2ProjectedTurnItem ;
4146 iconColor : ColorValue ;
47+ surfaceColor : string ;
4248} ) {
4349 const { item } = props . projectedItem ;
4450 const endpointsAtom = useMemo (
@@ -53,6 +59,7 @@ export function ThreadHandoffRow(props: {
5359 environmentId = { props . environmentId }
5460 endpoints = { endpoints }
5561 iconColor = { color }
62+ surfaceColor = { props . surfaceColor }
5663 label = "Context handoff"
5764 failed = { item . status === "failed" }
5865 />
@@ -63,11 +70,17 @@ export function ThreadHandoffDivider(props: {
6370 environmentId : EnvironmentId ;
6471 endpoints : HandoffEndpoints ;
6572 iconColor : ColorValue ;
73+ surfaceColor : string ;
6674 label : string ;
6775 failed ?: boolean ;
6876} ) {
6977 const config = useAtomValue ( serverEnvironment . configValueAtom ( props . environmentId ) ) ;
7078 const { endpoints } = props ;
79+ const endpointProps = {
80+ providers : config ?. providers ?? [ ] ,
81+ providerInstances : config ?. settings . providerInstances ,
82+ surfaceColor : props . surfaceColor ,
83+ } ;
7184 return (
7285 < ThreadContextDivider
7386 label = { props . label }
@@ -81,7 +94,7 @@ export function ThreadHandoffDivider(props: {
8194 key = { `${ endpoint . instanceId } :${ endpoint . model ?? "" } ` }
8295 className = "max-w-full flex-row items-center"
8396 >
84- < HandoffEndpoint { ...endpoint } providers = { config ?. providers ?? [ ] } />
97+ < HandoffEndpoint { ...endpoint } { ... endpointProps } />
8598 { index < endpoints . from . length - 1 ? (
8699 < Text accessible = { false } className = "text-xs text-foreground-muted" >
87100 ,
@@ -92,7 +105,7 @@ export function ThreadHandoffDivider(props: {
92105 { endpoints . from . length > 0 ? (
93106 < SymbolView name = "arrow.right" size = { 12 } tintColor = { props . iconColor } />
94107 ) : null }
95- < HandoffEndpoint { ...endpoints . to } providers = { config ?. providers ?? [ ] } />
108+ < HandoffEndpoint { ...endpoints . to } { ... endpointProps } />
96109 </ View >
97110 </ ThreadContextDivider >
98111 ) ;
@@ -102,6 +115,8 @@ function HandoffEndpoint(props: {
102115 instanceId : ProviderInstanceId ;
103116 model ?: string | undefined ;
104117 providers : ReadonlyArray < ServerProvider > ;
118+ providerInstances : Readonly < Record < string , ProviderInstanceConfig > > | undefined ;
119+ surfaceColor : string ;
105120} ) {
106121 const provider = props . providers . find ( ( candidate ) => candidate . instanceId === props . instanceId ) ;
107122 const model = provider ?. models . find ( ( candidate ) => candidate . slug === props . model ) ;
@@ -110,12 +125,36 @@ function HandoffEndpoint(props: {
110125 model ?. name ??
111126 props . model ??
112127 ( provider ? resolveProviderInstanceDisplayName ( provider ) : props . instanceId ) ;
128+ const accentColor = normalizeProviderAccentColor ( provider ?. accentColor ) ;
129+ // Same account badge as the thread rows: a handoff between two accounts of
130+ // one provider would otherwise show the same glyph on both sides. Settings
131+ // carry each ACP instance's agent, which tells its glyph apart.
132+ const badgeEntry = ( candidate : ServerProvider ) => {
133+ const config = props . providerInstances ?. [ candidate . instanceId ] ?. config ;
134+ // A local ACP command has no registry agent, as on web.
135+ const agentId =
136+ typeof config === "object" && config && Reflect . get ( config , "source" ) !== "local"
137+ ? Reflect . get ( config , "agentId" )
138+ : null ;
139+ return {
140+ driverKind : candidate . driver ,
141+ ...( typeof agentId === "string" && agentId . trim ( )
142+ ? { acpRegistryAgentId : agentId . trim ( ) }
143+ : { } ) ,
144+ } ;
145+ } ;
146+ const showBadge =
147+ provider !== undefined &&
148+ shouldShowInstanceBadge (
149+ { ...badgeEntry ( provider ) , accentColor } ,
150+ props . providers . map ( badgeEntry ) ,
151+ ) ;
113152 return (
114153 < Pressable
115154 accessibilityRole = "button"
116155 accessibilityLabel = { label }
117156 accessibilityHint = "Show provider account"
118- className = "min-h-6 max-w-full flex-row items-center justify-center gap-1"
157+ className = "min-h-6 max-w-full flex-row items-center justify-center gap-1.5 "
119158 hitSlop = { { top : 8 , bottom : 8 } }
120159 onPress = { ( ) =>
121160 Dialog . alert (
@@ -124,11 +163,23 @@ function HandoffEndpoint(props: {
124163 )
125164 }
126165 >
127- < ProviderIcon
128- provider = { provider ?. driver ?? props . instanceId }
129- iconUrl = { provider ?. iconUrl }
130- size = { 12 }
131- />
166+ { showBadge && provider !== undefined ? (
167+ < ProviderInstanceIcon
168+ provider = { provider . driver }
169+ iconUrl = { provider . iconUrl }
170+ size = { 14 }
171+ displayName = { resolveProviderInstanceDisplayName ( provider ) }
172+ accentColor = { accentColor }
173+ showBadge
174+ surfaceColor = { props . surfaceColor }
175+ />
176+ ) : (
177+ < ProviderIcon
178+ provider = { provider ?. driver ?? props . instanceId }
179+ iconUrl = { provider ?. iconUrl }
180+ size = { 12 }
181+ />
182+ ) }
132183 < Text numberOfLines = { 1 } className = "shrink text-xs text-foreground-muted" >
133184 { label }
134185 </ Text >
0 commit comments