@@ -36,7 +36,7 @@ import { pathToFileURL } from "url"
3636import { Filesystem } from "../util"
3737import { Hash } from "@opencode-ai/core/util/hash"
3838import { ACPSessionManager } from "./session"
39- import type { ACPConfig } from "./types"
39+ import type { ACPConfig , ACPSessionState } from "./types"
4040import { Provider } from "../provider"
4141import { ModelID , ProviderID } from "../provider/schema"
4242import { Agent as AgentModule } from "../agent/agent"
@@ -147,6 +147,8 @@ export class Agent implements ACPAgent {
147147 private bashSnapshots = new Map < string , string > ( )
148148 private toolStarts = new Set < string > ( )
149149 private permissionQueues = new Map < string , Promise < void > > ( )
150+ // Maps child session IDs to their root ACP-tracked session ID
151+ private childToRootSession = new Map < string , string > ( )
150152 private permissionOptions : PermissionOption [ ] = [
151153 { optionId : "once" , kind : "allow_once" , name : "Allow once" } ,
152154 { optionId : "always" , kind : "allow_always" , name : "Always allow" } ,
@@ -187,11 +189,55 @@ export class Agent implements ACPAgent {
187189 }
188190 }
189191
192+ /**
193+ * Given a session ID, returns the ACPSessionState for that session or the
194+ * nearest ACP-tracked ancestor. Child sessions created by the Task tool
195+ * have a parentID chain leading back to the root ACP session. We cache
196+ * the mapping so we only traverse the chain once per child session.
197+ */
198+ private async resolveRootSession ( sessionID : string ) : Promise < ACPSessionState | undefined > {
199+ // Fast path: directly tracked by ACP session manager
200+ const direct = this . sessionManager . tryGet ( sessionID )
201+ if ( direct ) return direct
202+
203+ // Check cache
204+ const cached = this . childToRootSession . get ( sessionID )
205+ if ( cached ) return this . sessionManager . tryGet ( cached )
206+
207+ // Walk the parentID chain via the SDK until we find a known root session
208+ let currentID = sessionID
209+ const visited : string [ ] = [ ]
210+ while ( true ) {
211+ visited . push ( currentID )
212+ const info = await this . sdk . session
213+ . get ( { sessionID : currentID , directory : "" } , { throwOnError : false } )
214+ . then ( ( x ) => x . data )
215+ . catch ( ( ) => undefined )
216+
217+ if ( ! info ) break
218+
219+ const parentID = info . parentID
220+ if ( ! parentID ) break
221+
222+ const root = this . sessionManager . tryGet ( parentID )
223+ if ( root ) {
224+ // Cache the mapping for all visited IDs
225+ for ( const id of visited ) {
226+ this . childToRootSession . set ( id , root . id )
227+ }
228+ return root
229+ }
230+ currentID = parentID
231+ }
232+
233+ return undefined
234+ }
235+
190236 private async handleEvent ( event : Event ) {
191237 switch ( event . type ) {
192238 case "permission.asked" : {
193239 const permission = event . properties
194- const session = this . sessionManager . tryGet ( permission . sessionID )
240+ const session = await this . resolveRootSession ( permission . sessionID )
195241 if ( ! session ) return
196242
197243 const prev = this . permissionQueues . get ( permission . sessionID ) ?? Promise . resolve ( )
@@ -274,7 +320,7 @@ export class Agent implements ACPAgent {
274320 log . info ( "message part updated" , { event : event . properties } )
275321 const props = event . properties
276322 const part = props . part
277- const session = this . sessionManager . tryGet ( part . sessionID )
323+ const session = await this . resolveRootSession ( part . sessionID )
278324 if ( ! session ) return
279325 const sessionId = session . id
280326
@@ -466,16 +512,19 @@ export class Agent implements ACPAgent {
466512
467513 case "message.part.delta" : {
468514 const props = event . properties
469- const session = this . sessionManager . tryGet ( props . sessionID )
515+ const session = await this . resolveRootSession ( props . sessionID )
470516 if ( ! session ) return
471517 const sessionId = session . id
518+ // Use the child session's own cwd for SDK lookups (may differ from root)
519+ const childSession = this . sessionManager . tryGet ( props . sessionID )
520+ const directory = childSession ?. cwd ?? session . cwd
472521
473522 const message = await this . sdk . session
474523 . message (
475524 {
476525 sessionID : props . sessionID ,
477526 messageID : props . messageID ,
478- directory : session . cwd ,
527+ directory,
479528 } ,
480529 { throwOnError : true } ,
481530 )
0 commit comments