12// Small ACP peer used by buzz-runner-integration.mjs. It is intentionally3// disposable: it never connects to a relay and only speaks JSON-RPC on stdio.4let buffer = '';5let permissionID = 40;6let sessionID = 'qa-session';7let awaitingPermission = false;8if (process.env.FAKE_ACP_MODE === 'crash-once') {9 const fs = await import('node:fs');10 if (!fs.existsSync(process.env.FAKE_ACP_MARKER)) fs.writeFileSync(process.env.FAKE_ACP_MARKER, 'crashed');11 else process.env.FAKE_ACP_MODE = '';12}13process.stdin.setEncoding('utf8');14process.stdin.on('data', chunk => {15 buffer += chunk;16 while (buffer.includes('\n')) {17 const newline = buffer.indexOf('\n');18 const line = buffer.slice(0, newline);19 buffer = buffer.slice(newline + 1);20 if (!line.trim()) continue;21 let frame;22 try { frame = JSON.parse(line); } catch { continue; }23 handle(frame);24 }25});26function send(frame) { process.stdout.write(JSON.stringify(frame) + '\n'); }27function handle(frame) {28 if (frame.method === 'initialize') {29 send({jsonrpc: '2.0', id: frame.id, result: {protocolVersion: 1}});30 return;31 }32 if (frame.method === 'session/new') {33 send({jsonrpc: '2.0', id: frame.id, result: {sessionId: sessionID}});34 return;35 }36 if (frame.method === 'session/cancel') {37 send({jsonrpc: '2.0', id: frame.id, result: {}});38 return;39 }40 if (frame.method === 'session/prompt') {41 if (process.env.FAKE_ACP_MODE === 'crash-once') process.exit(23);42 send({jsonrpc: '2.0', method: 'session/update', params: {sessionId: sessionID, update: {sessionUpdate: 'tool_call', title: 'publish preview', status: 'running'}}});43 awaitingPermission = true;44 send({jsonrpc: '2.0', id: ++permissionID, method: 'session/request_permission', params: {sessionId: sessionID, toolCall: {title: 'Publish preview', rawInput: {path: 'public/preview.html'}}, options: [{optionId: 'allow', name: 'Allow once', kind: 'allow_once'}, {optionId: 'deny', name: 'Deny once', kind: 'reject_once'}]}});45 return;46 }47 if (frame.id === permissionID && awaitingPermission) {48 awaitingPermission = false;49 const outcome = frame.result?.outcome;50 if (outcome?.outcome !== 'selected' || outcome.optionId !== 'allow') {51 send({jsonrpc: '2.0', id: 3, result: {stopReason: 'cancelled'}});52 return;53 }54 send({jsonrpc: '2.0', method: 'session/update', params: {sessionId: sessionID, update: {sessionUpdate: 'agent_message_chunk', content: {type: 'text', text: 'Preview published.'}}}});55 send({jsonrpc: '2.0', id: 3, result: {stopReason: 'end_turn'}});56 }57}