hive-agent: correct doc comments that describe sentinel files, session flags and outcomes the code no longer has
This commit is contained in:
parent
70d1b542e3
commit
ac0bed47e9
11 changed files with 40 additions and 38 deletions
|
|
@ -46,8 +46,8 @@ pub fn announce(label: &str, todos: &Todos, wake: &Notify) {
|
|||
let summary = format!(
|
||||
"You were just (re)started. If you were mid-task, check \
|
||||
`/agents/{label}/state/` for your notes and pick up where you left \
|
||||
off — claude's `--continue` session is intact, so prior context is \
|
||||
still in your window."
|
||||
off — your claude session (resumed by title) is intact, so prior \
|
||||
context is still in your window."
|
||||
);
|
||||
match todos.upsert(SUBSYSTEM, Some(KEY), &summary, None, true) {
|
||||
Ok((_, true)) => wake.notify_one(),
|
||||
|
|
|
|||
|
|
@ -302,10 +302,9 @@ pub struct Bus {
|
|||
last_cost_usage: Arc<Mutex<Option<TokenUsage>>>,
|
||||
/// True while the harness is parked after a rate-limit response.
|
||||
/// Set by `emit_status("rate_limited")`, cleared by
|
||||
/// `emit_status("online")`. Also mirrored to a sentinel file at
|
||||
/// `{state_dir}/hyperhive-rate-limited` so the host-side
|
||||
/// `container_view` can surface the status on the dashboard without
|
||||
/// a live socket call.
|
||||
/// `emit_status("online")`. `emit_status` also persists it in
|
||||
/// `hyperhive-harness.json` (see `harness_state`), so the host can
|
||||
/// surface the status on the dashboard without a live socket call.
|
||||
rate_limited: Arc<AtomicBool>,
|
||||
/// One-shot: operator asked to reset the claude session (`POST
|
||||
/// /api/new-session`). Consumed at the *next* turn boundary by
|
||||
|
|
|
|||
|
|
@ -30,10 +30,10 @@ pub fn hive_domain() -> Option<String> {
|
|||
|
||||
/// Human display name of this hive (e.g. `pr1ma`). Distinct from
|
||||
/// [`hive_domain`] — the domain is the machine-readable DNS address;
|
||||
/// this is the prose label humans use in conversation. Returns None
|
||||
/// when `/etc/hyperhive-hive-facts` carries no name (the host's
|
||||
/// `services.hyperhive.hiveName` is unset), in which case callers fall
|
||||
/// back to the domain or the short label at their discretion.
|
||||
/// this is the prose label humans use in conversation. Reads the
|
||||
/// `HYPERHIVE_HIVE_NAME` env var; returns None when it is unset or empty
|
||||
/// (the host's `services.hyperhive.hiveName` is unset), in which case
|
||||
/// callers fall back to the domain or the short label at their discretion.
|
||||
#[must_use]
|
||||
pub fn hive_name() -> Option<String> {
|
||||
non_empty_env("HYPERHIVE_HIVE_NAME")
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ pub struct ClearedSession {
|
|||
|
||||
/// Delete the credential files (see [`CRED_FILE_NAMES`]) from `dir`, forcing a
|
||||
/// re-login on the next turn, while preserving the session-history files
|
||||
/// alongside them so `claude --continue` keeps working after a fresh login.
|
||||
/// alongside them so the session resumed by title survives a fresh login.
|
||||
/// Idempotent: an already-absent file is skipped, not reported. This is the
|
||||
/// write-side counterpart to [`has_session`]; `/logout` (`web_ui::auth`) drives
|
||||
/// it.
|
||||
|
|
@ -165,13 +165,12 @@ pub async fn wait_for_login(
|
|||
claude_dir = %claude_dir.display(),
|
||||
"no claude session — staying in partial-run mode (web UI only)"
|
||||
);
|
||||
// Announce `needs_login_idle` to the bus so the sentinel file
|
||||
// (`{state_dir}/hyperhive-needs-login`) gets written on every entry
|
||||
// path — cold-boot, 401-mid-turn, and `/api/logout`. The host's
|
||||
// `auth_failed_sentinel` reads that file to surface `needs_login`
|
||||
// on the dashboard. Idempotent — `emit_status` is a `write` on a
|
||||
// small empty file, so re-entering this function after a transient
|
||||
// operator action is a no-op for the on-disk state.
|
||||
// Announce `needs_login_idle` to the bus so
|
||||
// `hyperhive-harness.json` records it on every entry path —
|
||||
// cold-boot, 401-mid-turn, and `/api/logout`. The host reads that
|
||||
// file to surface `needs_login` on the dashboard. Idempotent —
|
||||
// re-entering this function after a transient operator action
|
||||
// rewrites the same on-disk state.
|
||||
bus.emit_status("needs_login_idle");
|
||||
// Baseline for the unreadable-mtime fallback in `has_fresh_credentials`
|
||||
// — captured once, here, not recomputed per poll. See that fn's doc
|
||||
|
|
|
|||
|
|
@ -614,9 +614,8 @@ async fn serve_main<S: Surface>(socket: &Path, poll_ms: u64) -> Result<()> {
|
|||
)
|
||||
.await;
|
||||
} else {
|
||||
// Clear any stale `hyperhive-needs-login` sentinel left over
|
||||
// from a prior boot — `online` status writes the sentinel
|
||||
// cleanup in `Bus::emit_status`.
|
||||
// Record `online` in `hyperhive-harness.json`, which clears a
|
||||
// `needs_login` left over from a prior boot (`Bus::emit_status`).
|
||||
bus.emit_status("online");
|
||||
}
|
||||
serve_loop::<S>(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
//! only the harness-only concepts: wall-clock turn duration as *this harness*
|
||||
//! measures it (not Claude's own per-request latency), what woke the turn,
|
||||
//! the harness's own outcome classification, the loose-ends backlog at turn
|
||||
//! end, session boundaries (fresh vs. `--continue`'d), context-window fill,
|
||||
//! end, session boundaries (fresh vs. resumed by title), context-window fill,
|
||||
//! and the cost an ACP agent reports — claude's runtime reports none.
|
||||
//!
|
||||
//! Synchronous instruments, not observable ones: unlike hive-c0re's
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//! Pure helpers factored out of the harness serve loop
|
||||
//! (`bin/hive-agent.rs`).
|
||||
//! (`serve_main` in `main.rs`).
|
||||
//! Only functions with no wire-type dependency live here;
|
||||
//! request/response-flavored helpers (`requeue_inflight`, `ack_turn`, etc.)
|
||||
//! stay in the binary because they touch the request enum variants directly.
|
||||
|
|
|
|||
|
|
@ -151,9 +151,9 @@ pub struct Snapshot {
|
|||
pub first_turn_ctx: Option<u64>,
|
||||
/// Number of fresh claude sessions (i.e. rows in the `sessions` table)
|
||||
/// that started within the window. A new session is minted whenever the
|
||||
/// harness runs a turn without `--continue` (manual `/new-session`,
|
||||
/// auto-compaction fallback, or first-ever turn). `None` when the
|
||||
/// `sessions` table doesn't exist yet (older db) — same inert-until-data
|
||||
/// harness creates a session instead of resuming it by title (manual
|
||||
/// `/new-session`, auto-compaction fallback, or first-ever turn). `None` when
|
||||
/// the `sessions` table doesn't exist yet (older db) — same inert-until-data
|
||||
/// pattern as `first_turn_ctx`.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub session_count: Option<u64>,
|
||||
|
|
@ -380,7 +380,7 @@ fn snapshot(path: &Path, window: Window) -> Result<Snapshot> {
|
|||
|
||||
/// First-turn input tokens of the most recent fresh claude session that
|
||||
/// started in `[from, now]`. Tracks system-prompt + AGENTS.md sprawl:
|
||||
/// the first turn of a fresh session (`--continue` suppressed) pays the
|
||||
/// the first turn of a fresh session (created, not resumed by title) pays the
|
||||
/// full static prefix as uncached input, so watching this over time
|
||||
/// surfaces creep. Uses the agreed per-session derive — the first turn
|
||||
/// (`ORDER BY started_at LIMIT 1`) of the latest session row.
|
||||
|
|
@ -404,8 +404,9 @@ fn read_first_turn_ctx(conn: &Connection, from: i64) -> rusqlite::Result<u64> {
|
|||
/// Count of fresh claude sessions that started within `[from, now]`.
|
||||
///
|
||||
/// Each row in the `sessions` table represents one fresh `claude --print`
|
||||
/// invocation (without `--continue`): a manual `/new-session`, an
|
||||
/// auto-compaction fallback, or the very first turn after spawning.
|
||||
/// invocation that created a session rather than resuming by title: a manual
|
||||
/// `/new-session`, an auto-compaction fallback, or the very first turn after
|
||||
/// spawning.
|
||||
///
|
||||
/// Returns `Err` when the `sessions` table doesn't exist (older db) so the
|
||||
/// caller can map to `None` — same inert-until-data pattern as
|
||||
|
|
|
|||
|
|
@ -717,8 +717,9 @@ fn claude_config(bus: &Bus, files: &TurnFiles) -> Config {
|
|||
|
||||
/// Map a `hive_claude::Error` onto the harness's `TurnOutcome`. The recognized
|
||||
/// sentinels become their matching outcomes; a residual `SessionNotFound`
|
||||
/// (create path itself missed — shouldn't happen) settles as `Ok`; genuine
|
||||
/// failures become `Failed` (converting the typed lib error into `anyhow`).
|
||||
/// (create path itself missed — shouldn't happen) stays `Err(SessionNotFound)`
|
||||
/// for `handle_turn_error_recovery` to requeue; genuine failures become
|
||||
/// `Failed` (converting the typed lib error into `anyhow`).
|
||||
fn error_to_turn(err: hive_claude::Error) -> TurnOutcome {
|
||||
use hive_claude::Error;
|
||||
match err {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@
|
|||
//! `open_reminders_count` (planned: a loose-ends-over-time trend), and
|
||||
//! `note` (failure detail for `result_kind = "failed"`).
|
||||
//!
|
||||
//! Lives next to `hyperhive-events.sqlite` in the agent's state dir
|
||||
//! so the host-side state vacuum sweep can reach both. Schema is
|
||||
//! Lives in the harness dir (`HYPERHIVE_HARNESS_DIR`) as
|
||||
//! `hyperhive-turn-stats.sqlite`; the dashboard reads it read-only and no
|
||||
//! vacuum sweeps it. Schema is
|
||||
//! intentionally append-only — every column has a default so future
|
||||
//! additions don't break old readers; new columns land via
|
||||
//! `ALTER TABLE ... ADD COLUMN ... DEFAULT ...` in the migration
|
||||
|
|
@ -62,7 +63,8 @@ CREATE INDEX IF NOT EXISTS idx_turn_stats_started
|
|||
-- (the old table has no session_id column), so indexing session_id in this
|
||||
-- batch would fail with `no such column` and abort the whole SCHEMA apply —
|
||||
-- which disables the stats sink. The column is added by MIGRATIONS first.
|
||||
-- One row per fresh claude session (minted when --continue is suppressed).
|
||||
-- One row per fresh claude session (minted when a session is created rather
|
||||
-- than resumed by title).
|
||||
-- turn_stats.session_id FKs here so per-session stats (first-turn tokens,
|
||||
-- per-session totals, turn count, duration) are one GROUP BY away.
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
|
|
|
|||
|
|
@ -169,11 +169,12 @@ pub async fn serve(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Bind a `UnixListener` at `path` and drop a `.bound` marker next
|
||||
/// to it so c0re's gateway-map writer knows the socket is live.
|
||||
/// Bind a `UnixListener` at `path` and drop a `hyperhive-socket-bound` marker
|
||||
/// next to it so c0re's gateway-map writer knows the socket is live.
|
||||
/// Best-effort unlinks any stale socket left from a crashed previous
|
||||
/// harness (clean exit removes it, but `bind(2)` refuses to overwrite
|
||||
/// an existing file) and `mkdir -p`s the parent for first-boot. Mode
|
||||
/// harness (nothing removes the socket or marker on exit, and `bind(2)`
|
||||
/// refuses to overwrite an existing file) and `mkdir -p`s the parent for
|
||||
/// first-boot. Mode
|
||||
/// `0o666` — world-accessible so the gateway's nginx process can
|
||||
/// `connect(2)` without sharing a group with the agent user. What bounds
|
||||
/// that is the per-agent subdir (`/run/hive-agent/<name>/`): the socket
|
||||
|
|
|
|||
Loading…
Reference in a new issue