Skip to content

start a resumed CoS agent's task immediately instead of leaving it pending for a manual Run - #6096

Merged
atomantic merged 2 commits into
mainfrom
cos/task-mtlripte/agent-c297d619
Sep 3, 2026
Merged

start a resumed CoS agent's task immediately instead of leaving it pending for a manual Run#6096
atomantic merged 2 commits into
mainfrom
cos/task-mtlripte/agent-c297d619

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Summary

  • Resume and Relaunch now start the task they requeue, instead of leaving it pending for the user to find on the task list and press Run.
  • The requeue only made a task eligible. What spawns it is the automatic dequeue completeAgent schedules, and that path admits pending user tasks plus auto-approved system tasks under CoS auto-run in execute mode — so a resumed internal task on an install with auto-run off, or one still awaiting approval, sat there. One click became two, on the screen the user had just acted from.
  • resumeAgent now force-spawns through forceSpawnTask — the same door "Run now" uses — so it inherits that gate's refusals rather than restating them. A stopped/paused daemon, an unreachable runner, a task needing approval, and a full agent pool all still mean "leave it queued"; the refusal comes back as spawnHold so the dialog names it instead of toasting a resume that silently didn't start.
  • Relaunch is a pause plus a resume, so it inherits the dispatch through that composition — no second code path.

Why resumeAgent and not deeper

Not pushed into reviveBlockedTask or the tasks:changed unblock listener: those are shared with the autonomous revival paths (investigation retry, orphan cooldown, completion cleanup), where force-spawning would strip the auto-run gate that deliberately withholds unattended spawns. Both doors into the new dispatch are a human clicking a button.

Reporting the outcome honestly

Two ways to get the toast wrong, and both dialogs could get them wrong identically, so they share one helper (client/src/lib/agentResumeOutcome.js): saying "queued" for a run that already started reads as the click not having taken, and saying "queued" with no reason sends the user hunting the task list for why.

A spawn registers its agent as running before it flips the task off pending, so a refusal landing in that window is forceSpawnTask's own holder guard — the dispatch checks the running-agent holder as well as the task status, or it would report a hold for a run already under way.

Test plan

  • server/services/agentManagement.test.js — force-spawns the requeued task; reports the refusal rather than claiming a resume that never started; treats a task the racing dequeue already claimed as started; treats a task claimed mid-spawn (still pending) as started; does not dispatch already-active/superseded; relaunch inherits the dispatch outcome. The mid-spawn test was bypass-probed — it fails when the holder half of the check is removed.
  • client/src/components/cos/tabs/AgentsTab.test.jsx, RelaunchAgentModal.test.jsx — running vs queued wording, the spawnHold reason, and the new-task replacement outcome.
  • Full server suite green (38,520 passing); client/src/components/cos/tabs/ and client/src/lib/index.test.js green.

…r a manual Run

Resume and Relaunch both requeued the paused agent's task and stopped there.
The requeue only makes a task ELIGIBLE — what spawns it is the automatic
dequeue `completeAgent` schedules, and that path admits pending user tasks
plus auto-approved system tasks under CoS auto-run in `execute` mode. So on
an install with auto-run off, or for a task still awaiting approval, the task
sat `pending` until the user opened the task list and pressed Run. One click
became two, on the screen the user had just acted from.

`resumeAgent` now force-spawns the task it requeued, through `forceSpawnTask`
— the same door "Run now" uses — so it inherits those refusals rather than
restating them. A stopped/paused daemon, an unreachable runner, a task needing
approval, and a full agent pool all still mean "leave it queued"; the refusal
comes back as `spawnHold` so the dialog names it instead of toasting a resume
that silently didn't start. Relaunch is a pause plus a resume, so it inherits
the dispatch through that composition.

Deliberately not pushed deeper into `reviveBlockedTask` or the `tasks:changed`
unblock listener: those are shared with the autonomous revival paths
(investigation retry, orphan cooldown, completion cleanup), where force-spawning
would strip the auto-run gate that withholds unattended spawns. Both doors into
the new dispatch are a human clicking a button.

Both dialogs render the outcome through one shared helper so neither can say
"queued" for a run that already started, or omit the reason it didn't.
… name the new-task resume outcome

A spawn registers its agent as running BEFORE it flips the task off pending,
and the refusal that lands in that window is forceSpawnTask's own holder guard
— so reading the task status alone still saw 'pending' and reported a hold for
a run already under way. Check the running-agent holder too.

AgentsTab had no 'new-task' wording, so a resumed replacement task that the
server started reported only the generic 'Created resume task'.
@atomantic
atomantic merged commit 87b02ef into main Sep 3, 2026
7 checks passed
@atomantic
atomantic deleted the cos/task-mtlripte/agent-c297d619 branch September 3, 2026 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant