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.
- 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 +8 -6
- package/dist/src/action-result.d.ts +131 -0
- package/dist/src/action-result.js +82 -159
- package/dist/src/action.d.ts +43 -0
- package/dist/src/action.js +18 -106
- package/dist/src/activity.d.ts +26 -0
- package/dist/src/ai/agent.d.ts +3 -0
- package/dist/src/ai/captain/file-tools.d.ts +28 -0
- package/dist/src/ai/captain/idle-mode.d.ts +11 -0
- package/dist/src/ai/captain/mixin.d.ts +10 -0
- package/dist/src/ai/captain/test-mode.d.ts +13 -0
- package/dist/src/ai/captain/web-mode.d.ts +13 -0
- package/dist/src/ai/captain/web-mode.js +0 -7
- package/dist/src/ai/captain.d.ts +141 -0
- package/dist/src/ai/captain.js +4 -19
- package/dist/src/ai/conversation.d.ts +28 -0
- package/dist/src/ai/conversation.js +9 -28
- package/dist/src/ai/driller.d.ts +157 -0
- package/dist/src/ai/driller.js +3 -12
- package/dist/src/ai/experience-compactor.d.ts +53 -0
- package/dist/src/ai/experience-compactor.js +16 -33
- package/dist/src/ai/fisherman-tools.d.ts +90 -0
- package/dist/src/ai/fisherman-tools.js +1 -1
- package/dist/src/ai/fisherman.d.ts +29 -0
- package/dist/src/ai/fisherman.js +0 -3
- package/dist/src/ai/historian/codeceptjs.d.ts +17 -0
- package/dist/src/ai/historian/codeceptjs.js +3 -2
- package/dist/src/ai/historian/experience.d.ts +40 -0
- package/dist/src/ai/historian/mixin.d.ts +2 -0
- package/dist/src/ai/historian/playwright.d.ts +22 -0
- package/dist/src/ai/historian/playwright.js +3 -2
- package/dist/src/ai/historian/screencast.d.ts +32 -0
- package/dist/src/ai/historian/utils.d.ts +3 -0
- package/dist/src/ai/historian/utils.js +0 -1
- package/dist/src/ai/historian.d.ts +109 -0
- package/dist/src/ai/historian.js +1 -5
- package/dist/src/ai/navigator.d.ts +54 -0
- package/dist/src/ai/navigator.js +11 -46
- package/dist/src/ai/pilot.d.ts +81 -0
- package/dist/src/ai/pilot.js +6 -17
- package/dist/src/ai/planner/session-dedup.d.ts +12 -0
- package/dist/src/ai/planner/styles.d.ts +6 -0
- package/dist/src/ai/planner/subpages.d.ts +35 -0
- package/dist/src/ai/planner.d.ts +79 -0
- package/dist/src/ai/planner.js +49 -37
- package/dist/src/ai/provider.d.ts +53 -0
- package/dist/src/ai/provider.js +78 -130
- package/dist/src/ai/quartermaster.d.ts +56 -0
- package/dist/src/ai/quartermaster.js +4 -10
- package/dist/src/ai/rerunner.d.ts +48 -0
- package/dist/src/ai/rerunner.js +14 -22
- package/dist/src/ai/researcher/cache.d.ts +6 -0
- package/dist/src/ai/researcher/cache.js +11 -18
- package/dist/src/ai/researcher/coordinates.d.ts +56 -0
- package/dist/src/ai/researcher/deep-analysis.d.ts +74 -0
- package/dist/src/ai/researcher/deep-analysis.js +3 -6
- package/dist/src/ai/researcher/fingerprint-worker.d.ts +1 -0
- package/dist/src/ai/researcher/focus.d.ts +8 -0
- package/dist/src/ai/researcher/locators.d.ts +36 -0
- package/dist/src/ai/researcher/mixin.d.ts +2 -0
- package/dist/src/ai/researcher/parser.d.ts +35 -0
- package/dist/src/ai/researcher/parser.js +1 -1
- package/dist/src/ai/researcher/research-result.d.ts +15 -0
- package/dist/src/ai/researcher/research-result.js +0 -19
- package/dist/src/ai/researcher/sections.d.ts +19 -0
- package/dist/src/ai/researcher/sections.js +5 -1
- package/dist/src/ai/researcher.js +12 -53
- package/dist/src/ai/rules.d.ts +16 -0
- package/dist/src/ai/rules.js +22 -15
- package/dist/src/ai/session-analyst.d.ts +11 -0
- package/dist/src/ai/session-analyst.js +8 -5
- package/dist/src/ai/task-agent.d.ts +28 -0
- package/dist/src/ai/task-agent.js +2 -27
- package/dist/src/ai/tester.d.ts +101 -0
- package/dist/src/ai/tester.js +10 -61
- package/dist/src/ai/tools.d.ts +52 -0
- package/dist/src/ai/tools.js +119 -101
- package/dist/src/api/api-client.d.ts +17 -0
- package/dist/src/api/request-result.d.ts +44 -0
- package/dist/src/api/request-store.d.ts +29 -0
- package/dist/src/api/spec-reader.d.ts +5 -0
- package/dist/src/api/xhr-capture.d.ts +10 -0
- package/dist/src/browser-server.d.ts +10 -0
- package/dist/src/command-handler.d.ts +68 -0
- package/dist/src/commands/add-rule-command.d.ts +11 -0
- package/dist/src/commands/base-command.d.ts +26 -0
- package/dist/src/commands/clean-command.d.ts +12 -0
- package/dist/src/commands/compact-command.d.ts +20 -0
- package/dist/src/commands/compact-command.js +6 -6
- package/dist/src/commands/context-aria-command.d.ts +6 -0
- package/dist/src/commands/context-command.d.ts +11 -0
- package/dist/src/commands/context-command.js +1 -1
- package/dist/src/commands/context-data-command.d.ts +6 -0
- package/dist/src/commands/context-experience-command.d.ts +6 -0
- package/dist/src/commands/context-html-command.d.ts +6 -0
- package/dist/src/commands/context-knowledge-command.d.ts +6 -0
- package/dist/src/commands/context-knowledge-command.js +1 -1
- package/dist/src/commands/debug-command.d.ts +6 -0
- package/dist/src/commands/drill-command.d.ts +10 -0
- package/dist/src/commands/exit-command.d.ts +7 -0
- package/dist/src/commands/experience-command.d.ts +15 -0
- package/dist/src/commands/experience-command.js +1 -1
- package/dist/src/commands/explore-command.d.ts +51 -0
- package/dist/src/commands/freesail-command.d.ts +12 -0
- package/dist/src/commands/help-command.d.ts +6 -0
- package/dist/src/commands/index.d.ts +4 -0
- package/dist/src/commands/init-command.d.ts +7 -0
- package/dist/src/commands/knows-command.d.ts +10 -0
- package/dist/src/commands/knows-command.js +1 -1
- package/dist/src/commands/learn-command.d.ts +7 -0
- package/dist/src/commands/learn-command.js +3 -1
- package/dist/src/commands/navigate-command.d.ts +7 -0
- package/dist/src/commands/path-command.d.ts +10 -0
- package/dist/src/commands/plan-clear-command.d.ts +7 -0
- package/dist/src/commands/plan-command.d.ts +14 -0
- package/dist/src/commands/plan-edit-command.d.ts +7 -0
- package/dist/src/commands/plan-load-command.d.ts +7 -0
- package/dist/src/commands/plan-reload-command.d.ts +7 -0
- package/dist/src/commands/plan-save-command.d.ts +7 -0
- package/dist/src/commands/plans-command.d.ts +21 -0
- package/dist/src/commands/rerun-command.d.ts +7 -0
- package/dist/src/commands/research-command.d.ts +11 -0
- package/dist/src/commands/runs-command.d.ts +7 -0
- package/dist/src/commands/start-command.d.ts +7 -0
- package/dist/src/commands/status-command.d.ts +6 -0
- package/dist/src/commands/test-command.d.ts +11 -0
- package/dist/src/components/ActivityPane.d.ts +6 -0
- package/dist/src/components/AddKnowledge.d.ts +10 -0
- package/dist/src/components/AddKnowledge.js +3 -7
- package/dist/src/components/AddRule.d.ts +9 -0
- package/dist/src/components/App.d.ts +9 -0
- package/dist/src/components/App.js +2 -2
- package/dist/src/components/Autocomplete.d.ts +3 -0
- package/dist/src/components/InputPane.d.ts +13 -0
- package/dist/src/components/InputReadline.d.ts +17 -0
- package/dist/src/components/LogPane.d.ts +6 -0
- package/dist/src/components/PlanEditor.d.ts +9 -0
- package/dist/src/components/PlanPane.d.ts +13 -0
- package/dist/src/components/SessionTimer.d.ts +6 -0
- package/dist/src/components/StateTransitionPane.d.ts +8 -0
- package/dist/src/components/StatusPane.d.ts +4 -0
- package/dist/src/components/TaskPane.d.ts +9 -0
- package/dist/src/components/Welcome.d.ts +3 -0
- package/dist/src/components/WelcomeChecklist.d.ts +9 -0
- package/dist/src/components/WelcomeCommands.d.ts +6 -0
- package/dist/src/components/autocomplete-store.d.ts +10 -0
- package/dist/src/components/parse-keypress.d.ts +12 -0
- package/dist/src/config.d.ts +253 -0
- package/dist/src/config.js +12 -0
- package/dist/src/execution-controller.d.ts +25 -0
- package/dist/src/experience-tracker.d.ts +112 -0
- package/dist/src/experience-tracker.js +83 -155
- package/dist/src/explorbot.d.ts +106 -0
- package/dist/src/explorbot.js +17 -51
- package/dist/src/explorer.d.ts +129 -0
- package/dist/src/explorer.js +31 -77
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +2 -55
- package/dist/src/knowledge-tracker.d.ts +33 -0
- package/dist/src/knowledge-tracker.js +47 -47
- package/dist/src/observability.d.ts +13 -0
- package/dist/src/observability.js +2 -1
- package/dist/src/playwright-recorder.d.ts +31 -0
- package/dist/src/reporter.d.ts +34 -0
- package/dist/src/state-manager.d.ts +160 -0
- package/dist/src/state-manager.js +10 -136
- package/dist/src/stats.d.ts +24 -0
- package/dist/src/suite.d.ts +24 -0
- package/dist/src/test-plan.d.ts +166 -0
- package/dist/src/utils/aria.d.ts +25 -0
- package/dist/src/utils/aria.js +4 -1
- package/dist/src/utils/browser-errors.d.ts +8 -0
- package/dist/src/utils/cache.d.ts +15 -0
- package/dist/src/utils/cache.js +34 -0
- package/dist/src/utils/cli-name.d.ts +1 -0
- package/dist/src/utils/code-extractor.d.ts +1 -0
- package/dist/src/utils/context-formatter.d.ts +31 -0
- package/dist/src/utils/context-formatter.js +15 -27
- package/dist/src/utils/error-page.d.ts +12 -0
- package/dist/src/utils/expandable.d.ts +4 -0
- package/dist/src/utils/hooks-runner.d.ts +15 -0
- package/dist/src/utils/html-diff.d.ts +19 -0
- package/dist/src/utils/html.d.ts +129 -0
- package/dist/src/utils/html.js +71 -0
- package/dist/src/utils/log-filters.d.ts +6 -0
- package/dist/src/utils/logger.d.ts +35 -0
- package/dist/src/utils/loop.d.ts +35 -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-parser.d.ts +17 -0
- package/dist/src/utils/markdown-query.d.ts +46 -0
- package/dist/src/utils/markdown-query.js +14 -1
- package/dist/src/utils/markdown-terminal.d.ts +1 -0
- package/dist/src/utils/next-steps.d.ts +11 -0
- package/dist/src/utils/page-readiness.d.ts +7 -0
- package/dist/src/utils/page-readiness.js +1 -1
- package/dist/src/utils/research-parser.d.ts +1 -0
- package/dist/src/utils/retry.d.ts +8 -0
- package/dist/src/utils/rules-loader.d.ts +12 -0
- package/dist/src/utils/secrets.d.ts +4 -0
- package/dist/src/utils/secrets.js +28 -0
- package/dist/src/utils/step-analyzer.d.ts +11 -0
- package/dist/src/utils/strings.d.ts +5 -0
- package/dist/src/utils/strings.js +10 -0
- package/dist/src/utils/test-files.d.ts +3 -0
- package/dist/src/utils/test-plan-markdown.d.ts +9 -0
- package/dist/src/utils/throttle.d.ts +2 -0
- package/dist/src/utils/unique-names.d.ts +2 -0
- package/dist/src/utils/url-matcher.d.ts +6 -0
- package/dist/src/utils/web-element.d.ts +45 -0
- 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/dist/src/utils/xpath.d.ts +17 -0
- package/package.json +8 -6
- package/src/action-result.ts +80 -168
- package/src/action.ts +16 -123
- 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 +86 -139
- 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 +90 -152
- package/src/explorbot.ts +17 -51
- package/src/explorer.ts +34 -81
- package/src/index.ts +9 -0
- package/src/knowledge-tracker.ts +49 -50
- package/src/observability.ts +2 -1
- package/src/state-manager.ts +15 -164
- 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 +28 -0
- package/src/utils/strings.ts +12 -0
- package/src/utils/web-element.ts +4 -0
- package/src/utils/web-sandbox.ts +43 -0
- package/src/index.tsx +0 -62
package/src/components/App.tsx
CHANGED
|
@@ -142,7 +142,7 @@ export function App({ explorBot, initialShowInput = false, exitOnEmptyInput = fa
|
|
|
142
142
|
if (!mounted) return;
|
|
143
143
|
setChecklistData({
|
|
144
144
|
config: explorBot.getConfig(),
|
|
145
|
-
knowledgeTracker: explorBot.
|
|
145
|
+
knowledgeTracker: explorBot.knowledgeTracker(),
|
|
146
146
|
});
|
|
147
147
|
setShowWelcome(true);
|
|
148
148
|
setShowInput(true);
|
|
@@ -334,7 +334,7 @@ export function App({ explorBot, initialShowInput = false, exitOnEmptyInput = fa
|
|
|
334
334
|
{showSessionTimer && <SessionTimer startedAt={sessionStartedAtRef.current} />}
|
|
335
335
|
</Box>
|
|
336
336
|
|
|
337
|
-
{showWelcome && <WelcomeCommands hasKnowledge={explorBot.
|
|
337
|
+
{showWelcome && <WelcomeCommands hasKnowledge={explorBot.knowledgeTracker().listAllKnowledge().length > 0} />}
|
|
338
338
|
{showInput && <Box height={1} />}
|
|
339
339
|
{interruptPrompt && showInput && (
|
|
340
340
|
<Box paddingX={1}>
|
package/src/config.ts
CHANGED
|
@@ -362,6 +362,18 @@ export class ConfigParser {
|
|
|
362
362
|
return path.join(path.dirname(configPath), config.dirs?.output || 'output');
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
+
public getProjectRoot(): string {
|
|
366
|
+
const configPath = this.getConfigPath();
|
|
367
|
+
if (configPath) return path.dirname(configPath);
|
|
368
|
+
return process.cwd();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
public resolveProjectDir(relativeDir: string): string {
|
|
372
|
+
const configPath = this.getConfigPath();
|
|
373
|
+
if (!configPath) return relativeDir;
|
|
374
|
+
return path.join(path.dirname(configPath), relativeDir);
|
|
375
|
+
}
|
|
376
|
+
|
|
365
377
|
public getStatesDir(): string {
|
|
366
378
|
return outputPath('states');
|
|
367
379
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync,
|
|
2
|
-
import { basename,
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { basename, join } from 'node:path';
|
|
3
3
|
import matter from 'gray-matter';
|
|
4
|
-
import { type Tokens, marked } from 'marked';
|
|
5
4
|
import type { ActionResult } from './action-result.js';
|
|
6
5
|
import { ConfigParser } from './config.js';
|
|
7
6
|
import { KnowledgeTracker } from './knowledge-tracker.js';
|
|
8
7
|
import type { WebPageState } from './state-manager.js';
|
|
9
|
-
import { createDebug, tag } from './utils/logger.js';
|
|
8
|
+
import { createDebug, pluralize, tag } from './utils/logger.js';
|
|
9
|
+
import { loadMarkdownFiles } from './utils/markdown-files.js';
|
|
10
10
|
import { mdq } from './utils/markdown-query.js';
|
|
11
|
+
import { redactSecrets } from './utils/secrets.js';
|
|
11
12
|
import { isNonReusableCode } from './utils/step-analyzer.ts';
|
|
12
13
|
import { extractStatePath } from './utils/url-matcher.js';
|
|
13
14
|
|
|
@@ -38,20 +39,14 @@ export class ExperienceTracker {
|
|
|
38
39
|
private disabled: boolean;
|
|
39
40
|
private knowledgeTracker: KnowledgeTracker;
|
|
40
41
|
|
|
41
|
-
constructor(options: { disabled?: boolean } = {}) {
|
|
42
|
+
constructor(knowledgeTracker: KnowledgeTracker, options: { disabled?: boolean } = {}) {
|
|
42
43
|
const configParser = ConfigParser.getInstance();
|
|
43
44
|
const config = configParser.getConfig();
|
|
44
|
-
const configPath = configParser.getConfigPath();
|
|
45
45
|
this.disabled = options.disabled ?? false;
|
|
46
|
-
this.knowledgeTracker =
|
|
46
|
+
this.knowledgeTracker = knowledgeTracker;
|
|
47
47
|
|
|
48
48
|
// Resolve experience directory relative to the config file location (project root)
|
|
49
|
-
|
|
50
|
-
const projectRoot = dirname(configPath);
|
|
51
|
-
this.experienceDir = join(projectRoot, config.dirs?.experience || 'experience');
|
|
52
|
-
} else {
|
|
53
|
-
this.experienceDir = config.dirs?.experience || 'experience';
|
|
54
|
-
}
|
|
49
|
+
this.experienceDir = configParser.resolveProjectDir(config.dirs?.experience || 'experience');
|
|
55
50
|
|
|
56
51
|
if (!this.disabled) {
|
|
57
52
|
this.ensureDirectory(this.experienceDir);
|
|
@@ -98,7 +93,10 @@ export class ExperienceTracker {
|
|
|
98
93
|
}
|
|
99
94
|
|
|
100
95
|
readExperienceFile(stateHash: string): { content: string; data: any } {
|
|
101
|
-
|
|
96
|
+
return this.readExperienceFileAt(this.getExperienceFilePath(stateHash));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private readExperienceFileAt(filePath: string): { content: string; data: any } {
|
|
102
100
|
const fileContent = readFileSync(filePath, 'utf8');
|
|
103
101
|
const { content, data } = matter(fileContent);
|
|
104
102
|
return { content, data };
|
|
@@ -109,25 +107,10 @@ export class ExperienceTracker {
|
|
|
109
107
|
return;
|
|
110
108
|
}
|
|
111
109
|
const filePath = this.getExperienceFilePath(stateHash);
|
|
112
|
-
const fileContent = matter.stringify(content, frontmatter || {});
|
|
110
|
+
const fileContent = matter.stringify(redactSecrets(content), frontmatter || {});
|
|
113
111
|
writeFileSync(filePath, fileContent, 'utf8');
|
|
114
112
|
}
|
|
115
113
|
|
|
116
|
-
hasRecentExperience(stateHash: string, prefix = ''): boolean {
|
|
117
|
-
if (this.disabled) {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
if (prefix) {
|
|
121
|
-
stateHash = `${prefix}_${stateHash}`;
|
|
122
|
-
}
|
|
123
|
-
const filePath = this.getExperienceFilePath(stateHash);
|
|
124
|
-
if (!existsSync(filePath)) {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
const stats = statSync(filePath);
|
|
128
|
-
return stats.mtime.getTime() > Date.now() - 1000 * 60 * 60 * 24;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
114
|
private getExperienceFilePath(stateHash: string): string {
|
|
132
115
|
return join(this.experienceDir, `${stateHash}.md`);
|
|
133
116
|
}
|
|
@@ -175,7 +158,12 @@ export class ExperienceTracker {
|
|
|
175
158
|
this.ensureExperienceFile(state);
|
|
176
159
|
const stateHash = state.getStateHash();
|
|
177
160
|
const { content, data } = this.readExperienceFile(stateHash);
|
|
178
|
-
if (
|
|
161
|
+
if (
|
|
162
|
+
mdq(content)
|
|
163
|
+
.query('code')
|
|
164
|
+
.meta()
|
|
165
|
+
.some((m) => m.text.trim() === action.code.trim())
|
|
166
|
+
) {
|
|
179
167
|
debugLog('Skipping duplicate action', action.code);
|
|
180
168
|
return;
|
|
181
169
|
}
|
|
@@ -203,7 +191,12 @@ export class ExperienceTracker {
|
|
|
203
191
|
const stateHash = state.getStateHash();
|
|
204
192
|
const { content, data } = this.readExperienceFile(stateHash);
|
|
205
193
|
|
|
206
|
-
if (
|
|
194
|
+
if (
|
|
195
|
+
mdq(content)
|
|
196
|
+
.query('section2')
|
|
197
|
+
.each()
|
|
198
|
+
.some((s) => s.text().trim() === body.trim())
|
|
199
|
+
) {
|
|
207
200
|
debugLog('Skipping duplicate flow body');
|
|
208
201
|
return;
|
|
209
202
|
}
|
|
@@ -230,25 +223,11 @@ export class ExperienceTracker {
|
|
|
230
223
|
}
|
|
231
224
|
|
|
232
225
|
try {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
try {
|
|
239
|
-
const content = readFileSync(file, 'utf8');
|
|
240
|
-
const parsed = matter(content);
|
|
241
|
-
const mtime = statSync(file).mtime;
|
|
242
|
-
allFiles.push({
|
|
243
|
-
filePath: file,
|
|
244
|
-
data: parsed.data,
|
|
245
|
-
content: parsed.content,
|
|
246
|
-
mtime,
|
|
247
|
-
});
|
|
248
|
-
} catch (error) {
|
|
249
|
-
debugLog(`Failed to read experience file ${file}:`, error);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
226
|
+
allFiles.push(
|
|
227
|
+
...loadMarkdownFiles(experienceDir, {
|
|
228
|
+
onError: (filePath, error) => debugLog(`Failed to read experience file ${filePath}:`, error),
|
|
229
|
+
})
|
|
230
|
+
);
|
|
252
231
|
} catch (error) {
|
|
253
232
|
debugLog(`Failed to read experience directory ${experienceDir}:`, error);
|
|
254
233
|
}
|
|
@@ -274,20 +253,17 @@ export class ExperienceTracker {
|
|
|
274
253
|
});
|
|
275
254
|
})
|
|
276
255
|
.map((experience) => {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
256
|
+
if (experience.content.split('\n').length <= maxLines) return experience;
|
|
257
|
+
let content = experience.content;
|
|
258
|
+
while (content.split('\n').length > maxLines) {
|
|
259
|
+
const sections = mdq(content).query('section2').each();
|
|
260
|
+
if (sections.length <= 1) break;
|
|
261
|
+
content = sections[sections.length - 1].replace('');
|
|
262
|
+
}
|
|
263
|
+
return { ...experience, content };
|
|
280
264
|
});
|
|
281
265
|
}
|
|
282
266
|
|
|
283
|
-
/**
|
|
284
|
-
* Clean up experience tracker (for testing)
|
|
285
|
-
*/
|
|
286
|
-
cleanup(): void {
|
|
287
|
-
// Clear any in-memory state if needed
|
|
288
|
-
// The actual files will be cleaned up by test cleanup
|
|
289
|
-
}
|
|
290
|
-
|
|
291
267
|
getSuccessfulExperience(state: ActionResult, options?: { includeDescendants?: boolean; stripCode?: boolean }): string[] {
|
|
292
268
|
const records = this.getRelevantExperience(state, {
|
|
293
269
|
includeDescendantExperience: options?.includeDescendants,
|
|
@@ -325,36 +301,50 @@ export class ExperienceTracker {
|
|
|
325
301
|
return aHash.localeCompare(bHash);
|
|
326
302
|
});
|
|
327
303
|
|
|
304
|
+
return this.buildToc(sorted);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
renderExperienceTocFor(state: ActionResult): string {
|
|
308
|
+
const toc = this.getExperienceTableOfContents(state);
|
|
309
|
+
if (toc.length === 0) return '';
|
|
310
|
+
|
|
311
|
+
const totalSections = toc.reduce((sum, entry) => sum + entry.sections.length, 0);
|
|
312
|
+
debugLog(`injecting experience TOC (${toc.length} files, ${totalSections} sections)`);
|
|
313
|
+
tag('operation').log(`Found ${toc.length} experience ${pluralize(toc.length, 'file')} (${totalSections} sections)`);
|
|
314
|
+
return renderExperienceToc(toc);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
getExperienceSection(fileTag: string, sectionIndex: number, state: ActionResult, options?: { includeDescendantExperience?: boolean }): { title: string; url: string; content: string } | null {
|
|
318
|
+
const toc = this.getExperienceTableOfContents(state, options);
|
|
319
|
+
const entry = toc.find((e) => e.fileTag === fileTag);
|
|
320
|
+
if (!entry) return null;
|
|
321
|
+
|
|
322
|
+
const extracted = this.readSectionByHash(entry.fileHash, sectionIndex);
|
|
323
|
+
if (!extracted) return null;
|
|
324
|
+
|
|
325
|
+
return { title: extracted.title, url: entry.url, content: extracted.body };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private buildToc(records: ExperienceFile[]): ExperienceTocEntry[] {
|
|
328
329
|
const toc: ExperienceTocEntry[] = [];
|
|
329
|
-
for (
|
|
330
|
-
const record = sorted[i];
|
|
331
|
-
const fileHash = basename(record.filePath, '.md');
|
|
332
|
-
const url = (record.data as WebPageState)?.url || '';
|
|
330
|
+
for (const record of records) {
|
|
333
331
|
const sections = listTocHeadings(record.content);
|
|
334
332
|
if (sections.length === 0) continue;
|
|
335
333
|
toc.push({
|
|
336
|
-
fileTag: indexToLetters(
|
|
337
|
-
fileHash,
|
|
338
|
-
url,
|
|
334
|
+
fileTag: indexToLetters(toc.length),
|
|
335
|
+
fileHash: basename(record.filePath, '.md'),
|
|
336
|
+
url: (record.data as WebPageState)?.url || '',
|
|
339
337
|
sections,
|
|
340
338
|
});
|
|
341
339
|
}
|
|
342
340
|
return toc;
|
|
343
341
|
}
|
|
344
342
|
|
|
345
|
-
|
|
346
|
-
const
|
|
347
|
-
const entry = toc.find((e) => e.fileTag === fileTag);
|
|
348
|
-
if (!entry) return null;
|
|
349
|
-
|
|
350
|
-
const filePath = this.findExperienceFileByHash(entry.fileHash);
|
|
343
|
+
private readSectionByHash(fileHash: string, sectionIndex: number): { title: string; body: string } | null {
|
|
344
|
+
const filePath = this.findExperienceFileByHash(fileHash);
|
|
351
345
|
if (!filePath) return null;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const extracted = extractHeadingSection(content, sectionIndex);
|
|
355
|
-
if (!extracted) return null;
|
|
356
|
-
|
|
357
|
-
return { title: extracted.title, url: entry.url, content: extracted.body };
|
|
346
|
+
const { content } = this.readExperienceFileAt(filePath);
|
|
347
|
+
return extractHeadingSection(content, sectionIndex);
|
|
358
348
|
}
|
|
359
349
|
|
|
360
350
|
private findExperienceFileByHash(fileHash: string): string | null {
|
|
@@ -401,19 +391,7 @@ export class ExperienceTracker {
|
|
|
401
391
|
return aUrl.localeCompare(bUrl);
|
|
402
392
|
});
|
|
403
393
|
|
|
404
|
-
|
|
405
|
-
for (let i = 0; i < sorted.length; i++) {
|
|
406
|
-
const record = sorted[i];
|
|
407
|
-
const sections = listTocHeadings(record.content);
|
|
408
|
-
if (sections.length === 0) continue;
|
|
409
|
-
toc.push({
|
|
410
|
-
fileTag: indexToLetters(toc.length),
|
|
411
|
-
fileHash: basename(record.filePath, '.md'),
|
|
412
|
-
url: (record.data as WebPageState)?.url || '',
|
|
413
|
-
sections,
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
return toc;
|
|
394
|
+
return this.buildToc(sorted);
|
|
417
395
|
}
|
|
418
396
|
|
|
419
397
|
getExperienceSectionByTag(fileTag: string, sectionIndex: number, filter?: string): { title: string; url: string; content: string; fileHash: string } | null {
|
|
@@ -421,11 +399,7 @@ export class ExperienceTracker {
|
|
|
421
399
|
const entry = toc.find((e) => e.fileTag === fileTag);
|
|
422
400
|
if (!entry) return null;
|
|
423
401
|
|
|
424
|
-
const
|
|
425
|
-
if (!filePath) return null;
|
|
426
|
-
|
|
427
|
-
const { content } = this.readExperienceFile(entry.fileHash);
|
|
428
|
-
const extracted = extractHeadingSection(content, sectionIndex);
|
|
402
|
+
const extracted = this.readSectionByHash(entry.fileHash, sectionIndex);
|
|
429
403
|
if (!extracted) return null;
|
|
430
404
|
|
|
431
405
|
return { title: extracted.title, url: entry.url, content: extracted.body, fileHash: entry.fileHash };
|
|
@@ -433,49 +407,22 @@ export class ExperienceTracker {
|
|
|
433
407
|
}
|
|
434
408
|
|
|
435
409
|
function listTocHeadings(content: string): { index: number; level: 2 | 3; title: string }[] {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const heading = token as Tokens.Heading;
|
|
442
|
-
if (heading.depth !== 2 && heading.depth !== 3) continue;
|
|
443
|
-
index++;
|
|
444
|
-
result.push({ index, level: heading.depth as 2 | 3, title: heading.text });
|
|
445
|
-
}
|
|
446
|
-
return result;
|
|
410
|
+
return mdq(content)
|
|
411
|
+
.query('heading')
|
|
412
|
+
.meta()
|
|
413
|
+
.filter((m) => m.depth === 2 || m.depth === 3)
|
|
414
|
+
.map((m, i) => ({ index: i + 1, level: m.depth as 2 | 3, title: m.text }));
|
|
447
415
|
}
|
|
448
416
|
|
|
449
417
|
function extractHeadingSection(content: string, sectionIndex: number): { title: string; body: string } | null {
|
|
450
|
-
const
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
454
|
-
const token = tokens[i];
|
|
455
|
-
if (token.type !== 'heading') continue;
|
|
456
|
-
const heading = token as Tokens.Heading;
|
|
457
|
-
if (heading.depth !== 2 && heading.depth !== 3) continue;
|
|
458
|
-
matching.push({ tokenIdx: i, depth: heading.depth, text: heading.text });
|
|
459
|
-
}
|
|
418
|
+
const sections = mdq(content).query('section').each();
|
|
419
|
+
const metas = mdq(content).query('section').meta();
|
|
420
|
+
const matching = metas.map((meta, i) => ({ meta, section: sections[i] })).filter((entry) => entry.meta.depth === 2 || entry.meta.depth === 3);
|
|
460
421
|
|
|
461
422
|
if (sectionIndex < 1 || sectionIndex > matching.length) return null;
|
|
462
423
|
|
|
463
424
|
const target = matching[sectionIndex - 1];
|
|
464
|
-
|
|
465
|
-
for (let j = target.tokenIdx + 1; j < tokens.length; j++) {
|
|
466
|
-
const token = tokens[j];
|
|
467
|
-
if (token.type !== 'heading') continue;
|
|
468
|
-
if ((token as Tokens.Heading).depth <= target.depth) {
|
|
469
|
-
endTokenIdx = j;
|
|
470
|
-
break;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
const body = tokens
|
|
475
|
-
.slice(target.tokenIdx, endTokenIdx)
|
|
476
|
-
.map((t) => (t as any).raw || '')
|
|
477
|
-
.join('');
|
|
478
|
-
return { title: target.text, body };
|
|
425
|
+
return { title: target.meta.text, body: target.section.text() };
|
|
479
426
|
}
|
|
480
427
|
|
|
481
428
|
function indexToLetters(index: number): string {
|
|
@@ -544,23 +491,14 @@ function generateActionContent(title: string, code: string, explanation?: string
|
|
|
544
491
|
}
|
|
545
492
|
|
|
546
493
|
function renderAsHowTo(content: string): string {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
if (text.startsWith('
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
}
|
|
556
|
-
if (text.startsWith('ACTION:')) {
|
|
557
|
-
result += `## HOW to ${text.slice(7).trim()} (single-step)\n\n`;
|
|
558
|
-
continue;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
result += (token as any).raw || '';
|
|
562
|
-
}
|
|
563
|
-
return result;
|
|
494
|
+
return mdq(content)
|
|
495
|
+
.query('h2')
|
|
496
|
+
.replaceEach((heading) => {
|
|
497
|
+
const text = heading.meta()[0]?.text.trim() || '';
|
|
498
|
+
if (text.startsWith('FLOW:')) return `## HOW to ${text.slice(5).trim()} (multi-step)\n\n`;
|
|
499
|
+
if (text.startsWith('ACTION:')) return `## HOW to ${text.slice(7).trim()} (single-step)\n\n`;
|
|
500
|
+
return heading.text();
|
|
501
|
+
});
|
|
564
502
|
}
|
|
565
503
|
|
|
566
504
|
export interface ExperienceFile {
|
package/src/explorbot.ts
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.ts';
|
|
4
3
|
import { Captain } from './ai/captain.ts';
|
|
5
4
|
import { Driller } from './ai/driller.ts';
|
|
6
5
|
import { ExperienceCompactor } from './ai/experience-compactor.ts';
|
|
@@ -28,6 +27,7 @@ import { WebPageState } from './state-manager.ts';
|
|
|
28
27
|
import { Stats } from './stats.ts';
|
|
29
28
|
import type { Suite } from './suite.ts';
|
|
30
29
|
import { Plan, type Test } from './test-plan.ts';
|
|
30
|
+
import { browserErrorMessage } from './utils/browser-errors.ts';
|
|
31
31
|
import { setVerboseMode, tag } from './utils/logger.ts';
|
|
32
32
|
import { relativeToCwd } from './utils/next-steps.ts';
|
|
33
33
|
import { sanitizeFilename } from './utils/strings.ts';
|
|
@@ -54,7 +54,6 @@ export class ExplorBot {
|
|
|
54
54
|
private config!: ExplorbotConfig;
|
|
55
55
|
private options: ExplorBotOptions;
|
|
56
56
|
private userResolveFn: UserResolveFunction | null = null;
|
|
57
|
-
public needsInput = false;
|
|
58
57
|
private currentPlan?: Plan;
|
|
59
58
|
private planFeature?: string;
|
|
60
59
|
lastPlanError: Error | null = null;
|
|
@@ -62,6 +61,8 @@ export class ExplorBot {
|
|
|
62
61
|
private agents: Record<string, any> = {};
|
|
63
62
|
private sessionPlans: Plan[] = [];
|
|
64
63
|
private lastReportedTestCount = 0;
|
|
64
|
+
private _experienceTracker?: ExperienceTracker;
|
|
65
|
+
private _knowledgeTracker?: KnowledgeTracker;
|
|
65
66
|
|
|
66
67
|
constructor(options: ExplorBotOptions = {}) {
|
|
67
68
|
this.options = options;
|
|
@@ -87,14 +88,13 @@ export class ExplorBot {
|
|
|
87
88
|
|
|
88
89
|
try {
|
|
89
90
|
await this.startProviderOnly();
|
|
90
|
-
this.explorer = new Explorer(this.config, this.provider, this.options);
|
|
91
|
+
this.explorer = new Explorer(this.config, this.provider, this.options, this.experienceTracker(), this.knowledgeTracker());
|
|
91
92
|
await this.explorer.start();
|
|
92
93
|
if (!this.options.incognito) {
|
|
93
94
|
await this.agentExperienceCompactor().autocompact();
|
|
94
95
|
}
|
|
95
|
-
if (this.userResolveFn) this.explorer.setUserResolve(this.userResolveFn);
|
|
96
96
|
} catch (error) {
|
|
97
|
-
const message =
|
|
97
|
+
const message = browserErrorMessage(error);
|
|
98
98
|
console.error('\nFailed to start:', message);
|
|
99
99
|
process.exit(1);
|
|
100
100
|
}
|
|
@@ -133,18 +133,12 @@ export class ExplorBot {
|
|
|
133
133
|
return this.explorer;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return this.explorer.getKnowledgeTracker();
|
|
139
|
-
}
|
|
140
|
-
return new KnowledgeTracker();
|
|
136
|
+
knowledgeTracker(): KnowledgeTracker {
|
|
137
|
+
return (this._knowledgeTracker ||= new KnowledgeTracker());
|
|
141
138
|
}
|
|
142
139
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return this.explorer.getStateManager().getExperienceTracker();
|
|
146
|
-
}
|
|
147
|
-
return new ExperienceTracker();
|
|
140
|
+
experienceTracker(): ExperienceTracker {
|
|
141
|
+
return (this._experienceTracker ||= new ExperienceTracker(this.knowledgeTracker()));
|
|
148
142
|
}
|
|
149
143
|
|
|
150
144
|
getConfig(): ExplorbotConfig {
|
|
@@ -154,13 +148,6 @@ export class ExplorBot {
|
|
|
154
148
|
getOptions(): ExplorBotOptions {
|
|
155
149
|
return this.options;
|
|
156
150
|
}
|
|
157
|
-
isReady(): boolean {
|
|
158
|
-
return this.explorer?.isStarted;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
getConfigParser(): ConfigParser {
|
|
162
|
-
return this.configParser;
|
|
163
|
-
}
|
|
164
151
|
|
|
165
152
|
getProvider(): AIProvider {
|
|
166
153
|
return this.provider;
|
|
@@ -173,12 +160,9 @@ export class ExplorBot {
|
|
|
173
160
|
config: this.config,
|
|
174
161
|
});
|
|
175
162
|
|
|
176
|
-
const agentEmoji = (agent as any).emoji || '';
|
|
177
163
|
const agentName = (agent as any).constructor.name.toLowerCase();
|
|
178
164
|
tag('debug').log(`Created ${agentName} agent`);
|
|
179
165
|
|
|
180
|
-
// Agent is stored by the calling method using a string key
|
|
181
|
-
|
|
182
166
|
return agent;
|
|
183
167
|
}
|
|
184
168
|
|
|
@@ -188,13 +172,13 @@ export class ExplorBot {
|
|
|
188
172
|
|
|
189
173
|
agentNavigator(): Navigator {
|
|
190
174
|
return (this.agents.navigator ||= this.createAgent(({ ai, explorer }) => {
|
|
191
|
-
return new Navigator(explorer, ai
|
|
175
|
+
return new Navigator(explorer, ai);
|
|
192
176
|
}));
|
|
193
177
|
}
|
|
194
178
|
|
|
195
179
|
agentPlanner(): Planner {
|
|
196
180
|
if (!this.agents.planner) {
|
|
197
|
-
this.agents.planner = this.createAgent(({ ai, explorer }) => new Planner(explorer, ai));
|
|
181
|
+
this.agents.planner = this.createAgent(({ ai, explorer }) => new Planner(explorer, ai, this.agentResearcher()));
|
|
198
182
|
const fisherman = this.agentFisherman();
|
|
199
183
|
if (fisherman) this.agents.planner.setFisherman(fisherman);
|
|
200
184
|
}
|
|
@@ -205,14 +189,8 @@ export class ExplorBot {
|
|
|
205
189
|
return (this.agents.pilot ||= this.createAgent(({ ai, explorer }) => {
|
|
206
190
|
const researcher = this.agentResearcher();
|
|
207
191
|
const navigator = this.agentNavigator();
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
const getState = () => {
|
|
211
|
-
const state = stateManager.getCurrentState();
|
|
212
|
-
return state ? ActionResult.fromState(state) : null;
|
|
213
|
-
};
|
|
214
|
-
const tools = createAgentTools({ explorer, researcher, navigator, experienceTracker, getState, supervisor: true });
|
|
215
|
-
return new Pilot(ai, tools, researcher, explorer, experienceTracker);
|
|
192
|
+
const tools = createAgentTools({ explorer, researcher, navigator, supervisor: true });
|
|
193
|
+
return new Pilot(ai, tools, researcher, explorer);
|
|
216
194
|
}));
|
|
217
195
|
}
|
|
218
196
|
|
|
@@ -221,13 +199,7 @@ export class ExplorBot {
|
|
|
221
199
|
this.agents.tester = this.createAgent(({ ai, explorer }) => {
|
|
222
200
|
const researcher = this.agentResearcher();
|
|
223
201
|
const navigator = this.agentNavigator();
|
|
224
|
-
const
|
|
225
|
-
const experienceTracker = stateManager.getExperienceTracker();
|
|
226
|
-
const getState = () => {
|
|
227
|
-
const state = stateManager.getCurrentState();
|
|
228
|
-
return state ? ActionResult.fromState(state) : null;
|
|
229
|
-
};
|
|
230
|
-
const tools = createAgentTools({ explorer, researcher, navigator, experienceTracker, getState });
|
|
202
|
+
const tools = createAgentTools({ explorer, researcher, navigator });
|
|
231
203
|
return new Tester(explorer, ai, researcher, navigator, tools);
|
|
232
204
|
});
|
|
233
205
|
|
|
@@ -246,16 +218,12 @@ export class ExplorBot {
|
|
|
246
218
|
agentCaptain(): Captain {
|
|
247
219
|
if (!this.agents.captain) {
|
|
248
220
|
this.agents.captain = new Captain(this);
|
|
249
|
-
|
|
250
|
-
const qm = this.agentQuartermaster();
|
|
251
|
-
if (qm) this.agents.captain.setQuartermaster(qm);
|
|
252
|
-
this.agents.captain.setHistorian(this.agentHistorian());
|
|
253
221
|
}
|
|
254
222
|
return this.agents.captain;
|
|
255
223
|
}
|
|
256
224
|
|
|
257
225
|
agentExperienceCompactor(): ExperienceCompactor {
|
|
258
|
-
return (this.agents.experienceCompactor ||= new ExperienceCompactor(this.provider, this.
|
|
226
|
+
return (this.agents.experienceCompactor ||= new ExperienceCompactor(this.provider, this.experienceTracker()));
|
|
259
227
|
}
|
|
260
228
|
|
|
261
229
|
agentQuartermaster(): Quartermaster | null {
|
|
@@ -287,11 +255,9 @@ export class ExplorBot {
|
|
|
287
255
|
this.agents.rerunner = this.createAgent(({ ai, explorer }) => {
|
|
288
256
|
const researcher = this.agentResearcher();
|
|
289
257
|
const navigator = this.agentNavigator();
|
|
290
|
-
const tools = createAgentTools({ explorer, researcher, navigator });
|
|
258
|
+
const tools = createAgentTools({ explorer, researcher, navigator, withExperience: false });
|
|
291
259
|
return new Rerunner(explorer, ai, tools);
|
|
292
260
|
});
|
|
293
|
-
const qm = this.agentQuartermaster();
|
|
294
|
-
if (qm) this.agents.rerunner.setQuartermaster(qm);
|
|
295
261
|
this.agents.rerunner.setHistorian(this.agentHistorian());
|
|
296
262
|
}
|
|
297
263
|
return this.agents.rerunner;
|
|
@@ -509,7 +475,7 @@ export class ExplorBot {
|
|
|
509
475
|
|
|
510
476
|
this.lastReportedTestCount = tests.length;
|
|
511
477
|
} catch (error) {
|
|
512
|
-
const message =
|
|
478
|
+
const message = browserErrorMessage(error);
|
|
513
479
|
tag('warning').log(`Session analysis failed: ${message}`);
|
|
514
480
|
}
|
|
515
481
|
}
|