fix(agent): pin claude session by constant title, archive on reset

This commit is contained in:
müde 2026-07-05 18:13:54 +02:00
commit e35acca814
4 changed files with 369 additions and 164 deletions

View file

@ -680,21 +680,24 @@ pub struct Bus {
/// `container_view` can surface the status on the dashboard without
/// a live socket call.
rate_limited: Arc<AtomicBool>,
/// One-shot: next `run_claude` call drops `--continue`, starting
/// a fresh claude session. Set by `POST /api/new-session` from
/// the per-agent web UI; consumed (cleared back to false) by the
/// next turn. Subsequent turns resume normal `--continue`
/// behavior. Atomic so the consumer can take-and-clear without a
/// lock.
skip_continue_once: Arc<AtomicBool>,
/// One-shot: operator asked to reset the claude session (`POST
/// /api/new-session`). Consumed at the *next* turn boundary by
/// `turn::drive_turn`, which archives the current session so the turn
/// starts fresh. Deferred (not applied in the handler) so the archive
/// never races a claude process mid-write — one claude per container,
/// serialized by the serve loop, so the turn boundary is the only point
/// where no session file is open.
session_reset_pending: Arc<AtomicBool>,
/// Current fresh-claude-session id (FK to `sessions.id`). Set by the
/// bin loop after minting a session row on a fresh start; stamped onto
/// every `turn_stats` row until the next fresh session. `None` before
/// the first fresh turn or when the stats db is unavailable.
session_id: Arc<Mutex<Option<i64>>>,
/// One-shot: `run_claude` flips this true when it suppresses
/// `--continue` (a fresh session). The bin loop takes-and-clears it
/// after the turn to decide whether to mint a new `sessions` row.
/// One-shot: `run_claude` flips this true when it creates a fresh claude
/// session (`--name <title>` on a title miss). The bin loop takes-and-
/// clears it after the turn to decide whether to mint a new `sessions`
/// row. Session identity itself is handled entirely in `turn.rs` via the
/// constant title + archive — this flag is purely a stats signal.
fresh_session: Arc<AtomicBool>,
/// Per-turn tool-call counter. Reset by the bin loop between
/// turns via `take_tool_calls`. Populated by `observe_stream` as
@ -773,7 +776,7 @@ impl Bus {
last_ctx_usage: Arc::new(Mutex::new(None)),
last_cost_usage: Arc::new(Mutex::new(None)),
rate_limited: Arc::new(AtomicBool::new(was_rate_limited)),
skip_continue_once: Arc::new(AtomicBool::new(false)),
session_reset_pending: Arc::new(AtomicBool::new(false)),
session_id: Arc::new(Mutex::new(None)),
fresh_session: Arc::new(AtomicBool::new(false)),
tool_calls: Arc::new(Mutex::new(std::collections::HashMap::new())),
@ -797,23 +800,24 @@ impl Bus {
self.event_seq.fetch_add(1, Ordering::SeqCst) + 1
}
/// Arm the one-shot: the next claude invocation will run without
/// `--continue`, dropping any prior session context. Idempotent
/// — calling twice in a row before the next turn still consumes
/// to a single fresh-start.
pub fn request_new_session(&self) {
self.skip_continue_once.store(true, Ordering::SeqCst);
/// Request a session reset (operator `POST /api/new-session`). Deferred:
/// the flag is consumed at the next turn boundary by `drive_turn`, which
/// archives the current session so no claude process is mid-write when the
/// file is renamed. Idempotent — two clicks before the next turn still
/// archive once.
pub fn request_session_reset(&self) {
self.session_reset_pending.store(true, Ordering::SeqCst);
}
/// Take + clear the one-shot. Returns true iff the caller should
/// run claude without `--continue` for this turn.
/// Take + clear the session-reset one-shot. Returns true iff `drive_turn`
/// should archive the current session before this turn.
#[must_use]
pub fn take_skip_continue(&self) -> bool {
self.skip_continue_once.swap(false, Ordering::SeqCst)
pub fn take_session_reset(&self) -> bool {
self.session_reset_pending.swap(false, Ordering::SeqCst)
}
/// Mark that the current turn started a fresh claude session.
/// `run_claude` calls this when it suppresses `--continue`.
/// `run_claude` calls this when it creates a new titled session.
pub fn mark_fresh_session(&self) {
self.fresh_session.store(true, Ordering::SeqCst);
}