feat(#2067): pin choom to a dedicated claude session id
This commit is contained in:
parent
964e55f0bb
commit
ce959d7700
1 changed files with 62 additions and 30 deletions
|
|
@ -111,29 +111,31 @@ 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
|
/// Replaces the current process with `machinectl shell
|
||||||
/// <name>@h-<name>` running `claude --continue` from the agent's
|
/// <name>@h-<name>` running claude from the agent's state dir — drops
|
||||||
/// state dir — drops the operator straight into the agent's live
|
/// the operator into a private Claude session in the agent's project,
|
||||||
/// Claude session with its full loaded context and persona. Requires
|
/// with its full settings, MCP tools, and persona. Requires root (same
|
||||||
/// root (same as all machinectl shell operations) and the container
|
/// as all machinectl shell operations) and the container must be
|
||||||
/// must be running.
|
/// running.
|
||||||
///
|
///
|
||||||
/// To match the harness exactly, choom enters **as the agent user**
|
/// To match the harness exactly, choom enters **as the agent user**
|
||||||
/// (not root) so claude reads the OAuth credentials from the agent's
|
/// (not root) so claude reads the OAuth credentials from the agent's
|
||||||
/// `/home/<name>/.claude`; runs from the agent's state dir
|
/// `/home/<name>/.claude`; runs from the agent's state dir
|
||||||
/// (`/agents/<name>/state`) so `--continue` resumes the right
|
/// (`/agents/<name>/state`) so the session is scoped to the right
|
||||||
/// per-project session and `CLAUDE.md` loads; and passes the same
|
/// project and `CLAUDE.md` loads; and passes the same `--settings` /
|
||||||
/// `--settings` / `--mcp-config` / `--system-prompt-file` the harness
|
/// `--mcp-config` / `--system-prompt-file` the harness writes to
|
||||||
/// writes to `/run/hive-config/` (settings, MCP tools, role prompt).
|
/// `/run/hive-config/` (settings, MCP tools, role prompt). Entering as
|
||||||
/// Entering as root (the `machinectl shell` default) loses all of it.
|
/// root (the `machinectl shell` default) loses all of it.
|
||||||
///
|
///
|
||||||
/// Pass `--fresh` to start a new Claude session instead of continuing
|
/// choom pins its OWN session id (not a bare `--continue`) so it never
|
||||||
/// the most recent one.
|
/// resumes or corrupts the harness's live session in the same project
|
||||||
|
/// dir; re-running choom rejoins that private session. Pass `--fresh`
|
||||||
|
/// to reset it and start clean.
|
||||||
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
|
/// Reset choom's dedicated Claude session and start it clean.
|
||||||
/// recent one. Without this flag `--continue` is passed to Claude
|
/// Without this flag choom resumes its own prior session (separate
|
||||||
/// so the operator joins the agent's ongoing session context.
|
/// from the harness's live session), creating it on first use.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
fresh: bool,
|
fresh: bool,
|
||||||
},
|
},
|
||||||
|
|
@ -1049,22 +1051,27 @@ fn agent_exists(name: &str) -> Result<bool> {
|
||||||
/// session as the agent user (the meta-flake sets
|
/// session as the agent user (the meta-flake sets
|
||||||
/// `hyperhive.user.name` to the agent label, so the unix user name
|
/// `hyperhive.user.name` to the agent label, so the unix user name
|
||||||
/// matches the agent name).
|
/// matches the agent name).
|
||||||
/// 2. **Start in the agent's state dir.** `claude --continue` resumes
|
/// 2. **Start in the agent's state dir.** A claude session is scoped to
|
||||||
/// the most recent session *for the current project directory*, and
|
/// the project directory it ran in, and project memory (`CLAUDE.md`)
|
||||||
/// project memory (`CLAUDE.md`) is read from the cwd. The harness
|
/// is read from the cwd. The harness runs claude from
|
||||||
/// runs claude from `/agents/<name>/state`, so choom has to `cd`
|
/// `/agents/<name>/state`, so choom has to `cd` there or its session +
|
||||||
/// there or `--continue` finds no session and the persona doesn't
|
/// persona resolve against the wrong project.
|
||||||
/// load.
|
|
||||||
/// 3. **Pass the harness's claude flags.** The harness drops
|
/// 3. **Pass the harness's claude flags.** The harness drops
|
||||||
/// `claude-{settings.json,mcp-config.json,system-prompt.md}` into
|
/// `claude-{settings.json,mcp-config.json,system-prompt.md}` into
|
||||||
/// `/run/hive-config/` (`hive-ag3nt::paths::config_dir()`) and runs
|
/// `/run/hive-config/` (`hive-ag3nt::paths::config_dir()`) and runs
|
||||||
/// claude with `--settings` / `--mcp-config` / `--system-prompt-file`
|
/// claude with `--settings` / `--mcp-config` / `--system-prompt-file`
|
||||||
/// pointing at them. A bare `claude --continue` skips all three, so
|
/// pointing at them. Skipping them gives the operator default settings,
|
||||||
/// the operator gets default settings, no hyperhive/matrix MCP tools
|
/// no hyperhive/matrix MCP tools (needed to replay a tool-using
|
||||||
/// (which `--continue` needs to replay a tool-using history), and no
|
/// history), and no role prompt. choom mirrors those flags; each is
|
||||||
/// role prompt. choom now mirrors those flags. Each is added only if
|
/// added only if the file exists, so a mid-restart container degrades
|
||||||
/// the file exists, so a mid-restart container degrades to a bare
|
/// to a bare session instead of erroring.
|
||||||
/// session instead of erroring.
|
/// 4. **Pin choom's own session (NOT bare `--continue`).** The harness's
|
||||||
|
/// live session is pinned to a claude-assigned id in this same project
|
||||||
|
/// dir; a bare `--continue` would resume the most-recent session there
|
||||||
|
/// and let choom + the harness clobber each other's history. choom
|
||||||
|
/// instead uses a fixed sentinel session id (`--resume` it if present,
|
||||||
|
/// else `--session-id` to create it), so it gets a private session that
|
||||||
|
/// never collides with the harness's. `--fresh` resets that session.
|
||||||
///
|
///
|
||||||
/// `machinectl shell` inherits the caller's PTY, so the session is
|
/// `machinectl shell` inherits the caller's PTY, so the session is
|
||||||
/// fully interactive. Requires root and a running container.
|
/// fully interactive. Requires root and a running container.
|
||||||
|
|
@ -1085,19 +1092,44 @@ fn choom(name: &str, fresh: bool) -> Result<()> {
|
||||||
// `hive-config`); matches `hive-ag3nt::paths::config_dir()`. These
|
// `hive-config`); matches `hive-ag3nt::paths::config_dir()`. These
|
||||||
// are the exact files the harness passes to claude each turn.
|
// 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" };
|
// choom pins its OWN claude session under a fixed sentinel id so it
|
||||||
|
// never resumes — and thus never corrupts — the harness's live session,
|
||||||
|
// which shares this exact project dir (`state_dir`). The harness session
|
||||||
|
// id is claude-assigned (random, captured + `--resume`d each turn; see
|
||||||
|
// hive-ag3nt turn.rs), so it can't collide with this id. A single fixed
|
||||||
|
// id is safe for every agent because containers are isolated (each has
|
||||||
|
// its own `$HOME/.claude`).
|
||||||
|
let choom_session_id = "c0c0c0c0-0000-4000-8000-c0ffeec0ffee";
|
||||||
|
// `--fresh` drops the stored choom session and starts it clean; the
|
||||||
|
// default resumes it when a session file for the id exists (so the
|
||||||
|
// operator rejoins their own prior choom context), creating it on first
|
||||||
|
// use. `find -name <id>.jsonl` matches regardless of claude's project
|
||||||
|
// slug, since the id is unique to choom.
|
||||||
|
let session_setup = if fresh {
|
||||||
|
format!(
|
||||||
|
"find \"$HOME/.claude/projects\" -name {choom_session_id}.jsonl -delete 2>/dev/null; \
|
||||||
|
set -- \"$@\" --session-id {choom_session_id}; "
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"if find \"$HOME/.claude/projects\" -name {choom_session_id}.jsonl -print -quit 2>/dev/null | grep -q .; \
|
||||||
|
then set -- \"$@\" --resume {choom_session_id}; \
|
||||||
|
else set -- \"$@\" --session-id {choom_session_id}; fi; "
|
||||||
|
)
|
||||||
|
};
|
||||||
// Build the claude argv as the harness does, but only include each
|
// Build the claude argv as the harness does, but only include each
|
||||||
// flag when its file is present so a half-up container falls back to
|
// flag when its file is present so a half-up container falls back to
|
||||||
// a bare session rather than a hard claude error. `name` is
|
// a bare session rather than a hard claude error. `name` is
|
||||||
// constrained to `[a-z0-9._-]` so there's nothing to quote.
|
// constrained to `[a-z0-9._-]` so there's nothing to quote.
|
||||||
// `cd` so `--continue` + CLAUDE.md resolve against the agent's
|
// `cd` so the pinned session + CLAUDE.md resolve against the agent's
|
||||||
// project; `exec` hands the PTY to claude directly.
|
// project; `exec` hands the PTY to claude directly.
|
||||||
let inner = format!(
|
let inner = format!(
|
||||||
"cd {state_dir} || exit 1; set --; \
|
"cd {state_dir} || exit 1; set --; \
|
||||||
[ -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}"
|
{session_setup}\
|
||||||
|
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