@@ -36,7 +36,7 @@ import { pathToFileURL } from "url"
3636import { Filesystem } from "../util/filesystem"
3737import { Hash } from "../util/hash"
3838import { ACPSessionManager } from "./session"
39- import type { ACPConfig } from "./types"
39+ import type { ACPConfig , ACPSessionState } from "./types"
4040import { Provider } from "../provider/provider"
4141import { ModelID , ProviderID } from "../provider/schema"
4242import { Agent as AgentModule } from "../agent/agent"
@@ -144,6 +144,8 @@ export namespace ACP {
144144 private bashSnapshots = new Map < string , string > ( )
145145 private toolStarts = new Set < string > ( )
146146 private permissionQueues = new Map < string , Promise < void > > ( )
147+ // Maps child session IDs to their root ACP-tracked session ID
148+ private childToRootSession = new Map < string , string > ( )
147149 private permissionOptions : PermissionOption [ ] = [
148150 { optionId : "once" , kind : "allow_once" , name : "Allow once" } ,
149151 { optionId : "always" , kind : "allow_always" , name : "Always allow" } ,
@@ -184,11 +186,55 @@ export namespace ACP {
184186 }
185187 }
186188
189+ /**
190+ * Given a session ID, returns the ACPSessionState for that session or the
191+ * nearest ACP-tracked ancestor. Child sessions created by the Task tool
192+ * have a parentID chain leading back to the root ACP session. We cache
193+ * the mapping so we only traverse the chain once per child session.
194+ */
195+ private async resolveRootSession ( sessionID : string ) : Promise < ACPSessionState | undefined > {
196+ // Fast path: directly tracked by ACP session manager
197+ const direct = this . sessionManager . tryGet ( sessionID )
198+ if ( direct ) return direct
199+
200+ // Check cache
201+ const cached = this . childToRootSession . get ( sessionID )
202+ if ( cached ) return this . sessionManager . tryGet ( cached )
203+
204+ // Walk the parentID chain via the SDK until we find a known root session
205+ let currentID = sessionID
206+ const visited : string [ ] = [ ]
207+ while ( true ) {
208+ visited . push ( currentID )
209+ const info = await this . sdk . session
210+ . get ( { sessionID : currentID , directory : "" } , { throwOnError : false } )
211+ . then ( ( x ) => x . data )
212+ . catch ( ( ) => undefined )
213+
214+ if ( ! info ) break
215+
216+ const parentID = info . parentID
217+ if ( ! parentID ) break
218+
219+ const root = this . sessionManager . tryGet ( parentID )
220+ if ( root ) {
221+ // Cache the mapping for all visited IDs
222+ for ( const id of visited ) {
223+ this . childToRootSession . set ( id , root . id )
224+ }
225+ return root
226+ }
227+ currentID = parentID
228+ }
229+
230+ return undefined
231+ }
232+
187233 private async handleEvent ( event : Event ) {
188234 switch ( event . type ) {
189235 case "permission.asked" : {
190236 const permission = event . properties
191- const session = this . sessionManager . tryGet ( permission . sessionID )
237+ const session = await this . resolveRootSession ( permission . sessionID )
192238 if ( ! session ) return
193239
194240 const prev = this . permissionQueues . get ( permission . sessionID ) ?? Promise . resolve ( )
@@ -271,7 +317,7 @@ export namespace ACP {
271317 log . info ( "message part updated" , { event : event . properties } )
272318 const props = event . properties
273319 const part = props . part
274- const session = this . sessionManager . tryGet ( part . sessionID )
320+ const session = await this . resolveRootSession ( part . sessionID )
275321 if ( ! session ) return
276322 const sessionId = session . id
277323
@@ -470,16 +516,19 @@ export namespace ACP {
470516
471517 case "message.part.delta" : {
472518 const props = event . properties
473- const session = this . sessionManager . tryGet ( props . sessionID )
519+ const session = await this . resolveRootSession ( props . sessionID )
474520 if ( ! session ) return
475521 const sessionId = session . id
522+ // Use the child session's own cwd for SDK lookups (may differ from root)
523+ const childSession = this . sessionManager . tryGet ( props . sessionID )
524+ const directory = childSession ?. cwd ?? session . cwd
476525
477526 const message = await this . sdk . session
478527 . message (
479528 {
480529 sessionID : props . sessionID ,
481530 messageID : props . messageID ,
482- directory : session . cwd ,
531+ directory,
483532 } ,
484533 { throwOnError : true } ,
485534 )
0 commit comments