cli-jaw 2.17.41 → 2.17.42
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/README.md +3 -3
- package/dist/bin/commands/browser.js +52 -5
- package/dist/bin/commands/browser.js.map +1 -1
- package/dist/src/agent/args.js +69 -19
- package/dist/src/agent/args.js.map +1 -1
- package/dist/src/agent/codex-app-events.js +31 -1
- package/dist/src/agent/codex-app-events.js.map +1 -1
- package/dist/src/agent/cursor-model-inventory.js +140 -0
- package/dist/src/agent/cursor-model-inventory.js.map +1 -0
- package/dist/src/agent/grok-models.js +80 -0
- package/dist/src/agent/grok-models.js.map +1 -0
- package/dist/src/browser/actions.js +383 -20
- package/dist/src/browser/actions.js.map +1 -1
- package/dist/src/browser/grounding-candidate.js +272 -0
- package/dist/src/browser/grounding-candidate.js.map +1 -0
- package/dist/src/browser/grounding-eval.js +247 -0
- package/dist/src/browser/grounding-eval.js.map +1 -0
- package/dist/src/browser/image-size.js +63 -0
- package/dist/src/browser/image-size.js.map +1 -0
- package/dist/src/browser/index.js +1 -0
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/browser/occlusion.js +148 -0
- package/dist/src/browser/occlusion.js.map +1 -0
- package/dist/src/browser/verify-candidate.js +162 -0
- package/dist/src/browser/verify-candidate.js.map +1 -0
- package/dist/src/browser/vision-input.js +74 -0
- package/dist/src/browser/vision-input.js.map +1 -0
- package/dist/src/browser/vision-provider.js +75 -0
- package/dist/src/browser/vision-provider.js.map +1 -0
- package/dist/src/browser/vision.js +360 -47
- package/dist/src/browser/vision.js.map +1 -1
- package/dist/src/browser/web-ai/candidate-reconcile.js +14 -5
- package/dist/src/browser/web-ai/candidate-reconcile.js.map +1 -1
- package/dist/src/cli/claude-model-discovery.js +175 -0
- package/dist/src/cli/claude-model-discovery.js.map +1 -0
- package/dist/src/cli/claude-models.js +46 -0
- package/dist/src/cli/claude-models.js.map +1 -1
- package/dist/src/cli/registry-live.js +104 -1
- package/dist/src/cli/registry-live.js.map +1 -1
- package/dist/src/cli/registry.js +28 -5
- package/dist/src/cli/registry.js.map +1 -1
- package/dist/src/code-mode/host.js +6 -0
- package/dist/src/code-mode/host.js.map +1 -1
- package/dist/src/code-mode/manager.js +37 -8
- package/dist/src/code-mode/manager.js.map +1 -1
- package/dist/src/code-mode/providers/catalog.js +53 -1
- package/dist/src/code-mode/providers/catalog.js.map +1 -1
- package/dist/src/code-mode/providers/codex-app.js +6 -0
- package/dist/src/code-mode/providers/codex-app.js.map +1 -1
- package/dist/src/code-mode/providers/live-models.js +85 -0
- package/dist/src/code-mode/providers/live-models.js.map +1 -0
- package/dist/src/code-mode/providers/provider-live-models.js +120 -0
- package/dist/src/code-mode/providers/provider-live-models.js.map +1 -0
- package/dist/src/code-mode/session.js +37 -0
- package/dist/src/code-mode/session.js.map +1 -1
- package/dist/src/prompt/templates/a1-system.md +24 -14
- package/dist/src/prompt/templates/control-system.md +6 -11
- package/dist/src/prompt/templates/employee.md +5 -5
- package/dist/src/routes/browser.js +17 -5
- package/dist/src/routes/browser.js.map +1 -1
- package/package.json +1 -1
- package/public/css/activity.css +20 -1
- package/public/css/layout.css +7 -3
- package/public/dist/.vite/manifest.json +104 -105
- package/public/dist/assets/Agent-CQ4EBgY8.js +1 -0
- package/public/dist/assets/CodeCanvas-DWsx6sv5.js +2 -0
- package/public/dist/assets/DashboardMeta-Dh-tasbY.js +1 -0
- package/public/dist/assets/Display-NrqY_IaN.js +1 -0
- package/public/dist/assets/{Permissions-BzbnqenZ.js → Permissions-Bh947tUG.js} +1 -1
- package/public/dist/assets/Permissions-DzNJCt8K.js +1 -0
- package/public/dist/assets/app-BCBnxiGr.css +1 -0
- package/public/dist/assets/{app-x5Kray4W.js → app-Q0prKOFx.js} +4 -4
- package/public/dist/assets/{bgtask-badge-W9djRQVu.js → bgtask-badge-DAcWaUfS.js} +1 -1
- package/public/dist/assets/bgtask-badge-DHA2OQm8.js +1 -0
- package/public/dist/assets/{employees--_dmDw_a.js → employees-CWS3Juca.js} +1 -1
- package/public/dist/assets/{iframe-renderer-D4BmLtAq.js → iframe-renderer-DCDKBMb7.js} +2 -2
- package/public/dist/assets/iframe-renderer-RpvBly2x.js +1 -0
- package/public/dist/assets/manager-Nk7-RYPs.css +1 -0
- package/public/dist/assets/manager-XkR-DRaQ.js +5 -0
- package/public/dist/assets/manager-shortcuts-jsiX-zV6.js +1 -0
- package/public/dist/assets/memory-4YH8sz0L.js +1 -0
- package/public/dist/assets/{memory-DENQWzM8.js → memory-DhNdKZLO.js} +1 -1
- package/public/dist/assets/{message-history-BsrLcGJj.js → message-history-DbRTl1P0.js} +1 -1
- package/public/dist/assets/message-history-lVcLpAw8.js +1 -0
- package/public/dist/assets/{render-CF6Q1oIh.js → render-DP8SJtdZ.js} +1 -1
- package/public/dist/assets/settings--EGy3_B9.js +1 -0
- package/public/dist/assets/settings-85n9NjvX.js +1 -0
- package/public/dist/assets/{settings-B-C_jdZR.js → settings-C9_D-pn0.js} +32 -32
- package/public/dist/assets/settings-core-8wBl1mg-.js +1 -0
- package/public/dist/assets/{settings-core-BzAJLsKN.js → settings-core-BcVD2FgV.js} +1 -1
- package/public/dist/assets/{settings-embedding-dEMyByVi.js → settings-embedding-BSpq785l.js} +1 -1
- package/public/dist/assets/{settings-embedding-BSA42oKM.css → settings-embedding-LIMq5ebM.css} +1 -1
- package/public/dist/assets/{sidebar-fFEOpvJl.js → sidebar-BVhLb5Ft.js} +3 -3
- package/public/dist/assets/skills-6GFkHTlQ.js +1 -0
- package/public/dist/assets/{skills-Da_b06ci.js → skills-X7qxmHka.js} +1 -1
- package/public/dist/assets/{slash-commands-BbPUHHF4.js → slash-commands-BnDH4IU_.js} +1 -1
- package/public/dist/assets/slash-commands-qu8fPAZ3.js +1 -0
- package/public/dist/assets/{trace-drawer-B-QmzUfP.js → trace-drawer-DasaS1z4.js} +1 -1
- package/public/dist/assets/{ui-CkRUrIfp.js → ui-BtiBTXng.js} +1 -1
- package/public/dist/assets/ui-BucJ1zMA.js +1 -0
- package/public/dist/index.html +2 -2
- package/public/dist/manager/index.html +3 -3
- package/public/dist/settings/index.html +2 -2
- package/public/js/features/settings.ts +16 -0
- package/public/manager/src/App.tsx +10 -12
- package/public/manager/src/AppChrome.tsx +2 -3
- package/public/manager/src/InstancePreview.tsx +47 -0
- package/public/manager/src/SidebarRailRouter.tsx +34 -30
- package/public/manager/src/code/CodeComposer.tsx +31 -6
- package/public/manager/src/code/CodeSessionList.tsx +25 -7
- package/public/manager/src/code/CodeToastHost.tsx +49 -0
- package/public/manager/src/code/CodeTranscript.tsx +136 -26
- package/public/manager/src/code/CodeWorkbench.tsx +16 -2
- package/public/manager/src/code/ComposerFooter.tsx +130 -46
- package/public/manager/src/code/ContextUsageMeter.tsx +72 -0
- package/public/manager/src/code/ProviderGlyph.tsx +58 -0
- package/public/manager/src/code/code-composer.css +159 -175
- package/public/manager/src/code/code-controller-runtime.ts +3 -1
- package/public/manager/src/code/code-session-state.ts +7 -1
- package/public/manager/src/code/code-toasts.ts +73 -0
- package/public/manager/src/code/code-workbench.css +42 -1
- package/public/manager/src/code/code.css +130 -15
- package/public/manager/src/code/context-usage.ts +74 -0
- package/public/manager/src/code/pending-user-item.ts +46 -0
- package/public/manager/src/code/session-order.ts +70 -0
- package/public/manager/src/code/tool-summary.ts +101 -0
- package/public/manager/src/code/use-dictation.ts +130 -0
- package/public/manager/src/components/CommandCenter.tsx +4 -8
- package/public/manager/src/components/InstanceDetailPanel.tsx +0 -18
- package/public/manager/src/components/Workbench.tsx +6 -10
- package/public/manager/src/components/WorkbenchHeader.tsx +0 -11
- package/public/manager/src/dashboard-settings/dashboard-settings-ui.ts +3 -1
- package/public/manager/src/hooks/useDashboardView.ts +38 -38
- package/public/manager/src/manager-components.css +0 -8
- package/public/manager/src/manager-shortcut-runner.ts +0 -1
- package/public/manager/src/manager-shortcuts.ts +0 -2
- package/public/manager/src/settings/pages/Agent.tsx +5 -2
- package/public/manager/src/settings/pages/DashboardMeta.tsx +20 -1
- package/public/manager/src/settings/pages/Permissions.tsx +23 -4
- package/public/manager/src/settings/pages/components/agent/PermissionQuickSection.tsx +6 -2
- package/public/manager/src/settings/pages/components/agent/agent-save.ts +24 -1
- package/public/manager/src/settings/settings-icons.ts +3 -1
- package/public/manager/src/settings/settings-registry.ts +3 -3
- package/public/manager/src/settings-controls.css +0 -1
- package/public/manager/src/settings-standalone.tsx +8 -8
- package/public/manager/src/types.ts +0 -1
- package/scripts/check-private-boundary.mjs +101 -0
- package/scripts/grounding-eval.mjs +223 -0
- package/public/dist/assets/Agent-B8ZEWJ6B.js +0 -1
- package/public/dist/assets/CodeCanvas-Bj3lTSu6.js +0 -2
- package/public/dist/assets/DashboardMeta-CmZavI4H.js +0 -1
- package/public/dist/assets/Display-DFkjKcMk.js +0 -1
- package/public/dist/assets/Permissions-CJftCO6p.js +0 -1
- package/public/dist/assets/app-DVbrIl8f.css +0 -1
- package/public/dist/assets/bgtask-badge-CuAc8bAl.js +0 -1
- package/public/dist/assets/iframe-renderer-By2WO5bd.js +0 -1
- package/public/dist/assets/manager-CxLXWCGi.css +0 -1
- package/public/dist/assets/manager-DA3dF3eN.js +0 -5
- package/public/dist/assets/manager-shortcuts-CzGkE0ZG.js +0 -1
- package/public/dist/assets/memory-thjr2apY.js +0 -1
- package/public/dist/assets/message-history-CfU7_P5t.js +0 -1
- package/public/dist/assets/settings-DXA5L4DR.js +0 -1
- package/public/dist/assets/settings-Dy_IaNVH.js +0 -1
- package/public/dist/assets/settings-core-D7sAQqhC.js +0 -1
- package/public/dist/assets/skills-BcIievZp.js +0 -1
- package/public/dist/assets/slash-commands-B-5Shayl.js +0 -1
- package/public/dist/assets/ui-CJLdMfRx.js +0 -1
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/browser/grounding-candidate.ts — the shape a grounding answer takes, and
|
|
3
|
+
* how it is recovered from model output.
|
|
4
|
+
*
|
|
5
|
+
* Two things were wrong with the previous extractor, and both are structural
|
|
6
|
+
* rather than cosmetic.
|
|
7
|
+
*
|
|
8
|
+
* It matched with `/\{[^{}]*"found"...\}/`. The `[^{}]*` class cannot cross a
|
|
9
|
+
* nested object, so a well-formed `{"found":true,"bbox":{...},"point":{...}}`
|
|
10
|
+
* could never match at all — the richer schema was unreachable no matter what
|
|
11
|
+
* the model returned. It also required canonical key order and rejected
|
|
12
|
+
* negative numbers.
|
|
13
|
+
*
|
|
14
|
+
* And nothing bounded the coordinate. A hallucinated `{"x":99999}` was
|
|
15
|
+
* converted and clicked.
|
|
16
|
+
*
|
|
17
|
+
* On bbox: it is carried for verification, cropping, and size sanity — NOT as
|
|
18
|
+
* an accuracy mechanism. A controlled comparison (GUI-Actor, same training
|
|
19
|
+
* recipe) scores bbox output below point output, so asking a model for a box
|
|
20
|
+
* does not make its answer better. The box earns its place by letting us
|
|
21
|
+
* reconcile against element geometry and re-crop for a second look.
|
|
22
|
+
*/
|
|
23
|
+
/** Below this, a candidate must be verified before it is acted on. */
|
|
24
|
+
export const LOW_CONFIDENCE_THRESHOLD = 0.75;
|
|
25
|
+
/** A point-only answer carries no extent, so it starts as a coin flip. */
|
|
26
|
+
const POINT_ONLY_CONFIDENCE = 0.5;
|
|
27
|
+
const BBOX_CONFIDENCE = 0.8;
|
|
28
|
+
/**
|
|
29
|
+
* Beyond this, a coordinate is not a plausible screen position. Finiteness is
|
|
30
|
+
* too weak a gate: `1e308` is finite, and a bbox that size yields a center of
|
|
31
|
+
* `5e307` which then flows into a click.
|
|
32
|
+
*/
|
|
33
|
+
export const MAX_PLAUSIBLE_COORDINATE = 100_000;
|
|
34
|
+
function plausible(n) {
|
|
35
|
+
return Math.abs(n) <= MAX_PLAUSIBLE_COORDINATE;
|
|
36
|
+
}
|
|
37
|
+
function isRecord(value) {
|
|
38
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
39
|
+
}
|
|
40
|
+
function finiteNumber(value) {
|
|
41
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
42
|
+
}
|
|
43
|
+
function normalizePoint(value) {
|
|
44
|
+
if (!isRecord(value))
|
|
45
|
+
return null;
|
|
46
|
+
const x = finiteNumber(value['x']);
|
|
47
|
+
const y = finiteNumber(value['y']);
|
|
48
|
+
if (x === null || y === null)
|
|
49
|
+
return null;
|
|
50
|
+
return plausible(x) && plausible(y) ? { x, y } : null;
|
|
51
|
+
}
|
|
52
|
+
function normalizeBBox(value) {
|
|
53
|
+
if (!isRecord(value))
|
|
54
|
+
return null;
|
|
55
|
+
const x = finiteNumber(value['x']);
|
|
56
|
+
const y = finiteNumber(value['y']);
|
|
57
|
+
const width = finiteNumber(value['width']);
|
|
58
|
+
const height = finiteNumber(value['height']);
|
|
59
|
+
if (x === null || y === null || width === null || height === null)
|
|
60
|
+
return null;
|
|
61
|
+
// A zero-area box carries no extent, which is the only thing a box is for.
|
|
62
|
+
if (width <= 0 || height <= 0)
|
|
63
|
+
return null;
|
|
64
|
+
// A box larger than any screen is a confused answer, not a big target.
|
|
65
|
+
if (![x, y, width, height].every(plausible))
|
|
66
|
+
return null;
|
|
67
|
+
return { x, y, width, height };
|
|
68
|
+
}
|
|
69
|
+
function centerOfBBox(bbox) {
|
|
70
|
+
return { x: Math.round(bbox.x + bbox.width / 2), y: Math.round(bbox.y + bbox.height / 2) };
|
|
71
|
+
}
|
|
72
|
+
function clampConfidence(value, fallback) {
|
|
73
|
+
const n = finiteNumber(value);
|
|
74
|
+
return n === null ? fallback : Math.max(0, Math.min(1, n));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Accepts the current shape and the legacy `{found,x,y}` one.
|
|
78
|
+
*
|
|
79
|
+
* A legacy answer is normalized rather than rejected, but it is marked:
|
|
80
|
+
* `kind: 'coordinate'`, `point_only` in riskFlags, and a confidence that fails
|
|
81
|
+
* the gate. It says where to click without saying how big the thing is, and
|
|
82
|
+
* that difference should survive into the decision.
|
|
83
|
+
*/
|
|
84
|
+
export function normalizeCandidate(raw) {
|
|
85
|
+
if (!isRecord(raw))
|
|
86
|
+
return null;
|
|
87
|
+
if (typeof raw['found'] !== 'boolean')
|
|
88
|
+
return null;
|
|
89
|
+
if (!raw['found']) {
|
|
90
|
+
return {
|
|
91
|
+
schemaVersion: 'grounding-candidate-v1',
|
|
92
|
+
found: false,
|
|
93
|
+
kind: 'not_found',
|
|
94
|
+
bbox: null,
|
|
95
|
+
point: { x: 0, y: 0 },
|
|
96
|
+
confidence: 0,
|
|
97
|
+
description: typeof raw['description'] === 'string' ? raw['description'] : 'not found',
|
|
98
|
+
reason: typeof raw['reason'] === 'string' ? raw['reason'] : 'target_not_found',
|
|
99
|
+
riskFlags: [],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const bbox = normalizeBBox(raw['bbox']);
|
|
103
|
+
const explicitPoint = normalizePoint(raw['point']);
|
|
104
|
+
const legacyPoint = normalizePoint(raw); // top-level {x, y}
|
|
105
|
+
const point = explicitPoint ?? legacyPoint ?? (bbox ? centerOfBBox(bbox) : null);
|
|
106
|
+
if (!point)
|
|
107
|
+
return null;
|
|
108
|
+
// Two stated locations that disagree is evidence of a confused answer.
|
|
109
|
+
// Prefer the richer field, but say so rather than resolving it silently.
|
|
110
|
+
const disagrees = Boolean(explicitPoint && legacyPoint &&
|
|
111
|
+
(explicitPoint.x !== legacyPoint.x || explicitPoint.y !== legacyPoint.y));
|
|
112
|
+
const pointOnly = !bbox;
|
|
113
|
+
const candidate = {
|
|
114
|
+
schemaVersion: 'grounding-candidate-v1',
|
|
115
|
+
found: true,
|
|
116
|
+
kind: bbox ? 'grounding_bbox' : 'coordinate',
|
|
117
|
+
bbox,
|
|
118
|
+
point,
|
|
119
|
+
confidence: clampConfidence(raw['confidence'], pointOnly ? POINT_ONLY_CONFIDENCE : BBOX_CONFIDENCE),
|
|
120
|
+
riskFlags: [
|
|
121
|
+
...(pointOnly ? ['point_only'] : []),
|
|
122
|
+
...(disagrees ? ['conflicting_point'] : []),
|
|
123
|
+
],
|
|
124
|
+
};
|
|
125
|
+
if (typeof raw['description'] === 'string')
|
|
126
|
+
candidate.description = raw['description'];
|
|
127
|
+
if (typeof raw['reason'] === 'string')
|
|
128
|
+
candidate.reason = raw['reason'];
|
|
129
|
+
return candidate;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Scan out balanced JSON objects, tracking string state and escapes so a brace
|
|
133
|
+
* inside a string literal does not open or close a span.
|
|
134
|
+
*
|
|
135
|
+
* Emits nested objects as well as depth-0 ones, innermost first within a span.
|
|
136
|
+
* Models routinely wrap the answer — `{"result":{...}}`, `{"candidates":[...]}`
|
|
137
|
+
* — and a depth-0-only scan silently loses those, which the regex it replaced
|
|
138
|
+
* happened to handle.
|
|
139
|
+
*
|
|
140
|
+
* A stray quote is the hard case. Model text quotes page content ("the 15\"
|
|
141
|
+
* monitor"), and stdout truncation can cut inside a string literal, either of
|
|
142
|
+
* which leaves an odd quote count that inverts string tracking for everything
|
|
143
|
+
* after it. Tracking from the start of the text would then hide the real
|
|
144
|
+
* answer entirely, so the scan is also run from each subsequent `{` as a
|
|
145
|
+
* fresh origin, and any span that parses is kept.
|
|
146
|
+
*/
|
|
147
|
+
export function extractJsonObjects(text) {
|
|
148
|
+
const found = new Set();
|
|
149
|
+
const out = [];
|
|
150
|
+
for (const span of scanFrom(text, 0)) {
|
|
151
|
+
if (!found.has(span)) {
|
|
152
|
+
found.add(span);
|
|
153
|
+
out.push(span);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Re-scan from each opening brace. A prior stray quote can only desync the
|
|
157
|
+
// tracker to the LEFT of a given origin, so restarting recovers the spans
|
|
158
|
+
// it swallowed. Bounded to keep a pathological input from going quadratic.
|
|
159
|
+
let origins = 0;
|
|
160
|
+
for (let i = 0; i < text.length && origins < MAX_RESCAN_ORIGINS; i++) {
|
|
161
|
+
if (text[i] !== '{')
|
|
162
|
+
continue;
|
|
163
|
+
origins += 1;
|
|
164
|
+
for (const span of scanFrom(text, i)) {
|
|
165
|
+
if (!found.has(span)) {
|
|
166
|
+
found.add(span);
|
|
167
|
+
out.push(span);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
/** Restart budget for the stray-quote recovery above. */
|
|
174
|
+
const MAX_RESCAN_ORIGINS = 64;
|
|
175
|
+
function scanFrom(text, from) {
|
|
176
|
+
const objects = [];
|
|
177
|
+
const starts = [];
|
|
178
|
+
let inString = false;
|
|
179
|
+
let escaped = false;
|
|
180
|
+
for (let i = from; i < text.length; i++) {
|
|
181
|
+
const ch = text[i];
|
|
182
|
+
if (escaped) {
|
|
183
|
+
escaped = false;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (inString && ch === '\\') {
|
|
187
|
+
escaped = true;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (ch === '"') {
|
|
191
|
+
inString = !inString;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (inString)
|
|
195
|
+
continue;
|
|
196
|
+
if (ch === '{') {
|
|
197
|
+
starts.push(i);
|
|
198
|
+
}
|
|
199
|
+
else if (ch === '}') {
|
|
200
|
+
const start = starts.pop();
|
|
201
|
+
if (start === undefined)
|
|
202
|
+
continue; // stray closer
|
|
203
|
+
objects.push(text.slice(start, i + 1));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return objects;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Recover a candidate from arbitrary model text.
|
|
210
|
+
*
|
|
211
|
+
* Prefers a located answer over a not-found one, and only then prefers the
|
|
212
|
+
* later of two equally useful candidates.
|
|
213
|
+
*
|
|
214
|
+
* Ordering alone is not enough here. The prompt itself contains a literal
|
|
215
|
+
* `{"found":false,...}` template, so any echo of the instructions parses as a
|
|
216
|
+
* perfectly valid not-found answer — and taking simply the last one turns a
|
|
217
|
+
* successful lookup into "target not found". Preferring `found: true` is what
|
|
218
|
+
* actually resolves that, not the scan direction.
|
|
219
|
+
*
|
|
220
|
+
* A malformed span is skipped rather than aborting the scan: a stray quote in
|
|
221
|
+
* quoted page text can desynchronize string tracking for the rest of the
|
|
222
|
+
* input, so a later object may be the only parseable one.
|
|
223
|
+
*/
|
|
224
|
+
export function parseCandidate(text) {
|
|
225
|
+
const objects = extractJsonObjects(text);
|
|
226
|
+
let fallback = null;
|
|
227
|
+
for (let i = objects.length - 1; i >= 0; i--) {
|
|
228
|
+
let parsed;
|
|
229
|
+
try {
|
|
230
|
+
parsed = JSON.parse(objects[i]);
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const candidate = normalizeCandidate(parsed);
|
|
236
|
+
if (!candidate)
|
|
237
|
+
continue;
|
|
238
|
+
if (candidate.found)
|
|
239
|
+
return candidate;
|
|
240
|
+
fallback ??= candidate;
|
|
241
|
+
}
|
|
242
|
+
return fallback;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Reject a point that cannot be on screen.
|
|
246
|
+
*
|
|
247
|
+
* The old path converted and clicked whatever came back, so a hallucinated
|
|
248
|
+
* coordinate produced a click at an arbitrary place. Bounds are checked in the
|
|
249
|
+
* candidate's own frame — image pixels when a clip was captured, otherwise the
|
|
250
|
+
* viewport scaled by device pixel ratio.
|
|
251
|
+
*/
|
|
252
|
+
export function validateCandidate(candidate, frame) {
|
|
253
|
+
if (!candidate.found)
|
|
254
|
+
return candidate;
|
|
255
|
+
const { x, y } = candidate.point;
|
|
256
|
+
const inside = x >= 0 && y >= 0 && x <= frame.width && y <= frame.height;
|
|
257
|
+
if (inside)
|
|
258
|
+
return candidate;
|
|
259
|
+
return {
|
|
260
|
+
...candidate,
|
|
261
|
+
found: false,
|
|
262
|
+
kind: 'not_found',
|
|
263
|
+
confidence: 0,
|
|
264
|
+
reason: `point (${x}, ${y}) is outside the ${frame.width}x${frame.height} capture`,
|
|
265
|
+
riskFlags: [...candidate.riskFlags, 'out_of_bounds'],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/** A candidate that must be verified before it is acted on. */
|
|
269
|
+
export function isLowConfidence(candidate, threshold = LOW_CONFIDENCE_THRESHOLD) {
|
|
270
|
+
return candidate.confidence < threshold || candidate.riskFlags.includes('point_only');
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=grounding-candidate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grounding-candidate.js","sourceRoot":"","sources":["../../../src/browser/grounding-candidate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/browser/grounding-eval.ts — scoring for the grounding harness.
|
|
3
|
+
*
|
|
4
|
+
* Every phase in this work shipped on unit evidence with the same admitted
|
|
5
|
+
* gap: the pipeline seam needs a live browser and a model spawn, so nothing
|
|
6
|
+
* measured whether grounding actually works. This is the scoring half of the
|
|
7
|
+
* thing that measures it. The runner is separate and lives in scripts/, so the
|
|
8
|
+
* arithmetic here stays testable without Chrome.
|
|
9
|
+
*
|
|
10
|
+
* The measurement that matters is NOT "did the call return success". A click
|
|
11
|
+
* that lands on the wrong element also returns success, which is the failure
|
|
12
|
+
* this whole stack was opened to address. So a case declares the element it
|
|
13
|
+
* expects, and a run is scored against what was actually clicked.
|
|
14
|
+
*
|
|
15
|
+
* Abstention is a first-class outcome, not a failure. Refusing an ambiguous
|
|
16
|
+
* target, an occluded one, or a stale observation is the pipeline working.
|
|
17
|
+
* Folding those into "failed" would make every guard look like a regression
|
|
18
|
+
* and reward a system that clicks confidently regardless.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Percentile by nearest-rank on a sorted sample.
|
|
22
|
+
*
|
|
23
|
+
* Deliberately not interpolating: with a handful of cases an interpolated p95
|
|
24
|
+
* reports a duration that never happened, which reads as precision the sample
|
|
25
|
+
* does not have.
|
|
26
|
+
*/
|
|
27
|
+
export function percentile(sortedMs, p) {
|
|
28
|
+
if (sortedMs.length === 0)
|
|
29
|
+
return 0;
|
|
30
|
+
const rank = Math.ceil((p / 100) * sortedMs.length);
|
|
31
|
+
const index = Math.min(Math.max(rank, 1), sortedMs.length) - 1;
|
|
32
|
+
return sortedMs[index];
|
|
33
|
+
}
|
|
34
|
+
export function scoreRun(results) {
|
|
35
|
+
const counts = { verified: 0, misclicked: 0, abstained: 0, groundingFailed: 0, errored: 0 };
|
|
36
|
+
const durations = [];
|
|
37
|
+
const group = () => ({ scored: 0, verified: 0, misclicked: 0, abstained: 0, groundingFailed: 0, verifiedRate: 0, misclickRate: 0 });
|
|
38
|
+
const click = group();
|
|
39
|
+
const refusal = group();
|
|
40
|
+
const errors = new Set();
|
|
41
|
+
for (const r of results) {
|
|
42
|
+
// `expected: 'abstain'` marks a case whose correct answer is a refusal.
|
|
43
|
+
const bucket = r.expected === 'abstain' ? refusal : click;
|
|
44
|
+
switch (r.outcome.kind) {
|
|
45
|
+
case 'verified':
|
|
46
|
+
counts.verified += 1;
|
|
47
|
+
bucket.verified += 1;
|
|
48
|
+
bucket.scored += 1;
|
|
49
|
+
break;
|
|
50
|
+
case 'misclick':
|
|
51
|
+
counts.misclicked += 1;
|
|
52
|
+
bucket.misclicked += 1;
|
|
53
|
+
bucket.scored += 1;
|
|
54
|
+
break;
|
|
55
|
+
case 'abstained':
|
|
56
|
+
counts.abstained += 1;
|
|
57
|
+
bucket.abstained += 1;
|
|
58
|
+
bucket.scored += 1;
|
|
59
|
+
break;
|
|
60
|
+
// A grounding failure stays in the denominator. It is a defect in
|
|
61
|
+
// the thing being measured, so excluding it would let a pipeline
|
|
62
|
+
// that grounds nothing report a clean sheet.
|
|
63
|
+
case 'grounding-failure':
|
|
64
|
+
counts.groundingFailed += 1;
|
|
65
|
+
bucket.groundingFailed += 1;
|
|
66
|
+
bucket.scored += 1;
|
|
67
|
+
break;
|
|
68
|
+
case 'errored':
|
|
69
|
+
counts.errored += 1;
|
|
70
|
+
errors.add(r.outcome.error);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
// An errored case measures the harness, not the pipeline, so its
|
|
74
|
+
// duration would skew the latency picture.
|
|
75
|
+
if (r.outcome.kind !== 'errored')
|
|
76
|
+
durations.push(r.outcome.ms);
|
|
77
|
+
}
|
|
78
|
+
// Errors are excluded from the denominator. A case the harness could not
|
|
79
|
+
// run is not evidence that grounding failed, and counting it as one would
|
|
80
|
+
// let a broken fixture look like a regression.
|
|
81
|
+
const scored = counts.verified + counts.misclicked + counts.abstained + counts.groundingFailed;
|
|
82
|
+
durations.sort((a, b) => a - b);
|
|
83
|
+
for (const g of [click, refusal]) {
|
|
84
|
+
g.verifiedRate = g.scored === 0 ? 0 : g.verified / g.scored;
|
|
85
|
+
g.misclickRate = g.scored === 0 ? 0 : g.misclicked / g.scored;
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
total: results.length,
|
|
89
|
+
scored,
|
|
90
|
+
...counts,
|
|
91
|
+
// All three headline rates are scoped to click cases. Blending them
|
|
92
|
+
// with refusal cases produced numbers that could not be read.
|
|
93
|
+
verifiedRate: click.verifiedRate,
|
|
94
|
+
misclickRate: click.misclickRate,
|
|
95
|
+
abstentionRate: click.scored === 0 ? 0 : click.abstained / click.scored,
|
|
96
|
+
latency: durations.length
|
|
97
|
+
? { p50: percentile(durations, 50), p95: percentile(durations, 95), max: durations[durations.length - 1] }
|
|
98
|
+
: null,
|
|
99
|
+
click,
|
|
100
|
+
refusal,
|
|
101
|
+
errors: [...errors],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Decide a case outcome from what the pipeline returned.
|
|
106
|
+
*
|
|
107
|
+
* The refusal codes are named rather than pattern-matched on prose, so a
|
|
108
|
+
* reworded message cannot silently reclassify a refusal as a failure.
|
|
109
|
+
*/
|
|
110
|
+
export const ABSTENTION_CODES = new Set([
|
|
111
|
+
'COMPUTER_TARGET_AMBIGUOUS',
|
|
112
|
+
'COMPUTER_TARGET_COVERED',
|
|
113
|
+
'COMPUTER_VERIFY_DISAGREED',
|
|
114
|
+
'COMPUTER_OBSERVATION_EXPIRED',
|
|
115
|
+
'COMPUTER_OBSERVATION_STALE',
|
|
116
|
+
// Declining because the geometry was only partly captured, so a single
|
|
117
|
+
// containing element could not be concluded safely. A refusal, not a
|
|
118
|
+
// failure: the pipeline knew what it did not know.
|
|
119
|
+
'COMPUTER_GEOMETRY_TRUNCATED',
|
|
120
|
+
]);
|
|
121
|
+
/**
|
|
122
|
+
* The pipeline looked and did not find it.
|
|
123
|
+
*
|
|
124
|
+
* Separate from the abstention codes above because it is a different claim:
|
|
125
|
+
* those say "I found something and declined to act", this says "there was
|
|
126
|
+
* nothing to act on". A reader chasing a missing button needs to know which.
|
|
127
|
+
*/
|
|
128
|
+
export const NOT_FOUND_CODE = 'COMPUTER_TARGET_NOT_FOUND';
|
|
129
|
+
/**
|
|
130
|
+
* The call could not be completed for reasons that have nothing to do with
|
|
131
|
+
* the page: a capture that could not be measured, a viewport that could not be
|
|
132
|
+
* read, a scale factor that was absent.
|
|
133
|
+
*
|
|
134
|
+
* These used to arrive with no code at all, which made them indistinguishable
|
|
135
|
+
* from a refusal — an infrastructure failure scored as commendable restraint,
|
|
136
|
+
* and printed to a human as a missing element.
|
|
137
|
+
*/
|
|
138
|
+
export const INFRASTRUCTURE_CODES = new Set([
|
|
139
|
+
'COMPUTER_CAPTURE_UNMEASURABLE',
|
|
140
|
+
'COMPUTER_VIEWPORT_UNAVAILABLE',
|
|
141
|
+
'COMPUTER_CAPTURE_NO_DPR',
|
|
142
|
+
]);
|
|
143
|
+
/**
|
|
144
|
+
* The pipeline produced an answer that was wrong on its face.
|
|
145
|
+
*
|
|
146
|
+
* `COMPUTER_CANDIDATE_OUT_OF_BOUNDS` fires when the model returns a point
|
|
147
|
+
* outside the frame it was shown. That is a defect in the thing being
|
|
148
|
+
* measured, not in the environment measuring it — grouping it with the
|
|
149
|
+
* capture failures would take a real grounding defect out of the denominator
|
|
150
|
+
* and blame the harness for it.
|
|
151
|
+
*/
|
|
152
|
+
export const GROUNDING_FAILURE_CODES = new Set([
|
|
153
|
+
'COMPUTER_CANDIDATE_OUT_OF_BOUNDS',
|
|
154
|
+
]);
|
|
155
|
+
export function classifyFailure(code) {
|
|
156
|
+
// An absent code and an unrecognised one are different situations. The
|
|
157
|
+
// first says the responder does not speak this protocol — an older server,
|
|
158
|
+
// most likely, since this runner talks to a separately built one over
|
|
159
|
+
// HTTP. The second says it speaks a newer dialect. Collapsing them would
|
|
160
|
+
// let a version skew silently reinstate the mis-scoring this fixes.
|
|
161
|
+
if (!code)
|
|
162
|
+
return 'uncoded';
|
|
163
|
+
if (ABSTENTION_CODES.has(code))
|
|
164
|
+
return 'abstention';
|
|
165
|
+
if (code === NOT_FOUND_CODE)
|
|
166
|
+
return 'not-found';
|
|
167
|
+
if (GROUNDING_FAILURE_CODES.has(code))
|
|
168
|
+
return 'grounding-failure';
|
|
169
|
+
if (INFRASTRUCTURE_CODES.has(code))
|
|
170
|
+
return 'infrastructure';
|
|
171
|
+
return 'unknown';
|
|
172
|
+
}
|
|
173
|
+
export function classify(response, expected, ms, clickedRef) {
|
|
174
|
+
if (response.success !== true) {
|
|
175
|
+
const kind = classifyFailure(response.code);
|
|
176
|
+
if (kind === 'abstention') {
|
|
177
|
+
return { kind: 'abstained', ms, reason: response.code };
|
|
178
|
+
}
|
|
179
|
+
if (kind === 'infrastructure') {
|
|
180
|
+
// The harness could not ask the question. That is not evidence
|
|
181
|
+
// about grounding in either direction, and scoring it as
|
|
182
|
+
// restraint made a broken capture look like good judgement.
|
|
183
|
+
return { kind: 'errored', ms, error: response.code };
|
|
184
|
+
}
|
|
185
|
+
if (kind === 'grounding-failure') {
|
|
186
|
+
return { kind: 'grounding-failure', ms, reason: response.code };
|
|
187
|
+
}
|
|
188
|
+
if (kind === 'not-found') {
|
|
189
|
+
// Expected-aware, because the same code means opposite things.
|
|
190
|
+
// On a case that should have declined, not finding the target IS
|
|
191
|
+
// the right answer. On a case where the element is present and
|
|
192
|
+
// described, failing to find it is a grounding failure — filing it
|
|
193
|
+
// as an abstention credits restraint that was never exercised and
|
|
194
|
+
// keeps it out of the misclick denominator, so a pipeline that
|
|
195
|
+
// grounds nothing reports a clean sheet.
|
|
196
|
+
return expected === 'abstain'
|
|
197
|
+
? { kind: 'abstained', ms, reason: response.code }
|
|
198
|
+
: { kind: 'grounding-failure', ms, reason: response.code };
|
|
199
|
+
}
|
|
200
|
+
if (kind === 'uncoded' && response.reason) {
|
|
201
|
+
// A `success:false` body carrying prose and no code. Every current
|
|
202
|
+
// source path names its failures, so this means the responder is
|
|
203
|
+
// older than the naming — which is exactly the case where the
|
|
204
|
+
// prose could be describing a capture failure. Scoring it as an
|
|
205
|
+
// abstention is the mistake this function was rewritten to stop,
|
|
206
|
+
// so it is surfaced as unscoreable rather than credited.
|
|
207
|
+
return { kind: 'errored', ms, error: `uncoded failure: ${response.reason}` };
|
|
208
|
+
}
|
|
209
|
+
// An unrecognised code is forward compatibility, not breakage: this
|
|
210
|
+
// build cannot judge a dialect it does not know, and hard-failing
|
|
211
|
+
// would make the harness brittle against its own future.
|
|
212
|
+
if (response.reason)
|
|
213
|
+
return { kind: 'abstained', ms, reason: response.reason };
|
|
214
|
+
return { kind: 'errored', ms, error: 'no reason given' };
|
|
215
|
+
}
|
|
216
|
+
const got = clickedRef ?? response.ref;
|
|
217
|
+
// Success without an identifiable target cannot be scored. Calling it
|
|
218
|
+
// verified would be the exact assumption this harness exists to test.
|
|
219
|
+
if (!got)
|
|
220
|
+
return { kind: 'errored', ms, error: 'clicked but no element identity was reported' };
|
|
221
|
+
return got === expected ? { kind: 'verified', ms } : { kind: 'misclick', ms, got };
|
|
222
|
+
}
|
|
223
|
+
/** One line per rate, plus the caveat the numbers need to be read with. */
|
|
224
|
+
export function formatReport(report) {
|
|
225
|
+
const pct = (n) => `${(n * 100).toFixed(1)}%`;
|
|
226
|
+
const lines = [
|
|
227
|
+
`cases ${report.total} (${report.scored} scored, ${report.errored} errored)`,
|
|
228
|
+
`verified ${report.verified} (${pct(report.verifiedRate)})`,
|
|
229
|
+
`misclicked ${report.misclicked} (${pct(report.misclickRate)})`,
|
|
230
|
+
`abstained ${report.abstained} (${pct(report.abstentionRate)} of cases that should have clicked)`,
|
|
231
|
+
];
|
|
232
|
+
if (report.groundingFailed > 0) {
|
|
233
|
+
lines.push(`not grounded ${report.groundingFailed} (the target was there and the pipeline could not find it)`);
|
|
234
|
+
}
|
|
235
|
+
if (report.latency) {
|
|
236
|
+
lines.push(`latency p50 ${report.latency.p50}ms p95 ${report.latency.p95}ms max ${report.latency.max}ms`);
|
|
237
|
+
}
|
|
238
|
+
lines.push('', `when clicking ${report.click.verified}/${report.click.scored} right, ${report.click.misclicked} wrong`, `when refusing ${report.refusal.verified}/${report.refusal.scored} correctly declined, ${report.refusal.misclicked} clicked anyway`);
|
|
239
|
+
if (report.errors.length > 0) {
|
|
240
|
+
lines.push('', 'could not be scored:');
|
|
241
|
+
for (const e of report.errors)
|
|
242
|
+
lines.push(` ${e}`);
|
|
243
|
+
}
|
|
244
|
+
lines.push('', 'An abstention is the pipeline declining, not failing. Read the misclick', 'rate first: a wrong click is worse than no click.');
|
|
245
|
+
return lines.join('\n');
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=grounding-eval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grounding-eval.js","sourceRoot":"","sources":["../../../src/browser/grounding-eval.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/browser/image-size.ts — read pixel dimensions from a capture file.
|
|
3
|
+
*
|
|
4
|
+
* Kept out of actions.ts so it can be tested without the CDP layer.
|
|
5
|
+
*
|
|
6
|
+
* This exists because the bounds check needs the frame the model actually saw.
|
|
7
|
+
* The requested clip is not that frame: Playwright trims a clip to the viewport
|
|
8
|
+
* before capture, and the route accepts an array clip whose `.width` is
|
|
9
|
+
* undefined — and comparing a number against `undefined` is always false, so
|
|
10
|
+
* such a bound looks present while checking nothing.
|
|
11
|
+
*/
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
/**
|
|
14
|
+
* Returns null when the size cannot be read. Callers must fail closed on null:
|
|
15
|
+
* a plausible-looking default would be worse than admitting it is unknown.
|
|
16
|
+
*/
|
|
17
|
+
export function imageSize(filepath) {
|
|
18
|
+
let head;
|
|
19
|
+
try {
|
|
20
|
+
const fd = fs.openSync(filepath, 'r');
|
|
21
|
+
try {
|
|
22
|
+
const buffer = Buffer.alloc(65536);
|
|
23
|
+
const read = fs.readSync(fd, buffer, 0, buffer.length, 0);
|
|
24
|
+
head = buffer.subarray(0, read);
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
fs.closeSync(fd);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
// PNG: 8-byte signature, then an IHDR chunk whose data begins at byte 16.
|
|
34
|
+
if (head.length >= 24 && head.readUInt32BE(0) === 0x89504e47) {
|
|
35
|
+
return { width: head.readUInt32BE(16), height: head.readUInt32BE(20) };
|
|
36
|
+
}
|
|
37
|
+
// JPEG: walk the segment chain to a start-of-frame marker.
|
|
38
|
+
if (head.length >= 4 && head[0] === 0xff && head[1] === 0xd8) {
|
|
39
|
+
let i = 2;
|
|
40
|
+
while (i + 9 < head.length) {
|
|
41
|
+
if (head[i] !== 0xff) {
|
|
42
|
+
i += 1;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const marker = head[i + 1];
|
|
46
|
+
// Standalone markers carry no length field.
|
|
47
|
+
if (marker === 0xd8 || marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) {
|
|
48
|
+
i += 2;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const length = head.readUInt16BE(i + 2);
|
|
52
|
+
if (length < 2)
|
|
53
|
+
return null; // malformed chain
|
|
54
|
+
// SOF0..SOF15, excluding DHT (c4), JPG (c8) and DAC (cc).
|
|
55
|
+
if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {
|
|
56
|
+
return { height: head.readUInt16BE(i + 5), width: head.readUInt16BE(i + 7) };
|
|
57
|
+
}
|
|
58
|
+
i += 2 + length;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=image-size.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-size.js","sourceRoot":"","sources":["../../../src/browser/image-size.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,QAAgB;IACtC,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACD,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;YAC1D,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QACpC,CAAC;gBAAS,CAAC;YACP,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACrB,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;IAExB,0EAA0E;IAC1E,IAAI,IAAI,CAAC,MAAM,IAAI,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,UAAU,EAAE,CAAC;QAC3D,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,CAAC;IAC3E,CAAC;IAED,2DAA2D;IAC3D,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC3D,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBAAC,CAAC,IAAI,CAAC,CAAC;gBAAC,SAAS;YAAC,CAAC;YAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAW,CAAC;YACrC,4CAA4C;YAC5C,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,CAAC,EAAE,CAAC;gBAAC,CAAC,IAAI,CAAC,CAAC;gBAAC,SAAS;YAAC,CAAC;YACnG,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACxC,IAAI,MAAM,GAAG,CAAC;gBAAE,OAAO,IAAI,CAAC,CAAC,kBAAkB;YAC/C,0DAA0D;YAC1D,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBAC5F,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACjF,CAAC;YACD,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC;QACpB,CAAC;IACL,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC"}
|
|
@@ -5,6 +5,7 @@ export { cleanupIdleTabs, isPinned, parseTabDuration, pinTab, selectTabsForClean
|
|
|
5
5
|
export * from './primitives.js';
|
|
6
6
|
export { snapshot, screenshot, click, type, press, hover, navigate, evaluate, getPageText, mouseClick, getDom, waitForSelector, waitForText, reload, resize, scroll, select, drag, mouseMove, mouseDown, mouseUp, getConsole, getNetwork, } from './actions.js';
|
|
7
7
|
export { visionClick, extractCoordinates } from './vision.js';
|
|
8
|
+
export { sanitizeTarget, appendBounded, MAX_TARGET_LENGTH, MAX_CODEX_STDOUT_BYTES } from './vision-input.js';
|
|
8
9
|
export * as adaptiveFetch from './adaptive-fetch/index.js';
|
|
9
10
|
export * as webAi from './web-ai/index.js';
|
|
10
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/browser/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EACtD,QAAQ,EAAE,gBAAgB,EAAE,YAAY,EACxC,uBAAuB,EAAE,oBAAoB,EAAE,mBAAmB,EAClE,2BAA2B,EAC3B,aAAa,EAAE,YAAY,EAAE,SAAS,EACtC,SAAS,EAAE,QAAQ,EAAE,iBAAiB,EAAE,qBAAqB,EAC7D,uBAAuB,EAAE,sBAAsB,EAC/C,aAAa,EAAE,iBAAiB,EAAE,cAAc,GACnD,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,OAAO,EAAE,4BAA4B,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAElG,OAAO,EACH,eAAe,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,oBAAoB,EAAE,QAAQ,GACtF,MAAM,oBAAoB,CAAC;AAE5B,cAAc,iBAAiB,CAAC;AAEhC,OAAO,EACH,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EACxC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EACtC,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,WAAW,EAChD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EACpC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,GACxD,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,aAAa,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,KAAK,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/browser/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EACtD,QAAQ,EAAE,gBAAgB,EAAE,YAAY,EACxC,uBAAuB,EAAE,oBAAoB,EAAE,mBAAmB,EAClE,2BAA2B,EAC3B,aAAa,EAAE,YAAY,EAAE,SAAS,EACtC,SAAS,EAAE,QAAQ,EAAE,iBAAiB,EAAE,qBAAqB,EAC7D,uBAAuB,EAAE,sBAAsB,EAC/C,aAAa,EAAE,iBAAiB,EAAE,cAAc,GACnD,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,OAAO,EAAE,4BAA4B,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAElG,OAAO,EACH,eAAe,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,oBAAoB,EAAE,QAAQ,GACtF,MAAM,oBAAoB,CAAC;AAE5B,cAAc,iBAAiB,CAAC;AAEhC,OAAO,EACH,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EACxC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EACtC,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,WAAW,EAChD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EACpC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,GACxD,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC7G,OAAO,KAAK,aAAa,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,KAAK,MAAM,mBAAmB,CAAC"}
|