Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@ Agent OS is the agent-facing wrapper around secure-exec. It provides ACP session
- secure-exec dependency workflow. Manage the secure-exec dependency ONLY through `scripts/secure-exec-dep.mjs` (the `just secure-exec-*` recipes); never hand-edit the `path` / `version` / `catalog:` pins.
- Testing against local secure-exec changes: run `just secure-exec-local` to repoint npm (`link:`) and crates (`path = "../secure-exec/..."`) at the sibling checkout, then `node scripts/secure-exec-dep.mjs set-crate-version <sibling-version>` so the Cargo version requirement matches the sibling crate version (otherwise cargo cannot resolve the path deps). Also run `pnpm install` in `../secure-exec` first, or cargo panics in `v8-runtime/build.rs` with "missing Node dependencies at .../packages/build-tools/node_modules" (the V8 bridge assets are built from there). Use `just secure-exec-status` to inspect. This mode is for local builds/tests ONLY.
- Pushing changes that depend on secure-exec changes: NEVER push with local (`path:` / `link:`) dependencies — this rule still holds. First preview-publish the secure-exec changes to their own secure-exec branch (the `preview-publish-secure-exec` flow), then point agent-os back at that exact published version with `just secure-exec-pinned` + `just secure-exec-set-version <version>`. Only commit/push the pinned state. Note the committed `Cargo.toml`/`Cargo.lock` stay pinned to a **crates.io** version (release-clean), NOT to a path/clone: a preview pin keeps the crate version at the last crates.io release and the secure-exec *crate* changes are picked up at CI build time by `prepare-build`, which clones secure-exec at the pinned `<sha>` and builds cargo in local mode (crates.io has no preview track). So you never commit a local path dep, yet a preview's crate changes still build. See "Depending on unreleased secure-exec changes".
- Keep generic runtime, kernel, VFS, language execution, and registry software behavior in secure-exec.
- Agent OS owns ACP, sessions, agent adapters, toolkit semantics, quickstarts, and the AgentOs facade.
- Keep generic runtime, kernel, VFS, language execution, generic registry software, and packaged agent definitions/adapters in secure-exec.
- Agent OS owns ACP, sessions, toolkit semantics, quickstarts, docs, and the AgentOs facade.
- Call OS instances VMs, never sandboxes.
- The protocol has no backwards compatibility. Clients and the sidecar ship in same-version lockstep, so never add protocol or config versioning, runtime negotiation, fallbacks, or converters. Configs such as `CreateVmConfig` carry no `version` field; the single same-version wire handshake is the only version check. Change the protocol freely and update both sides together.

## Development

### secure-exec dependency versions (`just`)

Two independent version tracks:
- **secure-exec** — the `@secure-exec/*` npm packages and the `secure-exec-*` Cargo crates share **one** version on a real **release** (npm + crates publish together). On a **preview** they diverge: npm pins to the `0.0.0-<branch>.<sha>` preview tag while the crate version requirement stays at the last crates.io release (crates.io has no preview track), and CI's `prepare-build` clones secure-exec at `<sha>` to build the crates. See "Depending on unreleased secure-exec changes".
- **`@agentos-software/*`** software packages (registry agents / WASM commands) are on a **separate** track and version independently of secure-exec.
Three release tracks:
- **secure-exec runtime** — `@secure-exec/*` npm packages and `secure-exec-*` crates. See "Depending on unreleased secure-exec changes" for preview-vs-release behavior.
- **`@agentos-software/*` registry packages** — generic VM software from secure-exec `registry/software/*` plus agent adapters from secure-exec `registry/agent/*`; versioned independently of secure-exec runtime packages.
- **agent-os product/API** — `@rivet-dev/agentos*`, AgentOs APIs, sidecar wrapper, docs, quickstarts, and examples; pins compatible secure-exec and registry package versions.

Manage them ONLY via these recipes (never hand-edit `path`/`version`/`catalog:` pins):
- `just secure-exec-local` — point deps at the sibling `../secure-exec` checkout for local hacking.
Expand Down
37 changes: 10 additions & 27 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ repository = "https://github.com/rivet-dev/agent-os"
# normal crates.io dependencies so CI/publish builds do not need a sibling
# checkout.
[workspace.dependencies]
agentos-bridge = { package = "secure-exec-bridge", version = "0.3.1-rc.4" }
agentos-bridge = { package = "secure-exec-bridge", path = "../secure-exec/crates/bridge", version = "0.3.0-rc.1" }
agentos-protocol = { path = "crates/agentos-protocol", version = "0.2.0-rc.3" }
agentos-sidecar = { path = "crates/agentos-sidecar", version = "0.2.0-rc.3" }
agentos-sidecar-browser = { path = "crates/agentos-sidecar-browser", version = "0.2.0-rc.3" }
agentos-kernel = { package = "secure-exec-kernel", version = "0.3.1-rc.4" }
agentos-execution = { package = "secure-exec-execution", version = "0.3.1-rc.4" }
agentos-v8-runtime = { package = "secure-exec-v8-runtime", version = "0.3.1-rc.4" }
secure-exec-client = { version = "0.3.1-rc.4" }
secure-exec-bridge = { version = "0.3.1-rc.4" }
secure-exec-sidecar = { version = "0.3.1-rc.4" }
secure-exec-vm-config = { version = "0.3.1-rc.4" }
agentos-kernel = { package = "secure-exec-kernel", path = "../secure-exec/crates/kernel", version = "0.3.0-rc.1" }
agentos-execution = { package = "secure-exec-execution", path = "../secure-exec/crates/execution", version = "0.3.0-rc.1" }
agentos-v8-runtime = { package = "secure-exec-v8-runtime", path = "../secure-exec/crates/v8-runtime", version = "0.3.0-rc.1" }
secure-exec-client = { path = "../secure-exec/crates/secure-exec-client", version = "0.3.0-rc.1" }
secure-exec-bridge = { path = "../secure-exec/crates/bridge", version = "0.3.0-rc.1" }
secure-exec-sidecar = { path = "../secure-exec/crates/sidecar", version = "0.3.0-rc.1" }
secure-exec-vm-config = { path = "../secure-exec/crates/vm-config", version = "0.3.0-rc.1" }
vbare = "0.0.4"
vbare-compiler = { package = "rivet-vbare-compiler", version = "0.0.5" }
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# TODO

- Add OCI import/export support for overlay filesystem layers and snapshots after phase 1. The phase-1 API should only guarantee the bundled base filesystem artifact and the internal snapshot export/import format.
- Run the full registry native-kernel suite after rebuilding the WASM command artifacts. `registry/tests/smoke.test.ts` skipped in this pass because the local `registry/native/target/wasm32-wasip1/release/commands` binaries were not present, so the new `createKernel` sidecar-backed path is only verified by package-level builds plus targeted core tests right now.
- Run the full secure-exec registry native-kernel suite after rebuilding the WASM command artifacts. `../secure-exec/registry/tests/smoke.test.ts` skipped in this pass because the local `../secure-exec/registry/native/target/wasm32-wasip1/release/commands` binaries were not present, so the new `createKernel` sidecar-backed path is only verified by package-level builds plus targeted core tests right now.
- Expand verification for the new sidecar-backed kernel compatibility surface around `socketTable`/`processTable` observability and browser runtime end-to-end specs. The source builds are green and targeted tests passed, but the deeper integration suites were not exercised in this pass.
62 changes: 48 additions & 14 deletions crates/agentos-sidecar/src/acp_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ use tokio::sync::Mutex;
const INITIALIZE_TIMEOUT: Duration = Duration::from_secs(10);
const SESSION_NEW_TIMEOUT: Duration = Duration::from_secs(30);
const SESSION_CLOSE_TIMEOUT: Duration = Duration::from_secs(5);
// While an ACP request is in flight the stdio loop is inside the extension
// dispatch, so this wait loop becomes the cooperative VM I/O pump. Keep it at
// the same cadence as secure-exec's outer event pump so adapter fetches and
// process output keep moving mid-turn.
const ACP_JSON_RPC_POLL_INTERVAL: Duration = Duration::from_micros(250);
const ACP_CANCEL_METHOD: &str = "session/cancel";
/// Transcript-continuation preamble prepended (once) to the first prompt after a
/// fallback resume. Lossy-but-universal floor: the agent is handed a *pointer* to
Expand Down Expand Up @@ -673,7 +678,11 @@ impl AcpExtension {
Err(error) => return AcpHandlerOutput::response(Err(error)),
};
match ctx.ext_event_wire(event) {
Ok(event) => exchange.events.push(event),
Ok(frame) => {
if let Err(error) = deliver_event(&ctx, &mut exchange.events, frame) {
return AcpHandlerOutput::response(Err(error));
}
}
Err(error) => return AcpHandlerOutput::response(Err(error)),
}
}
Expand Down Expand Up @@ -1336,6 +1345,23 @@ impl AcpSessionRecord {
}

#[allow(clippy::too_many_arguments)]
/// Deliver an ACP event frame to the host. Streams it live through the sidecar's
/// event sink (the stdio path) the instant it is produced; only when no live sink
/// is configured (an in-process `NativeSidecar` with no stdout loop) does it fall
/// back to collecting the frame into `events` for the dispatch-result batch. This
/// is what makes `session/update`s arrive mid-turn instead of all arriving at
/// once when the `session/prompt` dispatch finally resolves.
fn deliver_event(
ctx: &ExtensionContext<'_>,
events: &mut Vec<secure_exec_sidecar::wire::EventFrame>,
frame: secure_exec_sidecar::wire::EventFrame,
) -> Result<(), SidecarError> {
if let Some(frame) = ctx.emit_event_wire(frame)? {
events.push(frame);
}
Ok(())
}

async fn send_json_rpc_request(
ctx: &mut ExtensionContext<'_>,
process_id: &str,
Expand Down Expand Up @@ -1400,7 +1426,7 @@ async fn send_json_rpc_request(
}
let remaining = deadline.saturating_duration_since(now);
let event = ctx
.poll_event_wire(remaining.min(Duration::from_millis(250)))
.poll_event_wire(remaining.min(ACP_JSON_RPC_POLL_INTERVAL))
.await?;
let Some(event) = event else {
continue;
Expand Down Expand Up @@ -1455,7 +1481,7 @@ async fn send_json_rpc_request(
);
}
if let Some(session_id) = event_session_id {
events.push(ctx.ext_event_wire(encode_event(
let frame = ctx.ext_event_wire(encode_event(
AcpEvent::AcpSessionEvent(AcpSessionEvent {
session_id: session_id.to_string(),
notification: serde_json::to_string(&message).map_err(
Expand All @@ -1466,7 +1492,8 @@ async fn send_json_rpc_request(
},
)?,
}),
)?)?);
)?)?;
deliver_event(ctx, &mut events, frame)?;
} else {
notifications.push(serde_json::to_string(&message).map_err(
|error| {
Expand All @@ -1482,16 +1509,23 @@ async fn send_json_rpc_request(
EventPayload::ProcessOutputEvent(output)
if output.process_id == process_id && output.channel == StreamChannel::Stderr =>
{
events.push(
ctx.ext_event_wire(encode_event(AcpEvent::AcpAgentStderrEvent(
AcpAgentStderrEvent {
session_id: event_session_id.unwrap_or_default().to_string(),
agent_type: agent_type.to_string(),
process_id: process_id.to_string(),
chunk: output.chunk,
},
))?)?,
);
let frame = ctx.ext_event_wire(encode_event(AcpEvent::AcpAgentStderrEvent(
AcpAgentStderrEvent {
session_id: event_session_id.unwrap_or_default().to_string(),
agent_type: agent_type.to_string(),
process_id: process_id.to_string(),
chunk: output.chunk,
},
))?)?;
// Stream live during an owned session turn (prompt/cancel), but
// keep bootstrap stderr (initialize/session-new/load, which pass
// no session id) in the batch so it still arrives for callers that
// only subscribe after create/resume resolves.
if event_session_id.is_some() {
deliver_event(ctx, &mut events, frame)?;
} else {
events.push(frame);
}
}
EventPayload::ProcessExitedEvent(exited) if exited.process_id == process_id => {
// Embed ADAPTER_EXITED_ERROR_MARKER directly so is_adapter_exited_error()
Expand Down
10 changes: 5 additions & 5 deletions examples/quickstart/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
"sandbox-agent": "^0.4.2",
"dockerode": "^4.0.9",
"get-port": "^7.1.0",
"@agentos-software/git": "catalog:",
"@agentos-software/claude-code": "catalog:",
"@agentos-software/opencode": "catalog:",
"@agentos-software/pi": "catalog:",
"@secure-exec/s3": "catalog:",
"@agentos-software/git": "link:../../../secure-exec/registry/software/git",
"@agentos-software/claude-code": "link:../../../secure-exec/registry/agent/claude",
"@agentos-software/opencode": "link:../../../secure-exec/registry/agent/opencode",
"@agentos-software/pi": "link:../../../secure-exec/registry/agent/pi",
"@secure-exec/s3": "link:../../../secure-exec/registry/file-system/s3",
"zod": "^4.1.11"
},
"devDependencies": {
Expand Down
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"build": "npx turbo build",
"test": "pnpm --dir packages/core build && pnpm --dir packages/dev-shell build && pnpm --dir packages/dev-shell check-types && pnpm --dir packages/dev-shell test && npx turbo test --concurrency=1 --filter='!@rivet-dev/agentos-dev-shell'",
"test:migration-parity": "pnpm --dir packages/core exec vitest run tests/migration-parity.test.ts --reporter=verbose",
"test:post-python-parity": "pnpm --dir packages/core build && pnpm --dir packages/core exec vitest run tests/agentos-base-filesystem.test.ts && pnpm --dir packages/dev-shell exec vitest run test/dev-shell.integration.test.ts && ./node_modules/.bin/vitest run --testTimeout=55000 --hookTimeout=30000 registry/tests/kernel/cross-runtime-terminal.test.ts registry/tests/kernel/ctrl-c-shell-behavior.test.ts registry/tests/kernel/node-binary-behavior.test.ts registry/tests/kernel/e2e-project-matrix.test.ts",
"test:post-python-parity": "pnpm --dir packages/core build && pnpm --dir packages/core exec vitest run tests/agentos-base-filesystem.test.ts && pnpm --dir packages/dev-shell exec vitest run test/dev-shell.integration.test.ts",
"test:watch": "npx turbo watch test",
"check-types": "npx turbo check-types --concurrency=1",
"lint": "pnpm biome check .",
Expand All @@ -23,11 +23,11 @@
"@copilotkit/llmock": "^1.6.0",
"@mariozechner/pi-coding-agent": "^0.60.0",
"@rivet-dev/agentos-core": "workspace:*",
"@agentos-software/claude-code": "catalog:",
"@agentos-software/codex": "catalog:",
"@agentos-software/common": "catalog:",
"@secure-exec/core": "catalog:",
"@agentos-software/pi": "catalog:",
"@agentos-software/claude-code": "link:../secure-exec/registry/agent/claude",
"@agentos-software/codex": "link:../secure-exec/registry/agent/codex",
"@agentos-software/common": "link:../secure-exec/registry/software/common",
"@secure-exec/core": "link:../secure-exec/packages/core",
"@agentos-software/pi": "link:../secure-exec/registry/agent/pi",
"@types/node": "^22.19.15",
"jszip": "^3.10.1",
"pdf-lib": "^1.17.1",
Expand Down
4 changes: 2 additions & 2 deletions packages/agentos-sandbox/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
},
"dependencies": {
"@rivet-dev/agentos-core": "workspace:*",
"@secure-exec/sandbox": "catalog:",
"@secure-exec/sandbox": "link:../../../secure-exec/registry/tool/sandbox",
"sandbox-agent": "^0.4.2",
"zod": "^4.1.11"
},
"devDependencies": {
"@agentos-software/common": "catalog:",
"@agentos-software/common": "link:../../../secure-exec/registry/software/common",
"@types/node": "^22.10.2",
"typescript": "^5.7.2",
"vitest": "^2.1.8"
Expand Down
2 changes: 1 addition & 1 deletion packages/agentos/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"test": "vitest run"
},
"dependencies": {
"@agentos-software/common": "catalog:",
"@agentos-software/common": "link:../../../secure-exec/registry/software/common",
"@rivet-dev/agentos-core": "workspace:*",
"@rivet-dev/agentos-sidecar": "workspace:*",
"@rivetkit/react": "0.0.0-feat-dylib-actor-plugin.c44621f",
Expand Down
Loading
Loading