Let an agent archive its own session - #972
Merged
Merged
Conversation
dispatch_archive_agent was scoped to direct children, so an agent that had finished and reported had no way to retire itself — archiving was only ever something done to it from outside. It now accepts the caller's own ID. Same tool, same rules, one path. The only thing self-archiving needs that archiving a child does not is that the teardown cannot be awaited: the session being stopped is the one waiting for the response. So the handler claims the archive, answers, and runs teardown in the background — gated on the response actually having been written, which the MCP route now hands down from the hijacked reply's finish/close. Holding teardown until the response flushes costs a child archive nothing, so both targets take the identical path and the handler has no branch in it. That backgrounding is the one behavioural change to archiving a child: the tool returns `archiving: true` when the teardown has been scheduled rather than `archived: true` once it finished, matching what the HTTP archive route already does. agentLifecycleRuntime.beginBackgroundArchive owns the claim → publish → background teardown → shutdown tracking, so the archive is still tracked at shutdown and still publishes its UI events. Everything else is unchanged: still rejects agents you neither launched nor are, still defaults cleanupWorktree to 'auto', so a worktree holding unmerged or uncommitted work is preserved on disk. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
selfcontainedforce-pushed
the
agt_9e362f1d3dd3/build-agent-self-archive
branch
from
August 17, 2026 16:03
686b6bc to
bdc5367CompareAn archive stops the parent moments from now and only cascades to review children, so a child launched after the claim is orphaned the instant it starts. The window existed before — a child being archived stays alive through its own teardown — but self-archive widens it: the parent is deliberately kept alive until its response is written, and it is the one holding the MCP connection, so it can issue the launch itself. Guarded in handleLaunchAgent for every archive target, not only self. Verified on the dev instance: self-archive, then dispatch_launch_agent inside the grace window, is refused and no orphan row is created. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A job agent leaves by reporting its outcome; the run's terminal state is what auto-archives it. Archiving around that path stops the session mid-run, so the job monitor reports the run as crashed and pages whoever the job notifies — a false alarm for what was a deliberate exit. dispatch_archive_agent now rejects a target with an active run and names the tools that actually end one. Uniform across targets: a job agent cannot retire itself this way, and neither can a parent archive a child mid-run. Scoped to agent-initiated archives only. The HTTP archive route and the job runner's own auto-archive are untouched, since a human still needs to be able to archive a job agent that has genuinely gone wrong. Verified on the dev instance: the self-archive attempt is refused with the run and agent intact; DELETE /api/v1/agents/:id still archives the same agent (202); and job_complete still drives the run terminal and auto-archives its agent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Idea:
agent-self-archive— Give an agent the ability to archive itself.dispatch_archive_agentwas scoped to direct children, so an agent that had finished its work and reported it had no way to retire itself — archiving was only ever something done to it from outside. It now accepts the caller's own ID.Same tool, same rules, one path
No new tool and no new guardrails. Archiving yourself is not more dangerous than archiving a child — the same worktree can be lost either way — so the rules don't fork: still rejects an agent you neither launched nor are, still defaults
cleanupWorktreetoauto, so a worktree with unmerged or uncommitted work is preserved on disk.The one thing self-archiving genuinely needs is that the teardown can't be awaited: the session being stopped is the one waiting for the response. So the handler claims the archive, answers, and runs teardown in the background — gated on the response actually having been written, which the MCP route hands down from the hijacked reply's
finish/close. Holding teardown until the response flushes costs a child archive nothing, so both targets take the identical path and the handler has no branch in it.agentLifecycleRuntime.beginBackgroundArchiveowns claim → publish → background teardown → shutdown tracking, so the archive is still awaited at shutdown and still publishes its UI events.Two guards came out of review. A job agent can't archive itself — a job agent leaves by reporting its outcome, and the run's terminal state is what auto-archives it; archiving around that made the job monitor report the run as
crashedand page whoever the job notifies.dispatch_archive_agentnow rejects any target with an active run and namesjob_complete/job_failed/job_needs_input. That applies to both targets, and only to agent-initiated archives — the HTTP archive route and the job runner's own auto-archive are untouched, since a human still needs to be able to kill a job agent that has genuinely gone wrong.Second,
handleLaunchAgentnow refuses to create a child under a parent whose status isarchiving. Only review children cascade, so a child launched after the claim would be orphaned the instant it started. That window existed before this change — a child being archived stays alive through its own teardown — but self-archive widens it, since the parent is deliberately kept alive until its response is written and is the one holding the MCP connection. The guard applies to every archive target, not only self.One behavioural change to archiving a child: the tool returns
archiving: truewhen teardown is scheduled rather thanarchived: trueonce it finished — matching what the HTTP archive route already reports.Verified on a dev instance
Over the real agent-scoped MCP route:
tools/listautodefault)request completed, teardown 837ms laterjob_complete; run and agent intactDELETE /api/v1/agents/:idon a job agentjob_completeon a job agentReview
Three personas (release-readiness, backend-security, infra) reviewed this specifically for release side effects.
runningafter its agent self-archived; measured on the dev stack, a run the JobService is monitoring goes tocrashedin under 2s (agentSessionCrashedreturns true oncegetAgentfinds no row), andreconcileActiveRunsrestarts monitors at boot. The observed row had been inserted outside that lifecycle; the reviewer confirmed and dismissed it. The underlying concern was still worth acting on, though —crashedis a false alarm for a deliberate exit — hence the active-run guard above.finishis kernel handoff, not client receipt, so the 750ms settle can't be a hard bound. Accepted as a best-effort UX race rather than a correctness one — the archive is claimed and durable before the response, and MCP offers no client acknowledgement to wait on. Reviewer withdrew it and advised keeping 750ms.Checks
pnpm run check,pnpm run test(3780 passing),pnpm run finalize:webgreen.An earlier revision of this PR added a second tool plus reason/force/live-children guardrails and the locking to make those guardrails correct — ~1.3k lines. Those rules didn't apply to archiving a child, so they had no business applying only to archiving yourself; dropping them took the change to ~156 lines of
src.