explorbot 0.1.32-beta.1 → 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 +35 -19
- package/bin/explorbot-cli.ts +33 -10
- package/boat/api-tester/src/config.ts +45 -3
- package/boat/doc-collector/src/ai/documentarian.ts +26 -28
- package/boat/doc-collector/src/ai/tools.ts +74 -16
- package/boat/doc-collector/src/cli.ts +1 -6
- package/boat/doc-collector/src/docbot.ts +30 -9
- package/boat/doc-collector/src/docs-renderer.ts +19 -24
- package/boat/doc-collector/src/screenshots.ts +34 -2
- package/boat/doc-collector/src/state-diagram.ts +281 -0
- package/dist/bin/explorbot-cli.js +29 -9
- package/dist/boat/api-tester/src/config.js +43 -4
- package/dist/boat/doc-collector/src/ai/documentarian.js +19 -7
- package/dist/boat/doc-collector/src/ai/tools.js +53 -16
- package/dist/boat/doc-collector/src/cli.js +1 -5
- package/dist/boat/doc-collector/src/docbot.js +27 -8
- package/dist/boat/doc-collector/src/docs-renderer.js +18 -3
- package/dist/boat/doc-collector/src/screenshots.js +30 -1
- package/dist/boat/doc-collector/src/state-diagram.js +200 -0
- package/dist/models.json +30 -0
- package/dist/package.json +23 -3
- package/dist/src/action-result.d.ts +8 -10
- package/dist/src/action-result.js +72 -164
- package/dist/src/action.d.ts +13 -12
- package/dist/src/action.js +37 -113
- 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 -29
- package/dist/src/ai/captain.d.ts +1 -3
- package/dist/src/ai/captain.js +17 -45
- package/dist/src/ai/conversation.d.ts +2 -5
- package/dist/src/ai/conversation.js +9 -28
- package/dist/src/ai/driller.d.ts +2 -17
- package/dist/src/ai/driller.js +26 -50
- package/dist/src/ai/experience-compactor.d.ts +2 -2
- package/dist/src/ai/experience-compactor.js +16 -33
- package/dist/src/ai/fisherman-tools.js +1 -1
- package/dist/src/ai/fisherman.d.ts +0 -1
- package/dist/src/ai/fisherman.js +0 -3
- package/dist/src/ai/historian/codeceptjs.d.ts +2 -0
- package/dist/src/ai/historian/codeceptjs.js +4 -3
- package/dist/src/ai/historian/playwright.d.ts +4 -1
- package/dist/src/ai/historian/playwright.js +4 -3
- package/dist/src/ai/historian/screencast.d.ts +2 -1
- package/dist/src/ai/historian/screencast.js +2 -2
- package/dist/src/ai/historian/utils.d.ts +0 -1
- package/dist/src/ai/historian/utils.js +0 -1
- package/dist/src/ai/historian.d.ts +10 -8
- package/dist/src/ai/historian.js +1 -5
- package/dist/src/ai/navigator.d.ts +8 -6
- package/dist/src/ai/navigator.js +34 -65
- package/dist/src/ai/pilot.d.ts +13 -8
- package/dist/src/ai/pilot.js +53 -59
- package/dist/src/ai/planner.d.ts +5 -4
- package/dist/src/ai/planner.js +66 -52
- package/dist/src/ai/provider.d.ts +4 -1
- package/dist/src/ai/provider.js +77 -137
- package/dist/src/ai/quartermaster.d.ts +22 -22
- package/dist/src/ai/quartermaster.js +8 -14
- package/dist/src/ai/rerunner.d.ts +3 -13
- package/dist/src/ai/rerunner.js +23 -44
- package/dist/src/ai/researcher/cache.js +11 -18
- 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 +8 -11
- package/dist/src/ai/researcher/locators.d.ts +9 -2
- package/dist/src/ai/researcher/locators.js +65 -8
- package/dist/src/ai/researcher/parser.js +1 -1
- package/dist/src/ai/researcher/research-result.d.ts +0 -1
- package/dist/src/ai/researcher/research-result.js +0 -19
- package/dist/src/ai/researcher/sections.d.ts +2 -0
- package/dist/src/ai/researcher/sections.js +7 -3
- package/dist/src/ai/researcher.js +40 -106
- package/dist/src/ai/rules.d.ts +1 -0
- package/dist/src/ai/rules.js +22 -15
- package/dist/src/ai/session-analyst.js +8 -5
- package/dist/src/ai/task-agent.d.ts +19 -6
- package/dist/src/ai/task-agent.js +37 -33
- package/dist/src/ai/tester.d.ts +6 -14
- package/dist/src/ai/tester.js +46 -111
- package/dist/src/ai/tools.d.ts +26 -14
- package/dist/src/ai/tools.js +138 -132
- package/dist/src/command-handler.js +1 -1
- package/dist/src/commands/compact-command.d.ts +1 -1
- package/dist/src/commands/compact-command.js +6 -6
- package/dist/src/commands/context-aria-command.js +1 -1
- package/dist/src/commands/context-command.js +8 -6
- 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 +2 -2
- package/dist/src/commands/drill-command.js +1 -1
- package/dist/src/commands/experience-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/knows-command.js +1 -1
- package/dist/src/commands/learn-command.js +5 -3
- 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/AddKnowledge.d.ts +2 -0
- package/dist/src/components/AddKnowledge.js +3 -7
- package/dist/src/components/App.js +3 -3
- package/dist/src/config.d.ts +19 -0
- package/dist/src/config.js +156 -10
- package/dist/src/experience-tracker.d.ts +11 -6
- package/dist/src/experience-tracker.js +82 -155
- package/dist/src/explorbot.d.ts +21 -13
- package/dist/src/explorbot.js +84 -80
- package/dist/src/explorer.d.ts +66 -102
- package/dist/src/explorer.js +332 -623
- package/dist/src/knowledge-tracker.d.ts +2 -2
- package/dist/src/knowledge-tracker.js +38 -45
- package/dist/src/state-manager.d.ts +10 -30
- package/dist/src/state-manager.js +11 -129
- package/dist/src/utils/aria.d.ts +6 -1
- package/dist/src/utils/aria.js +4 -1
- package/dist/src/utils/cache.d.ts +15 -0
- package/dist/src/utils/cache.js +34 -0
- package/dist/src/utils/context-formatter.d.ts +6 -0
- package/dist/src/utils/context-formatter.js +15 -27
- package/dist/src/utils/hooks-runner.js +2 -4
- package/dist/src/utils/html.d.ts +5 -0
- package/dist/src/utils/html.js +71 -0
- package/dist/src/utils/markdown-files.d.ts +10 -0
- package/dist/src/utils/markdown-files.js +21 -0
- package/dist/src/utils/markdown-query.d.ts +6 -0
- package/dist/src/utils/markdown-query.js +14 -1
- package/dist/src/utils/page-readiness.d.ts +1 -0
- package/dist/src/utils/page-readiness.js +1 -1
- package/dist/src/utils/secrets.js +2 -2
- package/dist/src/utils/strings.d.ts +2 -0
- package/dist/src/utils/strings.js +10 -0
- 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 +3 -1
- package/dist/src/utils/web-element.js +32 -0
- package/dist/src/utils/web-sandbox.d.ts +4 -0
- package/dist/src/utils/web-sandbox.js +37 -0
- package/models.json +30 -0
- package/package.json +23 -3
- package/src/action-result.ts +70 -173
- package/src/action.ts +38 -132
- 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 -30
- package/src/ai/captain.ts +17 -42
- package/src/ai/conversation.ts +9 -32
- package/src/ai/driller.ts +53 -83
- package/src/ai/experience-compactor.ts +16 -35
- package/src/ai/fisherman-tools.ts +1 -1
- package/src/ai/fisherman.ts +0 -4
- package/src/ai/historian/codeceptjs.ts +5 -3
- package/src/ai/historian/playwright.ts +7 -4
- package/src/ai/historian/screencast.ts +4 -3
- package/src/ai/historian/utils.ts +0 -1
- package/src/ai/historian.ts +6 -8
- package/src/ai/navigator.ts +39 -70
- package/src/ai/pilot.ts +59 -61
- package/src/ai/planner.ts +76 -62
- package/src/ai/provider.ts +85 -146
- package/src/ai/quartermaster.ts +39 -45
- package/src/ai/rerunner.ts +27 -50
- package/src/ai/researcher/cache.ts +10 -16
- package/src/ai/researcher/coordinates.ts +4 -3
- package/src/ai/researcher/deep-analysis.ts +10 -11
- package/src/ai/researcher/locators.ts +66 -9
- package/src/ai/researcher/parser.ts +1 -1
- package/src/ai/researcher/research-result.ts +0 -18
- package/src/ai/researcher/sections.ts +9 -3
- package/src/ai/researcher.ts +42 -119
- package/src/ai/rules.ts +24 -13
- package/src/ai/session-analyst.ts +8 -5
- package/src/ai/task-agent.ts +45 -38
- package/src/ai/tester.ts +51 -129
- package/src/ai/tools.ts +145 -153
- package/src/command-handler.ts +1 -1
- package/src/commands/compact-command.ts +7 -8
- package/src/commands/context-aria-command.ts +1 -1
- package/src/commands/context-command.ts +8 -6
- 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 +2 -2
- package/src/commands/drill-command.ts +1 -1
- package/src/commands/experience-command.ts +1 -1
- package/src/commands/explore-command.ts +4 -3
- package/src/commands/freesail-command.ts +2 -2
- package/src/commands/knows-command.ts +1 -1
- package/src/commands/learn-command.ts +5 -3
- 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/AddKnowledge.tsx +5 -7
- package/src/components/App.tsx +3 -3
- package/src/config.ts +185 -11
- package/src/experience-tracker.ts +89 -152
- package/src/explorbot.ts +88 -83
- package/src/explorer.ts +402 -693
- package/src/knowledge-tracker.ts +42 -49
- package/src/state-manager.ts +20 -155
- package/src/utils/aria.ts +11 -2
- package/src/utils/cache.ts +40 -0
- package/src/utils/context-formatter.ts +12 -15
- package/src/utils/hooks-runner.ts +2 -4
- package/src/utils/html.ts +79 -0
- package/src/utils/markdown-files.ts +30 -0
- package/src/utils/markdown-query.ts +15 -1
- package/src/utils/page-readiness.ts +1 -1
- package/src/utils/secrets.ts +2 -3
- package/src/utils/strings.ts +12 -0
- 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 +34 -0
- package/src/utils/web-sandbox.ts +43 -0
- package/dist/boat/api-tester/example/apibot.config.js +0 -30
package/dist/src/ai/tester.d.ts
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
import { ActionResult } from '../action-result.js';
|
|
2
|
-
import type {
|
|
3
|
-
import type
|
|
2
|
+
import type { RequestStore } from '../api/request-store.js';
|
|
3
|
+
import type { TestRun } from '../explorer.js';
|
|
4
4
|
import { type Test } from '../test-plan.js';
|
|
5
|
-
import {
|
|
6
|
-
import type { Agent } from './agent.js';
|
|
5
|
+
import type { Agent, AgentDeps } from './agent.js';
|
|
7
6
|
import type { Captain } from './captain.js';
|
|
8
7
|
import type { Conversation } from './conversation.js';
|
|
9
8
|
import { Navigator } from './navigator.js';
|
|
10
9
|
import type { Pilot } from './pilot.js';
|
|
11
|
-
import { Provider } from './provider.js';
|
|
12
10
|
import { Researcher } from './researcher.js';
|
|
13
11
|
import { TaskAgent } from './task-agent.js';
|
|
14
12
|
export declare class Tester extends TaskAgent implements Agent {
|
|
15
13
|
readonly ACTION_TOOLS: string[];
|
|
16
14
|
readonly SPECIAL_CONTEXT_ACTION_TOOLS: string[];
|
|
17
15
|
emoji: string;
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
requestStore: RequestStore;
|
|
17
|
+
testRun: TestRun | null;
|
|
20
18
|
currentConversation: Conversation | null;
|
|
21
19
|
pilot: Pilot | null;
|
|
22
20
|
captain: Captain | null;
|
|
@@ -26,21 +24,16 @@ export declare class Tester extends TaskAgent implements Agent {
|
|
|
26
24
|
researcher: Researcher;
|
|
27
25
|
navigator: Navigator;
|
|
28
26
|
agentTools: any;
|
|
29
|
-
executionLogFile: string | null;
|
|
30
27
|
previousUrl: string | null;
|
|
31
28
|
previousStateHash: string | null;
|
|
32
29
|
pageStateHash: string | null;
|
|
33
30
|
pageActionResult: ActionResult | null;
|
|
34
|
-
hooksRunner: HooksRunner;
|
|
35
31
|
seenUiMapUrls: Set<string>;
|
|
36
32
|
lastAnalyzedStateHash: string | null;
|
|
37
33
|
stalledIterations: number;
|
|
38
34
|
readonly MAX_STALLED_ITERATIONS = 3;
|
|
39
|
-
constructor(
|
|
35
|
+
constructor(deps: AgentDeps, researcher: Researcher, navigator: Navigator, agentTools?: any);
|
|
40
36
|
getNavigator(): Navigator;
|
|
41
|
-
getExperienceTracker(): ExperienceTracker;
|
|
42
|
-
getKnowledgeTracker(): import("../knowledge-tracker.js").KnowledgeTracker;
|
|
43
|
-
getProvider(): Provider;
|
|
44
37
|
setPilot(pilot: Pilot): void;
|
|
45
38
|
setCaptain(captain: Captain): void;
|
|
46
39
|
getCurrentState(): ActionResult;
|
|
@@ -56,7 +49,6 @@ export declare class Tester extends TaskAgent implements Agent {
|
|
|
56
49
|
shouldStopForStalledExecution(task: Test, previousState: ActionResult, toolExecutions: any[]): boolean;
|
|
57
50
|
prepareInstructionsForNextStep(task: Test): Promise<string>;
|
|
58
51
|
reinjectContextIfNeeded(iteration: number, currentState: ActionResult): Promise<string>;
|
|
59
|
-
promptLogStep(task: Test): Promise<string>;
|
|
60
52
|
finishTest(task: Test): void;
|
|
61
53
|
abortStartedTestOnErrorPage(task: Test, actionResult: ActionResult): Promise<{
|
|
62
54
|
success: boolean;
|
package/dist/src/ai/tester.js
CHANGED
|
@@ -5,18 +5,16 @@ import dedent from 'dedent';
|
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { ActionResult } from "../action-result.js";
|
|
7
7
|
import { clearActivity, setActivity } from "../activity.js";
|
|
8
|
-
import { ConfigParser } from "../config.js";
|
|
9
8
|
import { Observability } from "../observability.js";
|
|
10
9
|
import { Stats } from "../stats.js";
|
|
11
10
|
import { TestResult } from "../test-plan.js";
|
|
12
11
|
import { detectFocusArea, extractFocusedElement } from "../utils/aria.js";
|
|
13
12
|
import { ErrorPageError, isErrorPage } from "../utils/error-page.js";
|
|
14
|
-
import { HooksRunner } from "../utils/hooks-runner.js";
|
|
15
13
|
import { createDebug, tag } from "../utils/logger.js";
|
|
16
14
|
import { loop } from "../utils/loop.js";
|
|
17
15
|
import { actionRule, capabilityGroundingRule, dataProtectionRules, focusedElementRule, formRequirementsRule, locatorRule, multipleTabsRule, sectionContextRule } from "./rules.js";
|
|
18
16
|
import { TaskAgent } from "./task-agent.js";
|
|
19
|
-
import { createCodeceptJSTools,
|
|
17
|
+
import { createCodeceptJSTools, createIframeTools } from "./tools.js";
|
|
20
18
|
const debugLog = createDebug('explorbot:tester');
|
|
21
19
|
const SAMPLE_FILES_DIR = resolve(dirname(fileURLToPath(import.meta.url)), '../../assets/sample-files');
|
|
22
20
|
const SAMPLE_FILES = {
|
|
@@ -32,8 +30,8 @@ export class Tester extends TaskAgent {
|
|
|
32
30
|
ACTION_TOOLS = ['click', 'hover', 'pressKey', 'form'];
|
|
33
31
|
SPECIAL_CONTEXT_ACTION_TOOLS = ['exitIframe'];
|
|
34
32
|
emoji = '🧪';
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
requestStore;
|
|
34
|
+
testRun = null;
|
|
37
35
|
currentConversation = null;
|
|
38
36
|
pilot = null;
|
|
39
37
|
captain = null;
|
|
@@ -43,37 +41,24 @@ export class Tester extends TaskAgent {
|
|
|
43
41
|
researcher;
|
|
44
42
|
navigator;
|
|
45
43
|
agentTools;
|
|
46
|
-
executionLogFile = null;
|
|
47
44
|
previousUrl = null;
|
|
48
45
|
previousStateHash = null;
|
|
49
46
|
pageStateHash = null;
|
|
50
47
|
pageActionResult = null;
|
|
51
|
-
hooksRunner;
|
|
52
48
|
seenUiMapUrls = new Set();
|
|
53
49
|
lastAnalyzedStateHash = null;
|
|
54
50
|
stalledIterations = 0;
|
|
55
51
|
MAX_STALLED_ITERATIONS = 3;
|
|
56
|
-
constructor(
|
|
57
|
-
super();
|
|
58
|
-
this.
|
|
59
|
-
this.provider = provider;
|
|
52
|
+
constructor(deps, researcher, navigator, agentTools) {
|
|
53
|
+
super(deps);
|
|
54
|
+
this.requestStore = deps.requestStore;
|
|
60
55
|
this.researcher = researcher;
|
|
61
56
|
this.navigator = navigator;
|
|
62
57
|
this.agentTools = agentTools;
|
|
63
|
-
this.hooksRunner = new HooksRunner(explorer, explorer.getConfig());
|
|
64
58
|
}
|
|
65
59
|
getNavigator() {
|
|
66
60
|
return this.navigator;
|
|
67
61
|
}
|
|
68
|
-
getExperienceTracker() {
|
|
69
|
-
return this.explorer.getStateManager().getExperienceTracker();
|
|
70
|
-
}
|
|
71
|
-
getKnowledgeTracker() {
|
|
72
|
-
return this.explorer.getKnowledgeTracker();
|
|
73
|
-
}
|
|
74
|
-
getProvider() {
|
|
75
|
-
return this.provider;
|
|
76
|
-
}
|
|
77
62
|
setPilot(pilot) {
|
|
78
63
|
this.pilot = pilot;
|
|
79
64
|
}
|
|
@@ -81,17 +66,17 @@ export class Tester extends TaskAgent {
|
|
|
81
66
|
this.captain = captain;
|
|
82
67
|
}
|
|
83
68
|
getCurrentState() {
|
|
84
|
-
return ActionResult.fromState(this.
|
|
69
|
+
return ActionResult.fromState(this.stateManager.getCurrentState());
|
|
85
70
|
}
|
|
86
71
|
get progressCheckInterval() {
|
|
87
|
-
return this.
|
|
72
|
+
return this.config.ai?.agents?.tester?.progressCheckInterval ?? 3;
|
|
88
73
|
}
|
|
89
74
|
getConversation() {
|
|
90
75
|
return this.currentConversation;
|
|
91
76
|
}
|
|
92
77
|
async test(task) {
|
|
93
78
|
Stats.tests++;
|
|
94
|
-
const state = this.
|
|
79
|
+
const state = this.stateManager.getCurrentState();
|
|
95
80
|
if (!state)
|
|
96
81
|
throw new Error('No state found');
|
|
97
82
|
setActivity(`🧪 Testing: ${task.scenario}`, 'action');
|
|
@@ -102,27 +87,24 @@ export class Tester extends TaskAgent {
|
|
|
102
87
|
this.seenUiMapUrls.clear();
|
|
103
88
|
this.lastAnalyzedStateHash = null;
|
|
104
89
|
this.stalledIterations = 0;
|
|
105
|
-
this.
|
|
90
|
+
this.stateManager.clearHistory();
|
|
106
91
|
this.resetFailureCount();
|
|
107
92
|
this.pilot?.reset();
|
|
108
|
-
const requestStore = this.
|
|
109
|
-
requestStore
|
|
110
|
-
const offFailedRequest = requestStore
|
|
93
|
+
const requestStore = this.requestStore;
|
|
94
|
+
requestStore.clear();
|
|
95
|
+
const offFailedRequest = requestStore.onFailedRequest((r) => {
|
|
111
96
|
task.addNote(`Network error: ${r.method} ${r.path} → ${r.status}`, TestResult.FAILED);
|
|
112
97
|
});
|
|
113
98
|
const initialState = ActionResult.fromState(state);
|
|
114
99
|
if (isErrorPage(initialState)) {
|
|
115
100
|
task.start();
|
|
116
|
-
await this.explorer.
|
|
101
|
+
this.testRun = await this.explorer.beginTest(task);
|
|
117
102
|
offFailedRequest?.();
|
|
118
103
|
return await this.abortStartedTestOnErrorPage(task, initialState);
|
|
119
104
|
}
|
|
120
105
|
const conversation = this.provider.startConversation(this.getSystemMessage(), 'tester');
|
|
121
106
|
conversation.markLastMessageCacheable();
|
|
122
107
|
this.currentConversation = conversation;
|
|
123
|
-
const outputDir = ConfigParser.getInstance().getOutputDir();
|
|
124
|
-
this.executionLogFile = join(outputDir, `tester_${task.sessionName}.md`);
|
|
125
|
-
// Note: Markdown saving functionality removed from Conversation class
|
|
126
108
|
const scenarioBlock = this.buildScenarioBlock(task, initialState);
|
|
127
109
|
conversation.addUserText(scenarioBlock);
|
|
128
110
|
conversation.markLastMessageCacheable();
|
|
@@ -163,7 +145,8 @@ export class Tester extends TaskAgent {
|
|
|
163
145
|
}
|
|
164
146
|
}
|
|
165
147
|
debugLog('Starting test execution with tools');
|
|
166
|
-
|
|
148
|
+
this.testRun = await this.explorer.beginTest(task);
|
|
149
|
+
if (!this.testRun.started) {
|
|
167
150
|
offFailedRequest?.();
|
|
168
151
|
await this.cleanupStartedTest(task);
|
|
169
152
|
return { success: task.isSuccessful };
|
|
@@ -180,7 +163,7 @@ export class Tester extends TaskAgent {
|
|
|
180
163
|
return { success: task.isSuccessful };
|
|
181
164
|
}
|
|
182
165
|
}
|
|
183
|
-
const startState = this.
|
|
166
|
+
const startState = this.stateManager.getCurrentState();
|
|
184
167
|
if (startState) {
|
|
185
168
|
task.addUrlNote(startState);
|
|
186
169
|
const startActionResult = ActionResult.fromState(startState);
|
|
@@ -191,7 +174,7 @@ export class Tester extends TaskAgent {
|
|
|
191
174
|
}
|
|
192
175
|
const currentUrl = startState?.url || task.startUrl || '';
|
|
193
176
|
await this.hooksRunner.runBeforeHook('tester', currentUrl);
|
|
194
|
-
const offStateChange = this.
|
|
177
|
+
const offStateChange = this.stateManager.onStateChange((event) => {
|
|
195
178
|
if (task.hasFinished)
|
|
196
179
|
return;
|
|
197
180
|
if (event.toState?.url === event.fromState?.url)
|
|
@@ -200,7 +183,7 @@ export class Tester extends TaskAgent {
|
|
|
200
183
|
task.addUrlNote(event.toState, event.fromState || undefined);
|
|
201
184
|
task.states.push(event.toState);
|
|
202
185
|
});
|
|
203
|
-
const codeceptjsTools = createCodeceptJSTools(this.
|
|
186
|
+
const codeceptjsTools = createCodeceptJSTools(this.toolDeps, task);
|
|
204
187
|
let assertionPerformed = false;
|
|
205
188
|
let extensions = 0;
|
|
206
189
|
let shouldContinue = true;
|
|
@@ -208,7 +191,7 @@ export class Tester extends TaskAgent {
|
|
|
208
191
|
shouldContinue = false;
|
|
209
192
|
await loop(async ({ stop, pause, iteration, userInput }) => {
|
|
210
193
|
debugLog('iteration', iteration);
|
|
211
|
-
if (!(await this.explorer.
|
|
194
|
+
if (!(await this.explorer.recover()).ok) {
|
|
212
195
|
task.addNote('Browser page is unavailable');
|
|
213
196
|
task.finish(TestResult.FAILED);
|
|
214
197
|
stop();
|
|
@@ -217,12 +200,14 @@ export class Tester extends TaskAgent {
|
|
|
217
200
|
const currentState = this.getCurrentState();
|
|
218
201
|
const tools = {
|
|
219
202
|
...codeceptjsTools,
|
|
220
|
-
...(currentState.isInsideIframe ? createSpecialContextTools(this.explorer, 'iframe') : {}),
|
|
221
203
|
...this.createTestFlowTools(task, currentState, conversation),
|
|
222
204
|
...this.agentTools,
|
|
223
205
|
};
|
|
206
|
+
if (currentState.isInsideIframe) {
|
|
207
|
+
Object.assign(tools, createIframeTools(this.toolDeps));
|
|
208
|
+
}
|
|
224
209
|
debugLog(`Test ${task.scenario} iteration ${iteration}`);
|
|
225
|
-
if (this.
|
|
210
|
+
if (this.stateManager.isInDeadLoop()) {
|
|
226
211
|
task.addNote('Dead loop detected. Stopped');
|
|
227
212
|
stop();
|
|
228
213
|
return;
|
|
@@ -357,7 +342,7 @@ export class Tester extends TaskAgent {
|
|
|
357
342
|
});
|
|
358
343
|
if (task.hasFinished)
|
|
359
344
|
break;
|
|
360
|
-
if (!(await this.explorer.
|
|
345
|
+
if (!(await this.explorer.recover()).ok)
|
|
361
346
|
break;
|
|
362
347
|
const finalState = this.getCurrentState();
|
|
363
348
|
const wantsContinue = await this.pilot.finalReview(task, finalState, conversation, this.navigator);
|
|
@@ -375,7 +360,7 @@ export class Tester extends TaskAgent {
|
|
|
375
360
|
conversation.compactToolResults(1);
|
|
376
361
|
shouldContinue = true;
|
|
377
362
|
}
|
|
378
|
-
const finalUrl = this.
|
|
363
|
+
const finalUrl = this.stateManager.getCurrentState()?.url || currentUrl;
|
|
379
364
|
await this.hooksRunner.runAfterHook('tester', finalUrl);
|
|
380
365
|
await this.getHistorian().saveSession(task, initialState, conversation);
|
|
381
366
|
if (task.plan) {
|
|
@@ -385,7 +370,7 @@ export class Tester extends TaskAgent {
|
|
|
385
370
|
offStateChange();
|
|
386
371
|
offFailedRequest?.();
|
|
387
372
|
await this.finishTest(task);
|
|
388
|
-
await this.
|
|
373
|
+
await this.testRun?.stop(this.buildStopTestMeta(task));
|
|
389
374
|
return {
|
|
390
375
|
success: task.isSuccessful,
|
|
391
376
|
...task,
|
|
@@ -456,7 +441,6 @@ export class Tester extends TaskAgent {
|
|
|
456
441
|
const currentUrl = currentState.url;
|
|
457
442
|
const currentStateHash = currentState.hash;
|
|
458
443
|
const isNewUrl = this.previousUrl !== currentUrl;
|
|
459
|
-
const isStateChanged = !isNewUrl && this.previousStateHash !== currentStateHash;
|
|
460
444
|
this.previousUrl = currentUrl;
|
|
461
445
|
this.previousStateHash = currentStateHash;
|
|
462
446
|
let context = '';
|
|
@@ -490,7 +474,7 @@ export class Tester extends TaskAgent {
|
|
|
490
474
|
`;
|
|
491
475
|
}
|
|
492
476
|
if (currentState.isInsideIframe) {
|
|
493
|
-
const iframeInfo = currentState.iframeURL ||
|
|
477
|
+
const iframeInfo = currentState.iframeURL || 'iframe context active';
|
|
494
478
|
context += dedent `
|
|
495
479
|
<iframe_context>
|
|
496
480
|
INSIDE IFRAME: ${iframeInfo}
|
|
@@ -498,10 +482,10 @@ export class Tester extends TaskAgent {
|
|
|
498
482
|
</iframe_context>
|
|
499
483
|
`;
|
|
500
484
|
}
|
|
501
|
-
|
|
502
|
-
|
|
485
|
+
const otherTabs = this.stateManager.otherTabs;
|
|
486
|
+
if (otherTabs.length > 0) {
|
|
503
487
|
context += multipleTabsRule(otherTabs);
|
|
504
|
-
this.
|
|
488
|
+
this.stateManager.otherTabs = [];
|
|
505
489
|
}
|
|
506
490
|
if (isNewUrl) {
|
|
507
491
|
const alreadySeenUiMap = this.seenUiMapUrls.has(currentUrl);
|
|
@@ -564,23 +548,6 @@ export class Tester extends TaskAgent {
|
|
|
564
548
|
`;
|
|
565
549
|
}
|
|
566
550
|
}
|
|
567
|
-
// if (isStateChanged) {
|
|
568
|
-
// const combinedHtml = await currentState.combinedHtml();
|
|
569
|
-
// context += dedent`
|
|
570
|
-
// Context (state changed):
|
|
571
|
-
// <page>
|
|
572
|
-
// CURRENT URL: ${currentState.url}
|
|
573
|
-
// CURRENT TITLE: ${currentState.title}
|
|
574
|
-
// </page>
|
|
575
|
-
// <page_html>
|
|
576
|
-
// ${combinedHtml}
|
|
577
|
-
// </page_html>
|
|
578
|
-
// <page_aria>
|
|
579
|
-
// ${currentState.ariaSnapshot}
|
|
580
|
-
// </page_aria>
|
|
581
|
-
// `;
|
|
582
|
-
// return context;
|
|
583
|
-
// }
|
|
584
551
|
if (context)
|
|
585
552
|
return context;
|
|
586
553
|
if (iteration % 5)
|
|
@@ -598,34 +565,6 @@ export class Tester extends TaskAgent {
|
|
|
598
565
|
</page_aria>
|
|
599
566
|
`;
|
|
600
567
|
}
|
|
601
|
-
async promptLogStep(task) {
|
|
602
|
-
let logPrompt = dedent `
|
|
603
|
-
<task>
|
|
604
|
-
Add a note explaining what you achieved with previous action.
|
|
605
|
-
Use tools to interact with the page to achieve the scenario goal or expected outcomes.
|
|
606
|
-
Call record tool to explain the last action
|
|
607
|
-
Format: record([<action performed>, <what has changed>, <what you expect to do next>])
|
|
608
|
-
</task>
|
|
609
|
-
`;
|
|
610
|
-
if (task.getPrintableNotes()) {
|
|
611
|
-
logPrompt = dedent `
|
|
612
|
-
Your interaction log notes:
|
|
613
|
-
<notes>
|
|
614
|
-
${task.getPrintableNotes()}
|
|
615
|
-
</notes>
|
|
616
|
-
|
|
617
|
-
<rules>
|
|
618
|
-
Use your previous interaction notes to guide your next actions.
|
|
619
|
-
Do not perform the same checks.
|
|
620
|
-
</rules>
|
|
621
|
-
`;
|
|
622
|
-
}
|
|
623
|
-
const remaining = task.getRemainingExpectations();
|
|
624
|
-
if (remaining.length > 0) {
|
|
625
|
-
logPrompt += `\nExpected steps to check: ${remaining.join(', ')}`;
|
|
626
|
-
}
|
|
627
|
-
return logPrompt;
|
|
628
|
-
}
|
|
629
568
|
finishTest(task) {
|
|
630
569
|
if (!task.hasFinished) {
|
|
631
570
|
task.finish(TestResult.FAILED);
|
|
@@ -649,7 +588,7 @@ export class Tester extends TaskAgent {
|
|
|
649
588
|
task.addNote(error.message, TestResult.FAILED, actionResult.screenshotFile, actionResult.fullUrl || actionResult.url);
|
|
650
589
|
task.finish(TestResult.FAILED);
|
|
651
590
|
this.finishTest(task);
|
|
652
|
-
await this.
|
|
591
|
+
await this.testRun?.stop(this.buildStopTestMeta(task));
|
|
653
592
|
clearActivity(true);
|
|
654
593
|
return { success: false };
|
|
655
594
|
}
|
|
@@ -725,11 +664,13 @@ export class Tester extends TaskAgent {
|
|
|
725
664
|
- Before retrying your actions check maybe they already achived expected results. Use see() tool for that
|
|
726
665
|
- If the current URL is already a create/edit/new form and the scenario is about creating/editing that entity, fill and submit that form. Do not click the list-page "New" button again from inside the form.
|
|
727
666
|
- If the scenario is about search/filter/sort/tabs/list inspection and the current URL is a create/edit/new form, go back or reset to the stable list page before interacting with list controls.
|
|
728
|
-
- When selecting related entities from a list, do not choose rows/options/cards marked as "0 items", "0
|
|
729
|
-
- In selection pickers, counters such as "Selected 0", "Matched
|
|
667
|
+
- When selecting related entities from a list, do not choose rows/options/cards marked as "0 items", "0 results", or otherwise empty if the scenario requires selecting real content.
|
|
668
|
+
- In selection pickers, counters such as "Selected 0", "Matched 0", or disabled Save/Apply mean the selection did not register. Choose a non-empty item or change filters before submitting.
|
|
730
669
|
- A passed form/click command only means the command executed. If a required field remains empty, submit stays disabled, or the expected text is not visible, treat the action as not completed and correct the missing field/state.
|
|
731
|
-
- For filter/tab scenarios, success requires BOTH: the requested
|
|
732
|
-
-
|
|
670
|
+
- For filter/tab scenarios, success requires BOTH: the requested state is evidenced by a selected control, URL/query, or another explicit state indicator AND the list content matches that state. Do not finish from only one of these signals.
|
|
671
|
+
- Once the requested control state and matching content are both visible, finish the scenario instead of repeating the interaction. Do not require an aggregate count change unless a baseline was observed immediately before the action.
|
|
672
|
+
- Empty-state text such as "No matched items" only proves a filter when the requested filter state is explicit and the empty state belongs to the filtered list.
|
|
673
|
+
- Associate validation feedback with a field only through explicit evidence such as the field label in the message, an accessibility relationship, focus on the invalid control, or visual confirmation. Do not infer the affected field from DOM order or proximity alone.
|
|
733
674
|
- When filling complex form with lot of actions performed, use see() to look which fields were filled and which are not
|
|
734
675
|
- When verify() fails, use see() to visually confirm the result — visual confirmation is equally valid evidence
|
|
735
676
|
- For visual state verification (active tabs, selected items, counts, colors), prefer see() over DOM-based verify()
|
|
@@ -762,7 +703,7 @@ export class Tester extends TaskAgent {
|
|
|
762
703
|
|
|
763
704
|
${dataProtectionRules}
|
|
764
705
|
|
|
765
|
-
${this.provider.getSystemPromptForAgent('tester', this.
|
|
706
|
+
${this.provider.getSystemPromptForAgent('tester', this.stateManager.getCurrentState()?.url) || ''}
|
|
766
707
|
`;
|
|
767
708
|
}
|
|
768
709
|
buildScenarioBlock(task, actionResult) {
|
|
@@ -788,10 +729,6 @@ export class Tester extends TaskAgent {
|
|
|
788
729
|
When creating or editing items via form() or type() you should include ${task.sessionName} in the value (if it is not restricted by the application logic)
|
|
789
730
|
Initial page URL: ${actionResult.url}
|
|
790
731
|
|
|
791
|
-
${capabilityGroundingRule}
|
|
792
|
-
|
|
793
|
-
${dataProtectionRules}
|
|
794
|
-
|
|
795
732
|
${this.buildDeletionScope(task)}
|
|
796
733
|
|
|
797
734
|
${this.buildAvailableFiles()}
|
|
@@ -810,7 +747,7 @@ export class Tester extends TaskAgent {
|
|
|
810
747
|
.map((t) => t.sessionName);
|
|
811
748
|
}
|
|
812
749
|
buildAvailableFiles() {
|
|
813
|
-
const userFiles = this.
|
|
750
|
+
const userFiles = this.config.files || {};
|
|
814
751
|
const codeceptDir = global.codecept_dir || process.cwd();
|
|
815
752
|
const lines = [];
|
|
816
753
|
for (const [description, filename] of Object.entries(SAMPLE_FILES)) {
|
|
@@ -864,10 +801,8 @@ export class Tester extends TaskAgent {
|
|
|
864
801
|
reason: z.string().optional().describe('Explanation why reset is the only option'),
|
|
865
802
|
}),
|
|
866
803
|
execute: async ({ reason }) => {
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
}
|
|
870
|
-
const currentState = this.explorer.getStateManager().getCurrentState();
|
|
804
|
+
await this.explorer.action().exitIframe();
|
|
805
|
+
const currentState = this.stateManager.getCurrentState();
|
|
871
806
|
const currentUrl = currentState?.fullUrl || currentState?.url;
|
|
872
807
|
if (currentUrl === resetUrl) {
|
|
873
808
|
return {
|
|
@@ -892,7 +827,7 @@ export class Tester extends TaskAgent {
|
|
|
892
827
|
const explanation = reason ? `${reason} (RESET)` : 'Resetting to initial page';
|
|
893
828
|
const targetUrl = resetUrl;
|
|
894
829
|
task.addNote(explanation);
|
|
895
|
-
const resetAction = this.explorer.
|
|
830
|
+
const resetAction = this.explorer.action();
|
|
896
831
|
const success = await resetAction.attempt(`I.amOnPage(${JSON.stringify(targetUrl)})`, explanation);
|
|
897
832
|
if (success) {
|
|
898
833
|
return {
|
|
@@ -1035,7 +970,7 @@ export class Tester extends TaskAgent {
|
|
|
1035
970
|
else if (input.status === 'fail') {
|
|
1036
971
|
mappedStatus = TestResult.FAILED;
|
|
1037
972
|
}
|
|
1038
|
-
const screenshotFile = this.
|
|
973
|
+
const screenshotFile = this.stateManager.getCurrentState()?.screenshotFile;
|
|
1039
974
|
for (const noteText of input.notes) {
|
|
1040
975
|
task.addNote(noteText, mappedStatus, screenshotFile);
|
|
1041
976
|
if (input.status === 'success') {
|
|
@@ -1072,7 +1007,7 @@ export class Tester extends TaskAgent {
|
|
|
1072
1007
|
const message = error instanceof Error ? error.message : String(error);
|
|
1073
1008
|
if (!task.hasFinished)
|
|
1074
1009
|
task.addNote(`Execution error: ${message}`);
|
|
1075
|
-
const result = await this.explorer.
|
|
1010
|
+
const result = await this.explorer.recover(error);
|
|
1076
1011
|
tag('info').log(`Browser supervisor: ${result.action} - ${result.message}`);
|
|
1077
1012
|
task.addNote(result.message);
|
|
1078
1013
|
if (result.action === 'stop') {
|
|
@@ -1103,7 +1038,7 @@ export class Tester extends TaskAgent {
|
|
|
1103
1038
|
}
|
|
1104
1039
|
async cleanupStartedTest(task) {
|
|
1105
1040
|
await this.finishTest(task);
|
|
1106
|
-
await this.
|
|
1041
|
+
await this.testRun?.stop({
|
|
1107
1042
|
startUrl: task.startUrl,
|
|
1108
1043
|
style: task.style,
|
|
1109
1044
|
sessionName: task.sessionName,
|
package/dist/src/ai/tools.d.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import { ActionResult } from '../action-result.js';
|
|
1
|
+
import { ActionResult, type PageDiff } from '../action-result.js';
|
|
2
2
|
import type { ExperienceTracker } from '../experience-tracker.js';
|
|
3
|
-
import type Explorer from '../explorer.js';
|
|
4
3
|
import { type Task } from '../test-plan.js';
|
|
4
|
+
import type { ToolDeps } from './agent.js';
|
|
5
5
|
import { Navigator } from './navigator.js';
|
|
6
6
|
import { Researcher } from './researcher.js';
|
|
7
|
+
interface AgentToolDeps extends ToolDeps {
|
|
8
|
+
researcher: Researcher;
|
|
9
|
+
navigator: Navigator;
|
|
10
|
+
supervisor?: boolean;
|
|
11
|
+
withExperience?: boolean;
|
|
12
|
+
}
|
|
7
13
|
export declare const ASSERTION_TOOLS: readonly ["verify"];
|
|
8
|
-
export declare function createCodeceptJSTools(explorer:
|
|
14
|
+
export declare function createCodeceptJSTools({ explorer, stateManager, ai }: ToolDeps, task: Task): {
|
|
9
15
|
click: import("@ai-sdk/provider-utils").ExecutableTool<import("ai").Tool<{
|
|
10
16
|
commands: any;
|
|
11
17
|
explanation: any;
|
|
@@ -24,18 +30,24 @@ export declare function createCodeceptJSTools(explorer: Explorer, task: Task): {
|
|
|
24
30
|
explanation: any;
|
|
25
31
|
}, Record<string, any>, import("@ai-sdk/provider-utils").Context>>;
|
|
26
32
|
};
|
|
27
|
-
export declare function
|
|
28
|
-
exitIframe?: undefined;
|
|
29
|
-
} | {
|
|
33
|
+
export declare function createIframeTools({ explorer, stateManager }: ToolDeps): {
|
|
30
34
|
exitIframe: import("@ai-sdk/provider-utils").ExecutableTool<import("ai").Tool<{
|
|
31
35
|
reason?: string;
|
|
32
36
|
}, Record<string, any>, import("@ai-sdk/provider-utils").Context>>;
|
|
33
37
|
};
|
|
34
|
-
export declare function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
export declare function createLearnExperienceTool({ getExperienceTracker, getState }: {
|
|
39
|
+
getExperienceTracker: () => ExperienceTracker;
|
|
40
|
+
getState: () => ActionResult | null;
|
|
41
|
+
}): import("@ai-sdk/provider-utils").ExecutableTool<import("ai").Tool<{
|
|
42
|
+
fileTag: any;
|
|
43
|
+
sectionIndex: any;
|
|
44
|
+
}, {
|
|
45
|
+
title: string;
|
|
46
|
+
url: string;
|
|
47
|
+
content: string;
|
|
48
|
+
} | {
|
|
49
|
+
error: string;
|
|
50
|
+
}, import("@ai-sdk/provider-utils").Context>>;
|
|
51
|
+
export declare function createAgentTools({ explorer, stateManager, ai, researcher, navigator, supervisor, withExperience }: AgentToolDeps): any;
|
|
52
|
+
export declare function isMajorPageChange(pageDiff: PageDiff): boolean;
|
|
53
|
+
export {};
|