@@ -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"
@@ -145,6 +145,8 @@ export namespace ACP {
145145 private bashSnapshots = new Map < string , string > ( )
146146 private toolStarts = new Set < string > ( )
147147 private permissionQueues = new Map < string , Promise < void > > ( )
148+ // Maps child session IDs to their root ACP-tracked session ID
149+ private childToRootSession = new Map < string , string > ( )
148150 private permissionOptions : PermissionOption [ ] = [
149151 { optionId : "once" , kind : "allow_once" , name : "Allow once" } ,
150152 { optionId : "always" , kind : "allow_always" , name : "Always allow" } ,
@@ -185,11 +187,55 @@ export namespace ACP {
185187 }
186188 }
187189
190+ /**
191+ * Given a session ID, returns the ACPSessionState for that session or the
192+ * nearest ACP-tracked ancestor. Child sessions created by the Task tool
193+ * have a parentID chain leading back to the root ACP session. We cache
194+ * the mapping so we only traverse the chain once per child session.
195+ */
196+ private async resolveRootSession ( sessionID : string ) : Promise < ACPSessionState | undefined > {
197+ // Fast path: directly tracked by ACP session manager
198+ const direct = this . sessionManager . tryGet ( sessionID )
199+ if ( direct ) return direct
200+
201+ // Check cache
202+ const cached = this . childToRootSession . get ( sessionID )
203+ if ( cached ) return this . sessionManager . tryGet ( cached )
204+
205+ // Walk the parentID chain via the SDK until we find a known root session
206+ let currentID = sessionID
207+ const visited : string [ ] = [ ]
208+ while ( true ) {
209+ visited . push ( currentID )
210+ const info = await this . sdk . session
211+ . get ( { sessionID : currentID , directory : "" } , { throwOnError : false } )
212+ . then ( ( x ) => x . data )
213+ . catch ( ( ) => undefined )
214+
215+ if ( ! info ) break
216+
217+ const parentID = info . parentID
218+ if ( ! parentID ) break
219+
220+ const root = this . sessionManager . tryGet ( parentID )
221+ if ( root ) {
222+ // Cache the mapping for all visited IDs
223+ for ( const id of visited ) {
224+ this . childToRootSession . set ( id , root . id )
225+ }
226+ return root
227+ }
228+ currentID = parentID
229+ }
230+
231+ return undefined
232+ }
233+
188234 private async handleEvent ( event : Event ) {
189235 switch ( event . type ) {
190236 case "permission.asked" : {
191237 const permission = event . properties
192- const session = this . sessionManager . tryGet ( permission . sessionID )
238+ const session = await this . resolveRootSession ( permission . sessionID )
193239 if ( ! session ) return
194240
195241 const prev = this . permissionQueues . get ( permission . sessionID ) ?? Promise . resolve ( )
@@ -272,7 +318,7 @@ export namespace ACP {
272318 log . info ( "message part updated" , { event : event . properties } )
273319 const props = event . properties
274320 const part = props . part
275- const session = this . sessionManager . tryGet ( part . sessionID )
321+ const session = await this . resolveRootSession ( part . sessionID )
276322 if ( ! session ) return
277323 const sessionId = session . id
278324
@@ -464,16 +510,19 @@ export namespace ACP {
464510
465511 case "message.part.delta" : {
466512 const props = event . properties
467- const session = this . sessionManager . tryGet ( props . sessionID )
513+ const session = await this . resolveRootSession ( props . sessionID )
468514 if ( ! session ) return
469515 const sessionId = session . id
516+ // Use the child session's own cwd for SDK lookups (may differ from root)
517+ const childSession = this . sessionManager . tryGet ( props . sessionID )
518+ const directory = childSession ?. cwd ?? session . cwd
470519
471520 const message = await this . sdk . session
472521 . message (
473522 {
474523 sessionID : props . sessionID ,
475524 messageID : props . messageID ,
476- directory : session . cwd ,
525+ directory,
477526 } ,
478527 { throwOnError : true } ,
479528 )
0 commit comments