explorbot 0.1.13 → 0.1.16

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 (42) hide show
  1. package/dist/package.json +3 -2
  2. package/dist/src/action.js +3 -2
  3. package/dist/src/ai/conversation.js +20 -4
  4. package/dist/src/ai/historian/utils.js +8 -1
  5. package/dist/src/ai/pilot.js +198 -260
  6. package/dist/src/ai/provider.js +25 -12
  7. package/dist/src/ai/quartermaster.js +2 -2
  8. package/dist/src/ai/researcher/focus.js +51 -10
  9. package/dist/src/ai/researcher/sections.js +8 -4
  10. package/dist/src/ai/researcher.js +9 -24
  11. package/dist/src/ai/rules.js +2 -0
  12. package/dist/src/ai/session-analyst.js +46 -41
  13. package/dist/src/ai/tester.js +63 -22
  14. package/dist/src/ai/tools.js +19 -4
  15. package/dist/src/commands/explore-command.js +8 -2
  16. package/dist/src/components/StatusPane.js +6 -1
  17. package/dist/src/experience-tracker.js +9 -0
  18. package/dist/src/explorer.js +2 -5
  19. package/dist/src/reporter.js +41 -1
  20. package/dist/src/stats.js +2 -1
  21. package/dist/src/test-plan.js +47 -3
  22. package/package.json +3 -2
  23. package/src/action.ts +3 -2
  24. package/src/ai/conversation.ts +21 -4
  25. package/src/ai/historian/utils.ts +8 -1
  26. package/src/ai/pilot.ts +199 -259
  27. package/src/ai/provider.ts +24 -12
  28. package/src/ai/quartermaster.ts +2 -2
  29. package/src/ai/researcher/focus.ts +57 -8
  30. package/src/ai/researcher/sections.ts +7 -3
  31. package/src/ai/researcher.ts +8 -23
  32. package/src/ai/rules.ts +2 -0
  33. package/src/ai/session-analyst.ts +47 -41
  34. package/src/ai/tester.ts +55 -20
  35. package/src/ai/tools.ts +18 -4
  36. package/src/commands/explore-command.ts +9 -2
  37. package/src/components/StatusPane.tsx +6 -3
  38. package/src/experience-tracker.ts +9 -0
  39. package/src/explorer.ts +1 -4
  40. package/src/reporter.ts +44 -1
  41. package/src/stats.ts +3 -1
  42. package/src/test-plan.ts +62 -3
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "explorbot",
3
- "version": "0.1.13",
3
+ "version": "0.1.16",
4
4
  "description": "CLI app built with React Ink, CodeceptJS, and Playwright",
5
5
  "license": "Elastic-2.0",
6
6
  "type": "module",
@@ -67,6 +67,7 @@
67
67
  "@ai-sdk/openai": "^3.0",
68
68
  "@axe-core/playwright": "^4.11.0",
69
69
  "@codeceptjs/reflection": "^0.5.2",
70
+ "@faker-js/faker": "^10.4.0",
70
71
  "@inkjs/ui": "^2.0.0",
71
72
  "@langfuse/otel": "^4.5.1",
72
73
  "@openrouter/ai-sdk-provider": "^2.3.3",
@@ -78,7 +79,7 @@
78
79
  "@opentelemetry/sdk-trace-base": "^2.2.0",
79
80
  "@opentelemetry/semantic-conventions": "^1.38.0",
80
81
  "@scalar/openapi-parser": "^0.25.6",
81
- "@testomatio/reporter": "^2.7.9-beta.2-markdown",
82
+ "@testomatio/reporter": "^2.7.9-beta.3-markdown",
82
83
  "ai": "^6.0.6",
83
84
  "axe-core": "^4.11.1",
84
85
  "bash-tool": "^1.3.15",
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import { join } from 'node:path';
3
+ import { faker } from '@faker-js/faker';
3
4
  import { context, trace } from '@opentelemetry/api';
4
5
  import { highlight } from 'cli-highlight';
5
6
  import { container, recorder } from 'codeceptjs';
@@ -218,8 +219,8 @@ class Action {
218
219
  await sleep(this.config.action?.delay || 500);
219
220
  }
220
221
  else {
221
- const codeFunction = new Function('I', 'tryTo', 'retryTo', 'within', 'hopeThat', 'step', sanitizedCode);
222
- codeFunction(this.actor, tryTo, retryTo, within, hopeThat, step);
222
+ const codeFunction = new Function('I', 'tryTo', 'retryTo', 'within', 'hopeThat', 'step', 'faker', sanitizedCode);
223
+ codeFunction(this.actor, tryTo, retryTo, within, hopeThat, step, faker);
223
224
  await recorder.add(() => sleep(this.config.action?.delay || 500));
224
225
  await recorder.promise();
225
226
  }
@@ -8,6 +8,7 @@ export class Conversation {
8
8
  messages;
9
9
  model;
10
10
  telemetryFunctionId;
11
+ protectedPrefixCount = 0;
11
12
  autoTrimRules;
12
13
  constructor(messages = [], model, telemetryFunctionId) {
13
14
  this.id = this.generateId();
@@ -16,6 +17,9 @@ export class Conversation {
16
17
  this.telemetryFunctionId = telemetryFunctionId;
17
18
  this.autoTrimRules = new Map();
18
19
  }
20
+ protectPrefix(count) {
21
+ this.protectedPrefixCount = count;
22
+ }
19
23
  addUserText(text) {
20
24
  this.messages.push({
21
25
  role: 'user',
@@ -63,8 +67,10 @@ export class Conversation {
63
67
  const escapedTag = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
64
68
  const regex = new RegExp(`<${escapedTag}>[\\s\\S]*?<\\/${escapedTag}>`, 'g');
65
69
  const replacementText = `<${tagName}>${replacement}</${tagName}>`;
70
+ const start = this.protectedPrefixCount;
66
71
  if (keepLast === 0) {
67
- for (const message of this.messages) {
72
+ for (let i = start; i < this.messages.length; i++) {
73
+ const message = this.messages[i];
68
74
  if (typeof message.content === 'string') {
69
75
  message.content = message.content.replace(regex, replacementText);
70
76
  }
@@ -72,7 +78,7 @@ export class Conversation {
72
78
  return;
73
79
  }
74
80
  const allMatches = [];
75
- for (let i = 0; i < this.messages.length; i++) {
81
+ for (let i = start; i < this.messages.length; i++) {
76
82
  const message = this.messages[i];
77
83
  if (typeof message.content === 'string') {
78
84
  const matches = [...message.content.matchAll(new RegExp(`<${escapedTag}>[\\s\\S]*?<\\/${escapedTag}>`, 'g'))];
@@ -86,7 +92,7 @@ export class Conversation {
86
92
  const keepCount = Math.min(keepLast, allMatches.length);
87
93
  const keepMatches = allMatches.slice(-keepCount);
88
94
  const keepSet = new Set(keepMatches.map((m) => `${m.messageIndex}:${m.startIndex}`));
89
- for (let i = 0; i < this.messages.length; i++) {
95
+ for (let i = start; i < this.messages.length; i++) {
90
96
  const message = this.messages[i];
91
97
  if (typeof message.content === 'string') {
92
98
  const matches = [...message.content.matchAll(new RegExp(`<${escapedTag}>[\\s\\S]*?<\\/${escapedTag}>`, 'g'))];
@@ -109,7 +115,7 @@ export class Conversation {
109
115
  }
110
116
  compactToolResults(keepLastN) {
111
117
  const toolMessageIndexes = [];
112
- for (let i = 0; i < this.messages.length; i++) {
118
+ for (let i = this.protectedPrefixCount; i < this.messages.length; i++) {
113
119
  if (this.messages[i].role === 'tool')
114
120
  toolMessageIndexes.push(i);
115
121
  }
@@ -144,6 +150,16 @@ export class Conversation {
144
150
  }
145
151
  }
146
152
  }
153
+ markLastMessageCacheable() {
154
+ const last = this.messages[this.messages.length - 1];
155
+ if (!last)
156
+ return;
157
+ last.providerOptions = {
158
+ ...last.providerOptions,
159
+ anthropic: { cacheControl: { type: 'ephemeral' } },
160
+ bedrock: { cachePoint: { type: 'default' } },
161
+ };
162
+ }
147
163
  hasTag(tagName, lastN) {
148
164
  const escapedTag = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
149
165
  const regex = new RegExp(`<${escapedTag}>`, 'g');
@@ -1,5 +1,12 @@
1
+ import { isDynamicId } from "../../utils/xpath.js";
1
2
  export function isNonReusableCode(code) {
2
- return /\bI\.clickXY\s*\(/.test(code);
3
+ if (/\bI\.clickXY\s*\(/.test(code))
4
+ return true;
5
+ for (const m of code.matchAll(/#([A-Za-z_][\w-]*)/g)) {
6
+ if (isDynamicId(m[1]))
7
+ return true;
8
+ }
9
+ return false;
3
10
  }
4
11
  export function escapeString(str) {
5
12
  return str.replace(/'/g, "\\'").replace(/\n/g, ' ');