@@ -998,6 +998,76 @@ describe("sessions tools", () => {
998998 expect ( details . text ) . toContain ( "active (waiting on 1 child)" ) ;
999999 } ) ;
10001000
1001+ it ( "subagents list does not double-count restarted descendants on one child session" , async ( ) => {
1002+ resetSubagentRegistryForTests ( ) ;
1003+ const now = Date . now ( ) ;
1004+ const parentKey = "agent:main:subagent:orchestrator-restarted-child" ;
1005+ const childKey = `${ parentKey } :subagent:worker` ;
1006+ addSubagentRunForTests ( {
1007+ runId : "run-orchestrator-ended-restarted" ,
1008+ childSessionKey : parentKey ,
1009+ requesterSessionKey : "agent:main:main" ,
1010+ requesterDisplayKey : "main" ,
1011+ task : "orchestrate restarted child worker" ,
1012+ cleanup : "keep" ,
1013+ createdAt : now - 5 * 60_000 ,
1014+ startedAt : now - 5 * 60_000 ,
1015+ endedAt : now - 4 * 60_000 ,
1016+ outcome : { status : "ok" } ,
1017+ } ) ;
1018+ addSubagentRunForTests ( {
1019+ runId : "run-restarted-child-stale" ,
1020+ childSessionKey : childKey ,
1021+ requesterSessionKey : parentKey ,
1022+ requesterDisplayKey : parentKey ,
1023+ task : "stale child run" ,
1024+ cleanup : "keep" ,
1025+ createdAt : now - 90_000 ,
1026+ startedAt : now - 90_000 ,
1027+ endedAt : now - 70_000 ,
1028+ cleanupCompletedAt : undefined ,
1029+ outcome : { status : "ok" } ,
1030+ } ) ;
1031+ addSubagentRunForTests ( {
1032+ runId : "run-restarted-child-current" ,
1033+ childSessionKey : childKey ,
1034+ requesterSessionKey : parentKey ,
1035+ requesterDisplayKey : parentKey ,
1036+ task : "current child run" ,
1037+ cleanup : "keep" ,
1038+ createdAt : now - 60_000 ,
1039+ startedAt : now - 60_000 ,
1040+ } ) ;
1041+
1042+ const tool = createOpenClawTools ( {
1043+ agentSessionKey : "agent:main:main" ,
1044+ } ) . find ( ( candidate ) => candidate . name === "subagents" ) ;
1045+ expect ( tool ) . toBeDefined ( ) ;
1046+ if ( ! tool ) {
1047+ throw new Error ( "missing subagents tool" ) ;
1048+ }
1049+
1050+ const result = await tool . execute ( "call-subagents-list-restarted-child" , { action : "list" } ) ;
1051+ const details = result . details as {
1052+ status ?: string ;
1053+ active ?: Array < { runId ?: string ; status ?: string ; pendingDescendants ?: number } > ;
1054+ text ?: string ;
1055+ } ;
1056+
1057+ expect ( details . status ) . toBe ( "ok" ) ;
1058+ expect ( details . active ) . toEqual (
1059+ expect . arrayContaining ( [
1060+ expect . objectContaining ( {
1061+ runId : "run-orchestrator-ended-restarted" ,
1062+ status : "active (waiting on 1 child)" ,
1063+ pendingDescendants : 1 ,
1064+ } ) ,
1065+ ] ) ,
1066+ ) ;
1067+ expect ( details . text ) . toContain ( "active (waiting on 1 child)" ) ;
1068+ expect ( details . text ) . not . toContain ( "active (waiting on 2 children)" ) ;
1069+ } ) ;
1070+
10011071 it ( "subagents list dedupes stale rows for the same child session" , async ( ) => {
10021072 resetSubagentRegistryForTests ( ) ;
10031073 const now = Date . now ( ) ;
0 commit comments