Compare commits

...
3 changed files with 39 additions and 29 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; `--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. 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.
**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:**
* `--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 * `--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

View file

@ -127,18 +127,21 @@ 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 --continue <id|name> # rejoin a prior session by id or display name hivectl choom iris --resume <session-id> # rejoin a prior session by id
``` ```
Bare `choom` starts a fresh blank session. `--continue <value>` is Bare `choom` starts a fresh blank session. `--resume <value>` passes
passed straight through as `claude --continue <value>` to rejoin a through as `claude --resume <value>` to rejoin a prior session by its
prior session — claude resolves whether the value is a session id or a session id — the flag name deliberately matches the claude flag it maps
display name (you can `/rename` a session in-session for an easy to. (choom never uses claude's `--continue`: that's a bare flag that
handle). A value is required when the flag is given. Either way choom takes no argument and resumes the cwd's *latest* session, i.e. the
never collides with the harness's live session in the same project dir: harness's; a value after it would be consumed as the first prompt,
the harness pins its own id via `--resume`, so a blank choom session is silently poking the live harness session.) A value is required when the
invisible to it. The container must be running. flag is given. Either way choom never collides with the harness's live
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,17 +112,20 @@ 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; `--continue <id|name>` rejoins a prior one. Never collides /// session; `--resume <session-id>` rejoins a prior one. Never
/// with the harness's live session. Requires root + a running /// collides 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 id or display name, passed /// Resume a prior claude session by its session id, passed
/// through as `claude --continue <value>`. Omit for a fresh /// through as `claude --resume <value>` (claude's `--continue`
/// blank session. A value is required when the flag is given. /// takes no value — it resumes the cwd's latest session, which
#[arg(long = "continue", value_name = "SESSION")] /// is the harness's, so choom never uses it; this flag matches
continue_session: Option<String>, /// the claude flag it maps to). Omit for a fresh blank session.
/// 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,
@ -631,8 +634,8 @@ async fn main() -> Result<()> {
}, },
Cmd::Choom { Cmd::Choom {
name, name,
continue_session, resume_session,
} => choom(&name, continue_session.as_deref()), } => choom(&name, resume_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,
@ -1031,7 +1034,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, continue_session: Option<&str>) -> Result<()> { fn choom(name: &str, resume_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/)");
} }
@ -1044,16 +1047,20 @@ fn choom(name: &str, continue_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";
// `--continue <value>` passes straight through to claude. The value is // `--resume <value>` passes through as `claude --resume <value>` —
// single-quoted into the shell script, so reject an embedded single // the rejoin-by-id surface. (claude's `--continue` is a bare flag
// quote (the only char that breaks single-quoting) to rule out // that resumes the cwd's latest session — the harness's — and would
// injection — session ids / display names never contain one. // consume a trailing value as the first PROMPT; choom never uses
let session_arg = match continue_session { // it.) The value is single-quoted into the shell script, so reject
// 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 --continue value '{val}': must not contain a single quote"); bail!("invalid --resume value '{val}': must not contain a single quote");
} }
format!("set -- --continue '{val}';") format!("set -- --resume '{val}';")
} }
None => "set --;".to_string(), None => "set --;".to_string(),
}; };