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
|
@@ -1,20 +1,22 @@
|
|
|
1
|
+
export function toToolExecution(toolName, input, rawOutput) {
|
|
2
|
+
let output = rawOutput;
|
|
3
|
+
if (rawOutput?.type === 'json' && rawOutput?.value)
|
|
4
|
+
output = rawOutput.value;
|
|
5
|
+
return { toolName, input, output, wasSuccessful: output?.success !== false };
|
|
6
|
+
}
|
|
1
7
|
export function toolExecutionLabel(input) {
|
|
2
8
|
return input?.explanation || input?.assertion || input?.reason || input?.request || '';
|
|
3
9
|
}
|
|
4
10
|
const AUTO_COMPACT_ARIA_CHANGES_CUTOFF = 500;
|
|
5
11
|
const AUTO_COMPACT_TARGETED_HTML_CUTOFF = 500;
|
|
6
12
|
export class Conversation {
|
|
7
|
-
id;
|
|
8
13
|
messages;
|
|
9
14
|
model;
|
|
10
|
-
telemetryFunctionId;
|
|
11
15
|
protectedPrefixCount = 0;
|
|
12
16
|
autoTrimRules;
|
|
13
|
-
constructor(messages = [], model
|
|
14
|
-
this.id = this.generateId();
|
|
17
|
+
constructor(messages = [], model) {
|
|
15
18
|
this.messages = messages;
|
|
16
19
|
this.model = model || '';
|
|
17
|
-
this.telemetryFunctionId = telemetryFunctionId;
|
|
18
20
|
this.autoTrimRules = new Map();
|
|
19
21
|
}
|
|
20
22
|
protectPrefix(count) {
|
|
@@ -26,17 +28,6 @@ export class Conversation {
|
|
|
26
28
|
content: this.applyAutoTrim(text),
|
|
27
29
|
});
|
|
28
30
|
}
|
|
29
|
-
addUserImage(image) {
|
|
30
|
-
if (!image || image.trim() === '') {
|
|
31
|
-
console.warn('Warning: Attempting to add empty image to conversation');
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
const imageData = image.startsWith('data:') ? image : `data:image/png;base64,${image}`;
|
|
35
|
-
this.messages.push({
|
|
36
|
-
role: 'user',
|
|
37
|
-
content: [{ type: 'file', mediaType: 'image/png', data: imageData }],
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
31
|
addAssistantText(text) {
|
|
41
32
|
// Skip empty or whitespace-only messages
|
|
42
33
|
if (!text || text.trim() === '') {
|
|
@@ -61,7 +52,7 @@ export class Conversation {
|
|
|
61
52
|
return '';
|
|
62
53
|
}
|
|
63
54
|
clone() {
|
|
64
|
-
return new Conversation([...this.messages], this.model
|
|
55
|
+
return new Conversation([...this.messages], this.model);
|
|
65
56
|
}
|
|
66
57
|
cleanupTag(tagName, replacement, keepLast = 0) {
|
|
67
58
|
const escapedTag = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -190,9 +181,6 @@ export class Conversation {
|
|
|
190
181
|
}
|
|
191
182
|
return result;
|
|
192
183
|
}
|
|
193
|
-
generateId() {
|
|
194
|
-
return `conv_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
195
|
-
}
|
|
196
184
|
getToolExecutions() {
|
|
197
185
|
const toolCalls = new Map();
|
|
198
186
|
for (const message of this.messages) {
|
|
@@ -215,14 +203,7 @@ export class Conversation {
|
|
|
215
203
|
for (const part of message.content) {
|
|
216
204
|
if (part.type !== 'tool-result')
|
|
217
205
|
continue;
|
|
218
|
-
|
|
219
|
-
const output = rawOutput?.type === 'json' && rawOutput?.value ? rawOutput.value : rawOutput;
|
|
220
|
-
executions.push({
|
|
221
|
-
toolName: part.toolName,
|
|
222
|
-
input: toolCalls.get(part.toolCallId) || {},
|
|
223
|
-
output,
|
|
224
|
-
wasSuccessful: output?.success !== false,
|
|
225
|
-
});
|
|
206
|
+
executions.push(toToolExecution(part.toolName, toolCalls.get(part.toolCallId) || {}, part.output));
|
|
226
207
|
}
|
|
227
208
|
}
|
|
228
209
|
return executions;
|
package/dist/src/ai/driller.d.ts
CHANGED
|
@@ -1,14 +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';
|
|
9
|
-
import type { Conversation } from './conversation.js';
|
|
4
|
+
import type { Agent, AgentDeps } from './agent.js';
|
|
10
5
|
import type { Navigator } from './navigator.js';
|
|
11
|
-
import type { Provider } from './provider.js';
|
|
12
6
|
import { TaskAgent } from './task-agent.js';
|
|
13
7
|
interface ComponentInfo {
|
|
14
8
|
id: string;
|
|
@@ -49,12 +43,8 @@ interface DrillOptions {
|
|
|
49
43
|
export declare class Driller extends TaskAgent implements Agent {
|
|
50
44
|
readonly ACTION_TOOLS: string[];
|
|
51
45
|
emoji: string;
|
|
52
|
-
explorer: Explorer;
|
|
53
|
-
provider: Provider;
|
|
54
46
|
navigator: Navigator;
|
|
55
|
-
hooksRunner: HooksRunner;
|
|
56
47
|
currentPlan?: Plan;
|
|
57
|
-
currentConversation: Conversation | null;
|
|
58
48
|
allResults: InteractionResult[];
|
|
59
49
|
verifiedAction: {
|
|
60
50
|
componentId: string;
|
|
@@ -64,11 +54,8 @@ export declare class Driller extends TaskAgent implements Agent {
|
|
|
64
54
|
} | null;
|
|
65
55
|
pendingNestedContext: string | null;
|
|
66
56
|
MAX_COMPONENT_ITERATIONS: number;
|
|
67
|
-
constructor(
|
|
57
|
+
constructor(deps: AgentDeps, navigator: Navigator);
|
|
68
58
|
getNavigator(): Navigator;
|
|
69
|
-
getExperienceTracker(): ExperienceTracker;
|
|
70
|
-
getKnowledgeTracker(): KnowledgeTracker;
|
|
71
|
-
getProvider(): Provider;
|
|
72
59
|
getSystemMessage(component?: ComponentInfo): string;
|
|
73
60
|
drill(opts?: DrillOptions): Promise<Plan>;
|
|
74
61
|
captureAnnotatedState(): Promise<ActionResult>;
|
|
@@ -153,8 +140,6 @@ export declare class Driller extends TaskAgent implements Agent {
|
|
|
153
140
|
saveToKnowledge(knowledgePath: string, state: ActionResult, results: InteractionResult[]): Promise<void>;
|
|
154
141
|
generateKnowledgeContent(state: ActionResult, interactions: InteractionResult[]): string;
|
|
155
142
|
logSummary(): void;
|
|
156
|
-
getCurrentPlan(): Plan | undefined;
|
|
157
|
-
getConversation(): Conversation | null;
|
|
158
143
|
}
|
|
159
144
|
export declare function buildCanonicalClickCode(component: ComponentInfo): string;
|
|
160
145
|
export declare function formatExperienceTitle(interaction: InteractionResult): string;
|
package/dist/src/ai/driller.js
CHANGED
|
@@ -6,49 +6,34 @@ import { setActivity } from "../activity.js";
|
|
|
6
6
|
import { Observability } from "../observability.js";
|
|
7
7
|
import { Plan, Test, TestResult } from "../test-plan.js";
|
|
8
8
|
import { collectInteractiveNodes } from "../utils/aria.js";
|
|
9
|
-
import { HooksRunner } from "../utils/hooks-runner.js";
|
|
10
9
|
import { EXPLORBOT_ATTRS, HTML_COMPOSITE_AREA_HINTS, HTML_COMPOSITE_TARGET_ROLES, HTML_EXTRACTION_LIMITS, HTML_FORM_CONTROL_ROLES, HTML_FORM_CONTROL_TAGS, HTML_INTERACTIVE_ROLES, HTML_SELECTORS, HTML_VISIBILITY_LIMITS, getComponentScopeHtmlExtractorSource, getVisibleOverlayHtmlExtractorSource, inferHtmlRole, } from "../utils/html.js";
|
|
11
10
|
import { createDebug, tag } from "../utils/logger.js";
|
|
12
11
|
import { loop, pause } from "../utils/loop.js";
|
|
12
|
+
import { annotatePageElements } from "../utils/web-annotate.js";
|
|
13
|
+
import { eidxInContainer } from "../utils/web-eidx.js";
|
|
13
14
|
import { WebElement } from "../utils/web-element.js";
|
|
14
|
-
import {
|
|
15
|
+
import { drillLocatorRule } from "./rules.js";
|
|
15
16
|
import { TaskAgent, isInteractive } from "./task-agent.js";
|
|
16
17
|
import { createCodeceptJSTools } from "./tools.js";
|
|
17
18
|
const debugLog = createDebug('explorbot:driller');
|
|
18
19
|
export class Driller extends TaskAgent {
|
|
19
20
|
ACTION_TOOLS = ['click', 'pressKey', 'form'];
|
|
20
21
|
emoji = 'D';
|
|
21
|
-
explorer;
|
|
22
|
-
provider;
|
|
23
22
|
navigator;
|
|
24
|
-
hooksRunner;
|
|
25
23
|
currentPlan;
|
|
26
|
-
currentConversation = null;
|
|
27
24
|
allResults = [];
|
|
28
25
|
verifiedAction = null;
|
|
29
26
|
pendingNestedContext = null;
|
|
30
27
|
MAX_COMPONENT_ITERATIONS = 12;
|
|
31
|
-
constructor(
|
|
32
|
-
super();
|
|
33
|
-
this.explorer = explorer;
|
|
34
|
-
this.provider = provider;
|
|
28
|
+
constructor(deps, navigator) {
|
|
29
|
+
super(deps);
|
|
35
30
|
this.navigator = navigator;
|
|
36
|
-
this.hooksRunner = new HooksRunner(explorer, explorer.getConfig());
|
|
37
31
|
}
|
|
38
32
|
getNavigator() {
|
|
39
33
|
return this.navigator;
|
|
40
34
|
}
|
|
41
|
-
getExperienceTracker() {
|
|
42
|
-
return this.explorer.getStateManager().getExperienceTracker();
|
|
43
|
-
}
|
|
44
|
-
getKnowledgeTracker() {
|
|
45
|
-
return this.explorer.getKnowledgeTracker();
|
|
46
|
-
}
|
|
47
|
-
getProvider() {
|
|
48
|
-
return this.provider;
|
|
49
|
-
}
|
|
50
35
|
getSystemMessage(component) {
|
|
51
|
-
const currentUrl = this.
|
|
36
|
+
const currentUrl = this.stateManager.getCurrentState()?.url;
|
|
52
37
|
const customPrompt = this.provider.getSystemPromptForAgent('driller', currentUrl);
|
|
53
38
|
return dedent `
|
|
54
39
|
<role>
|
|
@@ -90,7 +75,7 @@ export class Driller extends TaskAgent {
|
|
|
90
75
|
}
|
|
91
76
|
async drill(opts = {}) {
|
|
92
77
|
const { knowledgePath, maxComponents = 30, interactive = isInteractive() } = opts;
|
|
93
|
-
const currentState = this.
|
|
78
|
+
const currentState = this.stateManager.getCurrentState();
|
|
94
79
|
if (!currentState)
|
|
95
80
|
throw new Error('No page state available');
|
|
96
81
|
const sessionName = `driller_${Date.now().toString(36)}`;
|
|
@@ -131,15 +116,15 @@ export class Driller extends TaskAgent {
|
|
|
131
116
|
}
|
|
132
117
|
async captureAnnotatedState() {
|
|
133
118
|
setActivity(`${this.emoji} Capturing annotated page state...`, 'action');
|
|
134
|
-
const action = this.explorer.
|
|
119
|
+
const action = this.explorer.action();
|
|
135
120
|
try {
|
|
136
121
|
const annotated = await Promise.race([
|
|
137
|
-
this.explorer.
|
|
122
|
+
this.explorer.withPage(annotatePageElements),
|
|
138
123
|
new Promise((_, reject) => {
|
|
139
124
|
setTimeout(() => reject(new Error('annotateElements timeout')), 15000);
|
|
140
125
|
}),
|
|
141
126
|
]);
|
|
142
|
-
return action.capturePageState(
|
|
127
|
+
return action.capturePageState();
|
|
143
128
|
}
|
|
144
129
|
catch (error) {
|
|
145
130
|
tag('warning').log(`Annotated capture failed, falling back to plain page state: ${error instanceof Error ? error.message : error}`);
|
|
@@ -151,9 +136,10 @@ export class Driller extends TaskAgent {
|
|
|
151
136
|
}
|
|
152
137
|
async collectComponents(state, maxComponents) {
|
|
153
138
|
setActivity(`${this.emoji} Collecting components...`, 'action');
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
139
|
+
const webElements = await this.explorer.withPage(async (page) => {
|
|
140
|
+
const eidxList = await eidxInContainer(page, null);
|
|
141
|
+
return WebElement.fromEidxList(page, eidxList);
|
|
142
|
+
});
|
|
157
143
|
const ariaNodes = collectInteractiveNodes(state.ariaSnapshot);
|
|
158
144
|
const scored = webElements
|
|
159
145
|
.filter((element) => isDrillableElement(element))
|
|
@@ -245,16 +231,15 @@ export class Driller extends TaskAgent {
|
|
|
245
231
|
this.verifiedAction = null;
|
|
246
232
|
this.pendingNestedContext = null;
|
|
247
233
|
const conversation = this.provider.startConversation(this.getSystemMessage(component), 'driller');
|
|
248
|
-
this.currentConversation = conversation;
|
|
249
234
|
conversation.addUserText(await this.buildComponentPrompt(originalState, component));
|
|
250
235
|
let finished = false;
|
|
251
|
-
const actionTools = this.createVerifiedActionTools(createCodeceptJSTools(this.
|
|
236
|
+
const actionTools = this.createVerifiedActionTools(createCodeceptJSTools(this.toolDeps, test), component);
|
|
252
237
|
const tools = { ...actionTools, ...this.createDrillFlowTools(originalState, test, interactive) };
|
|
253
238
|
await loop(async ({ stop, iteration }) => {
|
|
254
239
|
debugLog(`Drilling component ${component.name}, iteration ${iteration}`);
|
|
255
240
|
setActivity(`${this.emoji} Drilling ${component.name}...`, 'action');
|
|
256
241
|
if (iteration > 1) {
|
|
257
|
-
const currentState = ActionResult.fromState(this.
|
|
242
|
+
const currentState = ActionResult.fromState(this.stateManager.getCurrentState() || originalState);
|
|
258
243
|
conversation.addUserText(await this.buildContextUpdate(currentState, component));
|
|
259
244
|
if (this.pendingNestedContext) {
|
|
260
245
|
conversation.addUserText(this.pendingNestedContext);
|
|
@@ -481,7 +466,7 @@ export class Driller extends TaskAgent {
|
|
|
481
466
|
execute: async ({ reason }) => {
|
|
482
467
|
await this.restoreOriginalState(originalState, reason);
|
|
483
468
|
await this.captureAnnotatedState();
|
|
484
|
-
const currentState = this.
|
|
469
|
+
const currentState = this.stateManager.getCurrentState();
|
|
485
470
|
return { success: true, url: currentState?.url || originalState.url };
|
|
486
471
|
},
|
|
487
472
|
}),
|
|
@@ -502,14 +487,14 @@ export class Driller extends TaskAgent {
|
|
|
502
487
|
};
|
|
503
488
|
}
|
|
504
489
|
async restoreOriginalState(originalState, reason) {
|
|
505
|
-
const currentState = this.
|
|
490
|
+
const currentState = this.stateManager.getCurrentState();
|
|
506
491
|
const targetUrl = originalState.fullUrl || originalState.url;
|
|
507
|
-
const action = this.explorer.
|
|
492
|
+
const action = this.explorer.action();
|
|
508
493
|
if (currentState?.url !== originalState.url) {
|
|
509
|
-
await action.attempt(`I.amOnPage(${JSON.stringify(targetUrl)})`, `${reason} (restore URL)
|
|
494
|
+
await action.attempt(`I.amOnPage(${JSON.stringify(targetUrl)})`, `${reason} (restore URL)`);
|
|
510
495
|
return;
|
|
511
496
|
}
|
|
512
|
-
await action.attempt('I.pressKey("Escape")', `${reason} (restore state)
|
|
497
|
+
await action.attempt('I.pressKey("Escape")', `${reason} (restore state)`);
|
|
513
498
|
}
|
|
514
499
|
async saveToExperience(state, results) {
|
|
515
500
|
const experienceTracker = this.getExperienceTracker();
|
|
@@ -560,7 +545,7 @@ export class Driller extends TaskAgent {
|
|
|
560
545
|
const overlayHtml = await this.getVisibleOverlayHtml();
|
|
561
546
|
if (!overlayHtml)
|
|
562
547
|
return null;
|
|
563
|
-
const state = this.
|
|
548
|
+
const state = this.stateManager.getCurrentState();
|
|
564
549
|
if (!state)
|
|
565
550
|
return null;
|
|
566
551
|
const currentState = ActionResult.fromState(state);
|
|
@@ -581,8 +566,7 @@ export class Driller extends TaskAgent {
|
|
|
581
566
|
`;
|
|
582
567
|
}
|
|
583
568
|
async getVisibleOverlayHtml() {
|
|
584
|
-
|
|
585
|
-
return page.evaluate(({ extractorSource, config }) => {
|
|
569
|
+
return this.explorer.withPage((page) => page.evaluate(({ extractorSource, config }) => {
|
|
586
570
|
const extract = new Function(`return ${extractorSource}`)();
|
|
587
571
|
return extract(config);
|
|
588
572
|
}, {
|
|
@@ -593,11 +577,10 @@ export class Driller extends TaskAgent {
|
|
|
593
577
|
overlaySelectors: HTML_SELECTORS.semanticOverlays,
|
|
594
578
|
visibilityLimits: HTML_VISIBILITY_LIMITS,
|
|
595
579
|
},
|
|
596
|
-
});
|
|
580
|
+
}));
|
|
597
581
|
}
|
|
598
582
|
async getComponentScopeHtml(component, originalState) {
|
|
599
|
-
const
|
|
600
|
-
const scopedHtml = await page.evaluate(({ eidx, extractorSource, config }) => {
|
|
583
|
+
const scopedHtml = await this.explorer.withPage((page) => page.evaluate(({ eidx, extractorSource, config }) => {
|
|
601
584
|
const extract = new Function(`return ${extractorSource}`)();
|
|
602
585
|
return extract(eidx, config);
|
|
603
586
|
}, {
|
|
@@ -608,7 +591,7 @@ export class Driller extends TaskAgent {
|
|
|
608
591
|
interactiveControlSelector: HTML_SELECTORS.interactiveControl,
|
|
609
592
|
limits: HTML_EXTRACTION_LIMITS,
|
|
610
593
|
},
|
|
611
|
-
});
|
|
594
|
+
}));
|
|
612
595
|
if (scopedHtml)
|
|
613
596
|
return scopedHtml;
|
|
614
597
|
return await originalState.combinedHtml();
|
|
@@ -667,12 +650,6 @@ export class Driller extends TaskAgent {
|
|
|
667
650
|
tag('step').log(` ${status} ${componentTest.component?.name || test.scenario}`);
|
|
668
651
|
}
|
|
669
652
|
}
|
|
670
|
-
getCurrentPlan() {
|
|
671
|
-
return this.currentPlan;
|
|
672
|
-
}
|
|
673
|
-
getConversation() {
|
|
674
|
-
return this.currentConversation;
|
|
675
|
-
}
|
|
676
653
|
}
|
|
677
654
|
function formatAriaNode(node) {
|
|
678
655
|
const role = typeof node.role === 'string' ? node.role : 'unknown';
|
|
@@ -1105,4 +1082,3 @@ function isInteractiveElement(element) {
|
|
|
1105
1082
|
return true;
|
|
1106
1083
|
return false;
|
|
1107
1084
|
}
|
|
1108
|
-
const drillLocatorRule = locatorRule.replace(/<context_simplification>[\s\S]*?<\/context_simplification>/, '').trim();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ExperienceFile, type ExperienceTracker } from '../experience-tracker.js';
|
|
2
2
|
import type { Agent } from './agent.js';
|
|
3
3
|
import type { Provider } from './provider.js';
|
|
4
|
-
export
|
|
4
|
+
export declare const COMPACT_MAX_LENGTH = 5000;
|
|
5
5
|
interface MergeGroup {
|
|
6
6
|
pattern: string;
|
|
7
7
|
files: ExperienceFile[];
|
|
@@ -10,7 +10,6 @@ export declare class ExperienceCompactor implements Agent {
|
|
|
10
10
|
emoji: string;
|
|
11
11
|
provider: Provider;
|
|
12
12
|
experienceTracker: ExperienceTracker;
|
|
13
|
-
MAX_LENGTH: number;
|
|
14
13
|
constructor(provider: Provider, experienceTracker: ExperienceTracker);
|
|
15
14
|
compactExperience(experience: string): Promise<string>;
|
|
16
15
|
stripNonUsefulEntries(content: string): string;
|
|
@@ -51,3 +50,4 @@ export declare class ExperienceCompactor implements Agent {
|
|
|
51
50
|
title?: string;
|
|
52
51
|
}): string;
|
|
53
52
|
}
|
|
53
|
+
export {};
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
import { unlinkSync } from 'node:fs';
|
|
2
|
+
import { basename } from 'node:path';
|
|
2
3
|
import dedent from 'dedent';
|
|
3
|
-
import { marked } from 'marked';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { RECENT_WINDOW_DAYS } from '../experience-tracker.js';
|
|
6
6
|
import { Observability } from '../observability.js';
|
|
7
7
|
import { createDebug, log, tag } from '../utils/logger.js';
|
|
8
8
|
import { mdq } from '../utils/markdown-query.js';
|
|
9
|
+
import { isNonReusableCode } from '../utils/step-analyzer.js';
|
|
9
10
|
import { generalizeUrl, hasDynamicUrlSegment } from '../utils/url-matcher.js';
|
|
10
11
|
const debugLog = createDebug('explorbot:experience-compactor');
|
|
12
|
+
export const COMPACT_MAX_LENGTH = 5000;
|
|
11
13
|
export class ExperienceCompactor {
|
|
12
14
|
emoji = '🗜️';
|
|
13
15
|
provider;
|
|
14
16
|
experienceTracker;
|
|
15
|
-
MAX_LENGTH = 5000;
|
|
16
17
|
constructor(provider, experienceTracker) {
|
|
17
18
|
this.provider = provider;
|
|
18
19
|
this.experienceTracker = experienceTracker;
|
|
19
20
|
}
|
|
20
21
|
async compactExperience(experience) {
|
|
21
22
|
const stripped = this.stripNonUsefulEntries(experience);
|
|
22
|
-
if (stripped.length <
|
|
23
|
+
if (stripped.length < COMPACT_MAX_LENGTH) {
|
|
23
24
|
return stripped;
|
|
24
25
|
}
|
|
25
26
|
const prompt = this.buildCompactionPrompt(stripped);
|
|
@@ -101,7 +102,7 @@ export class ExperienceCompactor {
|
|
|
101
102
|
const generalized = generalizeUrl(url);
|
|
102
103
|
if (generalized === url)
|
|
103
104
|
continue;
|
|
104
|
-
const stateHash = file.filePath
|
|
105
|
+
const stateHash = basename(file.filePath, '.md');
|
|
105
106
|
const newData = { ...file.data, url: `~${generalized}~`, mergedFrom: [url] };
|
|
106
107
|
this.experienceTracker.writeExperienceFile(stateHash, file.content, newData);
|
|
107
108
|
tag('substep').log(`Generalized URL: ${url} → ~${generalized}~`);
|
|
@@ -110,7 +111,7 @@ export class ExperienceCompactor {
|
|
|
110
111
|
return count;
|
|
111
112
|
}
|
|
112
113
|
async compactFiles(files, options) {
|
|
113
|
-
const workingSet = options?.skipSmall ? files.filter((f) => f.content.length >=
|
|
114
|
+
const workingSet = options?.skipSmall ? files.filter((f) => f.content.length >= COMPACT_MAX_LENGTH) : files;
|
|
114
115
|
let compactedCount = 0;
|
|
115
116
|
const total = workingSet.length;
|
|
116
117
|
const aiReviewCount = workingSet.filter((f) => this.isRecent(f)).length;
|
|
@@ -119,7 +120,7 @@ export class ExperienceCompactor {
|
|
|
119
120
|
}
|
|
120
121
|
for (let i = 0; i < workingSet.length; i++) {
|
|
121
122
|
const experience = workingSet[i];
|
|
122
|
-
const shortName = experience.filePath
|
|
123
|
+
const shortName = basename(experience.filePath);
|
|
123
124
|
const willReview = this.isRecent(experience);
|
|
124
125
|
if (total > 1 && willReview) {
|
|
125
126
|
tag('substep').log(`[${i + 1}/${total}] reviewing ${shortName}`);
|
|
@@ -128,9 +129,9 @@ export class ExperienceCompactor {
|
|
|
128
129
|
if (willReview) {
|
|
129
130
|
content = await this.reviewExperienceQuality(content, experience.data);
|
|
130
131
|
}
|
|
131
|
-
if (content.length >=
|
|
132
|
+
if (content.length >= COMPACT_MAX_LENGTH) {
|
|
132
133
|
if (total > 1)
|
|
133
|
-
tag('substep').log(`[${i + 1}/${total}] compacting ${shortName} (over ${
|
|
134
|
+
tag('substep').log(`[${i + 1}/${total}] compacting ${shortName} (over ${COMPACT_MAX_LENGTH} chars)`);
|
|
134
135
|
const prompt = this.buildCompactionPrompt(content);
|
|
135
136
|
const model = this.provider.getModelForAgent('experience-compactor');
|
|
136
137
|
const response = await this.provider.chat([
|
|
@@ -141,7 +142,7 @@ export class ExperienceCompactor {
|
|
|
141
142
|
}
|
|
142
143
|
if (content === experience.content)
|
|
143
144
|
continue;
|
|
144
|
-
const stateHash = experience.filePath
|
|
145
|
+
const stateHash = basename(experience.filePath, '.md');
|
|
145
146
|
this.experienceTracker.writeExperienceFile(stateHash, content, experience.data);
|
|
146
147
|
debugLog('Experience file compacted:', experience.filePath);
|
|
147
148
|
compactedCount++;
|
|
@@ -250,7 +251,7 @@ export class ExperienceCompactor {
|
|
|
250
251
|
async mergeExperienceGroup(group) {
|
|
251
252
|
const [targetFile, ...sourceFiles] = group.files;
|
|
252
253
|
const combinedContent = group.files.map((f) => f.content).join('\n\n---\n\n');
|
|
253
|
-
const targetStateHash = targetFile.filePath
|
|
254
|
+
const targetStateHash = basename(targetFile.filePath, '.md');
|
|
254
255
|
const newFrontmatter = {
|
|
255
256
|
...targetFile.data,
|
|
256
257
|
url: group.pattern,
|
|
@@ -283,7 +284,7 @@ export class ExperienceCompactor {
|
|
|
283
284
|
<rules>
|
|
284
285
|
- Use markdown h2 headers only (##) - NO XML tags or wrappers in output
|
|
285
286
|
- Merge similar flows to remove duplicates
|
|
286
|
-
- Keep output under ${
|
|
287
|
+
- Keep output under ${COMPACT_MAX_LENGTH} characters
|
|
287
288
|
- Be explicit and short - no proposals or explanations
|
|
288
289
|
|
|
289
290
|
KEEP only:
|
|
@@ -368,27 +369,9 @@ export class ExperienceCompactor {
|
|
|
368
369
|
}
|
|
369
370
|
}
|
|
370
371
|
function listSections(content) {
|
|
371
|
-
const
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
let currentRaw = '';
|
|
375
|
-
const flush = () => {
|
|
376
|
-
if (currentHeading !== null)
|
|
377
|
-
sections.push({ title: currentHeading, raw: currentRaw });
|
|
378
|
-
};
|
|
379
|
-
for (const token of tokens) {
|
|
380
|
-
const raw = token.raw || '';
|
|
381
|
-
if (token.type === 'heading' && token.depth === 2) {
|
|
382
|
-
flush();
|
|
383
|
-
currentHeading = token.text.trim();
|
|
384
|
-
currentRaw = raw;
|
|
385
|
-
continue;
|
|
386
|
-
}
|
|
387
|
-
if (currentHeading !== null)
|
|
388
|
-
currentRaw += raw;
|
|
389
|
-
}
|
|
390
|
-
flush();
|
|
391
|
-
return sections;
|
|
372
|
+
const sections = mdq(content).query('section2').each();
|
|
373
|
+
const metas = mdq(content).query('section2').meta();
|
|
374
|
+
return sections.map((q, i) => ({ title: metas[i].text.trim(), raw: q.text() }));
|
|
392
375
|
}
|
|
393
376
|
function dropEmptySections(content) {
|
|
394
377
|
let result = content;
|
|
@@ -408,7 +391,7 @@ function dropNonReusableSections(content) {
|
|
|
408
391
|
const sections = [...mdq(result).query('section2(~"FLOW:")').each(), ...mdq(result).query('section2(~"ACTION:")').each()];
|
|
409
392
|
for (const section of sections) {
|
|
410
393
|
const raw = section.text();
|
|
411
|
-
if (
|
|
394
|
+
if (!isNonReusableCode(raw))
|
|
412
395
|
continue;
|
|
413
396
|
result = result.replace(raw, '');
|
|
414
397
|
}
|
|
@@ -136,7 +136,7 @@ function extractKeyFields(body, result = {}, depth = 0) {
|
|
|
136
136
|
return result;
|
|
137
137
|
}
|
|
138
138
|
for (const [key, value] of Object.entries(body)) {
|
|
139
|
-
if (value
|
|
139
|
+
if (value == null)
|
|
140
140
|
continue;
|
|
141
141
|
if (typeof value === 'object') {
|
|
142
142
|
extractKeyFields(value, result, depth + 1);
|
|
@@ -18,7 +18,6 @@ export declare class Fisherman implements Agent {
|
|
|
18
18
|
hasApiConfig: boolean;
|
|
19
19
|
constructor(provider: Provider, apiClient: ApiClient, requestStore: RequestStore, specLoader: () => Promise<any | null>, baseEndpoint: string, cookieProvider: () => Promise<Record<string, string>>, configHeaders?: Record<string, string>, hasApiConfig?: boolean);
|
|
20
20
|
isAvailable(): boolean;
|
|
21
|
-
getMode(): string;
|
|
22
21
|
ensureReady(scopeUrl?: string): Promise<void>;
|
|
23
22
|
getEndpointList(scopeUrl?: string): string;
|
|
24
23
|
prepareData(instructions: string, scopeUrl?: string, sessionName?: string): Promise<FishermanResult>;
|
package/dist/src/ai/fisherman.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { StateManager } from '../../state-manager.js';
|
|
1
2
|
import type { Plan } from '../../test-plan.js';
|
|
2
3
|
import type { Conversation } from '../conversation.js';
|
|
3
4
|
import type { Constructor } from './mixin.js';
|
|
@@ -8,6 +9,7 @@ export interface CodeceptJSMethods {
|
|
|
8
9
|
export declare function WithCodeceptJS<T extends Constructor>(Base: T): {
|
|
9
10
|
new (...args: any[]): {
|
|
10
11
|
savedFiles: Set<string>;
|
|
12
|
+
stateManager?: StateManager;
|
|
11
13
|
toCode(conversation: Conversation, scenario: string): string;
|
|
12
14
|
saveCodeceptPlanToFile(plan: Plan): string;
|
|
13
15
|
getKnowledgeLines(url: string, indent?: string): string[];
|
|
@@ -2,11 +2,10 @@ import { mkdirSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { ActionResult } from "../../action-result.js";
|
|
4
4
|
import { ConfigParser } from "../../config.js";
|
|
5
|
-
import { KnowledgeTracker } from "../../knowledge-tracker.js";
|
|
6
5
|
import { tag } from "../../utils/logger.js";
|
|
7
6
|
import { relativeToCwd } from "../../utils/next-steps.js";
|
|
8
|
-
import { safeFilename } from "../../utils/strings.js";
|
|
9
7
|
import { CODECEPT_TOOLS, isNonReusableCode, stripComments } from "../../utils/step-analyzer.js";
|
|
8
|
+
import { safeFilename } from "../../utils/strings.js";
|
|
10
9
|
import { ASSERTION_TOOLS } from "../tools.js";
|
|
11
10
|
import { escapeString, getExecutionLabel } from "./utils.js";
|
|
12
11
|
export function WithCodeceptJS(Base) {
|
|
@@ -87,7 +86,9 @@ export function WithCodeceptJS(Base) {
|
|
|
87
86
|
return filePath;
|
|
88
87
|
}
|
|
89
88
|
getKnowledgeLines(url, indent = ' ') {
|
|
90
|
-
const knowledgeTracker =
|
|
89
|
+
const knowledgeTracker = this.stateManager?.getKnowledgeTracker();
|
|
90
|
+
if (!knowledgeTracker)
|
|
91
|
+
return [];
|
|
91
92
|
const state = new ActionResult({ url });
|
|
92
93
|
const { wait, waitForElement, code } = knowledgeTracker.getStateParameters(state, ['wait', 'waitForElement', 'code']);
|
|
93
94
|
const lines = [];
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type Explorer from '../../explorer.js';
|
|
1
2
|
import { type PlaywrightRecorder } from '../../playwright-recorder.js';
|
|
3
|
+
import type { StateManager } from '../../state-manager.js';
|
|
2
4
|
import type { Plan } from '../../test-plan.js';
|
|
3
5
|
import type { Conversation } from '../conversation.js';
|
|
4
6
|
import type { Constructor } from './mixin.js';
|
|
@@ -10,9 +12,10 @@ export declare function WithPlaywright<T extends Constructor>(Base: T): {
|
|
|
10
12
|
new (...args: any[]): {
|
|
11
13
|
playwright: {
|
|
12
14
|
recorder: PlaywrightRecorder;
|
|
13
|
-
|
|
15
|
+
explorer: Explorer;
|
|
14
16
|
} | undefined;
|
|
15
17
|
savedFiles: Set<string>;
|
|
18
|
+
stateManager?: StateManager;
|
|
16
19
|
toPlaywrightCode(conversation: Conversation, scenario: string): Promise<string>;
|
|
17
20
|
savePlaywrightPlanToFile(plan: Plan): string;
|
|
18
21
|
getPlaywrightKnowledgeLines(url: string, indent?: string): string[];
|
|
@@ -2,12 +2,11 @@ import { mkdirSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { ActionResult } from "../../action-result.js";
|
|
4
4
|
import { ConfigParser } from "../../config.js";
|
|
5
|
-
import { KnowledgeTracker } from "../../knowledge-tracker.js";
|
|
6
5
|
import { renderAssertion, renderCall } from "../../playwright-recorder.js";
|
|
7
6
|
import { tag } from "../../utils/logger.js";
|
|
8
7
|
import { relativeToCwd } from "../../utils/next-steps.js";
|
|
9
|
-
import { safeFilename } from "../../utils/strings.js";
|
|
10
8
|
import { CODECEPT_TOOLS } from "../../utils/step-analyzer.js";
|
|
9
|
+
import { safeFilename } from "../../utils/strings.js";
|
|
11
10
|
import { ASSERTION_TOOLS } from "../tools.js";
|
|
12
11
|
import { escapeString, getExecutionLabel } from "./utils.js";
|
|
13
12
|
const PLAYWRIGHT_EMITTED_TOOLS = [...CODECEPT_TOOLS, ...ASSERTION_TOOLS];
|
|
@@ -124,7 +123,9 @@ export function WithPlaywright(Base) {
|
|
|
124
123
|
return filePath;
|
|
125
124
|
}
|
|
126
125
|
getPlaywrightKnowledgeLines(url, indent = ' ') {
|
|
127
|
-
const knowledgeTracker =
|
|
126
|
+
const knowledgeTracker = this.stateManager?.getKnowledgeTracker();
|
|
127
|
+
if (!knowledgeTracker)
|
|
128
|
+
return [];
|
|
128
129
|
const state = new ActionResult({ url });
|
|
129
130
|
const { wait, waitForElement } = knowledgeTracker.getStateParameters(state, ['wait', 'waitForElement']);
|
|
130
131
|
const lines = [];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExplorbotConfig } from '../../config.js';
|
|
2
|
+
import type Explorer from '../../explorer.js';
|
|
2
3
|
import type { PlaywrightRecorder } from '../../playwright-recorder.js';
|
|
3
4
|
import { type Constructor } from './mixin.js';
|
|
4
5
|
export interface ScreencastMethods {
|
|
@@ -12,7 +13,7 @@ export declare function WithScreencast<T extends Constructor>(Base: T): {
|
|
|
12
13
|
savedFiles: Set<string>;
|
|
13
14
|
playwright: {
|
|
14
15
|
recorder: PlaywrightRecorder;
|
|
15
|
-
|
|
16
|
+
explorer: Explorer;
|
|
16
17
|
} | undefined;
|
|
17
18
|
screencastPage: any;
|
|
18
19
|
screencastActive: boolean;
|
|
@@ -27,7 +27,7 @@ export function WithScreencast(Base) {
|
|
|
27
27
|
return;
|
|
28
28
|
if (!this.config?.ai?.agents?.historian?.screencast)
|
|
29
29
|
return;
|
|
30
|
-
if (!this.playwright?.
|
|
30
|
+
if (!this.playwright?.explorer)
|
|
31
31
|
return;
|
|
32
32
|
this.onTestBefore = (test) => {
|
|
33
33
|
void this.startScreencast(test);
|
|
@@ -46,7 +46,7 @@ export function WithScreencast(Base) {
|
|
|
46
46
|
async startScreencast(test) {
|
|
47
47
|
if (this.screencastActive)
|
|
48
48
|
return;
|
|
49
|
-
const page = this.playwright?.
|
|
49
|
+
const page = this.playwright?.explorer?.page;
|
|
50
50
|
if (!page?.screencast?.start)
|
|
51
51
|
return;
|
|
52
52
|
const task = test?._explorbotTest;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import type { ToolExecution } from '../conversation.js';
|
|
2
|
-
export { isNonReusableCode, stripComments } from '../../utils/step-analyzer.js';
|
|
3
2
|
export declare function escapeString(str: string): string;
|
|
4
3
|
export declare function getExecutionLabel(exec: ToolExecution, fallback?: string): string;
|