From 6f653ae275e065932031933d568a785e5b5827c4 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Mon, 20 Jul 2026 19:45:31 -0700 Subject: [PATCH] feat(exec): publish standalone language execution packages --- .claude/skills/publish-registry/SKILL.md | 2 +- .claude/skills/release-preview/SKILL.md | 6 +- .claude/skills/release/SKILL.md | 6 +- .gitattributes | 2 +- .github/workflows/ci-nightly.yml | 1 - .github/workflows/ci.yml | 2 - .pnpmfile.cjs | 15 + .secure-exec-local-path | 9 - CLAUDE.md | 16 +- Cargo.lock | 589 ++++- TODO.md | 2 +- biome.json | 2 +- crates/CLAUDE.md | 8 +- .../protocol/agent_os_acp_v1.bare | 10 +- crates/agentos-sidecar-browser/src/wasm.rs | 4 +- crates/agentos-sidecar-core/src/engine.rs | 8 +- crates/agentos-sidecar-core/src/lib.rs | 2 +- crates/agentos-sidecar/CLAUDE.md | 2 +- crates/agentos-sidecar/src/acp/mod.rs | 6 +- crates/agentos-sidecar/src/acp/runtime.rs | 6 +- crates/bridge/Cargo.toml | 2 +- crates/bridge/src/lib.rs | 2 +- crates/bridge/src/queue_tracker.rs | 4 +- crates/build-support/Cargo.toml | 2 +- crates/build-support/v8_bridge_build.rs | 2 +- crates/client/Cargo.toml | 2 +- crates/client/src/agent_os.rs | 50 +- crates/client/src/config.rs | 8 +- crates/client/src/cron.rs | 2 +- crates/client/src/fs.rs | 4 +- crates/client/src/language_execution.rs | 1014 ++++++++ crates/client/src/lib.rs | 9 +- crates/client/src/process.rs | 12 +- crates/client/src/shell.rs | 6 + crates/client/tests/common/mod.rs | 4 +- crates/client/tests/exec_command_line_e2e.rs | 14 +- crates/client/tests/fetch_e2e.rs | 2 +- crates/client/tests/link_software_e2e.rs | 2 +- crates/client/tests/native_root_mount_e2e.rs | 12 +- crates/client/tests/packages_aospkg_e2e.rs | 2 +- crates/client/tests/process_e2e.rs | 8 +- crates/client/tests/wasm_command_mount_e2e.rs | 2 +- crates/execution/CLAUDE.md | 10 +- crates/execution/Cargo.toml | 2 +- crates/execution/assets/pyodide/README.md | 4 +- .../assets/runners/python-runner.mjs | 87 +- .../execution/assets/runners/wasi-module.js | 2 +- .../assets/runners/wasi-module.js.orig | 2 +- .../execution/assets/runners/wasm-runner.mjs | 12 +- .../assets/undici-shims/async_hooks.js | 2 +- crates/execution/assets/undici-shims/http2.js | 2 +- .../execution/assets/undici-shims/stream.js | 4 +- crates/execution/assets/undici-shims/zlib.js | 2 +- .../benchmarks/node-import-baseline.md | 2 +- crates/execution/build.rs | 66 + crates/execution/src/benchmark.rs | 10 +- crates/execution/src/javascript.rs | 87 +- crates/execution/src/lib.rs | 4 +- crates/execution/src/node_import_cache.rs | 54 +- crates/execution/src/python.rs | 18 +- crates/execution/src/v8_ipc.rs | 2 +- crates/execution/src/wasm.rs | 98 +- crates/execution/tests/javascript_v8.rs | 14 +- crates/execution/tests/process.rs | 2 +- crates/kernel/Cargo.toml | 2 +- crates/kernel/src/kernel.rs | 6 +- crates/kernel/src/lib.rs | 2 +- crates/kernel/tests/command_registry.rs | 8 +- crates/kernel/tests/default_deny_guards.rs | 2 +- crates/kernel/tests/identity.rs | 2 +- crates/native-baseline/Cargo.toml | 2 +- crates/native-baseline/src/main.rs | 30 +- crates/native-sidecar-browser/CLAUDE.md | 2 +- crates/native-sidecar-browser/Cargo.toml | 2 +- crates/native-sidecar-browser/src/lib.rs | 2 +- .../native-sidecar-browser/tests/service.rs | 12 +- .../tests/wire_dispatch.rs | 9 +- crates/native-sidecar-core/Cargo.toml | 2 +- crates/native-sidecar-core/src/frames.rs | 5 +- crates/native-sidecar-core/src/identity.rs | 12 +- crates/native-sidecar-core/src/router.rs | 62 + crates/native-sidecar/Cargo.toml | 10 +- .../assets/base-filesystem.json | 10 +- crates/native-sidecar/src/bindings.rs | 2 +- crates/native-sidecar/src/bootstrap.rs | 4 +- crates/native-sidecar/src/crypto_cipher.rs | 2 +- .../src/execution/javascript/rpc.rs | 2 +- crates/native-sidecar/src/execution/launch.rs | 23 +- crates/native-sidecar/src/execution/mod.rs | 6 +- .../src/execution/network/dns.rs | 4 +- .../src/execution/network/udp.rs | 2 +- .../native-sidecar/src/execution/process.rs | 20 + .../src/execution/process_events.rs | 53 +- crates/native-sidecar/src/filesystem.rs | 4 +- .../native-sidecar/src/language_execution.rs | 2072 +++++++++++++++++ crates/native-sidecar/src/lib.rs | 1 + .../src/plugins/google_drive.rs | 2 +- crates/native-sidecar/src/service.rs | 17 +- crates/native-sidecar/src/state.rs | 53 +- crates/native-sidecar/src/stdio.rs | 2 +- crates/native-sidecar/src/vm.rs | 12 +- .../tests/architecture_guards.rs | 10 +- .../tests/builtin_conformance.rs | 24 +- .../native-sidecar/tests/connection_auth.rs | 4 +- crates/native-sidecar/tests/extension.rs | 8 +- crates/native-sidecar/tests/filesystem.rs | 2 +- .../tests/generated_protocol.rs | 4 +- crates/native-sidecar/tests/guest_identity.rs | 35 +- crates/native-sidecar/tests/host_dir.rs | 20 +- .../tests/language_execution.rs | 710 ++++++ .../native-sidecar/tests/posix_path_repro.rs | 2 +- .../native-sidecar/tests/projection_bench.rs | 6 +- .../tests/promisify_module_load.rs | 2 +- crates/native-sidecar/tests/protocol.rs | 2 +- crates/native-sidecar/tests/sandbox_agent.rs | 31 +- crates/native-sidecar/tests/service.rs | 37 +- crates/native-sidecar/tests/support/mod.rs | 2 + .../tests/xfstests_correctness.rs | 33 +- crates/sidecar-client/CLAUDE.md | 2 +- crates/sidecar-client/Cargo.toml | 2 +- crates/sidecar-client/src/lib.rs | 4 +- crates/sidecar-client/src/transport.rs | 2 +- crates/sidecar-client/src/wire.rs | 2 +- crates/sidecar-protocol/Cargo.toml | 2 +- .../protocol/agentos_sidecar_v1.bare | 391 +++- crates/sidecar-protocol/src/lib.rs | 2 +- crates/sidecar-protocol/src/protocol.rs | 494 +++- crates/sidecar-protocol/src/wire.rs | 2 +- crates/v8-runtime/Cargo.toml | 2 +- crates/v8-runtime/npm/.gitignore | 4 +- crates/v8-runtime/npm/linux-x64-gnu/README.md | 6 +- crates/v8-runtime/src/bridge.rs | 2 +- crates/v8-runtime/src/embedded_runtime.rs | 2 +- crates/v8-runtime/src/execution.rs | 4 +- crates/v8-runtime/src/runtime_protocol.rs | 4 +- crates/v8-runtime/src/session.rs | 102 +- crates/v8-runtime/src/snapshot.rs | 4 +- crates/v8-runtime/src/stream.rs | 6 +- crates/v8-runtime/src/timeout.rs | 2 +- crates/vfs-store/CLAUDE.md | 2 +- crates/vfs-store/Cargo.toml | 2 +- crates/vfs-store/src/callback_store.rs | 2 +- crates/vfs/CLAUDE.md | 2 +- crates/vfs/assets/base-filesystem.json | 10 +- crates/vfs/src/posix/overlay_fs.rs | 18 +- crates/vfs/src/posix/root_fs.rs | 4 +- crates/vfs/tests/package_format.rs | 2 +- crates/vfs/tests/posix_root_fs.rs | 12 +- crates/vfs/tests/posix_tar_fs.rs | 4 +- crates/vm-config/Cargo.toml | 2 +- docs-internal/kernel-runtime-subsystem-map.md | 2 +- .../registry-flatten-colocation-spec.md | 4 +- docs-internal/registry-parity-worklist.md | 18 +- docs/design/language-execution-api.md | 1446 ++++++++++++ docs/features/typescript.mdx | 220 +- examples/agent-to-agent/client.ts | 4 +- examples/agent-to-agent/package.json | 2 +- examples/agent-to-agent/server.ts | 8 +- examples/agentos-typecheck/src/index.ts | 10 +- examples/approvals/auto-approve-client.ts | 4 +- examples/approvals/client.ts | 6 +- examples/approvals/package.json | 2 +- examples/approvals/selective-client.ts | 6 +- examples/authentication/client.ts | 4 +- examples/authentication/package.json | 2 +- examples/bindings/client.ts | 4 +- examples/bindings/guest.mjs | 14 + examples/bindings/guest.py | 12 + examples/bindings/package.json | 2 +- examples/browser-terminal/README.md | 9 +- examples/browser-terminal/src/ActorView.tsx | 38 +- examples/browserbase/README.md | 2 +- examples/browserbase/client-agent.ts | 6 +- examples/browserbase/client-direct.ts | 2 +- examples/browserbase/client.ts | 8 +- examples/claude/client.ts | 28 +- examples/claude/package.json | 2 +- examples/codex/README.md | 2 +- examples/codex/client.ts | 28 +- examples/codex/package.json | 2 +- examples/core/README.md | 2 +- examples/core/advanced.ts | 2 +- examples/core/config-reference.ts | 2 +- examples/core/hooks.ts | 4 +- examples/core/mounts.ts | 2 +- examples/core/package.json | 2 +- examples/core/vm.ts | 44 +- .../crash-course/agent-to-agent-client.ts | 4 +- .../crash-course/agent-to-agent-server.ts | 8 +- examples/crash-course/cron-client.ts | 4 +- examples/crash-course/filesystem-client.ts | 6 +- examples/crash-course/minimal-client.ts | 6 +- examples/crash-course/multiplayer-client.ts | 4 +- examples/crash-course/networking-client.ts | 2 +- examples/crash-course/package.json | 2 +- examples/crash-course/permissions-client.ts | 6 +- examples/crash-course/processes-client.ts | 4 +- examples/crash-course/sandbox-stubs.d.ts | 2 +- examples/crash-course/sessions-client.ts | 4 +- examples/crash-course/workflows.ts | 8 +- examples/cron/heartbeat.ts | 2 +- examples/cron/list-cancel.ts | 4 +- examples/cron/monitor.ts | 2 +- examples/cron/overlap.ts | 2 +- examples/cron/package.json | 2 +- examples/cron/schedule-command.ts | 2 +- examples/cron/schedule-session.ts | 2 +- examples/custom/package.json | 2 +- examples/debugging/agent-logs.ts | 2 +- examples/debugging/package.json | 2 +- examples/filesystem/host-api.ts | 4 +- examples/filesystem/isolation.ts | 8 +- examples/filesystem/mount-custom-vfs.ts | 6 +- examples/filesystem/operations.ts | 24 +- examples/filesystem/package.json | 2 +- examples/js-ai-agent-code-exec/README.md | 19 + examples/js-ai-agent-code-exec/package.json | 17 + examples/js-ai-agent-code-exec/src/index.ts | 28 + examples/js-ai-agent-code-exec/tsconfig.json | 9 + examples/js-child-processes/package.json | 17 + examples/js-child-processes/src/index.ts | 35 + examples/js-child-processes/tsconfig.json | 9 + examples/js-code-mode/README.md | 19 + examples/js-code-mode/package.json | 18 + examples/js-code-mode/src/index.ts | 61 + examples/js-code-mode/tsconfig.json | 9 + examples/js-dev-servers/README.md | 19 + examples/js-dev-servers/package.json | 17 + examples/js-dev-servers/src/index.ts | 43 + examples/js-dev-servers/tsconfig.json | 9 + examples/js-filesystem/host-data/greeting.txt | 1 + examples/js-filesystem/package.json | 17 + examples/js-filesystem/src/index.ts | 77 + examples/js-filesystem/tsconfig.json | 9 + examples/js-module-loading/package.json | 17 + examples/js-module-loading/src/index.ts | 89 + examples/js-module-loading/tsconfig.json | 9 + examples/js-networking-wasm/package.json | 17 + examples/js-networking-wasm/src/index.ts | 15 + examples/js-networking-wasm/tsconfig.json | 9 + examples/js-networking/package.json | 17 + examples/js-networking/src/index.ts | 144 ++ examples/js-networking/tsconfig.json | 9 + examples/js-output-capture/package.json | 17 + examples/js-output-capture/src/index.ts | 17 + examples/js-output-capture/tsconfig.json | 9 + examples/js-permissions/package.json | 17 + examples/js-permissions/src/index.ts | 32 + examples/js-permissions/tsconfig.json | 9 + examples/js-plugin-systems/README.md | 19 + examples/js-plugin-systems/package.json | 17 + examples/js-plugin-systems/src/index.ts | 39 + examples/js-plugin-systems/tsconfig.json | 9 + examples/js-process-isolation/package.json | 17 + examples/js-process-isolation/src/index.ts | 36 + examples/js-process-isolation/tsconfig.json | 9 + examples/js-quickstart/package.json | 17 + examples/js-quickstart/src/index.ts | 38 + examples/js-quickstart/tsconfig.json | 9 + examples/js-resource-limits/package.json | 17 + examples/js-resource-limits/src/index.ts | 39 + examples/js-resource-limits/tsconfig.json | 9 + examples/js-runtime-platform/package.json | 17 + examples/js-runtime-platform/src/index.ts | 24 + examples/js-runtime-platform/tsconfig.json | 9 + examples/js-runtime/package.json | 2 +- examples/js-sdk-overview/package.json | 17 + examples/js-sdk-overview/src/index.ts | 28 + examples/js-sdk-overview/src/lifecycle.ts | 20 + examples/js-sdk-overview/src/npm.ts | 17 + examples/js-sdk-overview/tsconfig.json | 9 + examples/js-typescript/package.json | 17 + examples/js-typescript/src/index.ts | 33 + examples/js-typescript/tsconfig.json | 9 + examples/js-virtual-filesystem/package.json | 17 + examples/js-virtual-filesystem/src/index.ts | 23 + examples/js-virtual-filesystem/tsconfig.json | 9 + examples/llm-credentials/client.ts | 2 +- examples/llm-credentials/package.json | 2 +- examples/llm-credentials/per-tenant.ts | 2 +- examples/multiplayer/collaborative.ts | 4 +- examples/multiplayer/observe-session.ts | 4 +- examples/multiplayer/package.json | 2 +- examples/multiplayer/shared-output.ts | 4 +- examples/networking/README.md | 2 +- examples/networking/client-fetch-options.ts | 2 +- examples/networking/client-fetch.ts | 2 +- examples/networking/client-preview.ts | 2 +- examples/networking/client-run-server.ts | 4 +- examples/networking/package.json | 2 +- examples/opencode/README.md | 2 +- examples/opencode/client.ts | 28 +- examples/opencode/package.json | 2 +- examples/permissions/combine.ts | 2 +- examples/permissions/fs-rules.ts | 2 +- examples/permissions/package.json | 2 +- examples/permissions/server.ts | 2 +- examples/persistence/lifecycle-client.ts | 2 +- examples/persistence/package.json | 2 +- examples/persistence/restore-filesystem.ts | 2 +- examples/pi/client.ts | 38 +- examples/pi/package.json | 2 +- examples/processes/exec.ts | 4 +- examples/processes/lifecycle.ts | 10 +- examples/processes/package.json | 2 +- examples/processes/process-events.ts | 2 +- examples/processes/shell-events.ts | 2 +- examples/processes/shell.ts | 8 +- examples/processes/spawn.ts | 2 +- examples/processes/stdin.ts | 8 +- examples/processes/visibility.ts | 4 +- examples/python-quickstart/package.json | 17 + examples/python-quickstart/src/index.ts | 12 + examples/python-quickstart/tsconfig.json | 9 + examples/python-sdk-overview/package.json | 17 + examples/python-sdk-overview/src/index.ts | 29 + examples/python-sdk-overview/src/lifecycle.ts | 20 + examples/python-sdk-overview/src/packages.ts | 15 + examples/python-sdk-overview/src/workflows.ts | 23 + examples/python-sdk-overview/tsconfig.json | 9 + examples/quickstart-app/Agent.tsx | 6 +- examples/quickstart-app/client.ts | 6 +- examples/quickstart-app/package.json | 2 +- examples/quickstart/agent-session/index.ts | 10 +- .../quickstart/agent-session/package.json | 2 +- examples/quickstart/bash/README.md | 4 +- examples/quickstart/bash/index.ts | 16 +- examples/quickstart/bash/package.json | 2 +- examples/quickstart/bindings/index.ts | 6 +- examples/quickstart/bindings/package.json | 2 +- examples/quickstart/cron/index.ts | 10 +- examples/quickstart/cron/package.json | 2 +- examples/quickstart/filesystem/index.ts | 30 +- examples/quickstart/filesystem/package.json | 2 +- examples/quickstart/git/README.md | 2 +- examples/quickstart/git/index.ts | 14 +- examples/quickstart/git/package.json | 2 +- examples/quickstart/hello-world/README.md | 2 +- examples/quickstart/hello-world/index.ts | 6 +- examples/quickstart/hello-world/package.json | 2 +- examples/quickstart/network/README.md | 6 +- examples/quickstart/network/index.ts | 16 +- examples/quickstart/network/package.json | 2 +- examples/quickstart/nodejs/README.md | 4 +- examples/quickstart/nodejs/index.ts | 6 +- examples/quickstart/nodejs/package.json | 2 +- examples/quickstart/package.json | 2 +- examples/quickstart/pi-extensions/index.ts | 18 +- .../quickstart/pi-extensions/package.json | 2 +- examples/quickstart/processes/README.md | 2 +- examples/quickstart/processes/index.ts | 20 +- examples/quickstart/processes/package.json | 2 +- examples/quickstart/python/index.ts | 34 + examples/quickstart/python/package.json | 18 + examples/quickstart/python/tsconfig.json | 11 + examples/quickstart/s3-filesystem/README.md | 2 +- examples/quickstart/s3-filesystem/index.ts | 12 +- .../quickstart/s3-filesystem/package.json | 2 +- examples/quickstart/sandbox/index.ts | 18 +- examples/quickstart/sandbox/package.json | 2 +- examples/resource-limits/package.json | 2 +- examples/sandbox/README.md | 2 +- examples/sandbox/client.ts | 8 +- examples/sandbox/package.json | 2 +- examples/sessions/cancel-prompt.ts | 6 +- examples/sessions/client.ts | 50 +- examples/sessions/close-destroy.ts | 6 +- examples/sessions/create-session.ts | 2 +- examples/sessions/multiple-sessions.ts | 12 +- examples/sessions/package.json | 2 +- examples/sessions/runtime-config.ts | 6 +- examples/sessions/send-prompt.ts | 4 +- examples/sessions/stream-responses.ts | 4 +- examples/software/client.ts | 2 +- examples/software/package.json | 2 +- examples/software/quickstart-agent/index.ts | 2 +- examples/webhooks/README.md | 25 - examples/webhooks/package.json | 2 +- examples/webhooks/server.ts | 4 +- examples/workflows/package.json | 2 +- examples/workflows/server.ts | 16 +- experiments/gigacode/GOOSE_MIGRATION_PLAN.md | 6 +- package.json | 2 +- packages/CLAUDE.md | 21 +- packages/agentos-toolchain/README.md | 6 +- packages/agentos/package.json | 2 +- packages/agentos/src/actor.ts | 819 ++++++- packages/agentos/src/index.ts | 24 +- .../src/inspector-tabs/lib/actor-client.ts | 8 +- .../agentos/src/inspector-tabs/lib/source.ts | 3 +- .../agentos/src/type-tests/nested-actions.ts | 37 + packages/agentos/src/types.ts | 43 + packages/agentos/tests/actor.test.ts | 187 +- .../tests/agent-os-conformance.e2e.test.ts | 7 +- packages/agentos/tsup.config.ts | 4 +- packages/browser/scripts/build-dist-wasm.mjs | 2 +- .../browser/scripts/build-sidecar-wasm.mjs | 2 +- .../scripts/build-wasm-test-assets.mjs | 14 +- .../browser/scripts/check-converged-gates.mjs | 4 +- packages/browser/scripts/verify-demo.mjs | 2 +- .../src/converged-execution-host-bridge.ts | 2 +- packages/browser/src/converged-sidecar.ts | 2 +- .../tests/browser-wasm/agent-demo.html | 2 +- .../browser/tests/browser-wasm/boot.spec.ts | 2 +- .../browser/tests/browser-wasm/pi-demo.html | 2 +- packages/browser/tests/browser-wasm/serve.mjs | 2 +- packages/browser/vite.config.mts | 4 +- packages/build-tools/README.md | 2 +- .../bridge-src/builtins/buffer-runtime.ts | 4 +- .../bridge-src/builtins/builtin-modules.ts | 10 +- .../bridge-src/builtins/console.ts | 2 +- .../build-tools/bridge-src/builtins/crypto.ts | 8 +- .../build-tools/bridge-src/builtins/dgram.ts | 2 +- .../build-tools/bridge-src/builtins/dns.ts | 12 +- .../build-tools/bridge-src/builtins/fetch.ts | 6 +- .../build-tools/bridge-src/builtins/http.ts | 16 +- .../build-tools/bridge-src/builtins/http2.ts | 8 +- .../bridge-src/builtins/misc-stubs.ts | 6 +- .../bridge-src/builtins/module-loader.ts | 8 +- .../build-tools/bridge-src/builtins/net.ts | 10 +- .../bridge-src/builtins/network.ts | 2 +- .../build-tools/bridge-src/builtins/os.ts | 6 +- .../bridge-src/builtins/process.ts | 6 +- .../build-tools/bridge-src/builtins/timers.ts | 2 +- .../build-tools/bridge-src/builtins/tls.ts | 4 +- .../build-tools/bridge-src/builtins/undici.ts | 16 +- .../build-tools/bridge-src/builtins/vm.ts | 6 +- .../bridge-src/builtins/whatwg-url.ts | 10 +- .../build-tools/bridge-src/global-exposure.ts | 6 +- .../build-tools/bridge-src/polyfills/index.ts | 4 +- packages/build-tools/bridge-src/transport.ts | 2 +- .../scripts/build-base-filesystem.mjs | 16 +- .../build-tools/scripts/build-v8-bridge.mjs | 32 +- packages/core/AGENTS.md | 12 +- packages/core/CLAUDE.md | 12 +- packages/core/README.md | 211 +- packages/core/package.json | 5 + packages/core/pnpm-lock.yaml | 50 +- packages/core/src/agent-os.ts | 1820 ++++++++++++++- packages/core/src/agentos-package.ts | 2 +- packages/core/src/base-filesystem.ts | 4 +- packages/core/src/code-execution.ts | 216 ++ packages/core/src/index.ts | 6 +- packages/core/src/language-execution.ts | 216 ++ packages/core/src/options-schema.ts | 2 +- packages/core/src/packages.ts | 2 +- packages/core/src/runtime-compat.ts | 2 +- packages/core/src/sidecar/agentos-protocol.ts | 10 +- packages/core/src/sidecar/rpc-client.ts | 18 +- .../core/tests/agent-os-conformance.test.ts | 55 +- .../agentos-package-real-agent.e2e.test.ts | 4 +- packages/core/tests/agentos-package.test.ts | 2 +- .../core/tests/allowed-node-builtins.test.ts | 2 +- .../core/tests/codex-fullturn.nightly.test.ts | 2 +- packages/core/tests/fixtures/pty/pty_probe.c | 4 +- .../tests/fs-native-parity.nightly.test.ts | 16 +- .../tests/helpers/fixture-node-modules.ts | 4 +- .../core/tests/helpers/node-modules-mount.ts | 2 +- packages/core/tests/leak-rpc-client.test.ts | 37 +- .../tests/opencode-session.nightly.test.ts | 2 +- packages/core/tests/options-schema.test.ts | 13 + .../core/tests/pty-protocol.nightly.test.ts | 8 +- .../core/tests/python-cli.nightly.test.ts | 2 +- packages/core/tests/spawn-flat-api.test.ts | 35 +- .../tests/vim-interactive.nightly.test.ts | 2 +- .../tests/vim-native-parity.nightly.test.ts | 2 +- .../core/tests/vim-provides.nightly.test.ts | 2 +- .../core/tests/vim-render.nightly.test.ts | 4 +- .../core/tests/wasm-commands.nightly.test.ts | 4 +- .../core/tests/wasm-permission-tiers.test.ts | 4 +- packages/core/type-tests/nested-api.ts | 106 + packages/javascript/README.md | 17 + .../{secure-exec => javascript}/package.json | 19 +- packages/javascript/src/index.ts | 258 ++ .../javascript/src/type-tests/public-api.ts | 21 + packages/javascript/tsconfig.json | 10 + packages/javascript/tsup.config.ts | 8 + packages/manifest/agentos-package.schema.json | 2 +- packages/playground/agentos-worker.js | 16 +- .../playground/frontend/runtime-harness.ts | 8 +- packages/python/README.md | 17 + packages/python/package.json | 37 +- packages/python/src/index.ts | 147 ++ packages/python/src/type-tests/public-api.ts | 17 + packages/python/tsconfig.json | 10 + packages/python/tsup.config.ts | 8 + packages/runtime-benchmarks/README.md | 6 +- packages/runtime-benchmarks/bench-utils.ts | 2 +- .../runtime-benchmarks/coldstart.bench.ts | 359 +-- packages/runtime-benchmarks/coldstart.log | 2 +- packages/runtime-benchmarks/memory.bench.ts | 4 +- packages/runtime-benchmarks/package.json | 2 + .../results/baseline-ci.json | 2 +- .../results/baseline-local.json | 2 +- .../results/coldstart-final.log | 4 +- ...ldstart-resident-full-matrix-20260619.json | 2 +- ...oldstart-resident-full-matrix-20260619.log | 2 +- .../src/families/ecosystem.ts | 36 +- .../src/families/modules.ts | 2 +- .../src/families/perf-findings.ts | 4 +- .../src/focused/concurrency-common.ts | 2 +- .../src/focused/echo.bench.ts | 2 +- .../src/focused/overlay-readdir.bench.ts | 4 +- .../src/focused/process-spawn.bench.ts | 4 +- packages/runtime-benchmarks/src/lib/layers.ts | 6 +- packages/runtime-browser/AGENTS.md | 4 +- packages/runtime-browser/CLAUDE.md | 4 +- packages/runtime-browser/README.md | 4 +- packages/runtime-browser/package.json | 2 +- .../scripts/build-browser-test-assets.mjs | 12 +- .../scripts/serve-browser-tests.mjs | 18 +- .../src/converged-executor-session.ts | 4 +- packages/runtime-browser/src/driver.ts | 2 +- packages/runtime-browser/src/runtime.ts | 12 +- .../src/wasi-command-bootstrap.ts | 4 +- packages/runtime-browser/src/wasi-polyfill.ts | 2 +- packages/runtime-browser/src/worker.ts | 16 +- .../converged-conformance-harness.entry.ts | 12 +- .../converged-conformance-harness.html | 4 +- .../fixtures/frontend/converged-harness.html | 4 +- .../converged-runtime-harness.entry.ts | 2 +- .../frontend/converged-runtime-harness.html | 4 +- .../tests/browser/harness.smoke.spec.ts | 2 +- .../runtime-browser/tests/browser/harness.ts | 22 +- .../tests/browser/runtime-driver.spec.ts | 16 +- .../tests/runtime/resolve-module.test.ts | 2 +- .../runtime/wasi-command-bootstrap.test.ts | 2 +- .../tests/runtime/wasi-command-host.test.ts | 2 +- packages/runtime-core/README.md | 2 +- packages/runtime-core/src/event-buffer.ts | 65 +- .../runtime-core/src/generated-protocol.ts | 1863 ++++++++++++++- packages/runtime-core/src/kernel-proxy.ts | 2 +- packages/runtime-core/src/node-runtime.ts | 10 +- packages/runtime-core/src/request-payloads.ts | 135 ++ .../runtime-core/src/response-payloads.ts | 39 + packages/runtime-core/src/sidecar-process.ts | 34 +- packages/runtime-core/src/test-runtime.ts | 6 +- .../bridge-child-process.nightly.test.ts | 6 +- .../projects/developer-clis-pass/src/index.js | 4 +- .../projects/rivetkit/.pnpmfile.cjs | 12 + .../projects/rivetkit/package.json | 2 +- .../projects/rivetkit/pnpm-lock.yaml | 1881 +-------------- .../runtime-core/tests/native-client.test.ts | 4 +- .../tests/node-runtime-decode.test.ts | 2 +- .../npm/darwin-arm64/package.json | 4 +- .../npm/darwin-x64/package.json | 4 +- .../npm/linux-arm64-gnu/package.json | 4 +- .../npm/linux-x64-gnu/package.json | 4 +- packages/runtime-sidecar/package.json | 4 +- .../package.json | 22 - .../scripts/verify-docs.mjs | 78 - .../src/index.ts | 130 -- .../tsconfig.json | 18 - packages/secure-exec/README.md | 6 - packages/secure-exec/src/index.ts | 57 - packages/secure-exec/tests/public-api.test.ts | 124 - .../secure-exec/tests/quickstart-smoke.ts | 25 - .../tests/tsconfig.quickstart.json | 9 - packages/secure-exec/tsconfig.json | 19 - packages/typescript/README.md | 6 +- packages/typescript/package.json | 5 +- packages/typescript/src/index.ts | 116 +- packages/typescript/tests/quickstart-smoke.ts | 2 +- ...pescript-tools.integration.nightly.test.ts | 4 +- packages/typescript/tsconfig.json | 2 +- packages/typescript/vitest.config.ts | 2 +- pnpm-lock.yaml | 1393 +++++++---- pnpm-workspace.yaml | 11 +- scripts/benchmarks/README.md | 6 +- scripts/benchmarks/session.bench.ts | 8 +- .../check-agentos-client-protocol-compat.mjs | 14 +- ...ck-agentos-client-protocol-compat.test.mjs | 58 +- scripts/check-rust-package-metadata.test.mjs | 4 +- scripts/ci.sh | 2 +- scripts/generate-secure-exec-mirror.mjs | 395 ---- scripts/generate-secure-exec-mirror.test.mjs | 83 - scripts/publish/src/lib/artifacts.test.ts | 17 - scripts/publish/src/lib/packages.test.ts | 4 + scripts/publish/src/lib/packages.ts | 4 +- scripts/publish/src/lib/version.test.ts | 2 + scripts/secure-exec-agentos-map.json | 320 --- software/README.md | 2 +- software/browserbase/package.json | 2 +- software/build-essential/package.json | 2 +- .../native/crates/cmd-codex-exec/Cargo.toml | 4 +- .../native/crates/cmd-codex-exec/src/main.rs | 2 +- .../native/crates/cmd-codex/Cargo.toml | 6 +- .../native/crates/cmd-codex/src/main.rs | 4 +- software/codex-cli/package.json | 2 +- software/common/package.json | 2 +- .../native/crates/cmd-arch/Cargo.toml | 2 +- .../native/crates/cmd-b2sum/Cargo.toml | 2 +- .../native/crates/cmd-base32/Cargo.toml | 2 +- .../native/crates/cmd-base64/Cargo.toml | 2 +- .../native/crates/cmd-basename/Cargo.toml | 2 +- .../native/crates/cmd-basenc/Cargo.toml | 2 +- .../native/crates/cmd-cat/Cargo.toml | 2 +- .../native/crates/cmd-chmod/Cargo.toml | 2 +- .../native/crates/cmd-cksum/Cargo.toml | 2 +- .../native/crates/cmd-column/Cargo.toml | 4 +- .../native/crates/cmd-column/src/main.rs | 2 +- .../native/crates/cmd-comm/Cargo.toml | 2 +- .../coreutils/native/crates/cmd-cp/Cargo.toml | 2 +- .../native/crates/cmd-cut/Cargo.toml | 2 +- .../native/crates/cmd-date/Cargo.toml | 2 +- .../coreutils/native/crates/cmd-dd/Cargo.toml | 2 +- .../native/crates/cmd-dircolors/Cargo.toml | 2 +- .../native/crates/cmd-dirname/Cargo.toml | 2 +- .../coreutils/native/crates/cmd-du/Cargo.toml | 4 +- .../native/crates/cmd-du/src/main.rs | 2 +- .../native/crates/cmd-echo/Cargo.toml | 2 +- .../native/crates/cmd-env/Cargo.toml | 4 +- .../native/crates/cmd-expand/Cargo.toml | 2 +- .../native/crates/cmd-expr/Cargo.toml | 4 +- .../native/crates/cmd-expr/src/main.rs | 2 +- .../native/crates/cmd-factor/Cargo.toml | 2 +- .../native/crates/cmd-false/Cargo.toml | 2 +- .../native/crates/cmd-fmt/Cargo.toml | 2 +- .../native/crates/cmd-fold/Cargo.toml | 2 +- .../native/crates/cmd-head/Cargo.toml | 2 +- .../native/crates/cmd-join/Cargo.toml | 2 +- .../native/crates/cmd-link/Cargo.toml | 2 +- .../coreutils/native/crates/cmd-ln/Cargo.toml | 2 +- .../native/crates/cmd-logname/Cargo.toml | 2 +- .../coreutils/native/crates/cmd-ls/Cargo.toml | 2 +- .../native/crates/cmd-md5sum/Cargo.toml | 2 +- .../native/crates/cmd-mkdir/Cargo.toml | 2 +- .../native/crates/cmd-mktemp/Cargo.toml | 2 +- .../coreutils/native/crates/cmd-mv/Cargo.toml | 2 +- .../native/crates/cmd-nice/Cargo.toml | 4 +- .../coreutils/native/crates/cmd-nl/Cargo.toml | 2 +- .../native/crates/cmd-nohup/Cargo.toml | 4 +- .../native/crates/cmd-nproc/Cargo.toml | 2 +- .../native/crates/cmd-numfmt/Cargo.toml | 2 +- .../coreutils/native/crates/cmd-od/Cargo.toml | 2 +- .../native/crates/cmd-paste/Cargo.toml | 2 +- .../native/crates/cmd-pathchk/Cargo.toml | 2 +- .../native/crates/cmd-printenv/Cargo.toml | 2 +- .../native/crates/cmd-printf/Cargo.toml | 2 +- .../native/crates/cmd-ptx/Cargo.toml | 2 +- .../native/crates/cmd-pwd/Cargo.toml | 2 +- .../native/crates/cmd-readlink/Cargo.toml | 2 +- .../native/crates/cmd-realpath/Cargo.toml | 2 +- .../native/crates/cmd-rev/Cargo.toml | 4 +- .../native/crates/cmd-rev/src/main.rs | 2 +- .../coreutils/native/crates/cmd-rm/Cargo.toml | 2 +- .../native/crates/cmd-rmdir/Cargo.toml | 2 +- .../native/crates/cmd-seq/Cargo.toml | 2 +- .../coreutils/native/crates/cmd-sh/Cargo.toml | 2 +- .../native/crates/cmd-sha1sum/Cargo.toml | 2 +- .../native/crates/cmd-sha224sum/Cargo.toml | 2 +- .../native/crates/cmd-sha256sum/Cargo.toml | 2 +- .../native/crates/cmd-sha384sum/Cargo.toml | 2 +- .../native/crates/cmd-sha512sum/Cargo.toml | 2 +- .../native/crates/cmd-shred/Cargo.toml | 2 +- .../native/crates/cmd-shuf/Cargo.toml | 2 +- .../native/crates/cmd-sleep/Cargo.toml | 4 +- .../native/crates/cmd-sleep/src/main.rs | 2 +- .../native/crates/cmd-sort/Cargo.toml | 2 +- .../crates/cmd-sort/tests/external_sort.rs | 2 +- .../native/crates/cmd-split/Cargo.toml | 2 +- .../native/crates/cmd-stat/Cargo.toml | 2 +- .../native/crates/cmd-stdbuf/Cargo.toml | 4 +- .../native/crates/cmd-strings/Cargo.toml | 4 +- .../native/crates/cmd-strings/src/main.rs | 2 +- .../native/crates/cmd-sum/Cargo.toml | 2 +- .../native/crates/cmd-tac/Cargo.toml | 2 +- .../native/crates/cmd-tail/Cargo.toml | 2 +- .../native/crates/cmd-tee/Cargo.toml | 2 +- .../native/crates/cmd-test/Cargo.toml | 4 +- .../native/crates/cmd-test/src/main.rs | 2 +- .../native/crates/cmd-timeout/Cargo.toml | 4 +- .../native/crates/cmd-touch/Cargo.toml | 2 +- .../coreutils/native/crates/cmd-tr/Cargo.toml | 2 +- .../native/crates/cmd-true/Cargo.toml | 2 +- .../native/crates/cmd-truncate/Cargo.toml | 2 +- .../native/crates/cmd-tsort/Cargo.toml | 2 +- .../native/crates/cmd-uname/Cargo.toml | 2 +- .../native/crates/cmd-unexpand/Cargo.toml | 2 +- .../native/crates/cmd-uniq/Cargo.toml | 2 +- .../native/crates/cmd-unlink/Cargo.toml | 2 +- .../coreutils/native/crates/cmd-wc/Cargo.toml | 2 +- .../native/crates/cmd-which/Cargo.toml | 4 +- .../crates/cmd-which/tests/executable_mode.rs | 2 +- .../native/crates/cmd-whoami/Cargo.toml | 4 +- .../native/crates/cmd-whoami/src/main.rs | 2 +- .../native/crates/cmd-yes/Cargo.toml | 2 +- .../coreutils/native/crates/column/Cargo.toml | 4 +- .../coreutils/native/crates/du/Cargo.toml | 4 +- .../coreutils/native/crates/expr/Cargo.toml | 4 +- .../coreutils/native/crates/rev/Cargo.toml | 4 +- .../native/crates/strings-cmd/Cargo.toml | 4 +- software/coreutils/package.json | 2 +- .../curl/native/crates/cmd-curl/Cargo.toml | 4 +- software/curl/package.json | 2 +- .../native/crates/cmd-diff/Cargo.toml | 4 +- .../native/crates/cmd-diff/src/main.rs | 2 +- .../diffutils/native/crates/diff/Cargo.toml | 4 +- software/diffutils/package.json | 2 +- software/duckdb/package.json | 2 +- software/everything/package.json | 2 +- software/fd/native/crates/cmd-fd/Cargo.toml | 2 +- software/fd/package.json | 2 +- .../file/native/crates/cmd-file/Cargo.toml | 4 +- .../file/native/crates/cmd-file/src/main.rs | 2 +- .../file/native/crates/file-cmd/Cargo.toml | 4 +- software/file/package.json | 2 +- .../native/crates/cmd-find/Cargo.toml | 2 +- .../native/crates/cmd-xargs/Cargo.toml | 2 +- software/findutils/package.json | 2 +- software/gawk/native/crates/awk/Cargo.toml | 4 +- .../gawk/native/crates/cmd-awk/Cargo.toml | 4 +- .../gawk/native/crates/cmd-awk/src/main.rs | 2 +- software/gawk/package.json | 2 +- software/git/test/git.nightly.test.ts | 4 +- .../grep/native/crates/cmd-egrep/Cargo.toml | 2 +- .../grep/native/crates/cmd-fgrep/Cargo.toml | 2 +- software/grep/package.json | 2 +- .../gzip/native/crates/cmd-gzip/Cargo.toml | 4 +- .../gzip/native/crates/cmd-gzip/src/main.rs | 2 +- software/gzip/native/crates/gzip/Cargo.toml | 4 +- software/gzip/package.json | 2 +- software/jq/native/crates/cmd-jq/Cargo.toml | 4 +- software/jq/native/crates/cmd-jq/src/main.rs | 2 +- software/jq/native/crates/jq/Cargo.toml | 4 +- software/jq/package.json | 2 +- .../ripgrep/native/crates/cmd-rg/Cargo.toml | 2 +- software/ripgrep/package.json | 2 +- software/ripgrep/test/ripgrep.nightly.test.ts | 2 +- software/sed/native/crates/cmd-sed/Cargo.toml | 2 +- software/sed/package.json | 2 +- software/sqlite3/package.json | 2 +- software/tar/native/crates/cmd-tar/Cargo.toml | 4 +- .../tar/native/crates/cmd-tar/src/main.rs | 2 +- software/tar/native/crates/tar/Cargo.toml | 4 +- software/tar/package.json | 2 +- software/tree/package.json | 2 +- software/unzip/package.json | 2 +- software/vim/native/c/vim-bridge/compat.h | 6 +- software/vim/native/c/vim-bridge/termios.h | 2 +- .../vim/native/c/vim-bridge/termios_bridge.c | 2 +- software/vim/package.json | 2 +- software/yq/native/crates/cmd-yq/Cargo.toml | 4 +- software/yq/native/crates/cmd-yq/src/main.rs | 2 +- software/yq/native/crates/yq/Cargo.toml | 4 +- software/yq/package.json | 2 +- software/zip/package.json | 2 +- test-harness/src/agent-os-conformance.ts | 312 +-- tests/fixtures/crypto-basic-conformance.json | 10 +- toolchain/Cargo.lock | 98 +- toolchain/c/scripts/build-curl-upstream.sh | 2 +- toolchain/c/scripts/build-wget-upstream.sh | 4 +- toolchain/crates/commands/_stubs/Cargo.toml | 4 +- toolchain/crates/commands/_stubs/src/main.rs | 2 +- .../crates/commands/http-test/Cargo.toml | 2 +- .../commands/spawn-test-host/Cargo.toml | 2 +- toolchain/crates/commands/xu/Cargo.toml | 2 +- toolchain/crates/libs/builtins/Cargo.toml | 4 +- toolchain/crates/libs/builtins/src/lib.rs | 2 +- toolchain/crates/libs/shims/Cargo.toml | 2 +- toolchain/crates/libs/shims/src/which.rs | 2 +- toolchain/crates/libs/stubs/Cargo.toml | 2 +- toolchain/crates/libs/wasi-http/Cargo.toml | 2 +- toolchain/crates/libs/wasi-pty/Cargo.toml | 4 +- toolchain/crates/libs/wasi-spawn/Cargo.toml | 2 +- .../std-patches/0009-wasi-split-paths.patch | 2 +- toolchain/std-patches/codex-source/README.md | 20 +- .../codex-source/build-codex-wasip1.sh | 6 +- .../0005-wasi-build-sysroot-cleanup.patch | 2 +- .../0001-wasi-empty-certs.patch | 4 +- .../tokio/VERIFIED-tokio-process-wasi.md | 2 +- .../crates/tokio/std-wasi-childpipe-fd.rs | 2 +- toolchain/std-patches/std/os/wasi/process.rs | 4 +- .../std-patches/wasi-libc/0008-sockets.patch | 2 +- .../0013-posix-socket-header-surface.patch | 4 +- .../0029-openssh-compat-header-surface.patch | 2 +- toolchain/stubs/portable-pty-wasi/src/wasi.rs | 2 +- toolchain/stubs/reqwest-shim/Cargo.toml | 4 +- toolchain/stubs/reqwest-shim/src/lib.rs | 2 +- toolchain/test-programs/sqlite3_mem.c | 2 +- website/docs.config.mjs | 62 +- website/public/docs/docs/agent-to-agent.md | 2 +- website/public/docs/docs/agents/claude.md | 6 +- website/public/docs/docs/agents/codex.md | 6 +- website/public/docs/docs/agents/opencode.md | 12 +- website/public/docs/docs/agents/pi.md | 6 +- website/public/docs/docs/architecture.md | 6 +- .../docs/docs/architecture/agent-sessions.md | 2 +- .../docs/architecture/compiler-toolchain.md | 2 +- .../docs/architecture/javascript-executor.md | 2 +- .../packages-and-command-resolution.md | 2 +- .../docs/docs/architecture/processes.md | 10 +- website/public/docs/docs/core.md | 25 +- website/public/docs/docs/cost-evaluation.md | 52 - website/public/docs/docs/crash-course.md | 16 +- website/public/docs/docs/cron.md | 8 +- .../docs/docs/custom-software/definition.md | 2 +- website/public/docs/docs/deployment.md | 2 +- website/public/docs/docs/execution/bash.md | 50 + .../execution/javascript-compatibility.md | 74 + .../public/docs/docs/execution/javascript.md | 108 + website/public/docs/docs/execution/python.md | 81 + .../docs/docs/{ => extensions}/browser.md | 4 +- .../custom-bindings.md} | 6 +- .../{sandbox.md => extensions/sandboxes.md} | 6 +- website/public/docs/docs/filesystem.md | 4 +- website/public/docs/docs/js-runtime.md | 32 - website/public/docs/docs/limitations.md | 4 +- website/public/docs/docs/llm-gateway.md | 12 - ...edentials.md => models-and-credentials.md} | 15 +- website/public/docs/docs/nodejs-runtime.md | 50 - .../docs/{benchmarks.md => performance.md} | 24 +- website/public/docs/docs/python-runtime.md | 62 - website/public/docs/docs/quickstart.md | 11 +- website/public/docs/docs/security-model.md | 4 +- website/public/docs/docs/sessions.md | 14 +- website/public/docs/docs/software.md | 2 +- website/public/docs/docs/versus-sandbox.md | 10 +- website/public/docs/docs/webhooks.md | 21 - website/public/docs/docs/workflows.md | 2 +- website/public/secure-exec-logo-long.svg | 3 - website/public/secure-exec-logo.png | Bin 714803 -> 0 bytes website/public/secure-exec-logo.svg | 8 - website/src/components/GitHubStars.tsx | 2 +- .../marketing/diagrams/AgentSessionDemo.tsx | 4 +- .../marketing/solutions/AgentOSPage.tsx | 17 +- .../src/content/docs/docs/agent-to-agent.mdx | 2 +- .../src/content/docs/docs/agents/claude.mdx | 6 +- .../src/content/docs/docs/agents/codex.mdx | 6 +- .../src/content/docs/docs/agents/opencode.mdx | 12 +- website/src/content/docs/docs/agents/pi.mdx | 7 +- .../src/content/docs/docs/architecture.mdx | 6 +- .../docs/docs/architecture/agent-sessions.mdx | 2 +- .../docs/architecture/compiler-toolchain.mdx | 2 +- .../docs/architecture/javascript-executor.mdx | 2 +- .../packages-and-command-resolution.mdx | 2 +- .../docs/docs/architecture/processes.mdx | 10 +- website/src/content/docs/docs/core.mdx | 25 +- .../src/content/docs/docs/cost-evaluation.mdx | 54 - .../src/content/docs/docs/crash-course.mdx | 16 +- website/src/content/docs/docs/cron.mdx | 8 +- .../docs/docs/custom-software/definition.mdx | 2 +- website/src/content/docs/docs/deployment.mdx | 2 +- .../src/content/docs/docs/execution/bash.mdx | 54 + .../execution/javascript-compatibility.mdx | 77 + .../docs/docs/execution/javascript.mdx | 122 + .../content/docs/docs/execution/python.mdx | 93 + .../docs/docs/{ => extensions}/browser.mdx | 4 +- .../custom-bindings.mdx} | 6 +- .../{sandbox.mdx => extensions/sandboxes.mdx} | 6 +- website/src/content/docs/docs/filesystem.mdx | 4 +- website/src/content/docs/docs/js-runtime.mdx | 36 - website/src/content/docs/docs/limitations.mdx | 5 +- website/src/content/docs/docs/llm-gateway.mdx | 16 - ...entials.mdx => models-and-credentials.mdx} | 15 +- .../src/content/docs/docs/nodejs-runtime.mdx | 52 - .../docs/{benchmarks.mdx => performance.mdx} | 24 +- .../src/content/docs/docs/python-runtime.mdx | 64 - website/src/content/docs/docs/quickstart.mdx | 11 +- .../src/content/docs/docs/security-model.mdx | 4 +- website/src/content/docs/docs/sessions.mdx | 16 +- website/src/content/docs/docs/software.mdx | 2 +- .../src/content/docs/docs/versus-sandbox.mdx | 10 +- website/src/content/docs/docs/webhooks.mdx | 27 - website/src/content/docs/docs/workflows.mdx | 2 +- website/src/data/docs-landings.ts | 11 +- website/src/data/registry.ts | 2 +- website/src/generated/registry.json | 115 +- website/src/generated/routes.json | 84 +- website/src/pages/[...slug].astro | 51 +- website/src/pages/registry/[slug].astro | 2 +- website/tsconfig.typedoc.json | 12 + website/typedoc.json | 8 +- 873 files changed, 19115 insertions(+), 7410 deletions(-) create mode 100644 .pnpmfile.cjs delete mode 100644 .secure-exec-local-path create mode 100644 crates/client/src/language_execution.rs create mode 100644 crates/native-sidecar/src/language_execution.rs create mode 100644 crates/native-sidecar/tests/language_execution.rs create mode 100644 docs/design/language-execution-api.md create mode 100644 examples/bindings/guest.mjs create mode 100644 examples/bindings/guest.py create mode 100644 examples/js-ai-agent-code-exec/README.md create mode 100644 examples/js-ai-agent-code-exec/package.json create mode 100644 examples/js-ai-agent-code-exec/src/index.ts create mode 100644 examples/js-ai-agent-code-exec/tsconfig.json create mode 100644 examples/js-child-processes/package.json create mode 100644 examples/js-child-processes/src/index.ts create mode 100644 examples/js-child-processes/tsconfig.json create mode 100644 examples/js-code-mode/README.md create mode 100644 examples/js-code-mode/package.json create mode 100644 examples/js-code-mode/src/index.ts create mode 100644 examples/js-code-mode/tsconfig.json create mode 100644 examples/js-dev-servers/README.md create mode 100644 examples/js-dev-servers/package.json create mode 100644 examples/js-dev-servers/src/index.ts create mode 100644 examples/js-dev-servers/tsconfig.json create mode 100644 examples/js-filesystem/host-data/greeting.txt create mode 100644 examples/js-filesystem/package.json create mode 100644 examples/js-filesystem/src/index.ts create mode 100644 examples/js-filesystem/tsconfig.json create mode 100644 examples/js-module-loading/package.json create mode 100644 examples/js-module-loading/src/index.ts create mode 100644 examples/js-module-loading/tsconfig.json create mode 100644 examples/js-networking-wasm/package.json create mode 100644 examples/js-networking-wasm/src/index.ts create mode 100644 examples/js-networking-wasm/tsconfig.json create mode 100644 examples/js-networking/package.json create mode 100644 examples/js-networking/src/index.ts create mode 100644 examples/js-networking/tsconfig.json create mode 100644 examples/js-output-capture/package.json create mode 100644 examples/js-output-capture/src/index.ts create mode 100644 examples/js-output-capture/tsconfig.json create mode 100644 examples/js-permissions/package.json create mode 100644 examples/js-permissions/src/index.ts create mode 100644 examples/js-permissions/tsconfig.json create mode 100644 examples/js-plugin-systems/README.md create mode 100644 examples/js-plugin-systems/package.json create mode 100644 examples/js-plugin-systems/src/index.ts create mode 100644 examples/js-plugin-systems/tsconfig.json create mode 100644 examples/js-process-isolation/package.json create mode 100644 examples/js-process-isolation/src/index.ts create mode 100644 examples/js-process-isolation/tsconfig.json create mode 100644 examples/js-quickstart/package.json create mode 100644 examples/js-quickstart/src/index.ts create mode 100644 examples/js-quickstart/tsconfig.json create mode 100644 examples/js-resource-limits/package.json create mode 100644 examples/js-resource-limits/src/index.ts create mode 100644 examples/js-resource-limits/tsconfig.json create mode 100644 examples/js-runtime-platform/package.json create mode 100644 examples/js-runtime-platform/src/index.ts create mode 100644 examples/js-runtime-platform/tsconfig.json create mode 100644 examples/js-sdk-overview/package.json create mode 100644 examples/js-sdk-overview/src/index.ts create mode 100644 examples/js-sdk-overview/src/lifecycle.ts create mode 100644 examples/js-sdk-overview/src/npm.ts create mode 100644 examples/js-sdk-overview/tsconfig.json create mode 100644 examples/js-typescript/package.json create mode 100644 examples/js-typescript/src/index.ts create mode 100644 examples/js-typescript/tsconfig.json create mode 100644 examples/js-virtual-filesystem/package.json create mode 100644 examples/js-virtual-filesystem/src/index.ts create mode 100644 examples/js-virtual-filesystem/tsconfig.json create mode 100644 examples/python-quickstart/package.json create mode 100644 examples/python-quickstart/src/index.ts create mode 100644 examples/python-quickstart/tsconfig.json create mode 100644 examples/python-sdk-overview/package.json create mode 100644 examples/python-sdk-overview/src/index.ts create mode 100644 examples/python-sdk-overview/src/lifecycle.ts create mode 100644 examples/python-sdk-overview/src/packages.ts create mode 100644 examples/python-sdk-overview/src/workflows.ts create mode 100644 examples/python-sdk-overview/tsconfig.json create mode 100644 examples/quickstart/python/index.ts create mode 100644 examples/quickstart/python/package.json create mode 100644 examples/quickstart/python/tsconfig.json delete mode 100644 examples/webhooks/README.md create mode 100644 packages/agentos/src/type-tests/nested-actions.ts create mode 100644 packages/core/src/code-execution.ts create mode 100644 packages/core/src/language-execution.ts create mode 100644 packages/core/type-tests/nested-api.ts create mode 100644 packages/javascript/README.md rename packages/{secure-exec => javascript}/package.json (58%) create mode 100644 packages/javascript/src/index.ts create mode 100644 packages/javascript/src/type-tests/public-api.ts create mode 100644 packages/javascript/tsconfig.json create mode 100644 packages/javascript/tsup.config.ts create mode 100644 packages/python/README.md create mode 100644 packages/python/src/index.ts create mode 100644 packages/python/src/type-tests/public-api.ts create mode 100644 packages/python/tsconfig.json create mode 100644 packages/python/tsup.config.ts create mode 100644 packages/runtime-core/tests/integration/projects/rivetkit/.pnpmfile.cjs delete mode 100644 packages/secure-exec-example-ai-agent-type-check/package.json delete mode 100644 packages/secure-exec-example-ai-agent-type-check/scripts/verify-docs.mjs delete mode 100644 packages/secure-exec-example-ai-agent-type-check/src/index.ts delete mode 100644 packages/secure-exec-example-ai-agent-type-check/tsconfig.json delete mode 100644 packages/secure-exec/README.md delete mode 100644 packages/secure-exec/src/index.ts delete mode 100644 packages/secure-exec/tests/public-api.test.ts delete mode 100644 packages/secure-exec/tests/quickstart-smoke.ts delete mode 100644 packages/secure-exec/tests/tsconfig.quickstart.json delete mode 100644 packages/secure-exec/tsconfig.json delete mode 100644 scripts/generate-secure-exec-mirror.mjs delete mode 100644 scripts/generate-secure-exec-mirror.test.mjs delete mode 100644 scripts/secure-exec-agentos-map.json delete mode 100644 website/public/docs/docs/cost-evaluation.md create mode 100644 website/public/docs/docs/execution/bash.md create mode 100644 website/public/docs/docs/execution/javascript-compatibility.md create mode 100644 website/public/docs/docs/execution/javascript.md create mode 100644 website/public/docs/docs/execution/python.md rename website/public/docs/docs/{ => extensions}/browser.md (93%) rename website/public/docs/docs/{bindings.md => extensions/custom-bindings.md} (94%) rename website/public/docs/docs/{sandbox.md => extensions/sandboxes.md} (91%) delete mode 100644 website/public/docs/docs/js-runtime.md delete mode 100644 website/public/docs/docs/llm-gateway.md rename website/public/docs/docs/{llm-credentials.md => models-and-credentials.md} (56%) delete mode 100644 website/public/docs/docs/nodejs-runtime.md rename website/public/docs/docs/{benchmarks.md => performance.md} (88%) delete mode 100644 website/public/docs/docs/python-runtime.md delete mode 100644 website/public/docs/docs/webhooks.md delete mode 100644 website/public/secure-exec-logo-long.svg delete mode 100644 website/public/secure-exec-logo.png delete mode 100644 website/public/secure-exec-logo.svg delete mode 100644 website/src/content/docs/docs/cost-evaluation.mdx create mode 100644 website/src/content/docs/docs/execution/bash.mdx create mode 100644 website/src/content/docs/docs/execution/javascript-compatibility.mdx create mode 100644 website/src/content/docs/docs/execution/javascript.mdx create mode 100644 website/src/content/docs/docs/execution/python.mdx rename website/src/content/docs/docs/{ => extensions}/browser.mdx (95%) rename website/src/content/docs/docs/{bindings.mdx => extensions/custom-bindings.mdx} (94%) rename website/src/content/docs/docs/{sandbox.mdx => extensions/sandboxes.mdx} (92%) delete mode 100644 website/src/content/docs/docs/js-runtime.mdx delete mode 100644 website/src/content/docs/docs/llm-gateway.mdx rename website/src/content/docs/docs/{llm-credentials.mdx => models-and-credentials.mdx} (61%) delete mode 100644 website/src/content/docs/docs/nodejs-runtime.mdx rename website/src/content/docs/docs/{benchmarks.mdx => performance.mdx} (88%) delete mode 100644 website/src/content/docs/docs/python-runtime.mdx delete mode 100644 website/src/content/docs/docs/webhooks.mdx create mode 100644 website/tsconfig.typedoc.json diff --git a/.claude/skills/publish-registry/SKILL.md b/.claude/skills/publish-registry/SKILL.md index d7ad81e43e..0f930f130f 100644 --- a/.claude/skills/publish-registry/SKILL.md +++ b/.claude/skills/publish-registry/SKILL.md @@ -29,7 +29,7 @@ just registry-publish-all [tag] ## Rules -- Do not publish registry packages from secure-exec. +- Do not publish registry packages from agentos. - Do not move `latest` unless the user explicitly asks for a release. - Prefer AgentOS workspace builds while iterating; published pins are for consumers and release validation. diff --git a/.claude/skills/release-preview/SKILL.md b/.claude/skills/release-preview/SKILL.md index 64738cb814..1af13060c4 100644 --- a/.claude/skills/release-preview/SKILL.md +++ b/.claude/skills/release-preview/SKILL.md @@ -9,8 +9,8 @@ AgentOS previews are branch snapshots from this repository. The workflow builds debug artifacts and publishes npm packages under a sanitized branch dist-tag. No crates.io release, git tag, or GitHub release is created. -secure-exec is a generated mirror. Do not bump a secure-exec ref or cut a -secure-exec preview by hand. +agentos is a generated mirror. Do not bump a agentos ref or cut a +agentos preview by hand. ## Procedure @@ -32,6 +32,6 @@ npm install @rivet-dev/agentos-core@ ## Rules - Release-preview is for previews only; releases use the `release` skill. -- All code changes belong in AgentOS. The secure-exec mirror is regenerated +- All code changes belong in AgentOS. The agentos mirror is regenerated from AgentOS and follows the published AgentOS version. - On failure: `gh run view --log-failed`, fix, re-dispatch, re-watch. diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index 056ac1ab2a..f1291a38bc 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -10,8 +10,8 @@ The publish flow lives in `scripts/publish` and is driven by AgentOS is the source of truth. It publishes npm packages, crates, runtime sidecars, Pyodide/R2 assets, and `@agentos-software/*` registry packages from -this repository. secure-exec releases are generated compatibility shims that -follow the AgentOS version; never release secure-exec first. +this repository. agentos releases are generated compatibility shims that +follow the AgentOS version; never release agentos first. ## Procedure @@ -37,6 +37,6 @@ gh run watch -R rivet-dev/agentos "$run" --exit-status - `scripts/publish/src/local/cut-release.ts` is a pure trigger; version changes happen in the ephemeral CI checkout. - `workspace:*` deps are rewritten to literal versions by the publish bump pass. -- Generated secure-exec shims are dispatched after AgentOS publishes and must +- Generated agentos shims are dispatched after AgentOS publishes and must use the same version. - If anything fails, stop and report — do not retry automatically. diff --git a/.gitattributes b/.gitattributes index 6fae589b06..e575d5386f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,7 +2,7 @@ # # The Rust kernel is the substance of this repo; the rest is the TypeScript # client/protocol/registry plus a large test suite (the kernel itself lives in -# the secure-exec repo). We count Rust in full, plus the core client surface +# the agentos repo). We count Rust in full, plus the core client surface # (the AgentOS client entrypoint + its sidecar wire layer) and the core JS # binary-resolver shim, and exclude everything else (tests, demos, tooling, # generated bundles, the website, and the remaining client/registry code). diff --git a/.github/workflows/ci-nightly.yml b/.github/workflows/ci-nightly.yml index e0931f9eb2..e6f804f213 100644 --- a/.github/workflows/ci-nightly.yml +++ b/.github/workflows/ci-nightly.yml @@ -40,7 +40,6 @@ jobs: sudo apt-get install --yes cmake fi - run: pnpm install --frozen-lockfile - - run: node --test scripts/generate-secure-exec-mirror.test.mjs - run: make -C toolchain commands - run: make -C toolchain cmd/duckdb - run: make -C toolchain codex diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12fcd8be58..d1c026e0f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,7 +72,6 @@ jobs: - run: node scripts/verify-fixed-versions.mjs - run: node --test scripts/check-layout.test.mjs - run: pnpm check-layout - - run: node --test scripts/generate-secure-exec-mirror.test.mjs - run: node --test scripts/check-rustfmt.test.mjs - run: node scripts/check-rustfmt.mjs - run: pnpm check-types @@ -82,7 +81,6 @@ jobs: --filter '@rivet-dev/agentos-build-tools' \ --filter '@rivet-dev/agentos-toolchain' \ --filter '@rivet-dev/agentos-runtime-sidecar' \ - --filter 'secure-exec' \ test - run: pnpm lint continue-on-error: true diff --git a/.pnpmfile.cjs b/.pnpmfile.cjs new file mode 100644 index 0000000000..f587b62497 --- /dev/null +++ b/.pnpmfile.cjs @@ -0,0 +1,15 @@ +const PINNED_RIVETKIT_VERSION = "0.0.0-sqlite-uds.4e59a38"; + +module.exports = { + hooks: { + readPackage(pkg) { + if (pkg.name === "rivetkit" && pkg.version === PINNED_RIVETKIT_VERSION) { + // This preview bundles its ./agent-os implementation but still declares + // the retired external runtime. Keeping the declaration would restore the + // sunset package graph even though no bundled RivetKit module imports it. + delete pkg.dependencies?.["@rivet-dev/agent-os-core"]; + } + return pkg; + }, + }, +}; diff --git a/.secure-exec-local-path b/.secure-exec-local-path deleted file mode 100644 index 50a51e944b..0000000000 --- a/.secure-exec-local-path +++ /dev/null @@ -1,9 +0,0 @@ -# Local secure-exec checkout that carries the converged browser/wasm build. -# Agent OS depends on secure-exec; the web/wasm work links against this checkout -# via `just secure-exec-local` (which repoints crates to `path = "../secure-exec/..."` -# and npm to `link:`). The converged web architecture (the template for this work) -# currently lives on a branch, NOT yet on secure-exec main. - -SECURE_EXEC_CANONICAL=/home/nathan/secure-exec -SECURE_EXEC_CONVERGED_WEB_WORKSPACE=/home/nathan/secure-exec-convwasi -SECURE_EXEC_CONVERGED_WEB_BRANCH=browser-convergence-item-c diff --git a/CLAUDE.md b/CLAUDE.md index 7273a477f6..32c9245b88 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,8 +1,8 @@ # agentOS AgentOS owns the runtime, kernel, VFS, language execution, registry packages, -ACP/session layer, AgentOS client APIs, docs, and publish machinery. The -`secure-exec` repository is now a generated compatibility mirror only. +ACP/session layer, AgentOS client APIs, docs, and publish machinery. Agent OS +Exec is the JavaScript, TypeScript, and Python execution surface of AgentOS. ## Boundaries @@ -16,9 +16,8 @@ ACP/session layer, AgentOS client APIs, docs, and publish machinery. The or public API that already uses the word. - The protocol has no backward compatibility guarantee. Client, sidecar, and protocol crates ship in same-version lockstep; update both sides together. -- Generic runtime work belongs here, not in `../secure-exec`. Regenerate that - mirror with `node scripts/generate-secure-exec-mirror.mjs` after changing a - shimmed public surface. +- Generic runtime and language-execution work belongs here. Do not add a + compatibility mirror or a second package namespace for AgentOS language execution. - Keep root `package.json` scripts limited to Turbo orchestration; repo-specific commands belong in `justfile` recipes or scoped package scripts. - AgentOS targets native Linux/container execution. Browser support is not @@ -218,9 +217,10 @@ custom host-syscall imports. Treat that target as **native POSIX**; - `scripts/publish` is the source of truth for npm/crates discovery, version rewriting, npm publish, crates publish, release assets, and R2 upload. -- Publishable npm packages and Rust crates are AgentOS-owned. Compatibility - `@secure-exec/*`, `secure-exec`, and `secure-exec-*` artifacts are emitted - from the generated mirror. +- Publishable npm packages and Rust crates are AgentOS-owned. AgentOS language execution is + published through `@rivet-dev/agentos-javascript` and + `@rivet-dev/agentos-python`; `@rivet-dev/agentos` owns the actor API. Do not + publish compatibility artifacts or language subpaths from the actor package. - The release workflow must build and stage the native sidecar binaries, runtime-sidecar binaries, registry WASM commands, and pyodide assets before publish. diff --git a/Cargo.lock b/Cargo.lock index c8740a18e0..d0ea48db4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -197,6 +197,14 @@ dependencies = [ "md-5 0.10.6", "nix 0.29.0", "openssl", + "oxc-browserslist", + "oxc_allocator", + "oxc_ast", + "oxc_codegen", + "oxc_parser", + "oxc_semantic", + "oxc_span", + "oxc_transformer", "pbkdf2", "rusqlite", "rustix 1.1.4", @@ -1026,13 +1034,13 @@ dependencies = [ "bitflags", "cexpr", "clang-sys", - "itertools", + "itertools 0.13.0", "log", "prettyplease", "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex 1.3.0", "syn", ] @@ -1086,9 +1094,12 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.3" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +dependencies = [ + "allocator-api2", +] [[package]] name = "bytes" @@ -1106,6 +1117,15 @@ dependencies = [ "either", ] +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" version = "1.2.66" @@ -1124,7 +1144,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] @@ -1248,6 +1268,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "compact_str" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1310,6 +1344,12 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cow-utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417bef24afe1460300965a25ff4a24b8b45ad011948302ec221e8a0a81eb2c79" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -1674,6 +1714,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.9" @@ -1702,6 +1748,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -1972,6 +2024,16 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "foldhash 0.1.5", +] + [[package]] name = "hashbrown" version = "0.16.1" @@ -1980,7 +2042,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.2.0", ] [[package]] @@ -2487,6 +2549,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -2814,6 +2885,21 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "nonmax" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -2932,6 +3018,345 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" +[[package]] +name = "owo-colors" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" + +[[package]] +name = "oxc-browserslist" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5482acf27be53699af8214b9067b71117f34d74974db4b0feef1c09b556c274" +dependencies = [ + "nom 8.0.0", + "rustc-hash 2.1.3", + "serde", + "serde_json", + "thiserror", + "time", +] + +[[package]] +name = "oxc-miette" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4356a61f2ed4c9b3610245215fbf48970eb277126919f87db9d0efa93a74245c" +dependencies = [ + "cfg-if", + "owo-colors", + "oxc-miette-derive", + "textwrap", + "thiserror", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "oxc-miette-derive" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b237422b014f8f8fff75bb9379e697d13f8d57551a22c88bebb39f073c1bf696" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "oxc_allocator" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5dc7d7719a4f90c691fbd6bcf8b623df2832ccc6b303c1d07d85aeb476c5b8e" +dependencies = [ + "allocator-api2", + "bumpalo", + "hashbrown 0.15.5", + "oxc_data_structures", + "rustc-hash 2.1.3", +] + +[[package]] +name = "oxc_ast" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c1308e677a69e31e51537f5b3267aae44a67cd61a2f3a740f815839c89ea97" +dependencies = [ + "bitflags", + "oxc_allocator", + "oxc_ast_macros", + "oxc_data_structures", + "oxc_estree", + "oxc_regular_expression", + "oxc_span", + "oxc_syntax", +] + +[[package]] +name = "oxc_ast_macros" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e85f4a6422af514f59d0d5aab918237b799ca25164bd59e11b59ea61991777ae" +dependencies = [ + "phf", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "oxc_ast_visit" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a8f7472e608b7410cc6ce5b0abb9cac2529a197374e5310f9de567d8853a5d" +dependencies = [ + "oxc_allocator", + "oxc_ast", + "oxc_span", + "oxc_syntax", +] + +[[package]] +name = "oxc_cfg" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd0c7687f260ceee84da03ccd857f3144a8603b421f189d78836068e5dd2785" +dependencies = [ + "bitflags", + "itertools 0.14.0", + "nonmax", + "oxc_index", + "oxc_syntax", + "petgraph", + "rustc-hash 2.1.3", +] + +[[package]] +name = "oxc_codegen" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb7f6be284d2073471ae0123f32e4092ce1063370f0ef2889b5a9c3d16e9e17" +dependencies = [ + "bitflags", + "cow-utils", + "nonmax", + "oxc_allocator", + "oxc_ast", + "oxc_data_structures", + "oxc_index", + "oxc_semantic", + "oxc_sourcemap", + "oxc_span", + "oxc_syntax", + "rustc-hash 2.1.3", + "ryu-js", +] + +[[package]] +name = "oxc_data_structures" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d713c1b57fe1f55af1d557efd461025c9c9bd28835a6b3e0e91f46cdeb995a6" +dependencies = [ + "ropey", + "rustversion", +] + +[[package]] +name = "oxc_diagnostics" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c6188d0a1aa83656795c29b7e7f036060f5c3e5c234ebebc2a5899fb43a579" +dependencies = [ + "cow-utils", + "oxc-miette", + "percent-encoding", +] + +[[package]] +name = "oxc_ecmascript" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "851355a2d035526d3a3525cbd66224bde1e234d7cf339835a4516bf27732dcec" +dependencies = [ + "num-bigint", + "num-traits", + "oxc_ast", + "oxc_span", + "oxc_syntax", +] + +[[package]] +name = "oxc_estree" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d67d74c11e1c8a741c9321c86d2f0b30de107e1cc89d5912a6c5b9771b7e56cd" + +[[package]] +name = "oxc_index" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "967ae797e1f284bd1385f2d8e8ab94293ad27f623c76839ecf66827521365f5b" + +[[package]] +name = "oxc_parser" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bbd1f8184ae33834222f9620673c40296b4ba9c4951c54bcc5548bb7a1f2621" +dependencies = [ + "bitflags", + "cow-utils", + "memchr", + "num-bigint", + "num-traits", + "oxc_allocator", + "oxc_ast", + "oxc_data_structures", + "oxc_diagnostics", + "oxc_ecmascript", + "oxc_regular_expression", + "oxc_span", + "oxc_syntax", + "rustc-hash 2.1.3", + "seq-macro", +] + +[[package]] +name = "oxc_regular_expression" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54d16a7346afe27716433d691343ee137d640f94f978bd3244babf7248ed6453" +dependencies = [ + "bitflags", + "oxc_allocator", + "oxc_ast_macros", + "oxc_diagnostics", + "oxc_span", + "phf", + "rustc-hash 2.1.3", + "unicode-id-start", +] + +[[package]] +name = "oxc_semantic" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0adb3c19e857e5338522ece075450227cea3de0323ec679789899c9b3cf7be6c" +dependencies = [ + "itertools 0.14.0", + "oxc_allocator", + "oxc_ast", + "oxc_ast_visit", + "oxc_cfg", + "oxc_data_structures", + "oxc_diagnostics", + "oxc_ecmascript", + "oxc_index", + "oxc_span", + "oxc_syntax", + "phf", + "rustc-hash 2.1.3", + "self_cell", +] + +[[package]] +name = "oxc_sourcemap" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24015d93ed1d8f0c2a0d9f534ca85690888990658a8fc4a87ff0c92640e73300" +dependencies = [ + "base64-simd", + "cfg-if", + "cow-utils", + "rustc-hash 2.1.3", + "serde", + "serde_json", +] + +[[package]] +name = "oxc_span" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93b7cf8b6447a4d0bb9aff91c6c8fcccbae3d28bad9f9ccb1128599761a982b6" +dependencies = [ + "compact_str", + "oxc-miette", + "oxc_allocator", + "oxc_ast_macros", + "oxc_estree", +] + +[[package]] +name = "oxc_syntax" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b6ac136f155820331b326d5cbd178a9c2ee032787f04aa6e14b494c8aeb227" +dependencies = [ + "bitflags", + "cow-utils", + "nonmax", + "oxc_allocator", + "oxc_ast_macros", + "oxc_data_structures", + "oxc_estree", + "oxc_index", + "oxc_span", + "phf", + "rustc-hash 2.1.3", + "ryu-js", + "unicode-id-start", +] + +[[package]] +name = "oxc_transformer" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf60b4314988dd121184340c0d9958a33703f9a01582baf37b9afc2dd2a4e4c" +dependencies = [ + "base64 0.22.1", + "compact_str", + "cow-utils", + "indexmap 2.14.0", + "itoa", + "memchr", + "oxc-browserslist", + "oxc_allocator", + "oxc_ast", + "oxc_ast_visit", + "oxc_data_structures", + "oxc_diagnostics", + "oxc_ecmascript", + "oxc_parser", + "oxc_regular_expression", + "oxc_semantic", + "oxc_span", + "oxc_syntax", + "oxc_traverse", + "rustc-hash 2.1.3", + "serde", + "serde_json", + "sha1 0.10.6", +] + +[[package]] +name = "oxc_traverse" +version = "0.75.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b915b0d7d967c5769e35f2259b29f1e8861f0bb38920b8209b624455c464005b" +dependencies = [ + "itoa", + "oxc_allocator", + "oxc_ast", + "oxc_ast_visit", + "oxc_data_structures", + "oxc_ecmascript", + "oxc_semantic", + "oxc_span", + "oxc_syntax", + "rustc-hash 2.1.3", +] + [[package]] name = "p256" version = "0.13.2" @@ -3066,6 +3491,61 @@ dependencies = [ "pest", ] +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap 2.14.0", + "serde", +] + +[[package]] +name = "phf" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_generator" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cbb1126afed61dd6368748dae63b1ee7dc480191c6262a3b4ff1e29d86a6c5b" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d713258393a82f091ead52047ca779d37e5766226d009de21696c4e667044368" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -3343,6 +3823,16 @@ dependencies = [ "syn", ] +[[package]] +name = "ropey" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5" +dependencies = [ + "smallvec", + "str_indices", +] + [[package]] name = "rusqlite" version = "0.32.1" @@ -3363,6 +3853,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -3490,6 +3986,12 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "ryu-js" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04d056b875a9d2e6cb9a61d127afee9ac5999b9f87bcb32079d1318e505be714" + [[package]] name = "salsa20" version = "0.10.2" @@ -3634,12 +4136,24 @@ dependencies = [ "libc", ] +[[package]] +name = "self_cell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" + [[package]] name = "semver" version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +[[package]] +name = "seq-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" + [[package]] name = "serde" version = "1.0.228" @@ -3865,6 +4379,12 @@ dependencies = [ "time", ] +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + [[package]] name = "slab" version = "0.4.12" @@ -3877,6 +4397,12 @@ version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +[[package]] +name = "smawk" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8e2fb0f499abb4d162f2bedad68f5ef91a1682b5a03596ddb67efd37768d100" + [[package]] name = "socket2" version = "0.5.10" @@ -3934,6 +4460,18 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "str_indices" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6" + [[package]] name = "strsim" version = "0.11.1" @@ -4055,6 +4593,17 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + [[package]] name = "thiserror" version = "2.0.18" @@ -4350,12 +4899,24 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" +[[package]] +name = "unicode-id-start" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b79ad29b5e19de4260020f8919b443b2ef0277d242ce532ec7b7a2cc8b6007" + [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + [[package]] name = "unicode-segmentation" version = "1.13.3" @@ -4571,9 +5132,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.252.0" +version = "0.248.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8185ae345fa5687c054626ff9a50e7089797a343d9904d1dc9820eb4c4d3196f" +checksum = "ac92cf547bc18d27ecc521015c08c353b4f18b84ab388bb6d1b6b682c620d9b6" dependencies = [ "leb128fmt", "wasmparser", @@ -4581,9 +5142,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.252.0" +version = "0.248.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3eb099dcadcde5be9eef55e3a337128efd4e44b4c93122487e4d2e4e1c6627c" +checksum = "aa4439c5eee9df71ee0c6efb37f63b1fcb1fec38f85f5142c54e7ed05d33091a" dependencies = [ "bitflags", "indexmap 2.14.0", @@ -4592,9 +5153,9 @@ dependencies = [ [[package]] name = "wast" -version = "252.0.0" +version = "248.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942a3449d6a593fccc111a6241c8df52bda168af30e40bf9580d4394d7374c65" +checksum = "acc54622ed5a5cddafcdf152043f9d4aed54d4a653d686b7dfe874809fca99d7" dependencies = [ "bumpalo", "leb128fmt", @@ -4605,9 +5166,9 @@ dependencies = [ [[package]] name = "wat" -version = "1.252.0" +version = "1.248.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c72a4ba7088f7bac94cf516e49882bdf97068904a563768cf249efc839ec42cb" +checksum = "d75cd9e510603909748e6ebab89f27cd04472c1d9d85a3c88a7a6fc51a1a7934" dependencies = [ "wast", ] diff --git a/TODO.md b/TODO.md index 048046cd7a..7ff5f3c317 100644 --- a/TODO.md +++ b/TODO.md @@ -78,7 +78,7 @@ fixtures and benchmarks that exercise host VFS callbacks. Disabled browser sources remain dormant reference code, not a supported production VFS. - [x] Remove the core in-memory VFS and overlay/layer-store implementations. -- [x] Remove their root and secure-exec compatibility exports. +- [x] Remove their root and agentos compatibility exports. - [x] Require a caller-owned filesystem in the low-level compatibility runtime. - [x] Move remaining repository fixtures to the explicit test-only surface. - [x] Remove obsolete duplicate semantic tests. diff --git a/biome.json b/biome.json index dfd8ce5d8c..af860c6ab1 100644 --- a/biome.json +++ b/biome.json @@ -8,7 +8,7 @@ "!packages/runtime-browser", "!packages/playground", "!packages/core/src/sidecar/generated-protocol.ts", - "!_secure-exec-sibling", + "!_agentos-sibling", "!/**/node_modules" ], "ignoreUnknown": true diff --git a/crates/CLAUDE.md b/crates/CLAUDE.md index fb92d0c360..b56394f676 100644 --- a/crates/CLAUDE.md +++ b/crates/CLAUDE.md @@ -5,7 +5,7 @@ The Rust sidecar implements the kernel: virtual filesystem, process table, socke The kernel orchestrates three execution environments: - **WASM processes** -- POSIX utilities (coreutils, sh, grep, etc.) compiled to WebAssembly, running within the sidecar's managed runtime. -- **Node.js (V8 isolates)** -- JS/TS executes inside isolated V8 contexts managed by the sidecar's execution engine. All Node.js builtin modules (`fs`, `net`, `child_process`, `dns`, `http`, `os`, etc.) are kernel-backed polyfills that route through the kernel VFS, socket table, and process table via synchronous RPC. Module loading is intercepted so guest code never touches real host APIs. **⚠️ CURRENTLY BROKEN**: The execution engine currently spawns real host `node` child processes instead of V8 isolates, and most builtins fall through to real host modules. This is being actively fixed. The complete working polyfill code from the original `@secure-exec` packages has been recovered to `~/.agents/recovery/secure-exec/` (source repo: `/home/nathan/secure-exec-1/`). See `crates/execution/CLAUDE.md` for the gap table and recovery file list. +- **Node.js (V8 isolates)** -- JS/TS executes inside isolated V8 contexts managed by the sidecar's execution engine. All Node.js builtin modules (`fs`, `net`, `child_process`, `dns`, `http`, `os`, etc.) are kernel-backed polyfills that route through the kernel VFS, socket table, and process table via synchronous RPC. Module loading is intercepted so guest code never touches real host APIs. **⚠️ CURRENTLY BROKEN**: The execution engine currently spawns real host `node` child processes instead of V8 isolates, and most builtins fall through to real host modules. This is being actively fixed. The complete working polyfill code from the original `@agentos` packages has been recovered to `~/.agents/recovery/agentos/` (source repo: `/home/nathan/agentos-1/`). See `crates/execution/CLAUDE.md` for the gap table and recovery file list. - **Python (Pyodide)** -- CPython compiled to WASM via Pyodide, with kernel-backed file/network I/O. **ALL guest code MUST execute inside the kernel with ZERO host escapes.** No runtime may spawn unsandboxed host processes, touch real host filesystems, open real network sockets, or call real Node.js builtins. Every syscall goes through the kernel. This is the single most important architectural invariant. Any path where guest code reaches the real host is a critical security violation. @@ -19,7 +19,7 @@ These are hard rules with no exceptions: 1. **Every guest syscall goes through the kernel.** File reads go through the kernel VFS, not real `node:fs`. Network connections go through the kernel socket table, not real `node:net`. Process spawning go through the kernel process table, not real `node:child_process`. DNS resolution goes through the kernel's DNS resolver, not real `node:dns`. There are no shortcuts where guest code touches host resources directly. 2. **No real host builtins.** When a guest does `require('fs')` or `import net from 'node:net'`, the module loader must return a kernel-backed polyfill. If no polyfill exists yet, the builtin must be denied (`ERR_ACCESS_DENIED`). The loader must never fall through to Node.js's default resolution and hand the guest the real host module. 3. **The host is an implementation detail.** Guest code should not be able to observe that it is running on a host Node.js process. `process.pid` should be the kernel PID, `os.hostname()` should be the kernel hostname, `fs.readdirSync('/')` should show the kernel VFS root. `process.cwd()` should return the kernel CWD, not a host path. `process.env` must not contain internal `AGENT_OS_*` control variables. Error messages and stack traces must not reveal host filesystem paths. `require.resolve()` must return guest-visible paths, not host paths. Any host state leaking through to the guest is a bug. -4. **Polyfills are ports, not wrappers.** A path-translating shim over real `node:fs` is not a polyfill -- it is a wrapper around a host API. A real polyfill implements the API semantics using only kernel primitives (VFS, socket table, process table, pipe manager). The original JS kernel (`@secure-exec/core` + `@secure-exec/nodejs`, deleted in commit `5a43882`) had full kernel-backed polyfills for `fs`, `net`, `http`, `dns`, `dgram`, `child_process`, and `os`. The Rust sidecar must reach the same level of isolation. +4. **Polyfills are ports, not wrappers.** A path-translating shim over real `node:fs` is not a polyfill -- it is a wrapper around a host API. A real polyfill implements the API semantics using only kernel primitives (VFS, socket table, process table, pipe manager). The original JavaScript kernel packages, deleted in commit `5a43882`, had full kernel-backed polyfills for `fs`, `net`, `http`, `dns`, `dgram`, `child_process`, and `os`. The Rust sidecar must reach the same level of isolation. 5. **Control channels must be out-of-band.** The sidecar must not use in-band magic prefixes on stdout/stderr for control signaling (exit codes, metrics, signal registration). Guest code can write these prefixes to inject fake control messages. Use dedicated file descriptors, separate pipes, or a side-channel protocol for all sidecar-internal communication. 6. **Resource consumption must be bounded.** Every guest-allocatable resource must have a configurable limit enforced by the kernel: filesystem total size, inode count, process count, open FDs, pipes, PTYs, sockets, connections. Unbounded allocation from guest input is a DoS vector. The kernel's `ResourceLimits` must cover all resource types, not just processes and FDs. Sidecar metadata parsing should start from `ResourceLimits::default()` and only override keys that are actually present; rebuilding the struct from sparse metadata drops default filesystem byte/inode caps. @@ -61,8 +61,8 @@ These are hard rules with no exceptions: - **`sandbox_agent` `pread` should prefer HTTP `Range` on `/v1/fs/file`, but older remotes may ignore it and return `200 OK`.** Keep the compatibility path that warns and slices the full response body instead of failing, because the sidecar still has to interoperate with pre-range sandbox-agent servers. - **Native sidecar security/audit telemetry should use structured bridge events, not ad hoc strings.** In `crates/sidecar/src/service.rs`, emit security-relevant records with `bridge.emit_structured_event(...)` and include a `timestamp` field plus stable keys such as `policy`, `path`, `source_pid`, `target_pid`, or `reason` so tests and downstream aggregation can assert on them directly. - **Native mount plugins live under `crates/sidecar/src/plugins/` and register through `plugins/mod.rs`.** Keep `host_dir`, `s3`, `google_drive`, and `sandbox_agent` there with shared registration glue, while `bridge.rs` only layers on sidecar-specific in-memory and JS-bridge plugin registration. -- **The sidecar execution driver's shell-visible builtin commands must mirror the compat runtime surface.** Seed `node` and `wasm` during `create_vm()` and preserve them when refreshing `/__secure_exec/commands/*` in `configure_vm()`, but do not materialize extra `/bin/*` runtime stubs such as `python` unless a lower snapshot or installed software explicitly provides them. Direct `ExecuteRequest { runtime: ... }` dispatch still covers runtimes that are not meant to appear in the guest filesystem. -- **Mounted WASM command directories need both command-map refresh and guest `PATH` refresh.** When `crates/sidecar/src/vm.rs` rediscovers `/__secure_exec/commands/*`, update `vm.command_guest_paths` and prepend those parent directories into `vm.guest_env["PATH"]`; `crates/sidecar/src/execution.rs` child-process resolution should search `PATH` entries before falling back to `/bin`, `/usr/bin`, and `/usr/local/bin`, or guest `child_process.spawn("sh")`/`spawn("grep")` misses mounted registry commands. +- **The sidecar execution driver's shell-visible builtin commands must mirror the compat runtime surface.** Seed `node` and `wasm` during `create_vm()` and preserve them when refreshing `/__agentos/commands/*` in `configure_vm()`, but do not materialize extra `/bin/*` runtime stubs such as `python` unless a lower snapshot or installed software explicitly provides them. Direct `ExecuteRequest { runtime: ... }` dispatch still covers runtimes that are not meant to appear in the guest filesystem. +- **Mounted WASM command directories need both command-map refresh and guest `PATH` refresh.** When `crates/sidecar/src/vm.rs` rediscovers `/__agentos/commands/*`, update `vm.command_guest_paths` and prepend those parent directories into `vm.guest_env["PATH"]`; `crates/sidecar/src/execution.rs` child-process resolution should search `PATH` entries before falling back to `/bin`, `/usr/bin`, and `/usr/local/bin`, or guest `child_process.spawn("sh")`/`spawn("grep")` misses mounted registry commands. - **Native-sidecar command resolution must use the caller's effective `PATH`, not just `vm.guest_env`.** In `crates/sidecar/src/execution.rs`, path-like guest candidates emitted by shells (for example `/home/agentos/.pi/agent/bin/printf`) should only be treated as executables when the candidate or mapped host file actually exists; if the parent directory came from `PATH` but the file is missing, fall back to the basename so registry command remapping can still find `printf`/`bash`/`which`. - **Native-sidecar WASM commands see the shadow root, so standard guest directories must be seeded there during VM creation.** In `crates/sidecar/src/vm.rs`, keep `/tmp`, `/var/tmp`, `/bin`, `/usr`, and the rest of the POSIX bootstrap tree materialized in the shadow root before any WASM command runs, or shell redirection and absolute-path checks will disagree with the kernel VFS (`vm.stat("/tmp")` works while `sh -c 'echo hi > /tmp/x'` fails). - **Host filesystem API writes that should be visible to WASM commands must mirror into the shadow root immediately.** In `crates/sidecar/src/filesystem.rs`, `GuestFilesystemOperation::WriteFile` needs to update both the kernel VFS and the VM shadow tree right away, or `vm.writeFile("/tmp/x")` will succeed while guest `sh`/`cat`/`ls` still miss the file until some later sync path runs. diff --git a/crates/agentos-protocol/protocol/agent_os_acp_v1.bare b/crates/agentos-protocol/protocol/agent_os_acp_v1.bare index 4cc34059c9..f6ca0c6483 100644 --- a/crates/agentos-protocol/protocol/agent_os_acp_v1.bare +++ b/crates/agentos-protocol/protocol/agent_os_acp_v1.bare @@ -1,5 +1,5 @@ # Agent OS ACP extension protocol schema, version 1. -# This schema is intentionally standalone. It imports no secure-exec schema. +# This schema is intentionally standalone. It imports no agentos schema. type JsonUtf8 str @@ -181,12 +181,14 @@ type AcpCloseSessionRequest struct { # Resume a session that exists in durable storage but is not live in the current # VM (e.g. after a Rivet actor slept and woke with a fresh VM). The sidecar runs # the stateless resume state machine (native session/load when the agent supports -# it, else a fresh session/new + transcript continuation preamble). `cwd`/`env` -# describe the fresh adapter launch used by the fallback tier. `transcriptPath`, -# when present, is a guest-readable path the fallback preamble points the agent at. +# it, else a fresh session/new + transcript continuation preamble). `runtime`, +# `cwd`, and `env` describe the fresh adapter launch used by the fallback tier. +# `transcriptPath`, when present, is a guest-readable path the fallback preamble +# points the agent at. type AcpResumeSessionRequest struct { sessionId: str agentType: str + runtime: AcpRuntimeKind transcriptPath: optional cwd: str additionalDirectories: list diff --git a/crates/agentos-sidecar-browser/src/wasm.rs b/crates/agentos-sidecar-browser/src/wasm.rs index 3780937455..cc078fe557 100644 --- a/crates/agentos-sidecar-browser/src/wasm.rs +++ b/crates/agentos-sidecar-browser/src/wasm.rs @@ -1,10 +1,10 @@ //! wasm-bindgen entry point for the Agent OS browser sidecar. //! -//! Mirrors secure-exec's `BrowserSidecarWasm` (pushFrame/pollEvent over the shared +//! Mirrors agentos's `BrowserSidecarWasm` (pushFrame/pollEvent over the shared //! `BrowserWireDispatcher` + `BrowserJsBridge`) but registers the Agent OS ACP //! `BrowserExtension` into the dispatcher's sidecar, so guest ACP/session traffic //! is handled by the Agent OS wrapper while every kernel syscall still routes -//! through the converged secure-exec wasm kernel (the sole enforcement point). +//! through the converged agentos wasm kernel (the sole enforcement point). use agentos_native_sidecar_browser::wire_dispatch::{BrowserWireDispatcher, BROWSER_SIDECAR_ID}; use agentos_native_sidecar_browser::BrowserJsBridge; diff --git a/crates/agentos-sidecar-core/src/engine.rs b/crates/agentos-sidecar-core/src/engine.rs index 3d6f980f1a..bcf0d3e3eb 100644 --- a/crates/agentos-sidecar-core/src/engine.rs +++ b/crates/agentos-sidecar-core/src/engine.rs @@ -15,7 +15,7 @@ use std::collections::BTreeMap; use agentos_protocol::generated::v1::{ AcpCloseSessionRequest, AcpCreateSessionRequest, AcpDeliverAgentOutputRequest, AcpGetSessionStateRequest, AcpPendingResponse, AcpRequest, AcpResponse, - AcpResumeSessionRequest, AcpRuntimeKind, AcpSessionClosedResponse, AcpSessionRequest, + AcpResumeSessionRequest, AcpSessionClosedResponse, AcpSessionRequest, AcpSessionResumedResponse, AcpSessionRpcResponse, }; use serde_json::{json, Map, Value}; @@ -63,13 +63,14 @@ struct AgentPackageManifest { struct AgentPackageAgentBlock { #[serde(rename = "acpEntrypoint", default)] acp_entrypoint: String, + #[serde(default)] env: BTreeMap, #[serde(rename = "launchArgs", default)] launch_args: Vec, } /// Resolve an agent name to its launch parameters by reading the projected -/// manifest at `/opt/agentos//current/agentos-package.json` over the host +/// manifest at `/opt/agentos/pkgs//current/agentos-package.json` over the host /// filesystem seam. A missing file, a missing `agent` block, or an empty /// `agent.acpEntrypoint` all map to a single typed "unknown agent" error. Mirrors /// the native sidecar `resolve_agent`. @@ -1782,7 +1783,7 @@ mod tests { #[test] fn resume_falls_back_to_session_new_when_no_native_capability() { - use agentos_protocol::generated::v1::AcpResumeSessionRequest; + use agentos_protocol::generated::v1::{AcpResumeSessionRequest, AcpRuntimeKind}; use serde_json::Value; use std::collections::{HashMap, VecDeque}; @@ -1852,6 +1853,7 @@ mod tests { let request = AcpResumeSessionRequest { session_id: "old-session".into(), agent_type: "echo".into(), + runtime: AcpRuntimeKind::JavaScript, transcript_path: Some("/transcripts/old.jsonl".into()), cwd: "/workspace".into(), additional_directories: Vec::new(), diff --git a/crates/agentos-sidecar-core/src/lib.rs b/crates/agentos-sidecar-core/src/lib.rs index 85b7547bf8..25034f58b5 100644 --- a/crates/agentos-sidecar-core/src/lib.rs +++ b/crates/agentos-sidecar-core/src/lib.rs @@ -3,7 +3,7 @@ //! Host-free core for the Agent OS ACP sidecar extension. //! //! This crate holds the parts of the ACP extension that do NOT depend on the host -//! runtime (tokio, std::fs, the native secure-exec sidecar): the request/response +//! runtime (tokio, std::fs, the native agentos sidecar): the request/response //! wire codec and the per-session data model. It compiles to wasm32 so the browser //! sidecar (`agentos-sidecar-browser`) can run the same ACP logic the native sidecar //! (`agentos-sidecar`) runs, with each backend supplying the host operations diff --git a/crates/agentos-sidecar/CLAUDE.md b/crates/agentos-sidecar/CLAUDE.md index 894ac4580a..4b19f04fe7 100644 --- a/crates/agentos-sidecar/CLAUDE.md +++ b/crates/agentos-sidecar/CLAUDE.md @@ -2,6 +2,6 @@ - Author ACP behavior as an `Ext` extension over `agentos-protocol`; do not add new top-level sidecar request/response variants for agent-session RPCs. - Keep `agentos-protocol` as the only ACP payload schema source; extension requests, responses, events, and callbacks must use the generated BARE types. -- Keep generic secure-exec sidecar code agent-agnostic; ACP namespace handling belongs in this wrapper extension, not in secure-exec transport or kernel layers. +- Keep generic agentos sidecar code agent-agnostic; ACP namespace handling belongs in this wrapper extension, not in agentos transport or kernel layers. - Extension guest work must still run through the kernel boundary via `ExtensionContext`; never spawn host-native agent adapters or touch host files directly from extension logic. - Emit live session notifications as generated `AcpEvent` payloads in `EventPayload::Ext`; do not add event cursor replay to snapshot state. diff --git a/crates/agentos-sidecar/src/acp/mod.rs b/crates/agentos-sidecar/src/acp/mod.rs index 835a923210..ed30ebe3e5 100644 --- a/crates/agentos-sidecar/src/acp/mod.rs +++ b/crates/agentos-sidecar/src/acp/mod.rs @@ -61,7 +61,7 @@ const ACP_INACTIVITY_WARNING_INTERVAL: Duration = Duration::from_secs(30); const PROMPT_RESPONSE_DRAIN_QUIET: Duration = Duration::from_millis(50); // 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 +// the same cadence as agentos's outer event pump so adapter fetches and // process output keep moving mid-turn. const ACP_CANCEL_METHOD: &str = "session/cancel"; const ACP_TRACE_PATH_ENV: &str = "AGENT_OS_ACP_TRACE_PATH"; @@ -85,7 +85,7 @@ const AGENTOS_SYSTEM_PROMPT: &str = include_str!("../AGENTOS_SYSTEM_PROMPT.md"); /// evict the now-dead session record instead of leaking it until an explicit /// internal runtime cleanup that may never run. const ADAPTER_EXITED_ERROR_MARKER: &str = "exited with code"; -/// Substring of the secure-exec process-table error returned when an operation +/// Substring of the agentos process-table error returned when an operation /// targets a process that already exited ("VM has no active process "). /// Writing a request to an adapter that crashed while *idle* surfaces this way /// (the exit is observed lazily, on the next stdin write), so it is classified @@ -1424,7 +1424,7 @@ mod tests { assert_eq!(adapter_exit_code_from_error(&exited), Some(7)); // Lazy observation: a request write to an already-reaped adapter fails - // with secure-exec's process-table error (the exact production shape: + // with agentos's process-table error (the exact production shape: // "VM vm-5 has no active process agent-6"). No exit code is observed. let gone = SidecarError::InvalidState(String::from("VM vm-5 has no active process agent-6")); diff --git a/crates/agentos-sidecar/src/acp/runtime.rs b/crates/agentos-sidecar/src/acp/runtime.rs index 34e8d60238..cc2d597b68 100644 --- a/crates/agentos-sidecar/src/acp/runtime.rs +++ b/crates/agentos-sidecar/src/acp/runtime.rs @@ -1229,6 +1229,8 @@ pub(super) async fn send_json_rpc_request( } EventPayload::ProcessOutputEvent(_) | EventPayload::ProcessExitedEvent(_) + | EventPayload::ExecutionOutputEvent(_) + | EventPayload::ExecutionCompletedEvent(_) | EventPayload::VmLifecycleEvent(_) | EventPayload::StructuredEvent(_) | EventPayload::ExtEnvelope(_) => {} @@ -1576,7 +1578,7 @@ pub(super) async fn resolve_agent( } /// Extract the owning connection id from an ownership scope. Every scope carries -/// a connection id, which is the tenant boundary secure-exec enforces; ACP +/// a connection id, which is the tenant boundary AgentOS enforces; ACP /// session ownership is keyed off this same connection id. /// Remove every session in `sessions` owned by `connection_id`, returning the /// adapter process ids of the dropped records. Split out from @@ -1624,7 +1626,7 @@ pub(super) fn is_adapter_exited_error(error: &SidecarError) -> bool { } /// True when `error` means the adapter process is gone: either the in-pump exit -/// observation (`is_adapter_exited_error`) or a secure-exec process-table +/// observation (`is_adapter_exited_error`) or an AgentOS process-table /// lookup failure from operating on an adapter that already exited — the lazy /// observation of an idle-time crash (`ADAPTER_NO_ACTIVE_PROCESS_MARKER`). pub(super) fn is_adapter_gone_error(error: &SidecarError) -> bool { diff --git a/crates/bridge/Cargo.toml b/crates/bridge/Cargo.toml index cddd3c08d1..2a4b4c3e90 100644 --- a/crates/bridge/Cargo.toml +++ b/crates/bridge/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -description = "Shared bridge contracts between the secure-exec kernel and execution planes" +description = "Shared bridge contracts between the agentos kernel and execution planes" [dependencies] serde = { version = "1.0", features = ["derive"] } diff --git a/crates/bridge/src/lib.rs b/crates/bridge/src/lib.rs index b7d9298b88..06ba41c19b 100644 --- a/crates/bridge/src/lib.rs +++ b/crates/bridge/src/lib.rs @@ -1,6 +1,6 @@ #![forbid(unsafe_code)] -//! Shared bridge contracts between the secure-exec kernel and execution planes. +//! Shared bridge contracts between the agentos kernel and execution planes. pub mod queue_tracker; diff --git a/crates/bridge/src/queue_tracker.rs b/crates/bridge/src/queue_tracker.rs index 06ef0ca1f6..7ef9130f9e 100644 --- a/crates/bridge/src/queue_tracker.rs +++ b/crates/bridge/src/queue_tracker.rs @@ -1,6 +1,6 @@ //! Centralized bounded-queue usage tracker. //! -//! secure-exec streams guest output through a *chain* of bounded queues: the +//! agentos streams guest output through a *chain* of bounded queues: the //! V8 -> host event channel, the sidecar stdout/stdin frame queues, and so on. //! Each queue applies backpressure when full (it parks the producer until the //! consumer drains) rather than crashing, but backpressure is invisible: a slow @@ -403,7 +403,7 @@ pub struct QueueRegistry { } impl QueueRegistry { - /// The shared registry. All `secure-exec` bounded queues register here so + /// The shared registry. All `agentos` bounded queues register here so /// their usage can be inspected from one place. pub fn global() -> &'static QueueRegistry { static REGISTRY: OnceLock = OnceLock::new(); diff --git a/crates/build-support/Cargo.toml b/crates/build-support/Cargo.toml index d9bed0e4d2..abfd01b416 100644 --- a/crates/build-support/Cargo.toml +++ b/crates/build-support/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -description = "Build script helpers for secure-exec crates" +description = "Build script helpers for agentos crates" [lib] path = "v8_bridge_build.rs" diff --git a/crates/build-support/v8_bridge_build.rs b/crates/build-support/v8_bridge_build.rs index fc40414f64..c9d64475fb 100644 --- a/crates/build-support/v8_bridge_build.rs +++ b/crates/build-support/v8_bridge_build.rs @@ -301,7 +301,7 @@ mod tests { fn temp_test_dir(name: &str) -> io::Result { let mut path = std::env::temp_dir(); path.push(format!( - "secure-exec-v8-bridge-build-{name}-{}", + "agentos-v8-bridge-build-{name}-{}", std::process::id() )); let _ = fs::remove_dir_all(&path); diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 2f7d5499d6..77cb647d80 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -7,7 +7,7 @@ repository.workspace = true description = "High-level Rust client SDK for the Agent OS native sidecar (1:1 port of the TypeScript AgentOs client)" [dependencies] -# Reuse the secure-exec BARE wire schema + IPC transport. No new wire types are defined here. +# Reuse the agentos BARE wire schema + IPC transport. No new wire types are defined here. agent-client-protocol-schema = { workspace = true } agentos-sidecar-client = { workspace = true } agentos-vm-config = { workspace = true } diff --git a/crates/client/src/agent_os.rs b/crates/client/src/agent_os.rs index 325f52c27d..d3b6df02f0 100644 --- a/crates/client/src/agent_os.rs +++ b/crates/client/src/agent_os.rs @@ -289,7 +289,16 @@ impl AgentOs { | wire::ResponsePayload::ResourceSnapshotResponse(_) | wire::ResponsePayload::PackageLinkedResponse(_) | wire::ResponsePayload::ProvidedCommandsResponse(_) - | wire::ResponsePayload::ListMountsResponse(_) => { + | wire::ResponsePayload::ListMountsResponse(_) + | wire::ResponsePayload::ExecutionAcceptedResponse(_) + | wire::ResponsePayload::ExecutionCompletedResponse(_) + | wire::ResponsePayload::ExecutionEvaluationResponse(_) + | wire::ResponsePayload::TypeScriptCheckResponse(_) + | wire::ResponsePayload::ExecutionDescriptorResponse(_) + | wire::ResponsePayload::ExecutionListResponse(_) + | wire::ResponsePayload::ExecutionDeletedResponse(_) + | wire::ResponsePayload::ExecutionIoResponse(_) + | wire::ResponsePayload::ExecutionOutputPageResponse(_) => { return Err(ClientError::Sidecar( "unexpected open_session response".to_string(), )); @@ -359,7 +368,16 @@ impl AgentOs { | wire::ResponsePayload::ResourceSnapshotResponse(_) | wire::ResponsePayload::PackageLinkedResponse(_) | wire::ResponsePayload::ProvidedCommandsResponse(_) - | wire::ResponsePayload::ListMountsResponse(_) => { + | wire::ResponsePayload::ListMountsResponse(_) + | wire::ResponsePayload::ExecutionAcceptedResponse(_) + | wire::ResponsePayload::ExecutionCompletedResponse(_) + | wire::ResponsePayload::ExecutionEvaluationResponse(_) + | wire::ResponsePayload::TypeScriptCheckResponse(_) + | wire::ResponsePayload::ExecutionDescriptorResponse(_) + | wire::ResponsePayload::ExecutionListResponse(_) + | wire::ResponsePayload::ExecutionDeletedResponse(_) + | wire::ResponsePayload::ExecutionIoResponse(_) + | wire::ResponsePayload::ExecutionOutputPageResponse(_) => { return Err(ClientError::Sidecar( "unexpected create_vm response".to_string(), )); @@ -392,7 +410,7 @@ impl AgentOs { permissions: Some(permissions), // Client-side `moduleAccessCwd` was removed in favor of an // explicit `nodeModulesMount(...)` entry in `mounts`; the - // secure-exec wire field is left unset. + // agentos wire field is left unset. module_access_cwd: None, instructions: config.additional_instructions.clone().into_iter().collect(), projected_modules: Vec::new(), @@ -450,7 +468,16 @@ impl AgentOs { | wire::ResponsePayload::ResourceSnapshotResponse(_) | wire::ResponsePayload::PackageLinkedResponse(_) | wire::ResponsePayload::ProvidedCommandsResponse(_) - | wire::ResponsePayload::ListMountsResponse(_) => { + | wire::ResponsePayload::ListMountsResponse(_) + | wire::ResponsePayload::ExecutionAcceptedResponse(_) + | wire::ResponsePayload::ExecutionCompletedResponse(_) + | wire::ResponsePayload::ExecutionEvaluationResponse(_) + | wire::ResponsePayload::TypeScriptCheckResponse(_) + | wire::ResponsePayload::ExecutionDescriptorResponse(_) + | wire::ResponsePayload::ExecutionListResponse(_) + | wire::ResponsePayload::ExecutionDeletedResponse(_) + | wire::ResponsePayload::ExecutionIoResponse(_) + | wire::ResponsePayload::ExecutionOutputPageResponse(_) => { return Err(ClientError::Sidecar( "unexpected configure_vm response".to_string(), )); @@ -534,7 +561,16 @@ impl AgentOs { | wire::ResponsePayload::ResourceSnapshotResponse(_) | wire::ResponsePayload::PackageLinkedResponse(_) | wire::ResponsePayload::ProvidedCommandsResponse(_) - | wire::ResponsePayload::ListMountsResponse(_) => { + | wire::ResponsePayload::ListMountsResponse(_) + | wire::ResponsePayload::ExecutionAcceptedResponse(_) + | wire::ResponsePayload::ExecutionCompletedResponse(_) + | wire::ResponsePayload::ExecutionEvaluationResponse(_) + | wire::ResponsePayload::TypeScriptCheckResponse(_) + | wire::ResponsePayload::ExecutionDescriptorResponse(_) + | wire::ResponsePayload::ExecutionListResponse(_) + | wire::ResponsePayload::ExecutionDeletedResponse(_) + | wire::ResponsePayload::ExecutionIoResponse(_) + | wire::ResponsePayload::ExecutionOutputPageResponse(_) => { return Err(ClientError::Sidecar( "unexpected register_host_callbacks response".to_string(), )); @@ -887,6 +923,8 @@ fn spawn_acp_event_pump(client: &AgentOs) { wire::EventPayload::VmLifecycleEvent(_) | wire::EventPayload::ProcessOutputEvent(_) | wire::EventPayload::ProcessExitedEvent(_) + | wire::EventPayload::ExecutionOutputEvent(_) + | wire::EventPayload::ExecutionCompletedEvent(_) | wire::EventPayload::StructuredEvent(_), )) => {} Err(broadcast::error::RecvError::Lagged(_)) => {} @@ -1410,6 +1448,8 @@ async fn wait_for_vm_ready( } wire::EventPayload::ProcessOutputEvent(_) | wire::EventPayload::ProcessExitedEvent(_) + | wire::EventPayload::ExecutionOutputEvent(_) + | wire::EventPayload::ExecutionCompletedEvent(_) | wire::EventPayload::StructuredEvent(_) | wire::EventPayload::ExtEnvelope(_) => {} }, diff --git a/crates/client/src/config.rs b/crates/client/src/config.rs index f8de322e9a..2b21a204cb 100644 --- a/crates/client/src/config.rs +++ b/crates/client/src/config.rs @@ -30,11 +30,11 @@ pub struct AgentOsConfig { /// Software packages to install (flattened). Default `[]`. pub software: Vec, /// Package directories to project into the VM's `/opt/agentos` tree (the - /// secure-exec package projection). Each entry is a host dir containing an + /// agentos package projection). Each entry is a host dir containing an /// `agentos-package.json` manifest + the package payload. Default `[]`. pub packages: Vec, /// Guest mount point for the package projection. Default `/opt/agentos` - /// (secure-exec's `OPT_AGENTOS_ROOT`) when `None`. + /// (agentos's `OPT_AGENTOS_ROOT`) when `None`. pub packages_mount_at: Option, /// Loopback ports exempt from the default outbound-to-host block. pub loopback_exempt_ports: Vec, @@ -166,7 +166,7 @@ impl AgentOsConfigBuilder { #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] pub enum SoftwareKind { - /// A directory of wasm command binaries. Mounted at `/__secure_exec/commands/{index}/` so the + /// A directory of wasm command binaries. Mounted at `/__agentos/commands/{index}/` so the /// sidecar's command discovery can resolve guest commands (`echo`, `sh`, `grep`, ...). #[default] WasmCommands, @@ -221,7 +221,7 @@ pub struct SidecarJsBridgeCall { /// Host-side handler for sidecar `JsBridgeCallRequest` payloads. /// /// This is Rust-only and intentionally not JSON-serializable. RivetKit uses it to bind a native -/// sidecar root filesystem to actor-owned SQLite (`ctx.db_*`) without teaching secure-exec about +/// sidecar root filesystem to actor-owned SQLite (`ctx.db_*`) without teaching agentos about /// Rivet actors. pub type SidecarJsBridgeCallback = Arc< dyn Fn( diff --git a/crates/client/src/cron.rs b/crates/client/src/cron.rs index 8043eb4a3f..bbe8ef7007 100644 --- a/crates/client/src/cron.rs +++ b/crates/client/src/cron.rs @@ -423,7 +423,7 @@ async fn run_action(vm: &AgentOs, action: &CronAction) -> Result<(), ClientError // and re-parsing it through the `exec` command-line parser would re-split argv elements // on whitespace and shell-evaluate any `$()`/backtick content; `exec_argv` preserves the // structured (command, args) contract element-for-element. - vm.exec_argv(command, args, crate::process::ExecOptions::default()) + vm.exec_argv_process(command, args, crate::process::ExecOptions::default()) .await .map_err(|err| ClientError::Sidecar(err.to_string()))?; Ok(()) diff --git a/crates/client/src/fs.rs b/crates/client/src/fs.rs index 069a498083..5595007a41 100644 --- a/crates/client/src/fs.rs +++ b/crates/client/src/fs.rs @@ -518,7 +518,7 @@ impl AgentOs { let result = self .guest_fs_call(Self::fs_request(GuestFilesystemOperation::ReadDir, path)) .await?; - // secure-exec's READ_DIR now returns rich entries (`entries: + // agentos's READ_DIR now returns rich entries (`entries: // list` with name + is_directory + is_symbolic_link); // this name-only accessor projects the basenames. The richer fields back // the typed [`Self::read_dir_with_types`] path. @@ -703,7 +703,7 @@ impl AgentOs { Ok(entries) } - /// Typed directory listing: each child reported with its resolved type. secure-exec's native + /// Typed directory listing: each child reported with its resolved type. agentos's native /// `READ_DIR` returns basenames only (`entries: list`), so the type of each entry is derived /// with a per-child `lstat` (a symlink is reported as such, lstat-style, not followed). Goes /// through the kernel, so mounts are listed correctly. `.`/`..` are filtered. diff --git a/crates/client/src/language_execution.rs b/crates/client/src/language_execution.rs new file mode 100644 index 0000000000..0178528215 --- /dev/null +++ b/crates/client/src/language_execution.rs @@ -0,0 +1,1014 @@ +//! First-class JavaScript, TypeScript, Python, and shared execution lifecycle. + +use std::collections::HashMap; + +use agentos_sidecar_client::wire; +use tokio::sync::broadcast; + +use crate::agent_os::AgentOs; +use crate::error::{ClientError, ClientResult}; + +pub type ExecutionDescriptor = wire::ExecutionDescriptor; +pub type CodeExecutionResult = wire::ExecutionCompletedResponse; +pub type ExecutionOutputEvent = wire::ExecutionOutputEvent; +pub type ExecutionCompletedEvent = wire::ExecutionCompletedEvent; +pub type ExecutionOutputPage = wire::ExecutionOutputPageResponse; +pub type TypeScriptDiagnostic = wire::TypeScriptDiagnostic; + +#[derive(Debug, Clone, Default)] +pub struct LanguageExecutionOptions { + pub execution_id: Option, + pub create_if_missing: Option, + pub cwd: Option, + pub env: HashMap, + pub args: Vec, + pub stdin: Option>, + pub timeout_ms: Option, + pub detached: bool, + pub pty: Option, +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct ExecutionPtyOptions { + pub cols: Option, + pub rows: Option, +} + +#[derive(Debug, Clone, Default)] +pub struct InlineExecutionOptions { + pub process: LanguageExecutionOptions, + pub inputs: Option>, +} + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub enum JavaScriptModuleFormat { + #[default] + Module, + CommonJs, +} + +#[derive(Debug, Clone, Default)] +pub struct JavaScriptExecutionOptions { + pub inline: InlineExecutionOptions, + pub format: JavaScriptModuleFormat, + pub file_path: Option, +} + +#[derive(Debug, Clone, Default)] +pub struct TypeScriptExecutionOptions { + pub inline: InlineExecutionOptions, + pub file_path: Option, + pub tsconfig_path: Option, + pub compiler_options: Option>, +} + +#[derive(Debug, Clone, Default)] +pub struct TypeScriptCheckOptions { + pub execution_id: Option, + pub create_if_missing: Option, + pub cwd: Option, + pub file_path: Option, + pub tsconfig_path: Option, + pub compiler_options: Option>, + pub timeout_ms: Option, +} + +#[derive(Debug, Clone, Default)] +pub struct NpmProjectInstallOptions { + pub execution_id: Option, + pub create_if_missing: Option, + pub cwd: Option, + pub env: HashMap, + pub timeout_ms: Option, + pub frozen: Option, +} + +#[derive(Debug, Clone, Default)] +pub struct NpmPackageInstallOptions { + pub execution_id: Option, + pub create_if_missing: Option, + pub cwd: Option, + pub env: HashMap, + pub timeout_ms: Option, + pub dev: Option, + pub global: Option, +} + +#[derive(Debug, Clone, Default)] +pub struct PythonInstallOptions { + pub execution_id: Option, + pub create_if_missing: Option, + pub cwd: Option, + pub env: HashMap, + pub timeout_ms: Option, + pub upgrade: Option, + pub requirements_file: Option, + pub index_url: Option, + pub extra_index_urls: Vec, +} + +#[derive(Debug, Clone)] +pub struct TypeScriptCheckResult { + pub result: CodeExecutionResult, + pub has_errors: Option, + pub diagnostics: Vec, +} + +#[derive(Debug, Clone)] +pub enum ExecutionSubmission { + Completed(CodeExecutionResult), + Detached(ExecutionDescriptor), +} + +#[derive(Debug, Clone)] +pub struct CodeEvaluationResult { + pub result: CodeExecutionResult, + pub value: Option, +} + +fn identity(options: &LanguageExecutionOptions) -> wire::ExecutionIdentityOptions { + wire::ExecutionIdentityOptions { + execution_id: options.execution_id.clone(), + create_if_missing: options.create_if_missing, + } +} + +fn process(options: &LanguageExecutionOptions) -> wire::ProcessExecutionOptions { + wire::ProcessExecutionOptions { + identity: identity(options), + detached: Some(options.detached), + cwd: options.cwd.clone(), + env: (!options.env.is_empty()).then(|| options.env.clone()), + args: options.args.clone(), + stdin: options.stdin.clone(), + timeout_ms: options.timeout_ms, + pty: options.pty.map(|pty| wire::ExecutionPtyOptions { + cols: pty.cols, + rows: pty.rows, + }), + } +} + +fn json_inputs(options: &InlineExecutionOptions) -> ClientResult> { + options + .inputs + .as_ref() + .map(serde_json::to_string) + .transpose() + .map_err(|error| ClientError::Sidecar(format!("failed to serialize inputs: {error}"))) +} + +impl AgentOs { + fn execution_ownership(&self) -> wire::OwnershipScope { + let inner = self.inner(); + wire::OwnershipScope::VmOwnership(wire::VmOwnership { + connection_id: inner.connection_id.clone(), + session_id: inner.session_id.clone(), + vm_id: inner.vm_id.clone(), + }) + } + + async fn submit_execution( + &self, + payload: wire::RequestPayload, + detached: bool, + ) -> ClientResult { + let mut events = self.transport().subscribe_wire_events(); + let accepted = match self + .transport() + .request_wire(self.execution_ownership(), payload) + .await? + { + wire::ResponsePayload::ExecutionAcceptedResponse(response) => response.execution, + wire::ResponsePayload::RejectedResponse(rejected) => { + return Err(ClientError::from_rejection(rejected)) + } + response => { + return Err(ClientError::Sidecar(format!( + "unexpected execution response: {response:?}" + ))) + } + }; + if detached { + return Ok(ExecutionSubmission::Detached(accepted)); + } + wait_for_completion_event(&mut events, &accepted.execution_id).await?; + Ok(ExecutionSubmission::Completed( + self.wait_execution(&accepted.execution_id).await?, + )) + } + + pub async fn exec( + &self, + command: impl Into, + options: LanguageExecutionOptions, + ) -> ClientResult { + let detached = options.detached; + self.submit_execution( + wire::RequestPayload::ShellExecutionRequest(wire::ShellExecutionRequest { + process: process(&options), + command: command.into(), + }), + detached, + ) + .await + } + + pub async fn exec_argv( + &self, + command: impl Into, + args: Vec, + mut options: LanguageExecutionOptions, + ) -> ClientResult { + options.args = args; + let detached = options.detached; + self.submit_execution( + wire::RequestPayload::ArgvExecutionRequest(wire::ArgvExecutionRequest { + process: process(&options), + command: command.into(), + }), + detached, + ) + .await + } + + pub async fn spawn( + &self, + command: impl Into, + args: Vec, + mut options: LanguageExecutionOptions, + ) -> ClientResult { + options.args = args; + options.detached = true; + match self + .exec_argv(command, options.args.clone(), options) + .await? + { + ExecutionSubmission::Detached(descriptor) => Ok(descriptor), + ExecutionSubmission::Completed(_) => Err(ClientError::Sidecar(String::from( + "spawn unexpectedly returned an attached result", + ))), + } + } + + pub async fn execute_javascript( + &self, + source: impl Into, + options: JavaScriptExecutionOptions, + ) -> ClientResult { + let inputs = json_inputs(&options.inline)?; + self.submit_execution( + wire::RequestPayload::JavaScriptExecutionRequest(wire::JavaScriptExecutionRequest { + process: process(&options.inline.process), + source: source.into(), + format: Some(match options.format { + JavaScriptModuleFormat::Module => wire::JavaScriptModuleFormat::Module, + JavaScriptModuleFormat::CommonJs => wire::JavaScriptModuleFormat::CommonJs, + }), + file_path: options.file_path, + inputs, + }), + options.inline.process.detached, + ) + .await + } + + pub async fn evaluate_javascript( + &self, + expression: impl Into, + options: JavaScriptExecutionOptions, + ) -> ClientResult { + let inputs = json_inputs(&options.inline)?; + let submission = self + .submit_execution( + wire::RequestPayload::JavaScriptEvaluationRequest( + wire::JavaScriptEvaluationRequest { + process: process(&options.inline.process), + expression: expression.into(), + format: Some(match options.format { + JavaScriptModuleFormat::Module => wire::JavaScriptModuleFormat::Module, + JavaScriptModuleFormat::CommonJs => { + wire::JavaScriptModuleFormat::CommonJs + } + }), + file_path: options.file_path, + inputs, + }, + ), + false, + ) + .await?; + evaluation_result(submission) + } + + pub async fn execute_javascript_file( + &self, + path: impl Into, + options: LanguageExecutionOptions, + ) -> ClientResult { + let detached = options.detached; + self.submit_execution( + wire::RequestPayload::JavaScriptFileExecutionRequest( + wire::JavaScriptFileExecutionRequest { + process: process(&options), + path: path.into(), + }, + ), + detached, + ) + .await + } + + pub async fn execute_typescript( + &self, + source: impl Into, + options: TypeScriptExecutionOptions, + ) -> ClientResult { + let inputs = json_inputs(&options.inline)?; + self.submit_execution( + wire::RequestPayload::TypeScriptExecutionRequest(wire::TypeScriptExecutionRequest { + process: process(&options.inline.process), + source: source.into(), + file_path: options.file_path, + tsconfig_path: options.tsconfig_path, + compiler_options: options + .compiler_options + .as_ref() + .map(serde_json::to_string) + .transpose() + .map_err(|error| ClientError::Sidecar(error.to_string()))?, + inputs, + }), + options.inline.process.detached, + ) + .await + } + + pub async fn evaluate_typescript( + &self, + expression: impl Into, + options: TypeScriptExecutionOptions, + ) -> ClientResult { + let inputs = json_inputs(&options.inline)?; + let submission = self + .submit_execution( + wire::RequestPayload::TypeScriptEvaluationRequest( + wire::TypeScriptEvaluationRequest { + process: process(&options.inline.process), + expression: expression.into(), + file_path: options.file_path, + tsconfig_path: options.tsconfig_path, + compiler_options: options + .compiler_options + .as_ref() + .map(serde_json::to_string) + .transpose() + .map_err(|error| ClientError::Sidecar(error.to_string()))?, + inputs, + }, + ), + false, + ) + .await?; + evaluation_result(submission) + } + + pub async fn execute_typescript_file( + &self, + path: impl Into, + options: TypeScriptExecutionOptions, + ) -> ClientResult { + let detached = options.inline.process.detached; + self.submit_execution( + wire::RequestPayload::TypeScriptFileExecutionRequest( + wire::TypeScriptFileExecutionRequest { + process: process(&options.inline.process), + path: path.into(), + tsconfig_path: options.tsconfig_path, + compiler_options: options + .compiler_options + .as_ref() + .map(serde_json::to_string) + .transpose() + .map_err(|error| ClientError::Sidecar(error.to_string()))?, + }, + ), + detached, + ) + .await + } + + pub async fn check_typescript( + &self, + source: impl Into, + options: TypeScriptCheckOptions, + ) -> ClientResult { + let compiler_options = options + .compiler_options + .as_ref() + .map(serde_json::to_string) + .transpose() + .map_err(|error| ClientError::Sidecar(error.to_string()))?; + let submission = self + .submit_execution( + wire::RequestPayload::TypeScriptCheckRequest(wire::TypeScriptCheckRequest { + identity: wire::ExecutionIdentityOptions { + execution_id: options.execution_id, + create_if_missing: options.create_if_missing, + }, + source: source.into(), + cwd: options.cwd, + file_path: options.file_path, + tsconfig_path: options.tsconfig_path, + compiler_options, + timeout_ms: options.timeout_ms, + }), + false, + ) + .await?; + let result = completed_submission(submission)?; + typescript_check_result(result) + } + + pub async fn check_typescript_project( + &self, + options: TypeScriptCheckOptions, + ) -> ClientResult { + let submission = self + .submit_execution( + wire::RequestPayload::TypeScriptProjectCheckRequest( + wire::TypeScriptProjectCheckRequest { + identity: wire::ExecutionIdentityOptions { + execution_id: options.execution_id, + create_if_missing: options.create_if_missing, + }, + cwd: options.cwd, + tsconfig_path: options.tsconfig_path, + timeout_ms: options.timeout_ms, + }, + ), + false, + ) + .await?; + let result = completed_submission(submission)?; + typescript_check_result(result) + } + + pub async fn install_npm_project( + &self, + options: NpmProjectInstallOptions, + ) -> ClientResult { + completed_submission( + self.submit_execution( + wire::RequestPayload::NpmProjectInstallRequest(wire::NpmProjectInstallRequest { + identity: wire::ExecutionIdentityOptions { + execution_id: options.execution_id, + create_if_missing: options.create_if_missing, + }, + cwd: options.cwd, + env: (!options.env.is_empty()).then_some(options.env), + timeout_ms: options.timeout_ms, + frozen: options.frozen, + }), + false, + ) + .await?, + ) + } + + pub async fn install_npm_packages( + &self, + packages: Vec, + options: NpmPackageInstallOptions, + ) -> ClientResult { + completed_submission( + self.submit_execution( + wire::RequestPayload::NpmPackageInstallRequest(wire::NpmPackageInstallRequest { + identity: wire::ExecutionIdentityOptions { + execution_id: options.execution_id, + create_if_missing: options.create_if_missing, + }, + cwd: options.cwd, + env: (!options.env.is_empty()).then_some(options.env), + timeout_ms: options.timeout_ms, + packages, + dev: options.dev, + global: options.global, + }), + false, + ) + .await?, + ) + } + + pub async fn execute_npm_script( + &self, + script: impl Into, + options: LanguageExecutionOptions, + ) -> ClientResult { + let detached = options.detached; + self.submit_execution( + wire::RequestPayload::NpmScriptExecutionRequest(wire::NpmScriptExecutionRequest { + process: process(&options), + script: script.into(), + }), + detached, + ) + .await + } + + pub async fn execute_npm_package( + &self, + package_spec: impl Into, + binary: Option, + options: LanguageExecutionOptions, + ) -> ClientResult { + let detached = options.detached; + self.submit_execution( + wire::RequestPayload::NpmPackageExecutionRequest(wire::NpmPackageExecutionRequest { + process: process(&options), + package_spec: package_spec.into(), + binary, + }), + detached, + ) + .await + } + + pub async fn execute_python( + &self, + source: impl Into, + options: InlineExecutionOptions, + ) -> ClientResult { + let inputs = json_inputs(&options)?; + self.submit_execution( + wire::RequestPayload::PythonExecutionRequest(wire::PythonExecutionRequest { + process: process(&options.process), + source: source.into(), + inputs, + }), + options.process.detached, + ) + .await + } + + pub async fn evaluate_python( + &self, + expression: impl Into, + options: InlineExecutionOptions, + ) -> ClientResult { + let inputs = json_inputs(&options)?; + let submission = self + .submit_execution( + wire::RequestPayload::PythonEvaluationRequest(wire::PythonEvaluationRequest { + process: process(&options.process), + expression: expression.into(), + inputs, + }), + false, + ) + .await?; + evaluation_result(submission) + } + + pub async fn execute_python_file( + &self, + path: impl Into, + options: LanguageExecutionOptions, + ) -> ClientResult { + let detached = options.detached; + self.submit_execution( + wire::RequestPayload::PythonFileExecutionRequest(wire::PythonFileExecutionRequest { + process: process(&options), + path: path.into(), + }), + detached, + ) + .await + } + + pub async fn execute_python_module( + &self, + module: impl Into, + options: LanguageExecutionOptions, + ) -> ClientResult { + let detached = options.detached; + self.submit_execution( + wire::RequestPayload::PythonModuleExecutionRequest( + wire::PythonModuleExecutionRequest { + process: process(&options), + module: module.into(), + }, + ), + detached, + ) + .await + } + + pub async fn install_python_packages( + &self, + packages: Vec, + options: PythonInstallOptions, + ) -> ClientResult { + if !packages.is_empty() && options.requirements_file.is_some() { + return Err(ClientError::Sidecar(String::from( + "install_python_packages cannot combine packages with requirements_file", + ))); + } + completed_submission( + self.submit_execution( + wire::RequestPayload::PythonInstallRequest(wire::PythonInstallRequest { + identity: wire::ExecutionIdentityOptions { + execution_id: options.execution_id, + create_if_missing: options.create_if_missing, + }, + cwd: options.cwd, + env: (!options.env.is_empty()).then_some(options.env), + timeout_ms: options.timeout_ms, + packages, + upgrade: options.upgrade, + requirements_file: options.requirements_file, + index_url: options.index_url, + extra_index_urls: options.extra_index_urls, + }), + false, + ) + .await?, + ) + } + + pub async fn get_execution(&self, execution_id: &str) -> ClientResult { + match self + .transport() + .request_wire( + self.execution_ownership(), + wire::RequestPayload::GetExecutionRequest(wire::GetExecutionRequest { + execution_id: execution_id.to_owned(), + }), + ) + .await? + { + wire::ResponsePayload::ExecutionDescriptorResponse(response) => Ok(response.execution), + wire::ResponsePayload::RejectedResponse(rejected) => { + Err(ClientError::from_rejection(rejected)) + } + response => Err(ClientError::Sidecar(format!( + "unexpected get_execution response: {response:?}" + ))), + } + } + + pub async fn list_executions(&self) -> ClientResult> { + match self + .transport() + .request_wire( + self.execution_ownership(), + wire::RequestPayload::ListExecutionsRequest, + ) + .await? + { + wire::ResponsePayload::ExecutionListResponse(response) => Ok(response.executions), + wire::ResponsePayload::RejectedResponse(rejected) => { + Err(ClientError::from_rejection(rejected)) + } + response => Err(ClientError::Sidecar(format!( + "unexpected list_executions response: {response:?}" + ))), + } + } + + pub async fn wait_execution(&self, execution_id: &str) -> ClientResult { + let mut events = self.transport().subscribe_wire_events(); + let first = self + .transport() + .request_wire( + self.execution_ownership(), + wire::RequestPayload::WaitExecutionRequest(wire::WaitExecutionRequest { + execution_id: execution_id.to_owned(), + }), + ) + .await?; + let response = match first { + wire::ResponsePayload::RejectedResponse(rejected) + if rejected.code == "execution_busy" => + { + wait_for_completion_event(&mut events, execution_id).await?; + self.transport() + .request_wire( + self.execution_ownership(), + wire::RequestPayload::WaitExecutionRequest(wire::WaitExecutionRequest { + execution_id: execution_id.to_owned(), + }), + ) + .await? + } + response => response, + }; + match response { + wire::ResponsePayload::ExecutionCompletedResponse(response) => Ok(response), + wire::ResponsePayload::RejectedResponse(rejected) => { + Err(ClientError::from_rejection(rejected)) + } + response => Err(ClientError::Sidecar(format!( + "unexpected wait_execution response: {response:?}" + ))), + } + } + + pub async fn cancel_execution(&self, execution_id: &str) -> ClientResult { + self.execution_descriptor_request(wire::RequestPayload::CancelExecutionRequest( + wire::CancelExecutionRequest { + execution_id: execution_id.to_owned(), + }, + )) + .await + } + + pub async fn signal_execution( + &self, + execution_id: &str, + signal: impl Into, + ) -> ClientResult { + self.execution_descriptor_request(wire::RequestPayload::SignalExecutionRequest( + wire::SignalExecutionRequest { + execution_id: execution_id.to_owned(), + signal: signal.into(), + }, + )) + .await + } + + pub async fn reset_execution(&self, execution_id: &str) -> ClientResult { + self.execution_descriptor_request(wire::RequestPayload::ResetExecutionRequest( + wire::ResetExecutionRequest { + execution_id: execution_id.to_owned(), + }, + )) + .await + } + + async fn execution_descriptor_request( + &self, + payload: wire::RequestPayload, + ) -> ClientResult { + match self + .transport() + .request_wire(self.execution_ownership(), payload) + .await? + { + wire::ResponsePayload::ExecutionDescriptorResponse(response) => Ok(response.execution), + wire::ResponsePayload::RejectedResponse(rejected) => { + Err(ClientError::from_rejection(rejected)) + } + response => Err(ClientError::Sidecar(format!( + "unexpected execution lifecycle response: {response:?}" + ))), + } + } + + pub async fn delete_execution(&self, execution_id: &str) -> ClientResult<()> { + match self + .transport() + .request_wire( + self.execution_ownership(), + wire::RequestPayload::DeleteExecutionRequest(wire::DeleteExecutionRequest { + execution_id: execution_id.to_owned(), + }), + ) + .await? + { + wire::ResponsePayload::ExecutionDeletedResponse(_) => Ok(()), + wire::ResponsePayload::RejectedResponse(rejected) => { + Err(ClientError::from_rejection(rejected)) + } + response => Err(ClientError::Sidecar(format!( + "unexpected delete_execution response: {response:?}" + ))), + } + } + + pub async fn read_execution_output( + &self, + execution_id: &str, + cursor: Option, + limit: Option, + ) -> ClientResult { + match self + .transport() + .request_wire( + self.execution_ownership(), + wire::RequestPayload::ReadExecutionOutputRequest( + wire::ReadExecutionOutputRequest { + execution_id: execution_id.to_owned(), + cursor, + limit, + }, + ), + ) + .await? + { + wire::ResponsePayload::ExecutionOutputPageResponse(response) => Ok(response), + wire::ResponsePayload::RejectedResponse(rejected) => { + Err(ClientError::from_rejection(rejected)) + } + response => Err(ClientError::Sidecar(format!( + "unexpected read_execution_output response: {response:?}" + ))), + } + } + + pub async fn write_execution_stdin( + &self, + execution_id: &str, + chunk: Vec, + ) -> ClientResult<()> { + self.execution_io_request(wire::RequestPayload::WriteExecutionStdinRequest( + wire::WriteExecutionStdinRequest { + execution_id: execution_id.to_owned(), + chunk, + }, + )) + .await + } + + pub async fn close_execution_stdin(&self, execution_id: &str) -> ClientResult<()> { + self.execution_io_request(wire::RequestPayload::CloseExecutionStdinRequest( + wire::CloseExecutionStdinRequest { + execution_id: execution_id.to_owned(), + }, + )) + .await + } + + pub async fn resize_execution_pty( + &self, + execution_id: &str, + cols: u16, + rows: u16, + ) -> ClientResult<()> { + self.execution_io_request(wire::RequestPayload::ResizeExecutionPtyRequest( + wire::ResizeExecutionPtyRequest { + execution_id: execution_id.to_owned(), + cols, + rows, + }, + )) + .await + } + + async fn execution_io_request(&self, payload: wire::RequestPayload) -> ClientResult<()> { + match self + .transport() + .request_wire(self.execution_ownership(), payload) + .await? + { + wire::ResponsePayload::ExecutionIoResponse(_) => Ok(()), + wire::ResponsePayload::RejectedResponse(rejected) => { + Err(ClientError::from_rejection(rejected)) + } + response => Err(ClientError::Sidecar(format!( + "unexpected execution I/O response: {response:?}" + ))), + } + } +} + +async fn wait_for_completion_event( + events: &mut broadcast::Receiver<(wire::OwnershipScope, wire::EventPayload)>, + execution_id: &str, +) -> ClientResult<()> { + loop { + match events.recv().await { + Ok((_, wire::EventPayload::ExecutionCompletedEvent(event))) + if event.execution_id == execution_id => + { + return Ok(()) + } + Ok(_) | Err(broadcast::error::RecvError::Lagged(_)) => {} + Err(broadcast::error::RecvError::Closed) => { + return Err(ClientError::Sidecar(String::from( + "execution event stream closed before completion", + ))) + } + } + } +} + +fn evaluation_result(submission: ExecutionSubmission) -> ClientResult { + let ExecutionSubmission::Completed(result) = submission else { + return Err(ClientError::Sidecar(String::from( + "evaluation unexpectedly returned detached execution", + ))); + }; + let value = serde_json::from_str::(&result.outputs) + .ok() + .and_then(|outputs| outputs.as_array().cloned()) + .and_then(|outputs| { + outputs + .into_iter() + .find_map(|output| output.get("data").cloned()) + }); + Ok(CodeEvaluationResult { result, value }) +} + +fn typescript_check_result(result: CodeExecutionResult) -> ClientResult { + if result.outcome != wire::ExecutionOutcome::Succeeded { + return Ok(TypeScriptCheckResult { + result, + has_errors: None, + diagnostics: Vec::new(), + }); + } + let outputs: serde_json::Value = serde_json::from_str(&result.outputs).map_err(|error| { + ClientError::Sidecar(format!( + "failed to decode TypeScript checker output: {error}" + )) + })?; + let data = outputs + .as_array() + .and_then(|outputs| outputs.iter().find_map(|output| output.get("data"))) + .and_then(serde_json::Value::as_object) + .ok_or_else(|| { + ClientError::Sidecar(String::from( + "TypeScript checker returned no diagnostic result", + )) + })?; + let has_errors = data + .get("hasErrors") + .and_then(serde_json::Value::as_bool) + .ok_or_else(|| { + ClientError::Sidecar(String::from( + "TypeScript checker returned an invalid hasErrors value", + )) + })?; + let diagnostics = data + .get("diagnostics") + .and_then(serde_json::Value::as_array) + .ok_or_else(|| { + ClientError::Sidecar(String::from( + "TypeScript checker returned invalid diagnostics", + )) + })? + .iter() + .map(|diagnostic| { + let code = diagnostic + .get("code") + .and_then(serde_json::Value::as_u64) + .and_then(|code| u32::try_from(code).ok()) + .ok_or_else(|| { + ClientError::Sidecar(String::from( + "TypeScript checker returned an invalid diagnostic code", + )) + })?; + let category = diagnostic + .get("category") + .and_then(serde_json::Value::as_str) + .ok_or_else(|| { + ClientError::Sidecar(String::from( + "TypeScript checker returned an invalid diagnostic category", + )) + })? + .to_owned(); + let message = diagnostic + .get("message") + .and_then(serde_json::Value::as_str) + .ok_or_else(|| { + ClientError::Sidecar(String::from( + "TypeScript checker returned an invalid diagnostic message", + )) + })? + .to_owned(); + Ok(wire::TypeScriptDiagnostic { + code, + category, + message, + file_path: diagnostic + .get("filePath") + .and_then(serde_json::Value::as_str) + .map(str::to_owned), + line: diagnostic + .get("line") + .and_then(serde_json::Value::as_u64) + .and_then(|line| u32::try_from(line).ok()), + column: diagnostic + .get("column") + .and_then(serde_json::Value::as_u64) + .and_then(|column| u32::try_from(column).ok()), + }) + }) + .collect::>>()?; + Ok(TypeScriptCheckResult { + result, + has_errors: Some(has_errors), + diagnostics, + }) +} + +fn completed_submission(submission: ExecutionSubmission) -> ClientResult { + match submission { + ExecutionSubmission::Completed(result) => Ok(result), + ExecutionSubmission::Detached(_) => Err(ClientError::Sidecar(String::from( + "attached operation unexpectedly returned a detached execution", + ))), + } +} diff --git a/crates/client/src/lib.rs b/crates/client/src/lib.rs index c0389caab2..c58be5f377 100644 --- a/crates/client/src/lib.rs +++ b/crates/client/src/lib.rs @@ -8,7 +8,7 @@ //! //! The client spawns the native `agentos-sidecar` binary and speaks the existing framed BARE //! protocol over its stdio (see [`transport`]). It does NOT embed the kernel in-process and does NOT -//! define a new sidecar wire protocol. The generated Secure Exec schema surface comes from +//! define a new sidecar wire protocol. The generated AgentOS language execution schema surface comes from //! `agentos_sidecar_client::wire`; Agent OS layers ACP/session semantics on top of those generated wire //! frames through the wrapper client. //! @@ -21,6 +21,7 @@ pub mod config; pub mod cron; pub mod error; pub mod fs; +pub mod language_execution; pub mod net; pub mod process; pub mod session; @@ -53,6 +54,12 @@ pub const CRON_JOB_LIMIT: usize = 1024; pub use agent_os::{AgentOs, PackageDescriptor, ProjectedAgent, SoftwareInfo}; pub use error::{ClientError, ClientResult, ResourceLimitDetails}; +pub use language_execution::{ + CodeEvaluationResult, CodeExecutionResult, ExecutionCompletedEvent, ExecutionDescriptor, + ExecutionOutputEvent, ExecutionOutputPage, ExecutionPtyOptions, ExecutionSubmission, + InlineExecutionOptions, JavaScriptExecutionOptions, JavaScriptModuleFormat, + LanguageExecutionOptions, TypeScriptDiagnostic, +}; pub use sidecar::{ AgentOsSidecar, AgentOsSidecarDescription, AgentOsSidecarPlacement, SidecarState, }; diff --git a/crates/client/src/process.rs b/crates/client/src/process.rs index 604c058830..b03ca545a0 100644 --- a/crates/client/src/process.rs +++ b/crates/client/src/process.rs @@ -216,12 +216,12 @@ impl AgentOs { /// process id immediately; stdout/stderr are accumulated and the call resolves once the matching /// `ProcessExited` event arrives. This mirrors the TS pass-through to `kernel.exec` semantically: /// the result is the full captured stdout/stderr plus exit code. - pub async fn exec(&self, command: &str, options: ExecOptions) -> Result { + pub async fn exec_process(&self, command: &str, options: ExecOptions) -> Result { // Parse the command line into a `(command, args)` pair the same way the sidecar's // child_process path does: shell-free argv lists spawn directly (preserving the command's // real exit code), while shell syntax or a builtin head runs under `sh -c `. let (resolved_command, resolved_args) = resolve_exec_command(command)?; - self.exec_argv(&resolved_command, &resolved_args, options) + self.exec_argv_process(&resolved_command, &resolved_args, options) .await } @@ -230,7 +230,7 @@ impl AgentOs { /// no whitespace re-splitting, no shell metacharacter detection, and no routing through /// `sh -c`. Callers that already hold a structured argv (for example the cron `Exec` action) /// must use this so the structured-argv contract is preserved end to end. - pub async fn exec_argv( + pub async fn exec_argv_process( &self, command: &str, args: &[String], @@ -377,6 +377,8 @@ impl AgentOs { } EventPayload::ProcessOutputEvent(_) | EventPayload::ProcessExitedEvent(_) + | EventPayload::ExecutionOutputEvent(_) + | EventPayload::ExecutionCompletedEvent(_) | EventPayload::VmLifecycleEvent(_) | EventPayload::StructuredEvent(_) | EventPayload::ExtEnvelope(_) => {} @@ -397,7 +399,7 @@ impl AgentOs { /// Spawn a process. SYNC; returns `{ pid }` only. Installs stdout/stderr fan-out over broadcast /// channels and wires exit via a background event-pump task. The user-facing `pid` is the /// SDK-allocated map key (the wire `process_id` is held inside the [`ProcessEntry`]). - pub fn spawn( + pub fn spawn_process( &self, command: &str, args: Vec, @@ -1118,6 +1120,8 @@ impl AgentOs { } EventPayload::ProcessOutputEvent(_) | EventPayload::ProcessExitedEvent(_) + | EventPayload::ExecutionOutputEvent(_) + | EventPayload::ExecutionCompletedEvent(_) | EventPayload::VmLifecycleEvent(_) | EventPayload::StructuredEvent(_) | EventPayload::ExtEnvelope(_) => {} diff --git a/crates/client/src/shell.rs b/crates/client/src/shell.rs index a2a9f5a41f..fc0e2ccc9b 100644 --- a/crates/client/src/shell.rs +++ b/crates/client/src/shell.rs @@ -376,6 +376,8 @@ impl AgentOs { } } EventPayload::VmLifecycleEvent(_) + | EventPayload::ExecutionOutputEvent(_) + | EventPayload::ExecutionCompletedEvent(_) | EventPayload::StructuredEvent(_) | EventPayload::ExtEnvelope(_) => {} } @@ -512,6 +514,8 @@ impl AgentOs { } } EventPayload::VmLifecycleEvent(_) + | EventPayload::ExecutionOutputEvent(_) + | EventPayload::ExecutionCompletedEvent(_) | EventPayload::StructuredEvent(_) | EventPayload::ExtEnvelope(_) => {} } @@ -644,6 +648,8 @@ impl AgentOs { } } EventPayload::VmLifecycleEvent(_) + | EventPayload::ExecutionOutputEvent(_) + | EventPayload::ExecutionCompletedEvent(_) | EventPayload::StructuredEvent(_) | EventPayload::ExtEnvelope(_) => {} } diff --git a/crates/client/tests/common/mod.rs b/crates/client/tests/common/mod.rs index fcebfbf55b..e8536ef63a 100644 --- a/crates/client/tests/common/mod.rs +++ b/crates/client/tests/common/mod.rs @@ -147,7 +147,7 @@ fn wasm_command_mounts() -> Vec { }; vec![MountConfig::Native { - path: "/__secure_exec/commands/0".to_string(), + path: "/__agentos/commands/0".to_string(), plugin: MountPlugin { id: "host_dir".to_string(), config: Some(serde_json::json!({ @@ -243,7 +243,7 @@ pub async fn new_vm_with_commands() -> Option { /// registry WASM command packages are absent (the common case in unbuilt trees), so the /// process/shell/fetch suites can gate cleanly without each re-implementing the probe. pub async fn wasm_commands_available(os: &AgentOs) -> bool { - os.exec("sh", agentos_client::ExecOptions::default()) + os.exec_process("sh", agentos_client::ExecOptions::default()) .await .is_ok() } diff --git a/crates/client/tests/exec_command_line_e2e.rs b/crates/client/tests/exec_command_line_e2e.rs index c5af5f1cd4..88c8a750a0 100644 --- a/crates/client/tests/exec_command_line_e2e.rs +++ b/crates/client/tests/exec_command_line_e2e.rs @@ -24,7 +24,7 @@ async fn exec_command_line_paths() { // Direct path: a simple command with arguments runs and returns its output. let echo = os - .exec("echo hello world", ExecOptions::default()) + .exec_process("echo hello world", ExecOptions::default()) .await .expect("exec echo"); assert_eq!(echo.exit_code, 0, "echo exit (stderr: {:?})", echo.stderr); @@ -32,7 +32,7 @@ async fn exec_command_line_paths() { // Direct path preserves a real non-zero exit code (the `sh -c` wrapper can swallow it). let missing = os - .exec("cat /no/such/file", ExecOptions::default()) + .exec_process("cat /no/such/file", ExecOptions::default()) .await .expect("exec cat missing returns a result"); assert_ne!( @@ -42,7 +42,7 @@ async fn exec_command_line_paths() { // Shell path: a `&&` chain runs as a single `sh -c` execution. let chain = os - .exec("echo a && echo b", ExecOptions::default()) + .exec_process("echo a && echo b", ExecOptions::default()) .await .expect("exec && chain"); assert_eq!( @@ -54,7 +54,7 @@ async fn exec_command_line_paths() { // Shell path: a redirect writes a file the VM can read back. let redirect = os - .exec( + .exec_process( "echo redirected > /tmp/exec_redirect.txt", ExecOptions::default(), ) @@ -77,14 +77,16 @@ async fn exec_command_line_paths() { // Shell path: a quoted argument with a space stays one token through `sh -c`. let quoted = os - .exec("echo 'a b'", ExecOptions::default()) + .exec_process("echo 'a b'", ExecOptions::default()) .await .expect("exec quoted"); assert_eq!(quoted.stdout.trim_end(), "a b", "quoted stdout"); // An empty command line is an explicit error, not a silent no-op. assert!( - os.exec(" ", ExecOptions::default()).await.is_err(), + os.exec_process(" ", ExecOptions::default()) + .await + .is_err(), "empty command line must error" ); diff --git a/crates/client/tests/fetch_e2e.rs b/crates/client/tests/fetch_e2e.rs index 29aae9dc56..505f5ef0a2 100644 --- a/crates/client/tests/fetch_e2e.rs +++ b/crates/client/tests/fetch_e2e.rs @@ -184,7 +184,7 @@ async fn fetch_surface_get_post_and_headers() { } let server = os - .spawn( + .spawn_process( "node", vec![ "-e".to_string(), diff --git a/crates/client/tests/link_software_e2e.rs b/crates/client/tests/link_software_e2e.rs index 4c9df70dff..92bae6afb0 100644 --- a/crates/client/tests/link_software_e2e.rs +++ b/crates/client/tests/link_software_e2e.rs @@ -76,7 +76,7 @@ async fn link_software_makes_command_resolve_live() { let captured = Arc::new(Mutex::new(Vec::::new())); let err_cap = Arc::new(Mutex::new(Vec::::new())); let handle = os - .spawn("linked-cmd", Vec::new(), SpawnOptions::default()) + .spawn_process("linked-cmd", Vec::new(), SpawnOptions::default()) .expect("spawn linked-cmd"); let cb = captured.clone(); let ecb = err_cap.clone(); diff --git a/crates/client/tests/native_root_mount_e2e.rs b/crates/client/tests/native_root_mount_e2e.rs index a13f9a2817..71fdd0831d 100644 --- a/crates/client/tests/native_root_mount_e2e.rs +++ b/crates/client/tests/native_root_mount_e2e.rs @@ -556,7 +556,7 @@ async fn native_root_mount_files_visible_to_wasm_commands() { // The regression: guest WASM commands must observe the mount-backed root // content, not an empty/broken view. let cat = os - .exec("cat /workspace/data.txt", ExecOptions::default()) + .exec_process("cat /workspace/data.txt", ExecOptions::default()) .await .expect("exec cat"); assert_eq!( @@ -572,7 +572,7 @@ async fn native_root_mount_files_visible_to_wasm_commands() { ); let wc = os - .exec("wc -c /workspace/data.txt", ExecOptions::default()) + .exec_process("wc -c /workspace/data.txt", ExecOptions::default()) .await .expect("exec wc"); assert_eq!( @@ -589,7 +589,7 @@ async fn native_root_mount_files_visible_to_wasm_commands() { // A command spawned by the guest shell must retain the same mounted root. let sh_absolute = os - .exec("sh -c 'cat /workspace/data.txt'", ExecOptions::default()) + .exec_process("sh -c 'cat /workspace/data.txt'", ExecOptions::default()) .await .expect("exec sh absolute cat"); assert_eq!( @@ -601,7 +601,7 @@ async fn native_root_mount_files_visible_to_wasm_commands() { // Shell traversal into the mounted root must work too. let sh = os - .exec( + .exec_process( "sh -c 'cd /workspace && cat data.txt'", ExecOptions::default(), ) @@ -615,7 +615,7 @@ async fn native_root_mount_files_visible_to_wasm_commands() { assert_eq!(sh.stdout.trim_end(), "hello-bridge-root"); let relative_write = os - .exec( + .exec_process( "sh -c 'cd /workspace && echo relative-write > relative.txt'", ExecOptions::default(), ) @@ -635,7 +635,7 @@ async fn native_root_mount_files_visible_to_wasm_commands() { // Guest writes must round-trip back to the host view as well. let write = os - .exec( + .exec_process( "sh -c 'echo guest-write > /workspace/out.txt'", ExecOptions::default(), ) diff --git a/crates/client/tests/packages_aospkg_e2e.rs b/crates/client/tests/packages_aospkg_e2e.rs index a7aff0e832..319fcfb9b3 100644 --- a/crates/client/tests/packages_aospkg_e2e.rs +++ b/crates/client/tests/packages_aospkg_e2e.rs @@ -31,7 +31,7 @@ async fn spawn_capture(os: &AgentOs, cmd: &str, args: Vec) -> (i32, Stri let captured = Arc::new(Mutex::new(Vec::::new())); let err_cap = Arc::new(Mutex::new(Vec::::new())); let handle = os - .spawn(cmd, args, SpawnOptions::default()) + .spawn_process(cmd, args, SpawnOptions::default()) .unwrap_or_else(|e| panic!("spawn {cmd}: {e:?}")); let cb = captured.clone(); let ecb = err_cap.clone(); diff --git a/crates/client/tests/process_e2e.rs b/crates/client/tests/process_e2e.rs index ff7242f055..93bb92bed6 100644 --- a/crates/client/tests/process_e2e.rs +++ b/crates/client/tests/process_e2e.rs @@ -95,7 +95,7 @@ async fn process_surface_exec_spawn_and_snapshot() { // `exec` forwards only the `command` field (no args), so to get deterministic stdout we use a // command that echoes its stdin: `cat` round-trips its input to stdout and exits 0 on EOF. let echoed = os - .exec( + .exec_process( "cat", ExecOptions { stdin: Some(StdinInput::Text("hello-stdout".to_string())), @@ -115,7 +115,7 @@ async fn process_surface_exec_spawn_and_snapshot() { let streamed = Arc::new(Mutex::new(Vec::::new())); let streamed_cb = Arc::clone(&streamed); let res = os - .exec( + .exec_process( "cat", ExecOptions { stdin: Some(StdinInput::Text("stream-me".to_string())), @@ -142,7 +142,7 @@ async fn process_surface_exec_spawn_and_snapshot() { let captured_cb = Arc::clone(&captured); let bin_input = binary.clone(); let res = os - .exec( + .exec_process( "cat", ExecOptions { stdin: Some(StdinInput::Bytes(bin_input)), @@ -163,7 +163,7 @@ async fn process_surface_exec_spawn_and_snapshot() { // --- spawn: pid + stdin write + stdout stream + exit wait ------------------------------------- let handle = os - .spawn("cat", Vec::new(), SpawnOptions::default()) + .spawn_process("cat", Vec::new(), SpawnOptions::default()) .expect("spawn cat"); assert!( handle.pid >= 1_000_000, diff --git a/crates/client/tests/wasm_command_mount_e2e.rs b/crates/client/tests/wasm_command_mount_e2e.rs index 2e76534922..3dac5b6c1a 100644 --- a/crates/client/tests/wasm_command_mount_e2e.rs +++ b/crates/client/tests/wasm_command_mount_e2e.rs @@ -30,7 +30,7 @@ async fn wasm_command_software_mounts_into_vm() { // Before the mount fix this failed with "command not found"; before the exec command-line fix // the space made the whole string resolve as one command name. let result = os - .exec("echo hello", ExecOptions::default()) + .exec_process("echo hello", ExecOptions::default()) .await .expect("exec echo hello"); assert_eq!( diff --git a/crates/execution/CLAUDE.md b/crates/execution/CLAUDE.md index 8512bf579d..5b741aff1a 100644 --- a/crates/execution/CLAUDE.md +++ b/crates/execution/CLAUDE.md @@ -14,7 +14,7 @@ If tests fail because they were written for the old `Command::new("node")` path, ## Node.js Isolation Model -**Desired state:** Guest JS/TS runs inside isolated V8 contexts managed by the execution engine. All Node.js builtins (`fs`, `net`, `child_process`, `dns`, `http`, `os`, etc.) are kernel-backed polyfills that route through the kernel VFS, socket table, and process table. Module loading is fully intercepted — guest code never touches real host APIs. The execution engine previously had this working via `@secure-exec/core` + `@secure-exec/nodejs` with full kernel-backed polyfills for all builtins. +**Desired state:** Guest JS/TS runs inside isolated V8 contexts managed by the execution engine. All Node.js builtins (`fs`, `net`, `child_process`, `dns`, `http`, `os`, etc.) are kernel-backed polyfills that route through the kernel VFS, socket table, and process table. Module loading is fully intercepted — guest code never touches real host APIs. The previous JavaScript kernel packages had full kernel-backed polyfills for all builtins. **Current state (⚠️ STILL INCOMPLETE -- see `~/.agents/todo/node-isolation-gaps.md`):** @@ -23,7 +23,7 @@ Guest JavaScript entrypoints in `javascript.rs` now run only through the shared - Keep any real-host Node helpers isolated to clearly host-only modules used by benchmarks or import-cache tests. Guest JS/WASM/Python runtime code should depend only on neutral shared helpers (for example signal metadata or path resolution), not on files that also own host launch behavior. - Guest-side WebAssembly inside the V8 isolate must stay enabled on both fresh isolates and snapshot restores. Real npm packages rely on `WebAssembly.Module`, `WebAssembly.Instance`, and `WebAssembly.instantiate*`, and allowing those APIs does not violate the kernel-isolation boundary because compilation stays inside the isolate. Do not reintroduce an embedder callback that blocks WASM; rely on V8's own implementation limits instead. -**Recovery reference:** The complete working polyfill + V8 isolate code from the original `@secure-exec/core` + `@secure-exec/nodejs` + `@secure-exec/v8` packages has been recovered to `~/.agents/recovery/secure-exec/`. Key files to port: +**Recovery reference:** The complete working polyfill + V8 isolate code from the original JavaScript runtime packages has been recovered to `~/.agents/recovery/agentos/`. Key files to port: - `nodejs/src/bridge/fs.ts` (3,974 lines) -- full kernel-backed `fs`/`fs/promises` polyfill - `nodejs/src/bridge/network.ts` (11,149 lines) -- full `net`/`dgram`/`dns` polyfill via kernel socket table - `nodejs/src/bridge/child-process.ts` (1,058 lines) -- `child_process` polyfill via kernel process table @@ -34,11 +34,11 @@ Guest JavaScript entrypoints in `javascript.rs` now run only through the shared - `kernel/` -- the JS kernel (VFS, process table, socket table, PTY, pipes) - `v8/` -- V8 runtime process manager, IPC binary protocol -The original source repo is at `/home/nathan/secure-exec-1/` (tagged `v0.2.1`). +The original source repo is at `/home/nathan/agentos-1/` (tagged `v0.2.1`). **Prior art -- the original JS kernel had full polyfills:** -Before the Rust sidecar (commit `5a43882`), the JS kernel (`@secure-exec/core` + `@secure-exec/nodejs` + `packages/posix/`) had complete kernel-backed polyfills for all builtins. The pattern was: +Before the Rust sidecar (commit `5a43882`), the JavaScript kernel and `packages/posix/` had complete kernel-backed polyfills for all builtins. The pattern was: - **Kernel socket table** -- `kernel.socketTable.create/connect/send/recv` managed all TCP/UDP. Loopback stayed in-kernel; external connections went through a `HostNetworkAdapter`. - **Kernel VFS** -- All `fs` operations routed through the kernel VFS via syscall RPC. - **Kernel process table** -- `child_process.spawn` routed through `kernel.spawn()`. @@ -114,7 +114,7 @@ The host-support ESM loader hook (`loader.mjs`) is generated from a Rust string ## Runner Script Assets - Execution-host runner scripts materialized by `NodeImportCache` should live as checked-in assets under `crates/execution/assets/runners/` and be loaded via `include_str!`. -- The stdlib-backed V8 bridge bundle is generated from `packages/build-tools/bridge-src/` into Cargo `OUT_DIR`; `pnpm --dir packages/secure-exec-core build:v8-bridge` is only for manual debugging. Keep the heavier assert/util/zlib payload in `v8-bridge-zlib.js` so the main `v8-bridge.js` stays below the 500KB cap. +- The stdlib-backed V8 bridge bundle is generated from `packages/build-tools/bridge-src/` into Cargo `OUT_DIR`; `pnpm --dir packages/agentos-core build:v8-bridge` is only for manual debugging. Keep the heavier assert/util/zlib payload in `v8-bridge-zlib.js` so the main `v8-bridge.js` stays below the 500KB cap. - Guest `os` virtualization has two env surfaces: public `process.env` is intentionally scrubbed of `AGENTOS_*`, while the real per-execution values live in the hidden runtime env (`globalThis.__agentOSProcessConfigEnv` in `javascript.rs`, mirrored from the sidecar's `prepare_guest_runtime_env(...)`). If `bridge-src` needs VM-scoped CPU/memory/home metadata, read that hidden env path or `_processConfig.env` rather than the sanitized public env, and keep it aligned with `node_import_cache.rs`. - When `build:v8-bridge` pulls deeper undici API modules (for example `undici/lib/api/*`), keep `packages/build-tools/scripts/build-v8-bridge.mjs` aliasing any extra Node builtins they require to standalone shim files under `crates/execution/assets/undici-shims/`; those imports execute while the bundle is still bootstrapping, so they cannot depend on later `exposeCustomGlobal(...)` wiring like `_asyncHooksModule`. - Keep `http` and `https` default agents scoped to their own module instances inside `packages/build-tools/bridge-src/`; sharing a single global default agent makes `http.request()` inherit HTTPS TLS behavior. Guest-local loopback TLS upgrades must also short-circuit inside the bridge instead of calling `net.socket_upgrade_tls`, because loopback fast-path sockets never have a kernel socket id. diff --git a/crates/execution/Cargo.toml b/crates/execution/Cargo.toml index c772bb9f63..f7b05b573f 100644 --- a/crates/execution/Cargo.toml +++ b/crates/execution/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -description = "Native execution plane scaffold for secure-exec" +description = "Native execution plane scaffold for agentos" build = "build.rs" # Large Pyodide runtime assets are staged at build time (copied from the # in-tree assets directory during workspace builds, or downloaded from the diff --git a/crates/execution/assets/pyodide/README.md b/crates/execution/assets/pyodide/README.md index 5a59900469..d5fb73cf19 100644 --- a/crates/execution/assets/pyodide/README.md +++ b/crates/execution/assets/pyodide/README.md @@ -1,4 +1,4 @@ -Pyodide runtime bundle for the secure-exec Python sidecar. +Pyodide runtime bundle for the agentos Python sidecar. Bundled runtime files: - `pyodide.mjs` @@ -26,7 +26,7 @@ Bundle size as vendored in this directory: 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. -- `await micropip.install("https://.../package.whl")` goes through the Python runner's bridge-backed fetch path, which means network permissions are enforced by the secure-exec kernel rather than bypassing it. +- `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. diff --git a/crates/execution/assets/runners/python-runner.mjs b/crates/execution/assets/runners/python-runner.mjs index ebea35b7f0..1a200f6825 100644 --- a/crates/execution/assets/runners/python-runner.mjs +++ b/crates/execution/assets/runners/python-runner.mjs @@ -19,13 +19,13 @@ const PYTHON_MODULE_ENV = 'AGENTOS_PYTHON_MODULE'; const PYTHON_STDIN_PROGRAM_ENV = 'AGENTOS_PYTHON_STDIN_PROGRAM'; const PYTHON_INTERACTIVE_ENV = 'AGENTOS_PYTHON_INTERACTIVE'; const PYTHON_PREWARM_ONLY_ENV = 'AGENTOS_PYTHON_PREWARM_ONLY'; +const RETAIN_LANGUAGE_CONTEXT_ENV = 'AGENTOS_RETAIN_LANGUAGE_CONTEXT'; const PYTHON_WARMUP_DEBUG_ENV = 'AGENTOS_PYTHON_WARMUP_DEBUG'; const PYTHON_WARMUP_METRICS_PREFIX = '__AGENTOS_PYTHON_WARMUP_METRICS__:'; const PYTHON_PRELOAD_PACKAGES_ENV = 'AGENTOS_PYTHON_PRELOAD_PACKAGES'; const PYTHON_VFS_RPC_REQUEST_FD_ENV = 'AGENTOS_PYTHON_VFS_RPC_REQUEST_FD'; const PYTHON_VFS_RPC_RESPONSE_FD_ENV = 'AGENTOS_PYTHON_VFS_RPC_RESPONSE_FD'; const FORWARD_KERNEL_STDIN_RPC_ENV = 'AGENTOS_FORWARD_KERNEL_STDIN_RPC'; -const PYTHON_RUNTIME_ENV_NAMES = ['HOME', 'USER', 'LOGNAME', 'SHELL', 'PWD', 'TMPDIR', 'PATH']; const INTERNAL_ENV = globalThis.__agentOSPythonInternalEnv ?? Object.create(null); const ALLOW_PROCESS_BINDINGS = readRunnerEnv('AGENTOS_ALLOW_PROCESS_BINDINGS') === '1'; const STDIN_FD = 0; @@ -408,7 +408,7 @@ async function normalizeFetchBody(body) { return Buffer.from(await body.arrayBuffer()).toString('base64'); } - throw new Error('unsupported fetch body type for secure-exec Python package loading'); + throw new Error('unsupported fetch body type for agentos Python package loading'); } function emitPythonStartupMetrics({ @@ -714,7 +714,7 @@ function createPythonFdRpcBridge() { const chunk = Buffer.alloc(4096); const bytesRead = readSync(responseFd, chunk, 0, chunk.length, null); if (bytesRead === 0) { - throw new Error('secure-exec Python VFS RPC response channel closed unexpectedly'); + throw new Error('agentos Python VFS RPC response channel closed unexpectedly'); } responseBuffer += chunk.subarray(0, bytesRead).toString('utf8'); } @@ -724,7 +724,7 @@ function createPythonFdRpcBridge() { try { return JSON.parse(line); } catch (error) { - throw new Error(`invalid secure-exec Python VFS RPC response: ${formatError(error)}`); + throw new Error(`invalid agentos Python VFS RPC response: ${formatError(error)}`); } } @@ -765,7 +765,7 @@ function createPythonFdRpcBridge() { return message.result ?? {}; } - const error = new Error(message?.error?.message || `secure-exec Python VFS RPC request ${id} failed`); + const error = new Error(message?.error?.message || `agentos Python VFS RPC request ${id} failed`); error.code = message?.error?.code || 'ERR_AGENTOS_PYTHON_VFS_RPC'; throw error; } @@ -897,7 +897,7 @@ function createPythonFdRpcBridge() { } function accessDenied(subject) { - const error = new Error(`${subject} is not available in the secure-exec guest Python runtime`); + const error = new Error(`${subject} is not available in the agentos guest Python runtime`); error.code = ACCESS_DENIED_CODE; return error; } @@ -917,9 +917,9 @@ except Exception: _agentos_safe_pyodide_js_api = None def _agentos_raise_access_denied(module_name): - raise RuntimeError(f"{module_name} is not available in the secure-exec guest Python runtime") + raise RuntimeError(f"{module_name} is not available in the agentos guest Python runtime") -class _SecureExecBlockedModule(_agentos_types.ModuleType): +class _AgentOsBlockedModule(_agentos_types.ModuleType): def __init__(self, name): super().__init__(name) self.__dict__['__all__'] = () @@ -931,7 +931,7 @@ class _SecureExecBlockedModule(_agentos_types.ModuleType): return [] _agentos_blocked_modules = { - _agentos_module_name: _SecureExecBlockedModule(_agentos_module_name) + _agentos_module_name: _AgentOsBlockedModule(_agentos_module_name) for _agentos_module_name in ('js', 'pyodide_js') } @@ -967,13 +967,13 @@ import sys as _agentos_sys import types as _agentos_types import urllib.error as _agentos_urllib_error import urllib.request as _agentos_urllib_request -from email.message import Message as _SecureExecMessage +from email.message import Message as _AgentOsMessage from js import __agentOSPythonVfsRpc as _agentos_rpc def _agentos_raise_from_error(error): if not isinstance(error, dict): raise RuntimeError(str(error)) - message = str(error.get("message", "secure-exec Python bridge request failed")) + message = str(error.get("message", "agentos Python bridge request failed")) if "EACCES:" in message: raise PermissionError(message) if "command not found" in message: @@ -998,17 +998,17 @@ def _agentos_dns_lookup(hostname, family=None): _agentos_raise_from_error({"message": str(error)}) addresses = result.get("addresses") or [] if not addresses: - raise OSError(f"secure-exec DNS lookup returned no addresses for {hostname}") + raise OSError(f"agentos DNS lookup returned no addresses for {hostname}") return addresses -class _SecureExecHttpResponse: +class _AgentOsHttpResponse: def __init__(self, payload): self.status = int(payload.get("status", 0)) self.reason = str(payload.get("reason", "")) self.url = str(payload.get("url", "")) self._body = _agentos_base64.b64decode(payload.get("bodyBase64", "") or "") headers = payload.get("headers") or {} - self.headers = _SecureExecMessage() + self.headers = _AgentOsMessage() for name, values in headers.items(): for value in values: self.headers.add_header(str(name), str(value)) @@ -1034,7 +1034,7 @@ class _SecureExecHttpResponse: self.close() return False -class _SecureExecPyfetchResponse: +class _AgentOsPyfetchResponse: def __init__(self, payload): self.status = int(payload.get("status", 0)) self.status_text = str(payload.get("reason", "")) @@ -1079,7 +1079,7 @@ def _agentos_http_request(url_or_request, data=None): ) except Exception as error: _agentos_raise_from_error({"message": str(error)}) - response = _SecureExecHttpResponse(payload) + response = _AgentOsHttpResponse(payload) if response.status >= 400: raise _agentos_urllib_error.HTTPError( url, @@ -1108,7 +1108,7 @@ async def _agentos_pyfetch(url, **kwargs): ) except Exception as error: _agentos_raise_from_error({"message": str(error)}) - return _SecureExecPyfetchResponse(payload) + return _AgentOsPyfetchResponse(payload) def _agentos_urlopen(url, data=None, timeout=None, *args, **kwargs): del timeout, args, kwargs @@ -1143,7 +1143,7 @@ def _agentos_getaddrinfo(host, port, family=0, type=0, proto=0, flags=0): sockaddr = (address, port) results.append((entry_family, socktype, protocol, "", sockaddr)) if not results: - raise OSError(f"secure-exec DNS lookup returned no matching addresses for {host}") + raise OSError(f"agentos DNS lookup returned no matching addresses for {host}") return results def _agentos_gethostbyname(host): @@ -1178,7 +1178,7 @@ def _agentos_socket_rpc(call): except Exception as exc: raise _agentos_socket_oserror(exc) from None -class _SecureExecSocket: +class _AgentOsSocket: def __init__(self, family=None, type=None, proto=0, fileno=None): self.family = family if family is not None else _agentos_socket.AF_INET self.type = type if type is not None else _agentos_socket.SOCK_STREAM @@ -1334,12 +1334,12 @@ def _agentos_socket_factory(family=-1, type=-1, proto=0, fileno=None): and fam in (_agentos_socket.AF_INET, _agentos_socket.AF_INET6) and typ in (_agentos_socket.SOCK_STREAM, _agentos_socket.SOCK_DGRAM) ): - return _SecureExecSocket(fam, typ, proto) + return _AgentOsSocket(fam, typ, proto) return _agentos_original_socket_class(family, type, proto, fileno) _agentos_socket.socket = _agentos_socket_factory -class _SecureExecRequestsResponse: +class _AgentOsRequestsResponse: def __init__(self, payload): self.status_code = int(payload.get("status", 0)) self.reason = str(payload.get("reason", "")) @@ -1360,7 +1360,7 @@ class _SecureExecRequestsResponse: if self.status_code >= 400: raise RuntimeError(f"{self.status_code} {self.reason}") -class _SecureExecRequestsSession: +class _AgentOsRequestsSession: def request(self, method, url, **kwargs): headers = dict(kwargs.get("headers") or {}) data = kwargs.get("data") @@ -1378,16 +1378,16 @@ class _SecureExecRequestsSession: ) except Exception as error: _agentos_raise_from_error({"message": str(error)}) - return _SecureExecRequestsResponse(payload) + return _AgentOsRequestsResponse(payload) def get(self, url, **kwargs): return self.request("GET", url, **kwargs) def _agentos_install_requests_module(): module = _agentos_types.ModuleType("requests") - session = _SecureExecRequestsSession + session = _AgentOsRequestsSession module.Session = session - module.Response = _SecureExecRequestsResponse + module.Response = _AgentOsRequestsResponse module.request = lambda method, url, **kwargs: session().request(method, url, **kwargs) module.get = lambda url, **kwargs: session().get(url, **kwargs) module.exceptions = _agentos_types.SimpleNamespace(RequestException=RuntimeError) @@ -1398,12 +1398,12 @@ try: except ModuleNotFoundError: _agentos_install_requests_module() else: - _agentos_requests.Session = _SecureExecRequestsSession - _agentos_requests.Response = _SecureExecRequestsResponse - _agentos_requests.request = lambda method, url, **kwargs: _SecureExecRequestsSession().request(method, url, **kwargs) - _agentos_requests.get = lambda url, **kwargs: _SecureExecRequestsSession().get(url, **kwargs) + _agentos_requests.Session = _AgentOsRequestsSession + _agentos_requests.Response = _AgentOsRequestsResponse + _agentos_requests.request = lambda method, url, **kwargs: _AgentOsRequestsSession().request(method, url, **kwargs) + _agentos_requests.get = lambda url, **kwargs: _AgentOsRequestsSession().get(url, **kwargs) -class _SecureExecCompletedProcess: +class _AgentOsCompletedProcess: def __init__(self, args, returncode, stdout, stderr): self.args = args self.returncode = returncode @@ -1443,7 +1443,7 @@ def _agentos_subprocess_run(args, *, capture_output=False, check=False, cwd=None merged_env = dict(env or {}) resolved_cwd = cwd if cwd is not None else _agentos_os.environ.get("PWD") if input is not None: - raise NotImplementedError("subprocess.run input is not supported in the secure-exec Python runtime") + raise NotImplementedError("subprocess.run input is not supported in the agentos Python runtime") try: payload = _agentos_json.loads( _agentos_rpc.subprocessRunSync( @@ -1465,7 +1465,7 @@ def _agentos_subprocess_run(args, *, capture_output=False, check=False, cwd=None else: stdout_value = stdout_bytes stderr_value = stderr_bytes - result = _SecureExecCompletedProcess( + result = _AgentOsCompletedProcess( args, int(payload.get("exitCode", 1)), stdout_value if capture_output else None, @@ -1521,9 +1521,13 @@ function installPythonGuestImportBlocklist(pyodide) { function buildPythonRuntimeEnv() { const runtimeEnv = {}; - for (const name of PYTHON_RUNTIME_ENV_NAMES) { - if (typeof process.env[name] === 'string') { - runtimeEnv[name] = process.env[name]; + for (const [name, value] of Object.entries(process.env)) { + if ( + typeof value === 'string' && + !name.startsWith('AGENTOS_') && + !name.startsWith('NODE_SYNC_RPC_') + ) { + runtimeEnv[name] = value; } } return runtimeEnv; @@ -2429,6 +2433,7 @@ function resolvePythonSource(pyodide) { } let pythonVfsRpcBridge = null; +let retainLanguageContext = false; try { const startupStarted = realPerformance.now(); @@ -2442,6 +2447,7 @@ try { const packageCacheDir = resolvePyodidePackageCacheDir(); emitWarmupStage(`package-cache-dir:${packageCacheDir}`); const prewarmOnly = readRunnerEnv(PYTHON_PREWARM_ONLY_ENV) === '1'; + retainLanguageContext = readRunnerEnv(RETAIN_LANGUAGE_CONTEXT_ENV) === '1'; const preloadPackages = parsePreloadPackages(readRunnerEnv(PYTHON_PRELOAD_PACKAGES_ENV)); const lockFileContents = await readLockFileContents(indexPath, indexUrl).catch((error) => { throw wrapPythonStartupError('lock file read', { indexPath, indexUrl }, error); @@ -2502,6 +2508,9 @@ try { error, ); }); + if (retainLanguageContext) { + globalThis.__agentOSRetainedPyodide = pyodide; + } restorePyodideShellCompat(); emitWarmupStage('after-load-pyodide'); const loadPyodideMs = realPerformance.now() - loadPyodideStarted; @@ -2584,7 +2593,11 @@ try { writeStream(process.stderr, formatError(error)); process.exitCode = 1; } finally { - pythonVfsRpcBridge?.dispose(); + if (!retainLanguageContext) { + pythonVfsRpcBridge?.dispose(); + } emitControlMessage({ type: 'python_exit', exitCode: process.exitCode ?? 0 }); } -process.exit(process.exitCode ?? 0); +if (!retainLanguageContext) { + process.exit(process.exitCode ?? 0); +} diff --git a/crates/execution/assets/runners/wasi-module.js b/crates/execution/assets/runners/wasi-module.js index 171efbd1dd..0a9949429f 100644 --- a/crates/execution/assets/runners/wasi-module.js +++ b/crates/execution/assets/runners/wasi-module.js @@ -85,7 +85,7 @@ if (typeof globalThis !== "undefined" && typeof globalThis.__agentOSWasiModule = return; } try { - process.stderr.write(`[secure-exec-wasi] ${message}\n`); + process.stderr.write(`[agentos-wasi] ${message}\n`); } catch { // Ignore debug logging failures. } diff --git a/crates/execution/assets/runners/wasi-module.js.orig b/crates/execution/assets/runners/wasi-module.js.orig index a21b8ea079..ad24d0a4fb 100644 --- a/crates/execution/assets/runners/wasi-module.js.orig +++ b/crates/execution/assets/runners/wasi-module.js.orig @@ -76,7 +76,7 @@ if (typeof globalThis !== "undefined" && typeof globalThis.__agentOSWasiModule = return; } try { - process.stderr.write(`[secure-exec-wasi] ${message}\n`); + process.stderr.write(`[agentos-wasi] ${message}\n`); } catch { // Ignore debug logging failures. } diff --git a/crates/execution/assets/runners/wasm-runner.mjs b/crates/execution/assets/runners/wasm-runner.mjs index 6911085427..2d5d7fe799 100644 --- a/crates/execution/assets/runners/wasm-runner.mjs +++ b/crates/execution/assets/runners/wasm-runner.mjs @@ -798,7 +798,7 @@ function isProjectedCommandGuestPath(subject) { ? path.posix.normalize(raw) : path.posix.resolve(HOST_FS_GUEST_CWD, raw); return ( - /^\/__secure_exec\/commands\/\d+(?:\/|$)/u.test(guestPath) || + /^\/__agentos\/commands\/\d+(?:\/|$)/u.test(guestPath) || /^\/opt\/agentos\/bin\/[^/]+$/u.test(guestPath) ); } @@ -860,7 +860,7 @@ function projectedCommandImageBytes(command) { if (!name || name === '.' || name === '/') return null; for (const mapping of GUEST_PATH_MAPPINGS) { if ( - !/^\/__secure_exec\/commands\/\d+$/u.test(mapping?.guestPath ?? '') && + !/^\/__agentos\/commands\/\d+$/u.test(mapping?.guestPath ?? '') && mapping?.guestPath !== '/opt/agentos/bin' ) continue; const guestCandidate = path.posix.join(mapping.guestPath, name); @@ -4501,7 +4501,7 @@ function readSyncRpcLine() { const chunk = Buffer.alloc(4096); const bytesRead = readSync(NODE_SYNC_RPC_RESPONSE_FD, chunk, 0, chunk.length, null); if (bytesRead === 0) { - throw new Error('secure-exec WASM sync RPC response channel closed unexpectedly'); + throw new Error('agentos WASM sync RPC response channel closed unexpectedly'); } syncRpcResponseBuffer += chunk.subarray(0, bytesRead).toString('utf8'); } @@ -4529,7 +4529,7 @@ function callSyncRpc(method, args = []) { } if (!NODE_SYNC_RPC_ENABLE || NODE_SYNC_RPC_REQUEST_FD == null || NODE_SYNC_RPC_RESPONSE_FD == null) { - const error = new Error(`secure-exec WASM sync RPC is unavailable for ${method}`); + const error = new Error(`agentos WASM sync RPC is unavailable for ${method}`); error.code = 'ERR_AGENTOS_WASM_SYNC_RPC_UNAVAILABLE'; throw error; } @@ -4549,7 +4549,7 @@ function callSyncRpc(method, args = []) { } const error = new Error( - response?.error?.message || `secure-exec WASM sync RPC ${method} failed`, + response?.error?.message || `agentos WASM sync RPC ${method} failed`, ); if (typeof response?.error?.code === 'string') { error.code = response.error.code; @@ -12573,7 +12573,7 @@ function resetCaughtWasmSignalDispositionsForExec(sidecarCommitted) { } } -Object.defineProperty(globalThis, '__secureExecWasmSignalDispatch', { +Object.defineProperty(globalThis, '__agentOsWasmSignalDispatch', { configurable: true, writable: true, value: (_eventType, payload) => { diff --git a/crates/execution/assets/undici-shims/async_hooks.js b/crates/execution/assets/undici-shims/async_hooks.js index 5cf4ad48e4..ca09cf8fe1 100644 --- a/crates/execution/assets/undici-shims/async_hooks.js +++ b/crates/execution/assets/undici-shims/async_hooks.js @@ -48,7 +48,7 @@ class AsyncLocalStorage { } class AsyncResource { - constructor(type = "SecureExecAsyncResource") { + constructor(type = "AgentOsAsyncResource") { this.type = type; } diff --git a/crates/execution/assets/undici-shims/http2.js b/crates/execution/assets/undici-shims/http2.js index d6bc80355e..9d055a1209 100644 --- a/crates/execution/assets/undici-shims/http2.js +++ b/crates/execution/assets/undici-shims/http2.js @@ -19,7 +19,7 @@ const constants = { }; function notImplemented(name) { - const error = new Error(`node:http2 ${name} is not available in the secure-exec bridge bootstrap`); + const error = new Error(`node:http2 ${name} is not available in the agentos bridge bootstrap`); error.code = "ERR_NOT_IMPLEMENTED"; throw error; } diff --git a/crates/execution/assets/undici-shims/stream.js b/crates/execution/assets/undici-shims/stream.js index 875ee53a21..524c6c21d4 100644 --- a/crates/execution/assets/undici-shims/stream.js +++ b/crates/execution/assets/undici-shims/stream.js @@ -1,6 +1,6 @@ "use strict"; -import streamDefault, * as streamNs from "secure-exec-stream-stdlib"; +import streamDefault, * as streamNs from "agentos-stream-stdlib"; import { Buffer } from "node:buffer"; const baseStreamModule = streamNs.default ?? streamDefault ?? {}; @@ -130,7 +130,7 @@ export const isDisturbed = (stream) => { ); }; -export * from "secure-exec-stream-stdlib"; +export * from "agentos-stream-stdlib"; export default { ...baseStreamModule, diff --git a/crates/execution/assets/undici-shims/zlib.js b/crates/execution/assets/undici-shims/zlib.js index 3af356f1b5..ef6275d914 100644 --- a/crates/execution/assets/undici-shims/zlib.js +++ b/crates/execution/assets/undici-shims/zlib.js @@ -1,7 +1,7 @@ "use strict"; function getZlibModule() { - const mod = globalThis.__secureExecBuiltinZlibModule; + const mod = globalThis.__agentOsBuiltinZlibModule; if (!mod) { throw new Error("node:zlib bridge module is not available"); } diff --git a/crates/execution/benchmarks/node-import-baseline.md b/crates/execution/benchmarks/node-import-baseline.md index 760df0c6e9..9b6b24b385 100644 --- a/crates/execution/benchmarks/node-import-baseline.md +++ b/crates/execution/benchmarks/node-import-baseline.md @@ -1,4 +1,4 @@ -# secure-exec Node Import Benchmark +# agentos Node Import Benchmark - Generated at unix ms: `1775118070728` - Node binary: `node` diff --git a/crates/execution/build.rs b/crates/execution/build.rs index 6a26a76cde..c99e47ffb0 100644 --- a/crates/execution/build.rs +++ b/crates/execution/build.rs @@ -1,4 +1,5 @@ use std::env; +use std::fmt::Write as _; use std::fs; use std::path::{Path, PathBuf}; @@ -31,8 +32,73 @@ fn main() { // Declare the cfg used to gate Python availability so `cargo` does not warn // about an unexpected cfg name. println!("cargo:rustc-check-cfg=cfg(agentos_pyodide_unavailable)"); + println!("cargo:rustc-check-cfg=cfg(agentos_typescript_unavailable)"); agentos_build_support::build_v8_bridge(&manifest_dir, &out_dir); stage_pyodide_assets(&manifest_dir, &out_dir); + stage_typescript_assets(&manifest_dir, &out_dir); +} + +fn stage_typescript_assets(manifest_dir: &Path, out_dir: &Path) { + let source_dir = manifest_dir.join("../../node_modules/typescript/lib"); + let staged_dir = out_dir.join("typescript"); + let generated_path = out_dir.join("typescript_assets.rs"); + println!("cargo:rerun-if-changed={}", source_dir.display()); + fs::create_dir_all(&staged_dir).unwrap_or_else(|error| { + panic!( + "failed to create TypeScript staging dir {}: {error}", + staged_dir.display() + ) + }); + + let mut assets = Vec::new(); + if let Ok(entries) = fs::read_dir(&source_dir) { + for entry in entries { + let entry = entry.unwrap_or_else(|error| { + panic!("failed to read TypeScript compiler asset entry: {error}") + }); + let path = entry.path(); + let Some(file_name) = path.file_name().and_then(|name| name.to_str()) else { + continue; + }; + if file_name != "typescript.js" + && !(file_name.starts_with("lib.") && file_name.ends_with(".d.ts")) + { + continue; + } + let destination = staged_dir.join(file_name); + fs::copy(&path, &destination).unwrap_or_else(|error| { + panic!( + "failed to stage TypeScript compiler asset {}: {error}", + path.display() + ) + }); + assets.push(file_name.to_owned()); + } + } + assets.sort(); + + if !assets.iter().any(|asset| asset == "typescript.js") { + println!("cargo:rustc-cfg=agentos_typescript_unavailable"); + println!( + "cargo:warning=agentos-execution: building without the bundled TypeScript compiler; guest TypeScript checking will be unavailable in this build." + ); + } + + let mut generated = String::from("&[\n"); + for asset in assets { + writeln!( + generated, + " ({asset:?}, include_bytes!(concat!(env!(\"OUT_DIR\"), \"/typescript/{asset}\")) as &'static [u8])," + ) + .expect("writing generated TypeScript asset table cannot fail"); + } + generated.push_str("]\n"); + fs::write(&generated_path, generated).unwrap_or_else(|error| { + panic!( + "failed to write TypeScript asset table {}: {error}", + generated_path.display() + ) + }); } fn stage_pyodide_assets(manifest_dir: &Path, out_dir: &Path) { diff --git a/crates/execution/src/benchmark.rs b/crates/execution/src/benchmark.rs index cb406362c3..be426a539b 100644 --- a/crates/execution/src/benchmark.rs +++ b/crates/execution/src/benchmark.rs @@ -195,7 +195,7 @@ impl JavascriptBenchmarkReport { comparison: Option<&BenchmarkComparison>, ) -> String { let mut markdown = String::new(); - let _ = writeln!(&mut markdown, "# secure-exec Node Import Benchmark"); + let _ = writeln!(&mut markdown, "# agentos Node Import Benchmark"); let _ = writeln!(&mut markdown); let _ = writeln!( &mut markdown, @@ -3061,25 +3061,25 @@ fn projected_package_import_entrypoint_source() -> String { fn pdf_lib_startup_entrypoint_source() -> String { timed_entrypoint_source( - "const pdfLib = await import('pdf-lib');\nconst pdfDoc = await pdfLib.PDFDocument.create();\nconst page = pdfDoc.addPage([612, 792]);\nconst font = await pdfDoc.embedFont(pdfLib.StandardFonts.Helvetica);\npage.drawText('secure-exec pdf-lib benchmark', {\n x: 50,\n y: 750,\n font,\n size: 18,\n});\nif (pdfDoc.getPageCount() !== 1 || page.getSize().width !== 612) {\n throw new Error('pdf-lib fixture did not create the expected document');\n}", + "const pdfLib = await import('pdf-lib');\nconst pdfDoc = await pdfLib.PDFDocument.create();\nconst page = pdfDoc.addPage([612, 792]);\nconst font = await pdfDoc.embedFont(pdfLib.StandardFonts.Helvetica);\npage.drawText('agentos pdf-lib benchmark', {\n x: 50,\n y: 750,\n font,\n size: 18,\n});\nif (pdfDoc.getPageCount() !== 1 || page.getSize().width !== 612) {\n throw new Error('pdf-lib fixture did not create the expected document');\n}", ) } fn jszip_startup_entrypoint_source() -> String { timed_entrypoint_source( - "const jszipModule = await import('jszip');\nconst JSZip = jszipModule.default ?? jszipModule;\nconst zip = new JSZip();\nzip.file('README.txt', 'secure-exec benchmark archive');\nconst notes = zip.folder('notes');\nif (!notes) {\n throw new Error('jszip fixture failed to create nested folder');\n}\nnotes.file('todo.txt', 'benchmark staging payload');\nconst fileCount = Object.values(zip.files).filter((entry) => !entry.dir).length;\nif (typeof zip.generateAsync !== 'function' || fileCount !== 2) {\n throw new Error('jszip fixture did not stage the expected archive');\n}", + "const jszipModule = await import('jszip');\nconst JSZip = jszipModule.default ?? jszipModule;\nconst zip = new JSZip();\nzip.file('README.txt', 'agentos benchmark archive');\nconst notes = zip.folder('notes');\nif (!notes) {\n throw new Error('jszip fixture failed to create nested folder');\n}\nnotes.file('todo.txt', 'benchmark staging payload');\nconst fileCount = Object.values(zip.files).filter((entry) => !entry.dir).length;\nif (typeof zip.generateAsync !== 'function' || fileCount !== 2) {\n throw new Error('jszip fixture did not stage the expected archive');\n}", ) } fn jszip_end_to_end_entrypoint_source() -> String { timed_entrypoint_source( - "const jszipModule = await import('jszip');\nconst JSZip = jszipModule.default ?? jszipModule;\nconst zip = new JSZip();\nconst repeatedPayload = 'secure-exec benchmark payload '.repeat(512);\nzip.file('README.txt', repeatedPayload);\nconst notes = zip.folder('notes');\nif (!notes) {\n throw new Error('jszip end-to-end fixture failed to create notes folder');\n}\nnotes.file('todo.txt', 'complete the archive roundtrip');\nconst data = zip.folder('data');\nif (!data) {\n throw new Error('jszip end-to-end fixture failed to create data folder');\n}\ndata.file('payload.json', JSON.stringify({\n repeatedPayloadLength: repeatedPayload.length,\n mode: 'cold-end-to-end',\n}));\nconst archiveBytes = await zip.generateAsync({\n type: 'uint8array',\n compression: 'DEFLATE',\n compressionOptions: { level: 6 },\n});\nconst restored = await JSZip.loadAsync(archiveBytes);\nconst restoredFileCount = Object.values(restored.files).filter((entry) => !entry.dir).length;\nconst restoredReadme = await restored.file('README.txt')?.async('string');\nconst restoredTodo = await restored.file('notes/todo.txt')?.async('string');\nconst restoredPayload = await restored.file('data/payload.json')?.async('string');\nif (\n archiveBytes.byteLength >= repeatedPayload.length ||\n restoredFileCount !== 3 ||\n restoredReadme !== repeatedPayload ||\n restoredTodo !== 'complete the archive roundtrip' ||\n !restoredPayload?.includes('cold-end-to-end')\n) {\n throw new Error('jszip end-to-end fixture did not complete the compressed archive roundtrip');\n}", + "const jszipModule = await import('jszip');\nconst JSZip = jszipModule.default ?? jszipModule;\nconst zip = new JSZip();\nconst repeatedPayload = 'agentos benchmark payload '.repeat(512);\nzip.file('README.txt', repeatedPayload);\nconst notes = zip.folder('notes');\nif (!notes) {\n throw new Error('jszip end-to-end fixture failed to create notes folder');\n}\nnotes.file('todo.txt', 'complete the archive roundtrip');\nconst data = zip.folder('data');\nif (!data) {\n throw new Error('jszip end-to-end fixture failed to create data folder');\n}\ndata.file('payload.json', JSON.stringify({\n repeatedPayloadLength: repeatedPayload.length,\n mode: 'cold-end-to-end',\n}));\nconst archiveBytes = await zip.generateAsync({\n type: 'uint8array',\n compression: 'DEFLATE',\n compressionOptions: { level: 6 },\n});\nconst restored = await JSZip.loadAsync(archiveBytes);\nconst restoredFileCount = Object.values(restored.files).filter((entry) => !entry.dir).length;\nconst restoredReadme = await restored.file('README.txt')?.async('string');\nconst restoredTodo = await restored.file('notes/todo.txt')?.async('string');\nconst restoredPayload = await restored.file('data/payload.json')?.async('string');\nif (\n archiveBytes.byteLength >= repeatedPayload.length ||\n restoredFileCount !== 3 ||\n restoredReadme !== repeatedPayload ||\n restoredTodo !== 'complete the archive roundtrip' ||\n !restoredPayload?.includes('cold-end-to-end')\n) {\n throw new Error('jszip end-to-end fixture did not complete the compressed archive roundtrip');\n}", ) } fn jszip_repeated_session_compressed_entrypoint_source() -> String { timed_entrypoint_source( - "const jszipModule = await import('jszip');\nconst JSZip = jszipModule.default ?? jszipModule;\nconst zip = new JSZip();\nconst repeatedPayload = 'secure-exec benchmark payload '.repeat(512);\nzip.file('README.txt', repeatedPayload);\nconst notes = zip.folder('notes');\nif (!notes) {\n throw new Error('jszip repeated-session fixture failed to create notes folder');\n}\nnotes.file('todo.txt', 'repeat this session workload');\nconst data = zip.folder('data');\nif (!data) {\n throw new Error('jszip repeated-session fixture failed to create data folder');\n}\ndata.file('payload.json', JSON.stringify({\n repeatedPayloadLength: repeatedPayload.length,\n repeatedSessions: true,\n}));\nconst archiveBytes = await zip.generateAsync({\n type: 'uint8array',\n compression: 'DEFLATE',\n compressionOptions: { level: 6 },\n});\nconst restored = await JSZip.loadAsync(archiveBytes);\nconst restoredFileCount = Object.values(restored.files).filter((entry) => !entry.dir).length;\nconst restoredReadme = await restored.file('README.txt')?.async('string');\nconst restoredTodo = await restored.file('notes/todo.txt')?.async('string');\nif (\n archiveBytes.byteLength >= repeatedPayload.length ||\n restoredFileCount !== 3 ||\n restoredReadme !== repeatedPayload ||\n restoredTodo !== 'repeat this session workload'\n) {\n throw new Error('jszip repeated-session fixture did not complete the compressed archive roundtrip');\n}", + "const jszipModule = await import('jszip');\nconst JSZip = jszipModule.default ?? jszipModule;\nconst zip = new JSZip();\nconst repeatedPayload = 'agentos benchmark payload '.repeat(512);\nzip.file('README.txt', repeatedPayload);\nconst notes = zip.folder('notes');\nif (!notes) {\n throw new Error('jszip repeated-session fixture failed to create notes folder');\n}\nnotes.file('todo.txt', 'repeat this session workload');\nconst data = zip.folder('data');\nif (!data) {\n throw new Error('jszip repeated-session fixture failed to create data folder');\n}\ndata.file('payload.json', JSON.stringify({\n repeatedPayloadLength: repeatedPayload.length,\n repeatedSessions: true,\n}));\nconst archiveBytes = await zip.generateAsync({\n type: 'uint8array',\n compression: 'DEFLATE',\n compressionOptions: { level: 6 },\n});\nconst restored = await JSZip.loadAsync(archiveBytes);\nconst restoredFileCount = Object.values(restored.files).filter((entry) => !entry.dir).length;\nconst restoredReadme = await restored.file('README.txt')?.async('string');\nconst restoredTodo = await restored.file('notes/todo.txt')?.async('string');\nif (\n archiveBytes.byteLength >= repeatedPayload.length ||\n restoredFileCount !== 3 ||\n restoredReadme !== repeatedPayload ||\n restoredTodo !== 'repeat this session workload'\n) {\n throw new Error('jszip repeated-session fixture did not complete the compressed archive roundtrip');\n}", ) } diff --git a/crates/execution/src/javascript.rs b/crates/execution/src/javascript.rs index d71e776327..184e4957cd 100644 --- a/crates/execution/src/javascript.rs +++ b/crates/execution/src/javascript.rs @@ -46,6 +46,10 @@ const NODE_IMPORT_CACHE_PATH_ENV: &str = "AGENTOS_NODE_IMPORT_CACHE_PATH"; const NODE_KEEP_STDIN_OPEN_ENV: &str = "AGENTOS_KEEP_STDIN_OPEN"; const NODE_GUEST_ENTRYPOINT_ENV: &str = "AGENTOS_GUEST_ENTRYPOINT"; const NODE_GUEST_ENTRYPOINT_MODULE_MODE_ENV: &str = "AGENTOS_GUEST_ENTRYPOINT_MODULE_MODE"; +const NODE_RETAIN_CONTEXT_ENV: &str = "AGENTOS_RETAIN_LANGUAGE_CONTEXT"; +const NODE_INLINE_FILE_PATH_ENV: &str = "AGENTOS_INLINE_FILE_PATH"; +const NODE_USE_BUNDLED_TYPESCRIPT_ENV: &str = "AGENTOS_USE_BUNDLED_TYPESCRIPT"; +const NODE_TYPESCRIPT_COMPILER_PATH_ENV: &str = "AGENTOS_TYPESCRIPT_COMPILER_PATH"; const NODE_GUEST_PATH_MAPPINGS_ENV: &str = "AGENTOS_GUEST_PATH_MAPPINGS"; const NODE_VIRTUAL_PROCESS_EXEC_PATH_ENV: &str = "AGENTOS_VIRTUAL_PROCESS_EXEC_PATH"; const NODE_VIRTUAL_PROCESS_PID_ENV: &str = "AGENTOS_VIRTUAL_PROCESS_PID"; @@ -161,10 +165,10 @@ const JAVASCRIPT_CAPTURED_OUTPUT_LIMIT_BYTES: usize = 16 * 1024 * 1024; const KERNEL_STDIN_BUFFER_LIMIT_BYTES: usize = 16 * 1024 * 1024; const NODE_WARMUP_MARKER_VERSION: &str = "1"; const NODE_WARMUP_SPECIFIERS: &[&str] = &[ - "secure-exec:builtin/path", - "secure-exec:builtin/url", - "secure-exec:builtin/fs-promises", - "secure-exec:polyfill/path", + "agentos:builtin/path", + "agentos:builtin/url", + "agentos:builtin/fs-promises", + "agentos:polyfill/path", ]; #[derive(Debug, Default, Clone)] @@ -261,6 +265,7 @@ const RESERVED_NODE_ENV_KEYS: &[&str] = &[ NODE_FROZEN_TIME_ENV, NODE_GUEST_ENTRYPOINT_ENV, NODE_GUEST_ENTRYPOINT_MODULE_MODE_ENV, + NODE_INLINE_FILE_PATH_ENV, NODE_GUEST_ARGV_ENV, NODE_GUEST_PATH_MAPPINGS_ENV, NODE_VIRTUAL_PROCESS_EXEC_PATH_ENV, @@ -274,6 +279,9 @@ const RESERVED_NODE_ENV_KEYS: &[&str] = &[ NODE_IMPORT_CACHE_LOADER_PATH_ENV, NODE_IMPORT_CACHE_PATH_ENV, NODE_KEEP_STDIN_OPEN_ENV, + NODE_RETAIN_CONTEXT_ENV, + NODE_USE_BUNDLED_TYPESCRIPT_ENV, + NODE_TYPESCRIPT_COMPILER_PATH_ENV, NODE_ALLOWED_BUILTINS_ENV, NODE_LOOPBACK_EXEMPT_PORTS_ENV, NODE_SYNC_RPC_ENABLE_ENV, @@ -1848,6 +1856,31 @@ impl JavascriptExecution { self.exited.load(Ordering::Acquire) } + /// Run another sidecar-managed operation in this execution's retained V8 + /// context. Public clients submit semantic language requests; only the + /// sidecar calls this executor primitive. + pub fn execute_retained( + &mut self, + user_code: String, + file_path: String, + module: bool, + ) -> Result<(), JavascriptExecutionError> { + self.exited.store(false, Ordering::Release); + self.kernel_stdin.reset(); + self.v8_session + .execute( + 2 | u8::from(module), + file_path, + String::new(), + String::new(), + String::new(), + false, + user_code, + None, + ) + .map_err(JavascriptExecutionError::Spawn) + } + /// Enqueue a replacement image that was fully prepared without running /// guest code. This is the final step of an atomic cross-runtime execve and /// must only be called after the kernel and sidecar process state commit. @@ -2777,12 +2810,18 @@ impl JavascriptExecutionEngine { // synthetic resource name so its dynamic-import callback has the same // resolution base instead of trying to resolve from the literal `-e`. let execution_file_path = if matches!(guest_entrypoint.as_str(), "-e" | "--eval") { - let cwd = translator.guest_cwd().trim_end_matches('/'); - if cwd.is_empty() { - String::from("/[eval]") - } else { - format!("{cwd}/[eval]") - } + request + .env + .get(NODE_INLINE_FILE_PATH_ENV) + .cloned() + .unwrap_or_else(|| { + let cwd = translator.guest_cwd().trim_end_matches('/'); + if cwd.is_empty() { + String::from("/[eval]") + } else { + format!("{cwd}/[eval]") + } + }) } else { guest_entrypoint.clone() }; @@ -2891,8 +2930,12 @@ impl JavascriptExecutionEngine { record_js_start_phase("js_start_install_module_reader", phase_start.elapsed()); let phase_start = Instant::now(); + let retain_context = request + .env + .get(NODE_RETAIN_CONTEXT_ENV) + .is_some_and(|value| value == "1" || value.eq_ignore_ascii_case("true")); let prepared_execute = PreparedJavascriptExecute { - mode: if use_module_mode { 1 } else { 0 }, + mode: u8::from(use_module_mode) | if retain_context { 2 } else { 0 }, file_path: execution_file_path, bridge_code: V8RuntimeHost::bridge_code().to_owned(), post_restore_script: String::new(), @@ -5223,6 +5266,12 @@ fn hex_digit(byte: u8) -> Option { } impl LocalKernelStdinBridge { + fn reset(&self) { + let mut state = self.state.lock().expect("kernel stdin state poisoned"); + state.bytes.clear(); + state.closed = false; + } + fn write(&self, chunk: &[u8]) -> Result<(), JavascriptExecutionError> { let mut state = self.state.lock().expect("kernel stdin state poisoned"); if state.closed { @@ -5470,10 +5519,10 @@ fn polyfill_expression(request: &str) -> Option { format!( "(() => {{ const error = new Error({message}); error.code = {code}; throw error; }})()", message = serde_json::to_string(&format!( - "node:{normalized} is not available in the secure-exec guest runtime" + "node:{normalized} is not available in the agentos guest runtime" )) .unwrap_or_else(|_| format!( - "\"node:{normalized} is not available in the secure-exec guest runtime\"" + "\"node:{normalized} is not available in the agentos guest runtime\"" )), code = serde_json::to_string(error_code) .unwrap_or_else(|_| "\"ERR_ACCESS_DENIED\"".to_owned()) @@ -7264,11 +7313,11 @@ export default { if module_name == "vm" { return String::from( - r#"const VM_CONTEXT_TAG = typeof Symbol === "function" ? Symbol.for("secure-exec.vm.context") : "__secure_exec_vm_context__"; -const VM_CONTEXT_ID = typeof Symbol === "function" ? Symbol.for("secure-exec.vm.context.id") : "__secure_exec_vm_context_id__"; + r#"const VM_CONTEXT_TAG = typeof Symbol === "function" ? Symbol.for("agentos.vm.context") : "__agentos_vm_context__"; +const VM_CONTEXT_ID = typeof Symbol === "function" ? Symbol.for("agentos.vm.context.id") : "__agentos_vm_context_id__"; function createVmNotImplementedError(feature) { - const error = new Error(`node:vm ${feature} is not implemented in the secure-exec guest runtime`); + const error = new Error(`node:vm ${feature} is not implemented in the agentos guest runtime`); error.code = "ERR_NOT_IMPLEMENTED"; return error; } @@ -7406,7 +7455,7 @@ export default { Script, compileFunction, createContext, isContext, measureMemor if module_name == "worker_threads" { return String::from( r#"function createNotImplementedError(feature) { - const error = new Error(`node:worker_threads ${feature} is not available in the secure-exec guest runtime`); + const error = new Error(`node:worker_threads ${feature} is not available in the agentos guest runtime`); error.code = "ERR_NOT_IMPLEMENTED"; return error; } @@ -7460,7 +7509,7 @@ function setEnvironmentData() {} export const BroadcastChannel = globalThis.BroadcastChannel; export { MessageChannel, MessagePort, Worker, getEnvironmentData, markAsUncloneable, markAsUntransferable, moveMessagePortToContext, postMessageToThread, receiveMessageOnPort, setEnvironmentData }; -export const SHARE_ENV = Symbol.for("secure-exec.worker_threads.SHARE_ENV"); +export const SHARE_ENV = Symbol.for("agentos.worker_threads.SHARE_ENV"); export const isMainThread = true; export const parentPort = null; export const resourceLimits = {}; @@ -8761,7 +8810,7 @@ mod tests { .expect("system time") .as_nanos(); let root = std::env::temp_dir().join(format!( - "secure-exec-module-bridge-{}-{unique}", + "agentos-module-bridge-{}-{unique}", std::process::id() )); let bin_dir = root.join("node_modules/next/dist/bin"); diff --git a/crates/execution/src/lib.rs b/crates/execution/src/lib.rs index ba69788fac..737551c1e5 100644 --- a/crates/execution/src/lib.rs +++ b/crates/execution/src/lib.rs @@ -1,6 +1,6 @@ #![forbid(unsafe_code)] -//! Native execution plane scaffold for the secure-exec runtime migration. +//! Native execution plane scaffold for the agentos runtime migration. mod common; mod host_node; @@ -28,6 +28,8 @@ pub use javascript::{ LocalModuleResolutionCache, LocalResolvedModuleFormat, ModuleFsReader, ModuleResolveMode, ModuleResolver, StartJavascriptExecutionRequest, }; +#[doc(hidden)] +pub use node_import_cache::bundled_typescript_assets; pub use python::{ CreatePythonContextRequest, PythonContext, PythonExecution, PythonExecutionEngine, PythonExecutionError, PythonExecutionEvent, PythonExecutionLimits, PythonExecutionResult, diff --git a/crates/execution/src/node_import_cache.rs b/crates/execution/src/node_import_cache.rs index 8cc1a04bd8..0cebdb3ece 100644 --- a/crates/execution/src/node_import_cache.rs +++ b/crates/execution/src/node_import_cache.rs @@ -21,13 +21,13 @@ const NODE_IMPORT_CACHE_SCHEMA_VERSION: &str = "1"; const NODE_IMPORT_CACHE_LOADER_VERSION: &str = "8"; // Upstream reached 104 while the reactor branch independently changed bundled // assets; use a new generation so no stale materialization survives the merge. -const NODE_IMPORT_CACHE_ASSET_VERSION: &str = "105"; +const NODE_IMPORT_CACHE_ASSET_VERSION: &str = "106"; const NODE_IMPORT_CACHE_DIR_PREFIX: &str = "agentos-node-import-cache"; const DEFAULT_NODE_IMPORT_CACHE_MATERIALIZE_TIMEOUT: Duration = Duration::from_secs(30); const NODE_IMPORT_CACHE_BLOCKING_JOB_RESERVATION_BYTES: usize = 64 * 1024; const PYODIDE_DIST_DIR: &str = "pyodide-dist"; -const AGENTOS_BUILTIN_SPECIFIER_PREFIX: &str = "secure-exec:builtin/"; -const AGENTOS_POLYFILL_SPECIFIER_PREFIX: &str = "secure-exec:polyfill/"; +const AGENTOS_BUILTIN_SPECIFIER_PREFIX: &str = "agentos:builtin/"; +const AGENTOS_POLYFILL_SPECIFIER_PREFIX: &str = "agentos:polyfill/"; const BUNDLED_PYODIDE_MJS: &[u8] = include_bytes!("../assets/pyodide/pyodide.mjs"); // Large Pyodide assets are excluded from the published crate and staged into // OUT_DIR by build.rs (copied from `assets/pyodide/` in-tree, or downloaded @@ -57,6 +57,14 @@ const BUNDLED_MICROPIP_WHL: &[u8] = const BUNDLED_CLICK_WHL: &[u8] = include_bytes!("../assets/pyodide/click-8.3.1-py3-none-any.whl"); const NODE_PYTHON_RUNNER_SOURCE: &str = include_str!("../assets/runners/python-runner.mjs"); +const BUNDLED_TYPESCRIPT_ASSETS: &[(&str, &[u8])] = + include!(concat!(env!("OUT_DIR"), "/typescript_assets.rs")); + +#[doc(hidden)] +pub fn bundled_typescript_assets() -> &'static [(&'static str, &'static [u8])] { + BUNDLED_TYPESCRIPT_ASSETS +} + static CLEANED_NODE_IMPORT_CACHE_ROOTS: OnceLock>> = OnceLock::new(); #[cfg(test)] static NODE_IMPORT_CACHE_TEST_MATERIALIZE_DELAY_MS: AtomicU64 = AtomicU64::new(0); @@ -215,7 +223,7 @@ export async function resolve(specifier, context, nextResolve) { metrics.resolveMisses += 1; - const asset = resolveSecureExecAsset(specifier); + const asset = resolveAgentOsAsset(specifier); if (asset) { cacheState.resolutions[key] = { kind: 'explicit-file', @@ -640,7 +648,7 @@ function loadProjectedPackageSource(url, filePath, format) { return source; } -function resolveSecureExecAsset(specifier) { +function resolveAgentOsAsset(specifier) { if (typeof specifier !== 'string' || !ASSET_ROOT) { return null; } @@ -2900,7 +2908,7 @@ fn render_fs_builtin_asset_source(init_counter_key: &str) -> String { globalThis[{init_counter_key}] = initCount;\n\ const mod = globalThis.__agentOSBuiltinFs ?? globalThis.__agentOSGuestFs ?? process.getBuiltinModule?.(\"node:fs\");\n\ if (!mod) {{\n\ - throw new Error('secure-exec guest fs polyfill was not initialized');\n\ + throw new Error('agentos guest fs polyfill was not initialized');\n\ }}\n\n\ export const __agentOSInitCount = initCount;\n\ export default mod;\n\ @@ -2989,7 +2997,7 @@ fn render_fs_promises_builtin_asset_source(init_counter_key: &str) -> String { let init_counter_key = format!("{init_counter_key:?}"); format!( - "import fsModule from \"secure-exec:builtin/fs\";\n\n\ + "import fsModule from \"agentos:builtin/fs\";\n\n\ const initCount = (globalThis[{init_counter_key}] ?? 0) + 1;\n\ globalThis[{init_counter_key}] = initCount;\n\ const mod = fsModule.promises;\n\n\ @@ -3065,7 +3073,7 @@ class AsyncLocalStorage {{\n\ }}\n\ \n\ class AsyncResource {{\n\ - constructor(type = 'SecureExecAsyncResource') {{\n\ + constructor(type = 'AgentOsAsyncResource') {{\n\ this.type = type;\n\ }}\n\ emitBefore() {{}}\n\ @@ -3123,7 +3131,7 @@ fn render_child_process_builtin_asset_source(init_counter_key: &str) -> String { const initCount = (globalThis[{init_counter_key}] ?? 0) + 1;\n\ globalThis[{init_counter_key}] = initCount;\n\ if (!globalThis.__agentOSBuiltinChildProcess) {{\n\ - const error = new Error(\"node:child_process is not available in the secure-exec guest runtime\");\n\ + const error = new Error(\"node:child_process is not available in the agentos guest runtime\");\n\ error.code = ACCESS_DENIED_CODE;\n\ throw error;\n\ }}\n\n\ @@ -3150,7 +3158,7 @@ fn render_net_builtin_asset_source(init_counter_key: &str) -> String { const initCount = (globalThis[{init_counter_key}] ?? 0) + 1;\n\ globalThis[{init_counter_key}] = initCount;\n\ if (!globalThis.__agentOSBuiltinNet) {{\n\ - const error = new Error(\"node:net is not available in the secure-exec guest runtime\");\n\ + const error = new Error(\"node:net is not available in the agentos guest runtime\");\n\ error.code = ACCESS_DENIED_CODE;\n\ throw error;\n\ }}\n\n\ @@ -3183,7 +3191,7 @@ fn render_dgram_builtin_asset_source(init_counter_key: &str) -> String { const initCount = (globalThis[{init_counter_key}] ?? 0) + 1;\n\ globalThis[{init_counter_key}] = initCount;\n\ if (!globalThis.__agentOSBuiltinDgram) {{\n\ - const error = new Error(\"node:dgram is not available in the secure-exec guest runtime\");\n\ + const error = new Error(\"node:dgram is not available in the agentos guest runtime\");\n\ error.code = ACCESS_DENIED_CODE;\n\ throw error;\n\ }}\n\n\ @@ -3324,7 +3332,7 @@ fn render_dns_builtin_asset_source(init_counter_key: &str) -> String { const initCount = (globalThis[{init_counter_key}] ?? 0) + 1;\n\ globalThis[{init_counter_key}] = initCount;\n\ if (!globalThis.__agentOSBuiltinDns) {{\n\ - const error = new Error(\"node:dns is not available in the secure-exec guest runtime\");\n\ + const error = new Error(\"node:dns is not available in the agentos guest runtime\");\n\ error.code = ACCESS_DENIED_CODE;\n\ throw error;\n\ }}\n\n\ @@ -3358,7 +3366,7 @@ fn render_dns_promises_builtin_asset_source(init_counter_key: &str) -> String { const initCount = (globalThis[{init_counter_key}] ?? 0) + 1;\n\ globalThis[{init_counter_key}] = initCount;\n\ if (!globalThis.__agentOSBuiltinDns) {{\n\ - const error = new Error(\"node:dns/promises is not available in the secure-exec guest runtime\");\n\ + const error = new Error(\"node:dns/promises is not available in the agentos guest runtime\");\n\ error.code = ACCESS_DENIED_CODE;\n\ throw error;\n\ }}\n\n\ @@ -3391,7 +3399,7 @@ fn render_http_builtin_asset_source(init_counter_key: &str) -> String { const initCount = (globalThis[{init_counter_key}] ?? 0) + 1;\n\ globalThis[{init_counter_key}] = initCount;\n\ if (!globalThis.__agentOSBuiltinHttp) {{\n\ - const error = new Error(\"node:http is not available in the secure-exec guest runtime\");\n\ + const error = new Error(\"node:http is not available in the agentos guest runtime\");\n\ error.code = ACCESS_DENIED_CODE;\n\ throw error;\n\ }}\n\n\ @@ -3425,7 +3433,7 @@ fn render_http2_builtin_asset_source(init_counter_key: &str) -> String { const initCount = (globalThis[{init_counter_key}] ?? 0) + 1;\n\ globalThis[{init_counter_key}] = initCount;\n\ if (!globalThis.__agentOSBuiltinHttp2) {{\n\ - const error = new Error(\"node:http2 is not available in the secure-exec guest runtime\");\n\ + const error = new Error(\"node:http2 is not available in the agentos guest runtime\");\n\ error.code = ACCESS_DENIED_CODE;\n\ throw error;\n\ }}\n\n\ @@ -3455,7 +3463,7 @@ fn render_https_builtin_asset_source(init_counter_key: &str) -> String { const initCount = (globalThis[{init_counter_key}] ?? 0) + 1;\n\ globalThis[{init_counter_key}] = initCount;\n\ if (!globalThis.__agentOSBuiltinHttps) {{\n\ - const error = new Error(\"node:https is not available in the secure-exec guest runtime\");\n\ + const error = new Error(\"node:https is not available in the agentos guest runtime\");\n\ error.code = ACCESS_DENIED_CODE;\n\ throw error;\n\ }}\n\n\ @@ -3479,7 +3487,7 @@ fn render_tls_builtin_asset_source(init_counter_key: &str) -> String { const initCount = (globalThis[{init_counter_key}] ?? 0) + 1;\n\ globalThis[{init_counter_key}] = initCount;\n\ if (!globalThis.__agentOSBuiltinTls) {{\n\ - const error = new Error(\"node:tls is not available in the secure-exec guest runtime\");\n\ + const error = new Error(\"node:tls is not available in the agentos guest runtime\");\n\ error.code = ACCESS_DENIED_CODE;\n\ throw error;\n\ }}\n\n\ @@ -3514,7 +3522,7 @@ fn render_os_builtin_asset_source(init_counter_key: &str) -> String { const initCount = (globalThis[{init_counter_key}] ?? 0) + 1;\n\ globalThis[{init_counter_key}] = initCount;\n\ if (!globalThis.__agentOSBuiltinOs) {{\n\ - const error = new Error(\"node:os is not available in the secure-exec guest runtime\");\n\ + const error = new Error(\"node:os is not available in the agentos guest runtime\");\n\ error.code = ACCESS_DENIED_CODE;\n\ throw error;\n\ }}\n\n\ @@ -3555,7 +3563,7 @@ fn render_v8_builtin_asset_source(init_counter_key: &str) -> String { globalThis[{init_counter_key}] = initCount;\n\ const mod = process.getBuiltinModule?.(\"node:v8\");\n\ if (!mod) {{\n\ - throw new Error(\"secure-exec guest v8 compatibility module was not initialized\");\n\ + throw new Error(\"agentos guest v8 compatibility module was not initialized\");\n\ }}\n\n\ export const __agentOSInitCount = initCount;\n\ export default mod;\n\ @@ -3591,7 +3599,7 @@ fn render_vm_builtin_asset_source(init_counter_key: &str) -> String { globalThis[{init_counter_key}] = initCount;\n\ const mod = process.getBuiltinModule?.(\"node:vm\");\n\ if (!mod) {{\n\ - throw new Error(\"secure-exec guest vm compatibility module was not initialized\");\n\ + throw new Error(\"agentos guest vm compatibility module was not initialized\");\n\ }}\n\n\ export const __agentOSInitCount = initCount;\n\ export default mod;\n\ @@ -3611,7 +3619,7 @@ fn render_worker_threads_builtin_asset_source(init_counter_key: &str) -> String globalThis[{init_counter_key}] = initCount;\n\ \n\ function createNotImplementedError(feature) {{\n\ - const error = new Error(`node:worker_threads ${{feature}} is not available in the secure-exec guest runtime`);\n\ + const error = new Error(`node:worker_threads ${{feature}} is not available in the agentos guest runtime`);\n\ error.code = \"ERR_NOT_IMPLEMENTED\";\n\ return error;\n\ }}\n\ @@ -3667,7 +3675,7 @@ const mod = {{\n\ BroadcastChannel: globalThis.BroadcastChannel,\n\ MessageChannel,\n\ MessagePort,\n\ - SHARE_ENV: Symbol.for(\"secure-exec.worker_threads.SHARE_ENV\"),\n\ + SHARE_ENV: Symbol.for(\"agentos.worker_threads.SHARE_ENV\"),\n\ Worker,\n\ getEnvironmentData,\n\ isMainThread: true,\n\ @@ -3706,7 +3714,7 @@ export const workerData = mod.workerData;\n" } fn render_denied_asset_source(module_specifier: &str) -> String { - let message = format!("{module_specifier} is not available in the secure-exec guest runtime"); + let message = format!("{module_specifier} is not available in the agentos guest runtime"); format!( "const error = new Error({message:?});\nerror.code = \"ERR_ACCESS_DENIED\";\nthrow error;\n" ) diff --git a/crates/execution/src/python.rs b/crates/execution/src/python.rs index b976b0feb5..2cb9eada5b 100644 --- a/crates/execution/src/python.rs +++ b/crates/execution/src/python.rs @@ -520,6 +520,22 @@ impl PythonExecution { self.inner.uses_shared_v8_runtime() } + /// Run another sidecar-managed operation in the retained Pyodide + /// interpreter owned by this execution. + pub fn execute_retained(&mut self, source: String) -> Result<(), PythonExecutionError> { + let source = serde_json::to_string(&source).map_err(|error| { + PythonExecutionError::Control(std::io::Error::other(format!( + "failed to encode retained Python source: {error}" + ))) + })?; + let runner = format!( + "process.exitCode = 0; (async () => {{ const pyodide = globalThis.__agentOSRetainedPyodide; if (!pyodide) throw new Error('retained Python interpreter is unavailable'); await pyodide.runPythonAsync({source}); }})()" + ); + self.inner + .execute_retained(runner, String::from("/[agentos-python-retained]"), false) + .map_err(map_javascript_error) + } + pub fn start_prepared(&mut self) -> Result<(), PythonExecutionError> { self.inner.start_prepared().map_err(map_javascript_error) } @@ -2776,7 +2792,7 @@ fn warmup_marker_contents( env!("CARGO_PKG_NAME").to_string(), env!("CARGO_PKG_VERSION").to_string(), PYTHON_WARMUP_MARKER_VERSION.to_string(), - String::from("secure-exec-v8"), + String::from("agentos-v8"), python_max_old_space_mb(request).to_string(), compile_cache_dir.display().to_string(), pyodide_dist_path.display().to_string(), diff --git a/crates/execution/src/v8_ipc.rs b/crates/execution/src/v8_ipc.rs index c9999d357b..6fe3b383b6 100644 --- a/crates/execution/src/v8_ipc.rs +++ b/crates/execution/src/v8_ipc.rs @@ -1,4 +1,4 @@ -//! Binary IPC framing for communication with the secure-exec-v8 runtime process. +//! Binary IPC framing for communication with the agentos-v8 runtime process. //! //! Wire format per frame: //! [4B total_len (u32 BE, excludes self)] diff --git a/crates/execution/src/wasm.rs b/crates/execution/src/wasm.rs index af52e69681..e295592c9b 100644 --- a/crates/execution/src/wasm.rs +++ b/crates/execution/src/wasm.rs @@ -3132,7 +3132,7 @@ const __agentOSRequireBuiltin = (specifier) => {{ if (typeof process?.getBuiltinModule === "function") {{ return process.getBuiltinModule(specifier); }} - throw new Error(`secure-exec WASM bootstrap cannot load ${{specifier}}`); + throw new Error(`agentos WASM bootstrap cannot load ${{specifier}}`); }}; {wasi_module_source} {env_merge_source} @@ -3176,42 +3176,42 @@ if (typeof globalThis !== "undefined") {{ return __agentOSRequireBuiltin("node:fs").chmodSync(...args); case "__kernel_stdio_write": if (typeof _kernelStdioWriteRaw === "undefined") {{ - throw new Error("secure-exec WASM kernel stdio bridge is unavailable"); + throw new Error("agentos WASM kernel stdio bridge is unavailable"); }} return _kernelStdioWriteRaw.applySync(void 0, args); case "__kernel_stdin_read": if (typeof _kernelStdinReadRaw === "undefined") {{ - throw new Error("secure-exec WASM kernel stdin bridge is unavailable"); + throw new Error("agentos WASM kernel stdin bridge is unavailable"); }} return _kernelStdinReadRaw.applySync(void 0, args); case "__kernel_poll": if (typeof _kernelPollRaw === "undefined") {{ - throw new Error("secure-exec WASM kernel poll bridge is unavailable"); + throw new Error("agentos WASM kernel poll bridge is unavailable"); }} return _kernelPollRaw.applySync(void 0, args); case "__kernel_isatty": if (typeof _kernelIsattyRaw === "undefined") {{ - throw new Error("secure-exec WASM kernel isatty bridge is unavailable"); + throw new Error("agentos WASM kernel isatty bridge is unavailable"); }} return _kernelIsattyRaw.applySync(void 0, args); case "__kernel_flock_path": if (typeof _kernelFlockRaw === "undefined") {{ - throw new Error("secure-exec WASM kernel file-lock bridge is unavailable"); + throw new Error("agentos WASM kernel file-lock bridge is unavailable"); }} return _kernelFlockRaw.applySync(void 0, args); case "__kernel_tty_size": if (typeof _kernelTtySizeRaw === "undefined") {{ - throw new Error("secure-exec WASM kernel tty size bridge is unavailable"); + throw new Error("agentos WASM kernel tty size bridge is unavailable"); }} return _kernelTtySizeRaw.applySync(void 0, args); case "__pty_set_raw_mode": if (typeof _ptySetRawMode === "undefined") {{ - throw new Error("secure-exec WASM PTY raw-mode bridge is unavailable"); + throw new Error("agentos WASM PTY raw-mode bridge is unavailable"); }} return _ptySetRawMode.applySync(void 0, args); case "child_process.spawn": {{ if (typeof _childProcessSpawnStart === "undefined") {{ - throw new Error("secure-exec WASM child_process bridge is unavailable"); + throw new Error("agentos WASM child_process bridge is unavailable"); }} const [request] = args; return _childProcessSpawnStart.applySync(void 0, [ @@ -3222,32 +3222,32 @@ if (typeof globalThis !== "undefined") {{ }} case "child_process.poll": if (typeof _childProcessPoll === "undefined") {{ - throw new Error("secure-exec WASM child_process poll bridge is unavailable"); + throw new Error("agentos WASM child_process poll bridge is unavailable"); }} return _childProcessPoll.applySync(void 0, args); case "child_process.kill": if (typeof _childProcessKill === "undefined") {{ - throw new Error("secure-exec WASM child_process kill bridge is unavailable"); + throw new Error("agentos WASM child_process kill bridge is unavailable"); }} return _childProcessKill.applySync(void 0, args); case "process.kill": if (typeof _processKill === "undefined") {{ - throw new Error("secure-exec WASM process kill bridge is unavailable"); + throw new Error("agentos WASM process kill bridge is unavailable"); }} return _processKill.applySync(void 0, args); case "process.exec": if (typeof _processExec === "undefined") {{ - throw new Error("secure-exec WASM process exec bridge is unavailable"); + throw new Error("agentos WASM process exec bridge is unavailable"); }} return _processExec.applySync(void 0, args); case "process.exec_fd_image_commit": if (typeof _processExecFdImageCommit === "undefined") {{ - throw new Error("secure-exec WASM process fd image commit bridge is unavailable"); + throw new Error("agentos WASM process fd image commit bridge is unavailable"); }} return _processExecFdImageCommit.applySync(void 0, args); case "child_process.write_stdin": {{ if (typeof _childProcessStdinWrite === "undefined") {{ - throw new Error("secure-exec WASM child_process stdin bridge is unavailable"); + throw new Error("agentos WASM child_process stdin bridge is unavailable"); }} const [childId, chunk] = args; return _childProcessStdinWrite.applySync(void 0, [ @@ -3257,67 +3257,67 @@ if (typeof globalThis !== "undefined") {{ }} case "child_process.close_stdin": if (typeof _childProcessStdinClose === "undefined") {{ - throw new Error("secure-exec WASM child_process stdin-close bridge is unavailable"); + throw new Error("agentos WASM child_process stdin-close bridge is unavailable"); }} return _childProcessStdinClose.applySync(void 0, args); case "net.connect": if (typeof _netSocketConnectRaw === "undefined") {{ - throw new Error("secure-exec WASM net.connect bridge is unavailable"); + throw new Error("agentos WASM net.connect bridge is unavailable"); }} return _netSocketConnectRaw.applySync(void 0, args); case "net.bind_unix": if (typeof _netBindUnixRaw === "undefined") {{ - throw new Error("secure-exec WASM net.bind_unix bridge is unavailable"); + throw new Error("agentos WASM net.bind_unix bridge is unavailable"); }} return _netBindUnixRaw.applySync(void 0, args); case "net.bind_connected_unix": if (typeof _netBindConnectedUnixRaw === "undefined") {{ - throw new Error("secure-exec WASM net.bind_connected_unix bridge is unavailable"); + throw new Error("agentos WASM net.bind_connected_unix bridge is unavailable"); }} return _netBindConnectedUnixRaw.applySync(void 0, args); case "net.reserve_tcp_port": if (typeof _netReserveTcpPortRaw === "undefined") {{ - throw new Error("secure-exec WASM net.reserve_tcp_port bridge is unavailable"); + throw new Error("agentos WASM net.reserve_tcp_port bridge is unavailable"); }} return _netReserveTcpPortRaw.applySync(void 0, args); case "net.release_tcp_port": if (typeof _netReleaseTcpPortRaw === "undefined") {{ - throw new Error("secure-exec WASM net.release_tcp_port bridge is unavailable"); + throw new Error("agentos WASM net.release_tcp_port bridge is unavailable"); }} return _netReleaseTcpPortRaw.applySync(void 0, args); case "net.listen": if (typeof _netServerListenRaw === "undefined") {{ - throw new Error("secure-exec WASM net.listen bridge is unavailable"); + throw new Error("agentos WASM net.listen bridge is unavailable"); }} return _netServerListenRaw.applySync(void 0, args); case "net.server_accept": if (typeof _netServerAcceptRaw === "undefined") {{ - throw new Error("secure-exec WASM net.server_accept bridge is unavailable"); + throw new Error("agentos WASM net.server_accept bridge is unavailable"); }} return _netServerAcceptRaw.applySync(void 0, args); case "net.server_close": if (typeof _netServerCloseSyncRaw === "undefined") {{ - throw new Error("secure-exec WASM net.server_close bridge is unavailable"); + throw new Error("agentos WASM net.server_close bridge is unavailable"); }} return _netServerCloseSyncRaw.applySync(void 0, args); case "net.poll": if (typeof _netSocketPollRaw === "undefined") {{ - throw new Error("secure-exec WASM net.poll bridge is unavailable"); + throw new Error("agentos WASM net.poll bridge is unavailable"); }} return _netSocketPollRaw.applySync(void 0, args); case "net.socket_read": if (typeof _netSocketReadRaw === "undefined") {{ - throw new Error("secure-exec WASM net.socket_read bridge is unavailable"); + throw new Error("agentos WASM net.socket_read bridge is unavailable"); }} return _netSocketReadRaw.applySync(void 0, args); case "net.socket_wait_connect": if (typeof _netSocketWaitConnectSyncRaw === "undefined") {{ - throw new Error("secure-exec WASM net.socket_wait_connect bridge is unavailable"); + throw new Error("agentos WASM net.socket_wait_connect bridge is unavailable"); }} return _netSocketWaitConnectSyncRaw.applySync(void 0, args); case "net.write": if (typeof _netSocketWriteSyncRaw === "undefined") {{ - throw new Error("secure-exec WASM net.write bridge is unavailable"); + throw new Error("agentos WASM net.write bridge is unavailable"); }} return _netSocketWriteSyncRaw.applySync(void 0, [ args[0], @@ -3326,27 +3326,27 @@ if (typeof globalThis !== "undefined") {{ ]); case "net.destroy": if (typeof _netSocketDestroyRaw === "undefined") {{ - throw new Error("secure-exec WASM net.destroy bridge is unavailable"); + throw new Error("agentos WASM net.destroy bridge is unavailable"); }} return _netSocketDestroyRaw.applySync(void 0, args); case "net.socket_upgrade_tls": if (typeof _netSocketUpgradeTlsRaw === "undefined") {{ - throw new Error("secure-exec WASM TLS-upgrade bridge is unavailable"); + throw new Error("agentos WASM TLS-upgrade bridge is unavailable"); }} return _netSocketUpgradeTlsRaw.applySync(void 0, args); case "dgram.createSocket": if (typeof _dgramSocketCreateRaw === "undefined") {{ - throw new Error("secure-exec WASM dgram.createSocket bridge is unavailable"); + throw new Error("agentos WASM dgram.createSocket bridge is unavailable"); }} return _dgramSocketCreateRaw.applySync(void 0, args); case "dgram.bind": if (typeof _dgramSocketBindRaw === "undefined") {{ - throw new Error("secure-exec WASM dgram.bind bridge is unavailable"); + throw new Error("agentos WASM dgram.bind bridge is unavailable"); }} return _dgramSocketBindRaw.applySync(void 0, args); case "dgram.send": {{ if (typeof _dgramSocketSendRaw === "undefined") {{ - throw new Error("secure-exec WASM dgram.send bridge is unavailable"); + throw new Error("agentos WASM dgram.send bridge is unavailable"); }} const [socketId, chunk, options = {{}}] = args; return _dgramSocketSendRaw.applySync(void 0, [ @@ -3357,7 +3357,7 @@ if (typeof globalThis !== "undefined") {{ }} case "dgram.poll": if (typeof _dgramSocketRecvRaw === "undefined") {{ - throw new Error("secure-exec WASM dgram.poll bridge is unavailable"); + throw new Error("agentos WASM dgram.poll bridge is unavailable"); }} const event = _dgramSocketRecvRaw.applySync(void 0, args); if (event && event.type === "message") {{ @@ -3384,32 +3384,32 @@ if (typeof globalThis !== "undefined") {{ return event; case "dgram.close": if (typeof _dgramSocketCloseRaw === "undefined") {{ - throw new Error("secure-exec WASM dgram.close bridge is unavailable"); + throw new Error("agentos WASM dgram.close bridge is unavailable"); }} return _dgramSocketCloseRaw.applySync(void 0, args); case "dgram.address": if (typeof _dgramSocketAddressRaw === "undefined") {{ - throw new Error("secure-exec WASM dgram.address bridge is unavailable"); + throw new Error("agentos WASM dgram.address bridge is unavailable"); }} return _dgramSocketAddressRaw.applySync(void 0, args); case "dgram.setBufferSize": if (typeof _dgramSocketSetBufferSizeRaw === "undefined") {{ - throw new Error("secure-exec WASM dgram.setBufferSize bridge is unavailable"); + throw new Error("agentos WASM dgram.setBufferSize bridge is unavailable"); }} return _dgramSocketSetBufferSizeRaw.applySync(void 0, args); case "dgram.getBufferSize": if (typeof _dgramSocketGetBufferSizeRaw === "undefined") {{ - throw new Error("secure-exec WASM dgram.getBufferSize bridge is unavailable"); + throw new Error("agentos WASM dgram.getBufferSize bridge is unavailable"); }} return _dgramSocketGetBufferSizeRaw.applySync(void 0, args); case "dns.lookup": if (typeof _networkDnsLookupSyncRaw === "undefined") {{ - throw new Error("secure-exec WASM dns.lookup bridge is unavailable"); + throw new Error("agentos WASM dns.lookup bridge is unavailable"); }} return _networkDnsLookupSyncRaw.applySync(void 0, args); case "process.signal_state": {{ if (typeof _processSignalState === "undefined") {{ - throw new Error("secure-exec WASM signal-state bridge is unavailable"); + throw new Error("agentos WASM signal-state bridge is unavailable"); }} const [signal, action = "default", maskJson = "[]", flags = 0] = args; return _processSignalState.applySyncPromise(void 0, [ @@ -3421,7 +3421,7 @@ if (typeof globalThis !== "undefined") {{ }} case "process.take_signal": if (typeof _processTakeSignal === "undefined") {{ - throw new Error("secure-exec WASM signal-drain bridge is unavailable"); + throw new Error("agentos WASM signal-drain bridge is unavailable"); }} return _processTakeSignal.applySync(void 0, args); case "process.getpgid": @@ -3517,11 +3517,11 @@ if (typeof globalThis !== "undefined") {{ case "process.fd_recvmsg_rights": case "process.fd_socket_shutdown": if (typeof _processWasmSyncRpc === "undefined") {{ - throw new Error("secure-exec WASM process-syscall bridge is unavailable"); + throw new Error("agentos WASM process-syscall bridge is unavailable"); }} return _processWasmSyncRpc.applySync(void 0, [method, ...args]); default: - throw new Error(`secure-exec WASM sync RPC method not implemented in V8 runtime: ${{method}}`); + throw new Error(`agentos WASM sync RPC method not implemented in V8 runtime: ${{method}}`); }} }}, async call(method, args = []) {{ @@ -5250,14 +5250,14 @@ mod tests { &BTreeMap::from([( String::from("AGENTOS_GUEST_PATH_MAPPINGS"), format!( - "[{{\"guestPath\":\"/__secure_exec/commands/0\",\"hostPath\":\"{}\"}}]", + "[{{\"guestPath\":\"/__agentos/commands/0\",\"hostPath\":\"{}\"}}]", command_root.display() ), )]), ); assert!(candidates.contains(&module.to_string_lossy().into_owned())); - assert!(candidates.contains(&String::from("/__secure_exec/commands/0/hello"))); + assert!(candidates.contains(&String::from("/__agentos/commands/0/hello"))); } #[test] @@ -5382,7 +5382,7 @@ mod tests { read_only: false, }, super::WasmGuestPathMapping { - guest_path: String::from("/__secure_exec/commands/0"), + guest_path: String::from("/__agentos/commands/0"), host_path: mapped_root.clone(), read_only: false, }, @@ -5609,9 +5609,9 @@ mod tests { fn wasm_sandbox_root_reads_absolute_env_only() { let sandbox_root = wasm_sandbox_root(&BTreeMap::from([( String::from(WASM_SANDBOX_ROOT_ENV), - String::from("/tmp/secure-exec-shadow"), + String::from("/tmp/agentos-shadow"), )])); - assert_eq!(sandbox_root, Some(PathBuf::from("/tmp/secure-exec-shadow"))); + assert_eq!(sandbox_root, Some(PathBuf::from("/tmp/agentos-shadow"))); let relative = wasm_sandbox_root(&BTreeMap::from([( String::from(WASM_SANDBOX_ROOT_ENV), @@ -5639,7 +5639,7 @@ mod tests { module_host_path: PathBuf::from("/tmp/module.wasm"), guest_cwd: String::from("/"), host_cwd: PathBuf::from("/tmp"), - sandbox_root: Some(PathBuf::from("/tmp/secure-exec-shadow")), + sandbox_root: Some(PathBuf::from("/tmp/agentos-shadow")), guest_path_mappings: Vec::new(), route_fs_through_sidecar: true, next_fd: 64, diff --git a/crates/execution/tests/javascript_v8.rs b/crates/execution/tests/javascript_v8.rs index 22e0014121..df4c26e216 100644 --- a/crates/execution/tests/javascript_v8.rs +++ b/crates/execution/tests/javascript_v8.rs @@ -917,7 +917,7 @@ fn javascript_execution_uses_v8_runtime_without_spawning_guest_node_binary() { env: BTreeMap::new(), cwd: temp.path().to_path_buf(), wasm_module_bytes: None, - inline_code: Some(String::from("globalThis.__secureExecRanInV8 = true;")), + inline_code: Some(String::from("globalThis.__agentOsRanInV8 = true;")), }) .expect("start JavaScript execution"); @@ -963,7 +963,7 @@ fn javascript_execution_virtual_os_identity_comes_from_guest_runtime_not_env() { os_tmpdir: Some(String::from("/vm-tmp")), os_type: Some(String::from("VMType")), os_release: Some(String::from("1.2.3-vm")), - os_version: Some(String::from("VM secure-exec build 42")), + os_version: Some(String::from("VM agentos build 42")), os_machine: Some(String::from("vm64")), ..Default::default() }, @@ -992,7 +992,7 @@ if (os.hostname() !== "vm-hostname") throw new Error(`hostname=${os.hostname()}` if (os.tmpdir() !== "/vm-tmp") throw new Error(`tmpdir=${os.tmpdir()}`); if (os.type() !== "VMType") throw new Error(`type=${os.type()}`); if (os.release() !== "1.2.3-vm") throw new Error(`release=${os.release()}`); -if (os.version() !== "VM secure-exec build 42") throw new Error(`version=${os.version()}`); +if (os.version() !== "VM agentos build 42") throw new Error(`version=${os.version()}`); if (os.machine() !== "vm64") throw new Error(`machine=${os.machine()}`); "#, )), @@ -1893,14 +1893,14 @@ fn javascript_execution_high_resolution_time_opt_in_enables_sub_ms_hrtime() { wasm_module_bytes: None, inline_code: Some(String::from( r#" -if (typeof __secureExecHrNowUs !== "function") { +if (typeof __agentOsHrNowUs !== "function") { throw new Error("high-resolution host clock was not installed"); } let sawSubMs = false; for (let attempt = 0; attempt < 80 && !sawSubMs; attempt++) { const start = process.hrtime.bigint(); - const until = __secureExecHrNowUs() + 200; - while (__secureExecHrNowUs() < until) {} + const until = __agentOsHrNowUs() + 200; + while (__agentOsHrNowUs() < until) {} const delta = process.hrtime.bigint() - start; if (delta > 0n && delta < 1000000n) { sawSubMs = true; @@ -1942,7 +1942,7 @@ fn javascript_execution_high_resolution_time_default_off_keeps_coarse_clock() { wasm_module_bytes: None, inline_code: Some(String::from( r#" -if (typeof __secureExecHrNowUs !== "undefined") { +if (typeof __agentOsHrNowUs !== "undefined") { throw new Error("high-resolution host clock exists without opt-in"); } for (let attempt = 0; attempt < 20; attempt++) { diff --git a/crates/execution/tests/process.rs b/crates/execution/tests/process.rs index 4cfc5f05d3..353b00a4d0 100644 --- a/crates/execution/tests/process.rs +++ b/crates/execution/tests/process.rs @@ -57,7 +57,7 @@ fn embedded_runtime_process_keeps_host_pid_internal_for_javascript() { env: BTreeMap::new(), cwd: temp.path().to_path_buf(), wasm_module_bytes: None, - inline_code: Some(String::from("globalThis.__secureExecRanInV8 = true;")), + inline_code: Some(String::from("globalThis.__agentOsRanInV8 = true;")), }) .expect("start JavaScript execution"); diff --git a/crates/kernel/Cargo.toml b/crates/kernel/Cargo.toml index d03fd751a5..007aeda414 100644 --- a/crates/kernel/Cargo.toml +++ b/crates/kernel/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -description = "Shared kernel plane for secure-exec native and browser sidecars" +description = "Shared kernel plane for agentos native and browser sidecars" [dependencies] agentos-bridge = { workspace = true } diff --git a/crates/kernel/src/kernel.rs b/crates/kernel/src/kernel.rs index 793bad5cfe..28aa7c7b39 100644 --- a/crates/kernel/src/kernel.rs +++ b/crates/kernel/src/kernel.rs @@ -6970,7 +6970,7 @@ impl KernelVm { interpreter_depth: usize, ) -> KernelResult<()> { let resolved = self.validate_executable_path(path, cwd)?; - // `/bin/` and `/__secure_exec/commands/.../` may be + // `/bin/` and `/__agentos/commands/.../` may be // kernel-owned command stubs whose backing bytes are a self-referential // launcher rather than the projected WASM blob the runner loads. Once // the exact path has resolved to a registered command, its trusted @@ -7014,7 +7014,7 @@ impl KernelVm { } if let Some(name) = normalized - .strip_prefix("/__secure_exec/commands/") + .strip_prefix("/__agentos/commands/") .and_then(|suffix| suffix.rsplit('/').next()) { if !name.is_empty() && !name.contains('/') && self.commands.resolve(name).is_some() { @@ -7727,7 +7727,7 @@ impl KernelVm { let mut body = String::new(); for processor in 0..self.proc_cpu_count() { body.push_str(&format!( - "processor\t: {processor}\nmodel name\t: secure-exec Virtual CPU\ncpu MHz\t\t: 1000.000\nsiblings\t: 1\ncpu cores\t: 1\n\n" + "processor\t: {processor}\nmodel name\t: agentos Virtual CPU\ncpu MHz\t\t: 1000.000\nsiblings\t: 1\ncpu cores\t: 1\n\n" )); } body.into_bytes() diff --git a/crates/kernel/src/lib.rs b/crates/kernel/src/lib.rs index 36f2506684..5090568ff4 100644 --- a/crates/kernel/src/lib.rs +++ b/crates/kernel/src/lib.rs @@ -1,6 +1,6 @@ #![forbid(unsafe_code)] -//! Shared per-VM kernel plane for the secure-exec runtime migration. +//! Shared per-VM kernel plane for the agentos runtime migration. pub use agentos_bridge as bridge; pub mod command_registry; diff --git a/crates/kernel/tests/command_registry.rs b/crates/kernel/tests/command_registry.rs index 6188aded6b..933a82b079 100644 --- a/crates/kernel/tests/command_registry.rs +++ b/crates/kernel/tests/command_registry.rs @@ -166,21 +166,21 @@ fn mounted_agentos_command_paths_resolve_to_registered_drivers() { .expect("register drivers"); kernel - .mkdir("/__secure_exec/commands/0", true) + .mkdir("/__agentos/commands/0", true) .expect("create mounted command root"); kernel .write_file( - "/__secure_exec/commands/0/xu", + "/__agentos/commands/0/xu", b"#!/bin/sh\n# kernel command stub\n".to_vec(), ) .expect("write mounted command stub"); kernel - .chmod("/__secure_exec/commands/0/xu", 0o755) + .chmod("/__agentos/commands/0/xu", 0o755) .expect("chmod mounted command stub"); let process = kernel .spawn_process( - "/__secure_exec/commands/0/xu", + "/__agentos/commands/0/xu", vec![String::from("hello-agentos")], SpawnOptions::default(), ) diff --git a/crates/kernel/tests/default_deny_guards.rs b/crates/kernel/tests/default_deny_guards.rs index 0db396f330..0fd804e703 100644 --- a/crates/kernel/tests/default_deny_guards.rs +++ b/crates/kernel/tests/default_deny_guards.rs @@ -1,6 +1,6 @@ //! Default-deny / fail-closed guards (CI hardening, item #5). //! -//! These tests pin down the secure-exec security posture so a refactor cannot +//! These tests pin down the agentos security posture so a refactor cannot //! silently weaken it: //! //! 1. Default-deny: with no policy configured (`Permissions::default()`), diff --git a/crates/kernel/tests/identity.rs b/crates/kernel/tests/identity.rs index 17f33e619a..905a979c8c 100644 --- a/crates/kernel/tests/identity.rs +++ b/crates/kernel/tests/identity.rs @@ -338,7 +338,7 @@ fn procfs_exposes_linux_like_identity_and_system_files() { let cpuinfo = read_utf8(&mut kernel, "/proc/cpuinfo"); assert!(cpuinfo.contains("processor\t: 0")); - assert!(cpuinfo.contains("model name\t: secure-exec Virtual CPU")); + assert!(cpuinfo.contains("model name\t: agentos Virtual CPU")); let meminfo = read_utf8(&mut kernel, "/proc/meminfo"); assert!(meminfo.contains("MemTotal: 262144 kB")); diff --git a/crates/native-baseline/Cargo.toml b/crates/native-baseline/Cargo.toml index 28d539203d..1dd78c7811 100644 --- a/crates/native-baseline/Cargo.toml +++ b/crates/native-baseline/Cargo.toml @@ -7,7 +7,7 @@ publish = false # Native floor for the differential perf harness. std-only on purpose: this is the # glibc fork/posix_spawn + execve baseline that the agent-os "emulation tax" divides -# against. No secure-exec deps — it must measure the host, not the emulator. +# against. No agentos deps — it must measure the host, not the emulator. [[bin]] name = "agentos-native-baseline" diff --git a/crates/native-baseline/src/main.rs b/crates/native-baseline/src/main.rs index ff180dd639..52a4667fd7 100644 --- a/crates/native-baseline/src/main.rs +++ b/crates/native-baseline/src/main.rs @@ -410,13 +410,13 @@ fn run_once(op: Op, iter: usize, base_dir: &Path, config: &BenchConfig) { assert!(out.status.success(), "pipe chain failed: {out:?}"); } Op::FsStat => { - let path = base_dir.join("secure-exec-native-fs-stat.txt"); + let path = base_dir.join("agentos-native-fs-stat.txt"); std::fs::write(&path, b"hi").expect("write stat fixture"); let meta = std::fs::metadata(&path).expect("stat fixture"); assert!(meta.len() >= 2); } Op::FsStatX32 => { - let path = base_dir.join("secure-exec-native-fs-stat-x32.txt"); + let path = base_dir.join("agentos-native-fs-stat-x32.txt"); if !path.exists() { std::fs::write(&path, b"hi").expect("write stat fixture"); } @@ -433,7 +433,7 @@ fn run_once(op: Op, iter: usize, base_dir: &Path, config: &BenchConfig) { } } Op::FsWrite => { - let path = base_dir.join("secure-exec-native-fs-write.txt"); + let path = base_dir.join("agentos-native-fs-write.txt"); if let Some(size_bytes) = config.size_bytes { std::fs::write(path, vec![(iter & 255) as u8; size_bytes]).expect("write fixture"); } else { @@ -442,7 +442,7 @@ fn run_once(op: Op, iter: usize, base_dir: &Path, config: &BenchConfig) { } Op::FsRead => { let size_bytes = config.size_bytes.unwrap_or(64 * 1024); - let path = base_dir.join("secure-exec-native-fs-read.bin"); + let path = base_dir.join("agentos-native-fs-read.bin"); let rewrite = std::fs::metadata(&path) .map(|meta| meta.len() != size_bytes as u64) .unwrap_or(true); @@ -454,11 +454,9 @@ fn run_once(op: Op, iter: usize, base_dir: &Path, config: &BenchConfig) { } Op::StreamCopy => { let size_bytes = config.size_bytes.unwrap_or(64 * 1024); - let src = base_dir.join(format!( - "secure-exec-native-stream-copy-src-{size_bytes}.bin" - )); + let src = base_dir.join(format!("agentos-native-stream-copy-src-{size_bytes}.bin")); let dst = base_dir.join(format!( - "secure-exec-native-stream-copy-dst-{size_bytes}-{iter}.bin" + "agentos-native-stream-copy-dst-{size_bytes}-{iter}.bin" )); let rewrite = std::fs::metadata(&src) .map(|meta| meta.len() != size_bytes as u64) @@ -487,26 +485,26 @@ fn run_once(op: Op, iter: usize, base_dir: &Path, config: &BenchConfig) { assert_eq!(meta.len(), size_bytes as u64); } Op::FsOpenClose => { - let path = base_dir.join("secure-exec-native-fs-open-close.txt"); + let path = base_dir.join("agentos-native-fs-open-close.txt"); std::fs::write(&path, b"hi").expect("write open fixture"); let file = File::open(path).expect("open fixture"); drop(file); } Op::FsMkdirRmdir => { - let path = base_dir.join(format!("secure-exec-native-dir-{iter}")); + let path = base_dir.join(format!("agentos-native-dir-{iter}")); std::fs::create_dir(&path).expect("create dir"); std::fs::remove_dir(&path).expect("remove dir"); } Op::FsRename => { - let from = base_dir.join(format!("secure-exec-native-rename-{iter}.a")); - let to = base_dir.join(format!("secure-exec-native-rename-{iter}.b")); + let from = base_dir.join(format!("agentos-native-rename-{iter}.a")); + let to = base_dir.join(format!("agentos-native-rename-{iter}.b")); std::fs::write(&from, b"hi").expect("write rename fixture"); std::fs::rename(&from, &to).expect("rename fixture"); std::fs::remove_file(&to).expect("remove rename fixture"); } Op::FsReaddir => { let entry_count = config.entry_count.unwrap_or(32); - let dir = base_dir.join("secure-exec-native-readdir"); + let dir = base_dir.join("agentos-native-readdir"); std::fs::create_dir_all(&dir).expect("create readdir dir"); let marker = dir.join(format!(".fixture-ready-{entry_count}")); if !marker.exists() { @@ -522,7 +520,7 @@ fn run_once(op: Op, iter: usize, base_dir: &Path, config: &BenchConfig) { assert!(count > entry_count); } Op::FsFsync => { - let path = base_dir.join("secure-exec-native-fsync.txt"); + let path = base_dir.join("agentos-native-fsync.txt"); let mut file = File::create(path).expect("create fsync fixture"); file.write_all(b"hello").expect("write fsync fixture"); file.sync_all().expect("fsync fixture"); @@ -593,7 +591,7 @@ fn run_once(op: Op, iter: usize, base_dir: &Path, config: &BenchConfig) { } #[cfg(all(unix, not(target_family = "wasm")))] Op::UnixConnect => { - let sock = base_dir.join(format!("secure-exec-native-unix-connect-{iter}.sock")); + let sock = base_dir.join(format!("agentos-native-unix-connect-{iter}.sock")); let _ = std::fs::remove_file(&sock); let listener = UnixListener::bind(&sock).expect("bind unix listener"); let server = thread::spawn(move || { @@ -607,7 +605,7 @@ fn run_once(op: Op, iter: usize, base_dir: &Path, config: &BenchConfig) { #[cfg(all(unix, not(target_family = "wasm")))] Op::UnixEcho => { let payload = vec![7_u8; config.size_bytes.unwrap_or(16)]; - let sock = base_dir.join(format!("secure-exec-native-unix-echo-{iter}.sock")); + let sock = base_dir.join(format!("agentos-native-unix-echo-{iter}.sock")); let _ = std::fs::remove_file(&sock); let listener = UnixListener::bind(&sock).expect("bind unix listener"); let expected_len = payload.len(); diff --git a/crates/native-sidecar-browser/CLAUDE.md b/crates/native-sidecar-browser/CLAUDE.md index 91a5d062a9..fb974c10b3 100644 --- a/crates/native-sidecar-browser/CLAUDE.md +++ b/crates/native-sidecar-browser/CLAUDE.md @@ -1,5 +1,5 @@ # Browser Support -- Browser support is untested after the secure-exec split; only build-level validation is required during migration. +- Browser support is untested after the agentos split; only build-level validation is required during migration. - Provenance: moved from rivet-dev/agentos@87ed8e21e454. - Keep the browser sidecar separate from the native sidecar because worker transport and main-thread ownership differ from stdio/socket transport. diff --git a/crates/native-sidecar-browser/Cargo.toml b/crates/native-sidecar-browser/Cargo.toml index 2eeaa53c87..d595500874 100644 --- a/crates/native-sidecar-browser/Cargo.toml +++ b/crates/native-sidecar-browser/Cargo.toml @@ -3,7 +3,7 @@ name = "agentos-native-sidecar-browser" version.workspace = true edition.workspace = true license.workspace = true -description = "Browser-side Secure Exec sidecar scaffold" +description = "Browser-side AgentOS language execution sidecar scaffold" # Browser support is retained for a future migration but intentionally disabled # and must not be published while it is outside the unified reactor contract. publish = false diff --git a/crates/native-sidecar-browser/src/lib.rs b/crates/native-sidecar-browser/src/lib.rs index 4c6fa2c22e..574de48eb4 100644 --- a/crates/native-sidecar-browser/src/lib.rs +++ b/crates/native-sidecar-browser/src/lib.rs @@ -2,7 +2,7 @@ // AGENTOS_BROWSER_SUPPORT_DISABLED: retained for reference, but AgentOS is native-only. /* -//! Browser-side sidecar scaffold for the secure-exec runtime migration. +//! Browser-side sidecar scaffold for the agentos runtime migration. mod service; #[cfg(target_arch = "wasm32")] diff --git a/crates/native-sidecar-browser/tests/service.rs b/crates/native-sidecar-browser/tests/service.rs index 95ce97bede..b8539c475c 100644 --- a/crates/native-sidecar-browser/tests/service.rs +++ b/crates/native-sidecar-browser/tests/service.rs @@ -178,12 +178,12 @@ fn test_os_config() -> BrowserWorkerOsConfig { platform: String::from("linux"), arch: String::from("x64"), r#type: String::from("Linux"), - release: String::from("6.8.0-secure-exec"), - version: String::from("#1 SMP PREEMPT_DYNAMIC secure-exec"), + release: String::from("6.8.0-agentos"), + version: String::from("#1 SMP PREEMPT_DYNAMIC agentos"), cpu_count: 1, totalmem: 1024 * 1024 * 1024, freemem: 512 * 1024 * 1024, - hostname: String::from("secure-exec"), + hostname: String::from("agentos"), homedir: String::from("/home/user"), tmpdir: String::from("/tmp"), machine: String::from("x86_64"), @@ -355,16 +355,16 @@ fn browser_worker_spawn_receives_virtual_identity_config() { ); assert_eq!( spawn.get("os_hostname").map(String::as_str), - Some("secure-exec") + Some("agentos") ); assert_eq!(spawn.get("os_type").map(String::as_str), Some("Linux")); assert_eq!( spawn.get("os_release").map(String::as_str), - Some("6.8.0-secure-exec") + Some("6.8.0-agentos") ); assert_eq!( spawn.get("os_version").map(String::as_str), - Some("#1 SMP PREEMPT_DYNAMIC secure-exec") + Some("#1 SMP PREEMPT_DYNAMIC agentos") ); assert_eq!(spawn.get("os_tmpdir").map(String::as_str), Some("/tmp")); assert_eq!(spawn.get("os_machine").map(String::as_str), Some("x86_64")); diff --git a/crates/native-sidecar-browser/tests/wire_dispatch.rs b/crates/native-sidecar-browser/tests/wire_dispatch.rs index 6723d02e91..c4983862b2 100644 --- a/crates/native-sidecar-browser/tests/wire_dispatch.rs +++ b/crates/native-sidecar-browser/tests/wire_dispatch.rs @@ -33,7 +33,7 @@ struct WireExtension; impl BrowserExtension for WireExtension { fn namespace(&self) -> &str { - "dev.rivet.secure-exec.browser-wire-test" + "dev.rivet.agentos.browser-wire-test" } fn handle_request( @@ -775,7 +775,7 @@ fn browser_wire_dispatcher_routes_extension_frames() { connection_id: String::from("client"), }), payload: RequestPayload::ExtEnvelope(ExtEnvelope { - namespace: String::from("dev.rivet.secure-exec.browser-wire-test"), + namespace: String::from("dev.rivet.agentos.browser-wire-test"), payload: b"ping".to_vec(), }), }, @@ -784,10 +784,7 @@ fn browser_wire_dispatcher_routes_extension_frames() { let ResponsePayload::ExtEnvelope(envelope) = response.payload else { panic!("unexpected extension response: {:?}", response.payload); }; - assert_eq!( - envelope.namespace, - "dev.rivet.secure-exec.browser-wire-test" - ); + assert_eq!(envelope.namespace, "dev.rivet.agentos.browser-wire-test"); assert_eq!(envelope.payload, b"wire-ext:ping"); } diff --git a/crates/native-sidecar-core/Cargo.toml b/crates/native-sidecar-core/Cargo.toml index d153e0991b..6d4374b00f 100644 --- a/crates/native-sidecar-core/Cargo.toml +++ b/crates/native-sidecar-core/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -description = "Shared Secure Exec sidecar logic for native and browser shells" +description = "Shared AgentOS language execution sidecar logic for native and browser shells" [dependencies] agentos-bridge = { workspace = true } diff --git a/crates/native-sidecar-core/src/frames.rs b/crates/native-sidecar-core/src/frames.rs index f3dd47b172..58afef891a 100644 --- a/crates/native-sidecar-core/src/frames.rs +++ b/crates/native-sidecar-core/src/frames.rs @@ -495,14 +495,13 @@ mod tests { #[test] fn authenticated_response_sets_connection_ownership() { - let response = - authenticated_response(7, "secure-exec-test", String::from("conn-test"), 1024); + let response = authenticated_response(7, "agentos-test", String::from("conn-test"), 1024); assert_eq!(response.request_id, 7); assert_eq!(response.ownership, OwnershipScope::connection("conn-test")); match response.payload { ResponsePayload::Authenticated(authenticated) => { - assert_eq!(authenticated.sidecar_id, "secure-exec-test"); + assert_eq!(authenticated.sidecar_id, "agentos-test"); assert_eq!(authenticated.connection_id, "conn-test"); assert_eq!(authenticated.max_frame_bytes, 1024); } diff --git a/crates/native-sidecar-core/src/identity.rs b/crates/native-sidecar-core/src/identity.rs index 11f2509b16..9a6c768882 100644 --- a/crates/native-sidecar-core/src/identity.rs +++ b/crates/native-sidecar-core/src/identity.rs @@ -42,13 +42,13 @@ pub fn shared_guest_runtime_identity( os_totalmem: virtual_os_totalmem_bytes(resource_limits), os_freemem: virtual_os_freemem_bytes(resource_limits), os_homedir: user.homedir.clone(), - os_hostname: String::from("secure-exec"), + os_hostname: String::from("agentos"), os_shell: user.shell.clone(), os_user: user.username.clone(), os_tmpdir: String::from("/tmp"), os_type: String::from("Linux"), - os_release: String::from("6.8.0-secure-exec"), - os_version: String::from("#1 SMP PREEMPT_DYNAMIC secure-exec"), + os_release: String::from("6.8.0-agentos"), + os_version: String::from("#1 SMP PREEMPT_DYNAMIC agentos"), os_machine: String::from("x86_64"), } } @@ -86,13 +86,13 @@ mod tests { assert_eq!(identity.os_cpu_count, 6); assert_eq!(identity.os_totalmem, 512 * 1024 * 1024); assert_eq!(identity.os_homedir, "/Users/runner"); - assert_eq!(identity.os_hostname, "secure-exec"); + assert_eq!(identity.os_hostname, "agentos"); assert_eq!(identity.os_shell, "/bin/zsh"); assert_eq!(identity.os_user, "runner"); assert_eq!(identity.os_tmpdir, "/tmp"); assert_eq!(identity.os_type, "Linux"); - assert_eq!(identity.os_release, "6.8.0-secure-exec"); - assert_eq!(identity.os_version, "#1 SMP PREEMPT_DYNAMIC secure-exec"); + assert_eq!(identity.os_release, "6.8.0-agentos"); + assert_eq!(identity.os_version, "#1 SMP PREEMPT_DYNAMIC agentos"); assert_eq!(identity.os_machine, "x86_64"); } } diff --git a/crates/native-sidecar-core/src/router.rs b/crates/native-sidecar-core/src/router.rs index c9eee9916c..1cc13ca1d7 100644 --- a/crates/native-sidecar-core/src/router.rs +++ b/crates/native-sidecar-core/src/router.rs @@ -57,6 +57,8 @@ pub enum RequestRoute { GetZombieTimerCount(GetZombieTimerCountRequest), LinkPackage(LinkPackageRequest), ProvidedCommands(ProvidedCommandsRequest), + ExecutionOperation(RequestPayload), + ExecutionLifecycle(RequestPayload), Ext(ExtEnvelope), UnsupportedHostCallbackDirection, } @@ -105,6 +107,36 @@ pub fn route_request_payload(request: &RequestFrame) -> RequestRoute { RequestPayload::GetZombieTimerCount(payload) => RequestRoute::GetZombieTimerCount(payload), RequestPayload::LinkPackage(payload) => RequestRoute::LinkPackage(payload), RequestPayload::ProvidedCommands(payload) => RequestRoute::ProvidedCommands(payload), + payload @ (RequestPayload::ShellExecution(_) + | RequestPayload::ArgvExecution(_) + | RequestPayload::JavaScriptExecution(_) + | RequestPayload::JavaScriptEvaluation(_) + | RequestPayload::JavaScriptFileExecution(_) + | RequestPayload::TypeScriptExecution(_) + | RequestPayload::TypeScriptEvaluation(_) + | RequestPayload::TypeScriptFileExecution(_) + | RequestPayload::TypeScriptCheck(_) + | RequestPayload::TypeScriptProjectCheck(_) + | RequestPayload::NpmProjectInstall(_) + | RequestPayload::NpmPackageInstall(_) + | RequestPayload::NpmScriptExecution(_) + | RequestPayload::NpmPackageExecution(_) + | RequestPayload::PythonExecution(_) + | RequestPayload::PythonEvaluation(_) + | RequestPayload::PythonFileExecution(_) + | RequestPayload::PythonModuleExecution(_) + | RequestPayload::PythonInstall(_)) => RequestRoute::ExecutionOperation(payload), + payload @ (RequestPayload::GetExecution(_) + | RequestPayload::ListExecutions(_) + | RequestPayload::WaitExecution(_) + | RequestPayload::CancelExecution(_) + | RequestPayload::SignalExecution(_) + | RequestPayload::ResetExecution(_) + | RequestPayload::DeleteExecution(_) + | RequestPayload::WriteExecutionStdin(_) + | RequestPayload::CloseExecutionStdin(_) + | RequestPayload::ResizeExecutionPty(_) + | RequestPayload::ReadExecutionOutput(_)) => RequestRoute::ExecutionLifecycle(payload), RequestPayload::HostFilesystemCall(_) | RequestPayload::PersistenceLoad(_) | RequestPayload::PersistenceFlush(_) => RequestRoute::UnsupportedHostCallbackDirection, @@ -169,6 +201,36 @@ pub fn request_dispatch_mode(request: &RequestFrame) -> RequestDispatchMode { | RequestPayload::GetZombieTimerCount(_) | RequestPayload::LinkPackage(_) | RequestPayload::ProvidedCommands(_) + | RequestPayload::ShellExecution(_) + | RequestPayload::ArgvExecution(_) + | RequestPayload::JavaScriptExecution(_) + | RequestPayload::JavaScriptEvaluation(_) + | RequestPayload::JavaScriptFileExecution(_) + | RequestPayload::TypeScriptExecution(_) + | RequestPayload::TypeScriptEvaluation(_) + | RequestPayload::TypeScriptFileExecution(_) + | RequestPayload::TypeScriptCheck(_) + | RequestPayload::TypeScriptProjectCheck(_) + | RequestPayload::NpmProjectInstall(_) + | RequestPayload::NpmPackageInstall(_) + | RequestPayload::NpmScriptExecution(_) + | RequestPayload::NpmPackageExecution(_) + | RequestPayload::PythonExecution(_) + | RequestPayload::PythonEvaluation(_) + | RequestPayload::PythonFileExecution(_) + | RequestPayload::PythonModuleExecution(_) + | RequestPayload::PythonInstall(_) + | RequestPayload::GetExecution(_) + | RequestPayload::ListExecutions(_) + | RequestPayload::WaitExecution(_) + | RequestPayload::CancelExecution(_) + | RequestPayload::SignalExecution(_) + | RequestPayload::ResetExecution(_) + | RequestPayload::DeleteExecution(_) + | RequestPayload::WriteExecutionStdin(_) + | RequestPayload::CloseExecutionStdin(_) + | RequestPayload::ResizeExecutionPty(_) + | RequestPayload::ReadExecutionOutput(_) | RequestPayload::HostFilesystemCall(_) | RequestPayload::PersistenceLoad(_) | RequestPayload::PersistenceFlush(_) => RequestDispatchMode::Immediate, diff --git a/crates/native-sidecar/Cargo.toml b/crates/native-sidecar/Cargo.toml index 9ac6aa003e..47abfd7c56 100644 --- a/crates/native-sidecar/Cargo.toml +++ b/crates/native-sidecar/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -description = "Native Secure Exec sidecar runtime" +description = "Native AgentOS language execution sidecar runtime" [lib] name = "agentos_native_sidecar" @@ -45,6 +45,14 @@ nix = { version = "0.29", features = ["fs", "net", "poll", "process", "signal", # system-openssl discovery on any runner (ubuntu/macOS). openssl backs the # guest crypto runtime (RSA/EC/DH/AES), so it cannot be a TLS-only stack. openssl = { version = "0.10", features = ["vendored"] } +oxc_allocator = "0.75.0" +oxc_ast = "0.75.0" +oxc-browserslist = "=2.0.0" +oxc_codegen = "0.75.0" +oxc_parser = "0.75.0" +oxc_semantic = "0.75.0" +oxc_span = "0.75.0" +oxc_transformer = "0.75.0" pbkdf2 = "0.12" rustls = { version = "0.23.37", default-features = false, features = ["aws_lc_rs", "std", "tls12"] } rustls-pemfile = "2.2" diff --git a/crates/native-sidecar/assets/base-filesystem.json b/crates/native-sidecar/assets/base-filesystem.json index e641efddd3..bdb7b06ec7 100644 --- a/crates/native-sidecar/assets/base-filesystem.json +++ b/crates/native-sidecar/assets/base-filesystem.json @@ -5,8 +5,8 @@ "snapshotCreatedAt": "2026-06-22T19:45:20.529Z", "builtAt": "2026-06-23T03:47:12.644Z", "transforms": [ - "Normalize HOSTNAME to secure-exec", - "Preserve the captured user-level environment and filesystem layout as the secure-exec base layer", + "Normalize HOSTNAME to agentos", + "Preserve the captured user-level environment and filesystem layout as the agentos base layer", "Add the non-Alpine /workspace directory (default agent working directory) owned by the base user", "Restore Alpine 3.22's /etc/services database and add the VM's Docker-style /etc/hosts entries for libc lookup parity" ] @@ -15,7 +15,7 @@ "env": { "CHARSET": "UTF-8", "HOME": "/home/agentos", - "HOSTNAME": "secure-exec", + "HOSTNAME": "agentos", "LANG": "C.UTF-8", "LC_COLLATE": "C", "LOGNAME": "agentos", @@ -92,7 +92,7 @@ "mode": "644", "uid": 0, "gid": 0, - "content": "secure-exec\n" + "content": "agentos\n" }, { "path": "/etc/hosts", @@ -100,7 +100,7 @@ "mode": "644", "uid": 0, "gid": 0, - "content": "127.0.0.1 localhost localhost.localdomain\n::1 localhost localhost.localdomain ip6-localhost ip6-loopback\nfe00:: ip6-localnet\nff00:: ip6-mcastprefix\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n127.0.1.1 secure-exec\n" + "content": "127.0.0.1 localhost localhost.localdomain\n::1 localhost localhost.localdomain ip6-localhost ip6-loopback\nfe00:: ip6-localnet\nff00:: ip6-mcastprefix\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n127.0.1.1 agentos\n" }, { "path": "/etc/logrotate.d", diff --git a/crates/native-sidecar/src/bindings.rs b/crates/native-sidecar/src/bindings.rs index 19ade95e46..6ba4acc312 100644 --- a/crates/native-sidecar/src/bindings.rs +++ b/crates/native-sidecar/src/bindings.rs @@ -199,7 +199,7 @@ fn binding_command_name_from_specifier(command: &str) -> Option<&str> { let file_name = Path::new(command).file_name()?.to_str()?; let normalized = normalize_path(command); let registered_internal_path = normalized - .strip_prefix("/__secure_exec/commands/") + .strip_prefix("/__agentos/commands/") .and_then(|suffix| suffix.rsplit('/').next()) .is_some_and(|name| name == file_name); if !matches!( diff --git a/crates/native-sidecar/src/bootstrap.rs b/crates/native-sidecar/src/bootstrap.rs index 4d9ded5d1a..558a9a1112 100644 --- a/crates/native-sidecar/src/bootstrap.rs +++ b/crates/native-sidecar/src/bootstrap.rs @@ -36,7 +36,7 @@ where pub(crate) fn discover_command_guest_paths(kernel: &mut SidecarKernel) -> BTreeMap { let mut command_guest_paths = BTreeMap::new(); - let Ok(command_roots) = kernel.read_dir("/__secure_exec/commands") else { + let Ok(command_roots) = kernel.read_dir("/__agentos/commands") else { return command_guest_paths; }; @@ -47,7 +47,7 @@ pub(crate) fn discover_command_guest_paths(kernel: &mut SidecarKernel) -> BTreeM ordered_roots.sort(); for root in ordered_roots { - let guest_root = format!("/__secure_exec/commands/{root}"); + let guest_root = format!("/__agentos/commands/{root}"); let Ok(entries) = kernel.read_dir(&guest_root) else { continue; }; diff --git a/crates/native-sidecar/src/crypto_cipher.rs b/crates/native-sidecar/src/crypto_cipher.rs index 088c352251..9728d503f0 100644 --- a/crates/native-sidecar/src/crypto_cipher.rs +++ b/crates/native-sidecar/src/crypto_cipher.rs @@ -527,7 +527,7 @@ mod tests { fn aes_256_cbc_roundtrip() { let key = [7_u8; 32]; let iv = [9_u8; 16]; - let plaintext = b"secure-exec-crypto-surface"; + let plaintext = b"agentos-crypto-surface"; let (ciphertext, _) = encrypt_all("aes-256-cbc", &key, &iv, plaintext); let recovered = decrypt_all("aes-256-cbc", &key, &iv, &ciphertext, None); assert_eq!(recovered, plaintext); diff --git a/crates/native-sidecar/src/execution/javascript/rpc.rs b/crates/native-sidecar/src/execution/javascript/rpc.rs index 2074204375..bbe575c27e 100644 --- a/crates/native-sidecar/src/execution/javascript/rpc.rs +++ b/crates/native-sidecar/src/execution/javascript/rpc.rs @@ -5079,7 +5079,7 @@ mod error_code_tests { } #[test] - fn guest_errno_code_accepts_trusted_secure_exec_prefixes() { + fn guest_errno_code_accepts_trusted_agentos_prefixes() { assert_eq!( guest_errno_code("ERR_AGENTOS_NODE_SYNC_RPC: EACCES: permission denied on /foo"), Some("EACCES") diff --git a/crates/native-sidecar/src/execution/launch.rs b/crates/native-sidecar/src/execution/launch.rs index 2b833ab87b..ae7b772c39 100644 --- a/crates/native-sidecar/src/execution/launch.rs +++ b/crates/native-sidecar/src/execution/launch.rs @@ -1837,7 +1837,7 @@ pub(super) fn resolve_host_node_cli_entrypoint( continue; }; let package_root = entrypoint.parent()?.parent()?.to_path_buf(); - let guest_root = format!("/__secure_exec/node-runtime/{command_name}"); + let guest_root = format!("/__agentos/node-runtime/{command_name}"); let relative_entrypoint = entrypoint.strip_prefix(&package_root).ok()?; let guest_entrypoint = normalize_path(&format!( "{guest_root}/{}", @@ -1942,7 +1942,7 @@ pub(super) fn build_host_node_cli_eval(cli: &ResolvedHostNodeCliEntrypoint) -> S let guest_display_module = normalize_path(&format!("{}/lib/utils/display.js", cli.guest_root)); let guest_log_file_module = normalize_path(&format!("{}/lib/utils/log-file.js", cli.guest_root)); - let debug_preamble = "const __agentOSDebugNpmCli = !!process.env.CODEX_DEBUG_NPM_CLI; const __agentOSDebugLog = (...args) => { if (__agentOSDebugNpmCli) { console.error('[secure-exec npm debug]', ...args); } }; const __agentOSIsProcessExitError = (error) => !!(error && typeof error === 'object' && (error._isProcessExit === true || error.name === 'ProcessExitError')); const __agentOSResolveExitCode = (code) => Number.isFinite(code) ? code : (Number.isFinite(process.exitCode) ? process.exitCode : 0); const __agentOSFinish = (code) => { process.exitCode = __agentOSResolveExitCode(code); }; if (__agentOSDebugNpmCli) { const __agentOSWrapAsyncFsMethod = (__agentOSTarget, __agentOSMethod) => { const __agentOSOriginal = __agentOSTarget[__agentOSMethod]; if (typeof __agentOSOriginal !== 'function' || __agentOSOriginal.__agentOSDebugWrapped) { return; } const __agentOSWrapped = async (...args) => { const target = args.length > 0 ? args[0] : ''; __agentOSDebugLog(`fs.${__agentOSMethod}:start`, String(target)); try { const result = await __agentOSOriginal.apply(__agentOSTarget, args); __agentOSDebugLog(`fs.${__agentOSMethod}:done`, String(target)); return result; } catch (error) { __agentOSDebugLog(`fs.${__agentOSMethod}:error`, String(target), error && error.stack ? error.stack : String(error)); throw error; } }; __agentOSWrapped.__agentOSDebugWrapped = true; __agentOSTarget[__agentOSMethod] = __agentOSWrapped; }; const __agentOSWrapSyncFsMethod = (__agentOSTarget, __agentOSMethod) => { const __agentOSOriginal = __agentOSTarget[__agentOSMethod]; if (typeof __agentOSOriginal !== 'function' || __agentOSOriginal.__agentOSDebugWrapped) { return; } const __agentOSWrapped = (...args) => { const target = args.length > 0 ? args[0] : ''; __agentOSDebugLog(`fs.${__agentOSMethod}:start`, String(target)); try { const result = __agentOSOriginal.apply(__agentOSTarget, args); __agentOSDebugLog(`fs.${__agentOSMethod}:done`, String(target)); return result; } catch (error) { __agentOSDebugLog(`fs.${__agentOSMethod}:error`, String(target), error && error.stack ? error.stack : String(error)); throw error; } }; __agentOSWrapped.__agentOSDebugWrapped = true; __agentOSTarget[__agentOSMethod] = __agentOSWrapped; }; const __agentOSFsPromiseModules = [require('fs/promises'), require('node:fs/promises')]; for (const __agentOSFsPromises of __agentOSFsPromiseModules) { for (const __agentOSMethod of ['access', 'lstat', 'mkdir', 'open', 'readFile', 'readdir', 'readlink', 'realpath', 'rename', 'rm', 'rmdir', 'stat', 'symlink', 'unlink', 'writeFile']) { __agentOSWrapAsyncFsMethod(__agentOSFsPromises, __agentOSMethod); } } const __agentOSFsModules = [require('fs'), require('node:fs')]; for (const __agentOSFs of __agentOSFsModules) { for (const __agentOSMethod of ['accessSync', 'existsSync', 'lstatSync', 'mkdirSync', 'openSync', 'readFileSync', 'readdirSync', 'readlinkSync', 'realpathSync', 'renameSync', 'rmSync', 'rmdirSync', 'statSync', 'symlinkSync', 'unlinkSync', 'writeFileSync']) { __agentOSWrapSyncFsMethod(__agentOSFs, __agentOSMethod); } } }"; + let debug_preamble = "const __agentOSDebugNpmCli = !!process.env.CODEX_DEBUG_NPM_CLI; const __agentOSDebugLog = (...args) => { if (__agentOSDebugNpmCli) { console.error('[agentos npm debug]', ...args); } }; const __agentOSIsProcessExitError = (error) => !!(error && typeof error === 'object' && (error._isProcessExit === true || error.name === 'ProcessExitError')); const __agentOSResolveExitCode = (code) => Number.isFinite(code) ? code : (Number.isFinite(process.exitCode) ? process.exitCode : 0); const __agentOSFinish = (code) => { process.exitCode = __agentOSResolveExitCode(code); }; if (__agentOSDebugNpmCli) { const __agentOSWrapAsyncFsMethod = (__agentOSTarget, __agentOSMethod) => { const __agentOSOriginal = __agentOSTarget[__agentOSMethod]; if (typeof __agentOSOriginal !== 'function' || __agentOSOriginal.__agentOSDebugWrapped) { return; } const __agentOSWrapped = async (...args) => { const target = args.length > 0 ? args[0] : ''; __agentOSDebugLog(`fs.${__agentOSMethod}:start`, String(target)); try { const result = await __agentOSOriginal.apply(__agentOSTarget, args); __agentOSDebugLog(`fs.${__agentOSMethod}:done`, String(target)); return result; } catch (error) { __agentOSDebugLog(`fs.${__agentOSMethod}:error`, String(target), error && error.stack ? error.stack : String(error)); throw error; } }; __agentOSWrapped.__agentOSDebugWrapped = true; __agentOSTarget[__agentOSMethod] = __agentOSWrapped; }; const __agentOSWrapSyncFsMethod = (__agentOSTarget, __agentOSMethod) => { const __agentOSOriginal = __agentOSTarget[__agentOSMethod]; if (typeof __agentOSOriginal !== 'function' || __agentOSOriginal.__agentOSDebugWrapped) { return; } const __agentOSWrapped = (...args) => { const target = args.length > 0 ? args[0] : ''; __agentOSDebugLog(`fs.${__agentOSMethod}:start`, String(target)); try { const result = __agentOSOriginal.apply(__agentOSTarget, args); __agentOSDebugLog(`fs.${__agentOSMethod}:done`, String(target)); return result; } catch (error) { __agentOSDebugLog(`fs.${__agentOSMethod}:error`, String(target), error && error.stack ? error.stack : String(error)); throw error; } }; __agentOSWrapped.__agentOSDebugWrapped = true; __agentOSTarget[__agentOSMethod] = __agentOSWrapped; }; const __agentOSFsPromiseModules = [require('fs/promises'), require('node:fs/promises')]; for (const __agentOSFsPromises of __agentOSFsPromiseModules) { for (const __agentOSMethod of ['access', 'lstat', 'mkdir', 'open', 'readFile', 'readdir', 'readlink', 'realpath', 'rename', 'rm', 'rmdir', 'stat', 'symlink', 'unlink', 'writeFile']) { __agentOSWrapAsyncFsMethod(__agentOSFsPromises, __agentOSMethod); } } const __agentOSFsModules = [require('fs'), require('node:fs')]; for (const __agentOSFs of __agentOSFsModules) { for (const __agentOSMethod of ['accessSync', 'existsSync', 'lstatSync', 'mkdirSync', 'openSync', 'readFileSync', 'readdirSync', 'readlinkSync', 'realpathSync', 'renameSync', 'rmSync', 'rmdirSync', 'statSync', 'symlinkSync', 'unlinkSync', 'writeFileSync']) { __agentOSWrapSyncFsMethod(__agentOSFs, __agentOSMethod); } } }"; let display_stub = format!( "const __agentOSDisplayModulePath = require.resolve({display_module}); const __agentOSLogFileModulePath = require.resolve({log_file_module}); const __agentOSColorPassthrough = new Proxy((value) => value, {{ get: () => __agentOSColorPassthrough, apply: (_target, _thisArg, args) => args[0] }}); class __AgentOSNpmDisplayStub {{ constructor() {{ this.chalk = {{ noColor: __agentOSColorPassthrough, stdout: __agentOSColorPassthrough, stderr: __agentOSColorPassthrough }}; this._logPaused = true; this._logBuffer = []; this._outputBuffer = []; const levels = {{ silent: 0, error: 1, warn: 2, notice: 3, http: 4, info: 5, verbose: 6, silly: 7 }}; const loglevelIndex = process.argv.findIndex((arg) => arg === '--loglevel' || arg.startsWith('--loglevel=')); const loglevelArg = loglevelIndex < 0 ? undefined : process.argv[loglevelIndex]; const configuredLevel = loglevelArg && loglevelArg.includes('=') ? loglevelArg.slice(loglevelArg.indexOf('=') + 1) : process.argv[loglevelIndex + 1]; this._logThreshold = levels[String(configuredLevel || process.env.npm_config_loglevel || 'notice').toLowerCase()] ?? levels.notice; this._shouldLog = (level) => levels[level] === undefined || levels[level] <= this._logThreshold; this._write = (stream, values) => {{ if (!Array.isArray(values) || values.length === 0) {{ return; }} const text = values.map((value) => typeof value === 'string' ? value : String(value)).join(' '); if (text.length === 0) {{ return; }} const normalized = text.replace(/\\r\\n/g, '\\n'); if (/^\\n?> npx\\n> /u.test(normalized)) {{ return; }} stream.write(text.endsWith('\\n') ? text : `${{text}}\\n`); }}; this._inputHandler = (level, ...args) => {{ if (level !== 'read') {{ return; }} const [resolve, reject, callback] = args; Promise.resolve().then(() => callback()).then(resolve, reject); }}; this._logHandler = (level, ...args) => {{ if (level === 'resume') {{ this._logPaused = false; for (const [bufferLevel, bufferArgs] of this._logBuffer.splice(0)) {{ if (this._shouldLog(bufferLevel)) {{ this._write(process.stderr, bufferArgs); }} }} return; }} if (level === 'pause') {{ this._logPaused = true; return; }} if (!this._shouldLog(level)) {{ return; }} if (this._logPaused) {{ this._logBuffer.push([level, args]); return; }} this._write(process.stderr, args); }}; this._outputHandler = (level, ...args) => {{ if (level === 'buffer') {{ this._outputBuffer.push(['standard', args]); return; }} if (level === 'flush') {{ for (const [bufferLevel, bufferArgs] of this._outputBuffer.splice(0)) {{ this._write(bufferLevel === 'error' ? process.stderr : process.stdout, bufferArgs); }} return; }} this._write(level === 'error' ? process.stderr : process.stdout, args); }}; process.on('input', this._inputHandler); process.on('log', this._logHandler); process.on('output', this._outputHandler); }} async load() {{ process.emit('log', 'resume'); process.emit('output', 'flush'); }} off() {{ if (this._inputHandler) {{ process.off('input', this._inputHandler); }} if (this._logHandler) {{ process.off('log', this._logHandler); }} if (this._outputHandler) {{ process.off('output', this._outputHandler); }} this._logBuffer.length = 0; this._outputBuffer.length = 0; }} }} class __AgentOSNpmLogFileStub {{ constructor() {{ this.files = []; }} async load() {{ return []; }} off() {{}} }} globalThis._moduleCache[__agentOSDisplayModulePath] = {{ exports: __AgentOSNpmDisplayStub }}; globalThis._moduleCache[__agentOSLogFileModulePath] = {{ exports: __AgentOSNpmLogFileStub }};", display_module = serde_json::to_string(&guest_display_module) @@ -2288,7 +2288,7 @@ pub(super) fn registered_command_name_for_path(vm: &VmState, path: &str) -> Opti .find_map(|prefix| normalized.strip_prefix(prefix)) .or_else(|| { normalized - .strip_prefix("/__secure_exec/commands/") + .strip_prefix("/__agentos/commands/") .and_then(|suffix| suffix.rsplit('/').next()) })?; (!name.is_empty() && !name.contains('/') && vm.kernel.commands().contains_key(name)) @@ -2608,7 +2608,7 @@ fn resolve_guest_command_path_candidate(vm: &VmState, candidate: &str) -> Option || candidate.starts_with("/usr/bin/") || candidate.starts_with("/usr/local/bin/") || candidate.starts_with(&format!("{}/", crate::package_projection::OPT_AGENTOS_BIN)) - || candidate.starts_with("/__secure_exec/commands/") + || candidate.starts_with("/__agentos/commands/") { if let Some(file_name) = Path::new(candidate) .file_name() @@ -2884,9 +2884,20 @@ pub(super) fn guest_virtual_home(vm: &VmState) -> String { /// Build the typed per-execution Python limits from the per-VM `VmLimits`. pub(super) fn python_execution_limits(vm: &VmState) -> PythonExecutionLimits { + python_execution_limits_with_env(vm, &BTreeMap::new()) +} + +/// Build Python limits while honoring a sidecar-owned per-execution timeout. +pub(super) fn python_execution_limits_with_env( + vm: &VmState, + env: &BTreeMap, +) -> PythonExecutionLimits { PythonExecutionLimits { output_buffer_max_bytes: Some(vm.limits.python.output_buffer_max_bytes), - execution_timeout_ms: Some(vm.limits.python.execution_timeout_ms), + execution_timeout_ms: env + .get("AGENTOS_PYTHON_EXECUTION_TIMEOUT_MS") + .and_then(|value| value.parse().ok()) + .or(Some(vm.limits.python.execution_timeout_ms)), max_old_space_mb: Some(vm.limits.python.max_old_space_mb), vfs_rpc_timeout_ms: Some(vm.limits.python.vfs_rpc_timeout_ms), reactor_work_quantum: vm_reactor_work_quantum(&vm.limits), @@ -4938,7 +4949,7 @@ where file_path: python_file_path, env: env.clone(), cwd: resolved.host_cwd.clone(), - limits: python_execution_limits(vm), + limits: python_execution_limits_with_env(vm, &env), guest_runtime: guest_runtime_identity(vm, None, None), }, vm.runtime_context.clone(), diff --git a/crates/native-sidecar/src/execution/mod.rs b/crates/native-sidecar/src/execution/mod.rs index 34349b918a..0b5df9b174 100644 --- a/crates/native-sidecar/src/execution/mod.rs +++ b/crates/native-sidecar/src/execution/mod.rs @@ -100,9 +100,9 @@ use crate::protocol::{ JavascriptPosixSpawnFileAction, JavascriptSpawnHostNetFd, KillProcessRequest, OwnershipScope, ProcessExitedEvent, ProcessOutputEvent, ProcessSnapshotEntry, ProcessSnapshotStatus, PtyResizedResponse, QueueSnapshotEntry, RequestFrame, ResizePtyRequest, - ResourceSnapshotResponse, ResponseFrame, ResponsePayload, SidecarRequestPayload, - SignalDispositionAction, SignalHandlerRegistration, SocketStateEntry, StreamChannel, - VmFetchRequest, VmFetchResponse, WasmPermissionTier, WriteStdinRequest, + ResourceSnapshotResponse, ResponseFrame, ResponsePayload, RetainedExecutionLanguage, + SidecarRequestPayload, SignalDispositionAction, SignalHandlerRegistration, SocketStateEntry, + StreamChannel, VmFetchRequest, VmFetchResponse, WasmPermissionTier, WriteStdinRequest, }; use crate::service::{ audit_fields, dirname, emit_security_audit_event, emit_structured_event_or_stderr, diff --git a/crates/native-sidecar/src/execution/network/dns.rs b/crates/native-sidecar/src/execution/network/dns.rs index 6133600858..3bb18f532f 100644 --- a/crates/native-sidecar/src/execution/network/dns.rs +++ b/crates/native-sidecar/src/execution/network/dns.rs @@ -138,7 +138,7 @@ fn parse_dns_record_type(rrtype: &str) -> Result { "CAA" => Ok(RecordType::CAA), "ANY" => Ok(RecordType::ANY), other => Err(SidecarError::Execution(format!( - "ERR_NOT_IMPLEMENTED: dns rrtype {other} is not supported by the secure-exec dns bridge" + "ERR_NOT_IMPLEMENTED: dns rrtype {other} is not supported by the agentos dns bridge" ))), } } @@ -324,7 +324,7 @@ fn dns_resolution_to_node_value( .collect(), )), other => Err(SidecarError::Execution(format!( - "ERR_NOT_IMPLEMENTED: dns rrtype {other} is not supported by the secure-exec dns bridge" + "ERR_NOT_IMPLEMENTED: dns rrtype {other} is not supported by the agentos dns bridge" ))), } } diff --git a/crates/native-sidecar/src/execution/network/udp.rs b/crates/native-sidecar/src/execution/network/udp.rs index 9a683b7945..3411b29060 100644 --- a/crates/native-sidecar/src/execution/network/udp.rs +++ b/crates/native-sidecar/src/execution/network/udp.rs @@ -1207,7 +1207,7 @@ impl ActiveUdpSocket { ) -> Result { if self.native_commands.is_some() || self.guest_local_addr.is_some() { return Err(SidecarError::Execution(String::from( - "EINVAL: secure-exec dgram socket is already bound", + "EINVAL: agentos dgram socket is already bound", ))); } diff --git a/crates/native-sidecar/src/execution/process.rs b/crates/native-sidecar/src/execution/process.rs index 05a0bcfab2..ec24f235f9 100644 --- a/crates/native-sidecar/src/execution/process.rs +++ b/crates/native-sidecar/src/execution/process.rs @@ -1545,6 +1545,26 @@ impl ActiveExecution { matches!(self, Self::Javascript(execution) if execution.has_exited()) } + pub(crate) fn execute_retained_language( + &mut self, + language: RetainedExecutionLanguage, + source: String, + file_path: String, + module: bool, + ) -> Result<(), SidecarError> { + match (self, language) { + (Self::Javascript(execution), RetainedExecutionLanguage::JavaScript) => execution + .execute_retained(source, file_path, module) + .map_err(|error| SidecarError::Execution(error.to_string())), + (Self::Python(execution), RetainedExecutionLanguage::Python) => execution + .execute_retained(source) + .map_err(|error| SidecarError::Execution(error.to_string())), + _ => Err(SidecarError::InvalidState(String::from( + "retained language does not match the resident executor", + ))), + } + } + pub(crate) fn child_pid(&self) -> u32 { match self { Self::Javascript(execution) => execution.child_pid(), diff --git a/crates/native-sidecar/src/execution/process_events.rs b/crates/native-sidecar/src/execution/process_events.rs index 248a2360ad..ae7037fc22 100644 --- a/crates/native-sidecar/src/execution/process_events.rs +++ b/crates/native-sidecar/src/execution/process_events.rs @@ -1,4 +1,5 @@ use super::*; +use crate::protocol::ExecutionStreamChannel; pub(super) struct BindingProcessEventRequest { pub(super) runtime_context: agentos_runtime::RuntimeContext, @@ -406,6 +407,7 @@ where ownership: &OwnershipScope, ) -> Result { let mut emitted_any = false; + self.expire_public_execution_deadlines()?; let mut queued_envelopes = Vec::new(); { @@ -846,12 +848,29 @@ where } let (connection_id, session_id) = { (vm.connection_id.clone(), vm.session_id.clone()) }; let ownership = OwnershipScope::vm(&connection_id, &session_id, vm_id); + let public_execution = self.is_public_execution_process(vm_id, process_id); if self.capture_extension_process_output_event(vm_id, process_id, &event) { return Ok(None); } match event { + ActiveExecutionEvent::Stdout(chunk) if public_execution => Ok(self + .record_public_execution_output( + vm_id, + process_id, + ExecutionStreamChannel::Stdout, + chunk, + ) + .map(|payload| EventFrame::new(ownership, payload))), + ActiveExecutionEvent::Stderr(chunk) if public_execution => Ok(self + .record_public_execution_output( + vm_id, + process_id, + ExecutionStreamChannel::Stderr, + chunk, + ) + .map(|payload| EventFrame::new(ownership, payload))), ActiveExecutionEvent::Stdout(chunk) => Ok(Some(EventFrame::new( ownership, EventPayload::ProcessOutput(ProcessOutputEvent { @@ -909,25 +928,37 @@ where process_id, ); record_execute_response_to_exit(vm_id, process_id); + let park_resident = public_execution + && self.should_park_public_execution_process(vm_id, process_id); let phase_start = Instant::now(); - let became_idle = self - .finish_active_process_exit(vm_id, process_id, exit_code)? - .unwrap_or(false); + let became_idle = if park_resident { + false + } else { + self.finish_active_process_exit(vm_id, process_id, exit_code)? + .unwrap_or(false) + }; record_execute_phase("process_exit_cleanup", phase_start.elapsed()); let phase_start = Instant::now(); - if became_idle { + if became_idle || (park_resident && !self.has_running_nonresident_processes(vm_id)) + { self.bridge.emit_lifecycle(vm_id, LifecycleState::Ready)?; } record_execute_phase("process_exit_lifecycle_emit", phase_start.elapsed()); - Ok(Some(EventFrame::new( - ownership, - EventPayload::ProcessExited(ProcessExitedEvent { - process_id: process_id.to_owned(), - exit_code, - }), - ))) + if public_execution { + Ok(self + .complete_public_execution(vm_id, process_id, exit_code) + .map(|payload| EventFrame::new(ownership, payload))) + } else { + Ok(Some(EventFrame::new( + ownership, + EventPayload::ProcessExited(ProcessExitedEvent { + process_id: process_id.to_owned(), + exit_code, + }), + ))) + } } } } diff --git a/crates/native-sidecar/src/filesystem.rs b/crates/native-sidecar/src/filesystem.rs index 9cdb048dd6..f7d7c07f46 100644 --- a/crates/native-sidecar/src/filesystem.rs +++ b/crates/native-sidecar/src/filesystem.rs @@ -5869,7 +5869,7 @@ mod tests { ( Some(writable_mapping( "/mapped/file.txt", - "/tmp/secure-exec-mapped-source", + "/tmp/agentos-mapped-source", )), None, ), @@ -5877,7 +5877,7 @@ mod tests { None, Some(writable_mapping( "/mapped-dst/file.txt", - "/tmp/secure-exec-mapped-destination", + "/tmp/agentos-mapped-destination", )), ), ] { diff --git a/crates/native-sidecar/src/language_execution.rs b/crates/native-sidecar/src/language_execution.rs new file mode 100644 index 0000000000..1e6e98ab56 --- /dev/null +++ b/crates/native-sidecar/src/language_execution.rs @@ -0,0 +1,2072 @@ +//! First-class language execution admission and lifecycle. +//! +//! The public protocol names semantic operations. This module is the only +//! place that lowers them to the process transport used by the execution +//! engines; clients never construct runtime or package-manager commands. + +use crate::protocol::*; +use crate::service::{normalize_path, DispatchResult, NativeSidecar, SidecarError}; +use crate::state::{BridgeError, ExecutionValueKind, ManagedLanguageExecution}; +use crate::NativeSidecarBridge; +use oxc_allocator::Allocator; +use oxc_ast::ast::{ImportDeclarationSpecifier, Statement}; +use oxc_codegen::Codegen; +use oxc_parser::Parser; +use oxc_semantic::SemanticBuilder; +use oxc_span::SourceType; +use oxc_transformer::{Module, TransformOptions, Transformer}; +use std::collections::{BTreeMap, VecDeque}; +use std::fmt; +use std::path::Path; +use std::sync::Arc; +use std::time::{SystemTime, UNIX_EPOCH}; + +const EXECUTION_EVENT_BYTES_LIMIT: usize = 2 * 1024 * 1024; +const EXECUTION_RECORD_LIMIT: usize = 1_024; +const MAX_EXECUTION_OUTPUT_PAGE_EVENTS: u32 = 1_000; +const DEFAULT_EXECUTION_OUTPUT_PAGE_EVENTS: u32 = 100; +const EXECUTION_CANCEL_GRACE_MS: u64 = 1_000; +const TTY_ENV: &str = "AGENTOS_EXEC_TTY"; +const TTY_COLS_ENV: &str = "AGENTOS_EXEC_TTY_COLS"; +const TTY_ROWS_ENV: &str = "AGENTOS_EXEC_TTY_ROWS"; +const RETAIN_LANGUAGE_CONTEXT_ENV: &str = "AGENTOS_RETAIN_LANGUAGE_CONTEXT"; +const INLINE_FILE_PATH_ENV: &str = "AGENTOS_INLINE_FILE_PATH"; +const USE_BUNDLED_TYPESCRIPT_ENV: &str = "AGENTOS_USE_BUNDLED_TYPESCRIPT"; + +#[derive(Debug)] +struct LoweredOperation { + identity: ExecutionIdentityOptions, + command: String, + args: Vec, + cwd: Option, + env: BTreeMap, + stdin: Option>, + pty: Option, + timeout_ms: Option, + retained_language: Option, + retained_source: Option, + retained_file_path: Option, + retained_module: bool, + package_mutation: bool, + value_kind: ExecutionValueKind, + value_marker: Option, +} + +fn now_ms() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis() + .try_into() + .unwrap_or(u64::MAX) +} + +fn options( + process: ProcessExecutionOptions, +) -> ( + ExecutionIdentityOptions, + Vec, + Option, + BTreeMap, + Option>, + Option, + Option, +) { + ( + process.identity, + process.args, + process.cwd, + process.env.unwrap_or_default().into_iter().collect(), + process.stdin, + process.pty, + process.timeout_ms, + ) +} + +fn inline_inputs_prefix(inputs: Option, python: bool) -> String { + let inputs = inputs.unwrap_or_else(|| String::from("{}")); + if python { + format!( + "import json as __agentos_json\ninputs = __agentos_json.loads({})\n", + serde_json::to_string(&inputs).expect("JSON string serialization cannot fail") + ) + } else { + format!( + "globalThis.inputs = Object.freeze(JSON.parse({}));\n", + serde_json::to_string(&inputs).expect("JSON string serialization cannot fail") + ) + } +} + +fn evaluation_marker(execution_id_hint: Option<&str>) -> String { + let nonce = now_ms(); + format!( + "__AGENTOS_EVALUATION_{}_{}__", + execution_id_hint.unwrap_or("new"), + nonce + ) +} + +fn typescript_check_runner(request: serde_json::Value, marker: &str) -> String { + const RUNNER: &str = r#" +const __request = __AGENTOS_TYPESCRIPT_REQUEST__; +const __compilerPath = process.env.AGENTOS_TYPESCRIPT_COMPILER_PATH; +if (!__compilerPath) throw new Error("bundled TypeScript compiler path is unavailable"); +const ts = require(__compilerPath); +const path = require("node:path"); + +const diagnostic = (item) => { + const result = { + code: item.code, + category: item.category === ts.DiagnosticCategory.Warning + ? "warning" + : item.category === ts.DiagnosticCategory.Suggestion + ? "suggestion" + : item.category === ts.DiagnosticCategory.Message + ? "message" + : "error", + message: ts.flattenDiagnosticMessageText(item.messageText, "\n").trim(), + }; + if (item.file && item.start !== undefined) { + const location = item.file.getLineAndCharacterOfPosition(item.start); + result.filePath = item.file.fileName.replace(/\\/g, "/"); + result.line = location.line + 1; + result.column = location.character + 1; + } + return result; +}; + +const cwd = path.resolve(__request.cwd || process.cwd()); +let diagnostics; +if (__request.kind === "project") { + const configPath = __request.tsconfigPath + ? path.resolve(cwd, __request.tsconfigPath) + : ts.findConfigFile(cwd, ts.sys.fileExists, "tsconfig.json"); + if (!configPath) throw new Error(`Unable to find tsconfig.json from '${cwd}'`); + const config = ts.readConfigFile(configPath, ts.sys.readFile); + if (config.error) { + diagnostics = [config.error]; + } else { + const parsed = ts.parseJsonConfigFileContent( + config.config, + ts.sys, + path.dirname(configPath), + { noEmit: true }, + configPath, + ); + const program = ts.createProgram({ + rootNames: parsed.fileNames, + options: parsed.options, + projectReferences: parsed.projectReferences, + }); + diagnostics = [...parsed.errors, ...ts.getPreEmitDiagnostics(program)]; + } +} else { + const filePath = path.resolve(cwd, __request.filePath || "agentos-inline.ts"); + let projectOptions = {}; + let configDiagnostics = []; + if (__request.tsconfigPath) { + const configPath = path.resolve(cwd, __request.tsconfigPath); + const config = ts.readConfigFile(configPath, ts.sys.readFile); + if (config.error) { + configDiagnostics = [config.error]; + } else { + const parsed = ts.parseJsonConfigFileContent( + config.config, + ts.sys, + path.dirname(configPath), + {}, + configPath, + ); + projectOptions = parsed.options; + configDiagnostics = parsed.errors; + } + } + const converted = ts.convertCompilerOptionsFromJson( + __request.compilerOptions || {}, + cwd, + ); + const compilerOptions = { + target: ts.ScriptTarget.ES2022, + module: ts.ModuleKind.CommonJS, + ...projectOptions, + ...converted.options, + noEmit: true, + }; + const host = ts.createCompilerHost(compilerOptions); + const normalizedFilePath = ts.sys.useCaseSensitiveFileNames + ? filePath + : filePath.toLowerCase(); + const originalFileExists = host.fileExists.bind(host); + const originalReadFile = host.readFile.bind(host); + const originalGetSourceFile = host.getSourceFile.bind(host); + const normalize = (candidate) => ts.sys.useCaseSensitiveFileNames + ? candidate + : candidate.toLowerCase(); + host.fileExists = (candidate) => + normalize(candidate) === normalizedFilePath || originalFileExists(candidate); + host.readFile = (candidate) => + normalize(candidate) === normalizedFilePath + ? __request.source + : originalReadFile(candidate); + host.getSourceFile = (candidate, languageVersion, onError, fresh) => + normalize(candidate) === normalizedFilePath + ? ts.createSourceFile(candidate, __request.source, languageVersion, true) + : originalGetSourceFile(candidate, languageVersion, onError, fresh); + const program = ts.createProgram([filePath], compilerOptions, host); + diagnostics = [ + ...configDiagnostics, + ...converted.errors, + ...ts.getPreEmitDiagnostics(program), + ]; +} + +const result = ts.sortAndDeduplicateDiagnostics(diagnostics).map(diagnostic); +console.log(__AGENTOS_TYPESCRIPT_MARKER__ + JSON.stringify({ + hasErrors: result.some((item) => item.category === "error"), + diagnostics: result, +})); +"#; + format!("(async () => {{\n{}\n}})()", RUNNER) + .replace( + "__AGENTOS_TYPESCRIPT_REQUEST__", + &serde_json::to_string(&request).expect("TypeScript request serialization cannot fail"), + ) + .replace( + "__AGENTOS_TYPESCRIPT_MARKER__", + &serde_json::to_string(marker).expect("TypeScript marker serialization cannot fail"), + ) +} + +fn transform_source( + source: &str, + file_path: &str, + typescript: bool, + common_js: bool, +) -> Result { + let allocator = Allocator::default(); + let source_type = SourceType::from_path(Path::new(file_path)) + .unwrap_or_default() + .with_typescript(typescript) + .with_module(true); + let parsed = Parser::new(&allocator, source, source_type).parse(); + if !parsed.errors.is_empty() { + let message = parsed + .errors + .into_iter() + .map(|error| error.to_string()) + .collect::>() + .join("\n"); + let language = if typescript { + "TypeScript" + } else { + "JavaScript" + }; + return Err(SidecarError::InvalidState(format!( + "{language} syntax error in {file_path}: {message}" + ))); + } + + let mut program = parsed.program; + let semantic = SemanticBuilder::new() + .with_excess_capacity(2.0) + .build(&program); + if !semantic.errors.is_empty() { + let message = semantic + .errors + .into_iter() + .map(|error| error.to_string()) + .collect::>() + .join("\n"); + let language = if typescript { + "TypeScript" + } else { + "JavaScript" + }; + return Err(SidecarError::InvalidState(format!( + "{language} semantic transform error in {file_path}: {message}" + ))); + } + let mut transform_options = TransformOptions::default(); + if common_js { + transform_options.env.module = Module::CommonJS; + } + let result = Transformer::new(&allocator, Path::new(file_path), &transform_options) + .build_with_scoping(semantic.semantic.into_scoping(), &mut program); + if !result.errors.is_empty() { + let message = result + .errors + .into_iter() + .map(|error| error.to_string()) + .collect::>() + .join("\n"); + let language = if typescript { + "TypeScript" + } else { + "JavaScript" + }; + return Err(SidecarError::InvalidState(format!( + "{language} transpilation failed for {file_path}: {message}" + ))); + } + Ok(Codegen::new().build(&program).code) +} + +fn transpile_typescript( + source: &str, + file_path: &str, + common_js: bool, +) -> Result { + transform_source(source, file_path, true, common_js) +} + +fn transform_retained_javascript_module( + source: &str, + file_path: &str, +) -> Result { + let source = rewrite_static_imports(source, file_path, false)?; + transform_source(&source, file_path, false, true) +} + +fn transform_retained_typescript_module( + source: &str, + file_path: &str, +) -> Result { + let source = rewrite_static_imports(source, file_path, true)?; + transform_source(&source, file_path, true, true) +} + +/// Retained cells execute as scripts so their lexical declarations remain in +/// the context's shared script environment. Rewrite only static imports into +/// equivalent `require` declarations before the normal OXC transform; this +/// keeps the caller's local import names as real retained lexical bindings. +fn rewrite_static_imports( + source: &str, + file_path: &str, + typescript: bool, +) -> Result { + let allocator = Allocator::default(); + let source_type = SourceType::from_path(Path::new(file_path)) + .unwrap_or_default() + .with_typescript(typescript) + .with_module(true); + let parsed = Parser::new(&allocator, source, source_type).parse(); + if !parsed.errors.is_empty() { + let language = if typescript { + "TypeScript" + } else { + "JavaScript" + }; + let message = parsed + .errors + .into_iter() + .map(|error| error.to_string()) + .collect::>() + .join("\n"); + return Err(SidecarError::InvalidState(format!( + "{language} syntax error in {file_path}: {message}" + ))); + } + + let mut replacements = Vec::new(); + for statement in &parsed.program.body { + let Statement::ImportDeclaration(declaration) = statement else { + continue; + }; + let replacement = if declaration.import_kind.is_type() { + String::new() + } else { + let source_literal = serde_json::to_string(declaration.source.value.as_str()) + .expect("module specifier serialization cannot fail"); + let mut declarations = Vec::new(); + match declaration.specifiers.as_deref() { + None => declarations.push(format!("require({source_literal});")), + Some(specifiers) if specifiers.is_empty() => { + declarations.push(format!("require({source_literal});")); + } + Some(specifiers) => { + for specifier in specifiers { + match specifier { + ImportDeclarationSpecifier::ImportSpecifier(specifier) => { + if specifier.import_kind.is_type() { + continue; + } + let imported = + serde_json::to_string(specifier.imported.name().as_str()) + .expect("import name serialization cannot fail"); + declarations.push(format!( + "const {} = require({source_literal})[{imported}];", + specifier.local.name + )); + } + ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => { + declarations.push(format!( + "const {} = (() => {{ const value = require({source_literal}); return value && value.__esModule ? value.default : value; }})();", + specifier.local.name + )); + } + ImportDeclarationSpecifier::ImportNamespaceSpecifier(specifier) => { + declarations.push(format!( + "const {} = require({source_literal});", + specifier.local.name + )); + } + } + } + } + } + declarations.join("\n") + }; + replacements.push(( + declaration.span.start as usize, + declaration.span.end as usize, + replacement, + )); + } + + if replacements.is_empty() { + return Ok(source.to_owned()); + } + let mut rewritten = source.to_owned(); + for (start, end, replacement) in replacements.into_iter().rev() { + rewritten.replace_range(start..end, &replacement); + } + Ok(rewritten) +} + +fn lowered_process( + process: ProcessExecutionOptions, + command: impl Into, + mut prefix_args: Vec, +) -> LoweredOperation { + let (identity, args, cwd, env, stdin, pty, timeout_ms) = options(process); + prefix_args.extend(args); + LoweredOperation { + identity, + command: command.into(), + args: prefix_args, + cwd, + env, + stdin, + pty, + timeout_ms, + retained_language: None, + retained_source: None, + retained_file_path: None, + retained_module: false, + package_mutation: false, + value_kind: ExecutionValueKind::None, + value_marker: None, + } +} + +fn lowered_install( + identity: ExecutionIdentityOptions, + cwd: Option, + env: Option>, + timeout_ms: Option, + command: impl Into, + args: Vec, +) -> LoweredOperation { + LoweredOperation { + identity, + command: command.into(), + args, + cwd, + env: env.unwrap_or_default().into_iter().collect(), + stdin: None, + pty: None, + timeout_ms, + retained_language: None, + retained_source: None, + retained_file_path: None, + retained_module: false, + package_mutation: false, + value_kind: ExecutionValueKind::None, + value_marker: None, + } +} + +fn lower_operation(payload: RequestPayload) -> Result { + let lowered = match payload { + RequestPayload::ShellExecution(payload) => lowered_process( + payload.process, + "sh", + vec![String::from("-c"), payload.command], + ), + RequestPayload::ArgvExecution(payload) => { + lowered_process(payload.process, payload.command, Vec::new()) + } + RequestPayload::JavaScriptExecution(payload) => { + let file_path = payload + .file_path + .unwrap_or_else(|| String::from("/[agentos-inline.js]")); + let module = payload.format == Some(JavaScriptModuleFormat::Module); + let mut source = inline_inputs_prefix(payload.inputs, false); + source.push_str(&payload.source); + if module { + source = transform_retained_javascript_module(&source, &file_path)?; + } + let retained_source = source.clone(); + let mut operation = + lowered_process(payload.process, "node", vec![String::from("-e"), source]); + operation.retained_language = Some(RetainedExecutionLanguage::JavaScript); + operation.retained_source = Some(retained_source); + operation.retained_file_path = Some(file_path.clone()); + operation.retained_module = false; + operation + .env + .insert(String::from(INLINE_FILE_PATH_ENV), file_path); + operation + } + RequestPayload::JavaScriptEvaluation(payload) => { + let file_path = payload + .file_path + .unwrap_or_else(|| String::from("/[agentos-evaluation.js]")); + let module = payload.format == Some(JavaScriptModuleFormat::Module); + let marker = evaluation_marker(payload.process.identity.execution_id.as_deref()); + let mut source = inline_inputs_prefix(payload.inputs, false); + source.push_str(&format!( + "Promise.resolve((async () => ({}))()).then((value) => {{ let result; try {{ if (value === undefined || typeof value === 'function' || typeof value === 'symbol') throw new TypeError('undefined, functions, and symbols are not supported'); result = JSON.stringify({{ __agentosEvaluation: true, ok: true, value }}); }} catch (error) {{ result = JSON.stringify({{ __agentosEvaluation: true, ok: false, error: `AgentOS evaluation result must be JSON-serializable: ${{error instanceof Error ? error.message : String(error)}}` }}); }} console.log({} + result); }});", + payload.expression, + serde_json::to_string(&marker).expect("marker serialization cannot fail") + )); + if module { + source = transform_retained_javascript_module(&source, &file_path)?; + } + let mut operation = + lowered_process(payload.process, "node", vec![String::from("-e"), source]); + operation.retained_language = Some(RetainedExecutionLanguage::JavaScript); + operation.retained_source = operation.args.get(1).cloned(); + operation.retained_file_path = Some(file_path.clone()); + operation.retained_module = false; + operation + .env + .insert(String::from(INLINE_FILE_PATH_ENV), file_path); + operation.value_kind = ExecutionValueKind::JavaScript; + operation.value_marker = Some(marker); + operation + } + RequestPayload::JavaScriptFileExecution(payload) => { + lowered_process(payload.process, "node", vec![payload.path]) + } + RequestPayload::TypeScriptExecution(payload) => { + let file_path = payload + .file_path + .unwrap_or_else(|| String::from("agentos-inline.ts")); + let mut source = inline_inputs_prefix(payload.inputs, false); + source.push_str(&payload.source); + let source = transform_retained_typescript_module(&source, &file_path)?; + let mut operation = + lowered_process(payload.process, "node", vec![String::from("-e"), source]); + operation.retained_language = Some(RetainedExecutionLanguage::JavaScript); + operation.retained_source = operation.args.get(1).cloned(); + operation.retained_file_path = Some(file_path.clone()); + operation + .env + .insert(String::from(INLINE_FILE_PATH_ENV), file_path); + operation + } + RequestPayload::TypeScriptEvaluation(payload) => { + let marker = evaluation_marker(payload.process.identity.execution_id.as_deref()); + let file_path = payload + .file_path + .unwrap_or_else(|| String::from("agentos-evaluation.ts")); + let mut source = inline_inputs_prefix(payload.inputs, false); + source.push_str(&format!( + "Promise.resolve((async () => ({}))()).then((value) => {{ let result; try {{ if (value === undefined || typeof value === 'function' || typeof value === 'symbol') throw new TypeError('undefined, functions, and symbols are not supported'); result = JSON.stringify({{ __agentosEvaluation: true, ok: true, value }}); }} catch (error) {{ result = JSON.stringify({{ __agentosEvaluation: true, ok: false, error: `AgentOS evaluation result must be JSON-serializable: ${{error instanceof Error ? error.message : String(error)}}` }}); }} console.log({} + result); }});", + payload.expression, + serde_json::to_string(&marker).expect("marker serialization cannot fail") + )); + let source = transform_retained_typescript_module(&source, &file_path)?; + let mut operation = + lowered_process(payload.process, "node", vec![String::from("-e"), source]); + operation.retained_language = Some(RetainedExecutionLanguage::JavaScript); + operation.retained_source = operation.args.get(1).cloned(); + operation.retained_file_path = Some(file_path.clone()); + operation + .env + .insert(String::from(INLINE_FILE_PATH_ENV), file_path); + operation.value_kind = ExecutionValueKind::JavaScript; + operation.value_marker = Some(marker); + operation + } + RequestPayload::TypeScriptFileExecution(payload) => lowered_process( + payload.process, + "__agentos_typescript_file", + vec![payload.path], + ), + RequestPayload::TypeScriptCheck(payload) => { + let marker = evaluation_marker(payload.identity.execution_id.as_deref()); + let request = serde_json::json!({ + "kind": "source", + "source": payload.source, + "cwd": payload.cwd, + "filePath": payload.file_path, + "tsconfigPath": payload.tsconfig_path, + "compilerOptions": payload + .compiler_options + .as_deref() + .map(serde_json::from_str::) + .transpose() + .map_err(|error| SidecarError::InvalidState(format!("invalid TypeScript compiler options: {error}")))?, + }); + let mut operation = lowered_install( + payload.identity, + request["cwd"].as_str().map(str::to_owned), + None, + payload.timeout_ms, + "node", + vec![ + String::from("-e"), + typescript_check_runner(request, &marker), + ], + ); + operation + .env + .insert(String::from(USE_BUNDLED_TYPESCRIPT_ENV), String::from("1")); + operation.value_kind = ExecutionValueKind::TypeScriptCheck; + operation.value_marker = Some(marker); + operation + } + RequestPayload::TypeScriptProjectCheck(payload) => { + let marker = evaluation_marker(payload.identity.execution_id.as_deref()); + let cwd = payload.cwd.clone(); + let request = serde_json::json!({ + "kind": "project", + "cwd": payload.cwd, + "tsconfigPath": payload.tsconfig_path, + }); + let mut operation = lowered_install( + payload.identity, + cwd, + None, + payload.timeout_ms, + "node", + vec![ + String::from("-e"), + typescript_check_runner(request, &marker), + ], + ); + operation + .env + .insert(String::from(USE_BUNDLED_TYPESCRIPT_ENV), String::from("1")); + operation.value_kind = ExecutionValueKind::TypeScriptCheck; + operation.value_marker = Some(marker); + operation + } + RequestPayload::NpmProjectInstall(payload) => { + let args = if payload.frozen.unwrap_or(false) { + vec![String::from("ci")] + } else { + vec![String::from("install")] + }; + let mut operation = lowered_install( + payload.identity, + payload.cwd, + payload.env, + payload.timeout_ms, + "npm", + args, + ); + operation.package_mutation = true; + operation + } + RequestPayload::NpmPackageInstall(payload) => { + let mut args = vec![String::from("install")]; + if payload.dev.unwrap_or(false) { + args.push(String::from("--save-dev")); + } + if payload.global.unwrap_or(false) { + args.push(String::from("--global")); + } + args.extend(payload.packages); + let mut operation = lowered_install( + payload.identity, + payload.cwd, + payload.env, + payload.timeout_ms, + "npm", + args, + ); + operation.package_mutation = true; + operation + } + RequestPayload::NpmScriptExecution(payload) => { + let script = payload.script; + lowered_process( + payload.process, + "npm", + vec![String::from("run"), script, String::from("--")], + ) + } + RequestPayload::NpmPackageExecution(payload) => { + let mut args = vec![ + String::from("exec"), + String::from("--package"), + payload.package_spec, + ]; + if let Some(binary) = payload.binary { + args.extend([String::from("--"), binary]); + } + lowered_process(payload.process, "npm", args) + } + RequestPayload::PythonExecution(payload) => { + let mut source = inline_inputs_prefix(payload.inputs, true); + source.push_str(&payload.source); + let mut operation = + lowered_process(payload.process, "python", vec![String::from("-c"), source]); + operation.retained_language = Some(RetainedExecutionLanguage::Python); + operation.retained_source = operation.args.get(1).cloned(); + operation + } + RequestPayload::PythonEvaluation(payload) => { + let marker = evaluation_marker(payload.process.identity.execution_id.as_deref()); + let mut source = inline_inputs_prefix(payload.inputs, true); + source.push_str(&format!( + "\n__agentos_value = ({})\ntry:\n __agentos_result = __agentos_json.dumps({{\"__agentosEvaluation\": True, \"ok\": True, \"value\": __agentos_value}}, allow_nan=False)\nexcept Exception as __agentos_error:\n __agentos_result = __agentos_json.dumps({{\"__agentosEvaluation\": True, \"ok\": False, \"error\": \"AgentOS evaluation result must be JSON-serializable: \" + str(__agentos_error)}})\nprint({} + __agentos_result)\n", + payload.expression, + serde_json::to_string(&marker).expect("marker serialization cannot fail") + )); + let mut operation = + lowered_process(payload.process, "python", vec![String::from("-c"), source]); + operation.retained_language = Some(RetainedExecutionLanguage::Python); + operation.retained_source = operation.args.get(1).cloned(); + operation.value_kind = ExecutionValueKind::Python; + operation.value_marker = Some(marker); + operation + } + RequestPayload::PythonFileExecution(payload) => { + lowered_process(payload.process, "python", vec![payload.path]) + } + RequestPayload::PythonModuleExecution(payload) => lowered_process( + payload.process, + "python", + vec![String::from("-m"), payload.module], + ), + RequestPayload::PythonInstall(payload) => { + if !payload.packages.is_empty() && payload.requirements_file.is_some() { + return Err(SidecarError::InvalidState(String::from( + "installPythonPackages cannot combine packages with requirementsFile", + ))); + } + let mut args = vec![ + String::from("-m"), + String::from("pip"), + String::from("install"), + ]; + if payload.upgrade.unwrap_or(false) { + args.push(String::from("--upgrade")); + } + if let Some(path) = payload.requirements_file { + args.extend([String::from("--requirement"), path]); + } + if let Some(url) = payload.index_url { + args.extend([String::from("--index-url"), url]); + } + for url in payload.extra_index_urls { + args.extend([String::from("--extra-index-url"), url]); + } + args.extend(payload.packages); + let mut operation = lowered_install( + payload.identity, + payload.cwd, + payload.env, + payload.timeout_ms, + "python", + args, + ); + operation.package_mutation = true; + operation + } + _ => { + return Err(SidecarError::InvalidState(String::from( + "request is not a language execution operation", + ))) + } + }; + Ok(lowered) +} + +fn typed_rejection(request: &RequestFrame, code: &str, message: impl AsRef) -> DispatchResult { + DispatchResult { + response: agentos_native_sidecar_core::reject(request, code, message.as_ref()), + events: Vec::new(), + } +} + +impl NativeSidecar +where + B: NativeSidecarBridge + Send + 'static, + BridgeError: fmt::Debug + Send + Sync + 'static, +{ + pub(crate) async fn execute_language_operation( + &mut self, + request: &RequestFrame, + payload: RequestPayload, + ) -> Result { + // The caller deadline begins before source transformation, guest-file + // staging, and compiler staging. The remaining budget is handed to the + // runtime after those sidecar-owned phases finish. + let operation_started_at_ms = now_ms(); + let mut operation = match lower_operation(payload) { + Ok(operation) => operation, + Err(error) => { + return Ok(typed_rejection( + request, + "invalid_execution_request", + error.to_string(), + )); + } + }; + let (connection_id, session_id, vm_id) = self.vm_scope_for(&request.ownership)?; + self.require_owned_vm(&connection_id, &session_id, &vm_id)?; + + if operation.command == "__agentos_typescript_file" { + let requested_path = operation.args.first().cloned().ok_or_else(|| { + SidecarError::InvalidState(String::from( + "executeTypeScriptFile requires a file path", + )) + })?; + let vm = self + .vms + .get_mut(&vm_id) + .ok_or_else(|| SidecarError::InvalidState(format!("unknown sidecar VM {vm_id}")))?; + let guest_path = if requested_path.starts_with('/') { + normalize_path(&requested_path) + } else { + let cwd = operation.cwd.as_deref().unwrap_or(&vm.guest_cwd); + normalize_path(&format!("{}/{requested_path}", cwd.trim_end_matches('/'))) + }; + let source = vm.kernel.read_file(&guest_path).map_err(|error| { + SidecarError::InvalidState(format!( + "failed to read TypeScript file {guest_path}: {error}" + )) + })?; + let source = String::from_utf8(source).map_err(|error| { + SidecarError::InvalidState(format!( + "TypeScript file {guest_path} is not UTF-8: {error}" + )) + })?; + operation.command = String::from("node"); + operation.args = vec![ + String::from("-e"), + transpile_typescript(&source, &guest_path, false)?, + ]; + operation.env.insert( + String::from("AGENTOS_GUEST_ENTRYPOINT_MODULE_MODE"), + String::from("1"), + ); + operation + .env + .insert(String::from(INLINE_FILE_PATH_ENV), guest_path); + } + + if operation + .env + .get(USE_BUNDLED_TYPESCRIPT_ENV) + .is_some_and(|value| value == "1" || value.eq_ignore_ascii_case("true")) + { + const COMPILER_ROOT: &str = "/.agentos/runtime/typescript"; + const COMPILER_PATH: &str = "/.agentos/runtime/typescript/typescript.js"; + let vm = self + .vms + .get_mut(&vm_id) + .ok_or_else(|| SidecarError::InvalidState(format!("unknown sidecar VM {vm_id}")))?; + if !vm.typescript_compiler_staged { + let assets = agentos_execution::bundled_typescript_assets(); + if assets.is_empty() { + return Err(SidecarError::InvalidState(String::from( + "bundled TypeScript compiler is unavailable in this build", + ))); + } + vm.kernel.mkdir(COMPILER_ROOT, true).map_err(|error| { + SidecarError::InvalidState(format!( + "failed to create TypeScript compiler runtime directory: {error}" + )) + })?; + for (file_name, bytes) in assets { + vm.kernel + .write_file(&format!("{COMPILER_ROOT}/{file_name}"), bytes.to_vec()) + .map_err(|error| { + SidecarError::InvalidState(format!( + "failed to stage TypeScript compiler asset {file_name}: {error}" + )) + })?; + } + vm.typescript_compiler_staged = true; + } + operation.env.insert( + String::from("AGENTOS_TYPESCRIPT_COMPILER_PATH"), + String::from(COMPILER_PATH), + ); + } + + let now = now_ms(); + if let Some(timeout_ms) = operation.timeout_ms { + let deadline_ms = operation_started_at_ms.saturating_add(timeout_ms); + operation.timeout_ms = Some(deadline_ms.saturating_sub(now).max(1)); + } + let (output_limit_bytes, output_limit_setting) = { + let vm = self + .vms + .get(&vm_id) + .ok_or_else(|| SidecarError::InvalidState(format!("unknown sidecar VM {vm_id}")))?; + match operation.command.as_str() { + "node" | "npm" | "npx" | "__agentos_typescript_file" => ( + vm.limits.js_runtime.captured_output_limit_bytes, + "limits.jsRuntime.capturedOutputLimitBytes", + ), + "python" | "python3" | "pip" | "pip3" => ( + vm.limits.python.output_buffer_max_bytes, + "limits.python.outputBufferMaxBytes", + ), + _ => ( + vm.limits.wasm.captured_output_limit_bytes, + "limits.wasm.capturedOutputLimitBytes", + ), + } + }; + let execution_id = { + let vm = self + .vms + .get_mut(&vm_id) + .ok_or_else(|| SidecarError::InvalidState(format!("unknown sidecar VM {vm_id}")))?; + if operation.package_mutation { + let active_mutation = vm + .package_mutation_execution_id + .as_ref() + .filter(|execution_id| { + vm.executions.get(*execution_id).is_some_and(|execution| { + execution.descriptor.state == ExecutionState::Running + }) + }) + .cloned(); + if let Some(active_mutation) = active_mutation { + return Ok(typed_rejection( + request, + "execution_busy", + format!( + "package mutation execution {active_mutation} is already running in this VM; package installs are serialized at VM scope" + ), + )); + } + vm.package_mutation_execution_id = None; + } + match operation.identity.execution_id.take() { + Some(execution_id) => { + if let Some(existing) = vm.executions.get(&execution_id) { + if existing.descriptor.state == ExecutionState::Running { + return Ok(typed_rejection( + request, + "execution_busy", + format!("execution {execution_id} already has an active operation"), + )); + } + if existing.descriptor.state == ExecutionState::Failed { + return Ok(typed_rejection( + request, + "execution_failed", + format!("execution {execution_id} must be reset or deleted"), + )); + } + if let (Some(existing), Some(requested)) = ( + existing.descriptor.retained_language.as_ref(), + operation.retained_language.as_ref(), + ) { + if existing != requested { + return Ok(typed_rejection( + request, + "execution_language_conflict", + format!( + "execution {execution_id} is retained for {existing:?}" + ), + )); + } + } + } else if operation.identity.create_if_missing != Some(true) { + return Ok(typed_rejection( + request, + "execution_not_found", + format!("execution {execution_id} does not exist"), + )); + } + if !vm.executions.contains_key(&execution_id) + && vm.executions.len() >= EXECUTION_RECORD_LIMIT + { + return Ok(typed_rejection( + request, + "execution_limit_exceeded", + format!( + "VM execution records reached the limit of {EXECUTION_RECORD_LIMIT}; delete idle executions before creating another" + ), + )); + } + execution_id + } + None => { + if operation.identity.create_if_missing.is_some() { + return Ok(typed_rejection( + request, + "invalid_execution_identity", + "createIfMissing requires an explicit executionId", + )); + } + if vm.executions.len() >= EXECUTION_RECORD_LIMIT { + return Ok(typed_rejection( + request, + "execution_limit_exceeded", + format!( + "VM execution records reached the limit of {EXECUTION_RECORD_LIMIT}; delete idle executions before creating another" + ), + )); + } + loop { + vm.next_public_execution_id = vm.next_public_execution_id.saturating_add(1); + let candidate = format!("exec-{now:x}-{:x}", vm.next_public_execution_id); + if !vm.executions.contains_key(&candidate) { + break candidate; + } + } + } + } + }; + + if let Some(vm) = self.vms.get(&vm_id) { + let next_count = vm.executions.len().saturating_add(1); + let warning_threshold = EXECUTION_RECORD_LIMIT.saturating_mul(4) / 5; + if !vm.executions.contains_key(&execution_id) && next_count == warning_threshold { + eprintln!( + "agentos VM {vm_id} retained {next_count} of {EXECUTION_RECORD_LIMIT} execution records; delete idle executions before reaching the limit" + ); + } + } + + let (process_id, generation, descriptor, reused_resident) = { + let vm = self.vms.get_mut(&vm_id).expect("owned VM checked above"); + let resident_process_id = operation + .retained_source + .as_ref() + .and_then(|_| vm.executions.get(&execution_id)) + .and_then(|execution| execution.resident_process_id.clone()) + .filter(|process_id| vm.active_processes.contains_key(process_id)); + let resident_pid = resident_process_id + .as_ref() + .and_then(|process_id| vm.active_processes.get(process_id)) + .map(|process| process.kernel_pid); + let reused_resident = resident_process_id.is_some(); + let execution = vm + .executions + .entry(execution_id.clone()) + .or_insert_with(|| ManagedLanguageExecution { + descriptor: ExecutionDescriptor { + execution_id: execution_id.clone(), + generation: 0, + state: ExecutionState::Creating, + retained_language: None, + process_id: None, + pid: None, + created_at_ms: now, + last_started_at_ms: None, + last_completed_at_ms: None, + last_outcome: None, + last_exit_code: None, + }, + result: None, + events: VecDeque::new(), + retained_event_bytes: 0, + output_truncated: false, + next_sequence: 0, + stdout: Vec::new(), + stderr: Vec::new(), + stdout_truncated: false, + stderr_truncated: false, + output_limit_bytes, + output_limit_setting, + event_limit: self.config.runtime.protocol.max_process_events.max(1), + event_bytes_limit: EXECUTION_EVENT_BYTES_LIMIT, + uses_pty: false, + value_kind: ExecutionValueKind::None, + value_marker: None, + pending_outcome: None, + deadline_ms: None, + deadline_task: None, + resident_process_id: None, + }); + if let Some(task) = execution.deadline_task.take() { + task.abort(); + } + execution.descriptor.generation = execution.descriptor.generation.saturating_add(1); + execution.descriptor.state = ExecutionState::Running; + execution.descriptor.retained_language = execution + .descriptor + .retained_language + .clone() + .or(operation.retained_language.clone()); + execution.descriptor.last_started_at_ms = Some(now); + execution.descriptor.last_completed_at_ms = None; + execution.descriptor.last_outcome = None; + execution.descriptor.last_exit_code = None; + execution.result = None; + execution.events.clear(); + execution.retained_event_bytes = 0; + execution.output_truncated = false; + execution.next_sequence = 0; + execution.stdout.clear(); + execution.stderr.clear(); + execution.stdout_truncated = false; + execution.stderr_truncated = false; + execution.output_limit_bytes = output_limit_bytes; + execution.output_limit_setting = output_limit_setting; + execution.uses_pty = operation.pty.is_some(); + execution.value_kind = operation.value_kind; + execution.value_marker = operation.value_marker.clone(); + execution.pending_outcome = None; + execution.deadline_ms = operation + .timeout_ms + .map(|timeout| now.saturating_add(timeout)); + let generation = execution.descriptor.generation; + let process_id = resident_process_id + .unwrap_or_else(|| format!("execution:{execution_id}:{generation}")); + execution.descriptor.process_id = Some(process_id.clone()); + execution.descriptor.pid = resident_pid; + if operation.retained_source.is_some() { + execution.resident_process_id = Some(process_id.clone()); + } + vm.execution_processes + .insert(process_id.clone(), execution_id.clone()); + if operation.package_mutation { + vm.package_mutation_execution_id = Some(execution_id.clone()); + } + ( + process_id, + generation, + execution.descriptor.clone(), + reused_resident, + ) + }; + + if let Some(timeout_ms) = operation.timeout_ms { + let notify = Arc::clone(&self.process_event_notify); + let runtime = self + .vms + .get(&vm_id) + .expect("owned VM checked above") + .runtime_context + .clone(); + let task = runtime + .spawn(agentos_runtime::TaskClass::Timer, async move { + tokio::time::sleep(std::time::Duration::from_millis(timeout_ms)).await; + notify.notify_one(); + }) + .map_err(|error| SidecarError::Execution(error.to_string()))?; + self.vms + .get_mut(&vm_id) + .and_then(|vm| vm.executions.get_mut(&execution_id)) + .expect("admitted execution exists") + .deadline_task = Some(task); + } + + if let Some(pty) = &operation.pty { + operation + .env + .insert(String::from(TTY_ENV), String::from("1")); + if let Some(cols) = pty.cols { + operation + .env + .insert(String::from(TTY_COLS_ENV), cols.to_string()); + } + if let Some(rows) = pty.rows { + operation + .env + .insert(String::from(TTY_ROWS_ENV), rows.to_string()); + } + } + if let Some(timeout_ms) = operation.timeout_ms { + if matches!(operation.command.as_str(), "node" | "npm" | "npx") { + operation.env.insert( + String::from("AGENTOS_V8_WALL_CLOCK_LIMIT_MS"), + timeout_ms.to_string(), + ); + } else if matches!( + operation.command.as_str(), + "python" | "python3" | "pip" | "pip3" + ) { + operation.env.insert( + String::from("AGENTOS_PYTHON_EXECUTION_TIMEOUT_MS"), + timeout_ms.to_string(), + ); + } + } + if operation.retained_source.is_some() { + operation + .env + .insert(String::from(RETAIN_LANGUAGE_CONTEXT_ENV), String::from("1")); + } + let execute_payload = ExecuteRequest { + process_id: process_id.clone(), + command: Some(operation.command), + runtime: None, + entrypoint: None, + args: operation.args, + env: operation.env.into_iter().collect(), + cwd: operation.cwd, + wasm_permission_tier: None, + }; + let launch_result = if reused_resident { + let language = operation + .retained_language + .clone() + .expect("resident operations have a retained language"); + let source = operation + .retained_source + .clone() + .expect("resident operations have retained source"); + let file_path = operation + .retained_file_path + .clone() + .unwrap_or_else(|| String::from("/[agentos-retained]")); + let vm = self.vms.get_mut(&vm_id).expect("owned VM checked above"); + let process = vm.active_processes.get_mut(&process_id).ok_or_else(|| { + SidecarError::InvalidState(format!( + "resident process {process_id} disappeared before execution" + )) + })?; + process + .execution + .execute_retained_language(language, source, file_path, operation.retained_module) + .map(|()| None) + } else { + self.execute(request, execute_payload).await.map(Some) + }; + let launch = match launch_result { + Ok(result) => result, + Err(error) => { + if reused_resident { + let _ = self.finish_active_process_exit(&vm_id, &process_id, 1); + } + if let Some(vm) = self.vms.get_mut(&vm_id) { + vm.execution_processes.remove(&process_id); + if vm.package_mutation_execution_id.as_deref() == Some(&execution_id) { + vm.package_mutation_execution_id = None; + } + if let Some(execution) = vm.executions.get_mut(&execution_id) { + if let Some(task) = execution.deadline_task.take() { + task.abort(); + } + execution.resident_process_id = None; + execution.descriptor.state = ExecutionState::Failed; + execution.descriptor.process_id = None; + execution.descriptor.pid = None; + execution.descriptor.last_completed_at_ms = Some(now_ms()); + execution.descriptor.last_outcome = Some(ExecutionOutcome::Failed); + execution.result = Some(failed_result( + execution.descriptor.clone(), + "execution_start_failed", + error.to_string(), + )); + } + } + let result = self + .vms + .get(&vm_id) + .and_then(|vm| vm.executions.get(&execution_id)) + .and_then(|execution| execution.result.clone()) + .expect("admitted start failure stores a result"); + return Ok(DispatchResult { + response: self.respond( + request, + ResponsePayload::ExecutionAccepted(ExecutionAcceptedResponse { + execution: result.execution.clone(), + }), + ), + events: vec![EventFrame::new( + request.ownership.clone(), + EventPayload::ExecutionCompleted(ExecutionCompletedEvent { + execution_id, + generation, + outcome: ExecutionOutcome::Failed, + exit_code: None, + error: result.error, + }), + )], + }); + } + }; + + if let Some(launch) = &launch { + if let ResponsePayload::ProcessStarted(started) = &launch.response.payload { + if let Some(execution) = self + .vms + .get_mut(&vm_id) + .and_then(|vm| vm.executions.get_mut(&execution_id)) + { + execution.descriptor.pid = started.pid; + } + } + } + + if let Some(stdin) = operation.stdin { + self.write_stdin( + request, + WriteStdinRequest { + process_id: process_id.clone(), + chunk: stdin, + }, + ) + .await?; + } + + let descriptor = self + .vms + .get(&vm_id) + .and_then(|vm| vm.executions.get(&execution_id)) + .map(|execution| execution.descriptor.clone()) + .unwrap_or(descriptor); + debug_assert_eq!(descriptor.generation, generation); + Ok(DispatchResult { + response: self.respond( + request, + ResponsePayload::ExecutionAccepted(ExecutionAcceptedResponse { + execution: descriptor, + }), + ), + events: launch.map_or_else(Vec::new, |launch| launch.events), + }) + } + + pub(crate) async fn handle_execution_lifecycle( + &mut self, + request: &RequestFrame, + payload: RequestPayload, + ) -> Result { + let (connection_id, session_id, vm_id) = self.vm_scope_for(&request.ownership)?; + self.require_owned_vm(&connection_id, &session_id, &vm_id)?; + + let response = match payload { + RequestPayload::GetExecution(payload) => { + let Some(execution) = self + .vms + .get(&vm_id) + .and_then(|vm| vm.executions.get(&payload.execution_id)) + else { + return Ok(typed_rejection( + request, + "execution_not_found", + format!("execution {} does not exist", payload.execution_id), + )); + }; + ResponsePayload::ExecutionDescriptor(ExecutionDescriptorResponse { + execution: execution.descriptor.clone(), + }) + } + RequestPayload::ListExecutions(_) => { + let executions = self + .vms + .get(&vm_id) + .map(|vm| { + vm.executions + .values() + .map(|item| item.descriptor.clone()) + .collect() + }) + .unwrap_or_default(); + ResponsePayload::ExecutionList(ExecutionListResponse { executions }) + } + RequestPayload::WaitExecution(payload) => { + let Some(execution) = self + .vms + .get(&vm_id) + .and_then(|vm| vm.executions.get(&payload.execution_id)) + else { + return Ok(typed_rejection( + request, + "execution_not_found", + format!("execution {} does not exist", payload.execution_id), + )); + }; + if execution.descriptor.state == ExecutionState::Running { + return Ok(typed_rejection( + request, + "execution_busy", + format!("execution {} is still running", payload.execution_id), + )); + } + let Some(result) = execution.result.clone() else { + return Ok(typed_rejection( + request, + "execution_result_not_found", + format!( + "execution {} has no completed operation", + payload.execution_id + ), + )); + }; + ResponsePayload::ExecutionCompleted(result) + } + RequestPayload::CancelExecution(payload) => { + let process_id = match active_process_id(self, &vm_id, &payload.execution_id) { + Ok(process_id) => process_id, + Err((code, message)) => return Ok(typed_rejection(request, code, message)), + }; + if let Some(execution) = self + .vms + .get_mut(&vm_id) + .and_then(|vm| vm.executions.get_mut(&payload.execution_id)) + { + execution.pending_outcome = Some(ExecutionOutcome::Cancelled); + execution.deadline_ms = + Some(now_ms().saturating_add(EXECUTION_CANCEL_GRACE_MS)); + if let Some(task) = execution.deadline_task.take() { + task.abort(); + } + } + self.kill_process_internal(&vm_id, &process_id, "SIGTERM")?; + let notify = Arc::clone(&self.process_event_notify); + let runtime = self + .vms + .get(&vm_id) + .expect("execution VM exists") + .runtime_context + .clone(); + let task = runtime + .spawn(agentos_runtime::TaskClass::Timer, async move { + tokio::time::sleep(std::time::Duration::from_millis( + EXECUTION_CANCEL_GRACE_MS, + )) + .await; + notify.notify_one(); + }) + .map_err(|error| SidecarError::Execution(error.to_string()))?; + self.vms + .get_mut(&vm_id) + .and_then(|vm| vm.executions.get_mut(&payload.execution_id)) + .expect("execution checked above") + .deadline_task = Some(task); + let descriptor = self + .vms + .get(&vm_id) + .and_then(|vm| vm.executions.get(&payload.execution_id)) + .expect("execution checked above") + .descriptor + .clone(); + ResponsePayload::ExecutionDescriptor(ExecutionDescriptorResponse { + execution: descriptor, + }) + } + RequestPayload::SignalExecution(payload) => { + let process_id = match active_process_id(self, &vm_id, &payload.execution_id) { + Ok(process_id) => process_id, + Err((code, message)) => return Ok(typed_rejection(request, code, message)), + }; + self.kill_process_internal(&vm_id, &process_id, &payload.signal)?; + let descriptor = self + .vms + .get(&vm_id) + .and_then(|vm| vm.executions.get(&payload.execution_id)) + .expect("execution checked above") + .descriptor + .clone(); + ResponsePayload::ExecutionDescriptor(ExecutionDescriptorResponse { + execution: descriptor, + }) + } + RequestPayload::ResetExecution(payload) => { + let Some(existing) = self + .vms + .get(&vm_id) + .and_then(|vm| vm.executions.get(&payload.execution_id)) + else { + return Ok(typed_rejection( + request, + "execution_not_found", + format!("execution {} does not exist", payload.execution_id), + )); + }; + if existing.descriptor.state == ExecutionState::Running { + return Ok(typed_rejection( + request, + "execution_busy", + format!("execution {} is running", payload.execution_id), + )); + } + let resident_process_id = existing.resident_process_id.clone(); + if let Some(process_id) = resident_process_id { + self.finish_active_process_exit(&vm_id, &process_id, 0)?; + if let Some(vm) = self.vms.get_mut(&vm_id) { + vm.execution_processes.remove(&process_id); + } + } + let execution = self + .vms + .get_mut(&vm_id) + .and_then(|vm| vm.executions.get_mut(&payload.execution_id)) + .expect("execution checked above"); + execution.descriptor.state = ExecutionState::Resetting; + execution.descriptor.generation = execution.descriptor.generation.saturating_add(1); + execution.descriptor.retained_language = None; + execution.descriptor.process_id = None; + execution.descriptor.pid = None; + execution.descriptor.last_started_at_ms = None; + execution.descriptor.last_completed_at_ms = None; + execution.descriptor.last_outcome = None; + execution.descriptor.last_exit_code = None; + execution.result = None; + execution.events.clear(); + execution.retained_event_bytes = 0; + execution.stdout.clear(); + execution.stderr.clear(); + execution.value_marker = None; + execution.value_kind = ExecutionValueKind::None; + execution.deadline_ms = None; + if let Some(task) = execution.deadline_task.take() { + task.abort(); + } + execution.resident_process_id = None; + execution.descriptor.state = ExecutionState::Idle; + ResponsePayload::ExecutionDescriptor(ExecutionDescriptorResponse { + execution: execution.descriptor.clone(), + }) + } + RequestPayload::DeleteExecution(payload) => { + let Some(execution) = self + .vms + .get(&vm_id) + .and_then(|vm| vm.executions.get(&payload.execution_id)) + else { + return Ok(typed_rejection( + request, + "execution_not_found", + format!("execution {} does not exist", payload.execution_id), + )); + }; + if execution.descriptor.state == ExecutionState::Running { + return Ok(typed_rejection( + request, + "execution_busy", + format!("execution {} is running", payload.execution_id), + )); + } + let resident_process_id = execution.resident_process_id.clone(); + if let Some(process_id) = resident_process_id { + self.finish_active_process_exit(&vm_id, &process_id, 0)?; + if let Some(vm) = self.vms.get_mut(&vm_id) { + vm.execution_processes.remove(&process_id); + } + } + self.vms + .get_mut(&vm_id) + .expect("owned VM checked above") + .executions + .remove(&payload.execution_id); + ResponsePayload::ExecutionDeleted(ExecutionDeletedResponse { + execution_id: payload.execution_id, + }) + } + RequestPayload::WriteExecutionStdin(payload) => { + let process_id = match active_process_id(self, &vm_id, &payload.execution_id) { + Ok(process_id) => process_id, + Err((code, message)) => return Ok(typed_rejection(request, code, message)), + }; + let accepted = payload.chunk.len() as u64; + self.write_stdin( + request, + WriteStdinRequest { + process_id, + chunk: payload.chunk, + }, + ) + .await?; + ResponsePayload::ExecutionIo(ExecutionIoResponse { + execution_id: payload.execution_id, + accepted_bytes: Some(accepted), + }) + } + RequestPayload::CloseExecutionStdin(payload) => { + let process_id = match active_process_id(self, &vm_id, &payload.execution_id) { + Ok(process_id) => process_id, + Err((code, message)) => return Ok(typed_rejection(request, code, message)), + }; + self.close_stdin(request, CloseStdinRequest { process_id }) + .await?; + ResponsePayload::ExecutionIo(ExecutionIoResponse { + execution_id: payload.execution_id, + accepted_bytes: None, + }) + } + RequestPayload::ResizeExecutionPty(payload) => { + let process_id = match active_process_id(self, &vm_id, &payload.execution_id) { + Ok(process_id) => process_id, + Err((code, message)) => return Ok(typed_rejection(request, code, message)), + }; + self.resize_pty( + request, + ResizePtyRequest { + process_id, + cols: payload.cols, + rows: payload.rows, + }, + ) + .await?; + ResponsePayload::ExecutionIo(ExecutionIoResponse { + execution_id: payload.execution_id, + accepted_bytes: None, + }) + } + RequestPayload::ReadExecutionOutput(payload) => { + let Some(execution) = self + .vms + .get(&vm_id) + .and_then(|vm| vm.executions.get(&payload.execution_id)) + else { + return Ok(typed_rejection( + request, + "execution_not_found", + format!("execution {} does not exist", payload.execution_id), + )); + }; + let start = match payload.cursor.as_deref() { + None => 0, + Some(cursor) => { + let Some(start) = parse_cursor(cursor, execution.descriptor.generation) + else { + return Ok(typed_rejection( + request, + "execution_output_cursor_expired", + "the output cursor belongs to an earlier execution generation", + )); + }; + start + } + }; + let limit = payload + .limit + .unwrap_or(DEFAULT_EXECUTION_OUTPUT_PAGE_EVENTS) + .clamp(1, MAX_EXECUTION_OUTPUT_PAGE_EVENTS) + as usize; + let events: Vec<_> = execution + .events + .iter() + .filter(|event| event.sequence >= start) + .take(limit) + .cloned() + .collect(); + let next_sequence = events + .last() + .map_or(start, |event| event.sequence.saturating_add(1)); + let has_more = execution + .events + .iter() + .any(|event| event.sequence >= next_sequence); + ResponsePayload::ExecutionOutputPage(ExecutionOutputPageResponse { + execution_id: payload.execution_id, + generation: execution.descriptor.generation, + events, + next_cursor: format!("{}:{next_sequence}", execution.descriptor.generation), + has_more, + truncated: execution.output_truncated, + }) + } + _ => { + return Err(SidecarError::InvalidState(String::from( + "request is not an execution lifecycle operation", + ))) + } + }; + Ok(DispatchResult { + response: self.respond(request, response), + events: Vec::new(), + }) + } + + pub(crate) fn is_public_execution_process(&self, vm_id: &str, process_id: &str) -> bool { + self.vms + .get(vm_id) + .is_some_and(|vm| vm.execution_processes.contains_key(process_id)) + } + + pub(crate) fn should_park_public_execution_process( + &self, + vm_id: &str, + process_id: &str, + ) -> bool { + self.vms + .get(vm_id) + .and_then(|vm| { + let execution_id = vm.execution_processes.get(process_id)?; + vm.executions.get(execution_id) + }) + .is_some_and(|execution| { + execution.resident_process_id.as_deref() == Some(process_id) + && execution.pending_outcome.is_none() + && !execution + .deadline_ms + .is_some_and(|deadline| now_ms() >= deadline) + }) + } + + pub(crate) fn has_running_nonresident_processes(&self, vm_id: &str) -> bool { + let Some(vm) = self.vms.get(vm_id) else { + return false; + }; + vm.active_processes.keys().any(|process_id| { + !vm.executions + .values() + .any(|execution| execution.resident_process_id.as_deref() == Some(process_id)) + }) + } + + pub(crate) fn expire_public_execution_deadlines(&mut self) -> Result<(), SidecarError> { + let now = now_ms(); + let due = self + .vms + .iter() + .flat_map(|(vm_id, vm)| { + vm.executions.iter().filter_map(move |(_, execution)| { + (execution.descriptor.state == ExecutionState::Running + && execution + .deadline_ms + .is_some_and(|deadline| now >= deadline)) + .then(|| { + execution + .descriptor + .process_id + .as_ref() + .map(|process_id| (vm_id.clone(), process_id.clone())) + }) + .flatten() + }) + }) + .collect::>(); + for (vm_id, process_id) in due { + if let Some(execution_id) = self + .vms + .get(&vm_id) + .and_then(|vm| vm.execution_processes.get(&process_id)) + .cloned() + { + if let Some(execution) = self + .vms + .get_mut(&vm_id) + .and_then(|vm| vm.executions.get_mut(&execution_id)) + { + if execution.pending_outcome != Some(ExecutionOutcome::Cancelled) { + execution.pending_outcome = Some(ExecutionOutcome::TimedOut); + } + execution.deadline_ms = None; + } + // A deadline is already terminal. Force the process tree so a + // CPU-bound guest cannot defer timeout handling indefinitely. + self.kill_process_internal(&vm_id, &process_id, "SIGKILL")?; + } + } + Ok(()) + } + + pub(crate) fn record_public_execution_output( + &mut self, + vm_id: &str, + process_id: &str, + channel: ExecutionStreamChannel, + chunk: Vec, + ) -> Option { + let vm = self.vms.get_mut(vm_id)?; + let execution_id = vm.execution_processes.get(process_id)?.clone(); + let execution = vm.executions.get_mut(&execution_id)?; + if vm.package_mutation_execution_id.as_deref() == Some(&execution_id) { + vm.package_mutation_execution_id = None; + } + let channel = if execution.uses_pty { + ExecutionStreamChannel::Pty + } else { + channel + }; + let target = if matches!(channel, ExecutionStreamChannel::Stderr) { + &mut execution.stderr + } else { + &mut execution.stdout + }; + let previous_len = target.len(); + let available = execution.output_limit_bytes.saturating_sub(target.len()); + let retained_len = chunk.len().min(available); + target.extend_from_slice(&chunk[..retained_len]); + let warning_threshold = execution.output_limit_bytes.saturating_mul(4) / 5; + if previous_len < warning_threshold && target.len() >= warning_threshold { + eprintln!( + "agentos execution {} {:?} output reached {} of {} bytes; raise {} for more retained output", + execution.descriptor.execution_id, + channel, + target.len(), + execution.output_limit_bytes, + execution.output_limit_setting, + ); + } + if retained_len < chunk.len() { + if matches!(channel, ExecutionStreamChannel::Stderr) { + execution.stderr_truncated = true; + } else { + execution.stdout_truncated = true; + } + } + + let event = ExecutionOutputEvent { + execution_id, + generation: execution.descriptor.generation, + process_id: Some(process_id.to_owned()), + sequence: execution.next_sequence, + channel, + chunk, + timestamp_ms: now_ms(), + }; + execution.next_sequence = execution.next_sequence.saturating_add(1); + let event_bytes = event.chunk.len(); + while execution.events.len() >= execution.event_limit + || execution.retained_event_bytes.saturating_add(event_bytes) + > execution.event_bytes_limit + { + let Some(expired) = execution.events.pop_front() else { + break; + }; + execution.retained_event_bytes = execution + .retained_event_bytes + .saturating_sub(expired.chunk.len()); + execution.output_truncated = true; + } + if event_bytes <= execution.event_bytes_limit { + execution.retained_event_bytes = + execution.retained_event_bytes.saturating_add(event_bytes); + execution.events.push_back(event.clone()); + } else { + execution.output_truncated = true; + } + Some(EventPayload::ExecutionOutput(event)) + } + + pub(crate) fn complete_public_execution( + &mut self, + vm_id: &str, + process_id: &str, + exit_code: i32, + ) -> Option { + let vm = self.vms.get_mut(vm_id)?; + let execution_id = vm.execution_processes.get(process_id)?.clone(); + let resident_process_id = vm + .executions + .get(&execution_id) + .and_then(|execution| execution.resident_process_id.clone()) + .filter(|resident_id| vm.active_processes.contains_key(resident_id)); + let completing_resident = resident_process_id.as_deref() == Some(process_id); + if !completing_resident { + vm.execution_processes.remove(process_id); + } + let execution = vm.executions.get_mut(&execution_id)?; + if execution.resident_process_id.is_some() && resident_process_id.is_none() { + execution.resident_process_id = None; + } + let deadline_expired = execution + .deadline_ms + .take() + .is_some_and(|deadline| now_ms() >= deadline); + if let Some(task) = execution.deadline_task.take() { + task.abort(); + } + let mut outcome = execution.pending_outcome.take().unwrap_or_else(|| { + if deadline_expired { + return ExecutionOutcome::TimedOut; + } + if exit_code == 0 { + ExecutionOutcome::Succeeded + } else { + ExecutionOutcome::Failed + } + }); + let (outputs, evaluation_error) = if outcome == ExecutionOutcome::Succeeded { + match extract_evaluation_output(execution) { + Ok(outputs) => (outputs, None), + Err(message) => { + outcome = ExecutionOutcome::Failed; + (String::from("[]"), Some(message)) + } + } + } else { + execution.value_marker = None; + (String::from("[]"), None) + }; + + execution.descriptor.state = ExecutionState::Idle; + execution.descriptor.process_id = None; + execution.descriptor.pid = None; + execution.descriptor.last_completed_at_ms = Some(now_ms()); + execution.descriptor.last_outcome = Some(outcome.clone()); + execution.descriptor.last_exit_code = Some(exit_code); + + let error = if let Some(message) = evaluation_error { + Some(ExecutionErrorData { + code: String::from("evaluation_serialization_failed"), + name: String::from("ExecutionEvaluationError"), + message, + stack: None, + details: None, + }) + } else if outcome == ExecutionOutcome::Succeeded { + None + } else { + Some(ExecutionErrorData { + code: match outcome { + ExecutionOutcome::Cancelled => String::from("execution_cancelled"), + ExecutionOutcome::TimedOut => String::from("execution_timed_out"), + ExecutionOutcome::Failed | ExecutionOutcome::Succeeded => { + String::from("execution_failed") + } + }, + name: String::from("ExecutionError"), + message: match outcome { + ExecutionOutcome::Cancelled => String::from("execution was cancelled"), + ExecutionOutcome::TimedOut => String::from("execution timed out"), + ExecutionOutcome::Failed | ExecutionOutcome::Succeeded => { + format!("execution exited with code {exit_code}") + } + }, + stack: None, + details: None, + }) + }; + execution.result = Some(ExecutionCompletedResponse { + execution: execution.descriptor.clone(), + outcome: outcome.clone(), + exit_code: Some(exit_code), + error: error.clone(), + stdout: execution.stdout.clone(), + stderr: execution.stderr.clone(), + stdout_truncated: execution.stdout_truncated, + stderr_truncated: execution.stderr_truncated, + outputs, + }); + Some(EventPayload::ExecutionCompleted(ExecutionCompletedEvent { + execution_id, + generation: execution.descriptor.generation, + outcome, + exit_code: Some(exit_code), + error, + })) + } +} + +fn extract_evaluation_output(execution: &mut ManagedLanguageExecution) -> Result { + let Some(marker) = execution.value_marker.take() else { + return Ok(String::from("[]")); + }; + let stdout = String::from_utf8_lossy(&execution.stdout); + let Some(start) = stdout.rfind(&marker) else { + return Err(format!( + "evaluation produced no complete JSON result; the value must be JSON-serializable and fit within the {}-byte output limit (raise {})", + execution.output_limit_bytes, execution.output_limit_setting + )); + }; + let value_start = start.saturating_add(marker.len()); + let value_end = stdout[value_start..] + .find('\n') + .map_or(stdout.len(), |offset| value_start.saturating_add(offset)); + let value = stdout[value_start..value_end].to_owned(); + let mut clean = stdout.as_bytes().to_vec(); + let remove_end = if value_end < clean.len() { + value_end.saturating_add(1) + } else { + value_end + }; + clean.drain(start..remove_end); + execution.stdout = clean; + let value = serde_json::from_str::(&value).map_err(|error| { + format!( + "evaluation result must be JSON-serializable: {error}; raise {} if the retained result was truncated", + execution.output_limit_setting + ) + })?; + let value = match execution.value_kind { + ExecutionValueKind::JavaScript | ExecutionValueKind::Python => { + let object = value.as_object().ok_or_else(|| { + String::from("evaluation returned an invalid internal result envelope") + })?; + if object.get("__agentosEvaluation") != Some(&serde_json::Value::Bool(true)) { + return Err(String::from( + "evaluation returned an invalid internal result envelope", + )); + } + if object.get("ok") == Some(&serde_json::Value::Bool(false)) { + return Err(object + .get("error") + .and_then(serde_json::Value::as_str) + .unwrap_or("evaluation result must be JSON-serializable") + .to_owned()); + } + object.get("value").cloned().ok_or_else(|| { + String::from( + "AgentOS evaluation result must be JSON-serializable; undefined, functions, and symbols are not supported", + ) + })? + } + ExecutionValueKind::TypeScriptCheck | ExecutionValueKind::None => value, + }; + serde_json::to_string(&serde_json::json!([{ "type": "json", "data": value }])) + .map_err(|error| format!("failed to serialize evaluation display output: {error}")) +} + +fn active_process_id( + sidecar: &NativeSidecar, + vm_id: &str, + execution_id: &str, +) -> Result { + let Some(execution) = sidecar + .vms + .get(vm_id) + .and_then(|vm| vm.executions.get(execution_id)) + else { + return Err(( + "execution_not_found", + format!("execution {execution_id} does not exist"), + )); + }; + if execution.descriptor.state != ExecutionState::Running { + return Err(( + "execution_not_running", + format!("execution {execution_id} is not running"), + )); + } + execution.descriptor.process_id.clone().ok_or_else(|| { + ( + "execution_not_running", + format!("execution {execution_id} has no active process"), + ) + }) +} + +fn parse_cursor(cursor: &str, generation: u64) -> Option { + let (cursor_generation, sequence) = cursor.split_once(':')?; + (cursor_generation.parse::().ok()? == generation) + .then(|| sequence.parse::().ok()) + .flatten() +} + +fn failed_result( + execution: ExecutionDescriptor, + code: impl Into, + message: impl Into, +) -> ExecutionCompletedResponse { + ExecutionCompletedResponse { + execution, + outcome: ExecutionOutcome::Failed, + exit_code: None, + error: Some(ExecutionErrorData { + code: code.into(), + name: String::from("ExecutionError"), + message: message.into(), + stack: None, + details: None, + }), + stdout: Vec::new(), + stderr: Vec::new(), + stdout_truncated: false, + stderr_truncated: false, + outputs: String::from("[]"), + } +} diff --git a/crates/native-sidecar/src/lib.rs b/crates/native-sidecar/src/lib.rs index 2ed84f54dd..a6607fb770 100644 --- a/crates/native-sidecar/src/lib.rs +++ b/crates/native-sidecar/src/lib.rs @@ -12,6 +12,7 @@ pub mod extension; pub(crate) mod filesystem; #[allow(dead_code)] pub(crate) mod json_rpc; +pub(crate) mod language_execution; pub mod limits; pub(crate) mod metadata; pub mod package_projection; diff --git a/crates/native-sidecar/src/plugins/google_drive.rs b/crates/native-sidecar/src/plugins/google_drive.rs index a72afc8a6b..c12249cb70 100644 --- a/crates/native-sidecar/src/plugins/google_drive.rs +++ b/crates/native-sidecar/src/plugins/google_drive.rs @@ -19,7 +19,7 @@ use url::Url; const DEFAULT_CHUNK_SIZE: usize = 4 * 1024 * 1024; const DEFAULT_INLINE_THRESHOLD: usize = 64 * 1024; -const MANIFEST_FORMAT: &str = "secure_exec_google_drive_filesystem_manifest_v1"; +const MANIFEST_FORMAT: &str = "agentos_google_drive_filesystem_manifest_v1"; const LEGACY_AGENTOS_MANIFEST_FORMAT: &str = "agentos_google_drive_filesystem_manifest_v1"; const DRIVE_SCOPE: &str = "https://www.googleapis.com/auth/drive.file"; const DEFAULT_TOKEN_URL: &str = "https://oauth2.googleapis.com/token"; diff --git a/crates/native-sidecar/src/service.rs b/crates/native-sidecar/src/service.rs index 305f57046b..c76b55eb55 100644 --- a/crates/native-sidecar/src/service.rs +++ b/crates/native-sidecar/src/service.rs @@ -1540,6 +1540,12 @@ where self.snapshot_root_filesystem(&request, payload).await } RequestRoute::ListMounts(payload) => self.list_mounts(&request, payload).await, + RequestRoute::ExecutionOperation(payload) => { + self.execute_language_operation(&request, payload).await + } + RequestRoute::ExecutionLifecycle(payload) => { + self.handle_execution_lifecycle(&request, payload).await + } RequestRoute::Execute(payload) => self.execute(&request, payload).await, RequestRoute::WriteStdin(payload) => self.write_stdin(&request, payload).await, RequestRoute::ResizePty(payload) => self.resize_pty(&request, payload).await, @@ -3162,7 +3168,12 @@ where shared_respond(request, payload) } - fn reject(&self, request: &RequestFrame, code: &str, message: &str) -> ResponseFrame { + pub(crate) fn reject( + &self, + request: &RequestFrame, + code: &str, + message: &str, + ) -> ResponseFrame { shared_reject(request, code, message) } @@ -4007,7 +4018,7 @@ pub(crate) fn vfs_error(error: VfsError) -> SidecarError { /// required. The empirically-supported package managers are captured in /// `crates/sidecar/tests/module_layout_e2e.rs`. #[allow(dead_code)] -const HOISTED_NODE_MODULES_GUIDANCE: &str = "secure-exec can't load mounted node_modules: the directory uses a non-flat layout (pnpm / bun / yarn workspaces store, or yarn Plug'n'Play) whose package store isn't visible inside the VM. A flat (hoisted) node_modules is required.\n - pnpm -> add `node-linker=hoisted` to .npmrc, then reinstall\n - yarn berry -> set `nodeLinker: node-modules` in .yarnrc.yml (not pnp/pnpm)\n - bun -> install dependencies outside a workspace (workspaces use a .bun store)\n - npm / yarn classic -> already flat, no change needed"; +const HOISTED_NODE_MODULES_GUIDANCE: &str = "agentos can't load mounted node_modules: the directory uses a non-flat layout (pnpm / bun / yarn workspaces store, or yarn Plug'n'Play) whose package store isn't visible inside the VM. A flat (hoisted) node_modules is required.\n - pnpm -> add `node-linker=hoisted` to .npmrc, then reinstall\n - yarn berry -> set `nodeLinker: node-modules` in .yarnrc.yml (not pnp/pnpm)\n - bun -> install dependencies outside a workspace (workspaces use a .bun store)\n - npm / yarn classic -> already flat, no change needed"; /// Detect, from an adapter's captured stderr, a non-flat-`node_modules` failure /// signature. Returns the actionable guidance to fold into the surfaced error, @@ -4178,7 +4189,7 @@ mod symlinked_node_modules_hint_tests { // dist/package.json inside the unreachable .pnpm store. let stderr = "Error: ENOENT: no such file or directory, open '/root/node_modules/.pnpm/@mariozechner+pi-coding-agent@0.60.0_x/node_modules/@mariozechner/pi-coding-agent/dist/package.json'"; let hint = symlinked_node_modules_hint(stderr).expect("expected hoisted guidance"); - assert!(hint.contains("secure-exec can't load mounted node_modules")); + assert!(hint.contains("agentos can't load mounted node_modules")); assert!(!hint.contains("agentos")); } diff --git a/crates/native-sidecar/src/state.rs b/crates/native-sidecar/src/state.rs index 41ce39cac2..d09b39e581 100644 --- a/crates/native-sidecar/src/state.rs +++ b/crates/native-sidecar/src/state.rs @@ -4,9 +4,10 @@ //! types, and other shared data structures extracted from service.rs. use crate::protocol::{ - GuestRuntimeKind, MountDescriptor, ProjectedModuleDescriptor, RegisterHostCallbacksRequest, - SidecarRequestFrame, SidecarRequestPayload, SidecarResponseFrame, SidecarResponsePayload, - SignalHandlerRegistration, SoftwareDescriptor, WasmPermissionTier, + ExecutionCompletedResponse, ExecutionDescriptor, ExecutionOutputEvent, GuestRuntimeKind, + MountDescriptor, ProjectedModuleDescriptor, RegisterHostCallbacksRequest, SidecarRequestFrame, + SidecarRequestPayload, SidecarResponseFrame, SidecarResponsePayload, SignalHandlerRegistration, + SoftwareDescriptor, WasmPermissionTier, }; use crate::wire::DEFAULT_MAX_FRAME_BYTES; use agentos_bridge::{ @@ -528,7 +529,7 @@ pub(crate) const VM_LISTEN_PORT_MAX_METADATA_KEY: &str = "network.listen.port_ma pub(crate) const VM_LISTEN_ALLOW_PRIVILEGED_METADATA_KEY: &str = "network.listen.allow_privileged"; pub(crate) const DEFAULT_JAVASCRIPT_NET_BACKLOG: u32 = 511; pub(crate) const LOOPBACK_EXEMPT_PORTS_ENV: &str = "AGENTOS_LOOPBACK_EXEMPT_PORTS"; -pub(crate) const BINDING_DRIVER_NAME: &str = "secure-exec-host-callbacks"; +pub(crate) const BINDING_DRIVER_NAME: &str = "agentos-host-callbacks"; pub(crate) const MAPPED_HOST_FD_START: u32 = 1_000_000_000; // --------------------------------------------------------------------------- @@ -870,6 +871,15 @@ pub(crate) struct VmState { pub(crate) command_permissions: BTreeMap, pub(crate) bindings: BTreeMap, pub(crate) active_processes: BTreeMap, + /// Public language/process executions keyed by their sole lifecycle ID. + /// Process IDs remain internal routing details in `execution_processes`. + pub(crate) executions: BTreeMap, + pub(crate) execution_processes: BTreeMap, + pub(crate) next_public_execution_id: u64, + /// The VM filesystem is shared across executions, so package-manager + /// mutations must never run concurrently. + pub(crate) package_mutation_execution_id: Option, + pub(crate) typescript_compiler_staged: bool, pub(crate) exited_process_snapshots: VecDeque, pub(crate) detached_child_processes: BTreeSet, /// Rotating start positions for bounded child-process event turns. Durable @@ -2554,6 +2564,41 @@ pub(crate) struct ActiveUdpSocket { // Execution types // --------------------------------------------------------------------------- +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ExecutionValueKind { + None, + JavaScript, + Python, + TypeScriptCheck, +} + +#[derive(Debug)] +pub(crate) struct ManagedLanguageExecution { + pub(crate) descriptor: ExecutionDescriptor, + pub(crate) result: Option, + pub(crate) events: VecDeque, + pub(crate) retained_event_bytes: usize, + pub(crate) output_truncated: bool, + pub(crate) next_sequence: u64, + pub(crate) stdout: Vec, + pub(crate) stderr: Vec, + pub(crate) stdout_truncated: bool, + pub(crate) stderr_truncated: bool, + pub(crate) output_limit_bytes: usize, + pub(crate) output_limit_setting: &'static str, + pub(crate) event_limit: usize, + pub(crate) event_bytes_limit: usize, + pub(crate) uses_pty: bool, + pub(crate) value_kind: ExecutionValueKind, + pub(crate) value_marker: Option, + pub(crate) pending_outcome: Option, + pub(crate) deadline_ms: Option, + pub(crate) deadline_task: Option>, + /// Internal process whose V8/Pyodide context is parked while the public + /// execution is idle. It is never exposed as a second lifecycle identity. + pub(crate) resident_process_id: Option, +} + #[derive(Debug)] #[allow(clippy::large_enum_variant)] // execution state is process-registry owned and preserves backend drop affinity pub(crate) enum ActiveExecution { diff --git a/crates/native-sidecar/src/stdio.rs b/crates/native-sidecar/src/stdio.rs index ae94506c71..913b2eb022 100644 --- a/crates/native-sidecar/src/stdio.rs +++ b/crates/native-sidecar/src/stdio.rs @@ -1878,7 +1878,7 @@ mod tests { use crate::{ExtensionContext, ExtensionFuture, ExtensionInterruptResponse, ExtensionResponse}; use std::io::Cursor; - const TEST_EXTENSION_NAMESPACE: &str = "dev.rivet.secure-exec.test.blocking"; + const TEST_EXTENSION_NAMESPACE: &str = "dev.rivet.agentos.test.blocking"; fn test_protocol_budget( max_frames: usize, diff --git a/crates/native-sidecar/src/vm.rs b/crates/native-sidecar/src/vm.rs index 67cf2dcac7..3015c066a5 100644 --- a/crates/native-sidecar/src/vm.rs +++ b/crates/native-sidecar/src/vm.rs @@ -546,6 +546,11 @@ where command_permissions: BTreeMap::new(), bindings: BTreeMap::new(), active_processes: BTreeMap::new(), + executions: BTreeMap::new(), + execution_processes: BTreeMap::new(), + next_public_execution_id: 0, + package_mutation_execution_id: None, + typescript_compiler_staged: false, exited_process_snapshots: VecDeque::new(), detached_child_processes: BTreeSet::new(), attached_child_event_cursor: 0, @@ -680,7 +685,7 @@ where vm.command_guest_paths = discover_command_guest_paths(&mut vm.kernel); // The `{ packageDir }` projection lands each package's `bin/` at // `/opt/agentos/bin/` (on `$PATH`) but does NOT populate - // `/__secure_exec/commands`, so `discover_command_guest_paths` alone misses + // `/__agentos/commands`, so `discover_command_guest_paths` alone misses // projected commands and every projected wasm/js command resolves to // ENOEXEC (absolute path) / ENOENT (bare name). Register each projected // command by name -> its `/opt/agentos/bin/` entrypoint so both the @@ -3670,9 +3675,8 @@ mod tests { .expect("clock should be monotonic") .as_nanos(); let database_path = - std::env::temp_dir().join(format!("secure-exec-native-root-{unique}.sqlite")); - let block_root = - std::env::temp_dir().join(format!("secure-exec-native-root-blocks-{unique}")); + std::env::temp_dir().join(format!("agentos-native-root-{unique}.sqlite")); + let block_root = std::env::temp_dir().join(format!("agentos-native-root-blocks-{unique}")); let native_root = native_root_plugin_from_config(Some(&agentos_vm_config::NativeRootFilesystemConfig { plugin: agentos_vm_config::MountPluginDescriptor { diff --git a/crates/native-sidecar/tests/architecture_guards.rs b/crates/native-sidecar/tests/architecture_guards.rs index 09039f098d..560e26e393 100644 --- a/crates/native-sidecar/tests/architecture_guards.rs +++ b/crates/native-sidecar/tests/architecture_guards.rs @@ -20,7 +20,7 @@ //! (`sidecar::execution`), the embedded V8 runtime IPC pair, and //! host-backed storage plugins. //! * process -- `std::process::Command` / `tokio::process` / OS `fork`. -//! Sanctioned only where secure-exec spawns its own helper process (the +//! Sanctioned only where agentos spawns its own helper process (the //! client transport that launches the sidecar). Guest "process" spawns are //! dispatched through the kernel `CommandDriver` registry and never touch //! `Command::new`. @@ -394,12 +394,12 @@ const NET_ALLOW: &[&str] = &[ /// process: OS subprocess creation. /// -/// Sanctioned surface: only the client transport, which spawns secure-exec's +/// Sanctioned surface: only the client transport, which spawns agentos's /// own sidecar helper binary. Guest "process" spawns go through the kernel /// `CommandDriver` registry and never reach `Command::new`. const PROCESS_ALLOW: &[&str] = &[ "crates/sidecar-client/src/transport.rs", - // V8 snapshot builder re-execs secure-exec's OWN binary as a helper + // V8 snapshot builder re-execs agentos's OWN binary as a helper // (SNAPSHOT_HELPER_ENV) so snapshot creation runs in a clean process. // Host-side bootstrap only; no guest-controlled input picks the program. "crates/v8-runtime/src/snapshot.rs", @@ -1449,7 +1449,7 @@ fn browser_sources_are_retained_but_disabled_from_native_build_and_publish_gates } let mirror_generator = - std::fs::read_to_string(root.join("scripts/generate-secure-exec-mirror.mjs")) + std::fs::read_to_string(root.join("scripts/generate-agentos-mirror.mjs")) .expect("read compatibility mirror generator"); assert!( mirror_generator.contains("browserShim ? { private: true } : {}") @@ -1460,7 +1460,7 @@ fn browser_sources_are_retained_but_disabled_from_native_build_and_publish_gates let source = std::fs::read_to_string(root.join(relative_path)) .unwrap_or_else(|error| panic!("read {relative_path}: {error}")); assert!( - source.contains("node --test scripts/generate-secure-exec-mirror.test.mjs"), + source.contains("node --test scripts/generate-agentos-mirror.test.mjs"), "{relative_path} must enforce compatibility-mirror reproducibility" ); } diff --git a/crates/native-sidecar/tests/builtin_conformance.rs b/crates/native-sidecar/tests/builtin_conformance.rs index a633af8473..6f9c7a22be 100644 --- a/crates/native-sidecar/tests/builtin_conformance.rs +++ b/crates/native-sidecar/tests/builtin_conformance.rs @@ -579,7 +579,7 @@ fn fixture_dns_answers(query: &Query) -> Vec { ), fixture_dns_record( "bundle.example.test.", - RData::TXT(TXT::new(vec![String::from("secure-exec")])), + RData::TXT(TXT::new(vec![String::from("agentos")])), ), ], ("bundle.example.test.", RecordType::ANY) => vec![ @@ -2916,8 +2916,8 @@ console.log(JSON.stringify({ curvesIncludePrime256v1: curves.includes("prime256v1"), curvesIncludeSecp384r1: curves.includes("secp384r1"), curvesSorted: curves.join(",") === [...curves].sort().join(","), - sha256: crypto.createHash("sha256").update("secure-exec").digest("hex"), - hmacSha256: crypto.createHmac("sha256", "shared-secret").update("secure-exec").digest("hex"), + sha256: crypto.createHash("sha256").update("agentos").digest("hex"), + hmacSha256: crypto.createHmac("sha256", "shared-secret").update("agentos").digest("hex"), randomBytesLength: random.length, randomBytesHexLength: random.toString("hex").length, randomBytesAllZero: Array.from(random).every((value) => value === 0), @@ -2935,7 +2935,7 @@ import crypto from "node:crypto"; const cipherKey = Buffer.alloc(32, 7); const cipherIv = Buffer.alloc(16, 9); -const cipherPlaintext = Buffer.from("secure-exec-crypto-surface", "utf8"); +const cipherPlaintext = Buffer.from("agentos-crypto-surface", "utf8"); const cipher = crypto.createCipheriv("aes-256-cbc", cipherKey, cipherIv); const encrypted = Buffer.concat([cipher.update(cipherPlaintext), cipher.final()]); const decipher = crypto.createDecipheriv("aes-256-cbc", cipherKey, cipherIv); @@ -2968,24 +2968,24 @@ const importedPrivateKey = crypto.createPrivateKey(privatePem); const importedPublicKey = crypto.createPublicKey(publicPem); const signer = crypto.createSign("sha256"); -signer.update("secure-exec-signature"); +signer.update("agentos-signature"); const signature = signer.sign(importedPrivateKey); const verifier = crypto.createVerify("sha256"); -verifier.update("secure-exec-signature"); +verifier.update("agentos-signature"); const signatureVerified = verifier.verify(importedPublicKey, signature); -const oneShotSignature = crypto.sign("sha256", Buffer.from("secure-exec-signature"), importedPrivateKey); +const oneShotSignature = crypto.sign("sha256", Buffer.from("agentos-signature"), importedPrivateKey); const oneShotVerified = crypto.verify( "sha256", - Buffer.from("secure-exec-signature"), + Buffer.from("agentos-signature"), importedPublicKey, oneShotSignature, ); const rsaCiphertext = crypto.publicEncrypt( { key: importedPublicKey, padding: crypto.constants.RSA_PKCS1_PADDING }, - Buffer.from("secure-exec-rsa", "utf8"), + Buffer.from("agentos-rsa", "utf8"), ); const rsaPlaintext = crypto.privateDecrypt( { key: importedPrivateKey, padding: crypto.constants.RSA_PKCS1_PADDING }, @@ -3803,7 +3803,7 @@ const promisified = await util.promisify((value, callback) => callback(null, val const encodedLength = new util.TextEncoder().encode("Grüße").length; const decodedText = new util.TextDecoder().decode(textBytes); -const deflated = zlib.deflateSync(Buffer.from("secure-exec", "utf8")); +const deflated = zlib.deflateSync(Buffer.from("agentos", "utf8")); const inflated = zlib.inflateSync(deflated).toString("utf8"); console.log(JSON.stringify({ @@ -3938,7 +3938,7 @@ const bufferValue = await streamConsumers.buffer( makeAsyncStream([Buffer.from("buf")]), ); -const deflated = zlib.deflateSync(Buffer.from("secure-exec", "utf8")); +const deflated = zlib.deflateSync(Buffer.from("agentos", "utf8")); const inflated = zlib.inflateSync(deflated).toString("utf8"); process.stdout.write(`${JSON.stringify({ @@ -4060,7 +4060,7 @@ process.exit(0); assert_eq!(result["zlib"]["importConstantsHasSyncFlush"], true); assert_eq!(result["zlib"]["createDeflateType"], "function"); assert_eq!(result["zlib"]["createInflateType"], "function"); - assert_eq!(result["zlib"]["inflated"], "secure-exec"); + assert_eq!(result["zlib"]["inflated"], "agentos"); } fn timer_handle_ref_refresh_matches_host_node_impl() { diff --git a/crates/native-sidecar/tests/connection_auth.rs b/crates/native-sidecar/tests/connection_auth.rs index 7c35316838..aa80b2e3ae 100644 --- a/crates/native-sidecar/tests/connection_auth.rs +++ b/crates/native-sidecar/tests/connection_auth.rs @@ -149,7 +149,7 @@ fn ext_requests_fail_closed_when_namespace_is_unregistered() { 2, wire_connection(&connection_id), RequestPayload::ExtEnvelope(ExtEnvelope { - namespace: "dev.rivet.secure-exec.test".to_string(), + namespace: "dev.rivet.agentos.test".to_string(), payload: b"hello-ext".to_vec(), }), )) @@ -158,7 +158,7 @@ fn ext_requests_fail_closed_when_namespace_is_unregistered() { match result.response.payload { ResponsePayload::RejectedResponse(response) => { assert_eq!(response.code, "unknown_extension"); - assert!(response.message.contains("dev.rivet.secure-exec.test")); + assert!(response.message.contains("dev.rivet.agentos.test")); } other => panic!("unexpected ext response: {other:?}"), } diff --git a/crates/native-sidecar/tests/extension.rs b/crates/native-sidecar/tests/extension.rs index 9881739471..a0ba3b33c4 100644 --- a/crates/native-sidecar/tests/extension.rs +++ b/crates/native-sidecar/tests/extension.rs @@ -19,7 +19,7 @@ use support::{ temp_dir, wire_request, wire_vm, RecordingBridge, }; -const TEST_NAMESPACE: &str = "dev.rivet.secure-exec.extension-test"; +const TEST_NAMESPACE: &str = "dev.rivet.agentos.extension-test"; struct EchoExtension; struct VmLifetimeExtension; @@ -191,7 +191,7 @@ impl Extension for EchoExtension { impl Extension for VmLifetimeExtension { fn namespace(&self) -> &str { - "dev.rivet.secure-exec.extension-vm-lifetime-test" + "dev.rivet.agentos.extension-vm-lifetime-test" } fn handle_request<'a>( @@ -318,7 +318,7 @@ fn extension_session_resources_can_dispose_bound_vm() { 4, wire_vm(&connection_id, &session_id, &vm_id), RequestPayload::ExtEnvelope(ExtEnvelope { - namespace: String::from("dev.rivet.secure-exec.extension-vm-lifetime-test"), + namespace: String::from("dev.rivet.agentos.extension-vm-lifetime-test"), payload: Vec::new(), }), )) @@ -328,7 +328,7 @@ fn extension_session_resources_can_dispose_bound_vm() { ResponsePayload::ExtEnvelope(envelope) => { assert_eq!( envelope.namespace, - "dev.rivet.secure-exec.extension-vm-lifetime-test" + "dev.rivet.agentos.extension-vm-lifetime-test" ); assert_eq!(envelope.payload, b"vm-disposed"); } diff --git a/crates/native-sidecar/tests/filesystem.rs b/crates/native-sidecar/tests/filesystem.rs index b6816ab862..861b6a42a3 100644 --- a/crates/native-sidecar/tests/filesystem.rs +++ b/crates/native-sidecar/tests/filesystem.rs @@ -398,7 +398,7 @@ mod shadow_root { let command_root = registry_command_root() .expect("registry WASM commands are required before mounting command root"); let mut mounts = vec![MountDescriptor { - guest_path: String::from("/__secure_exec/commands/0"), + guest_path: String::from("/__agentos/commands/0"), guest_source: String::from("host_dir"), guest_fstype: String::from("host_dir"), read_only: true, diff --git a/crates/native-sidecar/tests/generated_protocol.rs b/crates/native-sidecar/tests/generated_protocol.rs index 4e9cf451a8..6bd8ee6ccc 100644 --- a/crates/native-sidecar/tests/generated_protocol.rs +++ b/crates/native-sidecar/tests/generated_protocol.rs @@ -127,7 +127,7 @@ fn live_bare_codec_matches_generated_request_bytes() { 11, live_protocol::OwnershipScope::connection("conn-1"), live_protocol::RequestPayload::Ext(live_protocol::ExtEnvelope { - namespace: "dev.rivet.secure-exec.test".to_string(), + namespace: "dev.rivet.agentos.test".to_string(), payload: b"extension-bytes".to_vec(), }), )); @@ -305,7 +305,7 @@ fn generated_ext_frame() -> ProtocolFrame { connection_id: "conn-1".to_string(), }), payload: RequestPayload::ExtEnvelope(ExtEnvelope { - namespace: "dev.rivet.secure-exec.test".to_string(), + namespace: "dev.rivet.agentos.test".to_string(), payload: b"extension-bytes".to_vec(), }), }) diff --git a/crates/native-sidecar/tests/guest_identity.rs b/crates/native-sidecar/tests/guest_identity.rs index 9e59ee4589..694dfdb718 100644 --- a/crates/native-sidecar/tests/guest_identity.rs +++ b/crates/native-sidecar/tests/guest_identity.rs @@ -1,7 +1,7 @@ mod support; use agentos_native_sidecar::wire::{ - CreateVmRequest, GuestRuntimeKind, RequestId, RequestPayload, ResponsePayload, + CreateVmRequest, ExecuteRequest, GuestRuntimeKind, RequestId, RequestPayload, ResponsePayload, RootFilesystemDescriptor, RootFilesystemEntry, RootFilesystemEntryEncoding, RootFilesystemEntryKind, RootFilesystemMode, }; @@ -197,6 +197,7 @@ print(json.dumps({ "env_pwd": os.environ.get("PWD"), "env_shell": os.environ.get("SHELL"), "env_path": os.environ.get("PATH"), + "custom_env": os.environ.get("EXEC_REVIEW"), "internal_keys": sorted([ key for key in os.environ if key.startswith("AGENTOS_") or key.startswith("NODE_SYNC_RPC_") @@ -213,17 +214,26 @@ print(json.dumps({ }, ); - execute_wire( - &mut sidecar, - 4, - &connection_id, - &session_id, - &vm_id, - "proc-python-identity", - GuestRuntimeKind::Python, - std::path::Path::new("/workspace/identity.py"), - Vec::new(), - ); + let result = sidecar + .dispatch_wire_blocking(wire_request( + 4, + support::wire_vm(&connection_id, &session_id, &vm_id), + RequestPayload::ExecuteRequest(ExecuteRequest { + process_id: String::from("proc-python-identity"), + command: None, + runtime: Some(GuestRuntimeKind::Python), + entrypoint: Some(String::from("/workspace/identity.py")), + args: Vec::new(), + env: HashMap::from([(String::from("EXEC_REVIEW"), String::from("visible"))]), + cwd: None, + wasm_permission_tier: None, + }), + )) + .expect("start Python identity execution"); + assert!(matches!( + result.response.payload, + ResponsePayload::ProcessStartedResponse(_) + )); let (stdout, stderr, exit_code) = collect_guest_identity_process_output( &mut sidecar, @@ -245,6 +255,7 @@ print(json.dumps({ assert_eq!(parsed["env_pwd"], "/"); assert_eq!(parsed["env_shell"], "/bin/sh"); assert_eq!(parsed["env_path"], DEFAULT_GUEST_PATH_ENV); + assert_eq!(parsed["custom_env"], "visible"); assert_eq!(parsed["internal_keys"], Value::Array(Vec::new())); assert_eq!(parsed["path_home"], "/home/agentos"); } diff --git a/crates/native-sidecar/tests/host_dir.rs b/crates/native-sidecar/tests/host_dir.rs index cb558a2281..899d704932 100644 --- a/crates/native-sidecar/tests/host_dir.rs +++ b/crates/native-sidecar/tests/host_dir.rs @@ -28,8 +28,8 @@ mod host_dir { #[test] fn filesystem_rejects_symlink_escapes_and_round_trips_writes() { - let host_dir = temp_dir("secure-exec-host-dir-plugin"); - let outside_dir = temp_dir("secure-exec-host-dir-plugin-outside"); + let host_dir = temp_dir("agentos-host-dir-plugin"); + let outside_dir = temp_dir("agentos-host-dir-plugin-outside"); fs::write(host_dir.join("hello.txt"), "hello from host").expect("seed host file"); std::os::unix::fs::symlink(&outside_dir, host_dir.join("escape")) .expect("seed escape symlink"); @@ -75,7 +75,7 @@ mod host_dir { #[test] fn filesystem_pwrite_updates_in_place_and_zero_fills_gaps() { - let host_dir = temp_dir("secure-exec-host-dir-plugin-pwrite"); + let host_dir = temp_dir("agentos-host-dir-plugin-pwrite"); fs::write(host_dir.join("data.txt"), b"abcdef").expect("seed host file"); let mut filesystem = HostDirFilesystem::new(&host_dir).expect("create host dir fs"); @@ -96,8 +96,8 @@ mod host_dir { #[test] fn filesystem_pwrite_rejects_symlink_escape_targets() { - let host_dir = temp_dir("secure-exec-host-dir-plugin-pwrite-escape"); - let outside_dir = temp_dir("secure-exec-host-dir-plugin-pwrite-escape-outside"); + let host_dir = temp_dir("agentos-host-dir-plugin-pwrite-escape"); + let outside_dir = temp_dir("agentos-host-dir-plugin-pwrite-escape-outside"); fs::write(outside_dir.join("outside.txt"), b"outside").expect("seed outside file"); std::os::unix::fs::symlink(&outside_dir, host_dir.join("escape")) .expect("seed escape symlink"); @@ -118,7 +118,7 @@ mod host_dir { #[test] fn filesystem_rejects_full_reads_above_host_dir_limit() { - let host_dir = temp_dir("secure-exec-host-dir-plugin-full-read-limit"); + let host_dir = temp_dir("agentos-host-dir-plugin-full-read-limit"); let huge_file = fs::File::create(host_dir.join("huge.bin")).expect("create huge file"); huge_file .set_len(MAX_HOST_DIR_READ_BYTES as u64 + 1) @@ -135,7 +135,7 @@ mod host_dir { #[test] fn filesystem_pread_rejects_lengths_above_host_dir_limit() { - let host_dir = temp_dir("secure-exec-host-dir-plugin-pread-limit"); + let host_dir = temp_dir("agentos-host-dir-plugin-pread-limit"); fs::write(host_dir.join("small.txt"), b"small").expect("seed host file"); let mut filesystem = HostDirFilesystem::new(&host_dir).expect("create host dir fs"); @@ -149,8 +149,8 @@ mod host_dir { #[test] fn filesystem_metadata_ops_reject_symlink_targets() { - let host_dir = temp_dir("secure-exec-host-dir-plugin-metadata"); - let outside_dir = temp_dir("secure-exec-host-dir-plugin-metadata-outside"); + let host_dir = temp_dir("agentos-host-dir-plugin-metadata"); + let outside_dir = temp_dir("agentos-host-dir-plugin-metadata-outside"); let outside_file = outside_dir.join("outside.txt"); fs::write(&outside_file, b"outside").expect("seed outside file"); std::os::unix::fs::symlink(&outside_file, host_dir.join("link")) @@ -293,7 +293,7 @@ mod host_dir { #[test] fn plugin_config_can_enforce_read_only_mounts() { - let host_dir = temp_dir("secure-exec-host-dir-plugin-readonly"); + let host_dir = temp_dir("agentos-host-dir-plugin-readonly"); fs::write(host_dir.join("hello.txt"), "hello from host").expect("seed host file"); let plugin = HostDirMountPlugin; diff --git a/crates/native-sidecar/tests/language_execution.rs b/crates/native-sidecar/tests/language_execution.rs new file mode 100644 index 0000000000..47dc0e53bf --- /dev/null +++ b/crates/native-sidecar/tests/language_execution.rs @@ -0,0 +1,710 @@ +mod support; + +use agentos_native_sidecar::wire; +use std::collections::HashMap; +use std::time::{Duration, Instant}; +use support::{ + authenticate_wire, create_vm_wire, dispose_vm_and_close_session_wire, new_sidecar, + open_session_wire, temp_dir, wire_request, wire_session, wire_vm, +}; + +fn process_options(execution_id: Option) -> wire::ProcessExecutionOptions { + wire::ProcessExecutionOptions { + identity: wire::ExecutionIdentityOptions { + execution_id, + create_if_missing: None, + }, + detached: Some(true), + cwd: None, + env: Some(HashMap::new()), + args: Vec::new(), + stdin: None, + timeout_ms: Some(30_000), + pty: None, + } +} + +fn accepted_execution_id(result: wire::WireDispatchResult) -> String { + match result.response.payload { + wire::ResponsePayload::ExecutionAcceptedResponse(response) => { + response.execution.execution_id + } + other => panic!("unexpected language execution response: {other:?}"), + } +} + +fn wait_for_execution( + sidecar: &mut agentos_native_sidecar::NativeSidecar, + connection_id: &str, + session_id: &str, + vm_id: &str, + execution_id: &str, +) -> wire::ExecutionCompletedResponse { + let deadline = Instant::now() + Duration::from_secs(30); + loop { + let event = sidecar + .poll_event_wire_blocking( + &wire_session(connection_id, session_id), + Duration::from_millis(100), + ) + .expect("poll execution event"); + if let Some(event) = event { + if let wire::EventPayload::ExecutionCompletedEvent(completed) = event.payload { + if completed.execution_id == execution_id { + break; + } + } + } + assert!(Instant::now() < deadline, "language execution timed out"); + } + + let response = sidecar + .dispatch_wire_blocking(wire_request( + 90, + wire_vm(connection_id, session_id, vm_id), + wire::RequestPayload::WaitExecutionRequest(wire::WaitExecutionRequest { + execution_id: execution_id.to_owned(), + }), + )) + .expect("wait for execution result"); + match response.response.payload { + wire::ResponsePayload::ExecutionCompletedResponse(result) => result, + other => panic!("unexpected wait response: {other:?}"), + } +} + +fn reset_execution( + sidecar: &mut agentos_native_sidecar::NativeSidecar, + connection_id: &str, + session_id: &str, + vm_id: &str, + execution_id: &str, +) { + let response = sidecar + .dispatch_wire_blocking(wire_request( + 91, + wire_vm(connection_id, session_id, vm_id), + wire::RequestPayload::ResetExecutionRequest(wire::ResetExecutionRequest { + execution_id: execution_id.to_owned(), + }), + )) + .expect("reset retained execution"); + match response.response.payload { + wire::ResponsePayload::ExecutionDescriptorResponse(response) => { + assert_eq!(response.execution.state, wire::ExecutionState::Idle); + assert_eq!(response.execution.retained_language, None); + } + other => panic!("unexpected reset response: {other:?}"), + } +} + +#[test] +fn javascript_execution_reuses_retained_context() { + let mut sidecar = new_sidecar("language-execution-retained-js"); + let connection_id = authenticate_wire(&mut sidecar, "language-execution-connection"); + let session_id = open_session_wire(&mut sidecar, 2, &connection_id); + let cwd = temp_dir("language-execution-retained-js-cwd"); + let (vm_id, _) = create_vm_wire( + &mut sidecar, + 3, + &connection_id, + &session_id, + wire::GuestRuntimeKind::JavaScript, + &cwd, + ); + let first = sidecar + .dispatch_wire_blocking(wire_request( + 4, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::JavaScriptExecutionRequest(wire::JavaScriptExecutionRequest { + process: process_options(None), + source: String::from( + "import { sep } from 'node:path'; let retainedAnswer = sep === '/' ? 41 : 0;", + ), + format: Some(wire::JavaScriptModuleFormat::Module), + file_path: None, + inputs: None, + }), + )) + .expect("start first JavaScript operation"); + let execution_id = accepted_execution_id(first); + let first_result = wait_for_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ); + assert_eq!(first_result.outcome, wire::ExecutionOutcome::Succeeded); + + let mut fresh_process_options = process_options(Some(execution_id.clone())); + fresh_process_options.args = vec![String::from("-e"), String::from("void 0")]; + let process = sidecar + .dispatch_wire_blocking(wire_request( + 5, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::ArgvExecutionRequest(wire::ArgvExecutionRequest { + process: fresh_process_options, + command: String::from("node"), + }), + )) + .expect("start fresh process between retained operations"); + assert!( + process.events.is_empty(), + "interleaved process failed during admission: {:?}", + process.events + ); + assert_eq!(accepted_execution_id(process), execution_id); + let process_result = wait_for_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ); + assert_eq!(process_result.outcome, wire::ExecutionOutcome::Succeeded); + + let typescript = sidecar + .dispatch_wire_blocking(wire_request( + 6, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::TypeScriptExecutionRequest(wire::TypeScriptExecutionRequest { + process: process_options(Some(execution_id.clone())), + source: String::from( + "const typedAnswer: number = sep === '/' ? retainedAnswer + 1 : 0;", + ), + file_path: Some(String::from("retained-cell.ts")), + tsconfig_path: None, + compiler_options: None, + inputs: None, + }), + )) + .expect("start retained TypeScript operation"); + assert_eq!(accepted_execution_id(typescript), execution_id); + let typescript_result = wait_for_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ); + assert_eq!(typescript_result.outcome, wire::ExecutionOutcome::Succeeded); + + let second = sidecar + .dispatch_wire_blocking(wire_request( + 7, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::JavaScriptEvaluationRequest(wire::JavaScriptEvaluationRequest { + process: process_options(Some(execution_id.clone())), + expression: String::from("typedAnswer"), + format: Some(wire::JavaScriptModuleFormat::Module), + file_path: None, + inputs: None, + }), + )) + .expect("start retained JavaScript evaluation"); + assert_eq!(accepted_execution_id(second), execution_id); + let second_result = wait_for_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ); + assert_eq!(second_result.outcome, wire::ExecutionOutcome::Succeeded); + assert!(second_result.outputs.contains("42")); + + reset_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ); + dispose_vm_and_close_session_wire(&mut sidecar, &connection_id, &session_id, &vm_id); +} + +#[test] +fn typescript_check_reports_semantic_diagnostics() { + let mut sidecar = new_sidecar("language-execution-typescript-check"); + let connection_id = authenticate_wire(&mut sidecar, "typescript-check-connection"); + let session_id = open_session_wire(&mut sidecar, 2, &connection_id); + let cwd = temp_dir("language-execution-typescript-check-cwd"); + let (vm_id, _) = create_vm_wire( + &mut sidecar, + 3, + &connection_id, + &session_id, + wire::GuestRuntimeKind::JavaScript, + &cwd, + ); + let check = sidecar + .dispatch_wire_blocking(wire_request( + 4, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::TypeScriptCheckRequest(wire::TypeScriptCheckRequest { + identity: wire::ExecutionIdentityOptions { + execution_id: None, + create_if_missing: None, + }, + source: String::from("const answer: string = 42;"), + cwd: None, + file_path: Some(String::from("answer.ts")), + tsconfig_path: None, + compiler_options: None, + timeout_ms: Some(30_000), + }), + )) + .expect("start TypeScript check"); + let execution_id = accepted_execution_id(check); + let result = wait_for_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ); + assert_eq!( + result.outcome, + wire::ExecutionOutcome::Succeeded, + "TypeScript check failed: {}", + String::from_utf8_lossy(&result.stderr) + ); + let outputs: serde_json::Value = + serde_json::from_str(&result.outputs).expect("decode TypeScript check outputs"); + assert_eq!(outputs[0]["data"]["hasErrors"], true); + assert!(outputs[0]["data"]["diagnostics"] + .as_array() + .is_some_and(|diagnostics| diagnostics.iter().any(|item| item["code"] == 2322))); + + dispose_vm_and_close_session_wire(&mut sidecar, &connection_id, &session_id, &vm_id); +} + +#[test] +fn python_execution_reuses_retained_globals() { + let mut sidecar = new_sidecar("language-execution-retained-python"); + let connection_id = authenticate_wire(&mut sidecar, "retained-python-connection"); + let session_id = open_session_wire(&mut sidecar, 2, &connection_id); + let cwd = temp_dir("language-execution-retained-python-cwd"); + let (vm_id, _) = create_vm_wire( + &mut sidecar, + 3, + &connection_id, + &session_id, + wire::GuestRuntimeKind::Python, + &cwd, + ); + let first = sidecar + .dispatch_wire_blocking(wire_request( + 4, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::PythonExecutionRequest(wire::PythonExecutionRequest { + process: process_options(None), + source: String::from( + "import asyncio\nawait asyncio.sleep(0)\nretained_answer = 41", + ), + inputs: None, + }), + )) + .expect("start first Python operation"); + let execution_id = accepted_execution_id(first); + assert_eq!( + wait_for_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ) + .outcome, + wire::ExecutionOutcome::Succeeded + ); + + let second = sidecar + .dispatch_wire_blocking(wire_request( + 5, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::PythonEvaluationRequest(wire::PythonEvaluationRequest { + process: process_options(Some(execution_id.clone())), + expression: String::from("retained_answer + 1"), + inputs: None, + }), + )) + .expect("start retained Python evaluation"); + assert_eq!(accepted_execution_id(second), execution_id); + let result = wait_for_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ); + assert_eq!(result.outcome, wire::ExecutionOutcome::Succeeded); + assert!(result.outputs.contains("42")); + + reset_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ); + dispose_vm_and_close_session_wire(&mut sidecar, &connection_id, &session_id, &vm_id); +} + +#[test] +fn execution_timeout_is_enforced_by_the_sidecar() { + let mut sidecar = new_sidecar("language-execution-timeout"); + let connection_id = authenticate_wire(&mut sidecar, "execution-timeout-connection"); + let session_id = open_session_wire(&mut sidecar, 2, &connection_id); + let cwd = temp_dir("language-execution-timeout-cwd"); + let (vm_id, _) = create_vm_wire( + &mut sidecar, + 3, + &connection_id, + &session_id, + wire::GuestRuntimeKind::JavaScript, + &cwd, + ); + let mut options = process_options(None); + options.timeout_ms = Some(100); + let started_at = Instant::now(); + let started = sidecar + .dispatch_wire_blocking(wire_request( + 4, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::JavaScriptExecutionRequest(wire::JavaScriptExecutionRequest { + process: options, + source: String::from("while (true) {}"), + format: Some(wire::JavaScriptModuleFormat::CommonJs), + file_path: None, + inputs: None, + }), + )) + .expect("start timed JavaScript operation"); + let execution_id = accepted_execution_id(started); + let result = wait_for_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ); + assert_eq!(result.outcome, wire::ExecutionOutcome::TimedOut); + assert_eq!( + result.error.as_ref().map(|error| error.code.as_str()), + Some("execution_timed_out") + ); + assert!( + started_at.elapsed() < Duration::from_secs(5), + "sidecar timeout did not terminate the guest promptly" + ); + + dispose_vm_and_close_session_wire(&mut sidecar, &connection_id, &session_id, &vm_id); +} + +#[test] +fn evaluation_rejects_non_json_values_with_a_structured_result() { + let mut sidecar = new_sidecar("language-execution-json-evaluation"); + let connection_id = authenticate_wire(&mut sidecar, "json-evaluation-connection"); + let session_id = open_session_wire(&mut sidecar, 2, &connection_id); + let cwd = temp_dir("language-execution-json-evaluation-cwd"); + let (vm_id, _) = create_vm_wire( + &mut sidecar, + 3, + &connection_id, + &session_id, + wire::GuestRuntimeKind::JavaScript, + &cwd, + ); + let started = sidecar + .dispatch_wire_blocking(wire_request( + 4, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::JavaScriptEvaluationRequest(wire::JavaScriptEvaluationRequest { + process: process_options(None), + expression: String::from("undefined"), + format: Some(wire::JavaScriptModuleFormat::CommonJs), + file_path: None, + inputs: None, + }), + )) + .expect("start non-JSON JavaScript evaluation"); + let execution_id = accepted_execution_id(started); + let result = wait_for_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ); + assert_eq!(result.outcome, wire::ExecutionOutcome::Failed); + assert_eq!( + result.error.as_ref().map(|error| error.code.as_str()), + Some("evaluation_serialization_failed") + ); + assert!(result + .error + .as_ref() + .is_some_and(|error| error.message.contains("JSON-serializable"))); + assert_eq!(result.outputs, "[]"); + + reset_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ); + + dispose_vm_and_close_session_wire(&mut sidecar, &connection_id, &session_id, &vm_id); +} + +#[test] +fn package_mutations_are_exclusive_across_executions() { + let mut sidecar = new_sidecar("language-execution-package-mutation"); + let connection_id = authenticate_wire(&mut sidecar, "package-mutation-connection"); + let session_id = open_session_wire(&mut sidecar, 2, &connection_id); + let cwd = temp_dir("language-execution-package-mutation-cwd"); + let (vm_id, _) = create_vm_wire( + &mut sidecar, + 3, + &connection_id, + &session_id, + wire::GuestRuntimeKind::Python, + &cwd, + ); + let first = sidecar + .dispatch_wire_blocking(wire_request( + 4, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::PythonInstallRequest(wire::PythonInstallRequest { + identity: wire::ExecutionIdentityOptions { + execution_id: None, + create_if_missing: None, + }, + cwd: None, + env: None, + timeout_ms: Some(30_000), + packages: vec![String::from("agentos-package-mutation-test")], + upgrade: None, + requirements_file: None, + index_url: Some(String::from("http://127.0.0.1:9/simple")), + extra_index_urls: Vec::new(), + }), + )) + .expect("start first package mutation"); + let first_execution_id = accepted_execution_id(first); + + let second = sidecar + .dispatch_wire_blocking(wire_request( + 5, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::NpmProjectInstallRequest(wire::NpmProjectInstallRequest { + identity: wire::ExecutionIdentityOptions { + execution_id: Some(String::from("second-package-mutation")), + create_if_missing: Some(true), + }, + cwd: None, + env: None, + timeout_ms: Some(30_000), + frozen: None, + }), + )) + .expect("reject concurrent package mutation"); + match second.response.payload { + wire::ResponsePayload::RejectedResponse(rejected) => { + assert_eq!(rejected.code, "execution_busy"); + assert!(rejected.message.contains(&first_execution_id)); + assert!(rejected.message.contains("serialized at VM scope")); + } + other => panic!("expected package mutation rejection, got {other:?}"), + } + + sidecar + .dispatch_wire_blocking(wire_request( + 6, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::CancelExecutionRequest(wire::CancelExecutionRequest { + execution_id: first_execution_id.clone(), + }), + )) + .expect("cancel first package mutation"); + assert_eq!( + wait_for_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &first_execution_id, + ) + .outcome, + wire::ExecutionOutcome::Cancelled + ); + + let resumed = sidecar + .dispatch_wire_blocking(wire_request( + 7, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::NpmProjectInstallRequest(wire::NpmProjectInstallRequest { + identity: wire::ExecutionIdentityOptions { + execution_id: Some(String::from("second-package-mutation")), + create_if_missing: Some(true), + }, + cwd: None, + env: None, + timeout_ms: Some(30_000), + frozen: None, + }), + )) + .expect("start package mutation after the prior one completed"); + let resumed_execution_id = accepted_execution_id(resumed); + sidecar + .dispatch_wire_blocking(wire_request( + 8, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::CancelExecutionRequest(wire::CancelExecutionRequest { + execution_id: resumed_execution_id.clone(), + }), + )) + .expect("cancel resumed package mutation"); + assert_eq!( + wait_for_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &resumed_execution_id, + ) + .outcome, + wire::ExecutionOutcome::Cancelled + ); + + dispose_vm_and_close_session_wire(&mut sidecar, &connection_id, &session_id, &vm_id); +} + +#[test] +fn detached_lifecycle_replays_cancels_resets_and_deletes() { + let mut sidecar = new_sidecar("language-execution-detached-lifecycle"); + let connection_id = authenticate_wire(&mut sidecar, "detached-lifecycle-connection"); + let session_id = open_session_wire(&mut sidecar, 2, &connection_id); + let cwd = temp_dir("language-execution-detached-lifecycle-cwd"); + let (vm_id, _) = create_vm_wire( + &mut sidecar, + 3, + &connection_id, + &session_id, + wire::GuestRuntimeKind::JavaScript, + &cwd, + ); + + let started = sidecar + .dispatch_wire_blocking(wire_request( + 4, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::JavaScriptExecutionRequest(wire::JavaScriptExecutionRequest { + process: process_options(None), + source: String::from("console.log('replay-me')"), + format: Some(wire::JavaScriptModuleFormat::CommonJs), + file_path: None, + inputs: None, + }), + )) + .expect("start detached JavaScript operation"); + let execution_id = accepted_execution_id(started); + assert_eq!( + wait_for_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ) + .outcome, + wire::ExecutionOutcome::Succeeded + ); + + let replay = sidecar + .dispatch_wire_blocking(wire_request( + 5, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::ReadExecutionOutputRequest(wire::ReadExecutionOutputRequest { + execution_id: execution_id.clone(), + cursor: None, + limit: Some(1), + }), + )) + .expect("read retained execution output"); + match replay.response.payload { + wire::ResponsePayload::ExecutionOutputPageResponse(page) => { + assert!(!page.truncated); + assert_eq!(page.events.len(), 1); + assert!(String::from_utf8_lossy(&page.events[0].chunk).contains("replay-me")); + assert!(!page.next_cursor.is_empty()); + } + other => panic!("expected execution output page, got {other:?}"), + } + + reset_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ); + + let mut cancel_options = process_options(Some(execution_id.clone())); + cancel_options.timeout_ms = Some(30_000); + let cancellable = sidecar + .dispatch_wire_blocking(wire_request( + 6, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::JavaScriptExecutionRequest(wire::JavaScriptExecutionRequest { + process: cancel_options, + source: String::from("while (true) {}"), + format: Some(wire::JavaScriptModuleFormat::CommonJs), + file_path: None, + inputs: None, + }), + )) + .expect("start cancellable execution"); + assert_eq!(accepted_execution_id(cancellable), execution_id); + sidecar + .dispatch_wire_blocking(wire_request( + 7, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::CancelExecutionRequest(wire::CancelExecutionRequest { + execution_id: execution_id.clone(), + }), + )) + .expect("cancel execution"); + assert_eq!( + wait_for_execution( + &mut sidecar, + &connection_id, + &session_id, + &vm_id, + &execution_id, + ) + .outcome, + wire::ExecutionOutcome::Cancelled + ); + + let deleted = sidecar + .dispatch_wire_blocking(wire_request( + 8, + wire_vm(&connection_id, &session_id, &vm_id), + wire::RequestPayload::DeleteExecutionRequest(wire::DeleteExecutionRequest { + execution_id: execution_id.clone(), + }), + )) + .expect("delete idle execution"); + match deleted.response.payload { + wire::ResponsePayload::ExecutionDeletedResponse(response) => { + assert_eq!(response.execution_id, execution_id); + } + other => panic!("expected execution deletion, got {other:?}"), + } + + dispose_vm_and_close_session_wire(&mut sidecar, &connection_id, &session_id, &vm_id); +} diff --git a/crates/native-sidecar/tests/posix_path_repro.rs b/crates/native-sidecar/tests/posix_path_repro.rs index 0fb58251a8..03f3796ca0 100644 --- a/crates/native-sidecar/tests/posix_path_repro.rs +++ b/crates/native-sidecar/tests/posix_path_repro.rs @@ -73,7 +73,7 @@ fn configure_mounts( mounts.insert( 0, MountDescriptor { - guest_path: String::from("/__secure_exec/commands/0"), + guest_path: String::from("/__agentos/commands/0"), guest_source: String::from("host_dir"), guest_fstype: String::from("host_dir"), read_only: true, diff --git a/crates/native-sidecar/tests/projection_bench.rs b/crates/native-sidecar/tests/projection_bench.rs index 034b4830f8..c623c19635 100644 --- a/crates/native-sidecar/tests/projection_bench.rs +++ b/crates/native-sidecar/tests/projection_bench.rs @@ -199,7 +199,7 @@ impl Drop for SyntheticTargets { fn write_repeated_file(path: &Path, len: usize) { let mut file = fs::File::create(path) .unwrap_or_else(|e| panic!("create synthetic payload {} failed: {e}", path.display())); - let chunk = b"secure-exec projection bench payload\n"; + let chunk = b"agentos projection bench payload\n"; let mut remaining = len; while remaining > 0 { let n = remaining.min(chunk.len()); @@ -257,7 +257,7 @@ fn create_package_tar(label: &str, dest: &Path, commands: &[&str], payload_bytes fn create_synthetic_targets() -> SyntheticTargets { let unique = format!( - "secure-exec-projection-bench-{}-{}", + "agentos-projection-bench-{}-{}", std::process::id(), std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) @@ -304,7 +304,7 @@ fn create_synthetic_targets() -> SyntheticTargets { fn create_repacked_real_targets(sources: &[(&'static str, &Path)]) -> RealTargets { let unique = format!( - "secure-exec-projection-real-{}-{}", + "agentos-projection-real-{}-{}", std::process::id(), std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) diff --git a/crates/native-sidecar/tests/promisify_module_load.rs b/crates/native-sidecar/tests/promisify_module_load.rs index 540fa323bc..c2c049e9b4 100644 --- a/crates/native-sidecar/tests/promisify_module_load.rs +++ b/crates/native-sidecar/tests/promisify_module_load.rs @@ -1,6 +1,6 @@ //! Regression guard for issue #11: `util.promisify()` throws at module load. //! -//! Original failure mode: a hand-rolled `@secure-exec/core` `util` polyfill exposed many +//! Original failure mode: a hand-rolled legacy `util` polyfill exposed many //! builtin functions as `undefined`. Adapter dependencies (extract-zip, get-stream) called //! `promisify(undefined)` at module-load time, and the polyfill's `promisify` threw a //! `TypeError` synchronously, crashing the whole module load before any application code ran. diff --git a/crates/native-sidecar/tests/protocol.rs b/crates/native-sidecar/tests/protocol.rs index c920439557..b1a17c8cea 100644 --- a/crates/native-sidecar/tests/protocol.rs +++ b/crates/native-sidecar/tests/protocol.rs @@ -130,7 +130,7 @@ fn ext_envelope_event_encoding_microbench() { let ext_frame = ProtocolFrame::Event(EventFrame::new( ownership.clone(), EventPayload::Ext(ExtEnvelope { - namespace: String::from("dev.rivet.secure-exec.acp"), + namespace: String::from("dev.rivet.agentos.acp"), payload: inner, }), )); diff --git a/crates/native-sidecar/tests/sandbox_agent.rs b/crates/native-sidecar/tests/sandbox_agent.rs index 83ce1dcf4d..144ff2e766 100644 --- a/crates/native-sidecar/tests/sandbox_agent.rs +++ b/crates/native-sidecar/tests/sandbox_agent.rs @@ -16,7 +16,7 @@ mod sandbox_agent { #[test] fn filesystem_round_trips_small_files_and_uses_http_range_for_large_pread() { - let server = MockSandboxAgentServer::start("secure-exec-sandbox-plugin", None); + let server = MockSandboxAgentServer::start("agentos-sandbox-plugin", None); fs::write(server.root().join("hello.txt"), "hello from sandbox").expect("seed file"); let large_file = (0..100 * 1024) .map(|index| (index % 251) as u8) @@ -73,10 +73,8 @@ mod sandbox_agent { #[test] fn filesystem_pread_falls_back_to_full_fetch_when_remote_ignores_range() { - let server = MockSandboxAgentServer::start_without_range_support( - "secure-exec-sandbox-plugin", - None, - ); + let server = + MockSandboxAgentServer::start_without_range_support("agentos-sandbox-plugin", None); let large_file = (0..100 * 1024) .map(|index| (index % 251) as u8) .collect::>(); @@ -117,7 +115,7 @@ mod sandbox_agent { #[test] fn filesystem_pread_rejects_full_fetch_fallback_above_limit() { let server = MockSandboxAgentServer::start_without_range_support( - "secure-exec-sandbox-plugin-limit", + "agentos-sandbox-plugin-limit", None, ); fs::write(server.root().join("large.bin"), vec![b'x'; 4096]).expect("seed large file"); @@ -145,7 +143,7 @@ mod sandbox_agent { #[test] fn filesystem_pread_rejects_streamed_full_fetch_fallback_above_limit() { let server = MockSandboxAgentServer::start_without_range_support( - "secure-exec-sandbox-plugin-stream-limit", + "agentos-sandbox-plugin-stream-limit", None, ); fs::write(server.root().join("stream-over-limit"), vec![b'x'; 4096]) @@ -185,7 +183,7 @@ mod sandbox_agent { #[test] fn sandbox_agent_client_does_not_follow_redirects() { - let server = MockSandboxAgentServer::start("secure-exec-sandbox-plugin-redirect", None); + let server = MockSandboxAgentServer::start("agentos-sandbox-plugin-redirect", None); let mut filesystem = SandboxAgentFilesystem::from_config(SandboxAgentMountConfig { base_url: server.base_url().to_owned(), @@ -274,7 +272,7 @@ mod sandbox_agent { #[test] fn filesystem_truncate_uses_process_api_without_full_file_buffering() { - let server = MockSandboxAgentServer::start("secure-exec-sandbox-plugin-truncate", None); + let server = MockSandboxAgentServer::start("agentos-sandbox-plugin-truncate", None); fs::write(server.root().join("large.bin"), vec![b'x'; 512]).expect("seed large file"); let mut filesystem = SandboxAgentFilesystem::from_config(SandboxAgentMountConfig { @@ -340,10 +338,8 @@ mod sandbox_agent { #[test] fn plugin_scopes_base_path_and_preserves_auth_headers() { - let server = MockSandboxAgentServer::start( - "secure-exec-sandbox-plugin-auth", - Some("secret-token"), - ); + let server = + MockSandboxAgentServer::start("agentos-sandbox-plugin-auth", Some("secret-token")); fs::create_dir_all(server.root().join("scoped")).expect("create scoped root"); fs::write(server.root().join("scoped/hello.txt"), "scoped hello") .expect("seed scoped file"); @@ -387,8 +383,7 @@ mod sandbox_agent { #[test] fn plugin_normalizes_relative_base_path_before_scoping_requests() { - let server = - MockSandboxAgentServer::start("secure-exec-sandbox-plugin-base-path", None); + let server = MockSandboxAgentServer::start("agentos-sandbox-plugin-base-path", None); fs::create_dir_all(server.root().join("scoped")).expect("create scoped root"); fs::write( server.root().join("scoped/hello.txt"), @@ -427,7 +422,7 @@ mod sandbox_agent { #[test] fn plugin_unscopes_process_helper_targets_for_relative_base_path() { let server = - MockSandboxAgentServer::start("secure-exec-sandbox-plugin-relative-process", None); + MockSandboxAgentServer::start("agentos-sandbox-plugin-relative-process", None); fs::create_dir_all(server.root().join("scoped")).expect("create scoped root"); fs::write( server.root().join("scoped/original.txt"), @@ -470,7 +465,7 @@ mod sandbox_agent { #[test] fn filesystem_uses_process_api_for_symlink_and_metadata_operations() { - let server = MockSandboxAgentServer::start("secure-exec-sandbox-plugin-process", None); + let server = MockSandboxAgentServer::start("agentos-sandbox-plugin-process", None); fs::write(server.root().join("original.txt"), "hello from sandbox") .expect("seed original file"); @@ -563,7 +558,7 @@ mod sandbox_agent { #[test] fn filesystem_reports_clear_error_when_process_api_is_unavailable() { let server = MockSandboxAgentServer::start_without_process_api( - "secure-exec-sandbox-plugin-no-proc", + "agentos-sandbox-plugin-no-proc", None, ); fs::write(server.root().join("original.txt"), "hello from sandbox") diff --git a/crates/native-sidecar/tests/service.rs b/crates/native-sidecar/tests/service.rs index 4a0769ce9b..cb1f69c64c 100644 --- a/crates/native-sidecar/tests/service.rs +++ b/crates/native-sidecar/tests/service.rs @@ -24,6 +24,9 @@ mod filesystem; #[allow(dead_code, unused_imports)] #[path = "../src/json_rpc.rs"] mod json_rpc; +#[allow(dead_code)] +#[path = "../src/language_execution.rs"] +mod language_execution; #[allow(dead_code, unused_imports)] #[path = "../src/limits.rs"] mod limits; @@ -2209,7 +2212,7 @@ ykAheWCsAteSEWVc0w==\n\ OwnershipScope::vm(connection_id, session_id, vm_id), RequestPayload::ConfigureVm(ConfigureVmRequest { mounts: vec![MountDescriptor { - guest_path: String::from("/__secure_exec/commands/0"), + guest_path: String::from("/__agentos/commands/0"), guest_source: String::from("agentos"), guest_fstype: String::from("agentos"), read_only: true, @@ -3338,7 +3341,7 @@ console.log(JSON.stringify({ status: "ok", summary })); ), fixture_dns_record( "bundle.example.test.", - RData::TXT(TXT::new(vec![String::from("secure-exec")])), + RData::TXT(TXT::new(vec![String::from("agentos")])), ), ], ("bundle.example.test.", RecordType::ANY) => vec![ @@ -9556,7 +9559,7 @@ console.log(JSON.stringify({ status: "ok", summary })); .expect("clock should be monotonic") .as_nanos(); let metadata_path = - std::env::temp_dir().join(format!("secure-exec-service-s3-{unique}.sqlite")); + std::env::temp_dir().join(format!("agentos-service-s3-{unique}.sqlite")); let mut sidecar = create_test_sidecar(); let (connection_id, session_id) = @@ -11304,7 +11307,7 @@ console.log(JSON.stringify({ status: "ok", summary })); OwnershipScope::vm(&connection_id, &session_id, &vm_id), RequestPayload::ConfigureVm(ConfigureVmRequest { mounts: vec![MountDescriptor { - guest_path: String::from("/__secure_exec/commands/0"), + guest_path: String::from("/__agentos/commands/0"), guest_source: String::from("agentos"), guest_fstype: String::from("agentos"), read_only: true, @@ -11406,7 +11409,7 @@ console.log(JSON.stringify({ status: "ok", summary })); OwnershipScope::vm(&connection_id, &session_id, &vm_id), RequestPayload::ConfigureVm(ConfigureVmRequest { mounts: vec![MountDescriptor { - guest_path: String::from("/__secure_exec/commands/0"), + guest_path: String::from("/__agentos/commands/0"), guest_source: String::from("agentos"), guest_fstype: String::from("agentos"), read_only: true, @@ -11804,7 +11807,7 @@ console.log(JSON.stringify({ status: "ok", summary })); OwnershipScope::vm(&connection_id, &session_id, &vm_id), RequestPayload::ConfigureVm(ConfigureVmRequest { mounts: vec![MountDescriptor { - guest_path: String::from("/__secure_exec/commands/0"), + guest_path: String::from("/__agentos/commands/0"), guest_source: String::from("agentos"), guest_fstype: String::from("agentos"), read_only: true, @@ -11841,11 +11844,11 @@ console.log(JSON.stringify({ status: "ok", summary })); assert!( path_entries .first() - .is_some_and(|entry| *entry == "/__secure_exec/commands/0"), + .is_some_and(|entry| *entry == "/__agentos/commands/0"), "PATH should prioritize mounted command root: {path}" ); assert!( - path_entries.contains(&"/__secure_exec/commands/0"), + path_entries.contains(&"/__agentos/commands/0"), "PATH should include mounted command root: {path}" ); @@ -13512,7 +13515,7 @@ process.stdout.write(`${JSON.stringify(snapshot)}\n`); &vm_id, "proc-js-binding-rpc", crate::protocol::JavascriptChildProcessSpawnRequest { - command: String::from("/__secure_exec/commands/0/agentos-math"), + command: String::from("/__agentos/commands/0/agentos-math"), args: vec![ String::from("add"), String::from("--a"), @@ -13566,7 +13569,7 @@ process.stdout.write(`${JSON.stringify(snapshot)}\n`); &vm.guest_cwd, &vm.host_cwd, &crate::protocol::JavascriptChildProcessSpawnRequest { - command: String::from("/__secure_exec/commands/0/agentos-math"), + command: String::from("/__agentos/commands/0/agentos-math"), args: vec![ String::from("add"), String::from("--a"), @@ -17505,7 +17508,7 @@ await new Promise(() => {}); json!("aes-256-gcm"), json!(base64::engine::general_purpose::STANDARD.encode([7_u8; 32])), json!(base64::engine::general_purpose::STANDARD.encode([3_u8; 12])), - json!(base64::engine::general_purpose::STANDARD.encode(b"secure-exec")), + json!(base64::engine::general_purpose::STANDARD.encode(b"agentos")), json!(r#"{"aad":"YWR2YW5jZWQ=","authTagLength":16}"#), ], }, @@ -17535,7 +17538,7 @@ await new Promise(() => {}); .expect("decipheriv response"); assert_eq!( decode_base64(decipher_response.as_str().expect("decipher response")), - b"secure-exec" + b"agentos" ); let mut streaming_process = create_crypto_test_process(); @@ -18986,7 +18989,7 @@ console.log(JSON.stringify({ membershipAddress, sourceAddress, reboundAddress, d r#" import net from "node:net"; -const path = "/tmp/secure-exec-unix-echo.sock"; +const path = "/tmp/agentos-unix-echo.sock"; const summary = await new Promise((resolve, reject) => { const server = net.createServer((socket) => { socket.setEncoding("utf8"); @@ -23755,7 +23758,7 @@ console.log(`BODY:${{body}}`); sidecar.vms.get(&vm_id).expect("javascript vm"), ) .expect("build Unix socket path context"); - let socket_path = "/tmp/secure-exec.sock"; + let socket_path = "/tmp/agentos.sock"; let listen = { let vm = sidecar.vms.get_mut(&vm_id).expect("javascript vm"); @@ -24157,7 +24160,7 @@ console.log(`BODY:${{body}}`); 19_u64, "net.connect", vec![json!({ "path": socket_path })], - "unix:/tmp/secure-exec.sock", + "unix:/tmp/agentos.sock", ), ( 20_u64, @@ -24968,7 +24971,7 @@ try { ), ( String::from("AGENTOS_VIRTUAL_OS_HOSTNAME"), - String::from("secure-exec-test"), + String::from("agentos-test"), ), ( String::from("AGENTOS_PARENT_NODE_ALLOW_CHILD_PROCESS"), @@ -24998,7 +25001,7 @@ try { ); assert_eq!( filtered.get("AGENTOS_VIRTUAL_OS_HOSTNAME"), - Some(&String::from("secure-exec-test")) + Some(&String::from("agentos-test")) ); assert!(!filtered.contains_key("AGENTOS_PARENT_NODE_ALLOW_CHILD_PROCESS")); assert!(!filtered.contains_key("VISIBLE_MARKER")); diff --git a/crates/native-sidecar/tests/support/mod.rs b/crates/native-sidecar/tests/support/mod.rs index 7e9411410f..46598c06aa 100644 --- a/crates/native-sidecar/tests/support/mod.rs +++ b/crates/native-sidecar/tests/support/mod.rs @@ -387,6 +387,8 @@ pub fn try_collect_process_output_wire_with_timeout( agentos_native_sidecar::wire::EventPayload::ProcessExitedEvent(_) | agentos_native_sidecar::wire::EventPayload::VmLifecycleEvent(_) | agentos_native_sidecar::wire::EventPayload::StructuredEvent(_) + | agentos_native_sidecar::wire::EventPayload::ExecutionOutputEvent(_) + | agentos_native_sidecar::wire::EventPayload::ExecutionCompletedEvent(_) | agentos_native_sidecar::wire::EventPayload::ExtEnvelope(_) => {} } } diff --git a/crates/native-sidecar/tests/xfstests_correctness.rs b/crates/native-sidecar/tests/xfstests_correctness.rs index 175fcdf938..b308b04193 100644 --- a/crates/native-sidecar/tests/xfstests_correctness.rs +++ b/crates/native-sidecar/tests/xfstests_correctness.rs @@ -466,15 +466,11 @@ fn configure_verification_mounts( session_id, vm_id, vec![ - host_dir_mount( - "/__secure_exec/commands/0", - &command_root("coreutils"), - true, - ), - host_dir_mount("/__secure_exec/commands/1", &c_probe_root(), true), - host_dir_mount("/__secure_exec/commands/2", &command_root("attr"), true), - host_dir_mount("/__secure_exec/commands/3", &command_root("acl"), true), - host_dir_mount("/__secure_exec/commands/4", &command_root("sed"), true), + host_dir_mount("/__agentos/commands/0", &command_root("coreutils"), true), + host_dir_mount("/__agentos/commands/1", &c_probe_root(), true), + host_dir_mount("/__agentos/commands/2", &command_root("attr"), true), + host_dir_mount("/__agentos/commands/3", &command_root("acl"), true), + host_dir_mount("/__agentos/commands/4", &command_root("sed"), true), xfstests_backend_mount( backend, "/mnt/test", @@ -3726,7 +3722,7 @@ fn xfstests_wasi_fifo_open_routes_to_kernel_pipe() { let vm_id = create_xfstests_vm_wire(&mut sidecar, 3, &connection_id, &session_id, &cwd); let mut mounts = xfstests_mounts(&source, root.path(), XFSTESTS_BACKEND, None); mounts.push(host_dir_mount( - &format!("/__secure_exec/commands/{}", COMMAND_PACKAGES.len()), + &format!("/__agentos/commands/{}", COMMAND_PACKAGES.len()), &c_probe_root(), true, )); @@ -3855,7 +3851,7 @@ fn xfstests_mknod_creates_a_working_null_device() { let vm_id = create_xfstests_vm_wire(&mut sidecar, 3, &connection_id, &session_id, &cwd); let mut mounts = xfstests_mounts(&source, root.path(), XFSTESTS_BACKEND, None); mounts.push(host_dir_mount( - &format!("/__secure_exec/commands/{}", COMMAND_PACKAGES.len()), + &format!("/__agentos/commands/{}", COMMAND_PACKAGES.len()), &c_probe_root(), true, )); @@ -4500,7 +4496,7 @@ fn xfstests_mounts( .enumerate() .map(|(index, package)| { host_dir_mount( - &format!("/__secure_exec/commands/{index}"), + &format!("/__agentos/commands/{index}"), &command_root(package), true, ) @@ -4887,10 +4883,7 @@ printf 'parent-commands-ok\\n'" &connection_id, &session_id, &vm_id, - filesystem_request( - GuestFilesystemOperation::Exists, - "/__secure_exec/commands/2/rm", - ), + filesystem_request(GuestFilesystemOperation::Exists, "/__agentos/commands/2/rm"), ) .exists; let (fresh_stdout, fresh_stderr, fresh_exit_code) = execute_command( @@ -5048,7 +5041,7 @@ fn xfstests_wasi_waitpid_options_and_status() { let vm_id = create_xfstests_vm_wire(&mut sidecar, 3, &connection_id, &session_id, &cwd); let mut mounts = xfstests_mounts(&source, root.path(), XFSTESTS_BACKEND, None); mounts.push(host_dir_mount( - &format!("/__secure_exec/commands/{}", COMMAND_PACKAGES.len()), + &format!("/__agentos/commands/{}", COMMAND_PACKAGES.len()), &c_probe_root(), true, )); @@ -5107,7 +5100,7 @@ fn xfstests_wasi_self_stop_and_parent_continue() { let vm_id = create_xfstests_vm_wire(&mut sidecar, 3, &connection_id, &session_id, &cwd); let mut mounts = xfstests_mounts(&source, root.path(), XFSTESTS_BACKEND, None); mounts.push(host_dir_mount( - &format!("/__secure_exec/commands/{}", COMMAND_PACKAGES.len()), + &format!("/__agentos/commands/{}", COMMAND_PACKAGES.len()), &c_probe_root(), true, )); @@ -5422,7 +5415,7 @@ fn xfstests_truncfile_scaled_throughput_regression() { let vm_id = create_xfstests_vm_wire(&mut sidecar, 3, &connection_id, &session_id, &cwd); let mut mounts = xfstests_mounts(&source, root.path(), &backend, s3_endpoint); mounts.push(host_dir_mount( - &format!("/__secure_exec/commands/{}", COMMAND_PACKAGES.len()), + &format!("/__agentos/commands/{}", COMMAND_PACKAGES.len()), &source.join("src"), true, )); @@ -5507,7 +5500,7 @@ fn xfstests_wasi_helper_ports() { let vm_id = create_xfstests_vm_wire(&mut sidecar, 3, &connection_id, &session_id, &cwd); let mut mounts = xfstests_mounts(&source, root.path(), XFSTESTS_BACKEND, None); mounts.push(host_dir_mount( - &format!("/__secure_exec/commands/{}", COMMAND_PACKAGES.len()), + &format!("/__agentos/commands/{}", COMMAND_PACKAGES.len()), &c_probe_root(), true, )); diff --git a/crates/sidecar-client/CLAUDE.md b/crates/sidecar-client/CLAUDE.md index 037e9b94a2..402e15f2e9 100644 --- a/crates/sidecar-client/CLAUDE.md +++ b/crates/sidecar-client/CLAUDE.md @@ -4,4 +4,4 @@ See `../CLAUDE.md` for crate-wide runtime and testing rules. - Keep this crate Agent OS-agnostic: no `agentos-protocol`, `agentos-client`, `agentos-sidecar`, ACP, agents, sessions, or binding semantics. - The generic transport resolves `AGENTOS_SIDECAR_BIN` / `agentos-native-sidecar`; product wrappers such as Agent OS must resolve their own wrapper binary and pass it explicitly. -- Expose raw secure-exec wire types and transport primitives only; ergonomic product facades belong in product-specific client crates. +- Expose raw agentos wire types and transport primitives only; ergonomic product facades belong in product-specific client crates. diff --git a/crates/sidecar-client/Cargo.toml b/crates/sidecar-client/Cargo.toml index 9f7e17a230..7b79cfffcf 100644 --- a/crates/sidecar-client/Cargo.toml +++ b/crates/sidecar-client/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -description = "Rust client transport for the Secure Exec native sidecar" +description = "Rust client transport for the AgentOS language execution native sidecar" [dependencies] agentos-sidecar-protocol = { workspace = true } diff --git a/crates/sidecar-client/src/lib.rs b/crates/sidecar-client/src/lib.rs index 4da4ab71fe..eb1d3347ac 100644 --- a/crates/sidecar-client/src/lib.rs +++ b/crates/sidecar-client/src/lib.rs @@ -1,8 +1,8 @@ #![forbid(unsafe_code)] -//! Low-level Rust client transport for the Secure Exec native sidecar. +//! Low-level Rust client transport for the AgentOS language execution native sidecar. //! -//! This crate owns the framed stdio transport and exposes the generated Secure Exec wire protocol. +//! This crate owns the framed stdio transport and exposes the generated AgentOS language execution wire protocol. //! Higher level products layer their own authentication, extension payloads, and //! typed API surfaces on top of this transport. diff --git a/crates/sidecar-client/src/transport.rs b/crates/sidecar-client/src/transport.rs index b2ee2c715a..9f248181bc 100644 --- a/crates/sidecar-client/src/transport.rs +++ b/crates/sidecar-client/src/transport.rs @@ -660,7 +660,7 @@ mod tests { } #[test] - fn binary_path_uses_secure_exec_env_fallback() { + fn binary_path_uses_agentos_env_fallback() { let _guard = ENV_LOCK.lock().expect("env lock"); let previous = std::env::var(SIDECAR_BIN_ENV).ok(); std::env::set_var(SIDECAR_BIN_ENV, "/tmp/agentos-native-sidecar"); diff --git a/crates/sidecar-client/src/wire.rs b/crates/sidecar-client/src/wire.rs index 73505b95ad..3538444bd3 100644 --- a/crates/sidecar-client/src/wire.rs +++ b/crates/sidecar-client/src/wire.rs @@ -1,3 +1,3 @@ -//! Raw generated Secure Exec sidecar protocol types and BARE frame helpers. +//! Raw generated AgentOS language execution sidecar protocol types and BARE frame helpers. pub use agentos_sidecar_protocol::wire::*; diff --git a/crates/sidecar-protocol/Cargo.toml b/crates/sidecar-protocol/Cargo.toml index 3bb07d4dca..a8fbe8c1b0 100644 --- a/crates/sidecar-protocol/Cargo.toml +++ b/crates/sidecar-protocol/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -description = "Shared Secure Exec sidecar wire protocol and frame helpers" +description = "Shared AgentOS language execution sidecar wire protocol and frame helpers" [lib] name = "agentos_sidecar_protocol" diff --git a/crates/sidecar-protocol/protocol/agentos_sidecar_v1.bare b/crates/sidecar-protocol/protocol/agentos_sidecar_v1.bare index 315ecd9ad1..0c145040b1 100644 --- a/crates/sidecar-protocol/protocol/agentos_sidecar_v1.bare +++ b/crates/sidecar-protocol/protocol/agentos_sidecar_v1.bare @@ -1,4 +1,4 @@ -# Secure Exec sidecar protocol schema, version 1. +# AgentOS language execution sidecar protocol schema, version 1. # This schema is generator-ready: numeric ordinals are intentionally omitted # and type blocks are ordered before first use. @@ -384,6 +384,246 @@ type ExecuteRequest struct { wasmPermissionTier: optional } +# First-class execution lifecycle. The legacy process request above remains an +# internal kernel transport while clients migrate in lockstep to these semantic +# operations; language/package command construction belongs to the sidecar. +type ExecutionState enum { + CREATING + IDLE + RUNNING + RESETTING + DELETING + FAILED +} + +type ExecutionOutcome enum { + SUCCEEDED + FAILED + CANCELLED + TIMED_OUT +} + +type RetainedExecutionLanguage enum { + JAVA_SCRIPT + PYTHON +} + +type ExecutionStreamChannel enum { + STDOUT + STDERR + PTY +} + +type JavaScriptModuleFormat enum { + MODULE + COMMON_JS +} + +type ExecutionIdentityOptions struct { + executionId: optional + createIfMissing: optional +} + +type ExecutionPtyOptions struct { + cols: optional + rows: optional +} + +type ProcessExecutionOptions struct { + identity: ExecutionIdentityOptions + detached: optional + cwd: optional + env: optional> + args: list + stdin: optional + timeoutMs: optional + pty: optional +} + +type ShellExecutionRequest struct { + process: ProcessExecutionOptions + command: str +} + +type ArgvExecutionRequest struct { + process: ProcessExecutionOptions + command: str +} + +type JavaScriptExecutionRequest struct { + process: ProcessExecutionOptions + source: str + format: optional + filePath: optional + inputs: optional +} + +type JavaScriptEvaluationRequest struct { + process: ProcessExecutionOptions + expression: str + format: optional + filePath: optional + inputs: optional +} + +type JavaScriptFileExecutionRequest struct { + process: ProcessExecutionOptions + path: str +} + +type TypeScriptExecutionRequest struct { + process: ProcessExecutionOptions + source: str + filePath: optional + tsconfigPath: optional + compilerOptions: optional + inputs: optional +} + +type TypeScriptEvaluationRequest struct { + process: ProcessExecutionOptions + expression: str + filePath: optional + tsconfigPath: optional + compilerOptions: optional + inputs: optional +} + +type TypeScriptFileExecutionRequest struct { + process: ProcessExecutionOptions + path: str + tsconfigPath: optional + compilerOptions: optional +} + +type TypeScriptCheckRequest struct { + identity: ExecutionIdentityOptions + source: str + cwd: optional + filePath: optional + tsconfigPath: optional + compilerOptions: optional + timeoutMs: optional +} + +type TypeScriptProjectCheckRequest struct { + identity: ExecutionIdentityOptions + cwd: optional + tsconfigPath: optional + timeoutMs: optional +} + +type NpmProjectInstallRequest struct { + identity: ExecutionIdentityOptions + cwd: optional + env: optional> + timeoutMs: optional + frozen: optional +} + +type NpmPackageInstallRequest struct { + identity: ExecutionIdentityOptions + cwd: optional + env: optional> + timeoutMs: optional + packages: list + dev: optional + global: optional +} + +type NpmScriptExecutionRequest struct { + process: ProcessExecutionOptions + script: str +} + +type NpmPackageExecutionRequest struct { + process: ProcessExecutionOptions + packageSpec: str + binary: optional +} + +type PythonExecutionRequest struct { + process: ProcessExecutionOptions + source: str + inputs: optional +} + +type PythonEvaluationRequest struct { + process: ProcessExecutionOptions + expression: str + inputs: optional +} + +type PythonFileExecutionRequest struct { + process: ProcessExecutionOptions + path: str +} + +type PythonModuleExecutionRequest struct { + process: ProcessExecutionOptions + module: str +} + +type PythonInstallRequest struct { + identity: ExecutionIdentityOptions + cwd: optional + env: optional> + timeoutMs: optional + packages: list + upgrade: optional + requirementsFile: optional + indexUrl: optional + extraIndexUrls: list +} + +type GetExecutionRequest struct { + executionId: str +} + +type ListExecutionsRequest void + +type WaitExecutionRequest struct { + executionId: str +} + +type CancelExecutionRequest struct { + executionId: str +} + +type SignalExecutionRequest struct { + executionId: str + signal: str +} + +type ResetExecutionRequest struct { + executionId: str +} + +type DeleteExecutionRequest struct { + executionId: str +} + +type WriteExecutionStdinRequest struct { + executionId: str + chunk: data +} + +type CloseExecutionStdinRequest struct { + executionId: str +} + +type ResizeExecutionPtyRequest struct { + executionId: str + cols: u16 + rows: u16 +} + +type ReadExecutionOutputRequest struct { + executionId: str + cursor: optional + limit: optional +} + type WriteStdinRequest struct { processId: str chunk: data @@ -492,7 +732,37 @@ type RequestPayload union { GetResourceSnapshotRequest | LinkPackageRequest | ProvidedCommandsRequest | - ListMountsRequest + ListMountsRequest | + ShellExecutionRequest | + ArgvExecutionRequest | + JavaScriptExecutionRequest | + JavaScriptEvaluationRequest | + JavaScriptFileExecutionRequest | + TypeScriptExecutionRequest | + TypeScriptEvaluationRequest | + TypeScriptFileExecutionRequest | + TypeScriptCheckRequest | + TypeScriptProjectCheckRequest | + NpmProjectInstallRequest | + NpmPackageInstallRequest | + NpmScriptExecutionRequest | + NpmPackageExecutionRequest | + PythonExecutionRequest | + PythonEvaluationRequest | + PythonFileExecutionRequest | + PythonModuleExecutionRequest | + PythonInstallRequest | + GetExecutionRequest | + ListExecutionsRequest | + WaitExecutionRequest | + CancelExecutionRequest | + SignalExecutionRequest | + ResetExecutionRequest | + DeleteExecutionRequest | + WriteExecutionStdinRequest | + CloseExecutionStdinRequest | + ResizeExecutionPtyRequest | + ReadExecutionOutputRequest } type RequestFrame struct { @@ -762,6 +1032,100 @@ type VmFetchResponse struct { responseJson: str } +type ExecutionDescriptor struct { + executionId: str + generation: u64 + state: ExecutionState + retainedLanguage: optional + processId: optional + pid: optional + createdAtMs: u64 + lastStartedAtMs: optional + lastCompletedAtMs: optional + lastOutcome: optional + lastExitCode: optional +} + +type ExecutionErrorData struct { + code: str + name: str + message: str + stack: optional + details: optional +} + +type ExecutionAcceptedResponse struct { + execution: ExecutionDescriptor +} + +type ExecutionCompletedResponse struct { + execution: ExecutionDescriptor + outcome: ExecutionOutcome + exitCode: optional + error: optional + stdout: data + stderr: data + stdoutTruncated: bool + stderrTruncated: bool + outputs: JsonUtf8 +} + +type ExecutionEvaluationResponse struct { + result: ExecutionCompletedResponse + value: optional +} + +type TypeScriptDiagnostic struct { + code: u32 + category: str + message: str + filePath: optional + line: optional + column: optional +} + +type TypeScriptCheckResponse struct { + result: ExecutionCompletedResponse + hasErrors: optional + diagnostics: list +} + +type ExecutionDescriptorResponse struct { + execution: ExecutionDescriptor +} + +type ExecutionListResponse struct { + executions: list +} + +type ExecutionDeletedResponse struct { + executionId: str +} + +type ExecutionIoResponse struct { + executionId: str + acceptedBytes: optional +} + +type ExecutionOutputEvent struct { + executionId: str + generation: u64 + processId: optional + sequence: u64 + channel: ExecutionStreamChannel + chunk: data + timestampMs: u64 +} + +type ExecutionOutputPageResponse struct { + executionId: str + generation: u64 + events: list + nextCursor: str + hasMore: bool + truncated: bool +} + type ResponsePayload union { AuthenticatedResponse | SessionOpenedResponse | @@ -798,7 +1162,16 @@ type ResponsePayload union { ResourceSnapshotResponse | PackageLinkedResponse | ProvidedCommandsResponse | - ListMountsResponse + ListMountsResponse | + ExecutionAcceptedResponse | + ExecutionCompletedResponse | + ExecutionEvaluationResponse | + TypeScriptCheckResponse | + ExecutionDescriptorResponse | + ExecutionListResponse | + ExecutionDeletedResponse | + ExecutionIoResponse | + ExecutionOutputPageResponse } type ResponseFrame struct { @@ -836,6 +1209,14 @@ type ProcessExitedEvent struct { exitCode: i32 } +type ExecutionCompletedEvent struct { + executionId: str + generation: u64 + outcome: ExecutionOutcome + exitCode: optional + error: optional +} + type StructuredEvent struct { name: str detail: map @@ -846,7 +1227,9 @@ type EventPayload union { ProcessOutputEvent | ProcessExitedEvent | StructuredEvent | - ExtEnvelope + ExtEnvelope | + ExecutionOutputEvent | + ExecutionCompletedEvent } type EventFrame struct { diff --git a/crates/sidecar-protocol/src/lib.rs b/crates/sidecar-protocol/src/lib.rs index 7929ac0511..20d162ba5a 100644 --- a/crates/sidecar-protocol/src/lib.rs +++ b/crates/sidecar-protocol/src/lib.rs @@ -3,7 +3,7 @@ // payload variants); boxing them is a wire-adjacent refactor tracked separately. #![allow(clippy::large_enum_variant, clippy::result_large_err)] -//! Shared Secure Exec sidecar wire protocol surface. +//! Shared AgentOS language execution sidecar wire protocol surface. pub mod generated_protocol; pub mod protocol; diff --git a/crates/sidecar-protocol/src/protocol.rs b/crates/sidecar-protocol/src/protocol.rs index 91518f4f3b..beb0cc6987 100644 --- a/crates/sidecar-protocol/src/protocol.rs +++ b/crates/sidecar-protocol/src/protocol.rs @@ -505,6 +505,96 @@ fn to_generated_request_payload( RequestPayload::ProvidedCommands(_) => { generated_protocol::RequestPayload::ProvidedCommandsRequest } + RequestPayload::ShellExecution(inner) => { + generated_protocol::RequestPayload::ShellExecutionRequest(inner.clone()) + } + RequestPayload::ArgvExecution(inner) => { + generated_protocol::RequestPayload::ArgvExecutionRequest(inner.clone()) + } + RequestPayload::JavaScriptExecution(inner) => { + generated_protocol::RequestPayload::JavaScriptExecutionRequest(inner.clone()) + } + RequestPayload::JavaScriptEvaluation(inner) => { + generated_protocol::RequestPayload::JavaScriptEvaluationRequest(inner.clone()) + } + RequestPayload::JavaScriptFileExecution(inner) => { + generated_protocol::RequestPayload::JavaScriptFileExecutionRequest(inner.clone()) + } + RequestPayload::TypeScriptExecution(inner) => { + generated_protocol::RequestPayload::TypeScriptExecutionRequest(inner.clone()) + } + RequestPayload::TypeScriptEvaluation(inner) => { + generated_protocol::RequestPayload::TypeScriptEvaluationRequest(inner.clone()) + } + RequestPayload::TypeScriptFileExecution(inner) => { + generated_protocol::RequestPayload::TypeScriptFileExecutionRequest(inner.clone()) + } + RequestPayload::TypeScriptCheck(inner) => { + generated_protocol::RequestPayload::TypeScriptCheckRequest(inner.clone()) + } + RequestPayload::TypeScriptProjectCheck(inner) => { + generated_protocol::RequestPayload::TypeScriptProjectCheckRequest(inner.clone()) + } + RequestPayload::NpmProjectInstall(inner) => { + generated_protocol::RequestPayload::NpmProjectInstallRequest(inner.clone()) + } + RequestPayload::NpmPackageInstall(inner) => { + generated_protocol::RequestPayload::NpmPackageInstallRequest(inner.clone()) + } + RequestPayload::NpmScriptExecution(inner) => { + generated_protocol::RequestPayload::NpmScriptExecutionRequest(inner.clone()) + } + RequestPayload::NpmPackageExecution(inner) => { + generated_protocol::RequestPayload::NpmPackageExecutionRequest(inner.clone()) + } + RequestPayload::PythonExecution(inner) => { + generated_protocol::RequestPayload::PythonExecutionRequest(inner.clone()) + } + RequestPayload::PythonEvaluation(inner) => { + generated_protocol::RequestPayload::PythonEvaluationRequest(inner.clone()) + } + RequestPayload::PythonFileExecution(inner) => { + generated_protocol::RequestPayload::PythonFileExecutionRequest(inner.clone()) + } + RequestPayload::PythonModuleExecution(inner) => { + generated_protocol::RequestPayload::PythonModuleExecutionRequest(inner.clone()) + } + RequestPayload::PythonInstall(inner) => { + generated_protocol::RequestPayload::PythonInstallRequest(inner.clone()) + } + RequestPayload::GetExecution(inner) => { + generated_protocol::RequestPayload::GetExecutionRequest(inner.clone()) + } + RequestPayload::ListExecutions(_) => { + generated_protocol::RequestPayload::ListExecutionsRequest + } + RequestPayload::WaitExecution(inner) => { + generated_protocol::RequestPayload::WaitExecutionRequest(inner.clone()) + } + RequestPayload::CancelExecution(inner) => { + generated_protocol::RequestPayload::CancelExecutionRequest(inner.clone()) + } + RequestPayload::SignalExecution(inner) => { + generated_protocol::RequestPayload::SignalExecutionRequest(inner.clone()) + } + RequestPayload::ResetExecution(inner) => { + generated_protocol::RequestPayload::ResetExecutionRequest(inner.clone()) + } + RequestPayload::DeleteExecution(inner) => { + generated_protocol::RequestPayload::DeleteExecutionRequest(inner.clone()) + } + RequestPayload::WriteExecutionStdin(inner) => { + generated_protocol::RequestPayload::WriteExecutionStdinRequest(inner.clone()) + } + RequestPayload::CloseExecutionStdin(inner) => { + generated_protocol::RequestPayload::CloseExecutionStdinRequest(inner.clone()) + } + RequestPayload::ResizeExecutionPty(inner) => { + generated_protocol::RequestPayload::ResizeExecutionPtyRequest(inner.clone()) + } + RequestPayload::ReadExecutionOutput(inner) => { + generated_protocol::RequestPayload::ReadExecutionOutputRequest(inner.clone()) + } }) } @@ -616,6 +706,96 @@ fn from_generated_request_payload( generated_protocol::RequestPayload::ProvidedCommandsRequest => { RequestPayload::ProvidedCommands(ProvidedCommandsRequest {}) } + generated_protocol::RequestPayload::ShellExecutionRequest(inner) => { + RequestPayload::ShellExecution(inner) + } + generated_protocol::RequestPayload::ArgvExecutionRequest(inner) => { + RequestPayload::ArgvExecution(inner) + } + generated_protocol::RequestPayload::JavaScriptExecutionRequest(inner) => { + RequestPayload::JavaScriptExecution(inner) + } + generated_protocol::RequestPayload::JavaScriptEvaluationRequest(inner) => { + RequestPayload::JavaScriptEvaluation(inner) + } + generated_protocol::RequestPayload::JavaScriptFileExecutionRequest(inner) => { + RequestPayload::JavaScriptFileExecution(inner) + } + generated_protocol::RequestPayload::TypeScriptExecutionRequest(inner) => { + RequestPayload::TypeScriptExecution(inner) + } + generated_protocol::RequestPayload::TypeScriptEvaluationRequest(inner) => { + RequestPayload::TypeScriptEvaluation(inner) + } + generated_protocol::RequestPayload::TypeScriptFileExecutionRequest(inner) => { + RequestPayload::TypeScriptFileExecution(inner) + } + generated_protocol::RequestPayload::TypeScriptCheckRequest(inner) => { + RequestPayload::TypeScriptCheck(inner) + } + generated_protocol::RequestPayload::TypeScriptProjectCheckRequest(inner) => { + RequestPayload::TypeScriptProjectCheck(inner) + } + generated_protocol::RequestPayload::NpmProjectInstallRequest(inner) => { + RequestPayload::NpmProjectInstall(inner) + } + generated_protocol::RequestPayload::NpmPackageInstallRequest(inner) => { + RequestPayload::NpmPackageInstall(inner) + } + generated_protocol::RequestPayload::NpmScriptExecutionRequest(inner) => { + RequestPayload::NpmScriptExecution(inner) + } + generated_protocol::RequestPayload::NpmPackageExecutionRequest(inner) => { + RequestPayload::NpmPackageExecution(inner) + } + generated_protocol::RequestPayload::PythonExecutionRequest(inner) => { + RequestPayload::PythonExecution(inner) + } + generated_protocol::RequestPayload::PythonEvaluationRequest(inner) => { + RequestPayload::PythonEvaluation(inner) + } + generated_protocol::RequestPayload::PythonFileExecutionRequest(inner) => { + RequestPayload::PythonFileExecution(inner) + } + generated_protocol::RequestPayload::PythonModuleExecutionRequest(inner) => { + RequestPayload::PythonModuleExecution(inner) + } + generated_protocol::RequestPayload::PythonInstallRequest(inner) => { + RequestPayload::PythonInstall(inner) + } + generated_protocol::RequestPayload::GetExecutionRequest(inner) => { + RequestPayload::GetExecution(inner) + } + generated_protocol::RequestPayload::ListExecutionsRequest => { + RequestPayload::ListExecutions(ListExecutionsRequest {}) + } + generated_protocol::RequestPayload::WaitExecutionRequest(inner) => { + RequestPayload::WaitExecution(inner) + } + generated_protocol::RequestPayload::CancelExecutionRequest(inner) => { + RequestPayload::CancelExecution(inner) + } + generated_protocol::RequestPayload::SignalExecutionRequest(inner) => { + RequestPayload::SignalExecution(inner) + } + generated_protocol::RequestPayload::ResetExecutionRequest(inner) => { + RequestPayload::ResetExecution(inner) + } + generated_protocol::RequestPayload::DeleteExecutionRequest(inner) => { + RequestPayload::DeleteExecution(inner) + } + generated_protocol::RequestPayload::WriteExecutionStdinRequest(inner) => { + RequestPayload::WriteExecutionStdin(inner) + } + generated_protocol::RequestPayload::CloseExecutionStdinRequest(inner) => { + RequestPayload::CloseExecutionStdin(inner) + } + generated_protocol::RequestPayload::ResizeExecutionPtyRequest(inner) => { + RequestPayload::ResizeExecutionPty(inner) + } + generated_protocol::RequestPayload::ReadExecutionOutputRequest(inner) => { + RequestPayload::ReadExecutionOutput(inner) + } }) } @@ -773,6 +953,33 @@ fn to_generated_response_payload( ResponsePayload::ProvidedCommands(inner) => { generated_protocol::ResponsePayload::ProvidedCommandsResponse(inner.clone()) } + ResponsePayload::ExecutionAccepted(inner) => { + generated_protocol::ResponsePayload::ExecutionAcceptedResponse(inner.clone()) + } + ResponsePayload::ExecutionCompleted(inner) => { + generated_protocol::ResponsePayload::ExecutionCompletedResponse(inner.clone()) + } + ResponsePayload::ExecutionEvaluation(inner) => { + generated_protocol::ResponsePayload::ExecutionEvaluationResponse(inner.clone()) + } + ResponsePayload::TypeScriptCheck(inner) => { + generated_protocol::ResponsePayload::TypeScriptCheckResponse(inner.clone()) + } + ResponsePayload::ExecutionDescriptor(inner) => { + generated_protocol::ResponsePayload::ExecutionDescriptorResponse(inner.clone()) + } + ResponsePayload::ExecutionList(inner) => { + generated_protocol::ResponsePayload::ExecutionListResponse(inner.clone()) + } + ResponsePayload::ExecutionDeleted(inner) => { + generated_protocol::ResponsePayload::ExecutionDeletedResponse(inner.clone()) + } + ResponsePayload::ExecutionIo(inner) => { + generated_protocol::ResponsePayload::ExecutionIoResponse(inner.clone()) + } + ResponsePayload::ExecutionOutputPage(inner) => { + generated_protocol::ResponsePayload::ExecutionOutputPageResponse(inner.clone()) + } }) } @@ -917,6 +1124,33 @@ fn from_generated_response_payload( generated_protocol::ResponsePayload::ProvidedCommandsResponse(inner) => { ResponsePayload::ProvidedCommands(inner) } + generated_protocol::ResponsePayload::ExecutionAcceptedResponse(inner) => { + ResponsePayload::ExecutionAccepted(inner) + } + generated_protocol::ResponsePayload::ExecutionCompletedResponse(inner) => { + ResponsePayload::ExecutionCompleted(inner) + } + generated_protocol::ResponsePayload::ExecutionEvaluationResponse(inner) => { + ResponsePayload::ExecutionEvaluation(inner) + } + generated_protocol::ResponsePayload::TypeScriptCheckResponse(inner) => { + ResponsePayload::TypeScriptCheck(inner) + } + generated_protocol::ResponsePayload::ExecutionDescriptorResponse(inner) => { + ResponsePayload::ExecutionDescriptor(inner) + } + generated_protocol::ResponsePayload::ExecutionListResponse(inner) => { + ResponsePayload::ExecutionList(inner) + } + generated_protocol::ResponsePayload::ExecutionDeletedResponse(inner) => { + ResponsePayload::ExecutionDeleted(inner) + } + generated_protocol::ResponsePayload::ExecutionIoResponse(inner) => { + ResponsePayload::ExecutionIo(inner) + } + generated_protocol::ResponsePayload::ExecutionOutputPageResponse(inner) => { + ResponsePayload::ExecutionOutputPage(inner) + } }) } @@ -943,6 +1177,12 @@ fn to_generated_event_payload(payload: &EventPayload) -> generated_protocol::Eve EventPayload::Ext(inner) => { generated_protocol::EventPayload::ExtEnvelope(to_generated_ext_envelope(inner)) } + EventPayload::ExecutionOutput(inner) => { + generated_protocol::EventPayload::ExecutionOutputEvent(inner.clone()) + } + EventPayload::ExecutionCompleted(inner) => { + generated_protocol::EventPayload::ExecutionCompletedEvent(inner.clone()) + } } } @@ -967,6 +1207,12 @@ fn from_generated_event_payload(payload: generated_protocol::EventPayload) -> Ev generated_protocol::EventPayload::ExtEnvelope(inner) => { EventPayload::Ext(from_generated_ext_envelope(inner)) } + generated_protocol::EventPayload::ExecutionOutputEvent(inner) => { + EventPayload::ExecutionOutput(inner) + } + generated_protocol::EventPayload::ExecutionCompletedEvent(inner) => { + EventPayload::ExecutionCompleted(inner) + } } } @@ -1290,6 +1536,36 @@ pub enum RequestPayload { GetResourceSnapshot(GetResourceSnapshotRequest), LinkPackage(LinkPackageRequest), ProvidedCommands(ProvidedCommandsRequest), + ShellExecution(ShellExecutionRequest), + ArgvExecution(ArgvExecutionRequest), + JavaScriptExecution(JavaScriptExecutionRequest), + JavaScriptEvaluation(JavaScriptEvaluationRequest), + JavaScriptFileExecution(JavaScriptFileExecutionRequest), + TypeScriptExecution(TypeScriptExecutionRequest), + TypeScriptEvaluation(TypeScriptEvaluationRequest), + TypeScriptFileExecution(TypeScriptFileExecutionRequest), + TypeScriptCheck(TypeScriptCheckRequest), + TypeScriptProjectCheck(TypeScriptProjectCheckRequest), + NpmProjectInstall(NpmProjectInstallRequest), + NpmPackageInstall(NpmPackageInstallRequest), + NpmScriptExecution(NpmScriptExecutionRequest), + NpmPackageExecution(NpmPackageExecutionRequest), + PythonExecution(PythonExecutionRequest), + PythonEvaluation(PythonEvaluationRequest), + PythonFileExecution(PythonFileExecutionRequest), + PythonModuleExecution(PythonModuleExecutionRequest), + PythonInstall(PythonInstallRequest), + GetExecution(GetExecutionRequest), + ListExecutions(ListExecutionsRequest), + WaitExecution(WaitExecutionRequest), + CancelExecution(CancelExecutionRequest), + SignalExecution(SignalExecutionRequest), + ResetExecution(ResetExecutionRequest), + DeleteExecution(DeleteExecutionRequest), + WriteExecutionStdin(WriteExecutionStdinRequest), + CloseExecutionStdin(CloseExecutionStdinRequest), + ResizeExecutionPty(ResizeExecutionPtyRequest), + ReadExecutionOutput(ReadExecutionOutputRequest), } #[derive(Debug, Clone, PartialEq, Eq)] @@ -1330,6 +1606,15 @@ pub enum ResponsePayload { ResourceSnapshot(ResourceSnapshotResponse), PackageLinked(PackageLinkedResponse), ProvidedCommands(ProvidedCommandsResponse), + ExecutionAccepted(ExecutionAcceptedResponse), + ExecutionCompleted(ExecutionCompletedResponse), + ExecutionEvaluation(ExecutionEvaluationResponse), + TypeScriptCheck(TypeScriptCheckResponse), + ExecutionDescriptor(ExecutionDescriptorResponse), + ExecutionList(ExecutionListResponse), + ExecutionDeleted(ExecutionDeletedResponse), + ExecutionIo(ExecutionIoResponse), + ExecutionOutputPage(ExecutionOutputPageResponse), } #[derive(Debug, Clone, PartialEq, Eq)] @@ -1353,6 +1638,8 @@ pub enum EventPayload { ProcessExited(ProcessExitedEvent), Structured(StructuredEvent), Ext(ExtEnvelope), + ExecutionOutput(ExecutionOutputEvent), + ExecutionCompleted(ExecutionCompletedEvent), } pub type SidecarPlacement = crate::wire::SidecarPlacement; @@ -1466,6 +1753,46 @@ pub type WasmPermissionTier = crate::wire::WasmPermissionTier; pub type ExecuteRequest = crate::wire::ExecuteRequest; +pub type ExecutionState = crate::wire::ExecutionState; +pub type ExecutionOutcome = crate::wire::ExecutionOutcome; +pub type RetainedExecutionLanguage = crate::wire::RetainedExecutionLanguage; +pub type ExecutionStreamChannel = crate::wire::ExecutionStreamChannel; +pub type JavaScriptModuleFormat = crate::wire::JavaScriptModuleFormat; +pub type ExecutionIdentityOptions = crate::wire::ExecutionIdentityOptions; +pub type ExecutionPtyOptions = crate::wire::ExecutionPtyOptions; +pub type ProcessExecutionOptions = crate::wire::ProcessExecutionOptions; +pub type ShellExecutionRequest = crate::wire::ShellExecutionRequest; +pub type ArgvExecutionRequest = crate::wire::ArgvExecutionRequest; +pub type JavaScriptExecutionRequest = crate::wire::JavaScriptExecutionRequest; +pub type JavaScriptEvaluationRequest = crate::wire::JavaScriptEvaluationRequest; +pub type JavaScriptFileExecutionRequest = crate::wire::JavaScriptFileExecutionRequest; +pub type TypeScriptExecutionRequest = crate::wire::TypeScriptExecutionRequest; +pub type TypeScriptEvaluationRequest = crate::wire::TypeScriptEvaluationRequest; +pub type TypeScriptFileExecutionRequest = crate::wire::TypeScriptFileExecutionRequest; +pub type TypeScriptCheckRequest = crate::wire::TypeScriptCheckRequest; +pub type TypeScriptProjectCheckRequest = crate::wire::TypeScriptProjectCheckRequest; +pub type NpmProjectInstallRequest = crate::wire::NpmProjectInstallRequest; +pub type NpmPackageInstallRequest = crate::wire::NpmPackageInstallRequest; +pub type NpmScriptExecutionRequest = crate::wire::NpmScriptExecutionRequest; +pub type NpmPackageExecutionRequest = crate::wire::NpmPackageExecutionRequest; +pub type PythonExecutionRequest = crate::wire::PythonExecutionRequest; +pub type PythonEvaluationRequest = crate::wire::PythonEvaluationRequest; +pub type PythonFileExecutionRequest = crate::wire::PythonFileExecutionRequest; +pub type PythonModuleExecutionRequest = crate::wire::PythonModuleExecutionRequest; +pub type PythonInstallRequest = crate::wire::PythonInstallRequest; +pub type GetExecutionRequest = crate::wire::GetExecutionRequest; +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct ListExecutionsRequest {} +pub type WaitExecutionRequest = crate::wire::WaitExecutionRequest; +pub type CancelExecutionRequest = crate::wire::CancelExecutionRequest; +pub type SignalExecutionRequest = crate::wire::SignalExecutionRequest; +pub type ResetExecutionRequest = crate::wire::ResetExecutionRequest; +pub type DeleteExecutionRequest = crate::wire::DeleteExecutionRequest; +pub type WriteExecutionStdinRequest = crate::wire::WriteExecutionStdinRequest; +pub type CloseExecutionStdinRequest = crate::wire::CloseExecutionStdinRequest; +pub type ResizeExecutionPtyRequest = crate::wire::ResizeExecutionPtyRequest; +pub type ReadExecutionOutputRequest = crate::wire::ReadExecutionOutputRequest; + pub type WriteStdinRequest = crate::wire::WriteStdinRequest; pub type CloseStdinRequest = crate::wire::CloseStdinRequest; @@ -1564,6 +1891,20 @@ pub type ListenerSnapshotResponse = crate::wire::ListenerSnapshotResponse; pub type BoundUdpSnapshotResponse = crate::wire::BoundUdpSnapshotResponse; pub type VmFetchResponse = crate::wire::VmFetchResponse; +pub type ExecutionDescriptor = crate::wire::ExecutionDescriptor; +pub type ExecutionErrorData = crate::wire::ExecutionErrorData; +pub type ExecutionAcceptedResponse = crate::wire::ExecutionAcceptedResponse; +pub type ExecutionCompletedResponse = crate::wire::ExecutionCompletedResponse; +pub type ExecutionEvaluationResponse = crate::wire::ExecutionEvaluationResponse; +pub type TypeScriptDiagnostic = crate::wire::TypeScriptDiagnostic; +pub type TypeScriptCheckResponse = crate::wire::TypeScriptCheckResponse; +pub type ExecutionDescriptorResponse = crate::wire::ExecutionDescriptorResponse; +pub type ExecutionListResponse = crate::wire::ExecutionListResponse; +pub type ExecutionDeletedResponse = crate::wire::ExecutionDeletedResponse; +pub type ExecutionIoResponse = crate::wire::ExecutionIoResponse; +pub type ExecutionOutputEvent = crate::wire::ExecutionOutputEvent; +pub type ExecutionOutputPageResponse = crate::wire::ExecutionOutputPageResponse; +pub type ExecutionCompletedEvent = crate::wire::ExecutionCompletedEvent; pub type SignalDispositionAction = crate::wire::SignalDispositionAction; @@ -1648,6 +1989,36 @@ impl_bare_newtype_union_enum!( LinkPackage(LinkPackageRequest) = 31, ProvidedCommands(ProvidedCommandsRequest) = 32, ListMounts(ListMountsRequest) = 33, + ShellExecution(ShellExecutionRequest) = 34, + ArgvExecution(ArgvExecutionRequest) = 35, + JavaScriptExecution(JavaScriptExecutionRequest) = 36, + JavaScriptEvaluation(JavaScriptEvaluationRequest) = 37, + JavaScriptFileExecution(JavaScriptFileExecutionRequest) = 38, + TypeScriptExecution(TypeScriptExecutionRequest) = 39, + TypeScriptEvaluation(TypeScriptEvaluationRequest) = 40, + TypeScriptFileExecution(TypeScriptFileExecutionRequest) = 41, + TypeScriptCheck(TypeScriptCheckRequest) = 42, + TypeScriptProjectCheck(TypeScriptProjectCheckRequest) = 43, + NpmProjectInstall(NpmProjectInstallRequest) = 44, + NpmPackageInstall(NpmPackageInstallRequest) = 45, + NpmScriptExecution(NpmScriptExecutionRequest) = 46, + NpmPackageExecution(NpmPackageExecutionRequest) = 47, + PythonExecution(PythonExecutionRequest) = 48, + PythonEvaluation(PythonEvaluationRequest) = 49, + PythonFileExecution(PythonFileExecutionRequest) = 50, + PythonModuleExecution(PythonModuleExecutionRequest) = 51, + PythonInstall(PythonInstallRequest) = 52, + GetExecution(GetExecutionRequest) = 53, + ListExecutions(ListExecutionsRequest) = 54, + WaitExecution(WaitExecutionRequest) = 55, + CancelExecution(CancelExecutionRequest) = 56, + SignalExecution(SignalExecutionRequest) = 57, + ResetExecution(ResetExecutionRequest) = 58, + DeleteExecution(DeleteExecutionRequest) = 59, + WriteExecutionStdin(WriteExecutionStdinRequest) = 60, + CloseExecutionStdin(CloseExecutionStdinRequest) = 61, + ResizeExecutionPty(ResizeExecutionPtyRequest) = 62, + ReadExecutionOutput(ReadExecutionOutputRequest) = 63, } ); @@ -1692,6 +2063,15 @@ impl_bare_newtype_union_enum!( PackageLinked(PackageLinkedResponse) = 33, ProvidedCommands(ProvidedCommandsResponse) = 34, MountsListed(ListMountsResponse) = 35, + ExecutionAccepted(ExecutionAcceptedResponse) = 36, + ExecutionCompleted(ExecutionCompletedResponse) = 37, + ExecutionEvaluation(ExecutionEvaluationResponse) = 38, + TypeScriptCheck(TypeScriptCheckResponse) = 39, + ExecutionDescriptor(ExecutionDescriptorResponse) = 40, + ExecutionList(ExecutionListResponse) = 41, + ExecutionDeleted(ExecutionDeletedResponse) = 42, + ExecutionIo(ExecutionIoResponse) = 43, + ExecutionOutputPage(ExecutionOutputPageResponse) = 44, } ); @@ -1728,6 +2108,8 @@ impl_bare_newtype_union_enum!( ProcessExited(ProcessExitedEvent) = 2, Structured(StructuredEvent) = 3, Ext(ExtEnvelope) = 4, + ExecutionOutput(ExecutionOutputEvent) = 5, + ExecutionCompleted(ExecutionCompletedEvent) = 6, } ); @@ -2274,6 +2656,15 @@ enum ExpectedResponseKind { PackageLinked, ProvidedCommands, MountsListed, + ExecutionOperation, + ExecutionCompleted, + ExecutionEvaluation, + TypeScriptCheck, + ExecutionDescriptor, + ExecutionList, + ExecutionDeleted, + ExecutionIo, + ExecutionOutputPage, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -2321,12 +2712,26 @@ impl ExpectedResponseKind { Self::PackageLinked => "package_linked", Self::ProvidedCommands => "provided_commands_response", Self::MountsListed => "mounts_listed", + Self::ExecutionOperation => "execution_operation", + Self::ExecutionCompleted => "execution_completed", + Self::ExecutionEvaluation => "execution_evaluation", + Self::TypeScriptCheck => "type_script_check", + Self::ExecutionDescriptor => "execution_descriptor", + Self::ExecutionList => "execution_list", + Self::ExecutionDeleted => "execution_deleted", + Self::ExecutionIo => "execution_io", + Self::ExecutionOutputPage => "execution_output_page", } } fn matches(self, payload: &ResponsePayload) -> bool { match payload { ResponsePayload::Rejected(_) => true, + ResponsePayload::ExecutionAccepted(_) | ResponsePayload::ExecutionCompleted(_) + if self == Self::ExecutionOperation => + { + true + } _ => payload.kind_name() == self.as_str(), } } @@ -2380,6 +2785,36 @@ impl RequestPayload { | Self::ResizePty(_) | Self::LinkPackage(_) | Self::ProvidedCommands(_) + | Self::ShellExecution(_) + | Self::ArgvExecution(_) + | Self::JavaScriptExecution(_) + | Self::JavaScriptEvaluation(_) + | Self::JavaScriptFileExecution(_) + | Self::TypeScriptExecution(_) + | Self::TypeScriptEvaluation(_) + | Self::TypeScriptFileExecution(_) + | Self::TypeScriptCheck(_) + | Self::TypeScriptProjectCheck(_) + | Self::NpmProjectInstall(_) + | Self::NpmPackageInstall(_) + | Self::NpmScriptExecution(_) + | Self::NpmPackageExecution(_) + | Self::PythonExecution(_) + | Self::PythonEvaluation(_) + | Self::PythonFileExecution(_) + | Self::PythonModuleExecution(_) + | Self::PythonInstall(_) + | Self::GetExecution(_) + | Self::ListExecutions(_) + | Self::WaitExecution(_) + | Self::CancelExecution(_) + | Self::SignalExecution(_) + | Self::ResetExecution(_) + | Self::DeleteExecution(_) + | Self::WriteExecutionStdin(_) + | Self::CloseExecutionStdin(_) + | Self::ResizeExecutionPty(_) + | Self::ReadExecutionOutput(_) | Self::HostFilesystemCall(_) => OwnershipRequirement::Vm, Self::Ext(_) => OwnershipRequirement::Any, } @@ -2421,6 +2856,37 @@ impl RequestPayload { Self::ResizePty(_) => ExpectedResponseKind::PtyResized, Self::LinkPackage(_) => ExpectedResponseKind::PackageLinked, Self::ProvidedCommands(_) => ExpectedResponseKind::ProvidedCommands, + Self::ShellExecution(_) + | Self::ArgvExecution(_) + | Self::JavaScriptExecution(_) + | Self::JavaScriptFileExecution(_) + | Self::TypeScriptExecution(_) + | Self::TypeScriptFileExecution(_) + | Self::NpmProjectInstall(_) + | Self::NpmPackageInstall(_) + | Self::NpmScriptExecution(_) + | Self::NpmPackageExecution(_) + | Self::PythonExecution(_) + | Self::PythonFileExecution(_) + | Self::PythonModuleExecution(_) + | Self::PythonInstall(_) => ExpectedResponseKind::ExecutionOperation, + Self::JavaScriptEvaluation(_) + | Self::TypeScriptEvaluation(_) + | Self::PythonEvaluation(_) => ExpectedResponseKind::ExecutionEvaluation, + Self::TypeScriptCheck(_) | Self::TypeScriptProjectCheck(_) => { + ExpectedResponseKind::TypeScriptCheck + } + Self::GetExecution(_) + | Self::CancelExecution(_) + | Self::SignalExecution(_) + | Self::ResetExecution(_) => ExpectedResponseKind::ExecutionDescriptor, + Self::ListExecutions(_) => ExpectedResponseKind::ExecutionList, + Self::WaitExecution(_) => ExpectedResponseKind::ExecutionCompleted, + Self::DeleteExecution(_) => ExpectedResponseKind::ExecutionDeleted, + Self::WriteExecutionStdin(_) + | Self::CloseExecutionStdin(_) + | Self::ResizeExecutionPty(_) => ExpectedResponseKind::ExecutionIo, + Self::ReadExecutionOutput(_) => ExpectedResponseKind::ExecutionOutputPage, } } } @@ -2475,7 +2941,16 @@ impl ResponsePayload { | Self::GuestKernelResult(_) | Self::PtyResized(_) | Self::PackageLinked(_) - | Self::ProvidedCommands(_) => OwnershipRequirement::Vm, + | Self::ProvidedCommands(_) + | Self::ExecutionAccepted(_) + | Self::ExecutionCompleted(_) + | Self::ExecutionEvaluation(_) + | Self::TypeScriptCheck(_) + | Self::ExecutionDescriptor(_) + | Self::ExecutionList(_) + | Self::ExecutionDeleted(_) + | Self::ExecutionIo(_) + | Self::ExecutionOutputPage(_) => OwnershipRequirement::Vm, Self::ExtResult(_) => OwnershipRequirement::Any, } } @@ -2518,6 +2993,15 @@ impl ResponsePayload { Self::PtyResized(_) => "pty_resized", Self::PackageLinked(_) => "package_linked", Self::ProvidedCommands(_) => "provided_commands_response", + Self::ExecutionAccepted(_) => "execution_accepted", + Self::ExecutionCompleted(_) => "execution_completed", + Self::ExecutionEvaluation(_) => "execution_evaluation", + Self::TypeScriptCheck(_) => "type_script_check", + Self::ExecutionDescriptor(_) => "execution_descriptor", + Self::ExecutionList(_) => "execution_list", + Self::ExecutionDeleted(_) => "execution_deleted", + Self::ExecutionIo(_) => "execution_io", + Self::ExecutionOutputPage(_) => "execution_output_page", } } } @@ -2540,9 +3024,11 @@ impl EventPayload { fn ownership_requirement(&self) -> OwnershipRequirement { match self { Self::Structured(_) => OwnershipRequirement::SessionOrVm, - Self::VmLifecycle(_) | Self::ProcessOutput(_) | Self::ProcessExited(_) => { - OwnershipRequirement::Vm - } + Self::VmLifecycle(_) + | Self::ProcessOutput(_) + | Self::ProcessExited(_) + | Self::ExecutionOutput(_) + | Self::ExecutionCompleted(_) => OwnershipRequirement::Vm, Self::Ext(_) => OwnershipRequirement::Any, } } diff --git a/crates/sidecar-protocol/src/wire.rs b/crates/sidecar-protocol/src/wire.rs index 61e2620560..fb0394e8ad 100644 --- a/crates/sidecar-protocol/src/wire.rs +++ b/crates/sidecar-protocol/src/wire.rs @@ -1,4 +1,4 @@ -//! Generated Secure Exec sidecar wire protocol surface. +//! Generated AgentOS language execution sidecar wire protocol surface. //! //! This module is the public generated protocol entrypoint. The hand-written //! `protocol` module remains an internal compatibility layer while callers move diff --git a/crates/v8-runtime/Cargo.toml b/crates/v8-runtime/Cargo.toml index 08e10dfee2..7d592de2ee 100644 --- a/crates/v8-runtime/Cargo.toml +++ b/crates/v8-runtime/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -description = "V8 isolate runtime for secure-exec guest JavaScript execution" +description = "V8 isolate runtime for agentos guest JavaScript execution" [features] test-support = [] diff --git a/crates/v8-runtime/npm/.gitignore b/crates/v8-runtime/npm/.gitignore index c37406f79d..d460955880 100644 --- a/crates/v8-runtime/npm/.gitignore +++ b/crates/v8-runtime/npm/.gitignore @@ -1,3 +1,3 @@ # Platform binaries are placed here by CI, not committed -*/secure-exec-v8 -*/secure-exec-v8.exe +*/agentos-v8 +*/agentos-v8.exe diff --git a/crates/v8-runtime/npm/linux-x64-gnu/README.md b/crates/v8-runtime/npm/linux-x64-gnu/README.md index 50fa0499ff..ca07d3d011 100644 --- a/crates/v8-runtime/npm/linux-x64-gnu/README.md +++ b/crates/v8-runtime/npm/linux-x64-gnu/README.md @@ -4,6 +4,6 @@ Linux x64 (glibc) binary for @rivet-dev/agentos-v8-runtime. This package is installed automatically by `@rivet-dev/agentos-v8-runtime` as a platform-specific optional dependency. -- Website: https://secureexec.dev -- Docs: https://secureexec.dev/docs -- GitHub: https://github.com/rivet-dev/secure-exec +- Website: https://agentos.dev +- Docs: https://agentos.dev/docs +- GitHub: https://github.com/rivet-dev/agentos diff --git a/crates/v8-runtime/src/bridge.rs b/crates/v8-runtime/src/bridge.rs index 9ec869c662..2f46e12228 100644 --- a/crates/v8-runtime/src/bridge.rs +++ b/crates/v8-runtime/src/bridge.rs @@ -2319,7 +2319,7 @@ mod tests { } #[test] - fn bridge_error_code_accepts_trusted_secure_exec_prefixes() { + fn bridge_error_code_accepts_trusted_agentos_prefixes() { assert_eq!( bridge_error_code("ERR_AGENTOS_NODE_SYNC_RPC: EACCES: permission denied on /foo"), Some("EACCES") diff --git a/crates/v8-runtime/src/embedded_runtime.rs b/crates/v8-runtime/src/embedded_runtime.rs index 96e0178151..aa83a3de23 100644 --- a/crates/v8-runtime/src/embedded_runtime.rs +++ b/crates/v8-runtime/src/embedded_runtime.rs @@ -594,7 +594,7 @@ impl EmbeddedV8SessionHandle { } fn validate_execute_mode(mode: u8) -> io::Result<()> { - if mode > 1 { + if mode > 3 { return Err(io::Error::new( io::ErrorKind::InvalidInput, format!("unknown Execute mode: {mode}"), diff --git a/crates/v8-runtime/src/execution.rs b/crates/v8-runtime/src/execution.rs index 24c1c03f8c..299d72129a 100644 --- a/crates/v8-runtime/src/execution.rs +++ b/crates/v8-runtime/src/execution.rs @@ -116,7 +116,7 @@ pub fn install_high_resolution_time_global(scope: &mut v8::HandleScope, origin: let Some(func) = template.get_function(scope) else { return; }; - let key = v8::String::new(scope, "__secureExecHrNowUs").unwrap(); + let key = v8::String::new(scope, "__agentOsHrNowUs").unwrap(); let attr = v8::PropertyAttribute::READ_ONLY | v8::PropertyAttribute::DONT_DELETE; global.define_own_property(scope, key.into(), func.into(), attr); } @@ -128,7 +128,7 @@ pub fn install_require_esm_sync_global(scope: &mut v8::HandleScope) { let Some(function) = template.get_function(scope) else { return; }; - let key = v8::String::new(scope, "__secureExecRequireEsmSync").unwrap(); + let key = v8::String::new(scope, "__agentOsRequireEsmSync").unwrap(); let attributes = v8::PropertyAttribute::READ_ONLY | v8::PropertyAttribute::DONT_DELETE; global.define_own_property(scope, key.into(), function.into(), attributes); } diff --git a/crates/v8-runtime/src/runtime_protocol.rs b/crates/v8-runtime/src/runtime_protocol.rs index 3d71736716..58b79097d9 100644 --- a/crates/v8-runtime/src/runtime_protocol.rs +++ b/crates/v8-runtime/src/runtime_protocol.rs @@ -208,7 +208,7 @@ impl TryFrom for RuntimeCommand { high_resolution_time, user_code, } => { - if mode > 1 { + if mode > 3 { return Err(io::Error::new( io::ErrorKind::InvalidInput, format!("unknown Execute mode: {mode}"), @@ -352,7 +352,7 @@ mod tests { fn rejects_unknown_execute_mode() { let err = RuntimeCommand::try_from(BinaryFrame::Execute { session_id: "s".into(), - mode: 2, + mode: 4, file_path: "/app/main.mjs".into(), bridge_code: String::new(), post_restore_script: String::new(), diff --git a/crates/v8-runtime/src/session.rs b/crates/v8-runtime/src/session.rs index 91ad64dd65..c5139ba709 100644 --- a/crates/v8-runtime/src/session.rs +++ b/crates/v8-runtime/src/session.rs @@ -3,7 +3,7 @@ #[cfg(not(test))] use std::collections::BTreeMap; use std::collections::{HashMap, HashSet}; -use std::sync::atomic::AtomicU64; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::{Arc, Condvar, Mutex, OnceLock}; use std::thread; use std::time::{Duration, Instant}; @@ -517,6 +517,7 @@ struct SessionAssignment { snapshot_cache: Arc, isolate_handle: SharedIsolateHandle, execution_abort: SharedExecutionAbort, + execution_active: Arc, pause_control: Arc, session_id: String, output_generation: Option, @@ -832,7 +833,7 @@ fn spawn_warm_worker( let worker_userland_code = userland_code.clone(); // AGENTOS_THREAD_SITE: bounded-v8-warm-worker let join_handle = match thread::Builder::new() - .name(String::from("secure-exec-v8-warm-worker")) + .name(String::from("agentos-v8-warm-worker")) .spawn(move || { let precreated = precreate_warm_isolate( snapshot_cache, @@ -934,7 +935,7 @@ fn precreate_warm_isolate( /// Normalize an opt-in CPU-time budget: `Some(0)` means "disabled" and folds to /// `None` so the CPU-budget watchdog is NOT armed. The runtime layer does not -/// invent a default here: secure-exec sidecar VM executions pass the typed +/// invent a default here: agentos sidecar VM executions pass the typed /// `limits.jsRuntime.cpuTimeLimitMs` default, while lower-level callers can pass /// `None`/`0` deliberately. fn normalize_cpu_time_limit_ms(cpu_time_limit_ms: Option) -> Option { @@ -1004,6 +1005,9 @@ struct SessionEntry { isolate_handle: SharedIsolateHandle, /// Current execution abort handle used to wake sync bridge waits. execution_abort: SharedExecutionAbort, + /// Set from Execute admission until that operation leaves the session + /// thread, including the startup window before an isolate handle exists. + execution_active: Arc, pause_control: Arc, /// Durable socket readiness and its dedicated capacity-one wake lane. ready_broker: Arc, @@ -1168,6 +1172,14 @@ impl Drop for ActiveExecutionAbort { } } +struct ExecutionActivityGuard(Arc); + +impl Drop for ExecutionActivityGuard { + fn drop(&mut self) { + self.0.store(false, Ordering::Release); + } +} + pub(crate) fn signal_execution_abort(shared: &SharedExecutionAbort, reason: ExecutionAbortReason) { let mut guard = shared.0.lock().unwrap(); if let Some(state) = guard.as_mut() { @@ -1406,6 +1418,7 @@ impl SessionManager { SessionReadiness::new(ready_generation, &session_runtime, ready_batch_handle_limit)?; let isolate_handle = Arc::new(Mutex::new(None)); let execution_abort = new_execution_abort(); + let execution_active = Arc::new(AtomicBool::new(false)); let pause_control = Arc::new(SessionPauseControl::default()); #[cfg(test)] let session_resources = Arc::clone(session_runtime.resources()); @@ -1424,6 +1437,7 @@ impl SessionManager { snapshot_cache: Arc::clone(&self.snapshot_cache), isolate_handle: Arc::clone(&isolate_handle), execution_abort: execution_abort.clone(), + execution_active: Arc::clone(&execution_active), pause_control: Arc::clone(&pause_control), session_id: session_id.clone(), output_generation, @@ -1474,6 +1488,7 @@ impl SessionManager { join_handle: Some(join_handle), isolate_handle, execution_abort, + execution_active, pause_control, ready_broker, #[cfg(test)] @@ -1751,7 +1766,7 @@ impl SessionManager { &self, session_id: &str, msg: &SessionMessage, - ) -> Result<(Sender, usize), String> { + ) -> Result<(Sender, usize, Arc), String> { let entry = self .sessions .get(session_id) @@ -1769,10 +1784,21 @@ impl SessionManager { } } if matches!(msg, SessionMessage::TerminateExecution) { - signal_execution_abort(&entry.execution_abort, ExecutionAbortReason::Terminated); + if entry.execution_active.load(Ordering::Acquire) { + signal_execution_abort_durable( + &entry.execution_abort, + ExecutionAbortReason::Terminated, + ); + } else { + signal_execution_abort(&entry.execution_abort, ExecutionAbortReason::Terminated); + } } - Ok((entry.tx.clone(), entry.command_capacity)) + Ok(( + entry.tx.clone(), + entry.command_capacity, + Arc::clone(&entry.execution_active), + )) } /// Admit an ordinary message without ever blocking the thread that also @@ -1780,6 +1806,7 @@ impl SessionManager { /// `publish_readiness`; ordinary events are never reclassified by name. pub fn try_send_to_session(&self, session_id: &str, msg: SessionMessage) -> Result<(), String> { let terminate_requested = matches!(&msg, SessionMessage::TerminateExecution); + let execute_requested = matches!(&msg, SessionMessage::Execute { .. }); let incoming_kind = match &msg { SessionMessage::InjectGlobals { .. } => String::from("inject_globals"), SessionMessage::Execute { .. } => String::from("execute"), @@ -1787,7 +1814,11 @@ impl SessionManager { SessionMessage::StreamEvent(event) => format!("stream_event:{}", event.event_type), SessionMessage::TerminateExecution => String::from("terminate_execution"), }; - let (sender, command_capacity) = self.session_command_sender(session_id, &msg)?; + let (sender, command_capacity, execution_active) = + self.session_command_sender(session_id, &msg)?; + if execute_requested { + execution_active.store(true, Ordering::Release); + } let command = SessionCommand::Message(msg); match sender.try_send(command) { @@ -1798,12 +1829,18 @@ impl SessionManager { Ok(()) } Err(crossbeam_channel::TrySendError::Full(_)) => { + if execute_requested { + execution_active.store(false, Ordering::Release); + } Err(format!( "ERR_AGENTOS_SESSION_COMMAND_LIMIT: session {session_id} command queue exceeded limit of {command_capacity} while admitting {incoming_kind} (queued={}); raise limits.reactor.maxHandleCommands", sender.len() )) } Err(crossbeam_channel::TrySendError::Disconnected(_)) => { + if execute_requested { + execution_active.store(false, Ordering::Release); + } Err(format!( "session thread disconnected for session {session_id}" )) @@ -2205,6 +2242,7 @@ fn session_thread( snapshot_cache, isolate_handle, execution_abort, + execution_active, pause_control, session_id, output_generation, @@ -2341,6 +2379,7 @@ fn session_thread( user_code, wasm_module_bytes, } => { + let _execution_activity = ExecutionActivityGuard(Arc::clone(&execution_active)); // `userland_code` is consumed only by the non-test snapshot // path below; keep it bound (without a warning) under `test`. #[cfg(test)] @@ -2490,11 +2529,19 @@ fn session_thread( let iso = v8_isolate.as_mut().unwrap(); iso.cancel_terminate_execution(); - // Create execution context: Context::new on a snapshot-restored - // isolate gives a fresh clone of the snapshot's default context - // (bridge IIFE already executed, all infrastructure set up). - // On a non-snapshot isolate, this gives a blank context. - let exec_context = isolate::create_context(iso); + // Language executions set bit 1 to reuse the session's + // process-lifetime context. Normal process executions keep + // receiving a fresh context for every Execute message. + let retain_context = mode & 2 != 0; + let module_mode = mode & 1 != 0; + let exec_context = if retain_context { + _v8_context + .as_ref() + .expect("session context exists after isolate creation") + .clone() + } else { + isolate::create_context(iso) + }; if high_resolution_time { let scope = &mut v8::HandleScope::new(iso); @@ -2538,6 +2585,28 @@ fn session_thread( // terminate requests can unblock sync bridge waits. let (_active_execution_abort, abort_rx) = ActiveExecutionAbort::arm(&execution_abort); + if execution_abort_requested(&abort_rx) { + // Termination may arrive after Execute admission but + // before isolate startup completes. Finish without + // entering guest code; there is no running isolate + // frame for terminate_execution() to interrupt yet. + send_event_with_generation( + &event_tx, + output_generation, + RuntimeEvent::ExecutionResult { + session_id, + exit_code: 1, + exports: None, + error: Some(ExecutionErrorBin { + error_type: String::from("Error"), + message: String::from("Execution terminated"), + stack: String::new(), + code: String::new(), + }), + }, + ); + continue; + } // Async completions have a dedicated bounded lane. // Synchronous calls register their own capacity-one @@ -2776,7 +2845,7 @@ fn session_thread( Some(file_path.as_str()) }; let phase_start = Instant::now(); - let (mut code, mut exports, mut error) = if mode == 0 { + let (mut code, mut exports, mut error) = if !module_mode { let scope = &mut v8::HandleScope::new(iso); let ctx = v8::Local::new(scope, &exec_context); let scope = &mut v8::ContextScope::new(scope, ctx); @@ -2806,7 +2875,7 @@ fn session_thread( // Re-check async ESM completion once immediately so // pure-microtask top-level await settles without // needing a bridge event-loop round-trip. - if mode != 0 && error.is_none() { + if module_mode && error.is_none() { let scope = &mut v8::HandleScope::new(iso); let ctx = v8::Local::new(scope, &exec_context); let scope = &mut v8::ContextScope::new(scope, ctx); @@ -2858,7 +2927,7 @@ fn session_thread( // Finalize any entry-module top-level await that was // waiting on bridge-driven async work (timers/network). - if !terminated && mode != 0 && error.is_none() { + if !terminated && module_mode && error.is_none() { let scope = &mut v8::HandleScope::new(iso); let ctx = v8::Local::new(scope, &exec_context); let scope = &mut v8::ContextScope::new(scope, ctx); @@ -2947,7 +3016,7 @@ fn session_thread( } } - if !terminated && mode == 0 && error.is_none() { + if !terminated && !module_mode && error.is_none() { let scope = &mut v8::HandleScope::new(iso); let ctx = v8::Local::new(scope, &exec_context); let scope = &mut v8::ContextScope::new(scope, ctx); @@ -4232,6 +4301,7 @@ mod tests { join_handle: Some(join_handle), isolate_handle: Arc::new(Mutex::new(None)), execution_abort: new_execution_abort(), + execution_active: Arc::new(AtomicBool::new(false)), pause_control: Arc::new(SessionPauseControl::default()), ready_broker, session_resources, diff --git a/crates/v8-runtime/src/snapshot.rs b/crates/v8-runtime/src/snapshot.rs index 2df4e3b4eb..bafe92aa17 100644 --- a/crates/v8-runtime/src/snapshot.rs +++ b/crates/v8-runtime/src/snapshot.rs @@ -87,9 +87,9 @@ const SNAPSHOT_USERLAND_PREP: &str = r#" // snapshot eval; it also works post-restore (resolution flows through the real // bridge fns swapped in after restore). if (typeof globalThis.require === "undefined" && - typeof globalThis.__secureExecGuestCreateRequire === "function") { + typeof globalThis.__agentOsGuestCreateRequire === "function") { try { - globalThis.require = globalThis.__secureExecGuestCreateRequire("/root/index.js"); + globalThis.require = globalThis.__agentOsGuestCreateRequire("/root/index.js"); } catch (e) {} } // `process.versions` is a bridge-backed lazy getter: it derives `.node` from the diff --git a/crates/v8-runtime/src/stream.rs b/crates/v8-runtime/src/stream.rs index 596e034f7c..aeffefbfda 100644 --- a/crates/v8-runtime/src/stream.rs +++ b/crates/v8-runtime/src/stream.rs @@ -10,7 +10,7 @@ /// - "http2" → _http2Dispatch /// - "stdin", "stdin_end" → _stdinDispatch /// - "net_socket" → _netSocketDispatch -/// - "signal" → __secureExecWasmSignalDispatch or _signalDispatch +/// - "signal" → __agentOsWasmSignalDispatch or _signalDispatch /// - "timer" → _timerDispatch pub fn dispatch_stream_event(scope: &mut v8::HandleScope, event_type: &str, payload: &[u8]) { // Look up the dispatch function on the global object @@ -23,7 +23,7 @@ pub fn dispatch_stream_event(scope: &mut v8::HandleScope, event_type: &str, payl "http2" => &["_http2Dispatch"], "stdin" | "stdin_end" => &["_stdinDispatch"], "net_socket" => &["_netSocketDispatch"], - "signal" => &["__secureExecWasmSignalDispatch", "_signalDispatch"], + "signal" => &["__agentOsWasmSignalDispatch", "_signalDispatch"], "timer" => &["_timerDispatch"], _ => return, // Unknown event type — ignore }; @@ -94,7 +94,7 @@ fn dispatch_stream_value( let context = scope.get_current_context(); let global = context.global(scope); let dispatch_names: &[&str] = match event_type { - "signal" => &["__secureExecWasmSignalDispatch", "_signalDispatch"], + "signal" => &["__agentOsWasmSignalDispatch", "_signalDispatch"], "timer" => &["_timerDispatch"], _ => return, }; diff --git a/crates/v8-runtime/src/timeout.rs b/crates/v8-runtime/src/timeout.rs index ebacaee9d7..7e25814a1a 100644 --- a/crates/v8-runtime/src/timeout.rs +++ b/crates/v8-runtime/src/timeout.rs @@ -15,7 +15,7 @@ // EXCLUDES idle/await. V8 has no native budget primitive, so this poll + // `terminate_execution()` approach is the standard embedder pattern. Armed // when the caller passes a nonzero `limits.jsRuntime.cpuTimeLimitMs`. -// secure-exec sidecar VM executions supply a bounded default; lower-level +// agentos sidecar VM executions supply a bounded default; lower-level // embedders may pass `None`/`0` to leave the guard disabled. // // The two guards are independent: setting one typed limit arms only that guard, diff --git a/crates/vfs-store/CLAUDE.md b/crates/vfs-store/CLAUDE.md index 4d830a1f02..9808ec0129 100644 --- a/crates/vfs-store/CLAUDE.md +++ b/crates/vfs-store/CLAUDE.md @@ -1,5 +1,5 @@ # agentos-vfs -- `agentos-vfs` contains concrete backend adapters for secure-exec deployments: S3, host-disk metadata/block stores, and bridge/callback-backed stores. +- `agentos-vfs` contains concrete backend adapters for agentos deployments: S3, host-disk metadata/block stores, and bridge/callback-backed stores. - Keep policy decisions, trusted configuration validation, mount descriptor parsing, and sidecar lifecycle wiring in the sidecar plugin layer. - Generic filesystem algorithms and in-memory stores belong in `vfs`. diff --git a/crates/vfs-store/Cargo.toml b/crates/vfs-store/Cargo.toml index 3252ceedd4..c277e334e8 100644 --- a/crates/vfs-store/Cargo.toml +++ b/crates/vfs-store/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -description = "Secure Exec virtual filesystem backends" +description = "AgentOS language execution virtual filesystem backends" [dependencies] async-trait = "0.1" diff --git a/crates/vfs-store/src/callback_store.rs b/crates/vfs-store/src/callback_store.rs index a3de96a573..674ba501bc 100644 --- a/crates/vfs-store/src/callback_store.rs +++ b/crates/vfs-store/src/callback_store.rs @@ -7,7 +7,7 @@ use vfs::engine::{ MetadataStore, SnapshotId, VfsError, VfsResult, }; -pub const VFS_METADATA_EXT_NAMESPACE: &str = "secure-exec.vfs.metadata.v1"; +pub const VFS_METADATA_EXT_NAMESPACE: &str = "agentos.vfs.metadata.v1"; const CALLBACK_METADATA_TIMEOUT: Duration = Duration::from_secs(30); pub trait CallbackMetadataClient: Clone + Send + Sync + 'static { diff --git a/crates/vfs/CLAUDE.md b/crates/vfs/CLAUDE.md index 4cccd1bb89..2a99a33d70 100644 --- a/crates/vfs/CLAUDE.md +++ b/crates/vfs/CLAUDE.md @@ -1,6 +1,6 @@ # vfs -- `vfs` is generic filesystem infrastructure only. Do not add secure-exec sidecar, bridge, S3, SQLite, host-disk, or registry coupling here. +- `vfs` is generic filesystem infrastructure only. Do not add agentos sidecar, bridge, S3, SQLite, host-disk, or registry coupling here. - The crate intentionally contains separate filesystem type universes under explicit modules while the consolidation is in progress. Do not glob-merge names that would confuse those boundaries. - Concrete environment-bound backends belong in `agentos-vfs`. - The `chunked` engine deliberately decouples the `MetadataStore` from the `BlockStore`: blocks are content-addressed, opaque, and self-describing only as a set, while the directory tree, inode table, chunk map, and refcounts live entirely in the metadata store. The engine makes **no self-containment promise** about either half. Pairing a block store with a metadata store, and ensuring the metadata store is durable and co-located with whatever lifecycle the blocks need, is the **caller's responsibility** (the sidecar plugin / client config that wires the backends), not the engine's. So e.g. an `S3BlockStore` backed by a local `SqliteMetadataStore` is a valid, intended configuration; the engine does not assume blocks carry enough information to reconstruct the tree, and "the metadata lives elsewhere than the blocks" is by design, not a defect. If a deployment needs the tree to survive loss of the local metadata, it must choose a durable metadata store (e.g. the callback backend) — that is a wiring decision, not an engine concern. diff --git a/crates/vfs/assets/base-filesystem.json b/crates/vfs/assets/base-filesystem.json index e641efddd3..bdb7b06ec7 100644 --- a/crates/vfs/assets/base-filesystem.json +++ b/crates/vfs/assets/base-filesystem.json @@ -5,8 +5,8 @@ "snapshotCreatedAt": "2026-06-22T19:45:20.529Z", "builtAt": "2026-06-23T03:47:12.644Z", "transforms": [ - "Normalize HOSTNAME to secure-exec", - "Preserve the captured user-level environment and filesystem layout as the secure-exec base layer", + "Normalize HOSTNAME to agentos", + "Preserve the captured user-level environment and filesystem layout as the agentos base layer", "Add the non-Alpine /workspace directory (default agent working directory) owned by the base user", "Restore Alpine 3.22's /etc/services database and add the VM's Docker-style /etc/hosts entries for libc lookup parity" ] @@ -15,7 +15,7 @@ "env": { "CHARSET": "UTF-8", "HOME": "/home/agentos", - "HOSTNAME": "secure-exec", + "HOSTNAME": "agentos", "LANG": "C.UTF-8", "LC_COLLATE": "C", "LOGNAME": "agentos", @@ -92,7 +92,7 @@ "mode": "644", "uid": 0, "gid": 0, - "content": "secure-exec\n" + "content": "agentos\n" }, { "path": "/etc/hosts", @@ -100,7 +100,7 @@ "mode": "644", "uid": 0, "gid": 0, - "content": "127.0.0.1 localhost localhost.localdomain\n::1 localhost localhost.localdomain ip6-localhost ip6-loopback\nfe00:: ip6-localnet\nff00:: ip6-mcastprefix\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n127.0.1.1 secure-exec\n" + "content": "127.0.0.1 localhost localhost.localdomain\n::1 localhost localhost.localdomain ip6-localhost ip6-loopback\nfe00:: ip6-localnet\nff00:: ip6-mcastprefix\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n127.0.1.1 agentos\n" }, { "path": "/etc/logrotate.d", diff --git a/crates/vfs/src/posix/overlay_fs.rs b/crates/vfs/src/posix/overlay_fs.rs index 362975cfbb..ed3e863c63 100644 --- a/crates/vfs/src/posix/overlay_fs.rs +++ b/crates/vfs/src/posix/overlay_fs.rs @@ -6,9 +6,9 @@ use base64::Engine; use std::collections::BTreeSet; const MAX_SNAPSHOT_DEPTH: usize = 1024; -const OVERLAY_METADATA_ROOT: &str = "/.secure-exec-overlay"; -const OVERLAY_WHITEOUT_DIR: &str = "/.secure-exec-overlay/whiteouts"; -const OVERLAY_OPAQUE_DIR: &str = "/.secure-exec-overlay/opaque"; +const OVERLAY_METADATA_ROOT: &str = "/.agentos-overlay"; +const OVERLAY_WHITEOUT_DIR: &str = "/.agentos-overlay/whiteouts"; +const OVERLAY_OPAQUE_DIR: &str = "/.agentos-overlay/opaque"; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum OverlayMode { @@ -304,7 +304,7 @@ impl OverlayFileSystem { /// /// The lexical [`is_internal_metadata_path`] check alone is bypassable: the /// underlying `MemoryFileSystem` follows symlinks, so a guest-created symlink - /// whose resolved target enters `/.secure-exec-overlay` (directly, or via a + /// whose resolved target enters `/.agentos-overlay` (directly, or via a /// symlink to an ancestor such as `/`) would slip past a purely lexical guard /// and let the guest read or tamper with whiteout/opaque markers (e.g. /// resurrecting a deleted lower-layer file). Resolving before the check @@ -334,7 +334,7 @@ impl OverlayFileSystem { } fn hidden_root_entry_name() -> &'static str { - ".secure-exec-overlay" + ".agentos-overlay" } fn should_hide_directory_entry(path: &str, entry: &str) -> bool { @@ -2165,7 +2165,7 @@ mod tests { // A guest symlink whose target is the metadata root must not become a // window into the reserved namespace. overlay - .symlink("/.secure-exec-overlay/whiteouts", "/escape") + .symlink("/.agentos-overlay/whiteouts", "/escape") .expect("creating the symlink itself is allowed"); // Listing through the symlink must be denied, not disclose markers. @@ -2191,7 +2191,7 @@ mod tests { .expect("symlink to root is allowed"); assert!( overlay - .read_dir("/rootlink/.secure-exec-overlay/whiteouts") + .read_dir("/rootlink/.agentos-overlay/whiteouts") .is_err(), "metadata must be unreachable via an ancestor symlink too" ); @@ -2257,9 +2257,7 @@ mod tests { Vec::::new() ); let root_entries = overlay.read_dir("/").expect("read root"); - assert!(!root_entries - .iter() - .any(|entry| entry == ".secure-exec-overlay")); + assert!(!root_entries.iter().any(|entry| entry == ".agentos-overlay")); } #[test] diff --git a/crates/vfs/src/posix/root_fs.rs b/crates/vfs/src/posix/root_fs.rs index 78c93523f4..a165a9ee54 100644 --- a/crates/vfs/src/posix/root_fs.rs +++ b/crates/vfs/src/posix/root_fs.rs @@ -12,12 +12,12 @@ use serde::Deserialize; use std::collections::BTreeSet; // The base filesystem fixture is staged into OUT_DIR by build.rs: copied from -// the canonical `packages/secure-exec-core/fixtures/base-filesystem.json` +// the canonical `packages/agentos-core/fixtures/base-filesystem.json` // during in-tree builds, or from the vendored `assets/base-filesystem.json` // copy bundled in the published crate. const BUNDLED_BASE_FILESYSTEM_JSON: &str = include_str!(concat!(env!("OUT_DIR"), "/base-filesystem.json")); -pub const ROOT_FILESYSTEM_SNAPSHOT_FORMAT: &str = "secure_exec_filesystem_snapshot_v1"; +pub const ROOT_FILESYSTEM_SNAPSHOT_FORMAT: &str = "agentos_filesystem_snapshot_v1"; const LEGACY_AGENTOS_ROOT_FILESYSTEM_SNAPSHOT_FORMAT: &str = "agentos_filesystem_snapshot_v1"; const ROOT_FILESYSTEM_SNAPSHOT_FIXED_OVERHEAD_BYTES: usize = 4 * 1024; const ROOT_FILESYSTEM_SNAPSHOT_ENTRY_OVERHEAD_BYTES: usize = MAX_PATH_LENGTH + 1024; diff --git a/crates/vfs/tests/package_format.rs b/crates/vfs/tests/package_format.rs index efadeea70e..31c670e774 100644 --- a/crates/vfs/tests/package_format.rs +++ b/crates/vfs/tests/package_format.rs @@ -97,7 +97,7 @@ fn package_format_rejects_unknown_schema_version_and_corrupt_headers() { #[test] fn tar_filesystem_rejects_unsorted_index() { - let path = unique_path("secure-exec-unsorted-aospkg"); + let path = unique_path("agentos-unsorted-aospkg"); let manifest = encode_package_manifest(v1::PackageManifest { name: String::from("unsorted"), version: String::from("1.0.0"), diff --git a/crates/vfs/tests/posix_root_fs.rs b/crates/vfs/tests/posix_root_fs.rs index 26af4db923..6bab39a9e3 100644 --- a/crates/vfs/tests/posix_root_fs.rs +++ b/crates/vfs/tests/posix_root_fs.rs @@ -304,7 +304,7 @@ fn root_filesystem_uses_bundled_base_and_round_trips_snapshots() { .any(|window| window == b"smtp\t\t25/tcp\t\tmail")); assert_eq!( root.read_file("/etc/hosts").expect("read bundled hosts file"), - b"127.0.0.1 localhost localhost.localdomain\n::1 localhost localhost.localdomain ip6-localhost ip6-loopback\nfe00:: ip6-localnet\nff00:: ip6-mcastprefix\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n127.0.1.1 secure-exec\n" + b"127.0.0.1 localhost localhost.localdomain\n::1 localhost localhost.localdomain ip6-localhost ip6-loopback\nfe00:: ip6-localnet\nff00:: ip6-mcastprefix\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n127.0.1.1 agentos\n" ); root.mkdir("/workspace", true).expect("create workspace"); @@ -503,7 +503,7 @@ fn snapshot_round_trip_preserves_file_type_bits_in_modes() { fn decode_snapshot_accepts_zero_mode_strings() { let decoded = decode_snapshot( br#"{ - "format": "secure_exec_filesystem_snapshot_v1", + "format": "agentos_filesystem_snapshot_v1", "filesystem": { "entries": [ { @@ -581,7 +581,7 @@ fn decode_snapshot_rejects_encoded_payloads_that_exceed_import_limits() { let error = decode_snapshot_with_import_limits( br#"{ - "format": "secure_exec_filesystem_snapshot_v1", + "format": "agentos_filesystem_snapshot_v1", "filesystem": { "entries": [] } }"#, &limits, @@ -601,7 +601,7 @@ fn decode_snapshot_rejects_entry_counts_that_exceed_import_limits() { let error = decode_snapshot_with_import_limits( br#"{ - "format": "secure_exec_filesystem_snapshot_v1", + "format": "agentos_filesystem_snapshot_v1", "filesystem": { "entries": [ { @@ -638,7 +638,7 @@ fn decode_snapshot_rejects_content_bytes_that_exceed_import_limits() { let error = decode_snapshot_with_import_limits( br#"{ - "format": "secure_exec_filesystem_snapshot_v1", + "format": "agentos_filesystem_snapshot_v1", "filesystem": { "entries": [ { @@ -665,7 +665,7 @@ fn decode_snapshot_allows_metadata_heavy_entries_within_import_limits() { let path = format!("/{}", "a".repeat(4000)); let snapshot = format!( r#"{{ - "format": "secure_exec_filesystem_snapshot_v1", + "format": "agentos_filesystem_snapshot_v1", "filesystem": {{ "entries": [ {{ diff --git a/crates/vfs/tests/posix_tar_fs.rs b/crates/vfs/tests/posix_tar_fs.rs index dc50551e23..daba597d75 100644 --- a/crates/vfs/tests/posix_tar_fs.rs +++ b/crates/vfs/tests/posix_tar_fs.rs @@ -101,7 +101,7 @@ fn tar_filesystem_cache_uses_file_identity_and_stable_guest_device() { assert_ne!(stat_a.dev, other_stat.dev); let same_bytes = std::fs::read(&tar_path).expect("read fixture tar"); - let copied_tar = unique_tar_path("secure-exec-tar-fs-copy"); + let copied_tar = unique_tar_path("agentos-tar-fs-copy"); std::fs::write(&copied_tar, same_bytes).expect("copy fixture tar bytes"); let fs_copy = TarFileSystem::open(&copied_tar).expect("open copied tar filesystem"); assert_ne!(fs_a.archive_ptr(), fs_copy.archive_ptr()); @@ -114,7 +114,7 @@ fn tar_filesystem_cache_uses_file_identity_and_stable_guest_device() { } fn write_fixture_tar() -> PathBuf { - let path = unique_tar_path("secure-exec-tar-fs-fixture"); + let path = unique_tar_path("agentos-tar-fs-fixture"); write_fixture_tar_at(path) } diff --git a/crates/vm-config/Cargo.toml b/crates/vm-config/Cargo.toml index bee108c954..733a8f3aac 100644 --- a/crates/vm-config/Cargo.toml +++ b/crates/vm-config/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -description = "Shared Secure Exec VM creation JSON config DTOs" +description = "Shared AgentOS language execution VM creation JSON config DTOs" [dependencies] serde = { version = "1.0", features = ["derive"] } diff --git a/docs-internal/kernel-runtime-subsystem-map.md b/docs-internal/kernel-runtime-subsystem-map.md index 40399736a1..5b8011f115 100644 --- a/docs-internal/kernel-runtime-subsystem-map.md +++ b/docs-internal/kernel-runtime-subsystem-map.md @@ -126,7 +126,7 @@ This is the baseline filesystem layer that everything else builds on. Relevant files: - `crates/kernel/src/vfs.rs` - `crates/kernel/src/root_fs.rs` -- `packages/secure-exec-core/fixtures/base-filesystem.json` +- `packages/agentos-core/fixtures/base-filesystem.json` - `crates/kernel/src/device_layer.rs` - `crates/kernel/src/overlay_fs.rs` - `crates/kernel/src/mount_table.rs` diff --git a/docs-internal/registry-flatten-colocation-spec.md b/docs-internal/registry-flatten-colocation-spec.md index b7a21b037f..227582cf5c 100644 --- a/docs-internal/registry-flatten-colocation-spec.md +++ b/docs-internal/registry-flatten-colocation-spec.md @@ -127,7 +127,7 @@ test-program fixtures stay in `toolchain/test-programs/` (not scattered into stub, or a hand-rolled CLI over a library. The only exception is a tool whose canonical upstream *is* the Rust project (`ripgrep`, `fd`). Several current commands violate this (coreutils=uutils, grep, curl driver, and the - `secureexec-*` rewrites) — tracked in `registry-parity-worklist.md` + `agentos-*` rewrites) — tracked in `registry-parity-worklist.md` Cross-cutting #0; new packages must not add more. ## What moves where @@ -325,7 +325,7 @@ Repo recipes orchestrate; they must not reimplement what the CLI does: | per-package `test` script + external authors | `agentos-toolchain test` | **Stays repo-specific** (not CLI, not overfit): `copy-wasm-commands` (vendor into -runtime-core), `verify-fixed-versions` (the 0.0.1 pin), `generate-secure-exec-mirror`, +runtime-core), `verify-fixed-versions` (the 0.0.1 pin), `generate-agentos-mirror`, registry-wide release orchestration, cross-repo dispatch, and the status reporter / coverage gate scoped to *our* registry. diff --git a/docs-internal/registry-parity-worklist.md b/docs-internal/registry-parity-worklist.md index e64c212335..deb88abcdf 100644 --- a/docs-internal/registry-parity-worklist.md +++ b/docs-internal/registry-parity-worklist.md @@ -85,7 +85,7 @@ fuel checkpoints that distinguish user CPU from user-plus-system CPU. **This is the highest-leverage item and reshapes several below.** Audit revealed that **most commands are NOT the real Linux tool** — they are custom Rust rewrites -(`secureexec-*` crates) or `uutils`, plus at least one hand-rolled C CLI (curl). +(`agentos-*` crates) or `uutils`, plus at least one hand-rolled C CLI (curl). Per the load-bearing rule, each must become the **real upstream tool** compiled to WASI and patched as needed. @@ -117,11 +117,11 @@ actual backing: | **wget** | DONE | our 174-line `wget.c` (dropped) | real GNU Wget vs our sysroot — stub `getrlimit`/`getgroups`, then build | | **http-get** | DONE | our 95-line `http_get.c` | dropped; real curl covers HTTP fetches | | **git** | DONE | our hand-rolled git from `sha1`+`flate2` | **real git** (upstream C), patched for WASI — **NOT gitoxide** | -| **fd** | DONE | our `secureexec-fd` on raw `regex` (not sharkdp/fd) | real **fd** (sharkdp) | +| **fd** | DONE | our `agentos-fd` on raw `regex` (not sharkdp/fd) | real **fd** (sharkdp) | | **findutils** (`find`,`xargs`) | DONE | our hand-rolled on `regex`/shims | replaced with `uutils/findutils` | | **tree** | DONE | our hand-rolled, zero deps | real `tree`, or an established one | -| **grep** | DONE | our `secureexec-grep` on raw `regex` (**not** an established grep pkg) | real **GNU grep** | -| **ripgrep** (`rg`) | DONE | our `secureexec-grep` recursive search shim, not real ripgrep | real upstream **ripgrep** | +| **grep** | DONE | our `agentos-grep` on raw `regex` (**not** an established grep pkg) | real **GNU grep** | +| **ripgrep** (`rg`) | DONE | our `agentos-grep` recursive search shim, not real ripgrep | real upstream **ripgrep** | | **zip** | DONE | our 203-line `zip.c` over zlib/minizip (not Info-ZIP) | real Info-ZIP, or an established lib's CLI | | **unzip** | DONE | our 669-line `unzip.c` over zlib/minizip | real Info-ZIP unzip | | **sqlite3 CLI** | DONE | our 558-line `sqlite3_cli.c` (engine is real SQLite; the shell is ours) | real SQLite `shell.c` (its official CLI) | @@ -244,7 +244,7 @@ works (`wasi-spawn` broker), so `xargs` is not a blocker. `2026-07-08T05-43-24-0700-http-get-clear-stale-generated-and-install.log`; cross-runtime network tests pass 11/11 with the WASM curl rows in `2026-07-08T05-47-43-0700-http-get-runtime-cross-network-test-pass.log`. -- **tree — DONE.** Replaced the custom Rust `secureexec-tree`/`cmd-tree` crates +- **tree — DONE.** Replaced the custom Rust `agentos-tree`/`cmd-tree` crates with upstream Steve Baker `tree` 2.3.2 from `OldManProgrammer/unix-tree`. It builds as a C toolchain command from pinned source, stages into `@agentos-software/tree`, and refreshes the tracked runtime-core fallback @@ -266,7 +266,7 @@ works (`wasi-spawn` broker), so `xargs` is not a blocker. longer includes the deleted Rust tree crates in `2026-07-08T05-33-17-0700-tree-cargo-metadata-after-removing-empty-dirs.log`. Rev: `kpmrwxln` — `fix(tree): build upstream tree`. -- **fd — DONE.** Replaced the custom Rust `secureexec-fd` regex walker with +- **fd — DONE.** Replaced the custom Rust `agentos-fd` regex walker with upstream sharkdp `fd-find` 10.4.2. Because `fd-find` is a bin-only crate, `cmd-fd` now acts as the workspace build trigger while the toolchain builds the upstream `fd` binary directly. WASI compatibility stays in dependency @@ -286,7 +286,7 @@ works (`wasi-spawn` broker), so `xargs` is not a blocker. `2026-07-08T06-25-00-0700-fd-vitest-upstream-after-dir-format.log`. Rev: `mrskpomv` — `fix(fd): build upstream fd-find`. - **grep — DONE.** Replaced the `@agentos-software/grep` package's custom - `secureexec-grep` command wrapper with upstream GNU grep 3.12 from the official + `agentos-grep` command wrapper with upstream GNU grep 3.12 from the official GNU release tarball. The real GNU `grep` binary builds through the C toolchain; `egrep` and `fgrep` are separate tiny WASM launchers that preserve GNU's upstream obsolescent scripts by spawning `grep -E` / `grep -F` through the @@ -308,10 +308,10 @@ works (`wasi-spawn` broker), so `xargs` is not a blocker. pass 8/8 in `2026-07-08T06-43-59-0700-grep-vitest-after-wrapper-cache-repair.log`. Rev: `uyukolvr` — `fix(grep): build upstream GNU grep`. - **ripgrep — DONE.** Replaced the `@agentos-software/ripgrep` package's custom - `secureexec-grep` recursive search shim with upstream ripgrep 15.1.0 from the + `agentos-grep` recursive search shim with upstream ripgrep 15.1.0 from the canonical `BurntSushi/ripgrep` crate/release. The local `cmd-rg` crate is now only a build trigger, and `toolchain/Makefile` builds ripgrep's own `rg` bin - directly. The old `secureexec-grep` library is gone. Proof: latest upstream + directly. The old `agentos-grep` library is gone. Proof: latest upstream release captured in `2026-07-08T06-50-40-0700-ripgrep-github-latest-release.json`; crate metadata captured in `2026-07-08T06-52-00-0700-ripgrep-cargo-info.log`; upstream WASM build passes in diff --git a/docs/design/language-execution-api.md b/docs/design/language-execution-api.md new file mode 100644 index 0000000000..bf2305c91a --- /dev/null +++ b/docs/design/language-execution-api.md @@ -0,0 +1,1446 @@ +# Language Execution API + +Status: implemented + +Audience: AgentOS client, actor, sidecar, JavaScript, TypeScript, Python, and +documentation owners + +## 1. Decision + +AgentOS will expose first-class JavaScript, TypeScript, and Python execution on +both the core `AgentOs` client and the `@rivet-dev/agentos` actor. These are +language-native conveniences over the existing VM, filesystem, process, and +package-manager capabilities. They are not a separate product or runtime. + +**Execution** is the only public lifecycle noun. An execution is identified by +`executionId`, can receive multiple sequential operations, and can retain +JavaScript or Python in-memory state between those operations. There is no +second public resource or identifier for retained language state. There is no +standalone `createExecution` method; the first Bash or language operation +creates the execution atomically when requested. + +Arbitrary commands use `process.exec` for configured-shell command strings and +`process.execFile` for exact argv invocation. The existing managed-process +surface is `process.spawn`. We will not add `executeBash` or another +shell-specific abstraction. + +Core and actor actions use the same nested shape. RivetKit serializes nested +actor actions with dotted wire names such as `javascript.execute`, +`javascript.typescript.check`, and `python.execute`. Public TypeScript +identifiers use the canonical `TypeScript` capitalization in type names. + +The API must cover the normal language ecosystem without forcing users to know +how AgentOS invokes `node`, `python`, `python -m`, `npm`, or `pip`. It must not +mirror every package-manager subcommand or CLI flag. Generic command APIs +remain the escape hatch for unusual workflows. + +## 2. Required repository guidance + +The implementation change must add a short scope rule to applicable +`CLAUDE.md` files. At minimum it belongs in `packages/CLAUDE.md`; add a more +specific copy only when a language package has its own `CLAUDE.md`. + +The guidance must preserve this meaning: + +> Language-specific modules own the common end-to-end workflows of their +> ecosystem. A user working purely in JavaScript, TypeScript, or Python should +> be able to execute source and files, evaluate values, install dependencies, +> run project entry points, and use standard module/script workflows without +> manually invoking `node`, `python`, `python -m`, `npm`, or `pip`. Add typed, +> injection-safe conveniences for stable workflows, not one method per CLI +> subcommand or flag. Keep `process.exec`, `process.execFile`, and +> `process.spawn` as the explicit escape hatch for uncommon commands. + +A helper belongs in a language module when it represents a common +language-level intent and can provide safer or clearer behavior than assembling +a command. It does not belong merely because a package-manager flag exists. + +## 3. Goals and non-goals + +### Goals + +- Make inline code, existing files, Python modules, npm scripts, and dependency + installation available without direct runtime binaries. +- Use one execution lifecycle across Bash, argv, JavaScript, TypeScript, + Python, and package-manager operations. +- Retain language state across operations when callers reuse an execution ID. +- Keep JavaScript, TypeScript, and Python results structurally consistent. +- Make TypeScript execution transpile-only by default while providing explicit + type-check APIs. +- Use argv-based internal invocation for user-supplied package names, scripts, + modules, paths, and arguments. +- Keep core, actor, TypeScript, and Rust behavior identical. +- Keep actor inputs, events, results, and errors serializable. +- Preserve `process.exec`, `process.execFile`, and `process.spawn` for + arbitrary Linux workflows. + +### Non-goals + +- A method for every npm, pip, Node.js, Python, or TypeScript CLI operation. +- A generic package-manager framework or immediate pnpm, Yarn, Bun, uv, or + Poetry parity. +- Live JavaScript handles, Python proxy objects, or isolate-owned values + crossing the actor boundary. +- Live notebook display handles or guest-owned display objects. +- A new Bash API. +- Client-side language behavior or command construction. + +## 4. Protocol-first execution model + +An execution is a sidecar-owned unit identified by `executionId`. +Shell, argv, JavaScript, TypeScript, Python, and package-manager operations are +submitted to an execution. An execution admits at most one active operation; +another submission while it is active throws `ExecutionBusyError`. Different +executions may run concurrently. + +An execution may retain JavaScript or Python variables, imports, functions, +loaded modules, and guest objects between operations. It may also own a root OS +process or process tree while an operation is active. `processId` and `pid` are +observational details; `executionId` is the only public lifecycle identity. + +The shared lifecycle is defined in Bare and implemented in the sidecar. The +TypeScript, Rust, standalone language, and actor clients validate and serialize +explicit caller input, route events, and expose generated results. They must not +translate language methods into `node`, `python`, `npm`, `pip`, or shell +commands themselves. + +### 4.1 Shared Bare shape + +The fragments below show the required factoring, not the complete generated +schema. The implementation must define request and response variants for every +semantic operation listed in sections 5 and 7. Exact generated names may +change, but the schema must preserve this shape: + +```bare +type ExecutionState enum { + CREATING + IDLE + RUNNING + RESETTING + DELETING + FAILED +} + +type ExecutionOutcome enum { + SUCCEEDED + FAILED + CANCELLED + TIMED_OUT +} + +type RetainedExecutionLanguage enum { + JAVA_SCRIPT + PYTHON +} + +type ExecutionStreamChannel enum { + STDOUT + STDERR + PTY +} + +type JavaScriptModuleFormat enum { + MODULE + COMMON_JS +} + +type ExecutionIdentityOptions struct { + executionId: optional + createIfMissing: optional +} + +type PtyOptions struct { + cols: optional + rows: optional +} + +type ProcessExecutionOptions struct { + identity: ExecutionIdentityOptions + detached: optional + cwd: optional + env: optional> + args: list + stdin: optional + timeoutMs: optional + pty: optional +} + +type ShellExecutionRequest struct { + process: ProcessExecutionOptions + command: str +} + +type ArgvExecutionRequest struct { + process: ProcessExecutionOptions + command: str +} + +type JavaScriptExecutionRequest struct { + process: ProcessExecutionOptions + source: str + format: optional + filePath: optional + inputs: optional +} + +type TypeScriptExecutionRequest struct { + process: ProcessExecutionOptions + source: str + filePath: optional + tsconfigPath: optional + compilerOptions: optional + inputs: optional +} + +type PythonExecutionRequest struct { + process: ProcessExecutionOptions + source: str + inputs: optional +} +``` + +File, Python-module, npm-script, npm-package, package-install, evaluation, and +type-check requests use the same identity and add only their stable +operation-specific fields. Each distinct semantic operation has a first-class +Bare request. Clients do not implement semantic language conveniences by +calling `process.execFile`. + +Common lifecycle and output are also protocol-owned: + +```bare +type ExecutionDescriptor struct { + executionId: str + generation: u64 + state: ExecutionState + retainedLanguage: optional + processId: optional + pid: optional + createdAtMs: u64 + lastStartedAtMs: optional + lastCompletedAtMs: optional + lastOutcome: optional + lastExitCode: optional +} + +type ExecutionErrorData struct { + code: str + name: str + message: str + stack: optional + details: optional +} + +type ExecutionCompletedResponse struct { + execution: ExecutionDescriptor + outcome: ExecutionOutcome + exitCode: optional + error: optional + stdout: data + stderr: data + stdoutTruncated: bool + stderrTruncated: bool + outputs: JsonUtf8 +} + +type ExecutionOutputEvent struct { + executionId: str + generation: u64 + processId: optional + sequence: u64 + channel: ExecutionStreamChannel + chunk: data + timestampMs: u64 +} + +type ExecutionCompletedEvent struct { + executionId: str + generation: u64 + outcome: ExecutionOutcome + exitCode: optional + error: optional +} + +type GetExecutionRequest struct { + executionId: str +} +type ListExecutionsRequest void +type WaitExecutionRequest struct { + executionId: str +} +type CancelExecutionRequest struct { + executionId: str +} +type SignalExecutionRequest struct { + executionId: str + signal: str +} +type ResetExecutionRequest struct { + executionId: str +} +type DeleteExecutionRequest struct { + executionId: str +} +type WriteExecutionStdinRequest struct { + executionId: str + chunk: data +} +type CloseExecutionStdinRequest struct { + executionId: str +} +type ResizeExecutionPtyRequest struct { + executionId: str + cols: u16 + rows: u16 +} +type ReadExecutionOutputRequest struct { + executionId: str + cursor: optional + limit: optional +} +``` + +A detached operation returns `ExecutionDescriptor` after sidecar admission. +`executions.wait` waits for the execution's sole active operation. When the +execution is already idle or failed, it returns the most recently retained +result or throws `ExecutionResultNotFoundError` if no operation has completed +since creation or reset. + +The sidecar internally orders output for the current or most recently completed +operation by monotonically increasing sequence number. Admitting a new +operation increments the execution's observational `generation`, replaces the +previous retained result and output buffer, and invalidates its cursors; it does +not clear retained language memory. `executions.readOutput` exposes only an opaque +cursor for bounded pagination and replay after actor reconnects. Resetting also +increments `generation`, clears retained output and language state, and +invalidates old cursors. `generation` is a revision marker, not a second +lifecycle identity. + +The current `ExecuteRequest`, PID-only output events, and client-maintained +process records are migration inputs, not the target architecture. The +protocol has no backwards-compatibility requirement, so implementation should +replace them in lockstep rather than add a compatibility path. Optional Bare +fields mean "use the sidecar default"; clients must not duplicate defaults. + +### 4.2 Identity and atomic create-if-missing + +When `executionId` is omitted, the sidecar generates a collision-resistant ID +and creates a fresh execution. When it is supplied, `createIfMissing` defaults +to `false`: + +- if the ID is absent, the sidecar atomically creates it and submits the + operation only when `createIfMissing` is `true`; +- if it exists and is idle, the sidecar submits the operation to that execution; +- if it exists and is active, the sidecar throws `ExecutionBusyError`; +- if it is absent and creation was not requested, the sidecar throws + `ExecutionNotFoundError`; +- concurrent requests for the same absent ID create exactly one execution; +- incompatible retained-language reuse throws `ExecutionLanguageConflictError`. + +JavaScript and TypeScript share one retained JavaScript realm. Python uses a +retained Python interpreter. Process-only operations do not pin a language and +may run before or between language operations. One execution cannot retain +JavaScript and Python state simultaneously. + +Execution IDs are scoped to their owning VM. The same caller-supplied string in +two VMs identifies two independent executions. Supplying +`createIfMissing` without `executionId` is invalid; omission of the ID already +means "create with a generated ID." + +`executionId` is not an idempotency key for every subsequent operation. The +protocol and actor transport retain internal request identities for retry +deduplication, but those identities are not part of the end-user API. Atomic +creation/admission and busy checking are one sidecar operation; client-side +`executions.get` followed by a language operation is prohibited. + +### 4.3 Public shared types + +The names below are illustrative public TypeScript definitions. Final code may +factor them differently, but must preserve their behavior. + +```ts +type JsonPrimitive = string | number | boolean | null; +type JsonValue = + | JsonPrimitive + | JsonValue[] + | { [key: string]: JsonValue }; + +type ExecutionSignal = + | "SIGHUP" + | "SIGINT" + | "SIGQUIT" + | "SIGTERM" + | "SIGKILL" + | "SIGSTOP" + | "SIGCONT" + | "SIGUSR1" + | "SIGUSR2"; + +type ActorData = + | { encoding: "utf8"; data: string } + | { encoding: "base64"; data: string }; + +interface LanguageExecutionOptions { + executionId?: string; + createIfMissing?: boolean; + cwd?: string; + env?: Record; + args?: string[]; + stdin?: string | Uint8Array; + /** Optional wall-clock deadline. Omitted means no caller deadline. */ + timeoutMs?: number; + detached?: boolean; + pty?: { cols?: number; rows?: number }; + + // Core client only. + signal?: AbortSignal; + onStdout?: (chunk: Uint8Array) => void; + onStderr?: (chunk: Uint8Array) => void; +} + +interface InlineExecutionOptions extends LanguageExecutionOptions { + inputs?: Record; +} + +type AttachedInlineExecutionOptions = Omit< + InlineExecutionOptions, + "detached" +>; + +interface ActorLanguageExecutionOptions + extends Omit< + LanguageExecutionOptions, + "stdin" | "signal" | "onStdout" | "onStderr" + > { + stdin?: ActorData; +} + +interface ActorInlineExecutionOptions + extends ActorLanguageExecutionOptions { + inputs?: Record; +} + +type ExecutionState = + | "creating" + | "idle" + | "running" + | "resetting" + | "deleting" + | "failed"; + +type ExecutionOutcome = + | "succeeded" + | "failed" + | "cancelled" + | "timed_out"; + +interface ExecutionDescriptor { + executionId: string; + generation: number; + state: ExecutionState; + retainedLanguage?: "javascript" | "python"; + processId?: string; + pid?: number; + createdAtMs: number; + lastStartedAtMs?: number; + lastCompletedAtMs?: number; + lastOutcome?: ExecutionOutcome; + lastExitCode?: number; +} + +interface DetachedExecution extends ExecutionDescriptor { + detached: true; +} + +interface ExecutionErrorData { + code: string; + name: string; + message: string; + stack?: string; + details?: JsonValue; +} + +interface CodeExecutionResultBase { + executionId: string; + generation: number; + detached: false; + exitCode?: number; + stdout: string; + stderr: string; + stdoutTruncated: boolean; + stderrTruncated: boolean; + outputs: CodeOutput[]; +} + +type CodeExecutionResult = + | (CodeExecutionResultBase & { + outcome: "succeeded"; + error?: never; + }) + | (CodeExecutionResultBase & { + outcome: "failed" | "cancelled" | "timed_out"; + error: ExecutionErrorData; + }); + +type CodeOutput = + | { type: "text" | "markdown" | "html" | "svg"; data: string } + | { type: "json"; data: JsonValue } + | { type: "png" | "jpeg"; data: string; encoding: "base64" }; + +type CodeEvaluationResult = + | (CodeExecutionResult & { + outcome: "succeeded"; + value: T; + }) + | (CodeExecutionResult & { + outcome: "failed" | "cancelled" | "timed_out"; + value?: never; + }); + +interface ExecutionOutputEvent { + executionId: string; + generation: number; + processId?: string; + sequence: number; + channel: "stdout" | "stderr" | "pty"; + chunk: TChunk; + timestampMs: number; +} + +interface ExecutionOutputPage { + executionId: string; + generation: number; + events: ExecutionOutputEvent[]; + nextCursor: string; + hasMore: boolean; + truncated: boolean; +} + +interface ExecutionCompletedEventBase { + executionId: string; + generation: number; + exitCode?: number; +} + +type ExecutionCompletedEvent = + | (ExecutionCompletedEventBase & { + outcome: "succeeded"; + error?: never; + }) + | (ExecutionCompletedEventBase & { + outcome: "failed" | "cancelled" | "timed_out"; + error: ExecutionErrorData; + }); + +executions.get(executionId: string): Promise; +executions.list(): Promise; +executions.wait(executionId: string): Promise; +executions.cancel(executionId: string): Promise; +executions.signal( + executionId: string, + signal: ExecutionSignal, +): Promise; +executions.reset(executionId: string): Promise; +executions.delete(executionId: string): Promise; +executions.writeStdin( + executionId: string, + data: string | Uint8Array, +): Promise; +executions.closeStdin(executionId: string): Promise; +executions.resizePty( + executionId: string, + size: { cols: number; rows: number }, +): Promise; +executions.readOutput( + executionId: string, + options?: { + cursor?: string; + limit?: number; + }, +): Promise; + +// Core client subscriptions. The actor broadcasts equivalent events. +onExecutionOutput( + executionId: string, + handler: (event: ExecutionOutputEvent) => void, +): () => void; +onExecutionCompleted( + executionId: string, + handler: (event: ExecutionCompletedEvent) => void, +): () => void; +``` + +`inputs` is exposed to guest code as a read-only top-level `inputs` object and +is serialized through the protocol rather than interpolated into source. +Evaluation values must be JSON-serializable; the generic type parameter is a +caller-side assertion, not a serializer. + +`executions.cancel` performs bounded graceful cancellation followed by forced +termination. `executions.signal` sends an explicit POSIX signal to the active +execution-owned process group. `executions.delete` accepts only an idle or +failed execution; callers cancel and wait before deleting active work. + +Only inline `javascript.execute`, `javascript.evaluate`, +`javascript.typescript.execute`, `javascript.typescript.evaluate`, +`python.execute`, and `python.evaluate` operations retain language memory. +JavaScript and TypeScript share one retained realm; Python uses a separate +interpreter and cannot share that execution. File, module, type-check, +arbitrary-command, npm, and Python-install operations use a fresh process and +do not mutate retained language memory. Every operation in a VM sees the same +filesystem, so files and installed packages remain visible across executions +until the VM filesystem is removed. + +The `outcome` field is the sole result discriminator. A succeeded evaluation +has `value` and no `error`; failed, cancelled, and timed-out operations have a +serializable `error`. Validation, transport, and other failures before +admission throw typed host errors. Once admitted, an +operation always reaches a retained result and completion event, including +sidecar enforcement failures. Buffered `stdout` and `stderr` are UTF-8 strings; +invalid byte sequences use the Unicode replacement character, and their +truncation flags are always explicit. `outputs` contains bounded serializable +display values and is empty for operations that produce none. Output events +retain exact bytes as `Uint8Array` in core and tagged `ActorData` on the actor +wire. With a PTY, merged terminal bytes use the `pty` event channel and the +`onStdout`/`stdout` attached surface; `stderr` is empty. + +For methods supporting detached operation, omitted or `detached: false` +returns `CodeExecutionResult`, while `detached: true` returns +`DetachedExecution`. No language-specific spawn twins are added. The existing +managed `process.spawn` API remains distinct and returns a PID. + +Actor actions accept only actor-safe options and tagged `ActorData`. They do +not carry `AbortSignal`, callbacks, `Uint8Array`, guest proxies, or class +instances. Core callback subscriptions become actor `executionOutput` and +`executionCompleted` events. Actor output events and +`executions.readOutput` use `ExecutionOutputEvent` and +`ExecutionOutputPage` respectively; core uses their default +`Uint8Array` chunk type. + +`timeoutMs` is an operation property shared by Bash and every language helper. +It starts when the sidecar admits the operation and covers staging, +transpilation or compilation, guest execution, and result collection. Omission +means no caller-configured deadline. Sidecar safety watchdogs and resource +limits remain bounded independently. Timeout cancellation, process-tree +termination, and cleanup are sidecar-owned; cleanup has its own bounded grace +period so a deadline cannot silently abandon resources. + +`timeoutMs` is the only execution-specific limit option introduced by this +API. CPU, memory, process, filesystem, networking, output, and other resource +policies come from the owning VM's existing AgentOS core configuration. The +language helpers inherit and report those limits; they do not duplicate them +under language-specific names or add resource-usage result fields. + +The core `AbortSignal` cancels only the operation submitted by that call. If it +fires before admission, the request is withdrawn; after admission it has the +same operation-level effect as `executions.cancel`. `executions.cancel` targets +the sole active operation and throws a typed state error when the execution is +idle. Cancelling or timing out an inline JavaScript, TypeScript, or Python +operation invalidates that execution's retained language memory before +returning it to idle. Its generation already identifies the admitted operation. +Cancelling a fresh-process operation leaves retained language memory unchanged. + +Public state transitions are: + +| From | Operation | To | +| --- | --- | --- | +| absent | admitted create and operation | `creating` → `running` | +| `idle` | admitted operation | `running` | +| `running` | success, guest failure, cancellation, or timeout | `idle` | +| `running` | sidecar enforcement or cleanup failure | `failed` | +| `idle` or `failed` | reset | `resetting` → `idle` | +| `idle` or `failed` | delete | `deleting` → absent | + +Reset and delete reject an active execution. A failed execution accepts only +inspection, output replay, reset, or deletion. + +## 5. Core API + +All execution semantics are implemented once in the sidecar/runtime. Core and +actor clients only serialize input, route events, and expose typed results. + +### 5.1 Arbitrary commands and managed processes + +The process namespace owns configured-shell commands, exact argv execution, and +managed child processes; no Bash-specific method is added: + +```ts +process.exec( + command: string, + options?: LanguageExecutionOptions & { detached?: false }, +): Promise; +process.exec( + command: string, + options: LanguageExecutionOptions & { detached: true }, +): Promise; + +process.execFile( + command: string, + args?: readonly string[], + options?: Omit & { detached?: false }, +): Promise; +process.execFile( + command: string, + args: readonly string[], + options: Omit & { detached: true }, +): Promise; + +process.spawn( + command: string, + args?: readonly string[], + options?: SpawnOptions, +): { pid: number }; +``` + +`process.exec` retains configured-shell command-string semantics. +`process.execFile` is the injection-safe arbitrary-command API. +`process.spawn` retains the established managed-process contract: it returns a +PID and is controlled with the remaining `process.*` methods. It is not an +alias for a detached execution. No language-specific `spawn*` methods are +added. + +### 5.2 Retained execution state + +Reusing an execution ID retains supported language state: + +```ts +await agentOs.javascript.execute("globalThis.answer = 40", { + executionId: "analysis", + createIfMissing: true, +}); + +const result = await agentOs.javascript.evaluate("answer + 2", { + executionId: "analysis", +}); +``` + +The second operation returns `42`. JavaScript and TypeScript reuse one realm; +Python reuses interpreter globals. A second operation cannot be submitted until +the first is terminal. `inputs` is replaced per operation and is not retained. + +An operation without `executionId` creates a fresh execution with a generated +ID. The returned result includes that ID, so callers may reuse it later. Idle +lifetime, retained memory, output, and execution count are bounded. Expiration +or deletion releases retained resources and invalidates the ID. + +| Methods | Retained language memory | Process behavior | +| --- | --- | --- | +| inline JS/TS execute and evaluate | Shared JavaScript realm | Runs in the execution-owned realm | +| inline Python execute and evaluate | Python interpreter globals | Runs in the execution-owned interpreter | +| JS/TS/Python file and Python module | None | Fresh process | +| Bash, argv, npm, installs, and type checks | None | Fresh process or sidecar compiler operation | + +All rows share the VM filesystem. Per-operation `cwd`, `env`, and `args` do not +persist; guest changes to the process cwd or environment are restored after the +operation, and a shell `cd` or `export` affects only that shell operation. + +Retained JavaScript is a cell/REPL contract, not merely reuse of the same V8 +global object. Top-level `let`, `const`, `var`, function, class, and import +bindings created by a successful JavaScript or TypeScript operation are +available to later inline operations. The implementation must not accomplish +this by replaying earlier source and repeating side effects. A syntax or +transpilation failure does not mutate retained bindings; a guest exception may +leave mutations performed before the exception visible, matching interactive +interpreter behavior. Python follows the same partial-mutation rule. A hard +interruption from cancellation or timeout clears retained memory because its +consistency cannot be guaranteed, but keeps the retained language assignment; +`executions.reset` clears both memory and the language assignment so another +language can claim the execution. + +### 5.3 JavaScript and TypeScript + +```ts +interface JavaScriptExecutionOptions extends InlineExecutionOptions { + format?: "module" | "commonjs"; // default: "module" + filePath?: string; +} + +type JavaScriptEvaluationOptions = Omit< + JavaScriptExecutionOptions, + "detached" +>; + +interface TypeScriptExecutionOptions extends InlineExecutionOptions { + filePath?: string; + tsconfigPath?: string; + compilerOptions?: Record; +} + +type TypeScriptEvaluationOptions = Omit< + TypeScriptExecutionOptions, + "detached" +>; + +interface TypeScriptFileExecutionOptions extends LanguageExecutionOptions { + tsconfigPath?: string; + compilerOptions?: Record; +} + +interface TypeScriptCheckOptions { + executionId?: string; + createIfMissing?: boolean; + cwd?: string; + filePath?: string; + tsconfigPath?: string; + compilerOptions?: Record; + timeoutMs?: number; + signal?: AbortSignal; // Core client only. +} + +interface TypeScriptDiagnostic { + code: number; + category: "error" | "warning" | "suggestion" | "message"; + message: string; + filePath?: string; + line?: number; + column?: number; +} + +type TypeScriptCheckResult = + | (CodeExecutionResult & { + outcome: "succeeded"; + hasErrors: boolean; + diagnostics: TypeScriptDiagnostic[]; + }) + | (CodeExecutionResult & { + outcome: "failed" | "cancelled" | "timed_out"; + hasErrors?: never; + diagnostics: TypeScriptDiagnostic[]; + }); + +javascript.execute( + source: string, + options?: JavaScriptExecutionOptions & { detached?: false }, +): Promise; +javascript.execute( + source: string, + options: JavaScriptExecutionOptions & { detached: true }, +): Promise; + +javascript.evaluate( + expression: string, + options?: JavaScriptEvaluationOptions, +): Promise>; + +javascript.executeFile( + path: string, + options?: LanguageExecutionOptions & { detached?: false }, +): Promise; +javascript.executeFile( + path: string, + options: LanguageExecutionOptions & { detached: true }, +): Promise; + +javascript.typescript.execute( + source: string, + options?: TypeScriptExecutionOptions & { detached?: false }, +): Promise; +javascript.typescript.execute( + source: string, + options: TypeScriptExecutionOptions & { detached: true }, +): Promise; + +javascript.typescript.evaluate( + expression: string, + options?: TypeScriptEvaluationOptions, +): Promise>; + +javascript.typescript.executeFile( + path: string, + options?: TypeScriptFileExecutionOptions & { detached?: false }, +): Promise; +javascript.typescript.executeFile( + path: string, + options: TypeScriptFileExecutionOptions & { detached: true }, +): Promise; + +javascript.typescript.check( + source: string, + options?: TypeScriptCheckOptions, +): Promise; +``` + +TypeScript execute/evaluate methods transpile without semantic type checking. +They may fail for invalid syntax or an emit failure. Users call +`javascript.typescript.check` explicitly when they want diagnostics. There is no +`checkTypes` option and no public `emitTypeScript` or `compileTypeScript` API. +For check results, semantic type errors set `hasErrors: true` while the +operation outcome remains `succeeded`; the outcome is `failed` only when the +checker itself cannot complete. Diagnostic lines and columns are one-based. + +`filePath` on inline source is diagnostic and module-resolution identity; it +does not read that path. File methods are unambiguous for reading and running +existing files. + +### 5.4 npm workflows + +```ts +interface NpmProjectInstallOptions + extends Omit { + frozen?: boolean; +} + +interface NpmPackageInstallOptions + extends Omit { + dev?: boolean; + global?: boolean; +} + +interface NpmScriptOptions extends LanguageExecutionOptions {} + +javascript.npm.install( + options?: NpmProjectInstallOptions, +): Promise; +javascript.npm.install( + packages: string | string[], + options?: NpmPackageInstallOptions, +): Promise; + +javascript.npm.runScript( + script: string, + options?: NpmScriptOptions & { detached?: false }, +): Promise; +javascript.npm.runScript( + script: string, + options: NpmScriptOptions & { detached: true }, +): Promise; +``` + +The options-only install overload installs the project in `cwd`. With +`frozen: true`, it requires the existing npm lockfile and performs a clean, +lockfile-exact install equivalent to `npm ci`; combining `frozen` with named or +global packages is invalid. Named packages are passed as separate argv entries. +`dev` and `global` cover stable install intents; uncommon npm flags remain +available through `process.execFile`. + +`javascript.npm.runScript("build", { args: ["--watch"] })` maps to +`npm run build -- --watch`. npm owns the script's shell semantics; AgentOS does +not construct an outer shell command. + +### 5.5 Python + +```ts +interface PythonInstallOptions + extends Omit { + upgrade?: boolean; + requirementsFile?: string; + indexUrl?: string; + extraIndexUrls?: string[]; +} + +python.execute( + source: string, + options?: InlineExecutionOptions & { detached?: false }, +): Promise; +python.execute( + source: string, + options: InlineExecutionOptions & { detached: true }, +): Promise; + +python.evaluate( + expression: string, + options?: AttachedInlineExecutionOptions, +): Promise>; + +python.executeFile( + path: string, + options?: LanguageExecutionOptions & { detached?: false }, +): Promise; +python.executeFile( + path: string, + options: LanguageExecutionOptions & { detached: true }, +): Promise; + +python.executeModule( + module: string, + options?: LanguageExecutionOptions & { detached?: false }, +): Promise; +python.executeModule( + module: string, + options: LanguageExecutionOptions & { detached: true }, +): Promise; + +python.install( + options?: PythonInstallOptions, +): Promise; +python.install( + packages: string | string[], + options?: PythonInstallOptions, +): Promise; +``` + +`python.executeModule("http.server", { args: ["8000"] })` is the typed +equivalent of `python -m http.server 8000`. + +The options-only install overload installs the project in `cwd` unless +`requirementsFile` is supplied. Providing both named packages and a +requirements file is invalid. + +Inline Python execution and evaluation support top-level `await`, `async for`, +and `async with`. Awaited work is part of the operation and subject to its +timeout. The API does not promise that background or otherwise unawaited +`asyncio` tasks continue between operations; reset, deletion, expiry, or +timeout cancels execution-owned asynchronous work. + +npm and Python installs modify the VM-wide filesystem rather than an +execution-local environment. Installed packages are visible to every execution +in that VM and survive execution reset or deletion. Python requirements files +and pinned package specs provide the reproducible Python workflow; the API does +not invent a cross-ecosystem `frozen` meaning for pip. The sidecar serializes +package mutations at VM scope so installs submitted through different +executions cannot corrupt shared package-manager state. One mutation is +admitted at a time; another install submitted while it is active fails +immediately with `ExecutionBusyError`, so callers can wait for the active +execution before retrying without an implicit unbounded install queue. + +## 6. Actor surface and serialization + +The actor exports the same nested action tree as Core: + +```text +process.exec +process.execFile +process.spawn +process.get +process.list +process.listAll +process.tree +process.wait +process.stop +process.kill +process.writeStdin +process.closeStdin +javascript.execute +javascript.evaluate +javascript.executeFile +javascript.typescript.execute +javascript.typescript.evaluate +javascript.typescript.executeFile +javascript.typescript.check +javascript.typescript.checkProject +javascript.npm.install +javascript.npm.runScript +javascript.npm.runPackage +python.execute +python.evaluate +python.executeFile +python.executeModule +python.install +executions.get +executions.list +executions.wait +executions.cancel +executions.writeStdin +executions.closeStdin +executions.readOutput +executions.reset +executions.delete +executions.signal +executions.resizePty +terminal.open +terminal.write +terminal.resize +terminal.wait +terminal.close +filesystem.readFile +filesystem.writeFile +filesystem.readFiles +filesystem.writeFiles +filesystem.stat +filesystem.mkdir +filesystem.readdir +filesystem.readdirEntries +filesystem.readdirRecursive +filesystem.exists +filesystem.move +filesystem.remove +filesystem.export +filesystem.mount +filesystem.unmount +filesystem.listMounts +network.httpRequest +software.list +software.link +agents.list +sessions.open +sessions.get +sessions.list +sessions.delete +sessions.unload +sessions.prompt +sessions.cancelPrompt +sessions.respondPermission +sessions.readHistory +sessions.getConfig +sessions.setConfigOption +sessions.getCapabilities +sessions.getAgentInfo +cron.schedule +cron.list +cron.cancel +``` + +RivetKit represents these on the wire with the same dotted names. Every action +maps to its corresponding sidecar request and does not reconstruct commands. +The actor-only preview URL actions remain outside this shared tree. + +Actor signatures are derived mechanically from the core signatures: replace +`LanguageExecutionOptions` with `ActorLanguageExecutionOptions`, replace binary +chunks with `ActorData`, and omit core-only `AbortSignal` and callback fields. +This changes only host representation, never admission or runtime behavior. + +The core and actor have matching behavior but intentionally different host +types: + +| Core | Actor wire | +| --- | --- | +| `AbortSignal` | explicit `executions.cancel` action | +| callback functions | `executionOutput` and `executionCompleted` events | +| `Uint8Array` | tagged UTF-8/base64 `ActorData` | +| typed error class | serializable `{ code, message, details }` | +| caller-side generic evaluation type | runtime `JsonValue` | + +Nested serializable option objects such as `pty: { cols, rows }` are allowed. +Actor methods must reject unsupported rich values rather than silently +dropping them. Every JavaScript +numeric input corresponding to Bare `u16`, `u32`, or `u64` is integer- and +range-checked. Every Bare integer exposed as a JavaScript `number` is checked +before conversion and must remain within JavaScript's safe-integer range; +bounded counts and retention make that enforceable. + +The actor automatically acquires an internal keepalive lease before submitting +any attached or detached operation. It holds the lease until the operation is +terminal, all prior output and the completion event have been routed, and +required cleanup has finished; a pre-admission failure releases it immediately. +This behavior has no public option or action. An idle execution does not keep +the actor alive. If hibernation disposes the VM, its in-memory executions +expire; a later lookup returns +`ExecutionNotFoundError` unless the caller explicitly requests creation again. + +## 7. Additional language and lifecycle APIs + +### 7.1 Project TypeScript checking + +```ts +javascript.typescript.checkProject(options?: { + executionId?: string; + createIfMissing?: boolean; + cwd?: string; + tsconfigPath?: string; + timeoutMs?: number; + signal?: AbortSignal; // Core client only. +}): Promise; +``` + +This checks the complete project graph selected by `tsconfig.json` and does not +emit JavaScript. + +### 7.2 One-shot npm package binaries + +```ts +javascript.npm.runPackage( + packageSpec: string, + options?: LanguageExecutionOptions & { detached?: false } & { + binary?: string; + }, +): Promise; +javascript.npm.runPackage( + packageSpec: string, + options: LanguageExecutionOptions & { detached: true } & { + binary?: string; + }, +): Promise; +``` + +This covers the stable `npm exec`/`npx` intent. `binary` handles packages whose +executable differs from the package name. It does not expand into npm init, +pack, publish, audit, outdated, or every npm subcommand. + +### 7.3 Serializable display outputs + +All execution and evaluation results include the bounded +`outputs: CodeOutput[]` field defined in section 4.3. Bash, package, and other +operations without rich displays return an empty array. These are plain tagged +display values, not live guest objects. Each item and the aggregate array have +explicit byte limits. HTML and SVG are untrusted guest output; renderers must +sanitize them or place them in an appropriate isolated display boundary. + +### 7.4 Output replay, reset, deletion, signals, and PTY + +The shared lifecycle includes the methods already defined in section 4.3: + +```text +executions.readOutput +executions.reset +executions.delete +executions.signal +executions.resizePty +``` + +`executions.readOutput` pages the current or most recently completed operation's +retained events with an opaque cursor. Omitting the cursor starts at its oldest +retained event. `nextCursor` is always returned and can be persisted even when +`hasMore` is false so a later call resumes from the same point while that +operation is still active. `limit` is a bounded event count; event chunk size is +also bounded independently. `truncated` reports that earlier output expired +from bounded retention. A new operation, reset, deletion, or recreation +invalidates old cursors with `ExecutionOutputCursorExpiredError` rather than +mixing output from different generations. Replay returns immediately; live +delivery uses the output subscription/event surface. +`executions.reset` accepts only an idle or failed execution, cancels +execution-owned background tasks, clears the retained language assignment and +state, results, and output, increments generation, and leaves filesystem +changes and installed packages untouched. + +`executions.delete` requires an idle or failed execution. It removes retained +state, results, and output immediately. It does not implicitly cancel active +work or revert VM filesystem changes. + +`executions.signal` targets the active execution-owned process group. A present +`pty` option allocates a terminal; `executions.resizePty` changes its dimensions. +PTY output uses the `pty` channel because terminal output merges stdout and +stderr. + +`executions.writeStdin` resolves only after the sidecar has accepted the bytes +into its bounded input path, providing backpressure. It rejects input after +stdin is closed or when the active operation has no writable stdin. + +## 8. Underlying mappings + +Commands below are sidecar implementation details. Public callers provide +typed arguments; the sidecar uses argv invocation and does not construct an +outer shell string except for `process.exec`, whose purpose is configured-shell +execution. + +| Public method | Sidecar operation | Shell involved | +| --- | --- | --- | +| `process.exec` | Submit configured-shell command to the execution | Yes | +| `process.execFile` | Submit exact command and argv | No | +| `process.spawn` | Start a managed child process and return its PID | No | +| `executions.get` / `executions.list` | Read bounded execution metadata | No command | +| `executions.wait` | Wait for the sole active operation, or return the last retained result | No command | +| `executions.cancel` | Gracefully terminate, then force the active process tree | No command | +| `executions.signal` | Signal the active execution-owned process group | No command | +| `executions.reset` | Clear retained state/output and increment generation | No command | +| `executions.delete` | Remove an idle execution and retained data | No command | +| `executions.readOutput` | Page bounded retained output | No command | +| `executions.writeStdin` / `executions.closeStdin` | Route input to the active process or terminal | No command | +| `executions.resizePty` | Resize the active terminal | No command | +| `javascript.execute` | Stage and run JS or evaluate it in the retained JS realm | No | +| `javascript.evaluate` | Evaluate in fresh or retained JS state and serialize the value | No | +| `javascript.executeFile` | `node ...args` | No | +| `javascript.typescript.execute` | Transpile internally, then execute emitted JS | No | +| `javascript.typescript.evaluate` | Transpile internally, evaluate, and serialize | No | +| `javascript.typescript.executeFile` | Project-aware transpile and entry execution | No | +| `javascript.typescript.check` / `javascript.typescript.checkProject` | Internal TypeScript compiler API | No command | +| `javascript.npm.install` | `npm install ...`, or `npm ci` for a frozen project install | No | +| `javascript.npm.runScript` | `npm run + diff --git a/packages/runtime-browser/tests/browser/fixtures/frontend/converged-harness.html b/packages/runtime-browser/tests/browser/fixtures/frontend/converged-harness.html index 8abc901e43..9331a88290 100644 --- a/packages/runtime-browser/tests/browser/fixtures/frontend/converged-harness.html +++ b/packages/runtime-browser/tests/browser/fixtures/frontend/converged-harness.html @@ -2,10 +2,10 @@ - secure-exec converged sidecar harness + agentos converged sidecar harness
loading
- + diff --git a/packages/runtime-browser/tests/browser/fixtures/frontend/converged-runtime-harness.entry.ts b/packages/runtime-browser/tests/browser/fixtures/frontend/converged-runtime-harness.entry.ts index 6209b8b9fb..b605506088 100644 --- a/packages/runtime-browser/tests/browser/fixtures/frontend/converged-runtime-harness.entry.ts +++ b/packages/runtime-browser/tests/browser/fixtures/frontend/converged-runtime-harness.entry.ts @@ -131,7 +131,7 @@ async function execConvergedGuest( } as never; const factory = createBrowserRuntimeDriverFactory({ - workerUrl: new URL("/secure-exec-worker.js", window.location.href), + workerUrl: new URL("/agentos-worker.js", window.location.href), convergedSidecar: { loadSidecar, config }, }); diff --git a/packages/runtime-browser/tests/browser/fixtures/frontend/converged-runtime-harness.html b/packages/runtime-browser/tests/browser/fixtures/frontend/converged-runtime-harness.html index 5ec93ed204..b064585bed 100644 --- a/packages/runtime-browser/tests/browser/fixtures/frontend/converged-runtime-harness.html +++ b/packages/runtime-browser/tests/browser/fixtures/frontend/converged-runtime-harness.html @@ -2,10 +2,10 @@ - secure-exec converged runtime harness + agentos converged runtime harness
loading
- + diff --git a/packages/runtime-browser/tests/browser/harness.smoke.spec.ts b/packages/runtime-browser/tests/browser/harness.smoke.spec.ts index e434b056cd..f17d295ee7 100644 --- a/packages/runtime-browser/tests/browser/harness.smoke.spec.ts +++ b/packages/runtime-browser/tests/browser/harness.smoke.spec.ts @@ -9,7 +9,7 @@ test("playground harness boots a real browser runtime in Chromium", async ({ const result = await smokeHarness(page); expect(result.crossOriginIsolated).toBe(true); - expect(result.workerUrl).toContain("/secure-exec-worker.js"); + expect(result.workerUrl).toContain("/agentos-worker.js"); expect(result.result.code).toBe(0); expect(result.stdio).toEqual([ { diff --git a/packages/runtime-browser/tests/browser/harness.ts b/packages/runtime-browser/tests/browser/harness.ts index e6b58d53fb..5538bcaba5 100644 --- a/packages/runtime-browser/tests/browser/harness.ts +++ b/packages/runtime-browser/tests/browser/harness.ts @@ -76,7 +76,7 @@ export type HarnessSmokeResponse = HarnessExecResponse & { workerUrl: string; }; -type SecureExecBrowserHarness = { +type AgentOsBrowserHarness = { createRuntime( options?: HarnessCreateRuntimeOptions, ): Promise; @@ -109,7 +109,7 @@ type SecureExecBrowserHarness = { declare global { interface Window { - __secureExecBrowserHarness?: SecureExecBrowserHarness; + __agentOsBrowserHarness?: AgentOsBrowserHarness; } } @@ -125,7 +125,7 @@ export async function createRuntime( options?: HarnessCreateRuntimeOptions, ): Promise { return page.evaluate(async (optionsArg) => { - const harness = window.__secureExecBrowserHarness; + const harness = window.__agentOsBrowserHarness; if (!harness) { throw new Error("Browser harness is unavailable on window"); } @@ -141,7 +141,7 @@ export async function execRuntime( ): Promise { return page.evaluate( async ({ runtimeId: runtimeIdArg, code: codeArg, options: optionsArg }) => { - const harness = window.__secureExecBrowserHarness; + const harness = window.__agentOsBrowserHarness; if (!harness) { throw new Error("Browser harness is unavailable on window"); } @@ -156,7 +156,7 @@ export async function disposeRuntime( runtimeId: string, ): Promise { await page.evaluate(async (runtimeIdArg) => { - const harness = window.__secureExecBrowserHarness; + const harness = window.__agentOsBrowserHarness; if (!harness) { return; } @@ -166,7 +166,7 @@ export async function disposeRuntime( export async function disposeAllRuntimes(page: Page): Promise { await page.evaluate(async () => { - const harness = window.__secureExecBrowserHarness; + const harness = window.__agentOsBrowserHarness; if (!harness) { return; } @@ -182,7 +182,7 @@ export async function terminatePendingExec( ): Promise { return page.evaluate( async ({ runtimeId: runtimeIdArg, code: codeArg, delayMs: delayMsArg }) => { - const harness = window.__secureExecBrowserHarness; + const harness = window.__agentOsBrowserHarness; if (!harness) { throw new Error("Browser harness is unavailable on window"); } @@ -206,7 +206,7 @@ export async function signalPendingExec( signal: signalArg, delayMs: delayMsArg, }) => { - const harness = window.__secureExecBrowserHarness; + const harness = window.__agentOsBrowserHarness; if (!harness) { throw new Error("Browser harness is unavailable on window"); } @@ -229,7 +229,7 @@ export async function debugPendingExec( ): Promise { return page.evaluate( async ({ runtimeId: runtimeIdArg, code: codeArg, delayMs: delayMsArg }) => { - const harness = window.__secureExecBrowserHarness; + const harness = window.__agentOsBrowserHarness; if (!harness) { throw new Error("Browser harness is unavailable on window"); } @@ -244,7 +244,7 @@ export async function runtimeDebug( runtimeId: string, ): Promise { return page.evaluate(async (runtimeIdArg) => { - const harness = window.__secureExecBrowserHarness; + const harness = window.__agentOsBrowserHarness; if (!harness) { throw new Error("Browser harness is unavailable on window"); } @@ -254,7 +254,7 @@ export async function runtimeDebug( export async function smokeHarness(page: Page): Promise { return page.evaluate(async () => { - const harness = window.__secureExecBrowserHarness; + const harness = window.__agentOsBrowserHarness; if (!harness) { throw new Error("Browser harness is unavailable on window"); } diff --git a/packages/runtime-browser/tests/browser/runtime-driver.spec.ts b/packages/runtime-browser/tests/browser/runtime-driver.spec.ts index d578c06a69..32f8acc920 100644 --- a/packages/runtime-browser/tests/browser/runtime-driver.spec.ts +++ b/packages/runtime-browser/tests/browser/runtime-driver.spec.ts @@ -36,7 +36,7 @@ test("preserves sync filesystem and module loading parity in a real Chromium wor await createRuntime(page); expect(crossOriginIsolated).toBe(true); - expect(workerUrl).toContain("/secure-exec-worker.js"); + expect(workerUrl).toContain("/agentos-worker.js"); const filesystemRoundTrip = await execRuntime( page, @@ -185,7 +185,7 @@ test("honors string open flags and positional writeSync without losing data", as test("persists browser filesystem data across OPFS-backed runtimes", async ({ page, }) => { - const path = `/secure-exec-opfs-${Date.now()}-${Math.random() + const path = `/agentos-opfs-${Date.now()}-${Math.random() .toString(16) .slice(2)}.txt`; const first = await createRuntime(page, { filesystem: "opfs" }); @@ -1150,7 +1150,7 @@ test("exposes configured virtual process and OS identity in the browser worker", tmpdir: "/browser-tmp", type: "BrowserLinux", release: "9.9.9-browser", - version: "Browser secure-exec build", + version: "Browser agentos build", machine: "browser64", }, }); @@ -1200,7 +1200,7 @@ test("exposes configured virtual process and OS identity in the browser worker", osTmpdir: "/browser-tmp", osType: "BrowserLinux", osRelease: "9.9.9-browser", - osVersion: "Browser secure-exec build", + osVersion: "Browser agentos build", osMachine: "browser64", userInfo: { username: "runner", @@ -1881,11 +1881,11 @@ test("provides browser node:crypto RSA sign and verify parity", async ({ }); }); }); - const oaepCiphertext = crypto.publicEncrypt(publicKeyObject, encodeText("secure-exec-rsa-oaep")); + const oaepCiphertext = crypto.publicEncrypt(publicKeyObject, encodeText("agentos-rsa-oaep")); const oaepPlaintext = crypto.privateDecrypt(privateKeyObject, oaepCiphertext).toString("utf8"); const pkcs1Ciphertext = crypto.publicEncrypt( { key: publicKeyObject, padding: crypto.constants.RSA_PKCS1_PADDING }, - encodeText("secure-exec-rsa-pkcs1"), + encodeText("agentos-rsa-pkcs1"), ); const pkcs1Plaintext = crypto.privateDecrypt( { key: privateKeyObject, padding: crypto.constants.RSA_PKCS1_PADDING }, @@ -1945,8 +1945,8 @@ test("provides browser node:crypto RSA sign and verify parity", async ({ publicAsymmetricKeyType: "rsa", privateAsymmetricKeyType: "rsa", }, - oaepPlaintext: "secure-exec-rsa-oaep", - pkcs1Plaintext: "secure-exec-rsa-pkcs1", + oaepPlaintext: "agentos-rsa-oaep", + pkcs1Plaintext: "agentos-rsa-pkcs1", keyTypes: ["private", "public"], constants: [1, 4], }); diff --git a/packages/runtime-browser/tests/runtime/resolve-module.test.ts b/packages/runtime-browser/tests/runtime/resolve-module.test.ts index 0308be1fd7..5b81f05858 100644 --- a/packages/runtime-browser/tests/runtime/resolve-module.test.ts +++ b/packages/runtime-browser/tests/runtime/resolve-module.test.ts @@ -85,7 +85,7 @@ describe("browser module resolution", () => { expect(loadPolyfill("node:dns/promises")).toContain("promises"); expect(loadPolyfill("dgram")).toContain("_dgramSocketCreateRaw"); expect(loadPolyfill("node:dgram")).toContain("_dgramSocketCreateRaw"); - expect(loadPolyfill("secure-exec:wasi-command-host")).toContain( + expect(loadPolyfill("agentos:wasi-command-host")).toContain( "createWasiCommandHost", ); }); diff --git a/packages/runtime-browser/tests/runtime/wasi-command-bootstrap.test.ts b/packages/runtime-browser/tests/runtime/wasi-command-bootstrap.test.ts index 540aa47fe4..f2ad39f3a8 100644 --- a/packages/runtime-browser/tests/runtime/wasi-command-bootstrap.test.ts +++ b/packages/runtime-browser/tests/runtime/wasi-command-bootstrap.test.ts @@ -21,7 +21,7 @@ describe("wasi command bootstrap", () => { }); expect(source).toContain('require("node:wasi")'); - expect(source).toContain('require("secure-exec:wasi-command-host")'); + expect(source).toContain('require("agentos:wasi-command-host")'); expect(source).toContain('const commandSource = "/commands/sh";'); expect(source).toContain("/commands/sh"); expect(source).toContain("/commands/echo"); diff --git a/packages/runtime-browser/tests/runtime/wasi-command-host.test.ts b/packages/runtime-browser/tests/runtime/wasi-command-host.test.ts index dec968c1b9..2bc1a4e714 100644 --- a/packages/runtime-browser/tests/runtime/wasi-command-host.test.ts +++ b/packages/runtime-browser/tests/runtime/wasi-command-host.test.ts @@ -100,7 +100,7 @@ function loadCommandHostFactory( exports: {} as { createWasiCommandHost?: CommandHostFactory }, }; const source = getRuntimePolyfillCode( - "secure-exec:wasi-command-host", + "agentos:wasi-command-host", processLimits, ); if (!source) throw new Error("command host polyfill was not found"); diff --git a/packages/runtime-core/README.md b/packages/runtime-core/README.md index 29d2fa75a3..2cfb5b4597 100644 --- a/packages/runtime-core/README.md +++ b/packages/runtime-core/README.md @@ -1,6 +1,6 @@ # @rivet-dev/agentos-runtime-core -Raw Secure Exec protocol types and codecs. +Raw AgentOS language execution protocol types and codecs. - `@rivet-dev/agentos-runtime-core/protocol` exports the generated sidecar protocol. - `@rivet-dev/agentos-runtime-core/binary` resolves the published `agentos-native-sidecar` binary. `AGENTOS_NATIVE_SIDECAR_BIN` overrides only this native runtime; the legacy `AGENTOS_SIDECAR_BIN` override remains the fallback. diff --git a/packages/runtime-core/src/event-buffer.ts b/packages/runtime-core/src/event-buffer.ts index 4e2cb9a6b6..db27ba0e89 100644 --- a/packages/runtime-core/src/event-buffer.ts +++ b/packages/runtime-core/src/event-buffer.ts @@ -1,7 +1,4 @@ -import { - fromGeneratedExtEnvelope, - type LiveExtEnvelope, -} from "./ext.js"; +import { fromGeneratedExtEnvelope, type LiveExtEnvelope } from "./ext.js"; import type * as protocol from "./generated-protocol.js"; import { ownershipMatchesSelector, @@ -38,6 +35,8 @@ export type LiveSidecarEventPayload = name: string; detail: Record; } + | { type: "execution_output"; event: protocol.ExecutionOutputEvent } + | { type: "execution_completed"; event: protocol.ExecutionCompletedEvent } | { type: "ext"; envelope: LiveExtEnvelope; @@ -79,6 +78,16 @@ export type LiveSidecarEventSelector = ownership?: LiveOwnershipScope; name?: string; detail?: Record; + } + | { + type: "execution_output"; + ownership?: LiveOwnershipScope; + executionId?: string; + } + | { + type: "execution_completed"; + ownership?: LiveOwnershipScope; + executionId?: string; }; export type LiveSidecarBufferedEventRecord< @@ -216,6 +225,7 @@ function buildBufferKey( ownership?: LiveOwnershipScope; state?: string; processId?: string; + executionId?: string; channel?: string; name?: string; }, @@ -230,6 +240,9 @@ function buildBufferKey( if (options?.processId) { parts.push(`process:${options.processId}`); } + if (options?.executionId) { + parts.push(`execution:${options.executionId}`); + } if (options?.channel) { parts.push(`channel:${options.channel}`); } @@ -239,10 +252,9 @@ function buildBufferKey( return parts.join("|"); } -export function sidecarSelectorMatchesEvent( - selector: LiveSidecarEventSelector, - event: TEvent, -): boolean { +export function sidecarSelectorMatchesEvent< + TEvent extends LiveSidecarEventFrame, +>(selector: LiveSidecarEventSelector, event: TEvent): boolean { if ("any" in selector) { return true; } @@ -299,6 +311,17 @@ export function sidecarSelectorMatchesEvent; + return ( + selector.executionId === undefined || + payload.event.executionId === selector.executionId + ); + } } } @@ -333,6 +356,12 @@ export function sidecarSelectorBufferKey( ownership: selector.ownership, name: selector.name, }); + case "execution_output": + case "execution_completed": + return buildBufferKey(selector.type, { + ownership: selector.ownership, + executionId: selector.executionId, + }); } } @@ -385,7 +414,11 @@ export function fromGeneratedEventPayload( return { type: "ext", envelope: fromGeneratedExtEnvelope(payload.val), - }; + }; + case "ExecutionOutputEvent": + return { type: "execution_output", event: payload.val }; + case "ExecutionCompletedEvent": + return { type: "execution_completed", event: payload.val }; } } @@ -481,6 +514,20 @@ export function sidecarEventBufferKeys( }), ); break; + case "execution_output": + case "execution_completed": + keys.add( + buildBufferKey(event.payload.type, { + executionId: event.payload.event.executionId, + }), + ); + keys.add( + buildBufferKey(event.payload.type, { + ownership: owner, + executionId: event.payload.event.executionId, + }), + ); + break; case "ext": break; } diff --git a/packages/runtime-core/src/generated-protocol.ts b/packages/runtime-core/src/generated-protocol.ts index 5a69a89ec1..e8b91fc083 100644 --- a/packages/runtime-core/src/generated-protocol.ts +++ b/packages/runtime-core/src/generated-protocol.ts @@ -2023,6 +2023,977 @@ export function writeExecuteRequest(bc: bare.ByteCursor, x: ExecuteRequest): voi write25(bc, x.wasmPermissionTier) } +/** + * First-class execution lifecycle. The legacy process request above remains an + * internal kernel transport while clients migrate in lockstep to these semantic + * operations; language/package command construction belongs to the sidecar. + */ +export enum ExecutionState { + Creating = "Creating", + Idle = "Idle", + Running = "Running", + Resetting = "Resetting", + Deleting = "Deleting", + Failed = "Failed", +} + +export function readExecutionState(bc: bare.ByteCursor): ExecutionState { + const offset = bc.offset + const tag = bare.readU8(bc) + switch (tag) { + case 0: + return ExecutionState.Creating + case 1: + return ExecutionState.Idle + case 2: + return ExecutionState.Running + case 3: + return ExecutionState.Resetting + case 4: + return ExecutionState.Deleting + case 5: + return ExecutionState.Failed + default: { + bc.offset = offset + throw new bare.BareError(offset, "invalid tag") + } + } +} + +export function writeExecutionState(bc: bare.ByteCursor, x: ExecutionState): void { + switch (x) { + case ExecutionState.Creating: { + bare.writeU8(bc, 0) + break + } + case ExecutionState.Idle: { + bare.writeU8(bc, 1) + break + } + case ExecutionState.Running: { + bare.writeU8(bc, 2) + break + } + case ExecutionState.Resetting: { + bare.writeU8(bc, 3) + break + } + case ExecutionState.Deleting: { + bare.writeU8(bc, 4) + break + } + case ExecutionState.Failed: { + bare.writeU8(bc, 5) + break + } + } +} + +export enum ExecutionOutcome { + Succeeded = "Succeeded", + Failed = "Failed", + Cancelled = "Cancelled", + TimedOut = "TimedOut", +} + +export function readExecutionOutcome(bc: bare.ByteCursor): ExecutionOutcome { + const offset = bc.offset + const tag = bare.readU8(bc) + switch (tag) { + case 0: + return ExecutionOutcome.Succeeded + case 1: + return ExecutionOutcome.Failed + case 2: + return ExecutionOutcome.Cancelled + case 3: + return ExecutionOutcome.TimedOut + default: { + bc.offset = offset + throw new bare.BareError(offset, "invalid tag") + } + } +} + +export function writeExecutionOutcome(bc: bare.ByteCursor, x: ExecutionOutcome): void { + switch (x) { + case ExecutionOutcome.Succeeded: { + bare.writeU8(bc, 0) + break + } + case ExecutionOutcome.Failed: { + bare.writeU8(bc, 1) + break + } + case ExecutionOutcome.Cancelled: { + bare.writeU8(bc, 2) + break + } + case ExecutionOutcome.TimedOut: { + bare.writeU8(bc, 3) + break + } + } +} + +export enum RetainedExecutionLanguage { + JavaScript = "JavaScript", + Python = "Python", +} + +export function readRetainedExecutionLanguage(bc: bare.ByteCursor): RetainedExecutionLanguage { + const offset = bc.offset + const tag = bare.readU8(bc) + switch (tag) { + case 0: + return RetainedExecutionLanguage.JavaScript + case 1: + return RetainedExecutionLanguage.Python + default: { + bc.offset = offset + throw new bare.BareError(offset, "invalid tag") + } + } +} + +export function writeRetainedExecutionLanguage(bc: bare.ByteCursor, x: RetainedExecutionLanguage): void { + switch (x) { + case RetainedExecutionLanguage.JavaScript: { + bare.writeU8(bc, 0) + break + } + case RetainedExecutionLanguage.Python: { + bare.writeU8(bc, 1) + break + } + } +} + +export enum ExecutionStreamChannel { + Stdout = "Stdout", + Stderr = "Stderr", + Pty = "Pty", +} + +export function readExecutionStreamChannel(bc: bare.ByteCursor): ExecutionStreamChannel { + const offset = bc.offset + const tag = bare.readU8(bc) + switch (tag) { + case 0: + return ExecutionStreamChannel.Stdout + case 1: + return ExecutionStreamChannel.Stderr + case 2: + return ExecutionStreamChannel.Pty + default: { + bc.offset = offset + throw new bare.BareError(offset, "invalid tag") + } + } +} + +export function writeExecutionStreamChannel(bc: bare.ByteCursor, x: ExecutionStreamChannel): void { + switch (x) { + case ExecutionStreamChannel.Stdout: { + bare.writeU8(bc, 0) + break + } + case ExecutionStreamChannel.Stderr: { + bare.writeU8(bc, 1) + break + } + case ExecutionStreamChannel.Pty: { + bare.writeU8(bc, 2) + break + } + } +} + +export enum JavaScriptModuleFormat { + Module = "Module", + CommonJs = "CommonJs", +} + +export function readJavaScriptModuleFormat(bc: bare.ByteCursor): JavaScriptModuleFormat { + const offset = bc.offset + const tag = bare.readU8(bc) + switch (tag) { + case 0: + return JavaScriptModuleFormat.Module + case 1: + return JavaScriptModuleFormat.CommonJs + default: { + bc.offset = offset + throw new bare.BareError(offset, "invalid tag") + } + } +} + +export function writeJavaScriptModuleFormat(bc: bare.ByteCursor, x: JavaScriptModuleFormat): void { + switch (x) { + case JavaScriptModuleFormat.Module: { + bare.writeU8(bc, 0) + break + } + case JavaScriptModuleFormat.CommonJs: { + bare.writeU8(bc, 1) + break + } + } +} + +function read26(bc: bare.ByteCursor): boolean | null { + return bare.readBool(bc) ? bare.readBool(bc) : null +} + +function write26(bc: bare.ByteCursor, x: boolean | null): void { + bare.writeBool(bc, x != null) + if (x != null) { + bare.writeBool(bc, x) + } +} + +export type ExecutionIdentityOptions = { + readonly executionId: string | null + readonly createIfMissing: boolean | null +} + +export function readExecutionIdentityOptions(bc: bare.ByteCursor): ExecutionIdentityOptions { + return { + executionId: read0(bc), + createIfMissing: read26(bc), + } +} + +export function writeExecutionIdentityOptions(bc: bare.ByteCursor, x: ExecutionIdentityOptions): void { + write0(bc, x.executionId) + write26(bc, x.createIfMissing) +} + +function read27(bc: bare.ByteCursor): u16 | null { + return bare.readBool(bc) ? bare.readU16(bc) : null +} + +function write27(bc: bare.ByteCursor, x: u16 | null): void { + bare.writeBool(bc, x != null) + if (x != null) { + bare.writeU16(bc, x) + } +} + +export type ExecutionPtyOptions = { + readonly cols: u16 | null + readonly rows: u16 | null +} + +export function readExecutionPtyOptions(bc: bare.ByteCursor): ExecutionPtyOptions { + return { + cols: read27(bc), + rows: read27(bc), + } +} + +export function writeExecutionPtyOptions(bc: bare.ByteCursor, x: ExecutionPtyOptions): void { + write27(bc, x.cols) + write27(bc, x.rows) +} + +function read28(bc: bare.ByteCursor): ReadonlyMap | null { + return bare.readBool(bc) ? read1(bc) : null +} + +function write28(bc: bare.ByteCursor, x: ReadonlyMap | null): void { + bare.writeBool(bc, x != null) + if (x != null) { + write1(bc, x) + } +} + +function read29(bc: bare.ByteCursor): ArrayBuffer | null { + return bare.readBool(bc) ? bare.readData(bc) : null +} + +function write29(bc: bare.ByteCursor, x: ArrayBuffer | null): void { + bare.writeBool(bc, x != null) + if (x != null) { + bare.writeData(bc, x) + } +} + +function read30(bc: bare.ByteCursor): ExecutionPtyOptions | null { + return bare.readBool(bc) ? readExecutionPtyOptions(bc) : null +} + +function write30(bc: bare.ByteCursor, x: ExecutionPtyOptions | null): void { + bare.writeBool(bc, x != null) + if (x != null) { + writeExecutionPtyOptions(bc, x) + } +} + +export type ProcessExecutionOptions = { + readonly identity: ExecutionIdentityOptions + readonly detached: boolean | null + readonly cwd: string | null + readonly env: ReadonlyMap | null + readonly args: readonly string[] + readonly stdin: ArrayBuffer | null + readonly timeoutMs: u64 | null + readonly pty: ExecutionPtyOptions | null +} + +export function readProcessExecutionOptions(bc: bare.ByteCursor): ProcessExecutionOptions { + return { + identity: readExecutionIdentityOptions(bc), + detached: read26(bc), + cwd: read0(bc), + env: read28(bc), + args: read6(bc), + stdin: read29(bc), + timeoutMs: read21(bc), + pty: read30(bc), + } +} + +export function writeProcessExecutionOptions(bc: bare.ByteCursor, x: ProcessExecutionOptions): void { + writeExecutionIdentityOptions(bc, x.identity) + write26(bc, x.detached) + write0(bc, x.cwd) + write28(bc, x.env) + write6(bc, x.args) + write29(bc, x.stdin) + write21(bc, x.timeoutMs) + write30(bc, x.pty) +} + +export type ShellExecutionRequest = { + readonly process: ProcessExecutionOptions + readonly command: string +} + +export function readShellExecutionRequest(bc: bare.ByteCursor): ShellExecutionRequest { + return { + process: readProcessExecutionOptions(bc), + command: bare.readString(bc), + } +} + +export function writeShellExecutionRequest(bc: bare.ByteCursor, x: ShellExecutionRequest): void { + writeProcessExecutionOptions(bc, x.process) + bare.writeString(bc, x.command) +} + +export type ArgvExecutionRequest = { + readonly process: ProcessExecutionOptions + readonly command: string +} + +export function readArgvExecutionRequest(bc: bare.ByteCursor): ArgvExecutionRequest { + return { + process: readProcessExecutionOptions(bc), + command: bare.readString(bc), + } +} + +export function writeArgvExecutionRequest(bc: bare.ByteCursor, x: ArgvExecutionRequest): void { + writeProcessExecutionOptions(bc, x.process) + bare.writeString(bc, x.command) +} + +function read31(bc: bare.ByteCursor): JavaScriptModuleFormat | null { + return bare.readBool(bc) ? readJavaScriptModuleFormat(bc) : null +} + +function write31(bc: bare.ByteCursor, x: JavaScriptModuleFormat | null): void { + bare.writeBool(bc, x != null) + if (x != null) { + writeJavaScriptModuleFormat(bc, x) + } +} + +function read32(bc: bare.ByteCursor): JsonUtf8 | null { + return bare.readBool(bc) ? readJsonUtf8(bc) : null +} + +function write32(bc: bare.ByteCursor, x: JsonUtf8 | null): void { + bare.writeBool(bc, x != null) + if (x != null) { + writeJsonUtf8(bc, x) + } +} + +export type JavaScriptExecutionRequest = { + readonly process: ProcessExecutionOptions + readonly source: string + readonly format: JavaScriptModuleFormat | null + readonly filePath: string | null + readonly inputs: JsonUtf8 | null +} + +export function readJavaScriptExecutionRequest(bc: bare.ByteCursor): JavaScriptExecutionRequest { + return { + process: readProcessExecutionOptions(bc), + source: bare.readString(bc), + format: read31(bc), + filePath: read0(bc), + inputs: read32(bc), + } +} + +export function writeJavaScriptExecutionRequest(bc: bare.ByteCursor, x: JavaScriptExecutionRequest): void { + writeProcessExecutionOptions(bc, x.process) + bare.writeString(bc, x.source) + write31(bc, x.format) + write0(bc, x.filePath) + write32(bc, x.inputs) +} + +export type JavaScriptEvaluationRequest = { + readonly process: ProcessExecutionOptions + readonly expression: string + readonly format: JavaScriptModuleFormat | null + readonly filePath: string | null + readonly inputs: JsonUtf8 | null +} + +export function readJavaScriptEvaluationRequest(bc: bare.ByteCursor): JavaScriptEvaluationRequest { + return { + process: readProcessExecutionOptions(bc), + expression: bare.readString(bc), + format: read31(bc), + filePath: read0(bc), + inputs: read32(bc), + } +} + +export function writeJavaScriptEvaluationRequest(bc: bare.ByteCursor, x: JavaScriptEvaluationRequest): void { + writeProcessExecutionOptions(bc, x.process) + bare.writeString(bc, x.expression) + write31(bc, x.format) + write0(bc, x.filePath) + write32(bc, x.inputs) +} + +export type JavaScriptFileExecutionRequest = { + readonly process: ProcessExecutionOptions + readonly path: string +} + +export function readJavaScriptFileExecutionRequest(bc: bare.ByteCursor): JavaScriptFileExecutionRequest { + return { + process: readProcessExecutionOptions(bc), + path: bare.readString(bc), + } +} + +export function writeJavaScriptFileExecutionRequest(bc: bare.ByteCursor, x: JavaScriptFileExecutionRequest): void { + writeProcessExecutionOptions(bc, x.process) + bare.writeString(bc, x.path) +} + +export type TypeScriptExecutionRequest = { + readonly process: ProcessExecutionOptions + readonly source: string + readonly filePath: string | null + readonly tsconfigPath: string | null + readonly compilerOptions: JsonUtf8 | null + readonly inputs: JsonUtf8 | null +} + +export function readTypeScriptExecutionRequest(bc: bare.ByteCursor): TypeScriptExecutionRequest { + return { + process: readProcessExecutionOptions(bc), + source: bare.readString(bc), + filePath: read0(bc), + tsconfigPath: read0(bc), + compilerOptions: read32(bc), + inputs: read32(bc), + } +} + +export function writeTypeScriptExecutionRequest(bc: bare.ByteCursor, x: TypeScriptExecutionRequest): void { + writeProcessExecutionOptions(bc, x.process) + bare.writeString(bc, x.source) + write0(bc, x.filePath) + write0(bc, x.tsconfigPath) + write32(bc, x.compilerOptions) + write32(bc, x.inputs) +} + +export type TypeScriptEvaluationRequest = { + readonly process: ProcessExecutionOptions + readonly expression: string + readonly filePath: string | null + readonly tsconfigPath: string | null + readonly compilerOptions: JsonUtf8 | null + readonly inputs: JsonUtf8 | null +} + +export function readTypeScriptEvaluationRequest(bc: bare.ByteCursor): TypeScriptEvaluationRequest { + return { + process: readProcessExecutionOptions(bc), + expression: bare.readString(bc), + filePath: read0(bc), + tsconfigPath: read0(bc), + compilerOptions: read32(bc), + inputs: read32(bc), + } +} + +export function writeTypeScriptEvaluationRequest(bc: bare.ByteCursor, x: TypeScriptEvaluationRequest): void { + writeProcessExecutionOptions(bc, x.process) + bare.writeString(bc, x.expression) + write0(bc, x.filePath) + write0(bc, x.tsconfigPath) + write32(bc, x.compilerOptions) + write32(bc, x.inputs) +} + +export type TypeScriptFileExecutionRequest = { + readonly process: ProcessExecutionOptions + readonly path: string + readonly tsconfigPath: string | null + readonly compilerOptions: JsonUtf8 | null +} + +export function readTypeScriptFileExecutionRequest(bc: bare.ByteCursor): TypeScriptFileExecutionRequest { + return { + process: readProcessExecutionOptions(bc), + path: bare.readString(bc), + tsconfigPath: read0(bc), + compilerOptions: read32(bc), + } +} + +export function writeTypeScriptFileExecutionRequest(bc: bare.ByteCursor, x: TypeScriptFileExecutionRequest): void { + writeProcessExecutionOptions(bc, x.process) + bare.writeString(bc, x.path) + write0(bc, x.tsconfigPath) + write32(bc, x.compilerOptions) +} + +export type TypeScriptCheckRequest = { + readonly identity: ExecutionIdentityOptions + readonly source: string + readonly cwd: string | null + readonly filePath: string | null + readonly tsconfigPath: string | null + readonly compilerOptions: JsonUtf8 | null + readonly timeoutMs: u64 | null +} + +export function readTypeScriptCheckRequest(bc: bare.ByteCursor): TypeScriptCheckRequest { + return { + identity: readExecutionIdentityOptions(bc), + source: bare.readString(bc), + cwd: read0(bc), + filePath: read0(bc), + tsconfigPath: read0(bc), + compilerOptions: read32(bc), + timeoutMs: read21(bc), + } +} + +export function writeTypeScriptCheckRequest(bc: bare.ByteCursor, x: TypeScriptCheckRequest): void { + writeExecutionIdentityOptions(bc, x.identity) + bare.writeString(bc, x.source) + write0(bc, x.cwd) + write0(bc, x.filePath) + write0(bc, x.tsconfigPath) + write32(bc, x.compilerOptions) + write21(bc, x.timeoutMs) +} + +export type TypeScriptProjectCheckRequest = { + readonly identity: ExecutionIdentityOptions + readonly cwd: string | null + readonly tsconfigPath: string | null + readonly timeoutMs: u64 | null +} + +export function readTypeScriptProjectCheckRequest(bc: bare.ByteCursor): TypeScriptProjectCheckRequest { + return { + identity: readExecutionIdentityOptions(bc), + cwd: read0(bc), + tsconfigPath: read0(bc), + timeoutMs: read21(bc), + } +} + +export function writeTypeScriptProjectCheckRequest(bc: bare.ByteCursor, x: TypeScriptProjectCheckRequest): void { + writeExecutionIdentityOptions(bc, x.identity) + write0(bc, x.cwd) + write0(bc, x.tsconfigPath) + write21(bc, x.timeoutMs) +} + +export type NpmProjectInstallRequest = { + readonly identity: ExecutionIdentityOptions + readonly cwd: string | null + readonly env: ReadonlyMap | null + readonly timeoutMs: u64 | null + readonly frozen: boolean | null +} + +export function readNpmProjectInstallRequest(bc: bare.ByteCursor): NpmProjectInstallRequest { + return { + identity: readExecutionIdentityOptions(bc), + cwd: read0(bc), + env: read28(bc), + timeoutMs: read21(bc), + frozen: read26(bc), + } +} + +export function writeNpmProjectInstallRequest(bc: bare.ByteCursor, x: NpmProjectInstallRequest): void { + writeExecutionIdentityOptions(bc, x.identity) + write0(bc, x.cwd) + write28(bc, x.env) + write21(bc, x.timeoutMs) + write26(bc, x.frozen) +} + +export type NpmPackageInstallRequest = { + readonly identity: ExecutionIdentityOptions + readonly cwd: string | null + readonly env: ReadonlyMap | null + readonly timeoutMs: u64 | null + readonly packages: readonly string[] + readonly dev: boolean | null + readonly global: boolean | null +} + +export function readNpmPackageInstallRequest(bc: bare.ByteCursor): NpmPackageInstallRequest { + return { + identity: readExecutionIdentityOptions(bc), + cwd: read0(bc), + env: read28(bc), + timeoutMs: read21(bc), + packages: read6(bc), + dev: read26(bc), + global: read26(bc), + } +} + +export function writeNpmPackageInstallRequest(bc: bare.ByteCursor, x: NpmPackageInstallRequest): void { + writeExecutionIdentityOptions(bc, x.identity) + write0(bc, x.cwd) + write28(bc, x.env) + write21(bc, x.timeoutMs) + write6(bc, x.packages) + write26(bc, x.dev) + write26(bc, x.global) +} + +export type NpmScriptExecutionRequest = { + readonly process: ProcessExecutionOptions + readonly script: string +} + +export function readNpmScriptExecutionRequest(bc: bare.ByteCursor): NpmScriptExecutionRequest { + return { + process: readProcessExecutionOptions(bc), + script: bare.readString(bc), + } +} + +export function writeNpmScriptExecutionRequest(bc: bare.ByteCursor, x: NpmScriptExecutionRequest): void { + writeProcessExecutionOptions(bc, x.process) + bare.writeString(bc, x.script) +} + +export type NpmPackageExecutionRequest = { + readonly process: ProcessExecutionOptions + readonly packageSpec: string + readonly binary: string | null +} + +export function readNpmPackageExecutionRequest(bc: bare.ByteCursor): NpmPackageExecutionRequest { + return { + process: readProcessExecutionOptions(bc), + packageSpec: bare.readString(bc), + binary: read0(bc), + } +} + +export function writeNpmPackageExecutionRequest(bc: bare.ByteCursor, x: NpmPackageExecutionRequest): void { + writeProcessExecutionOptions(bc, x.process) + bare.writeString(bc, x.packageSpec) + write0(bc, x.binary) +} + +export type PythonExecutionRequest = { + readonly process: ProcessExecutionOptions + readonly source: string + readonly inputs: JsonUtf8 | null +} + +export function readPythonExecutionRequest(bc: bare.ByteCursor): PythonExecutionRequest { + return { + process: readProcessExecutionOptions(bc), + source: bare.readString(bc), + inputs: read32(bc), + } +} + +export function writePythonExecutionRequest(bc: bare.ByteCursor, x: PythonExecutionRequest): void { + writeProcessExecutionOptions(bc, x.process) + bare.writeString(bc, x.source) + write32(bc, x.inputs) +} + +export type PythonEvaluationRequest = { + readonly process: ProcessExecutionOptions + readonly expression: string + readonly inputs: JsonUtf8 | null +} + +export function readPythonEvaluationRequest(bc: bare.ByteCursor): PythonEvaluationRequest { + return { + process: readProcessExecutionOptions(bc), + expression: bare.readString(bc), + inputs: read32(bc), + } +} + +export function writePythonEvaluationRequest(bc: bare.ByteCursor, x: PythonEvaluationRequest): void { + writeProcessExecutionOptions(bc, x.process) + bare.writeString(bc, x.expression) + write32(bc, x.inputs) +} + +export type PythonFileExecutionRequest = { + readonly process: ProcessExecutionOptions + readonly path: string +} + +export function readPythonFileExecutionRequest(bc: bare.ByteCursor): PythonFileExecutionRequest { + return { + process: readProcessExecutionOptions(bc), + path: bare.readString(bc), + } +} + +export function writePythonFileExecutionRequest(bc: bare.ByteCursor, x: PythonFileExecutionRequest): void { + writeProcessExecutionOptions(bc, x.process) + bare.writeString(bc, x.path) +} + +export type PythonModuleExecutionRequest = { + readonly process: ProcessExecutionOptions + readonly module: string +} + +export function readPythonModuleExecutionRequest(bc: bare.ByteCursor): PythonModuleExecutionRequest { + return { + process: readProcessExecutionOptions(bc), + module: bare.readString(bc), + } +} + +export function writePythonModuleExecutionRequest(bc: bare.ByteCursor, x: PythonModuleExecutionRequest): void { + writeProcessExecutionOptions(bc, x.process) + bare.writeString(bc, x.module) +} + +export type PythonInstallRequest = { + readonly identity: ExecutionIdentityOptions + readonly cwd: string | null + readonly env: ReadonlyMap | null + readonly timeoutMs: u64 | null + readonly packages: readonly string[] + readonly upgrade: boolean | null + readonly requirementsFile: string | null + readonly indexUrl: string | null + readonly extraIndexUrls: readonly string[] +} + +export function readPythonInstallRequest(bc: bare.ByteCursor): PythonInstallRequest { + return { + identity: readExecutionIdentityOptions(bc), + cwd: read0(bc), + env: read28(bc), + timeoutMs: read21(bc), + packages: read6(bc), + upgrade: read26(bc), + requirementsFile: read0(bc), + indexUrl: read0(bc), + extraIndexUrls: read6(bc), + } +} + +export function writePythonInstallRequest(bc: bare.ByteCursor, x: PythonInstallRequest): void { + writeExecutionIdentityOptions(bc, x.identity) + write0(bc, x.cwd) + write28(bc, x.env) + write21(bc, x.timeoutMs) + write6(bc, x.packages) + write26(bc, x.upgrade) + write0(bc, x.requirementsFile) + write0(bc, x.indexUrl) + write6(bc, x.extraIndexUrls) +} + +export type GetExecutionRequest = { + readonly executionId: string +} + +export function readGetExecutionRequest(bc: bare.ByteCursor): GetExecutionRequest { + return { + executionId: bare.readString(bc), + } +} + +export function writeGetExecutionRequest(bc: bare.ByteCursor, x: GetExecutionRequest): void { + bare.writeString(bc, x.executionId) +} + +export type ListExecutionsRequest = null + +export type WaitExecutionRequest = { + readonly executionId: string +} + +export function readWaitExecutionRequest(bc: bare.ByteCursor): WaitExecutionRequest { + return { + executionId: bare.readString(bc), + } +} + +export function writeWaitExecutionRequest(bc: bare.ByteCursor, x: WaitExecutionRequest): void { + bare.writeString(bc, x.executionId) +} + +export type CancelExecutionRequest = { + readonly executionId: string +} + +export function readCancelExecutionRequest(bc: bare.ByteCursor): CancelExecutionRequest { + return { + executionId: bare.readString(bc), + } +} + +export function writeCancelExecutionRequest(bc: bare.ByteCursor, x: CancelExecutionRequest): void { + bare.writeString(bc, x.executionId) +} + +export type SignalExecutionRequest = { + readonly executionId: string + readonly signal: string +} + +export function readSignalExecutionRequest(bc: bare.ByteCursor): SignalExecutionRequest { + return { + executionId: bare.readString(bc), + signal: bare.readString(bc), + } +} + +export function writeSignalExecutionRequest(bc: bare.ByteCursor, x: SignalExecutionRequest): void { + bare.writeString(bc, x.executionId) + bare.writeString(bc, x.signal) +} + +export type ResetExecutionRequest = { + readonly executionId: string +} + +export function readResetExecutionRequest(bc: bare.ByteCursor): ResetExecutionRequest { + return { + executionId: bare.readString(bc), + } +} + +export function writeResetExecutionRequest(bc: bare.ByteCursor, x: ResetExecutionRequest): void { + bare.writeString(bc, x.executionId) +} + +export type DeleteExecutionRequest = { + readonly executionId: string +} + +export function readDeleteExecutionRequest(bc: bare.ByteCursor): DeleteExecutionRequest { + return { + executionId: bare.readString(bc), + } +} + +export function writeDeleteExecutionRequest(bc: bare.ByteCursor, x: DeleteExecutionRequest): void { + bare.writeString(bc, x.executionId) +} + +export type WriteExecutionStdinRequest = { + readonly executionId: string + readonly chunk: ArrayBuffer +} + +export function readWriteExecutionStdinRequest(bc: bare.ByteCursor): WriteExecutionStdinRequest { + return { + executionId: bare.readString(bc), + chunk: bare.readData(bc), + } +} + +export function writeWriteExecutionStdinRequest(bc: bare.ByteCursor, x: WriteExecutionStdinRequest): void { + bare.writeString(bc, x.executionId) + bare.writeData(bc, x.chunk) +} + +export type CloseExecutionStdinRequest = { + readonly executionId: string +} + +export function readCloseExecutionStdinRequest(bc: bare.ByteCursor): CloseExecutionStdinRequest { + return { + executionId: bare.readString(bc), + } +} + +export function writeCloseExecutionStdinRequest(bc: bare.ByteCursor, x: CloseExecutionStdinRequest): void { + bare.writeString(bc, x.executionId) +} + +export type ResizeExecutionPtyRequest = { + readonly executionId: string + readonly cols: u16 + readonly rows: u16 +} + +export function readResizeExecutionPtyRequest(bc: bare.ByteCursor): ResizeExecutionPtyRequest { + return { + executionId: bare.readString(bc), + cols: bare.readU16(bc), + rows: bare.readU16(bc), + } +} + +export function writeResizeExecutionPtyRequest(bc: bare.ByteCursor, x: ResizeExecutionPtyRequest): void { + bare.writeString(bc, x.executionId) + bare.writeU16(bc, x.cols) + bare.writeU16(bc, x.rows) +} + +export type ReadExecutionOutputRequest = { + readonly executionId: string + readonly cursor: string | null + readonly limit: u32 | null +} + +export function readReadExecutionOutputRequest(bc: bare.ByteCursor): ReadExecutionOutputRequest { + return { + executionId: bare.readString(bc), + cursor: read0(bc), + limit: read2(bc), + } +} + +export function writeReadExecutionOutputRequest(bc: bare.ByteCursor, x: ReadExecutionOutputRequest): void { + bare.writeString(bc, x.executionId) + write0(bc, x.cursor) + write2(bc, x.limit) +} + export type WriteStdinRequest = { readonly processId: string readonly chunk: ArrayBuffer @@ -2095,17 +3066,6 @@ export type GetProcessSnapshotRequest = null export type GetResourceSnapshotRequest = null -function read26(bc: bare.ByteCursor): u16 | null { - return bare.readBool(bc) ? bare.readU16(bc) : null -} - -function write26(bc: bare.ByteCursor, x: u16 | null): void { - bare.writeBool(bc, x != null) - if (x != null) { - bare.writeU16(bc, x) - } -} - export type FindListenerRequest = { readonly host: string | null readonly port: u16 | null @@ -2115,14 +3075,14 @@ export type FindListenerRequest = { export function readFindListenerRequest(bc: bare.ByteCursor): FindListenerRequest { return { host: read0(bc), - port: read26(bc), + port: read27(bc), path: read0(bc), } } export function writeFindListenerRequest(bc: bare.ByteCursor, x: FindListenerRequest): void { write0(bc, x.host) - write26(bc, x.port) + write27(bc, x.port) write0(bc, x.path) } @@ -2134,13 +3094,13 @@ export type FindBoundUdpRequest = { export function readFindBoundUdpRequest(bc: bare.ByteCursor): FindBoundUdpRequest { return { host: read0(bc), - port: read26(bc), + port: read27(bc), } } export function writeFindBoundUdpRequest(bc: bare.ByteCursor, x: FindBoundUdpRequest): void { write0(bc, x.host) - write26(bc, x.port) + write27(bc, x.port) } export type GetSignalStateRequest = { @@ -2339,6 +3299,36 @@ export type RequestPayload = | { readonly tag: "LinkPackageRequest"; readonly val: LinkPackageRequest } | { readonly tag: "ProvidedCommandsRequest"; readonly val: ProvidedCommandsRequest } | { readonly tag: "ListMountsRequest"; readonly val: ListMountsRequest } + | { readonly tag: "ShellExecutionRequest"; readonly val: ShellExecutionRequest } + | { readonly tag: "ArgvExecutionRequest"; readonly val: ArgvExecutionRequest } + | { readonly tag: "JavaScriptExecutionRequest"; readonly val: JavaScriptExecutionRequest } + | { readonly tag: "JavaScriptEvaluationRequest"; readonly val: JavaScriptEvaluationRequest } + | { readonly tag: "JavaScriptFileExecutionRequest"; readonly val: JavaScriptFileExecutionRequest } + | { readonly tag: "TypeScriptExecutionRequest"; readonly val: TypeScriptExecutionRequest } + | { readonly tag: "TypeScriptEvaluationRequest"; readonly val: TypeScriptEvaluationRequest } + | { readonly tag: "TypeScriptFileExecutionRequest"; readonly val: TypeScriptFileExecutionRequest } + | { readonly tag: "TypeScriptCheckRequest"; readonly val: TypeScriptCheckRequest } + | { readonly tag: "TypeScriptProjectCheckRequest"; readonly val: TypeScriptProjectCheckRequest } + | { readonly tag: "NpmProjectInstallRequest"; readonly val: NpmProjectInstallRequest } + | { readonly tag: "NpmPackageInstallRequest"; readonly val: NpmPackageInstallRequest } + | { readonly tag: "NpmScriptExecutionRequest"; readonly val: NpmScriptExecutionRequest } + | { readonly tag: "NpmPackageExecutionRequest"; readonly val: NpmPackageExecutionRequest } + | { readonly tag: "PythonExecutionRequest"; readonly val: PythonExecutionRequest } + | { readonly tag: "PythonEvaluationRequest"; readonly val: PythonEvaluationRequest } + | { readonly tag: "PythonFileExecutionRequest"; readonly val: PythonFileExecutionRequest } + | { readonly tag: "PythonModuleExecutionRequest"; readonly val: PythonModuleExecutionRequest } + | { readonly tag: "PythonInstallRequest"; readonly val: PythonInstallRequest } + | { readonly tag: "GetExecutionRequest"; readonly val: GetExecutionRequest } + | { readonly tag: "ListExecutionsRequest"; readonly val: ListExecutionsRequest } + | { readonly tag: "WaitExecutionRequest"; readonly val: WaitExecutionRequest } + | { readonly tag: "CancelExecutionRequest"; readonly val: CancelExecutionRequest } + | { readonly tag: "SignalExecutionRequest"; readonly val: SignalExecutionRequest } + | { readonly tag: "ResetExecutionRequest"; readonly val: ResetExecutionRequest } + | { readonly tag: "DeleteExecutionRequest"; readonly val: DeleteExecutionRequest } + | { readonly tag: "WriteExecutionStdinRequest"; readonly val: WriteExecutionStdinRequest } + | { readonly tag: "CloseExecutionStdinRequest"; readonly val: CloseExecutionStdinRequest } + | { readonly tag: "ResizeExecutionPtyRequest"; readonly val: ResizeExecutionPtyRequest } + | { readonly tag: "ReadExecutionOutputRequest"; readonly val: ReadExecutionOutputRequest } export function readRequestPayload(bc: bare.ByteCursor): RequestPayload { const offset = bc.offset @@ -2412,6 +3402,66 @@ export function readRequestPayload(bc: bare.ByteCursor): RequestPayload { return { tag: "ProvidedCommandsRequest", val: null } case 33: return { tag: "ListMountsRequest", val: null } + case 34: + return { tag: "ShellExecutionRequest", val: readShellExecutionRequest(bc) } + case 35: + return { tag: "ArgvExecutionRequest", val: readArgvExecutionRequest(bc) } + case 36: + return { tag: "JavaScriptExecutionRequest", val: readJavaScriptExecutionRequest(bc) } + case 37: + return { tag: "JavaScriptEvaluationRequest", val: readJavaScriptEvaluationRequest(bc) } + case 38: + return { tag: "JavaScriptFileExecutionRequest", val: readJavaScriptFileExecutionRequest(bc) } + case 39: + return { tag: "TypeScriptExecutionRequest", val: readTypeScriptExecutionRequest(bc) } + case 40: + return { tag: "TypeScriptEvaluationRequest", val: readTypeScriptEvaluationRequest(bc) } + case 41: + return { tag: "TypeScriptFileExecutionRequest", val: readTypeScriptFileExecutionRequest(bc) } + case 42: + return { tag: "TypeScriptCheckRequest", val: readTypeScriptCheckRequest(bc) } + case 43: + return { tag: "TypeScriptProjectCheckRequest", val: readTypeScriptProjectCheckRequest(bc) } + case 44: + return { tag: "NpmProjectInstallRequest", val: readNpmProjectInstallRequest(bc) } + case 45: + return { tag: "NpmPackageInstallRequest", val: readNpmPackageInstallRequest(bc) } + case 46: + return { tag: "NpmScriptExecutionRequest", val: readNpmScriptExecutionRequest(bc) } + case 47: + return { tag: "NpmPackageExecutionRequest", val: readNpmPackageExecutionRequest(bc) } + case 48: + return { tag: "PythonExecutionRequest", val: readPythonExecutionRequest(bc) } + case 49: + return { tag: "PythonEvaluationRequest", val: readPythonEvaluationRequest(bc) } + case 50: + return { tag: "PythonFileExecutionRequest", val: readPythonFileExecutionRequest(bc) } + case 51: + return { tag: "PythonModuleExecutionRequest", val: readPythonModuleExecutionRequest(bc) } + case 52: + return { tag: "PythonInstallRequest", val: readPythonInstallRequest(bc) } + case 53: + return { tag: "GetExecutionRequest", val: readGetExecutionRequest(bc) } + case 54: + return { tag: "ListExecutionsRequest", val: null } + case 55: + return { tag: "WaitExecutionRequest", val: readWaitExecutionRequest(bc) } + case 56: + return { tag: "CancelExecutionRequest", val: readCancelExecutionRequest(bc) } + case 57: + return { tag: "SignalExecutionRequest", val: readSignalExecutionRequest(bc) } + case 58: + return { tag: "ResetExecutionRequest", val: readResetExecutionRequest(bc) } + case 59: + return { tag: "DeleteExecutionRequest", val: readDeleteExecutionRequest(bc) } + case 60: + return { tag: "WriteExecutionStdinRequest", val: readWriteExecutionStdinRequest(bc) } + case 61: + return { tag: "CloseExecutionStdinRequest", val: readCloseExecutionStdinRequest(bc) } + case 62: + return { tag: "ResizeExecutionPtyRequest", val: readResizeExecutionPtyRequest(bc) } + case 63: + return { tag: "ReadExecutionOutputRequest", val: readReadExecutionOutputRequest(bc) } default: { bc.offset = offset throw new bare.BareError(offset, "invalid tag") @@ -2585,6 +3635,155 @@ export function writeRequestPayload(bc: bare.ByteCursor, x: RequestPayload): voi bare.writeU8(bc, 33) break } + case "ShellExecutionRequest": { + bare.writeU8(bc, 34) + writeShellExecutionRequest(bc, x.val) + break + } + case "ArgvExecutionRequest": { + bare.writeU8(bc, 35) + writeArgvExecutionRequest(bc, x.val) + break + } + case "JavaScriptExecutionRequest": { + bare.writeU8(bc, 36) + writeJavaScriptExecutionRequest(bc, x.val) + break + } + case "JavaScriptEvaluationRequest": { + bare.writeU8(bc, 37) + writeJavaScriptEvaluationRequest(bc, x.val) + break + } + case "JavaScriptFileExecutionRequest": { + bare.writeU8(bc, 38) + writeJavaScriptFileExecutionRequest(bc, x.val) + break + } + case "TypeScriptExecutionRequest": { + bare.writeU8(bc, 39) + writeTypeScriptExecutionRequest(bc, x.val) + break + } + case "TypeScriptEvaluationRequest": { + bare.writeU8(bc, 40) + writeTypeScriptEvaluationRequest(bc, x.val) + break + } + case "TypeScriptFileExecutionRequest": { + bare.writeU8(bc, 41) + writeTypeScriptFileExecutionRequest(bc, x.val) + break + } + case "TypeScriptCheckRequest": { + bare.writeU8(bc, 42) + writeTypeScriptCheckRequest(bc, x.val) + break + } + case "TypeScriptProjectCheckRequest": { + bare.writeU8(bc, 43) + writeTypeScriptProjectCheckRequest(bc, x.val) + break + } + case "NpmProjectInstallRequest": { + bare.writeU8(bc, 44) + writeNpmProjectInstallRequest(bc, x.val) + break + } + case "NpmPackageInstallRequest": { + bare.writeU8(bc, 45) + writeNpmPackageInstallRequest(bc, x.val) + break + } + case "NpmScriptExecutionRequest": { + bare.writeU8(bc, 46) + writeNpmScriptExecutionRequest(bc, x.val) + break + } + case "NpmPackageExecutionRequest": { + bare.writeU8(bc, 47) + writeNpmPackageExecutionRequest(bc, x.val) + break + } + case "PythonExecutionRequest": { + bare.writeU8(bc, 48) + writePythonExecutionRequest(bc, x.val) + break + } + case "PythonEvaluationRequest": { + bare.writeU8(bc, 49) + writePythonEvaluationRequest(bc, x.val) + break + } + case "PythonFileExecutionRequest": { + bare.writeU8(bc, 50) + writePythonFileExecutionRequest(bc, x.val) + break + } + case "PythonModuleExecutionRequest": { + bare.writeU8(bc, 51) + writePythonModuleExecutionRequest(bc, x.val) + break + } + case "PythonInstallRequest": { + bare.writeU8(bc, 52) + writePythonInstallRequest(bc, x.val) + break + } + case "GetExecutionRequest": { + bare.writeU8(bc, 53) + writeGetExecutionRequest(bc, x.val) + break + } + case "ListExecutionsRequest": { + bare.writeU8(bc, 54) + break + } + case "WaitExecutionRequest": { + bare.writeU8(bc, 55) + writeWaitExecutionRequest(bc, x.val) + break + } + case "CancelExecutionRequest": { + bare.writeU8(bc, 56) + writeCancelExecutionRequest(bc, x.val) + break + } + case "SignalExecutionRequest": { + bare.writeU8(bc, 57) + writeSignalExecutionRequest(bc, x.val) + break + } + case "ResetExecutionRequest": { + bare.writeU8(bc, 58) + writeResetExecutionRequest(bc, x.val) + break + } + case "DeleteExecutionRequest": { + bare.writeU8(bc, 59) + writeDeleteExecutionRequest(bc, x.val) + break + } + case "WriteExecutionStdinRequest": { + bare.writeU8(bc, 60) + writeWriteExecutionStdinRequest(bc, x.val) + break + } + case "CloseExecutionStdinRequest": { + bare.writeU8(bc, 61) + writeCloseExecutionStdinRequest(bc, x.val) + break + } + case "ResizeExecutionPtyRequest": { + bare.writeU8(bc, 62) + writeResizeExecutionPtyRequest(bc, x.val) + break + } + case "ReadExecutionOutputRequest": { + bare.writeU8(bc, 63) + writeReadExecutionOutputRequest(bc, x.val) + break + } } } @@ -2885,7 +4084,7 @@ export function writeGuestDirEntry(bc: bare.ByteCursor, x: GuestDirEntry): void bare.writeU64(bc, x.size) } -function read27(bc: bare.ByteCursor): readonly GuestDirEntry[] { +function read33(bc: bare.ByteCursor): readonly GuestDirEntry[] { const len = bare.readUintSafe(bc) if (len === 0) { return [] @@ -2897,43 +4096,32 @@ function read27(bc: bare.ByteCursor): readonly GuestDirEntry[] { return result } -function write27(bc: bare.ByteCursor, x: readonly GuestDirEntry[]): void { +function write33(bc: bare.ByteCursor, x: readonly GuestDirEntry[]): void { bare.writeUintSafe(bc, x.length) for (let i = 0; i < x.length; i++) { writeGuestDirEntry(bc, x[i]) } } -function read28(bc: bare.ByteCursor): readonly GuestDirEntry[] | null { - return bare.readBool(bc) ? read27(bc) : null -} - -function write28(bc: bare.ByteCursor, x: readonly GuestDirEntry[] | null): void { - bare.writeBool(bc, x != null) - if (x != null) { - write27(bc, x) - } -} - -function read29(bc: bare.ByteCursor): GuestFilesystemStat | null { - return bare.readBool(bc) ? readGuestFilesystemStat(bc) : null +function read34(bc: bare.ByteCursor): readonly GuestDirEntry[] | null { + return bare.readBool(bc) ? read33(bc) : null } -function write29(bc: bare.ByteCursor, x: GuestFilesystemStat | null): void { +function write34(bc: bare.ByteCursor, x: readonly GuestDirEntry[] | null): void { bare.writeBool(bc, x != null) if (x != null) { - writeGuestFilesystemStat(bc, x) + write33(bc, x) } } -function read30(bc: bare.ByteCursor): boolean | null { - return bare.readBool(bc) ? bare.readBool(bc) : null +function read35(bc: bare.ByteCursor): GuestFilesystemStat | null { + return bare.readBool(bc) ? readGuestFilesystemStat(bc) : null } -function write30(bc: bare.ByteCursor, x: boolean | null): void { +function write35(bc: bare.ByteCursor, x: GuestFilesystemStat | null): void { bare.writeBool(bc, x != null) if (x != null) { - bare.writeBool(bc, x) + writeGuestFilesystemStat(bc, x) } } @@ -2954,9 +4142,9 @@ export function readGuestFilesystemResultResponse(bc: bare.ByteCursor): GuestFil path: bare.readString(bc), content: read0(bc), encoding: read3(bc), - entries: read28(bc), - stat: read29(bc), - exists: read30(bc), + entries: read34(bc), + stat: read35(bc), + exists: read26(bc), target: read0(bc), } } @@ -2966,9 +4154,9 @@ export function writeGuestFilesystemResultResponse(bc: bare.ByteCursor, x: Guest bare.writeString(bc, x.path) write0(bc, x.content) write3(bc, x.encoding) - write28(bc, x.entries) - write29(bc, x.stat) - write30(bc, x.exists) + write34(bc, x.entries) + write35(bc, x.stat) + write26(bc, x.exists) write0(bc, x.target) } @@ -3000,7 +4188,7 @@ export function writeRootFilesystemSnapshotResponse(bc: bare.ByteCursor, x: Root write4(bc, x.entries) } -function read31(bc: bare.ByteCursor): readonly MountInfo[] { +function read36(bc: bare.ByteCursor): readonly MountInfo[] { const len = bare.readUintSafe(bc) if (len === 0) { return [] @@ -3012,7 +4200,7 @@ function read31(bc: bare.ByteCursor): readonly MountInfo[] { return result } -function write31(bc: bare.ByteCursor, x: readonly MountInfo[]): void { +function write36(bc: bare.ByteCursor, x: readonly MountInfo[]): void { bare.writeUintSafe(bc, x.length) for (let i = 0; i < x.length; i++) { writeMountInfo(bc, x[i]) @@ -3025,12 +4213,12 @@ export type ListMountsResponse = { export function readListMountsResponse(bc: bare.ByteCursor): ListMountsResponse { return { - mounts: read31(bc), + mounts: read36(bc), } } export function writeListMountsResponse(bc: bare.ByteCursor, x: ListMountsResponse): void { - write31(bc, x.mounts) + write36(bc, x.mounts) } export type ProcessStartedResponse = { @@ -3155,11 +4343,11 @@ export function writeProcessSnapshotStatus(bc: bare.ByteCursor, x: ProcessSnapsh } } -function read32(bc: bare.ByteCursor): i32 | null { +function read37(bc: bare.ByteCursor): i32 | null { return bare.readBool(bc) ? bare.readI32(bc) : null } -function write32(bc: bare.ByteCursor, x: i32 | null): void { +function write37(bc: bare.ByteCursor, x: i32 | null): void { bare.writeBool(bc, x != null) if (x != null) { bare.writeI32(bc, x) @@ -3192,7 +4380,7 @@ export function readProcessSnapshotEntry(bc: bare.ByteCursor): ProcessSnapshotEn args: read6(bc), cwd: bare.readString(bc), status: readProcessSnapshotStatus(bc), - exitCode: read32(bc), + exitCode: read37(bc), } } @@ -3207,10 +4395,10 @@ export function writeProcessSnapshotEntry(bc: bare.ByteCursor, x: ProcessSnapsho write6(bc, x.args) bare.writeString(bc, x.cwd) writeProcessSnapshotStatus(bc, x.status) - write32(bc, x.exitCode) + write37(bc, x.exitCode) } -function read33(bc: bare.ByteCursor): readonly ProcessSnapshotEntry[] { +function read38(bc: bare.ByteCursor): readonly ProcessSnapshotEntry[] { const len = bare.readUintSafe(bc) if (len === 0) { return [] @@ -3222,7 +4410,7 @@ function read33(bc: bare.ByteCursor): readonly ProcessSnapshotEntry[] { return result } -function write33(bc: bare.ByteCursor, x: readonly ProcessSnapshotEntry[]): void { +function write38(bc: bare.ByteCursor, x: readonly ProcessSnapshotEntry[]): void { bare.writeUintSafe(bc, x.length) for (let i = 0; i < x.length; i++) { writeProcessSnapshotEntry(bc, x[i]) @@ -3235,12 +4423,12 @@ export type ProcessSnapshotResponse = { export function readProcessSnapshotResponse(bc: bare.ByteCursor): ProcessSnapshotResponse { return { - processes: read33(bc), + processes: read38(bc), } } export function writeProcessSnapshotResponse(bc: bare.ByteCursor, x: ProcessSnapshotResponse): void { - write33(bc, x.processes) + write38(bc, x.processes) } export type QueueSnapshotEntry = { @@ -3272,7 +4460,7 @@ export function writeQueueSnapshotEntry(bc: bare.ByteCursor, x: QueueSnapshotEnt bare.writeU64(bc, x.fillPercent) } -function read34(bc: bare.ByteCursor): readonly QueueSnapshotEntry[] { +function read39(bc: bare.ByteCursor): readonly QueueSnapshotEntry[] { const len = bare.readUintSafe(bc) if (len === 0) { return [] @@ -3284,7 +4472,7 @@ function read34(bc: bare.ByteCursor): readonly QueueSnapshotEntry[] { return result } -function write34(bc: bare.ByteCursor, x: readonly QueueSnapshotEntry[]): void { +function write39(bc: bare.ByteCursor, x: readonly QueueSnapshotEntry[]): void { bare.writeUintSafe(bc, x.length) for (let i = 0; i < x.length; i++) { writeQueueSnapshotEntry(bc, x[i]) @@ -3325,7 +4513,7 @@ export function readResourceSnapshotResponse(bc: bare.ByteCursor): ResourceSnaps socketConnections: bare.readU64(bc), socketBufferedBytes: bare.readU64(bc), socketDatagramQueueLen: bare.readU64(bc), - queueSnapshots: read34(bc), + queueSnapshots: read39(bc), } } @@ -3344,7 +4532,7 @@ export function writeResourceSnapshotResponse(bc: bare.ByteCursor, x: ResourceSn bare.writeU64(bc, x.socketConnections) bare.writeU64(bc, x.socketBufferedBytes) bare.writeU64(bc, x.socketDatagramQueueLen) - write34(bc, x.queueSnapshots) + write39(bc, x.queueSnapshots) } export type SocketStateEntry = { @@ -3358,7 +4546,7 @@ export function readSocketStateEntry(bc: bare.ByteCursor): SocketStateEntry { return { processId: bare.readString(bc), host: read0(bc), - port: read26(bc), + port: read27(bc), path: read0(bc), } } @@ -3366,15 +4554,15 @@ export function readSocketStateEntry(bc: bare.ByteCursor): SocketStateEntry { export function writeSocketStateEntry(bc: bare.ByteCursor, x: SocketStateEntry): void { bare.writeString(bc, x.processId) write0(bc, x.host) - write26(bc, x.port) + write27(bc, x.port) write0(bc, x.path) } -function read35(bc: bare.ByteCursor): SocketStateEntry | null { +function read40(bc: bare.ByteCursor): SocketStateEntry | null { return bare.readBool(bc) ? readSocketStateEntry(bc) : null } -function write35(bc: bare.ByteCursor, x: SocketStateEntry | null): void { +function write40(bc: bare.ByteCursor, x: SocketStateEntry | null): void { bare.writeBool(bc, x != null) if (x != null) { writeSocketStateEntry(bc, x) @@ -3387,12 +4575,12 @@ export type ListenerSnapshotResponse = { export function readListenerSnapshotResponse(bc: bare.ByteCursor): ListenerSnapshotResponse { return { - listener: read35(bc), + listener: read40(bc), } } export function writeListenerSnapshotResponse(bc: bare.ByteCursor, x: ListenerSnapshotResponse): void { - write35(bc, x.listener) + write40(bc, x.listener) } export type BoundUdpSnapshotResponse = { @@ -3401,12 +4589,12 @@ export type BoundUdpSnapshotResponse = { export function readBoundUdpSnapshotResponse(bc: bare.ByteCursor): BoundUdpSnapshotResponse { return { - socket: read35(bc), + socket: read40(bc), } } export function writeBoundUdpSnapshotResponse(bc: bare.ByteCursor, x: BoundUdpSnapshotResponse): void { - write35(bc, x.socket) + write40(bc, x.socket) } export enum SignalDispositionAction { @@ -3469,7 +4657,7 @@ export function writeSignalHandlerRegistration(bc: bare.ByteCursor, x: SignalHan bare.writeU32(bc, x.flags) } -function read36(bc: bare.ByteCursor): ReadonlyMap { +function read41(bc: bare.ByteCursor): ReadonlyMap { const len = bare.readUintSafe(bc) const result = new Map() for (let i = 0; i < len; i++) { @@ -3484,7 +4672,7 @@ function read36(bc: bare.ByteCursor): ReadonlyMap): void { +function write41(bc: bare.ByteCursor, x: ReadonlyMap): void { bare.writeUintSafe(bc, x.size) for (const kv of x) { bare.writeU32(bc, kv[0]) @@ -3500,13 +4688,13 @@ export type SignalStateResponse = { export function readSignalStateResponse(bc: bare.ByteCursor): SignalStateResponse { return { processId: bare.readString(bc), - handlers: read36(bc), + handlers: read41(bc), } } export function writeSignalStateResponse(bc: bare.ByteCursor, x: SignalStateResponse): void { bare.writeString(bc, x.processId) - write36(bc, x.handlers) + write41(bc, x.handlers) } export type ZombieTimerCountResponse = { @@ -3630,7 +4818,7 @@ export function readRejectedResponse(bc: bare.ByteCursor): RejectedResponse { capabilityId: read21(bc), operation: read0(bc), configurationPath: read0(bc), - retryable: read30(bc), + retryable: read26(bc), errno: read0(bc), } } @@ -3649,7 +4837,7 @@ export function writeRejectedResponse(bc: bare.ByteCursor, x: RejectedResponse): write21(bc, x.capabilityId) write0(bc, x.operation) write0(bc, x.configurationPath) - write30(bc, x.retryable) + write26(bc, x.retryable) write0(bc, x.errno) } @@ -3667,6 +4855,404 @@ export function writeVmFetchResponse(bc: bare.ByteCursor, x: VmFetchResponse): v bare.writeString(bc, x.responseJson) } +function read42(bc: bare.ByteCursor): RetainedExecutionLanguage | null { + return bare.readBool(bc) ? readRetainedExecutionLanguage(bc) : null +} + +function write42(bc: bare.ByteCursor, x: RetainedExecutionLanguage | null): void { + bare.writeBool(bc, x != null) + if (x != null) { + writeRetainedExecutionLanguage(bc, x) + } +} + +function read43(bc: bare.ByteCursor): ExecutionOutcome | null { + return bare.readBool(bc) ? readExecutionOutcome(bc) : null +} + +function write43(bc: bare.ByteCursor, x: ExecutionOutcome | null): void { + bare.writeBool(bc, x != null) + if (x != null) { + writeExecutionOutcome(bc, x) + } +} + +export type ExecutionDescriptor = { + readonly executionId: string + readonly generation: u64 + readonly state: ExecutionState + readonly retainedLanguage: RetainedExecutionLanguage | null + readonly processId: string | null + readonly pid: u32 | null + readonly createdAtMs: u64 + readonly lastStartedAtMs: u64 | null + readonly lastCompletedAtMs: u64 | null + readonly lastOutcome: ExecutionOutcome | null + readonly lastExitCode: i32 | null +} + +export function readExecutionDescriptor(bc: bare.ByteCursor): ExecutionDescriptor { + return { + executionId: bare.readString(bc), + generation: bare.readU64(bc), + state: readExecutionState(bc), + retainedLanguage: read42(bc), + processId: read0(bc), + pid: read2(bc), + createdAtMs: bare.readU64(bc), + lastStartedAtMs: read21(bc), + lastCompletedAtMs: read21(bc), + lastOutcome: read43(bc), + lastExitCode: read37(bc), + } +} + +export function writeExecutionDescriptor(bc: bare.ByteCursor, x: ExecutionDescriptor): void { + bare.writeString(bc, x.executionId) + bare.writeU64(bc, x.generation) + writeExecutionState(bc, x.state) + write42(bc, x.retainedLanguage) + write0(bc, x.processId) + write2(bc, x.pid) + bare.writeU64(bc, x.createdAtMs) + write21(bc, x.lastStartedAtMs) + write21(bc, x.lastCompletedAtMs) + write43(bc, x.lastOutcome) + write37(bc, x.lastExitCode) +} + +export type ExecutionErrorData = { + readonly code: string + readonly name: string + readonly message: string + readonly stack: string | null + readonly details: JsonUtf8 | null +} + +export function readExecutionErrorData(bc: bare.ByteCursor): ExecutionErrorData { + return { + code: bare.readString(bc), + name: bare.readString(bc), + message: bare.readString(bc), + stack: read0(bc), + details: read32(bc), + } +} + +export function writeExecutionErrorData(bc: bare.ByteCursor, x: ExecutionErrorData): void { + bare.writeString(bc, x.code) + bare.writeString(bc, x.name) + bare.writeString(bc, x.message) + write0(bc, x.stack) + write32(bc, x.details) +} + +export type ExecutionAcceptedResponse = { + readonly execution: ExecutionDescriptor +} + +export function readExecutionAcceptedResponse(bc: bare.ByteCursor): ExecutionAcceptedResponse { + return { + execution: readExecutionDescriptor(bc), + } +} + +export function writeExecutionAcceptedResponse(bc: bare.ByteCursor, x: ExecutionAcceptedResponse): void { + writeExecutionDescriptor(bc, x.execution) +} + +function read44(bc: bare.ByteCursor): ExecutionErrorData | null { + return bare.readBool(bc) ? readExecutionErrorData(bc) : null +} + +function write44(bc: bare.ByteCursor, x: ExecutionErrorData | null): void { + bare.writeBool(bc, x != null) + if (x != null) { + writeExecutionErrorData(bc, x) + } +} + +export type ExecutionCompletedResponse = { + readonly execution: ExecutionDescriptor + readonly outcome: ExecutionOutcome + readonly exitCode: i32 | null + readonly error: ExecutionErrorData | null + readonly stdout: ArrayBuffer + readonly stderr: ArrayBuffer + readonly stdoutTruncated: boolean + readonly stderrTruncated: boolean + readonly outputs: JsonUtf8 +} + +export function readExecutionCompletedResponse(bc: bare.ByteCursor): ExecutionCompletedResponse { + return { + execution: readExecutionDescriptor(bc), + outcome: readExecutionOutcome(bc), + exitCode: read37(bc), + error: read44(bc), + stdout: bare.readData(bc), + stderr: bare.readData(bc), + stdoutTruncated: bare.readBool(bc), + stderrTruncated: bare.readBool(bc), + outputs: readJsonUtf8(bc), + } +} + +export function writeExecutionCompletedResponse(bc: bare.ByteCursor, x: ExecutionCompletedResponse): void { + writeExecutionDescriptor(bc, x.execution) + writeExecutionOutcome(bc, x.outcome) + write37(bc, x.exitCode) + write44(bc, x.error) + bare.writeData(bc, x.stdout) + bare.writeData(bc, x.stderr) + bare.writeBool(bc, x.stdoutTruncated) + bare.writeBool(bc, x.stderrTruncated) + writeJsonUtf8(bc, x.outputs) +} + +export type ExecutionEvaluationResponse = { + readonly result: ExecutionCompletedResponse + readonly value: JsonUtf8 | null +} + +export function readExecutionEvaluationResponse(bc: bare.ByteCursor): ExecutionEvaluationResponse { + return { + result: readExecutionCompletedResponse(bc), + value: read32(bc), + } +} + +export function writeExecutionEvaluationResponse(bc: bare.ByteCursor, x: ExecutionEvaluationResponse): void { + writeExecutionCompletedResponse(bc, x.result) + write32(bc, x.value) +} + +export type TypeScriptDiagnostic = { + readonly code: u32 + readonly category: string + readonly message: string + readonly filePath: string | null + readonly line: u32 | null + readonly column: u32 | null +} + +export function readTypeScriptDiagnostic(bc: bare.ByteCursor): TypeScriptDiagnostic { + return { + code: bare.readU32(bc), + category: bare.readString(bc), + message: bare.readString(bc), + filePath: read0(bc), + line: read2(bc), + column: read2(bc), + } +} + +export function writeTypeScriptDiagnostic(bc: bare.ByteCursor, x: TypeScriptDiagnostic): void { + bare.writeU32(bc, x.code) + bare.writeString(bc, x.category) + bare.writeString(bc, x.message) + write0(bc, x.filePath) + write2(bc, x.line) + write2(bc, x.column) +} + +function read45(bc: bare.ByteCursor): readonly TypeScriptDiagnostic[] { + const len = bare.readUintSafe(bc) + if (len === 0) { + return [] + } + const result = [readTypeScriptDiagnostic(bc)] + for (let i = 1; i < len; i++) { + result[i] = readTypeScriptDiagnostic(bc) + } + return result +} + +function write45(bc: bare.ByteCursor, x: readonly TypeScriptDiagnostic[]): void { + bare.writeUintSafe(bc, x.length) + for (let i = 0; i < x.length; i++) { + writeTypeScriptDiagnostic(bc, x[i]) + } +} + +export type TypeScriptCheckResponse = { + readonly result: ExecutionCompletedResponse + readonly hasErrors: boolean | null + readonly diagnostics: readonly TypeScriptDiagnostic[] +} + +export function readTypeScriptCheckResponse(bc: bare.ByteCursor): TypeScriptCheckResponse { + return { + result: readExecutionCompletedResponse(bc), + hasErrors: read26(bc), + diagnostics: read45(bc), + } +} + +export function writeTypeScriptCheckResponse(bc: bare.ByteCursor, x: TypeScriptCheckResponse): void { + writeExecutionCompletedResponse(bc, x.result) + write26(bc, x.hasErrors) + write45(bc, x.diagnostics) +} + +export type ExecutionDescriptorResponse = { + readonly execution: ExecutionDescriptor +} + +export function readExecutionDescriptorResponse(bc: bare.ByteCursor): ExecutionDescriptorResponse { + return { + execution: readExecutionDescriptor(bc), + } +} + +export function writeExecutionDescriptorResponse(bc: bare.ByteCursor, x: ExecutionDescriptorResponse): void { + writeExecutionDescriptor(bc, x.execution) +} + +function read46(bc: bare.ByteCursor): readonly ExecutionDescriptor[] { + const len = bare.readUintSafe(bc) + if (len === 0) { + return [] + } + const result = [readExecutionDescriptor(bc)] + for (let i = 1; i < len; i++) { + result[i] = readExecutionDescriptor(bc) + } + return result +} + +function write46(bc: bare.ByteCursor, x: readonly ExecutionDescriptor[]): void { + bare.writeUintSafe(bc, x.length) + for (let i = 0; i < x.length; i++) { + writeExecutionDescriptor(bc, x[i]) + } +} + +export type ExecutionListResponse = { + readonly executions: readonly ExecutionDescriptor[] +} + +export function readExecutionListResponse(bc: bare.ByteCursor): ExecutionListResponse { + return { + executions: read46(bc), + } +} + +export function writeExecutionListResponse(bc: bare.ByteCursor, x: ExecutionListResponse): void { + write46(bc, x.executions) +} + +export type ExecutionDeletedResponse = { + readonly executionId: string +} + +export function readExecutionDeletedResponse(bc: bare.ByteCursor): ExecutionDeletedResponse { + return { + executionId: bare.readString(bc), + } +} + +export function writeExecutionDeletedResponse(bc: bare.ByteCursor, x: ExecutionDeletedResponse): void { + bare.writeString(bc, x.executionId) +} + +export type ExecutionIoResponse = { + readonly executionId: string + readonly acceptedBytes: u64 | null +} + +export function readExecutionIoResponse(bc: bare.ByteCursor): ExecutionIoResponse { + return { + executionId: bare.readString(bc), + acceptedBytes: read21(bc), + } +} + +export function writeExecutionIoResponse(bc: bare.ByteCursor, x: ExecutionIoResponse): void { + bare.writeString(bc, x.executionId) + write21(bc, x.acceptedBytes) +} + +export type ExecutionOutputEvent = { + readonly executionId: string + readonly generation: u64 + readonly processId: string | null + readonly sequence: u64 + readonly channel: ExecutionStreamChannel + readonly chunk: ArrayBuffer + readonly timestampMs: u64 +} + +export function readExecutionOutputEvent(bc: bare.ByteCursor): ExecutionOutputEvent { + return { + executionId: bare.readString(bc), + generation: bare.readU64(bc), + processId: read0(bc), + sequence: bare.readU64(bc), + channel: readExecutionStreamChannel(bc), + chunk: bare.readData(bc), + timestampMs: bare.readU64(bc), + } +} + +export function writeExecutionOutputEvent(bc: bare.ByteCursor, x: ExecutionOutputEvent): void { + bare.writeString(bc, x.executionId) + bare.writeU64(bc, x.generation) + write0(bc, x.processId) + bare.writeU64(bc, x.sequence) + writeExecutionStreamChannel(bc, x.channel) + bare.writeData(bc, x.chunk) + bare.writeU64(bc, x.timestampMs) +} + +function read47(bc: bare.ByteCursor): readonly ExecutionOutputEvent[] { + const len = bare.readUintSafe(bc) + if (len === 0) { + return [] + } + const result = [readExecutionOutputEvent(bc)] + for (let i = 1; i < len; i++) { + result[i] = readExecutionOutputEvent(bc) + } + return result +} + +function write47(bc: bare.ByteCursor, x: readonly ExecutionOutputEvent[]): void { + bare.writeUintSafe(bc, x.length) + for (let i = 0; i < x.length; i++) { + writeExecutionOutputEvent(bc, x[i]) + } +} + +export type ExecutionOutputPageResponse = { + readonly executionId: string + readonly generation: u64 + readonly events: readonly ExecutionOutputEvent[] + readonly nextCursor: string + readonly hasMore: boolean + readonly truncated: boolean +} + +export function readExecutionOutputPageResponse(bc: bare.ByteCursor): ExecutionOutputPageResponse { + return { + executionId: bare.readString(bc), + generation: bare.readU64(bc), + events: read47(bc), + nextCursor: bare.readString(bc), + hasMore: bare.readBool(bc), + truncated: bare.readBool(bc), + } +} + +export function writeExecutionOutputPageResponse(bc: bare.ByteCursor, x: ExecutionOutputPageResponse): void { + bare.writeString(bc, x.executionId) + bare.writeU64(bc, x.generation) + write47(bc, x.events) + bare.writeString(bc, x.nextCursor) + bare.writeBool(bc, x.hasMore) + bare.writeBool(bc, x.truncated) +} + export type ResponsePayload = | { readonly tag: "AuthenticatedResponse"; readonly val: AuthenticatedResponse } | { readonly tag: "SessionOpenedResponse"; readonly val: SessionOpenedResponse } @@ -3704,6 +5290,15 @@ export type ResponsePayload = | { readonly tag: "PackageLinkedResponse"; readonly val: PackageLinkedResponse } | { readonly tag: "ProvidedCommandsResponse"; readonly val: ProvidedCommandsResponse } | { readonly tag: "ListMountsResponse"; readonly val: ListMountsResponse } + | { readonly tag: "ExecutionAcceptedResponse"; readonly val: ExecutionAcceptedResponse } + | { readonly tag: "ExecutionCompletedResponse"; readonly val: ExecutionCompletedResponse } + | { readonly tag: "ExecutionEvaluationResponse"; readonly val: ExecutionEvaluationResponse } + | { readonly tag: "TypeScriptCheckResponse"; readonly val: TypeScriptCheckResponse } + | { readonly tag: "ExecutionDescriptorResponse"; readonly val: ExecutionDescriptorResponse } + | { readonly tag: "ExecutionListResponse"; readonly val: ExecutionListResponse } + | { readonly tag: "ExecutionDeletedResponse"; readonly val: ExecutionDeletedResponse } + | { readonly tag: "ExecutionIoResponse"; readonly val: ExecutionIoResponse } + | { readonly tag: "ExecutionOutputPageResponse"; readonly val: ExecutionOutputPageResponse } export function readResponsePayload(bc: bare.ByteCursor): ResponsePayload { const offset = bc.offset @@ -3781,6 +5376,24 @@ export function readResponsePayload(bc: bare.ByteCursor): ResponsePayload { return { tag: "ProvidedCommandsResponse", val: readProvidedCommandsResponse(bc) } case 35: return { tag: "ListMountsResponse", val: readListMountsResponse(bc) } + case 36: + return { tag: "ExecutionAcceptedResponse", val: readExecutionAcceptedResponse(bc) } + case 37: + return { tag: "ExecutionCompletedResponse", val: readExecutionCompletedResponse(bc) } + case 38: + return { tag: "ExecutionEvaluationResponse", val: readExecutionEvaluationResponse(bc) } + case 39: + return { tag: "TypeScriptCheckResponse", val: readTypeScriptCheckResponse(bc) } + case 40: + return { tag: "ExecutionDescriptorResponse", val: readExecutionDescriptorResponse(bc) } + case 41: + return { tag: "ExecutionListResponse", val: readExecutionListResponse(bc) } + case 42: + return { tag: "ExecutionDeletedResponse", val: readExecutionDeletedResponse(bc) } + case 43: + return { tag: "ExecutionIoResponse", val: readExecutionIoResponse(bc) } + case 44: + return { tag: "ExecutionOutputPageResponse", val: readExecutionOutputPageResponse(bc) } default: { bc.offset = offset throw new bare.BareError(offset, "invalid tag") @@ -3970,6 +5583,51 @@ export function writeResponsePayload(bc: bare.ByteCursor, x: ResponsePayload): v writeListMountsResponse(bc, x.val) break } + case "ExecutionAcceptedResponse": { + bare.writeU8(bc, 36) + writeExecutionAcceptedResponse(bc, x.val) + break + } + case "ExecutionCompletedResponse": { + bare.writeU8(bc, 37) + writeExecutionCompletedResponse(bc, x.val) + break + } + case "ExecutionEvaluationResponse": { + bare.writeU8(bc, 38) + writeExecutionEvaluationResponse(bc, x.val) + break + } + case "TypeScriptCheckResponse": { + bare.writeU8(bc, 39) + writeTypeScriptCheckResponse(bc, x.val) + break + } + case "ExecutionDescriptorResponse": { + bare.writeU8(bc, 40) + writeExecutionDescriptorResponse(bc, x.val) + break + } + case "ExecutionListResponse": { + bare.writeU8(bc, 41) + writeExecutionListResponse(bc, x.val) + break + } + case "ExecutionDeletedResponse": { + bare.writeU8(bc, 42) + writeExecutionDeletedResponse(bc, x.val) + break + } + case "ExecutionIoResponse": { + bare.writeU8(bc, 43) + writeExecutionIoResponse(bc, x.val) + break + } + case "ExecutionOutputPageResponse": { + bare.writeU8(bc, 44) + writeExecutionOutputPageResponse(bc, x.val) + break + } } } @@ -4134,6 +5792,32 @@ export function writeProcessExitedEvent(bc: bare.ByteCursor, x: ProcessExitedEve bare.writeI32(bc, x.exitCode) } +export type ExecutionCompletedEvent = { + readonly executionId: string + readonly generation: u64 + readonly outcome: ExecutionOutcome + readonly exitCode: i32 | null + readonly error: ExecutionErrorData | null +} + +export function readExecutionCompletedEvent(bc: bare.ByteCursor): ExecutionCompletedEvent { + return { + executionId: bare.readString(bc), + generation: bare.readU64(bc), + outcome: readExecutionOutcome(bc), + exitCode: read37(bc), + error: read44(bc), + } +} + +export function writeExecutionCompletedEvent(bc: bare.ByteCursor, x: ExecutionCompletedEvent): void { + bare.writeString(bc, x.executionId) + bare.writeU64(bc, x.generation) + writeExecutionOutcome(bc, x.outcome) + write37(bc, x.exitCode) + write44(bc, x.error) +} + export type StructuredEvent = { readonly name: string readonly detail: ReadonlyMap @@ -4157,6 +5841,8 @@ export type EventPayload = | { readonly tag: "ProcessExitedEvent"; readonly val: ProcessExitedEvent } | { readonly tag: "StructuredEvent"; readonly val: StructuredEvent } | { readonly tag: "ExtEnvelope"; readonly val: ExtEnvelope } + | { readonly tag: "ExecutionOutputEvent"; readonly val: ExecutionOutputEvent } + | { readonly tag: "ExecutionCompletedEvent"; readonly val: ExecutionCompletedEvent } export function readEventPayload(bc: bare.ByteCursor): EventPayload { const offset = bc.offset @@ -4172,6 +5858,10 @@ export function readEventPayload(bc: bare.ByteCursor): EventPayload { return { tag: "StructuredEvent", val: readStructuredEvent(bc) } case 4: return { tag: "ExtEnvelope", val: readExtEnvelope(bc) } + case 5: + return { tag: "ExecutionOutputEvent", val: readExecutionOutputEvent(bc) } + case 6: + return { tag: "ExecutionCompletedEvent", val: readExecutionCompletedEvent(bc) } default: { bc.offset = offset throw new bare.BareError(offset, "invalid tag") @@ -4206,6 +5896,16 @@ export function writeEventPayload(bc: bare.ByteCursor, x: EventPayload): void { writeExtEnvelope(bc, x.val) break } + case "ExecutionOutputEvent": { + bare.writeU8(bc, 5) + writeExecutionOutputEvent(bc, x.val) + break + } + case "ExecutionCompletedEvent": { + bare.writeU8(bc, 6) + writeExecutionCompletedEvent(bc, x.val) + break + } } } @@ -4340,17 +6040,6 @@ export function writeSidecarRequestFrame(bc: bare.ByteCursor, x: SidecarRequestF writeSidecarRequestPayload(bc, x.payload) } -function read37(bc: bare.ByteCursor): JsonUtf8 | null { - return bare.readBool(bc) ? readJsonUtf8(bc) : null -} - -function write37(bc: bare.ByteCursor, x: JsonUtf8 | null): void { - bare.writeBool(bc, x != null) - if (x != null) { - writeJsonUtf8(bc, x) - } -} - export type HostCallbackResultResponse = { readonly invocationId: string readonly result: JsonUtf8 | null @@ -4360,14 +6049,14 @@ export type HostCallbackResultResponse = { export function readHostCallbackResultResponse(bc: bare.ByteCursor): HostCallbackResultResponse { return { invocationId: bare.readString(bc), - result: read37(bc), + result: read32(bc), error: read0(bc), } } export function writeHostCallbackResultResponse(bc: bare.ByteCursor, x: HostCallbackResultResponse): void { bare.writeString(bc, x.invocationId) - write37(bc, x.result) + write32(bc, x.result) write0(bc, x.error) } @@ -4380,14 +6069,14 @@ export type JsBridgeResultResponse = { export function readJsBridgeResultResponse(bc: bare.ByteCursor): JsBridgeResultResponse { return { callId: bare.readString(bc), - result: read37(bc), + result: read32(bc), error: read0(bc), } } export function writeJsBridgeResultResponse(bc: bare.ByteCursor, x: JsBridgeResultResponse): void { bare.writeString(bc, x.callId) - write37(bc, x.result) + write32(bc, x.result) write0(bc, x.error) } diff --git a/packages/runtime-core/src/kernel-proxy.ts b/packages/runtime-core/src/kernel-proxy.ts index 92d569b6f0..3422edf366 100644 --- a/packages/runtime-core/src/kernel-proxy.ts +++ b/packages/runtime-core/src/kernel-proxy.ts @@ -74,7 +74,7 @@ export function serializeMountConfigForSidecar( const SYNTHETIC_PID_BASE = 1_000_000; const MISSING_EXIT_EVENT_GRACE_MS = 500; -const PROTECTED_READ_ONLY_GUEST_ROOTS = ["/etc/secure-exec"] as const; +const PROTECTED_READ_ONLY_GUEST_ROOTS = ["/etc/agentos"] as const; const TRAILING_OUTPUT_DRAIN_INTERVAL_MS = 10; const TRAILING_OUTPUT_DRAIN_MAX_MS = 250; const TRAILING_OUTPUT_DRAIN_QUIET_TURNS = 2; diff --git a/packages/runtime-core/src/node-runtime.ts b/packages/runtime-core/src/node-runtime.ts index daead327d4..1114af3eb0 100644 --- a/packages/runtime-core/src/node-runtime.ts +++ b/packages/runtime-core/src/node-runtime.ts @@ -76,7 +76,7 @@ const REPO_COMMANDS_DIR = path.join( /** * Commands vendored into the published `@rivet-dev/agentos-runtime-core` package by * `scripts/copy-wasm-commands.mjs` (listed in `files` as `commands`). This is - * the directory a real `npm install secure-exec` resolves: from the compiled + * the directory a real `npm install agentos` resolves: from the compiled * `dist/node-runtime.js` it sits at `/commands`. This is the analogue * of how the sidecar binary ships inside `@rivet-dev/agentos-runtime-sidecar`. */ @@ -708,7 +708,7 @@ export class NodeRuntime { code: string, options: NodeRuntimeExecOptions = {}, ): Promise { - const programPath = `/tmp/secure-exec-program-${nextProgramId++}.mjs`; + const programPath = `/tmp/agentos-program-${nextProgramId++}.mjs`; await this.kernel.writeFile(programPath, withBindingPreamble(code)); return this.runProgram(programPath, options); } @@ -827,7 +827,7 @@ export class NodeRuntime { code: string, options: NodeRuntimeSpawnOptions = {}, ): Promise { - const programPath = `/tmp/secure-exec-program-${nextProgramId++}.mjs`; + const programPath = `/tmp/agentos-program-${nextProgramId++}.mjs`; await this.kernel.writeFile(programPath, withBindingPreamble(code)); const proc = this.kernel.spawn("node", [programPath], { env: options.env, @@ -955,8 +955,8 @@ export class NodeRuntime { options: NodeRuntimeExecOptions = {}, ): Promise> { const id = nextProgramId++; - const resultPath = `/tmp/secure-exec-result-${id}.json`; - const programPath = `/tmp/secure-exec-program-${id}.mjs`; + const resultPath = `/tmp/agentos-result-${id}.json`; + const programPath = `/tmp/agentos-program-${id}.mjs`; // Inject the __return helper as a module-level preamble, then the user // code at module top level. Import declarations (preamble's and the // user's) are hoisted, so __return is defined before the user's diff --git a/packages/runtime-core/src/request-payloads.ts b/packages/runtime-core/src/request-payloads.ts index 80c32b869f..b53c680cff 100644 --- a/packages/runtime-core/src/request-payloads.ts +++ b/packages/runtime-core/src/request-payloads.ts @@ -238,6 +238,81 @@ export type LiveRequestPayload = key: string; payload_size_bytes: number; } + | { type: "shell_execution"; request: protocol.ShellExecutionRequest } + | { type: "argv_execution"; request: protocol.ArgvExecutionRequest } + | { + type: "javascript_execution"; + request: protocol.JavaScriptExecutionRequest; + } + | { + type: "javascript_evaluation"; + request: protocol.JavaScriptEvaluationRequest; + } + | { + type: "javascript_file_execution"; + request: protocol.JavaScriptFileExecutionRequest; + } + | { + type: "typescript_execution"; + request: protocol.TypeScriptExecutionRequest; + } + | { + type: "typescript_evaluation"; + request: protocol.TypeScriptEvaluationRequest; + } + | { + type: "typescript_file_execution"; + request: protocol.TypeScriptFileExecutionRequest; + } + | { type: "typescript_check"; request: protocol.TypeScriptCheckRequest } + | { + type: "typescript_project_check"; + request: protocol.TypeScriptProjectCheckRequest; + } + | { type: "npm_project_install"; request: protocol.NpmProjectInstallRequest } + | { type: "npm_package_install"; request: protocol.NpmPackageInstallRequest } + | { + type: "npm_script_execution"; + request: protocol.NpmScriptExecutionRequest; + } + | { + type: "npm_package_execution"; + request: protocol.NpmPackageExecutionRequest; + } + | { type: "python_execution"; request: protocol.PythonExecutionRequest } + | { type: "python_evaluation"; request: protocol.PythonEvaluationRequest } + | { + type: "python_file_execution"; + request: protocol.PythonFileExecutionRequest; + } + | { + type: "python_module_execution"; + request: protocol.PythonModuleExecutionRequest; + } + | { type: "python_install"; request: protocol.PythonInstallRequest } + | { type: "get_execution"; request: protocol.GetExecutionRequest } + | { type: "list_executions" } + | { type: "wait_execution"; request: protocol.WaitExecutionRequest } + | { type: "cancel_execution"; request: protocol.CancelExecutionRequest } + | { type: "signal_execution"; request: protocol.SignalExecutionRequest } + | { type: "reset_execution"; request: protocol.ResetExecutionRequest } + | { type: "delete_execution"; request: protocol.DeleteExecutionRequest } + | { + type: "write_execution_stdin"; + request: protocol.WriteExecutionStdinRequest; + } + | { + type: "close_execution_stdin"; + request: protocol.CloseExecutionStdinRequest; + } + | { + type: "resize_execution_pty"; + request: protocol.ResizeExecutionPtyRequest; + } + | { + type: "read_execution_output"; + request: protocol.ReadExecutionOutputRequest; + } | { type: "ext"; envelope: LiveExtEnvelope; @@ -522,6 +597,66 @@ export function toGeneratedRequestPayload( payloadSizeBytes: BigInt(payload.payload_size_bytes), }, }; + case "shell_execution": + return { tag: "ShellExecutionRequest", val: payload.request }; + case "argv_execution": + return { tag: "ArgvExecutionRequest", val: payload.request }; + case "javascript_execution": + return { tag: "JavaScriptExecutionRequest", val: payload.request }; + case "javascript_evaluation": + return { tag: "JavaScriptEvaluationRequest", val: payload.request }; + case "javascript_file_execution": + return { tag: "JavaScriptFileExecutionRequest", val: payload.request }; + case "typescript_execution": + return { tag: "TypeScriptExecutionRequest", val: payload.request }; + case "typescript_evaluation": + return { tag: "TypeScriptEvaluationRequest", val: payload.request }; + case "typescript_file_execution": + return { tag: "TypeScriptFileExecutionRequest", val: payload.request }; + case "typescript_check": + return { tag: "TypeScriptCheckRequest", val: payload.request }; + case "typescript_project_check": + return { tag: "TypeScriptProjectCheckRequest", val: payload.request }; + case "npm_project_install": + return { tag: "NpmProjectInstallRequest", val: payload.request }; + case "npm_package_install": + return { tag: "NpmPackageInstallRequest", val: payload.request }; + case "npm_script_execution": + return { tag: "NpmScriptExecutionRequest", val: payload.request }; + case "npm_package_execution": + return { tag: "NpmPackageExecutionRequest", val: payload.request }; + case "python_execution": + return { tag: "PythonExecutionRequest", val: payload.request }; + case "python_evaluation": + return { tag: "PythonEvaluationRequest", val: payload.request }; + case "python_file_execution": + return { tag: "PythonFileExecutionRequest", val: payload.request }; + case "python_module_execution": + return { tag: "PythonModuleExecutionRequest", val: payload.request }; + case "python_install": + return { tag: "PythonInstallRequest", val: payload.request }; + case "get_execution": + return { tag: "GetExecutionRequest", val: payload.request }; + case "list_executions": + return { tag: "ListExecutionsRequest", val: null }; + case "wait_execution": + return { tag: "WaitExecutionRequest", val: payload.request }; + case "cancel_execution": + return { tag: "CancelExecutionRequest", val: payload.request }; + case "signal_execution": + return { tag: "SignalExecutionRequest", val: payload.request }; + case "reset_execution": + return { tag: "ResetExecutionRequest", val: payload.request }; + case "delete_execution": + return { tag: "DeleteExecutionRequest", val: payload.request }; + case "write_execution_stdin": + return { tag: "WriteExecutionStdinRequest", val: payload.request }; + case "close_execution_stdin": + return { tag: "CloseExecutionStdinRequest", val: payload.request }; + case "resize_execution_pty": + return { tag: "ResizeExecutionPtyRequest", val: payload.request }; + case "read_execution_output": + return { tag: "ReadExecutionOutputRequest", val: payload.request }; case "ext": return { tag: "ExtEnvelope", diff --git a/packages/runtime-core/src/response-payloads.ts b/packages/runtime-core/src/response-payloads.ts index ba7c6fd44e..5de0982e66 100644 --- a/packages/runtime-core/src/response-payloads.ts +++ b/packages/runtime-core/src/response-payloads.ts @@ -264,6 +264,27 @@ export type LiveResponsePayload = retryable: boolean | null; errno: string | null; } + | { type: "execution_accepted"; response: protocol.ExecutionAcceptedResponse } + | { + type: "execution_completed"; + response: protocol.ExecutionCompletedResponse; + } + | { + type: "execution_evaluation"; + response: protocol.ExecutionEvaluationResponse; + } + | { type: "typescript_check"; response: protocol.TypeScriptCheckResponse } + | { + type: "execution_descriptor"; + response: protocol.ExecutionDescriptorResponse; + } + | { type: "execution_list"; response: protocol.ExecutionListResponse } + | { type: "execution_deleted"; response: protocol.ExecutionDeletedResponse } + | { type: "execution_io"; response: protocol.ExecutionIoResponse } + | { + type: "execution_output_page"; + response: protocol.ExecutionOutputPageResponse; + } | { type: "ext_result"; envelope: LiveExtEnvelope; @@ -625,6 +646,24 @@ export function fromGeneratedResponsePayload( type: "ext_result", envelope: fromGeneratedExtEnvelope(payload.val), }; + case "ExecutionAcceptedResponse": + return { type: "execution_accepted", response: payload.val }; + case "ExecutionCompletedResponse": + return { type: "execution_completed", response: payload.val }; + case "ExecutionEvaluationResponse": + return { type: "execution_evaluation", response: payload.val }; + case "TypeScriptCheckResponse": + return { type: "typescript_check", response: payload.val }; + case "ExecutionDescriptorResponse": + return { type: "execution_descriptor", response: payload.val }; + case "ExecutionListResponse": + return { type: "execution_list", response: payload.val }; + case "ExecutionDeletedResponse": + return { type: "execution_deleted", response: payload.val }; + case "ExecutionIoResponse": + return { type: "execution_io", response: payload.val }; + case "ExecutionOutputPageResponse": + return { type: "execution_output_page", response: payload.val }; } } diff --git a/packages/runtime-core/src/sidecar-process.ts b/packages/runtime-core/src/sidecar-process.ts index 913bce87ea..e8ecfc03d3 100644 --- a/packages/runtime-core/src/sidecar-process.ts +++ b/packages/runtime-core/src/sidecar-process.ts @@ -38,7 +38,10 @@ import { type ProtocolFramePayloadCodec, } from "./protocol-frames.js"; import { type LiveRequestPayload } from "./request-payloads.js"; -import type { LiveGuestDirEntry } from "./response-payloads.js"; +import type { + LiveGuestDirEntry, + LiveResponsePayload, +} from "./response-payloads.js"; import { type LiveGuestFilesystemStat, type LiveProcessSnapshotEntry, @@ -370,7 +373,8 @@ export class SidecarProcess { silenceTimeoutMs: options.silenceTimeoutMs, eventBufferCapacity: options.eventBufferCapacity ?? DEFAULT_SIDECAR_EVENT_BUFFER_CAPACITY, - gracefulExitMs: options.gracefulExitMs ?? DEFAULT_SIDECAR_GRACEFUL_EXIT_MS, + gracefulExitMs: + options.gracefulExitMs ?? DEFAULT_SIDECAR_GRACEFUL_EXIT_MS, forceExitMs: options.forceExitMs ?? DEFAULT_SIDECAR_FORCE_EXIT_MS, disposedErrorMessage: "native sidecar disposed", payloadCodec: options.payloadCodec ?? "bare", @@ -396,8 +400,8 @@ export class SidecarProcess { }, payload: { type: "authenticate", - client_name: "secure-exec-core-client", - auth_token: "secure-exec-core-client-token", + client_name: "agentos-core-client", + auth_token: "agentos-core-client-token", protocol_version: SIDECAR_PROTOCOL_SCHEMA.version, bridge_version: BRIDGE_CONTRACT_VERSION, }, @@ -865,7 +869,9 @@ export class SidecarProcess { payload: { type: "list_mounts" }, }); if (response.payload.type !== "mounts_listed") { - throw new Error(`unexpected list_mounts response: ${response.payload.type}`); + throw new Error( + `unexpected list_mounts response: ${response.payload.type}`, + ); } return response.payload.mounts.map((mount) => ({ path: mount.path, @@ -1685,6 +1691,24 @@ export class SidecarProcess { return await this.protocolClient.waitForEvent(matcher, timeoutMs, options); } + /** Internal semantic request entrypoint used by the AgentOS client surface. */ + async sendVmRequest( + session: AuthenticatedSession, + vm: CreatedVm, + payload: LiveRequestPayload, + ): Promise { + const response = await this.sendRequest({ + ownership: { + scope: "vm", + connection_id: session.connectionId, + session_id: session.sessionId, + vm_id: vm.vmId, + }, + payload, + }); + return response.payload; + } + async dispose(): Promise { await this.protocolClient.dispose(); } diff --git a/packages/runtime-core/src/test-runtime.ts b/packages/runtime-core/src/test-runtime.ts index 3b8dd14fb7..8ca174cce0 100644 --- a/packages/runtime-core/src/test-runtime.ts +++ b/packages/runtime-core/src/test-runtime.ts @@ -1938,7 +1938,7 @@ class WasmVmRuntimeDescriptor implements KernelRuntimeDriver { } guestPaths.set( name, - `/__secure_exec/commands/${startIndex + dirOffset}/${name}`, + `/__agentos/commands/${startIndex + dirOffset}/${name}`, ); } return guestPaths; @@ -2360,7 +2360,7 @@ function collectGuestCommandPaths( if (!guestPaths.has(entry.name)) { guestPaths.set( entry.name, - `/__secure_exec/commands/${startIndex + entry.dirOffset}/${entry.name}`, + `/__agentos/commands/${startIndex + entry.dirOffset}/${entry.name}`, ); } } @@ -2890,7 +2890,7 @@ class NativeKernel implements Kernel { const allCommandDirs = [...this.mountedCommandDirs, ...commandDirs]; const sidecarMounts = allCommandDirs.map((commandDir, index) => serializeMountConfigForSidecar({ - path: `/__secure_exec/commands/${index}`, + path: `/__agentos/commands/${index}`, readOnly: true, plugin: { id: "host_dir", diff --git a/packages/runtime-core/tests/integration/bridge-child-process.nightly.test.ts b/packages/runtime-core/tests/integration/bridge-child-process.nightly.test.ts index 97bc565c4b..fdb692714b 100644 --- a/packages/runtime-core/tests/integration/bridge-child-process.nightly.test.ts +++ b/packages/runtime-core/tests/integration/bridge-child-process.nightly.test.ts @@ -713,7 +713,7 @@ describeIf( it("execFileSync on node_modules/.bin shell shims unwraps to the node entrypoint", async () => { const projectRoot = mkdtempSync( - join(tmpdir(), "secure-exec-node-bin-shim-"), + join(tmpdir(), "agentos-node-bin-shim-"), ); cleanupPaths.push(projectRoot); @@ -785,7 +785,7 @@ describeIf( it("execFileSync dereferences node_modules/.bin main-module symlinks", async () => { const projectRoot = mkdtempSync( - join(tmpdir(), "secure-exec-node-bin-symlink-"), + join(tmpdir(), "agentos-node-bin-symlink-"), ); cleanupPaths.push(projectRoot); @@ -853,7 +853,7 @@ describeIf( it("execFileSync unwraps shell shims whose node entrypoint has no shebang or extension", async () => { const projectRoot = mkdtempSync( - join(tmpdir(), "secure-exec-node-bin-shim-no-shebang-"), + join(tmpdir(), "agentos-node-bin-shim-no-shebang-"), ); cleanupPaths.push(projectRoot); diff --git a/packages/runtime-core/tests/integration/projects/developer-clis-pass/src/index.js b/packages/runtime-core/tests/integration/projects/developer-clis-pass/src/index.js index 865e9a86f7..001aa44cbf 100644 --- a/packages/runtime-core/tests/integration/projects/developer-clis-pass/src/index.js +++ b/packages/runtime-core/tests/integration/projects/developer-clis-pass/src/index.js @@ -103,8 +103,8 @@ module.exports = { await mkdir(path.join(monorepo, "packages", "a"), { recursive: true }); await mkdir(path.join(monorepo, "packages", "b"), { recursive: true }); await writeFile(path.join(monorepo, "package.json"), `${JSON.stringify({ name: "agentos-cli-monorepo", version: "1.0.0", private: true, packageManager: "pnpm@11.15.0", workspaces: ["packages/*"] }, null, 2)}\n`); - await writeFile(path.join(monorepo, "packages", "a", "package.json"), "{\"name\":\"@agentos/a\",\"version\":\"1.0.0\"}\n"); - await writeFile(path.join(monorepo, "packages", "b", "package.json"), "{\"name\":\"@agentos/b\",\"version\":\"1.0.0\"}\n"); + await writeFile(path.join(monorepo, "packages", "a", "package.json"), "{\"name\":\"@fixture/a\",\"version\":\"1.0.0\"}\n"); + await writeFile(path.join(monorepo, "packages", "b", "package.json"), "{\"name\":\"@fixture/b\",\"version\":\"1.0.0\"}\n"); await execFileAsync(process.execPath, [require.resolve("@manypkg/cli"), "check"], { cwd: monorepo, env: { ...process.env, CI: "1", NO_COLOR: "1", FORCE_COLOR: "0" }, diff --git a/packages/runtime-core/tests/integration/projects/rivetkit/.pnpmfile.cjs b/packages/runtime-core/tests/integration/projects/rivetkit/.pnpmfile.cjs new file mode 100644 index 0000000000..5fdcdd5e63 --- /dev/null +++ b/packages/runtime-core/tests/integration/projects/rivetkit/.pnpmfile.cjs @@ -0,0 +1,12 @@ +const PINNED_RIVETKIT_VERSION = "0.0.0-sqlite-uds.4e59a38"; + +module.exports = { + hooks: { + readPackage(pkg) { + if (pkg.name === "rivetkit" && pkg.version === PINNED_RIVETKIT_VERSION) { + delete pkg.dependencies?.["@rivet-dev/agent-os-core"]; + } + return pkg; + }, + }, +}; diff --git a/packages/runtime-core/tests/integration/projects/rivetkit/package.json b/packages/runtime-core/tests/integration/projects/rivetkit/package.json index 6ae5ce94ef..16ceebb448 100644 --- a/packages/runtime-core/tests/integration/projects/rivetkit/package.json +++ b/packages/runtime-core/tests/integration/projects/rivetkit/package.json @@ -3,6 +3,6 @@ "private": true, "type": "module", "dependencies": { - "rivetkit": "2.3.4" + "rivetkit": "0.0.0-sqlite-uds.4e59a38" } } diff --git a/packages/runtime-core/tests/integration/projects/rivetkit/pnpm-lock.yaml b/packages/runtime-core/tests/integration/projects/rivetkit/pnpm-lock.yaml index 30b4f9226b..ca511c7382 100644 --- a/packages/runtime-core/tests/integration/projects/rivetkit/pnpm-lock.yaml +++ b/packages/runtime-core/tests/integration/projects/rivetkit/pnpm-lock.yaml @@ -4,13 +4,15 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +pnpmfileChecksum: sha256-US3jzAV8R7azjMsy2waowmY2OhX06rMtgR1bwKXvwtE= + importers: .: dependencies: rivetkit: - specifier: 2.3.4 - version: 2.3.4(better-sqlite3@12.11.1) + specifier: 0.0.0-sqlite-uds.4e59a38 + version: 0.0.0-sqlite-uds.4e59a38 packages: @@ -49,162 +51,6 @@ packages: cpu: [x64] os: [win32] - '@esbuild/aix-ppc64@0.27.7': - resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.27.7': - resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.27.7': - resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.27.7': - resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.27.7': - resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.27.7': - resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.27.7': - resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.27.7': - resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.27.7': - resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.27.7': - resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.27.7': - resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.27.7': - resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.27.7': - resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.27.7': - resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.27.7': - resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.27.7': - resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.27.7': - resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.27.7': - resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.27.7': - resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.27.7': - resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.27.7': - resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.27.7': - resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.27.7': - resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.27.7': - resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.27.7': - resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.27.7': - resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@hono/zod-openapi@1.5.1': resolution: {integrity: sha512-ZaDdEIkn6PEGjIYHXJeyByg6yhvLzI+UXn968pWtahpwcQ6HMjQYXI3zNffTl0Wl9QZ79nUnGqiN1erEIu23fA==} engines: {node: '>=16.0.0'} @@ -226,237 +72,106 @@ packages: '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} - '@rivet-dev/agent-os-core@0.1.1': - resolution: {integrity: sha512-Uw5jr+gUXDY7TDUFqlypjGe1BD2KL9kTHPNo/f1iNS1R+l9IWuvT+FF/MXsLOEkc3fB06OPVu2ZvUuNwp9MpLQ==} - - '@rivet-dev/agent-os-posix@0.1.0': - resolution: {integrity: sha512-NIrI7cCb9x6jdmzRPPx7dAeXoTF/YCqf93ydEzYFA2zshIelLW9Rp5KtgP/2hM6fP0ly4+vVnOeavxJW0wYtcA==} - - '@rivet-dev/agent-os-python@0.1.0': - resolution: {integrity: sha512-1tH1beMf1ceSpicQKwN/a6h+NmJrmfuT4GStiRDZmvN/UWfZhkxuy7HR5VPTQpE/feUZJ01FdtBS3Em/Qoxb2Q==} - peerDependencies: - pyodide: '>=0.28.0' - peerDependenciesMeta: - pyodide: - optional: true - '@rivetkit/bare-ts@0.6.2': resolution: {integrity: sha512-3qndQUQXLdwafMEqfhz24hUtDPcsf1Bu3q52Kb8MqeH8JUh3h6R4HYW3ZJXiQsLcyYyFM68PuIwlLRlg1xDEpg==} engines: {node: ^14.18.0 || >=16.0.0} - '@rivetkit/engine-cli-darwin-arm64@2.3.4': - resolution: {integrity: sha512-ssRb48PGIQ47qvraF+xjeypigFqMtDK7LNHcIjvtojl7rx0/T4JUmJnouurQaPCu/Ay8XgalOAcyjCrDaKUJVA==} + '@rivetkit/engine-cli-darwin-arm64@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-ZT+wqmQzLznezyYVTzRHckWbp7F4yOG3ZCCON+ErdnWd84A4mwJMYXZPIObGD2OVSxmNhWqXO1ocNTKwDsIllw==} engines: {node: '>= 20.0.0'} cpu: [arm64] os: [darwin] - '@rivetkit/engine-cli-darwin-x64@2.3.4': - resolution: {integrity: sha512-X6wsfv6FY9OpxRVMZIG9RUX+rG9HGddMAtZOWRvwBuY3+MfuEE1U5gpFVHN7MCcbao3mmBRj2fIWFXx5LKoNMA==} + '@rivetkit/engine-cli-darwin-x64@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-PgiKyvhciN17C7RjyB9J9ApVkCTJmdyaRZnsSmy0xdbNY12uKrhH2mNcaQAPV+7/QB4HB4TjVgD1wT+PCCGiQg==} engines: {node: '>= 20.0.0'} cpu: [x64] os: [darwin] - '@rivetkit/engine-cli-linux-arm64-musl@2.3.4': - resolution: {integrity: sha512-6zVTXPrn/95xS/JRHZ1sfhwOEvMS5iE+1YWQGR/bHtlLYmF9ZhtoCngQoI7mNJsspq9QE88x2mlGV470UFIFyQ==} + '@rivetkit/engine-cli-linux-arm64-musl@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-2FZhrpzYfNIId3Tn0wpz1klcoKZjf2NBzGkmdxGQKY0kh7XynBsZxUnqyHJVFAkfLSSW+1t8E6PLTMSfPHIKXQ==} engines: {node: '>= 20.0.0'} cpu: [arm64] os: [linux] - '@rivetkit/engine-cli-linux-x64-musl@2.3.4': - resolution: {integrity: sha512-9qfRJ9t7ZFQQZtv5EIry9kMu+d/Rbapzy+V8XS5pqgPOkGPjVyGP2CwpaLJsh1nMSu+u4OS9HTf6134qj/eGaw==} + '@rivetkit/engine-cli-linux-x64-musl@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-iw3v07nBtc1+qiQxl/g3HPT9UPOJUidvOYixPpxtydaRqh465/6pYy8cTzQOhEKBodBD3drsaB+6GJwDLy8CMA==} engines: {node: '>= 20.0.0'} cpu: [x64] os: [linux] - '@rivetkit/engine-cli-win32-x64@2.3.4': - resolution: {integrity: sha512-qahxiLiW7dnO4LtmKpiLUgq+0Bx+ypvJKxFIeyERZVBZeq53LYAw/eTIHRicyfgUMdlymbgTJKVBfbEd/QlbMg==} - engines: {node: '>= 20.0.0'} - cpu: [x64] - os: [win32] - - '@rivetkit/engine-cli@2.3.4': - resolution: {integrity: sha512-i+bGMloDq4iE24XNanJG4d/vvu1fZ7DQXE70HlW8s/fcLOaMALgfuyrGYCcnX5a1S8ukjq25h2ZafILK/pxnWw==} + '@rivetkit/engine-cli@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-5WYBHyO4Yudb2RuVbbp2srb/XDbEAdTihxUj/aUerahalR3KiNDo5zpUZpfhhoAQrjPQTVTN9Nan/RVloVpFRg==} engines: {node: '>= 20.0.0'} - '@rivetkit/engine-envoy-protocol@2.3.4': - resolution: {integrity: sha512-YHLWsCIKT6bKVuZ3Db/M+Yu7w2aGkdB9bCZvKi6DQNuSoCy9m0PBLbhtRkqb6vsxvmni7qVPyeZrLGKPeB5ETQ==} + '@rivetkit/engine-envoy-protocol@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-asUWA8E+fyD1RLQ5wH6KhCTNZ3laAqqS2bZQkcMQS8mQn/9BVl+sfCm/yM3hLK7lH7WT616sJhcP86EktZCLsA==} '@rivetkit/on-change@6.0.1': resolution: {integrity: sha512-QBN/KRBXLJdCgN4gBTL3XAc/zKm58atSnieXWMOyFSPmo6F1/yIVV/LTRdvAktfCttrGx7W6c32i/lwqCHWnsQ==} engines: {node: '>=20'} - '@rivetkit/rivetkit-napi-darwin-arm64@2.3.4': - resolution: {integrity: sha512-kxsS/fSJ5VERdNaFuNdRQp2gCQCI41I+Hj41jLP4jMnzjWhwRhKmVmBN1YxyVTDcmVHDGZLSwCp3sSJYHvsVKQ==} + '@rivetkit/rivetkit-napi-darwin-arm64@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-sH/wB5IvlDQ7lktL7lk/1h5K8/Rxm/UYyY8GKGOdPzgIhBK7RNE194vhlLLgjWCU2P5eTo/uG8U5n7Shw1VoBA==} engines: {node: '>= 20.0.0'} cpu: [arm64] os: [darwin] - '@rivetkit/rivetkit-napi-darwin-x64@2.3.4': - resolution: {integrity: sha512-DthpCVZ6eJTkjQ5QpTyM1Dh8h0JffNUHsYb4huO7EpO3/XC1gZl/SmUSbtmOl1/YcawMofD7bCFbJwdG/W0N0g==} + '@rivetkit/rivetkit-napi-darwin-x64@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-E8Lt44QIOjzNnrFDWALpIFn3p593l57Y7z5mmY4oK0xScJlw8hL6jn0ISvCA1BRd0wPTy5/fwCUxGAivhhVJAg==} engines: {node: '>= 20.0.0'} cpu: [x64] os: [darwin] - '@rivetkit/rivetkit-napi-linux-arm64-gnu@2.3.4': - resolution: {integrity: sha512-X63Bv0YqbMCgHUBQ5bqm5qiggYAB4d/+5nueqtFBTqQvnVSM2oJy8rLtdjwlKjNNBS9EFY6gckAhSyP/jih1vg==} + '@rivetkit/rivetkit-napi-linux-arm64-gnu@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-xdFAuc0s8tj6p63zeY/IoS0JKtnmTh6RyOauy+8cptTzGDUXcQnuzQU28YjPJ535je7aGIlJYfSXhL/rd4kmZA==} engines: {node: '>= 20.0.0'} cpu: [arm64] os: [linux] - '@rivetkit/rivetkit-napi-linux-arm64-musl@2.3.4': - resolution: {integrity: sha512-iYT0F37FhAbTf/mqVl0Da3o1Wubmo99qXxdppGluy2Cijwj9sQswSm5Dd50oBRH3aOY/bja3twITxvCtzHPlBg==} + '@rivetkit/rivetkit-napi-linux-arm64-musl@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-W6gTRHX1Sx9Oi2KoPpEN1fENEkF/dqvpc5OqNJErCcW8M2WCB/0BU/c7nHsP54ZLzS0kcZtaQov6qw0Nt2eaWA==} engines: {node: '>= 20.0.0'} cpu: [arm64] os: [linux] - '@rivetkit/rivetkit-napi-linux-x64-gnu@2.3.4': - resolution: {integrity: sha512-p8SCYiw+9vj8CMa/Sgfn7V/ebQ3o+PBHfS7gK/k85jDA6u8Qs1Xxb3j76tniKwbpLnXE8cdkeiJdkEfE7oHskg==} + '@rivetkit/rivetkit-napi-linux-x64-gnu@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-BY8SZke/5iHh35rFrkJ+GkmMelezHaIrClxjpZ6a3Yfu4owAfOUCOSTHZttYAoba4EdnCpB8aTQ2SimtvNN77g==} engines: {node: '>= 20.0.0'} cpu: [x64] os: [linux] - '@rivetkit/rivetkit-napi-linux-x64-musl@2.3.4': - resolution: {integrity: sha512-jZJfqqkFPkaQotXmz/N8Lk/ZtSSlzX3EuBaFGie+TVL32Z4XMPJMruwE+KwwXgnUYkauiWEjByzYBuU3Kf/oGQ==} + '@rivetkit/rivetkit-napi-linux-x64-musl@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-rxtzRH2hpaz8hx6F1GIIM7f0FpDzyBKNtmusDQswmYajkm/Hfxl9ha2bleHg6EFtNVAr8gQ9cHAZcAv583bAJw==} engines: {node: '>= 20.0.0'} cpu: [x64] os: [linux] - '@rivetkit/rivetkit-napi-win32-x64-msvc@2.3.4': - resolution: {integrity: sha512-J1VvCOMDqvEiUtmhhDgR0eBMCen42IVMPoyIPBJWnUF2Hkm9Ld+NB6DdN19h+U5u+/EwLSCgwrxewxTkNCnf0g==} - engines: {node: '>= 20.0.0'} - cpu: [x64] - os: [win32] - - '@rivetkit/rivetkit-napi@2.3.4': - resolution: {integrity: sha512-EKO533N+0yv4gXK5U75DncV3ubIG/32yIFCeTA6DZZsV+MjFvc+Aj/ojNlja00whJ1OBbKvf0fUGXlHJJxxMjA==} + '@rivetkit/rivetkit-napi@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-0SibmwqUugIfgRdwuG45+SvjMLzk5Bim/cViOtnt2FrucNB1E/YeC7QD4MxMi/Z+z+M/A33dljUst70C2f5uVA==} engines: {node: '>= 20.0.0'} - '@rivetkit/rivetkit-wasm@2.3.4': - resolution: {integrity: sha512-NWza+AJ6cHk7QhZ8nK4E3ejL7OwRKEEfH7h1+xfSgcn548zgOU1TkQMLcLMn+FkypukJdFv8tEON0rxmf53RqQ==} + '@rivetkit/rivetkit-wasm@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-jWMHRJjPU0IPjVmv7kmfpt3wh3ThYOMETsKEg9QU3QupRKANRltkSRZp9Km34kUzzsyKQcqiidmWavYmn61egA==} - '@rivetkit/traces@2.3.4': - resolution: {integrity: sha512-w/3QbwwJEdVC1WtzSjSBCXKXFdCIGeT70AlvW2H9DVjcgyT08Qn7R86gfRQkf+gnDMQxP9CUltzjl4vynOAl5w==} + '@rivetkit/traces@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-4d9Kodk7H0frBHNTmzJBXkOGKGgn5eU+A9XTmOTsoHIpPqK5AUrbJ35P2mQHQmzoU+WoHUumvey0Ze8UOKIy/Q==} engines: {node: '>=18.0.0'} - '@rivetkit/virtual-websocket@2.3.4': - resolution: {integrity: sha512-lXMwTt0SSOYI9SIUSOymh4Qhz2IVM+Gn74dshtEdG3reTPVcbcXWl/QO5aY/UFlzwopiY1YTTD7bapKjwYPsBg==} + '@rivetkit/virtual-websocket@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-U6C9jSf4VaQGEZTROFZHLsfCUci8No4ofBV6muzqmAKTiq6NN7mLE5V0muc0LTUx5dFpcX9hATJW1FSZVpnbYw==} - '@rivetkit/workflow-engine@2.3.4': - resolution: {integrity: sha512-n1J6ZByWXZrkWWmqyCDJ45TvTlcG4L925EeHliHSPSjmIMgV+Qqbxpa34//PtQkafvCsS604QEd+cZLfh7lVOw==} + '@rivetkit/workflow-engine@0.0.0-sqlite-uds.4e59a38': + resolution: {integrity: sha512-TUe2ZEg2D8yCNZTr+F6eeMp2uK2VAybYHhMj1IQUOuWwSyamBD8o5UFkqO12nYcE+52a7NwfHwhHEov8riVb1w==} engines: {node: '>=18.0.0'} - '@secure-exec/core@0.2.1': - resolution: {integrity: sha512-HsnUv6gClpMA1BBRmX86j30TKTZtgJC/fO1tVavr7IpM2zNKbHU8LgSlBd7mv2SNy02ImTmU/GnQ3aYB4NSbEg==} - - '@secure-exec/nodejs@0.2.1': - resolution: {integrity: sha512-UJMJqVFxexlHJV0Q9nWURvrz6GElj8673DDOOFln6FHR6JS+9SaSU3eISrN158DuNC3SFi4rgjb/scKnK4YOYQ==} - - '@secure-exec/v8-darwin-arm64@0.2.1': - resolution: {integrity: sha512-gEWhMHzUpLwzuBNAD0lVkZXE8wFlWMLp4IOZ+56FYwOW/C+m07cYxuW4TjHyPqZ+vPm3IkoaMqqH5yT9VhjX/Q==} - cpu: [arm64] - os: [darwin] - - '@secure-exec/v8-darwin-x64@0.2.1': - resolution: {integrity: sha512-H2Z5K+Cq+fn/kxjGvhJzepnNFWG6qNdyhZybVWGr5bAAZoSz/Qkad4WnXcurWU+880tKDtnf19LHBXrg7zewNQ==} - cpu: [x64] - os: [darwin] - - '@secure-exec/v8-linux-arm64-gnu@0.2.1': - resolution: {integrity: sha512-14subGhVV/gW35mYYm7Gv1Keeex7PxIgQfoKji/JH7wYyDuarP6kgaES0nJw+JXVkxEVud52c+kbcIjIggqCEw==} - cpu: [arm64] - os: [linux] - - '@secure-exec/v8-linux-x64-gnu@0.2.1': - resolution: {integrity: sha512-Az4s+vUf+78vWtsC7rTn/jQc6WKJafAdt2YpEjB4Gnu+sX+FFTIst1hRV4gJonbRyJdy6SW+OQ6DZatmwczorQ==} - cpu: [x64] - os: [linux] - - '@secure-exec/v8@0.2.1': - resolution: {integrity: sha512-ye/seCqzvyMGnvyP+AO7RkVMR/lE3x9m0D2PfmiAXA457R78ZmOFmZ6v+JlJG2vv3LM30KsSXTUhwpG+Teh0hw==} - '@types/retry@0.12.2': resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} - asn1.js@4.10.1: - resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} - - assert@2.1.0: - resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} - atomic-sleep@1.0.0: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - better-sqlite3@12.11.1: - resolution: {integrity: sha512-dq9AtApgg5PGFtBzPFSBl3HZQjHok5gaQCM6zh2Yk0aSmDCs1CbnVI8/HgASQkNKsWFpseIO9beg5xxpYhbIfA==} - engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x || 26.x} - - bindings@1.5.0: - resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - - bn.js@4.12.5: - resolution: {integrity: sha512-3aRg6/JxfffFD+OlOjOFR3Vo79l39ooBTFucxx+MT3dhCtzn3EmiUPQo+6/OZuI2jbXi3YKgmiTFBgChQMwIRQ==} - - bn.js@5.2.5: - resolution: {integrity: sha512-Vq886eXykuP5E6HcKSSStP3bJgrE6In5WKxVUvJ8XGpWWYs2xZHWqUwzCtGgEtBcxyd57KBFDPFoUfNzdaHCNg==} - - brorand@1.1.0: - resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} - - browser-resolve@2.0.0: - resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} - - browserify-aes@1.2.0: - resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} - - browserify-cipher@1.0.1: - resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} - - browserify-des@1.0.2: - resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} - - browserify-rsa@4.1.1: - resolution: {integrity: sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==} - engines: {node: '>= 0.10'} - - browserify-sign@4.2.6: - resolution: {integrity: sha512-sd+Q65fjlWCYWtZKXiKfrUc8d+4jtp/8f0W2NkwzLtoW4bI6UDnWusLWIurHnmurW0XShIRxpwiOX4EoPtXUAg==} - engines: {node: '>= 0.10'} - - browserify-zlib@0.2.0: - resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} - - buffer-xor@1.0.3: - resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - builtin-status-codes@3.0.0: - resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bind@1.0.9: - resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - cbor-extract@2.2.2: resolution: {integrity: sha512-hlSxxI9XO2yQfe9g6msd3g4xCfDqK5T5P0fRMLuaLHhxn4ViPrm+a+MUfhrvH2W962RGxcBwEGzLQyjbDG1gng==} hasBin: true @@ -464,75 +179,10 @@ packages: cbor-x@1.6.4: resolution: {integrity: sha512-UGKHjp6RHC6QuZ2yy5LCKm7MojM4716DwoSaqwQpaH4DvZvbBTGcoDNTiG9Y2lByXZYFEs9WRkS5tLl96IrF1Q==} - chownr@1.1.4: - resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - - cipher-base@1.0.7: - resolution: {integrity: sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==} - engines: {node: '>= 0.10'} - - cjs-module-lexer@2.2.0: - resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} - - console-browserify@1.2.0: - resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} - - constants-browserify@1.0.0: - resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} - - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - - create-ecdh@4.0.4: - resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} - - create-hash@1.2.0: - resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} - - create-hmac@1.1.7: - resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} - - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - - croner@10.0.1: - resolution: {integrity: sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==} - engines: {node: '>=18.0'} - - crypto-browserify@3.12.1: - resolution: {integrity: sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==} - engines: {node: '>= 0.10'} - - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - - deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - - des.js@1.1.0: - resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} - detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - diffie-hellman@5.0.3: - resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} - - domain-browser@4.22.0: - resolution: {integrity: sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==} - engines: {node: '>=10'} - drizzle-orm@0.44.7: resolution: {integrity: sha512-quIpnYznjU9lHshEOAYLoZ9s3jweleHlZIAWR/jX9gAWNg/JhQ1wj0KGRf7/Zm+obRrYd9GjPVJg790QY9N5AQ==} peerDependencies: @@ -625,294 +275,42 @@ packages: sqlite3: optional: true - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - elliptic@6.6.1: - resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} - - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - - es-object-atoms@1.1.2: - resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} - engines: {node: '>= 0.4'} - - esbuild@0.27.7: - resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} - engines: {node: '>=18'} - hasBin: true - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - - evp_bytestokey@1.0.3: - resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} - - expand-template@2.0.3: - resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} - engines: {node: '>=6'} - fdb-tuple@1.0.0: resolution: {integrity: sha512-8jSvKPCYCgTpi9Pt87qlfTk6griyMx4Gk3Xv31Dp72Qp8b6XgIyFsMm8KzPmFJ9iJ8K4pGvRxvOS8D0XGnrkjw==} - file-uri-to-path@1.0.0: - resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - - fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - generator-function@2.0.1: - resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} - engines: {node: '>= 0.4'} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - github-from-package@0.0.0: - resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hash-base@3.0.5: - resolution: {integrity: sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==} - engines: {node: '>= 0.10'} - - hash-base@3.1.2: - resolution: {integrity: sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==} - engines: {node: '>= 0.8'} - - hash.js@1.1.7: - resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} - - hasown@2.0.4: - resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} - engines: {node: '>= 0.4'} - - hmac-drbg@1.0.1: - resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} - hono@4.12.31: resolution: {integrity: sha512-zJIHFrl6bq3RDd2YusFNCDlM8qUprxKswyi/OPzPyzKDdyBXDqWx8bZlZ7R+saTdSTatUmb3O7K4SspGPaEOQg==} engines: {node: '>=16.9.0'} - https-browserify@1.0.0: - resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - is-arguments@1.2.0: - resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} - engines: {node: '>= 0.4'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-core-module@2.16.2: - resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} - engines: {node: '>= 0.4'} - - is-generator-function@1.1.2: - resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} - engines: {node: '>= 0.4'} - - is-nan@1.3.2: - resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} - engines: {node: '>= 0.4'} - is-network-error@1.3.2: resolution: {integrity: sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==} engines: {node: '>=16'} - is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - - isomorphic-timers-promises@1.0.1: - resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} - engines: {node: '>=10'} - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - - long-timeout@0.1.1: - resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==} - loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - md5.js@1.3.5: - resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} - - miller-rabin@4.0.1: - resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} - hasBin: true - - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - - minimalistic-assert@1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - - minimalistic-crypto-utils@1.0.1: - resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - mkdirp-classic@0.5.3: - resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - - napi-build-utils@2.0.0: - resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} - - node-abi@3.94.0: - resolution: {integrity: sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==} - engines: {node: '>=10'} - node-gyp-build-optional-packages@5.1.1: resolution: {integrity: sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==} hasBin: true - node-stdlib-browser@1.3.1: - resolution: {integrity: sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw==} - engines: {node: '>=10'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - object-is@1.1.6: - resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} - engines: {node: '>= 0.4'} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.7: - resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} - engines: {node: '>= 0.4'} - on-exit-leak-free@2.1.2: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - openapi3-ts@4.6.0: resolution: {integrity: sha512-a4sfn6L2sIShhtzJqmjGrARvxAW/3F2BJDdyRVvNF9VhAsZSh5hSyI3a9TNvmzBxXmq66nY5LNT5bQcBxYAZZg==} - os-browserify@0.3.0: - resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - p-retry@6.2.1: resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} engines: {node: '>=16.17'} - pako@1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - - parse-asn1@5.1.9: - resolution: {integrity: sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==} - engines: {node: '>= 0.10'} - - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - pbkdf2@3.1.6: - resolution: {integrity: sha512-BT6eelPB1EyGHo8pC0o9Bl6k6SYVhKO1jEbd3lcTrtr7XHdjP8BW1YpfCV3G9Kwkxgattk+S5q2/RvuttCsS1g==} - engines: {node: '>= 0.10'} - pino-abstract-transport@2.0.0: resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} @@ -923,86 +321,22 @@ packages: resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} hasBin: true - pkg-dir@5.0.0: - resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} - engines: {node: '>=10'} - - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - - prebuild-install@7.1.3: - resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} - engines: {node: '>=10'} - deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. - hasBin: true - - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - process-warning@5.0.0: resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - - public-encrypt@4.0.3: - resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} - - pump@3.0.4: - resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} - - punycode@1.4.1: - resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} - - qs@6.15.3: - resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} - engines: {node: '>=0.6'} - - querystring-es3@0.2.1: - resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} - engines: {node: '>=0.4.x'} - quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - - randomfill@1.0.4: - resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} - - rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true - - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - real-require@0.2.0: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} - resolve@1.22.12: - resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} - engines: {node: '>= 0.4'} - hasBin: true - retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} - ripemd160@2.0.3: - resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} - engines: {node: '>= 0.8'} - - rivetkit@2.3.4: - resolution: {integrity: sha512-SkQQwt9jXNjKrGFoYXKU72AgEEIV04rysRT5liTjRhH+Dypp+SLBuE3A+Do0BAbfG/F5fZSljk05WTSW556hNg==} + rivetkit@0.0.0-sqlite-uds.4e59a38: + resolution: {integrity: sha512-cZnF3JMeMFirZISiYtZHGrpBPuFXN52A0ctEoM5zON2g7g17x4N3z+sNJOqaUenPL9f73Sv8YPU97UMdeDiZWQ==} engines: {node: '>=22.0.0'} peerDependencies: drizzle-kit: ^0.31.2 @@ -1016,62 +350,10 @@ packages: ws: optional: true - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} - safe-stable-stringify@2.5.0: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} - secure-exec@0.2.1: - resolution: {integrity: sha512-oaQDzTPDSCOckYC8G0PimIqzEVxY6sYEvcx0fMGsRR/Wl4wkFVHaZgQ3kc2DHWysV6WHWt5g1AXc/6seafO2XQ==} - - semver@7.8.5: - resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} - engines: {node: '>=10'} - hasBin: true - - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - - sha.js@2.4.12: - resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} - engines: {node: '>= 0.10'} - hasBin: true - - side-channel-list@1.0.1: - resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.1: - resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} - engines: {node: '>= 0.4'} - - simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - - simple-get@4.0.1: - resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} - sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} @@ -1079,63 +361,8 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - stream-browserify@3.0.0: - resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} - - stream-http@3.2.0: - resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} - - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - tar-fs@2.1.5: - resolution: {integrity: sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==} - - tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} - - thread-stream@3.2.0: - resolution: {integrity: sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==} - - timers-browserify@2.0.12: - resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} - engines: {node: '>=0.6.0'} - - to-buffer@1.2.2: - resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} - engines: {node: '>= 0.4'} - - tty-browserify@0.0.1: - resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} - - tunnel-agent@0.6.0: - resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} - - typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} - engines: {node: '>= 0.4'} - - url@0.11.4: - resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} - engines: {node: '>= 0.4'} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + thread-stream@3.2.0: + resolution: {integrity: sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==} uuid@12.0.1: resolution: {integrity: sha512-9obBF8sMIHJWNQaO6IGOG8giGa/jUpKX34bz6o4whVs8M0WAvhID2tNxYp6A2XEBJPuZSX8wsS/6TEKfIDc+nw==} @@ -1145,33 +372,11 @@ packages: resolution: {integrity: sha512-QsxSVw76NqYUWYPVcQmOnQPX8buIVjgn+yqldTHlWISulBTB9TJ9rnzZceDu+GZmycOtzsmuPbPN1YNxvK12fg==} engines: {node: '>=18.0.0'} - vm-browserify@1.1.2: - resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} - - web-streams-polyfill@4.3.0: - resolution: {integrity: sha512-/Gnggvj9oSrEvJbDyyPtAnxBt5fGQM2iWOKQNu7ie1OxDgK40iZpyV3TKaRiEzVj1oA1UxKnEy9XPXh6PW3eVw==} - engines: {node: '>= 8'} - - which-typed-array@1.1.22: - resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} - engines: {node: '>= 0.4'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - yaml@2.9.0: resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} hasBin: true - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -1200,84 +405,6 @@ snapshots: '@cbor-extract/cbor-extract-win32-x64@2.2.2': optional: true - '@esbuild/aix-ppc64@0.27.7': - optional: true - - '@esbuild/android-arm64@0.27.7': - optional: true - - '@esbuild/android-arm@0.27.7': - optional: true - - '@esbuild/android-x64@0.27.7': - optional: true - - '@esbuild/darwin-arm64@0.27.7': - optional: true - - '@esbuild/darwin-x64@0.27.7': - optional: true - - '@esbuild/freebsd-arm64@0.27.7': - optional: true - - '@esbuild/freebsd-x64@0.27.7': - optional: true - - '@esbuild/linux-arm64@0.27.7': - optional: true - - '@esbuild/linux-arm@0.27.7': - optional: true - - '@esbuild/linux-ia32@0.27.7': - optional: true - - '@esbuild/linux-loong64@0.27.7': - optional: true - - '@esbuild/linux-mips64el@0.27.7': - optional: true - - '@esbuild/linux-ppc64@0.27.7': - optional: true - - '@esbuild/linux-riscv64@0.27.7': - optional: true - - '@esbuild/linux-s390x@0.27.7': - optional: true - - '@esbuild/linux-x64@0.27.7': - optional: true - - '@esbuild/netbsd-arm64@0.27.7': - optional: true - - '@esbuild/netbsd-x64@0.27.7': - optional: true - - '@esbuild/openbsd-arm64@0.27.7': - optional: true - - '@esbuild/openbsd-x64@0.27.7': - optional: true - - '@esbuild/openharmony-arm64@0.27.7': - optional: true - - '@esbuild/sunos-x64@0.27.7': - optional: true - - '@esbuild/win32-arm64@0.27.7': - optional: true - - '@esbuild/win32-ia32@0.27.7': - optional: true - - '@esbuild/win32-x64@0.27.7': - optional: true - '@hono/zod-openapi@1.5.1(hono@4.12.31)(zod@4.4.3)': dependencies: '@asteasolutions/zod-to-openapi': 8.5.0(zod@4.4.3) @@ -1295,104 +422,75 @@ snapshots: '@pinojs/redact@0.4.0': {} - '@rivet-dev/agent-os-core@0.1.1': - dependencies: - '@rivet-dev/agent-os-posix': 0.1.0 - '@rivet-dev/agent-os-python': 0.1.0 - '@secure-exec/core': 0.2.1 - '@secure-exec/nodejs': 0.2.1 - '@secure-exec/v8': 0.2.1 - croner: 10.0.1 - long-timeout: 0.1.1 - secure-exec: 0.2.1 - transitivePeerDependencies: - - pyodide - - '@rivet-dev/agent-os-posix@0.1.0': - dependencies: - '@secure-exec/core': 0.2.1 - - '@rivet-dev/agent-os-python@0.1.0': - dependencies: - '@secure-exec/core': 0.2.1 - '@rivetkit/bare-ts@0.6.2': {} - '@rivetkit/engine-cli-darwin-arm64@2.3.4': - optional: true - - '@rivetkit/engine-cli-darwin-x64@2.3.4': + '@rivetkit/engine-cli-darwin-arm64@0.0.0-sqlite-uds.4e59a38': optional: true - '@rivetkit/engine-cli-linux-arm64-musl@2.3.4': + '@rivetkit/engine-cli-darwin-x64@0.0.0-sqlite-uds.4e59a38': optional: true - '@rivetkit/engine-cli-linux-x64-musl@2.3.4': + '@rivetkit/engine-cli-linux-arm64-musl@0.0.0-sqlite-uds.4e59a38': optional: true - '@rivetkit/engine-cli-win32-x64@2.3.4': + '@rivetkit/engine-cli-linux-x64-musl@0.0.0-sqlite-uds.4e59a38': optional: true - '@rivetkit/engine-cli@2.3.4': + '@rivetkit/engine-cli@0.0.0-sqlite-uds.4e59a38': optionalDependencies: - '@rivetkit/engine-cli-darwin-arm64': 2.3.4 - '@rivetkit/engine-cli-darwin-x64': 2.3.4 - '@rivetkit/engine-cli-linux-arm64-musl': 2.3.4 - '@rivetkit/engine-cli-linux-x64-musl': 2.3.4 - '@rivetkit/engine-cli-win32-x64': 2.3.4 + '@rivetkit/engine-cli-darwin-arm64': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/engine-cli-darwin-x64': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/engine-cli-linux-arm64-musl': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/engine-cli-linux-x64-musl': 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/engine-envoy-protocol@2.3.4': + '@rivetkit/engine-envoy-protocol@0.0.0-sqlite-uds.4e59a38': dependencies: '@rivetkit/bare-ts': 0.6.2 '@rivetkit/on-change@6.0.1': {} - '@rivetkit/rivetkit-napi-darwin-arm64@2.3.4': + '@rivetkit/rivetkit-napi-darwin-arm64@0.0.0-sqlite-uds.4e59a38': optional: true - '@rivetkit/rivetkit-napi-darwin-x64@2.3.4': + '@rivetkit/rivetkit-napi-darwin-x64@0.0.0-sqlite-uds.4e59a38': optional: true - '@rivetkit/rivetkit-napi-linux-arm64-gnu@2.3.4': + '@rivetkit/rivetkit-napi-linux-arm64-gnu@0.0.0-sqlite-uds.4e59a38': optional: true - '@rivetkit/rivetkit-napi-linux-arm64-musl@2.3.4': + '@rivetkit/rivetkit-napi-linux-arm64-musl@0.0.0-sqlite-uds.4e59a38': optional: true - '@rivetkit/rivetkit-napi-linux-x64-gnu@2.3.4': + '@rivetkit/rivetkit-napi-linux-x64-gnu@0.0.0-sqlite-uds.4e59a38': optional: true - '@rivetkit/rivetkit-napi-linux-x64-musl@2.3.4': + '@rivetkit/rivetkit-napi-linux-x64-musl@0.0.0-sqlite-uds.4e59a38': optional: true - '@rivetkit/rivetkit-napi-win32-x64-msvc@2.3.4': - optional: true - - '@rivetkit/rivetkit-napi@2.3.4': + '@rivetkit/rivetkit-napi@0.0.0-sqlite-uds.4e59a38': dependencies: '@napi-rs/cli': 2.18.4 - '@rivetkit/engine-envoy-protocol': 2.3.4 + '@rivetkit/engine-envoy-protocol': 0.0.0-sqlite-uds.4e59a38 optionalDependencies: - '@rivetkit/rivetkit-napi-darwin-arm64': 2.3.4 - '@rivetkit/rivetkit-napi-darwin-x64': 2.3.4 - '@rivetkit/rivetkit-napi-linux-arm64-gnu': 2.3.4 - '@rivetkit/rivetkit-napi-linux-arm64-musl': 2.3.4 - '@rivetkit/rivetkit-napi-linux-x64-gnu': 2.3.4 - '@rivetkit/rivetkit-napi-linux-x64-musl': 2.3.4 - '@rivetkit/rivetkit-napi-win32-x64-msvc': 2.3.4 + '@rivetkit/rivetkit-napi-darwin-arm64': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/rivetkit-napi-darwin-x64': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/rivetkit-napi-linux-arm64-gnu': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/rivetkit-napi-linux-arm64-musl': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/rivetkit-napi-linux-x64-gnu': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/rivetkit-napi-linux-x64-musl': 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/rivetkit-wasm@2.3.4': {} + '@rivetkit/rivetkit-wasm@0.0.0-sqlite-uds.4e59a38': {} - '@rivetkit/traces@2.3.4': + '@rivetkit/traces@0.0.0-sqlite-uds.4e59a38': dependencies: '@rivetkit/bare-ts': 0.6.2 cbor-x: 1.6.4 fdb-tuple: 1.0.0 vbare: 0.0.4 - '@rivetkit/virtual-websocket@2.3.4': {} + '@rivetkit/virtual-websocket@0.0.0-sqlite-uds.4e59a38': {} - '@rivetkit/workflow-engine@2.3.4': + '@rivetkit/workflow-engine@0.0.0-sqlite-uds.4e59a38': dependencies: '@rivetkit/bare-ts': 0.6.2 cbor-x: 1.6.4 @@ -1400,161 +498,10 @@ snapshots: pino: 9.14.0 vbare: 0.0.4 - '@secure-exec/core@0.2.1': - dependencies: - better-sqlite3: 12.11.1 - - '@secure-exec/nodejs@0.2.1': - dependencies: - '@secure-exec/core': 0.2.1 - '@secure-exec/v8': 0.2.1 - cbor-x: 1.6.4 - cjs-module-lexer: 2.2.0 - es-module-lexer: 1.7.0 - esbuild: 0.27.7 - node-stdlib-browser: 1.3.1 - web-streams-polyfill: 4.3.0 - - '@secure-exec/v8-darwin-arm64@0.2.1': - optional: true - - '@secure-exec/v8-darwin-x64@0.2.1': - optional: true - - '@secure-exec/v8-linux-arm64-gnu@0.2.1': - optional: true - - '@secure-exec/v8-linux-x64-gnu@0.2.1': - optional: true - - '@secure-exec/v8@0.2.1': - dependencies: - cbor-x: 1.6.4 - optionalDependencies: - '@secure-exec/v8-darwin-arm64': 0.2.1 - '@secure-exec/v8-darwin-x64': 0.2.1 - '@secure-exec/v8-linux-arm64-gnu': 0.2.1 - '@secure-exec/v8-linux-x64-gnu': 0.2.1 - '@types/retry@0.12.2': {} - asn1.js@4.10.1: - dependencies: - bn.js: 4.12.5 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - - assert@2.1.0: - dependencies: - call-bind: 1.0.9 - is-nan: 1.3.2 - object-is: 1.1.6 - object.assign: 4.1.7 - util: 0.12.5 - atomic-sleep@1.0.0: {} - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.1.0 - - base64-js@1.5.1: {} - - better-sqlite3@12.11.1: - dependencies: - bindings: 1.5.0 - prebuild-install: 7.1.3 - - bindings@1.5.0: - dependencies: - file-uri-to-path: 1.0.0 - - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - - bn.js@4.12.5: {} - - bn.js@5.2.5: {} - - brorand@1.1.0: {} - - browser-resolve@2.0.0: - dependencies: - resolve: 1.22.12 - - browserify-aes@1.2.0: - dependencies: - buffer-xor: 1.0.3 - cipher-base: 1.0.7 - create-hash: 1.2.0 - evp_bytestokey: 1.0.3 - inherits: 2.0.4 - safe-buffer: 5.2.1 - - browserify-cipher@1.0.1: - dependencies: - browserify-aes: 1.2.0 - browserify-des: 1.0.2 - evp_bytestokey: 1.0.3 - - browserify-des@1.0.2: - dependencies: - cipher-base: 1.0.7 - des.js: 1.1.0 - inherits: 2.0.4 - safe-buffer: 5.2.1 - - browserify-rsa@4.1.1: - dependencies: - bn.js: 5.2.5 - randombytes: 2.1.0 - safe-buffer: 5.2.1 - - browserify-sign@4.2.6: - dependencies: - bn.js: 5.2.5 - browserify-rsa: 4.1.1 - create-hash: 1.2.0 - create-hmac: 1.1.7 - elliptic: 6.6.1 - inherits: 2.0.4 - parse-asn1: 5.1.9 - readable-stream: 2.3.8 - safe-buffer: 5.2.1 - - browserify-zlib@0.2.0: - dependencies: - pako: 1.0.11 - - buffer-xor@1.0.3: {} - - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - builtin-status-codes@3.0.0: {} - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bind@1.0.9: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - cbor-extract@2.2.2: dependencies: node-gyp-build-optional-packages: 5.1.1 @@ -1571,448 +518,44 @@ snapshots: optionalDependencies: cbor-extract: 2.2.2 - chownr@1.1.4: {} - - cipher-base@1.0.7: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - to-buffer: 1.2.2 - - cjs-module-lexer@2.2.0: {} - - console-browserify@1.2.0: {} - - constants-browserify@1.0.0: {} - - core-util-is@1.0.3: {} - - create-ecdh@4.0.4: - dependencies: - bn.js: 4.12.5 - elliptic: 6.6.1 - - create-hash@1.2.0: - dependencies: - cipher-base: 1.0.7 - inherits: 2.0.4 - md5.js: 1.3.5 - ripemd160: 2.0.3 - sha.js: 2.4.12 - - create-hmac@1.1.7: - dependencies: - cipher-base: 1.0.7 - create-hash: 1.2.0 - inherits: 2.0.4 - ripemd160: 2.0.3 - safe-buffer: 5.2.1 - sha.js: 2.4.12 - - create-require@1.1.1: {} - - croner@10.0.1: {} - - crypto-browserify@3.12.1: - dependencies: - browserify-cipher: 1.0.1 - browserify-sign: 4.2.6 - create-ecdh: 4.0.4 - create-hash: 1.2.0 - create-hmac: 1.1.7 - diffie-hellman: 5.0.3 - hash-base: 3.0.5 - inherits: 2.0.4 - pbkdf2: 3.1.6 - public-encrypt: 4.0.3 - randombytes: 2.1.0 - randomfill: 1.0.4 - - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - - deep-extend@0.6.0: {} - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - - des.js@1.1.0: - dependencies: - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - - detect-libc@2.1.2: {} - - diffie-hellman@5.0.3: - dependencies: - bn.js: 4.12.5 - miller-rabin: 4.0.1 - randombytes: 2.1.0 - - domain-browser@4.22.0: {} - - drizzle-orm@0.44.7(better-sqlite3@12.11.1): - optionalDependencies: - better-sqlite3: 12.11.1 - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - elliptic@6.6.1: - dependencies: - bn.js: 4.12.5 - brorand: 1.1.0 - hash.js: 1.1.7 - hmac-drbg: 1.0.1 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-module-lexer@1.7.0: {} - - es-object-atoms@1.1.2: - dependencies: - es-errors: 1.3.0 - - esbuild@0.27.7: - optionalDependencies: - '@esbuild/aix-ppc64': 0.27.7 - '@esbuild/android-arm': 0.27.7 - '@esbuild/android-arm64': 0.27.7 - '@esbuild/android-x64': 0.27.7 - '@esbuild/darwin-arm64': 0.27.7 - '@esbuild/darwin-x64': 0.27.7 - '@esbuild/freebsd-arm64': 0.27.7 - '@esbuild/freebsd-x64': 0.27.7 - '@esbuild/linux-arm': 0.27.7 - '@esbuild/linux-arm64': 0.27.7 - '@esbuild/linux-ia32': 0.27.7 - '@esbuild/linux-loong64': 0.27.7 - '@esbuild/linux-mips64el': 0.27.7 - '@esbuild/linux-ppc64': 0.27.7 - '@esbuild/linux-riscv64': 0.27.7 - '@esbuild/linux-s390x': 0.27.7 - '@esbuild/linux-x64': 0.27.7 - '@esbuild/netbsd-arm64': 0.27.7 - '@esbuild/netbsd-x64': 0.27.7 - '@esbuild/openbsd-arm64': 0.27.7 - '@esbuild/openbsd-x64': 0.27.7 - '@esbuild/openharmony-arm64': 0.27.7 - '@esbuild/sunos-x64': 0.27.7 - '@esbuild/win32-arm64': 0.27.7 - '@esbuild/win32-ia32': 0.27.7 - '@esbuild/win32-x64': 0.27.7 - - events@3.3.0: {} - - evp_bytestokey@1.0.3: - dependencies: - md5.js: 1.3.5 - safe-buffer: 5.2.1 + detect-libc@2.1.2: + optional: true - expand-template@2.0.3: {} + drizzle-orm@0.44.7: {} fdb-tuple@1.0.0: {} - file-uri-to-path@1.0.0: {} - - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 - - fs-constants@1.0.0: {} - - function-bind@1.1.2: {} - - generator-function@2.0.1: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.2 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.4 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.2 - - github-from-package@0.0.0: {} - - gopd@1.2.0: {} - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hash-base@3.0.5: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - - hash-base@3.1.2: - dependencies: - inherits: 2.0.4 - readable-stream: 2.3.8 - safe-buffer: 5.2.1 - to-buffer: 1.2.2 - - hash.js@1.1.7: - dependencies: - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - - hasown@2.0.4: - dependencies: - function-bind: 1.1.2 - - hmac-drbg@1.0.1: - dependencies: - hash.js: 1.1.7 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - hono@4.12.31: {} - https-browserify@1.0.0: {} - - ieee754@1.2.1: {} - - inherits@2.0.4: {} - - ini@1.3.8: {} - invariant@2.2.4: dependencies: loose-envify: 1.4.0 - is-arguments@1.2.0: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-callable@1.2.7: {} - - is-core-module@2.16.2: - dependencies: - hasown: 2.0.4 - - is-generator-function@1.1.2: - dependencies: - call-bound: 1.0.4 - generator-function: 2.0.1 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - - is-nan@1.3.2: - dependencies: - call-bind: 1.0.9 - define-properties: 1.2.1 - is-network-error@1.3.2: {} - is-regex@1.2.1: - dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.4 - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.22 - - isarray@1.0.0: {} - - isarray@2.0.5: {} - - isomorphic-timers-promises@1.0.1: {} - js-tokens@4.0.0: {} - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - - long-timeout@0.1.1: {} - loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 - math-intrinsics@1.1.0: {} - - md5.js@1.3.5: - dependencies: - hash-base: 3.0.5 - inherits: 2.0.4 - safe-buffer: 5.2.1 - - miller-rabin@4.0.1: - dependencies: - bn.js: 4.12.5 - brorand: 1.1.0 - - mimic-response@3.1.0: {} - - minimalistic-assert@1.0.1: {} - - minimalistic-crypto-utils@1.0.1: {} - - minimist@1.2.8: {} - - mkdirp-classic@0.5.3: {} - - napi-build-utils@2.0.0: {} - - node-abi@3.94.0: - dependencies: - semver: 7.8.5 - node-gyp-build-optional-packages@5.1.1: dependencies: detect-libc: 2.1.2 optional: true - node-stdlib-browser@1.3.1: - dependencies: - assert: 2.1.0 - browser-resolve: 2.0.0 - browserify-zlib: 0.2.0 - buffer: 5.7.1 - console-browserify: 1.2.0 - constants-browserify: 1.0.0 - create-require: 1.1.1 - crypto-browserify: 3.12.1 - domain-browser: 4.22.0 - events: 3.3.0 - https-browserify: 1.0.0 - isomorphic-timers-promises: 1.0.1 - os-browserify: 0.3.0 - path-browserify: 1.0.1 - pkg-dir: 5.0.0 - process: 0.11.10 - punycode: 1.4.1 - querystring-es3: 0.2.1 - readable-stream: 3.6.2 - stream-browserify: 3.0.0 - stream-http: 3.2.0 - string_decoder: 1.3.0 - timers-browserify: 2.0.12 - tty-browserify: 0.0.1 - url: 0.11.4 - util: 0.12.5 - vm-browserify: 1.1.2 - - object-inspect@1.13.4: {} - - object-is@1.1.6: - dependencies: - call-bind: 1.0.9 - define-properties: 1.2.1 - - object-keys@1.1.1: {} - - object.assign@4.1.7: - dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.2 - has-symbols: 1.1.0 - object-keys: 1.1.1 - on-exit-leak-free@2.1.2: {} - once@1.4.0: - dependencies: - wrappy: 1.0.2 - openapi3-ts@4.6.0: dependencies: yaml: 2.9.0 - os-browserify@0.3.0: {} - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - p-retry@6.2.1: dependencies: '@types/retry': 0.12.2 is-network-error: 1.3.2 retry: 0.13.1 - pako@1.0.11: {} - - parse-asn1@5.1.9: - dependencies: - asn1.js: 4.10.1 - browserify-aes: 1.2.0 - evp_bytestokey: 1.0.3 - pbkdf2: 3.1.6 - safe-buffer: 5.2.1 - - path-browserify@1.0.1: {} - - path-exists@4.0.0: {} - - path-parse@1.0.7: {} - - pbkdf2@3.1.6: - dependencies: - create-hash: 1.2.0 - create-hmac: 1.1.7 - ripemd160: 2.0.3 - safe-buffer: 5.2.1 - sha.js: 2.4.12 - to-buffer: 1.2.2 - pino-abstract-transport@2.0.0: dependencies: split2: 4.2.0 @@ -2033,121 +576,28 @@ snapshots: sonic-boom: 4.2.1 thread-stream: 3.2.0 - pkg-dir@5.0.0: - dependencies: - find-up: 5.0.0 - - possible-typed-array-names@1.1.0: {} - - prebuild-install@7.1.3: - dependencies: - detect-libc: 2.1.2 - expand-template: 2.0.3 - github-from-package: 0.0.0 - minimist: 1.2.8 - mkdirp-classic: 0.5.3 - napi-build-utils: 2.0.0 - node-abi: 3.94.0 - pump: 3.0.4 - rc: 1.2.8 - simple-get: 4.0.1 - tar-fs: 2.1.5 - tunnel-agent: 0.6.0 - - process-nextick-args@2.0.1: {} - process-warning@5.0.0: {} - process@0.11.10: {} - - public-encrypt@4.0.3: - dependencies: - bn.js: 4.12.5 - browserify-rsa: 4.1.1 - create-hash: 1.2.0 - parse-asn1: 5.1.9 - randombytes: 2.1.0 - safe-buffer: 5.2.1 - - pump@3.0.4: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - - punycode@1.4.1: {} - - qs@6.15.3: - dependencies: - es-define-property: 1.0.1 - side-channel: 1.1.1 - - querystring-es3@0.2.1: {} - quick-format-unescaped@4.0.4: {} - randombytes@2.1.0: - dependencies: - safe-buffer: 5.2.1 - - randomfill@1.0.4: - dependencies: - randombytes: 2.1.0 - safe-buffer: 5.2.1 - - rc@1.2.8: - dependencies: - deep-extend: 0.6.0 - ini: 1.3.8 - minimist: 1.2.8 - strip-json-comments: 2.0.1 - - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - real-require@0.2.0: {} - resolve@1.22.12: - dependencies: - es-errors: 1.3.0 - is-core-module: 2.16.2 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - retry@0.13.1: {} - ripemd160@2.0.3: - dependencies: - hash-base: 3.1.2 - inherits: 2.0.4 - - rivetkit@2.3.4(better-sqlite3@12.11.1): + rivetkit@0.0.0-sqlite-uds.4e59a38: dependencies: '@hono/zod-openapi': 1.5.1(hono@4.12.31)(zod@4.4.3) - '@rivet-dev/agent-os-core': 0.1.1 '@rivetkit/bare-ts': 0.6.2 - '@rivetkit/engine-cli': 2.3.4 - '@rivetkit/engine-envoy-protocol': 2.3.4 + '@rivetkit/engine-cli': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/engine-envoy-protocol': 0.0.0-sqlite-uds.4e59a38 '@rivetkit/on-change': 6.0.1 - '@rivetkit/rivetkit-napi': 2.3.4 - '@rivetkit/rivetkit-wasm': 2.3.4 - '@rivetkit/traces': 2.3.4 - '@rivetkit/virtual-websocket': 2.3.4 - '@rivetkit/workflow-engine': 2.3.4 + '@rivetkit/rivetkit-napi': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/rivetkit-wasm': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/traces': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/virtual-websocket': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/workflow-engine': 0.0.0-sqlite-uds.4e59a38 cbor-x: 1.6.4 - drizzle-orm: 0.44.7(better-sqlite3@12.11.1) + drizzle-orm: 0.44.7 hono: 4.12.31 invariant: 2.2.4 p-retry: 6.2.1 @@ -2183,192 +633,25 @@ snapshots: - pg - postgres - prisma - - pyodide - sql.js - sqlite3 - safe-buffer@5.1.2: {} - - safe-buffer@5.2.1: {} - - safe-regex-test@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-regex: 1.2.1 - safe-stable-stringify@2.5.0: {} - secure-exec@0.2.1: - dependencies: - '@secure-exec/core': 0.2.1 - '@secure-exec/nodejs': 0.2.1 - - semver@7.8.5: {} - - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - setimmediate@1.0.5: {} - - sha.js@2.4.12: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - to-buffer: 1.2.2 - - side-channel-list@1.0.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.1 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - - simple-concat@1.0.1: {} - - simple-get@4.0.1: - dependencies: - decompress-response: 6.0.0 - once: 1.4.0 - simple-concat: 1.0.1 - sonic-boom@4.2.1: dependencies: atomic-sleep: 1.0.0 split2@4.2.0: {} - stream-browserify@3.0.0: - dependencies: - inherits: 2.0.4 - readable-stream: 3.6.2 - - stream-http@3.2.0: - dependencies: - builtin-status-codes: 3.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - xtend: 4.0.2 - - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - - strip-json-comments@2.0.1: {} - - supports-preserve-symlinks-flag@1.0.0: {} - - tar-fs@2.1.5: - dependencies: - chownr: 1.1.4 - mkdirp-classic: 0.5.3 - pump: 3.0.4 - tar-stream: 2.2.0 - - tar-stream@2.2.0: - dependencies: - bl: 4.1.0 - end-of-stream: 1.4.5 - fs-constants: 1.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - thread-stream@3.2.0: dependencies: real-require: 0.2.0 - timers-browserify@2.0.12: - dependencies: - setimmediate: 1.0.5 - - to-buffer@1.2.2: - dependencies: - isarray: 2.0.5 - safe-buffer: 5.2.1 - typed-array-buffer: 1.0.3 - - tty-browserify@0.0.1: {} - - tunnel-agent@0.6.0: - dependencies: - safe-buffer: 5.2.1 - - typed-array-buffer@1.0.3: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-typed-array: 1.1.15 - - url@0.11.4: - dependencies: - punycode: 1.4.1 - qs: 6.15.3 - - util-deprecate@1.0.2: {} - - util@0.12.5: - dependencies: - inherits: 2.0.4 - is-arguments: 1.2.0 - is-generator-function: 1.1.2 - is-typed-array: 1.1.15 - which-typed-array: 1.1.22 - uuid@12.0.1: {} vbare@0.0.4: {} - vm-browserify@1.1.2: {} - - web-streams-polyfill@4.3.0: {} - - which-typed-array@1.1.22: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.9 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - - wrappy@1.0.2: {} - - xtend@4.0.2: {} - yaml@2.9.0: {} - yocto-queue@0.1.0: {} - zod@4.4.3: {} diff --git a/packages/runtime-core/tests/native-client.test.ts b/packages/runtime-core/tests/native-client.test.ts index a3ee6c19c7..b0c7308352 100644 --- a/packages/runtime-core/tests/native-client.test.ts +++ b/packages/runtime-core/tests/native-client.test.ts @@ -12,7 +12,7 @@ const ownership = { describe("stdio sidecar protocol client", () => { test("drives a stdio protocol process", async () => { - const fixtureRoot = mkdtempSync(join(tmpdir(), "secure-exec-client-")); + const fixtureRoot = mkdtempSync(join(tmpdir(), "agentos-client-")); const driverPath = join(fixtureRoot, "fake-sidecar.mjs"); writeFileSync( driverPath, @@ -90,7 +90,7 @@ describe("stdio sidecar protocol client", () => { }); test("registers native spawn for the shared SidecarProcess wrapper", async () => { - const fixtureRoot = mkdtempSync(join(tmpdir(), "secure-exec-process-")); + const fixtureRoot = mkdtempSync(join(tmpdir(), "agentos-process-")); const driverPath = join(fixtureRoot, "fake-sidecar.mjs"); writeFileSync( driverPath, diff --git a/packages/runtime-core/tests/node-runtime-decode.test.ts b/packages/runtime-core/tests/node-runtime-decode.test.ts index 7eb9cbb540..74fc66030e 100644 --- a/packages/runtime-core/tests/node-runtime-decode.test.ts +++ b/packages/runtime-core/tests/node-runtime-decode.test.ts @@ -4,7 +4,7 @@ import { describe, expect, test } from "vitest"; // Regression guard for #11 and #59. // -// Both issues were caused by the removed `@secure-exec/v8` runtime.js +// Both issues were caused by the removed legacy V8 runtime.js // (a stdout/socket-path handshake plus a TDZ bug) together with a host-side // `node:v8.deserialize()` decode of `run()` results. That host-side V8 // serialization made result decoding sensitive to the host Node version / diff --git a/packages/runtime-sidecar/npm/darwin-arm64/package.json b/packages/runtime-sidecar/npm/darwin-arm64/package.json index 9a30b703e0..738e2aae57 100644 --- a/packages/runtime-sidecar/npm/darwin-arm64/package.json +++ b/packages/runtime-sidecar/npm/darwin-arm64/package.json @@ -1,11 +1,11 @@ { "name": "@rivet-dev/agentos-runtime-sidecar-darwin-arm64", "version": "0.0.1", - "description": "Secure Exec native sidecar binary for macOS arm64", + "description": "AgentOS language execution native sidecar binary for macOS arm64", "license": "Apache-2.0", "repository": { "type": "git", - "url": "https://github.com/rivet-dev/secure-exec.git", + "url": "https://github.com/rivet-dev/agentos.git", "directory": "packages/sidecar/npm/darwin-arm64" }, "os": [ diff --git a/packages/runtime-sidecar/npm/darwin-x64/package.json b/packages/runtime-sidecar/npm/darwin-x64/package.json index 0dd77457dd..99b3d32c43 100644 --- a/packages/runtime-sidecar/npm/darwin-x64/package.json +++ b/packages/runtime-sidecar/npm/darwin-x64/package.json @@ -1,11 +1,11 @@ { "name": "@rivet-dev/agentos-runtime-sidecar-darwin-x64", "version": "0.0.1", - "description": "Secure Exec native sidecar binary for macOS x64", + "description": "AgentOS language execution native sidecar binary for macOS x64", "license": "Apache-2.0", "repository": { "type": "git", - "url": "https://github.com/rivet-dev/secure-exec.git", + "url": "https://github.com/rivet-dev/agentos.git", "directory": "packages/sidecar/npm/darwin-x64" }, "os": [ diff --git a/packages/runtime-sidecar/npm/linux-arm64-gnu/package.json b/packages/runtime-sidecar/npm/linux-arm64-gnu/package.json index 430e10f5d9..d310422730 100644 --- a/packages/runtime-sidecar/npm/linux-arm64-gnu/package.json +++ b/packages/runtime-sidecar/npm/linux-arm64-gnu/package.json @@ -1,11 +1,11 @@ { "name": "@rivet-dev/agentos-runtime-sidecar-linux-arm64-gnu", "version": "0.0.1", - "description": "Secure Exec native sidecar binary for Linux arm64 (glibc)", + "description": "AgentOS language execution native sidecar binary for Linux arm64 (glibc)", "license": "Apache-2.0", "repository": { "type": "git", - "url": "https://github.com/rivet-dev/secure-exec.git", + "url": "https://github.com/rivet-dev/agentos.git", "directory": "packages/sidecar/npm/linux-arm64-gnu" }, "os": [ diff --git a/packages/runtime-sidecar/npm/linux-x64-gnu/package.json b/packages/runtime-sidecar/npm/linux-x64-gnu/package.json index 38ae2b9c0f..43b30f593c 100644 --- a/packages/runtime-sidecar/npm/linux-x64-gnu/package.json +++ b/packages/runtime-sidecar/npm/linux-x64-gnu/package.json @@ -1,11 +1,11 @@ { "name": "@rivet-dev/agentos-runtime-sidecar-linux-x64-gnu", "version": "0.0.1", - "description": "Secure Exec native sidecar binary for Linux x64 (glibc)", + "description": "AgentOS language execution native sidecar binary for Linux x64 (glibc)", "license": "Apache-2.0", "repository": { "type": "git", - "url": "https://github.com/rivet-dev/secure-exec.git", + "url": "https://github.com/rivet-dev/agentos.git", "directory": "packages/sidecar/npm/linux-x64-gnu" }, "os": [ diff --git a/packages/runtime-sidecar/package.json b/packages/runtime-sidecar/package.json index 75de6c45eb..ce22d9202a 100644 --- a/packages/runtime-sidecar/package.json +++ b/packages/runtime-sidecar/package.json @@ -1,11 +1,11 @@ { "name": "@rivet-dev/agentos-runtime-sidecar", "version": "0.0.1", - "description": "Platform-specific resolver for the Secure Exec native sidecar binary", + "description": "Platform-specific resolver for the AgentOS language execution native sidecar binary", "license": "Apache-2.0", "repository": { "type": "git", - "url": "https://github.com/rivet-dev/secure-exec.git", + "url": "https://github.com/rivet-dev/agentos.git", "directory": "packages/sidecar" }, "main": "./index.js", diff --git a/packages/secure-exec-example-ai-agent-type-check/package.json b/packages/secure-exec-example-ai-agent-type-check/package.json deleted file mode 100644 index 1335d2cb7d..0000000000 --- a/packages/secure-exec-example-ai-agent-type-check/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "@rivet-dev/agentos-example-ai-agent-type-check", - "private": true, - "type": "module", - "scripts": { - "check-types": "tsc --noEmit -p tsconfig.json", - "dev": "tsx src/index.ts", - "verify-docs": "node scripts/verify-docs.mjs" - }, - "dependencies": { - "@ai-sdk/anthropic": "^3.0.58", - "@rivet-dev/agentos-typescript": "workspace:*", - "ai": "^6.0.116", - "@rivet-dev/agentos-core": "workspace:*", - "typescript": "^5.7.2", - "zod": "^3.24.0" - }, - "devDependencies": { - "@types/node": "^22.10.2", - "tsx": "^4.19.2" - } -} diff --git a/packages/secure-exec-example-ai-agent-type-check/scripts/verify-docs.mjs b/packages/secure-exec-example-ai-agent-type-check/scripts/verify-docs.mjs deleted file mode 100644 index 4d8d667aa3..0000000000 --- a/packages/secure-exec-example-ai-agent-type-check/scripts/verify-docs.mjs +++ /dev/null @@ -1,78 +0,0 @@ -import { readFile } from "node:fs/promises"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const repoRoot = path.resolve(__dirname, "../../.."); -const docsPath = path.join(repoRoot, "docs/features/typescript.mdx"); - -const expectedFiles = new Map([ - [ - "Type-Checked Execution", - path.join( - repoRoot, - "packages/secure-exec-example-ai-agent-type-check/src/index.ts", - ), - ], -]); - -function normalizeTitle(title) { - return title.trim().replace(/^"|"$/g, ""); -} - -function normalizeCode(source) { - const normalized = source.replace(/\r\n/g, "\n").replace(/^\n+|\n+$/g, ""); - const lines = normalized.split("\n"); - const nonEmptyLines = lines.filter((line) => line.trim().length > 0); - const minIndent = nonEmptyLines.reduce((indent, line) => { - const lineIndent = line.match(/^ */)?.[0].length ?? 0; - return Math.min(indent, lineIndent); - }, Number.POSITIVE_INFINITY); - - if (!Number.isFinite(minIndent) || minIndent === 0) { - return normalized; - } - - return lines.map((line) => line.slice(minIndent)).join("\n"); -} - -const docsSource = await readFile(docsPath, "utf8"); -const blockPattern = /^\s*```ts(?:\s+([^\n]+))?\n([\s\S]*?)^\s*```/gm; -const docBlocks = new Map(); - -for (const match of docsSource.matchAll(blockPattern)) { - const rawTitle = match[1]; - if (!rawTitle) { - continue; - } - - const title = normalizeTitle(rawTitle); - if (!expectedFiles.has(title)) { - continue; - } - - docBlocks.set(title, normalizeCode(match[2] ?? "")); -} - -const mismatches = []; - -for (const [title, filePath] of expectedFiles) { - const fileSource = normalizeCode(await readFile(filePath, "utf8")); - const docSource = docBlocks.get(title); - - if (!docSource) { - mismatches.push(`Missing docs snippet for ${title}`); - continue; - } - - if (docSource !== fileSource) { - mismatches.push(`Snippet mismatch for ${title}`); - } -} - -if (mismatches.length > 0) { - console.error(mismatches.join("\n")); - process.exit(1); -} - -console.log("AI agent docs match example sources."); diff --git a/packages/secure-exec-example-ai-agent-type-check/src/index.ts b/packages/secure-exec-example-ai-agent-type-check/src/index.ts deleted file mode 100644 index eba7435898..0000000000 --- a/packages/secure-exec-example-ai-agent-type-check/src/index.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { join } from "node:path"; -import { anthropic } from "@ai-sdk/anthropic"; -import { createTypeScriptTools } from "@rivet-dev/agentos-typescript"; -import { nodeModulesMount } from "@rivet-dev/agentos-core"; -import { generateText, stepCountIs, tool } from "ai"; -import { - allowAll, - createKernel, - createNodeDriver, - createNodeRuntime, - createNodeRuntimeDriverFactory, -} from "@rivet-dev/agentos-core/internal/runtime-compat"; -import { createInMemoryFileSystem } from "@rivet-dev/agentos-core/test/runtime"; -import { z } from "zod"; - -const filesystem = createInMemoryFileSystem(); -const systemDriver = createNodeDriver({ - filesystem, - mounts: [nodeModulesMount(join(process.cwd(), "node_modules"))], - permissions: allowAll, -}); -const runtimeDriverFactory = createNodeRuntimeDriverFactory(); -const ts = createTypeScriptTools({ - systemDriver, - runtimeDriverFactory, - memoryLimit: 256, - cpuTimeLimitMs: 5000, -}); - -const { text } = await generateText({ - model: anthropic("claude-sonnet-4-6"), - prompt: - "Write TypeScript that calculates the first 20 fibonacci numbers. Assign the result to module.exports.", - stopWhen: stepCountIs(5), - tools: { - execute_typescript: tool({ - description: - "Type-check TypeScript in a sandbox, compile it, then run the emitted JavaScript in a sandbox. Return diagnostics when validation fails.", - inputSchema: z.object({ code: z.string() }), - execute: async ({ code }) => { - const typecheck = await ts.typecheckSource({ - sourceText: code, - filePath: "/root/generated.ts", - compilerOptions: { - module: "commonjs", - target: "es2022", - }, - }); - - if (!typecheck.success) { - return { - ok: false, - stage: "typecheck", - diagnostics: typecheck.diagnostics, - }; - } - - const compiled = await ts.compileSource({ - sourceText: code, - filePath: "/root/generated.ts", - compilerOptions: { - module: "commonjs", - target: "es2022", - }, - }); - - if (!compiled.success || !compiled.outputText) { - return { - ok: false, - stage: "compile", - diagnostics: compiled.diagnostics, - }; - } - - try { - await filesystem.mkdir("/root", { recursive: true }); - await filesystem.writeFile("/root/generated.js", compiled.outputText); - const kernel = createKernel({ - filesystem, - permissions: allowAll, - syncFilesystemOnDispose: false, - }); - let stdout = ""; - let stderr = ""; - try { - await kernel.mount(createNodeRuntime()); - const child = kernel.spawn( - "node", - [ - "-e", - "const exportsValue = require('/root/generated.js'); console.log(JSON.stringify(exportsValue));", - ], - { - onStdout: (chunk) => { - stdout += Buffer.from(chunk).toString("utf8"); - }, - onStderr: (chunk) => { - stderr += Buffer.from(chunk).toString("utf8"); - }, - }, - ); - const exitCode = await child.wait(); - if (exitCode !== 0) { - throw new Error( - stderr.trim() || `sandboxed JavaScript exited ${exitCode}`, - ); - } - } finally { - await kernel.dispose(); - } - - return { - ok: true, - stage: "run", - exports: JSON.parse(stdout), - }; - } catch (error) { - return { - ok: false, - stage: "run", - errorMessage: - error instanceof Error ? error.message : String(error), - }; - } - }, - }), - }, -}); - -console.log(text); diff --git a/packages/secure-exec-example-ai-agent-type-check/tsconfig.json b/packages/secure-exec-example-ai-agent-type-check/tsconfig.json deleted file mode 100644 index eb71769d9d..0000000000 --- a/packages/secure-exec-example-ai-agent-type-check/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "noEmit": true, - "baseUrl": ".", - "paths": { - "@rivet-dev/agentos-typescript": ["../typescript/src/index.ts"], - "@rivet-dev/agentos-core": ["../core/src/index.ts"], - "@rivet-dev/agentos-core/internal/runtime-compat": ["../core/src/runtime-compat.ts"] - } - }, - "include": ["src/**/*.ts", "../core/src/cron/long-timeout.d.ts"] -} diff --git a/packages/secure-exec/README.md b/packages/secure-exec/README.md deleted file mode 100644 index c315a349a5..0000000000 --- a/packages/secure-exec/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# secure-exec - -Public Secure-Exec compatibility package backed by Agent OS runtime primitives. - -Use `secure-exec` when you need the documented stable Secure-Exec Node runtime -surface. New product-facing SDK work should use `@rivet-dev/agentos`. diff --git a/packages/secure-exec/src/index.ts b/packages/secure-exec/src/index.ts deleted file mode 100644 index 4714b3609f..0000000000 --- a/packages/secure-exec/src/index.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Public Secure-Exec compatibility surface backed by Agent OS primitives. - * - * This intentionally exposes only the stable Node-focused API. Deferred - * compatibility packages such as browser and Python remain out of scope. - */ - -export type { - BindingFunction, - BindingTree, - DefaultNetworkAdapterOptions, - DirEntry, - ExecOptions, - ExecResult, - Kernel, - KernelInterface, - NetworkAdapter, - NodeRuntimeDriver, - NodeRuntimeDriverFactory, - NodeRuntimeDriverFactoryOptions, - NodeRuntimeOptions, - OSConfig, - Permissions, - ProcessConfig, - ResourceBudgets, - RunResult, - StatInfo, - StdioChannel, - StdioEvent, - StdioHook, - TimingMitigation, - VirtualFileSystem, -} from "@rivet-dev/agentos-core/internal/runtime-compat"; -export type { NodeModulesMountConfig } from "@rivet-dev/agentos-core"; -export { - allowAll, - allowAllChildProcess, - allowAllEnv, - allowAllFs, - allowAllNetwork, - createDefaultNetworkAdapter, - createKernel, - createNodeDriver, - createNodeHostCommandExecutor, - createNodeRuntime, - createNodeRuntimeDriverFactory, - exists, - isPrivateIp, - mkdir, - NodeExecutionDriver, - NodeFileSystem, - NodeRuntime, - readDirWithTypes, - rename, - stat, -} from "@rivet-dev/agentos-core/internal/runtime-compat"; -export { nodeModulesMount } from "@rivet-dev/agentos-core"; diff --git a/packages/secure-exec/tests/public-api.test.ts b/packages/secure-exec/tests/public-api.test.ts deleted file mode 100644 index e15d195bcb..0000000000 --- a/packages/secure-exec/tests/public-api.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { - type BindingFunction, - type BindingTree, - NodeExecutionDriver as CoreNodeExecutionDriver, - NodeFileSystem as CoreNodeFileSystem, - NodeRuntime as CoreNodeRuntime, - allowAll as coreAllowAll, - allowAllChildProcess as coreAllowAllChildProcess, - allowAllEnv as coreAllowAllEnv, - allowAllFs as coreAllowAllFs, - allowAllNetwork as coreAllowAllNetwork, - createDefaultNetworkAdapter as coreCreateDefaultNetworkAdapter, - createKernel as coreCreateKernel, - createNodeDriver as coreCreateNodeDriver, - createNodeHostCommandExecutor as coreCreateNodeHostCommandExecutor, - createNodeRuntime as coreCreateNodeRuntime, - createNodeRuntimeDriverFactory as coreCreateNodeRuntimeDriverFactory, - exists as coreExists, - isPrivateIp as coreIsPrivateIp, - mkdir as coreMkdir, - readDirWithTypes as coreReadDirWithTypes, - rename as coreRename, - stat as coreStat, - type DefaultNetworkAdapterOptions, - type DirEntry, - type ExecOptions, - type ExecResult, - type Kernel, - type KernelInterface, - type ModuleAccessOptions, - type NetworkAdapter, - type NodeRuntimeDriver, - type NodeRuntimeDriverFactory, - type NodeRuntimeDriverFactoryOptions, - type NodeRuntimeOptions, - type OSConfig, - type Permissions, - type ProcessConfig, - type ResourceBudgets, - type RunResult, - type StatInfo, - type StdioChannel, - type StdioEvent, - type StdioHook, - type TimingMitigation, - type VirtualFileSystem, -} from "@rivet-dev/agentos-core/internal/runtime-compat"; -import * as secureExec from "secure-exec"; -import { describe, expect, it } from "vitest"; - -describe("secure-exec", () => { - it("re-exports the stable compatibility surface from Agent OS", () => { - expect(secureExec.NodeRuntime).toBe(CoreNodeRuntime); - expect(secureExec.NodeExecutionDriver).toBe(CoreNodeExecutionDriver); - expect(secureExec.NodeFileSystem).toBe(CoreNodeFileSystem); - expect(secureExec.createDefaultNetworkAdapter).toBe( - coreCreateDefaultNetworkAdapter, - ); - expect(secureExec.createNodeDriver).toBe(coreCreateNodeDriver); - expect(secureExec.createNodeHostCommandExecutor).toBe( - coreCreateNodeHostCommandExecutor, - ); - expect(secureExec.createNodeRuntime).toBe(coreCreateNodeRuntime); - expect(secureExec.createNodeRuntimeDriverFactory).toBe( - coreCreateNodeRuntimeDriverFactory, - ); - expect(secureExec.createKernel).toBe(coreCreateKernel); - expect(secureExec.allowAll).toBe(coreAllowAll); - expect(secureExec.allowAllFs).toBe(coreAllowAllFs); - expect(secureExec.allowAllNetwork).toBe(coreAllowAllNetwork); - expect(secureExec.allowAllChildProcess).toBe(coreAllowAllChildProcess); - expect(secureExec.allowAllEnv).toBe(coreAllowAllEnv); - expect(secureExec.exists).toBe(coreExists); - expect(secureExec.stat).toBe(coreStat); - expect(secureExec.rename).toBe(coreRename); - expect(secureExec.readDirWithTypes).toBe(coreReadDirWithTypes); - expect(secureExec.mkdir).toBe(coreMkdir); - expect(secureExec.isPrivateIp).toBe(coreIsPrivateIp); - }); - - it("preserves the published type surface through TypeScript", () => { - void (null as BindingFunction | null); - void (null as BindingTree | null); - void (null as DefaultNetworkAdapterOptions | null); - void (null as DirEntry | null); - void (null as ExecOptions | null); - void (null as ExecResult | null); - void (null as Kernel | null); - void (null as KernelInterface | null); - void (null as ModuleAccessOptions | null); - void (null as NetworkAdapter | null); - void (null as NodeRuntimeDriver | null); - void (null as NodeRuntimeDriverFactory | null); - void (null as NodeRuntimeDriverFactoryOptions | null); - void (null as NodeRuntimeOptions | null); - void (null as OSConfig | null); - void (null as Permissions | null); - void (null as ProcessConfig | null); - void (null as ResourceBudgets | null); - void (null as RunResult | null); - void (null as StatInfo | null); - void (null as StdioChannel | null); - void (null as StdioEvent | null); - void (null as StdioHook | null); - void (null as TimingMitigation | null); - void (null as VirtualFileSystem | null); - - expect(true).toBe(true); - }); - - it("does not expose deferred browser or python subpaths", async () => { - const importDeferred = (specifier: string) => - new Function("target", "return import(target)")( - specifier, - ) as Promise; - - await expect(importDeferred("secure-exec/browser")).rejects.toThrow(); - await expect(importDeferred("secure-exec/python")).rejects.toThrow(); - }); - - it("does not expose an in-memory filesystem factory", () => { - expect(secureExec).not.toHaveProperty("createInMemoryFileSystem"); - }); -}); diff --git a/packages/secure-exec/tests/quickstart-smoke.ts b/packages/secure-exec/tests/quickstart-smoke.ts deleted file mode 100644 index c44f26f65c..0000000000 --- a/packages/secure-exec/tests/quickstart-smoke.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { - allowAll, - createNodeDriver, - createNodeHostCommandExecutor, - createNodeRuntimeDriverFactory, - NodeRuntime, - type NodeRuntimeOptions, -} from "secure-exec"; -import { createInMemoryFileSystem } from "@rivet-dev/agentos-core/test/runtime"; - -export function createQuickstartOptions(): NodeRuntimeOptions { - const filesystem = createInMemoryFileSystem(); - const systemDriver = createNodeDriver({ - filesystem, - permissions: allowAll, - commandExecutor: createNodeHostCommandExecutor(), - }); - - return { - systemDriver, - runtimeDriverFactory: createNodeRuntimeDriverFactory(), - }; -} - -void NodeRuntime; diff --git a/packages/secure-exec/tests/tsconfig.quickstart.json b/packages/secure-exec/tests/tsconfig.quickstart.json deleted file mode 100644 index 462811a6d6..0000000000 --- a/packages/secure-exec/tests/tsconfig.quickstart.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "noEmit": true, - "rootDir": "." - }, - "exclude": [], - "include": ["quickstart-smoke.ts"] -} diff --git a/packages/secure-exec/tsconfig.json b/packages/secure-exec/tsconfig.json deleted file mode 100644 index bb2036bc5e..0000000000 --- a/packages/secure-exec/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "baseUrl": ".", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "esModuleInterop": true, - "declaration": true, - "outDir": "./dist", - "rootDir": "./src", - "paths": { - "@rivet-dev/agentos-core/internal/runtime-compat": [ - "../core/dist/runtime-compat.d.ts" - ] - } - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "tests"] -} diff --git a/packages/typescript/README.md b/packages/typescript/README.md index fe5d3b951a..eb708424c0 100644 --- a/packages/typescript/README.md +++ b/packages/typescript/README.md @@ -1,6 +1,8 @@ -# @rivet-dev/agentos-typescript +# AgentOS internal TypeScript compiler Public AgentOS TypeScript companion package backed by AgentOS runtime primitives. -Use `@rivet-dev/agentos-typescript` when you need sandboxed TypeScript type +This private workspace module implements the compiler used by +`@rivet-dev/agentos-javascript`. It is not a supported install target. Use +`runtime.typescript` when you need isolated TypeScript type checking or compilation on top of the maintained AgentOS runtime surface. diff --git a/packages/typescript/package.json b/packages/typescript/package.json index d19a95a556..a946917937 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -1,7 +1,8 @@ { - "name": "@rivet-dev/agentos-typescript", + "name": "@rivet-dev/agentos-internal-typescript", + "private": true, "version": "0.0.1", - "description": "AgentOS TypeScript companion tools backed by AgentOS runtime primitives.", + "description": "Internal TypeScript compiler implementation for AgentOS JavaScript execution.", "type": "module", "license": "Apache-2.0", "main": "./dist/index.js", diff --git a/packages/typescript/src/index.ts b/packages/typescript/src/index.ts index d912aa23b4..b4d25cded9 100644 --- a/packages/typescript/src/index.ts +++ b/packages/typescript/src/index.ts @@ -1,6 +1,7 @@ import { realpathSync } from "node:fs"; import { createRequire } from "node:module"; import path from "node:path"; +import type { AgentOs } from "@rivet-dev/agentos-core"; import { createKernel, type createNodeDriver, @@ -56,6 +57,15 @@ export interface TypeScriptToolsOptions { compilerSpecifier?: string; } +export interface AgentOsTypeScriptToolsOptions { + /** Guest path or package specifier for the TypeScript compiler. */ + compilerSpecifier: string; + /** Per-compiler-invocation wall-clock limit. */ + timeoutMs?: number; + /** Per-compiler-invocation CPU limit. */ + cpuTimeLimitMs?: number; +} + export interface TypeScriptTools { typecheckProject(options?: ProjectCompilerOptions): Promise; compileProject( @@ -139,6 +149,104 @@ export function createTypeScriptTools( }; } +let nextAgentOsCompilerRequestId = 0; + +/** + * Bind the TypeScript compiler to an existing AgentOS VM. + * + * Unlike the legacy driver-oriented constructor, this runs every compiler + * invocation in the caller's VM, so source files, tsconfig projects, + * permissions, and resource limits all share one sidecar-owned environment. + */ +export function createAgentOsTypeScriptTools( + vm: AgentOs, + options: AgentOsTypeScriptToolsOptions, +): TypeScriptTools { + const run = async ( + request: Omit, + ): Promise => { + const completeRequest = { + ...request, + compilerSpecifier: options.compilerSpecifier, + } as CompilerRequest; + try { + return (await runCompilerRequestInAgentOs( + vm, + completeRequest, + options, + )) as TResult; + } catch (error) { + return createFailureResult( + completeRequest.kind, + error instanceof Error ? error.message : String(error), + ); + } + }; + + return { + typecheckProject: (requestOptions = {}) => + run({ + kind: "typecheckProject", + options: requestOptions, + }), + compileProject: (requestOptions = {}) => + run({ + kind: "compileProject", + options: requestOptions, + }), + typecheckSource: (requestOptions) => + run({ + kind: "typecheckSource", + options: requestOptions, + }), + compileSource: (requestOptions) => + run({ + kind: "compileSource", + options: requestOptions, + }), + }; +} + +async function runCompilerRequestInAgentOs( + vm: AgentOs, + request: CompilerRequest, + options: AgentOsTypeScriptToolsOptions, +): Promise { + const requestId = nextAgentOsCompilerRequestId++; + const requestPath = `/tmp/agentos-typescript-request-${requestId}.json`; + const runnerPath = `/tmp/agentos-typescript-runner-${requestId}.cjs`; + await vm.writeFile(requestPath, JSON.stringify(request)); + await vm.writeFile(runnerPath, buildCompilerRuntimeScript(requestPath)); + + try { + const result = await vm.execArgv("node", [runnerPath], { + cwd: request.options.cwd ?? "/workspace", + timeout: options.timeoutMs, + cpuTimeLimitMs: options.cpuTimeLimitMs, + }); + if (result.stdout.trim()) { + return parseRuntimeResponse(result.stdout); + } + if (result.exitCode !== 0) { + throw new Error( + result.stderr.trim() || `TypeScript runtime exited ${result.exitCode}`, + ); + } + throw new Error("TypeScript runtime produced no response"); + } finally { + for (const targetPath of [requestPath, runnerPath]) { + try { + if (await vm.exists(targetPath)) await vm.remove(targetPath); + } catch (error) { + console.error( + `[agentos] failed to remove TypeScript compiler temporary file ${targetPath}:`, + error, + ); + } + } + } +} + async function runCompilerRequest( options: TypeScriptToolsOptions, request: CompilerRequest, @@ -388,10 +496,10 @@ function createFailureResult( function normalizeCompilerFailureMessage(errorMessage?: string): string { const message = (errorMessage ?? "TypeScript compiler failed").trim(); if (/memory limit/i.test(message)) { - return "TypeScript compiler exceeded sandbox memory limit"; + return "TypeScript compiler exceeded VM memory limit"; } if (/cpu time limit exceeded|timed out/i.test(message)) { - return "TypeScript compiler exceeded sandbox CPU time limit"; + return "TypeScript compiler exceeded VM CPU time limit"; } return message; } @@ -417,6 +525,7 @@ function buildCompilerRuntimeScript(requestPath: string): string { return ` const fs = require("node:fs"); const path = require("node:path"); +const __require = require; function loadTypeScriptCompiler(compilerSpecifier) { const specifier = @@ -450,6 +559,7 @@ try { function buildCompilerRuntimeEval(request: CompilerRequest): string { return ` const path = require("node:path"); +const __require = require; function loadTypeScriptCompiler(compilerSpecifier) { const specifier = @@ -619,7 +729,7 @@ function compilerRuntimeMain( const cwd = path.resolve(options.cwd ?? "/root"); const filePath = path.resolve( cwd, - options.filePath ?? "__secure_exec_typescript_input__.ts", + options.filePath ?? "__agentos_typescript_input__.ts", ); const projectCompilerOptions = options.configFilePath ? resolveProjectConfig( diff --git a/packages/typescript/tests/quickstart-smoke.ts b/packages/typescript/tests/quickstart-smoke.ts index 8ce0632bdd..9f1be03b49 100644 --- a/packages/typescript/tests/quickstart-smoke.ts +++ b/packages/typescript/tests/quickstart-smoke.ts @@ -3,7 +3,7 @@ import { type ProjectCompileResult, type TypeCheckResult, type TypeScriptTools, -} from "@rivet-dev/agentos-typescript"; +} from "@rivet-dev/agentos-internal-typescript"; import { createNodeDriver, createNodeRuntimeDriverFactory, diff --git a/packages/typescript/tests/typescript-tools.integration.nightly.test.ts b/packages/typescript/tests/typescript-tools.integration.nightly.test.ts index 9b80bbc2b5..6fdce0ec17 100644 --- a/packages/typescript/tests/typescript-tools.integration.nightly.test.ts +++ b/packages/typescript/tests/typescript-tools.integration.nightly.test.ts @@ -1,6 +1,6 @@ import { join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { createTypeScriptTools } from "@rivet-dev/agentos-typescript"; +import { createTypeScriptTools } from "@rivet-dev/agentos-internal-typescript"; import { allowAllFs, createKernel, @@ -32,7 +32,7 @@ function createTools() { }; } -describe("@rivet-dev/agentos-typescript", () => { +describe("AgentOS internal TypeScript compiler", () => { it("typechecks a project with node types from node_modules", async () => { const { filesystem, tools } = createTools(); await filesystem.mkdir("/root"); diff --git a/packages/typescript/tsconfig.json b/packages/typescript/tsconfig.json index 8692ed7bd4..3d9ab046f1 100644 --- a/packages/typescript/tsconfig.json +++ b/packages/typescript/tsconfig.json @@ -9,7 +9,7 @@ "outDir": "./dist", "rootDir": "./src", "paths": { - "@rivet-dev/agentos-typescript": ["./src/index.ts"], + "@rivet-dev/agentos-internal-typescript": ["./src/index.ts"], "@rivet-dev/agentos-core/internal/runtime-compat": [ "../core/dist/runtime-compat.d.ts" ] diff --git a/packages/typescript/vitest.config.ts b/packages/typescript/vitest.config.ts index 3cfd2d1ead..05c2448e4f 100644 --- a/packages/typescript/vitest.config.ts +++ b/packages/typescript/vitest.config.ts @@ -9,7 +9,7 @@ export default defineConfig({ replacement: resolve(__dirname, "../core/dist/runtime-compat.js"), }, { - find: "@rivet-dev/agentos-typescript", + find: "@rivet-dev/agentos-internal-typescript", replacement: resolve(__dirname, "./src/index.ts"), }, ], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2de244aab..9d86adcc2a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,18 +7,20 @@ settings: catalogs: rivetkit: '@rivetkit/react': - specifier: 0.0.0-sqlite-uds.4e59a38 - version: 0.0.0-sqlite-uds.4e59a38 + specifier: 2.3.7 + version: 2.3.7 rivetkit: - specifier: 0.0.0-sqlite-uds.4e59a38 - version: 0.0.0-sqlite-uds.4e59a38 + specifier: 2.3.7 + version: 2.3.7 overrides: '@rivet-dev/agentos-core': workspace:* - '@rivetkit/rivetkit-wasm': 2.3.2 + '@rivetkit/rivetkit-wasm': 2.3.7 '@mariozechner/pi-tui': 0.60.0 '@mariozechner/pi-agent-core': 0.60.0 +pnpmfileChecksum: sha256-SEnUwm6AFeob5DIJB/q7nwJGPinO5RVveyVjDzNYuIM= + importers: .: @@ -49,7 +51,7 @@ importers: version: 1.6.0 '@mariozechner/pi-coding-agent': specifier: ^0.60.0 - version: 0.60.0(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@3.25.76))(bufferutil@4.1.0)(ws@8.21.0(bufferutil@4.1.0))(zod@3.25.76) + version: 0.60.0(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6))(bufferutil@4.1.0)(ws@8.21.0(bufferutil@4.1.0))(zod@4.3.6) '@rivet-dev/agentos-core': specifier: workspace:* version: link:packages/core @@ -101,9 +103,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -115,7 +114,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -143,7 +142,7 @@ importers: version: link:../../packages/agentos rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) devDependencies: '@types/node': specifier: ^22.10.2 @@ -169,9 +168,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -183,7 +179,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -218,9 +214,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -232,7 +225,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -267,9 +260,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -281,7 +271,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -324,7 +314,7 @@ importers: version: link:../../packages/agentos '@rivetkit/react': specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(ws@8.21.0(bufferutil@4.1.0)) '@xterm/addon-fit': specifier: ^0.10.0 version: 0.10.0(@xterm/xterm@5.5.0) @@ -339,7 +329,7 @@ importers: version: 19.2.7(react@19.2.7) rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) devDependencies: '@types/react': specifier: ^19.0.0 @@ -379,7 +369,7 @@ importers: version: link:../../packages/agentos rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) devDependencies: '@types/node': specifier: ^22.10.2 @@ -408,9 +398,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -422,7 +409,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -457,9 +444,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -471,7 +455,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -506,9 +490,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -520,7 +501,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -555,9 +536,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -569,7 +547,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -604,9 +582,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -618,7 +593,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -653,9 +628,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -667,7 +639,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -702,9 +674,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -716,7 +685,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -751,9 +720,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -765,7 +731,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -783,6 +749,233 @@ importers: specifier: ^5.7.2 version: 5.9.3 + examples/js-ai-agent-code-exec: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-child-processes: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-code-mode: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + zod: + specifier: ^4.1.11 + version: 4.3.6 + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-dev-servers: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-filesystem: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-module-loading: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-networking: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-networking-wasm: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-output-capture: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-permissions: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-plugin-systems: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-process-isolation: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-quickstart: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-resource-limits: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + examples/js-runtime: dependencies: '@agentos-software/claude-code': @@ -800,9 +993,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -814,7 +1004,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -832,6 +1022,70 @@ importers: specifier: ^5.7.2 version: 5.9.3 + examples/js-runtime-platform: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-sdk-overview: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/js-typescript: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + typescript: + specifier: ^5.7.2 + version: 5.9.3 + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + + examples/js-virtual-filesystem: + dependencies: + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../../packages/javascript + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + examples/llm-credentials: dependencies: '@agentos-software/claude-code': @@ -849,9 +1103,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -863,7 +1114,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -898,9 +1149,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -912,7 +1160,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -947,9 +1195,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -961,7 +1206,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -996,9 +1241,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -1010,7 +1252,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1045,9 +1287,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -1059,7 +1298,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1094,9 +1333,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -1108,7 +1344,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1143,9 +1379,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -1157,7 +1390,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1192,9 +1425,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -1206,7 +1436,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1224,6 +1454,38 @@ importers: specifier: ^5.7.2 version: 5.9.3 + examples/python-quickstart: + dependencies: + '@rivet-dev/agentos-python': + specifier: workspace:* + version: link:../../packages/python + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + + examples/python-sdk-overview: + dependencies: + '@rivet-dev/agentos-python': + specifier: workspace:* + version: link:../../packages/python + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + examples/quickstart: dependencies: '@agentos-software/claude-code': @@ -1241,9 +1503,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -1255,7 +1514,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1290,9 +1549,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -1310,7 +1566,7 @@ importers: version: 18.3.1(react@18.3.1) rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1348,9 +1604,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../../packages/agentos-sandbox @@ -1362,7 +1615,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1397,9 +1650,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../../packages/agentos-sandbox @@ -1411,7 +1661,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1431,9 +1681,9 @@ importers: examples/quickstart/bindings: dependencies: - '@rivet-dev/agentos-core': + '@rivet-dev/agentos': specifier: workspace:* - version: link:../../../packages/core + version: link:../../../packages/agentos zod: specifier: ^4.1.11 version: 4.3.6 @@ -1465,9 +1715,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../../packages/agentos-sandbox @@ -1479,7 +1726,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1514,9 +1761,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../../packages/agentos-sandbox @@ -1528,7 +1772,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1563,9 +1807,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../../packages/agentos-sandbox @@ -1577,7 +1818,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1612,9 +1853,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../../packages/agentos-sandbox @@ -1626,7 +1864,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1661,9 +1899,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../../packages/agentos-sandbox @@ -1675,7 +1910,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1710,9 +1945,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../../packages/agentos-sandbox @@ -1724,7 +1956,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1759,9 +1991,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../../packages/agentos-sandbox @@ -1773,7 +2002,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1808,9 +2037,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../../packages/agentos-sandbox @@ -1822,7 +2048,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1840,6 +2066,22 @@ importers: specifier: ^5.7.2 version: 5.9.3 + examples/quickstart/python: + dependencies: + '@rivet-dev/agentos': + specifier: workspace:* + version: link:../../../packages/agentos + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + tsx: + specifier: ^4.19.0 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + examples/quickstart/s3-filesystem: dependencies: '@agentos-software/claude-code': @@ -1857,9 +2099,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../../packages/agentos-sandbox @@ -1871,7 +2110,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1906,9 +2145,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../../packages/agentos-sandbox @@ -1920,7 +2156,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -1955,9 +2191,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -1969,7 +2202,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -2004,9 +2237,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -2018,7 +2248,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -2053,9 +2283,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -2067,7 +2294,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -2111,9 +2338,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -2125,7 +2349,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -2160,9 +2384,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -2177,7 +2398,7 @@ importers: version: 4.12.9 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -2212,9 +2433,6 @@ importers: '@rivet-dev/agentos': specifier: workspace:* version: link:../../packages/agentos - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../../packages/core '@rivet-dev/agentos-sandbox': specifier: workspace:* version: link:../../packages/agentos-sandbox @@ -2226,7 +2444,7 @@ importers: version: 7.2.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) sandbox-agent: specifier: ^0.4.2 version: 0.4.2(dockerode@4.0.10)(get-port@7.2.0)(zod@4.3.6) @@ -2272,7 +2490,7 @@ importers: version: 9.14.0 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) tsx: specifier: ^4.19.0 version: 4.21.0 @@ -2306,10 +2524,10 @@ importers: version: link:../core '@rivetkit/react': specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(ws@8.21.0(bufferutil@4.1.0)) rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) zod: specifier: ^4.1.11 version: 4.3.6 @@ -2367,7 +2585,7 @@ importers: version: 3.4.19(tsx@4.21.0)(yaml@2.9.0) tsup: specifier: ^8.4.0 - version: 8.5.1(jiti@1.21.7)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) + version: 8.5.1(@microsoft/api-extractor@7.43.0(@types/node@22.19.15))(jiti@1.21.7)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) typescript: specifier: ^5.7.3 version: 5.9.3 @@ -2417,7 +2635,7 @@ importers: version: 22.19.15 tsup: specifier: ^8.3.5 - version: 8.5.1(jiti@2.7.0)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) + version: 8.5.1(@microsoft/api-extractor@7.43.0(@types/node@22.19.15))(jiti@2.7.0)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) typescript: specifier: ^5.7.2 version: 5.9.3 @@ -2649,6 +2867,25 @@ importers: specifier: npm:zod@^3.25.76 version: zod@3.25.76 + packages/javascript: + dependencies: + '@rivet-dev/agentos-core': + specifier: workspace:* + version: link:../core + devDependencies: + '@types/node': + specifier: ^22.19.15 + version: 22.19.15 + tsup: + specifier: ^8.4.0 + version: 8.5.1(@microsoft/api-extractor@7.43.0(@types/node@22.19.15))(jiti@2.7.0)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) + typescript: + specifier: ^5.7.3 + version: 5.9.3 + vitest: + specifier: ^2.1.8 + version: 2.1.9(@types/node@22.19.15) + packages/manifest: devDependencies: '@types/node': @@ -2719,18 +2956,18 @@ importers: packages/python: dependencies: - '@rivet-dev/agentos-runtime-core': + '@rivet-dev/agentos-core': specifier: workspace:* - version: link:../runtime-core - pyodide: - specifier: ^0.28.3 - version: 0.28.3(bufferutil@4.1.0) + version: link:../core devDependencies: '@types/node': - specifier: ^22.10.2 + specifier: ^22.19.15 version: 22.19.15 + tsup: + specifier: ^8.4.0 + version: 8.5.1(@microsoft/api-extractor@7.43.0(@types/node@22.19.15))(jiti@2.7.0)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) typescript: - specifier: ^5.7.2 + specifier: ^5.7.3 version: 5.9.3 vitest: specifier: ^2.1.8 @@ -2738,6 +2975,12 @@ importers: packages/runtime-benchmarks: dependencies: + '@rivet-dev/agentos': + specifier: workspace:* + version: link:../agentos + '@rivet-dev/agentos-javascript': + specifier: workspace:* + version: link:../javascript '@rivet-dev/agentos-runtime-core': specifier: workspace:* version: link:../runtime-core @@ -2812,50 +3055,6 @@ importers: packages/runtime-sidecar/npm/linux-x64-gnu: {} - packages/secure-exec: - dependencies: - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../core - devDependencies: - '@types/node': - specifier: ^22.10.2 - version: 22.19.15 - typescript: - specifier: ^5.7.2 - version: 5.9.3 - vitest: - specifier: ^2.1.8 - version: 2.1.9(@types/node@22.19.15) - - packages/secure-exec-example-ai-agent-type-check: - dependencies: - '@ai-sdk/anthropic': - specifier: ^3.0.58 - version: 3.0.64(zod@3.25.76) - '@rivet-dev/agentos-core': - specifier: workspace:* - version: link:../core - '@rivet-dev/agentos-typescript': - specifier: workspace:* - version: link:../typescript - ai: - specifier: ^6.0.116 - version: 6.0.141(zod@3.25.76) - typescript: - specifier: ^5.7.2 - version: 5.9.3 - zod: - specifier: ^3.24.0 - version: 3.25.76 - devDependencies: - '@types/node': - specifier: ^22.10.2 - version: 22.19.15 - tsx: - specifier: ^4.19.2 - version: 4.21.0 - packages/shell: dependencies: '@agentos-software/codex-cli': @@ -2938,7 +3137,7 @@ importers: version: 14.0.3 rivetkit: specifier: catalog:rivetkit - version: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + version: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) devDependencies: '@types/node': specifier: ^22.19.3 @@ -3120,14 +3319,14 @@ importers: version: 0.29.2(@cfworker/json-schema@4.1.1) '@anthropic-ai/claude-agent-sdk': specifier: 0.2.112 - version: 0.2.112(@cfworker/json-schema@4.1.1)(zod@3.25.76) + version: 0.2.112(@cfworker/json-schema@4.1.1)(zod@4.3.6) '@anthropic-ai/sdk': specifier: 0.111.0 - version: 0.111.0(zod@3.25.76) + version: 0.111.0(zod@4.3.6) devDependencies: '@agentclientprotocol/sdk': specifier: 1.2.1 - version: 1.2.1(zod@3.25.76) + version: 1.2.1(zod@4.3.6) '@agentos-software/manifest': specifier: workspace:* version: link:../../packages/manifest @@ -3998,12 +4197,6 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/anthropic@3.0.64': - resolution: {integrity: sha512-rwLi/Rsuj2pYniQXIrvClHvXDzgM4UQHHnvHTWEF14efnlKclG/1ghpNC+adsRujAbCTr6gRsSbDE2vEqriV7g==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/azure@2.0.104': resolution: {integrity: sha512-g0ZDc/IgNCnIQuMj+bCBPionZwH4YBkfj5/CYeEPNqWrGBJm3aYfuWCjdT6Yayg+zlimunHZIjpjdDwan3i8Qg==} engines: {node: '>=18'} @@ -4040,12 +4233,6 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/gateway@3.0.83': - resolution: {integrity: sha512-LvlWujbSdEkTBXBLFtF7GS6riXdHhH0O+DpDrCaNQvXeHmSF2jKsOg7JWXiCgygAHM5cWFAO3JYmZp83DjiuBQ==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/google-vertex@3.0.127': resolution: {integrity: sha512-kD2xC1HFbhNe5/yCJqkIP2rV40mlyK3IJiCoI6bwkjC5aPvWdBVoMIYvYcmM/eYlDYkPwC3pkUWd1HqRdLyzZw==} engines: {node: '>=18'} @@ -4106,12 +4293,6 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider-utils@4.0.21': - resolution: {integrity: sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider@2.0.1': resolution: {integrity: sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng==} engines: {node: '>=18'} @@ -4120,10 +4301,6 @@ packages: resolution: {integrity: sha512-h88OPkavHTiN9tMn2l5awAznGB0lXzjcLhgR1/rvjB2zlLprsNxbM2tt6OJsHUxduLC3klq0/eqaSf6fX5XVww==} engines: {node: '>=18'} - '@ai-sdk/provider@3.0.8': - resolution: {integrity: sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==} - engines: {node: '>=18'} - '@ai-sdk/togetherai@1.0.38': resolution: {integrity: sha512-3sdh58EZ2rz9fBL8flVIY70Qosmc2QBPO/pzFjXdtumfBL73KAWjweBs9HkQxrfM3jy5CuRaC8q5qBkktWGHeQ==} engines: {node: '>=18'} @@ -5516,6 +5693,19 @@ packages: engines: {node: '>=20.0.0'} deprecated: please use @earendil-works/pi-tui instead going forward + '@microsoft/api-extractor-model@7.28.13': + resolution: {integrity: sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==} + + '@microsoft/api-extractor@7.43.0': + resolution: {integrity: sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==} + hasBin: true + + '@microsoft/tsdoc-config@0.16.2': + resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==} + + '@microsoft/tsdoc@0.14.2': + resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} + '@mistralai/mistralai@1.14.1': resolution: {integrity: sha512-IiLmmZFCCTReQgPAT33r7KQ1nYo5JPdvGkrkZqA8qQ2qB1GHgs5LoP5K2ICyrjnpw2n8oSxMM/VP+liiKcGNlQ==} @@ -5873,102 +6063,114 @@ packages: resolution: {integrity: sha512-3qndQUQXLdwafMEqfhz24hUtDPcsf1Bu3q52Kb8MqeH8JUh3h6R4HYW3ZJXiQsLcyYyFM68PuIwlLRlg1xDEpg==} engines: {node: ^14.18.0 || >=16.0.0} - '@rivetkit/engine-cli-darwin-arm64@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-ZT+wqmQzLznezyYVTzRHckWbp7F4yOG3ZCCON+ErdnWd84A4mwJMYXZPIObGD2OVSxmNhWqXO1ocNTKwDsIllw==} + '@rivetkit/engine-cli-darwin-arm64@2.3.7': + resolution: {integrity: sha512-F3yGSDIaW94BZzuAm3erQRT/Ki55wHJ/b6tOaE7d9tRmTAZw/XqHmjNdUYjOvFUQKLb+SSsOittM4dINrAANQQ==} engines: {node: '>= 20.0.0'} cpu: [arm64] os: [darwin] - '@rivetkit/engine-cli-darwin-x64@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-PgiKyvhciN17C7RjyB9J9ApVkCTJmdyaRZnsSmy0xdbNY12uKrhH2mNcaQAPV+7/QB4HB4TjVgD1wT+PCCGiQg==} + '@rivetkit/engine-cli-darwin-x64@2.3.7': + resolution: {integrity: sha512-P8BbQsamIvv8U/cEoJUFr+uAzwjjl9cG+ZZxjDX9OJxbuD11ESpHvDLgge54ijynO3IjoP8cIzjIXat6mr2Gkw==} engines: {node: '>= 20.0.0'} cpu: [x64] os: [darwin] - '@rivetkit/engine-cli-linux-arm64-musl@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-2FZhrpzYfNIId3Tn0wpz1klcoKZjf2NBzGkmdxGQKY0kh7XynBsZxUnqyHJVFAkfLSSW+1t8E6PLTMSfPHIKXQ==} + '@rivetkit/engine-cli-linux-arm64-musl@2.3.7': + resolution: {integrity: sha512-G3s76BHfIs6R9rm6JYI13sOjPLy08+C8WvVoFwSdMqsibEaqlfo36wVkp/x1g0sEZUek/dBxVrKqMMR7c4/gug==} engines: {node: '>= 20.0.0'} cpu: [arm64] os: [linux] - '@rivetkit/engine-cli-linux-x64-musl@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-iw3v07nBtc1+qiQxl/g3HPT9UPOJUidvOYixPpxtydaRqh465/6pYy8cTzQOhEKBodBD3drsaB+6GJwDLy8CMA==} + '@rivetkit/engine-cli-linux-x64-musl@2.3.7': + resolution: {integrity: sha512-LWhrUVzULcsZ4ufCLsx23YfrdMW2INd9g0ANBac9+PzGwL8qYfVIXnDpR3587lapu68EbSEPZcconf1tBlvJxQ==} engines: {node: '>= 20.0.0'} cpu: [x64] os: [linux] - '@rivetkit/engine-cli@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-5WYBHyO4Yudb2RuVbbp2srb/XDbEAdTihxUj/aUerahalR3KiNDo5zpUZpfhhoAQrjPQTVTN9Nan/RVloVpFRg==} + '@rivetkit/engine-cli-win32-x64@2.3.7': + resolution: {integrity: sha512-jyA5PlHivHXyyq2ehnuXwxd5TUSbB4emomnph9EovsJo7qIdmSNaaCO13NYBT483fd0uXiNI0MK/lkJa9ePY5A==} + engines: {node: '>= 20.0.0'} + cpu: [x64] + os: [win32] + + '@rivetkit/engine-cli@2.3.7': + resolution: {integrity: sha512-CezLwJ0B7dWDbA7qM6Aq04mwnrJAdrDActRrrcb4NBa20h7wO9KPAYBwyJz/dRnKm9EUUNcFZ6hrSDpp6T3+Rg==} engines: {node: '>= 20.0.0'} - '@rivetkit/engine-envoy-protocol@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-asUWA8E+fyD1RLQ5wH6KhCTNZ3laAqqS2bZQkcMQS8mQn/9BVl+sfCm/yM3hLK7lH7WT616sJhcP86EktZCLsA==} + '@rivetkit/engine-envoy-protocol@2.3.7': + resolution: {integrity: sha512-lsJM3ERwozQCebaOMKJzqIQlcLbbN3LXurRB0+7LsM6FUUL2l6hMoq5I2De/G35DJ6OWDva9AmzfvzI+5f1GPg==} - '@rivetkit/framework-base@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-s4yFjZZbcPXA++VfS5n1FgEohSG2gJec/svlmV2zko3Q4iqMip7g4/6DIU+5wsD1EkCwKE2dmuYdn0ML9sTtbQ==} + '@rivetkit/framework-base@2.3.7': + resolution: {integrity: sha512-zxmdAzFogA3PeT3ZgFIoYlEAJVi3iCwbPBXczZKf2x2QMCl2cTZ2jswFCAgHUUMQWj+AWiX65usxq73a9Ls/qQ==} '@rivetkit/on-change@6.0.1': resolution: {integrity: sha512-QBN/KRBXLJdCgN4gBTL3XAc/zKm58atSnieXWMOyFSPmo6F1/yIVV/LTRdvAktfCttrGx7W6c32i/lwqCHWnsQ==} engines: {node: '>=20'} - '@rivetkit/react@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-O2DWeQ7RvisfhVV0s5BOy/+BSSRrthS8ntVqFcDAhSiXKex6iUQSflhA4mkfcVW4/XWOzLmf2tNYPxEul/hKhw==} + '@rivetkit/react@2.3.7': + resolution: {integrity: sha512-Y3UoUYhkLKDQrJdloCmYK+fhJMlPw5BPHbRb5VutFh5L8csIRZLoCoMitzV7mjNUxuDE1+k10iGuUHYPZUP4aA==} peerDependencies: react: ^18 || ^19 react-dom: ^18 || ^19 - '@rivetkit/rivetkit-napi-darwin-arm64@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-sH/wB5IvlDQ7lktL7lk/1h5K8/Rxm/UYyY8GKGOdPzgIhBK7RNE194vhlLLgjWCU2P5eTo/uG8U5n7Shw1VoBA==} + '@rivetkit/rivetkit-napi-darwin-arm64@2.3.7': + resolution: {integrity: sha512-gI1wMe6z85VDeR4K/wnzevVakXTg5UCfvjlDOp1+23nyU3NlInhN156dEWO8EsfRMrs+1bYoGdc4rcoN+4PUrw==} engines: {node: '>= 20.0.0'} cpu: [arm64] os: [darwin] - '@rivetkit/rivetkit-napi-darwin-x64@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-E8Lt44QIOjzNnrFDWALpIFn3p593l57Y7z5mmY4oK0xScJlw8hL6jn0ISvCA1BRd0wPTy5/fwCUxGAivhhVJAg==} + '@rivetkit/rivetkit-napi-darwin-x64@2.3.7': + resolution: {integrity: sha512-H5tUwvCbKwTIBiyQG01ikK5mdc9ISg+bv8gPUw41mYOKq8rb9nBLkcl2TKcreFHac8KaGxtnBZzYcjbUV3NcwQ==} engines: {node: '>= 20.0.0'} cpu: [x64] os: [darwin] - '@rivetkit/rivetkit-napi-linux-arm64-gnu@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-xdFAuc0s8tj6p63zeY/IoS0JKtnmTh6RyOauy+8cptTzGDUXcQnuzQU28YjPJ535je7aGIlJYfSXhL/rd4kmZA==} + '@rivetkit/rivetkit-napi-linux-arm64-gnu@2.3.7': + resolution: {integrity: sha512-MpOeiO5M737e93hM/FjcH+zv8xBEX1tgBd/59K/Uw7qlRbWW7JGz8WGjavdxv211LRqxsn8EUlGRxcADDsXnBA==} engines: {node: '>= 20.0.0'} cpu: [arm64] os: [linux] - '@rivetkit/rivetkit-napi-linux-arm64-musl@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-W6gTRHX1Sx9Oi2KoPpEN1fENEkF/dqvpc5OqNJErCcW8M2WCB/0BU/c7nHsP54ZLzS0kcZtaQov6qw0Nt2eaWA==} + '@rivetkit/rivetkit-napi-linux-arm64-musl@2.3.7': + resolution: {integrity: sha512-0GsUgd/HHvIq6v92kp0H175mGm2Exvs4krBLih0MIpVAkqJk8EIcKI6wJhCwY04Fr0MS8sHkUhjuHXa7QcmlYA==} engines: {node: '>= 20.0.0'} cpu: [arm64] os: [linux] - '@rivetkit/rivetkit-napi-linux-x64-gnu@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-BY8SZke/5iHh35rFrkJ+GkmMelezHaIrClxjpZ6a3Yfu4owAfOUCOSTHZttYAoba4EdnCpB8aTQ2SimtvNN77g==} + '@rivetkit/rivetkit-napi-linux-x64-gnu@2.3.7': + resolution: {integrity: sha512-8wCD1FuDpM+yiKRG8ro1K8ILVpJXzCNKViJ1axq1b4Nyqgod/nyEvvKDaT4qhA5+Om+W5i6YRO1uddjC6QjJSQ==} engines: {node: '>= 20.0.0'} cpu: [x64] os: [linux] - '@rivetkit/rivetkit-napi-linux-x64-musl@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-rxtzRH2hpaz8hx6F1GIIM7f0FpDzyBKNtmusDQswmYajkm/Hfxl9ha2bleHg6EFtNVAr8gQ9cHAZcAv583bAJw==} + '@rivetkit/rivetkit-napi-linux-x64-musl@2.3.7': + resolution: {integrity: sha512-8RGzXD33GH1/DWufFzCZRI+npF5IL/9z7Ja3pthsuYgrL+axGG0iqfAjbwj5HHnB/ceUGQVT+uCKzoLnG+odtw==} engines: {node: '>= 20.0.0'} cpu: [x64] os: [linux] - '@rivetkit/rivetkit-napi@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-0SibmwqUugIfgRdwuG45+SvjMLzk5Bim/cViOtnt2FrucNB1E/YeC7QD4MxMi/Z+z+M/A33dljUst70C2f5uVA==} + '@rivetkit/rivetkit-napi-win32-x64-msvc@2.3.7': + resolution: {integrity: sha512-n1by/coKxWOb2FZDGLM2qmULxcLSqx8hxJuk7Hsb1gl4uNHGviUzYr+r7WAiOzonK/Dgy477u3OKf5yBie743A==} + engines: {node: '>= 20.0.0'} + cpu: [x64] + os: [win32] + + '@rivetkit/rivetkit-napi@2.3.7': + resolution: {integrity: sha512-1d3HtNKzJwdkznWDWSpCwg1rBn+nVwUfcKrApINTfiEhfDyVeznwbVFkfHWqOBHsjU17vvWFfjjTtf8QsjLwUQ==} engines: {node: '>= 20.0.0'} - '@rivetkit/rivetkit-wasm@2.3.2': - resolution: {integrity: sha512-HNBzh6uQFi4ZYL0tHAZ6nrYKoGCfa+kObLCwdS+/ZzGePV7WGUl20Do1bHjBtnVgp9UwzQu+lka8kFwKfqm6WA==} + '@rivetkit/rivetkit-wasm@2.3.7': + resolution: {integrity: sha512-o7QBtFOJrajyPVBIDQ32k8sDNFsUc8v2ETfNpr+Ch374ydmZMzCv3XKJU9vubc9bZa7WpCHSzf5ztc8b8i/Xpg==} - '@rivetkit/traces@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-4d9Kodk7H0frBHNTmzJBXkOGKGgn5eU+A9XTmOTsoHIpPqK5AUrbJ35P2mQHQmzoU+WoHUumvey0Ze8UOKIy/Q==} + '@rivetkit/traces@2.3.7': + resolution: {integrity: sha512-B7QYjFP2HPxyfPsVjfcNIV7FPLWgGWGypmlT/dZM1i1Cf1WRdeuBumPLffgs3YceiY7Pm4D/gZZVt5p0m4Cx+A==} engines: {node: '>=18.0.0'} - '@rivetkit/virtual-websocket@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-U6C9jSf4VaQGEZTROFZHLsfCUci8No4ofBV6muzqmAKTiq6NN7mLE5V0muc0LTUx5dFpcX9hATJW1FSZVpnbYw==} + '@rivetkit/virtual-websocket@2.3.7': + resolution: {integrity: sha512-jwxDvYbr3YB6vFuxwRdB/AqUUMwdtC1rIAc4tNN6VQR3weHbE+PrDoHKbe85zrtlBtcnG89/p8tlovrj8qyQWA==} - '@rivetkit/workflow-engine@0.0.0-sqlite-uds.4e59a38': - resolution: {integrity: sha512-TUe2ZEg2D8yCNZTr+F6eeMp2uK2VAybYHhMj1IQUOuWwSyamBD8o5UFkqO12nYcE+52a7NwfHwhHEov8riVb1w==} + '@rivetkit/workflow-engine@2.3.7': + resolution: {integrity: sha512-+C4kGuSNysrw2zs/LQoYxouOxufKHHMxiIXKF8Ab9GU2BCJ32RUST9dYU/gaQE/w9uM+hqAHUz0DTlOBYU6p6g==} engines: {node: '>=18.0.0'} '@rolldown/pluginutils@1.0.0-beta.27': @@ -6099,6 +6301,28 @@ packages: cpu: [x64] os: [win32] + '@rushstack/node-core-library@4.0.2': + resolution: {integrity: sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/rig-package@0.5.2': + resolution: {integrity: sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==} + + '@rushstack/terminal@0.10.0': + resolution: {integrity: sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/ts-command-line@4.19.1': + resolution: {integrity: sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==} + '@sandbox-agent/cli-darwin-arm64@0.4.2': resolution: {integrity: sha512-+L1O8SI7k/LLhyB4dG0ghmz1cJHa0WtVjuRTrEE2gw/5EbGLWopPBsCVCmQ7snrQ4fPwtaiZDhfExcEj1VI7aw==} cpu: [arm64] @@ -6469,6 +6693,9 @@ packages: cpu: [arm64] os: [win32] + '@types/argparse@1.0.38': + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -6615,12 +6842,6 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - ai@6.0.141: - resolution: {integrity: sha512-+GomGQWaId3xN0wcugUW/H7xMMaFkID2PiS7K/Wugj45G3efv0BXhQ3psRZoQVoRbOpdNoUqcK/KTB+FR4h6qg==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} peerDependencies: @@ -6629,6 +6850,9 @@ packages: ajv: optional: true + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} @@ -6678,6 +6902,9 @@ packages: arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + asn1.js@4.10.1: resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} @@ -6824,6 +7051,9 @@ packages: bowser@2.14.1: resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + brace-expansion@1.1.16: + resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==} + brace-expansion@2.0.3: resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==} @@ -7056,10 +7286,17 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + compress-commons@6.0.2: resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} engines: {node: '>= 14'} + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + concurrently@9.2.3: resolution: {integrity: sha512-ihjs0E2SxvDgq/MK418hX6YycQgKhsqxpbZuZbHo0yKfqDWdymWMjWYIpCIzqDDLLKClHlXev8whW/8WXmJ0BA==} engines: {node: '>=18'} @@ -7560,6 +7797,9 @@ packages: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} @@ -7667,6 +7907,10 @@ packages: fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -7896,6 +8140,10 @@ packages: immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} @@ -8036,6 +8284,9 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + jose@6.2.2: resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} @@ -8061,6 +8312,9 @@ packages: resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} engines: {node: '>=16'} + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -8078,6 +8332,9 @@ packages: jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} @@ -8135,6 +8392,17 @@ packages: lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + + lodash@4.17.23: + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} @@ -8161,6 +8429,10 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + lru-cache@7.18.3: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} @@ -8245,6 +8517,9 @@ packages: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} + minimatch@3.0.8: + resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} + minimatch@5.1.9: resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} engines: {node: '>=10'} @@ -8859,14 +9134,14 @@ packages: punycode@1.4.1: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + puppeteer-core@22.15.0: resolution: {integrity: sha512-cHArnywCiAAVXa3t4GGL2vttNxh7GqXtIYGym99egkNJ3oG//wL9LkvO4WE8W1TJe95t1F1ocu9X4xWaGsOKOA==} engines: {node: '>=18'} - pyodide@0.28.3: - resolution: {integrity: sha512-rtCsyTU55oNGpLzSVuAd55ZvruJDEX8o6keSdWKN9jPeBVSNlynaKFG7eRqkiIgU7i2M6HEgYtm0atCEQX3u4A==} - engines: {node: '>=18.0.0'} - qs@6.15.0: resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} engines: {node: '>=0.6'} @@ -8992,6 +9267,9 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve@1.19.0: + resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} + resolve@1.22.11: resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} @@ -9013,8 +9291,8 @@ packages: resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} engines: {node: '>= 0.8'} - rivetkit@0.0.0-sqlite-uds.4e59a38: - resolution: {integrity: sha512-cZnF3JMeMFirZISiYtZHGrpBPuFXN52A0ctEoM5zON2g7g17x4N3z+sNJOqaUenPL9f73Sv8YPU97UMdeDiZWQ==} + rivetkit@2.3.7: + resolution: {integrity: sha512-V2IWlXq9SRLJWrcYVa8WUftELCpbdsBVWZIqz2WQjhhzRK9Aan65RfMoZVx62A9k8HuIO6MH1nAPLhOuk1xePQ==} engines: {node: '>=22.0.0'} peerDependencies: drizzle-kit: ^0.31.2 @@ -9112,6 +9390,11 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + semver@7.7.4: resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} engines: {node: '>=10'} @@ -9234,6 +9517,9 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + ssh2@1.17.0: resolution: {integrity: sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==} engines: {node: '>=10.16.0'} @@ -9260,6 +9546,10 @@ packages: streamx@2.25.0: resolution: {integrity: sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==} + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -9290,6 +9580,10 @@ packages: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + strip-json-comments@5.0.3: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} @@ -9493,6 +9787,11 @@ packages: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} + typescript@5.4.2: + resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} + engines: {node: '>=14.17'} + hasBin: true + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -9525,6 +9824,10 @@ packages: resolution: {integrity: sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==} engines: {node: '>=22.19.0'} + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -9535,6 +9838,9 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + url-template@2.0.8: resolution: {integrity: sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==} @@ -9578,6 +9884,10 @@ packages: deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true + validator@13.15.35: + resolution: {integrity: sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==} + engines: {node: '>= 0.10'} + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -9779,6 +10089,9 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yaml@2.8.3: resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} engines: {node: '>= 14.6'} @@ -9816,6 +10129,11 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} + z-schema@5.0.5: + resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} + engines: {node: '>=8.0.0'} + hasBin: true + zip-stream@6.0.1: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} @@ -9838,9 +10156,9 @@ snapshots: '@agentclientprotocol/claude-agent-acp@0.29.2(@cfworker/json-schema@4.1.1)': dependencies: - '@agentclientprotocol/sdk': 0.19.0(zod@3.25.76) - '@anthropic-ai/claude-agent-sdk': 0.2.112(@cfworker/json-schema@4.1.1)(zod@3.25.76) - zod: 3.25.76 + '@agentclientprotocol/sdk': 0.19.0(zod@4.3.6) + '@anthropic-ai/claude-agent-sdk': 0.2.112(@cfworker/json-schema@4.1.1)(zod@4.3.6) + zod: 4.3.6 transitivePeerDependencies: - '@cfworker/json-schema' - supports-color @@ -9853,9 +10171,9 @@ snapshots: dependencies: zod: 4.3.6 - '@agentclientprotocol/sdk@0.19.0(zod@3.25.76)': + '@agentclientprotocol/sdk@0.19.0(zod@4.3.6)': dependencies: - zod: 3.25.76 + zod: 4.3.6 '@agentclientprotocol/sdk@0.21.0(zod@4.3.6)': dependencies: @@ -9894,12 +10212,6 @@ snapshots: zod: 4.3.6 optional: true - '@ai-sdk/anthropic@3.0.64(zod@3.25.76)': - dependencies: - '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.21(zod@3.25.76) - zod: 3.25.76 - '@ai-sdk/azure@2.0.104(zod@4.3.6)': dependencies: '@ai-sdk/openai': 2.0.102(zod@4.3.6) @@ -9946,13 +10258,6 @@ snapshots: '@vercel/oidc': 3.1.0 zod: 4.3.6 - '@ai-sdk/gateway@3.0.83(zod@3.25.76)': - dependencies: - '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.21(zod@3.25.76) - '@vercel/oidc': 3.1.0 - zod: 3.25.76 - '@ai-sdk/google-vertex@3.0.127(zod@4.3.6)': dependencies: '@ai-sdk/anthropic': 2.0.74(zod@4.3.6) @@ -10030,13 +10335,6 @@ snapshots: zod: 4.3.6 optional: true - '@ai-sdk/provider-utils@4.0.21(zod@3.25.76)': - dependencies: - '@ai-sdk/provider': 3.0.8 - '@standard-schema/spec': 1.1.0 - eventsource-parser: 3.0.6 - zod: 3.25.76 - '@ai-sdk/provider@2.0.1': dependencies: json-schema: 0.4.0 @@ -10046,10 +10344,6 @@ snapshots: json-schema: 0.4.0 optional: true - '@ai-sdk/provider@3.0.8': - dependencies: - json-schema: 0.4.0 - '@ai-sdk/togetherai@1.0.38(zod@4.3.6)': dependencies: '@ai-sdk/openai-compatible': 1.0.35(zod@4.3.6) @@ -10068,11 +10362,11 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@anthropic-ai/claude-agent-sdk@0.2.112(@cfworker/json-schema@4.1.1)(zod@3.25.76)': + '@anthropic-ai/claude-agent-sdk@0.2.112(@cfworker/json-schema@4.1.1)(zod@4.3.6)': dependencies: - '@anthropic-ai/sdk': 0.81.0(zod@3.25.76) - '@modelcontextprotocol/sdk': 1.29.0(@cfworker/json-schema@4.1.1)(zod@3.25.76) - zod: 3.25.76 + '@anthropic-ai/sdk': 0.81.0(zod@4.3.6) + '@modelcontextprotocol/sdk': 1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6) + zod: 4.3.6 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.5 '@img/sharp-darwin-x64': 0.34.5 @@ -10118,12 +10412,12 @@ snapshots: '@img/sharp-win32-arm64': 0.34.5 '@img/sharp-win32-x64': 0.34.5 - '@anthropic-ai/sdk@0.111.0(zod@3.25.76)': + '@anthropic-ai/sdk@0.111.0(zod@4.3.6)': dependencies: json-schema-to-ts: 3.1.1 standardwebhooks: 1.0.0 optionalDependencies: - zod: 3.25.76 + zod: 4.3.6 '@anthropic-ai/sdk@0.39.0': dependencies: @@ -10143,17 +10437,23 @@ snapshots: optionalDependencies: zod: 3.25.76 + '@anthropic-ai/sdk@0.73.0(zod@4.3.6)': + dependencies: + json-schema-to-ts: 3.1.1 + optionalDependencies: + zod: 4.3.6 + '@anthropic-ai/sdk@0.74.0(zod@4.3.6)': dependencies: json-schema-to-ts: 3.1.1 optionalDependencies: zod: 4.3.6 - '@anthropic-ai/sdk@0.81.0(zod@3.25.76)': + '@anthropic-ai/sdk@0.81.0(zod@4.3.6)': dependencies: json-schema-to-ts: 3.1.1 optionalDependencies: - zod: 3.25.76 + zod: 4.3.6 '@anthropic-ai/sdk@0.91.1(zod@3.25.76)': dependencies: @@ -11870,6 +12170,18 @@ snapshots: - ws - zod + '@mariozechner/pi-agent-core@0.60.0(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6))(bufferutil@4.1.0)(ws@8.21.0(bufferutil@4.1.0))(zod@4.3.6)': + dependencies: + '@mariozechner/pi-ai': 0.60.0(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6))(bufferutil@4.1.0)(ws@8.21.0(bufferutil@4.1.0))(zod@4.3.6) + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - aws-crt + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + '@mariozechner/pi-ai@0.60.0(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@3.25.76))(bufferutil@4.1.0)(ws@8.21.0(bufferutil@4.1.0))(zod@3.25.76)': dependencies: '@anthropic-ai/sdk': 0.73.0(zod@3.25.76) @@ -11894,6 +12206,30 @@ snapshots: - ws - zod + '@mariozechner/pi-ai@0.60.0(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6))(bufferutil@4.1.0)(ws@8.21.0(bufferutil@4.1.0))(zod@4.3.6)': + dependencies: + '@anthropic-ai/sdk': 0.73.0(zod@4.3.6) + '@aws-sdk/client-bedrock-runtime': 3.1020.0 + '@google/genai': 1.47.0(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6))(bufferutil@4.1.0) + '@mistralai/mistralai': 1.14.1(bufferutil@4.1.0) + '@sinclair/typebox': 0.34.49 + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + chalk: 5.6.2 + openai: 6.26.0(ws@8.21.0(bufferutil@4.1.0))(zod@4.3.6) + partial-json: 0.1.7 + proxy-agent: 6.5.0 + undici: 7.24.6 + zod-to-json-schema: 3.25.2(zod@4.3.6) + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - aws-crt + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + '@mariozechner/pi-coding-agent@0.60.0(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@3.25.76))(bufferutil@4.1.0)(ws@8.21.0(bufferutil@4.1.0))(zod@3.25.76)': dependencies: '@mariozechner/jiti': 2.6.5 @@ -11926,6 +12262,38 @@ snapshots: - ws - zod + '@mariozechner/pi-coding-agent@0.60.0(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6))(bufferutil@4.1.0)(ws@8.21.0(bufferutil@4.1.0))(zod@4.3.6)': + dependencies: + '@mariozechner/jiti': 2.6.5 + '@mariozechner/pi-agent-core': 0.60.0(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6))(bufferutil@4.1.0)(ws@8.21.0(bufferutil@4.1.0))(zod@4.3.6) + '@mariozechner/pi-ai': 0.60.0(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6))(bufferutil@4.1.0)(ws@8.21.0(bufferutil@4.1.0))(zod@4.3.6) + '@mariozechner/pi-tui': 0.60.0 + '@silvia-odwyer/photon-node': 0.3.4 + chalk: 5.6.2 + cli-highlight: 2.1.11 + diff: 8.0.4 + extract-zip: 2.0.1 + file-type: 21.3.4 + glob: 13.0.6 + hosted-git-info: 9.0.2 + ignore: 7.0.5 + marked: 15.0.12 + minimatch: 10.2.5 + proper-lockfile: 4.1.2 + strip-ansi: 7.2.0 + undici: 7.24.6 + yaml: 2.8.3 + optionalDependencies: + '@mariozechner/clipboard': 0.3.2 + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - aws-crt + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + '@mariozechner/pi-tui@0.60.0': dependencies: '@types/mime-types': 2.1.4 @@ -11936,11 +12304,50 @@ snapshots: optionalDependencies: koffi: 2.15.2 + '@microsoft/api-extractor-model@7.28.13(@types/node@22.19.15)': + dependencies: + '@microsoft/tsdoc': 0.14.2 + '@microsoft/tsdoc-config': 0.16.2 + '@rushstack/node-core-library': 4.0.2(@types/node@22.19.15) + transitivePeerDependencies: + - '@types/node' + optional: true + + '@microsoft/api-extractor@7.43.0(@types/node@22.19.15)': + dependencies: + '@microsoft/api-extractor-model': 7.28.13(@types/node@22.19.15) + '@microsoft/tsdoc': 0.14.2 + '@microsoft/tsdoc-config': 0.16.2 + '@rushstack/node-core-library': 4.0.2(@types/node@22.19.15) + '@rushstack/rig-package': 0.5.2 + '@rushstack/terminal': 0.10.0(@types/node@22.19.15) + '@rushstack/ts-command-line': 4.19.1(@types/node@22.19.15) + lodash: 4.17.23 + minimatch: 3.0.8 + resolve: 1.22.11 + semver: 7.5.4 + source-map: 0.6.1 + typescript: 5.4.2 + transitivePeerDependencies: + - '@types/node' + optional: true + + '@microsoft/tsdoc-config@0.16.2': + dependencies: + '@microsoft/tsdoc': 0.14.2 + ajv: 6.12.6 + jju: 1.4.0 + resolve: 1.19.0 + optional: true + + '@microsoft/tsdoc@0.14.2': + optional: true + '@mistralai/mistralai@1.14.1(bufferutil@4.1.0)': dependencies: ws: 8.21.0(bufferutil@4.1.0) - zod: 3.25.76 - zod-to-json-schema: 3.25.2(zod@3.25.76) + zod: 4.3.6 + zod-to-json-schema: 3.25.2(zod@4.3.6) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -11949,8 +12356,8 @@ snapshots: dependencies: '@opentelemetry/semantic-conventions': 1.43.0 ws: 8.21.0(bufferutil@4.1.0) - zod: 3.25.76 - zod-to-json-schema: 3.25.2(zod@3.25.76) + zod: 4.3.6 + zod-to-json-schema: 3.25.2(zod@4.3.6) optionalDependencies: '@opentelemetry/api': 1.9.0 transitivePeerDependencies: @@ -11961,8 +12368,8 @@ snapshots: dependencies: '@opentelemetry/semantic-conventions': 1.43.0 ws: 8.21.0(bufferutil@4.1.0) - zod: 3.25.76 - zod-to-json-schema: 3.25.2(zod@3.25.76) + zod: 4.3.6 + zod-to-json-schema: 3.25.2(zod@4.3.6) optionalDependencies: '@opentelemetry/api': 1.9.0 transitivePeerDependencies: @@ -12282,10 +12689,10 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 - '@rivet-dev/agent-os-core@0.1.1(pyodide@0.28.3(bufferutil@4.1.0))': + '@rivet-dev/agent-os-core@0.1.1': dependencies: '@rivet-dev/agent-os-posix': 0.1.0 - '@rivet-dev/agent-os-python': 0.1.0(pyodide@0.28.3(bufferutil@4.1.0)) + '@rivet-dev/agent-os-python': 0.1.0 '@secure-exec/core': 0.2.1 '@secure-exec/nodejs': 0.2.1 '@secure-exec/v8': 0.2.1 @@ -12299,42 +12706,44 @@ snapshots: dependencies: '@secure-exec/core': 0.2.1 - '@rivet-dev/agent-os-python@0.1.0(pyodide@0.28.3(bufferutil@4.1.0))': + '@rivet-dev/agent-os-python@0.1.0': dependencies: '@secure-exec/core': 0.2.1 - optionalDependencies: - pyodide: 0.28.3(bufferutil@4.1.0) '@rivetkit/bare-ts@0.6.2': {} - '@rivetkit/engine-cli-darwin-arm64@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/engine-cli-darwin-arm64@2.3.7': + optional: true + + '@rivetkit/engine-cli-darwin-x64@2.3.7': optional: true - '@rivetkit/engine-cli-darwin-x64@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/engine-cli-linux-arm64-musl@2.3.7': optional: true - '@rivetkit/engine-cli-linux-arm64-musl@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/engine-cli-linux-x64-musl@2.3.7': optional: true - '@rivetkit/engine-cli-linux-x64-musl@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/engine-cli-win32-x64@2.3.7': optional: true - '@rivetkit/engine-cli@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/engine-cli@2.3.7': optionalDependencies: - '@rivetkit/engine-cli-darwin-arm64': 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/engine-cli-darwin-x64': 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/engine-cli-linux-arm64-musl': 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/engine-cli-linux-x64-musl': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/engine-cli-darwin-arm64': 2.3.7 + '@rivetkit/engine-cli-darwin-x64': 2.3.7 + '@rivetkit/engine-cli-linux-arm64-musl': 2.3.7 + '@rivetkit/engine-cli-linux-x64-musl': 2.3.7 + '@rivetkit/engine-cli-win32-x64': 2.3.7 - '@rivetkit/engine-envoy-protocol@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/engine-envoy-protocol@2.3.7': dependencies: '@rivetkit/bare-ts': 0.6.2 - '@rivetkit/framework-base@0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0))': + '@rivetkit/framework-base@2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0))': dependencies: '@tanstack/store': 0.7.7 fast-deep-equal: 3.1.3 - rivetkit: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + rivetkit: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) transitivePeerDependencies: - '@aws-sdk/client-rds-data' - '@cloudflare/workers-types' @@ -12372,13 +12781,13 @@ snapshots: '@rivetkit/on-change@6.0.1': {} - '@rivetkit/react@0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(ws@8.21.0(bufferutil@4.1.0))': + '@rivetkit/react@2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(ws@8.21.0(bufferutil@4.1.0))': dependencies: - '@rivetkit/framework-base': 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + '@rivetkit/framework-base': 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) '@tanstack/react-store': 0.7.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - rivetkit: 0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)) + rivetkit: 2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)) transitivePeerDependencies: - '@aws-sdk/client-rds-data' - '@cloudflare/workers-types' @@ -12414,48 +12823,52 @@ snapshots: - sqlite3 - ws - '@rivetkit/rivetkit-napi-darwin-arm64@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/rivetkit-napi-darwin-arm64@2.3.7': + optional: true + + '@rivetkit/rivetkit-napi-darwin-x64@2.3.7': optional: true - '@rivetkit/rivetkit-napi-darwin-x64@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/rivetkit-napi-linux-arm64-gnu@2.3.7': optional: true - '@rivetkit/rivetkit-napi-linux-arm64-gnu@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/rivetkit-napi-linux-arm64-musl@2.3.7': optional: true - '@rivetkit/rivetkit-napi-linux-arm64-musl@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/rivetkit-napi-linux-x64-gnu@2.3.7': optional: true - '@rivetkit/rivetkit-napi-linux-x64-gnu@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/rivetkit-napi-linux-x64-musl@2.3.7': optional: true - '@rivetkit/rivetkit-napi-linux-x64-musl@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/rivetkit-napi-win32-x64-msvc@2.3.7': optional: true - '@rivetkit/rivetkit-napi@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/rivetkit-napi@2.3.7': dependencies: '@napi-rs/cli': 2.18.4 - '@rivetkit/engine-envoy-protocol': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/engine-envoy-protocol': 2.3.7 optionalDependencies: - '@rivetkit/rivetkit-napi-darwin-arm64': 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/rivetkit-napi-darwin-x64': 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/rivetkit-napi-linux-arm64-gnu': 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/rivetkit-napi-linux-arm64-musl': 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/rivetkit-napi-linux-x64-gnu': 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/rivetkit-napi-linux-x64-musl': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/rivetkit-napi-darwin-arm64': 2.3.7 + '@rivetkit/rivetkit-napi-darwin-x64': 2.3.7 + '@rivetkit/rivetkit-napi-linux-arm64-gnu': 2.3.7 + '@rivetkit/rivetkit-napi-linux-arm64-musl': 2.3.7 + '@rivetkit/rivetkit-napi-linux-x64-gnu': 2.3.7 + '@rivetkit/rivetkit-napi-linux-x64-musl': 2.3.7 + '@rivetkit/rivetkit-napi-win32-x64-msvc': 2.3.7 - '@rivetkit/rivetkit-wasm@2.3.2': {} + '@rivetkit/rivetkit-wasm@2.3.7': {} - '@rivetkit/traces@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/traces@2.3.7': dependencies: '@rivetkit/bare-ts': 0.6.2 cbor-x: 1.6.4 fdb-tuple: 1.0.0 vbare: 0.0.4 - '@rivetkit/virtual-websocket@0.0.0-sqlite-uds.4e59a38': {} + '@rivetkit/virtual-websocket@2.3.7': {} - '@rivetkit/workflow-engine@0.0.0-sqlite-uds.4e59a38': + '@rivetkit/workflow-engine@2.3.7': dependencies: '@rivetkit/bare-ts': 0.6.2 cbor-x: 1.6.4 @@ -12540,6 +12953,42 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.60.1': optional: true + '@rushstack/node-core-library@4.0.2(@types/node@22.19.15)': + dependencies: + fs-extra: 7.0.1 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.11 + semver: 7.5.4 + z-schema: 5.0.5 + optionalDependencies: + '@types/node': 22.19.15 + optional: true + + '@rushstack/rig-package@0.5.2': + dependencies: + resolve: 1.22.11 + strip-json-comments: 3.1.1 + optional: true + + '@rushstack/terminal@0.10.0(@types/node@22.19.15)': + dependencies: + '@rushstack/node-core-library': 4.0.2(@types/node@22.19.15) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 22.19.15 + optional: true + + '@rushstack/ts-command-line@4.19.1(@types/node@22.19.15)': + dependencies: + '@rushstack/terminal': 0.10.0(@types/node@22.19.15) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + optional: true + '@sandbox-agent/cli-darwin-arm64@0.4.2': optional: true @@ -13028,6 +13477,9 @@ snapshots: '@turbo/windows-arm64@2.9.1': optional: true + '@types/argparse@1.0.38': + optional: true + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.29.7 @@ -13209,18 +13661,18 @@ snapshots: '@opentelemetry/api': 1.9.0 zod: 4.3.6 - ai@6.0.141(zod@3.25.76): - dependencies: - '@ai-sdk/gateway': 3.0.83(zod@3.25.76) - '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.21(zod@3.25.76) - '@opentelemetry/api': 1.9.0 - zod: 3.25.76 - ajv-formats@3.0.1(ajv@8.18.0): optionalDependencies: ajv: 8.18.0 + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + optional: true + ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 @@ -13279,6 +13731,11 @@ snapshots: arg@5.0.2: {} + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + optional: true + asn1.js@4.10.1: dependencies: bn.js: 4.12.4 @@ -13414,6 +13871,12 @@ snapshots: bowser@2.14.1: {} + brace-expansion@1.1.16: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + optional: true + brace-expansion@2.0.3: dependencies: balanced-match: 1.0.2 @@ -13723,6 +14186,9 @@ snapshots: commander@4.1.1: {} + commander@9.5.0: + optional: true + compress-commons@6.0.2: dependencies: crc-32: 1.2.2 @@ -13731,6 +14197,9 @@ snapshots: normalize-path: 3.0.0 readable-stream: 4.7.0 + concat-map@0.0.1: + optional: true + concurrently@9.2.3: dependencies: chalk: 4.1.2 @@ -14244,6 +14713,9 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: + optional: true + fast-safe-stringify@2.1.1: {} fast-sha256@1.3.0: {} @@ -14363,6 +14835,13 @@ snapshots: fs-constants@1.0.0: {} + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + optional: true + fsevents@2.3.2: optional: true @@ -14638,6 +15117,9 @@ snapshots: immediate@3.0.6: {} + import-lazy@4.0.0: + optional: true + indent-string@4.0.0: {} inherits@2.0.4: {} @@ -14753,6 +15235,9 @@ snapshots: jiti@2.7.0: {} + jju@1.4.0: + optional: true + jose@6.2.2: {} joycon@3.1.1: {} @@ -14774,6 +15259,9 @@ snapshots: '@babel/runtime': 7.29.2 ts-algebra: 2.0.0 + json-schema-traverse@0.4.1: + optional: true + json-schema-traverse@1.0.0: {} json-schema-typed@8.0.2: {} @@ -14784,6 +15272,11 @@ snapshots: jsonc-parser@3.3.1: {} + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + optional: true + jszip@3.10.1: dependencies: lie: 3.3.0 @@ -14845,6 +15338,15 @@ snapshots: lodash.camelcase@4.3.0: {} + lodash.get@4.4.2: + optional: true + + lodash.isequal@4.5.0: + optional: true + + lodash@4.17.23: + optional: true + lodash@4.18.1: {} long-timeout@0.1.1: {} @@ -14865,6 +15367,11 @@ snapshots: dependencies: yallist: 3.1.1 + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + optional: true + lru-cache@7.18.3: {} magic-string@0.30.21: @@ -14928,6 +15435,11 @@ snapshots: dependencies: brace-expansion: 5.0.5 + minimatch@3.0.8: + dependencies: + brace-expansion: 1.1.16 + optional: true + minimatch@5.1.9: dependencies: brace-expansion: 2.0.3 @@ -14977,7 +15489,7 @@ snapshots: node-abi@3.89.0: dependencies: - semver: 7.7.4 + semver: 7.8.5 node-domexception@1.0.0: {} @@ -15151,6 +15663,11 @@ snapshots: ws: 8.21.0(bufferutil@4.1.0) zod: 3.25.76 + openai@6.26.0(ws@8.21.0(bufferutil@4.1.0))(zod@4.3.6): + optionalDependencies: + ws: 8.21.0(bufferutil@4.1.0) + zod: 4.3.6 + openapi3-ts@4.6.0: dependencies: yaml: 2.9.0 @@ -15557,6 +16074,9 @@ snapshots: punycode@1.4.1: {} + punycode@2.3.1: + optional: true + puppeteer-core@22.15.0(bufferutil@4.1.0): dependencies: '@puppeteer/browsers': 2.3.0 @@ -15573,13 +16093,6 @@ snapshots: - utf-8-validate optional: true - pyodide@0.28.3(bufferutil@4.1.0): - dependencies: - ws: 8.21.0(bufferutil@4.1.0) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - qs@6.15.0: dependencies: side-channel: 1.1.0 @@ -15707,6 +16220,12 @@ snapshots: resolve-pkg-maps@1.0.0: {} + resolve@1.19.0: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + optional: true + resolve@1.22.11: dependencies: is-core-module: 2.16.1 @@ -15724,19 +16243,19 @@ snapshots: hash-base: 3.1.2 inherits: 2.0.4 - rivetkit@0.0.0-sqlite-uds.4e59a38(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(pyodide@0.28.3(bufferutil@4.1.0))(ws@8.21.0(bufferutil@4.1.0)): + rivetkit@2.3.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0)(ws@8.21.0(bufferutil@4.1.0)): dependencies: '@hono/zod-openapi': 1.4.0(hono@4.12.9)(zod@4.3.6) - '@rivet-dev/agent-os-core': 0.1.1(pyodide@0.28.3(bufferutil@4.1.0)) + '@rivet-dev/agent-os-core': 0.1.1 '@rivetkit/bare-ts': 0.6.2 - '@rivetkit/engine-cli': 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/engine-envoy-protocol': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/engine-cli': 2.3.7 + '@rivetkit/engine-envoy-protocol': 2.3.7 '@rivetkit/on-change': 6.0.1 - '@rivetkit/rivetkit-napi': 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/rivetkit-wasm': 2.3.2 - '@rivetkit/traces': 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/virtual-websocket': 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/workflow-engine': 0.0.0-sqlite-uds.4e59a38 + '@rivetkit/rivetkit-napi': 2.3.7 + '@rivetkit/rivetkit-wasm': 2.3.7 + '@rivetkit/traces': 2.3.7 + '@rivetkit/virtual-websocket': 2.3.7 + '@rivetkit/workflow-engine': 2.3.7 cbor-x: 1.6.4 drizzle-orm: 0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@12.8.0) hono: 4.12.9 @@ -15871,6 +16390,11 @@ snapshots: semver@6.3.1: {} + semver@7.5.4: + dependencies: + lru-cache: 6.0.0 + optional: true + semver@7.7.4: {} semver@7.8.0: {} @@ -16005,6 +16529,9 @@ snapshots: split2@4.2.0: {} + sprintf-js@1.0.3: + optional: true + ssh2@1.17.0: dependencies: asn1: 0.2.6 @@ -16045,6 +16572,9 @@ snapshots: - bare-abort-controller - react-native-b4a + string-argv@0.3.2: + optional: true + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -16077,6 +16607,9 @@ snapshots: strip-json-comments@2.0.1: {} + strip-json-comments@3.1.1: + optional: true + strip-json-comments@5.0.3: {} strnum@2.2.2: {} @@ -16271,7 +16804,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.1(jiti@1.21.7)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0): + tsup@8.5.1(@microsoft/api-extractor@7.43.0(@types/node@22.19.15))(jiti@1.21.7)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0): dependencies: bundle-require: 5.1.0(esbuild@0.27.4) cac: 6.7.14 @@ -16291,6 +16824,7 @@ snapshots: tinyglobby: 0.2.15 tree-kill: 1.2.2 optionalDependencies: + '@microsoft/api-extractor': 7.43.0(@types/node@22.19.15) postcss: 8.5.8 typescript: 5.9.3 transitivePeerDependencies: @@ -16299,7 +16833,7 @@ snapshots: - tsx - yaml - tsup@8.5.1(jiti@2.7.0)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0): + tsup@8.5.1(@microsoft/api-extractor@7.43.0(@types/node@22.19.15))(jiti@2.7.0)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0): dependencies: bundle-require: 5.1.0(esbuild@0.27.4) cac: 6.7.14 @@ -16319,6 +16853,7 @@ snapshots: tinyglobby: 0.2.15 tree-kill: 1.2.2 optionalDependencies: + '@microsoft/api-extractor': 7.43.0(@types/node@22.19.15) postcss: 8.5.8 typescript: 5.9.3 transitivePeerDependencies: @@ -16369,6 +16904,9 @@ snapshots: es-errors: 1.3.0 is-typed-array: 1.1.15 + typescript@5.4.2: + optional: true + typescript@5.9.3: {} ufo@1.6.4: {} @@ -16391,6 +16929,9 @@ snapshots: undici@8.5.0: {} + universalify@0.1.2: + optional: true + unpipe@1.0.0: {} update-browserslist-db@1.2.3(browserslist@4.28.2): @@ -16399,6 +16940,11 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + optional: true + url-template@2.0.8: {} url@0.11.4: @@ -16433,6 +16979,9 @@ snapshots: uuid@9.0.1: {} + validator@13.15.35: + optional: true + vary@1.1.2: {} vbare@0.0.4: {} @@ -16601,6 +17150,9 @@ snapshots: yallist@3.1.1: {} + yallist@4.0.0: + optional: true + yaml@2.8.3: {} yaml@2.9.0: {} @@ -16638,6 +17190,15 @@ snapshots: yoctocolors@2.1.2: {} + z-schema@5.0.5: + dependencies: + lodash.get: 4.4.2 + lodash.isequal: 4.5.0 + validator: 13.15.35 + optionalDependencies: + commander: 9.5.0 + optional: true + zip-stream@6.0.1: dependencies: archiver-utils: 5.0.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 77cb7b4ff3..372d1b07fc 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,6 +2,8 @@ packages: - packages/agentos-sandbox - packages/agentos - packages/agentos-toolchain + - packages/javascript + - packages/python - packages/browser - packages/build-tools - packages/core @@ -9,15 +11,12 @@ packages: - packages/node-pty - packages/playground - packages/posix - - packages/python - packages/runtime-benchmarks - packages/runtime-browser - packages/runtime-core - packages/runtime-sidecar - packages/runtime-sidecar/npm/* - packages/vm-test-harness - - packages/secure-exec - - packages/secure-exec-example-ai-agent-type-check - packages/typescript - packages/shell - packages/sidecar-binary @@ -57,9 +56,9 @@ onlyBuiltDependencies: # pinned once here and referenced everywhere via `catalog:rivetkit`. catalogs: rivetkit: - rivetkit: 0.0.0-sqlite-uds.4e59a38 - '@rivetkit/react': 0.0.0-sqlite-uds.4e59a38 + rivetkit: 2.3.7 + '@rivetkit/react': 2.3.7 overrides: - '@rivetkit/rivetkit-wasm': 2.3.2 + '@rivetkit/rivetkit-wasm': 2.3.7 web-streams-polyfill: 3.3.3 diff --git a/scripts/benchmarks/README.md b/scripts/benchmarks/README.md index f3d22a70bd..328126856d 100644 --- a/scripts/benchmarks/README.md +++ b/scripts/benchmarks/README.md @@ -13,13 +13,13 @@ Agent OS keeps only product-surface benchmarks here: The differential matrix, focused runtime lanes, fuzz/perf harness, leak and footprint probes, native comparisons, and ecosystem command benches now live in -secure-exec: +agentos: -`/home/nathan/.herdr/workspaces/agent-os/secure-exec-perf-rules/packages/benchmarks` +`/home/nathan/.herdr/workspaces/agent-os/agentos-perf-rules/packages/benchmarks` Use that package for runtime-focused investigations; also follow its `CLAUDE.md` Benchmarks section. `overlay-readdir` is deleted here too; its -secure-exec port is pending the API it needs. +agentos port is pending the API it needs. ## Standard Suite diff --git a/scripts/benchmarks/session.bench.ts b/scripts/benchmarks/session.bench.ts index ef683114c9..93364ee4be 100644 --- a/scripts/benchmarks/session.bench.ts +++ b/scripts/benchmarks/session.bench.ts @@ -299,7 +299,7 @@ async function loadPiSoftware(): Promise { // non-snapshot fallback path and hides the optimization entirely. const local = join( import.meta.dirname, - "../../../secure-exec/software/pi/dist/index.js", + "../../../agentos/software/pi/dist/index.js", ); if (existsSync(local)) return (await import(local)).default; // Fallback: the published/installed software package. Variable specifier so @@ -309,7 +309,7 @@ async function loadPiSoftware(): Promise { return (await import(piPkg)).default; } catch { throw new Error( - "Could not resolve the pi software package (../secure-exec/software/pi/dist or @agentos-software/pi). Build it first.", + "Could not resolve the pi software package (../agentos/software/pi/dist or @agentos-software/pi). Build it first.", ); } } @@ -323,7 +323,7 @@ function findPiSdkRoot(): string | null { createRequire( join( import.meta.dirname, - "../../../secure-exec/software/pi/package.json", + "../../../agentos/software/pi/package.json", ), ), ]; @@ -373,7 +373,7 @@ function resolvePiSdkRootOrThrow(): string { * The bare-node session-creation script, run in a FRESH node process per sample * so each pays the full cold SDK load (the VM lane reloads the SDK in a fresh V8 * isolate every session, so this is the apples-to-apples "Node.js equivalent"). - * Mirrors secure-exec software/pi/src/adapter.ts `newSession`, with no VM. It times the + * Mirrors agentos software/pi/src/adapter.ts `newSession`, with no VM. It times the * SDK load + session construction internally and prints `__MS__=`. */ function bareNodeScript(root: string): string { diff --git a/scripts/check-agentos-client-protocol-compat.mjs b/scripts/check-agentos-client-protocol-compat.mjs index b0eba06e47..acb13e2149 100644 --- a/scripts/check-agentos-client-protocol-compat.mjs +++ b/scripts/check-agentos-client-protocol-compat.mjs @@ -11,12 +11,12 @@ const defaultRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const allowedCompatFiles = new Set([]); -const compatPattern = /\bsecure_exec_client::protocol\b/g; +const compatPattern = /\bagentos_client::protocol\b/g; const legacyWireConstantPattern = - /\bsecure_exec_client::protocol::DEFAULT_MAX_FRAME_BYTES\b/g; + /\bagentos_client::protocol::DEFAULT_MAX_FRAME_BYTES\b/g; const staleCompatibilityDocPattern = /\blive transport still uses the compatibility protocol surface\b/g; -const sidecarCompatPattern = /\bsecure_exec_sidecar::protocol\b/g; +const sidecarCompatPattern = /\bagentos_sidecar::protocol\b/g; function isDir(path) { return existsSync(path) && statSync(path).isDirectory(); @@ -56,7 +56,7 @@ function formatPath(root, path) { function reportSidecarProtocolUse(errors, source, rel, index) { const location = lineAndColumn(source, index); errors.push( - `${rel}:${location.line}:${location.column} imports the secure-exec sidecar compatibility protocol surface; use secure_exec_sidecar::wire for generated wire types`, + `${rel}:${location.line}:${location.column} imports the agentos sidecar compatibility protocol surface; use agentos_sidecar::wire for generated wire types`, ); } @@ -80,14 +80,14 @@ export function checkAgentOsClientProtocolCompat(options = {}) { for (const match of source.matchAll(legacyWireConstantPattern)) { const location = lineAndColumn(source, match.index ?? 0); errors.push( - `${rel}:${location.line}:${location.column} reads the default frame limit through the compatibility protocol surface; use secure_exec_client::wire::DEFAULT_MAX_FRAME_BYTES`, + `${rel}:${location.line}:${location.column} reads the default frame limit through the compatibility protocol surface; use agentos_client::wire::DEFAULT_MAX_FRAME_BYTES`, ); } staleCompatibilityDocPattern.lastIndex = 0; for (const match of source.matchAll(staleCompatibilityDocPattern)) { const location = lineAndColumn(source, match.index ?? 0); errors.push( - `${rel}:${location.line}:${location.column} documents stale generated-wire migration state; describe secure_exec_client::wire as the active transport surface`, + `${rel}:${location.line}:${location.column} documents stale generated-wire migration state; describe agentos_client::wire as the active transport surface`, ); } compatPattern.lastIndex = 0; @@ -95,7 +95,7 @@ export function checkAgentOsClientProtocolCompat(options = {}) { if (!allowedCompatFiles.has(rel)) { const location = lineAndColumn(source, match.index ?? 0); errors.push( - `${rel}:${location.line}:${location.column} imports the live protocol compatibility surface; use secure_exec_client::wire for generated wire types or add this file to the migration inventory with justification`, + `${rel}:${location.line}:${location.column} imports the live protocol compatibility surface; use agentos_client::wire for generated wire types or add this file to the migration inventory with justification`, ); } } diff --git a/scripts/check-agentos-client-protocol-compat.test.mjs b/scripts/check-agentos-client-protocol-compat.test.mjs index f49e5b7ed3..897ba8721b 100644 --- a/scripts/check-agentos-client-protocol-compat.test.mjs +++ b/scripts/check-agentos-client-protocol-compat.test.mjs @@ -58,13 +58,13 @@ test("allows agentos-sidecar generated wire imports", () => { root, "crates/agentos-sidecar/src/acp_extension.rs", [ - "use secure_exec_sidecar::wire::{", + "use agentos_sidecar::wire::{", "\tCloseStdinRequest, EventPayload, ExecuteRequest, GuestFilesystemCallRequest,", "\tGuestFilesystemOperation, GuestRuntimeKind, KillProcessRequest, StreamChannel,", "\tWriteStdinRequest,", "};", "", - "fn accepts(events: &[secure_exec_sidecar::wire::EventFrame]) {", + "fn accepts(events: &[agentos_sidecar::wire::EventFrame]) {", "\tlet _ = events;", "}", "", @@ -82,13 +82,13 @@ test("rejects agentos-sidecar primitive protocol imports", () => { root, "crates/agentos-sidecar/src/acp_extension.rs", [ - "use secure_exec_sidecar::protocol::{", + "use agentos_sidecar::protocol::{", "\tCloseStdinRequest, EventPayload, ExecuteRequest, GuestFilesystemCallRequest,", "\tGuestFilesystemOperation, GuestRuntimeKind, KillProcessRequest, StreamChannel,", "\tWriteStdinRequest,", "};", "", - "fn accepts(events: &[secure_exec_sidecar::protocol::EventFrame]) {", + "fn accepts(events: &[agentos_sidecar::protocol::EventFrame]) {", "\tlet _ = events;", "}", "", @@ -96,8 +96,8 @@ test("rejects agentos-sidecar primitive protocol imports", () => { ); assert.deepEqual(checkAgentOsClientProtocolCompat({ root }), [ - "crates/agentos-sidecar/src/acp_extension.rs:1:5 imports the secure-exec sidecar compatibility protocol surface; use secure_exec_sidecar::wire for generated wire types", - "crates/agentos-sidecar/src/acp_extension.rs:7:22 imports the secure-exec sidecar compatibility protocol surface; use secure_exec_sidecar::wire for generated wire types", + "crates/agentos-sidecar/src/acp_extension.rs:1:5 imports the agentos sidecar compatibility protocol surface; use agentos_sidecar::wire for generated wire types", + "crates/agentos-sidecar/src/acp_extension.rs:7:22 imports the agentos sidecar compatibility protocol surface; use agentos_sidecar::wire for generated wire types", ]); }); }); @@ -108,11 +108,11 @@ test("rejects new agentos-client live protocol imports outside the inventory", ( write( root, "crates/client/src/new_feature.rs", - "use secure_exec_client::protocol::RequestPayload;\n", + "use agentos_client::protocol::RequestPayload;\n", ); assert.deepEqual(checkAgentOsClientProtocolCompat({ root }), [ - "crates/client/src/new_feature.rs:1:5 imports the live protocol compatibility surface; use secure_exec_client::wire for generated wire types or add this file to the migration inventory with justification", + "crates/client/src/new_feature.rs:1:5 imports the live protocol compatibility surface; use agentos_client::wire for generated wire types or add this file to the migration inventory with justification", ]); }); }); @@ -123,11 +123,11 @@ test("rejects agentos-client test protocol imports", () => { write( root, "crates/client/tests/session_e2e.rs", - "use secure_exec_client::protocol::RequestPayload;\n", + "use agentos_client::protocol::RequestPayload;\n", ); assert.deepEqual(checkAgentOsClientProtocolCompat({ root }), [ - "crates/client/tests/session_e2e.rs:1:5 imports the live protocol compatibility surface; use secure_exec_client::wire for generated wire types or add this file to the migration inventory with justification", + "crates/client/tests/session_e2e.rs:1:5 imports the live protocol compatibility surface; use agentos_client::wire for generated wire types or add this file to the migration inventory with justification", ]); }); }); @@ -138,11 +138,11 @@ test("rejects production agentos-sidecar dispatch protocol imports", () => { write( root, "crates/agentos-sidecar/src/acp_extension.rs", - "use secure_exec_sidecar::protocol::{EventPayload, RequestFrame, SidecarRequestPayload};\n", + "use agentos_sidecar::protocol::{EventPayload, RequestFrame, SidecarRequestPayload};\n", ); assert.deepEqual(checkAgentOsClientProtocolCompat({ root }), [ - "crates/agentos-sidecar/src/acp_extension.rs:1:5 imports the secure-exec sidecar compatibility protocol surface; use secure_exec_sidecar::wire for generated wire types", + "crates/agentos-sidecar/src/acp_extension.rs:1:5 imports the agentos sidecar compatibility protocol surface; use agentos_sidecar::wire for generated wire types", ]); }); }); @@ -153,11 +153,11 @@ test("rejects agentos-sidecar test protocol imports", () => { write( root, "crates/agentos-sidecar/tests/acp_extension.rs", - "use secure_exec_sidecar::protocol::EventPayload;\n", + "use agentos_sidecar::protocol::EventPayload;\n", ); assert.deepEqual(checkAgentOsClientProtocolCompat({ root }), [ - "crates/agentos-sidecar/tests/acp_extension.rs:1:5 imports the secure-exec sidecar compatibility protocol surface; use secure_exec_sidecar::wire for generated wire types", + "crates/agentos-sidecar/tests/acp_extension.rs:1:5 imports the agentos sidecar compatibility protocol surface; use agentos_sidecar::wire for generated wire types", ]); }); }); @@ -168,11 +168,11 @@ test("rejects production agentos-sidecar qualified dispatch protocol paths", () write( root, "crates/agentos-sidecar/src/acp_extension.rs", - "fn dispatch() { let _ = secure_exec_sidecar::protocol::RequestFrame::new; }\n", + "fn dispatch() { let _ = agentos_sidecar::protocol::RequestFrame::new; }\n", ); assert.deepEqual(checkAgentOsClientProtocolCompat({ root }), [ - "crates/agentos-sidecar/src/acp_extension.rs:1:25 imports the secure-exec sidecar compatibility protocol surface; use secure_exec_sidecar::wire for generated wire types", + "crates/agentos-sidecar/src/acp_extension.rs:1:25 imports the agentos sidecar compatibility protocol surface; use agentos_sidecar::wire for generated wire types", ]); }); }); @@ -183,11 +183,11 @@ test("rejects error taxonomy regressions to the compatibility protocol surface", write( root, "crates/client/src/error.rs", - "use secure_exec_client::protocol::ProtocolCodecError;\n", + "use agentos_client::protocol::ProtocolCodecError;\n", ); assert.deepEqual(checkAgentOsClientProtocolCompat({ root }), [ - "crates/client/src/error.rs:1:5 imports the live protocol compatibility surface; use secure_exec_client::wire for generated wire types or add this file to the migration inventory with justification", + "crates/client/src/error.rs:1:5 imports the live protocol compatibility surface; use agentos_client::wire for generated wire types or add this file to the migration inventory with justification", ]); }); }); @@ -198,11 +198,11 @@ test("rejects docs regressions to naming the compatibility protocol surface", () write( root, "crates/client/src/lib.rs", - "//! The generated schema surface is secure_exec_client::wire, not secure_exec_client::protocol.\n", + "//! The generated schema surface is agentos_client::wire, not agentos_client::protocol.\n", ); assert.deepEqual(checkAgentOsClientProtocolCompat({ root }), [ - "crates/client/src/lib.rs:1:67 imports the live protocol compatibility surface; use secure_exec_client::wire for generated wire types or add this file to the migration inventory with justification", + "crates/client/src/lib.rs:1:63 imports the live protocol compatibility surface; use agentos_client::wire for generated wire types or add this file to the migration inventory with justification", ]); }); }); @@ -213,11 +213,11 @@ test("rejects docs regressions to stale generated-wire migration wording", () => write( root, "crates/client/src/lib.rs", - "//! secure_exec_client::wire; the live transport still uses the compatibility protocol surface while migration continues.\n", + "//! agentos_client::wire; the live transport still uses the compatibility protocol surface while migration continues.\n", ); assert.deepEqual(checkAgentOsClientProtocolCompat({ root }), [ - "crates/client/src/lib.rs:1:35 documents stale generated-wire migration state; describe secure_exec_client::wire as the active transport surface", + "crates/client/src/lib.rs:1:31 documents stale generated-wire migration state; describe agentos_client::wire as the active transport surface", ]); }); }); @@ -228,11 +228,11 @@ test("rejects auth version regressions to the compatibility protocol surface", ( root, "crates/client/src/sidecar.rs", [ - "use secure_exec_client::protocol::{AuthenticateRequest, RequestPayload};", + "use agentos_client::protocol::{AuthenticateRequest, RequestPayload};", "", "fn authenticate() {", "\tlet _ = RequestPayload::Authenticate(AuthenticateRequest {", - "\t\tprotocol_version: secure_exec_client::protocol::PROTOCOL_VERSION,", + "\t\tprotocol_version: agentos_client::protocol::PROTOCOL_VERSION,", "\t});", "}", "", @@ -240,8 +240,8 @@ test("rejects auth version regressions to the compatibility protocol surface", ( ); assert.deepEqual(checkAgentOsClientProtocolCompat({ root }), [ - "crates/client/src/sidecar.rs:1:5 imports the live protocol compatibility surface; use secure_exec_client::wire for generated wire types or add this file to the migration inventory with justification", - "crates/client/src/sidecar.rs:5:21 imports the live protocol compatibility surface; use secure_exec_client::wire for generated wire types or add this file to the migration inventory with justification", + "crates/client/src/sidecar.rs:1:5 imports the live protocol compatibility surface; use agentos_client::wire for generated wire types or add this file to the migration inventory with justification", + "crates/client/src/sidecar.rs:5:21 imports the live protocol compatibility surface; use agentos_client::wire for generated wire types or add this file to the migration inventory with justification", "crates/client/src/sidecar.rs must import agentos_sidecar_client::wire", "crates/client/src/sidecar.rs authenticate request must use wire::PROTOCOL_VERSION", ]); @@ -255,14 +255,14 @@ test("rejects default frame limit regressions to the compatibility protocol surf root, "crates/client/src/net.rs", [ - "const LIMIT: usize = secure_exec_client::protocol::DEFAULT_MAX_FRAME_BYTES;", + "const LIMIT: usize = agentos_client::protocol::DEFAULT_MAX_FRAME_BYTES;", "", ].join("\n"), ); assert.deepEqual(checkAgentOsClientProtocolCompat({ root }), [ - "crates/client/src/net.rs:1:22 reads the default frame limit through the compatibility protocol surface; use secure_exec_client::wire::DEFAULT_MAX_FRAME_BYTES", - "crates/client/src/net.rs:1:22 imports the live protocol compatibility surface; use secure_exec_client::wire for generated wire types or add this file to the migration inventory with justification", + "crates/client/src/net.rs:1:22 reads the default frame limit through the compatibility protocol surface; use agentos_client::wire::DEFAULT_MAX_FRAME_BYTES", + "crates/client/src/net.rs:1:22 imports the live protocol compatibility surface; use agentos_client::wire for generated wire types or add this file to the migration inventory with justification", ]); }); }); diff --git a/scripts/check-rust-package-metadata.test.mjs b/scripts/check-rust-package-metadata.test.mjs index bc8ba216f1..01ec27d4e2 100644 --- a/scripts/check-rust-package-metadata.test.mjs +++ b/scripts/check-rust-package-metadata.test.mjs @@ -37,10 +37,10 @@ test("accepts expected Rust package metadata", () => { assert.deepEqual(checkRustPackageMetadata({ root, metadata: validMetadata }), []); }); -test("rejects stale agentos-client lib target names", () => { +test("rejects noncanonical agentos-client lib target names", () => { const metadata = structuredClone(validMetadata); const client = metadata.packages.find((item) => item.name === "agentos-client"); - client.targets[0].name = "secure_exec_client"; + client.targets[0].name = "agent_os_client"; assert.deepEqual(checkRustPackageMetadata({ root, metadata }), [ "agentos-client must expose a lib target named agentos_client", diff --git a/scripts/ci.sh b/scripts/ci.sh index 006cec47b5..0cd0fc5370 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -34,7 +34,7 @@ run_step node scripts/check-rust-package-metadata.mjs run_step node --test scripts/check-agentos-client-protocol-compat.test.mjs run_step node scripts/check-agentos-client-protocol-compat.mjs run_step pnpm check-layout -run_step node --test scripts/generate-secure-exec-mirror.test.mjs +run_step node --test scripts/generate-agentos-mirror.test.mjs if [[ -f scripts/check-registry-test-runtime-boundary.test.mjs ]]; then run_step node --test scripts/check-registry-test-runtime-boundary.test.mjs run_step node scripts/check-registry-test-runtime-boundary.mjs diff --git a/scripts/generate-secure-exec-mirror.mjs b/scripts/generate-secure-exec-mirror.mjs deleted file mode 100644 index 2877fb73f6..0000000000 --- a/scripts/generate-secure-exec-mirror.mjs +++ /dev/null @@ -1,395 +0,0 @@ -#!/usr/bin/env node -import { - mkdirSync, - readFileSync, - rmSync, - writeFileSync, -} from "node:fs"; -import { dirname, join, relative, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; - -const AGENTOS_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), ".."); -const DEFAULT_MIRROR_ROOT = resolve(AGENTOS_ROOT, "../secure-exec"); -const map = JSON.parse( - readFileSync(join(AGENTOS_ROOT, "scripts/secure-exec-agentos-map.json"), "utf8"), -); - -function parseArgs(argv) { - let mirrorRoot = DEFAULT_MIRROR_ROOT; - for (let i = 0; i < argv.length; i++) { - const arg = argv[i]; - if (arg === "--mirror-root") { - mirrorRoot = resolve(argv[++i]); - continue; - } - if (arg.startsWith("--mirror-root=")) { - mirrorRoot = resolve(arg.slice("--mirror-root=".length)); - continue; - } - throw new Error(`unknown argument: ${arg}`); - } - return { mirrorRoot }; -} - -function write(path, contents) { - mkdirSync(dirname(path), { recursive: true }); - writeFileSync(path, contents); -} - -function packageDirFor(pkg) { - if (pkg === "secure-exec") return "packages/secure-exec"; - const unscoped = pkg.replace(/^@secure-exec\//, ""); - return `packages/${unscoped}`; -} - -function crateDirFor(crateName) { - return `crates/${crateName.replace(/^secure-exec-/, "")}`; -} - -function json(value) { - return `${JSON.stringify(value, null, "\t")}\n`; -} - -function lockedRootVersion(lockfile, packageName) { - const importerStart = lockfile.indexOf("\n .:\n"); - const importerEnd = lockfile.indexOf("\n examples/", importerStart); - if (importerStart === -1 || importerEnd === -1) { - throw new Error("could not locate the AgentOS root importer in pnpm-lock.yaml"); - } - const importer = lockfile.slice(importerStart, importerEnd); - const match = importer.match( - new RegExp( - `\\n ${packageName}:\\n(?: [^\\n]*\\n)*? version: ([^\\n]+)`, - ), - ); - if (!match) { - throw new Error(`could not locate locked root dependency ${packageName}`); - } - return match[1]; -} - -function writePnpmLock(root, npmShims) { - const source = readFileSync(join(AGENTOS_ROOT, "pnpm-lock.yaml"), "utf8"); - const packagesStart = source.indexOf("\npackages:\n"); - if (packagesStart === -1) { - throw new Error("could not locate package snapshots in pnpm-lock.yaml"); - } - const turboVersion = lockedRootVersion(source, "turbo"); - const typescriptVersion = lockedRootVersion(source, "typescript"); - const importers = npmShims - .map((spec) => { - const packageDir = packageDirFor(spec.shimPackage); - const targetPath = join( - AGENTOS_ROOT, - spec.targetPath.replace(/^agentos\//, ""), - ); - const targetLink = `link:${relative(join(root, packageDir), targetPath)}`; - return ` ${packageDir}: - dependencies: - ${JSON.stringify(spec.targetPackage)}: - specifier: ${JSON.stringify(targetLink)} - version: ${JSON.stringify(targetLink)} - devDependencies: - typescript: - specifier: ^5.9.2 - version: ${typescriptVersion}`; - }) - .join("\n\n"); - write( - join(root, "pnpm-lock.yaml"), - `lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - devDependencies: - turbo: - specifier: ^2.5.6 - version: ${turboVersion} - typescript: - specifier: ^5.9.2 - version: ${typescriptVersion} - -${importers} -${source.slice(packagesStart)}`, - ); -} - -function npmShimExports(targetPackage) { - return { - ".": { - types: "./dist/index.d.ts", - import: "./dist/index.js", - default: "./dist/index.js", - }, - "./package.json": "./package.json", - }; -} - -function writeNpmShim(root, spec) { - const packageName = spec.shimPackage; - const dir = join(root, packageDirFor(packageName)); - const target = spec.targetPackage; - const browserShim = packageName === "@secure-exec/browser"; - const targetPath = join(AGENTOS_ROOT, spec.targetPath.replace(/^agentos\//, "")); - const targetLink = `link:${relative(dir, targetPath)}`; - write( - join(dir, "package.json"), - json({ - name: packageName, - ...(browserShim ? { private: true } : {}), - version: "0.0.1", - type: "module", - license: "Apache-2.0", - description: `${packageName} compatibility shim for ${target}.`, - main: "./dist/index.js", - types: "./dist/index.d.ts", - files: ["dist", "README.md"], - exports: npmShimExports(target), - scripts: { - build: "tsc", - "check-types": "tsc --noEmit", - }, - dependencies: { - [target]: targetLink, - }, - devDependencies: { - typescript: "^5.9.2", - }, - }), - ); - write( - join(dir, "tsconfig.json"), - json({ - extends: "../../tsconfig.base.json", - compilerOptions: { - outDir: "dist", - rootDir: "src", - }, - include: ["src/**/*.ts"], - }), - ); - write( - join(dir, "src/index.ts"), - `export * from "${target}";\n`, - ); - write( - join(dir, "README.md"), - browserShim - ? `# ${packageName}\n\nCompatibility shim source for \`${target}\`. Browser runtime support is retained but disabled from default CI and publication pending a dedicated security design.\n` - : `# ${packageName}\n\nCompatibility shim for \`${target}\`.\n`, - ); -} - -function rustDependencyName(spec) { - return spec.targetPackage === "agentos-vfs-core" - ? "vfs" - : spec.targetRustIdentifier; -} - -function writeRustShim(root, spec) { - const dir = join(root, crateDirFor(spec.shimPackage)); - const browserShim = spec.shimPackage === "secure-exec-sidecar-browser"; - const targetRel = relative(dir, join(AGENTOS_ROOT, spec.targetPath.replace(/^agentos\//, ""))); - write( - join(dir, "Cargo.toml"), - `[package] -name = "${spec.shimPackage}" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true -description = "${spec.shimPackage} compatibility shim for ${spec.targetPackage}" -${browserShim ? "publish = false" : ""} - -[lib] -name = "${spec.sourceRustIdentifier}" - -[dependencies] -${rustDependencyName(spec)} = { package = "${spec.targetPackage}", path = "${targetRel}", version = "0.0.1" } -`, - ); - write( - join(dir, "src/lib.rs"), - `//! Compatibility shim for \`${spec.targetPackage}\`.\n\npub use ${rustDependencyName(spec)}::*;\n`, - ); -} - -function writeRoot(root, npmShims, rustShims) { - write( - join(root, "package.json"), - json({ - name: "secure-exec-workspace", - private: true, - license: "Apache-2.0", - type: "module", - packageManager: "pnpm@10.13.1", - scripts: { - build: "turbo run build --filter='!@secure-exec/browser'", - "check-types": "turbo run check-types --filter='!@secure-exec/browser'", - test: "pnpm check-types", - }, - devDependencies: { - turbo: "^2.5.6", - typescript: "^5.9.2", - }, - }), - ); - write( - join(root, "pnpm-workspace.yaml"), - "packages:\n - packages/*\n\nonlyBuiltDependencies: []\n", - ); - write( - join(root, "turbo.json"), - json({ - $schema: "https://turbo.build/schema.json", - tasks: { - build: { - dependsOn: ["^build"], - outputs: ["dist/**"], - }, - "check-types": { - dependsOn: ["^check-types"], - outputs: [], - }, - }, - }), - ); - write( - join(root, "tsconfig.base.json"), - json({ - compilerOptions: { - target: "ES2022", - module: "NodeNext", - moduleResolution: "NodeNext", - declaration: true, - strict: true, - skipLibCheck: true, - }, - }), - ); - write( - join(root, "Cargo.toml"), - `[workspace] -resolver = "2" -members = [ -${rustShims.map((spec) => ` "${crateDirFor(spec.shimPackage)}",`).join("\n")} -] - -[workspace.package] -version = "0.0.1" -edition = "2021" -license = "Apache-2.0" -repository = "https://github.com/rivet-dev/secure-exec" -`, - ); - // Compatibility crates and AgentOS ship in lockstep. Seed the mirror from - // the authoritative runtime lock so Cargo cannot independently select a - // newer transitive API for path-linked AgentOS crates. - write(join(root, "Cargo.lock"), readFileSync(join(AGENTOS_ROOT, "Cargo.lock"))); - write( - join(root, "README.md"), - `# secure-exec\n\nCompatibility mirror. Active runtime development moved to the AgentOS runtime packages and crates.\n`, - ); - write( - join(root, "CLAUDE.md"), - "# secure-exec\n\nThis repository is a generated compatibility mirror. Make runtime changes in the AgentOS repository and regenerate the shims.\n", - ); - write( - join(root, "AGENTS.md"), - "# secure-exec\n\nThis repository is a generated compatibility mirror. Make runtime changes in the AgentOS repository and regenerate the shims.\n", - ); - write( - join(root, ".github/workflows/ci.yml"), - `name: CI - -on: - pull_request: - branches: [main] - push: - branches: [main] - -jobs: - static: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: pnpm - - uses: dtolnay/rust-toolchain@stable - - run: pnpm install --frozen-lockfile - - run: pnpm check-types - # Browser compatibility source is retained but disabled until its - # independent reactor/security design is complete. - - run: cargo check --workspace --exclude secure-exec-sidecar-browser -`, - ); - write( - join(root, ".github/workflows/publish.yaml"), - `name: publish - -on: - workflow_dispatch: - inputs: - version: - required: true - type: string - -jobs: - static: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - run: echo "secure-exec publishes generated compatibility shims only." -`, - ); - write( - join(root, ".github/workflows/sync-from-agentos.yml"), - `name: sync-from-agentos - -on: - workflow_dispatch: - -jobs: - static: - runs-on: ubuntu-latest - steps: - - run: echo "Regenerate this mirror from AgentOS with scripts/generate-secure-exec-mirror.mjs." -`, - ); -} - -function main() { - const { mirrorRoot } = parseArgs(process.argv.slice(2)); - const npmShims = map.npmPackages.filter((item) => item.shimPackage); - const rustShims = map.rustCrates.filter((item) => item.shimPackage); - - for (const rel of [ - "crates", - "docker", - "examples", - "packages", - "registry", - "scripts", - ".claude", - ".github/workflows", - ]) { - rmSync(join(mirrorRoot, rel), { recursive: true, force: true }); - } - writeRoot(mirrorRoot, npmShims, rustShims); - for (const spec of npmShims) writeNpmShim(mirrorRoot, spec); - for (const spec of rustShims) writeRustShim(mirrorRoot, spec); - writePnpmLock(mirrorRoot, npmShims); - console.log( - `generated ${npmShims.length} npm shims and ${rustShims.length} Rust shims in ${mirrorRoot}`, - ); -} - -main(); diff --git a/scripts/generate-secure-exec-mirror.test.mjs b/scripts/generate-secure-exec-mirror.test.mjs deleted file mode 100644 index b468d36c87..0000000000 --- a/scripts/generate-secure-exec-mirror.test.mjs +++ /dev/null @@ -1,83 +0,0 @@ -import assert from "node:assert/strict"; -import { execFileSync } from "node:child_process"; -import { - mkdtempSync, - readdirSync, - readFileSync, - rmSync, - statSync, - writeFileSync, -} from "node:fs"; -import { tmpdir } from "node:os"; -import { join, relative, resolve } from "node:path"; -import test from "node:test"; - -const repoRoot = resolve(import.meta.dirname, ".."); -const generator = join(repoRoot, "scripts/generate-secure-exec-mirror.mjs"); - -function generate(root) { - execFileSync(process.execPath, [generator, "--mirror-root", root], { - cwd: repoRoot, - stdio: "pipe", - }); -} - -function snapshot(root) { - const files = new Map(); - const visit = (directory) => { - for (const entry of readdirSync(directory).sort()) { - const path = join(directory, entry); - if (statSync(path).isDirectory()) visit(path); - else files.set(relative(root, path), readFileSync(path)); - } - }; - visit(root); - return files; -} - -test("secure-exec compatibility mirror generation is idempotent", () => { - const root = mkdtempSync(join(tmpdir(), "agentos-secure-exec-mirror-")); - try { - generate(root); - const first = snapshot(root); - writeFileSync(join(root, "packages/stale-generated-file"), "stale"); - generate(root); - assert.deepEqual(snapshot(root), first); - } finally { - rmSync(root, { recursive: true, force: true }); - } -}); - -test("generated browser compatibility shims stay private and excluded", () => { - const root = mkdtempSync(join(tmpdir(), "agentos-secure-exec-browser-")); - try { - generate(root); - const npmBrowser = JSON.parse( - readFileSync(join(root, "packages/browser/package.json"), "utf8"), - ); - assert.equal(npmBrowser.private, true); - const rustBrowser = readFileSync( - join(root, "crates/sidecar-browser/Cargo.toml"), - "utf8", - ); - assert.match(rustBrowser, /^publish = false$/m); - const workflow = readFileSync( - join(root, ".github/workflows/ci.yml"), - "utf8", - ); - assert.match(workflow, /--exclude secure-exec-sidecar-browser/); - const lockfile = readFileSync(join(root, "pnpm-lock.yaml"), "utf8"); - assert.match(lockfile, /^ packages\/browser:$/m); - assert.match( - lockfile, - new RegExp( - npmBrowser.dependencies["@rivet-dev/agentos-runtime-browser"].replace( - /[.*+?^${}()|[\]\\]/g, - "\\$&", - ), - ), - ); - } finally { - rmSync(root, { recursive: true, force: true }); - } -}); diff --git a/scripts/publish/src/lib/artifacts.test.ts b/scripts/publish/src/lib/artifacts.test.ts index 2548f159e5..6a03688199 100644 --- a/scripts/publish/src/lib/artifacts.test.ts +++ b/scripts/publish/src/lib/artifacts.test.ts @@ -18,23 +18,6 @@ test("defaults release artifact paths to agent-os", () => { ); }); -test("supports secure-exec release artifact namespace", () => { - const env = { - RELEASE_ARTIFACT_NAMESPACE: "secure-exec", - RELEASE_REPOSITORY_URL: "https://github.com/rivet-dev/secure-exec", - }; - - assert.equal(releaseArtifactNamespace(env), "secure-exec"); - assert.equal( - releaseArtifactPrefix({ ref: "0.3.0", name: "sidecar" }, env), - "secure-exec/0.3.0/sidecar/", - ); - assert.equal( - releaseUserAgent(env), - "secure-exec-release-publisher (https://github.com/rivet-dev/secure-exec)", - ); -}); - test("rejects invalid release artifact namespaces", () => { assert.throws( () => releaseArtifactNamespace({ RELEASE_ARTIFACT_NAMESPACE: "../agent-os" }), diff --git a/scripts/publish/src/lib/packages.test.ts b/scripts/publish/src/lib/packages.test.ts index 723b3355a4..08d245892d 100644 --- a/scripts/publish/src/lib/packages.test.ts +++ b/scripts/publish/src/lib/packages.test.ts @@ -74,8 +74,12 @@ test("builds platform map for the agent-os sidecar meta package", () => { test("sanity check passes for the agent-os workspace", () => { const packages = discoverPackages(repoRoot); + const names = new Set(packages.map((pkg) => pkg.name)); assert.doesNotThrow(() => assertDiscoverySanity(packages)); + assert(names.has("@rivet-dev/agentos")); + assert(names.has("@rivet-dev/agentos-javascript")); + assert(names.has("@rivet-dev/agentos-python")); }); test("browser migration packages stay explicitly excluded from publication", () => { diff --git a/scripts/publish/src/lib/packages.ts b/scripts/publish/src/lib/packages.ts index edebc0a19d..080c0e2446 100644 --- a/scripts/publish/src/lib/packages.ts +++ b/scripts/publish/src/lib/packages.ts @@ -40,7 +40,6 @@ export const EXCLUDED = new Set([ // unified sidecar reactor/security contract and must not be published. "@rivet-dev/agentos-browser", "@rivet-dev/agentos-runtime-browser", - "secure-exec", "publish", ]); @@ -220,7 +219,10 @@ export function assertDiscoverySanity(packages: Package[]): void { const required: string[] = []; if (hasAgentOsPackages) { required.push( + "@rivet-dev/agentos", "@rivet-dev/agentos-core", + "@rivet-dev/agentos-javascript", + "@rivet-dev/agentos-python", "@rivet-dev/agentos-sidecar", "@rivet-dev/agentos-runtime-sidecar", ); diff --git a/scripts/publish/src/lib/version.test.ts b/scripts/publish/src/lib/version.test.ts index d6fee5b8bf..6d6a4b8f8b 100644 --- a/scripts/publish/src/lib/version.test.ts +++ b/scripts/publish/src/lib/version.test.ts @@ -88,6 +88,8 @@ test("bumpPackageJsons injects sidecar platform optional dependencies", async () for (const [rel, name] of [ ["packages/agentos", "@rivet-dev/agentos"], ["packages/core", "@rivet-dev/agentos-core"], + ["packages/javascript", "@rivet-dev/agentos-javascript"], + ["packages/python", "@rivet-dev/agentos-python"], ["packages/sidecar-binary", "@rivet-dev/agentos-sidecar"], ["packages/runtime-sidecar", "@rivet-dev/agentos-runtime-sidecar"], ...DEFAULT_SIDECAR_PLATFORMS.map((platform) => [ diff --git a/scripts/secure-exec-agentos-map.json b/scripts/secure-exec-agentos-map.json deleted file mode 100644 index f257f17672..0000000000 --- a/scripts/secure-exec-agentos-map.json +++ /dev/null @@ -1,320 +0,0 @@ -{ - "schemaVersion": 1, - "createdAtPst": "2026-07-05", - "purpose": "Authoritative secure-exec -> AgentOS rename and shim map for the AgentOS/secure-exec flip.", - "decisions": { - "namingModel": "option-a-agentos-owned-core", - "typescriptRuntimeCore": "keep-separate", - "secureExecMirror": "generated-npm-and-crates-shims", - "registryOwnership": "agentos", - "envVarPolicy": "hard-rename-secure-exec-to-agentos-no-aliases", - "wireProtocolPolicy": "freeze-on-wire-version-and-namespace-string-values" - }, - "rustCrates": [ - { - "sourcePackage": "secure-exec-sidecar-browser", - "sourceDependencyKey": "secure-exec-sidecar-browser", - "sourceRustIdentifier": "secure_exec_sidecar_browser", - "sourcePath": "secure-exec/crates/sidecar-browser", - "targetPackage": "agentos-native-sidecar-browser", - "targetDependencyKey": "agentos-native-sidecar-browser", - "targetRustIdentifier": "agentos_native_sidecar_browser", - "targetPath": "agentos/crates/native-sidecar-browser", - "collision": "agentos already owns agentos-sidecar-browser", - "shimPackage": "secure-exec-sidecar-browser" - }, - { - "sourcePackage": "secure-exec-sidecar-core", - "sourceDependencyKey": "secure-exec-sidecar-core", - "sourceRustIdentifier": "secure_exec_sidecar_core", - "sourcePath": "secure-exec/crates/sidecar-core", - "targetPackage": "agentos-native-sidecar-core", - "targetDependencyKey": "agentos-native-sidecar-core", - "targetRustIdentifier": "agentos_native_sidecar_core", - "targetPath": "agentos/crates/native-sidecar-core", - "collision": "agentos already owns agentos-sidecar-core", - "shimPackage": "secure-exec-sidecar-core" - }, - { - "sourcePackage": "secure-exec-sidecar-protocol", - "sourceDependencyKey": "secure-exec-sidecar-protocol", - "sourceRustIdentifier": "secure_exec_sidecar_protocol", - "sourcePath": "secure-exec/crates/sidecar-protocol", - "targetPackage": "agentos-sidecar-protocol", - "targetDependencyKey": "agentos-sidecar-protocol", - "targetRustIdentifier": "agentos_sidecar_protocol", - "targetPath": "agentos/crates/sidecar-protocol", - "shimPackage": "secure-exec-sidecar-protocol" - }, - { - "sourcePackage": "secure-exec-sidecar", - "sourceDependencyKey": "secure-exec-sidecar", - "sourceRustIdentifier": "secure_exec_sidecar", - "sourcePath": "secure-exec/crates/sidecar", - "targetPackage": "agentos-native-sidecar", - "targetDependencyKey": "agentos-native-sidecar", - "targetRustIdentifier": "agentos_native_sidecar", - "targetPath": "agentos/crates/native-sidecar", - "collision": "agentos already owns agentos-sidecar", - "shimPackage": "secure-exec-sidecar" - }, - { - "sourcePackage": "secure-exec-client", - "sourceDependencyKey": "secure-exec-client", - "sourceRustIdentifier": "secure_exec_client", - "sourcePath": "secure-exec/crates/secure-exec-client", - "targetPackage": "agentos-sidecar-client", - "targetDependencyKey": "agentos-sidecar-client", - "targetRustIdentifier": "agentos_sidecar_client", - "targetPath": "agentos/crates/sidecar-client", - "collision": "agentos already owns agentos-client", - "shimPackage": "secure-exec-client" - }, - { - "sourcePackage": "secure-exec-build-support", - "sourceDependencyKey": "secure-exec-build-support", - "sourceRustIdentifier": "secure_exec_build_support", - "sourcePath": "secure-exec/crates/build-support", - "targetPackage": "agentos-build-support", - "targetDependencyKey": "agentos-build-support", - "targetRustIdentifier": "agentos_build_support", - "targetPath": "agentos/crates/build-support", - "shimPackage": "secure-exec-build-support" - }, - { - "sourcePackage": "secure-exec-v8-runtime", - "sourceDependencyKey": "secure-exec-v8-runtime", - "sourceRustIdentifier": "secure_exec_v8_runtime", - "sourcePath": "secure-exec/crates/v8-runtime", - "targetPackage": "agentos-v8-runtime", - "targetDependencyKey": "agentos-v8-runtime", - "targetRustIdentifier": "agentos_v8_runtime", - "targetPath": "agentos/crates/v8-runtime", - "shimPackage": "secure-exec-v8-runtime" - }, - { - "sourcePackage": "secure-exec-vm-config", - "sourceDependencyKey": "secure-exec-vm-config", - "sourceRustIdentifier": "secure_exec_vm_config", - "sourcePath": "secure-exec/crates/vm-config", - "targetPackage": "agentos-vm-config", - "targetDependencyKey": "agentos-vm-config", - "targetRustIdentifier": "agentos_vm_config", - "targetPath": "agentos/crates/vm-config", - "shimPackage": "secure-exec-vm-config" - }, - { - "sourcePackage": "secure-exec-execution", - "sourceDependencyKey": "secure-exec-execution", - "sourceRustIdentifier": "secure_exec_execution", - "sourcePath": "secure-exec/crates/execution", - "targetPackage": "agentos-execution", - "targetDependencyKey": "agentos-execution", - "targetRustIdentifier": "agentos_execution", - "targetPath": "agentos/crates/execution", - "shimPackage": "secure-exec-execution" - }, - { - "sourcePackage": "secure-exec-kernel", - "sourceDependencyKey": "secure-exec-kernel", - "sourceRustIdentifier": "secure_exec_kernel", - "sourcePath": "secure-exec/crates/kernel", - "targetPackage": "agentos-kernel", - "targetDependencyKey": "agentos-kernel", - "targetRustIdentifier": "agentos_kernel", - "targetPath": "agentos/crates/kernel", - "shimPackage": "secure-exec-kernel" - }, - { - "sourcePackage": "secure-exec-bridge", - "sourceDependencyKey": "secure-exec-bridge", - "sourceRustIdentifier": "secure_exec_bridge", - "sourcePath": "secure-exec/crates/bridge", - "targetPackage": "agentos-bridge", - "targetDependencyKey": "agentos-bridge", - "targetRustIdentifier": "agentos_bridge", - "targetPath": "agentos/crates/bridge", - "shimPackage": "secure-exec-bridge" - }, - { - "sourcePackage": "secure-exec-vfs-core", - "sourceDependencyKey": "vfs", - "sourceRustIdentifier": "secure_exec_vfs_core", - "sourcePath": "secure-exec/crates/vfs", - "targetPackage": "agentos-vfs-core", - "targetDependencyKey": "agentos-vfs-core", - "targetRustIdentifier": "agentos_vfs_core", - "targetPath": "agentos/crates/vfs", - "shimPackage": "secure-exec-vfs-core" - }, - { - "sourcePackage": "secure-exec-vfs", - "sourceDependencyKey": "secure-exec-vfs", - "sourceRustIdentifier": "secure_exec_vfs", - "sourcePath": "secure-exec/crates/secure-exec-vfs", - "targetPackage": "agentos-vfs", - "targetDependencyKey": "agentos-vfs", - "targetRustIdentifier": "agentos_vfs", - "targetPath": "agentos/crates/vfs-store", - "shimPackage": "secure-exec-vfs" - }, - { - "sourcePackage": "native-baseline", - "sourceDependencyKey": "native-baseline", - "sourceRustIdentifier": "native_baseline", - "sourcePath": "secure-exec/crates/native-baseline", - "targetPackage": "agentos-native-baseline", - "targetDependencyKey": "agentos-native-baseline", - "targetRustIdentifier": "agentos_native_baseline", - "targetPath": "agentos/crates/native-baseline", - "shimPackage": null - } - ], - "npmPackages": [ - { - "sourcePackage": "@secure-exec/core", - "sourcePath": "secure-exec/packages/core", - "targetPackage": "@rivet-dev/agentos-runtime-core", - "targetPath": "agentos/packages/runtime-core", - "shimPackage": "@secure-exec/core" - }, - { - "sourcePackage": "@secure-exec/browser", - "sourcePath": "secure-exec/packages/browser", - "targetPackage": "@rivet-dev/agentos-runtime-browser", - "targetPath": "agentos/packages/runtime-browser", - "shimPackage": "@secure-exec/browser" - }, - { - "sourcePackage": "@secure-exec/sandbox", - "sourcePath": "secure-exec/registry/tool/sandbox", - "targetPackage": "@rivet-dev/agentos-sandbox", - "targetPath": "agentos/packages/agentos-sandbox", - "migration": "fold into existing AgentOS sandbox wrapper package", - "shimPackage": "@secure-exec/sandbox" - }, - { - "sourcePackage": "@secure-exec/sidecar", - "sourcePath": "secure-exec/packages/sidecar", - "targetPackage": "@rivet-dev/agentos-sidecar", - "targetPath": "agentos/packages/sidecar-binary", - "migration": "fold into existing AgentOS native sidecar resolver package", - "shimPackage": "@secure-exec/sidecar" - }, - { - "sourcePackage": "@secure-exec/typescript", - "sourcePath": "secure-exec/packages/typescript", - "targetPackage": "@rivet-dev/agentos-typescript", - "targetPath": "agentos/packages/typescript", - "shimPackage": "@secure-exec/typescript" - }, - { - "sourcePackage": "@secure-exec/build-tools", - "sourcePath": "secure-exec/packages/build-tools", - "targetPackage": "@rivet-dev/agentos-build-tools", - "targetPath": "agentos/packages/build-tools", - "private": true, - "shimPackage": null - }, - { - "sourcePackage": "@secure-exec/benchmarks", - "sourcePath": "secure-exec/packages/benchmarks", - "targetPackage": "@rivet-dev/agentos-benchmarks", - "targetPath": "agentos/packages/benchmarks", - "private": true, - "shimPackage": null - }, - { - "sourcePackage": "secure-exec", - "sourcePath": "secure-exec/packages/secure-exec", - "targetPackage": "@rivet-dev/agentos-runtime-core", - "targetPath": "agentos/packages/runtime-core", - "migration": "facade shim; no AgentOS package named secure-exec", - "shimPackage": "secure-exec" - } - ], - "unchangedNpmPackagesMovingToAgentos": [ - { - "package": "@rivet-dev/agentos-toolchain", - "sourcePath": "secure-exec/packages/agentos-toolchain", - "targetPath": "agentos/packages/agentos-toolchain" - }, - { - "package": "@agentos-software/manifest", - "sourcePath": "secure-exec/packages/manifest", - "targetPath": "agentos/packages/manifest" - }, - { - "packageGlob": "@agentos-software/*", - "sourcePathGlob": "secure-exec/registry/{agent,software}/*", - "targetPathGlob": "agentos/software/*" - } - ], - "scriptedReplacementOrder": [ - ["secure_exec_sidecar_v1", "agentos_sidecar_v1"], - ["secure-exec-sidecar-v1", "agentos-sidecar-v1"], - ["secure_exec_sidecar_browser", "agentos_native_sidecar_browser"], - ["secure-exec-sidecar-browser", "agentos-native-sidecar-browser"], - ["secure_exec_sidecar_core", "agentos_native_sidecar_core"], - ["secure-exec-sidecar-core", "agentos-native-sidecar-core"], - ["secure_exec_sidecar_protocol", "agentos_sidecar_protocol"], - ["secure-exec-sidecar-protocol", "agentos-sidecar-protocol"], - ["secure_exec_sidecar", "agentos_native_sidecar"], - ["secure-exec-sidecar", "agentos-native-sidecar"], - ["secure_exec_client", "agentos_sidecar_client"], - ["secure-exec-client", "agentos-sidecar-client"], - ["secure_exec_build_support", "agentos_build_support"], - ["secure-exec-build-support", "agentos-build-support"], - ["secure_exec_v8_runtime", "agentos_v8_runtime"], - ["secure-exec-v8-runtime", "agentos-v8-runtime"], - ["secure_exec_vm_config", "agentos_vm_config"], - ["secure-exec-vm-config", "agentos-vm-config"], - ["secure_exec_execution", "agentos_execution"], - ["secure-exec-execution", "agentos-execution"], - ["secure_exec_kernel", "agentos_kernel"], - ["secure-exec-kernel", "agentos-kernel"], - ["secure_exec_bridge", "agentos_bridge"], - ["secure-exec-bridge", "agentos-bridge"], - ["secure_exec_vfs_core", "agentos_vfs_core"], - ["secure-exec-vfs-core", "agentos-vfs-core"], - ["secure_exec_vfs", "agentos_vfs"], - ["secure-exec-vfs", "agentos-vfs"], - ["native_baseline", "agentos_native_baseline"], - ["native-baseline", "agentos-native-baseline"], - ["@secure-exec/typescript", "@rivet-dev/agentos-typescript"], - ["@secure-exec/build-tools", "@rivet-dev/agentos-build-tools"], - ["@secure-exec/benchmarks", "@rivet-dev/agentos-benchmarks"], - ["@secure-exec/browser", "@rivet-dev/agentos-runtime-browser"], - ["@secure-exec/sandbox", "@rivet-dev/agentos-sandbox"], - ["@secure-exec/sidecar", "@rivet-dev/agentos-sidecar"], - ["@secure-exec/core", "@rivet-dev/agentos-runtime-core"], - ["SECURE_EXEC_KEEP_STDIN_OPEN", "AGENTOS_KEEP_STDIN_OPEN"], - ["SECURE_EXEC_SIDECAR_BIN", "AGENTOS_SIDECAR_BIN"], - ["SECURE_EXEC_WORKSPACE_PACKAGES", "AGENTOS_WORKSPACE_PACKAGES"], - ["SECURE_EXEC_WASM_COMMANDS_DIR", "AGENTOS_WASM_COMMANDS_DIR"], - ["SECURE_EXEC_C_WASM_COMMANDS_DIR", "AGENTOS_C_WASM_COMMANDS_DIR"], - ["SECURE_EXEC_C_ROOT", "AGENTOS_C_ROOT"], - ["SECURE_EXEC_ROOT", "AGENTOS_ROOT"], - ["SECURE_EXEC_LOCAL_PATH", "AGENTOS_LOCAL_PATH"], - ["SECURE_EXEC_GIT_URL", "AGENTOS_GIT_URL"], - ["SECURE_EXEC_REPO", "AGENTOS_REPO"], - ["SECURE_EXEC_DISPATCH_TOKEN", "AGENTOS_DISPATCH_TOKEN"], - ["SECURE_EXEC_LOG", "AGENTOS_LOG"], - ["SECURE_EXEC_TEST_HTTP_LOOPBACK_REQUEST_TIMEOUT_MS", "AGENTOS_TEST_HTTP_LOOPBACK_REQUEST_TIMEOUT_MS"], - ["__SECURE_EXEC_WASM_SYNC_READ_LIMIT_BYTES__", "__AGENTOS_WASM_SYNC_READ_LIMIT_BYTES__"], - ["__secure_exec_typescript_input__", "__agentos_typescript_input__"], - ["__secure_exec_net_timeout__", "__agentos_net_timeout__"] - ], - "manualLandmines": [ - "Do not mechanically rename string values used as on-wire PROTOCOL_VERSION values.", - "Do not mechanically rename ACP namespace string value dev.rivet.agent-os.acp.", - "Rename secure_exec_pyodide_unavailable to agentos_pyodide_unavailable in both execution/build.rs and python.rs in the same pass.", - "Update build.rs fixture paths after @secure-exec/core moves to packages/runtime-core.", - "Regenerate generated browser bundles instead of hand-editing baked paths.", - "Fold @secure-exec/sandbox into the existing @rivet-dev/agentos-sandbox package instead of creating a duplicate package.", - "Fold @secure-exec/sidecar into the existing @rivet-dev/agentos-sidecar resolver package instead of creating a duplicate package." - ], - "frozenWireStringValues": [ - "dev.rivet.agent-os.acp" - ] -} diff --git a/software/README.md b/software/README.md index d104021d1e..025d699599 100644 --- a/software/README.md +++ b/software/README.md @@ -1,6 +1,6 @@ # agentOS Software Catalog -Software packages for secure-exec VMs: WASM command binaries +Software packages for agentos VMs: WASM command binaries and JavaScript agent adapters live together under `software/*`. Everything in this catalog publishes under the `@agentos-software/*` npm scope. diff --git a/software/browserbase/package.json b/software/browserbase/package.json index fd6512e4b5..d610716e7f 100644 --- a/software/browserbase/package.json +++ b/software/browserbase/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "Browserbase browse CLI for secure-exec VMs (cloud browser automation)", + "description": "Browserbase browse CLI for agentos VMs (cloud browser automation)", "main": "./dist/index.js", "types": "./dist/index.d.ts", "bin": { diff --git a/software/build-essential/package.json b/software/build-essential/package.json index 1ad710650f..e4a24af26c 100644 --- a/software/build-essential/package.json +++ b/software/build-essential/package.json @@ -3,7 +3,7 @@ "version": "0.3.0-rc.2", "type": "module", "license": "Apache-2.0", - "description": "Build-essential WASM command set for secure-exec VMs (common + git + curl)", + "description": "Build-essential WASM command set for agentos VMs (common + git + curl)", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/codex-cli/native/crates/cmd-codex-exec/Cargo.toml b/software/codex-cli/native/crates/cmd-codex-exec/Cargo.toml index eeca177f14..4821e31d5a 100644 --- a/software/codex-cli/native/crates/cmd-codex-exec/Cargo.toml +++ b/software/codex-cli/native/crates/cmd-codex-exec/Cargo.toml @@ -4,14 +4,14 @@ name = "cmd-codex-exec" version.workspace = true edition.workspace = true license.workspace = true -description = "codex-exec command binary for secure-exec VM" +description = "codex-exec command binary for agentos VM" [[bin]] name = "codex-exec" path = "src/main.rs" [dependencies] -wasi-http = { package = "secureexec-wasi-http", path = "../../../../../toolchain/crates/libs/wasi-http" } +wasi-http = { package = "agentos-wasi-http", path = "../../../../../toolchain/crates/libs/wasi-http" } # WASI stub crates for future codex-core dependencies that don't support wasm32-wasip1. codex-network-proxy = "0.0.0" diff --git a/software/codex-cli/native/crates/cmd-codex-exec/src/main.rs b/software/codex-cli/native/crates/cmd-codex-exec/src/main.rs index 47c22f67f3..2f83f8697e 100644 --- a/software/codex-cli/native/crates/cmd-codex-exec/src/main.rs +++ b/software/codex-cli/native/crates/cmd-codex-exec/src/main.rs @@ -1,4 +1,4 @@ -/// Codex headless command for secure-exec VM. +/// Codex headless command for agentos VM. /// /// The prompt mode remains a placeholder command. The ACP session-turn path is /// disabled until it can delegate to the real Codex agent package instead of a diff --git a/software/codex-cli/native/crates/cmd-codex/Cargo.toml b/software/codex-cli/native/crates/cmd-codex/Cargo.toml index cc73f06a16..bcf4b29a2a 100644 --- a/software/codex-cli/native/crates/cmd-codex/Cargo.toml +++ b/software/codex-cli/native/crates/cmd-codex/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-codex" version.workspace = true edition.workspace = true license.workspace = true -description = "codex standalone binary for secure-exec VM" +description = "codex standalone binary for agentos VM" [[bin]] name = "codex" @@ -21,8 +21,8 @@ path = "src/main.rs" # tokio::process::Command in codex-core when compiled for WASI. [dependencies] -wasi-spawn = { package = "secureexec-wasi-spawn", path = "../../../../../toolchain/crates/libs/wasi-spawn" } -wasi-http = { package = "secureexec-wasi-http", path = "../../../../../toolchain/crates/libs/wasi-http" } +wasi-spawn = { package = "agentos-wasi-spawn", path = "../../../../../toolchain/crates/libs/wasi-spawn" } +wasi-http = { package = "agentos-wasi-http", path = "../../../../../toolchain/crates/libs/wasi-http" } # TUI dependencies — ratatui for rendering, crossterm for terminal backend # crossterm is patched for WASI support (see std-patches/crates/crossterm/) diff --git a/software/codex-cli/native/crates/cmd-codex/src/main.rs b/software/codex-cli/native/crates/cmd-codex/src/main.rs index 88f5d80c09..ab7075f943 100644 --- a/software/codex-cli/native/crates/cmd-codex/src/main.rs +++ b/software/codex-cli/native/crates/cmd-codex/src/main.rs @@ -1,4 +1,4 @@ -/// Codex TUI for secure-exec VM. +/// Codex TUI for agentos VM. /// /// Full terminal UI using ratatui + crossterm backend, rendering through /// the WasmVM PTY. This is the interactive entry point — for headless @@ -276,7 +276,7 @@ fn draw_ui(f: &mut Frame, input: &str, messages: &[String], model: Option<&str>) fn print_help() { println!( - "codex {} — interactive Codex TUI for secure-exec VM", + "codex {} — interactive Codex TUI for agentos VM", VERSION ); println!(); diff --git a/software/codex-cli/package.json b/software/codex-cli/package.json index afa587abfa..dc6d5c65f7 100644 --- a/software/codex-cli/package.json +++ b/software/codex-cli/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "OpenAI Codex command package for secure-exec VMs", + "description": "OpenAI Codex command package for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/common/package.json b/software/common/package.json index 8194cac3d7..275b286fa4 100644 --- a/software/common/package.json +++ b/software/common/package.json @@ -3,7 +3,7 @@ "version": "0.3.0-rc.2", "type": "module", "license": "Apache-2.0", - "description": "Common WASM command set for secure-exec VMs (coreutils + sed + grep + gawk + findutils + diffutils + tar + gzip)", + "description": "Common WASM command set for agentos VMs (coreutils + sed + grep + gawk + findutils + diffutils + tar + gzip)", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/coreutils/native/crates/cmd-arch/Cargo.toml b/software/coreutils/native/crates/cmd-arch/Cargo.toml index cb72652e83..a09e7e2213 100644 --- a/software/coreutils/native/crates/cmd-arch/Cargo.toml +++ b/software/coreutils/native/crates/cmd-arch/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-arch" version.workspace = true edition.workspace = true license.workspace = true -description = "arch standalone binary for secure-exec VM" +description = "arch standalone binary for agentos VM" [[bin]] name = "arch" diff --git a/software/coreutils/native/crates/cmd-b2sum/Cargo.toml b/software/coreutils/native/crates/cmd-b2sum/Cargo.toml index 3f5da391a6..38757a3a6e 100644 --- a/software/coreutils/native/crates/cmd-b2sum/Cargo.toml +++ b/software/coreutils/native/crates/cmd-b2sum/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-b2sum" version.workspace = true edition.workspace = true license.workspace = true -description = "b2sum standalone binary for secure-exec VM" +description = "b2sum standalone binary for agentos VM" [[bin]] name = "b2sum" diff --git a/software/coreutils/native/crates/cmd-base32/Cargo.toml b/software/coreutils/native/crates/cmd-base32/Cargo.toml index 29ea0a98a1..2ff6c26071 100644 --- a/software/coreutils/native/crates/cmd-base32/Cargo.toml +++ b/software/coreutils/native/crates/cmd-base32/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-base32" version.workspace = true edition.workspace = true license.workspace = true -description = "base32 standalone binary for secure-exec VM" +description = "base32 standalone binary for agentos VM" [[bin]] name = "base32" diff --git a/software/coreutils/native/crates/cmd-base64/Cargo.toml b/software/coreutils/native/crates/cmd-base64/Cargo.toml index 105af17592..e74c2cddc3 100644 --- a/software/coreutils/native/crates/cmd-base64/Cargo.toml +++ b/software/coreutils/native/crates/cmd-base64/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-base64" version.workspace = true edition.workspace = true license.workspace = true -description = "base64 standalone binary for secure-exec VM" +description = "base64 standalone binary for agentos VM" [[bin]] name = "base64" diff --git a/software/coreutils/native/crates/cmd-basename/Cargo.toml b/software/coreutils/native/crates/cmd-basename/Cargo.toml index aac009cc30..883b847502 100644 --- a/software/coreutils/native/crates/cmd-basename/Cargo.toml +++ b/software/coreutils/native/crates/cmd-basename/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-basename" version.workspace = true edition.workspace = true license.workspace = true -description = "basename standalone binary for secure-exec VM" +description = "basename standalone binary for agentos VM" [[bin]] name = "basename" diff --git a/software/coreutils/native/crates/cmd-basenc/Cargo.toml b/software/coreutils/native/crates/cmd-basenc/Cargo.toml index 9a297f21c0..798a0e5f2d 100644 --- a/software/coreutils/native/crates/cmd-basenc/Cargo.toml +++ b/software/coreutils/native/crates/cmd-basenc/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-basenc" version.workspace = true edition.workspace = true license.workspace = true -description = "basenc standalone binary for secure-exec VM" +description = "basenc standalone binary for agentos VM" [[bin]] name = "basenc" diff --git a/software/coreutils/native/crates/cmd-cat/Cargo.toml b/software/coreutils/native/crates/cmd-cat/Cargo.toml index 747109c7cd..62516e9c63 100644 --- a/software/coreutils/native/crates/cmd-cat/Cargo.toml +++ b/software/coreutils/native/crates/cmd-cat/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-cat" version.workspace = true edition.workspace = true license.workspace = true -description = "cat standalone binary for secure-exec VM" +description = "cat standalone binary for agentos VM" [[bin]] name = "cat" diff --git a/software/coreutils/native/crates/cmd-chmod/Cargo.toml b/software/coreutils/native/crates/cmd-chmod/Cargo.toml index 9e56965b70..97339833a3 100644 --- a/software/coreutils/native/crates/cmd-chmod/Cargo.toml +++ b/software/coreutils/native/crates/cmd-chmod/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-chmod" version.workspace = true edition.workspace = true license.workspace = true -description = "chmod standalone binary for secure-exec VM" +description = "chmod standalone binary for agentos VM" [[bin]] name = "chmod" diff --git a/software/coreutils/native/crates/cmd-cksum/Cargo.toml b/software/coreutils/native/crates/cmd-cksum/Cargo.toml index 25df33cb56..f2e5603a0b 100644 --- a/software/coreutils/native/crates/cmd-cksum/Cargo.toml +++ b/software/coreutils/native/crates/cmd-cksum/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-cksum" version.workspace = true edition.workspace = true license.workspace = true -description = "cksum standalone binary for secure-exec VM" +description = "cksum standalone binary for agentos VM" [[bin]] name = "cksum" diff --git a/software/coreutils/native/crates/cmd-column/Cargo.toml b/software/coreutils/native/crates/cmd-column/Cargo.toml index 8b16759a99..4f65732b71 100644 --- a/software/coreutils/native/crates/cmd-column/Cargo.toml +++ b/software/coreutils/native/crates/cmd-column/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-column" version.workspace = true edition.workspace = true license.workspace = true -description = "column standalone binary for secure-exec VM" +description = "column standalone binary for agentos VM" [[bin]] name = "column" path = "src/main.rs" [dependencies] -secureexec-column = { path = "../column" } +agentos-column = { path = "../column" } diff --git a/software/coreutils/native/crates/cmd-column/src/main.rs b/software/coreutils/native/crates/cmd-column/src/main.rs index d954d4ce4e..5716abc60d 100644 --- a/software/coreutils/native/crates/cmd-column/src/main.rs +++ b/software/coreutils/native/crates/cmd-column/src/main.rs @@ -1,4 +1,4 @@ fn main() { let args: Vec = std::env::args_os().collect(); - std::process::exit(secureexec_column::main(args)); + std::process::exit(agentos_column::main(args)); } diff --git a/software/coreutils/native/crates/cmd-comm/Cargo.toml b/software/coreutils/native/crates/cmd-comm/Cargo.toml index 91e507f0f8..520c47e75d 100644 --- a/software/coreutils/native/crates/cmd-comm/Cargo.toml +++ b/software/coreutils/native/crates/cmd-comm/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-comm" version.workspace = true edition.workspace = true license.workspace = true -description = "comm standalone binary for secure-exec VM" +description = "comm standalone binary for agentos VM" [[bin]] name = "comm" diff --git a/software/coreutils/native/crates/cmd-cp/Cargo.toml b/software/coreutils/native/crates/cmd-cp/Cargo.toml index 56a958c1fe..f4bc3430ca 100644 --- a/software/coreutils/native/crates/cmd-cp/Cargo.toml +++ b/software/coreutils/native/crates/cmd-cp/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-cp" version.workspace = true edition.workspace = true license.workspace = true -description = "cp standalone binary for secure-exec VM" +description = "cp standalone binary for agentos VM" [[bin]] name = "cp" diff --git a/software/coreutils/native/crates/cmd-cut/Cargo.toml b/software/coreutils/native/crates/cmd-cut/Cargo.toml index 72e5a5f814..95ec130aea 100644 --- a/software/coreutils/native/crates/cmd-cut/Cargo.toml +++ b/software/coreutils/native/crates/cmd-cut/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-cut" version.workspace = true edition.workspace = true license.workspace = true -description = "cut standalone binary for secure-exec VM" +description = "cut standalone binary for agentos VM" [[bin]] name = "cut" diff --git a/software/coreutils/native/crates/cmd-date/Cargo.toml b/software/coreutils/native/crates/cmd-date/Cargo.toml index 7b0d440e2f..e22a386416 100644 --- a/software/coreutils/native/crates/cmd-date/Cargo.toml +++ b/software/coreutils/native/crates/cmd-date/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-date" version.workspace = true edition.workspace = true license.workspace = true -description = "date standalone binary for secure-exec VM" +description = "date standalone binary for agentos VM" [[bin]] name = "date" diff --git a/software/coreutils/native/crates/cmd-dd/Cargo.toml b/software/coreutils/native/crates/cmd-dd/Cargo.toml index 744ee8059f..f29c944d4b 100644 --- a/software/coreutils/native/crates/cmd-dd/Cargo.toml +++ b/software/coreutils/native/crates/cmd-dd/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-dd" version.workspace = true edition.workspace = true license.workspace = true -description = "dd standalone binary for secure-exec VM" +description = "dd standalone binary for agentos VM" [[bin]] name = "dd" diff --git a/software/coreutils/native/crates/cmd-dircolors/Cargo.toml b/software/coreutils/native/crates/cmd-dircolors/Cargo.toml index 86c849c392..e45f718047 100644 --- a/software/coreutils/native/crates/cmd-dircolors/Cargo.toml +++ b/software/coreutils/native/crates/cmd-dircolors/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-dircolors" version.workspace = true edition.workspace = true license.workspace = true -description = "dircolors standalone binary for secure-exec VM" +description = "dircolors standalone binary for agentos VM" [[bin]] name = "dircolors" diff --git a/software/coreutils/native/crates/cmd-dirname/Cargo.toml b/software/coreutils/native/crates/cmd-dirname/Cargo.toml index bd1615de6b..5ed37bc89d 100644 --- a/software/coreutils/native/crates/cmd-dirname/Cargo.toml +++ b/software/coreutils/native/crates/cmd-dirname/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-dirname" version.workspace = true edition.workspace = true license.workspace = true -description = "dirname standalone binary for secure-exec VM" +description = "dirname standalone binary for agentos VM" [[bin]] name = "dirname" diff --git a/software/coreutils/native/crates/cmd-du/Cargo.toml b/software/coreutils/native/crates/cmd-du/Cargo.toml index 8a2cc1e9d3..57d2eaa60c 100644 --- a/software/coreutils/native/crates/cmd-du/Cargo.toml +++ b/software/coreutils/native/crates/cmd-du/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-du" version.workspace = true edition.workspace = true license.workspace = true -description = "du standalone binary for secure-exec VM" +description = "du standalone binary for agentos VM" [[bin]] name = "du" path = "src/main.rs" [dependencies] -secureexec-du = { path = "../du" } +agentos-du = { path = "../du" } diff --git a/software/coreutils/native/crates/cmd-du/src/main.rs b/software/coreutils/native/crates/cmd-du/src/main.rs index f76d032ac9..7f28900b38 100644 --- a/software/coreutils/native/crates/cmd-du/src/main.rs +++ b/software/coreutils/native/crates/cmd-du/src/main.rs @@ -1,4 +1,4 @@ fn main() { let args: Vec = std::env::args_os().collect(); - std::process::exit(secureexec_du::main(args)); + std::process::exit(agentos_du::main(args)); } diff --git a/software/coreutils/native/crates/cmd-echo/Cargo.toml b/software/coreutils/native/crates/cmd-echo/Cargo.toml index 2f882f37be..903f379cb3 100644 --- a/software/coreutils/native/crates/cmd-echo/Cargo.toml +++ b/software/coreutils/native/crates/cmd-echo/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-echo" version.workspace = true edition.workspace = true license.workspace = true -description = "echo standalone binary for secure-exec VM" +description = "echo standalone binary for agentos VM" [[bin]] name = "echo" diff --git a/software/coreutils/native/crates/cmd-env/Cargo.toml b/software/coreutils/native/crates/cmd-env/Cargo.toml index 6fe19a8d7d..02e8158936 100644 --- a/software/coreutils/native/crates/cmd-env/Cargo.toml +++ b/software/coreutils/native/crates/cmd-env/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-env" version.workspace = true edition.workspace = true license.workspace = true -description = "env standalone binary for secure-exec VM" +description = "env standalone binary for agentos VM" [[bin]] name = "env" path = "src/main.rs" [dependencies] -shims = { package = "secureexec-shims", path = "../../../../../toolchain/crates/libs/shims" } +shims = { package = "agentos-shims", path = "../../../../../toolchain/crates/libs/shims" } diff --git a/software/coreutils/native/crates/cmd-expand/Cargo.toml b/software/coreutils/native/crates/cmd-expand/Cargo.toml index 08807dc938..ae07f48bdd 100644 --- a/software/coreutils/native/crates/cmd-expand/Cargo.toml +++ b/software/coreutils/native/crates/cmd-expand/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-expand" version.workspace = true edition.workspace = true license.workspace = true -description = "expand standalone binary for secure-exec VM" +description = "expand standalone binary for agentos VM" [[bin]] name = "expand" diff --git a/software/coreutils/native/crates/cmd-expr/Cargo.toml b/software/coreutils/native/crates/cmd-expr/Cargo.toml index 10bd466ba0..7b02e80a76 100644 --- a/software/coreutils/native/crates/cmd-expr/Cargo.toml +++ b/software/coreutils/native/crates/cmd-expr/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-expr" version.workspace = true edition.workspace = true license.workspace = true -description = "expr standalone binary for secure-exec VM" +description = "expr standalone binary for agentos VM" [[bin]] name = "expr" path = "src/main.rs" [dependencies] -secureexec-expr = { path = "../expr" } +agentos-expr = { path = "../expr" } diff --git a/software/coreutils/native/crates/cmd-expr/src/main.rs b/software/coreutils/native/crates/cmd-expr/src/main.rs index 59edddd0f0..725779f2af 100644 --- a/software/coreutils/native/crates/cmd-expr/src/main.rs +++ b/software/coreutils/native/crates/cmd-expr/src/main.rs @@ -1,4 +1,4 @@ fn main() { let args: Vec = std::env::args_os().collect(); - std::process::exit(secureexec_expr::main(args)); + std::process::exit(agentos_expr::main(args)); } diff --git a/software/coreutils/native/crates/cmd-factor/Cargo.toml b/software/coreutils/native/crates/cmd-factor/Cargo.toml index cd4f1f233b..29af5723b2 100644 --- a/software/coreutils/native/crates/cmd-factor/Cargo.toml +++ b/software/coreutils/native/crates/cmd-factor/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-factor" version.workspace = true edition.workspace = true license.workspace = true -description = "factor standalone binary for secure-exec VM" +description = "factor standalone binary for agentos VM" [[bin]] name = "factor" diff --git a/software/coreutils/native/crates/cmd-false/Cargo.toml b/software/coreutils/native/crates/cmd-false/Cargo.toml index a3660c6acf..61738c8871 100644 --- a/software/coreutils/native/crates/cmd-false/Cargo.toml +++ b/software/coreutils/native/crates/cmd-false/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-false" version.workspace = true edition.workspace = true license.workspace = true -description = "false standalone binary for secure-exec VM" +description = "false standalone binary for agentos VM" [[bin]] name = "false" diff --git a/software/coreutils/native/crates/cmd-fmt/Cargo.toml b/software/coreutils/native/crates/cmd-fmt/Cargo.toml index 2ebb0f6f6a..b437da1155 100644 --- a/software/coreutils/native/crates/cmd-fmt/Cargo.toml +++ b/software/coreutils/native/crates/cmd-fmt/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-fmt" version.workspace = true edition.workspace = true license.workspace = true -description = "fmt standalone binary for secure-exec VM" +description = "fmt standalone binary for agentos VM" [[bin]] name = "fmt" diff --git a/software/coreutils/native/crates/cmd-fold/Cargo.toml b/software/coreutils/native/crates/cmd-fold/Cargo.toml index 7d79e95796..cd1ecec2ac 100644 --- a/software/coreutils/native/crates/cmd-fold/Cargo.toml +++ b/software/coreutils/native/crates/cmd-fold/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-fold" version.workspace = true edition.workspace = true license.workspace = true -description = "fold standalone binary for secure-exec VM" +description = "fold standalone binary for agentos VM" [[bin]] name = "fold" diff --git a/software/coreutils/native/crates/cmd-head/Cargo.toml b/software/coreutils/native/crates/cmd-head/Cargo.toml index 287d999055..60cf3e2f5b 100644 --- a/software/coreutils/native/crates/cmd-head/Cargo.toml +++ b/software/coreutils/native/crates/cmd-head/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-head" version.workspace = true edition.workspace = true license.workspace = true -description = "head standalone binary for secure-exec VM" +description = "head standalone binary for agentos VM" [[bin]] name = "head" diff --git a/software/coreutils/native/crates/cmd-join/Cargo.toml b/software/coreutils/native/crates/cmd-join/Cargo.toml index c81c31cb5f..b75d612ba3 100644 --- a/software/coreutils/native/crates/cmd-join/Cargo.toml +++ b/software/coreutils/native/crates/cmd-join/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-join" version.workspace = true edition.workspace = true license.workspace = true -description = "join standalone binary for secure-exec VM" +description = "join standalone binary for agentos VM" [[bin]] name = "join" diff --git a/software/coreutils/native/crates/cmd-link/Cargo.toml b/software/coreutils/native/crates/cmd-link/Cargo.toml index 043488ee6c..6c323e8a10 100644 --- a/software/coreutils/native/crates/cmd-link/Cargo.toml +++ b/software/coreutils/native/crates/cmd-link/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-link" version.workspace = true edition.workspace = true license.workspace = true -description = "link standalone binary for secure-exec VM" +description = "link standalone binary for agentos VM" [[bin]] name = "link" diff --git a/software/coreutils/native/crates/cmd-ln/Cargo.toml b/software/coreutils/native/crates/cmd-ln/Cargo.toml index 92950c1f35..1703688154 100644 --- a/software/coreutils/native/crates/cmd-ln/Cargo.toml +++ b/software/coreutils/native/crates/cmd-ln/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-ln" version.workspace = true edition.workspace = true license.workspace = true -description = "ln standalone binary for secure-exec VM" +description = "ln standalone binary for agentos VM" [[bin]] name = "ln" diff --git a/software/coreutils/native/crates/cmd-logname/Cargo.toml b/software/coreutils/native/crates/cmd-logname/Cargo.toml index 33b6d0d678..51bfc4b5e6 100644 --- a/software/coreutils/native/crates/cmd-logname/Cargo.toml +++ b/software/coreutils/native/crates/cmd-logname/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-logname" version.workspace = true edition.workspace = true license.workspace = true -description = "logname standalone binary for secure-exec VM" +description = "logname standalone binary for agentos VM" [[bin]] name = "logname" diff --git a/software/coreutils/native/crates/cmd-ls/Cargo.toml b/software/coreutils/native/crates/cmd-ls/Cargo.toml index 71ba7a86e8..fedc7cd84f 100644 --- a/software/coreutils/native/crates/cmd-ls/Cargo.toml +++ b/software/coreutils/native/crates/cmd-ls/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-ls" version.workspace = true edition.workspace = true license.workspace = true -description = "ls standalone binary for secure-exec VM" +description = "ls standalone binary for agentos VM" [[bin]] name = "ls" diff --git a/software/coreutils/native/crates/cmd-md5sum/Cargo.toml b/software/coreutils/native/crates/cmd-md5sum/Cargo.toml index ea660b5d0f..5fd7cf4378 100644 --- a/software/coreutils/native/crates/cmd-md5sum/Cargo.toml +++ b/software/coreutils/native/crates/cmd-md5sum/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-md5sum" version.workspace = true edition.workspace = true license.workspace = true -description = "md5sum standalone binary for secure-exec VM" +description = "md5sum standalone binary for agentos VM" [[bin]] name = "md5sum" diff --git a/software/coreutils/native/crates/cmd-mkdir/Cargo.toml b/software/coreutils/native/crates/cmd-mkdir/Cargo.toml index 7a3e9f4ccf..d41b274ae0 100644 --- a/software/coreutils/native/crates/cmd-mkdir/Cargo.toml +++ b/software/coreutils/native/crates/cmd-mkdir/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-mkdir" version.workspace = true edition.workspace = true license.workspace = true -description = "mkdir standalone binary for secure-exec VM" +description = "mkdir standalone binary for agentos VM" [[bin]] name = "mkdir" diff --git a/software/coreutils/native/crates/cmd-mktemp/Cargo.toml b/software/coreutils/native/crates/cmd-mktemp/Cargo.toml index 57935df632..d0afffcbac 100644 --- a/software/coreutils/native/crates/cmd-mktemp/Cargo.toml +++ b/software/coreutils/native/crates/cmd-mktemp/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-mktemp" version.workspace = true edition.workspace = true license.workspace = true -description = "mktemp standalone binary for secure-exec VM" +description = "mktemp standalone binary for agentos VM" [[bin]] name = "mktemp" diff --git a/software/coreutils/native/crates/cmd-mv/Cargo.toml b/software/coreutils/native/crates/cmd-mv/Cargo.toml index c4999d907c..5a10e172d6 100644 --- a/software/coreutils/native/crates/cmd-mv/Cargo.toml +++ b/software/coreutils/native/crates/cmd-mv/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-mv" version.workspace = true edition.workspace = true license.workspace = true -description = "mv standalone binary for secure-exec VM" +description = "mv standalone binary for agentos VM" [[bin]] name = "mv" diff --git a/software/coreutils/native/crates/cmd-nice/Cargo.toml b/software/coreutils/native/crates/cmd-nice/Cargo.toml index 2f77be052e..4786fba39b 100644 --- a/software/coreutils/native/crates/cmd-nice/Cargo.toml +++ b/software/coreutils/native/crates/cmd-nice/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-nice" version.workspace = true edition.workspace = true license.workspace = true -description = "nice standalone binary for secure-exec VM" +description = "nice standalone binary for agentos VM" [[bin]] name = "nice" path = "src/main.rs" [dependencies] -shims = { package = "secureexec-shims", path = "../../../../../toolchain/crates/libs/shims" } +shims = { package = "agentos-shims", path = "../../../../../toolchain/crates/libs/shims" } diff --git a/software/coreutils/native/crates/cmd-nl/Cargo.toml b/software/coreutils/native/crates/cmd-nl/Cargo.toml index 067baf0805..a219c47821 100644 --- a/software/coreutils/native/crates/cmd-nl/Cargo.toml +++ b/software/coreutils/native/crates/cmd-nl/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-nl" version.workspace = true edition.workspace = true license.workspace = true -description = "nl standalone binary for secure-exec VM" +description = "nl standalone binary for agentos VM" [[bin]] name = "nl" diff --git a/software/coreutils/native/crates/cmd-nohup/Cargo.toml b/software/coreutils/native/crates/cmd-nohup/Cargo.toml index 94edf64e00..f2e17edb49 100644 --- a/software/coreutils/native/crates/cmd-nohup/Cargo.toml +++ b/software/coreutils/native/crates/cmd-nohup/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-nohup" version.workspace = true edition.workspace = true license.workspace = true -description = "nohup standalone binary for secure-exec VM" +description = "nohup standalone binary for agentos VM" [[bin]] name = "nohup" path = "src/main.rs" [dependencies] -shims = { package = "secureexec-shims", path = "../../../../../toolchain/crates/libs/shims" } +shims = { package = "agentos-shims", path = "../../../../../toolchain/crates/libs/shims" } diff --git a/software/coreutils/native/crates/cmd-nproc/Cargo.toml b/software/coreutils/native/crates/cmd-nproc/Cargo.toml index 9c6fc0fbbb..865f11112c 100644 --- a/software/coreutils/native/crates/cmd-nproc/Cargo.toml +++ b/software/coreutils/native/crates/cmd-nproc/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-nproc" version.workspace = true edition.workspace = true license.workspace = true -description = "nproc standalone binary for secure-exec VM" +description = "nproc standalone binary for agentos VM" [[bin]] name = "nproc" diff --git a/software/coreutils/native/crates/cmd-numfmt/Cargo.toml b/software/coreutils/native/crates/cmd-numfmt/Cargo.toml index 6ae2fedb83..6cf53f3433 100644 --- a/software/coreutils/native/crates/cmd-numfmt/Cargo.toml +++ b/software/coreutils/native/crates/cmd-numfmt/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-numfmt" version.workspace = true edition.workspace = true license.workspace = true -description = "numfmt standalone binary for secure-exec VM" +description = "numfmt standalone binary for agentos VM" [[bin]] name = "numfmt" diff --git a/software/coreutils/native/crates/cmd-od/Cargo.toml b/software/coreutils/native/crates/cmd-od/Cargo.toml index 102a12cdab..a60719144b 100644 --- a/software/coreutils/native/crates/cmd-od/Cargo.toml +++ b/software/coreutils/native/crates/cmd-od/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-od" version.workspace = true edition.workspace = true license.workspace = true -description = "od standalone binary for secure-exec VM" +description = "od standalone binary for agentos VM" [[bin]] name = "od" diff --git a/software/coreutils/native/crates/cmd-paste/Cargo.toml b/software/coreutils/native/crates/cmd-paste/Cargo.toml index 8e45b33c09..3800c5de53 100644 --- a/software/coreutils/native/crates/cmd-paste/Cargo.toml +++ b/software/coreutils/native/crates/cmd-paste/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-paste" version.workspace = true edition.workspace = true license.workspace = true -description = "paste standalone binary for secure-exec VM" +description = "paste standalone binary for agentos VM" [[bin]] name = "paste" diff --git a/software/coreutils/native/crates/cmd-pathchk/Cargo.toml b/software/coreutils/native/crates/cmd-pathchk/Cargo.toml index b675dc27f1..9db0a38b9b 100644 --- a/software/coreutils/native/crates/cmd-pathchk/Cargo.toml +++ b/software/coreutils/native/crates/cmd-pathchk/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-pathchk" version.workspace = true edition.workspace = true license.workspace = true -description = "pathchk standalone binary for secure-exec VM" +description = "pathchk standalone binary for agentos VM" [[bin]] name = "pathchk" diff --git a/software/coreutils/native/crates/cmd-printenv/Cargo.toml b/software/coreutils/native/crates/cmd-printenv/Cargo.toml index 918b962ea6..5790aebe00 100644 --- a/software/coreutils/native/crates/cmd-printenv/Cargo.toml +++ b/software/coreutils/native/crates/cmd-printenv/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-printenv" version.workspace = true edition.workspace = true license.workspace = true -description = "printenv standalone binary for secure-exec VM" +description = "printenv standalone binary for agentos VM" [[bin]] name = "printenv" diff --git a/software/coreutils/native/crates/cmd-printf/Cargo.toml b/software/coreutils/native/crates/cmd-printf/Cargo.toml index c0512cfd17..a6880a7cef 100644 --- a/software/coreutils/native/crates/cmd-printf/Cargo.toml +++ b/software/coreutils/native/crates/cmd-printf/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-printf" version.workspace = true edition.workspace = true license.workspace = true -description = "printf standalone binary for secure-exec VM" +description = "printf standalone binary for agentos VM" [[bin]] name = "printf" diff --git a/software/coreutils/native/crates/cmd-ptx/Cargo.toml b/software/coreutils/native/crates/cmd-ptx/Cargo.toml index 9eefecd190..e25bcaa743 100644 --- a/software/coreutils/native/crates/cmd-ptx/Cargo.toml +++ b/software/coreutils/native/crates/cmd-ptx/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-ptx" version.workspace = true edition.workspace = true license.workspace = true -description = "ptx standalone binary for secure-exec VM" +description = "ptx standalone binary for agentos VM" [[bin]] name = "ptx" diff --git a/software/coreutils/native/crates/cmd-pwd/Cargo.toml b/software/coreutils/native/crates/cmd-pwd/Cargo.toml index b3e7ff6a74..5536c67f3a 100644 --- a/software/coreutils/native/crates/cmd-pwd/Cargo.toml +++ b/software/coreutils/native/crates/cmd-pwd/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-pwd" version.workspace = true edition.workspace = true license.workspace = true -description = "pwd standalone binary for secure-exec VM" +description = "pwd standalone binary for agentos VM" [[bin]] name = "pwd" diff --git a/software/coreutils/native/crates/cmd-readlink/Cargo.toml b/software/coreutils/native/crates/cmd-readlink/Cargo.toml index 7fa48bbe6f..a040bddcc8 100644 --- a/software/coreutils/native/crates/cmd-readlink/Cargo.toml +++ b/software/coreutils/native/crates/cmd-readlink/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-readlink" version.workspace = true edition.workspace = true license.workspace = true -description = "readlink standalone binary for secure-exec VM" +description = "readlink standalone binary for agentos VM" [[bin]] name = "readlink" diff --git a/software/coreutils/native/crates/cmd-realpath/Cargo.toml b/software/coreutils/native/crates/cmd-realpath/Cargo.toml index dbbbc9da3a..8fc76e6fae 100644 --- a/software/coreutils/native/crates/cmd-realpath/Cargo.toml +++ b/software/coreutils/native/crates/cmd-realpath/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-realpath" version.workspace = true edition.workspace = true license.workspace = true -description = "realpath standalone binary for secure-exec VM" +description = "realpath standalone binary for agentos VM" [[bin]] name = "realpath" diff --git a/software/coreutils/native/crates/cmd-rev/Cargo.toml b/software/coreutils/native/crates/cmd-rev/Cargo.toml index 66a1ffd19d..0716a90d57 100644 --- a/software/coreutils/native/crates/cmd-rev/Cargo.toml +++ b/software/coreutils/native/crates/cmd-rev/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-rev" version.workspace = true edition.workspace = true license.workspace = true -description = "rev standalone binary for secure-exec VM" +description = "rev standalone binary for agentos VM" [[bin]] name = "rev" path = "src/main.rs" [dependencies] -secureexec-rev = { path = "../rev" } +agentos-rev = { path = "../rev" } diff --git a/software/coreutils/native/crates/cmd-rev/src/main.rs b/software/coreutils/native/crates/cmd-rev/src/main.rs index 6841e7d15a..1004a8a78b 100644 --- a/software/coreutils/native/crates/cmd-rev/src/main.rs +++ b/software/coreutils/native/crates/cmd-rev/src/main.rs @@ -1,4 +1,4 @@ fn main() { let args: Vec = std::env::args_os().collect(); - std::process::exit(secureexec_rev::main(args)); + std::process::exit(agentos_rev::main(args)); } diff --git a/software/coreutils/native/crates/cmd-rm/Cargo.toml b/software/coreutils/native/crates/cmd-rm/Cargo.toml index 4a9dc744e7..efc64218a8 100644 --- a/software/coreutils/native/crates/cmd-rm/Cargo.toml +++ b/software/coreutils/native/crates/cmd-rm/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-rm" version.workspace = true edition.workspace = true license.workspace = true -description = "rm standalone binary for secure-exec VM" +description = "rm standalone binary for agentos VM" [[bin]] name = "rm" diff --git a/software/coreutils/native/crates/cmd-rmdir/Cargo.toml b/software/coreutils/native/crates/cmd-rmdir/Cargo.toml index f9729df6f7..f44ec436eb 100644 --- a/software/coreutils/native/crates/cmd-rmdir/Cargo.toml +++ b/software/coreutils/native/crates/cmd-rmdir/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-rmdir" version.workspace = true edition.workspace = true license.workspace = true -description = "rmdir standalone binary for secure-exec VM" +description = "rmdir standalone binary for agentos VM" [[bin]] name = "rmdir" diff --git a/software/coreutils/native/crates/cmd-seq/Cargo.toml b/software/coreutils/native/crates/cmd-seq/Cargo.toml index bb035b54fc..11f10db48a 100644 --- a/software/coreutils/native/crates/cmd-seq/Cargo.toml +++ b/software/coreutils/native/crates/cmd-seq/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-seq" version.workspace = true edition.workspace = true license.workspace = true -description = "seq standalone binary for secure-exec VM" +description = "seq standalone binary for agentos VM" [[bin]] name = "seq" diff --git a/software/coreutils/native/crates/cmd-sh/Cargo.toml b/software/coreutils/native/crates/cmd-sh/Cargo.toml index 51976f0cc4..ce950f492e 100644 --- a/software/coreutils/native/crates/cmd-sh/Cargo.toml +++ b/software/coreutils/native/crates/cmd-sh/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-sh" version.workspace = true edition.workspace = true license.workspace = true -description = "sh standalone binary for secure-exec VM" +description = "sh standalone binary for agentos VM" [[bin]] name = "sh" diff --git a/software/coreutils/native/crates/cmd-sha1sum/Cargo.toml b/software/coreutils/native/crates/cmd-sha1sum/Cargo.toml index 1e4b91ec01..f924733ff5 100644 --- a/software/coreutils/native/crates/cmd-sha1sum/Cargo.toml +++ b/software/coreutils/native/crates/cmd-sha1sum/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-sha1sum" version.workspace = true edition.workspace = true license.workspace = true -description = "sha1sum standalone binary for secure-exec VM" +description = "sha1sum standalone binary for agentos VM" [[bin]] name = "sha1sum" diff --git a/software/coreutils/native/crates/cmd-sha224sum/Cargo.toml b/software/coreutils/native/crates/cmd-sha224sum/Cargo.toml index bdd6f4cb70..d37b29d52b 100644 --- a/software/coreutils/native/crates/cmd-sha224sum/Cargo.toml +++ b/software/coreutils/native/crates/cmd-sha224sum/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-sha224sum" version.workspace = true edition.workspace = true license.workspace = true -description = "sha224sum standalone binary for secure-exec VM" +description = "sha224sum standalone binary for agentos VM" [[bin]] name = "sha224sum" diff --git a/software/coreutils/native/crates/cmd-sha256sum/Cargo.toml b/software/coreutils/native/crates/cmd-sha256sum/Cargo.toml index a43ebd9ff9..15f5b2bef8 100644 --- a/software/coreutils/native/crates/cmd-sha256sum/Cargo.toml +++ b/software/coreutils/native/crates/cmd-sha256sum/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-sha256sum" version.workspace = true edition.workspace = true license.workspace = true -description = "sha256sum standalone binary for secure-exec VM" +description = "sha256sum standalone binary for agentos VM" [[bin]] name = "sha256sum" diff --git a/software/coreutils/native/crates/cmd-sha384sum/Cargo.toml b/software/coreutils/native/crates/cmd-sha384sum/Cargo.toml index 3cec44da9d..50ce3881d2 100644 --- a/software/coreutils/native/crates/cmd-sha384sum/Cargo.toml +++ b/software/coreutils/native/crates/cmd-sha384sum/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-sha384sum" version.workspace = true edition.workspace = true license.workspace = true -description = "sha384sum standalone binary for secure-exec VM" +description = "sha384sum standalone binary for agentos VM" [[bin]] name = "sha384sum" diff --git a/software/coreutils/native/crates/cmd-sha512sum/Cargo.toml b/software/coreutils/native/crates/cmd-sha512sum/Cargo.toml index 0d15e9ac32..ed5c6c2d4a 100644 --- a/software/coreutils/native/crates/cmd-sha512sum/Cargo.toml +++ b/software/coreutils/native/crates/cmd-sha512sum/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-sha512sum" version.workspace = true edition.workspace = true license.workspace = true -description = "sha512sum standalone binary for secure-exec VM" +description = "sha512sum standalone binary for agentos VM" [[bin]] name = "sha512sum" diff --git a/software/coreutils/native/crates/cmd-shred/Cargo.toml b/software/coreutils/native/crates/cmd-shred/Cargo.toml index 15cb98f87c..0daefa2e81 100644 --- a/software/coreutils/native/crates/cmd-shred/Cargo.toml +++ b/software/coreutils/native/crates/cmd-shred/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-shred" version.workspace = true edition.workspace = true license.workspace = true -description = "shred standalone binary for secure-exec VM" +description = "shred standalone binary for agentos VM" [[bin]] name = "shred" diff --git a/software/coreutils/native/crates/cmd-shuf/Cargo.toml b/software/coreutils/native/crates/cmd-shuf/Cargo.toml index 06dc44cf52..49f8570602 100644 --- a/software/coreutils/native/crates/cmd-shuf/Cargo.toml +++ b/software/coreutils/native/crates/cmd-shuf/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-shuf" version.workspace = true edition.workspace = true license.workspace = true -description = "shuf standalone binary for secure-exec VM" +description = "shuf standalone binary for agentos VM" [[bin]] name = "shuf" diff --git a/software/coreutils/native/crates/cmd-sleep/Cargo.toml b/software/coreutils/native/crates/cmd-sleep/Cargo.toml index 816b051b22..3b09388019 100644 --- a/software/coreutils/native/crates/cmd-sleep/Cargo.toml +++ b/software/coreutils/native/crates/cmd-sleep/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-sleep" version.workspace = true edition.workspace = true license.workspace = true -description = "sleep standalone binary for secure-exec VM" +description = "sleep standalone binary for agentos VM" [[bin]] name = "sleep" path = "src/main.rs" [dependencies] -secureexec-builtins = { path = "../../../../../toolchain/crates/libs/builtins" } +agentos-builtins = { path = "../../../../../toolchain/crates/libs/builtins" } diff --git a/software/coreutils/native/crates/cmd-sleep/src/main.rs b/software/coreutils/native/crates/cmd-sleep/src/main.rs index 87f399d995..e471bc9d0a 100644 --- a/software/coreutils/native/crates/cmd-sleep/src/main.rs +++ b/software/coreutils/native/crates/cmd-sleep/src/main.rs @@ -1,4 +1,4 @@ fn main() { let args: Vec = std::env::args_os().collect(); - std::process::exit(secureexec_builtins::sleep(args)); + std::process::exit(agentos_builtins::sleep(args)); } diff --git a/software/coreutils/native/crates/cmd-sort/Cargo.toml b/software/coreutils/native/crates/cmd-sort/Cargo.toml index 0507575a3b..613c9b3757 100644 --- a/software/coreutils/native/crates/cmd-sort/Cargo.toml +++ b/software/coreutils/native/crates/cmd-sort/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-sort" version.workspace = true edition.workspace = true license.workspace = true -description = "sort standalone binary for secure-exec VM" +description = "sort standalone binary for agentos VM" [[bin]] name = "sort" diff --git a/software/coreutils/native/crates/cmd-sort/tests/external_sort.rs b/software/coreutils/native/crates/cmd-sort/tests/external_sort.rs index 50a9fe3261..670a2b9ad9 100644 --- a/software/coreutils/native/crates/cmd-sort/tests/external_sort.rs +++ b/software/coreutils/native/crates/cmd-sort/tests/external_sort.rs @@ -29,7 +29,7 @@ impl Drop for TestDir { } } -// The workspace patches ctrlc to the secure-exec stub, which reports +// The workspace patches ctrlc to the agentos stub, which reports // ErrorKind::Unsupported for signal registration on every target. Forcing an // external-sort spill into a temp directory exercises uu_sort's // ensure_signal_handler_installed soft-skip path. Before the soft skip this diff --git a/software/coreutils/native/crates/cmd-split/Cargo.toml b/software/coreutils/native/crates/cmd-split/Cargo.toml index c0aa7cc026..cfd964daa7 100644 --- a/software/coreutils/native/crates/cmd-split/Cargo.toml +++ b/software/coreutils/native/crates/cmd-split/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-split" version.workspace = true edition.workspace = true license.workspace = true -description = "split standalone binary for secure-exec VM" +description = "split standalone binary for agentos VM" [[bin]] name = "split" diff --git a/software/coreutils/native/crates/cmd-stat/Cargo.toml b/software/coreutils/native/crates/cmd-stat/Cargo.toml index ee741b2614..d270357fa2 100644 --- a/software/coreutils/native/crates/cmd-stat/Cargo.toml +++ b/software/coreutils/native/crates/cmd-stat/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-stat" version.workspace = true edition.workspace = true license.workspace = true -description = "stat standalone binary for secure-exec VM" +description = "stat standalone binary for agentos VM" [[bin]] name = "stat" diff --git a/software/coreutils/native/crates/cmd-stdbuf/Cargo.toml b/software/coreutils/native/crates/cmd-stdbuf/Cargo.toml index 499e2ef4d7..6fa4cda0dd 100644 --- a/software/coreutils/native/crates/cmd-stdbuf/Cargo.toml +++ b/software/coreutils/native/crates/cmd-stdbuf/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-stdbuf" version.workspace = true edition.workspace = true license.workspace = true -description = "stdbuf standalone binary for secure-exec VM" +description = "stdbuf standalone binary for agentos VM" [[bin]] name = "stdbuf" path = "src/main.rs" [dependencies] -shims = { package = "secureexec-shims", path = "../../../../../toolchain/crates/libs/shims" } +shims = { package = "agentos-shims", path = "../../../../../toolchain/crates/libs/shims" } diff --git a/software/coreutils/native/crates/cmd-strings/Cargo.toml b/software/coreutils/native/crates/cmd-strings/Cargo.toml index 4ad7da7c02..2801880dcd 100644 --- a/software/coreutils/native/crates/cmd-strings/Cargo.toml +++ b/software/coreutils/native/crates/cmd-strings/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-strings" version.workspace = true edition.workspace = true license.workspace = true -description = "strings standalone binary for secure-exec VM" +description = "strings standalone binary for agentos VM" [[bin]] name = "strings" path = "src/main.rs" [dependencies] -secureexec-strings-cmd = { path = "../strings-cmd" } +agentos-strings-cmd = { path = "../strings-cmd" } diff --git a/software/coreutils/native/crates/cmd-strings/src/main.rs b/software/coreutils/native/crates/cmd-strings/src/main.rs index 0af588fac5..ec6056d4a4 100644 --- a/software/coreutils/native/crates/cmd-strings/src/main.rs +++ b/software/coreutils/native/crates/cmd-strings/src/main.rs @@ -1,4 +1,4 @@ fn main() { let args: Vec = std::env::args_os().collect(); - std::process::exit(secureexec_strings_cmd::main(args)); + std::process::exit(agentos_strings_cmd::main(args)); } diff --git a/software/coreutils/native/crates/cmd-sum/Cargo.toml b/software/coreutils/native/crates/cmd-sum/Cargo.toml index 5b07f6452b..538bcd6f90 100644 --- a/software/coreutils/native/crates/cmd-sum/Cargo.toml +++ b/software/coreutils/native/crates/cmd-sum/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-sum" version.workspace = true edition.workspace = true license.workspace = true -description = "sum standalone binary for secure-exec VM" +description = "sum standalone binary for agentos VM" [[bin]] name = "sum" diff --git a/software/coreutils/native/crates/cmd-tac/Cargo.toml b/software/coreutils/native/crates/cmd-tac/Cargo.toml index f0069010db..2b4ace4610 100644 --- a/software/coreutils/native/crates/cmd-tac/Cargo.toml +++ b/software/coreutils/native/crates/cmd-tac/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-tac" version.workspace = true edition.workspace = true license.workspace = true -description = "tac standalone binary for secure-exec VM" +description = "tac standalone binary for agentos VM" [[bin]] name = "tac" diff --git a/software/coreutils/native/crates/cmd-tail/Cargo.toml b/software/coreutils/native/crates/cmd-tail/Cargo.toml index 7a14ea0dba..1dac932acd 100644 --- a/software/coreutils/native/crates/cmd-tail/Cargo.toml +++ b/software/coreutils/native/crates/cmd-tail/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-tail" version.workspace = true edition.workspace = true license.workspace = true -description = "tail standalone binary for secure-exec VM" +description = "tail standalone binary for agentos VM" [[bin]] name = "tail" diff --git a/software/coreutils/native/crates/cmd-tee/Cargo.toml b/software/coreutils/native/crates/cmd-tee/Cargo.toml index c94c0664e7..8df5425d54 100644 --- a/software/coreutils/native/crates/cmd-tee/Cargo.toml +++ b/software/coreutils/native/crates/cmd-tee/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-tee" version.workspace = true edition.workspace = true license.workspace = true -description = "tee standalone binary for secure-exec VM" +description = "tee standalone binary for agentos VM" [[bin]] name = "tee" diff --git a/software/coreutils/native/crates/cmd-test/Cargo.toml b/software/coreutils/native/crates/cmd-test/Cargo.toml index 1a1581e18b..3f4b21cf28 100644 --- a/software/coreutils/native/crates/cmd-test/Cargo.toml +++ b/software/coreutils/native/crates/cmd-test/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-test" version.workspace = true edition.workspace = true license.workspace = true -description = "test/[ standalone binary for secure-exec VM" +description = "test/[ standalone binary for agentos VM" [[bin]] name = "test" path = "src/main.rs" [dependencies] -secureexec-builtins = { path = "../../../../../toolchain/crates/libs/builtins" } +agentos-builtins = { path = "../../../../../toolchain/crates/libs/builtins" } diff --git a/software/coreutils/native/crates/cmd-test/src/main.rs b/software/coreutils/native/crates/cmd-test/src/main.rs index 4d6acd26f4..5201cac1bb 100644 --- a/software/coreutils/native/crates/cmd-test/src/main.rs +++ b/software/coreutils/native/crates/cmd-test/src/main.rs @@ -1,4 +1,4 @@ fn main() { let args: Vec = std::env::args_os().collect(); - std::process::exit(secureexec_builtins::test_cmd(args)); + std::process::exit(agentos_builtins::test_cmd(args)); } diff --git a/software/coreutils/native/crates/cmd-timeout/Cargo.toml b/software/coreutils/native/crates/cmd-timeout/Cargo.toml index af6e289f59..8f9577734b 100644 --- a/software/coreutils/native/crates/cmd-timeout/Cargo.toml +++ b/software/coreutils/native/crates/cmd-timeout/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-timeout" version.workspace = true edition.workspace = true license.workspace = true -description = "timeout standalone binary for secure-exec VM" +description = "timeout standalone binary for agentos VM" [[bin]] name = "timeout" path = "src/main.rs" [dependencies] -shims = { package = "secureexec-shims", path = "../../../../../toolchain/crates/libs/shims" } +shims = { package = "agentos-shims", path = "../../../../../toolchain/crates/libs/shims" } diff --git a/software/coreutils/native/crates/cmd-touch/Cargo.toml b/software/coreutils/native/crates/cmd-touch/Cargo.toml index 6353e89c77..e5391a2ccd 100644 --- a/software/coreutils/native/crates/cmd-touch/Cargo.toml +++ b/software/coreutils/native/crates/cmd-touch/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-touch" version.workspace = true edition.workspace = true license.workspace = true -description = "touch standalone binary for secure-exec VM" +description = "touch standalone binary for agentos VM" [[bin]] name = "touch" diff --git a/software/coreutils/native/crates/cmd-tr/Cargo.toml b/software/coreutils/native/crates/cmd-tr/Cargo.toml index c11682cac7..3d8d1437cf 100644 --- a/software/coreutils/native/crates/cmd-tr/Cargo.toml +++ b/software/coreutils/native/crates/cmd-tr/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-tr" version.workspace = true edition.workspace = true license.workspace = true -description = "tr standalone binary for secure-exec VM" +description = "tr standalone binary for agentos VM" [[bin]] name = "tr" diff --git a/software/coreutils/native/crates/cmd-true/Cargo.toml b/software/coreutils/native/crates/cmd-true/Cargo.toml index 9730e7f14f..96401018ca 100644 --- a/software/coreutils/native/crates/cmd-true/Cargo.toml +++ b/software/coreutils/native/crates/cmd-true/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-true" version.workspace = true edition.workspace = true license.workspace = true -description = "true standalone binary for secure-exec VM" +description = "true standalone binary for agentos VM" [[bin]] name = "true" diff --git a/software/coreutils/native/crates/cmd-truncate/Cargo.toml b/software/coreutils/native/crates/cmd-truncate/Cargo.toml index 65fa7e80fd..770cc1ca89 100644 --- a/software/coreutils/native/crates/cmd-truncate/Cargo.toml +++ b/software/coreutils/native/crates/cmd-truncate/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-truncate" version.workspace = true edition.workspace = true license.workspace = true -description = "truncate standalone binary for secure-exec VM" +description = "truncate standalone binary for agentos VM" [[bin]] name = "truncate" diff --git a/software/coreutils/native/crates/cmd-tsort/Cargo.toml b/software/coreutils/native/crates/cmd-tsort/Cargo.toml index b0e057b0c8..07932d1b94 100644 --- a/software/coreutils/native/crates/cmd-tsort/Cargo.toml +++ b/software/coreutils/native/crates/cmd-tsort/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-tsort" version.workspace = true edition.workspace = true license.workspace = true -description = "tsort standalone binary for secure-exec VM" +description = "tsort standalone binary for agentos VM" [[bin]] name = "tsort" diff --git a/software/coreutils/native/crates/cmd-uname/Cargo.toml b/software/coreutils/native/crates/cmd-uname/Cargo.toml index b0f0a8c71a..ee112adbce 100644 --- a/software/coreutils/native/crates/cmd-uname/Cargo.toml +++ b/software/coreutils/native/crates/cmd-uname/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-uname" version.workspace = true edition.workspace = true license.workspace = true -description = "uname standalone binary for secure-exec VM" +description = "uname standalone binary for agentos VM" [[bin]] name = "uname" diff --git a/software/coreutils/native/crates/cmd-unexpand/Cargo.toml b/software/coreutils/native/crates/cmd-unexpand/Cargo.toml index d8a9dc9e78..4e10477987 100644 --- a/software/coreutils/native/crates/cmd-unexpand/Cargo.toml +++ b/software/coreutils/native/crates/cmd-unexpand/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-unexpand" version.workspace = true edition.workspace = true license.workspace = true -description = "unexpand standalone binary for secure-exec VM" +description = "unexpand standalone binary for agentos VM" [[bin]] name = "unexpand" diff --git a/software/coreutils/native/crates/cmd-uniq/Cargo.toml b/software/coreutils/native/crates/cmd-uniq/Cargo.toml index c3ead17984..36c7e5057f 100644 --- a/software/coreutils/native/crates/cmd-uniq/Cargo.toml +++ b/software/coreutils/native/crates/cmd-uniq/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-uniq" version.workspace = true edition.workspace = true license.workspace = true -description = "uniq standalone binary for secure-exec VM" +description = "uniq standalone binary for agentos VM" [[bin]] name = "uniq" diff --git a/software/coreutils/native/crates/cmd-unlink/Cargo.toml b/software/coreutils/native/crates/cmd-unlink/Cargo.toml index eb669d0add..3c2b6b90be 100644 --- a/software/coreutils/native/crates/cmd-unlink/Cargo.toml +++ b/software/coreutils/native/crates/cmd-unlink/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-unlink" version.workspace = true edition.workspace = true license.workspace = true -description = "unlink standalone binary for secure-exec VM" +description = "unlink standalone binary for agentos VM" [[bin]] name = "unlink" diff --git a/software/coreutils/native/crates/cmd-wc/Cargo.toml b/software/coreutils/native/crates/cmd-wc/Cargo.toml index 728b9fdad7..bfeaaf2a7e 100644 --- a/software/coreutils/native/crates/cmd-wc/Cargo.toml +++ b/software/coreutils/native/crates/cmd-wc/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-wc" version.workspace = true edition.workspace = true license.workspace = true -description = "wc standalone binary for secure-exec VM" +description = "wc standalone binary for agentos VM" [[bin]] name = "wc" diff --git a/software/coreutils/native/crates/cmd-which/Cargo.toml b/software/coreutils/native/crates/cmd-which/Cargo.toml index 0ea0f38b94..82370fb0db 100644 --- a/software/coreutils/native/crates/cmd-which/Cargo.toml +++ b/software/coreutils/native/crates/cmd-which/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-which" version.workspace = true edition.workspace = true license.workspace = true -description = "which standalone binary for secure-exec VM" +description = "which standalone binary for agentos VM" [[bin]] name = "which" path = "src/main.rs" [dependencies] -shims = { package = "secureexec-shims", path = "../../../../../toolchain/crates/libs/shims" } +shims = { package = "agentos-shims", path = "../../../../../toolchain/crates/libs/shims" } diff --git a/software/coreutils/native/crates/cmd-which/tests/executable_mode.rs b/software/coreutils/native/crates/cmd-which/tests/executable_mode.rs index 8a51e070e4..a2910222ce 100644 --- a/software/coreutils/native/crates/cmd-which/tests/executable_mode.rs +++ b/software/coreutils/native/crates/cmd-which/tests/executable_mode.rs @@ -13,7 +13,7 @@ struct TempDir { impl TempDir { fn new() -> Self { let unique = format!( - "secure-exec-which-test-{}-{}", + "agentos-which-test-{}-{}", std::process::id(), SystemTime::now() .duration_since(UNIX_EPOCH) diff --git a/software/coreutils/native/crates/cmd-whoami/Cargo.toml b/software/coreutils/native/crates/cmd-whoami/Cargo.toml index 988ca47dae..2aafe988fc 100644 --- a/software/coreutils/native/crates/cmd-whoami/Cargo.toml +++ b/software/coreutils/native/crates/cmd-whoami/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-whoami" version.workspace = true edition.workspace = true license.workspace = true -description = "whoami standalone binary for secure-exec VM" +description = "whoami standalone binary for agentos VM" [[bin]] name = "whoami" path = "src/main.rs" [dependencies] -secureexec-builtins = { path = "../../../../../toolchain/crates/libs/builtins" } +agentos-builtins = { path = "../../../../../toolchain/crates/libs/builtins" } diff --git a/software/coreutils/native/crates/cmd-whoami/src/main.rs b/software/coreutils/native/crates/cmd-whoami/src/main.rs index 6d399bd1bc..1180627184 100644 --- a/software/coreutils/native/crates/cmd-whoami/src/main.rs +++ b/software/coreutils/native/crates/cmd-whoami/src/main.rs @@ -1,4 +1,4 @@ fn main() { let args: Vec = std::env::args_os().collect(); - std::process::exit(secureexec_builtins::whoami(args)); + std::process::exit(agentos_builtins::whoami(args)); } diff --git a/software/coreutils/native/crates/cmd-yes/Cargo.toml b/software/coreutils/native/crates/cmd-yes/Cargo.toml index d32f9d582d..91e69e6c6f 100644 --- a/software/coreutils/native/crates/cmd-yes/Cargo.toml +++ b/software/coreutils/native/crates/cmd-yes/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-yes" version.workspace = true edition.workspace = true license.workspace = true -description = "yes standalone binary for secure-exec VM" +description = "yes standalone binary for agentos VM" [[bin]] name = "yes" diff --git a/software/coreutils/native/crates/column/Cargo.toml b/software/coreutils/native/crates/column/Cargo.toml index 3134b792fd..f1d45753fd 100644 --- a/software/coreutils/native/crates/column/Cargo.toml +++ b/software/coreutils/native/crates/column/Cargo.toml @@ -1,9 +1,9 @@ [package] workspace = "../../../../../toolchain" -name = "secureexec-column" +name = "agentos-column" version.workspace = true edition.workspace = true license.workspace = true -description = "column implementation for secure-exec standalone binaries" +description = "column implementation for agentos standalone binaries" [dependencies] diff --git a/software/coreutils/native/crates/du/Cargo.toml b/software/coreutils/native/crates/du/Cargo.toml index f167b14657..e2f71e46bf 100644 --- a/software/coreutils/native/crates/du/Cargo.toml +++ b/software/coreutils/native/crates/du/Cargo.toml @@ -1,9 +1,9 @@ [package] workspace = "../../../../../toolchain" -name = "secureexec-du" +name = "agentos-du" version.workspace = true edition.workspace = true license.workspace = true -description = "du implementation for secure-exec standalone binaries" +description = "du implementation for agentos standalone binaries" [dependencies] diff --git a/software/coreutils/native/crates/expr/Cargo.toml b/software/coreutils/native/crates/expr/Cargo.toml index e8f413d5c6..c2b49e04dc 100644 --- a/software/coreutils/native/crates/expr/Cargo.toml +++ b/software/coreutils/native/crates/expr/Cargo.toml @@ -1,10 +1,10 @@ [package] workspace = "../../../../../toolchain" -name = "secureexec-expr" +name = "agentos-expr" version.workspace = true edition.workspace = true license.workspace = true -description = "expr implementation for secure-exec standalone binaries" +description = "expr implementation for agentos standalone binaries" [dependencies] regex = "1" diff --git a/software/coreutils/native/crates/rev/Cargo.toml b/software/coreutils/native/crates/rev/Cargo.toml index 3c8a175e92..51f335f5da 100644 --- a/software/coreutils/native/crates/rev/Cargo.toml +++ b/software/coreutils/native/crates/rev/Cargo.toml @@ -1,9 +1,9 @@ [package] workspace = "../../../../../toolchain" -name = "secureexec-rev" +name = "agentos-rev" version.workspace = true edition.workspace = true license.workspace = true -description = "rev implementation for secure-exec standalone binaries" +description = "rev implementation for agentos standalone binaries" [dependencies] diff --git a/software/coreutils/native/crates/strings-cmd/Cargo.toml b/software/coreutils/native/crates/strings-cmd/Cargo.toml index 7c3c023a54..3960298ab2 100644 --- a/software/coreutils/native/crates/strings-cmd/Cargo.toml +++ b/software/coreutils/native/crates/strings-cmd/Cargo.toml @@ -1,9 +1,9 @@ [package] workspace = "../../../../../toolchain" -name = "secureexec-strings-cmd" +name = "agentos-strings-cmd" version.workspace = true edition.workspace = true license.workspace = true -description = "strings command implementation for secure-exec standalone binaries" +description = "strings command implementation for agentos standalone binaries" [dependencies] diff --git a/software/coreutils/package.json b/software/coreutils/package.json index 77e14e7a00..0e2f485859 100644 --- a/software/coreutils/package.json +++ b/software/coreutils/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "GNU coreutils for secure-exec VMs (sh, cat, ls, cp, sort, and 80+ commands)", + "description": "GNU coreutils for agentos VMs (sh, cat, ls, cp, sort, and 80+ commands)", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/curl/native/crates/cmd-curl/Cargo.toml b/software/curl/native/crates/cmd-curl/Cargo.toml index 5a65154842..c73e7d9943 100644 --- a/software/curl/native/crates/cmd-curl/Cargo.toml +++ b/software/curl/native/crates/cmd-curl/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-curl" version.workspace = true edition.workspace = true license.workspace = true -description = "Minimal curl-compatible HTTP client for secure-exec" +description = "Minimal curl-compatible HTTP client for agentos" [[bin]] name = "curl" path = "src/main.rs" [dependencies] -wasi-http = { package = "secureexec-wasi-http", path = "../../../../../toolchain/crates/libs/wasi-http" } +wasi-http = { package = "agentos-wasi-http", path = "../../../../../toolchain/crates/libs/wasi-http" } diff --git a/software/curl/package.json b/software/curl/package.json index db20039ec6..766ece1d85 100644 --- a/software/curl/package.json +++ b/software/curl/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "curl HTTP client for secure-exec VMs", + "description": "curl HTTP client for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/diffutils/native/crates/cmd-diff/Cargo.toml b/software/diffutils/native/crates/cmd-diff/Cargo.toml index 8cdc14cb7d..ddcd868da0 100644 --- a/software/diffutils/native/crates/cmd-diff/Cargo.toml +++ b/software/diffutils/native/crates/cmd-diff/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-diff" version.workspace = true edition.workspace = true license.workspace = true -description = "diff standalone binary for secure-exec VM" +description = "diff standalone binary for agentos VM" [[bin]] name = "diff" path = "src/main.rs" [dependencies] -secureexec-diff = { path = "../diff" } +agentos-diff = { path = "../diff" } diff --git a/software/diffutils/native/crates/cmd-diff/src/main.rs b/software/diffutils/native/crates/cmd-diff/src/main.rs index d892591ad3..6c88983f54 100644 --- a/software/diffutils/native/crates/cmd-diff/src/main.rs +++ b/software/diffutils/native/crates/cmd-diff/src/main.rs @@ -1,4 +1,4 @@ fn main() { let args: Vec = std::env::args_os().collect(); - std::process::exit(secureexec_diff::main(args)); + std::process::exit(agentos_diff::main(args)); } diff --git a/software/diffutils/native/crates/diff/Cargo.toml b/software/diffutils/native/crates/diff/Cargo.toml index 587bcf5968..3bde435b91 100644 --- a/software/diffutils/native/crates/diff/Cargo.toml +++ b/software/diffutils/native/crates/diff/Cargo.toml @@ -1,10 +1,10 @@ [package] workspace = "../../../../../toolchain" -name = "secureexec-diff" +name = "agentos-diff" version.workspace = true edition.workspace = true license.workspace = true -description = "diff implementation for secure-exec standalone binaries" +description = "diff implementation for agentos standalone binaries" [dependencies] similar = "2" diff --git a/software/diffutils/package.json b/software/diffutils/package.json index 2882b62490..c92a052d30 100644 --- a/software/diffutils/package.json +++ b/software/diffutils/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "GNU diffutils for secure-exec VMs (diff)", + "description": "GNU diffutils for agentos VMs (diff)", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/duckdb/package.json b/software/duckdb/package.json index ff3b48a02c..a510aa798f 100644 --- a/software/duckdb/package.json +++ b/software/duckdb/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "DuckDB CLI for secure-exec VMs", + "description": "DuckDB CLI for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/everything/package.json b/software/everything/package.json index 513c40afa7..908a561efc 100644 --- a/software/everything/package.json +++ b/software/everything/package.json @@ -3,7 +3,7 @@ "version": "0.3.0-rc.2", "type": "module", "license": "Apache-2.0", - "description": "All available WASM command packages for secure-exec VMs in a single bundle", + "description": "All available WASM command packages for agentos VMs in a single bundle", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/fd/native/crates/cmd-fd/Cargo.toml b/software/fd/native/crates/cmd-fd/Cargo.toml index ef6223d123..c4550c5955 100644 --- a/software/fd/native/crates/cmd-fd/Cargo.toml +++ b/software/fd/native/crates/cmd-fd/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-fd" version.workspace = true edition.workspace = true license.workspace = true -description = "fd standalone binary for secure-exec VM" +description = "fd standalone binary for agentos VM" publish = false [dependencies] diff --git a/software/fd/package.json b/software/fd/package.json index 9b31f4f20e..8c1df066dc 100644 --- a/software/fd/package.json +++ b/software/fd/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "fd fast file finder for secure-exec VMs", + "description": "fd fast file finder for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/file/native/crates/cmd-file/Cargo.toml b/software/file/native/crates/cmd-file/Cargo.toml index 65e6ee9f90..6384168a9a 100644 --- a/software/file/native/crates/cmd-file/Cargo.toml +++ b/software/file/native/crates/cmd-file/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-file" version.workspace = true edition.workspace = true license.workspace = true -description = "file standalone binary for secure-exec VM" +description = "file standalone binary for agentos VM" [[bin]] name = "file" path = "src/main.rs" [dependencies] -secureexec-file-cmd = { path = "../file-cmd" } +agentos-file-cmd = { path = "../file-cmd" } diff --git a/software/file/native/crates/cmd-file/src/main.rs b/software/file/native/crates/cmd-file/src/main.rs index 75ed52c6c5..a73f816ebf 100644 --- a/software/file/native/crates/cmd-file/src/main.rs +++ b/software/file/native/crates/cmd-file/src/main.rs @@ -1,4 +1,4 @@ fn main() { let args: Vec = std::env::args_os().collect(); - std::process::exit(secureexec_file_cmd::main(args)); + std::process::exit(agentos_file_cmd::main(args)); } diff --git a/software/file/native/crates/file-cmd/Cargo.toml b/software/file/native/crates/file-cmd/Cargo.toml index 203113931a..c058ac7ab1 100644 --- a/software/file/native/crates/file-cmd/Cargo.toml +++ b/software/file/native/crates/file-cmd/Cargo.toml @@ -1,10 +1,10 @@ [package] workspace = "../../../../../toolchain" -name = "secureexec-file-cmd" +name = "agentos-file-cmd" version.workspace = true edition.workspace = true license.workspace = true -description = "file command implementation for secure-exec standalone binaries" +description = "file command implementation for agentos standalone binaries" [dependencies] infer = "0.16" diff --git a/software/file/package.json b/software/file/package.json index e3ab79cf8a..30511e0241 100644 --- a/software/file/package.json +++ b/software/file/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "file type detection for secure-exec VMs", + "description": "file type detection for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/findutils/native/crates/cmd-find/Cargo.toml b/software/findutils/native/crates/cmd-find/Cargo.toml index 19cc8f38f4..57bd3955da 100644 --- a/software/findutils/native/crates/cmd-find/Cargo.toml +++ b/software/findutils/native/crates/cmd-find/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-find" version.workspace = true edition.workspace = true license.workspace = true -description = "find standalone binary for secure-exec VM" +description = "find standalone binary for agentos VM" [[bin]] name = "find" diff --git a/software/findutils/native/crates/cmd-xargs/Cargo.toml b/software/findutils/native/crates/cmd-xargs/Cargo.toml index 1037c80a58..9feb70ce20 100644 --- a/software/findutils/native/crates/cmd-xargs/Cargo.toml +++ b/software/findutils/native/crates/cmd-xargs/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-xargs" version.workspace = true edition.workspace = true license.workspace = true -description = "xargs standalone binary for secure-exec VM" +description = "xargs standalone binary for agentos VM" [[bin]] name = "xargs" diff --git a/software/findutils/package.json b/software/findutils/package.json index a877bd859d..78e158a99e 100644 --- a/software/findutils/package.json +++ b/software/findutils/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "GNU findutils for secure-exec VMs (find, xargs)", + "description": "GNU findutils for agentos VMs (find, xargs)", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/gawk/native/crates/awk/Cargo.toml b/software/gawk/native/crates/awk/Cargo.toml index c630320b93..3ed5dae9df 100644 --- a/software/gawk/native/crates/awk/Cargo.toml +++ b/software/gawk/native/crates/awk/Cargo.toml @@ -1,10 +1,10 @@ [package] workspace = "../../../../../toolchain" -name = "secureexec-awk" +name = "agentos-awk" version.workspace = true edition.workspace = true license.workspace = true -description = "awk implementation for secure-exec standalone binaries" +description = "awk implementation for agentos standalone binaries" [dependencies] awk-rs = "0.1.0" diff --git a/software/gawk/native/crates/cmd-awk/Cargo.toml b/software/gawk/native/crates/cmd-awk/Cargo.toml index af4cd03274..a8e848c71b 100644 --- a/software/gawk/native/crates/cmd-awk/Cargo.toml +++ b/software/gawk/native/crates/cmd-awk/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-awk" version.workspace = true edition.workspace = true license.workspace = true -description = "awk standalone binary for secure-exec VM" +description = "awk standalone binary for agentos VM" [[bin]] name = "awk" path = "src/main.rs" [dependencies] -secureexec-awk = { path = "../awk" } +agentos-awk = { path = "../awk" } diff --git a/software/gawk/native/crates/cmd-awk/src/main.rs b/software/gawk/native/crates/cmd-awk/src/main.rs index a80b7ea9ad..4542ac8005 100644 --- a/software/gawk/native/crates/cmd-awk/src/main.rs +++ b/software/gawk/native/crates/cmd-awk/src/main.rs @@ -2,7 +2,7 @@ fn main() { use std::io::Write; let args: Vec = std::env::args_os().collect(); - let mut code = secureexec_awk::main(args); + let mut code = agentos_awk::main(args); if let Err(error) = std::io::stdout().flush() { eprintln!("Error flushing stdout: {error}"); if code == 0 { diff --git a/software/gawk/package.json b/software/gawk/package.json index ed40a09c97..8ff4fbaa7c 100644 --- a/software/gawk/package.json +++ b/software/gawk/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "GNU awk text processing for secure-exec VMs", + "description": "GNU awk text processing for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/git/test/git.nightly.test.ts b/software/git/test/git.nightly.test.ts index c42be71d67..9f9a31e429 100644 --- a/software/git/test/git.nightly.test.ts +++ b/software/git/test/git.nightly.test.ts @@ -712,7 +712,7 @@ describeIf(hasGit, 'git command', () => { runHostGit(['-C', worktree, 'add', 'README.md']); runHostGit([ '-C', worktree, - '-c', 'user.name=secure-exec', '-c', 'user.email=agent@example.com', + '-c', 'user.name=agentos', '-c', 'user.email=agent@example.com', 'commit', '-m', 'seed', ]); runHostGit(['-C', worktree, 'checkout', '-b', 'feature/deep']); @@ -720,7 +720,7 @@ describeIf(hasGit, 'git command', () => { runHostGit(['-C', worktree, 'add', 'feature.txt']); runHostGit([ '-C', worktree, - '-c', 'user.name=secure-exec', '-c', 'user.email=agent@example.com', + '-c', 'user.name=agentos', '-c', 'user.email=agent@example.com', 'commit', '-m', 'feature branch', ]); runHostGit(['-C', worktree, 'checkout', 'main']); diff --git a/software/grep/native/crates/cmd-egrep/Cargo.toml b/software/grep/native/crates/cmd-egrep/Cargo.toml index b3eb4c9222..dc166edd22 100644 --- a/software/grep/native/crates/cmd-egrep/Cargo.toml +++ b/software/grep/native/crates/cmd-egrep/Cargo.toml @@ -11,4 +11,4 @@ name = "egrep" path = "src/main.rs" [dependencies] -shims = { package = "secureexec-shims", path = "../../../../../toolchain/crates/libs/shims" } +shims = { package = "agentos-shims", path = "../../../../../toolchain/crates/libs/shims" } diff --git a/software/grep/native/crates/cmd-fgrep/Cargo.toml b/software/grep/native/crates/cmd-fgrep/Cargo.toml index 573b6b100e..9b6b4b3f07 100644 --- a/software/grep/native/crates/cmd-fgrep/Cargo.toml +++ b/software/grep/native/crates/cmd-fgrep/Cargo.toml @@ -11,4 +11,4 @@ name = "fgrep" path = "src/main.rs" [dependencies] -shims = { package = "secureexec-shims", path = "../../../../../toolchain/crates/libs/shims" } +shims = { package = "agentos-shims", path = "../../../../../toolchain/crates/libs/shims" } diff --git a/software/grep/package.json b/software/grep/package.json index 9953786470..f75391e73a 100644 --- a/software/grep/package.json +++ b/software/grep/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "GNU grep pattern matching for secure-exec VMs (grep, egrep, fgrep)", + "description": "GNU grep pattern matching for agentos VMs (grep, egrep, fgrep)", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/gzip/native/crates/cmd-gzip/Cargo.toml b/software/gzip/native/crates/cmd-gzip/Cargo.toml index dafe689b6d..1ec90fc294 100644 --- a/software/gzip/native/crates/cmd-gzip/Cargo.toml +++ b/software/gzip/native/crates/cmd-gzip/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-gzip" version.workspace = true edition.workspace = true license.workspace = true -description = "gzip standalone binary for secure-exec VM" +description = "gzip standalone binary for agentos VM" [[bin]] name = "gzip" path = "src/main.rs" [dependencies] -secureexec-gzip = { path = "../gzip" } +agentos-gzip = { path = "../gzip" } diff --git a/software/gzip/native/crates/cmd-gzip/src/main.rs b/software/gzip/native/crates/cmd-gzip/src/main.rs index 59581509ad..c32fbf080e 100644 --- a/software/gzip/native/crates/cmd-gzip/src/main.rs +++ b/software/gzip/native/crates/cmd-gzip/src/main.rs @@ -1,4 +1,4 @@ fn main() { let args: Vec = std::env::args_os().collect(); - std::process::exit(secureexec_gzip::main(args)); + std::process::exit(agentos_gzip::main(args)); } diff --git a/software/gzip/native/crates/gzip/Cargo.toml b/software/gzip/native/crates/gzip/Cargo.toml index 897731b6d4..f3d4a6df8b 100644 --- a/software/gzip/native/crates/gzip/Cargo.toml +++ b/software/gzip/native/crates/gzip/Cargo.toml @@ -1,10 +1,10 @@ [package] workspace = "../../../../../toolchain" -name = "secureexec-gzip" +name = "agentos-gzip" version.workspace = true edition.workspace = true license.workspace = true -description = "gzip/gunzip/zcat implementations for secure-exec standalone binaries" +description = "gzip/gunzip/zcat implementations for agentos standalone binaries" [dependencies] flate2 = { version = "1.0", default-features = false, features = ["rust_backend"] } diff --git a/software/gzip/package.json b/software/gzip/package.json index 5bf04cc052..317b715754 100644 --- a/software/gzip/package.json +++ b/software/gzip/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "GNU gzip compression for secure-exec VMs (gzip, gunzip, zcat)", + "description": "GNU gzip compression for agentos VMs (gzip, gunzip, zcat)", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/jq/native/crates/cmd-jq/Cargo.toml b/software/jq/native/crates/cmd-jq/Cargo.toml index bdc904ce10..7953ef04e1 100644 --- a/software/jq/native/crates/cmd-jq/Cargo.toml +++ b/software/jq/native/crates/cmd-jq/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-jq" version.workspace = true edition.workspace = true license.workspace = true -description = "jq standalone binary for secure-exec VM" +description = "jq standalone binary for agentos VM" [[bin]] name = "jq" path = "src/main.rs" [dependencies] -secureexec-jq = { path = "../jq" } +agentos-jq = { path = "../jq" } diff --git a/software/jq/native/crates/cmd-jq/src/main.rs b/software/jq/native/crates/cmd-jq/src/main.rs index b99f91eb3b..df83960ada 100644 --- a/software/jq/native/crates/cmd-jq/src/main.rs +++ b/software/jq/native/crates/cmd-jq/src/main.rs @@ -2,7 +2,7 @@ fn main() { use std::io::Write; let args: Vec = std::env::args_os().collect(); - let mut code = secureexec_jq::main(args); + let mut code = agentos_jq::main(args); if let Err(error) = std::io::stdout().flush() { eprintln!("Error flushing stdout: {error}"); if code == 0 { diff --git a/software/jq/native/crates/jq/Cargo.toml b/software/jq/native/crates/jq/Cargo.toml index 6a86e30e47..f991a42280 100644 --- a/software/jq/native/crates/jq/Cargo.toml +++ b/software/jq/native/crates/jq/Cargo.toml @@ -1,10 +1,10 @@ [package] workspace = "../../../../../toolchain" -name = "secureexec-jq" +name = "agentos-jq" version.workspace = true edition.workspace = true license.workspace = true -description = "jq implementation for secure-exec standalone binaries" +description = "jq implementation for agentos standalone binaries" [dependencies] jaq-core = "2.2" diff --git a/software/jq/package.json b/software/jq/package.json index 26d3c971ec..34c7e15d58 100644 --- a/software/jq/package.json +++ b/software/jq/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "jq JSON processor for secure-exec VMs", + "description": "jq JSON processor for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/ripgrep/native/crates/cmd-rg/Cargo.toml b/software/ripgrep/native/crates/cmd-rg/Cargo.toml index f6184892c7..3824b95025 100644 --- a/software/ripgrep/native/crates/cmd-rg/Cargo.toml +++ b/software/ripgrep/native/crates/cmd-rg/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-rg" version.workspace = true edition.workspace = true license.workspace = true -description = "rg (ripgrep) standalone binary for secure-exec VM" +description = "rg (ripgrep) standalone binary for agentos VM" publish = false [dependencies] diff --git a/software/ripgrep/package.json b/software/ripgrep/package.json index 6179903927..1de009f1ae 100644 --- a/software/ripgrep/package.json +++ b/software/ripgrep/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "ripgrep fast search for secure-exec VMs", + "description": "ripgrep fast search for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/ripgrep/test/ripgrep.nightly.test.ts b/software/ripgrep/test/ripgrep.nightly.test.ts index aac5a9f08f..9bab2d3217 100644 --- a/software/ripgrep/test/ripgrep.nightly.test.ts +++ b/software/ripgrep/test/ripgrep.nightly.test.ts @@ -71,7 +71,7 @@ describeIf(hasWasmBinaries, "ripgrep command", { timeout: 10_000 }, () => { const result = await kernel.exec("rg --version", {}); expect(result.stdout).toContain("ripgrep 15.1.0"); - expect(result.stdout).not.toContain("secure-exec"); + expect(result.stdout).not.toContain("agentos"); }); it("searches recursively and respects .gitignore by default", async () => { diff --git a/software/sed/native/crates/cmd-sed/Cargo.toml b/software/sed/native/crates/cmd-sed/Cargo.toml index 990de688db..5b82328c9b 100644 --- a/software/sed/native/crates/cmd-sed/Cargo.toml +++ b/software/sed/native/crates/cmd-sed/Cargo.toml @@ -4,7 +4,7 @@ name = "cmd-sed" version.workspace = true edition.workspace = true license.workspace = true -description = "sed standalone binary for secure-exec VM" +description = "sed standalone binary for agentos VM" [[bin]] name = "sed" diff --git a/software/sed/package.json b/software/sed/package.json index f1e1e364c4..59a2b5e2cb 100644 --- a/software/sed/package.json +++ b/software/sed/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "GNU sed stream editor for secure-exec VMs", + "description": "GNU sed stream editor for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/sqlite3/package.json b/software/sqlite3/package.json index bbbfd350fc..e7631d659c 100644 --- a/software/sqlite3/package.json +++ b/software/sqlite3/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "SQLite3 CLI for secure-exec VMs", + "description": "SQLite3 CLI for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/tar/native/crates/cmd-tar/Cargo.toml b/software/tar/native/crates/cmd-tar/Cargo.toml index a5ca3d0b4b..880003f2ca 100644 --- a/software/tar/native/crates/cmd-tar/Cargo.toml +++ b/software/tar/native/crates/cmd-tar/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-tar" version.workspace = true edition.workspace = true license.workspace = true -description = "tar standalone binary for secure-exec VM" +description = "tar standalone binary for agentos VM" [[bin]] name = "tar" path = "src/main.rs" [dependencies] -secureexec-tar = { path = "../tar" } +agentos-tar = { path = "../tar" } diff --git a/software/tar/native/crates/cmd-tar/src/main.rs b/software/tar/native/crates/cmd-tar/src/main.rs index 304ba12711..5545be73a1 100644 --- a/software/tar/native/crates/cmd-tar/src/main.rs +++ b/software/tar/native/crates/cmd-tar/src/main.rs @@ -1,4 +1,4 @@ fn main() { let args: Vec = std::env::args_os().collect(); - std::process::exit(secureexec_tar::main(args)); + std::process::exit(agentos_tar::main(args)); } diff --git a/software/tar/native/crates/tar/Cargo.toml b/software/tar/native/crates/tar/Cargo.toml index f10f711e29..effe8c4c6b 100644 --- a/software/tar/native/crates/tar/Cargo.toml +++ b/software/tar/native/crates/tar/Cargo.toml @@ -1,10 +1,10 @@ [package] workspace = "../../../../../toolchain" -name = "secureexec-tar" +name = "agentos-tar" version.workspace = true edition.workspace = true license.workspace = true -description = "tar implementation for secure-exec standalone binaries" +description = "tar implementation for agentos standalone binaries" [dependencies] flate2 = { version = "1.0", default-features = false, features = ["rust_backend"] } diff --git a/software/tar/package.json b/software/tar/package.json index 916fe4f32f..eb296ac842 100644 --- a/software/tar/package.json +++ b/software/tar/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "GNU tar archiver for secure-exec VMs", + "description": "GNU tar archiver for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/tree/package.json b/software/tree/package.json index 851e452e08..498ea7c83f 100644 --- a/software/tree/package.json +++ b/software/tree/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "GPL-2.0-or-later", - "description": "tree directory listing for secure-exec VMs", + "description": "tree directory listing for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/unzip/package.json b/software/unzip/package.json index 38b24d6d5c..3277b5708a 100644 --- a/software/unzip/package.json +++ b/software/unzip/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "unzip archive extraction for secure-exec VMs", + "description": "unzip archive extraction for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/vim/native/c/vim-bridge/compat.h b/software/vim/native/c/vim-bridge/compat.h index 05477f040a..cd28a6e3b4 100644 --- a/software/vim/native/c/vim-bridge/compat.h +++ b/software/vim/native/c/vim-bridge/compat.h @@ -1,7 +1,7 @@ /* compat.h — forced-include shim filling full-OS libc gaps for porting - * Linux C programs (vim) to the secure-exec wasm runtime. */ -#ifndef _SECUREEXEC_WASM_COMPAT_H -#define _SECUREEXEC_WASM_COMPAT_H + * Linux C programs (vim) to the agentos wasm runtime. */ +#ifndef _AGENTOS_WASM_COMPAT_H +#define _AGENTOS_WASM_COMPAT_H #include #include #include diff --git a/software/vim/native/c/vim-bridge/termios.h b/software/vim/native/c/vim-bridge/termios.h index e7f777081d..3d8897dcf2 100644 --- a/software/vim/native/c/vim-bridge/termios.h +++ b/software/vim/native/c/vim-bridge/termios.h @@ -1,4 +1,4 @@ -/* termios.h — POSIX termios for the secure-exec full-OS libc (musl layout). +/* termios.h — POSIX termios for the agentos full-OS libc (musl layout). * * wasi-libc ships no termios; this header pairs with termios_bridge.c, which * implements the functions on top of the `host_tty` kernel imports (raw-mode diff --git a/software/vim/native/c/vim-bridge/termios_bridge.c b/software/vim/native/c/vim-bridge/termios_bridge.c index 987fa1a05a..bf1a9dd6e9 100644 --- a/software/vim/native/c/vim-bridge/termios_bridge.c +++ b/software/vim/native/c/vim-bridge/termios_bridge.c @@ -1,4 +1,4 @@ -/* termios_bridge.c — termios for the secure-exec full-OS libc. +/* termios_bridge.c — termios for the agentos full-OS libc. * * The kernel owns the PTY line discipline (canonical/echo/signals) and exposes * a raw-mode toggle + window size to guests via the `host_tty` WASM imports. diff --git a/software/vim/package.json b/software/vim/package.json index 6c7ee85c78..48752ac884 100644 --- a/software/vim/package.json +++ b/software/vim/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "vim for secure-exec VMs (wasm build + bundled runtime via provides)", + "description": "vim for agentos VMs (wasm build + bundled runtime via provides)", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/yq/native/crates/cmd-yq/Cargo.toml b/software/yq/native/crates/cmd-yq/Cargo.toml index dfa19879f9..06ccd3b1cd 100644 --- a/software/yq/native/crates/cmd-yq/Cargo.toml +++ b/software/yq/native/crates/cmd-yq/Cargo.toml @@ -4,11 +4,11 @@ name = "cmd-yq" version.workspace = true edition.workspace = true license.workspace = true -description = "yq standalone binary for secure-exec VM" +description = "yq standalone binary for agentos VM" [[bin]] name = "yq" path = "src/main.rs" [dependencies] -secureexec-yq = { path = "../yq" } +agentos-yq = { path = "../yq" } diff --git a/software/yq/native/crates/cmd-yq/src/main.rs b/software/yq/native/crates/cmd-yq/src/main.rs index b2e37359de..6a7f52bc9d 100644 --- a/software/yq/native/crates/cmd-yq/src/main.rs +++ b/software/yq/native/crates/cmd-yq/src/main.rs @@ -1,4 +1,4 @@ fn main() { let args: Vec = std::env::args_os().collect(); - std::process::exit(secureexec_yq::main(args)); + std::process::exit(agentos_yq::main(args)); } diff --git a/software/yq/native/crates/yq/Cargo.toml b/software/yq/native/crates/yq/Cargo.toml index 5712402a21..15ac1ee193 100644 --- a/software/yq/native/crates/yq/Cargo.toml +++ b/software/yq/native/crates/yq/Cargo.toml @@ -1,10 +1,10 @@ [package] workspace = "../../../../../toolchain" -name = "secureexec-yq" +name = "agentos-yq" version.workspace = true edition.workspace = true license.workspace = true -description = "yq (YAML/XML/TOML/JSON processor) implementation for secure-exec standalone binaries" +description = "yq (YAML/XML/TOML/JSON processor) implementation for agentos standalone binaries" [dependencies] jaq-core = "2.2" diff --git a/software/yq/package.json b/software/yq/package.json index 923fe10bcb..b1bb175f33 100644 --- a/software/yq/package.json +++ b/software/yq/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "yq YAML/JSON processor for secure-exec VMs", + "description": "yq YAML/JSON processor for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/software/zip/package.json b/software/zip/package.json index 0367d1268b..786007476c 100644 --- a/software/zip/package.json +++ b/software/zip/package.json @@ -3,7 +3,7 @@ "version": "0.3.3", "type": "module", "license": "Apache-2.0", - "description": "zip archive creation for secure-exec VMs", + "description": "zip archive creation for agentos VMs", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/test-harness/src/agent-os-conformance.ts b/test-harness/src/agent-os-conformance.ts index c3dd250c6e..21c8cae5b2 100644 --- a/test-harness/src/agent-os-conformance.ts +++ b/test-harness/src/agent-os-conformance.ts @@ -7,59 +7,86 @@ export { } from "./agent-os-conformance-fixture.js"; export const AGENT_OS_CONFORMANCE_ACTIONS = [ - "readFile", - "writeFile", - "readFiles", - "writeFiles", - "stat", - "mkdir", - "readdir", - "readdirEntries", - "readdirRecursive", - "exists", - "move", - "remove", - "exec", - "execArgv", - "spawn", - "waitProcess", - "killProcess", - "stopProcess", - "listProcesses", - "allProcesses", - "processTree", - "getProcess", - "writeProcessStdin", - "closeProcessStdin", - "openShell", - "writeShell", - "resizeShell", - "closeShell", - "waitShell", - "httpRequest", - "scheduleCron", - "listCronJobs", - "cancelCronJob", - "listAgents", - "listMounts", - "listSoftware", - "exportRootFilesystem", - "mountFs", - "unmountFs", - "linkSoftware", - "openSession", - "getSession", - "prompt", - "cancelPrompt", - "unloadSession", - "deleteSession", - "respondPermission", - "listSessions", - "readHistory", - "getSessionConfig", - "setSessionConfigOption", - "getSessionCapabilities", - "getSessionAgentInfo", + "process.exec", + "process.execFile", + "process.spawn", + "process.get", + "process.list", + "process.listAll", + "process.tree", + "process.wait", + "process.stop", + "process.kill", + "process.writeStdin", + "process.closeStdin", + "javascript.execute", + "javascript.evaluate", + "javascript.executeFile", + "javascript.typescript.execute", + "javascript.typescript.evaluate", + "javascript.typescript.executeFile", + "javascript.typescript.check", + "javascript.typescript.checkProject", + "javascript.npm.install", + "javascript.npm.runScript", + "javascript.npm.runPackage", + "python.execute", + "python.evaluate", + "python.executeFile", + "python.executeModule", + "python.install", + "executions.get", + "executions.list", + "executions.wait", + "executions.cancel", + "executions.signal", + "executions.reset", + "executions.delete", + "executions.writeStdin", + "executions.closeStdin", + "executions.resizePty", + "executions.readOutput", + "terminal.open", + "terminal.write", + "terminal.resize", + "terminal.wait", + "terminal.close", + "filesystem.readFile", + "filesystem.writeFile", + "filesystem.readFiles", + "filesystem.writeFiles", + "filesystem.stat", + "filesystem.mkdir", + "filesystem.readdir", + "filesystem.readdirEntries", + "filesystem.readdirRecursive", + "filesystem.exists", + "filesystem.move", + "filesystem.remove", + "filesystem.export", + "filesystem.mount", + "filesystem.unmount", + "filesystem.listMounts", + "network.httpRequest", + "software.list", + "software.link", + "agents.list", + "sessions.open", + "sessions.get", + "sessions.list", + "sessions.delete", + "sessions.unload", + "sessions.prompt", + "sessions.cancelPrompt", + "sessions.respondPermission", + "sessions.readHistory", + "sessions.getConfig", + "sessions.setConfigOption", + "sessions.getCapabilities", + "sessions.getAgentInfo", + "cron.schedule", + "cron.list", + "cron.cancel", ] as const; export type AgentOsConformanceAction = @@ -74,6 +101,8 @@ export const AGENT_OS_CONFORMANCE_EVENTS = [ "cronEvent", "sessionEvent", "agentExit", + "executionOutput", + "executionCompleted", ] as const; export type AgentOsConformanceEvent = @@ -155,44 +184,52 @@ export function defineAgentOsConformanceSuite( }, 120_000); test("filesystem actions preserve bytes, metadata, batches, and directory semantics", async () => { - await backend.call("mkdir", "/conformance/fs/nested", { + await backend.call("filesystem.mkdir", "/conformance/fs/nested", { recursive: true, }); - await backend.call("writeFile", "/conformance/fs/a.txt", "alpha"); + await backend.call("filesystem.writeFile", "/conformance/fs/a.txt", "alpha"); await backend.call( - "writeFile", + "filesystem.writeFile", "/conformance/fs/nested/b.bin", new Uint8Array([0, 1, 2, 255]), ); expect( - text(await backend.call("readFile", "/conformance/fs/a.txt")), + text(await backend.call("filesystem.readFile", "/conformance/fs/a.txt")), ).toBe("alpha"); expect([ ...asBytes( - await backend.call("readFile", "/conformance/fs/nested/b.bin"), + await backend.call( + "filesystem.readFile", + "/conformance/fs/nested/b.bin", + ), ), ]).toEqual([0, 1, 2, 255]); - const writes = await backend.call("writeFiles", [ + const writes = await backend.call("filesystem.writeFiles", [ { path: "/conformance/fs/batch/c.txt", content: "charlie" }, { path: "/proc/conformance-denied", content: "no" }, ]); expect(writes.map((entry) => entry.success)).toEqual([true, false]); - const reads = await backend.call("readFiles", [ + const reads = await backend.call("filesystem.readFiles", [ "/conformance/fs/a.txt", "/conformance/fs/missing", ]); expect(text(reads[0].content)).toBe("alpha"); expect(reads[1].content).toBeNull(); - expect(await backend.call("exists", "/conformance/fs/a.txt")).toBe(true); - const stat = await backend.call("stat", "/conformance/fs/nested"); + expect( + await backend.call("filesystem.exists", "/conformance/fs/a.txt"), + ).toBe(true); + const stat = await backend.call( + "filesystem.stat", + "/conformance/fs/nested", + ); expect(stat.isDirectory).toBe(true); expect( - await backend.call("readdir", "/conformance/fs"), + await backend.call("filesystem.readdir", "/conformance/fs"), ).toEqual(expect.arrayContaining(["a.txt", "nested", "batch"])); const entries = await backend.call( - "readdirEntries", + "filesystem.readdirEntries", "/conformance/fs", ); expect(entries).toEqual( @@ -201,7 +238,7 @@ export function defineAgentOsConformanceSuite( ]), ); const recursive = await backend.call( - "readdirRecursive", + "filesystem.readdirRecursive", "/conformance/fs", ); expect(recursive.map((entry) => entry.path)).toContain( @@ -209,21 +246,26 @@ export function defineAgentOsConformanceSuite( ); await backend.call( - "move", + "filesystem.move", "/conformance/fs/a.txt", "/conformance/fs/moved.txt", ); - expect(await backend.call("exists", "/conformance/fs/a.txt")).toBe(false); - await backend.call("remove", "/conformance/fs/moved.txt"); - expect(await backend.call("exists", "/conformance/fs/moved.txt")).toBe( - false, - ); + expect( + await backend.call("filesystem.exists", "/conformance/fs/a.txt"), + ).toBe(false); + await backend.call("filesystem.remove", "/conformance/fs/moved.txt"); + expect( + await backend.call("filesystem.exists", "/conformance/fs/moved.txt"), + ).toBe(false); }, 60_000); test("process actions and events cover execution, inspection, stdin, stop, and kill", async () => { - const execResult = await backend.call("exec", "printf exec-ok"); + const execResult = await backend.call( + "process.exec", + "printf exec-ok", + ); expect(execResult).toMatchObject({ exitCode: 0, stdout: "exec-ok" }); - const argvResult = await backend.call("execArgv", "printf", [ + const argvResult = await backend.call("process.execFile", "printf", [ "argv-ok", ]); expect(argvResult).toMatchObject({ exitCode: 0, stdout: "argv-ok" }); @@ -235,7 +277,7 @@ export function defineAgentOsConformanceSuite( ); const offExit = backend.on("processExit", (event) => exits.push(event)); const spawned = await backend.call( - "spawn", + "process.spawn", "node", [ "-e", @@ -243,27 +285,27 @@ export function defineAgentOsConformanceSuite( ], { streamStdin: true }, ); - expect((await backend.call("getProcess", spawned.pid)).running).toBe( + expect((await backend.call("process.get", spawned.pid)).running).toBe( true, ); expect( - (await backend.call("listProcesses")).some( + (await backend.call("process.list")).some( (process) => process.pid === spawned.pid, ), ).toBe(true); expect( - (await backend.call("allProcesses")).some( + (await backend.call("process.listAll")).some( (process) => process.pid === spawned.pid, ), ).toBe(true); expect( - (await backend.call("processTree")).some( + (await backend.call("process.tree")).some( (process) => process.pid === spawned.pid, ), ).toBe(true); - await backend.call("writeProcessStdin", spawned.pid, "hello"); - await backend.call("closeProcessStdin", spawned.pid); - expect(await backend.call("waitProcess", spawned.pid)).toBe(0); + await backend.call("process.writeStdin", spawned.pid, "hello"); + await backend.call("process.closeStdin", spawned.pid); + expect(await backend.call("process.wait", spawned.pid)).toBe(0); await eventually( () => output, (events) => @@ -278,18 +320,18 @@ export function defineAgentOsConformanceSuite( ), ); - const stopped = await backend.call("spawn", "node", [ + const stopped = await backend.call("process.spawn", "node", [ "-e", "setInterval(() => {}, 1000)", ]); - await backend.call("stopProcess", stopped.pid); - await backend.call("waitProcess", stopped.pid); - const killed = await backend.call("spawn", "node", [ + await backend.call("process.stop", stopped.pid); + await backend.call("process.wait", stopped.pid); + const killed = await backend.call("process.spawn", "node", [ "-e", "setInterval(() => {}, 1000)", ]); - await backend.call("killProcess", killed.pid); - await backend.call("waitProcess", killed.pid); + await backend.call("process.kill", killed.pid); + await backend.call("process.wait", killed.pid); offOutput(); offExit(); }, 60_000); @@ -303,7 +345,7 @@ export function defineAgentOsConformanceSuite( stderr.push(event), ); const offExit = backend.on("shellExit", (event) => exits.push(event)); - const shell = await backend.call("openShell", { + const shell = await backend.call("terminal.open", { command: "node", args: [ "-e", @@ -312,9 +354,9 @@ export function defineAgentOsConformanceSuite( cols: 80, rows: 24, }); - await backend.call("resizeShell", shell.shellId, 100, 30); - await backend.call("writeShell", shell.shellId, "hello-shell\n"); - expect(await backend.call("waitShell", shell.shellId)).toBe(0); + await backend.call("terminal.resize", shell.shellId, 100, 30); + await backend.call("terminal.write", shell.shellId, "hello-shell\n"); + expect(await backend.call("terminal.wait", shell.shellId)).toBe(0); await eventually( () => data, (events) => @@ -338,11 +380,11 @@ export function defineAgentOsConformanceSuite( (events) => events.some((event) => event.shellId === shell.shellId), ); - const closable = await backend.call("openShell", { + const closable = await backend.call("terminal.open", { command: "node", args: ["-e", "setInterval(() => {}, 1000)"], }); - await backend.call("closeShell", closable.shellId); + await backend.call("terminal.close", closable.shellId); offData(); offStderr(); offExit(); @@ -353,7 +395,7 @@ export function defineAgentOsConformanceSuite( const offOutput = backend.on("processOutput", (event) => output.push(event), ); - const server = await backend.call("spawn", "node", [ + const server = await backend.call("process.spawn", "node", [ "-e", ` const http = require('http'); @@ -373,7 +415,7 @@ export function defineAgentOsConformanceSuite( ), ); const response = await backend.call( - "httpRequest", + "network.httpRequest", { port: 31337, path: "/path?q=1", @@ -385,27 +427,27 @@ export function defineAgentOsConformanceSuite( expect(response.status).toBe(200); expect(response.headers["x-conformance"]).toBe("yes"); expect(text(response.body)).toBe("POST:/path?q=1:payload"); - await backend.call("killProcess", server.pid); - await backend.call("waitProcess", server.pid); + await backend.call("process.kill", server.pid); + await backend.call("process.wait", server.pid); offOutput(); - const job = await backend.call("scheduleCron", { + const job = await backend.call("cron.schedule", { id: "conformance-cron", schedule: "0 0 1 1 *", action: { type: "exec", command: "node", args: ["-e", "void 0"] }, overlap: "skip", }); expect(job.id).toBe("conformance-cron"); - expect(await backend.call("listCronJobs")).toEqual([ + expect(await backend.call("cron.list")).toEqual([ expect.objectContaining({ id: "conformance-cron", overlap: "skip" }), ]); - await backend.call("cancelCronJob", job.id); - expect(await backend.call("listCronJobs")).toEqual([]); + await backend.call("cron.cancel", job.id); + expect(await backend.call("cron.list")).toEqual([]); const cronEvents: any[] = []; const offCron = backend.on("cronEvent", (event) => cronEvents.push(event), ); - const oneShot = await backend.call("scheduleCron", { + const oneShot = await backend.call("cron.schedule", { id: "conformance-cron-event", schedule: new Date(Date.now() + 750).toISOString(), action: { type: "exec", command: "node", args: ["-e", "void 0"] }, @@ -419,10 +461,10 @@ export function defineAgentOsConformanceSuite( ), 10_000, ); - await backend.call("cancelCronJob", oneShot.id); + await backend.call("cron.cancel", oneShot.id); offCron(); - const agents = await backend.call("listAgents"); + const agents = await backend.call("agents.list"); expect(agents).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -431,7 +473,7 @@ export function defineAgentOsConformanceSuite( }), ]), ); - expect(await backend.call("listMounts")).toContainEqual( + expect(await backend.call("filesystem.listMounts")).toContainEqual( expect.objectContaining({ path: "/conformance-mount", kind: "host_dir", @@ -441,19 +483,19 @@ export function defineAgentOsConformanceSuite( expect( text( await backend.call( - "readFile", + "filesystem.readFile", "/conformance-mount/package.json", ), ), ).toContain(CONFORMANCE_AGENT_NAME); await expect( backend.call( - "writeFile", + "filesystem.writeFile", "/conformance-mount/should-fail.txt", "read-only", ), ).rejects.toThrow(); - const software = await backend.call("listSoftware"); + const software = await backend.call("software.list"); expect( software.some((entry) => entry.commands.includes("conformance-agent-acp"), @@ -464,7 +506,7 @@ export function defineAgentOsConformanceSuite( test("sessions cover durable history, live events, permission replies, config, restoration, unload, and deletion", async () => { const sessionId = "conformance-session"; expect( - await backend.call("openSession", { + await backend.call("sessions.open", { sessionId, agent: CONFORMANCE_AGENT_NAME, permissionPolicy: "ask", @@ -474,20 +516,20 @@ export function defineAgentOsConformanceSuite( additionalInstructions: "shared-suite", }), ).toBeUndefined(); - expect((await backend.call("listSessions")).sessions).toContainEqual( + expect((await backend.call("sessions.list")).sessions).toContainEqual( expect.objectContaining({ sessionId, agent: CONFORMANCE_AGENT_NAME, }), ); expect( - (await backend.call("getSessionConfig", { sessionId })).options, + (await backend.call("sessions.getConfig", { sessionId })).options, ).toHaveLength(2); expect( - await backend.call("getSessionCapabilities", { sessionId }), + await backend.call("sessions.getCapabilities", { sessionId }), ).toMatchObject({ loadSession: true }); expect( - await backend.call("getSessionAgentInfo", { sessionId }), + await backend.call("sessions.getAgentInfo", { sessionId }), ).toMatchObject({ name: CONFORMANCE_AGENT_NAME }); const sessionEvents: any[] = []; @@ -503,7 +545,7 @@ export function defineAgentOsConformanceSuite( permissionReady.resolve(event); } }); - const prompt = backend.call("prompt", { + const prompt = backend.call("sessions.prompt", { sessionId, content: [{ type: "text", text: "permission please" }], }); @@ -526,7 +568,7 @@ export function defineAgentOsConformanceSuite( ), ]); expect(permission.toolCall.toolCallId).toBe("binding-call-1"); - await backend.call("respondPermission", { + await backend.call("sessions.respondPermission", { sessionId, requestId: permission.requestId, optionId: "allow_once", @@ -534,17 +576,19 @@ export function defineAgentOsConformanceSuite( expect(JSON.stringify((await prompt).message)).toContain("permission"); expect(permissions).toHaveLength(1); - await backend.call("setSessionConfigOption", { + await backend.call("sessions.setConfigOption", { sessionId, configId: "model", value: "next-model", }); - await backend.call("setSessionConfigOption", { + await backend.call("sessions.setConfigOption", { sessionId, configId: "thought_level", value: "high", }); - const config = (await backend.call("getSessionConfig", { sessionId })) + const config = ( + await backend.call("sessions.getConfig", { sessionId }) + ) .options; expect( config.find((entry: any) => entry.category === "model")?.currentValue, @@ -553,8 +597,10 @@ export function defineAgentOsConformanceSuite( config.find((entry: any) => entry.category === "thought_level") ?.currentValue, ).toBe("high"); - await backend.call("cancelPrompt", { sessionId }); - const history = await backend.call("readHistory", { sessionId }); + await backend.call("sessions.cancelPrompt", { sessionId }); + const history = await backend.call("sessions.readHistory", { + sessionId, + }); expect(history.events.length).toBeGreaterThan(0); const permissionHistory = history.events.filter( (entry: any) => @@ -584,14 +630,16 @@ export function defineAgentOsConformanceSuite( } expect(recoveredBySequence.size).toBe(history.events.length); - await backend.call("unloadSession", { sessionId }); - const restored = await backend.call("prompt", { + await backend.call("sessions.unload", { sessionId }); + const restored = await backend.call("sessions.prompt", { sessionId, content: [{ type: "text", text: "restored" }], }); expect(JSON.stringify(restored.message)).toContain("restored"); - await backend.call("deleteSession", { sessionId }); - expect((await backend.call("listSessions")).sessions).not.toContainEqual( + await backend.call("sessions.delete", { sessionId }); + expect( + (await backend.call("sessions.list")).sessions, + ).not.toContainEqual( expect.objectContaining({ sessionId }), ); offSession(); @@ -607,19 +655,21 @@ export function defineAgentOsConformanceSuite( // permissionPolicy is deliberately omitted: the sidecar-owned default // must be allow_all for both Core and actor clients. expect( - await backend.call("openSession", { + await backend.call("sessions.open", { sessionId, agent: CONFORMANCE_AGENT_NAME, skipOsInstructions: true, }), ).toBeUndefined(); - const result = await backend.call("prompt", { + const result = await backend.call("sessions.prompt", { sessionId, content: [{ type: "text", text: "permission automatically" }], }); expect(JSON.stringify(result.message)).toContain("allow_once"); - const history = await backend.call("readHistory", { sessionId }); + const history = await backend.call("sessions.readHistory", { + sessionId, + }); expect( history.events.filter( (entry: any) => @@ -636,7 +686,7 @@ export function defineAgentOsConformanceSuite( ).toBe(false); } finally { offSession(); - await backend.call("deleteSession", { sessionId }); + await backend.call("sessions.delete", { sessionId }); } }, 90_000); @@ -646,13 +696,13 @@ export function defineAgentOsConformanceSuite( crashes.push(event), ); const sessionId = "crash-session"; - await backend.call("openSession", { + await backend.call("sessions.open", { sessionId, agent: CONFORMANCE_AGENT_NAME, skipOsInstructions: true, }); await backend - .call("prompt", { + .call("sessions.prompt", { sessionId, content: [{ type: "text", text: "crash-adapter" }], }) @@ -662,7 +712,7 @@ export function defineAgentOsConformanceSuite( (events) => events.some((event) => event.sessionId === sessionId), 15_000, ); - await backend.call("deleteSession", { sessionId }); + await backend.call("sessions.delete", { sessionId }); offCrash(); }, 30_000); diff --git a/tests/fixtures/crypto-basic-conformance.json b/tests/fixtures/crypto-basic-conformance.json index d24fe4c19e..7493a8e4ae 100644 --- a/tests/fixtures/crypto-basic-conformance.json +++ b/tests/fixtures/crypto-basic-conformance.json @@ -1,5 +1,5 @@ { - "message": "secure-exec", + "message": "agentos", "hmacKey": "shared-secret", "password": "password", "salt": "salt", @@ -12,20 +12,20 @@ }, "aesCbc": { "algorithm": "aes-256-cbc", - "plaintext": "secure-exec-crypto-surface", + "plaintext": "agentos-crypto-surface", "keyHex": "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff", "ivHex": "0102030405060708090a0b0c0d0e0f10" }, "aesGcm": { "algorithm": "aes-256-gcm", - "plaintext": "secure-exec-gcm-authenticated", - "aad": "secure-exec-aad", + "plaintext": "agentos-gcm-authenticated", + "aad": "agentos-aad", "keyHex": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "ivHex": "101112131415161718191a1b", "authTagLength": 16 }, "rsa": { - "message": "secure-exec advanced crypto", + "message": "agentos advanced crypto", "privatePem": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5r8vgRgQj8qd5\nBzip2/NdY6qTnrF5D5V7F+cSoARQbWTxqoAq92II5swnKTGafmf4m1mdFT9Jqwh8\n+oD2q9cokZsPONBEQTbhgLziUC5h8BU8xZf/5yHi8oQ9CX96D5XNyGZA4fpG4l+0\nxMUXCvPL4loKElgBvSrGKYqA+/3gAqQESELKFzdwiwy6BsJuIYgeHp48aHSm/Mei\nhi7nGncWIWRSccbUbQ2rskqwNJJEi7w1cGzPfyyxlmK7RvxFM979lHtbS266j3K/\nD8azaL14oJkWesjwjAwXaCG+YTBZsznpNIkbzNcboqb7azxPc8ihLYEhZgw7Mypw\nW34i3IWpAgMBAAECggEABiGud0m7y3ew6dezTZsBDPvq2MFCqTWhB4JxQG9Lq5MF\n/gIinyIx7Asgm0hw2WIQ/kFO/73N+OZIO1953VHGViu2Ty94MFDNxm63jgRjMhu5\nIwHmJ4g5cu+NyCAyQ+5HN/2rY4gZLIVyiJsYyptNFDxb7pcCR47SEauy5aVGK4/y\nuv3WmZQPU8EkCol7Cxhgq5MFkfLuXN44+Lt81ADaI8bcKgzosyczswkijAVzmmjt\ncc4T/SDy8dZBQZistej5pBWVaDJHIEJyoxxRrVy5mJCDEfSfDDAm2iNrGGQAMQ6x\nlpVAjT5MtBA+tbwXe4ODMZaGYKiiFC6DxJX8yThrawKBgQDo3Fnf0+3qkmfXbuG2\nO3GjJQ0S5tthS7Av6e90c68zLMn795lh30BCCuMnwfIAw3gP7A3mxXBepFNaG3/h\nUJ5BhT3pegg/9qetNHgLuUpOZE7F22h/TGNBeV2TJr5oYyCQU289yvRgl9tYSE5J\no5/YnGCX9nnTj2LdOvN1BDqHGwKBgQDMI2d8MROUsbLCKR7sHpkpk8lC6wb23x2s\nLe3tb8cjnSLwfIDKruV1O85AVPmQ9vD7lkwDUtn6WsSjK01GBCdS19uOlPbwhsAe\nsVDEa0pchTHerHIFyWX67gX2lWB9wyb5WnoaOW3Z07E2l//5x6/MQihF+KJ7+ooB\nZNj5ZcAeiwKBgQDEfpyAWY9b76sciX6Bjeu4ZV2A47mfgoTsCZV8SNpAbr0Kl8ag\nZgkNMa65L9mMd2Vq2iBo6ZaG5ldHpAjnEmZYl5zE1ar8fqDDcZETI4nmWJJ4N0sY\nkFb5OvaRY5Is9+jUoPMvy4EnuTzoZCtbzGzoFh0UXnIy6b3dPQ+PYMAanQKBgAHo\nzt39g4ZfhyGDyvNAcgROipJiqmUCvz9OCqyu3/j4TkxbjcTXj/PhxFMbuF3fwW9I\n/5sEWl+aG76+9/EQtuFyfW4+/HRRfliLJgtASajF4iqICGT/dkG7mjitOwLSIXox\nm3TFVr6z2TN+hnlqob1SXRgSdEqelE3hCJqEliBzAoGAY8tfltBpUviP6QvUWLuR\nUAVVJAbO5+4nqoAuw99YUk8ROynPJ0DRcl4gWdpmsn3en4l8tDa2B2+4f4aqIHk9\n2pHtDYOvwcOHUmVM3wUd+A8g51aTGc4cDrYNFCU0MNKeZIGUNvmUUIzOe3Ip2baV\nhQMaC2t1SHE0udGv4T0LBLE=\n-----END PRIVATE KEY-----", "publicPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAua/L4EYEI/KneQc4qdvz\nXWOqk56xeQ+VexfnEqAEUG1k8aqAKvdiCObMJykxmn5n+JtZnRU/SasIfPqA9qvX\nKJGbDzjQREE24YC84lAuYfAVPMWX/+ch4vKEPQl/eg+VzchmQOH6RuJftMTFFwrz\ny+JaChJYAb0qximKgPv94AKkBEhCyhc3cIsMugbCbiGIHh6ePGh0pvzHooYu5xp3\nFiFkUnHG1G0Nq7JKsDSSRIu8NXBsz38ssZZiu0b8RTPe/ZR7W0tuuo9yvw/Gs2i9\neKCZFnrI8IwMF2ghvmEwWbM56TSJG8zXG6Km+2s8T3PIoS2BIWYMOzMqcFt+ItyF\nqQIDAQAB\n-----END PUBLIC KEY-----", "sha256SignatureHex": "3b63c4ed2e760a6c49b11889b9b2d3b854f2ef849ddedcdba6da75838122640a79f01959cc314732fe005e7af3a1c5437a735cdbf17796dd5e2ef23ed8e29e7963ab28b22f0cc0992b7519d397ac2fd29259e3557f59c71cf6b2d334715302534ecdf6dae04aea9fca5c3e658b9be66f2b5b51c960f7d41dc2381049ecfc471e7c3119be82f8156f3655451eebb8412eeb989481f28864d6fed41a2bd1b1e8591fbd6afc2b8b46ab2ae19ab1028f380b7ebeb5d1cfe851af08764f40f295ef6ea9b9a64fde565e85dde122cd21ae8cb7100065cecb74e2168ec3404f4ff5f99ed80f0f081000c337a387d29425a33cf15cd8c41f5ebe64a0a99cc9164fde8777" diff --git a/toolchain/Cargo.lock b/toolchain/Cargo.lock index 9ca803d670..e5afb7654c 100644 --- a/toolchain/Cargo.lock +++ b/toolchain/Cargo.lock @@ -677,7 +677,7 @@ dependencies = [ name = "cmd-awk" version = "0.1.0" dependencies = [ - "secureexec-awk", + "agentos-awk", ] [[package]] @@ -770,8 +770,8 @@ dependencies = [ "codex-network-proxy", "codex-otel", "ratatui", - "secureexec-wasi-http", - "secureexec-wasi-spawn", + "agentos-wasi-http", + "agentos-wasi-spawn", ] [[package]] @@ -780,14 +780,14 @@ version = "0.1.0" dependencies = [ "codex-network-proxy", "codex-otel", - "secureexec-wasi-http", + "agentos-wasi-http", ] [[package]] name = "cmd-column" version = "0.1.0" dependencies = [ - "secureexec-column", + "agentos-column", ] [[package]] @@ -808,7 +808,7 @@ dependencies = [ name = "cmd-curl" version = "0.1.0" dependencies = [ - "secureexec-wasi-http", + "agentos-wasi-http", ] [[package]] @@ -840,7 +840,7 @@ version = "0.1.0" name = "cmd-diff" version = "0.1.0" dependencies = [ - "secureexec-diff", + "agentos-diff", ] [[package]] @@ -861,7 +861,7 @@ dependencies = [ name = "cmd-du" version = "0.1.0" dependencies = [ - "secureexec-du", + "agentos-du", ] [[package]] @@ -875,14 +875,14 @@ dependencies = [ name = "cmd-egrep" version = "0.1.0" dependencies = [ - "secureexec-shims", + "agentos-shims", ] [[package]] name = "cmd-env" version = "0.1.0" dependencies = [ - "secureexec-shims", + "agentos-shims", ] [[package]] @@ -896,7 +896,7 @@ dependencies = [ name = "cmd-expr" version = "0.1.0" dependencies = [ - "secureexec-expr", + "agentos-expr", ] [[package]] @@ -924,14 +924,14 @@ dependencies = [ name = "cmd-fgrep" version = "0.1.0" dependencies = [ - "secureexec-shims", + "agentos-shims", ] [[package]] name = "cmd-file" version = "0.1.0" dependencies = [ - "secureexec-file-cmd", + "agentos-file-cmd", ] [[package]] @@ -971,7 +971,7 @@ dependencies = [ name = "cmd-gzip" version = "0.1.0" dependencies = [ - "secureexec-gzip", + "agentos-gzip", ] [[package]] @@ -989,7 +989,7 @@ version = "0.1.0" name = "cmd-http-test" version = "0.1.0" dependencies = [ - "secureexec-wasi-http", + "agentos-wasi-http", ] [[package]] @@ -1010,7 +1010,7 @@ dependencies = [ name = "cmd-jq" version = "0.1.0" dependencies = [ - "secureexec-jq", + "agentos-jq", ] [[package]] @@ -1087,7 +1087,7 @@ dependencies = [ name = "cmd-nice" version = "0.1.0" dependencies = [ - "secureexec-shims", + "agentos-shims", ] [[package]] @@ -1101,7 +1101,7 @@ dependencies = [ name = "cmd-nohup" version = "0.1.0" dependencies = [ - "secureexec-shims", + "agentos-shims", ] [[package]] @@ -1185,7 +1185,7 @@ dependencies = [ name = "cmd-rev" version = "0.1.0" dependencies = [ - "secureexec-rev", + "agentos-rev", ] [[package]] @@ -1290,7 +1290,7 @@ dependencies = [ name = "cmd-sleep" version = "0.1.0" dependencies = [ - "secureexec-builtins", + "agentos-builtins", ] [[package]] @@ -1304,7 +1304,7 @@ dependencies = [ name = "cmd-spawn-test-host" version = "0.1.0" dependencies = [ - "secureexec-wasi-spawn", + "agentos-wasi-spawn", "tokio", ] @@ -1326,21 +1326,21 @@ dependencies = [ name = "cmd-stdbuf" version = "0.1.0" dependencies = [ - "secureexec-shims", + "agentos-shims", ] [[package]] name = "cmd-strings" version = "0.1.0" dependencies = [ - "secureexec-strings-cmd", + "agentos-strings-cmd", ] [[package]] name = "cmd-stubs" version = "0.1.0" dependencies = [ - "secureexec-stubs", + "agentos-stubs", ] [[package]] @@ -1375,7 +1375,7 @@ dependencies = [ name = "cmd-tar" version = "0.1.0" dependencies = [ - "secureexec-tar", + "agentos-tar", ] [[package]] @@ -1389,14 +1389,14 @@ dependencies = [ name = "cmd-test" version = "0.1.0" dependencies = [ - "secureexec-builtins", + "agentos-builtins", ] [[package]] name = "cmd-timeout" version = "0.1.0" dependencies = [ - "secureexec-shims", + "agentos-shims", ] [[package]] @@ -1470,14 +1470,14 @@ dependencies = [ name = "cmd-which" version = "0.1.0" dependencies = [ - "secureexec-shims", + "agentos-shims", ] [[package]] name = "cmd-whoami" version = "0.1.0" dependencies = [ - "secureexec-builtins", + "agentos-builtins", ] [[package]] @@ -1502,7 +1502,7 @@ dependencies = [ name = "cmd-yq" version = "0.1.0" dependencies = [ - "secureexec-yq", + "agentos-yq", ] [[package]] @@ -4110,14 +4110,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "secureexec-awk" +name = "agentos-awk" version = "0.1.0" dependencies = [ "awk-rs", ] [[package]] -name = "secureexec-builtins" +name = "agentos-builtins" version = "0.1.0" dependencies = [ "libc", @@ -4125,43 +4125,43 @@ dependencies = [ ] [[package]] -name = "secureexec-column" +name = "agentos-column" version = "0.1.0" [[package]] -name = "secureexec-diff" +name = "agentos-diff" version = "0.1.0" dependencies = [ "similar", ] [[package]] -name = "secureexec-du" +name = "agentos-du" version = "0.1.0" [[package]] -name = "secureexec-expr" +name = "agentos-expr" version = "0.1.0" dependencies = [ "regex", ] [[package]] -name = "secureexec-file-cmd" +name = "agentos-file-cmd" version = "0.1.0" dependencies = [ "infer", ] [[package]] -name = "secureexec-gzip" +name = "agentos-gzip" version = "0.1.0" dependencies = [ "flate2", ] [[package]] -name = "secureexec-jq" +name = "agentos-jq" version = "0.1.0" dependencies = [ "jaq-core", @@ -4171,26 +4171,26 @@ dependencies = [ ] [[package]] -name = "secureexec-rev" +name = "agentos-rev" version = "0.1.0" [[package]] -name = "secureexec-shims" +name = "agentos-shims" version = "0.1.0" dependencies = [ "wasi-ext", ] [[package]] -name = "secureexec-strings-cmd" +name = "agentos-strings-cmd" version = "0.1.0" [[package]] -name = "secureexec-stubs" +name = "agentos-stubs" version = "0.1.0" [[package]] -name = "secureexec-tar" +name = "agentos-tar" version = "0.1.0" dependencies = [ "flate2", @@ -4198,22 +4198,22 @@ dependencies = [ ] [[package]] -name = "secureexec-wasi-http" +name = "agentos-wasi-http" version = "0.1.0" dependencies = [ "wasi-ext", ] [[package]] -name = "secureexec-wasi-pty" +name = "agentos-wasi-pty" version = "0.1.0" dependencies = [ - "secureexec-wasi-spawn", + "agentos-wasi-spawn", "wasi-ext", ] [[package]] -name = "secureexec-wasi-spawn" +name = "agentos-wasi-spawn" version = "0.1.0" dependencies = [ "wasi", @@ -4221,7 +4221,7 @@ dependencies = [ ] [[package]] -name = "secureexec-yq" +name = "agentos-yq" version = "0.1.0" dependencies = [ "jaq-core", diff --git a/toolchain/c/scripts/build-curl-upstream.sh b/toolchain/c/scripts/build-curl-upstream.sh index 8d11dccca3..27fb34432f 100644 --- a/toolchain/c/scripts/build-curl-upstream.sh +++ b/toolchain/c/scripts/build-curl-upstream.sh @@ -115,7 +115,7 @@ if [[ ! -d "$SRC_DIR" ]]; then exit 1 fi -echo "Applying secure-exec overlay..." +echo "Applying agentos overlay..." while IFS= read -r -d '' file; do rel="${file#$OVERLAY_DIR/}" mkdir -p "$SRC_DIR/$(dirname "$rel")" diff --git a/toolchain/c/scripts/build-wget-upstream.sh b/toolchain/c/scripts/build-wget-upstream.sh index edbd025d9f..1f57119f22 100644 --- a/toolchain/c/scripts/build-wget-upstream.sh +++ b/toolchain/c/scripts/build-wget-upstream.sh @@ -144,8 +144,8 @@ ZLIB_CFLAGS="-I$ZLIB_INCLUDE" \ ZLIB_LIBS="-L$ZLIB_LIBDIR -lz" \ CPPFLAGS="-I$OVERLAY_INCLUDE_DIR -I$MBEDTLS_INCLUDE" \ gl_cv_func_posix_spawn_works=yes \ -gl_cv_func_posix_spawn_secure_exec=yes \ -gl_cv_func_posix_spawnp_secure_exec=yes \ +gl_cv_func_posix_spawn_agentos=yes \ +gl_cv_func_posix_spawnp_agentos=yes \ ac_cv_func_posix_spawn_file_actions_addchdir=yes \ gl_cv_func_posix_spawn_file_actions_addclose_works=yes \ gl_cv_func_posix_spawn_file_actions_adddup2_works=yes \ diff --git a/toolchain/crates/commands/_stubs/Cargo.toml b/toolchain/crates/commands/_stubs/Cargo.toml index 36c2fee922..acf032197b 100644 --- a/toolchain/crates/commands/_stubs/Cargo.toml +++ b/toolchain/crates/commands/_stubs/Cargo.toml @@ -3,11 +3,11 @@ name = "cmd-stubs" version.workspace = true edition.workspace = true license.workspace = true -description = "_stubs mini-multicall binary for unsupported commands in secure-exec VM" +description = "_stubs mini-multicall binary for unsupported commands in agentos VM" [[bin]] name = "_stubs" path = "src/main.rs" [dependencies] -secureexec-stubs = { path = "../../libs/stubs" } +agentos-stubs = { path = "../../libs/stubs" } diff --git a/toolchain/crates/commands/_stubs/src/main.rs b/toolchain/crates/commands/_stubs/src/main.rs index c4367e5076..0424f00086 100644 --- a/toolchain/crates/commands/_stubs/src/main.rs +++ b/toolchain/crates/commands/_stubs/src/main.rs @@ -1,4 +1,4 @@ fn main() { let args: Vec = std::env::args().collect(); - std::process::exit(secureexec_stubs::run(&args)); + std::process::exit(agentos_stubs::run(&args)); } diff --git a/toolchain/crates/commands/http-test/Cargo.toml b/toolchain/crates/commands/http-test/Cargo.toml index f6aae5bde7..0254ad44eb 100644 --- a/toolchain/crates/commands/http-test/Cargo.toml +++ b/toolchain/crates/commands/http-test/Cargo.toml @@ -16,4 +16,4 @@ test = false required-features = ["bin"] [dependencies] -wasi-http = { package = "secureexec-wasi-http", path = "../../libs/wasi-http" } +wasi-http = { package = "agentos-wasi-http", path = "../../libs/wasi-http" } diff --git a/toolchain/crates/commands/spawn-test-host/Cargo.toml b/toolchain/crates/commands/spawn-test-host/Cargo.toml index 580e54c109..dea03a860a 100644 --- a/toolchain/crates/commands/spawn-test-host/Cargo.toml +++ b/toolchain/crates/commands/spawn-test-host/Cargo.toml @@ -10,5 +10,5 @@ name = "spawn-test-host" path = "src/main.rs" [dependencies] -wasi-spawn = { package = "secureexec-wasi-spawn", path = "../../libs/wasi-spawn" } +wasi-spawn = { package = "agentos-wasi-spawn", path = "../../libs/wasi-spawn" } tokio = { version = "1.50.0", features = ["process", "rt"] } diff --git a/toolchain/crates/commands/xu/Cargo.toml b/toolchain/crates/commands/xu/Cargo.toml index 4ed41c9f30..95bc554c3f 100644 --- a/toolchain/crates/commands/xu/Cargo.toml +++ b/toolchain/crates/commands/xu/Cargo.toml @@ -3,7 +3,7 @@ name = "cmd-xu" version.workspace = true edition.workspace = true license.workspace = true -description = "xu standalone binary for secure-exec VM tests" +description = "xu standalone binary for agentos VM tests" [[bin]] name = "xu" diff --git a/toolchain/crates/libs/builtins/Cargo.toml b/toolchain/crates/libs/builtins/Cargo.toml index daac258bf5..6eac724435 100644 --- a/toolchain/crates/libs/builtins/Cargo.toml +++ b/toolchain/crates/libs/builtins/Cargo.toml @@ -1,9 +1,9 @@ [package] -name = "secureexec-builtins" +name = "agentos-builtins" version.workspace = true edition.workspace = true license.workspace = true -description = "Built-in command implementations (sleep, test/[, whoami) for secure-exec VM" +description = "Built-in command implementations (sleep, test/[, whoami) for agentos VM" [dependencies] libc = "0.2" diff --git a/toolchain/crates/libs/builtins/src/lib.rs b/toolchain/crates/libs/builtins/src/lib.rs index dee2b00af5..7b1067f782 100644 --- a/toolchain/crates/libs/builtins/src/lib.rs +++ b/toolchain/crates/libs/builtins/src/lib.rs @@ -637,7 +637,7 @@ mod tests { .expect("system time") .as_nanos(); let root = std::env::temp_dir().join(format!( - "secureexec-builtins-test-{}-{}", + "agentos-builtins-test-{}-{}", std::process::id(), unique )); diff --git a/toolchain/crates/libs/shims/Cargo.toml b/toolchain/crates/libs/shims/Cargo.toml index 0d4b05ce1f..a3c7993bad 100644 --- a/toolchain/crates/libs/shims/Cargo.toml +++ b/toolchain/crates/libs/shims/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "secureexec-shims" +name = "agentos-shims" version.workspace = true edition.workspace = true license.workspace = true diff --git a/toolchain/crates/libs/shims/src/which.rs b/toolchain/crates/libs/shims/src/which.rs index c128714637..7fde0037c7 100644 --- a/toolchain/crates/libs/shims/src/which.rs +++ b/toolchain/crates/libs/shims/src/which.rs @@ -1,4 +1,4 @@ -//! Minimal `which` implementation for the secure-exec VM. +//! Minimal `which` implementation for the agentos VM. //! //! Searches the current PATH for one or more command names and prints the first //! matching executable path for each command. This is primarily needed for diff --git a/toolchain/crates/libs/stubs/Cargo.toml b/toolchain/crates/libs/stubs/Cargo.toml index ca054d68ee..a19de8c5d9 100644 --- a/toolchain/crates/libs/stubs/Cargo.toml +++ b/toolchain/crates/libs/stubs/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "secureexec-stubs" +name = "agentos-stubs" version.workspace = true edition.workspace = true license.workspace = true diff --git a/toolchain/crates/libs/wasi-http/Cargo.toml b/toolchain/crates/libs/wasi-http/Cargo.toml index af90f82ce7..775cdef89b 100644 --- a/toolchain/crates/libs/wasi-http/Cargo.toml +++ b/toolchain/crates/libs/wasi-http/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "secureexec-wasi-http" +name = "agentos-wasi-http" version.workspace = true edition.workspace = true license.workspace = true diff --git a/toolchain/crates/libs/wasi-pty/Cargo.toml b/toolchain/crates/libs/wasi-pty/Cargo.toml index 06ccd7e586..7eb5446a6c 100644 --- a/toolchain/crates/libs/wasi-pty/Cargo.toml +++ b/toolchain/crates/libs/wasi-pty/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "secureexec-wasi-pty" +name = "agentos-wasi-pty" version.workspace = true edition.workspace = true license.workspace = true @@ -7,4 +7,4 @@ description = "WASI PTY-based process management via host_process FFI for intera [dependencies] wasi-ext = { path = "../../wasi-ext" } -wasi-spawn = { package = "secureexec-wasi-spawn", path = "../wasi-spawn" } +wasi-spawn = { package = "agentos-wasi-spawn", path = "../wasi-spawn" } diff --git a/toolchain/crates/libs/wasi-spawn/Cargo.toml b/toolchain/crates/libs/wasi-spawn/Cargo.toml index bc2ec7ebf8..a412e3b15c 100644 --- a/toolchain/crates/libs/wasi-spawn/Cargo.toml +++ b/toolchain/crates/libs/wasi-spawn/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "secureexec-wasi-spawn" +name = "agentos-wasi-spawn" version.workspace = true edition.workspace = true license.workspace = true diff --git a/toolchain/std-patches/0009-wasi-split-paths.patch b/toolchain/std-patches/0009-wasi-split-paths.patch index 2ff1aefe10..aae6141601 100644 --- a/toolchain/std-patches/0009-wasi-split-paths.patch +++ b/toolchain/std-patches/0009-wasi-split-paths.patch @@ -13,7 +13,7 @@ -pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { - panic!("unsupported") +pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { -+ // wasm32-wasip1 uses ':'-separated PATH-like lists, like Unix. (secure-exec ++ // wasm32-wasip1 uses ':'-separated PATH-like lists, like Unix. (agentos + // pipeline-only codex port — codex parses PATH via env::split_paths.) + use crate::os::wasi::ffi::OsStrExt; + let bytes = unparsed.as_bytes(); diff --git a/toolchain/std-patches/codex-source/README.md b/toolchain/std-patches/codex-source/README.md index e7ba9c70ea..d6e568d7ab 100644 --- a/toolchain/std-patches/codex-source/README.md +++ b/toolchain/std-patches/codex-source/README.md @@ -3,7 +3,7 @@ These are minimal, upstreamable additions to codex's own source where it has a hard `compile_error!`/missing-platform arm that NO toolchain patch can bypass (first-party compile-time gates). Each adds REAL wasi support (wasi has the API), -not a hack. Applied to the vendored codex source during the secure-exec build. +not a hack. Applied to the vendored codex source during the agentos build. 1. utils/git/src/platform.rs — add `#[cfg(target_os = "wasi")] create_symlink` using `std::os::wasi::fs::symlink_path`, and widen the fallback @@ -28,7 +28,7 @@ NOT gate the SOURCE usage — an incomplete author wasi port. Breakdown: build_config_state, host_and_port_from_network_addr, normalize_host, validate_policy_against_constraints, NetworkProxyConstraints/Error, NetworkProtocol, NetworkProxyState, NetworkProxyAuditMetadata, NetworkPolicyRequest, …). The real crate - can't compile on wasi (rama proxy stack); EXPAND the secure-exec codex-network-proxy + can't compile on wasi (rama proxy stack); EXPAND the agentos codex-network-proxy stub to this full API (no-op/inert — the VM kernel brokers network policy host-side). - ~6: codex_otel SessionTelemetry methods the stub lacks → expand codex-otel stub. - tokio::signal (no signals on wasi) → gate/stub codex-core's signal usage. @@ -87,7 +87,7 @@ git symlink, rmcp-client resolver. Plus std ExitStatusExt patch (std-patches/ 0008-wasi-exit-status-ext.md). `cargo build -p codex-core -p codex-exec --target wasm32-wasip1 -Z build-std` → Finished, EXIT 0. -REMAINING to e2e: (1) the secure-exec command crate crates/commands/codex-exec +REMAINING to e2e: (1) the agentos command crate crates/commands/codex-exec un-stub --session-turn to delegate to the real codex-core/codex-exec engine and emit the EE newline-JSON protocol (start/text_delta/tool_call_update/permission_request/ done) — the real functional integration; (2) build via `make -C toolchain wasm` @@ -98,7 +98,7 @@ agent-os matrix + un-skip the codex session test. codex-exec/src/lib.rs `wasi_stub_main()` is a placeholder ("WASI runtime support is under development"). codex-core compiles now, so replace it with the real engine (mirror lib_native.rs::run_exec_session, ~200-300 lines, in codex's workspace so it has -codex-core access). The built codex-exec.wasm IS the secure-exec `codex-exec` command. +codex-core access). The built codex-exec.wasm IS the agentos `codex-exec` command. PROTOCOL (newline-JSON, the EE adapter @rivet-ee/agent-os-codex-agent drives it): - stdin: first line {type:"start", cwd, mode, model, thought_level, @@ -146,7 +146,7 @@ agent and emits the EE protocol. `cargo build -p codex-exec --target wasm32-wasi 1. Produce optimized wasm artifacts from the fork (branch wasi-port-codex-core): `cargo build -p codex-exec -p codex --release --target wasm32-wasip1 -Z build-std` (+ wasm-opt) → copy to software/codex/wasm/{codex-exec,codex}. NOTE: the - secure-exec `make wasm` currently builds the STUB crates/commands/{codex,codex-exec}; + agentos `make wasm` currently builds the STUB crates/commands/{codex,codex-exec}; to ship the real engine, either build from the fork directly (above) or vendor the fork into toolchain and point cmd-codex-exec at the real codex-exec. 2. ACP adapter: the codex ACP bridge (spawns `codex-exec --session-turn`, newline-JSON↔ACP) @@ -164,7 +164,7 @@ Built the real codex-exec from the fork (release, wasm-opt'd to 28MB) and placed software/codex/wasm/{codex-exec,codex}. Smoke test (vm.exec("... | codex-exec --session-turn")) shows it SPAWNS and attempts WebAssembly instantiation in the VM — 44 imports, of which 43 (wasi_snapshot_preview1×30, host_net×6, host_process×5, -host_fs×2) are provided by the secure-exec runtime. The ONLY unprovided one was +host_fs×2) are provided by the agentos runtime. The ONLY unprovided one was `env.sqlite3_load_extension` (sqlx's sqlite). Fixed by defining a #[no_mangle] no-op stub in the engine (fork commit). Rebuild + re-place → instantiation should succeed. Then the smoke test ({"type":"start"} before the model call) and the full EE @@ -174,7 +174,7 @@ codex-session.test.ts (with the responses mock) verify the e2e turn. Smoke test (packages/core/tests/codex-smoke.test.ts) PASSES: vm.exec("printf '' | codex-exec --session-turn") → stdout: {"type":"start"} The real codex agent (codex-rs → wasip1, session-turn engine driving codex-core) BOOTS -in the secure-exec VM, instantiates all 44 imports (after adding host +in the agentos VM, instantiates all 44 imports (after adding host path_filestat_set_times), runs the engine, and emits the EE protocol's start event. (exit 1 only because the smoke test provides no model mock/auth, so the turn errors AFTER start — proving the boot + protocol path works.) @@ -187,7 +187,7 @@ complete agent turn. The boot+protocol path is now VERIFIED end-to-end in the VM DBG markers (engine) confirm the real codex agent runs the entire lifecycle in the VM: load config → AuthManager::shared → ThreadManager::new → start_thread → submit Op::UserInput → LIVE next_event loop (received a Warning event, processing). 8 runtime -blockers fixed and committed (host imports, secure-exec host wasi shim +blockers fixed and committed (host imports, agentos host wasi shim path_filestat_set_times, tokio fs-asyncify + spawn_blocking inline on wasi, std split_paths, now_local→UTC). FINAL GAP: the model HTTP POST to the OpenAI Responses mock does not reach it (mock requests=0, then hang). codex honors OPENAI_BASE_URL @@ -209,7 +209,7 @@ single-threaded (no OS threads); the spawn_blocking-as-current-thread-task worka task that can't run. This is an architectural mismatch (codex's threading model vs single-thread wasi), the genuinely hard core of running codex on wasi. Resolving it needs codex-internal tracing to find the exact blocking construct and make it cooperative, or a -different runtime strategy. (Sidecar instrumentation reverted; secure-exec is clean.) +different runtime strategy. (Sidecar instrumentation reverted; agentos is clean.) STATUS: codex compiles + runs the full session lifecycle in the VM (boot → EE protocol → config → auth → ThreadManager → start_thread → submit → live event loop) but hangs in the agent loop before the model call on the single-threaded runtime. @@ -259,7 +259,7 @@ Because `-Z build-std` injects `panic_unwind` via `--extern` while the wasm targ bin's `-Cpanic=abort` resolves a `panic_abort` that is ABI-matched to build-std's core), - builds with `--config 'profile.release.panic="abort"'`. See `/tmp/relink-codex.sh` for the exact invocation. (This sysroot massaging should be folded -into the secure-exec wasm toolchain so it is not a manual step.) +into the agentos wasm toolchain so it is not a manual step.) KNOWN REMAINING (non-blocking; turn works e2e): - Rollout recorder logs `failed to queue rollout items: channel closed` (the writer task exits diff --git a/toolchain/std-patches/codex-source/build-codex-wasip1.sh b/toolchain/std-patches/codex-source/build-codex-wasip1.sh index 3bc9ce0144..a432314194 100755 --- a/toolchain/std-patches/codex-source/build-codex-wasip1.sh +++ b/toolchain/std-patches/codex-source/build-codex-wasip1.sh @@ -4,13 +4,13 @@ # Captures every fix discovered while driving `cargo build -p codex-core # --target wasm32-wasip1` to its current frontier. Run from the codex-rs checkout. # This is the DIAGNOSTIC harness (builds codex IN its own workspace to find the -# frontier). The SHIPPING build vendors codex into secure-exec and swaps the same +# frontier). The SHIPPING build vendors codex into agentos and swaps the same # crates via [patch.crates-io]; the fixes are identical. # -# Usage: CODEX=/path/to/codex-rs SECEXEC=/path/to/secure-exec ./build-codex-wasip1.sh +# Usage: CODEX=/path/to/codex-rs SECEXEC=/path/to/agentos ./build-codex-wasip1.sh set -uo pipefail CODEX="${CODEX:-/home/nathan/agent-e2e/codex-rs/codex-rs}" -SECEXEC="${SECEXEC:-/home/nathan/agent-e2e/secure-exec}" +SECEXEC="${SECEXEC:-/home/nathan/agent-e2e/agentos}" STUBS="$SECEXEC/toolchain/stubs" WSDK="$SECEXEC/toolchain/c/vendor/wasi-sdk" TOOLCHAIN="nightly-2026-03-01" diff --git a/toolchain/std-patches/codex/0005-wasi-build-sysroot-cleanup.patch b/toolchain/std-patches/codex/0005-wasi-build-sysroot-cleanup.patch index 35e2150f3e..fae5387dbe 100644 --- a/toolchain/std-patches/codex/0005-wasi-build-sysroot-cleanup.patch +++ b/toolchain/std-patches/codex/0005-wasi-build-sysroot-cleanup.patch @@ -1,6 +1,6 @@ --- a/scripts/build-wasi-codex-exec.sh +++ b/scripts/build-wasi-codex-exec.sh -@@ -33,6 +33,7 @@ INSTALL="${INSTALL:-${SECURE_EXEC_DIR:+1}}" +@@ -34,5 +34,6 @@ SYSROOT="$(rustc "+$TOOLCHAIN" --print sysroot)" LIBDIR="$SYSROOT/lib/rustlib/wasm32-wasip1/lib" STASH="$LIBDIR/.prebuilt-stash" diff --git a/toolchain/std-patches/crates/rustls-native-certs/0001-wasi-empty-certs.patch b/toolchain/std-patches/crates/rustls-native-certs/0001-wasi-empty-certs.patch index 4bf12668bd..388b164735 100644 --- a/toolchain/std-patches/crates/rustls-native-certs/0001-wasi-empty-certs.patch +++ b/toolchain/std-patches/crates/rustls-native-certs/0001-wasi-empty-certs.patch @@ -2,7 +2,7 @@ # for `load_native_certs()` and has NO wasm/wasi arm -> on wasm32-wasip1 the # `platform` module is undefined and the crate fails to compile (unresolved import # in the top-level `load_native_certs`). wasm32-wasip1 has no native OS cert store; -# TLS is brokered host-side by the secure-exec VM (host CAs), so the in-guest rustls +# TLS is brokered host-side by the agentos VM (host CAs), so the in-guest rustls # never validates certificates. Add a `target_os = "wasi"` platform arm that returns # an empty CertificateResult so the crate compiles (compile/link-only for codex; the # guest never performs cert validation). Pipeline-only codex WASI port. @@ -13,7 +13,7 @@ use macos as platform; +// wasm32-wasip1: no native OS cert store. TLS is brokered host-side by the -+// secure-exec VM (host CAs), so the in-guest rustls never validates certificates. ++// agentos VM (host CAs), so the in-guest rustls never validates certificates. +// Provide an empty native-cert loader so the crate compiles. +#[cfg(target_os = "wasi")] +mod platform { diff --git a/toolchain/std-patches/crates/tokio/VERIFIED-tokio-process-wasi.md b/toolchain/std-patches/crates/tokio/VERIFIED-tokio-process-wasi.md index cdea85d0df..b9f5cc0314 100644 --- a/toolchain/std-patches/crates/tokio/VERIFIED-tokio-process-wasi.md +++ b/toolchain/std-patches/crates/tokio/VERIFIED-tokio-process-wasi.md @@ -4,7 +4,7 @@ Proven in isolation (/tmp/tokio-dev + [patch], --cfg tokio_unstable, -Z build-st `Command::new("echo").arg("hi").output().await` COMPILES for wasm32-wasip1. This de-risks codex's entire exec path under the pipeline-only approach. -## The exact changes (to capture as secure-exec patches): +## The exact changes (to capture as agentos patches): ### A. tokio crate patch (std-patches/crates/tokio/): 1. src/macros/cfg.rs — in `macro_rules! cfg_process`, remove `#[cfg(not(target_os = "wasi"))]`. diff --git a/toolchain/std-patches/crates/tokio/std-wasi-childpipe-fd.rs b/toolchain/std-patches/crates/tokio/std-wasi-childpipe-fd.rs index c778628f54..1f841acd16 100644 --- a/toolchain/std-patches/crates/tokio/std-wasi-childpipe-fd.rs +++ b/toolchain/std-patches/crates/tokio/std-wasi-childpipe-fd.rs @@ -1,6 +1,6 @@ //! std patch artifact (pipeline-only codex port) — wasi public child-pipe fd traits. //! -//! PROBLEM (real secure-exec bug, found 2026-06-23): the patched wasi std provides +//! PROBLEM (real agentos bug, found 2026-06-23): the patched wasi std provides //! the SEOS process impl (`sys/process/wasi.rs`, `ChildPipe(FileDesc)`) but NOT the //! public `process::{ChildStdin,ChildStdout,ChildStderr}` fd traits — `os/unix/process.rs` //! has them (AsRawFd/IntoRawFd/AsFd/From<_> for OwnedFd) but there is no `os/wasi` diff --git a/toolchain/std-patches/std/os/wasi/process.rs b/toolchain/std-patches/std/os/wasi/process.rs index 5b506abe48..9d5c4ff6a7 100644 --- a/toolchain/std-patches/std/os/wasi/process.rs +++ b/toolchain/std-patches/std/os/wasi/process.rs @@ -2,7 +2,7 @@ //! //! Mirrors `os/unix/process.rs`' child-pipe fd traits for wasm32-wasip1 so that //! `tokio::process` (and other fd-extracting code) can reach the parent-side -//! pipe ends of a spawned child. (secure-exec pipeline-only codex port.) +//! pipe ends of a spawned child. (agentos pipeline-only codex port.) //! //! [`std::process`]: crate::process @@ -84,7 +84,7 @@ impl_child_pipe_fd!(process::ChildStdout); impl_child_pipe_fd!(process::ChildStderr); /// WASI-specific extension to construct an [`ExitStatus`] from a raw code, -/// mirroring `std::os::unix::process::ExitStatusExt::from_raw`. (secure-exec +/// mirroring `std::os::unix::process::ExitStatusExt::from_raw`. (agentos /// pipeline-only codex port — codex's synthetic exit statuses need this.) #[stable(feature = "rust1", since = "1.0.0")] pub trait ExitStatusExt { diff --git a/toolchain/std-patches/wasi-libc/0008-sockets.patch b/toolchain/std-patches/wasi-libc/0008-sockets.patch index 8c2e33fb0a..c43aaaefc6 100644 --- a/toolchain/std-patches/wasi-libc/0008-sockets.patch +++ b/toolchain/std-patches/wasi-libc/0008-sockets.patch @@ -28,7 +28,7 @@ Import signatures match wasmvm/crates/wasi-ext/src/lib.rs exactly. - $TARGET_TRIPLE == *"wasip1" || $TARGET_TRIPLE == *"wasip1-threads" ]]; then - MUSL_OMIT_HEADERS+=("netdb.h") -fi -+# NOTE: commented out by the secure-exec 0008-sockets patch — we provide getaddrinfo via host_net ++# NOTE: commented out by the agentos 0008-sockets patch — we provide getaddrinfo via host_net +#if [[ $TARGET_TRIPLE == *"wasi" || $TARGET_TRIPLE == *"wasi-threads" || \ +# $TARGET_TRIPLE == *"wasip1" || $TARGET_TRIPLE == *"wasip1-threads" ]]; then +# MUSL_OMIT_HEADERS+=("netdb.h") diff --git a/toolchain/std-patches/wasi-libc/0013-posix-socket-header-surface.patch b/toolchain/std-patches/wasi-libc/0013-posix-socket-header-surface.patch index 4d5b655f8c..fe3de40a29 100644 --- a/toolchain/std-patches/wasi-libc/0013-posix-socket-header-surface.patch +++ b/toolchain/std-patches/wasi-libc/0013-posix-socket-header-surface.patch @@ -1,5 +1,5 @@ Expose the socket option and poll constants needed by POSIX networking -applications when secure-exec routes sockets through host_net on WASI p1. +applications when agentos routes sockets through host_net on WASI p1. This keeps the WASI-native AF_/SOCK_ values, but publishes the common socket-level constants and non-conflicting extra poll bits that upstream @@ -34,7 +34,7 @@ software such as curl expects to find in and . #endif // __wasilibc_use_wasip2 +/* -+ * secure-exec exposes a POSIX-style socket layer over host_net for WASI p1. ++ * agentos exposes a POSIX-style socket layer over host_net for WASI p1. + * Keep the WASI-native domain/type values below, but expose the common + * socket option and message flag constants that networking applications + * expect to find in . diff --git a/toolchain/std-patches/wasi-libc/0029-openssh-compat-header-surface.patch b/toolchain/std-patches/wasi-libc/0029-openssh-compat-header-surface.patch index 8d576f0037..053de1379d 100644 --- a/toolchain/std-patches/wasi-libc/0029-openssh-compat-header-surface.patch +++ b/toolchain/std-patches/wasi-libc/0029-openssh-compat-header-surface.patch @@ -179,5 +179,5 @@ index 8813360..9a740ad 100755 +# sshbuf-misc.c) include unconditionally. No resolver code is +# built; linking res_* still fails, which is the honest surface. # Exclude `netdb.h` from all of the p1 targets. - # NOTE: commented out by the secure-exec 0008-sockets patch — we provide getaddrinfo via host_net + # NOTE: commented out by the agentos 0008-sockets patch — we provide getaddrinfo via host_net #if [[ $TARGET_TRIPLE == *"wasi" || $TARGET_TRIPLE == *"wasi-threads" || \ diff --git a/toolchain/stubs/portable-pty-wasi/src/wasi.rs b/toolchain/stubs/portable-pty-wasi/src/wasi.rs index f6c5f07987..f44cf80651 100644 --- a/toolchain/stubs/portable-pty-wasi/src/wasi.rs +++ b/toolchain/stubs/portable-pty-wasi/src/wasi.rs @@ -1,6 +1,6 @@ //! wasm32-wasip1 backend stub for portable-pty. //! -//! The secure-exec VM brokers process execution via wasi-spawn/wasi-pty; a full +//! The agentos VM brokers process execution via wasi-spawn/wasi-pty; a full //! PTY bridge is future work. This backend lets portable-pty (and thus //! codex-utils-pty / codex-core) COMPILE for wasip1. PTY operations return an //! Unsupported error at runtime (codex's exec falls back to non-PTY execution). diff --git a/toolchain/stubs/reqwest-shim/Cargo.toml b/toolchain/stubs/reqwest-shim/Cargo.toml index f597af2fe8..f9d7879ab9 100644 --- a/toolchain/stubs/reqwest-shim/Cargo.toml +++ b/toolchain/stubs/reqwest-shim/Cargo.toml @@ -1,4 +1,4 @@ -# reqwest-shim: a wasm32-wasip1 drop-in for `reqwest` 0.12, backed by secure-exec +# reqwest-shim: a wasm32-wasip1 drop-in for `reqwest` 0.12, backed by agentos # `wasi-http` (host_net TCP/TLS). Wired in via `[patch.crates-io] reqwest = { path }` # ONLY once it compiles against codex-exec's whole subtree — patching it in early # would break every command's `make wasm` build. @@ -19,7 +19,7 @@ name = "reqwest" path = "src/lib.rs" [dependencies] -wasi-http = { package = "secureexec-wasi-http", path = "../../crates/libs/wasi-http" } +wasi-http = { package = "agentos-wasi-http", path = "../../crates/libs/wasi-http" } http = "1" bytes = "1" serde = "1" diff --git a/toolchain/stubs/reqwest-shim/src/lib.rs b/toolchain/stubs/reqwest-shim/src/lib.rs index aee774f00a..06fdaf0f45 100644 --- a/toolchain/stubs/reqwest-shim/src/lib.rs +++ b/toolchain/stubs/reqwest-shim/src/lib.rs @@ -1,4 +1,4 @@ -//! `reqwest` 0.12 API shim for wasm32-wasip1, backed by secure-exec `wasi-http` +//! `reqwest` 0.12 API shim for wasm32-wasip1, backed by agentos `wasi-http` //! (host_net TCP/TLS). Drop-in target for `[patch.crates-io] reqwest`. //! //! STATUS: scaffold. The buffered request/response path is implemented against diff --git a/toolchain/test-programs/sqlite3_mem.c b/toolchain/test-programs/sqlite3_mem.c index 9d7eef32f7..ce60717205 100644 --- a/toolchain/test-programs/sqlite3_mem.c +++ b/toolchain/test-programs/sqlite3_mem.c @@ -103,7 +103,7 @@ static sqlite3_vfs memVfs __attribute__((used)) = { sizeof(MemFile), 512, 0, - "secure-exec-mem", + "agentos-mem", 0, memOpen, memDelete, diff --git a/website/docs.config.mjs b/website/docs.config.mjs index a382cbe3dc..7ffef5ed39 100644 --- a/website/docs.config.mjs +++ b/website/docs.config.mjs @@ -20,7 +20,6 @@ import { faMessages, faCheck, faKey, - faCloud, faDownload, faFloppyDisk, faTerminal, @@ -29,7 +28,6 @@ import { faHardDrive, faNodeJs, faGauge, - faLink, faTowerBroadcast, faArrowsLeftRight, faDiagramNext, @@ -45,17 +43,15 @@ export const siteConfig = { repo: "rivet-dev/agentos", editPath: "website/", - // Keep in sync with the marketing nav (src/components/Navigation.tsx): - // same links in the same order across docs + marketing. + // Cookbooks lives in the docs tab strip below, so do not duplicate it here. topNav: [ { label: "Documentation", href: "/docs", match: "/docs" }, - { label: "Cookbooks", href: "/cookbooks", match: "/cookbooks" }, { label: "Use Cases", href: "/use-cases" }, { label: "Registry", href: "/registry" }, { label: "Deploy", href: "/docs/deployment", match: "/docs/deployment" }, ], tabs: [ - { label: "Documentation", href: "/docs", match: "/docs" }, + { label: "General", href: "/docs", match: "/docs" }, { label: "Cookbooks", href: "/cookbooks", match: "/cookbooks" }, ], social: { discord: "https://rivet.dev/discord" }, @@ -76,7 +72,6 @@ export const siteConfig = { title: "Documentation", href: "/docs", sidebar: [ - { title: "Introduction", href: "/docs", icon: faCircleInfo }, { title: "General", pages: [ @@ -102,8 +97,25 @@ export const siteConfig = { }, { title: "Sessions & Transcripts", href: "/docs/sessions", icon: faMessages }, { title: "Approvals", href: "/docs/approvals", icon: faCheck }, - { title: "LLM Credentials", href: "/docs/llm-credentials", icon: faKey }, - { title: "LLM Gateway", href: "/docs/llm-gateway", badge: "Coming Soon", icon: faCloud }, + { title: "Models & Credentials", href: "/docs/models-and-credentials", icon: faKey }, + ], + }, + { + title: "Execution", + pages: [ + { title: "Bash", href: "/docs/execution/bash", icon: faTerminal }, + { title: "JavaScript", href: "/docs/execution/javascript", icon: faNodeJs }, + { title: "Python", href: "/docs/execution/python", icon: faTerminal }, + ], + }, + { + title: "Orchestration", + pages: [ + { title: "Authentication", href: "/docs/authentication", icon: faKey }, + { title: "Multiplayer", href: "/docs/multiplayer", icon: faTowerBroadcast }, + { title: "Workflows & Graphs", href: "/docs/workflows", icon: faDiagramNext }, + { title: "Crons & Loops", href: "/docs/cron", icon: faClock }, + { title: "Agent-to-Agent", href: "/docs/agent-to-agent", icon: faArrowsLeftRight }, ], }, { @@ -111,25 +123,18 @@ export const siteConfig = { pages: [ { title: "Software", href: "/docs/software", icon: faDownload }, { title: "Filesystem", href: "/docs/filesystem", icon: faFloppyDisk }, - { title: "Bindings", href: "/docs/bindings", icon: faWrench }, - { title: "Processes & Shell", href: "/docs/processes", icon: faTerminal }, + { title: "Processes & Shells", href: "/docs/processes", icon: faTerminal }, { title: "Networking & Previews", href: "/docs/networking", icon: faGlobe }, - { title: "Cron Jobs", href: "/docs/cron", icon: faClock }, - { title: "Browser", href: "/docs/browser", badge: "Beta", icon: faWindowMaximize }, - { title: "Sandbox Mounting", href: "/docs/sandbox", badge: "Beta", icon: faHardDrive }, - { title: "JavaScript Runtime", href: "/docs/js-runtime", icon: faNodeJs }, { title: "Permissions", href: "/docs/permissions", icon: faKey }, { title: "Resource Limits", href: "/docs/resource-limits", icon: faGauge }, ], }, { - title: "Orchestration", + title: "Extensions", pages: [ - { title: "Authentication", href: "/docs/authentication", icon: faKey }, - { title: "Webhooks", href: "/docs/webhooks", icon: faLink }, - { title: "Multiplayer & Realtime", href: "/docs/multiplayer", icon: faTowerBroadcast }, - { title: "Agent-to-Agent", href: "/docs/agent-to-agent", icon: faArrowsLeftRight }, - { title: "Workflows", href: "/docs/workflows", icon: faDiagramNext }, + { title: "Custom Bindings", href: "/docs/extensions/custom-bindings", icon: faWrench }, + { title: "Browser Automation", href: "/docs/extensions/browser", badge: "Beta", icon: faWindowMaximize }, + { title: "External Sandboxes", href: "/docs/extensions/sandboxes", badge: "Beta", icon: faHardDrive }, ], }, { @@ -179,10 +184,9 @@ export const siteConfig = { title: "More", collapsible: true, pages: [ - { title: "Core SDK", href: "/docs/core" }, + { title: "Direct VM SDK", href: "/docs/core" }, { title: "Debugging", href: "/docs/debugging" }, - { title: "Benchmarks", href: "/docs/benchmarks" }, - { title: "Cost Evaluation", href: "/docs/cost-evaluation" }, + { title: "Performance", href: "/docs/performance" }, ], }, ], @@ -211,6 +215,15 @@ export const siteConfig = { { title: "Agent to Agent", href: "/cookbooks/agent-to-agent" }, ], }, + { + title: "Code Execution", + pages: [ + { title: "AI Agent Code Exec", href: "/cookbooks/js-ai-agent-code-exec" }, + { title: "Code Mode", href: "/cookbooks/js-code-mode" }, + { title: "Dev Servers", href: "/cookbooks/js-dev-servers" }, + { title: "Plugin Systems", href: "/cookbooks/js-plugin-systems" }, + ], + }, { title: "Filesystem", pages: [{ title: "Filesystem", href: "/cookbooks/filesystem" }], @@ -243,7 +256,6 @@ export const siteConfig = { pages: [ { title: "Cron", href: "/cookbooks/cron" }, { title: "Workflows", href: "/cookbooks/workflows" }, - { title: "Webhooks", href: "/cookbooks/webhooks" }, ], }, { diff --git a/website/public/docs/docs/agent-to-agent.md b/website/public/docs/docs/agent-to-agent.md index 5557b173d0..e3a9a0b1a9 100644 --- a/website/public/docs/docs/agent-to-agent.md +++ b/website/public/docs/docs/agent-to-agent.md @@ -2,7 +2,7 @@ Use bindings to let agents communicate with each other. -Agents communicate through [bindings](/docs/bindings). You define a bindings group that lets one agent send work to another, and the agent calls it like any other CLI command. +Agents communicate through [custom bindings](/docs/extensions/custom-bindings). You define a bindings group that lets one agent send work to another, and the agent calls it like any other CLI command. ## Example: code writer + reviewer diff --git a/website/public/docs/docs/agents/claude.md b/website/public/docs/docs/agents/claude.md index f9b7d27c95..7a9a65863c 100644 --- a/website/public/docs/docs/agents/claude.md +++ b/website/public/docs/docs/agents/claude.md @@ -6,7 +6,7 @@ Run the Claude Code agent inside a VM with skills, MCP servers, and custom confi Read [Sessions](/docs/sessions) first for session options, streaming events, prompts, and lifecycle management. -## LLM Credentials +## Model & credentials Set the relevant variable(s) on the session's `env`, sourced from your server's environment: @@ -17,7 +17,7 @@ Set the relevant variable(s) on the session's `env`, sourced from your server's - `CLAUDE_CODE_USE_BEDROCK=1` — use Amazon Bedrock (auth via the AWS credential chain: `AWS_REGION`, `AWS_PROFILE`, …). - `CLAUDE_CODE_USE_VERTEX=1` — use Google Vertex AI (auth via Google Cloud credentials). -See [LLM Credentials](/docs/llm-credentials), and Claude Code's [environment variables](https://code.claude.com/docs/en/env-vars) for the full list. +See [Models & Credentials](/docs/models-and-credentials), and Claude Code's [environment variables](https://code.claude.com/docs/en/env-vars) for the full list. ## Skills @@ -27,7 +27,7 @@ Claude Code discovers [agent skills](https://docs.claude.com/en/docs/claude-code Expose extra tools to the agent by passing `mcpServers` to `openSession`. Both local child-process servers and remote URLs are supported. -**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. +**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.process.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. ## Customizing the agent diff --git a/website/public/docs/docs/agents/codex.md b/website/public/docs/docs/agents/codex.md index 441d123039..baeacc7081 100644 --- a/website/public/docs/docs/agents/codex.md +++ b/website/public/docs/docs/agents/codex.md @@ -6,7 +6,7 @@ Run the Codex coding agent inside a VM with skills, MCP servers, and custom conf Read [Sessions](/docs/sessions) first for session options, streaming events, prompts, and lifecycle management. -## LLM Credentials +## Model & credentials Set the relevant variable(s) on the session's `env`, sourced from your server's environment: @@ -14,7 +14,7 @@ Set the relevant variable(s) on the session's `env`, sourced from your server's - `OPENAI_BASE_URL` — route through a gateway or OpenAI-compatible endpoint. - Custom providers — defined in `~/.codex/config.toml`; each provider's `env_key` names the variable Codex reads for its key (e.g. `AZURE_OPENAI_API_KEY`, `MISTRAL_API_KEY`). -See [LLM Credentials](/docs/llm-credentials), and Codex's [config reference](https://developers.openai.com/codex/config-reference) for details. +See [Models & Credentials](/docs/models-and-credentials), and Codex's [config reference](https://developers.openai.com/codex/config-reference) for details. ## Skills @@ -24,7 +24,7 @@ Codex discovers `SKILL.md` files from its skills directory. Write the skill into Expose extra tools to the agent by passing `mcpServers` to `openSession`. Both local child-process servers and remote URLs are supported. -**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. +**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.process.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. ## Customizing the agent diff --git a/website/public/docs/docs/agents/opencode.md b/website/public/docs/docs/agents/opencode.md index e72aa1fecc..cb8b9576fa 100644 --- a/website/public/docs/docs/agents/opencode.md +++ b/website/public/docs/docs/agents/opencode.md @@ -6,7 +6,7 @@ Run the OpenCode coding agent inside a VM with skills, MCP servers, and custom c Read [Sessions](/docs/sessions) first for session options, streaming events, prompts, and lifecycle management. -## LLM Credentials +## Model & credentials OpenCode auto-detects a provider when its key is present on the session's `env`, sourced from your server's environment. Common variables: @@ -17,7 +17,7 @@ OpenCode auto-detects a provider when its key is present on the session's `env`, - `GROQ_API_KEY` — Groq. - …plus Amazon Bedrock, Azure, Google Vertex, and 70+ providers via [models.dev](https://models.dev). -See [LLM Credentials](/docs/llm-credentials), and OpenCode's [providers docs](https://opencode.ai/docs/providers/) for the full list. +See [Models & Credentials](/docs/models-and-credentials), and OpenCode's [providers docs](https://opencode.ai/docs/providers/) for the full list. ## Model configuration @@ -29,8 +29,8 @@ Two settings will silently produce an **empty response** if wrong: ```ts // Write the config before creating the session -await agent.mkdir("/home/agentos/.config/opencode", { recursive: true }); -await agent.writeFile( +await agent.filesystem.mkdir("/home/agentos/.config/opencode", { recursive: true }); +await agent.filesystem.writeFile( "/home/agentos/.config/opencode/opencode.json", JSON.stringify({ $schema: "https://opencode.ai/config.json", @@ -42,7 +42,7 @@ await agent.writeFile( }), ); -await agent.openSession({ +await agent.sessions.open({ agent: "opencode", env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! }, }); @@ -56,7 +56,7 @@ OpenCode discovers `SKILL.md` files from its skills directory. Write the skill i Expose extra tools to the agent by passing `mcpServers` to `openSession`. Both local child-process servers and remote URLs are supported. -**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. +**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.process.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. ## Customizing the agent diff --git a/website/public/docs/docs/agents/pi.md b/website/public/docs/docs/agents/pi.md index 539b9c8b89..4334b9d1e2 100644 --- a/website/public/docs/docs/agents/pi.md +++ b/website/public/docs/docs/agents/pi.md @@ -6,14 +6,14 @@ Run the Pi coding agent inside a VM with extensions and custom configuration. Read [Sessions](/docs/sessions) first for session options, streaming events, prompts, and lifecycle management. -## LLM Credentials +## Model & credentials Set the relevant variable on the session's `env`, sourced from your server's environment: - `ANTHROPIC_API_KEY` — Anthropic (Claude), the default. - Other providers — use the provider-named key (e.g. `OPENAI_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY`). -See [LLM Credentials](/docs/llm-credentials), and Pi's [providers docs](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/providers.md) for the full list. +See [Models & Credentials](/docs/models-and-credentials), and Pi's [providers docs](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/providers.md) for the full list. ## Skills @@ -23,7 +23,7 @@ Pi discovers `SKILL.md` files from its skills directory. Write the skill into th Expose extra tools to the agent by passing `mcpServers` to `openSession`. Both local child-process servers and remote URLs are supported. -**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. +**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.process.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. ## Extensions diff --git a/website/public/docs/docs/architecture.md b/website/public/docs/docs/architecture.md index 407eaf4ad4..eb3709ca4c 100644 --- a/website/public/docs/docs/architecture.md +++ b/website/public/docs/docs/architecture.md @@ -91,7 +91,7 @@ The kernel is the single chokepoint. Each kind of guest operation is serviced by The executor is the untrusted half of the VM. It runs the guest code and reaches the kernel for everything else. -- **JavaScript Acceleration.** Guest JavaScript runs on a native V8 runtime (the same engine in Chrome and Node.js, with the full JIT compiler) inside an isolate. This is what we call **JavaScript Acceleration**: the guest's JavaScript executes at native speed, not through an interpreter or a translation shim. It is genuinely fast, and it presents normal Node.js semantics. See [JavaScript Runtime](/docs/nodejs-runtime). +- **JavaScript Acceleration.** Guest JavaScript runs on a native V8 runtime (the same engine in Chrome and Node.js, with the full JIT compiler) inside an isolate. This is what we call **JavaScript Acceleration**: the guest's JavaScript executes at native speed, not through an interpreter or a translation shim. It is genuinely fast, and it presents normal Node.js semantics. See [JavaScript](/docs/execution/javascript). - **WASM alongside it.** The shell (`sh`) and the coreutils behind process execution ship as WebAssembly modules, and you can run your own WASM too. See [POSIX Syscalls](/docs/architecture/posix-syscalls) and the [Compiler Toolchain](/docs/architecture/compiler-toolchain). - **Native binaries.** Tools mounted into the VM run inside the same boundary as everything else. - **No host fallthrough.** The executor holds no capability of its own. For every file read, process spawn, or socket open, it issues a syscall and blocks for the kernel's reply. @@ -197,13 +197,13 @@ The `agentOS()` actor (from `@rivet-dev/agentos`) wraps the raw VM in a [Rivet A - **Recurring work.** Schedule a shell command or an agent session on a cron expression. - **Overlap control.** Choose what happens when a run is still going when the next is due (`allow`, `skip`, or `queue`). -- **Observable.** Stream `cronEvent`s to watch executions. See [Cron Jobs](/docs/cron). +- **Observable.** Stream `cronEvent`s to watch executions. See [Crons & Loops](/docs/cron). ### Workflows - **Durable multi-step tasks.** A workflow is the actor's `run` handler wrapped in `workflow()`, where each `ctx.step()` is recorded, retried, and resumed independently. - **Crash-proof.** If the process dies mid-run, replay skips completed steps and continues where it left off. -- **Composable.** The output of one step feeds the next: clone a repo, let an agent fix a bug, run the tests. See [Workflow Automation](/docs/workflows). +- **Composable.** The output of one step feeds the next: clone a repo, let an agent fix a bug, run the tests. See [Workflows & Graphs](/docs/workflows). ### Persistence & sleep/wake diff --git a/website/public/docs/docs/architecture/agent-sessions.md b/website/public/docs/docs/architecture/agent-sessions.md index 85d5c41f5d..566ec61ba9 100644 --- a/website/public/docs/docs/architecture/agent-sessions.md +++ b/website/public/docs/docs/architecture/agent-sessions.md @@ -28,7 +28,7 @@ The sidecar—not the actor or SDK—owns defaults and orchestration. TypeScript One prompt may run per session. Cancellation races are first-writer-wins. AgentOS does not automatically replay interrupted prompts because tool side effects may already have occurred. -The ACP runtime and core SDK transports impose no wall-clock deadline on prompts or human permission waits. After 30 seconds without ACP activity, AgentOS logs an inactivity warning with the total elapsed time and last observed activity, repeating every 30 seconds until activity resumes. Streaming output resets that interval. Bootstrap, teardown, filesystem, terminal, and other machine-to-machine operations retain bounded failure deadlines. Actor-hosted calls currently inherit RivetKit's maximum timer-safe action bound of about 24.8 days because RivetKit does not yet support an unbounded action. +The ACP runtime and AgentOS client transports impose no wall-clock deadline on prompts or human permission waits. After 30 seconds without ACP activity, AgentOS logs an inactivity warning with the total elapsed time and last observed activity, repeating every 30 seconds until activity resumes. Streaming output resets that interval. Bootstrap, teardown, filesystem, terminal, and other machine-to-machine operations retain bounded failure deadlines. Actor-hosted calls currently inherit RivetKit's maximum timer-safe action bound of about 24.8 days because RivetKit does not yet support an unbounded action. ## Reads versus adapter operations diff --git a/website/public/docs/docs/architecture/compiler-toolchain.md b/website/public/docs/docs/architecture/compiler-toolchain.md index cefb2222a9..5124083504 100644 --- a/website/public/docs/docs/architecture/compiler-toolchain.md +++ b/website/public/docs/docs/architecture/compiler-toolchain.md @@ -88,7 +88,7 @@ The same path is open to your own programs. A program you compile for `wasm32-wasip1` runs as a guest command exactly like the bundled ones; link the `wasi-ext` bindings if it needs processes, users, or sockets, and leave them out for a pure-compute tool. Heavy native binaries that are not yet available as -WASM belong in a [mounted sandbox](/docs/sandbox) instead. +WASM belong in an [external sandbox](/docs/extensions/sandboxes) instead. ## Recommendations diff --git a/website/public/docs/docs/architecture/javascript-executor.md b/website/public/docs/docs/architecture/javascript-executor.md index c3da35a00a..d7fc671e27 100644 --- a/website/public/docs/docs/architecture/javascript-executor.md +++ b/website/public/docs/docs/architecture/javascript-executor.md @@ -10,7 +10,7 @@ transport. For socket ownership, loopback, DNS, and network policy, see [Networking](/docs/architecture/networking). For the client-facing JavaScript -environment, see [JavaScript Runtime](/docs/js-runtime). +environment, see [JavaScript](/docs/execution/javascript). Tokio never invokes guest JavaScript from a runtime worker. A Tokio task stores bounded work and publishes durable readiness. The separate V8 executor thread diff --git a/website/public/docs/docs/architecture/packages-and-command-resolution.md b/website/public/docs/docs/architecture/packages-and-command-resolution.md index d60b9e563a..d813ec103b 100644 --- a/website/public/docs/docs/architecture/packages-and-command-resolution.md +++ b/website/public/docs/docs/architecture/packages-and-command-resolution.md @@ -63,7 +63,7 @@ is what the runtime **projects** from that package when it mounts it: | `share/` | FHS data — `share/man/man/*`, etc. | | `current` | Symlink `→ `; switching versions is one atomic rename. | -```jsonc +```json // package.json — commands come from "bin"; an agent's ACP entrypoint is just one of them { "name": "@agentos-software/pi", "version": "0.0.1", "bin": { "pi-acp": "dist/pi-acp.wasm" } } ``` diff --git a/website/public/docs/docs/architecture/processes.md b/website/public/docs/docs/architecture/processes.md index 6d65a445cf..fbf5c20153 100644 --- a/website/public/docs/docs/architecture/processes.md +++ b/website/public/docs/docs/architecture/processes.md @@ -6,7 +6,7 @@ Internals of the kernel process model: the virtual process table, how spawns are This page is an internals deep-dive on the kernel's **process model**: the data structures and syscall paths behind every guest process. For the client-facing -API (`exec`, `spawn`, `openShell`, lifecycle, the process tree), see +API (`process.exec`, `process.spawn`, `terminal.open`, lifecycle, the process tree), see [Processes & Shell](/docs/processes). For the surrounding component and trust model, see [Architecture](/docs/architecture). @@ -80,10 +80,10 @@ An interactive shell needs a terminal, not just piped stdio: line editing, job control signals, and window size all depend on a PTY. The kernel provides virtual PTY devices for this. -- **A shell is a process plus a PTY.** `openShell` allocates a kernel PTY and starts a shell process attached to it, returning a `shellId`. The PTY is a virtualized terminal device, never a host `/dev/pts` entry. -- **Bidirectional terminal I/O.** `writeShell` feeds keystrokes into the PTY master side; everything the shell and its children emit comes back as `shellData` events. This carries terminal control sequences, so full-screen TUIs behave correctly. -- **Resize is a terminal operation.** `resizeShell` updates the PTY's window size (columns and rows), which the kernel propagates to the foreground process the way a real terminal resize would, so programs relying on `TIOCGWINSZ`-style sizing redraw correctly. -- **Teardown.** `closeShell` tears down the PTY and the attached shell process. An open shell keeps the VM active, the same way an open PTY keeps a session alive on a real system. +- **A shell is a process plus a PTY.** `terminal.open` allocates a kernel PTY and starts a shell process attached to it, returning a `shellId`. The PTY is a virtualized terminal device, never a host `/dev/pts` entry. +- **Bidirectional terminal I/O.** `terminal.write` feeds keystrokes into the PTY master side; everything the shell and its children emit comes back as `shellData` events. This carries terminal control sequences, so full-screen TUIs behave correctly. +- **Resize is a terminal operation.** `terminal.resize` updates the PTY's window size (columns and rows), which the kernel propagates to the foreground process the way a real terminal resize would, so programs relying on `TIOCGWINSZ`-style sizing redraw correctly. +- **Teardown.** `terminal.close` tears down the PTY and the attached shell process. An open shell keeps the VM active, the same way an open PTY keeps a session alive on a real system. ## WASM sh and coreutils on the process model diff --git a/website/public/docs/docs/core.md b/website/public/docs/docs/core.md index f3af996317..8d126bf0ac 100644 --- a/website/public/docs/docs/core.md +++ b/website/public/docs/docs/core.md @@ -1,12 +1,13 @@ -# Core Package +# Direct VM API -Use @rivet-dev/agentos-core standalone for direct VM control without the Rivet Actor runtime. +Use the direct AgentOS VM API or layer it into an AgentOS actor. -## agentOS vs agentOS Core +## Direct VM vs actor -The `agentOS()` actor (from `@rivet-dev/agentos`) wraps the core package and adds: +`@rivet-dev/agentos` includes both the direct `AgentOs` VM API and the +`agentOS()` actor API: -| | Core (`@rivet-dev/agentos-core`) | Actor (`@rivet-dev/agentos`) | +| | Direct VM | Actor | |-|---|---| | Persistence | In-memory by default (pluggable via [mounts](#mounts)) | Persistent filesystem and sessions | | Distributed state | Manage yourself | Built-in distributed statefulness | @@ -19,16 +20,16 @@ The `agentOS()` actor (from `@rivet-dev/agentos`) wraps the core package and add | Agent-to-agent communication | Custom | Built into [Rivet Actors](/docs/agent-to-agent) | | Authentication | Set up yourself | [Documentation](/docs/authentication) | -We recommend using [Rivet Actors](https://rivet.dev/docs/actors) because they provide a portable way to run `agentOS()` on any infrastructure with built-in persistence, networking, and orchestration. Use the core package if you need the most bare-bones implementation possible. +We recommend using [Rivet Actors](https://rivet.dev/docs/actors) when you need persistence, networking, and orchestration. Use `AgentOs.create()` from the same package when you need direct VM control in a Node.js process. -`agentOS()` returns an ordinary TypeScript Rivet actor definition. Its config accepts the core VM options together with normal actor state, actions, events, queues, connection types, and lifecycle hooks such as `onBeforeConnect`. AgentOS actions and events are merged in automatically; their names are reserved so they cannot be accidentally shadowed. After a wake, the actor creates the core SDK VM lazily on the first AgentOS action and disposes it on sleep. This lets a connection subscribe before the `vmBooted` event is emitted. +`agentOS()` returns an ordinary TypeScript Rivet actor definition. Its config accepts the VM options together with normal actor state, actions, events, queues, connection types, and lifecycle hooks such as `onBeforeConnect`. AgentOS actions and events are merged in automatically; their names are reserved so they cannot be accidentally shadowed. After a wake, the actor creates its VM lazily on the first AgentOS action and disposes it on sleep. This lets a connection subscribe before the `vmBooted` event is emitted. Creation input is inferred from the actor definition and is passed through normal client creation options: `client.vm.create("key", { input })`. The same input reaches `createState(c, input)` and `onCreate(c, input)`. ## Install ```bash -npm install @rivet-dev/agentos-core +npm install @rivet-dev/agentos ``` ## Boot a VM @@ -37,11 +38,11 @@ Create a VM and drive it directly — no actor runtime, no client/server split. ## Sidecar process -Every VM runs inside a **shared sidecar process** rather than a process of its own. By default all VMs are tenants of a single, process-global sidecar (the `default` pool), so each additional VM only adds its marginal cost — a V8 isolate plus its kernel state — instead of a whole OS process. This is what keeps per-VM memory in the tens of MB and warm VM creation in the single-digit milliseconds (see [Benchmarks](/docs/benchmarks)). +Every VM runs inside a **shared sidecar process** rather than a process of its own. By default all VMs are tenants of a single, process-global sidecar (the `default` pool), so each additional VM only adds its marginal cost — a V8 isolate plus its kernel state — instead of a whole OS process. This is what keeps per-VM memory in the tens of MB and warm VM creation in the single-digit milliseconds (see [Performance](/docs/performance)). This is automatic — `agentOS()` and `AgentOs.create()` use the shared default sidecar with no configuration, and the same applies to Rivet Actors (each actor's VM is a tenant of the shared process). Disposing a VM tears down only that VM; the shared sidecar process is reused across VMs and stays alive for the lifetime of the host process. -For advanced cases the core package exposes explicit sidecar handles so you can isolate a group of VMs in their own process: +For advanced cases the direct VM API exposes explicit sidecar handles so you can isolate a group of VMs in their own process: ## Filesystem @@ -78,8 +79,8 @@ The top-level fields are documented inline above. See [Mounts](#mounts) and [Sof ### Session events -With the core package, `onSessionEvent` receives a generic union containing exact native ACP `SessionUpdate`, `RequestPermissionRequest`, and `RequestPermissionResponse` payloads wrapped with AgentOS durability metadata. Register it before prompting. On reconnect, also read durable history after your last sequence and deduplicate by `(sessionId, sequence)`: +With the direct VM API, `onSessionEvent` receives a generic union containing exact native ACP `SessionUpdate`, `RequestPermissionRequest`, and `RequestPermissionResponse` payloads wrapped with AgentOS durability metadata. Register it before prompting. On reconnect, also read durable history after your last sequence and deduplicate by `(sessionId, sequence)`: ### Timeouts and sleep -Action timeouts and automatic sleep/wake are features of the [`agentOS()` actor](/docs/quickstart), not the core package. A core VM stays alive until you call `dispose()`. See [Persistence & Sleep](/docs/persistence) for the actor's sleep lifecycle. \ No newline at end of file +Action timeouts and automatic sleep/wake are features of the [`agentOS()` actor](/docs/quickstart), not the direct VM API. A direct VM stays alive until you call `dispose()`. See [Persistence & Sleep](/docs/persistence) for the actor's sleep lifecycle. \ No newline at end of file diff --git a/website/public/docs/docs/cost-evaluation.md b/website/public/docs/docs/cost-evaluation.md deleted file mode 100644 index 70eb8a1b47..0000000000 --- a/website/public/docs/docs/cost-evaluation.md +++ /dev/null @@ -1,52 +0,0 @@ -# Cost Evaluation - -How agentOS compares on cost to per-second sandbox providers when you run coding-agent VMs on your own hardware. - -agentOS is a library you run on hardware you already control, not a metered service. That changes the cost model for running coding-agent VMs from "pay a provider per sandbox-second" to "pay for the compute you provision, then pack as much work onto it as it can hold." This page explains where the savings come from and how to reason about them honestly. It does not publish a single magic multiplier, because the real number depends on your workload, your hardware, and how you share VMs. - -For measured latency (cold start, warm execution, and reuse fast paths), see [Benchmarks](/docs/benchmarks). This page is about cost structure, not raw performance. - -## Where the savings come from - -Two structural differences drive the cost gap versus per-second sandbox providers: - -- **You run on your own hardware**: you choose the cloud, instance type, architecture, and region. A small commodity instance (for example an ARM VM from a budget host) costs a flat hourly or monthly rate that is typically far below what per-sandbox-second billing adds up to once you have steady agent traffic. You also avoid egress fees and vendor lock-in. -- **You decide the isolation granularity**: sandbox providers bill a full container or microVM per execution, usually with a minimum memory reservation that you pay for even when your code uses a fraction of it. With agentOS you own the VM lifecycle: you can dedicate a VM per tenant or per task for maximum isolation, or amortize setup by reusing one VM across many runs. - -## The isolation model matters for cost - -Each `AgentOs.create()` boots a fully virtualized VM, and each `exec()` / `run()` inside it is a fresh guest process. That gives you a dial between isolation and density: - -- **One VM per task or tenant (strongest isolation)**: create a VM, run the work, and dispose it, or give each tenant its own VM. Each VM is its own crash and resource domain, with the highest per-VM overhead. Best when load is untrusted or bursty. -- **A shared VM for trusted work**: reuse one VM across many runs to amortize the VM boot cost. Each `exec()` / `run()` still executes in a fresh guest process, so in-memory state does not leak between runs, but the VM and filesystem are shared. Good for trusted, sequential work. - -The denser you can safely pack agent work onto an instance, the lower your effective cost per execution. See [Resource Limits](/docs/resource-limits) for the per-VM caps that govern how densely you can pack, and [Processes & Shell](/docs/processes) for how guest processes run inside a VM. - -## How to estimate your own cost - -Because agentOS runs on hardware you provision, the honest way to compare is to plug in your own numbers: - -1. **Pick your hardware and its rate**: take the hourly or monthly price of the instance you would run on, and divide down to a per-second instance cost. -2. **Estimate how many concurrent VMs fit**: measure per-VM memory overhead on your target hardware under your isolation strategy, then divide your usable RAM by that figure. Leave headroom (the measurement and any orchestration layer will not bin-pack perfectly). -3. **Divide instance cost by concurrent VMs**: that gives a cost-per-VM-second you can compare against a provider's per-sandbox-second rate. - -Measure on the hardware and isolation strategy you will actually deploy. Per-VM overhead depends on whether you create a fresh VM per task or reuse one across runs, and on the work the agent does, so a number measured on one machine will not transfer cleanly to another. - -## Comparing against sandbox providers - -When you do compare against a per-second sandbox provider, hold the methodology honest: - -- **Sandbox cost** is the provider's minimum allocatable memory times their per-GiB-second rate (plus any egress and platform fees). The minimum reservation is the floor you pay even for tiny workloads. -- **agentOS cost** is your instance cost per second divided by the number of VMs you can keep live on it, with realistic headroom for bin-packing inefficiency. - -The advantage is largest for **many small, short executions**, where a per-sandbox minimum reservation dominates and your own hardware lets you pack densely. It narrows for **heavyweight, long-lived workloads** (for example dev servers that need hundreds of megabytes regardless), where the win shifts from density to hardware choice: you still avoid per-second metering, egress fees, and lock-in, but the raw memory-density advantage is smaller. - -| Workload | Primary cost advantage | -| --- | --- | -| Many small, short executions | Density: pack many VMs per instance, no per-sandbox minimum | -| Heavyweight, long-lived workloads | Hardware choice, flat instance pricing, no egress or lock-in | -| High concurrency | Reuse a VM across runs to amortize VM boot cost | - -Be careful generalizing cost ratios from a single benchmark. Provider pricing, instance pricing, and exchange rates change over time, and per-VM overhead varies by workload and isolation strategy. Re-measure on your own hardware before quoting a number. - -When you do need a full Linux sandbox for heavier agent workloads, see [agentOS vs Sandbox](/docs/versus-sandbox) for how the two models combine. \ No newline at end of file diff --git a/website/public/docs/docs/crash-course.md b/website/public/docs/docs/crash-course.md index ae308cd24a..0807021294 100644 --- a/website/public/docs/docs/crash-course.md +++ b/website/public/docs/docs/crash-course.md @@ -30,25 +30,25 @@ Approve or deny agent tool use with human-in-the-loop patterns or auto-approve f *See [Full Example](https://github.com/rivet-dev/agentos/tree/main/examples/crash-course) or [Documentation](/docs/approvals)* -### Bindings +### Custom Bindings Expose your JavaScript functions to agents as CLI commands inside the VM. Each binding group becomes a binary at `/usr/local/bin/agentos-{name}`, and each binding becomes a subcommand with flags auto-generated from its Zod input schema. The server below defines a `weather` binding group with a `forecast` binding; the client opens a session and prompts the agent, which calls the binding itself as a shell command. -*See [Full Example](https://github.com/rivet-dev/agentos/tree/main/examples/bindings) or [Documentation](/docs/bindings)* +*See [Full Example](https://github.com/rivet-dev/agentos/tree/main/examples/bindings) or [Documentation](/docs/extensions/custom-bindings)* ### Agent-to-Agent -Let one agent call another through a [binding](/docs/bindings). The coder gets a `review` binding it invokes itself, which bridges into the reviewer's isolated VM. +Let one agent call another through a [custom binding](/docs/extensions/custom-bindings). The coder gets a `review` binding it invokes itself, which bridges into the reviewer's isolated VM. *See [Full Example](https://github.com/rivet-dev/agentos/tree/main/examples/crash-course) or [Documentation](/docs/agent-to-agent)* -### Multiplayer & Realtime +### Multiplayer Connect multiple clients to the same agent VM. All subscribers see session output, process logs, and shell data in realtime. *See [Full Example](https://github.com/rivet-dev/agentos/tree/main/examples/crash-course) or [Documentation](/docs/multiplayer)* -### Workflows +### Workflows & Graphs Orchestrate multi-step agent tasks with durable workflows that survive crashes and restarts. @@ -74,14 +74,14 @@ Proxy HTTP requests into VMs with `httpRequest`. Create actor-namespaced preview *See [Full Example](https://github.com/rivet-dev/agentos/tree/main/examples/crash-course) or [Documentation](/docs/networking)* -### Cron Jobs +### Crons & Loops Schedule recurring commands and agent sessions with cron expressions. *See [Full Example](https://github.com/rivet-dev/agentos/tree/main/examples/crash-course) or [Documentation](/docs/cron)* -### Sandbox Mounting +### External Sandboxes agentOS uses a hybrid model: agents run in a lightweight VM by default and mount a full sandbox on demand for heavy workloads like browsers, compilation, and desktop automation. Sandboxes are powered by [Sandbox Agent](https://sandboxagent.dev), so you can swap providers without changing agent code. Mount the sandbox as a filesystem and expose its process management as bindings. -[Documentation](/docs/sandbox) \ No newline at end of file +[Documentation](/docs/extensions/sandboxes) \ No newline at end of file diff --git a/website/public/docs/docs/cron.md b/website/public/docs/docs/cron.md index e4a7fa69b9..57c20ed905 100644 --- a/website/public/docs/docs/cron.md +++ b/website/public/docs/docs/cron.md @@ -1,8 +1,12 @@ -# Cron Jobs +# Crons & Loops Schedule recurring commands and agent sessions in agentOS VMs. -Schedule recurring work with cron expressions, running either a shell command (`exec`) or an agent session (`session`), with overlap modes (`allow`, `skip`, `queue`) and native `cronEvent` streaming to monitor execution. Cron jobs keep the actor alive while a job runs; the actor can sleep between executions. +Schedule recurring work and long-running agent loops with cron expressions, +running either a shell command (`exec`) or an agent session (`session`), with +overlap modes (`allow`, `skip`, `queue`) and native `cronEvent` streaming to +monitor execution. Cron jobs keep the actor alive while a job runs; the actor +can sleep between executions. ## Schedule a command diff --git a/website/public/docs/docs/custom-software/definition.md b/website/public/docs/docs/custom-software/definition.md index ed25f6f09e..94b51898e2 100644 --- a/website/public/docs/docs/custom-software/definition.md +++ b/website/public/docs/docs/custom-software/definition.md @@ -107,7 +107,7 @@ packed files, so the JSON never ships into the VM and the metadata never travels command/WASM packages it is **generated** for you (name from `package.json`); for agents you author the `agent` block (or `agentos-toolchain pack --agent ` writes it). -```jsonc +```json { "name": "my-agent", // → /opt/agentos/ "agent": { // optional — also exposes an agent session diff --git a/website/public/docs/docs/deployment.md b/website/public/docs/docs/deployment.md index df6a8d45cb..dd6e9a2620 100644 --- a/website/public/docs/docs/deployment.md +++ b/website/public/docs/docs/deployment.md @@ -6,7 +6,7 @@ agentOS is powered by [Rivet](https://rivet.dev), an open-source actor platform, - **[Rivet Cloud](https://rivet.dev/cloud)**: fully managed (Rivet Compute, or bring your own cloud). Zero-ops. - **Self-hosted**: run the open-source Rivet platform on your own infrastructure (Kubernetes, Hetzner, VMs, and more) for full control. -- **[agentOS Core](/docs/core)**: embed `@rivet-dev/agentos-core` directly in any Node.js backend, no platform required. +- **[Direct VM API](/docs/core)**: embed `@rivet-dev/agentos` directly in any Node.js backend, no platform required. Pick a deploy target below, or see [Rivet's deployment guides](https://rivet.dev/docs/deploy/). diff --git a/website/public/docs/docs/execution/bash.md b/website/public/docs/docs/execution/bash.md new file mode 100644 index 0000000000..5074763bcc --- /dev/null +++ b/website/public/docs/docs/execution/bash.md @@ -0,0 +1,50 @@ +# Bash + +Run shell commands and arbitrary argv operations through the AgentOS execution lifecycle. + +Shell commands are the simplest AgentOS execution surface. Use `process.exec()` +for shell syntax such as pipes and redirects, and `process.execFile()` for +injection-safe command arguments. Both use the same execution lifecycle as +JavaScript, TypeScript, Python, and package workflows. + +## Run commands + +`process.exec()` runs a command through the VM's configured shell and returns +its outcome, exit code, stdout, stderr, and `executionId`. The command runs +inside the VM, never in the host shell. + +Prefer `process.execFile()` when command names or arguments come from data. Each +argument is transferred separately and AgentOS does not interpolate it into a +shell command. Keep `process.exec()` for workflows that intentionally need +shell syntax. + +## Detached and interactive work + +Set `detached: true` on `process.exec()` or `process.execFile()`. Manage the +returned `executionId` with `executions.wait()`, `executions.cancel()`, +`executions.signal()`, stdin, PTY resize, bounded output replay, reset, and +deletion. `process.spawn()` is the separate managed-child-process API: it +returns a PID and is controlled through the remaining `process.*` methods. + +An execution accepts only one active operation. Reusing a running ID fails +immediately. Process-only operations do not pin a retained language, so the +same idle execution can later run JavaScript or Python. + +## Files and software + +Shell commands see the persistent [filesystem](/docs/filesystem) shared by +agents, JavaScript, and Python. Common POSIX commands are available by default, +and additional software is projected through the [software registry](/docs/software). + +## Custom bindings + +[Custom Bindings](/docs/extensions/custom-bindings) appear as commands such as +`agentos-weather forecast`, so shell pipelines can use trusted host +capabilities without putting credentials inside the VM. + +## Permissions, limits, and timeouts + +Every command inherits the VM [permission policy](/docs/permissions) and +[resource limits](/docs/resource-limits). `timeoutMs` sets an operation-level +wall-clock deadline. A denied guest operation receives its normal POSIX error, +and a timed-out execution retains a structured `timed_out` result. \ No newline at end of file diff --git a/website/public/docs/docs/execution/javascript-compatibility.md b/website/public/docs/docs/execution/javascript-compatibility.md new file mode 100644 index 0000000000..eb41f8fcef --- /dev/null +++ b/website/public/docs/docs/execution/javascript-compatibility.md @@ -0,0 +1,74 @@ +# Node.js Compatibility + +Node.js builtins available to JavaScript running inside AgentOS. + +Guest JavaScript never touches the host Node.js runtime. Every `import` or +`require` of a `node:` builtin resolves to a kernel-backed bridge or an +in-isolate implementation, and unknown or denied modules fail explicitly. The +guest reports Node.js `v22.0.0` through `process.version`. + +## How builtins are backed + +- **Kernel-backed:** calls route through the VM filesystem, socket table, + process table, DNS resolver, or entropy source. +- **In-isolate:** pure JavaScript implementations run entirely inside V8 and + require no host access. +- **Denied:** importing the module throws `ERR_ACCESS_DENIED`. + +The canonical inventory lives in +`crates/execution/assets/polyfill-registry.json`. + +A guest never falls through to a real host builtin. Anything not bridged +or implemented in the isolate is denied. + +## Kernel-backed builtins + +| Module | Backed by | +| --- | --- | +| `fs`, `fs/promises` | VM filesystem, including fds, streams, metadata, symlinks, and polling-based watchers. | +| `child_process` | VM process table. `spawn`, `exec`, `execFile`, and sync variants launch guest processes. | +| `net`, `dgram` | VM TCP, Unix-socket, and UDP tables. | +| `dns`, `dns/promises` | VM DNS resolver. | +| `http`, `https`, `http2`, `tls` | VM socket and TLS paths, including clients, servers, and connection pooling. | +| `os` | VM-scoped platform, architecture, hostname, CPU, memory, and user information. | +| `crypto` | Entropy, hashes, HMAC, ciphers, scrypt, UUIDs, and WebCrypto. | +| `process` | VM environment, working directory, signals, timers, stdio, and umask. | +| `module` | `createRequire`, builtin resolution, and basic `Module` compatibility. | +| `console` | Bounded formatting and guest stdout/stderr. | +| `readline`, `sqlite`, `tty` | Kernel-backed compatibility surfaces. | +| `timers`, `timers/promises` | Timeout, interval, immediate, and promise variants. | +| `stream/web`, `stream/consumers`, `stream/promises` | Web Streams and stream helpers. | + +Network builtins obey the VM's [permission policy](/docs/permissions). Network +access is denied until the VM creator grants it. + +## In-isolate builtins + +Pure JavaScript implementations include `path`, `buffer`, `events`, `stream`, +`util`, `assert`, `url`, `querystring`, `string_decoder`, `zlib`, `punycode`, +`constants`, and `sys`. Default and named ESM imports are supported. + +Compatibility shims are also provided for common feature detection, including +`async_hooks`, `diagnostics_channel`, `perf_hooks`, `worker_threads`, `vm`, and +`v8`. The `worker_threads` shim does not create real worker threads. + +## Denied builtins + +`cluster`, `domain`, `inspector`, `repl`, `trace_events`, and `wasi` are denied. + +## Global APIs + +Modern web globals are available, including `fetch`, `Headers`, `Request`, +`Response`, `TextEncoder`, `TextDecoder`, `Buffer`, URL APIs, `Blob`, `File`, +`FormData`, abort APIs, `structuredClone`, `performance`, and WebAssembly. +`fetch()` uses the VM socket table and follows the same policy as `http` and +`net`. + +## Modules and output + +Both ESM and CommonJS use the VM filesystem and normal `node_modules` +resolution. Console and stream output is delivered through the same bounded +process-output path described in [Processes & Shells](/docs/processes). + +Return to the [JavaScript guide](/docs/execution/javascript) for TypeScript, +packages, files, processes, networking, bindings, permissions, and limits. \ No newline at end of file diff --git a/website/public/docs/docs/execution/javascript.md b/website/public/docs/docs/execution/javascript.md new file mode 100644 index 0000000000..5c62be329a --- /dev/null +++ b/website/public/docs/docs/execution/javascript.md @@ -0,0 +1,108 @@ +# JavaScript + +Execute JavaScript and TypeScript, install npm dependencies, and manage execution lifecycles in AgentOS. + +AgentOS runs JavaScript in native V8 behind the VM kernel boundary. Use the +first-class language methods for source, files, values, TypeScript, npm +projects, and package binaries. Use `process.execFile()` only when you need an +unusual command that does not have a language-level method. + +## Get started + +`@rivet-dev/agentos-javascript` is the focused JavaScript/TypeScript entry +point. It creates the same AgentOS VM and delegates every operation to the +sidecar-owned execution protocol. + +Every attached call returns one result discriminated by `outcome`. Successful +evaluations contain a JSON-compatible `value`; failed, cancelled, and timed-out +results contain a structured `error`. Stdout and stderr are always captured +with explicit truncation flags. Evaluation results also expose the value as a +bounded `{ type: "json", data }` display output. Returning `undefined`, a +function, a symbol, a circular object, or another non-JSON value produces an +`evaluation_serialization_failed` result instead of losing the value silently. + +## Use the core client + +The same methods are available directly on `AgentOs` under +`javascript.execute()`, `javascript.evaluate()`, `javascript.executeFile()`, +`javascript.typescript.*`, and `javascript.npm.*`. + +The focused runtime adds default `cwd` and `env` values and exposes the core VM +as `runtime.vm`; it does not implement a second execution engine. + +## Retain JavaScript state + +Give an operation an `executionId` and set `createIfMissing: true` on the first +call. Later inline JavaScript and TypeScript operations with the same ID share +one retained JavaScript realm. `inputs` is replaced on every operation and is +serialized separately from source. + +An execution accepts one active operation at a time. Reusing a busy ID fails +immediately. Bash, files, npm operations, and type checks can use the same +lifecycle ID, but run in fresh processes and do not alter retained language +memory. `executions.reset()` clears memory, results, and output; filesystem and +installed-package changes remain. `executions.delete()` removes an idle +execution. + +## TypeScript + +TypeScript execution is a convenience API in the JavaScript layer. **Executing +TypeScript transpiles it without semantic type checking.** Call +`javascript.typescript.check()` or +`javascript.typescript.checkProject()` when diagnostics are part of the +workflow. + +Inline `filePath` identifies source for diagnostics and module resolution; it +does not read that file. Use `javascript.typescript.executeFile()` on the core +client to execute an existing file. JavaScript and TypeScript inline calls +share retained state when they use the same execution ID. + +## npm projects and packages + +Use the options-only `javascript.npm.install()` overload for the project in +`cwd`. `frozen: true` performs a lockfile-exact clean install. Pass package +names for a named install, and use `javascript.npm.runScript()` or +`javascript.npm.runPackage()` instead of assembling npm commands. The focused +runtime exposes the same workflows as `installNpmPackages()`, +`executeNpmScript()`, and `executeNpmPackage()`. + +Package names, script names, paths, and arguments are transferred as distinct +argv values. Installs modify the VM-wide filesystem, so all executions in that +VM see the same dependencies. AgentOS admits only one npm or Python package +mutation at a time in a VM; a concurrent install fails with `execution_busy` +instead of allowing package managers to corrupt shared state. + +## Detached work and output + +Set `detached: true` on an execution method for a long-running program. The +returned descriptor contains its `executionId`; use `executions.wait()`, +`executions.cancel()`, `executions.signal()`, `executions.writeStdin()`, +`executions.closeStdin()`, `executions.resizePty()`, and +`executions.readOutput()` to manage it. Core callbacks receive exact +`Uint8Array` chunks. + +Actor actions use the same nested method paths as Core; RivetKit transports +them with dotted wire names such as `javascript.execute` and +`executions.readOutput`. Actor stdin and output use tagged +`{ encoding: "utf8" | "base64", data: string }` values, and live output is +broadcast through `executionOutput` and `executionCompleted` events. + +## Filesystem, processes, and networking + +`node:fs` uses the VM [filesystem](/docs/filesystem), `node:child_process` +starts kernel-managed guest processes, and Node networking APIs use the VM +socket table. Files and installed packages are immediately visible to Bash, +Python, agents, and other executions. See [Processes & Shells](/docs/processes), +[Networking & Previews](/docs/networking), and the detailed +[Node.js compatibility matrix](/docs/execution/javascript-compatibility). + +## Custom bindings and policy + +Guest JavaScript invokes [Custom Bindings](/docs/extensions/custom-bindings) +as ordinary typed commands, keeping host credentials outside the VM. + +All operations inherit the VM's [permissions](/docs/permissions) and +[resource limits](/docs/resource-limits). `timeoutMs` adds an operation-level +wall-clock deadline covering sidecar staging, TypeScript transformation, +guest execution, and result collection; it does not replace the independently +bounded VM safety limits. \ No newline at end of file diff --git a/website/public/docs/docs/execution/python.md b/website/public/docs/docs/execution/python.md new file mode 100644 index 0000000000..fd270a4f79 --- /dev/null +++ b/website/public/docs/docs/execution/python.md @@ -0,0 +1,81 @@ +# Python + +Execute Python source, files, modules, and package workflows in AgentOS. + +AgentOS runs CPython 3.13 as a first-class VM execution engine. Python shares +the VM filesystem, process tree, networking policy, permissions, and limits +with agents, Bash, JavaScript, and installed software. + +## Get started + +`@rivet-dev/agentos-python` is the focused Python entry point. It uses the same +sidecar protocol as the core `AgentOs` client. + +Use `python.execute()` for source and `python.evaluate()` for a JSON-compatible +value on the core client. The focused package exposes them as `execute()` and +`evaluate()` and provides default `cwd` and `env` values. Evaluation values are +also returned as bounded JSON display outputs. A value that Python's JSON +encoder cannot represent produces a structured +`evaluation_serialization_failed` result. + +## Retain Python state + +Reuse an `executionId` to keep Python globals, functions, imports, modules, and +guest objects in one interpreter. Set `createIfMissing: true` only on the first +operation. Structured `inputs` is replaced for every call. + +Only inline execute and evaluate calls retain interpreter memory. File, +module, install, Bash, and arbitrary-command operations use fresh processes. +`executions.reset()` clears retained memory and output without reverting the +filesystem; `executions.delete()` removes an idle execution. + +## Files, modules, and async Python + +Use `python.executeFile()` for an existing script and +`python.executeModule()` for the standard `python -m` workflow. Inline Python +supports top-level `await`, `async for`, and `async with`; awaited work is part +of the operation deadline. + +The API does not promise that unawaited `asyncio` tasks survive between +operations. Cancellation, timeout, reset, deletion, or VM disposal stops +execution-owned asynchronous work. + +## Install packages + +Call `python.install()` on `AgentOs`, or `install()` on the focused runtime. +Pass package specs for named installs, `requirementsFile` for a +requirements install, and `upgrade`, `indexUrl`, or `extraIndexUrls` for the +stable package workflows. + +Named packages and a requirements file cannot be combined in one call. +Installs modify the VM-wide filesystem and are visible to every execution in +that VM. AgentOS admits only one npm or Python package mutation at a time in a +VM; a concurrent install fails with `execution_busy` instead of risking shared +package state. Package downloads obey the VM network policy. + +## Detached work and output + +Set `detached: true` on execute, file, or module operations. Manage the returned +`executionId` with `executions.wait()`, `executions.cancel()`, +`executions.signal()`, stdin, PTY resize, reset, deletion, and bounded output +replay. Core output callbacks receive `Uint8Array`; actor events carry tagged +UTF-8/base64 data. Actor actions use the same nested method paths as Core and +RivetKit transports them with dotted wire names. + +## Filesystem, processes, and networking + +`pathlib`, `os`, and file objects use the VM [filesystem](/docs/filesystem). +`subprocess` starts kernel-managed guest commands. Python DNS, HTTP clients, and +outbound sockets use the VM network policy. See +[Processes & Shells](/docs/processes) and +[Networking & Previews](/docs/networking). + +## Custom bindings and policy + +Python can call [Custom Bindings](/docs/extensions/custom-bindings) as normal +commands through `subprocess`. + +All operations inherit [permissions](/docs/permissions) and +[resource limits](/docs/resource-limits). `timeoutMs` is an additional +operation wall-clock deadline covering staging, compilation, guest execution, +and result collection, while VM safety watchdogs remain independently bounded. \ No newline at end of file diff --git a/website/public/docs/docs/browser.md b/website/public/docs/docs/extensions/browser.md similarity index 93% rename from website/public/docs/docs/browser.md rename to website/public/docs/docs/extensions/browser.md index 2b97baa607..4bc062ac84 100644 --- a/website/public/docs/docs/browser.md +++ b/website/public/docs/docs/extensions/browser.md @@ -1,4 +1,4 @@ -# Browser +# Browser Automation Let agents read and search the web from an agentOS VM using Browserbase's cloud browser through the browse CLI — no local browser or sandbox required. @@ -36,4 +36,4 @@ browse cloud sessions list # list cloud browser sessions browse cloud projects list # list Browserbase projects ``` -The [interactive driver mode](https://docs.browserbase.com/integrations/skills/browse-cli) (`browse open`, `browse click`, …) is not supported inside the VM yet ([#1631](https://github.com/rivet-dev/agentos/issues/1631)). For interactive automation, run `browse` inside a sandbox via [Sandbox Mounting](/docs/sandbox). \ No newline at end of file +The [interactive driver mode](https://docs.browserbase.com/integrations/skills/browse-cli) (`browse open`, `browse click`, …) is not supported inside the VM yet ([#1631](https://github.com/rivet-dev/agentos/issues/1631)). For interactive automation, run `browse` inside an external sandbox via [External Sandboxes](/docs/extensions/sandboxes). \ No newline at end of file diff --git a/website/public/docs/docs/bindings.md b/website/public/docs/docs/extensions/custom-bindings.md similarity index 94% rename from website/public/docs/docs/bindings.md rename to website/public/docs/docs/extensions/custom-bindings.md index 9ef4ab7ca3..48770ba4a8 100644 --- a/website/public/docs/docs/bindings.md +++ b/website/public/docs/docs/extensions/custom-bindings.md @@ -1,4 +1,4 @@ -# Bindings +# Custom Bindings Expose custom host functions to agents as CLI commands inside the VM. @@ -62,9 +62,9 @@ On failure (validation or execution error), the binding exits non-zero and write Missing required flag: --city ``` -## Bindings vs MCP servers +## Bindings and MCP servers -agentOS supports two ways to give agents access to external functionality: **bindings** and **MCP servers**. Both work, but they have different tradeoffs. +AgentOS supports two ways to give agents access to external functionality: **bindings** and session-scoped **MCP servers**. Both work, but they have different tradeoffs. | | Bindings | MCP Servers | |---|---|---| diff --git a/website/public/docs/docs/sandbox.md b/website/public/docs/docs/extensions/sandboxes.md similarity index 91% rename from website/public/docs/docs/sandbox.md rename to website/public/docs/docs/extensions/sandboxes.md index f50fb54a13..9cb16b36fa 100644 --- a/website/public/docs/docs/sandbox.md +++ b/website/public/docs/docs/extensions/sandboxes.md @@ -1,8 +1,8 @@ -# Sandbox Mounting +# External Sandboxes Extend agentOS with full sandboxes for heavy workloads like browsers, desktop automation, and compilation. -For heavy workloads like browsers, desktop automation, and compilation, pair agentOS with a full sandbox on demand. Its filesystem mounts into the VM as a native directory, and its process management is exposed as [bindings](/docs/bindings), all provider-agnostic through [Sandbox Agent](https://sandboxagent.dev). +For heavy workloads like browsers, desktop automation, and compilation, pair agentOS with a full sandbox on demand. Its filesystem mounts into the VM as a native directory, and its process management is exposed as [custom bindings](/docs/extensions/custom-bindings), all provider-agnostic through [Sandbox Agent](https://sandboxagent.dev). ## Why use agentOS with a sandbox? @@ -25,7 +25,7 @@ Start with the default agentOS VM for all workloads, and only spin up a sandbox The sandbox integration ships as the `@rivet-dev/agentos-sandbox` package. It works through two mechanisms: - **Filesystem mount**: Projects the sandbox into the VM as a native directory, like mounting a hard drive on your own machine. Read and write files through the mount directly. -- **Bindings**: Exposes sandbox process management as [bindings](/docs/bindings). Execute commands on the sandbox from within the VM. +- **Bindings**: Exposes sandbox process management as [custom bindings](/docs/extensions/custom-bindings). Execute commands on the sandbox from within the VM. Both are powered by [Sandbox Agent](https://sandboxagent.dev), and you can swap providers without changing agent code. Install both packages: diff --git a/website/public/docs/docs/filesystem.md b/website/public/docs/docs/filesystem.md index 7fff8acdbc..f3a583288a 100644 --- a/website/public/docs/docs/filesystem.md +++ b/website/public/docs/docs/filesystem.md @@ -26,7 +26,7 @@ The actor's durable root is handled separately: the sidecar connects directly to ## File operations -These operations are primarily what the agent uses inside the VM, and are also available from the client to seed inputs and read results. For large or read-only inputs (a repo, a dataset), a read-only [host mount](#mounts) is faster than copying files in. Programs that need stdin or live output use exec instead (see [Core](/docs/core)). +These operations are primarily what the agent uses inside the VM, and are also available from the client to seed inputs and read results. For large or read-only inputs (a repo, a dataset), a read-only [host mount](#mounts) is faster than copying files in. Programs that need stdin or live output use exec instead (see the [Direct VM API](/docs/core)). ### Read and write @@ -57,7 +57,7 @@ See [Permissions](/docs/permissions) for the full configuration. ## Sandboxes -For heavier or untrusted workloads, run a full Linux [sandbox](/docs/sandbox) alongside the VM and mount its filesystem into agentOS. The agent then reads and writes the sandbox's files through the same `fs` APIs while the sandbox handles execution. See [Sandbox Mounting](/docs/sandbox) for setup. +For heavier workloads, run a full Linux [external sandbox](/docs/extensions/sandboxes) alongside the VM and mount its filesystem into agentOS. The agent then reads and writes the sandbox's files through the same `fs` APIs while the sandbox handles execution. ## Default layout diff --git a/website/public/docs/docs/js-runtime.md b/website/public/docs/docs/js-runtime.md deleted file mode 100644 index c82300f150..0000000000 --- a/website/public/docs/docs/js-runtime.md +++ /dev/null @@ -1,32 +0,0 @@ -# JavaScript Runtime - -How agentOS runs guest JavaScript: native V8 acceleration, low memory overhead, and Node.js compatibility. - -The JavaScript runtime is powered by the Rivet [Secure Exec](https://secureexec.dev) project, which provides the isolated V8 runtime that agentOS runs guest code in. Every guest VM executes its JavaScript inside this runtime, fully sandboxed from the host. - -## JavaScript Acceleration - -- **JavaScript is unusually slow as WebAssembly**: unlike most software, JavaScript pays a heavy penalty when compiled to WebAssembly, because so much engineering has gone into JIT-compiling JavaScript directly in V8. -- **Native V8, full JIT**: agentOS therefore runs guest JavaScript on the native V8 engine with its full JIT compiler, not through a WASM translation layer. We call this **JavaScript Acceleration**. -- **Native-speed execution**: guest JavaScript runs at native speed while staying inside the isolation boundary, with normal Node.js semantics. - -## Comparison to Node.js efficiency - -- **Isolate model, not processes**: agentOS runs each agent inside a V8 isolate rather than spawning a full Node.js process per agent. -- **Low memory overhead**: an isolate carries far less per-agent memory overhead than a full Node.js process, so many agents fit in the footprint that a process-per-agent model would spend on a handful. -- **Benchmarks**: see the Secure Exec [benchmarks](https://secureexec.dev/docs/benchmarks) for cold start, warm execution, and reuse measurements. - -## Node.js compatibility - -Guest code runs as Node.js (reporting `process.version` as `v22.0.0`), but it never touches the host runtime. Every `node:` builtin resolves to a kernel-backed bridge or an in-isolate polyfill, never the real host module. For the full builtin matrix (`fs`, `net`, `http`, `crypto`, undici-backed `fetch`, and more), see the Secure Exec [Node.js Compatibility](https://secureexec.dev/docs/nodejs-compatibility) reference. - -For the implementation-level event loop—how process-wide Tokio tasks notify a -thread-affine V8 isolate, how readiness is coalesced, and how `Duplex` -backpressure reaches a sidecar-owned socket—see -[JavaScript Executor & Socket Reactor](/docs/architecture/javascript-executor). - -### npm packages - -By default the VM has no npm packages installed. Mount a host `node_modules` directory to give guest code access to real packages: the `nodeModulesMount` helper projects it read-only at `/root/node_modules`, and the in-kernel resolver walks it exactly like Node.js does, with no bundling or patching. - -Resolution matches naive Node.js over the mounted tree: the ancestor `node_modules` walk, `package.json` `exports`/`imports` and conditions, and `realpath`/symlink following (so pnpm and yarn layouts resolve too). Both ESM `import` and CommonJS `require` work. See the Secure Exec [module loading](https://secureexec.dev/docs/features/module-loading) guide for the full model. \ No newline at end of file diff --git a/website/public/docs/docs/limitations.md b/website/public/docs/docs/limitations.md index f45cb0f8bf..7e1864b63b 100644 --- a/website/public/docs/docs/limitations.md +++ b/website/public/docs/docs/limitations.md @@ -6,7 +6,7 @@ agentOS is a Linux environment with a POSIX-compliant virtual kernel. It handles ## Sandbox mounting -When a workload needs a full Linux OS, agents can escalate to a full sandbox on demand without changing code. The [sandbox mounting](/docs/sandbox) extension mounts the sandbox as a filesystem and lets you execute commands on it, like mounting a hard drive on your own machine. Files written in the VM are available in the sandbox and vice versa. +When a workload needs a full Linux OS, agents can escalate to a full sandbox on demand without changing code. The [external sandbox](/docs/extensions/sandboxes) extension mounts the sandbox as a filesystem and lets you execute commands on it, like mounting a hard drive on your own machine. Files written in the VM are available in the sandbox and vice versa. See [agentOS vs Sandbox](/docs/versus-sandbox) for a detailed comparison. @@ -14,7 +14,7 @@ See [agentOS vs Sandbox](/docs/versus-sandbox) for a detailed comparison. ### Software registry -agentOS uses its own [software registry](/registry) of popular tools cross-compiled for the runtime. You cannot download and install arbitrary binaries (for example via `curl` or `apt`), and standard Linux package managers (`apt`, `yum`) are not available since agentOS runs a streamlined Linux environment rather than a full distribution. Native binaries that are not yet available in the registry (such as Go, Rust, or C++ toolchains) require a full [sandbox](/docs/sandbox). +agentOS uses its own [software registry](/registry) of popular tools cross-compiled for the runtime. You cannot download and install arbitrary binaries (for example via `curl` or `apt`), and standard Linux package managers (`apt`, `yum`) are not available since agentOS runs a streamlined Linux environment rather than a full distribution. Native binaries that are not yet available in the registry (such as Go, Rust, or C++ toolchains) require a full [external sandbox](/docs/extensions/sandboxes). See [Software](/docs/software) for how to install and configure available packages. diff --git a/website/public/docs/docs/llm-gateway.md b/website/public/docs/docs/llm-gateway.md deleted file mode 100644 index 6824a3ac08..0000000000 --- a/website/public/docs/docs/llm-gateway.md +++ /dev/null @@ -1,12 +0,0 @@ -# Embedded LLM Gateway - -Route, meter, and manage LLM API calls from agents. - -The Embedded LLM Gateway runs as part of the agentOS library, not as an external service. It intercepts and manages all LLM API calls made by agents inside the VM. - -- **Unified routing** for all agent LLM requests -- **API keys stay on the server** so they are never exposed to agent code inside the VM -- **Usage metering** with per-session and per-agent breakdowns -- **Rate limiting** and cost controls - -Check back soon for full documentation. \ No newline at end of file diff --git a/website/public/docs/docs/llm-credentials.md b/website/public/docs/docs/models-and-credentials.md similarity index 56% rename from website/public/docs/docs/llm-credentials.md rename to website/public/docs/docs/models-and-credentials.md index 3890f09975..d51cb8a16c 100644 --- a/website/public/docs/docs/llm-credentials.md +++ b/website/public/docs/docs/models-and-credentials.md @@ -1,8 +1,10 @@ -# LLM Credentials +# Models & Credentials -Pass LLM API keys to agent sessions securely. +Choose agent models and pass provider credentials to sessions securely. -Pass LLM provider API keys to agent sessions so keys stay on the server and are injected at session creation, with per-tenant isolation for multi-tenant deployments. +Choose the model through your agent adapter, then pass its provider credentials +to the session from trusted server code. Credentials are injected at session +creation and can be isolated per tenant. ## Passing API keys @@ -18,6 +20,9 @@ Then resolve each tenant's key and pass it at session creation: Because keys are resolved per tenant from your own credential store (the `lookupTenantApiKey` stand-in above) and stay on the server, each session uses the tenant's own key and one tenant's key never reaches another tenant or the client. -## Embedded LLM Gateway +## Models -The [Embedded LLM Gateway](/docs/llm-gateway) (coming soon) will remove the need to manage API keys manually. It routes all agent LLM requests through a managed proxy built into agentOS, providing per-tenant usage metering, rate limiting, and cost controls without deploying a separate gateway service. \ No newline at end of file +Model selection belongs to the configured agent adapter. AgentOS forwards the +session environment and preserves the agent's native model behavior instead of +introducing a second model-selection layer. See the page for your +[agent](/docs/agents/pi) for its supported model and provider options. \ No newline at end of file diff --git a/website/public/docs/docs/nodejs-runtime.md b/website/public/docs/docs/nodejs-runtime.md deleted file mode 100644 index 0a113689c6..0000000000 --- a/website/public/docs/docs/nodejs-runtime.md +++ /dev/null @@ -1,50 +0,0 @@ -# Node.js Runtime - -Run Node.js in agentOS: native V8 acceleration, the node CLI, installing packages, and Node.js compatibility. - -agentOS runs **Node.js** (`process.version` `v22.0.0`), fully isolated from the host. `node`, `npm`, and `npx` are on the `PATH`. - -## JavaScript Acceleration - -Normally, JavaScript running inside WebAssembly is exceptionally slow. In agentOS, JavaScript runs inside a native V8 isolate (powered by [Secure Exec](https://secureexec.dev)) for native runtime speeds: - -- **Native V8 speed, no overhead** — guest JS runs on V8's full JIT, not a WASM translation layer. -- **Lower memory than a Node.js process** — each agent is a V8 isolate, not a full process, so many fit where process-per-agent fits a handful. See [benchmarks](https://secureexec.dev/docs/benchmarks). - -## Running Node - -```ts -await agent.exec('node -e "console.log(1 + 1)"'); // inline -await agent.exec("node /workspace/main.js a b"); // script + argv -await agent.exec("npx tsx script.ts"); // npx -await agent.exec('echo "console.log(42)" | node'); // stdin -``` - -`node` works directly (`exec` / `execArgv` / `spawn`), through the guest shell (`sh -c`, pipes), and as a REPL. - -## Installing packages - -### Ahead of time - -Mount a host `node_modules` tree — projected read-only at `/root/node_modules` and resolved exactly like Node.js (ancestor walk, `package.json` `exports`/`imports`, symlinks — so pnpm/yarn layouts work), for both `import` and `require`: - -```ts -import { agentOS, setup, nodeModulesMount } from "@rivet-dev/agentos"; - -const vm = agentOS({ - mounts: [nodeModulesMount("/absolute/path/to/node_modules")], -}); -``` - -### At runtime - -Or install in the VM mid-task: - -```ts -await agent.exec("npm install chalk"); -await agent.exec("node /workspace/app.js"); // app.js: require("chalk") -``` - -## Node.js compatibility - -Guest code runs as Node.js v22, isolated from the host. `node:` builtins — `fs`, `net`, `http`, `crypto`, undici-backed `fetch`, and more — are provided by the runtime, never the host's. See the full [Node.js Compatibility](https://secureexec.dev/docs/nodejs-compatibility) matrix. \ No newline at end of file diff --git a/website/public/docs/docs/benchmarks.md b/website/public/docs/docs/performance.md similarity index 88% rename from website/public/docs/docs/benchmarks.md rename to website/public/docs/docs/performance.md index d23f4fce2e..805273df65 100644 --- a/website/public/docs/docs/benchmarks.md +++ b/website/public/docs/docs/performance.md @@ -1,8 +1,11 @@ -# Benchmarks +# Performance -Performance benchmarks comparing agentOS to traditional sandbox providers. +AgentOS latency, memory, cost, and benchmark methodology. -These are the benchmark figures shown on the agentOS marketing page. All numbers are computed from the same data source used by the marketing page. For independent sandbox comparison data, see the [ComputeSDK benchmarks](https://www.computesdk.com/benchmarks/). +These are the benchmark figures shown on the agentOS marketing page. All +numbers are computed from the same data source used by the marketing page. Use +the methodology below to measure the workload and hardware you will actually +deploy. ## Cold start @@ -68,6 +71,21 @@ Sandbox baseline: **Daytona** at $0.0504/vCPU-h + $0.0162/GiB-h with a 1 vCPU + | Hetzner ARM | $0.000000011/s | $0.000018/s | 1738x cheaper | | Hetzner x86 | $0.000000017/s | $0.000018/s | 1061x cheaper | +## Evaluating your own cost + +AgentOS runs on hardware you provision instead of billing per VM-second. To +estimate your workload, divide the instance's cost per second by the number of +concurrent VMs it can hold with realistic memory and utilization headroom. + +The result depends on workload size and lifecycle. A VM per task or tenant gives +the strongest isolation, while safely reusing a VM for trusted sequential work +amortizes boot and filesystem setup. Small, short executions benefit most from +dense packing; heavyweight, long-lived work depends more on hardware choice and +avoiding per-sandbox minimums. + +Provider prices, instance prices, and workload memory change. Measure +on your target hardware before quoting a cost ratio. + ## Test environment | Component | Details | diff --git a/website/public/docs/docs/python-runtime.md b/website/public/docs/docs/python-runtime.md deleted file mode 100644 index 8c551e8d36..0000000000 --- a/website/public/docs/docs/python-runtime.md +++ /dev/null @@ -1,62 +0,0 @@ -# Python Runtime - -Run Python in agentOS: the python CLI, installing packages ahead of time or at runtime, and what's supported. - -agentOS runs **CPython 3.13** as a first-class runtime. `python` and `python3` are on the `PATH` and plug into the VM's filesystem, processes, and network — agents use them like any other command. - -## Running Python - -```ts -await agent.exec('python -c "print(1 + 1)"'); // inline -await agent.exec("python /workspace/main.py a b"); // script + sys.argv -await agent.exec("python -m http.server 8000"); // module -await agent.exec('echo "print(40 + 2)" | python -'); // stdin -``` - -`python` works directly (`exec` / `execArgv` / `spawn`), through the guest shell (`sh -c`, pipes), and as an interactive REPL. - -## Installing packages - -`pip install` writes to a persistent spot on the VM filesystem, so a package installed once is importable by every later `python` run in that VM. - -### Ahead of time - -Install once during setup so the agent starts ready — no install cost mid-task: - -```ts -// one-off setup pass on the VM, before handing it to the agent -await agent.exec("pip install requests pandas"); -// requests + pandas now import in every python run on this VM -``` - -### At runtime - -Or let the agent install what it needs, mid-task: - -```ts -await agent.exec("pip install rich"); -await agent.exec('python -c "import rich; print(rich.__version__)"'); -``` - -`pip install ` and `python -m pip install ` both work; downloads obey the VM's network policy (default-deny + allowlist). - -## Compatibility - -CPython 3.13 and the standard library, with a few VM-shaped differences. - -### Supported - -- Full VM filesystem (`/tmp`, `/etc`, `/root`, …) — shared with other processes and `readFile()` -- Reading, writing, creating, deleting, and renaming files anywhere on the VM, plus symlinks and file metadata (`os.symlink` / `os.readlink` / `os.chmod` / `os.chown` / `os.utime`) -- A real process in the tree: stdin/stdout/stderr, signals, `kill` -- `subprocess` launching other VM commands (`node`, etc.) -- Pure-Python packages, plus native packages with a prebuilt wheel — `numpy`, `pandas`, `scipy`, `scikit-learn`, `pydantic`, `cryptography`, `Pillow`, and [many more](https://pyodide.org/en/stable/usage/packages-in-pyodide.html) -- `requests`, `urllib`, and `pip` over HTTP/DNS, under the VM network policy -- Outbound raw TCP and UDP sockets (the `socket` module), under the VM network policy - -### Unsupported - -- OS threads and `multiprocessing` — the runtime is single-threaded -- `os.fork` / `os.exec` -- Some packages with native (C/Rust) extensions — see the [full list of supported packages](https://pyodide.org/en/stable/usage/packages-in-pyodide.html) -- Socket servers / listeners (`bind`/`listen`/`accept`) — outbound connections only \ No newline at end of file diff --git a/website/public/docs/docs/quickstart.md b/website/public/docs/docs/quickstart.md index 6079271c04..ad39fc3c94 100644 --- a/website/public/docs/docs/quickstart.md +++ b/website/public/docs/docs/quickstart.md @@ -49,18 +49,19 @@ Set up an agentOS actor, create a session, and run your first coding agent. - **[Software](/docs/software)** — Install software packages inside the VM - **[Filesystem](/docs/filesystem)** — Read, write, and manage files inside the VM - **[Permissions & Resource Limits](/docs/permissions)** — Gate what the agent can do and cap its resource usage - - **[Bindings](/docs/bindings)** — Expose your JavaScript functions to agents as CLI commands + - **[Custom Bindings](/docs/extensions/custom-bindings)** — Expose your JavaScript functions to agents as CLI commands 5. **Deploy** By default, agentOS runs locally with `npx rivetkit dev` — no infrastructure needed. To run in production, deploy to any of these targets: - See [Deployment](/docs/deployment) for managed, self-hosted, and agentOS Core options. + See [Deployment](/docs/deployment) for managed, self-hosted, and direct VM options. agentOS is in preview and the API is subject to change. If you run into issues, please [report them on GitHub](https://github.com/rivet-dev/rivet/issues) or [join our Discord](https://rivet.dev/discord). -## agentOS Core +## Direct VM API -The quickstart above uses `@rivet-dev/agentos`, which includes statefulness, multiplayer, and orchestration out of the box. If you only need direct VM control without those features, you can use the core package (`@rivet-dev/agentos-core`) standalone. +The same `@rivet-dev/agentos` package also exports `AgentOs.create()` for direct +VM control without the actor lifecycle, multiplayer, or orchestration layer. -See [agentOS core documentation](/docs/core) for reference. \ No newline at end of file +See the [direct VM API documentation](/docs/core) for reference. \ No newline at end of file diff --git a/website/public/docs/docs/security-model.md b/website/public/docs/docs/security-model.md index 33d3550123..be7f6b55e0 100644 --- a/website/public/docs/docs/security-model.md +++ b/website/public/docs/docs/security-model.md @@ -110,7 +110,7 @@ Every guest syscall is mediated by the kernel and checked against the runtime's - **Processes.** `node:child_process` spawns kernel-managed guest processes, never real host processes. Children can only run the commands the VM mounts (WASM-backed `sh` and coreutils, V8-backed `node`). See [Processes](/docs/processes). - **Network.** Guest `fetch()`, `node:http`, and raw sockets all flow through the kernel socket table. Guest `fetch()` runs through undici inside the isolate and then through the kernel socket table; it never opens a real host socket. See [Networking](/docs/networking). - **DNS, pipes, and PTYs** are likewise kernel-owned: no guest path reaches the host directly. -- **Bindings.** Registered [bindings](/docs/bindings) are the only sanctioned way to hand the guest a named host capability. The guest invokes a binding by name with JSON input, the call round-trips to the host handler, and only the handler's return value comes back. The guest never receives the underlying host access. +- **Bindings.** Registered [custom bindings](/docs/extensions/custom-bindings) are the only sanctioned way to hand the guest a named host capability. The guest invokes a binding by name with JSON input, the call round-trips to the host handler, and only the handler's return value comes back. The guest never receives the underlying host access. ## What enters the VM @@ -159,7 +159,7 @@ The boundary protects the host from the guest. It does **not** harden your host - Hardening the host process and deployment environment. For internet-facing workloads that take untrusted input, run your host inside an already-hardened environment (for example AWS Lambda, Google Cloud Run, or a similar sandboxed platform). - Validating authentication tokens in `onBeforeConnect`. - Scoping [permissions](/docs/permissions) appropriately for your use case. -- Managing API keys and secrets on the host side (use the [LLM gateway](/docs/llm-gateway) to avoid passing keys into the VM). +- Managing API keys and secrets on the host side (see [Models & Credentials](/docs/models-and-credentials)). - Configuring [resource limits and network controls](/docs/security-model) to match your threat model. - Choosing your blast radius: prefer a fresh VM per untrusted or high-risk task so an escape attempt cannot outlive a single VM. diff --git a/website/public/docs/docs/sessions.md b/website/public/docs/docs/sessions.md index ef990646d0..a938644859 100644 --- a/website/public/docs/docs/sessions.md +++ b/website/public/docs/docs/sessions.md @@ -10,6 +10,18 @@ AgentOS sessions are durable records backed by the VM's SQLite database. The pub The input supports `agent`, `cwd`, `additionalDirectories`, `env`, `mcpServers`, `permissionPolicy`, `skipOsInstructions`, and `additionalInstructions`. Omitted `cwd` defaults to `/home/agentos` in the sidecar. Actor deployments inject their SQLite UDS database automatically. Standalone core clients must configure a VM SQLite file or UDS descriptor. +## MCP servers + +MCP configuration belongs to the session because its tools are part of the +agent's runtime context. Configure local child-process servers or remote servers +before opening the session. The exact config path and supported transports come +from the selected agent adapter; for example, Pi reads `.mcp.json` from its +AgentOS home directory: + +Install local MCP server packages before opening the session so first-run +package-manager output cannot corrupt a stdio handshake. See the selected +[agent guide](/docs/agents/pi) for adapter-specific configuration. + ## Prompt `prompt` accepts native ACP `ContentBlock[]`, not a special AgentOS text format. It never creates a missing session. AgentOS commits the complete user message before dispatching it and never automatically replays a prompt whose delivery may have reached the adapter. @@ -44,7 +56,7 @@ ACP itself does not define a portable history-reading API, and adapters implemen `permissionPolicy` is `reject_all`, `ask`, or `allow_all`, and defaults to `allow_all`. It controls how AgentOS answers native ACP permission requests; it does not configure VM permissions or adapter tool access. Set `permissionPolicy: "ask"` when opening the session before subscribing for interactive decisions; subscribing alone does not change the immutable policy. With `ask`, AgentOS durably records the native ACP `RequestPermissionRequest` as a `permission_request` variant in the generic session-event stream. With the default `allow_all`, AgentOS resolves the adapter request automatically and emits no permission event. Reply to an `ask` request with the exact adapter-supplied `optionId` and an explicit public session ID: ```ts -await agent.respondPermission({ +await agent.sessions.respondPermission({ sessionId: request.sessionId, requestId: request.requestId, optionId: request.options[0].optionId, diff --git a/website/public/docs/docs/software.md b/website/public/docs/docs/software.md index 79abcd8b3b..19edcafb48 100644 --- a/website/public/docs/docs/software.md +++ b/website/public/docs/docs/software.md @@ -22,4 +22,4 @@ Browse all available software packages on the [Registry](/registry). ## Custom Software -Package your own agents, command packages, and WASM commands. See [Software Definition](/docs/custom-software/definition) to define a package, and [Building Binaries](/docs/custom-software/building-wasm) to compile WASM commands from source in the [secure-exec registry](https://github.com/rivet-dev/secure-exec/tree/main/registry). \ No newline at end of file +Package your own agents, command packages, and WASM commands. See [Software Definition](/docs/custom-software/definition) to define a package, and [Building Binaries](/docs/custom-software/building-wasm) to compile WASM commands from source in the [agentos registry](https://github.com/rivet-dev/agentos/tree/main/registry). \ No newline at end of file diff --git a/website/public/docs/docs/versus-sandbox.md b/website/public/docs/docs/versus-sandbox.md index 062161ae53..6921832d72 100644 --- a/website/public/docs/docs/versus-sandbox.md +++ b/website/public/docs/docs/versus-sandbox.md @@ -2,9 +2,9 @@ When to use the lightweight agentOS VM, a full sandbox, or both together. -- **agentOS** is a lightweight VM that runs inside your process. Near-zero cold start, low memory, direct backend integration via [bindings](/docs/bindings). +- **agentOS** is a lightweight VM that runs inside your process. Near-zero cold start, low memory, direct backend integration via [custom bindings](/docs/extensions/custom-bindings). - **Sandboxes** are full Linux environments with root access, system packages, and native binary support. -- **You can use both.** agentOS works with sandboxes through [sandbox mounting](/docs/sandbox). Agents run in the lightweight VM by default and spin up a full sandbox on demand. +- **You can use both.** agentOS works with sandboxes through the [external sandbox extension](/docs/extensions/sandboxes). Agents run in the lightweight VM by default and spin up a full sandbox on demand. ## Comparison @@ -12,8 +12,8 @@ When to use the lightweight agentOS VM, a full sandbox, or both together. |---|---|---| | **Cost** | Very low. Runs in your process. | Pay per second of uptime. | | **Startup** | Near-zero cold start (~6 ms). | Seconds to spin up. | -| **Backend integration** | Direct. [Bindings](/docs/bindings) call your functions with zero latency. | Indirect. Requires network calls back to your backend. | -| **Credentials** | Stay on the host. [Bindings](/docs/bindings) run your functions server-side; agents see only inputs and outputs. | Must be injected into the sandbox environment. | +| **Backend integration** | Direct. [Custom bindings](/docs/extensions/custom-bindings) call your functions with zero latency. | Indirect. Requires network calls back to your backend. | +| **Credentials** | Stay on the host. [Custom bindings](/docs/extensions/custom-bindings) run your functions server-side; agents see only inputs and outputs. | Must be injected into the sandbox environment. | | **Permissions** | Granular, deny-by-default. | Coarse-grained (container-level). | | **Infrastructure** | `npm install` | Vendor account + API keys. | | **Best for** | Coding, file manipulation, scripting, API calls, orchestration. | Browsers, desktop automation, native compilation, dev servers. | @@ -41,7 +41,7 @@ Spin up a sandbox when the workload needs a real Linux kernel: ### Both together -Use agentOS with [sandbox mounting](/docs/sandbox) for workflows that need both: +Use agentOS with [external sandboxes](/docs/extensions/sandboxes) for workflows that need both: - Agent runs in the agentOS VM with full access to bindings and permissions - Sandbox spins up on demand for heavy tasks diff --git a/website/public/docs/docs/webhooks.md b/website/public/docs/docs/webhooks.md deleted file mode 100644 index 350ad55a45..0000000000 --- a/website/public/docs/docs/webhooks.md +++ /dev/null @@ -1,21 +0,0 @@ -# Webhooks - -Trigger agent sessions directly from external webhooks using Hono. - -Use a lightweight HTTP server to receive webhooks and drive an agent. This example uses [Hono](https://hono.dev) to receive Slack webhooks and call an agent directly. - -## Example: Slack webhook to agent - -## How it works - -1. Slack sends an HTTP POST to `/slack/events` -2. The Hono handler validates the event and prompts one durable AgentOS session -3. AgentOS automatically serializes concurrent prompts targeting that session -4. The handler posts the response back to Slack - -No application queue is needed for prompt ordering. Durable session history remains in SQLite, but AgentOS never automatically replays a prompt whose delivery was uncertain. - -## Recommendations - -- The direct example waits for the turn. If the provider requires an immediate `200`, use the hosting platform's durable background-task primitive and call AgentOS from that task. -- Store webhook secrets in environment variables, not in code. \ No newline at end of file diff --git a/website/public/docs/docs/workflows.md b/website/public/docs/docs/workflows.md index 1cab1ed0a6..84eb098e85 100644 --- a/website/public/docs/docs/workflows.md +++ b/website/public/docs/docs/workflows.md @@ -1,4 +1,4 @@ -# Workflow Automation +# Workflows & Graphs Orchestrate multi-step agent tasks with durable workflows. diff --git a/website/public/secure-exec-logo-long.svg b/website/public/secure-exec-logo-long.svg deleted file mode 100644 index cd27d6b8b2..0000000000 --- a/website/public/secure-exec-logo-long.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/website/public/secure-exec-logo.png b/website/public/secure-exec-logo.png deleted file mode 100644 index 564c5d52e18bd90bafda714a66a35cc09dd8a603..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 714803 zcmb@tb99|u*DoB~wj0~FohE6M#Hv>l#5J?L& z8-v`sR3i|$!)h6E5jA(vGapF*FAH_9jk-$?X*n*BCT^M>Dzs_}ku%jMmxagF7B|J#`<*p5FhT z={g_`gn)qHlMW}e_kvhNUkG9Qzv;=26cieINDq;WSl9R2go6MHzNDmNFcc9#di}O} z!!;|sI5^%bHt659=Lah0e$lEk$u`-t?zJ1yJR;7!PZTHckbZZpHa3>va?7$RkA!HbtD6XYJcTrc5&LR@BmEb}%GiSi2JO}K ze7wKvw7c2C;S9PzjM6D7>;s6e%7`Mw=XZB4{p7~{9hY?69Dx7&AKJtD=TU**%m{T| z99qv&dT&jGmAuwlZ-4e~5V_XN_uC9Q?iV@r%*@rwg7xw zsRtGYDfZF7FATY|T7M)lv{Q)ZVtq~#zM<(nx6}MOnE?0x_#tmu4#Pd7%#Z%^uU z!(;Q+%F;6NUpjP6wG9CQJ>(_q;o-61xA~IAmW_1ZQt&}}on5cwy7^Qdw_w~@%YZj) zxe+V;)bC;?$@^o?@ZU>(23Y--03=Q*>E-$yzceFZYhLs5;Llx5!gE z9mR?*Z#TE3IniRQ8IUT;!d%WQ8Hz!3=Xgnwr?#FK8;q-SA$E ziQmnUUr^hAzPX^6@LwEbB{-0&? z0t|3w@#8w`J;z>b=;HJ|I?m_kzT-xSL$GGhODDnr$lzzpF~n8o1&HjXj-K41|9dO; zZ_WR+NN)l;A!MAu%O6;PK2!Vl?>SPOaW*n(ZfSt@3Ua(mDQSju;M`7l8Wv{L=glPF zD^SP(LwntjR3zNo9|%YUM?ad3mP+>~xxGRfc3WEGWvt>wXL}DEg9=wkFmh2E<)Oqi z3C2!VnnLEvoZg}TTS-1a|MJ!yo0peY_M-c6XO50eV8JY>9?aV}jwnVv{i=BExIZrt zRjywI1;V=-#c{ZaQyh%@zv)BkAMoM;HC6C_dk~$sop&+EdlhZ1GhzhT|Onr*wne{TjU_VXV}b3=taHY^IhpJka%X(?;9KNKHq zJ3ik}i(@#eK)Ch96$c^ymKH4DrKv4{5I4}3=_AhbjQoG34f%IZeSrozCw4@hC;jOd zdOoA&mHWr4rDF7WA?6ftu4&-eu2d9_?7>u&5E3fo@1_Vsi`0{{(cu5Ffv0a=)WAV8mFcr#70jAUTd zj<@&n(RI(O?cZo(`Ug$nzqGQ_Y&HOgVoTaSwky$Xvf)Fjp-8M}>ng~AGf2J^ZIZ%- z^lDh)FSwfR8w(#Vr!|it|2mf*1W%k!)3c=QM2Q?kfKze@meb_W-2Q?x$DpXMKJ-v-wPU`n@Bj^LRlee^0G{98{Ho2@?yfw%ERXVQ-(<+?bnNMEsXzFJXZYdTI_Z z@Q)wUaCh+Wx{g)Aw|iTbViMe9CVRNpWc2w644GbB1!EF?1bYGlH5wQ-3EjE`2X_t^ zCu1hU!#qdgqmE=farVsgdp~U-`nSXd0)Y1OlM@9(f4{GOO7PUeZqu(Xu(iG>B^Y+= zLgx|({*WP9p3{o|ZMo=WSzNj@_p6nB9!ruBBdU@pKT+7QI8Jzc_`t5%lk?uukvYzP zLzjCK9jKMpc94C{{gb)ZPGyT&I(jJfFI;v%klD%<*l6i!w}FxiCggJy^%W76&*D{J zqGJeg=0%JU2P4~oDXz!8f_@97A2WK=zSVKtNCEwqUOYko^}>7Gq3Qw%QSI)A2EbWc zz8-_$=|oz7)lUt*26I$vZ?pQ3S$HOVk6wNqbv2vGbq|tqxc(#=X_LA5$zl-|WBXM? zPZAWI%d@lpN9!ID{;odFQvP~Xxh15Xph#tx&f#=&ZP$5C3*k-l{=`8gY$nCWf7L^b zh!PS#cA)rOO|6)YZp^^iT3#6g(^;Vm0NKytE+2DMi9%O+r^$WUOZsUNoUNe zIqd?P7(p=%0E?s_a&Ve>eY_<8x54|LGw2)mYkE4Y*__q1pG}Jq zzgMUG9yDhW=5_hr>3e10nfqO%3K;>(6q`viv%I_llS&l5aP6GaSqEBSjwC}Kb{>@c zmx*Z0NUG4=4zkR@T$lS4(CZ9Aqg5SHrk7x=*EcjC=DM%PY`uRxRy3V0+YxmCq4@ce zKFloF=bN9`t!Za*mGiOhpA4DoTPqPT@}Nwqr|=foeIp z=Y7dTcEwGSU%;I1dH- zC2Suu3ad1YK`*nyj+=rGfbjeb8AuHmFJnf6s}w-L2AMUHDq&l6>DK*9zU24ctbC&D zg@uVl#!J8G+r_t4_PyS?)M+?hz@Gg46X&)4YT=etY`ik1ehe>dXsSt_GKlge_ed3;_{crQOO3`Y$Wv zhxr$&B47!~^shSucJEigsi8jKz0b3nF}=V&uAa4QQAx5w?fh=P8JT;_D`Vz4+iw|6 z_&N%L>{TsZgmffTqv}*9lm_+>zx>dFS=-Pno;gl!o||Cvn8#bip3p;) z-RBWG& z>pDNR+Znn~)3t1hiI!J8fCejZ%_55k1@V*&v*(5bD1d2l79Kl$)&c;mkpD+o()+SZ zSHycCdfw!-4oAZIT*W$on=_{_w)Zm{uFk7@RW@R;GZxr?T+YY$zgFQD%a}s>7!nsO zEsyV4Pr)$Z)oEVKF>hUQ6^Ym5`(xRv*UQ(!GrTeL>ewVD!RF^7p{GH% zI6;p~LSXW!V`Qr}bEMl|G3xP=->i&*fC35o^*CAN1z9l-okE5n0n($ks3-_t+3}wU zz*|_vbzpirzPSM`O9$rU%tj z_+Ozv(UF2f54~dWY=oJN#R_%)`q;+y{b=VaGsehrxb`@V7)zrc?YbKsBhPX>$HSK^ zVU2G>WYX($-%k^IReX*6JYD3n>wf>=w;@PV@dn}#Eiu92uMh6mR59=KfM2xR7#2MplQxCbA!(ITuFD$oeP!~ z+ODFRG1vM;0QT>3*-Wd39=UY9uLtn{Iq;jr6#`7`BS+@xni34M^ZHws-lwnYmU2J$ z-d%k#`Zt2Kjke>Q)UsS}%dsQnBL(sz!FK%xSbX2>q#8uxmB%JIEbDsP_VcD)qvoR1 zwqoi?)fCLEiqT{p17NgFk~I>zbP6dJmP|(1CoO;Uf1wkyMGV_r@8PtV@SWXnbN36? zC>e%a#m}KR3muRkkXjR}ljaJ=H8H*d^(^E|eTDpRs6I**Bo@gQK#n>uig0Fa1DvXQW(LW!XE z+SUh=gu|{dD*bO{JOqu0iAjf!h)8IM6dN}$6RCkie}zSf&az?ZCd!*TZxq44%poCP zGAB`?@E%W1GgevpJ!f@u;o~ipYs=f}pv|T8A68tO^|Fz0Z@MivTBQLjl=dtLJ3IAw zmlqx19t5EG(U+S{cjEje`n96+lxQXu zeZB=7$4?i@JoSFdo68>w&_wQl|5@on=pSXmjAus4^SK@{YsKMH+q?=bGM@0ujR42q{3P&EDu5Ou4m8wkZp})WhZr= zQOkZ|w$-freK9M`*M3SM>GNvYq1Sq=z&=sPcxnf>j(zBxNWynYWEg<}* zPs*PNP^Y9g-uX4{3|GJBrg9n`7a|^boXlVq%Lof+ei9)mDYG#a)fd4xSbwXkqGkH7 z_WGqnJ%vu`(POFm;U=_cg zmmwvBJ4IB;F{H*|ygb2ACnm&GYKN0EYssDz)l^@3>`%fCCkIWyz6+v`asVyXl}Rl8 zKiIJYo%Q+Ay}9gtQeI|BPQI5w{C*@=mgBlQB5|na-gWfoH6Tw3u3IDJ#N;7U%j6WbMXvY^;hYsly^#39!D}o_WP*yfX zO-svqmBY;GDtzbJDNBNag3xEy&~%|FMV4^4+Gs_krB|)Db@IL#C#2VG)5q5pc{1zH zTWP}C*-7KDzwK+ZR?X0*L0^^QIhwIc{Ne7_SV*fH8YWeYtoMTZ%a;VQ`SZlJfPM-b zXabpnH5(7~o6BX5teg}?lQ^1(1Sa0#n!kAQ`Zz_D`?aEL-rO1|jVL|}FE zFU9)dSLx6h&k(J8ub2?yDwXV~LKAh|7KetCvk5*&C}E~0V?^m}Y>=(;z8s|K!g)QM zg(zfg&NSPsfGwz}++=cCRajY_R%>|Otftuf(ya)kiv@n1Q zY5*BbaghGcJ7GnTt-ORQ55tEq>gtFxXff8`z$H+{b+C;+<6^tbGby)EF-YUt87gHH z1J{y``-VrK0O3qBcg>}^vXn_^CYpnX#uV7jEJR8>7bj4@xw1qEVrchjWM6h_0MsLa z6w9jD?qsaY?ChHxD{P62YQD~W;Lug`$QUl*6BoVlL6G8Ug7L4!MDVf)hT z$=V%9Ez@h49Xs|sIRi0Lu|Lc7-dF@QI24{Hlx(4g@vipen0j+{yh0i)vElPNPO1`^ zwjO@jYzq56t+Od*3H)j@ZCg8NimE7dYq;-Qv#Y`WGnGS%k%rZD6nedJ5*Z17tugn1 zbsV~(87|lRA*4j;b0kg)=IO1{uKhlXt#m3A)!f1YtAe)Dxp@t;&K$$_V$;BS12AE3 zagH)ZThZ?{$pik4UQ4t$xw^V(aVhfzU-!rwZ~3Ke%Fi0%uetk}=wT!Ikg%6;0!@Hn zO@spUsnD#RqtfIwjVLG}ItHxKEGd?&Q+-9}B&}S96sdzZT%4JoKdxP~QCyGg%+D8X zecCOT<;+sQx9&+DF}CT=H}1Z<;q{`%_&PX928WY5G3}!6Uu6jssKDhMMiJ%Oi*lX_ z0oWoR8>Sr&+z8DMwDw0LX~Wouff(swA;%#ht0URAB8f=!UrV&V#FPQiga-FpmOyd7 zTL6WHr*w|&<~y=mmv}>V?mdV<*5oxuwWnfV4_C#Mu>;MEkIzJ@k1Q+|$W1iXw9wUF zigC9nP_kb>2)w9fG*dOQx(PYd3wRGhhVgp1_%@rtKAzDi?0tNI4Hxfyb+O#0%KJHs zAFVpc^ZjXbsT*d3FsRl`iR~y7A0uyV7#RWrb&42`jtxw=ndr*%*5)Y$?II?1fiy=U zh(a#VvVIReo`HcWM)2cluVaV{7>GkdwOpV$C?qxh%}4xD-D?o`d~PLmMQTs2^gbNd z2GV@oCv@E2eOEd>;Az*^_~vH;XgX*T8ttw*wKpv(zX#o#dc}~1D*T>)M@6?;xa>;R zbO9dTZUvps+v+WsIOaL6y(@OFzw>2fa{63U>Ye}1kYoMOuMoohQ0PTPtSu}O_G}z@ zvSBM}+6%v7Cez`Gr(pP3SOjj6Mk&rH=N!wADr!O;z4K=4Hjf=U@``IV?xhl-z4S&> ztj(1-2&Bj^&#*KV5nn>c)E4zs7bYpKz(2K{V?&}wK(f;&AfO1xo2X-%;0lpOJT-?; zk#p&9pO|ke$+DwpaDubKg__sW!x6HJ8U3MtFPiH;f=VIXX4$D}11bF_+Yu--G!l!= z1rLRaDNQoX7HTV?v_;j?z5-{XRNrLb-wGQe%X$^iPJ#kz;9qJbZ9gphY9>Y|jOKh~ zUd`2HvytL{zwo`@>yZ#E>qpj|8l}y6hJcor)w^e1etu8grErkrxs(thaod|?%iU=wLl;PbS(cu{oSE(QEFzG>>J5XB7b1CwY%ayFFlThNS^`Nh{2vpkNj z&v#-xDWr2Q)A4L39jA#D`{x5q3w6>x+OB$YvXzNTy^k@5G_dX4&oX={8W>%`YU3Y?t9aU6n}_ek`eI(gD* zu^(;iX48CQH$orBoY{q6Hnl$l{J;_#Z3*%Wc3X6S1P97e0& zzY=+)fCYHB)^{qso%|BQO8v#JsL}A^endbJFq6PMT!9|P1K-yPUTZdG`?QZ~)&r1W z18hA#JkA|vyFk%va_c=jXVVfA5V1PDJmCF0pV&>NN!fO6-E63w)PjQtfBjo8+4Bj{ z`?zBnIuvx98Q=Q+^H_KO+T`}wcsJ4V*tO-+z+&+I`fV2nDEc?`Wq?Uk-^I-@{?m1~ zsnP!3F(R-12t0NxIOg!egu*M)iAHx2sectbea88JumykDVP$ZAjO+upBf+x>u4EOFEu3v&2i_ zL8NJ^k&0=pLqn%XlSrayQ#hmx&4)#b9TPR?{0r3-Pxa84B$fv4oyWuIb(F)q!`TJR>8BlYB}F_ zrk3PP2}n#tYpwmM;mmz5%2*TxNaRoeM{8HMc3s_3(NR+Po8SQRSXT$bleq5NdU&LZ zm6^9cV>M|XOH95^j2b`qA5MDDz5cdPYoMSCXW2|KLWw-L3ufRad3>H!iMt0M=h>Jl zeZc!ZE~;&34j)BSo#>L&(t5?s14V@g4}~6ixNobRieG*Gkoh03`d0WZo=hGt+I^X_ zYhU_z*`~P9qfN&C)MT^Red|dU3VN2y)O5h08Y6z5e*mo>ZQAT>`?=i=RER6!$0$&}SIr#nTv_5}RDTZ~`3g{27|)nG$f{^jX1_?OdP*3>3+|qE#fwJ*$4h87qre2> zGp8!WK7VW8K0zs^vLt6~8W^e2ovWR`TDMD)tCv6&m|b!q%@)NTIjkk z%CJNJukm`1MrH>U>F)u~NEGWcn+H+1+(h2KWmaJp2I*S5(MkY*?yp!kiAHy&4H#Y* zT1!4wa?VIZMj&NPkPa7C)RA=cuAA@T8#@9dDmy@fJ}8=m8M`DW8%yh``N(k)R+2@h z%X5q(c_o>7h35c3tg;OEQV|QrAhE(yqXopv6a~qzfMg&T+iR`RcL^2k+*(kOA~;X0 zj*DP@S)nh;FNP!IuonYRvwRh9tlYkT@z1sxwM3Es1OAg&oR_paH=xh%WnBCvH z%0IvXL&ygF&9u4Xe5eFZx>*V#($sMGVMxEF6m<;e=P~+r{&1%A8F6C#d4{A_pSfNg zNPw}hx(o~m@O2oS&kP>hX(?*?x!vw=ZSc1m=AO6H6EL_sxj4DDNYH&krZjCJZp30S z)a7%WLig>>b!#%fu1Z@o0pxEj2hYaFMt$Rbrlv}h=kd-)i)+BP?RalgVQD?tug^^A z*w^gTwl4oCiZ>DV*;YNaH4X$V{dmxoI7DJO0+iZlaN;gmQCF9W-^;OC%y&JnnV)vt z=?bNZRX7WKH?WwP1J@R+r)0N>;|L1n)hBv&?#HBMY%Ol?)@RCfLyIcxcDi6T=!AbW zgH0*-tyFytvbZXv;JITAK3?Aay1HbulliZCeLGjL>s`WjUf)GDH4O}Uu<0FJvJXbw zJ1B_cAdM1?N%*V1d_Zf+bL8bBjJ1>1Q+O&hojqMBpuf&jCXO)TR9Oo{m(G-syO0T(ei2Y^HbJHtglAb!Pt$>0D;lNoDB1{u_%g<-6C1!mmD zb<;?b#Dg=9h+{1G{IL)0`vVfTWME1|O)E{yd)QCE?PVfil7UoF1)xRHOx7$9khh_~ z;$}^aW6aV91%FMEMkH~N3NTTdcANdM%(O*=lZK1`28N1N3@M_C^=%TovNr#5?hmWJ z39H5$HUp~c=(>IeikgXt2~Mb{tmu%wsbsjm?AOTXA%kK_A~s*TM9SonfT5rv9C|qa zo6m^vV0>Za;%K{d+-EEG#GCFuQV-m#c9KGa$>2!aI#>3)dPBTjNe0&fNfLBezxA4 zlrr;eNiAw={fgziSak_DtI7Inlif;1Lv!P?QIV0Sl~huHVqZ)QVT8D0dYa|m7@7ErNlutRj(OWyAqS5+{@l?L z&%J0yxwgK(Ok)lZQgL#}o1?o_7$SEw4azjj&Y<5+XWPDUApyOXH)6V8L7&83Y^{18 zSix;zY>Z)84inw%LaGvP3hFb6u-h9;{2R^J1FL6~5cgSo0f=*pF$OmGoC z%%&y(s);p16&dKNF*H@+7N_WwnE<-)J{AcUIT4D~G+ubc7=}+_hmvCm?ar_3JqhLN zO_Bgsu@Q@$(V1pKq`U{W^E1AP6vi*H>E;3hOA;wRq`cM|9DD>hNnF2#ID>wyG@liT zOmlX)$E+S#Qv67hjl6HXs|T8APi-*Zk|Tx4nYU#G4V}$shZ;0tO^mHlq=9I0E_E|# zG8|9;Rn%-O0wN&turIV2+$1#|FdUmD5HCIxKR-gDIxWNuQbfL2Yczxe6Bnkxv6@uS zUWSc_hgZDHr*0S(?)rZ{9Or7#F9VRG_8|~)?MrJxy zj46{1BlUYkziaN&nR}ke?|$s%9a)fqpj=45N}~&7vAdCImxH4g!H*aRCm=S~)$~#! zM-bf8>s5biQ!HNo9IaC~kHu>cEd8qhVyM(>$;T#RK~0$+LQEWfAGw%0G$I`0FNV=g zu`oD2_xAzbxVf%|ic)WDOBbE3a;ltJ;G<`YO=1&L2;D{3G1!lH>-K#-u(}n0YkM`} zez0JMOVv7&6bG$m55>fvcll07=SSxE;f1@`Z5^A|Kc>E`)rqS#esxxjI*aV1qm9&4 zUl+s_@RU#~cIU6gll+EaB7*}TBf_82L+ybOQ-!l_h0?dxx5LbvD&rXJA{%1l2`i1Z ze1^mf3FGo@R5&m^o@ESj_9&jpL2JK3^-dsZL9+>zXA&d-I}5+9TU$?1f52SCe!v7$ z%W}Yp@-o<-{jz2RxIkPnjF)uPM)5d-Et}T1AJjQ?iktYJyS%h-JS9X5WpI&!tMy({ z(GyOPF_>N)6{+L~X*`I`t{3_f$vLD1>#KR8(Zn6mEN+YmKhRGWlG_g5imYHmknu@6e+k0&XSvuqp*M@1VE2T1(3QGS3-K$Z6kw9t~KuM9jk{;`U7=W*grN|RV_ z6L6Re5ICy8i8q zgrnAbfi_*h4iy1ONth`S6F}zQN9iw$i|H;emAG=rf2xC?(N&9Hp!*IFYy+cu##?LD+LKmkG$pT?CYZwo zdUz=7utC~nlLF_GP&2deLZVY=5yjBGb-VY|cY8rp2Bn$37#StW_B@54^d+Sv`xELw zUlU#=FE_#OLzDzRe~;&nVKOl=;8j;w9&0xlA0(I&$8iv?cXb+3AksS8%lQ_pVEk6ieK=}K& zv9<&PWz}m6OdOcZfMQ{Gh&uCRo*ncA7zAW8K7LNP2=UAW7QuiqCbO!cz+BZC09mTCO|T0AoiGtYEQ|N(Lb3niv zCHZmSR#{mA_Y^c^SyBUck4bagt-cOn=`AP}xj00-f(Y86bZ8O!byE{3uuTUvJia}} zVh?}l?3gTUZM&+f3JY0r5N1^+Su?PZ3Hbw^^0Rnf?RD^DANd^jg9!byQSqX5ucEfr zkDm@|E^Q-1J+p}1w>LW>>*0=E_K3wrjVZEbQsJb+nBBjXpN8VZhB?>FAyZZT09WGw zfY5e3zaIo(j)gRDZGzx$=_0e4jtAqim5Er?5xQ+c*STYr%9o3_8-XGqB8%$zH7~C( z8?tjb3p*L%o5bJ=A{6HPAcR1EnN2>SiI<_0p&P@&#Z3$i3nk1E&^ejD@;{XJvfBA5 zU0h-6x*3dM4CB?%QD0>9>b;qan&Pdo`AlVi#?$(-I*me9`Ri816Wwbbo+4DlajE!uv zo~-1EjViH?f}$QJrVyNn$SmRN3IYx3C|k;iQ~)gwf5bVpF{gMoz?hc-4R^=>YkxaP zHX{Yxx}vu`y@4bw^{!3xie`V#9ez3^_!oYPInzcE-!ck=eS#d zERjZ;s-$4*;nFKmp6kfpg`ZW)BG{z4;dSyM#`J?^F9y!(V(5F!;S?h%l1D?5L-Fhx z{uEuQA^`G`ErmAla%;Fp1=AI@MA7|#zhF#HH4?2ij@A>Lha?iK-EZKy#A7cTx$)ZF za_2BzsY%BAL8iMl#Iv;x#3Ct6Nx50Ar|)ySS~oN_sPVt_?CFV+y%!zLFL&Q=*d2>x zUpAfd(A#>Og*hx*lnK7~_;hz1yl#E^;sc|^I2LZ4Of-bHYTu7%?5Wq4sSgsn^(Sj8 ze}S7K;`Ti{+ngu zn3zlJAg%6Obkd__1i~PEp_h@V{PJJ$j2+uz_UktT=4Jc!=f*PW{7ELRD*UOz2|FxB zbp^OhHY+yPYfZR=Lo6^U(m0?E?-do5Ggt_CzBO9ktqbF2zeJl81UZ0eC+#6=^8 zixn6!peYTqbfLRdG)*Uu-UMI{5pOjfwJhXtU@qtg>iHV+=GYA2titFC0kf=+j3+%a z;eB?zo;$@5aw%bR%hhfEy=ZiJv6h;#8*z{M_iTiih z5b9!Mkv}+T6!q0Id6A9F(IFbs$J33$MhHwB=o{16XGajixr01)SOvk9wf9Yq-_^wZ zk(gS4(mUqK?9pdYl8Jz|p9q^_7OmmjE0w558 z%3uXwb*SRX(g((g2kOYJeuJuMq~*()FA0B@6-{OT2Cx_nt`%cu0wE_iiZ4EcSMkP% zG&|8%)`TadN?F=IHK1^!0q1W_I>FWZzw+22l~fW>Gw(3qUJKM zr~TWd25^N;g3l)IRPW_>x0=uESK{ubrluFMIR2M(pADK*1Tx}TY0hSshTlia%7c3j zNC$_9w|_@gzPO&VCSPAqe3>&<)XZJnORL(dJ$2Unnp|1a)nIaRr}=Pm6IiI^^9PM7 z6S=)Z*AC(%K$-&Wz0rlu&DeQIK8pmS5+G-8W6R-fOH9zb%e^xs^wE0VPp)*D*Kz%| z;8#8I!+B-c!v!eL*{1;i>pmN^k0&9$W*eC4R0g6%h;Ng^36!Z5Z}4uUp@z$ES_Av- zujah7mevxihOe^-`(oHT&~%Mv{(>S2RT8wo~k8;`~H$7a0B6=;VOv6T>o~M`WJ_3XH(0N?ixEPH_nriM0U;@C6 zil0uW1Q!@qp&H5=Ww8F1hJbG(OHwDYLR>5%Qll~w8W`I_0(*2-8BLNuBL)#nQE9q_ zICBVPUe4_$KzVFTkQhGRKmG_=8nsCeo{K1*QW zFk#=rX{lLKmL zQ;1GRY6kj#>R{+VBHWwXH|sSqF{7sp_Cz(>r#EMT;t`fuZI}kPyMzG7R17GD8C$QB z^LyXEOUH9y{lkyUyi4G5-RV|Vmev#-`;e)>ER|%12bdAw)YC8s@=tRt&jS4Xn74hM z*(t5No_3>J6=9**9fk;XC6oovAKe8Tt)^mkt1pP2o)q$Cm3U1U5Xdz@=If`TjP}rH zvZ}i;9uD(--=M$ixTpjB=$p{Bqwe5k*$#v2y#bq;BgU34oJRC0A@UXqCOJp<&#Z+M z<^>Vy@d{rgZ{cr=1?;zbln$rzw!lC2?;aoXevX}-KYwh4!77w;ZLUf;C8}s+%y&Nw zOr2sL%xypDef)Si;p!T)j^{X43?r9960v$G#UUa98QreR`3fqR#t?`}m)cjQ$<`C( zieMr^@fD+g)L*q=Tsb;2c<$u`{Jz%em1w!ui0f>n5g#h_HAJ40_UYZWh(stK8~fl? zq-M%~6+;A*TE7^VJRyEvh?PDT0TFNJ`dXelRk{RG;EyY}WU1N`(&FMm6O%5Wp#n+hgYE z1cO3D;b?4RcKJb=oT47cRfB`E0!8EuBXHEeW?xuR1r<4+)M7}17RqdbNzoU-$Xqco z`%lQd?(zi6TqMkEuQF`M~ zeL=@)9&LPda@;S4j4pr>Qiu|fxMxM;upQ!|k0u&ES%`He~}HU3dxTQ7*mJPXC^xSMZ{YiaW>V!l)3#DxY7RM z%%bE6F*bP}q#Q(@`Yf30hG^&MiPRF5p<6Ksa0{pS3yIW+jIS6DBBhE%z=kbd6+`)C zKMQ0ZnXLR~khoO61>e{e3Pg+!d_@lXqax=$L)9#@lN4ksZtpOLSkDGKrMN;A81#@3 zDErjKX6G6{3`D8K;2d4H;t=c)P7WcWxYU!(?Rbjz?HlQB>V6NV{P{qJ>YeqM7hp~F zOCPgir?zu$-K|}a5gr5hU2vaA_tPem9sSksvIhCWCut+{Y==K9o+d7sUIJJ-JQ2V&~y8YPQja{mghZL1c zA8;_1+S}3Ul;3k1$PQJ6W)5BMD#LFu!LQ-A>rFvf&C1(eo-~O1p3~gi*L2>VUtUgx zhsO&U*^Qa6pre)!9TJeg?Z_K_jJdEnpZSA4DH?WXI@rkGd!B`x7sK4#>gT4OpZSOC zlo0kcw?LoOx9U^DonrLWrCdOp#nz4wG;|yEk0zTntUo$Vz<^R9ft;XfYbCCGV9|RD z0>WqH<^Hts=H>hV1!PW}4je#PSs8@Qls?#!g4oisGGh{KWjx1cZ+jpDV7W@KY9*3T9sp)M zGvfRV7O(d;68+c8iij}-8ONfGNI`g4ekRR3>oN4Qrc7_~B8rnZ4WFpd%w^SrB^s43 z1rBe1c`JOI7nHuUdvXMR=~vwN>4-E3;=tY?)Rhv6y{Be{Nf;(({b3@(63Hn%6tfOE z#Q0_qkUU$*?tzU>6iWW1r0LA$bn49Yus`R5Gs^vqqlOS40`oNug(1Lr@6iBjaIm7g zr<_?q@3yY#C@YWXUcwi3CuD23-;6d$mmiL}k6Ro|;G(Ohr8g5s#jJrzVzV>pRnidmjlPL``j(g% z6{`Tu#%v^WrP~qV(RS8&cmg@nkwB;-1%!!5@imn$x=I;*7&uu(R87FtnmA;$MQs%> zT7U5*-W}KW;kEuaJ>ssQ^T-KAA%G=egz^ecxhoV*W&@+|4Lw{uvsiOXz2lp+`#fk6 zos`p?*v$I+86iG?c9Y-d$H12_19TmXkBKL6mly=ANDhFdU&4>I0Z=~!ihmw8@b#Cg zVyl;Zl~UyPZgJ=U0B(k>b=n7kmCvz}Fh6htEk}cQf!A0*;ugV71RZ4+HH{XlN88SZ zDoJDv40w-_XKT|^c>sO2L!hx-+1F!gG#&nl%so9c?%ai@rrZj5Uf$-)N+aA=nX#F- z^u?J?weTi?ZM7d@&jDbgfBH2r@X`SkOT@P0i^Tjbhs8-szWVO&PuIVH{|0@c0?y?G zz2E*}@Hi-0BC7Vc?aVrs8*1b)BVXBE`_+T@;JjFVJv2KE}BG`W)Zv2g_B zwA@~UL>(kBp;Cmy2m6|4W>CG{?DA@w-SW2wxN^$}h7tjMbX1DkK3@+NxO^*~+v0pX zb|UGj?SQQAI#_pFLWDQ2{af(R>KXOqez@!&S$wOMpq{{Ji5l41SwWJxURXKk!JRW3 z7xGzgfH&P86LbW{&uZJ*fzCtavRMJSHpYO117kKiQKM$03D6hG-G1=YrN!pK(rA=;Oz}W5dObOiV%&$0M{aUGWe0 zPc00O0~eKMj@~QsGl%em8YE4nuz^8KX$XSuP6ObsGcwVA=10S!Swx4*Sm}dNQVwd$ zi1T8a{i6Tk*k_Sym}{zH!%Dno`#gYgL!#HAKcT_#s#qNR@(VJ&0#trYCK(`EUE+j4jh#^U%pe<&})z zc2)51GEArgWs2ja&8+0OXgC(Gqf?*7NR(f6%2baaf!$klb0Pu9{qVqBb(uw! z@4)`BQuWWD8iCD*43m&V`flQ}BuQ746KG6BZNtWzQThar08-gNG-BeoyNk8%+X>*E z%;!xz-`lKq_w$QxY~Po;JYHJ5(@6qcqHaJ@nU}gu!qk%cQ!Q3(zro1O0kh2_yT^h9 zoj66~cF#`G)#8gAaOTTZ&-GeDZv?)DXn%#3;FGq8BrRGb`$-4j=09QJdj_XLu=%MX%+s$+o{=5b+{1*ucVJwCf zh5&!!Y(;1~^imQzob$<@4^SMbuZD)!5O`Nz=SOG#M&Ar%b-s+JYl7eYndr-#D`+T2 z39==tzZ^y?K2y!$%`C{ljIo;D+^D(S@)=%kbZS=C+lua&tY?{CGcpFqG0$OWRzXDU?EsXdo-U9v#gL08Yb}-LBV9+0y=^Un54q zx}c%Ha@{5PeILfnEx66i3I6+p`i^S_>5>LKu&d$VAZ;PY#^xf1N$9&KL5HuaO{3or zU*8!7!epM6DqTTd)}5Xpv1CvSu zkvv^&I54mF=}U- zW}<`>p%81$Ifg$Q>KkBaXb@JeTnWop90!AAap>tD219m0uyi_55`A{r8jIO`(e#)W~rUVmO5c-@sOQ-L%j>`bpfWfcsxzqnIO1&(}Z9f<>$ z7l2N5Je$I(Q(uCLe&;K%fpDpx~x4YM^J#%n03J`^v}> zEz4SBlG9Im8#-kZP>BnjCp;f40|V^17*g5^E_i}rKG3t5n68CR%~HWC4q~){U};ED zxd~-zqy@8Ory=CBAtWmA&}rY@4)ZG zeP>((UROmma7>X6BC<@HJ-hcoC_D=D7tV$xksGp*8{igMBD1ljvGGOiN<}@-UywR* zU?;DCAdXDJAYjn^!ucMYi>H5PC-?5t>zB~<;u!3wsw-1PMk9IaP18i__SCmo6hIt z1IPa{NbBY!XPgm8WV6$zPM^Ni=Cm!3#G~_(e5gUI!a0IA+}=I=aUP7q!i5WA+O%mD zULpBwTD2vzNBRc_>i6y4_t26BOCIs(=eGt53jX2a`rR2wmtOkWqFJ+MpQP))n(4ET zS7**j+D8Vul$yFJ+J>!r_2T>zp{&p&PoFUb_BC`u1npMJiHM>NJ2W!Jf>v$}l29ga zz@LwgAZRt!XqQIexZ_qca)HW%J38CoNPQ!0*|r;e7+}c{2BB`s6qs692Q_uoP=thp zibfOFNytteW=<~^=T0w&4_|y52GkPJ*3t*{huZW#`}V-TeY>H#r3G4An*^Jhg|Z^A zo}U+hvf>hPN>!b{Xx2O-t0$%14M(K?2llIN?TyZ#-O*V5%irEHeddhW%RYM5RUOAI zTfTo{Vq)i0PrcQ6%{AwTfP;hM1BJgg@sPuNE5X>XLGo2O0t-r4R~Aj3wWPOi;B?Ut zmizO9`F4j*B!?UwRE*FWh8-u-s)}-mk93#=eHl^B=@N^LEfmH~4;^~v-PWV=b-7lwz2S$j&4I|z{p>N*i;nh73H5T;F? z1xpsMfT}4qFc_ME!HFF68AwG&nZ85}iH0Plq~dJtP(|?j9Z*(AEjPV|oI zbo{hE*5v{ZmV_yFt97Q%$~FWuW5O^qJ1l7fP^pWCVCgBa(`zvDfVITL{m5Emg=k2u zJ|R!g%lq)Y{cm4FC=Q6RCOl~*2TkVSBMY7cM~~b zhu)q(Xl!Z*8}1hoTP&$0tFv=#+A-$1(y|mq&_pVC(M2I;vnlbOo*vHbbNto7Xr5hB zd_k8>EyH0p1@GxuK3{NdQ9;2}x7S(Xpw>4?K@t&Yl{gtldKz&gLMOub2t{Fhd>qGb z7K#gtSbJW|Xrclsn&)(t%8q22YM*7*5gP0njg*#_^6ZCy13dQFV`?NWoVaMotm}vS zyI1|@&O069@j=b+FtP`B?M?@af-;f~a!GNii1VvR0==voP$nzV{Hw}J*5z{Zd5z=$ z1k}}M!-jp{>N0~`A87VoI9JX0i)w1Fxb}%d-olJjT<+E>{h`S%!jh_ zI#{}B8qAwj2L*mVnE9O=xP3CrUt9xAmeq+@=%>Qi#JJH|-voQMAJ(^S+YWp7?1r5O znuWawo1i4WOqe%gx==Tz0#>bFDIS0PDzTxVUf8*Nx6<0#?mT#4Z^0LC-dmejP(1tO zwI{Fn^d~-1-_p>swWFtF@49uoJcEOSvW6&iDtEw5WJ8$43vFNNFMwR<;oboD@2X9tuPmqJlt2}a{}C@yj1ByMNv z$+E)>m6esq827=Z&6{zeCGko-I;>6*l`OtL%>;d1{s83JJ>WwFt)x5%iqnB)fPb7%u*1SY!3r~@J(z~cAesH}oy(g=`FnU1m{<}x9y9uo9^t6YT$fZWW_ z@(}PMX_lyC#@eQC={W!^w=l~kD4@z(U@9zc5L^yBIFT4}t9Ja|g-*QH|BgkscMlv;@5StMqK+Mt?JnoPHXsYI8qFtsI!_%SFO0^ca(KU9X3795a5 zf+vO16Kc`vb~|uiWoT^ZfXbR`mH~4VI%{2uC(A(cNpL6TgfNLpGFHVbGIU6cPJ~9Yk?`nvRcY}*tj|5i|1B)_ zf2Fo|zPGirWtPKXzu<%uW}ZLT)l&SQU-|Axb8E-gqIpxKSZG4)9B9)V!y)yCo6hhp zU$#hY>uhE%jT1?nuQM7Wqh(2n+Es&hzu@?%fCz`i#zt3cELN1vWGnaV+Fn;zT{t@s z2+VU%shWkxV}3TB664Vrbai(ClKxbps*`;;Z%!r4J{=t$!RSA(+ZB~$X-TP6JUcKj z@JefI$L*({dg?~BH~uN>^s(f_mMvQxyY|+fw{G3Z*QC>_CF|dM%fER39I>pVNFE;> zj)z9ZwJq!4Q5MXYtFAa{tr(A`jpnvK5S(_1qdgKP-7&OjB}>zbN?j@Q)9H4B3+*kh zlLB-3knatEV#DWbHhfPCvT=b0cRc1ds%1iTwl9B=_FRPX+s%piN(5T$l&|s@S(xi;wb$Rr$2b{0J{#x;>RZHgG z`099>#6??uY34S!3?USEG|2TycEk&9YBqS^u0M@Z0rw2fw$(I~+MN*hc zZHRxG;bA(jBFmm=Y;J~e%4WzQE1XWl_~-=wJp$ELrT98!5vcf@0X;}Qw6?aQ;gtZV z7bjj71CEjlzbk=c29gg>FW6mPBu1P#!J~2SM5mR+8Vv>Fi7YF@pmh9rEQSkX6rF`M za}Hv-J)Ny}robeTE~2GZL8JnC%G6~-V4XS)tHFZ6oOH?nFwdx&1(DN55Uig%Q>OVL5XES?sFc=@oA*Sv53OHC*-|A|l|nb}@?Q#II2k^z7KUVN+~6l7Vb{7!5VDUccdqrS!3hF#!qyg6W_^zXyFm zBa@dgMn@Gv{gb~(g}l5x_Olp@**DPh$Bs?<4E@Yw&zG|26lGW#YaA2OxE=N>`ey*3 z5w~4XPzb}rLqI2%hh?+n`%!Rrm+HijS^4&(259)|G^1;>0D{+;%v@%<{>f4uHO zI4t9PNwqW|>~=*|6EQIwiwaU8AmA9qi14uB^OXv+EQ!s{O>(~5B{+jlp}&7z@_0Pd zOhUr%lYBm}uw=|Ul7N! z2Z>pgkq=HJ>!Ohuo6B$?*?fs}C5=d_x2HEeGCcPCsncivBrnhTPf>jTcgHa)iIcLs zM~)o9dwUi}FgrxNKSW2wA*TIKIUl3BNt-N<_l!w{!vhkIbrHv&;L4W-cYeNT$9q~7 zFrtgse{gV6!oTHN^A{_LaLAs>q+Li}`+Na+&}DO0BqF1Wic5=^d0eidq5f{|@++=x z-G8uUXyuBfqFu=fjYk^DuMn^P*bV-hzxesej;=OuGL_Kl>ZU;~nm~tXySQiHL0GZk zxJ}b)tG`rTUA6`OMR9D}=P@{zm;bBq_~Rg4bkU!D_J5m3`agYca@+ro+8RVOw6wG+ zNFX@o&70?uBzs|KBwC5{Zo!|n=T3*P!?prEjrnR zvuDqOsne!{18uWZB8?ILeJ~N8fJKWIL4JNvOyguZFxbE0aAW;X&ph?aH~(4D_G8J1 z8*aPJ`IQ@Q`e?B~@L4UZPityw1r3eTcq}0zA)LjvNBiNAzHhv_emy+$hX>^guewZr z_4VEG$Je*Q^aV3vbYKuhhsGG`Oc|(xRbETM6f+nH;6Pi+X>)>2^?=)BL)**^K3o?( zXt&yIc4il6&<4h}gz2AARs^NY#cJU7xyMOf}ek_)zwve(n%-PVC1_NpPPp}J71C{s=+8i zL)oQU(Qq(>^W*}98PhjQ2z0AegJ_9MvPt;Q5D+=-hcr%@J9h2H2{(=8fdhsHM@+^X zw>%M=K<8Irb#cut&5T)A(8H5e7dq{Bh@@kX!3p1QcjM%1hZqtHK74OcX(_s(HZwB= zom`4Yh9@SNW(9sPBOoX%BEz&SkX#UTmf>J(B;mb>s+$qX#T8XWgt^Ff|;&$|#`omj!(?1A^6GOlm$_=g+{fswEByRstT= zO~A#9Q5j560+0c>K|#JB3WG=h;JLKR3L{<8B!m33 z*Ne(d=sFMqL+8hoUyyMBWJY!{u}yr9^eLEL1?_v*(vgtNR-rZ|Bb;tGk`Q@NnxBXJ zlMlt^V9C2b{Ee9)6ltOR}tr;gJy}AUZ^^#|gEy z)#J$WKaR)tV9nHpqgg|n;*e6sX~cm82aM6NVb8>P$kWp`V6U#IP?s#7XBWVx`27W< z(~a(_O3OP5ZcqlaL8(D>%J6iEl3{9TvQDQ|?@tFq!loLX7;0r=usjw~kR)Pv&?nYN zU_+yv3>qnTZFNzU@bi+H-a-d~zth5Jzbr0$oRuMJx?#AtCbVt(o*Vz66mCn#GHxGn@ihE8Bl36K4@55-mNkfWkgc3z%$fC@YKr-3vj%ASy9K&#HYh3SZ8^QP5Q9A7p( zugc)yxQN@5ip$D`csweNj}ED5EJ{VirMlatO7TS8CI~iSbbL5BIhk;Yp|ei5Hiy%^ zCn>8bt80lwn)=U>n5{4{)Gwd`4L!XBL_`{XzgH-)sD#pzaxp(Ii2EiHVT&Y#q^I%z zdf>r_b(>8U&OiTrf#$%;xiFO$2L^_ReO~X+@BaOxzx>btym10J{vwVc6Ip^8t{>81 z!=S1Z`fZAqc6bUrsw6q2cs!1GiYTRxlp-6Fiqp9*nTo2ZYz8MdT@_>nIVPv%u;Hk~ z3qs2MIQ&$+|5Q;FY)BF*l)8rl(}r`m4WZa<4!o}g8f#R3!z6lf?NmwKS7NDghOK{i zoS0bjzr22jUGd<(=C;|9A{Pa^x!~Z=E!{u(@h`UTJ6zvAb83}NdOYo|oydi&o~y3C zw)namJ~s8O4ewMA4fm=io^XPmpI?OJY6#wXYlGO@-U+K$uYPOQ^5vhi*@OfC&H15o zq9P$8EMC0W*t~f&9-S;KU%p(Yxv!|G2#AC{+}9_0HI0ZnykQ09%{MpM@fd99op-KE zB#d3saZ-|~KccLPs-$TtN@*iOX_11xR3=BH8`U`CO--i|r#ypFDRIux@L4={f*}da zgphH*&qy?Jv1ZUhhUP?TC1aw?OgxAb{h4Mly~X13@jhZrq)9l1=zTWV;x;j$LGzJ- zK@u^G#ID=zrS4TarNPtlv0P+eVJth~IO1c*M6Epd;zf24i;_PJ`yY7114TGrXqQ(h1Z7AuNaJUl#7 zg7yKDvTB(>5Xg7CT~3_SBuk`;Wh+v9_J$*E`r#v8X!2-KH?34yymSc?$U*uVy|b+g z-gxs(CK7)3S!e2K@5y++bsU%L4;|WnM@>!LEB_Z_llR~Ms%z14bFQ9JQ+jiBB2+Un zFoHu-(PKy`4-b#(K957+xbZFc>es$%oOAlA#uLxID2F3)@rIj!0EM#_pl#!Yj;1D< z7!5IdNV7V=nPtZYvsbdIBops;V&H{{XcDP(;e%SqlW?WS>&3sFtmV9uUUztyEydcK z;|5b`d!^CVP2xU9qZ81NBzjA81AgZS+C(zcPN~6Q%mP@vbOG8rHIVOjGP^W_!4M*a zJT466Fij$~i-tmQNWEEkg0fVA~Y-3h7y;acC8inY{hM}@zT{0vCZ9~KC zNryq8EV6cJ%c0F~ut|zkM5#g<*M6DF#ab=`B|S&H}GCE+S=NXq^@H| zpwH(+TP0!8ybni}uz?dO{~q{r#Q(n}0r75_b$uJWm6eqhXh=-QXyhV%dQmVKoPq{N z5x(x3e6KDTxo_84#MZ3q&t*jXIRb+HMEfj}Od4b3A=tisJM{Mt2@4l2WDdmPL;GPO z5`lO$4qkMu4<9-RWo2bBb^0`L;)1sKKog8i43Z=v&Xx&`JjTF|(Z(q=W>9PzGP(y8 z)s0SW3Mb(RON~q7WX!Y&C}}_^0ZGax(=9PXGpj*k9VATV+7yMF%RZ})A1hlr-;mun2T-_6x6lC5SiV{wr?^T66<1-Wn~D0NkZgGR;aCoVeSVb zNpPVTj3n#DiBlW(fQLYy4{A$FpsFCB9aG=XfY~;bG$Ar^7#Qe>(ZM0eAQ3|uEE$oC zITX_k6=lfc^&ux;Ll>3GCs^5oOu7%6Nj6C$BLXNRgb^Z|spClRqhlcEfXY|sck;iJ zxXRgSp;aBMRcR2TyrpHuP*YO@6=fB;{|;#G=z->*0SHki4#{jsNq=MDz)yy`be{c(i#;(6%Q0rP?@+|0!>x_|LY!ohgw3HcU z<`G3)SWA@2ZCk%lG)l+CF4qW>HYu3`E#&f>qtg`TU^=9hONYk2Ilzs{JRXB(1BtBr z3Aq|H22371ncZYQL?(I;06sbN8=F-AW=sZ zb`C806iT6?bA{K0@p397&Dc3((k>>0M+rkWJ6{^x2ZB-N1MlkGnU;= zggCB4q8AN+58ho4#ils%6(@eyt%`yVnL2Mc7Io0JaGnw~NJL43q@tm%;J}e_%!s0_ z8ZxEpf#E>oP%#*ZiLVn6ge6sSHXPz#&_~MJiGtWGowRG<8?uFN0dsB==V{qu2a0IB!Zt#T1;AvM#Ta#Gcg1L?j!`%hQv1U#L!1bUIyXHl4NOLE!G<8DTAj zKpq|%mht|>nL?xMLTo3JEdtXho@C{i2_T!1L7YKkkr4?3QRg)6(erqs@qB4)9;N#n zuLGVLO&~YG(k&9LnngC7n1llpu*T11k-$vh97n{?v88r`+0t4tWSaU|FDR3ow7MY) zY@MUslC5=G)6J48+#@5MO{9~0HtTi-G@MnliD-X#xTm>q+t$4Y-`c!m%ZD$x^uR|y z{_zo@R5xHA#QV~4x9%;n=@f^vY%ye|wS`i;W}4j}n($Ven~Vb0uD!lEU!g~B2~ zeKnrlZ1Rr}+%LTI_9i&t#1oVYFT7ABr#qQQiNhm9hwy&?f#2_c0-f1^Mtc9TS&vNk zb>#`mKkBioHw+AqRrhxFfK5_$whm>f1XR)w9o(yb;g-)an`ry?tr*}c7r*}H?}~?p zbvW&ki{bE|UC@d1djjoHvi#zd{YDwRWD8-S$A&{D&+mnR*9C6M{KT6vk&LtQUMhRV zdybW_y4)TXbaA=yeNM{!bTQjUMZ1S=TRS?Qbf0C?S!Um*GBIWgjUoxz+|&%6U1q{u zc||EKUAh>KKWR10pEnKiF>sU7sT7c^v~=92%j_10XQ8XTPv5wCi*V#XgWlcM2P8w} za@)b-pt&lM9UATrcXhReTUwgaqoEMYoH;{2|3epNue{>Y!E7YGJro{$Z8SEq>%8;M zi@?7iaNIjF$hHQ_;#2UP%^V#W@n}W{BbYj=wGqFmkqx7<#ow@^Bcdb|JE72$p$ld? zHO>5th!9OJHnXQ6nPVCY%F+uo35q$beS`T9U@Z=;dK-f(tUH}xWe^)#dL8d2rg5Lf zK_k$c$ihx%(UsD&8EQ(aX-Qmbk_G;v3@#}d3i{|8hVzi_AW{iA4%|Bu9{RCv>P@1-VU@eD3i8WJkW3`Q794j*?Zh4xWKMY1G=-$@)Y9x|DfSq0RzCTdv; zCrX>gmWq#Q*`A(W%@J^B<#a|1jfZqw&_>(N3=a=$1qB60VPT<8Lv2rzICp0sfBf+*o#(%Fj^9l{{5?e{-;KX3 z0dXwoAn>HPkX@@l0%B?~zi^&im6swrI~(u65+ZhUhs2x!(|rNKsvVHTKeMx@6ORiP zC9ms{NMzu-XI?^wu@`2|n#v5ly6+9v4FYd7a-0*U zGnvy}u#Q8woGB}~Z7~Su&s>%Ro3ku3BKLWdV#&1@HCUy=(E|tO^yj|A)SNKg#H?YK zvzXsS{$$n?Yy=htB0rGw5vbdREIFa5q!^}6t%O1(Xu_l}edrMMjEA9Q+5)JknhL{h z`=Gz`0E8z-AfC{0Va!4j7cXMk^YROj759My7bn@SAYtId{ZQF4kn?5abRe=U@WZqq zH2l2Ix_`uR0gdAHnPp`(WQi&y2q~HEG%1TB%`zwyAe&N1q;Yb_?{&#YE_h(tlscGF zHwVT-3E0=r41;Jy%V_^ANLElgtAG98X2X#p0B@n()Jq{~>(E<=0C2 z1%=vEk34CgF@0)&en|kr;bGXidoPTRCCK=Ig9mp&M|-=L#N*JC35=a)nNgr1Nkik7 zNLk zyGUS=Q*!7%rmayQV? zG03jKampeR)VW2l?qQa-#Y&En7+^}J!{Kmde0)5P#Aj+`WGFj0(3|b(Vgk9g-tK;- zr*8nG!6Pb?5Tf1TR#zW?f`rDGwC>cCr0U8tcB;^HNDmE-q#7CyM|SVpAA9wcH>FLR z-gM!5?z-jXThwoT>stz5J1Pxfh9(K^68X^3(4=8>)c_!+Xe!%#S~l(mkq8w98Cl#)h_% z?7%T@$9c(~$!4ASSr=YMw8{-HMl9`kkEt?7>?N8^@HTbW?TTqQ65nN*4S7V-x_<<# z>`R_Zov?y=xX%A$L}8* z85>aY~BqDlmy6Iz2-SYX*f9CevZ~y<=3l9wqx!>Nh z{Q?`vU-WxCv+AZ)v93V9gM&gU9yi>0ic}D+Uspi&6!|Fh7k)-sxYy~KE;plb@n4IS%tE&|Il(n|9YiG4~ zHdx$IWF%>>7(=kGjb>P_t9A2Oa%P&v*;2Ito8xGwNE9#}iolV>hv2}zoyqN+H-&n- z2HS7E=`$NoIrHpydPfEt=FFMXk7MR7XH+j6s$57v7-Yd+i1TS^Vwt} zK5lRB8+M9X*zNUsT{dFlaB)edl3u(IeE6IfiJl-5s)gwB6r&ANj?9b?*DIF?L;ZMa z9F&~na5x2dD&%`+VTES!r`dW*W1_D-roMJs;a7c=gpfp4*x*B zJ8ho+-n41&yxFshZU_WQF6$p0@(&CQG4g>tOtLHeZjYWsviXMVu7X1yt*~k1CYZN) zk@(0TABOM#>It~zdq08pzGkRDcmzTtV~|4oQ=>pkmIw%3hLF;%t|){hGp0dJX(>2F zDl?{Xy%drG_%KK}Z5)Yl{s{`T8jVgG?fAs$ImS2)9FqfEq1W^{BYJ2Enu?H=q%?mciY9Sg^` zWs6S|uD;?@{rvM!9qjGx-98oyzapyQj#ExK{#1=Dw;KHeOD~Rqq)Juox`%N;U7mf?Fgemoc(BW#A@pn<=Nw zPCN^8lIav~Hij{xn4;LCIKSdDlFXnjoWglH9Y?Z*2uD2D6owO%I4)C?O-jlJks5l! ztTBXCN+7EvQAA{=f$Ly4cY@yMgm2FN5lc%;6|^DU=s$bW z_V;U=;6q=)p(uhaok(C%C9C3GOT}iq<9_Ow3H>7$z+InN~8T9!?8Gl z1jU3?b`FmXkA>of5}iA@GWzZoy8lfE1bYeo&UkNKF#Z>?20;`b_4HW3e*J5+X3Z+Y zFw_E7l9r>>v;^0w3VaROMZtq4jZ?_oDQ37b=d9=4B2#ezf3w+TArVi42c63+uDlc( z=DqOF#!bdh{~%OVmkXt(m5`rb455iJ2ox5;hS%SKM<0C*<}aMjMBJ!NC$%|RxNsSS zkPUw2jn`oLaRRKIyAZY?XoG>FBszRHc0c2tgCqp5j56fPL+EKU?_4(~J6Ev%GmerG z0*H_^y;w4RPW!>~*9>v;Ji$@J?CAA9_DVBHjTDl}z>-Y^E6Y;TC1w;Erc=aN2gB^2 z$sBQt6{)hwL>~Q~0+=&>4g{SF^mR7CGyC_$k?slb*3N@<7hDa+g(Yxc$5v?X=!VG9 zI4+7J5)2+FuPg+#N+5uSrHhCM>I_0g9+C?z&=8IwB$yT@}*QFx3zgByA!wof&mh=0jN%67~KjT@M_iRJTdj2K{D6 zKv0WY(+E_UbAD_IgPDme!(@IkE2qf0^5%Ufmqag5~hjb_K=M zKgp2+mXj~(B8zy576D+GA7hKRZn_j^x0jqoiuI2uijx@~W~9!tw}BO2m{v)r{OVHW&#uHijfyd;)0gBwr|n1eTW0=xnTtU^zXM zry=O6WQsWhl&~=op3ryh+n24cZ-@^M4`$I}OQ6R$hUCBy5^2dyMx(rNil9?C&!LE_ zT3S}+JoUn}T_>NiRzC5h6Xnv}Pk83JXZ42qhQz-82gCdJ9SF6zHij|MopL(^ z_G_-bUi{jv|7j~LD|KKbSE6zzjQX*|YHDhNXP$WmPCM<4ZY0Ity!-CE+u?nH+#>t< z<6CVdPR%}J`67p&$T+f+;k8LZ-th255CiG{nyP8;L}J+4)R1tYvE#uD%g8s0$L#kGd%`X9ze> z1a$C79|;XPjok>WGz~pl+{suxoE{$>9vB!L?C%;M9cxB@WdF39nuEd8(*D<8do6YK z)mQ7!J@=dw$L|!EzwoSp+ker4`i7C+dk;PuOT}ZSoqoE8i2-%O8)x+d9WHs27rASeOXcWcbbZvo~5AONN!{ht6A9cegNxk^G>)ME=O7*Lgi& z|FoI2=16^m{i%1hY#ObvukVAu>0V*~3NOFhqV8)u=$>Cwu9QsiibMS|=Z=HTu2fjF zdF%;$JRCwhR8q59n$=}?Otd_B@s7IZu7_|5&~b-F1=kI`-|w?M@v!F5g{;%A+8ua} zTm=P%&gzPwFRS^yc%(L`%i~aF(dqU297u>e@px@`?;!Cf;GHEiRcTt&bSo1~vhE?> zBz_F@*1^>k-KIAzSTJ8$dHgb=y0U_1SX{c(LodDbLemd_ z^waLm?`)PUD`(`t@ZxLbt5z*=MKq))PF4bKseTs`PF0V z-7mhrr!v7zA_kR}#d>IX$oS7MeG2N^Tcr(eykQ{u zAnf0{2X6W1PvH|^|2B^26b!Vr<9#?`+9!!PTaRdL=LZxxZt+YwY1I<2W6)=8s2jQ( z8ewo?kkSqDzHm|@6SzEf2o?mOAmCi>}adXO=)g=cAsvGR? zLbOE+z+X^=fh77pfPo}GRO5P5H)|d&TXQmg-!L>|aOcSWdiec)PryCDe+-r_ngeH@ zeJZS8vw)SyQm8(Y%rHG7yWcCGchR|U!rF1zwQZlVbLRn|4o-Oizgj-GQVqu@^l9Na z>g*XSvWE^gXghXn(+?clCA{>?+Wc!jcGc8*^X9HZ3*ogV9)9?xNHV$qnrp84KNG98 z)}@MlyY^g&F8IX`r*lp;787l%4d*c%`pj7Y*=d0?ElE^Ak&Fl|6NNh5WpqX!vj7|g zz+}^{VrdPzK#2g8-R)RB(HdEro^$qCNwZ-EQUqqRkxe66`f?T-AzRyqTwqDCO0_Ag z0j!R0M4+3biKOQ;uXWSrqMA1%l#VLOkvKk0m}W$ZmCK7q_2gu;k|ei(>^+Qh5p0vJ z`%#j`N(K?kRZ%eKPztWle&TRPOPA``(R=Xv1y(6U5mT?Xr$T1Og5~gvuQo;aJW;=4ZU$ZXGyebQ!Oq1N#s!D zEL%ghrvyPyQY9;H6N5ZMSyi>^$z%pyglNX$aA>KNU+?VfF+84#PFWe~r|Q&s;ql?e zjf*b2Xp;T;-x3=nP=5B=XG^rHIJ&3^( zjZ&6e`Gtipyj1SOyg&e1oM1d2511jM^xrOLJ9fwcAa_rIOUW@{WaTLm6jg~R2T>asiM;_6n3LpdJ@uhSrl zA{T^($;H6RTa`(`5bt+0LbYo3B4O6-=|=tGdN^?4h=CVNC@w32{DL5qRa8P%RW0n@ zxeE>-Jj9&c{@y-xsQno6uZH7SorqyC2R!)b1K_a9uyXb+Xz3ngk9d}h8;Kb^x@HPP zf|YA72r?l52G+ir#;S3Ygvdo)Oi@YWXza)O#V~S$&7zeZaV%(iCi8hTa+Hf48eq7imd(^_>k=>F3x9} z06OwlPP-egRWUR)9fl)E4&wKQVa~i+Fnf9h%YKL^Q;guz%)EN5>_nI>{U9fvI$K1? z`tdlEAlY3;Dw`A%i3xl@VVy^l!lA{SSbi!av7td78jpz!7cLaHZrdQQSayO;4j>+j zJ}@wl9v>M_H`O;K^SsWnD=xcY_|y}RACDoKGB!HlZ*FWNI!V)H)Y# z$6XjKP%(ljpraw!9STMjQX)FMK+$!x`wJNvRM^1k70kuJGMsZGfE;R#WtnK~XNI-3 z)mUp&=18I8LhVdxKBp)>J)5<(H>`ojwnB>=Yx$~alWe=$T|r~cJCO&ud_m@XnGr|J zU^gZMbBg7x5dpv=#pVq$Np6UuC9A+%!b*UaG8+#RB{S0$a|^REc`uo}X^n1+#$(qt zSBNEAyKE62?5P|HBALZYvLGJi^}t-5jog2DEObuj{7|X_ZPr4HDyg_St9QrI%hxl$Tc=o$ec{F%Z~Q-PsrW8U z1^K{%12XQa+R)kTD)HxgrcJB$j*X4_cI?>a^Z4DqMGHy;XijEF7p-OXRXkrM4M~6w!mko2`AV!9V^r69l#Ng221QNP~7&z)e z@1{8`s0T|+XV)LD?h7GNtH|DL@H^l6K}B0z$9yE=PnZ;Hy9dcv2d(e6uIuOz1cITFk&6>!Bl80e zL9Zz-%e?X26UHt7`3+qYB>m=_znH!JqVwq-*xNfh<^JA&7#bagRBwXVNv3&}#%L~y z6BDsY6(;Puuu3v>uAp;58DR<<_e4(9nI(dO2?yhPK`WXf+gWEUGPsFcl~_jCQLvh7 zoG2Lq!3=Dj%|9~hYGkg9j6V{r3@(eLw5kKqur@nxQPl?%jX>gp>9$b&X0U^SkzUp! zo3htvPN99E$fuP$E!dqfeMV6CxL9{6S#jHG5RPqpdwt8aS$2gDt)YIu%b_dYyh7QD(Q|us*;K{n@rjg87mtx+lt4mA z(Qwir%?b&}sfY$QF5`COrA1oDWMV8Zk}Ve$6iJ0eMI`V`P7js3u(3{SfoKuFbdMO; ztxe$BGv3K)38{2F)hkWywzFhmQ+iop)*lop)&8^NzB&>FIU}&Vf z^MT7QJOBQ|!kS_DCxPa*2OocY=IYf`F0Cr5xD1`n+V1WlTq{yWGL;>7H#ID1yaM2BXHtbSHYG4@p)JewC9MEBYTQ<8IvW! zLH7whSrG_=5C9{dhzbduo8oAXn9gzxqT?eF>Xx7fze}JJ}*-P`uTO`rY-5*|JzB38p$XPf|w=goki-wPD`9uG%Z z#?y(Xt`z1kn;{%J*a!#r9yWSMx`mR$VzIcWNSaYMSD!z35zLxB&DghRSN65HUdg_( z{x$W&4_z?h`s=T+IP;uyRwLQ+<_~VW?X`7hpS^#{k|qE2wT$jHBtS+#eBSx(#id2v zxN=m7!{c6`--ok-BH}uo-L-x46WJ^mJz%?Rk zkbV={&WU8)ppx!*B0*i&COhD<6oHJ{9gmP(XCx|N)`=+BSq7H~CRt0Fy_6YCfhIE; zlM5T^?03p4mPI5@WIGE`39{*%5J@Pjlojk`HkggddPoe*8b|C~@jXUI2>YDPcC3SHWe>5Z*>ORZVps|eU&pbTvlPHGbXo(fz6HjlpJSM0 zCI)>3S)=3)gYqsiS%cN@8e*CRFLbJN)~SM4#-%rd)0ZBn$YL}aN>SZXG@>EZpd*Qq zjpO{Dc6&W3JgI57Q%$OBArc)%Mo<&k8B_B!X)T~3X`0@-^Kd$bR&;UklFeQrV&d1B*CZoc;?h zstck|;7cYVzC<{VWPfhGJIhUpx!I&4aT!Ra^Zw7JGXwh!QW z+>f^U27K#oAk}xPbnsmy#NS;=_3uw<{5SY>J8vBvDJB=_l2oImwdtzeHpgzW&9NzR zsoiGxQ)31kDp%B8}+58Ve(KJheMcHsrEc;Rel>KcW8jf0R( z)8$J~w!Ji2M__`NSrS07o-;{8V{#&qSl0@J87%BrCy!dNoHxzPrx2_dZ7yOvsSPn{ zNU^_j9HSXAGTBxfUREi*8KI%_Fp7}7TsH9GqJSX|m{L;?9-9cw`*y(2-G`wkEkgC; z6Jhbnb>PCuH6DpW>)}=yYHkFNFb-a1;oWKwJT3=o$2l=J3d4ha(AV7xXc(hY9>Rg2 z!3ELDI$01w;BtDHvFLI846oO3Pu-kf4F>E2n>!)BtRER&A4Of?wUFqySrm;fvKZN;9#B~Zt5DV~!Pztt1Yv`+YlwUx?HOF*GL+VqNW{`Z zBcpmclNCKK4-=8hFDlI9*v|(1c|v|ck=-Bgqk-?k3oI*w*~Ufy`2W+`EK-r8AGt2M zIX#2PQ%+q%9(eF!c>2kwb-XWn3k&mJTe@`l3s+om#XGp|QTPWSxA=VXo8J^}z4cb1 zqoYF^92``8n|obaN_I_OFx4vwI?mCeH<3xW(#eD;ozA$zv9v>woNk=QJprFL9~ZSol3h=R#+4#4gQG6Z#2Qb(BySw;{fQz`aZ?gFt(Li0J55Rt(L z=D0MhpR`?LGS@9fBAer$IjUx#V9KOo>B?lxnXUE=mT{A&{X%wwb#g*h0>=?XF(a9w zf&N5KUw3385*|yX(u3KQF^FDrzifBK@mxwJC3#Md+gSj^Ks>)%RFYR$TvSq5SzZzJ z`{)vg+Q8sw{owG>i+_CZzPG#Ey9&=c|3g<@^x+R5pGs5udPqOg&}_W&%4^cU>g)SLM^j5N8Oh7n91lIi!|_l2$IZzXpLhfo zFIg#m=eyrhPg%FlPSX{gTOu4L#zRIl5+Mf&60D{@15`as=bPyy38JOBVxBjhWyzW1 zzf>MY1cj7~9%^QEk7XmsW^Fy)6*9~CGgy}^5OW5wd97IanRV_B>mmwPX_iTjimX%+ zEUg(P=bRJYrS+Hydz_z0oSF=Au%drd;zlKJrhtWMXtT~G;$;3bm0vYI^VAb>pwao_ z>8GE)3!jVqt;X7&ciyQahK6d>M*N~oWZ;U*s+y?9R+Z3>+Yn@>C zkilA$heG2_;BoeY*yM2=2AM$L*r=Ggk7x4Y&Q=j@& zC;SsYQOm`PPcEM}rQ(F@+TsgTo4PI(?)UWe4rAa&&?7is7#OUi!TDz#2Z@ORc4aT%9%(+%^g_eI)>p3k+;wfjKdH0HvB1T{h7MnCUw7x4v1sHp3=*xGJ!5K~ z#}k0(UUfk>TM9Yn~PU9GZ=q)4Xl5l^x} z9f{OyX@RoP*g0dww`s-URW$trgOyUV#fCPgrjDVnK7FSrZGQEG+)nc*07|^ayB8& zY2+(X5;_IjXmljgWcAUXxH@Y(vkFKzEt}4e!AI^@1~;7~#dj+E zAUzb3Iq68bvx*g#b}J#TORdRpszln*Wv6sB8%+@ySaI59$%b|)+C88!@w%g$MUy=U zawIujLo;hnVBI6xp^9QI;5IpdvvOt9aDl{Wqg+sd%PiPHJrezofDMSiC-|2LDFmmA~g>lQcU?!F(kZe1^Y z=PtdnM?Xx&BM`*}M`QM_WDSxmT~RT2NM^LfIyk(@ilyn0J6tXIS}t{5 z%%%OCET&{8mNUbfrF;rUUqANr84|nMbS>v7vUF6D`Cepu_?eOk$AYv#QNeJz&UB>X}2<+Rx4+c8-;Q>y7%@xq6&6qE)T62Q9a_#ZBxEf;9zMUZ8 zWIc1*bSNn;F-zW%@$2kphF4yF5xj06EM2-95;$=Upy88Z-8N(}T}kR7ATR=gh#&r$%;_Ejw#|0C$%=woWXN#Y}um54013tchXriGXN}&16{Xz zqU1`442wXJtxOWl8foTuHtUV7?1o&VSvZ=a&IplQy0VZnUd=f*w^Xozw6(~QuodYU zkt4zOLDMa##Bv%e7)LYQVe&>WXJIR0!@TE2%bCd)TY#R^eh}fik7yk`=QZ8J%8-L(!R7lvU6Sg4KeCQJ^g0o1H?YG?i;K{H~-XBJgA3un`N5 z!RXioQ1Uy8SbDu~C@C%x&~Rh9{74oF3e)spNt&6)`h%$_o_N-{^LKYE7y(pIIr+@U zM=rVO@vU1o-ErD!r`2D4@x>ba9kFRsgD2>brg|M}>EPgig5;Qu^7tdMCVycUUI+;mlfkBEO)`n1>{%8~Vg=*^WwX%s$N;1@ zfDAK=;Ia%S_C1zyLKH8R1>iNKD3B`2mP0Eu<4RU+Y-A{=uqd1N=|(h5(5zCRoReo6 zJSK^3OggH$(PrKk);QG#Yut$@pTcwwh=q|^G}C4iX{n$c&fsgNn2sTkiZE@|iO1of zE;=R;Zhk+W5B=c21`7Wh3m434yyy3Sc;}vb?lJyw-yc>_tD7<-8c&O3W8?avLr07a z?`)PZ!kIng#N&SR!yo?et6%!km;OH?IO^nqQQEagrrmJx;DOV(ZG78#;iZ>R1UvEK zt8c~s^Oi4Vnj0HapZ)CT^iO~KCZFA*_}y-gXeWo|f5x9U6q>G2W2GdBxS&IM2QO2C=i@$oRLvb8E}K!!78f*+;1?#_d;q zcDvgj@C8a;9#5g)AMh%w6P&73R2_zFvnx2ZWQ^RB-jH2V6_<#&wc&F+aoLmvnUzke zCedwJ0@8-*9Fc|=rJGasOe}`6!8Ap3)6B8Tu1UoH-+ap#Q_no}^h2kvJLyMdWo3=<4+c4XFTM0ur6d!UZ4xxNzYo zZh`ARaUC?FGv3(J0^L1r5SbWfjwqdLoG@YOjB2>*!qf5h82s*zU%*2TJ`Lwyb(Qg@ zuYDUj+FK2eDhY=VcfkFRy~u2!(a|0wnToJQCmBz%V3ejMSVI`5q>2~MPMHz7rl9j8 zx=Ezl>o(k82iVa*B$7ZDY>>)I!bBts<9IE@)TB%Hg2Ph``NbtrT2TrmMTJo4p*HDm z%9>(Awu(tN0@tY9LU?VX*t)@;_dyt7$vYHi5Onlsb*?stBy&zL@Av~QquZ&TyJ7hJOc zwR0{$rx*SYKm^oPH{4Ls-_>=}cz@T~OO`KNQ&duw-_Y6xmt1+R-f(cQvF^m<;gnO> z82K1H!}x+RWy(~opddf%@p#g~ydXNMG+6)>a#R;6n4JJKj6^_> zL6c2e&Bh|uU>XrJ%SfeWXQrk^uJl`AbEak)HpVe!;LJp^=Ed9+m77n^PIV+onqu}B z)7UX46)_^kyI!Y|CQV^OGbHctP)+z!Hd zXqJ#$`($O!nzDbw(e;$UDy=n*J}^s4EMJ{gJ}U!6xAsnA;W#Tgz>d)TPde%sTK21G zmG+rF6XT+D?-6qT{ZY%&IA&eR{m%C4m|dm9J?8CMpU?S%??#S1VJ!k!B+M{U37Sij z29;xCuuDuPQ%K-rz$=kt8M#b27VjOCNK^%x#)(J*SCU|N+Q5VJt=H!#0cQ-zI;=p4 z%!sC(ZEs57Q)LEyD{P??kdJPIL%)6jU~N2)DNbNtRu>!KT{UQW8{# zzCKN#H?N4&{{L>m%KvNv;yq*6u3ZW`yAEBqyG1b>z=f_zG?ZW>9WTL?I13}}hWoZ~4NfjjKL-3D|J9{MI+W z1;4rTPB?ze8vW|4uM(*&IeHfi7B%r8Qhhx7!?imWU)sIfB8AB+Eulf!mx6v|Klk$t_qix~7e*v$`z{ zlnO@!)bB+j0Nu_aWU#9$Y9Tb(4=qRbLRZ@$bm}gcb?yZ)cjg>GW&nBzqtG=lfCNPd zD)SvMrO+)Pv!f@XA+4#U9yYzRNoZ?tq!S9WX3iDYp0HM4v0{a=c{5w{p6?47Gp5ecU3QmNUReWeUA@MR z-FwoTx9mu7dFLH%pl6>Gl4PEMG(R7a)S{YF?PKAG!h;8`d5B-r}pdL z{5t*9AAJv=fB9AU?9)$Cb#}HTycv_i_HA1z4Og5pWqJyYv?qppTfQ}O!2%}kclhvO zb#FsMp~vo?Idkfi<>>4jkIv$v^73+@EZasWCL--kO-&tJwruP>eE8rHj?-?8?zZFW zeQ3;&-g3(=vD`JI1@5AYF2V?Tfx2naCfjT8yknm;f2Kd^&8LIRw<}VCrls?7n#x1R zHb8=3f+4tMRnB*~oP|^ipNJ)>uGb-wv_82w)XmAtDJG|iGEs=_CdY^(gF=p>whVSb zFe8L)S{6+woUv@Vlse|NnYD6sa}3m9wSE|rQRx&TAQYXI4_TdqNZ6Ok6U=BjmBgSkNYPg!6)*~y zOeU>t0o_ai*Q~4{8iR%zEyuZm@w&1ZWM#wxkql%EQyCdOn2;+*W@XjJG8@RLWMx4{ zPN38z=W-+!jZZNbWntGh$37i}nfYMxPJ;L+&ztG3X4fP(R%xus=DlPJnn|Wki8GED`|p1OK6CTuh0{-2m1${g4edXCD0BY#=X%ju z@}Og{k7I;->(*_s>%c+rg7eQ$tynbsC%4~z`*%M7`OhaNH~FqC#lCPjf3UUnte2mC z_5z$6Px$1gKjSN{EYTwqvGG6LbAQiwfAGWoICeX}{MB2Ek(`)@1Z!DgabdvabjZlY zi3;`I6!BNe=*kiSlwrLu>6+(S4w3;(1~`)zptMplc&&4n>+EBmM~VHeO^Q5QBx6%eGba*_>u4z$e5Xx{(5rv~wsA*Q)9tWq#0g*_AWka|< z&Pk)2=uRYwh44g3%*!i?w6rws*s){R6JujzuYdZ}pY9Y`RO0W0#=gG%@q-7i@9b&$ zkKUod!caJ(7vvWTt4=sc80c&pd*kWHoAxw!x1j&noXSLdsWmKUQX!6}DUn#J28U^} zu1Tuf;ZglTo85^&old*t^?5{(*CV)*!1uU3Vm><34hMBnkXRHqmC_ioAW7z&Pv?&! zsdV46Qc5P2Y(oPWgB1=to*x&r?E{a`gX@i(b#6IUSTBM1Z2!mv95~Vd`wkw0hK42> z7#Pu9c5mjmr3>O0o_EI3%(~Jz+T{K>-hMl9^XG4N9Nf2C{NyJ-O9GU_ORv24RxA?x z&P5lUzwsX_E=Obk@yFjPojSGngu+7aSp_9U$IF6TJ~%QeP>c8B!C}e)N4v)+pa0LV>0_ao{8rY20}>@+<7ICA1uN(sRGI?E0K`%LVke@ZA=e@ z!$~->Zy&t*#w%zi?!pkS2B)982Cn+(g>c-mx!|_j7|Be!2w9w;hLE_ZuWy8owl3yq z78Dlay;lm}fE#Vo4&&vQUp8KN`4ue^i5Z{zp-Q+*?pk+BtPCdUON40=$}CB_v6CIevMg0OOBkr7!e(}`?FJ=YaXgQG3lnd89-%8V}Ie3~`QA6nh?j7b<yoCrtlJt8&~(X{!jNLel$ z_b7D835F9yEBo-+HI!v)9YWJvIy2pGIp z>13K+16tQ6A`v4IjR@I9Qm0Z&D#f8pn6c3@9eordjlV~tQHZ5e2Cd!U@C1p8LkNA# z;o(t{v<=PjYMsrcZX|G#)DRS4)-X*b#h6v)w;R+EiBOAac_!zTG{;#9`)i#lVmOIvnmAm&-|-5~X;; zos@Qy(Xc&ds3-jhnQ{*4t z=X2Y@g+bKvd_R;G=RsdbCyewBKx;=gw2e!!?81*i&HSY>KF|phLtQX}Ay{HJrqb+Ui zeRv!8X0-U=V1NG@ItUr3-R=zfe1&M36qlBjnBA*iyBW=cp=bweD z*-MO@zx{*c&0n}V`P5U7>94KdU_a^jRZhcfk_t#52-~)8MPtg11Vmk^D6jB0cin#1 zPj9^W#*ufAMIy*Bx>Fj7jGmC6UwnQ^N!jYrk@27?DY!nNojWq#*V5k7+0)b277B$r z#wNzw@s4lL&(H6}#VGul&wM8JKaF)FC}^ysqiG9=!xB197?ljy;$wARU*GcH-rmwg zG8#xF(*ZQjofz?wi;D~KQ6QnSDg^!cfRStVuo4WI@F~j-Amg0$I#@LCDAA=^EnZFc z&GM}wH|dMVIQ+O^$Y$n%Y%LTy#+ODZBRx!7!^)1Z@_ejqn+&#OIvUn@SPs>s7Z1k> z8|CgY<_H)Trz>h!%fg&Wtyw}s{0nsnfn#(5a;e&^S$WnHz_V`IqYSxujApkIy1%Rr zGPEEHCP87gv^C7mBeLmGQ`eJNA_XJEVd(Gc#-DvKIzDdn42;0g$OueC!$OQY!2k(} z*&&c$0992rFt=_xE-Wq>3yr|Wx8H)gDaG*FPhO`N_yftG{_sb`|MmUvb>m`2>frW| zf9!hycfS8UUtV6Gucf8+$j^WBv+w-!x4(P~{th4m>+sgCf!3y`Id-SCyt;12+F*I@ zaZab|HwhYvG3w0W$7__0F_O?4!050=up5&L9d);%g_hW48fr))DwfeiWh$P!|^3^r`~qsjW_=6uDkB~i}lt=cI_%b3+MD#UVQEH<5sO&J9oi6 z(dn{}cXV~U_=PWg;U^wXUGq1;`HB3Yp`qH5k&!Zw-{VRpWI5 zwKdhy-#egpbhZo2S1c2zPoI7?@`r;uk&KBG;R&k69X?Wjf5;a%i7XzkA2f#(fP#4?gw$pv|TB9)H~O z;mCMYsGB+?Z`SO26<&X!#23hOi!>XmnnT3{NAh36h#9RVOzXm~+Vuq9$GAR0e?L0e z!-Gg7I9WC$=?YMtxXQXPVMGn@Ga?x1Im!Z2oi3A@v(As14X5L_qR`XR3$3kf(1H$U zV@nsbcJ^XqJOHaaQP6@dw1;*<9wHW`spXp$;t{pyZsmH zXaD0<-h+pZw7mJooB#D6|MBTZ@H+g%1PcH7$5%?cUS)N4W$D>7W=%a&Ri*N1G%R*> zwgIlQI<=y7JKab+6f6Qv`l~H<(K4H^X94XctpJHBR2{! zzOf0u^|N0KC#_it2kVbO1KMJPgCj7Gfh}hv8@k|jiJ2~6Ozfa zK9062BlRd+no0{8dlNF*IB1E8p=P5%d7-IvmWW)IAx0U9R6C9&Gf*Gef;^8v?apha z&VV#V;Tt;!prt1a<6#}_fg%V{HcL@4)K*nN5fTx>B0uE$J#2kEbhr^Vy#6{o|KjtI ziH*VOXRU=RK5{;+UAqSIy>3Ptl1OWA}(IM0H-7g$EnzmNTmLlpC5Smj5E#{ z#rG(up1MeFZS8ZFPnqr$WWPJ3C7pUaZI?Arkwq32yGY7j5D7)qp`nm_A{;A9B*W8^ ziO4)%%Ty!v=aytuL5EU8$5JHGUuum?K_=>&M&!Dw^+4U_6f;W?j)vqJ(T?{$z0P#9 zY-YCFWXiv2Wmgzxrh-X_o8^#Zbl-B&*+1lm$V{h>Wunp8)6p?5N(*|Is!_OxaofcBqdHlY0_v&wvH6%~&Loy@gYWo9B} ze6XxK%7Dydt;{a^U1uZ%Jz|Ljk||~hCapR2J}R-qHH+O-kyr@F?Ez@OdpU&n8j&`N zBwB>5d7fD`3Kyvej`JjCm*5&6Po^0WltH5izsESnTR+zCHoZMu^IR@3D?2a7b&-PM z`FRCIF0e8-pV!MW%X~gNc->C)^&AG-K2En8WHgvY2%ZL zAxOEb4?(bkZp;!l*HST;Q6o|ZaYIky_C#v2t%zzj+C+Ebh`U#C3H|Ul;(bLxycA3K*n?40U_|CVCU;XN*xaj>zy!4VAjFV439ky?O8}{tkh8Nu_EL^Y@jl3FUu16q> z>=dc|4&fji#osMMQ+)S+QTmf?n6>(>y$wm0Qh5Lwwk&E4lMaySpb5;eA|s2X3!7>$ zrW0sI4q3XAdCWTNM@}~)8f}z5<3wX3&m%!)Q6A)Z9njO!3ZdR1=tS~i?_dNLo^>@W zUVah`_oJaP-iMzV1)tpsQ!C4%+=+}vs6YG0n=hwcdE>>DO|fOJx#l{!>C-nU)27u? zDS(ttX9qE|-*n{Ak;d`y(dLn{NOM_DU3dNd{Xk4U;!UrsiW<+R_2ia~ z??6>erCd^0;+z;8vDrLs!RztE`Zu2!Z@TU#o!ZjheDfD!dQCaJ^xQ+R{_Tyh>V&hP zYUWHB#wgzeO&WO9r*MK%@SF$H?;CB~Z@l&5E5eGi&eyN~)MulYeE72P;l?BS+ncv~ zmo1#@)v5LzXD}onVB5BBg3IF+s%okR%7R7Ty8FI+@BT{yf6qSqP|?W9=S?KDrq+egCA-T>eA7fc79Hsw7#L`%uW#?|?dd`%cUX%h2VD-^(Da$Jhef;Cch*^F zP5e)c;{N^nT?dXFSl83tbw+nb=X4w(g>&c5^Ou#EdC*`~+!(^b=!8s*3^^*2q%rnY z&zO!C5i%?aNWH#vii$`qN?}+IJPB!0Ix_1|ZGETC#Q2V8MC6p24Z+lg5QUt$o3+rH zzP{;98Anx|1#2Yhx$^HE=a5S$H1x@|`COL6(JmOmWXc|mds!yyDfL7aHh3tF5{1#QglSx#iPm_@n8RNLdI;#>neBMMtTYn8*YoA%;eV z(5N1ug&TdOl#xm%%+5GkE+IsmT<~%>0&B~wnNfc-Oo%|BR=%{5)8!*kLUcY2BDIJ- zV8U5431{JV+ennq&eGvAIxE-}(O?d~*?KnTiJCSHm1jWOYN=?7K$?*VGa5mnU}>uz zaE8Q3@j9~9@??q`EfggrVL}oXGqY$+BhJ)%5X}gJ6?vj#G9xr*l%38&nh0tlZ0I{k zAee{;EzO3RW|0_aEAxl$BQkn&k$)y&$+9~PHWo89m*IYs(tVcIvns4SpuD1(2{;l# z=Jrx_vJ@(6s$kxnS`ai{G(@(JFnWZvZAM1M;jImupu2BC#E9xpZCTz|k$nICUz0`C zbhv53qfb5Y)zeNt2rDVe*VmsO~1VH+;ijBc%l#|7w-8XF5;7G>8 zXS!XsPfn|wwh~DJxwN!I_j&!yxDDX3xR6k$t^m=9nVnz~Ri?qn99x#<0PNbbk^p9q zYG$c`If~i&B5|$^Bj?R)PV0b~Z78zZU-6Hi*B4-EEc-}vgS#_#UEJ2%}K z-}}LT3DfGPi?6);ig?S-HzO%Aee&4Dk&sB^3Fox_4IAEma{vB4fB5>>zqR)tS*PCI z+-$q+_S*wEPV=t3=9*$(o_G45ZJQSdT((8~5AUr^f>VtoGO33jcp!$v$S5wO;W_i> z>;LuL@4MElUhPMJ#SNxpRjwYCiiO3{SXdkx?NK{AJEg-14@;w?A(2R)+9?&VV#RW( znNmkFKr@?%-BYGeFTF2jm81L6Glddp$iFz&dz@J=~#qAh%`Dh}GPG5_pOjSCWl6w2P zprfPJ7#SWl+-QIJydI$-FE4|0QTmlvU(I~;+uzM%#2c=@@+$GScikx?p<~~(Yqxy< zc^5%cbHGpTd=REgt$>634x=;Q4g(Xz5FQSJj<%!fl5nk!gMp-AWkEiC{E{<3PetMT zKmHjUJkkW;_|~_fqO8KW{EACJQgzs|Z6_4w7sD&Bysh_+B*ZgMJ;f+3mRaE+at~q@ zw;72}Kx6`K(&3OXG>F7RI4Sht?}>PdmCg7)H)2I7!vNTB3#GH!H0xFFyDh@L^)JfltNw* zNh!Mk4eed<>ho{FTdzI~$>-cmcoaI?+M%VrlXc{A zs!mqwR8@_RSBVc^dG&SUp5H%YbaxMEr=NPVaKp#0RTj>l6^%s4cXqXQJ=}Y^^Vu`6 zz4)IdLi*=$aP#IV!;#2GRvfqTO4(+c@$B=@!>g~prlWJLA*mwHUobB{fBu5K)2Gk8 zPf^4t{~C=Znm_OQ#V=~3q0n)OM0QO&ky$)GF;?aA+gyP_Kt|t2EG;X+^PbPjVkj_@ zb3qJio@6E2lHK$xL^Ci$bE-*%%PbXC7o|z997(ifx^saVfXOT-rpqIlfftdH8>&e{ zuk0)HsbQFLCzc^++MPsZ(7rRl zP7--0o0YAz8OW7lvP@jV%A{p$f^Lb9n}X96R1hsq3-Opz;?KfA1X~D`-zAl zyWIuFg@sUAUe3zdF-l9q&qTxtMMVV=L|f72bYtMqw0+PYvx-cRfDu$v>xkL2IghvI|k^sE1!Y_aG0DS&)x4^WzQsLppelO4f zC_uw*O3e($GEt;}495&!sDbeajN;#nW}q}868R<)f%O^BvPg{C0l+K>B7Fk=XiW~Q z8v+3)qr%cLMI01?V@<{k8O-{lEQ>4?JQkku8x(jvPGPuxZP#oiE1IiCt16(Va{tW4GRV>wB&Ny7#u*?CR91 z6;o@cF7gL`Cnb`J6CE~3t;6n=sl}#jnU1UNm&Kl<8#!JKJR#HU_(3|g8S;pCIghJx~HXzc2S35>$9 z_K$e1E=WP&kpsp%n>Gsvo-j)6ukWIO3p*!O0>I*# zgnF*aR9`~=HH&;?V0dT%$j}%a!*Mx2hE7(D)sW>{w(3lYpNw>*z;M!*ae-phfH*1T zq*O(6)>8DGSq4E7V`A1B5y8^-z?z_NOloGwfn3RuZYicyBwN$1nm#5BY5AXmZYphv zW{n^%aAwmsagrIQ=WOU;vV=fdFsM!s%(|na-89TpJo9$Z&4@Bn88p|rTnH&^3A>=R{sPz>GK}?(LXpcG z-n;Fc*2f-up!fbq9!Xw*%_o)L-u;Uicp|6bSIZ+qBgQMQyz(+GTwhJAg8I z4!-b0o*492o^jsAmFcWjp3+itE9$1KLE@nfBQi`_&SrON_)}=_XfqT=6_zhsijJd= z3H{Q%NNL;AXapC)C~3W*A1<-9O3D&u4_RgHyKFYI41&%X6ZS(Vl!PV~5>G>;7fA_& z)Vj?~P1BIcX3Q)E)@=u`kCxF`p#-HkX0j>fu#?y@ExxfRQyhmxD#oH3L@vZh1d)*+ z8Iy>jc!6k}tRf+iLDtJ%G&N+ORXf|u6xyrJsU%;|6M6(2m2p5BCV~LDlrZG>X3=uVyG2o(y-wTul zl87bo_!1x^S(1-N%Rp}@EMGboRxX)^-x)e{L+eBPiPjkux8%F2h=zxn#39UUF}jxFgR zJpAy(6;26MDRc5!luKmSTRJAI z#4Op!FhV9fS>|O4JsXCV*_G3MFtfQ7NyUh>#*F0f=mhi*4nk37mHw44e@dTTQ>p*- zjvvC8ZvLu%=kI>2UwYA{Vk(oA&2&^z^dhlC#{2Ndut@9w;NW2MjvYH5oi=USW5*qL z+~I$=NFD`TF248_4@Syne)hBfan2);Jayj0=x|L%WsUai3$IP=+O;=IMLDGfdFh$c zX2dI|&xse6mM4QATMF;FEOL!TbS$9^4E6Zy8+Q~SAyaz%niKqM*PW!yn>SZ*xxGyA zbs{nj$%#=2O+;Bue?L04qazbA9*Q8jFp9yLF(h51Fph-%1Ui&-Er=+iEKLv2bAdoU z&J8#Rx||>*d8lJlt}y6_+L8cVbjoU>vM8Wopdt3tJAU4?|KOqS{(+%TEIgk1kI#L+ z==PufWEm1q-aCHrt5=pSTl}rnt5@&%dzM~M86{rFn&D7qLi1+H5CoF*{ zo_q}Md*n%&HG2{K`g_r7>KPUXhtYo* zQc&o(89pZ%Hdz-N+MA%hu~Af#8Dr6c>Bia>v&Ch{ErfxgFuc979uD>*v5~L2)r7hwBd?gxB793Eq6|Z5ZtCfW>ncz%|!i2$y{L9JKH3 z<{C|zGO;kUHMK%dXD{m_p*n0B?Jb6y+8P+c0N)c&JPpr0_YAc?fRBFkqtca^Ut#10 z{2fOckG!~d@7^bIKHvU-*|ziEd+)WE=9R8od)m5d6jeHRd_3fR!% zojqb%Y5CAuXP^1Tnzbi9jCS)|7|s5(nNmms%U54?QNAHNraIipe6L+tqB?x@CDl=Z zM7Rqtt71iYfR#Jo{65&%2lWlDjPMKNeVVc&_-u_~+D8(zTL26za3rv-G0NU#r4*A@ zum+a{d{A|?#c$`Uri4CX^JS#SD!l?6!zYsoQK=0t{xER)R<#{fnY z15!~&l&}mFEBg^icD%nZh~jVx^gT|8%b*}3>#QVA))@aqo>Ry^9np4yYYHQV^^ne@r4&lT^)zr$FEqW-+T9+;$8RLCqMnz6ViA8>o%jl zy;0n_@ofq%lkr2C-Sv2s*kiiX8``>D8;UudrTehrX#i~^+Fxt0fB0M@v zl?AsVh^nkOWp5y0i^rlqB-xzQ^dy~18oq$r7>(%351oHgEHv0}Y~J*Y!(q!h&OGm2 zsi~n)yzLubm*!6^gy)`m5Djt-PCWfA&{P+6boH@TV0JtbH61e!Yb%shhtM zubDo(@4O2yZ1NWr>KosDdrn0~aV;{25{chYzw(_scVe`rKq@aT-xrFFeR0;TS+B!i zi7nmTj_pspJbm%fIja{gS+Lfjs4H|sEMcQS6ejDR$>E?0bRrzd#3Qj`TvY2Zy0m39 zG_qsg!GoQi)@&FiI!8eXX9C50&XOn1WeQttnV@>Kqf*Yn*A@R7>UjWwTsFL_%L>w zRO~gASR(;AfoZJ}nWUJvf~DJFrVtsd6_MHUT7=221i7`^Feh%tf*Vg#t1-fDrHF8_3OgurD&u<%8lnG2>1Vn$4NA8ze>^3jKm-2RIn zk5-pW6<>JmmAdNM;`w;C-1M!@jm=~G_U*eH9iX2tTD0gN5fx-t?D2bU_f^bTyn4l| zHJ9Swr-j1tK$;wBTmjo!S~TQ9^k_JmI&tmV?D1<>lbD?Y9d^~!k=9v92_gho9VSIx z6iHVUbop&wlNb~-%rQJBtCLNAmZ&6a9Y>vWBDkneghIyH$fz)a4{Gs8VyWoF3+%fX zb(Lk2ayG&YlVD?-CrB`8EMhIOPB@ei!-xRp=qt>gAu=FIEm+fJ$VfQGbyl;UVcRq3 zW!9FEwbx9T0$-Auc1{@%EbW!X3`?^#BQUH6o^(buv!)i9zJbifHEWSZqLwtC6p3Xp zkVXZKdx}`lXPNM+2yELfmjmZ)Crh7H+;*^2J*;6cQNRq#ro)MvjQNof=xJ$$vrbFXOXUVZ&7iCRt{f5Mubd3M`Zipz@M_-n>S&Jjf8ySTKn z{7ZA@%)QaB+8n(-eObH1*@Q;e+oQvM8^a^xhX&f)dyA*fD>&!8GjEgx5)yPn8A_|PNTRaag~DrQP+Yl}o= z!?bDB1riSK?d=ti5H@f_){`8aIP06Ye&up+`Se9g zSD$Lrl0xc*Cm&5TH0&S6OVt}sYduI}_2v7DCIneXJKgq_p3=REtWkpz_BoeddC{DO z^XBK{_)~g%x`mDJY-A-9o&Dn&;X}d!qv~2#U}P+HWs)e3S0S>{33i8%r38~P?G51vwEB8k&xz*KgfC7>>l6di#5uwrtoC zJ!##k^OI=$t zwPaZd)iCK^;~Ddm7qc@4o$dJT?fcm(3RE&#r}@jtgP$a7{+gZcAj zh)WmDg{HPn*uJk5ItC+mE%21*I$C_nj)B5R|^#tWl&g{$L8eb`opm4&5f|> zt?kg&-3lvK&4EwfbS<2A%383?RyGaJ_hUnY(2PN|?w&!KJ&{b!hk~Mf@OpiWpnT-v zhm8$yzHQ8&H6K3psq5vF*R4tQ47MM9>-D!Eo?1Hn(Ut2~PKs3iBiaPM8}I&(J$v>P z;5_}2y!^n80e`_9Djyse=+j?*;Z>+VQZJ@(-M~4g<%Csh9;=--?NMB}>jgoW9MkRn z{nd@TcVAX$bFE!?!tpgYftBF5*!5Uk?C)$xa$%T~F`<8CSSRAIr+*mwN5jxL9EMmV z&gM*(Zm*f5mXg)tl8Hh}R=AVMS*r{~oYclQtl+50KNz}Y@0igc081M|vQispKBo0b zA)F=$KwA>gMaViiR}R^RZ6E>P(#a@qS|IjONyOOMrv3ZZmdib z?G`!~B5cSCk_9tkf{s~2TS8OKu^>)T3f6x9nY+Vanjx%Bg07n>(SpRp&SW8*)}meA zU5#U-k&T&@_PXEgZZ5AYaN>&vHk&Kqa47A#iP8V9{?P}PfXLyFJMQp0-2Nq^A*|cH zVZ$u9-Qhapj5G62KmUxv#^zSx_!Ccr;l3Ve-ASkF|MiWp+8=xHVS8gsi+I)b*TKDi zxLZPJ0;Az&f-evRKRzhpN6|hOUBa6r8co7jB!;Y1R%Ce$jA$@ejg(oNYW4SpcQH?J ztXP?u0YMy;X6ike(PP7E<%!lkdrmT$L6+W&(ISr%JyMJ+IJFqGHts`0B#Vv?M!Wj@ zK?;6DNk@TN6G8d!bWWqqy9z4ravUaIgZ8UL9VULiCNkBYZ_`lDiPP=c3U zdVm0&j59zwnNIeNhsGQGhWd}}*}W%(k;S?%{>SG|M1!e(WN5(n z?hk)%3}95MAg_pOoT*zc%Vdb*+~IV1!0Yg0q=F()Zm{8)F(b+*>uU-D0b@(EdWsP> zxtet%8Yt?>SW*!fS;5lOSuLNnP#NZ2%zC}a@}$$w1oc?Pgu-e=8CNfxWk<)xP${)b zH^@n3>C8xEP=;?7a+(of)&0@h$rdleY$HXg%qF8OqtlgSM$XY`Uoa!5g4zDoOfxgU zYFW)l9<;HKNxw4>gmy`@Hj@+SVc8JGU&uOJ%t!!*KzhHOT4tn*Nl>s|6wIle$OUp} z=xMV%g2KAjuxL9vv1W_95OntTLKjA$#?Ww}^XvDhuykfM2N`qyqf(mym9|Lw2tE4uE63s)qw$y&Q?mqy1%^xeDmZd<%~(GS1!m9M|ontuH8$3|a&{q@F*@~Zk1)~-!2STMh! zy0WrNu?Z!qlu?*Y$Gp^OikuhGlIcyQGGMnUhR5T|#A6AqyQ@p<>+QkFT%Qm}B6nzKNjZ+U2IzEqI^5*4(&FR50W*4Y`I2m0si=2>y6vOL)q(er6L>h?& z{N4o4k*p6pE$o!Jg3qUMAE?rYa>`Pfw3%|5$(l~TDKg6LMM<_XLn{|BFz+3SIke0n zru&10c-a`!t-4g1m5zvJQXKn?BAO8)B0;PY6PE2I%7#^YYoTxC>azWUmhiJeygv zM8+fWiw>sS04zP0<{>JHVfC~rbDp8=lwr{?;TX|F!4ypsO=sDxX*DBFv}OtzNOY0l zR%#4pR_DX2`BRL+&K~U(pZ$z6G&Uq0w_>GC^|#ktbG701dL@ct7Zw&~-hA`T{v$_@ zJoo8OfBJXj<>d$e(N4~L1+6cum*&^S(!p~ceDtXg)l^l@Sh;+Sj1Ft~Nd1wagL~@- z^9wxv7}g$l+ig+VZcny%_B*b=_7l^uy8ObWcttC>?bs|o`tXa!n;Uir!|@Dwi%P+R z0SkvcfJCYjquhC{1j>QyiN{4Pw;hZGqOnZ(rbCs@j2%4RHqv6z&1j@9Yv7cTKvMzg zoAw~lEEyu+dkyss*^a)Rl*{iL8ygKZzx>S8?W$c7H*MLtU}oL4B^~XZv41n<}RRaLdq0tLQ6Dis&{`v);t(P8v-ci^HM z7w~=%Dl03Eg8ZVmVQ3?V4;~u-$&Y`Q*}P*XES@)Cx%>C`1Q#q`oQIJ|$0t7ZpYXdo ze-1@8^WoP2`YFssQlPcHjWR57ejJ48|6}hh0OL55ZQ<%3W|BtCWJzXbb`r-)9CDbM znUf7|n3=(0a1tlR#LUc=NftAWq+x3KtG*sN-hF%DyRaL$`+vKNBug6T{-Elds&l-4 z*3t+(0+;F-`Er4lwVvU%L4bBtSst7)X$-7dyBfl{u2-+#2+uw963%-Q;F)XtrcXN|4d-a8&F--4O*VBz;25}W zG_rO>%q*fxEh6rD;UGK9S?U=<5oy_mt@8Xu%Q-90N#~t5NzO`ht9I_#DdD`} zyL*GY-mFulBwOThgN8vtW(MEVg&=x!C$t6dtkXI{GPv+OmceZ`L2*F=vsz6`w&M48 zOdfSmg^F|y#61~{S*nJcK(X1F5)l0{m#%LMTLn zkZ9K>fS);K5VFNuXGKMOfaxm4nVu07$gkL zv>ViY5K7-`Q{r;q6B<>J@c2xOzXM9-KCLbSE=1N+icm9$Q7i@`WOeQ7=xXTc^)4`3 ztcz`0$3bU?p^d%+29#b51iSqa+}x?y7!#(1tqcuOjw>bR^55u=03?vgU;jl? zM70wC%qb<}632x01SP@4B&x4J-fGZ6p4$jUnJOQ4LuVig-ChJB+PYcL>na})oqRge zhcFn4jkQA-26)lBz4CYS-xkU3ZtiDa2G5vtHa)!)KjJ&LZr@Vh*f?*=&r246_~D23 z@EeT2pq4iAvckgh!Q&>}kd>Zt0WS*HDAMg>#3$R69G)&eR1}uZ>NfF~ynUO$EDQ@C z2Fw4qzJFF-ZCneXX0?mgk=dDZIE=(u-<-E zqRdVK85GkPj@dua>j}w9zaoW0EJ+pQV~LxV`UT^Wg!LP3%*3q682~}Ag%U@BX+Mx! zbE5w-Ap)pLs4MqX9VyA3MzN)02Z&Z-%USD&@}3z`n5gvY)0N;8E*QM5SVd9~qteQu zAl59*X7-P9JXEMhki*v%#lt0pAf+FI2Mi3LNRp}u!)dfzA1mg$h!n032 z%MBhnihJ(mXJZQ%{#-Nn{dadPSomYz>@zM?zw^#3BU9bBQMf)#v8afF`;oT##_F#} zjT-e@YHDgN{6^#5ci+{_m~rwDqt$w$&)ah*9_pFtsTcsMx!$^}`prw1{Jiu)<)OWx za3XZIMBz861j62O)r;)za3XwcCx6{{8d2+FL!XtCnw+mM&djKU#Uj zh-`+gUrDjS>2w&gGSc-{vq_bfmI}^PH?d$mOG@#^VbH^Q0=Um&5FH4TGX@FX;XaJ} z9+iprgsF#91wt6_3+XP9x)%vls+ICiN|tC|lBzVrns}KV=xn~I8T`xUjRAj0r%@+@ zIxS<>m4jYs6;UxzSz?tw6s##?T1qPtpQvh5xEzrMEs9zRuo5FRpAhXLVJ(`=R9O*? z9Xo|tphRpl&T-P;pvnd6wfB+eCn?>N$Q*vA1W>~D7mZ0UFee#G)9kQm#|})KFM@tq z>58zXN}0d(Sk&TBgAC^SO`G|QoE&Mys9`_E{l5DVT>URI`F{sO*jifJZq3NbzO%5P z$V$~pyezPmmn2tmbpO6Xt&>igE3GprJ>6H^Avb2;5 z;L+-Bpv6G2AW4QZP8 z|Kf`;wyjvP;{Ck5ycNH7+r@uo$hEcl)fH>UJ^1kBb9hzK_&lzz~hqJ>MP{DMO6_B(DeM!v?!#9iWq-lyt!i*=nRZa01cunCxC^7ycNsb zxnJ2M=wsf2=@Ti{r%LkQR|&y%2IS*h$CTiRJ+>I5iV9Vty;n@=Z~@aXqWPuhd4+yW z%9#kd(k{ycX^;iva0_chfsM@{8235r#?(yGshJ4F*JjxihoDJ&kRs!ElXJtkQf>oBKeBnxX_ zBO7BYxDb4>+KdRs*}!BVAz+o_#+CqS7(q=0o>rBJNtL!3W-qQ(Q_6j}?f6^B{Vhl! zE1;9i+PUZuRemVZ4e&)G(bhm9ykD=^EYxVs>yZ=aX>A2aOM@uKapK=S^nVMKSHr)- zm^^uM?B2iprN(5jWcK=lhCLMroGoo_@;&$7rJZxeZ0XQn9uGY7;C;d`D_8MDh7Xsn zyWwU{V{Nsjy6Ol^GC~-@C0VTC@9}{i(P<4Lc^Dpogux<(m<}T{TQ~`NFgT7duz(rg zCxY5OuX5#5_o>N>iLxM0>eGHurWTiAlNIVsWFln@$)X?&4>KM9f*mJow%Y+_J`Pn! zs&O%@pw|sU2{VaCe;3Y`b#9-h0FJbfM<>G+CqR3B+76^GI z{m3lV*woh2_TzUyF2Du-6*5md5PbNjt9t)Oqi+KzF*IBZ6<^JpKd&n%HFwCsfhQPE zI#ryvh`!DqXHs%%!RntriH{gFyY4(Z_?Le7*M9Fle23xEd!Li$F5AK1d*(Z@4L8uJDq5)1$r56j_jm~&;Mab|-N^TKGe%bn4Xk(SO=f0o2#3Y2S4J>#Sxi{D9j z@Oe2o?uUQUNVBAoaMFSgfBJBBwlTZB-vH~V+Panr29th7vdfXK)2r1?fKE~Dl`w$j zd4b3HPirbGE|q<084wACM3c!Xr6nb)ya6v{=VZaI-G{)_iyNE`o3kn*)gK zQh@Gfn=r>|Sp@(k?^J!jY?>6zgOxJ$;a2@Cjr(IJ7+2^lHe?P6Hk85B)OTuxZ5#SiNqY{LtgiMY=iz)!Wu@-Lh=y zs;2zxve>P+U0L8xwie@`o9J9@>u8rE(P)*;W?T2uPd_!mZ!r3Tt3(CVHbl9r;*t0^ zAtqf?T~$A^ATQgVTbMK9x*M-DR97E$uHLX|e9rxq7y=iEEAU^undF25GKtsv%M3Q zKnvGVoB>%3y<;Go(h8zjo6#Iq4DguUmrRQDi2HI%b%WA$8eyW8io*s4RitqdMuvpV zskowxKq~TbLYSCzG-SOAqGA}_E(4V1x?l^=rTs_h*_?&Gg40CdC9epr(Cd}R8UuqJ zIn$jS{bcc?=znKkaDBdO_^88&5BIL#z5BIrG!(CIsJ|g6J1bM8)8ky%6ZobZIH)Z9 zyU#uiAlT3SvZB9m(d=pJh0AvGXP$Q<0x)4{Xy``ZD2_>M;FxZMqK_oAy_n}>EZ}2u z4xqS|aEbN{N$RWkR9dW}noX{GbVAi!mzTFIy22O@fOmyZi zSq$6u?gyQbF|l&L;u7i1GtQuZma594_uX?>*WyKsD^{;wGv8=5Zu}#H4>Y*EJnF$+ zId|?`y$j|o5TAJb&Xb;b`g!Zrlcwt(NxUtL(XZWXjoFCnzTD+{$ABrsQ?rz@ypLyQ|IBeXw(S(UwO6C5B{DnWRElEzXmLcd=mXn!U zf?#u!*=kf#aVO~_?cH+->Kp2$?w&SkgWz1rX09l|81izNGrVkjXV#nSgN*USo&D zA`^Ij>unEVI`*ulusBN-7rD+2tJkkJneopnK7qKvnxsLFE3v{RCSQsx^oX#dE}^S`naKdQGOgx?avd4vIUN$CCc1THez_}Nm4nv0zfn|%$`T{2H3T8H{=%-u(gb_AY6aVH85=O2>!#5K7~7Oy-&LN z`s>W+&Y3+@h(*$>YZ{BTZQAt3h-h>hH)YB{%?|hfHjV=y*nT*6>`C4D`P~N|cwkGv zenl6rU9;xA(PPG>Q)?13-!g8GTuxTDVZeaFBRe}gNA&i3#WQAKEbQC2SKhjNw`g}- z`27`oRQoG-Ls4M~6c+b`#+q(u_u)B!V7eaT7I%`90kq`dsnaC1<&b=eQK>$?5CX;% zt=&{X*T;VMS!v3NOoRO{Rf;hYOQi*u2c-glDn1$8pJ-!a)d&eUFfD-vI{uh7HGeE6 zPE^6ltV9%IyucoZ2F4Be_-fXA#PT7LL?uHEK*j(H&)87HjvF6?0xW7sX9x-rMbt!~ z1%dW_0;fSK5s>g~Wn>x?wdUCCk29?$B)5>1z>}2$46K@iRS~Kf_$SGsgdUWbc#fFj zu)!(Sas=eC1CED;>l-L;5}b1ONC{h0Na#9A34lYnvXWAT0-!jku?k@#IgDU+>6sCM z5n~mz#ll)wD1ISZUFO=Q;g415lCD#PfDm7RwTJ5QdK*m!%;k)XAnZH)2xuV^g|!4H zDmdt|tjF4wf1R9(&oUfWi>y?AaHQ$Y;h8Jo`|H8`-6qLgT{s-w8;OJ~aFeKuM!7l+ zrASCsMDRgr;87eY<-{8JZ&w8VTL2IgI9zkhHEof&`oJYuUY)*s+xDTMt{&I^Jr%0S zQzvjY-*AnzeD&(!hUH6Qo3`yTopo-Pb=ZiZ{IG4q#9cdgAi^EwQc~5-M3c&8OlC9l zTBSgd5>^%BMkZtpSmi@7!Yq~qQK3{hl>$RD>%xRDY@^N~9F!lte;` ze~XEX9ybItZba!03)t|^bu~BRUO=t~e(34&(CdWp(`P|#PYfAm4P+%Hu|;fAOX8aO z@5}WKN2IIIJva8^`yYsixS287*Y2y>kK@os!Jztwd+)uW@%Jn*rRT=6WzYWopVu@r z*mHBTN9uJ5LLg-5@9an$GCWIu=c8xVUwzq4E5G~U#OQhFOdNIh_2;SQF4`n6-QUSg zD$IrC?p~;^J_s2ZMPR{&+)z`=QGb)!VwF^CE@;wfI+11Y8V&jgGW;Ty#4~wBgaI+h zBa164ixylid(V$QeE(1Pp1yMSl>};R`S#myx4SYkzjH&ld}>#q1AfEiupX;%Wu3RPM#ieBcZK_42JA2USr@E%im^yXi(W6IyZf(}oO zhXXTb%s4UiyZQ5)F23-J&Ov2m*0)}MuHck2&MsDK^;y0iulc~<1DdL;X8vehBj3`| z1Kq(0?~O(95awlGZAKu;fC0LJGaDUXbsLZwFo6w0SZawgTg_m_4?D306r5nN;=?)R z!#$B~D1E*T2nYSh+F;UziF^nXDB7o0m=ut)x`q;UYC94Wt{J2zjlah{h!UV1twl;| z6$@H5yZ-{KSd-XXC+JF`s83QVbZCUhT_YwcT0cB5lZFUm;;AH-d2K5?C2}IblM`jH z0u@D4RSsiaRBZoOCDto*;K1h(gnGeg(!eUBc4@mxSO;z39xq0d7qz#}l zAk-3$@YAMEH(!0_9ntgWoGmS0yh?0JGxJa0a3Oc?EtknDwp3WYYafJg9a|7U)yq-k zddq62b-~FB;K(Ixag~UVX?^gDz6gU?7%-_=xQa~og9Wy{q6#A-+osmx{4fTgAln2d zPZ$K1NA^KmL#woE?Kb(x#Xm4lpX|&W@v~1qmr~N)d|g#t&m|XM+_QbxuKJ%BFJ73G zlCs$8bpDUjzJH4`V#K8V@jztR@@1>@7tf!co|~Jc&hJ;k_4f8^YpbjD(@vT!1>#}h ztg~iwAHVmdxN_Ml)$5=91pcrGbQTVJLyBc?q^Ab~;y9bXBxvmqP>X2@$O}Fa4MLDU zha*V9Afp$FAXpX#K2Zfku1mBxOK}ZM7?umCP8tk5)~ymbok?1@c$w7ETrJ;o$341d zUwqd1<-8xX`}XWHJMDJU(c0SZ!2>&2=cQ*Zz542_egDyW+_Y(vVa%8@X*CVaWt%te zDfIi}g%*pcc--XSIe2EL8w@IAT#WD*Oz5?G16hm8+qWN*o0^(%A0Q_qna|G1g3^L~ zNX^L-Rcb>=XKQoo(@#C?fBE%SMKx#CUw`Etwg(@*-RX2WsV!Ci%cAAnb+m)vv-K28jluUrl-__&>29wezE>}(Zr&*A&}%PC1@NeHm2EDo;?S#J&~DPqiX zRaGsVeTEAULM`<4V$$yQgUx7UV4U9<;7&PZw(jIv(>Y1-I*%MYV%xZWZ|9}=UU?MY z!eiGXu}>2K)kq&0S8T4WZ8G8tNj-G%P`}*#!r^9zb3~Rqr2yGhH!`CL*a+%`-J+<~ zYvs5gbD|X0o^!?o{`6TB;p=afM_zsX-SDziTXd5rj56kBrtniIySUVp41W2tRnm7$ zH>jtM8>K49NRstxp6j2T%eCX4yRRAdT;9Z5Jq{l^3az#d==AhLMtVAeJn5J~=^ziU z`;E63!AYk~gyl-}y$6|=2F_)O zg~Kpx=pZO8cmh8A^jq$YH{O*F95@Jf+ zkAMj^)xxsnE2M^oMmZ1&^4r(1hUVHMFlF)#sBSt0hugZK|KK5Tq^A{$j~Hl}Hcp=n zxx&mVM5RjED=7})`UC|PV4X{}-}Z4y^gX>6t`A7y6kByxF-iiH6kjFICq7XDpi(JS zu=F$fJJue@DRZ3$#cHHkL@HWD+)`^L0hh?6W1bR+kUz{~#bjy)EGhtp(}lljVnw%% z)JgywEXkJo1fUgX3u^y}3*loecT{;wl?#0$)O2nW03q>W!U9xg=P{iRthZfFEt*Qa z&su|3N|gt*^rY%8JfJBaU>av?NL1D-?a8_XQ3VO{V_r^xIUJwhz~qF8r-2bz8YbAO zR;q4EgH5NjCzH;T0yHRk7mSok=&&)7PXZ5EHHgSsfQcv+sg)Uz^+-rym9&BoVOF|| z-MC_}%33b)wHe9rf>VS>nSRHwAjGc{7qXnFTxPD`e!PyYp>X7o5R2^=Bxye{AOIms zEro@t)B<INtlf3Q7ZcYpD1Hbuxzx3NM)Vo2r)Q{BrPPS3D;L=$`DFA!HhUL z?PeV$JItWNMb_Qf!W7p1xEUQjQpapUZ@lSN5VcNN^yQCGG;};EqsoSuUt0RjhY=~* z75n)0*QLvDypc{sb9+apX4AU$+we(WKIx?CKVgCqzVE(k;rAXm!9#fMf8Jkt(9zu5 z;wZ?>E5Soj^2I_bL(r3X`32Lmzg~FCx|hFR(mnH}k?u2RjxP#=iQ9d&9V(HHPR}iY zm{bjPF_m6wWJ(b%ve+C5R?2?dq&mZaP}pcRXz{WdFxcm5)0XfMj)X#-22lzmV&fVW z-;F4B82&{g;b%<(O3k&kwHrdA&<`z*jbjZ)!&D~*47iav2%01|mNo#Ye_>h(VvNto zPKP`1xf?!y|0C(em!6d_zWgHIVC3PwkG_UOd-uVhkz>J;oQ!N#1U#O01TJXfCB`?( zs%U8U)M^r&eFBK!pzp9!&QVUOHA{#C!Lj+sRQd@UQ>)n@@QI26+AL+#KTyyKO8Oy+ zn`aPz-!CYo(^{Sc8x?PPLdQeBozT;E1VSB^pwdLa>kmi)WJ5FU7OpTOol8kikqvsY ztkG#1a;KCNsvuD~qaYp+2 z7o0P|Y_^h%4|y?jF%0zETiOp8jHYGRU3Xmz0ubh5v9eTZSg45+1nwFY2I2?(6J$CHmtt(e9%RlFw)065N8yw$$vt;=B=U>$Su3K(O z88mcgKE6HIf}hs#i+@>-_auVxfu4?3;Poyrrkf$zW+oi~j%*sKPt$5JF#l_zXHwYmwvtP7Yovi)Bu2J+wxF|j+sY!=> z!Qa!xl-TL9qA{g}lt3uv1jF1{loEduj_fNdC7~x0Xr;b)5=h0#gm)Rk7nO#%4{)k@ zIi|rqB^HyT$Rc}!KIp;cl&Q{(BT-KLTU>+*9f@OWi6`}GC1c?z*kEO$)b5jj6XIeJ zgElh^E=mKl9ENX~E|zQSYvs~mqoi;MnR|zplN4kO`nvvT-Na)8N5Q30rw`%M>oS>4 z|D(Mv0TwGD#FX;D&p&Val#@fYrl!W*GR=BWp-=Po+)RCz+lAlO#h1I^vEH;|4?Sm|{I#Vv={4h3T@&`U_H9V>+ZCBT#u zrNiVjK$RaVNpUa{XrmwzvSi;1K{^J#@$zN3W+RrM0zn zNM?Q>Z?Rh9X&I?fIOyZgJo7aE;w!F@uD|sfSif$UG<(tj{?-TXg2}T^2CK;iwH>`U zFU`0%Ld^XJgHi;t$!0E`P?lG$bfuUA3K0gRlEq_5Hak-AKS@pt%$Ph@{>xt;fbHA& za8Ew|4D8vt9d5YwFWP5cd|ZtIvih7ePS;;B=S&1PqN+73SMIi&4ZozMNLBK2{i??W zzZ#FnGxX@u>Y3ZNY#(JX==e2?mzk@{?pgqgwp&%I9yW;`>IImp#=g+NJVjvCP12$WmKG^M$adb zKAe;ZXB%MPW)f0~X~}NrZ0~@c?rzW|8;9NT%wEkYCW7?AG-3 zjGO`2+%PFOB`J8{{ zi$Aa2q?t5kG*_JI=6bhp<#S4k_^~Agu&t^a+Heo%NgOlK13lGVX!W!~C!Y0rxp|P3 zWCzNZ(sMH5sv9nY6K76_Z@*j!&p-1TEd6;gJp0r`aKhA)%r$K4T3D1M;(6q@?7x z!*4hKrW%6cSp2;3qp!Z&6N^Sq%ScbT2;=eL2+paQ-WCp}-IKMFaYY4%d|`e*eDn3U z^8WpM<;yO)ko)HAZ{Vfp{|XmfdJ*OpX4t**dyq*-!GZv<3b8R+r-+KPHg5&^W=Soz zvQkAVgC>-CNTXIX6;uRy6JyRRWv$G$f>UbuIAzymf6r@_B(-nHO+<%@oG3Dl6xL!wV$7sn zM($K3tf}Uh^AkNOa!Jfl3j{S7^gOesRB04(a3X>wNzta@C58B;K=2Ci!z;wJ;xMA5jgY2_xeQD(8SEGMc4@%1p2xK9f=6pXebQLRY^%|zZb(Z?3H~FH z;a?^2`fme3&=}D_zZXFSU%1C3A&4NfwYSAPx>}+RyS+O%BeT<;k4!7t%=z5}eE!iO-{i0?FWSFnXHJWuJvk#e$%#p8ysI~)Nlmquzwz|# z`NPk-b^SL>Hm$$r{ORsd#Tm9Pyxd6q%P~m@g(Vhf>*$0af<+pwS~ltRvH>@}P&DQX z1^pekctzaYZ1_}G_JlH~<I*Cj76uNOb9N92C1-fa24p`l@% z$!a>uZm~@=>J4dl$S^QgO#!{6=oP4RJa@$nSHOZ*3#GHpIvbvQ@@cs5rfd1z|^u57?7XMjK z*e^;z;;hwBqaepryo%M|&qkwFb^=vdG1mnJhVN>Ffr-$^hZ0zy%{A znGPRz_R@1AvVjMh9Fc`zRn76B)0=Iw#bDwyvr@UNOcyxtaMY?*a(723*V57gCbJ&O zOG{zJ()rTLja!9@lV-%HOrP%EwR_i|Z@&BVsKH=1U4PS6MH$&?gYhAa7?enUzn=?7 z!W|~FX#ws*n@5ix{<~Et9Y4%wa~mE=AN2He>{+yA;RS(^|D*|{C*L?%{)EJLa7PBb7OqQ^NyOpyU3{2PHoMiG~e*-GO*3%lb3l9Nsv+uhmF z`TmAIn_ZDu{OW<_<$5hirt@mS=WiC=E_>w2;X~2qUw90@`~C~(wO8p4E9ZX$7g!B& z@#IPH*`n=G)#e2!f-5|shGdfpYQ{v-Cd5lBmh2`va1-L7q&cCqM$rK)a007Gm<*n5u^0<6ZoTibVWgvai@9ej-~u%)V3QCP>sh8Nj9WpkYVnA(@ra$@gI zq&ScOttsiDwgb|o99mu=Z`!aCKKS@c?v|UblY0=*aORZrcRz3sQG6N_Wy5`16N?J` zq5TIsmo8eqFAxeG)SY@0-B4BH?Hr?I*00=Jsx_)^$j-``<8W9EWOK{}TB8DE8!3nn z1|k@!_d=Ja1Bs#lM|Qyl`2~>TP6D&d9K~hW5a{XMzi-?2ojdkc9C_vSk3xqJAIv@P zl8Yxk`{YAqX(`DW_*Cj0+qc65e|bh;zWf)-nmP*ZeR(c_^2~9#emi09j;0l{bS(0a+NqqacyzhEDT7kuRg@sVbVX!%*=qS^mNvXZZfJ^ z+ZXls6PVN7+ALRZ+N()P&dN#2EJ$6kqK9>f{b%MvVtrC20F_c7*tv73p}o63yRxcs zKyp&@==_4BF*=>GNCur#h)cX$QVTIT#)Y~)DyPk?d+o{Fbf=v#A$Zf>4+nqzeu-+& z?tT1~7o5f4b=z&Kk3Ro``(^!FwSUBLdGvr{II!;^XLmMo6Gu*iz3oxhai~@)Gp7Wp z%Zq2JmsLaL=M+FjrWF4`QAJ4=B7;@-%s77 zzrC%cEoaZ3?H>#pG=wxn__%hc=P$lXDbrFf-DPPk;iffwRxbor!iT`fE(iK~%&b`>GP9zO&}o@Ba!- z@W)+8cHJ*{bk6;HYwr5elB*(+y zSW`F@St*F}LP-v9&&kR3{2vqf9{|Ds?D1~}K%}Lm5$kg(B{luX>NOjzS=kw}t=l(y zHm~2_GH$~7nxTXGsdKUlIkR3T#F0Iwa+v&rLK#^!*=@6M5;D1j-u3#t;6+w|1aedo z2BoZTh{|xOHdiL0N}i#ITol5*z^u|JIU_4OT4^HPSE`IiENC^^Yf6e{gkmqoF;q{F ziI1MxJBtn+2zY4md3q3hpg=5)0eC}m7wkV$3!{gQfXAMBlza2j1wsqP(ql#p3@ve_LZuPQ@fHuB)o9Ro7Kl?;SOA^hZ@SRSO<{^wGW~yRU@o4>1t$ ztamz-f2?Y*FSVPECmMA+O&}DK!rm@dzk;lZ=bwCH)d%0MSU+p@fP&l<`?x}Px2m=a z6Hqx7k0v!3?YJ3|fT2poVSXtav}$tL>y-te*P!7go5^IrORc5yYTVx#lZK=;FEdd> zJ`|0|y=7%(|EYuipEVLn&Lj+1b-1Bnd3$T;1e{2xnJvaiI=v>{pwsbYqm>RA89@rU zxw%!o=E`g1yLaq@8*aIoyZqAg;PvO9R4-Y)Ow;S{kSn(?mxm1K&)qaMrdehfS!&f&SKPx!zN9ava|EV=7W1> zx67uIc|A0D1;vV^^{NhJuX_AZ5)_hnm7>-`uM-h*j*?0e!89<-CZ!aR*&34AAZCkd zZI1$^kZXZT%k&>;!`B)#V8k!d@n8xfzz`N?@I?Z0$lC)kLjY#Zm;mmiGN?FE!_~Eg zps`oQwfVx#?S?IGrZbCBL>#XN9vFH`obm8WMW#M8$pJZOE->K%(bd}q9SDFS6Ch)P z$c-6PChgz0US70#F}{AiaN#9a#cXzK^JkxZv9qbEsr|+qZyYvl#+0G>l2h?RqPd7` zJkr$Av@#86 zxTLsj{P=Njq`Hb9J9ccmp}ARIQ+ZTvM3}v;#Up+B;}$3xKMB(E^FW2@Jt?9)oCah- zG^~15#zQq0ib8KR$l4$L)Y3qrjNKjtV0*!bfoNxY3q<@qc$kMkEku~ba8Y&^20TN+ zj@(`V_vu<>S&kk&2u)RW&=c?g6=7p84h94Z$e&xIHG<3S0IS0aPKOHvA~SQ=pcV}V z3V;H3i3wlvFapqV62OWgm=N&EaZLP4sE1UljV2SPNtAN>FyNMvjnSK}pfl-Zm)Q<( zs|&b{6bR$K)X~|CKtvC8_yP!;D&_0M<%`UwR_UxI$Vs+?m5V@c zhabq6t;-YOW}PsTn=tLf_%FY#6i1d{MuDeR37&ZNxFTyQigT1i8V$x^OcXnY4IB2) z?2~45A~`#`W&QfCAI4+Rl!_yV&M3;y)zluTitXLGEtr>)(TjUl_g7zk8NcBCi=ywo z|9;jD5~ulQ<#ODstuT9hK770C0C=gj3HK)rC2{20o~Y7N%Ctm9(Jpl;oVLH)svLJw9XuduVOQ zOvn`h;a;zgi-x1JTCL?wCJSWc=ERUS_6f0QkEgS>cJH31owW^3dpkU#1NHTurf=8I z7yG#j23E}fWJqyA!E}6`NqBwWp8KAb7cBZ((pj?Qt8RUQKkJ-xIW_J{dn)!pBN9EH zjxOl#?Z$l|NN5buYGuLxaplgCQ19;}n%F;KJV`d_Nl7*)M%UC(hx0Se?9?q*6Wnyg zm5}UmF{W5e8bV6>4;PAr#j46Wbu6wnCN*|zhYf@O)<`4}#L6GWn6DCJn>F5W5P5;z zNeB{8z~?HG`M8tQsSR2Y8MH`TjAOhwebP|Pss&#J?tJL=;MX6%ta{>^*Y#Ijd70|Y zJ8l=}e*C_)V)ZIjFLL=aNB5U|>-NiQ7k$D{m~}qnSj%DInr#?x$+)i@Aw(6Cp?V1V z0?^Z)30axx;C9osAg7=hlnpH99)0G1ShI2ieDV2@uyVo$@bLXN$QN8Zi~8ibVZ(<) zD&j-?aW8LaBeBnJJP#x&Eh&a)o_+$}diQoZTu4NaGE=bU|Zs?F-Sq@kfY{g=gy zKR9~y=n4ep{wFPG|D%x*?AyC~_39%B;34= zbYX3?lH$xj8Lr4HmB91@c;&@NthzOkPsT}1@mPx+DBuPCFUBWcI~@CbEMJ!x_@EG_ z^k7s0mC(uJk$Ay-34@CJLwWyFa3a9mfcI?Mx+P>e3VROhfx61wFuW`mrcauPXPbdp zk#SN)iPtcmBP|E2riwFtCZ1I~N=UXw#Rz`LjPND5EAn>s4l9MI#zaw31kn8`J7snsIpbGD5QigpVViI zB_D@JB>q8?l~!H4_SB-qT6bx$WZDr*dmtpjSV80zaDhZ>l>!v@8mL;?LhaT{xh<>A zCE`%c3LsSoQid=VRWnkJS~LYDxTzvCCnF02!2nb?w;;fmE?cZ-(z0T2Nkye0ianzg zk4b@mKhPW&gv~mYYAF)gdyB0D>;$+O=zsj2JcUh*obJ z6pqDYtIZLpsc&la1(#Lk=Vcm83-dEoq!`BLiNqx(yW~(tIwU#l&>f-jIz|1Tl3P;a zr%9kC?`wugQ4cZo3oF7X2A!6%v0+>>v~ZYN8=WxJmkxc4TY((eroj?F3DG?2QP+V1 z(JWFxH5xQbEQM62>3|Oe5di7-!oG?c*8lU|%TL3>+RpelE4Bs(4IeDj>{$sldsjyv zxcxrH`P%I_&mE zEo~k7`B^!ohyA)HzLQ1 z8xkfGX39hWi49H@3r9i$Uo^yHf?+UO%zD;lEVGtrj)E9uO_*}NR;vnz!r{=IIdgtD z(XoEn*`(J&dwcy;)IRx4i^V)yjK>{q?d@>%Xsz_YN1sSp+1bK%H{2Y1?Twc; zAI<%muditluD#-B!Duq8{oyXvhwr=q9e7vQ)~F>ZCh!^!0J}g$zY|E}Gzcmnh=Ttl@KIUj5qMy)3ef@3#A0mCsFC#3 zlTtA0VUXA#9du%ZOE&6wtHmrEb!rLYY|xvW&=VEm#rM8|6Hge=%^W)nk`5jPm(~w* zVHz}hV$jgq&4pzZQGLj$Bbtq%hZ&P6lgY>>n+=@Bq~b!}E@<_1LNFc%tLFF)x_PtP{qamO8{27`fwCwXM$9BA!mzi$1;O|y#&i!x3=>12w{k?pg^ z7cE)}AAc}c{pLF_v#4g#@~vP@&4!%fd_4OFaM{e@N^!8>#$F8Wd%Y2yM-nn7K8WIc z3Xz)#vW>mno#5;30AFVZQ7y<4AY*7yL194_l=jO-@W%~W3}y}=-Ulm|Y=8p?k3eTf zfH8?W3{12}7wF6m1aqC>v{=Drw?TGpmh5yon9sQeL4-IS>{Rft*Xk2?uE45mnD`@w zij?wDk{gQP93VTb5O{q(_}bc_JLm^KC?F7q*^CS41%B?v*BopSIK5g6Hm6;7;B#3K z%r4GOf-JWcy8J=#hk~*{fR9CL*_`6_txl+Nt5q7#fB}fvh(I(xpq2|GP)b5~oa{jW z!{h1YX3suLFxrgqw?245xa)@V5TvBOTp=NbsR&$BAY~vcFO@0~>hX8#k{$od{r=cv zjUPX9AMT@Xzwz#yhP|m76T`heJkE^VoO8|&9ynal-PPmg9=P}J-pP|Ex}JXVIm^J| zL!|jXE#puL0!Gg}yPCG4x3D4KjSWv^s@jFx+Q$L)!eC?5=LPvX>6bZ*LB-Qfo zy!9@3(YbTvt8cwSy7Age_%S1f!ueAt!rbK>p*lrClIq2<_8yf2f@DlqK^%K^7_Pt~>nq;$1#j3&u2^n|%lB&x_jvj8f z>z><7`02%yl z$wjSyvPz>D5q|dS)Y@9D&RB7@sTq21F3J|BXg{%$;< zA`DNrxYCnqQ~CPXGAt-8Are8e+c(G^nu{*;qvLT@Av zG2GYi5;ELhl$BmcJ2IQN52$n|4PRVb{6AT@Y;KU?H-08j>{TCs{P8ZmL%*Vb$-q%r z*|}qJ1r5XBO2fF8sZ@r%UO6?%Y=8Ip2leH71(Bzoc~E-u-IwBxE$7Sk-E^y*mYk;g z?7M|>-0#zzK7BMFt~(@s^}*}>S!bOGSDtkOEMC7C>e`yXU?z907{qW+MT0#M@^|6> znGJ4NGF8+;Oe16B$iqpeodBaJO@#&D&xiZ}@+2%=u$a65u^VC7puv!tn*vE`cG!2w z4EuIgz}BrhSgTc0aWUL^+bx)cWy05Ae=YTN_i&e9e4)9pxNN4?>TtGqwI}c1z5B-@ zLx%Li?>MGRnL<@(t9I<`VlT(+KO+sFRg)ewhIkiVGOW^ve}emE?G+z13l} zKuKl}6c?32W?nWp>;`B&co;tY;S1;?(N|szM=CM)tUd%~*>0FQWfFV*)}}hDOlECf zXlK@l_diMh;C zdluBx?b4~#+YJWI5tGT$1MoK*CdbBaQ91N)1whb9Pfw2^I1n-zjRGz@{i&zU z0wJyneEs>CAw5Q&4yRp*94kpUQ#rn5u~?{vm5<@#&>_M|<=fO|A>zRiCJQC4;xIsc zs%$cH$|R-9s%5_Mxbd=rF$qBn9^&|Su|!(iCqzl5d-S14G*G3M7{Fo11!KZQj*=(p z@rvM~Cn1Cv==MR6$In^}s#@yc{=4qv`VAQ?UvbxypkioyHWzm0UOerr9)HXG3Y3pp6;0lLx^f-Eb zekmUHS^DKV%TAg)(ztm2wk@a37&`1WZj*5+&JXy*q%*EY0qR}I*%&B?AMP!b= zig6QVdX&*vRE6tJMS3XFXcU2q7*GAmq7m^gYO zK2GTNs)T4zvRQfQ%B!w|j_v^5bmu+ty!k7+t1dkgG-jRL)pC>@HgE(K=Vx)1^{spW znKqTn$b_1BW{<>kIx!3ZWafOa5FYw5usbZEGFo9!Spj%z4#|s`eh>aw9QjqfaQ>XL z#pGm5*TWCJSJB+7^PWqI_0? zlbY-Rj4SZEw;?m;2TxZw1i~bG7l%Nv5B%L2`1gA7-&+9JD5*q%-E4%k>@+AT%z=Id z+2FJppt+?67A#x@>o;zIhQ?ObyJ|M;!I6>zMw1l}j5@L(h%O9UQR>ny3xdnK-; zvz0lqV6cKI8tan9eOJwbAtrRHqH}J9fz9c7Wv50N~QypnVBu8xKlX|0@TSkPm&#Gj#+I6DL&;B zfgJ@g0!Q_eT6Tuupgw#I#HhzM;EzC8S1)8{WeZ~`jE^l|@?+%i?)7qOW3#5Dw1~Z% zKK6=AyK!AGHwa0TLRzEN2mhjxm8+$qlYKm5WlYvzoJLEh^eIecW~+Uu{1J@fR- z(*DB-G(Y_Kee{w`E-_qn*_H6>i%-jIw`^Ap8$O&H-9H!hAH{$bnJt#&vbatGArj@j z@N18%3<@zNCjw_qQ7%_2z18Gwlb7R$tPB^t{L=HRXtty@Pxf?&IFmDvE9zIw!ty{Q z%2W68BY%0^($G|=jtbGP(&A$7gAZDM(+aqM(l{1OQo{4$hhHr|bg;T~`jlzO`Ppf< z!v_ybk3RNS*uHC{tWw*gJv(>8inUwev{O$(R@)}MG4~_>?i=rd2_s8j6kXySh|Sq`!CV-X|?xu>vx(bEKrCWKnN4_#5jRcel58e4Uw=v(IYfy%%111)D2K z!fMLPcgo28tM)fF^?T%&TW&yR@Pdln`wg>ZUm`WNH%hZFyPH4#^fMsE?S$r@cG$Xm z7w$W_u5n){ATbp3;`@)Wst^?;EyYCdH>-9BRl=^B5-FCb$jT|nGn+yg_ah^oMOyG8 z$Qr`;0aS$-R63!em_%t*N*pL9rZ-J%1Ohn5*y3SBTTx|YB?tfg@qffKmtTH4YyZSi zv1$8;^Pv!1zk9_U5SLw3zvPbJpMr%z? zrF++|UEd5HI{%f+u&QpfELzh&O@ZwEj)olYGdP)3K{ zCOMMrQfg`jmEpTGGI9#@a&zo>6O-_e)eEAsVD$zQZT)gI7RU9^3htN)B?%spBDBDh zZH@LDyG&&G4I100JoW4FPVEvXfarM+Uu2-{Cb^}J`dM(ey zM(8HVIyLn8eF!{sAws5t%IYJ8e9AZ7dIzGu3!=NWZHsm^)Wss*mA)4rdyqr$!Huie zZ#U?( zXP+=?$gNMlvv}XZy6qSDJ$m_w2pikIfmh@^fC-{TLa4=+x z1E<#;^tO0h)X_#l_bCz#LpT(~A7l!mB=*={t}gh43}P(s;|PgL?OwnB+l9-Qr#}4f z!x`m6MwV~bzGdj@bvw(?J9oD0S~=o>fWP2+C7_D*o67r-rd-y~go?IrPNp8r&S z;-QyijnOXU7Uw{Dc_}2PWO1(KRC&V05vqaxGk7r;;E072Wq}&n)KnwQ{FOyz^N+^_ zJ{pa)*MvbG@Avv~v+a>RVL#^&L@*g`f#%j03~WT!&rcr98JR_#-IOfF!Xdop_e0Me!M`Mk`OqqVj5fIBsPd0t-DZ(_Uo-xXdwpYd=iPVRVejeoYF4dT&6kywrQG}Qy?GRiFJ7~g3xGz>vsm%)v|$h$ zfVTE7MDPQcQ2Q7==neFM&)W@=o?eKD!r&(t3k1p1QAp|-i5-F35n9-{Z z2Bwj~f*+BSRTK`X5;jxxPpq_GvHrB#Y=HX%WFX^~>P}@kC#jBP$Vf_tNDcx}FbJBP zs-ga94Q$_A2LsCUY5-wD;qN0>Co-eLyHd~dr1lQfK${glwMjzIE>CyPUaGAi^}s-;K<%H zHJE95up;AJn3D!g^;NKP?MBA5V!$cKF!Aj_Xds`K?k1L*Lp&P9sL1CR+-wlsEbgy{$ z$YE*c-W@ROgz+$G%tU_q&+}o&fo*)rpdnD&o(i=M$oj|pjKSxac$*TlFw0j8w&_VE z#Ujs$v5Uik(FrEt1`480I?iCyb84-IHyX`IUt;{u$!d!2)y%OOqZ?eGhYugr*zGng zt~x!3yZx|X!&suD%ghubfk1V|fxU}%@7~chpnrKzS^xfNnc3N7AI>|SN!nf{MQ8}k&>(+|y{{8zydVWN|8v z4uKDAmMk&MJnIS}W6%lk=DVNqR-AYK?jEQ(+z#EneoR<=(2L-w-%C{wVU|!3lS%#A ziu#4fD@-uQET=^(Nf+bHW>BS+wJIXbptPJ&TdjcS1&L2e46x8(^4y!aVUm)a(6wYy zIiC>0ReHBcK+b5=iij8eOU8r=6Nm}j`r2!+?b+e7Y#u#k?8#=M`HZ^8#zDx`YqS`D zO7V#H%+sc-&FSw3uDkKB$f|YAL~kT6JazA#g4tw}Uw-F9{__PZOc$Rv1twT6uz22= z^7zhP?$py}K}M<>maRJkL5#;Vm;j3YUOaorStU;UFeZwAu*Vd^VS3Bq_6~45QsBNv z?va$y&4|*%dIeV(gXx?)8ULWX2Z%gYhe4f?f6(R7&m?_OrA0hoGvqb z{O&jK^wTd&x88n}wqJ49Ae}*HYiek8Y}&Nrld)rmABNv)90%Id9N+WHFTWhcIkv&? z^_`~G8z#24bmU_4YfQ^XQD1ZYjnYex-zF?v{4;mv=_kWmufHU=b~o`?U4Ij7T(_1# z!S7o7H(m@*A1(Rv|E$F&YA zKR5E(sQ4TY~eLCxiOhTYuCSr$y z%A~DApa<^v!AJyx7!w5Xd4j$;bozRbKyGL2jI5%ANRlnQpO;!wd<`B?j&sI(%p<40+aBoacc)<`7QN+xV-POW&4 zs1^4RJ|Rj@ZMi%XlvIobNKBK?C2Frx@qF=RK^=`oCnNc- z!uVM1?djbd7sa=6U9EcOowq%dPaLoB&^NeaCHcSI__qTf5Gjk)wbX?1aOBY;v+B9S=X@M&!)i)L5?x zhT|$^9{s#ZTJr5TUww?=LoNJ?#_rv_gPFOx`@4F&4>--nG%^Ii0GhX|Ln*`i7mO_~ zOy9Y9-S(pshZHsCrKFVYY^(*r5C0W7`Aq^Nl9w z^!x9=Ut4={UvSZaC7Ny9R`a{|ZHW%m^s8{s4d)0Ow;ttuJzbhlzkW$uQk*Y2EG98M)h_CE zDvWK}){p0KK$hZkB8VK73oDg!bYdDXcGW3}zNDyUa!U82q)1jOrF58Fa^0S8xwWka z0lPX3v@7BpHf$1itoO+#i(8eIlgFoJ<$}YVEuB4lELY!Dp?dDsxA>c{yA&2K_(65S zd6&XtPd*P1+dA zb%nHc*$R#`+IU}EME2sLdEK>VNKTid@!D&zsfk8Ijt3sTqn|6;MV-B-U@(CBm8=ej zqn!w{ENO18-=3eJ{fA0|{$33kU+0*4KPG812ht{@s@ObcgdV3%ojX)d& z4U8~Az|-1{NdMr1A{afe0GTo^G}P6>#$UEza(@8YyvVE?)R0tC3Z>&Ff)zpAsMiDC z2xvsf1{Uv081GRaf&s5T$YG$wVH^PliwUyu5br;5urzQ$xtyGA5lsd&zBL`^^M)l) zw_on^c({PyivdrJ^ZLCq&LwE?=!BZe!#KyAaOxpwpttZjg~jrSaU=O5!-vSpDXAO* z$E5f^W~c}UD-OWELt97_f;)Tm8S?mXlVQj9z1#{6s%vYHK-qx)zzetyMIwxCA@-D2 z7EvFmq-FpFD>DTpGKjNE1|3B3b2us>Q+rz}>#3LC1tTuJp1=I^%b~fU z5n7v@8Gt}7PE6porxQd>XryQqw8{y{<{z)2uOfvHtVDrjDcUeWGw@XW}Gk%h7KtO_x-oPdvAXRFFyN>blV*_aOLIYdHRBWmozlj zI@hgR_34BOQ??}Hso!+`J-dA3i6>H<*YXQ5ym0H8=bSbG0r(L+_U#`y^VHMI2j}M% z+<*5&hL>M{4%Tm4ubFes+0u=--WqG~j;Qat_c8TnAHK-%-L(r29odF}fEmJ=W4M#D z!0pC$Xw^b*KnGqV)~Etc5)|-ONvBzv&95~N@-gyXX(6DmNxOi^PYooBBzVQv5%U+5 z$q0E_sZ8YA+ff5YYO7$|<{eO4S`K4Jjz-|;8Myh@2Vuy#Q{dIP^Fh*C;exB~MDU)6 zjhi;Wl7;KRuzDBR?DL8+YUN!S#xy6tiz8@mJ<*f%%wHh!YNY2_hM1g?}1PsNHEF+s$(ti;mPC2za0Q!u~>S``j;MTZt4se^oC@cNr$9ph}=}- zxw!?Hck0Zd$XsG&M7g+FBr5qsK@|y=k=SIP&5VYrtm=`e3bXQ7D#d1&IHblMQ^ZRY zEKOF*d8pqpVI9WG_4-OmHB?%p96a*a6L#(vCJoegaFOsu^W9N?Tnug{^^{7!}abog&KK=C5 zuRA(CyWvkZ$X2SYt+!#v;XQ|uO&@OJWm7PM*T>3f91e58lg1Al`RWJrY7fpnp*F*A z%2daK`nX@E4q`Ix$G9qpmo^-y@D%jHxajhDeNGI-wHU;k5GkcL8-@uJD?u9%qKksV zX7estvgD5<`u86Wx0?&pwX`(aj~oqeT)##)AkSgF{PHWLwQDwqy1M;(QUu2lPfp9w zI9#dF>kaUPUUM3966mJ>T*ycd7ahSD*YVS5WF#t=qT`hK~Wr%gzTE zCb2q1+5?doW6nh>0w#kFN(zhN;GXUB&W&p^nMsoC+B(ISwr+9Q(BZ{Wa@<{Qjr44GonCCQO;4b68STcinw|-kkH#8#w)>X=%YgP&4o6jgV1L z0B)xR*IO&Ogg|$f7pkjjp$Ea7P{{RGzJPO}_wh}gP z-G_-O4~0eL&~L;9Fx!k^!|NW!grWAxA@Fqc%0AMN5JZlEjc~{#8_lV5hC7oRJz^xx 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�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 z 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

Benchmark document diff --git a/website/src/content/docs/docs/agent-to-agent.mdx b/website/src/content/docs/docs/agent-to-agent.mdx index 9f964103a1..a25ede4465 100644 --- a/website/src/content/docs/docs/agent-to-agent.mdx +++ b/website/src/content/docs/docs/agent-to-agent.mdx @@ -4,7 +4,7 @@ description: "Use bindings to let agents communicate with each other." skill: true --- -Agents communicate through [bindings](/docs/bindings). You define a bindings group that lets one agent send work to another, and the agent calls it like any other CLI command. +Agents communicate through [custom bindings](/docs/extensions/custom-bindings). You define a bindings group that lets one agent send work to another, and the agent calls it like any other CLI command. ## Example: code writer + reviewer diff --git a/website/src/content/docs/docs/agents/claude.mdx b/website/src/content/docs/docs/agents/claude.mdx index 1862db52a2..aa0181b2d8 100644 --- a/website/src/content/docs/docs/agents/claude.mdx +++ b/website/src/content/docs/docs/agents/claude.mdx @@ -14,7 +14,7 @@ skill: false Read [Sessions](/docs/sessions) first for session options, streaming events, prompts, and lifecycle management. -## LLM Credentials +## Model & credentials Set the relevant variable(s) on the session's `env`, sourced from your server's environment: @@ -25,7 +25,7 @@ Set the relevant variable(s) on the session's `env`, sourced from your server's - `CLAUDE_CODE_USE_BEDROCK=1` — use Amazon Bedrock (auth via the AWS credential chain: `AWS_REGION`, `AWS_PROFILE`, …). - `CLAUDE_CODE_USE_VERTEX=1` — use Google Vertex AI (auth via Google Cloud credentials). -See [LLM Credentials](/docs/llm-credentials), and Claude Code's [environment variables](https://code.claude.com/docs/en/env-vars) for the full list. +See [Models & Credentials](/docs/models-and-credentials), and Claude Code's [environment variables](https://code.claude.com/docs/en/env-vars) for the full list. ## Skills @@ -40,7 +40,7 @@ Expose extra tools to the agent by passing `mcpServers` to `openSession`. Both l -**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. +**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.process.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. ## Customizing the agent diff --git a/website/src/content/docs/docs/agents/codex.mdx b/website/src/content/docs/docs/agents/codex.mdx index 4072b98043..852af14ee0 100644 --- a/website/src/content/docs/docs/agents/codex.mdx +++ b/website/src/content/docs/docs/agents/codex.mdx @@ -13,7 +13,7 @@ skill: false Read [Sessions](/docs/sessions) first for session options, streaming events, prompts, and lifecycle management. -## LLM Credentials +## Model & credentials Set the relevant variable(s) on the session's `env`, sourced from your server's environment: @@ -21,7 +21,7 @@ Set the relevant variable(s) on the session's `env`, sourced from your server's - `OPENAI_BASE_URL` — route through a gateway or OpenAI-compatible endpoint. - Custom providers — defined in `~/.codex/config.toml`; each provider's `env_key` names the variable Codex reads for its key (e.g. `AZURE_OPENAI_API_KEY`, `MISTRAL_API_KEY`). -See [LLM Credentials](/docs/llm-credentials), and Codex's [config reference](https://developers.openai.com/codex/config-reference) for details. +See [Models & Credentials](/docs/models-and-credentials), and Codex's [config reference](https://developers.openai.com/codex/config-reference) for details. ## Skills @@ -36,7 +36,7 @@ Expose extra tools to the agent by passing `mcpServers` to `openSession`. Both l -**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. +**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.process.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. ## Customizing the agent diff --git a/website/src/content/docs/docs/agents/opencode.mdx b/website/src/content/docs/docs/agents/opencode.mdx index 347dc8bcc1..857fc084d2 100644 --- a/website/src/content/docs/docs/agents/opencode.mdx +++ b/website/src/content/docs/docs/agents/opencode.mdx @@ -14,7 +14,7 @@ skill: false Read [Sessions](/docs/sessions) first for session options, streaming events, prompts, and lifecycle management. -## LLM Credentials +## Model & credentials OpenCode auto-detects a provider when its key is present on the session's `env`, sourced from your server's environment. Common variables: @@ -25,7 +25,7 @@ OpenCode auto-detects a provider when its key is present on the session's `env`, - `GROQ_API_KEY` — Groq. - …plus Amazon Bedrock, Azure, Google Vertex, and 70+ providers via [models.dev](https://models.dev). -See [LLM Credentials](/docs/llm-credentials), and OpenCode's [providers docs](https://opencode.ai/docs/providers/) for the full list. +See [Models & Credentials](/docs/models-and-credentials), and OpenCode's [providers docs](https://opencode.ai/docs/providers/) for the full list. ## Model configuration @@ -39,8 +39,8 @@ Two settings will silently produce an **empty response** if wrong: ```ts // Write the config before creating the session -await agent.mkdir("/home/agentos/.config/opencode", { recursive: true }); -await agent.writeFile( +await agent.filesystem.mkdir("/home/agentos/.config/opencode", { recursive: true }); +await agent.filesystem.writeFile( "/home/agentos/.config/opencode/opencode.json", JSON.stringify({ $schema: "https://opencode.ai/config.json", @@ -52,7 +52,7 @@ await agent.writeFile( }), ); -await agent.openSession({ +await agent.sessions.open({ agent: "opencode", env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! }, }); @@ -71,7 +71,7 @@ Expose extra tools to the agent by passing `mcpServers` to `openSession`. Both l -**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. +**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.process.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. ## Customizing the agent diff --git a/website/src/content/docs/docs/agents/pi.mdx b/website/src/content/docs/docs/agents/pi.mdx index 2d15263b0b..dc9d49ebab 100644 --- a/website/src/content/docs/docs/agents/pi.mdx +++ b/website/src/content/docs/docs/agents/pi.mdx @@ -14,14 +14,14 @@ skill: true Read [Sessions](/docs/sessions) first for session options, streaming events, prompts, and lifecycle management. -## LLM Credentials +## Model & credentials Set the relevant variable on the session's `env`, sourced from your server's environment: - `ANTHROPIC_API_KEY` — Anthropic (Claude), the default. - Other providers — use the provider-named key (e.g. `OPENAI_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY`). -See [LLM Credentials](/docs/llm-credentials), and Pi's [providers docs](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/providers.md) for the full list. +See [Models & Credentials](/docs/models-and-credentials), and Pi's [providers docs](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/providers.md) for the full list. ## Skills @@ -36,7 +36,7 @@ Expose extra tools to the agent by passing `mcpServers` to `openSession`. Both l -**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. +**Pre-install `npx`-launched servers.** A local server started with `npx -y …` writes install progress to **stdout** on its first run, which corrupts the MCP stdio handshake (you'll see `Connection closed`). Pre-install it in the VM so `npx` is silent — `await agent.process.exec("npm install -g @modelcontextprotocol/server-filesystem")` before the session — or pin the package and point `command` at the installed binary. ## Extensions @@ -57,4 +57,3 @@ See the [Pi extension documentation](https://github.com/badlogic/pi-mono/tree/ma ## Customizing the agent Pi is a built-in agent, but it's just a software package under the hood. To ship your own ACP adapter, swap the underlying agent SDK, or register a tweaked Pi build as a new agent, see [Custom Agents](/docs/agents/custom). - diff --git a/website/src/content/docs/docs/architecture.mdx b/website/src/content/docs/docs/architecture.mdx index 5df952c5f4..0f153967c8 100644 --- a/website/src/content/docs/docs/architecture.mdx +++ b/website/src/content/docs/docs/architecture.mdx @@ -174,7 +174,7 @@ The kernel is the single chokepoint. Each kind of guest operation is serviced by The executor is the untrusted half of the VM. It runs the guest code and reaches the kernel for everything else. -- **JavaScript Acceleration.** Guest JavaScript runs on a native V8 runtime (the same engine in Chrome and Node.js, with the full JIT compiler) inside an isolate. This is what we call **JavaScript Acceleration**: the guest's JavaScript executes at native speed, not through an interpreter or a translation shim. It is genuinely fast, and it presents normal Node.js semantics. See [JavaScript Runtime](/docs/nodejs-runtime). +- **JavaScript Acceleration.** Guest JavaScript runs on a native V8 runtime (the same engine in Chrome and Node.js, with the full JIT compiler) inside an isolate. This is what we call **JavaScript Acceleration**: the guest's JavaScript executes at native speed, not through an interpreter or a translation shim. It is genuinely fast, and it presents normal Node.js semantics. See [JavaScript](/docs/execution/javascript). - **WASM alongside it.** The shell (`sh`) and the coreutils behind process execution ship as WebAssembly modules, and you can run your own WASM too. See [POSIX Syscalls](/docs/architecture/posix-syscalls) and the [Compiler Toolchain](/docs/architecture/compiler-toolchain). - **Native binaries.** Tools mounted into the VM run inside the same boundary as everything else. - **No host fallthrough.** The executor holds no capability of its own. For every file read, process spawn, or socket open, it issues a syscall and blocks for the kernel's reply. @@ -336,13 +336,13 @@ The `agentOS()` actor (from `@rivet-dev/agentos`) wraps the raw VM in a [Rivet A - **Recurring work.** Schedule a shell command or an agent session on a cron expression. - **Overlap control.** Choose what happens when a run is still going when the next is due (`allow`, `skip`, or `queue`). -- **Observable.** Stream `cronEvent`s to watch executions. See [Cron Jobs](/docs/cron). +- **Observable.** Stream `cronEvent`s to watch executions. See [Crons & Loops](/docs/cron). ### Workflows - **Durable multi-step tasks.** A workflow is the actor's `run` handler wrapped in `workflow()`, where each `ctx.step()` is recorded, retried, and resumed independently. - **Crash-proof.** If the process dies mid-run, replay skips completed steps and continues where it left off. -- **Composable.** The output of one step feeds the next: clone a repo, let an agent fix a bug, run the tests. See [Workflow Automation](/docs/workflows). +- **Composable.** The output of one step feeds the next: clone a repo, let an agent fix a bug, run the tests. See [Workflows & Graphs](/docs/workflows). ### Persistence & sleep/wake diff --git a/website/src/content/docs/docs/architecture/agent-sessions.mdx b/website/src/content/docs/docs/architecture/agent-sessions.mdx index 4b9705ac99..2b6681e8b6 100644 --- a/website/src/content/docs/docs/architecture/agent-sessions.mdx +++ b/website/src/content/docs/docs/architecture/agent-sessions.mdx @@ -30,7 +30,7 @@ The sidecar—not the actor or SDK—owns defaults and orchestration. TypeScript One prompt may run per session. Cancellation races are first-writer-wins. AgentOS does not automatically replay interrupted prompts because tool side effects may already have occurred. -The ACP runtime and core SDK transports impose no wall-clock deadline on prompts or human permission waits. After 30 seconds without ACP activity, AgentOS logs an inactivity warning with the total elapsed time and last observed activity, repeating every 30 seconds until activity resumes. Streaming output resets that interval. Bootstrap, teardown, filesystem, terminal, and other machine-to-machine operations retain bounded failure deadlines. Actor-hosted calls currently inherit RivetKit's maximum timer-safe action bound of about 24.8 days because RivetKit does not yet support an unbounded action. +The ACP runtime and AgentOS client transports impose no wall-clock deadline on prompts or human permission waits. After 30 seconds without ACP activity, AgentOS logs an inactivity warning with the total elapsed time and last observed activity, repeating every 30 seconds until activity resumes. Streaming output resets that interval. Bootstrap, teardown, filesystem, terminal, and other machine-to-machine operations retain bounded failure deadlines. Actor-hosted calls currently inherit RivetKit's maximum timer-safe action bound of about 24.8 days because RivetKit does not yet support an unbounded action. ## Reads versus adapter operations diff --git a/website/src/content/docs/docs/architecture/compiler-toolchain.mdx b/website/src/content/docs/docs/architecture/compiler-toolchain.mdx index 0187b61362..18010bf2d9 100644 --- a/website/src/content/docs/docs/architecture/compiler-toolchain.mdx +++ b/website/src/content/docs/docs/architecture/compiler-toolchain.mdx @@ -92,7 +92,7 @@ The same path is open to your own programs. A program you compile for `wasm32-wasip1` runs as a guest command exactly like the bundled ones; link the `wasi-ext` bindings if it needs processes, users, or sockets, and leave them out for a pure-compute tool. Heavy native binaries that are not yet available as -WASM belong in a [mounted sandbox](/docs/sandbox) instead. +WASM belong in an [external sandbox](/docs/extensions/sandboxes) instead. ## Recommendations diff --git a/website/src/content/docs/docs/architecture/javascript-executor.mdx b/website/src/content/docs/docs/architecture/javascript-executor.mdx index 89a0de62f0..018a421183 100644 --- a/website/src/content/docs/docs/architecture/javascript-executor.mdx +++ b/website/src/content/docs/docs/architecture/javascript-executor.mdx @@ -12,7 +12,7 @@ transport. For socket ownership, loopback, DNS, and network policy, see [Networking](/docs/architecture/networking). For the client-facing JavaScript -environment, see [JavaScript Runtime](/docs/js-runtime). +environment, see [JavaScript](/docs/execution/javascript). Tokio never invokes guest JavaScript from a runtime worker. A Tokio task stores diff --git a/website/src/content/docs/docs/architecture/packages-and-command-resolution.mdx b/website/src/content/docs/docs/architecture/packages-and-command-resolution.mdx index cc3135cfc4..1d4f3e1f85 100644 --- a/website/src/content/docs/docs/architecture/packages-and-command-resolution.mdx +++ b/website/src/content/docs/docs/architecture/packages-and-command-resolution.mdx @@ -91,7 +91,7 @@ is what the runtime **projects** from that package when it mounts it: | `share/` | FHS data — `share/man/man/*`, etc. | | `current` | Symlink `→ `; switching versions is one atomic rename. | -```jsonc +```json // package.json — commands come from "bin"; an agent's ACP entrypoint is just one of them { "name": "@agentos-software/pi", "version": "0.0.1", "bin": { "pi-acp": "dist/pi-acp.wasm" } } ``` diff --git a/website/src/content/docs/docs/architecture/processes.mdx b/website/src/content/docs/docs/architecture/processes.mdx index 0ae436ddbb..9c98235c1d 100644 --- a/website/src/content/docs/docs/architecture/processes.mdx +++ b/website/src/content/docs/docs/architecture/processes.mdx @@ -8,7 +8,7 @@ skill: true This page is an internals deep-dive on the kernel's **process model**: the data structures and syscall paths behind every guest process. For the client-facing -API (`exec`, `spawn`, `openShell`, lifecycle, the process tree), see +API (`process.exec`, `process.spawn`, `terminal.open`, lifecycle, the process tree), see [Processes & Shell](/docs/processes). For the surrounding component and trust model, see [Architecture](/docs/architecture). @@ -82,10 +82,10 @@ An interactive shell needs a terminal, not just piped stdio: line editing, job control signals, and window size all depend on a PTY. The kernel provides virtual PTY devices for this. -- **A shell is a process plus a PTY.** `openShell` allocates a kernel PTY and starts a shell process attached to it, returning a `shellId`. The PTY is a virtualized terminal device, never a host `/dev/pts` entry. -- **Bidirectional terminal I/O.** `writeShell` feeds keystrokes into the PTY master side; everything the shell and its children emit comes back as `shellData` events. This carries terminal control sequences, so full-screen TUIs behave correctly. -- **Resize is a terminal operation.** `resizeShell` updates the PTY's window size (columns and rows), which the kernel propagates to the foreground process the way a real terminal resize would, so programs relying on `TIOCGWINSZ`-style sizing redraw correctly. -- **Teardown.** `closeShell` tears down the PTY and the attached shell process. An open shell keeps the VM active, the same way an open PTY keeps a session alive on a real system. +- **A shell is a process plus a PTY.** `terminal.open` allocates a kernel PTY and starts a shell process attached to it, returning a `shellId`. The PTY is a virtualized terminal device, never a host `/dev/pts` entry. +- **Bidirectional terminal I/O.** `terminal.write` feeds keystrokes into the PTY master side; everything the shell and its children emit comes back as `shellData` events. This carries terminal control sequences, so full-screen TUIs behave correctly. +- **Resize is a terminal operation.** `terminal.resize` updates the PTY's window size (columns and rows), which the kernel propagates to the foreground process the way a real terminal resize would, so programs relying on `TIOCGWINSZ`-style sizing redraw correctly. +- **Teardown.** `terminal.close` tears down the PTY and the attached shell process. An open shell keeps the VM active, the same way an open PTY keeps a session alive on a real system. ## WASM sh and coreutils on the process model diff --git a/website/src/content/docs/docs/core.mdx b/website/src/content/docs/docs/core.mdx index e66f826e33..a2374a2929 100644 --- a/website/src/content/docs/docs/core.mdx +++ b/website/src/content/docs/docs/core.mdx @@ -1,14 +1,15 @@ --- -title: "Core Package" -description: "Use @rivet-dev/agentos-core standalone for direct VM control without the Rivet Actor runtime." +title: "Direct VM API" +description: "Use the direct AgentOS VM API or layer it into an AgentOS actor." skill: true --- -## agentOS vs agentOS Core +## Direct VM vs actor -The `agentOS()` actor (from `@rivet-dev/agentos`) wraps the core package and adds: +`@rivet-dev/agentos` includes both the direct `AgentOs` VM API and the +`agentOS()` actor API: -| | Core (`@rivet-dev/agentos-core`) | Actor (`@rivet-dev/agentos`) | +| | Direct VM | Actor | |-|---|---| | Persistence | In-memory by default (pluggable via [mounts](#mounts)) | Persistent filesystem and sessions | | Distributed state | Manage yourself | Built-in distributed statefulness | @@ -21,16 +22,16 @@ The `agentOS()` actor (from `@rivet-dev/agentos`) wraps the core package and add | Agent-to-agent communication | Custom | Built into [Rivet Actors](/docs/agent-to-agent) | | Authentication | Set up yourself | [Documentation](/docs/authentication) | -We recommend using [Rivet Actors](https://rivet.dev/docs/actors) because they provide a portable way to run `agentOS()` on any infrastructure with built-in persistence, networking, and orchestration. Use the core package if you need the most bare-bones implementation possible. +We recommend using [Rivet Actors](https://rivet.dev/docs/actors) when you need persistence, networking, and orchestration. Use `AgentOs.create()` from the same package when you need direct VM control in a Node.js process. -`agentOS()` returns an ordinary TypeScript Rivet actor definition. Its config accepts the core VM options together with normal actor state, actions, events, queues, connection types, and lifecycle hooks such as `onBeforeConnect`. AgentOS actions and events are merged in automatically; their names are reserved so they cannot be accidentally shadowed. After a wake, the actor creates the core SDK VM lazily on the first AgentOS action and disposes it on sleep. This lets a connection subscribe before the `vmBooted` event is emitted. +`agentOS()` returns an ordinary TypeScript Rivet actor definition. Its config accepts the VM options together with normal actor state, actions, events, queues, connection types, and lifecycle hooks such as `onBeforeConnect`. AgentOS actions and events are merged in automatically; their names are reserved so they cannot be accidentally shadowed. After a wake, the actor creates its VM lazily on the first AgentOS action and disposes it on sleep. This lets a connection subscribe before the `vmBooted` event is emitted. Creation input is inferred from the actor definition and is passed through normal client creation options: `client.vm.create("key", { input })`. The same input reaches `createState(c, input)` and `onCreate(c, input)`. ## Install ```bash -npm install @rivet-dev/agentos-core +npm install @rivet-dev/agentos ``` ## Boot a VM @@ -41,11 +42,11 @@ Create a VM and drive it directly — no actor runtime, no client/server split. ## Sidecar process -Every VM runs inside a **shared sidecar process** rather than a process of its own. By default all VMs are tenants of a single, process-global sidecar (the `default` pool), so each additional VM only adds its marginal cost — a V8 isolate plus its kernel state — instead of a whole OS process. This is what keeps per-VM memory in the tens of MB and warm VM creation in the single-digit milliseconds (see [Benchmarks](/docs/benchmarks)). +Every VM runs inside a **shared sidecar process** rather than a process of its own. By default all VMs are tenants of a single, process-global sidecar (the `default` pool), so each additional VM only adds its marginal cost — a V8 isolate plus its kernel state — instead of a whole OS process. This is what keeps per-VM memory in the tens of MB and warm VM creation in the single-digit milliseconds (see [Performance](/docs/performance)). This is automatic — `agentOS()` and `AgentOs.create()` use the shared default sidecar with no configuration, and the same applies to Rivet Actors (each actor's VM is a tenant of the shared process). Disposing a VM tears down only that VM; the shared sidecar process is reused across VMs and stays alive for the lifetime of the host process. -For advanced cases the core package exposes explicit sidecar handles so you can isolate a group of VMs in their own process: +For advanced cases the direct VM API exposes explicit sidecar handles so you can isolate a group of VMs in their own process: @@ -98,10 +99,10 @@ The top-level fields are documented inline above. See [Mounts](#mounts) and [Sof ### Session events -With the core package, `onSessionEvent` receives a generic union containing exact native ACP `SessionUpdate`, `RequestPermissionRequest`, and `RequestPermissionResponse` payloads wrapped with AgentOS durability metadata. Register it before prompting. On reconnect, also read durable history after your last sequence and deduplicate by `(sessionId, sequence)`: +With the direct VM API, `onSessionEvent` receives a generic union containing exact native ACP `SessionUpdate`, `RequestPermissionRequest`, and `RequestPermissionResponse` payloads wrapped with AgentOS durability metadata. Register it before prompting. On reconnect, also read durable history after your last sequence and deduplicate by `(sessionId, sequence)`: ### Timeouts and sleep -Action timeouts and automatic sleep/wake are features of the [`agentOS()` actor](/docs/quickstart), not the core package. A core VM stays alive until you call `dispose()`. See [Persistence & Sleep](/docs/persistence) for the actor's sleep lifecycle. +Action timeouts and automatic sleep/wake are features of the [`agentOS()` actor](/docs/quickstart), not the direct VM API. A direct VM stays alive until you call `dispose()`. See [Persistence & Sleep](/docs/persistence) for the actor's sleep lifecycle. diff --git a/website/src/content/docs/docs/cost-evaluation.mdx b/website/src/content/docs/docs/cost-evaluation.mdx deleted file mode 100644 index 386b500520..0000000000 --- a/website/src/content/docs/docs/cost-evaluation.mdx +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: "Cost Evaluation" -description: "How agentOS compares on cost to per-second sandbox providers when you run coding-agent VMs on your own hardware." -skill: true ---- - -agentOS is a library you run on hardware you already control, not a metered service. That changes the cost model for running coding-agent VMs from "pay a provider per sandbox-second" to "pay for the compute you provision, then pack as much work onto it as it can hold." This page explains where the savings come from and how to reason about them honestly. It does not publish a single magic multiplier, because the real number depends on your workload, your hardware, and how you share VMs. - -For measured latency (cold start, warm execution, and reuse fast paths), see [Benchmarks](/docs/benchmarks). This page is about cost structure, not raw performance. - -## Where the savings come from - -Two structural differences drive the cost gap versus per-second sandbox providers: - -- **You run on your own hardware**: you choose the cloud, instance type, architecture, and region. A small commodity instance (for example an ARM VM from a budget host) costs a flat hourly or monthly rate that is typically far below what per-sandbox-second billing adds up to once you have steady agent traffic. You also avoid egress fees and vendor lock-in. -- **You decide the isolation granularity**: sandbox providers bill a full container or microVM per execution, usually with a minimum memory reservation that you pay for even when your code uses a fraction of it. With agentOS you own the VM lifecycle: you can dedicate a VM per tenant or per task for maximum isolation, or amortize setup by reusing one VM across many runs. - -## The isolation model matters for cost - -Each `AgentOs.create()` boots a fully virtualized VM, and each `exec()` / `run()` inside it is a fresh guest process. That gives you a dial between isolation and density: - -- **One VM per task or tenant (strongest isolation)**: create a VM, run the work, and dispose it, or give each tenant its own VM. Each VM is its own crash and resource domain, with the highest per-VM overhead. Best when load is untrusted or bursty. -- **A shared VM for trusted work**: reuse one VM across many runs to amortize the VM boot cost. Each `exec()` / `run()` still executes in a fresh guest process, so in-memory state does not leak between runs, but the VM and filesystem are shared. Good for trusted, sequential work. - -The denser you can safely pack agent work onto an instance, the lower your effective cost per execution. See [Resource Limits](/docs/resource-limits) for the per-VM caps that govern how densely you can pack, and [Processes & Shell](/docs/processes) for how guest processes run inside a VM. - -## How to estimate your own cost - -Because agentOS runs on hardware you provision, the honest way to compare is to plug in your own numbers: - -1. **Pick your hardware and its rate**: take the hourly or monthly price of the instance you would run on, and divide down to a per-second instance cost. -2. **Estimate how many concurrent VMs fit**: measure per-VM memory overhead on your target hardware under your isolation strategy, then divide your usable RAM by that figure. Leave headroom (the measurement and any orchestration layer will not bin-pack perfectly). -3. **Divide instance cost by concurrent VMs**: that gives a cost-per-VM-second you can compare against a provider's per-sandbox-second rate. - -Measure on the hardware and isolation strategy you will actually deploy. Per-VM overhead depends on whether you create a fresh VM per task or reuse one across runs, and on the work the agent does, so a number measured on one machine will not transfer cleanly to another. - -## Comparing against sandbox providers - -When you do compare against a per-second sandbox provider, hold the methodology honest: - -- **Sandbox cost** is the provider's minimum allocatable memory times their per-GiB-second rate (plus any egress and platform fees). The minimum reservation is the floor you pay even for tiny workloads. -- **agentOS cost** is your instance cost per second divided by the number of VMs you can keep live on it, with realistic headroom for bin-packing inefficiency. - -The advantage is largest for **many small, short executions**, where a per-sandbox minimum reservation dominates and your own hardware lets you pack densely. It narrows for **heavyweight, long-lived workloads** (for example dev servers that need hundreds of megabytes regardless), where the win shifts from density to hardware choice: you still avoid per-second metering, egress fees, and lock-in, but the raw memory-density advantage is smaller. - -| Workload | Primary cost advantage | -| --- | --- | -| Many small, short executions | Density: pack many VMs per instance, no per-sandbox minimum | -| Heavyweight, long-lived workloads | Hardware choice, flat instance pricing, no egress or lock-in | -| High concurrency | Reuse a VM across runs to amortize VM boot cost | - -Be careful generalizing cost ratios from a single benchmark. Provider pricing, instance pricing, and exchange rates change over time, and per-VM overhead varies by workload and isolation strategy. Re-measure on your own hardware before quoting a number. - -When you do need a full Linux sandbox for heavier agent workloads, see [agentOS vs Sandbox](/docs/versus-sandbox) for how the two models combine. diff --git a/website/src/content/docs/docs/crash-course.mdx b/website/src/content/docs/docs/crash-course.mdx index f02fc4a326..e3105b5cef 100644 --- a/website/src/content/docs/docs/crash-course.mdx +++ b/website/src/content/docs/docs/crash-course.mdx @@ -54,7 +54,7 @@ Approve or deny agent tool use with human-in-the-loop patterns or auto-approve f *See [Full Example](https://github.com/rivet-dev/agentos/tree/main/examples/crash-course) or [Documentation](/docs/approvals)* -### Bindings +### Custom Bindings Expose your JavaScript functions to agents as CLI commands inside the VM. Each binding group becomes a binary at `/usr/local/bin/agentos-{name}`, and each binding becomes a subcommand with flags auto-generated from its Zod input schema. The server below defines a `weather` binding group with a `forecast` binding; the client opens a session and prompts the agent, which calls the binding itself as a shell command. @@ -64,11 +64,11 @@ Expose your JavaScript functions to agents as CLI commands inside the VM. Each b -*See [Full Example](https://github.com/rivet-dev/agentos/tree/main/examples/bindings) or [Documentation](/docs/bindings)* +*See [Full Example](https://github.com/rivet-dev/agentos/tree/main/examples/bindings) or [Documentation](/docs/extensions/custom-bindings)* ### Agent-to-Agent -Let one agent call another through a [binding](/docs/bindings). The coder gets a `review` binding it invokes itself, which bridges into the reviewer's isolated VM. +Let one agent call another through a [custom binding](/docs/extensions/custom-bindings). The coder gets a `review` binding it invokes itself, which bridges into the reviewer's isolated VM. @@ -78,7 +78,7 @@ Let one agent call another through a [binding](/docs/bindings). The coder gets a *See [Full Example](https://github.com/rivet-dev/agentos/tree/main/examples/crash-course) or [Documentation](/docs/agent-to-agent)* -### Multiplayer & Realtime +### Multiplayer Connect multiple clients to the same agent VM. All subscribers see session output, process logs, and shell data in realtime. @@ -90,7 +90,7 @@ Connect multiple clients to the same agent VM. All subscribers see session outpu *See [Full Example](https://github.com/rivet-dev/agentos/tree/main/examples/crash-course) or [Documentation](/docs/multiplayer)* -### Workflows +### Workflows & Graphs Orchestrate multi-step agent tasks with durable workflows that survive crashes and restarts. @@ -136,7 +136,7 @@ Proxy HTTP requests into VMs with `httpRequest`. Create actor-namespaced preview *See [Full Example](https://github.com/rivet-dev/agentos/tree/main/examples/crash-course) or [Documentation](/docs/networking)* -### Cron Jobs +### Crons & Loops Schedule recurring commands and agent sessions with cron expressions. @@ -148,12 +148,12 @@ Schedule recurring commands and agent sessions with cron expressions. *See [Full Example](https://github.com/rivet-dev/agentos/tree/main/examples/crash-course) or [Documentation](/docs/cron)* -### Sandbox Mounting +### External Sandboxes agentOS uses a hybrid model: agents run in a lightweight VM by default and mount a full sandbox on demand for heavy workloads like browsers, compilation, and desktop automation. Sandboxes are powered by [Sandbox Agent](https://sandboxagent.dev), so you can swap providers without changing agent code. Mount the sandbox as a filesystem and expose its process management as bindings. -[Documentation](/docs/sandbox) +[Documentation](/docs/extensions/sandboxes) {/* SKILL_OVERVIEW_END */} diff --git a/website/src/content/docs/docs/cron.mdx b/website/src/content/docs/docs/cron.mdx index 9ad5346fc7..f579b8a378 100644 --- a/website/src/content/docs/docs/cron.mdx +++ b/website/src/content/docs/docs/cron.mdx @@ -1,10 +1,14 @@ --- -title: "Cron Jobs" +title: "Crons & Loops" description: "Schedule recurring commands and agent sessions in agentOS VMs." skill: true --- -Schedule recurring work with cron expressions, running either a shell command (`exec`) or an agent session (`session`), with overlap modes (`allow`, `skip`, `queue`) and native `cronEvent` streaming to monitor execution. Cron jobs keep the actor alive while a job runs; the actor can sleep between executions. +Schedule recurring work and long-running agent loops with cron expressions, +running either a shell command (`exec`) or an agent session (`session`), with +overlap modes (`allow`, `skip`, `queue`) and native `cronEvent` streaming to +monitor execution. Cron jobs keep the actor alive while a job runs; the actor +can sleep between executions. ## Schedule a command diff --git a/website/src/content/docs/docs/custom-software/definition.mdx b/website/src/content/docs/docs/custom-software/definition.mdx index 9d9d8ec5d7..be0ba98720 100644 --- a/website/src/content/docs/docs/custom-software/definition.mdx +++ b/website/src/content/docs/docs/custom-software/definition.mdx @@ -134,7 +134,7 @@ packed files, so the JSON never ships into the VM and the metadata never travels command/WASM packages it is **generated** for you (name from `package.json`); for agents you author the `agent` block (or `agentos-toolchain pack --agent ` writes it). -```jsonc +```json { "name": "my-agent", // → /opt/agentos/ "agent": { // optional — also exposes an agent session diff --git a/website/src/content/docs/docs/deployment.mdx b/website/src/content/docs/docs/deployment.mdx index e3bad50eaa..5ee64f65c2 100644 --- a/website/src/content/docs/docs/deployment.mdx +++ b/website/src/content/docs/docs/deployment.mdx @@ -10,7 +10,7 @@ agentOS is powered by [Rivet](https://rivet.dev), an open-source actor platform, - **[Rivet Cloud](https://rivet.dev/cloud)**: fully managed (Rivet Compute, or bring your own cloud). Zero-ops. - **Self-hosted**: run the open-source Rivet platform on your own infrastructure (Kubernetes, Hetzner, VMs, and more) for full control. -- **[agentOS Core](/docs/core)**: embed `@rivet-dev/agentos-core` directly in any Node.js backend, no platform required. +- **[Direct VM API](/docs/core)**: embed `@rivet-dev/agentos` directly in any Node.js backend, no platform required. Pick a deploy target below, or see [Rivet's deployment guides](https://rivet.dev/docs/deploy/). diff --git a/website/src/content/docs/docs/execution/bash.mdx b/website/src/content/docs/docs/execution/bash.mdx new file mode 100644 index 0000000000..93bba5d862 --- /dev/null +++ b/website/src/content/docs/docs/execution/bash.mdx @@ -0,0 +1,54 @@ +--- +title: "Bash" +description: "Run shell commands and arbitrary argv operations through the AgentOS execution lifecycle." +skill: true +--- + +Shell commands are the simplest AgentOS execution surface. Use `process.exec()` +for shell syntax such as pipes and redirects, and `process.execFile()` for +injection-safe command arguments. Both use the same execution lifecycle as +JavaScript, TypeScript, Python, and package workflows. + +## Run commands + +`process.exec()` runs a command through the VM's configured shell and returns +its outcome, exit code, stdout, stderr, and `executionId`. The command runs +inside the VM, never in the host shell. + + + +Prefer `process.execFile()` when command names or arguments come from data. Each +argument is transferred separately and AgentOS does not interpolate it into a +shell command. Keep `process.exec()` for workflows that intentionally need +shell syntax. + +## Detached and interactive work + +Set `detached: true` on `process.exec()` or `process.execFile()`. Manage the +returned `executionId` with `executions.wait()`, `executions.cancel()`, +`executions.signal()`, stdin, PTY resize, bounded output replay, reset, and +deletion. `process.spawn()` is the separate managed-child-process API: it +returns a PID and is controlled through the remaining `process.*` methods. + +An execution accepts only one active operation. Reusing a running ID fails +immediately. Process-only operations do not pin a retained language, so the +same idle execution can later run JavaScript or Python. + +## Files and software + +Shell commands see the persistent [filesystem](/docs/filesystem) shared by +agents, JavaScript, and Python. Common POSIX commands are available by default, +and additional software is projected through the [software registry](/docs/software). + +## Custom bindings + +[Custom Bindings](/docs/extensions/custom-bindings) appear as commands such as +`agentos-weather forecast`, so shell pipelines can use trusted host +capabilities without putting credentials inside the VM. + +## Permissions, limits, and timeouts + +Every command inherits the VM [permission policy](/docs/permissions) and +[resource limits](/docs/resource-limits). `timeoutMs` sets an operation-level +wall-clock deadline. A denied guest operation receives its normal POSIX error, +and a timed-out execution retains a structured `timed_out` result. diff --git a/website/src/content/docs/docs/execution/javascript-compatibility.mdx b/website/src/content/docs/docs/execution/javascript-compatibility.mdx new file mode 100644 index 0000000000..bca2275d35 --- /dev/null +++ b/website/src/content/docs/docs/execution/javascript-compatibility.mdx @@ -0,0 +1,77 @@ +--- +title: "Node.js Compatibility" +description: "Node.js builtins available to JavaScript running inside AgentOS." +--- + +Guest JavaScript never touches the host Node.js runtime. Every `import` or +`require` of a `node:` builtin resolves to a kernel-backed bridge or an +in-isolate implementation, and unknown or denied modules fail explicitly. The +guest reports Node.js `v22.0.0` through `process.version`. + +## How builtins are backed + +- **Kernel-backed:** calls route through the VM filesystem, socket table, + process table, DNS resolver, or entropy source. +- **In-isolate:** pure JavaScript implementations run entirely inside V8 and + require no host access. +- **Denied:** importing the module throws `ERR_ACCESS_DENIED`. + +The canonical inventory lives in +`crates/execution/assets/polyfill-registry.json`. + +A guest never falls through to a real host builtin. Anything not bridged +or implemented in the isolate is denied. + +## Kernel-backed builtins + +| Module | Backed by | +| --- | --- | +| `fs`, `fs/promises` | VM filesystem, including fds, streams, metadata, symlinks, and polling-based watchers. | +| `child_process` | VM process table. `spawn`, `exec`, `execFile`, and sync variants launch guest processes. | +| `net`, `dgram` | VM TCP, Unix-socket, and UDP tables. | +| `dns`, `dns/promises` | VM DNS resolver. | +| `http`, `https`, `http2`, `tls` | VM socket and TLS paths, including clients, servers, and connection pooling. | +| `os` | VM-scoped platform, architecture, hostname, CPU, memory, and user information. | +| `crypto` | Entropy, hashes, HMAC, ciphers, scrypt, UUIDs, and WebCrypto. | +| `process` | VM environment, working directory, signals, timers, stdio, and umask. | +| `module` | `createRequire`, builtin resolution, and basic `Module` compatibility. | +| `console` | Bounded formatting and guest stdout/stderr. | +| `readline`, `sqlite`, `tty` | Kernel-backed compatibility surfaces. | +| `timers`, `timers/promises` | Timeout, interval, immediate, and promise variants. | +| `stream/web`, `stream/consumers`, `stream/promises` | Web Streams and stream helpers. | + +Network builtins obey the VM's [permission policy](/docs/permissions). Network +access is denied until the VM creator grants it. + +## In-isolate builtins + +Pure JavaScript implementations include `path`, `buffer`, `events`, `stream`, +`util`, `assert`, `url`, `querystring`, `string_decoder`, `zlib`, `punycode`, +`constants`, and `sys`. Default and named ESM imports are supported. + +Compatibility shims are also provided for common feature detection, including +`async_hooks`, `diagnostics_channel`, `perf_hooks`, `worker_threads`, `vm`, and +`v8`. The `worker_threads` shim does not create real worker threads. + +## Denied builtins + +`cluster`, `domain`, `inspector`, `repl`, `trace_events`, and `wasi` are denied. + +## Global APIs + +Modern web globals are available, including `fetch`, `Headers`, `Request`, +`Response`, `TextEncoder`, `TextDecoder`, `Buffer`, URL APIs, `Blob`, `File`, +`FormData`, abort APIs, `structuredClone`, `performance`, and WebAssembly. +`fetch()` uses the VM socket table and follows the same policy as `http` and +`net`. + +## Modules and output + +Both ESM and CommonJS use the VM filesystem and normal `node_modules` +resolution. Console and stream output is delivered through the same bounded +process-output path described in [Processes & Shells](/docs/processes). + + + +Return to the [JavaScript guide](/docs/execution/javascript) for TypeScript, +packages, files, processes, networking, bindings, permissions, and limits. diff --git a/website/src/content/docs/docs/execution/javascript.mdx b/website/src/content/docs/docs/execution/javascript.mdx new file mode 100644 index 0000000000..b3af7306e0 --- /dev/null +++ b/website/src/content/docs/docs/execution/javascript.mdx @@ -0,0 +1,122 @@ +--- +title: "JavaScript" +description: "Execute JavaScript and TypeScript, install npm dependencies, and manage execution lifecycles in AgentOS." +skill: true +--- + +AgentOS runs JavaScript in native V8 behind the VM kernel boundary. Use the +first-class language methods for source, files, values, TypeScript, npm +projects, and package binaries. Use `process.execFile()` only when you need an +unusual command that does not have a language-level method. + +## Get started + +`@rivet-dev/agentos-javascript` is the focused JavaScript/TypeScript entry +point. It creates the same AgentOS VM and delegates every operation to the +sidecar-owned execution protocol. + + + +Every attached call returns one result discriminated by `outcome`. Successful +evaluations contain a JSON-compatible `value`; failed, cancelled, and timed-out +results contain a structured `error`. Stdout and stderr are always captured +with explicit truncation flags. Evaluation results also expose the value as a +bounded `{ type: "json", data }` display output. Returning `undefined`, a +function, a symbol, a circular object, or another non-JSON value produces an +`evaluation_serialization_failed` result instead of losing the value silently. + +## Use the core client + +The same methods are available directly on `AgentOs` under +`javascript.execute()`, `javascript.evaluate()`, `javascript.executeFile()`, +`javascript.typescript.*`, and `javascript.npm.*`. + +The focused runtime adds default `cwd` and `env` values and exposes the core VM +as `runtime.vm`; it does not implement a second execution engine. + +## Retain JavaScript state + +Give an operation an `executionId` and set `createIfMissing: true` on the first +call. Later inline JavaScript and TypeScript operations with the same ID share +one retained JavaScript realm. `inputs` is replaced on every operation and is +serialized separately from source. + + + +An execution accepts one active operation at a time. Reusing a busy ID fails +immediately. Bash, files, npm operations, and type checks can use the same +lifecycle ID, but run in fresh processes and do not alter retained language +memory. `executions.reset()` clears memory, results, and output; filesystem and +installed-package changes remain. `executions.delete()` removes an idle +execution. + +## TypeScript + +TypeScript execution is a convenience API in the JavaScript layer. **Executing +TypeScript transpiles it without semantic type checking.** Call +`javascript.typescript.check()` or +`javascript.typescript.checkProject()` when diagnostics are part of the +workflow. + + + +Inline `filePath` identifies source for diagnostics and module resolution; it +does not read that file. Use `javascript.typescript.executeFile()` on the core +client to execute an existing file. JavaScript and TypeScript inline calls +share retained state when they use the same execution ID. + +## npm projects and packages + +Use the options-only `javascript.npm.install()` overload for the project in +`cwd`. `frozen: true` performs a lockfile-exact clean install. Pass package +names for a named install, and use `javascript.npm.runScript()` or +`javascript.npm.runPackage()` instead of assembling npm commands. The focused +runtime exposes the same workflows as `installNpmPackages()`, +`executeNpmScript()`, and `executeNpmPackage()`. + + + +Package names, script names, paths, and arguments are transferred as distinct +argv values. Installs modify the VM-wide filesystem, so all executions in that +VM see the same dependencies. AgentOS admits only one npm or Python package +mutation at a time in a VM; a concurrent install fails with `execution_busy` +instead of allowing package managers to corrupt shared state. + +## Detached work and output + +Set `detached: true` on an execution method for a long-running program. The +returned descriptor contains its `executionId`; use `executions.wait()`, +`executions.cancel()`, `executions.signal()`, `executions.writeStdin()`, +`executions.closeStdin()`, `executions.resizePty()`, and +`executions.readOutput()` to manage it. Core callbacks receive exact +`Uint8Array` chunks. + + + +Actor actions use the same nested method paths as Core; RivetKit transports +them with dotted wire names such as `javascript.execute` and +`executions.readOutput`. Actor stdin and output use tagged +`{ encoding: "utf8" | "base64", data: string }` values, and live output is +broadcast through `executionOutput` and `executionCompleted` events. + +## Filesystem, processes, and networking + +`node:fs` uses the VM [filesystem](/docs/filesystem), `node:child_process` +starts kernel-managed guest processes, and Node networking APIs use the VM +socket table. Files and installed packages are immediately visible to Bash, +Python, agents, and other executions. See [Processes & Shells](/docs/processes), +[Networking & Previews](/docs/networking), and the detailed +[Node.js compatibility matrix](/docs/execution/javascript-compatibility). + +## Custom bindings and policy + +Guest JavaScript invokes [Custom Bindings](/docs/extensions/custom-bindings) +as ordinary typed commands, keeping host credentials outside the VM. + + + +All operations inherit the VM's [permissions](/docs/permissions) and +[resource limits](/docs/resource-limits). `timeoutMs` adds an operation-level +wall-clock deadline covering sidecar staging, TypeScript transformation, +guest execution, and result collection; it does not replace the independently +bounded VM safety limits. diff --git a/website/src/content/docs/docs/execution/python.mdx b/website/src/content/docs/docs/execution/python.mdx new file mode 100644 index 0000000000..75ad1d71b3 --- /dev/null +++ b/website/src/content/docs/docs/execution/python.mdx @@ -0,0 +1,93 @@ +--- +title: "Python" +description: "Execute Python source, files, modules, and package workflows in AgentOS." +skill: true +--- + +AgentOS runs CPython 3.13 as a first-class VM execution engine. Python shares +the VM filesystem, process tree, networking policy, permissions, and limits +with agents, Bash, JavaScript, and installed software. + +## Get started + +`@rivet-dev/agentos-python` is the focused Python entry point. It uses the same +sidecar protocol as the core `AgentOs` client. + + + +Use `python.execute()` for source and `python.evaluate()` for a JSON-compatible +value on the core client. The focused package exposes them as `execute()` and +`evaluate()` and provides default `cwd` and `env` values. Evaluation values are +also returned as bounded JSON display outputs. A value that Python's JSON +encoder cannot represent produces a structured +`evaluation_serialization_failed` result. + +## Retain Python state + +Reuse an `executionId` to keep Python globals, functions, imports, modules, and +guest objects in one interpreter. Set `createIfMissing: true` only on the first +operation. Structured `inputs` is replaced for every call. + + + +Only inline execute and evaluate calls retain interpreter memory. File, +module, install, Bash, and arbitrary-command operations use fresh processes. +`executions.reset()` clears retained memory and output without reverting the +filesystem; `executions.delete()` removes an idle execution. + +## Files, modules, and async Python + +Use `python.executeFile()` for an existing script and +`python.executeModule()` for the standard `python -m` workflow. Inline Python +supports top-level `await`, `async for`, and `async with`; awaited work is part +of the operation deadline. + + + +The API does not promise that unawaited `asyncio` tasks survive between +operations. Cancellation, timeout, reset, deletion, or VM disposal stops +execution-owned asynchronous work. + +## Install packages + +Call `python.install()` on `AgentOs`, or `install()` on the focused runtime. +Pass package specs for named installs, `requirementsFile` for a +requirements install, and `upgrade`, `indexUrl`, or `extraIndexUrls` for the +stable package workflows. + + + +Named packages and a requirements file cannot be combined in one call. +Installs modify the VM-wide filesystem and are visible to every execution in +that VM. AgentOS admits only one npm or Python package mutation at a time in a +VM; a concurrent install fails with `execution_busy` instead of risking shared +package state. Package downloads obey the VM network policy. + +## Detached work and output + +Set `detached: true` on execute, file, or module operations. Manage the returned +`executionId` with `executions.wait()`, `executions.cancel()`, +`executions.signal()`, stdin, PTY resize, reset, deletion, and bounded output +replay. Core output callbacks receive `Uint8Array`; actor events carry tagged +UTF-8/base64 data. Actor actions use the same nested method paths as Core and +RivetKit transports them with dotted wire names. + +## Filesystem, processes, and networking + +`pathlib`, `os`, and file objects use the VM [filesystem](/docs/filesystem). +`subprocess` starts kernel-managed guest commands. Python DNS, HTTP clients, and +outbound sockets use the VM network policy. See +[Processes & Shells](/docs/processes) and +[Networking & Previews](/docs/networking). + +## Custom bindings and policy + +Python can call [Custom Bindings](/docs/extensions/custom-bindings) as normal +commands through `subprocess`. + + + +All operations inherit [permissions](/docs/permissions) and +[resource limits](/docs/resource-limits). `timeoutMs` is an additional +operation wall-clock deadline covering staging, compilation, guest execution, +and result collection, while VM safety watchdogs remain independently bounded. diff --git a/website/src/content/docs/docs/browser.mdx b/website/src/content/docs/docs/extensions/browser.mdx similarity index 95% rename from website/src/content/docs/docs/browser.mdx rename to website/src/content/docs/docs/extensions/browser.mdx index 405fe83990..1a2dcd9c50 100644 --- a/website/src/content/docs/docs/browser.mdx +++ b/website/src/content/docs/docs/extensions/browser.mdx @@ -1,5 +1,5 @@ --- -title: "Browser" +title: "Browser Automation" description: "Let agents read and search the web from an agentOS VM using Browserbase's cloud browser through the browse CLI — no local browser or sandbox required." skill: true --- @@ -65,5 +65,5 @@ browse cloud projects list # list Browserbase projects ``` -The [interactive driver mode](https://docs.browserbase.com/integrations/skills/browse-cli) (`browse open`, `browse click`, …) is not supported inside the VM yet ([#1631](https://github.com/rivet-dev/agentos/issues/1631)). For interactive automation, run `browse` inside a sandbox via [Sandbox Mounting](/docs/sandbox). +The [interactive driver mode](https://docs.browserbase.com/integrations/skills/browse-cli) (`browse open`, `browse click`, …) is not supported inside the VM yet ([#1631](https://github.com/rivet-dev/agentos/issues/1631)). For interactive automation, run `browse` inside an external sandbox via [External Sandboxes](/docs/extensions/sandboxes). diff --git a/website/src/content/docs/docs/bindings.mdx b/website/src/content/docs/docs/extensions/custom-bindings.mdx similarity index 94% rename from website/src/content/docs/docs/bindings.mdx rename to website/src/content/docs/docs/extensions/custom-bindings.mdx index 2dc5e77c39..a69b18bc47 100644 --- a/website/src/content/docs/docs/bindings.mdx +++ b/website/src/content/docs/docs/extensions/custom-bindings.mdx @@ -1,5 +1,5 @@ --- -title: "Bindings" +title: "Custom Bindings" description: "Expose custom host functions to agents as CLI commands inside the VM." skill: true --- @@ -70,9 +70,9 @@ On failure (validation or execution error), the binding exits non-zero and write Missing required flag: --city ``` -## Bindings vs MCP servers +## Bindings and MCP servers -agentOS supports two ways to give agents access to external functionality: **bindings** and **MCP servers**. Both work, but they have different tradeoffs. +AgentOS supports two ways to give agents access to external functionality: **bindings** and session-scoped **MCP servers**. Both work, but they have different tradeoffs. | | Bindings | MCP Servers | |---|---|---| diff --git a/website/src/content/docs/docs/sandbox.mdx b/website/src/content/docs/docs/extensions/sandboxes.mdx similarity index 92% rename from website/src/content/docs/docs/sandbox.mdx rename to website/src/content/docs/docs/extensions/sandboxes.mdx index 29ceb9815e..b58a07b33a 100644 --- a/website/src/content/docs/docs/sandbox.mdx +++ b/website/src/content/docs/docs/extensions/sandboxes.mdx @@ -1,10 +1,10 @@ --- -title: "Sandbox Mounting" +title: "External Sandboxes" description: "Extend agentOS with full sandboxes for heavy workloads like browsers, desktop automation, and compilation." skill: true --- -For heavy workloads like browsers, desktop automation, and compilation, pair agentOS with a full sandbox on demand. Its filesystem mounts into the VM as a native directory, and its process management is exposed as [bindings](/docs/bindings), all provider-agnostic through [Sandbox Agent](https://sandboxagent.dev). +For heavy workloads like browsers, desktop automation, and compilation, pair agentOS with a full sandbox on demand. Its filesystem mounts into the VM as a native directory, and its process management is exposed as [custom bindings](/docs/extensions/custom-bindings), all provider-agnostic through [Sandbox Agent](https://sandboxagent.dev). ## Why use agentOS with a sandbox? @@ -27,7 +27,7 @@ Start with the default agentOS VM for all workloads, and only spin up a sandbox The sandbox integration ships as the `@rivet-dev/agentos-sandbox` package. It works through two mechanisms: - **Filesystem mount**: Projects the sandbox into the VM as a native directory, like mounting a hard drive on your own machine. Read and write files through the mount directly. -- **Bindings**: Exposes sandbox process management as [bindings](/docs/bindings). Execute commands on the sandbox from within the VM. +- **Bindings**: Exposes sandbox process management as [custom bindings](/docs/extensions/custom-bindings). Execute commands on the sandbox from within the VM. Both are powered by [Sandbox Agent](https://sandboxagent.dev), and you can swap providers without changing agent code. Install both packages: diff --git a/website/src/content/docs/docs/filesystem.mdx b/website/src/content/docs/docs/filesystem.mdx index 68c6c2f133..47d9235857 100644 --- a/website/src/content/docs/docs/filesystem.mdx +++ b/website/src/content/docs/docs/filesystem.mdx @@ -62,7 +62,7 @@ The actor's durable root is handled separately: the sidecar connects directly to ## File operations -These operations are primarily what the agent uses inside the VM, and are also available from the client to seed inputs and read results. For large or read-only inputs (a repo, a dataset), a read-only [host mount](#mounts) is faster than copying files in. Programs that need stdin or live output use exec instead (see [Core](/docs/core)). +These operations are primarily what the agent uses inside the VM, and are also available from the client to seed inputs and read results. For large or read-only inputs (a repo, a dataset), a read-only [host mount](#mounts) is faster than copying files in. Programs that need stdin or live output use exec instead (see the [Direct VM API](/docs/core)). ### Read and write @@ -103,7 +103,7 @@ See [Permissions](/docs/permissions) for the full configuration. ## Sandboxes -For heavier or untrusted workloads, run a full Linux [sandbox](/docs/sandbox) alongside the VM and mount its filesystem into agentOS. The agent then reads and writes the sandbox's files through the same `fs` APIs while the sandbox handles execution. See [Sandbox Mounting](/docs/sandbox) for setup. +For heavier workloads, run a full Linux [external sandbox](/docs/extensions/sandboxes) alongside the VM and mount its filesystem into agentOS. The agent then reads and writes the sandbox's files through the same `fs` APIs while the sandbox handles execution. ## Default layout diff --git a/website/src/content/docs/docs/js-runtime.mdx b/website/src/content/docs/docs/js-runtime.mdx deleted file mode 100644 index 1ac3bdb30a..0000000000 --- a/website/src/content/docs/docs/js-runtime.mdx +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: "JavaScript Runtime" -description: "How agentOS runs guest JavaScript: native V8 acceleration, low memory overhead, and Node.js compatibility." -skill: true ---- - -The JavaScript runtime is powered by the Rivet [Secure Exec](https://secureexec.dev) project, which provides the isolated V8 runtime that agentOS runs guest code in. Every guest VM executes its JavaScript inside this runtime, fully sandboxed from the host. - -## JavaScript Acceleration - -- **JavaScript is unusually slow as WebAssembly**: unlike most software, JavaScript pays a heavy penalty when compiled to WebAssembly, because so much engineering has gone into JIT-compiling JavaScript directly in V8. -- **Native V8, full JIT**: agentOS therefore runs guest JavaScript on the native V8 engine with its full JIT compiler, not through a WASM translation layer. We call this **JavaScript Acceleration**. -- **Native-speed execution**: guest JavaScript runs at native speed while staying inside the isolation boundary, with normal Node.js semantics. - -## Comparison to Node.js efficiency - -- **Isolate model, not processes**: agentOS runs each agent inside a V8 isolate rather than spawning a full Node.js process per agent. -- **Low memory overhead**: an isolate carries far less per-agent memory overhead than a full Node.js process, so many agents fit in the footprint that a process-per-agent model would spend on a handful. -- **Benchmarks**: see the Secure Exec [benchmarks](https://secureexec.dev/docs/benchmarks) for cold start, warm execution, and reuse measurements. - -## Node.js compatibility - -Guest code runs as Node.js (reporting `process.version` as `v22.0.0`), but it never touches the host runtime. Every `node:` builtin resolves to a kernel-backed bridge or an in-isolate polyfill, never the real host module. For the full builtin matrix (`fs`, `net`, `http`, `crypto`, undici-backed `fetch`, and more), see the Secure Exec [Node.js Compatibility](https://secureexec.dev/docs/nodejs-compatibility) reference. - -For the implementation-level event loop—how process-wide Tokio tasks notify a -thread-affine V8 isolate, how readiness is coalesced, and how `Duplex` -backpressure reaches a sidecar-owned socket—see -[JavaScript Executor & Socket Reactor](/docs/architecture/javascript-executor). - -### npm packages - -By default the VM has no npm packages installed. Mount a host `node_modules` directory to give guest code access to real packages: the `nodeModulesMount` helper projects it read-only at `/root/node_modules`, and the in-kernel resolver walks it exactly like Node.js does, with no bundling or patching. - - - -Resolution matches naive Node.js over the mounted tree: the ancestor `node_modules` walk, `package.json` `exports`/`imports` and conditions, and `realpath`/symlink following (so pnpm and yarn layouts resolve too). Both ESM `import` and CommonJS `require` work. See the Secure Exec [module loading](https://secureexec.dev/docs/features/module-loading) guide for the full model. diff --git a/website/src/content/docs/docs/limitations.mdx b/website/src/content/docs/docs/limitations.mdx index edf74a11a1..6c1e04512a 100644 --- a/website/src/content/docs/docs/limitations.mdx +++ b/website/src/content/docs/docs/limitations.mdx @@ -8,7 +8,7 @@ agentOS is a Linux environment with a POSIX-compliant virtual kernel. It handles ## Sandbox mounting -When a workload needs a full Linux OS, agents can escalate to a full sandbox on demand without changing code. The [sandbox mounting](/docs/sandbox) extension mounts the sandbox as a filesystem and lets you execute commands on it, like mounting a hard drive on your own machine. Files written in the VM are available in the sandbox and vice versa. +When a workload needs a full Linux OS, agents can escalate to a full sandbox on demand without changing code. The [external sandbox](/docs/extensions/sandboxes) extension mounts the sandbox as a filesystem and lets you execute commands on it, like mounting a hard drive on your own machine. Files written in the VM are available in the sandbox and vice versa. See [agentOS vs Sandbox](/docs/versus-sandbox) for a detailed comparison. @@ -16,7 +16,7 @@ See [agentOS vs Sandbox](/docs/versus-sandbox) for a detailed comparison. ### Software registry -agentOS uses its own [software registry](/registry) of popular tools cross-compiled for the runtime. You cannot download and install arbitrary binaries (for example via `curl` or `apt`), and standard Linux package managers (`apt`, `yum`) are not available since agentOS runs a streamlined Linux environment rather than a full distribution. Native binaries that are not yet available in the registry (such as Go, Rust, or C++ toolchains) require a full [sandbox](/docs/sandbox). +agentOS uses its own [software registry](/registry) of popular tools cross-compiled for the runtime. You cannot download and install arbitrary binaries (for example via `curl` or `apt`), and standard Linux package managers (`apt`, `yum`) are not available since agentOS runs a streamlined Linux environment rather than a full distribution. Native binaries that are not yet available in the registry (such as Go, Rust, or C++ toolchains) require a full [external sandbox](/docs/extensions/sandboxes). See [Software](/docs/software) for how to install and configure available packages. @@ -31,4 +31,3 @@ agentOS provides a POSIX-compliant virtual Linux kernel with full filesystem ope ### No hardware access The VM has no access to GPUs, USB devices, or other hardware. - diff --git a/website/src/content/docs/docs/llm-gateway.mdx b/website/src/content/docs/docs/llm-gateway.mdx deleted file mode 100644 index a000dfdb46..0000000000 --- a/website/src/content/docs/docs/llm-gateway.mdx +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: "Embedded LLM Gateway" -description: "Route, meter, and manage LLM API calls from agents." -skill: true ---- - -{/* TODO: This page is coming soon. */} - -The Embedded LLM Gateway runs as part of the agentOS library, not as an external service. It intercepts and manages all LLM API calls made by agents inside the VM. - -- **Unified routing** for all agent LLM requests -- **API keys stay on the server** so they are never exposed to agent code inside the VM -- **Usage metering** with per-session and per-agent breakdowns -- **Rate limiting** and cost controls - -Check back soon for full documentation. diff --git a/website/src/content/docs/docs/llm-credentials.mdx b/website/src/content/docs/docs/models-and-credentials.mdx similarity index 61% rename from website/src/content/docs/docs/llm-credentials.mdx rename to website/src/content/docs/docs/models-and-credentials.mdx index e5e1e925b8..1b5b914b74 100644 --- a/website/src/content/docs/docs/llm-credentials.mdx +++ b/website/src/content/docs/docs/models-and-credentials.mdx @@ -1,10 +1,12 @@ --- -title: "LLM Credentials" -description: "Pass LLM API keys to agent sessions securely." +title: "Models & Credentials" +description: "Choose agent models and pass provider credentials to sessions securely." skill: true --- -Pass LLM provider API keys to agent sessions so keys stay on the server and are injected at session creation, with per-tenant isolation for multi-tenant deployments. +Choose the model through your agent adapter, then pass its provider credentials +to the session from trusted server code. Credentials are injected at session +creation and can be isolated per tenant. ## Passing API keys @@ -26,6 +28,9 @@ Then resolve each tenant's key and pass it at session creation: Because keys are resolved per tenant from your own credential store (the `lookupTenantApiKey` stand-in above) and stay on the server, each session uses the tenant's own key and one tenant's key never reaches another tenant or the client. -## Embedded LLM Gateway +## Models -The [Embedded LLM Gateway](/docs/llm-gateway) (coming soon) will remove the need to manage API keys manually. It routes all agent LLM requests through a managed proxy built into agentOS, providing per-tenant usage metering, rate limiting, and cost controls without deploying a separate gateway service. +Model selection belongs to the configured agent adapter. AgentOS forwards the +session environment and preserves the agent's native model behavior instead of +introducing a second model-selection layer. See the page for your +[agent](/docs/agents/pi) for its supported model and provider options. diff --git a/website/src/content/docs/docs/nodejs-runtime.mdx b/website/src/content/docs/docs/nodejs-runtime.mdx deleted file mode 100644 index 67df0783cd..0000000000 --- a/website/src/content/docs/docs/nodejs-runtime.mdx +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: "Node.js Runtime" -description: "Run Node.js in agentOS: native V8 acceleration, the node CLI, installing packages, and Node.js compatibility." -skill: true ---- - -agentOS runs **Node.js** (`process.version` `v22.0.0`), fully isolated from the host. `node`, `npm`, and `npx` are on the `PATH`. - -## JavaScript Acceleration - -Normally, JavaScript running inside WebAssembly is exceptionally slow. In agentOS, JavaScript runs inside a native V8 isolate (powered by [Secure Exec](https://secureexec.dev)) for native runtime speeds: - -- **Native V8 speed, no overhead** — guest JS runs on V8's full JIT, not a WASM translation layer. -- **Lower memory than a Node.js process** — each agent is a V8 isolate, not a full process, so many fit where process-per-agent fits a handful. See [benchmarks](https://secureexec.dev/docs/benchmarks). - -## Running Node - -```ts -await agent.exec('node -e "console.log(1 + 1)"'); // inline -await agent.exec("node /workspace/main.js a b"); // script + argv -await agent.exec("npx tsx script.ts"); // npx -await agent.exec('echo "console.log(42)" | node'); // stdin -``` - -`node` works directly (`exec` / `execArgv` / `spawn`), through the guest shell (`sh -c`, pipes), and as a REPL. - -## Installing packages - -### Ahead of time - -Mount a host `node_modules` tree — projected read-only at `/root/node_modules` and resolved exactly like Node.js (ancestor walk, `package.json` `exports`/`imports`, symlinks — so pnpm/yarn layouts work), for both `import` and `require`: - -```ts -import { agentOS, setup, nodeModulesMount } from "@rivet-dev/agentos"; - -const vm = agentOS({ - mounts: [nodeModulesMount("/absolute/path/to/node_modules")], -}); -``` - -### At runtime - -Or install in the VM mid-task: - -```ts -await agent.exec("npm install chalk"); -await agent.exec("node /workspace/app.js"); // app.js: require("chalk") -``` - -## Node.js compatibility - -Guest code runs as Node.js v22, isolated from the host. `node:` builtins — `fs`, `net`, `http`, `crypto`, undici-backed `fetch`, and more — are provided by the runtime, never the host's. See the full [Node.js Compatibility](https://secureexec.dev/docs/nodejs-compatibility) matrix. diff --git a/website/src/content/docs/docs/benchmarks.mdx b/website/src/content/docs/docs/performance.mdx similarity index 88% rename from website/src/content/docs/docs/benchmarks.mdx rename to website/src/content/docs/docs/performance.mdx index bef9ba7ba3..8cc1e100f1 100644 --- a/website/src/content/docs/docs/benchmarks.mdx +++ b/website/src/content/docs/docs/performance.mdx @@ -1,10 +1,13 @@ --- -title: "Benchmarks" -description: "Performance benchmarks comparing agentOS to traditional sandbox providers." +title: "Performance" +description: "AgentOS latency, memory, cost, and benchmark methodology." skill: true --- -These are the benchmark figures shown on the agentOS marketing page. All numbers are computed from the same data source used by the marketing page. For independent sandbox comparison data, see the [ComputeSDK benchmarks](https://www.computesdk.com/benchmarks/). +These are the benchmark figures shown on the agentOS marketing page. All +numbers are computed from the same data source used by the marketing page. Use +the methodology below to measure the workload and hardware you will actually +deploy. ## Cold start @@ -70,6 +73,21 @@ Sandbox baseline: **Daytona** at $0.0504/vCPU-h + $0.0162/GiB-h with a 1 vCPU + | Hetzner ARM | $0.000000011/s | $0.000018/s | 1738x cheaper | | Hetzner x86 | $0.000000017/s | $0.000018/s | 1061x cheaper | +## Evaluating your own cost + +AgentOS runs on hardware you provision instead of billing per VM-second. To +estimate your workload, divide the instance's cost per second by the number of +concurrent VMs it can hold with realistic memory and utilization headroom. + +The result depends on workload size and lifecycle. A VM per task or tenant gives +the strongest isolation, while safely reusing a VM for trusted sequential work +amortizes boot and filesystem setup. Small, short executions benefit most from +dense packing; heavyweight, long-lived work depends more on hardware choice and +avoiding per-sandbox minimums. + +Provider prices, instance prices, and workload memory change. Measure +on your target hardware before quoting a cost ratio. + ## Test environment | Component | Details | diff --git a/website/src/content/docs/docs/python-runtime.mdx b/website/src/content/docs/docs/python-runtime.mdx deleted file mode 100644 index f0d82ef9bd..0000000000 --- a/website/src/content/docs/docs/python-runtime.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: "Python Runtime" -description: "Run Python in agentOS: the python CLI, installing packages ahead of time or at runtime, and what's supported." -skill: true ---- - -agentOS runs **CPython 3.13** as a first-class runtime. `python` and `python3` are on the `PATH` and plug into the VM's filesystem, processes, and network — agents use them like any other command. - -## Running Python - -```ts -await agent.exec('python -c "print(1 + 1)"'); // inline -await agent.exec("python /workspace/main.py a b"); // script + sys.argv -await agent.exec("python -m http.server 8000"); // module -await agent.exec('echo "print(40 + 2)" | python -'); // stdin -``` - -`python` works directly (`exec` / `execArgv` / `spawn`), through the guest shell (`sh -c`, pipes), and as an interactive REPL. - -## Installing packages - -`pip install` writes to a persistent spot on the VM filesystem, so a package installed once is importable by every later `python` run in that VM. - -### Ahead of time - -Install once during setup so the agent starts ready — no install cost mid-task: - -```ts -// one-off setup pass on the VM, before handing it to the agent -await agent.exec("pip install requests pandas"); -// requests + pandas now import in every python run on this VM -``` - -### At runtime - -Or let the agent install what it needs, mid-task: - -```ts -await agent.exec("pip install rich"); -await agent.exec('python -c "import rich; print(rich.__version__)"'); -``` - -`pip install ` and `python -m pip install ` both work; downloads obey the VM's network policy (default-deny + allowlist). - -## Compatibility - -CPython 3.13 and the standard library, with a few VM-shaped differences. - -### Supported - -- Full VM filesystem (`/tmp`, `/etc`, `/root`, …) — shared with other processes and `readFile()` -- Reading, writing, creating, deleting, and renaming files anywhere on the VM, plus symlinks and file metadata (`os.symlink` / `os.readlink` / `os.chmod` / `os.chown` / `os.utime`) -- A real process in the tree: stdin/stdout/stderr, signals, `kill` -- `subprocess` launching other VM commands (`node`, etc.) -- Pure-Python packages, plus native packages with a prebuilt wheel — `numpy`, `pandas`, `scipy`, `scikit-learn`, `pydantic`, `cryptography`, `Pillow`, and [many more](https://pyodide.org/en/stable/usage/packages-in-pyodide.html) -- `requests`, `urllib`, and `pip` over HTTP/DNS, under the VM network policy -- Outbound raw TCP and UDP sockets (the `socket` module), under the VM network policy - -### Unsupported - -- OS threads and `multiprocessing` — the runtime is single-threaded -- `os.fork` / `os.exec` -- Some packages with native (C/Rust) extensions — see the [full list of supported packages](https://pyodide.org/en/stable/usage/packages-in-pyodide.html) -- Socket servers / listeners (`bind`/`listen`/`accept`) — outbound connections only diff --git a/website/src/content/docs/docs/quickstart.mdx b/website/src/content/docs/docs/quickstart.mdx index c5dd0e4dee..0c1d5989a4 100644 --- a/website/src/content/docs/docs/quickstart.mdx +++ b/website/src/content/docs/docs/quickstart.mdx @@ -104,7 +104,7 @@ import { AGENT_PROMPT } from '../../../components/marketing/agentPrompt'; - **[Software](/docs/software)** — Install software packages inside the VM - **[Filesystem](/docs/filesystem)** — Read, write, and manage files inside the VM - **[Permissions & Resource Limits](/docs/permissions)** — Gate what the agent can do and cap its resource usage - - **[Bindings](/docs/bindings)** — Expose your JavaScript functions to agents as CLI commands + - **[Custom Bindings](/docs/extensions/custom-bindings)** — Expose your JavaScript functions to agents as CLI commands 5. **Deploy** @@ -112,7 +112,7 @@ import { AGENT_PROMPT } from '../../../components/marketing/agentPrompt'; - See [Deployment](/docs/deployment) for managed, self-hosted, and agentOS Core options. + See [Deployment](/docs/deployment) for managed, self-hosted, and direct VM options. @@ -120,8 +120,9 @@ import { AGENT_PROMPT } from '../../../components/marketing/agentPrompt'; agentOS is in preview and the API is subject to change. If you run into issues, please [report them on GitHub](https://github.com/rivet-dev/rivet/issues) or [join our Discord](https://rivet.dev/discord). -## agentOS Core +## Direct VM API -The quickstart above uses `@rivet-dev/agentos`, which includes statefulness, multiplayer, and orchestration out of the box. If you only need direct VM control without those features, you can use the core package (`@rivet-dev/agentos-core`) standalone. +The same `@rivet-dev/agentos` package also exports `AgentOs.create()` for direct +VM control without the actor lifecycle, multiplayer, or orchestration layer. -See [agentOS core documentation](/docs/core) for reference. +See the [direct VM API documentation](/docs/core) for reference. diff --git a/website/src/content/docs/docs/security-model.mdx b/website/src/content/docs/docs/security-model.mdx index ef8b9424f9..182cf603b6 100644 --- a/website/src/content/docs/docs/security-model.mdx +++ b/website/src/content/docs/docs/security-model.mdx @@ -123,7 +123,7 @@ Every guest syscall is mediated by the kernel and checked against the runtime's - **Processes.** `node:child_process` spawns kernel-managed guest processes, never real host processes. Children can only run the commands the VM mounts (WASM-backed `sh` and coreutils, V8-backed `node`). See [Processes](/docs/processes). - **Network.** Guest `fetch()`, `node:http`, and raw sockets all flow through the kernel socket table. Guest `fetch()` runs through undici inside the isolate and then through the kernel socket table; it never opens a real host socket. See [Networking](/docs/networking). - **DNS, pipes, and PTYs** are likewise kernel-owned: no guest path reaches the host directly. -- **Bindings.** Registered [bindings](/docs/bindings) are the only sanctioned way to hand the guest a named host capability. The guest invokes a binding by name with JSON input, the call round-trips to the host handler, and only the handler's return value comes back. The guest never receives the underlying host access. +- **Bindings.** Registered [custom bindings](/docs/extensions/custom-bindings) are the only sanctioned way to hand the guest a named host capability. The guest invokes a binding by name with JSON input, the call round-trips to the host handler, and only the handler's return value comes back. The guest never receives the underlying host access. ## What enters the VM @@ -172,7 +172,7 @@ The boundary protects the host from the guest. It does **not** harden your host - Hardening the host process and deployment environment. For internet-facing workloads that take untrusted input, run your host inside an already-hardened environment (for example AWS Lambda, Google Cloud Run, or a similar sandboxed platform). - Validating authentication tokens in `onBeforeConnect`. - Scoping [permissions](/docs/permissions) appropriately for your use case. -- Managing API keys and secrets on the host side (use the [LLM gateway](/docs/llm-gateway) to avoid passing keys into the VM). +- Managing API keys and secrets on the host side (see [Models & Credentials](/docs/models-and-credentials)). - Configuring [resource limits and network controls](/docs/security-model) to match your threat model. - Choosing your blast radius: prefer a fresh VM per untrusted or high-risk task so an escape attempt cannot outlive a single VM. diff --git a/website/src/content/docs/docs/sessions.mdx b/website/src/content/docs/docs/sessions.mdx index 78fdea2012..9f95b941ef 100644 --- a/website/src/content/docs/docs/sessions.mdx +++ b/website/src/content/docs/docs/sessions.mdx @@ -17,6 +17,20 @@ AgentOS sessions are durable records backed by the VM's SQLite database. The pub The input supports `agent`, `cwd`, `additionalDirectories`, `env`, `mcpServers`, `permissionPolicy`, `skipOsInstructions`, and `additionalInstructions`. Omitted `cwd` defaults to `/home/agentos` in the sidecar. Actor deployments inject their SQLite UDS database automatically. Standalone core clients must configure a VM SQLite file or UDS descriptor. +## MCP servers + +MCP configuration belongs to the session because its tools are part of the +agent's runtime context. Configure local child-process servers or remote servers +before opening the session. The exact config path and supported transports come +from the selected agent adapter; for example, Pi reads `.mcp.json` from its +AgentOS home directory: + + + +Install local MCP server packages before opening the session so first-run +package-manager output cannot corrupt a stdio handshake. See the selected +[agent guide](/docs/agents/pi) for adapter-specific configuration. + ## Prompt `prompt` accepts native ACP `ContentBlock[]`, not a special AgentOS text format. It never creates a missing session. AgentOS commits the complete user message before dispatching it and never automatically replays a prompt whose delivery may have reached the adapter. @@ -61,7 +75,7 @@ ACP itself does not define a portable history-reading API, and adapters implemen `permissionPolicy` is `reject_all`, `ask`, or `allow_all`, and defaults to `allow_all`. It controls how AgentOS answers native ACP permission requests; it does not configure VM permissions or adapter tool access. Set `permissionPolicy: "ask"` when opening the session before subscribing for interactive decisions; subscribing alone does not change the immutable policy. With `ask`, AgentOS durably records the native ACP `RequestPermissionRequest` as a `permission_request` variant in the generic session-event stream. With the default `allow_all`, AgentOS resolves the adapter request automatically and emits no permission event. Reply to an `ask` request with the exact adapter-supplied `optionId` and an explicit public session ID: ```ts -await agent.respondPermission({ +await agent.sessions.respondPermission({ sessionId: request.sessionId, requestId: request.requestId, optionId: request.options[0].optionId, diff --git a/website/src/content/docs/docs/software.mdx b/website/src/content/docs/docs/software.mdx index 2d8f1c1aec..a991eb80c7 100644 --- a/website/src/content/docs/docs/software.mdx +++ b/website/src/content/docs/docs/software.mdx @@ -30,4 +30,4 @@ Browse all available software packages on the [Registry](/registry). ## Custom Software -Package your own agents, command packages, and WASM commands. See [Software Definition](/docs/custom-software/definition) to define a package, and [Building Binaries](/docs/custom-software/building-wasm) to compile WASM commands from source in the [secure-exec registry](https://github.com/rivet-dev/secure-exec/tree/main/registry). +Package your own agents, command packages, and WASM commands. See [Software Definition](/docs/custom-software/definition) to define a package, and [Building Binaries](/docs/custom-software/building-wasm) to compile WASM commands from source in the [agentos registry](https://github.com/rivet-dev/agentos/tree/main/registry). diff --git a/website/src/content/docs/docs/versus-sandbox.mdx b/website/src/content/docs/docs/versus-sandbox.mdx index ace6b3a6c3..3aa505a06c 100644 --- a/website/src/content/docs/docs/versus-sandbox.mdx +++ b/website/src/content/docs/docs/versus-sandbox.mdx @@ -4,9 +4,9 @@ description: "When to use the lightweight agentOS VM, a full sandbox, or both to skill: true --- -- **agentOS** is a lightweight VM that runs inside your process. Near-zero cold start, low memory, direct backend integration via [bindings](/docs/bindings). +- **agentOS** is a lightweight VM that runs inside your process. Near-zero cold start, low memory, direct backend integration via [custom bindings](/docs/extensions/custom-bindings). - **Sandboxes** are full Linux environments with root access, system packages, and native binary support. -- **You can use both.** agentOS works with sandboxes through [sandbox mounting](/docs/sandbox). Agents run in the lightweight VM by default and spin up a full sandbox on demand. +- **You can use both.** agentOS works with sandboxes through the [external sandbox extension](/docs/extensions/sandboxes). Agents run in the lightweight VM by default and spin up a full sandbox on demand. ## Comparison @@ -14,8 +14,8 @@ skill: true |---|---|---| | **Cost** | Very low. Runs in your process. | Pay per second of uptime. | | **Startup** | Near-zero cold start (~6 ms). | Seconds to spin up. | -| **Backend integration** | Direct. [Bindings](/docs/bindings) call your functions with zero latency. | Indirect. Requires network calls back to your backend. | -| **Credentials** | Stay on the host. [Bindings](/docs/bindings) run your functions server-side; agents see only inputs and outputs. | Must be injected into the sandbox environment. | +| **Backend integration** | Direct. [Custom bindings](/docs/extensions/custom-bindings) call your functions with zero latency. | Indirect. Requires network calls back to your backend. | +| **Credentials** | Stay on the host. [Custom bindings](/docs/extensions/custom-bindings) run your functions server-side; agents see only inputs and outputs. | Must be injected into the sandbox environment. | | **Permissions** | Granular, deny-by-default. | Coarse-grained (container-level). | | **Infrastructure** | `npm install` | Vendor account + API keys. | | **Best for** | Coding, file manipulation, scripting, API calls, orchestration. | Browsers, desktop automation, native compilation, dev servers. | @@ -43,7 +43,7 @@ Spin up a sandbox when the workload needs a real Linux kernel: ### Both together -Use agentOS with [sandbox mounting](/docs/sandbox) for workflows that need both: +Use agentOS with [external sandboxes](/docs/extensions/sandboxes) for workflows that need both: - Agent runs in the agentOS VM with full access to bindings and permissions - Sandbox spins up on demand for heavy tasks diff --git a/website/src/content/docs/docs/webhooks.mdx b/website/src/content/docs/docs/webhooks.mdx deleted file mode 100644 index bfd4217e9e..0000000000 --- a/website/src/content/docs/docs/webhooks.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: "Webhooks" -description: "Trigger agent sessions directly from external webhooks using Hono." -skill: true ---- - -Use a lightweight HTTP server to receive webhooks and drive an agent. This example uses [Hono](https://hono.dev) to receive Slack webhooks and call an agent directly. - -## Example: Slack webhook to agent - - - - - -## How it works - -1. Slack sends an HTTP POST to `/slack/events` -2. The Hono handler validates the event and prompts one durable AgentOS session -3. AgentOS automatically serializes concurrent prompts targeting that session -4. The handler posts the response back to Slack - -No application queue is needed for prompt ordering. Durable session history remains in SQLite, but AgentOS never automatically replays a prompt whose delivery was uncertain. - -## Recommendations - -- The direct example waits for the turn. If the provider requires an immediate `200`, use the hosting platform's durable background-task primitive and call AgentOS from that task. -- Store webhook secrets in environment variables, not in code. diff --git a/website/src/content/docs/docs/workflows.mdx b/website/src/content/docs/docs/workflows.mdx index d7886f21b0..af17ac3a0d 100644 --- a/website/src/content/docs/docs/workflows.mdx +++ b/website/src/content/docs/docs/workflows.mdx @@ -1,5 +1,5 @@ --- -title: "Workflow Automation" +title: "Workflows & Graphs" description: "Orchestrate multi-step agent tasks with durable workflows." skill: true --- diff --git a/website/src/data/docs-landings.ts b/website/src/data/docs-landings.ts index 8f1b64639d..521db5bb4f 100644 --- a/website/src/data/docs-landings.ts +++ b/website/src/data/docs-landings.ts @@ -15,7 +15,6 @@ import { faHardDrive, faNodeJs, faGauge, - faLink, faTowerBroadcast, faArrowsLeftRight, faDiagramNext, @@ -67,6 +66,15 @@ export const docsLandings: Record = { { title: "Agent to Agent", href: "/cookbooks/agent-to-agent", icon: faArrowsLeftRight, description: "Agents calling agents." }, ], }, + { + title: "Code Execution", + items: [ + { title: "AI Agent Code Exec", href: "/cookbooks/js-ai-agent-code-exec", icon: faRobot, description: "Execute generated code with explicit limits." }, + { title: "Code Mode", href: "/cookbooks/js-code-mode", icon: faTerminal, description: "Let generated code orchestrate narrow bindings." }, + { title: "Dev Servers", href: "/cookbooks/js-dev-servers", icon: faGlobe, description: "Run and drive a long-lived guest server." }, + { title: "Plugin Systems", href: "/cookbooks/js-plugin-systems", icon: faWrench, description: "Evaluate plugins inside a restricted VM." }, + ], + }, { title: "Sessions & Permissions", items: [ @@ -83,7 +91,6 @@ export const docsLandings: Record = { items: [ { title: "Cron", href: "/cookbooks/cron", icon: faClock, description: "Scheduled jobs." }, { title: "Workflows", href: "/cookbooks/workflows", icon: faDiagramNext, description: "Durable workflows." }, - { title: "Webhooks", href: "/cookbooks/webhooks", icon: faLink, description: "Webhook handling." }, ], }, { diff --git a/website/src/data/registry.ts b/website/src/data/registry.ts index 60d7eaec10..10c6b60439 100644 --- a/website/src/data/registry.ts +++ b/website/src/data/registry.ts @@ -242,7 +242,7 @@ export const registry = setup({ use: { vm } });`, "Mount a sandbox filesystem and expose process management bindings. Works with any Sandbox Agent provider.", types: ["binding", "file-system"], icon: "Monitor", - docsHref: "/docs/sandbox", + docsHref: "/docs/extensions/sandboxes", }, // Sandbox Mounting { diff --git a/website/src/generated/registry.json b/website/src/generated/registry.json index 4b656488e9..52f204a117 100644 --- a/website/src/generated/registry.json +++ b/website/src/generated/registry.json @@ -1,67 +1,5 @@ { "entries": [ - { - "slug": "pi", - "title": "PI", - "description": "Run the PI coding agent with lightweight, fast execution.", - "types": [ - "agent" - ], - "category": "agents", - "priority": 100, - "package": "@agentos-software/pi", - "status": "available", - "image": "/images/registry/pi.svg", - "docsHref": "/docs/agents/pi", - "agentId": "pi" - }, - { - "slug": "claude-code", - "title": "Claude Code", - "description": "Run Claude Code as an agentOS agent with full tool access, file editing, and shell execution.", - "types": [ - "agent" - ], - "category": "agents", - "priority": 90, - "package": "@agentos-software/claude-code", - "status": "docs", - "beta": true, - "image": "/images/registry/claude-code.svg", - "docsHref": "/docs/agents/claude", - "agentId": "claude" - }, - { - "slug": "codex", - "title": "Codex", - "description": "Run OpenAI's Codex coding agent inside agentOS with programmatic API access.", - "types": [ - "agent" - ], - "category": "agents", - "priority": 80, - "package": "@agentos-software/codex", - "status": "docs", - "beta": true, - "image": "/images/registry/codex.svg", - "docsHref": "/docs/agents/codex", - "agentId": "codex" - }, - { - "slug": "opencode", - "title": "OpenCode", - "description": "Run OpenCode, an open-source coding agent, inside agentOS.", - "types": [ - "agent" - ], - "category": "agents", - "priority": 70, - "package": "@agentos-software/opencode", - "status": "docs", - "image": "/images/registry/opencode.svg", - "docsHref": "/docs/agents/opencode", - "agentId": "opencode" - }, { "slug": "browserbase", "title": "Browserbase", @@ -100,6 +38,18 @@ "package": "@agentos-software/build-essential", "status": "available" }, + { + "slug": "pi", + "title": "PI", + "description": "Run the PI coding agent with lightweight, fast execution.", + "types": [ + "software" + ], + "priority": 100, + "package": "@agentos-software/pi", + "status": "available", + "image": "/images/registry/pi.svg" + }, { "slug": "common", "title": "Common", @@ -112,6 +62,20 @@ "package": "@agentos-software/common", "status": "available" }, + { + "slug": "claude-code", + "title": "Claude Code", + "description": "Run Claude Code as an agentOS agent with full tool access, file editing, and shell execution.", + "types": [ + "software" + ], + "priority": 90, + "package": "@agentos-software/claude-code", + "status": "docs", + "beta": true, + "image": "/images/registry/claude-code.svg", + "docsHref": "/docs/agents/claude" + }, { "slug": "git", "title": "git", @@ -137,6 +101,20 @@ "package": "@agentos-software/ripgrep", "status": "available" }, + { + "slug": "codex", + "title": "Codex", + "description": "Run OpenAI's Codex coding agent inside agentOS with programmatic API access.", + "types": [ + "software" + ], + "priority": 80, + "package": "@agentos-software/codex", + "status": "docs", + "beta": true, + "image": "/images/registry/codex.svg", + "docsHref": "/docs/agents/codex" + }, { "slug": "jq", "title": "jq", @@ -176,6 +154,19 @@ "status": "available", "image": "/images/registry/duckdb.svg" }, + { + "slug": "opencode", + "title": "OpenCode", + "description": "Run OpenCode, an open-source coding agent, inside agentOS.", + "types": [ + "software" + ], + "priority": 70, + "package": "@agentos-software/opencode", + "status": "docs", + "image": "/images/registry/opencode.svg", + "docsHref": "/docs/agents/opencode" + }, { "slug": "vim", "title": "vim", diff --git a/website/src/generated/routes.json b/website/src/generated/routes.json index 98183e3340..e6b11961f0 100644 --- a/website/src/generated/routes.json +++ b/website/src/generated/routes.json @@ -16,32 +16,16 @@ "title": "Authentication", "description": "Authenticate connections to agentOS actors using Rivet Actor connection params and hooks." }, - "/docs/docs/benchmarks": { - "title": "Benchmarks", - "description": "Performance benchmarks comparing agentOS to traditional sandbox providers." - }, - "/docs/docs/bindings": { - "title": "Bindings", - "description": "Expose custom host functions to agents as CLI commands inside the VM." - }, - "/docs/docs/browser": { - "title": "Browser", - "description": "Let agents read and search the web from an agentOS VM using Browserbase's cloud browser through the browse CLI — no local browser or sandbox required." - }, "/docs/docs/core": { - "title": "Core Package", - "description": "Use @rivet-dev/agentos-core standalone for direct VM control without the Rivet Actor runtime." - }, - "/docs/docs/cost-evaluation": { - "title": "Cost Evaluation", - "description": "How agentOS compares on cost to per-second sandbox providers when you run coding-agent VMs on your own hardware." + "title": "Direct VM API", + "description": "Use the direct AgentOS VM API or layer it into an AgentOS actor." }, "/docs/docs/crash-course": { "title": "Crash Course", "description": "Run coding agents inside isolated VMs with full filesystem, process, and network control." }, "/docs/docs/cron": { - "title": "Cron Jobs", + "title": "Crons & Loops", "description": "Schedule recurring commands and agent sessions in agentOS VMs." }, "/docs/docs/debugging": { @@ -60,21 +44,13 @@ "title": "Introduction", "description": "Run coding agents inside isolated VMs with full filesystem, process, and network control." }, - "/docs/docs/js-runtime": { - "title": "JavaScript Runtime", - "description": "How agentOS runs guest JavaScript: native V8 acceleration, low memory overhead, and Node.js compatibility." - }, "/docs/docs/limitations": { "title": "Limitations", "description": "What the agentOS VM does not support, and how to work around it." }, - "/docs/docs/llm-credentials": { - "title": "LLM Credentials", - "description": "Pass LLM API keys to agent sessions securely." - }, - "/docs/docs/llm-gateway": { - "title": "Embedded LLM Gateway", - "description": "Route, meter, and manage LLM API calls from agents." + "/docs/docs/models-and-credentials": { + "title": "Models & Credentials", + "description": "Choose agent models and pass provider credentials to sessions securely." }, "/docs/docs/multiplayer": { "title": "Multiplayer", @@ -84,9 +60,9 @@ "title": "Networking & Previews", "description": "Proxy HTTP requests into agentOS VMs and create shareable preview URLs." }, - "/docs/docs/nodejs-runtime": { - "title": "Node.js Runtime", - "description": "Run Node.js in agentOS: native V8 acceleration, the node CLI, installing packages, and Node.js compatibility." + "/docs/docs/performance": { + "title": "Performance", + "description": "AgentOS latency, memory, cost, and benchmark methodology." }, "/docs/docs/permissions": { "title": "Permissions", @@ -100,10 +76,6 @@ "title": "Processes & Shell", "description": "Execute commands, spawn long-running processes, and open interactive shells in agentOS VMs." }, - "/docs/docs/python-runtime": { - "title": "Python Runtime", - "description": "Run Python in agentOS: the python CLI, installing packages ahead of time or at runtime, and what's supported." - }, "/docs/docs/quickstart": { "title": "Quickstart", "description": "Set up an agentOS actor, create a session, and run your first coding agent." @@ -112,10 +84,6 @@ "title": "Resource Limits", "description": "Cap per-VM resources, JavaScript CPU/wall-clock time, Python execution, and WASM runtime work so guest code can never exhaust the host." }, - "/docs/docs/sandbox": { - "title": "Sandbox Mounting", - "description": "Extend agentOS with full sandboxes for heavy workloads like browsers, desktop automation, and compilation." - }, "/docs/docs/security-model": { "title": "Security Model", "description": "Trust boundaries, isolation guarantees, and the agentOS threat model." @@ -136,12 +104,8 @@ "title": "agentOS vs Sandbox", "description": "When to use the lightweight agentOS VM, a full sandbox, or both together." }, - "/docs/docs/webhooks": { - "title": "Webhooks", - "description": "Trigger agent sessions directly from external webhooks using Hono." - }, "/docs/docs/workflows": { - "title": "Workflow Automation", + "title": "Workflows & Graphs", "description": "Orchestrate multi-step agent tasks with durable workflows." }, "/docs/docs/agents/claude": { @@ -223,6 +187,34 @@ "/docs/docs/custom-software/publishing": { "title": "Publishing Packages", "description": "Build, publish, and consume agentOS packages — locally, from npm, or from your own repo." + }, + "/docs/docs/execution/bash": { + "title": "Bash", + "description": "Run shell commands and arbitrary argv operations through the AgentOS execution lifecycle." + }, + "/docs/docs/execution/javascript-compatibility": { + "title": "Node.js Compatibility", + "description": "Node.js builtins available to JavaScript running inside AgentOS." + }, + "/docs/docs/execution/javascript": { + "title": "JavaScript", + "description": "Execute JavaScript and TypeScript, install npm dependencies, and manage execution lifecycles in AgentOS." + }, + "/docs/docs/execution/python": { + "title": "Python", + "description": "Execute Python source, files, modules, and package workflows in AgentOS." + }, + "/docs/docs/extensions/browser": { + "title": "Browser Automation", + "description": "Let agents read and search the web from an agentOS VM using Browserbase's cloud browser through the browse CLI — no local browser or sandbox required." + }, + "/docs/docs/extensions/custom-bindings": { + "title": "Custom Bindings", + "description": "Expose custom host functions to agents as CLI commands inside the VM." + }, + "/docs/docs/extensions/sandboxes": { + "title": "External Sandboxes", + "description": "Extend agentOS with full sandboxes for heavy workloads like browsers, desktop automation, and compilation." } } } \ No newline at end of file diff --git a/website/src/pages/[...slug].astro b/website/src/pages/[...slug].astro index 8793ad083e..1985d3eaed 100644 --- a/website/src/pages/[...slug].astro +++ b/website/src/pages/[...slug].astro @@ -15,13 +15,54 @@ import config from "virtual:rivet-docs/config"; export async function getStaticPaths() { const docs = await getCollection("docs"); - return docs.map((entry) => ({ - params: { slug: entry.id }, - props: { entry }, - })); + const legacyRedirects = { + "docs/exec": "/docs/execution/javascript", + "docs/exec/crash-course": "/docs/crash-course", + "docs/exec/architecture": "/docs/architecture", + "docs/exec/security-model": "/docs/security-model", + "docs/exec/benchmarks": "/docs/performance", + "docs/exec/getting-started/javascript": "/docs/execution/javascript", + "docs/exec/getting-started/nodejs": "/docs/execution/javascript", + "docs/exec/getting-started/python": "/docs/execution/python", + "docs/exec/nodejs-compatibility": "/docs/execution/javascript-compatibility", + "docs/exec/python": "/docs/execution/python", + "docs/exec/python/packages": "/docs/execution/python", + "docs/exec/features/typescript": "/docs/execution/javascript", + "docs/exec/features/module-loading": "/docs/execution/javascript", + "docs/exec/features/runtime-platform": "/docs/execution/javascript", + "docs/exec/features/executing-code": "/docs/execution/javascript", + "docs/exec/features/output-capture": "/docs/processes", + "docs/exec/features/long-running-processes": "/docs/processes", + "docs/exec/features/child-processes": "/docs/processes", + "docs/exec/features/bindings": "/docs/extensions/custom-bindings", + "docs/exec/features/permissions": "/docs/permissions", + "docs/exec/features/filesystem": "/docs/filesystem", + "docs/exec/features/networking": "/docs/networking", + "docs/exec/features/resource-limits": "/docs/resource-limits", + "docs/js-runtime": "/docs/execution/javascript", + "docs/nodejs-runtime": "/docs/execution/javascript", + "docs/python-runtime": "/docs/execution/python", + "docs/bindings": "/docs/extensions/custom-bindings", + "docs/browser": "/docs/extensions/browser", + "docs/sandbox": "/docs/extensions/sandboxes", + "docs/llm-credentials": "/docs/models-and-credentials", + "docs/benchmarks": "/docs/performance", + "docs/cost-evaluation": "/docs/performance", + } as const; + return [ + ...docs.map((entry) => ({ + params: { slug: entry.id }, + props: { entry }, + })), + ...Object.entries(legacyRedirects).map(([slug, redirectTo]) => ({ + params: { slug }, + props: { redirectTo }, + })), + ]; } -const { entry } = Astro.props; +const { entry, redirectTo } = Astro.props; +if (redirectTo) return Astro.redirect(redirectTo, 301); const { Content, headings } = await render(entry); const { title, description } = entry.data as { title: string; description?: string }; diff --git a/website/src/pages/registry/[slug].astro b/website/src/pages/registry/[slug].astro index 9908f22062..2c07c22965 100644 --- a/website/src/pages/registry/[slug].astro +++ b/website/src/pages/registry/[slug].astro @@ -68,7 +68,7 @@ const npmHref = packageName // fall back to the relevant documentation page for their type. const docsHref = entry.docsHref ?? - (isSoftware ? "/docs/software" : isSandboxExt ? "/docs/sandbox" : null); + (isSoftware ? "/docs/software" : isSandboxExt ? "/docs/extensions/sandboxes" : null); const configId = entry.status === "config" ? entry.configId : null; const agentId = entry.status === "docs" ? (entry.agentId ?? null) : null; diff --git a/website/tsconfig.typedoc.json b/website/tsconfig.typedoc.json new file mode 100644 index 0000000000..2a466f7863 --- /dev/null +++ b/website/tsconfig.typedoc.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "jsx": "react", + "noEmit": true + }, + "include": [ + "../packages/agentos/src/**/*", + "../packages/javascript/src/**/*", + "../packages/python/src/**/*" + ] +} diff --git a/website/typedoc.json b/website/typedoc.json index 9277eb9f8b..f1b245f8c6 100644 --- a/website/typedoc.json +++ b/website/typedoc.json @@ -1,7 +1,11 @@ { "$schema": "https://typedoc.org/schema.json", - "entryPoints": ["../packages/agentos/src/index.ts"], - "tsconfig": "../packages/agentos/tsconfig.json", + "entryPoints": [ + "../packages/agentos/src/index.ts", + "../packages/javascript/src/index.ts", + "../packages/python/src/index.ts" + ], + "tsconfig": "./tsconfig.typedoc.json", "out": "public/api", "name": "agentOS API Reference", "includeVersion": true,

%Jq1%BJFmF~Xox&)(E?JJaN&ey)>yF1a zzduKuQWl?-yJkk>$Nu~w)dlNvj_fxa?t-`m`!U7H;-O(h3WV_?CG-ErZvi}f@h52{ zjC?)oY}gw&WD1h6y=674ru_%15(CNHHY!m-aW(N78oaPg h?_pY?}%b*@57D9!d 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 z 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

q7=5mYeEvMnFZpQ4Y|ni;N* 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 diff --git a/website/public/secure-exec-logo.svg b/website/public/secure-exec-logo.svg deleted file mode 100644 index d7119e8854..0000000000 --- a/website/public/secure-exec-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/website/src/components/GitHubStars.tsx b/website/src/components/GitHubStars.tsx index afe86190f3..b8b7a24459 100644 --- a/website/src/components/GitHubStars.tsx +++ b/website/src/components/GitHubStars.tsx @@ -13,7 +13,7 @@ function formatNumber(num: number): string { return num.toString(); } -export function GitHubStars({ repo = "rivet-dev/secure-exec", className, ...props }: GitHubStarsProps) { +export function GitHubStars({ repo = "rivet-dev/agentos", className, ...props }: GitHubStarsProps) { const [stars, setStars] = useState(null); useEffect(() => { diff --git a/website/src/components/marketing/diagrams/AgentSessionDemo.tsx b/website/src/components/marketing/diagrams/AgentSessionDemo.tsx index a1cf3708fe..3a89b545fe 100644 --- a/website/src/components/marketing/diagrams/AgentSessionDemo.tsx +++ b/website/src/components/marketing/diagrams/AgentSessionDemo.tsx @@ -202,7 +202,7 @@ const TABS: SessionTab[] = [ key: 'nodejs', title: 'Node.js', description: 'Node v22 compatible, running on WASM. node, npm, and npx on the PATH.', - docsHref: '/docs/nodejs-runtime', + docsHref: '/docs/execution/javascript', docsLabel: 'Node.js runtime docs', iconSrc: '/images/registry/nodejs.svg', script: { fileName: 'report.mjs', lang: 'js', code: REPORT_JS }, @@ -213,7 +213,7 @@ const TABS: SessionTab[] = [ key: 'python', title: 'Python', description: 'CPython 3.13 with pip. Native wheels like numpy and pandas work.', - docsHref: '/docs/python-runtime', + docsHref: '/docs/execution/python', docsLabel: 'Python runtime docs', iconSrc: 'https://upload.wikimedia.org/wikipedia/commons/thumb/3/31/Python-logo.png/120px-Python-logo.png', script: { fileName: 'report.py', lang: 'python', code: REPORT_PY }, diff --git a/website/src/components/marketing/solutions/AgentOSPage.tsx b/website/src/components/marketing/solutions/AgentOSPage.tsx index 5c659ac531..d841b1a31e 100644 --- a/website/src/components/marketing/solutions/AgentOSPage.tsx +++ b/website/src/components/marketing/solutions/AgentOSPage.tsx @@ -1130,13 +1130,6 @@ const ExecutionSection = () => ( - {/* agentOS Exec callout intentionally hidden until the standalone execution product is ready. */} - {/* - - */}
@@ -1940,7 +1933,7 @@ function RuntimeBenchInfo({ {detail.label} {detail.text} ))} - + Methodology & reproduction → @@ -2176,7 +2169,7 @@ const secondaryFeatures = [ icon: Wrench, title: 'Bindings', description: 'Expose typed JavaScript functions as CLI tools while credentials remain on the host.', - docsHref: '/docs/bindings', + docsHref: '/docs/extensions/custom-bindings', }, { icon: ShieldCheck, @@ -2188,13 +2181,13 @@ const secondaryFeatures = [ icon: Package, title: 'Sandbox mounting', description: 'Mount a full Linux sandbox only when native binaries or heavy compilation need one.', - docsHref: '/docs/sandbox', + docsHref: '/docs/extensions/sandboxes', }, { icon: AppWindow, title: 'Browsers', description: 'Connect agents to serverless browser providers like Browserbase without running a browser inside the VM.', - docsHref: '/docs/browser', + docsHref: '/docs/extensions/browser', }, { icon: ChartNoAxesCombined, @@ -2363,7 +2356,7 @@ const BenchmarksSection = () => { What staying in-process saves.

GVW!FAQPKkwB8gWUw(^2U@BP2f?6;HC=vh4eHk?Xfz68yGfS!(hz(|N5H7Nejn^yvl1!})WEb;&xJL64#M(Hd*RLp zo`oS}MvGbIaRoZm<3u$ z>+0&5$P6+=j418t?jiStO&G9!hJ@+=NSXA}@y{EVUw%0iGp=jfvuEc!pMLVQ-e~MI zbktbO#`W8Cjb`_RFFtywS95c#>)khB))i*D;O3idgv|%{@l&VGzDL;QX7 z9_6M_pTVDh;Z^X$(+{xV%WQE#Rb4d{=H@|GMkeqG4n$%}#t1Q@!Dy5zC}0(NysB<2 zOPGTbhs3L*2?D1A5h>MVJ<*ut)WS;fh_#3_;U%icV}Fl-V?pqN*!w^%1_!H~VcU*! zIRC=4;fzyGf`=Y>923cHFlN+rIQFCyV9~;_aPw+}@&o(fytA%^nX^XIB~e=1Iy)Er zy5V4FXQ&x>pD-_&sKQgg;6abUj84&s0%f3jMV};BdXDrOOa>|r;4xScMAGfYtkYmN z@J5V6FvvI1k1*JwstVr7$@p5xGvY5`0BykI*RxVX=HSK?xM4Jzkf${nOnRN(i03U> zOnQ^sZnGY9{IK}6lScbXU(M*;y>E}QXV*@BOItu!wSOHnw>0WPAs;tq_Hp8~PrhP! z@|9QlPu}}P8q{kroHl(n*V0sn%*Glh$S;JEgG-^hzM1t$5{Q%P6S6gW>cddf zo^TS<@^^T_+wFrk3}l)c>cQ^|kbE5i4OVca=Rkg8F*LV!z}`dEkW<(Xrp-7RM&YLZ z^Un*EmtTI8Y|fOMZ@v}T#22_oB*L|}w(4?obEvHO(18OL?|k~{C!ap|+;bi9zcBC| zL`-hh`~2R1(MY5?(9@M~NY7+|mjM&9b5ENl);#e7|G<6M^HYvLgWIuTDL?<+x4GG; zoC-g#S`RZY@QQ{+mSij{mkmQyTuba*eW&~=jI2*#4=tbkI@4TQ+UbSZx z=dLKLqGlvMCo?S*wYQj+ZEHU0Oe_z-oC zY>uf5zx%<8^DEhJKwo0EWj``nTQ+aOfI6ztizkyj2|tjnX?0z_+}PA4O`0^3 zDyUjyIlfYo48$VG;SWB(`(d#m|H9+VZl{g2S=%}2l~U87#f;a~{8_;;gH+WXZ5^$vypoatmI+ML z@e!lDu~59;Jq(CAO{P0?xp82VT-^^&p*%J&+84- zC*ESUD@jol_wKIrH#W70yxyQ(R9YhU9qdAIvQ3X`y|J~eg*WTXyp&XQ1UAH?Avqij z$z}veO(v5r918Phi zg~d|aw{L-}igG3_OZ^$ynb}ZNR}Tjd9a3I<>jPQ`l&4>KNs1+7QolZfFmZW^Vo?DB z2aY^HIy<^TK7a7L!Gi~UJ7Bo;@p6Z9xLe?d>A$-CqZl zhj9N4$gCPEFDFCp+qVQpj~WD%jv10@YiVm;wfd)Rkyvb3ED~q@2Tg<%M;lerU!R;rXon<1S zFi@hUsRNpuyOEe_hc5(^~l3e z(yKo-B5CsBTQ9-h@=bJT;;YZS0H>clmvx9B=qK+xP$87{DoX_X{=wI9^f?Ayt4 zXZ@$E7>Cp%3FEX-+4yCAY8wQ=*@iU){+tYIk z`W`st)ML+n;^F&8KYZUEwtMcr+wk?b-^1K<&WC-Km2lE&bL7Q~epHsN+Q!d&_;Ezb zKjRCK6{5b|V~&}|M1NY^yD*;RAU!RO7~Sq zREB~EmFSWEmBJqpBW5a0s;h$6ag*Jz9uHL(0#KzHe;njC_;f%3tC2Hibi7ojPP1IeSK$ zV6-b32;^{CxWSUjk}Old0v<|?o*79-7MsJUml7y0EQWbLnWR2;0oi{Yo~JGz5Ag=f zgp$#y5D&>ZQA!}_uhZl2@c3DvtDrYnOi`U;vDmCmm)(_yfT5?Mt1~?q^=Dz!Z*#iR zS`XD8+$@6Be&79f7fr!&|HBU#eE9DBfkg{HmfIVv`O{80gTM3IYi#)$Mb2eQ)(eHs zq*B^{it9oY8J(W?4(RF$ zLKm_rJ<$M&f(xXKyS0zzexs ziTlZ#cb}4yTg7!ah)hvycQ^FIIXrXRIC$aB*WlFCr-94n00RbIp-6xU-O;c+Y;fGn z>D-2m8@Rl}45Q2K7|_|)b^Ji9l^lt7!5?@~>qVC@F>cK8{wI_lI-GT|s#^BAUA)z7 z0;kh1ty!^B-n4lGOh0C(a{B3K3iTK~KK=50TtPt|m+p4Lq_JZZo81T-*REDpuUo6g zVnVs)=4-`Y)~toCKnlZu7;D-$z&-d=#OGzL&=5VCpx|WT} zg`Wx2>5z@pMU6Oz6VM~ZA%g3$*{o*?vq5Ln=AXj!Obl#Z@~TxWTccG`Rn{lZs@6zV zKFZ3#@p;B%Ph<&P1iv<0jQISIK`>|wR982`_!CZmt1g@k%YNMiy~;+wz`_0DiYu<9 zQ(=xv)rT9x-mdOLHY>l;RvvHmly_N!Q!$PQ4ON za#p8V$;r!;DMV8gNoO@%Nk4`U$D;!74~TUqjmc!fYO$Ctc+A$;mU??M;p&dT zCQw}(BCZIO>!wecz!#Rh9A30&VSLw?-PQx;hhWRL)#6!mSHrpIT&R2Msb^u!mX-XK zS6nGP@W8E{^Tl`Jq6;sBS6+Joc5UCrwxI0nVhAWPX4Na;cAytb%zX*h0@iK;tZ$!( zuBIl~v!@(d+gkD2N2Wd+hr)tlCeFEP<5uON2cCdgQ>V%I-glR@_rNaP4)xNz@4qLz zTz0(=!I?-jg5Yg5a_I1(A4iWK`AI=R!JlHz{{2|6;D=tl%Svyx+HB`%W_V13j*B)n zv<$uU@@uk3jUAIX^{h@~Q_W%h#`T*_%T}zjP8@TLZFv7-*6u(Y7XPpWCXO74KvE`i zbVziNHDm-Li4d#AiHCy-2$1k#41x0m(>#zxiDyF9Oq`O$90`|Y@HuNwpt1sD;3+0C zAh1PxPcEFtnD0Y|%K>)0mmTf3(9~1~z5DcrzWw_#J4$NtV3nH*wLM79tMNI*7(-8* z4G3l=kRa*qibKW0COA-8N0Ai_9MlH}4DJm>1{A=cz6DTRmaiC10;h;cdExv|ee0Gk ztr|LH*!nbQ{$YK#F;XCO%f>)Vuvm<`NHS^icC{G8N}7IeLuJ~p%T^A(=e~zdm@(s+ z@u6VULnoD#bzCHxz)(KH`T%)a{Ztug1i}QRSFcPM*tZDen2fPn3miZq!tYPOtNB3AIpoEe9lcfZYJD)?_Dte^H1R0FMfpAUVRaM{`D8|26|xn)Z^fiOKyZU zYk!56zb;0Qp$>ZWAuDeSw6*#n)04(DI%s{tI0^Sci;6nPGOxCzikeNQ%s_a$52|3M zpjN(;;3v~a;#rj?trt;Tzd{&?kSkb>0X`HknN+X`?;XXI^minXZLyocs*Ay)9ou2+ z#%<8h-U^;c92kE)TpMV=~zm+X~2&~Hc z_U#q&^9v&FZS8ATu3Yop#EIi}oO$Mdu7%>L!^P3@|9AW)0f^C~M_a0DnhWyt^E0T2 zxUs%XF&Hf5$;<*|dg=&#AimZ*AO7&{+R^b} zAKHRR!T{~tw{QP>{#WaxbLX7eh+OZay1Iq|1BMUNz476PiObJD2lnhOS8loeR_Ude zpVM7>+1-3)RRhd8?gaSun~zxOIVn^}5in>)<~)I~nBlg85kUeafeb{9sz%kdnF0Yx zZNgNoDJ7mIW+b{t8 z->>(A@GdFy`n{sTL8 zo^%&vrH62^1HUKp5H2WWb3kJD_`Sz<)=$8J^dddnwaT!ABYBG-v+w7`uKeQQ3ygHR%8-; zeBHd$u2~ zIY;P%!VKAys&E$0Rwo9=B!HTriUwRqNtHb*Nvu4S`~#DqBkfcWIfVfes*+ghgOu3g z2%_mQU_AuWrcZz$mo3C#>3MkWsYk$WvqCHuVnMXs?!dQAKtWM9l(ZEhd$R|oOqp!= zxIN?ATRW%q^e8nR59fzJ=)gd==;T=wr^TYt(oLJTP;Se6(maaY=}`PWU-F0Vf1oZT z{<-HKXAZsh&wG*cg#+9$4A{qw9SwbY^}{u2Gi=(t4)z~708c!4kDQyI$)9lc#RiMT zCEx$R{q#0M0zp6wYB9c-xV`)KN$Ge|1`iq_1bl%ylgUEvP5bB0oh!kgVA!2*QPAlV zl?N)spAP?Ez`&SqZ*Snu4mb24I23_07ptJ5>W55Z)|(sl;9n&%FgN4Aj?cNC+A25* z5^I}?BWRXHhF)es1Z#hjsT~MOR073Rg*&xGn#$Bgnax*n_#rDnvNuGW5(9r7#6l7n z4MwILlZou~rd3;kYAY^2{~|~r2r_oea9Fc$9eC1oWM!zdx3-DZwUyDP#zwEd$J^!i z_Us5J>wW|%PT=~-tzNyFv)k=l?zoBE0DPq;hhI2wz|RlO$$_jPL!i9896{x3#fYdM zfwx(DJr^~b`E1Ov+-YfokB{i~9o&HrsAw58V!XYkrpnpX-Ra3l%SzAA$xZipd(!o0 zM_Q~E>^rw_O^h5c+;`IPr`kRC^x`i*o1gjWYtQjtd^umgVdDyN}GC@Cy~&h~D=Boc1C@lLq?_S@uFUVcIPcHx&q$x9DB_^>!>(pUqvW_9;; z5jYgCZ)jMC=kiW@`R;x2Cmd_mtm$vDd+x}|%RCpEZL`&G5u=f?^V#R08zf0epBRGS z2}H!(iu)!{IPq-$4sTe%kicMd+xSH**1@`UYcNc>gS{5DS5S*E0T9s$0_ixX6H(@P zfkZ`uRX-r`&#W_e;hK z!Q^9y!UzN(GBVRNn@SQdPAEa2SBWQO{oD&L&7M4U*5IJu+ZBoW^~t1CXLjd!i_Cd) z2lzl2*2hIFLahmrPcx^7dlmIkveKPt?F}v2CX-pGH<^usfKQ^4SR9d;WRcM{3e$|h z=n4rE1nrPth$+;tkzeFga*H#NU_f%A%L^0641^hzCrj_o|47-qeHW})ww_l-J=A?iOl*}AvX}b&M^Xe6joIt z7zHMXO_hX31RUstr`8S9tit`*hVNOiX&da=uol`8%r6}|0sILgNW2lQx$aV^JXj9R zO$~7HKqc%sxP|s+@%iVUl5f8CF1G%K!%>-BD;zF&81M7)Uw>Wk#_hM?y!y}D@^p0k zxyD}-fJh{iqO9z!;TVu*W+3z1($ucdhG?-BFjQV|P*0mmDFe89BH*A{ZDygPy)$aD zTNkINd%na6up5q!zx)_AYE)$2yah|^{To|mpLCqfotEBr+m`L--o5)tFFyB@eCeg< z!Pnn>rwki8T6*%Chjc!F047hF1wSwR4q5X~NXyPoRGn4R+ZDKmyZ^C zQgWu&OR9`uCF`sVGG&d$%nF$OCQI^owPJ#79|;*JcA0>(AlXMj0!%h!@^Je4qe<{~ zcfho%<6!E{@emAl!_s9-nBVuc*IdnB`^4jCgT+*+Tzkva@|4MwNd-E7*(F!DeD}?Q z_P1YsB|dKaxW4#nS@`k4E=u=zAcrz<-aG)cVw7r%Dbk@b+fMwbC9r}(irgwfslfHLMjOY6T-t5fm46xdbN@rIGSCEs( zz47vM^4X`J0asmr4;tX#8>ud1qtL4Eq<1FAL)g>Gbw~_WFhs_~>^Tk(CU3-wb z?2^mm;bTT~>sM`-7cE-Jmmt{Ew@(oqGiegEv@|HYw(WxTYc?sPhYm?xe#4cq`yPCl zYi;Y&-*W4{a-Uwsx}+p3Jc-1}oKRa+&54PqG;Gu;D#PwhCZa3T(sNhi;rwAin!g`$ zf!ypNwrkJ+*nM~3uD|T!3yiqP@)=p#VqS3$=0H3FE|~C`!Gi#Bb!7~h!Wd?7sxA%_ zdZQ#5=UW69RcalG#mE{mi2wr5L8G26v&^Xl=Qm?^@hz#nA%Tkm0cuHwXW9^y1dugv z3=`zc+-xX7aA42AgUo_Air+I>OK`pMKtpRQWM`Ft71sM09{FY%8hgtge9c1-nx?#)a zouR;@AI-B*J8|km58rd{g%_OH`+|!u3T@oDM!w>Ta}7%L4fEfgxYw$1g6{3l7xLF% zI)~f0cRSN;k>U|Wr!#V_-(SY$GwMYKxCh$2F?>#w%weD>gs`LC1Ib7at`7p_6%^s~ z-HB}Sa=7%etKq399#MY$X^Hg0%g;+OL~|!hm>@m);QbVg={nlG5FgUZyLRo|fq>bE z2+VA{OncREhP_t9JqYxr~6UTu@p70|xbHX)`4e42#91IM>6hqK{hLsKr8L z_Qf(IM5rBz2}vuQW&_D1U}k1ICJw52aG1e@YcFXX>Nskvh#=V93H7y&kds#keFqK0 zJ13S@ladU1$%Z@B4BKTbIzU&vricfMP>QaN}ySK z0ehCZyS#FUbP}kw$zb3!)3Y)P^9!dWW681}fA{{1g9mp=$=IHdPFI(%&+*6YIw|4z z8*#4b!qKRlpOfpaJXo=BWqp%5JEyS9ptl&EPN&`GwmZ#EtJUH#S!`yD#cVK{^@y0` zexMh{q(Cb`T#4~gTvig{s1gl?6}yGnAdFnEyd-)ydtv5fGr9_B-|Mka} z6~BBhe(>hIy0>3@oqzPPC*hU%UWcZhCh&*OhdIZe0BKGud@%n#I8=E6h7THo$ch14 z8k;c=#5LV+VK%<%b!RT*pfxCxh^E>Fj=%#6bhF(`5m_}X{c0j9s$h{$tHvTB3|=n_ zB<6|t%81}9wPw)2>AsTyxgPKFu8rGZ_tvd=&&-fnk`GA^fu*b*To)bi@q4d8S~`Iv z4sOrBZMcgH;+wC$B42sKwSpo`TrwdlRfnquS>l5NSQh>C?cz5Ji*q+~s%rPqadiBz zkG~uMfs2pO-rn9X98C_TYA2V|DGwOfSBWDkN)IsgT(J`NNEABSI;avtL6ky3_Ctxr zqC1L;3ctXYIjZ~cFEr-Oo11v*skf^e8hctju54-A^qJiDOTSp4u2Bx?kj6{$%6PQROX&%rHq$30yHyfRx z*WQuDqC{08nOJ#}90Qf5V9L+9S;diOmgA}@VgdsX60_v>B=2X0XfO(XpBIJ=8VJKM zDcimGAY|p`Gr#9vWdmWz@WB|EM8M%R!`BNJa1$pFr1Hz?9k<=q^2JwQ?aFl74qHt| z(!CBHu5WeYTerg>sD$p{E9pDyg423-mhWyJHDaLR&dkpK<(I`-x88nt`^zuB7<=oT z4+67iOiE0dFwwlXqF&j)zLHDN%Ygxdhd@Vn1S%S-+QHA-RuZJqj+?chGl9-*Ko-vg z8N>#qxgpKt29FCryId^LTfb&GR9BS4m>~mT`qZhApPvtVcI{Sfy5(A>;?MylH#=Ke zv2vx{uYZ4@Uf=JF81nLpV(sl6E4Odo@m470TXp^Q|M)5@Og{XzwJqO=qyC}(u>TB8 z$Y3y8tfIx7P+D7?xj84DC~v*vVR-)82jKn(pOjvB;Sv6!`|jhex#>=*Y3_z>w}-K8 zp@eElNTsAw0vQ^uf`k|xod&L@Y)w+Uh%qZ(a|7pus@2=UyB2z#TW7UQt!udw=C&5>ex_($l$| z>|D8g?_TMP&p%_}_mfXQL2Xz3-=2Jpdb0Vf3>(Zo{se_wMRsi60+stKWI^WTzdiPV zSbexE@#^az^Pco9<<{G7V14r#*oxFQ-__X#t<5dat4}X*I2_^T=H@MVd3ngV!~Q=a zK=Xea1A7ft0^S(uFeUSHa}dLd6H6-RW@Q3_H*_MTGa&FLb?#9JPhxw9Boj)E@G%A$ zD74-ZNs4oi#2sUiB(4D>7)&DbZBg}i1eRn`VnAZ43Uke%mLi*2Diua%#F?m4-9*IxPld+!i?yzQ7ErHP+^{uzk@ z=`da+rvs(>Q0>0v=9W*+JoEI`#~**}pN@fj=9!m!mzMRuwXi7n{8%hvw%e^rFdR|> zNLoZfQ3N8QiqG#w);R!u`t*igy?S#MhpHeuFM~50b#i;B7dCF#%*}h~UTUXtsaQSc<^>5pmI+&w6(Ou zNhghk^DaCd3QBU|H`Q9g@UqPU*RyB8bdveI#J~0wq`IVWzmz6 zIhVm=bvqo+G>g%iVRtyP5tPia+3g;q#po~^1-n6KGU^dEL*P-UuCA9Gni^zxx}6(4 zZWRC83-|IT&YB`#ciXLE-N6pNu6h^DJbtQp&z+A7k3V^z&}U!?Tyy*F@cJ8Xf{c6O zxo6IW%PzYCKK$r4IC!W6`u7_IcD&x*2r4BLP9{`Mc?N+AB)+YQKWLWVz?`6%2#uoZ z;>%hEECVj^vkuoPBB=~UTH0l{*HRk}323uYEW95slL>@K7&dR)0##Lq!R)YsV6=nT zoef5N8g#U_z%M`j2;(PDfa0QJc8W zYsu1OD_`?R+PB_1_u8Y*6-UQEZ2aW_h}PCtqtop!GnkS&6gbd=mz9ymk%urPgp>?Y zk9|vXGwbKf&CTV=0;sdSt0g-->)WQLrcDI}1xNdt{>4USQL!WF>vq^|79KZ4W%h|P zWHFxLFTLan>6c|orMBi4-QM!OFl@+3+=z{E%(P?S$Aup=m1#RBQei%+2CfJtgb_cE zgcGV4Hd#87K@%&@qds~A+f3MoqFHj%h$j`s77zdtM^v2HV@b7uXXSS~y$+K$1G0J4 z_bEcA(+%gJKNntm^G(PvC$sr|=UDvxkMVcjd5ePK z_}HOCx^s()w!7W#x-Y)?Iskw0L9E``vcUy<6PFnb1UajYH}M8Tua{nUfkPrK+|tzG zF`G=}-NB6?*ay}u`2`czA{aS#61Q&KA!zmS5S1OEvyc+EgH@O45Huo@y37m@`%H9; z(_#a=$qE5)4{X@D2E5%ZaKiLSaLh3iFknuXfBN+ki2hi0(^$xH!|E*1;& z^B%rUT=e62%FC}j&0To$xytaNgSd<5oC%+O`5k0V84a0f2mmy-Ks1Ix76KCFf+16@ z1(#rLL4u%X$sYo^swW?(>6sAVhRFwyfiPLeN+tp92=tIfN>V0PnPOu~{^A%E>zVUN zH0XuBdv?R@S<~U^S6+utK3@nIUvMVaEEdLGQxYjACDxaZpbrI3%Au+%7&T(BD?Kf3 zVmQ*XXVt3OFQ!fV;|Nl|`rc=G=bm};>_|8|YTM>*7GIB7#-xJl)vJu6aq+7!zKZ+( zJtTZ4WM*dRpL^kTVSRZy->-KOH+9N*rFZXM@|F!7;n1N&Pa$`iNnT>lxLrG zj)|(R9(dqxD9q0_B_xr@AeM{AlX7)!t>R8g0|Xs(p-`yZ>9j4wL)rRgE2pj6yGJ-- zSebFifI-Fq1N!OOJ34ubBaQRq6mU+PjmN}_bZl9x1PQlsn!`}a-HO&&T0fcI1E?#k z$b?7`1jDshOd#OE%G9Ya$G{RbPIVT>uMs?>`ORsIFVPj$YKIA0c6K(b-doN93o1z; zg5WPNBr#dE;`5S$2_gY)nBOG@-R<{1tZVCO=f#+$cUi2KYp%WS(4@)Z*We3wQ*{^o z69L~oL&tgR8(Nxew)CjklArzZ^RLG1YXa>{mV6a>?VS&lzJo@X&N=INLw5j~dpRci ztAk(zkr~s(1cTBVLJ-_m#F<>_kR;(eeG+U4j=QZUNOyTyMPEndPFS^kC0u{W^~(K^ zKBOQpCf)p}Z-1vT3S;>!hox|`a7z8q= zfTSC4xGQ(em=V|X?v-~*OpKVF2tGt&F{Q^F0KeY{`20gC99EKIk}E0ct(fd-%Jnzi z3YT7dkA0j6=em2s z5DX>|Bt*a@7G+%=c(BoMI2wsZqdtQ{@D=3c2i=+J;jFye=*Tf+13f+7MqINbv)P1e zmsgRyx?)kYSq=mOoGCX~9$>e77w_FGwzhUk4?p~{^7Yqq`GEuH3H7^nnVO?fvl924 z+dI1L^~qQo5`Gs|U9%Xc`P>2{mZ<#agQz01m+qzXNM!I#|pPZx5trcD(t zxcChI;O_PC*kjMYv(G*$Br$e+;^{k-s){Yiciw-8cewJn3(r53fBl_zF-FrV=gpbJ zU3S@3@X<#fL3Pa`C@CohvMtAO4@^jAQ0tZ=6R1GfgccG4iPXwh0M)}>QE>_8sKHun z@iXNnELIe#YDFijm0Aj`#3B-kzz4St_wY~vHm_d`Ep6=xrW&X>7jjEVA;voql-L0c zHHYEi3$K82la7HKZ@LPiiGcjgr(bhto`04>PKq4PCD|Vg=vvx3!#HhL?%KZX?Hg~r zWb2;^q&_TubA(ySYgj(@R{<+0?3gI#52i{8-E(j0yJ-PO{#QDeB{j-M%x8#gJw zV(DUIG$PVmkrj)9A3k~%{QSdb5b$?{88>L=VZjmYq$OEYMJ%g5?@WGAQ5C3lLV}h5 znlNBv)>vd|#!4rF_0w~Vb&yyYC9%vTmPj>xCK5U#QKMv>ZA8;&P6MM}f}a;Jg?+HgR8=L&GRua+5DA&N95%P?_vcJLX40XI%-nX|a9ZF`FficIZ{4~l z({8uBV{y@7HQ98rNHkq1OO}^kcrunu1WmY6TObkU(%n|b$j*WRgNAU5fWV?36B27C zUMqotI6T|zIb<%)Kq7x0TmvX#rleVq_oTV033hJZf@w?`PCjlrOc+0sDS$~yeQh}Sql z4+k+lIs_=hFz7MiGhi^>)6oUl8D8u6FK-$^4zPT;P+?rOfZwH@C7_zTXF>41XbH078H6XdqW7P+dj0&1#i zAUn&QyzSn*;@^JtBme8lHHPBiKJrypU&3m2@m_F5AJuRBFL(rs(}Pf@pXr}oUCl)9d|tJ88&3FgXfY_gU%Sl zdr_yIA_xEAK{_MAitIQgnM|hHof#gEH;|p9Ou{#>z4o&2q_dm4ckI{|*|xLX(7R8z zHO*n-o#|=dtp><;+m)Ek${h;%p$U_%gxw5+Vg@($vm29;oo0oitaM1r$VW2A2W!ed zfJ@Il18%|9DP zE^qEZ6}-e+bEk2=4ze z=FCDBm6Dj`$^C-0acZK50wXz$U?H~z1FpF`d{*=TW{M?97ea}F1p)U60zU^2?1hLw z0KJC{U~!9JG_rOMva|Jv2s}{0Y_Wm^fu;cdd)L;(5bW{6iKkD3bI&`Gv{3N;ljMcM z5y02k(+pl;9DxUY6yuK0K)~Cq({rr|h}7GxmL{9a-kF<|+v)OHy)A8R{=*fuVKc|Y z5M&T1oOq(lS5+x1SFXedbVu^4tKR-SK*#pdt8rb_=CLqm&I0tVqUU-3J4?@?ARTgES4{3}20xM}jsuf9q;J8;o? zCv)eVb2k6wSD!;o-C^ik+6&?$UT*~5F+gE^WkghgAtpf1Ku&dSp&@9Z%(P~ZHV5S> zBut5MicGDyf`(U!a+0~3kQD7TB_GEwlc#J2F6LGUI+&3f++etXj8*=`h`S z&z*4QX=lQjXU)O6*QUJo;wv(O4}3BfN5Up1$B{H^ZEqJ5?Af_($M#Q#4IjMuDEM%6 z{4>U14uHtd&vzf(U*XBk&(%@cS|oxD9tN_Ufn$Ah@mL%e$}aYBBYTNNs!r}~>)A12 zP~UIx&uif5_?H{RWc<2%o89e7Pq&%vy8PU%=+8ebjNftFt@6t+zZUCLR+?Ce#Fj~K z)+Lh?(Ywel7!i1|fj{gAqtyna)fbO5u$!^Jgn(1eyOQ#kXJzReCSWr2>5j%k7UHsy z7uSA{geN(@S~`gv5zqQKF?bLZ76j|bnv*QpF!0FAOoQUWELir-66kL4K(Ik!LaIi) z6Krlb+pLl@&3D@GbV|^lP zC_nJ%JdP|tfVt}k@&mJGPya(~=F_GP_wC-j`-f;e)W568 zdlJqOlR>YS1%ji(e(uB*W+`XQy+rzE!3X?fk3Ppg`qbKfGR{Yh8Wqs2I;q?=2?osykr6)> zJk!@?_HN?A>-WMb$4!CVJ9fg|_dNpdy!ISeY!>h#h|HcJ9s{~$0@6G#$j-@7Di2k1 z9@iLqdRF$x*4CEkKA*3eoIv28Ja~h3$oMfsPCHc9QdW8R5Mq8lz7zx0jEqd^@^*{w zfA}E=(z5X0yRR`2;gP3bQ<}QEF^=x!*^baV6qXv z>%qs2bk&}G>JdmwbDNR%moeE8np@hq!-r~=;Uk7pm6X)h(Xux?FZZV%J9bpTpKw^r z$WbCHB^YdiCo305d#Q~_cgj|Z96d+X-VL+gQTpmcxcxvNFBGatkQZ48ZFx7SS^zhkP9(`p+_3!OZ{~6T}^q#_o4I4Z=x9yqe zPRkjaUtH|5;L?e#v{YJ}mAqii>0S5VeV4DYs+vnA6IlkFTntcQyw6{x60M8Tdo*-o5{HVA+i~zG5sLTyXkT7oBi9 z{?kKNm5oi!QcFt<=fxyHDClZ?1xOwTA9V4ifmPAxA zJ4XV-6t=G7g3l@^Y28#r1)K;ZOYs;94?<&0BMcroP!*JnU?R+05Y&r7N4FmmiV-}H zVsKdO5DP_M|JFmmB{{h9<_lrM1d>c;+aVr?4uRlKJmUaK)a5atJ{X2ygawA8M)Sjgs+>RYlWmZ~RqRQ#? z4X|2+cn=^L4D#{LxS_`%*LOub^!3g4X_YmV{jxH$N1S%bvH2Mu-u2U>AFMCF{Hp1l z_dnKOe8DBg7w)~!`t^@Ln%@5SeN$C^g<;&}iSn{vm&q%at>CB4oDMI(@d9rDqH^9D zv*lUG&g2*V^c~bUG(uTXA@B)5INjM`;T+JTwbPLl=dtE*w-+BNKVZSHh1JF+0Jq#xAadvDyd4GAa{ zJaqp9aNc=mDyN)$99MCu9PYgJX8HPCZX{p{fzh6X-e}|_$l}P7vTJ|E-gk!$8@vKR zzoUYaN5?;9{N(_MNF-^}84NZ=_xLy>xjo%JW~E0qf)qIWkTLQ0^f2}8%#1Wdd6V6^ zf~`X2baO#L!BG_OUvv}~l*(eFRyOEOLPuAW8!>WJqM+pG*o`+{6G?M=uNr*bIzGgCQ z4@E_;q*pKNm8CbBn_HUuAUorPKgrOTrt00htG^6}quH@&Y_86jbm(;$W$^|+7ziT1 zph#Aug*lJ(F6)EW(*k>|_CS!hkoPyP10jJA>%%v0T2tFvh#F$jN^fiYMLG}K< zP?GP0Ij0^6z54WlKmOoD{@9adQ&YIm)!m5KRN@0cxjvCp z-m9*w`|zA|&S-`|*cdu==-!&TnqNA)JBC}$=CX`5H!A^=MUm5E)cU}^x5!HuFP5Kr z`Y|~BoKuqn2lmsSb?R*R@Z*J0QhXc?8PE?_uV0N!{2}N&a3okP76_9~X;Mx0I7$X} zI+<5e(%-+a$P9{LT4)GIkfESXkR>MUN55iZ37|;rMeJ`xR@UxBM)i`5&x6-qc?~W; z_k5T*aX9dyAO`WG>L-H#dxv{miU9s8h!r;LJz;3sR-!5DzZ{M<(Kli*#xZ%TxbL%&5=RW)5 zTP`y%of|uBkbKOz(Xgky9BLbCxHaoH!09KRAWlE??8MDiT_)EyH6>4&eNtl1oH=HC zFG?uF7^v>tRj#CGq|0R`C1Na*Y>tFtKL^dhwSU&Y62TcBpKTp3c}kzY10}r};y3R& zC^%d$1Q1OiN^zWrxbAhdBPh@Ug}5w}MgxL0I5)_4Q>`eYXt=1G2}Pf-PHkM0w81&S~2M=E6#&+=bi~po1M84FpF6{XacBd zZYL9Qo=VV>^bl<>(?7L6h}O^r3l=CicZ@@Z3~^Q0H@gt9vzT){qM#>75YSIPW}J*H zvcILRHh_yFs~?vlF)kESls#=xZuudR3vg~^)04RW;SUkxF`IOdo0CQDK+w|I0vmr` z2>tSmxR#vG8QmG;!w)^0eDCddSr^P@7hjTm_QhvS_+F;gwsvDA7VT_lZvG-1^nW^i z>f}!NlMPCUAASDQ$tTYmb&bhr?3YBQdjI}?a&v1d7mp*^g#?%+N=jaCuA(;?MVvqJ z`5(?#7XSLQaLr|y$d8}E($HJ4x&z@zGk@vT=fJN0Ezpg?1%(G8pC5ckmc^n`NJQhz z^@4N&NsGrx|+w2>>K{?uEYNqofPHw*B_2l6ODwCBh^Q_ml|A2 z;c!@>iZd}^Oco>(N`3tRZo&kA`;HwobzW~cS-)wsrK!2a_|{vm znb-fiRR7cyPh}4oGAQ?fM<21av^D6*PZ`gzTeFs5xndvi-sQeyvm)sV9%bt;Iulx?nwh@P7ydgd9dd|C9K)J5i+x~;kl=tgi}wNuFO4S z4y@g@|_)Biool6+#ke%Ke)ZAwe<}oUw_0s;%LS9(eY0ke<=Wh z7Nl5AbddHWk5n850Wm~$5t>ED(}D|Z5<0qiz+|y1Ma2a~#F6@MZQuT-8#pel2ab+^ z$w3?4`Yn3{$PdSQ0zTYGOq7@=2J|lT^~uTdwzYH^vkUr|Yz_y3)SSSAOA$KSyDbQhU|Fh2$r`H<= zo6GhOF9ZJXj-f+KYj*Fb*doT1X*ynU$GNa%F*{|aCsU64dZ@bsw`yd7^fDZ-Z-TBs z6oX^~NPH6Pm^f!RJ>Vu$E(D7>5|{%$0+s>Tzk3Jtv^Kz`u|weOGfqMl$pFvIds2De ziFpc%C|+>J+42jozRYLjW?=>`3mqM;e3IvsctY7$b?ERrBZiLsX8eeLKKKI;%@t$4 z)nwh=)zg*Zuv$z8!6-`@_avh+Zs@SV!UGT71$W(Z8=QCP_0o=wONC1>yOdwEeitlV zy9G`?b{J_2KvP>6w6!;aGcApk@RI6%suhFi9fd$ODmNv&7wTixSY{fs44MMC%zmaY zmGC69L?&Gxo;ed?G6M!yQa^me02n@U5?pxkHE?kM2JpB&(1{73s2a;58we~BEi5YF z8X8*U{M=k!W>)6F>YDml7(^56|4*sxdHe0R^(V}lI!&kJPuaTtU{+&eqZE(F`QgLI zlGvo&-rg?0|G|e~aX9!FUwj&u3W>k-uKSV5l!e0lZ0^L9j^h*2kWy1!rR?0fgZx?G z!N=#xdzSvfzx}}i-i`_QOE14Cl-4J{$?_op!sfpdg=&EZ|A^;JlLwgJO7X-QjA8M`9qQTY<`oqp=tm zcoVB~NP!A^oR^dY2uuV|wcSirooR5eTET;BILB$rCNHb~*c>p834_rHF?^rKhI)LD zIGfPbRaJ1}anoSZF;g(Uh=B={KuRlQj%UsqYA?Q#y zgP9LO6q8!F&IDO*Cy>idYfC-s_~j?)m1cs|Pd@=X1!eM!&%c#;>ba*S@_;(;+;gON zKX~7QuW4#&Y1R3>zMlHVy7`zLzkTkx=l&^L0sns7`_QYyrcW7uSy@s3m?-Wedv+uE z(A)?SoQDMb8*ou8EXYOhRZg}vHAELKTqu3}!`J%30|&t?Z@xnpn1AENH~81*e*)k9 zvKfIY5gKZ6j^WxG2?voVYlleC4@sPJq%}qD7K#`nSyeVa%_f0q7p2xw;vATM@~ULWanh#+{Jqt4?{E=geWeo2?Wsem{>c^>CC3O zzVQxMAeE<=*F z+mZ$)ffJ03Co3|{BQZyQhbPBxE>@XWTdzbbW)6&v;J<>(YGhUJvpVtooWW>lyiI_4f zccj7nU5jrDieP?*ApM)4Fr{@&#F%v)v(i!_3u?b0rFIImsq%qDh$tAg*{ncJOIJ@P z^IAtx1pNLGdo5&Im{1xGMNzGvR9QA=@uWzy%#liVJdu<$GBQN?^9`w?y{)~YtGmab zGb)&bDsA0uv8LwsgvI4S5F*6Y)HOmdk^qM%6CCag1cnS?bK6<+g2^{n@EVe(V;~Y_ zJ>8pEuY|a-70#b?EL?WcIS}af!YL=7s@(a=eX_w|kUstB6PUm7OC5p_x@0`bx3{$M zqzBd0EpF*-_daPh$n#4|+$AvH=%OKYtT$I@stR_hCfBwi z^HWbeODNyAQupdBA2N4}yKlV_np-o| zv{RpxoyH$KWeW5vF5&j=-^*1TsFW+p4~h@paeXp9BTc;hu_xp>Uc)=@x!2IASD8H# zOBfKG6uP@G5vZ(!e*OB(Zl?_s-DnHm(v^F*Z$AKk;$aqa3Z5Qkwb^xcyNy2h{{8xJ zE{Bt&`J)pM5Qs@oUS1B-NSMtt66&K5mXT1|GEOdD;Iy_1trCq>!C(eH5a3H6)SFFf z?va8Bh3S~6MLawhSw)2&S?O+Q@91D|8Pwz7(9poX7IbRD$DA@`g=v*gKR2?`BsQv0 zq9Gs%2&zioKYdVD46gl^2r#^dP{{|at*s4ID<_FDx6^Lzb=SRj51xJej3Qt)qaYg~ zsWf)@p(ANf?5;FqQInXchM0|RMrJzXXJ56^*7(>i6LLUe5p=vFd24k z-Ke|aj+^)^?)n?wclaRKxqBarRcTIlcXmL)*MW074AfpIiwT}SI3}{nu_^~*$7!a? zQXJz25rK6*Sa6**;aaN0IZgWA*QUGVX1Z^+}vPl^S7J&EbFW{KH3>GF@?e=Wyi zJ@S(;zY5LmUhc%>kE4A`@#4Mj>gYsZG)$}!d6}^mF_U0mI&=mEjhXHdZ^U)XKy|82 zK$l`X3v(&K-y;v#A`m?${5Unk0NXBusgx+a$pjXlnh4w22Ak2!^P^K z-Zy-7{By_5nX`KI24jO>5Mqd!%kg+5f`MpPFc|DeD3UifFE?p3S!KOmM^0_X8X&vQ zB^46t3x>i#o$XGeiETEjH6f@0E-TAbjRMpUwQ3N z6el4gPzC?2RvVaYW=x*D@I9j#NE=yj5hQUyOi++Th4c(H_adz(X3xb~V*0Z0uYzNz&4eGmT__J4GF1L$)iUY$ z87D{wD=Ojqi!UU}1R)v@@W@~bn4HU@P;5g(ee2T)_9}~J&z>EJKlq?32#ejh(PFl4 z?(Xr1L$Q#K$NwQQ$Hzlq-fXhL+b=#PSsjJ^gAd)yH@CDYLq-jROV2$Mwr|-F30VjE znb}}R;33%44Iu>GSZSzEU}CA%lTSc|rsSt8vQw>std^h><{-%wXz6(BQi{egVU#H& zsK-(3UUI8z>@(3oH72tBGvFC^s+NlRWktA0m4hjnMl^(BG zXlQCQxYHaZR;%&2&Q9-e_y>(;qlR_iJ8Xmm$l7O2aS+#y8b#`9%Nb~@cb1xP*DYDqfQ<)sFa|bWNUNF4yV(x;mr;Q)-mVx%^g-NynXa&n*|7nMlO#TQ~27z5DkoflvVFMF`w>7xeGjmyg9` zGOh)&pMLmB{%+wyWT1`w>QyUsnb|ppj@B-idHft0I&lu%c-u{|YxhAQ8{H5x-klxI z5Ds`jjK*<3CumOd1k%!4tCch&Fs4(r0wmRK%S7pf0i`2nLjJ&sFlPEm z;K}a?t2Xb3LvZ#v-9$xp|+kwvO%yI zElS8A+_-wp+UK*f(!U=#a9{$Cj-%rbF#b{i1c^RbY(^IbswP!=mSeVLnhqfAmCMY? z;4!QKpEp!zwVIdI)z$5TqvPo4<%#btFK_SZ>5fVWi`%Srh1cnlU0puKXtEjd@(T?P zyBz}oiRD+mfETA3F~~ARqES}CK=TVURdwdLfWa*#O)3D98memml~B^}Q{YS>1qHu4 zru(3&C2N(1s=FKq)D}Wzl*D`xOG3dMy$)GzdHp>Q4NFj3Tmb1FN2=!i$e;u-iNFH} z(Mlv5Q_L14rj)urrPZuiZ{n(}nkAji7&&mT!vFIRKZHAbI)Zp7lCF#lnKRntu3!YZ zgJDe0^pNIoAn0gkEPGdbC)Czd!QS$X5b@T-*(XhgJMXv!9C)uUzvMFIjI+*GB$*RG z{oo5}$*+s~y!>39D2iMl5J2!oe$(WTu`~zEAH`?*99462svx>6Y6bWpm@|t1pCs0|vpyJry8WtdO0X zg$#}ioo#KbwascVAJHk%1R9vp5H(^DcqXyR2ucFERCdOC?+F}>B}IGz~$~eyJ7vhjd+SN`RQjLlA3Dj;*UM`3W5oG z@rCD~?Dnuv);lR;{3Zio81Iv6r|5N9=6ptGlYAA%3xxm>1t_>&L2!yz%xSDZg~ zy94xQJ0?L6$j!|J*6D-usrqmw0%1PZK0&tHibz!u3CxkHS6)=be}5}$fWOyvU{G*M zj8c`LWYr7QszDaBItH+*k|tDX7ANG4Y4cDuj)}E${AT|0`}aEXCQO=@5D*^zm z?OiJ#T>FhUvdkl0fb462{~^MgZ@deaUvWN{Ooo;7&%0Fmdco%=K@c3tq-ffAV1M1_ z&6__$AosO1&N$;B{0YZZSG}!otnN7WwU-{c&}88Aw{G16yY}psL-@Q?m125E2K4XW zpX=!8OjI1K2rT@1VPxSC--%|uF}Y^dQm+4iL54VjS;tH{MKL;h!Q1bD0JYUM(2770 zIZ*Vpw?Tx!2dZA6d4-89YolNqII18a%}oZbk#_}>9A;$AaZO;@7i)2&nn(J5TqiNE zkYkasTv1txaY6tJOG?1zutN_La}smDure^SrM>}PdEs@~zo!N!j2{Q1#twv{;!Nfq zLzdocxaQMCGiJ;fdEEGM^1y-p@`eo^c&=Wrf4IH1=js<<{9yV?$DKX&+t0r(z{^A| z$R8%RARvO;j}|RjB<oVm$oo_G{$>e}RQ7j1;> z%pAx{cPUXR4qaW{AW=mY?pL%fDa<{JEKX?ylb99~5Fil^$_Z}^c-vc{p`iK{jATQqs4=lPvfB5k~YWX?2$?v{hmcnx^b~HB13L>?+x%pBe zo~rwP<%;K~&7Az<{Q2`U%!f?;WMYrffG8*^u*PCZ4_@%1K?7#uzPOoEYCtq~pVtR= zhXaQE;bc#D=i2=Itfd(=FcTE*OP6XttlJ(LRb8ePO&x2=^17|DNX!)qhLU5)jl+Ms z?YN;!DrY1BOC?m^^>q<=D^D<0A<~1!f@aWyd5&u+ zv~xQx#`uj2z|e*u#bip5G3DT059$%8A_)>t<IyU_DlP`wVcu=1(eiWVz@O-mm znZ2d08%jn_fG{RiN$iG_?e0)C3W1(Z2>0~hzk@JqLJ6F8#tBf21?@*z5MF%M)zCdS zz)m>kboSKKkBbFGxm3VNiBN=)Bv7nQ+)q?JI22sc-`V&3tRl}>GiT2KSy4zR0o$}` z)A!|-rJuL8wpMoc^yTFj6lj`Awk?9FMu*^{b52gZ`}w!p+i(0ue&?hGaMCHq!}Zr) z0he5RGt{>GU~+W@An*p=y#eU%>V`^8W}Qwau0?9N?<(*N5mr-j)%-Ry*CZFDamiX*VjVkFmG9vrWwa4ZO+a1UH}!=(s>Tm`q@@c=w@|7}3Uc1n!{Q)JGBpgGy9 zvN8yVg5vh=JH)BeCg*uPu0unySpA9>D_-4apMCz)3#7+@x6rwRd zUYfYSDaJaZ@dsLSG-9UHjFfDS!xk+_o) zf@uUm5J*A5gd!mzk>CZVg@eq71I+G0Of%Z|aotld7#F(wlRGcOj9R&JWxYkVwN9Tt zxeyY95El^sQY?^Fl0`Z(LMq8bQPPD0(jvgEVsb?}coT7Q8sfu-cva#0 z_3ICN^wGzwEuy3SmRoP!JZs+c59{meyGM;0rNK`u3^v3f`YR)bMs00{M9l^IMsII# zyr!lxURanTjTlj3ji*wI!{tOg(waozUW-H{E=3U?e|_dp;bV?HCHdkD&je36=1BWB zS6*oM4~B*B)@;;0iYgZ5ln9;u-H?dKI5U<;X2~L0QZ>F8B@iG@;?Kfi1Re-5O$m_D ztf1Soz~;#TPi_&ovU54e>B4jBc3H^r3F6@dtod#|40djZF_j*;chQaD^Lg~g9=MRG>W^WStq|0q-IWa6h5dlUl#yG|DD ziaD50!|x;rCGgGJVv)Jn8KSi!rNKjkgPr+#c}r?*YwI8rnZP*cpo6fnCMDc%SE{?a zmu|mGJRbAJ;<2pk>@26l<5U;2Ht(DV;27{r&-(Rn%NA*H11%`1j(jyFgg-`KNN< zP^ptY6F-*;dA*t5>kRas_#d*yBR_Is}q_#mYcF*y?UGl}*Nt5wugA=Xge z`k^k!4{qMM;VbMS{4D2Jrc9aAD=_^7o5Q(05DLfp2M1Y-)b~WvDbqxk-6`F9`*k2% zo$UIXZqcIA5Su!C7MyzYp|E7d3Wy@GR$W;RE{6sDgM-l5+r#PZq_gq&)fIJpg)|G7 z?bd-d>pmJ|Qt|r<1R-KXGbV9Bt_xwbrxADK^VIpdJ~;P+i{O!m?uGRmcR)@-4%wO! zMqzZO5o98a3~oza01d@%g-PAbdO`jq|bN>DXD< zsK*jpPdTy+t| z0z)ujL?z_dRMz_CYgoOl(2v}IKk@vV?viA89y{~2)AidI-NAp~+wZ&w{`B}Gq}A-8 z+t@c>EnWY~C!hRDW6JXgJ~YA4G@f|kg~B5bnQ;6ehs>Sd+O$Jnvt|v~fzq(J^!a=+ zX3SWjrL{FtS6A=<;QbH6OP7DkSJNA>zAhhf#Ni~sDII(21+aGQdf~;7J_W(4!0L5t z`FKxXZ!g3mA>7nbhTvq9#xD|F|A9s4n1@-UPf^~CNMNG zh`DkILeUuB2W^;ZI7Au+CEyA{hQvuHos^n4Z=O()pP&2t#~#q%C&L} za_}rjj)~*Oc^>%f9S(99yXx9o6QRC=#I={5Ba9h03Idvq4f!?5^X5QKZVq>}Fioim za6piw58B$=p`ob-T08rpV@QMEqzhv9VsICXh4K*-p{Sq$91@LaBrunAK`EZIa72Yo zn`@w}s~G`!o!$4_J0LqJo85Q!eeBxb+#)z_F72DIzGh=5Pqef(H7JVJB4y>|NU?Z4 zuyyO!R}zWP3)q18*;(9XV$Y8~3V@I#$%cIk9}`LD9yl48oQsH#NwfQcD6v?Kca9s2 zit^WG5W!6B7HstR>S!b$iH3qfQITvmOe(VNHd}E)QGw6lb(3xsujW-%6Jn7tiwnxL_S zfC#)M2?}Yx|Lm^)@#8^g@9nkZRTQfI_{(+r`^ZW(yQgm;JEdyY(N*P;>ye;o=SCRl z+{yGzx~70^b~f*)mOoo>akcLuffEpkVLM)?G#`L z3;v;@L9Ss!txno!8MzAZ`FaF@ap7A;V-7wo_(z}{{TtW6NQE005X7Y%PcaiXV|e{g zQmt@m{Xl02oOtAcP*ptFj2n$N1LtDt6Et^VDz@3rtyICaNuzlmzP z5V_{2ds9hOWxxIHBB`>h$R3M_C70bJ_Vo`zUE@x@YD5KKw@x1l1h>nwZI7tzWQ%DC z`5Kr+T7hR60A?C5){K4I0E?+xESd_GWV0dBNS$Z^5F`LBSu7kZ<{f96azhkNp-qCy zLERiPqJay;4}--LA$)6<@{+XV>Q62v&6+hddg8>sR4T@AC?9$!i*{5Gn?_P8p9cnw`MEX}>eY5(_H{N_oQKUB>ee~X+oK5k6G-_&UEGyU6&i}<} zXPy`i`16;4yGHNr>S1)RwMhyaGh!qJh6YnDP0fL?7B7yjSiO?pUmttq5$U8exNWX< z?gclo&p!AXUVZBuC@U|5ZxCm_7 z(SV;}1o(1Cz@|;D@T(ghfm2UA4&OVS6y}0&&5uaEn38pN)4M&9FREn*khx2 z-+g=ic^6&OHetfV;?ZNq%+1No9!Xx`iehKkIXPHAL>=dzb(-s>6Hc?Xwrokg^ztiF zZ+@0`&S}R8IoY{lURgE7F(35g7hyBXirF0Fx<)-c{m|5g$26=%ka;0#uL8k465J(Y zp|ENU6qXcmVP~exTpz3u&sSktA$0ZkLt|qrbhI}@AkfV2`t40ZR!*+^@ZG;l-FoM( zf<<=di$DELn>KeAv^F;2tymJ=Zm;CW4nsp@<4fh0rB5JG)CHNy#7{8xC;%cFRUJt! z>6Y!dU6T+X-u`u!MxJ;AfqRT7VCSO0vb=ot15Z8mFR^ON#7{QZ)bZn@et!V-eogUt zd=8HX0SJdB9|4FgtDR(mh(4|pD!}5gDCMYvMUgohkva^-h=5^Vc^yo{d4qk!=QinW zj8O}t$7axesZ%Tn=|-9HGwL*0$!UPPZ~Jk8&@v;;)q#r^(AM+)Jc=z(ceAJ#!Q$6 z&%E?9J7Ayr`jRh~2#-JY2dS_q*H8z=BqSbBQn@1GvB!>jup76GC)!)L|IY0ewwi)g zKkLA?kh0mbzjQb}A0^_ket&RCqC!_A<#ek})Iw45H`iVP1=SOUhwr%`w(e{o+hVxq z)|;TS+YehBx}dDG8Vd4qxGkJN=!X;*duB$=FeVgY627muz&q#7H{bkUDgLg#@zx0^EZFCSKrlGw+wWHk+jndOm&+whoH$YJ z?(U91|Cbkrni}dtzx?Gd)gzBMhAmsVTz}>-PcZ6qO_?--?X&MZXlrj_t5>dpts86f zv(G#;e&{jB4PY0sZ~dmN(YbT?gLBS31G{aijK@RQ@E6^-Z9DD@RyKM}HI*JiSbTgV z2+5T{1qJ&5sjv!Gf~iCmH%d{K5DWwZ>=g~+Q`%d}zK%wA^z@pe|M9)l03q1FG4LP^ zb9LCRFO#8@wvH9VUF?VSBe|v&*&UM81%3uBM1YuY(k`yVu=O#R*2}uCk_!f>d#kGY zlgzc0E=Wu)uL=l~OwAa!y=JF)HucmDP!y!g!X!lK*n!8PrG0K1k2ZbA6#a({tHsfyN9#+MF3~T%_+0uuNjoR`!t*aK`|}fjesbE( zDIYxi@WVe_TUd$*{`lhLQ%^ts)SMjOxK%4RVi&uWpMSDWtgfmSQt_19RNok0y=G+Q{aWjyA4IavM^UDz~GRB}pgkht7^p z=>Bxqea~8r!pRI%03yy(1Zo3G7YK(5Qv~)oKKtC^t zX&fmxJDW!BQag6+2w*WA{q?VZoxC%mz59Xi`)~8L6;vOtT{li=VK!&4=k=CaORXlP7yXt3W~wy z_Hy7oiM0y3%T!d9LwRX2^x*l61_G?Lu@-i2U&|hT_)ej)xLAMu{@>}>-+2>)LyqL9 zAAXeFcm952Pe(iA(2C&A@`{O+n(XfDd84hP?Xj_A$L=w}bSCz^*rNc5o$cMWKp^7m z?QG#H{|VffgIJs;q6sF6q@$!W|G>abY-BIF`LfG?($;yI_`eOh5pCI4A0IPj6sGyA zj1{`Y?sB{APKP%qC)bGufJKmWY&b{)$tQ%uL4mx|EfyOel}K`tGNM6Bqz%D^qKw&O zW70t!cE>OZGs&d#@?s4W40^n5f)vz|vtn}LcDXral4!4(URRE+ z#dXUE@4W$kdFii=VZyZV(EZ_*6OV!;4?i4A%B$erH$TwFPnf0Mbla^kVcayf_|wnz z&%gRq*#F@DL~}e`P?Jd^5se{EDNEsKII(T}_T`b#{(_3H}qMrScv`ls@nxB@+ME60T-WlE_}0cE5z_| zg$0Eq2nxYanDiGoP)7PFIv3|N3P2iH!qu*m^MJ%b1%u_Io1;adF}TcUjfp)$Gxd~! z8=#%xQb40befbLVZ)(%cD!MMeB~F@4|hn$S3DsI;tD81(mx{vp3Z zvD(H40)c~0Irfw)_#ckO#>Rs3g6xwJ2ARKQ!$uo+47o)8jG5D+yu6%!^3g}h*Is)m zSyECWJo)64wnRcz&b#0$-Ra2_aRIZrb7nDz(*P2f*B7e>7yl$3!@yX}*Py`^ryp z45PWZodr9)V&of`O6qLE;U|a}UUIW|+-b)Ox88EK@Yv&z!M*oC%pGe^KK}|fZl8tv z;w8&zKZRTFy#rhhH{5;KZ}oL+*TKAb^GM5I9vTYxAAkIbcfb1j>qqD8GxMV>ue@>q ze#UYC{ZD317*l=Bf`extyt$@E-mtY+Pie-eLs@aD;BvU2v7t%bxpQay%dftcusWAd zKJHlI_YXcoVvM%uU;6;=y5}Ld^TCJV-~}_`^Cin*5DWYm0uChRtES=zrp7os5?dJ} zu5QBJh{ed86{N?YAS6cv#1@%>-KKz`NSp;J>FF^ygQ-I!nl?#+us;A@ot=C?&GvZ_ zaBx7FTu5;548&A8QBu!0!)&#pWR3Lh$*rK(<(- zpMUz<;Gu^cl3KiYF_e~+sM~65bv)lY5{blf%a$$sdQSl7(=m7M+~A{+KKI_hK>w?5 zpRXmBNLok}N)cqyZgHVme05YX`8I?F+HmVnJm8V^N~l~ahoLpJ7a3fHcoMv}wnf}#Q+_&gRv^b_&* zO+Tz*YnFeZKl%9mcwebhpLp!C_*J*utjU5UvG{{`qlX-Eu-M<(A*s41IbALh@1c=~ zhNf5XzW2zVJq-Xs=Kx{-5=7Mo(R<>lqr+-@J{EZ1)^f*>UDJ9SU4CG!r`zO3B_kG>T3@1U*OQpq;?k4gUUKIP16t;I>=g;w!J`09+O()?m_(IS7c)rP)}o z+aol$v>kY;8CGj4AHG<#({C7Z`5_Gm()9D%rPUePGhP3=3ldAIJ&70DC2q z!C-tDd#$0pLt}0RFf0xOV(dqT8N>VrV+ZA3X&Lv86pJV0|7Hfo0S=CK=mLQcSg8FZ z0SJqwM~Lbv2w~e))tI0sG_kL9mEQXN3g>W zJ5+6J>q%XH<=y&*%^TS5cibu4+Mjrwd>b2a-G8Sk9(*Ws;etlS9iPK*50Nr{`_+(5{pTrM~x7k zfB9uZ_yxzxHMPP;7yKHIIsY;^^W0*!#6~=vY2oi}XuG2VoOyIx+Y3h(Q zHy8kfMHcx8ryv;22NE?T-3J0(Qsg@9P6A<_@`e`CKHs zqoW(a+#pPtG7X&0d|0|<9o&7}RWNf*0mQIVANJ!q2u1iQO!1;HP>WMrP(IXx~Y%+Cd{+YX`N0G?9@pMCT>Te0Lz_QErd!KhJV z)IUG@Wc$(*a5+Se#|v&}wkUDSOpQAVat(-B+@SqR#<&H!#tEhsA+Z+Hoj9|> z4rxI?gY27DUN_&ErHeG`;>moBf)Woci3tc6T6V?4VFe8n#dtEtXPkM5O(4Ap1rk`e zVfx5tM0qEWcjP2U_vUz~Pn3B_&d9?>F&>ZoBopF)EB^B8(juqba`41aS<`G*uTvCM z4G|)7zk~K;SN-ZY`tKjU6FR$^g}%N)?T6Jr@aM>QgP$~UHr&1FK6dKq#|loDgGw%;_=Amg@w7@um=YTi7j8h{ktqr-e;Yi1BG69 zc6M&AM|J6Tk$mT^HUaLr<0^g0*PrTFUiWM9kV9rces->S+x6GNxmR2T4Q+myICdO# z4f!D)k6_{&0Ix3_*I^Q(nC#jy@g>!EJuL<}3`k(c#=$hrxHfd-`p`u_RK!8HD6VnR zL7_EGnXQOGTNuHi!eSd-dHDr!$KCh9Utc*H&OPfyh~v4VM=@Q(bC?3vqCs(KDcin% zCvsapN5w+&05sd)~E!tj2knW=-z`)x0UW= z-~1eD&;MR1gf`;RK|nw!VO~ZgX_|f`?Hp0&-9mvxMG%Zo6f5q9iQzGy;nU0aX_dH5 zKnxts9FOC#!LbYzh%jxA1rqo*bxbG-;38{S!qSMLvpZ(YIb;Ik05M;+e?RYG02PA9 zG=bcU@UIraec>kPKAR&ux4;?>$Dp&L4Lml75RXTh%k75uK6qPr`}L3YRjW5b zPj4?w9W?{y&YJ;89CeUTSzVmoPwBWNRvC#8#S^VI=Wh*tE(sZ-S^+?t+?OVtcv>X2k$Fxx5v@l z+Ap4T+_|i3{2bx#+pmWW8@EDhX9t1~F)muI-$~ZXd^rJwHBq0SfD?aj^2Pu_q3{U1E@%rif~SWB>6v1+N-#PQ->gtigl5f6**majNyT`*Uip)qXdHP953I#i}FxL$(%==QY1hAwCZ@%;@v^UiWuf6g#jG8b({o4yK zgfF||SDGN$*vId_uO5EPA?{#*mpz_H$q2YpO4rv=SO3D`p#RCEjykFZGLea&LF_RA z1kG92)VDeYdj}+J4-K}ov_W{N%hlQJ)^nYru2fX@1cQN<8-A$SxX(TlGwg><{6mZ$ zJt^MZQ5W}kvvglp7Tz@MLbfkY@_2lBUsgneZ6I^%r9TibJ7N;2JyIuv7gdC63-EWg z@IU70rj;Jd+?OY~n5}}%ZUIWD$vTs|c#5KM3OfDCJHc4oaq%@~=t$^HHSOmF60PFn z8(Qk`-)Z>o2KgBOE8&Y_GV`+mAZUhvM}6zm$rH!SbUGxDEJ{p}WId(Fn20URb1yto zt83k;9XMg?6!))rz@xt*(9}aJS_67Xr@-tv`RW;OZ-vPnE5UxK2$}AR4 zkX1gHEI1s7BMdHZ!LSc7sqA2~bqqj)rJXT!qiakFHa*WqqJLy}LDR{2?a?(id-`P9 z|A2*X)s>gR@kh;v?7RZ#>+Ys96-cxVK_sv{9SBzBv+k~5F(=#S&C2%88|WKs92^{^ z#HC_z5Djt;i95a;eiM4 z^WpF7I{%`pC95l6sje)8S(C@GiKEMfZQHgB-!J=)DR_8pxb})zcqq_v|AS9;VWDij z_ue~-3kr$~ft!K|(9+r|H8(Y@Q)kR%SWGjEDAZyZ{wc20PS^v3an%ITz;HRC3%6cE zh50w=KO)h>F_)LcSR%mz-Sj##eK+}fqWzjc0)%wUSJusKaQKb>Zlm6X2n)s=^F5u< z*Q?qt(9C?UDD1BP@b|A}tabVf^uAV7Wc>+7EH>m=ylidu?KfU#OC>ZtL1R!-6t6!` zh(;3F4YvxvyzpcZztZAH!=OX@&%yqoD} zuS^&>(Vj|baQvwkLR?P?uRr@3ce&VJR|iR4uWB?#2skba6`KhO(344!xL~T`pklCi zxVVEMtcvT(&||Rh^`tQ)!ANO_E()C&0we60KM>oeudg4!`uG^9&*R~44DH?h(ACoi zC!YKZ1V3chw4oN>{orZvcoYZ@kuYTxQhE}fV@Kf0DYy}Qc>Sf9!k1lnCFUKfR$f}x z|LwAGJqSLKt?`Q(@9m-B1I2KC(B9ryrwb##P-M$=MHI(dEXEA0rfQ-uE5|bbp#75X zzW<5TJ21d1JYHtAx!~(1U&HA~EySLW3}I|?(3MZjKf1qh`6Jwp$Yztym&av;9G?q3 zn2yPe1bE}{^pk(mBjG-H9cVB%aa?XX9L`A_8|KObup?#HP+wmtCG6Yh#_Hxnd0l1o}WxHOx)<=k+G{M zaobH|Igo=QP z=g?RzIUlbV1^$bH&$Sljdya}lhK}p%Xmeo~Q1|39>Qwt2>Ku_M(9Uj2JyXNd`o2t{u}QZz=wk_Eel9 zK%3xhoQUxf2!ZP_#)y3s35E<{k%!Jlr!-Z^HM~XNw0@m5Ff^dXBeCWq7am!U;KR_* zeZ+OqA|{4{@wp2QKH%s`BvH6w{RX|hp@A=%veHuUd7aSS+N$;S4^{FSp3)^6GWQC!o}2p0MzBArNZt4&Z7G-^TFfGBa+ zgTc1pz=Fx1q+*pO21V1lo7{gAG-8B#Ktf}D&jcpucDqxLMk3JJ-huCtfZS{!AeaDy zfgl2>osd&d2$QBwhFAag9-MaOiE!8~&npdz40g5oOXWEzcq$(Y|cJm5lb-s3=k z$SFZymJQtan%%A4@X!PIfdoPE%{Tr`;6w6_mtPB?a`t&KO&3F7eEdQ5=o1ce1%~>Z z{ymv>C;qq_V;hghTqSWx4_4%Kab|cS}Yh_yF2HM7*)2PFUys) zedjjT-P5B70s(>C#}u;4(-VTjhVTs@uvj!EVF4;?DNTq+l0th+JNth5N`3jaE5zE` zZ6rLPI-HKWqQb)0u>8G#?8NbR6c-nN`^#VcG6H*H;JS;C9W{0-c6YyMX=(LiA}vWa zg@jo3STqbiR}TFCp<8rOwrIb;@g6;Z>&%zsflJT70G6%V20i{Lj2S-?finlWt6+yO zWw3Lw$RkB|5?M0^{L&T}#^{o0x(zU+<7ar->&7)jQf#V7u136L#Ao`c^M}`%-#-X; zIR!V~a6P>B=3DT^XKTQf>*a0)^n7{@F(~Nm3Jdb_HKMG)w_mc`?W1Ff*wF(615@F@ z7=ix&N&bPzDT>8axp~WG)`~z@VPPRmm^hwBPvWn>{Mz95ZJUE9pM08j!imQ?Yqsr> z|M=*i#PSK#<PcRu&>8$)hemhIjL z?kcn>i0+WFBmNTP89(Ir>!U`F5*1kvV}ZB?Kg!4R=U45q1%RTs73R-Avo zp$XjnSZ93SG=xSB;DDveHi54NW;ziWk=`8H-wQnqzwYjshhY9kF!^c*cx&N|v_w2k z&K4l1B`W?t_~#JKEm=^ap%_h;{G?cC_<6)HzFAf}d(Px4Rnr}Byz#ye4hF=Eic-A! zFk;*>Qv0NwiLO62nZ{;C-9He}x7XCs=#!k2pO=ayqD{CD{BY*EXEy8!Aa2R{FDj0h zKl_-nLeC`J7loCp)<9BC3V8iONl`Jhv^GH`5>mHpsZpESIs}))Dg62AX9(Q1y!YNG z!n034D*Wc&$Ata%oeV2~SO=kKjI$qf)c{(_{el39VbFoBiD~45HylK66HDuitiNeo z%fPu*?mVQaNmIyI9NzOtM*yp0=c?3pi^tnPeP*!1rPvmkYdZH*2hKP_MY+a(Q4VV8b_!z4X#w#ZjY2`?OT*yFe)P z*NTdYy{d`l_1aoRi?lKjjdvzBCXywOtXNnonNsG>nQhI@&zEbqb5lL$aXWRZ)yaO? zv>lQHnP(UyV*3M5Nk%J?o0i7zy0>(S}Q-b zJth;G`2WS81wiz+4-E8ne%CwFIoTQ@IRGMGU%k~LbIeCK3WhZ-OKCda^O$9q6*kw(b;${X2!ROCu{()f! zHWh;u7eL!}c`@O~Uqp+fjuOK!N+1%%NT3l9!LaPKV)xgMH!mk8Af%Uw1zjqs8dPA_ z7`;ehca%ox$qQV?q=b)i7)dYhlIWc8XVNs%bIgVkyd)^X&*Dba{Nd_xPMiDi38TkN z?Cov0F%)k2~8KW~QQ$NF-SpJ3XZOtz!qht+fRffAI->@4a_10qd7WjI0uh ziVFNAN00c_;j-W1aJz3j@ZbYK&d<*e!(JYFd3inaX3SaI)!$p&(%nrCh|FS>)f8r@ zdNc%wAAFE@<8N-)>NkA@kNn{UK1~0M(-*>lGbh2@AASkBr6ZxdqynrK>_p@G=Lr`T zgw%absXX!K^!43V+;Df9L`lfsUR3C8iZn_@$#)X3pGYJQh`E6vQ&UmGf=J`nN?_0r z`%Rw#3lBUJF1g|ge(hxCc==2<^#|$Ni^pTw%~K%1Fk9#w?9*ehxU(QHf4{zgzN4C& zntbq|jJ`hR?&$74I=`f3UQKPS)X>h%$*}T9M;tL-+$Nj%F8cyneTPGw!LE-9)&W$bnHeZDae4#zM1 z)s?vuCrv2D=Q^kd8jmNi$g5RLib?SpIy&<$-KJ|nMl7h;h~=9kC~m6=HUr_T4| zv^gwkmWW)K(irLCDkikA>xRY;tz+NsIbIMbOG3MReTcMY3ZibX9=KQtxv*gW9Ls+PXQPm03FD&|Nt#4r z3ds0(nV2;y0|ghd&4&A9JP5u09h{A3v0%X~3*0rUv%LqrSx(&NbK%>s*TOBgT#Nfr z2?RoMNOB8si8--2#<$iHNY$Qv>Pcbij-BLalDzcNi=xM#aFREXNbFd#eEDCdPMx|F z_Ohs|s*3wO&aDUpHzrAbOVb3!LOKvJA;*`i95DL;+^ab&2wZrL2#Unk?req)jlD$Q z#M7nldm@d{C|2AZEIK%IERbL1<^aU9#Y^C^`yPNp4w}kd`0EppomUus;?XBtF1+l* zHc3)?mn>eaFFf{ul2CttemESHy*?M7GE1;#Tf^f+U4bW#J8r-9H78^mteN;Z#hwH} zh%FtBO)Xs=Uo4!xe+wajZ@u-FuyNxK|CuM9xS^ylZ_RnIX4(H)BPcDGwyWEgPF5*I z!!r(SSj<@zve=|#SLB7A(IpXKc=ltrnPt(mver$81AjgG)TZjYF(*x4FoapKpM?X) zmMx71J>5Hwn>}gFVV&)5Ui@9yn6YDo-2808?y~aED0Y-Y>O^9vn-{yGP>|KsY-U?( zHVBfU%Lg2|k34ny#H7pZSU+v%tVgXj$8D9BBi_U$yC-wAHiC{rG#TAkF{1j59SwEO zfnXqIwYqdsQdlyX#Koscx88WUIC|ndc=*BJLwkEaBZrFXt~>{Zf)UubqZ>wzoeEwL zb?X@K;&J-9U=(cRPLZJQA1}s)f3o%r_ZNe9%(x9LjVBRE7scmlWIiLx#uY-E5_o<1 zhkD?OU!4v^gWYiRE%$+JwezCXaN(d#TIZl#UVaYP>{iy>-=kY?mI9~SeWcZDJ(%q_ zr}Q6*`W>tG8!=+UvE7}Wo{gJp^e86w(`U^PvU74-S9f>+!;d`DkVr%ZUw!R0r_by2 z-hJ0S^4itQEt6-?6mlGPHfzRAkgO7H*t|j5vSBkEdhkK&{KF3Ddij;Nw}1T2(%{I- zDW2eO8P0D?F?#{`oQ7bHeMjCj?zg5ai2G zCO`AL9Db;lr;cxmtaI@^kXR|0`y7E0yNol;u?n5-?qvaKA{Y#_&|vs`OfZEG(xSky zi&eqz0lLwtCKeoZvW@pOfQa2WZ^Dn!HY`3QEBsq@xPMU`cJ$Fj6UUC9UbC$&@7d>G z&@R8~5`EUJ@w%i)5KSb61Oj^G#$>bEIST@>E!NoBOjdu2FWaMa_qJ~?F3o$&>vi3~ zbm`KM@iBXZ9qfdn^6@^GvJeZ#@-^$$>a{gBI*BrtmlhF`Uf)@_L&YXVbj_+C!ht|Q zudJ?4U3>ktv`Dpc&;PY9+Oma59=RVH8oHsWtp#GSh{0MwAd9|IY67=ZLc#c#VYMk6 zE-VIMh4+7}f`G2Vzt8tlW^yIT?S-xdYF=n67pkN*giN{u{sHLk?*kIZcH3>>!gGgU z9<+6Kz{oKpVe`61m^p14oO#A^5DG^)&_;l-!{Ojv404rOwQ{xk%4@Gu;-ZcjGdlXf z1HW_Pnjh-v>Ur&z*I!=&dub$ez18b-)gdSxO{7ws_@r1Aa-I;+IpZ{O!G5zvKtO_+ zdm7%y`ut(NrK=b3Nmjlkk>-&NK~fu@ryP$1Mwb;qMNvL{`0m^A)RT|tXZ+$A{SQz5 z9vmK5?4H{ebzS-ED>@yvEdP=vORWbSx^G!Z!WL;_mv@4Y#$AJmLtd*2L=SYH|Gdsv${Y8*MgQ3uG?P zO#FXh{P>YtC_JQ!k}f25Rdo5XAg`!czdN5eFOy5FjB+(qPa{ zC2P9b2%=%nY52|4tDC@sIb)obFVoGTX7aSRSgia{iR(e&4a;W5&Yon_h{3enl(-T+ zy=U5Djy?!=eCaa?rbrv?CYC1f&%-_L1(EA9is=XaWaHDiI_u``jfak?s625X&|el$ z#o35am6!;+X(pEU&*^bA@pDc zmtTJQVJtqk?CAyEe^>HML4QBkts2~X;}!7uqmRN5>$YJ>sR-XI$#oM* zKY~QR2!zNl$Ppru2nz)KqSxb{oQ%g$?Ck6x^&dPm@X05ijLyr;I~#wWNgFqAhM}Q= zURqj?_m>I*UqA5H8}Dwf-Lkpu>g%s{9(M4-CEK^vT7UP@qeA(_X|m5@fpKH2pb{Uu zeftimsi`GVRQ1%8kL&952R1+Y=NFstEn@dScvn_lUQPk7XRfE%*B=x%)og`PqescU z9G{j-YFlj%nL5mS{G2cDZ^;A=PgCO;LC0auNSaD90R$SoGF&T+kvBFxRl&_NOdCv{ zGscX+gJJ)QYe;9tYzMbXHeMII%ZA9Lt}$~4UmSKYGJs;k!kG3^dL0@^NI%wy-2^N} zFhvAOSXHAj1`z=W`!mlx^OIpgkkLqcbKBIpGbc^%Z0mAdcipdH=Bz2Ae{e`da9v1Y zm)GTVf!pO`2(;^A1l-%&S`~-Gh9`k-Z)<6Oee~$j4|+VVKNl7jZka!S{?A8L=>HST zmoIlW*X}%IpP94gG&Q$cmn~ndiGm~)7UV*f$H`jicM29!P#fy%VjH*C@cYO^zk5(} z*xZ&E{_?u;)t4W`T@U{W99aCgO<_&JN9^=v_fCs z0B1>R%mzKZQMms4%fKRQ9K0}uRaF8!_|zQkk=kGX`l8m?Kd2%2;(zS1KP0?fAN2P2 zE?xb@>QDDz(aC=|q_VOBpU=HL7L5(*8X`AL5%2~Hkyu=uF=M=3U79T! zjy(o|cHolE6juxEqF=wqpDLzMmo5E^y$0TX4z7 z>+|Lt*%ESErao*fXU@`_o#I`uPlcJvJ94{#jdXDE!C*{~gau{Z626)uXxdNu+TXEk z{Y*)hPXlHfA4)`^s;XGe$#RRvaoPP?aF-B91w$eclQ7@MWZ$05%PrjDuzUZ~J1}%z ze}CYPS#$UMa&Ogn_>YFmV%bqpRPt$8U(eRwzW$UfE4=#C$i^ZT5sqAN7@T^_Iqb!k zpU}ViZVlg0&pPdRHgaSoEM2n}O3Oz<9ww~uh@S&P1gOz9qpJ*(n2aS8Nj^UGZ^6L3 zgaUVUkrSA#hZ7`jX@~%FMnyc%*)D_qL$Lq6DKK-!G&uKyD|ul_09A@=SL#)&B;uIj zbI?AY2SR}`K18(Qb+(VoZaZ}Q_U-O}@0cuJ{C$3wC+oZsqemS6{qpbRj*f2C>&_8p z&X^&(oR;X?^*^kC=GmuLj2JOS``x_{RNzzW7hQU_&P0b)U0$h=t}KJ;vnD}zcQRusgP6`VbB0VX+%N>e>;sFu@S-m;M|sl2SjK3ZQLqi`Iqb%E zFkxTVT@r^2`Qm!gRL#J`Fjp{M&(!!Cw&pbUW^zIz)i!{sF;>fb)pre0^xpuZKfHCNcvMrM-hRHujl06|(FK+;RQ2`ZtR|)t>vy6YSC} zFNJWxA3yTYBZGf_;kjgG*+hBWx*rP0P98gYsJA~S5>1lhhKTor*tYGp&(_y8JaFQP z3wt3GnfRH<9_4}|a41(`ixw^70$v%=Art?}U{;Hi(p7A==_0u7B+BHaRb^TS3Y-Oj zT_PZrcGCNy&KcLz>jA_8G1e@G5sJIi&nR+U|jG1TCqn9J?piaQKNvZEa_T7GkG~l6YV& z9<_|9s@4jN@div917LL1DRIIs4VjAS*!2+`8yh4n&{C?VZYe7(dj`AV*M}mJ+sBL< z^I>suaX)0@-woU^QVJonBbTv={^eez@+si(zN&5AfI@pF;p57uUE7 zNj+(3px}#9qMeiWxN%RKXY0Dv37w~%|AVYr<3}wWgENW?ie|4^y~YlD91Exb z6~27U7Ut*b4x1G*L`?_`4Op>YCWpXnxW7JSv$^gbJ$m%td_G^(o|a!7f6XG_$nm2O zD=w>=-qP4Ct>3s2JRYA=P*_Bx4nk8?Gi2rZ5}P+}j5W1)%aS4qe|YR6u9|zrmA?@Z zc+PLT*B~2)u?(IoFE=lW?uRR2_Btc3KZsNa1upywJo7S3` z#Tw4BwL68k4vXt3*u-ytPN85pGkQNUC6W+rjH`A}EoBL4Obe{sa^jR=}C( z9F4D;#MgK6y;dd$CBA1o8ex%WK=|~N50pS4C|6fkvD=3xrc;z1}W@G-08q|2U>E|`w@qLSh)*mu@A zXx_MjU3KnhS|T3MKVSTgw%~{bsiwNR@XT5BVqbjwjb)#i^PL+uugEU1Dk&Q3A8^Ey zad3Ma2zJK8&CRWkJ6xW7PdfXgF33bC_JG)<0EkRv;=dDvgE6iGjR}E{X#qGLPOw{T z*F?wlD88|m(zDRrjn zY1+hY7IDVU6Qrt535)sOV!EypjKs!xn_b@n`KNj9_xAR>R;~H|lx&ZDq9jB-Q>Kj4 zt1C-Hr`^VZ91iY?BE#;GhFx&6x2IQWsIM1;!BDT)m-Wu5kz?+{g5fTFsZR)a$ml%$ z=fVNdJ!-_bZ~gwz#-853s3<9j$KfSF8e7SzFk$>ixb2=h^bH#q>wo_9t3#~ria^x8FRix+>sJQ57WlOsn|X=BHis|h{c@yg#m`fkmt zrCs+vc>ma`Q^!o$xW3l@yWc%+8!>shB{$0}&YUq>964&F(AC{7?5L}Q9kmVm5r-TW z8aZmjw#S})X=i7DPwd+3uJz8GIe8RgdJgR`Dt@>P8#by4Hn0)Z6%p(&)>OS7~8J7pPK(0mY6gJCOPBE94m6UoM5xsjTaa;+1Q0;4GRz(&l{e5`zb`@ z&`@c1w(r39uJ%!6mFvy&fZbsc3vzOGVhSM;PM=o^1_MeATk4TWczM9@|9w_g)}v#_ zjQD|oO4!4KfTAl-J9XxMlSV8^BqDh$)^28Gv0GG_3vRoUH8nL7IG{(O@#Lx>*3xQ{ z&pG>-mMIgb+Sjb!B)|UF%fjW?-3S#}Xzr}tieM{(IDn;f{nFT>hf zT6XIEQ}=qIW{R?+tc2ZaO_2pJ&&3Q~EkRMN+Uk{?;%zPMG0CB%=FA!|3@Focj`m;-kv)7_=W06AHSnlRae21ua=}{&E7A$qkg-3+Og;8OO`Em z`+V-a!Tw%EVVU6Z`DhJ9+FDwk+F4)!(1ZySy3LRClWibnA`}17$N&(T$b=b*gdg)u zSrpB!-C`vT28AaI)FmUTBQ`6v!GNC;m7G8VRpmrTbKcmX|BA+pfSLGGq&sI4f35WR z!0unvC6*Nn*LN6}JTrgBY4F_Qlkucm8Xl`KG$rWYiFlHG(^I)^r0C{br%4v(ds6TQ z^iS{xMsvw^bsYynN;}o%VkJ{2kA%vKBJ9jr__3vgjAp`#nu;ACrKPn+Y-(zXyPU42 z)2Gk4e^h0~U6Mt5&*SlAfQSEL&~eLkI&0lt@6w*0-p+(dJOW9JYbhwGn5cvA0uApHt$3&g~4Eu)oIU>mvcfw#ieHM8l9*@VNtE&@EJLPDwIW2JU zrPqUupdrzi>1Xs9S+??`)Mk^xZdYg&1FQ~57N+$JV&TN0_&&D3KMtEVZE~J^_#wx1 z*ELPJ>89I5J^lW8K|!HDYuW_N`;+~je)Zk*2k(D$>p=@nFTUl*YxheglLZ%Fb(<`E za-8Lbh04f^a+o!3GDHxptlzmE)~(+p`W#N}&{2qQ;MD7oq8t0&;w zRTBA%-nXf#O&IhK2{UHRkdmr4U{S>HB7wmTdlQ>)jb4}pFfarx1-{OhL3c2|YmRXdyMZ$$($6;qd__|O zBE1(=fkz`g#l@w}>2eIe1_-+kzZn1ssoon}68s~Gxc2|$`4;{zRxuFUHx>;a$`KkQuyHP zY|q5D)-GY=mhIs7x*#c-9*>TSiimGS8 z8E2jbO)X6@Fc9GTO#(qs4c7`y5;4IjSozW;m01P%A_UPCTw@jzC?qWh0@O%Vl7KR< zTVk;NjJ^ZoOO^lwK4NClBMF2ckRlWe@i8J?#KhXb{ml)n9o-N_5G1Fd7>+n%KYT9< z*S`!Y-2i%Nk412ai&m~(vktm?2iUR4p2$u))Z-0fG7u_b2dgU!Q-v);*Txc2a_uFo}{eaiyKA6hv|M13!z?nHBk6;V zMt}ti8q!K3X91pA3#Kh6Ia?UQlsYBqyvs+)G$zO<0cf{oGBepfA|JsZ_lbBSWvpi2 z;a0&PAQv(XuDjw=_~gA;;Qf!kfuiD4E^JA2{f0#?6_^RG_)bl7>X z=k50k>$lZOqbH6Mvb|15;Df_vfd&L0c5bUfV4+3+#hJ&4Tu$qbNB;0ae^O&b5B~ml z`(}H+^K>odC2$gpZehd5^{le0S}M-Z)e&@S0A2h(KR2u?<@=p zsS*H60(6Y_UK7iehna7tV5Mld8qipXsYr?7D*Vf1>B^cie=vG*etzz_su7h!eqJ8r zW#yz{cNu|oVcoiQZHye44}j-F_2Yg0c9su#xo^Qm*zUU{X~!TXf5{!jn%uERUZuLD{r+jc~xe z2k7@dcrSuZ4tVML*W`O1y44Lr$}=?BCn%DY*{u!|PL2-r4?gGh7Ct(C`t;89=g2fy zGO?#b27t&!Cd`ONb;V+}U{g)ubv zkW*TcGa=H&R62%D-%Nmk(=aJ1p-!8W_Kr9BnHEe^L|Gi3OQzQ~K8w*wNk3Pi4jTgw zcoT;SsPqlN919 zt{EVbg~w1JD73YAbRtOfqBlGH*SWdAdkPD4SL1QoqZ8!+>qteTJ92XhKI``nZA~#v zOX<2u)`L`NsllK$Z{`%?mK*QWKYZsY?W0c@bE?n53l4|5)2G0am8-$w%ZJkJT(A*? z0SkXzaR#MbWC+@++{Ta?Ee89+Frme+5@+Waz6S*CsfLz{k!u4}8BgfR9xy@ImuLWd zsbmxeJG<31iBkvb+p>JG-I1tCOw!;Rj)Aby@QC6OOL`+uuH` z|9<^?>*=SSIpTz4kDP?RXQAm5$+qp-iNIh~pE_v*F+cn^#j>ouy?MjlmT>0h2lS+t ziW6Vl#8OQJfes5}h(j`)!wzBG8^W;|UpI7~`Pdm9Op_ixEZ9e)l-RJ8xPzghXwf1W59w&9&s$trQr4TFozv#9*aCEq!9rI=tTDB9^X4z`9&^jc zDI;&Msv7y_e*5hgf;~Leu4N9pB2Ts}&Jlgx0dd>*dR?(8P*hMz1v(q(?-RUUmtJ4L zgZ1?GNFJA0yy^DaEn136H{SXH8-L(waQMPQ5qzkF05K6ra5o+WjirpH#4c71_i7@h z1Fazn0%k2%1<2`}|Bsdm>D-WwH|>ku!2;J1{wIn+2IUs2n&507$y`mbFpXb?A%Q@I z$K&Qgyxl#$&^P4A9KsGq9C0}KvRt@!lYGq6Dp@#CM*2C#sAz3(hjkk_!x0OPP0g7% zmvnA=J#NnrJGLU4oQdHm&Mip{4XM$Ts_2FtDrhyEwug7t)^yC8JZ}An>Z--7)_nJQ zef`c_QFO#+&zLS=cIoMstFO6KnK^g15Df+NyKcEHHFv**QmfXkQLDQ z&!tWm7c~V_T#SGM5_=4VLxz2oWC%f}ZQTs9G9yXlU0zaE-wlBfhX1>p0HrzLysJnu zIv>;xmPGN#&lP_3VNKnLU9-l<_u1dQ6H6<-8xJShY+Vy%;U_0TsU{MUu-zu9`yV)m zY)G|4G8R|0WJ*yi_;F=%FfbI{w0ZOAHk{Q5!hLU zz}9!cV60yfEuyX(BSiwKm#M0B{q+|>dBtS)ymK$rg29L`U{ZblIcJj2AFN!r70N2A zA=~Q#iJ9~F!Y~BK0SNk{9}sJWm<&XL-{oA8t~+&67@nC1PBTv6wUy+L75QifX`c-F z`(bEs04}-kmoU)V4A)$D7g#M;uvmbLz8UW?@NS~rW+g2MEEWQE8J6IiA9nfW=T6!6 z+b7DKcJ3@(xn}))qsLEvyu7gRvk9Xn_PN|X@wHdqj6ePC6RvBnzkbHyhaWU&pno9u zs_P$=JjDfaaZxr@W6?5w!g&0hRqUL$z^2Vxg#O+x?dEGP?dj@SWEb8BM30C3ZD^G1k#Y-4z%OS8+cxTC$6)`!GF0&Ulu3uysK zfj?b0_fuw$wedN8{2$rk#o{URW9?45GWTBEyG3*VmH6^i09yW~`PQ9x-f1~t->J6x zhMgN9{{8PC{p7R7PekLX)lo!wdwRR09UUDn=N1&)G=2K4*9u$(J$noAm7U#L77O*y za(I0C+v{5Oo}M1PAU})iKQ%TqBJM4an4r3O^Hxn|x^Vuv=ZK|cW%47BKPz>1HjB61 zdMn=VRcL5#;%o;s8N*Y~46#WPMa9Et!DfO&U;}<%p`{@jcAHj<#7AF-DL$MbX;>E< zqdx{AC(hTk0fZqO%wJdHno&AL5PV3*Q~Y_^*&Ys{)i<_4Jf4J#s!=e1{sEjN!O;&! zpT)|V6lB{;K*pN2>p8pZ(#tPrHk*_h3I-c3Hf8g^`|g|3e%LK0&6sX$?dVc;ra(N& z_Y7_8mYS4JQNjpzH)iJ)ELW_l7vFpDr9U+9*zpo#sLO#RclLEP^uF|$r@}L5%nC2M z|K5bAS(2xmaIX5@cS~U317;)e5RtKwr3-kEwb~qFS7%ocx4x$a2Kw$VE-r3?Ok`pY zjSK*hiA1i)YMmL4eFRPkS zx9$2wyNRcXe74@uvC`FeJn@qnRlv`v4q)XtdGfegyF=bsyLJ2LiFkMu{?@U zY^@m@8d{X?abG>6vhroOAY^Q){+A;+H@C}bcP~q%w5`d67E8vHSlg*7GAs`V24#=W zsyzPWL&88`n|{r;cX5Ta(#i@r@{mK|+ZAhx)(m<1d0?USoznUGBRgAKohfxUku~03 zHmqprPktTf=17J~r%&Igj2V2q1{217H19eSofs4HzWyO7D$0d3&-x|2_?O4vhqYV5 zy*? z(PAVXZ}57Y?=Ai6t4*+1#!_-z(P&RF#@dEuC=ICvY#ZBbHn2PFd>leF^g?hwCHb1t z(>|_X>fta0RAA;ZptVJ;2O7`dw!ZW@&19CDNbhc2^e!O@`a@tQfT1#4!xRRqOdkfK zP47@c=hxWSaeabhx7#K7*G6ifH?Zl2r=EJ%>bAW;W6IQZYuBs}ELpm&p}4r@$?e-W z{s;eUIebCe00lhsPEH}ri)z>%a^-XPR zmdBI4;il_}nJ545p+CZrzqkOVP8koIH*Ez3VY%B6+)fM_iN2BJcsgM&%BcU(V93wI`ri>9!zHV{rh{vK3PedUa3v*YCERPq+ zy{NUlhyQ*@9kmdu%8R)mCcQrClHl4!(1J!bV*EN-xpD zpeGWE{(ki6k@wA=J2%68$iyBU82};^nJ`0Di60<=%|6V=HdPx0M&gGghNqc{E20#< zsOKV4%uGH>yFD#(#>|YBzY5!JB*N^l5t0DATq)897~Fv{JuV@n|7|{#?JA%I!AP#@ zvrs3G)7dp4Pu9JZ4s;DL%p zZZ}7^$>}#{|2*$Ctq^#zh)Ejn@bT|+ZV!TknEVF^`|OErWbHs4@}wxtHlO3E4^gxm;EM(t?iMEAsQbjc>j6YV9poUmbbz#TPwaFa4%-%EU>(#~Z!kMg1s#6dS+>s4EAXy#rc(8y?p`#5X_0!vE62YrpBGR)go%R9+I0jY>DGyOr7)Ir;Nq*XSd8z54$NEJE+=$!b_4z{;Bw|{+1~~Q9Q}wJI7ewP~03W<<`}XbKkcr{I^C8PN zXK7B3uaF-{L?V+qNRy{cwpWiDo6RilDw{29N^5Im>UXOfCbV?+Rd;mv=gT%v&M(is zIP0T#zp!oHT4THAw(G6OpLD3*?Y7%75ff8*f<@Uv#xr;y@bBp9>A$DAxbRWDRtF#x znb^bQCp8D2iA?+}!)nzeMV2MSDDe6C#jeo>n#EN4%{vqdaqB8s$-?G^YR>w~MmLtI zlYC%Z<9_p^q8KyD!!cHD$5bYzq8@f=0WWo0~g2BHeFQmsiIR zIAH$4eF48XcEb2ivkMBI7*k!n1~Tz~9wSDK2<&KT`Wox@ac-BZBpM24Ic*k3%%E5z zDR`Vrdg9@`*@VwN6)(E@N@2~a#o){F!9|yx3)lbpH?Xa?4XUd~KqwM|IIT}C9C?A4 zGzvhX6-$!LK?K7=W*2iOT}Yb2r|or9laRWM(O8^Q=&g2zuXBPJ`UeN0+UtRvufGPa zy834L!yjLU>#jQo5^8i<*-{o1t{;(;o5em~{JCCUTBYb(YNFL?U0Azu?|q z4iTDnwm{AHZLo7k9o&2O-7$~1pyTQr&j^ZOu|M&rM@#V7mB-^zD_NXU6aM?4;iEw`hQUlwg5mqnfW4@a3ZTHpFc@9{OcOb9 zY{TMoFro?3M9lcKdU^!U5F$3^GKpr7p$iCtk;{surKRa#KK7RGYWMW?;ANyYop|`M zb!BBG+xJ9k&HqRoG%;tC%PLJxBvfZlU$55N-zSV2H429O16p5ipH@|tueY_eCwDeB zr#uc1+;G#)RxI|VC;#+UcH{}Ci)E!Huyx}`++S2~*G*DH8jFLLGypLH*9JMZ$he+t zis5u2@U^BI8=P*8^^ijl-j|12lC;hYA|!$mddgsNF@xzN4ksi!ciD)9BmDh~a=q9` zmk~H=;|#O?=Nv74CLZucNjFoY>}bCg4pj6{XB zDadw^zm^We#ny2BCKz43l%|UKd1Ng;*YOhxI^5?VS zZCtnh)(rUYuZ_;;=K9{={$);&yP4FtLeYpMDH8SU#Grpbtg5Jx@4ovEVcpuV;DuM- zwfWa)G zzOD{_b=6fYuc)xto0EOu#@gBn>{$ENtgO&8&pacwwg!CbR&E^o$tTMyKl$iY=j5>? z(51cg>gh&uZRhL3(c@fQwLx1mp@cp-IVD_xpi3`p@tMiY4_)}+n zeWUI2E3PV^J!k4@RZV1(eu2fR2(>jk_4>NT)c*S)DA^s>pw((whQ+~(y_x=u$v)|O zXvAF?Ohyi z=9_QcJaeC!TlQA)0f9z)GBPwhE5|)CmQ>~K^>w(mR3Wb*M{jCs)UYd^blTm?Z8ba8 zxSABsIQbV!Sw)5Al^6de4uoRz+2@?g@4EwoLtI;iY_JVq3!{lZ>r%lRG<{hSuvLcF z88dCUMKI)c5oAajV;AI_q7AP+!E|5IOqP!}?3%$r3tYc+&yPo=d{1+GoRGkOcXV{~ zN1S}($@o1ta1Cd39Eh$RdQOTnHFUNS!IK%&X9(lQjb^H*M|54?;j-BpArn7}dGqFF z_4NnKWUJMo;k9qUdzh$`z_HG%t1DcGEjVb(oPB02JngJwjyUDiWA-06cH9gEA4Vn< zx^G8qujRgb|02~l){6V=GZkpVAn|hS0^!ypv6l9Zuj-o`7R{VF>BFn8Ix+)3WMZ$0 z3;>adOn@QMD9Mt=LK!JF*$f(FI-TT+fQl`)XgrFEMgr0nfSPX5Bu!gEqc}=;F?PN7 zKfRNrX_b;m;Wg%vjrTJ~osCh8VG1xcJvfLV@h*rW8_Wk;QXr1q;6x&^>+x(?`m8gf z>qk$;LXVFtI*j21u7?rc^EW$_&GDD@M`#mo1TCzO})Y?M;!z#*a2DD9+)s`BJ_53LpLV> z-!ESUA%8&s?cH|_?QE)Re(Z@SdkYIH;PzW@EXC)S;(mdbt|G+a2`Ym0vE#--adDv< z4n^1F2J~T8R+b<3;y?hBX@aI28hmLLNz-S7&%$X~09eJ6veAr;OlXeXurS`;HWP-| z4Ihg$feg+WlZa;~ap67M3~g4$5cH$s5BFe6l#OvA08@j+crJGx!gVVOH1-D2(%gcN zXZ&@jcr@)1jrZ4ezUR^iP$Izv>qNWV{*&3m1Yr?r9BHr@M?$a{gyN}5UUznGG!ll^ zmL_mJU5F#m?1uKesXFUfNcJw&V;Q4O8#T@Z~W z!Pv8OK6b%yFOzYPvEj0@DFW_05@$ZhxW;4>w`Q=bL1B(rXdIeIHzCZm%8Zc&3}A1_2MU z-|T4bfI|*BOvmj{kA$QBB_%~QpMCaOA7tW3F>n8a%Uj#KDt$hutYiL1AiBVtMIs{t ztk2^VoDQ2T%Eo4a=^$*{&<>w{x<-HO4^L~spdWsD&MAy=W!fvOik(z$)SY$B9}V^e z?>g|neV6|o=$ru`GO@=;27t&!CX5iU|6mtI!GcMFWC~9jKj8mK`@l0sLV7eiOvxy~ zF0dgz5u;`=Yu(N*v zJAHn*`_^k=$>OizsaM~_Yt{u8d>>ggM?J8%&Cbb%^0H#Owrp+f9lq_`>nws57n_}V zHrLk|zx?Xk^QKLmddPyq4)xJHzWTQ>^%p;03WpzkD5Rq8po(FbHgg6R2XPqa9}t?G z+F;%4wd$o8o*kJsXG-@CH{H_{4oB77Z@qDX--kFG5Xz?uUO4UpUMVG9}krUkLsBpD$!1g*o-^he9YPb3J?jzkhwSa^@K+7(;>-~g+uZxCE=CxQyC47WCc z^b~4qw?iNt77szdVcg^i%2yvRmN(Vx6i+zq6x_E|=CPZa+eAO zsuU3{cz?Fwp9FG=>GeT>hz97u35#O-Cx&(jjeBakA=Im>>HUy?rt1>!xiof3KO-?j za8{w9k?Q2K6A?I=cvXW%K7BtP_qFjYWII z;c#PS{Nl%fYeiYPYV(9qqpHVhYLZ{q1OQ>*2gF@@6hTl3po@Amrs^Mlyi)&m#X9}_ zRcpcFmWAKkb~)Z7jlDyZETTUc(OcR&7vnDTz&`s-+N&9Fnb> zTrwZu)lEGHop-aS`-KT6OC}PWTFV&=yLD>Q-3m%*jgbrEMfJ3TIvC0iJn>+LN;dbG zr^MG3J4@-Nwu4}F&?q4mc>|Cr=ehw>n9zly*tnyBjGYgg)yl_1V%SlotU2AYGd?5b zITFu^NdcSdZfiPz2^mH~CjP@gBQl{-=m*hi`O0Q>^u{AZ{Y~gL*}^Y_L@Fp8de9+o z(&-nfFTL#%^J&doK3uP;6WP1mMij4P2sd;~j?0e=h&}0A!n>Yg$a8LQS;D2I#<5fJrBU_G4*S$d@J=(KVI3SctffS`dV=7y^G_sOL}#DVn4!BF_7}0o_uSC!c*q96fnD%$hy}_lZFm3=H79 zNpSFqMlh(rr~OHhG|qgmS{2S#prApUDc_>zkpXdK4=yFV^DGSUegIb+}xvPRPWM;@x-GKL#wu z-ojZcCbUQkgeD2UUtSNL-9r#T06d`$z%|!j06wpS?+RK<5Zl`alCfywU3}iXW5-sk zf=py$FO3WUk%>$gLHB3}b|#(N_l+ac!@Vz>4dn{el(eRXedgJ2|K0T+V~*W?QP}kl zlSyoTFWvhvoR#$(!>idP)U3>YycB>-w^$fD}ZoE^-t1f}* z<4Zt~2Vmm(@sQ(nBM=jWj`mKral^)BTU%@Nfd}si;I-KOn_KVp1K6AoKDembq9~Qg zMAAXFf7r2Gh&0Aa@HnStUCvmU_wLookG>=Rl`Gy+588623!VvV!ba33b! z9>cK3V4)E#A!1P|TV3GFDuB}J2?!eH@|NfJ`t2|j9N@*B0Z6BXfXtbD!pRs%Ka!Fp zWmphDkti<@WvOa>tkdDllJMTx*sue7Iy+#Xw}e8oNB1AS3CQ9S#fcZzHq@p zsi~<&TCr}UaN-%Kf(`ezuFg)*d;&G?!lD}E6$nPy6%nimK!}F$0>8KOAq~=fz;#7X znr3DI#OSgRdkaA@a>>Z@d7z{)9~^cYBohXkG40Mx>Sz$ijT|fv&lVWpgo8Mu# zJ{?IV?;JU@WL1XwkcquHGA(wbOg1=(9UI zG`#WF8!)jtAIb{yptWZZ68IdM)+5$u0s?B>ML=hqT8vki&fN-hW`g9xFm)ab;C$D3 z4gnx|-3$gpc%2F0blE6L1X)2)4%d1^Lmixd)^YH_W6!`-FZ>OzxZn&BlbC0##NvoU zIF5xUzV3iOV6|H8W4pS#w71^=VB9Ugx&DCs+#FXjnuLq4zLRxCH8}C0sgN3Kf#QNf zaA4spu@v<7^uUhVdTrJBYmz6OaBS?bg-81@zx=XzdwaX*;>&(zJ9xi+@^C+}QL<^X zD&m^8>-9)D!VW!bfr7`ZEho$M*}8S>et=A*gHev3O5pCM;Tlj3L=}$}Sg|Xt*eo2E zb$h)`!45O+v()W1ECqyNuz~hi&L%U#Ez;Csrg*O?DhBAQYQ|cj{gD|=HtJ>%@BRdG zk!FblacR*GqRj*OzC3Wa-4HIwh0QHHAQTEJ>vB5nPKl8*lP1YFnc=m;zpMymV8KZojwD`o$)29Rq}?th+#tvC z)2a!(OPA$<>h*fHy1F{Z&(GIq%$PyH7|Rqr0QDWc<#=$XU?U(4@5WkxZx{6SbimHq z&HChVRS0ON^tBt-^L2K{nZJ-+PLJ}$8!reJkB?2CI~xWD2O$^?a!X$|g{PLN`NW{n zX#@kniuYYkWMoEDfrF)7JQ{%q)!4c?t5VbWC@1OD(Cyh~x8VKX7%d~s2d+z^r-tpf zCC+N#o-hb7#5tRi7>>05X&fUEiNet*oeI?>${?1A@;%TPFEUMG=~}_pA4K2|e}6V% z{CExXk5n{~h?JEU_u@VjhD;1ceSIiz$?_$KjT$w3QY0D`Vvz{*d7Z;IATIcj-eWKv zE(LtKG7JqeIQJJPK|!I9|1cU(2#t*m{*scCKMM}&$x*pQZIFpf?B$UGATp5&BP1Gz zP|PU@L!o+?h5{8bl&r?$F~TG8#v}0#8JLN##=B=4*o1#%Lfi4BJob1B&NXQ@xu1HM)-dDw{ZM1heIMJa$v&LQ8N-3 z5=0}LFRo*uVIyYmR6ftde=jb%Pixz5svu5G1v?jVnX=RYwyFX zX|rMW^bx?40Y0{2b>s*dUj7cZ;?_iM--jOiU89#E=`t|Fp@cG5n<&{N~$4|(PM8eLLo^mkKm=@rchQ5)Iqy zB5F#H)bD5x7GAJmL4ve*U=NSAYu73Rq10%b-Bv{-QK4Wg znM5EGAy5_#1(@AtgAMC9h|O&sqTON@F1p|XD}p_i&p!JG<{fw_*xerJZtuZsKgNY{ zxhOT=n{^fU1|4i<;mTdKHF5Z6E()et^)78DT8CDv6%fD$yVb@42n&Il_^fyo_eRYC zzf2WYV~rVXMn0lOVv^j|2mdX}GW7KiaJ`2GhaARp5>YaQG5NkKFjK<{bC^gB0>L25 z%g+;3fvT#CXY+Erf5P+L0h!1|CNcm-CNe?M*ViX$S`upr zD{nP0mZCun5X|yO58_gh=N^{pE-Q`OXgybMO>`naU2N1wq3b8fLi)W-8Pn$IRr89O%-3r`P)gPO~m zi%UBNsc1-YyFJQd58k03a>zpMvMa7rmo9lv$tx@sFF5@q_Pakl361T&P+gb}$=Co4 zM(Dawco&HljGlHapliSomgFpUF5+&oAj}X=?G>GCL+~+a29a3QWFDD8ZUp$pKwlRW zmN?<)BMyQ;{rPG5!|!f`ET0!9j2;8)>bh7$7cp`4xYw@TH0FwnFBDEa<``-n3itf( zFZwHsSHX$L9RXzy9b!Ep@Vf0_w;7$v?G5d)wPw3syK|fVhesby78Df4PdxsNBwk+9 zt+(D}D=99^)mh4k*RYJASYNSfC6ttw!1!_F)&9QzEhWXp?{D0=t`0Ksqj1~psi89Gzo|^XKbgm4mJK* zV&xFX;j}xTsWqS9uT! zQA9*ru&5MB#2ni%7lQD*m(P?UhRD}=}#0I^P z#K)@fU^1a;sTkg?;t`vhyc@%K90D_D%o)J@$Ux2J9lgtzt!wpovK!ml2HNBC#-USB zJ#~*ONCyT6vf6sWlV{JJQGm;Ug(6|*4+L43&!^|+Cyp6A zR$BJW3TSBQf#c8LA3-Y(1_FMFC!z>I#P|q?LDFHkh74gsLo>=?J*I&vu4jYalSt$g zK?Bl;AQmHyN!Z8^Rx+~<-E3@_%pD_VAgvO`bY~&9S)8w5#;rLS$btmDxUh=KOJMTk zNw`LmTrf2ar6Hn+FRSyB%IsMVmbAP3ihmA)nR_W7Z;t$q8riDO?5 z4fek%2-z9%ArqM}BLhHWVlNIx%t(p4J~cHRa(=EwvEW^pX`KGZsKmjWzQA29Xaqvg z4Uoa0NEqF+v{QxocmAqumj&F9oGeVehjbTi_`A%|Mx){@fgL%UY#n~QZgzS_Q$UTs zwrI?JikP@KonA=wCt%vt=@5vgU|?_naIwck zN5}vYg!=k=1-mB}k5Fvg(b;6fek11pvI9X5dn6jSYFZqBuFAv(#p9)X@WFTZj@ZYP zWjuaN|KyYJHJQw*Bv}=75j*Coq{Hn@+GTr+GB_qH$pp5OY|It&6*=P_9X&~pCqKDk z$G53dPB}%*Bt}22h{q$Vf+6(-yTe`BJ4jJ5Pds(M z@Yh$~)XzWX3~}MnN5lJHe+SDq)I#Nv`+?7EhiF^{Es2GjsPRObccD31z~nwKQ;Ox^ zhOa~dA-GG8rt`8fiXa?;Z$6*fP$}2Q_C0`!BkuNy^f;{}-*-1^q+Cma)w$`woo*p>q zq>~URjCE&cdA`D$_xqVMXJ*(){}3q^_g+L(Ns-6Y# z?+E-E;}03^otq2uvVm51;BVt!Xt)N(Qe5H5!eEWyv})(BI!2iG*t^ z%PMMLdG>`ZB?XoBupaye-Fc9I3^z!ZJlv7TnMI*ic@n?`3@R!nhK*W;7Ql zm_R7y0J_0!lsGVj>&fOoKtbfq2zod<_z1(+Eiep12&QhTITdQKE=|TI0Z#_gLE=q` z&fWnyV8J{nDJd~?43R&Uv&3}%SI&SiY&})T%gxm-xR^DKoHE3KV3WHb6FlBrvcwe% zL{6MLd*8!cF1J`;SI_62JZ>K!cTpr^cnl+brBZ8%T-+so!a(f8^RZ(`-KJU7r~bj| zu)XbbmW3b_naIR&WB`av?6pDQLr+gQ`^hI?P37e&LNF9jtyV=KWof#32QkrbyWPA~ zLG#DJskJhnxuouvuA88P$cF9R4A24o0c7AK!KRg*#zoG=VTJ_FAQRs>fgKf^KNe+) z3rNx9Npwm#=a6|aOd1gQ90|K`1VG3R8+dYZ@MkuFKoA0$6qghgfEJCKpT~H6q6AwM znJh#FMY32~O1J!5$EBvG#?{^3T~JzDSyaEHwJ@1V^CP z#EF%a)8*A|4x1h80lSQiH|+A^Z-DQDpH-ACB&nfM=1k-KYEl(6iQ9;zlGu$&M59>B zMx)78G?j=YlJU+!LsV;lU{-G4P&hj1cR0L(=bwK*@cYLe3yz;Kp?_5VxW2~jfuRKp zCdV?M#J?`)&z~QzYw7yfAqtb-uB^kup`g=gbBdS@i!yc&MI~Xq`ML{}Z+-GUTy^br z`hok-)klo15-+^ye7O48H^7>$^|1fUiJ-=k(CLpsR7)!&v*9(*sq|*CXf_ymvJIvq zhG2pen9z$xPaLr>-4wD!5JJG~$mz1<<5fcp5MQUetr5x#OQ9h;2shvNDE#{J3*d|s zj)N^V+hFp932fZBDt^U0_}Gi^hnL@mBac1`=8PzU-p=hH(L6R5bRM4%>&pbRwl?b< zH*D6DiDcsGr~ecS21BXqZn%jqPxo(syU6DCc(YVJVZmOxJ~ZfuRjXIAIdkUd4u_o$ z3=VG3$twH~GNNz)G!h9sX@bPdFfd&LBys>C_Y_lJmu7Tug8>N(IRX;w3O+^@hWA-+ za>$HzBH{ig@oJCpKZ$up4g+LKNEoYCvEd#ma27>+Unk}P6_bKOE*CNa!!i~iHZC+s z4lW=?!QrBj47=&j0*3pBZdiC$j;LVwJ@9+&kv}|QTk`c+`&q1({d2P14jVqf<*5ZG`2BvsJ&{`|rbEPzDvbsxR)rB$LzXPuy@P;gi_KtM@SZt$kTF6>LsR>Q2`gE~KY^UL1JV<&a?HMe)qJoe`|Hbf$@&%X3Vc}ZbWG2XMpq@Lm$ zqnfJg9z)VIYtzUjmr+cwQ;_D5}i5I-LaxEwd4j6E8U7a(-6iMbI zpXTT1uR;4zlG|yDAmjSQ8!G|}3}#>>f`qAgLxUP7v?gg~>4sP@S$7j~fq4vbdpy*b z&|^t8QdnHnzxjtBf{+O#`a(0j)5acEIVQ)GOhj26n>!^%#o%#S@k=%V2O|h(QcZ9a z&o@zzptY?NF}}#^edo@227%Of@SJ5v>@tywzl#h2k%_%I6iL_0efh3rFc@sd&Qc5u zWeso8qQzn-swNlw%E`$gCNKvbsM|(`KOqKYf69ERe@-YcK?rg8e^U~XQng*(4x*on zloTd`RNlp5MR(wT=N%Q^AtBufQy>kCG(j@#HM4SZAr;plmZEN#V7O85Hl)a32mfie z+prNVV)Mn8o0F66O#i&DuFe_?X*q*~-6i<(MksBuxtX0;wmlT(`eDY>Z>$jhM(Pgvxx{Hg7 zySpRZ!AvLl|28(P{%%Lr$g1~>ii$^It~`Ax;1`N=viV|*M-x)%h)Uts8?Q=UedT5P z^@|?T-+Sdr%bcl`#RZ2P44;3p7^aUM38lqZ5XOW!7BoQuv*kzKM1w^{tvg9KNf7_& z+O#{9h_fLK0(}Y#uy8cWzpGfOL!1IDcFV0;wDop(Kv`)4sJ#l@_xq<{;l6XBx~dqq zZr=$$w*#Ji?rr#Z$tpPUg+IL?jh-a;&CNz}!at5)hxr%RYJX)^2S>+Q($WqpIi z-=@;i(hP|9PXY_31fKgO(I0eD79|h?H#Nm#5oH7f2sIEznb|Bhtlv{qrh&uh=0L+R zV71$9wyFOMX;-3w1kpLNG9dKNG>}@IXO9KIG=+@=+nD*Te0WIrD=$u=sn@Jt+-h8#)6pywgg8 zWL{4&Or#L47G}}y9N1G3IPv7=K=tTRTwf@eI9YFNZ-mvWSF+VBR_d$Qtg*JVwiRQq zy%4X-DPD&q)Y(@zAI_LO1wxS!^bT}E z2+xzCrSLc-0D)K;ZwiRUaW@bw1iEC=7$pM(IOVq477J%1lJk$q8yXh#HmREck~qwo zG<0qRV_btqfFvVbr|;89s4;p2d~_oejUZs+fyv`1^Ybfn%@m#AlMRO$ZWzghDih=u zgwM6vY@Ba~?SZhcVRLIf4+P7qb^RZAAy>NC{DY!vk>EiJ8sfk3FYs(i%uv12CIe)ry&O^$3=Pi|Imcg5t2A;`r4YS4Umd$4_J zWFR>K3&ZkgG*%pRBMQa~B`jT{A@R)9PS*eY{2R&lUwsz7JogMZ?9lzKmtXixaqZeI z?1S$%!Y_|L07|ox*zN6s0HPSWfSqba3A$=n+c9n(Yb5NHRI^f!0Ykok3I4ztKYd(`^#Tm&}>$h za@QSqS}`BBt11(jE(u#|cR*uf6Fd3j6B%9?;Z!2^z0+>}%2`q}2$}e&fq(#>uBgg* z4_M=cog^ttBxOB3!y1k2Sai-nP^=EH+TGZl_83JQVVSzYGQ+*aDEK&lD{_E=3-d{& z9ioYec#N~#6o(UwdPBb_sU~=#CX2>?t4qA?fXjf&bjf)2{Zsllbo)F82UnkreTHwDdo%2H^{`a=SWZU-E z)@0jmtIeEj+qT>6+6|j)vu)e1&905lyg%RPPnaKO=Dg0i@9QGle{Q5utxO>fw`A2K zd)8vez4@8>(Khy#q>hR?o=6yGC^oyA%L%yu|X|Clr3}Q9z zHAXTzZ@K9cf7%`|s)K)*5x(CEd$aVrU$qdEHKSpq{~8t_&jtU8ve!C;aVxe1`NTkQ zCU4)3z!uSQqBrXo>l}!!?Xo9I2NA>}j$QA2m?DB84lS4HPP7VKLdnIN!^AK{nMM-g zFgr<``5|(wvTv__dL0=ST0=m5OXZmr*R3TK`K$J-_WBEgzGVq?m?A1*qv!_cF?ZXR zTarajv(M2D@TWkR=`kR*UGXN7!FTP8zDm3Fxxa~c2}bDHN+!H(VN%h`2q`EAk#Dy3 z^g7Qi&MB1sQlYLy{rA4!VX(}<`tP5E)>w?YX=4p|f(hXNZ`Jv@W@&GaYm$_~{X@<& zsVne%8Ue!kH}f$|rDQ})ISDVgICez!t;2%8Ov%Yrkc-m%3}td`x1(si{@ob)`BFIB zXaB1Mtt3|!ndmvh51(kdN#4Fj*a7=<#0Hn)Fvc5J#8Ov)xOv6^|ABWM5%=*uLF)Uvz=_jX^%XNY%P280ml@**9OUx&xpjlpIC{k_+ zY-n8|Xz)>uj#=QLLm(puV@>rH%gkBn@fF~V1a6A5IZB06dLeND^|=jkIKM~!7J$_P z)CiHaJKf*&e4S8u^R!GdgG#A?kb5(>qOR1nrKW0$O>Zr&q-?IOMO~4Bh^(7Psr_%i zImr5pdYEZzFJ~l~Sbx6l84-Mh2DxBGx>=c`Km4}t9a7i| zYp>+O{7B0NkoU;QqMt`PU^E{BQalg5tFNB21zoT;h0I`)(eOh?`Z09EIP7{8ch4y& z(SsvT+2`N7Vf0jb+-aT*5qUY~D7^e9Mz2eMcDs^F*)u_O_#Th(?qx79aC}TIIwd4T zd|ggXuB#`ZES9|)I=m-}b%S73jdH7jm z>pS5qHG(wT_k&=60U2FL{@#k}+jpt7O7)f)Kt1SNMLP+6$!v$9Htl|YN(*WKE`5uD z{WV{%sT^cTOV?VPccHau7_T>6OBeCX!UDv!@DriHbO(z|$1XoHIZeThW3&;ivGds20bGnF_z8d6tS5mUWd0WG zYZnm$2^cGeN2SWBB20CRllqR$NZ&GrvkFVs;$Hk_2YoVpT%|trR;%_~Tn8|AaBWtbRBIPau-YVvN z>4ebE!}t7ZZrfyYFCPv5CCeu2%kV@ROoH$O;r3SfSl;9F5wX@)Lc~X;r`W zGbkcDchj%dY@KW6c*lf1{Ab%zT;&bh2SZot8kVOabLaL1P=y?y*G+L}zbVdYrKAOSHRVw#1}b0s-+6!c`pf3q z8JlZG3ufm2Z=^9GDpY}2Lt8D{RV?Y=c9@*}V?0T1m9r%Gaa~VoSz)wsf^vl*EJ+YQmDQ$HIw=RxXcnI$watMqS4$xh~zrv zZ1s_XX(`8HIwdjFn;2Rulv6mY@Tgnz4sNyExc}pc%j^~|#WLArb?Wncf4$O-M#3dc z4jBL1d@zyAK95>*NK0fHLUAvp?s* z{rF$qk)ZAFlg^c>Oe__4b52~>a~9J_f%|CfuK);xhMo)VlF;38r?n6R8DvV(UO9#& zEW~HQ2d8o)^_rB$wuBpm$^g)df)v3atfQ6y@5MqS1W-|boATh7^DX00nCtKm!+sV( zwUV*~YucZXxQc3alf8+Hj!ceo4Zz;q`M)o6JFZ!k_Ww*bm|&de$K6DO7m75YQ_?h_ z&f3plcS!@99I3AlMD1gj_M597oHUu>Hau_Dc|sNS!rwnqzy}(B^osVNG6F*U;Q8qdvR*Gk-%_83Pw6IImQa( zfk@Rd`q2=9d@Ljb7+aw*d?B&J5jm@a6%3HaG+;Mz1Zo`ogE;;1@0Cny+iU;fWcasc zDdO)f{BJr2-jd=D9=p*vM20IC`Q^g0KLbvkJsuto4~K8%a!@OK)V_SBD{zt@1z~}= zpSLY~Z|3@{28Egl#8d${511GUl#Df`Fu8()=%Tf7>L7Gr$MU!FAT3rt51~RgsYwJ; zAV4V!!9*N5R+LEc+cH=HmR#gXqS^vl%v*I9KpyKGG-aaW@V-y1w7q+YW?5Vmm*qwB{WjUB&uv~aTfH%GaQ**36$G9aW6iTnzfaY$+pKX zDl~(za&wzU>BsNo{JK47r`1G+1Zr_;KHEw?k}NR{Nd`fY&^sM;?E+Xbxb*HFPJ`i@ zdQ%+LM0!|*RX zOZq>~gU)Gv!yFt7NSnDNAT>3r^OY{~larGWOL|ngd{@{U?;`^6+AOAKAJ(uM4eLt? z-BYgw*@5PM>3xifz@!Fh?<^YkWj!FSN z4|+MvwpXdmx_7D&o+1E#TlG)`z_rtR(HJxyOdh%k#cxdrW{xbSz!!XM+)d96CAK_f zokh|PR(D^**0nSysswqXoYqx$l9w zY+tQ@Obl!r`Da$JLXkxj7ftQnBP#Tv3gIr7L{Vv@h@_OmN@hX8of4O>vYt!Zvh8Su z15_}8)~vnrJeNOjB*Ydt_?taxI^lGD8SL#WA%$@Zb1$k?zG)!i{5Y&Ne*X`rJNVw5 z;XpayU%SS1z5Z%1ohVK{BN8bgKBqR_RhI8PCcDIxF#iSLwRQ861hx*}O4LYo zyIIu61_}B8iNX;l)RJ;Y5@4lIR~J|Um3pRGo9ZMyvszmXvm9IyfOVZM!jsAV&vF5Z zCu3usTZ9h}%H;l4N3?FO$J)c$X)(!UxIgyq{B94gn$b&RRLeGhMn7#xaf!%(EQu;k z6p%pSN_ESl)W4JB_&VXcbUG8|?hj+8FDr6+KD&o8TRs7GS2Y2CSLH6#f`yR&-V{Sc zQ3B1&c{OJY2v;-!egv-H;*GN{G1Mtz@BthCaOX9QTli3M-AfV@p+Wu|?Wh>TgQrl%P*rNRP zDK&yX?5hSRo?PH5EyyV|AijU6LcgvhtR|>vor5u%#PT#hkt$oz>Uk~I9sU&*E}~me z*+^aM{czgpZ3?zMZl_)^CASBJxUH9e|GAqq{A&O4V7E=` zxf8UVm7MDywPdn>UmNGsS)S8^}#nP+8!^wT=6Nk zGyrL!5pZIu=3VsKOK|UCbpOx@xTUG1^M>-_OJijrLi^F(t0mJ9?qkh$;O_TZcO)Ls zrEmyGp2lxC@@|sz`x$w70kaFC5>Tt^gk5G0Py@$^;mN_@a@sW8=sScC{v3r8Pe`jj zx)=q?PKKp^=w*z0?IRX# zG{o%Kl{`V_XYr!vt2}S(me8U@G`C_CCPji z$FqJ+xbnSP9U6VJ@PKzw@?Occl$7$8mCf0rCs)^=v6}AC=G)s)-q(o`81Se{T<0i+ zJFfvSQ_tYD;y_LfH`-4lZ1ugj{I}ZOWxIMjh)3MK>CJDyeavs@{qLt5m&0mVjdsJo z=>`jB`_Oz1dhsBYpyB=dWLj}VV~^yq+SMx0r$m@`$z5`&aL1hv=v1(u$=cQ`yl)N@ z|J*L@L`8UnY4AZt05i@2f2Rxd2d!Nv>PT?=u$GXS1`4@>5=GQ%KcwWRc-#%8P`)Ju z+?&tP8Yf=+jiUKmHha6wQ!lY=N!_?*>`<_;ipID2CmFf`r>-+6R{4GClY#r*GrIV* z^seI@&ylHY8k5nI@yl2{JKGJL2Qqpf8ok!r>heCZQmK|8C^UFEPX<{z?}e3_N7ldq zN?-XxKi61)Uh@DCr?cVcqFGq*tg>MZoNEEM4fy_VWmAw{D|4#5@;2AlA}LRcU{?$O z3saaS8HM0+H$HR`6iYAw{*57sZIjt)C<%8o)!C=mEL8LW#g+HE_RFs|aAF_-6+Pv% zNJA;H5ju@j^ridTL@M?CO$o25xA3_bE9O#a9iz0XDki9ZD&NS1j~p){YWX;fSm81T z5s92zn5^E9c}lua4SV=&@_@WM+she8*;@Y<5mH zV-O+)?;V=d)mAd-a^LRsun7tAJG~5S;Hv+;w9WslB&yvD0k%QJrLO0K-aoMOe-@A<%W|*1 zb*$_&iXHgW&0mCpbF+Gk{wn+GsRYkTIy{ zzOop#=0opuVT0AS5WQ@1%>?$Wvoh%3KI;_tv`zy6iQ?yfDXgdhSkEHM=$k~Qz6OX_ zBvIIqI0y4qA#ZQV9IX<=`#nX4z!=G55M>e=7xId1;PdXrW7kZ#cAL1#&nH)9dxl=$Z;|uXX*j1u@VUqD6ppM*)3HyMF1O<{f(&z7__Nt!O87ID z>p4n7T6*NKlLg-D*WeH^u`yM2AZI&(M(Bln$ONI+xIq?+P64;sev7iqnpgmpKNPlI zL|Nh=Jb6@tjzkE>=r9Y-4j~#gG5xSlZNHcTBIF#R(ooaSD=w}kn=3Ys0wZok29i0X z)J!S@;uZ3k??5KO6l-fO%O4r+yu7?B8V2#lTDyIbAa9Ad?pXWC-+O%ID8!(bl?(T8 zYMdC$Uh0~QAIZ0sorHnpl50mxLmB;FkCB4Y#JS296{{*(#``-+6C^IEa4>{Ax)< zq*YjDTB$I=R4q^xm;8ATgLNAb|ghc8KBibCEZmYVYRbq<>4nckD*15x?Mu4wcqdOm3N=_ zl|zx3TLJ^RJ3HaF{2a=C=VXt2XwvgamGfD@kB#1qWKzxYNGMBlH#Rkis+nMc z$*N+S7Id!eZjQ}~B1Z5)r^9`$`&AB?qcypEgWrXHQ!y~)daCICf02t*4GFM@S|57pohAR4ZXs$7WAP6BF3ocNT?0)a0lB?HgC!PrG9)=n!b7((%IGe|URAk+y$En%eO;6ZLK{ zoZt67wiiBc_ne}Trg`W6Unje9Q3Y?(ZEx!19EXB4XSi38f&lssE0U?4m-V29ygB>6 zRM$};tKYkdBX{imxg_76h16bxSq?=}^m5EVCcj33Y%KOic0+;;ZhUd1R^@KKJzVS7 z&YzgGOhiO)1I0;=!n&Vvg4ZKtb&li8n_lCJb$MM~2MguwD-5N%A~+EWK9Yu?5W3y| z?CP3v*wXQp;ay!jzd8h_V^F0(G{FB_^o?CY`tnf-iKcPlx-B<*d?2W8B=s%DUup`M zv@5+wBsXc)OD2@_GY>>_EgPOMvkT*I3@F5#6Z7#VUjRsnckLDDAn_Yj0nl=?74 zooGME*dnurBwbqPouH;0B>Fnq$a=MLuzmAugkSL`enSoC`ZGh745~CYK*nq_Z_xSd ztkaAeG5Fh+SM3G|4Z{2bT0A0Wc)yvBNEa*LGGzXJxzZ>FiSxpC;oU!J1)L?~Hz-TtF;1gcK~L`^7FV3wMpsYlWEIhbzbq17W6o8*aqjj_|y z*(WQ~YP6!G^S6s}^w&|+z?Db)5EjZ4ZCpsfi+vn?AO{X;StPMV9$IY0FUNM7K5X}e z3g3sqD(t#LG{lkzQIcjcaX1?_*tnnO99AUpHEn9bhh0$Gtg&?VDnb99-rwyYJ_k3b zlgCl`s;Z2Bo7KDVr_b%eH+juUE#6c)t1w>P6uSMoyO)q4X4^eqJCBuZ0zv14Ww6kh z;=kzq&35$vQZ;-)uK|1IvE*nP{V1(5n#5%yYM9eG`5cXM!oF`t&t56@JW*eN$)--+ zU#2m^()S`2Z4$hm6j8@Rd{BGkj z^3Q%Y<9vR!0NyFe9Be}s&z4Q-9I!q*N!$4ND8j*ws@>3z987w8`oOtl)%xj!9Vqwj zK**F<&a$vJk!OcJth;ZBv-G6J@T%atm)Ag@}S?b zM&Eg+X0NO?85~*nXJN-d_Vfl(b9Mc;=lWM60n%fApZ*#bL$AYk;T*T68(ng&&#DYA zH(Pn8>aN|J7HO3Grz>_2p3uSQ9|)MZ@r7kSYg;B-Px6RB-2!(|4{bK~-xBm3;Tdbn zFsFsyO&ijB_VVVU~yuTE$h0eeuf>|vJX`C@xb(S7z}Kib9cKjzQL z5yQubf_(X`qR8=$htDG0GNn9LJUjB_K&*us$zQ2A()Z*ZxME@&e7g)=vlR1qw7YNX zV~ibZb$u=e)8QG^Yg&(VW-r{eRjpM^mUSDY$A%+X?H~pGnN9c&#)jXaEaIJhuS$s+cQvQ7eO4FMtZLIG@;cWM7M@a&I>3WfsJ;6hM$yigYPO$IPbw1;=_==BaVr z6_FoBhM}33KD0aFgk@f*_&Xrbrn*&@n3ymW3@0aneS3d?8BEO1r(3xZe0gohDEN)T z$oNIuFd)6wzCZl-#)K{>lx3#XbToPScsl=+1yBFSeFokY%WD0iIcpP2&@L~Yr>OF0 z&nfBEE)-Pso(g&F)2#V*+ooQ}w_mSaxOYzULHFerF*~t}Px8r4~Z?$Y}Y#hOBUybZZ|Hsqs zXG5|0+MC*{8n@>|4>pE+;C7RXlqnln>z4mN&QB-TwKzkF|K3ltaG?? zdxVTam13p&lyEA5+sSoITfHeX9&&hHU^9VB;hSI!QtnC50546-hco70hPp%%#=ID+ zC_K~9g$CStnl%DKd7_$uI={#rr)HDs}Lsuf&-|C3Tv6{h^{Azq~5&+SC zeIC`yVKxZR$|99j6uNub+Xp!k3?`5RTjH7`G(UG?J%zd=yde0CQ84u$>tQThB??SC zi9E`06f~+`QBwHd{xVX@3d>kbxKY=4qMPkTJVRM)e8!7tjJt=LhT*FXsKM-0JmOl8 z2OlchQTaHcR1=%VLlbiJEbS1Ff=0ByubZle;Eil;d%&xTzmoA`CSAU}^_I_--{Y|Q zmqrfu;?He(g(+)3eodT_F`ri6)*qfzj2rgc{bKQhs>pif85j3h%1Tu&zWQ&Y!a_OE zi-*mTR~=6|7g^&hi9-LLT^@%bVs^6_r$W!cZsb80^jhe0Z{QQ1hwvo6186P*LU7+M zjLZ+Y|9vp$j(Zr`m^!s8vv9c7dT1MM*ss2Y2`5&fRA>$ikBw24ycRtoH#Jd%#*Os% zd|ra<3EEl9&-de8oL$Y$;qd?ExQ8&Dq!Ohll2~i*gQDByTkgKWI?NzB*owUT@&&LU zBynwAa|!&w3wC0|_FCFGDx105l_(z{5 zj;vmUR2p?|GChEnytxedS8FF2atO(_*#z(H?PJva{s@_{40AYWwxV4VUNJ{my{Teo zi1s_ZCgkPO9!!H23lt3tibRB=p)%^-9Ts)^CRDmA9kq#Twu2^F*lGv2aA_G--NdXS zPEnwn&OZ)0NL8`3!-~WUBiv1&efZ)WYHNYputu$)B++Z!9JpDYh{86HNl5*Gz1kf} zDJwyN$zzkSE*L#w{s%vU+g~_ST@v66NFN@hmNebmgr3>j3VKo?Iy*(}Z?YTT>G4CA zDA+(WYhr^>BC(`oJ~+g!lc8n?E5@zfz}_+?w6K1C&s={7_`YuJKqQG`=7{;dS{W#h zMGfkppS6L_d%SSPUgTibDw(hutc|@rUh)mO>8k*|xg>f9YY%f#%D{djGj`XL3}Q9a zXx|fna63t%#}5x^jr?tdmZ*|zIwN6gF;=F5`fso62|SRBmU25Ad%i9# zH0y>nEDW3oHoI|Cpp^Z4qXLE~7Lcn16B5-qhGwuRB+KTD{bEf$=-DwE(N}L=4k>OC zf_@&Bdf_Ma7#_RQ9Io%kdx?%tR=(C{*&*G6`LA2}G5mikr#z#}epup6pHIgz&$cmvSgtx5s ze7%nbenlk&&7ebJ<08mK$Dr%2AoKns9T}!(6u=HoWJmtLM2>7kXEj3>B0}FApov-DsD%U2tf%1qouS7Gs98o~ZkH!F59{B>j+7P6 z6>iT>HZD$>jCGNmKoxL}9W=oJTLr>N)|+qk5?$F}fJq)&#EA+g*Aw}Mfeu}IuyB*x zWT-rjs0uj(xQ1$h9h7v$8Qf^JO4%cP?QL~hD-}-Ru%Yz6Rj>C98un&flm-uV zpau-=ta5%dX6pgyM9)D4->_l=5mtSXwbwBx=v2=zW8_i)B~f-6VVCJPFNl4A&;sKP zv0wlw56xSi>7-)}@I%21P|C&yBUlTLZ(+9P>98(qy5jNBd)NHsd>#u`9 z$*NH}Mqy90>V}JCK;RIGYG33jVu+ViuEi?)BUXl}6L+*=2%_>UMzbhx=HC&MXy|_X zfL~9hA9_g%F~(}dd$PF+Mu|e9xx#YX1AuvEXr_PmBDKm|BaFse;$b_fay=2m{l1-w)s|9F5Yd0xqp()A&SOGWm zjgPL5D5>EuTxFYUfZR^#XCgWy2{VETz9aMuHe&&Gbv$t^FQLm~_v`E61y|xt9L)2n z+hfsl!M^eEZefAS#_{_l_>C}0<7S5+ILD$4{<9L&+Oh92_y1J5&HEs71J?wny3#iOCnBu)iiRrPc>xmB^P>3*v~YbgjXiJC~Yt zEJttE3jJ#i^NW(^O{OONJ+k3X41SWi{AINZ)p?VDW@es>3l&QMf^5}r*Jecpi|{tt3T)&US#iDSw(4ICsXZ`+ye^c<)ZrnhrWiHZd0ViKqShB& zj)=(_J=8KW!)`eIjk{X#sqnO7sOOPC)O$P({b0?)gcN>#DU3{(`W~G1l@sXq0#HaL zt}S+l6Zlx6y6#`&)H;DFAZH;%6?6<>TA%|Jeuva1J%6LVcB^}B`pF9gj=0Qh;O!y2 zha$2l5NX4#C5@6-dNieM#`ktl!wm)E=j_MK(fUmh_RaaTHJ9dWL(@qVO4$)cFhDnJHt0}OrZw))fKS+Y(vyhqgFH$kMK{q^vV4sIL5%}A!OV~vbK1x`6$ES&vO{&PR5mjPClLkB? z9|i(9DX+KC@d<-X1%d~^@Rm{sNlEY=k}Ml4xpPfq@S!}(^4o5=!D>`%A*`d16#X%G zF%9WI>!+N94C^gjuO%6x&6#5t@5t>VgrQ3CC45s1AGdVwBGo*5jZ zqPh_|1|PJZzoIwxoVf3U9u<{sH?^mkou`7W;%jcu%Dw zdYm?mEJePf6r0qwB~`0SQ~hehqs1z;BqK#T;9Ov0-H?P8g@Y8py>gDaQ`FpU_?Py? zYU5Q=cd`*M06a1ZMrsd2*Aot2_r1dO<6`28dr&eQ@H{9=ZG()SwLq6;*I~b=hAsmx z1yQVxJqt-6>tW*bg6i1x_FD$D#WYfpA#V23m#TCLo!U3^xw*K)*<(k*eS!eG|DWu& zwCstW<3Xa$;5vuZS~N{YG|C-wj`Mb>o)kBWknDNzkC zdSPC4yCFN-1#ZkMPnT!A%J!=NqYH!@S)>?jcd%_iF%w8LUna?TD{*BG4GVJ#PEax8 zsyuu4m*kZMn!QOpKX-ZK1W@ME2z#ef#^oYQ{F$|!YDS`sCOVDtIsG%l{Yx9$s;)PP# z>MDf)`QGY=*GeW4H2aZ8Pvos!8PnI$A}F+$4h;ih^!E&etbhFMGn4j7J&PlXKOHfj z_g1rp1ZpvCGtD3zBEODE3jc&6)6t8C~6w#iU(ox_-j8$NyfA z_Sk-q_PiPN*tu+X2>+`#96Slqy8j2w%EYAq#r=<|y#H3Lh|x*7il#Wg@U=gH)9(40 zkb~~^D4Aw^I-l*4B%Sd)F=~XIodyz?Sy;c7HHS4nMsI=gG@q{j`_E%lkBxVc)53_$ z`KyixZ>L_?(%L7Xe_}1qsaQS2K=@Ah#MMz)_QLBVBKr`exyxKpW|@D}!tifp`!dF{ z_UBO0twVBI6uonm)Ko#5Ot>ePmoT>3eNl9zVIz6+f=v95_5umxZJ=NsF>7cI^{e8K;|<`7!x8xT(PL0W9o*JYnI7yr%p*{tj+BR zWoh%7-g+cYWfIclb+0={#Ue_@fXHDW^k=d+XYBpjT@DcnOIjKI|lm&4n-!&n(C zqZdx{0PT2rsM%L`Q@oSL$P#FGmfkVdDN4K zYPE1ikKMGBM@!~rRsd02vHe1`DyC(n-QE18%C3^2*xE$2B;c8<{W0se>p&z-=8T|#0CQc66q0nq}lbGx21Tarqx3zgAvb~+8U3v)&;yd0049A9~R0g+1q7Ze%LjN+6g4n_q; z|3xE&bjm9UGk=u>9~JX+4;dCr7GHPHjDp3AJqRACdIkd|MOk+vO}OJWXarE{x=JUB z!HBl4@?vRyd_1qTcA4QIr>7WG0cMc2lBO#Ltx_(4<_GW2Q$)d^pLhWYhaw0^Lvh)x z+TVr+ms8+G!ad`P`Z~;eFx*0=Dt=_Nujziz=pnF808c>c zIU>YKYiR{am&yT!Dw=%`hC+a2z@wQx3QyGH*0~32_u5lp-)0sFztIvKuy19S4bDhU z=k{_mx(+@Z*Gvi9-~%5*7?g5_g**J;UzF<`1n2841%xo8v});Rqedx_+Rk0qZ8h~g zn?7dS$)dp~c8vG)1|>zMJhnym(SP08m!3Nq8SHMfa6{o-RgjA6J#mBCq}MKtqQDWP zj%)0RpZ!P*-wq^;jZ|}KfD0e<50*L;ip#Py(^^>@{XgLJGw-$fk7nt=sLrg-hMbuZ zs%vP9NJ~dtFWHdQgAv@dU~+`4f>g@hBg*eZ?$5{iuIosu186iMtvc<)c*XTKLxf(RQZ7l4mP=+oZ7~-?tI5rE{d#_#W8Ke+gW-T!B)BXU+*)2( zZ7l7h>*J!4*38Y%&rh#?5IKE}u&#d@*>9~Fi~kM7u-nY%wA_MpSzc~@A-O}qypG4% zs6gI!ZPk*2nYk&)zwEiGADP*Yr`{ zrLA=h<}CG6AO-kLJ8Z6M3-MfaxC~fwgbsEDKGmk~+Dg@?XvzdS;Y#}XliZ~lA&G@XiEB5l z%5+qbPZ2ka`b%BGvcyyoFqDp9J0iTQNfh6n7ax>7^fXW24}=yA-0Xo9x*c6++=On` zFOx0Cx3>lYiCgAunSn!L3NJ8lU!J!WmQit#9Sy}|Bg+|*BV`6b-sT(E!$}yK6W~1t z+W(Kj6&6z5y3?|L3D#LMi%mjcw zjB4{d5p)oIcx?fyxPfT3flxi--VHTJqw7vSLk624t$T_Y{nxPwFeC{b^`G$}cYe>y zcgBL&E*L^SXmZ?%P=ts}=&YU4Q(JI1kqautf_vFZtF?i&X<JT(9%k!>HOgdXCF2WSm_)f39!80FX zx%34??Z$!wH)Us-O01YJx{r(q{sjSh?#|goJ$s%O+!Vs9!dS)FV1_$ym^Ov%zGe?3 z9ojDjZPCQ=376K}PE3K4DpN4cA0jJ4;9%b#emB`#NJ~9ML zU{NJqOPuP_G2x0u19%yt8Z4C5m^<%nA9lBei$3-y?uv-GsxVkupMB4K$O3)|KaGv| z`897s6idYhlekU3wYl)*qQ?;w<72m%51m1kesw8dzJGFe%3Iwwbo{#-Fq8{&HR@k> zU0af+Zl*}Pl0MbhGbjp^(p!`*SxoQD54vN>Vf~5J zo)b1;yydgTD!HPFB(X!zhJZ+j7}Wmg$gIL}ekxHq2uTyfg4;g_Oxl4;PAD2?Q9#`E#m{{&tH9Dlfi;AtM`|6v+e5jdb69# zRTr)j>`v~?*db-XhXW3jgm(+IFj1@w+;3HLD$#Emr5D> zQSd;aM2OcMyZ=7&*S(HGjjkWcmW4FeHQp!4@C1)gy^cL$kto<^QKgRe)jO7Sa^38;TWdLp2N z16o4uT1v56J(!x}k+Q6fbo^ZHf7{6E|ACTf;!1=Ws24)X<#L+H>GlvZhae?R+gLBn zdPjm|=s%3&VO54Vj9oy|X-5d?fd$m~SVn^&cI*eQTI0coO}hrqppsI*E`lC#&Y&%m zc)r^c4<z}-|wC$07J>jG(|9g#jy1iiPOXAXkK=of^Rj- zOVFO4aV#Wy4@yP~y7u4>ZFk+FD-^6vQfS&hU)D<;%R;?VZiA4>1{lQPn8#Ox)uy3j zTpWFAZ5sIJRuN8A^}DzGXc4U`!xS0D)EcpY;gO;aw1allONz2+ZrZLx{67#U59YPP ze$WZ)IkMwNkw>DNlzS2zidit=d@gW&#YtcTDusvVN@abg!BJ5gQtgp0i>|} zAjdWXGeqQ2kzd*^rn8z%CR*yCAvw<@0sUUd6?i`maMI$51^I;O=$57Chz-(XdN6X$ zI}loSy-7Tg^_jV$2p~xkN{h)^j!R5+kK^0g+VK7pwrj4ZzH5dY4R?QjygJ0l-Ik6e z|G{tAJU11nj?!qJQjfBH99z`P$=7*#AoSY|KYXe3*uV<7?rJ|2e*f^lNraK^ea&YI zcTpCG^lpDO?jbv{&8CLZVE`-wbTH+-!`E^S&5s@iCwghski?W90<|5C-hH#Y&nXLMGIovVCmq?SWrO<1C>cQ&J2w+eIw;k2J4cO$KXgYtYg;p5C7n zU@a#TK|%+=v#Bu)cmlv_Q)X;5w>;1ADYMu5T7cm?#wnYHWwZaxqea=gfhrzG-MWPZ3!yspRI*${}sIcX0JQn3?h zsblU9ou#5!@7>wtHVk!0t1bxV$Nr%ixOq7wOec0FLM-%t?Xpq&?s#Rm4U;|$gGbyB zf!WK)b6U`_BJc2yQSZ_aXoHZr=&U;QC;kbS@iJF~sK%+C&!Z};i60(f*aGzN#g*_;3rqwZrVfF> zl6I=(#<0`;kPD7{N$B~x9+%6l>p0q(KaY{6|TcC zXTlK~pt=wnIZT@a7e~O8g*-5r5Sfk41h|A9XErO7Mg-;{;@-9Kg6Dpu|gl^66M$Kv7ONj|7E{gP2-<7Q!`NPdGUqxMgu{*5MZ z$siz%ppdItL^;pH1BFf~z7v>8iP5gIVpJc=tF2w}{JHXq2+>+`_I_uox6GOSqXqp5 zT^d>xYXdP%OdjX!pbjE7z0L+h)(&%!5rUJ4w#W7jxbhP))#+f)&G7!tC=xUgGKRGnp!yS@;ECNH$q|D>i6%9`_3*d z`r!3+(c;CdNd;70WFI_%hAZ>CQ2y*ZmdLU4Mf&bj-sap|BU1O9Sz^=Y$SOU7L6%hk8I~*yXJDbyv80js}0X%v-FR? z=Xny3-ZF8izS_cmqs7xjkpX-G~huc<nlA z@m_(gmoDsG!vNp*M`dknc#)a==e_*|VSCpW;`J(xf2O7LthkzXbe!d^uEE7_n9!WJ z&JAG{ArVIo8}&Ev3B%2Y{PvR!mf@ld0or(tT{y@&+VqaUawU@qED45vEQ4KtXqBV7 z4bExpDA%YpBjqPZqzamA8waz}y3vMLIPL1Dcp06*)f2tsd31NHp5Z{Em)s4E#f0)$ zNM|9F_HU%Uq5R^P)Hgy0$OE0e0ku=T5$9$UD`+5b!>5iRf?d>?P062?^h5XBHuDTh z=hB)Q{f^UM$>;tbPv01qX|%naZQHhO+cnvfIk_g=HQAi(C-cd+t;wETQ@zhQ|MPpl z-5>7{d#}CLwJvzhWm~NxP5mTTI*12@8&sP|O-52eQ0oZ`QU&zllwRhgwo{g;agZ0Q z%v8sZ<&v4{6f{|-meP(u*ssI?E1R8U1#7ROaK57rszmRHpv_XJi|(~$v;Jo8G4zLT zXdZt!4r4Qs-4JE^`QA_h>@u>l=zQeP%=Q@WVDzB3)Ua&!8&5W zeUKnCC_aZbL=*rPkw65gCnEYHOtRKc3!@OJ9Oi?F68h@^06buxyehkM)aHqe=owEJPaH8;&?(d=^@#^~8RW`~J(DkHdtC+)< z=8QlllxZ0qsnG*`W&s$~96_=_fL-S1I!`c-}f1cXoahVl5XX#;6(+`ymkTvghe<&=k z*v6-0iPBkyNkE56Ld*@$A4_C0(H<^5crh6I2?NVuF5LwFW4gOiOch>Z-?M=KM5bYG z^3<;eJa(BV)c1C2eO-L0A_R85BG9qkzzTYs-TqAVGF{FC2$hLbCm>t{{6(z{XF_Hc8eW^9n8Rx%vgyB zGi`*x%yTAQsi=YP?L64 zICo=55h29B%I{Vu6GbPdne?WZzq77ef%!Q(9gl)wq@UC|pc%h+lr->Pw0eom)!6uW zmWhVO4?LJr$UAC&j95Zf<#}rHg~^F`Mde!N!#2nDA69()gFdbb{}X`FuP&0g%mruI61N?N z9|h*Ca5)Z=GuhYZWzkDnE%={_2Zc>LmBjH&ttG#@bwh==Ay@`$Xc(ECZr&K2Qm#2! ziI2SwyD?;5|88|dusZ#|Zg)M)8JtaCrXWk-THJfDgoV6YGI}@!9EZtrVnt#Z3s$N} z-toOh_CCIuTtwdmqcWSRMbwqknl3FZ;dvk<&#mlWUz}arYGE{9 zy&%`PbPy3nBH?SKq@^tj_&%J@#q;h|e6i|dYP7-zCc){)N;frsym|GB91UTrFpWM_ zem-;;z!|q`B85>##O!t~UPHBw##@wb#)yxizfjjp`_-{7^E-ZZDi??dudU482Dtex z!nLjXQOr;}dwJoGeIgCU7&j|(UE<~kRy8@SN2>K$e>>pKFv9kEaenT+={(didyC^A zxsNqL5AgiVngiv{U5lZ@K=SBVfxNAM4ctvlRhGIjV^iYmUz&J=8)j-q0@Zk=Qu6EX zR;5KqvowSR-Z5rwn!vFe{7zU-hnRQ@3WVO7Uxk?5)ehC?Bkref+Vmy%rvlQn-%*VE z0zRz0yR!q!VMHC@v&a!~q*vKJoEjAT*Do_7 z3OKh^*!!{)QqUv7jbCz@L{igeS7ce6&7L`I^LQ)DbS+QV@UWB<|K;py@O&I?IImXi zFF1ni+C-kWA!3-238COKTensuUt2~#Oyq}sdz_3A{W|7~3g3)RNfgA+4|u{KJZp`* zx|%MI(sS@LT@5{a!MP@_r7|`_^?oEGS}&-v-o= zth}BSkW%RkzTX6P`oV61B-b+y>qp~I*N6VHQBoxllSewTNTk-685DN0S6QJL-wMlJf{l*)<*j72hvK+;!-FXTgb$06tvXAFVi=8!zH~_ zXv(R-C;^-(RGk>`7$Q+_Aa@WQ10l@C_k zT`_tf%+nlFLd+8-h|TK94CKyI2M3@K?7}uet-1YCO%9dXCKh55)cRv>zQPO_=yU4y zvX`QYE`^7XQmzDnkd8I=7Ew%4quYnyYuJG!L+XL9oBRECsemLTqu@n2gkHS;HCwd5 zzOEfB*BF;XP^Jdf-I__sKqUACDSV*tye1=PXJY~75t-SX3@yIHC}EpFs~0u){$7+w z=;sSh+vc{sKG>}=`%l)#Q##Sdjp}+I=hus?ldXW&ypD?%$GMx&3*v~?qcAnHf!rCT zzz7m#>7jNEMpRkKQ9))E4Ao6A+xJAKt_Bxp3%^MN$Gp7In0IW|D~AiQJ#meDg!fj|(~=|m zJFfWuH0c<=T8$G;3cCQABUh|X!d&?DbC#hAS|l3ZNzU$Kdb&zLOXh|h0s|~j6bUQ- z_+BEIIrr~P3pQkQVeCTnHCeKz{qn8t*D82|Rv0f$lly7B>EizWkbjVKz%gk^5L9sgmzN*-ws(fEd(xiDB4*SYXqydxh4DtBu<9|7-3jDOkF3l)i=XThvSYeb z2Yh`z;|7Av;kHX;;?HMhX7Z_rExnea2$F4d-8ypZ)Xl_KsS@?WRw9BXH z_zLB(++e;Was5<9*3;8V{CZ6c^xZ$-HPQZhdcOh9jaNqy&;ZZ!&>(D`8&D#*q)BH~ z!dG)#g7_MDQ&xUvxT5XmCOtQs3P2Hno(5FvvCJyZ#ry<`dZLAf=V0(&uoFtSyV1=Rw6H`cycbUZV&9vyPC+!F1gYa4%-! ziO8EAj4tm91?biLjm-@eKGWYyc;d7Zo&EV{-}NXK;4aXts^s2qua>4 zo>OG)ZvNLFN!l4W-RZ%5y$LueQ_gAqan4xWZguG{8A>#)^?j36@|kuvkf@9=r6tb0G~uco65aTC}(rR%-{ zUxxQBs3|VhIYO7M&a2haoQup{%qf|2J_;QH+Q81PdR1Hd%5VC@W1HLVPgKqixBG&D z%PE+DD8;wJJU;`wxT#$?RaFEp>UfR)91q_fa~>J%d&Q;1V)OwVZL>$7_yK`1VFU9H zTNb@k^o!9OsG#@ia!wNUYU1`!@AlH#XZW&tyx%_(8%IY_o&8--d@$Ef8GeXMlngeX z?Hza~I6vEKh(12f$aCHOC{wZCHtp>?UacnL0(T)G2^Iti4i4lWJxO$0u}A#b3*nqY zm$QwWIsa2;PTZWl6lw`M{k%m_57RzR*0f-PRuHC*%9lPE22z2;Z1GLo;gep;P0X4p zDy@12SV%r-dilccvYHo&z2oyNnP>B|&hHa?4`@=YtE|kjyWOa4{_rb{E!Iw{K#@>l z@Ov1>@TL#5+^7Db#bH4Om}MGR;2#5KJ__6fiTh>!Ee#0{!u~ABgd_juO}+X_HYADD zehg`nI8HSZRMi8`|`0qqdO2 z43E?%n|uw?=vz#NYpTpx~`CqLAtuZ+UsLQ^yBQfOQ%-P*PVZSQ7Pv$=eqz37%+;t4|L?*4<~MuT4y8?;@w&)Q+O`3kd0YHLB&G6xq) z%DA_+z&f^9;`TbmN;wo=R~S)=C->51g5k7ul+9r9;Z$JBw07Wu?N3gDJIj#3ynDh3 ztX;9Jyi#Z*LiHTX|I24)mkD8dY|Z!-z|{O2zuaBgEP{;}$7oIGQ?tta3@zjY*Al6N zK3_72#D$<9%3p8shn2!~J$K55IC5uk>rZehI)M(Y^-)UV5SONlK#P}+l20}+_#gb@ zS-R*sr(YbY&;PduyhU5BC9I4(vkZKD=y4|wM%9(dz{16)e*l1%X_TbPOjB8DpsQ<< z+0#cKI^wbD&RJ38dOJfK8#{+s#>U@O1buDo z?Wd?J<@XWU*>CTTJgbo}e!}0o_Wc~j3+Il0a0@>!X`&{O@H|XdlLQ`gbWWVlP_y?)`pt12;C3_F<$1N4otHK^@^|CW zzNKoyR~S73J~lx}iIB%iLQ7HVAkc5`6D~S|N#IIpe~dCA>?Gf3qN2(^th<X}RU3-hmAj2W=72xNosA+PK5FPb7H3S0cXeM={Dk((MTF1Oq7`IE=~y zMJKuDY47fP=uTQF1Bsyu?HbyayE=TtCL&H3Yz>@1)DVvgAKrcR8vAi%s4;u_4kfi7 zcMr1GCwc_hn%uj>Qr*tlmkPwhMw-McQOEzivFk@gRg(n82mDASUShU)BBf(fTm;L7M)PY7zDr1;k3X zZ1ltksPavE3IfWu=GJH1-}g3B6vXye+8i%)5Q4_^+f^ zR+-*0I``eFSbnT9v*#j?fV^-SU%EwJE`*;$xAVa9_8_&-1g|*Up13s)8+hj|-Q;3vubj#RI?)$51~p3*sbc(-}A;^W=xxpU{qmb8Zb+L0 zHZTSlv3U4L8M%_1)-f|mOi@#q@k4${+vW-h2xLDjazDj;`8oV!t;e39gb|!Jw`pvy z7~K1zS9DMhQFR4c5|g)fS2{1yr0cp$)bQJ8!%1x{i#}Q~X$LY# zAJIT$p3(ATUEk-qtO%9b1Tj`SdK=(HW{xrBNw`gvWyA(coKgMc+RZs zDfPpoWcD-ZO<68I>sermCQZ{6CA6Q&QUDfST_cB|-A76DH2eGCOq6hFAs%dj>Stgi zX)h{#4rT-r5e#%z7MirfQ3^8m-WW3sH&IAlwFo#jdWgYIJL!-v?w0SikD9u`)XN{h zGdV4k*LVll=jOwiW_|qVVBr1hPeu@M zhw-n7;e+(0H&4ZSFbMqUnbhMU8DxRkvP$ylebz**1pZcO*xqX7cQYkxZ=bx zI=$HP@fA+&wdKluI^EY}J{xBVef}gmZTG z^JShouRV6B&%Ds(X8ReK9$&p*y?j9~C|ba+-#k&q6HCwGQ;3%P+SY3l&jUY_^<63M zwU7o6C`Ts4MDU%M2QG}QYuDV99paESF8HYxr~UD%t0+*OXDE$Ksw9uiQVzPr+S<~p ze`x%$u(4OArrvSr&Jwmy6c9*JL{v>T(d>RU^X2I7Uy)b$Dj0EY9s&;&Qu(K#IEdQ* zz(ePtjF17w1mPx-+{H0!C)yDvfCj9H2BUjWzUiqT&GMFgkc&JMCunF$TK+3^#jtI5 z6hafhnE$0(vakeSLinmZ9sxO?Imtj50n>UWJTL9>cDQL)T+w}C&2|yVBte<)V~KKa zCF5K;=SpZ zxX(+YoBILV98e+$F_2&__rL;8!oW0U@quf|?JI|F-lAg@ zD~ zz^QJxKbhHh-Zgl8qn#A#sjuv~%I}AS-NKH7srx#JWxXcmvYC^aG9TTOwUn2EY&jP? z&a_C+bA2k#xy!oP*xWP`kL;kqfWNA#uQo_8C;)UY&K2=+aq*Emex*hG{v6z?BK}W+ zUW?D|9Qi?R2PXuv(N)*Z&*$lQN2BTYgzP}6^6eCS-?P_p1A99{$@W;6bK7!YG;OYf z5a5MB>fmQX7ZINc5)0l7_sF_bF+EI9=`rNDcxz%Ov7sO>Ef*HeE(384vbys*;$pDW zS=rELa<1@3$^=j>p!a#>=-&MiW08q1z56qnZV^F}NfLCC8mw&uaIQ$wbaTdWZku>c z*f??d%Vpn*=7itj6jVf-eSSU^j!(cumPtb;4-ICvrt{ICF`M$wx5rZ zu*>{AgB9oA&rN!YA781`AO-tyw16*QZHaCpwp~*(360x_gJ+{~vT{3Z57$Ys1Rh!j z&F*fv{*u29?2K0Ym&=r8#zVp5VpL)oo^(DCJ}Nt;+$y(|V0vqS3$ASNpDP0Szje>$ zr9<~FCZRf;_mgQLy}fYO=cRJ@?*J-rNVc;bAx<-@0uFm-KQ~IPa;2-D_BL`CVpuoS z5>-@J6?kP8#kz$8vhXjel#r(G(hb!a&Ja`;_aw2{{+(^VqN?6t^?pYkUI7N4HKTQe z;c!T?0WrIaw^=d*)PbS9l>O&bL+J9nR=7N3<*&W%#D{|b=uGiA5CAB9eSUu3akKL- z>hl@?&B-x3@~gr1{;dlkLQ80Uo?PVaRkolzvfl^yAKaA~4-^E``}+yP>hfZ?$@jF#TYz8%2?J{OeiV z*;bcEcf%7;#Wk1t^Oz%M#T_EIAiE)GSKPF{upH<11R$8j#msh6Rz_lfX|L$wX^;~q z{ReDtc!|~AVi&XX{g~iv12q_DqJo6QZ&fTLGO|$!jjF~vI@a*B6dK?#?mq|!Vf3+S zRxvV;m`KHjIzHiGus=9|#Kpx~YV;qm6-BLHH~~N0nohKIR;HY7ZO22K{cGnR-30Qi zI&m`ulvUA$z=UL{TlYSxn>%|hk}wgd$iR99)=Hf3 zrEB~Yi^S-qr9mvE?qt8dV?P+{(nr{s9c3a_pvC=>g#>8x_Bu0Mpk=a(dng(5!d_No zk7=7X8p{3m+}^A$fJuQ$J4o^kqmX-`Y`9PfN`@AP&Wsz0{Q~5OeiqTVUte+~cQMohH2C9~mJaR?Axn63626>d80J)M|a4MK81ItHy+-_4fX?LIdhjL)&k+Yd}kJH~juBwJVR`j~AK>}+r zX)7yLr#du&wOlI%-C4fFIALjM>DhqW{m7eM5GRn4wflB}dw6)*AW`tTovrk$ul4m_ zbxm(q>ubM@w0BcWpJ$B2@>Ffz$!l%NMr)_bUlz*f2+wy<-^j5xJ<1*pQ6{{fQWn9Q zRR~h#!vR@$J0PisBZt zmeMx!7k@o!mJEAi1A`MOAyxf{EBnh4LT9Y(?CjGEYj>P~EZl}V+7Ph((L^ag0azqB zszRL(EuQ|El_?fnA~2q*)+mZeGEM?NQewD4spvDjW+y^_eP+NfqP#EnHTH`u*HC0< zjLrdlN^!0&=-OJ3mriKA3jww@z+kFvB!}1wU{*BPr4TvMoSlJzrXM|0d1V`i2oPMT zWni?asj(3F%2>~&*J?Hx;Uu@|WtUB+>-xDar<)Z$^R(lbHD7DU%Lh!$$QTTQZNofi z9YjD`2yD>B1CFgC)xi$GoyzI#@KdI7?GdQUJJQxYF4=b`V>RO_-5I*oi~P2bzoHjF z2_})KGSoCZ)Zp#Q9zL%6NCYRX%`e+Zb^7gn z%lH?9rB+x~hTqiWhL4?Us>ENe+Mt_I>EOO|dK^qpM-r zly8Q->o)$Fi&(kyPgQp5%fbBEED)~FA#XudFC0kMAeIpQcW zZAV5VJPrsDEeO1NwKmjolLrG+hK>mjOjP&;Y4D~Q&Q2ordH=DVjzFu3!@jbBK& z!&o09mY@wv@H&62=w^oh;`^*7ltFK=R3kRGaP=%S3Q}O2XT!THfP$8sZkD0F@ zy1+Y_&Z7X=9gvUVEiW?xf2Mf4>~m{2oYR%C&Z8ZjwSgwJje~Ck%o+lA6M^R;u;-hO zrm|}?KB|u%`*%}gwzKJBxvOlTgpy$xrrdcT3dl%(T-(@(wzi_kV0sBw;`*arn}vpK z{jfPZl>!!F2tZ!3ePu$sTqau&ys+QzjdhF?W*B`i!iemZyid2K_cZ%1Pr%-E-PAFQ zYKf0KFHYT*(&kaWdDfJbS?zZF(Drseq|C;u3Z0FaL(Q1WG7HhWu8h;35PPg8ADynA z>=Su_QmImc3X5!DQc>xG4v%NUr*k}kce1k)E7>$rX9PDQl3c!KUm8e~8B>>hEwwGD zA$31SLdQ6h9byPVDqLd#D#WPnKbbLNLlzV8)r$ESJA?Yg3K`)-VbTZ3X^A1f8GgWK z`VC=?O+HA1gcLK%u!vGfQ!}_&#{+X_lBP#h^+O>c(Ale3kE2?SDsF!EM|DQ=)1k1q z0T<_AqFj<>WO`EKU||u66d`E8$g>y(q;x2k#FfsEGPA7G@vkXGvMnW&T$3b};1ik( z5AOC`yQ|~Kgw9!W*DX5Zx?YZRy``SNYD=VIGqlav^2Utj2 z#J9_TJf-y# z$=+Sx)Q|Y#yZaVv^#_Y49&_DWUv95AAr{HV`})!Rf>BT?s2KaX>f}>?me8a~u<-#R z#UNuX#Yd%f>zm=GOwPQUmD%tt-)M;Ze&5H&HA?2kQ6)L0B6xN|{uE&yL(kITv*Trv z#~C*R2;^R@)KaD%7P25JfO>|fZGD1Z)lG5xJUiU&Fm4EviuEwLO63QnFiQ?C-lr1V zUw7|aN#^1~YwuM_2)=Pxgdu)}{t)E-rGb=i;^OhSHT#xa6PARk^IDIpWg1d6?zZ1I z-Ol-ve?bZxfn046z}*j*NI#Br?tRUkw!Pi|YJGUVzrss#t}${QTUJCFRWv z*wFjVD)czD1U*V3C|o23J9MC4P&aCsI18P%9zb;AiDSsZ_XIbG5rl7x-gF-W-(!)b zAfu>~u1u?#r^I=t66Z6-8KOa|#-649hY1cxvVtJYg=H86Ap{jdGr3%?9Z(C8lP`qp z;E5h^Uu};ECWq2nl)?oEGM+D>aw$Nkj0~Q{DCQ2; z>fzzROo^EqD-<=!O=DNT(IfP7w#6M=oQT^9_C`*bbY$h|e(tL#~?f zZnGlWg}g-P*AS7LPL{aGLm(Ua8*@c_zrN4k)Rd#~`0N2Mw=qqiuF4>!*`Kz|v~2g# zU9r&xD%&7?dWLT90=#?#B}5e`x{)%PImy9Do+--MGZ?snYwT;gbK=3mWvgcJ4e-4! zpFkP55XPUW694g$rr}ZEiemLLo?|XGsQJ&u(4RZso+T@+gBMe`fhAkzyg?>rR{^8F z`*(OlcJFc89>4zZ$=_{_jqE)*zyHHAQ2Fn8MaIKgvX3zuq`-D-_I(3Ih*II%+W2Oi z2)>86WP&Z+vM4MGtJ!>=Q^zgWZqRmtlp5U^nufoy|6Gc?AIl5(ZI0*lhH2ewMrFK4 zynHHl41(4a#g%Lga^D7wiAM-0AJAC@&f)e$um11xlYT|zmeY_0s7nja2!g&GV4P(M z7If}e!Q*R4`Yxzbzu8TLr=CA~XV5AJx)Y+!`-9wPe$Xb`NpS-j8hC9h_fxB{W}0ev)%x|vALnQIl@@ij`ss0 zMr@QFEqVxKb~Luu<&|+alH<(()7WG_Ec$Syp!xPChBn`u<^g197Eb248;;-TBA0R} zqXcHIt&9u_XK6?|76u7}3jHq&PNZ?O-l~B;iD_5iJpOd3VgdST?-z9$-S7S`V|E;z zSCnFUtL>AQufI7oGzoNvJ-ZWXT!UB@rLsS$M$Oe>c!>NT=0TyLN+^&S0Hq0ecp5d? z8q~LOUGrSzu+pw#8zt)q5T9Uzl1e+U*hqnaCw-)#Qpy8*)$r`$a zKgiObQ1Fr`7AJ0o?)s>zNaoJGVm(VQq?iWJU@u5PVJ3n-0}Pd&OiBeo4qgWX z5Zp5G2cn`BTjJR|?~E;z462b#?w`&c5Yid&N~&|sz9$BTcmXtHe^h_L^mJAm@O?)g4*WBJ*|Y3pXC8+Leh(g#8D=OcLM& zgGu1n+iddMNG8Du^LXYR5hjKNE^I)}a1Avi&*;T3YFacxa&L*~9=^Q59jitk0AJUB%zFFh zBM@dD|2udBO@%3Xt=1^`(eL58rFG;BUT2KIC(|4pS2GgQF~mCy^8n=4=m@-vi7iS!J6>J0& zQ|QPDj*X42dC-t8?F{dh6YY>BeCxGePnL|ETS~yq2dW(Q(I9hR;5B|C6}%s2^w0t< z>Cao)^_iDn~p0afmZ2eZTv&mrgJWQmzVhyo2Icq13R}B3XeEBj*2KBIRXUl zWb`Nw-6T~bhKdj8kQS@uTbw%ACnQ0_n+Az=C5=)Je?vhK48(C1V1+kcjLhOW9!5uh zXA0nG2wh?6cUb&A?>*v596ds|fDi4CH04&thel61jd^}7!7Oddi_lBG}e?bQ>S9Cn^_Cgl?fJWAjD{do1Bu5 zK)R~UE3|=#!xzQh$TlbhbfC*M)SKUuv)N7Yy_^nuwG1X^IwBGCBn>MY@LXgBr4N9Zhixr=jA$N zKsn;^RF*B2r0c&fm^?e^pLkdr+r4DEFaJ0nQfUxZ&CY6{Rh6&m+2@(2!7ytI3t|*f z-|&X+glc$l6l!ex#r6ghL<2qMJ8qw?KT(_mn=v>5oE2_#FboV84u0l82-$px;Ba29 zcfM6_60yXtUg1|(MGq1R3dVwpVb`bcCnpMme_&J9IfX~1nAq}Oe{m9)1Dhh_TRXc9 z2Nae!lvSq36WIHiaA_;2txa~im3op$T>>d!Cv43ziRX5i|Fj@+mZfr6% zV0d^gZvCXJ)0ghb1UzLVUVKh+$V>UigNW7##3~JIib3!ho|rZI4v8QMJseId(&2M& z^&iY83mQ5&s;b$Htv=<}jcTezG`?3$s>w1WxMgF=J|PH`HSdo;2Ncm9-o5}5j4B=3 zix{ykmr-PWS3JTOIV6SN5Ay6Z-HiX$`AV;Q*&I%M}vfPg^Zavl2${lqS$SUP{Ic56WGH zHwA2ZTO%Pvv7sSYMDP+tH}%moYG@lET8t`FwRl*(uPCFPo|RtKxSm(W#=Uj=lGB!} z6$rUjsZbLMwra0}{A>L?&4<5TWH9L!-_BafO{Oy|0*V%%dK%9#%erx9)8RS7Qhy8$T2TdfBix^<{ z#7Io-_{1H9=o&EPi^b!s@wgt5ab87-)KEvQ|jMqdKg&$u_5im%gNIvn-`5d| zMj|t|#Kkk;l)%1S){+CzFcpjM@J6{S;L*w~lt?^8#aF0sB}_%)x1Fpbj|j9&S;GON z<}IU-y1pOs|Cv|w^a+CGK=UfVDn0_VoVsMX2SudI6S*4mY9Nufh?)d)-gL~4~ z;NLwRbVc(OkJMU~*$?_i`694lvs@TV*^~N#=o@b*N{j+KJBEhLPZDZFH4soP-&KYcgOUW9!;e={Tu`~aqp z?PLt+F2iZx8u*`HFlQwqL~&1~mZmPL*M`m+@g}7WnXSt4X9Ud8UE-V)fl!ybaY2#o z5_Tvw_t^+AmHWC#6t3rCKyf*Yl;4HLU0uj5LWkZ_P=b3+ySriHjo95zyol!53!!9h z%`sW`Awpwg6F8n%l~~j{!`GgCksXdJuPP=htEasv;q#ERWb9)heZ?I!-igEfM#d!=!?}4i9@1a~8Gq5K3)% zp6>lKzF?$g5D|+y_we;@JG<&;bNfT5W=wnG@LF^>4hdlZdDc8hV{zez2 zIAMCecdQr`cw`dta1dl@V#-aVFZRIWuxUGsldTpD#*h;5y#btq0I9g%I8a0cJP(v+ zaU&jtC48Ag^x-lk6^8NuHYIcaw<)Qt-|g&d_Am)_N`MPx6I9aQ8-Yqbo2dng1m3agpDktuEVl1mBuZ!e(KYs;=ZBz&N&N2@lY&pvY4~7*@wXnkE3CxT^XbLxx;fZtaRC3Vs+zfq?)TEJ|Xf*J%sFz{U+Jz{v zo1;Tvr>1TQUj@4&=SxN^vI^to|NQ6b{gId0pOY1@rvNKTf*Ds5=Svikp0US{RZ;2q zd&Qe1a+}9^sd-S}K=L(-hXa5Z0r3mktr>%TM^6+f9qf2R=s11c4ykN;k4_)8_J@MG z%J&6?x#K65?jQQ4ez!}(qs)WrY3%L6hU^^yFdz z6NHS1@CDrifpA$ZmqXIozI&tTk;alU8dt&bEO3;UBvqO zIUx70VJPia0-A+XHfe2SObriyqqzMN8Z5Z*lA#$OE_WN3$@g=$P(~{5%jaW_U0t5gGCqVTbsG;mY3f*R%-PYxx8+?l2sKs#-^{rsY1IKtl{HP3xkVu4#C6ZLk^+dXpj^*Q|aMd z&kZ#rGLeY60&WGFm6T%Zt~>`CYCo)R=N>9G163w6`}~Po=Wn694k{?#_F1NaA{G{z z=D{wd#&pp17Z_qJQ#KjYy6B42vky(Q5?fx7O)3;Two1#fI%3EB+#+TWdn8gQW?}(q z#=pyqxzKw~PTJ5ka4NKhDEfyeGc?n1aB6BbZM|^4zhT#&zMRA;qeaBqSDMe4>uzdo zgPefg%j#!qOEtd4$?_3atcUal>U!kh!J28;n~5#Ok`q}ac$gGw`oH5BPNgd*ri^51 zbL%ypo<<&_oQs(_sC7pwk(ie+g}?Y98pmW%s)Ig zhZi8_ABo?dUG2mmLYcgdlKMJjOd?;MxV{4B!A1NDhj&}t4(;%V4!Pv1Bdem49E39T zNH^A6dubCA1M&FOt+<67^W{cMPOa+C6OmE&YPQo4%#;u@Bpr+ia$JGN&F^j2%v-%| z(;-U#eN*-R?@jfVEFY@CiydaeVy}TiLnBTOXAC*RXY02uy8~*k=qc0f0QI&T_1vbK zF#PEgA#f`#5*p8VFS@U^t5{OGh%@gU5EPT|4i&5loFKO{YsYTib@K$Ewb(UQR2jMs zTOIRKuUddZl_`ZCPNeQ0%}G3A?EWK2KSi@sGqlOl({;vTFmUaws+PTWeFVu2zR$|j zG8^JWhs_FDKDbzn#Mzo`gKVAEbr z!8)-jxSfY3we&M`A}Zh$}P$p`lH!IBZGuqW0Yje1_m%?TG zgDCTZpul5=bj*mI>G0q@-GS7&xd7fDiv{Dz({5fU4jXPw~#H0DTn1ZCmT{vp-W_L8|vb7p&qdJLl+eOBd| zpL=<+75~C(J3^8=^G#@x#=<|Sw(S6?P-ypLwHYbtOkDszBC>Tp6)qRkK%zjETVKwp zsCm<81ZBx2-T9&(Y9tSjM>cOc>7m0$FUgJ1Z&rVBj-_uu@rpdtyd9w#Hq9@kq26oZ z1h{#4)K1b;U6{eY+yfKoT`_^h>_v@X5)e0|TKd?U7;t=Qn?|ARxKVm3Y97WEl{rDU z4|SCCoJWO0UY%^CfaE1A`Se75`VV>tV#`=;KFBCRqGnXvP46Bjs9`DH9w+Ls5_H3i zit5%eFfJLF!e=Cb(oQ?w+PRe#7~$>hKh9`;R;$qDZj#dx?u;}4D~jMzprV+bmq(qX z@ALm?I>*My0<7J}cG9srb}F`Q+crD4-AOvOJGO1xwvCQ`Yi8d259-4?bvD*s4=-km zRKlgm^L#WzNOI^;+208z8(u6^@A(MCK5{@r_XK<<;Wl;3+PQD?`ftc=%y#bBC0yDO zVK2)M9DadSJN0=>3mnygU<#XgYR`rFWyChfAw_+4I9E0A7)#DxsuYp%IDllW^~36) zUAK*~JwOVBP6iA_$7avh)lG40))Q$s5*|h-tJTMZM<L!sgJVV?m)At{SE&$=M4e)! zkZGzxdrlo(u0*8;6kwiQ=|KW4B9dfi)TonGvEWhf=NCFckre>P+uqx*Xa0F`tM}aW z;_{5*il`}jP;ylCLg|>_fFIXP7tm0)ZU?U||MQMUy;OLj~JRLI*+np)N z#;286wPgy)A0znJz@3GKY3;9ORt~&Kh(Ox*s;Y)90V;1r?Fi=o0te>*0}j+Q+!IaP zgnXNs0#IB<%&afsDr6o>HlpXBsKlze~@qak!=KEp@zk5#U{}(PDN*i@ahNHu1bzNdghho zg96&X&HnxcMqGpA;P_EdITvE(258b*8`he%yl@vL?io&+ir(^v|G(-a;Zv1m6DG$KZPhDgdJ1N0@S z+$moeA=-08d@=})M~VU}bXt2yFpmce{2qR<#DXDY=j)n-ld%xS4KpC%K6SAi!xnzmGrlZ4dAE^28zYUo8xACA?Rho4iBh3gNcj1Vr6Yl*J zx-1@0zwWzA$cs-GxhJF30aGY-IL>j&zHXn>7@YJRO8QxPcO6l0D*u zr;Z^a+q}s!Ec2C+&z>>q@djBwF0<-zGM^-VJRV!5xUl2LhnD^5*aP{t?{jeD*TDDa zf9mgz96KJ@=q0oy+xnlooFw#E>J&!x_`ZHbw%hf=Qx<)u^O{a}l7ndGCp0t;ir5`O z%<1r~C$N^1dElrcc)s`&f-yit{|jAYM){(c7z8>Nq>5xv1Gbi)=syROp75N-Ab|h; zDoK122!IB?;4!MMGcDNA$j)%%oHpNiMIM4I%hr2~4(sg4=rKUYO`O6{U0`s|Kylv* zen3lD-)3N?2hSr|$mj|)D5es%@-Ev^(egbMn& zsxC(Ym)dHNAFrB*EkCJazT}rO(c_bTP7BH zuIu0w`_Bsw-wry0F^cI><+)(r$sLZESZEkz94Znd;`lzjwnvz*64UC+8v27~<+3zF zLATF!+X+~FGf{}$M)OqrVX}rir^|4Wn3EWF4)53Od>aczc3OI3-Kc$P116(>Wk2%b zG~ct-yIIfos}g_BN!n0NXL5S7ilwQU1w4VBoiG_%ASIDE8bjwUJ{6HF(gwRjc!_}* zeAsVph)pC|<_|&`ap76?b0}rkl9WsgL0mFJs!~G{XoEbrv_IJ96{zCG&U{dC<>d`| z!O2#mol^u{k171bq6RW*` z%a`cX;U3V!468fGD(gyvVPDP-6Gqayk?@J*j47%?-!Rl1)uZQh+!D2ir_f@P_(MiR zsKpYu1hFQKQVcyd`5^+s>zuj3iY@V!KmK0eM~0yKz>#2IvH&a>!w_&1me*cm5l0gB zl={)Xz6N(B7FE^D*`xo;Euq0iPl07`=~c!Au^K0NKuAYP=T)`+ILFJBg$e{+$AVy8k%nXNfdcU=d z*;dmZasH`l0|+WTOlP=u`rq+P{KM*b-9D>f&~i4C$m=L*C}f%2UdB{TYCV5`7gBS~ zA>ov|PhG~v3zEi3Cww_vn3*Dll{M%Kx`3iB>k|^c6y?$PXXg96K?e{Btixs1H~yO* zK<d!i@?_32UHv2^np- zJ8|}%Xb=x89nqJjZ#d&wAK#=da?LP7F}5SNVV7LHzw-#;e1l;1P{$U3V8e7dZM zerk{46bBG46|;n=XAJ)RxGGxbLC(2E;xXrk>x-QRaFK<6MuiFugbDJStC{_2+q=}n zKv`0~3Z&Ozzc|&s;M3bu)^TrvuzD9-?RxIEc^~5{QJ5MZ$6a39GQ(pn^?9ky500Cj znx?0vId8H1d|UF~g|?ny@bsUJl$hcxtZQ?9%i&Y2U!uY3mW>l~dAHkW!FA}oqxm?B z)#*EUhQFcngii5m2OXBhMfU4K-$bHpY-&{Gnrj*cp$X6)?nlZ*FMYyA^AVBc@NDk z;Jn>#em1kcu<(9vv5(kVs1kSrpdt2Z?MKN_tl>@#Ysrb@d*K*8;?2|6xjR39PEb+4 zC*z}dkf-j0%W>{%{Q;)IIvgB6Dpb9K&6qAUxfxS^*56od8afabbby*jz5Iznp=uAav>mBq{`Q=_id01ldSJ4lm11 zqsRe3t3d1^xL1m1U{`us7b_@{(DEaST0QoT5p!nF93ljD*RW~y7}w0`KG?3OoA-Y#S& zu?}5;RuiXFDHEP~EqmHz8{6b0ltvo5>9$E`BQ6<*V+o4{$67!@)7Ju|cnsNzph-Jo z7Rgu^nh&-)Z1|uPl8f#OlXvolC;!l1V?(n}L0JKP1`?Y}w|FgRgALuq@#AaXajo(3 zB4sO|{i%UAg~ul0Huv-}kF`s)`)RKK(_d6&k!fXJp1Qok@|({9Inl) z`e~pCLLp^~2G{K9F9WVfnqJk!=)bDXMk^KC50I;rlkI7$>(pPJD`q^_YvD54fz^b* zyLrx|lMnDq$xFj_En9F67KCsmxo%LvXbo)tw}Wiqo}b`985!Z3H`!0GFGGl-;L>pj zv32y3FkM<{TdKp(-?&R{t;4sL5K%5+_YpL-a8$Hji8&u%OpLxZ*J}MX>^G|+E8S@2 z?Q(=pqVC(JdMGV&N#vdoX#egjAu;yMt0}2EIS-qO(xa1*ks%6+5SXTztuWn}?PoO! z%gB)8`}gnNuASKIybJx3Qb=`1>NB*xlatr_*4CgyD#ag|!af1Fb~ zEv(@Gad)Uy>p-9GCI)dGks4!jiq77tCfad$zCu>aNOBc)Lc1rRrMuuK#FoU5kDfu7 zR$h?lTBNuYO#%pFyZOU=S^UWS#I#~m!-ib^%J{RX`XFRbj^=}qP#=qKG;|Syp%6m+ zAVU{Na*65%DlA zC=+%r3?w|@>IF#hH20z_@JeOEb9~;hm!6%4L@rPYVwz_zNkX)*^AJzuf)<7&hPQ`7 zy7U1ScwR;z++CqPSm|1?3vJ{rGsDem5K(MFn_% zD7qXD@PYtNFtLJ?C!3>nUvru!l3(t})G9Q?C65rbbb^1+#Pw1j*gj+XrY7=nwpVN7OdB z-AlLNccj<)H?4)G=dui$K@l4@gqAKr#I==^z4vrl&Rky9XlpAZRDE|8#n>W7a)*nk z6gF8Ys~#lyYca1Lho(-@a5w(XfYF^zP0971XWLp{DQxkY@UwP;VQo||&j#`3Q2CCF zK7|$b2b#*hkjefZ-S~tmXjXkz{eMHN3aZtAGuKgL5Qk`8x~pl$oo!{zH?-8Hg(A_} zkI9O^fhp+I+x|=4zCNWYk)>1*d>|}nPJf$+TF2iJ*^w~!*e%j+@!*`N)bJLgC zuvrcJ_sQwNsso$bCvSCm8*EQg)Ad~sv0K)_Zm0c~D9X3B&3d+G8klTaU41>b(Q<`R zSXa8;(qH z(B>5L)`M>EJoMm5m1$1oX})h`&Kt@T5i)s{0)&<=Po7QB5xcj7gX4=4eE+O&+fVb) z7ZrsO*;b@P@i>r*!48vvUP%42lw1gE#0HnBvZ?4KvqpfTf;qV+0-q?EB8HZt5kG5U zov0~QmUks)M@a{C6x@g7%chm%fgFbspg`D79;uofcJ?lxYO#!h!PG++`|>bCt;v+8JAuxE>5fQ!r~f-P`d#QcIwkTNZoKE`>scffsLX97N31(%wb2KKjy*VVtRCZQ)O5B z)mm8LX+>39Ig;r;7z)a{Wg(IeqtCL<7GC`sNr?}Rw_Fqc!=TFi#Cf#}cvQ#AwgPgd zhCXUYS*Ok0B`Vz9^;?w_T;fn9h_G_uhpSkhoH0$Z*=dZDm^Lax=ll}?e(A?{^~JXj zSBb`9kpuBBJv7|l#bjE69+bdk%WT#4*(EpMY;>6#czP8wr}5s$3zckv0ung=$B@R5LWX7ark4Od-E7}B z&GWCi0Tc$tVIg?Jh$gzKa{u`vwMYd8z)4caGG9~o>iqH?mJ>e(28x#nI2#_9Jr)5z z&dAyJjpC;a1}Dz1POqanbhvQxiEq?BtkW*%eqnT;@uHJ2+p?490XP48FaXwhSDY! zvHby4cw!zNQ;n%Hr+8omxtr32^I2`4s`Y!A%Lo+-L+HRSk&%*-p#}b?J8hcI3_H84 z+3lsZ#pSK1wac>x4&Kkv1#g=Qo0i>9o8mf?mbJla!GM-7GDG5gLCwqAJmT`fUMoseWt>2n1m2~+-o2%j7u>uC*MJmc2vL*(S-=z;S~--y>>eEt5epBQ!(cqb3Z?AJ{MU}Z3*?X{4Jnq0^-@t)$gFW_Q7z;jv zB3D>U9KH*JG>83W+H%l;z=bDZoZWx%mt;K6jZ zO-HzNbTLIfDDINC*%)xB>{qDy^bx8X# zFmTc*?K%M>n<*Z9K+@Q_Ax3hrKBDu}xSo&HVIg(W2VVA!znEOJgAa2v%I8Z3Q#Y@& zq@R^%_oG1}xJTfV%67F8)fqlo>sI!c0Pad)FS*uyUM&^8bq3F)rZ85gpF`b229ao26q+6g*+L_ZV|IMeiI0rs8LTW^$R~l zu&T7{c6u-pv}b&?!|u#`I^-`t)m+!~mSgO$nujxj!9l&xHWNRG61EqfsdvR&W ziZJ}wLT|#qs)2B1jZz&&83~=H0jr8a;+?BI>^{Lbqf$7=Y?3RK&J>HRPLyr03;M$9 zvdHWT10_Oz1D?w@wUNj4N{o-hUb5{5u`P_~V~3bQ3dG=4O(Ow zhIM`^uOR5^9|!d3rN8fByjSboAh zJx2&a7eWhkAxSW<;&uR)wZR$_Y}?ewe~+4*o2Q9bG#IuYv$VEhz~}X9#QP8Q4=n1N z|0|MRX>ac}u8ZgP#NUdHAjUQ7%yzx{b#Ru3 zZo~~b*^LP%NppRl7j7m0;G3Rhoq{umy&z?AZ&$^HbDXf;P}x{vJ(7myZ}RMrU_eqjPhA#3q#>1Ou*vO3TVvu6w@y zfQm$*2nLeU=yn#SW#S1+vU+X!6`*QHxpRHIv z9hNf32VXXWeJzdOq@2lk@aNk}u5W>Nm;Ga%1d)=rmoVU>cHl9a=jmKS->&7<|-KUyaqNap$mtqE}i2u<^(^|<4c!@!N} zc#fNO3!%LE?28HPtJ-l1py3HAD0ozqi9(5kNKS*vJYlf1QWkx`Jp75CwD zeAQzF6k%Mos%3LZ6#Xrw=aFsT5J0vFv7jO4#v8Qw^ieXLX2vO88cGAKeX)W(?g=#N_dliHPWfawhBEbhq00z zV~w0uG!ZA?)i!S;QO9W=0`=nWJX$z8eSC1-H0P$W@5u(xwlx~2>{2raVUr9LSbQDW zDO0iC`#VkEp>`uPv~M!L-aS|(=YME&Yji{`Y1=ABN{%_1<_CNR6l!|uPZcvOAbM}W8?RJh`R;*3@19u%s z>uDmjq{?TNwqFp%S+}&NRe&^ji9*GW+N-OQJH6>)mIXxuZ;r((!ZP>=4(+J;nk?D40M-L7jGt2Vu=okm6K1O$WblKlz zTjVhC7cr=RG2?uCNnDIPD5QNPN%pAL=_(1-hVGRcg@b%=6sZc`nu zC>j_wPg1*4O3PR)7C26s_5}Wg5kp2yHxzq~B%>$Npyt-b z?CcCr89q>#&c?=u;~0+(6`oK%c;_t_!CWw?AEyv+sx^yzi6JOhD@(w*Dh8g63y$68 z`r4jyo(dDTHbf?6#xWWDBjzw+@p2Ab7GHJgA!(f4$`il`6MP|uk{JsBDJ1Oy@bm%9 zjD@f?L2L;`nA89pM2BI0zt^jOxgRcl$s%E?9}J7iYRxA1G-x4<(eTm4ks@juE735s z^FCG2?Rt1X`gty8^LtT6!LIM3TRTz=&klxpoeq-;8_DTTt8=w+HbQq&1ZR$V$Xz)pQYEkeHovI#T+e z!EW=Z(#bF-=2FtK*}v!$*BIsJt}ie$?Mvq}naIO6Zw^X>=2FjX%q0Le;x4;IB1UN^ z09i9P`5RLljTAlda!Wcwa{?xh75JQg#MaakN!T$>`2`Ert9H##>+S0qmbzB;S_FFX z9rw^HTzez-r>qLV(QwaJbA{W#R0G?P_IBLQ5cD{xo*iU*Il0;G^{Chyxv`mu-0LR-xpXJczJ7B6x6rVbpLwie>_Z7P@qZBuCA0k`=p(pQH&kjDyxrT2pFadf0``^!GGvmXos--$t`_V@b;Q|VnBf%tcU zaDI6^@Uw`PFmXUb!@!Uhy*sQ)y7v4$;;@L3KC2g69XfKsMpti22D+g-0vEV4s4|{< zlIIN?P8NxnrxEjys1-RTeYk~m3*T1&s9(K+r7KQ==SHs6FTi%u=3L4pBKIWZBNv<$ zjghj3u0wx<@(S*6{KRYCIeMEg$iWdXf4H@&BG&74+A804tc7p*T=MeN!ZEdYUuEYD z1YB-+;dSaefTgBfv1fM57*^3tg2M$Z|2WhBE%d_8_Gk9C1RE)Qia`ww9A-8Kc(f}~ zQ&p}Eyi4fMarDXtm@irab#FCkZb35#6vb#%e*l9{*UaK%WJi=~bq30~io*4Dl7@LA ztkY}C5W=;lq4}BX%w0sfFO;e<4UG|1a#DOS|5PG`)D#s1a{aOH!PDaEbBW;?vB>rC zQa8q=*$4XaX4ltgfPTK(QRC)`Ju3YUx2pf3#Q!Oh8vke8f4V#aUcZ~qMS>!kCm&!4 z)E1?TW}MY!7zA6UV_c$7Et?A0Y6>3;{}Qfm!b`JWkU9w-Gn9!b&P*CK@44^#sda6Y z#*IE%wj7F4+V_E!Z6kIYL!eQ=EQbhN{lf4I0QOSnktyiswJER22F6HNPNr3W8BMyl zwXk&1xY6$G=RaDDi(7Q%ZL-mFLonClKj%xOv`gE?|_Mc=y@*HAW9BV?rn9hFrTUi1eo@|2NZgS zRaVlqKDFDVA?&)YY};cz(FR=l@VxM$nOeO3q$Ne?=J9@L+4fqow66*ZCu++KYx{by zsGgIE%YqomtqmA@3r8TzMlxm2s6##u<+V1bnp(#)H2SXbA3-SV= z7z4+Sum+WDa3?w5NG{Fn{n3ncft#`rJj|7{N84f3T@%&|Wqg!OxDYJp)eU8n=_pxK zq6?7~8!8mc1yr)wu{EgpF@|3BttM2Z%&1f7vDx4pTMt+fBW$Bn7szxQHKIRL1Z@=& z;mz8#!sul7m(P70xivrZza_(m{4n(2onQXo!Pe765v$Zj2o(fLmPf&A6OO@p<<>45 zvO2?7R^ox!q{4N|Fu+G6fV=NOg!xXIdoK#IT(yoOc5CwKGqeEhLpPYGS~hO=v_U1 zOS}6wA47NTyKXdg?v3?ch7CIVxj!3gb6Y)*v&2OIH+{1F-}I@>I(lw>Q@T+MHCsna zh6=S)h!O#buT~f##gHd{$g;T`L)MY_@BHitwY>RhLfO23&e*O%dZMiK+G(kzaZQBw zKaUvRx$u>#dO_@OnDxtQK~GQ57ho|YXCwH5Jay>- zJOnI0ug@2u^;=Wa_%*q{L~B+fv68vo+!qOtY=R4ch@HpL!WzBC#RVgcaeW3%=wtJK zxl;_jYw{iaql&da2zLXGK#=^?Ui#?Q=xadeGbx!Jn() z@o{%zxHT*Xa6SaNZ6mvV-`sWKL9Cd{uX!;J{W_B%!j&LoSKzx2QEA-yICe*gg^B?> zntms34vF&8U|{|Lre4Qg3^ID$JXP_yv$3wYFE(?Wh8_(pK|o;+dA`2wW9ds?uC|Hv z+*)Au7wf&}75~M<%yy)sGx+*Zxl@bdW6zkg0g9<*SoNms)?r^K$=x(~^g%%{HbZIh zxPdzpkXX+bxi+M>jg$&GM9s~HT7DUzNsRBksCv5!+4}j^!*t%bas*0_c_96oMj)oE zN<~X87~iN@ZD(BvlMtVL_DqDUk~H9m@UaRG0g4Jkof82M`@w+93#{c9r$qx3H<`L8 zmH9d?`v^>tULLyFPb?`Ir%tQ#6}o!a*)=Hg zOlsZ;=no)B&VVbF@*oq}&-iBlxwq=yrtZ8S=5F>va|$QZ*?t4jGkpH519AM+L~e(B z>$DDC!{bGgvZd_6ZYDw1gv)A?>Zm}y>Q-w7vpiw#!Rw&O*ckdw9Ml5~56gmZ;X$L}d zcvVPLqF=%TXDbHlCr_k4*Ae3~pg*a34x12G2&5z0Y`pbehupFei$ z9RdZSGG=mgVgFf5SiNKZR|B!zblInQSIToRDc1k8TB84Vr(R zwLC%7E0*8V0Lc@draYpMK@0;8lXLfGtY811PlSokNKyb zdR4qOvx#MMB4^3(dmEc_Oux4=jVBRtL=N?;PlPZ;92nrUYuIP2r-!ad@8*KtAaZJH z?+ql{o`4@9_U`w+q%{?B`f?jlx0_>x08j_;Cgx|zhaXu z9mNyV6xr0n{`3+H&Lu9$8osjt4d#9JfK}L&EKfQ*;7m=k$(R=7=^}i$@CW!$1aSZ< zt^_d&&j2{2r`CRVyB8ORlIh2_v=8A-Wo&E?Q!CO(TEn@qPToiqXxfiHg24dC!vicT zGAtE#Z-@p=Ae+OY8y9|Yj`FE%V#nMTU1+6E)QnqeN4;ta@~3yr>H)2)M)iIsK(8=$ zGv!jSSMqH!Nad&sT`S%ayDTNLi(~-In=me10WV|ilFaTqh72e7lKImktS(Bn&!b};?v<=b>Dw88gGfv`m z9o)46a~&ZDkicr6nPa7r&8A<5i49AOCmgJp3g1@maUUKZ_GUb0t}>aRa6bZJh|W{EbKLnEgNO)5r8k4VgJ|tE%)F4KcWiUD~ODrJc2!X zidw23+2Pi0J3R3YA7R@$u9;1_Av6mkO7d!BcRv+`v&8Y8!egrEYM%g~-nI7z-^ZGN>j z7qEwc0NSbhm5Z)sR%&L_*=#mLUA-@+y)LE{J{C%5f~Ituu@Qe~JC0LZ)c>w{RulRq z6AWZNrLsw^Z>h&mUHg#-b3FEf{p@qU7qd1m?<%@A8CaJ%&OKjWfuJ-XzoJ2=6|oN* z;U%O;;2j}TRKviPw5bd|N;;}9G*HY>AIa3z0`8CGtY(LKTeI1O`DcG_PGJ%>0;Vd%`=yNJU||{o#wu@NHH%v&q-Z)* zu8X=#PLXIP{e1*vADEt!xvHae9bSnt0x+m1oOMO2J;4nbDmXs%Ak-u_1CitVjzYgj zPctOX7y}H3PQ1VaZnLq7lZXxz=jn2b^sM}R^4244qiTBXz4?kZn5d4+6xNBOf?yAkYz}goNqpqOBW;&;s^K_*BULX^li0BW3}|9R|5lP`*uf3 zZwC&5J%somP0hoNI}#z-ld#XU0iNH{y%ENttR~$B#5> zjnDlcm7A>ws{>E?SiQ8*YwwBOf3(%J9qmEG=nCDHQ`z20{4Os5$rQe7y`~@gOmbvO z8<(;>Z@`uPUkQ9C(U;t8^cr8%{TCiIX%bmtbz=PCS39X>gJBXUCjeOA2%RR1Fhaw{ z8WtAx9XU{&hZO$rXW;{RO&1i~?;0TOjqNO zO_8)nUS3$3^9`i@n+ccHt_l-WGHu;4A;I{9Wx^3BLjU)9N}>B-=AUSyxbUE`~cCpoDL!WJVwGLo=4uiL%I$FMD zYkP5?3^;9(s=mB*k^A=z9Nuep`>0&!n+$&#?_6*7c)i?N$Q)0n$KiAad_85}^k=O} z2s%j->Th6QmTd=$D22}d`%;RYY9bB)HNe-k=&SA?o6X*!57iRIouoyuefcKBP> zEln;kBG8CXnM>xpZ>I-uVN;pEJFdGYsE8DQc7Ckm-HvY%!K0hY^~W^-Xo%8^Pu>Hs zD8=)3znTRuSLnBHew}fA-I=>CY2=m>`M;HUJ)qt{riaUkgvj+7X2TUC7V^w)8I8_2 zNBjHTy~SQHSFu>gS7;0vRdwYrcug0eA05PTtFJYhX>=OjDn4#^*lRR&VVcZ2Nx*Kn z2UAelAZ)C_z;otB6hmS>y71R-K}dUu*kS0_|6H{I7$of_OBjT88^>UNl(kbfKqQ=&b^m zY7Sxs`(URWt)DSHXGMqvCCDsJE>J&)RV4=6xBxYI2p}p7CF&;{>UvjUMw%6$wZjn??N6F=5t?<~v612<)jj=AFhynRpl@z$6kLC5A-kQO zfT^fw^t}H(AZ$G8MyfoK1JyLa);H=A1?5=un?=( z3SIFh1>JVN9Y-7i4+>N1Jjra&7lPRB%iv^sauOlb_z!`&@RbYq77Cmg#C&d2uee}O zU?49Nx)Se2l8UHS(qlN`i%w}n1?BO?!+_^WHt5un>+P$?L_QBDn2?f@pcRg$Erelc zz@g$_vQxu&@yNk1^K94CKK&B!#z7F6!BXOUB}xL+g28}->l_+C&fnh9WY+K$??N2u z{2%Wo281RWj*`=cf6457!eO`V*Xto6@LS5)KFQ6B)P%Z`stwY)Jd2=>AcFK}(NB}? zJo+iXyoxd4+`WP7a9#EaI=necZSZ}6Q-kUqEl$_-pFP`c zb}Qs9+h2~?ohGqN_4l1`6=_NnGmIgxBZOnk73Jk2)3Ne(wY5a17ALH$Rm*HZZ>=!F zhv4Os0(%_q>mV0A8@=|++`K}}miKdSQdy$W>2Ch*`9w|W^R|BtyVZxs6;{s&xr^&# zZ~;DM;h2)2lLqZhM5QTZl+WszfYQKDh!w)6{m@0IWR*qLHU?Uk;kN=AIPZ!~c7LyIhirAw z=GQh7q1Q!^41hrm1Z4nvZ``6@xwwpdO%kzVhHTk8{_@fe@bDCfij$qZ25KD=b&>$(vz3;#_IlYEmfg}{g3zW){#VlBOgX(FRg6Zm~`ggq8NDVaQa#~9VHBrA-= zg1Jnt;|z8qU6ctDY#7uHVK}Q%DUypR1??hP;2S9>l-bzGDuO(>F2+^HpViDiTtf{_ zNHzQUctb=;yJpgo>vDZA{YR1}j1u}A20N_K2zWgOW}oZL7$in(K9lq5-<(n0Mmup`RX@}|bn|?s zL{5Q{mJ9J>6|?);0L(@m;`#L?t8;WsP-jS& z)#wciA{KQunXV}wRrA&}`#(vH&6$IZHgD>ncd9M59`_0 z^*NFrv;Cp)+5ZQS1^so10(wuY>?FEEl!BJ7oOBR`o(;_xNS*395vVGmmTZ5G)2}$p zYL&nm7ARowk_G!k4?*b?Pmxi<2%q=09NRuXy71~TRxu0W#>5L~BU4~Sw;R}XRr&t@S2mP+ zE3n_J3KoxX%=MgB$U>YVsNXtLCiJ{K%aaQ`O{XM;O@5r}PZ>8)16UORt(r){WGCam z(&YLZ>|N%+-jVKuAPwG#HLu6HnJ&;o`@gJ^KHQ-&L9?I=NI@C)d` zXqWmgfZ2m&>t3;rR&_lw4|q%(7~dTBbwzzt@P}^CbuG_O`I|ZyaAOj;#Y7>%Nwu$5z(xBsv6M{i9rmC32l{luz z(upWO``6w$n!30uH9ykHKoi(be|Cao}4? z(cY!b#=-H+7s&ri`yc(*jE;H=6x+6Jh)^o`xdf^-1FzQ-EMsAX60VR=%1W}2`zy+~ zy;rvE`1k@1rJVjaO;eYgKfa$oKI$~rT@WHQllEFc#!bP({$6E;Fr0hYqsvl-g-pwk z^ZkVQxn>n@{|q5ahMg(R=CZw<+D*gN#^(rX%L~(VNbVE#^p~0b*kRWuQWQh_?Wiw@ z&pW@39tKlg+cd~l3Xx{ca6g%$1iI1AQ1j%2YK#)+F1xA@CdIg$xN&@W^BXrSw50Z*!vZuin_BF$8I1y&sr~(h+ z{D@pu20HcVT^DYD-L?6fd;)9)zdGlw)=*$v1h>c~pw6eo2v+vByE0tO@I?{3IdSv8U`u1iC!OyiD?l(Cm==wFI z{=D9FLG3_C==Y+VN49kk#|Njzrq=K3-zra6Q;H(+?c8>IY2yG7w9`RN1>tbDKyYdC zkB&`;ytU(+OdS8#{^cF+^=g)iV;@K4_qj1~MmB$xY9NH#B^z~vFeqZdjd6+8;_jlV zlQsQh7u{TU<9|6A`Hn%}Y_hrwYHeZ?Or>SFoQOr>aIw;pYpX8{XDG7fE}3{BF=-#`FKYgot^?OnF`aAf)xH~ zJyU|s4j~#1_ir#qad7{1=WL0iuz{{xcrzPBzZO9PQ%Za zRpqdSW5KpbGcaL;jz!MQ%ka{`IPM^Tm0D7R7(eP%by(zw6H@qrp}{ZjSI^#MiMz9t z?HoR*!2rr>ZI?r$fvYKz8J_#e(V?F1dbzq6;S&R&GOz1&3wJlxMLVq)^UvYxCrJ%C zxu*8rwDib1lkoLLM?o}*=sMpMe#?tf%rFE3;Q+%5F|?JGq$HPbS_hav9kolxn!ydt zXwShZ6Bul?R=za5 zycC#=Boyp{P~X7ZXOF7sNfzP7_yTzww^f^ft2`!6c#Mo)IYsM)5doV2h+5A#n}Eyu z5Grks79yw!xg()(PZ8UL;`8+?O?)ToOcA3h!x(6SqKX=J|C5puYhN0yyu9M)<^sLe z6(%${uIJwMwa@>gLJocZ+b(Z3d6gJ8tq+Bit5jKJC8T;Vrj8*3M6;Jb67PG#;eQ{_ z+>zsueMoV5J7caYxUo)lkL&)YitAm87F{b?b}lDwOa5Hg8xs{9ZuQ7A9>+ss&hjsPwc!2hfG z**cGY?HLCT~??{kgvK8|M(%=0T~VIS-J0l~6*6B2p*Ij{R;qUkNY zB*%JJ6L9)=v)R0*XaZZu)P!r%8=Gz3jh2!V3X zNYfQvS8Su}eGUEle*iK;&Aw>A!h+bPm;Tat{@K49+qUnP=g(gtO`bMcRN@JNh4T1i z%d!Vx#81o!f$F>Qp({EBLlLmqZE*Z?N5aDoJqUGm z4Ny{64lcotfk9k|#4#wrH|w;?LWa*JR=2cU5tG2TAr8T8<(y5MHkFy5^?z1c^-Z%c zR8U1tHEd3&o4#y;buh%WT2&~p3qU;54mAyRKuSDR^}tvO7`V}cwM6Ch8eFf#fL>;T z$i*fJ4KqZ6M`6?ofok#Tx>44dfe;!G*aU+KPfDT%A2JK(G}a=~V4$(sC$47aN@_+( z76SndF&K?lFc7tNcXuC@Yi^%$ks6ziJ@-ogbz7>gIpdV0o*6Nu;;5(YeW3lwW6xZ< zYIEJD1;?CHb=9wbT{~g)xD8)^wc@qa8#cV;a{4wmHnb~lr^~+Rq~o&$UGwS&-n5m3 zv1!v5qaZ(5Bs~_V!?~xWrDfZ{IuB^OyzK1Fc+Xtt^>`D3o*pR{iyL%bH<;)t&QDbk zbF=!%3r{-1hP&v0yZ7GiUAuOM%Lfh;&pi25sI6+(w{G9bG(-rLk`b+>;NL205Rb)p z(m)hh8H8X8QVOXy-hBO_`!$VYgAE-LIYlrvtTF(SlXV0Jgc$;7;`I3h>|d31P&8m; zR22Dpw>xd%^|;`x<*Rtf7x^>-XJ4}aOq~>ZUpYBBFl_K(Xl`yqFsBXQuHbTdTtzL- zodeq1j32GeOxn_xw*Q@_gOIcxwAQV4iB{Pz*lpGXW~+h-8gy7?kthfTE0GXIbVMX{ zb3`NM(C9p)VYTvZ6&40W2&NBfx}ri-ccHgkXJ1Pu--sG)upZ$YSfC7q%Ba0uW3X?w zL`ao^aGEdet5GJ9B-aJ_M`w$RqEQ%ZphK-CR1Ad-4O4fTl_;HFtMvyVWcWk0zTm^G zSvSZx|K*Mo#*ZFx-O{&TpL_WHlPptaP0??>_5vvy?9lK1+uxMOUwBDhwD44Y+O)|m zJ=NLX36)i~kein$#jR>tIaN4f8))<&RuoFv}sd8!c3n{(7|oDfY0ZF=9Xq- z-TL+MrjE|&Z8!f}kuBEvRaajV6$DAW>Z+^h-X)P>AS*~hCps7mL3MSF6=T(GhalLJ z*I??@snPE4@EX52V^>~Qj>N3Aq-@wEOE4OC{OlLM)JBgQsh%`v z9&FgSOONSRp={6~sH?4FRAzkNV8iS;VT)Fr+ZINs*Ub5w6ag^y6P3k>h*MOgt_jgd zgk{*-JjQiINhDHBgPKs74#wp1qhZK^{&4kGe_(-2US1X}_n?3{5=$@<+rr!|%KppE zZ5@&<+cT`PJgG20fBLsANZIG3eHSJ)H@7=t(U`;Kaxgv^E@J{{3J4hJQINl{vJP51 zh%&Ep(14gH)V)G8GsdR&j@dB7fJEyvJz>G?ZXp1Q zX?cjORVS^}EK_cJQKqyoZ->YM2#6x+gyWJRW@~YA@j>Tr^<4|eo_ONjoXy*IpLh0& zb6*@cvh4c3+cv6apLPBne}3?p%Lj}a_t3>ZyWq9sjy>W{{N5kmvuoF1+Pk}-%P%Tz z5-qZ?zP{RW#Nmfq2MrsV9SZc=1w%5Ln_Hl@rCAt^!E+=WjyRq2W;{(L{CigK@pwPa z$Jex?U)|qqXNG1J?#E$m1_${~G-i_l!z3BW4U_k#eea*U! z(A3fjx=0;w5@5siAuA(;iBTp9guuK2X~(cqj00}h44h!yu=JGZHMQuZvaM)L#W5`~ zP>qkp1AU3!4lEq}ka=Q)bW9W#MvoZi|wzQ?~8@4n6k+y@@#tj-rLQ!HQ5+xq36FpHY-kwzw zF&E6@2g69-^3Bo-qGFRfi5WCT!xs7Tlyyj|MAGjpTcH~!#ZolY*Ev!km`d#A5ZG+g zLe9ci6BJyqdSbJeeP2tety7>Sn!UVPrj4BPUX3f$Qi^CqH>QNEpeu1r#1lw@MJXvM zPQ3fcCut3cZ^@Dl_puk=;C%F{pPoCQtmK*x-gss5iKm}!oqP1r(!&qjW=+Jpjk|8W zRlW0X59%fT`|H=-Fd_Joke7<%+NL!{*GOi`CWFDW{%#hBAHHWDMvN#LDMk zF*^56EzQu{+A2(%G$k={;>1wj_x9LhOExAHW7Q!;N5*g-NIQ3I6Z1;5oo6mO+3mJk zZJx}0+iUNCAiwwe>&i7({7$pmox+;UJB`AUGQ0*B*uG;s1VSO+=Y~zi1UT(71{(N` zu9={uRTroMhQWhg`k7!QQYsU(S1^c(#}h2D<*jppl@S;$_o;)507D`efQ82&1v__a zg(shQ7MvajcpP@zCm}>}+c;)Ai6LH;>lYfDo1{=AE<2sB{&uHxw$teyvv~1h$-EZA zH?Ifr-H|J{ZwIlivDuA)tSb}_Q?0roQQP52Hv&K^2Av(SZfiA&76*PeF&3}^%OoS{ zBk(oUFh%}2cq^D)FnC7{3N)!^UPyK{F@F*O%v!3dykth95)20VlDMRmt^3s0P)TSI zK|qO7+xKIrCBG(t-5-dlhR4(pAQ7U`=s}|aabS0gH{X2I`Q_3tPdWbh2}?!}8~9Ln zAT;5o8*cdcqMu*=%T5_?I`g#oFI{}zNpI&AIbUvXZ+fh(tl%k&5Z^jz(9l7ioXiCc z_4Na@a=gO)V-Cwm#A9B%J?ffaRPNgcZjVdM&hmlXDmB||wk`Gb_5T)A4Bw*`jfU1a zY_`pAmkYXjx+NM*qyZ(07B+%3Js}SqFvK%&&S6=&OXkv#KGt4-?X5`v@?zn_b59p* ztLtIMj!MKeqYMyJ6H#2#ERf;%GDc&J^hr`o2tEu9oiikPruA*f;;K3l#P0W>=+9TLn038|zo-3jzp zNY^IA<3+$T2j^BRw6$~zHmf~HvRX!}R;4VtuB0t(X-nHTWoZB+Z3nS=K$Jxiqo!H~ zCptBL^a`vzt01OS>rCPL4CFSJQDcT}y2RefBU<&x9=EaaG4`u@a4hqfU=c!9eYQ zM%Df?+JV8|^?$jy_?g=;zhXfD!mFQu>ghvHIOQDmknv;nC!cswFtn(8%b)+O-2B(O zb*IClKltGNP*haJyw5N?jk|a6fvoIoF()^dF5f=Pwr-j|d$tPSb6fJ(hyCZx7VeHV-#Jysk#Y^c6s#fro|_uivhF<^V(p$EyCm4Et^Ux7Qn6b23(jNpR- zI5=7TwBP47YO1QVRjZb3fnZp?`>wy*qoJUD(=E49nT6$wE3TlrCmHcHR(?TqfU?o+ z)~(Y6Jv|L0Mhx3DWy+M+zVGdpTdwLxhyL^2{EU6Whm;9lu3Qd1-4Wr)88dB1&7370 zUKfV-vh?fU{U%{`3W;m3{T=Msz1QdoYA|@{Xy^Y8mP}&-M4YvJ)Y&{&Om%zSnv)44QgY&_Qoxg$ zgX^EdwHT;739E?^%vyK$QU2lBDUo2aG%m@EFh2m3{ZZm76ZmB!z${3lwVQ8mkt_@> zrZqcxtvU3ZY_rOMVgtv5;IP|tB^FIkw@(C|ze@(m|H}3Q9tf0t@y$;sOqekF!D(}+ zJ>hkGXFmV;XPbZW(<^WJV*T!4efq@*Z@>54bAcOfxIvvecW&nyC!JCAv!DH}t-86p zI3h?F?XKE=K~G1gR9U@e<+MX5b`Bm~meJnPE)fu)z&TV~Uk~~DxnQ?jprA0X8Be)= z{P^(+_%|&#w|FlC0$=$3zCa`vgOoOENRUnDDLy(9(=uwa2|Jc%;N7!IseCfBMIQs4uXX>hg# z)m<`Q%yfDsi8CQ2tIV8?xEf~x3ehF0q=Jq^NemSoT$UF#-TkDmLhc2%&XNSK2+#Wi zN-2TMWP3pN)+mdj8R&*s^{5IKVt80>R;j0_$H>acg0y`z)|X~XzdyZr@sw+SbII*~ zuk$yL-FsL6^Dq8&Z1}|4@Zz&iLp;=#xc2wg$8WpyUNweM=nXgBsvmj8QDTCED75;f zdgu=JKw)tqczoUn2E3~r4#)RieDck;Yn@(~=eYd5oVnHe_S)C3S($LT9pbFnGoUBX z1^3){m(kSHYW(5aD`3FDL6%4FdqP;gYKt**)?AQ`IH=($*exy?RMAfi_q4(K^=sp+ zSFejspE^!CeExCpr|`Ra^`ljK#a*F#}x1^Baap|YkLy1Kg=4WDH!MLzxHWXneqkRf1Dwm@%p zMx2vWiork#ov6(aiY9>2B#pKD)y#~f&T7cxIPZhOD9o8L6~Cu)_~W1d3Ic*QIk`TZ z&QzX-r%^11Y*=uN6o@?t%yb2U7MsJCsAfFwHOAonxbIZ8|0d{HW_~WmRbkjc<;->fl%beQ;wba zQht8+*<04F>;C1huex{f>mQwe?EK^I`0%Y4cJ}SuLce3C-TKl?&&`+OZPzb4@z_BF z3yYrHyJ_c5h1oeD9(LHAa5NTiu-Yq8GTITajO!v47Zwo+sb~7V^-Gp4`H$KZDr#ax!Tg!%vw@d%!Gl+AwpbxCW$4bLj^?%%aNYe_%qKuAGf%C!lF}75_;O| zVC%+R5Dr9GOKs5N2vo)(Khp(%x1H|sx`Jo|F(QbeAd50%Q;DXo1A$kvWJQ3&!t|lQ z;6kSaS_VULpl(03mdK{=2xIP;9kYlLqOm-LXm-<(nWjk$0qyZ4heCa04eY6I1gq1= z7#X@@n4QJBzKPZ5#`$3buip=Dw*+lXH9}Wsw}ie*o=cWTLn1U7(w4UW(9HV^>uO{e zYc2TG*4*6eYTdTYT~}A<=-k)oGa^P&{f_#qWy`j?m%h3*tF6r_L{~MF_Jg$jDD7al zA*L;DUu$-I7cq4;yTwAqyozKIHIhu=G)Eph0K3Tp%c$`zmnkx4vK$!wk+N?3{%Y&| zXNrZ8dXALQnL{H|&u=AFra|gc$y((;?1kQ%W$4SP;4KH4C?>s2(Nxkl`c#)1$+n;9 zLI{E-p&D*Umi#d#VI-6UDd?ve5Z{3H1s^WF;bG@P&o4RYXQwZ`P>aVVU2(~!aOXo0 zM`zAET72%QMKBI};oTCt*}1fuac?Ao=HQGZKH zN_0t-_jrB&RX5&vV-I}Kt!nMgDHoo%=#+3USh8XLHm$uaBut+<4GN30v?rfH zcVO_~fyTAh-6Xa&wTM^!;m@j@cn4Hiv+{zd&TcsKN|+jpz0R(z#IV`}V< z+x{w&FxtKM-m7~38N%#XLvv*x~+Usy0>+_=#L-+JdA?7USLr9*&=O%DKjM z*Ztmd>~RZ(=GG3Vs2BnpR(}cAHIuWQR0FMvXM|#!Q;UR?X?LF4b9;9cwKnj3Y*n>XjyUg-et>{t;KPvCP9V&A0y(x zt`L2}hE$#T0l-8bjhYl_Aqd|LF453g>y0ZV*}(wIJdJY={X#_DyOjfb9m z7RoEe2}6bqfYlpUgGF|L6(5oq#!PYiAJQr!6PWC){Q#*li6nQS*ZAk49vGzg5spN_ z>9B(&bKsb+8wa0trW2tHy+=uYFT8DrArQFjblQb@JZ=ex!`5&pbWqUP*RI`_U0Yp! z`hp{;{RICU8Ek7&UVQPn*WdVH_1}wzO!)kbSMO?n#)c83$PvomPt03Kqe&*$ra z|InK2>o*k_7B6jZI!Cs3w!2FEm4Fq4avWR)I2ok7Qe0eYoiS^sWAE-wG9=>qw#q%x zC!b$znSbQr_RY)R)i-b603{W}VHg5sq6t(v>>}i4dNI+ULO4p2J_Ksm*f|;Nfwgfp zSr8_HAz7A1ri+{rN#LawlOe>K)cUX;S?h4pCrQm!eoPveRK8SFQVu?!13vn61q`k@ z*&MS;Z-xs|V%mj$mx}5pCJF4>T_?1+cZj&3*XQw%XlZGkuz7RM?n4gg|AP=hPTO~9 zG;b03vkVzB#H9ozPs|FQP%zf+J*g z%ANIXTBvDY2`rJv&z;yl7wHapb&)r>TXs{pc(WK&0)UHIJtV zV|Ym;had034qYR3mf{(W0R7O zndv+4=8i4rkEa^BWo#$U%aYNxRPM>@6;DHtA)$G`sf8o!66aVnj zW!l2CPM2@_{cnveD?e5La?kzRXUkVwh<<<3PcG5_a{CSPE3YoKOq?{FxuMN1O+s61 zn~|5BD_}esRkg&9STL~lh8u49Uh8#SdBd$G3+5fV&~A~(tXjQA*t}^MF& za`?>Y`lM+y}^VS%2d3r^Jbq#)otB z^BX_?^wao1d8%Y9{B4N=9bFFK(tx=zRswokR zb;f>k#pSV&K7QYF!FlJpo_~HZ%sFhPn46am&CT`DT95;U_I{Xk=Rt%8rxJCtpll_B z{OD@pTs9OgD#x<%q%S~g2hLgopJFkQWk5MmidGm4@5~?ytUM#w(*@bNS#aX9v*7** z9)-zMCqq8|-B{lQFSDh49>BQG~o+_$eDT3S2xg8WRMV8q5nlz7#kL4z9bt<)tm z%>Qm_#Mif}{09^M#qIuW%k%j%TiUz)!B_&++GeQSS8cfM4&&pGzY@0Xs)LhHxx{d| zL?IptgU6LY`WsA0FOi5**B?^^STsW{Bh@{K1IiLxdy^FQ-V!$cxmkw`Vyh@I=ne$I z<#dAEDKn6WG#`@dKb2?@&7A^AiuY3OI9VG`t4@KTs_8~37}T6LyD(<-=pS_^@7{Z# z^^Tu3e9pLWgMNyC9u|vsNFThvY{ToXe(cZ^|qOI zm-UFutQ=*`ghO7Rd)U!iAAkID!1Yg@aOh+o&I!LPi4wIq#TzDs!yyc;-Iz$Q<2vAK z*EHCE#1Th?;Xkypva;?(BJoi{Zo#~bTQ*K^ZEF+C`}H&8j7!BjObITRU0!hf9Qln` z{;sUwuq}S^^tti3UVBq6&GA~#KI2^BFSp-rtlPK}atbma&+h@9#xMF8ZkH1Zvz*Y- zu0xDE->~r~nk_g*GkK5{l;nM1S$QR=HLx`!6ji}y$8nCImuXMwz%@v8l@T`)@HD7h zFCLRxDREQ-SDa`p1fzxzfmQ3*!C7Y<&%{*q1g%v}y3+I!tO%4ia9y%MZf+jb)V8tv zQAxi%t6{O^+gz4GHc2Eg$}|9RkQ*@)KHa|EnW?Hd{IBRaI9+#fXWjiXg3I2cO3Ui02D=r5WJ!`oZtb z*7xk&hhV)1d-rV~IBdws0G{&&9M>^O+mFi907TjjTFcMRH>i|BRaFUZ$X7Is9D7o{RVEPW+4`Xow9aHWFGz_zy z+X2B>F`^{kizi zBY(3z^6*mo zw4&sw%AGrH8`f=65$F<6I^}rE*6DZLewWtJ*aE-&<*$U1BgROtzw#=)_Szf95vQIH zq7|KA4ASkg9da;sc00k?zIBhbX5G3t;&9Qs?)FY_-WO2z=&tHiAO=gU6}Kl9WxU6=ggXZ8=@`ADkVSE&yiG+5lY z<_ka&5B!-~;IzBhDnTVtvK2u}HAP_p3p(^V0+FQGz>2qMUK6-Lk!m6V5egn~(u)oX z17rqFSO|bL=P&8OggU!m>f}S<%Qaizj=S!ITmEOMt99441<$WMyZF zO)YIgZnj^v$YPPrF3y%kN8P@C``)gosQ9nhGdJFNBU@aqzy5k7`8$~jK78W{1PR{x zY+~V-ZJToKSuXLA(lP`Y+~9DzARKQ5hbJ2b4j2q!1RuhYAQa^0vOtV9by(0w+Brt= z`Yf27vE&;U%=MD3w}z3*1}8rWE735Q(MT)~J)scTtaiN4R(8D!aG>)&MYibCc$^An^wRuN_%RxlO$-=3{Ln*&4?Y*~Z^!7G;$6FX^{yqay!Uo( zUGsZm#?0M#)m5i=f8D-MzV@WwEoIGgJDtam9XEWsAj@76Y&#+mWo9JO6L&bgt8p`< zJs9yd*H-0bX81)4+^J-d7)D-ShA1%sSgAqR6IJkET0oXJX8L^}I2?{aH4Sx{B}K)& z^QA$+pk{=lL2=5oQR3kz9t|%%`W)!Ns8m*5ApH66htx;@beVk0f+OLRySl{;kIRdxnR2_`)gQnr+Y`3E>Mor7E3TT4ZYkgG-mZkNL;Tirg<>(6AYKGMlyT1T4B5@xE( z>x4**SyV|(hnliUVDoxG7oE7iTUeR%uATeDyzD%~;RK;yzv4rxt7}edYO1B~HtQg5 zKPpQD5NSJTZP>74qq8$?koXd{fKO&`GgfBPDzeJ|smgNyXnVe`kAZ+5xI?0;5W@Ze4~cf|+ap zK#c*-^9p8OgS;EjEbO3c3j!W8W(n<>DY1rnB0}xN6y6pC6Toe1}p1APD zc|X&%#OPmNa-MSkBTu(x=H|#ZU3R@xT#%tE~l-Fb(2 z=k51cpMU8kdFj&kt)#qgSir#YF|yXjG9Y zlqA-V6@iP7mcFMw_uR9>&wh4^GIjdY`puiyHBOy6^ZyKr6xb)&PGOoGxZn0m*0LacR zfY#O)=xFZ%x7`Cyr;qyUv2GR#(MajSfWf=u$hyqv)ASTElvq~;OpV771r)^yZsTU) z1uNmg%_NyKoG?*@feU8RgK;?Rg!%B7`=5dZ#~lNc#|(o&O&t+Qs1pOmYZ~O|6&TTI zqg2<>Y$@$uDA=s(&^U;P7336Du01hj8B*LNGBEQd8 zP>@TS7lso7h{o1Vp&+je4jDaI*s-@7zi$=1UJt8Av)XK!!IeNu2vqliUofr0NlYi% zEx>%&xI`w|8^lV}O+`?{AU0frT|eTh#G@MY^h6PCim+`(f^u=)P2d{d*4kkTRPJA@ zLd!6f=iq(Aa3FAEQ2PSikg7Txe^d;J8*W%^9XqXJ?7V41Ps4zHAp!yg8#iujedmq$ z|5jPm_|D*AgSMV}+KFu>3VHR_XY9Z4C6~AtTyXAWe^$;ytJQf-QGRYgOo<9R_w0a< zuI3!KMHuIH6;F8V@2@-TX&Q(ph#VU zm0?hcCzV1l|3ha<3|;S&bTCLW1_6>-4CFuvWIT?H>!-mO5(ZC};62}Av+9gtX|f^7 zj|hfiP>A5!s3C*k-FH8N6Xwlei4c|PN$_*fxvNdwYhffs?IGaC@T2!%heTbr)Rj$YdfT~r?Vm_ zHz&{M_c`5emn`LrCcxWkx*SI`N-gr#WKSwNEez!lbkJG7FR6pmtu03=Hz*zKH& z(Fsghn#3e2qd*HUCC$mH&^5z!csYQ;{0~tuwIqaIrxqZUyEDrzxKx!Xcqy7fRiGPR zs|cr?`{2tK1VU`+Ai5)wh~43`izUNKG)UXOYzOwUzwqjthy4Eh1?OdC_)e+Xy}j_X zv(D^UwR(d(Xvkpce@m7`bw?h3f!&o`>~-}h+fcUSyY zeCM6lv;2N zu357|&B*jhGiFUSwrt(1-u#!_m^#9%ue~B-kSCva-Y*2zoh?q7ISnjE3@~2;m>-1F zqAYOQ1a14aO6AipRw@>BJMO&a0cq2kb^790UeyZ=`$<2$=%-fnk!`xBQI1fzVdlRl z7|`#!^B&FT_3Kw$@w*U??-p5h{C}O}>C>kNKi{zR-I$`z_}Nb{p8Cs6FV+{o{IYQR z!qYADW*%x^vtf<;!5c3*ckS5`y8nSkT5h=M57OdymuBolaAd^bAwoxIJJdHc3RxN1 zfRhQRvxlPdKq5(^CPY$XG-`{evQ~~7o6}knbhL{IFko;Hp}KCFXCg7f$=d#je2t*2 zXCM%Ul7f7gbLeEa>u-<2bC2E$*?u1cq7kMqK`r|1vLWOW3!=Hv5{qPrcBd^{GStbM zD(tH$s%XL^SN9eB`*)3)5C6YyQpBroz+QgjS@*doosiYi)Nal3=ZYSu3j-?`G_|xt zWo4al@g>KJUauW0tEw1V!RfNIteTYkbS5~haSP7lRTB-IeeFMX6uS`vrbj&nc@O4|&j>;be=(~8^ zHmj?pbIi2yBTx2ue2efCm+aZTOL_ObPe0kQbMLbyWkv6olx4MCdF7?LDLj{2J8!t* zF8}!PL&waUH{&Rq%`qQ=gdts>LGk@}KZnZN9e^P|*lkuJQwBEzOLM&*yJhe8x=J;! zj_|oXnKql1GOIe3DXK9|a5}66t}9-zw{G3Kb)E1Hn9F9{=y7?L;rkxBXW!n;>Z5m&?9_ zJOmF_CQ>Ug4F{a|P?+OqVzPlygyjuHgXX-H8k0uSHxPJwLC1iN=~%P!PMzsGIP5m? zIPJjw4vuT#IK$;b#u00W2xI5yti%FWB4}%Fff>^#1GPQ=Z1onHJ!vF#cZE1;#xy82 zCdO*Fy1 zN85;SI9!J3+YD*@(O4RQNZUbcs@i6B2W-0CVZltSWH&oVkbI-TI=PW}f-D=XX4o(_ z762HLi81|HOpa&dv zsqM1UCeOdJL5qq@JIbLlo}~X#bK3 z-GI@z37Pps}gS`sm-E?>ze_ zXSHAT+h4iPI_D>rP;s7-hu65Rz7C!97VvryfN(h&2to5z6FAVo=A*&eb>f6WFhOU+ zg_QYGGz9}!L8Q!2C>qmkxSsehm>^aa4gt2l@VU;}OdVZaaQK`VaLYZ9!t<}b2j?uB z58-%>$I7HwgHD{+>3~FrUubG=75kU;(-HT`M*w4v&F$RN*x1-wSXdZ=Z_~a(F2u$? zoA6^Tup+=}~c{|2MTD$f&I&(cp-NPMuYf;=n}nCnq^h?Njlm%I*UQcE}n8@VZ10K(bkQ zMjleaojPNnQEN!6)hdKTVauL<)%Jx8PPE>B)3xwJH40L;ZQDDvynoTzj$y-2CE%gC zwb^*@?T^;K_2!3*i%W_>oIGv*j`Pl&ovPIq1TL%i@WT%~2M-=TZQ_LS3-DUb(G+FS z_O09GP1|jqL z9(uxE2)-pmI|kx-k`BC14V9IZarg!-D=RAyO(d4(Wo6B4Zf=?0*48fOX64{1FldV5 zXj2UlgDLB2i;j!GviK$Sv8SIg?!E604dV1fiQ4DIV^tbZJ3Dwgq3ZNzJp+{O{`SZ z0d|WQB619TUK_M@HW_UlokDhYuFLCmkLe0@PiSs#-jJW4{~v2@r0x4-RC3(V&|bc3 z!?ux%ZXLOH^ZE%MkN1#*;=G)^ylkrvZ_vpuEqr@0;_;}VqEAbpY@C2V9LFTci^(kr z^@IS&2n0g`@VLD=mvSI0I}@@psk@4w3Ho*-FwxLZ$GXWm5RhPMhjO0K^4p`%zgMfJoaxYdM1k3Bl$Fl~72s&Ekl}6%lXR zR*^Cm2PO-{MH`spc z-@Y}U98`!Zm>IQOA(fH?Fe$_Wb2gZSg9%0(efCYM(L)Nypg%Bij4raKC2&6l-?!#; zIlmS9^pD97WIddJ{?GegdF8Lp95}Gz^wyfH^7GFc=a}{UcEX5-&4yQJZ9wdDU(iZuGw3*a@AM*#!VZ9Su>`? z@R38*x8Hs}_RRCoDT68o>wmiWCTC+^z4-g zufaJdEwoIZFxI;8q!VNJ-2af=*wE;{?vFRC&pz{5eEz(7mXB7hGA2wOVFWwcgf;{q z{C*c?TJk_fr`;;)5YsgtfU>iQR0g11WJx5tv#N8pl#W^67!$3ufeoXEP$Z_isP@`I zSs^W@7sG>Qsy)XnKmL0B5wqaM7hXg#Vj4Pjnb6+W&U)NXuU!KpwhUi}(a_d}-%W>7 zkeh?waiRh&(mbEfw-3kix@0iemkIITFeCb!@wLDHXLj=R=@?aNp0?&j>5x$)M32uQ z27+C%Y0DO&f4@=~IdTxRcXUHzZ8PK*=K!s(w63Zqn84a(CZ&5#J)B++**|}Se5wok z0lD-mZ=V`Ppn=5u6a<+RqLg7!*9~1=K`1H8!Rr}G9hZrb;#cZ)y1{0*Lo65+s;a8& z#~pLH6Mle3B9K>Jd1W#Fdfbq~#b?H1#<=a<_ex)_Shwqom8+J-!;v?NOER|Hci*iD z0NlFYzNF{ja5zRxpE~tqe`e;%_|(A8_HL;Ovz-ycVw4RXr8#p;g-9qY1~KSpX=;K! zyY~r4&zr$6Al^=G1IkKm4wmg#XaQhaAHh(NT_-DPAsA{a&Y9Xpixz!jp!&|OTQ?6H zGUVfy_V!`*jg6VP**Ux%lLGsMYN)Y6BAXQ6GHZp2Ws4)=9to>1lAm@&&te`N{b4LL!s_N zRf~mK5Gx^VKUl*tX5YGXPubIty*jm}rTx&1T*uhaqlfnEUs~aHINb>B39JPzSzL`9 z@mRu$gkum1Dy#%C816tI&Bnml1g?oB0!(FR2zbNbf?V`tJYrU6Hk+#ikIABBG&Z)t zYoD$#y1P4J(v&eUZ1@mx*j=D02;3XI%TPRuW7=V5^8-ARL8VKPXjKUPqP>ql{`mJc zA(OWM%+dfv+74>7Qiy<1tOkM_S9M7cNikj*sV0MTaww3X)@w|oft0IR_BC1Wt225e zQ5aRZEIef^F{;e(Tjsyly z)anm|8Dq%c!4i%=O%kPTc-kG`*Yb<2Z~j}>Sqlzd7>`A#ez|g^xOUBEwX~#IoI7W> zUb(MIx#iDyQV=FS^X!u{afmMd#UBhIx5$_>W)ujq9dqZI)VJP! zSF%|xUGcd3`J<0My7%At9Aih6?ndY9-F=mngMNC!MFZ}<^)LF)9oyw8D~+cB>6AZ-l`B*N32GBY}U@GC+=3Pb>}s&TE#HG)23>WLS~zKq$}& z<3|mGoqPAf1CKrlf4cfNL{-PRpJ4hkl*yMcV#)GnKznDG=ykabOc;3-B{2yv!M8c)w+O3Vo&RzS2^MCR)YAI+e{dj{A z?CF8B@&f!0RLq9QAg<7mp)9V`Hhi04+U~|0W;kO>Hnyg_8n- z;H<8mpvCKQ3Z&_T6A}<)Hbg`W&OY}HcwzDT#siN$rN8j}gYi=r9V@)@;yczU75#-H zk2nH0R6Y%#eYF}E9&;qvaa>X9NDRU_&t*UW8geo{y58O+#v&@ax6{Kg6Bl$}=UuaO zMp4HllZ{!tnS+kbNiY}%2L^r?uN#t#2d4c*>kS5j30XnFBf-jqh_zGSP!AKvPlnRm zY*_NnQW$mV`M4C3AEGh8gbCzA0%V&EGU--XtdNtH4g1z@h5EW`V?ben&1G|qZEb2g zd_z^$&M{?W-H^5)jL}-(9*Goe-?(SQb$_~NysE&Iva*5^N1iYBvWcOGtvaU(4$75*#B5en?>3V{C;R)zXD0Jh;8$IC; z=yU24BR2LaZ2mg;n%7VoV39@U#FEl42|S5RmPe|_FF)iMK@v=9V5oz2r?*6rJp|Er&08hibN&*FBgU0QJD0==Z9U%0-u zE*#U8PIp#TaM!k-p^o}S*Tl(Fix-}L7KDP~WzWC#{M-5Y`CIyxmrFI34P`TC%qmEz znm^bRfQ-BxWAEN7aJpTB*Y73=vpYXOZ|nB$+rO7+Kh<=vTDxJk%W0p#cFkJ>i|LK9A!8DrGIVxH z7P4~PphQ~LJ)1W~map9&?q5=*TyW9F!UOj|sP3!UtsOphp>g8E!|f#ehQJ`nvWYgE z#n`iZkABbH_vnWoc{I#F>PR^ji|y>}>|FZPQ&0U{ZMsMtZ_l3E&v#etnK66Ltl|md zCwkUy+6aRO54H^&SmrtF)KemVy7e{^dlaw#<86_ppL}RN|J<{^e|zi^ICShNbldAN z6W;|H8Qlm5co-0&l1L$ALuo7vZkVho(t!a43^1L9bDM@27p5ezQNaNhQ80@XN5|jk zU^IUM9mHh2U)EVig>aw;792Gf9)5Z;tXQ=XrcOEp+S{5)=u6Oa^x`y4aJ$^-C~9(R zYpd9=Uq4-z#eSNmAEv5Mh2PP885tRYf9E^uD?IssX+eBb;KRG`?yGa4j&afKfPV}Vh&!{G?T@F8k=8@7s&>Cb^sRu)h|6N|(VjEaEYZD$}GZ>y@~_|~aC zs^Imy=o)D`x!G;)?d{*VE`dXqHy0KbF5S9g=iutPn$n>I1{$(tVM_k05jT25F=6na zf%1hvyFkC~rpt}To_R(4$$6)0UoBl>JoLhQ!u7v79gdti0hYY?Ic(Wl1Ve`o#(=9E zLZL7dne>WQ$U;zy?&ZyD0LQS-hpK3fIjvVfH7QQXjn#)j6a&3rIK*h}R$ME{m(Wc$ zRq{co6)bhmU_FpQ;6leu^>FCqN$}kJAH(j(b{LTD!}rsJpa;<)G&W5z>H`O^Wi9P3 zP?(nwKF40zv|*hwsI=V3%*xEKX{etup`zk5JnUzXwjYFT*;1MJ*1O9lf3_da`qU0_igVEA^3d5=d`pu7|z*8erG%J=Ez3!S({t?Q;nk8AXtnmkF5} zKAfLUCeE%I2>}seAry*0ZCwqtb#$qTXd;9NS!mDh-QjiXD`T&{`jHTg^!V?8;Lgl5 z&s<<9hg4M}`o|k@guQ$A>?|rSc_XL5 zzv=7Nnza3}Ee$}V?Vz=2SduWaipeQe!~nrzL2sQLN6LKRuZ#jI;o?jSBn1GXzyksX z8kqmF`Z(6YA6DiPvq@Q%&DT9?{nBta9Wj%p` z3I-*1i_H{KL;J!T9(E4Od;FyHPyOQsZkKb&pRd0*0saPS~xE zj>h_?_INbf9>+Y^p1qs86Byqrws-uoV~hr(g`msk85#!jCh`Y?#lV_|`Y zzRhBR{9M22lH<@^SEa37zh3DGD#D{r+-B_w1fw_IdaG)Yz0Mo|bhRJ-Xm1dYgMp|) zZRV)g{q1+$4xODH@(tHtOS7h{tE=bZ4?p~H2mA+CU%#)WwX5fg-Fx=VIO^yl{SQC* zH+{?Aonlr_zWuB-Pc&Y7`weaP_9}h##=Y9(kN-V*{sm{*7Qgh8t+uH{&nfILb~WvR zp3XM#V)n;kl@VjGL6n$Im~o^|9RwyStP8G2L4rt}a$QkP$}}&{569#74b=@Lq z;J^$dF(}y0S%efdTZzTMpXG*`QzpQ(e}4f+j~t5U&4Fkr#6xTxJP2S3KCeflZJS$~ zjlzOLd>hgbeCb%L)!JFv)wQZTBjf*45>Ls;kRj+mAjJRq9VYA5zX=ENsG|ntezde@ zk*@0_0 zuDRi08`RY713^BFt;MO1ifpmf!0xmW;!6T-77Utwc89}zU|HAq#g;8wX3z8c`xO`E zPxEB^<~KGq&fKtJZFYTq0Qf)$zpH?ckY;Er`u8h%W9-QcQ|Bx<7+xOO%FW;0~+fX-o@!PK-mY1DBWB!qI2aOvw#BR6Q z%q>XoS~p@!!f0x)HJY1SgxZ=~Hiilcb0IU!FP4>-2yTyu2?1-msx~$?C{@+f@y3P* zt)->O8Vm-lvS`))Svg%cyRUY~=52MKeYP~}a5&wcEnDFjK6Ipa$!qWUii`8~5!ZMa;-_I6Xeg7Lei(-T0>~-$PLI-vZ9l z_QS9=0FkzX*4p;&5lhSSEwS!^&ExU7BZ(fymZqS*XNRhhE zK>-p%bC5bWwa!F`Gl~vD(xDa2YH(R3N3^%>qHo(aR_~o`{mF|!88D$GTYEyEvkN90 zLS$)CQ*5ozhuW_g+eiw)GdXOghliRZ83A8u0#RlZ$S{Ix% zH~*)<{<$?0jkmo1*2j_Z0Tsn%gUTY^ZAMLJT+zl39^*B6sn^J}B6wZ(eLCBO4bo-!sE!8M0Pvg1Hb2zHy(Sh;c~JpJ@D;z=higej9J zVVrMl%+AjH@VxWR`?uH+Nt-=;w)*M^AFSTJYxjoZjy<|~)P%9HdD~WfKj_&<(EHzv(H|D8RTA(S_B{ri)bMsJrS>$9u9>P9PgU;TSVdI8mSb<-MZYsWfW4(Gr@18r^XZHevMw-}Ah z`{2xT{s1nw3toKreIXK45JW(*2eap)?l=Zp60r>!ut0U|#79dc_`=5*z*J)l-cg|M z2M`YUKqk1He1Se+0sc3*t2gJC~=*y$DpUa0mPgPs-gC0_{Ctb zM~w7@oi4lKhyU_B`X-IBnYM1t@e7VI#d#TXof*FAUELkS_w26rG&VFtV@qqSvnROR z>vO*_V$9&BV}_2YC;gxUkAvWa?9a^|F=EJ|lVwq6IuDAfiO)U%7A$$=BfV_I2)OH^ zn_1hvtvmK0co+u68QEH4vs%DOr9}8$+Gu^i-}7?w5WI0f?asYGC2Bselidq-jbD?V z?jVBs36bWKAR0aRWLtfG{Wl^&d7yc{S=$Dc+dr?XZyHloRb7&sk%hsN#6}QnfEiQ7 zf?TKk$3I@Ko_^x#+F$Q{IC1}9uZKA^r(56nWUY0?pnPf4#L>nZ?|cE9Hf@H?v!-|E3-scdvgzjJnY`A8) zac(5L(a@F_(uctBGm1F}IwpZ=p>PyJJ)JOP!XdDH{br~?`UuFy;4&HwbG;J+Rj6#t zYQ^`I3-zHMC@aYaztkWs(IPwW{6c)`$6vMFe#@OTAARy> zm)lJhAd=PZ&2~Qa_#?(iC!g>I=E`20JbCigDO0BCk3ar6r0vINX#gT^2dzaVQSi7t z7Cj!5+#a{y-W{Y4+-!wrR7Tw}g9HnZ_fI!xL*RN4L_ecG-gdn4JMDP$cRAqyvG)~# zaa`BhXJ(m6D`vK386-0^C9#t@b{uAgVv=&wrfHxJH0&e|qaCtiJ7#8P*^=FYw6eBU|WN5ussRu-4!qDMAJM*^uaYNwFFgG7HP zV+{M1+ky3PQ?Q(fZCJ1%MDaK3#FKz{P7#|~T1fKq(tC;vG5AXjA}pzvgJF!15lsPw zIwr(57K>RkFr!2T|K{U7Hj;XnTV_u++CUJ228tsGXS z7+x_nsNi*N9+&5AcaN*D)72B}>~#5Un0(%F?G>pvTrt0(WXz~SAtoBmo<1D=;RAOS z6yz7SwKjIHIe2J&%w{lXvNN)Z^;&I~30Z0iIH{B|8jEmLZmd-E;jFA|%)y!);X5`S zd1SM4&9;LRub4A&{>dXp9mkFyWZd$_7tbY&$>^(Zyb(W9RWB^Q{F3;bOBZN&?>o%D z_0BrDc=44GLB@}ZMZsh;Tpdu1ff6v@p-7NI>W#6(kqah znx_5vFMepj)1pJR6HgK*mwMz#V=?K8$DbrYRpHmNK8D-l>D}fJg-*i%V5FJ`)>gG` z+S=9GK78t=$*I-HPl!iOp5X0H2Y<~K3;6dxStXo2QzzAT1;tmFyzl?P5AKY<^8Ux# zrnWXAB`rtUjI3g3XE&G(W>AotH!_M;{U9(`3}hlPk%&qhb7UZkP0S0}`a<~>OXoP* ztsxSNfF`Pjhz8kniIGLXizK?IWY;`pJdr4XK?uP`v&O-fUu}i)_b+_Caey!P^~TeoM=n|~1rVTdPARZ08zA7`~xPHosVw6>+^(SW{18-S#yh5%nQHac)p1R?A60}74 zStM{M%AQbUYb{l;@uZ4~fHOI5IP3;!Zf*cKCXFVO4wL0@;xNkXb}C*e*{oI(nL?$C zS77ij+P*&fHLbgUHCaAzKvMa9TNINgPmcV(D0etWjIjoJ2T5L}8!Vi1k9GI8*EeGo^$j%g<0vBdSDud%UFfPcWC zDu|}`_U(CjIg?KwIhN6oWJh2!R}7L8L|kAchTcBEV%Fp_imR@>E&Rb-&k8&DAC2F9 z?E?P5!Q;w}J5TTzO)HlQaeG;wo(5?dY0&HSFnd=DClp=o-GXxnpvU89 zF$7+-?D`kk+9AgYOq^1Z>-BJ!G8HNU7(9LiA#@sCYnUh}o3j)vs7P0aND+(?s5(eM z;0zgTD9S5=1IJIp*IReMZCA{OXqac=4n2<6Efua=m^3nxKp_%=VS|TB>vn8~BS#KN zNfRcioDS!Jvvm!Vs;a7v3>YxrZ*m^GF#hpl*REz=W8=x;UENI!%7zY@Q(Tx`pd)nl z+=xs5fE#M->R`{VgZ#b&2ci1R8NQ>llQWsDFm2jYC@#*A=jCO2ynb)Tkwd4OPMxkg z)7VsB+l@hw*=$o}Wo0Ehlaq^_&SXVlQPF9e#k{4irr}KWsWah$g9atveb2qafB(d* zCA~eKqLr(b7vMayh*FeTGX&W6F_+8J``Dw;biA_Una)Tg*gj+0MXg?cZ@9U+C42M6 z_4b0I{N)IYeEJV}Zn`l3<>LYXabf)14tHOaSE|&UL9dNQLSb(t8X^(EXo3M_$thzb zk)@$Z2MR!0)dOzwdSnPljK^lNAcLdA)Lw}!S?pZjF_)-4;Ldr@_g6f~F{NzfDf`U# z+j+|IWP!KcCTL zvFNl~;on+>^88Y0`u>g|{C?Pdw=cYP(16@I`?jr1yY0^JiN~r=$DLLuH*VJGp3Kao zc4U*A!;w&9M@L`Bnd(NtWYXBKz4EfuD=xo0WBB+{`3j{vtIzMVo;uUSwYPi3{DLH9 zs@<92)!ZJhIb9PgA5yMJNwSYhcG^!;kn zp%t9dj=)@?6&ywjSP*>BDG_wRq%i2~5o;P+qPq^(3bUq6hl!J>!DSa;DMVvY<PMiL<6monA3ysAm7cLtq~hwQkAREs50PwJws{3^C&zB3zVE zF{qNY4lv6{J{*gR%CMSMMi{iX?ocufGFZvh0DNq~ABN1#bQn@z2w!~pC4B$;cYy}k z|4<}E1z0qw6$3#VJyme2W6`Ln&}i^U6iN8JW0h*8oB6mI)AB03bpjZv8loSF=pW~| zV*JHKIHCI>{XO&_nwvd^jx^&fPd)zZ*vzaP-H^dU_|$A^@af;H^U9S!){;?gd$+C($axi{~oMIA-Aq ziDS#E(wM+7e6x0wj)%lEmyJj`#u8v70!`saH-0rib8825;JLTkav58H?%*Yi4D5D? zM0ydp469n2+fynsDiBy>qKSWTeEs#3+-c*6PQhw zzjyCmLnx%N7nEcs9XWP9W7nQzIoa9Hya9$0g<36N7z{>po9dgjF(Ck+Krqly*KsHk z4(=$-ckG`qGQTP=>F3NXB*{)V3^a*+&dE}9Q2mnFj$o~B&E60vlXG={Q zSW{b*m1MTzb*4muAkL~D@q11PM3wj5d5voM7oWm2ufHX||K_ve<#Q&9pRZgeRtzrW z2M;KQ?zU}k_~1b>Sf*nPp$0MLhfpZUu5nI8VAgCClWkVS5aZD6>BG6nF;QFAj*+P4 z>9^=4p;XeGB8Ln5;AG1i423{#RLX+YvJMHd%?eSc)gW*w18{MAZg(#@eLfggJ_Ppf zKLGQlj03yL03ibO@VNP;1z7`?G)d1Zxbk>iuMk2v94G7`u_1j87R2#k?n9`p1VCJ_0JH5{J+4`_pzN5Zv&KoS>OxLlc@|VKF-ppEn2nLh0F84)cE!MyhpO9@94YYYj2flIV$eh)ihtJ@ z3_0~kfDaEo_$SlHpMAaf)n|WvYrxZ0diVXea&Nr)PEd%8+~6TY#o?u8od^P*?dk}e z731pe+S>X^XIC4H89yTF)*EgtoN>|25`|Kk8;*p{yN?`IRyVbA2BVo%>vW=$C+j%S z^tpQSgCWnT+^kHs#bz3$*K1jpM0r9K&tY#*ALp={DOv5sp!(F1Aw#}X9_ve&E;T~T zd-2f01yeR{+^#)!x;~cdOjisYI#4`QeKPv;OG_}9mcs9U^uE??wQ7F(*t2}IE6OdL zHi(P*yTOi(af(d~m?Cp#of>mrKeTtYiW_$xMzoLTUVQll^}*c-qhGFCCJr4uUVZy5 zw;1E`m^RT^fY+%p?)UqJM}GAyL9aImk3RY+1-l_+KzBX)``;gdzr%2tOs5-K+t)O= zH4mCFdUVdg{fEUp2lgw|lT(z77tT?<`O!!G;nTI6hVG#B$5&tX|MI5~8s2;NU2St~ zH>4mK&|GyIx;nbRn&cq0LLlgXtpk#*DyQV-q#&NCV#3J*3NE27z-W1%IWEM6I1`s6 z!jsy!RC)#Avck&Km1;Jhm=bq0tXLOBY4sZ<%4q}9*8rbLrgpu9Ve5?$Wv*&h>J%c5Xl^9Oi6`r z0TBI8Bn=G>nxH#4Fu%C)#`ccZk{GxeufAr?;nP(uNB8bMaV9&psO7-19qv%5Cz_g? z3c;W^-;D-J3aRU~#fmtu~tw z4Iu~IU?6}%mJipSuAb)Bx?Mee-ZcYD25u`X98sH@nHl~|)fbfH*zNYggGx*1;r=!Y zPrzV=0rWd}9fY^vejj?=A#uu#nK1wI*`$UKC#xEvuAvQ@J6+(yB-W3w7Y^b9B(WqR zOt270$gZn-BUq_@2AO-kSr5)MCv^3=!H)@bZbk}wZK_S7t&~Lad_K3VlfblA#Gpc< z_=mbN%$qkac)5}4uVLi4am;!!7z}bkOyIoTZe>Yfy5{~L-^2ax z7e9}$TenNR>WYiRZ3hm)#(h<=X!ayioHCipu)!Pj*DaMtT zmWW?(*uWn-ehi$>B(Rvxr8Tv+bH4Y`Lnjb`Xn+gjpFYSUclPYr1wDN|a|R8_yDUGq zaFjx!)QK^|KVSiO2djxb6wJ@jBlIP4E) zWaPlyxwGRTpD!?T_8iZmg$p}2uG_qO|B?MC5q!7+X8oIt3joB0@ozg?YMK=p4zns0 zj%zsumlO#{w3q}k)kmga&+LIz)K@I^2eK?E#bm&a2_pqk6bMB^Q5Hl{pD^^7N+!~K z?$2WMZ_w!T`lg{j++~7i=TzGvp#gE;V7d?z^IN7zP*X)16W9>GJ~5^ob&v4UIdylc z+9t6|1+TL7Bv-0lS7Tjh_w#;0+>O<$f z_dFn-ZEO?G7E`QpXk~ADW=2Oa9PZ+HWqT+TX+CwLDxlXI?LT?o$Hmv(blCuFQhF{v zm9x2}L$!YQQE2ZCNOrqLGGI_C#KW9HL%og=?dtJq^agWrQDLrGZ&0UD89o9^Vl0k9 zp~u54{`5KnBquvNJ32b*;5#)W?$EJk$NuW4cV2PoJYcxOk-5(U`*_Bdqatd^Jc5oJ_4bn1mAdc^uT5MD* zS?r6%4V5gBm6*!~wP=u10Sovk0Zv%#QG=IKBBlSh7{SMfk@@9uT~RS@h(x;<=^KPY ztgU6_*rBjx$95PqY$zB_W(WrSROpI%v)o!lwvkALDdL!*#Bo94I9`o^vEbs6iQl^r zgor28VEmnNKa_1E7b4jw#+fBuCfe7CDpsvJ6m>**!;4*_Bkfvs~|tv*pz)?c;} zXSee#ZP=QqRC2Pb04wJw5e%LsvQ*i?)>kSRBQchk$f`~hq~XDwO?UGh-u!bc;~6g-vb_~&EaIws0kEI2g9>6om` z&7Bwv3*`o_#W1yb5`Nhc-XTZ!k zlR;3N3MRBi5Jrj%1lIn;1?mtesaC2K8*EnVptB8) zMb73%t}s7efncE&4M&*VQYh$C-+c2m!dq{z5?_DgBbYX_3>GbzEj{0E~= z$H)hSXTTQ>A}OK;6~!hXJMgghgQJ!#6=)D^6bRg8S`YzK~tUp2&35yot<4!Tu=zxckhyR@7o*C$;=W44;f;_q;*Qb>pgnlfb`bz z;oJq*`kyo?e%Z6-NKR*O?{r)*FP=7K(m0Jqp9E5rxtZ`PEx3B!ux{NZSoZ0c-Fx@# zZPKW@=8+?Y)sG%M`sB#rLz~jlQaU6_)p_X9A@`|M3x!7>k!@D!xu1CA39GZ)GSFOL zE;bsi1C9EUBCo&Cxnsu;|LN+Qx`E{beaW_D$Iy`@lQNT&gD$Vj(B08J>4W!{PQLi! zdFhv2GTDHHmrkjm7)T2J=CQ{*ANl1YE_`k&DJfGkd-j~3jMU7dH~r+QQ*{`OE?m60 z>8r24y3lg*Z#FIf5EsV3?a*k{d~#X_7xsF|CzDe#*9%MxFq@Zmrzs+l@Ht=JJ#m}bE)&sa-Lw=lp;s6H|eZ%F7p#Lro3{@Gx$g(R0)gB&2o znYII!6~|$%iYFw(UK2izfJUN&M>#bBHe8OVNV8d|#`j9H5_ zH3g!)hZrVKKw!blX%M6`u?@Ygt4(V%JCU@^u;_Gpl@KF;c7d^99-mht)=HsLL~S;E z-O;1R{_b{i|C8~PXWlQk^YUvKh62%HJGUNI96f$0Hgnb#E-x=j_~MICLmw~uBAo0@ zjlcBTOL{!S`rGgS9cRfJ0GUoR7;$czv`R2)sgzj2@3ou|_DS7+?%38nX9Szg8hiMW zha>O3`)T;-iM{Hp7vE)=I%9-gh{rS_DY$4f1U9=3+B@3evBw@)Ivh!{`|rPx=B+Oh ziEcW1^7tY6JB~{xj%@hg z6`dN5^2sNk4L<$s6Z*2xHW(Wk+PDE3sZw802Q-|m#rNz0tJ#JhfoF^*UA+n_^OyBq z7>S9OBTp5wG$Z}U1`CN+4Z7x)0#!LFc_ktSQT$mgh^)O!h3F&`#iex$6J~F~2SxeW z&`{q2Yu0Un%P*S)RO(8&rBWd?q^!?~ixc8Ozy&Q`YJ4|(UJ_GqoykyeVxJ&zK?F64 zMWz6dO78!WB8jrz%F?BZ`5No!g>$Cf*4O8r^M~KR>{zhqig?L@e1653Ux)_}?^SHy zwv|gwNf)1f<|)3Zv6*}Ed6*?kmod0mxlj(K^7;02JB5hfhUtEdkjpEoKnpfG^ft`(Vkqsa+Q zYab+Ia(3e6Nr(vKh@@g5l8hnCwvi@-p0iu65Q>Jla5$KZxKs|ZmdbCp45(E4^scV9 zS)3<0#cHH%$d_e6+?$iciMhWqJ*cfqHt_rSs{rb}CQALlmjKLUjd zrbBLN8MqG}f>Xy2gGQ@Hz{UsybG*WfFX#i(yC#4dPdOx870@Z*@gw0EmF&zSC5k6` z=m)yk|M8!S#R@J2d{hYfBGS#OCU^>D@IgwRW33nh#VS#`ZL_nh1M+eT;7IiuSg~;@ z+;!z#V0JQ+EZa(P1;rcoBqwxtxfljCtYRp9^2Ha@{=K{7sj10)vNI|D?3ptc4Jc8b zzz6Mu3*#R?$k}4c_PtY`DbD#rMhuv$GpL;aF`4VaxPw5(b!&I{mwmFL`RI{jr;?n; zy*J-zh1f!q&2~;?+9c-j1AtS^u#1sSD$~JuUzc z7skKsC@L@G1Hll+sX|nYb3v_AN5QI?@MdFiD=`OZrHVB(^sm><4vW|}1SFyulu}^I zB2FYkJaH1$6 zYwOYv8Z=O!SieB}g7mD0-r*F`5$=jp}57IrhVks4^XY9 zJ{l2uJZ=)sagG?1r=NZX+S=NL#~*(xmXeZ8A^5?ns_M^>$@#l7pLBsQS-O7H(X%z9 zooUXj*|TOEKl$_vv8kg=QJ9mX`TlL!i2L>(iW#lR{9ErVjofkDjlmy1aKHBUd+$>O zQ>J>o{E8q^m*pZd6X8ks3YpR5_tYk|1IM(9P zzlgC9N5Y7rOd}()i7%Cc%LXQI3LgTa;aE)UQ>lCiLYU5P!}t$C#NWe4vc>Cl7ovetkCe-}meo|xfaBn&BzByc9DgAEztmi89B?jvAgrSEd`*53j`=hvB(go$A< z0@<9st;1Dt!wugv;#%ZBZ{Ee$uV24KtJSKFMx(~zu#HFji4O*XAOxa9UvIB6J3Cjr z=icvE{q&(9F8$e09$GtX_U!o3p~DK3l5?iw-=^kf=cR_jA*I{Xhu4mz#7&Lo6}-sB zMUJebk*Vi9J367Qu^IY2?%vrmrXMdaFFS#+?LGh8KKbNp=0U@93tjE4mD49p9TAJi zhvGgmFcOXH4;)hvXmO63bV@Mk)vTpJr`It6o!l|Vnm9i@o7r0jgR(R$s_v10`=?FPy19%bhuS z0tQtK!#RlH0VeyAkjSijA;^JFQh-rs0MTlOZUhy=(QpDNlSQoiXY#o)f?6;UgdibX zS8pHaHAqrWl?iEQFacYRwT>DPD58gw{i)aQ!(;1*A?1T$&%Oh2(S)&(k!ps3-_I_i zIIb_$>S?vx!D6$5r>B=MA2>+Zci;fG_rL)~MrLLdfm($|s~z6j+Pa{oroE=9DD681 zp#NVQpMSo=5#!<$j28Vxg@q-P^%_kcf`bYn9tNs3=<|kQ>-OED_3PF(o;-GHS4L{a zng<`a=io&bP3ufaNug^b{_VB6rCIaA8Mx&tk zq@ke-_up1Yt<>v>RSq#soH!->$kF3sOLP0d)vLeWGq|EaT~%G3yXNc7`OiM{tUA9S z1LK}JNl-|~P8}D2^^4yqgjg6cF{|+Uo3Auanl$E%haP%t^-C{3enGtS-+WvEATEr5 z+o9D`tSLwqi&>%3sBFAKp{H_X`WrGx7{MteWOk@gnmgB1NrlrG1d!r1saX3lNF!3dPYzy^sy{6`MoiZeQN@X+qm4%r zJ$T|ot8})yMLlcA1nomVxxZ-ixRFEflw}4(epPLKpA-?|Qc7AH(#awp3wd}k#!D$l zqze+1LYT+dZAMO|)p0IYrwS7SuBAB1G^=+rweYo{f9)r3(J;%8a;4iclg??ZdA;i zGe>{;$Z6&CZ>)@unmU%(@j=jIAgxuCr+i#et0ZLPG!NT04z|FVa z7WmNvzle2pH7Otd)zj*t;ykSoi*V$(N6?wUXcc#D-xYiHCsO= zUmsN|b;2XRdsg`H!`H>}qem$Z962P89$dh=d%C#J&NfI&NyCJbTp1MXnq*?KoPrfI zF>MHBBqb&gAu=n%C{YMT9la>fr zt$G+xR0vyl?uIL`oDUk6Qf9QMR}7CPM{5r9QFuL&8F`A-xAR-Xlshv8ZHy`GO#ST&jK(;+Rll zVjV?BI)-br-eQH^qI@`9)eKFIEihz2K5N~Oco~Esivt2Us9!#a38X^B=^N_L7K|S= zn%XcJ0MWlr&6qL6gU9EyJMOq8aQx)ahbo3vj#sPAyvx<2B=D)P&jV%UL!&Rf_(tys zOP4m{=-s#Pc*mJDpN7=DGPK}|%SVhDK0F(r&1|*Vv?h~T8I21J$o6>s;C8!3g+d+8 zD=H1-Nn)4PZiOXpyq)>s>&rW@x^-UUi!Z*&>FDkr`p`qS4-a{xBLX4c08XtkN)d@a ze54wV9zH?UeBzjiWAS~IB4{Q-Q*$qLbh*Lh^+InbfWU?iffzhK)Q*V9Ns1$=M(zt@ zO#YsdN2m%{s1BY9y zg63!tM-=`EgM>hrJp1hS(%~b9U3cRR&gY+f*7?TkZ)mStd@aW*F&4mNo-`_oi%XS1 z`^o+Bo3Fo8dh^{+#YcX4qjdSK8L)iQE-oj@26=gf5Rb&+bZrBiJ$(ks2M@*LDev_` z1Z)xwnW7p&D5F*lF|!TAwm>W)iizv-%w&5#4mXacu0R(WU`o!mjWCRW*PLSPO z@E&T4Meta1S_!;j1cC?*b-6krH@^_7PFKU18#cqe*I!2NEjYhpEJmUB5YmE3MR3*U z_OLdN31i2>d+)yw`}Xal3bpvqVZ+RAEp5{fNI8b{`Tf6S)p}w4_s7zuOVv7^ZDe*< z(!!E~17>Sf>U>d%C`BoX=UWHfP!RSVIOzZKi#4auoI1IqFfV)eefQma*x^vslg;#B zdd_DbdDd#pNF6$CNcpsalI%$alL6x!$wbjDf#aufK78@{XVUTGM+J-3Djqs~oN1gr z@%!KNqecu@rZ_UhZCk&LC~-r|bmk2C&2Jwcf5S~zWz3j09>e1}PtHxjKty`tw=Z)o zjc1fkT3oi!d&YiyV<(FUn0>KAATp0iM;{pJ2Vf>#T{UTre0&0oxCA(hFD>3r4 zT5VdbPU}=+R*%uS7K3pbl)xu?e`WTJXDpfAmrg;D$Lk>rLXw(h&r5$-vJ68ZmtGT4 zfDu8QQQk56IoNadt6BHIzrXON6Mu`7m0w(DF_^6u^(5o(=sjq4I{X{Y>_0{5S8bOc zNAHC)Mf!YrU*hx>5+~`E_zMiWm7GTNe^rF?&qO9~zkP{n!t~;4m(Lk_SJ2%w`KRBz z#qi>5AB6qkXk4o|N!gjHU4x1W>aBKXlNjeax_Z2U{RfZgaT9es^4L9T_uP3~uF+`E zN8li-uAznR?g>g}vz<#%H8KxH@}|c`Q_^C->muPiMo zl->TQvIRGCUd2Oudk1@uCbL=6YV}P$Jv|NZ9T^Y4@J`-M7mr%#>FFG@Z~HFTw|{SZ z!4(VnoZNJA^VSWqH{W?rNJ-8RUs>{kR*Vbk`+xj6WEGV1+36{u6g>z&P^}IoABs3w zEqYF?jDy?j77rdiCARmtrO#h}Q#^C34qkudRdr5wzV@DbZzRS=FT@1QRl+#8)qsp{ z_=%^U3I&3G;i;#d3)_%|^ZUa)Po6xx;UB4zo#JpDYHn>^+TGooH+{;K?9aa5Ah!2* z@dHem${*f-t*~zEVNR*mslQyc4UQZ*A>4h(4a$W#>;w-c51E z&csV1&fz(hR7#$3FRrk#6yAC3Gk($H$xwBo3LgCVZ{X5-^EjwKg^7xOzIRXX{RDN(zg?-`5*%Yita9yzYqCT8_HK1ejgB#SeDI8sl!JMpT2s{*G{V?iJv)TCKvSkC7;g& zo$W2$H4El&pDn*CzUs3NVB&<);=%=s`L?b;34t{3;whscH#biTMFg&)t{#lIZxjtG zL(ssF01JXOK&=>Y1QHOO(HOv_*Wt%h5r_n&>B2Jx1LU?M8dAatm+GC!E*yV$_j(Zo zCh9?!2Bj^cXi0YA>oQKLq|*PA!Pw6h2>6{bMUrjie2!c zvOAL@B@I7YTRTTu5EX+53pMq1@k0j=#gl9fA-5nWqovh#*_ktS1PmX53*&!0C~jT1 zZd-10vUPrGNzp=#_l8De5q%^WW>&yaT$hfVIN@Hkc2o6%{U>%NCnasT{g!JFEnGOa zi{rTeAmoTZhN8NuD>E%6WB8bnL#7txXH8Nnl|}d`wM5NPh=SuBlY+sJbokH_ar2g~ z(9zim=?Fd^u zv2d|?NF+Z*l|resSd0o5?MR5=DwUE-1(>HR658mZ>_`D}F|WwVshLFu(|u%W>_S`? ze#E~EoXqNo?DkDngA6>#mY)5CRRDA6k&UcULM|zeGm$!CKBxsxOfW?}W8^SL1JR&l zrI{48QNqF}o`*}M3{;Cj0Sg9^N`$rzMw6a1n@!55rl$W>Hk{{GJe+H{uqF>0blt+~ z*IO;x1@FE8ZpIUjKjuAM)7WWHt2KiM4vb`Eq_-MPmKu}ZQiGX7&*?J_v9tAU+L^Pb zS|0e(%>_d%h7?nQgsZDZSKrVar{-LzGX)GLEtS#pB=muNGDk|}dbP-$PmrJH47?4@ zCIfpb4Gm3@m65?GSsg413S!Vl=Ys-Gx4VzC*(`W&RDsmg)Ki~*wyYJt1LKjm-qKd2 z7hIH`nmTj&XDhUuwr&;k^K$qJ6URVXTTAqn=a)qMe!uws`=4lyCZp+zCtg+5GCb+q zb2{zjNH~CrmLL&pr&8PEpMLOB{Hs+fprm+E?D`w7R^vHt)ag_!hYcIh1pmN6RVX_T zom$=8+BLCqa9PHoqlcB$ev*){+zkW zEt|iV#$XbvGuSy-cMqgGl0fYs6HR0k<5bS9lmSYPFhH3Jrj9QyE&vua^+U)m4WuG~ z>@@^%u4uIy&??k?C@hJ10YjLV`?6_WBBnTsj4LnEk`;!Mk{mc$bsCBa^Vxmk(HQmN zCX{ex7L*nbQHn_lD%s*W4JS!Su?S>Dqkfy-LI$avTpuqlb@2rFK;xWv>wl#Rg092w zKk&d%OoVO<+p;d(xA#Ku6Ui7S%Imuz36$mn$@$Sbs-<(rla3Dp5KK0LdOTFDEW{fKf*}|Lg~|yz zIfdY~ra(<~9WL9`SfU;0WG@X82Bflhg-T7O^?29?US~9B);6>Zu^82xAyEZ!{z$OL z+t#dEbK)ltJp9$zu_GtXoHc%$(O?;9GMNzdgo#GIY5c&_L2>24J0y(9A-iwGK(A|`=OueRC;x&47z&ZsF} zLO`a&-2;0L?iUrDTGAtmIHaN+3iEPVTg2(64(RIfK%d(WJuW|ZJZ_dW2SYJt0ZEA} zRXW5GWDw&KW>LxZ#su(BJ0W$vXz;z*wH#Otcr&RnRf)suuUEn?_ZH&uGlLR=iDAPA zvwe_S1xN^#<~qxV@xG*)K+{;EQu@O}5Z{jfp9VnCD9p(@vhM43pHG=IJ-e;F!? z7~~g}NOX*jA3lJeSp#_mB^;3&)JCBUatKVSSZK6&!!%Wy59C`Kd6k)RLMI*cn+oY>UT-uu`@}I4t$~3+Ao}*(??nQ^c45}+1(C-d{bl&<*@lDZ>FKB7!niOJ z;{pJ2Vf_1zhjH(Hgv6M9ywPmIcu|Z}8wN3qasn?mGAI=)R?;XXgv?m`MWTq8DjAq4 z6sxwC+bzU|g&Wgf5dOT0nx6NZ`R2FUFTN3`?C`7OZmzON8J9B8j5AXf8xb(}fdj&y^6c*$Oxw$!AX1%GwU@$c$Ia0hXcfh@O`|gO< zW>0$l#Yf7oSTKK}O07zbgu<4iRn>|fk55WXN`}-FCq6s=hm0-j9j7xuC>8PBYM$Ac zk>!*g&yCSQjDcUGV7a`!jFSmKq?@C1NvpfN8~XbC_(A1^$U&t;lH#WqE?oFsP=V$g zl5Nr@GsiF9v2#b-j_n5}7qV&h{^$;{TFk3$dq&O^?pm^ZO@#8RR#4uor z2+Wt0>sLjf$}SNxO-?{6UZKP#k5d>Ce9i0i_h!c==}eNpYHEi&@A*Dt z=jPzo+n}kT4bx*ItN`sm62JO-H&oRe1DDIoR@`tb$n*oW2zXH?iOJ}Lq$EAq9XeT4 zfIc(XBu5C4AaGXz3DNr~Vm2!0oC&uZEC@K{oe`FO~!-Ek7hJqpR zc)b|3hafdMg|2UCYiS0b2j@HjLo5+z)fe({C)WzA#SUHF?V!`?>?+eiXDERcPXwBqYob3H(IULRu<+K{0rJpiw zn#%8Wdv~wj7+bMoS)Z$?yKrDkO&^mYs=C@n{e*3tAnMVd;$w^xDDqz zb*F^SX=pGJQQ1M2wI|WB4TU3ITwtw4u}CBm5~QAgYRNyb%j?%4*cRi2l8F;0CU4%b zI{(wpmLZaI84Mm$&K*B;l6&*bW!!Z)&R1;R@R>NIauPiF(68W?=l=*d-*}hw#%q6o zW2a7WWhFx)FRuhb(ID*Ixf|3fgOrh-!Fl~XtisYS5mC&8T2vs|qTsE}@*9)IfS&=B z-{?lk$Eu&_;FwLfnmM14&O?$_cEu3ma`glvkU(lB1_CJs1f)DZ=xc9-Nux)>$6tO4 zTaMPiw4p^zcY>}40)3*m_L$6ONYBpV8tNL~-aUJR4fT!wgZmGLkqx)u zm}@c^YVg|s;4iW87sh`+R;)OncRJORDl3YvP^q=kBq^LFL_N%uS)(^Wcel^IVf*$2 zJ9g~a$cegLS6n)?8Wzs&UWCBIRagC4wXcNWFH%&RY@;Q8^sKofXB6aRj86a$ELw~p z;LzFLB}IiO=#6?LQPi9Q$pX5Z1rksfI3!HsTuydARF>z%p56O+{2kL}3Wfa8?dr1b z+q+YJ-L=*CXPE>iE}Ruas7P^(CK?QOy78PcK-Sn_HWM!N|T@vu1s# zEXFU4f9<#cKwKFAwu3TPlR>A7OME$tU4zv-N*l9TONT8=ZjGS>)TfFBt%J!$2o&J zE@2PHtC*IAiWg+dH&J%JINx?o_OVxx8W|BF{D_ewmDw5T@en4)2D1(RH;-=v4>T6u z_dDm34?bJ)@DFdkCLRh^zVOWBnm@k$aVdF_p;9$DtM z-g=Wta@4ne?{|_lB^&Z`lQ?b6111tm<71%332W9-+d!0y3Sp_Sr9<4Y=TPk0>#mQb zWu-?iU2p|?HRv9D>}OVF!p)@4Penx3F2rLZzU0-{l_yS}gh}HkL@&GiQmTH~_vM#g zeS+Y_KiqPU29?8?j~KDlkes|dD?M$|@WJKWvEx-@c1kLrndIc}xowf)e&HQ{XVXdk z!CyWa-nwxmTsn82_T8l)autJ$B#Si(T-_~Twi&>Yl7@4`fXNyWwJOMTNF}@PL>R(J5ry5Ju@wph(*$;S?mvZWTU%pZMy!%>aJC7uj)Yb5SvdUc_Xo%N>QYjc!cz zfr+4|BpF1f!w6=p3G^lnkI5@U!%-&msZ?rkerjd+A_RF@ivT+(vVsJGjDnZ|aOgB4 zNTR{>=MP1|his&qk~^mjhE)!MHEXs&Q*#FlEX@W8g_xXyD9ZZ>J=S0_f}%%@fMl3= zSoP&yt&L;4y1I@gCnx`><;e5ym`9-G5u6Lh>g$`9&6zc~;L}e(DV{K4a^Z>Nr*d%h zOHD~}CcAx}^p=(uE1_5pyEQ{^HQC3GpD-{K4YxHlHgt4!Ho3aHIzbWh+f*tp-DojM z)2B=gmgJ-xYtPn-SsA(EAOHLje$(nt`Jepy5g0yvFdRSCjK?|v)Zpj#hoJ+(HCI;` z1UznLJsl-MNKvj*ked?`CBsvUmRoXB*A}ve8gbFc6O?qETBn!KA5EAtNgjdfj-Osj37ByduXf(t1*X0oT-M z9Ak>0ygta95H$J;3W>Faid+P!V#@E2fKD9+v(Z4n1NAr&P%e@!FFjVPR50y{*3MRN zrY9qCIRch_xeA7q-U(I&(Sw07dk-XV~EIjznG#}XSxrBdwe<%Ue?<#9RB+6L(t0$y{l}5jq znv{4E>HOjfPB!GEC=S_te*A=Se12YmAOt1Q#BLG@p~LyhMbNbKEJW8v92V)gg~D2H8%cCSLOdVsO@6jqU*CT z(ZAT!=bp@|b>^I$lo*fPStx=4FGEBUIb))0N)%<`Mk=?jtvQ33NeF@ zgEPs5$y17$mTCi&-3VMn-~*Ukk%K zKl~oeK)$0BkFP*3Eip`p-QenUL0@kV1GQoaTTB8NXikyl5!pTSkPzWa*w3=+2Rx2? zjTV6iH5d>qqSH)@hm3&(%b>l}mH4cQ`62tr%9|Q{P6;r3Pk(ac$X8`U24{7+dn$bXn05WeEiiOQxny!$M0;Tlf7{*P=RW+? zoBZ~T8{%W9PT+s}=%eu4M}N&-e&zMBeA!#j+{!_!Jp&30iy(G72wOL9lxEDi2-31r zx%T!>2n48&LIp-vQbNFs28>!YA3>rc6qemb+3v&1F#(B2vV92W8`%JpQ;x?UV0tua z1~$mzlEk6J@Vu$X+8Y-ge>?&W2#8b+8VOa^b@1NjU&0TrUW{uvhp#GI*Ai2%)@mRt zI|q*S9Og4pQLNO$t8ip94to}8MRIv#&N5dX~Fw_4gdVaJYxVpCHaI1xCl z95EP@QamU}Uf7>T~gNAD&K;o_)NBJ$=w!#AsKFCd&I9k=w-CbQ=RJir}>#ym63**8#Z(IN% zE{y;4gOjVqxNrT;`@_TrDJ&Fi#xdB}X*A6DT^=Hs=VoLnp@SYUt#8@w!z#sV(+Ihj?KATnQYV)yh3T?i(c zhS7sctzgv+^6|tI zOD#$1%9%5#kG?u9Ep4>V>q&a*cfXCl^!$rGO`R^E(_{)36c+YcZPsR^+0>vjB{j$5 zs?e^jM|II~zq>zn(#_CNQ%hoYd|GNc zS)I8w8cprity|sjFC3IKR+d-Ja5x={x36EHwsy_BnAv3FZolJJQ3!{kPd)Kuw7RxV zoHb*rc2TK&}>ciiz$bqJw#V&%&1o4kVJa|5rudr(=4`PAte!5xSx92Pw{a!`g?-CV&n zAFtwn@r%dB*|Vp_u3mhF=9w2>6?3c(J}EUztgSu8XH8Zio$ z5@dH>8l0>xM`h|%RfQCKAkONrSVBi@K_C84 zukWgF&P&dtL-1GaIRDx}R^NzZc|}f^b6!ucf0|BZ$VIdhF@cE4N*wS_NKrYFm&^0z zmyr^fD&kw1l(mVICXGSn*T}6~yG_b2nJkV>E94_FFT%qD=P+48N=|`Ll!9g-xIG^5 z_PG&^@IpA^hj=suN=&q-Qg9)>_weG|S1Jb!8} zyZ+gM6B7cA)VqaCw%;F!Cnq~qCY^TJp+iT<;fJQmr~gt}aw7SNCT;{4wc+g~?P*F* zsXlq~q|TO_YM*t{ywsydj-GQIpXa2uI_wLx&E*zhY3o{r4Yvc56|(p(s5oExoqBU5QJj zwz;*1GkDzbtkhI4lvAcyu;3b4zI>^)V(Gi$#g|{jJ@b+c9{JIOFh3Z8<*Prz{agh} z78{fl4}c>lj>Ee3tKs5HE|D;SGA2#-SU}b*sw{ z0R$e&8a5R{-*l%L+`ZkU!_cHW%3Sb8s-GcNPN2ktE#IjU>`K4nI8eUpCm zn$6JX^>QP|jNk?j#=RTie=32ces6N()9dtfyd{-V&1}l3`a!{TJ^Bx}Y?I2|4?&(D z)M9~2IgeyEW3!2QrlhE;30ZNij;bG!#qyDF`f&sR!tgH~bLTG{JfuA5x{kKGLF?CV z;%jRf#2-KS0GE;Bj6eU!r-iRptrn6THu3!rKG5RbHMidN6W;1fQVz(magksPWFVtr z)M+pnp%??1HKh?jGM;k>La^+!&ElNd3*&d(c3pVh<(G=#NKo^G`yMow4=K0DqCu@F z=P#hu8$=Si`t0-1M1#Q)d*Hzb=&dz2H#aYxHtnK+NrlM87f!qycQ{s&ot-nI zyR(aLYwIMulFv>{Qp_GV8crNMqdm6k%h<~=zZ1LX`?si8eZ3x9+k3^5yevpYcD|{; zo_CsUkeOGEU}6Ll=P>KR)^a*l#lW*`pJN&#%%+!?76pP1YRd0K5~62rI1wg(i}xnR zUV%w4=Ja07_SG64OO%Nj#ubd5SRggY0S(Qqkd*8I4bD3Z5ILNGq?yV|tj~Z)ut?AM zcsybd!7zhGZ^lt-wb_e&Zch~?Gl9fE10Vi>>jfpi?d|QEX?Ev0yavWOtj2=404hOX z(waOe+EL*+=8#f6Cds6Je-#(Upgrj7?2eBdF*d5x1QnvFQ2y+pAM!u>)${S5&H&7s zKc6?_XNe0TkO<7f>&J$mvDspWpu>*|vJZNCd&!rPl?aRRFc^gZuE`;J6~}AO@9Kin z)qPN3e+IHL^-z$Pgvp>pQzqH~=%&Pm?SAiX9#8(URiApuuPkpF7b1g7MrVKX5N!zWl2sn<4#deZw@5w>u3#sutlr zskNbrE6mU5TH8Bd)+O^{`{uRWtIs_v-TD2WNpDy)`1@~MEOiAuuxs~Lpnb)vMQZb8Z)=fyQrv|94ZGhUk8u-<(f1~_0Dt<`3WoMb^nLWyjRWHRYF z5^R*Dn1qWiM{B*T-EdxS11I;Q_9ytV_x3lM5EM*PVS@ti6X(y@#**77XwxSR2ssBh z@A(shu|54g9SM*kLX4>>*HrL{`?A)Lga{_FM^sv=!lXFfFCmc-#N_dFC{-#XFnG>j zLLOJGl<;%w3_6W-_wL=4Oorfp!MNs{`^-bfOd7E2>+K`IfA_qpI*WB2eoANEiT%=( z&%7FX@8hNZP$=NZNXhLeD=TWt%1Q2&IBj1z9Pu=^y7=z(u<3c zZO;t_L*@-%Z{v@hJOjA}nf&y*GbN+h!ubOM=M~X2YmuHmMZ6Q%|`ES;Xd+M##&}qj@8$)tY9!`@gg|a_!ok$#$i9 ztqL>~SA4ZWv3l*s_}q);@Z%;_NUK+_h`;j68*Em6^wB$tBxkDj4^O?uZ{Jf5*WY?I z2;nZyY12qH6SbxwwJnI829pFWiF0xaY3q)?qQ>A9KK}SE@#L8^>Xpk^nRBxX`5*lF zJ_|mjB^-)jW~Pt~$aZ(MHNs0TysQicg8a=l-y#hgI@E*r`RdI#-`ocO!V&4~I^L(V zu1HGZD+ZRAC!IP`Bk2rgoC_8{Hz^{`n?8a+wSTSn_%qLjufPQE&Reh3|Kg{=CZCFztL5DuzU^X%`G6pO4| zL&2=q{6Rln_c~b=H^hSmK{AVyXR8BqMeF;A^2E$C`kzuha%-k@!jDG(4^5BGVLt*sj3PoB@kug6j zL;2fBpOjvH?E7Lt=?HPmgsHqOE1iUOL4mUWt2Jr}9 zeJ}Z2R5h*vLr)$*J_bMh8NB~L41gp(6S@JDMm{%upMLu3=0LA!^wFlyOuIc>5B``s z#V(4K<=N7kpRR!)-+U#E8I%hxjqPyS9cFE0B#JDraG1o#;tYh~%F=x6fuMUrZ(kQ@v6z*y zSXgEg>S<5F>6ws&+gK}l*+BF&R{Ji?BT*`Q%A2GJM;g$SyoGJ2ll z6Z$IDQOZ-xPz1Vq+@RO$F}c(t02JUAjARknA*MBl08j)Y;lzn!FmC*0sA;H!*FIVa z58kp6cycx&L2P^ky%y;~6cv{8NBsNMPODj8Q9jtSYtO#;(UT|qT7x0DW#?Ayyo)a@ z9#A~s#tm!MwBW;bz`x-j(6*_m$y!}qS7LXjjVLHCoM<+hM&b#|2uH&Tm)pzYIJ?6M zeZBs`7n|4Ysjfb~#$id?G;{ixsxqX^*FU{t{Mjz`i& z4_c)LG%t{!{i(dy)#F0eF9LQ$f`LBoBU50g!l1=~0eUzRrt)hf50x5>|1FqgP;cOO z7J#7R_xb1BQ_MEin4Ik78ObTuaTcS!5dWlewKc(ee|k%N{*5=>jm>R+X(`!lbLL#! zR#Z^ZC@IBucb{9RKHaEjZSIcPlag%r-g{@sO*btbY_nOiF)A}3KX_ENV)+({M3v@T zJe|uyq`%J_;>hE^%iRlpWawlj4+Gzrgh^khUu=!jVuCG-$SYB)JWGGR8Rt(nVw~tS z5xdu~wBO#=241g+vm$#Ci^Y0Nic5|i>pa#8|DtjGk^?GFr2CSBL1QmnvvRfV(@(#M zr{$*dH{P*WY;HX(JooI2k={Oc?EZW27pKp-L|lw>4_ zd4VaaV*(=SwOUS)BKTP&Vs*VI_PeK^6wMZca@C(U8YNNC{pL51D4i*GV>BFA;MFFn z5Uh~mg812IpTX80yLpGrF5G&{t<(#3y1To3*}u9vWXhB&!oh=Q*EY6wPAo6Xy&^5O zPtg_V{vP&+OUpjbAN~J^#XMz9%1jK!eG%a_7FI;?Uv}aHf??$Ex;7 z2M+D$rj8rOS@mY<^@TB+qjoGz-qifLsw#=qT#3vvK#f75QbTJD28Sg2hY6nolRF8o zaXu(pNs>@50tlefsTc$0^ZFPAs>S3vHQ521ckhRklw?3;0fP9qIIGm5Y5;~SNC-|s zpWDs%^|?j6(`MAEb>((@(x@(1ZyN#--_ZDf8weo{8&*PnzU^DKY}wb=r8}ljYZhCK zh7n12yUl1aTeNz$g~(SD!DCE12Gmrf#V53Fj7FXKw2vB{6Qp17K6n;HKe4EvD< zRI+}0V)z)dOaKHS3wYi*gAUqzgHYGh0?9T#6Hxqo)iyYCvI#27vXHg*gV^tGg#T!s z>-7fC?RHC;lt6BNNk*@`bM~4ITTZ|~IYFa+@zBvjY4!D|tQKpI@T=dx9vnBZQgPK4 zgH?lvlyFbJwF)xR)8V?glc2J+08ZC+vVDlOMnsn6##vhgRVrxI3NY|02Aq=)1Ow0T z>LooAJ+74|qZSN$wQR$QJ7NG6peV^aW9CaOLDj~YJGUs+lCPj$ig_8=yD$&w`p2MsJ*5efPoTlX9v+}zfz z%}h_z`n^G^qot9%Wado%ta95N;yTgU(+gP{$$-%KIZJ3rIF?YmCxI1R-Q9rm0DSi0y-4ag~GA;&K-N|4jnkQT(8r8K6C1XqvyA+oG-Xd-@m(TW%`7%qsHW9 zIOpKmn+P)Spzi7O!ch_kE>|xc zK6DhO&zQ8OB!VXNKUR+koz%Yfh}I{SEV#G-e3BVD91lHI2lABMRfT&Bql^p z;?;3>e`e<>Tfk9bz$dJ=Bo4kQ?N=o1I5BcpCOpY_POh}TgrojF zWui)ho+PQ1yAJea=jAnAe#OO}>wr+3u$dMZ1 z)X^$?R*ED0t~+kYyL{oSqMY1J>WR$g>U1eSU%FB}b+T5PG|4FfV7te`}9yQt<^aocuoz6P=4vdP5g1QZR_J7vc+BzgFH>V_k>|iV& zVWu}~yg(N(TABRNiWThEBv`p-6Wnp#A_hdpga~QvvUUdU1j`bNtoi?7meTC-aD?z?Nm58ixU)LGN{8Plfll_N(&dWIcAy&#Cl|Pco;NK7F-hcDfG@IY zC~}bTcsyKVW3#B!8P$UZ4w$%l&APVNK3vgv`_=PK!ar#&TemLvk_i*8aobZTw)BMD z&U91j{ont-NvrvF+O=0MFeK}(@ao$i@w3Jb1G8O+`$+_Z7=p4=m;qQykoPdPLIp;g zn+67k$-s=Bk{D##^$1Lo7%jO3ky{YSfcOwdi;$>lj9M))HAe6)>h(fKQVML?TLmYo z>S6G}Lb)ALhBV|UB{4{!=DDQRX%z@^YP#CnqkezD=XBct<>mdPPxI1CFKtM6Z1b>VjW8j%Kk;t4^Ir&8Ki55+l#|;L98q(}m21-aAwJJrKt*25!;HQAV zgaoIm&Om-vKHR+cN_h9Z4`6UXCOECMW<{98OBBI8Bt@hoy90&}8L8U1VY4~Ok!&1Z zQQ5I`*N#Ay?o^%GU=8g*c%Wk3m@(r^2b3><>#et*zv-r%{-t2@KWflgyk*NNJwR6~ zrd7H1^^GONhLw#mTg{^oT+PO%QB7+=REQxF6bHXQ4xvy~Y;LM;YHe%Ugc07#abwGO zjvYI;``c}9{Xef*u{o)wqkF_Pm(RIKp-@c2UlAFj>_!4=$tnp(vA`aIC<^5a?<056guF;T8mqKZ2r4M-JOq)I&eYD1z*O+P~b3Nq8#>u+4Y zp1H0~n@TS!GR+rx|4=YNZYEuZ*YdIBr>OuE0sHavFyz9b4T2yX#B1Y%2FQi+7smwv z;==fM8)7VG6-?T23>hiJ4dN0LVHX94DRA7kbIET21sQ6fe3Xpir1S3t-}b!_Me#p$ zrZVgy0W5Ibx84;cyyqKeO0xPyoHfXEj4dUhACa1pMfrYItw9WCC>Q_?#-Ok;?R! zhZGcK6xu9SJs~|}#0y6cpAt0{LXn&!zUL)JkDxlk~OLrCNy}c6iRkJ{C*#pw8S{;xPGyF?k{{axegl` z5J4XH$mT<9ODouIHVgvwlF4dn@17O_L@tDM^`GChSQ4PLUtL7D4 z9A9I(c<|84h}mrLE?Brgi87D3of3o`sCBs6i+$aD3Sb3{ZE$j2YuWaQ70M&oO~png@uCnoK6>?C#_n z8k+h1!W^AeqbhRP?L#}-y3d|EmEkKZZEeAs z*vWFWT2qnYbe1P2IkHSvi$Sf{Dnh}ia`O0z)}1>x#NK=N3-QUvpLGB3*H3o;@#!VO zMOR$fci&w%B~6`snH_K1`q~=)&DWQSU#$L$+qiihS3bCspEi94pO=>ndRlA)5y1Hc zdb17!-T(v;0QC60z=xDfFgA{fml6R9jZKd%vK@ik4g?|^;n0~bsH*LNVWqh+FfSQ& z8ax-#5JG5V$En3Rj_WB|9pWw7p@rI351cwt1$MIu#*7*XufFp>tl4=OE}mEcaRi_Q zP(xgbvgIH-IT?C;dzmB3@#Dv(k;6yYCXN|>O;ycVp&=A{Hdm*shrjdqwsyw{pMH=( ztT6LBrBYS8dFP1}OO~!ZF=R-(&+0T34JsXA#JlKy-T)u;#=(P$k`X}@9qu1g{H@|- zOHzdf=O4}pBd(8nC22kIto4HCAXTKOl?bRQnY0JnS7YIr7z`mxkHABOz+9Z$I^35e zknLDCTHNa}d9FH!c;f=OvQK2m4ZUrJ?C6BYo7xA=IA7HYg|?@!*U%UCe`hp|?z!h4 z|B4kWRwngy+jBEBE~~DsPd1q>F^$Qjs&8)*veJ@OQzwk$fAsUmB_zaQ&9XP4Z1e=- z(O*BSeB*`3VbQ{Q{JS4~0X5wo258wV3K%?80S6B3hmYU?7_PqdYRJybhK8m(korWh zTdb@lgZ3nHv#=ou>A`sxrhZN0rYTNPvXXtC2_*9zYbk8&=wxm?1_UXoK_iY#h(8p< z=fV9>t7H3IG#H0Hd-lWZOJ>8+$|3O7tM9`T58jE7!{;EdLaC6oDDX*5mL%n{ijmee zYgeY`W@L3mh7WJAI$hIs_H0cPo-4OK$vLs8py+!1+KF@Vy}y?pL}E?i{+ap`{jYiU z>{(TNd%GC{^3+&N%&ajUhOl_A3xTHgjxOl#@v?T(7=qVSRYa95^df9;VLC220}vD;K-*%s z1I|~7hXase*F$!?ojx&qy8H_mH*PFg9ad(u%PIJCt)~%;mzXFme!qLqJ}4?GhV(Q$ zRap3(j-;b6yzs(lxG*k^zcMZW5EsV3+rX8x8&d%cA~`0-js!I^j zCM#VK&p{s~Y#9uPG1<_w-_UcgUR+Vug75`A(Cuo)L}VmLQ6KD4g`WeIC%0j zbaW##;PHbOH(fe+tV|e>6D4eHYn;88zsho69 zUzGZn{h0i8V&K<{K|@Jt5i%i>kkg)fYQu&NE$~kq{mIDfw=YrMc-6T18Yy+ui#6vt(*?S zDuYI3nk95i;5_7vnEZw#e$nT~;HRxq>~ZyqN)@k&i4mm~uk7{D24 zSt+ICsVS-a*|YV0N}5wdV8mgu8b^cz;hNswv(z5c{cR_q|LP#e0Q}-h+^To&+O?&x zuP>{kqrJSgzHUTJh>Xb2&MPY_C{BvTIGaYJHr#&uV*8bsU)1yQ(og!HdFFZlTW`PZ z#+z^Km@&P#Tz`G?qQ#4Bk3R7;^+P{*^B*l;&VBUZI(X*iTf~gKQf|VmncT3Ud0{QlMX-k2GNLga1O;8oJQMXyP17TQV@IH{6WZGAVEdud(A3lj6@v;O zJIyI;I&g8>oPn2Q=K(wqNhTGz@G-~F)Ip`i36;Yt;mb7}Vf?UxV8QblhzJb4rgejy zVyFs&K&Gw^SM19#J{PZAbfvMfY|w&T>$dd1{`zNc+;-c7zw5m32OpL!S)!Ugeafu- z{GvN>swLlg%MW%nbad~Sdif&HZ-4RNz_(w0x@_o>fyUa}X6|rR4L5i03`{tcpd;Z& z!3SDZ98CB#Vhcb{uH@EY!uh2|;DH2a)%caQBN9r_%hiBXt{=wFPuc=ha?TVxL|Mp_ z5?xBU)H-UD@k3Uo6OJ4>48fql9E0MKi0u4CQ-|!OaEI1uwVYO~6;%oq-|O{Qe18AG z97I2V%$qmQ_35Af^rhG5ak_iDCp9(J+H!IWRgy|6G&DBG(^Av8qWl8o;_GjW@87#w z`PJv|D`(7FDE;OS&vAcz^dXpi(QNL$<*TH2cLefM(zs-$UK%_Uq{I7maUXv45iGuH zA>`#|LVaxm+ovoRGag0-vf2Wu@Vd8{j39db5T(jHJXdmOmn3&(Qr#0ihJc_pOir^i z)CjEOvB%>~;1P9iiXssfcpmYPcl7kY{zFG$@uFGq)HAQa=U;DwMHfv3E*u9k(IA%- zDzc!}-or?!Mq5(yba!v>olVV6)S|T( z{s-ecK#lSjQz#^vPVPAvsOzrlTeN7=|EL{@t#QkM|4RFnrl6qQY&Pqh3WaFJKj|@m z(YH3Wn!@3z*=jW>rKhH3;C#9n^PrfIARox(;%TDzNJ>fO=Wh`%?82?x z7;<|&jj7J$&G+4RpX>hn?}rQH!uU($0swJg{GT1)3Yh!zd0vCjEiRe{+$T)9{AzG} zR(`AU;V%c;=YGpGhKCk#ViZUlfchZ>1`G%ckT_rafpu<3=hPv;RnJUM8kg0?nTVW1 zcC$EFS|OKC((@9E9p$5J!(faM2Eb&;slZ_G^y!lrP|pKKHnZ|ovdt0_7NAP7S}f3_ zY$GiL-e}S&BC43SudnOx-hz=(X;yT0cB<=KE{#rMFzb0fbH|Ru`6`XhX|rUh7cLy6 zJ-ELvW9!x(rEXt$UQS+`W5}Qq^}w=x&SG(p)HXy(C@C21-n@P9(e)d5?CGpX%EPj3i%C|HZhB7YJSw2L!f2%~O%b`5BCJnNxkm=aUgN0vyf zXCO}qlSYo5Nfh#i%t_Lpdz~cObBJ^3?Qua%YZK0wFy~B8C2PALOfXLqhywq_(VvWr zn4LADthnsv_m(`L_3pCe@!XsO{+8RW6+P~*=!<`RGuYVD629w}d!$P*pJzC6>;(V) zA3g*HL#FbhM+^eJF3#zJX$Z3VOQgp+lR+!H-ui?KSe)vxuM|xbY z)m@#g@|@JP43gn;B&Um2dk*k7FJ1&MKJ#S!x|^xHcJbjZxjg?B&v1b%q`os4-7A>%3W z42GHy9FW{}&HX(ca}wfe)wo{NoIKgG z*&k^wcJ;K6YN)RoZ?#y578ec5NJ~vmvLu=8cieqz;KrM;YhC@-x~`XBemS&Z%f{fY z-MifnKlVHE#%mWFZ@u$Q^=-G@pv9YV|Msoo>#x2qyz|^6{2OKmKW^Goe&&=Zd~s$5 z7))9)n)P4_1;FR=lJ*7o{1Kdjq~Q}M83`~Nte8;iAt}j**K0jAx70%W_G2&@1M-R? zC6HueZ7dL>q#W5lT5GLlB_?$o;57pVn;ixg7eRG(EquCa6Wn^$Tzvir(~aOnS!CGi zuygL-`gmSmc68geOfMU-O&;J8L#ojxB7&<1N77Y1Gx#r6i{& zU8~b*zSraGa=iT3r%T)8hRvm83ePV6{H2u7K6<10k}2Z{MWa#cb4xxFle5xc?wnDO z>NFr2(~SvLFC-`7@go6F6@g)DTn9<{Uc@q zHGyd4+M>kEJBEo70$r?~qoSf1Hf%fvbq#$`Ho%VSN*Ls3M;VN#eH6h5-fA_8WQE$% z*K6+U?oOgT=ey*xaK$aR)V=@m^UK}dKF6`DGnE}3Eu7t&qzJ@y(AwH5mKPSl7wgXQ zH{EfM_LC1kG(P^=&y){6^jq%Pw~7@%x?`box~q~~vt>Ufg(gV1rg0f*h{X?=;OOB! zuF&OcZf%x3V655?v+W+^E+gppNSTn>{4>ic=fsA5St5gsReSgGT`o;7Q@SLd6tz2HtljBI(b@SO|sbr7M6|x z1ws10?d$gapyo(JSJBYi!|>l6zy9^F*>u2j9y@%vUJSXzAVYy93` zmk`s1+uPhRQ7uYVtBpx*I`F!S#dyBExrXcZc@$#E&Sw^+s*;j8joaO2NKH+*oju!< zq#OZBW6hJ&!v0`tI1+YBl3>;9G)4smeGao#tyE(CsE~AcEVUAe7*TSc5byH)k<9S= zB(K*a(f9sfn8b21uCYe^q2TFYAh6n=tR<{+=vYmc<{wjRuMWPTU-@C)NRGA;lR7skKc;N%8L6>_&~ z(q@oD8wQ-qZP8S|$jJ*W<9Qp&SnpJ!HA)JKOma<-t7Hqs7|5~ZVwry><~ z+AT>3KoAq?2HUItT-yzEH})8o6Mzh6P{hgvDS25SC>##5;FzgK^k=&Gri{q$wYIfF zPmdoQDJFHTgI!w=KvTO*%+Ak; z1(y#&c20!4);4H!d7;}AgucE2Deglk=*Pe-$~I)VYetg6BZb@(NnY3@9dKGHF`!fN z8dlXBiFh#Kqd+bQPG>4^;>vHP56u@bF`cLgB^<+`#0}kD9bikca|W}SJl&gIZdY}B za{51IgFe3u`v<>#vFyfqGw<8Fd1J+rw?7pr(f!`{?&C5uQsYnm;kn3)wO>UF^9rLc zy!xEpTBnReWAWjYqqt!eL%U*PY}1q}Q##-~ zIPiUij~_q2p`o#PWRlIX$eH9+d))!9N~4E$dv`0Yz40pH=$X^v^KZPx@4ow5aXd1b zpR8Ojc`?|}&MknJ+GuN z?8Z^m6oNQB9y@BYAfSp>9bj0;l_0uD5HGm}&B{19^;19xW|OdU54{`B^H zuxkAdxOmoh5HKJO6G%zP9iF8~I3g9~7r>4^+xf&Jj&r6sJG(op8bNNQ zI0x$fy}*dX^Wd9?JvEYaICA#v`i_>CVf%OQ9icPmDvFCsO7imZ92%{r__BqUWX`*E zZtv!;JA-fh={@1AuRint_KDxSUwGwhX~C5Xba&l;qj|)HNk+UWf8Ejque|Yr@b-IO z@EbpUj~_OEy7H>4FXso8t)!J?cj3Jb?L^$!NaUh6RaH4cU=#YhnIm0=hDvIdq9t;-C@+Sb_=-@2V@5qgi}!p%8&%tZ9+$f0W?xxK6578c{=Y zNP`Am#ZhTDbDT zhXRT{duur@uZLuNCYOcRavV1yJYVp^N1wuVix)#dW+v3OHWN^fz?mM86>d;5KNzSL zPNM+7L}HP$eibi8S*IyaJ#zS5Q9Li*ZXXbklZIfM4UeD#6X`Gm+Lf&B%Y^H54EM%k z$B)B|X%k@9^hxm1@>TGYyKX?XTnEumm^pw^af(*dNS<(5J!|&7(8+19jnZpkpI`o6KwzglUr>8fX2ddTlpm0g(5Ye(kvS5NSf zSX?E<;yMoU4Dq;NR45cC1Prta1*hR{23k|`5>%dZETbEdLDUi(@dwGxND766k_Y!U zuh%P*7&tvICgnW2>g42@;~tNjOH6c=Z<{iXGD^XARk^6~*gWZ*#{ z@L?$4$=KH20lRkYBaH>bFV#?4Q4V9r3_*}I3A$W;P>qC2dsiRyBB)Ax2kqx^yq?10 zFw?}rc#3IH#OeK0y9BEyl34o%ZQS(Li1U+2u0V+(RsjOoY9;CAM5Gn#HpBF3GXOa| zE*cBtwVMz^$M`fxz&Hjv)Yi8D;1MVtSPEu5-VrI*ZZ??rfBeD6|1xpy3*&#sxBx(0 z82?t|)TvWkR#p}+($v1lOVj}eatQ*pVo1a$a$LfkzF&g7e~7ZLHYKRRP!NI$2vA9z zD2qrW7Iay(G7j-`k2zNjAxQ~m3rJXOp7;Es+*{lKv+U!Y*d$mfYn=Jp8?|Z{$PutW zZ(E6f*Xgw2_E0N<2b^gZhDyrByL!6d$dRL`jvP3? z#H6);@zQHgcYJ%Z-nDC&?!bYQ!yR_}qH$w}T#`u=;rLPAJ-yPF9s8iGy<1FA&4eqj znm`>W(AwpKZdWh#x`W6J3E=kmA%Mq{ZJij}?qht~>-Zg(HANl9US!z7+YR$p;EVEgtT5O?i8Ko?wm{P;2Pg3`j; zy1K?)@EsmiRaMmnbLy988yf~y3@RI%ot?(#mlg_NeDyWo=jv16Fn5~x(dS?A&%E}L z^w}Su~+9@SLcHj4lr{lF68G*uiWx z5_=|w{C=L20i{Nr054@11_C)*`wJ)AyOL-Itsf+)>g~f|6cb0Y*}~QavfPYEBQRys zR9LZU6^y7XgA}`s3-$IfMH>Pt<3fzn=?#2dZl3z+k)z7Alr)^vszQU-FxzJ^)to+k zy0^5n)YspN@%IKIXcTcAMenCJZ=)rws%LbKIWMcMWG+DE! zPoEf@I(58f_paM|-+g;o$Fk4g_rLkttLpdOddE9$+6?`jx7}o#F?)vbH;?>O_p=|} zCBF0i$MI+0ULJqod*4%xpEOyy_{yvKoSb~n8zY=ftHVUr1g<^^m9CT)3zxm;~ctrim%+uirwo%Z>cU+SNH;>r3MGiC(` z3@HvYwKPc&Jn#qY(xpqo|CO?GIxowX?beE_Q2Oe%`$mOh{)-~eI=!_~NR9<#UQT8C zEG0ev!``_6h_9{L{D+_aY{-!E!nv;A-pn;?w?a!-pZL>&V``TXc@@H_CZeG$8bNCELNP?1_Ngm z73S`4S1Euw1OCNU#@`;~ob&wi&u=R%D#{9kqLaJ2yOj8z6;``N9qI}zH3%$?tQ_Y5 z&F`LePaHq4_)o7q;ix)&BKGu?zl&sLF40V%G)+PB7KctWNxZI@b6CW{Rlyb1(1`O1J&(qY(3WN^zs_P#lVE$Q-!AJk1~+BU&Ip$2vP3f=Xk2N>gwsk z`K`qD#sC_`d!ppWn6L_0<8vT3iRZ=-hmW3O!iHxXn&6Y=Tj7=k)0qAg*$HbEYEbBv zQX~@PLcxG#@wGRW{PwrM>wf2>k6W&}_R8+Kq-y@;!)1ZbzFZ;PcJuZ2k>e^B?B9DN zv}@y$-;A9$v=#n~WAbE>R($3w2!&!3tE;Omqef21%g#y7arLyO;SQdrP-t1D8LP^{ zMBAQZ1dCn6w3~cE7X%}6>nq2pm=z^WF48R_ml|4Eqfu&C2+J{mn5Y^M;!-pk7D@8} z0SJy9#qjl|fIljR5p+Pjfw$N!xUS@I>FKE)UGJel%(G+1zTGvor&ktcXKk6a;Nluw zFQec7Jb2A&d%QhE9A*cB4zuxAjzpHJ6$y)-+Yi9;qg4_DOw!mYt)lpq>~7_-Rw2&?1R8iz!92VO+*ae7M21Ti zg9i5oGkaghPM!i+cQ0Ii`NiyKX8q2F$l5fR^}9|D$7*Y#qq`e!x&0QpLrfuZRo%Ys zV=`fMVO$u0eOv$_E{uPxp;oIoOym`M6X_|4af+h_L6kFg+{O|8=D*QqNa#2)J1f~D zkEGy)sK821sT^NSv|SK=&QNle7|7ZWwB@tk^#c@%vIXXxfWMbL)e|GpqsfGw&i&#% ze+6RFiDANILapW#M7#W2Mw1cTnBWr2#>gT`PJ)SsSMqB1U?V0pc83EK?*PjhR9dw) z5*NtKS@!w+|BN$djwL&t#uyGJLT22k9I@vH~*x>Sc!-kiQL^fDWV9Zw=Hj7nNb;t^3 zNHZ^zj9^&84n7CLh23FbN*BOunO_nQR5M$*3^+CU&)>tpx*t7?+cq zk5fVHP-(Tt-+c4UUijw>Hi@pg?)Qc#et63bqObdk-~9fM+Uoj-I0hH|qATZ#M-Ly2 z{Pd^4j3H|oec_p>6r}?Pn121}vx-d{H^X(e-_7M_SUHm_&Z+3QQ9BSKGo+?2A+y%i z(I)!5eq?_nvArW8#+Au1aAXDV^$Lonre;3i_JbA!vN#LoVxZM)n52r&-4oxvW4GjT z`QvtnT_`Ob5Yp>SHJ^U^X#;$R2PK4O+S<0BJ$ZUmdv{lHW@@s&I42h}9O~&C^7)D{w;h#QS{q>O@DZTEl%MP$1t|`8YZ@4g4hZ_a(AMXN z&hC0-O1mICD;1KFp*P{RMrHnCO4JavAguzj-=olx))qAtXCrjT8uUWIgw`R>(t>weU>u$Ssf%B>> z7AD_u*PX_jZ@pRZ*{AEJ7hilQ{GR|9$orbfm>tNcbAy7780B|hv4uJ)Oalscs0JH_n z_H-CrHV{@|GX25lE8(tduf!wfVdvRww?GV!H;Kq~x3$^STFvllF1^fNS6g@J%;_`x zOp4Iyc{8Vm4jnozu32+{f8>!zq>74S%>DJrB5zDlaRzOiEErWW5>k$6kf#lXLJ9<< zw9CK#G*xTXl<&e@T*AuguuC zd#CMeV=HJ34r#&VL!qKH4@m2H=bmk_a^-5sOv?Zx0$@toPgGR-1WeC@O1#6N7=p23 z@FNHl2*wzD&a?+`9%?mI?Vx1fE}1te@q6}Ivhu_^9YuDQfcjv_&&tBp3fbQ8_`y9~ zV_P@mXCh!3@ym`+9G7qvQh?27mkbC%AUSQpU3j=J*jtHvMBR6(4THx0_ut?B<z zc6D~8ha!>Uu8yvFMtX+ebUKy2Jw04%l0DSYtZ%*grdz$cwyYbu>hpIU^DwvXmPnAQ>jKk&Wz8Eb2JiVx+p3M0e7{Q zRi3zddf{wS2V69JI=uVg7qIzAHB1>=460~C6B~ggv&n?t#(9Iqkb2|ow^Y9H{8LT# zWNZE9vlrFsI4<=5`=1%F(LZ@cYw^PuuUSMJ_;IJj!n$tN$msEk#+eOoN`uMMgS zs;OxVfyz{7GFs+kWv80(*Gik!PSQ(K6hAAqC}^}=j;inIL&p#W4@Z1bb5lLn-QCHB zqqzD?lz8hUo5Km|sTsW0Y84|O;4(5& zc?^s=jIB5lR3@AA_Vy0fnbXZxHD}N6MuKJIv}xlHB6!>T?a$WH(P2nQNg0R>_FM!X z=Hjo)5bQ9VI&&5d96SZpXRAq%M;tqTBwt=$#sqaENP5-PHA8)SAGEuA7_h)99@w=Q zlv{PF;sHSivdX1c@O+IjdJEJtLEsUQQ!4z~Kti$_Jm1PNIPFPb(COKIzh1c(a*(Xc z&&$T=5m=iP#k+LEL*X!cEhWy8g9i_R$!y??3$q{;i+gn{o6KUOM-l*tc@A;v8zg7g7aNA@cg(TZL;ZUW$(;Gn+IS#!D0C*L?ZW#uFei*sEnM= zipgd$)Pa%8@z-8^?VrXFP^C+KQ^!T=N$Tq!{`nKm-N#Rf6UL9?esI_AQUqzB`|iCj z($Uo!nLBf~c<&D$Fz(-bQ1RPF?z@;pwD)EkvbbI1y*a1xu%Mx2Ksv9-NT z?C$LmdoYsi^>LypxriS;XoLa-L+;?-!<AuggW9vz z_&l+AT55XOZnwK*kr?&9f9GwxO48F^%hzsPx$n@S@%G7+#$=?V@FiL4LfhGs@gBd2 z8FQNsAQu6H)(CpNnK4$ufDby5 zscS#nj7(Q2WM`NmJ4B)Ogl*A$U}N_>p;e#e9*7l*-^>HSf*kc&(Y^1cgeiitBWmXiTAylt@EsHAcNtYp|J2R>VfF zYMa%F>vO!bx5xE$Q*GVyLnn_P2#A`_iItV$>-0HJoH9#l_Q`VCxP3EJ4lV(mO2Nwbqcrz$jf^3H>+^^3dX7N|K?fl&mvYnmB+*la65pXp zWZ(lG4`3BjY}aE#cm%S`RXj1a22--+@kSBk(Lr8XHc$g$TbB<(xD?igsKD1|3nf(- z;@mfz&ET-x>h9+23QHm z6*h;SZMbSa28|6J;_(wFV*zhKB2Apk%uLBC+siLBCo@ALHuCr&hWw6%4e zz))rv0)x9tOS4Yl`{@`kU;tC&z%@*Tz?s=*Gp47cq!r;o8->@zWW09Dy}qFC@Zl41 z=-@G_t-V{a+8r=v%y<}7Uc{S_hm0f5rs>;==fM8EkXH z4M2&=tv0Sv1VoW<$Hdc%Kih)AsO0MMa>>cboLa3sPc%}leqaR!60ov7rVGH78YH>@ zmHr8|MGJ6#A*3YT=S3yEe+bh7wa;m*kgcvf>I+LCiTT z52S#AL@-IokKU$ADchcPW0Ff7A`=)Q#F{+;K>se3o|-~Iq=Y~V29+8!25{86Kwe9} zzjn|9fq-rQuEVo=HFtArvZIjRV~o5t6x6}O@fMTKmgYzwQD>}O^u6yra5_6ZGnAE< zhKPq!5%mW(UoQR3GP z>3xKIdwT^87!>_r5hm(Nr!!@o!Egf3|#mIG!mm|zLtC|S2z>zNhj5JJK zXyu7|;WA_~-&?*4MhqE=?5Q0>u0Hu%z+gKP4ofzRMJg=J=g*uz4QZ*#B4)NpMhPZ| zBGK9d>kstd8sPb#IAZ*F1_F`vVO#KZTTh6imh9QLZ&ZrYHZeOhyD}*$1tG~) z>ra33^VB1Wcu)4FS6ynwn`-UK zwc_tzcw2b#*S{1B2URF9zUU%lZZ-lD1})^MQXtcj1YJHqGn)>6|`Cm4vtd{h_`pAr)H_8+@k#CGQ;38 zNy*ux3`U)uwc&6AOY2F)h!{=oJYQ$r&qTjB+!~P` z=%&P6T>Qv>Qk#X6wfHF*h)!UGTBAkQ8NXsO${Ot1R7^NPF}IvJo3m`LArMTh07^~+ zMvWDYHy(uYakJs|KRpL;zV$BD)i%P&;YD&4A*W({7+nt}zG~3xIJ@1(k($3jr!$_d zs~d39MH33~W%j{$Y)~u4+O=!5 z;oe{U^p}C(JpS7m>z9A-oH%Pv_*c(92ai8=zk2-0O4z#RnAF+T!|^FeOmk>}W)vP5 zHJsSD9X|f#OSp33e8|pBhBlm61Q%$v8o3e@fnE*92fP`9p&&VuV6w`#K`Qu+F;Itk zx7?U;QsS9pv#@qe(HFwC0%HgT>rMqF-aj71YoWFdib_gh^pH{T)%x9V^HsC(not8z zy>|o_;XJTg9HQ6fR?eI{ZIGwCckV|YecoqrCLf$UW=ueBFf_jO#=F}ded=lH!Tax? zJZW{Z>fZ!`)=NRU~N>0wFg4s0V|fT%NsBtXZXPKq7k^q z>htmnd5lkZg5}4pHi(@2? z*{Q}8q?MFt`BfwiCol2rx|47c(D}9^=x4%sSINb2o{+V;l0D$a;UkjI?frvf3~ zO!u6VE#SkU5NlsG7)?-fwi$t1A6$3sXo<9?jRt*NC=@zP^ByjY3*-MYE&vc0#{b!I zUXaXf*|LQgBiQv9W{}B(^pG3+4guH%p zERhW&`y;Cf%T}1Ou7eCBFu+2T?K%Z2b^NA-BGG#nmn$jcKFq}TfBstX_aLiPCWH*- z#h+&uN*b+#iT{X_yy4JhNmUTNJw41RgAjTuxr77Ra%WM zD-;r_f41?j9Yfqq0uv^UsZ*#Gb+K41OR2y^N1k0QT}SlXtToP_HF3hsnG=d}19XWo zGV|h)o1?5kasZ0s(Ij zqL}zckd2IzjUls&BsuUHbF*NgW}NI?!B8IDVKrncr&WOwZ^-&0NTX!!2E-(j022W_ zN-7h^V=qL);KJnD=k_AF(2dE336pFkt8Afa80N2yGy{HK1VG#zITYk&=L&d!J96`L zs$O{Z*>3m;j`M@K&AUz%BKUCk&h;B7Ecsw1Z%uZJx8HOPKX&AB;n%x^x$nLI1Ns2=>vw5v|-^UtfRt=+XV_YU`?oH#RklOHR!hk)NAiXt5;? zyy=!3axcB?lD74mcGQ2ld`-iKbsK8ldgGP)_uqXdb-|SjGJo=;@8!+9c$Nci%CFaM z5PtX42jM?F@mpm{X}R*Ei{~hdix7w~Y9Ji}p`@e&aQAwlqrDsYkbz9Ls4&UKpq(EK z7GpAWHlKx$zTO9wB~37X*dR#5BvGM|ps}?UYI!XTtr!F?J#FxVN1uVmfA&3?Iy4_# ztu2yTh;WZS@(}-r$AtL)1Bcwj8f}BSue(7Kf!QE7~# zT9u4*1Azc8&JrcE1*JaxK3AVqS6dTrX=(Phx3`{cX=^#+?hWj6q$cmj^|L)$oveHQ z_e-+ddpmN}N=13uppuCL$_9*4sg)^d2s~JfR`KMC!|?9A9{@Qfj~zE0XzlX(f=qBP z8X;YWIK-&hPn2~>I1;1OAmF0Y;eMhan`xfrud-h#KT%=E_D2F`;{?&OPDiFWU{>1@ zz-@s*PX~CqTM!T{hNQF%pc=L2<_4hR|2}#gJs&Su8uGlF2_)K+?0h>i#rXae%}tF1 zMvti+hV$~^|I~)@A2DXlniY8V*;jTH`rU_M-h|pHN9n;n~)fVDy zN=i!7^ua@h3>!M6Jo&jH<+>{_Ul4!t@hAPOzFICWy6jTvkI%m-U4H2-0l_@I!D!Yx zk{mp_RE0tjuBD|~YH4kY38ENHNpbe#@od61u@M=6A0DnK0uSL}FdU79A|a2@>(=Si z{*;uQs7|MAi$o$-BS((x-LYebb@l446MK7mMrgI_fg{Ha&M7G=O2xQ|T3*eONK`@X zp*6Mjz+6drl{m0$pqQDS!rPn{4)+$q~Whaq|l5H)S@O+&9$^ zvONM@+GB`1M`ICEu*Cau2{B2zgkMSs5Q>SCep=*dV_=U@*lG5cHNbgb!};USe|KJd z&&l?X)RI8~p;9fs4!t(@02+*XXv0m-izs?BCg{qzO7>1?gJ<;`6tWuhdM@De5kpG3 zcdA0c<(${s{U3(Y>GYjEdGhP+dk(bN4W`)+r+r3RT5`DsBNVk715Yj8kpEIDGZALb z1eH4RNPK;eT|P0lgya+_iDCsYsVeO2b0@o8u7R!1ZBxJa?5jrH&O4VZS>i!}BY+#d zM^dSJt$IfwJ>3>xwQ-}euDY?buq40CLD)qdrVbVi?dVI3@l~4ahvQ4V&mcg;K%!y*{bFwqC-G zo6E_|!+0**=IL>t{NWFO_>WX=KNq}xzIJW$#D;j?A$DGQ29wLgF4S$@anef+KW-=a)UQu79&mLE(iR2pdM=tMTS z8^QtxEvX1RB&TyZS-Fswk;Rij`;n?MTy;Yev>{uQVNc;Mo;pr^@9n3hGbc}RmtS%b z1~h3v!BtmxC$u)SaD`(lxv4XzOP{Y=uh5y)3TlDM$jFMq|DqB0dQV3awqj#LgG zRF;*VlO5kz)gboxwOq0`4wXaF#kFge!>&y;q>%%26(6nJ0pll+lvJSQtVWRuaw;QQ z#z-mnB+y_`sMaVj5Y)2#ji$D%w;MzR4&6Py;^EUhpynccR;nIyvy;J&NktG@>kxt$ zpdzb7k@eq`n~ov@Y*NzK%%YL1sHnUdpSIy|NEH5|BQGzHlnzfJP+4uU zsMdRTA1QU%9b>X`(`QwJ2O=3G2YQC2(v>;^lerY1pp zYAW>hd7-7b8B~25P?04n9%U_}YOP&uu=`*W0&ATxc61R8EgJ~MS!QVQoPpM6Kg^su z8J4bE2RHrbXYlY3Z-MLP&4!4pOGs!rAPj-3+stIL%R6365;DoLqUNaQAgth2Ot?{?|)&pwYKiy74Fw0^zG z5Fa?OG&MObW3s!?t!VH!A%2^bSyoY0jO$yGQLjxg>on@DyaEza7N33TIoP#*9}FrR zgr7A9>YH1@&%g(z%&;N=K}m=f=cmD-VQU>(K$B~W9`^xGLA5uuh4gPDxZjXfG$*$& z5+F}#vRSKREUQ|KL34dIoIQ0KdU-hHrR%NAI+dLxXUFb(g08z#RR#^%b- zPqN+#VG^0?8F*Yf5Rg&v2*9VGsybV~aN$C7L2|)&aD4y!cQ$TZvt~zUM`vz4uFPm_ zZ`C?%wl=%X-X8Y(G{XlMDnrqk(&C$L-n*x8t?{bGi>EZTbsK*2qaQ_*Z5G9i3ola= zO26~q38|&CgUdcGI``7+;h({&U3+_2}fg z?|!oSO#N3Q3-j|QjHnnoe*CD)-0@#7HSgKK-}i^7{wUmZ!;Rc?LxzSIF1{o_WN2A@ zaQQHGR!)wBcI{+$Ix|2%e)w3Rxw)+>EvqaZn*RQr<&ot+1T z%F0R+$t`Z}+O^`t4?iq@@x>QKp-}wFw2X{Pr;nIYoSbCU^Bl(`8AH{O(AwG#tq5cW z{1FM`32Dse3a~q@NM7i8g<5X0>+0@-=Jrl#Y3;;g9YWBLid00>U1IIO5ddgCrG^SNjee zh6xi#Gp8y9U}T{%0y@ajGZbQ#o>2tNsr{m;sDMv$Ah_l8cOd|AVDH|&U2tJs7=OdK z06<(A|EI?JtQ>(4X=xWLFyB_Sw+FNq3#ayC2G1#@A+OgP$6KFDt@2TJ9|{H($jpiy zPwYN9Hpw~;lxXrSZI#GMj0<2gN@dC%Bou*pxx1Bql=UiQWEz++Ixo`3Acz7_32&TP zYjWpwBmNQq@gGJ)oHEAV2k8>XD$VMC+eeVwI+C1DICSU?G&I&jYI=pNPw^M_@PxpS zBqGzP(Q52&x2F&THzWSe^Vi-&Sy>roH?aHU$*$vCt#2iMrV6_~X(+z`p*?hT7Lz3LDzK|D zO~$aHv#E`>OT>bGY$I2Yy(S5S3bGgz*^J5B8l=%6Yjx0Z!ayI7i<;T#QRRacrvUX- z;`CtE6SQOV8WR{zCZ;Iuak~+)P(XWY8$MP?b422amIU&~j&dS`MLL#13SxIxw{-Nx zan5RSNCuNJ8jMDJBazrYnjv8CWyOjE`b%b3UhnSiS^Uf2{@&c?@rYMkHlM$8(PiS6 z?OzLb-*Zo#tQ(*C!&8`SU8&i+Wt-x6k3OlKdf7tNutAwXRlL!F$YGoTb;QDicu*&m zLWB&a!hIzB%?PpF1Z@ln|_|`jba|MOj zinBHK%G8u3Nt>|aj4PDfq^VQ6Q6q*)dV@x^n(Y`5bAPin>~~=haQD>PA8+QGg0o7p za?*wjtx&Gma!_#LI+5mZh{>5noT{%bzH{py?%|)@CG9(~pL^lOCDMc6dqCoFuBhTM z4wFHk+IA(*J1x$`m=Ix>X#%c60+V%9A?vFLttko-{9Tt9#P%~TsB7(o!VD{)gMg#M zWxDlLK`C3yf| zKY?^qG%v;Ry^)f8N=gdU*4Bw;lNEo~*ewR*sBq9yHhzCP&ac0q^P01dSWLuiZ;Hrb)0vj&!}-wnrW>tOnXkx-PM4?X@K=8Zx2p8MUvm0ztgA31U?MUtXTzNp|1a`9lC zksD$M{v zjNvqj;!|+E!{N~AbXsF58q@~;UOp9*M!h)+_U_&(F8R|t(2HQ|u%VThhSi6fZk zGgD|b|1`*eYjn6a>NF%=N-YowlBrnRg`9BGP8pT$hZAy3f)r1*6=IO7#+{2=GmssR z;~aI7YJOui0&6}9BZ%Jk!eQUk*_UrPwSN;l^uzn6cXYY5&pr9OV6wxZoi%N|Iuea=`%j*Q=B_R% zL=Yl7tpJp~9``i`oI17xzW8h##v84`$FeSFTLcw_qyv^2Il3(uD|Ni&gH8& zuY2k3_u=NNuDs*&OQ$}td+!PTj~@BMQ%^tgQ*P zSC1?#%$tos!KBe6Mr3`u?0v4fx<0&e)oP)mvmFkFAO=+dJ6-_WFDxwZ>YlRy;oru-T|;k_v;Ocuj|(&xNr?Ul5uQeCT%f(RI$i<1ocE znD`UXA;yFR5FjMFYh@s!-%w3)?qJA&=gG5*x^ksBYci!6l2&SsQUNBt2Cr$n6OuL! z^>r|C=umdOo~><$Kp@0TpFTx?ZbYo2acVsl8Hq!BW3&debs(71)&VnS&1bZl5Q|YI z?a2%77Z=9gIxYYZ7smg|A%hQTinO#gmCa_;XpyE81cj*Z`w{R@iWu!NF&vH~lA!eA zDjo`kJsOYSucRE6N=T{Sl*&yxR{ek=gMv2TD6-#vVsl80oAuBMg4ExI!GM4$F3!Xp zP|qFpVN;Sz%p=jdq&h(CL!4Dr(7Qb6#R;-d1Z@#q!fs41zm`Nf5F{FjmDusr212g` ztk;&1ZmQl$OU}c@yaUdjt%A{GD!y?M?H~QfI3_tt+!$j5CK$Zd77qI{XtO85e*!@C zkFv640wVU~W_$3|sZ;uJIGkTmQ85eylnN!UEX5Z|M|L$yq2P_Yf@dmCN(y}NLr~v8 zCPEVZhRJuTIMAtd3LWTlcDqHFo0kp4Ugww<@^tyT=x1dmJ z#6V`aPDiJmi-}Q&OldV5slBZe!H1*b+1f@-R7VR2v&k=be2wYp&VP8D0R=>u;$I>} z!nZv3+vn1bRUMCAddWrH^@|otZH=w58?L=2=JN)GTW`4*e(<9Q46V&=%1bW4fiE03 zmY*|qIH$qS?GD7i?~Q^li0q5T0r{8|WThkPZqb9;WW+hGg+nKs;doUwgmDgrd_FE& z73Z$KWNg@E2?y``-NQmi2#TMtTrSL=Hy3jTo$=U-6MP{iycVlPYVYWf29=NCFT3J0 z*uP^3Sj=VvUXynCUp6d*3Y)|;r?xlOG>uEi%1#+jnk9Ce+>fAvQ*>ku1xHgrdGPQV zTu)TebH96-pSS21c=6R|rFrw_b0)1xva5|;Tnw^aYi0v1sWDl=d5d$EEFMXT-r-C} zR?>(8qaUFw1xk*q`l*SWt84Qr^W(bo24NEvU5)b7C7Q9m8 zK-D}tJ_mA>l3?2SN_gt!m*M5d9%e#paSwGE$!r>8gQSTAK3|ACdh|Gq7+IkI>PviSJgL}Zbj_$yaJ^OZ#8!}|jgtC&s z1C!FSM&5hxUAdRdzqsM^HEWySU$(q+>-KH!TeogHI_iaqnLm5z`{j6EvOX9;QCD^9 zg!qR)E)hR{_tn^E1}8Ud`fPsWuu4Tns$HTggsikQNJHj6Eh!gjYpS5Jt&SplP_!E1 z#K}6Ssrv#(R+Pf@NmC(7jR&@;9dhxBd;du3F_}=w5i@UTqpLz_wSg{te^ALb49||s9&jJA- z1For6k3eoMg0fn*ifIZM5jZ5!L4os!XIh2?G)qhlnM>G4%K@cC3@dw_f*SF{0YDHG z8oN88v!e$AToOmsf-kCry5<%znlT|mfGwqH1mxpk`pE-#!NLWXfJQA>83vK@HR5|0 znHV(BG!0Y=HKb>xb2YVfczh!Ck#n_rzHI5zrTf2&Z5T9ex@k&y>C#U%7Gf z<@N2&MK$$jyDJ9`YDB=VOTj7pi!QxL`~JtFjtNsQT5;gOPX5^^em$kDrIUZ`w?Fe6 zjZYg#R#d7aNzI)&aRwRqHb_g$g!Gh5)?z{X+VO*X;G-3*;fe(_AvY@zS~@xa*Lkq% z4NNGMId)O`Jqal)b$D!Pucg)ry6Q2;kVL-Q?FEBI4@q__0_jRTPZ8EuNVQaib!?Y^B2gG!2E&8EF@*+o;Cz_3KxGZY|U5Q1Qa7>>kvhdn9p_SiK7$kG}Ke>Mw7&;i~q0r_(=w?iAtE&3o6q`o@Q9Utj3zIWs5Uo04Qovg+c$|K&pu zRek#yo@f8demsB8Io}#M@q2RDSn<}D4#+`4?TH7(aS6N4C9I zs}bW3O~Qo&f#E3Gf`_52M{c#~?C~&%HH!Trf-G)MqLQJItP?~f4kRwgDjq;eH1aZ4 z$;4M+Euyl$x?G7#pde|m09AwX7z1N0W;bdOh~jWB*Fk-CH3ShjN=Zs)wA$I)YDi9X z0+K(8BP%i>l3p*0YYjaW2Gnq}riS3{cyVzdAHff8(5Y(SKWXQ^FfNR5j|%|Ah4Fu8 zoUi#n-~)bDA&aL%B%a4)IgVfBxFZSqdY{nQ=|KQO2&%LyUrZE2)n{w@?!JhEnPv)c z<^@kkuM`P$T7^FJ2Ux=w^J{VLDftwFiK`6B<*}9X2EHU6qOHYA@hDO$+ zL9GP@fWWy*1Yig@_2J`j0KeZ)YT_E)jI#0AWaFP};cpxXyE6hKj^Qysj^M+mZ@&3T zib`cnA3tVfdY{*m9FN7Ev6x^r(7-6x?f3JsXgH3{TpW*wDi{cBG%BUmWVR^HHj8L5 z+eM8+th5h(3}ht(Y8(CgB}A|C2qhv1RqFP&V(B(sb5DB zU<&#??3j{;A(cg1Ef)5D(Bs98JkHuTYHDggZ?v!tzM-iROja8oW4a)MWHcH`*FZc| zT@~N4dp8%1gdw*uU&8r-L8PJP;K74^@DCpTaCibH40k{I%^*7!{wT+<6y8 zwrt%PUUbz}{*T}Pz*#xG!qVE=uFTEL6FXX(Br4~eJ?A25s;gDEwl^d@(o<4N`vY&^ zDNDWoY}_$?c)nomJ`toU1!NR4Q6sB70wTO# zBlPxZKoQVD5W$9hXS$%axknsYk_~y8PF70mLk5|y15QDJCSLmr*~+(}xq$(pIq6w2 zYG^r}ZmNUdKKlwh^@|@PE2hT##+lZPtnTiEoSYo~@WCU}@#CkoLxz+k+iaGRxW@Th zu0SuIr+u^@{44Yz{al3i*wD)_f3Ruvm>_WQEyn>$-eva>oPVx)^#1S~gQe~IC(*OxWU zoO8*C+c$5Be(?UQlRA5PRloe@PuRJI?x~kgGn*dgNU(!L|{#wK73$XODmKX6(S%iz}BsYVdm5d5E*bJ>t@k& zs#L1D5Q!+VGctPhvy#u)h_5K_|IEo64j%Bam`8GkCo!kI&jmXMIHFN&p!F$i=!Q# zo%^je%VmWHg~L*uPP4;i=EjYwjML2@2#Py)?tuOK_VGJ*ZILP}D*21&&NgLaB%6^m z$S*D_n%djrI@8(H^_8nn`DJ%^cQpbKOfb9VL`{-WZySItM4DEohs`?<#UFqARb^pW zDL0^UC|kGnR0X7zZP`Nz()f^J>FWzXd$$X^-7fq&m>{!wI>d}v3WsIAg?PeRlaplY z^@KVY6M>~iu;otbFF+FeKfHYC*xz`j!J<@)?40YhJkz=&jVo%S$jvTe?K)AM7mbbe zP&%LxuR)9K>PAnDS9%=5Kx#i`;UB(6)u}U5Mn)DSV_XnMBs9h0sM)e*%Z2uf3*&Dd z3b-&XjQ<0pe{-VBhwAET1t!@FEhfGyRgxke4^X=W>vfMh9HP?W5i2VzSR@40Eh$MRJ@@%uWYZ``(7%U4H?OHha`qIjZGa)1Trr#jRid2UEPc&@Ob+m z6brC63~C4A6|(SBJT8Ml2z-En%6Ro8wxwtNu2hm3#vsj)K`}XFAcMgK!YL6j;NSgT z51wNTzv2;S?`VOBmKMm$E0j9ByQQ{{Zt03EuB6gRVgfh6Vw*Ouk1zdX8DOv@ z%)RI$P7^tlr(4j2lw6+ zTDx(Rke!|)?%KUutJdiCmt1}=xA(w7#qIY$fb+(Hj982dM-}+loyf4ILrQ82*|RYP z`aT4%>RVlKvbqK88=4><@qx+Wl{9Q9X)bNIDPs|w5PX6OwUMBm6erOhr;0MfGWFS(3o%_kh}bY4`OYtE%CB}rIDU4Pqe+O8`DNn*uM9O=(aF25vnP#;SA|VAPZ7tB%)rsfM1j#9O{2K+Xq*0<^pc>OW!TH_Z+bLV;VF*x; z?Dyua`ydzy00In{q)@lHf}^4axr#_6wwXVDrb?++s}v4Ll1`;kTjMbyl9QWNzhuc0 zH?0G-2GiH?+Q6V#ShZ?R&&riwpC~RY+}qUJaMEVA`|LJrrb;0VDJsax8#%PXTwGKr zd40Z~-Mh9nFI)C$!`k(m+PPRXUQs#NaOM1Y`l}bsR7Tw0!sj1+Dy(0(8G?!^pOT#h z1{1QHm~>?$8=aGr2Wn#LIcm!=FmcMoJs-A)|UVZyiPb3!r2C`p))-%d&e8EzUqeW z@L09#^YE+BzuXlLd92MX9mx?+vxhE%}LU5C5AUbCw4$}1O{ zuD^Ow?z;8cR3Co$wm5vm2xU=mF^9<~--qCW8_z2)fcTs59-YCRN@dDr6}sZwj0r4y-hvu|kL z@%`L8g8dN@^78Ycv!ergJYA5Lktu`!AXlsq7)B2iqA?BuqonlQO#Rl)o4UPjPuGyr z5fP(V5qCJu;g0SO@9w>O+hMgVou}z=I?=RPgM`ip(lh{+oQ<`y)PCa!fu`bS;}SD#@r* zfW@o>(rxHyZh`}Q55UOLd&{-)UYD;2FhOH-P&;RohUz z-)GC$N&^NCg)w6)#Be0siu?T61qJzszl(doh4I}S7b+kwjQ{oHd}Ms!z=2#xM+em$ zAW2}Cf(9XG*BIisQODEM)1~BSlxS~m*|KFEZWNJ}G)rG36bZ)Sk`Whuv9YdR@_RcZ zP6#0zpygB+o49f7cF}INiI-hor1S@4_&8uCaKwr;u{dJe33QNT-3Z#?$s&n?52QAb zsCY;;Q!w_Cfzs!|g+Kc&{onnE&nW}ivfBnL!K0GbgxG=~H;$x~G=_AaK7AH4vyxem z!QKOI9wbV~^Ri$jB`#E&O8^QoN6B8lcX)Yu#uj{<6YxJA=ZQkmEdu}T@e62gZ|^M` zGNj_|cV9CV>I{)^T*Fr#Kdvt?$ky<9Zm5#M+vk?NUMuThNiu7ompscEAQWYmn>?#J zk^7O!)l)^HX$U@8RLBD8HJB_=kFkIZpc|qgKhIh-cwVGqWwR%N#-IhS$H%H3;*^mM5ECfH5$!)AQ)2Xbw-R6B=NxheZubDyZIiMOB_06u&7h( zDVc0Jbg1eq{QXCQ>Hugs-H?})lX>@neLH48{p{PyQKKgcr0QRkn<@O_=RXO5_QjVG zy+$o;*|H63R)_Hyzj~NkwfqzAvKxMapqhgLI5dBgY<6%a*}-ZdJpl!{eO~DEMKDV^i?f8?NvDV)d6Ej@JqI-1B4p?(f~Ae&v<-VbzLN zq@c>b^xA9QKR*9Lc=Du)u}?mHCo?y%)Oez*E_VA}_qZHpt8mer`F6Z)^K`mo6a0_n z&Od*w*nP5h{G4kWHPP;nLmPyZyG{$4S%uN5lV>wMk)u`B(9+}LAARC!>9s$-D=k>K zKzZM-welfS`n#(z4q4*73_e=W@Z-u?0hD*W(b>M)36u6S+46EId5Oh>1)Xa^A z=Bd+}49Q6;$Z~t2qq`ftnqG()Owiif1#KG+LP>!QSm(kI zL;YEtiZKKiFqybw&TM$=iKkY}w$!gIQJ2ccm5MuMFvd2Oc1XZq36FKfG)B@S$spi%Tw-BH~P3 z7sihoF>+8zX~~g8M-Cp{x^-vEkt0VsZ@YVE^Rq8Jn{m~ZmlxiC#~r!PKKq#UXAj&D z&%gX9>7`db6*jKk&QHH+20vxg7|6{`g`9i~6b_n<$EgSz!df_dzzj{i9s~DQymngP z&G$coExY!@q$!hO%H-)Vs(cU(DkuO?UoW&YH9+}r3v>=;qGnv7GKnJbE#I4pe)WNQO2cWa9 z4Q{z=0W7|127K`TQt8%PZxX2msIsCG!3`}>W$63%?u8*khu}V!gwUlxLWj(KAsi#S zNR^_Yp)PayunMx4u7~gVNX<>H$Ha1ZhueR}@!HzFKqT4{3Wa(tmW+5r^m&vz-f-J( zSLCf5|LUJV1vOW^RK<8b6u0}LCOD-$5_ zjlDR=TQqkSLk12Qe)m0h_Pz4rYmtxP<3sc2&GR{(PI1<>X@RvHHZ&~#{L?+X?mo+{ z*W5VjM|a)z*wQaQH{?F@$ou#_LE6X3O7_&LQ~gB%|H*h59)1`ky8q_Qo4Lu8C(|qr zEjx8;XKGK+iC8RVZfR;yYG`cC$N4$H=}0a~N^$1uG}_c8dy@5v%NH0B%taug6B-)p zAQ{0z6Roe!jfV8}jD;n|#i>@4>9Yra^sAn*FFx_cTdywmA^@jQ7{trw&x9R^tEA-2 zJWj7Rb6&5Xfdg>_cDx=x^icH!k{Uh)(WoSp0nJq15REe6f!b|}FrgL<68|LOY`XB6 z11}?~2!jyi(pf2?10l(kZgNEupKxyz*?vv{GyxDg5;(?v#AHx2vV{b7>HapnCTWEI zepUfhI&eVZ7;*`bbzt)&&P31E(ALo*5ePA$WB?zFk>#4y?e_<|9S+NPb?v_}zJudJ z1;mB%zkVc;aaR4%-QCUWbUI@85r8mBPQ55#Fec{b=Sx1HPYMJAlEGl0`$@|SDCEs6(f#UyT?=1lADwA#D+WWX$yK7qF?(QKF0widFAi-S*8Qgtf5I?8UDHN-MM$}Jh;=voaxT#BU`_!T2-}{49ojii3F;SY1De= z%D)8s4;oXO-_;5JN7v|@I&mCYh z$sL_t(B0F`f-cH~Wbu|na==v4GvLF7z=K(*V&s!zG=zb?gX9VMc;$dq8f_SC==BC- zc7`AZ@^p-8=C!T0P3-Bmb3{hzbb3Tg8e(Q!Vr1dMGext>C^MS$LV0;vVAF>6!rtAx z6oMENCry|L`S}HYgVD0{`0|Vcql{kFVILj9weW|$Rwp+Lf^RM8Cj~vd9lhJ5| z)bvzHNHBt0FM}Xner-K&XzjFPGSm*0HI2XpJuq%iI=5ok0eqpvD|sA z?6AAEc#Qw$mtNynTz!c(7>*cfS{)*$NMbA`Dub9%xt-mys&7aUf~_6hy}fLW$-t_#i1*0KFTaMlv!}DJpP-)z4&`jX z*p2sr$z&m30q*$mV?ug*I**lj6V?O`SiS2SHDW~5@ZrN*m69I6^(xpezx)#V{PQpC z@gCouoRE4P{Xjs*$uscYE=o>HOdU6VxN$(qK#a9jj>@WI%g}ix%42o1nb19DZK23f8V)4WE4cIXwUD3-I~ppTdsa zTcPaaF*tGTFznsEhdmDNKOmkwS)^G~~8{|BRpy-gDxI&`e^hwnE!4j$awbI!SoQ!Zb2Vd`g}ejfhz z`&FUI6DP_tGt+3QpL6#*M3>LcX)!@I>h+*C8Sy--p|Pz4Dr&1m>iR1#D(0wa?!o(? z#%*YfrBB|Al2s!2R3e~6U=J&naKuv?VnBtCffjO>5Q|YkWS{~k5pW9>=LjSZu7iw> zY`jjZ@$&%_b_)p67%RK5u>mnIE*=$>S(%v`nOQmd^&8jOT@F`APEJnPpj2{M>FHn( z294XdZa1_xHE9a7v$7^k7&n-tu=DeVR?V6<#sAe;U*Y{rOJELHTB?KY(o%kEP7d)g zum{F>REWa$X+IHY1A-M?N=gdf*x0CSYHCtP+S<6;vuC?8CbWO?#b>Qwe(}Z0vXdwG zc6D`ZYwPUX=J&Yw%H^^na;35kL4$TIK6R>b9jL0Ta^r5f4JMO!)vBMV^Cr6homNrF zpn_X(y(=vtL8~7$bdcKP@hWn%GP&kfI|O1%4#9OG!dT}2JRU4uIKl4oAsFmo9ZPi2 zMS|2_K-~@zR=lDI>wr7eF-t`hfr3J|9*dmR<HlrX<+D!*#zG`mNvolhzMF^xN;;$QMe$13GkB zRu<{E>sgh^Xyjv^ot+{+ClVUpXN1N(91g6UyNMA#v$4K$Xj*D|!i*VHwOco>7aiR_ zqE?SVVRBaN=!uiD#->K`l4TcB>!Ez-j@=w3Xfj^T1+Xf`FaajrDUlsAhT@0>K+s5r zAd*%yD;Y@OLtLse`YQ#D|33iH_e_j+K5)|L#a{`R2$aBRkj(=#l~9p&M5L!@U=Ujg zIobJ;lA6S1o2V;-s%K0Zl#pRY>{tKt;WCw0n}T3Uw5h(j<+~r(9{uAVpF84o2kgeQEM0=lVpJ>8?aEac z&?%s@q6T`f8qAIeRTVJ6R$+jx!%CkW)q$`vY^ zn4X!ARX06W6MI+{hZv}7*0s6036q2ltUkIiA@dQrRt!)^qk{we$hr&pUlTDML&w50e- zgIayVWA{H{jR;)iybI3b#*Q5sTfh1T#~<##t1Bjot~W7JKKJ}ftq1n*)*`?IS*5dN z)91}$a(c-&D+Z96V76Hx#7Dv52|!DS3##in;pp*NaN0XyOi3zt|RX`*!a3 zoOk}Eu5Z5i$feUJdGGzhL*lEiK5H5`Zb-UJ&L@~nCherL!*u7JHAjEhr59@EOqryg zG;xe9Daq3P$;V$*oh++!Uv|j_+Ncn6Y+S#wx~t2<4IVPsh<`uv=Rg0s0)9vK%vD!$ ztAGAESF2JEs%vVK$D*Q2XHAx`{&{`G?}-Xa&c9f8@#R-2ue$nj`I%>)B^DPBjXK@o z*sh)1xv#$Xnt%75kA#-yb}>IMBQ|N$RG7P9Hk9V4K}TbaxOL+ey!P5KdCTA|)+AP` zCS@KLY|_1rYmEYvLK?eBNleB>%mln#11gmftKc!H#Py)Nvkj6hiI8kfAlY1?fSxLA za=bqXydx6Pl9D{Audl-dcpaRza6Tiy3{u-RR?Uda(&cnfQYqs7z?Ge>kRxEL(CO4x z8g|oY4K3%Ne_ls!Zm#fq03rI;njJfK1V8%ZqnZ%|M(obc%|4u$pXYTrJ()GN^+OE? zL&~sW1H~bO2iYy=#P+)Srq0!CeyaQUlaH#sj!wte31j(%3+HMsyJ!*Bhhf!sYhr7D z*d#PIIXHtpkxNN7b7rFsGSf1lqio zkb=)8r)GdTAqjL^8l_Mpuwj9M{2a*3$pV{Ihu?!>EQEoE_Y^XTD3I1S5q%ZcH}Z$R zBE~APQAXOUkjr=tUJFWmZ_wuv8fx3c{fBGBz2)6d*^MBtE)&0FGz=Ux67mXhI}kxL z8nAMngg{JIDr97);I^AVhWkhf&-NX=VDskfK)!9s*>mC6n=XOutQ2_pPcMo$-FypN zdDRN>?RQ?MF{9|+cmE;u=Rdy^I^&Fm!l+Rrh-_)`!^D_#xSfgvj_MT^f)ylmOUY45%FvH0DpRgp&z&L0dQ z(5?Z!$^aIlRaEOu(B+gVcrb#KZfV1W@gW6CQC z94Y5e2PH{;6KFxB7ziw|=70J(wdd1S6~O|GS}kLM>4E)6K&#V0B7zXHXqZulFzzct zA@DPU!LTYPFDD}}Cs)39!-h7G+ifROS6?I|)E(9oKem{TNWtZ`3X=yNh`gHO0&p(f>T)EQo)fZp1z4zXG6$o-1XlZWV zmYSNj$!OB7SE`ijj3(>46UR?%kjqpXY>CO6*R5MuVKQ5_C8fpbE{_lHx&J|7-kFP( zDai?H9xFi_tr1RCR6$l|1_Q{V2#etb^Y>l9{dVj(MI6;{{q~R8X}k{%NJ~vkesW^G_#EBEfAlR?^^22wd3M>sKreyPpN!h|@pMMVF zs9$*Ry*FevHFc_Qe)w5FaneMo4c2aFj)E3^Oz2q~Y#Cj*8?d_8jvt80l}y3r=Si<#uHaY&fOsFMnh*_mfB+~80BA&pU?d$@ zk1{ecn5XaU>0)G`Q~^Q|3j^mCmJs-Teynb03eBDNU_oJlSIbBJCToh~k*8m>==4hE z&OJMQ>sJ31*}P`0=IF5#iVM#_pD!6WkQ+Q|%)#Q)K@Vi7r)-3Of}IXNy!qyj+R@{( zuNpph$ZbzO@w{$^mRXqkq5g0bG7%n<{It(pHj#V7o z7kcp7mx4d9{voPV>Z3Q@cx&YP8?T3g+*Bi8n9WhOx@l+^8gb$%ctj zr$ctm07(HS6pD{>N%uE_ZF;>9@9zk7w6x(`PyroX^4444k9+dra~~FRpJvUl0>R3sh8AaN$r` z7(afzpisy<+%Dg@^^J`m4INr~^mJ0n-|9HVujTvte#fLjmyw(^+#B%DKYaAi)Wv1;TOkD8Yi1Kf3Cc=bP$(hwh{54h!s{id~hG^+Or~3>2t*m5ka)^pkY8cgv?yHzloS#eOZ920L~Ake zpe`6{W|1>BBB@0iViIsp*&(Z__7N77gd%34Op;iwV7{!Y-3fJ#T|xu?^aNxe(#+*PB0!792+0IgZh#)lNVf`MJl&F!#%_fe>;u7K2ZBP={~4lI~A z9RYM1?AW?pxc!d%VE>^V+&y4MO3G)t+y*)ed~SIdFRiY@bZh#T;g=NhmIUKJ>qry z;e{8T(`RL7Y0AqQ`4iPOqEexP1XBVdCv^w>Vr5kg96Gpz^YoU(=)t)#f95_z{>iaTw%grQ^EcV}%*&`iwGiZfWkoWH#IJUi$Wpx8MFQJtd{Cq@*OM z5Ec3eClu{Jc6j>kz57QSRl1}LmYkb-&f>FU4twwF?^k{EPGx27t`}c?F?jIcL3v$W z-LHbCfsmIM5H%VN=W@AZ=#vP1U@;=@MntRCM#Cf}h873}SZ6H#P9qxs8J`meFS+dO zY$dPM4H+?F#3ej08~fN3&stBMIAQzgr_Xgs7KGK#tkH&3mjmNtA>e@|+(fC! zCW=x~N`ZejYqem~DIwiv5)^U~j+dW=j`nW04L*!%{&4SokUyXp0{#H|e6?050gxia zw$eCAAm9U^-wly~OZ@PIFT{vk2e;pGyBLZ1n=_IV-@WFVYhFVD;x|hG+i$-;>*s*z zx8J4p1&}n~*Z16j0Rs>fuM*<(fz*a4BKQD(|E7V!hk#Wc*n5Ba=_hgO%$XsDS}tNB zrQW)24;BS+*%e|6jY)^WIaYY8%V5!23q+k>Bde>g;}r@e`zS94NI_hzg?R8rl>`+g z_fe)$5oS&y94Dn@WRlW0v)2ov_%{p>f2DRl{U4|0=My)3*{- zgTvVadAXUeYx_|+dZ-G94jX_K`w)8rB_%!#CS)=bt8QjyWpdlMZx>BwgE(;TknFSP zPMfv&NaYdymYwjgwNotDhfbU*-oF3f6+1R>y->Khut>vLh?6=P~2a;)krSQS80{vZ}=s=9fx zS{4m>S(_tCS}Cbgn2^ANX>zo7haIT%krWylnwp@qqYHYwdnvFN3kwP`f>#I{UBokM z*6hwt-~O|6bV_D|&Y(156(_oH*RD`iWhGx;Rza-aTuye5z+>c}k(1qZqWnY?{B!IS z`0()iThz8x*`>pW47u&I*WSo!X{wJ7oiGvx4J(m3?Cq}GZoQ!%!3W1B=bvLh@FCsj za_FZ_og(@oAYX9i60Fkb#GH%_C>$^V1B)=;D<|Q2MFrG0v;l`f>Ck}%aPE2YVbZVy zp?v?o=;h1q3V-wC_hGz_V`nbBkbmOwd$fax4pHN#sIZdC%c+%wx@^M%5wRUiV-#%O zxTmK#T2ob1yKDEJoxyN${cShhw(r>|pY46*nn(5;%~s=;S1uQ{T0?qCX-Nly56$p9 zGddq1eD-x~W}>O#&O1KiGc#<>kKX@~aq-1hr3RxhO~4=Icv(!rM+Lb*5K@B3E9H0! z$Br5yOqw(%@bDjR53gRgHuT1uuXEeCZPDOE_SggYaLqMWiPv6x4LtMgBSJ%cE%(Ns z-{ro0_ch?Gnc~FBQ@O!|hp@FK8ul}=jnr}Mm{=GL3apyvfYEAzssJt1p9xAAW`u2H@U1u47A)AGcqtRx=%8 z7gkU63yI^PjNf};Uv%8qu~wr=w=h3HmoGn7^C8~HNBTgB-zq0W-!{d!k&izo%$+;e zhrjMXAmZ?-QDcgncIWgX$Bs^KYHk~4v6@S#O`TRdZrq3?m6er8_wPD!;JFuHu6gg> z505W8_pISJ-gM=tM<2N}_l`TR)V%xlm*E#){WSFOgL|Vz!-pwmjh)6#n>vxtE6Ihd z+!V+!83_CKZi0%MBMhVtBhXpXTm$9re+fxHt%0J_LKri8926IpKz?o>G&QwAcW0MG z_=pLkj1mq}l741z0s#@cr1Qf@a}b0enPII$!K$DHTo8#Y$xBmPJX22MM8={oNV6BL zKB%ee5h@#7p~Fq1|0$p`4uixDGo++gAtgZ%R)hM7?-zLF(Whbc&ui)M3wPc2 z0QcBq_shb8AV)tzr$nbqaSa?;>OOk3 zRjp9y!%nX!l98Waxb?a#6|cPa_Cb}&BzyLu`{3~Y&81_cn2RgEN_R2uN~Ves(?r1Pbx!Ng3=rewYh5+;HZZiy##%-5otW=nDgQ?i38r zq{xH-dAbIGSK@kt#SZFlz^JIbtqrrT6dlUwX~eD+X-XvGu)2UU_EK!ilpO(Bw-@4tT_|5UE23 z*9w=z&AFT|F|#Nurq(FAV8BCN4|E>-yC3@PAF_S`qTha3cDjQg6dFMW_B(YtkYROB zpb8z?Dc}JJdw58EB%a>)bwm(A(c9T6&(F`)%jHUWLw#e|=Z#Q{IB(P$g#CMW5K*Hr zeCRMl(M8gG=818f7J5H|4}M}RCKhr0ST(0+C^V@l2)O8zj2$0G6NscqhlNNB+ z?i$D0`_w6TdJQMd+R-0njG{}3b3kyXwJMR*kxQ+R_}w0?PRgyk(1EHQThQnOPGGGc%SP zJJ#4fWJq=${0r@;?b|JNZJmp8NxJIT!GoixjGrtIMtsp-dw1Kc35f-`(?Q(742{vs z6%HJdZcB9(%f&znFU0tqoLr&4sS7GAYOx|5M6iKpdgN3AQmU{zfX79T0F6NEAD&L| zAOJzd2DMs)l>s%MkdVaK=6zV92x8D3#h|OHyBQi9spBCC(NGjr8VxypE+#XJwGHi| z>#x5)=<9B5+<)}wp-E?-or*4h>h9fpl4~og!^ig>RbnzHOd3Ct*Bi_cPNfOtWM((N z{Kgx0_;1?j3EhenZ)yjp=r1{E?x@=~eD_sOS!H=NFF!AuoteRlV$^xpA8u=IXl`)K zo;FGG@fTmG#6ls%_z9D^#^xsZ$OV_lv(gZF$jgV^{A{RgZh(C|4ncYKNeo1zFa)b1 zm!3BdCX62zYiVkTTzmDE;>RC-B9esL)JgMHk3VrgOqetQ^B|toEM!77iiwK~3%Ox2 z5)N_-g^DP;vHBSlD=Uw6Y}&N`;PIm;HY#}KFB!Q>wL5m~@Go7ul=j2_@LLbFz~$kG zx7f{QUDG}H+!KA~nP+}C-4A`Hl?#munQraSVdHFgoLq?+d79SdX5;pqJ52~snoI_r zCBbG+G#QiAMQuX5S#L9O5ndGx1(Z6SUVHJf3zQdLxHP(M<96|lH{T2WwCXF_z4zU# zc;&S}b2r_3BX`}^*YHoj@FaKJZEf(*d!NAvpMM1Fe>QW|=FUf;aRjSY(=|@?uTEzO ze0&cWjAj@%Xe8(`NNuLBAiNLiTD!opZXb*qk_m&0a>1IB0NuDgI9)y9?&X2nl5e>F zDtPdL2SJbb$W1p~&Z^>0y8{DNyq`EZR!4(kVNoGeRaWx{4<3?_95EtQuh%TfEXY>x zJAC|u185vcaKLYyn38o6^v$N@LX8O!Pl-t!h&WVTU42rcGq3AtYn)hCeqvUAZOzbx zL~F^|QKJVB8$5JZYvYuPy$22)`u5u&t5>f4Zr}U`vxePy%Z(#$zvJ5cYp=ds`~Lf1 zMgR1d4}yPw@ei^uzaFYQZ`pbL84KnixG)NGQZwLS)lk@f{3Is(-H@7*2i@(>jH6@! z(Pm8CD@CaDd)@MRub_!S%(-43# zVbxj>33@FXYm>VQ8lw0sLuXct+gDXsm>uDHb-PiesF^ZmY(ZmPW8&c>N1~5B{y2C2wO5XqJ!N)E zQC7i#&09Bp9Z+gd@AozAEpR8lO2f_|>KxrQ_8 zja*hvnxHGIgifa);}H$UCta+gAQ~UZrJg`elC!~obL<|b-(>?_VqzpJ3k7`Mwk~_?TW`Jfo8b@Z zx8H~Lk6`rM@5W9CANo2Uh+qiO2O#)?fQ8dR2QmU4PX9N77WDJshaVQ+eDh5#kn`uA zefAj-D3sc~mtK4&5DtXp!-fuqa6}M3{^&D-Mif5(@&hG0O4ai(yuu9}Ivh+^8(>m~ z6?B^GBW?-&D+Ybk^r|IxZ_IA}7$o}8VTXbN2!*2PXCf?UWb>6moK+Yg4lrP#h)$US z5cDKtJSGNmY4(nh3iZuUg7gB?g(m=lBqo!REr8Ar|1DL%w1{}yuzI=UmwmAKoO5CF zv~l3*aY=jzxNS7Ur`M_>ssyO1tr0(dZA!By9HM$f-snQ!KV zA&sv*`_T6%%Z{(T_nGGs7oEBI{0ATWlFogIlASSUarv@%n;&bUDvL- z>MwV^@!A`&-hA_aS>56EL~hx0AL-^4+;tBIq<0j7G2Mrtu)csIVT?uP9ZvuBm z4-6TY1Ix}^1k)yu5Inuj(dVCiRe0&Ow_>442qsRNhe_!@{EX=nRdNNZmvMcaI({P5 z3P&*yQn1$0j*j+dU0p-lp+kp`VU=sgv}rT9tJUVpnKQ>YPHS`ho6(#94Td0!e94j| z9@i(jfoc7LYp%IQ=5RQuRalvnlw`=u%uCA8%FoWuD#*?mFaWWsv?94uor&v4qFSvq z>h)}C3+>srFZSXKufexpujFGwK$(=7#$S8&3ht(xuj3Pvllb0VyZGJ*pTegr*Kppb zk()euI*c4KoJll>BSA?4hh!S5Vh&-fM%O?`Ya0Zd9&mNHgE!a!SxH(LiPu3|W)k?q zJao7)U{A_|6a*oDUh^Y7_tcZ{)YC7(J%6~JbzJq@dm$7+9IDlh9aQ4=ITWaMAg5VA8}93?$jLXAAuCfyaa`8@DoM z zsZ^GLA)f}6i98DBEmc3dY0e)`d0N=}qF zw~U)NuiVxp3kNMhMKBmd{K&+s)!xLGPP_HpcR%7Tz3{w|_uqf_WkH{B@PtV-gY^w9 zv6o+ZO`n&Up*~sD1jlQtL5|K)uhHX`A!muN+vgQeoIC)P$G3wz66VgFISa$5Q4kQL z(BA20+o9C(OgRgK8Z2(85hxHK;3Z5)z~3VX>Z^Dny(JZ?5L0ZR?irN;a;mVa@fyM8 z7B4cj+5kz3DbUmDfWrq4z>>vtp&%~}oV^Hm!~$%zFRqNi6Ht(lkl=N?TzfwL=%Wo7 z%N05d_%zNNnL3yK@53j%)Si^$jh z8a(a$_nU7XP#7$A6Q+zGe`8vD+5)@>G3sE-U$l6~YO9Wl;ecOWGITt*de>pGqc;jg z+38SKegf)RTG=Q=G!}$dgl+{nK)l!_XZNy11WQIzs|t)Q<zc=Sm?JcoS` zPkawlp^=fA7%z=|#Y6~tToCYi#3P6HbE|*eB#fCdJGOYy8G_gA*vZM|_hzM~9e{r8 zw|~I;0f>J4-Pq}47l{Nsp#1`W?S3F$2m1MI(BU*=@|%6Z$v5A8lNt<$?!4f_rB|+A z{gZ0*#;viG#8laU0mZRHhmQzfe)Y9DckUe7D}R1oxqjVx+1fRmVEUYSOnCt*Ef*Kco;#i=OTq17@`hz5q$%i9;iBX)|8HsZM!2+xrpSET3E(bQ+%QTrz zja^9Xc^o6PjNh`<5y@sV$@iGeCP+%Kfm)+wbC&eFjMNn9?d*nzwsyGr&bwKK-|4Wk zt_+Eg6P=w#4q6NzfBfd#=sSOUHN5!TCE>F#yoi@;boN6BtH1RIl^5A?cUb)HFgff)6pd5tKR$SgffSc$!grNh6>Z3B5Cd_e_mtT6( zBY1861pfqk<(1VcTPAneIrFFA-&$3fwRP(Tq0{b-;w2(KZ}FngH!HvHy#KK$I#QF9 zLuC~w6U}Cu`P_N)g>Tob)u-i;(kxtjp{#J&Xg<+qg2Q`u!odUk!DcbS;(62IoJDg$ zhUnYtul`whwb=4Hz)EAR{YdV2Ukm zsMa9Q!+0?PU#6wk9^88<{L=Gpax1_8gbPRgV6oU_7hSwecITZpb2-_$;P?8t_dfZC z|L}uV;0XYX8b1z(j2w;?$V5^IU|ckE!hEn2?!|<+x4i?xem^+;9bk7fgEr=e9Ge01 zii<&KvVzMWgOua~NXt%!PrmvLo`2?fxbCLA;4iN{%fP#)mPS(V6gk%MrKRXImEn z*S)w_`!MMDg2&kbSXu?ERs%^kIV2_-0M|^GSnxzdieLm049C?5Sf!E^1zzGv<}fLd zNn;s2N$Ew22QgMHMKt381kg;&p)Ff?fkU^ko|S4jqCJI*kge z=vb{+ium0+wmwq6h2RUcqn|l;{0Ma7wUv#&=iIYr!9xN)8M_+!tBziivW z?*G}d=EEI#-X_kNF`WP_N!kT3DB@4*aoWK{>P#jRXEYcErCjbkUUstg`0-=am_YB6 z%ay8?cVJx9$JR-m& z7)5n;Dua{S7j-;@{Ja`{YjScT8>J!1$Ov&rh%!OWI3b94iAbSUpivtkEhQ6^+!h2O zupqK%9t;_j2R%JqY)YDpNF zo{^l|*4`jLe(Z=)Qd}xueD-3w$&%32R9~~ZzNz^rUf?YTt--D~8oLnHaEPKN#BuVl zQW=T{g9Zd2LNT}79Xp--;q=1Qe-t~O#?8e*iLbu;cEYTgGwx_^shjV1xue-xY4zQ9 z2Tdl(>nkdQJ9h7c1+%8*EWPxi%&iA1WZiC2$V#(9#qs0Nfbomp?}AXoD{*hcsQWd_ zIvq%rhIO7$7c5V*w^XyD4ktP53W{$6liQ?NkNCf+(^H`!=~a&Ly^!jKipiexY~+I8 zPv?WkiV++YJP0-h(0}U?P;9Gjf`j|^!IRIv1jELRV#>R;cElJDg~&QZB>(~-;C4aK z=M;DE+{SI#utS)6#<}77^QPnHxwq&|#(QiALmBj2zx@N&4?y(WZ{Om;19^t{*ag=I zK)`7vlVxQ~MACi!dmlmXBj?hW1pWBqmV~yBhNS}s7OogRbl9M$pME+CQnkuu@8bhv6#|hF10eu`L7s-%^@$IH zB=4}2j#UEQjjxwvIRz#i#w5f@?n;ulVAGT2UU(rsdnfzrt@-N?2#&Ceh&0jxjATrM z$%YLR6RQMn&@3N1pMuN`*tC5&m{YUhj$3a4m&=V#B!K956nyCX^*W9mI@Eh$-`-leQc;~-ROBljI3RE8lqr)cDyp-by-q$c zF%gChA0{Lxm|^=bd*J8QKS40y!GKr;7Ly6gNjBg}<${jATVk80)^Q_N^sH7Z{?wpz zr&(18oH$wqwTXFt2F0B4++O!S^r|I}7AG!GKS+=4Tg;O3bp3xO~O62@}SS&%NrJ6`4Gz&_)Gb;S(ae7oU07 zy@_VsA^2|^RoG2g9cN#4!C4RZJMBYutXm^AcD98(-5%LF3m3#18*A;Ct+=5(Du|)V z@^WKtL4ofHYL||?xoi&#*Uc8%MC`@xp_VGw$;M4@uT3vMf0)3sS-Z? z=wspGhaZ6+M-L1cHd}n*xu-Bu7=dvwhCDHmi-iS_x_tyD$ra@38pLZ+Y;I}p?e6M6 zhR1h1C*yY6?OkR2_GvmEet7yn<+}ObZYx%-P?VIEBxGb1=4K?Pm!@Z=3{On9jZkY9 z1^8#0DdWnHoCrSi;oou&Q6dk4B&PWSzQYPrw;TPL5Ls_5%CAG z3h6%2NV7e*GT~!E-yF{!1jdY z5E3;)Oi<%GhyH_PR@G`y;`%4oD!`=If?lU$?cemVM!gymEm{O0w5-d?gwo>qHpiA+a1*d3i9{sw7Na*UA-qtODDGACC%9KZhPc5 zQ>G!)pjN9LH{5W;znVPxf5fOW^O8#z5Bm6n4>Kyu8ym(?Ik(>9adXjVh}UR>QA(cE z_35gr&JOOw58u^XyyCK%cU^zY4dHNT*coS?>)m(YSn%m5p4AO0EYTf3Q7x}+Z53oH z8G;HlqD38;Y6zq6@5Fn)w)~h_UbY`BIt6#-;(0K9=pbn9>1M>C3Tn}pYZy5tFITcL zQ6kzVQtMzO#1nv@n2-QU1S>)zB2>hRyqvyAD-bxz@t6!N2`Do|BKqCN+EzGlU=J)^ zx)4SVE@Em`5lP}z;xC}U2Jbr>bH)3wZPS*GhY#=F-^>vycv6atGqn#3+r^csKeoi z{1NXQs+a2jZG*Kn9$<6RjN4zgJ}(rNfN6>0?j&U zC8>8T2#{t8gn|*l;c$XK5@6D*T`ldfd)rR9>;8ve#_Z`pH8D8@An@MAHH2zr1VCV+ z0gGu~aP+i`+qZ7!4jwEQW-M47oH2a@I6cnwN>2TU%*>=3=(m3R2dp1}=(pdr{T7f(*&RV{F*?^-5kEl0n-Yg_q zEs&X-E!0$13va&tCO=}-XyJ!d-;jQ^?5QW8gVAHhb2R(d)Y^s%bqJ&p38~^l4@TaE zkh2CIw;Ue?Bt)Y$gB#=h=+ONkYCEJ(2$4AhLZss~L((V)C&}wc8r;0Z4lDbz&4H%+`rvIhUFSZ2c*M<4>zrRoB&2 zA4^C{DJv-%6b^*NoSdBO1&N848SSmj`n1#xVfgUjkbo82{d*6?_uqd5E=;BpY!(IPBeU;OKGKxu+J?x)dlN&HAhy44m|= z>Y&yk@L)9JF_c3?LnG|jvmM5cDB%Bi%jMA3+FbL-tFOFMUU}jNi&5`QPR`-58l=5o z>BTA6Tz_TO{=-KHJ^sYw=iV1|JIC$Vx=pOBtBO`uR`83?SP-%qbk6fGyWHK-+8NrsX%jKVTOWJyK6vo4 zrv!sJNps1qk1B?b8mn}6w#yFg-zVm$TjBEa=Yz!{hfhBKQh4gAmxV4z2R~}~czEow zN5on4W@0?aaacv?5Pai_g`Y^t{60VaJH&bX{#bWcS1Sf|`)pS0mS{A(8*!@2QKLrn zlTrO2tZ$YWtK|H&Y15dP`)TVFNm%MaAxJA7P*Pelc+}W}f~;{Sle`%JrUx1kIC%_S z`SUx%%CEoX>^-8NHg4VmH{N;= z6c&`irj0*9T6!je5N+V|`9zw;P-EqjI*^VXJ5Dlq;>eLBIF&*k^LQP#M-Es0guA#> zt5qJxYvcDcC)`I+DPMJPl`L2nC}Ilo`SO`A42jLC5X6B%uHYj;*K5*~~|^Uz2* zR3M0g7X$u7n9v{AsdTNKu1?tX%dW(lnwFwbV}?(8@|g#w6cr83I=H`#U$W%VXnkWf zTzd6`{EfHWD91IKFFR4mNR1I=hL-MLu)Cd@sE3(6AIbOy{G^)Ug%~Cg(m0)rw`pXc zQqz1myPPnm!}pU~MmP*%QuFbP2tr^232gpC;1nwSNm8wPe7_DsStCnQR0<^m=sM6C z)Qs3yL+Tky5lkixSS=dn+nf$RoWO0|v3(l^JZ=~_dN`bqNo7${E`Dz`_RY$#V~;)Z zq_HslLOkgJdI~EQH9cdYf zjR>Y3)9dwn{eJIJOdcB$cqWE@YTpw{LX}2ieto{+f5}J}0`hrv zxK6Zty=J_3T1Y}X8dv+mtP*06z4D=Y#rfyWy79)#Z^UCb>fEK5J2!9I9(w87XH2C< z`Nl)XPV&v24y;P(@t#z(QBgY9VSIS-!f0r2gkuNyae}i27S0<7vt~_3Kq(BZo!tm7 z;I*LE;gOXA>vN-SPmDuIo|igSlxiBsNI|e(0lq+xjh<5Mqt<8y>Yfll#YTo`d?&${ z2<=$?KXmvIEIMO8j2~4DUZ)d+SdB*vTk6~aikZSZ6>&6LUGV7PBhBmAZ|HG4J&>1I zU@)7^X01V`FDfonqL1a#|HQD!!)2tWDXmFKa_YlyI$ZYNUe|uy$Dd6m!+v>G-rkDX5BGoAF$;0NhrTCHo~%zPD#|&t@_6Ohx7>AGT1n~Ps_C=m?KNAC zl^(aV`Q4Ac=rkI5e)!fJWXVl1eI6MVY#dTB&B8E!6oyin=`On2;t(I8GYJ zVjqG3#|XI*L<)r?g4^X`T`M}Rmht-R+P(uWS$-wVJ@X6}Px7KP4#M#w8|!5%82Eg^ z>xH)FT4B%bZE*N_wJ>w;xsh?>h6|xs@Tc^|#QX4^H9)`h+dp9a07Sq2rtLJTO$I*D z?`3vUAj~zZKcYA%{?CllQ za;5Oi%9U{F$RYlxpMMf2Or9Wr;>l?85 zm?9v8W5_cxmdkmXcXVQ~7V!H{bty2Jqf^@!>)T+7#hgv@O1mU=Nk|f&@fR@yCm|M# zKgVGLuY_bQ3zFu(a4SKPl zus}?-7~tTp!|=tIp8<`wq@*QdrB2I^dlZjzBphJQlPWZNnqNi`Mz1qKFy@29hst2{ zmP6nQsbJ91F_2wY1PZL^(k!aQY+^EFWF%3zf6qba>}Y~ZE?x*1oi&;7>Fx}^`P!eq zKeYG2YkEy$l`Y#wI^?lAbLP-7P~LdWHOY(4IcMg1=bg8>wyJ7iaq&R)-h)S5S6qMf z(^yS>`@bVANyqr{S3jJ6`DN$+F-fhOxNXgPC@Vi1si-bjOrAV8G;-L`j=S%=$MwU; zZNlq+c|&*Yb=O!|egD1Uti_8tt==TNjWJ7@7Z^1JW5OB_9F1e*%0s;Yr^KllQE z*?AH%PYnzlJP=9;6@yui38zTo8Gdkj9pG|0Kt<$c)TZ0pf$Kv%3ycZl95QSOjGr_X zcI`h3H{Ngullfe|b}ft=F$B80>|l5Gh*Txh5D0;5(b1#FAR)mjj2=B46CMrc@%SBw z4;|Ysh>_2-veGx?=jZ>Pq}m7&;ub!%P-bhe$;!*im34J>3IZSSnvbTXrP0sWtFOMQ zKrkj96R-)9P5o48&`5*9 zY`O1_Mxr)x*RZm>|nU`;>D1WmO`U!!gt@Uj6VF( z<3dGsB}3B}%sWeV&s}%RCQccTml8VS2yUSm;YBL0(gmY=9^8ZvrF8v-)ShiEkW zV%^=PJQhUc@&L`r7;fdEtLB1z5ejux?|zMN~SIVO%Q&Ed{HYc}Au3C$hdh$ARKqtG(N zD3%U$lHvl%aS{gvNpxD15)c4WLxhSO_z-v^fF^|$wLtWUG>%~~=qPrB_KsdSbnpnw zn>re1Pa8)HI|z;i*+{7vm!TwH2&&0ww0fblv%|M%_ilepbxkBEH=j>WPtmJ2YGp=7 znvA%vIGIf3aqZ$|MDUGud)&5I1cENN>xA8IUoDey+wr*7Ac#eRP|>cgE+G#5_Qi(3 zjvN1fz{7_herTILd-jyn)YS9so%V50Jo&V|-R|(7f9X}7YMrstVpdgjx3)F!-nXyW zpq431iw7<^W8RF*8=G5`pZxP@P&{OeFlkgNf-n1^tJ{G-)P-?ckP*ewI?L2gF=mky z(7;qe;sq#(@w6FnA-?0rxbt4PtW`#9dR4#uzqNhT3A^@gD)iH8f~`k99#1A|2O55}3Rv)tV##g64in zdMP0x0c;62Nedn~R)GPhR%?Zlq5?R$b1NJ*xM6MvW zK-8(l;2Uqe;NH1&tK08&_Z&NT*l#qb<$3ufLRMzJUE~yQYeKR=IU}({X*AU-R9d@2 zrIM*NdRs|OUSe9BB`FpWlhJ2d5iqd&d@d^<--M*(B%{@qq{8FDVNd{Ww*xD6s*tnC zQB_`Ew(IDzBiloM|B;ac$G0cuC%QEOjrMS3+4$t-#EXWH9DbJFxc*V&sDC?40CKFcRIqYww;r?z;IY zJa-%5zi5=K?%a8BnkB(o3&%gL=ert29hAP)?uV4P~6EAXOCQjj8b_Xwug*msQ9e(`rJ8tc|wIEm6#AO#P zhud$ynky{LVt0>7`sAdci69!!$vCgi&tXXkfj3VW_px57RBXfZy1K2c?EnUHZT}r$ zrQevbBzNl6sl35p;4q=3=VE=wmCpb4>C;Wc#l^Xc7hgDH%B1n*)f&YZJXg8+XLVCm zd+fE>{vv$#^{0GGTfL0qlrU-fe7NoAtN4ZUXMqB*?e5MV_;%$wShsEmbYmb{JYqNu z9#Vo;FkDZ9UKafO{nT$Of)T4~;YcsI?LFYI+rf$V>i}Fsrq7%T)%DGA<4t#C72E;u zy!$C!e(7S?-rv&RiB-LTs3wL(+;9XF#RO|2j2<n!eDLmBXPmjX_|Tz~$~p5Eg54dE z|Kai16c;Qxm#e9+;u~8UL7~xu%Pl}hdmHO&Ag~~cRb$poM~PdE6qoooN07*~gaPki zff3rV(R!Nb6XY@_$grZVRjAn59s#rpV!$WDI;=RWbt+640rYA)q!^W8MKD{9ul0F6 zP+i#!yLazrv-`tG6vOh%&x5p-1mYDE-g^DD=$~JIQ)q5!rGW}=!Muh11An}aA3J_5 z&98HqRLUsfL39Lykr?j}2Cx#|D!QF^m&@&JEXXUMR*7Arz#o#U#oD7sk2-#jB$EFV zhJHmoYs$F7bsIKWY8qOamMvS>Mk53V4<2NSUzkwy0|pF`H8eE9i|>A|xogGpNz0a< zdlkNQ%pG?=6#UEUuM3a<@m}lr(WA7-%j@Lz?G6Zm3QPtgf*5L`LI9H#j|9NsbwNjS zvsiiZDE>XnU3T6)$j{4%rVb|?7gg)EQYQqBgOXAM69z?QKl>=Nj~Cw z#8d3#3kITXtu0}P(-TchOpt3dO1VyN;He9O_^m|D-VvM==sgI^iA_yybZ-Xm@~gz- zwLTh*ZZX+X%WXD|BPS>4bRy_q=RkiGJHGVNOXh*a1E)-wFyS1XQ8yR;U&^N+d;+z# zjgjmDCBAbnx;VK1*s(}aVTM!mhiccZ+tOjt8!hKAJ#(a7$>)Fm#gE*HiY8(1!n5#r zMPUEIL+D3+c-%Y~qeK~rEC>h;@B$-@$>{R7qy zK=j*h)_y&H@%LZ#(5WANT@Zcj#bssPI#KM(;^guXzzK83NMxd1txw*veK)U_%lWY* zhsqJ<7s_jEL~3JiYibideD7VsW;Tku_wOcRCFT5uX9FgAFmCi%sHm)F3pOcF#FzO# zp2)r}jw2u>G{surcmfTmJ&{_JIXMRbZv-%*V~pW|3q8$ZG8uL_J>FvY^C=B7@X2nUyk+IhoJT#yIRkT-hAAMOYzr>1y^4JdUTId~}S@VdpEyaI0U$YI*7 z#3X|ljYJSb_k_{q2f|SV;5mJw-efcybQ*-()hG_GM;fwy#*RB0%Tuv(G+DZf@?LGjL$>r9*}do`QjqG8zep zndzA#9mAi0{uzGw?prWo0G*YSfk8_cfdmEakDl#6QD;z~L#@OXs~Mb5JFHo|1-9?6 zg1r3EFm3JvNWlOogy%qGkb}vj$BGSr-4TGc_I7A#p;7fHESNt57R??9%?&kh`we#q z>o;tTE;wtc{k+STHl<}IH)BN+6K|QLqO!94>#sg*b9^m zSFc?k$jr)FxnTaR$8q1P;UB#A`vRd)KmBILWkJ{%iJk$2$ci#<>%VoC7lg7c0oqJ`s-}5*>aL`cU@WBIoQ%#jv zRaF7C_4RyKP7Yjo<#llFic2_)Su1@hoA-<|wrsliyuJVe4=%2=t22hzQ>{uV-`H#K zU4vDuy#)mYJ^iGT|6Du0E28hYUngP+_^5}D8Iyg^S!azLHFESU4X>PpcmpCwptASa zqwl=^ZuH&vKM|{{%V?G23kD41Z@g|fTy*hLtUe||!0m(YR<4FGSFHm_C8T%K-up{#Ztm|+ zGO@4Y3zI&1a&oeA`}XZB1cyleIdJBgX9fv~pz1Y(6uGf*Y&Zg>gF}&s=ns0Uu-e%` zuT!gKk?QIi-TpmCOQubqGU@r}o|vrQHK|K4SrPg6+mD6Gi*A)a`~2f7m!p?2t0)sS z+9c*HnwlC})sm{ck!YByPq1-7A}MBsc9_6LBSc$A|4~CVCX2YyL{dj17I;3iDg!1O zI?!vCj28fbVK#SeGN{31QG?ltz%3>{0hbSsA3YAcw(o*4`mNCjj4fNf7!ysi*wx(` zdFrXBg?Hb6k4WfP8Z~9Y9R8sP?^8~nJ%xY=p1|=)D8k8=3ThCCUWX4V>*^yKm8Rb7 z>^Vbw4t!dM>##sw zpwvNyNwzGhIZ3$Yt+(KYJLXMTe&OO<@ins^f8x2|!;d`^y#1D&^k>eVuCJ(Ul8cDXI7lp2O3{9T)b#DH*nYp=t96T5DbF`eJp0O z0IXaiO5>x9>&nf3n$1=O1dQxjrO}EoUXKd3R#0nfOi_cnM0E&!B_||84_;H-cI}7Z z2yR|+;XKAA<0mkL2#%@RL&-FanBv9h_7aylqDr4b!api4<54+bus-tKlbjs?-~>e z&FBf^$1KiBPoJmKsPhomdnSW-aaMLFZ$}63U3OzQ zKR;anrMNO9ukb-5$NkNSX5R%r27`KnN+mDA6)-X256FU{h#*(WV_BJ*GU5biXlRbES-*zs?Y7H4 z`tTFc-rE`5w|5tpS5U~EdFEoQ7Q4AyZ@L!t95@1QbigW|8kB5Z=h>)3pH!yg1o(KD zLj2#N$QX=?DO!b&pCYLupX>{_6l6g=XCaZybiaRmYSh2E-p$fC@LI;Uu_ey zrb6w%u?WTiawYpt^!oxSfcX4D!P#pUIyySTHFZ_3wUrfnJG(pASu6>AFnDjmK%E7h zjNia&&DAM17mOJ@_R{>koWkDTZuFs|n1SE}q4}SF^Z~40yAD#56Cpj>Le(_Tn@qUB z$*kie8uH_Dh=C5Pd>p7@=f3^0X8m^HRf({0@kKCl%qZ~UzDB7f8P5lGtl-XJMK}N* zT|FQdeK2c$DV#NLBK|D^cieG@_`wGsixyK_^zp}@73QBYn^WtQyv3>&G5!e$vAo&S zYY!@9a<|E#k9fQujibkIb-7%Mvhp(Z&#PBQE?#!&`$8o2&^hOv(+mFvd*IoRCtrX5 z{6|tUY}2-E-p<*38l#5~9^&mTPxSh$E^j|{;9%tyH{I4+P*|wjwR`sRxF1C1nwdT2>$f# zIs_$mfCm#0vpE41%OpS$kyYbjLYUR!RKzcXNk;_lwdTe~@cO%9w(tR7SU)R6$6X7ecLup6e8l(sbdHL;k;fycj)j*jC*}Y zaSi^4k3?1#4=t|wy>`*y`H>?4VOhWaN4;8Yj$$>MWJd#KWo4nb)U(#r(=#9viwtvn zd>P?zFyQt08Uo>Pdt#y`$_u=D*S_6WolG|N&3E3Kmsd1sz~`TR$z8nc>Tqh|IOXFn zy`)M{vU2q`wIUXlK_wTV$6*J%(<6BVtVmM=5y8ZcRFy~_NgC~A=r`lBp%!hGO2cOH zl>|Uw0%Fi>@FyiT=%4Umv8W)yEO9&p0#P`6xC}O~*#N$P8^(~Ap=FFG}x7>WAe9nT|N?y*(#aM(7hlyrF$q7*ooQ@!kZg>Kb zK$R^yVN**}{nnBJB}a>jGQ0XghyR@2dFP$F=bn2md*#Xxlr@K|S__5`x5rgk_&yZ} z>Yze^GRAR_a36eHGU>95mfnGHo$=b6??rCD^}fK0WfvGOJm*|>V_S!;vbhcXQH5yM zn>aHTBcgboeCQjUH0R&hAyS7zYhwjCT2F9urjCFGbLJobAVROp1#&G`;dvz~@FHd{ zvg=I5Yt!v=GeAslGJrWDktwi*(YHidoTNgqODCvwT9PFP1zwX$nOHy&6|iCJ9>`5e zf$J|`fZ&%J+z3KMv4}vQPQ4H`&Y_h{mH6IXQgK#_2?^;4k}4UQw8&m1MMo44(bv&< zLBL03lPQfI}HLx(EA{^6$$`}XZSdjGxmpBy`OLg(gfzvKsF(QD3} zJ?U%=b!08w4$)$;aBF^A2UT?~LUv9%%$zm`3mksff3OOIG2(IQL1<85N*0_DkL!pq z5`V)A4kM7&u$U2VC#8b%yl`CPuh|;CN07*7MHUUlOJBUCRuYZNyyH>;A-1ZLS`p7W zn=~q=pr&;Ik54EP#rVt*)K%Kk<%H3bCP7g_4m22}~r z>vHkdJ3_rJRx6mW+9^|@gU0VB(RT&@o|&2o#ravVXZLn!s;`5~mR}Az7z7ai1(9Tu zx&U62VQu(C66$iTs~(Ak^4J})t_3pPt4gn}V>>BW~|-P+YKYQ!MO%1p(5 zkAO~R0E^j%heJW=0|F1g#s(Ujy5Q%vTcN796DCZa2MZRR2WA8*dK}&8kfTfjm6)Kr z5Qyoud)Q|UE=_?Y3noD#R{#F|>TB@GV^0bgL<=|HbT{00KY=wmI?P1vEtmYM1UQj- zygsp_vf969?b_DkM~^mi_jGlR9y7*VQd(rZ{L;(6dhOL$-@WAa-AM{mq?bo6WO%)8>>RLxvOugF*fM z4?HB&_|F6kCd9_Z|7gw%{w7*JdSYY-58=}#|0dPX{|`f06LQdq!waBXYC z1jlGJi!|c4W7{_9?(7n$PM-u>Ss9`q*Wxz(yTj?|wAySNOgh77$I1?Gn>%-|4}OP6 zM3v7z`>bxnh!K|k`}eC6*o>BzX8R)%WvI8eml3jJVq*>aeA$>B4Dk8AiGH8AHx!L@ zlD?fzr;C(Vl*<|$8;c)%^nqDtpSNs$ZB4Uo+RU?p9c~Z*#4B&9XHJ|TJ96+4=fkzo zpw~f%-GM+)2ZRM_=9^~c>0FXDGy&r>1Rj_g68amhQj1k*EvN~6K!8c9!UR&y<8wS% z5l~Ms8o`Kv3x>V0|Iks`xN$dlTppM@elRS*dhY8Pca0);G>T| zAF*uN1=I;~=8xa48e|Ayi(iI;ZT&!{6wsg=v0%OOG!orgrwr$^oV2q57 zvxUNTV1p1mJA5qaqmBnU{yL1)a5s4-pD9XwV2q0aq^?&M6cW}z{A08MqSQsfzz6&p zMt>i~2TeTDxV0~(`c0p^ct0*P|DeHM6eJ%9uF(nG&XfW>Cj;?e%wgLqb2a- zDFA{#AMr#8y1h_Qwkx`A+g72fsw=!?>GD`%NmeMyru)n;x*yHA{*~J&-oeR!g?{U| z|G@eIh<^Jm+9~db|I~}6kqcRSd%LBoxxP@R)s-ltoWW=|24phMfYtLsiMGViO0}BE z+tj3iPQ*utj~?fb9z70+4jkiJnwsIoKRp9Oh75vdo_UgcQX(XD*qlH-%PgN*SX~>gf5ZRapFT^BJfGPwui$^Wae$ zGNxexB+Y03)Mxf}S1^)F?ynF9$s*D1%Ve3*yGzW)qClMjxSwD^5JHQ9i3+QiJO-aM zwLu&SX-P>?l$Q;S4dqaF>;%l6I}Zkx79j``p99CAK3{;(&(0BgdR=XA{pIZ)bv5Oi zv$Hezr&-dPb*VZZ2Fk?3Z8BR^&br}-t8dh4WFtsMREBPq+A+}{(UUITBGYT~tjV&AqFv$Q7Fnufiq@JgrpQ5eD=jkc<{c5pr^A(#?&Pn-CN#bh{R%lfUn;O&SVJ$w+h?K{j}dEJd6gEk?s z?CPsRiK&+0>#x4ZjT<-Kf)z;vUPBxvG$PG)|J9^xga^QKRRtRx!(0v zkDWTU9)71r@`>r`>3R(6O=Z<(I(uiQDlI2Fs#ofr4qONk~JVV zU0cpjB-Uj#T0Cv7tz18+rlP8@ zS?uU_g5Icy9!wI6QD5r17Fp6I`45T2ln9V@L@KF4079cdK$p&+0+d8zr{TbYpJCOi zfWvI1>UaZe-n<=}yIW!Wgkf;yB}*V9EkO*0L$TN2elzm)<4?vqJ3859#iE7hh7(Q-VAZ+*MW8M>@LgbqPrcJ3?XKrbtZOJ8RPDrv7e+|Gpuxpnd4x z+p|9S>~jSIhHWa9%7H)x#1(a71W4rNAKIS}@eLWU2bBua_Num-o0Mu&Jz)@gho5)Mm%I)gcQ zX=($2bvAl2u|`0hI!%S7q-3$z-XpAEw?S0ORs5{kGoYlj5DN?uuBW$C4Ep^76==+6 zi&C#Q%30SbZgT*OAT>3WG;z8yCnx9SAAVZ9Y5DTye={<+Y15|c(xTkw6O&WUBa+t+ zyGPi&|0pK*PRK^kKRqoA_8vG4b_5K)?zr*=9|JYUX{`H*k-;h$hZp^uQBd+2#)({f zyydTPFpC8H;^O#EiY-Y&B!aIGM?~;rTp48zP!cgEP0MJN#6u=W5Q4ZQ@Wb(YPRAZtQvZJkBe<21JR&a|KVFeGX9Ry}{UHIX zw#)%z&{vq6oO+f~Z#-M4*B8naGL75oX26gngLiR~GaHw+VP4npa`5*Rsb82*iC&fAYp z$?bIWMxBW&BaH|49^SQX{igi^udfb$RS&O|JEiN>(%Gjw{LNB9sQen@YeZnu>*9)z!ZNd8b7GFwoY8wVe2V8FF?);p*Lo%6A zjX_~Vx_r66$1wVTv`@eLZrHgC7v3*~BWG{^d9^I+x5o}0-p_B@eFDzAc!fA&(&$)Q zODE{93EZS{139d4kjyKuP$+0rMP#hll5VSXjk0Qqj6Eff2W+8;AR^enwYIkMJ>5Mt z!V@$ZEyvO`Q+{x{9N%KVRnwm^{64IYFXYV?D-;bXm06?L4;Vgj?Br3SN6xa?j6)H; zv?|p)9uYKJi-el;8rZb;i1^L=tz20}J>Ewe$j-39v?-Hd%G8lyFsTu+b3)b0YN)Si zMPR`z$uK4&uxZeON~vS(Q*%QDs4(FeJAMoV@jm&}%dd(X)~|xAuDc3e|MQz9Kg=~W zw?JxIDi}3tIC7#6e%Z2996G!Rl2c8h-|L3-q;xsnV**w)8;n}(>Z6BFey-H<`+sM0 zoquCnwrr7icX#XD5x2RqqA^9Llv%VomEdxF?e9N=U#`q?te;o=4DrFue$USS#NKTh~O}* zAon<2;38Ig8oxjwf#%iiZqO^)*o6ttuTiUDfJJ3h9c=u0 zEp)YY!NjqnVfpg&AUi8XL|-I+_}+(+C!TsL+Spjf>dP~yp20oz$NS}zW{g#EnE8pM zB~3K>oQ&&rcmRRv(226j+5rW{>(S?~+rIsmL(7&e?&&9~{pZ?8>ps#gn{$57`|rOm zHZ^rLASmIdE*z33?Hf&{ag6EHr?d6@rI%jMx%uW5SDx2_UC(kIOlKJ*R|iTwbt|8_nP{)rNDko%m%s^V}Z|Y z!na!05>Z19CcAS4XT>b2dL$I+T6v?kQ7p@$iXM?3b3ZF4ZPL|#mn2sH&s-UHyE$fO z3z_caQwURrm^&@V6ue_>PTWX-V^w1(Z)RL;nly#8Bx&@55ng((R{Ug`7Q=*~Frs_f zZe1+S!kSi+H6tP5?g0k7g(U(A-P3u0tytZsEi#QYVb%}b*M-c zDy;r!sV7GV!lM`{Tpk5a&%UsV4X#xE9Hr7; zT9Dx12_Gndhy{WFSqt!>#Rda0v z$j*sKlJ>`%lMxd$r9C-W6Fk=kjkBjadoyGOzQ>6T#_?@HJ#KxH^R>4FS{+vV#ep3o zV}dy7cHf%fV~r$ISB7ceC@7BLg|9K;gXx>mQ|>bkU16!EG5QOK60tetIiuw%J%;!v zU1Bq%^h*Yd;E6^fhnWoS$)*j@(GzfAeyq$r&RQY4z#yMxMh;OXt3@MRq;c~pkcVPa z8^iu2X!b?=L$_l!-st-+)G`MJ7KPa|Xt4KPu0gOoM4<#uB6@L5lnWnGQmrkI4j>{x zGfRRfd(O!8!)Sf2%lAz*gAO0Qa{gzN8+bh|q+gXvCl|*4#~4vWq=$J|rNMB2oi+G` zgVu)S+SV3j;8;FFSez5sel;}W$ejZNM%wJ+Uu-lYfy?G>&bLDxnd~;4%GvxFZ=`JM zzmiik;T0I73j49ZZi$uF0ex%rTMDbFxwisYH)lcWCXC#iX-HyWT-$x8phs|uf zMHY)|YYeG6xxi3$)ryp>i;H+_nu?{03d=X{fVVL+J3G4sY&<-_o&Rq0b-M|DCPMC~ zzYHEbef@AR*PA2=Tpy$Z4Eut&-FK+lp56}k46O18>1bBlH@dNB7-(28+TYS==mv+M zB=sSwC+aXLlPIg$c=2L@IqQe}f`VNgf45Y#Z_vOH?%c6Sd6V_|`8zF5%{jA;^lcK3 zoxVnwzy!KI@EMZ#MCZp|uoP@O5x3>eP)*I`u3`so4xTwM4QWYOEnzLe2WK^=z{E2LUcbn z8clR{3^qwoVF+V&tF1X~wb02q0Z9L~Kb}+kBUR6$fyOagUsGI1)ScId58`bsVN6(TK2S)kdtWI20B?#kG*N9FeX9;Lc z2Y~_F!37;aUfgz{Zf|J;$R}y(mCb$JE>-ZsKEBS@EC8Hz_gyxwAOo&ov#|H z7^~>>P{`6=vgCe|1y0iluMpOE>Y#1wX7;c2Vz{vI{Z}r&(rrBm@noA5Ez2YkVO$jh zWq79j3vBGvLGX868p`%K;6f;S2hHb|co4I69;PA=n-={FJ-v&L#&=2bP*s%@!f(Gu zIVf5|JQPIPT^r8DO_;LjxWBq@q=L}82IO3zOb3-&by?vMW_R{KSSBFUZ>JRj>y>!L zo#lkRpC3YISG}9~7ziNM60GB?tPq!YQcuTi+|9rp`4vlcrqBC|j0I>oRBZq#fCK${ zt-AULQ$im9T&XsF*SE>+9ySJs2RwkfVW9t;*vnbQM)Af*45q`xZ#TCTq)Mfxx+)cZ zUEOIxNm*p3@D#b~!lu^n@nsM?NV@xTQg$N{c>&v{|7hW!mGmb*ldjBbIOobEyo-P` z0iB`?-3YbB-eUh1(!R(EnpTa%^zTvy3vS+j{Y6-d&SX&NRSdfDqc0Hf3fQD=5Ej2OXY}2)`I`deydQh9g74URX?ZQ> zUB=Em14yu_rjL(~JpaFDjVyQ&Xo>nHdG#Zg+Sr{7p|m|*sx@jcm(M4Le|2?@=2X&5 z(F%VN{umJ1jCb!7Q`Y{>{bPHGm69w_CMyGt+zvk~-3rmhJE-%!K8S-#uD=qaq`k#% zi35s;hVpN!G?ViH?}<&k?szU4%L{T4(80C8xk8~c@rNR%T}%o;acJfcE%N}pGh(Vb z#Z#dib?ySOJSPjTS1GlO40$NNm*PF~K9PB?bDyAS@-618=?|#;_dNduW!7uzs0b&j zIf|H7M8=v(jhK988T(^cdF2L4Q&Uk`V%*A_8jxcRAu?s7h)*@<=Qco-z64$4c#msERy+q1kD|Hw? zrpIR|jID8)KX)7G6?p|NCR77Id*2hS2cs|U#}kv1!lHVi0q(8PuAKT|BW71&A@IUA zuV2X)mb@fQcY&E+Q<(T-j@Z?-+Dp6=I{sL+s-`u_fJV&oi0*sr%;>Z=4XL}CRb6w? zKe?L>7BMMvb6oIDweLKPqXb!>XI-~*%xSrnsk*zb;Z>kX^a)Fuz;#5^}Wz)Vz1P>ZQi{0+ddHW zGC?+4vW>e<;jO&hSDR_pX*mD zIn|56gQT3m<=P(YybsUK&I4#a&sb6&PS@yF`zf~iI!{v3?0%@xF{I~KkvW$DEVdJg8|F) z#}+0kv^|i^>?~R~#U5YAwLo-(ZY9G9qtSNlh=e&gI~gQK_+vQ*d8l5Sz9g|^0-3y8 zPham|u1&neur~vp(Nro&zo`kt^j!rD9cq=UiO1%D{hoO$ec1l7Pk50)5xKf`eog8k zaM?#qL(ukc-(0|dUi0&9#_1CiTwJn3P7rqW`c-El7UtJRc47L$gbu!DcVngpiQmlk zJc`=*?ewm*&7J7z$w)Pp+SPY;9mlZs?U4CD<%3nzv_${NYSUW&wf7UD-WsMwF-RwY z@P;Mw@=#AsZ=)T(;1-rif>Yqy+(K@XfvYrlk`lY(fL|Lk>vmQpWeZjK6gJKPV)}sM z2_JH7^Y2paB*F@*DS?OBn5T_~)iAM(fG|lU!4e#M1p@fcm!k4IR9DRu;M)#V%|wb) z4A8g;*a$7$#p-!GzXSHAADQh7I#x3e%oNrxt=E&PnYuD>}1)+3M>vS=Am(Ml*{om z;78BR%}FUn*!4fZ_XZ?eKRyf4ld?nnuw~pC+r`4?ayN^`?T3Q%u>V1zz^fsaLJ6aV zficvc9!BobH6mT57YfCgZ(&9jb6lB|UdoQ)e4SzoGosEhazpOHr|AXF6J*W)p~>*| zf?|JPT;=vQWUNT$F)Gd(8D;7CX|T_(wKmUpY~#W7Un=Zq)em9B<>H@Qy3U zOHCwx*k#s+4r+>vZ^A>?EFjzfsRq8u99LL!rRU{cs)y{1g3;ZD;9hSl@gg(W@EKM2=~NQ1H4 zMxhZ+VF;1k!C!vRX^Lzx-t^@-X>B-kX%&^Se8houF5Ym=VgJFd3S(_P(yh&ITB-XL_hFoBo$|pE}i7E_iYx`EY`a zzo+b-eqf_rY_oOWthSwH{uucafySkIxF(PDJ9r9yhK`OwKB6w+=0=`t8u?A2X=t<={sX;TkO7&VkUSor2T@atB~v}3 zLGmwFf&V3!GDv0P-OKowU6t+H60}%A8c0&ta)w|ScKRS85oq-PNV8W#7tABU{mTzxS#P6!njCEU8WUD#l^?uA z%|^WPQ|tRlk(_z;vQy!O3ckp9M&XYqi(3OXI?U^Sep2(KOk}Zix6TcWjCH;3m4&AI z+S;#oV40xj%lG(~?YptbtJS)`53~0WG}D~WL%-cWXkr)MPrDHYk8AZoky^HZWz$#Z zz4?UAhOyMd2IQ-|Vlk1&rpK*UQas1*Q`z(6yXhq|QH?>qmM)(!@`-|2afY!Zsj=Hd zZFGp6J)1M<7nnppm