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.
- package/README.md +14 -14
- package/bin/explorbot-cli.ts +4 -3
- package/boat/doc-collector/src/ai/documentarian.ts +22 -27
- package/boat/doc-collector/src/ai/tools.ts +67 -10
- package/boat/doc-collector/src/docbot.ts +28 -7
- package/boat/doc-collector/src/docs-renderer.ts +19 -24
- package/boat/doc-collector/src/screenshots.ts +33 -1
- package/boat/doc-collector/src/state-diagram.ts +281 -0
- package/dist/bin/explorbot-cli.js +3 -3
- package/dist/boat/doc-collector/src/ai/documentarian.js +16 -6
- package/dist/boat/doc-collector/src/ai/tools.js +47 -10
- package/dist/boat/doc-collector/src/docbot.js +25 -6
- package/dist/boat/doc-collector/src/docs-renderer.js +18 -3
- package/dist/boat/doc-collector/src/screenshots.js +29 -0
- package/dist/boat/doc-collector/src/state-diagram.js +200 -0
- package/dist/package.json +20 -3
- package/dist/src/action-result.d.ts +8 -10
- package/dist/src/action-result.js +72 -164
- package/dist/src/action.d.ts +3 -10
- package/dist/src/action.js +18 -111
- package/dist/src/ai/captain/web-mode.js +0 -7
- package/dist/src/ai/captain.d.ts +1 -3
- package/dist/src/ai/captain.js +4 -19
- package/dist/src/ai/conversation.d.ts +2 -5
- package/dist/src/ai/conversation.js +9 -28
- package/dist/src/ai/driller.d.ts +0 -4
- package/dist/src/ai/driller.js +3 -12
- package/dist/src/ai/experience-compactor.d.ts +2 -2
- package/dist/src/ai/experience-compactor.js +16 -33
- package/dist/src/ai/fisherman-tools.js +1 -1
- package/dist/src/ai/fisherman.d.ts +0 -1
- package/dist/src/ai/fisherman.js +0 -3
- package/dist/src/ai/historian/codeceptjs.d.ts +2 -0
- package/dist/src/ai/historian/codeceptjs.js +3 -2
- package/dist/src/ai/historian/playwright.d.ts +2 -0
- package/dist/src/ai/historian/playwright.js +3 -2
- package/dist/src/ai/historian/utils.d.ts +0 -1
- package/dist/src/ai/historian/utils.js +0 -1
- package/dist/src/ai/historian.d.ts +5 -4
- package/dist/src/ai/historian.js +1 -5
- package/dist/src/ai/navigator.d.ts +3 -5
- package/dist/src/ai/navigator.js +11 -46
- package/dist/src/ai/pilot.d.ts +1 -3
- package/dist/src/ai/pilot.js +6 -17
- package/dist/src/ai/planner.d.ts +3 -3
- package/dist/src/ai/planner.js +49 -37
- package/dist/src/ai/provider.d.ts +4 -1
- package/dist/src/ai/provider.js +77 -136
- package/dist/src/ai/quartermaster.d.ts +21 -22
- package/dist/src/ai/quartermaster.js +4 -10
- package/dist/src/ai/rerunner.d.ts +1 -2
- package/dist/src/ai/rerunner.js +14 -22
- package/dist/src/ai/researcher/cache.js +11 -18
- package/dist/src/ai/researcher/deep-analysis.js +3 -6
- package/dist/src/ai/researcher/parser.js +1 -1
- package/dist/src/ai/researcher/research-result.d.ts +0 -1
- package/dist/src/ai/researcher/research-result.js +0 -19
- package/dist/src/ai/researcher/sections.js +5 -1
- package/dist/src/ai/researcher.js +12 -53
- package/dist/src/ai/rules.d.ts +1 -0
- package/dist/src/ai/rules.js +22 -15
- package/dist/src/ai/session-analyst.js +8 -5
- package/dist/src/ai/task-agent.d.ts +1 -1
- package/dist/src/ai/task-agent.js +2 -27
- package/dist/src/ai/tester.d.ts +0 -2
- package/dist/src/ai/tester.js +10 -61
- package/dist/src/ai/tools.d.ts +18 -7
- package/dist/src/ai/tools.js +119 -101
- package/dist/src/commands/compact-command.d.ts +1 -1
- package/dist/src/commands/compact-command.js +6 -6
- package/dist/src/commands/context-command.js +1 -1
- package/dist/src/commands/context-knowledge-command.js +1 -1
- package/dist/src/commands/experience-command.js +1 -1
- package/dist/src/commands/knows-command.js +1 -1
- package/dist/src/commands/learn-command.js +3 -1
- package/dist/src/components/AddKnowledge.d.ts +2 -0
- package/dist/src/components/AddKnowledge.js +3 -7
- package/dist/src/components/App.js +2 -2
- package/dist/src/config.d.ts +2 -0
- package/dist/src/config.js +12 -0
- package/dist/src/experience-tracker.d.ts +11 -6
- package/dist/src/experience-tracker.js +81 -154
- package/dist/src/explorbot.d.ts +4 -5
- package/dist/src/explorbot.js +17 -51
- package/dist/src/explorer.d.ts +4 -15
- package/dist/src/explorer.js +18 -68
- package/dist/src/knowledge-tracker.d.ts +2 -2
- package/dist/src/knowledge-tracker.js +38 -45
- package/dist/src/state-manager.d.ts +6 -31
- package/dist/src/state-manager.js +10 -129
- package/dist/src/utils/aria.d.ts +6 -1
- package/dist/src/utils/aria.js +4 -1
- package/dist/src/utils/cache.d.ts +15 -0
- package/dist/src/utils/cache.js +34 -0
- package/dist/src/utils/context-formatter.d.ts +6 -0
- package/dist/src/utils/context-formatter.js +15 -27
- package/dist/src/utils/html.d.ts +5 -0
- package/dist/src/utils/html.js +71 -0
- package/dist/src/utils/markdown-files.d.ts +10 -0
- package/dist/src/utils/markdown-files.js +21 -0
- package/dist/src/utils/markdown-query.d.ts +6 -0
- package/dist/src/utils/markdown-query.js +14 -1
- package/dist/src/utils/page-readiness.d.ts +1 -0
- package/dist/src/utils/page-readiness.js +1 -1
- package/dist/src/utils/secrets.js +2 -2
- package/dist/src/utils/strings.d.ts +2 -0
- package/dist/src/utils/strings.js +10 -0
- package/dist/src/utils/web-element.d.ts +2 -1
- package/dist/src/utils/web-element.js +3 -0
- package/dist/src/utils/web-sandbox.d.ts +4 -0
- package/dist/src/utils/web-sandbox.js +37 -0
- package/package.json +20 -3
- package/src/action-result.ts +70 -173
- package/src/action.ts +16 -130
- package/src/ai/captain/web-mode.ts +0 -7
- package/src/ai/captain.ts +5 -17
- package/src/ai/conversation.ts +9 -32
- package/src/ai/driller.ts +3 -16
- package/src/ai/experience-compactor.ts +16 -35
- package/src/ai/fisherman-tools.ts +1 -1
- package/src/ai/fisherman.ts +0 -4
- package/src/ai/historian/codeceptjs.ts +4 -2
- package/src/ai/historian/playwright.ts +4 -2
- package/src/ai/historian/utils.ts +0 -1
- package/src/ai/historian.ts +4 -7
- package/src/ai/navigator.ts +13 -49
- package/src/ai/pilot.ts +6 -16
- package/src/ai/planner.ts +56 -44
- package/src/ai/provider.ts +85 -145
- package/src/ai/quartermaster.ts +34 -41
- package/src/ai/rerunner.ts +17 -21
- package/src/ai/researcher/cache.ts +10 -16
- package/src/ai/researcher/deep-analysis.ts +3 -6
- package/src/ai/researcher/parser.ts +1 -1
- package/src/ai/researcher/research-result.ts +0 -18
- package/src/ai/researcher/sections.ts +5 -1
- package/src/ai/researcher.ts +11 -59
- package/src/ai/rules.ts +24 -13
- package/src/ai/session-analyst.ts +8 -5
- package/src/ai/task-agent.ts +3 -30
- package/src/ai/tester.ts +12 -73
- package/src/ai/tools.ts +119 -106
- package/src/commands/compact-command.ts +7 -8
- package/src/commands/context-command.ts +1 -1
- package/src/commands/context-knowledge-command.ts +1 -1
- package/src/commands/experience-command.ts +1 -1
- package/src/commands/knows-command.ts +1 -1
- package/src/commands/learn-command.ts +3 -1
- package/src/components/AddKnowledge.tsx +5 -7
- package/src/components/App.tsx +2 -2
- package/src/config.ts +12 -0
- package/src/experience-tracker.ts +88 -151
- package/src/explorbot.ts +17 -51
- package/src/explorer.ts +20 -72
- package/src/knowledge-tracker.ts +42 -49
- package/src/state-manager.ts +15 -156
- package/src/utils/aria.ts +11 -2
- package/src/utils/cache.ts +40 -0
- package/src/utils/context-formatter.ts +12 -15
- package/src/utils/html.ts +79 -0
- package/src/utils/markdown-files.ts +30 -0
- package/src/utils/markdown-query.ts +15 -1
- package/src/utils/page-readiness.ts +1 -1
- package/src/utils/secrets.ts +2 -3
- package/src/utils/strings.ts +12 -0
- package/src/utils/web-element.ts +4 -0
- package/src/utils/web-sandbox.ts +43 -0
- package/dist/boat/api-tester/example/apibot.config.js +0 -30
package/dist/src/config.d.ts
CHANGED
|
@@ -230,6 +230,8 @@ export declare class ConfigParser {
|
|
|
230
230
|
getConfig(): ExplorbotConfig;
|
|
231
231
|
getConfigPath(): string | null;
|
|
232
232
|
getOutputDir(): string;
|
|
233
|
+
getProjectRoot(): string;
|
|
234
|
+
resolveProjectDir(relativeDir: string): string;
|
|
233
235
|
getStatesDir(): string;
|
|
234
236
|
getPlansDir(): string;
|
|
235
237
|
getTestsDir(): string;
|
package/dist/src/config.js
CHANGED
|
@@ -96,6 +96,18 @@ export class ConfigParser {
|
|
|
96
96
|
throw new Error('Config path not found');
|
|
97
97
|
return path.join(path.dirname(configPath), config.dirs?.output || 'output');
|
|
98
98
|
}
|
|
99
|
+
getProjectRoot() {
|
|
100
|
+
const configPath = this.getConfigPath();
|
|
101
|
+
if (configPath)
|
|
102
|
+
return path.dirname(configPath);
|
|
103
|
+
return process.cwd();
|
|
104
|
+
}
|
|
105
|
+
resolveProjectDir(relativeDir) {
|
|
106
|
+
const configPath = this.getConfigPath();
|
|
107
|
+
if (!configPath)
|
|
108
|
+
return relativeDir;
|
|
109
|
+
return path.join(path.dirname(configPath), relativeDir);
|
|
110
|
+
}
|
|
99
111
|
getStatesDir() {
|
|
100
112
|
return outputPath('states');
|
|
101
113
|
}
|
|
@@ -22,7 +22,7 @@ export declare class ExperienceTracker {
|
|
|
22
22
|
experienceDir: string;
|
|
23
23
|
disabled: boolean;
|
|
24
24
|
knowledgeTracker: KnowledgeTracker;
|
|
25
|
-
constructor(options?: {
|
|
25
|
+
constructor(knowledgeTracker: KnowledgeTracker, options?: {
|
|
26
26
|
disabled?: boolean;
|
|
27
27
|
});
|
|
28
28
|
getExperienceDirectories(): string[];
|
|
@@ -31,8 +31,11 @@ export declare class ExperienceTracker {
|
|
|
31
31
|
content: string;
|
|
32
32
|
data: any;
|
|
33
33
|
};
|
|
34
|
+
readExperienceFileAt(filePath: string): {
|
|
35
|
+
content: string;
|
|
36
|
+
data: any;
|
|
37
|
+
};
|
|
34
38
|
writeExperienceFile(stateHash: string, content: string, frontmatter?: any): void;
|
|
35
|
-
hasRecentExperience(stateHash: string, prefix?: string): boolean;
|
|
36
39
|
getExperienceFilePath(stateHash: string): string;
|
|
37
40
|
ensureExperienceFile(state: ActionResult): string;
|
|
38
41
|
updateSummary(state: ActionResult, summary: string): void;
|
|
@@ -43,10 +46,6 @@ export declare class ExperienceTracker {
|
|
|
43
46
|
getRelevantExperience(state: ActionResult, options?: {
|
|
44
47
|
includeDescendantExperience?: boolean;
|
|
45
48
|
}): ExperienceFile[];
|
|
46
|
-
/**
|
|
47
|
-
* Clean up experience tracker (for testing)
|
|
48
|
-
*/
|
|
49
|
-
cleanup(): void;
|
|
50
49
|
getSuccessfulExperience(state: ActionResult, options?: {
|
|
51
50
|
includeDescendants?: boolean;
|
|
52
51
|
stripCode?: boolean;
|
|
@@ -54,6 +53,7 @@ export declare class ExperienceTracker {
|
|
|
54
53
|
getExperienceTableOfContents(state: ActionResult, options?: {
|
|
55
54
|
includeDescendantExperience?: boolean;
|
|
56
55
|
}): ExperienceTocEntry[];
|
|
56
|
+
renderExperienceTocFor(state: ActionResult): string;
|
|
57
57
|
getExperienceSection(fileTag: string, sectionIndex: number, state: ActionResult, options?: {
|
|
58
58
|
includeDescendantExperience?: boolean;
|
|
59
59
|
}): {
|
|
@@ -61,6 +61,11 @@ export declare class ExperienceTracker {
|
|
|
61
61
|
url: string;
|
|
62
62
|
content: string;
|
|
63
63
|
} | null;
|
|
64
|
+
buildToc(records: ExperienceFile[]): ExperienceTocEntry[];
|
|
65
|
+
readSectionByHash(fileHash: string, sectionIndex: number): {
|
|
66
|
+
title: string;
|
|
67
|
+
body: string;
|
|
68
|
+
} | null;
|
|
64
69
|
findExperienceFileByHash(fileHash: string): string | null;
|
|
65
70
|
listAllExperienceToc(filter?: string, options?: {
|
|
66
71
|
recency?: 'recent' | 'old';
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync,
|
|
2
|
-
import { basename,
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { basename, join } from 'node:path';
|
|
3
3
|
import matter from 'gray-matter';
|
|
4
|
-
import { marked } from 'marked';
|
|
5
4
|
import { ConfigParser } from './config.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { createDebug, pluralize, tag } from './utils/logger.js';
|
|
6
|
+
import { loadMarkdownFiles } from './utils/markdown-files.js';
|
|
8
7
|
import { mdq } from './utils/markdown-query.js';
|
|
9
8
|
import { redactSecrets } from './utils/secrets.js';
|
|
10
9
|
import { isNonReusableCode } from "./utils/step-analyzer.js";
|
|
@@ -33,20 +32,13 @@ export class ExperienceTracker {
|
|
|
33
32
|
experienceDir;
|
|
34
33
|
disabled;
|
|
35
34
|
knowledgeTracker;
|
|
36
|
-
constructor(options = {}) {
|
|
35
|
+
constructor(knowledgeTracker, options = {}) {
|
|
37
36
|
const configParser = ConfigParser.getInstance();
|
|
38
37
|
const config = configParser.getConfig();
|
|
39
|
-
const configPath = configParser.getConfigPath();
|
|
40
38
|
this.disabled = options.disabled ?? false;
|
|
41
|
-
this.knowledgeTracker =
|
|
39
|
+
this.knowledgeTracker = knowledgeTracker;
|
|
42
40
|
// Resolve experience directory relative to the config file location (project root)
|
|
43
|
-
|
|
44
|
-
const projectRoot = dirname(configPath);
|
|
45
|
-
this.experienceDir = join(projectRoot, config.dirs?.experience || 'experience');
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
this.experienceDir = config.dirs?.experience || 'experience';
|
|
49
|
-
}
|
|
41
|
+
this.experienceDir = configParser.resolveProjectDir(config.dirs?.experience || 'experience');
|
|
50
42
|
if (!this.disabled) {
|
|
51
43
|
this.ensureDirectory(this.experienceDir);
|
|
52
44
|
}
|
|
@@ -84,7 +76,9 @@ export class ExperienceTracker {
|
|
|
84
76
|
}
|
|
85
77
|
}
|
|
86
78
|
readExperienceFile(stateHash) {
|
|
87
|
-
|
|
79
|
+
return this.readExperienceFileAt(this.getExperienceFilePath(stateHash));
|
|
80
|
+
}
|
|
81
|
+
readExperienceFileAt(filePath) {
|
|
88
82
|
const fileContent = readFileSync(filePath, 'utf8');
|
|
89
83
|
const { content, data } = matter(fileContent);
|
|
90
84
|
return { content, data };
|
|
@@ -97,20 +91,6 @@ export class ExperienceTracker {
|
|
|
97
91
|
const fileContent = matter.stringify(redactSecrets(content), frontmatter || {});
|
|
98
92
|
writeFileSync(filePath, fileContent, 'utf8');
|
|
99
93
|
}
|
|
100
|
-
hasRecentExperience(stateHash, prefix = '') {
|
|
101
|
-
if (this.disabled) {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
if (prefix) {
|
|
105
|
-
stateHash = `${prefix}_${stateHash}`;
|
|
106
|
-
}
|
|
107
|
-
const filePath = this.getExperienceFilePath(stateHash);
|
|
108
|
-
if (!existsSync(filePath)) {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
const stats = statSync(filePath);
|
|
112
|
-
return stats.mtime.getTime() > Date.now() - 1000 * 60 * 60 * 24;
|
|
113
|
-
}
|
|
114
94
|
getExperienceFilePath(stateHash) {
|
|
115
95
|
return join(this.experienceDir, `${stateHash}.md`);
|
|
116
96
|
}
|
|
@@ -154,7 +134,10 @@ export class ExperienceTracker {
|
|
|
154
134
|
this.ensureExperienceFile(state);
|
|
155
135
|
const stateHash = state.getStateHash();
|
|
156
136
|
const { content, data } = this.readExperienceFile(stateHash);
|
|
157
|
-
if (content
|
|
137
|
+
if (mdq(content)
|
|
138
|
+
.query('code')
|
|
139
|
+
.meta()
|
|
140
|
+
.some((m) => m.text.trim() === action.code.trim())) {
|
|
158
141
|
debugLog('Skipping duplicate action', action.code);
|
|
159
142
|
return;
|
|
160
143
|
}
|
|
@@ -179,7 +162,10 @@ export class ExperienceTracker {
|
|
|
179
162
|
this.ensureExperienceFile(state);
|
|
180
163
|
const stateHash = state.getStateHash();
|
|
181
164
|
const { content, data } = this.readExperienceFile(stateHash);
|
|
182
|
-
if (content
|
|
165
|
+
if (mdq(content)
|
|
166
|
+
.query('section2')
|
|
167
|
+
.each()
|
|
168
|
+
.some((s) => s.text().trim() === body.trim())) {
|
|
183
169
|
debugLog('Skipping duplicate flow body');
|
|
184
170
|
return;
|
|
185
171
|
}
|
|
@@ -200,25 +186,9 @@ export class ExperienceTracker {
|
|
|
200
186
|
continue;
|
|
201
187
|
}
|
|
202
188
|
try {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
for (const file of files) {
|
|
207
|
-
try {
|
|
208
|
-
const content = readFileSync(file, 'utf8');
|
|
209
|
-
const parsed = matter(content);
|
|
210
|
-
const mtime = statSync(file).mtime;
|
|
211
|
-
allFiles.push({
|
|
212
|
-
filePath: file,
|
|
213
|
-
data: parsed.data,
|
|
214
|
-
content: parsed.content,
|
|
215
|
-
mtime,
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
catch (error) {
|
|
219
|
-
debugLog(`Failed to read experience file ${file}:`, error);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
189
|
+
allFiles.push(...loadMarkdownFiles(experienceDir, {
|
|
190
|
+
onError: (filePath, error) => debugLog(`Failed to read experience file ${filePath}:`, error),
|
|
191
|
+
}));
|
|
222
192
|
}
|
|
223
193
|
catch (error) {
|
|
224
194
|
debugLog(`Failed to read experience directory ${experienceDir}:`, error);
|
|
@@ -242,19 +212,18 @@ export class ExperienceTracker {
|
|
|
242
212
|
});
|
|
243
213
|
})
|
|
244
214
|
.map((experience) => {
|
|
245
|
-
|
|
246
|
-
if (lines.length <= maxLines)
|
|
215
|
+
if (experience.content.split('\n').length <= maxLines)
|
|
247
216
|
return experience;
|
|
248
|
-
|
|
217
|
+
let content = experience.content;
|
|
218
|
+
while (content.split('\n').length > maxLines) {
|
|
219
|
+
const sections = mdq(content).query('section2').each();
|
|
220
|
+
if (sections.length <= 1)
|
|
221
|
+
break;
|
|
222
|
+
content = sections[sections.length - 1].replace('');
|
|
223
|
+
}
|
|
224
|
+
return { ...experience, content };
|
|
249
225
|
});
|
|
250
226
|
}
|
|
251
|
-
/**
|
|
252
|
-
* Clean up experience tracker (for testing)
|
|
253
|
-
*/
|
|
254
|
-
cleanup() {
|
|
255
|
-
// Clear any in-memory state if needed
|
|
256
|
-
// The actual files will be cleaned up by test cleanup
|
|
257
|
-
}
|
|
258
227
|
getSuccessfulExperience(state, options) {
|
|
259
228
|
const records = this.getRelevantExperience(state, {
|
|
260
229
|
includeDescendantExperience: options?.includeDescendants,
|
|
@@ -286,36 +255,48 @@ export class ExperienceTracker {
|
|
|
286
255
|
const bHash = basename(b.filePath, '.md');
|
|
287
256
|
return aHash.localeCompare(bHash);
|
|
288
257
|
});
|
|
258
|
+
return this.buildToc(sorted);
|
|
259
|
+
}
|
|
260
|
+
renderExperienceTocFor(state) {
|
|
261
|
+
const toc = this.getExperienceTableOfContents(state);
|
|
262
|
+
if (toc.length === 0)
|
|
263
|
+
return '';
|
|
264
|
+
const totalSections = toc.reduce((sum, entry) => sum + entry.sections.length, 0);
|
|
265
|
+
debugLog(`injecting experience TOC (${toc.length} files, ${totalSections} sections)`);
|
|
266
|
+
tag('operation').log(`Found ${toc.length} experience ${pluralize(toc.length, 'file')} (${totalSections} sections)`);
|
|
267
|
+
return renderExperienceToc(toc);
|
|
268
|
+
}
|
|
269
|
+
getExperienceSection(fileTag, sectionIndex, state, options) {
|
|
270
|
+
const toc = this.getExperienceTableOfContents(state, options);
|
|
271
|
+
const entry = toc.find((e) => e.fileTag === fileTag);
|
|
272
|
+
if (!entry)
|
|
273
|
+
return null;
|
|
274
|
+
const extracted = this.readSectionByHash(entry.fileHash, sectionIndex);
|
|
275
|
+
if (!extracted)
|
|
276
|
+
return null;
|
|
277
|
+
return { title: extracted.title, url: entry.url, content: extracted.body };
|
|
278
|
+
}
|
|
279
|
+
buildToc(records) {
|
|
289
280
|
const toc = [];
|
|
290
|
-
for (
|
|
291
|
-
const record = sorted[i];
|
|
292
|
-
const fileHash = basename(record.filePath, '.md');
|
|
293
|
-
const url = record.data?.url || '';
|
|
281
|
+
for (const record of records) {
|
|
294
282
|
const sections = listTocHeadings(record.content);
|
|
295
283
|
if (sections.length === 0)
|
|
296
284
|
continue;
|
|
297
285
|
toc.push({
|
|
298
|
-
fileTag: indexToLetters(
|
|
299
|
-
fileHash,
|
|
300
|
-
url,
|
|
286
|
+
fileTag: indexToLetters(toc.length),
|
|
287
|
+
fileHash: basename(record.filePath, '.md'),
|
|
288
|
+
url: record.data?.url || '',
|
|
301
289
|
sections,
|
|
302
290
|
});
|
|
303
291
|
}
|
|
304
292
|
return toc;
|
|
305
293
|
}
|
|
306
|
-
|
|
307
|
-
const
|
|
308
|
-
const entry = toc.find((e) => e.fileTag === fileTag);
|
|
309
|
-
if (!entry)
|
|
310
|
-
return null;
|
|
311
|
-
const filePath = this.findExperienceFileByHash(entry.fileHash);
|
|
294
|
+
readSectionByHash(fileHash, sectionIndex) {
|
|
295
|
+
const filePath = this.findExperienceFileByHash(fileHash);
|
|
312
296
|
if (!filePath)
|
|
313
297
|
return null;
|
|
314
|
-
const { content } = this.
|
|
315
|
-
|
|
316
|
-
if (!extracted)
|
|
317
|
-
return null;
|
|
318
|
-
return { title: extracted.title, url: entry.url, content: extracted.body };
|
|
298
|
+
const { content } = this.readExperienceFileAt(filePath);
|
|
299
|
+
return extractHeadingSection(content, sectionIndex);
|
|
319
300
|
}
|
|
320
301
|
findExperienceFileByHash(fileHash) {
|
|
321
302
|
for (const dir of this.getExperienceDirectories()) {
|
|
@@ -359,81 +340,34 @@ export class ExperienceTracker {
|
|
|
359
340
|
const bUrl = b.data?.url || '';
|
|
360
341
|
return aUrl.localeCompare(bUrl);
|
|
361
342
|
});
|
|
362
|
-
|
|
363
|
-
for (let i = 0; i < sorted.length; i++) {
|
|
364
|
-
const record = sorted[i];
|
|
365
|
-
const sections = listTocHeadings(record.content);
|
|
366
|
-
if (sections.length === 0)
|
|
367
|
-
continue;
|
|
368
|
-
toc.push({
|
|
369
|
-
fileTag: indexToLetters(toc.length),
|
|
370
|
-
fileHash: basename(record.filePath, '.md'),
|
|
371
|
-
url: record.data?.url || '',
|
|
372
|
-
sections,
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
return toc;
|
|
343
|
+
return this.buildToc(sorted);
|
|
376
344
|
}
|
|
377
345
|
getExperienceSectionByTag(fileTag, sectionIndex, filter) {
|
|
378
346
|
const toc = this.listAllExperienceToc(filter);
|
|
379
347
|
const entry = toc.find((e) => e.fileTag === fileTag);
|
|
380
348
|
if (!entry)
|
|
381
349
|
return null;
|
|
382
|
-
const
|
|
383
|
-
if (!filePath)
|
|
384
|
-
return null;
|
|
385
|
-
const { content } = this.readExperienceFile(entry.fileHash);
|
|
386
|
-
const extracted = extractHeadingSection(content, sectionIndex);
|
|
350
|
+
const extracted = this.readSectionByHash(entry.fileHash, sectionIndex);
|
|
387
351
|
if (!extracted)
|
|
388
352
|
return null;
|
|
389
353
|
return { title: extracted.title, url: entry.url, content: extracted.body, fileHash: entry.fileHash };
|
|
390
354
|
}
|
|
391
355
|
}
|
|
392
356
|
function listTocHeadings(content) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
continue;
|
|
399
|
-
const heading = token;
|
|
400
|
-
if (heading.depth !== 2 && heading.depth !== 3)
|
|
401
|
-
continue;
|
|
402
|
-
index++;
|
|
403
|
-
result.push({ index, level: heading.depth, title: heading.text });
|
|
404
|
-
}
|
|
405
|
-
return result;
|
|
357
|
+
return mdq(content)
|
|
358
|
+
.query('heading')
|
|
359
|
+
.meta()
|
|
360
|
+
.filter((m) => m.depth === 2 || m.depth === 3)
|
|
361
|
+
.map((m, i) => ({ index: i + 1, level: m.depth, title: m.text }));
|
|
406
362
|
}
|
|
407
363
|
function extractHeadingSection(content, sectionIndex) {
|
|
408
|
-
const
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
const token = tokens[i];
|
|
412
|
-
if (token.type !== 'heading')
|
|
413
|
-
continue;
|
|
414
|
-
const heading = token;
|
|
415
|
-
if (heading.depth !== 2 && heading.depth !== 3)
|
|
416
|
-
continue;
|
|
417
|
-
matching.push({ tokenIdx: i, depth: heading.depth, text: heading.text });
|
|
418
|
-
}
|
|
364
|
+
const sections = mdq(content).query('section').each();
|
|
365
|
+
const metas = mdq(content).query('section').meta();
|
|
366
|
+
const matching = metas.map((meta, i) => ({ meta, section: sections[i] })).filter((entry) => entry.meta.depth === 2 || entry.meta.depth === 3);
|
|
419
367
|
if (sectionIndex < 1 || sectionIndex > matching.length)
|
|
420
368
|
return null;
|
|
421
369
|
const target = matching[sectionIndex - 1];
|
|
422
|
-
|
|
423
|
-
for (let j = target.tokenIdx + 1; j < tokens.length; j++) {
|
|
424
|
-
const token = tokens[j];
|
|
425
|
-
if (token.type !== 'heading')
|
|
426
|
-
continue;
|
|
427
|
-
if (token.depth <= target.depth) {
|
|
428
|
-
endTokenIdx = j;
|
|
429
|
-
break;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
const body = tokens
|
|
433
|
-
.slice(target.tokenIdx, endTokenIdx)
|
|
434
|
-
.map((t) => t.raw || '')
|
|
435
|
-
.join('');
|
|
436
|
-
return { title: target.text, body };
|
|
370
|
+
return { title: target.meta.text, body: target.section.text() };
|
|
437
371
|
}
|
|
438
372
|
function indexToLetters(index) {
|
|
439
373
|
let n = index;
|
|
@@ -499,21 +433,14 @@ function generateActionContent(title, code, explanation) {
|
|
|
499
433
|
return lines.join('\n');
|
|
500
434
|
}
|
|
501
435
|
function renderAsHowTo(content) {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
result += `## HOW to ${text.slice(7).trim()} (single-step)\n\n`;
|
|
513
|
-
continue;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
result += token.raw || '';
|
|
517
|
-
}
|
|
518
|
-
return result;
|
|
436
|
+
return mdq(content)
|
|
437
|
+
.query('h2')
|
|
438
|
+
.replaceEach((heading) => {
|
|
439
|
+
const text = heading.meta()[0]?.text.trim() || '';
|
|
440
|
+
if (text.startsWith('FLOW:'))
|
|
441
|
+
return `## HOW to ${text.slice(5).trim()} (multi-step)\n\n`;
|
|
442
|
+
if (text.startsWith('ACTION:'))
|
|
443
|
+
return `## HOW to ${text.slice(7).trim()} (single-step)\n\n`;
|
|
444
|
+
return heading.text();
|
|
445
|
+
});
|
|
519
446
|
}
|
package/dist/src/explorbot.d.ts
CHANGED
|
@@ -39,7 +39,6 @@ export declare class ExplorBot {
|
|
|
39
39
|
config: ExplorbotConfig;
|
|
40
40
|
options: ExplorBotOptions;
|
|
41
41
|
userResolveFn: UserResolveFunction | null;
|
|
42
|
-
needsInput: boolean;
|
|
43
42
|
currentPlan?: Plan;
|
|
44
43
|
planFeature?: string;
|
|
45
44
|
lastPlanError: Error | null;
|
|
@@ -47,6 +46,8 @@ export declare class ExplorBot {
|
|
|
47
46
|
agents: Record<string, any>;
|
|
48
47
|
sessionPlans: Plan[];
|
|
49
48
|
lastReportedTestCount: number;
|
|
49
|
+
_experienceTracker?: ExperienceTracker;
|
|
50
|
+
_knowledgeTracker?: KnowledgeTracker;
|
|
50
51
|
constructor(options?: ExplorBotOptions);
|
|
51
52
|
get isExploring(): boolean;
|
|
52
53
|
setUserResolve(fn: UserResolveFunction): void;
|
|
@@ -58,12 +59,10 @@ export declare class ExplorBot {
|
|
|
58
59
|
openFreshTab(): Promise<void>;
|
|
59
60
|
getCurrentState(): WebPageState | null;
|
|
60
61
|
getExplorer(): Explorer;
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
knowledgeTracker(): KnowledgeTracker;
|
|
63
|
+
experienceTracker(): ExperienceTracker;
|
|
63
64
|
getConfig(): ExplorbotConfig;
|
|
64
65
|
getOptions(): ExplorBotOptions;
|
|
65
|
-
isReady(): boolean;
|
|
66
|
-
getConfigParser(): ConfigParser;
|
|
67
66
|
getProvider(): AIProvider;
|
|
68
67
|
createAgent<T>(factory: (deps: {
|
|
69
68
|
explorer: Explorer;
|
package/dist/src/explorbot.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { existsSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { ActionResult } from "./action-result.js";
|
|
4
3
|
import { Captain } from "./ai/captain.js";
|
|
5
4
|
import { Driller } from "./ai/driller.js";
|
|
6
5
|
import { ExperienceCompactor } from "./ai/experience-compactor.js";
|
|
@@ -25,6 +24,7 @@ import Explorer from "./explorer.js";
|
|
|
25
24
|
import { KnowledgeTracker } from "./knowledge-tracker.js";
|
|
26
25
|
import { Stats } from "./stats.js";
|
|
27
26
|
import { Plan } from "./test-plan.js";
|
|
27
|
+
import { browserErrorMessage } from "./utils/browser-errors.js";
|
|
28
28
|
import { setVerboseMode, tag } from "./utils/logger.js";
|
|
29
29
|
import { relativeToCwd } from "./utils/next-steps.js";
|
|
30
30
|
import { sanitizeFilename } from "./utils/strings.js";
|
|
@@ -36,7 +36,6 @@ export class ExplorBot {
|
|
|
36
36
|
config;
|
|
37
37
|
options;
|
|
38
38
|
userResolveFn = null;
|
|
39
|
-
needsInput = false;
|
|
40
39
|
currentPlan;
|
|
41
40
|
planFeature;
|
|
42
41
|
lastPlanError = null;
|
|
@@ -44,6 +43,8 @@ export class ExplorBot {
|
|
|
44
43
|
agents = {};
|
|
45
44
|
sessionPlans = [];
|
|
46
45
|
lastReportedTestCount = 0;
|
|
46
|
+
_experienceTracker;
|
|
47
|
+
_knowledgeTracker;
|
|
47
48
|
constructor(options = {}) {
|
|
48
49
|
this.options = options;
|
|
49
50
|
this.configParser = ConfigParser.getInstance();
|
|
@@ -64,16 +65,14 @@ export class ExplorBot {
|
|
|
64
65
|
}
|
|
65
66
|
try {
|
|
66
67
|
await this.startProviderOnly();
|
|
67
|
-
this.explorer = new Explorer(this.config, this.provider, this.options);
|
|
68
|
+
this.explorer = new Explorer(this.config, this.provider, this.options, this.experienceTracker(), this.knowledgeTracker());
|
|
68
69
|
await this.explorer.start();
|
|
69
70
|
if (!this.options.incognito) {
|
|
70
71
|
await this.agentExperienceCompactor().autocompact();
|
|
71
72
|
}
|
|
72
|
-
if (this.userResolveFn)
|
|
73
|
-
this.explorer.setUserResolve(this.userResolveFn);
|
|
74
73
|
}
|
|
75
74
|
catch (error) {
|
|
76
|
-
const message =
|
|
75
|
+
const message = browserErrorMessage(error);
|
|
77
76
|
console.error('\nFailed to start:', message);
|
|
78
77
|
process.exit(1);
|
|
79
78
|
}
|
|
@@ -105,17 +104,11 @@ export class ExplorBot {
|
|
|
105
104
|
getExplorer() {
|
|
106
105
|
return this.explorer;
|
|
107
106
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return this.explorer.getKnowledgeTracker();
|
|
111
|
-
}
|
|
112
|
-
return new KnowledgeTracker();
|
|
107
|
+
knowledgeTracker() {
|
|
108
|
+
return (this._knowledgeTracker ||= new KnowledgeTracker());
|
|
113
109
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return this.explorer.getStateManager().getExperienceTracker();
|
|
117
|
-
}
|
|
118
|
-
return new ExperienceTracker();
|
|
110
|
+
experienceTracker() {
|
|
111
|
+
return (this._experienceTracker ||= new ExperienceTracker(this.knowledgeTracker()));
|
|
119
112
|
}
|
|
120
113
|
getConfig() {
|
|
121
114
|
return this.config;
|
|
@@ -123,12 +116,6 @@ export class ExplorBot {
|
|
|
123
116
|
getOptions() {
|
|
124
117
|
return this.options;
|
|
125
118
|
}
|
|
126
|
-
isReady() {
|
|
127
|
-
return this.explorer?.isStarted;
|
|
128
|
-
}
|
|
129
|
-
getConfigParser() {
|
|
130
|
-
return this.configParser;
|
|
131
|
-
}
|
|
132
119
|
getProvider() {
|
|
133
120
|
return this.provider;
|
|
134
121
|
}
|
|
@@ -138,10 +125,8 @@ export class ExplorBot {
|
|
|
138
125
|
ai: this.provider,
|
|
139
126
|
config: this.config,
|
|
140
127
|
});
|
|
141
|
-
const agentEmoji = agent.emoji || '';
|
|
142
128
|
const agentName = agent.constructor.name.toLowerCase();
|
|
143
129
|
tag('debug').log(`Created ${agentName} agent`);
|
|
144
|
-
// Agent is stored by the calling method using a string key
|
|
145
130
|
return agent;
|
|
146
131
|
}
|
|
147
132
|
agentResearcher() {
|
|
@@ -149,12 +134,12 @@ export class ExplorBot {
|
|
|
149
134
|
}
|
|
150
135
|
agentNavigator() {
|
|
151
136
|
return (this.agents.navigator ||= this.createAgent(({ ai, explorer }) => {
|
|
152
|
-
return new Navigator(explorer, ai
|
|
137
|
+
return new Navigator(explorer, ai);
|
|
153
138
|
}));
|
|
154
139
|
}
|
|
155
140
|
agentPlanner() {
|
|
156
141
|
if (!this.agents.planner) {
|
|
157
|
-
this.agents.planner = this.createAgent(({ ai, explorer }) => new Planner(explorer, ai));
|
|
142
|
+
this.agents.planner = this.createAgent(({ ai, explorer }) => new Planner(explorer, ai, this.agentResearcher()));
|
|
158
143
|
const fisherman = this.agentFisherman();
|
|
159
144
|
if (fisherman)
|
|
160
145
|
this.agents.planner.setFisherman(fisherman);
|
|
@@ -165,14 +150,8 @@ export class ExplorBot {
|
|
|
165
150
|
return (this.agents.pilot ||= this.createAgent(({ ai, explorer }) => {
|
|
166
151
|
const researcher = this.agentResearcher();
|
|
167
152
|
const navigator = this.agentNavigator();
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
const getState = () => {
|
|
171
|
-
const state = stateManager.getCurrentState();
|
|
172
|
-
return state ? ActionResult.fromState(state) : null;
|
|
173
|
-
};
|
|
174
|
-
const tools = createAgentTools({ explorer, researcher, navigator, experienceTracker, getState, supervisor: true });
|
|
175
|
-
return new Pilot(ai, tools, researcher, explorer, experienceTracker);
|
|
153
|
+
const tools = createAgentTools({ explorer, researcher, navigator, supervisor: true });
|
|
154
|
+
return new Pilot(ai, tools, researcher, explorer);
|
|
176
155
|
}));
|
|
177
156
|
}
|
|
178
157
|
agentTester() {
|
|
@@ -180,13 +159,7 @@ export class ExplorBot {
|
|
|
180
159
|
this.agents.tester = this.createAgent(({ ai, explorer }) => {
|
|
181
160
|
const researcher = this.agentResearcher();
|
|
182
161
|
const navigator = this.agentNavigator();
|
|
183
|
-
const
|
|
184
|
-
const experienceTracker = stateManager.getExperienceTracker();
|
|
185
|
-
const getState = () => {
|
|
186
|
-
const state = stateManager.getCurrentState();
|
|
187
|
-
return state ? ActionResult.fromState(state) : null;
|
|
188
|
-
};
|
|
189
|
-
const tools = createAgentTools({ explorer, researcher, navigator, experienceTracker, getState });
|
|
162
|
+
const tools = createAgentTools({ explorer, researcher, navigator });
|
|
190
163
|
return new Tester(explorer, ai, researcher, navigator, tools);
|
|
191
164
|
});
|
|
192
165
|
const qm = this.agentQuartermaster();
|
|
@@ -204,15 +177,11 @@ export class ExplorBot {
|
|
|
204
177
|
agentCaptain() {
|
|
205
178
|
if (!this.agents.captain) {
|
|
206
179
|
this.agents.captain = new Captain(this);
|
|
207
|
-
const qm = this.agentQuartermaster();
|
|
208
|
-
if (qm)
|
|
209
|
-
this.agents.captain.setQuartermaster(qm);
|
|
210
|
-
this.agents.captain.setHistorian(this.agentHistorian());
|
|
211
180
|
}
|
|
212
181
|
return this.agents.captain;
|
|
213
182
|
}
|
|
214
183
|
agentExperienceCompactor() {
|
|
215
|
-
return (this.agents.experienceCompactor ||= new ExperienceCompactor(this.provider, this.
|
|
184
|
+
return (this.agents.experienceCompactor ||= new ExperienceCompactor(this.provider, this.experienceTracker()));
|
|
216
185
|
}
|
|
217
186
|
agentQuartermaster() {
|
|
218
187
|
const config = this.config.ai?.agents?.quartermaster;
|
|
@@ -241,12 +210,9 @@ export class ExplorBot {
|
|
|
241
210
|
this.agents.rerunner = this.createAgent(({ ai, explorer }) => {
|
|
242
211
|
const researcher = this.agentResearcher();
|
|
243
212
|
const navigator = this.agentNavigator();
|
|
244
|
-
const tools = createAgentTools({ explorer, researcher, navigator });
|
|
213
|
+
const tools = createAgentTools({ explorer, researcher, navigator, withExperience: false });
|
|
245
214
|
return new Rerunner(explorer, ai, tools);
|
|
246
215
|
});
|
|
247
|
-
const qm = this.agentQuartermaster();
|
|
248
|
-
if (qm)
|
|
249
|
-
this.agents.rerunner.setQuartermaster(qm);
|
|
250
216
|
this.agents.rerunner.setHistorian(this.agentHistorian());
|
|
251
217
|
}
|
|
252
218
|
return this.agents.rerunner;
|
|
@@ -443,7 +409,7 @@ export class ExplorBot {
|
|
|
443
409
|
this.lastReportedTestCount = tests.length;
|
|
444
410
|
}
|
|
445
411
|
catch (error) {
|
|
446
|
-
const message =
|
|
412
|
+
const message = browserErrorMessage(error);
|
|
447
413
|
tag('warning').log(`Session analysis failed: ${message}`);
|
|
448
414
|
}
|
|
449
415
|
}
|