autokap 1.0.6 → 1.0.8

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 (347) hide show
  1. package/assets/chrome/ios-statusbar-comparison-reference.jpg +0 -0
  2. package/assets/chrome/ios-statusbar-dark-reference.jpg +0 -0
  3. package/assets/chrome/ios-statusbar-light-reference.jpg +0 -0
  4. package/assets/cursors/macos.svg +4 -0
  5. package/assets/cursors/windows.svg +15 -0
  6. package/assets/devices/ipad-pro-11-m4.json +52 -0
  7. package/assets/devices/iphone-16-pro.json +53 -0
  8. package/assets/devices/macbook-air-13.json +45 -0
  9. package/assets/frames/MacBook Air 13.svg +242 -0
  10. package/assets/frames/Status bar - iPhone.png +0 -0
  11. Menu bar- iPad.png +0 -0
  12. package/assets/frames/iPad Pro M4 11_.png +0 -0
  13. package/assets/frames/iPhone 16 Pro.png +0 -0
  14. package/assets/icons/Cellular Connection.svg +3 -0
  15. package/assets/icons/Union.svg +6 -0
  16. package/assets/icons/Wifi.svg +3 -0
  17. package/assets/icons/battery.svg +5 -0
  18. package/assets/icons/battery_charging.svg +8 -0
  19. package/assets/skill/OPCODE-REFERENCE.md +607 -0
  20. package/assets/skill/README.md +39 -0
  21. package/assets/skill/SKILL.md +453 -468
  22. package/assets/skill/STUDIO-SKILL.md +476 -0
  23. package/assets/skill/references/examples.md +104 -0
  24. package/assets/skill/references/interactive-demo.md +225 -0
  25. package/assets/skill/references/mock-data.md +178 -0
  26. package/dist/abort.d.ts +5 -0
  27. package/dist/abort.js +44 -0
  28. package/dist/action-verifier.d.ts +29 -0
  29. package/dist/action-verifier.js +133 -0
  30. package/dist/agent-action-recovery.d.ts +45 -0
  31. package/dist/agent-action-recovery.js +370 -0
  32. package/dist/agent-message-utils.d.ts +21 -0
  33. package/dist/agent-message-utils.js +77 -0
  34. package/dist/agent-url-utils.d.ts +30 -0
  35. package/dist/agent-url-utils.js +138 -0
  36. package/dist/agent.d.ts +226 -0
  37. package/dist/agent.js +6666 -0
  38. package/dist/ak-tree.d.ts +39 -0
  39. package/dist/ak-tree.js +368 -0
  40. package/dist/alt-text.d.ts +26 -0
  41. package/dist/alt-text.js +55 -0
  42. package/dist/auth-capture.d.ts +17 -0
  43. package/dist/auth-capture.js +164 -0
  44. package/dist/benchmark.d.ts +59 -0
  45. package/dist/benchmark.js +135 -0
  46. package/dist/billing-operation-logging.d.ts +38 -0
  47. package/dist/billing-operation-logging.js +248 -0
  48. package/dist/browser-bar.d.ts +48 -0
  49. package/dist/browser-bar.js +284 -0
  50. package/dist/browser-pool.d.ts +7 -0
  51. package/dist/browser-pool.js +15 -5
  52. package/dist/browser-utils.d.ts +31 -0
  53. package/dist/browser-utils.js +97 -0
  54. package/dist/browser.d.ts +76 -1
  55. package/dist/browser.js +1657 -39
  56. package/dist/capture-alt-text.d.ts +12 -0
  57. package/dist/capture-alt-text.js +52 -0
  58. package/dist/capture-encryption.d.ts +10 -0
  59. package/dist/capture-encryption.js +41 -0
  60. package/dist/capture-language-preflight.d.ts +41 -0
  61. package/dist/capture-language-preflight.js +300 -0
  62. package/dist/capture-llm-page-identity.d.ts +15 -0
  63. package/dist/capture-llm-page-identity.js +128 -0
  64. package/dist/capture-model-resolution.d.ts +9 -0
  65. package/dist/capture-model-resolution.js +21 -0
  66. package/dist/capture-page-identity.d.ts +7 -0
  67. package/dist/capture-page-identity.js +352 -0
  68. package/dist/capture-preset-credentials.d.ts +62 -0
  69. package/dist/capture-preset-credentials.js +184 -0
  70. package/dist/capture-request-plan.d.ts +58 -0
  71. package/dist/capture-request-plan.js +264 -0
  72. package/dist/capture-run-optimizer.d.ts +139 -0
  73. package/dist/capture-run-optimizer.js +863 -0
  74. package/dist/capture-selector-memory.d.ts +31 -0
  75. package/dist/capture-selector-memory.js +345 -0
  76. package/dist/capture-session-profile-encryption.d.ts +2 -0
  77. package/dist/capture-session-profile-encryption.js +22 -0
  78. package/dist/capture-step-timeout.d.ts +10 -0
  79. package/dist/capture-step-timeout.js +30 -0
  80. package/dist/capture-strategy.d.ts +36 -0
  81. package/dist/capture-strategy.js +95 -0
  82. package/dist/capture-studio-sync.d.ts +23 -0
  83. package/dist/capture-studio-sync.js +172 -0
  84. package/dist/capture-surface-contract.d.ts +36 -0
  85. package/dist/capture-surface-contract.js +299 -0
  86. package/dist/capture-transition-engine.d.ts +28 -0
  87. package/dist/capture-transition-engine.js +292 -0
  88. package/dist/capture-variant-state.d.ts +56 -0
  89. package/dist/capture-variant-state.js +182 -0
  90. package/dist/capture-verification.d.ts +35 -0
  91. package/dist/capture-verification.js +95 -0
  92. package/dist/capture-viewport-lock.d.ts +48 -0
  93. package/dist/capture-viewport-lock.js +74 -0
  94. package/dist/circuit-breaker.d.ts +42 -0
  95. package/dist/circuit-breaker.js +119 -0
  96. package/dist/cli-config.d.ts +8 -1
  97. package/dist/cli-config.js +62 -6
  98. package/dist/cli-contract.d.ts +15 -0
  99. package/dist/cli-contract.js +167 -0
  100. package/dist/cli-runner-local.d.ts +12 -0
  101. package/dist/cli-runner-local.js +102 -0
  102. package/dist/cli-runner.d.ts +34 -0
  103. package/dist/cli-runner.js +433 -0
  104. package/dist/cli-utils.d.ts +0 -1
  105. package/dist/cli-utils.js +2 -5
  106. package/dist/cli.js +1005 -252
  107. package/dist/clip-orchestrator.d.ts +148 -0
  108. package/dist/clip-orchestrator.js +957 -0
  109. package/dist/clip-postprocess.d.ts +42 -0
  110. package/dist/clip-postprocess.js +201 -0
  111. package/dist/cookie-dismiss.d.ts +2 -0
  112. package/dist/cookie-dismiss.js +48 -13
  113. package/dist/cost-logging.d.ts +35 -0
  114. package/dist/cost-logging.js +242 -0
  115. package/dist/cost-resolution-monitor.d.ts +16 -0
  116. package/dist/cost-resolution-monitor.js +34 -0
  117. package/dist/credential-templates.d.ts +5 -0
  118. package/dist/credential-templates.js +60 -0
  119. package/dist/cursor-overlay-script.d.ts +6 -0
  120. package/dist/cursor-overlay-script.js +169 -0
  121. package/dist/dom-css-purger.d.ts +65 -0
  122. package/dist/dom-css-purger.js +333 -0
  123. package/dist/dom-font-inliner.d.ts +45 -0
  124. package/dist/dom-font-inliner.js +148 -0
  125. package/dist/dom-patch-resolver.d.ts +52 -0
  126. package/dist/dom-patch-resolver.js +242 -0
  127. package/dist/dom-serializer.d.ts +82 -0
  128. package/dist/dom-serializer.js +378 -0
  129. package/dist/element-capture.d.ts +13 -0
  130. package/dist/element-capture.js +522 -0
  131. package/dist/env-validation.d.ts +5 -0
  132. package/dist/env-validation.js +29 -0
  133. package/dist/execution-schema.d.ts +4423 -0
  134. package/dist/execution-schema.js +507 -0
  135. package/dist/execution-types.d.ts +886 -0
  136. package/dist/execution-types.js +65 -0
  137. package/dist/fonts-loader.d.ts +14 -0
  138. package/dist/fonts-loader.js +55 -0
  139. package/dist/hybrid-navigator.d.ts +138 -0
  140. package/dist/hybrid-navigator.js +468 -0
  141. package/dist/index.d.ts +18 -0
  142. package/dist/index.js +17 -0
  143. package/dist/legacy/agent-action-recovery.d.ts +45 -0
  144. package/dist/legacy/agent-action-recovery.js +370 -0
  145. package/dist/legacy/agent-message-utils.d.ts +21 -0
  146. package/dist/legacy/agent-message-utils.js +77 -0
  147. package/dist/legacy/agent-url-utils.d.ts +30 -0
  148. package/dist/legacy/agent-url-utils.js +138 -0
  149. package/dist/legacy/agent.d.ts +226 -0
  150. package/dist/legacy/agent.js +6666 -0
  151. package/dist/legacy/clip-orchestrator.d.ts +148 -0
  152. package/dist/legacy/clip-orchestrator.js +957 -0
  153. package/dist/legacy/credential-templates.d.ts +5 -0
  154. package/dist/legacy/credential-templates.js +60 -0
  155. package/dist/legacy/hybrid-navigator.d.ts +138 -0
  156. package/dist/legacy/hybrid-navigator.js +468 -0
  157. package/dist/legacy/llm-usage.d.ts +17 -0
  158. package/dist/legacy/llm-usage.js +45 -0
  159. package/dist/legacy/prompt-cache.d.ts +10 -0
  160. package/dist/legacy/prompt-cache.js +24 -0
  161. package/dist/legacy/prompts.d.ts +175 -0
  162. package/dist/legacy/prompts.js +1038 -0
  163. package/dist/legacy/tools.d.ts +4 -0
  164. package/dist/legacy/tools.js +216 -0
  165. package/dist/legacy/video-agent.d.ts +143 -0
  166. package/dist/legacy/video-agent.js +4788 -0
  167. package/dist/legacy/video-observation.d.ts +36 -0
  168. package/dist/legacy/video-observation.js +192 -0
  169. package/dist/legacy/video-planner.d.ts +12 -0
  170. package/dist/legacy/video-planner.js +501 -0
  171. package/dist/legacy/video-prompts.d.ts +37 -0
  172. package/dist/legacy/video-prompts.js +569 -0
  173. package/dist/legacy/video-tools.d.ts +3 -0
  174. package/dist/legacy/video-tools.js +59 -0
  175. package/dist/legacy/video-variant-state.d.ts +29 -0
  176. package/dist/legacy/video-variant-state.js +80 -0
  177. package/dist/legacy/vision-model.d.ts +17 -0
  178. package/dist/legacy/vision-model.js +74 -0
  179. package/dist/llm-healer.d.ts +63 -0
  180. package/dist/llm-healer.js +166 -0
  181. package/dist/llm-provider.d.ts +29 -0
  182. package/dist/llm-provider.js +80 -0
  183. package/dist/llm-usage.d.ts +17 -0
  184. package/dist/llm-usage.js +45 -0
  185. package/dist/logger.d.ts +6 -2
  186. package/dist/logger.js +15 -1
  187. package/dist/mockup-html.d.ts +119 -0
  188. package/dist/mockup-html.js +263 -0
  189. package/dist/mockup.d.ts +187 -0
  190. package/dist/mockup.js +869 -0
  191. package/dist/mouse-animation.d.ts +46 -0
  192. package/dist/mouse-animation.js +114 -0
  193. package/dist/opcode-actions.d.ts +42 -0
  194. package/dist/opcode-actions.js +511 -0
  195. package/dist/opcode-runner.d.ts +51 -0
  196. package/dist/opcode-runner.js +770 -0
  197. package/dist/openrouter-client.d.ts +40 -0
  198. package/dist/openrouter-client.js +16 -0
  199. package/dist/overlay-engine.d.ts +24 -0
  200. package/dist/overlay-engine.js +176 -0
  201. package/dist/overlay-utils.d.ts +14 -0
  202. package/dist/overlay-utils.js +13 -0
  203. package/dist/postcondition.d.ts +16 -0
  204. package/dist/postcondition.js +269 -0
  205. package/dist/posthog.d.ts +4 -0
  206. package/dist/posthog.js +26 -0
  207. package/dist/program-patcher.d.ts +25 -0
  208. package/dist/program-patcher.js +44 -0
  209. package/dist/prompt-cache.d.ts +10 -0
  210. package/dist/prompt-cache.js +24 -0
  211. package/dist/prompts.d.ts +175 -0
  212. package/dist/prompts.js +1038 -0
  213. package/dist/provider-config.d.ts +12 -0
  214. package/dist/provider-config.js +15 -0
  215. package/dist/recovery-chain.d.ts +37 -0
  216. package/dist/recovery-chain.js +350 -0
  217. package/dist/remote-browser.d.ts +215 -0
  218. package/dist/remote-browser.js +360 -0
  219. package/dist/safari-browser-bar.d.ts +15 -0
  220. package/dist/safari-browser-bar.js +95 -0
  221. package/dist/safari-toolbar-asset.d.ts +15 -0
  222. package/dist/safari-toolbar-asset.js +12 -0
  223. package/dist/security.d.ts +21 -0
  224. package/dist/security.js +608 -0
  225. package/dist/selector-resolver.d.ts +34 -0
  226. package/dist/selector-resolver.js +181 -0
  227. package/dist/semantic-resolver.d.ts +35 -0
  228. package/dist/semantic-resolver.js +161 -0
  229. package/dist/server-capture-runtime.d.ts +125 -0
  230. package/dist/server-capture-runtime.js +585 -0
  231. package/dist/server-credit-usage.d.ts +12 -0
  232. package/dist/server-credit-usage.js +41 -0
  233. package/dist/server-posthog.d.ts +2 -0
  234. package/dist/server-posthog.js +16 -0
  235. package/dist/server-project-webhooks.d.ts +59 -0
  236. package/dist/server-project-webhooks.js +123 -0
  237. package/dist/server-screenshot-watermark.d.ts +7 -0
  238. package/dist/server-screenshot-watermark.js +60 -0
  239. package/dist/session-profile.d.ts +86 -0
  240. package/dist/session-profile.js +1536 -0
  241. package/dist/sf-pro-fonts.d.ts +4 -0
  242. package/dist/sf-pro-fonts.js +7 -0
  243. package/dist/sf-pro-symbols.d.ts +1 -0
  244. package/dist/sf-pro-symbols.js +55 -0
  245. package/dist/skill-packaging.d.ts +28 -0
  246. package/dist/skill-packaging.js +169 -0
  247. package/dist/smart-wait.d.ts +27 -0
  248. package/dist/smart-wait.js +81 -0
  249. package/dist/status-bar-l10n.d.ts +14 -0
  250. package/dist/status-bar-l10n.js +177 -0
  251. package/dist/status-bar-render.d.ts +20 -0
  252. package/dist/status-bar-render.js +410 -0
  253. package/dist/status-bar.d.ts +53 -0
  254. package/dist/status-bar.js +620 -0
  255. package/dist/svg-browser-bar.d.ts +33 -0
  256. package/dist/svg-browser-bar.js +206 -0
  257. package/dist/svg-status-bar.d.ts +36 -0
  258. package/dist/svg-status-bar.js +597 -0
  259. package/dist/svg-text.d.ts +61 -0
  260. package/dist/svg-text.js +118 -0
  261. package/dist/tools.d.ts +4 -0
  262. package/dist/tools.js +216 -0
  263. package/dist/types.d.ts +240 -5
  264. package/dist/types.js +23 -1
  265. package/dist/v2/action-verifier.d.ts +29 -0
  266. package/dist/v2/action-verifier.js +133 -0
  267. package/dist/v2/alt-text.d.ts +26 -0
  268. package/dist/v2/alt-text.js +55 -0
  269. package/dist/v2/benchmark.d.ts +59 -0
  270. package/dist/v2/benchmark.js +135 -0
  271. package/dist/v2/capture-strategy.d.ts +30 -0
  272. package/dist/v2/capture-strategy.js +67 -0
  273. package/dist/v2/capture-verification.d.ts +35 -0
  274. package/dist/v2/capture-verification.js +95 -0
  275. package/dist/v2/circuit-breaker.d.ts +42 -0
  276. package/dist/v2/circuit-breaker.js +119 -0
  277. package/dist/v2/cli-runner-local.d.ts +11 -0
  278. package/dist/v2/cli-runner-local.js +91 -0
  279. package/dist/v2/cli-runner.d.ts +34 -0
  280. package/dist/v2/cli-runner.js +300 -0
  281. package/dist/v2/compiler-prompts.d.ts +27 -0
  282. package/dist/v2/compiler-prompts.js +123 -0
  283. package/dist/v2/compiler.d.ts +37 -0
  284. package/dist/v2/compiler.js +147 -0
  285. package/dist/v2/explorer.d.ts +41 -0
  286. package/dist/v2/explorer.js +56 -0
  287. package/dist/v2/index.d.ts +37 -0
  288. package/dist/v2/index.js +31 -0
  289. package/dist/v2/llm-healer.d.ts +62 -0
  290. package/dist/v2/llm-healer.js +166 -0
  291. package/dist/v2/llm-provider.d.ts +29 -0
  292. package/dist/v2/llm-provider.js +80 -0
  293. package/dist/v2/opcode-runner.d.ts +47 -0
  294. package/dist/v2/opcode-runner.js +634 -0
  295. package/dist/v2/overlay-engine.d.ts +24 -0
  296. package/dist/v2/overlay-engine.js +150 -0
  297. package/dist/v2/postcondition.d.ts +16 -0
  298. package/dist/v2/postcondition.js +249 -0
  299. package/dist/v2/program-patcher.d.ts +25 -0
  300. package/dist/v2/program-patcher.js +44 -0
  301. package/dist/v2/recovery-chain.d.ts +30 -0
  302. package/dist/v2/recovery-chain.js +368 -0
  303. package/dist/v2/schema.d.ts +2580 -0
  304. package/dist/v2/schema.js +295 -0
  305. package/dist/v2/selector-resolver.d.ts +34 -0
  306. package/dist/v2/selector-resolver.js +181 -0
  307. package/dist/v2/semantic-resolver.d.ts +35 -0
  308. package/dist/v2/semantic-resolver.js +161 -0
  309. package/dist/v2/smart-wait.d.ts +27 -0
  310. package/dist/v2/smart-wait.js +81 -0
  311. package/dist/v2/types.d.ts +444 -0
  312. package/dist/v2/types.js +19 -0
  313. package/dist/v2/web-playwright-local.d.ts +69 -0
  314. package/dist/v2/web-playwright-local.js +392 -0
  315. package/dist/version.d.ts +1 -0
  316. package/dist/version.js +5 -0
  317. package/dist/video-agent.d.ts +143 -0
  318. package/dist/video-agent.js +4788 -0
  319. package/dist/video-observation.d.ts +36 -0
  320. package/dist/video-observation.js +192 -0
  321. package/dist/video-planner.d.ts +12 -0
  322. package/dist/video-planner.js +501 -0
  323. package/dist/video-prompts.d.ts +37 -0
  324. package/dist/video-prompts.js +554 -0
  325. package/dist/video-tools.d.ts +3 -0
  326. package/dist/video-tools.js +59 -0
  327. package/dist/video-variant-state.d.ts +29 -0
  328. package/dist/video-variant-state.js +80 -0
  329. package/dist/vision-model.d.ts +17 -0
  330. package/dist/vision-model.js +74 -0
  331. package/dist/web-playwright-local.d.ts +126 -0
  332. package/dist/web-playwright-local.js +819 -0
  333. package/dist/ws-auth.d.ts +20 -0
  334. package/dist/ws-auth.js +70 -0
  335. package/dist/ws-broadcast.d.ts +34 -0
  336. package/dist/ws-broadcast.js +85 -0
  337. package/dist/ws-connection-limits.d.ts +12 -0
  338. package/dist/ws-connection-limits.js +44 -0
  339. package/dist/ws-handler-utils.d.ts +32 -0
  340. package/dist/ws-handler-utils.js +139 -0
  341. package/dist/ws-handler.d.ts +10 -0
  342. package/dist/ws-handler.js +1793 -0
  343. package/dist/ws-metrics-server.d.ts +9 -0
  344. package/dist/ws-metrics-server.js +31 -0
  345. package/dist/ws-server.d.ts +9 -0
  346. package/dist/ws-server.js +92 -0
  347. package/package.json +142 -71
@@ -0,0 +1,634 @@
1
+ /**
2
+ * Capture Agent — Opcode Runner
3
+ *
4
+ * Deterministic execution engine for ExecutionProgram.
5
+ * Executes opcodes sequentially, verifies postconditions,
6
+ * delegates to recovery chain on failure, and respects circuit breaker.
7
+ */
8
+ import { evaluatePostcondition } from './postcondition.js';
9
+ import { ActionVerifier } from './action-verifier.js';
10
+ import { CircuitBreaker } from './circuit-breaker.js';
11
+ import { smartWaitForStability } from './smart-wait.js';
12
+ import { verifyCaptureQuality } from './capture-verification.js';
13
+ import { generateAltText } from './alt-text.js';
14
+ /** No-op recovery chain for Phase 1 (deterministic-only execution) */
15
+ export class NoOpRecoveryChain {
16
+ async attempt() {
17
+ return { recovered: false, reason: 'no recovery chain configured' };
18
+ }
19
+ }
20
+ // ── Main execution function ─────────────────────────────────────────
21
+ export async function executeProgram(program, createAdapter, options = {}) {
22
+ const recoveryChain = options.recoveryChain ?? new NoOpRecoveryChain();
23
+ const startTime = Date.now();
24
+ const variantResults = [];
25
+ const healerPatches = [];
26
+ const telemetry = {
27
+ llmCallCount: 0,
28
+ llmCostEur: 0,
29
+ llmStepUsages: [],
30
+ totalOpcodes: 0,
31
+ recoveredOpcodes: 0,
32
+ failedOpcodes: 0,
33
+ healerInvocations: 0,
34
+ circuitBreakerTrips: 0,
35
+ };
36
+ for (const variant of program.variants) {
37
+ if (options.abortSignal?.aborted)
38
+ break;
39
+ options.onProgress?.({
40
+ type: 'variant_start',
41
+ variantId: variant.id,
42
+ message: `starting variant ${variant.id}`,
43
+ });
44
+ const variantResult = await executeVariant(program, variant, createAdapter, recoveryChain, telemetry, healerPatches, options);
45
+ variantResults.push(variantResult);
46
+ options.onProgress?.({
47
+ type: 'variant_end',
48
+ variantId: variant.id,
49
+ status: variantResult.success ? 'ok' : 'failed',
50
+ message: variantResult.success
51
+ ? `variant ${variant.id} completed`
52
+ : `variant ${variant.id} failed: ${variantResult.error}`,
53
+ });
54
+ }
55
+ const aborted = options.abortSignal?.aborted && variantResults.length < program.variants.length;
56
+ const success = !aborted && variantResults.length > 0 && variantResults.every(v => v.success);
57
+ return {
58
+ programId: program.presetId,
59
+ success,
60
+ variantResults,
61
+ telemetry,
62
+ healerPatches: success ? healerPatches : [], // Only propagate patches on success
63
+ totalDurationMs: Date.now() - startTime,
64
+ error: aborted ? 'aborted' : (success ? undefined : variantResults.find(v => !v.success)?.error),
65
+ };
66
+ }
67
+ // ── Variant execution ───────────────────────────────────────────────
68
+ async function executeVariant(program, variant, createAdapter, recoveryChain, telemetry, healerPatches, options) {
69
+ const startTime = Date.now();
70
+ const breaker = new CircuitBreaker();
71
+ const verifier = new ActionVerifier();
72
+ const opcodeResults = [];
73
+ const artifacts = [];
74
+ const executionState = {};
75
+ let adapter = null;
76
+ try {
77
+ adapter = await createAdapter(variant);
78
+ for (let i = 0; i < program.steps.length; i++) {
79
+ if (options.abortSignal?.aborted) {
80
+ return {
81
+ variantId: variant.id,
82
+ success: false,
83
+ opcodeResults,
84
+ durationMs: Date.now() - startTime,
85
+ artifacts,
86
+ error: 'aborted',
87
+ };
88
+ }
89
+ // Check circuit breaker before starting
90
+ const breakerState = breaker.isTripped();
91
+ if (breakerState.tripped) {
92
+ telemetry.circuitBreakerTrips++;
93
+ options.onProgress?.({
94
+ type: 'breaker_trip',
95
+ variantId: variant.id,
96
+ opcodeIndex: i,
97
+ message: breakerState.reason,
98
+ });
99
+ return {
100
+ variantId: variant.id,
101
+ success: false,
102
+ opcodeResults,
103
+ durationMs: Date.now() - startTime,
104
+ artifacts,
105
+ error: `circuit breaker tripped: ${breakerState.reason}`,
106
+ };
107
+ }
108
+ const opcode = program.steps[i];
109
+ options.onProgress?.({
110
+ type: 'opcode_start',
111
+ variantId: variant.id,
112
+ opcodeIndex: i,
113
+ opcodeKind: opcode.kind,
114
+ message: opcode.description,
115
+ });
116
+ const result = await executeOpcode(opcode, i, adapter, verifier, breaker, recoveryChain, telemetry, healerPatches, artifacts, options, variant.id, executionState, program.artifactPlan, variant);
117
+ opcodeResults.push(result);
118
+ telemetry.totalOpcodes++;
119
+ if (result.status === 'recovered')
120
+ telemetry.recoveredOpcodes++;
121
+ if (result.status === 'failed')
122
+ telemetry.failedOpcodes++;
123
+ options.onProgress?.({
124
+ type: 'opcode_end',
125
+ variantId: variant.id,
126
+ opcodeIndex: i,
127
+ opcodeKind: opcode.kind,
128
+ status: result.status,
129
+ message: result.error ?? `${opcode.kind} ${result.status}`,
130
+ });
131
+ // Abort variant on fatal opcode failure
132
+ if (result.status === 'failed') {
133
+ return {
134
+ variantId: variant.id,
135
+ success: false,
136
+ opcodeResults,
137
+ durationMs: Date.now() - startTime,
138
+ artifacts,
139
+ error: `opcode ${i} (${opcode.kind}) failed: ${result.error}`,
140
+ };
141
+ }
142
+ }
143
+ return {
144
+ variantId: variant.id,
145
+ success: true,
146
+ opcodeResults,
147
+ durationMs: Date.now() - startTime,
148
+ artifacts,
149
+ };
150
+ }
151
+ catch (err) {
152
+ return {
153
+ variantId: variant.id,
154
+ success: false,
155
+ opcodeResults,
156
+ durationMs: Date.now() - startTime,
157
+ artifacts,
158
+ error: `unexpected error: ${err instanceof Error ? err.message : String(err)}`,
159
+ };
160
+ }
161
+ finally {
162
+ if (adapter) {
163
+ try {
164
+ await adapter.close();
165
+ }
166
+ catch { /* ignore close errors */ }
167
+ }
168
+ }
169
+ }
170
+ // ── Single opcode execution ─────────────────────────────────────────
171
+ async function executeOpcode(opcode, index, adapter, verifier, breaker, recoveryChain, telemetry, healerPatches, artifacts, options, variantId, executionState, artifactPlan, currentVariant) {
172
+ const startTime = Date.now();
173
+ const deadlineMs = startTime + opcode.timeoutMs;
174
+ const isInteraction = ['CLICK', 'TYPE', 'PRESS_KEY', 'SCROLL'].includes(opcode.kind);
175
+ // Track page context for circuit breaker
176
+ try {
177
+ const url = await adapter.getCurrentUrl();
178
+ breaker.setPage(url);
179
+ }
180
+ catch { /* ignore */ }
181
+ // Execute with timeout
182
+ try {
183
+ if (isInteraction) {
184
+ await verifier.captureBeforeState(adapter);
185
+ }
186
+ const actionBudgetMs = getRemainingTimeMs(deadlineMs);
187
+ if (actionBudgetMs <= 0) {
188
+ return handleFailure(opcode, index, adapter, breaker, recoveryChain, telemetry, healerPatches, options, variantId, startTime, deadlineMs, `timeout after ${opcode.timeoutMs}ms`);
189
+ }
190
+ const result = await withTimeout(() => executeOpcodeAction(opcode, index, adapter, artifacts, telemetry, currentVariant, executionState, artifactPlan, options), actionBudgetMs);
191
+ if (!result.success) {
192
+ return handleFailure(opcode, index, adapter, breaker, recoveryChain, telemetry, healerPatches, options, variantId, startTime, deadlineMs, result.error ?? 'action failed');
193
+ }
194
+ // Verify postcondition
195
+ const postconditionBudgetMs = getRemainingTimeMs(deadlineMs);
196
+ if (postconditionBudgetMs <= 0) {
197
+ return handleFailure(opcode, index, adapter, breaker, recoveryChain, telemetry, healerPatches, options, variantId, startTime, deadlineMs, `timeout after ${opcode.timeoutMs}ms`);
198
+ }
199
+ const postcondition = await evaluatePostcondition(adapter, withClampedPostconditionTimeout(opcode.postcondition, postconditionBudgetMs));
200
+ if (!postcondition.passed) {
201
+ return handleFailure(opcode, index, adapter, breaker, recoveryChain, telemetry, healerPatches, options, variantId, startTime, deadlineMs, `postcondition failed: ${postcondition.reason}`);
202
+ }
203
+ // Verify action had effect (for interaction opcodes)
204
+ if (isInteraction) {
205
+ const verification = await verifier.verifyAfterAction(adapter);
206
+ if (!verification.hadEffect && opcode.postcondition.type !== 'always' && opcode.postcondition.type !== 'any_change') {
207
+ return handleFailure(opcode, index, adapter, breaker, recoveryChain, telemetry, healerPatches, options, variantId, startTime, deadlineMs, `action had no effect: ${verification.summary}`);
208
+ }
209
+ }
210
+ breaker.recordSuccess(index);
211
+ return {
212
+ opcodeIndex: index,
213
+ kind: opcode.kind,
214
+ status: 'ok',
215
+ durationMs: Date.now() - startTime,
216
+ recoveryAttempts: 0,
217
+ };
218
+ }
219
+ catch (err) {
220
+ const errorMsg = err instanceof Error ? err.message : String(err);
221
+ return handleFailure(opcode, index, adapter, breaker, recoveryChain, telemetry, healerPatches, options, variantId, startTime, deadlineMs, errorMsg);
222
+ }
223
+ }
224
+ // ── Failure handling with recovery ──────────────────────────────────
225
+ async function handleFailure(opcode, index, adapter, breaker, recoveryChain, telemetry, healerPatches, options, variantId, startTime, deadlineMs, errorMsg) {
226
+ const breakerState = breaker.recordFailure(index, opcode.maxFailures);
227
+ if (breakerState.tripped) {
228
+ telemetry.circuitBreakerTrips++;
229
+ return {
230
+ opcodeIndex: index,
231
+ kind: opcode.kind,
232
+ status: 'failed',
233
+ durationMs: Date.now() - startTime,
234
+ recoveryAttempts: 0,
235
+ error: `${errorMsg} (circuit breaker: ${breakerState.reason})`,
236
+ };
237
+ }
238
+ const remainingTimeMs = getRemainingTimeMs(deadlineMs);
239
+ if (remainingTimeMs <= 0) {
240
+ return {
241
+ opcodeIndex: index,
242
+ kind: opcode.kind,
243
+ status: 'failed',
244
+ durationMs: Date.now() - startTime,
245
+ recoveryAttempts: 0,
246
+ error: `${errorMsg} (timeout after ${opcode.timeoutMs}ms)`,
247
+ };
248
+ }
249
+ // Attempt recovery
250
+ options.onProgress?.({
251
+ type: 'recovery',
252
+ variantId,
253
+ opcodeIndex: index,
254
+ opcodeKind: opcode.kind,
255
+ message: `recovering from: ${errorMsg}`,
256
+ });
257
+ const recovery = await recoveryChain.attempt(opcode, index, adapter, {
258
+ remainingTimeMs,
259
+ maxDeterministicRetries: Math.max(0, opcode.maxFailures - breakerState.opcodeFailures),
260
+ });
261
+ if (recovery.recovered) {
262
+ if (recovery.patch) {
263
+ healerPatches.push(recovery.patch);
264
+ telemetry.healerInvocations++;
265
+ }
266
+ breaker.recordSuccess(index);
267
+ return {
268
+ opcodeIndex: index,
269
+ kind: opcode.kind,
270
+ status: 'recovered',
271
+ durationMs: Date.now() - startTime,
272
+ recoveryAttempts: 1,
273
+ recoveryStrategy: recovery.strategy,
274
+ };
275
+ }
276
+ return {
277
+ opcodeIndex: index,
278
+ kind: opcode.kind,
279
+ status: 'failed',
280
+ durationMs: Date.now() - startTime,
281
+ recoveryAttempts: 1,
282
+ error: `${errorMsg} (recovery failed: ${recovery.reason})`,
283
+ };
284
+ }
285
+ // ── Opcode action dispatch ──────────────────────────────────────────
286
+ async function executeOpcodeAction(opcode, opcodeIndex, adapter, artifacts, telemetry, currentVariant, executionState, artifactPlan, runOptions) {
287
+ try {
288
+ void artifactPlan;
289
+ switch (opcode.kind) {
290
+ case 'NAVIGATE':
291
+ await adapter.navigate(opcode.url);
292
+ break;
293
+ case 'DISMISS_OVERLAYS':
294
+ await adapter.dismissOverlays();
295
+ break;
296
+ case 'ASSERT_ROUTE':
297
+ return evaluateImmediateAssertion(await evaluatePostcondition(adapter, {
298
+ type: 'route_matches',
299
+ pattern: opcode.urlPattern,
300
+ waitMs: 1,
301
+ }), 'ASSERT_ROUTE failed');
302
+ case 'ASSERT_SURFACE':
303
+ return evaluateSurfaceAssertion(adapter, opcode.selectors, opcode.matchAll);
304
+ case 'CLICK':
305
+ try {
306
+ await adapter.click(opcode.selector);
307
+ }
308
+ catch (error) {
309
+ if (!opcode.target || !adapter.clickByTarget)
310
+ throw error;
311
+ await adapter.clickByTarget({
312
+ selector: opcode.selector,
313
+ target: opcode.target,
314
+ selectorAlternates: opcode.selectorAlternates,
315
+ });
316
+ }
317
+ break;
318
+ case 'TYPE':
319
+ try {
320
+ await adapter.type(opcode.selector, opcode.text, opcode.clearFirst);
321
+ }
322
+ catch (error) {
323
+ if (!opcode.target || !adapter.typeByTarget)
324
+ throw error;
325
+ await adapter.typeByTarget({
326
+ selector: opcode.selector,
327
+ target: opcode.target,
328
+ selectorAlternates: opcode.selectorAlternates,
329
+ }, opcode.text, opcode.clearFirst);
330
+ }
331
+ break;
332
+ case 'PRESS_KEY':
333
+ await adapter.pressKey(opcode.key);
334
+ break;
335
+ case 'WAIT_FOR': {
336
+ // WAIT_FOR supports both selector and semantic target
337
+ const waitSelector = opcode.selector;
338
+ if (waitSelector) {
339
+ const found = await adapter.waitFor({
340
+ selector: waitSelector,
341
+ state: opcode.state,
342
+ timeoutMs: opcode.timeoutMs,
343
+ });
344
+ if (!found) {
345
+ return { success: false, error: `element "${waitSelector}" not found within timeout` };
346
+ }
347
+ }
348
+ else if (opcode.target && adapter.waitForTarget) {
349
+ const found = await adapter.waitForTarget({ target: opcode.target }, opcode.timeoutMs);
350
+ if (!found) {
351
+ return { success: false, error: `target "${opcode.target.text ?? opcode.target.role ?? 'unknown'}" not found within timeout` };
352
+ }
353
+ }
354
+ else {
355
+ return { success: false, error: 'WAIT_FOR needs a selector or target' };
356
+ }
357
+ break;
358
+ }
359
+ case 'SET_LOCALE':
360
+ return applyLocaleOpcode(opcode, adapter);
361
+ case 'SET_THEME':
362
+ return applyThemeOpcode(opcode, adapter);
363
+ case 'SCROLL':
364
+ if (opcode.targetSelector) {
365
+ await adapter.scrollIntoView(opcode.targetSelector);
366
+ }
367
+ else if (opcode.target && adapter.scrollIntoViewByTarget) {
368
+ await adapter.scrollIntoViewByTarget({ target: opcode.target });
369
+ }
370
+ else {
371
+ await adapter.scroll(opcode.direction, opcode.amount);
372
+ }
373
+ break;
374
+ case 'CAPTURE_SCREENSHOT': {
375
+ await smartWaitForStability(adapter, { maxWaitMs: 5000 });
376
+ const captureUrl = await adapter.getCurrentUrl();
377
+ const takeBuffer = async () => {
378
+ if (opcode.elementSelector && adapter.takeElementScreenshot) {
379
+ return adapter.takeElementScreenshot(opcode.elementSelector);
380
+ }
381
+ if (opcode.elementSelector) {
382
+ throw new Error(`element capture requires adapter support for selector "${opcode.elementSelector}"`);
383
+ }
384
+ return adapter.takeScreenshot();
385
+ };
386
+ let buffer = await takeBuffer();
387
+ if (runOptions?.llmConfig) {
388
+ const verification = await verifyCaptureQuality(buffer, {
389
+ expectedDescription: opcode.description,
390
+ url: captureUrl,
391
+ locale: currentVariant?.locale,
392
+ theme: currentVariant?.theme,
393
+ }, runOptions.llmConfig);
394
+ if (verification.llmResult) {
395
+ telemetry.llmCallCount++;
396
+ telemetry.llmCostEur += verification.llmResult.costEur;
397
+ telemetry.llmStepUsages.push({
398
+ stepType: 'capture_verification',
399
+ generationId: verification.llmResult.generationId,
400
+ model: verification.llmResult.model,
401
+ promptTokens: verification.llmResult.promptTokens,
402
+ completionTokens: verification.llmResult.completionTokens,
403
+ });
404
+ }
405
+ if (!verification.passed) {
406
+ await smartWaitForStability(adapter, { maxWaitMs: 8000 });
407
+ const retryBuffer = await takeBuffer();
408
+ const retryVerification = await verifyCaptureQuality(retryBuffer, {
409
+ expectedDescription: opcode.description,
410
+ url: captureUrl,
411
+ locale: currentVariant?.locale,
412
+ theme: currentVariant?.theme,
413
+ }, runOptions.llmConfig);
414
+ if (retryVerification.llmResult) {
415
+ telemetry.llmCallCount++;
416
+ telemetry.llmCostEur += retryVerification.llmResult.costEur;
417
+ telemetry.llmStepUsages.push({
418
+ stepType: 'capture_verification',
419
+ generationId: retryVerification.llmResult.generationId,
420
+ model: retryVerification.llmResult.model,
421
+ promptTokens: retryVerification.llmResult.promptTokens,
422
+ completionTokens: retryVerification.llmResult.completionTokens,
423
+ });
424
+ }
425
+ if (retryVerification.passed) {
426
+ buffer = retryBuffer;
427
+ }
428
+ }
429
+ }
430
+ let altText;
431
+ if (runOptions?.llmConfig) {
432
+ try {
433
+ const altResult = await generateAltText(buffer, {
434
+ description: opcode.description,
435
+ url: captureUrl,
436
+ locale: currentVariant?.locale,
437
+ presetName: runOptions.presetName,
438
+ }, runOptions.llmConfig);
439
+ altText = altResult.altText;
440
+ if (altResult.llmResult) {
441
+ telemetry.llmCallCount++;
442
+ telemetry.llmCostEur += altResult.llmResult.costEur;
443
+ telemetry.llmStepUsages.push({
444
+ stepType: 'alt_text_generation',
445
+ generationId: altResult.llmResult.generationId,
446
+ model: altResult.llmResult.model,
447
+ promptTokens: altResult.llmResult.promptTokens,
448
+ completionTokens: altResult.llmResult.completionTokens,
449
+ });
450
+ }
451
+ }
452
+ catch {
453
+ // Alt text generation failed — non-fatal
454
+ }
455
+ }
456
+ artifacts.push({
457
+ mediaMode: 'screenshot',
458
+ buffer,
459
+ mimeType: 'image/png',
460
+ captureType: opcode.elementSelector ? 'element' : 'fullpage',
461
+ captureUrl,
462
+ dimensions: currentVariant?.viewport,
463
+ captureId: opcode.captureId,
464
+ captureName: opcode.captureName ?? opcode.description,
465
+ elementSelector: opcode.elementSelector,
466
+ altText,
467
+ stepDescription: opcode.description,
468
+ stepIndex: opcodeIndex,
469
+ variantId: currentVariant?.id,
470
+ });
471
+ break;
472
+ }
473
+ case 'BEGIN_CLIP':
474
+ if (executionState.activeClip) {
475
+ return { success: false, error: 'cannot start a new clip before the previous one ends' };
476
+ }
477
+ executionState.activeClip = {
478
+ clipId: opcode.clipId,
479
+ clipName: opcode.clipName,
480
+ };
481
+ await adapter.beginRecording({ mediaMode: 'clip' });
482
+ break;
483
+ case 'END_CLIP': {
484
+ const clipIdentity = resolveClipIdentity(executionState.activeClip, opcode);
485
+ const recording = await adapter.endRecording();
486
+ executionState.activeClip = undefined;
487
+ artifacts.push({
488
+ mediaMode: 'clip',
489
+ buffer: recording.buffer,
490
+ mimeType: recording.mimeType,
491
+ durationMs: recording.durationMs,
492
+ trimStartMs: recording.trimStartMs,
493
+ dimensions: undefined,
494
+ captureType: 'fullpage',
495
+ captureUrl: await adapter.getCurrentUrl(),
496
+ clipId: clipIdentity.clipId,
497
+ clipName: clipIdentity.clipName,
498
+ stepDescription: opcode.description,
499
+ stepIndex: opcodeIndex,
500
+ variantId: currentVariant?.id,
501
+ });
502
+ break;
503
+ }
504
+ default: {
505
+ const _exhaustive = opcode;
506
+ return { success: false, error: `unknown opcode kind: ${opcode.kind}` };
507
+ }
508
+ }
509
+ return { success: true };
510
+ }
511
+ catch (err) {
512
+ return {
513
+ success: false,
514
+ error: err instanceof Error ? err.message : String(err),
515
+ };
516
+ }
517
+ }
518
+ // ── Helpers ─────────────────────────────────────────────────────────
519
+ async function withTimeout(fn, timeoutMs) {
520
+ return new Promise((resolve, reject) => {
521
+ const timer = setTimeout(() => reject(new Error(`timeout after ${timeoutMs}ms`)), timeoutMs);
522
+ fn()
523
+ .then(result => { clearTimeout(timer); resolve(result); })
524
+ .catch(err => { clearTimeout(timer); reject(err); });
525
+ });
526
+ }
527
+ function getRemainingTimeMs(deadlineMs) {
528
+ return Math.max(0, deadlineMs - Date.now());
529
+ }
530
+ function resolveClipIdentity(activeClip, opcode) {
531
+ if (activeClip?.clipId && opcode.clipId && activeClip.clipId !== opcode.clipId) {
532
+ throw new Error(`END_CLIP clipId "${opcode.clipId}" does not match active clip "${activeClip.clipId}"`);
533
+ }
534
+ return {
535
+ clipId: opcode.clipId ?? activeClip?.clipId,
536
+ clipName: opcode.clipName ?? activeClip?.clipName ?? opcode.description,
537
+ };
538
+ }
539
+ function withClampedPostconditionTimeout(spec, maxWaitMs) {
540
+ return {
541
+ ...spec,
542
+ waitMs: Math.max(1, Math.min(spec.waitMs ?? maxWaitMs, maxWaitMs)),
543
+ };
544
+ }
545
+ function evaluateImmediateAssertion(result, prefix) {
546
+ return result.passed
547
+ ? { success: true }
548
+ : { success: false, error: `${prefix}: ${result.reason}` };
549
+ }
550
+ async function evaluateSurfaceAssertion(adapter, selectors, matchAll) {
551
+ const checks = await Promise.all(selectors.map(async (selector) => ({
552
+ selector,
553
+ result: await evaluatePostcondition(adapter, {
554
+ type: 'element_visible',
555
+ selector,
556
+ waitMs: 1,
557
+ }),
558
+ })));
559
+ if (matchAll) {
560
+ const failed = checks.find((entry) => !entry.result.passed);
561
+ return failed
562
+ ? { success: false, error: `ASSERT_SURFACE failed for "${failed.selector}": ${failed.result.reason}` }
563
+ : { success: true };
564
+ }
565
+ if (checks.some((entry) => entry.result.passed)) {
566
+ return { success: true };
567
+ }
568
+ return {
569
+ success: false,
570
+ error: `ASSERT_SURFACE failed: none of the selectors matched (${selectors.join(', ')})`,
571
+ };
572
+ }
573
+ async function applyLocaleOpcode(opcode, adapter) {
574
+ if (opcode.method === 'browser_context') {
575
+ await adapter.setLocale(opcode.locale);
576
+ if (adapter.reloadPage) {
577
+ await adapter.reloadPage();
578
+ }
579
+ return { success: true };
580
+ }
581
+ if (opcode.method === 'ui_interaction') {
582
+ if (!opcode.selector) {
583
+ return { success: false, error: 'SET_LOCALE ui_interaction requires selector' };
584
+ }
585
+ await adapter.click(opcode.selector);
586
+ return { success: true };
587
+ }
588
+ if (!opcode.storageHints || opcode.storageHints.length === 0 || !adapter.writeStorageHint) {
589
+ return { success: false, error: 'SET_LOCALE storage mode requires storageHints and adapter storage support' };
590
+ }
591
+ const writes = await Promise.all(opcode.storageHints.map((hint) => adapter.writeStorageHint({
592
+ storage: hint.storage,
593
+ key: hint.key,
594
+ value: hint.value,
595
+ kind: 'locale',
596
+ })));
597
+ if (!writes.some(Boolean)) {
598
+ return { success: false, error: 'SET_LOCALE storage hints did not match any writable locale keys' };
599
+ }
600
+ if (adapter.reloadPage) {
601
+ await adapter.reloadPage();
602
+ }
603
+ return { success: true };
604
+ }
605
+ async function applyThemeOpcode(opcode, adapter) {
606
+ if (opcode.method === 'color_scheme') {
607
+ await adapter.setColorScheme(opcode.theme);
608
+ return { success: true };
609
+ }
610
+ if (opcode.method === 'ui_interaction') {
611
+ if (!opcode.selector) {
612
+ return { success: false, error: 'SET_THEME ui_interaction requires selector' };
613
+ }
614
+ await adapter.click(opcode.selector);
615
+ return { success: true };
616
+ }
617
+ if (!opcode.storageHints || opcode.storageHints.length === 0 || !adapter.writeStorageHint) {
618
+ return { success: false, error: 'SET_THEME storage mode requires storageHints and adapter storage support' };
619
+ }
620
+ const writes = await Promise.all(opcode.storageHints.map((hint) => adapter.writeStorageHint({
621
+ storage: hint.storage,
622
+ key: hint.key,
623
+ value: hint.value,
624
+ kind: 'theme',
625
+ })));
626
+ if (!writes.some(Boolean)) {
627
+ return { success: false, error: 'SET_THEME storage hints did not match any writable theme keys' };
628
+ }
629
+ if (adapter.reloadPage) {
630
+ await adapter.reloadPage();
631
+ }
632
+ return { success: true };
633
+ }
634
+ //# sourceMappingURL=opcode-runner.js.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Capture Agent — Overlay Engine
3
+ *
4
+ * Consolidated deterministic overlay handling.
5
+ * Wraps and extends the existing cookie-dismiss module with
6
+ * additional patterns for newsletter popups, chat widgets, and age gates.
7
+ *
8
+ * This module is used by the DISMISS_OVERLAYS opcode.
9
+ */
10
+ import type { RuntimeAdapter } from './types.js';
11
+ export interface OverlayDismissResult {
12
+ dismissed: boolean;
13
+ methods: string[];
14
+ overlaysFound: number;
15
+ overlaysRemaining: number;
16
+ }
17
+ /**
18
+ * Multi-pass overlay dismissal.
19
+ * 1. Delegate to the adapter's built-in dismissOverlays (cookie-dismiss.ts)
20
+ * 2. Check AKTree for remaining blocking overlays
21
+ * 3. Try additional heuristics for newsletter/chat/age gate
22
+ * 4. Final AKTree check
23
+ */
24
+ export declare function dismissAllOverlays(adapter: RuntimeAdapter): Promise<OverlayDismissResult>;