Skip to content

feat(agent): add live upgrade — checkpoint subagents before restart and adopt orphans on startup (#71023) - #71027

Open
webtecnica wants to merge 2 commits into
NousResearch:mainfrom
webtecnica:feat/live-upgrade-subagent-checkpoint
Open

feat(agent): add live upgrade — checkpoint subagents before restart and adopt orphans on startup (#71023)#71027
webtecnica wants to merge 2 commits into
NousResearch:mainfrom
webtecnica:feat/live-upgrade-subagent-checkpoint

Conversation

@webtecnica

@webtecnica webtecnica commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

What

Implements live upgrade support for Hermes Agent: when the process restarts (e.g. via /update), running subagents are checkpointed with full state before shutdown and automatically recreated on startup, preserving their progress.

Part 1 — Rich checkpoint before restart (version 2)

checkpoint_active_subagents() in delegate_tool.py now serialises the full state of running subagents to $HERMES_HOME/state/subagent_checkpoints.json. Each record contains:

  • Metadata: subagent_id, parent_id, depth, goal, model, started_at, tool_count, status, last_tool
  • saved_messages: The full conversation history (tool calls + results) extracted from the child agent's _session_messages and safely serialized (non-serializable fields stripped, long content truncated)
  • resolved_context: The subagent's context from _subagent_goal

Checkpoint format is now version 2 (detectable by the "version": 2 field). Version 1 checkpoints (metadata only) from older code are still accepted gracefully.

Part 2 — Orphan adoption with progress preservation

adopt_orphaned_subagents() consumes the checkpoint on startup and stores the orphan data in a module-level variable _pending_orphans. Instead of just logging lost work and deleting the checkpoint:

  • Full orphan records (with saved messages if available) are kept in memory
  • The checkpoint warning now reports how many subagents have saved conversation state
  • get_pending_orphans() provides access to the stored data

Part 3 — Auto-recreation with progress continuity

New recreate_pending_subagents(parent_agent) function:

  • Reads the orphan data stored by adopt_orphaned_subagents()
  • For each orphan, calls delegate_task with the saved goal and context
  • When saved_messages are available (version 2+ checkpoint), formats the previous conversation as enriched context so the new subagent knows what was already done and picks up where it left off
  • Includes a CONTINUATION note in the context telling the new subagent not to re-do completed work
  • Called automatically from cli.py's first-turn agent initialization

Hooks

  • cli.py _run_cleanup() — calls checkpoint_active_subagents() before resource teardown
  • cli.py run() — calls adopt_orphaned_subagents() after the welcome banner
  • cli.py first-turn init — calls recreate_pending_subagents() when the agent is first initialized, auto-re-delegating orphans

Changes separated

The unrelated "vision capability guard" change has been split into a separate PR: #26 (fix/vision-capability-guard-71027)

Testing

24 tests in tests/tools/test_delegate_checkpoint.py covering:

  • _safe_serialize_messages edge cases (bytes, callbacks, truncation)
  • Path resolution under $HERMES_HOME
  • Version 2 checkpoint write with full state (messages + context)
  • Checkpoint write without agent object (graceful fallback)
  • Orphan adoption reads, stores pending, and removes checkpoint
  • Corrupt checkpoint is handled gracefully
  • get_pending_orphans() returns correct data
  • recreate_pending_subagents() re-delegates with goal
  • recreate_pending_subagents() builds enriched context from messages
  • Empty/missing goals skip gracefully
  • _remove_checkpoint_safe never raises

Closes

Closes #71023

Breaking Changes

None. The adopt_orphaned_subagents() return signature (int) is preserved. New functions (get_pending_orphans(), recreate_pending_subagents()) are additive.

@alt-glitch alt-glitch added type/feature New feature or request comp/cli CLI entry point, hermes_cli/, setup wizard tool/delegate Subagent delegation tool/vision Vision analysis and image generation area/sessions Session lifecycle, resume, persistence, history area/install-update Installer, updater, packaging, wheels, doctor P3 Low — cosmetic, nice to have needs-decision Awaiting maintainer decision before any implementation sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 24, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: #71027 combines the live-upgrade checkpoint proposal for #71023 with the same vision capability guard as #71024. The process-checkpoint work in #69918 is adjacent but covers detached processes rather than in-process subagents; please choose the intended scope and split/consolidate as appropriate.

@webtecnica
webtecnica force-pushed the feat/live-upgrade-subagent-checkpoint branch from ba078d8 to 266e2b4 Compare July 27, 2026 21:26
…eation on startup

- Save full conversation messages (tool calls + results) from each
  subagent's _session_messages into the checkpoint (version 2)
- Add _safe_serialize_messages() helper for safe JSON serialization
- Store orphan data in _pending_orphans module-level variable
- Add get_pending_orphans() to access stored orphan records
- Add recreate_pending_subagents(parent_agent) to auto-re-delegate
  orphans with saved goal + context + previous conversation history
- Integrate auto-recreation in cli.py first-turn agent init
- Update tests to cover version 2 checkpoint, pending orphans,
  safe serialization, and auto-recreation
@webtecnica

Copy link
Copy Markdown
Contributor Author

Rebased onto latest main, split the unrelated vision guard into #72917, and upgraded the checkpoint to preserve full subagent state (goal, context, conversation history) with auto-recreation via recreate_pending_subagents() on startup. Ready for re-review 🚀

@teknium1 teknium1 left a comment

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.

Thanks for splitting the vision change and extending the checkpoint data. The restart-recovery gap is real on current main, but this implementation needs lifecycle and delivery rework before it is safe.

Problems

  • tools/delegate_tool.py:4244 calls delegate_task without background=True. Its default is synchronous (tools/delegate_tool.py:2823, 3390-3391), unlike the normal top-level dispatch path (run_agent.py:6900-6919), so the first recovered user turn waits for the recreated work.
  • cli.py:1180-1187 checkpoints every _run_cleanup; normal interactive exit reaches cleanup at cli.py:17318. Startup then adopts (cli.py:13994-14000) and first chat recreates (cli.py:12778-12790) against whichever new session starts next. This can replay work after a normal exit into an unrelated session.
  • The checkpoint is removed before recreation (tools/delegate_tool.py:4134-4138), while failures are caught after pending state is cleared (4172-4175, 4259-4266), so failed recovery is not retryable.

Suggested changes

  • Gate recovery on a verified update handoff plus originating-session ownership.
  • Use the existing asynchronous delegation delivery path and add a real lifecycle test for non-blocking recovery and failed-dispatch retention.

Automated hermes-sweeper review.

Comment thread cli.py
# can be surfaced by adopt_orphaned_subagents() on next startup.
try:
from tools.delegate_tool import checkpoint_active_subagents
checkpoint_active_subagents()

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.

_run_cleanup() also runs for ordinary CLI exit (cli.py:17318 on current main), not only /update. With startup adoption and first-chat recreation below, this checkpoints and replays work into the next unrelated CLI session. Please gate this on a verified update/restart handoff and preserve originating-session ownership.

Comment thread tools/delegate_tool.py
# Store the full orphan data for later recreation
_pending_orphans = list(subagents)

# Remove the checkpoint file — data is now in memory

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.

This deletes the only durable recovery record before any recreation attempt. recreate_pending_subagents() clears pending state before dispatching and catches failures, so a failed dispatch cannot be retried after another restart. Retain or atomically update the record until each dispatch is accepted.

Comment thread tools/delegate_tool.py
# Call delegate_task to recreate the subagent
from tools.delegate_tool import delegate_task as _dt

_dt(

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.

This omits background=True. Current delegate_task resolves an omitted background value to false and waits synchronously, whereas normal top-level model delegation explicitly dispatches in the background. Recovery will block the first user turn; route it through the existing async ownership/delivery path.

Comment thread tools/delegate_tool.py
prev_work_lines.append(f"[tool_call]: {tname}")

if enriched_context:
enriched_context += "\n\n" + "\n".join(prev_work_lines)

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.

The saved transcript becomes context, and _build_child_system_prompt() inserts context verbatim into the new child's system prompt. That promotes previous tool output and assistant text to privileged instructions. Use a bounded, structured continuation representation that preserves the original trust boundary.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 30, 2026
@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown

This was generated by AI during triage.

Summary

One PR addresses Issue #71023. #71027 adds pre-restart checkpointing and startup re-delegation with saved goals, context, and conversation history, directly targeting lost subagent work, but the diff does not preserve live execution or safely recover results across restart.

Related pull requests

  • feat(agent): add live upgrade — checkpoint subagents before restart and adopt orphans on startup (#71023) #71027 best fix — (+919/-0) — best available fix (recorded best_fix), partial implementation: adds versioned subagent checkpoints, orphan adoption, and automatic recreation with prior conversation context, addressing the reported loss of in-progress work. The contributor review marked keep_open and identifies blocking lifecycle and recovery defects: recreation omits background=True, ordinary CLI exits create cross-session replay, the checkpoint is deleted before successful dispatch, and saved transcript content crosses into privileged prompt context.

Suggested consolidation

keep open with a salvage path: retain #71027 as the best available direction, but do not merge it until the blocking contributor review is addressed. Gate checkpointing and replay on a verified update/restart handoff with originating-session ownership, dispatch recovered work asynchronously through the existing delivery path, retain or atomically advance durable recovery state until dispatch succeeds, and represent saved progress with a bounded structured continuation that preserves the original trust boundary.

Complex graph

flowchart LR
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    I71023(["issue #71023 (open)"])
    P71027["PR #71027 (open)"]
    P71027 -->|best fix| I71023
    class I71023 open
    class P71027 open
    class P71027 best
    class P71027 target
    click I71023 "https://github.com/NousResearch/hermes-agent/issues/71023"
    click P71027 "https://github.com/NousResearch/hermes-agent/pull/71027"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label).

Cross-PR triage: Reviewed 1 pull request and 1 issue in this complex. Each diff was read against this issue; Assessment working set: 37 kB of PR diffs, 6 kB of issue/PR text, 3 kB of discussion (7 comments), 2 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/install-update Installer, updater, packaging, wheels, doctor area/sessions Session lifecycle, resume, persistence, history comp/cli CLI entry point, hermes_cli/, setup wizard needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state tool/delegate Subagent delegation type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Live upgrade — update Hermes without killing running subagents (zero-downtime update)

4 participants