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
|
@@ -98,7 +98,6 @@ export function WithDeepAnalysis<T extends Constructor>(Base: T) {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
const diff = await current.diff(previous);
|
|
101
|
-
await diff.calculate();
|
|
102
101
|
|
|
103
102
|
if (!diff.ariaChanged && diff.htmlParts.length === 0) {
|
|
104
103
|
debugLog(`No diff between current and previous state for overlay "${focusArea.name}"`);
|
|
@@ -359,13 +358,12 @@ export function WithDeepAnalysis<T extends Constructor>(Base: T) {
|
|
|
359
358
|
const isCoordinateClick = el.commands[0].startsWith('I.clickXY(');
|
|
360
359
|
if (!isCoordinateClick) {
|
|
361
360
|
const hoverCmd = el.commands[0].replace('I.click(', 'I.moveCursorTo(');
|
|
362
|
-
await this.explorer.attemptAction(hoverCmd, undefined
|
|
361
|
+
await this.explorer.attemptAction(hoverCmd, undefined);
|
|
363
362
|
await new Promise((r) => setTimeout(r, 500));
|
|
364
363
|
|
|
365
364
|
await this.explorer.capturePageState();
|
|
366
365
|
const hoverAR = ActionResult.fromState(this.stateManager.getCurrentState()!);
|
|
367
366
|
const hoverDiff = await hoverAR.diff(previousState);
|
|
368
|
-
await hoverDiff.calculate();
|
|
369
367
|
const hoverHtmlSize = hoverDiff.htmlParts.reduce((sum, p) => sum + p.subtree.length, 0);
|
|
370
368
|
const hoverRevealed = hoverDiff.ariaChanged && hoverHtmlSize > 500;
|
|
371
369
|
|
|
@@ -405,7 +403,7 @@ export function WithDeepAnalysis<T extends Constructor>(Base: T) {
|
|
|
405
403
|
let clickCode: string | null = null;
|
|
406
404
|
const action = this.explorer.createAction();
|
|
407
405
|
for (const cmd of commands) {
|
|
408
|
-
if (await action.attempt(cmd, undefined
|
|
406
|
+
if (await action.attempt(cmd, undefined)) {
|
|
409
407
|
clickCode = cmd;
|
|
410
408
|
break;
|
|
411
409
|
}
|
|
@@ -422,7 +420,6 @@ export function WithDeepAnalysis<T extends Constructor>(Base: T) {
|
|
|
422
420
|
await this.explorer.createAction().capturePageState();
|
|
423
421
|
const currAR = ActionResult.fromState(this.stateManager.getCurrentState()!);
|
|
424
422
|
diff = await currAR.diff(previousState);
|
|
425
|
-
await diff.calculate();
|
|
426
423
|
} catch (err) {
|
|
427
424
|
tag('warning').log(`State capture failed after click: ${err instanceof Error ? err.message : err}`);
|
|
428
425
|
await this._restorePageState(state.url, originalAria);
|
|
@@ -453,7 +450,7 @@ export function WithDeepAnalysis<T extends Constructor>(Base: T) {
|
|
|
453
450
|
await (this as any).cancelInUi();
|
|
454
451
|
await this.explorer.capturePageState();
|
|
455
452
|
const currentAria = this.stateManager.getCurrentState()?.ariaSnapshot || '';
|
|
456
|
-
if (!diffAriaSnapshots(originalAria, currentAria)) return;
|
|
453
|
+
if (!diffAriaSnapshots(originalAria, currentAria).text) return;
|
|
457
454
|
} catch (err) {
|
|
458
455
|
tag('warning').log(`State capture failed after cancelInUi: ${err instanceof Error ? err.message : err}`);
|
|
459
456
|
}
|
|
@@ -94,7 +94,7 @@ export function extractContainerFromBlockquote(sectionMarkdown: string): string
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
export function parseResearchSections(markdown: string): ResearchSection[] {
|
|
97
|
-
const hasExtendedResearch = markdown.
|
|
97
|
+
const hasExtendedResearch = mdq(markdown).query('section1(~"Extended Research")').count() > 0;
|
|
98
98
|
|
|
99
99
|
return parseSections(markdown)
|
|
100
100
|
.filter((s) => !SKIP_SECTIONS.has(s.name.toLowerCase()) && !s.name.toLowerCase().includes('data:'))
|
|
@@ -46,24 +46,6 @@ export class ResearchResult {
|
|
|
46
46
|
}));
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
updateSection(sectionName: string, locators: Locator[]): void {
|
|
50
|
-
const sections = parseResearchSections(this.text);
|
|
51
|
-
const section = sections.find((s) => s.name === sectionName);
|
|
52
|
-
if (!section) return;
|
|
53
|
-
|
|
54
|
-
for (const el of section.elements) {
|
|
55
|
-
const elLocators = locators.filter((l) => l.element === el.name);
|
|
56
|
-
for (const loc of elLocators) {
|
|
57
|
-
const value = loc.valid === false ? null : loc.locator || null;
|
|
58
|
-
if (loc.type === 'css') el.css = value;
|
|
59
|
-
if (loc.type === 'xpath') el.xpath = value;
|
|
60
|
-
if (loc.type === 'aria') el.aria = value ? parseAriaLocator(value) : null;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
this.rebuildSectionInText(section);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
49
|
rebuildSectionInText(section: ResearchSection): void {
|
|
68
50
|
if (section.elements.length === 0) return;
|
|
69
51
|
const newTable = rebuildSectionMarkdown(section);
|
|
@@ -105,9 +105,13 @@ export function WithSections<T extends Constructor>(Base: T) {
|
|
|
105
105
|
</section_format>
|
|
106
106
|
|
|
107
107
|
<rules>
|
|
108
|
-
-
|
|
108
|
+
- List only elements physically inside this section's declared container.
|
|
109
|
+
- Do not copy global toolbar, navigation, list, or detail elements into this section unless they are descendants of this section container.
|
|
110
|
+
- Every element with eidx inside this section's container MUST appear in the table.
|
|
109
111
|
- Every row needs CSS; ARIA may be "-" for icon-only buttons.
|
|
110
112
|
- ARIA locator JSON uses keys "role" and "text" (NOT "name").
|
|
113
|
+
- Elements marked data-explorbot-hit="covered" or "offscreen" are not directly actionable; describe the covering or focused UI first.
|
|
114
|
+
- In split-pane pages, entity detail panels are active detail context; include close/back/pin controls in the detail panel section when present.
|
|
111
115
|
</rules>
|
|
112
116
|
|
|
113
117
|
${generalLocatorRuleText}
|
package/src/ai/researcher.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
2
1
|
import dedent from 'dedent';
|
|
3
2
|
import { ActionResult } from '../action-result.js';
|
|
4
3
|
import { setActivity } from '../activity.ts';
|
|
@@ -15,7 +14,7 @@ import { diffAriaSnapshots } from '../utils/aria.ts';
|
|
|
15
14
|
import { ErrorPageError, detectPageCondition } from '../utils/error-page.ts';
|
|
16
15
|
import { HooksRunner } from '../utils/hooks-runner.ts';
|
|
17
16
|
import { isBodyEmpty } from '../utils/html.ts';
|
|
18
|
-
import { createDebug,
|
|
17
|
+
import { createDebug, tag } from '../utils/logger.js';
|
|
19
18
|
import { mdq } from '../utils/markdown-query.ts';
|
|
20
19
|
import { RulesLoader } from '../utils/rules-loader.ts';
|
|
21
20
|
import type { Agent } from './agent.js';
|
|
@@ -315,11 +314,9 @@ export class Researcher extends ResearcherBase implements Agent {
|
|
|
315
314
|
researchFile = saveResearch(stateHash, result.text, combinedHtml);
|
|
316
315
|
}
|
|
317
316
|
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (summaryLine) this.experienceTracker.updateSummary(this.actionResult!, summaryLine);
|
|
322
|
-
}
|
|
317
|
+
const summaryText = mdq(result.text).query('section2(/^summary/)').query('paragraph[0]').text().trim();
|
|
318
|
+
const summaryLine = summaryText.split('\n')[0]?.trim().slice(0, 200);
|
|
319
|
+
if (summaryLine) this.experienceTracker.updateSummary(this.actionResult!, summaryLine);
|
|
323
320
|
|
|
324
321
|
tag('multiline').log(formatResearchSummary(result.text, { visionUsed: this.hasScreenshotToAnalyze }));
|
|
325
322
|
tag('success').log('Research complete');
|
|
@@ -428,7 +425,10 @@ export class Researcher extends ResearcherBase implements Agent {
|
|
|
428
425
|
- Research all menus and navigational areas.
|
|
429
426
|
- Ignore decorative sidebars, footer-only links, and external links.
|
|
430
427
|
- Detect layout patterns: list/detail split, 2-pane, or 3-pane layouts.
|
|
431
|
-
-
|
|
428
|
+
- 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.
|
|
429
|
+
- 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.
|
|
430
|
+
- 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.
|
|
431
|
+
- Every element with an eidx attribute MUST appear in exactly one matching UI map section — describe icon-only buttons by their visual role.
|
|
432
432
|
- Every UI map row needs a CSS selector; ARIA may be "-" for icon-only buttons, CSS must never be "-".
|
|
433
433
|
- ARIA locator JSON uses keys "role" and "text" (NOT "name").
|
|
434
434
|
- Mark elements with likely hover interactions (title, aria-describedby, menu items with submenus) as "(hover)".
|
|
@@ -445,6 +445,7 @@ export class Researcher extends ResearcherBase implements Agent {
|
|
|
445
445
|
.join('\n')}
|
|
446
446
|
|
|
447
447
|
- Sections can overlap; prefer more detailed sections over broader ones.
|
|
448
|
+
- 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.
|
|
448
449
|
- Never name a section "Focus" or "Focused" — use what it contains (Detail, Modal, Form, Content, List).
|
|
449
450
|
- Omit sections that are not present or not relevant.
|
|
450
451
|
- Each section needs a container CSS locator; UI map CSS locators are relative to it.
|
|
@@ -466,23 +467,7 @@ export class Researcher extends ResearcherBase implements Agent {
|
|
|
466
467
|
if (!this.actionResult) throw new Error('actionResult is not set');
|
|
467
468
|
|
|
468
469
|
const html = await this.actionResult.combinedHtml();
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
let knowledge = '';
|
|
472
|
-
if (knowledgeFiles.length > 0) {
|
|
473
|
-
const knowledgeContent = knowledgeFiles
|
|
474
|
-
.map((k) => k.content)
|
|
475
|
-
.filter((k) => !!k)
|
|
476
|
-
.join('\n\n');
|
|
477
|
-
|
|
478
|
-
tag('operation').log(`Found ${knowledgeFiles.length} relevant knowledge ${pluralize(knowledgeFiles.length, 'file')} for: ${this.actionResult.url}`);
|
|
479
|
-
knowledge = `
|
|
480
|
-
<hint>
|
|
481
|
-
Here is relevant knowledge for this page:
|
|
482
|
-
|
|
483
|
-
${knowledgeContent}
|
|
484
|
-
</hint>`;
|
|
485
|
-
}
|
|
470
|
+
const knowledge = this.explorer.getKnowledgeTracker().renderRelevantKnowledge(this.actionResult);
|
|
486
471
|
|
|
487
472
|
const ariaSnapshot = this.actionResult.getCompactARIA();
|
|
488
473
|
|
|
@@ -540,39 +525,6 @@ export class Researcher extends ResearcherBase implements Agent {
|
|
|
540
525
|
`;
|
|
541
526
|
}
|
|
542
527
|
|
|
543
|
-
async textContent(state: WebPageState): Promise<string> {
|
|
544
|
-
const actionResult = ActionResult.fromState(state);
|
|
545
|
-
const html = await actionResult.combinedHtml();
|
|
546
|
-
|
|
547
|
-
const prompt = dedent`
|
|
548
|
-
Transform into markdown.
|
|
549
|
-
Identify headers, footers, asides, special application parts and main contant.
|
|
550
|
-
Content should be in markdown format. If it is content: tables must be tables, lists must be lists.
|
|
551
|
-
Navigation elements should be represented as standalone blocks after the content.
|
|
552
|
-
Do not summarize content, just transform it into markdown.
|
|
553
|
-
It is important to list all the content text
|
|
554
|
-
If it is link it must be linked
|
|
555
|
-
You can summarize footers/navigation/aside elements.
|
|
556
|
-
But main conteint should be kept as text and formatted as markdown based on its current markup.
|
|
557
|
-
Links to external web sites should be avoided in output.
|
|
558
|
-
|
|
559
|
-
Break down into sections:
|
|
560
|
-
|
|
561
|
-
## Content Area
|
|
562
|
-
|
|
563
|
-
## Navigation Area
|
|
564
|
-
|
|
565
|
-
<page_html>
|
|
566
|
-
${html}
|
|
567
|
-
</page_html>
|
|
568
|
-
`;
|
|
569
|
-
|
|
570
|
-
const model = this.provider.getModelForAgent('researcher');
|
|
571
|
-
const r = await this.provider.chat([{ role: 'user', content: prompt }], model, { agentName: 'researcher', telemetryFunctionId: 'researcher.textContent' });
|
|
572
|
-
|
|
573
|
-
return r.text;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
528
|
private getScreenshotFromState(state: WebPageState): { actionResult: ActionResult; image: Buffer } | null {
|
|
577
529
|
const actionResult = ActionResult.fromState(state);
|
|
578
530
|
const image = actionResult.screenshot;
|
|
@@ -777,7 +729,7 @@ export class Researcher extends ResearcherBase implements Agent {
|
|
|
777
729
|
const beforeAria = this.stateManager.getCurrentState()?.ariaSnapshot || null;
|
|
778
730
|
|
|
779
731
|
await this.explorer.executeAction('I.clickXY(0, 0)');
|
|
780
|
-
if (diffAriaSnapshots(beforeAria, this.stateManager.getCurrentState()?.ariaSnapshot || null)) return;
|
|
732
|
+
if (diffAriaSnapshots(beforeAria, this.stateManager.getCurrentState()?.ariaSnapshot || null).text) return;
|
|
781
733
|
|
|
782
734
|
await this.explorer.executeAction(`I.pressKey('Escape')`);
|
|
783
735
|
}
|
package/src/ai/rules.ts
CHANGED
|
@@ -2,7 +2,7 @@ import dedent from 'dedent';
|
|
|
2
2
|
|
|
3
3
|
export const recommendedCodeceptCommands = ['I.click', 'I.type', 'I.fillField', 'I.see', 'I.seeElement'] as const;
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
const locatorPriorityRule = dedent`
|
|
6
6
|
<locator_priority>
|
|
7
7
|
Use the following priority when selecting locators:
|
|
8
8
|
|
|
@@ -19,10 +19,12 @@ export const locatorRule = dedent`
|
|
|
19
19
|
Example: '#login-btn', '[data-testid="submit"]', 'form#login input[name="email"]'
|
|
20
20
|
|
|
21
21
|
4. XPath (last resort) - for complex hierarchy or when CSS can't express the path
|
|
22
|
-
Always start with
|
|
22
|
+
Always start with //. Avoid positional indices like [1], [2] except as a last-resort disambiguator
|
|
23
23
|
Example: '//form[@id="login"]//input[@name="email"]'
|
|
24
24
|
</locator_priority>
|
|
25
|
+
`;
|
|
25
26
|
|
|
27
|
+
const contextSimplificationRule = dedent`
|
|
26
28
|
<context_simplification>
|
|
27
29
|
When container is available from UI map sections:
|
|
28
30
|
- Text + container is simplest and PREFERRED: I.click('Save', '.modal')
|
|
@@ -30,7 +32,9 @@ export const locatorRule = dedent`
|
|
|
30
32
|
- ALWAYS use context parameter unless locator is XPath or unique ID
|
|
31
33
|
- No need for complex ARIA when container narrows scope sufficiently
|
|
32
34
|
</context_simplification>
|
|
35
|
+
`;
|
|
33
36
|
|
|
37
|
+
const locatorStrategyRule = dedent`
|
|
34
38
|
<disambiguation>
|
|
35
39
|
When multiple elements could match the request, select based on intent:
|
|
36
40
|
1. Match the context of recent actions - if filling a form, use elements in that same form
|
|
@@ -48,8 +52,9 @@ export const locatorRule = dedent`
|
|
|
48
52
|
- Use aria-label value if present: { "role": "button", "text": "Close" } (from aria-label="Close")
|
|
49
53
|
- Use title attribute if present: { "role": "button", "text": "Settings" } (from title="Settings")
|
|
50
54
|
- If no accessible name exists, mark ARIA as "-" and use CSS/XPath:
|
|
51
|
-
* CSS: use partial href a[href*="settings"] or SVG icon class a:has(svg.
|
|
52
|
-
* XPath: use contains(@href,"settings") or SVG class //a[.//svg[contains(@class,"
|
|
55
|
+
* CSS: use partial href a[href*="settings"] or SVG icon class a:has(svg.icon-settings)
|
|
56
|
+
* XPath: use contains(@href,"settings") or SVG class //a[.//svg[contains(@class,"icon-settings")]]
|
|
57
|
+
- 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.
|
|
53
58
|
- NEVER use empty text: { "role": "button", "text": "" } is INVALID and useless
|
|
54
59
|
|
|
55
60
|
<good_aria_locator_example>
|
|
@@ -80,9 +85,9 @@ export const locatorRule = dedent`
|
|
|
80
85
|
- Vue: data-v-* attributes
|
|
81
86
|
Avoid locators that seem to have generated ids or class names (long random numbers, uuids, hashes, etc)
|
|
82
87
|
Prefer text or ARIA locators over href-based ones. But for icon-only links with no accessible name, use:
|
|
83
|
-
- Partial href match: a[href*="settings"], a[href*="
|
|
84
|
-
- SVG icon class: a:has(svg.
|
|
85
|
-
Avoid full absolute href like a[href="/
|
|
88
|
+
- Partial href match: a[href*="settings"], a[href*="reports"] (use path segments, not full URLs)
|
|
89
|
+
- SVG icon class: a:has(svg.icon-settings), button:has(svg.icon-add) (target the SVG class inside the link/button)
|
|
90
|
+
Avoid full absolute href like a[href="/items/12345/settings"] — use generic path segments instead
|
|
86
91
|
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)
|
|
87
92
|
Prefer semantic class names, roles, data attributes, or element hierarchy for containers
|
|
88
93
|
|
|
@@ -94,7 +99,7 @@ export const locatorRule = dedent`
|
|
|
94
99
|
|
|
95
100
|
<xpath_rules>
|
|
96
101
|
XPath locators must start with //.
|
|
97
|
-
XPath
|
|
102
|
+
XPath may use positional indices [1], [2] and contains(., "text") as a last-resort disambiguator when attribute/text strategies are exhausted.
|
|
98
103
|
XPath should rely less on class names — prefer element hierarchy, position, and text content.
|
|
99
104
|
XPath and CSS MUST provide different strategies for finding the same element.
|
|
100
105
|
</xpath_rules>
|
|
@@ -106,22 +111,26 @@ export const locatorRule = dedent`
|
|
|
106
111
|
'#content-top #user_name'
|
|
107
112
|
'#content-top form input[name="name"]'
|
|
108
113
|
'a.nav-item[href*="settings"]' // icon-only link matched by partial href
|
|
109
|
-
'a.nav-item:has(svg.
|
|
114
|
+
'a.nav-item:has(svg.icon-settings)' // icon-only link matched by SVG icon class
|
|
110
115
|
'//nav//a[contains(@href,"settings")]' // XPath for icon-only nav link
|
|
111
116
|
</good locator example>
|
|
112
117
|
|
|
113
118
|
<bad locator example>
|
|
114
|
-
'a.filter-tab:nth-of-type(1)' // WRONG: positional in CSS, use :has-text("
|
|
115
|
-
'//a[contains(@class,"filter-tab") and contains(@class,"active")]' // WRONG: XPath repeats CSS approach,
|
|
119
|
+
'a.filter-tab:nth-of-type(1)' // WRONG: positional in CSS, use :has-text("Active") instead
|
|
120
|
+
'//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]
|
|
116
121
|
'//table//tbody/tr[1]//button[contains(@onclick,'fn()')]' // onclick is not semantic attribute
|
|
117
122
|
'//html/body/vue-button-123' // vue-framework specific locator
|
|
118
|
-
'link "New
|
|
119
|
-
'a[href="/
|
|
123
|
+
'link "New Item"' // WRONG: malformed string, use {"role":"link","text":"New Item"}
|
|
124
|
+
'a[href="/items/12345/settings"]' // WRONG: full absolute href, use a[href*="settings"] instead
|
|
120
125
|
</bad locator example>
|
|
121
126
|
|
|
122
127
|
HTML locators must be valid JS strings
|
|
123
128
|
`;
|
|
124
129
|
|
|
130
|
+
export const locatorRule = [locatorPriorityRule, contextSimplificationRule, locatorStrategyRule].join('\n\n');
|
|
131
|
+
|
|
132
|
+
export const drillLocatorRule = [locatorPriorityRule, locatorStrategyRule].join('\n\n');
|
|
133
|
+
|
|
125
134
|
export const fileUploadRule = dedent`
|
|
126
135
|
<file_upload>
|
|
127
136
|
Explorbot CAN upload files using I.attachFile() via form() tool.
|
|
@@ -307,6 +316,7 @@ export const actionRule = dedent`
|
|
|
307
316
|
Use context parameter (second argument) to narrow click area when:
|
|
308
317
|
- The same text/button appears multiple times on page
|
|
309
318
|
- You need to click inside a specific form, modal, or section
|
|
319
|
+
- You need an icon-only confirm/save control next to a field in an inline create/edit row
|
|
310
320
|
Context should be a CSS selector pointing to a unique container.
|
|
311
321
|
|
|
312
322
|
<example>
|
|
@@ -319,6 +329,7 @@ export const actionRule = dedent`
|
|
|
319
329
|
</example>
|
|
320
330
|
|
|
321
331
|
Prefer text/ARIA locators with context over complex CSS/XPath selectors.
|
|
332
|
+
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.
|
|
322
333
|
If locator doesn't work, try CSS or XPath locators.
|
|
323
334
|
If nothing works, use I.clickXY(x, y) as last resort.
|
|
324
335
|
|
|
@@ -49,6 +49,9 @@ export class SessionAnalyst implements Agent {
|
|
|
49
49
|
## Severity (defects only)
|
|
50
50
|
[High] blocks a core flow · [Medium] degrades a flow but workaround exists · [Low] cosmetic / edge case
|
|
51
51
|
|
|
52
|
+
## Naming tests
|
|
53
|
+
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\`.
|
|
54
|
+
|
|
52
55
|
## Format
|
|
53
56
|
|
|
54
57
|
# Session Analysis
|
|
@@ -60,22 +63,22 @@ export class SessionAnalyst implements Agent {
|
|
|
60
63
|
- Features: <capabilities>
|
|
61
64
|
|
|
62
65
|
## What works
|
|
63
|
-
- **<feature>** —
|
|
66
|
+
- **<feature>** — \`ET-2 <title>\`, \`ET-7 <title>\`
|
|
64
67
|
|
|
65
68
|
## Defects
|
|
66
69
|
|
|
67
70
|
### [Medium] <plain-English bug title>
|
|
68
|
-
Affects:
|
|
71
|
+
Affects: \`ET-3 <title>\`, \`ET-5 <title>\`
|
|
69
72
|
Reproduce:
|
|
70
73
|
1. <concrete UI step>
|
|
71
74
|
2. <next>
|
|
72
75
|
Evidence: <one short observation>
|
|
73
76
|
|
|
74
77
|
## UX issues
|
|
75
|
-
- **<feature>** — <what's confusing> (
|
|
78
|
+
- **<feature>** — <what's confusing> (\`ET-7 <title>\`)
|
|
76
79
|
|
|
77
80
|
## Execution Issues
|
|
78
|
-
-
|
|
81
|
+
- \`ET-2 <title>\` — <≤10 words, what was unreliable>
|
|
79
82
|
|
|
80
83
|
## Brevity rules
|
|
81
84
|
|
|
@@ -143,7 +146,7 @@ export class SessionAnalyst implements Agent {
|
|
|
143
146
|
: 'verification_status: none';
|
|
144
147
|
|
|
145
148
|
return dedent`
|
|
146
|
-
<test ref="
|
|
149
|
+
<test ref="ET-${ref}">
|
|
147
150
|
url: ${test.startUrl || '/'}
|
|
148
151
|
scenario: ${test.scenario}
|
|
149
152
|
result: ${test.result || 'unknown'}
|
package/src/ai/task-agent.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import dedent from 'dedent';
|
|
2
1
|
import type { ActionResult } from '../action-result.js';
|
|
3
|
-
import {
|
|
2
|
+
import type { ExperienceTracker } from '../experience-tracker.js';
|
|
4
3
|
import type { KnowledgeTracker } from '../knowledge-tracker.js';
|
|
5
|
-
import { createDebug, pluralize, tag } from '../utils/logger.js';
|
|
6
|
-
|
|
7
|
-
const debugLog = createDebug('explorbot:task-agent');
|
|
8
4
|
import { Historian } from './historian.js';
|
|
9
5
|
import type { Navigator } from './navigator.js';
|
|
10
6
|
import type { Provider } from './provider.js';
|
|
@@ -35,34 +31,11 @@ export abstract class TaskAgent {
|
|
|
35
31
|
protected abstract getProvider(): Provider;
|
|
36
32
|
|
|
37
33
|
protected getKnowledge(actionResult: ActionResult): string {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (knowledgeFiles.length === 0) return '';
|
|
41
|
-
|
|
42
|
-
const knowledgeContent = knowledgeFiles
|
|
43
|
-
.map((k) => k.content)
|
|
44
|
-
.filter((k) => !!k)
|
|
45
|
-
.join('\n\n');
|
|
46
|
-
|
|
47
|
-
tag('operation').log(`Found ${knowledgeFiles.length} relevant knowledge ${pluralize(knowledgeFiles.length, 'file')}`);
|
|
48
|
-
return dedent`
|
|
49
|
-
<knowledge>
|
|
50
|
-
Here is relevant knowledge for this page:
|
|
51
|
-
|
|
52
|
-
${knowledgeContent}
|
|
53
|
-
</knowledge>
|
|
54
|
-
`;
|
|
34
|
+
return this.getKnowledgeTracker().renderRelevantKnowledge(actionResult);
|
|
55
35
|
}
|
|
56
36
|
|
|
57
37
|
protected getExperience(actionResult: ActionResult): string {
|
|
58
|
-
|
|
59
|
-
const toc = tracker.getExperienceTableOfContents(actionResult);
|
|
60
|
-
if (toc.length === 0) return '';
|
|
61
|
-
|
|
62
|
-
const totalSections = toc.reduce((sum, entry) => sum + entry.sections.length, 0);
|
|
63
|
-
debugLog(`injecting experience TOC (${toc.length} files, ${totalSections} sections)`);
|
|
64
|
-
tag('operation').log(`Found ${toc.length} experience ${pluralize(toc.length, 'file')} (${totalSections} sections)`);
|
|
65
|
-
return renderExperienceToc(toc);
|
|
38
|
+
return this.getExperienceTracker().renderExperienceTocFor(actionResult);
|
|
66
39
|
}
|
|
67
40
|
|
|
68
41
|
setHistorian(historian: Historian): void {
|
package/src/ai/tester.ts
CHANGED
|
@@ -5,17 +5,15 @@ import dedent from 'dedent';
|
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { ActionResult } from '../action-result.ts';
|
|
7
7
|
import { clearActivity, setActivity } from '../activity.ts';
|
|
8
|
-
import { ConfigParser } from '../config.ts';
|
|
9
8
|
import type { ExperienceTracker } from '../experience-tracker.ts';
|
|
10
9
|
import type Explorer from '../explorer.ts';
|
|
11
10
|
import { Observability } from '../observability.ts';
|
|
12
|
-
import type { StateTransition
|
|
11
|
+
import type { StateTransition } from '../state-manager.ts';
|
|
13
12
|
import { Stats } from '../stats.ts';
|
|
14
|
-
import { type
|
|
13
|
+
import { type Test, TestResult, type TestResultType } from '../test-plan.ts';
|
|
15
14
|
import { detectFocusArea, extractFocusedElement } from '../utils/aria.ts';
|
|
16
15
|
import { ErrorPageError, isErrorPage } from '../utils/error-page.ts';
|
|
17
16
|
import { HooksRunner } from '../utils/hooks-runner.ts';
|
|
18
|
-
import { codeToMarkdown } from '../utils/html.ts';
|
|
19
17
|
import { createDebug, tag } from '../utils/logger.ts';
|
|
20
18
|
import { loop } from '../utils/loop.ts';
|
|
21
19
|
import type { Agent } from './agent.ts';
|
|
@@ -27,7 +25,7 @@ import { Provider } from './provider.ts';
|
|
|
27
25
|
import { Researcher } from './researcher.ts';
|
|
28
26
|
import { actionRule, capabilityGroundingRule, dataProtectionRules, focusedElementRule, formRequirementsRule, locatorRule, multipleTabsRule, sectionContextRule } from './rules.ts';
|
|
29
27
|
import { TaskAgent } from './task-agent.ts';
|
|
30
|
-
import { createCodeceptJSTools,
|
|
28
|
+
import { createCodeceptJSTools, createIframeTools } from './tools.ts';
|
|
31
29
|
|
|
32
30
|
const debugLog = createDebug('explorbot:tester');
|
|
33
31
|
|
|
@@ -58,7 +56,6 @@ export class Tester extends TaskAgent implements Agent {
|
|
|
58
56
|
researcher: Researcher;
|
|
59
57
|
navigator: Navigator;
|
|
60
58
|
agentTools: any;
|
|
61
|
-
executionLogFile: string | null = null;
|
|
62
59
|
private previousUrl: string | null = null;
|
|
63
60
|
private previousStateHash: string | null = null;
|
|
64
61
|
private pageStateHash: string | null = null;
|
|
@@ -151,10 +148,6 @@ export class Tester extends TaskAgent implements Agent {
|
|
|
151
148
|
conversation.markLastMessageCacheable();
|
|
152
149
|
this.currentConversation = conversation;
|
|
153
150
|
|
|
154
|
-
const outputDir = ConfigParser.getInstance().getOutputDir();
|
|
155
|
-
this.executionLogFile = join(outputDir, `tester_${task.sessionName}.md`);
|
|
156
|
-
// Note: Markdown saving functionality removed from Conversation class
|
|
157
|
-
|
|
158
151
|
const scenarioBlock = this.buildScenarioBlock(task, initialState);
|
|
159
152
|
conversation.addUserText(scenarioBlock);
|
|
160
153
|
conversation.markLastMessageCacheable();
|
|
@@ -261,10 +254,12 @@ export class Tester extends TaskAgent implements Agent {
|
|
|
261
254
|
|
|
262
255
|
const tools = {
|
|
263
256
|
...codeceptjsTools,
|
|
264
|
-
...(currentState.isInsideIframe ? createSpecialContextTools(this.explorer, 'iframe') : {}),
|
|
265
257
|
...this.createTestFlowTools(task, currentState, conversation),
|
|
266
258
|
...this.agentTools,
|
|
267
259
|
};
|
|
260
|
+
if (currentState.isInsideIframe) {
|
|
261
|
+
Object.assign(tools, createIframeTools(this.explorer));
|
|
262
|
+
}
|
|
268
263
|
|
|
269
264
|
debugLog(`Test ${task.scenario} iteration ${iteration}`);
|
|
270
265
|
|
|
@@ -526,7 +521,6 @@ export class Tester extends TaskAgent implements Agent {
|
|
|
526
521
|
const currentStateHash = currentState.hash;
|
|
527
522
|
|
|
528
523
|
const isNewUrl = this.previousUrl !== currentUrl;
|
|
529
|
-
const isStateChanged = !isNewUrl && this.previousStateHash !== currentStateHash;
|
|
530
524
|
|
|
531
525
|
this.previousUrl = currentUrl;
|
|
532
526
|
this.previousStateHash = currentStateHash;
|
|
@@ -641,27 +635,6 @@ export class Tester extends TaskAgent implements Agent {
|
|
|
641
635
|
}
|
|
642
636
|
}
|
|
643
637
|
|
|
644
|
-
// if (isStateChanged) {
|
|
645
|
-
// const combinedHtml = await currentState.combinedHtml();
|
|
646
|
-
// context += dedent`
|
|
647
|
-
// Context (state changed):
|
|
648
|
-
|
|
649
|
-
// <page>
|
|
650
|
-
// CURRENT URL: ${currentState.url}
|
|
651
|
-
// CURRENT TITLE: ${currentState.title}
|
|
652
|
-
// </page>
|
|
653
|
-
|
|
654
|
-
// <page_html>
|
|
655
|
-
// ${combinedHtml}
|
|
656
|
-
// </page_html>
|
|
657
|
-
|
|
658
|
-
// <page_aria>
|
|
659
|
-
// ${currentState.ariaSnapshot}
|
|
660
|
-
// </page_aria>
|
|
661
|
-
// `;
|
|
662
|
-
// return context;
|
|
663
|
-
// }
|
|
664
|
-
|
|
665
638
|
if (context) return context;
|
|
666
639
|
|
|
667
640
|
if (iteration % 5) return '';
|
|
@@ -680,38 +653,6 @@ export class Tester extends TaskAgent implements Agent {
|
|
|
680
653
|
`;
|
|
681
654
|
}
|
|
682
655
|
|
|
683
|
-
private async promptLogStep(task: Test): Promise<string> {
|
|
684
|
-
let logPrompt = dedent`
|
|
685
|
-
<task>
|
|
686
|
-
Add a note explaining what you achieved with previous action.
|
|
687
|
-
Use tools to interact with the page to achieve the scenario goal or expected outcomes.
|
|
688
|
-
Call record tool to explain the last action
|
|
689
|
-
Format: record([<action performed>, <what has changed>, <what you expect to do next>])
|
|
690
|
-
</task>
|
|
691
|
-
`;
|
|
692
|
-
|
|
693
|
-
if (task.getPrintableNotes()) {
|
|
694
|
-
logPrompt = dedent`
|
|
695
|
-
Your interaction log notes:
|
|
696
|
-
<notes>
|
|
697
|
-
${task.getPrintableNotes()}
|
|
698
|
-
</notes>
|
|
699
|
-
|
|
700
|
-
<rules>
|
|
701
|
-
Use your previous interaction notes to guide your next actions.
|
|
702
|
-
Do not perform the same checks.
|
|
703
|
-
</rules>
|
|
704
|
-
`;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
const remaining = task.getRemainingExpectations();
|
|
708
|
-
if (remaining.length > 0) {
|
|
709
|
-
logPrompt += `\nExpected steps to check: ${remaining.join(', ')}`;
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
return logPrompt;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
656
|
private finishTest(task: Test): void {
|
|
716
657
|
if (!task.hasFinished) {
|
|
717
658
|
task.finish(TestResult.FAILED);
|
|
@@ -810,11 +751,13 @@ export class Tester extends TaskAgent implements Agent {
|
|
|
810
751
|
- Before retrying your actions check maybe they already achived expected results. Use see() tool for that
|
|
811
752
|
- 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.
|
|
812
753
|
- 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.
|
|
813
|
-
- When selecting related entities from a list, do not choose rows/options/cards marked as "0 items", "0
|
|
814
|
-
- In selection pickers, counters such as "Selected 0", "Matched
|
|
754
|
+
- 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.
|
|
755
|
+
- 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.
|
|
815
756
|
- 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.
|
|
816
|
-
- For filter/tab scenarios, success requires BOTH: the requested
|
|
817
|
-
-
|
|
757
|
+
- 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.
|
|
758
|
+
- 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.
|
|
759
|
+
- 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.
|
|
760
|
+
- 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.
|
|
818
761
|
- When filling complex form with lot of actions performed, use see() to look which fields were filled and which are not
|
|
819
762
|
- When verify() fails, use see() to visually confirm the result — visual confirmation is equally valid evidence
|
|
820
763
|
- For visual state verification (active tabs, selected items, counts, colors), prefer see() over DOM-based verify()
|
|
@@ -875,10 +818,6 @@ export class Tester extends TaskAgent implements Agent {
|
|
|
875
818
|
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)
|
|
876
819
|
Initial page URL: ${actionResult.url}
|
|
877
820
|
|
|
878
|
-
${capabilityGroundingRule}
|
|
879
|
-
|
|
880
|
-
${dataProtectionRules}
|
|
881
|
-
|
|
882
821
|
${this.buildDeletionScope(task)}
|
|
883
822
|
|
|
884
823
|
${this.buildAvailableFiles()}
|