@@ -1885,7 +1885,7 @@ describe("task-registry", () => {
18851885 } ) ;
18861886 } ) ;
18871887
1888- it ( "does not mark codex-native subagent tasks lost when they have no OpenClaw child session " , async ( ) => {
1888+ it ( "keeps fresh childless codex-native subagent tasks live " , async ( ) => {
18891889 await withTaskRegistryTempDir ( async ( root ) => {
18901890 process . env . OPENCLAW_STATE_DIR = root ;
18911891 resetTaskRegistryForTests ( ) ;
@@ -1914,14 +1914,85 @@ describe("task-registry", () => {
19141914 cleanupStamped : 0 ,
19151915 pruned : 0 ,
19161916 } ) ;
1917- expect ( getTaskById ( task . taskId ) ) . toEqual ( {
1918- ...task ,
1919- createdAt : now - 10 * 60_000 ,
1917+ expectRecordFields ( requireTaskById ( task . taskId ) , {
1918+ status : "running" ,
19201919 lastEventAt : now - 10 * 60_000 ,
19211920 } ) ;
19221921 } ) ;
19231922 } ) ;
19241923
1924+ it ( "marks stale childless codex-native subagent tasks lost" , async ( ) => {
1925+ await withTaskRegistryTempDir ( async ( root ) => {
1926+ process . env . OPENCLAW_STATE_DIR = root ;
1927+ resetTaskRegistryForTests ( ) ;
1928+ const now = Date . now ( ) ;
1929+
1930+ const task = createTaskRecord ( {
1931+ runtime : "subagent" ,
1932+ taskKind : "codex-native" ,
1933+ ownerKey : "agent:main:main" ,
1934+ scopeKind : "session" ,
1935+ sourceId : "codex-thread:child-thread" ,
1936+ runId : "codex-thread:child-thread" ,
1937+ task : "Codex native child" ,
1938+ status : "running" ,
1939+ deliveryStatus : "not_applicable" ,
1940+ notifyPolicy : "silent" ,
1941+ } ) ;
1942+ setTaskTimingById ( {
1943+ taskId : task . taskId ,
1944+ lastEventAt : now - 31 * 60_000 ,
1945+ } ) ;
1946+
1947+ expect ( await runTaskRegistryMaintenance ( ) ) . toEqual ( {
1948+ reconciled : 1 ,
1949+ recovered : 0 ,
1950+ cleanupStamped : 0 ,
1951+ pruned : 0 ,
1952+ } ) ;
1953+ expectRecordFields ( requireTaskById ( task . taskId ) , {
1954+ status : "lost" ,
1955+ error : "Codex native subagent stopped reporting progress" ,
1956+ } ) ;
1957+ } ) ;
1958+ } ) ;
1959+
1960+ it ( "does not mark unrelated childless subagent tasks lost" , async ( ) => {
1961+ await withTaskRegistryTempDir ( async ( root ) => {
1962+ process . env . OPENCLAW_STATE_DIR = root ;
1963+ resetTaskRegistryForTests ( ) ;
1964+ const now = Date . now ( ) ;
1965+
1966+ const task = createTaskRecord ( {
1967+ runtime : "subagent" ,
1968+ taskKind : "codex-native" ,
1969+ ownerKey : "agent:main:main" ,
1970+ scopeKind : "session" ,
1971+ sourceId : "other-runtime:child-thread" ,
1972+ runId : "other-runtime:child-thread" ,
1973+ task : "Non-Codex childless row" ,
1974+ status : "running" ,
1975+ deliveryStatus : "not_applicable" ,
1976+ notifyPolicy : "silent" ,
1977+ } ) ;
1978+ setTaskTimingById ( {
1979+ taskId : task . taskId ,
1980+ lastEventAt : now - 31 * 60_000 ,
1981+ } ) ;
1982+
1983+ expect ( await runTaskRegistryMaintenance ( ) ) . toEqual ( {
1984+ reconciled : 0 ,
1985+ recovered : 0 ,
1986+ cleanupStamped : 0 ,
1987+ pruned : 0 ,
1988+ } ) ;
1989+ expectRecordFields ( requireTaskById ( task . taskId ) , {
1990+ status : "running" ,
1991+ lastEventAt : now - 31 * 60_000 ,
1992+ } ) ;
1993+ } ) ;
1994+ } ) ;
1995+
19251996 it ( "closes terminal parent-owned one-shot ACP sessions during maintenance" , async ( ) => {
19261997 await withTaskRegistryTempDir ( async ( root ) => {
19271998 process . env . OPENCLAW_STATE_DIR = root ;
@@ -3168,7 +3239,7 @@ describe("task-registry", () => {
31683239 } ) ;
31693240 } ) ;
31703241
3171- it ( "does not route codex-native task cancellation through OpenClaw subagent sessions" , async ( ) => {
3242+ it ( "cancels childless codex-native tasks without routing through OpenClaw subagent sessions" , async ( ) => {
31723243 await withTaskRegistryTempDir ( async ( root ) => {
31733244 process . env . OPENCLAW_STATE_DIR = root ;
31743245 resetTaskRegistryForTests ( ) ;
@@ -3190,6 +3261,44 @@ describe("task-registry", () => {
31903261 taskId : task . taskId ,
31913262 } ) ;
31923263
3264+ expectRecordFields ( result , {
3265+ found : true ,
3266+ cancelled : true ,
3267+ } ) ;
3268+ expectRecordFields ( result . task , {
3269+ taskId : task . taskId ,
3270+ status : "cancelled" ,
3271+ endedAt : expect . any ( Number ) ,
3272+ lastEventAt : expect . any ( Number ) ,
3273+ cleanupAfter : expect . any ( Number ) ,
3274+ error : "Cancelled by operator." ,
3275+ } ) ;
3276+ expect ( hoisted . killSubagentRunAdminMock ) . not . toHaveBeenCalled ( ) ;
3277+ } ) ;
3278+ } ) ;
3279+
3280+ it ( "does not cancel unrelated childless subagent tasks" , async ( ) => {
3281+ await withTaskRegistryTempDir ( async ( root ) => {
3282+ process . env . OPENCLAW_STATE_DIR = root ;
3283+ resetTaskRegistryForTests ( ) ;
3284+ const task = createTaskRecord ( {
3285+ runtime : "subagent" ,
3286+ taskKind : "codex-native" ,
3287+ ownerKey : "agent:main:main" ,
3288+ scopeKind : "session" ,
3289+ sourceId : "other-runtime:child-thread" ,
3290+ runId : "other-runtime:child-thread" ,
3291+ task : "Non-Codex childless row" ,
3292+ status : "running" ,
3293+ deliveryStatus : "not_applicable" ,
3294+ notifyPolicy : "silent" ,
3295+ } ) ;
3296+
3297+ const result = await cancelTaskById ( {
3298+ cfg : { } as never ,
3299+ taskId : task . taskId ,
3300+ } ) ;
3301+
31933302 expect ( result ) . toEqual ( {
31943303 found : true ,
31953304 cancelled : false ,
0 commit comments