explorbot 0.1.9 → 0.1.11
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/README.md +27 -1
- package/bin/explorbot-cli.ts +86 -15
- package/boat/api-tester/src/ai/curler-tools.ts +3 -3
- package/boat/api-tester/src/ai/curler.ts +1 -1
- package/boat/api-tester/src/apibot.ts +2 -2
- package/boat/api-tester/src/config.ts +1 -1
- package/dist/bin/explorbot-cli.js +85 -14
- package/dist/boat/api-tester/src/ai/curler-tools.js +2 -2
- package/dist/boat/api-tester/src/apibot.js +2 -2
- package/dist/package.json +2 -2
- package/dist/rules/navigator/output.md +9 -0
- package/dist/rules/navigator/verification-actions.md +2 -0
- package/dist/src/action-result.js +23 -1
- package/dist/src/action.js +46 -38
- package/dist/src/ai/bosun.js +16 -2
- package/dist/src/ai/conversation.js +39 -0
- package/dist/src/ai/experience-compactor.js +235 -50
- package/dist/src/ai/historian/codeceptjs.js +109 -0
- package/dist/src/ai/historian/experience.js +320 -0
- package/dist/src/ai/historian/mixin.js +2 -0
- package/dist/src/ai/historian/playwright.js +145 -0
- package/dist/src/ai/historian/utils.js +18 -0
- package/dist/src/ai/historian.js +19 -398
- package/dist/src/ai/navigator.js +133 -80
- package/dist/src/ai/pilot.js +254 -13
- package/dist/src/ai/planner/subpages.js +1 -30
- package/dist/src/ai/planner.js +33 -13
- package/dist/src/ai/provider.js +55 -18
- package/dist/src/ai/rerunner.js +3 -3
- package/dist/src/ai/researcher/deep-analysis.js +1 -1
- package/dist/src/ai/researcher/fingerprint-worker.js +1 -1
- package/dist/src/ai/researcher/locators.js +1 -1
- package/dist/src/ai/researcher/sections.js +8 -1
- package/dist/src/ai/researcher.js +43 -41
- package/dist/src/ai/rules.js +26 -14
- package/dist/src/ai/tester.js +90 -26
- package/dist/src/ai/tools.js +18 -10
- package/dist/src/api/request-store.js +20 -0
- package/dist/src/api/xhr-capture.js +19 -3
- package/dist/src/browser-server.js +16 -3
- package/dist/src/command-handler.js +1 -1
- package/dist/src/commands/add-rule-command.js +12 -9
- package/dist/src/commands/base-command.js +20 -0
- package/dist/src/commands/clean-command.js +3 -2
- package/dist/src/commands/compact-command.js +138 -0
- package/dist/src/commands/context-command.js +7 -1
- package/dist/src/commands/drill-command.js +4 -1
- package/dist/src/commands/experience-command.js +104 -0
- package/dist/src/commands/explore-command.js +54 -19
- package/dist/src/commands/freesail-command.js +2 -0
- package/dist/src/commands/index.js +7 -3
- package/dist/src/commands/init-command.js +11 -10
- package/dist/src/commands/learn-command.js +1 -1
- package/dist/src/commands/navigate-command.js +4 -1
- package/dist/src/commands/plan-clear-command.js +4 -1
- package/dist/src/commands/plan-command.js +43 -4
- package/dist/src/commands/plan-edit-command.js +1 -1
- package/dist/src/commands/plan-load-command.js +4 -1
- package/dist/src/commands/plan-reload-command.js +4 -1
- package/dist/src/commands/plan-save-command.js +20 -8
- package/dist/src/commands/rerun-command.js +4 -0
- package/dist/src/commands/research-command.js +5 -2
- package/dist/src/commands/start-command.js +5 -1
- package/dist/src/commands/test-command.js +7 -1
- package/dist/src/components/App.js +15 -5
- package/dist/src/execution-controller.js +13 -2
- package/dist/src/experience-tracker.js +174 -83
- package/dist/src/explorbot.js +31 -22
- package/dist/src/explorer.js +12 -5
- package/dist/src/observability.js +50 -99
- package/dist/src/playwright-recorder.js +309 -0
- package/dist/src/reporter.js +17 -2
- package/dist/src/stats.js +2 -0
- package/dist/src/suite.js +1 -1
- package/dist/src/test-plan.js +12 -0
- package/dist/src/utils/aria.js +37 -1
- package/dist/src/utils/error-page.js +30 -7
- package/dist/src/utils/logger.js +1 -1
- package/dist/src/utils/next-steps.js +37 -0
- package/dist/src/utils/rules-loader.js +1 -1
- package/dist/src/utils/test-files.js +1 -1
- package/dist/src/utils/url-matcher.js +50 -0
- package/package.json +2 -2
- package/rules/navigator/output.md +9 -0
- package/rules/navigator/verification-actions.md +2 -0
- package/src/action-result.ts +26 -1
- package/src/action.ts +44 -37
- package/src/ai/bosun.ts +16 -2
- package/src/ai/conversation.ts +37 -0
- package/src/ai/experience-compactor.ts +270 -63
- package/src/ai/historian/codeceptjs.ts +130 -0
- package/src/ai/historian/experience.ts +383 -0
- package/src/ai/historian/mixin.ts +4 -0
- package/src/ai/historian/playwright.ts +169 -0
- package/src/ai/historian/utils.ts +23 -0
- package/src/ai/historian.ts +35 -468
- package/src/ai/navigator.ts +140 -85
- package/src/ai/pilot.ts +259 -14
- package/src/ai/planner/subpages.ts +1 -24
- package/src/ai/planner.ts +34 -14
- package/src/ai/provider.ts +52 -18
- package/src/ai/rerunner.ts +3 -3
- package/src/ai/researcher/deep-analysis.ts +1 -1
- package/src/ai/researcher/fingerprint-worker.ts +1 -1
- package/src/ai/researcher/locators.ts +2 -2
- package/src/ai/researcher/sections.ts +7 -1
- package/src/ai/researcher.ts +47 -42
- package/src/ai/rules.ts +27 -14
- package/src/ai/task-agent.ts +1 -1
- package/src/ai/tester.ts +94 -26
- package/src/ai/tools.ts +53 -29
- package/src/api/request-store.ts +22 -0
- package/src/api/xhr-capture.ts +21 -3
- package/src/browser-server.ts +17 -3
- package/src/command-handler.ts +1 -1
- package/src/commands/add-rule-command.ts +13 -9
- package/src/commands/base-command.ts +26 -1
- package/src/commands/clean-command.ts +4 -3
- package/src/commands/compact-command.ts +156 -0
- package/src/commands/context-command.ts +8 -2
- package/src/commands/drill-command.ts +5 -2
- package/src/commands/experience-command.ts +125 -0
- package/src/commands/explore-command.ts +58 -21
- package/src/commands/freesail-command.ts +2 -0
- package/src/commands/index.ts +7 -3
- package/src/commands/init-command.ts +11 -10
- package/src/commands/learn-command.ts +2 -2
- package/src/commands/navigate-command.ts +5 -2
- package/src/commands/plan-clear-command.ts +5 -2
- package/src/commands/plan-command.ts +47 -5
- package/src/commands/plan-edit-command.ts +2 -2
- package/src/commands/plan-load-command.ts +5 -2
- package/src/commands/plan-reload-command.ts +5 -2
- package/src/commands/plan-save-command.ts +20 -9
- package/src/commands/rerun-command.ts +5 -0
- package/src/commands/research-command.ts +6 -3
- package/src/commands/start-command.ts +6 -2
- package/src/commands/test-command.ts +8 -2
- package/src/components/App.tsx +16 -5
- package/src/config.ts +6 -1
- package/src/execution-controller.ts +14 -3
- package/src/experience-tracker.ts +198 -100
- package/src/explorbot.ts +33 -23
- package/src/explorer.ts +14 -5
- package/src/observability.ts +50 -109
- package/src/playwright-recorder.ts +305 -0
- package/src/reporter.ts +17 -3
- package/src/stats.ts +4 -0
- package/src/suite.ts +1 -1
- package/src/test-plan.ts +12 -0
- package/src/utils/aria.ts +38 -1
- package/src/utils/error-page.ts +32 -7
- package/src/utils/logger.ts +1 -1
- package/src/utils/next-steps.ts +51 -0
- package/src/utils/rules-loader.ts +1 -1
- package/src/utils/test-files.ts +1 -1
- package/src/utils/url-matcher.ts +43 -0
package/dist/src/ai/navigator.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import dedent from 'dedent';
|
|
2
2
|
import { ActionResult } from '../action-result.js';
|
|
3
|
-
import { ExperienceTracker } from '../experience-tracker.js';
|
|
3
|
+
import { ExperienceTracker, renderExperienceToc } from '../experience-tracker.js';
|
|
4
4
|
import { KnowledgeTracker } from '../knowledge-tracker.js';
|
|
5
5
|
import { normalizeUrl } from '../state-manager.js';
|
|
6
6
|
import { extractCodeBlocks } from '../utils/code-extractor.js';
|
|
7
7
|
import { HooksRunner } from "../utils/hooks-runner.js";
|
|
8
8
|
import { createDebug, pluralize, tag } from '../utils/logger.js';
|
|
9
9
|
import { loop, pause } from '../utils/loop.js';
|
|
10
|
-
import { Researcher } from "./researcher.js";
|
|
11
|
-
import { actionRule, locatorRule } from './rules.js';
|
|
12
10
|
import { RulesLoader } from "../utils/rules-loader.js";
|
|
11
|
+
import { extractStatePath } from '../utils/url-matcher.js';
|
|
12
|
+
import { Researcher } from "./researcher.js";
|
|
13
|
+
import { actionRule, locatorRule, unexpectedPopupRule } from './rules.js';
|
|
13
14
|
import { isInteractive } from './task-agent.js';
|
|
15
|
+
import { createAgentTools } from "./tools.js";
|
|
14
16
|
const debugLog = createDebug('explorbot:navigator');
|
|
15
17
|
class Navigator {
|
|
16
18
|
emoji = '🧭';
|
|
@@ -18,8 +20,6 @@ class Navigator {
|
|
|
18
20
|
experienceCompactor;
|
|
19
21
|
knowledgeTracker;
|
|
20
22
|
experienceTracker;
|
|
21
|
-
currentAction = null;
|
|
22
|
-
currentUrl = null;
|
|
23
23
|
hooksRunner;
|
|
24
24
|
MAX_ATTEMPTS = Number.parseInt(process.env.MAX_ATTEMPTS || '5');
|
|
25
25
|
systemPrompt = dedent `
|
|
@@ -85,9 +85,7 @@ class Navigator {
|
|
|
85
85
|
const actualPath = action.stateManager.getCurrentState()?.url || '';
|
|
86
86
|
const actionResult = action.actionResult || ActionResult.fromState(action.stateManager.getCurrentState());
|
|
87
87
|
const originalMessage = `Navigate to: ${url}. Current page: ${actualPath}`;
|
|
88
|
-
this.
|
|
89
|
-
this.currentUrl = url;
|
|
90
|
-
const resolved = await this.resolveState(originalMessage, actionResult);
|
|
88
|
+
const resolved = await this.resolveState(originalMessage, actionResult, { action, expectedUrl: url });
|
|
91
89
|
if (!resolved) {
|
|
92
90
|
throw new Error(`Navigation to ${url} failed: redirected to ${actualPath} and could not resolve`);
|
|
93
91
|
}
|
|
@@ -99,9 +97,7 @@ class Navigator {
|
|
|
99
97
|
And I expected to see the URL in the browser
|
|
100
98
|
But I got error: ${action.lastError?.message || 'Navigation failed'}.
|
|
101
99
|
`.trim();
|
|
102
|
-
this.
|
|
103
|
-
this.currentUrl = url;
|
|
104
|
-
const resolved = await this.resolveState(originalMessage, actionResult);
|
|
100
|
+
const resolved = await this.resolveState(originalMessage, actionResult, { action, expectedUrl: url });
|
|
105
101
|
if (!resolved) {
|
|
106
102
|
throw new Error(`Navigation to ${url} failed: ${action.lastError?.message}`);
|
|
107
103
|
}
|
|
@@ -119,9 +115,11 @@ class Navigator {
|
|
|
119
115
|
throw error;
|
|
120
116
|
}
|
|
121
117
|
}
|
|
122
|
-
async resolveState(message, actionResult) {
|
|
118
|
+
async resolveState(message, actionResult, opts) {
|
|
123
119
|
tag('info').log('AI Navigator resolving state at', actionResult.url);
|
|
124
120
|
debugLog('Resolution message:', message);
|
|
121
|
+
const action = opts?.action ?? this.explorer.createAction();
|
|
122
|
+
const expectedUrl = opts?.expectedUrl;
|
|
125
123
|
let knowledge = '';
|
|
126
124
|
let experience = '';
|
|
127
125
|
const relevantKnowledge = this.knowledgeTracker.getRelevantKnowledge(actionResult);
|
|
@@ -133,24 +131,12 @@ class Navigator {
|
|
|
133
131
|
${knowledgeContent}
|
|
134
132
|
</hint>`;
|
|
135
133
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
<experience>
|
|
143
|
-
Here is the experience of interacting with the page.
|
|
144
|
-
Learn from it AND DO NOT REPEAT THE SAME MISTAKES.
|
|
145
|
-
If there was found successful solution to an issue, propose it as a first solution.
|
|
146
|
-
If there are no successful solutions, analyze failed intentions and actions to avoid them.
|
|
147
|
-
Do not try again same failed solutions
|
|
148
|
-
|
|
149
|
-
Focus on successful solutions and avoid actions and locators that caused errors in past.
|
|
150
|
-
|
|
151
|
-
${experienceContent}
|
|
152
|
-
|
|
153
|
-
</experience>`;
|
|
134
|
+
if (!actionResult.isInsideIframe) {
|
|
135
|
+
const successful = this.experienceTracker.getSuccessfulExperience(actionResult);
|
|
136
|
+
if (successful.length > 0) {
|
|
137
|
+
tag('substep').log(`Found ${successful.length} experience ${pluralize(successful.length, 'file')} for: ${actionResult.url}`);
|
|
138
|
+
experience = `<experience>\nPast successful recipes recorded from prior runs for this page. Prefer these solutions first if they match the goal.\n\n${successful.join('\n\n')}\n</experience>`;
|
|
139
|
+
}
|
|
154
140
|
}
|
|
155
141
|
const prompt = dedent `
|
|
156
142
|
<message>
|
|
@@ -177,6 +163,8 @@ class Navigator {
|
|
|
177
163
|
|
|
178
164
|
${actionRule}
|
|
179
165
|
|
|
166
|
+
${unexpectedPopupRule}
|
|
167
|
+
|
|
180
168
|
${RulesLoader.loadRules('navigator', ['multiple-locator', 'output'], actionResult.url || '').replace('{{maxAttempts}}', String(this.MAX_ATTEMPTS))}
|
|
181
169
|
|
|
182
170
|
${experience}
|
|
@@ -185,19 +173,22 @@ class Navigator {
|
|
|
185
173
|
`;
|
|
186
174
|
const conversation = this.provider.startConversation(this.systemPrompt, 'navigator');
|
|
187
175
|
conversation.addUserText(prompt);
|
|
176
|
+
const tools = undefined;
|
|
188
177
|
let codeBlocks = [];
|
|
189
178
|
let htmlContextAdded = false;
|
|
190
179
|
let codeBlockIndex = 0;
|
|
191
180
|
let totalAttempts = 0;
|
|
181
|
+
const progressBlocks = [];
|
|
182
|
+
const batchFailures = [];
|
|
192
183
|
let resolved = false;
|
|
193
184
|
await loop(async ({ stop }) => {
|
|
194
185
|
if (codeBlocks.length === 0) {
|
|
195
|
-
const result = await this.provider.invokeConversation(conversation);
|
|
186
|
+
const result = await this.provider.invokeConversation(conversation, tools);
|
|
196
187
|
if (!result)
|
|
197
188
|
return;
|
|
198
189
|
const aiResponse = result?.response?.text;
|
|
199
190
|
debugLog('AI:', aiResponse?.split('\n')[0]);
|
|
200
|
-
debugLog('Received AI response:', aiResponse
|
|
191
|
+
debugLog('Received AI response:', aiResponse?.length ?? 0, 'characters');
|
|
201
192
|
codeBlocks = extractCodeBlocks(aiResponse ?? '');
|
|
202
193
|
codeBlockIndex = 0;
|
|
203
194
|
}
|
|
@@ -207,42 +198,90 @@ class Navigator {
|
|
|
207
198
|
}
|
|
208
199
|
const codeBlock = codeBlocks[codeBlockIndex];
|
|
209
200
|
if (!codeBlock) {
|
|
201
|
+
if (batchFailures.length === 0 && htmlContextAdded) {
|
|
202
|
+
stop();
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
tag('substep').log('Feeding failures back to AI for a new batch...');
|
|
206
|
+
let contextMsg = 'Previous solutions did not work. Analyze the failures and try DIFFERENT strategies (not syntactic variants of the same locator).\n\n';
|
|
207
|
+
if (batchFailures.length > 0) {
|
|
208
|
+
const lines = batchFailures.map((f) => `- \`${f.code.split('\n')[0]}\` → ${f.error}`).join('\n');
|
|
209
|
+
contextMsg += `<previous_failures>\n${lines}\n</previous_failures>\n\n`;
|
|
210
|
+
}
|
|
210
211
|
if (!htmlContextAdded) {
|
|
211
212
|
htmlContextAdded = true;
|
|
212
|
-
|
|
213
|
-
conversation.addUserText(dedent `
|
|
214
|
-
Previous solutions did not work. Here is the full HTML context:
|
|
215
|
-
|
|
216
|
-
<page_html>
|
|
217
|
-
${await actionResult.combinedHtml()}
|
|
218
|
-
</page_html>
|
|
219
|
-
|
|
220
|
-
Please suggest new solutions based on this additional context.
|
|
221
|
-
`);
|
|
222
|
-
codeBlocks = [];
|
|
223
|
-
return;
|
|
213
|
+
contextMsg += `Full HTML context:\n\n<page_html>\n${await actionResult.combinedHtml()}\n</page_html>\n\n`;
|
|
224
214
|
}
|
|
225
|
-
|
|
215
|
+
contextMsg += 'Propose new solutions. If errors mention "intercepts pointer events" or timeouts on visible elements, an overlay is blocking — dismiss it first (Escape, click outside, Close button) before retrying the original action.';
|
|
216
|
+
conversation.addUserText(contextMsg);
|
|
217
|
+
codeBlocks = [];
|
|
218
|
+
batchFailures.length = 0;
|
|
226
219
|
return;
|
|
227
220
|
}
|
|
228
221
|
codeBlockIndex++;
|
|
229
222
|
totalAttempts++;
|
|
223
|
+
await this.explorer.switchToMainFrame();
|
|
224
|
+
const prevHash = action.actionResult?.getStateHash() ?? actionResult.getStateHash();
|
|
230
225
|
debugLog(`Attempting resolution: ${codeBlock}`);
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
resolved = true;
|
|
226
|
+
const attemptOk = await action.attempt(codeBlock, message);
|
|
227
|
+
const page = action.playwrightHelper?.page;
|
|
228
|
+
if (page) {
|
|
229
|
+
try {
|
|
230
|
+
await page.waitForLoadState('load', { timeout: 5000 });
|
|
237
231
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
resolved = false;
|
|
232
|
+
catch {
|
|
233
|
+
// Navigation did not reach 'load' state within timeout; continue and verify URL
|
|
241
234
|
}
|
|
242
235
|
}
|
|
236
|
+
if (!attemptOk) {
|
|
237
|
+
const raw = action.lastError?.message || 'attempt failed';
|
|
238
|
+
const firstMeaningful = raw.split('\n').find((l) => l.trim() && !l.trim().startsWith('at ')) || raw;
|
|
239
|
+
const shortErr = firstMeaningful.replace(/\s+/g, ' ').trim().slice(0, 220);
|
|
240
|
+
batchFailures.push({ code: codeBlock, error: shortErr });
|
|
241
|
+
}
|
|
242
|
+
if (expectedUrl) {
|
|
243
|
+
if (page) {
|
|
244
|
+
try {
|
|
245
|
+
await page.waitForURL((url) => normalizeUrl(url.pathname) === normalizeUrl(expectedUrl), { timeout: 5000 });
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
// URL did not transition to expectedUrl within timeout
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const freshState = await action.capturePageState();
|
|
252
|
+
const urlMatches = normalizeUrl(freshState.url || '') === normalizeUrl(expectedUrl);
|
|
253
|
+
const stateChanged = freshState.getStateHash() !== actionResult.getStateHash();
|
|
254
|
+
resolved = urlMatches && stateChanged;
|
|
255
|
+
if (!resolved && attemptOk) {
|
|
256
|
+
tag('warning').log(`URL verification failed: expected ${expectedUrl}, got ${freshState.url}`);
|
|
257
|
+
}
|
|
258
|
+
if (freshState.getStateHash() !== prevHash && (attemptOk || urlMatches)) {
|
|
259
|
+
progressBlocks.push(codeBlock);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
resolved = attemptOk;
|
|
264
|
+
if (attemptOk)
|
|
265
|
+
progressBlocks.push(codeBlock);
|
|
266
|
+
}
|
|
243
267
|
if (resolved) {
|
|
244
268
|
tag('success').log('Navigation resolved successfully');
|
|
245
|
-
|
|
269
|
+
let scenario = message.split('\n')[0];
|
|
270
|
+
if (expectedUrl) {
|
|
271
|
+
const fromPath = extractStatePath(actionResult.url || '');
|
|
272
|
+
const toPath = extractStatePath(expectedUrl);
|
|
273
|
+
scenario = `reach ${toPath} from ${fromPath}`;
|
|
274
|
+
}
|
|
275
|
+
const recipe = progressBlocks
|
|
276
|
+
.join('\n')
|
|
277
|
+
.split('\n')
|
|
278
|
+
.filter((line) => !/^\s*I\.amOnPage\s*\(/.test(line))
|
|
279
|
+
.join('\n')
|
|
280
|
+
.trim();
|
|
281
|
+
if (recipe) {
|
|
282
|
+
const body = `## FLOW: ${scenario}\n\n* ${scenario}\n\n\`\`\`js\n${recipe}\n\`\`\`\n\n---\n`;
|
|
283
|
+
this.experienceTracker.writeFlow(actionResult, body);
|
|
284
|
+
}
|
|
246
285
|
stop();
|
|
247
286
|
return;
|
|
248
287
|
}
|
|
@@ -256,9 +295,9 @@ class Navigator {
|
|
|
256
295
|
resolved = false;
|
|
257
296
|
},
|
|
258
297
|
});
|
|
259
|
-
if (!resolved &&
|
|
260
|
-
await
|
|
261
|
-
if (this.isOnExpectedPage(
|
|
298
|
+
if (!resolved && expectedUrl) {
|
|
299
|
+
await action.getActor().wait(1);
|
|
300
|
+
if (this.isOnExpectedPage(expectedUrl, action.stateManager)) {
|
|
262
301
|
resolved = true;
|
|
263
302
|
tag('success').log('Navigation resolved after delayed redirect');
|
|
264
303
|
}
|
|
@@ -267,12 +306,12 @@ class Navigator {
|
|
|
267
306
|
tag('error').log(`Navigation failed after ${totalAttempts} attempts`);
|
|
268
307
|
}
|
|
269
308
|
if (!resolved && isInteractive()) {
|
|
270
|
-
const userInput = await pause(`Navigator failed to resolve. Current: ${
|
|
309
|
+
const userInput = await pause(`Navigator failed to resolve. Current: ${action.stateManager.getCurrentState()?.url}\n` + `Target: ${expectedUrl ?? '(none)'}\nEnter CodeceptJS commands (or press Enter to skip):`);
|
|
271
310
|
if (userInput?.trim()) {
|
|
272
|
-
resolved = await
|
|
273
|
-
if (resolved &&
|
|
274
|
-
await
|
|
275
|
-
if (!this.isOnExpectedPage(
|
|
311
|
+
resolved = await action.attempt(userInput, message);
|
|
312
|
+
if (resolved && expectedUrl) {
|
|
313
|
+
await action.getActor().wait(1);
|
|
314
|
+
if (!this.isOnExpectedPage(expectedUrl, action.stateManager)) {
|
|
276
315
|
resolved = false;
|
|
277
316
|
}
|
|
278
317
|
}
|
|
@@ -280,6 +319,23 @@ class Navigator {
|
|
|
280
319
|
}
|
|
281
320
|
return resolved;
|
|
282
321
|
}
|
|
322
|
+
buildExperienceTools() {
|
|
323
|
+
const stateManager = this.explorer.getStateManager();
|
|
324
|
+
const getState = () => {
|
|
325
|
+
const s = stateManager.getCurrentState();
|
|
326
|
+
return s ? ActionResult.fromState(s) : null;
|
|
327
|
+
};
|
|
328
|
+
const { learn_experience } = createAgentTools({
|
|
329
|
+
explorer: this.explorer,
|
|
330
|
+
researcher: null,
|
|
331
|
+
navigator: this,
|
|
332
|
+
experienceTracker: this.experienceTracker,
|
|
333
|
+
getState,
|
|
334
|
+
});
|
|
335
|
+
if (!learn_experience)
|
|
336
|
+
return undefined;
|
|
337
|
+
return { learn_experience };
|
|
338
|
+
}
|
|
283
339
|
async freeSail(opts, actionResult) {
|
|
284
340
|
const stateManager = this.explorer.getStateManager();
|
|
285
341
|
const state = stateManager.getCurrentState();
|
|
@@ -403,20 +459,13 @@ class Navigator {
|
|
|
403
459
|
${knowledgeContent}
|
|
404
460
|
</hint>`;
|
|
405
461
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
Here is the experience of interacting with the page.
|
|
414
|
-
Learn from it AND DO NOT REPEAT THE SAME MISTAKES.
|
|
415
|
-
If there was found successful solution to an issue, propose it as a first solution.
|
|
416
|
-
|
|
417
|
-
${experienceContent}
|
|
418
|
-
|
|
419
|
-
</experience>`;
|
|
462
|
+
if (!actionResult.isInsideIframe) {
|
|
463
|
+
const toc = this.experienceTracker.getExperienceTableOfContents(actionResult);
|
|
464
|
+
if (toc.length > 0) {
|
|
465
|
+
const totalSections = toc.reduce((sum, entry) => sum + entry.sections.length, 0);
|
|
466
|
+
tag('substep').log(`Found ${toc.length} experience ${pluralize(toc.length, 'file')} (${totalSections} sections) for: ${actionResult.url}`);
|
|
467
|
+
experience = renderExperienceToc(toc);
|
|
468
|
+
}
|
|
420
469
|
}
|
|
421
470
|
const prompt = dedent `
|
|
422
471
|
<message>
|
|
@@ -454,16 +503,18 @@ class Navigator {
|
|
|
454
503
|
tag('debug').log('Prompt:', prompt);
|
|
455
504
|
const conversation = this.provider.startConversation(this.systemPrompt, 'navigator');
|
|
456
505
|
conversation.addUserText(prompt);
|
|
506
|
+
const tools = this.buildExperienceTools();
|
|
457
507
|
let codeBlocks = [];
|
|
458
508
|
const successfulCodes = [];
|
|
509
|
+
const assertionSteps = [];
|
|
459
510
|
const action = this.explorer.createAction();
|
|
460
511
|
await loop(async ({ stop, iteration }) => {
|
|
461
512
|
if (codeBlocks.length === 0) {
|
|
462
|
-
const result = await this.provider.invokeConversation(conversation);
|
|
513
|
+
const result = await this.provider.invokeConversation(conversation, tools);
|
|
463
514
|
if (!result)
|
|
464
515
|
return;
|
|
465
516
|
const aiResponse = result?.response?.text;
|
|
466
|
-
debugLog('Received AI response:', aiResponse
|
|
517
|
+
debugLog('Received AI response:', aiResponse?.length ?? 0, 'characters');
|
|
467
518
|
tag('step').log('Verifying assertion...');
|
|
468
519
|
codeBlocks = extractCodeBlocks(aiResponse ?? '');
|
|
469
520
|
}
|
|
@@ -475,10 +526,12 @@ class Navigator {
|
|
|
475
526
|
stop();
|
|
476
527
|
return;
|
|
477
528
|
}
|
|
529
|
+
await this.explorer.switchToMainFrame();
|
|
478
530
|
const verified = await action.attempt(codeBlock, message, false);
|
|
479
531
|
if (verified) {
|
|
480
532
|
tag('success').log('Verification passed');
|
|
481
533
|
successfulCodes.push(codeBlock);
|
|
534
|
+
assertionSteps.push(...action.assertionSteps);
|
|
482
535
|
}
|
|
483
536
|
}, {
|
|
484
537
|
maxAttempts: this.MAX_ATTEMPTS,
|
|
@@ -493,7 +546,7 @@ class Navigator {
|
|
|
493
546
|
const verified = totalAttempted <= 1 ? successfulCodes.length > 0 : successfulCodes.length > totalAttempted / 2;
|
|
494
547
|
actionResult.addVerification(message, verified);
|
|
495
548
|
this.explorer.getStateManager().updateState(actionResult);
|
|
496
|
-
return { verified, successfulCodes, totalAttempted };
|
|
549
|
+
return { verified, successfulCodes, assertionSteps, totalAttempted };
|
|
497
550
|
}
|
|
498
551
|
}
|
|
499
552
|
export { Navigator };
|