Skip to content

fix(oneshot): honor --resume/--continue so -z can be session-aware - #40333

Closed
jibot3 wants to merge 1 commit into
NousResearch:mainfrom
jibot3:fix/oneshot-honor-resume-continue
Closed

fix(oneshot): honor --resume/--continue so -z can be session-aware#40333
jibot3 wants to merge 1 commit into
NousResearch:mainfrom
jibot3:fix/oneshot-honor-resume-continue

Conversation

@jibot3

@jibot3 jibot3 commented Jun 6, 2026

Copy link
Copy Markdown

Problem

hermes -z (oneshot) bypasses cli.py entirely and historically ignored --resume / --continue. It always built a fresh AIAgent and called agent.chat(), so the flags were silently dropped.

This bites any frontend that drives oneshot with a stable session id per conversation — e.g. a small HTTP shim that runs hermes --profile p -z "<prompt>" --continue <id> for each inbound message (a piped-channel bridge). The agent is amnesiac: every turn starts with history=0 and writes a new throwaway timestamped session, so the requested id never accumulates a transcript. The user sees "this is the first thing you've said to me" on every message.

Reproduce:

SID="diag-$(date +%s)"
hermes -z "Remember codeword PURPLE-NARWHAL-42. Ack." --continue "$SID"
hermes -z "What codeword did I give you?" --continue "$SID"
# turn 2 (before fix): "this is the first message."

Fix

Make oneshot session-aware when (and only when) a session is requested:

  • hermes_cli/main.py -z dispatch resolves --resume/-r <id|title> or --continue/-c [name] (reusing the existing _resolve_session_by_name_or_id / _resolve_last_session) and passes session_id to run_oneshot.
  • hermes_cli/oneshot.py run_oneshot threads session_id to _run_agent.
  • _run_agent, when session_id is set: walks the compression chain to the live tip (resolve_resume_session_id), loads prior history (get_messages_as_conversation), reopens the session, builds AIAgent(session_id=...), and runs via run_conversation(conversation_history=...) so the transcript replays and only new messages flush back under the same id. With no session_id the path is unchanged — a fresh, stateless one-shot run.

create_session is INSERT OR IGNORE, so reusing an id is idempotent and a brand-new conversation id works on its first turn (empty history, created on first write).

Why not switch the caller to chat -q --resume?

That path is session-aware but emits banner/box/"Resume this session with…" chrome on stdout, which a piping caller then has to strip with fragile rules. Fixing oneshot keeps -z's clean single-block stdout contract intact.

Tests

Added to tests/hermes_cli/test_tui_resume_flow.py:

  • main -z dispatch forwards the resolved session id to run_oneshot;
  • _run_agent(session_id=...) resumes with replayed history via run_conversation (not chat) and reopens the session;
  • the no-session path stays on chat() with session_id=None (stateless behavior unchanged).

Also updated the existing test_main_top_level_oneshot_accepts_toolsets exact-match assertion for the new session_id kwarg. Full file: 48 passed.

After: turn 2 above correctly answers PURPLE-NARWHAL-42, and a single session id holds the whole conversation.

`hermes -z` (oneshot) bypasses cli.py and historically ignored
--resume/--continue, always building a fresh AIAgent and calling
agent.chat(). Any frontend that drives oneshot with a stable session id
per conversation — e.g. an HTTP shim that runs
`hermes --profile p -z "<prompt>" --continue <id>` for each inbound
message — therefore got an amnesiac agent: every turn started with
history=0 and wrote a new throwaway session, so the id never accumulated
a transcript.

Make oneshot session-aware when (and only when) a session is requested:

- main.py `-z` dispatch resolves --resume/-r <id|title> or
  --continue/-c [name] (reusing _resolve_session_by_name_or_id /
  _resolve_last_session) and passes session_id to run_oneshot.
- run_oneshot threads session_id to _run_agent.
- _run_agent, when session_id is set: walks the compression chain to the
  live tip, loads prior history via get_messages_as_conversation, reopens
  the session, builds AIAgent(session_id=...), and runs via
  run_conversation(conversation_history=...) so the transcript replays and
  only new messages flush back under the same id. With no session_id the
  path is unchanged — a fresh, stateless one-shot run.

create_session is INSERT OR IGNORE, so reusing an id is idempotent and a
brand-new conversation id works on its first turn (empty history, created
on first write).

Tests: main dispatch forwards the resolved session id; _run_agent resumes
with replayed history via run_conversation (not chat) and reopens the
session; the no-session path stays on chat() with session_id=None.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants