Compare commits

..
3 changed files with 29 additions and 39 deletions

View file

@ -379,7 +379,7 @@ Generate the federation peer-config block for THIS hive — the nix a peer opera
Open an interactive Claude session inside an agent container. Open an interactive Claude session inside an agent container.
Runs claude as the agent user from its state dir with the harness's settings / MCP / system prompt. Bare `choom <name>` is a fresh session; `--resume <session-id>` rejoins a prior one. Never collides with the harness's live session. Requires root + a running container. See `docs/tools/hivectl.md` (Choom) for details. Runs claude as the agent user from its state dir with the harness's settings / MCP / system prompt. Bare `choom <name>` is a fresh session; `--continue <id|name>` rejoins a prior one. Never collides with the harness's live session. Requires root + a running container. See `docs/tools/hivectl.md` (Choom) for details.
**Usage:** `hivectl choom [OPTIONS] <NAME>` **Usage:** `hivectl choom [OPTIONS] <NAME>`
@ -389,7 +389,7 @@ Runs claude as the agent user from its state dir with the harness's settings / M
###### **Options:** ###### **Options:**
* `--resume <SESSION>` — Resume a prior claude session by its session id, passed through as `claude --resume <value>` (claude's `--continue` takes no value — it resumes the cwd's latest session, which is the harness's, so choom never uses it; this flag matches the claude flag it maps to). Omit for a fresh blank session. A value is required when the flag is given * `--continue <SESSION>` — Resume a prior claude session by id or display name, passed through as `claude --continue <value>`. Omit for a fresh blank session. A value is required when the flag is given

View file

@ -127,21 +127,18 @@ running claude from the agent's state dir. Requires root (same as all
`machinectl shell` operations). `machinectl shell` operations).
```bash ```bash
hivectl choom iris # fresh blank Claude session in iris's container hivectl choom iris # fresh blank Claude session in iris's container
hivectl choom iris --resume <session-id> # rejoin a prior session by id hivectl choom iris --continue <id|name> # rejoin a prior session by id or display name
``` ```
Bare `choom` starts a fresh blank session. `--resume <value>` passes Bare `choom` starts a fresh blank session. `--continue <value>` is
through as `claude --resume <value>` to rejoin a prior session by its passed straight through as `claude --continue <value>` to rejoin a
session id — the flag name deliberately matches the claude flag it maps prior session — claude resolves whether the value is a session id or a
to. (choom never uses claude's `--continue`: that's a bare flag that display name (you can `/rename` a session in-session for an easy
takes no argument and resumes the cwd's *latest* session, i.e. the handle). A value is required when the flag is given. Either way choom
harness's; a value after it would be consumed as the first prompt, never collides with the harness's live session in the same project dir:
silently poking the live harness session.) A value is required when the the harness pins its own id via `--resume`, so a blank choom session is
flag is given. Either way choom never collides with the harness's live invisible to it. The container must be running.
session in the same project dir: the harness pins its own id via
`--resume`, so a blank choom session is invisible to it. The container
must be running.
choom reproduces the harness's own claude invocation so the operator choom reproduces the harness's own claude invocation so the operator
lands in a faithful copy of the agent's environment: lands in a faithful copy of the agent's environment:

View file

@ -112,20 +112,17 @@ enum Cmd {
/// ///
/// Runs claude as the agent user from its state dir with the harness's /// Runs claude as the agent user from its state dir with the harness's
/// settings / MCP / system prompt. Bare `choom <name>` is a fresh /// settings / MCP / system prompt. Bare `choom <name>` is a fresh
/// session; `--resume <session-id>` rejoins a prior one. Never /// session; `--continue <id|name>` rejoins a prior one. Never collides
/// collides with the harness's live session. Requires root + a running /// with the harness's live session. Requires root + a running
/// container. See `docs/tools/hivectl.md` (Choom) for details. /// container. See `docs/tools/hivectl.md` (Choom) for details.
Choom { Choom {
/// Agent name (e.g. `damocles`, `iris`). /// Agent name (e.g. `damocles`, `iris`).
name: String, name: String,
/// Resume a prior claude session by its session id, passed /// Resume a prior claude session by id or display name, passed
/// through as `claude --resume <value>` (claude's `--continue` /// through as `claude --continue <value>`. Omit for a fresh
/// takes no value — it resumes the cwd's latest session, which /// blank session. A value is required when the flag is given.
/// is the harness's, so choom never uses it; this flag matches #[arg(long = "continue", value_name = "SESSION")]
/// the claude flag it maps to). Omit for a fresh blank session. continue_session: Option<String>,
/// A value is required when the flag is given.
#[arg(long = "resume", value_name = "SESSION")]
resume_session: Option<String>,
}, },
/// Stop containers hive-wide in one operator action. Bare `hivectl /// Stop containers hive-wide in one operator action. Bare `hivectl
/// stop` stops **everything** — all sub-agents plus the ci, forge, /// stop` stops **everything** — all sub-agents plus the ci, forge,
@ -634,8 +631,8 @@ async fn main() -> Result<()> {
}, },
Cmd::Choom { Cmd::Choom {
name, name,
resume_session, continue_session,
} => choom(&name, resume_session.as_deref()), } => choom(&name, continue_session.as_deref()),
Cmd::Quota { cmd } => match cmd { Cmd::Quota { cmd } => match cmd {
QuotaCmd::Enable => quota_enable().await, QuotaCmd::Enable => quota_enable().await,
QuotaCmd::Show { name } => quota_show(name.as_deref()).await, QuotaCmd::Show { name } => quota_show(name.as_deref()).await,
@ -1034,7 +1031,7 @@ fn agent_exists(name: &str) -> Result<bool> {
/// live harness session). See `docs/tools/hivectl.md` (Choom) for the /// live harness session). See `docs/tools/hivectl.md` (Choom) for the
/// full rationale. Inherits the caller's PTY; requires root + a running /// full rationale. Inherits the caller's PTY; requires root + a running
/// container. /// container.
fn choom(name: &str, resume_session: Option<&str>) -> Result<()> { fn choom(name: &str, continue_session: Option<&str>) -> Result<()> {
if !agent_exists(name)? { if !agent_exists(name)? {
bail!("no such agent: '{name}' (no state dir under /var/lib/hyperhive/agents/)"); bail!("no such agent: '{name}' (no state dir under /var/lib/hyperhive/agents/)");
} }
@ -1047,20 +1044,16 @@ fn choom(name: &str, resume_session: Option<&str>) -> Result<()> {
let state_dir = format!("/agents/{name}/state"); let state_dir = format!("/agents/{name}/state");
// Per-turn config the harness writes; matches `paths::config_dir()`. // Per-turn config the harness writes; matches `paths::config_dir()`.
let cfg = "/run/hive-config"; let cfg = "/run/hive-config";
// `--resume <value>` passes through as `claude --resume <value>` — // `--continue <value>` passes straight through to claude. The value is
// the rejoin-by-id surface. (claude's `--continue` is a bare flag // single-quoted into the shell script, so reject an embedded single
// that resumes the cwd's latest session — the harness's — and would // quote (the only char that breaks single-quoting) to rule out
// consume a trailing value as the first PROMPT; choom never uses // injection — session ids / display names never contain one.
// it.) The value is single-quoted into the shell script, so reject let session_arg = match continue_session {
// an embedded single quote (the only char that breaks
// single-quoting) to rule out injection — session ids never
// contain one.
let session_arg = match resume_session {
Some(val) => { Some(val) => {
if val.contains('\'') { if val.contains('\'') {
bail!("invalid --resume value '{val}': must not contain a single quote"); bail!("invalid --continue value '{val}': must not contain a single quote");
} }
format!("set -- --resume '{val}';") format!("set -- --continue '{val}';")
} }
None => "set --;".to_string(), None => "set --;".to_string(),
}; };