@@ -2421,6 +2421,70 @@ describe("task-registry", () => {
24212421 } ) ;
24222422 } ) ;
24232423
2424+ it ( "keeps fresh childless copilot-native subagent tasks live" , async ( ) => {
2425+ await withTaskRegistryTempDir ( async ( ) => {
2426+ resetTaskRegistryForTests ( ) ;
2427+ const now = Date . now ( ) ;
2428+
2429+ const task = createTaskRecord ( {
2430+ runtime : "subagent" ,
2431+ taskKind : "copilot-native" ,
2432+ ownerKey : "agent:main:main" ,
2433+ scopeKind : "session" ,
2434+ sourceId : "copilot-agent:child-agent" ,
2435+ runId : "copilot-agent:child-agent" ,
2436+ task : "Copilot native child" ,
2437+ status : "running" ,
2438+ deliveryStatus : "not_applicable" ,
2439+ notifyPolicy : "silent" ,
2440+ lastEventAt : now - 10 * 60_000 ,
2441+ } ) ;
2442+
2443+ expect ( await runTaskRegistryMaintenance ( ) ) . toEqual ( {
2444+ reconciled : 0 ,
2445+ recovered : 0 ,
2446+ cleanupStamped : 0 ,
2447+ pruned : 0 ,
2448+ } ) ;
2449+ expectRecordFields ( requireTaskById ( task . taskId ) , {
2450+ status : "running" ,
2451+ lastEventAt : now - 10 * 60_000 ,
2452+ } ) ;
2453+ } ) ;
2454+ } ) ;
2455+
2456+ it ( "marks stale childless copilot-native subagent tasks lost" , async ( ) => {
2457+ await withTaskRegistryTempDir ( async ( ) => {
2458+ resetTaskRegistryForTests ( ) ;
2459+ const now = Date . now ( ) ;
2460+
2461+ const task = createTaskRecord ( {
2462+ runtime : "subagent" ,
2463+ taskKind : "copilot-native" ,
2464+ ownerKey : "agent:main:main" ,
2465+ scopeKind : "session" ,
2466+ sourceId : "copilot-agent:child-agent" ,
2467+ runId : "copilot-agent:child-agent" ,
2468+ task : "Copilot native child" ,
2469+ status : "running" ,
2470+ deliveryStatus : "not_applicable" ,
2471+ notifyPolicy : "silent" ,
2472+ lastEventAt : now - 31 * 60_000 ,
2473+ } ) ;
2474+
2475+ expect ( await runTaskRegistryMaintenance ( ) ) . toEqual ( {
2476+ reconciled : 1 ,
2477+ recovered : 0 ,
2478+ cleanupStamped : 0 ,
2479+ pruned : 0 ,
2480+ } ) ;
2481+ expectRecordFields ( requireTaskById ( task . taskId ) , {
2482+ status : "lost" ,
2483+ error : "Native subagent stopped reporting progress" ,
2484+ } ) ;
2485+ } ) ;
2486+ } ) ;
2487+
24242488 it ( "does not mark unrelated childless subagent tasks lost" , async ( ) => {
24252489 await withTaskRegistryTempDir ( async ( ) => {
24262490 resetTaskRegistryForTests ( ) ;
@@ -3794,6 +3858,43 @@ describe("task-registry", () => {
37943858 } ) ;
37953859 } ) ;
37963860
3861+ it ( "cancels childless copilot-native tasks without routing through OpenClaw subagent sessions" , async ( ) => {
3862+ await withTaskRegistryTempDir ( async ( ) => {
3863+ resetTaskRegistryForTests ( ) ;
3864+ const task = createTaskRecord ( {
3865+ runtime : "subagent" ,
3866+ taskKind : "copilot-native" ,
3867+ ownerKey : "agent:main:main" ,
3868+ scopeKind : "session" ,
3869+ sourceId : "copilot-agent:child-agent" ,
3870+ runId : "copilot-agent:child-agent" ,
3871+ task : "Copilot native child" ,
3872+ status : "running" ,
3873+ deliveryStatus : "not_applicable" ,
3874+ notifyPolicy : "silent" ,
3875+ } ) ;
3876+
3877+ const result = await cancelTaskById ( {
3878+ cfg : { } as never ,
3879+ taskId : task . taskId ,
3880+ } ) ;
3881+
3882+ expectRecordFields ( result , {
3883+ found : true ,
3884+ cancelled : true ,
3885+ } ) ;
3886+ expectRecordFields ( result . task , {
3887+ taskId : task . taskId ,
3888+ status : "cancelled" ,
3889+ endedAt : expect . any ( Number ) ,
3890+ lastEventAt : expect . any ( Number ) ,
3891+ cleanupAfter : expect . any ( Number ) ,
3892+ error : "Cancelled by operator." ,
3893+ } ) ;
3894+ expect ( hoisted . killSubagentRunAdminMock ) . not . toHaveBeenCalled ( ) ;
3895+ } ) ;
3896+ } ) ;
3897+
37973898 it ( "does not cancel unrelated childless subagent tasks" , async ( ) => {
37983899 await withTaskRegistryTempDir ( async ( ) => {
37993900 resetTaskRegistryForTests ( ) ;
0 commit comments