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,368 @@
|
|
|
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 { resolveSelector } from './selector-resolver.js';
|
|
12
|
+
import { evaluatePostcondition } from './postcondition.js';
|
|
13
|
+
import { LLMHealer } from './llm-healer.js';
|
|
14
|
+
import { serializeAKTree } from '../ak-tree.js';
|
|
15
|
+
export class RecoveryChainImpl {
|
|
16
|
+
healer;
|
|
17
|
+
selectorMemory;
|
|
18
|
+
programSteps;
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.selectorMemory = options.selectorMemory ?? {};
|
|
21
|
+
this.programSteps = options.programSteps ?? [];
|
|
22
|
+
this.healer = options.llmProvider
|
|
23
|
+
? new LLMHealer(options.llmProvider, { maxInvocations: options.maxHealerInvocations ?? 3 })
|
|
24
|
+
: null;
|
|
25
|
+
}
|
|
26
|
+
async attempt(failedOpcode, opcodeIndex, adapter, options = {}) {
|
|
27
|
+
const recovery = failedOpcode.recovery;
|
|
28
|
+
// Strategy 1: Deterministic retry
|
|
29
|
+
const retryBudget = Math.max(0, Math.min(recovery.retries, options.maxDeterministicRetries ?? recovery.retries));
|
|
30
|
+
if (retryBudget > 0) {
|
|
31
|
+
const result = await retryOpcode(failedOpcode, adapter, retryBudget, options.remainingTimeMs);
|
|
32
|
+
if (result.recovered)
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
// Strategy 2: Selector memory + alternate selectors
|
|
36
|
+
if (recovery.useSelectorMemory && hasSelector(failedOpcode)) {
|
|
37
|
+
const result = await trySelectorAlternatives(failedOpcode, opcodeIndex, adapter, this.selectorMemory);
|
|
38
|
+
if (result.recovered)
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
// Strategy 3: Alternative interaction methods
|
|
42
|
+
if (recovery.useAltInteraction && hasSelector(failedOpcode)) {
|
|
43
|
+
const result = await tryAltInteraction(failedOpcode, adapter);
|
|
44
|
+
if (result.recovered)
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
// Strategy 4: Targeted reload
|
|
48
|
+
if (recovery.allowReload) {
|
|
49
|
+
const result = await tryReload(failedOpcode, adapter);
|
|
50
|
+
if (result.recovered)
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
// Strategy 5: LLM Healer
|
|
54
|
+
if (recovery.allowHealer && this.healer && this.healer.remainingInvocations > 0) {
|
|
55
|
+
const result = await tryHealer(failedOpcode, opcodeIndex, adapter, this.healer, this.programSteps);
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
return { recovered: false, reason: 'all recovery strategies exhausted' };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// ── Strategy 1: Deterministic retry ─────────────────────────────────
|
|
62
|
+
async function retryOpcode(opcode, adapter, maxRetries, remainingTimeMs) {
|
|
63
|
+
const deadlineMs = typeof remainingTimeMs === 'number'
|
|
64
|
+
? Date.now() + remainingTimeMs
|
|
65
|
+
: Number.POSITIVE_INFINITY;
|
|
66
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
67
|
+
if (Date.now() >= deadlineMs) {
|
|
68
|
+
return { recovered: false, reason: 'retry budget exhausted by timeout' };
|
|
69
|
+
}
|
|
70
|
+
await sleep(500 * (i + 1)); // backoff
|
|
71
|
+
try {
|
|
72
|
+
await executeRawAction(opcode, adapter);
|
|
73
|
+
const postcondition = await evaluatePostcondition(adapter, clampPostconditionWait(opcode.postcondition, deadlineMs));
|
|
74
|
+
if (postcondition.passed) {
|
|
75
|
+
return { recovered: true, strategy: 'retry', reason: `retry ${i + 1}/${maxRetries} succeeded` };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// continue to next retry
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return { recovered: false, reason: `${maxRetries} retries exhausted` };
|
|
83
|
+
}
|
|
84
|
+
// ── Strategy 2: Selector memory ─────────────────────────────────────
|
|
85
|
+
async function trySelectorAlternatives(opcode, opcodeIndex, adapter, selectorMemory) {
|
|
86
|
+
if (!hasSelector(opcode)) {
|
|
87
|
+
return { recovered: false, reason: 'opcode has no selector' };
|
|
88
|
+
}
|
|
89
|
+
const selectorOpcode = opcode;
|
|
90
|
+
const primarySelector = selectorOpcode.selector ?? '';
|
|
91
|
+
const stepSignature = `${opcode.kind}:${primarySelector}`;
|
|
92
|
+
const resolved = await resolveSelector(adapter, {
|
|
93
|
+
primary: primarySelector,
|
|
94
|
+
fingerprint: selectorOpcode.fingerprint,
|
|
95
|
+
alternates: selectorOpcode.selectorAlternates,
|
|
96
|
+
selectorMemory,
|
|
97
|
+
stepSignature,
|
|
98
|
+
description: opcode.description,
|
|
99
|
+
});
|
|
100
|
+
if (!resolved || resolved.strategy === 'primary') {
|
|
101
|
+
return { recovered: false, reason: 'no alternative selectors found' };
|
|
102
|
+
}
|
|
103
|
+
// Try the resolved selector
|
|
104
|
+
try {
|
|
105
|
+
await executeWithSelector(opcode, adapter, resolved.selector);
|
|
106
|
+
const postcondition = await evaluatePostcondition(adapter, opcode.postcondition);
|
|
107
|
+
if (postcondition.passed) {
|
|
108
|
+
return {
|
|
109
|
+
recovered: true,
|
|
110
|
+
strategy: 'selector_memory',
|
|
111
|
+
reason: `resolved via ${resolved.strategy}: "${resolved.selector}"`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// fall through
|
|
117
|
+
}
|
|
118
|
+
return { recovered: false, reason: `resolved selector "${resolved.selector}" also failed` };
|
|
119
|
+
}
|
|
120
|
+
// ── Strategy 3: Alternative interaction ─────────────────────────────
|
|
121
|
+
async function tryAltInteraction(opcode, adapter) {
|
|
122
|
+
if (opcode.kind !== 'CLICK' || !opcode.selector) {
|
|
123
|
+
return { recovered: false, reason: 'alt interaction only for CLICK opcodes with selectors' };
|
|
124
|
+
}
|
|
125
|
+
const clickOpcode = opcode;
|
|
126
|
+
const sel = clickOpcode.selector;
|
|
127
|
+
const methods = [
|
|
128
|
+
{ name: 'keyboard', options: { useKeyboard: true } },
|
|
129
|
+
{ name: 'js_dispatch', options: { useJsDispatch: true } },
|
|
130
|
+
];
|
|
131
|
+
for (const method of methods) {
|
|
132
|
+
try {
|
|
133
|
+
await adapter.click(sel, method.options);
|
|
134
|
+
await sleep(500);
|
|
135
|
+
const postcondition = await evaluatePostcondition(adapter, opcode.postcondition);
|
|
136
|
+
if (postcondition.passed) {
|
|
137
|
+
return {
|
|
138
|
+
recovered: true,
|
|
139
|
+
strategy: 'alt_interaction',
|
|
140
|
+
reason: `${method.name} interaction succeeded`,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// continue to next method
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Try coordinate-based click from AKTree
|
|
149
|
+
try {
|
|
150
|
+
const tree = await adapter.getAKTree();
|
|
151
|
+
const node = findInteractiveNodeBySelector(tree, sel);
|
|
152
|
+
if (node) {
|
|
153
|
+
const x = node.bounds.x + node.bounds.w / 2;
|
|
154
|
+
const y = node.bounds.y + node.bounds.h / 2;
|
|
155
|
+
await adapter.click(sel, { coordinates: { x, y } });
|
|
156
|
+
await sleep(500);
|
|
157
|
+
const postcondition = await evaluatePostcondition(adapter, opcode.postcondition);
|
|
158
|
+
if (postcondition.passed) {
|
|
159
|
+
return {
|
|
160
|
+
recovered: true,
|
|
161
|
+
strategy: 'alt_interaction',
|
|
162
|
+
reason: `coordinate click at (${x}, ${y}) succeeded`,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// fall through
|
|
169
|
+
}
|
|
170
|
+
return { recovered: false, reason: 'all alternative interactions failed' };
|
|
171
|
+
}
|
|
172
|
+
// ── Strategy 4: Targeted reload ─────────────────────────────────────
|
|
173
|
+
async function tryReload(opcode, adapter) {
|
|
174
|
+
try {
|
|
175
|
+
const url = await adapter.getCurrentUrl();
|
|
176
|
+
await adapter.navigate(url);
|
|
177
|
+
await sleep(1000);
|
|
178
|
+
await adapter.dismissOverlays();
|
|
179
|
+
await executeRawAction(opcode, adapter);
|
|
180
|
+
const postcondition = await evaluatePostcondition(adapter, opcode.postcondition);
|
|
181
|
+
if (postcondition.passed) {
|
|
182
|
+
return { recovered: true, strategy: 'reload', reason: 'reload + retry succeeded' };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// fall through
|
|
187
|
+
}
|
|
188
|
+
return { recovered: false, reason: 'reload + retry failed' };
|
|
189
|
+
}
|
|
190
|
+
// ── Strategy 5: LLM Healer ─────────────────────────────────────────
|
|
191
|
+
async function tryHealer(failedOpcode, opcodeIndex, adapter, healer, programSteps) {
|
|
192
|
+
try {
|
|
193
|
+
const tree = await adapter.getAKTree();
|
|
194
|
+
const akTreeSerialized = serializeAKTree(tree);
|
|
195
|
+
const currentUrl = await adapter.getCurrentUrl();
|
|
196
|
+
let screenshot;
|
|
197
|
+
try {
|
|
198
|
+
screenshot = await adapter.takeScreenshot();
|
|
199
|
+
}
|
|
200
|
+
catch { /* optional */ }
|
|
201
|
+
// Get surrounding opcodes for context
|
|
202
|
+
const start = Math.max(0, opcodeIndex - 3);
|
|
203
|
+
const end = Math.min(programSteps.length, opcodeIndex + 4);
|
|
204
|
+
const surroundingOpcodes = programSteps.slice(start, end).filter((_, i) => start + i !== opcodeIndex);
|
|
205
|
+
const context = {
|
|
206
|
+
failedOpcode,
|
|
207
|
+
opcodeIndex,
|
|
208
|
+
akTreeSerialized,
|
|
209
|
+
currentUrl,
|
|
210
|
+
screenshot,
|
|
211
|
+
surroundingOpcodes,
|
|
212
|
+
errorMessage: `opcode ${failedOpcode.kind} failed after all deterministic recovery strategies`,
|
|
213
|
+
};
|
|
214
|
+
const result = await healer.heal(context);
|
|
215
|
+
if (result.healed && result.patch) {
|
|
216
|
+
// Execute the replacement opcode(s) to verify they work
|
|
217
|
+
for (const replacementOp of result.patch.replacementOpcodes) {
|
|
218
|
+
try {
|
|
219
|
+
await executeRawAction(replacementOp, adapter);
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
return {
|
|
223
|
+
recovered: false,
|
|
224
|
+
reason: `healer patch failed during execution: ${err instanceof Error ? err.message : String(err)}`,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// Verify the last replacement's postcondition
|
|
229
|
+
const lastOp = result.patch.replacementOpcodes[result.patch.replacementOpcodes.length - 1];
|
|
230
|
+
const postcondition = await evaluatePostcondition(adapter, lastOp.postcondition);
|
|
231
|
+
if (postcondition.passed) {
|
|
232
|
+
return {
|
|
233
|
+
recovered: true,
|
|
234
|
+
strategy: 'healer',
|
|
235
|
+
patch: result.patch,
|
|
236
|
+
reason: result.reason,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return { recovered: false, reason: `healer patch executed but postcondition failed: ${postcondition.reason}` };
|
|
240
|
+
}
|
|
241
|
+
return { recovered: false, reason: result.reason };
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
return {
|
|
245
|
+
recovered: false,
|
|
246
|
+
reason: `healer error: ${err instanceof Error ? err.message : String(err)}`,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// ── Shared helpers ──────────────────────────────────────────────────
|
|
251
|
+
function hasSelector(opcode) {
|
|
252
|
+
return 'selector' in opcode && typeof opcode.selector === 'string' && !!opcode.selector;
|
|
253
|
+
}
|
|
254
|
+
async function executeRawAction(opcode, adapter) {
|
|
255
|
+
switch (opcode.kind) {
|
|
256
|
+
case 'NAVIGATE':
|
|
257
|
+
await adapter.navigate(opcode.url);
|
|
258
|
+
break;
|
|
259
|
+
case 'DISMISS_OVERLAYS':
|
|
260
|
+
await adapter.dismissOverlays();
|
|
261
|
+
break;
|
|
262
|
+
case 'CLICK':
|
|
263
|
+
try {
|
|
264
|
+
await adapter.click(opcode.selector);
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
if (!adapter.clickByTarget || !opcode.target)
|
|
268
|
+
throw error;
|
|
269
|
+
await adapter.clickByTarget({
|
|
270
|
+
selector: opcode.selector,
|
|
271
|
+
target: opcode.target,
|
|
272
|
+
selectorAlternates: opcode.selectorAlternates,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
break;
|
|
276
|
+
case 'TYPE':
|
|
277
|
+
try {
|
|
278
|
+
await adapter.type(opcode.selector, opcode.text, opcode.clearFirst);
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
if (!adapter.typeByTarget || !opcode.target)
|
|
282
|
+
throw error;
|
|
283
|
+
await adapter.typeByTarget({
|
|
284
|
+
selector: opcode.selector,
|
|
285
|
+
target: opcode.target,
|
|
286
|
+
selectorAlternates: opcode.selectorAlternates,
|
|
287
|
+
}, opcode.text, opcode.clearFirst);
|
|
288
|
+
}
|
|
289
|
+
break;
|
|
290
|
+
case 'PRESS_KEY':
|
|
291
|
+
await adapter.pressKey(opcode.key);
|
|
292
|
+
break;
|
|
293
|
+
case 'WAIT_FOR':
|
|
294
|
+
if (opcode.selector) {
|
|
295
|
+
await adapter.waitFor({ selector: opcode.selector, state: opcode.state, timeoutMs: opcode.timeoutMs });
|
|
296
|
+
}
|
|
297
|
+
else if (adapter.waitForTarget) {
|
|
298
|
+
await adapter.waitForTarget({ target: opcode.target }, opcode.timeoutMs);
|
|
299
|
+
}
|
|
300
|
+
break;
|
|
301
|
+
case 'SCROLL':
|
|
302
|
+
if (opcode.targetSelector)
|
|
303
|
+
await adapter.scrollIntoView(opcode.targetSelector);
|
|
304
|
+
else
|
|
305
|
+
await adapter.scroll(opcode.direction, opcode.amount);
|
|
306
|
+
break;
|
|
307
|
+
case 'SET_LOCALE':
|
|
308
|
+
if (opcode.method === 'browser_context') {
|
|
309
|
+
await adapter.setLocale(opcode.locale);
|
|
310
|
+
if (adapter.reloadPage)
|
|
311
|
+
await adapter.reloadPage();
|
|
312
|
+
}
|
|
313
|
+
else if (opcode.method === 'ui_interaction' && opcode.selector) {
|
|
314
|
+
await adapter.click(opcode.selector);
|
|
315
|
+
}
|
|
316
|
+
break;
|
|
317
|
+
case 'SET_THEME':
|
|
318
|
+
if (opcode.method === 'color_scheme')
|
|
319
|
+
await adapter.setColorScheme(opcode.theme);
|
|
320
|
+
else if (opcode.method === 'ui_interaction' && opcode.selector)
|
|
321
|
+
await adapter.click(opcode.selector);
|
|
322
|
+
break;
|
|
323
|
+
case 'CAPTURE_SCREENSHOT':
|
|
324
|
+
case 'BEGIN_CLIP':
|
|
325
|
+
case 'END_CLIP':
|
|
326
|
+
case 'ASSERT_ROUTE':
|
|
327
|
+
case 'ASSERT_SURFACE':
|
|
328
|
+
// These are handled by postconditions, not raw actions
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
async function executeWithSelector(opcode, adapter, newSelector) {
|
|
333
|
+
switch (opcode.kind) {
|
|
334
|
+
case 'CLICK':
|
|
335
|
+
await adapter.click(newSelector);
|
|
336
|
+
break;
|
|
337
|
+
case 'TYPE':
|
|
338
|
+
await adapter.type(newSelector, opcode.text, opcode.clearFirst);
|
|
339
|
+
break;
|
|
340
|
+
default:
|
|
341
|
+
throw new Error(`cannot swap selector for opcode kind: ${opcode.kind}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function findInteractiveNodeBySelector(tree, selector) {
|
|
345
|
+
function walk(node) {
|
|
346
|
+
if (node.visible && node.interactive && node.sourceRef.includes(selector.replace(/[[\]"#.]/g, ''))) {
|
|
347
|
+
return node;
|
|
348
|
+
}
|
|
349
|
+
for (const child of node.children) {
|
|
350
|
+
const found = walk(child);
|
|
351
|
+
if (found)
|
|
352
|
+
return found;
|
|
353
|
+
}
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
return walk(tree.root);
|
|
357
|
+
}
|
|
358
|
+
function sleep(ms) {
|
|
359
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
360
|
+
}
|
|
361
|
+
function clampPostconditionWait(spec, deadlineMs) {
|
|
362
|
+
const remainingTimeMs = Math.max(1, deadlineMs - Date.now());
|
|
363
|
+
return {
|
|
364
|
+
...spec,
|
|
365
|
+
waitMs: Math.max(1, Math.min(spec.waitMs ?? remainingTimeMs, remainingTimeMs)),
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
//# sourceMappingURL=recovery-chain.js.map
|