Skip to content

Runenv revised#424

Open
jhlee525 wants to merge 5 commits into
developfrom
runenv-revised
Open

Runenv revised#424
jhlee525 wants to merge 5 commits into
developfrom
runenv-revised

Conversation

@jhlee525

@jhlee525 jhlee525 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Summary

The RunEnv / RunEnvHandle indirection — an Arc-counted wrapper that wrapped a Box<dyn MachineDyn> and managed start-on-first-get / stop-on-last-drop ref-counting — is gone. Agents now share a Machine directly via a SharedMachine = Arc<Mutex<dyn MachineDyn>> handle, with explicit start / stop and no implicit lifecycle.

This removes a layer of state that always existed and was always a half-step out of sync with the underlying machine. The previous design hid two correctness pitfalls behind the ref-count:

  • Pure tools (ones that never need a console) would still boot the machine on first get, even when nothing would touch the console.
  • RunEnvHandle::Drop's background stop kept Arc semantics surprising: dropping the last handle was the only way to actually stop the VM, but callers couldn't tell which Arc::clone was the last one without inspecting refcounts.

After this PR, the machine's running state is exactly what Machine::is_running reports, and ToolFunc carries enough type-level information for the runtime to skip starting the machine for tools that don't need it. The companion microsandbox bump to v0.5.4 lets us delete the ailoy-side /tmp volume management that #416 had to put in place to compensate for missing upstream behaviour.

Changes

1. refactor(runenv): drop RunEnv wrapper, expose Machine via SharedMachine (85e0e81)

  • Machine trait reshape. Add is_running. start now returns &Console whose lifetime is tied to &mut self, so a console borrow holds the machine for the duration of a tool stream. stop returns Result and defaults to no-op.
  • SandboxConfigSandboxBuilder. The struct + builder split is collapsed into a single chainable SandboxBuilder. Per-process /tmp volume sweeping and the ailoy.managed label scheme introduced in fix(runenv): unblock Drop, persist /tmp, rename Machine verbs, bump msb 0.5.0 #416 are gone — microsandbox::SandboxConfig is reused directly (see the dep bump below).
  • ToolFunc splits. ToolFunc::Pure and ToolFunc::WithConsole, with a public needs_console(). The runtime skips locking and starting the machine for pure tools in execute_tool_calls. Builtins that need shell access — shell, read, write, edit, apply_patch, glob, grep, python_repl — take &dyn Console instead of Arc<RunEnvHandle>.
  • AgentBuilder / Agent API renames. .runenv(..).machine(..) (owned) and .shared_machine(..) (pre-shared handle); Agent::try_with_runenvAgent::try_with_machine; subagents inherit the parent machine via Arc::clone.

2. chore(deps): bump microsandbox 0.5.2 → 0.5.4 (rolled into 85e0e81)

The runenv refactor depends on this bump for two reasons:

  • Drop ailoy-side /tmp volume management. fix(runenv): unblock Drop, persist /tmp, rename Machine verbs, bump msb 0.5.0 #416 added per-sandbox named volumes (ailoy-tmp-{name}) with an ailoy.managed label and a startup sweep, working around microsandbox not having a first-class auto-volume API. The 0.5.4 release ships that auto-volume work upstream, so the ~200 lines of label-tracking, sweep, and rollback logic in runenv/sandbox.rs collapse to a direct microsandbox::SandboxConfig reuse.
  • SandboxBuilder parity. The chainable builder this PR introduces mirrors the upstream builder API surface that landed in 0.5.3/0.5.4.

Cargo.toml: microsandbox = "0.5.2""0.5.4". cargo update regenerates lock-file entries for microsandbox-* crates and their transitive deps.

3. refactor(runenv): promote SharedMachine to a struct (891563c)

The handle moves out of agent::rt into runenv, alongside the Machine trait it wraps. The Arc<Mutex<dyn MachineDyn>> type alias and free shared_machine() helper are replaced with #[derive(Clone)] struct SharedMachine exposing new, get, and a From<M: Machine> impl. Agents pass SharedMachine around as an opaque handle; callers don't need to know about the Arc<Mutex<>> shape.

4. refactor(agent): split subagent module, introduce AgentParts, drop spec field (107b422)

Riding on the new Machine API, the agent-side wiring gets a parallel cleanup:

  • Sub-agent ToolFunc construction moves from tool/impl/subagent.rs into agent/subagent.rs so it can share Agent-internal resolution helpers with agent/rt.rs.
  • AgentParts: everything that depends on AgentProvider is resolved once at outer-call time (model factory + options, tool descs and funcs, the flattened file list, the system message). The sub-agent closure captures only this owned, Clone-able struct — no per-invocation AgentProvider clone.
  • LangModelFactory: LangModelProvider::provide now returns a cheap factory that can be cloned freely and turned into a LangModel lazily via .make(). This is what lets AgentParts defer the actual LangModel construction to the moment a sub-agent is actually invoked.
  • Agent no longer retains AgentSpec. Files are pre-flattened across the sub-spec subtree at construction time; skills and card_name are lifted onto the runtime struct directly. Adds Agent::files() accessor. Agent::spec() is removed.
  • AgentState moves to its own module (agent/state.rs).

5. chore(agent): remove dead subagent file (012a854)

src/tool/impl/subagent.rs was already dead (its mod declaration was commented out in step 4). Deleting the file and dropping the leftover commented-out lines in src/tool/impl/mod.rs.

jhlee525 and others added 2 commits June 3, 2026 03:09
Replace the RunEnv/RunEnvHandle indirection with a Machine handle that
agents share directly as `SharedMachine = Arc<Mutex<dyn MachineDyn>>`.
This removes the implicit start-on-first-get / stop-on-last-drop
lifecycle in favor of explicit `start`/`stop`, and lets pure tools skip
booting the machine entirely.

- Machine trait: add `is_running`, `start` now returns `&Console`
  (borrow tied to the machine), `stop` returns Result and defaults to
  no-op.
- Replace `SandboxConfig` struct with a chainable `SandboxBuilder`;
  per-process /tmp volume sweeping and ailoy-managed labels are gone,
  microsandbox::SandboxConfig is reused directly.
- `ToolFunc` splits into Pure / WithConsole variants and exposes
  `needs_console()`; builtins now take `&dyn Console` instead of
  `Arc<RunEnvHandle>`.
- `AgentBuilder`/`Agent` constructors renamed `.runenv(..)` →
  `.machine(..)`/`.shared_machine(..)` and `try_with_runenv` →
  `try_with_machine`. Subagents inherit the parent machine via
  `Arc::clone`.
- Bump microsandbox 0.5.2 → 0.5.4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the SharedMachine handle out of `agent::rt` into the `runenv`
module, where the underlying `MachineDyn` trait already lives, and
replace the `Arc<Mutex<dyn MachineDyn>>` type alias + free
`shared_machine()` helper with a `#[derive(Clone)] struct SharedMachine`
exposing `new`, `get`, and a `From<M: Machine>` impl.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jhlee525 jhlee525 requested a review from khj809 June 5, 2026 05:21
@jhlee525 jhlee525 self-assigned this Jun 5, 2026
@jhlee525 jhlee525 requested review from grf53 and ljhh-0611 June 5, 2026 05:22
jhlee525 and others added 2 commits June 5, 2026 21:30
…ec field

Move sub-agent ToolFunc construction from tool/impl/subagent.rs into a
new agent/subagent.rs so it can share resolution logic with rt.rs. Pre-
resolve everything that depends on AgentProvider once at outer-call time
into a new AgentParts struct (model factory + options, tool descs/funcs,
flattened files, skills, card name, system message); the sub-agent
closure captures only these owned values, so it no longer clones an
AgentProvider per invocation. LangModelProvider::provide now returns a
cheap LangModelFactory that can be cloned and lazily turned into a
LangModel.

Agent no longer keeps a copy of AgentSpec: files are pre-flattened
across the sub-spec subtree, and skills/card_name are lifted onto the
runtime struct directly. Adds Agent::files() accessor. AgentState moves
to its own module.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jhlee525 jhlee525 marked this pull request as ready for review June 5, 2026 12:58
Comment thread src/runenv/sandbox.rs
Comment on lines -26 to -59
/// A volume mount attached to a sandbox at creation time.
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum VolumeMount {
/// Bind-mount a host directory into the guest.
Bind {
/// Absolute or relative host path.
host: PathBuf,
/// Absolute guest path (e.g. `/data`).
guest: String,
/// When `true`, the guest cannot write to this mount.
#[serde(default)]
readonly: bool,
},
/// Mount a microsandbox named volume, stored under the resolved
/// microsandbox home directory (e.g. `~/.microsandbox/volumes/<name>/`
/// or `$MSB_HOME/volumes/<name>/`).
/// The volume persists across sandbox restarts and can be shared between sandboxes.
Named {
/// Name of the pre-existing microsandbox volume.
name: String,
/// Absolute guest path.
guest: String,
/// When `true`, the guest cannot write to this mount.
#[serde(default)]
readonly: bool,
},
/// Memory-backed temporary filesystem. Disappears when the sandbox stops.
Tmpfs {
/// Absolute guest path.
guest: String,
/// Size limit in MiB. `None` means no limit.
size_mib: Option<u32>,
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agent-k already depends on this volume mount for mounting project/session files. Why did you decide to remove this, and how you're planning to mount the files without this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants