@tangle-network/browser-agent-driver 0.24.2 → 0.25.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.
- package/dist/cli-preview.d.ts +59 -0
- package/dist/cli-preview.d.ts.map +1 -0
- package/dist/cli-preview.js +144 -0
- package/dist/cli-preview.js.map +1 -0
- package/dist/cli-share.d.ts +77 -0
- package/dist/cli-share.d.ts.map +1 -0
- package/dist/cli-share.js +141 -0
- package/dist/cli-share.js.map +1 -0
- package/dist/cli-ui.d.ts.map +1 -1
- package/dist/cli-ui.js +8 -4
- package/dist/cli-ui.js.map +1 -1
- package/dist/cli.js +153 -5
- package/dist/cli.js.map +1 -1
- package/dist/drivers/cursor-overlay.d.ts +18 -10
- package/dist/drivers/cursor-overlay.d.ts.map +1 -1
- package/dist/drivers/cursor-overlay.js +221 -30
- package/dist/drivers/cursor-overlay.js.map +1 -1
- package/dist/drivers/overlay-label.d.ts +33 -0
- package/dist/drivers/overlay-label.d.ts.map +1 -0
- package/dist/drivers/overlay-label.js +92 -0
- package/dist/drivers/overlay-label.js.map +1 -0
- package/dist/drivers/playwright.d.ts +22 -0
- package/dist/drivers/playwright.d.ts.map +1 -1
- package/dist/drivers/playwright.js +87 -9
- package/dist/drivers/playwright.js.map +1 -1
- package/dist/drivers/snapshot.d.ts +12 -0
- package/dist/drivers/snapshot.d.ts.map +1 -1
- package/dist/drivers/snapshot.js +17 -0
- package/dist/drivers/snapshot.js.map +1 -1
- package/dist/drivers/types.d.ts +8 -0
- package/dist/drivers/types.d.ts.map +1 -1
- package/dist/runner/interrupt-controller.d.ts +67 -0
- package/dist/runner/interrupt-controller.d.ts.map +1 -0
- package/dist/runner/interrupt-controller.js +142 -0
- package/dist/runner/interrupt-controller.js.map +1 -0
- package/dist/runner/overlay-narration.d.ts +83 -0
- package/dist/runner/overlay-narration.d.ts.map +1 -0
- package/dist/runner/overlay-narration.js +172 -0
- package/dist/runner/overlay-narration.js.map +1 -0
- package/dist/runner/runner.d.ts +9 -0
- package/dist/runner/runner.d.ts.map +1 -1
- package/dist/runner/runner.js +46 -0
- package/dist/runner/runner.js.map +1 -1
- package/dist/runner/stream-webhook.d.ts +70 -0
- package/dist/runner/stream-webhook.d.ts.map +1 -0
- package/dist/runner/stream-webhook.js +132 -0
- package/dist/runner/stream-webhook.js.map +1 -0
- package/dist/test-runner.d.ts +7 -0
- package/dist/test-runner.d.ts.map +1 -1
- package/dist/test-runner.js +3 -0
- package/dist/test-runner.js.map +1 -1
- package/dist/viewer/viewer.html +122 -8
- package/package.json +2 -2
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Agent overlay — cursor + reasoning + verdict badges + progress bar.
|
|
3
3
|
*
|
|
4
|
-
* Injected into every page so
|
|
5
|
-
*
|
|
6
|
-
* ring on click, and a highlight box around the target element.
|
|
4
|
+
* Injected into every page so recordings and screenshots show the agent
|
|
5
|
+
* narrating its own work. Five concerns, one DOM widget:
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* 1. Cursor arrow that animates to click targets
|
|
8
|
+
* 2. Highlight box around the target element
|
|
9
|
+
* 3. Single-line action label next to cursor ("click · Search")
|
|
10
|
+
* 4. Reasoning panel (translucent, top-right) with the agent's own
|
|
11
|
+
* words for the current turn — "Clicking Search to submit C-003"
|
|
12
|
+
* 5. Verdict badges (stacked, bottom-left) that fade in when a
|
|
13
|
+
* conclusion is reached — "✓ C-003 PUTIN → POSITIVE MATCH"
|
|
14
|
+
* 6. Progress timeline bar (top edge) — thin strip with a dot per
|
|
15
|
+
* completed action and a highlighted current position
|
|
16
|
+
*
|
|
17
|
+
* All primitives are DOM. They show up in screenshots, video frames, and
|
|
18
|
+
* the replayer without extra plumbing. The overlay exposes a single
|
|
19
|
+
* `window.__bad_overlay` API so the driver can drive everything in one
|
|
20
|
+
* `page.evaluate` round-trip per turn.
|
|
13
21
|
*
|
|
14
22
|
* Toggle via `PlaywrightDriverOptions.showCursor`.
|
|
15
23
|
*/
|
|
@@ -35,19 +43,28 @@ export const CURSOR_OVERLAY_INIT_SCRIPT = `
|
|
|
35
43
|
height: '100vh',
|
|
36
44
|
pointerEvents: 'none',
|
|
37
45
|
zIndex: String(Z),
|
|
46
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
38
47
|
});
|
|
39
48
|
|
|
40
|
-
// ──
|
|
41
|
-
// ── Idle bounce keyframes ──────────────────────────────────────────
|
|
49
|
+
// ── Keyframes ──────────────────────────────────────────────────────────
|
|
42
50
|
const style = document.createElement('style');
|
|
43
51
|
style.textContent = \`
|
|
44
52
|
@keyframes __bad_cursor_bob {
|
|
45
53
|
0%, 100% { margin-top: 0px; }
|
|
46
54
|
50% { margin-top: -3px; }
|
|
47
55
|
}
|
|
56
|
+
@keyframes __bad_badge_in {
|
|
57
|
+
from { transform: translateX(-20px); opacity: 0; }
|
|
58
|
+
to { transform: translateX(0); opacity: 1; }
|
|
59
|
+
}
|
|
60
|
+
@keyframes __bad_badge_out {
|
|
61
|
+
from { transform: translateX(0); opacity: 1; }
|
|
62
|
+
to { transform: translateX(20px); opacity: 0; }
|
|
63
|
+
}
|
|
48
64
|
\`;
|
|
49
65
|
(document.head || document.documentElement).appendChild(style);
|
|
50
66
|
|
|
67
|
+
// ── Cursor sprite (SVG arrow) ──────────────────────────────────────────
|
|
51
68
|
const cursor = document.createElementNS(NS, 'svg');
|
|
52
69
|
cursor.setAttribute('width', '24');
|
|
53
70
|
cursor.setAttribute('height', '24');
|
|
@@ -111,7 +128,6 @@ export const CURSOR_OVERLAY_INIT_SCRIPT = `
|
|
|
111
128
|
padding: '5px 12px',
|
|
112
129
|
background: 'rgba(11, 18, 32, 0.95)',
|
|
113
130
|
color: '#ffffff',
|
|
114
|
-
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
115
131
|
fontSize: '13px',
|
|
116
132
|
fontWeight: '600',
|
|
117
133
|
letterSpacing: '0.01em',
|
|
@@ -124,6 +140,106 @@ export const CURSOR_OVERLAY_INIT_SCRIPT = `
|
|
|
124
140
|
whiteSpace: 'nowrap',
|
|
125
141
|
});
|
|
126
142
|
|
|
143
|
+
// ── Progress timeline bar (top edge, thin strip) ───────────────────────
|
|
144
|
+
const progress = document.createElement('div');
|
|
145
|
+
progress.id = '__bad_overlay_progress';
|
|
146
|
+
Object.assign(progress.style, {
|
|
147
|
+
position: 'fixed',
|
|
148
|
+
top: '0',
|
|
149
|
+
left: '0',
|
|
150
|
+
right: '0',
|
|
151
|
+
height: '3px',
|
|
152
|
+
background: 'rgba(11, 18, 32, 0.6)',
|
|
153
|
+
pointerEvents: 'none',
|
|
154
|
+
opacity: '0',
|
|
155
|
+
transition: 'opacity 300ms ease',
|
|
156
|
+
});
|
|
157
|
+
const progressFill = document.createElement('div');
|
|
158
|
+
Object.assign(progressFill.style, {
|
|
159
|
+
position: 'absolute',
|
|
160
|
+
top: '0',
|
|
161
|
+
left: '0',
|
|
162
|
+
height: '100%',
|
|
163
|
+
width: '0%',
|
|
164
|
+
background: 'linear-gradient(90deg, #4f46e5 0%, #7aa2ff 100%)',
|
|
165
|
+
transition: 'width 500ms cubic-bezier(0.4, 0, 0.2, 1)',
|
|
166
|
+
boxShadow: '0 0 8px rgba(79, 70, 229, 0.6)',
|
|
167
|
+
});
|
|
168
|
+
progress.appendChild(progressFill);
|
|
169
|
+
|
|
170
|
+
// ── Progress label (turn counter, top-left) ────────────────────────────
|
|
171
|
+
const progressLabel = document.createElement('div');
|
|
172
|
+
Object.assign(progressLabel.style, {
|
|
173
|
+
position: 'fixed',
|
|
174
|
+
top: '10px',
|
|
175
|
+
left: '14px',
|
|
176
|
+
padding: '4px 10px',
|
|
177
|
+
background: 'rgba(11, 18, 32, 0.88)',
|
|
178
|
+
color: '#ffffff',
|
|
179
|
+
fontSize: '11px',
|
|
180
|
+
fontWeight: '700',
|
|
181
|
+
letterSpacing: '0.06em',
|
|
182
|
+
textTransform: 'uppercase',
|
|
183
|
+
borderRadius: '6px',
|
|
184
|
+
pointerEvents: 'none',
|
|
185
|
+
opacity: '0',
|
|
186
|
+
transition: 'opacity 300ms ease',
|
|
187
|
+
boxShadow: '0 2px 8px rgba(0,0,0,0.3)',
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// ── Reasoning panel (top-right, translucent; agent's own words) ────────
|
|
191
|
+
const reasoning = document.createElement('div');
|
|
192
|
+
reasoning.id = '__bad_overlay_reasoning';
|
|
193
|
+
Object.assign(reasoning.style, {
|
|
194
|
+
position: 'fixed',
|
|
195
|
+
top: '40px',
|
|
196
|
+
right: '14px',
|
|
197
|
+
maxWidth: '360px',
|
|
198
|
+
minWidth: '220px',
|
|
199
|
+
padding: '12px 14px',
|
|
200
|
+
background: 'rgba(11, 18, 32, 0.92)',
|
|
201
|
+
color: '#e6ebf5',
|
|
202
|
+
fontSize: '12px',
|
|
203
|
+
lineHeight: '1.5',
|
|
204
|
+
borderRadius: '10px',
|
|
205
|
+
borderLeft: '3px solid #7aa2ff',
|
|
206
|
+
pointerEvents: 'none',
|
|
207
|
+
opacity: '0',
|
|
208
|
+
transform: 'translateY(-6px)',
|
|
209
|
+
transition: 'opacity 250ms ease, transform 250ms ease',
|
|
210
|
+
boxShadow: '0 6px 20px rgba(0,0,0,0.35)',
|
|
211
|
+
backdropFilter: 'blur(4px)',
|
|
212
|
+
});
|
|
213
|
+
const reasoningHeader = document.createElement('div');
|
|
214
|
+
Object.assign(reasoningHeader.style, {
|
|
215
|
+
fontSize: '10px',
|
|
216
|
+
fontWeight: '700',
|
|
217
|
+
letterSpacing: '0.1em',
|
|
218
|
+
textTransform: 'uppercase',
|
|
219
|
+
color: '#7aa2ff',
|
|
220
|
+
marginBottom: '6px',
|
|
221
|
+
});
|
|
222
|
+
reasoningHeader.textContent = 'Agent reasoning';
|
|
223
|
+
const reasoningBody = document.createElement('div');
|
|
224
|
+
Object.assign(reasoningBody.style, { whiteSpace: 'pre-wrap' });
|
|
225
|
+
reasoning.appendChild(reasoningHeader);
|
|
226
|
+
reasoning.appendChild(reasoningBody);
|
|
227
|
+
|
|
228
|
+
// ── Verdict badges (bottom-left, stacked) ──────────────────────────────
|
|
229
|
+
const badges = document.createElement('div');
|
|
230
|
+
badges.id = '__bad_overlay_badges';
|
|
231
|
+
Object.assign(badges.style, {
|
|
232
|
+
position: 'fixed',
|
|
233
|
+
bottom: '14px',
|
|
234
|
+
left: '14px',
|
|
235
|
+
display: 'flex',
|
|
236
|
+
flexDirection: 'column-reverse',
|
|
237
|
+
gap: '6px',
|
|
238
|
+
pointerEvents: 'none',
|
|
239
|
+
maxHeight: '60vh',
|
|
240
|
+
overflow: 'hidden',
|
|
241
|
+
});
|
|
242
|
+
|
|
127
243
|
// Append to documentElement (not body) so the overlay is not affected by
|
|
128
244
|
// body's stacking context — many modern sites apply transform/will-change
|
|
129
245
|
// to body which would clip a max-z-index child appended to body.
|
|
@@ -138,6 +254,10 @@ export const CURSOR_OVERLAY_INIT_SCRIPT = `
|
|
|
138
254
|
return;
|
|
139
255
|
}
|
|
140
256
|
host.appendChild(root);
|
|
257
|
+
root.appendChild(progress);
|
|
258
|
+
root.appendChild(progressLabel);
|
|
259
|
+
root.appendChild(reasoning);
|
|
260
|
+
root.appendChild(badges);
|
|
141
261
|
root.appendChild(box);
|
|
142
262
|
root.appendChild(ring);
|
|
143
263
|
root.appendChild(cursor);
|
|
@@ -145,11 +265,42 @@ export const CURSOR_OVERLAY_INIT_SCRIPT = `
|
|
|
145
265
|
}
|
|
146
266
|
attach();
|
|
147
267
|
|
|
268
|
+
// ── Badge factory ─────────────────────────────────────────────────────
|
|
269
|
+
const BADGE_COLORS = {
|
|
270
|
+
positive: { fg: '#fb7185', bg: 'rgba(251,113,133,0.15)', border: 'rgba(251,113,133,0.5)' },
|
|
271
|
+
cleared: { fg: '#4ade80', bg: 'rgba(74,222,128,0.15)', border: 'rgba(74,222,128,0.5)' },
|
|
272
|
+
review: { fg: '#fbbf24', bg: 'rgba(251,191,36,0.15)', border: 'rgba(251,191,36,0.5)' },
|
|
273
|
+
info: { fg: '#7aa2ff', bg: 'rgba(122,162,255,0.15)', border: 'rgba(122,162,255,0.5)' },
|
|
274
|
+
};
|
|
275
|
+
const MAX_BADGES = 6;
|
|
276
|
+
|
|
277
|
+
function makeBadge(kind, text) {
|
|
278
|
+
const palette = BADGE_COLORS[kind] || BADGE_COLORS.info;
|
|
279
|
+
const el = document.createElement('div');
|
|
280
|
+
Object.assign(el.style, {
|
|
281
|
+
padding: '8px 14px',
|
|
282
|
+
background: 'rgba(11, 18, 32, 0.92)',
|
|
283
|
+
color: palette.fg,
|
|
284
|
+
fontSize: '12px',
|
|
285
|
+
fontWeight: '700',
|
|
286
|
+
letterSpacing: '0.02em',
|
|
287
|
+
borderRadius: '8px',
|
|
288
|
+
border: '1px solid ' + palette.border,
|
|
289
|
+
boxShadow: '0 4px 16px rgba(0,0,0,0.35), inset 0 0 0 9999px ' + palette.bg,
|
|
290
|
+
animation: '__bad_badge_in 300ms cubic-bezier(0.34, 1.56, 0.64, 1) both',
|
|
291
|
+
whiteSpace: 'nowrap',
|
|
292
|
+
maxWidth: '480px',
|
|
293
|
+
overflow: 'hidden',
|
|
294
|
+
textOverflow: 'ellipsis',
|
|
295
|
+
});
|
|
296
|
+
el.textContent = text;
|
|
297
|
+
return el;
|
|
298
|
+
}
|
|
299
|
+
|
|
148
300
|
// ── Public API ─────────────────────────────────────────────────────────
|
|
149
301
|
window.__bad_overlay = {
|
|
150
302
|
/**
|
|
151
303
|
* Highlight a target element by CSS selector. Returns the rect or null.
|
|
152
|
-
* Used when the caller has a stable CSS selector.
|
|
153
304
|
*/
|
|
154
305
|
highlight(selector) {
|
|
155
306
|
try {
|
|
@@ -169,11 +320,6 @@ export const CURSOR_OVERLAY_INIT_SCRIPT = `
|
|
|
169
320
|
} catch { return null; }
|
|
170
321
|
},
|
|
171
322
|
|
|
172
|
-
/**
|
|
173
|
-
* Highlight an arbitrary rect at given page coordinates. Used when the
|
|
174
|
-
* caller already computed the rect via Playwright's boundingBox (i.e.,
|
|
175
|
-
* the @ref selector isn't a real CSS selector).
|
|
176
|
-
*/
|
|
177
323
|
highlightRect(x, y, width, height) {
|
|
178
324
|
try {
|
|
179
325
|
Object.assign(box.style, {
|
|
@@ -185,9 +331,6 @@ export const CURSOR_OVERLAY_INIT_SCRIPT = `
|
|
|
185
331
|
} catch { /* never let cosmetic overlay break a run */ }
|
|
186
332
|
},
|
|
187
333
|
|
|
188
|
-
/**
|
|
189
|
-
* Move the cursor to (x, y) and show a label.
|
|
190
|
-
*/
|
|
191
334
|
moveTo(x, y, labelText) {
|
|
192
335
|
cursor.style.transform = \`translate(\${x - 4}px, \${y - 4}px)\`;
|
|
193
336
|
if (labelText) {
|
|
@@ -199,18 +342,13 @@ export const CURSOR_OVERLAY_INIT_SCRIPT = `
|
|
|
199
342
|
}
|
|
200
343
|
},
|
|
201
344
|
|
|
202
|
-
/**
|
|
203
|
-
* Pulse a click ring at (x, y).
|
|
204
|
-
*/
|
|
205
345
|
pulseClick(x, y) {
|
|
206
|
-
// reset
|
|
207
346
|
Object.assign(ring.style, {
|
|
208
347
|
width: '0',
|
|
209
348
|
height: '0',
|
|
210
349
|
transform: \`translate(\${x}px, \${y}px)\`,
|
|
211
350
|
opacity: '1',
|
|
212
351
|
});
|
|
213
|
-
// expand
|
|
214
352
|
requestAnimationFrame(() => {
|
|
215
353
|
Object.assign(ring.style, {
|
|
216
354
|
width: '60px',
|
|
@@ -222,17 +360,70 @@ export const CURSOR_OVERLAY_INIT_SCRIPT = `
|
|
|
222
360
|
},
|
|
223
361
|
|
|
224
362
|
/**
|
|
225
|
-
*
|
|
363
|
+
* Show the agent's reasoning for the current turn. Multi-line text,
|
|
364
|
+
* word-wrapped, in the top-right panel. Pass empty string or null to
|
|
365
|
+
* hide the panel.
|
|
226
366
|
*/
|
|
367
|
+
setReasoning(text) {
|
|
368
|
+
try {
|
|
369
|
+
const t = (text || '').trim();
|
|
370
|
+
if (!t) {
|
|
371
|
+
Object.assign(reasoning.style, { opacity: '0', transform: 'translateY(-6px)' });
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
reasoningBody.textContent = t;
|
|
375
|
+
Object.assign(reasoning.style, { opacity: '1', transform: 'translateY(0)' });
|
|
376
|
+
} catch { /* cosmetic */ }
|
|
377
|
+
},
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Update the progress strip: "current" of "total" actions done.
|
|
381
|
+
* progressText is optional (e.g., "Turn 27 · C-003").
|
|
382
|
+
*/
|
|
383
|
+
setProgress(current, total, progressText) {
|
|
384
|
+
try {
|
|
385
|
+
const c = Math.max(0, Number(current) || 0);
|
|
386
|
+
const tot = Math.max(1, Number(total) || 1);
|
|
387
|
+
const pct = Math.min(100, (c / tot) * 100);
|
|
388
|
+
progressFill.style.width = pct + '%';
|
|
389
|
+
progress.style.opacity = '1';
|
|
390
|
+
if (progressText) {
|
|
391
|
+
progressLabel.textContent = progressText;
|
|
392
|
+
progressLabel.style.opacity = '1';
|
|
393
|
+
}
|
|
394
|
+
} catch { /* cosmetic */ }
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Push a verdict badge onto the bottom-left stack. Auto-trims to the
|
|
399
|
+
* most recent MAX_BADGES. kind is one of "positive" | "cleared" |
|
|
400
|
+
* "review" | "info".
|
|
401
|
+
*/
|
|
402
|
+
pushBadge(kind, text) {
|
|
403
|
+
try {
|
|
404
|
+
const el = makeBadge(kind, text);
|
|
405
|
+
badges.appendChild(el);
|
|
406
|
+
// Trim oldest if over cap (column-reverse means first child = bottom)
|
|
407
|
+
while (badges.children.length > MAX_BADGES) {
|
|
408
|
+
const first = badges.firstChild;
|
|
409
|
+
if (!first) break;
|
|
410
|
+
badges.removeChild(first);
|
|
411
|
+
}
|
|
412
|
+
} catch { /* cosmetic */ }
|
|
413
|
+
},
|
|
414
|
+
|
|
415
|
+
clearBadges() {
|
|
416
|
+
try {
|
|
417
|
+
while (badges.firstChild) badges.removeChild(badges.firstChild);
|
|
418
|
+
} catch { /* cosmetic */ }
|
|
419
|
+
},
|
|
420
|
+
|
|
227
421
|
hide() {
|
|
228
422
|
box.style.opacity = '0';
|
|
229
423
|
label.style.opacity = '0';
|
|
230
424
|
ring.style.opacity = '0';
|
|
231
425
|
},
|
|
232
426
|
|
|
233
|
-
/**
|
|
234
|
-
* Hide just the highlight box (cursor + label stay).
|
|
235
|
-
*/
|
|
236
427
|
clearHighlight() {
|
|
237
428
|
box.style.opacity = '0';
|
|
238
429
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor-overlay.js","sourceRoot":"","sources":["../../src/drivers/cursor-overlay.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cursor-overlay.js","sourceRoot":"","sources":["../../src/drivers/cursor-overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoZzC,CAAA"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor-overlay label builder.
|
|
3
|
+
*
|
|
4
|
+
* The overlay renders a single short label string next to the animated
|
|
5
|
+
* cursor. A bare verb (`click`, `type`) leaves 90% of the agent's intent
|
|
6
|
+
* invisible to anyone watching the recording. Rich labels turn the cursor
|
|
7
|
+
* into a readable narrative — `click · Search`, `type · "IVANOV ALEKSANDR"`,
|
|
8
|
+
* `press · Enter`, `nav · sanctionssearch.ofac.treas.gov`.
|
|
9
|
+
*
|
|
10
|
+
* Design rules:
|
|
11
|
+
* - Labels MUST fit on one line at ~13px. Target max 48 chars rendered.
|
|
12
|
+
* - Truncate with an ellipsis; never wrap.
|
|
13
|
+
* - Verb-only is the SAFE FALLBACK — never throw, never return empty.
|
|
14
|
+
* - Target accessible name goes in the label only when it's short and
|
|
15
|
+
* informative. Skip generic names ('button', 'link', 'textbox') that
|
|
16
|
+
* restate the role the viewer already sees from context.
|
|
17
|
+
*/
|
|
18
|
+
import type { Action } from '../types.js';
|
|
19
|
+
export interface OverlayLabelContext {
|
|
20
|
+
/** Accessible name of the target element (from snapshot ref lookup). */
|
|
21
|
+
targetName?: string;
|
|
22
|
+
/** ARIA role of the target, used to filter generic names. */
|
|
23
|
+
targetRole?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build a single-line label string for the cursor overlay given an action
|
|
27
|
+
* and (optionally) the resolved target element's accessible name.
|
|
28
|
+
*
|
|
29
|
+
* Always returns a non-empty string. Falls back to the bare verb on any
|
|
30
|
+
* missing context.
|
|
31
|
+
*/
|
|
32
|
+
export declare function formatOverlayLabel(action: Action, ctx?: OverlayLabelContext): string;
|
|
33
|
+
//# sourceMappingURL=overlay-label.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay-label.d.ts","sourceRoot":"","sources":["../../src/drivers/overlay-label.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AA0CzC,MAAM,WAAW,mBAAmB;IAClC,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAE,mBAAwB,GAAG,MAAM,CAsCxF"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const MAX_LABEL_LEN = 48;
|
|
2
|
+
const MAX_TEXT_PREVIEW = 30;
|
|
3
|
+
const MAX_NAME_PREVIEW = 30;
|
|
4
|
+
/**
|
|
5
|
+
* Role names that are generic; including them in the label adds no info.
|
|
6
|
+
* NOTE: "Search" is NOT here — a button labeled "Search" is informative
|
|
7
|
+
* (OFAC, Google, etc.). "searchbox" IS here — that's the role spilling
|
|
8
|
+
* through as a name when an input has no label, which just restates what
|
|
9
|
+
* the viewer already sees from the cursor's position on the textbox.
|
|
10
|
+
*/
|
|
11
|
+
const GENERIC_NAMES = new Set([
|
|
12
|
+
'button', 'link', 'textbox', 'input', 'field', 'searchbox',
|
|
13
|
+
'combobox', 'listbox', 'option', 'checkbox', 'radio', 'switch',
|
|
14
|
+
]);
|
|
15
|
+
function truncate(s, max) {
|
|
16
|
+
const collapsed = s.replace(/\s+/g, ' ').trim();
|
|
17
|
+
if (collapsed.length <= max)
|
|
18
|
+
return collapsed;
|
|
19
|
+
return collapsed.slice(0, max - 1).trimEnd() + '…';
|
|
20
|
+
}
|
|
21
|
+
function cleanName(name) {
|
|
22
|
+
if (!name)
|
|
23
|
+
return undefined;
|
|
24
|
+
const c = name.replace(/\s+/g, ' ').trim();
|
|
25
|
+
if (!c)
|
|
26
|
+
return undefined;
|
|
27
|
+
if (GENERIC_NAMES.has(c.toLowerCase()))
|
|
28
|
+
return undefined;
|
|
29
|
+
return truncate(c, MAX_NAME_PREVIEW);
|
|
30
|
+
}
|
|
31
|
+
/** Host of a URL without protocol/trailing-slash. Never throws. */
|
|
32
|
+
function displayHost(url) {
|
|
33
|
+
try {
|
|
34
|
+
const u = new URL(url);
|
|
35
|
+
return u.host || url;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return url.replace(/^https?:\/\//, '').replace(/\/.*$/, '').slice(0, MAX_NAME_PREVIEW);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build a single-line label string for the cursor overlay given an action
|
|
43
|
+
* and (optionally) the resolved target element's accessible name.
|
|
44
|
+
*
|
|
45
|
+
* Always returns a non-empty string. Falls back to the bare verb on any
|
|
46
|
+
* missing context.
|
|
47
|
+
*/
|
|
48
|
+
export function formatOverlayLabel(action, ctx = {}) {
|
|
49
|
+
const name = cleanName(ctx.targetName);
|
|
50
|
+
switch (action.action) {
|
|
51
|
+
case 'click': {
|
|
52
|
+
return truncate(name ? `click · ${name}` : 'click', MAX_LABEL_LEN);
|
|
53
|
+
}
|
|
54
|
+
case 'type': {
|
|
55
|
+
const text = truncate(action.text ?? '', MAX_TEXT_PREVIEW);
|
|
56
|
+
if (text && name)
|
|
57
|
+
return truncate(`type "${text}" · ${name}`, MAX_LABEL_LEN);
|
|
58
|
+
if (text)
|
|
59
|
+
return truncate(`type · "${text}"`, MAX_LABEL_LEN);
|
|
60
|
+
return name ? truncate(`type · ${name}`, MAX_LABEL_LEN) : 'type';
|
|
61
|
+
}
|
|
62
|
+
case 'press': {
|
|
63
|
+
const key = action.key ?? '';
|
|
64
|
+
if (key && name)
|
|
65
|
+
return truncate(`press ${key} · ${name}`, MAX_LABEL_LEN);
|
|
66
|
+
return truncate(key ? `press · ${key}` : 'press', MAX_LABEL_LEN);
|
|
67
|
+
}
|
|
68
|
+
case 'hover': {
|
|
69
|
+
return truncate(name ? `hover · ${name}` : 'hover', MAX_LABEL_LEN);
|
|
70
|
+
}
|
|
71
|
+
case 'select': {
|
|
72
|
+
const val = truncate(action.value ?? '', MAX_TEXT_PREVIEW);
|
|
73
|
+
if (val && name)
|
|
74
|
+
return truncate(`select ${val} · ${name}`, MAX_LABEL_LEN);
|
|
75
|
+
if (val)
|
|
76
|
+
return truncate(`select · ${val}`, MAX_LABEL_LEN);
|
|
77
|
+
return name ? truncate(`select · ${name}`, MAX_LABEL_LEN) : 'select';
|
|
78
|
+
}
|
|
79
|
+
case 'scroll': {
|
|
80
|
+
return truncate(`scroll ${action.direction}`, MAX_LABEL_LEN);
|
|
81
|
+
}
|
|
82
|
+
case 'navigate': {
|
|
83
|
+
return truncate(`nav · ${displayHost(action.url)}`, MAX_LABEL_LEN);
|
|
84
|
+
}
|
|
85
|
+
case 'wait': {
|
|
86
|
+
return truncate(`wait ${action.ms}ms`, MAX_LABEL_LEN);
|
|
87
|
+
}
|
|
88
|
+
default:
|
|
89
|
+
return action.action;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=overlay-label.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay-label.js","sourceRoot":"","sources":["../../src/drivers/overlay-label.ts"],"names":[],"mappings":"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"}
|
|
@@ -99,6 +99,28 @@ export declare class PlaywrightDriver implements Driver {
|
|
|
99
99
|
private animateCursorToCoord;
|
|
100
100
|
/** Get phase-level timing from the last observe() call */
|
|
101
101
|
getLastTiming(): ObserveTiming | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* Set the reasoning panel (top-right) to the agent's current-turn
|
|
104
|
+
* reasoning. No-op when the overlay is disabled.
|
|
105
|
+
*
|
|
106
|
+
* Defensive: wraps page.evaluate with try/catch so navigation races,
|
|
107
|
+
* closed contexts, or a missing overlay (XML / PDF viewer / chrome://
|
|
108
|
+
* pages) never crash a run — the overlay is strictly cosmetic.
|
|
109
|
+
*/
|
|
110
|
+
setOverlayReasoning(text: string): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Update the progress bar + turn counter at the top of the overlay.
|
|
113
|
+
* `total` is the max-turns budget; `label` is a short status string
|
|
114
|
+
* (e.g., "Turn 27 · C-003").
|
|
115
|
+
*/
|
|
116
|
+
setOverlayProgress(current: number, total: number, label?: string): Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Push a verdict badge to the bottom-left stack. Used when the agent's
|
|
119
|
+
* reasoning indicates a conclusion ("POSITIVE MATCH", "CLEARED",
|
|
120
|
+
* "NEEDS REVIEW") — the driver runner parses reasoning + emits these
|
|
121
|
+
* to celebrate the moment.
|
|
122
|
+
*/
|
|
123
|
+
pushOverlayBadge(kind: 'positive' | 'cleared' | 'review' | 'info', text: string): Promise<void>;
|
|
102
124
|
getPage(): Page;
|
|
103
125
|
getUrl(): string;
|
|
104
126
|
inspectSelectorHref(selector: string): Promise<string | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../../src/drivers/playwright.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../../src/drivers/playwright.ts"],"names":[],"mappings":"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"}
|