sandbox-agent/sdks/acp-http-client/tests/smoke.test.ts
Nathan Flurry 2ba630c180 acp spec
2026-02-11 06:43:36 -08:00

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();
});
});