explorbot 0.1.32-beta.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/README.md +35 -19
  2. package/bin/explorbot-cli.ts +33 -10
  3. package/boat/api-tester/src/config.ts +45 -3
  4. package/boat/doc-collector/src/ai/documentarian.ts +26 -28
  5. package/boat/doc-collector/src/ai/tools.ts +74 -16
  6. package/boat/doc-collector/src/cli.ts +1 -6
  7. package/boat/doc-collector/src/docbot.ts +30 -9
  8. package/boat/doc-collector/src/docs-renderer.ts +19 -24
  9. package/boat/doc-collector/src/screenshots.ts +34 -2
  10. package/boat/doc-collector/src/state-diagram.ts +281 -0
  11. package/dist/bin/explorbot-cli.js +29 -9
  12. package/dist/boat/api-tester/src/config.js +43 -4
  13. package/dist/boat/doc-collector/src/ai/documentarian.js +19 -7
  14. package/dist/boat/doc-collector/src/ai/tools.js +53 -16
  15. package/dist/boat/doc-collector/src/cli.js +1 -5
  16. package/dist/boat/doc-collector/src/docbot.js +27 -8
  17. package/dist/boat/doc-collector/src/docs-renderer.js +18 -3
  18. package/dist/boat/doc-collector/src/screenshots.js +30 -1
  19. package/dist/boat/doc-collector/src/state-diagram.js +200 -0
  20. package/dist/models.json +30 -0
  21. package/dist/package.json +23 -3
  22. package/dist/src/action-result.d.ts +8 -10
  23. package/dist/src/action-result.js +72 -164
  24. package/dist/src/action.d.ts +13 -12
  25. package/dist/src/action.js +37 -113
  26. package/dist/src/ai/agent.d.ts +17 -0
  27. package/dist/src/ai/captain/idle-mode.js +1 -1
  28. package/dist/src/ai/captain/test-mode.js +1 -1
  29. package/dist/src/ai/captain/web-mode.js +18 -29
  30. package/dist/src/ai/captain.d.ts +1 -3
  31. package/dist/src/ai/captain.js +17 -45
  32. package/dist/src/ai/conversation.d.ts +2 -5
  33. package/dist/src/ai/conversation.js +9 -28
  34. package/dist/src/ai/driller.d.ts +2 -17
  35. package/dist/src/ai/driller.js +26 -50
  36. package/dist/src/ai/experience-compactor.d.ts +2 -2
  37. package/dist/src/ai/experience-compactor.js +16 -33
  38. package/dist/src/ai/fisherman-tools.js +1 -1
  39. package/dist/src/ai/fisherman.d.ts +0 -1
  40. package/dist/src/ai/fisherman.js +0 -3
  41. package/dist/src/ai/historian/codeceptjs.d.ts +2 -0
  42. package/dist/src/ai/historian/codeceptjs.js +4 -3
  43. package/dist/src/ai/historian/playwright.d.ts +4 -1
  44. package/dist/src/ai/historian/playwright.js +4 -3
  45. package/dist/src/ai/historian/screencast.d.ts +2 -1
  46. package/dist/src/ai/historian/screencast.js +2 -2
  47. package/dist/src/ai/historian/utils.d.ts +0 -1
  48. package/dist/src/ai/historian/utils.js +0 -1
  49. package/dist/src/ai/historian.d.ts +10 -8
  50. package/dist/src/ai/historian.js +1 -5
  51. package/dist/src/ai/navigator.d.ts +8 -6
  52. package/dist/src/ai/navigator.js +34 -65
  53. package/dist/src/ai/pilot.d.ts +13 -8
  54. package/dist/src/ai/pilot.js +53 -59
  55. package/dist/src/ai/planner.d.ts +5 -4
  56. package/dist/src/ai/planner.js +66 -52
  57. package/dist/src/ai/provider.d.ts +4 -1
  58. package/dist/src/ai/provider.js +77 -137
  59. package/dist/src/ai/quartermaster.d.ts +22 -22
  60. package/dist/src/ai/quartermaster.js +8 -14
  61. package/dist/src/ai/rerunner.d.ts +3 -13
  62. package/dist/src/ai/rerunner.js +23 -44
  63. package/dist/src/ai/researcher/cache.js +11 -18
  64. package/dist/src/ai/researcher/coordinates.js +4 -3
  65. package/dist/src/ai/researcher/deep-analysis.d.ts +2 -0
  66. package/dist/src/ai/researcher/deep-analysis.js +8 -11
  67. package/dist/src/ai/researcher/locators.d.ts +9 -2
  68. package/dist/src/ai/researcher/locators.js +65 -8
  69. package/dist/src/ai/researcher/parser.js +1 -1
  70. package/dist/src/ai/researcher/research-result.d.ts +0 -1
  71. package/dist/src/ai/researcher/research-result.js +0 -19
  72. package/dist/src/ai/researcher/sections.d.ts +2 -0
  73. package/dist/src/ai/researcher/sections.js +7 -3
  74. package/dist/src/ai/researcher.js +40 -106
  75. package/dist/src/ai/rules.d.ts +1 -0
  76. package/dist/src/ai/rules.js +22 -15
  77. package/dist/src/ai/session-analyst.js +8 -5
  78. package/dist/src/ai/task-agent.d.ts +19 -6
  79. package/dist/src/ai/task-agent.js +37 -33
  80. package/dist/src/ai/tester.d.ts +6 -14
  81. package/dist/src/ai/tester.js +46 -111
  82. package/dist/src/ai/tools.d.ts +26 -14
  83. package/dist/src/ai/tools.js +138 -132
  84. package/dist/src/command-handler.js +1 -1
  85. package/dist/src/commands/compact-command.d.ts +1 -1
  86. package/dist/src/commands/compact-command.js +6 -6
  87. package/dist/src/commands/context-aria-command.js +1 -1
  88. package/dist/src/commands/context-command.js +8 -6
  89. package/dist/src/commands/context-data-command.js +2 -2
  90. package/dist/src/commands/context-experience-command.js +2 -2
  91. package/dist/src/commands/context-html-command.js +2 -2
  92. package/dist/src/commands/context-knowledge-command.js +2 -2
  93. package/dist/src/commands/drill-command.js +1 -1
  94. package/dist/src/commands/experience-command.js +1 -1
  95. package/dist/src/commands/explore-command.js +4 -3
  96. package/dist/src/commands/freesail-command.js +2 -2
  97. package/dist/src/commands/knows-command.js +1 -1
  98. package/dist/src/commands/learn-command.js +5 -3
  99. package/dist/src/commands/path-command.js +1 -1
  100. package/dist/src/commands/research-command.js +1 -1
  101. package/dist/src/commands/test-command.js +1 -1
  102. package/dist/src/components/AddKnowledge.d.ts +2 -0
  103. package/dist/src/components/AddKnowledge.js +3 -7
  104. package/dist/src/components/App.js +3 -3
  105. package/dist/src/config.d.ts +19 -0
  106. package/dist/src/config.js +156 -10
  107. package/dist/src/experience-tracker.d.ts +11 -6
  108. package/dist/src/experience-tracker.js +82 -155
  109. package/dist/src/explorbot.d.ts +21 -13
  110. package/dist/src/explorbot.js +84 -80
  111. package/dist/src/explorer.d.ts +66 -102
  112. package/dist/src/explorer.js +332 -623
  113. package/dist/src/knowledge-tracker.d.ts +2 -2
  114. package/dist/src/knowledge-tracker.js +38 -45
  115. package/dist/src/state-manager.d.ts +10 -30
  116. package/dist/src/state-manager.js +11 -129
  117. package/dist/src/utils/aria.d.ts +6 -1
  118. package/dist/src/utils/aria.js +4 -1
  119. package/dist/src/utils/cache.d.ts +15 -0
  120. package/dist/src/utils/cache.js +34 -0
  121. package/dist/src/utils/context-formatter.d.ts +6 -0
  122. package/dist/src/utils/context-formatter.js +15 -27
  123. package/dist/src/utils/hooks-runner.js +2 -4
  124. package/dist/src/utils/html.d.ts +5 -0
  125. package/dist/src/utils/html.js +71 -0
  126. package/dist/src/utils/markdown-files.d.ts +10 -0
  127. package/dist/src/utils/markdown-files.js +21 -0
  128. package/dist/src/utils/markdown-query.d.ts +6 -0
  129. package/dist/src/utils/markdown-query.js +14 -1
  130. package/dist/src/utils/page-readiness.d.ts +1 -0
  131. package/dist/src/utils/page-readiness.js +1 -1
  132. package/dist/src/utils/secrets.js +2 -2
  133. package/dist/src/utils/strings.d.ts +2 -0
  134. package/dist/src/utils/strings.js +10 -0
  135. package/dist/src/utils/test-files.js +1 -1
  136. package/dist/src/utils/web-annotate.d.ts +5 -0
  137. package/dist/src/utils/web-annotate.js +58 -0
  138. package/dist/src/utils/web-eidx.d.ts +2 -0
  139. package/dist/src/utils/web-eidx.js +20 -0
  140. package/dist/src/utils/web-element.d.ts +3 -1
  141. package/dist/src/utils/web-element.js +32 -0
  142. package/dist/src/utils/web-sandbox.d.ts +4 -0
  143. package/dist/src/utils/web-sandbox.js +37 -0
  144. package/models.json +30 -0
  145. package/package.json +23 -3
  146. package/src/action-result.ts +70 -173
  147. package/src/action.ts +38 -132
  148. package/src/ai/agent.ts +20 -0
  149. package/src/ai/captain/idle-mode.ts +1 -1
  150. package/src/ai/captain/test-mode.ts +1 -1
  151. package/src/ai/captain/web-mode.ts +18 -30
  152. package/src/ai/captain.ts +17 -42
  153. package/src/ai/conversation.ts +9 -32
  154. package/src/ai/driller.ts +53 -83
  155. package/src/ai/experience-compactor.ts +16 -35
  156. package/src/ai/fisherman-tools.ts +1 -1
  157. package/src/ai/fisherman.ts +0 -4
  158. package/src/ai/historian/codeceptjs.ts +5 -3
  159. package/src/ai/historian/playwright.ts +7 -4
  160. package/src/ai/historian/screencast.ts +4 -3
  161. package/src/ai/historian/utils.ts +0 -1
  162. package/src/ai/historian.ts +6 -8
  163. package/src/ai/navigator.ts +39 -70
  164. package/src/ai/pilot.ts +59 -61
  165. package/src/ai/planner.ts +76 -62
  166. package/src/ai/provider.ts +85 -146
  167. package/src/ai/quartermaster.ts +39 -45
  168. package/src/ai/rerunner.ts +27 -50
  169. package/src/ai/researcher/cache.ts +10 -16
  170. package/src/ai/researcher/coordinates.ts +4 -3
  171. package/src/ai/researcher/deep-analysis.ts +10 -11
  172. package/src/ai/researcher/locators.ts +66 -9
  173. package/src/ai/researcher/parser.ts +1 -1
  174. package/src/ai/researcher/research-result.ts +0 -18
  175. package/src/ai/researcher/sections.ts +9 -3
  176. package/src/ai/researcher.ts +42 -119
  177. package/src/ai/rules.ts +24 -13
  178. package/src/ai/session-analyst.ts +8 -5
  179. package/src/ai/task-agent.ts +45 -38
  180. package/src/ai/tester.ts +51 -129
  181. package/src/ai/tools.ts +145 -153
  182. package/src/command-handler.ts +1 -1
  183. package/src/commands/compact-command.ts +7 -8
  184. package/src/commands/context-aria-command.ts +1 -1
  185. package/src/commands/context-command.ts +8 -6
  186. package/src/commands/context-data-command.ts +2 -2
  187. package/src/commands/context-experience-command.ts +2 -2
  188. package/src/commands/context-html-command.ts +2 -2
  189. package/src/commands/context-knowledge-command.ts +2 -2
  190. package/src/commands/drill-command.ts +1 -1
  191. package/src/commands/experience-command.ts +1 -1
  192. package/src/commands/explore-command.ts +4 -3
  193. package/src/commands/freesail-command.ts +2 -2
  194. package/src/commands/knows-command.ts +1 -1
  195. package/src/commands/learn-command.ts +5 -3
  196. package/src/commands/path-command.ts +1 -1
  197. package/src/commands/research-command.ts +1 -1
  198. package/src/commands/test-command.ts +1 -1
  199. package/src/components/AddKnowledge.tsx +5 -7
  200. package/src/components/App.tsx +3 -3
  201. package/src/config.ts +185 -11
  202. package/src/experience-tracker.ts +89 -152
  203. package/src/explorbot.ts +88 -83
  204. package/src/explorer.ts +402 -693
  205. package/src/knowledge-tracker.ts +42 -49
  206. package/src/state-manager.ts +20 -155
  207. package/src/utils/aria.ts +11 -2
  208. package/src/utils/cache.ts +40 -0
  209. package/src/utils/context-formatter.ts +12 -15
  210. package/src/utils/hooks-runner.ts +2 -4
  211. package/src/utils/html.ts +79 -0
  212. package/src/utils/markdown-files.ts +30 -0
  213. package/src/utils/markdown-query.ts +15 -1
  214. package/src/utils/page-readiness.ts +1 -1
  215. package/src/utils/secrets.ts +2 -3
  216. package/src/utils/strings.ts +12 -0
  217. package/src/utils/test-files.ts +1 -1
  218. package/src/utils/web-annotate.ts +64 -0
  219. package/src/utils/web-eidx.ts +21 -0
  220. package/src/utils/web-element.ts +34 -0
  221. package/src/utils/web-sandbox.ts +43 -0
  222. package/dist/boat/api-tester/example/apibot.config.js +0 -30
@@ -1,3 +1,4 @@
1
+ import type { AgentDeps } from './ai/agent.js';
1
2
  import { Captain } from './ai/captain.js';
2
3
  import { Driller } from './ai/driller.js';
3
4
  import { ExperienceCompactor } from './ai/experience-compactor.js';
@@ -12,12 +13,15 @@ import { Rerunner } from './ai/rerunner.js';
12
13
  import { Researcher } from './ai/researcher.js';
13
14
  import { SessionAnalyst } from './ai/session-analyst.js';
14
15
  import { Tester } from './ai/tester.js';
16
+ import { RequestStore } from './api/request-store.js';
15
17
  import type { ExplorbotConfig } from './config.js';
16
18
  import { ConfigParser } from './config.js';
17
19
  import { ExperienceTracker } from './experience-tracker.js';
18
20
  import Explorer from './explorer.js';
19
21
  import { KnowledgeTracker } from './knowledge-tracker.js';
20
- import { WebPageState } from './state-manager.js';
22
+ import { PlaywrightRecorder } from './playwright-recorder.js';
23
+ import { Reporter } from './reporter.js';
24
+ import { StateManager, WebPageState } from './state-manager.js';
21
25
  import type { Suite } from './suite.js';
22
26
  import { Plan, type Test } from './test-plan.js';
23
27
  export interface ExplorBotOptions {
@@ -29,7 +33,7 @@ export interface ExplorBotOptions {
29
33
  show?: boolean;
30
34
  headless?: boolean;
31
35
  incognito?: boolean;
32
- session?: string;
36
+ session?: string | boolean;
33
37
  }
34
38
  export type UserResolveFunction = (error?: Error, showWelcome?: boolean) => Promise<string | null>;
35
39
  export declare class ExplorBot {
@@ -39,7 +43,6 @@ export declare class ExplorBot {
39
43
  config: ExplorbotConfig;
40
44
  options: ExplorBotOptions;
41
45
  userResolveFn: UserResolveFunction | null;
42
- needsInput: boolean;
43
46
  currentPlan?: Plan;
44
47
  planFeature?: string;
45
48
  lastPlanError: Error | null;
@@ -47,6 +50,12 @@ export declare class ExplorBot {
47
50
  agents: Record<string, any>;
48
51
  sessionPlans: Plan[];
49
52
  lastReportedTestCount: number;
53
+ _experienceTracker?: ExperienceTracker;
54
+ _knowledgeTracker?: KnowledgeTracker;
55
+ _stateManager?: StateManager;
56
+ _reporter?: Reporter;
57
+ _requestStore?: RequestStore;
58
+ _playwrightRecorder?: PlaywrightRecorder;
50
59
  constructor(options?: ExplorBotOptions);
51
60
  get isExploring(): boolean;
52
61
  setUserResolve(fn: UserResolveFunction): void;
@@ -55,21 +64,19 @@ export declare class ExplorBot {
55
64
  stop(): Promise<void>;
56
65
  visitInitialState(): Promise<void>;
57
66
  visit(url: string): Promise<void>;
58
- openFreshTab(): Promise<void>;
67
+ openTab(): Promise<void>;
59
68
  getCurrentState(): WebPageState | null;
60
69
  getExplorer(): Explorer;
61
- getKnowledgeTracker(): KnowledgeTracker;
62
- getExperienceTracker(): ExperienceTracker;
70
+ knowledgeTracker(): KnowledgeTracker;
71
+ experienceTracker(): ExperienceTracker;
72
+ stateManager(): StateManager;
73
+ reporter(): Reporter;
74
+ requestStore(): RequestStore;
75
+ playwrightRecorder(): PlaywrightRecorder;
63
76
  getConfig(): ExplorbotConfig;
64
77
  getOptions(): ExplorBotOptions;
65
- isReady(): boolean;
66
- getConfigParser(): ConfigParser;
67
78
  getProvider(): AIProvider;
68
- createAgent<T>(factory: (deps: {
69
- explorer: Explorer;
70
- ai: AIProvider;
71
- config: ExplorbotConfig;
72
- }) => T): T;
79
+ createAgent<T>(factory: (deps: AgentDeps) => T): T;
73
80
  agentResearcher(): Researcher;
74
81
  agentNavigator(): Navigator;
75
82
  agentPlanner(): Planner;
@@ -104,4 +111,5 @@ export declare class ExplorBot {
104
111
  setCurrentPlan(plan?: Plan): void;
105
112
  getSessionTests(): Test[];
106
113
  printSessionAnalysis(): Promise<void>;
114
+ isHistorianEnabled(): boolean;
107
115
  }
@@ -1,6 +1,5 @@
1
1
  import { existsSync, mkdirSync } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { ActionResult } from "./action-result.js";
4
3
  import { Captain } from "./ai/captain.js";
5
4
  import { Driller } from "./ai/driller.js";
6
5
  import { ExperienceCompactor } from "./ai/experience-compactor.js";
@@ -23,8 +22,12 @@ import { ConfigParser } from "./config.js";
23
22
  import { ExperienceTracker } from "./experience-tracker.js";
24
23
  import Explorer from "./explorer.js";
25
24
  import { KnowledgeTracker } from "./knowledge-tracker.js";
25
+ import { PlaywrightRecorder } from "./playwright-recorder.js";
26
+ import { Reporter } from "./reporter.js";
27
+ import { StateManager } from "./state-manager.js";
26
28
  import { Stats } from "./stats.js";
27
29
  import { Plan } from "./test-plan.js";
30
+ import { browserErrorMessage } from "./utils/browser-errors.js";
28
31
  import { setVerboseMode, tag } from "./utils/logger.js";
29
32
  import { relativeToCwd } from "./utils/next-steps.js";
30
33
  import { sanitizeFilename } from "./utils/strings.js";
@@ -36,7 +39,6 @@ export class ExplorBot {
36
39
  config;
37
40
  options;
38
41
  userResolveFn = null;
39
- needsInput = false;
40
42
  currentPlan;
41
43
  planFeature;
42
44
  lastPlanError = null;
@@ -44,6 +46,12 @@ export class ExplorBot {
44
46
  agents = {};
45
47
  sessionPlans = [];
46
48
  lastReportedTestCount = 0;
49
+ _experienceTracker;
50
+ _knowledgeTracker;
51
+ _stateManager;
52
+ _reporter;
53
+ _requestStore;
54
+ _playwrightRecorder;
47
55
  constructor(options = {}) {
48
56
  this.options = options;
49
57
  this.configParser = ConfigParser.getInstance();
@@ -53,27 +61,32 @@ export class ExplorBot {
53
61
  }
54
62
  }
55
63
  get isExploring() {
56
- return this.explorer?.isStarted;
64
+ return !!this.explorer;
57
65
  }
58
66
  setUserResolve(fn) {
59
67
  this.userResolveFn = fn;
60
68
  }
61
69
  async start() {
62
- if (this.explorer?.isStarted) {
70
+ if (this.explorer) {
63
71
  return;
64
72
  }
65
73
  try {
66
74
  await this.startProviderOnly();
67
- this.explorer = new Explorer(this.config, this.provider, this.options);
75
+ const explorerOptions = { ...this.options, session: typeof this.options.session === 'string' ? this.options.session : undefined };
76
+ this.explorer = new Explorer(this.config, explorerOptions, {
77
+ stateManager: this.stateManager(),
78
+ knowledgeTracker: this.knowledgeTracker(),
79
+ reporter: this.reporter(),
80
+ requestStore: this.requestStore(),
81
+ playwrightRecorder: this.playwrightRecorder(),
82
+ });
68
83
  await this.explorer.start();
69
84
  if (!this.options.incognito) {
70
85
  await this.agentExperienceCompactor().autocompact();
71
86
  }
72
- if (this.userResolveFn)
73
- this.explorer.setUserResolve(this.userResolveFn);
74
87
  }
75
88
  catch (error) {
76
- const message = error instanceof Error ? error.message : String(error);
89
+ const message = browserErrorMessage(error);
77
90
  console.error('\nFailed to start:', message);
78
91
  process.exit(1);
79
92
  }
@@ -82,6 +95,8 @@ export class ExplorBot {
82
95
  if (this.provider)
83
96
  return;
84
97
  this.config = await this.configParser.loadConfig(this.options);
98
+ if (this.options.session === true)
99
+ this.options.session = path.join(this.configParser.getOutputDir(), 'session.json');
85
100
  this.provider = new AIProvider(this.config.ai);
86
101
  await this.provider.validateConnection();
87
102
  }
@@ -96,26 +111,32 @@ export class ExplorBot {
96
111
  async visit(url) {
97
112
  return this.agentNavigator().visit(url);
98
113
  }
99
- async openFreshTab() {
100
- await this.explorer.openFreshTab();
114
+ async openTab() {
115
+ await this.explorer.openTab();
101
116
  }
102
117
  getCurrentState() {
103
- return this.explorer?.getStateManager().getCurrentState() ?? null;
118
+ return this._stateManager?.getCurrentState() ?? null;
104
119
  }
105
120
  getExplorer() {
106
121
  return this.explorer;
107
122
  }
108
- getKnowledgeTracker() {
109
- if (this.explorer) {
110
- return this.explorer.getKnowledgeTracker();
111
- }
112
- return new KnowledgeTracker();
123
+ knowledgeTracker() {
124
+ return (this._knowledgeTracker ||= new KnowledgeTracker());
113
125
  }
114
- getExperienceTracker() {
115
- if (this.explorer) {
116
- return this.explorer.getStateManager().getExperienceTracker();
117
- }
118
- return new ExperienceTracker();
126
+ experienceTracker() {
127
+ return (this._experienceTracker ||= new ExperienceTracker(this.knowledgeTracker()));
128
+ }
129
+ stateManager() {
130
+ return (this._stateManager ||= new StateManager(this.experienceTracker(), this.knowledgeTracker()));
131
+ }
132
+ reporter() {
133
+ return (this._reporter ||= new Reporter(this.config.reporter, this.stateManager()));
134
+ }
135
+ requestStore() {
136
+ return (this._requestStore ||= new RequestStore(this.configParser.getOutputDir()));
137
+ }
138
+ playwrightRecorder() {
139
+ return (this._playwrightRecorder ||= new PlaywrightRecorder());
119
140
  }
120
141
  getConfig() {
121
142
  return this.config;
@@ -123,12 +144,6 @@ export class ExplorBot {
123
144
  getOptions() {
124
145
  return this.options;
125
146
  }
126
- isReady() {
127
- return this.explorer?.isStarted;
128
- }
129
- getConfigParser() {
130
- return this.configParser;
131
- }
132
147
  getProvider() {
133
148
  return this.provider;
134
149
  }
@@ -137,24 +152,24 @@ export class ExplorBot {
137
152
  explorer: this.explorer,
138
153
  ai: this.provider,
139
154
  config: this.config,
155
+ stateManager: this.stateManager(),
156
+ knowledgeTracker: this.knowledgeTracker(),
157
+ requestStore: this.requestStore(),
158
+ playwrightRecorder: this.playwrightRecorder(),
140
159
  });
141
- const agentEmoji = agent.emoji || '';
142
160
  const agentName = agent.constructor.name.toLowerCase();
143
161
  tag('debug').log(`Created ${agentName} agent`);
144
- // Agent is stored by the calling method using a string key
145
162
  return agent;
146
163
  }
147
164
  agentResearcher() {
148
- return (this.agents.researcher ||= this.createAgent(({ ai, explorer }) => new Researcher(explorer, ai)));
165
+ return (this.agents.researcher ||= this.createAgent((deps) => new Researcher(deps)));
149
166
  }
150
167
  agentNavigator() {
151
- return (this.agents.navigator ||= this.createAgent(({ ai, explorer }) => {
152
- return new Navigator(explorer, ai, this.agentExperienceCompactor(), explorer.getStateManager().getExperienceTracker());
153
- }));
168
+ return (this.agents.navigator ||= this.createAgent((deps) => new Navigator(deps)));
154
169
  }
155
170
  agentPlanner() {
156
171
  if (!this.agents.planner) {
157
- this.agents.planner = this.createAgent(({ ai, explorer }) => new Planner(explorer, ai));
172
+ this.agents.planner = this.createAgent((deps) => new Planner(deps, this.agentResearcher()));
158
173
  const fisherman = this.agentFisherman();
159
174
  if (fisherman)
160
175
  this.agents.planner.setFisherman(fisherman);
@@ -162,37 +177,26 @@ export class ExplorBot {
162
177
  return this.agents.planner;
163
178
  }
164
179
  agentPilot() {
165
- return (this.agents.pilot ||= this.createAgent(({ ai, explorer }) => {
180
+ return (this.agents.pilot ||= this.createAgent((deps) => {
166
181
  const researcher = this.agentResearcher();
167
182
  const navigator = this.agentNavigator();
168
- const stateManager = explorer.getStateManager();
169
- const experienceTracker = stateManager.getExperienceTracker();
170
- const getState = () => {
171
- const state = stateManager.getCurrentState();
172
- return state ? ActionResult.fromState(state) : null;
173
- };
174
- const tools = createAgentTools({ explorer, researcher, navigator, experienceTracker, getState, supervisor: true });
175
- return new Pilot(ai, tools, researcher, explorer, experienceTracker);
183
+ const tools = createAgentTools({ ...deps, researcher, navigator, supervisor: true });
184
+ return new Pilot(deps, tools, researcher);
176
185
  }));
177
186
  }
178
187
  agentTester() {
179
188
  if (!this.agents.tester) {
180
- this.agents.tester = this.createAgent(({ ai, explorer }) => {
189
+ this.agents.tester = this.createAgent((deps) => {
181
190
  const researcher = this.agentResearcher();
182
191
  const navigator = this.agentNavigator();
183
- const stateManager = explorer.getStateManager();
184
- const experienceTracker = stateManager.getExperienceTracker();
185
- const getState = () => {
186
- const state = stateManager.getCurrentState();
187
- return state ? ActionResult.fromState(state) : null;
188
- };
189
- const tools = createAgentTools({ explorer, researcher, navigator, experienceTracker, getState });
190
- return new Tester(explorer, ai, researcher, navigator, tools);
192
+ const tools = createAgentTools({ ...deps, researcher, navigator });
193
+ return new Tester(deps, researcher, navigator, tools);
191
194
  });
192
195
  const qm = this.agentQuartermaster();
193
196
  if (qm)
194
197
  this.agents.tester.setQuartermaster(qm);
195
- this.agents.tester.setHistorian(this.agentHistorian());
198
+ if (this.isHistorianEnabled())
199
+ this.agents.tester.setHistorian(this.agentHistorian());
196
200
  this.agents.tester.setPilot(this.agentPilot());
197
201
  this.agents.tester.setCaptain(this.agentCaptain());
198
202
  const fisherman = this.agentFisherman();
@@ -204,15 +208,11 @@ export class ExplorBot {
204
208
  agentCaptain() {
205
209
  if (!this.agents.captain) {
206
210
  this.agents.captain = new Captain(this);
207
- const qm = this.agentQuartermaster();
208
- if (qm)
209
- this.agents.captain.setQuartermaster(qm);
210
- this.agents.captain.setHistorian(this.agentHistorian());
211
211
  }
212
212
  return this.agents.captain;
213
213
  }
214
214
  agentExperienceCompactor() {
215
- return (this.agents.experienceCompactor ||= new ExperienceCompactor(this.provider, this.getExperienceTracker()));
215
+ return (this.agents.experienceCompactor ||= new ExperienceCompactor(this.provider, this.experienceTracker()));
216
216
  }
217
217
  agentQuartermaster() {
218
218
  const config = this.config.ai?.agents?.quartermaster;
@@ -222,39 +222,35 @@ export class ExplorBot {
222
222
  this.agents.quartermaster = new Quartermaster(this.provider, {
223
223
  model: config?.model,
224
224
  });
225
- this.agents.quartermaster.start(this.explorer.playwrightHelper, this.explorer.getStateManager());
225
+ this.agents.quartermaster.start(this.explorer, this.stateManager());
226
226
  }
227
227
  return this.agents.quartermaster;
228
228
  }
229
229
  agentHistorian() {
230
- return (this.agents.historian ||= this.createAgent(({ ai, explorer, config }) => {
231
- const experienceTracker = explorer.getStateManager().getExperienceTracker();
232
- const reporter = explorer.getReporter();
233
- return new Historian(ai, experienceTracker, reporter, explorer.getStateManager(), config, {
234
- recorder: explorer.getPlaywrightRecorder(),
235
- helper: explorer.playwrightHelper,
230
+ return (this.agents.historian ||= this.createAgent(({ ai, explorer, config, stateManager, playwrightRecorder }) => {
231
+ return new Historian(ai, this.experienceTracker(), this.reporter(), stateManager, config, {
232
+ recorder: playwrightRecorder,
233
+ explorer,
236
234
  });
237
235
  }));
238
236
  }
239
237
  agentRerunner() {
240
238
  if (!this.agents.rerunner) {
241
- this.agents.rerunner = this.createAgent(({ ai, explorer }) => {
239
+ this.agents.rerunner = this.createAgent((deps) => {
242
240
  const researcher = this.agentResearcher();
243
241
  const navigator = this.agentNavigator();
244
- const tools = createAgentTools({ explorer, researcher, navigator });
245
- return new Rerunner(explorer, ai, tools);
242
+ const tools = createAgentTools({ ...deps, researcher, navigator, withExperience: false });
243
+ return new Rerunner(deps, tools);
246
244
  });
247
- const qm = this.agentQuartermaster();
248
- if (qm)
249
- this.agents.rerunner.setQuartermaster(qm);
250
- this.agents.rerunner.setHistorian(this.agentHistorian());
245
+ if (this.isHistorianEnabled())
246
+ this.agents.rerunner.setHistorian(this.agentHistorian());
251
247
  }
252
248
  return this.agents.rerunner;
253
249
  }
254
250
  agentDriller() {
255
- return (this.agents.driller ||= this.createAgent(({ ai, explorer }) => {
251
+ return (this.agents.driller ||= this.createAgent((deps) => {
256
252
  const navigator = this.agentNavigator();
257
- return new Driller(explorer, ai, navigator);
253
+ return new Driller(deps, navigator);
258
254
  }));
259
255
  }
260
256
  agentSessionAnalyst() {
@@ -268,7 +264,7 @@ export class ExplorBot {
268
264
  if (!this.agents.fisherman) {
269
265
  const apiConfig = this.config.api;
270
266
  const outputDir = this.configParser.getOutputDir();
271
- const requestStore = this.explorer.getRequestStore() || new RequestStore(outputDir);
267
+ const requestStore = this.requestStore();
272
268
  const baseEndpoint = apiConfig?.baseEndpoint || this.config.playwright.url;
273
269
  const configHeaders = apiConfig?.headers || {};
274
270
  const apiClient = new ApiClient(baseEndpoint);
@@ -283,7 +279,12 @@ export class ExplorBot {
283
279
  return null;
284
280
  }
285
281
  };
286
- const cookieProvider = () => this.explorer.extractCookies();
282
+ const cookieProvider = async () => {
283
+ const cookies = await this.explorer.withPage((page) => page.context().cookies()).catch(() => []);
284
+ if (!cookies.length)
285
+ return {};
286
+ return { Cookie: cookies.map((c) => `${c.name}=${c.value}`).join('; ') };
287
+ };
287
288
  this.agents.fisherman = this.createAgent(({ ai }) => {
288
289
  return new Fisherman(ai, apiClient, requestStore, specLoader, baseEndpoint, cookieProvider, configHeaders, hasApiConfig);
289
290
  });
@@ -309,7 +310,7 @@ export class ExplorBot {
309
310
  this.clearPlan();
310
311
  }
311
312
  if (!opts.extend && this.currentPlan?.url) {
312
- const currentUrl = this.explorer?.getStateManager().getCurrentState()?.url;
313
+ const currentUrl = this._stateManager?.getCurrentState()?.url;
313
314
  if (currentUrl && currentUrl !== this.currentPlan.url) {
314
315
  tag('info').log('Different page detected, clearing previous plan');
315
316
  this.clearPlan();
@@ -358,7 +359,7 @@ export class ExplorBot {
358
359
  return planPath;
359
360
  }
360
361
  generatePlanFilename(feature) {
361
- const state = this.explorer?.getStateManager().getCurrentState();
362
+ const state = this._stateManager?.getCurrentState();
362
363
  const urlPath = state?.url || '/';
363
364
  const urlPart = sanitizeFilename(urlPath) || 'root';
364
365
  const suffix = '.md';
@@ -432,7 +433,7 @@ export class ExplorBot {
432
433
  tag('multiline').log(markdown, { maxLines: 22 });
433
434
  const filePath = this.agentSessionAnalyst().writeReport(markdown);
434
435
  tag('info').log(`Session report saved: ${relativeToCwd(filePath)}`);
435
- const reporter = this.explorer?.getReporter();
436
+ const reporter = this._reporter;
436
437
  if (reporter?.isEnabled()) {
437
438
  let description = markdown;
438
439
  const modelsTable = Stats.modelsTable(this.provider.getConfiguredModels());
@@ -443,8 +444,11 @@ export class ExplorBot {
443
444
  this.lastReportedTestCount = tests.length;
444
445
  }
445
446
  catch (error) {
446
- const message = error instanceof Error ? error.message : String(error);
447
+ const message = browserErrorMessage(error);
447
448
  tag('warning').log(`Session analysis failed: ${message}`);
448
449
  }
449
450
  }
451
+ isHistorianEnabled() {
452
+ return this.config.ai?.agents?.historian?.enabled !== false;
453
+ }
450
454
  }
@@ -1,17 +1,14 @@
1
- import type { BrowserContextOptions } from 'playwright';
1
+ import type { BrowserContextOptions, Page } from 'playwright';
2
2
  import { ActionResult } from './action-result.js';
3
3
  import Action from './action.js';
4
- import { AIProvider } from './ai/provider.js';
5
- import { RequestStore } from './api/request-store.js';
4
+ import type { RequestStore } from './api/request-store.js';
6
5
  import { XhrCapture } from './api/xhr-capture.js';
7
6
  import type { ExplorbotConfig } from './config.js';
8
- import type { UserResolveFunction } from './explorbot.js';
9
- import { KnowledgeTracker } from './knowledge-tracker.js';
10
- import { PlaywrightRecorder } from './playwright-recorder.js';
11
- import { Reporter } from './reporter.js';
12
- import { StateManager } from './state-manager.js';
7
+ import type { KnowledgeTracker } from './knowledge-tracker.js';
8
+ import type { PlaywrightRecorder } from './playwright-recorder.js';
9
+ import type { Reporter } from './reporter.js';
10
+ import type { StateManager } from './state-manager.js';
13
11
  import { Test } from './test-plan.js';
14
- import { WebElement } from './utils/web-element.js';
15
12
  declare global {
16
13
  namespace NodeJS {
17
14
  interface Global {
@@ -24,117 +21,84 @@ declare namespace CodeceptJS {
24
21
  [key: string]: any;
25
22
  }
26
23
  }
27
- interface TabInfo {
28
- url: string;
29
- title: string;
30
- }
31
- interface BrowserExecutionErrorResult {
32
- action: 'continue' | 'stop';
33
- message: string;
34
- recovered?: boolean;
35
- }
36
24
  declare class Explorer {
37
- aiProvider: AIProvider;
38
- playwrightHelper: any;
39
- isStarted: boolean;
40
- isSharedBrowser: boolean;
41
- actor: CodeceptJS.I;
25
+ config: ExplorbotConfig;
26
+ options?: ExplorerOptions;
42
27
  stateManager: StateManager;
43
28
  knowledgeTracker: KnowledgeTracker;
44
- config: ExplorbotConfig;
45
- userResolveFn: UserResolveFunction | null;
46
- options?: {
47
- show?: boolean;
48
- headless?: boolean;
49
- incognito?: boolean;
50
- session?: string;
51
- };
52
29
  reporter: Reporter;
53
- otherTabs: TabInfo[];
54
- _activeTest: Test | null;
55
- xhrCapture: XhrCapture | null;
56
- requestStore: RequestStore | null;
30
+ requestStore: RequestStore;
57
31
  playwrightRecorder: PlaywrightRecorder;
32
+ playwrightHelper: any;
33
+ _actor: CodeceptJS.I;
34
+ started: boolean;
35
+ isSharedBrowser: boolean;
36
+ xhrCapture: XhrCapture | null;
37
+ _activeTest: Test | null;
58
38
  observedTestPages: Set<any>;
59
39
  testPageErrorHandler: ((error: Error) => void) | null;
60
40
  testConsoleHandler: ((message: any) => void) | null;
61
41
  testDialogHandler: ((dialog: any) => void) | null;
62
- constructor(config: ExplorbotConfig, aiProvider: AIProvider, options?: {
63
- show?: boolean;
64
- headless?: boolean;
65
- incognito?: boolean;
66
- session?: string;
67
- });
42
+ constructor(config: ExplorbotConfig, options: ExplorerOptions | undefined, deps: ExplorerDeps);
43
+ get actor(): CodeceptJS.I;
44
+ get page(): Page | null;
45
+ get activeTest(): Test | null;
46
+ start(): Promise<void>;
47
+ stop(): Promise<void>;
48
+ action(): Action;
49
+ visit(url: string, opts?: CaptureOpts): Promise<ActionResult>;
50
+ capture(opts?: CaptureOpts): Promise<ActionResult>;
51
+ withPage<T>(fn: (page: Page) => Promise<T>): Promise<T>;
52
+ recover(error?: unknown): Promise<Recovery>;
53
+ beginTest(test: Test): Promise<TestRun>;
54
+ openTab(): Promise<void>;
68
55
  initializeContainer(): void;
69
56
  convertToCodeceptConfig(config: ExplorbotConfig): any;
70
- getConfig(): ExplorbotConfig;
71
- getConfigPath(): string | null;
72
- getAIProvider(): AIProvider;
73
- getStateManager(): StateManager;
74
- getCurrentUrl(): string;
75
- getKnowledgeTracker(): KnowledgeTracker;
76
- getReporter(): Reporter;
77
- getRequestStore(): RequestStore | null;
78
- extractCookies(): Promise<Record<string, string>>;
79
- setupXhrCapture(reuseRequestStore?: boolean): void;
80
- start(): Promise<any>;
81
57
  connectOrLaunchBrowser(): Promise<void>;
82
58
  createBrowserContextOptions(): BrowserContextOptions;
83
- createAction(): Action;
84
- runWithBrowserRecovery<T>(label: string, operation: () => Promise<T>): Promise<T>;
85
- capturePageState(opts?: {
86
- includeScreenshot?: boolean;
87
- }): Promise<ActionResult>;
88
- capturePageWithScreenshot(): Promise<ActionResult>;
89
- executeAction(code: string): Promise<Action>;
90
- attemptAction(code: string, originalMessage?: string, experience?: boolean): Promise<boolean>;
91
- getPlaywrightRecorder(): PlaywrightRecorder;
92
- visit(url: string): Promise<Action>;
59
+ attachXhrCapture(): void;
60
+ runWithRecovery<T>(label: string, operation: () => Promise<T>): Promise<T>;
93
61
  visitOnce(url: string): Promise<Action>;
94
- annotateElements(): Promise<WebElement[]>;
95
- visuallyAnnotateElements(opts?: {
96
- containers?: Array<{
97
- css: string;
98
- label: string;
99
- }>;
100
- }): Promise<number>;
101
- getEidxInContainer(containerCss: string | null): Promise<string[]>;
102
- getEidxByLocator(locator: string, container?: string | null): Promise<string | null>;
103
- reload(): Promise<void>;
104
- resolveBrowserUrl(url?: string): string | null;
105
- isFatalBrowserError(error: unknown): boolean;
106
- recoverFromBrowserError(): Promise<boolean>;
62
+ recoverFromExecutionError(error: unknown): Promise<Recovery>;
63
+ recoverOrRestart(): Promise<boolean>;
64
+ recoverPage(): Promise<boolean>;
65
+ activateNewPage(context: any): Promise<void>;
66
+ stopCaptures(): Promise<void>;
67
+ closeBrowserContext(): Promise<void>;
107
68
  restartBrowser(): Promise<boolean>;
108
- switchToMainFrame(): Promise<void>;
69
+ resolveBrowserUrl(url?: string): string | null;
109
70
  waitForPageReadiness(): Promise<boolean>;
110
- isInsideIframe(): Promise<boolean>;
111
- getCurrentIframeInfo(): string | null;
112
- hasOtherTabs(): boolean;
113
- getOtherTabsInfo(): TabInfo[];
114
- clearOtherTabsInfo(): void;
115
- setUserResolve(userResolveFn: UserResolveFunction): void;
116
71
  listenToStateChanged(): void;
117
- stop(): Promise<void>;
118
- get activeTest(): Test | null;
119
- startTest(test: Test): Promise<boolean>;
120
- ensurePageAvailable(): Promise<boolean>;
121
- ensureActiveTestPageAvailable(): Promise<boolean>;
122
- handleExecutionError(error: unknown): Promise<BrowserExecutionErrorResult>;
123
- watchActiveTestPage(page?: any): void;
124
- stopTest(test: Test, meta?: Record<string, string>): Promise<void>;
125
- unwatchActiveTestPages(): void;
126
- hasPlaywrightLocator(locatorFn: (page: any) => any, opts?: {
127
- multiple?: boolean;
128
- contents?: boolean;
129
- success?: (locator: any) => Promise<void> | void;
130
- }): Promise<boolean>;
131
- playwrightLocatorCount(locatorFn: (page: any) => any): Promise<number>;
132
72
  bindFrameNavigated(page: any): void;
133
- openFreshTab(): Promise<void>;
134
73
  closeOtherTabs(): Promise<void>;
74
+ finishTest(test: Test, meta?: Record<string, string>): Promise<void>;
75
+ watchActiveTestPage(page?: any): void;
76
+ unwatchActiveTestPages(): void;
77
+ }
78
+ export interface ExplorerOptions {
79
+ show?: boolean;
80
+ headless?: boolean;
81
+ incognito?: boolean;
82
+ session?: string;
83
+ }
84
+ export interface ExplorerDeps {
85
+ stateManager: StateManager;
86
+ knowledgeTracker: KnowledgeTracker;
87
+ reporter: Reporter;
88
+ requestStore: RequestStore;
89
+ playwrightRecorder: PlaywrightRecorder;
90
+ }
91
+ export interface Recovery {
92
+ ok: boolean;
93
+ action: 'continue' | 'stop';
94
+ message: string;
95
+ recovered?: boolean;
96
+ }
97
+ export interface TestRun {
98
+ started: boolean;
99
+ stop(meta?: Record<string, string>): Promise<void>;
100
+ }
101
+ export interface CaptureOpts {
102
+ screenshot?: boolean;
135
103
  }
136
- export declare function annotatePageElements(page: any): Promise<{
137
- ariaSnapshot: string;
138
- elements: WebElement[];
139
- }>;
140
104
  export default Explorer;