Skip to content

Gracefully protect live sessions during daemon updates - #333

Closed
sethkarten wants to merge 20 commits into
mainfrom
sethkarten/graceful-daemon-update
Closed

Gracefully protect live sessions during daemon updates#333
sethkarten wants to merge 20 commits into
mainfrom
sethkarten/graceful-daemon-update

Conversation

@sethkarten

@sethkarten sethkarten commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Make Prime Agent daemon updates/replacements graceful by protecting every live active session, even when the session is currently idle.

Previously the update/startup safety guard only treated streaming, compacting, or pending-message sessions as risky. That allowed an idle but still live session to be shut down when a new CLI version replaced a stale daemon, which makes existing orchestrator/long-running sessions disappear after updating.

This PR changes the safety rule to:

  • saved-only sessions can be reloaded by a fresh daemon
  • any live session with an activeSessionId blocks implicit daemon replacement
  • --force remains the explicit override for users who really want to replace a daemon with live sessions
  • stale-daemon startup/update wording now says “live sessions” instead of “busy sessions”

Tests

  • npm test --workspace @earendil-works/pi-coding-agent -- daemon-launch.test.ts daemon-stop-confirm.test.ts
  • npm run check
  • pre-commit npm run check

Note

Medium Risk
Changes daemon stop/restart and session identity across update and stale takeover paths; incorrect classification could still drop live orchestrator sessions or mis-bind cron jobs, though behavior is covered by new unit tests.

Overview
Daemon updates and stale-daemon replacement no longer treat every live session as disposable. Safety now distinguishes restorable idle top-level sessions (persisted sessionFile, no volatile in-memory work) from at-risk ones that would lose state if the daemon stops—streaming/compacting, bash, queued messages, running RLM children, subagents, or missing session files.

Restorable sessions are reopened automatically after restart via create with the original activeSessionId and session path; partial failures surface warnings but do not abort startup. Implicit stale-daemon takeover only blocks when unrestorable live work is present; user confirmation (startup/update) triggers on at-risk sessions, not idle live chats.

Cron job rebinding gains matchActiveSessionId so restores can bind heartbeats by session file without stealing jobs from another live session with the same id seed.

Reviewed by Cursor Bugbot for commit f68ebd8. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Preserve and restore live sessions during daemon updates and stale-daemon takeovers

  • When replacing a stale daemon (update or startup takeover), the CLI now classifies each live session as restorable (idle, persisted, top-level) or at-risk (volatile work, subagent, no session file) before proceeding.
  • Safe sessions are automatically reopened on the new daemon via create commands that seed the previous activeSessionId and session path; failures emit warnings to stderr but do not abort other restores.
  • Replacement is blocked entirely when unrestorable or volatile sessions exist; the user is re-prompted if risk increases between the initial probe and the actual restart.
  • Cron jobs are rebound by session file alone (not activeSessionId) when restoring, to avoid unintended moves.
  • Behavioral Change: daemon replacement that previously silently dropped live sessions now either refuses to proceed or restores them; confirmation prompts now distinguish restorable from unrestorable sessions.

Macroscope summarized e7d2517.

@sethkarten
sethkarten marked this pull request as ready for review July 7, 2026 00:03
Comment thread packages/coding-agent/src/cli/daemon-launch.ts
Comment thread packages/coding-agent/src/package-manager-cli.ts Outdated
Comment thread packages/coding-agent/src/cli/daemon-launch.ts
Comment thread packages/coding-agent/src/cli/daemon-launch.ts
Comment thread packages/coding-agent/src/main.ts
Comment thread packages/coding-agent/src/cli/daemon-launch.ts Outdated
Comment thread packages/coding-agent/src/modes/daemon/daemon-mode.ts Outdated
Comment thread packages/coding-agent/src/cli/daemon-launch.ts
Comment thread packages/coding-agent/src/package-manager-cli.ts Outdated
Comment thread packages/coding-agent/src/package-manager-cli.ts Outdated
Comment thread packages/coding-agent/src/cli/daemon-launch.ts
Comment thread packages/coding-agent/src/cli/daemon-launch.ts Outdated
Comment thread packages/coding-agent/src/main.ts Outdated
Comment thread packages/coding-agent/src/main.ts Outdated
Comment thread packages/coding-agent/src/package-manager-cli.ts Outdated
Comment thread packages/coding-agent/src/modes/daemon/daemon-mode.ts
Comment thread packages/coding-agent/src/cli/daemon-launch.ts
Comment thread packages/coding-agent/src/cli/daemon-launch.ts
];
}

export async function restoreDaemonSessionSummaries(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium cli/daemon-launch.ts:215

restoreDaemonSessionSummaries recreates each session with a config object containing only sessionDir and cwd, dropping the per-session runtime settings (model/provider, thinkingLevel, tool/extension restrictions, etc.) from the original live session. createRuntime() fills missing fields from the daemon defaults, so after a stale-daemon takeover an idle live session can reopen with a different model and tool restrictions than it was running with before the restart. Consider forwarding the original session's runtime config fields into the create request so the reopened session preserves its settings.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @packages/coding-agent/src/cli/daemon-launch.ts around line 215:

`restoreDaemonSessionSummaries` recreates each session with a `config` object containing only `sessionDir` and `cwd`, dropping the per-session runtime settings (model/provider, `thinkingLevel`, tool/extension restrictions, etc.) from the original live session. `createRuntime()` fills missing fields from the daemon defaults, so after a stale-daemon takeover an idle live session can reopen with a different model and tool restrictions than it was running with before the restart. Consider forwarding the original session's runtime config fields into the `create` request so the reopened session preserves its settings.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e7d2517. Configure here.

const createResponse = await client.request(
{
type: "create",
activeSessionId: session.activeSessionId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update drops restorable live sessions

High Severity

Self-update confirmation now treats idle live sessions as safe because they are supposed to be reopened, but the prepare-less restart path still shuts the daemon down and brings it back with no restore. When an older daemon lacks prepare_update_restart, those restorable sessions are discarded after the update even though the user was never warned.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e7d2517. Configure here.

@kevinjosethomas

Copy link
Copy Markdown
Member

Made some improvements here in #369 and #326, and this will architecturally shift with #383 so closing this PR.

@kevinjosethomas
kevinjosethomas deleted the sethkarten/graceful-daemon-update branch July 16, 2026 23:50
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.

2 participants