autokap 1.0.7 → 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 (278) hide show
  1. package/assets/cursors/macos.svg +4 -0
  2. package/assets/cursors/windows.svg +15 -0
  3. package/assets/skill/OPCODE-REFERENCE.md +607 -0
  4. package/assets/skill/README.md +39 -0
  5. package/assets/skill/SKILL.md +453 -468
  6. package/assets/skill/STUDIO-SKILL.md +476 -0
  7. package/assets/skill/references/examples.md +104 -0
  8. package/assets/skill/references/interactive-demo.md +225 -0
  9. package/assets/skill/references/mock-data.md +178 -0
  10. package/dist/action-verifier.d.ts +29 -0
  11. package/dist/action-verifier.js +133 -0
  12. package/dist/agent-action-recovery.d.ts +45 -0
  13. package/dist/agent-action-recovery.js +370 -0
  14. package/dist/agent-message-utils.d.ts +21 -0
  15. package/dist/agent-message-utils.js +77 -0
  16. package/dist/agent-url-utils.d.ts +30 -0
  17. package/dist/agent-url-utils.js +138 -0
  18. package/dist/agent.d.ts +92 -8
  19. package/dist/agent.js +2936 -781
  20. package/dist/ak-tree.d.ts +39 -0
  21. package/dist/ak-tree.js +368 -0
  22. package/dist/alt-text.d.ts +26 -0
  23. package/dist/alt-text.js +55 -0
  24. package/dist/auth-capture.d.ts +17 -0
  25. package/dist/auth-capture.js +164 -0
  26. package/dist/benchmark.d.ts +59 -0
  27. package/dist/benchmark.js +135 -0
  28. package/dist/browser-bar.d.ts +14 -6
  29. package/dist/browser-bar.js +145 -8
  30. package/dist/browser-pool.d.ts +7 -0
  31. package/dist/browser-pool.js +15 -5
  32. package/dist/browser-utils.d.ts +31 -0
  33. package/dist/browser-utils.js +97 -0
  34. package/dist/browser.d.ts +51 -1
  35. package/dist/browser.js +1481 -31
  36. package/dist/capture-alt-text.js +2 -1
  37. package/dist/capture-language-preflight.js +14 -0
  38. package/dist/capture-llm-page-identity.js +22 -10
  39. package/dist/capture-page-identity.d.ts +5 -7
  40. package/dist/capture-page-identity.js +211 -78
  41. package/dist/capture-preset-credentials.d.ts +50 -0
  42. package/dist/capture-preset-credentials.js +127 -0
  43. package/dist/capture-request-plan.d.ts +2 -2
  44. package/dist/capture-request-plan.js +64 -16
  45. package/dist/capture-run-optimizer.js +48 -33
  46. package/dist/capture-selector-memory.d.ts +5 -0
  47. package/dist/capture-selector-memory.js +18 -0
  48. package/dist/capture-strategy.d.ts +36 -0
  49. package/dist/capture-strategy.js +95 -0
  50. package/dist/capture-studio-sync.d.ts +1 -0
  51. package/dist/capture-studio-sync.js +9 -3
  52. package/dist/capture-surface-contract.d.ts +36 -0
  53. package/dist/capture-surface-contract.js +299 -0
  54. package/dist/capture-transition-engine.d.ts +28 -0
  55. package/dist/capture-transition-engine.js +292 -0
  56. package/dist/capture-variant-state.d.ts +2 -0
  57. package/dist/capture-variant-state.js +26 -0
  58. package/dist/capture-verification.d.ts +35 -0
  59. package/dist/capture-verification.js +95 -0
  60. package/dist/capture-viewport-lock.d.ts +48 -0
  61. package/dist/capture-viewport-lock.js +74 -0
  62. package/dist/circuit-breaker.d.ts +42 -0
  63. package/dist/circuit-breaker.js +119 -0
  64. package/dist/cli-config.d.ts +8 -1
  65. package/dist/cli-config.js +62 -6
  66. package/dist/cli-contract.d.ts +15 -0
  67. package/dist/cli-contract.js +167 -0
  68. package/dist/cli-runner-local.d.ts +12 -0
  69. package/dist/cli-runner-local.js +102 -0
  70. package/dist/cli-runner.d.ts +34 -0
  71. package/dist/cli-runner.js +433 -0
  72. package/dist/cli-utils.d.ts +0 -1
  73. package/dist/cli-utils.js +2 -5
  74. package/dist/cli.js +1005 -267
  75. package/dist/clip-orchestrator.js +9 -2
  76. package/dist/clip-postprocess.js +25 -16
  77. package/dist/cookie-dismiss.d.ts +2 -0
  78. package/dist/cookie-dismiss.js +48 -13
  79. package/dist/cost-logging.d.ts +8 -0
  80. package/dist/cost-logging.js +160 -46
  81. package/dist/cost-resolution-monitor.d.ts +16 -0
  82. package/dist/cost-resolution-monitor.js +34 -0
  83. package/dist/credential-templates.js +2 -2
  84. package/dist/cursor-overlay-script.d.ts +6 -0
  85. package/dist/cursor-overlay-script.js +169 -0
  86. package/dist/dom-css-purger.d.ts +65 -0
  87. package/dist/dom-css-purger.js +333 -0
  88. package/dist/dom-font-inliner.d.ts +45 -0
  89. package/dist/dom-font-inliner.js +148 -0
  90. package/dist/dom-patch-resolver.d.ts +52 -0
  91. package/dist/dom-patch-resolver.js +242 -0
  92. package/dist/dom-serializer.d.ts +82 -0
  93. package/dist/dom-serializer.js +378 -0
  94. package/dist/element-capture.d.ts +1 -41
  95. package/dist/element-capture.js +202 -446
  96. package/dist/env-validation.d.ts +5 -0
  97. package/dist/env-validation.js +29 -0
  98. package/dist/execution-schema.d.ts +4423 -0
  99. package/dist/execution-schema.js +507 -0
  100. package/dist/execution-types.d.ts +886 -0
  101. package/dist/execution-types.js +65 -0
  102. package/dist/fonts-loader.d.ts +14 -0
  103. package/dist/fonts-loader.js +55 -0
  104. package/dist/hybrid-navigator.js +12 -12
  105. package/dist/index.d.ts +9 -6
  106. package/dist/index.js +10 -4
  107. package/dist/legacy/agent-action-recovery.d.ts +45 -0
  108. package/dist/legacy/agent-action-recovery.js +370 -0
  109. package/dist/legacy/agent-message-utils.d.ts +21 -0
  110. package/dist/legacy/agent-message-utils.js +77 -0
  111. package/dist/legacy/agent-url-utils.d.ts +30 -0
  112. package/dist/legacy/agent-url-utils.js +138 -0
  113. package/dist/legacy/agent.d.ts +226 -0
  114. package/dist/legacy/agent.js +6666 -0
  115. package/dist/legacy/clip-orchestrator.d.ts +148 -0
  116. package/dist/legacy/clip-orchestrator.js +957 -0
  117. package/dist/legacy/credential-templates.d.ts +5 -0
  118. package/dist/legacy/credential-templates.js +60 -0
  119. package/dist/legacy/hybrid-navigator.d.ts +138 -0
  120. package/dist/legacy/hybrid-navigator.js +468 -0
  121. package/dist/legacy/llm-usage.d.ts +17 -0
  122. package/dist/legacy/llm-usage.js +45 -0
  123. package/dist/legacy/prompt-cache.d.ts +10 -0
  124. package/dist/legacy/prompt-cache.js +24 -0
  125. package/dist/legacy/prompts.d.ts +175 -0
  126. package/dist/legacy/prompts.js +1038 -0
  127. package/dist/legacy/tools.d.ts +4 -0
  128. package/dist/legacy/tools.js +216 -0
  129. package/dist/legacy/video-agent.d.ts +143 -0
  130. package/dist/legacy/video-agent.js +4788 -0
  131. package/dist/legacy/video-observation.d.ts +36 -0
  132. package/dist/legacy/video-observation.js +192 -0
  133. package/dist/legacy/video-planner.d.ts +12 -0
  134. package/dist/legacy/video-planner.js +501 -0
  135. package/dist/legacy/video-prompts.d.ts +37 -0
  136. package/dist/legacy/video-prompts.js +569 -0
  137. package/dist/legacy/video-tools.d.ts +3 -0
  138. package/dist/legacy/video-tools.js +59 -0
  139. package/dist/legacy/video-variant-state.d.ts +29 -0
  140. package/dist/legacy/video-variant-state.js +80 -0
  141. package/dist/legacy/vision-model.d.ts +17 -0
  142. package/dist/legacy/vision-model.js +74 -0
  143. package/dist/llm-healer.d.ts +63 -0
  144. package/dist/llm-healer.js +166 -0
  145. package/dist/llm-provider.d.ts +29 -0
  146. package/dist/llm-provider.js +80 -0
  147. package/dist/logger.d.ts +6 -2
  148. package/dist/logger.js +15 -1
  149. package/dist/mockup-html.js +35 -25
  150. package/dist/mockup.d.ts +95 -2
  151. package/dist/mockup.js +427 -166
  152. package/dist/mouse-animation.d.ts +2 -2
  153. package/dist/mouse-animation.js +34 -20
  154. package/dist/opcode-actions.d.ts +42 -0
  155. package/dist/opcode-actions.js +511 -0
  156. package/dist/opcode-runner.d.ts +51 -0
  157. package/dist/opcode-runner.js +770 -0
  158. package/dist/openrouter-client.d.ts +40 -0
  159. package/dist/openrouter-client.js +16 -0
  160. package/dist/overlay-engine.d.ts +24 -0
  161. package/dist/overlay-engine.js +176 -0
  162. package/dist/postcondition.d.ts +16 -0
  163. package/dist/postcondition.js +269 -0
  164. package/dist/program-patcher.d.ts +25 -0
  165. package/dist/program-patcher.js +44 -0
  166. package/dist/prompts.d.ts +13 -5
  167. package/dist/prompts.js +224 -351
  168. package/dist/provider-config.d.ts +12 -0
  169. package/dist/provider-config.js +15 -0
  170. package/dist/recovery-chain.d.ts +37 -0
  171. package/dist/recovery-chain.js +350 -0
  172. package/dist/remote-browser.d.ts +28 -4
  173. package/dist/remote-browser.js +60 -5
  174. package/dist/safari-browser-bar.d.ts +15 -0
  175. package/dist/safari-browser-bar.js +95 -0
  176. package/dist/safari-toolbar-asset.d.ts +15 -0
  177. package/dist/safari-toolbar-asset.js +12 -0
  178. package/dist/security.d.ts +2 -1
  179. package/dist/security.js +49 -10
  180. package/dist/selector-resolver.d.ts +34 -0
  181. package/dist/selector-resolver.js +181 -0
  182. package/dist/semantic-resolver.d.ts +35 -0
  183. package/dist/semantic-resolver.js +161 -0
  184. package/dist/server-capture-runtime.d.ts +5 -3
  185. package/dist/server-capture-runtime.js +42 -95
  186. package/dist/server-credit-usage.d.ts +2 -2
  187. package/dist/server-project-webhooks.d.ts +15 -1
  188. package/dist/server-project-webhooks.js +34 -8
  189. package/dist/server-screenshot-watermark.js +27 -5
  190. package/dist/session-profile.js +164 -1
  191. package/dist/sf-pro-symbols.d.ts +1 -0
  192. package/dist/sf-pro-symbols.js +55 -0
  193. package/dist/skill-packaging.d.ts +28 -0
  194. package/dist/skill-packaging.js +169 -0
  195. package/dist/smart-wait.d.ts +27 -0
  196. package/dist/smart-wait.js +81 -0
  197. package/dist/status-bar-render.d.ts +20 -0
  198. package/dist/status-bar-render.js +410 -0
  199. package/dist/status-bar.d.ts +9 -0
  200. package/dist/status-bar.js +298 -14
  201. package/dist/svg-browser-bar.d.ts +33 -0
  202. package/dist/svg-browser-bar.js +206 -0
  203. package/dist/svg-status-bar.d.ts +36 -0
  204. package/dist/svg-status-bar.js +597 -0
  205. package/dist/svg-text.d.ts +61 -0
  206. package/dist/svg-text.js +118 -0
  207. package/dist/tools.js +89 -451
  208. package/dist/types.d.ts +240 -5
  209. package/dist/types.js +23 -1
  210. package/dist/v2/action-verifier.d.ts +29 -0
  211. package/dist/v2/action-verifier.js +133 -0
  212. package/dist/v2/alt-text.d.ts +26 -0
  213. package/dist/v2/alt-text.js +55 -0
  214. package/dist/v2/benchmark.d.ts +59 -0
  215. package/dist/v2/benchmark.js +135 -0
  216. package/dist/v2/capture-strategy.d.ts +30 -0
  217. package/dist/v2/capture-strategy.js +67 -0
  218. package/dist/v2/capture-verification.d.ts +35 -0
  219. package/dist/v2/capture-verification.js +95 -0
  220. package/dist/v2/circuit-breaker.d.ts +42 -0
  221. package/dist/v2/circuit-breaker.js +119 -0
  222. package/dist/v2/cli-runner-local.d.ts +11 -0
  223. package/dist/v2/cli-runner-local.js +91 -0
  224. package/dist/v2/cli-runner.d.ts +34 -0
  225. package/dist/v2/cli-runner.js +300 -0
  226. package/dist/v2/compiler-prompts.d.ts +27 -0
  227. package/dist/v2/compiler-prompts.js +123 -0
  228. package/dist/v2/compiler.d.ts +37 -0
  229. package/dist/v2/compiler.js +147 -0
  230. package/dist/v2/explorer.d.ts +41 -0
  231. package/dist/v2/explorer.js +56 -0
  232. package/dist/v2/index.d.ts +37 -0
  233. package/dist/v2/index.js +31 -0
  234. package/dist/v2/llm-healer.d.ts +62 -0
  235. package/dist/v2/llm-healer.js +166 -0
  236. package/dist/v2/llm-provider.d.ts +29 -0
  237. package/dist/v2/llm-provider.js +80 -0
  238. package/dist/v2/opcode-runner.d.ts +47 -0
  239. package/dist/v2/opcode-runner.js +634 -0
  240. package/dist/v2/overlay-engine.d.ts +24 -0
  241. package/dist/v2/overlay-engine.js +150 -0
  242. package/dist/v2/postcondition.d.ts +16 -0
  243. package/dist/v2/postcondition.js +249 -0
  244. package/dist/v2/program-patcher.d.ts +25 -0
  245. package/dist/v2/program-patcher.js +44 -0
  246. package/dist/v2/recovery-chain.d.ts +30 -0
  247. package/dist/v2/recovery-chain.js +368 -0
  248. package/dist/v2/schema.d.ts +2580 -0
  249. package/dist/v2/schema.js +295 -0
  250. package/dist/v2/selector-resolver.d.ts +34 -0
  251. package/dist/v2/selector-resolver.js +181 -0
  252. package/dist/v2/semantic-resolver.d.ts +35 -0
  253. package/dist/v2/semantic-resolver.js +161 -0
  254. package/dist/v2/smart-wait.d.ts +27 -0
  255. package/dist/v2/smart-wait.js +81 -0
  256. package/dist/v2/types.d.ts +444 -0
  257. package/dist/v2/types.js +19 -0
  258. package/dist/v2/web-playwright-local.d.ts +69 -0
  259. package/dist/v2/web-playwright-local.js +392 -0
  260. package/dist/version.d.ts +1 -0
  261. package/dist/version.js +5 -0
  262. package/dist/video-agent.js +18 -13
  263. package/dist/video-planner.js +2 -1
  264. package/dist/video-prompts.js +3 -3
  265. package/dist/web-playwright-local.d.ts +126 -0
  266. package/dist/web-playwright-local.js +819 -0
  267. package/dist/ws-auth.js +4 -1
  268. package/dist/ws-broadcast.d.ts +34 -0
  269. package/dist/ws-broadcast.js +85 -0
  270. package/dist/ws-connection-limits.d.ts +12 -0
  271. package/dist/ws-connection-limits.js +44 -0
  272. package/dist/ws-handler-utils.d.ts +32 -0
  273. package/dist/ws-handler-utils.js +139 -0
  274. package/dist/ws-handler.js +294 -164
  275. package/dist/ws-metrics-server.d.ts +9 -0
  276. package/dist/ws-metrics-server.js +31 -0
  277. package/dist/ws-server.js +41 -1
  278. package/package.json +51 -34
@@ -0,0 +1,292 @@
1
+ import { evaluateSurfaceContract } from "./capture-surface-contract.js";
2
+ import { evaluateRequestedLanguageState, evaluateRequestedThemeState, } from "./session-profile.js";
3
+ function normalizePath(value) {
4
+ if (!value)
5
+ return null;
6
+ try {
7
+ const parsed = new URL(value);
8
+ const pathname = parsed.pathname.replace(/\/+$/, "") || "/";
9
+ return `${parsed.origin}${pathname}`;
10
+ }
11
+ catch {
12
+ return value.trim().replace(/\/+$/, "") || null;
13
+ }
14
+ }
15
+ function sameOrigin(left, right) {
16
+ if (!left || !right)
17
+ return false;
18
+ try {
19
+ return new URL(left).origin === new URL(right).origin;
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ }
25
+ function urlMatchesPolicy(currentUrl, targetUrl, policy) {
26
+ if (policy === "ignore")
27
+ return true;
28
+ if (!currentUrl || !targetUrl)
29
+ return false;
30
+ try {
31
+ const current = new URL(currentUrl);
32
+ const target = new URL(targetUrl);
33
+ if (current.origin !== target.origin)
34
+ return false;
35
+ if (policy === "same_origin")
36
+ return true;
37
+ const normalizedTarget = target.pathname.replace(/\/+$/, "") || "/";
38
+ const normalizedCurrent = current.pathname.replace(/\/+$/, "") || "/";
39
+ if (normalizedCurrent === normalizedTarget)
40
+ return true;
41
+ return normalizedCurrent.startsWith(`${normalizedTarget}/`);
42
+ }
43
+ catch {
44
+ return normalizePath(currentUrl) === normalizePath(targetUrl);
45
+ }
46
+ }
47
+ function detectLikelyAuthMismatch(signals, profile) {
48
+ if (!signals || profile?.authState !== "authenticated")
49
+ return false;
50
+ const hasAuthenticatedHints = (signals.authHints?.logoutButtons.length ?? 0) > 0
51
+ || (signals.authHints?.accountMenuLabels.length ?? 0) > 0
52
+ || (signals.authHints?.accountLikeText.length ?? 0) > 0;
53
+ const hasAnonymousHints = signals.authHints?.hasAuthForm
54
+ || signals.authHints?.hasPasswordField
55
+ || ((signals.authHints?.loginButtons.length ?? 0) > 0 && !hasAuthenticatedHints);
56
+ return !!hasAnonymousHints && !hasAuthenticatedHints;
57
+ }
58
+ function detectLikelyVariantMismatch(params) {
59
+ const { signals, requestedLang, requestedTheme } = params;
60
+ if (!signals)
61
+ return false;
62
+ const languageState = requestedLang
63
+ ? evaluateRequestedLanguageState({
64
+ currentUrl: signals.url || signals.canonicalUrl || "",
65
+ requestedLang,
66
+ signals,
67
+ })
68
+ : null;
69
+ const themeState = requestedTheme
70
+ ? evaluateRequestedThemeState({
71
+ requestedTheme,
72
+ signals,
73
+ })
74
+ : null;
75
+ const langMismatch = !!languageState?.requested
76
+ && languageState.detected !== null
77
+ && languageState.active === false
78
+ && languageState.ambiguous === false
79
+ && languageState.confidence === "high";
80
+ const themeMismatch = !!themeState?.requested
81
+ && themeState.detected !== null
82
+ && themeState.active === false
83
+ && themeState.ambiguous === false
84
+ && themeState.confidence === "high";
85
+ return langMismatch || themeMismatch;
86
+ }
87
+ export function deriveCapturePageContract(params) {
88
+ const identity = params.pageIdentity ?? null;
89
+ switch (identity?.kind) {
90
+ case "modal_selection":
91
+ return {
92
+ expectedSurfaceKind: "dialog_selection",
93
+ dialogPolicy: "must_be_open",
94
+ routePolicy: "same_origin",
95
+ reusePolicy: "verified_only",
96
+ variantPolicy: "must_match",
97
+ };
98
+ case "modal_configuration":
99
+ return {
100
+ expectedSurfaceKind: "dialog_configuration",
101
+ dialogPolicy: "must_be_open",
102
+ routePolicy: "same_origin",
103
+ reusePolicy: "verified_only",
104
+ variantPolicy: "must_match",
105
+ };
106
+ case "editor_route":
107
+ return {
108
+ expectedSurfaceKind: "editor_route",
109
+ dialogPolicy: "must_be_closed",
110
+ routePolicy: "exact_or_descendant",
111
+ reusePolicy: "same_surface_only",
112
+ variantPolicy: "must_match",
113
+ };
114
+ case "detail_route":
115
+ return {
116
+ expectedSurfaceKind: "detail_route",
117
+ dialogPolicy: identity.dedicatedRoute ? "must_be_closed" : "allow_either",
118
+ routePolicy: identity.dedicatedRoute ? "exact_or_descendant" : "same_origin",
119
+ reusePolicy: "same_surface_only",
120
+ variantPolicy: "must_match",
121
+ };
122
+ case "gallery":
123
+ return {
124
+ expectedSurfaceKind: "gallery",
125
+ dialogPolicy: identity.dialogTarget ? "must_be_open" : "must_be_closed",
126
+ routePolicy: identity.dedicatedRoute ? "exact_or_descendant" : "same_origin",
127
+ reusePolicy: "same_surface_only",
128
+ variantPolicy: "must_match",
129
+ };
130
+ default:
131
+ return {
132
+ expectedSurfaceKind: "route",
133
+ dialogPolicy: identity?.dialogTarget ? "must_be_open" : "allow_either",
134
+ routePolicy: identity?.dedicatedRoute ? "exact_or_descendant" : "same_origin",
135
+ reusePolicy: "same_surface_only",
136
+ variantPolicy: "prefer_match",
137
+ };
138
+ }
139
+ }
140
+ export function buildCaptureCheckpointEvidence(params) {
141
+ const contract = deriveCapturePageContract({ pageIdentity: params.pageIdentity });
142
+ const reasons = [];
143
+ const urlMatched = urlMatchesPolicy(params.currentUrl ?? params.observation.url, params.targetUrl, contract.routePolicy);
144
+ if (!urlMatched) {
145
+ reasons.push("current route does not match the target route policy");
146
+ }
147
+ const dialogMatched = contract.dialogPolicy === "allow_either"
148
+ ? true
149
+ : contract.dialogPolicy === "must_be_open"
150
+ ? params.observation.dialogCount > 0
151
+ : params.observation.dialogCount === 0;
152
+ if (!dialogMatched) {
153
+ reasons.push(contract.dialogPolicy === "must_be_open"
154
+ ? "expected dialog/configuration surface is not open"
155
+ : "unexpected dialog/overlay is still open");
156
+ }
157
+ const authMatched = !detectLikelyAuthMismatch(params.pageSignals ?? null, params.profile);
158
+ if (!authMatched) {
159
+ reasons.push("authenticated session looks degraded on the current page");
160
+ }
161
+ const variantMatched = !detectLikelyVariantMismatch({
162
+ signals: params.pageSignals ?? null,
163
+ requestedLang: params.requestedLang,
164
+ requestedTheme: params.requestedTheme,
165
+ });
166
+ if (!variantMatched && contract.variantPolicy === "must_match") {
167
+ reasons.push("current page hints do not match the requested language/theme variant");
168
+ }
169
+ const strongSurfaceObserved = !!params.observation.strongSurfaceSignature
170
+ && (!!params.observation.primarySurface
171
+ || !!params.observation.overlaySurface
172
+ || !!params.observation.configurationSurface);
173
+ if (!strongSurfaceObserved) {
174
+ reasons.push("current page surface is too weakly identified for a strong checkpoint");
175
+ }
176
+ const surfaceContract = evaluateSurfaceContract({
177
+ contract,
178
+ currentUrl: params.currentUrl,
179
+ observation: params.observation,
180
+ pageSignals: params.pageSignals ?? null,
181
+ pageIdentity: params.pageIdentity ?? null,
182
+ });
183
+ if (!surfaceContract.matched) {
184
+ reasons.push(surfaceContract.reason ?? "current page surface does not match the expected capture contract");
185
+ }
186
+ const evidence = {
187
+ urlMatched,
188
+ dialogMatched,
189
+ authMatched,
190
+ variantMatched: contract.variantPolicy === "ignore" ? true : variantMatched,
191
+ strongSurfaceObserved,
192
+ surfaceMatched: surfaceContract.matched,
193
+ strongSurfaceSignature: params.observation.strongSurfaceSignature ?? params.observation.surfaceSignature ?? null,
194
+ primarySurface: params.observation.primarySurface ?? null,
195
+ overlaySurface: params.observation.overlaySurface ?? null,
196
+ navigationSurface: params.observation.navigationSurface ?? null,
197
+ configurationSurface: params.observation.configurationSurface ?? null,
198
+ reasons: [...reasons],
199
+ };
200
+ const mode = evidence.urlMatched
201
+ && evidence.dialogMatched
202
+ && evidence.authMatched
203
+ && evidence.variantMatched
204
+ && (evidence.surfaceMatched ?? true)
205
+ && evidence.strongSurfaceObserved
206
+ ? "verified"
207
+ : !evidence.authMatched
208
+ ? "dirty"
209
+ : "degraded";
210
+ return {
211
+ mode,
212
+ contract,
213
+ evidence,
214
+ reasons,
215
+ };
216
+ }
217
+ export function decideCaptureTransition(params) {
218
+ const contract = deriveCapturePageContract({ pageIdentity: params.pageIdentity });
219
+ if (!params.observation) {
220
+ return {
221
+ mode: "degraded",
222
+ contract,
223
+ evidence: {
224
+ urlMatched: false,
225
+ dialogMatched: contract.dialogPolicy === "allow_either",
226
+ authMatched: true,
227
+ variantMatched: true,
228
+ strongSurfaceObserved: false,
229
+ surfaceMatched: false,
230
+ strongSurfaceSignature: null,
231
+ primarySurface: null,
232
+ overlaySurface: null,
233
+ navigationSurface: null,
234
+ configurationSurface: null,
235
+ reasons: ["baseline observation unavailable"],
236
+ },
237
+ reasons: ["baseline observation unavailable"],
238
+ shouldNavigateToCanonical: true,
239
+ shouldRecreateContext: false,
240
+ allowFastPath: false,
241
+ };
242
+ }
243
+ const checkpointDecision = buildCaptureCheckpointEvidence({
244
+ currentUrl: params.currentUrl ?? params.observation.url,
245
+ targetUrl: params.resumeUrl ?? params.targetUrl,
246
+ pageIdentity: params.pageIdentity,
247
+ observation: params.observation,
248
+ pageSignals: params.pageSignals ?? null,
249
+ profile: params.profile,
250
+ requestedLang: params.requestedLang,
251
+ requestedTheme: params.requestedTheme,
252
+ });
253
+ const reasons = [...checkpointDecision.reasons];
254
+ const handoffTrust = params.handoffContext?.trust ?? "degraded";
255
+ const sameSessionOrigin = sameOrigin(params.currentUrl ?? params.observation.url, params.targetUrl)
256
+ || sameOrigin(params.currentUrl ?? params.observation.url, params.resumeUrl ?? null);
257
+ const derivedFromRepair = params.handoffContext?.derivedFromRepair === true;
258
+ let mode = checkpointDecision.mode;
259
+ if (params.pageIndex === 0) {
260
+ mode = checkpointDecision.mode === "dirty" ? "dirty" : "degraded";
261
+ reasons.push("first page in variant must rebase canonically before capture");
262
+ }
263
+ else if (derivedFromRepair) {
264
+ mode = checkpointDecision.mode === "dirty" ? "dirty" : "degraded";
265
+ reasons.push("previous page state came from repair/recovery and cannot be reused as trusted handoff");
266
+ }
267
+ else if (handoffTrust !== "verified") {
268
+ mode = checkpointDecision.mode === "dirty" ? "dirty" : "degraded";
269
+ reasons.push("handoff context is not strongly trusted");
270
+ }
271
+ else if (!sameSessionOrigin) {
272
+ mode = "dirty";
273
+ reasons.push("current live state belongs to a different origin");
274
+ }
275
+ else if (checkpointDecision.mode !== "verified") {
276
+ mode = checkpointDecision.mode;
277
+ }
278
+ else if (contract.reusePolicy === "never") {
279
+ mode = "degraded";
280
+ reasons.push("current page contract forbids opportunistic live-state reuse");
281
+ }
282
+ return {
283
+ mode,
284
+ contract,
285
+ evidence: checkpointDecision.evidence,
286
+ reasons,
287
+ shouldNavigateToCanonical: mode === "degraded",
288
+ shouldRecreateContext: mode === "dirty",
289
+ allowFastPath: mode === "verified",
290
+ };
291
+ }
292
+ //# sourceMappingURL=capture-transition-engine.js.map
@@ -3,6 +3,7 @@ import type { ScreenshotPageRunInput } from "./capture-run-optimizer.js";
3
3
  export interface VariantCaptureState {
4
4
  expectedPageIds: string[];
5
5
  pageIdentities: Record<string, CapturePageIdentity>;
6
+ pagePromptFingerprints: Record<string, string>;
6
7
  validatedCaptures: VariantValidatedCapture[];
7
8
  captureStatuses: Record<string, VariantCaptureStatus>;
8
9
  lastCheckpointId: string | null;
@@ -10,6 +11,7 @@ export interface VariantCaptureState {
10
11
  recoveryAttempts: Record<string, number>;
11
12
  repairHistory: VariantCaptureRepairRecord[];
12
13
  }
14
+ export declare function buildPageRunPromptFingerprint(pageRun: Pick<ScreenshotPageRunInput, "prompt" | "url">, pageIdentity?: CapturePageIdentity | null): string;
13
15
  export declare function createVariantCaptureState(pageRuns: ScreenshotPageRunInput[], precomputedIdentities?: Record<string, CapturePageIdentity>): VariantCaptureState;
14
16
  export declare function buildVariantManifestContext(params: {
15
17
  state: VariantCaptureState;
@@ -1,15 +1,33 @@
1
+ import { createHash } from "crypto";
1
2
  import { inferCapturePageIdentity } from "./capture-page-identity.js";
2
3
  function normalizePageId(pageId) {
3
4
  return pageId ?? "main";
4
5
  }
6
+ export function buildPageRunPromptFingerprint(pageRun, pageIdentity) {
7
+ return createHash("sha1")
8
+ .update(pageRun.prompt ?? "")
9
+ .update("\n")
10
+ .update(pageRun.url ?? "")
11
+ .update("\n")
12
+ .update(pageIdentity?.summary ?? "")
13
+ .digest("hex");
14
+ }
5
15
  export function createVariantCaptureState(pageRuns, precomputedIdentities) {
6
16
  const pageIdentities = precomputedIdentities ?? Object.fromEntries(pageRuns.map((pageRun) => [
7
17
  normalizePageId(pageRun.pageId),
8
18
  inferCapturePageIdentity(pageRun),
9
19
  ]));
20
+ const pagePromptFingerprints = Object.fromEntries(pageRuns.map((pageRun) => {
21
+ const pageId = normalizePageId(pageRun.pageId);
22
+ return [
23
+ pageId,
24
+ buildPageRunPromptFingerprint(pageRun, pageIdentities[pageId] ?? null),
25
+ ];
26
+ }));
10
27
  return {
11
28
  expectedPageIds: pageRuns.map((pageRun) => normalizePageId(pageRun.pageId)),
12
29
  pageIdentities,
30
+ pagePromptFingerprints,
13
31
  validatedCaptures: [],
14
32
  captureStatuses: Object.fromEntries(pageRuns.map((pageRun) => [normalizePageId(pageRun.pageId), "pending"])),
15
33
  lastCheckpointId: null,
@@ -25,6 +43,8 @@ export function buildVariantManifestContext(params) {
25
43
  expectedPageIds: [...params.state.expectedPageIds],
26
44
  currentPageId,
27
45
  currentPageIdentity: params.state.pageIdentities[currentPageId] ?? null,
46
+ promptFingerprint: params.state.pagePromptFingerprints[currentPageId] ?? null,
47
+ pagePromptFingerprints: { ...params.state.pagePromptFingerprints },
28
48
  completedPages,
29
49
  remainingPages: params.state.expectedPageIds.filter((pageId) => !completedPages.includes(pageId)),
30
50
  previousValidatedCaptures: [...params.state.validatedCaptures],
@@ -53,6 +73,7 @@ export function recordValidatedVariantCapture(params) {
53
73
  state: {
54
74
  expectedPageIds: [...params.state.expectedPageIds],
55
75
  pageIdentities: { ...params.state.pageIdentities },
76
+ pagePromptFingerprints: { ...params.state.pagePromptFingerprints },
56
77
  validatedCaptures: nextValidated,
57
78
  captureStatuses: {
58
79
  ...params.state.captureStatuses,
@@ -106,6 +127,7 @@ export function markVariantCaptureInProgress(state, pageId) {
106
127
  const normalized = normalizePageId(pageId);
107
128
  return {
108
129
  ...state,
130
+ pagePromptFingerprints: { ...state.pagePromptFingerprints },
109
131
  captureStatuses: {
110
132
  ...state.captureStatuses,
111
133
  [normalized]: "in_progress",
@@ -120,6 +142,7 @@ export function markVariantCaptureBlocked(params) {
120
142
  const normalized = normalizePageId(params.pageId);
121
143
  return {
122
144
  ...params.state,
145
+ pagePromptFingerprints: { ...params.state.pagePromptFingerprints },
123
146
  captureStatuses: {
124
147
  ...params.state.captureStatuses,
125
148
  [normalized]: "blocked",
@@ -134,6 +157,7 @@ export function recordVariantRecoveryAttempt(state, pageId) {
134
157
  const normalized = normalizePageId(pageId);
135
158
  return {
136
159
  ...state,
160
+ pagePromptFingerprints: { ...state.pagePromptFingerprints },
137
161
  recoveryAttempts: {
138
162
  ...state.recoveryAttempts,
139
163
  [normalized]: (state.recoveryAttempts[normalized] ?? 0) + 1,
@@ -144,12 +168,14 @@ export function recordVariantRepair(params) {
144
168
  const nextHistory = [...params.state.repairHistory, params.repair];
145
169
  return {
146
170
  ...params.state,
171
+ pagePromptFingerprints: { ...params.state.pagePromptFingerprints },
147
172
  repairHistory: nextHistory.slice(-12),
148
173
  };
149
174
  }
150
175
  export function recordVariantCheckpoint(params) {
151
176
  return {
152
177
  ...params.state,
178
+ pagePromptFingerprints: { ...params.state.pagePromptFingerprints },
153
179
  lastCheckpointId: params.checkpoint.id,
154
180
  };
155
181
  }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Capture Agent — Capture Verification
3
+ *
4
+ * Post-capture quality check via LLM vision.
5
+ * Detects: blank pages, error pages, loading spinners, skeletons,
6
+ * obstructing overlays, and other visual issues.
7
+ *
8
+ * This is the ONLY place where AutoKap uses AI at runtime.
9
+ * Cost: ~0.002 EUR per check (1 image + short prompt).
10
+ */
11
+ import { type LLMProviderConfig, type LLMCallResult } from './llm-provider.js';
12
+ export interface VerificationResult {
13
+ /** Whether the screenshot is acceptable for delivery */
14
+ passed: boolean;
15
+ /** Issue detected (if failed) */
16
+ issue?: 'blank_page' | 'error_page' | 'loading' | 'overlay_blocking' | 'wrong_content' | 'other';
17
+ /** Human-readable explanation */
18
+ reason: string;
19
+ /** LLM call metadata */
20
+ llmResult?: LLMCallResult;
21
+ }
22
+ export interface VerificationContext {
23
+ /** What was this screenshot supposed to show? */
24
+ expectedDescription: string;
25
+ /** Target URL */
26
+ url: string;
27
+ /** Locale of this variant */
28
+ locale?: string;
29
+ /** Theme of this variant */
30
+ theme?: string;
31
+ }
32
+ /**
33
+ * Verifies a captured screenshot is clean and ready for delivery.
34
+ */
35
+ export declare function verifyCaptureQuality(screenshot: Buffer, context: VerificationContext, llmConfig: LLMProviderConfig): Promise<VerificationResult>;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Capture Agent — Capture Verification
3
+ *
4
+ * Post-capture quality check via LLM vision.
5
+ * Detects: blank pages, error pages, loading spinners, skeletons,
6
+ * obstructing overlays, and other visual issues.
7
+ *
8
+ * This is the ONLY place where AutoKap uses AI at runtime.
9
+ * Cost: ~0.002 EUR per check (1 image + short prompt).
10
+ */
11
+ import { callLLM } from './llm-provider.js';
12
+ const SYSTEM_PROMPT = `You are a screenshot quality checker for a web capture tool. You receive a screenshot of a web page and must verify it is clean and ready for delivery.
13
+
14
+ Check for these issues:
15
+ 1. BLANK - The page is completely white/blank or has no meaningful content
16
+ 2. ERROR - The page shows an error message (404, 500, "something went wrong", crash screen)
17
+ 3. LOADING - The page has visible loading indicators (spinners, skeletons, progress bars, "Loading..." text)
18
+ 4. OVERLAY - A modal, popup, cookie banner, or other overlay is blocking the main content
19
+ 5. WRONG - The content doesn't match what was expected
20
+
21
+ Respond with EXACTLY one line in this format:
22
+ PASS: Screenshot looks clean and ready.
23
+ or
24
+ FAIL:<issue>: <explanation>
25
+
26
+ Where <issue> is one of: blank_page, error_page, loading, overlay_blocking, wrong_content, other
27
+
28
+ Examples:
29
+ PASS: Screenshot looks clean and ready.
30
+ FAIL:loading: A skeleton loader is visible in the main content area.
31
+ FAIL:overlay_blocking: A cookie consent banner covers the bottom of the page.
32
+ FAIL:error_page: The page shows a 404 Not Found error.`;
33
+ /**
34
+ * Verifies a captured screenshot is clean and ready for delivery.
35
+ */
36
+ export async function verifyCaptureQuality(screenshot, context, llmConfig) {
37
+ const userPrompt = buildUserPrompt(context);
38
+ try {
39
+ const result = await callLLM(llmConfig, SYSTEM_PROMPT, userPrompt, screenshot);
40
+ return parseVerificationResponse(result.text, result);
41
+ }
42
+ catch (err) {
43
+ // If LLM fails, pass the screenshot — don't block delivery because of LLM issues
44
+ return {
45
+ passed: true,
46
+ reason: `verification skipped (LLM error: ${err instanceof Error ? err.message : String(err)})`,
47
+ };
48
+ }
49
+ }
50
+ function buildUserPrompt(context) {
51
+ const parts = [
52
+ `Expected content: ${context.expectedDescription}`,
53
+ `URL: ${context.url}`,
54
+ ];
55
+ if (context.locale)
56
+ parts.push(`Locale: ${context.locale}`);
57
+ if (context.theme)
58
+ parts.push(`Theme: ${context.theme}`);
59
+ parts.push('\nIs this screenshot clean and ready for delivery?');
60
+ return parts.join('\n');
61
+ }
62
+ function parseVerificationResponse(text, llmResult) {
63
+ const trimmed = text.trim();
64
+ if (trimmed.startsWith('PASS')) {
65
+ return {
66
+ passed: true,
67
+ reason: trimmed.replace(/^PASS:?\s*/, ''),
68
+ llmResult,
69
+ };
70
+ }
71
+ if (trimmed.startsWith('FAIL')) {
72
+ const match = trimmed.match(/^FAIL:(\w+):\s*(.+)/);
73
+ if (match) {
74
+ return {
75
+ passed: false,
76
+ issue: match[1],
77
+ reason: match[2],
78
+ llmResult,
79
+ };
80
+ }
81
+ return {
82
+ passed: false,
83
+ issue: 'other',
84
+ reason: trimmed.replace(/^FAIL:?\s*/, ''),
85
+ llmResult,
86
+ };
87
+ }
88
+ // Ambiguous response — assume pass
89
+ return {
90
+ passed: true,
91
+ reason: `ambiguous LLM response: "${trimmed.slice(0, 100)}"`,
92
+ llmResult,
93
+ };
94
+ }
95
+ //# sourceMappingURL=capture-verification.js.map
@@ -0,0 +1,48 @@
1
+ export interface CaptureViewport {
2
+ width: number;
3
+ height: number;
4
+ }
5
+ export interface ViewportLockBrowser {
6
+ resizeViewport(width: number, height: number): Promise<void>;
7
+ wait(ms: number): Promise<void>;
8
+ takeScreenshot(): Promise<Buffer>;
9
+ currentPage: {
10
+ viewportSize(): {
11
+ width: number;
12
+ height: number;
13
+ } | null;
14
+ };
15
+ }
16
+ export interface LockedViewportScreenshot {
17
+ buffer: Buffer;
18
+ actualViewport: {
19
+ width: number;
20
+ height: number;
21
+ } | null;
22
+ screenshotSize: {
23
+ width: number;
24
+ height: number;
25
+ };
26
+ expectedScreenshotSize: {
27
+ width: number;
28
+ height: number;
29
+ };
30
+ repaired: boolean;
31
+ }
32
+ interface ViewportLockOptions {
33
+ settleMs?: number;
34
+ maxAttempts?: number;
35
+ }
36
+ interface TakeViewportLockedScreenshotOptions extends ViewportLockOptions {
37
+ deviceScaleFactor: number;
38
+ maxScreenshotAttempts?: number;
39
+ }
40
+ export declare function lockCaptureViewport(browser: ViewportLockBrowser, viewport: CaptureViewport, options?: ViewportLockOptions): Promise<{
41
+ actualViewport: {
42
+ width: number;
43
+ height: number;
44
+ } | null;
45
+ repaired: boolean;
46
+ }>;
47
+ export declare function takeViewportLockedScreenshot(browser: ViewportLockBrowser, viewport: CaptureViewport, options: TakeViewportLockedScreenshotOptions): Promise<LockedViewportScreenshot>;
48
+ export {};
@@ -0,0 +1,74 @@
1
+ import sharp from 'sharp';
2
+ const DEFAULT_SETTLE_MS = 300;
3
+ const DEFAULT_MAX_VIEWPORT_ATTEMPTS = 2;
4
+ const DEFAULT_MAX_SCREENSHOT_ATTEMPTS = 2;
5
+ function matchesDimension(actual, expected) {
6
+ return Math.abs(actual - expected) <= 1;
7
+ }
8
+ function matchesViewport(actual, expected) {
9
+ return !!actual
10
+ && matchesDimension(actual.width, expected.width)
11
+ && matchesDimension(actual.height, expected.height);
12
+ }
13
+ function formatViewport(value) {
14
+ if (!value)
15
+ return 'unknown';
16
+ return `${value.width}x${value.height}`;
17
+ }
18
+ export async function lockCaptureViewport(browser, viewport, options = {}) {
19
+ const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
20
+ const maxAttempts = options.maxAttempts ?? DEFAULT_MAX_VIEWPORT_ATTEMPTS;
21
+ let repaired = false;
22
+ let actualViewport = browser.currentPage.viewportSize();
23
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
24
+ await browser.resizeViewport(viewport.width, viewport.height);
25
+ if (settleMs > 0) {
26
+ await browser.wait(settleMs);
27
+ }
28
+ actualViewport = browser.currentPage.viewportSize();
29
+ if (matchesViewport(actualViewport, viewport)) {
30
+ return {
31
+ actualViewport,
32
+ repaired,
33
+ };
34
+ }
35
+ repaired = true;
36
+ }
37
+ throw new Error(`Viewport lock failed: expected ${viewport.width}x${viewport.height}, got ${formatViewport(actualViewport)}.`);
38
+ }
39
+ export async function takeViewportLockedScreenshot(browser, viewport, options) {
40
+ const deviceScaleFactor = Math.max(0.5, Number(options.deviceScaleFactor) || 1);
41
+ const maxScreenshotAttempts = options.maxScreenshotAttempts ?? DEFAULT_MAX_SCREENSHOT_ATTEMPTS;
42
+ const expectedScreenshotSize = {
43
+ width: Math.round(viewport.width * deviceScaleFactor),
44
+ height: Math.round(viewport.height * deviceScaleFactor),
45
+ };
46
+ let repaired = false;
47
+ let lastViewport = null;
48
+ let lastScreenshotSize = null;
49
+ for (let attempt = 1; attempt <= maxScreenshotAttempts; attempt += 1) {
50
+ const viewportLock = await lockCaptureViewport(browser, viewport, options);
51
+ repaired ||= viewportLock.repaired;
52
+ lastViewport = viewportLock.actualViewport;
53
+ const buffer = await browser.takeScreenshot();
54
+ const metadata = await sharp(buffer).metadata();
55
+ const screenshotSize = {
56
+ width: metadata.width ?? 0,
57
+ height: metadata.height ?? 0,
58
+ };
59
+ lastScreenshotSize = screenshotSize;
60
+ if (matchesDimension(screenshotSize.width, expectedScreenshotSize.width)
61
+ && matchesDimension(screenshotSize.height, expectedScreenshotSize.height)) {
62
+ return {
63
+ buffer,
64
+ actualViewport: lastViewport,
65
+ screenshotSize,
66
+ expectedScreenshotSize,
67
+ repaired: repaired || attempt > 1,
68
+ };
69
+ }
70
+ repaired = true;
71
+ }
72
+ throw new Error(`Screenshot viewport mismatch: expected ${expectedScreenshotSize.width}x${expectedScreenshotSize.height}, got ${formatViewport(lastScreenshotSize)} (viewport ${formatViewport(lastViewport)}).`);
73
+ }
74
+ //# sourceMappingURL=capture-viewport-lock.js.map