@@ -638,6 +638,7 @@ type AutorouterCapture = {
638638
639639const LAST_ROUTE_LIMIT = 256
640640const lastRouteBySessionId = new Map < string , AutorouterCapture > ( )
641+ const lastRouteByAgentId = new Map < string , AutorouterCapture > ( )
641642
642643function pruneLastRouteMap ( ) : void {
643644 if ( lastRouteBySessionId . size <= LAST_ROUTE_LIMIT ) return
@@ -649,11 +650,41 @@ function pruneLastRouteMap(): void {
649650 for ( let i = 0 ; i < toDrop ; i ++ ) {
650651 lastRouteBySessionId . delete ( entries [ i ] [ 0 ] )
651652 }
653+ if ( lastRouteByAgentId . size <= LAST_ROUTE_LIMIT ) return
654+ const aEntries = [ ...lastRouteByAgentId . entries ( ) ] . sort (
655+ ( a , b ) => a [ 1 ] . capturedAt - b [ 1 ] . capturedAt
656+ )
657+ const aDrop = aEntries . length - LAST_ROUTE_LIMIT
658+ for ( let i = 0 ; i < aDrop ; i ++ ) {
659+ lastRouteByAgentId . delete ( aEntries [ i ] [ 0 ] )
660+ }
661+ }
662+
663+ /**
664+ * Derive a stable agent identifier from the OpenClaw runtime context. Prefers
665+ * explicit env vars set by the gateway for sub-agents (OPENCLAW_AGENT_ID /
666+ * OPENCLAW_RUN_ID), then falls back to the trailing path segment of agentDir
667+ * or workspaceDir (e.g. /home/u/.openclaw/workspace-new-agent-3 -> "new-agent-3",
668+ * /home/u/.openclaw/workspace -> "main").
669+ */
670+ function deriveAgentIdForCapture (
671+ ctx : { agentDir ?: string ; workspaceDir ?: string }
672+ ) : string | null {
673+ const envAgent = process . env . OPENCLAW_AGENT_ID ?? process . env . OPENCLAW_RUN_ID
674+ if ( envAgent && envAgent . trim ( ) ) return envAgent . trim ( )
675+ const base = ctx . agentDir ?? ctx . workspaceDir
676+ if ( ! base ) return null
677+ const seg = path . basename ( path . resolve ( base ) )
678+ if ( ! seg ) return null
679+ if ( seg === "workspace" ) return "main"
680+ if ( seg . startsWith ( "workspace-" ) ) return seg . slice ( "workspace-" . length )
681+ return seg
652682}
653683
654684function captureAutorouterFromHeaders (
655685 sessionId : string ,
656- headers : Record < string , string >
686+ headers : Record < string , string > ,
687+ agentId : string | null
657688) : void {
658689 // Header names from TaaS proxy are emitted in canonical "X-TaaS-*" form
659690 // but Node/undici lowercases incoming response headers. Read case-insensitively.
@@ -678,6 +709,7 @@ function captureAutorouterFromHeaders(
678709 } ) ( ) ,
679710 }
680711 lastRouteBySessionId . set ( sessionId , capture )
712+ if ( agentId ) lastRouteByAgentId . set ( agentId , capture )
681713 pruneLastRouteMap ( )
682714 if ( isDev ) {
683715 console . debug (
@@ -693,11 +725,16 @@ function getLastRouteForSession(sessionId: string): AutorouterCapture | null {
693725 return lastRouteBySessionId . get ( sessionId ) ?? null
694726}
695727
728+ function getLastRouteForAgent ( agentId : string ) : AutorouterCapture | null {
729+ return lastRouteByAgentId . get ( agentId ) ?? null
730+ }
731+
696732function buildWrapper ( ctx : ProviderWrapStreamFnContext ) {
697733 const { streamFn } = ctx
698734 if ( ! streamFn ) return undefined
699735
700736 const { sessionId, source } = resolveSessionId ( ctx . workspaceDir )
737+ const agentIdForCapture = deriveAgentIdForCapture ( ctx )
701738 const requesterRuntime = buildRequesterRuntime ( ctx , sessionId , source )
702739
703740 if ( isDev ) {
@@ -727,7 +764,11 @@ function buildWrapper(ctx: ProviderWrapStreamFnContext) {
727764 responseModel
728765 ) => {
729766 try {
730- captureAutorouterFromHeaders ( sessionId , response ?. headers ?? { } )
767+ captureAutorouterFromHeaders (
768+ sessionId ,
769+ response ?. headers ?? { } ,
770+ agentIdForCapture
771+ )
731772 } catch ( err ) {
732773 if ( isDev ) {
733774 console . debug (
@@ -806,11 +847,27 @@ export default {
806847 async ( { params, respond } ) => {
807848 // Accept either { workspaceDir } (preferred — derives sessionId the
808849 // same way the wrapper does) or { sessionId } (direct lookup).
809- const p = ( params ?? { } ) as Record < string , unknown >
850+ const pp = ( params ?? { } ) as Record < string , unknown >
851+ const directAgentId =
852+ typeof pp . agentId === "string" && pp . agentId . trim ( )
853+ ? pp . agentId . trim ( )
854+ : null
810855 const directSessionId =
811- typeof p . sessionId === "string" ? p . sessionId : null
856+ typeof pp . sessionId === "string" ? pp . sessionId : null
812857 const workspaceDir =
813- typeof p . workspaceDir === "string" ? p . workspaceDir : undefined
858+ typeof pp . workspaceDir === "string" ? pp . workspaceDir : undefined
859+
860+ // Prefer agent-keyed lookup when the caller supplied an agentId.
861+ if ( directAgentId ) {
862+ const captured = getLastRouteForAgent ( directAgentId )
863+ respond ( true , {
864+ agentId : directAgentId ,
865+ sessionId : captured ?. sessionId ?? null ,
866+ capture : captured ,
867+ } )
868+ return
869+ }
870+
814871 const resolvedSessionId =
815872 directSessionId ?? resolveSessionId ( workspaceDir ) . sessionId
816873 const captured = getLastRouteForSession ( resolvedSessionId )
0 commit comments