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 --b `", ); 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 --b `"); expect(prompt).toContain("### math"); await vm.unloadSession({ sessionId }); }); });