@@ -73,6 +73,12 @@ type AgentTaskWorkspaceEntry = WorkspaceConfigEntry & { projectPath: string };
7373const COMPLETED_REPORT_CACHE_TTL_MS = 60 * 60 * 1000 ; // 1 hour
7474const COMPLETED_REPORT_CACHE_MAX_ENTRIES = 128 ;
7575
76+ interface AgentTaskIndex {
77+ byId : Map < string , AgentTaskWorkspaceEntry > ;
78+ childrenByParent : Map < string , string [ ] > ;
79+ parentById : Map < string , string > ;
80+ }
81+
7682interface PendingTaskWaiter {
7783 createdAt : number ;
7884 resolve : ( report : { reportMarkdown : string ; title ?: string } ) => void ;
@@ -582,16 +588,19 @@ export class TaskService {
582588 return Err ( "Task not found" ) ;
583589 }
584590
585- if ( ! this . isDescendantAgentTask ( ancestorWorkspaceId , taskId ) ) {
591+ const index = this . buildAgentTaskIndex ( cfg ) ;
592+ if (
593+ ! this . isDescendantAgentTaskUsingParentById ( index . parentById , ancestorWorkspaceId , taskId )
594+ ) {
586595 return Err ( "Task is not a descendant of this workspace" ) ;
587596 }
588597
589598 // Terminate the entire subtree to avoid orphaned descendant tasks.
590- const descendants = this . listDescendantAgentTaskIds ( cfg , taskId ) ;
599+ const descendants = this . listDescendantAgentTaskIdsFromIndex ( index , taskId ) ;
591600 const toTerminate = Array . from ( new Set ( [ taskId , ...descendants ] ) ) ;
592601
593602 // Delete leaves first to avoid leaving children with missing parents.
594- const parentById = this . buildAgentTaskIndex ( cfg ) . parentById ;
603+ const parentById = index . parentById ;
595604 const depthById = new Map < string , number > ( ) ;
596605 for ( const id of toTerminate ) {
597606 depthById . set ( id , this . getTaskDepthFromParentById ( parentById , id ) ) ;
@@ -918,13 +927,35 @@ export class TaskService {
918927 return result ;
919928 }
920929
921- private listDescendantAgentTaskIds (
922- config : ReturnType < Config [ "loadConfigOrDefault" ] > ,
930+ filterDescendantAgentTaskIds ( ancestorWorkspaceId : string , taskIds : string [ ] ) : string [ ] {
931+ assert (
932+ ancestorWorkspaceId . length > 0 ,
933+ "filterDescendantAgentTaskIds: ancestorWorkspaceId required"
934+ ) ;
935+ assert ( Array . isArray ( taskIds ) , "filterDescendantAgentTaskIds: taskIds must be an array" ) ;
936+
937+ const cfg = this . config . loadConfigOrDefault ( ) ;
938+ const parentById = this . buildAgentTaskIndex ( cfg ) . parentById ;
939+
940+ const result : string [ ] = [ ] ;
941+ for ( const taskId of taskIds ) {
942+ if ( typeof taskId !== "string" || taskId . length === 0 ) continue ;
943+ if ( this . isDescendantAgentTaskUsingParentById ( parentById , ancestorWorkspaceId , taskId ) ) {
944+ result . push ( taskId ) ;
945+ }
946+ }
947+
948+ return result ;
949+ }
950+
951+ private listDescendantAgentTaskIdsFromIndex (
952+ index : AgentTaskIndex ,
923953 workspaceId : string
924954 ) : string [ ] {
925- assert ( workspaceId . length > 0 , "listDescendantAgentTaskIds: workspaceId must be non-empty" ) ;
926-
927- const index = this . buildAgentTaskIndex ( config ) ;
955+ assert (
956+ workspaceId . length > 0 ,
957+ "listDescendantAgentTaskIdsFromIndex: workspaceId must be non-empty"
958+ ) ;
928959
929960 const result : string [ ] = [ ] ;
930961 const stack : string [ ] = [ ...( index . childrenByParent . get ( workspaceId ) ?? [ ] ) ] ;
@@ -947,7 +978,14 @@ export class TaskService {
947978
948979 const cfg = this . config . loadConfigOrDefault ( ) ;
949980 const parentById = this . buildAgentTaskIndex ( cfg ) . parentById ;
981+ return this . isDescendantAgentTaskUsingParentById ( parentById , ancestorWorkspaceId , taskId ) ;
982+ }
950983
984+ private isDescendantAgentTaskUsingParentById (
985+ parentById : Map < string , string > ,
986+ ancestorWorkspaceId : string ,
987+ taskId : string
988+ ) : boolean {
951989 let current = taskId ;
952990 for ( let i = 0 ; i < 32 ; i ++ ) {
953991 const parent = parentById . get ( current ) ;
@@ -957,7 +995,7 @@ export class TaskService {
957995 }
958996
959997 throw new Error (
960- `isDescendantAgentTask : possible parentWorkspaceId cycle starting at ${ taskId } `
998+ `isDescendantAgentTaskUsingParentById : possible parentWorkspaceId cycle starting at ${ taskId } `
961999 ) ;
9621000 }
9631001
@@ -977,11 +1015,7 @@ export class TaskService {
9771015 return tasks ;
9781016 }
9791017
980- private buildAgentTaskIndex ( config : ReturnType < Config [ "loadConfigOrDefault" ] > ) : {
981- byId : Map < string , AgentTaskWorkspaceEntry > ;
982- childrenByParent : Map < string , string [ ] > ;
983- parentById : Map < string , string > ;
984- } {
1018+ private buildAgentTaskIndex ( config : ReturnType < Config [ "loadConfigOrDefault" ] > ) : AgentTaskIndex {
9851019 const byId = new Map < string , AgentTaskWorkspaceEntry > ( ) ;
9861020 const childrenByParent = new Map < string , string [ ] > ( ) ;
9871021 const parentById = new Map < string , string > ( ) ;
0 commit comments