import { afterEach, beforeEach, describe, expect, test } from "vitest";import { z } from "zod";import { AgentOs, binding, bindings } from "../src/index.js";import { createProjectedAgentPackage, type ProjectedAgentPackage,} from "./helpers/projected-agent-package.js"; /** * Mock ACP adapter that answers initialize/session/new and echoes its launch environment in agentInfo so * the test can assert the sidecar-injected system prompt. */const MOCK_ACP_ADAPTER = `let buffer = '';process.stdin.resume();process.stdin.on('data', (chunk) => { const str = chunk instanceof Uint8Array ? new TextDecoder().decode(chunk) : String(chunk); buffer += str; while (true) { const idx = buffer.indexOf('\\n'); if (idx === -1) break; const line = buffer.substring(0, idx); buffer = buffer.substring(idx + 1); if (!line.trim()) continue; try { const msg = JSON.parse(line); if (msg.id === undefined) continue; let result; switch (msg.method) { case 'initialize': result = { protocolVersion: 1, agentInfo: { name: 'mock-adapter', version: '1.0', systemPrompt: process.env.ACP_APPEND_SYSTEM_PROMPT || null } }; break; case 'session/new': result = { sessionId: 'mock-session-1' }; break; case 'session/cancel': result = {}; break; default: process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: msg.id, error: { code: -32601, message: 'Method not found' } }) + '\\n'); continue; } process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: msg.id, result }) + '\\n'); } catch (e) {} }});`; const mathBindings = bindings({ name: "math", description: "Math utilities", bindings: { add: binding({ description: "Add two numbers", inputSchema: z.object({ a: z.number(), b: z.number(), }), execute: ({ a, b }) => ({ sum: a + b }), examples: [ { description: "Add 1 and 2", input: { a: 1, b: 2 }, }, ], }), },}); describe("binding reference registration", () => { let vm: AgentOs; let agentPackage: ProjectedAgentPackage; beforeEach(async () => { agentPackage = createProjectedAgentPackage({ name: "pi", adapterScript: MOCK_ACP_ADAPTER, }); vm = await AgentOs.create({ defaultSoftware: false, software: [agentPackage.software], bindings: [mathBindings], }); }); afterEach(async () => { await vm.dispose(); agentPackage.cleanup(); }); test("stores generated binding reference markdown on the VM", () => { const bindingReference = (vm as unknown as { _bindingReference: string }) ._bindingReference; expect(bindingReference).toContain("## Available Host Bindings"); expect(bindingReference).toContain( "Run `agentos list-bindings` to see all available bindings.", ); expect(bindingReference).toContain("### math"); expect(bindingReference).toContain("Math utilities"); expect(bindingReference).toContain( "`agentos-math add --a <number> --b <number>`", ); expect(bindingReference).toContain("Add 1 and 2"); }); test("openSession injects the registered binding reference into the system prompt", async () => { const sessionId = "binding-reference"; await vm.openSession({ sessionId, agent: "pi" }); const agentInfo = (await vm.getSessionAgentInfo({ sessionId })) as { systemPrompt?: string; }; const prompt = agentInfo.systemPrompt ?? ""; expect(prompt).toContain("## Available Host Bindings"); expect(prompt).toContain("`agentos-math add --a <number> --b <number>`"); expect(prompt).toContain("### math"); await vm.unloadSession({ sessionId }); });});
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { z } from "zod";
import { AgentOs, binding, bindings } from "../src/index.js";
import {
createProjectedAgentPackage,
type ProjectedAgentPackage,
} from "./helpers/projected-agent-package.js";
/**
* Mock ACP adapter that answers initialize/session/new and echoes its launch environment in agentInfo so
* the test can assert the sidecar-injected system prompt.
*/
const MOCK_ACP_ADAPTER = `
let buffer = '';
process.stdin.resume();
process.stdin.on('data', (chunk) => {
const str = chunk instanceof Uint8Array ? new TextDecoder().decode(chunk) : String(chunk);
buffer += str;
while (true) {
const idx = buffer.indexOf('\\n');
if (idx === -1) break;
const line = buffer.substring(0, idx);
buffer = buffer.substring(idx + 1);
if (!line.trim()) continue;
try {
const msg = JSON.parse(line);
if (msg.id === undefined) continue;
let result;
switch (msg.method) {
case 'initialize':
result = { protocolVersion: 1, agentInfo: { name: 'mock-adapter', version: '1.0', systemPrompt: process.env.ACP_APPEND_SYSTEM_PROMPT || null } };
break;
case 'session/new':
result = { sessionId: 'mock-session-1' };
break;
case 'session/cancel':
result = {};
break;
default:
process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: msg.id, error: { code: -32601, message: 'Method not found' } }) + '\\n');
continue;
}
process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: msg.id, result }) + '\\n');
} catch (e) {}
}
});
`;
const mathBindings = bindings({
name: "math",
description: "Math utilities",
bindings: {
add: binding({
description: "Add two numbers",
inputSchema: z.object({
a: z.number(),
b: z.number(),
}),
execute: ({ a, b }) => ({ sum: a + b }),
examples: [
{
description: "Add 1 and 2",
input: { a: 1, b: 2 },
},
],
}),
},
});
describe("binding reference registration", () => {
let vm: AgentOs;
let agentPackage: ProjectedAgentPackage;
beforeEach(async () => {
agentPackage = createProjectedAgentPackage({
name: "pi",
adapterScript: MOCK_ACP_ADAPTER,
});
vm = await AgentOs.create({
defaultSoftware: false,
software: [agentPackage.software],
bindings: [mathBindings],
});
});
afterEach(async () => {
await vm.dispose();
agentPackage.cleanup();
});
test("stores generated binding reference markdown on the VM", () => {
const bindingReference = (vm as unknown as { _bindingReference: string })
._bindingReference;
expect(bindingReference).toContain("## Available Host Bindings");
expect(bindingReference).toContain(
"Run `agentos list-bindings` to see all available bindings.",
);
expect(bindingReference).toContain("### math");
expect(bindingReference).toContain("Math utilities");
expect(bindingReference).toContain(
"`agentos-math add --a <number> --b <number>`",
);
expect(bindingReference).toContain("Add 1 and 2");
});
test("openSession injects the registered binding reference into the system prompt", async () => {
const sessionId = "binding-reference";
await vm.openSession({ sessionId, agent: "pi" });
const agentInfo = (await vm.getSessionAgentInfo({ sessionId })) as {
systemPrompt?: string;
};
const prompt = agentInfo.systemPrompt ?? "";
expect(prompt).toContain("## Available Host Bindings");
expect(prompt).toContain("`agentos-math add --a <number> --b <number>`");
expect(prompt).toContain("### math");
await vm.unloadSession({ sessionId });
});
});