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.
Files changed (188) hide show
  1. package/dist/browser.d.ts +1 -0
  2. package/dist/browser.js +16 -5
  3. package/dist/cli-runner.js +80 -18
  4. package/dist/cli.js +0 -0
  5. package/dist/clip-capture-loop.d.ts +21 -0
  6. package/dist/clip-capture-loop.js +37 -0
  7. package/dist/mouse-animation.d.ts +4 -1
  8. package/dist/mouse-animation.js +7 -3
  9. package/dist/web-playwright-local.js +13 -3
  10. package/package.json +1 -1
  11. package/dist/agent-action-recovery.d.ts +0 -45
  12. package/dist/agent-action-recovery.js +0 -370
  13. package/dist/agent-message-utils.d.ts +0 -21
  14. package/dist/agent-message-utils.js +0 -77
  15. package/dist/agent-url-utils.d.ts +0 -30
  16. package/dist/agent-url-utils.js +0 -138
  17. package/dist/agent.d.ts +0 -226
  18. package/dist/agent.js +0 -6666
  19. package/dist/browser-utils.d.ts +0 -31
  20. package/dist/browser-utils.js +0 -97
  21. package/dist/capture-studio-sync.d.ts +0 -23
  22. package/dist/capture-studio-sync.js +0 -172
  23. package/dist/clip-begin-frame-recorder.d.ts +0 -44
  24. package/dist/clip-begin-frame-recorder.js +0 -250
  25. package/dist/clip-capture-backend.d.ts +0 -25
  26. package/dist/clip-capture-backend.js +0 -189
  27. package/dist/clip-frame-recorder.d.ts +0 -63
  28. package/dist/clip-frame-recorder.js +0 -305
  29. package/dist/clip-orchestrator.d.ts +0 -148
  30. package/dist/clip-orchestrator.js +0 -957
  31. package/dist/clip-runtime.d.ts +0 -18
  32. package/dist/clip-runtime.js +0 -67
  33. package/dist/clip-scale.d.ts +0 -10
  34. package/dist/clip-scale.js +0 -21
  35. package/dist/clip-screencast-recorder.d.ts +0 -42
  36. package/dist/clip-screencast-recorder.js +0 -242
  37. package/dist/clip-sidecar.d.ts +0 -54
  38. package/dist/clip-sidecar.js +0 -208
  39. package/dist/cost-resolution-monitor.d.ts +0 -16
  40. package/dist/cost-resolution-monitor.js +0 -34
  41. package/dist/credential-templates.d.ts +0 -5
  42. package/dist/credential-templates.js +0 -60
  43. package/dist/dom-css-purger.d.ts +0 -65
  44. package/dist/dom-css-purger.js +0 -333
  45. package/dist/dom-font-inliner.d.ts +0 -45
  46. package/dist/dom-font-inliner.js +0 -148
  47. package/dist/dom-patch-resolver.d.ts +0 -52
  48. package/dist/dom-patch-resolver.js +0 -242
  49. package/dist/dom-serializer.d.ts +0 -82
  50. package/dist/dom-serializer.js +0 -378
  51. package/dist/element-capture.d.ts +0 -13
  52. package/dist/element-capture.js +0 -522
  53. package/dist/fonts-loader.d.ts +0 -14
  54. package/dist/fonts-loader.js +0 -55
  55. package/dist/hybrid-navigator.d.ts +0 -138
  56. package/dist/hybrid-navigator.js +0 -468
  57. package/dist/legacy/agent-action-recovery.d.ts +0 -45
  58. package/dist/legacy/agent-action-recovery.js +0 -370
  59. package/dist/legacy/agent-message-utils.d.ts +0 -21
  60. package/dist/legacy/agent-message-utils.js +0 -77
  61. package/dist/legacy/agent-url-utils.d.ts +0 -30
  62. package/dist/legacy/agent-url-utils.js +0 -138
  63. package/dist/legacy/agent.d.ts +0 -226
  64. package/dist/legacy/agent.js +0 -6666
  65. package/dist/legacy/clip-orchestrator.d.ts +0 -148
  66. package/dist/legacy/clip-orchestrator.js +0 -957
  67. package/dist/legacy/credential-templates.d.ts +0 -5
  68. package/dist/legacy/credential-templates.js +0 -60
  69. package/dist/legacy/hybrid-navigator.d.ts +0 -138
  70. package/dist/legacy/hybrid-navigator.js +0 -468
  71. package/dist/legacy/llm-usage.d.ts +0 -17
  72. package/dist/legacy/llm-usage.js +0 -45
  73. package/dist/legacy/prompt-cache.d.ts +0 -10
  74. package/dist/legacy/prompt-cache.js +0 -24
  75. package/dist/legacy/prompts.d.ts +0 -175
  76. package/dist/legacy/prompts.js +0 -1038
  77. package/dist/legacy/tools.d.ts +0 -4
  78. package/dist/legacy/tools.js +0 -216
  79. package/dist/legacy/video-agent.d.ts +0 -143
  80. package/dist/legacy/video-agent.js +0 -4788
  81. package/dist/legacy/video-observation.d.ts +0 -36
  82. package/dist/legacy/video-observation.js +0 -192
  83. package/dist/legacy/video-planner.d.ts +0 -12
  84. package/dist/legacy/video-planner.js +0 -501
  85. package/dist/legacy/video-prompts.d.ts +0 -37
  86. package/dist/legacy/video-prompts.js +0 -569
  87. package/dist/legacy/video-tools.d.ts +0 -3
  88. package/dist/legacy/video-tools.js +0 -59
  89. package/dist/legacy/video-variant-state.d.ts +0 -29
  90. package/dist/legacy/video-variant-state.js +0 -80
  91. package/dist/legacy/vision-model.d.ts +0 -17
  92. package/dist/legacy/vision-model.js +0 -74
  93. package/dist/llm-usage.d.ts +0 -17
  94. package/dist/llm-usage.js +0 -45
  95. package/dist/overlay-utils.d.ts +0 -14
  96. package/dist/overlay-utils.js +0 -13
  97. package/dist/prompt-cache.d.ts +0 -10
  98. package/dist/prompt-cache.js +0 -24
  99. package/dist/prompts.d.ts +0 -175
  100. package/dist/prompts.js +0 -1038
  101. package/dist/remote-browser.d.ts +0 -215
  102. package/dist/remote-browser.js +0 -360
  103. package/dist/svg-browser-bar.d.ts +0 -33
  104. package/dist/svg-browser-bar.js +0 -206
  105. package/dist/svg-status-bar.d.ts +0 -36
  106. package/dist/svg-status-bar.js +0 -597
  107. package/dist/svg-text.d.ts +0 -61
  108. package/dist/svg-text.js +0 -118
  109. package/dist/tools.d.ts +0 -4
  110. package/dist/tools.js +0 -216
  111. package/dist/v2/action-verifier.d.ts +0 -29
  112. package/dist/v2/action-verifier.js +0 -133
  113. package/dist/v2/alt-text.d.ts +0 -26
  114. package/dist/v2/alt-text.js +0 -55
  115. package/dist/v2/benchmark.d.ts +0 -59
  116. package/dist/v2/benchmark.js +0 -135
  117. package/dist/v2/capture-strategy.d.ts +0 -30
  118. package/dist/v2/capture-strategy.js +0 -67
  119. package/dist/v2/capture-verification.d.ts +0 -35
  120. package/dist/v2/capture-verification.js +0 -95
  121. package/dist/v2/circuit-breaker.d.ts +0 -42
  122. package/dist/v2/circuit-breaker.js +0 -119
  123. package/dist/v2/cli-runner-local.d.ts +0 -11
  124. package/dist/v2/cli-runner-local.js +0 -91
  125. package/dist/v2/cli-runner.d.ts +0 -34
  126. package/dist/v2/cli-runner.js +0 -300
  127. package/dist/v2/compiler-prompts.d.ts +0 -27
  128. package/dist/v2/compiler-prompts.js +0 -123
  129. package/dist/v2/compiler.d.ts +0 -37
  130. package/dist/v2/compiler.js +0 -147
  131. package/dist/v2/explorer.d.ts +0 -41
  132. package/dist/v2/explorer.js +0 -56
  133. package/dist/v2/index.d.ts +0 -37
  134. package/dist/v2/index.js +0 -31
  135. package/dist/v2/llm-healer.d.ts +0 -62
  136. package/dist/v2/llm-healer.js +0 -166
  137. package/dist/v2/llm-provider.d.ts +0 -29
  138. package/dist/v2/llm-provider.js +0 -80
  139. package/dist/v2/opcode-runner.d.ts +0 -47
  140. package/dist/v2/opcode-runner.js +0 -634
  141. package/dist/v2/overlay-engine.d.ts +0 -24
  142. package/dist/v2/overlay-engine.js +0 -150
  143. package/dist/v2/postcondition.d.ts +0 -16
  144. package/dist/v2/postcondition.js +0 -249
  145. package/dist/v2/program-patcher.d.ts +0 -25
  146. package/dist/v2/program-patcher.js +0 -44
  147. package/dist/v2/recovery-chain.d.ts +0 -30
  148. package/dist/v2/recovery-chain.js +0 -368
  149. package/dist/v2/schema.d.ts +0 -2580
  150. package/dist/v2/schema.js +0 -295
  151. package/dist/v2/selector-resolver.d.ts +0 -34
  152. package/dist/v2/selector-resolver.js +0 -181
  153. package/dist/v2/semantic-resolver.d.ts +0 -35
  154. package/dist/v2/semantic-resolver.js +0 -161
  155. package/dist/v2/smart-wait.d.ts +0 -27
  156. package/dist/v2/smart-wait.js +0 -81
  157. package/dist/v2/types.d.ts +0 -444
  158. package/dist/v2/types.js +0 -19
  159. package/dist/v2/web-playwright-local.d.ts +0 -69
  160. package/dist/v2/web-playwright-local.js +0 -392
  161. package/dist/video-agent.d.ts +0 -143
  162. package/dist/video-agent.js +0 -4788
  163. package/dist/video-observation.d.ts +0 -36
  164. package/dist/video-observation.js +0 -192
  165. package/dist/video-planner.d.ts +0 -12
  166. package/dist/video-planner.js +0 -501
  167. package/dist/video-prompts.d.ts +0 -37
  168. package/dist/video-prompts.js +0 -554
  169. package/dist/video-tools.d.ts +0 -3
  170. package/dist/video-tools.js +0 -59
  171. package/dist/video-variant-state.d.ts +0 -29
  172. package/dist/video-variant-state.js +0 -80
  173. package/dist/vision-model.d.ts +0 -17
  174. package/dist/vision-model.js +0 -74
  175. package/dist/ws-auth.d.ts +0 -20
  176. package/dist/ws-auth.js +0 -70
  177. package/dist/ws-broadcast.d.ts +0 -34
  178. package/dist/ws-broadcast.js +0 -85
  179. package/dist/ws-connection-limits.d.ts +0 -12
  180. package/dist/ws-connection-limits.js +0 -44
  181. package/dist/ws-handler-utils.d.ts +0 -32
  182. package/dist/ws-handler-utils.js +0 -139
  183. package/dist/ws-handler.d.ts +0 -10
  184. package/dist/ws-handler.js +0 -1793
  185. package/dist/ws-metrics-server.d.ts +0 -9
  186. package/dist/ws-metrics-server.js +0 -31
  187. package/dist/ws-server.d.ts +0 -9
  188. 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;
@@ -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