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,10 +1,11 @@
1
1
  import fs from 'node:fs';
2
- import { join } from 'node:path';
3
2
  import { ConfigParser, outputPath } from "./config.js";
4
- import { compactAriaSnapshot, diffAriaSnapshots } from "./utils/aria.js";
3
+ import { LARGE_ARIA_CHANGE_THRESHOLD, compactAriaSnapshot, diffAriaSnapshots } from "./utils/aria.js";
4
+ import { TTLCache } from "./utils/cache.js";
5
5
  import { htmlDiff } from "./utils/html-diff.js";
6
6
  import { extractHeadings, extractLinks, extractTargetedHtml, htmlCombinedSnapshot, htmlMinimalUISnapshot, htmlTextSnapshot, minifyHtml } from "./utils/html.js";
7
7
  import { createDebug } from "./utils/logger.js";
8
+ import { slugify } from "./utils/strings.js";
8
9
  import { extractStatePath, matchesUrl } from "./utils/url-matcher.js";
9
10
  const debugLog = createDebug('explorbot:state');
10
11
  export class ActionResult {
@@ -26,12 +27,12 @@ export class ActionResult {
26
27
  _screenshot = undefined;
27
28
  htmlFile = undefined;
28
29
  _html = undefined;
30
+ snapshotCache = new TTLCache();
29
31
  logFile = undefined;
30
- _browserLogs = undefined;
31
32
  ariaSnapshotFile = undefined;
32
33
  _ariaSnapshot = undefined;
33
34
  _lastExtractedHtml = undefined;
34
- notes;
35
+ notes = [];
35
36
  links = [];
36
37
  verifications;
37
38
  constructor(data) {
@@ -60,20 +61,17 @@ export class ActionResult {
60
61
  if (data.ariaSnapshotFile !== undefined) {
61
62
  this.ariaSnapshotFile = data.ariaSnapshotFile;
62
63
  }
63
- // Store HTML and browser logs in private properties if provided
64
+ // Store HTML in a private property if provided
64
65
  if (data.html !== undefined) {
65
66
  this._html = data.html;
66
67
  }
67
- if (data.browserLogs !== undefined) {
68
- this._browserLogs = data.browserLogs;
69
- }
70
68
  if (data.screenshot !== undefined) {
71
69
  this._screenshot = data.screenshot;
72
70
  }
73
71
  if (data.ariaSnapshot !== undefined) {
74
72
  this._ariaSnapshot = data.ariaSnapshot;
75
73
  }
76
- if (!this.fullUrl && this.url && this.url !== '') {
74
+ if (!this.fullUrl && this.url) {
77
75
  this.fullUrl = this.url;
78
76
  }
79
77
  this.extractHeadings(this.html);
@@ -83,7 +81,7 @@ export class ActionResult {
83
81
  else if (this._html) {
84
82
  this.links = extractLinks(this._html);
85
83
  }
86
- if (this.url && this.url !== '') {
84
+ if (this.url) {
87
85
  this.url = extractStatePath(this.url);
88
86
  }
89
87
  }
@@ -100,6 +98,7 @@ export class ActionResult {
100
98
  }
101
99
  set html(value) {
102
100
  this._html = value;
101
+ this.snapshotCache.clear();
103
102
  }
104
103
  get screenshot() {
105
104
  if (this._screenshot) {
@@ -110,15 +109,6 @@ export class ActionResult {
110
109
  }
111
110
  return undefined;
112
111
  }
113
- get browserLogsContent() {
114
- if (this._browserLogs !== undefined) {
115
- return this._browserLogs;
116
- }
117
- if (this.logFile) {
118
- return ActionResult.loadBrowserLogsFromFile(this.logFile);
119
- }
120
- return [];
121
- }
122
112
  get ariaSnapshot() {
123
113
  if (this._ariaSnapshot !== undefined) {
124
114
  return this._ariaSnapshot;
@@ -166,13 +156,13 @@ export class ActionResult {
166
156
  return null;
167
157
  }
168
158
  isSameUrl(state) {
169
- if (!this.url || this.url === '') {
159
+ if (!this.url) {
170
160
  return false;
171
161
  }
172
162
  return extractStatePath(state.url) === extractStatePath(this.url);
173
163
  }
174
164
  isMatchedBy(state) {
175
- if (!this.url || this.url === '') {
165
+ if (!this.url) {
176
166
  return false;
177
167
  }
178
168
  const isRelevant = matchesUrl(extractStatePath(state.url), extractStatePath(this.url));
@@ -206,19 +196,24 @@ export class ActionResult {
206
196
  }
207
197
  async simplifiedHtml(htmlConfig) {
208
198
  const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
209
- return await minifyHtml(htmlMinimalUISnapshot(this.html ?? '', normalizedConfig?.minimal));
199
+ const cacheKey = `simplified:${JSON.stringify(normalizedConfig?.minimal ?? null)}`;
200
+ return this.snapshotCache.fetch(cacheKey, () => minifyHtml(htmlMinimalUISnapshot(this.html ?? '', normalizedConfig?.minimal)));
210
201
  }
211
202
  async combinedHtml(htmlConfig) {
212
203
  const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
213
- const combinedHtml = await minifyHtml(htmlCombinedSnapshot(this.html ?? '', normalizedConfig?.combined, { keepPositions: htmlConfig?.keepPositions }));
214
- debugLog(`----${this.url}----`);
215
- debugLog(`Combined HTML: \n${combinedHtml}`);
216
- debugLog('----');
217
- return combinedHtml;
204
+ const cacheKey = `combined:${!!htmlConfig?.keepPositions}:${JSON.stringify(normalizedConfig?.combined ?? null)}`;
205
+ return this.snapshotCache.fetch(cacheKey, async () => {
206
+ const combinedHtml = await minifyHtml(htmlCombinedSnapshot(this.html ?? '', normalizedConfig?.combined, { keepPositions: htmlConfig?.keepPositions }));
207
+ debugLog(`----${this.url}----`);
208
+ debugLog(`Combined HTML: \n${combinedHtml}`);
209
+ debugLog('----');
210
+ return combinedHtml;
211
+ });
218
212
  }
219
213
  async textHtml(htmlConfig) {
220
214
  const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
221
- return await minifyHtml(htmlTextSnapshot(this.html ?? '', normalizedConfig?.text));
215
+ const cacheKey = `text:${JSON.stringify(normalizedConfig?.text ?? null)}`;
216
+ return this.snapshotCache.fetch(cacheKey, () => minifyHtml(htmlTextSnapshot(this.html ?? '', normalizedConfig?.text)));
222
217
  }
223
218
  getInteractiveARIA() {
224
219
  return compactAriaSnapshot(this.ariaSnapshot, false);
@@ -270,26 +265,27 @@ export class ActionResult {
270
265
  }
271
266
  return new ActionResult(actionResultData);
272
267
  }
273
- static loadHtmlFromFile(htmlFile) {
268
+ static loadStateFile(file) {
274
269
  try {
275
- const filePath = outputPath('states', htmlFile);
276
- if (fs.existsSync(filePath)) {
277
- return fs.readFileSync(filePath, 'utf8');
278
- }
279
- return null;
270
+ const filePath = outputPath('states', file);
271
+ if (!fs.existsSync(filePath))
272
+ return null;
273
+ return fs.readFileSync(filePath, 'utf8');
280
274
  }
281
275
  catch (error) {
282
- console.error('Failed to load HTML from file:', error);
276
+ console.error('Failed to load state file:', error);
283
277
  return null;
284
278
  }
285
279
  }
280
+ static loadHtmlFromFile(htmlFile) {
281
+ return ActionResult.loadStateFile(htmlFile);
282
+ }
286
283
  static loadScreenshotFromFile(screenshotFile) {
287
284
  try {
288
285
  const filePath = outputPath('states', screenshotFile);
289
- if (fs.existsSync(filePath)) {
290
- return fs.readFileSync(filePath);
291
- }
292
- return undefined;
286
+ if (!fs.existsSync(filePath))
287
+ return undefined;
288
+ return fs.readFileSync(filePath);
293
289
  }
294
290
  catch (error) {
295
291
  console.error('Failed to load screenshot from file:', error);
@@ -297,55 +293,38 @@ export class ActionResult {
297
293
  }
298
294
  }
299
295
  static loadBrowserLogsFromFile(logFile) {
300
- try {
301
- const filePath = outputPath('states', logFile);
302
- if (fs.existsSync(filePath)) {
303
- const logContent = fs.readFileSync(filePath, 'utf8');
304
- return logContent
305
- .split('\n')
306
- .filter((line) => line.trim())
307
- .map((line) => {
308
- const match = line.match(/\[([^\]]+)\] (\w+): (.+)/);
309
- if (match) {
310
- return {
311
- timestamp: match[1],
312
- type: match[2].toLowerCase(),
313
- text: match[3],
314
- level: match[2].toLowerCase(),
315
- message: match[3],
316
- };
317
- }
318
- return { text: line, type: 'log', level: 'log', message: line };
319
- });
320
- }
321
- return [];
322
- }
323
- catch (error) {
324
- console.error('Failed to load browser logs from file:', error);
296
+ const logContent = ActionResult.loadStateFile(logFile);
297
+ if (!logContent)
325
298
  return [];
326
- }
299
+ return logContent
300
+ .split('\n')
301
+ .filter((line) => line.trim())
302
+ .map((line) => {
303
+ const match = line.match(/\[([^\]]+)\] (\w+): (.+)/);
304
+ if (match) {
305
+ return {
306
+ timestamp: match[1],
307
+ type: match[2].toLowerCase(),
308
+ text: match[3],
309
+ level: match[2].toLowerCase(),
310
+ message: match[3],
311
+ };
312
+ }
313
+ return { text: line, type: 'log', level: 'log', message: line };
314
+ });
327
315
  }
328
316
  static loadAriaSnapshotFromFile(ariaSnapshotFile) {
329
- try {
330
- const filePath = outputPath('states', ariaSnapshotFile);
331
- if (!fs.existsSync(filePath)) {
332
- return null;
333
- }
334
- const content = fs.readFileSync(filePath, 'utf8');
335
- const trimmed = content.trim();
336
- if (!trimmed) {
337
- return null;
338
- }
339
- return trimmed.endsWith('\n') ? trimmed : `${trimmed}\n`;
340
- }
341
- catch (error) {
342
- console.error('Failed to load aria snapshot from file:', error);
317
+ const content = ActionResult.loadStateFile(ariaSnapshotFile);
318
+ if (!content)
343
319
  return null;
344
- }
320
+ const trimmed = content.trim();
321
+ if (!trimmed)
322
+ return null;
323
+ return trimmed.endsWith('\n') ? trimmed : `${trimmed}\n`;
345
324
  }
346
325
  toAiContext() {
347
326
  const parts = [];
348
- if (this.url && this.url !== '') {
327
+ if (this.url) {
349
328
  parts.push(`<url>${this.url}</url>`);
350
329
  }
351
330
  if (this.title) {
@@ -380,7 +359,7 @@ export class ActionResult {
380
359
  return parts.join('\n');
381
360
  }
382
361
  get relativeUrl() {
383
- if (!this.url || this.url === '')
362
+ if (!this.url)
384
363
  return null;
385
364
  try {
386
365
  const urlObj = new URL(this.url);
@@ -401,20 +380,11 @@ export class ActionResult {
401
380
  const parts = [];
402
381
  parts.push(this.relativeUrl || this.url || '/');
403
382
  this.extractHeadings(this.html);
404
- const headings = ['h1', 'h2'];
405
- for (const heading of headings) {
406
- const value = this[heading];
407
- if (value) {
408
- parts.push(`${heading}_${value}`);
409
- }
410
- }
411
- let stateString = parts
412
- .map((part) => part.substring(0, 100))
413
- .join('_')
414
- .replace(/[^a-zA-Z0-9_]/g, '_')
415
- .replace(/_+/g, '_')
416
- .replace(/^_|_$/g, '')
417
- .toLowerCase();
383
+ if (this.h1)
384
+ parts.push(`h1_${this.h1}`);
385
+ if (this.h2)
386
+ parts.push(`h2_${this.h2}`);
387
+ let stateString = slugify(parts.map((part) => part.substring(0, 100)).join('_'));
418
388
  if (stateString.length > 200) {
419
389
  stateString = stateString.substring(0, 200);
420
390
  if (stateString.endsWith('_')) {
@@ -423,55 +393,8 @@ export class ActionResult {
423
393
  }
424
394
  return stateString;
425
395
  }
426
- saveBrowserLogs() {
427
- const logs = this.browserLogsContent;
428
- if (!logs || logs.length === 0)
429
- return;
430
- try {
431
- const outputDir = 'output';
432
- const stateHash = this.getStateHash();
433
- const filename = `${stateHash}.log`;
434
- const filePath = join(outputDir, filename);
435
- // Ensure output directory exists
436
- if (!fs.existsSync(outputDir)) {
437
- fs.mkdirSync(outputDir, { recursive: true });
438
- }
439
- // Format logs for saving
440
- const formattedLogs = logs.map((log) => {
441
- const timestamp = new Date().toISOString();
442
- const level = (log.type || log.level || 'LOG').toUpperCase();
443
- const message = log.text || log.message || String(log);
444
- return `[${timestamp}] ${level}: ${message}`;
445
- });
446
- // Save log content to file
447
- const logContent = `${formattedLogs.join('\n')}\n`;
448
- fs.writeFileSync(filePath, logContent, 'utf8');
449
- }
450
- catch (error) {
451
- // Silently fail to avoid breaking the main flow
452
- console.error('Failed to save browser logs:', error);
453
- }
454
- }
455
- saveHtmlOutput() {
456
- try {
457
- const outputDir = 'output';
458
- const stateHash = this.getStateHash();
459
- const filename = `${stateHash}.html`;
460
- const filePath = join(outputDir, filename);
461
- // Ensure output directory exists
462
- if (!fs.existsSync(outputDir)) {
463
- fs.mkdirSync(outputDir, { recursive: true });
464
- }
465
- // Save HTML content to file
466
- fs.writeFileSync(filePath, this.html, 'utf8');
467
- }
468
- catch (error) {
469
- // Silently fail to avoid breaking the main flow
470
- console.error('Failed to save HTML output:', error);
471
- }
472
- }
473
396
  async diff(previousState) {
474
- return new Diff(this, previousState);
397
+ return Diff.create(this, previousState);
475
398
  }
476
399
  async toToolResult(previousState, locator) {
477
400
  const result = {
@@ -497,7 +420,6 @@ export class ActionResult {
497
420
  return result;
498
421
  }
499
422
  const diff = await this.diff(previousState);
500
- await diff.calculate();
501
423
  const pageDiff = {
502
424
  urlChanged,
503
425
  previousUrl: previousState.url,
@@ -505,6 +427,7 @@ export class ActionResult {
505
427
  };
506
428
  if (diff.ariaChanged) {
507
429
  pageDiff.ariaChanges = diff.ariaChanged;
430
+ pageDiff.ariaChangeCount = diff.ariaChangeCount;
508
431
  }
509
432
  if (diff.htmlParts.length > 0) {
510
433
  const htmlConfig = this.normalizeHtmlConfig();
@@ -521,10 +444,7 @@ export class ActionResult {
521
444
  }
522
445
  }
523
446
  if (pageDiff.ariaChanges && this.iframeSnapshots.length > 0) {
524
- const addedCount = (pageDiff.ariaChanges.match(/\n {4}- /g) || []).length;
525
- const removedMatch = pageDiff.ariaChanges.match(/removed: (\d+) interactive/);
526
- const removedCount = removedMatch ? Number.parseInt(removedMatch[1]) : 0;
527
- if (addedCount + removedCount >= 50) {
447
+ if (diff.ariaChangeCount >= LARGE_ARIA_CHANGE_THRESHOLD) {
528
448
  pageDiff.iframes = this.iframeSnapshots.map((snap) => `iframe src="${snap.src}":\n${snap.html}`).join('\n\n');
529
449
  }
530
450
  }
@@ -559,18 +479,22 @@ export class Diff {
559
479
  previous;
560
480
  _htmlDiffResult = null;
561
481
  _ariaDiffResult = null;
482
+ _ariaChangeCount = 0;
562
483
  _isSameUrl;
563
- _urlChanged;
564
484
  constructor(current, previous) {
565
485
  this.current = current;
566
486
  this.previous = previous;
567
487
  this._isSameUrl = previous ? current.isSameUrl({ url: previous.url }) : false;
568
- this._urlChanged = !this._isSameUrl;
488
+ }
489
+ static async create(current, previous) {
490
+ const diff = new Diff(current, previous);
491
+ await diff.calculate();
492
+ return diff;
569
493
  }
570
494
  hasChanges() {
571
495
  if (!this.previous)
572
496
  return false;
573
- if (this._urlChanged)
497
+ if (!this._isSameUrl)
574
498
  return true;
575
499
  const hasHtmlChanges = this._htmlDiffResult && (this._htmlDiffResult.parts.length > 0 || this._htmlDiffResult.added.length > 0 || this._htmlDiffResult.removed.length > 0);
576
500
  const hasAriaChanges = this._ariaDiffResult !== null;
@@ -580,7 +504,7 @@ export class Diff {
580
504
  return this._isSameUrl;
581
505
  }
582
506
  urlHasChanged() {
583
- return this._urlChanged;
507
+ return !this._isSameUrl;
584
508
  }
585
509
  get htmlParts() {
586
510
  if (!this._htmlDiffResult)
@@ -590,21 +514,20 @@ export class Diff {
590
514
  get ariaChanged() {
591
515
  return this._ariaDiffResult;
592
516
  }
593
- get ariaRemoved() {
594
- return this._ariaDiffResult;
517
+ get ariaChangeCount() {
518
+ return this._ariaChangeCount;
595
519
  }
596
520
  get htmlDiff() {
597
521
  return this._htmlDiffResult;
598
522
  }
599
- get ariaDiff() {
600
- return this._ariaDiffResult;
601
- }
602
523
  async calculate() {
603
524
  if (!this.previous)
604
525
  return;
605
526
  if (this._isSameUrl) {
606
527
  this._htmlDiffResult = await htmlDiff(this.previous.html, this.current.html, ConfigParser.getInstance().getConfig().html);
607
528
  }
608
- this._ariaDiffResult = diffAriaSnapshots(this.previous.ariaSnapshot, this.current.ariaSnapshot);
529
+ const ariaDiff = diffAriaSnapshots(this.previous.ariaSnapshot, this.current.ariaSnapshot);
530
+ this._ariaDiffResult = ariaDiff.text;
531
+ this._ariaChangeCount = ariaDiff.count;
609
532
  }
610
533
  }
@@ -0,0 +1,43 @@
1
+ import { ActionResult } from './action-result.js';
2
+ import type { ExplorbotConfig } from './config.js';
3
+ import type { PlaywrightRecorder } from './playwright-recorder.js';
4
+ import type { StateManager } from './state-manager.js';
5
+ declare class Action {
6
+ actor: CodeceptJS.I;
7
+ stateManager: StateManager;
8
+ actionResult: ActionResult | null;
9
+ config: ExplorbotConfig;
10
+ action: string | null;
11
+ lastError: Error | null;
12
+ playwrightHelper: any;
13
+ playwrightGroupId: string | null;
14
+ assertionSteps: Array<{
15
+ name: string;
16
+ args: any[];
17
+ }>;
18
+ recorder?: PlaywrightRecorder;
19
+ mainDocumentStatus: number | undefined;
20
+ constructor(actor: CodeceptJS.I, stateManager: StateManager, recorder?: PlaywrightRecorder);
21
+ saveScreenshot(): Promise<string | undefined>;
22
+ capturePageState({ includeScreenshot, codeBlock }?: {
23
+ includeScreenshot?: boolean;
24
+ codeBlock?: string;
25
+ }): Promise<ActionResult>;
26
+ captureMainDocumentStatus(): Promise<number | undefined>;
27
+ captureMainDocumentResponse(): () => void;
28
+ /**
29
+ * Capture HTML snapshots of all iframes on the page
30
+ */
31
+ captureIframeSnapshots(mainHtml: string): Promise<Array<{
32
+ src: string;
33
+ html: string;
34
+ id?: string;
35
+ }>>;
36
+ captureBrowserLogs(): Promise<any[]>;
37
+ execute(code: string): Promise<Action>;
38
+ attempt(codeBlock: string, originalMessage?: string): Promise<boolean>;
39
+ getActor(): CodeceptJS.I;
40
+ getActionResult(): ActionResult | null;
41
+ waitForPageReadiness(page: any): Promise<void>;
42
+ }
43
+ export default Action;