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
1 change: 1 addition & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion crates/execution/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ The host-support ESM loader hook (`loader.mjs`) is generated from a Rust string
- Warmup marker fingerprints for guest assets must include mutation data (`size` plus `mtime`/`mtime_nsec`), not just inode identity; in-place rewrites of Pyodide or WASM assets can preserve the inode and still need to invalidate prewarm stamps.
- Pyodide bootstrap hardening in `node_import_cache.rs` must stay staged: `globalThis` guards can go in before `loadPyodide()`, but mutating `process` before `loadPyodide()` breaks the bundled Pyodide runtime under Node `--permission`.
- Python RPC shims in `crates/execution/assets/runners/python-runner.mjs` should translate JS bridge failures into Python-native exceptions (`PermissionError`, `FileNotFoundError`, `OSError`) instead of leaking `JsException`, and Python `subprocess.run()` should inherit the VM cwd from sidecar process state rather than Pyodide's internal `/home/pyodide` working directory.
- Treat bundled Pyodide package loading and user-configured `AGENTOS_PYODIDE_PACKAGE_BASE_URL` as separate phases in `python-runner.mjs`: keep `loadPyodide(... packageBaseUrl)` plus the initial `pyodide.loadPackage("micropip")`/bundled preload path pinned to `/__agentos_pyodide`, then switch `pyodide._api.config.packageBaseUrl` afterward for user `micropip.install(...)` URLs. When guest Python needs HTTP wheels, patch `pyodide.http.pyfetch` through the Python `httpRequestSync` bridge so `micropip` obeys sidecar network policy and loopback exemptions instead of bypassing them.
- Pyodide reads `packageBaseUrl` only in `loadPyodide()`, and it checks its package cache before it downloads. Keep bundled wheels seeded in the package cache, and keep the sidecar's sha256 check on every write into the cache. When guest Python needs HTTP wheels, patch `pyodide.http.pyfetch` through the Python `httpRequestSync` bridge so `micropip` obeys sidecar network policy and loopback exemptions instead of bypassing them.
- Guest runtime identity defaults must stay aligned across JS, WASM, and Python: keep `HOME` bound to the kernel user's homedir, keep `PWD` bound to the execution cwd, feed those values into the Pyodide bootstrap env, and make sure Python `execute()` requests still pass through `prepare_guest_runtime_env(...)` instead of bypassing the shared runtime-env assembly.
- The shared runtime env contract also includes a stable guest `PATH` plus internal-env filtering: `prepare_guest_runtime_env(...)` should supply the canonical guest search path, `python-runner.mjs` should expose that `PATH` inside `os.environ`, and the WASM `AGENTOS_GUEST_ENV` payload must strip internal control vars like `AGENTOS_*` / `NODE_SYNC_RPC_*` before they reach guest-visible WASI env.
- Pyodide `micropip` support must keep guest `js` / `pyodide_js` imports blocked for user Python code while exposing only a narrow internal compat surface to `micropip` and `pyodide.http`; widening that exception re-opens host escape hatches.
Expand Down
1 change: 1 addition & 0 deletions crates/execution/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ flume = "0.11"
nix = { version = "0.29", features = ["fs"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
sha2 = "0.10"
tokio = { version = "1", features = ["rt", "sync", "time"] }
tracing = "0.1"

Expand Down
12 changes: 8 additions & 4 deletions crates/execution/assets/pyodide/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,21 @@ Bundle size as vendored in this directory:
- Offline package wheels: 8,347,517 bytes
- Total: 20,631,138 bytes (19.68 MiB)

`python-runner.mjs` points `indexURL` at this local directory and defaults `packageBaseUrl` to the same bundled asset root so `pyodide.loadPackage()` and the built-in `micropip` bootstrap stay offline.
`python-runner.mjs` points `indexURL` at this local directory.

The offline wheels need no install step: the first `import` of one of their top-level modules (for example `import pandas`) unpacks the wheel and its bundled dependencies.

Dynamic package installs:
- `AGENTOS_PYODIDE_PACKAGE_BASE_URL` can override the package base used by Pyodide package resolution when a Python execution needs to install additional wheels from a network-visible host.
- The bundled `micropip` wheel is still loaded from the local asset directory first so package-manager bootstrap does not depend on external network access.
- Other `pyodide-lock.json` packages (for example Pillow) download from Pyodide's own CDN for the bundled runtime version, `https://cdn.jsdelivr.net/pyodide/v<version>/full/`. Downloads go through the VM network policy, so the VM needs a network grant for that host. `AGENTOS_PYODIDE_PACKAGE_BASE_URL` overrides the base URL, for example with a mirror.
- The sidecar seeds the bundled wheels, including `micropip`, into the Pyodide package cache. Pyodide checks the cache first, so package-manager bootstrap and bundled packages do not depend on external network access.
- The sidecar rejects a write into the package cache unless its bytes match the sha256 that `pyodide-lock.json` pins, so a downloaded wheel is verified before Pyodide installs it.
- `pip install` fails with a non-zero exit when a requested package does not load.
- `await micropip.install("https://.../package.whl")` goes through the Python runner's bridge-backed fetch path, which means network permissions are enforced by the agentos kernel rather than bypassing it.

Debug timing output:
- Set `AGENTOS_PYTHON_WARMUP_DEBUG=1` on a Python execution request to emit `__AGENTOS_PYTHON_WARMUP_METRICS__:` JSON lines on stderr.
- The Rust execution engine emits a `phase:"prewarm"` line that reports whether warmup executed or reused the cached compile-cache path, plus the measured warmup duration in milliseconds.
- `python-runner.mjs` emits a `phase:"startup"` line just before guest code runs, including total startup time, `loadPyodide()` time, package-load time, package count, and whether the source was inline code, a file, or prewarm-only.
- `python-runner.mjs` emits a `phase:"startup"` line just before guest code runs, including total startup time, `loadPyodide()` time, and whether the source was inline code, a file, or prewarm-only.

Startup targets:
- Cold start target: first request in a fresh cache should keep the combined prewarm plus startup path under `3000ms` on commodity hardware.
Expand Down
Loading
Loading