explorbot 0.1.32-beta.1 → 0.2.0
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 +14 -14
- package/bin/explorbot-cli.ts +4 -3
- package/boat/doc-collector/src/ai/documentarian.ts +22 -27
- package/boat/doc-collector/src/ai/tools.ts +67 -10
- package/boat/doc-collector/src/docbot.ts +28 -7
- package/boat/doc-collector/src/docs-renderer.ts +19 -24
- package/boat/doc-collector/src/screenshots.ts +33 -1
- package/boat/doc-collector/src/state-diagram.ts +281 -0
- package/dist/bin/explorbot-cli.js +3 -3
- package/dist/boat/doc-collector/src/ai/documentarian.js +16 -6
- package/dist/boat/doc-collector/src/ai/tools.js +47 -10
- package/dist/boat/doc-collector/src/docbot.js +25 -6
- package/dist/boat/doc-collector/src/docs-renderer.js +18 -3
- package/dist/boat/doc-collector/src/screenshots.js +29 -0
- package/dist/boat/doc-collector/src/state-diagram.js +200 -0
- package/dist/package.json +20 -3
- package/dist/src/action-result.d.ts +8 -10
- package/dist/src/action-result.js +72 -164
- package/dist/src/action.d.ts +3 -10
- package/dist/src/action.js +18 -111
- package/dist/src/ai/captain/web-mode.js +0 -7
- package/dist/src/ai/captain.d.ts +1 -3
- package/dist/src/ai/captain.js +4 -19
- package/dist/src/ai/conversation.d.ts +2 -5
- package/dist/src/ai/conversation.js +9 -28
- package/dist/src/ai/driller.d.ts +0 -4
- package/dist/src/ai/driller.js +3 -12
- 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 +3 -2
- package/dist/src/ai/historian/playwright.d.ts +2 -0
- package/dist/src/ai/historian/playwright.js +3 -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 +5 -4
- package/dist/src/ai/historian.js +1 -5
- package/dist/src/ai/navigator.d.ts +3 -5
- package/dist/src/ai/navigator.js +11 -46
- package/dist/src/ai/pilot.d.ts +1 -3
- package/dist/src/ai/pilot.js +6 -17
- package/dist/src/ai/planner.d.ts +3 -3
- package/dist/src/ai/planner.js +49 -37
- package/dist/src/ai/provider.d.ts +4 -1
- package/dist/src/ai/provider.js +77 -136
- package/dist/src/ai/quartermaster.d.ts +21 -22
- package/dist/src/ai/quartermaster.js +4 -10
- package/dist/src/ai/rerunner.d.ts +1 -2
- package/dist/src/ai/rerunner.js +14 -22
- package/dist/src/ai/researcher/cache.js +11 -18
- package/dist/src/ai/researcher/deep-analysis.js +3 -6
- 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.js +5 -1
- package/dist/src/ai/researcher.js +12 -53
- 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 +1 -1
- package/dist/src/ai/task-agent.js +2 -27
- package/dist/src/ai/tester.d.ts +0 -2
- package/dist/src/ai/tester.js +10 -61
- package/dist/src/ai/tools.d.ts +18 -7
- package/dist/src/ai/tools.js +119 -101
- package/dist/src/commands/compact-command.d.ts +1 -1
- package/dist/src/commands/compact-command.js +6 -6
- package/dist/src/commands/context-command.js +1 -1
- package/dist/src/commands/context-knowledge-command.js +1 -1
- package/dist/src/commands/experience-command.js +1 -1
- package/dist/src/commands/knows-command.js +1 -1
- package/dist/src/commands/learn-command.js +3 -1
- package/dist/src/components/AddKnowledge.d.ts +2 -0
- package/dist/src/components/AddKnowledge.js +3 -7
- package/dist/src/components/App.js +2 -2
- package/dist/src/config.d.ts +2 -0
- package/dist/src/config.js +12 -0
- package/dist/src/experience-tracker.d.ts +11 -6
- package/dist/src/experience-tracker.js +81 -154
- package/dist/src/explorbot.d.ts +4 -5
- package/dist/src/explorbot.js +17 -51
- package/dist/src/explorer.d.ts +4 -15
- package/dist/src/explorer.js +18 -68
- package/dist/src/knowledge-tracker.d.ts +2 -2
- package/dist/src/knowledge-tracker.js +38 -45
- package/dist/src/state-manager.d.ts +6 -31
- package/dist/src/state-manager.js +10 -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/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/web-element.d.ts +2 -1
- package/dist/src/utils/web-element.js +3 -0
- package/dist/src/utils/web-sandbox.d.ts +4 -0
- package/dist/src/utils/web-sandbox.js +37 -0
- package/package.json +20 -3
- package/src/action-result.ts +70 -173
- package/src/action.ts +16 -130
- package/src/ai/captain/web-mode.ts +0 -7
- package/src/ai/captain.ts +5 -17
- package/src/ai/conversation.ts +9 -32
- package/src/ai/driller.ts +3 -16
- 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 +4 -2
- package/src/ai/historian/playwright.ts +4 -2
- package/src/ai/historian/utils.ts +0 -1
- package/src/ai/historian.ts +4 -7
- package/src/ai/navigator.ts +13 -49
- package/src/ai/pilot.ts +6 -16
- package/src/ai/planner.ts +56 -44
- package/src/ai/provider.ts +85 -145
- package/src/ai/quartermaster.ts +34 -41
- package/src/ai/rerunner.ts +17 -21
- package/src/ai/researcher/cache.ts +10 -16
- package/src/ai/researcher/deep-analysis.ts +3 -6
- package/src/ai/researcher/parser.ts +1 -1
- package/src/ai/researcher/research-result.ts +0 -18
- package/src/ai/researcher/sections.ts +5 -1
- package/src/ai/researcher.ts +11 -59
- package/src/ai/rules.ts +24 -13
- package/src/ai/session-analyst.ts +8 -5
- package/src/ai/task-agent.ts +3 -30
- package/src/ai/tester.ts +12 -73
- package/src/ai/tools.ts +119 -106
- package/src/commands/compact-command.ts +7 -8
- package/src/commands/context-command.ts +1 -1
- package/src/commands/context-knowledge-command.ts +1 -1
- package/src/commands/experience-command.ts +1 -1
- package/src/commands/knows-command.ts +1 -1
- package/src/commands/learn-command.ts +3 -1
- package/src/components/AddKnowledge.tsx +5 -7
- package/src/components/App.tsx +2 -2
- package/src/config.ts +12 -0
- package/src/experience-tracker.ts +88 -151
- package/src/explorbot.ts +17 -51
- package/src/explorer.ts +20 -72
- package/src/knowledge-tracker.ts +42 -49
- package/src/state-manager.ts +15 -156
- 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/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/web-element.ts +4 -0
- package/src/utils/web-sandbox.ts +43 -0
- package/dist/boat/api-tester/example/apibot.config.js +0 -30
|
@@ -74,7 +74,6 @@ export function WithDeepAnalysis(Base) {
|
|
|
74
74
|
return null;
|
|
75
75
|
}
|
|
76
76
|
const diff = await current.diff(previous);
|
|
77
|
-
await diff.calculate();
|
|
78
77
|
if (!diff.ariaChanged && diff.htmlParts.length === 0) {
|
|
79
78
|
debugLog(`No diff between current and previous state for overlay "${focusArea.name}"`);
|
|
80
79
|
return null;
|
|
@@ -312,12 +311,11 @@ export function WithDeepAnalysis(Base) {
|
|
|
312
311
|
const isCoordinateClick = el.commands[0].startsWith('I.clickXY(');
|
|
313
312
|
if (!isCoordinateClick) {
|
|
314
313
|
const hoverCmd = el.commands[0].replace('I.click(', 'I.moveCursorTo(');
|
|
315
|
-
await this.explorer.attemptAction(hoverCmd, undefined
|
|
314
|
+
await this.explorer.attemptAction(hoverCmd, undefined);
|
|
316
315
|
await new Promise((r) => setTimeout(r, 500));
|
|
317
316
|
await this.explorer.capturePageState();
|
|
318
317
|
const hoverAR = ActionResult.fromState(this.stateManager.getCurrentState());
|
|
319
318
|
const hoverDiff = await hoverAR.diff(previousState);
|
|
320
|
-
await hoverDiff.calculate();
|
|
321
319
|
const hoverHtmlSize = hoverDiff.htmlParts.reduce((sum, p) => sum + p.subtree.length, 0);
|
|
322
320
|
const hoverRevealed = hoverDiff.ariaChanged && hoverHtmlSize > 500;
|
|
323
321
|
if (hoverRevealed) {
|
|
@@ -355,7 +353,7 @@ export function WithDeepAnalysis(Base) {
|
|
|
355
353
|
let clickCode = null;
|
|
356
354
|
const action = this.explorer.createAction();
|
|
357
355
|
for (const cmd of commands) {
|
|
358
|
-
if (await action.attempt(cmd, undefined
|
|
356
|
+
if (await action.attempt(cmd, undefined)) {
|
|
359
357
|
clickCode = cmd;
|
|
360
358
|
break;
|
|
361
359
|
}
|
|
@@ -370,7 +368,6 @@ export function WithDeepAnalysis(Base) {
|
|
|
370
368
|
await this.explorer.createAction().capturePageState();
|
|
371
369
|
const currAR = ActionResult.fromState(this.stateManager.getCurrentState());
|
|
372
370
|
diff = await currAR.diff(previousState);
|
|
373
|
-
await diff.calculate();
|
|
374
371
|
}
|
|
375
372
|
catch (err) {
|
|
376
373
|
tag('warning').log(`State capture failed after click: ${err instanceof Error ? err.message : err}`);
|
|
@@ -399,7 +396,7 @@ export function WithDeepAnalysis(Base) {
|
|
|
399
396
|
await this.cancelInUi();
|
|
400
397
|
await this.explorer.capturePageState();
|
|
401
398
|
const currentAria = this.stateManager.getCurrentState()?.ariaSnapshot || '';
|
|
402
|
-
if (!diffAriaSnapshots(originalAria, currentAria))
|
|
399
|
+
if (!diffAriaSnapshots(originalAria, currentAria).text)
|
|
403
400
|
return;
|
|
404
401
|
}
|
|
405
402
|
catch (err) {
|
|
@@ -69,7 +69,7 @@ export function extractContainerFromBlockquote(sectionMarkdown) {
|
|
|
69
69
|
return css;
|
|
70
70
|
}
|
|
71
71
|
export function parseResearchSections(markdown) {
|
|
72
|
-
const hasExtendedResearch = markdown.
|
|
72
|
+
const hasExtendedResearch = mdq(markdown).query('section1(~"Extended Research")').count() > 0;
|
|
73
73
|
return parseSections(markdown)
|
|
74
74
|
.filter((s) => !SKIP_SECTIONS.has(s.name.toLowerCase()) && !s.name.toLowerCase().includes('data:'))
|
|
75
75
|
.map((section) => {
|
|
@@ -9,7 +9,6 @@ export declare class ResearchResult {
|
|
|
9
9
|
parseLocators(): void;
|
|
10
10
|
get containers(): string[];
|
|
11
11
|
get containerLocators(): Locator[];
|
|
12
|
-
updateSection(sectionName: string, locators: Locator[]): void;
|
|
13
12
|
rebuildSectionInText(section: ResearchSection): void;
|
|
14
13
|
cleanup(): void;
|
|
15
14
|
reorderColumns(columns: string[]): string[];
|
|
@@ -42,25 +42,6 @@ export class ResearchResult {
|
|
|
42
42
|
pwLocator: null,
|
|
43
43
|
}));
|
|
44
44
|
}
|
|
45
|
-
updateSection(sectionName, locators) {
|
|
46
|
-
const sections = parseResearchSections(this.text);
|
|
47
|
-
const section = sections.find((s) => s.name === sectionName);
|
|
48
|
-
if (!section)
|
|
49
|
-
return;
|
|
50
|
-
for (const el of section.elements) {
|
|
51
|
-
const elLocators = locators.filter((l) => l.element === el.name);
|
|
52
|
-
for (const loc of elLocators) {
|
|
53
|
-
const value = loc.valid === false ? null : loc.locator || null;
|
|
54
|
-
if (loc.type === 'css')
|
|
55
|
-
el.css = value;
|
|
56
|
-
if (loc.type === 'xpath')
|
|
57
|
-
el.xpath = value;
|
|
58
|
-
if (loc.type === 'aria')
|
|
59
|
-
el.aria = value ? parseAriaLocator(value) : null;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
this.rebuildSectionInText(section);
|
|
63
|
-
}
|
|
64
45
|
rebuildSectionInText(section) {
|
|
65
46
|
if (section.elements.length === 0)
|
|
66
47
|
return;
|
|
@@ -89,9 +89,13 @@ export function WithSections(Base) {
|
|
|
89
89
|
</section_format>
|
|
90
90
|
|
|
91
91
|
<rules>
|
|
92
|
-
-
|
|
92
|
+
- List only elements physically inside this section's declared container.
|
|
93
|
+
- Do not copy global toolbar, navigation, list, or detail elements into this section unless they are descendants of this section container.
|
|
94
|
+
- Every element with eidx inside this section's container MUST appear in the table.
|
|
93
95
|
- Every row needs CSS; ARIA may be "-" for icon-only buttons.
|
|
94
96
|
- ARIA locator JSON uses keys "role" and "text" (NOT "name").
|
|
97
|
+
- Elements marked data-explorbot-hit="covered" or "offscreen" are not directly actionable; describe the covering or focused UI first.
|
|
98
|
+
- In split-pane pages, entity detail panels are active detail context; include close/back/pin controls in the detail panel section when present.
|
|
95
99
|
</rules>
|
|
96
100
|
|
|
97
101
|
${generalLocatorRuleText}
|
|
@@ -9,7 +9,7 @@ import { diffAriaSnapshots } from "../utils/aria.js";
|
|
|
9
9
|
import { ErrorPageError, detectPageCondition } from "../utils/error-page.js";
|
|
10
10
|
import { HooksRunner } from "../utils/hooks-runner.js";
|
|
11
11
|
import { isBodyEmpty } from "../utils/html.js";
|
|
12
|
-
import { createDebug,
|
|
12
|
+
import { createDebug, tag } from '../utils/logger.js';
|
|
13
13
|
import { mdq } from "../utils/markdown-query.js";
|
|
14
14
|
import { RulesLoader } from "../utils/rules-loader.js";
|
|
15
15
|
import { ContextLengthError } from './provider.js';
|
|
@@ -261,12 +261,10 @@ export class Researcher extends ResearcherBase {
|
|
|
261
261
|
if (stateHash) {
|
|
262
262
|
researchFile = saveResearch(stateHash, result.text, combinedHtml);
|
|
263
263
|
}
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
this.experienceTracker.updateSummary(this.actionResult, summaryLine);
|
|
269
|
-
}
|
|
264
|
+
const summaryText = mdq(result.text).query('section2(/^summary/)').query('paragraph[0]').text().trim();
|
|
265
|
+
const summaryLine = summaryText.split('\n')[0]?.trim().slice(0, 200);
|
|
266
|
+
if (summaryLine)
|
|
267
|
+
this.experienceTracker.updateSummary(this.actionResult, summaryLine);
|
|
270
268
|
tag('multiline').log(formatResearchSummary(result.text, { visionUsed: this.hasScreenshotToAnalyze }));
|
|
271
269
|
tag('success').log('Research complete');
|
|
272
270
|
if (researchFile)
|
|
@@ -365,7 +363,10 @@ export class Researcher extends ResearcherBase {
|
|
|
365
363
|
- Research all menus and navigational areas.
|
|
366
364
|
- Ignore decorative sidebars, footer-only links, and external links.
|
|
367
365
|
- Detect layout patterns: list/detail split, 2-pane, or 3-pane layouts.
|
|
368
|
-
-
|
|
366
|
+
- In split-pane layouts, keep global toolbar, list pane, and detail pane as separate sections. Do not copy toolbar/list elements into the detail section unless they are physically inside that detail container.
|
|
367
|
+
- If the URL or page state opens an entity detail panel, describe that panel as the active detail context and include its close/back/pin controls when present.
|
|
368
|
+
- If an element has data-explorbot-hit="covered" or "offscreen", do not present it as directly actionable. Prefer the overlay, drawer, dialog, or focused section covering it, and mention what must be dismissed or revealed first.
|
|
369
|
+
- Every element with an eidx attribute MUST appear in exactly one matching UI map section — describe icon-only buttons by their visual role.
|
|
369
370
|
- Every UI map row needs a CSS selector; ARIA may be "-" for icon-only buttons, CSS must never be "-".
|
|
370
371
|
- ARIA locator JSON uses keys "role" and "text" (NOT "name").
|
|
371
372
|
- Mark elements with likely hover interactions (title, aria-describedby, menu items with submenus) as "(hover)".
|
|
@@ -382,6 +383,7 @@ export class Researcher extends ResearcherBase {
|
|
|
382
383
|
.join('\n')}
|
|
383
384
|
|
|
384
385
|
- Sections can overlap; prefer more detailed sections over broader ones.
|
|
386
|
+
- Section tables must list only elements physically inside the declared container. If a control belongs to a global toolbar, list it only in the toolbar section.
|
|
385
387
|
- Never name a section "Focus" or "Focused" — use what it contains (Detail, Modal, Form, Content, List).
|
|
386
388
|
- Omit sections that are not present or not relevant.
|
|
387
389
|
- Each section needs a container CSS locator; UI map CSS locators are relative to it.
|
|
@@ -402,21 +404,7 @@ export class Researcher extends ResearcherBase {
|
|
|
402
404
|
if (!this.actionResult)
|
|
403
405
|
throw new Error('actionResult is not set');
|
|
404
406
|
const html = await this.actionResult.combinedHtml();
|
|
405
|
-
const
|
|
406
|
-
let knowledge = '';
|
|
407
|
-
if (knowledgeFiles.length > 0) {
|
|
408
|
-
const knowledgeContent = knowledgeFiles
|
|
409
|
-
.map((k) => k.content)
|
|
410
|
-
.filter((k) => !!k)
|
|
411
|
-
.join('\n\n');
|
|
412
|
-
tag('operation').log(`Found ${knowledgeFiles.length} relevant knowledge ${pluralize(knowledgeFiles.length, 'file')} for: ${this.actionResult.url}`);
|
|
413
|
-
knowledge = `
|
|
414
|
-
<hint>
|
|
415
|
-
Here is relevant knowledge for this page:
|
|
416
|
-
|
|
417
|
-
${knowledgeContent}
|
|
418
|
-
</hint>`;
|
|
419
|
-
}
|
|
407
|
+
const knowledge = this.explorer.getKnowledgeTracker().renderRelevantKnowledge(this.actionResult);
|
|
420
408
|
const ariaSnapshot = this.actionResult.getCompactARIA();
|
|
421
409
|
return dedent `
|
|
422
410
|
Analyze this web page and provide a comprehensive research report in markdown format.
|
|
@@ -471,35 +459,6 @@ export class Researcher extends ResearcherBase {
|
|
|
471
459
|
|
|
472
460
|
`;
|
|
473
461
|
}
|
|
474
|
-
async textContent(state) {
|
|
475
|
-
const actionResult = ActionResult.fromState(state);
|
|
476
|
-
const html = await actionResult.combinedHtml();
|
|
477
|
-
const prompt = dedent `
|
|
478
|
-
Transform into markdown.
|
|
479
|
-
Identify headers, footers, asides, special application parts and main contant.
|
|
480
|
-
Content should be in markdown format. If it is content: tables must be tables, lists must be lists.
|
|
481
|
-
Navigation elements should be represented as standalone blocks after the content.
|
|
482
|
-
Do not summarize content, just transform it into markdown.
|
|
483
|
-
It is important to list all the content text
|
|
484
|
-
If it is link it must be linked
|
|
485
|
-
You can summarize footers/navigation/aside elements.
|
|
486
|
-
But main conteint should be kept as text and formatted as markdown based on its current markup.
|
|
487
|
-
Links to external web sites should be avoided in output.
|
|
488
|
-
|
|
489
|
-
Break down into sections:
|
|
490
|
-
|
|
491
|
-
## Content Area
|
|
492
|
-
|
|
493
|
-
## Navigation Area
|
|
494
|
-
|
|
495
|
-
<page_html>
|
|
496
|
-
${html}
|
|
497
|
-
</page_html>
|
|
498
|
-
`;
|
|
499
|
-
const model = this.provider.getModelForAgent('researcher');
|
|
500
|
-
const r = await this.provider.chat([{ role: 'user', content: prompt }], model, { agentName: 'researcher', telemetryFunctionId: 'researcher.textContent' });
|
|
501
|
-
return r.text;
|
|
502
|
-
}
|
|
503
462
|
getScreenshotFromState(state) {
|
|
504
463
|
const actionResult = ActionResult.fromState(state);
|
|
505
464
|
const image = actionResult.screenshot;
|
|
@@ -693,7 +652,7 @@ export class Researcher extends ResearcherBase {
|
|
|
693
652
|
async cancelInUi() {
|
|
694
653
|
const beforeAria = this.stateManager.getCurrentState()?.ariaSnapshot || null;
|
|
695
654
|
await this.explorer.executeAction('I.clickXY(0, 0)');
|
|
696
|
-
if (diffAriaSnapshots(beforeAria, this.stateManager.getCurrentState()?.ariaSnapshot || null))
|
|
655
|
+
if (diffAriaSnapshots(beforeAria, this.stateManager.getCurrentState()?.ariaSnapshot || null).text)
|
|
697
656
|
return;
|
|
698
657
|
await this.explorer.executeAction(`I.pressKey('Escape')`);
|
|
699
658
|
}
|
package/dist/src/ai/rules.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const recommendedCodeceptCommands: readonly ["I.click", "I.type", "I.fillField", "I.see", "I.seeElement"];
|
|
2
2
|
export declare const locatorRule: string;
|
|
3
|
+
export declare const drillLocatorRule: string;
|
|
3
4
|
export declare const fileUploadRule: string;
|
|
4
5
|
export declare const formRequirementsRule: string;
|
|
5
6
|
export declare const protectionRule: string;
|
package/dist/src/ai/rules.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import dedent from 'dedent';
|
|
2
2
|
export const recommendedCodeceptCommands = ['I.click', 'I.type', 'I.fillField', 'I.see', 'I.seeElement'];
|
|
3
|
-
|
|
3
|
+
const locatorPriorityRule = dedent `
|
|
4
4
|
<locator_priority>
|
|
5
5
|
Use the following priority when selecting locators:
|
|
6
6
|
|
|
@@ -17,10 +17,11 @@ export const locatorRule = dedent `
|
|
|
17
17
|
Example: '#login-btn', '[data-testid="submit"]', 'form#login input[name="email"]'
|
|
18
18
|
|
|
19
19
|
4. XPath (last resort) - for complex hierarchy or when CSS can't express the path
|
|
20
|
-
Always start with
|
|
20
|
+
Always start with //. Avoid positional indices like [1], [2] except as a last-resort disambiguator
|
|
21
21
|
Example: '//form[@id="login"]//input[@name="email"]'
|
|
22
22
|
</locator_priority>
|
|
23
|
-
|
|
23
|
+
`;
|
|
24
|
+
const contextSimplificationRule = dedent `
|
|
24
25
|
<context_simplification>
|
|
25
26
|
When container is available from UI map sections:
|
|
26
27
|
- Text + container is simplest and PREFERRED: I.click('Save', '.modal')
|
|
@@ -28,7 +29,8 @@ export const locatorRule = dedent `
|
|
|
28
29
|
- ALWAYS use context parameter unless locator is XPath or unique ID
|
|
29
30
|
- No need for complex ARIA when container narrows scope sufficiently
|
|
30
31
|
</context_simplification>
|
|
31
|
-
|
|
32
|
+
`;
|
|
33
|
+
const locatorStrategyRule = dedent `
|
|
32
34
|
<disambiguation>
|
|
33
35
|
When multiple elements could match the request, select based on intent:
|
|
34
36
|
1. Match the context of recent actions - if filling a form, use elements in that same form
|
|
@@ -46,8 +48,9 @@ export const locatorRule = dedent `
|
|
|
46
48
|
- Use aria-label value if present: { "role": "button", "text": "Close" } (from aria-label="Close")
|
|
47
49
|
- Use title attribute if present: { "role": "button", "text": "Settings" } (from title="Settings")
|
|
48
50
|
- If no accessible name exists, mark ARIA as "-" and use CSS/XPath:
|
|
49
|
-
* CSS: use partial href a[href*="settings"] or SVG icon class a:has(svg.
|
|
50
|
-
* XPath: use contains(@href,"settings") or SVG class //a[.//svg[contains(@class,"
|
|
51
|
+
* CSS: use partial href a[href*="settings"] or SVG icon class a:has(svg.icon-settings)
|
|
52
|
+
* XPath: use contains(@href,"settings") or SVG class //a[.//svg[contains(@class,"icon-settings")]]
|
|
53
|
+
- In inline create/edit rows, confirmation can be an icon-only control near the edited field instead of a text Save button. Anchor the locator to the same row/form as the field and target the adjacent confirm icon/control.
|
|
51
54
|
- NEVER use empty text: { "role": "button", "text": "" } is INVALID and useless
|
|
52
55
|
|
|
53
56
|
<good_aria_locator_example>
|
|
@@ -78,9 +81,9 @@ export const locatorRule = dedent `
|
|
|
78
81
|
- Vue: data-v-* attributes
|
|
79
82
|
Avoid locators that seem to have generated ids or class names (long random numbers, uuids, hashes, etc)
|
|
80
83
|
Prefer text or ARIA locators over href-based ones. But for icon-only links with no accessible name, use:
|
|
81
|
-
- Partial href match: a[href*="settings"], a[href*="
|
|
82
|
-
- SVG icon class: a:has(svg.
|
|
83
|
-
Avoid full absolute href like a[href="/
|
|
84
|
+
- Partial href match: a[href*="settings"], a[href*="reports"] (use path segments, not full URLs)
|
|
85
|
+
- SVG icon class: a:has(svg.icon-settings), button:has(svg.icon-add) (target the SVG class inside the link/button)
|
|
86
|
+
Avoid full absolute href like a[href="/items/12345/settings"] — use generic path segments instead
|
|
84
87
|
Avoid CSS framework utility classes as containers (Tailwind: flex, grid, space-x-*, justify-*, items-*, w-*, h-*, p-*, m-*, etc; Bootstrap: col-*, row, d-flex, etc)
|
|
85
88
|
Prefer semantic class names, roles, data attributes, or element hierarchy for containers
|
|
86
89
|
|
|
@@ -92,7 +95,7 @@ export const locatorRule = dedent `
|
|
|
92
95
|
|
|
93
96
|
<xpath_rules>
|
|
94
97
|
XPath locators must start with //.
|
|
95
|
-
XPath
|
|
98
|
+
XPath may use positional indices [1], [2] and contains(., "text") as a last-resort disambiguator when attribute/text strategies are exhausted.
|
|
96
99
|
XPath should rely less on class names — prefer element hierarchy, position, and text content.
|
|
97
100
|
XPath and CSS MUST provide different strategies for finding the same element.
|
|
98
101
|
</xpath_rules>
|
|
@@ -104,21 +107,23 @@ export const locatorRule = dedent `
|
|
|
104
107
|
'#content-top #user_name'
|
|
105
108
|
'#content-top form input[name="name"]'
|
|
106
109
|
'a.nav-item[href*="settings"]' // icon-only link matched by partial href
|
|
107
|
-
'a.nav-item:has(svg.
|
|
110
|
+
'a.nav-item:has(svg.icon-settings)' // icon-only link matched by SVG icon class
|
|
108
111
|
'//nav//a[contains(@href,"settings")]' // XPath for icon-only nav link
|
|
109
112
|
</good locator example>
|
|
110
113
|
|
|
111
114
|
<bad locator example>
|
|
112
|
-
'a.filter-tab:nth-of-type(1)' // WRONG: positional in CSS, use :has-text("
|
|
113
|
-
'//a[contains(@class,"filter-tab") and contains(@class,"active")]' // WRONG: XPath repeats CSS approach,
|
|
115
|
+
'a.filter-tab:nth-of-type(1)' // WRONG: positional in CSS, use :has-text("Active") instead
|
|
116
|
+
'//a[contains(@class,"filter-tab") and contains(@class,"active")]' // WRONG: XPath repeats CSS approach, provide a different strategy e.g. //a[contains(@class,"filter-tab")][1]
|
|
114
117
|
'//table//tbody/tr[1]//button[contains(@onclick,'fn()')]' // onclick is not semantic attribute
|
|
115
118
|
'//html/body/vue-button-123' // vue-framework specific locator
|
|
116
|
-
'link "New
|
|
117
|
-
'a[href="/
|
|
119
|
+
'link "New Item"' // WRONG: malformed string, use {"role":"link","text":"New Item"}
|
|
120
|
+
'a[href="/items/12345/settings"]' // WRONG: full absolute href, use a[href*="settings"] instead
|
|
118
121
|
</bad locator example>
|
|
119
122
|
|
|
120
123
|
HTML locators must be valid JS strings
|
|
121
124
|
`;
|
|
125
|
+
export const locatorRule = [locatorPriorityRule, contextSimplificationRule, locatorStrategyRule].join('\n\n');
|
|
126
|
+
export const drillLocatorRule = [locatorPriorityRule, locatorStrategyRule].join('\n\n');
|
|
122
127
|
export const fileUploadRule = dedent `
|
|
123
128
|
<file_upload>
|
|
124
129
|
Explorbot CAN upload files using I.attachFile() via form() tool.
|
|
@@ -294,6 +299,7 @@ export const actionRule = dedent `
|
|
|
294
299
|
Use context parameter (second argument) to narrow click area when:
|
|
295
300
|
- The same text/button appears multiple times on page
|
|
296
301
|
- You need to click inside a specific form, modal, or section
|
|
302
|
+
- You need an icon-only confirm/save control next to a field in an inline create/edit row
|
|
297
303
|
Context should be a CSS selector pointing to a unique container.
|
|
298
304
|
|
|
299
305
|
<example>
|
|
@@ -306,6 +312,7 @@ export const actionRule = dedent `
|
|
|
306
312
|
</example>
|
|
307
313
|
|
|
308
314
|
Prefer text/ARIA locators with context over complex CSS/XPath selectors.
|
|
315
|
+
For inline create/edit flows, after filling a field verify it contains the value, then confirm using the nearest explicit button/link, an adjacent icon-only confirm control in the same row/form, or Enter if the field remains focused.
|
|
309
316
|
If locator doesn't work, try CSS or XPath locators.
|
|
310
317
|
If nothing works, use I.clickXY(x, y) as last resort.
|
|
311
318
|
|
|
@@ -42,6 +42,9 @@ export class SessionAnalyst {
|
|
|
42
42
|
## Severity (defects only)
|
|
43
43
|
[High] blocks a core flow · [Medium] degrades a flow but workaround exists · [Low] cosmetic / edge case
|
|
44
44
|
|
|
45
|
+
## Naming tests
|
|
46
|
+
Reference every test by its full label wrapped in backticks: \`\`\`ET-<number> <test title>\`\`\` — the \`ref\` and \`scenario\` from its \`<test>\` block. Backticks only, never bold. Never write a bare \`#2\`.
|
|
47
|
+
|
|
45
48
|
## Format
|
|
46
49
|
|
|
47
50
|
# Session Analysis
|
|
@@ -53,22 +56,22 @@ export class SessionAnalyst {
|
|
|
53
56
|
- Features: <capabilities>
|
|
54
57
|
|
|
55
58
|
## What works
|
|
56
|
-
- **<feature>** —
|
|
59
|
+
- **<feature>** — \`ET-2 <title>\`, \`ET-7 <title>\`
|
|
57
60
|
|
|
58
61
|
## Defects
|
|
59
62
|
|
|
60
63
|
### [Medium] <plain-English bug title>
|
|
61
|
-
Affects:
|
|
64
|
+
Affects: \`ET-3 <title>\`, \`ET-5 <title>\`
|
|
62
65
|
Reproduce:
|
|
63
66
|
1. <concrete UI step>
|
|
64
67
|
2. <next>
|
|
65
68
|
Evidence: <one short observation>
|
|
66
69
|
|
|
67
70
|
## UX issues
|
|
68
|
-
- **<feature>** — <what's confusing> (
|
|
71
|
+
- **<feature>** — <what's confusing> (\`ET-7 <title>\`)
|
|
69
72
|
|
|
70
73
|
## Execution Issues
|
|
71
|
-
-
|
|
74
|
+
- \`ET-2 <title>\` — <≤10 words, what was unreliable>
|
|
72
75
|
|
|
73
76
|
## Brevity rules
|
|
74
77
|
|
|
@@ -127,7 +130,7 @@ export class SessionAnalyst {
|
|
|
127
130
|
`
|
|
128
131
|
: 'verification_status: none';
|
|
129
132
|
return dedent `
|
|
130
|
-
<test ref="
|
|
133
|
+
<test ref="ET-${ref}">
|
|
131
134
|
url: ${test.startUrl || '/'}
|
|
132
135
|
scenario: ${test.scenario}
|
|
133
136
|
result: ${test.result || 'unknown'}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ActionResult } from '../action-result.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { ExperienceTracker } from '../experience-tracker.js';
|
|
3
3
|
import type { KnowledgeTracker } from '../knowledge-tracker.js';
|
|
4
4
|
import { Historian } from './historian.js';
|
|
5
5
|
import type { Navigator } from './navigator.js';
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import dedent from 'dedent';
|
|
2
|
-
import { renderExperienceToc } from '../experience-tracker.js';
|
|
3
|
-
import { createDebug, pluralize, tag } from '../utils/logger.js';
|
|
4
|
-
const debugLog = createDebug('explorbot:task-agent');
|
|
5
1
|
export function isInteractive() {
|
|
6
2
|
return process.env.INK_RUNNING === 'true';
|
|
7
3
|
}
|
|
@@ -18,31 +14,10 @@ export class TaskAgent {
|
|
|
18
14
|
_historian = null;
|
|
19
15
|
_quartermaster = null;
|
|
20
16
|
getKnowledge(actionResult) {
|
|
21
|
-
|
|
22
|
-
if (knowledgeFiles.length === 0)
|
|
23
|
-
return '';
|
|
24
|
-
const knowledgeContent = knowledgeFiles
|
|
25
|
-
.map((k) => k.content)
|
|
26
|
-
.filter((k) => !!k)
|
|
27
|
-
.join('\n\n');
|
|
28
|
-
tag('operation').log(`Found ${knowledgeFiles.length} relevant knowledge ${pluralize(knowledgeFiles.length, 'file')}`);
|
|
29
|
-
return dedent `
|
|
30
|
-
<knowledge>
|
|
31
|
-
Here is relevant knowledge for this page:
|
|
32
|
-
|
|
33
|
-
${knowledgeContent}
|
|
34
|
-
</knowledge>
|
|
35
|
-
`;
|
|
17
|
+
return this.getKnowledgeTracker().renderRelevantKnowledge(actionResult);
|
|
36
18
|
}
|
|
37
19
|
getExperience(actionResult) {
|
|
38
|
-
|
|
39
|
-
const toc = tracker.getExperienceTableOfContents(actionResult);
|
|
40
|
-
if (toc.length === 0)
|
|
41
|
-
return '';
|
|
42
|
-
const totalSections = toc.reduce((sum, entry) => sum + entry.sections.length, 0);
|
|
43
|
-
debugLog(`injecting experience TOC (${toc.length} files, ${totalSections} sections)`);
|
|
44
|
-
tag('operation').log(`Found ${toc.length} experience ${pluralize(toc.length, 'file')} (${totalSections} sections)`);
|
|
45
|
-
return renderExperienceToc(toc);
|
|
20
|
+
return this.getExperienceTracker().renderExperienceTocFor(actionResult);
|
|
46
21
|
}
|
|
47
22
|
setHistorian(historian) {
|
|
48
23
|
this._historian = historian;
|
package/dist/src/ai/tester.d.ts
CHANGED
|
@@ -26,7 +26,6 @@ export declare class Tester extends TaskAgent implements Agent {
|
|
|
26
26
|
researcher: Researcher;
|
|
27
27
|
navigator: Navigator;
|
|
28
28
|
agentTools: any;
|
|
29
|
-
executionLogFile: string | null;
|
|
30
29
|
previousUrl: string | null;
|
|
31
30
|
previousStateHash: string | null;
|
|
32
31
|
pageStateHash: string | null;
|
|
@@ -56,7 +55,6 @@ export declare class Tester extends TaskAgent implements Agent {
|
|
|
56
55
|
shouldStopForStalledExecution(task: Test, previousState: ActionResult, toolExecutions: any[]): boolean;
|
|
57
56
|
prepareInstructionsForNextStep(task: Test): Promise<string>;
|
|
58
57
|
reinjectContextIfNeeded(iteration: number, currentState: ActionResult): Promise<string>;
|
|
59
|
-
promptLogStep(task: Test): Promise<string>;
|
|
60
58
|
finishTest(task: Test): void;
|
|
61
59
|
abortStartedTestOnErrorPage(task: Test, actionResult: ActionResult): Promise<{
|
|
62
60
|
success: boolean;
|
package/dist/src/ai/tester.js
CHANGED
|
@@ -5,7 +5,6 @@ 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";
|
|
@@ -16,7 +15,7 @@ import { createDebug, tag } from "../utils/logger.js";
|
|
|
16
15
|
import { loop } from "../utils/loop.js";
|
|
17
16
|
import { actionRule, capabilityGroundingRule, dataProtectionRules, focusedElementRule, formRequirementsRule, locatorRule, multipleTabsRule, sectionContextRule } from "./rules.js";
|
|
18
17
|
import { TaskAgent } from "./task-agent.js";
|
|
19
|
-
import { createCodeceptJSTools,
|
|
18
|
+
import { createCodeceptJSTools, createIframeTools } from "./tools.js";
|
|
20
19
|
const debugLog = createDebug('explorbot:tester');
|
|
21
20
|
const SAMPLE_FILES_DIR = resolve(dirname(fileURLToPath(import.meta.url)), '../../assets/sample-files');
|
|
22
21
|
const SAMPLE_FILES = {
|
|
@@ -43,7 +42,6 @@ export class Tester extends TaskAgent {
|
|
|
43
42
|
researcher;
|
|
44
43
|
navigator;
|
|
45
44
|
agentTools;
|
|
46
|
-
executionLogFile = null;
|
|
47
45
|
previousUrl = null;
|
|
48
46
|
previousStateHash = null;
|
|
49
47
|
pageStateHash = null;
|
|
@@ -120,9 +118,6 @@ export class Tester extends TaskAgent {
|
|
|
120
118
|
const conversation = this.provider.startConversation(this.getSystemMessage(), 'tester');
|
|
121
119
|
conversation.markLastMessageCacheable();
|
|
122
120
|
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
121
|
const scenarioBlock = this.buildScenarioBlock(task, initialState);
|
|
127
122
|
conversation.addUserText(scenarioBlock);
|
|
128
123
|
conversation.markLastMessageCacheable();
|
|
@@ -217,10 +212,12 @@ export class Tester extends TaskAgent {
|
|
|
217
212
|
const currentState = this.getCurrentState();
|
|
218
213
|
const tools = {
|
|
219
214
|
...codeceptjsTools,
|
|
220
|
-
...(currentState.isInsideIframe ? createSpecialContextTools(this.explorer, 'iframe') : {}),
|
|
221
215
|
...this.createTestFlowTools(task, currentState, conversation),
|
|
222
216
|
...this.agentTools,
|
|
223
217
|
};
|
|
218
|
+
if (currentState.isInsideIframe) {
|
|
219
|
+
Object.assign(tools, createIframeTools(this.explorer));
|
|
220
|
+
}
|
|
224
221
|
debugLog(`Test ${task.scenario} iteration ${iteration}`);
|
|
225
222
|
if (this.explorer.getStateManager().isInDeadLoop()) {
|
|
226
223
|
task.addNote('Dead loop detected. Stopped');
|
|
@@ -456,7 +453,6 @@ export class Tester extends TaskAgent {
|
|
|
456
453
|
const currentUrl = currentState.url;
|
|
457
454
|
const currentStateHash = currentState.hash;
|
|
458
455
|
const isNewUrl = this.previousUrl !== currentUrl;
|
|
459
|
-
const isStateChanged = !isNewUrl && this.previousStateHash !== currentStateHash;
|
|
460
456
|
this.previousUrl = currentUrl;
|
|
461
457
|
this.previousStateHash = currentStateHash;
|
|
462
458
|
let context = '';
|
|
@@ -564,23 +560,6 @@ export class Tester extends TaskAgent {
|
|
|
564
560
|
`;
|
|
565
561
|
}
|
|
566
562
|
}
|
|
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
563
|
if (context)
|
|
585
564
|
return context;
|
|
586
565
|
if (iteration % 5)
|
|
@@ -598,34 +577,6 @@ export class Tester extends TaskAgent {
|
|
|
598
577
|
</page_aria>
|
|
599
578
|
`;
|
|
600
579
|
}
|
|
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
580
|
finishTest(task) {
|
|
630
581
|
if (!task.hasFinished) {
|
|
631
582
|
task.finish(TestResult.FAILED);
|
|
@@ -725,11 +676,13 @@ export class Tester extends TaskAgent {
|
|
|
725
676
|
- Before retrying your actions check maybe they already achived expected results. Use see() tool for that
|
|
726
677
|
- 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
678
|
- 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
|
|
679
|
+
- 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.
|
|
680
|
+
- 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
681
|
- 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
|
-
-
|
|
682
|
+
- 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.
|
|
683
|
+
- 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.
|
|
684
|
+
- 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.
|
|
685
|
+
- 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
686
|
- When filling complex form with lot of actions performed, use see() to look which fields were filled and which are not
|
|
734
687
|
- When verify() fails, use see() to visually confirm the result — visual confirmation is equally valid evidence
|
|
735
688
|
- For visual state verification (active tabs, selected items, counts, colors), prefer see() over DOM-based verify()
|
|
@@ -788,10 +741,6 @@ export class Tester extends TaskAgent {
|
|
|
788
741
|
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
742
|
Initial page URL: ${actionResult.url}
|
|
790
743
|
|
|
791
|
-
${capabilityGroundingRule}
|
|
792
|
-
|
|
793
|
-
${dataProtectionRules}
|
|
794
|
-
|
|
795
744
|
${this.buildDeletionScope(task)}
|
|
796
745
|
|
|
797
746
|
${this.buildAvailableFiles()}
|
package/dist/src/ai/tools.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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
3
|
import type Explorer from '../explorer.js';
|
|
4
4
|
import { type Task } from '../test-plan.js';
|
|
@@ -24,18 +24,29 @@ export declare function createCodeceptJSTools(explorer: Explorer, task: Task): {
|
|
|
24
24
|
explanation: any;
|
|
25
25
|
}, Record<string, any>, import("@ai-sdk/provider-utils").Context>>;
|
|
26
26
|
};
|
|
27
|
-
export declare function
|
|
28
|
-
exitIframe?: undefined;
|
|
29
|
-
} | {
|
|
27
|
+
export declare function createIframeTools(explorer: Explorer): {
|
|
30
28
|
exitIframe: import("@ai-sdk/provider-utils").ExecutableTool<import("ai").Tool<{
|
|
31
29
|
reason?: string;
|
|
32
30
|
}, Record<string, any>, import("@ai-sdk/provider-utils").Context>>;
|
|
33
31
|
};
|
|
34
|
-
export declare function
|
|
32
|
+
export declare function createLearnExperienceTool({ getExperienceTracker, getState }: {
|
|
33
|
+
getExperienceTracker: () => ExperienceTracker;
|
|
34
|
+
getState: () => ActionResult | null;
|
|
35
|
+
}): import("@ai-sdk/provider-utils").ExecutableTool<import("ai").Tool<{
|
|
36
|
+
fileTag: any;
|
|
37
|
+
sectionIndex: any;
|
|
38
|
+
}, {
|
|
39
|
+
title: string;
|
|
40
|
+
url: string;
|
|
41
|
+
content: string;
|
|
42
|
+
} | {
|
|
43
|
+
error: string;
|
|
44
|
+
}, import("@ai-sdk/provider-utils").Context>>;
|
|
45
|
+
export declare function createAgentTools({ explorer, researcher, navigator, supervisor, withExperience, }: {
|
|
35
46
|
explorer: Explorer;
|
|
36
47
|
researcher: Researcher;
|
|
37
48
|
navigator: Navigator;
|
|
38
|
-
experienceTracker?: ExperienceTracker;
|
|
39
|
-
getState?: () => ActionResult | null;
|
|
40
49
|
supervisor?: boolean;
|
|
50
|
+
withExperience?: boolean;
|
|
41
51
|
}): any;
|
|
52
|
+
export declare function isMajorPageChange(pageDiff: PageDiff): boolean;
|