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
@@ -3,7 +3,6 @@ import { z } from 'zod';
3
3
  import { ActionResult } from "../action-result.js";
4
4
  import { setActivity } from "../activity.js";
5
5
  import { ConfigParser } from "../config.js";
6
- import { ExperienceTracker } from "../experience-tracker.js";
7
6
  import { Observability } from "../observability.js";
8
7
  import { Stats } from "../stats.js";
9
8
  import { Suite } from "../suite.js";
@@ -16,7 +15,7 @@ import { Conversation } from "./conversation.js";
16
15
  import { WithSessionDedup } from "./planner/session-dedup.js";
17
16
  import { getActiveStyle } from "./planner/styles.js";
18
17
  import { WithSubPages, getPlannedByStateHash, getRegisteredPlan, registerPlan } from "./planner/subpages.js";
19
- import { POSSIBLE_SECTIONS, Researcher } from "./researcher.js";
18
+ import { POSSIBLE_SECTIONS } from "./researcher.js";
20
19
  import { findSimilarStateHash } from "./researcher/cache.js";
21
20
  import { hasFocusedSection } from "./researcher/focus.js";
22
21
  import { capabilityGroundingRule, dataProtectionRules, fileUploadRule } from "./rules.js";
@@ -50,13 +49,13 @@ export class Planner extends PlannerBase {
50
49
  lastSuite = null;
51
50
  researcher;
52
51
  fisherman = null;
53
- constructor(explorer, provider) {
52
+ constructor(explorer, provider, researcher) {
54
53
  super();
55
54
  this.explorer = explorer;
56
55
  this.provider = provider;
57
- this.researcher = new Researcher(explorer, provider);
56
+ this.researcher = researcher;
58
57
  this.stateManager = explorer.getStateManager();
59
- this.experienceTracker = new ExperienceTracker();
58
+ this.experienceTracker = explorer.getStateManager().getExperienceTracker();
60
59
  }
61
60
  setFisherman(fisherman) {
62
61
  this.fisherman = fisherman;
@@ -81,7 +80,7 @@ export class Planner extends PlannerBase {
81
80
  You are ISTQB certified senior manual QA planning exploratory testing session of a web application.
82
81
  Each test scenario must complete a meaningful user workflow — not just open a UI element and verify it exists.
83
82
  Bad: "Open the Help panel" — just opens something.
84
- Good: "Create a new suite and verify it appears in the list" — completes a business action.
83
+ Good: "Create a new post and verify it appears in the list" — completes a business action.
85
84
  </role>
86
85
  <task>
87
86
  List possible testing scenarios for the web page.
@@ -149,6 +148,7 @@ export class Planner extends PlannerBase {
149
148
  const tags = ['planner'];
150
149
  if (style)
151
150
  tags.push(style);
151
+ const existingTestScenarios = this.getExistingTestFileScenarios(state.url);
152
152
  const result = await Observability.run(`planner: ${state.url}`, { tags, sessionId: state.url }, async () => {
153
153
  const actionResult = ActionResult.fromState(state);
154
154
  const conversation = await this.buildConversation(actionResult, style, parentPlan, feature);
@@ -182,7 +182,6 @@ export class Planner extends PlannerBase {
182
182
  if (parentPlan)
183
183
  this.currentPlan.parentPlan = parentPlan;
184
184
  const allPreviousScenarios = this.getPreviousSessionScenarios();
185
- const existingTestScenarios = this.getExistingTestFileScenarios(state.url);
186
185
  for (const s of existingTestScenarios)
187
186
  allPreviousScenarios.add(s);
188
187
  for (const t of tests) {
@@ -232,6 +231,7 @@ export class Planner extends PlannerBase {
232
231
  return added;
233
232
  }
234
233
  getExistingTestFileScenarios(currentUrl) {
234
+ this.lastSuite = null;
235
235
  if (!currentUrl)
236
236
  return new Set();
237
237
  try {
@@ -244,27 +244,37 @@ export class Planner extends PlannerBase {
244
244
  }
245
245
  }
246
246
  cleanExperienceFlows(text) {
247
- const seenTitles = new Set();
248
247
  let result = text;
249
- for (const section of [...mdq(text).query('section2').each(), ...mdq(text).query('section3').each()]) {
250
- const heading = section.query('heading').text().trim();
251
- const body = mdq(section.text())
252
- .query('heading')
253
- .replace('')
254
- .replace(/^---\s*$/gm, '')
255
- .trim();
256
- if (!body || seenTitles.has(heading)) {
257
- result = result.replace(section.text(), '');
258
- continue;
259
- }
260
- seenTitles.add(heading);
261
- const blockquotes = section.query('blockquote').each();
262
- if (blockquotes.length <= 10)
263
- continue;
264
- for (const bq of blockquotes.slice(10)) {
265
- result = result.replace(bq.text(), '');
266
- }
267
- result = result.replace(section.text().trim(), `${section.text().trim()}\n> ... and ${blockquotes.length - 10} more discoveries`);
248
+ const seenTitles = new Set();
249
+ for (const selector of ['section2', 'section3']) {
250
+ result = mdq(result)
251
+ .query(selector)
252
+ .replaceEach((section) => {
253
+ const heading = section.query('heading').text().trim();
254
+ const withoutHeadings = mdq(section.text()).query('heading').replace('');
255
+ const body = mdq(withoutHeadings).query('hr').replace('').trim();
256
+ if (body && !seenTitles.has(heading)) {
257
+ seenTitles.add(heading);
258
+ return section.text();
259
+ }
260
+ return '';
261
+ });
262
+ }
263
+ const trimmedTitles = new Set();
264
+ for (const selector of ['section2', 'section3']) {
265
+ result = mdq(result)
266
+ .query(selector)
267
+ .replaceEach((section) => {
268
+ const heading = section.query('heading').text().trim();
269
+ if (trimmedTitles.has(heading))
270
+ return section.text();
271
+ const count = section.query('blockquote').count();
272
+ if (count <= 10)
273
+ return section.text();
274
+ const kept = mdq(section.text()).query('blockquote[10:]').replace('');
275
+ trimmedTitles.add(heading);
276
+ return `${kept.trimEnd()}\n> ... and ${count - 10} more discoveries\n`;
277
+ });
268
278
  }
269
279
  return result.trim() || null;
270
280
  }
@@ -298,6 +308,7 @@ export class Planner extends PlannerBase {
298
308
  If there are subpages (pages with same URL path) plan testing of those subpages as well
299
309
  If you plan to test CRUD operations, plan them in correct order: create, read, update.
300
310
  Do not invent specific route names, success messages, validation texts, badge counts, or welcome messages unless they are visible in research, visited pages, or prior observed flows.
311
+ When validation placement or wording was not observed, require feedback associated with the invalid input without inventing a specific location or message.
301
312
  If exact wording is unknown, describe the expected result generically, for example "an authentication error is shown" or "the user stays on the login page" instead of guessing the literal text.
302
313
  If exact redirect destination is unknown, describe the destination by visible page identity, for example "the dashboard page opens" or "the current workspace home page opens" instead of inventing a URL slug.
303
314
  Only propose scenarios whose prerequisites are evident from page research, visited pages, or API data preparation context.
@@ -337,17 +348,18 @@ export class Planner extends PlannerBase {
337
348
  deep: true,
338
349
  });
339
350
  let plannerResearch = mdq(research).query('code').replace('');
340
- for (const table of mdq(plannerResearch).query('table').each()) {
341
- const rawTable = table.text();
351
+ plannerResearch = mdq(plannerResearch)
352
+ .query('table')
353
+ .replaceEach((table) => {
342
354
  const rows = table.toJson();
343
355
  if (rows.length === 0 || !rows[0].Element)
344
- continue;
356
+ return table.text();
345
357
  const elementWithType = rows.map((r) => ({
346
358
  Element: r.Element,
347
359
  Type: r.Type || '',
348
360
  }));
349
- plannerResearch = plannerResearch.replace(rawTable, jsonToTable(elementWithType, ['Element', 'Type']));
350
- }
361
+ return jsonToTable(elementWithType, ['Element', 'Type']);
362
+ });
351
363
  const hasFocusedOverlay = hasFocusedSection(plannerResearch);
352
364
  const focusNote = hasFocusedOverlay ? "IMPORTANT: One section is marked as **Focused** — this is the user's current focus area. Concentrate testing on the Focused section FIRST — test all interactions inside it before planning tests for the rest of the page." : '';
353
365
  const featureFilter = feature ? `FOCUS FILTER: Only propose scenarios using elements relevant to "${feature}". Ignore all other elements.` : '';
@@ -525,17 +537,17 @@ export class Planner extends PlannerBase {
525
537
 
526
538
  STEPS - actionable commands:
527
539
  - Each step should be a specific action to perform
528
- - Good: "Click Create Suite button", "Enter 'My New Suite' as suite name", "Click Save button"
529
- - Good: "Click Star icon on 'Template API Testing' suite"
540
+ - Good: "Click Create Post button", "Enter 'My New Post' as post title", "Click Save button"
541
+ - Good: "Click Star icon on 'Quarterly Report' item"
530
542
  - Bad: "Open the dropdown menu" (opening is not a goal, it's a means)
531
543
  - Bad: "Verify modal appears" (verification belongs in expected outcomes, not steps)
532
544
  - Steps should form a complete workflow, not stop at opening a UI element
533
545
  - NEVER split a workflow across two tests. One test = one complete action + its verification
534
546
 
535
547
  EXPECTED OUTCOMES - verifiable results:
536
- - Good: "New suite 'My New Suite' appears in the suite list"
537
- - Good: "Suite appears under Starred filter tab"
538
- - Good: "Success message 'Suite created' is displayed"
548
+ - Good: "New post 'My New Post' appears in the posts list"
549
+ - Good: "Item appears under Starred filter tab"
550
+ - Good: "Success message 'Post created' is displayed"
539
551
  - Good when wording is unknown: "An authentication error is displayed"
540
552
  - Good when route is unknown: "The workspace home page is displayed"
541
553
  - Bad: "Modal is displayed" (just verifying existence, no business value)
@@ -0,0 +1,53 @@
1
+ import { NodeSDK } from '@opentelemetry/sdk-node';
2
+ import type { ModelMessage } from 'ai';
3
+ import type { AIConfig } from '../config.js';
4
+ import { type RetryOptions } from '../utils/retry.js';
5
+ import { Conversation } from './conversation.js';
6
+ declare class AiError extends Error {
7
+ }
8
+ export declare class ContextLengthError extends Error {
9
+ }
10
+ export declare class Provider {
11
+ config: AIConfig;
12
+ telemetryEnabled: boolean;
13
+ otelSdk: NodeSDK | null;
14
+ defaultRetryOptions: RetryOptions;
15
+ lastConversation: Conversation | null;
16
+ constructor(config: AIConfig);
17
+ getModelName(model: any): string;
18
+ validateConnection(): Promise<void>;
19
+ getModelForAgent(agentName?: string): any;
20
+ getAgenticModel(agentName?: string): any;
21
+ getConfiguredModels(): Record<string, string>;
22
+ getSystemPromptForAgent(agentName: string, currentUrl?: string): string | undefined;
23
+ getProviderOptionsForAgent(agentName: string): Record<string, any> | undefined;
24
+ getReasoningForAgent(agentName?: string): string | undefined;
25
+ getRetryOptions(options?: any): RetryOptions;
26
+ mergeProviderOptions(config: Record<string, any>, agentName?: string): Record<string, any>;
27
+ finalizeConfig(config: Record<string, any>, options: any, telemetry: any): void;
28
+ buildGenerateConfig(defaults: Record<string, any>, overrides: Record<string, any>, options: any): Record<string, any>;
29
+ recordUsage(agentName: string, modelName: string, usage: any): void;
30
+ raceWithIdleTimeout<T>(fn: (signal: AbortSignal) => Promise<T>, timeoutMs: number): Promise<T>;
31
+ recoverFromContextLength(error: any, messages: ModelMessage[], options: any, retry: (messages: ModelMessage[], options: any) => Promise<any>): Promise<any>;
32
+ initLangfuse(): void;
33
+ getTelemetry(options: any): any;
34
+ startConversation(systemMessage: string, agentName?: string, model?: any): Conversation;
35
+ invokeConversation(conversation: Conversation, tools?: any, options?: any): Promise<{
36
+ conversation: Conversation;
37
+ response: any;
38
+ toolExecutions?: any[];
39
+ } | null>;
40
+ chat(messages: ModelMessage[], model: any, options?: any): Promise<any>;
41
+ generateWithTools(messages: ModelMessage[], model: any, tools: any, options?: any): Promise<any>;
42
+ generateObject(messages: ModelMessage[], schema: any, model?: any, options?: any): Promise<any>;
43
+ static isContextLengthError(error: any): boolean;
44
+ static trimMessagesForRetry(messages: ModelMessage[]): ModelMessage[] | null;
45
+ static compactMessagesForRetry(messages: ModelMessage[]): ModelMessage[] | null;
46
+ tryReduceMessages(messages: ModelMessage[], retryLevel: number): {
47
+ messages: ModelMessage[];
48
+ nextLevel: number;
49
+ } | null;
50
+ processImage(prompt: string, image: string): Promise<any>;
51
+ hasVision(): boolean;
52
+ }
53
+ export { AiError, Provider as AIProvider };
@@ -9,7 +9,7 @@ import { Stats } from "../stats.js";
9
9
  import { createDebug, tag } from '../utils/logger.js';
10
10
  import { withRetry } from '../utils/retry.js';
11
11
  import { RulesLoader } from "../utils/rules-loader.js";
12
- import { Conversation } from './conversation.js';
12
+ import { Conversation, toToolExecution } from './conversation.js';
13
13
  const debugLog = createDebug('explorbot:provider');
14
14
  const promptLog = createDebug('explorbot:provider:out');
15
15
  const responseLog = createDebug('explorbot:provider:in');
@@ -18,6 +18,7 @@ class AiError extends Error {
18
18
  export class ContextLengthError extends Error {
19
19
  }
20
20
  let telemetryRegistered = false;
21
+ const CONTEXT_LENGTH_PATTERNS = ['reduce the length', 'context length', 'maximum context', 'token limit', 'too many tokens', 'max_tokens', 'context_length_exceeded', 'output truncated at maxtokens'];
21
22
  function extractCachedTokens(usage) {
22
23
  if (!usage)
23
24
  return 0;
@@ -28,20 +29,27 @@ function extractCachedTokens(usage) {
28
29
  const fromRaw = raw?.prompt_tokens_details?.cached_tokens ?? raw?.promptTokensDetails?.cachedTokens;
29
30
  return typeof fromRaw === 'number' ? fromRaw : 0;
30
31
  }
31
- function rejectAfterIdle(ms, signal) {
32
+ function abortAfterIdle(ms, cancel, controller) {
32
33
  return new Promise((_, reject) => {
33
34
  const tick = () => {
34
- if (signal.cancelled)
35
+ if (cancel.cancelled)
35
36
  return;
36
37
  if (executionController.isAwaitingInput()) {
37
38
  setTimeout(tick, ms);
38
39
  return;
39
40
  }
41
+ controller.abort();
40
42
  reject(new Error('AI request timeout'));
41
43
  };
42
44
  setTimeout(tick, ms);
43
45
  });
44
46
  }
47
+ function combinedAbortSignal(controller) {
48
+ const executionSignal = executionController.getAbortSignal();
49
+ if (!executionSignal)
50
+ return controller.signal;
51
+ return AbortSignal.any([controller.signal, executionSignal]);
52
+ }
45
53
  export class Provider {
46
54
  config;
47
55
  telemetryEnabled = false;
@@ -63,7 +71,6 @@ export class Provider {
63
71
  !error.message.includes('output truncated at maxTokens'));
64
72
  },
65
73
  };
66
- static CONTEXT_LENGTH_PATTERNS = ['reduce the length', 'context length', 'maximum context', 'token limit', 'too many tokens', 'max_tokens', 'context_length_exceeded', 'output truncated at maxtokens'];
67
74
  lastConversation = null;
68
75
  constructor(config) {
69
76
  if (!config?.model) {
@@ -160,6 +167,46 @@ export class Provider {
160
167
  if (reasoning)
161
168
  config.reasoning ??= reasoning;
162
169
  }
170
+ buildGenerateConfig(defaults, overrides, options) {
171
+ const telemetry = this.getTelemetry(options);
172
+ const config = this.mergeProviderOptions({
173
+ ...defaults,
174
+ allowSystemInMessages: true,
175
+ ...(this.config.config || {}),
176
+ ...options,
177
+ ...overrides,
178
+ }, options.agentName);
179
+ this.finalizeConfig(config, options, telemetry);
180
+ return config;
181
+ }
182
+ recordUsage(agentName, modelName, usage) {
183
+ if (!usage)
184
+ return;
185
+ Stats.recordTokens(agentName, modelName, {
186
+ input: usage.inputTokens ?? usage.promptTokens ?? 0,
187
+ output: usage.outputTokens ?? usage.completionTokens ?? 0,
188
+ total: usage.totalTokens ?? 0,
189
+ cached: extractCachedTokens(usage),
190
+ });
191
+ }
192
+ async raceWithIdleTimeout(fn, timeoutMs) {
193
+ const cancel = { cancelled: false };
194
+ const controller = new AbortController();
195
+ const combinedSignal = combinedAbortSignal(controller);
196
+ try {
197
+ return await Promise.race([fn(combinedSignal), abortAfterIdle(timeoutMs, cancel, controller)]);
198
+ }
199
+ finally {
200
+ cancel.cancelled = true;
201
+ }
202
+ }
203
+ async recoverFromContextLength(error, messages, options, retry) {
204
+ const reduced = this.tryReduceMessages(messages, options._contextRetryLevel || 0);
205
+ if (!reduced)
206
+ throw new ContextLengthError(error.message || error.toString());
207
+ tag('warning').log('Context length exceeded, retrying with reduced messages...');
208
+ return retry(reduced.messages, { ...options, _contextRetryLevel: reduced.nextLevel });
209
+ }
163
210
  initLangfuse() {
164
211
  const langfuseConfig = this.config.langfuse;
165
212
  const publicKey = langfuseConfig?.publicKey || process.env.LANGFUSE_PUBLIC_KEY;
@@ -190,18 +237,22 @@ export class Provider {
190
237
  return undefined;
191
238
  }
192
239
  const runTelemetry = Observability.getTelemetry();
193
- if (!options.telemetry) {
240
+ let optionTelemetry = options.telemetry;
241
+ if (options.telemetryFunctionId && !optionTelemetry?.functionId) {
242
+ optionTelemetry = { ...optionTelemetry, functionId: options.telemetryFunctionId };
243
+ }
244
+ if (!optionTelemetry) {
194
245
  return runTelemetry;
195
246
  }
196
247
  if (!runTelemetry) {
197
- return options.telemetry;
248
+ return optionTelemetry;
198
249
  }
199
250
  return {
200
251
  ...runTelemetry,
201
- ...options.telemetry,
252
+ ...optionTelemetry,
202
253
  metadata: {
203
254
  ...runTelemetry.metadata,
204
- ...options.telemetry.metadata,
255
+ ...optionTelemetry.metadata,
205
256
  },
206
257
  };
207
258
  }
@@ -227,28 +278,15 @@ export class Provider {
227
278
  this.lastConversation = conversation;
228
279
  const toolCalls = response.toolCalls || [];
229
280
  const toolResults = response.toolResults || [];
230
- const toolExecutions = toolCalls.map((call, index) => ({
231
- toolName: call.toolName || '',
232
- input: call.input,
233
- output: toolResults[index]?.output,
234
- wasSuccessful: toolResults[index]?.output?.success || false,
235
- }));
281
+ const resultsById = new Map(toolResults.map((r) => [r.toolCallId, r]));
282
+ const toolExecutions = toolCalls.map((call) => toToolExecution(call.toolName || '', call.input, resultsById.get(call.toolCallId)?.output));
236
283
  return { conversation, response, toolExecutions };
237
284
  }
238
285
  async chat(messages, model, options = {}) {
239
286
  const modelName = this.getModelName(model);
240
287
  setActivity(`🤖 Asking ${modelName}`, 'ai');
241
288
  promptLog(`Using model: ${modelName}`);
242
- const telemetry = this.getTelemetry(options);
243
- const config = this.mergeProviderOptions({
244
- maxOutputTokens: 16384,
245
- allowSystemInMessages: true,
246
- ...(this.config.config || {}),
247
- ...options,
248
- model,
249
- abortSignal: executionController.getAbortSignal(),
250
- }, options.agentName);
251
- this.finalizeConfig(config, options, telemetry);
289
+ const config = this.buildGenerateConfig({ maxOutputTokens: 16384 }, { model, abortSignal: executionController.getAbortSignal() }, options);
252
290
  promptLog(messages[messages.length - 1].content);
253
291
  try {
254
292
  const response = await withRetry(async () => {
@@ -267,14 +305,7 @@ export class Provider {
267
305
  }, this.getRetryOptions(options));
268
306
  clearActivity();
269
307
  responseLog(response.text);
270
- if (response.usage) {
271
- Stats.recordTokens(options.agentName || 'unknown', modelName, {
272
- input: response.usage.inputTokens ?? response.usage.promptTokens ?? 0,
273
- output: response.usage.outputTokens ?? response.usage.completionTokens ?? 0,
274
- total: response.usage.totalTokens ?? 0,
275
- cached: extractCachedTokens(response.usage),
276
- });
277
- }
308
+ this.recordUsage(options.agentName || 'unknown', modelName, response.usage);
278
309
  return response;
279
310
  }
280
311
  catch (error) {
@@ -284,12 +315,7 @@ export class Provider {
284
315
  if (error instanceof ContextLengthError)
285
316
  throw error;
286
317
  if (Provider.isContextLengthError(error)) {
287
- const reduced = this.tryReduceMessages(messages, options._contextRetryLevel || 0);
288
- if (reduced) {
289
- tag('warning').log('Context length exceeded, retrying with reduced messages...');
290
- return this.chat(reduced.messages, model, { ...options, _contextRetryLevel: reduced.nextLevel });
291
- }
292
- throw new ContextLengthError(error.message || error.toString());
318
+ return this.recoverFromContextLength(error, messages, options, (m, o) => this.chat(m, model, o));
293
319
  }
294
320
  const message = error.message || error.toString();
295
321
  if (message !== 'No response text from AI') {
@@ -306,46 +332,20 @@ export class Provider {
306
332
  tag('debug').log(`Tools enabled: [${toolNames.join(', ')}]`);
307
333
  promptLog('Available tools:', toolNames);
308
334
  promptLog(messages[messages.length - 1].content);
309
- const telemetry = this.getTelemetry(options);
310
335
  const maxRoundtrips = options.maxToolRoundtrips ?? 5;
311
336
  const extraStop = options.stopWhen;
312
337
  const stopConditions = [isStepCount(maxRoundtrips)];
313
338
  if (extraStop)
314
339
  stopConditions.push(extraStop);
315
- const { stopWhen: _ignoredStopWhen, ...optionsWithoutStop } = options;
316
- const config = this.mergeProviderOptions({
317
- tools,
318
- maxOutputTokens: 16384,
319
- toolChoice: 'auto',
320
- allowSystemInMessages: true,
321
- ...(this.config.config || {}),
322
- ...optionsWithoutStop,
323
- stopWhen: stopConditions,
324
- model,
325
- abortSignal: executionController.getAbortSignal(),
326
- }, options.agentName);
327
- this.finalizeConfig(config, options, telemetry);
340
+ const config = this.buildGenerateConfig({ tools, maxOutputTokens: 16384, toolChoice: 'auto' }, { stopWhen: stopConditions, model }, options);
328
341
  try {
329
342
  const response = await withRetry(async () => {
330
- const timeout = config.timeout || 30000;
331
- const cancel = { cancelled: false };
332
- try {
333
- const result = (await Promise.race([
334
- generateText({
335
- messages,
336
- ...config,
337
- }),
338
- rejectAfterIdle(timeout, cancel),
339
- ]));
340
- const hasToolCall = (result.toolCalls?.length || 0) > 0;
341
- if (!result.text && !hasToolCall && result.finishReason === 'length') {
342
- throw new ContextLengthError('AI response empty: output truncated at maxTokens. Increase maxOutputTokens in config or use a model with higher output capacity.');
343
- }
344
- return result;
345
- }
346
- finally {
347
- cancel.cancelled = true;
343
+ const result = (await this.raceWithIdleTimeout((signal) => generateText({ messages, ...config, abortSignal: signal }), config.timeout || 30000));
344
+ const hasToolCall = (result.toolCalls?.length || 0) > 0;
345
+ if (!result.text && !hasToolCall && result.finishReason === 'length') {
346
+ throw new ContextLengthError('AI response empty: output truncated at maxTokens. Increase maxOutputTokens in config or use a model with higher output capacity.');
348
347
  }
348
+ return result;
349
349
  }, this.getRetryOptions(options));
350
350
  clearActivity();
351
351
  // Log tool usage summary
@@ -356,14 +356,7 @@ export class Provider {
356
356
  });
357
357
  }
358
358
  responseLog(response.text);
359
- if (response.usage) {
360
- Stats.recordTokens(options.agentName || 'unknown', modelName, {
361
- input: response.usage.inputTokens ?? response.usage.promptTokens ?? 0,
362
- output: response.usage.outputTokens ?? response.usage.completionTokens ?? 0,
363
- total: response.usage.totalTokens ?? 0,
364
- cached: extractCachedTokens(response.usage),
365
- });
366
- }
359
+ this.recordUsage(options.agentName || 'unknown', modelName, response.usage);
367
360
  return response;
368
361
  }
369
362
  catch (error) {
@@ -376,12 +369,7 @@ export class Provider {
376
369
  if (error instanceof ContextLengthError)
377
370
  throw error;
378
371
  if (Provider.isContextLengthError(error)) {
379
- const reduced = this.tryReduceMessages(messages, options._contextRetryLevel || 0);
380
- if (reduced) {
381
- tag('warning').log('Context length exceeded, retrying with reduced messages...');
382
- return this.generateWithTools(reduced.messages, model, tools, { ...options, _contextRetryLevel: reduced.nextLevel });
383
- }
384
- throw new ContextLengthError(error.message || error.toString());
372
+ return this.recoverFromContextLength(error, messages, options, (m, o) => this.generateWithTools(m, model, tools, o));
385
373
  }
386
374
  if (error.constructor?.name === 'AI_APICallError') {
387
375
  responseLog(error.message);
@@ -395,44 +383,15 @@ export class Provider {
395
383
  const modelName = this.getModelName(modelToUse);
396
384
  setActivity(`🤖 Asking ${modelName} for structured output`, 'ai');
397
385
  promptLog(`Using model: ${modelName}`);
398
- const telemetry = this.getTelemetry(options);
399
- const config = this.mergeProviderOptions({
400
- schema,
401
- allowSystemInMessages: true,
402
- ...(this.config.config || {}),
403
- ...options,
404
- model: modelToUse,
405
- abortSignal: executionController.getAbortSignal(),
406
- }, options.agentName);
407
- this.finalizeConfig(config, options, telemetry);
386
+ const config = this.buildGenerateConfig({ schema }, { model: modelToUse }, options);
408
387
  try {
409
388
  promptLog(messages[messages.length - 1].content);
410
389
  const response = await withRetry(async () => {
411
- const timeout = config.timeout || 30000;
412
- const cancel = { cancelled: false };
413
- try {
414
- return (await Promise.race([
415
- generateObject({
416
- messages,
417
- ...config,
418
- }),
419
- rejectAfterIdle(timeout, cancel),
420
- ]));
421
- }
422
- finally {
423
- cancel.cancelled = true;
424
- }
390
+ return (await this.raceWithIdleTimeout((signal) => generateObject({ messages, ...config, abortSignal: signal }), config.timeout || 30000));
425
391
  }, this.getRetryOptions(options));
426
392
  clearActivity();
427
393
  responseLog(response.object);
428
- if (response.usage) {
429
- Stats.recordTokens(options.agentName || 'unknown', modelName, {
430
- input: response.usage.inputTokens ?? response.usage.promptTokens ?? 0,
431
- output: response.usage.outputTokens ?? response.usage.completionTokens ?? 0,
432
- total: response.usage.totalTokens ?? 0,
433
- cached: extractCachedTokens(response.usage),
434
- });
435
- }
394
+ this.recordUsage(options.agentName || 'unknown', modelName, response.usage);
436
395
  return response;
437
396
  }
438
397
  catch (error) {
@@ -442,19 +401,14 @@ export class Provider {
442
401
  if (error instanceof ContextLengthError)
443
402
  throw error;
444
403
  if (Provider.isContextLengthError(error)) {
445
- const reduced = this.tryReduceMessages(messages, options._contextRetryLevel || 0);
446
- if (reduced) {
447
- tag('warning').log('Context length exceeded, retrying with reduced messages...');
448
- return this.generateObject(reduced.messages, schema, model, { ...options, _contextRetryLevel: reduced.nextLevel });
449
- }
450
- throw new ContextLengthError(error.message || error.toString());
404
+ return this.recoverFromContextLength(error, messages, options, (m, o) => this.generateObject(m, schema, model, o));
451
405
  }
452
406
  throw new AiError(error.message || error.toString());
453
407
  }
454
408
  }
455
409
  static isContextLengthError(error) {
456
410
  const msg = (error?.message || error?.toString() || '').toLowerCase();
457
- return Provider.CONTEXT_LENGTH_PATTERNS.some((p) => msg.includes(p));
411
+ return CONTEXT_LENGTH_PATTERNS.some((p) => msg.includes(p));
458
412
  }
459
413
  static trimMessagesForRetry(messages) {
460
414
  const tagRegex = /<(\w[\w-]*)>([\s\S]*?)<\/\1>/g;
@@ -604,13 +558,7 @@ export class Provider {
604
558
  }, this.getRetryOptions());
605
559
  clearActivity();
606
560
  responseLog(response.text);
607
- if (response.usage) {
608
- Stats.recordTokens('vision', this.getModelName(this.config.visionModel), {
609
- input: response.usage.inputTokens ?? response.usage.promptTokens ?? 0,
610
- output: response.usage.outputTokens ?? response.usage.completionTokens ?? 0,
611
- total: response.usage.totalTokens ?? 0,
612
- });
613
- }
561
+ this.recordUsage('vision', this.getModelName(this.config.visionModel), response.usage);
614
562
  return response;
615
563
  }
616
564
  catch (error) {
@@ -0,0 +1,56 @@
1
+ import type { ActionResult } from '../action-result.js';
2
+ import type { StateManager, StateTransition, WebPageState } from '../state-manager.js';
3
+ import type { Task } from '../test-plan.js';
4
+ import type { Conversation, ToolExecution } from './conversation.js';
5
+ import type { Provider } from './provider.js';
6
+ export declare class Quartermaster {
7
+ provider: Provider;
8
+ model?: any;
9
+ outputDir: string;
10
+ pageAnalyses: Map<string, PageAnalysis>;
11
+ unsubscribe: (() => void) | null;
12
+ playwrightHelper: any;
13
+ pendingAnalyses: Promise<void>[];
14
+ constructor(provider: Provider, options?: {
15
+ model?: any;
16
+ });
17
+ start(playwrightHelper: any, stateManager: StateManager): void;
18
+ stop(): void;
19
+ onPageChange(event: StateTransition): void;
20
+ runAxeAnalysis(state: WebPageState): Promise<void>;
21
+ analyzeSession(task: Task, initialState: ActionResult, conversation: Conversation): Promise<AnalysisReport | null>;
22
+ generateSemanticAnalysis(axeViolations: AxeViolation[], executions: ToolExecution[], initialState: ActionResult): Promise<SemanticIssue[]>;
23
+ addNotesToTask(task: Task, report: AnalysisReport): void;
24
+ saveReport(stateHash: string, report: AnalysisReport): void;
25
+ formatReportMarkdown(report: AnalysisReport): string;
26
+ }
27
+ interface AxeViolation {
28
+ id: string;
29
+ impact: 'critical' | 'serious' | 'moderate' | 'minor';
30
+ description: string;
31
+ helpUrl: string;
32
+ nodes: Array<{
33
+ html: string;
34
+ target: string[];
35
+ }>;
36
+ }
37
+ interface PageAnalysis {
38
+ url: string;
39
+ stateHash: string;
40
+ timestamp: string;
41
+ axeViolations: AxeViolation[];
42
+ }
43
+ interface SemanticIssue {
44
+ type: 'unclear_intention' | 'confusing_naming' | 'hard_to_interact';
45
+ element: string;
46
+ issue: string;
47
+ suggestion: string;
48
+ }
49
+ interface AnalysisReport {
50
+ scenario: string;
51
+ timestamp: string;
52
+ url: string;
53
+ axeViolations: AxeViolation[];
54
+ semanticIssues: SemanticIssue[];
55
+ }
56
+ export {};