@miller-tech/uap 1.170.0 → 1.172.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/bin/cli.js +26 -0
  3. package/dist/bin/cli.js.map +1 -1
  4. package/dist/browser/web-browser.d.ts +17 -0
  5. package/dist/browser/web-browser.d.ts.map +1 -1
  6. package/dist/browser/web-browser.js +36 -0
  7. package/dist/browser/web-browser.js.map +1 -1
  8. package/dist/cli/interaction.d.ts +29 -0
  9. package/dist/cli/interaction.d.ts.map +1 -0
  10. package/dist/cli/interaction.js +217 -0
  11. package/dist/cli/interaction.js.map +1 -0
  12. package/dist/cli/verify.d.ts +6 -0
  13. package/dist/cli/verify.d.ts.map +1 -1
  14. package/dist/cli/verify.js +112 -7
  15. package/dist/cli/verify.js.map +1 -1
  16. package/dist/delivery/fidelity.d.ts +16 -0
  17. package/dist/delivery/fidelity.d.ts.map +1 -1
  18. package/dist/delivery/fidelity.js +7 -0
  19. package/dist/delivery/fidelity.js.map +1 -1
  20. package/dist/delivery/interaction/driver.d.ts +60 -0
  21. package/dist/delivery/interaction/driver.d.ts.map +1 -0
  22. package/dist/delivery/interaction/driver.js +18 -0
  23. package/dist/delivery/interaction/driver.js.map +1 -0
  24. package/dist/delivery/interaction/manifest.d.ts +84 -0
  25. package/dist/delivery/interaction/manifest.d.ts.map +1 -0
  26. package/dist/delivery/interaction/manifest.js +241 -0
  27. package/dist/delivery/interaction/manifest.js.map +1 -0
  28. package/dist/delivery/interaction/mine.d.ts +54 -0
  29. package/dist/delivery/interaction/mine.d.ts.map +1 -0
  30. package/dist/delivery/interaction/mine.js +218 -0
  31. package/dist/delivery/interaction/mine.js.map +1 -0
  32. package/dist/delivery/interaction/runner.d.ts +51 -0
  33. package/dist/delivery/interaction/runner.d.ts.map +1 -0
  34. package/dist/delivery/interaction/runner.js +277 -0
  35. package/dist/delivery/interaction/runner.js.map +1 -0
  36. package/dist/delivery/interaction/types.d.ts +223 -0
  37. package/dist/delivery/interaction/types.d.ts.map +1 -0
  38. package/dist/delivery/interaction/types.js +19 -0
  39. package/dist/delivery/interaction/types.js.map +1 -0
  40. package/dist/delivery/interaction/verdict.d.ts +26 -0
  41. package/dist/delivery/interaction/verdict.d.ts.map +1 -0
  42. package/dist/delivery/interaction/verdict.js +139 -0
  43. package/dist/delivery/interaction/verdict.js.map +1 -0
  44. package/dist/delivery/interaction/watchdog.d.ts +82 -0
  45. package/dist/delivery/interaction/watchdog.d.ts.map +1 -0
  46. package/dist/delivery/interaction/watchdog.js +210 -0
  47. package/dist/delivery/interaction/watchdog.js.map +1 -0
  48. package/dist/delivery/interaction/web-driver.d.ts +102 -0
  49. package/dist/delivery/interaction/web-driver.d.ts.map +1 -0
  50. package/dist/delivery/interaction/web-driver.js +306 -0
  51. package/dist/delivery/interaction/web-driver.js.map +1 -0
  52. package/dist/delivery/interaction-gate.d.ts +45 -0
  53. package/dist/delivery/interaction-gate.d.ts.map +1 -0
  54. package/dist/delivery/interaction-gate.js +248 -0
  55. package/dist/delivery/interaction-gate.js.map +1 -0
  56. package/dist/delivery/mission-acceptance.d.ts +2 -0
  57. package/dist/delivery/mission-acceptance.d.ts.map +1 -1
  58. package/dist/delivery/mission-acceptance.js +23 -0
  59. package/dist/delivery/mission-acceptance.js.map +1 -1
  60. package/dist/delivery/vision-judge.d.ts +21 -0
  61. package/dist/delivery/vision-judge.d.ts.map +1 -1
  62. package/dist/delivery/vision-judge.js +96 -0
  63. package/dist/delivery/vision-judge.js.map +1 -1
  64. package/dist/types/config.d.ts +20 -0
  65. package/dist/types/config.d.ts.map +1 -1
  66. package/dist/types/config.js +7 -0
  67. package/dist/types/config.js.map +1 -1
  68. package/package.json +1 -1
  69. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  70. package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
  71. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
  72. package/tools/agents/scripts/anthropic_proxy.py +14 -5
  73. package/tools/agents/tests/test_proxy_auth_headers.py +82 -0
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Interaction watchdog — invariants that hold for EVERY probe, checked
3
+ * independently of what any individual probe asserts.
4
+ *
5
+ * Probes prove the promised behaviour. The watchdog catches the failures nobody
6
+ * thought to write a probe for: the loop stops ticking, an uncaught error fires,
7
+ * a tracked number goes NaN, a collection grows without bound. Tonight's fatal
8
+ * defect (`player.takeDamage is not a function`) is exactly the first class —
9
+ * one uncaught throw, the rAF loop never rescheduled, and every later frame was
10
+ * a frozen copy of the last good one, which no screenshot can distinguish from a
11
+ * paused game.
12
+ */
13
+ /**
14
+ * Injected before any page script runs. Wraps requestAnimationFrame to count
15
+ * ticks and records uncaught errors. Deliberately defensive: a page that
16
+ * replaces rAF or throws during setup must not break the watchdog itself.
17
+ *
18
+ * SELF-INVOKED — `addInitScript` evaluates the string as source, so a bare
19
+ * function expression would construct a function and discard it. That mistake
20
+ * is invisible at runtime: the counters simply never appear, every sample falls
21
+ * back to `ticks: 0`, `everTicked` stays false and `loopAlive` is unconditionally
22
+ * true — the frozen-loop detection this gate exists for silently never fires.
23
+ * Matches RAF_INSTRUMENT in execution-gate.ts, which is self-invoked for the
24
+ * same reason.
25
+ */
26
+ export const DEFAULT_WATCH_GLOBAL = '__uapWatch';
27
+ /**
28
+ * Build the init script under a caller-chosen global name.
29
+ *
30
+ * The name is randomised per run because the page can otherwise DEFEAT its own
31
+ * watchdog: `window.__uapWatch = {ticks: 1e9, errors: []}` forges liveness and
32
+ * empties the error list, and the `if (already defined) return` guard means a
33
+ * page that predefines the global blocks instrumentation outright. Randomising
34
+ * is not a security boundary — it raises the cost of a targeted forge, while
35
+ * the authoritative error channel remains Playwright-side, out of page reach.
36
+ */
37
+ export function buildWatchdogInitScript(globalName = DEFAULT_WATCH_GLOBAL) {
38
+ return WATCHDOG_INIT_SCRIPT.split(DEFAULT_WATCH_GLOBAL).join(globalName);
39
+ }
40
+ export const WATCHDOG_INIT_SCRIPT = `(function () {
41
+ try {
42
+ if (window.__uapWatch) return;
43
+ var w = { ticks: 0, errors: [], startedAt: Date.now() };
44
+ window.__uapWatch = w;
45
+ var raf = window.requestAnimationFrame;
46
+ if (typeof raf === 'function') {
47
+ window.requestAnimationFrame = function (cb) {
48
+ return raf.call(window, function (t) {
49
+ w.ticks++;
50
+ return cb(t);
51
+ });
52
+ };
53
+ }
54
+ window.addEventListener('error', function (e) {
55
+ try { w.errors.push('uncaught: ' + (e && e.message ? e.message : String(e))); } catch (x) {}
56
+ });
57
+ window.addEventListener('unhandledrejection', function (e) {
58
+ try { w.errors.push('unhandledrejection: ' + String(e && e.reason)); } catch (x) {}
59
+ });
60
+ } catch (x) {
61
+ /* the watchdog must never break the page it observes */
62
+ }
63
+ })()`;
64
+ /**
65
+ * Read one watchdog sample. `watchExprs` are artifact expressions yielding
66
+ * numbers (collection sizes, key state fields) — the miner supplies them, and
67
+ * each one that goes NaN or grows without bound becomes a defect.
68
+ */
69
+ /**
70
+ * NaN cannot survive the JSON transport — `JSON.stringify(NaN)` is `null`, so a
71
+ * corrupted field arrives indistinguishable from "not present" and the NaN check
72
+ * can never fire in production. Encode it explicitly and decode on the way back.
73
+ */
74
+ export const NAN_SENTINEL = '__uap_NaN__';
75
+ export function watchdogSampleScript(watchExprs, globalName = DEFAULT_WATCH_GLOBAL) {
76
+ const entries = watchExprs
77
+ .map((e) => {
78
+ const key = JSON.stringify(e);
79
+ return ` try { var v = (${e}); vals[${key}] = (typeof v === 'number' && v !== v) ? ${JSON.stringify(NAN_SENTINEL)} : v; } catch (x) { vals[${key}] = null; }`;
80
+ })
81
+ .join('\n');
82
+ return `(function () {
83
+ var w = window[${JSON.stringify(globalName)}] || { ticks: 0, errors: [] };
84
+ var vals = {};
85
+ ${entries}
86
+ return JSON.stringify({ ticks: w.ticks, errors: w.errors.slice(0, 20), values: vals });
87
+ })`;
88
+ }
89
+ export function parseWatchdogSample(raw) {
90
+ try {
91
+ const text = typeof raw === 'string' ? raw : JSON.stringify(raw);
92
+ const parsed = JSON.parse(text);
93
+ if (!parsed || typeof parsed !== 'object')
94
+ return null;
95
+ const values = {};
96
+ for (const [k, v] of Object.entries(parsed.values ?? {})) {
97
+ values[k] = v === NAN_SENTINEL ? Number.NaN : v;
98
+ }
99
+ return {
100
+ ticks: Number(parsed.ticks) || 0,
101
+ errors: Array.isArray(parsed.errors) ? parsed.errors.map(String) : [],
102
+ values,
103
+ };
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ }
109
+ /** Does this expression observe the SIZE of a collection (vs a counter)? */
110
+ export function isCollectionSize(expr) {
111
+ return /\.(length|size)\s*$/.test(expr) || /\.(length|size)\b/.test(expr);
112
+ }
113
+ /**
114
+ * Pure verdict from the observed samples. Kept free of IO so the interesting
115
+ * judgements are unit-testable without a browser.
116
+ */
117
+ export function judgeWatchdog(samples, driverErrors = [], opts = {}) {
118
+ const minTicks = opts.minTicks ?? 1;
119
+ const growthFactor = opts.growthFactor ?? 20;
120
+ const growthFloor = opts.growthFloor ?? 500;
121
+ const errors = [...new Set([...samples.flatMap((s) => s.errors), ...driverErrors])];
122
+ const first = samples[0];
123
+ const last = samples[samples.length - 1];
124
+ const prev = samples.length >= 2 ? samples[samples.length - 2] : first;
125
+ // Ticks in the FINAL window: a loop that ran for two seconds and then died
126
+ // still has a large cumulative count, so only the last interval is evidence
127
+ // that it is alive NOW.
128
+ const ticksObserved = last && prev ? Math.max(0, last.ticks - prev.ticks) : 0;
129
+ // A page with no rAF at all (a form, a static doc) legitimately never ticks.
130
+ // Only claim the loop is dead when it demonstrably ticked earlier.
131
+ const everTicked = (last?.ticks ?? 0) > 0;
132
+ // TWO samples minimum. With one, `prev === last`, the delta is 0 by
133
+ // construction, and any artifact that ever ticked is declared dead — so a
134
+ // single dropped watchdog read would fail a perfectly healthy build. Liveness
135
+ // is a claim about change over time and needs two points to make.
136
+ const loopAlive = samples.length < 2 || !everTicked || ticksObserved >= minTicks;
137
+ // NaN over EVERY sample, not just the last. Judging only the final sample
138
+ // meant a field corrupted during any earlier probe was wiped by the next
139
+ // page reload and never seen — the check silently became "did the LAST probe
140
+ // produce NaN".
141
+ const nanSet = new Set();
142
+ for (const s of samples) {
143
+ for (const [key, v] of Object.entries(s.values)) {
144
+ if (typeof v === 'number' && Number.isNaN(v))
145
+ nanSet.add(key);
146
+ }
147
+ }
148
+ const nanFields = [...nanSet];
149
+ // Growth compared only WITHIN a contiguous segment: across a reload the
150
+ // start and end points belong to different runs of the artifact, so the
151
+ // comparison is meaningless (and a real leak measured over one short probe
152
+ // never crosses the floor).
153
+ const growth = new Map();
154
+ const bySegment = new Map();
155
+ for (const s of samples) {
156
+ const seg = s.segment ?? 0;
157
+ if (!bySegment.has(seg))
158
+ bySegment.set(seg, []);
159
+ bySegment.get(seg).push(s);
160
+ }
161
+ for (const group of bySegment.values()) {
162
+ const a = group[0];
163
+ const b = group[group.length - 1];
164
+ if (!a || !b || a === b)
165
+ continue;
166
+ for (const key of Object.keys(b.values)) {
167
+ // Growth means "a COLLECTION grew without bound". An ordinary counter —
168
+ // a score, a frame number, elapsed time — is supposed to climb, and
169
+ // flagging it reports a leak in a working game (observed: score 0 →
170
+ // 38 060 reported as unbounded growth). Only size-like observations
171
+ // qualify; every watched value is still checked for NaN.
172
+ if (!isCollectionSize(key))
173
+ continue;
174
+ const start = a.values[key];
175
+ const end = b.values[key];
176
+ if (typeof start === 'number' &&
177
+ typeof end === 'number' &&
178
+ !Number.isNaN(end) &&
179
+ end > growthFloor &&
180
+ end > Math.max(1, start) * growthFactor) {
181
+ growth.set(key, `${key}: ${start} → ${end}`);
182
+ }
183
+ }
184
+ }
185
+ const unboundedGrowth = [...growth.values()];
186
+ return { errors, loopAlive, ticksObserved, nanFields, unboundedGrowth };
187
+ }
188
+ /** True when the watchdog saw something that should fail the gate. */
189
+ export function watchdogFailed(r) {
190
+ return r.errors.length > 0 || !r.loopAlive || r.nanFields.length > 0 || r.unboundedGrowth.length > 0;
191
+ }
192
+ export function watchdogSummary(r) {
193
+ const lines = [];
194
+ if (r.errors.length > 0) {
195
+ lines.push(`✗ runtime errors (${r.errors.length}): ${r.errors.slice(0, 5).join(' | ')}`);
196
+ }
197
+ if (!r.loopAlive) {
198
+ lines.push(`✗ the main loop STOPPED — ${r.ticksObserved} frame(s) in the final sampling window. ` +
199
+ `An uncaught error inside a requestAnimationFrame callback stops the loop being ` +
200
+ `rescheduled; every later frame is a frozen copy of the last good one, which looks ` +
201
+ `identical to a working paused screen in a screenshot.`);
202
+ }
203
+ if (r.nanFields.length > 0)
204
+ lines.push(`✗ NaN in tracked state: ${r.nanFields.join(', ')}`);
205
+ if (r.unboundedGrowth.length > 0) {
206
+ lines.push(`✗ unbounded growth (leak): ${r.unboundedGrowth.join(', ')}`);
207
+ }
208
+ return lines.join('\n');
209
+ }
210
+ //# sourceMappingURL=watchdog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watchdog.js","sourceRoot":"","sources":["../../../src/delivery/interaction/watchdog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,YAAY,CAAC;AAEjD;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB,CAAC,aAAqB,oBAAoB;IAC/E,OAAO,oBAAoB,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;KAuB/B,CAAC;AAEN;;;;GAIG;AACH;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC;AAE1C,MAAM,UAAU,oBAAoB,CAClC,UAAoB,EACpB,aAAqB,oBAAoB;IAEzC,MAAM,OAAO,GAAG,UAAU;SACvB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC9B,OAAO,sBAAsB,CAAC,WAAW,GAAG,4CAA4C,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,4BAA4B,GAAG,aAAa,CAAC;IACnK,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,OAAO;mBACU,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;;EAE3C,OAAO;;GAEN,CAAC;AACJ,CAAC;AAcD,MAAM,UAAU,mBAAmB,CAAC,GAAY;IAC9C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAmB,CAAC;QAClD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACvD,MAAM,MAAM,GAAkC,EAAE,CAAC;QACjD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;YACzD,MAAM,CAAC,CAAC,CAAC,GAAI,CAAa,KAAK,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAE,CAAmB,CAAC;QAClF,CAAC;QACD,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;YAChC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;YACrE,MAAM;SACP,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5E,CAAC;AAWD;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAyB,EACzB,eAAyB,EAAE,EAC3B,OAAwB,EAAE;IAE1B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;IACpC,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,GAAG,CAAC;IAE5C,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IAEpF,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACzB,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACvE,2EAA2E;IAC3E,4EAA4E;IAC5E,wBAAwB;IACxB,MAAM,aAAa,GAAG,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,6EAA6E;IAC7E,mEAAmE;IACnE,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC1C,oEAAoE;IACpE,0EAA0E;IAC1E,8EAA8E;IAC9E,kEAAkE;IAClE,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,aAAa,IAAI,QAAQ,CAAC;IAEjF,0EAA0E;IAC1E,yEAAyE;IACzE,6EAA6E;IAC7E,gBAAgB;IAChB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;YAChD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IACD,MAAM,SAAS,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;IAE9B,wEAAwE;IACxE,wEAAwE;IACxE,2EAA2E;IAC3E,4BAA4B;IAC5B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,GAAG,EAA4B,CAAC;IACtD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC;QAC3B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC/C,SAAS,CAAC,GAAG,CAAC,GAAG,CAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACnD,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACnB,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAClC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,SAAS;QAClC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;YACxC,wEAAwE;YACxE,oEAAoE;YACpE,oEAAoE;YACpE,oEAAoE;YACpE,yDAAyD;YACzD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC;gBAAE,SAAS;YACrC,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC5B,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC1B,IACE,OAAO,KAAK,KAAK,QAAQ;gBACzB,OAAO,GAAG,KAAK,QAAQ;gBACvB,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;gBAClB,GAAG,GAAG,WAAW;gBACjB,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,YAAY,EACvC,CAAC;gBACD,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,eAAe,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAE7C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC;AAC1E,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,cAAc,CAAC,CAAiB;IAC9C,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC;AACvG,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,CAAiB;IAC/C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,MAAM,CAAC,MAAM,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CACR,6BAA6B,CAAC,CAAC,aAAa,0CAA0C;YACpF,iFAAiF;YACjF,oFAAoF;YACpF,uDAAuD,CAC1D,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5F,IAAI,CAAC,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Web driver — drives a real headless browser with real pointer and key input.
3
+ *
4
+ * Deliberately uses coordinate-level mouse control rather than selector clicks:
5
+ * inside a <canvas> there are no elements to target, so aiming and firing only
6
+ * exist as pointer positions. A gate that can only click selectors cannot play a
7
+ * game, and "cannot play it" is indistinguishable from "it works".
8
+ */
9
+ import { type InteractionDriver, type ReadResult } from './driver.js';
10
+ import type { Step } from './types.js';
11
+ /** The subset of WebBrowser the driver needs — keeps tests free of a browser. */
12
+ export interface PointerBrowser {
13
+ launch(opts?: Record<string, unknown>): Promise<unknown>;
14
+ goto(url: string): Promise<string>;
15
+ waitForLoadState(state?: 'load' | 'domcontentloaded' | 'networkidle'): Promise<void>;
16
+ addInitScript(script: string): Promise<void>;
17
+ evaluate<T>(script: string): Promise<T>;
18
+ screenshot(path: string): Promise<void>;
19
+ getErrors(): Array<{
20
+ kind: string;
21
+ message: string;
22
+ }>;
23
+ mouseMove(x: number, y: number): Promise<void>;
24
+ mouseDown(): Promise<void>;
25
+ mouseUp(): Promise<void>;
26
+ mouseClick(x: number, y: number): Promise<void>;
27
+ press(key: string): Promise<void>;
28
+ viewportSize(): Promise<{
29
+ width: number;
30
+ height: number;
31
+ }>;
32
+ close(): Promise<void>;
33
+ }
34
+ export interface WebDriverOptions {
35
+ projectRoot: string;
36
+ /** Entry html path relative to the served root (default index.html). */
37
+ entry?: string;
38
+ /** Directory that becomes the server root (default: the project root). */
39
+ webRoot?: string;
40
+ browserFactory?: () => Promise<PointerBrowser>;
41
+ serverFactory?: (dir: string, entry: string) => Promise<{
42
+ url: string;
43
+ close: () => void;
44
+ }>;
45
+ }
46
+ export declare class WebInteractionDriver implements InteractionDriver {
47
+ private browser;
48
+ private server;
49
+ private readonly opts;
50
+ /** Last pointer position, so `down`/`up` act where the pointer actually is. */
51
+ private pointer;
52
+ private launched;
53
+ /** Per-run instrumentation global — see buildWatchdogInitScript. */
54
+ private readonly watchGlobal;
55
+ constructor(opts: WebDriverOptions);
56
+ start(): Promise<void>;
57
+ /**
58
+ * Reload the entry page. Init scripts are registered on the CONTEXT, so the
59
+ * watchdog re-installs automatically on the fresh document.
60
+ */
61
+ reset(): Promise<void>;
62
+ /** True once the browser is up — distinguishes infra failure from load failure. */
63
+ didLaunch(): boolean;
64
+ /** A probe may only navigate within the artifact's own served origin. */
65
+ private sameOrigin;
66
+ runStep(step: Step): Promise<void>;
67
+ /**
68
+ * Evaluate an observation. Wrapped in a function so `const`/`let` declared at
69
+ * the top level of a classic script — which live in the global LEXICAL scope
70
+ * and are NOT properties of `window` — are still readable. Reading these
71
+ * through `window.X` returns undefined, which silently turns every assertion
72
+ * into "undefined", so the wrapper matters.
73
+ */
74
+ read(expr: string): Promise<unknown>;
75
+ /**
76
+ * `ok: false` means the expression THREW or named something undefined — a
77
+ * broken probe, reported as such rather than as a behavioural failure.
78
+ * `undefined` reached by a resolving expression stays `ok: true`.
79
+ */
80
+ readDetailed(expr: string): Promise<ReadResult>;
81
+ inject(expr: string): Promise<void>;
82
+ /**
83
+ * GATING errors only: uncaught page exceptions.
84
+ *
85
+ * `console.error` output and failed sub-resource requests are common in
86
+ * working apps — a missing decorative asset or a handled-and-logged warning
87
+ * would otherwise hard-fail the behavioural gate. Both sibling gates already
88
+ * refuse to gate on them (execution-gate treats them as advisory;
89
+ * visual-gate filters to `pageerror`), and a gate that false-fails working
90
+ * builds gets switched off.
91
+ */
92
+ errors(): string[];
93
+ /** Sample the watchdog under this run's private global name. */
94
+ watchdogSample(watchExprs: string[]): Promise<unknown>;
95
+ /** Non-gating observations, surfaced in the report as context. */
96
+ advisoryErrors(): string[];
97
+ capture(path: string): Promise<void>;
98
+ stop(): Promise<void>;
99
+ }
100
+ /** Where probe evidence (screenshots) is written. */
101
+ export declare function evidenceDir(projectRoot: string): string;
102
+ //# sourceMappingURL=web-driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-driver.d.ts","sourceRoot":"","sources":["../../../src/delivery/interaction/web-driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Web driver — drives a real headless browser with real pointer and key input.
3
+ *
4
+ * Deliberately uses coordinate-level mouse control rather than selector clicks:
5
+ * inside a <canvas> there are no elements to target, so aiming and firing only
6
+ * exist as pointer positions. A gate that can only click selectors cannot play a
7
+ * game, and "cannot play it" is indistinguishable from "it works".
8
+ */
9
+ import { join } from 'node:path';
10
+ import { startStaticServer } from '../execution-gate.js';
11
+ import { buildWatchdogInitScript, watchdogSampleScript } from './watchdog.js';
12
+ import { delay } from './driver.js';
13
+ async function loadRealBrowser() {
14
+ const mod = await import('../../browser/web-browser.js');
15
+ return new mod.WebBrowser();
16
+ }
17
+ export class WebInteractionDriver {
18
+ browser = null;
19
+ server = null;
20
+ opts;
21
+ /** Last pointer position, so `down`/`up` act where the pointer actually is. */
22
+ pointer = { x: 0, y: 0 };
23
+ launched = false;
24
+ /** Per-run instrumentation global — see buildWatchdogInitScript. */
25
+ watchGlobal = `__uapWatch_${Math.random().toString(36).slice(2, 10)}`;
26
+ constructor(opts) {
27
+ this.opts = opts;
28
+ }
29
+ async start() {
30
+ const entry = this.opts.entry ?? 'index.html';
31
+ const root = this.opts.webRoot ?? this.opts.projectRoot;
32
+ const startServer = this.opts.serverFactory ?? startStaticServer;
33
+ this.server = await startServer(root, entry);
34
+ this.browser = this.opts.browserFactory
35
+ ? await this.opts.browserFactory()
36
+ : await loadRealBrowser();
37
+ await this.browser.launch({ headless: true });
38
+ // MUST precede goto: the watchdog wraps requestAnimationFrame before the
39
+ // page's own scripts capture a reference to it.
40
+ await this.browser.addInitScript(buildWatchdogInitScript(this.watchGlobal));
41
+ // Everything above is INFRASTRUCTURE — if it fails the gate has no opinion
42
+ // about the artifact. Everything below is the ARTIFACT failing to load,
43
+ // which is a real defect and must not be laundered into "no browser
44
+ // available, skipping".
45
+ this.launched = true;
46
+ await this.browser.goto(this.server.url);
47
+ await this.browser.waitForLoadState('load');
48
+ const size = await this.browser.viewportSize();
49
+ this.pointer = { x: Math.round(size.width / 2), y: Math.round(size.height / 2) };
50
+ }
51
+ /**
52
+ * Reload the entry page. Init scripts are registered on the CONTEXT, so the
53
+ * watchdog re-installs automatically on the fresh document.
54
+ */
55
+ async reset() {
56
+ const b = this.browser;
57
+ if (!b || !this.server)
58
+ throw new Error('cannot reset: driver not started');
59
+ // Release a button a previous probe left held: Playwright's mouse state is
60
+ // browser-level and survives navigation, so a probe ending on `down`
61
+ // otherwise keeps firing into the next one.
62
+ try {
63
+ await b.mouseUp();
64
+ }
65
+ catch {
66
+ /* nothing was held */
67
+ }
68
+ await b.goto(this.server.url);
69
+ await b.waitForLoadState('load');
70
+ // Persisted storage survives a reload, so a save-state or "seen the
71
+ // tutorial" flag would carry the previous probe's writes across — exactly
72
+ // the leak the reload is meant to close.
73
+ try {
74
+ await b.evaluate('(function(){try{localStorage.clear();sessionStorage.clear();}catch(e){}return true;})');
75
+ }
76
+ catch {
77
+ /* storage may be unavailable (file://, blocked) */
78
+ }
79
+ const size = await b.viewportSize();
80
+ const cx = Math.round(size.width / 2);
81
+ const cy = Math.round(size.height / 2);
82
+ this.pointer = { x: cx, y: cy };
83
+ // Move the REAL cursor too — otherwise the driver believes the pointer is
84
+ // centred while it sits wherever the last probe left it, and coordinate-free
85
+ // `down`/`up` fire at the wrong place.
86
+ await b.mouseMove(cx, cy);
87
+ }
88
+ /** True once the browser is up — distinguishes infra failure from load failure. */
89
+ didLaunch() {
90
+ return this.launched;
91
+ }
92
+ /** A probe may only navigate within the artifact's own served origin. */
93
+ sameOrigin(url) {
94
+ if (!this.server)
95
+ return false;
96
+ try {
97
+ return new URL(url, this.server.url).origin === new URL(this.server.url).origin;
98
+ }
99
+ catch {
100
+ return false;
101
+ }
102
+ }
103
+ async runStep(step) {
104
+ const b = this.browser;
105
+ if (!b)
106
+ throw new Error('web driver not started');
107
+ switch (step.do) {
108
+ case 'goto':
109
+ if (step.url) {
110
+ // SAME-ORIGIN ONLY. The manifest is model-authored, and an unrestricted
111
+ // goto lets it navigate to `file:///…/.uap/proxy.env` or an attacker
112
+ // host — after which every `read` expression executes in THAT origin
113
+ // and its values flow out through the verdict feedback.
114
+ if (!this.sameOrigin(step.url)) {
115
+ throw new Error(`probe tried to navigate off-origin (${step.url}); interaction probes may only drive the artifact under test`);
116
+ }
117
+ await b.goto(step.url);
118
+ await b.waitForLoadState('load');
119
+ }
120
+ return;
121
+ case 'wait':
122
+ await delay(step.ms);
123
+ return;
124
+ case 'move':
125
+ this.pointer = { x: step.x, y: step.y };
126
+ await b.mouseMove(step.x, step.y);
127
+ return;
128
+ case 'down':
129
+ await b.mouseDown();
130
+ return;
131
+ case 'up':
132
+ await b.mouseUp();
133
+ return;
134
+ case 'click':
135
+ if (typeof step.x === 'number' && typeof step.y === 'number') {
136
+ this.pointer = { x: step.x, y: step.y };
137
+ await b.mouseClick(step.x, step.y);
138
+ }
139
+ else if (step.selector) {
140
+ // Selector clicks stay available for DOM UIs (menus, forms).
141
+ await b.evaluate(`(function(){var el=document.querySelector(${JSON.stringify(step.selector)});if(el)el.click();return true;})`);
142
+ }
143
+ else {
144
+ await b.mouseClick(this.pointer.x, this.pointer.y);
145
+ }
146
+ return;
147
+ case 'aimAt': {
148
+ // Read-only: the same evaluation path as any observation, so the
149
+ // mutation guard covers it.
150
+ const at = await this.read(step.expr);
151
+ let x;
152
+ let y;
153
+ if (typeof at === 'number')
154
+ x = at;
155
+ else if (at && typeof at === 'object') {
156
+ const o = at;
157
+ if (typeof o.x === 'number')
158
+ x = o.x;
159
+ if (typeof o.y === 'number')
160
+ y = o.y;
161
+ }
162
+ // An expression that resolves to nothing (no target on screen) leaves
163
+ // the pointer where it is rather than throwing — "nothing to aim at"
164
+ // is a normal moment in play, not a probe failure.
165
+ if (typeof x !== 'number' || !Number.isFinite(x))
166
+ return;
167
+ const ty = typeof step.y === 'number' ? step.y : (y ?? this.pointer.y);
168
+ this.pointer = { x, y: ty };
169
+ await b.mouseMove(x, ty);
170
+ return;
171
+ }
172
+ case 'key':
173
+ await b.press(step.key);
174
+ return;
175
+ case 'eval':
176
+ await this.read(step.expr);
177
+ return;
178
+ case 'inject':
179
+ await this.inject(step.expr);
180
+ return;
181
+ case 'repeat': {
182
+ // The RUNNER expands repeat blocks so every adapter gets them; this is a
183
+ // fallback for callers driving the driver directly (e.g. the operator).
184
+ const times = Math.max(0, Math.min(step.times, 10_000));
185
+ for (let i = 0; i < times; i++) {
186
+ for (const inner of step.steps)
187
+ await this.runStep(inner);
188
+ }
189
+ return;
190
+ }
191
+ default:
192
+ // Unknown step kinds are ignored rather than thrown: a manifest mined by
193
+ // a newer version must not hard-fail an older gate.
194
+ return;
195
+ }
196
+ }
197
+ /**
198
+ * Evaluate an observation. Wrapped in a function so `const`/`let` declared at
199
+ * the top level of a classic script — which live in the global LEXICAL scope
200
+ * and are NOT properties of `window` — are still readable. Reading these
201
+ * through `window.X` returns undefined, which silently turns every assertion
202
+ * into "undefined", so the wrapper matters.
203
+ */
204
+ async read(expr) {
205
+ return (await this.readDetailed(expr)).value;
206
+ }
207
+ /**
208
+ * `ok: false` means the expression THREW or named something undefined — a
209
+ * broken probe, reported as such rather than as a behavioural failure.
210
+ * `undefined` reached by a resolving expression stays `ok: true`.
211
+ */
212
+ async readDetailed(expr) {
213
+ const b = this.browser;
214
+ if (!b)
215
+ throw new Error('web driver not started');
216
+ const script = `(function(){try{var v=(${expr});return JSON.stringify({ok:true,v:v===undefined?null:v,u:v===undefined});}catch(e){return JSON.stringify({ok:false,e:String(e)});}})`;
217
+ const raw = await b.evaluate(script);
218
+ try {
219
+ const parsed = JSON.parse(typeof raw === 'string' ? raw : JSON.stringify(raw));
220
+ if (!parsed.ok)
221
+ return { ok: false, error: parsed.e ?? 'expression threw' };
222
+ // An expression that resolves to `undefined` is almost always a probe
223
+ // naming a member the artifact does not expose (`Particles.particles`
224
+ // against a module that only exports functions), not a real observation.
225
+ if (parsed.u)
226
+ return { ok: false, error: `${expr} is undefined — the artifact does not expose it` };
227
+ return { ok: true, value: parsed.v };
228
+ }
229
+ catch {
230
+ return { ok: false, error: 'observation could not be serialised' };
231
+ }
232
+ }
233
+ async inject(expr) {
234
+ const b = this.browser;
235
+ if (!b)
236
+ throw new Error('web driver not started');
237
+ await b.evaluate(`(function(){try{${expr};}catch(e){}return true;})`);
238
+ }
239
+ /**
240
+ * GATING errors only: uncaught page exceptions.
241
+ *
242
+ * `console.error` output and failed sub-resource requests are common in
243
+ * working apps — a missing decorative asset or a handled-and-logged warning
244
+ * would otherwise hard-fail the behavioural gate. Both sibling gates already
245
+ * refuse to gate on them (execution-gate treats them as advisory;
246
+ * visual-gate filters to `pageerror`), and a gate that false-fails working
247
+ * builds gets switched off.
248
+ */
249
+ errors() {
250
+ try {
251
+ return (this.browser?.getErrors() ?? [])
252
+ .filter((e) => e.kind === 'pageerror')
253
+ .map((e) => `${e.kind}: ${e.message}`);
254
+ }
255
+ catch {
256
+ return [];
257
+ }
258
+ }
259
+ /** Sample the watchdog under this run's private global name. */
260
+ async watchdogSample(watchExprs) {
261
+ return this.read(`(${watchdogSampleScript(watchExprs, this.watchGlobal)})()`);
262
+ }
263
+ /** Non-gating observations, surfaced in the report as context. */
264
+ advisoryErrors() {
265
+ try {
266
+ return (this.browser?.getErrors() ?? [])
267
+ .filter((e) => e.kind !== 'pageerror')
268
+ .map((e) => `${e.kind}: ${e.message}`);
269
+ }
270
+ catch {
271
+ return [];
272
+ }
273
+ }
274
+ async capture(path) {
275
+ try {
276
+ await this.browser?.screenshot(path);
277
+ }
278
+ catch {
279
+ /* evidence capture is best-effort */
280
+ }
281
+ }
282
+ async stop() {
283
+ // Each teardown independently guarded: a browser left alive leaks a Chromium
284
+ // process per gate pass, which has previously held SIGTERM'd deliver runs
285
+ // open for hours.
286
+ try {
287
+ await this.browser?.close();
288
+ }
289
+ catch {
290
+ /* ignore */
291
+ }
292
+ try {
293
+ this.server?.close();
294
+ }
295
+ catch {
296
+ /* ignore */
297
+ }
298
+ this.browser = null;
299
+ this.server = null;
300
+ }
301
+ }
302
+ /** Where probe evidence (screenshots) is written. */
303
+ export function evidenceDir(projectRoot) {
304
+ return join(projectRoot, '.uap', 'interaction', 'evidence');
305
+ }
306
+ //# sourceMappingURL=web-driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-driver.js","sourceRoot":"","sources":["../../../src/delivery/interaction/web-driver.ts"],"names":[],"mappings":"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"}