@@ -36,7 +36,7 @@ import { pathToFileURL } from "url"
3636import { Filesystem } from "../util"
3737import { Hash } from "@opencode-ai/shared/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"
@@ -146,6 +146,8 @@ export class Agent implements ACPAgent {
146146 private bashSnapshots = new Map < string , string > ( )
147147 private toolStarts = new Set < string > ( )
148148 private permissionQueues = new Map < string , Promise < void > > ( )
149+ // Maps child session IDs to their root ACP-tracked session ID
150+ private childToRootSession = new Map < string , string > ( )
149151 private permissionOptions : PermissionOption [ ] = [
150152 { optionId : "once" , kind : "allow_once" , name : "Allow once" } ,
151153 { optionId : "always" , kind : "allow_always" , name : "Always allow" } ,
@@ -186,11 +188,55 @@ export class Agent implements ACPAgent {
186188 }
187189 }
188190
191+ /**
192+ * Given a session ID, returns the ACPSessionState for that session or the
193+ * nearest ACP-tracked ancestor. Child sessions created by the Task tool
194+ * have a parentID chain leading back to the root ACP session. We cache
195+ * the mapping so we only traverse the chain once per child session.
196+ */
197+ private async resolveRootSession ( sessionID : string ) : Promise < ACPSessionState | undefined > {
198+ // Fast path: directly tracked by ACP session manager
199+ const direct = this . sessionManager . tryGet ( sessionID )
200+ if ( direct ) return direct
201+
202+ // Check cache
203+ const cached = this . childToRootSession . get ( sessionID )
204+ if ( cached ) return this . sessionManager . tryGet ( cached )
205+
206+ // Walk the parentID chain via the SDK until we find a known root session
207+ let currentID = sessionID
208+ const visited : string [ ] = [ ]
209+ while ( true ) {
210+ visited . push ( currentID )
211+ const info = await this . sdk . session
212+ . get ( { sessionID : currentID , directory : "" } , { throwOnError : false } )
213+ . then ( ( x ) => x . data )
214+ . catch ( ( ) => undefined )
215+
216+ if ( ! info ) break
217+
218+ const parentID = info . parentID
219+ if ( ! parentID ) break
220+
221+ const root = this . sessionManager . tryGet ( parentID )
222+ if ( root ) {
223+ // Cache the mapping for all visited IDs
224+ for ( const id of visited ) {
225+ this . childToRootSession . set ( id , root . id )
226+ }
227+ return root
228+ }
229+ currentID = parentID
230+ }
231+
232+ return undefined
233+ }
234+
189235 private async handleEvent ( event : Event ) {
190236 switch ( event . type ) {
191237 case "permission.asked" : {
192238 const permission = event . properties
193- const session = this . sessionManager . tryGet ( permission . sessionID )
239+ const session = await this . resolveRootSession ( permission . sessionID )
194240 if ( ! session ) return
195241
196242 const prev = this . permissionQueues . get ( permission . sessionID ) ?? Promise . resolve ( )
@@ -273,7 +319,7 @@ export class Agent implements ACPAgent {
273319 log . info ( "message part updated" , { event : event . properties } )
274320 const props = event . properties
275321 const part = props . part
276- const session = this . sessionManager . tryGet ( part . sessionID )
322+ const session = await this . resolveRootSession ( part . sessionID )
277323 if ( ! session ) return
278324 const sessionId = session . id
279325
@@ -465,16 +511,19 @@ export class Agent implements ACPAgent {
465511
466512 case "message.part.delta" : {
467513 const props = event . properties
468- const session = this . sessionManager . tryGet ( props . sessionID )
514+ const session = await this . resolveRootSession ( props . sessionID )
469515 if ( ! session ) return
470516 const sessionId = session . id
517+ // Use the child session's own cwd for SDK lookups (may differ from root)
518+ const childSession = this . sessionManager . tryGet ( props . sessionID )
519+ const directory = childSession ?. cwd ?? session . cwd
471520
472521 const message = await this . sdk . session
473522 . message (
474523 {
475524 sessionID : props . sessionID ,
476525 messageID : props . messageID ,
477- directory : session . cwd ,
526+ directory,
478527 } ,
479528 { throwOnError : true } ,
480529 )
0 commit comments