Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/dev/verify_autonomous_posture.mjs

9.9 KiB, 1 run

created by r2519314175:247, 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// verify_autonomous_posture.mjs — the per-computer "autonomous" posture, the
2// device-local switch that lets THIS machine finish work dispatched to it
3// without asking, reaching the web and running commands on its own. The bound is
4// the account's provider and Daimond credit, not a dialog; there is no spend gate
5// by the owner's decision, so the only thing to prove here is the SHAPE of the
6// gate, not a cap on it.
7//
8// It runs entirely in one page against the static server — no gateway, no o3db,
9// no mock provider. The egress gate `egressAllowed` is driven through the same
10// `window.__daimondEgressAllowed` hook the wasm tools call, and the parked-vs-
11// allowed outcome is read three ways that cannot lie: whether the promise
12// resolved, whether a consent tile landed in `daimond-pending`, and whether a
13// real `.modal.dlg` was raised.
14//
15// node dev/verify_autonomous_posture.mjs
16//
17// Needs dev/serve.mjs (DAIMOND_PORT, default 8777). No gateway, no mock LLM.
18//
19// The four claims, in the task's order:
20// a. OFF — a dispatched worker's web act (alone, nobody able to answer) still
21// PARKS on the Pending panel: unresolved, a consent tile written, no auto-allow.
22// b. ON — the same act returns 'allow' at once: no park, no dialog, no tile.
23// c. STRICT — a `strict:true` navigation is NEVER auto-allowed, even with the
24// posture on: it still raises the dialog and does not resolve to 'allow'.
25// d. NO SYNC — the posture key appears nowhere in `DaimondCore.collectSync()`
26// nor in `DaimondSync.parcel()`, so arming one machine cannot arm another.
27import { open } from './harness.mjs';
28
29const KEY = 'daimond-autonomous-posture'; // must match daimond.js/handmode.js
30
31const ok = [], bad = [];
32const check = (name, pass, detail) => {
33 (pass ? ok : bad).push(name);
34 console.log((pass ? ' ok ' : ' FAIL ') + name + (detail !== undefined && detail !== '' ? ' — ' + detail : ''));
35};
36const note = (t) => console.log(' · ' + t);
37
38const s = await open({ name: 'autonomous', signIn: true, connect: false, defaults: false });
39const { page } = s;
40
41/// Drive the gate and watch it for `ms`. Returns whether the promise resolved in
42/// that window and, if so, the verdict — plus the DELTA in consent tiles on the
43/// Pending panel and in real `.modal.dlg` dialogs, measured across the call so
44/// nothing an earlier phase left behind is counted. The tagged stand-in modal for
45/// "nobody can answer" is never counted as a dialog.
46async function drive(payload, opts, ms) {
47 return await page.evaluate(async (args) => {
48 const consentCount = () => {
49 try { return (JSON.parse(localStorage.getItem('daimond-pending') || '[]') || [])
50 .filter(it => it && it.kind === 'consent').length; }
51 catch (e) { return 0; }
52 };
53 const dialogCount = () => document.querySelectorAll('.modal.dlg:not([data-fake])').length;
54 const consentBefore = consentCount(), dialogBefore = dialogCount();
55 let done = false, verdict = null;
56 const p = Promise.resolve(window.__daimondEgressAllowed(JSON.stringify(args.payload), args.opts))
57 .then(v => { done = true; verdict = v; }, e => { done = true; verdict = 'ERR:' + (e && e.message || e); });
58 await Promise.race([p, new Promise(r => setTimeout(r, args.ms))]);
59 return { done, verdict, consent: consentCount() - consentBefore, dialogs: dialogCount() - dialogBefore };
60 }, { payload, opts, ms });
61}
62
63/// Set the posture, and raise or clear the "nobody can answer" condition — a
64/// tagged `.modal.dlg` makes `someoneCanAnswer()` false without being mistaken
65/// for a dialog the gate itself put up.
66async function setup(postureOn, blockAnswer) {
67 await page.evaluate((args) => {
68 if (args.postureOn) localStorage.setItem(args.KEY, '1');
69 else localStorage.removeItem(args.KEY);
70 document.querySelectorAll('.modal.dlg[data-fake]').forEach(n => n.remove());
71 if (args.blockAnswer) {
72 const m = document.createElement('div');
73 m.className = 'modal dlg';
74 m.setAttribute('data-fake', '1');
75 document.body.appendChild(m);
76 }
77 }, { postureOn, blockAnswer, KEY });
78}
79
80try {
81 await page.waitForFunction(() =>
82 !!(window.__daimondEgressAllowed && window.DaimondCore && DaimondCore.collectSync),
83 null, { timeout: 15000 });
84
85 const act = { tool: 'web_click', url: 'https://shop.example/checkout', alone: true };
86
87 // ═══════════════════════════════════════════════════════════════
88 // a. POSTURE OFF — the dispatched worker still parks
89 // ═══════════════════════════════════════════════════════════════
90 console.log('\n— a: posture OFF, a dispatched worker\'s web act still parks —');
91 await setup(false, true);
92 const offPosture = await page.evaluate((k) => localStorage.getItem(k), KEY);
93 check('the posture reads OFF (silence, or explicitly cleared)', offPosture !== '1', `stored=${offPosture}`);
94 const a = await drive(act, undefined, 800);
95 note(`resolved=${a.done} verdict=${a.verdict} consentTiles=${a.consent} dialogs=${a.dialogs}`);
96 check('OFF: the worker\'s act does NOT auto-allow (it waits)', a.done === false && a.verdict !== 'allow');
97 check('OFF: it PARKS — a consent tile is raised on the Pending panel', a.consent === 1, `${a.consent} tile(s)`);
98 check('OFF: and it does so by parking, not by a dialog into an empty room', a.dialogs === 0, `${a.dialogs} dialog(s)`);
99
100 // ═══════════════════════════════════════════════════════════════
101 // b. POSTURE ON — the same act is allowed at once
102 // ═══════════════════════════════════════════════════════════════
103 console.log('\n— b: posture ON, the same act is allowed with no dialog and no park —');
104 await setup(true, true);
105 const onPosture = await page.evaluate((k) => localStorage.getItem(k), KEY);
106 check('the posture reads ON for this computer', onPosture === '1', `stored=${onPosture}`);
107 const b = await drive(act, undefined, 800);
108 note(`resolved=${b.done} verdict=${b.verdict} consentTiles=${b.consent} dialogs=${b.dialogs}`);
109 check('ON: the worker\'s act resolves to \'allow\'', b.done === true && b.verdict === 'allow', String(b.verdict));
110 check('ON: nothing is parked — no consent tile', b.consent === 0, `${b.consent} tile(s)`);
111 check('ON: and nothing is asked — no dialog', b.dialogs === 0, `${b.dialogs} dialog(s)`);
112
113 // ═══════════════════════════════════════════════════════════════
114 // c. STRICT is never auto-allowed, even with the posture on
115 // ═══════════════════════════════════════════════════════════════
116 console.log('\n— c: strict:true is never auto-allowed, posture on or not —');
117 await setup(true, false); // posture on, no stand-in modal: a real dialog is expected
118 const c = await drive({ url: 'https://elsewhere.example/' }, { strict: true }, 800);
119 note(`resolved=${c.done} verdict=${c.verdict} dialogs=${c.dialogs}`);
120 check('STRICT: the posture does NOT wave it through', !(c.done === true && c.verdict === 'allow'), String(c.verdict));
121 check('STRICT: it still asks — a dialog is raised', c.dialogs >= 1, `${c.dialogs} dialog(s)`);
122 // Clear the dialog we raised, so it cannot bleed into the next phase's counts.
123 await page.evaluate(() => document.querySelectorAll('.modal.dlg').forEach(n => n.remove()));
124
125 // ═══════════════════════════════════════════════════════════════
126 // d. the posture key never enters the sync parcel
127 // ═══════════════════════════════════════════════════════════════
128 console.log('\n— d: the posture is device-local — it never enters the sync parcel —');
129 await page.evaluate((k) => { try { localStorage.setItem(k, '1'); } catch (e) {} }, KEY);
130 const sync = await page.evaluate(async () => {
131 const collectJson = JSON.stringify(await window.DaimondCore.collectSync());
132 let parcelJson = '', parcelErr = '';
133 try {
134 if (window.DaimondSync && DaimondSync.parcel) parcelJson = JSON.stringify(await DaimondSync.parcel());
135 } catch (e) { parcelErr = String(e && e.message || e); }
136 return { collectJson, parcelJson, parcelErr };
137 });
138 const inCollect = sync.collectJson.includes('daimond-autonomous-posture') || sync.collectJson.includes('autonomousPosture');
139 check('the posture key is ABSENT from DaimondCore.collectSync()', !inCollect,
140 `collect parcel is ${sync.collectJson.length} B`);
141 if (sync.parcelJson) {
142 const inParcel = sync.parcelJson.includes('daimond-autonomous-posture') || sync.parcelJson.includes('autonomousPosture');
143 check('the posture key is ABSENT from DaimondSync.parcel() output', !inParcel,
144 `sealed parcel is ${sync.parcelJson.length} B`);
145 } else {
146 // The sealed parcel only ever wraps what collectSync() returns, so absence
147 // there is absence here; the offline seal did not run, which is noted, not failed.
148 note(`DaimondSync.parcel() did not run offline (${sync.parcelErr || 'no output'}); collectSync() is the binding evidence`);
149 check('the posture key is ABSENT from the sync parcel (via collectSync, its only source)', !inCollect);
150 }
151
152} catch (e) {
153 check('no exception during the run', false, String(e && e.stack || e).slice(0, 400));
154} finally {
155 await s.close();
156}
157
158console.log('\n' + ok.length + ' ok, ' + bad.length + ' failed');
159process.exit(bad.length ? 1 : 0);