explorbot 0.1.22 → 0.1.24
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/dist/package.json +1 -1
- package/dist/src/ai/captain/web-mode.js +2 -2
- package/dist/src/ai/navigator.js +3 -3
- package/dist/src/ai/pilot.js +9 -7
- package/dist/src/ai/tester.js +1 -1
- package/dist/src/ai/tools.js +39 -37
- package/dist/src/experience-tracker.js +1 -1
- package/dist/src/explorbot.js +1 -1
- package/package.json +1 -1
- package/src/ai/captain/web-mode.ts +2 -2
- package/src/ai/navigator.ts +4 -4
- package/src/ai/pilot.ts +8 -7
- package/src/ai/tester.ts +1 -1
- package/src/ai/tools.ts +45 -41
- package/src/experience-tracker.ts +1 -1
- package/src/explorbot.ts +1 -1
package/dist/package.json
CHANGED
|
@@ -21,7 +21,7 @@ export function WithWebMode(Base) {
|
|
|
21
21
|
return state ? ActionResult.fromState(state) : null;
|
|
22
22
|
},
|
|
23
23
|
});
|
|
24
|
-
const { see, context, visualClick,
|
|
24
|
+
const { see, context, visualClick, learnExperience } = agentTools;
|
|
25
25
|
return {
|
|
26
26
|
navigate: tool({
|
|
27
27
|
description: 'Navigate to a URL or page description using AI-powered navigation.',
|
|
@@ -98,7 +98,7 @@ export function WithWebMode(Base) {
|
|
|
98
98
|
see,
|
|
99
99
|
context,
|
|
100
100
|
visualClick,
|
|
101
|
-
|
|
101
|
+
learnExperience,
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
104
|
webModePrompt() {
|
package/dist/src/ai/navigator.js
CHANGED
|
@@ -448,16 +448,16 @@ class Navigator {
|
|
|
448
448
|
const s = stateManager.getCurrentState();
|
|
449
449
|
return s ? ActionResult.fromState(s) : null;
|
|
450
450
|
};
|
|
451
|
-
const {
|
|
451
|
+
const { learnExperience } = createAgentTools({
|
|
452
452
|
explorer: this.explorer,
|
|
453
453
|
researcher: null,
|
|
454
454
|
navigator: this,
|
|
455
455
|
experienceTracker: this.experienceTracker,
|
|
456
456
|
getState,
|
|
457
457
|
});
|
|
458
|
-
if (!
|
|
458
|
+
if (!learnExperience)
|
|
459
459
|
return undefined;
|
|
460
|
-
return {
|
|
460
|
+
return { learnExperience };
|
|
461
461
|
}
|
|
462
462
|
async freeSail(opts, actionResult) {
|
|
463
463
|
const stateManager = this.explorer.getStateManager();
|
package/dist/src/ai/pilot.js
CHANGED
|
@@ -389,12 +389,12 @@ export class Pilot {
|
|
|
389
389
|
the elements needed for the scenario. The page summary does not list every element.
|
|
390
390
|
Prefer interacting with the current page over navigating away.
|
|
391
391
|
|
|
392
|
-
If you load a recipe via
|
|
392
|
+
If you load a recipe via learnExperience, do NOT rewrite its code in your plan — the
|
|
393
393
|
raw recipe is forwarded to Tester automatically. Reference it by step ("apply recipe
|
|
394
394
|
steps 1–3, then…") and call out anywhere your scenario diverges from it.
|
|
395
395
|
|
|
396
396
|
Be concise and specific. Tester will follow your plan.
|
|
397
|
-
`, 'pilot.planTest', { tools: true,
|
|
397
|
+
`, 'pilot.planTest', { tools: true, maxToolRoundtrips: 3, task });
|
|
398
398
|
}
|
|
399
399
|
async reviewNewPage(task, currentState, testerConversation) {
|
|
400
400
|
if (!this.conversation)
|
|
@@ -484,7 +484,7 @@ export class Pilot {
|
|
|
484
484
|
this.conversation.addUserText(finalUserText);
|
|
485
485
|
let tools;
|
|
486
486
|
if (opts.tools) {
|
|
487
|
-
tools =
|
|
487
|
+
tools = this.pickPlanningTools();
|
|
488
488
|
}
|
|
489
489
|
if (opts.tools && opts.task) {
|
|
490
490
|
tools = { ...tools, ...this.buildPreconditionTool(opts.task) };
|
|
@@ -496,7 +496,7 @@ export class Pilot {
|
|
|
496
496
|
experimental_telemetry: { functionId },
|
|
497
497
|
});
|
|
498
498
|
const text = result?.response?.text || '';
|
|
499
|
-
const learned = (result?.toolExecutions || []).filter((e) => e.toolName === '
|
|
499
|
+
const learned = (result?.toolExecutions || []).filter((e) => e.toolName === 'learnExperience' && e.output?.content).map((e) => e.output.content);
|
|
500
500
|
if (learned.length === 0)
|
|
501
501
|
return text;
|
|
502
502
|
return dedent `
|
|
@@ -521,7 +521,7 @@ export class Pilot {
|
|
|
521
521
|
return renderExperienceToc(toc);
|
|
522
522
|
}
|
|
523
523
|
pickPlanningTools() {
|
|
524
|
-
const { see, context, verify, research, getVisitedStates, xpathCheck,
|
|
524
|
+
const { see, context, verify, research, getVisitedStates, xpathCheck, learnExperience, askUser } = this.agentTools ?? {};
|
|
525
525
|
const planning = {};
|
|
526
526
|
if (see)
|
|
527
527
|
planning.see = see;
|
|
@@ -535,8 +535,10 @@ export class Pilot {
|
|
|
535
535
|
planning.getVisitedStates = getVisitedStates;
|
|
536
536
|
if (xpathCheck)
|
|
537
537
|
planning.xpathCheck = xpathCheck;
|
|
538
|
-
if (
|
|
539
|
-
planning.
|
|
538
|
+
if (learnExperience)
|
|
539
|
+
planning.learnExperience = learnExperience;
|
|
540
|
+
if (askUser)
|
|
541
|
+
planning.askUser = askUser;
|
|
540
542
|
return planning;
|
|
541
543
|
}
|
|
542
544
|
buildPreconditionTool(task) {
|
package/dist/src/ai/tester.js
CHANGED
|
@@ -668,7 +668,7 @@ export class Tester extends TaskAgent {
|
|
|
668
668
|
- When filling complex form with lot of actions performed, use see() to look which fields were filled and which are not
|
|
669
669
|
- When verify() fails, use see() to visually confirm the result — visual confirmation is equally valid evidence
|
|
670
670
|
- For visual state verification (active tabs, selected items, counts, colors), prefer see() over DOM-based verify()
|
|
671
|
-
- When click() fails
|
|
671
|
+
- When click() fails on an element you can see — or believe is there but may look different — you MUST try visualClick() before giving up; don't repeat visualClick in a row
|
|
672
672
|
- If you land on a "Not Found", 404, or error page that is NOT part of the scenario, call reset() immediately to return to the initial page and try again
|
|
673
673
|
- If you see a server error page (500, 503, etc.), record it with record({ notes: ["Server error on /path"], status: "fail" }) and call reset() to continue testing
|
|
674
674
|
</rules>
|
package/dist/src/ai/tools.js
CHANGED
|
@@ -391,7 +391,7 @@ export function createSpecialContextTools(explorer, context) {
|
|
|
391
391
|
}),
|
|
392
392
|
};
|
|
393
393
|
}
|
|
394
|
-
export function createAgentTools({ explorer, researcher, navigator, experienceTracker, getState, }) {
|
|
394
|
+
export function createAgentTools({ explorer, researcher, navigator, experienceTracker, getState, supervisor, }) {
|
|
395
395
|
let visionDisabled = false;
|
|
396
396
|
const tools = {
|
|
397
397
|
see: tool({
|
|
@@ -685,41 +685,6 @@ export function createAgentTools({ explorer, researcher, navigator, experienceTr
|
|
|
685
685
|
}
|
|
686
686
|
},
|
|
687
687
|
}),
|
|
688
|
-
askUser: tool({
|
|
689
|
-
description: dedent `
|
|
690
|
-
Ask the user for help when you're stuck or unsure how to proceed.
|
|
691
|
-
Only available in interactive mode (TUI).
|
|
692
|
-
|
|
693
|
-
Use when:
|
|
694
|
-
- Locator-based clicks keep failing
|
|
695
|
-
- You can't find an element that should exist
|
|
696
|
-
- Form interaction isn't working as expected
|
|
697
|
-
- You need clarification on what action to take
|
|
698
|
-
`,
|
|
699
|
-
inputSchema: z.object({
|
|
700
|
-
question: z.string().describe('What you need help with - be specific about what failed'),
|
|
701
|
-
context: z.string().optional().describe('Relevant context like locators tried, errors received'),
|
|
702
|
-
}),
|
|
703
|
-
execute: async ({ question, context }) => {
|
|
704
|
-
if (!isInteractive()) {
|
|
705
|
-
return {
|
|
706
|
-
success: false,
|
|
707
|
-
message: 'User input not available in non-interactive mode',
|
|
708
|
-
suggestion: 'Continue with automated recovery',
|
|
709
|
-
};
|
|
710
|
-
}
|
|
711
|
-
const prompt = context ? `${question}\n\nContext: ${context}\n\nYour suggestion ("skip" to continue):` : `${question}\n\nYour suggestion ("skip" to continue):`;
|
|
712
|
-
const userInput = await pause(prompt);
|
|
713
|
-
if (!userInput || userInput.toLowerCase() === 'skip') {
|
|
714
|
-
return { success: false, message: 'User skipped' };
|
|
715
|
-
}
|
|
716
|
-
return {
|
|
717
|
-
success: true,
|
|
718
|
-
userSuggestion: userInput,
|
|
719
|
-
instruction: 'Follow the user suggestion. Use interact() tool to execute.',
|
|
720
|
-
};
|
|
721
|
-
},
|
|
722
|
-
}),
|
|
723
688
|
back: tool({
|
|
724
689
|
description: dedent `
|
|
725
690
|
Navigate back to the previous page (most recent URL different from current).
|
|
@@ -834,7 +799,7 @@ export function createAgentTools({ explorer, researcher, navigator, experienceTr
|
|
|
834
799
|
}),
|
|
835
800
|
};
|
|
836
801
|
if (experienceTracker && getState) {
|
|
837
|
-
tools.
|
|
802
|
+
tools.learnExperience = tool({
|
|
838
803
|
description: dedent `
|
|
839
804
|
Read the full body of a specific experience section listed in <experience>.
|
|
840
805
|
The TOC shows entries like "A.1 ## FLOW: ..." or "A.2 ## ACTION: ...". Pass the fileTag and sectionIndex.
|
|
@@ -857,6 +822,43 @@ export function createAgentTools({ explorer, researcher, navigator, experienceTr
|
|
|
857
822
|
},
|
|
858
823
|
});
|
|
859
824
|
}
|
|
825
|
+
if (supervisor) {
|
|
826
|
+
tools.askUser = tool({
|
|
827
|
+
description: dedent `
|
|
828
|
+
Ask the user for help when automated recovery is stuck or the next step is unclear.
|
|
829
|
+
Only available in interactive mode (TUI).
|
|
830
|
+
|
|
831
|
+
Use when:
|
|
832
|
+
- The Tester keeps failing the same locator/element
|
|
833
|
+
- An element that should exist cannot be found
|
|
834
|
+
- Form interaction isn't working as expected
|
|
835
|
+
- You need a human decision on how to proceed
|
|
836
|
+
`,
|
|
837
|
+
inputSchema: z.object({
|
|
838
|
+
question: z.string().describe('What you need help with - be specific about what failed'),
|
|
839
|
+
context: z.string().optional().describe('Relevant context like locators tried, errors received'),
|
|
840
|
+
}),
|
|
841
|
+
execute: async ({ question, context }) => {
|
|
842
|
+
if (!isInteractive()) {
|
|
843
|
+
return {
|
|
844
|
+
success: false,
|
|
845
|
+
message: 'User input not available in non-interactive mode',
|
|
846
|
+
suggestion: 'Continue with automated recovery',
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
const prompt = context ? `${question}\n\nContext: ${context}\n\nYour suggestion ("skip" to continue):` : `${question}\n\nYour suggestion ("skip" to continue):`;
|
|
850
|
+
const userInput = await pause(prompt);
|
|
851
|
+
if (!userInput || userInput.toLowerCase() === 'skip') {
|
|
852
|
+
return { success: false, message: 'User skipped' };
|
|
853
|
+
}
|
|
854
|
+
return {
|
|
855
|
+
success: true,
|
|
856
|
+
userSuggestion: userInput,
|
|
857
|
+
instruction: 'Relay this suggestion to the Tester as the next concrete step.',
|
|
858
|
+
};
|
|
859
|
+
},
|
|
860
|
+
});
|
|
861
|
+
}
|
|
860
862
|
return tools;
|
|
861
863
|
}
|
|
862
864
|
const PAGE_DIFF_SUGGESTION = 'Analyze page diff. htmlParts shows what changed and WHERE — each part has a container selector. Use the container as context when clicking elements from the diff.';
|
|
@@ -455,7 +455,7 @@ export function renderExperienceToc(toc) {
|
|
|
455
455
|
lines.push('Locators and step ordering worked then; the page may have changed since.');
|
|
456
456
|
lines.push('Treat as a starting hypothesis, not ground truth. If a step fails, fall back to ARIA/UI-map.');
|
|
457
457
|
lines.push('FLOW: = multi-step recipe (bullets + code + discovery). ACTION: = single-step snippet (one code block).');
|
|
458
|
-
lines.push('Call
|
|
458
|
+
lines.push('Call learnExperience({ fileTag, sectionIndex }) to read a section when it looks relevant to the current step.');
|
|
459
459
|
lines.push('');
|
|
460
460
|
for (const entry of toc) {
|
|
461
461
|
lines.push(`File ${entry.fileTag} ${entry.url}:`);
|
package/dist/src/explorbot.js
CHANGED
|
@@ -170,7 +170,7 @@ export class ExplorBot {
|
|
|
170
170
|
const state = stateManager.getCurrentState();
|
|
171
171
|
return state ? ActionResult.fromState(state) : null;
|
|
172
172
|
};
|
|
173
|
-
const tools = createAgentTools({ explorer, researcher, navigator, experienceTracker, getState });
|
|
173
|
+
const tools = createAgentTools({ explorer, researcher, navigator, experienceTracker, getState, supervisor: true });
|
|
174
174
|
return new Pilot(ai, tools, researcher, explorer, experienceTracker);
|
|
175
175
|
}));
|
|
176
176
|
}
|
package/package.json
CHANGED
|
@@ -22,7 +22,7 @@ export function WithWebMode<T extends Constructor>(Base: T) {
|
|
|
22
22
|
return state ? ActionResult.fromState(state) : null;
|
|
23
23
|
},
|
|
24
24
|
});
|
|
25
|
-
const { see, context, visualClick,
|
|
25
|
+
const { see, context, visualClick, learnExperience } = agentTools;
|
|
26
26
|
|
|
27
27
|
return {
|
|
28
28
|
navigate: tool({
|
|
@@ -103,7 +103,7 @@ export function WithWebMode<T extends Constructor>(Base: T) {
|
|
|
103
103
|
see,
|
|
104
104
|
context,
|
|
105
105
|
visualClick,
|
|
106
|
-
|
|
106
|
+
learnExperience,
|
|
107
107
|
};
|
|
108
108
|
}
|
|
109
109
|
|
package/src/ai/navigator.ts
CHANGED
|
@@ -481,21 +481,21 @@ class Navigator implements Agent {
|
|
|
481
481
|
return resolved;
|
|
482
482
|
}
|
|
483
483
|
|
|
484
|
-
private buildExperienceTools(): {
|
|
484
|
+
private buildExperienceTools(): { learnExperience: unknown } | undefined {
|
|
485
485
|
const stateManager = this.explorer.getStateManager();
|
|
486
486
|
const getState = () => {
|
|
487
487
|
const s = stateManager.getCurrentState();
|
|
488
488
|
return s ? ActionResult.fromState(s) : null;
|
|
489
489
|
};
|
|
490
|
-
const {
|
|
490
|
+
const { learnExperience } = createAgentTools({
|
|
491
491
|
explorer: this.explorer,
|
|
492
492
|
researcher: null as unknown as Researcher,
|
|
493
493
|
navigator: this,
|
|
494
494
|
experienceTracker: this.experienceTracker,
|
|
495
495
|
getState,
|
|
496
496
|
});
|
|
497
|
-
if (!
|
|
498
|
-
return {
|
|
497
|
+
if (!learnExperience) return undefined;
|
|
498
|
+
return { learnExperience };
|
|
499
499
|
}
|
|
500
500
|
|
|
501
501
|
async freeSail(opts?: { strategy?: 'deep' | 'shallow'; scope?: string; visitedUrls?: Set<string> }, actionResult?: ActionResult): Promise<{ target: string; reason: string } | null> {
|
package/src/ai/pilot.ts
CHANGED
|
@@ -439,14 +439,14 @@ export class Pilot implements Agent {
|
|
|
439
439
|
the elements needed for the scenario. The page summary does not list every element.
|
|
440
440
|
Prefer interacting with the current page over navigating away.
|
|
441
441
|
|
|
442
|
-
If you load a recipe via
|
|
442
|
+
If you load a recipe via learnExperience, do NOT rewrite its code in your plan — the
|
|
443
443
|
raw recipe is forwarded to Tester automatically. Reference it by step ("apply recipe
|
|
444
444
|
steps 1–3, then…") and call out anywhere your scenario diverges from it.
|
|
445
445
|
|
|
446
446
|
Be concise and specific. Tester will follow your plan.
|
|
447
447
|
`,
|
|
448
448
|
'pilot.planTest',
|
|
449
|
-
{ tools: true,
|
|
449
|
+
{ tools: true, maxToolRoundtrips: 3, task }
|
|
450
450
|
);
|
|
451
451
|
}
|
|
452
452
|
|
|
@@ -547,7 +547,7 @@ export class Pilot implements Agent {
|
|
|
547
547
|
return `CHECKED: ${checked.length > 0 ? checked.join(', ') : 'none'}\nREMAINING: ${remaining.length > 0 ? remaining.join(', ') : 'none'}`;
|
|
548
548
|
}
|
|
549
549
|
|
|
550
|
-
private async sendToPilot(userText: string, functionId: string, opts: { tools?: boolean;
|
|
550
|
+
private async sendToPilot(userText: string, functionId: string, opts: { tools?: boolean; maxToolRoundtrips?: number; task?: Test } = {}): Promise<string> {
|
|
551
551
|
debugLog(`sendToPilot: ${functionId}, tools: ${!!opts.tools}, roundtrips: ${opts.maxToolRoundtrips ?? 0}`);
|
|
552
552
|
|
|
553
553
|
let finalUserText = userText;
|
|
@@ -560,7 +560,7 @@ export class Pilot implements Agent {
|
|
|
560
560
|
this.conversation!.addUserText(finalUserText);
|
|
561
561
|
let tools: any;
|
|
562
562
|
if (opts.tools) {
|
|
563
|
-
tools =
|
|
563
|
+
tools = this.pickPlanningTools();
|
|
564
564
|
}
|
|
565
565
|
|
|
566
566
|
if (opts.tools && opts.task) {
|
|
@@ -574,7 +574,7 @@ export class Pilot implements Agent {
|
|
|
574
574
|
experimental_telemetry: { functionId },
|
|
575
575
|
});
|
|
576
576
|
const text = result?.response?.text || '';
|
|
577
|
-
const learned = (result?.toolExecutions || []).filter((e: any) => e.toolName === '
|
|
577
|
+
const learned = (result?.toolExecutions || []).filter((e: any) => e.toolName === 'learnExperience' && e.output?.content).map((e: any) => e.output.content);
|
|
578
578
|
if (learned.length === 0) return text;
|
|
579
579
|
return dedent`
|
|
580
580
|
${text}
|
|
@@ -598,7 +598,7 @@ export class Pilot implements Agent {
|
|
|
598
598
|
}
|
|
599
599
|
|
|
600
600
|
private pickPlanningTools() {
|
|
601
|
-
const { see, context, verify, research, getVisitedStates, xpathCheck,
|
|
601
|
+
const { see, context, verify, research, getVisitedStates, xpathCheck, learnExperience, askUser } = this.agentTools ?? {};
|
|
602
602
|
const planning: Record<string, unknown> = {};
|
|
603
603
|
if (see) planning.see = see;
|
|
604
604
|
if (context) planning.context = context;
|
|
@@ -606,7 +606,8 @@ export class Pilot implements Agent {
|
|
|
606
606
|
if (research) planning.research = research;
|
|
607
607
|
if (getVisitedStates) planning.getVisitedStates = getVisitedStates;
|
|
608
608
|
if (xpathCheck) planning.xpathCheck = xpathCheck;
|
|
609
|
-
if (
|
|
609
|
+
if (learnExperience) planning.learnExperience = learnExperience;
|
|
610
|
+
if (askUser) planning.askUser = askUser;
|
|
610
611
|
return planning;
|
|
611
612
|
}
|
|
612
613
|
|
package/src/ai/tester.ts
CHANGED
|
@@ -750,7 +750,7 @@ export class Tester extends TaskAgent implements Agent {
|
|
|
750
750
|
- When filling complex form with lot of actions performed, use see() to look which fields were filled and which are not
|
|
751
751
|
- When verify() fails, use see() to visually confirm the result — visual confirmation is equally valid evidence
|
|
752
752
|
- For visual state verification (active tabs, selected items, counts, colors), prefer see() over DOM-based verify()
|
|
753
|
-
- When click() fails
|
|
753
|
+
- When click() fails on an element you can see — or believe is there but may look different — you MUST try visualClick() before giving up; don't repeat visualClick in a row
|
|
754
754
|
- If you land on a "Not Found", 404, or error page that is NOT part of the scenario, call reset() immediately to return to the initial page and try again
|
|
755
755
|
- If you see a server error page (500, 503, etc.), record it with record({ notes: ["Server error on /path"], status: "fail" }) and call reset() to continue testing
|
|
756
756
|
</rules>
|
package/src/ai/tools.ts
CHANGED
|
@@ -466,12 +466,14 @@ export function createAgentTools({
|
|
|
466
466
|
navigator,
|
|
467
467
|
experienceTracker,
|
|
468
468
|
getState,
|
|
469
|
+
supervisor,
|
|
469
470
|
}: {
|
|
470
471
|
explorer: Explorer;
|
|
471
472
|
researcher: Researcher;
|
|
472
473
|
navigator: Navigator;
|
|
473
474
|
experienceTracker?: ExperienceTracker;
|
|
474
475
|
getState?: () => ActionResult | null;
|
|
476
|
+
supervisor?: boolean;
|
|
475
477
|
}): any {
|
|
476
478
|
let visionDisabled = false;
|
|
477
479
|
|
|
@@ -803,46 +805,6 @@ export function createAgentTools({
|
|
|
803
805
|
},
|
|
804
806
|
}),
|
|
805
807
|
|
|
806
|
-
askUser: tool({
|
|
807
|
-
description: dedent`
|
|
808
|
-
Ask the user for help when you're stuck or unsure how to proceed.
|
|
809
|
-
Only available in interactive mode (TUI).
|
|
810
|
-
|
|
811
|
-
Use when:
|
|
812
|
-
- Locator-based clicks keep failing
|
|
813
|
-
- You can't find an element that should exist
|
|
814
|
-
- Form interaction isn't working as expected
|
|
815
|
-
- You need clarification on what action to take
|
|
816
|
-
`,
|
|
817
|
-
inputSchema: z.object({
|
|
818
|
-
question: z.string().describe('What you need help with - be specific about what failed'),
|
|
819
|
-
context: z.string().optional().describe('Relevant context like locators tried, errors received'),
|
|
820
|
-
}),
|
|
821
|
-
execute: async ({ question, context }) => {
|
|
822
|
-
if (!isInteractive()) {
|
|
823
|
-
return {
|
|
824
|
-
success: false,
|
|
825
|
-
message: 'User input not available in non-interactive mode',
|
|
826
|
-
suggestion: 'Continue with automated recovery',
|
|
827
|
-
};
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
const prompt = context ? `${question}\n\nContext: ${context}\n\nYour suggestion ("skip" to continue):` : `${question}\n\nYour suggestion ("skip" to continue):`;
|
|
831
|
-
|
|
832
|
-
const userInput = await pause(prompt);
|
|
833
|
-
|
|
834
|
-
if (!userInput || userInput.toLowerCase() === 'skip') {
|
|
835
|
-
return { success: false, message: 'User skipped' };
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
return {
|
|
839
|
-
success: true,
|
|
840
|
-
userSuggestion: userInput,
|
|
841
|
-
instruction: 'Follow the user suggestion. Use interact() tool to execute.',
|
|
842
|
-
};
|
|
843
|
-
},
|
|
844
|
-
}),
|
|
845
|
-
|
|
846
808
|
back: tool({
|
|
847
809
|
description: dedent`
|
|
848
810
|
Navigate back to the previous page (most recent URL different from current).
|
|
@@ -973,7 +935,7 @@ export function createAgentTools({
|
|
|
973
935
|
};
|
|
974
936
|
|
|
975
937
|
if (experienceTracker && getState) {
|
|
976
|
-
tools.
|
|
938
|
+
tools.learnExperience = tool({
|
|
977
939
|
description: dedent`
|
|
978
940
|
Read the full body of a specific experience section listed in <experience>.
|
|
979
941
|
The TOC shows entries like "A.1 ## FLOW: ..." or "A.2 ## ACTION: ...". Pass the fileTag and sectionIndex.
|
|
@@ -997,6 +959,48 @@ export function createAgentTools({
|
|
|
997
959
|
});
|
|
998
960
|
}
|
|
999
961
|
|
|
962
|
+
if (supervisor) {
|
|
963
|
+
tools.askUser = tool({
|
|
964
|
+
description: dedent`
|
|
965
|
+
Ask the user for help when automated recovery is stuck or the next step is unclear.
|
|
966
|
+
Only available in interactive mode (TUI).
|
|
967
|
+
|
|
968
|
+
Use when:
|
|
969
|
+
- The Tester keeps failing the same locator/element
|
|
970
|
+
- An element that should exist cannot be found
|
|
971
|
+
- Form interaction isn't working as expected
|
|
972
|
+
- You need a human decision on how to proceed
|
|
973
|
+
`,
|
|
974
|
+
inputSchema: z.object({
|
|
975
|
+
question: z.string().describe('What you need help with - be specific about what failed'),
|
|
976
|
+
context: z.string().optional().describe('Relevant context like locators tried, errors received'),
|
|
977
|
+
}),
|
|
978
|
+
execute: async ({ question, context }) => {
|
|
979
|
+
if (!isInteractive()) {
|
|
980
|
+
return {
|
|
981
|
+
success: false,
|
|
982
|
+
message: 'User input not available in non-interactive mode',
|
|
983
|
+
suggestion: 'Continue with automated recovery',
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
const prompt = context ? `${question}\n\nContext: ${context}\n\nYour suggestion ("skip" to continue):` : `${question}\n\nYour suggestion ("skip" to continue):`;
|
|
988
|
+
|
|
989
|
+
const userInput = await pause(prompt);
|
|
990
|
+
|
|
991
|
+
if (!userInput || userInput.toLowerCase() === 'skip') {
|
|
992
|
+
return { success: false, message: 'User skipped' };
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
return {
|
|
996
|
+
success: true,
|
|
997
|
+
userSuggestion: userInput,
|
|
998
|
+
instruction: 'Relay this suggestion to the Tester as the next concrete step.',
|
|
999
|
+
};
|
|
1000
|
+
},
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1000
1004
|
return tools;
|
|
1001
1005
|
}
|
|
1002
1006
|
|
|
@@ -499,7 +499,7 @@ export function renderExperienceToc(toc: ExperienceTocEntry[]): string {
|
|
|
499
499
|
lines.push('Locators and step ordering worked then; the page may have changed since.');
|
|
500
500
|
lines.push('Treat as a starting hypothesis, not ground truth. If a step fails, fall back to ARIA/UI-map.');
|
|
501
501
|
lines.push('FLOW: = multi-step recipe (bullets + code + discovery). ACTION: = single-step snippet (one code block).');
|
|
502
|
-
lines.push('Call
|
|
502
|
+
lines.push('Call learnExperience({ fileTag, sectionIndex }) to read a section when it looks relevant to the current step.');
|
|
503
503
|
lines.push('');
|
|
504
504
|
for (const entry of toc) {
|
|
505
505
|
lines.push(`File ${entry.fileTag} ${entry.url}:`);
|
package/src/explorbot.ts
CHANGED
|
@@ -210,7 +210,7 @@ export class ExplorBot {
|
|
|
210
210
|
const state = stateManager.getCurrentState();
|
|
211
211
|
return state ? ActionResult.fromState(state) : null;
|
|
212
212
|
};
|
|
213
|
-
const tools = createAgentTools({ explorer, researcher, navigator, experienceTracker, getState });
|
|
213
|
+
const tools = createAgentTools({ explorer, researcher, navigator, experienceTracker, getState, supervisor: true });
|
|
214
214
|
return new Pilot(ai, tools, researcher, explorer, experienceTracker);
|
|
215
215
|
}));
|
|
216
216
|
}
|