Skip to content

feat(agents): reopen a closed agent instead of only deleting it - #503

Open
snehithareddy28 wants to merge 1 commit into
chaitanyagiri:mainfrom
snehithareddy28:fix/reopen-archived-agent
Open

feat(agents): reopen a closed agent instead of only deleting it#503
snehithareddy28 wants to merge 1 commit into
chaitanyagiri:mainfrom
snehithareddy28:fix/reopen-archived-agent

Conversation

@snehithareddy28

Copy link
Copy Markdown
Contributor

What & why

Closes #447. Closing an agent from the strip retains it — store.ts says so outright ("Retained + flagged, NOT deleted"), and Command Center → Archived → show closed agents lists it. But the only control on an archived row was the ✕ that forgets it permanently, so the retained record had no way back onto the floor: the one deliberate way to close an agent was effectively one-way.

"Restore team" does not cover this, and it is easy to assume it does. That button is fed by restorableAgents, which only collects agents whose terminal died unexpectedly (crash / quit recovery). An agent you closed on purpose never enters that list.

Archived rows now have a reopen button. It brings the agent back with its own id, its own cwd and its prior CLI session resumed — which is what makes memory.md, the inbox and the registry entry reattach by id rather than coming back as a stranger. addAgent already un-archives by id ("an id is active xor archived"), so the row leaves the list as the card returns to the floor.

The recipe is shared, not copied. The subtle part of a respawn already existed inside useRestoreTeam, so rather than writing a second copy that could drift, it moves to hooks/respawnAgent.ts and both entry points call it:

  • re-enter the isolated worktree when it is still on disk — probed, never assumed, because git worktree add would conflict on the existing path and branch, and a fresh worktree would strand the uncommitted work in the old one;
  • fall back to the base repo when that worktree has been pruned, and tell the caller so the dead path is dropped instead of re-probed forever;
  • resume the prior session, never re-isolate;
  • treat an id whose terminal is already running as already-live rather than a phantom failure.

The module is structural and dependency-injected (no store, no window, no config imports), so the sequence is unit-testable without Electron — the same shape queueDelivery.ts uses for its gate.

Two smaller UX repairs on the same row: a reopen that cannot work now says why, on the row that failed, instead of looking inert; and the ✕ finally carries a title saying it deletes permanently.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Docs
  • Build / CI

Evidence

Before

Test step log on main: every test of the reopen recipe fails — the shared respawn module does not exist, because the only path to it was buried inside useRestoreTeam and archived rows had no action at all

After

Test step log with the fix: the recipe is pinned — own id, resume on, isolate off, durable role kept, worktree re-entered or base repo when pruned, already-live is not a failure, a throwing spawn is reported; suite 841/841


Notes for review:

  • The UI change, in words, since the evidence above is the recipe's tests: each archived row keeps its portrait, name and cwd, and gains a small reopen button before the ✕. While a reopen is in flight the button reads reopening… and every row's button is disabled, so a second click cannot spawn a duplicate. A failure renders as one coral line under that row (Couldn't reopen: …). Nothing else in the panel moves; the row becomes a small column so the error has somewhere to go. Strings are new keys in commandCenter and are translated in en, zh-CN and ar.
  • useRestoreTeam's behaviour is unchanged — same flags, same per-agent tallies (restored / already live / failed — …), same roster-order insertion, same worktree-gone warning. It is ~60 lines shorter because the per-agent body is now one call.
  • Reopening an isolated agent pairs with the teardown fix in fix(worktree): never auto-discard a named agent's unintegrated work on PTY exit #496: a worktree holding unintegrated work is no longer removed when the tab closes, so reopening walks straight back into it. When it was removed (clean and integrated), gitIsRepo returns false and the agent reopens on its base repo with worktree gone — using base repo on its card.
  • already-live puts the card back rather than reporting an error: the roster and a live PTY had disagreed, and restoring the card is the honest repair.
  • Seven tests in test/respawn-agent.test.cjs, with the two main-process calls faked. They fail on main because the module does not exist there.
  • npm run typecheck and the full npm run test:focused suite (841/841) pass locally.

🤖 Generated with Claude Code

Closing an agent's tab retains it — the store says so outright ("Retained
+ flagged, NOT deleted") and Command Center → Archived lists it — but the
only control on an archived row was the ✕ that forgets it permanently. The
retained record had no way back onto the floor, so the one deliberate way
to close an agent was effectively one-way. "Restore team" does not help:
it is fed by restorableAgents, which only collects agents whose terminal
died unexpectedly, and a deliberately closed agent never enters that list.

Archived rows gain a reopen button. It puts the agent back with its own id,
its own cwd and its prior CLI session resumed, which is what makes memory.md,
the inbox and the registry entry reattach by id. addAgent already un-archives
by id, so the row leaves the list as the card returns to the floor.

The respawn recipe is the subtle part and it already existed inside
useRestoreTeam, so rather than writing a second one that could drift, it
moves to hooks/respawnAgent.ts and both callers share it: re-enter the
isolated worktree when it is still on disk (probed, never assumed) and fall
back to the base repo when it has been pruned, never re-isolate, resume the
prior session, and treat an id whose terminal is already running as
already-live rather than a failure. The module is structural and
dependency-injected, so the sequence is unit-testable without Electron.

A reopen that cannot work now says why on the row that failed, and the ✕
finally states that it deletes permanently.

Seven tests in test/respawn-agent.test.cjs cover the recipe: own id, resume
on, isolate off, durable role preserved, worktree re-entered, worktree gone
→ base repo, already-live, no saved command, and a throwing spawn reported
rather than propagated.

Closes chaitanyagiri#447

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

Archived (manually closed) agents have no way back — only permanent delete

1 participant