explorbot 0.2.0 → 0.2.1
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 +21 -5
- package/bin/explorbot-cli.ts +29 -7
- package/boat/api-tester/src/config.ts +45 -3
- package/boat/doc-collector/src/ai/documentarian.ts +5 -2
- package/boat/doc-collector/src/ai/tools.ts +12 -11
- package/boat/doc-collector/src/cli.ts +1 -6
- package/boat/doc-collector/src/docbot.ts +3 -3
- package/boat/doc-collector/src/screenshots.ts +2 -2
- package/dist/bin/explorbot-cli.js +26 -6
- package/dist/boat/api-tester/src/config.js +43 -4
- package/dist/boat/doc-collector/src/ai/documentarian.js +4 -2
- package/dist/boat/doc-collector/src/ai/tools.js +11 -11
- package/dist/boat/doc-collector/src/cli.js +1 -5
- package/dist/boat/doc-collector/src/docbot.js +3 -3
- package/dist/boat/doc-collector/src/screenshots.js +2 -2
- package/dist/models.json +30 -0
- package/dist/package.json +5 -2
- package/dist/src/action.d.ts +11 -3
- package/dist/src/action.js +22 -5
- package/dist/src/ai/agent.d.ts +17 -0
- package/dist/src/ai/captain/idle-mode.js +1 -1
- package/dist/src/ai/captain/test-mode.js +1 -1
- package/dist/src/ai/captain/web-mode.js +18 -22
- package/dist/src/ai/captain.js +13 -26
- package/dist/src/ai/driller.d.ts +2 -13
- package/dist/src/ai/driller.js +23 -38
- package/dist/src/ai/historian/codeceptjs.js +1 -1
- package/dist/src/ai/historian/playwright.d.ts +3 -2
- package/dist/src/ai/historian/playwright.js +1 -1
- package/dist/src/ai/historian/screencast.d.ts +2 -1
- package/dist/src/ai/historian/screencast.js +2 -2
- package/dist/src/ai/historian.d.ts +5 -4
- package/dist/src/ai/navigator.d.ts +6 -2
- package/dist/src/ai/navigator.js +26 -22
- package/dist/src/ai/pilot.d.ts +13 -6
- package/dist/src/ai/pilot.js +49 -44
- package/dist/src/ai/planner.d.ts +3 -2
- package/dist/src/ai/planner.js +19 -17
- package/dist/src/ai/provider.js +0 -1
- package/dist/src/ai/quartermaster.d.ts +3 -2
- package/dist/src/ai/quartermaster.js +4 -4
- package/dist/src/ai/rerunner.d.ts +2 -11
- package/dist/src/ai/rerunner.js +9 -22
- package/dist/src/ai/researcher/coordinates.js +4 -3
- package/dist/src/ai/researcher/deep-analysis.d.ts +2 -0
- package/dist/src/ai/researcher/deep-analysis.js +6 -6
- package/dist/src/ai/researcher/locators.d.ts +9 -2
- package/dist/src/ai/researcher/locators.js +65 -8
- package/dist/src/ai/researcher/sections.d.ts +2 -0
- package/dist/src/ai/researcher/sections.js +2 -2
- package/dist/src/ai/researcher.js +29 -54
- package/dist/src/ai/task-agent.d.ts +18 -5
- package/dist/src/ai/task-agent.js +35 -6
- package/dist/src/ai/tester.d.ts +6 -12
- package/dist/src/ai/tester.js +37 -51
- package/dist/src/ai/tools.d.ts +11 -10
- package/dist/src/ai/tools.js +24 -36
- package/dist/src/command-handler.js +1 -1
- package/dist/src/commands/context-aria-command.js +1 -1
- package/dist/src/commands/context-command.js +7 -5
- package/dist/src/commands/context-data-command.js +2 -2
- package/dist/src/commands/context-experience-command.js +2 -2
- package/dist/src/commands/context-html-command.js +2 -2
- package/dist/src/commands/context-knowledge-command.js +1 -1
- package/dist/src/commands/drill-command.js +1 -1
- package/dist/src/commands/explore-command.js +4 -3
- package/dist/src/commands/freesail-command.js +2 -2
- package/dist/src/commands/learn-command.js +4 -4
- package/dist/src/commands/path-command.js +1 -1
- package/dist/src/commands/research-command.js +1 -1
- package/dist/src/commands/test-command.js +1 -1
- package/dist/src/components/App.js +1 -1
- package/dist/src/config.d.ts +17 -0
- package/dist/src/config.js +144 -10
- package/dist/src/experience-tracker.js +1 -1
- package/dist/src/explorbot.d.ts +17 -8
- package/dist/src/explorbot.js +74 -36
- package/dist/src/explorer.d.ts +66 -91
- package/dist/src/explorer.js +326 -567
- package/dist/src/state-manager.d.ts +6 -1
- package/dist/src/state-manager.js +1 -0
- package/dist/src/utils/hooks-runner.js +2 -4
- package/dist/src/utils/test-files.js +1 -1
- package/dist/src/utils/web-annotate.d.ts +5 -0
- package/dist/src/utils/web-annotate.js +58 -0
- package/dist/src/utils/web-eidx.d.ts +2 -0
- package/dist/src/utils/web-eidx.js +20 -0
- package/dist/src/utils/web-element.d.ts +1 -0
- package/dist/src/utils/web-element.js +29 -0
- package/models.json +30 -0
- package/package.json +5 -2
- package/src/action.ts +25 -5
- package/src/ai/agent.ts +20 -0
- package/src/ai/captain/idle-mode.ts +1 -1
- package/src/ai/captain/test-mode.ts +1 -1
- package/src/ai/captain/web-mode.ts +18 -23
- package/src/ai/captain.ts +12 -25
- package/src/ai/driller.ts +50 -67
- package/src/ai/historian/codeceptjs.ts +1 -1
- package/src/ai/historian/playwright.ts +4 -3
- package/src/ai/historian/screencast.ts +4 -3
- package/src/ai/historian.ts +3 -2
- package/src/ai/navigator.ts +30 -25
- package/src/ai/pilot.ts +55 -47
- package/src/ai/planner.ts +22 -20
- package/src/ai/provider.ts +0 -1
- package/src/ai/quartermaster.ts +5 -4
- package/src/ai/rerunner.ts +10 -29
- package/src/ai/researcher/coordinates.ts +4 -3
- package/src/ai/researcher/deep-analysis.ts +8 -6
- package/src/ai/researcher/locators.ts +66 -9
- package/src/ai/researcher/sections.ts +4 -2
- package/src/ai/researcher.ts +32 -61
- package/src/ai/task-agent.ts +45 -11
- package/src/ai/tester.ts +40 -57
- package/src/ai/tools.ts +32 -53
- package/src/command-handler.ts +1 -1
- package/src/commands/context-aria-command.ts +1 -1
- package/src/commands/context-command.ts +7 -5
- package/src/commands/context-data-command.ts +2 -2
- package/src/commands/context-experience-command.ts +2 -2
- package/src/commands/context-html-command.ts +2 -2
- package/src/commands/context-knowledge-command.ts +1 -1
- package/src/commands/drill-command.ts +1 -1
- package/src/commands/explore-command.ts +4 -3
- package/src/commands/freesail-command.ts +2 -2
- package/src/commands/learn-command.ts +4 -4
- package/src/commands/path-command.ts +1 -1
- package/src/commands/research-command.ts +1 -1
- package/src/commands/test-command.ts +1 -1
- package/src/components/App.tsx +1 -1
- package/src/config.ts +173 -11
- package/src/experience-tracker.ts +1 -1
- package/src/explorbot.ts +78 -39
- package/src/explorer.ts +389 -628
- package/src/state-manager.ts +7 -1
- package/src/utils/hooks-runner.ts +2 -4
- package/src/utils/test-files.ts +1 -1
- package/src/utils/web-annotate.ts +64 -0
- package/src/utils/web-eidx.ts +21 -0
- package/src/utils/web-element.ts +30 -0
|
@@ -4,10 +4,6 @@ import { Command } from 'commander';
|
|
|
4
4
|
import { setPreserveConsoleLogs } from "../../../src/utils/logger.js";
|
|
5
5
|
import { DocBot } from "./docbot.js";
|
|
6
6
|
function buildOptions(options) {
|
|
7
|
-
let session = options.session;
|
|
8
|
-
if (options.session === true) {
|
|
9
|
-
session = 'output/session.json';
|
|
10
|
-
}
|
|
11
7
|
return {
|
|
12
8
|
verbose: options.verbose || options.debug,
|
|
13
9
|
config: options.config,
|
|
@@ -15,7 +11,7 @@ function buildOptions(options) {
|
|
|
15
11
|
show: options.show,
|
|
16
12
|
headless: options.headless,
|
|
17
13
|
incognito: options.incognito,
|
|
18
|
-
session,
|
|
14
|
+
session: options.session,
|
|
19
15
|
docsConfig: options.docsConfig,
|
|
20
16
|
};
|
|
21
17
|
}
|
|
@@ -7,10 +7,10 @@ import { sanitizeFilename } from "../../../src/utils/strings.js";
|
|
|
7
7
|
import { Documentarian } from "./ai/documentarian.js";
|
|
8
8
|
import { DocbotConfigParser } from "./config.js";
|
|
9
9
|
import { renderPageDocumentation, renderSpecIndex } from "./docs-renderer.js";
|
|
10
|
-
import { renderMermaidBody } from "./state-diagram.js";
|
|
11
10
|
import { getDocPageKey, shouldCrawlDocPath } from "./path-filter.js";
|
|
12
11
|
import { extractResearchNavigationTargets } from "./research-navigation.js";
|
|
13
12
|
import { captureDocumentationScreenshots, captureInteractionScreenshot } from "./screenshots.js";
|
|
13
|
+
import { renderMermaidBody } from "./state-diagram.js";
|
|
14
14
|
class DocBot {
|
|
15
15
|
explorBot;
|
|
16
16
|
configParser;
|
|
@@ -39,7 +39,7 @@ class DocBot {
|
|
|
39
39
|
config: this.options.docsConfig,
|
|
40
40
|
path: this.options.path,
|
|
41
41
|
});
|
|
42
|
-
this.documentarian = new Documentarian(this.explorBot.getProvider(), this.config, this.explorBot.getExplorer());
|
|
42
|
+
this.documentarian = new Documentarian(this.explorBot.getProvider(), this.config, this.explorBot.getExplorer(), this.explorBot.stateManager());
|
|
43
43
|
this.ensureDirectory(this.configParser.getOutputDir());
|
|
44
44
|
this.ensureDirectory(this.getPagesDir());
|
|
45
45
|
}
|
|
@@ -66,7 +66,7 @@ class DocBot {
|
|
|
66
66
|
if (documented.has(targetKey)) {
|
|
67
67
|
continue;
|
|
68
68
|
}
|
|
69
|
-
const stateManager = this.explorBot.
|
|
69
|
+
const stateManager = this.explorBot.stateManager();
|
|
70
70
|
if (stateManager.hasVisitedState(target)) {
|
|
71
71
|
continue;
|
|
72
72
|
}
|
|
@@ -5,7 +5,7 @@ import { detectFocusArea } from "../../../src/utils/aria.js";
|
|
|
5
5
|
import { safeFilename, sanitizeFilename } from "../../../src/utils/strings.js";
|
|
6
6
|
const DEFAULT_MAX_SECTION_SCREENSHOTS = 8;
|
|
7
7
|
export async function captureDocumentationScreenshots(explorer, state, research, options) {
|
|
8
|
-
const page = explorer.
|
|
8
|
+
const page = explorer.page;
|
|
9
9
|
if (!page) {
|
|
10
10
|
return [];
|
|
11
11
|
}
|
|
@@ -48,7 +48,7 @@ export function getScreenshotSections(research) {
|
|
|
48
48
|
return sections;
|
|
49
49
|
}
|
|
50
50
|
export async function captureInteractionScreenshot(explorer, state, transition, options) {
|
|
51
|
-
const page = explorer.
|
|
51
|
+
const page = explorer.page;
|
|
52
52
|
if (!page) {
|
|
53
53
|
return null;
|
|
54
54
|
}
|
package/dist/models.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openrouter": {
|
|
3
|
+
"model": "openai/gpt-oss-20b:nitro",
|
|
4
|
+
"visionModel": "google/gemma-4-31b-it:nitro",
|
|
5
|
+
"agenticModel": "google/gemma-4-31b-it:nitro"
|
|
6
|
+
},
|
|
7
|
+
"groq": {
|
|
8
|
+
"model": "openai/gpt-oss-20b",
|
|
9
|
+
"visionModel": "qwen/qwen3.6-27b",
|
|
10
|
+
"agenticModel": "qwen/qwen3.6-27b"
|
|
11
|
+
},
|
|
12
|
+
"openai": {
|
|
13
|
+
"model": "gpt-5.4-nano",
|
|
14
|
+
"visionModel": "gpt-5.4-nano",
|
|
15
|
+
"agenticModel": "gpt-5.6-luna"
|
|
16
|
+
},
|
|
17
|
+
"anthropic": {
|
|
18
|
+
"agenticModel": "claude-haiku-4-5-20251001"
|
|
19
|
+
},
|
|
20
|
+
"mistral": {
|
|
21
|
+
"model": "mistral-small-latest",
|
|
22
|
+
"visionModel": "mistral-small-latest",
|
|
23
|
+
"agenticModel": "mistral-large-latest"
|
|
24
|
+
},
|
|
25
|
+
"google": {
|
|
26
|
+
"model": "gemini-3.1-flash-lite",
|
|
27
|
+
"visionModel": "gemini-3.1-flash-lite",
|
|
28
|
+
"agenticModel": "gemini-3.5-flash"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "explorbot",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "CLI app built with React Ink, CodeceptJS, and Playwright",
|
|
5
5
|
"license": "Elastic-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"boat/doc-collector/bin/**/*.ts",
|
|
27
27
|
"boat/doc-collector/package.json",
|
|
28
28
|
"rules/",
|
|
29
|
-
"assets/sample-files/"
|
|
29
|
+
"assets/sample-files/",
|
|
30
|
+
"models.json"
|
|
30
31
|
],
|
|
31
32
|
"scripts": {
|
|
32
33
|
"build": "bun run build:bin",
|
|
@@ -68,7 +69,9 @@
|
|
|
68
69
|
"author": "",
|
|
69
70
|
"dependencies": {
|
|
70
71
|
"@ai-sdk/anthropic": "^4.0",
|
|
72
|
+
"@ai-sdk/google": "^4.0.18",
|
|
71
73
|
"@ai-sdk/groq": "^4.0",
|
|
74
|
+
"@ai-sdk/mistral": "^4.0.13",
|
|
72
75
|
"@ai-sdk/openai": "^4.0",
|
|
73
76
|
"@ai-sdk/otel": "^1.0.2",
|
|
74
77
|
"@axe-core/playwright": "^4.11.0",
|
package/dist/src/action.d.ts
CHANGED
|
@@ -16,10 +16,16 @@ declare class Action {
|
|
|
16
16
|
args: any[];
|
|
17
17
|
}>;
|
|
18
18
|
recorder?: PlaywrightRecorder;
|
|
19
|
+
recovery: RecoveryRunner;
|
|
19
20
|
mainDocumentStatus: number | undefined;
|
|
20
|
-
constructor(actor: CodeceptJS.I, stateManager: StateManager, recorder?: PlaywrightRecorder);
|
|
21
|
+
constructor(actor: CodeceptJS.I, stateManager: StateManager, recorder?: PlaywrightRecorder, recovery?: RecoveryRunner);
|
|
21
22
|
saveScreenshot(): Promise<string | undefined>;
|
|
22
|
-
capturePageState(
|
|
23
|
+
capturePageState(opts?: {
|
|
24
|
+
includeScreenshot?: boolean;
|
|
25
|
+
codeBlock?: string;
|
|
26
|
+
}): Promise<ActionResult>;
|
|
27
|
+
execute(code: string): Promise<Action>;
|
|
28
|
+
captureOnce({ includeScreenshot, codeBlock }?: {
|
|
23
29
|
includeScreenshot?: boolean;
|
|
24
30
|
codeBlock?: string;
|
|
25
31
|
}): Promise<ActionResult>;
|
|
@@ -34,10 +40,12 @@ declare class Action {
|
|
|
34
40
|
id?: string;
|
|
35
41
|
}>>;
|
|
36
42
|
captureBrowserLogs(): Promise<any[]>;
|
|
37
|
-
|
|
43
|
+
executeOnce(code: string): Promise<Action>;
|
|
38
44
|
attempt(codeBlock: string, originalMessage?: string): Promise<boolean>;
|
|
45
|
+
exitIframe(): Promise<void>;
|
|
39
46
|
getActor(): CodeceptJS.I;
|
|
40
47
|
getActionResult(): ActionResult | null;
|
|
41
48
|
waitForPageReadiness(page: any): Promise<void>;
|
|
42
49
|
}
|
|
43
50
|
export default Action;
|
|
51
|
+
export type RecoveryRunner = <T>(fn: () => Promise<T>) => Promise<T>;
|
package/dist/src/action.js
CHANGED
|
@@ -11,8 +11,8 @@ import { browserErrorMessage, isFatalBrowserError, isNavigationTransitionError }
|
|
|
11
11
|
import { captureHtmlForSnapshot, htmlCombinedSnapshot, minifyHtml } from './utils/html.js';
|
|
12
12
|
import { createDebug, setStepSpanParent, tag } from './utils/logger.js';
|
|
13
13
|
import { sleep, waitForPageReadiness } from "./utils/page-readiness.js";
|
|
14
|
-
import { codeceptJSSandbox, hasPlaywrightCommands, playwrightSandbox, sanitizeCodeBlock } from "./utils/web-sandbox.js";
|
|
15
14
|
import { safeFilename } from "./utils/strings.js";
|
|
15
|
+
import { codeceptJSSandbox, hasPlaywrightCommands, playwrightSandbox, sanitizeCodeBlock } from "./utils/web-sandbox.js";
|
|
16
16
|
const debugLog = createDebug('explorbot:action');
|
|
17
17
|
const CAPTURE_NAVIGATION_TRANSITION_ATTEMPTS = 3;
|
|
18
18
|
class Action {
|
|
@@ -27,13 +27,15 @@ class Action {
|
|
|
27
27
|
playwrightGroupId = null;
|
|
28
28
|
assertionSteps = [];
|
|
29
29
|
recorder;
|
|
30
|
+
recovery;
|
|
30
31
|
mainDocumentStatus = undefined;
|
|
31
|
-
constructor(actor, stateManager, recorder) {
|
|
32
|
+
constructor(actor, stateManager, recorder, recovery) {
|
|
32
33
|
this.actor = actor;
|
|
33
34
|
this.stateManager = stateManager;
|
|
34
35
|
this.config = ConfigParser.getInstance().getConfig();
|
|
35
36
|
this.playwrightHelper = container.helpers('Playwright');
|
|
36
37
|
this.recorder = recorder;
|
|
38
|
+
this.recovery = recovery || ((fn) => fn());
|
|
37
39
|
}
|
|
38
40
|
async saveScreenshot() {
|
|
39
41
|
const currentState = this.stateManager.getCurrentState();
|
|
@@ -52,7 +54,13 @@ class Action {
|
|
|
52
54
|
return undefined;
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
|
-
async capturePageState(
|
|
57
|
+
async capturePageState(opts = {}) {
|
|
58
|
+
return this.recovery(() => this.captureOnce(opts));
|
|
59
|
+
}
|
|
60
|
+
async execute(code) {
|
|
61
|
+
return this.recovery(() => this.executeOnce(code));
|
|
62
|
+
}
|
|
63
|
+
async captureOnce({ includeScreenshot = false, codeBlock } = {}) {
|
|
56
64
|
try {
|
|
57
65
|
const currentState = this.stateManager.getCurrentState();
|
|
58
66
|
const stateHash = currentState?.hash || 'screenshot';
|
|
@@ -243,7 +251,7 @@ class Action {
|
|
|
243
251
|
return [];
|
|
244
252
|
}
|
|
245
253
|
}
|
|
246
|
-
async
|
|
254
|
+
async executeOnce(code) {
|
|
247
255
|
let error = null;
|
|
248
256
|
setActivity('🔎 Browsing...', 'action');
|
|
249
257
|
let codeString = code.replace(/^\(I\) => /, '').trim();
|
|
@@ -277,7 +285,7 @@ class Action {
|
|
|
277
285
|
if (executedSteps.length > 0) {
|
|
278
286
|
codeString = executedSteps.join('\n');
|
|
279
287
|
}
|
|
280
|
-
const pageState = await this.
|
|
288
|
+
const pageState = await this.captureOnce({ codeBlock: codeString });
|
|
281
289
|
this.actionResult = pageState;
|
|
282
290
|
this.assertionSteps = assertionSteps;
|
|
283
291
|
}
|
|
@@ -305,6 +313,7 @@ class Action {
|
|
|
305
313
|
return this;
|
|
306
314
|
}
|
|
307
315
|
async attempt(codeBlock, originalMessage) {
|
|
316
|
+
const wasInFrame = !!this.playwrightHelper.frame;
|
|
308
317
|
try {
|
|
309
318
|
debugLog('Resolution attempt...');
|
|
310
319
|
setActivity('🦾 Acting in browser...', 'action');
|
|
@@ -319,10 +328,18 @@ class Action {
|
|
|
319
328
|
this.lastError = error;
|
|
320
329
|
if (isFatalBrowserError(error))
|
|
321
330
|
throw error;
|
|
331
|
+
if (!wasInFrame)
|
|
332
|
+
await this.exitIframe().catch(() => { });
|
|
322
333
|
debugLog(`Attempt failed: ${codeBlock}: ${errorToString(error) || this.lastError?.toString()}`);
|
|
323
334
|
return false;
|
|
324
335
|
}
|
|
325
336
|
}
|
|
337
|
+
async exitIframe() {
|
|
338
|
+
if (!this.playwrightHelper.frame)
|
|
339
|
+
return;
|
|
340
|
+
debugLog('Switching to main frame');
|
|
341
|
+
await this.playwrightHelper.switchTo();
|
|
342
|
+
}
|
|
326
343
|
getActor() {
|
|
327
344
|
return this.actor;
|
|
328
345
|
}
|
package/dist/src/ai/agent.d.ts
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
import type { RequestStore } from '../api/request-store.js';
|
|
2
|
+
import type { ExplorbotConfig } from '../config.js';
|
|
3
|
+
import type Explorer from '../explorer.js';
|
|
4
|
+
import type { KnowledgeTracker } from '../knowledge-tracker.js';
|
|
5
|
+
import type { PlaywrightRecorder } from '../playwright-recorder.js';
|
|
6
|
+
import type { StateManager } from '../state-manager.js';
|
|
7
|
+
import type { AIProvider } from './provider.js';
|
|
1
8
|
export interface Agent {
|
|
2
9
|
emoji?: string;
|
|
3
10
|
}
|
|
11
|
+
export interface AgentDeps {
|
|
12
|
+
explorer: Explorer;
|
|
13
|
+
ai: AIProvider;
|
|
14
|
+
config: ExplorbotConfig;
|
|
15
|
+
stateManager: StateManager;
|
|
16
|
+
knowledgeTracker: KnowledgeTracker;
|
|
17
|
+
requestStore: RequestStore;
|
|
18
|
+
playwrightRecorder: PlaywrightRecorder;
|
|
19
|
+
}
|
|
20
|
+
export type ToolDeps = Pick<AgentDeps, 'explorer' | 'stateManager' | 'ai'>;
|
|
@@ -60,7 +60,7 @@ export function WithIdleMode(Base) {
|
|
|
60
60
|
if (!action || action === 'replace') {
|
|
61
61
|
plan.tests.length = 0;
|
|
62
62
|
}
|
|
63
|
-
const currentUrl = ctx.explorBot.
|
|
63
|
+
const currentUrl = ctx.explorBot.stateManager().getCurrentState()?.url || '';
|
|
64
64
|
for (const testInput of tests) {
|
|
65
65
|
const priority = testInput.priority || 'normal';
|
|
66
66
|
const expected = testInput.expected?.length ? testInput.expected : [];
|
|
@@ -144,7 +144,7 @@ export function WithTestMode(Base) {
|
|
|
144
144
|
states = t.states;
|
|
145
145
|
}
|
|
146
146
|
else {
|
|
147
|
-
const stateManager = ctx.explorBot.
|
|
147
|
+
const stateManager = ctx.explorBot.stateManager();
|
|
148
148
|
const history = stateManager.getStateHistory();
|
|
149
149
|
const seen = new Set();
|
|
150
150
|
states = history
|
|
@@ -8,9 +8,10 @@ export function WithWebMode(Base) {
|
|
|
8
8
|
return class extends Base {
|
|
9
9
|
webModeTools(ctx) {
|
|
10
10
|
const explorer = ctx.explorBot.getExplorer();
|
|
11
|
-
const
|
|
11
|
+
const toolDeps = { explorer, stateManager: ctx.explorBot.stateManager(), ai: ctx.explorBot.getProvider() };
|
|
12
|
+
const codeceptTools = createCodeceptJSTools(toolDeps, ctx.task);
|
|
12
13
|
const agentTools = createAgentTools({
|
|
13
|
-
|
|
14
|
+
...toolDeps,
|
|
14
15
|
researcher: ctx.explorBot.agentResearcher(),
|
|
15
16
|
navigator: ctx.explorBot.agentNavigator(),
|
|
16
17
|
});
|
|
@@ -25,7 +26,7 @@ export function WithWebMode(Base) {
|
|
|
25
26
|
try {
|
|
26
27
|
debugLog('navigate', destination);
|
|
27
28
|
await ctx.explorBot.agentNavigator().visit(destination);
|
|
28
|
-
const stateManager = ctx.explorBot.
|
|
29
|
+
const stateManager = ctx.explorBot.stateManager();
|
|
29
30
|
const state = stateManager.getCurrentState();
|
|
30
31
|
return { success: true, url: state?.url, title: state?.title };
|
|
31
32
|
}
|
|
@@ -43,43 +44,38 @@ export function WithWebMode(Base) {
|
|
|
43
44
|
- closeTabs: Close all browser tabs except the current one
|
|
44
45
|
- reload: Reload the current page
|
|
45
46
|
- screenshot: Take a screenshot of current page
|
|
46
|
-
- recover: Recover from a closed/crashed page
|
|
47
|
-
-
|
|
48
|
-
- openFreshTab: Open a fresh tab in the current browser context
|
|
47
|
+
- recover: Recover from a closed/crashed page, escalating to a full browser restart when needed
|
|
48
|
+
- openTab: Open a fresh tab in the current browser context
|
|
49
49
|
`,
|
|
50
50
|
inputSchema: z.object({
|
|
51
|
-
action: z.enum(['status', 'evaluate', 'closeTabs', 'reload', 'screenshot', 'recover', '
|
|
51
|
+
action: z.enum(['status', 'evaluate', 'closeTabs', 'reload', 'screenshot', 'recover', 'openTab']).describe('Browser action to perform'),
|
|
52
52
|
code: z.string().optional().describe('JavaScript code for evaluate action'),
|
|
53
53
|
}),
|
|
54
54
|
execute: async ({ action, code }) => {
|
|
55
55
|
const explorer = ctx.explorBot.getExplorer();
|
|
56
56
|
if (action === 'status') {
|
|
57
|
-
const page = explorer.
|
|
57
|
+
const page = explorer.page;
|
|
58
58
|
const pages = page?.context?.().pages?.() || [];
|
|
59
59
|
return {
|
|
60
60
|
success: true,
|
|
61
61
|
hasPage: !!page,
|
|
62
|
-
isClosed: page
|
|
63
|
-
url: page
|
|
64
|
-
title: page
|
|
62
|
+
isClosed: !page,
|
|
63
|
+
url: page ? await page.url() : null,
|
|
64
|
+
title: page ? await page.title().catch(() => null) : null,
|
|
65
65
|
tabs: pages.length,
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
if (action === 'recover') {
|
|
69
|
-
const
|
|
70
|
-
return { success:
|
|
69
|
+
const { ok } = await explorer.recover();
|
|
70
|
+
return { success: ok, message: ok ? 'Browser page recovered' : 'Browser recovery failed' };
|
|
71
71
|
}
|
|
72
|
-
if (action === '
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
if (action === 'openFreshTab') {
|
|
77
|
-
await ctx.explorBot.openFreshTab();
|
|
78
|
-
const state = explorer.getStateManager().getCurrentState();
|
|
72
|
+
if (action === 'openTab') {
|
|
73
|
+
await ctx.explorBot.openTab();
|
|
74
|
+
const state = ctx.explorBot.stateManager().getCurrentState();
|
|
79
75
|
return { success: true, url: state?.url, title: state?.title };
|
|
80
76
|
}
|
|
81
|
-
const page = explorer.
|
|
82
|
-
if (!page
|
|
77
|
+
const page = explorer.page;
|
|
78
|
+
if (!page)
|
|
83
79
|
return { success: false, message: 'No browser page available. Try browser({ action: "recover" }) first.' };
|
|
84
80
|
if (action === 'evaluate') {
|
|
85
81
|
if (!code)
|
package/dist/src/ai/captain.js
CHANGED
|
@@ -37,7 +37,7 @@ export class Captain extends CaptainBase {
|
|
|
37
37
|
getHooksRunner() {
|
|
38
38
|
if (!this.hooksRunner) {
|
|
39
39
|
const explorer = this.explorBot.getExplorer();
|
|
40
|
-
this.hooksRunner = new HooksRunner(explorer,
|
|
40
|
+
this.hooksRunner = new HooksRunner(explorer, this.explorBot.getConfig());
|
|
41
41
|
}
|
|
42
42
|
return this.hooksRunner;
|
|
43
43
|
}
|
|
@@ -72,18 +72,17 @@ export class Captain extends CaptainBase {
|
|
|
72
72
|
getMode() {
|
|
73
73
|
const explorer = this.explorBot.getExplorer();
|
|
74
74
|
const activeTest = explorer.activeTest;
|
|
75
|
-
|
|
76
|
-
if (activeTest && (!page || page.isClosed?.()))
|
|
75
|
+
if (activeTest && !explorer.page)
|
|
77
76
|
return 'heal';
|
|
78
77
|
if (activeTest)
|
|
79
78
|
return 'test';
|
|
80
|
-
if (
|
|
79
|
+
if (this.explorBot.stateManager().getCurrentState())
|
|
81
80
|
return 'web';
|
|
82
81
|
return 'idle';
|
|
83
82
|
}
|
|
84
83
|
systemPrompt() {
|
|
85
84
|
const mode = this.getMode();
|
|
86
|
-
const currentUrl = this.explorBot.
|
|
85
|
+
const currentUrl = this.explorBot.stateManager().getCurrentState()?.url;
|
|
87
86
|
const customPrompt = this.explorBot.getProvider().getSystemPromptForAgent('captain', currentUrl);
|
|
88
87
|
return dedent `
|
|
89
88
|
<role>
|
|
@@ -127,7 +126,7 @@ export class Captain extends CaptainBase {
|
|
|
127
126
|
return this.conversation;
|
|
128
127
|
}
|
|
129
128
|
async getPageContext() {
|
|
130
|
-
const state = this.explorBot.
|
|
129
|
+
const state = this.explorBot.stateManager().getCurrentState();
|
|
131
130
|
if (!state) {
|
|
132
131
|
return 'No page loaded';
|
|
133
132
|
}
|
|
@@ -197,10 +196,9 @@ export class Captain extends CaptainBase {
|
|
|
197
196
|
`;
|
|
198
197
|
}
|
|
199
198
|
async reinjectContextIfNeeded(conversation, currentState) {
|
|
200
|
-
if (conversation.hasTag('
|
|
201
|
-
return
|
|
199
|
+
if (conversation.hasTag('page_aria', 5))
|
|
200
|
+
return;
|
|
202
201
|
const actionResult = ActionResult.fromState(currentState);
|
|
203
|
-
const html = await actionResult.combinedHtml();
|
|
204
202
|
const context = dedent `
|
|
205
203
|
Context:
|
|
206
204
|
|
|
@@ -212,13 +210,8 @@ export class Captain extends CaptainBase {
|
|
|
212
210
|
<page_aria>
|
|
213
211
|
${actionResult.getInteractiveARIA()}
|
|
214
212
|
</page_aria>
|
|
215
|
-
|
|
216
|
-
<page_html>
|
|
217
|
-
${html}
|
|
218
|
-
</page_html>
|
|
219
213
|
`;
|
|
220
214
|
conversation.addUserText(context);
|
|
221
|
-
return Promise.resolve();
|
|
222
215
|
}
|
|
223
216
|
coreTools(task, onDone) {
|
|
224
217
|
return {
|
|
@@ -248,15 +241,8 @@ export class Captain extends CaptainBase {
|
|
|
248
241
|
runCommand: tool({
|
|
249
242
|
description: dedent `
|
|
250
243
|
Execute a TUI command. Returns log output from command execution.
|
|
251
|
-
Use only when the user
|
|
252
|
-
|
|
253
|
-
Never run a slash command unless the user request itself starts with that slash command.
|
|
254
|
-
${this.commandDescriptions
|
|
255
|
-
.map((c) => {
|
|
256
|
-
const opts = c.options ? ` (${c.options})` : '';
|
|
257
|
-
return `${c.name} — ${c.description}${opts}`;
|
|
258
|
-
})
|
|
259
|
-
.join('\n')}
|
|
244
|
+
Use only when the user request itself starts with a slash command; pass it through verbatim.
|
|
245
|
+
Available commands: ${this.commandDescriptions.map((c) => c.name).join(', ')}
|
|
260
246
|
`,
|
|
261
247
|
inputSchema: z.object({
|
|
262
248
|
command: z.string().describe('Slash command to execute, e.g. "/research", "/plan authentication", "/test brave-fox123"'),
|
|
@@ -314,7 +300,7 @@ export class Captain extends CaptainBase {
|
|
|
314
300
|
recentToolSummary = execs.map((e) => `${e.toolName}: ${e.wasSuccessful ? 'ok' : 'fail'} ${truncateJson(e.input)}`).join('\n');
|
|
315
301
|
}
|
|
316
302
|
const pilotAnalysis = this.explorBot.agentPilot().getLastAnalysis() || '';
|
|
317
|
-
const currentUrl = this.explorBot.
|
|
303
|
+
const currentUrl = this.explorBot.stateManager().getCurrentState()?.url || '';
|
|
318
304
|
const schema = z.object({
|
|
319
305
|
action: z.enum(['inject', 'stop', 'pass', 'skip']),
|
|
320
306
|
message: z.string().describe('Message to pass to the tester or display to user'),
|
|
@@ -356,7 +342,7 @@ export class Captain extends CaptainBase {
|
|
|
356
342
|
}
|
|
357
343
|
async processExecutionError(error, activeTest) {
|
|
358
344
|
const explorer = this.explorBot.getExplorer();
|
|
359
|
-
const result = await explorer.
|
|
345
|
+
const result = await explorer.recover(error);
|
|
360
346
|
return {
|
|
361
347
|
...result,
|
|
362
348
|
message: result.recovered ? `${result.message}\nContinue the test "${activeTest.scenario}" from the restored page.` : result.message,
|
|
@@ -366,7 +352,7 @@ export class Captain extends CaptainBase {
|
|
|
366
352
|
return (this.recentToolCalls || []).some(hasBrowserCompletionEvidence);
|
|
367
353
|
}
|
|
368
354
|
async handle(input, options = {}) {
|
|
369
|
-
const stateManager = this.explorBot.
|
|
355
|
+
const stateManager = this.explorBot.stateManager();
|
|
370
356
|
const initialState = stateManager.getCurrentState();
|
|
371
357
|
const conversation = options.reset ? this.resetConversation() : this.ensureConversation();
|
|
372
358
|
let isDone = false;
|
|
@@ -426,6 +412,7 @@ export class Captain extends CaptainBase {
|
|
|
426
412
|
const result = await this.explorBot.getProvider().invokeConversation(conversation, tools, {
|
|
427
413
|
maxToolRoundtrips: 5,
|
|
428
414
|
toolChoice: 'auto',
|
|
415
|
+
stopWhen: () => isDone,
|
|
429
416
|
});
|
|
430
417
|
if (!result) {
|
|
431
418
|
stop();
|
package/dist/src/ai/driller.d.ts
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import { ActionResult } from '../action-result.js';
|
|
2
|
-
import type { ExperienceTracker } from '../experience-tracker.js';
|
|
3
|
-
import type Explorer from '../explorer.js';
|
|
4
|
-
import type { KnowledgeTracker } from '../knowledge-tracker.js';
|
|
5
2
|
import { Plan, Test } from '../test-plan.js';
|
|
6
|
-
import { HooksRunner } from '../utils/hooks-runner.js';
|
|
7
3
|
import { WebElement } from '../utils/web-element.js';
|
|
8
|
-
import type { Agent } from './agent.js';
|
|
4
|
+
import type { Agent, AgentDeps } from './agent.js';
|
|
9
5
|
import type { Navigator } from './navigator.js';
|
|
10
|
-
import type { Provider } from './provider.js';
|
|
11
6
|
import { TaskAgent } from './task-agent.js';
|
|
12
7
|
interface ComponentInfo {
|
|
13
8
|
id: string;
|
|
@@ -48,10 +43,7 @@ interface DrillOptions {
|
|
|
48
43
|
export declare class Driller extends TaskAgent implements Agent {
|
|
49
44
|
readonly ACTION_TOOLS: string[];
|
|
50
45
|
emoji: string;
|
|
51
|
-
explorer: Explorer;
|
|
52
|
-
provider: Provider;
|
|
53
46
|
navigator: Navigator;
|
|
54
|
-
hooksRunner: HooksRunner;
|
|
55
47
|
currentPlan?: Plan;
|
|
56
48
|
allResults: InteractionResult[];
|
|
57
49
|
verifiedAction: {
|
|
@@ -62,11 +54,8 @@ export declare class Driller extends TaskAgent implements Agent {
|
|
|
62
54
|
} | null;
|
|
63
55
|
pendingNestedContext: string | null;
|
|
64
56
|
MAX_COMPONENT_ITERATIONS: number;
|
|
65
|
-
constructor(
|
|
57
|
+
constructor(deps: AgentDeps, navigator: Navigator);
|
|
66
58
|
getNavigator(): Navigator;
|
|
67
|
-
getExperienceTracker(): ExperienceTracker;
|
|
68
|
-
getKnowledgeTracker(): KnowledgeTracker;
|
|
69
|
-
getProvider(): Provider;
|
|
70
59
|
getSystemMessage(component?: ComponentInfo): string;
|
|
71
60
|
drill(opts?: DrillOptions): Promise<Plan>;
|
|
72
61
|
captureAnnotatedState(): Promise<ActionResult>;
|