explorbot 0.1.31 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/README.md +14 -14
  2. package/bin/explorbot-cli.ts +4 -3
  3. package/boat/doc-collector/src/ai/documentarian.ts +22 -27
  4. package/boat/doc-collector/src/ai/tools.ts +67 -10
  5. package/boat/doc-collector/src/docbot.ts +28 -7
  6. package/boat/doc-collector/src/docs-renderer.ts +19 -24
  7. package/boat/doc-collector/src/screenshots.ts +33 -1
  8. package/boat/doc-collector/src/state-diagram.ts +281 -0
  9. package/dist/bin/explorbot-cli.js +3 -3
  10. package/dist/boat/doc-collector/src/ai/documentarian.js +16 -6
  11. package/dist/boat/doc-collector/src/ai/tools.js +47 -10
  12. package/dist/boat/doc-collector/src/docbot.js +25 -6
  13. package/dist/boat/doc-collector/src/docs-renderer.js +18 -3
  14. package/dist/boat/doc-collector/src/screenshots.js +29 -0
  15. package/dist/boat/doc-collector/src/state-diagram.js +200 -0
  16. package/dist/package.json +8 -6
  17. package/dist/src/action-result.d.ts +131 -0
  18. package/dist/src/action-result.js +82 -159
  19. package/dist/src/action.d.ts +43 -0
  20. package/dist/src/action.js +18 -106
  21. package/dist/src/activity.d.ts +26 -0
  22. package/dist/src/ai/agent.d.ts +3 -0
  23. package/dist/src/ai/captain/file-tools.d.ts +28 -0
  24. package/dist/src/ai/captain/idle-mode.d.ts +11 -0
  25. package/dist/src/ai/captain/mixin.d.ts +10 -0
  26. package/dist/src/ai/captain/test-mode.d.ts +13 -0
  27. package/dist/src/ai/captain/web-mode.d.ts +13 -0
  28. package/dist/src/ai/captain/web-mode.js +0 -7
  29. package/dist/src/ai/captain.d.ts +141 -0
  30. package/dist/src/ai/captain.js +4 -19
  31. package/dist/src/ai/conversation.d.ts +28 -0
  32. package/dist/src/ai/conversation.js +9 -28
  33. package/dist/src/ai/driller.d.ts +157 -0
  34. package/dist/src/ai/driller.js +3 -12
  35. package/dist/src/ai/experience-compactor.d.ts +53 -0
  36. package/dist/src/ai/experience-compactor.js +16 -33
  37. package/dist/src/ai/fisherman-tools.d.ts +90 -0
  38. package/dist/src/ai/fisherman-tools.js +1 -1
  39. package/dist/src/ai/fisherman.d.ts +29 -0
  40. package/dist/src/ai/fisherman.js +0 -3
  41. package/dist/src/ai/historian/codeceptjs.d.ts +17 -0
  42. package/dist/src/ai/historian/codeceptjs.js +3 -2
  43. package/dist/src/ai/historian/experience.d.ts +40 -0
  44. package/dist/src/ai/historian/mixin.d.ts +2 -0
  45. package/dist/src/ai/historian/playwright.d.ts +22 -0
  46. package/dist/src/ai/historian/playwright.js +3 -2
  47. package/dist/src/ai/historian/screencast.d.ts +32 -0
  48. package/dist/src/ai/historian/utils.d.ts +3 -0
  49. package/dist/src/ai/historian/utils.js +0 -1
  50. package/dist/src/ai/historian.d.ts +109 -0
  51. package/dist/src/ai/historian.js +1 -5
  52. package/dist/src/ai/navigator.d.ts +54 -0
  53. package/dist/src/ai/navigator.js +11 -46
  54. package/dist/src/ai/pilot.d.ts +81 -0
  55. package/dist/src/ai/pilot.js +6 -17
  56. package/dist/src/ai/planner/session-dedup.d.ts +12 -0
  57. package/dist/src/ai/planner/styles.d.ts +6 -0
  58. package/dist/src/ai/planner/subpages.d.ts +35 -0
  59. package/dist/src/ai/planner.d.ts +79 -0
  60. package/dist/src/ai/planner.js +49 -37
  61. package/dist/src/ai/provider.d.ts +53 -0
  62. package/dist/src/ai/provider.js +78 -130
  63. package/dist/src/ai/quartermaster.d.ts +56 -0
  64. package/dist/src/ai/quartermaster.js +4 -10
  65. package/dist/src/ai/rerunner.d.ts +48 -0
  66. package/dist/src/ai/rerunner.js +14 -22
  67. package/dist/src/ai/researcher/cache.d.ts +6 -0
  68. package/dist/src/ai/researcher/cache.js +11 -18
  69. package/dist/src/ai/researcher/coordinates.d.ts +56 -0
  70. package/dist/src/ai/researcher/deep-analysis.d.ts +74 -0
  71. package/dist/src/ai/researcher/deep-analysis.js +3 -6
  72. package/dist/src/ai/researcher/fingerprint-worker.d.ts +1 -0
  73. package/dist/src/ai/researcher/focus.d.ts +8 -0
  74. package/dist/src/ai/researcher/locators.d.ts +36 -0
  75. package/dist/src/ai/researcher/mixin.d.ts +2 -0
  76. package/dist/src/ai/researcher/parser.d.ts +35 -0
  77. package/dist/src/ai/researcher/parser.js +1 -1
  78. package/dist/src/ai/researcher/research-result.d.ts +15 -0
  79. package/dist/src/ai/researcher/research-result.js +0 -19
  80. package/dist/src/ai/researcher/sections.d.ts +19 -0
  81. package/dist/src/ai/researcher/sections.js +5 -1
  82. package/dist/src/ai/researcher.js +12 -53
  83. package/dist/src/ai/rules.d.ts +16 -0
  84. package/dist/src/ai/rules.js +22 -15
  85. package/dist/src/ai/session-analyst.d.ts +11 -0
  86. package/dist/src/ai/session-analyst.js +8 -5
  87. package/dist/src/ai/task-agent.d.ts +28 -0
  88. package/dist/src/ai/task-agent.js +2 -27
  89. package/dist/src/ai/tester.d.ts +101 -0
  90. package/dist/src/ai/tester.js +10 -61
  91. package/dist/src/ai/tools.d.ts +52 -0
  92. package/dist/src/ai/tools.js +119 -101
  93. package/dist/src/api/api-client.d.ts +17 -0
  94. package/dist/src/api/request-result.d.ts +44 -0
  95. package/dist/src/api/request-store.d.ts +29 -0
  96. package/dist/src/api/spec-reader.d.ts +5 -0
  97. package/dist/src/api/xhr-capture.d.ts +10 -0
  98. package/dist/src/browser-server.d.ts +10 -0
  99. package/dist/src/command-handler.d.ts +68 -0
  100. package/dist/src/commands/add-rule-command.d.ts +11 -0
  101. package/dist/src/commands/base-command.d.ts +26 -0
  102. package/dist/src/commands/clean-command.d.ts +12 -0
  103. package/dist/src/commands/compact-command.d.ts +20 -0
  104. package/dist/src/commands/compact-command.js +6 -6
  105. package/dist/src/commands/context-aria-command.d.ts +6 -0
  106. package/dist/src/commands/context-command.d.ts +11 -0
  107. package/dist/src/commands/context-command.js +1 -1
  108. package/dist/src/commands/context-data-command.d.ts +6 -0
  109. package/dist/src/commands/context-experience-command.d.ts +6 -0
  110. package/dist/src/commands/context-html-command.d.ts +6 -0
  111. package/dist/src/commands/context-knowledge-command.d.ts +6 -0
  112. package/dist/src/commands/context-knowledge-command.js +1 -1
  113. package/dist/src/commands/debug-command.d.ts +6 -0
  114. package/dist/src/commands/drill-command.d.ts +10 -0
  115. package/dist/src/commands/exit-command.d.ts +7 -0
  116. package/dist/src/commands/experience-command.d.ts +15 -0
  117. package/dist/src/commands/experience-command.js +1 -1
  118. package/dist/src/commands/explore-command.d.ts +51 -0
  119. package/dist/src/commands/freesail-command.d.ts +12 -0
  120. package/dist/src/commands/help-command.d.ts +6 -0
  121. package/dist/src/commands/index.d.ts +4 -0
  122. package/dist/src/commands/init-command.d.ts +7 -0
  123. package/dist/src/commands/knows-command.d.ts +10 -0
  124. package/dist/src/commands/knows-command.js +1 -1
  125. package/dist/src/commands/learn-command.d.ts +7 -0
  126. package/dist/src/commands/learn-command.js +3 -1
  127. package/dist/src/commands/navigate-command.d.ts +7 -0
  128. package/dist/src/commands/path-command.d.ts +10 -0
  129. package/dist/src/commands/plan-clear-command.d.ts +7 -0
  130. package/dist/src/commands/plan-command.d.ts +14 -0
  131. package/dist/src/commands/plan-edit-command.d.ts +7 -0
  132. package/dist/src/commands/plan-load-command.d.ts +7 -0
  133. package/dist/src/commands/plan-reload-command.d.ts +7 -0
  134. package/dist/src/commands/plan-save-command.d.ts +7 -0
  135. package/dist/src/commands/plans-command.d.ts +21 -0
  136. package/dist/src/commands/rerun-command.d.ts +7 -0
  137. package/dist/src/commands/research-command.d.ts +11 -0
  138. package/dist/src/commands/runs-command.d.ts +7 -0
  139. package/dist/src/commands/start-command.d.ts +7 -0
  140. package/dist/src/commands/status-command.d.ts +6 -0
  141. package/dist/src/commands/test-command.d.ts +11 -0
  142. package/dist/src/components/ActivityPane.d.ts +6 -0
  143. package/dist/src/components/AddKnowledge.d.ts +10 -0
  144. package/dist/src/components/AddKnowledge.js +3 -7
  145. package/dist/src/components/AddRule.d.ts +9 -0
  146. package/dist/src/components/App.d.ts +9 -0
  147. package/dist/src/components/App.js +2 -2
  148. package/dist/src/components/Autocomplete.d.ts +3 -0
  149. package/dist/src/components/InputPane.d.ts +13 -0
  150. package/dist/src/components/InputReadline.d.ts +17 -0
  151. package/dist/src/components/LogPane.d.ts +6 -0
  152. package/dist/src/components/PlanEditor.d.ts +9 -0
  153. package/dist/src/components/PlanPane.d.ts +13 -0
  154. package/dist/src/components/SessionTimer.d.ts +6 -0
  155. package/dist/src/components/StateTransitionPane.d.ts +8 -0
  156. package/dist/src/components/StatusPane.d.ts +4 -0
  157. package/dist/src/components/TaskPane.d.ts +9 -0
  158. package/dist/src/components/Welcome.d.ts +3 -0
  159. package/dist/src/components/WelcomeChecklist.d.ts +9 -0
  160. package/dist/src/components/WelcomeCommands.d.ts +6 -0
  161. package/dist/src/components/autocomplete-store.d.ts +10 -0
  162. package/dist/src/components/parse-keypress.d.ts +12 -0
  163. package/dist/src/config.d.ts +253 -0
  164. package/dist/src/config.js +12 -0
  165. package/dist/src/execution-controller.d.ts +25 -0
  166. package/dist/src/experience-tracker.d.ts +112 -0
  167. package/dist/src/experience-tracker.js +83 -155
  168. package/dist/src/explorbot.d.ts +106 -0
  169. package/dist/src/explorbot.js +17 -51
  170. package/dist/src/explorer.d.ts +129 -0
  171. package/dist/src/explorer.js +31 -77
  172. package/dist/src/index.d.ts +6 -0
  173. package/dist/src/index.js +2 -55
  174. package/dist/src/knowledge-tracker.d.ts +33 -0
  175. package/dist/src/knowledge-tracker.js +47 -47
  176. package/dist/src/observability.d.ts +13 -0
  177. package/dist/src/observability.js +2 -1
  178. package/dist/src/playwright-recorder.d.ts +31 -0
  179. package/dist/src/reporter.d.ts +34 -0
  180. package/dist/src/state-manager.d.ts +160 -0
  181. package/dist/src/state-manager.js +10 -136
  182. package/dist/src/stats.d.ts +24 -0
  183. package/dist/src/suite.d.ts +24 -0
  184. package/dist/src/test-plan.d.ts +166 -0
  185. package/dist/src/utils/aria.d.ts +25 -0
  186. package/dist/src/utils/aria.js +4 -1
  187. package/dist/src/utils/browser-errors.d.ts +8 -0
  188. package/dist/src/utils/cache.d.ts +15 -0
  189. package/dist/src/utils/cache.js +34 -0
  190. package/dist/src/utils/cli-name.d.ts +1 -0
  191. package/dist/src/utils/code-extractor.d.ts +1 -0
  192. package/dist/src/utils/context-formatter.d.ts +31 -0
  193. package/dist/src/utils/context-formatter.js +15 -27
  194. package/dist/src/utils/error-page.d.ts +12 -0
  195. package/dist/src/utils/expandable.d.ts +4 -0
  196. package/dist/src/utils/hooks-runner.d.ts +15 -0
  197. package/dist/src/utils/html-diff.d.ts +19 -0
  198. package/dist/src/utils/html.d.ts +129 -0
  199. package/dist/src/utils/html.js +71 -0
  200. package/dist/src/utils/log-filters.d.ts +6 -0
  201. package/dist/src/utils/logger.d.ts +35 -0
  202. package/dist/src/utils/loop.d.ts +35 -0
  203. package/dist/src/utils/markdown-files.d.ts +10 -0
  204. package/dist/src/utils/markdown-files.js +21 -0
  205. package/dist/src/utils/markdown-parser.d.ts +17 -0
  206. package/dist/src/utils/markdown-query.d.ts +46 -0
  207. package/dist/src/utils/markdown-query.js +14 -1
  208. package/dist/src/utils/markdown-terminal.d.ts +1 -0
  209. package/dist/src/utils/next-steps.d.ts +11 -0
  210. package/dist/src/utils/page-readiness.d.ts +7 -0
  211. package/dist/src/utils/page-readiness.js +1 -1
  212. package/dist/src/utils/research-parser.d.ts +1 -0
  213. package/dist/src/utils/retry.d.ts +8 -0
  214. package/dist/src/utils/rules-loader.d.ts +12 -0
  215. package/dist/src/utils/secrets.d.ts +4 -0
  216. package/dist/src/utils/secrets.js +28 -0
  217. package/dist/src/utils/step-analyzer.d.ts +11 -0
  218. package/dist/src/utils/strings.d.ts +5 -0
  219. package/dist/src/utils/strings.js +10 -0
  220. package/dist/src/utils/test-files.d.ts +3 -0
  221. package/dist/src/utils/test-plan-markdown.d.ts +9 -0
  222. package/dist/src/utils/throttle.d.ts +2 -0
  223. package/dist/src/utils/unique-names.d.ts +2 -0
  224. package/dist/src/utils/url-matcher.d.ts +6 -0
  225. package/dist/src/utils/web-element.d.ts +45 -0
  226. package/dist/src/utils/web-element.js +3 -0
  227. package/dist/src/utils/web-sandbox.d.ts +4 -0
  228. package/dist/src/utils/web-sandbox.js +37 -0
  229. package/dist/src/utils/xpath.d.ts +17 -0
  230. package/package.json +8 -6
  231. package/src/action-result.ts +80 -168
  232. package/src/action.ts +16 -123
  233. package/src/ai/captain/web-mode.ts +0 -7
  234. package/src/ai/captain.ts +5 -17
  235. package/src/ai/conversation.ts +9 -32
  236. package/src/ai/driller.ts +3 -16
  237. package/src/ai/experience-compactor.ts +16 -35
  238. package/src/ai/fisherman-tools.ts +1 -1
  239. package/src/ai/fisherman.ts +0 -4
  240. package/src/ai/historian/codeceptjs.ts +4 -2
  241. package/src/ai/historian/playwright.ts +4 -2
  242. package/src/ai/historian/utils.ts +0 -1
  243. package/src/ai/historian.ts +4 -7
  244. package/src/ai/navigator.ts +13 -49
  245. package/src/ai/pilot.ts +6 -16
  246. package/src/ai/planner.ts +56 -44
  247. package/src/ai/provider.ts +86 -139
  248. package/src/ai/quartermaster.ts +34 -41
  249. package/src/ai/rerunner.ts +17 -21
  250. package/src/ai/researcher/cache.ts +10 -16
  251. package/src/ai/researcher/deep-analysis.ts +3 -6
  252. package/src/ai/researcher/parser.ts +1 -1
  253. package/src/ai/researcher/research-result.ts +0 -18
  254. package/src/ai/researcher/sections.ts +5 -1
  255. package/src/ai/researcher.ts +11 -59
  256. package/src/ai/rules.ts +24 -13
  257. package/src/ai/session-analyst.ts +8 -5
  258. package/src/ai/task-agent.ts +3 -30
  259. package/src/ai/tester.ts +12 -73
  260. package/src/ai/tools.ts +119 -106
  261. package/src/commands/compact-command.ts +7 -8
  262. package/src/commands/context-command.ts +1 -1
  263. package/src/commands/context-knowledge-command.ts +1 -1
  264. package/src/commands/experience-command.ts +1 -1
  265. package/src/commands/knows-command.ts +1 -1
  266. package/src/commands/learn-command.ts +3 -1
  267. package/src/components/AddKnowledge.tsx +5 -7
  268. package/src/components/App.tsx +2 -2
  269. package/src/config.ts +12 -0
  270. package/src/experience-tracker.ts +90 -152
  271. package/src/explorbot.ts +17 -51
  272. package/src/explorer.ts +34 -81
  273. package/src/index.ts +9 -0
  274. package/src/knowledge-tracker.ts +49 -50
  275. package/src/observability.ts +2 -1
  276. package/src/state-manager.ts +15 -164
  277. package/src/utils/aria.ts +11 -2
  278. package/src/utils/cache.ts +40 -0
  279. package/src/utils/context-formatter.ts +12 -15
  280. package/src/utils/html.ts +79 -0
  281. package/src/utils/markdown-files.ts +30 -0
  282. package/src/utils/markdown-query.ts +15 -1
  283. package/src/utils/page-readiness.ts +1 -1
  284. package/src/utils/secrets.ts +28 -0
  285. package/src/utils/strings.ts +12 -0
  286. package/src/utils/web-element.ts +4 -0
  287. package/src/utils/web-sandbox.ts +43 -0
  288. package/src/index.tsx +0 -62
@@ -1,19 +1,17 @@
1
1
  import fs from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { faker } from '@faker-js/faker';
4
3
  import { context, trace } from '@opentelemetry/api';
5
4
  import { container, recorder } from 'codeceptjs';
6
5
  import * as codeceptjs from 'codeceptjs';
7
- import { hopeThat, retryTo, tryTo, within } from 'codeceptjs/lib/effects';
8
- import step from 'codeceptjs/steps';
9
6
  import { ActionResult } from './action-result.js';
10
7
  import { clearActivity, setActivity } from "./activity.js";
11
8
  import { ConfigParser, outputPath } from './config.js';
12
9
  import { Observability } from "./observability.js";
13
- import { isFatalBrowserError, isNavigationTransitionError } from "./utils/browser-errors.js";
14
- import { htmlCombinedSnapshot, minifyHtml } from './utils/html.js';
10
+ import { browserErrorMessage, isFatalBrowserError, isNavigationTransitionError } from "./utils/browser-errors.js";
11
+ import { captureHtmlForSnapshot, htmlCombinedSnapshot, minifyHtml } from './utils/html.js';
15
12
  import { createDebug, setStepSpanParent, tag } from './utils/logger.js';
16
- import { waitForPageReadiness } from "./utils/page-readiness.js";
13
+ import { sleep, waitForPageReadiness } from "./utils/page-readiness.js";
14
+ import { codeceptJSSandbox, hasPlaywrightCommands, playwrightSandbox, sanitizeCodeBlock } from "./utils/web-sandbox.js";
17
15
  import { safeFilename } from "./utils/strings.js";
18
16
  const debugLog = createDebug('explorbot:action');
19
17
  const CAPTURE_NAVIGATION_TRANSITION_ATTEMPTS = 3;
@@ -24,7 +22,6 @@ class Action {
24
22
  config;
25
23
  // action info
26
24
  action = null;
27
- expectation = null;
28
25
  lastError = null;
29
26
  playwrightHelper;
30
27
  playwrightGroupId = null;
@@ -38,9 +35,6 @@ class Action {
38
35
  this.playwrightHelper = container.helpers('Playwright');
39
36
  this.recorder = recorder;
40
37
  }
41
- async caputrePageWithScreenshot() {
42
- return this.capturePageState({ includeScreenshot: true });
43
- }
44
38
  async saveScreenshot() {
45
39
  const currentState = this.stateManager.getCurrentState();
46
40
  if (currentState?.screenshotFile)
@@ -58,7 +52,7 @@ class Action {
58
52
  return undefined;
59
53
  }
60
54
  }
61
- async capturePageState({ includeScreenshot = false } = {}) {
55
+ async capturePageState({ includeScreenshot = false, codeBlock } = {}) {
62
56
  try {
63
57
  const currentState = this.stateManager.getCurrentState();
64
58
  const stateHash = currentState?.hash || 'screenshot';
@@ -145,11 +139,11 @@ class Action {
145
139
  ariaSnapshotFile,
146
140
  iframeURL: frame ? frame.url?.() || 'iframe' : undefined,
147
141
  });
148
- this.stateManager.updateState(result);
142
+ this.stateManager.updateState(result, codeBlock);
149
143
  return result;
150
144
  }
151
145
  catch (err) {
152
- const msg = err instanceof Error ? err.message : String(err);
146
+ const msg = browserErrorMessage(err);
153
147
  if (isFatalBrowserError(err))
154
148
  throw err;
155
149
  debugLog('capturePageState failed with non-fatal error:', msg);
@@ -272,21 +266,18 @@ class Action {
272
266
  }
273
267
  if (isPlaywright) {
274
268
  const page = this.playwrightHelper.page;
275
- const asyncFn = new Function('page', `return (async () => { ${sanitizedCode} })()`);
276
- await asyncFn(page);
269
+ await playwrightSandbox(page, sanitizedCode);
277
270
  await sleep(this.config.action?.delay || 500);
278
271
  }
279
272
  else {
280
- const codeFunction = new Function('I', 'tryTo', 'retryTo', 'within', 'hopeThat', 'step', 'faker', sanitizedCode);
281
- codeFunction(this.actor, tryTo, retryTo, within, hopeThat, step, faker);
273
+ codeceptJSSandbox(this.actor, sanitizedCode);
282
274
  await recorder.add(() => sleep(this.config.action?.delay || 500));
283
275
  await recorder.promise();
284
276
  }
285
- const pageState = await this.capturePageState();
286
277
  if (executedSteps.length > 0) {
287
278
  codeString = executedSteps.join('\n');
288
279
  }
289
- this.stateManager.updateState(pageState, codeString);
280
+ const pageState = await this.capturePageState({ codeBlock: codeString });
290
281
  this.actionResult = pageState;
291
282
  this.assertionSteps = assertionSteps;
292
283
  }
@@ -313,79 +304,15 @@ class Action {
313
304
  }
314
305
  return this;
315
306
  }
316
- async expect(codeOrFunction) {
317
- const codeString = typeof codeOrFunction === 'string' ? codeOrFunction : codeOrFunction.toString();
318
- this.expectation = codeString.toString();
319
- const expectationPreview = sanitizeCodeBlock(codeString)
320
- .split('\n')
321
- .map((line) => line.trim())
322
- .filter(Boolean)
323
- .slice(0, 2)
324
- .join(' ');
325
- tag('step').log(`Expecting: ${expectationPreview || 'assertion'}`);
326
- try {
327
- debugLog('Executing expectation:', codeString);
328
- let codeFunction;
329
- if (typeof codeOrFunction === 'function') {
330
- codeFunction = codeOrFunction;
331
- }
332
- else {
333
- const sanitizedCode = sanitizeCodeBlock(codeString);
334
- if (!sanitizedCode) {
335
- throw new Error('No valid I.* commands found in code block');
336
- }
337
- codeFunction = new Function('I', 'tryTo', 'retryTo', 'within', 'hopeThat', 'step', sanitizedCode);
338
- }
339
- codeFunction(this.actor, tryTo, retryTo, within, hopeThat, step);
340
- await recorder.promise();
341
- debugLog('Expectation executed successfully');
342
- // Get current state from state manager
343
- const currentState = this.stateManager.getCurrentState();
344
- if (currentState) {
345
- // Create ActionResult from current state for compatibility
346
- this.actionResult = new ActionResult({
347
- url: currentState.fullUrl || '',
348
- title: currentState.title,
349
- timestamp: currentState.timestamp,
350
- html: '', // Empty HTML for expectation state
351
- });
352
- }
353
- return this;
354
- }
355
- catch (err) {
356
- tag('error').log('Expectation failed:', errorToString(err));
357
- this.lastError = err;
358
- await recorder.reset();
359
- await recorder.start();
360
- debugLog('Expectation failed:', errorToString(err));
361
- }
362
- finally {
363
- clearActivity();
364
- }
365
- return this;
366
- }
367
- async waitForInteraction() {
368
- // start with basic approach
369
- await this.actor.wait(0.5);
370
- return this;
371
- }
372
- async attempt(codeBlock, originalMessage, experience = true) {
307
+ async attempt(codeBlock, originalMessage) {
373
308
  try {
374
309
  debugLog('Resolution attempt...');
375
310
  setActivity('🦾 Acting in browser...', 'action');
376
311
  if (!this.actionResult) {
377
312
  this.actionResult = ActionResult.fromState(this.stateManager.getCurrentState());
378
313
  }
379
- const prevActionResult = this.actionResult;
380
314
  this.lastError = null;
381
315
  await this.execute(codeBlock);
382
- if (!this.expectation && originalMessage) {
383
- this.expectation = originalMessage;
384
- }
385
- if (!this.expectation) {
386
- return true;
387
- }
388
- debugLog('Resolved Expectation:', this.expectation);
389
316
  return true;
390
317
  }
391
318
  catch (error) {
@@ -399,12 +326,6 @@ class Action {
399
326
  getActor() {
400
327
  return this.actor;
401
328
  }
402
- setActor(actor) {
403
- this.actor = actor;
404
- }
405
- getCurrentState() {
406
- return this.actionResult;
407
- }
408
329
  getActionResult() {
409
330
  return this.actionResult;
410
331
  }
@@ -423,6 +344,13 @@ function errorToString(error) {
423
344
  return error.message || error.toString();
424
345
  }
425
346
  async function captureHtml(page, frame, actor) {
347
+ for (const scope of [frame, page]) {
348
+ if (!scope?.evaluate)
349
+ continue;
350
+ const html = await scope.evaluate(captureHtmlForSnapshot).catch(() => null);
351
+ if (typeof html === 'string')
352
+ return html;
353
+ }
426
354
  if (frame?.content)
427
355
  return frame.content();
428
356
  if (page?.content)
@@ -438,22 +366,6 @@ async function captureTitle(page, actor) {
438
366
  return actor.grabTitle();
439
367
  return '';
440
368
  }
441
- function sanitizeCodeBlock(code) {
442
- return code
443
- .split('\n')
444
- .map((line) => line.trim())
445
- .filter((line) => line.startsWith('I.') || line.startsWith('page.') || line.startsWith('await '))
446
- .join('\n');
447
- }
448
- function hasPlaywrightCommands(code) {
449
- return code.split('\n').some((line) => {
450
- const trimmed = line.trim();
451
- return trimmed.startsWith('page.') || trimmed.startsWith('await page.');
452
- });
453
- }
454
- function sleep(ms) {
455
- return new Promise((resolve) => setTimeout(resolve, ms));
456
- }
457
369
  const ASSERTION_STEP_NAMES = new Set(['see', 'dontSee', 'seeElement', 'dontSeeElement', 'seeInField', 'dontSeeInField', 'seeInCurrentUrl', 'dontSeeInCurrentUrl']);
458
370
  const attachStepLogger = (target, assertionsTarget) => {
459
371
  const listener = (step, error) => {
@@ -0,0 +1,26 @@
1
+ export interface ActivityEntry {
2
+ message: string;
3
+ timestamp: Date;
4
+ type?: 'ai' | 'action' | 'navigation' | 'general';
5
+ }
6
+ type ActivityListener = (activity: ActivityEntry | null) => void;
7
+ declare class Activity {
8
+ static instance: Activity;
9
+ currentActivity: ActivityEntry | null;
10
+ listeners: ActivityListener[];
11
+ clearTimeoutId: NodeJS.Timeout | null;
12
+ constructor();
13
+ static getInstance(): Activity;
14
+ setActivity(message: string, type?: ActivityEntry['type']): void;
15
+ clearActivity(force?: boolean): void;
16
+ getCurrentActivity(): ActivityEntry | null;
17
+ addListener(listener: ActivityListener): void;
18
+ removeListener(listener: ActivityListener): void;
19
+ notifyListeners(): void;
20
+ }
21
+ export declare const setActivity: (message: string, type?: ActivityEntry["type"]) => void;
22
+ export declare const clearActivity: (force?: boolean) => void;
23
+ export declare const getCurrentActivity: () => ActivityEntry;
24
+ export declare const addActivityListener: (listener: ActivityListener) => void;
25
+ export declare const removeActivityListener: (listener: ActivityListener) => void;
26
+ export default Activity;
@@ -0,0 +1,3 @@
1
+ export interface Agent {
2
+ emoji?: string;
3
+ }
@@ -0,0 +1,28 @@
1
+ export declare const CAPTAIN_ARTIFACT_DIRS: readonly ["reports", "plans", "tests", "states"];
2
+ export declare const CAPTAIN_ALLOWED_READ_DIRS: readonly ["output", "knowledge", "experience"];
3
+ export declare const CAPTAIN_ARTIFACT_SCAN_LIMIT = 200;
4
+ export declare const CAPTAIN_ARTIFACT_LIST_LIMIT = 20;
5
+ export declare const CAPTAIN_READ_FILE_DEFAULT_LIMIT = 12000;
6
+ export declare const CAPTAIN_READ_FILE_MAX_LIMIT = 50000;
7
+ export declare const CAPTAIN_READ_FILE_MIN_LIMIT = 1000;
8
+ export declare function listRecentArtifacts(outputDir: string): Array<{
9
+ path: string;
10
+ size: number;
11
+ modifiedAt: string;
12
+ }>;
13
+ export declare function readCaptainFile(projectRoot: string | null, input: ReadCaptainFileInput, allowedDirs?: readonly string[]): ReadCaptainFileResult;
14
+ export interface ReadCaptainFileInput {
15
+ path: string;
16
+ startLine?: number;
17
+ endLine?: number;
18
+ maxChars?: number;
19
+ }
20
+ export type ReadCaptainFileResult = {
21
+ success: true;
22
+ path: string;
23
+ truncated: boolean;
24
+ content: string;
25
+ } | {
26
+ success: false;
27
+ message: string;
28
+ };
@@ -0,0 +1,11 @@
1
+ import { type Constructor, type ModeContext } from './mixin.js';
2
+ export declare function WithIdleMode<T extends Constructor>(Base: T): {
3
+ new (...args: any[]): {
4
+ idleModeTools(ctx: ModeContext): Promise<Record<string, any>>;
5
+ idleModePrompt(): string;
6
+ };
7
+ } & T;
8
+ export interface IdleModeMethods {
9
+ idleModeTools(ctx: ModeContext): Promise<Record<string, any>>;
10
+ idleModePrompt(): string;
11
+ }
@@ -0,0 +1,10 @@
1
+ import type { ExplorBot } from '../../explorbot.js';
2
+ import type { Task } from '../../test-plan.js';
3
+ export type Constructor<T = object> = new (...args: any[]) => T;
4
+ export declare const debugLog: (...args: any[]) => void;
5
+ export type CaptainMode = 'idle' | 'web' | 'test' | 'heal';
6
+ export interface ModeContext {
7
+ explorBot: ExplorBot;
8
+ task: Task;
9
+ }
10
+ export declare function resolveProjectRoot(): string | null;
@@ -0,0 +1,13 @@
1
+ import { type Constructor, type ModeContext } from './mixin.js';
2
+ export declare function WithTestMode<T extends Constructor>(Base: T): {
3
+ new (...args: any[]): {
4
+ testModeTools(ctx: ModeContext): Record<string, any>;
5
+ testModePrompt(): string;
6
+ testModeRules(): string;
7
+ };
8
+ } & T;
9
+ export interface TestModeMethods {
10
+ testModeTools(ctx: ModeContext): Record<string, any>;
11
+ testModePrompt(): string;
12
+ testModeRules(): string;
13
+ }
@@ -0,0 +1,13 @@
1
+ import { type Constructor, type ModeContext } from './mixin.js';
2
+ export declare function WithWebMode<T extends Constructor>(Base: T): {
3
+ new (...args: any[]): {
4
+ webModeTools(ctx: ModeContext): Record<string, any>;
5
+ webModePrompt(): string;
6
+ webModeRules(): string;
7
+ };
8
+ } & T;
9
+ export interface WebModeMethods {
10
+ webModeTools(ctx: ModeContext): Record<string, any>;
11
+ webModePrompt(): string;
12
+ webModeRules(): string;
13
+ }
@@ -1,7 +1,6 @@
1
1
  import { tool } from 'ai';
2
2
  import dedent from 'dedent';
3
3
  import { z } from 'zod';
4
- import { ActionResult } from "../../action-result.js";
5
4
  import { actionRule, locatorRule, sectionContextRule } from "../rules.js";
6
5
  import { createAgentTools, createCodeceptJSTools } from "../tools.js";
7
6
  import { debugLog } from "./mixin.js";
@@ -9,17 +8,11 @@ export function WithWebMode(Base) {
9
8
  return class extends Base {
10
9
  webModeTools(ctx) {
11
10
  const explorer = ctx.explorBot.getExplorer();
12
- const stateManager = explorer.getStateManager();
13
11
  const codeceptTools = createCodeceptJSTools(explorer, ctx.task);
14
12
  const agentTools = createAgentTools({
15
13
  explorer,
16
14
  researcher: ctx.explorBot.agentResearcher(),
17
15
  navigator: ctx.explorBot.agentNavigator(),
18
- experienceTracker: stateManager.getExperienceTracker(),
19
- getState: () => {
20
- const state = stateManager.getCurrentState();
21
- return state ? ActionResult.fromState(state) : null;
22
- },
23
16
  });
24
17
  const { see, context, visualClick, learnExperience } = agentTools;
25
18
  return {
@@ -0,0 +1,141 @@
1
+ import type { ExperienceTracker } from '../experience-tracker.js';
2
+ import type { ExplorBot } from '../explorbot.js';
3
+ import type { WebPageState } from '../state-manager.js';
4
+ import { Task, Test } from '../test-plan.js';
5
+ import { HooksRunner } from '../utils/hooks-runner.js';
6
+ import type { Agent } from './agent.js';
7
+ import { type CaptainMode, type ModeContext } from './captain/mixin.js';
8
+ import { type Conversation } from './conversation.js';
9
+ import type { Navigator } from './navigator.js';
10
+ import type { Provider } from './provider.js';
11
+ import { TaskAgent } from './task-agent.js';
12
+ declare const CaptainBase: {
13
+ new (...args: any[]): {
14
+ testModeTools(ctx: ModeContext): Record<string, any>;
15
+ testModePrompt(): string;
16
+ testModeRules(): string;
17
+ };
18
+ } & {
19
+ new (...args: any[]): {
20
+ webModeTools(ctx: ModeContext): Record<string, any>;
21
+ webModePrompt(): string;
22
+ webModeRules(): string;
23
+ };
24
+ } & {
25
+ new (...args: any[]): {
26
+ idleModeTools(ctx: ModeContext): Promise<Record<string, any>>;
27
+ idleModePrompt(): string;
28
+ };
29
+ } & (new (...args: any[]) => TaskAgent);
30
+ export declare class Captain extends CaptainBase implements Agent {
31
+ emoji: string;
32
+ explorBot: ExplorBot;
33
+ conversation: Conversation | null;
34
+ experienceTracker: ExperienceTracker;
35
+ hooksRunner: HooksRunner | null;
36
+ commandExecutor: ((cmd: string) => Promise<void>) | null;
37
+ commandDescriptions: {
38
+ name: string;
39
+ description: string;
40
+ options: string;
41
+ }[];
42
+ constructor(explorBot: ExplorBot);
43
+ setCommandExecutor(fn: (cmd: string) => Promise<void>, descriptions: {
44
+ name: string;
45
+ description: string;
46
+ options: string;
47
+ }[]): void;
48
+ getHooksRunner(): HooksRunner;
49
+ getNavigator(): Navigator;
50
+ getExperienceTracker(): ExperienceTracker;
51
+ getKnowledgeTracker(): import("../knowledge-tracker.js").KnowledgeTracker;
52
+ getProvider(): Provider;
53
+ trackToolExecutions(toolExecutions: any[]): void;
54
+ getMode(): CaptainMode;
55
+ systemPrompt(): string;
56
+ resetConversation(): Conversation;
57
+ ensureConversation(): Conversation;
58
+ getPageContext(): Promise<string>;
59
+ planSummary(): string;
60
+ reinjectContextIfNeeded(conversation: Conversation, currentState: WebPageState): Promise<void>;
61
+ coreTools(task: Task, onDone: (summary: string) => void): {
62
+ done: import("@ai-sdk/provider-utils").ExecutableTool<import("ai").Tool<{
63
+ summary: string;
64
+ details?: string;
65
+ }, {
66
+ success: boolean;
67
+ message: string;
68
+ summary?: undefined;
69
+ } | {
70
+ success: boolean;
71
+ summary: any;
72
+ message?: undefined;
73
+ }, import("@ai-sdk/provider-utils").Context>>;
74
+ runCommand: import("@ai-sdk/provider-utils").ExecutableTool<import("ai").Tool<{
75
+ command: any;
76
+ }, {
77
+ success: boolean;
78
+ message: string;
79
+ command?: undefined;
80
+ output?: undefined;
81
+ } | {
82
+ success: boolean;
83
+ command: any;
84
+ message: string;
85
+ output?: undefined;
86
+ } | {
87
+ success: boolean;
88
+ command: any;
89
+ output: string;
90
+ message?: undefined;
91
+ }, import("@ai-sdk/provider-utils").Context>>;
92
+ };
93
+ tools(task: Task, onDone: (summary: string) => void): Promise<{
94
+ done: import("@ai-sdk/provider-utils").ExecutableTool<import("ai").Tool<{
95
+ summary: string;
96
+ details?: string;
97
+ }, {
98
+ success: boolean;
99
+ message: string;
100
+ summary?: undefined;
101
+ } | {
102
+ success: boolean;
103
+ summary: any;
104
+ message?: undefined;
105
+ }, import("@ai-sdk/provider-utils").Context>>;
106
+ runCommand: import("@ai-sdk/provider-utils").ExecutableTool<import("ai").Tool<{
107
+ command: any;
108
+ }, {
109
+ success: boolean;
110
+ message: string;
111
+ command?: undefined;
112
+ output?: undefined;
113
+ } | {
114
+ success: boolean;
115
+ command: any;
116
+ message: string;
117
+ output?: undefined;
118
+ } | {
119
+ success: boolean;
120
+ command: any;
121
+ output: string;
122
+ message?: undefined;
123
+ }, import("@ai-sdk/provider-utils").Context>>;
124
+ }>;
125
+ processSupervisorInterrupt(userMessage: string, activeTest: Test): Promise<SupervisorAction>;
126
+ processExecutionError(error: Error, activeTest: Test): Promise<ExecutionRecoveryAction>;
127
+ canCompleteWithoutDetails(): boolean;
128
+ handle(input: string, options?: {
129
+ reset?: boolean;
130
+ }): Promise<string | null>;
131
+ }
132
+ export default Captain;
133
+ interface SupervisorAction {
134
+ action: 'inject' | 'stop' | 'pass' | 'skip';
135
+ message: string;
136
+ }
137
+ interface ExecutionRecoveryAction {
138
+ action: 'continue' | 'stop';
139
+ message: string;
140
+ recovered?: boolean;
141
+ }
@@ -2,8 +2,8 @@ import { tool } from 'ai';
2
2
  import dedent from 'dedent';
3
3
  import { z } from 'zod';
4
4
  import { ActionResult } from '../action-result.js';
5
- import { ExperienceTracker } from '../experience-tracker.js';
6
5
  import { Task } from "../test-plan.js";
6
+ import { formatHeadings } from "../utils/context-formatter.js";
7
7
  import { HooksRunner } from "../utils/hooks-runner.js";
8
8
  import { startLogCapture, stopLogCapture, tag } from '../utils/logger.js';
9
9
  import { loop } from '../utils/loop.js';
@@ -28,7 +28,7 @@ export class Captain extends CaptainBase {
28
28
  constructor(explorBot) {
29
29
  super();
30
30
  this.explorBot = explorBot;
31
- this.experienceTracker = new ExperienceTracker();
31
+ this.experienceTracker = explorBot.experienceTracker();
32
32
  }
33
33
  setCommandExecutor(fn, descriptions) {
34
34
  this.commandExecutor = fn;
@@ -48,7 +48,7 @@ export class Captain extends CaptainBase {
48
48
  return this.experienceTracker;
49
49
  }
50
50
  getKnowledgeTracker() {
51
- return this.explorBot.getExplorer().getKnowledgeTracker();
51
+ return this.explorBot.knowledgeTracker();
52
52
  }
53
53
  getProvider() {
54
54
  return this.explorBot.getProvider();
@@ -126,13 +126,6 @@ export class Captain extends CaptainBase {
126
126
  }
127
127
  return this.conversation;
128
128
  }
129
- getConversation() {
130
- return this.conversation;
131
- }
132
- cleanConversation() {
133
- this.conversation = null;
134
- tag('info').log('Conversation cleaned');
135
- }
136
129
  async getPageContext() {
137
130
  const state = this.explorBot.getExplorer().getStateManager().getCurrentState();
138
131
  if (!state) {
@@ -141,15 +134,7 @@ export class Captain extends CaptainBase {
141
134
  const actionResult = ActionResult.fromState(state);
142
135
  const knowledge = this.getKnowledge(actionResult);
143
136
  const experience = this.getExperience(actionResult);
144
- const headingLines = [];
145
- if (state.h1)
146
- headingLines.push(`H1: ${state.h1}`);
147
- if (state.h2)
148
- headingLines.push(`H2: ${state.h2}`);
149
- if (state.h3)
150
- headingLines.push(`H3: ${state.h3}`);
151
- if (state.h4)
152
- headingLines.push(`H4: ${state.h4}`);
137
+ const headingLines = formatHeadings(state);
153
138
  const headingsBlock = headingLines.join('\n');
154
139
  let pageSummary = '';
155
140
  const cachedResearch = Researcher.getCachedResearch(state);
@@ -0,0 +1,28 @@
1
+ import type { ModelMessage } from 'ai';
2
+ export interface ToolExecution {
3
+ toolName: string;
4
+ input: any;
5
+ output: any;
6
+ wasSuccessful: boolean;
7
+ }
8
+ export declare function toToolExecution(toolName: string, input: any, rawOutput: any): ToolExecution;
9
+ export declare function toolExecutionLabel(input: Record<string, any> | undefined): string;
10
+ export declare class Conversation {
11
+ messages: ModelMessage[];
12
+ model: any;
13
+ protectedPrefixCount: number;
14
+ autoTrimRules: Map<string, number>;
15
+ constructor(messages?: ModelMessage[], model?: any);
16
+ protectPrefix(count: number): void;
17
+ addUserText(text: string): void;
18
+ addAssistantText(text: string): void;
19
+ getLastMessage(): string;
20
+ clone(): Conversation;
21
+ cleanupTag(tagName: string, replacement: string, keepLast?: number): void;
22
+ autoTrimTag(tagName: string, maxLength: number): void;
23
+ compactToolResults(keepLastN: number): void;
24
+ markLastMessageCacheable(): void;
25
+ hasTag(tagName: string, lastN?: number): boolean;
26
+ applyAutoTrim(text: string): string;
27
+ getToolExecutions(): ToolExecution[];
28
+ }
@@ -1,20 +1,22 @@
1
+ export function toToolExecution(toolName, input, rawOutput) {
2
+ let output = rawOutput;
3
+ if (rawOutput?.type === 'json' && rawOutput?.value)
4
+ output = rawOutput.value;
5
+ return { toolName, input, output, wasSuccessful: output?.success !== false };
6
+ }
1
7
  export function toolExecutionLabel(input) {
2
8
  return input?.explanation || input?.assertion || input?.reason || input?.request || '';
3
9
  }
4
10
  const AUTO_COMPACT_ARIA_CHANGES_CUTOFF = 500;
5
11
  const AUTO_COMPACT_TARGETED_HTML_CUTOFF = 500;
6
12
  export class Conversation {
7
- id;
8
13
  messages;
9
14
  model;
10
- telemetryFunctionId;
11
15
  protectedPrefixCount = 0;
12
16
  autoTrimRules;
13
- constructor(messages = [], model, telemetryFunctionId) {
14
- this.id = this.generateId();
17
+ constructor(messages = [], model) {
15
18
  this.messages = messages;
16
19
  this.model = model || '';
17
- this.telemetryFunctionId = telemetryFunctionId;
18
20
  this.autoTrimRules = new Map();
19
21
  }
20
22
  protectPrefix(count) {
@@ -26,17 +28,6 @@ export class Conversation {
26
28
  content: this.applyAutoTrim(text),
27
29
  });
28
30
  }
29
- addUserImage(image) {
30
- if (!image || image.trim() === '') {
31
- console.warn('Warning: Attempting to add empty image to conversation');
32
- return;
33
- }
34
- const imageData = image.startsWith('data:') ? image : `data:image/png;base64,${image}`;
35
- this.messages.push({
36
- role: 'user',
37
- content: [{ type: 'file', mediaType: 'image/png', data: imageData }],
38
- });
39
- }
40
31
  addAssistantText(text) {
41
32
  // Skip empty or whitespace-only messages
42
33
  if (!text || text.trim() === '') {
@@ -61,7 +52,7 @@ export class Conversation {
61
52
  return '';
62
53
  }
63
54
  clone() {
64
- return new Conversation([...this.messages], this.model, this.telemetryFunctionId);
55
+ return new Conversation([...this.messages], this.model);
65
56
  }
66
57
  cleanupTag(tagName, replacement, keepLast = 0) {
67
58
  const escapedTag = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -190,9 +181,6 @@ export class Conversation {
190
181
  }
191
182
  return result;
192
183
  }
193
- generateId() {
194
- return `conv_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
195
- }
196
184
  getToolExecutions() {
197
185
  const toolCalls = new Map();
198
186
  for (const message of this.messages) {
@@ -215,14 +203,7 @@ export class Conversation {
215
203
  for (const part of message.content) {
216
204
  if (part.type !== 'tool-result')
217
205
  continue;
218
- const rawOutput = part.output;
219
- const output = rawOutput?.type === 'json' && rawOutput?.value ? rawOutput.value : rawOutput;
220
- executions.push({
221
- toolName: part.toolName,
222
- input: toolCalls.get(part.toolCallId) || {},
223
- output,
224
- wasSuccessful: output?.success !== false,
225
- });
206
+ executions.push(toToolExecution(part.toolName, toolCalls.get(part.toolCallId) || {}, part.output));
226
207
  }
227
208
  }
228
209
  return executions;