import { spawn, type ChildProcess } from "node:child_process"; import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { createInterface } from "node:readline"; import { expect, it } from "vitest"; import { z } from "zod"; import { launchVerificationServer, runControlOmb } from "../scripts/control-omb.ts"; import { waitForExit } from "./testing/cleanup.ts"; type Bot = { id: string; threadId: string; name: string }; type Room = { id: string; name: string; memberIds: string[]; section?: string; bulletin: string; working?: boolean }; type RpcReply = { result: { isError?: boolean; content: Array<{ text: string }> } }; type Mounted = { command: string; args: string[]; env: Record }; const CHIEF_ONLY_TOOLS = ["create_bot", "list_team_setup", "propose_team_setup", "propose_bot_deletion", "manage_room", "retry_thread"]; it("creates and manages an own-section room through the mounted Chief MCP proxy", async () => { const fixture = await launchVerificationServer({ ...process.env, FAKE_CLAUDE_MODE: "hang" }); const evidence: unknown[] = [{ fixture: fixture.info }]; const proxies: ChildProcess[] = []; const control = async (args: string[]) => { const result = await runControlOmb([...args, "--url", fixture.info.url]); evidence.push({ command: args, result }); return result; }; const api = async (method: string, path: string, body?: unknown): Promise => { const response = await fetch(`${fixture.info.url}${path}`, { method, headers: { "content-type": "application/json" }, ...(body === undefined ? {} : { body: JSON.stringify(body) }), }); expect(response.ok, `${method} ${path}`).toBe(true); return await response.json() as T; }; try { const bots: Bot[] = []; for (const name of ["Fixture Chief", "Fixture Peer", "Fixture Second", "Foreign Peer"]) { const { bot } = await control(["new-bot", "--name", name]) as { bot: Bot }; bots.push(bot); await api("PATCH", `/api/bots/${bot.id}`, { section: name === "Foreign Peer" ? "Private" : "Room verification" }); } const [chief, peer, second, foreign] = bots; await api("PATCH", `/api/bots/${chief.id}`, { chiefOfStaff: true }); const mount = async (bot: Bot, text: string): Promise => { rmSync(fixture.fixtureDumpPath, { force: true }); await control(["send", "--bot", bot.id, "--text", text]); await expect.poll(() => existsSync(fixture.fixtureDumpPath), { timeout: 15_000 }).toBe(true); const { mcpConfig } = z.object({ mcpConfig: z.object({ mcpServers: z.object({ agents: z.object({ command: z.string(), args: z.array(z.string()), env: z.record(z.string(), z.string()) }), }) }) }).parse(JSON.parse(readFileSync(fixture.fixtureDumpPath, "utf8"))); const mounted = mcpConfig.mcpServers.agents; expect(mounted.env.OMB_BOT_ID).toBe(bot.id); expect(mounted.env.OMB_HARNESS_URL).toBe(fixture.info.url); return mounted; }; const connect = (mounted: Mounted) => { const proxy = spawn(mounted.command, mounted.args, { cwd: process.cwd(), env: { ...mounted.env, HOME: fixture.info.dataDir, USERPROFILE: fixture.info.dataDir, ...(process.env.SystemRoot ? { SystemRoot: process.env.SystemRoot } : {}) }, stdio: ["pipe", "pipe", "pipe"], }); proxies.push(proxy); const pending = new Map void>(); let nextId = 0; createInterface({ input: proxy.stdout! }).on("line", (line) => { const reply = JSON.parse(line) as { id: number; result: unknown }; pending.get(reply.id)?.(reply.result); }); return (method: string, params: Record = {}) => new Promise((resolve, reject) => { const id = ++nextId; const timer = setTimeout(() => { pending.delete(id); reject(new Error(`${method} did not answer`)); }, 10_000); pending.set(id, (result) => { clearTimeout(timer); pending.delete(id); resolve(result as T); }); proxy.stdin!.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n"); }); }; const toolNames = async (rpc: ReturnType) => (await rpc<{ tools: Array<{ name: string }> }>("tools/list")).tools.map((entry) => entry.name); const peerMount = await mount(peer, "Say hello."); expect(peerMount.env.OMB_CHIEF_OF_STAFF).toBe("0"); const peerTools = await toolNames(connect(peerMount)); expect(peerTools).toContain("list_rooms"); expect(CHIEF_ONLY_TOOLS.filter((name) => peerTools.includes(name))).toEqual([]); evidence.push({ peerTools }); await control(["interrupt", "--bot", peer.id]); const mounted = await mount(chief, "Set up a small room for the team."); expect(mounted.env.OMB_CHIEF_OF_STAFF).toBe("1"); const rpc = connect(mounted); expect(await toolNames(rpc)).toEqual(expect.arrayContaining(CHIEF_ONLY_TOOLS)); const tool = async (name: string, args: Record, denied = false) => { const result = await rpc("tools/call", { name, arguments: args }); evidence.push({ tool: name, args, result }); expect(Boolean(result.isError), JSON.stringify(result)).toBe(denied); return result.content.map((item) => item.text).join("\n"); }; const rooms = async () => (await api<{ groups: Room[] }>("GET", "/api/bots?messages=0")).groups; await tool("create_room", { name: "Review room", member_bot_ids: [peer.id], bulletin: "Review only the assigned change." }); const room = (await rooms()).find((candidate) => candidate.name === "Review room")!; expect(room).toMatchObject({ section: "Room verification", memberIds: [chief.id, peer.id], working: false }); expect(await tool("list_rooms", {})).toContain(room.id); await tool("manage_room", { room_id: room.id, action: "rename", name: "Verified room" }); await tool("manage_room", { room_id: room.id, action: "add_members", member_bot_ids: [second.id] }); await tool("manage_room", { room_id: room.id, action: "remove_members", member_bot_ids: [peer.id] }); await tool("manage_room", { room_id: room.id, action: "set_bulletin", bulletin: "Verified short brief ✓" }); const saved = (await rooms()).find((candidate) => candidate.id === room.id); expect(saved).toMatchObject({ name: "Verified room", memberIds: [chief.id, second.id], bulletin: "Verified short brief ✓", section: "Room verification" }); evidence.push({ saved }); await tool("create_room", { name: "Mixed room", member_bot_ids: [foreign.id] }); expect((await rooms()).find((candidate) => candidate.name === "Mixed room")) .toMatchObject({ section: "Room verification", memberIds: [chief.id, foreign.id] }); await control(["messages", "--channel", room.id, "--limit", "10"]); await control(["interrupt", "--bot", chief.id]); await tool("manage_room", { room_id: room.id, action: "rename", name: "Expired turn" }, true); expect((await rooms()).find((candidate) => candidate.id === room.id)?.name).toBe("Verified room"); } finally { for (const proxy of proxies) await waitForExit(proxy, { signal: "SIGTERM" }); await fixture.close(); const evidencePath = `${fixture.info.logPath}.chief-rooms.json`; writeFileSync(evidencePath, JSON.stringify(evidence, null, 2)); expect(existsSync(fixture.info.dataDir)).toBe(false); console.info(JSON.stringify({ ...fixture.info, evidencePath, fixtureRemoved: true, exitCode: fixture.child.exitCode })); } }, 60_000);