mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-21 22:01:49 +00:00
135 lines
3.3 KiB
TypeScript
135 lines
3.3 KiB
TypeScript
import { describe, expect, it, beforeAll, afterAll } from "vitest";
|
|
import { existsSync } from "node:fs";
|
|
import { dirname, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { AcpHttpClient, type SessionNotification } from "../src/index.ts";
|
|
import { spawnSandboxAgent, type SandboxAgentSpawnHandle } from "../../typescript/src/spawn.ts";
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
|
function findBinary(): string | null {
|
|
if (process.env.SANDBOX_AGENT_BIN) {
|
|
return process.env.SANDBOX_AGENT_BIN;
|
|
}
|
|
|
|
const cargoPaths = [
|
|
resolve(__dirname, "../../../target/debug/sandbox-agent"),
|
|
resolve(__dirname, "../../../target/release/sandbox-agent"),
|
|
];
|
|
|
|
for (const p of cargoPaths) {
|
|
if (existsSync(p)) {
|
|
return p;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
const BINARY_PATH = findBinary();
|
|
if (!BINARY_PATH) {
|
|
throw new Error(
|
|
"sandbox-agent binary not found. Build it (cargo build -p sandbox-agent) or set SANDBOX_AGENT_BIN.",
|
|
);
|
|
}
|
|
if (!process.env.SANDBOX_AGENT_BIN) {
|
|
process.env.SANDBOX_AGENT_BIN = BINARY_PATH;
|
|
}
|
|
|
|
function sleep(ms: number): Promise<void> {
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
}
|
|
|
|
async function waitFor<T>(
|
|
fn: () => T | undefined | null,
|
|
timeoutMs = 5000,
|
|
stepMs = 25,
|
|
): Promise<T> {
|
|
const started = Date.now();
|
|
while (Date.now() - started < timeoutMs) {
|
|
const value = fn();
|
|
if (value !== undefined && value !== null) {
|
|
return value;
|
|
}
|
|
await sleep(stepMs);
|
|
}
|
|
throw new Error("timed out waiting for condition");
|
|
}
|
|
|
|
describe("AcpHttpClient integration", () => {
|
|
let handle: SandboxAgentSpawnHandle;
|
|
let baseUrl: string;
|
|
let token: string;
|
|
|
|
beforeAll(async () => {
|
|
handle = await spawnSandboxAgent({
|
|
enabled: true,
|
|
log: "silent",
|
|
timeoutMs: 30000,
|
|
});
|
|
baseUrl = handle.baseUrl;
|
|
token = handle.token;
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await handle.dispose();
|
|
});
|
|
|
|
it("runs initialize/newSession/prompt against real /v2/rpc", async () => {
|
|
const updates: SessionNotification[] = [];
|
|
|
|
const client = new AcpHttpClient({
|
|
baseUrl,
|
|
token,
|
|
client: {
|
|
sessionUpdate: async (notification) => {
|
|
updates.push(notification);
|
|
},
|
|
},
|
|
});
|
|
|
|
const initialize = await client.initialize({
|
|
_meta: {
|
|
"sandboxagent.dev": {
|
|
agent: "mock",
|
|
},
|
|
},
|
|
});
|
|
expect(initialize.protocolVersion).toBeTruthy();
|
|
|
|
const session = await client.newSession({
|
|
cwd: process.cwd(),
|
|
mcpServers: [],
|
|
_meta: {
|
|
"sandboxagent.dev": {
|
|
agent: "mock",
|
|
},
|
|
},
|
|
});
|
|
expect(session.sessionId).toBeTruthy();
|
|
|
|
const prompt = await client.prompt({
|
|
sessionId: session.sessionId,
|
|
prompt: [{ type: "text", text: "acp package integration" }],
|
|
});
|
|
expect(prompt.stopReason).toBe("end_turn");
|
|
|
|
await waitFor(() => {
|
|
const text = updates
|
|
.flatMap((entry) => {
|
|
if (entry.update.sessionUpdate !== "agent_message_chunk") {
|
|
return [];
|
|
}
|
|
const content = entry.update.content;
|
|
if (content.type !== "text") {
|
|
return [];
|
|
}
|
|
return [content.text];
|
|
})
|
|
.join("");
|
|
return text.includes("mock: acp package integration") ? text : undefined;
|
|
});
|
|
|
|
await client.disconnect();
|
|
});
|
|
});
|