// The engine's views inside an agent host, driven by a scripted host in a // headless browser: // // WQ_CLI=PATH/wai-quantum [WQ_SERVICE=PATH/wai-quantum-service] node test/views.test.mjs // // The host speaks the extension for interactive views (io.modelcontextprotocol/ui, // 2026-01-26) as a host does: it says so in initialize, lists the tools and finds each // one's ui:// resource, reads the resource, and draws it in a sandboxed frame under a // content-security policy, answering the view's ui/initialize, giving it the tool's // input and result, carrying its tool calls to the server (only to tools whose // visibility lets a view call them), and keeping what it tells the model. // // For the command line's server (stdio), under the extension's default policy (no // WebAssembly) and under that policy with 'wasm-unsafe-eval': // - every tool with a view names it, and the six views are listed and read, each // one self-contained page within the limit (the sizes are printed); // - a run: its verdict, its counts, its circuit, and its receipt checked in the view, // by the engine as WebAssembly where the policy lets it and as JavaScript where it // does not; the model told the check; a count changed after sealing fails the check; // - a refusal with its code; a receipt checked beside the server's own answer, for a // sealed job and a changed one; a compile's passes and its certificate checked; a // lint's refusal; a repair's changes; a critique's smells marked on the circuit; an // estimate's frontier and its seal; a device target's map; a run on a target; // - a view makes no request of its own, and draws without a script error. // With WQ_SERVICE, the job service started here over HTTP: the session id that says // the host draws views, the view's own tool kept from the model, a run drawn and its // receipt checked as the service's, and a job still running followed by the view // until it is done. // Exit 0 iff every check passes. import { spawn } from 'node:child_process'; import { createServer } from 'node:http'; import { mkdirSync, mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; const { chromium } = (await import(process.env.WAI_E2E_DRIVER ?? 'playwright')).default; const SHOTS = resolve(dirname(fileURLToPath(import.meta.url)), 'screenshots'); const CLI = process.env.WQ_CLI; const SERVICE = process.env.WQ_SERVICE; if (!CLI) { console.error('give WQ_CLI: the wai-quantum binary'); process.exit(2); } const LIMIT = 1 << 20; const UI = { extensions: { 'io.modelcontextprotocol/ui': { mimeTypes: ['text/html;profile=mcp-app'] } } }; const STRICT = "default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; media-src 'self' data:; connect-src 'none';"; const WASM = STRICT.replace("script-src 'self' 'unsafe-inline';", "script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval';"); const LONG = 60_000; let fails = 0; const check = (ok, what) => { console.log(`${ok ? 'PASS' : 'FAIL'} ${what}`); if (!ok) fails += 1; }; // ---- the servers ---- function stdio(bin) { const p = spawn(bin, ['mcp'], { stdio: ['pipe', 'pipe', 'inherit'] }); let buf = ''; let next = 1; const waiting = new Map(); p.stdout.setEncoding('utf8'); p.stdout.on('data', (d) => { buf += d; for (let i = buf.indexOf('\n'); i >= 0; i = buf.indexOf('\n')) { const line = buf.slice(0, i); buf = buf.slice(i + 1); if (!line.trim()) continue; const m = JSON.parse(line); waiting.get(m.id)?.(m); waiting.delete(m.id); } }); const rpc = (method, params = {}) => new Promise((ok) => { const id = next++; waiting.set(id, ok); p.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n'); }); return { name: 'the command line’s server', rpc, close: () => p.kill() }; } function http(base) { let session = null; let next = 1; const rpc = async (method, params = {}) => { const headers = { 'content-type': 'application/json', accept: 'application/json, text/event-stream' }; if (session) headers['mcp-session-id'] = session; const r = await fetch(`${base}/mcp`, { method: 'POST', headers, body: JSON.stringify({ jsonrpc: '2.0', id: next++, method, params }) }); session = r.headers.get('mcp-session-id') ?? session; return r.json(); }; return { name: 'the job service’s server', rpc, session: () => session }; } // ---- the host page ---- // The host's side of the protocol, in the page: each view in a sandboxed frame of its // own, under `csp`, given its tool's input and result; what it says is kept in `log`. const HOST = `