oxedyne/daimond/dev/verify_localpage.mjs
3.7 KiB, 1 run
created by r2519314175:511, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | // The agent builds a page in the workspace and opens it: it must render in the |
| 2 | // sandboxed local driver (operable), not become a dead https://page.html frame. |
| 3 | // |
| 4 | // THE PAGE IS BUILT IN THE CHAT'S OWN SCRATCH. Until 2026-08-12 the agent was asked |
| 5 | // to write `built.html` at the workspace ROOT; since the chat fence landed a chat is |
| 6 | // confined to `chats/<id>/work` (`scopeChatTo`, www/js/daimond.js) and `Tool::guard` |
| 7 | // (src/tools.rs:5490) refuses a root path before anything is written. Nothing threw: |
| 8 | // the refusal came back as an ordinary tool result, `DaimondWeb.open` then had no such |
| 9 | // page, and the checks below measured an error path rather than the local driver. |
| 10 | // The scratch is chosen over an out-of-band seed BECAUSE THE AGENT BUILDING THE PAGE |
| 11 | // IS HALF THE PROPERTY -- "drive the page you made" is what the local driver is for, |
| 12 | // and a page seeded from outside would not prove the agent could produce one. |
| 13 | import { open, chat, newChat, shot, errors } from './harness.mjs'; |
| 14 | |
| 15 | const ok = [], bad = []; |
| 16 | const check = (name, pass, detail) => { |
| 17 | (pass ? ok : bad).push(name + (detail ? ' — ' + detail : '')); |
| 18 | console.log((pass ? ' ok ' : ' FAIL ') + name + (detail ? ' — ' + detail : '')); |
| 19 | }; |
| 20 | |
| 21 | const s = await open({ name: 'localpage' }); |
| 22 | await newChat(s); |
| 23 | |
| 24 | const dir = await s.page.evaluate(() => { |
| 25 | const f = window.DaimondAttach.focus(); |
| 26 | return f && f.id ? window.DaimondAttach.chatScratch(f.id) : ''; |
| 27 | }); |
| 28 | check('the chat has a scratch folder to build in', !!dir, dir || '(no chat in focus)'); |
| 29 | const PAGE = dir + '/built.html'; |
| 30 | |
| 31 | // Agent writes an HTML page, then opens it. |
| 32 | const wrote = await chat(s, `@tool file_write {"path":"${PAGE}","content":"<html><body><h1 id=hi>Hello from the agent</h1><button id=go>Go</button></body></html>"}`); |
| 33 | check('the agent really built the page — a refused write leaves nothing to open', |
| 34 | !/Refused/.test(wrote) && /Wrote \d+ bytes/.test(wrote), |
| 35 | wrote.slice(-160).replace(/\n/g, ' | ')); |
| 36 | |
| 37 | let r; |
| 38 | try { |
| 39 | r = await s.page.evaluate(async (p) => { |
| 40 | const res = await window.DaimondWeb.open(p); |
| 41 | // Peek at what the panel actually loaded. |
| 42 | const frame = document.getElementById('web-frame'); |
| 43 | let framed = ''; |
| 44 | try { framed = frame && frame.src ? frame.src.slice(0, 12) : ''; } catch (e) {} |
| 45 | return { driver: res.driver, url: res.url, note: res.note, frameSrcScheme: framed }; |
| 46 | }, PAGE); |
| 47 | } catch (e) { r = { err: String(e).split('\n')[0] }; } |
| 48 | console.log('open result:', JSON.stringify(r)); |
| 49 | await s.page.waitForTimeout(500); |
| 50 | await shot(s, 'localpage-open'); |
| 51 | |
| 52 | // snapshot() should see the page's own elements (proves it is operable, not a dead frame). |
| 53 | let snap = null; |
| 54 | try { |
| 55 | snap = await s.page.evaluate(async () => { |
| 56 | const sn = await window.DaimondWeb.snapshot(); |
| 57 | return { nodes: (sn.nodes||[]).length, hasButton: (sn.nodes||[]).some(n => /go|Go/.test(n.name||'') || n.role==='button') }; |
| 58 | }); |
| 59 | } catch (e) { snap = { err: String(e).slice(0,120) }; } |
| 60 | console.log('snapshot:', JSON.stringify(snap)); |
| 61 | |
| 62 | check('OPENED AS LOCAL DRIVER (not a frame to https://built.html)', |
| 63 | r.driver === 'local' && String(r.frameSrcScheme || '').startsWith('blob:'), |
| 64 | JSON.stringify(r)); |
| 65 | // The other half: a frame that loaded is not a page that can be driven, and the |
| 66 | // whole point of the local driver is that the agent can operate what it built. |
| 67 | check('and the page is OPERABLE — its own elements come back from a snapshot', |
| 68 | !!(snap && snap.nodes > 0 && snap.hasButton), JSON.stringify(snap)); |
| 69 | |
| 70 | const errs = errors(s).filter(e => !/502|Bad Gateway/.test(e)); |
| 71 | check('nothing threw', errs.length === 0, errs.slice(0, 2).join(' | ')); |
| 72 | |
| 73 | await s.close(); |
| 74 | console.log(`\n${ok.length} passed, ${bad.length} failed`); |
| 75 | if (bad.length) { bad.forEach(b => console.log(' FAILED: ' + b)); process.exit(1); } |