@miller-tech/uap 1.170.1 → 1.172.1
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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/bin/cli.js +26 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/browser/web-browser.d.ts +17 -0
- package/dist/browser/web-browser.d.ts.map +1 -1
- package/dist/browser/web-browser.js +36 -0
- package/dist/browser/web-browser.js.map +1 -1
- package/dist/cli/interaction.d.ts +29 -0
- package/dist/cli/interaction.d.ts.map +1 -0
- package/dist/cli/interaction.js +217 -0
- package/dist/cli/interaction.js.map +1 -0
- package/dist/cli/verify.d.ts +6 -0
- package/dist/cli/verify.d.ts.map +1 -1
- package/dist/cli/verify.js +112 -7
- package/dist/cli/verify.js.map +1 -1
- package/dist/delivery/execution-gate.d.ts +7 -0
- package/dist/delivery/execution-gate.d.ts.map +1 -1
- package/dist/delivery/execution-gate.js +42 -1
- package/dist/delivery/execution-gate.js.map +1 -1
- package/dist/delivery/fidelity.d.ts +16 -0
- package/dist/delivery/fidelity.d.ts.map +1 -1
- package/dist/delivery/fidelity.js +7 -0
- package/dist/delivery/fidelity.js.map +1 -1
- package/dist/delivery/interaction/driver.d.ts +60 -0
- package/dist/delivery/interaction/driver.d.ts.map +1 -0
- package/dist/delivery/interaction/driver.js +18 -0
- package/dist/delivery/interaction/driver.js.map +1 -0
- package/dist/delivery/interaction/manifest.d.ts +84 -0
- package/dist/delivery/interaction/manifest.d.ts.map +1 -0
- package/dist/delivery/interaction/manifest.js +241 -0
- package/dist/delivery/interaction/manifest.js.map +1 -0
- package/dist/delivery/interaction/mine.d.ts +54 -0
- package/dist/delivery/interaction/mine.d.ts.map +1 -0
- package/dist/delivery/interaction/mine.js +218 -0
- package/dist/delivery/interaction/mine.js.map +1 -0
- package/dist/delivery/interaction/runner.d.ts +61 -0
- package/dist/delivery/interaction/runner.d.ts.map +1 -0
- package/dist/delivery/interaction/runner.js +359 -0
- package/dist/delivery/interaction/runner.js.map +1 -0
- package/dist/delivery/interaction/types.d.ts +223 -0
- package/dist/delivery/interaction/types.d.ts.map +1 -0
- package/dist/delivery/interaction/types.js +19 -0
- package/dist/delivery/interaction/types.js.map +1 -0
- package/dist/delivery/interaction/verdict.d.ts +26 -0
- package/dist/delivery/interaction/verdict.d.ts.map +1 -0
- package/dist/delivery/interaction/verdict.js +139 -0
- package/dist/delivery/interaction/verdict.js.map +1 -0
- package/dist/delivery/interaction/watchdog.d.ts +82 -0
- package/dist/delivery/interaction/watchdog.d.ts.map +1 -0
- package/dist/delivery/interaction/watchdog.js +210 -0
- package/dist/delivery/interaction/watchdog.js.map +1 -0
- package/dist/delivery/interaction/web-driver.d.ts +102 -0
- package/dist/delivery/interaction/web-driver.d.ts.map +1 -0
- package/dist/delivery/interaction/web-driver.js +306 -0
- package/dist/delivery/interaction/web-driver.js.map +1 -0
- package/dist/delivery/interaction-gate.d.ts +45 -0
- package/dist/delivery/interaction-gate.d.ts.map +1 -0
- package/dist/delivery/interaction-gate.js +255 -0
- package/dist/delivery/interaction-gate.js.map +1 -0
- package/dist/delivery/mission-acceptance.d.ts +2 -0
- package/dist/delivery/mission-acceptance.d.ts.map +1 -1
- package/dist/delivery/mission-acceptance.js +23 -0
- package/dist/delivery/mission-acceptance.js.map +1 -1
- package/dist/delivery/vision-judge.d.ts +21 -0
- package/dist/delivery/vision-judge.d.ts.map +1 -1
- package/dist/delivery/vision-judge.js +96 -0
- package/dist/delivery/vision-judge.js.map +1 -1
- package/dist/delivery/visual-gate.js.map +1 -1
- package/dist/types/config.d.ts +20 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +7 -0
- package/dist/types/config.js.map +1 -1
- package/package.json +1 -1
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/src/policies/enforcers/enforcement_self_protect.py +96 -5
- package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
|
@@ -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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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interaction gate — runs the artifact's own promises against real input.
|
|
3
|
+
*
|
|
4
|
+
* Sits between the execution smoke gate and the visual gate:
|
|
5
|
+
*
|
|
6
|
+
* build → execution smoke (does it load) → INTERACTION (does it do what it
|
|
7
|
+
* promised) → visual (does it look right) → acceptance (is anything missing)
|
|
8
|
+
*
|
|
9
|
+
* Before vision, for two reasons. A vision judge grades a FRAME, and a frozen
|
|
10
|
+
* frame is indistinguishable from a working one — so a broken build can collect
|
|
11
|
+
* a passing aesthetic score, and the fix loop then chases palette notes instead
|
|
12
|
+
* of the defect. And the vision pass costs a headless render plus a model call,
|
|
13
|
+
* which is waste on a build that cannot be played.
|
|
14
|
+
*
|
|
15
|
+
* SECURITY NOTE: manifest expression strings are interpolated into script that
|
|
16
|
+
* runs in the driving browser, so the manifest is a code-bearing input. It is
|
|
17
|
+
* kept under `.uap/interaction/` — which the self-protect enforcer denies to the
|
|
18
|
+
* agent — and every expression is checked for mutation before use. Both are load
|
|
19
|
+
* bearing: the write-protection is a security control, not just an integrity one.
|
|
20
|
+
*/
|
|
21
|
+
import type { InteractionDriver } from './interaction/driver.js';
|
|
22
|
+
import type { InteractionManifest, InteractionVerdict, ProbeMode } from './interaction/types.js';
|
|
23
|
+
export interface InteractionGateOptions {
|
|
24
|
+
/** Supply the manifest directly (tests, or a caller that just mined one). */
|
|
25
|
+
manifest?: InteractionManifest;
|
|
26
|
+
/** Which probe modes to run. Default: core only. */
|
|
27
|
+
modes?: ProbeMode[];
|
|
28
|
+
/** Max fidelity: uncovered requirements block. */
|
|
29
|
+
strictCoverage?: boolean;
|
|
30
|
+
/** Injected driver (tests). Default: the real web driver. */
|
|
31
|
+
driverFactory?: (manifest: InteractionManifest) => InteractionDriver;
|
|
32
|
+
/** Overall budget; probes still running when it expires are reported skipped. */
|
|
33
|
+
budgetMs?: number;
|
|
34
|
+
/** Per-probe wall-clock bound. */
|
|
35
|
+
probeTimeoutMs?: number;
|
|
36
|
+
/** Requirements text, so a manifest mined from older requirements is flagged. */
|
|
37
|
+
specText?: string;
|
|
38
|
+
}
|
|
39
|
+
export declare const DEFAULT_BUDGET_MS = 300000;
|
|
40
|
+
/** Evidence path that cannot escape the evidence directory. */
|
|
41
|
+
export declare function evidencePathFor(dir: string, probeId: string): string | null;
|
|
42
|
+
export declare function runInteractionGate(projectRoot: string, options?: InteractionGateOptions): Promise<InteractionVerdict>;
|
|
43
|
+
/** One-line summary for the verify report. */
|
|
44
|
+
export declare function interactionSummary(v: InteractionVerdict): string;
|
|
45
|
+
//# sourceMappingURL=interaction-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interaction-gate.d.ts","sourceRoot":"","sources":["../../src/delivery/interaction-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAeH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,OAAO,KAAK,EACV,mBAAmB,EACnB,kBAAkB,EAClB,SAAS,EAEV,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,sBAAsB;IACrC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,oDAAoD;IACpD,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;IACpB,kDAAkD;IAClD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,mBAAmB,KAAK,iBAAiB,CAAC;IACrE,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,iBAAiB,SAAU,CAAC;AAiBzC,+DAA+D;AAC/D,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI3E;AA8MD,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,kBAAkB,CAAC,CAS7B;AAED,8CAA8C;AAC9C,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAKhE"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interaction gate — runs the artifact's own promises against real input.
|
|
3
|
+
*
|
|
4
|
+
* Sits between the execution smoke gate and the visual gate:
|
|
5
|
+
*
|
|
6
|
+
* build → execution smoke (does it load) → INTERACTION (does it do what it
|
|
7
|
+
* promised) → visual (does it look right) → acceptance (is anything missing)
|
|
8
|
+
*
|
|
9
|
+
* Before vision, for two reasons. A vision judge grades a FRAME, and a frozen
|
|
10
|
+
* frame is indistinguishable from a working one — so a broken build can collect
|
|
11
|
+
* a passing aesthetic score, and the fix loop then chases palette notes instead
|
|
12
|
+
* of the defect. And the vision pass costs a headless render plus a model call,
|
|
13
|
+
* which is waste on a build that cannot be played.
|
|
14
|
+
*
|
|
15
|
+
* SECURITY NOTE: manifest expression strings are interpolated into script that
|
|
16
|
+
* runs in the driving browser, so the manifest is a code-bearing input. It is
|
|
17
|
+
* kept under `.uap/interaction/` — which the self-protect enforcer denies to the
|
|
18
|
+
* agent — and every expression is checked for mutation before use. Both are load
|
|
19
|
+
* bearing: the write-protection is a security control, not just an integrity one.
|
|
20
|
+
*/
|
|
21
|
+
import { mkdirSync, rmSync } from 'node:fs';
|
|
22
|
+
import { resolve, sep } from 'node:path';
|
|
23
|
+
import { detectArtifactType, findWebEntryDir } from './execution-gate.js';
|
|
24
|
+
import { coverageOf, loadManifestDetailed, manifestIsStale } from './interaction/manifest.js';
|
|
25
|
+
import { runProbe } from './interaction/runner.js';
|
|
26
|
+
import { judgeInteraction, skippedVerdict } from './interaction/verdict.js';
|
|
27
|
+
import { judgeWatchdog, parseWatchdogSample, watchdogSampleScript, } from './interaction/watchdog.js';
|
|
28
|
+
import { WebInteractionDriver, evidenceDir } from './interaction/web-driver.js';
|
|
29
|
+
import { delay } from './interaction/driver.js';
|
|
30
|
+
export const DEFAULT_BUDGET_MS = 300_000;
|
|
31
|
+
/** Sample the watchdog without letting a failure there break the run. */
|
|
32
|
+
async function sampleWatchdog(driver, watchExprs) {
|
|
33
|
+
try {
|
|
34
|
+
const raw = driver.watchdogSample
|
|
35
|
+
? await driver.watchdogSample(watchExprs)
|
|
36
|
+
: await driver.read(`(${watchdogSampleScript(watchExprs)})()`);
|
|
37
|
+
return parseWatchdogSample(raw);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Evidence path that cannot escape the evidence directory. */
|
|
44
|
+
export function evidencePathFor(dir, probeId) {
|
|
45
|
+
const candidate = resolve(dir, `${probeId}.png`);
|
|
46
|
+
const root = resolve(dir);
|
|
47
|
+
return candidate === root || candidate.startsWith(root + sep) ? candidate : null;
|
|
48
|
+
}
|
|
49
|
+
async function runGate(projectRoot, options) {
|
|
50
|
+
let manifest = options.manifest;
|
|
51
|
+
if (!manifest) {
|
|
52
|
+
const loaded = loadManifestDetailed(projectRoot);
|
|
53
|
+
if (loaded.status === 'absent') {
|
|
54
|
+
return skippedVerdict('no interaction manifest — run `uap interaction mine` to derive probes from the requirements');
|
|
55
|
+
}
|
|
56
|
+
if (loaded.status === 'invalid') {
|
|
57
|
+
// NOT a skip. An invalid manifest is a tampered or broken acceptance
|
|
58
|
+
// criterion; reporting it as "no manifest" would tell the operator to
|
|
59
|
+
// re-mine and launder the problem away.
|
|
60
|
+
return {
|
|
61
|
+
passed: false,
|
|
62
|
+
skipped: false,
|
|
63
|
+
results: [],
|
|
64
|
+
coverage: { total: 0, covered: 0, uncovered: [] },
|
|
65
|
+
feedback: `interaction gate: the manifest at .uap/interaction/manifest.json is INVALID and was not run:\n` +
|
|
66
|
+
loaded.problems.map((p) => ` · ${p}`).join('\n'),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
manifest = loaded.manifest;
|
|
70
|
+
}
|
|
71
|
+
const modes = options.modes ?? ['core'];
|
|
72
|
+
const probes = manifest.probes.filter((p) => modes.includes(p.mode));
|
|
73
|
+
// Coverage over the probes that will RUN, not the whole manifest.
|
|
74
|
+
const coverage = coverageOf(manifest, probes);
|
|
75
|
+
if (probes.length === 0) {
|
|
76
|
+
return skippedVerdict(`manifest has no probes for mode(s) ${modes.join(', ')}`, coverage);
|
|
77
|
+
}
|
|
78
|
+
// Only the web adapter exists. Silently handing a `cli`/`http` manifest to the
|
|
79
|
+
// web driver would spin up a static server and a browser for an artifact that
|
|
80
|
+
// is neither, and report the resulting mess as an artifact defect.
|
|
81
|
+
if (manifest.kind !== 'web') {
|
|
82
|
+
return skippedVerdict(`no driver for artifact kind '${manifest.kind}' yet — only 'web' is implemented`, coverage);
|
|
83
|
+
}
|
|
84
|
+
const webRoot = findWebEntryDir(projectRoot);
|
|
85
|
+
if (!webRoot && detectArtifactType(projectRoot) !== 'web') {
|
|
86
|
+
return skippedVerdict('manifest declares a web artifact but no web entry point was found', coverage);
|
|
87
|
+
}
|
|
88
|
+
const evidence = evidenceDir(projectRoot);
|
|
89
|
+
try {
|
|
90
|
+
// Clear stale evidence so screenshots always describe THIS run — leftovers
|
|
91
|
+
// from probes that no longer exist read as current evidence.
|
|
92
|
+
rmSync(evidence, { recursive: true, force: true });
|
|
93
|
+
mkdirSync(evidence, { recursive: true });
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
/* evidence is best-effort */
|
|
97
|
+
}
|
|
98
|
+
const driver = options.driverFactory?.(manifest) ??
|
|
99
|
+
new WebInteractionDriver({
|
|
100
|
+
projectRoot,
|
|
101
|
+
entry: manifest.entry,
|
|
102
|
+
...(webRoot ? { webRoot } : {}),
|
|
103
|
+
});
|
|
104
|
+
const budgetMs = options.budgetMs ?? DEFAULT_BUDGET_MS;
|
|
105
|
+
const startedAt = Date.now();
|
|
106
|
+
const results = [];
|
|
107
|
+
const samples = [];
|
|
108
|
+
const watchExprs = manifest.watch ?? [];
|
|
109
|
+
let ranAny = false;
|
|
110
|
+
let segment = 0;
|
|
111
|
+
const resetProblems = [];
|
|
112
|
+
try {
|
|
113
|
+
try {
|
|
114
|
+
await driver.start();
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
const probe = driver;
|
|
118
|
+
const launched = typeof probe.didLaunch === 'function' && probe.didLaunch();
|
|
119
|
+
if (!launched) {
|
|
120
|
+
// Infrastructure: no browser, no server. The gate has no opinion.
|
|
121
|
+
return skippedVerdict(`driver unavailable (${String(e).slice(0, 160)})`, coverage);
|
|
122
|
+
}
|
|
123
|
+
// The browser came up and the ARTIFACT failed to load. That is a defect.
|
|
124
|
+
return {
|
|
125
|
+
passed: false,
|
|
126
|
+
skipped: false,
|
|
127
|
+
results: [],
|
|
128
|
+
coverage,
|
|
129
|
+
feedback: `interaction gate: the artifact failed to load in a real browser — ` +
|
|
130
|
+
`${String(e).slice(0, 200)}\n` +
|
|
131
|
+
`Nothing could be driven, so no behaviour was verified. Check the entry ` +
|
|
132
|
+
`path (${manifest.entry}) resolves and the page reaches 'load'.`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const first = await sampleWatchdog(driver, watchExprs);
|
|
136
|
+
if (first)
|
|
137
|
+
samples.push({ ...first, segment });
|
|
138
|
+
for (const probe of probes) {
|
|
139
|
+
if (Date.now() - startedAt > budgetMs) {
|
|
140
|
+
results.push({
|
|
141
|
+
probeId: probe.id,
|
|
142
|
+
description: probe.description,
|
|
143
|
+
mode: probe.mode,
|
|
144
|
+
requirementIds: probe.requirementIds ?? [],
|
|
145
|
+
passed: false,
|
|
146
|
+
skipped: true,
|
|
147
|
+
skipReason: `interaction budget of ${budgetMs}ms exhausted before this probe ran`,
|
|
148
|
+
assertions: [],
|
|
149
|
+
errors: [],
|
|
150
|
+
durationMs: 0,
|
|
151
|
+
});
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
// Fresh page per probe: shared state makes results order-dependent and
|
|
155
|
+
// reports one probe's damage as the next probe's defect.
|
|
156
|
+
if (ranAny) {
|
|
157
|
+
if (driver.reset) {
|
|
158
|
+
try {
|
|
159
|
+
await driver.reset();
|
|
160
|
+
segment++;
|
|
161
|
+
}
|
|
162
|
+
catch (e) {
|
|
163
|
+
// Silence here would read exactly like a properly isolated run,
|
|
164
|
+
// while every later probe inherits the previous probe's state.
|
|
165
|
+
resetProblems.push(`before ${probe.id}: ${String(e).slice(0, 120)}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
resetProblems.push(`before ${probe.id}: this driver cannot reset between probes`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
ranAny = true;
|
|
173
|
+
const evPath = evidencePathFor(evidence, probe.id);
|
|
174
|
+
results.push(await runProbe(driver, probe, {
|
|
175
|
+
...(evPath ? { evidencePath: evPath } : {}),
|
|
176
|
+
// Leave room inside the overall budget so a single probe cannot eat it.
|
|
177
|
+
...(options.probeTimeoutMs ? { timeoutMs: options.probeTimeoutMs } : {}),
|
|
178
|
+
}));
|
|
179
|
+
const s = await sampleWatchdog(driver, watchExprs);
|
|
180
|
+
if (s)
|
|
181
|
+
samples.push({ ...s, segment });
|
|
182
|
+
}
|
|
183
|
+
// A final short window so "is the loop still ticking NOW" is answered by
|
|
184
|
+
// fresh frames rather than a cumulative count from earlier in the run.
|
|
185
|
+
await delay(600);
|
|
186
|
+
const last = await sampleWatchdog(driver, watchExprs);
|
|
187
|
+
if (last)
|
|
188
|
+
samples.push({ ...last, segment });
|
|
189
|
+
let driverErrors = [];
|
|
190
|
+
try {
|
|
191
|
+
driverErrors = driver.errors();
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
/* an unreadable error channel must not abort the verdict */
|
|
195
|
+
}
|
|
196
|
+
const watchdog = samples.length > 0 ? judgeWatchdog(samples, driverErrors) : undefined;
|
|
197
|
+
// Coverage from the probes that actually RAN — a probe skipped for budget
|
|
198
|
+
// exhaustion must not mark its requirement covered.
|
|
199
|
+
const ranIds = new Set(results.filter((r) => !r.skipped).map((r) => r.probeId));
|
|
200
|
+
const ranCoverage = coverageOf(manifest, probes.filter((p) => ranIds.has(p.id)));
|
|
201
|
+
// Say WHICH artifact was driven. `findWebEntry` prefers the shallowest
|
|
202
|
+
// entry point, so a stray index.html left at the project root silently
|
|
203
|
+
// shadows the real deliverable in a subdirectory — the gate then reports
|
|
204
|
+
// confident failures about a page the author never meant to ship. Observed
|
|
205
|
+
// live: a runaway agent wrote a second copy of a game at the project root
|
|
206
|
+
// and every probe failed against it while the real build was fine.
|
|
207
|
+
const verdict = judgeInteraction(manifest, results, ranCoverage, watchdog, {
|
|
208
|
+
...(options.strictCoverage ? { strictCoverage: true } : {}),
|
|
209
|
+
});
|
|
210
|
+
verdict.feedback += `\nserved: ${webRoot ?? projectRoot}/${manifest.entry}`;
|
|
211
|
+
if (resetProblems.length > 0) {
|
|
212
|
+
verdict.feedback +=
|
|
213
|
+
`\n⚠ ${resetProblems.length} probe(s) ran WITHOUT a clean reset, so they inherited the ` +
|
|
214
|
+
`previous probe's state and their results may be order-dependent:\n ` +
|
|
215
|
+
resetProblems.slice(0, 5).join('\n ');
|
|
216
|
+
}
|
|
217
|
+
if (options.specText && manifestIsStale(manifest, options.specText)) {
|
|
218
|
+
verdict.feedback +=
|
|
219
|
+
`\n⚠ these probes were mined from DIFFERENT requirements than the current ones — ` +
|
|
220
|
+
`re-run \`uap interaction mine\`. A pass here is evidence about the old requirements.`;
|
|
221
|
+
}
|
|
222
|
+
return verdict;
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
// Guarded: a rejecting stop() in a bare `finally` REPLACES the computed
|
|
226
|
+
// verdict with a throw, which would escape runVerify and destroy its
|
|
227
|
+
// documented exit-code contract.
|
|
228
|
+
try {
|
|
229
|
+
await driver.stop();
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
/* teardown failure must not lose the verdict */
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
export async function runInteractionGate(projectRoot, options = {}) {
|
|
237
|
+
try {
|
|
238
|
+
return await runGate(projectRoot, options);
|
|
239
|
+
}
|
|
240
|
+
catch (e) {
|
|
241
|
+
// Never let this gate crash `uap verify`. The visual gate degrades the same
|
|
242
|
+
// way; an exception here would bypass the 0/1/3 exit contract the Stop hook
|
|
243
|
+
// depends on and abort the session with a stack trace.
|
|
244
|
+
return skippedVerdict(`interaction gate error: ${String(e).slice(0, 200)}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/** One-line summary for the verify report. */
|
|
248
|
+
export function interactionSummary(v) {
|
|
249
|
+
if (v.skipped)
|
|
250
|
+
return `interaction gate: SKIPPED (${v.skipReason ?? 'no reason'})`;
|
|
251
|
+
const ran = v.results.filter((r) => !r.skipped).length;
|
|
252
|
+
const ok = v.results.filter((r) => !r.skipped && r.passed).length;
|
|
253
|
+
return `interaction gate: ${v.passed ? 'PASS' : 'FAIL'} — ${ok}/${ran} probe(s), requirements covered ${v.coverage.covered}/${v.coverage.total}`;
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=interaction-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interaction-gate.js","sourceRoot":"","sources":["../../src/delivery/interaction-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC9F,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,oBAAoB,GAErB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAEhF,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAyBhD,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC;AAEzC,yEAAyE;AACzE,KAAK,UAAU,cAAc,CAC3B,MAAyB,EACzB,UAAoB;IAEpB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,CAAC,cAAc;YAC/B,CAAC,CAAC,MAAM,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC;YACzC,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,oBAAoB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACjE,OAAO,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,OAAe;IAC1D,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,OAAO,MAAM,CAAC,CAAC;IACjD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AACnF,CAAC;AAED,KAAK,UAAU,OAAO,CACpB,WAAmB,EACnB,OAA+B;IAE/B,IAAI,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAChC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,MAAM,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;QACjD,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,OAAO,cAAc,CACnB,6FAA6F,CAC9F,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAChC,qEAAqE;YACrE,sEAAsE;YACtE,wCAAwC;YACxC,OAAO;gBACL,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;gBACjD,QAAQ,EACN,gGAAgG;oBAChG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;aACpD,CAAC;QACJ,CAAC;QACD,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC7B,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAK,CAAC,MAAM,CAAiB,CAAC;IACzD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACrE,kEAAkE;IAClE,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC9C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,cAAc,CAAC,sCAAsC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC5F,CAAC;IAED,+EAA+E;IAC/E,8EAA8E;IAC9E,mEAAmE;IACnE,IAAI,QAAQ,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC5B,OAAO,cAAc,CACnB,gCAAgC,QAAQ,CAAC,IAAI,mCAAmC,EAChF,QAAQ,CACT,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAC7C,IAAI,CAAC,OAAO,IAAI,kBAAkB,CAAC,WAAW,CAAC,KAAK,KAAK,EAAE,CAAC;QAC1D,OAAO,cAAc,CAAC,mEAAmE,EAAE,QAAQ,CAAC,CAAC;IACvG,CAAC;IAED,MAAM,QAAQ,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IAC1C,IAAI,CAAC;QACH,2EAA2E;QAC3E,6DAA6D;QAC7D,MAAM,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,6BAA6B;IAC/B,CAAC;IAED,MAAM,MAAM,GACV,OAAO,CAAC,aAAa,EAAE,CAAC,QAAQ,CAAC;QACjC,IAAI,oBAAoB,CAAC;YACvB,WAAW;YACX,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChC,CAAC,CAAC;IAEL,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,iBAAiB,CAAC;IACvD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,MAAM,OAAO,GAAqB,EAAE,CAAC;IACrC,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC;IACxC,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,aAAa,GAAa,EAAE,CAAC;IAEnC,IAAI,CAAC;QACH,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,KAAK,GAAG,MAAkD,CAAC;YACjE,MAAM,QAAQ,GAAG,OAAO,KAAK,CAAC,SAAS,KAAK,UAAU,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YAC5E,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,kEAAkE;gBAClE,OAAO,cAAc,CAAC,uBAAuB,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YACrF,CAAC;YACD,yEAAyE;YACzE,OAAO;gBACL,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,EAAE;gBACX,QAAQ;gBACR,QAAQ,EACN,oEAAoE;oBACpE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI;oBAC9B,yEAAyE;oBACzE,SAAS,QAAQ,CAAC,KAAK,yCAAyC;aACnE,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACvD,IAAI,KAAK;YAAE,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAE/C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,QAAQ,EAAE,CAAC;gBACtC,OAAO,CAAC,IAAI,CAAC;oBACX,OAAO,EAAE,KAAK,CAAC,EAAE;oBACjB,WAAW,EAAE,KAAK,CAAC,WAAW;oBAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,EAAE;oBAC1C,MAAM,EAAE,KAAK;oBACb,OAAO,EAAE,IAAI;oBACb,UAAU,EAAE,yBAAyB,QAAQ,oCAAoC;oBACjF,UAAU,EAAE,EAAE;oBACd,MAAM,EAAE,EAAE;oBACV,UAAU,EAAE,CAAC;iBACd,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YACD,uEAAuE;YACvE,yDAAyD;YACzD,IAAI,MAAM,EAAE,CAAC;gBACX,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;oBACjB,IAAI,CAAC;wBACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;wBACrB,OAAO,EAAE,CAAC;oBACZ,CAAC;oBAAC,OAAO,CAAC,EAAE,CAAC;wBACX,gEAAgE;wBAChE,+DAA+D;wBAC/D,aAAa,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;oBACvE,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,aAAa,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,EAAE,2CAA2C,CAAC,CAAC;gBACpF,CAAC;YACH,CAAC;YACD,MAAM,GAAG,IAAI,CAAC;YACd,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;YACnD,OAAO,CAAC,IAAI,CACV,MAAM,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE;gBAC5B,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3C,wEAAwE;gBACxE,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACzE,CAAC,CACH,CAAC;YACF,MAAM,CAAC,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;YACnD,IAAI,CAAC;gBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,yEAAyE;QACzE,uEAAuE;QACvE,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACtD,IAAI,IAAI;YAAE,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAE7C,IAAI,YAAY,GAAa,EAAE,CAAC;QAChC,IAAI,CAAC;YACH,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,4DAA4D;QAC9D,CAAC;QACD,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACvF,0EAA0E;QAC1E,oDAAoD;QACpD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAChF,MAAM,WAAW,GAAG,UAAU,CAC5B,QAAQ,EACR,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CACvC,CAAC;QACF,uEAAuE;QACvE,uEAAuE;QACvE,yEAAyE;QACzE,2EAA2E;QAC3E,0EAA0E;QAC1E,mEAAmE;QACnE,MAAM,OAAO,GAAG,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE;YACzE,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5D,CAAC,CAAC;QACH,OAAO,CAAC,QAAQ,IAAI,aAAa,OAAO,IAAI,WAAW,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAC5E,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,QAAQ;gBACd,OAAO,aAAa,CAAC,MAAM,6DAA6D;oBACxF,sEAAsE;oBACtE,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3C,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,IAAI,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpE,OAAO,CAAC,QAAQ;gBACd,kFAAkF;oBAClF,sFAAsF,CAAC;QAC3F,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;YAAS,CAAC;QACT,wEAAwE;QACxE,qEAAqE;QACrE,iCAAiC;QACjC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACtB,CAAC;QAAC,MAAM,CAAC;YACP,gDAAgD;QAClD,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,UAAkC,EAAE;IAEpC,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,4EAA4E;QAC5E,4EAA4E;QAC5E,uDAAuD;QACvD,OAAO,cAAc,CAAC,2BAA2B,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,kBAAkB,CAAC,CAAqB;IACtD,IAAI,CAAC,CAAC,OAAO;QAAE,OAAO,8BAA8B,CAAC,CAAC,UAAU,IAAI,WAAW,GAAG,CAAC;IACnF,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IACvD,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IAClE,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,MAAM,EAAE,IAAI,GAAG,mCAAmC,CAAC,CAAC,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;AACnJ,CAAC"}
|
|
@@ -20,6 +20,7 @@ import type { AcceptanceGate, LoopExecutor } from './convergence-loop.js';
|
|
|
20
20
|
import { runAcceptanceGate, type AcceptanceResult } from './acceptance-judge.js';
|
|
21
21
|
import { runExecutionGate } from './execution-gate.js';
|
|
22
22
|
import { runVisualGate } from './visual-gate.js';
|
|
23
|
+
import { runInteractionGate } from './interaction-gate.js';
|
|
23
24
|
import { buildUserPathsNote } from './user-validation.js';
|
|
24
25
|
import type { SpecRegistry } from './spec-registry.js';
|
|
25
26
|
/**
|
|
@@ -53,6 +54,7 @@ export interface MissionAcceptanceDeps {
|
|
|
53
54
|
/** Test seams — default to the real gates. */
|
|
54
55
|
executionGate?: typeof runExecutionGate;
|
|
55
56
|
visualGate?: typeof runVisualGate;
|
|
57
|
+
interactionGate?: typeof runInteractionGate;
|
|
56
58
|
visionReview?: typeof visionAcceptanceFeedback;
|
|
57
59
|
judge?: typeof runAcceptanceGate;
|
|
58
60
|
userPathsNote?: typeof buildUserPathsNote;
|