fix(agent): pin claude session by constant title, archive on reset
This commit is contained in:
parent
6528a90233
commit
e35acca814
4 changed files with 369 additions and 164 deletions
|
|
@ -137,7 +137,7 @@ if a new inbox message arrives before this turn ends"). The
|
|||
|
||||
```
|
||||
claude --print --verbose --output-format stream-json --model <name> \
|
||||
--effort <level> --continue \
|
||||
--effort <level> --resume <title> # or --name <title> on first use \
|
||||
--system-prompt-file /run/hive/claude-system-prompt.md \
|
||||
--mcp-config /run/hive/claude-mcp-config.json --strict-mcp-config \
|
||||
--tools <builtins> --allowedTools <builtins+mcp>
|
||||
|
|
@ -180,16 +180,34 @@ The effective window drives watermarks and is exposed at runtime
|
|||
via `/api/state.context_window_tokens` so the UI can show a
|
||||
percentage-of-window ctx badge.
|
||||
|
||||
`--continue` keeps a persistent session per agent (claude stores
|
||||
sessions in `~/.claude/projects/`, which is bind-mounted
|
||||
persistently). Auto-compact and auto-memory are disabled via the
|
||||
managed settings at `/etc/claude-code/managed-settings.json` because
|
||||
hyperhive owns compaction — see [Compaction](#compaction) below.
|
||||
A one-shot `--continue` suppression is available via
|
||||
`POST /api/new-session` (or `/new-session` slash command in the
|
||||
per-agent terminal) — `Bus::take_skip_continue()` flips an
|
||||
`AtomicBool` once per turn, the next claude invocation drops
|
||||
`--continue`, every subsequent turn resumes normal behaviour.
|
||||
**Session identity — a constant title.** Every turn keys on one fixed,
|
||||
harness-owned session title (`turn::session_title()`, default
|
||||
`hive-session`, override `HIVE_SESSION_TITLE`). A turn `--resume <title>`s
|
||||
it; the *first* use (bootstrap, post-archive, post-purge) misses and
|
||||
`turn::run_claude_resume_or_create` re-runs the same prompt once with
|
||||
`--name <title>` to mint it. That single self-heal rule is the whole
|
||||
identity system — there is **no** scraped session-id file. Because the
|
||||
title is constant, `/compact` and its post-compact retry provably target
|
||||
the same session (killing the old "compact ran on a different/empty
|
||||
session" bug), and a `choom` invocation in the same cwd can't hijack the
|
||||
context (it won't carry our title). claude stores sessions in
|
||||
`~/.claude/projects/<cwd-slug>/<uuid>.jsonl` (bind-mounted persistently);
|
||||
`--name` writes the title into the file as a `custom-title` event, which
|
||||
is what `--resume <title>` resolves against. We never pass bare
|
||||
`--continue` (it resumes the *latest* session in the cwd — the hijack
|
||||
vector). Auto-compact and auto-memory are disabled via the managed
|
||||
settings at `/etc/claude-code/managed-settings.json` because hyperhive
|
||||
owns compaction — see [Compaction](#compaction) below.
|
||||
|
||||
**Session reset** is available via `POST /api/new-session` (or
|
||||
`/new-session` slash command). It does *not* touch the session inline —
|
||||
that would race a mid-write claude process. Instead `Bus::request_session_reset()`
|
||||
sets a one-shot flag consumed at the next turn boundary by `drive_turn`,
|
||||
which **archives** the current session: the backing `<uuid>.jsonl` is
|
||||
renamed to `<uuid>.jsonl.archived` (dropped out of claude's `*.jsonl`
|
||||
resolution glob, history preserved on disk, only the file carrying *our*
|
||||
title — any `choom` session sharing the cwd is left alone). The next
|
||||
turn's `--resume <title>` then misses and self-heals into a fresh session.
|
||||
|
||||
### Compaction
|
||||
|
||||
|
|
@ -231,20 +249,22 @@ Useful for agents running large-context models (sonnet/opus) where the 75%
|
|||
heuristic fires before the session is actually full — the reactive path
|
||||
(compact-on-overflow when the session hits the hard limit) still applies.
|
||||
|
||||
- **Auto session-reset** — a third path that fires when both
|
||||
conditions hold: context is ≥ a watermark (`HIVE_AUTO_RESET_WATERMARK_TOKENS`,
|
||||
default **50% of `context_window_tokens(model)`**) AND the time since
|
||||
the last turn exceeds the assumed prompt-cache TTL
|
||||
(`HIVE_CACHE_TTL_SECS`, default `3600`).
|
||||
Claude's prompt cache lives ~5 minutes; if the cache is already
|
||||
cold, resuming with `--continue` pays the full re-upload cost of
|
||||
the current context with no benefit over starting fresh. So:
|
||||
`drive_turn` injects one `AUTO_RESET_CHECKPOINT_PROMPT` notes turn
|
||||
("flush state to files, cache is cold") then arms
|
||||
`Bus::take_skip_continue()` for the real turn — the next turn runs
|
||||
without `--continue`, starting a fresh session. Unlike proactive
|
||||
compaction the session is dropped entirely, not compacted. Set
|
||||
`HIVE_AUTO_RESET_WATERMARK_TOKENS=0` to disable.
|
||||
- **Auto session-reset** — a third path (`turn::maybe_auto_reset`,
|
||||
pre-turn) that fires when both conditions hold: context is ≥ a watermark
|
||||
(`HIVE_AUTO_RESET_WATERMARK_TOKENS`, default **50% of
|
||||
`context_window_tokens(model)`**) AND the time since the last turn
|
||||
exceeds the assumed prompt-cache TTL (`HIVE_CACHE_TTL_SECS`, default
|
||||
`3600`). Claude's prompt cache goes cold after a while; once it's cold,
|
||||
`--resume`-ing a large session pays the full re-upload cost with no
|
||||
benefit over starting fresh. So `drive_turn` **archives** the current
|
||||
session (same mechanism as the operator reset — rename `<uuid>.jsonl` →
|
||||
`.archived`) so the next turn's `--resume <title>` misses and starts
|
||||
fresh. Unlike proactive compaction the session is dropped entirely, not
|
||||
compacted — and *no* preceding checkpoint turn runs, because any turn
|
||||
before the reset would just re-warm the cache and defeat the purpose.
|
||||
Set `HIVE_AUTO_RESET_WATERMARK_TOKENS=0` to disable. Auto-reset and the
|
||||
operator reset are mutually exclusive per turn (both archive → fresh
|
||||
turn), so an explicit operator reset short-circuits the heuristic.
|
||||
|
||||
The child runs with `cwd = /state` (when the bind exists; falls
|
||||
back to the parent's cwd in dev), so any relative path in a tool
|
||||
|
|
@ -318,9 +338,11 @@ socket at `/run/hive/` once at startup:
|
|||
same way.
|
||||
|
||||
The shared per-turn plumbing lives in `hive_ag3nt::turn::{write_mcp_config,
|
||||
write_settings, write_system_prompt, run_turn, drive_turn,
|
||||
emit_turn_end, wait_for_login, compact_session}` — the single code path
|
||||
every agent role runs through.
|
||||
write_system_prompt, run_turn, drive_turn, emit_turn_end, wait_for_login,
|
||||
compact_session, session_title}` — the single code path every agent role
|
||||
runs through. Session identity is internal to the module:
|
||||
`run_claude_resume_or_create` (resume-or-create self-heal) and
|
||||
`archive_session` (turn-boundary reset) sit under `run_turn` / `drive_turn`.
|
||||
|
||||
### Reference docs (`hyperhive.docs.enable`)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue