Restore local agent work after sai merge

This commit is contained in:
-Puter
2026-07-23 01:27:59 +05:30
parent 608f468219
commit f73e4407c0
12 changed files with 5041 additions and 311 deletions

View File

@@ -0,0 +1,91 @@
/**
* Smoke test: exercise the agentos sandbox adapter through Flue's
* createSandboxSessionEnv wrapper — the same path the zopu agent uses.
*
* Run: bun --env-file=../../.env packages/agents/scripts/agentos-adapter-test.ts
*/
import { agentos } from "../src/sandboxes/agentos";
const main = async () => {
const factory = agentos();
// Flue calls this once per harness initialization.
const env = await factory.createSessionEnv({ id: "smoke-test" });
let pass = 0;
let fail = 0;
const assert = (label: string, condition: boolean) => {
if (condition) {
pass += 1;
console.log(`${label}`);
} else {
fail += 1;
console.log(`${label}`);
}
};
console.log("\n=== File operations ===");
await env.writeFile("test.txt", "hello from Flue adapter");
assert("writeFile", true);
const content = await env.readFile("test.txt");
assert(
"readFile returns written content",
content === "hello from Flue adapter"
);
const stat = await env.stat("test.txt");
assert("stat isFile", stat.isFile === true);
assert("stat size", stat.size === "hello from Flue adapter".length);
assert("exists true", (await env.exists("test.txt")) === true);
assert("exists false for missing", (await env.exists("nope.txt")) === false);
console.log("\n=== Directory operations ===");
await env.mkdir("subdir", { recursive: true });
await env.writeFile("subdir/a.ts", "export const a = 1;");
await env.writeFile("subdir/b.ts", "export const b = 2;");
const entries = await env.readdir("subdir");
assert(
"readdir returns names",
entries.length === 2 && entries.includes("a.ts")
);
await env.rm("subdir/b.ts");
const after = await env.readdir("subdir");
assert("rm removes file", after.length === 1 && after[0] === "a.ts");
await env.rm("subdir", { recursive: true });
assert("rm recursive removes dir", (await env.exists("subdir")) === false);
console.log("\n=== Shell exec ===");
const echoRes = await env.exec("echo 'adapter shell works'");
assert("exec echo exit code", echoRes.exitCode === 0);
assert("exec echo stdout", echoRes.stdout.trim() === "adapter shell works");
const pipeRes = await env.exec("echo 'hello' | tr a-z A-Z");
assert("exec pipe", pipeRes.stdout.trim() === "HELLO");
const lsRes = await env.exec("ls", { cwd: "/workspace" });
assert("exec with cwd", lsRes.stdout.includes("test.txt"));
const envRes = await env.exec("echo $TEST_VAR", {
env: { TEST_VAR: "injected" },
});
assert("exec with env", envRes.stdout.trim() === "injected");
console.log(`\n=== ${pass} passed, ${fail} failed ===`);
if (fail > 0) {
process.exit(1);
}
};
try {
await main();
} catch (error) {
console.error(error);
process.exit(1);
}

View File

@@ -0,0 +1,142 @@
/**
* Reference: agentOS VM as a sandbox backend for Flue.
*
* This script demonstrates the core idea behind a Flue sandbox adapter that
* delegates file/shell operations to an agentOS VM. It does NOT wire into the
* Flue agent yet — it proves the primitives work end-to-end so you can see
* exactly how the adapter will map calls.
*
* Run: bun --env-file=../../.env packages/agents/scripts/agentos-sandbox-ref.ts
*
* Architecture:
*
* Flue agent (host) agentOS VM (isolated Wasm+V8)
* ┌─────────────────────┐ ┌──────────────────────┐
* │ defineAgent(...) │ │ Linux-like sandbox │
* │ sandbox: agentOs()│── readFile ──► │ persistent FS │
* │ │── writeFile ─► │ shell (sh, coreutils)│
* │ │── exec ──────► │ ~6ms cold start │
* └─────────────────────┘ └──────────────────────┘
*
* The Flue SandboxApi maps 1:1 to AgentOs methods:
*
* Flue SandboxApi AgentOs (core SDK)
* ───────────────────────── ──────────────────────────
* readFile(path) vm.readFile(path)
* readFileBuffer(path) vm.readFile(path)
* writeFile(path, content) vm.writeFile(path, content)
* stat(path) vm.stat(path)
* readdir(path) vm.readdir(path)
* exists(path) vm.exists(path)
* mkdir(path) vm.mkdir(path)
* rm(path) vm.remove(path)
* exec(command, opts) vm.exec(command, opts)
*/
import { AgentOs } from "@rivet-dev/agentos-core";
import type { VirtualStat } from "@rivet-dev/agentos-core";
const formatStat = (stat: VirtualStat): string =>
JSON.stringify({
isDirectory: stat.isDirectory,
isSymbolicLink: stat.isSymbolicLink,
mtime: new Date(stat.mtimeMs).toISOString(),
size: stat.size,
});
const main = async () => {
console.log("=== Booting agentOS VM ===\n");
// AgentOs.create() boots a local VM with the default software bundle
// (sh + coreutils). The VM sleeps when idle and wakes on the next action.
const vm = await AgentOs.create();
console.log("VM booted.\n");
// ── File operations ──────────────────────────────────────────────
console.log("=== writeFile ===");
await vm.writeFile("/workspace/hello.txt", "Hello from agentOS VM!");
console.log("wrote /workspace/hello.txt\n");
console.log("=== readFile ===");
const content = await vm.readFile("/workspace/hello.txt");
console.log("content:", new TextDecoder().decode(content), "\n");
console.log("=== stat ===");
const stat = await vm.stat("/workspace/hello.txt");
console.log("stat:", formatStat(stat), "\n");
console.log("=== exists ===");
console.log(
"exists /workspace/hello.txt:",
await vm.exists("/workspace/hello.txt")
);
console.log(
"exists /workspace/nope.txt:",
await vm.exists("/workspace/nope.txt"),
"\n"
);
console.log("=== mkdir + readdir ===");
await vm.mkdir("/workspace/src", { recursive: true });
await vm.writeFile("/workspace/src/a.ts", "export const a = 1;");
await vm.writeFile("/workspace/src/b.ts", "export const b = 2;");
console.log(
"readdir /workspace/src:",
await vm.readdir("/workspace/src"),
"\n"
);
// ── Shell exec ───────────────────────────────────────────────────
console.log("=== exec: echo ===");
const echoResult = await vm.exec("echo 'shell works!'");
console.log(
"stdout:",
echoResult.stdout.trim(),
`(exit ${echoResult.exitCode})\n`
);
console.log("=== exec: pipe + coreutils ===");
const pipeResult = await vm.exec("echo 'hello world' | tr a-z A-Z | wc -w");
console.log(
"stdout:",
pipeResult.stdout.trim(),
`(exit ${pipeResult.exitCode})\n`
);
console.log("=== exec: with cwd + env ===");
await vm.mkdir("/workspace/project", { recursive: true });
await vm.writeFile("/workspace/project/file.txt", "data");
const cwdResult = await vm.exec("pwd && ls", {
cwd: "/workspace/project",
env: { CUSTOM_VAR: "from-env" },
});
console.log(
"stdout:",
cwdResult.stdout.trim(),
`(exit ${cwdResult.exitCode})\n`
);
// ── Remove ───────────────────────────────────────────────────────
console.log("=== remove ===");
await vm.remove("/workspace/src/b.ts");
console.log(
"after remove, readdir:",
await vm.readdir("/workspace/src"),
"\n"
);
// ── Cleanup ──────────────────────────────────────────────────────
await vm.dispose();
console.log("=== VM disposed. All primitives work. ===");
};
try {
await main();
} catch (error) {
console.error(error);
process.exit(1);
}