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.
- package/assets/cursors/macos.svg +4 -0
- package/assets/cursors/windows.svg +15 -0
- package/assets/skill/OPCODE-REFERENCE.md +607 -0
- package/assets/skill/README.md +39 -0
- package/assets/skill/SKILL.md +453 -468
- package/assets/skill/STUDIO-SKILL.md +476 -0
- package/assets/skill/references/examples.md +104 -0
- package/assets/skill/references/interactive-demo.md +225 -0
- package/assets/skill/references/mock-data.md +178 -0
- package/dist/action-verifier.d.ts +29 -0
- package/dist/action-verifier.js +133 -0
- package/dist/agent-action-recovery.d.ts +45 -0
- package/dist/agent-action-recovery.js +370 -0
- package/dist/agent-message-utils.d.ts +21 -0
- package/dist/agent-message-utils.js +77 -0
- package/dist/agent-url-utils.d.ts +30 -0
- package/dist/agent-url-utils.js +138 -0
- package/dist/agent.d.ts +92 -8
- package/dist/agent.js +2936 -781
- package/dist/ak-tree.d.ts +39 -0
- package/dist/ak-tree.js +368 -0
- package/dist/alt-text.d.ts +26 -0
- package/dist/alt-text.js +55 -0
- package/dist/auth-capture.d.ts +17 -0
- package/dist/auth-capture.js +164 -0
- package/dist/benchmark.d.ts +59 -0
- package/dist/benchmark.js +135 -0
- package/dist/browser-bar.d.ts +14 -6
- package/dist/browser-bar.js +145 -8
- package/dist/browser-pool.d.ts +7 -0
- package/dist/browser-pool.js +15 -5
- package/dist/browser-utils.d.ts +31 -0
- package/dist/browser-utils.js +97 -0
- package/dist/browser.d.ts +51 -1
- package/dist/browser.js +1481 -31
- package/dist/capture-alt-text.js +2 -1
- package/dist/capture-language-preflight.js +14 -0
- package/dist/capture-llm-page-identity.js +22 -10
- package/dist/capture-page-identity.d.ts +5 -7
- package/dist/capture-page-identity.js +211 -78
- package/dist/capture-preset-credentials.d.ts +50 -0
- package/dist/capture-preset-credentials.js +127 -0
- package/dist/capture-request-plan.d.ts +2 -2
- package/dist/capture-request-plan.js +64 -16
- package/dist/capture-run-optimizer.js +48 -33
- package/dist/capture-selector-memory.d.ts +5 -0
- package/dist/capture-selector-memory.js +18 -0
- package/dist/capture-strategy.d.ts +36 -0
- package/dist/capture-strategy.js +95 -0
- package/dist/capture-studio-sync.d.ts +1 -0
- package/dist/capture-studio-sync.js +9 -3
- package/dist/capture-surface-contract.d.ts +36 -0
- package/dist/capture-surface-contract.js +299 -0
- package/dist/capture-transition-engine.d.ts +28 -0
- package/dist/capture-transition-engine.js +292 -0
- package/dist/capture-variant-state.d.ts +2 -0
- package/dist/capture-variant-state.js +26 -0
- package/dist/capture-verification.d.ts +35 -0
- package/dist/capture-verification.js +95 -0
- package/dist/capture-viewport-lock.d.ts +48 -0
- package/dist/capture-viewport-lock.js +74 -0
- package/dist/circuit-breaker.d.ts +42 -0
- package/dist/circuit-breaker.js +119 -0
- package/dist/cli-config.d.ts +8 -1
- package/dist/cli-config.js +62 -6
- package/dist/cli-contract.d.ts +15 -0
- package/dist/cli-contract.js +167 -0
- package/dist/cli-runner-local.d.ts +12 -0
- package/dist/cli-runner-local.js +102 -0
- package/dist/cli-runner.d.ts +34 -0
- package/dist/cli-runner.js +433 -0
- package/dist/cli-utils.d.ts +0 -1
- package/dist/cli-utils.js +2 -5
- package/dist/cli.js +1005 -267
- package/dist/clip-orchestrator.js +9 -2
- package/dist/clip-postprocess.js +25 -16
- package/dist/cookie-dismiss.d.ts +2 -0
- package/dist/cookie-dismiss.js +48 -13
- package/dist/cost-logging.d.ts +8 -0
- package/dist/cost-logging.js +160 -46
- package/dist/cost-resolution-monitor.d.ts +16 -0
- package/dist/cost-resolution-monitor.js +34 -0
- package/dist/credential-templates.js +2 -2
- package/dist/cursor-overlay-script.d.ts +6 -0
- package/dist/cursor-overlay-script.js +169 -0
- package/dist/dom-css-purger.d.ts +65 -0
- package/dist/dom-css-purger.js +333 -0
- package/dist/dom-font-inliner.d.ts +45 -0
- package/dist/dom-font-inliner.js +148 -0
- package/dist/dom-patch-resolver.d.ts +52 -0
- package/dist/dom-patch-resolver.js +242 -0
- package/dist/dom-serializer.d.ts +82 -0
- package/dist/dom-serializer.js +378 -0
- package/dist/element-capture.d.ts +1 -41
- package/dist/element-capture.js +202 -446
- package/dist/env-validation.d.ts +5 -0
- package/dist/env-validation.js +29 -0
- package/dist/execution-schema.d.ts +4423 -0
- package/dist/execution-schema.js +507 -0
- package/dist/execution-types.d.ts +886 -0
- package/dist/execution-types.js +65 -0
- package/dist/fonts-loader.d.ts +14 -0
- package/dist/fonts-loader.js +55 -0
- package/dist/hybrid-navigator.js +12 -12
- package/dist/index.d.ts +9 -6
- package/dist/index.js +10 -4
- package/dist/legacy/agent-action-recovery.d.ts +45 -0
- package/dist/legacy/agent-action-recovery.js +370 -0
- package/dist/legacy/agent-message-utils.d.ts +21 -0
- package/dist/legacy/agent-message-utils.js +77 -0
- package/dist/legacy/agent-url-utils.d.ts +30 -0
- package/dist/legacy/agent-url-utils.js +138 -0
- package/dist/legacy/agent.d.ts +226 -0
- package/dist/legacy/agent.js +6666 -0
- package/dist/legacy/clip-orchestrator.d.ts +148 -0
- package/dist/legacy/clip-orchestrator.js +957 -0
- package/dist/legacy/credential-templates.d.ts +5 -0
- package/dist/legacy/credential-templates.js +60 -0
- package/dist/legacy/hybrid-navigator.d.ts +138 -0
- package/dist/legacy/hybrid-navigator.js +468 -0
- package/dist/legacy/llm-usage.d.ts +17 -0
- package/dist/legacy/llm-usage.js +45 -0
- package/dist/legacy/prompt-cache.d.ts +10 -0
- package/dist/legacy/prompt-cache.js +24 -0
- package/dist/legacy/prompts.d.ts +175 -0
- package/dist/legacy/prompts.js +1038 -0
- package/dist/legacy/tools.d.ts +4 -0
- package/dist/legacy/tools.js +216 -0
- package/dist/legacy/video-agent.d.ts +143 -0
- package/dist/legacy/video-agent.js +4788 -0
- package/dist/legacy/video-observation.d.ts +36 -0
- package/dist/legacy/video-observation.js +192 -0
- package/dist/legacy/video-planner.d.ts +12 -0
- package/dist/legacy/video-planner.js +501 -0
- package/dist/legacy/video-prompts.d.ts +37 -0
- package/dist/legacy/video-prompts.js +569 -0
- package/dist/legacy/video-tools.d.ts +3 -0
- package/dist/legacy/video-tools.js +59 -0
- package/dist/legacy/video-variant-state.d.ts +29 -0
- package/dist/legacy/video-variant-state.js +80 -0
- package/dist/legacy/vision-model.d.ts +17 -0
- package/dist/legacy/vision-model.js +74 -0
- package/dist/llm-healer.d.ts +63 -0
- package/dist/llm-healer.js +166 -0
- package/dist/llm-provider.d.ts +29 -0
- package/dist/llm-provider.js +80 -0
- package/dist/logger.d.ts +6 -2
- package/dist/logger.js +15 -1
- package/dist/mockup-html.js +35 -25
- package/dist/mockup.d.ts +95 -2
- package/dist/mockup.js +427 -166
- package/dist/mouse-animation.d.ts +2 -2
- package/dist/mouse-animation.js +34 -20
- package/dist/opcode-actions.d.ts +42 -0
- package/dist/opcode-actions.js +511 -0
- package/dist/opcode-runner.d.ts +51 -0
- package/dist/opcode-runner.js +770 -0
- package/dist/openrouter-client.d.ts +40 -0
- package/dist/openrouter-client.js +16 -0
- package/dist/overlay-engine.d.ts +24 -0
- package/dist/overlay-engine.js +176 -0
- package/dist/postcondition.d.ts +16 -0
- package/dist/postcondition.js +269 -0
- package/dist/program-patcher.d.ts +25 -0
- package/dist/program-patcher.js +44 -0
- package/dist/prompts.d.ts +13 -5
- package/dist/prompts.js +224 -351
- package/dist/provider-config.d.ts +12 -0
- package/dist/provider-config.js +15 -0
- package/dist/recovery-chain.d.ts +37 -0
- package/dist/recovery-chain.js +350 -0
- package/dist/remote-browser.d.ts +28 -4
- package/dist/remote-browser.js +60 -5
- package/dist/safari-browser-bar.d.ts +15 -0
- package/dist/safari-browser-bar.js +95 -0
- package/dist/safari-toolbar-asset.d.ts +15 -0
- package/dist/safari-toolbar-asset.js +12 -0
- package/dist/security.d.ts +2 -1
- package/dist/security.js +49 -10
- package/dist/selector-resolver.d.ts +34 -0
- package/dist/selector-resolver.js +181 -0
- package/dist/semantic-resolver.d.ts +35 -0
- package/dist/semantic-resolver.js +161 -0
- package/dist/server-capture-runtime.d.ts +5 -3
- package/dist/server-capture-runtime.js +42 -95
- package/dist/server-credit-usage.d.ts +2 -2
- package/dist/server-project-webhooks.d.ts +15 -1
- package/dist/server-project-webhooks.js +34 -8
- package/dist/server-screenshot-watermark.js +27 -5
- package/dist/session-profile.js +164 -1
- package/dist/sf-pro-symbols.d.ts +1 -0
- package/dist/sf-pro-symbols.js +55 -0
- package/dist/skill-packaging.d.ts +28 -0
- package/dist/skill-packaging.js +169 -0
- package/dist/smart-wait.d.ts +27 -0
- package/dist/smart-wait.js +81 -0
- package/dist/status-bar-render.d.ts +20 -0
- package/dist/status-bar-render.js +410 -0
- package/dist/status-bar.d.ts +9 -0
- package/dist/status-bar.js +298 -14
- package/dist/svg-browser-bar.d.ts +33 -0
- package/dist/svg-browser-bar.js +206 -0
- package/dist/svg-status-bar.d.ts +36 -0
- package/dist/svg-status-bar.js +597 -0
- package/dist/svg-text.d.ts +61 -0
- package/dist/svg-text.js +118 -0
- package/dist/tools.js +89 -451
- package/dist/types.d.ts +240 -5
- package/dist/types.js +23 -1
- package/dist/v2/action-verifier.d.ts +29 -0
- package/dist/v2/action-verifier.js +133 -0
- package/dist/v2/alt-text.d.ts +26 -0
- package/dist/v2/alt-text.js +55 -0
- package/dist/v2/benchmark.d.ts +59 -0
- package/dist/v2/benchmark.js +135 -0
- package/dist/v2/capture-strategy.d.ts +30 -0
- package/dist/v2/capture-strategy.js +67 -0
- package/dist/v2/capture-verification.d.ts +35 -0
- package/dist/v2/capture-verification.js +95 -0
- package/dist/v2/circuit-breaker.d.ts +42 -0
- package/dist/v2/circuit-breaker.js +119 -0
- package/dist/v2/cli-runner-local.d.ts +11 -0
- package/dist/v2/cli-runner-local.js +91 -0
- package/dist/v2/cli-runner.d.ts +34 -0
- package/dist/v2/cli-runner.js +300 -0
- package/dist/v2/compiler-prompts.d.ts +27 -0
- package/dist/v2/compiler-prompts.js +123 -0
- package/dist/v2/compiler.d.ts +37 -0
- package/dist/v2/compiler.js +147 -0
- package/dist/v2/explorer.d.ts +41 -0
- package/dist/v2/explorer.js +56 -0
- package/dist/v2/index.d.ts +37 -0
- package/dist/v2/index.js +31 -0
- package/dist/v2/llm-healer.d.ts +62 -0
- package/dist/v2/llm-healer.js +166 -0
- package/dist/v2/llm-provider.d.ts +29 -0
- package/dist/v2/llm-provider.js +80 -0
- package/dist/v2/opcode-runner.d.ts +47 -0
- package/dist/v2/opcode-runner.js +634 -0
- package/dist/v2/overlay-engine.d.ts +24 -0
- package/dist/v2/overlay-engine.js +150 -0
- package/dist/v2/postcondition.d.ts +16 -0
- package/dist/v2/postcondition.js +249 -0
- package/dist/v2/program-patcher.d.ts +25 -0
- package/dist/v2/program-patcher.js +44 -0
- package/dist/v2/recovery-chain.d.ts +30 -0
- package/dist/v2/recovery-chain.js +368 -0
- package/dist/v2/schema.d.ts +2580 -0
- package/dist/v2/schema.js +295 -0
- package/dist/v2/selector-resolver.d.ts +34 -0
- package/dist/v2/selector-resolver.js +181 -0
- package/dist/v2/semantic-resolver.d.ts +35 -0
- package/dist/v2/semantic-resolver.js +161 -0
- package/dist/v2/smart-wait.d.ts +27 -0
- package/dist/v2/smart-wait.js +81 -0
- package/dist/v2/types.d.ts +444 -0
- package/dist/v2/types.js +19 -0
- package/dist/v2/web-playwright-local.d.ts +69 -0
- package/dist/v2/web-playwright-local.js +392 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +5 -0
- package/dist/video-agent.js +18 -13
- package/dist/video-planner.js +2 -1
- package/dist/video-prompts.js +3 -3
- package/dist/web-playwright-local.d.ts +126 -0
- package/dist/web-playwright-local.js +819 -0
- package/dist/ws-auth.js +4 -1
- package/dist/ws-broadcast.d.ts +34 -0
- package/dist/ws-broadcast.js +85 -0
- package/dist/ws-connection-limits.d.ts +12 -0
- package/dist/ws-connection-limits.js +44 -0
- package/dist/ws-handler-utils.d.ts +32 -0
- package/dist/ws-handler-utils.js +139 -0
- package/dist/ws-handler.js +294 -164
- package/dist/ws-metrics-server.d.ts +9 -0
- package/dist/ws-metrics-server.js +31 -0
- package/dist/ws-server.js +41 -1
- package/package.json +51 -34
|
@@ -0,0 +1,150 @@
|
|
|
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
|
+
/**
|
|
11
|
+
* Multi-pass overlay dismissal.
|
|
12
|
+
* 1. Delegate to the adapter's built-in dismissOverlays (cookie-dismiss.ts)
|
|
13
|
+
* 2. Check AKTree for remaining blocking overlays
|
|
14
|
+
* 3. Try additional heuristics for newsletter/chat/age gate
|
|
15
|
+
* 4. Final AKTree check
|
|
16
|
+
*/
|
|
17
|
+
export async function dismissAllOverlays(adapter) {
|
|
18
|
+
const methods = [];
|
|
19
|
+
let totalFound = 0;
|
|
20
|
+
// Pass 1: Built-in cookie/widget dismissal
|
|
21
|
+
const cookieResult = await adapter.dismissOverlays();
|
|
22
|
+
if (cookieResult.dismissed) {
|
|
23
|
+
methods.push(cookieResult.method ?? 'cookie-dismiss');
|
|
24
|
+
}
|
|
25
|
+
// Check remaining overlays
|
|
26
|
+
let tree = await adapter.getAKTree();
|
|
27
|
+
const blocking = tree.overlays.filter(o => o.blocksInteraction);
|
|
28
|
+
totalFound = tree.overlays.length;
|
|
29
|
+
if (blocking.length === 0) {
|
|
30
|
+
return { dismissed: methods.length > 0, methods, overlaysFound: totalFound, overlaysRemaining: 0 };
|
|
31
|
+
}
|
|
32
|
+
// Pass 2: Try Escape key to dismiss modals/popups
|
|
33
|
+
try {
|
|
34
|
+
await adapter.pressKey('Escape');
|
|
35
|
+
await sleep(300);
|
|
36
|
+
tree = await adapter.getAKTree();
|
|
37
|
+
const remaining = tree.overlays.filter(o => o.blocksInteraction);
|
|
38
|
+
if (remaining.length < blocking.length) {
|
|
39
|
+
methods.push('escape-key');
|
|
40
|
+
}
|
|
41
|
+
if (remaining.length === 0) {
|
|
42
|
+
return { dismissed: true, methods, overlaysFound: totalFound, overlaysRemaining: 0 };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Non-fatal
|
|
47
|
+
}
|
|
48
|
+
// Pass 3: Try clicking common close patterns in AKTree
|
|
49
|
+
const closePatterns = [
|
|
50
|
+
'close', 'dismiss', 'fermer', 'schließen', 'cerrar', 'chiudi',
|
|
51
|
+
'no thanks', 'non merci', 'maybe later', 'not now', 'skip',
|
|
52
|
+
];
|
|
53
|
+
for (const overlay of tree.overlays) {
|
|
54
|
+
if (!overlay.blocksInteraction)
|
|
55
|
+
continue;
|
|
56
|
+
// Look for close buttons within the overlay's subtree
|
|
57
|
+
const closeNode = findCloseButton(tree, overlay.nodeId, closePatterns);
|
|
58
|
+
if (closeNode) {
|
|
59
|
+
try {
|
|
60
|
+
// Build selector from the close node's sourceRef
|
|
61
|
+
const selector = buildSelectorFromSourceRef(closeNode.sourceRef);
|
|
62
|
+
if (selector) {
|
|
63
|
+
await adapter.click(selector);
|
|
64
|
+
methods.push(`close-button:${selector}`);
|
|
65
|
+
await sleep(300);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Continue to next overlay
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Final check
|
|
74
|
+
tree = await adapter.getAKTree();
|
|
75
|
+
const finalBlocking = tree.overlays.filter(o => o.blocksInteraction);
|
|
76
|
+
return {
|
|
77
|
+
dismissed: methods.length > 0,
|
|
78
|
+
methods,
|
|
79
|
+
overlaysFound: totalFound,
|
|
80
|
+
overlaysRemaining: finalBlocking.length,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function findCloseButton(tree, overlayNodeId, patterns) {
|
|
84
|
+
// Find the overlay node first
|
|
85
|
+
const overlayNode = findNodeById(tree.root, overlayNodeId);
|
|
86
|
+
if (!overlayNode)
|
|
87
|
+
return null;
|
|
88
|
+
// Search for interactive close-like buttons within this subtree
|
|
89
|
+
let best = null;
|
|
90
|
+
function walk(node) {
|
|
91
|
+
if (node.visible && node.interactive) {
|
|
92
|
+
const label = node.label.toLowerCase();
|
|
93
|
+
const ref = node.sourceRef.toLowerCase();
|
|
94
|
+
const combined = label + ' ' + ref;
|
|
95
|
+
for (const pattern of patterns) {
|
|
96
|
+
if (combined.includes(pattern)) {
|
|
97
|
+
// Prefer buttons over links, and shorter labels over longer ones
|
|
98
|
+
if (!best || (node.type === 'button' && best.type !== 'button') || node.label.length < best.label.length) {
|
|
99
|
+
best = node;
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Also match aria-label="close" or title="close" in sourceRef
|
|
105
|
+
if (ref.includes('aria-label="close"') || ref.includes('title="close"') || ref.includes('class="close"')) {
|
|
106
|
+
if (!best)
|
|
107
|
+
best = node;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
node.children.forEach(walk);
|
|
111
|
+
}
|
|
112
|
+
walk(overlayNode);
|
|
113
|
+
return best;
|
|
114
|
+
}
|
|
115
|
+
function findNodeById(node, id) {
|
|
116
|
+
if (node.id === id)
|
|
117
|
+
return node;
|
|
118
|
+
for (const child of node.children) {
|
|
119
|
+
const found = findNodeById(child, id);
|
|
120
|
+
if (found)
|
|
121
|
+
return found;
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
function buildSelectorFromSourceRef(sourceRef) {
|
|
126
|
+
// Extract data-testid
|
|
127
|
+
const testIdMatch = sourceRef.match(/data-testid="([^"]+)"/);
|
|
128
|
+
if (testIdMatch)
|
|
129
|
+
return `[data-testid="${testIdMatch[1]}"]`;
|
|
130
|
+
// Extract id
|
|
131
|
+
const idMatch = sourceRef.match(/ id="([^"]+)"/);
|
|
132
|
+
if (idMatch)
|
|
133
|
+
return `#${idMatch[1]}`;
|
|
134
|
+
// Extract aria-label
|
|
135
|
+
const ariaMatch = sourceRef.match(/aria-label="([^"]+)"/);
|
|
136
|
+
if (ariaMatch)
|
|
137
|
+
return `[aria-label="${ariaMatch[1]}"]`;
|
|
138
|
+
// Extract class-based close button
|
|
139
|
+
const classMatch = sourceRef.match(/class="([^"]*close[^"]*)"/);
|
|
140
|
+
if (classMatch) {
|
|
141
|
+
const tagMatch = sourceRef.match(/^<(\w+)/);
|
|
142
|
+
const tag = tagMatch?.[1] ?? 'button';
|
|
143
|
+
return `${tag}.${classMatch[1].split(/\s+/).find(c => c.includes('close'))}`;
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
function sleep(ms) {
|
|
148
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=overlay-engine.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture Agent — Postcondition Evaluator
|
|
3
|
+
*
|
|
4
|
+
* Deterministic evaluation of postconditions after each opcode.
|
|
5
|
+
* No LLM calls — purely structural checks against AKTree, URL, and screenshots.
|
|
6
|
+
*/
|
|
7
|
+
import type { RuntimeAdapter, PostconditionSpec } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Evaluates whether a postcondition holds.
|
|
10
|
+
* Retries internally up to postcondition.waitMs (polling).
|
|
11
|
+
* Returns true if the condition is satisfied, false otherwise.
|
|
12
|
+
*/
|
|
13
|
+
export declare function evaluatePostcondition(adapter: RuntimeAdapter, spec: PostconditionSpec): Promise<{
|
|
14
|
+
passed: boolean;
|
|
15
|
+
reason: string;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture Agent — Postcondition Evaluator
|
|
3
|
+
*
|
|
4
|
+
* Deterministic evaluation of postconditions after each opcode.
|
|
5
|
+
* No LLM calls — purely structural checks against AKTree, URL, and screenshots.
|
|
6
|
+
*/
|
|
7
|
+
import { serializeAKTree } from '../ak-tree.js';
|
|
8
|
+
/**
|
|
9
|
+
* Evaluates whether a postcondition holds.
|
|
10
|
+
* Retries internally up to postcondition.waitMs (polling).
|
|
11
|
+
* Returns true if the condition is satisfied, false otherwise.
|
|
12
|
+
*/
|
|
13
|
+
export async function evaluatePostcondition(adapter, spec) {
|
|
14
|
+
const maxWait = spec.waitMs ?? 5000;
|
|
15
|
+
const pollInterval = 500;
|
|
16
|
+
const deadline = Date.now() + maxWait;
|
|
17
|
+
// 'always' postcondition always passes immediately
|
|
18
|
+
if (spec.type === 'always') {
|
|
19
|
+
return { passed: true, reason: 'always passes' };
|
|
20
|
+
}
|
|
21
|
+
while (Date.now() < deadline) {
|
|
22
|
+
const result = await checkOnce(adapter, spec);
|
|
23
|
+
if (result.passed)
|
|
24
|
+
return result;
|
|
25
|
+
const remaining = deadline - Date.now();
|
|
26
|
+
if (remaining <= 0)
|
|
27
|
+
break;
|
|
28
|
+
await sleep(Math.min(pollInterval, remaining));
|
|
29
|
+
}
|
|
30
|
+
// Final check after timeout
|
|
31
|
+
return checkOnce(adapter, spec);
|
|
32
|
+
}
|
|
33
|
+
async function checkOnce(adapter, spec) {
|
|
34
|
+
switch (spec.type) {
|
|
35
|
+
case 'route_matches':
|
|
36
|
+
return checkRouteMatches(adapter, spec.pattern);
|
|
37
|
+
case 'element_visible':
|
|
38
|
+
return checkElementVisible(adapter, spec.selector);
|
|
39
|
+
case 'element_absent':
|
|
40
|
+
return checkElementAbsent(adapter, spec.selector);
|
|
41
|
+
case 'text_contains':
|
|
42
|
+
return checkTextContains(adapter, spec.selector, spec.text);
|
|
43
|
+
case 'overlay_dismissed':
|
|
44
|
+
return checkOverlayDismissed(adapter);
|
|
45
|
+
case 'screenshot_stable':
|
|
46
|
+
return checkScreenshotStable(adapter, spec.threshold ?? 0.01);
|
|
47
|
+
case 'any_change':
|
|
48
|
+
// 'any_change' is a soft postcondition — we just assume the action did something.
|
|
49
|
+
// The action-verifier handles real change detection via AKTree diff.
|
|
50
|
+
return { passed: true, reason: 'any_change always passes (action verifier handles real detection)' };
|
|
51
|
+
case 'always':
|
|
52
|
+
return { passed: true, reason: 'always passes' };
|
|
53
|
+
default:
|
|
54
|
+
return { passed: false, reason: `unknown postcondition type: ${spec.type}` };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// ── Individual checks ───────────────────────────────────────────────
|
|
58
|
+
async function checkRouteMatches(adapter, pattern) {
|
|
59
|
+
const url = await adapter.getCurrentUrl();
|
|
60
|
+
try {
|
|
61
|
+
const { pathname, search } = new URL(url);
|
|
62
|
+
const fullPath = pathname + search;
|
|
63
|
+
// Support glob-like patterns: * matches anything, ** matches path segments
|
|
64
|
+
const regexStr = pattern
|
|
65
|
+
.replace(/\*\*/g, '.*')
|
|
66
|
+
.replace(/\*/g, '[^/]*')
|
|
67
|
+
.replace(/\?/g, '[^/]');
|
|
68
|
+
const regex = new RegExp(`^${regexStr}$`);
|
|
69
|
+
if (regex.test(fullPath) || regex.test(pathname)) {
|
|
70
|
+
return { passed: true, reason: `URL "${fullPath}" matches pattern "${pattern}"` };
|
|
71
|
+
}
|
|
72
|
+
return { passed: false, reason: `URL "${fullPath}" does not match pattern "${pattern}"` };
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return { passed: false, reason: `invalid URL "${url}" or pattern "${pattern}"` };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function checkElementVisible(adapter, selector) {
|
|
79
|
+
// Primary check: use Playwright waitFor (fast, reliable)
|
|
80
|
+
try {
|
|
81
|
+
const found = await adapter.waitFor({ selector, state: 'visible', timeoutMs: 2000 });
|
|
82
|
+
if (found) {
|
|
83
|
+
return { passed: true, reason: `element "${selector}" is visible (Playwright)` };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Fall through to AKTree check
|
|
88
|
+
}
|
|
89
|
+
// Fallback: check AKTree
|
|
90
|
+
try {
|
|
91
|
+
const tree = await adapter.getAKTree();
|
|
92
|
+
if (hasVisibleNodeWithSelector(tree, selector)) {
|
|
93
|
+
return { passed: true, reason: `element "${selector}" is visible in AKTree` };
|
|
94
|
+
}
|
|
95
|
+
const serialized = serializeAKTree(tree);
|
|
96
|
+
if (serialized.includes(selector.replace(/[[\]"]/g, ''))) {
|
|
97
|
+
return { passed: true, reason: `element pattern "${selector}" found in serialized AKTree` };
|
|
98
|
+
}
|
|
99
|
+
return { passed: false, reason: `element "${selector}" not visible` };
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return { passed: false, reason: `element "${selector}" not verifiable (AKTree unavailable)` };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function checkElementAbsent(adapter, selector) {
|
|
106
|
+
// Use Playwright: if waitFor fails (element not found), it's absent = good
|
|
107
|
+
try {
|
|
108
|
+
const found = await adapter.waitFor({ selector, state: 'visible', timeoutMs: 1000 });
|
|
109
|
+
if (found) {
|
|
110
|
+
return { passed: false, reason: `element "${selector}" is still visible` };
|
|
111
|
+
}
|
|
112
|
+
return { passed: true, reason: `element "${selector}" is absent` };
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return { passed: true, reason: `element "${selector}" is absent` };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async function checkTextContains(adapter, selector, expectedText) {
|
|
119
|
+
try {
|
|
120
|
+
const tree = await adapter.getAKTree();
|
|
121
|
+
const node = findNodeBySelector(tree, selector);
|
|
122
|
+
if (!node) {
|
|
123
|
+
return { passed: false, reason: `element "${selector}" not found for text check` };
|
|
124
|
+
}
|
|
125
|
+
const nodeText = (node.label || '') + (node.value || '');
|
|
126
|
+
if (nodeText.includes(expectedText)) {
|
|
127
|
+
return { passed: true, reason: `element "${selector}" contains "${expectedText}"` };
|
|
128
|
+
}
|
|
129
|
+
return { passed: false, reason: `element "${selector}" text "${nodeText}" does not contain "${expectedText}"` };
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
return { passed: false, reason: `error checking text: ${err}` };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function checkOverlayDismissed(adapter) {
|
|
136
|
+
try {
|
|
137
|
+
const tree = await adapter.getAKTree();
|
|
138
|
+
// Check if any overlays are reported in the tree
|
|
139
|
+
if (tree.overlays.length === 0) {
|
|
140
|
+
return { passed: true, reason: 'no overlays detected' };
|
|
141
|
+
}
|
|
142
|
+
// Check if remaining overlays are blocking
|
|
143
|
+
const blocking = tree.overlays.filter(o => o.blocksInteraction);
|
|
144
|
+
if (blocking.length === 0) {
|
|
145
|
+
return { passed: true, reason: 'overlays present but none blocking interaction' };
|
|
146
|
+
}
|
|
147
|
+
return { passed: false, reason: `${blocking.length} blocking overlay(s) still present` };
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// If AKTree is unavailable (e.g. page.evaluate failure), assume overlays are dismissed.
|
|
151
|
+
// The overlay dismissal itself ran; we just can't verify via AKTree.
|
|
152
|
+
return { passed: true, reason: 'overlay check skipped (AKTree unavailable), assuming dismissed' };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
let lastScreenshotHash = null;
|
|
156
|
+
async function checkScreenshotStable(adapter, threshold) {
|
|
157
|
+
try {
|
|
158
|
+
const screenshot = await adapter.takeScreenshot();
|
|
159
|
+
const currentHash = simpleHash(screenshot);
|
|
160
|
+
if (lastScreenshotHash === null) {
|
|
161
|
+
lastScreenshotHash = currentHash;
|
|
162
|
+
// Wait and take another screenshot
|
|
163
|
+
await sleep(500);
|
|
164
|
+
const screenshot2 = await adapter.takeScreenshot();
|
|
165
|
+
const hash2 = simpleHash(screenshot2);
|
|
166
|
+
lastScreenshotHash = null;
|
|
167
|
+
if (currentHash === hash2) {
|
|
168
|
+
return { passed: true, reason: 'consecutive screenshots are identical' };
|
|
169
|
+
}
|
|
170
|
+
return { passed: false, reason: 'consecutive screenshots differ (page still changing)' };
|
|
171
|
+
}
|
|
172
|
+
// Compare with previous
|
|
173
|
+
if (currentHash === lastScreenshotHash) {
|
|
174
|
+
lastScreenshotHash = null;
|
|
175
|
+
return { passed: true, reason: 'screenshot matches previous' };
|
|
176
|
+
}
|
|
177
|
+
lastScreenshotHash = currentHash;
|
|
178
|
+
return { passed: false, reason: 'screenshot changed from previous check' };
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
lastScreenshotHash = null;
|
|
182
|
+
return { passed: false, reason: `error checking screenshot stability: ${err}` };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
186
|
+
function hasVisibleNodeWithSelector(tree, selector) {
|
|
187
|
+
// Walk the AKTree looking for a visible node whose sourceRef matches the selector
|
|
188
|
+
function walk(node) {
|
|
189
|
+
if (node.visible && node.sourceRef && matchesSelectorHeuristic(node.sourceRef, selector)) {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
return node.children.some(walk);
|
|
193
|
+
}
|
|
194
|
+
return walk(tree.root);
|
|
195
|
+
}
|
|
196
|
+
function findNodeBySelector(tree, selector) {
|
|
197
|
+
function walk(node) {
|
|
198
|
+
if (node.sourceRef && matchesSelectorHeuristic(node.sourceRef, selector)) {
|
|
199
|
+
return node;
|
|
200
|
+
}
|
|
201
|
+
for (const child of node.children) {
|
|
202
|
+
const found = walk(child);
|
|
203
|
+
if (found)
|
|
204
|
+
return found;
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
return walk(tree.root);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Heuristic match between an AKTree sourceRef and a CSS-like selector.
|
|
212
|
+
* Not a full CSS selector engine — handles common patterns:
|
|
213
|
+
* - Tag name: "h1", "button"
|
|
214
|
+
* - ID: "#my-id"
|
|
215
|
+
* - data-testid: [data-testid="x"]
|
|
216
|
+
* - Class: ".my-class"
|
|
217
|
+
*/
|
|
218
|
+
function matchesSelectorHeuristic(sourceRef, selector) {
|
|
219
|
+
const lower = sourceRef.toLowerCase();
|
|
220
|
+
const selectorLower = selector.toLowerCase();
|
|
221
|
+
// data-testid match
|
|
222
|
+
const testIdMatch = selector.match(/\[data-testid=["'](.+?)["']\]/);
|
|
223
|
+
if (testIdMatch) {
|
|
224
|
+
return lower.includes(`data-testid="${testIdMatch[1]}"`);
|
|
225
|
+
}
|
|
226
|
+
// ID match
|
|
227
|
+
if (selector.startsWith('#')) {
|
|
228
|
+
return lower.includes(`id="${selector.slice(1)}"`) || lower.includes(`#${selector.slice(1)}`);
|
|
229
|
+
}
|
|
230
|
+
// Tag name match
|
|
231
|
+
if (/^[a-z][a-z0-9]*$/i.test(selector)) {
|
|
232
|
+
return lower.startsWith(`<${selectorLower}`) || lower.includes(`<${selectorLower} `);
|
|
233
|
+
}
|
|
234
|
+
// Fallback: contains
|
|
235
|
+
return lower.includes(selectorLower);
|
|
236
|
+
}
|
|
237
|
+
function simpleHash(buffer) {
|
|
238
|
+
// Fast non-crypto hash for screenshot comparison
|
|
239
|
+
let hash = 0;
|
|
240
|
+
const step = Math.max(1, Math.floor(buffer.length / 10000));
|
|
241
|
+
for (let i = 0; i < buffer.length; i += step) {
|
|
242
|
+
hash = ((hash << 5) - hash + buffer[i]) | 0;
|
|
243
|
+
}
|
|
244
|
+
return hash.toString(36);
|
|
245
|
+
}
|
|
246
|
+
function sleep(ms) {
|
|
247
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=postcondition.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture Agent — Program Patcher
|
|
3
|
+
*
|
|
4
|
+
* Applies healer patches to compiled programs.
|
|
5
|
+
* Patches are applied in-memory during the run, and propagated to
|
|
6
|
+
* the server ONLY after a fully successful run.
|
|
7
|
+
*/
|
|
8
|
+
import type { ExecutionProgram, HealerPatch } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Applies a healer patch to a program in-place.
|
|
11
|
+
* The original opcode at the given index is replaced by the healer's output.
|
|
12
|
+
* If the healer produced multiple opcodes, subsequent steps are shifted.
|
|
13
|
+
*
|
|
14
|
+
* Returns the modified program (same reference, mutated).
|
|
15
|
+
*/
|
|
16
|
+
export declare function applyPatch(program: ExecutionProgram, patch: HealerPatch): ExecutionProgram;
|
|
17
|
+
/**
|
|
18
|
+
* Applies all patches from a successful run to a program.
|
|
19
|
+
* Patches are applied in reverse index order to avoid shifting issues.
|
|
20
|
+
*/
|
|
21
|
+
export declare function applyAllPatches(program: ExecutionProgram, patches: HealerPatch[]): ExecutionProgram;
|
|
22
|
+
/**
|
|
23
|
+
* Creates a deep clone of a program for safe patching.
|
|
24
|
+
*/
|
|
25
|
+
export declare function cloneProgram(program: ExecutionProgram): ExecutionProgram;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture Agent — Program Patcher
|
|
3
|
+
*
|
|
4
|
+
* Applies healer patches to compiled programs.
|
|
5
|
+
* Patches are applied in-memory during the run, and propagated to
|
|
6
|
+
* the server ONLY after a fully successful run.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Applies a healer patch to a program in-place.
|
|
10
|
+
* The original opcode at the given index is replaced by the healer's output.
|
|
11
|
+
* If the healer produced multiple opcodes, subsequent steps are shifted.
|
|
12
|
+
*
|
|
13
|
+
* Returns the modified program (same reference, mutated).
|
|
14
|
+
*/
|
|
15
|
+
export function applyPatch(program, patch) {
|
|
16
|
+
const { opcodeIndex, replacementOpcodes } = patch;
|
|
17
|
+
if (opcodeIndex < 0 || opcodeIndex >= program.steps.length) {
|
|
18
|
+
throw new Error(`patch index ${opcodeIndex} out of bounds (program has ${program.steps.length} steps)`);
|
|
19
|
+
}
|
|
20
|
+
// Replace the failed opcode with the replacement(s)
|
|
21
|
+
program.steps.splice(opcodeIndex, 1, ...replacementOpcodes);
|
|
22
|
+
// Bump version
|
|
23
|
+
program.programVersion++;
|
|
24
|
+
return program;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Applies all patches from a successful run to a program.
|
|
28
|
+
* Patches are applied in reverse index order to avoid shifting issues.
|
|
29
|
+
*/
|
|
30
|
+
export function applyAllPatches(program, patches) {
|
|
31
|
+
// Sort patches by index descending to apply from end to start
|
|
32
|
+
const sorted = [...patches].sort((a, b) => b.opcodeIndex - a.opcodeIndex);
|
|
33
|
+
for (const patch of sorted) {
|
|
34
|
+
applyPatch(program, patch);
|
|
35
|
+
}
|
|
36
|
+
return program;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Creates a deep clone of a program for safe patching.
|
|
40
|
+
*/
|
|
41
|
+
export function cloneProgram(program) {
|
|
42
|
+
return JSON.parse(JSON.stringify(program));
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=program-patcher.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture Agent — Recovery Chain
|
|
3
|
+
*
|
|
4
|
+
* Ordered recovery strategies when a deterministic opcode fails:
|
|
5
|
+
* 1. Deterministic retry (same opcode, fresh observation)
|
|
6
|
+
* 2. Selector memory lookup (known-good selectors from Supabase)
|
|
7
|
+
* 3. Alternative interaction (keyboard, JS dispatch, coordinates)
|
|
8
|
+
* 4. Targeted reload (reload page and retry)
|
|
9
|
+
* 5. LLM Healer (last resort)
|
|
10
|
+
*/
|
|
11
|
+
import type { ExecutionOpcode, RuntimeAdapter } from './types.js';
|
|
12
|
+
import type { RecoveryChain as IRecoveryChain, RecoveryAttemptResult, RecoveryAttemptOptions } from './opcode-runner.js';
|
|
13
|
+
import { type HealerLLMProvider } from './llm-healer.js';
|
|
14
|
+
export interface RecoveryChainOptions {
|
|
15
|
+
/** Selector memory: stepSignature -> known-good selectors */
|
|
16
|
+
selectorMemory?: Record<string, string[]>;
|
|
17
|
+
/** LLM provider for the healer (optional, disables healer if not provided) */
|
|
18
|
+
llmProvider?: HealerLLMProvider;
|
|
19
|
+
/** Max healer invocations per run. Default: 3 */
|
|
20
|
+
maxHealerInvocations?: number;
|
|
21
|
+
/** Full program steps for healer context */
|
|
22
|
+
programSteps?: ExecutionOpcode[];
|
|
23
|
+
}
|
|
24
|
+
export declare class RecoveryChainImpl implements IRecoveryChain {
|
|
25
|
+
private healer;
|
|
26
|
+
private selectorMemory;
|
|
27
|
+
private programSteps;
|
|
28
|
+
constructor(options?: RecoveryChainOptions);
|
|
29
|
+
attempt(failedOpcode: ExecutionOpcode, opcodeIndex: number, adapter: RuntimeAdapter, options?: RecoveryAttemptOptions): Promise<RecoveryAttemptResult>;
|
|
30
|
+
}
|