autokap 1.1.3 → 1.1.5
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/browser.d.ts +1 -0
- package/dist/browser.js +16 -5
- package/dist/cli-runner.js +80 -18
- package/dist/cli.js +0 -0
- package/dist/clip-capture-loop.d.ts +21 -0
- package/dist/clip-capture-loop.js +37 -0
- package/dist/mouse-animation.d.ts +4 -1
- package/dist/mouse-animation.js +7 -3
- package/dist/web-playwright-local.js +13 -3
- package/package.json +1 -1
- package/dist/agent-action-recovery.d.ts +0 -45
- package/dist/agent-action-recovery.js +0 -370
- package/dist/agent-message-utils.d.ts +0 -21
- package/dist/agent-message-utils.js +0 -77
- package/dist/agent-url-utils.d.ts +0 -30
- package/dist/agent-url-utils.js +0 -138
- package/dist/agent.d.ts +0 -226
- package/dist/agent.js +0 -6666
- package/dist/browser-utils.d.ts +0 -31
- package/dist/browser-utils.js +0 -97
- package/dist/capture-studio-sync.d.ts +0 -23
- package/dist/capture-studio-sync.js +0 -172
- package/dist/clip-begin-frame-recorder.d.ts +0 -44
- package/dist/clip-begin-frame-recorder.js +0 -250
- package/dist/clip-capture-backend.d.ts +0 -25
- package/dist/clip-capture-backend.js +0 -189
- package/dist/clip-frame-recorder.d.ts +0 -63
- package/dist/clip-frame-recorder.js +0 -305
- package/dist/clip-orchestrator.d.ts +0 -148
- package/dist/clip-orchestrator.js +0 -957
- package/dist/clip-runtime.d.ts +0 -18
- package/dist/clip-runtime.js +0 -67
- package/dist/clip-scale.d.ts +0 -10
- package/dist/clip-scale.js +0 -21
- package/dist/clip-screencast-recorder.d.ts +0 -42
- package/dist/clip-screencast-recorder.js +0 -242
- package/dist/clip-sidecar.d.ts +0 -54
- package/dist/clip-sidecar.js +0 -208
- package/dist/cost-resolution-monitor.d.ts +0 -16
- package/dist/cost-resolution-monitor.js +0 -34
- package/dist/credential-templates.d.ts +0 -5
- package/dist/credential-templates.js +0 -60
- package/dist/dom-css-purger.d.ts +0 -65
- package/dist/dom-css-purger.js +0 -333
- package/dist/dom-font-inliner.d.ts +0 -45
- package/dist/dom-font-inliner.js +0 -148
- package/dist/dom-patch-resolver.d.ts +0 -52
- package/dist/dom-patch-resolver.js +0 -242
- package/dist/dom-serializer.d.ts +0 -82
- package/dist/dom-serializer.js +0 -378
- package/dist/element-capture.d.ts +0 -13
- package/dist/element-capture.js +0 -522
- package/dist/fonts-loader.d.ts +0 -14
- package/dist/fonts-loader.js +0 -55
- package/dist/hybrid-navigator.d.ts +0 -138
- package/dist/hybrid-navigator.js +0 -468
- package/dist/legacy/agent-action-recovery.d.ts +0 -45
- package/dist/legacy/agent-action-recovery.js +0 -370
- package/dist/legacy/agent-message-utils.d.ts +0 -21
- package/dist/legacy/agent-message-utils.js +0 -77
- package/dist/legacy/agent-url-utils.d.ts +0 -30
- package/dist/legacy/agent-url-utils.js +0 -138
- package/dist/legacy/agent.d.ts +0 -226
- package/dist/legacy/agent.js +0 -6666
- package/dist/legacy/clip-orchestrator.d.ts +0 -148
- package/dist/legacy/clip-orchestrator.js +0 -957
- package/dist/legacy/credential-templates.d.ts +0 -5
- package/dist/legacy/credential-templates.js +0 -60
- package/dist/legacy/hybrid-navigator.d.ts +0 -138
- package/dist/legacy/hybrid-navigator.js +0 -468
- package/dist/legacy/llm-usage.d.ts +0 -17
- package/dist/legacy/llm-usage.js +0 -45
- package/dist/legacy/prompt-cache.d.ts +0 -10
- package/dist/legacy/prompt-cache.js +0 -24
- package/dist/legacy/prompts.d.ts +0 -175
- package/dist/legacy/prompts.js +0 -1038
- package/dist/legacy/tools.d.ts +0 -4
- package/dist/legacy/tools.js +0 -216
- package/dist/legacy/video-agent.d.ts +0 -143
- package/dist/legacy/video-agent.js +0 -4788
- package/dist/legacy/video-observation.d.ts +0 -36
- package/dist/legacy/video-observation.js +0 -192
- package/dist/legacy/video-planner.d.ts +0 -12
- package/dist/legacy/video-planner.js +0 -501
- package/dist/legacy/video-prompts.d.ts +0 -37
- package/dist/legacy/video-prompts.js +0 -569
- package/dist/legacy/video-tools.d.ts +0 -3
- package/dist/legacy/video-tools.js +0 -59
- package/dist/legacy/video-variant-state.d.ts +0 -29
- package/dist/legacy/video-variant-state.js +0 -80
- package/dist/legacy/vision-model.d.ts +0 -17
- package/dist/legacy/vision-model.js +0 -74
- package/dist/llm-usage.d.ts +0 -17
- package/dist/llm-usage.js +0 -45
- package/dist/overlay-utils.d.ts +0 -14
- package/dist/overlay-utils.js +0 -13
- package/dist/prompt-cache.d.ts +0 -10
- package/dist/prompt-cache.js +0 -24
- package/dist/prompts.d.ts +0 -175
- package/dist/prompts.js +0 -1038
- package/dist/remote-browser.d.ts +0 -215
- package/dist/remote-browser.js +0 -360
- package/dist/svg-browser-bar.d.ts +0 -33
- package/dist/svg-browser-bar.js +0 -206
- package/dist/svg-status-bar.d.ts +0 -36
- package/dist/svg-status-bar.js +0 -597
- package/dist/svg-text.d.ts +0 -61
- package/dist/svg-text.js +0 -118
- package/dist/tools.d.ts +0 -4
- package/dist/tools.js +0 -216
- package/dist/v2/action-verifier.d.ts +0 -29
- package/dist/v2/action-verifier.js +0 -133
- package/dist/v2/alt-text.d.ts +0 -26
- package/dist/v2/alt-text.js +0 -55
- package/dist/v2/benchmark.d.ts +0 -59
- package/dist/v2/benchmark.js +0 -135
- package/dist/v2/capture-strategy.d.ts +0 -30
- package/dist/v2/capture-strategy.js +0 -67
- package/dist/v2/capture-verification.d.ts +0 -35
- package/dist/v2/capture-verification.js +0 -95
- package/dist/v2/circuit-breaker.d.ts +0 -42
- package/dist/v2/circuit-breaker.js +0 -119
- package/dist/v2/cli-runner-local.d.ts +0 -11
- package/dist/v2/cli-runner-local.js +0 -91
- package/dist/v2/cli-runner.d.ts +0 -34
- package/dist/v2/cli-runner.js +0 -300
- package/dist/v2/compiler-prompts.d.ts +0 -27
- package/dist/v2/compiler-prompts.js +0 -123
- package/dist/v2/compiler.d.ts +0 -37
- package/dist/v2/compiler.js +0 -147
- package/dist/v2/explorer.d.ts +0 -41
- package/dist/v2/explorer.js +0 -56
- package/dist/v2/index.d.ts +0 -37
- package/dist/v2/index.js +0 -31
- package/dist/v2/llm-healer.d.ts +0 -62
- package/dist/v2/llm-healer.js +0 -166
- package/dist/v2/llm-provider.d.ts +0 -29
- package/dist/v2/llm-provider.js +0 -80
- package/dist/v2/opcode-runner.d.ts +0 -47
- package/dist/v2/opcode-runner.js +0 -634
- package/dist/v2/overlay-engine.d.ts +0 -24
- package/dist/v2/overlay-engine.js +0 -150
- package/dist/v2/postcondition.d.ts +0 -16
- package/dist/v2/postcondition.js +0 -249
- package/dist/v2/program-patcher.d.ts +0 -25
- package/dist/v2/program-patcher.js +0 -44
- package/dist/v2/recovery-chain.d.ts +0 -30
- package/dist/v2/recovery-chain.js +0 -368
- package/dist/v2/schema.d.ts +0 -2580
- package/dist/v2/schema.js +0 -295
- package/dist/v2/selector-resolver.d.ts +0 -34
- package/dist/v2/selector-resolver.js +0 -181
- package/dist/v2/semantic-resolver.d.ts +0 -35
- package/dist/v2/semantic-resolver.js +0 -161
- package/dist/v2/smart-wait.d.ts +0 -27
- package/dist/v2/smart-wait.js +0 -81
- package/dist/v2/types.d.ts +0 -444
- package/dist/v2/types.js +0 -19
- package/dist/v2/web-playwright-local.d.ts +0 -69
- package/dist/v2/web-playwright-local.js +0 -392
- package/dist/video-agent.d.ts +0 -143
- package/dist/video-agent.js +0 -4788
- package/dist/video-observation.d.ts +0 -36
- package/dist/video-observation.js +0 -192
- package/dist/video-planner.d.ts +0 -12
- package/dist/video-planner.js +0 -501
- package/dist/video-prompts.d.ts +0 -37
- package/dist/video-prompts.js +0 -554
- package/dist/video-tools.d.ts +0 -3
- package/dist/video-tools.js +0 -59
- package/dist/video-variant-state.d.ts +0 -29
- package/dist/video-variant-state.js +0 -80
- package/dist/vision-model.d.ts +0 -17
- package/dist/vision-model.js +0 -74
- package/dist/ws-auth.d.ts +0 -20
- package/dist/ws-auth.js +0 -70
- package/dist/ws-broadcast.d.ts +0 -34
- package/dist/ws-broadcast.js +0 -85
- package/dist/ws-connection-limits.d.ts +0 -12
- package/dist/ws-connection-limits.js +0 -44
- package/dist/ws-handler-utils.d.ts +0 -32
- package/dist/ws-handler-utils.js +0 -139
- package/dist/ws-handler.d.ts +0 -10
- package/dist/ws-handler.js +0 -1793
- package/dist/ws-metrics-server.d.ts +0 -9
- package/dist/ws-metrics-server.js +0 -31
- package/dist/ws-server.d.ts +0 -9
- package/dist/ws-server.js +0 -92
|
@@ -1,370 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Action recovery, guard inference, and login detection utilities for the capture agent.
|
|
3
|
-
* Extracted from agent.ts — these are pure functions with no closure dependencies.
|
|
4
|
-
*/
|
|
5
|
-
import { urlsRoughlyMatch, buildClickGuardSignature, } from './agent-url-utils.js';
|
|
6
|
-
// -- Constants --
|
|
7
|
-
export const META_ACTIONS = new Set(['note', 'begin_subgoal', 'focus', 'capture', 'analyze_screenshot']);
|
|
8
|
-
export const BOOTSTRAP_ACTIONS = new Set(['wait']);
|
|
9
|
-
const REPEAT_GUARD_ACTIONS = new Set([
|
|
10
|
-
'tap',
|
|
11
|
-
'press_key',
|
|
12
|
-
]);
|
|
13
|
-
export const REPLAYABLE_ACTIONS = [
|
|
14
|
-
'navigate_to', 'tap', 'type', 'scroll',
|
|
15
|
-
'press_key', 'wait',
|
|
16
|
-
];
|
|
17
|
-
const LOGIN_URL_RE = /\b(login|log-in|signin|sign-in|auth|session|connexion|connect)\b/i;
|
|
18
|
-
const LOGIN_FIELD_RE = /\b(password|mot de passe|passcode|otp|verification.?code)\b/i;
|
|
19
|
-
const LOGIN_CLICK_RE = /\b(login|log-in|signin|sign-in|auth|session|connexion|connect|password|mot de passe|passcode|otp|verification.?code|email|e-mail)\b/i;
|
|
20
|
-
const AUTH_SUBMIT_RE = /\b(continue|next|submit|verify|unlock|access|enter|continuer|suivant|soumettre|verifier|v[eé]rifier|acc[eé]der|entrer)\b/i;
|
|
21
|
-
const HARD_GIVE_UP_RE = /\b(5xx|500\b|404\b|page not found|not found|http error|server error|js crash|javascript crash|browser crashed|connection refused|dns|net::|ssl|certificate|blank page|white page|no content|infinite spinner)\b/i;
|
|
22
|
-
const RECOVERABLE_GIVE_UP_RE = /\b(verification|ready_to_capture|dialog|modal|overlay|gallery|editor|route|navigation|assistant|conversation|wrong page|duplicate capture|capture target)\b/i;
|
|
23
|
-
// -- Internal helpers --
|
|
24
|
-
export function isNoEffectAction(action) {
|
|
25
|
-
return (action.success === false
|
|
26
|
-
|| action.stateChanged === false
|
|
27
|
-
|| action.outcome === 'No visible state change detected after the action.');
|
|
28
|
-
}
|
|
29
|
-
export function isBootstrapStabilizationAction(action) {
|
|
30
|
-
if (BOOTSTRAP_ACTIONS.has(action.action))
|
|
31
|
-
return true;
|
|
32
|
-
return action.action === 'press_key' && String(action.params.key || '').toLowerCase() === 'escape';
|
|
33
|
-
}
|
|
34
|
-
export function hasMeaningfulBrowserAction(actionHistory) {
|
|
35
|
-
return actionHistory.some((action) => !META_ACTIONS.has(action.action) && !isBootstrapStabilizationAction(action));
|
|
36
|
-
}
|
|
37
|
-
export function buildRecoveryActionSignature(action) {
|
|
38
|
-
const parts = [action.action];
|
|
39
|
-
if (typeof action.params.nodeId === 'string' && action.params.nodeId.trim()) {
|
|
40
|
-
parts.push(`nodeId:${action.params.nodeId.trim()}`);
|
|
41
|
-
}
|
|
42
|
-
else if (typeof action.params.selector === 'string' && action.params.selector.trim()) {
|
|
43
|
-
parts.push(`selector:${action.params.selector.trim()}`);
|
|
44
|
-
}
|
|
45
|
-
else if (action.params.index !== undefined) {
|
|
46
|
-
parts.push(`index:${String(action.params.index)}`);
|
|
47
|
-
}
|
|
48
|
-
else if (typeof action.params.url === 'string' && action.params.url.trim()) {
|
|
49
|
-
parts.push(`url:${action.params.url.trim().slice(0, 160)}`);
|
|
50
|
-
}
|
|
51
|
-
else if (action.params.x !== undefined && action.params.y !== undefined) {
|
|
52
|
-
parts.push(`xy:${String(action.params.x)},${String(action.params.y)}`);
|
|
53
|
-
}
|
|
54
|
-
if (typeof action.params.text === 'string' && action.params.text.trim()) {
|
|
55
|
-
parts.push(`text:${action.params.text.trim().slice(0, 80)}`);
|
|
56
|
-
}
|
|
57
|
-
if (typeof action.params.key === 'string' && action.params.key.trim()) {
|
|
58
|
-
parts.push(`key:${action.params.key.trim()}`);
|
|
59
|
-
}
|
|
60
|
-
return parts.join('|');
|
|
61
|
-
}
|
|
62
|
-
function getMeaningfulBrowserActions(actionHistory) {
|
|
63
|
-
return actionHistory.filter(action => !META_ACTIONS.has(action.action) && !isBootstrapStabilizationAction(action));
|
|
64
|
-
}
|
|
65
|
-
function countDistinctActionSignatures(actionHistory) {
|
|
66
|
-
return new Set(getMeaningfulBrowserActions(actionHistory).map(action => buildRecoveryActionSignature(action))).size;
|
|
67
|
-
}
|
|
68
|
-
// -- Login detection --
|
|
69
|
-
export function isExplicitLoginAction(action) {
|
|
70
|
-
if (action.action === 'navigate_to' && typeof action.params.url === 'string' && LOGIN_URL_RE.test(action.params.url)) {
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
if ((action.action === 'type_text' || action.action === 'type') && typeof action.params.selector === 'string' && LOGIN_FIELD_RE.test(action.params.selector)) {
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
if ((action.action === 'type_text' || action.action === 'type') && typeof action.params.text === 'string' && /\{\{credential\./i.test(action.params.text)) {
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
if (action.action === 'click' || action.action === 'tap') {
|
|
80
|
-
const haystack = [
|
|
81
|
-
action.params.selector,
|
|
82
|
-
action.params.elementLabel,
|
|
83
|
-
action.params.href,
|
|
84
|
-
action.params.nodeId,
|
|
85
|
-
]
|
|
86
|
-
.filter((value) => typeof value === 'string')
|
|
87
|
-
.join(' ');
|
|
88
|
-
if (LOGIN_CLICK_RE.test(haystack)) {
|
|
89
|
-
return true;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
export function hasRecentExplicitLoginAction(previousActions) {
|
|
95
|
-
return previousActions.slice(-3).some(isExplicitLoginAction);
|
|
96
|
-
}
|
|
97
|
-
export function isLoginAction(action, previousActions = []) {
|
|
98
|
-
if (isExplicitLoginAction(action)) {
|
|
99
|
-
return true;
|
|
100
|
-
}
|
|
101
|
-
if (action.action === 'click' || action.action === 'tap') {
|
|
102
|
-
const haystack = [
|
|
103
|
-
action.params.selector,
|
|
104
|
-
action.params.elementLabel,
|
|
105
|
-
action.params.href,
|
|
106
|
-
action.params.nodeId,
|
|
107
|
-
]
|
|
108
|
-
.filter((value) => typeof value === 'string')
|
|
109
|
-
.join(' ');
|
|
110
|
-
if (AUTH_SUBMIT_RE.test(haystack) && hasRecentExplicitLoginAction(previousActions)) {
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
if (action.action === 'press_key'
|
|
115
|
-
&& action.params.key === 'Enter'
|
|
116
|
-
&& hasRecentExplicitLoginAction(previousActions)) {
|
|
117
|
-
return true;
|
|
118
|
-
}
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
export function compactReplayActions(recordedActions, params = {}) {
|
|
122
|
-
let replayable = recordedActions.filter(a => REPLAYABLE_ACTIONS.includes(a.action));
|
|
123
|
-
if (params.ignoreRecordedViewport) {
|
|
124
|
-
replayable = replayable.filter((action) => action.action !== 'resize_viewport');
|
|
125
|
-
}
|
|
126
|
-
if (params.isAuthenticated) {
|
|
127
|
-
const authAwareReplayable = replayable;
|
|
128
|
-
replayable = authAwareReplayable.filter((action, index) => !isLoginAction(action, authAwareReplayable.slice(0, index)));
|
|
129
|
-
}
|
|
130
|
-
let startIndex = 0;
|
|
131
|
-
while (startIndex < replayable.length) {
|
|
132
|
-
const action = replayable[startIndex];
|
|
133
|
-
if (action.action === 'wait') {
|
|
134
|
-
startIndex += 1;
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
if (action.action === 'dismiss_overlays') {
|
|
138
|
-
startIndex += 1;
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
if (action.action === 'resize_viewport'
|
|
142
|
-
&& params.currentViewport
|
|
143
|
-
&& Number(action.params.width) === params.currentViewport.width
|
|
144
|
-
&& Number(action.params.height) === params.currentViewport.height) {
|
|
145
|
-
startIndex += 1;
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
if (action.action === 'navigate_to'
|
|
149
|
-
&& typeof action.params.url === 'string'
|
|
150
|
-
&& (urlsRoughlyMatch(action.params.url, params.currentUrl)
|
|
151
|
-
|| urlsRoughlyMatch(action.params.url, params.targetUrl))) {
|
|
152
|
-
startIndex += 1;
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
return replayable.slice(startIndex);
|
|
158
|
-
}
|
|
159
|
-
// -- Recovery --
|
|
160
|
-
export function countRecentNoEffectActions(actionHistory) {
|
|
161
|
-
let count = 0;
|
|
162
|
-
for (let index = actionHistory.length - 1; index >= 0; index -= 1) {
|
|
163
|
-
const action = actionHistory[index];
|
|
164
|
-
if (META_ACTIONS.has(action.action))
|
|
165
|
-
continue;
|
|
166
|
-
if (isNoEffectAction(action)) {
|
|
167
|
-
count += 1;
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
break;
|
|
171
|
-
}
|
|
172
|
-
return count;
|
|
173
|
-
}
|
|
174
|
-
export function shouldTriggerRecovery(actionHistory) {
|
|
175
|
-
const browserActions = actionHistory.filter(a => !META_ACTIONS.has(a.action));
|
|
176
|
-
if (browserActions.length < 2)
|
|
177
|
-
return false;
|
|
178
|
-
if (!hasMeaningfulBrowserAction(browserActions))
|
|
179
|
-
return false;
|
|
180
|
-
const last = browserActions[browserActions.length - 1];
|
|
181
|
-
const previous = browserActions[browserActions.length - 2];
|
|
182
|
-
const sameFailureSignature = !last.success
|
|
183
|
-
&& !previous.success
|
|
184
|
-
&& buildRecoveryActionSignature(last) === buildRecoveryActionSignature(previous)
|
|
185
|
-
&& String(last.error || '').slice(0, 120) === String(previous.error || '').slice(0, 120);
|
|
186
|
-
if (browserActions.length >= 4) {
|
|
187
|
-
const [a, b, c, d] = browserActions.slice(-4);
|
|
188
|
-
const sigA = buildRecoveryActionSignature(a);
|
|
189
|
-
const sigB = buildRecoveryActionSignature(b);
|
|
190
|
-
const sigC = buildRecoveryActionSignature(c);
|
|
191
|
-
const sigD = buildRecoveryActionSignature(d);
|
|
192
|
-
if ([a, b, c, d].every(isNoEffectAction)
|
|
193
|
-
&& sigA === sigC
|
|
194
|
-
&& sigB === sigD
|
|
195
|
-
&& sigA !== sigB) {
|
|
196
|
-
return true;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
return sameFailureSignature || countRecentNoEffectActions(actionHistory) >= 2;
|
|
200
|
-
}
|
|
201
|
-
// -- Guard inference --
|
|
202
|
-
export function inferPrematureGiveUpCorrection(params) {
|
|
203
|
-
const reason = params.reason.trim();
|
|
204
|
-
const lastVerificationFailure = params.lastVerificationFailure?.trim();
|
|
205
|
-
if (HARD_GIVE_UP_RE.test(reason) || (lastVerificationFailure && HARD_GIVE_UP_RE.test(lastVerificationFailure))) {
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
const recentCaptureFailures = params.actionHistory
|
|
209
|
-
.filter(a => a.action === 'ready_to_capture' && !a.success && a.error)
|
|
210
|
-
.slice(-6);
|
|
211
|
-
if (recentCaptureFailures.length >= 3) {
|
|
212
|
-
const distinctReasons = new Set(recentCaptureFailures.map(a => (a.error || '').replace(/^Verification failed:\s*/i, '').trim().slice(0, 120)));
|
|
213
|
-
if (distinctReasons.size >= 2 && distinctReasons.size <= 3) {
|
|
214
|
-
return null;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
if (countRecentNoEffectActions(params.actionHistory) >= 8) {
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
if (lastVerificationFailure && RECOVERABLE_GIVE_UP_RE.test(lastVerificationFailure)) {
|
|
221
|
-
return `Do not give up yet. The last verification failure is still recoverable: ${lastVerificationFailure}. Try a materially different navigation or repair step first.`;
|
|
222
|
-
}
|
|
223
|
-
const hasTriedSearch = params.actionHistory.some(a => a.action === 'search_text');
|
|
224
|
-
const hasTriedNavigate = params.actionHistory.some(a => a.action === 'navigate_to');
|
|
225
|
-
if (!hasTriedSearch && !hasTriedNavigate) {
|
|
226
|
-
return 'Do not give up yet. You have not tried search_text or navigate_to. Use search_text to find the element by text, or navigate_to to go directly to the target URL.';
|
|
227
|
-
}
|
|
228
|
-
const meaningfulActions = getMeaningfulBrowserActions(params.actionHistory);
|
|
229
|
-
const distinctActionCount = countDistinctActionSignatures(params.actionHistory);
|
|
230
|
-
const hasTriedEnoughStrategies = meaningfulActions.length >= 4 && distinctActionCount >= 3;
|
|
231
|
-
const nearingBudget = params.iteration >= Math.max(6, params.maxIterations - 2);
|
|
232
|
-
if (!hasTriedEnoughStrategies && !nearingBudget) {
|
|
233
|
-
return 'Do not give up yet. You have not tried enough materially different actions. Change strategy before giving up.';
|
|
234
|
-
}
|
|
235
|
-
if (/click.*no effect|no.*effect.*click|clicking.*no/i.test(reason) && !hasTriedNavigate) {
|
|
236
|
-
return 'Clicking has no effect, but you have not tried navigate_to. Use navigate_to to go directly to the target URL instead of clicking.';
|
|
237
|
-
}
|
|
238
|
-
if (RECOVERABLE_GIVE_UP_RE.test(reason) && !nearingBudget) {
|
|
239
|
-
return 'Do not give up yet. The current issue still looks recoverable. Try a different navigation, search, or repair approach first.';
|
|
240
|
-
}
|
|
241
|
-
return null;
|
|
242
|
-
}
|
|
243
|
-
function normalizeSearchLoopQuery(value) {
|
|
244
|
-
return typeof value === 'string'
|
|
245
|
-
? value.trim().toLowerCase().replace(/\s+/g, ' ')
|
|
246
|
-
: '';
|
|
247
|
-
}
|
|
248
|
-
function getTrailingSearchScrollActions(actionHistory) {
|
|
249
|
-
const trailing = [];
|
|
250
|
-
for (let index = actionHistory.length - 1; index >= 0; index -= 1) {
|
|
251
|
-
const action = actionHistory[index];
|
|
252
|
-
if (META_ACTIONS.has(action.action))
|
|
253
|
-
continue;
|
|
254
|
-
if (action.action !== 'search_text' && action.action !== 'scroll')
|
|
255
|
-
break;
|
|
256
|
-
trailing.unshift(action);
|
|
257
|
-
}
|
|
258
|
-
return trailing;
|
|
259
|
-
}
|
|
260
|
-
export function inferSearchScrollLoopGuard(params) {
|
|
261
|
-
if (params.action !== 'search_text' && params.action !== 'scroll')
|
|
262
|
-
return null;
|
|
263
|
-
const candidate = {
|
|
264
|
-
iteration: 0,
|
|
265
|
-
action: params.action,
|
|
266
|
-
params: params.args,
|
|
267
|
-
success: params.action === 'scroll',
|
|
268
|
-
stateChanged: params.action === 'scroll',
|
|
269
|
-
};
|
|
270
|
-
const trailingLoopActions = [...getTrailingSearchScrollActions(params.actionHistory), candidate];
|
|
271
|
-
if (trailingLoopActions.length < 4)
|
|
272
|
-
return null;
|
|
273
|
-
if (!trailingLoopActions.every((action) => action.action === 'search_text' || action.action === 'scroll')) {
|
|
274
|
-
return null;
|
|
275
|
-
}
|
|
276
|
-
const repeatedQueries = new Map();
|
|
277
|
-
for (const action of trailingLoopActions) {
|
|
278
|
-
if (action.action !== 'search_text')
|
|
279
|
-
continue;
|
|
280
|
-
const query = normalizeSearchLoopQuery(action.params.query);
|
|
281
|
-
if (!query)
|
|
282
|
-
continue;
|
|
283
|
-
repeatedQueries.set(query, (repeatedQueries.get(query) ?? 0) + 1);
|
|
284
|
-
}
|
|
285
|
-
const repeatedQueryEntry = Array.from(repeatedQueries.entries())
|
|
286
|
-
.sort((a, b) => b[1] - a[1])[0];
|
|
287
|
-
if (!repeatedQueryEntry || repeatedQueryEntry[1] < 2)
|
|
288
|
-
return null;
|
|
289
|
-
const [repeatedQuery, repeatedCount] = repeatedQueryEntry;
|
|
290
|
-
if (params.action === 'search_text') {
|
|
291
|
-
const candidateQuery = normalizeSearchLoopQuery(params.args.query);
|
|
292
|
-
if (candidateQuery !== repeatedQuery || repeatedCount < 3)
|
|
293
|
-
return null;
|
|
294
|
-
return `BLOCKED: You have already searched for "${repeatedQuery}" repeatedly and only scrolled around without interacting. Stop looping on the same query. Try a different search term, navigate_to the target page, or interact with a concrete result instead.`;
|
|
295
|
-
}
|
|
296
|
-
return `BLOCKED: You are stuck in a search_text/scroll loop around "${repeatedQuery}" without acting on a result. Stop scrolling blindly. Try a different query, navigate_to the correct page, or interact with a specific matching control.`;
|
|
297
|
-
}
|
|
298
|
-
export function inferRepeatedActionGuard(params) {
|
|
299
|
-
if (params.action === 'navigate_to' && typeof params.args.url === 'string' && params.currentUrl) {
|
|
300
|
-
const targetUrl = params.args.url.replace(/\/$/, '');
|
|
301
|
-
const recentNavigations = params.actionHistory
|
|
302
|
-
.filter(a => !META_ACTIONS.has(a.action) && a.success !== false)
|
|
303
|
-
.slice(-5);
|
|
304
|
-
if (urlsRoughlyMatch(params.currentUrl, targetUrl)) {
|
|
305
|
-
return 'WARNING: You are already on this URL. No navigation needed. Focus on interacting with the current page instead.';
|
|
306
|
-
}
|
|
307
|
-
const recentAllActions = params.actionHistory
|
|
308
|
-
.filter(a => !META_ACTIONS.has(a.action))
|
|
309
|
-
.slice(-4);
|
|
310
|
-
const hasRecentNoEffect = recentAllActions.some(a => isNoEffectAction(a));
|
|
311
|
-
if (!hasRecentNoEffect) {
|
|
312
|
-
const recentUrls = recentNavigations
|
|
313
|
-
.filter(a => typeof a.params.href === 'string' || typeof a.params.url === 'string')
|
|
314
|
-
.slice(-3)
|
|
315
|
-
.map(a => String(a.params.href || a.params.url || '').replace(/\/$/, ''));
|
|
316
|
-
const wasRecentlyOnTarget = recentUrls.some(url => urlsRoughlyMatch(url, targetUrl));
|
|
317
|
-
if (wasRecentlyOnTarget && !urlsRoughlyMatch(params.currentUrl, targetUrl)) {
|
|
318
|
-
return `WARNING: You just navigated away from ${targetUrl} — going back suggests you are confused about the goal. Re-read the <task>/<goal> and <variant_manifest> carefully. If you need a modal, open it from the current page instead of navigating back.`;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
if (!REPEAT_GUARD_ACTIONS.has(params.action))
|
|
323
|
-
return null;
|
|
324
|
-
const recentBrowserActions = params.actionHistory
|
|
325
|
-
.filter(action => !META_ACTIONS.has(action.action))
|
|
326
|
-
.slice(-2);
|
|
327
|
-
if (recentBrowserActions.length < 2)
|
|
328
|
-
return null;
|
|
329
|
-
const candidateSignature = buildRecoveryActionSignature({
|
|
330
|
-
iteration: 0,
|
|
331
|
-
action: params.action,
|
|
332
|
-
params: params.args,
|
|
333
|
-
success: false,
|
|
334
|
-
});
|
|
335
|
-
const candidateClickSignature = params.action === 'click'
|
|
336
|
-
? buildClickGuardSignature(params.args, params.currentUrl)
|
|
337
|
-
: null;
|
|
338
|
-
const repeatedNoEffect = recentBrowserActions.every(action => isNoEffectAction(action)
|
|
339
|
-
&& (candidateClickSignature && action.action === 'click'
|
|
340
|
-
? buildClickGuardSignature(action.params, params.currentUrl) === candidateClickSignature
|
|
341
|
-
: buildRecoveryActionSignature(action) === candidateSignature));
|
|
342
|
-
if (repeatedNoEffect) {
|
|
343
|
-
return 'BLOCKED: The previous attempts on this same target had no effect. Try a different control, search_text, scrolling, or a repair step instead of repeating it.';
|
|
344
|
-
}
|
|
345
|
-
const recentInteractions = params.actionHistory
|
|
346
|
-
.filter(action => REPEAT_GUARD_ACTIONS.has(action.action)
|
|
347
|
-
&& !META_ACTIONS.has(action.action)
|
|
348
|
-
&& !(typeof action.error === 'string' && action.error.startsWith('BLOCKED:')))
|
|
349
|
-
.slice(-4);
|
|
350
|
-
if (recentInteractions.length >= 4 && recentInteractions.every(a => isNoEffectAction(a))) {
|
|
351
|
-
return 'BLOCKED: The last 4 interaction actions all failed or had no visible effect. You are stuck. Step back and reconsider: verify the current URL, check if you are on the right page, try navigate_to to reach the correct page, or call give_up if this capture is impossible.';
|
|
352
|
-
}
|
|
353
|
-
if (params.action === 'click') {
|
|
354
|
-
const sig = buildClickGuardSignature(params.args, params.currentUrl);
|
|
355
|
-
if (sig) {
|
|
356
|
-
const candidateTarget = String((typeof params.args.elementLabel === 'string' && params.args.elementLabel.trim())
|
|
357
|
-
? params.args.elementLabel
|
|
358
|
-
: params.args.selector ?? params.args.index ?? 'this target');
|
|
359
|
-
const recentClicks = params.actionHistory
|
|
360
|
-
.filter(a => a.action === 'click' && !META_ACTIONS.has(a.action))
|
|
361
|
-
.slice(-6);
|
|
362
|
-
const sameTargetCount = recentClicks.filter(a => buildClickGuardSignature(a.params, params.currentUrl) === sig).length;
|
|
363
|
-
if (sameTargetCount >= 3) {
|
|
364
|
-
return `BLOCKED: You have clicked "${candidateTarget}" ${sameTargetCount} times recently without progress. This is likely a toggle/multi-select control. Press Escape to close any open dropdown, then look for a DIFFERENT button (e.g., an edit/settings icon) to achieve your goal.`;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
return null;
|
|
369
|
-
}
|
|
370
|
-
//# sourceMappingURL=agent-action-recovery.js.map
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Message compression and conversation history utilities for the capture agent.
|
|
3
|
-
* Extracted from agent.ts — these are pure functions with no closure dependencies.
|
|
4
|
-
*/
|
|
5
|
-
import type { ChatCompletionMessageParam } from 'openai/resources/chat/completions';
|
|
6
|
-
/**
|
|
7
|
-
* Remove old screenshots from the conversation history, keeping only the most recent ones.
|
|
8
|
-
* Replaces image_url parts with a compact text placeholder.
|
|
9
|
-
*/
|
|
10
|
-
export declare function compressOldScreenshots(messages: ChatCompletionMessageParam[], keepRecentImages?: number): void;
|
|
11
|
-
/**
|
|
12
|
-
* Strip <page_dom>...</page_dom> blocks from old user messages to reduce token count.
|
|
13
|
-
* Keeps the DOM in the most recent `keepRecentWithDom` user messages intact.
|
|
14
|
-
*/
|
|
15
|
-
export declare function compressOldDomBlocks(messages: ChatCompletionMessageParam[], keepRecentWithDom?: number, preservedPrefixMessages?: number): void;
|
|
16
|
-
/**
|
|
17
|
-
* Trim the conversation thread to avoid context window overflow.
|
|
18
|
-
* Always preserves the first `preservedPrefixMessages` entries, then the last maxMessages messages.
|
|
19
|
-
* Also compresses old screenshots and DOM blocks to save tokens.
|
|
20
|
-
*/
|
|
21
|
-
export declare function trimConversationHistory(messages: ChatCompletionMessageParam[], maxMessages?: number, preservedPrefixMessages?: number): void;
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Message compression and conversation history utilities for the capture agent.
|
|
3
|
-
* Extracted from agent.ts — these are pure functions with no closure dependencies.
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Remove old screenshots from the conversation history, keeping only the most recent ones.
|
|
7
|
-
* Replaces image_url parts with a compact text placeholder.
|
|
8
|
-
*/
|
|
9
|
-
export function compressOldScreenshots(messages, keepRecentImages = 3) {
|
|
10
|
-
let imageCount = 0;
|
|
11
|
-
for (let i = messages.length - 1; i >= 1; i--) {
|
|
12
|
-
const msg = messages[i];
|
|
13
|
-
if (msg.role !== 'user' || !Array.isArray(msg.content))
|
|
14
|
-
continue;
|
|
15
|
-
const content = msg.content;
|
|
16
|
-
const hasImage = content.some(p => p.type === 'image_url');
|
|
17
|
-
if (!hasImage)
|
|
18
|
-
continue;
|
|
19
|
-
imageCount++;
|
|
20
|
-
if (imageCount <= keepRecentImages)
|
|
21
|
-
continue;
|
|
22
|
-
msg.content = content.map(p => p.type === 'image_url'
|
|
23
|
-
? { type: 'text', text: '[screenshot removed — older context]' }
|
|
24
|
-
: p);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Strip <page_dom>...</page_dom> blocks from old user messages to reduce token count.
|
|
29
|
-
* Keeps the DOM in the most recent `keepRecentWithDom` user messages intact.
|
|
30
|
-
*/
|
|
31
|
-
export function compressOldDomBlocks(messages, keepRecentWithDom = 6, preservedPrefixMessages = 1) {
|
|
32
|
-
const PAGE_DOM_RE = /<page_dom>[\s\S]*?<\/page_dom>/g;
|
|
33
|
-
const PLACEHOLDER = '<page_dom>[older context — see current iteration for latest DOM]</page_dom>';
|
|
34
|
-
let domMessageCount = 0;
|
|
35
|
-
for (let i = messages.length - 1; i >= preservedPrefixMessages; i--) {
|
|
36
|
-
const msg = messages[i];
|
|
37
|
-
if (msg.role !== 'user')
|
|
38
|
-
continue;
|
|
39
|
-
const contentArr = Array.isArray(msg.content) ? msg.content : null;
|
|
40
|
-
const hasDom = contentArr
|
|
41
|
-
? contentArr.some(p => typeof p === 'object' && 'text' in p && typeof p.text === 'string' && PAGE_DOM_RE.test(p.text))
|
|
42
|
-
: typeof msg.content === 'string' && PAGE_DOM_RE.test(msg.content);
|
|
43
|
-
PAGE_DOM_RE.lastIndex = 0;
|
|
44
|
-
if (!hasDom)
|
|
45
|
-
continue;
|
|
46
|
-
domMessageCount++;
|
|
47
|
-
if (domMessageCount <= keepRecentWithDom)
|
|
48
|
-
continue;
|
|
49
|
-
if (contentArr) {
|
|
50
|
-
for (const part of contentArr) {
|
|
51
|
-
if (typeof part === 'object' && 'text' in part && typeof part.text === 'string') {
|
|
52
|
-
PAGE_DOM_RE.lastIndex = 0;
|
|
53
|
-
part.text = part.text.replace(PAGE_DOM_RE, PLACEHOLDER);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
else if (typeof msg.content === 'string') {
|
|
58
|
-
PAGE_DOM_RE.lastIndex = 0;
|
|
59
|
-
msg.content = msg.content.replace(PAGE_DOM_RE, PLACEHOLDER);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Trim the conversation thread to avoid context window overflow.
|
|
65
|
-
* Always preserves the first `preservedPrefixMessages` entries, then the last maxMessages messages.
|
|
66
|
-
* Also compresses old screenshots and DOM blocks to save tokens.
|
|
67
|
-
*/
|
|
68
|
-
export function trimConversationHistory(messages, maxMessages = 48, preservedPrefixMessages = 1) {
|
|
69
|
-
compressOldScreenshots(messages, 1);
|
|
70
|
-
compressOldDomBlocks(messages, 6, preservedPrefixMessages);
|
|
71
|
-
if (messages.length <= maxMessages + preservedPrefixMessages)
|
|
72
|
-
return;
|
|
73
|
-
const preservedPrefix = messages.slice(0, preservedPrefixMessages);
|
|
74
|
-
const recent = messages.slice(-(maxMessages));
|
|
75
|
-
messages.splice(0, messages.length, ...preservedPrefix, ...recent);
|
|
76
|
-
}
|
|
77
|
-
//# sourceMappingURL=agent-message-utils.js.map
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* URL matching, click guard, and verification cache utilities for the capture agent.
|
|
3
|
-
* Extracted from agent.ts — these are pure functions with no closure dependencies.
|
|
4
|
-
*/
|
|
5
|
-
import type { VerificationResult, VariantCaptureManifest } from './types.js';
|
|
6
|
-
import type { BrowserVerificationBundle } from './browser.js';
|
|
7
|
-
export declare function urlsRoughlyMatch(expectedUrl: string | undefined, currentUrl: string | undefined): boolean;
|
|
8
|
-
export declare function normalizeStrictComparableUrl(value: string): string;
|
|
9
|
-
export declare function urlsStrictlyMatch(expectedUrl: string | undefined, currentUrl: string | undefined): boolean;
|
|
10
|
-
export declare function normalizeGuardPageUrl(value: unknown): string;
|
|
11
|
-
export declare function buildClickGuardAnchor(params: Record<string, unknown>): string | null;
|
|
12
|
-
export declare function buildClickGuardSignature(params: Record<string, unknown>, currentUrl?: string): string | null;
|
|
13
|
-
export declare function buildVerificationCacheKey(config: {
|
|
14
|
-
prompt: string;
|
|
15
|
-
variantManifest?: VariantCaptureManifest | null;
|
|
16
|
-
currentLang?: string | null;
|
|
17
|
-
currentTheme?: string | null;
|
|
18
|
-
captureCursor?: {
|
|
19
|
-
targetId?: string;
|
|
20
|
-
} | null;
|
|
21
|
-
}, bundle: BrowserVerificationBundle): string;
|
|
22
|
-
export declare function cloneVerificationResult(result: VerificationResult): VerificationResult;
|
|
23
|
-
export declare function summarizeVariantManifestForPlanner(manifest: VariantCaptureManifest | undefined): string;
|
|
24
|
-
export declare function buildPageStateFingerprint(pageState: {
|
|
25
|
-
simplifiedDOM?: string | null;
|
|
26
|
-
interactiveElements: Array<unknown>;
|
|
27
|
-
scrollInfo?: {
|
|
28
|
-
scrollY?: number;
|
|
29
|
-
} | null;
|
|
30
|
-
}, currentUrl: string): string;
|
package/dist/agent-url-utils.js
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* URL matching, click guard, and verification cache utilities for the capture agent.
|
|
3
|
-
* Extracted from agent.ts — these are pure functions with no closure dependencies.
|
|
4
|
-
*/
|
|
5
|
-
import { createHash } from 'crypto';
|
|
6
|
-
// -- URL matching --
|
|
7
|
-
export function urlsRoughlyMatch(expectedUrl, currentUrl) {
|
|
8
|
-
if (!expectedUrl || !currentUrl)
|
|
9
|
-
return false;
|
|
10
|
-
try {
|
|
11
|
-
const expected = new URL(expectedUrl);
|
|
12
|
-
const current = new URL(currentUrl);
|
|
13
|
-
if (expected.origin !== current.origin)
|
|
14
|
-
return false;
|
|
15
|
-
const expectedPath = expected.pathname || '/';
|
|
16
|
-
const currentPath = current.pathname || '/';
|
|
17
|
-
if (currentPath === expectedPath)
|
|
18
|
-
return true;
|
|
19
|
-
if (currentPath.startsWith(expectedPath) && (expectedPath.endsWith('/') || currentPath[expectedPath.length] === '/')) {
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
return currentUrl === expectedUrl;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
export function normalizeStrictComparableUrl(value) {
|
|
29
|
-
const parsed = new URL(value);
|
|
30
|
-
const normalizedPath = parsed.pathname.replace(/\/+$/, '') || '/';
|
|
31
|
-
const searchEntries = Array.from(parsed.searchParams.entries()).sort(([aKey, aValue], [bKey, bValue]) => {
|
|
32
|
-
if (aKey === bKey)
|
|
33
|
-
return aValue.localeCompare(bValue);
|
|
34
|
-
return aKey.localeCompare(bKey);
|
|
35
|
-
});
|
|
36
|
-
const normalizedSearch = searchEntries.length > 0
|
|
37
|
-
? `?${new URLSearchParams(searchEntries).toString()}`
|
|
38
|
-
: '';
|
|
39
|
-
return `${parsed.origin}${normalizedPath}${normalizedSearch}`;
|
|
40
|
-
}
|
|
41
|
-
export function urlsStrictlyMatch(expectedUrl, currentUrl) {
|
|
42
|
-
if (!expectedUrl || !currentUrl)
|
|
43
|
-
return false;
|
|
44
|
-
try {
|
|
45
|
-
return normalizeStrictComparableUrl(expectedUrl) === normalizeStrictComparableUrl(currentUrl);
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return currentUrl.trim().replace(/\/+$/, '') === expectedUrl.trim().replace(/\/+$/, '');
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
export function normalizeGuardPageUrl(value) {
|
|
52
|
-
if (typeof value !== 'string' || value.trim().length === 0)
|
|
53
|
-
return '';
|
|
54
|
-
try {
|
|
55
|
-
const parsed = new URL(value);
|
|
56
|
-
return `${parsed.origin}${parsed.pathname}`.replace(/\/$/, '') || parsed.origin;
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
return value.trim().replace(/\/$/, '');
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
// -- Click guards --
|
|
63
|
-
export function buildClickGuardAnchor(params) {
|
|
64
|
-
const parts = [];
|
|
65
|
-
if (typeof params.selector === 'string' && params.selector.trim()) {
|
|
66
|
-
parts.push(`selector:${params.selector.trim()}`);
|
|
67
|
-
}
|
|
68
|
-
if (params.index !== undefined) {
|
|
69
|
-
parts.push(`index:${String(params.index)}`);
|
|
70
|
-
}
|
|
71
|
-
if (typeof params.elementLabel === 'string' && params.elementLabel.trim()) {
|
|
72
|
-
parts.push(`label:${params.elementLabel.trim().toLowerCase()}`);
|
|
73
|
-
}
|
|
74
|
-
if (typeof params.href === 'string' && params.href.trim()) {
|
|
75
|
-
parts.push(`href:${normalizeGuardPageUrl(params.href)}`);
|
|
76
|
-
}
|
|
77
|
-
return parts.length > 0 ? parts.join('|') : null;
|
|
78
|
-
}
|
|
79
|
-
export function buildClickGuardSignature(params, currentUrl) {
|
|
80
|
-
const anchor = buildClickGuardAnchor(params);
|
|
81
|
-
if (!anchor)
|
|
82
|
-
return null;
|
|
83
|
-
const pageKey = normalizeGuardPageUrl(params.preActionUrl ?? params.postActionUrl ?? currentUrl);
|
|
84
|
-
return `click|${pageKey}|${anchor}`;
|
|
85
|
-
}
|
|
86
|
-
// -- Verification cache --
|
|
87
|
-
export function buildVerificationCacheKey(config, bundle) {
|
|
88
|
-
const promptHash = createHash('sha1').update(config.prompt).digest('hex').slice(0, 12);
|
|
89
|
-
return [
|
|
90
|
-
bundle.coherenceKey,
|
|
91
|
-
config.variantManifest?.currentPageId ?? 'main',
|
|
92
|
-
config.currentLang ?? 'default-lang',
|
|
93
|
-
config.currentTheme ?? 'default-theme',
|
|
94
|
-
config.captureCursor?.targetId ?? 'default-target',
|
|
95
|
-
promptHash,
|
|
96
|
-
].join('|');
|
|
97
|
-
}
|
|
98
|
-
export function cloneVerificationResult(result) {
|
|
99
|
-
return {
|
|
100
|
-
...result,
|
|
101
|
-
usage: result.usage.map(u => ({ ...u })),
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
export function summarizeVariantManifestForPlanner(manifest) {
|
|
105
|
-
if (!manifest)
|
|
106
|
-
return '';
|
|
107
|
-
const parts = [
|
|
108
|
-
`expected=${manifest.expectedPageIds.join(',') || 'main'}`,
|
|
109
|
-
`current=${manifest.currentPageId ?? 'main'}`,
|
|
110
|
-
manifest.currentPageIdentity ? `identity=${manifest.currentPageIdentity.summary}` : '',
|
|
111
|
-
`completed=${manifest.completedPages.join(',') || 'none'}`,
|
|
112
|
-
`remaining=${manifest.remainingPages.join(',') || 'none'}`,
|
|
113
|
-
manifest.lastCheckpointId ? `checkpoint=${manifest.lastCheckpointId}` : '',
|
|
114
|
-
];
|
|
115
|
-
if (manifest.captureStatuses) {
|
|
116
|
-
parts.push(`statuses=${Object.entries(manifest.captureStatuses)
|
|
117
|
-
.slice(0, 6)
|
|
118
|
-
.map(([pageId, status]) => `${pageId}:${status}`)
|
|
119
|
-
.join('|') || 'none'}`);
|
|
120
|
-
}
|
|
121
|
-
if (manifest.previousValidatedCaptures.length > 0) {
|
|
122
|
-
parts.push(`validated=${manifest.previousValidatedCaptures
|
|
123
|
-
.slice(-3)
|
|
124
|
-
.map((capture) => `${capture.pageId}${capture.identity ? `[${capture.identity.summary}]` : ''}:${capture.assessment.slice(0, 80)}`)
|
|
125
|
-
.join(' | ')}`);
|
|
126
|
-
}
|
|
127
|
-
return parts.join('; ');
|
|
128
|
-
}
|
|
129
|
-
// -- Page state fingerprint --
|
|
130
|
-
export function buildPageStateFingerprint(pageState, currentUrl) {
|
|
131
|
-
return createHash('sha1')
|
|
132
|
-
.update(pageState.simplifiedDOM || '')
|
|
133
|
-
.update(String(pageState.interactiveElements.length))
|
|
134
|
-
.update(String(pageState.scrollInfo?.scrollY ?? 0))
|
|
135
|
-
.update(currentUrl)
|
|
136
|
-
.digest('hex');
|
|
137
|
-
}
|
|
138
|
-
//# sourceMappingURL=agent-url-utils.js.map
|