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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- run: cargo build --release -p agentos-native-sidecar
- run: cargo build --release -p agentos-native-baseline
- run: cargo build --release --target wasm32-wasip1 -p agentos-native-baseline
- run: make -C registry/native commands
- run: make -C toolchain commands
- run: node packages/runtime-core/scripts/copy-wasm-commands.mjs --require
- run: pnpm --dir packages/runtime-core build
- run: pnpm --dir packages/runtime-benchmarks bench:check
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
with:
targets: wasm32-wasip1
- run: pnpm install --frozen-lockfile
- run: make -C registry/native commands
- run: make -C toolchain commands
- run: node packages/runtime-core/scripts/copy-wasm-commands.mjs --require
- run: cargo check --workspace
- run: cargo clippy --workspace --all-targets -- -D warnings
Expand Down
11 changes: 5 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ jobs:
- run: node scripts/check-agentos-client-protocol-compat.mjs
- run: node --test scripts/verify-fixed-versions.test.mjs
- run: node scripts/verify-fixed-versions.mjs
- run: pnpm check-layout
- run: cargo fmt --check
- run: pnpm check-types
- run: pnpm lint
Expand All @@ -63,11 +64,11 @@ jobs:
targets: wasm32-wasip1
- uses: Swatinem/rust-cache@v2
with:
workspaces: registry/native -> target
workspaces: toolchain -> toolchain/target
cache-workspace-crates: true
key: wasm-commands-${{ hashFiles('registry/native/Cargo.lock') }}
key: wasm-commands-${{ hashFiles('toolchain/Cargo.lock') }}
- run: pnpm install --frozen-lockfile --filter '@rivet-dev/agentos-runtime-core...'
- run: make -C registry/native commands
- run: make -C toolchain commands
- run: node packages/runtime-core/scripts/copy-wasm-commands.mjs --require
- uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -104,9 +105,7 @@ jobs:
- uses: actions/download-artifact@v4
with:
name: wasm-commands
# Restore the canonical build output so registry packages can stage
# from the same path used by a local `just registry-native` build.
path: registry/native/target/wasm32-wasip1/release/commands
path: packages/runtime-core/commands
- run: node packages/runtime-core/scripts/copy-wasm-commands.mjs --require
- run: |
if grep -qE '^[[:space:]]*-[[:space:]]*website[[:space:]]*$' pnpm-workspace.yaml; then
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ jobs:
targets: wasm32-wasip1
- uses: Swatinem/rust-cache@v2
with:
workspaces: registry/native -> target
workspaces: toolchain -> toolchain/target
cache-workspace-crates: true
key: wasm-commands-${{ hashFiles('registry/native/Cargo.lock') }}
key: wasm-commands-${{ hashFiles('toolchain/Cargo.lock') }}
- run: pnpm install --frozen-lockfile --filter '@rivet-dev/agentos-runtime-core...'
- run: make -C registry/native commands
- run: make -C toolchain commands
- run: node packages/runtime-core/scripts/copy-wasm-commands.mjs --require
- uses: actions/upload-artifact@v4
with:
Expand Down
15 changes: 14 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,19 @@ core.[0-9]*
# Secrets
secrets/**/*

# Registry build artifacts
# Generated WASM toolchain and software artifacts. These are rebuilt for tests
# and releases and must never be committed.
packages/runtime-core/commands/
toolchain/vendor/
toolchain/c/build/
toolchain/c/vendor/
toolchain/c/libs/
toolchain/c/sysroot/
toolchain/c/.cache/
toolchain/std-patches/wasi-libc-overrides/*.o
software/*/bin/

# Legacy registry build paths retained for old worktrees.
registry/native/target/
registry/native/vendor/
registry/native/c/build/
Expand All @@ -47,6 +59,7 @@ registry/native/c/.cache/
registry/native/c/sysroot/
registry/software/coreutils/bin/
registry/software/*/wasm/
software/*/wasm/
registry/software/*/agentos-package.meta.json
registry/.last-publish-hash
registry/software/*/.last-publish-hash
Expand Down
59 changes: 44 additions & 15 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,26 +74,55 @@ the guest — over inventing a softer fallback that hides the failure.
only when the caller explicitly supplied them.
- Agent adapters must use real upstream SDKs. Do not replace SDK adapters with
direct API-call stubs.
- Native registry command binaries are generated artifacts. Do not commit
`registry/software/coreutils/bin/`; a fresh checkout intentionally has no
staged coreutils commands. Any VM, browser demo, or test that needs `sh` or
coreutils must build and stage the complete package from the repository root:
- WASM command binaries and every toolchain build output are generated
artifacts. Never commit `packages/runtime-core/commands/`, `software/*/bin/`,
`toolchain/vendor/`, `toolchain/c/{build,vendor,libs,sysroot,.cache}/`, or
`toolchain/std-patches/wasi-libc-overrides/*.o`. A fresh checkout intentionally
contains source and patches only. Rebuild and stage the complete default tool
set from the repository root with:

```bash
pnpm install --frozen-lockfile
just registry-native
pnpm --filter @agentos-software/coreutils build:runtime
just tools-rebuild
```

`just registry-native` compiles the patched Rust and C WASI sources into
`registry/native/target/wasm32-wasip1/release/commands/`; the pnpm command
then stages `bin/` and assembles `dist/package/`. `just registry-native-cmd
sh` builds only one development artifact and is not sufficient to assemble
the complete coreutils package. The ordinary `build` script may create an
empty placeholder for source-only repository checks; that placeholder is not
runnable and must never be treated as proof that coreutils was built.
Publishing coreutils must run the strict `build:runtime` lifecycle and fail
when the generated command set is absent or incomplete.
`just tools-rebuild` runs `just toolchain-build`, copies the canonical output
from `toolchain/target/wasm32-wasip1/release/commands/` into runtime staging,
and builds the `@agentos-software/*` packages. For focused development,
`just toolchain-cmd <command>` rebuilds one command, but it is not sufficient
for a release or complete package validation. Publish workflows must rebuild
and stage the complete command set and fail when it is absent or incomplete.

## Software Build (WASM Toolchain)

Registry software is **real upstream Linux software** (GNU coreutils, grep, sed,
gawk, real curl/sqlite/duckdb/vim, …) compiled to `wasm32-wasip1` against a
**sysroot we fully own** — a patched Rust std + libc whose gaps are filled by
custom host-syscall imports. Treat that target as **native POSIX**;
`wasm32-wasip1` is an implementation detail, not a feature ceiling.

- **We do not depend on stock WASI / wasi-libc.** The sysroot is ours. A missing
libc/POSIX API (`getrlimit`/`RLIMIT_NOFILE`, `getgroups`, spawn, fd dup, …) is
never a blocker — implement it (real, or a sane stub) in the patched
std/libc/host-import layer. "WASI doesn't have X" is not a reason to stop; X is
ours to add.
- **Fix portability one layer down, in the sysroot** — a new std/libc patch or a
new host import — not with `cfg(target_*)` branches or shims in the tool's own
source. A WASM-specific branch in application code usually means the fix
belongs in the libc layer.
- **Patch the real upstream tool only as a fallback**, when the fix genuinely
cannot live in the sysroot. Patching the real tool is allowed; reimplementing
it is not.
- **"NOT POSSIBLE" is reserved for genuine impossibility** after exhausting both
sysroot patches and tool patches — never for a missing syscall we could
implement. Document the specific wall if you claim it.
- **Working in `software/`, you may (and should) fix the layer underneath.** When
a package behaves differently from real Linux, the root cause is usually not the
package — it's the runtime. It is in-scope and expected to fix the underlying
implementation: the Node-compat / bridge layer, the WASM execution runtime, the
kernel/VFS syscalls, or the patched sysroot/libc. Do **not** paper over a
Linux-deviating behavior in the package, its wrapper, or its test — chase it
down into whichever runtime layer owns it and make that layer match Linux.

## Publishing

Expand Down
10 changes: 10 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
resolver = "2"
exclude = ["software"]
members = [
"crates/agentos-protocol",
"crates/agentos-sidecar-core",
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,15 +157,14 @@ Browse pre-built agents, tools, filesystems, and software packages at the [agent
| `@agentos-software/git` | git | git version control | rust | - | - |
| `@agentos-software/grep` | grep | GNU grep pattern matching (grep, egrep, fgrep) | rust | - | - |
| `@agentos-software/gzip` | gzip | GNU gzip compression (gzip, gunzip, zcat) | rust | - | - |
| `@agentos-software/http-get` | http-get | Minimal HTTP GET fetch helper | c | - | - |
| `@agentos-software/jq` | jq | jq JSON processor | rust | - | - |
| `@agentos-software/ripgrep` | ripgrep | ripgrep fast recursive search | rust | - | - |
| `@agentos-software/sed` | sed | GNU sed stream editor | rust | - | - |
| `@agentos-software/sqlite3` | sqlite3 | SQLite3 command-line interface | c | - | - |
| `@agentos-software/tar` | tar | GNU tar archiver | rust | - | - |
| `@agentos-software/tree` | tree | tree directory listing | rust | - | - |
| `@agentos-software/unzip` | unzip | unzip archive extraction | c | - | - |
| `@agentos-software/wget` | wget | GNU wget HTTP client | c | - | - |
| `@agentos-software/wget` | wget | GNU Wget file downloader | c | - | - |
| `@agentos-software/yq` | yq | yq YAML/JSON processor | rust | - | - |
| `@agentos-software/zip` | zip | zip archive creation | c | - | - |

Expand All @@ -175,7 +174,7 @@ Browse pre-built agents, tools, filesystems, and software packages at the [agent
|---------|-------------|----------|
| `@agentos-software/build-essential` | Build-essential WASM command set (standard + make + git + curl) | standard, make, git, curl |
| `@agentos-software/common` | Common WASM command set (coreutils + sed + grep + gawk + findutils + diffutils + tar + gzip) | coreutils, sed, grep, gawk, findutils, diffutils, tar, gzip |
| `@agentos-software/everything` | All available WASM command packages in a single bundle | coreutils, sed, grep, gawk, findutils, diffutils, tar, gzip, curl, zip, unzip, jq, ripgrep, fd, tree, file, yq, codex-cli |
| `@agentos-software/everything` | All available WASM command packages in a single bundle | coreutils, sed, grep, gawk, findutils, diffutils, tar, gzip, curl, wget, zip, unzip, jq, ripgrep, fd, tree, file, yq, codex-cli |
<!-- END PACKAGE TABLE -->

## License
Expand Down
31 changes: 18 additions & 13 deletions crates/bridge/bridge-contract.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,13 @@
"_childProcessSpawnStart",
"_childProcessPoll",
"_childProcessStdinWrite",
"_childProcessStdinClose",
"_childProcessKill",
"_processKill",
"_processSignalState"
]
},
"_childProcessStdinClose",
"_childProcessKill",
"_processKill",
"_processSignalState",
"_processTakeSignal"
]
},
{
"convention": "syncPromise",
"argumentTypes": [
Expand Down Expand Up @@ -945,13 +946,17 @@
"method": "process.kill",
"translateArgs": false
},
"_processSignalState": {
"method": "process.signal_state",
"translateArgs": false
},
"_ptySetRawMode": {
"method": "__pty_set_raw_mode",
"translateArgs": false
"_processSignalState": {
"method": "process.signal_state",
"translateArgs": false
},
"_processTakeSignal": {
"method": "process.take_signal",
"translateArgs": false
},
"_ptySetRawMode": {
"method": "__pty_set_raw_mode",
"translateArgs": false
},
"_resolveModule": {
"method": "__resolve_module",
Expand Down
8 changes: 4 additions & 4 deletions crates/client/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ fn wasm_command_mounts() -> Vec<MountConfig> {

/// Locate the materialized coreutils package under the in-repo registry build.
pub fn coreutils_package_dir() -> Option<PathBuf> {
let registry_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../registry/software/coreutils/dist/package");
let registry_dir =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../software/coreutils/dist/package");
if registry_dir.join("agentos-package.json").is_file() {
return std::fs::canonicalize(registry_dir).ok();
}
Expand All @@ -175,7 +175,7 @@ pub fn coreutils_wasm_dir() -> Option<PathBuf> {
}

let legacy_registry_dir =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../registry/software/coreutils/wasm");
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../software/coreutils/wasm");
if legacy_registry_dir.is_dir() {
return std::fs::canonicalize(legacy_registry_dir).ok();
}
Expand Down Expand Up @@ -244,6 +244,6 @@ pub async fn require_wasm_commands(os: &AgentOs, test_name: &str) -> bool {
eprintln!("skipping {message}");
false
} else {
panic!("{message}; run the registry/native command build or set AGENT_OS_CLIENT_ALLOW_E2E_SKIPS=1 for local skip-only runs");
panic!("{message}; run the toolchain command build or set AGENT_OS_CLIENT_ALLOW_E2E_SKIPS=1 for local skip-only runs");
}
}
2 changes: 1 addition & 1 deletion crates/client/tests/packages_aospkg_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mod common;
fn coreutils_aospkg() -> Option<PathBuf> {
for path in [
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../registry/software/coreutils/dist/package.aospkg"),
.join("../../software/coreutils/dist/package.aospkg"),
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../node_modules/@agentos-software/coreutils/dist/package.aospkg"),
] {
Expand Down
6 changes: 3 additions & 3 deletions crates/client/tests/pi_session_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//! - `AGENT_OS_PI_MODULE_CWD` env (a workspace with a built/installed `@agentos-software/pi`), else
//! - the repo root, but only when the in-repo adapter is built
//! (`node_modules/@agentos-software/pi/dist/adapter.js`). Build it with `pnpm --dir packages/core
//! build && pnpm --dir registry/agent/pi build` (core first for types).
//! build && pnpm --dir software/pi build` (core first for types).
//!
//! Background: a real agent SDK exercises module-loading patterns (tsc `__exportStar` CJS barrels,
//! deep pnpm symlink graphs, `__dirname` package self-location) that mock ACP adapters never touch.
Expand Down Expand Up @@ -55,11 +55,11 @@ fn pi_module_cwd() -> Option<String> {

fn pi_package_path() -> Option<PathBuf> {
// Prefer the packed .aospkg — the artifact the registry actually ships.
let aospkg = repo_root().join("registry/agent/pi/dist/package.aospkg");
let aospkg = repo_root().join("software/pi/dist/package.aospkg");
if aospkg.is_file() {
return std::fs::canonicalize(aospkg).ok();
}
let dir = repo_root().join("registry/agent/pi/dist/package");
let dir = repo_root().join("software/pi/dist/package");
if dir.join("agentos-package.json").is_file() {
std::fs::canonicalize(dir).ok()
} else {
Expand Down
5 changes: 2 additions & 3 deletions crates/client/tests/shell_pty_packages_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn coreutils_package_path() -> Option<PathBuf> {
// Prefer the packed .aospkg — the artifact registry packages actually ship.
for aospkg in [
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../registry/software/coreutils/dist/package.aospkg"),
.join("../../software/coreutils/dist/package.aospkg"),
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../node_modules/@agentos-software/coreutils/dist/package.aospkg"),
] {
Expand All @@ -26,8 +26,7 @@ fn coreutils_package_path() -> Option<PathBuf> {
}
}
for dir in [
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../registry/software/coreutils/dist/package"),
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../software/coreutils/dist/package"),
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../node_modules/@agentos-software/coreutils/dist/package"),
] {
Expand Down
2 changes: 1 addition & 1 deletion crates/execution/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ ESM loader hooks (`loader.mjs`) and CJS `Module._load` patches (`runner.mjs`) ar
- Node-facing `readdir` results must filter `.`/`..`.
- Async methods should dispatch under `fs.promises.*`.
- `fs.promises` methods that need real concurrency must use dedicated async bridge globals in `packages/build-tools/bridge-src/`; wrapping `fs.*Sync` inside `async` functions still serializes `Promise.all(...)` behind the first sidecar response.
- When adding WASI guest imports in `registry/native/crates/wasi-ext`, mirror the required module/object in `crates/execution/src/node_import_cache.rs`'s inline `NODE_WASM_RUNNER_SOURCE`; missing modules fail at `WebAssembly.instantiate()` before guest `main()` runs.
- When adding WASI guest imports in `toolchain/crates/wasi-ext`, mirror the required module/object in `crates/execution/src/node_import_cache.rs`'s inline `NODE_WASM_RUNNER_SOURCE`; missing modules fail at `WebAssembly.instantiate()` before guest `main()` runs.
- Keep the embedded WASI shim in `crates/execution/src/wasm.rs` aligned with the patched wasi-libc surface used by the C command suite; overrides like `fcntl(F_SETFL)` now depend on `fd_fdstat_set_flags`, and missing imports fail at instantiation time before the guest command can do real work.
- The shared-V8 WASM runner now resolves its own module loads plus internal guest `fs.openSync` / `fs.readSync` / `fs.writeSync` / `fs.closeSync` traffic inside `crates/execution/src/wasm.rs`; if the embedded runner gains more internal file syscalls, extend that internal sync-RPC handling there instead of surfacing those requests to callers or reintroducing a host-Node runtime path.
- fd-based APIs (`open`, `read`, `write`, `close`, `fstat`) plus `createReadStream`/`createWriteStream` should ride the same bridge.
Expand Down
Loading