Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1502392e5 | ||
|
|
2248881be8 | ||
|
|
0125fcfae8 | ||
|
|
c5847e25ea | ||
|
|
09b83c9d1e | ||
|
|
ce959d7700 |
3 changed files with 61 additions and 93 deletions
|
|
@ -379,11 +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.
|
||||||
|
|
||||||
Replaces the current process with `machinectl shell <name>@h-<name>` 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.
|
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.
|
||||||
|
|
||||||
To match the harness exactly, choom enters **as the agent user** (not root) so claude reads the OAuth credentials from the agent's `/home/<name>/.claude`; runs from the agent's state dir (`/agents/<name>/state`) so `--continue` resumes the right per-project session and `CLAUDE.md` loads; and passes the same `--settings` / `--mcp-config` / `--system-prompt-file` the harness writes to `/run/hive-config/` (settings, MCP tools, role prompt). Entering as root (the `machinectl shell` default) loses all of it.
|
|
||||||
|
|
||||||
Pass `--fresh` to start a new Claude session instead of continuing the most recent one.
|
|
||||||
|
|
||||||
**Usage:** `hivectl choom [OPTIONS] <NAME>`
|
**Usage:** `hivectl choom [OPTIONS] <NAME>`
|
||||||
|
|
||||||
|
|
@ -393,7 +389,7 @@ Pass `--fresh` to start a new Claude session instead of continuing the most rece
|
||||||
|
|
||||||
###### **Options:**
|
###### **Options:**
|
||||||
|
|
||||||
* `--fresh` — Start a fresh Claude session instead of continuing the most recent one. Without this flag `--continue` is passed to Claude so the operator joins the agent's ongoing session context
|
* `--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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,17 +123,22 @@ reported at the end rather than aborting mid-run.
|
||||||
|
|
||||||
Drop into an interactive Claude session inside an agent container.
|
Drop into an interactive Claude session inside an agent container.
|
||||||
Replaces the current process with `machinectl shell <name>@h-<name>`
|
Replaces the current process with `machinectl shell <name>@h-<name>`
|
||||||
running `claude --continue` from the agent's state dir. Requires root
|
running claude from the agent's state dir. Requires root (same as all
|
||||||
(same as all `machinectl shell` operations).
|
`machinectl shell` operations).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
hivectl choom iris # join iris's ongoing Claude session (--continue)
|
hivectl choom iris # fresh blank Claude session in iris's container
|
||||||
hivectl choom iris --fresh # start a new Claude session instead
|
hivectl choom iris --continue <id|name> # rejoin a prior session by id or display name
|
||||||
```
|
```
|
||||||
|
|
||||||
Without `--fresh`, `--continue` is passed so the operator joins the
|
Bare `choom` starts a fresh blank session. `--continue <value>` is
|
||||||
agent's live context window. With `--fresh` a clean session starts.
|
passed straight through as `claude --continue <value>` to rejoin a
|
||||||
The container must be running.
|
prior session — claude resolves whether the value is a session id or a
|
||||||
|
display name (you can `/rename` a session in-session for an easy
|
||||||
|
handle). A value is required when the 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:
|
||||||
|
|
@ -143,14 +148,13 @@ lands in a faithful copy of the agent's environment:
|
||||||
agent's `/home/<name>/.claude` where its OAuth credentials live. choom
|
agent's `/home/<name>/.claude` where its OAuth credentials live. choom
|
||||||
prefixes the machine with `<name>@` (the meta-flake sets the agent's
|
prefixes the machine with `<name>@` (the meta-flake sets the agent's
|
||||||
unix user name to its label).
|
unix user name to its label).
|
||||||
- **from `/agents/<name>/state`**: `--continue` resolves the session for
|
- **from `/agents/<name>/state`**: the session and `CLAUDE.md` (the
|
||||||
that project dir and `CLAUDE.md` (the persona) loads from the cwd.
|
persona) resolve against the right project dir.
|
||||||
- **with the harness flags**: `--settings`, `--mcp-config`, and
|
- **with the harness flags**: `--settings`, `--mcp-config`, and
|
||||||
`--system-prompt-file` from `/run/hive-config/` — the same files the
|
`--system-prompt-file` from `/run/hive-config/` — the same files the
|
||||||
harness writes each turn — so the operator gets the agent's settings,
|
harness writes each turn — so the operator gets the agent's settings,
|
||||||
the hyperhive/matrix MCP tools (which `--continue` needs to replay a
|
the hyperhive/matrix MCP tools, and the role prompt. Each flag is
|
||||||
tool-using history), and the role prompt. Each flag is included only
|
included only when its file exists.
|
||||||
when its file exists.
|
|
||||||
|
|
||||||
## Open
|
## Open
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,32 +110,19 @@ enum Cmd {
|
||||||
},
|
},
|
||||||
/// Open an interactive Claude session inside an agent container.
|
/// Open an interactive Claude session inside an agent container.
|
||||||
///
|
///
|
||||||
/// Replaces the current process with `machinectl shell
|
/// Runs claude as the agent user from its state dir with the harness's
|
||||||
/// <name>@h-<name>` running `claude --continue` from the agent's
|
/// settings / MCP / system prompt. Bare `choom <name>` is a fresh
|
||||||
/// state dir — drops the operator straight into the agent's live
|
/// session; `--continue <id|name>` rejoins a prior one. Never collides
|
||||||
/// Claude session with its full loaded context and persona. Requires
|
/// with the harness's live session. Requires root + a running
|
||||||
/// root (same as all machinectl shell operations) and the container
|
/// container. See `docs/tools/hivectl.md` (Choom) for details.
|
||||||
/// must be running.
|
|
||||||
///
|
|
||||||
/// To match the harness exactly, choom enters **as the agent user**
|
|
||||||
/// (not root) so claude reads the OAuth credentials from the agent's
|
|
||||||
/// `/home/<name>/.claude`; runs from the agent's state dir
|
|
||||||
/// (`/agents/<name>/state`) so `--continue` resumes the right
|
|
||||||
/// per-project session and `CLAUDE.md` loads; and passes the same
|
|
||||||
/// `--settings` / `--mcp-config` / `--system-prompt-file` the harness
|
|
||||||
/// writes to `/run/hive-config/` (settings, MCP tools, role prompt).
|
|
||||||
/// Entering as root (the `machinectl shell` default) loses all of it.
|
|
||||||
///
|
|
||||||
/// Pass `--fresh` to start a new Claude session instead of continuing
|
|
||||||
/// the most recent one.
|
|
||||||
Choom {
|
Choom {
|
||||||
/// Agent name (e.g. `damocles`, `iris`).
|
/// Agent name (e.g. `damocles`, `iris`).
|
||||||
name: String,
|
name: String,
|
||||||
/// Start a fresh Claude session instead of continuing the most
|
/// Resume a prior claude session by id or display name, passed
|
||||||
/// recent one. Without this flag `--continue` is passed to Claude
|
/// through as `claude --continue <value>`. Omit for a fresh
|
||||||
/// so the operator joins the agent's ongoing session context.
|
/// blank session. A value is required when the flag is given.
|
||||||
#[arg(long)]
|
#[arg(long = "continue", value_name = "SESSION")]
|
||||||
fresh: bool,
|
continue_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,
|
||||||
|
|
@ -642,7 +629,10 @@ async fn main() -> Result<()> {
|
||||||
Cmd::Subvol { cmd } => match cmd {
|
Cmd::Subvol { cmd } => match cmd {
|
||||||
SubvolCmd::Upgrade { name, yes } => subvol_upgrade(&socket, &name, yes).await,
|
SubvolCmd::Upgrade { name, yes } => subvol_upgrade(&socket, &name, yes).await,
|
||||||
},
|
},
|
||||||
Cmd::Choom { name, fresh } => choom(&name, fresh),
|
Cmd::Choom {
|
||||||
|
name,
|
||||||
|
continue_session,
|
||||||
|
} => 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,
|
||||||
|
|
@ -1035,69 +1025,47 @@ fn agent_exists(name: &str) -> Result<bool> {
|
||||||
|
|
||||||
/// Drop into an interactive Claude session in the agent container.
|
/// Drop into an interactive Claude session in the agent container.
|
||||||
///
|
///
|
||||||
/// Replaces the current process (exec) with `machinectl shell
|
/// Execs `machinectl shell <name>@h-<name>` running claude as the agent
|
||||||
/// <name>@h-<name> /bin/sh -lc '<script>'`, where the script `cd`s into
|
/// user from its state dir, reproducing the harness's per-turn claude
|
||||||
/// the agent's state dir and execs claude with the *same* flags the
|
/// invocation (flags, session selection, why it never collides with the
|
||||||
/// harness uses. Three things matter here, all of which the naive
|
/// live harness session). See `docs/tools/hivectl.md` (Choom) for the
|
||||||
/// `machinectl shell h-<name> claude` got wrong (issue: choom missing
|
/// full rationale. Inherits the caller's PTY; requires root + a running
|
||||||
/// creds + settings):
|
/// container.
|
||||||
///
|
fn choom(name: &str, continue_session: Option<&str>) -> Result<()> {
|
||||||
/// 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/<name>/.claude` where the OAuth
|
|
||||||
/// credentials live. Prefixing the machine with `<name>@` 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*, and
|
|
||||||
/// project memory (`CLAUDE.md`) is read from the cwd. The harness
|
|
||||||
/// runs claude from `/agents/<name>/state`, so choom has to `cd`
|
|
||||||
/// there or `--continue` finds no session and the persona doesn't
|
|
||||||
/// load.
|
|
||||||
/// 3. **Pass the harness's claude flags.** The harness drops
|
|
||||||
/// `claude-{settings.json,mcp-config.json,system-prompt.md}` into
|
|
||||||
/// `/run/hive-config/` (`hive-ag3nt::paths::config_dir()`) and runs
|
|
||||||
/// claude with `--settings` / `--mcp-config` / `--system-prompt-file`
|
|
||||||
/// pointing at them. A bare `claude --continue` skips all three, so
|
|
||||||
/// the operator gets default settings, no hyperhive/matrix MCP tools
|
|
||||||
/// (which `--continue` needs to replay a tool-using history), and no
|
|
||||||
/// role prompt. choom now mirrors those flags. Each is added only if
|
|
||||||
/// the file exists, so a mid-restart container degrades to a bare
|
|
||||||
/// session instead of erroring.
|
|
||||||
///
|
|
||||||
/// `machinectl shell` inherits the caller's PTY, so the session is
|
|
||||||
/// fully interactive. Requires root and a running container.
|
|
||||||
fn choom(name: &str, fresh: bool) -> 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/)");
|
||||||
}
|
}
|
||||||
let container = hive_c0re::lifecycle::container_name(name);
|
let container = hive_c0re::lifecycle::container_name(name);
|
||||||
// The agent's unix user name matches its agent name (meta-flake
|
// Enter as the agent's unix user (== agent name) so claude reads the
|
||||||
// sets `hyperhive.user.name = <label>`); enter the session as that
|
// right `$HOME/.claude`.
|
||||||
// user so claude sees the right `$HOME/.claude`.
|
|
||||||
let target = format!("{name}@{container}");
|
let target = format!("{name}@{container}");
|
||||||
let claude = "/run/current-system/sw/bin/claude";
|
let claude = "/run/current-system/sw/bin/claude";
|
||||||
// In-container state dir (host `/var/lib/hyperhive/agents/<name>/state`
|
// Bind-mounted state dir; matches `hive-ag3nt::paths::state_dir()`.
|
||||||
// is bind-mounted here); matches `hive-ag3nt::paths::state_dir()`.
|
|
||||||
let state_dir = format!("/agents/{name}/state");
|
let state_dir = format!("/agents/{name}/state");
|
||||||
// Per-turn config dir the harness writes (RuntimeDirectory
|
// Per-turn config the harness writes; matches `paths::config_dir()`.
|
||||||
// `hive-config`); matches `hive-ag3nt::paths::config_dir()`. These
|
|
||||||
// are the exact files the harness passes to claude each turn.
|
|
||||||
let cfg = "/run/hive-config";
|
let cfg = "/run/hive-config";
|
||||||
let continue_flag = if fresh { "" } else { " --continue" };
|
// `--continue <value>` passes straight through to claude. The value is
|
||||||
// Build the claude argv as the harness does, but only include each
|
// single-quoted into the shell script, so reject an embedded single
|
||||||
// flag when its file is present so a half-up container falls back to
|
// quote (the only char that breaks single-quoting) to rule out
|
||||||
// a bare session rather than a hard claude error. `name` is
|
// injection — session ids / display names never contain one.
|
||||||
// constrained to `[a-z0-9._-]` so there's nothing to quote.
|
let session_arg = match continue_session {
|
||||||
// `cd` so `--continue` + CLAUDE.md resolve against the agent's
|
Some(val) => {
|
||||||
// project; `exec` hands the PTY to claude directly.
|
if val.contains('\'') {
|
||||||
|
bail!("invalid --continue value '{val}': must not contain a single quote");
|
||||||
|
}
|
||||||
|
format!("set -- --continue '{val}';")
|
||||||
|
}
|
||||||
|
None => "set --;".to_string(),
|
||||||
|
};
|
||||||
|
// Build claude's argv as the harness does, each flag included only
|
||||||
|
// when its file is present so a half-up container degrades to a bare
|
||||||
|
// session. `name` is `[a-z0-9._-]` so there's nothing to quote.
|
||||||
let inner = format!(
|
let inner = format!(
|
||||||
"cd {state_dir} || exit 1; set --; \
|
"cd {state_dir} || exit 1; {session_arg} \
|
||||||
[ -f {cfg}/claude-settings.json ] && set -- \"$@\" --settings {cfg}/claude-settings.json; \
|
[ -f {cfg}/claude-settings.json ] && set -- \"$@\" --settings {cfg}/claude-settings.json; \
|
||||||
[ -f {cfg}/claude-mcp-config.json ] && set -- \"$@\" --mcp-config {cfg}/claude-mcp-config.json; \
|
[ -f {cfg}/claude-mcp-config.json ] && set -- \"$@\" --mcp-config {cfg}/claude-mcp-config.json; \
|
||||||
[ -f {cfg}/claude-system-prompt.md ] && set -- \"$@\" --system-prompt-file {cfg}/claude-system-prompt.md; \
|
[ -f {cfg}/claude-system-prompt.md ] && set -- \"$@\" --system-prompt-file {cfg}/claude-system-prompt.md; \
|
||||||
exec {claude} \"$@\"{continue_flag}"
|
exec {claude} \"$@\""
|
||||||
);
|
);
|
||||||
let mut cmd = std::process::Command::new("machinectl");
|
let mut cmd = std::process::Command::new("machinectl");
|
||||||
cmd.arg("shell")
|
cmd.arg("shell")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue