explorbot 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +21 -5
  2. package/bin/explorbot-cli.ts +29 -7
  3. package/boat/api-tester/src/config.ts +45 -3
  4. package/boat/doc-collector/src/ai/documentarian.ts +5 -2
  5. package/boat/doc-collector/src/ai/tools.ts +12 -11
  6. package/boat/doc-collector/src/cli.ts +1 -6
  7. package/boat/doc-collector/src/docbot.ts +3 -3
  8. package/boat/doc-collector/src/screenshots.ts +2 -2
  9. package/dist/bin/explorbot-cli.js +26 -6
  10. package/dist/boat/api-tester/src/config.js +43 -4
  11. package/dist/boat/doc-collector/src/ai/documentarian.js +4 -2
  12. package/dist/boat/doc-collector/src/ai/tools.js +11 -11
  13. package/dist/boat/doc-collector/src/cli.js +1 -5
  14. package/dist/boat/doc-collector/src/docbot.js +3 -3
  15. package/dist/boat/doc-collector/src/screenshots.js +2 -2
  16. package/dist/models.json +30 -0
  17. package/dist/package.json +5 -2
  18. package/dist/src/action.d.ts +11 -3
  19. package/dist/src/action.js +22 -5
  20. package/dist/src/ai/agent.d.ts +17 -0
  21. package/dist/src/ai/captain/idle-mode.js +1 -1
  22. package/dist/src/ai/captain/test-mode.js +1 -1
  23. package/dist/src/ai/captain/web-mode.js +18 -22
  24. package/dist/src/ai/captain.js +13 -26
  25. package/dist/src/ai/driller.d.ts +2 -13
  26. package/dist/src/ai/driller.js +23 -38
  27. package/dist/src/ai/historian/codeceptjs.js +1 -1
  28. package/dist/src/ai/historian/playwright.d.ts +3 -2
  29. package/dist/src/ai/historian/playwright.js +1 -1
  30. package/dist/src/ai/historian/screencast.d.ts +2 -1
  31. package/dist/src/ai/historian/screencast.js +2 -2
  32. package/dist/src/ai/historian.d.ts +5 -4
  33. package/dist/src/ai/navigator.d.ts +6 -2
  34. package/dist/src/ai/navigator.js +26 -22
  35. package/dist/src/ai/pilot.d.ts +13 -6
  36. package/dist/src/ai/pilot.js +49 -44
  37. package/dist/src/ai/planner.d.ts +3 -2
  38. package/dist/src/ai/planner.js +19 -17
  39. package/dist/src/ai/provider.js +0 -1
  40. package/dist/src/ai/quartermaster.d.ts +3 -2
  41. package/dist/src/ai/quartermaster.js +4 -4
  42. package/dist/src/ai/rerunner.d.ts +2 -11
  43. package/dist/src/ai/rerunner.js +9 -22
  44. package/dist/src/ai/researcher/coordinates.js +4 -3
  45. package/dist/src/ai/researcher/deep-analysis.d.ts +2 -0
  46. package/dist/src/ai/researcher/deep-analysis.js +6 -6
  47. package/dist/src/ai/researcher/locators.d.ts +9 -2
  48. package/dist/src/ai/researcher/locators.js +65 -8
  49. package/dist/src/ai/researcher/sections.d.ts +2 -0
  50. package/dist/src/ai/researcher/sections.js +2 -2
  51. package/dist/src/ai/researcher.js +29 -54
  52. package/dist/src/ai/task-agent.d.ts +18 -5
  53. package/dist/src/ai/task-agent.js +35 -6
  54. package/dist/src/ai/tester.d.ts +6 -12
  55. package/dist/src/ai/tester.js +37 -51
  56. package/dist/src/ai/tools.d.ts +11 -10
  57. package/dist/src/ai/tools.js +24 -36
  58. package/dist/src/command-handler.js +1 -1
  59. package/dist/src/commands/context-aria-command.js +1 -1
  60. package/dist/src/commands/context-command.js +7 -5
  61. package/dist/src/commands/context-data-command.js +2 -2
  62. package/dist/src/commands/context-experience-command.js +2 -2
  63. package/dist/src/commands/context-html-command.js +2 -2
  64. package/dist/src/commands/context-knowledge-command.js +1 -1
  65. package/dist/src/commands/drill-command.js +1 -1
  66. package/dist/src/commands/explore-command.js +4 -3
  67. package/dist/src/commands/freesail-command.js +2 -2
  68. package/dist/src/commands/learn-command.js +4 -4
  69. package/dist/src/commands/path-command.js +1 -1
  70. package/dist/src/commands/research-command.js +1 -1
  71. package/dist/src/commands/test-command.js +1 -1
  72. package/dist/src/components/App.js +1 -1
  73. package/dist/src/config.d.ts +17 -0
  74. package/dist/src/config.js +144 -10
  75. package/dist/src/experience-tracker.js +1 -1
  76. package/dist/src/explorbot.d.ts +17 -8
  77. package/dist/src/explorbot.js +74 -36
  78. package/dist/src/explorer.d.ts +66 -91
  79. package/dist/src/explorer.js +326 -567
  80. package/dist/src/state-manager.d.ts +6 -1
  81. package/dist/src/state-manager.js +1 -0
  82. package/dist/src/utils/hooks-runner.js +2 -4
  83. package/dist/src/utils/test-files.js +1 -1
  84. package/dist/src/utils/web-annotate.d.ts +5 -0
  85. package/dist/src/utils/web-annotate.js +58 -0
  86. package/dist/src/utils/web-eidx.d.ts +2 -0
  87. package/dist/src/utils/web-eidx.js +20 -0
  88. package/dist/src/utils/web-element.d.ts +1 -0
  89. package/dist/src/utils/web-element.js +29 -0
  90. package/models.json +30 -0
  91. package/package.json +5 -2
  92. package/src/action.ts +25 -5
  93. package/src/ai/agent.ts +20 -0
  94. package/src/ai/captain/idle-mode.ts +1 -1
  95. package/src/ai/captain/test-mode.ts +1 -1
  96. package/src/ai/captain/web-mode.ts +18 -23
  97. package/src/ai/captain.ts +12 -25
  98. package/src/ai/driller.ts +50 -67
  99. package/src/ai/historian/codeceptjs.ts +1 -1
  100. package/src/ai/historian/playwright.ts +4 -3
  101. package/src/ai/historian/screencast.ts +4 -3
  102. package/src/ai/historian.ts +3 -2
  103. package/src/ai/navigator.ts +30 -25
  104. package/src/ai/pilot.ts +55 -47
  105. package/src/ai/planner.ts +22 -20
  106. package/src/ai/provider.ts +0 -1
  107. package/src/ai/quartermaster.ts +5 -4
  108. package/src/ai/rerunner.ts +10 -29
  109. package/src/ai/researcher/coordinates.ts +4 -3
  110. package/src/ai/researcher/deep-analysis.ts +8 -6
  111. package/src/ai/researcher/locators.ts +66 -9
  112. package/src/ai/researcher/sections.ts +4 -2
  113. package/src/ai/researcher.ts +32 -61
  114. package/src/ai/task-agent.ts +45 -11
  115. package/src/ai/tester.ts +40 -57
  116. package/src/ai/tools.ts +32 -53
  117. package/src/command-handler.ts +1 -1
  118. package/src/commands/context-aria-command.ts +1 -1
  119. package/src/commands/context-command.ts +7 -5
  120. package/src/commands/context-data-command.ts +2 -2
  121. package/src/commands/context-experience-command.ts +2 -2
  122. package/src/commands/context-html-command.ts +2 -2
  123. package/src/commands/context-knowledge-command.ts +1 -1
  124. package/src/commands/drill-command.ts +1 -1
  125. package/src/commands/explore-command.ts +4 -3
  126. package/src/commands/freesail-command.ts +2 -2
  127. package/src/commands/learn-command.ts +4 -4
  128. package/src/commands/path-command.ts +1 -1
  129. package/src/commands/research-command.ts +1 -1
  130. package/src/commands/test-command.ts +1 -1
  131. package/src/components/App.tsx +1 -1
  132. package/src/config.ts +173 -11
  133. package/src/experience-tracker.ts +1 -1
  134. package/src/explorbot.ts +78 -39
  135. package/src/explorer.ts +389 -628
  136. package/src/state-manager.ts +7 -1
  137. package/src/utils/hooks-runner.ts +2 -4
  138. package/src/utils/test-files.ts +1 -1
  139. package/src/utils/web-annotate.ts +64 -0
  140. package/src/utils/web-eidx.ts +21 -0
  141. package/src/utils/web-element.ts +30 -0
@@ -6,10 +6,11 @@ import { setActivity } from "../activity.js";
6
6
  import { Observability } from "../observability.js";
7
7
  import { Plan, Test, TestResult } from "../test-plan.js";
8
8
  import { collectInteractiveNodes } from "../utils/aria.js";
9
- import { HooksRunner } from "../utils/hooks-runner.js";
10
9
  import { EXPLORBOT_ATTRS, HTML_COMPOSITE_AREA_HINTS, HTML_COMPOSITE_TARGET_ROLES, HTML_EXTRACTION_LIMITS, HTML_FORM_CONTROL_ROLES, HTML_FORM_CONTROL_TAGS, HTML_INTERACTIVE_ROLES, HTML_SELECTORS, HTML_VISIBILITY_LIMITS, getComponentScopeHtmlExtractorSource, getVisibleOverlayHtmlExtractorSource, inferHtmlRole, } from "../utils/html.js";
11
10
  import { createDebug, tag } from "../utils/logger.js";
12
11
  import { loop, pause } from "../utils/loop.js";
12
+ import { annotatePageElements } from "../utils/web-annotate.js";
13
+ import { eidxInContainer } from "../utils/web-eidx.js";
13
14
  import { WebElement } from "../utils/web-element.js";
14
15
  import { drillLocatorRule } from "./rules.js";
15
16
  import { TaskAgent, isInteractive } from "./task-agent.js";
@@ -18,36 +19,21 @@ const debugLog = createDebug('explorbot:driller');
18
19
  export class Driller extends TaskAgent {
19
20
  ACTION_TOOLS = ['click', 'pressKey', 'form'];
20
21
  emoji = 'D';
21
- explorer;
22
- provider;
23
22
  navigator;
24
- hooksRunner;
25
23
  currentPlan;
26
24
  allResults = [];
27
25
  verifiedAction = null;
28
26
  pendingNestedContext = null;
29
27
  MAX_COMPONENT_ITERATIONS = 12;
30
- constructor(explorer, provider, navigator) {
31
- super();
32
- this.explorer = explorer;
33
- this.provider = provider;
28
+ constructor(deps, navigator) {
29
+ super(deps);
34
30
  this.navigator = navigator;
35
- this.hooksRunner = new HooksRunner(explorer, explorer.getConfig());
36
31
  }
37
32
  getNavigator() {
38
33
  return this.navigator;
39
34
  }
40
- getExperienceTracker() {
41
- return this.explorer.getStateManager().getExperienceTracker();
42
- }
43
- getKnowledgeTracker() {
44
- return this.explorer.getKnowledgeTracker();
45
- }
46
- getProvider() {
47
- return this.provider;
48
- }
49
35
  getSystemMessage(component) {
50
- const currentUrl = this.explorer.getStateManager().getCurrentState()?.url;
36
+ const currentUrl = this.stateManager.getCurrentState()?.url;
51
37
  const customPrompt = this.provider.getSystemPromptForAgent('driller', currentUrl);
52
38
  return dedent `
53
39
  <role>
@@ -89,7 +75,7 @@ export class Driller extends TaskAgent {
89
75
  }
90
76
  async drill(opts = {}) {
91
77
  const { knowledgePath, maxComponents = 30, interactive = isInteractive() } = opts;
92
- const currentState = this.explorer.getStateManager().getCurrentState();
78
+ const currentState = this.stateManager.getCurrentState();
93
79
  if (!currentState)
94
80
  throw new Error('No page state available');
95
81
  const sessionName = `driller_${Date.now().toString(36)}`;
@@ -130,15 +116,15 @@ export class Driller extends TaskAgent {
130
116
  }
131
117
  async captureAnnotatedState() {
132
118
  setActivity(`${this.emoji} Capturing annotated page state...`, 'action');
133
- const action = this.explorer.createAction();
119
+ const action = this.explorer.action();
134
120
  try {
135
121
  const annotated = await Promise.race([
136
- this.explorer.annotateElements(),
122
+ this.explorer.withPage(annotatePageElements),
137
123
  new Promise((_, reject) => {
138
124
  setTimeout(() => reject(new Error('annotateElements timeout')), 15000);
139
125
  }),
140
126
  ]);
141
- return action.capturePageState({ ariaSnapshot: annotated.ariaSnapshot });
127
+ return action.capturePageState();
142
128
  }
143
129
  catch (error) {
144
130
  tag('warning').log(`Annotated capture failed, falling back to plain page state: ${error instanceof Error ? error.message : error}`);
@@ -150,9 +136,10 @@ export class Driller extends TaskAgent {
150
136
  }
151
137
  async collectComponents(state, maxComponents) {
152
138
  setActivity(`${this.emoji} Collecting components...`, 'action');
153
- const page = this.explorer.playwrightHelper.page;
154
- const eidxList = await this.explorer.getEidxInContainer(null);
155
- const webElements = await WebElement.fromEidxList(page, eidxList);
139
+ const webElements = await this.explorer.withPage(async (page) => {
140
+ const eidxList = await eidxInContainer(page, null);
141
+ return WebElement.fromEidxList(page, eidxList);
142
+ });
156
143
  const ariaNodes = collectInteractiveNodes(state.ariaSnapshot);
157
144
  const scored = webElements
158
145
  .filter((element) => isDrillableElement(element))
@@ -246,13 +233,13 @@ export class Driller extends TaskAgent {
246
233
  const conversation = this.provider.startConversation(this.getSystemMessage(component), 'driller');
247
234
  conversation.addUserText(await this.buildComponentPrompt(originalState, component));
248
235
  let finished = false;
249
- const actionTools = this.createVerifiedActionTools(createCodeceptJSTools(this.explorer, test), component);
236
+ const actionTools = this.createVerifiedActionTools(createCodeceptJSTools(this.toolDeps, test), component);
250
237
  const tools = { ...actionTools, ...this.createDrillFlowTools(originalState, test, interactive) };
251
238
  await loop(async ({ stop, iteration }) => {
252
239
  debugLog(`Drilling component ${component.name}, iteration ${iteration}`);
253
240
  setActivity(`${this.emoji} Drilling ${component.name}...`, 'action');
254
241
  if (iteration > 1) {
255
- const currentState = ActionResult.fromState(this.explorer.getStateManager().getCurrentState() || originalState);
242
+ const currentState = ActionResult.fromState(this.stateManager.getCurrentState() || originalState);
256
243
  conversation.addUserText(await this.buildContextUpdate(currentState, component));
257
244
  if (this.pendingNestedContext) {
258
245
  conversation.addUserText(this.pendingNestedContext);
@@ -479,7 +466,7 @@ export class Driller extends TaskAgent {
479
466
  execute: async ({ reason }) => {
480
467
  await this.restoreOriginalState(originalState, reason);
481
468
  await this.captureAnnotatedState();
482
- const currentState = this.explorer.getStateManager().getCurrentState();
469
+ const currentState = this.stateManager.getCurrentState();
483
470
  return { success: true, url: currentState?.url || originalState.url };
484
471
  },
485
472
  }),
@@ -500,9 +487,9 @@ export class Driller extends TaskAgent {
500
487
  };
501
488
  }
502
489
  async restoreOriginalState(originalState, reason) {
503
- const currentState = this.explorer.getStateManager().getCurrentState();
490
+ const currentState = this.stateManager.getCurrentState();
504
491
  const targetUrl = originalState.fullUrl || originalState.url;
505
- const action = this.explorer.createAction();
492
+ const action = this.explorer.action();
506
493
  if (currentState?.url !== originalState.url) {
507
494
  await action.attempt(`I.amOnPage(${JSON.stringify(targetUrl)})`, `${reason} (restore URL)`);
508
495
  return;
@@ -558,7 +545,7 @@ export class Driller extends TaskAgent {
558
545
  const overlayHtml = await this.getVisibleOverlayHtml();
559
546
  if (!overlayHtml)
560
547
  return null;
561
- const state = this.explorer.getStateManager().getCurrentState();
548
+ const state = this.stateManager.getCurrentState();
562
549
  if (!state)
563
550
  return null;
564
551
  const currentState = ActionResult.fromState(state);
@@ -579,8 +566,7 @@ export class Driller extends TaskAgent {
579
566
  `;
580
567
  }
581
568
  async getVisibleOverlayHtml() {
582
- const page = this.explorer.playwrightHelper.page;
583
- return page.evaluate(({ extractorSource, config }) => {
569
+ return this.explorer.withPage((page) => page.evaluate(({ extractorSource, config }) => {
584
570
  const extract = new Function(`return ${extractorSource}`)();
585
571
  return extract(config);
586
572
  }, {
@@ -591,11 +577,10 @@ export class Driller extends TaskAgent {
591
577
  overlaySelectors: HTML_SELECTORS.semanticOverlays,
592
578
  visibilityLimits: HTML_VISIBILITY_LIMITS,
593
579
  },
594
- });
580
+ }));
595
581
  }
596
582
  async getComponentScopeHtml(component, originalState) {
597
- const page = this.explorer.playwrightHelper.page;
598
- const scopedHtml = await page.evaluate(({ eidx, extractorSource, config }) => {
583
+ const scopedHtml = await this.explorer.withPage((page) => page.evaluate(({ eidx, extractorSource, config }) => {
599
584
  const extract = new Function(`return ${extractorSource}`)();
600
585
  return extract(eidx, config);
601
586
  }, {
@@ -606,7 +591,7 @@ export class Driller extends TaskAgent {
606
591
  interactiveControlSelector: HTML_SELECTORS.interactiveControl,
607
592
  limits: HTML_EXTRACTION_LIMITS,
608
593
  },
609
- });
594
+ }));
610
595
  if (scopedHtml)
611
596
  return scopedHtml;
612
597
  return await originalState.combinedHtml();
@@ -4,8 +4,8 @@ import { ActionResult } from "../../action-result.js";
4
4
  import { ConfigParser } from "../../config.js";
5
5
  import { tag } from "../../utils/logger.js";
6
6
  import { relativeToCwd } from "../../utils/next-steps.js";
7
- import { safeFilename } from "../../utils/strings.js";
8
7
  import { CODECEPT_TOOLS, isNonReusableCode, stripComments } from "../../utils/step-analyzer.js";
8
+ import { safeFilename } from "../../utils/strings.js";
9
9
  import { ASSERTION_TOOLS } from "../tools.js";
10
10
  import { escapeString, getExecutionLabel } from "./utils.js";
11
11
  export function WithCodeceptJS(Base) {
@@ -1,5 +1,6 @@
1
- import type { StateManager } from '../../state-manager.js';
1
+ import type Explorer from '../../explorer.js';
2
2
  import { type PlaywrightRecorder } from '../../playwright-recorder.js';
3
+ import type { StateManager } from '../../state-manager.js';
3
4
  import type { Plan } from '../../test-plan.js';
4
5
  import type { Conversation } from '../conversation.js';
5
6
  import type { Constructor } from './mixin.js';
@@ -11,7 +12,7 @@ export declare function WithPlaywright<T extends Constructor>(Base: T): {
11
12
  new (...args: any[]): {
12
13
  playwright: {
13
14
  recorder: PlaywrightRecorder;
14
- helper: any;
15
+ explorer: Explorer;
15
16
  } | undefined;
16
17
  savedFiles: Set<string>;
17
18
  stateManager?: StateManager;
@@ -5,8 +5,8 @@ import { ConfigParser } from "../../config.js";
5
5
  import { renderAssertion, renderCall } from "../../playwright-recorder.js";
6
6
  import { tag } from "../../utils/logger.js";
7
7
  import { relativeToCwd } from "../../utils/next-steps.js";
8
- import { safeFilename } from "../../utils/strings.js";
9
8
  import { CODECEPT_TOOLS } from "../../utils/step-analyzer.js";
9
+ import { safeFilename } from "../../utils/strings.js";
10
10
  import { ASSERTION_TOOLS } from "../tools.js";
11
11
  import { escapeString, getExecutionLabel } from "./utils.js";
12
12
  const PLAYWRIGHT_EMITTED_TOOLS = [...CODECEPT_TOOLS, ...ASSERTION_TOOLS];
@@ -1,4 +1,5 @@
1
1
  import type { ExplorbotConfig } from '../../config.js';
2
+ import type Explorer from '../../explorer.js';
2
3
  import type { PlaywrightRecorder } from '../../playwright-recorder.js';
3
4
  import { type Constructor } from './mixin.js';
4
5
  export interface ScreencastMethods {
@@ -12,7 +13,7 @@ export declare function WithScreencast<T extends Constructor>(Base: T): {
12
13
  savedFiles: Set<string>;
13
14
  playwright: {
14
15
  recorder: PlaywrightRecorder;
15
- helper: any;
16
+ explorer: Explorer;
16
17
  } | undefined;
17
18
  screencastPage: any;
18
19
  screencastActive: boolean;
@@ -27,7 +27,7 @@ export function WithScreencast(Base) {
27
27
  return;
28
28
  if (!this.config?.ai?.agents?.historian?.screencast)
29
29
  return;
30
- if (!this.playwright?.helper)
30
+ if (!this.playwright?.explorer)
31
31
  return;
32
32
  this.onTestBefore = (test) => {
33
33
  void this.startScreencast(test);
@@ -46,7 +46,7 @@ export function WithScreencast(Base) {
46
46
  async startScreencast(test) {
47
47
  if (this.screencastActive)
48
48
  return;
49
- const page = this.playwright?.helper?.page;
49
+ const page = this.playwright?.explorer?.page;
50
50
  if (!page?.screencast?.start)
51
51
  return;
52
52
  const task = test?._explorbotTest;
@@ -1,5 +1,6 @@
1
1
  import type { ExplorbotConfig } from '../config.js';
2
2
  import type { ExperienceTracker } from '../experience-tracker.js';
3
+ import type Explorer from '../explorer.js';
3
4
  import type { PlaywrightRecorder } from '../playwright-recorder.js';
4
5
  import type { Reporter } from '../reporter.js';
5
6
  import type { StateManager } from '../state-manager.js';
@@ -15,7 +16,7 @@ declare const HistorianBase: {
15
16
  savedFiles: Set<string>;
16
17
  playwright: {
17
18
  recorder: PlaywrightRecorder;
18
- helper: any;
19
+ explorer: Explorer;
19
20
  };
20
21
  screencastPage: any;
21
22
  screencastActive: boolean;
@@ -36,7 +37,7 @@ declare const HistorianBase: {
36
37
  new (...args: any[]): {
37
38
  playwright: {
38
39
  recorder: PlaywrightRecorder;
39
- helper: any;
40
+ explorer: Explorer;
40
41
  };
41
42
  savedFiles: Set<string>;
42
43
  stateManager?: StateManager;
@@ -91,12 +92,12 @@ export declare class Historian extends HistorianBase {
91
92
  config: ExplorbotConfig | undefined;
92
93
  playwright: {
93
94
  recorder: PlaywrightRecorder;
94
- helper: any;
95
+ explorer: Explorer;
95
96
  } | undefined;
96
97
  savedFiles: Set<string>;
97
98
  constructor(provider: Provider, experienceTracker: ExperienceTracker, reporter?: Reporter, stateManager?: StateManager, config?: ExplorbotConfig, playwright?: {
98
99
  recorder: PlaywrightRecorder;
99
- helper: any;
100
+ explorer: Explorer;
100
101
  });
101
102
  isPlaywrightFramework(): boolean;
102
103
  getSavedFiles(): string[];
@@ -1,10 +1,12 @@
1
1
  import { ActionResult } from '../action-result.js';
2
2
  import type Action from '../action.js';
3
+ import type { ExplorbotConfig } from '../config.js';
3
4
  import type { ExperienceTracker } from '../experience-tracker.js';
4
5
  import Explorer from '../explorer.js';
5
6
  import type { KnowledgeTracker } from '../knowledge-tracker.js';
7
+ import { type StateManager } from '../state-manager.js';
6
8
  import { HooksRunner } from '../utils/hooks-runner.js';
7
- import type { Agent } from './agent.js';
9
+ import type { Agent, AgentDeps } from './agent.js';
8
10
  import type { Provider } from './provider.js';
9
11
  declare class Navigator implements Agent {
10
12
  emoji: string;
@@ -16,7 +18,9 @@ declare class Navigator implements Agent {
16
18
  systemPrompt: string;
17
19
  freeSailSystemPrompt: string;
18
20
  explorer: Explorer;
19
- constructor(explorer: Explorer, provider: Provider);
21
+ config: ExplorbotConfig;
22
+ stateManager: StateManager;
23
+ constructor(deps: AgentDeps);
20
24
  get verifyAttempts(): number;
21
25
  get verifyTimeout(): number;
22
26
  getBaseOrigin(): string | null;
@@ -59,21 +59,25 @@ class Navigator {
59
59
  </rules>
60
60
  `;
61
61
  explorer;
62
- constructor(explorer, provider) {
63
- this.provider = provider;
64
- this.explorer = explorer;
65
- this.knowledgeTracker = explorer.getKnowledgeTracker();
66
- this.experienceTracker = explorer.getStateManager().getExperienceTracker();
67
- this.hooksRunner = new HooksRunner(explorer, explorer.getConfig());
62
+ config;
63
+ stateManager;
64
+ constructor(deps) {
65
+ this.provider = deps.ai;
66
+ this.explorer = deps.explorer;
67
+ this.config = deps.config;
68
+ this.stateManager = deps.stateManager;
69
+ this.knowledgeTracker = deps.knowledgeTracker;
70
+ this.experienceTracker = deps.stateManager.getExperienceTracker();
71
+ this.hooksRunner = new HooksRunner(deps.explorer, deps.config);
68
72
  }
69
73
  get verifyAttempts() {
70
- return this.explorer.getConfig().ai?.agents?.navigator?.verifyAttempts ?? 3;
74
+ return this.config.ai?.agents?.navigator?.verifyAttempts ?? 3;
71
75
  }
72
76
  get verifyTimeout() {
73
- return this.explorer.getConfig().ai?.agents?.navigator?.verifyTimeout ?? 1500;
77
+ return this.config.ai?.agents?.navigator?.verifyTimeout ?? 1500;
74
78
  }
75
79
  getBaseOrigin() {
76
- const baseUrl = this.explorer.getConfig().playwright.url;
80
+ const baseUrl = this.config.playwright.url;
77
81
  try {
78
82
  return new URL(baseUrl).origin;
79
83
  }
@@ -117,11 +121,11 @@ class Navigator {
117
121
  return normalizeUrl(currentUrl) === normalizeUrl(expectedUrl);
118
122
  }
119
123
  async visit(url) {
120
- return this.explorer.runWithBrowserRecovery('navigator.visit', () => this.visitOnce(url));
124
+ return this.visitOnce(url);
121
125
  }
122
126
  async visitOnce(url) {
123
127
  try {
124
- const action = this.explorer.createAction();
128
+ const action = this.explorer.action();
125
129
  await action.execute(`I.amOnPage('${url}')`);
126
130
  const currentUrl = action.stateManager.getCurrentState()?.url || '';
127
131
  if (currentUrl === 'about:blank' || currentUrl === '') {
@@ -149,7 +153,7 @@ class Navigator {
149
153
  throw new Error(`Navigation to ${url} failed: ${action.lastError?.message}`);
150
154
  }
151
155
  }
152
- await this.explorer.capturePageWithScreenshot();
156
+ await this.explorer.capture({ screenshot: true });
153
157
  await this.hooksRunner.runAfterHook('navigator', url);
154
158
  }
155
159
  catch (error) {
@@ -165,7 +169,7 @@ class Navigator {
165
169
  async resolveState(message, actionResult, opts) {
166
170
  tag('info').log('AI Navigator resolving state at', actionResult.url);
167
171
  debugLog('Resolution message:', message);
168
- const action = opts?.action ?? this.explorer.createAction();
172
+ const action = opts?.action ?? this.explorer.action();
169
173
  const expectedUrl = opts?.expectedUrl;
170
174
  const knowledge = this.knowledgeTracker.renderRelevantKnowledge(actionResult);
171
175
  let experience = '';
@@ -310,7 +314,7 @@ class Navigator {
310
314
  }
311
315
  codeBlockIndex++;
312
316
  totalAttempts++;
313
- await this.explorer.switchToMainFrame();
317
+ await action.exitIframe();
314
318
  const prevActionResult = action.actionResult ?? actionResult;
315
319
  const prevHash = prevActionResult.getStateHash();
316
320
  debugLog(`Attempting resolution: ${codeBlock}`);
@@ -339,7 +343,7 @@ class Navigator {
339
343
  // URL did not transition to expectedUrl within timeout
340
344
  }
341
345
  }
342
- const freshState = await this.explorer.capturePageState();
346
+ const freshState = await this.explorer.capture();
343
347
  const currentUrl = /^https?:\/\//i.test(expectedUrl) ? freshState.fullUrl || freshState.url || '' : freshState.url || '';
344
348
  const urlMatches = this.isSameExpectedOrigin(expectedUrl, action.stateManager) && normalizeUrl(currentUrl) === normalizeUrl(expectedUrl);
345
349
  const stateChanged = freshState.getStateHash() !== actionResult.getStateHash();
@@ -434,7 +438,7 @@ class Navigator {
434
438
  buildExperienceTools() {
435
439
  if (!this.experienceTracker)
436
440
  return undefined;
437
- const stateManager = this.explorer.getStateManager();
441
+ const stateManager = this.stateManager;
438
442
  const getState = () => {
439
443
  const s = stateManager.getCurrentState();
440
444
  return s ? ActionResult.fromState(s) : null;
@@ -442,7 +446,7 @@ class Navigator {
442
446
  return { learnExperience: createLearnExperienceTool({ getExperienceTracker: () => this.experienceTracker, getState }) };
443
447
  }
444
448
  async freeSail(opts, actionResult) {
445
- const stateManager = this.explorer.getStateManager();
449
+ const stateManager = this.stateManager;
446
450
  const state = stateManager.getCurrentState();
447
451
  if (!state) {
448
452
  return null;
@@ -620,10 +624,10 @@ class Navigator {
620
624
  let codeBlocks = [];
621
625
  const successfulCodes = [];
622
626
  const assertionSteps = [];
623
- const action = this.explorer.createAction();
627
+ const action = this.explorer.action();
624
628
  let failures = 0;
625
- const page = this.explorer.playwrightHelper?.page;
626
- const originalTimeout = this.explorer.playwrightHelper?.options?.timeout ?? 3000;
629
+ const page = this.explorer.page;
630
+ const originalTimeout = this.config.playwright.timeout ?? 3000;
627
631
  page?.setDefaultTimeout(this.verifyTimeout);
628
632
  try {
629
633
  await loop(async ({ stop, iteration }) => {
@@ -649,7 +653,7 @@ class Navigator {
649
653
  stop();
650
654
  return;
651
655
  }
652
- await this.explorer.switchToMainFrame();
656
+ await action.exitIframe();
653
657
  const verified = await action.attempt(codeBlock, message);
654
658
  if (verified) {
655
659
  tag('success').log('Verification passed');
@@ -683,7 +687,7 @@ class Navigator {
683
687
  if (alreadyVerified)
684
688
  verified = true;
685
689
  actionResult.addVerification(message, verified);
686
- this.explorer.getStateManager().updateState(actionResult);
690
+ this.stateManager.updateState(actionResult);
687
691
  return { verified, successfulCodes, assertionSteps, totalAttempted };
688
692
  }
689
693
  checkAlreadyVerified(aiResponse, actionResult) {
@@ -1,7 +1,10 @@
1
1
  import { ActionResult } from '../action-result.js';
2
+ import type { RequestStore } from '../api/request-store.js';
2
3
  import type Explorer from '../explorer.js';
4
+ import type { PlaywrightRecorder } from '../playwright-recorder.js';
5
+ import type { StateManager } from '../state-manager.js';
3
6
  import { type Test } from '../test-plan.js';
4
- import type { Agent } from './agent.js';
7
+ import type { Agent, AgentDeps } from './agent.js';
5
8
  import type { Conversation } from './conversation.js';
6
9
  import type { Fisherman } from './fisherman.js';
7
10
  import type { Navigator } from './navigator.js';
@@ -14,8 +17,11 @@ export declare class Pilot implements Agent {
14
17
  conversation: Conversation | null;
15
18
  researcher: Researcher;
16
19
  explorer: Explorer;
20
+ stateManager: StateManager;
21
+ requestStore: RequestStore;
22
+ playwrightRecorder: PlaywrightRecorder;
17
23
  fisherman: Fisherman | null;
18
- constructor(provider: Provider, agentTools: any, researcher: Researcher, explorer: Explorer);
24
+ constructor(deps: AgentDeps, agentTools: any, researcher: Researcher);
19
25
  setFisherman(fisherman: Fisherman): void;
20
26
  get stepsToReview(): number;
21
27
  reset(): void;
@@ -27,17 +33,18 @@ export declare class Pilot implements Agent {
27
33
  reviewReset(task: Test, currentState: ActionResult, reason: string, testerConversation: Conversation): Promise<boolean>;
28
34
  reviewDecision(type: 'finish' | 'stop', task: Test, currentState: ActionResult, testerConversation: Conversation, navigator?: Navigator): Promise<boolean>;
29
35
  reviewResetDecision(task: Test, currentState: ActionResult, reason: string, testerConversation: Conversation): Promise<boolean>;
30
- buildSharedEvidenceRules(task: Test): string;
36
+ buildSharedEvidenceRules(): string;
37
+ buildTaskContext(task: Test): string;
31
38
  buildResetSystemPrompt(task: Test): string;
32
- buildVerdictSystemPrompt(type: string, task: Test): string;
39
+ buildVerdictSystemPrompt(task: Test): string;
33
40
  planTest(task: Test, currentState: ActionResult): Promise<string>;
34
41
  reviewNewPage(task: Test, currentState: ActionResult, testerConversation: Conversation): Promise<string>;
35
42
  analyzeProgress(task: Test, currentState: ActionResult, testerConversation: Conversation): Promise<string>;
36
43
  formatExpectations(task: Test): string;
37
- sendToPilot(userText: string, functionId: string, opts?: {
44
+ sendToPilot(userText: string, functionId: string, opts: {
38
45
  tools?: boolean;
39
46
  maxToolRoundtrips?: number;
40
- task?: Test;
47
+ task: Test;
41
48
  }): Promise<string>;
42
49
  getExperienceToc(): string;
43
50
  pickPlanningTools(): Record<string, unknown>;