From c59a0de01e5d22fc17b6701c0999d426aa8b1dec Mon Sep 17 00:00:00 2001 From: damocles Date: Fri, 5 Jun 2026 21:41:50 +0200 Subject: [PATCH] fix: hivectl choom enters as the agent user from the state dir so claude gets creds + session --- docs/tools/hivectl-cli.md | 4 ++- docs/tools/hivectl.md | 14 +++++++-- hive-c0re/src/bin/hivectl.rs | 61 ++++++++++++++++++++++++++++-------- 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/docs/tools/hivectl-cli.md b/docs/tools/hivectl-cli.md index 645eb377..972abbe4 100644 --- a/docs/tools/hivectl-cli.md +++ b/docs/tools/hivectl-cli.md @@ -279,7 +279,9 @@ Stop and restart ALL managed agent containers in sequence. Iterates the live con Open an interactive Claude session inside an agent container. -Replaces the current process with `machinectl shell h-` running `claude --continue` — drops the operator straight into the agent's live Claude session with its full loaded context and persona. Requires root (same as all machinectl shell operations) and the container must be running. +Replaces the current process with `machinectl shell @h-` running `claude --continue` from the agent's state dir — drops the operator straight into the agent's live Claude session with its full loaded context and persona. Requires root (same as all machinectl shell operations) and the container must be running. + +The session is entered **as the agent user** (not root): claude reads its OAuth credentials + settings from the agent's `/home//.claude`, and the working directory is set to the agent's state dir (`/agents//state`) so `--continue` resumes the same per-project session the harness runs. Entering as root (the `machinectl shell` default) is what loses both. Pass `--fresh` to start a new Claude session instead of continuing the most recent one. diff --git a/docs/tools/hivectl.md b/docs/tools/hivectl.md index 11bf3598..ddc974ba 100644 --- a/docs/tools/hivectl.md +++ b/docs/tools/hivectl.md @@ -111,9 +111,9 @@ reported at the end rather than aborting mid-run. ## Choom Drop into an interactive Claude session inside an agent container. -Replaces the current process with `machinectl shell h-` running -`claude --continue`. Requires root (same as all `machinectl shell` -operations). +Replaces the current process with `machinectl shell @h-` +running `claude --continue` from the agent's state dir. Requires root +(same as all `machinectl shell` operations). ```bash hivectl choom iris # join iris's ongoing Claude session (--continue) @@ -123,3 +123,11 @@ hivectl choom iris --fresh # start a new Claude session instead Without `--fresh`, `--continue` is passed so the operator joins the agent's live context window. With `--fresh` a clean session starts. The container must be running. + +The session is entered **as the agent user**, not root: `machinectl +shell` defaults to root in the container, which would make claude read +`/root/.claude` (empty) instead of the agent's `/home//.claude` +where its OAuth credentials + settings live. choom prefixes the +machine with `@` (the meta-flake sets the agent's unix user name +to its label) and `cd`s into `/agents//state` first so +`--continue` resolves the same per-project session the harness runs. diff --git a/hive-c0re/src/bin/hivectl.rs b/hive-c0re/src/bin/hivectl.rs index d356b931..fd36f8a8 100644 --- a/hive-c0re/src/bin/hivectl.rs +++ b/hive-c0re/src/bin/hivectl.rs @@ -73,11 +73,19 @@ enum Cmd { }, /// Open an interactive Claude session inside an agent container. /// - /// Replaces the current process with `machinectl shell h-` - /// running `claude --continue` — drops the operator straight into - /// the agent's live Claude session with its full loaded context and - /// persona. Requires root (same as all machinectl shell operations) - /// and the container must be running. + /// Replaces the current process with `machinectl shell + /// @h-` running `claude --continue` from the agent's + /// state dir — drops the operator straight into the agent's live + /// Claude session with its full loaded context and persona. Requires + /// root (same as all machinectl shell operations) and the container + /// must be running. + /// + /// The session is entered **as the agent user** (not root): claude + /// reads its OAuth credentials + settings from the agent's + /// `/home//.claude`, and the working directory is set to the + /// agent's state dir (`/agents//state`) so `--continue` + /// resumes the same per-project session the harness runs. Entering + /// as root (the `machinectl shell` default) is what loses both. /// /// Pass `--fresh` to start a new Claude session instead of continuing /// the most recent one. @@ -357,10 +365,24 @@ fn is_agent(name: &str) -> bool { /// Drop into an interactive Claude session in the agent container. /// -/// Replaces the current process (exec) with `machinectl shell h- -/// /run/current-system/sw/bin/claude [--continue]`. The `--continue` -/// flag is passed by default so the operator joins the agent's active -/// session; omit it via `--fresh` to start a blank session instead. +/// Replaces the current process (exec) with `machinectl shell +/// @h- /bin/sh -lc 'cd /agents//state && exec claude +/// [--continue]'`. Two things matter here, both of which the naive +/// `machinectl shell h- claude` got wrong (issue: choom missing +/// creds + settings): +/// +/// 1. **Run as the agent user.** `machinectl shell` defaults to root in +/// the container, so claude would read `/root/.claude` (empty) +/// instead of the agent's `/home//.claude` where the OAuth +/// credentials + settings live. Prefixing the machine with +/// `@` enters the session as the agent user (the meta-flake +/// sets `hyperhive.user.name` to the agent label, so the unix user +/// name matches the agent name). +/// 2. **Start in the agent's state dir.** `claude --continue` resumes +/// the most recent session *for the current project directory*. The +/// harness runs claude from `/agents//state`, so choom has to +/// `cd` there or `--continue` finds no session (it would look under +/// the user's home instead). /// /// `machinectl shell` inherits the caller's PTY, so the session is /// fully interactive. Requires root and a running container. @@ -369,12 +391,25 @@ fn choom(name: &str, fresh: bool) -> Result<()> { bail!("no such agent: '{name}' (no state dir under /var/lib/hyperhive/agents/)"); } let container = hive_c0re::lifecycle::container_name(name); + // The agent's unix user name matches its agent name (meta-flake + // sets `hyperhive.user.name =