Skip to content

fix(oneshot): honor resume state - #74397

Open
SE87H wants to merge 8 commits into
NousResearch:mainfrom
SE87H:agent/fix-oneshot-resume
Open

fix(oneshot): honor resume state#74397
SE87H wants to merge 8 commits into
NousResearch:mainfrom
SE87H:agent/fix-oneshot-resume

Conversation

@SE87H

@SE87H SE87H commented Jul 29, 2026

Copy link
Copy Markdown

What changed

  • forward --resume, --continue, and --no-restore-cwd through the top-level -z/--oneshot path
  • resolve the durable session tip, restore its conversation history, reopen the same session row, and reuse its session ID
  • restore the session working directory by default before constructing the agent
  • synchronize TERMINAL_CWD after a successful workspace restore so prompt, file, and terminal resolution use the same resumed workspace
  • make -z --continue prefer the current workspace before the historical global-MRU fallback, matching interactive CLI behavior
  • add regression coverage for option forwarding, same-session continuation, workspace-scoped selection, unavailable workspaces, explicit CWD opt-out, and stale TERMINAL_CWD
  • isolate the workspace-transition tests so their intentional production chdir() calls cannot leak into later tests

Root cause

The CLI parser accepted resume options for one-shot mode, but _run_and_exit_oneshot() and run_oneshot() did not forward or consume that state. A hermes -z --resume <id> invocation therefore created a fresh session instead of continuing the requested durable row.

The first patch also resolved bare --continue directly from the global CLI MRU. The follow-up scopes that lookup to the current Git workspace/CWD first, matching the established interactive resolver and preventing an unrelated project session from winning when the current workspace has history.

A later review identified a second workspace reference: Hermes prompt, file, and terminal resolution can prefer TERMINAL_CWD. Restoring only the process CWD could therefore resume the right history while tools still targeted a stale configured workspace. The current patch updates TERMINAL_CWD only after os.chdir() succeeds and before reopening the session or constructing the agent.

Impact

One-shot callers can now resume a persistent Hermes conversation without forking the session or losing its recorded workspace. Process CWD and TERMINAL_CWD are kept coherent for restored sessions. Failed workspace restoration leaves the prior environment and durable session untouched. --no-restore-cwd remains available for deliberate context changes. Bare --continue now selects the current workspace's latest CLI session when available.

Validation

  • earlier focused regression suite: tests/hermes_cli/test_tui_resume_flow.py — 75 passed
  • real explicit-resume E2E at that revision — same session row, no durable session-count increase, recorded working directory restored, cache reuse observed
  • workspace-context transition tests cover stale TERMINAL_CWD, ordering before session reopen/agent construction, missing workspace, failed chdir, and explicit opt-out
  • final test-isolation head: c55ce708ed2889ca3e9ec4529f6da9b1fd48b609
  • exact code was validated in disposable fork PR SE87H/hermes-agent#4; the validation branch differed only by contributor-attribution mappings required by fork governance
  • full CI run Endless Terminals Environment Integration #24: all 8 Python slices, Python E2E, desktop Playwright E2E, ruff/ty and Windows checks, OSV/supply-chain scans, lockfile and common-history gates passed
  • one unrelated asynchronous TTS timing test failed on the first slice-7 attempt; only that job was rerun and passed without code changes
  • disposable validation PR closed without merge after evidence capture
  • focused security/public-exposure review — no private identifiers, local paths, or real credentials in the published diff

Scope

This PR intentionally remains a focused one-shot resume correction. A separate non-blocking follow-up comment proposes centralizing future workspace transitions behind typed task-local and process-local APIs; that architectural direction is not required for this fix and is not bundled here.

@SE87H
SE87H marked this pull request as ready for review July 29, 2026 21:25
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard area/sessions Session lifecycle, resume, persistence, history 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 needs-decision Awaiting maintainer decision before any implementation labels Jul 29, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: #57859 and #70136 already implement one-shot session hydration. This patch adds explicit CWD restoration but differs in missing-session and first-use behavior, so please consolidate the intended resume contract rather than treating the patches as duplicates.

SE87H commented Jul 29, 2026

Copy link
Copy Markdown
Author

Consolidation decision after comparing #57859 and #70136:

  • This PR intentionally uses existing-session-only, fail-closed resume semantics. --resume must resolve to a durable session; an unknown ID or unavailable store is an error. Silently minting a new ID or degrading to stateless execution would make a caller believe continuity was preserved when it was not.
  • It keeps the useful --continue behavior from fix(cli): honor --resume/--continue in oneshot (-z) — hydrate and chain the session #57859, including latest-session and title resolution.
  • It reuses the existing canonical resolve_resume_session_id() path rather than bundling the broader compression-lineage changes from fix: preserve one-shot resume context #70136 into this focused fix.
  • It adds the missing workspace contract: restore the recorded CWD by default, with --no-restore-cwd as the explicit opt-out.
  • Validation includes a real two-invocation explicit-resume E2E: same requested session ID in usage readback, no increase in durable session count, prior context restored, and recorded CWD restored. The focused regression suite is 73/73 passing.

So the intended contract is: exact durable identity + canonical tip + history + recorded CWD, or fail visibly; never claim a resume after silently creating/forking/statelessly continuing.

@teknium1, you reviewed the directly related #57859 path; could you review this consolidated, current-main variant?

SE87H commented Jul 29, 2026

Copy link
Copy Markdown
Author

Follow-up pushed in 9c5274e9b: bare one-shot --continue now prefers the current Git workspace/CWD before the established global-MRU fallback, matching interactive CLI behavior. Added focused coverage for both workspace preference and fallback; the focused suite is now 75/75 passing. Maintainer write access to the fork branch has also been disabled; review remains possible through the PR normally.

SE87H commented Jul 30, 2026

Copy link
Copy Markdown
Author

Follow-up hardening pushed in 591ca367c and 284e6bafd:

  • CWD restoration now fails visibly when a resumed session records a workspace that no longer exists or cannot be entered, instead of silently continuing in the caller's current directory.
  • The durable session is reopened only after workspace restoration succeeds, so a failed resume does not mutate the session state first.
  • --no-restore-cwd remains the explicit opt-out and continues to resume the exact session/history without changing directories.
  • Added focused regression coverage for both the unavailable-workspace failure and the explicit opt-out.

The two follow-up commits touch only hermes_cli/oneshot.py and the new focused test file. I could inspect the resulting diff here, but did not have a runnable repository checkout in this environment to rerun the pytest suite.

@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 consolidating one-shot resume around the canonical session tip and adding fail-closed workspace handling. The current-main premise is valid: hermes_cli/oneshot.py:245-251 forwards no resume state, and hermes_cli/oneshot.py:410-443 creates a fresh session.

Problems

  • hermes_cli/oneshot.py:389 changes only the process CWD. It does not retarget TERMINAL_CWD. The established interactive helper deliberately sets both (cli.py:7134-7142, cli.py:7178-7179), because agent/runtime_cwd.py:67-73 and tools/file_tools.py:314-321 prefer TERMINAL_CWD. With an inherited/configured value, this resumes the history but still gives prompt construction and file/terminal tools the prior workspace.

Suggested changes

  • After successful os.chdir(saved_cwd), set os.environ["TERMINAL_CWD"] = saved_cwd.
  • Add coverage with a distinct pre-set TERMINAL_CWD and assert it is changed before agent construction.

This is an automated hermes-sweeper review.

Comment thread hermes_cli/oneshot.py
f"{saved_cwd}"
)
try:
os.chdir(saved_cwd)

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.

Please also set os.environ["TERMINAL_CWD"] = saved_cwd after this succeeds. Interactive resume does this in cli.py:7178-7179; prompt, terminal, and file resolution prefer that variable, so os.chdir() alone can leave a resumed one-shot operating on the prior configured workspace.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in e9db5835a: after os.chdir(saved_cwd) succeeds, one-shot resume now sets os.environ["TERMINAL_CWD"] = saved_cwd before the durable session is reopened and before AIAgent construction. Added focused coverage in b5123936c for stale-value replacement, missing/unenterable workspaces leaving the prior environment and session untouched, --no-restore-cwd preservation, and the value observed during agent construction. The implementation commit diff is limited to this synchronization. I reproduced the transition logic in an isolated environment (4/4); the full repository pytest run still requires a runnable checkout.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Full validation is now complete. The exact PR code head b5123936cb13005ba446835ee46b5da5d24564f7 was tested against the same main base in a disposable internal PR; its only additional changes were contributor-attribution mappings required by the fork's governance check. CI run #19 passed all 8 Python slices (including the focused one-shot tests), Python E2E, desktop Playwright E2E, ruff/ty and Windows checks, OSV/supply-chain scans, and lockfile/history gates. The validation PR was closed without merge. Leaving this thread for reviewer confirmation rather than self-resolving it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Final verification on head c55ce708ed2889ca3e9ec4529f6da9b1fd48b609: the focused workspace tests now also restore the process CWD after each test, preventing their intentional production chdir() calls from leaking into later tests. Disposable validation PR SE87H/hermes-agent#4 completed full CI run #24 successfully; one unrelated asynchronous TTS timing flake passed when only its job was rerun, with no code change. Leaving the thread unresolved for reviewer confirmation.

@teknium1 teknium1 added the sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform label Jul 30, 2026

SE87H commented Jul 31, 2026

Copy link
Copy Markdown
Author

Current-main integration postflight:

  • exact PR head b5123936cb13005ba446835ee46b5da5d24564f7 has completed the full repository CI successfully in the disposable validation described above
  • upstream main has since advanced to 98105f31f46d3de58a8f69a2a439cee3f7a5e389; the PR branch is now 347 commits behind and GitHub reports real merge conflicts
  • current main still lacks one-shot resume/continue state in run_oneshot, so the underlying defect remains present
  • a non-destructive merge gate in the fork confirmed that the validated branch cannot be merged mechanically without resolving actively changed entry-point files

I am not force-updating or replacing current-main files from the older branch, because that would risk erasing newer upstream work. The next safe operation is a line-by-line rebase/cherry-pick in a real Git checkout, followed by the same full CI validation. The validated semantics and focused tests are ready; only current-main conflict resolution remains.

SE87H commented Jul 31, 2026

Copy link
Copy Markdown
Author

Non-blocking architectural follow-up — not proposing to broaden this PR:

Hermes already has a centralized read side in agent/runtime_cwd.py, but workspace transitions are still written through several carriers: durable sessions.cwd, process CWD, TERMINAL_CWD, and task-local _SESSION_CWD.

A useful follow-up could be a small typed write-side API with two deliberately separate modes:

  • task-local bind for concurrent gateway/cron/ACP hosts (ContextVar only; never mutate process CWD or process environment)
  • process-local activate for finite CLI/one-shot execution (validate and enter the workspace, then publish the canonical TERMINAL_CWD, with fail-closed rollback semantics)

Resume resolution could also return one resolved context — canonical durable session ID, filtered history, recorded workspace — and let each surface apply the appropriate workspace mode before reopening the session or constructing the agent.

That would turn this split-brain class into one contract and test seam instead of requiring each surface to remember every carrier independently. The current PR should remain focused; this is only a possible follow-up design direction.

SE87H commented Jul 31, 2026

Copy link
Copy Markdown
Author

Final third-eye validation pass:

  • isolated the focused workspace tests so their production chdir() calls cannot leak process state into later tests (c55ce708e)
  • revalidated the exact fix in disposable fork PR SE87H/hermes-agent#4
  • all eight Python slices passed after rerunning only one unrelated flaky asynchronous TTS timing test; the one-shot resume/CWD suites were green on the original attempt
  • Python E2E, desktop Playwright, ruff/ty, Windows checks, supply-chain/OSV, locks, attribution, and common-history gates all passed
  • validation PR was closed without merge

No additional product behavior was added in this pass. The separate architectural comment is explicitly non-blocking and should not broaden this PR.

@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown

This was generated by AI during triage.

Summary

Seven PRs cover two one-shot flag-loss causes: #26669, #26771, and #72075 propagate rule isolation into AIAgent, while #40333, #49204, #57859, and #74397 either hydrate resume state or reject unsupported resume combinations. The current implementations differ materially in Termux coverage, session creation semantics, workspace restoration, and whether --resume/--continue is honored or rejected.

Related pull requests

Duplicates

#26669, #26771, and #72075 implement substantially the same --ignore-rules propagation; #72075 is the explicit current-main salvage, so #26669 is the closed predecessor and #26771 can close as duplicate/superseded by #72075. #40333, #57859, and #74397 overlap on resume hydration but are not exact duplicates: #57859 adds create-on-first-use IDs, while #74397 requires an existing session and adds workspace restoration; #49204 is the competing fail-loud design rather than a duplicate.

Suggested consolidation

Keep #72075 open as the consolidated salvage path for #26633/#72064 and close #26771 as duplicate of #72075 despite its keep-open review, because #72075's diff supplies the review's missing Termux forwarding and current tuple contract. Author action: rebase #74397 onto current main, preserve its validated session-tip/CWD/TERMINAL_CWD behavior, and explicitly reconcile #57859's create-on-first-use contract; keep #57859 and #49204 open meanwhile because both are recorded best fixes with distinct supported designs, requiring respectively the requested SQLite E2E coverage and the requested pre-startup guard/dispatch tests before maintainers choose hydration versus fail-loud behavior.

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
    I49195(["issue #49195 (open)"])
    P74397["PR #74397 (open)"]
    P74397 -->|best fix| I49195
    class I49195 open
    class P74397 open
    class P74397 best
    class P74397 target
    click I49195 "https://github.com/NousResearch/hermes-agent/issues/49195"
    click P74397 "https://github.com/NousResearch/hermes-agent/pull/74397"
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 7 pull requests and 3 issues in this complex. Each diff was read against this issue; Assessment working set: 88 kB of PR diffs, 33 kB of issue/PR text, 18 kB of discussion (27 comments), 18 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

@SE87H
SE87H force-pushed the agent/fix-oneshot-resume branch from c55ce70 to 5931f54 Compare August 4, 2026 21:15
@SE87H

SE87H commented Aug 4, 2026

Copy link
Copy Markdown
Author

M6R fresh-main rebase complete.

  • Rebased onto frozen main 4371712; main later advanced to fdc342c without touching concerned files.
  • Resolved the semantic conflict by preserving current-main test topology and transplanting resume invariants into tests/hermes_cli/test_oneshot_resume_flow.py.
  • 6 dedicated tests and 31 current resume/session/CWD tests passed; compile, Ruff, and diff-check passed.
  • New head: 5931f54.

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

Labels

area/sessions Session lifecycle, resume, persistence, history comp/cli CLI entry point, hermes_cli/, setup wizard needs-decision Awaiting maintainer decision before any implementation P2 Medium — degraded but workaround exists 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 type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants