explorbot 0.1.32-beta.1 → 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 (168) 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 +20 -3
  17. package/dist/src/action-result.d.ts +8 -10
  18. package/dist/src/action-result.js +72 -164
  19. package/dist/src/action.d.ts +3 -10
  20. package/dist/src/action.js +18 -111
  21. package/dist/src/ai/captain/web-mode.js +0 -7
  22. package/dist/src/ai/captain.d.ts +1 -3
  23. package/dist/src/ai/captain.js +4 -19
  24. package/dist/src/ai/conversation.d.ts +2 -5
  25. package/dist/src/ai/conversation.js +9 -28
  26. package/dist/src/ai/driller.d.ts +0 -4
  27. package/dist/src/ai/driller.js +3 -12
  28. package/dist/src/ai/experience-compactor.d.ts +2 -2
  29. package/dist/src/ai/experience-compactor.js +16 -33
  30. package/dist/src/ai/fisherman-tools.js +1 -1
  31. package/dist/src/ai/fisherman.d.ts +0 -1
  32. package/dist/src/ai/fisherman.js +0 -3
  33. package/dist/src/ai/historian/codeceptjs.d.ts +2 -0
  34. package/dist/src/ai/historian/codeceptjs.js +3 -2
  35. package/dist/src/ai/historian/playwright.d.ts +2 -0
  36. package/dist/src/ai/historian/playwright.js +3 -2
  37. package/dist/src/ai/historian/utils.d.ts +0 -1
  38. package/dist/src/ai/historian/utils.js +0 -1
  39. package/dist/src/ai/historian.d.ts +5 -4
  40. package/dist/src/ai/historian.js +1 -5
  41. package/dist/src/ai/navigator.d.ts +3 -5
  42. package/dist/src/ai/navigator.js +11 -46
  43. package/dist/src/ai/pilot.d.ts +1 -3
  44. package/dist/src/ai/pilot.js +6 -17
  45. package/dist/src/ai/planner.d.ts +3 -3
  46. package/dist/src/ai/planner.js +49 -37
  47. package/dist/src/ai/provider.d.ts +4 -1
  48. package/dist/src/ai/provider.js +77 -136
  49. package/dist/src/ai/quartermaster.d.ts +21 -22
  50. package/dist/src/ai/quartermaster.js +4 -10
  51. package/dist/src/ai/rerunner.d.ts +1 -2
  52. package/dist/src/ai/rerunner.js +14 -22
  53. package/dist/src/ai/researcher/cache.js +11 -18
  54. package/dist/src/ai/researcher/deep-analysis.js +3 -6
  55. package/dist/src/ai/researcher/parser.js +1 -1
  56. package/dist/src/ai/researcher/research-result.d.ts +0 -1
  57. package/dist/src/ai/researcher/research-result.js +0 -19
  58. package/dist/src/ai/researcher/sections.js +5 -1
  59. package/dist/src/ai/researcher.js +12 -53
  60. package/dist/src/ai/rules.d.ts +1 -0
  61. package/dist/src/ai/rules.js +22 -15
  62. package/dist/src/ai/session-analyst.js +8 -5
  63. package/dist/src/ai/task-agent.d.ts +1 -1
  64. package/dist/src/ai/task-agent.js +2 -27
  65. package/dist/src/ai/tester.d.ts +0 -2
  66. package/dist/src/ai/tester.js +10 -61
  67. package/dist/src/ai/tools.d.ts +18 -7
  68. package/dist/src/ai/tools.js +119 -101
  69. package/dist/src/commands/compact-command.d.ts +1 -1
  70. package/dist/src/commands/compact-command.js +6 -6
  71. package/dist/src/commands/context-command.js +1 -1
  72. package/dist/src/commands/context-knowledge-command.js +1 -1
  73. package/dist/src/commands/experience-command.js +1 -1
  74. package/dist/src/commands/knows-command.js +1 -1
  75. package/dist/src/commands/learn-command.js +3 -1
  76. package/dist/src/components/AddKnowledge.d.ts +2 -0
  77. package/dist/src/components/AddKnowledge.js +3 -7
  78. package/dist/src/components/App.js +2 -2
  79. package/dist/src/config.d.ts +2 -0
  80. package/dist/src/config.js +12 -0
  81. package/dist/src/experience-tracker.d.ts +11 -6
  82. package/dist/src/experience-tracker.js +81 -154
  83. package/dist/src/explorbot.d.ts +4 -5
  84. package/dist/src/explorbot.js +17 -51
  85. package/dist/src/explorer.d.ts +4 -15
  86. package/dist/src/explorer.js +18 -68
  87. package/dist/src/knowledge-tracker.d.ts +2 -2
  88. package/dist/src/knowledge-tracker.js +38 -45
  89. package/dist/src/state-manager.d.ts +6 -31
  90. package/dist/src/state-manager.js +10 -129
  91. package/dist/src/utils/aria.d.ts +6 -1
  92. package/dist/src/utils/aria.js +4 -1
  93. package/dist/src/utils/cache.d.ts +15 -0
  94. package/dist/src/utils/cache.js +34 -0
  95. package/dist/src/utils/context-formatter.d.ts +6 -0
  96. package/dist/src/utils/context-formatter.js +15 -27
  97. package/dist/src/utils/html.d.ts +5 -0
  98. package/dist/src/utils/html.js +71 -0
  99. package/dist/src/utils/markdown-files.d.ts +10 -0
  100. package/dist/src/utils/markdown-files.js +21 -0
  101. package/dist/src/utils/markdown-query.d.ts +6 -0
  102. package/dist/src/utils/markdown-query.js +14 -1
  103. package/dist/src/utils/page-readiness.d.ts +1 -0
  104. package/dist/src/utils/page-readiness.js +1 -1
  105. package/dist/src/utils/secrets.js +2 -2
  106. package/dist/src/utils/strings.d.ts +2 -0
  107. package/dist/src/utils/strings.js +10 -0
  108. package/dist/src/utils/web-element.d.ts +2 -1
  109. package/dist/src/utils/web-element.js +3 -0
  110. package/dist/src/utils/web-sandbox.d.ts +4 -0
  111. package/dist/src/utils/web-sandbox.js +37 -0
  112. package/package.json +20 -3
  113. package/src/action-result.ts +70 -173
  114. package/src/action.ts +16 -130
  115. package/src/ai/captain/web-mode.ts +0 -7
  116. package/src/ai/captain.ts +5 -17
  117. package/src/ai/conversation.ts +9 -32
  118. package/src/ai/driller.ts +3 -16
  119. package/src/ai/experience-compactor.ts +16 -35
  120. package/src/ai/fisherman-tools.ts +1 -1
  121. package/src/ai/fisherman.ts +0 -4
  122. package/src/ai/historian/codeceptjs.ts +4 -2
  123. package/src/ai/historian/playwright.ts +4 -2
  124. package/src/ai/historian/utils.ts +0 -1
  125. package/src/ai/historian.ts +4 -7
  126. package/src/ai/navigator.ts +13 -49
  127. package/src/ai/pilot.ts +6 -16
  128. package/src/ai/planner.ts +56 -44
  129. package/src/ai/provider.ts +85 -145
  130. package/src/ai/quartermaster.ts +34 -41
  131. package/src/ai/rerunner.ts +17 -21
  132. package/src/ai/researcher/cache.ts +10 -16
  133. package/src/ai/researcher/deep-analysis.ts +3 -6
  134. package/src/ai/researcher/parser.ts +1 -1
  135. package/src/ai/researcher/research-result.ts +0 -18
  136. package/src/ai/researcher/sections.ts +5 -1
  137. package/src/ai/researcher.ts +11 -59
  138. package/src/ai/rules.ts +24 -13
  139. package/src/ai/session-analyst.ts +8 -5
  140. package/src/ai/task-agent.ts +3 -30
  141. package/src/ai/tester.ts +12 -73
  142. package/src/ai/tools.ts +119 -106
  143. package/src/commands/compact-command.ts +7 -8
  144. package/src/commands/context-command.ts +1 -1
  145. package/src/commands/context-knowledge-command.ts +1 -1
  146. package/src/commands/experience-command.ts +1 -1
  147. package/src/commands/knows-command.ts +1 -1
  148. package/src/commands/learn-command.ts +3 -1
  149. package/src/components/AddKnowledge.tsx +5 -7
  150. package/src/components/App.tsx +2 -2
  151. package/src/config.ts +12 -0
  152. package/src/experience-tracker.ts +88 -151
  153. package/src/explorbot.ts +17 -51
  154. package/src/explorer.ts +20 -72
  155. package/src/knowledge-tracker.ts +42 -49
  156. package/src/state-manager.ts +15 -156
  157. package/src/utils/aria.ts +11 -2
  158. package/src/utils/cache.ts +40 -0
  159. package/src/utils/context-formatter.ts +12 -15
  160. package/src/utils/html.ts +79 -0
  161. package/src/utils/markdown-files.ts +30 -0
  162. package/src/utils/markdown-query.ts +15 -1
  163. package/src/utils/page-readiness.ts +1 -1
  164. package/src/utils/secrets.ts +2 -3
  165. package/src/utils/strings.ts +12 -0
  166. package/src/utils/web-element.ts +4 -0
  167. package/src/utils/web-sandbox.ts +43 -0
  168. package/dist/boat/api-tester/example/apibot.config.js +0 -30
@@ -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,13 +27,12 @@ export class ActionResult {
26
27
  _screenshot = undefined;
27
28
  htmlFile = undefined;
28
29
  _html = undefined;
29
- snapshotCache = new Map();
30
+ snapshotCache = new TTLCache();
30
31
  logFile = undefined;
31
- _browserLogs = undefined;
32
32
  ariaSnapshotFile = undefined;
33
33
  _ariaSnapshot = undefined;
34
34
  _lastExtractedHtml = undefined;
35
- notes;
35
+ notes = [];
36
36
  links = [];
37
37
  verifications;
38
38
  constructor(data) {
@@ -61,20 +61,17 @@ export class ActionResult {
61
61
  if (data.ariaSnapshotFile !== undefined) {
62
62
  this.ariaSnapshotFile = data.ariaSnapshotFile;
63
63
  }
64
- // Store HTML and browser logs in private properties if provided
64
+ // Store HTML in a private property if provided
65
65
  if (data.html !== undefined) {
66
66
  this._html = data.html;
67
67
  }
68
- if (data.browserLogs !== undefined) {
69
- this._browserLogs = data.browserLogs;
70
- }
71
68
  if (data.screenshot !== undefined) {
72
69
  this._screenshot = data.screenshot;
73
70
  }
74
71
  if (data.ariaSnapshot !== undefined) {
75
72
  this._ariaSnapshot = data.ariaSnapshot;
76
73
  }
77
- if (!this.fullUrl && this.url && this.url !== '') {
74
+ if (!this.fullUrl && this.url) {
78
75
  this.fullUrl = this.url;
79
76
  }
80
77
  this.extractHeadings(this.html);
@@ -84,7 +81,7 @@ export class ActionResult {
84
81
  else if (this._html) {
85
82
  this.links = extractLinks(this._html);
86
83
  }
87
- if (this.url && this.url !== '') {
84
+ if (this.url) {
88
85
  this.url = extractStatePath(this.url);
89
86
  }
90
87
  }
@@ -112,15 +109,6 @@ export class ActionResult {
112
109
  }
113
110
  return undefined;
114
111
  }
115
- get browserLogsContent() {
116
- if (this._browserLogs !== undefined) {
117
- return this._browserLogs;
118
- }
119
- if (this.logFile) {
120
- return ActionResult.loadBrowserLogsFromFile(this.logFile);
121
- }
122
- return [];
123
- }
124
112
  get ariaSnapshot() {
125
113
  if (this._ariaSnapshot !== undefined) {
126
114
  return this._ariaSnapshot;
@@ -168,13 +156,13 @@ export class ActionResult {
168
156
  return null;
169
157
  }
170
158
  isSameUrl(state) {
171
- if (!this.url || this.url === '') {
159
+ if (!this.url) {
172
160
  return false;
173
161
  }
174
162
  return extractStatePath(state.url) === extractStatePath(this.url);
175
163
  }
176
164
  isMatchedBy(state) {
177
- if (!this.url || this.url === '') {
165
+ if (!this.url) {
178
166
  return false;
179
167
  }
180
168
  const isRelevant = matchesUrl(extractStatePath(state.url), extractStatePath(this.url));
@@ -209,12 +197,12 @@ export class ActionResult {
209
197
  async simplifiedHtml(htmlConfig) {
210
198
  const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
211
199
  const cacheKey = `simplified:${JSON.stringify(normalizedConfig?.minimal ?? null)}`;
212
- return this.cachedSnapshot(cacheKey, () => minifyHtml(htmlMinimalUISnapshot(this.html ?? '', normalizedConfig?.minimal)));
200
+ return this.snapshotCache.fetch(cacheKey, () => minifyHtml(htmlMinimalUISnapshot(this.html ?? '', normalizedConfig?.minimal)));
213
201
  }
214
202
  async combinedHtml(htmlConfig) {
215
203
  const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
216
204
  const cacheKey = `combined:${!!htmlConfig?.keepPositions}:${JSON.stringify(normalizedConfig?.combined ?? null)}`;
217
- return this.cachedSnapshot(cacheKey, async () => {
205
+ return this.snapshotCache.fetch(cacheKey, async () => {
218
206
  const combinedHtml = await minifyHtml(htmlCombinedSnapshot(this.html ?? '', normalizedConfig?.combined, { keepPositions: htmlConfig?.keepPositions }));
219
207
  debugLog(`----${this.url}----`);
220
208
  debugLog(`Combined HTML: \n${combinedHtml}`);
@@ -225,7 +213,7 @@ export class ActionResult {
225
213
  async textHtml(htmlConfig) {
226
214
  const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
227
215
  const cacheKey = `text:${JSON.stringify(normalizedConfig?.text ?? null)}`;
228
- return this.cachedSnapshot(cacheKey, () => minifyHtml(htmlTextSnapshot(this.html ?? '', normalizedConfig?.text)));
216
+ return this.snapshotCache.fetch(cacheKey, () => minifyHtml(htmlTextSnapshot(this.html ?? '', normalizedConfig?.text)));
229
217
  }
230
218
  getInteractiveARIA() {
231
219
  return compactAriaSnapshot(this.ariaSnapshot, false);
@@ -240,14 +228,6 @@ export class ActionResult {
240
228
  const parser = ConfigParser.getInstance();
241
229
  return parser.getConfig().html;
242
230
  }
243
- async cachedSnapshot(key, compute) {
244
- const cached = this.snapshotCache.get(key);
245
- if (cached !== undefined)
246
- return cached;
247
- const value = await compute();
248
- this.snapshotCache.set(key, value);
249
- return value;
250
- }
251
231
  static fromState(state) {
252
232
  let html = undefined;
253
233
  let screenshot = undefined;
@@ -285,26 +265,27 @@ export class ActionResult {
285
265
  }
286
266
  return new ActionResult(actionResultData);
287
267
  }
288
- static loadHtmlFromFile(htmlFile) {
268
+ static loadStateFile(file) {
289
269
  try {
290
- const filePath = outputPath('states', htmlFile);
291
- if (fs.existsSync(filePath)) {
292
- return fs.readFileSync(filePath, 'utf8');
293
- }
294
- return null;
270
+ const filePath = outputPath('states', file);
271
+ if (!fs.existsSync(filePath))
272
+ return null;
273
+ return fs.readFileSync(filePath, 'utf8');
295
274
  }
296
275
  catch (error) {
297
- console.error('Failed to load HTML from file:', error);
276
+ console.error('Failed to load state file:', error);
298
277
  return null;
299
278
  }
300
279
  }
280
+ static loadHtmlFromFile(htmlFile) {
281
+ return ActionResult.loadStateFile(htmlFile);
282
+ }
301
283
  static loadScreenshotFromFile(screenshotFile) {
302
284
  try {
303
285
  const filePath = outputPath('states', screenshotFile);
304
- if (fs.existsSync(filePath)) {
305
- return fs.readFileSync(filePath);
306
- }
307
- return undefined;
286
+ if (!fs.existsSync(filePath))
287
+ return undefined;
288
+ return fs.readFileSync(filePath);
308
289
  }
309
290
  catch (error) {
310
291
  console.error('Failed to load screenshot from file:', error);
@@ -312,55 +293,38 @@ export class ActionResult {
312
293
  }
313
294
  }
314
295
  static loadBrowserLogsFromFile(logFile) {
315
- try {
316
- const filePath = outputPath('states', logFile);
317
- if (fs.existsSync(filePath)) {
318
- const logContent = fs.readFileSync(filePath, 'utf8');
319
- return logContent
320
- .split('\n')
321
- .filter((line) => line.trim())
322
- .map((line) => {
323
- const match = line.match(/\[([^\]]+)\] (\w+): (.+)/);
324
- if (match) {
325
- return {
326
- timestamp: match[1],
327
- type: match[2].toLowerCase(),
328
- text: match[3],
329
- level: match[2].toLowerCase(),
330
- message: match[3],
331
- };
332
- }
333
- return { text: line, type: 'log', level: 'log', message: line };
334
- });
335
- }
296
+ const logContent = ActionResult.loadStateFile(logFile);
297
+ if (!logContent)
336
298
  return [];
337
- }
338
- catch (error) {
339
- console.error('Failed to load browser logs from file:', error);
340
- return [];
341
- }
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
+ });
342
315
  }
343
316
  static loadAriaSnapshotFromFile(ariaSnapshotFile) {
344
- try {
345
- const filePath = outputPath('states', ariaSnapshotFile);
346
- if (!fs.existsSync(filePath)) {
347
- return null;
348
- }
349
- const content = fs.readFileSync(filePath, 'utf8');
350
- const trimmed = content.trim();
351
- if (!trimmed) {
352
- return null;
353
- }
354
- return trimmed.endsWith('\n') ? trimmed : `${trimmed}\n`;
355
- }
356
- catch (error) {
357
- console.error('Failed to load aria snapshot from file:', error);
317
+ const content = ActionResult.loadStateFile(ariaSnapshotFile);
318
+ if (!content)
358
319
  return null;
359
- }
320
+ const trimmed = content.trim();
321
+ if (!trimmed)
322
+ return null;
323
+ return trimmed.endsWith('\n') ? trimmed : `${trimmed}\n`;
360
324
  }
361
325
  toAiContext() {
362
326
  const parts = [];
363
- if (this.url && this.url !== '') {
327
+ if (this.url) {
364
328
  parts.push(`<url>${this.url}</url>`);
365
329
  }
366
330
  if (this.title) {
@@ -395,7 +359,7 @@ export class ActionResult {
395
359
  return parts.join('\n');
396
360
  }
397
361
  get relativeUrl() {
398
- if (!this.url || this.url === '')
362
+ if (!this.url)
399
363
  return null;
400
364
  try {
401
365
  const urlObj = new URL(this.url);
@@ -416,20 +380,11 @@ export class ActionResult {
416
380
  const parts = [];
417
381
  parts.push(this.relativeUrl || this.url || '/');
418
382
  this.extractHeadings(this.html);
419
- const headings = ['h1', 'h2'];
420
- for (const heading of headings) {
421
- const value = this[heading];
422
- if (value) {
423
- parts.push(`${heading}_${value}`);
424
- }
425
- }
426
- let stateString = parts
427
- .map((part) => part.substring(0, 100))
428
- .join('_')
429
- .replace(/[^a-zA-Z0-9_]/g, '_')
430
- .replace(/_+/g, '_')
431
- .replace(/^_|_$/g, '')
432
- .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('_'));
433
388
  if (stateString.length > 200) {
434
389
  stateString = stateString.substring(0, 200);
435
390
  if (stateString.endsWith('_')) {
@@ -438,55 +393,8 @@ export class ActionResult {
438
393
  }
439
394
  return stateString;
440
395
  }
441
- saveBrowserLogs() {
442
- const logs = this.browserLogsContent;
443
- if (!logs || logs.length === 0)
444
- return;
445
- try {
446
- const outputDir = 'output';
447
- const stateHash = this.getStateHash();
448
- const filename = `${stateHash}.log`;
449
- const filePath = join(outputDir, filename);
450
- // Ensure output directory exists
451
- if (!fs.existsSync(outputDir)) {
452
- fs.mkdirSync(outputDir, { recursive: true });
453
- }
454
- // Format logs for saving
455
- const formattedLogs = logs.map((log) => {
456
- const timestamp = new Date().toISOString();
457
- const level = (log.type || log.level || 'LOG').toUpperCase();
458
- const message = log.text || log.message || String(log);
459
- return `[${timestamp}] ${level}: ${message}`;
460
- });
461
- // Save log content to file
462
- const logContent = `${formattedLogs.join('\n')}\n`;
463
- fs.writeFileSync(filePath, logContent, 'utf8');
464
- }
465
- catch (error) {
466
- // Silently fail to avoid breaking the main flow
467
- console.error('Failed to save browser logs:', error);
468
- }
469
- }
470
- saveHtmlOutput() {
471
- try {
472
- const outputDir = 'output';
473
- const stateHash = this.getStateHash();
474
- const filename = `${stateHash}.html`;
475
- const filePath = join(outputDir, filename);
476
- // Ensure output directory exists
477
- if (!fs.existsSync(outputDir)) {
478
- fs.mkdirSync(outputDir, { recursive: true });
479
- }
480
- // Save HTML content to file
481
- fs.writeFileSync(filePath, this.html, 'utf8');
482
- }
483
- catch (error) {
484
- // Silently fail to avoid breaking the main flow
485
- console.error('Failed to save HTML output:', error);
486
- }
487
- }
488
396
  async diff(previousState) {
489
- return new Diff(this, previousState);
397
+ return Diff.create(this, previousState);
490
398
  }
491
399
  async toToolResult(previousState, locator) {
492
400
  const result = {
@@ -512,7 +420,6 @@ export class ActionResult {
512
420
  return result;
513
421
  }
514
422
  const diff = await this.diff(previousState);
515
- await diff.calculate();
516
423
  const pageDiff = {
517
424
  urlChanged,
518
425
  previousUrl: previousState.url,
@@ -520,6 +427,7 @@ export class ActionResult {
520
427
  };
521
428
  if (diff.ariaChanged) {
522
429
  pageDiff.ariaChanges = diff.ariaChanged;
430
+ pageDiff.ariaChangeCount = diff.ariaChangeCount;
523
431
  }
524
432
  if (diff.htmlParts.length > 0) {
525
433
  const htmlConfig = this.normalizeHtmlConfig();
@@ -536,10 +444,7 @@ export class ActionResult {
536
444
  }
537
445
  }
538
446
  if (pageDiff.ariaChanges && this.iframeSnapshots.length > 0) {
539
- const addedCount = (pageDiff.ariaChanges.match(/\n {4}- /g) || []).length;
540
- const removedMatch = pageDiff.ariaChanges.match(/removed: (\d+) interactive/);
541
- const removedCount = removedMatch ? Number.parseInt(removedMatch[1]) : 0;
542
- if (addedCount + removedCount >= 50) {
447
+ if (diff.ariaChangeCount >= LARGE_ARIA_CHANGE_THRESHOLD) {
543
448
  pageDiff.iframes = this.iframeSnapshots.map((snap) => `iframe src="${snap.src}":\n${snap.html}`).join('\n\n');
544
449
  }
545
450
  }
@@ -574,18 +479,22 @@ export class Diff {
574
479
  previous;
575
480
  _htmlDiffResult = null;
576
481
  _ariaDiffResult = null;
482
+ _ariaChangeCount = 0;
577
483
  _isSameUrl;
578
- _urlChanged;
579
484
  constructor(current, previous) {
580
485
  this.current = current;
581
486
  this.previous = previous;
582
487
  this._isSameUrl = previous ? current.isSameUrl({ url: previous.url }) : false;
583
- 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;
584
493
  }
585
494
  hasChanges() {
586
495
  if (!this.previous)
587
496
  return false;
588
- if (this._urlChanged)
497
+ if (!this._isSameUrl)
589
498
  return true;
590
499
  const hasHtmlChanges = this._htmlDiffResult && (this._htmlDiffResult.parts.length > 0 || this._htmlDiffResult.added.length > 0 || this._htmlDiffResult.removed.length > 0);
591
500
  const hasAriaChanges = this._ariaDiffResult !== null;
@@ -595,7 +504,7 @@ export class Diff {
595
504
  return this._isSameUrl;
596
505
  }
597
506
  urlHasChanged() {
598
- return this._urlChanged;
507
+ return !this._isSameUrl;
599
508
  }
600
509
  get htmlParts() {
601
510
  if (!this._htmlDiffResult)
@@ -605,21 +514,20 @@ export class Diff {
605
514
  get ariaChanged() {
606
515
  return this._ariaDiffResult;
607
516
  }
608
- get ariaRemoved() {
609
- return this._ariaDiffResult;
517
+ get ariaChangeCount() {
518
+ return this._ariaChangeCount;
610
519
  }
611
520
  get htmlDiff() {
612
521
  return this._htmlDiffResult;
613
522
  }
614
- get ariaDiff() {
615
- return this._ariaDiffResult;
616
- }
617
523
  async calculate() {
618
524
  if (!this.previous)
619
525
  return;
620
526
  if (this._isSameUrl) {
621
527
  this._htmlDiffResult = await htmlDiff(this.previous.html, this.current.html, ConfigParser.getInstance().getConfig().html);
622
528
  }
623
- 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;
624
532
  }
625
533
  }
@@ -8,7 +8,6 @@ declare class Action {
8
8
  actionResult: ActionResult | null;
9
9
  config: ExplorbotConfig;
10
10
  action: string | null;
11
- expectation: string | null;
12
11
  lastError: Error | null;
13
12
  playwrightHelper: any;
14
13
  playwrightGroupId: string | null;
@@ -19,10 +18,10 @@ declare class Action {
19
18
  recorder?: PlaywrightRecorder;
20
19
  mainDocumentStatus: number | undefined;
21
20
  constructor(actor: CodeceptJS.I, stateManager: StateManager, recorder?: PlaywrightRecorder);
22
- caputrePageWithScreenshot(): Promise<ActionResult>;
23
21
  saveScreenshot(): Promise<string | undefined>;
24
- capturePageState({ includeScreenshot }?: {
22
+ capturePageState({ includeScreenshot, codeBlock }?: {
25
23
  includeScreenshot?: boolean;
24
+ codeBlock?: string;
26
25
  }): Promise<ActionResult>;
27
26
  captureMainDocumentStatus(): Promise<number | undefined>;
28
27
  captureMainDocumentResponse(): () => void;
@@ -36,15 +35,9 @@ declare class Action {
36
35
  }>>;
37
36
  captureBrowserLogs(): Promise<any[]>;
38
37
  execute(code: string): Promise<Action>;
39
- expect(codeOrFunction: string | ((I: CodeceptJS.I) => void)): Promise<Action>;
40
- waitForInteraction(): Promise<Action>;
41
- attempt(codeBlock: string, originalMessage?: string, experience?: boolean): Promise<boolean>;
38
+ attempt(codeBlock: string, originalMessage?: string): Promise<boolean>;
42
39
  getActor(): CodeceptJS.I;
43
- setActor(actor: CodeceptJS.I): void;
44
- getCurrentState(): ActionResult | null;
45
40
  getActionResult(): ActionResult | null;
46
41
  waitForPageReadiness(page: any): Promise<void>;
47
42
  }
48
43
  export default Action;
49
- export declare function sanitizeCodeBlock(code: string): string;
50
- export declare function createSandboxedFunction(argNames: string[], body: string): (...args: any[]) => any;