explorbot 0.1.32-beta.1 → 0.2.1
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 +35 -19
- package/bin/explorbot-cli.ts +33 -10
- package/boat/api-tester/src/config.ts +45 -3
- package/boat/doc-collector/src/ai/documentarian.ts +26 -28
- package/boat/doc-collector/src/ai/tools.ts +74 -16
- package/boat/doc-collector/src/cli.ts +1 -6
- package/boat/doc-collector/src/docbot.ts +30 -9
- package/boat/doc-collector/src/docs-renderer.ts +19 -24
- package/boat/doc-collector/src/screenshots.ts +34 -2
- package/boat/doc-collector/src/state-diagram.ts +281 -0
- package/dist/bin/explorbot-cli.js +29 -9
- package/dist/boat/api-tester/src/config.js +43 -4
- package/dist/boat/doc-collector/src/ai/documentarian.js +19 -7
- package/dist/boat/doc-collector/src/ai/tools.js +53 -16
- package/dist/boat/doc-collector/src/cli.js +1 -5
- package/dist/boat/doc-collector/src/docbot.js +27 -8
- package/dist/boat/doc-collector/src/docs-renderer.js +18 -3
- package/dist/boat/doc-collector/src/screenshots.js +30 -1
- package/dist/boat/doc-collector/src/state-diagram.js +200 -0
- package/dist/models.json +30 -0
- package/dist/package.json +23 -3
- package/dist/src/action-result.d.ts +8 -10
- package/dist/src/action-result.js +72 -164
- package/dist/src/action.d.ts +13 -12
- package/dist/src/action.js +37 -113
- package/dist/src/ai/agent.d.ts +17 -0
- package/dist/src/ai/captain/idle-mode.js +1 -1
- package/dist/src/ai/captain/test-mode.js +1 -1
- package/dist/src/ai/captain/web-mode.js +18 -29
- package/dist/src/ai/captain.d.ts +1 -3
- package/dist/src/ai/captain.js +17 -45
- package/dist/src/ai/conversation.d.ts +2 -5
- package/dist/src/ai/conversation.js +9 -28
- package/dist/src/ai/driller.d.ts +2 -17
- package/dist/src/ai/driller.js +26 -50
- 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 +4 -3
- package/dist/src/ai/historian/playwright.d.ts +4 -1
- package/dist/src/ai/historian/playwright.js +4 -3
- package/dist/src/ai/historian/screencast.d.ts +2 -1
- package/dist/src/ai/historian/screencast.js +2 -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 +10 -8
- package/dist/src/ai/historian.js +1 -5
- package/dist/src/ai/navigator.d.ts +8 -6
- package/dist/src/ai/navigator.js +34 -65
- package/dist/src/ai/pilot.d.ts +13 -8
- package/dist/src/ai/pilot.js +53 -59
- package/dist/src/ai/planner.d.ts +5 -4
- package/dist/src/ai/planner.js +66 -52
- package/dist/src/ai/provider.d.ts +4 -1
- package/dist/src/ai/provider.js +77 -137
- package/dist/src/ai/quartermaster.d.ts +22 -22
- package/dist/src/ai/quartermaster.js +8 -14
- package/dist/src/ai/rerunner.d.ts +3 -13
- package/dist/src/ai/rerunner.js +23 -44
- package/dist/src/ai/researcher/cache.js +11 -18
- package/dist/src/ai/researcher/coordinates.js +4 -3
- package/dist/src/ai/researcher/deep-analysis.d.ts +2 -0
- package/dist/src/ai/researcher/deep-analysis.js +8 -11
- package/dist/src/ai/researcher/locators.d.ts +9 -2
- package/dist/src/ai/researcher/locators.js +65 -8
- 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.d.ts +2 -0
- package/dist/src/ai/researcher/sections.js +7 -3
- package/dist/src/ai/researcher.js +40 -106
- 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 +19 -6
- package/dist/src/ai/task-agent.js +37 -33
- package/dist/src/ai/tester.d.ts +6 -14
- package/dist/src/ai/tester.js +46 -111
- package/dist/src/ai/tools.d.ts +26 -14
- package/dist/src/ai/tools.js +138 -132
- package/dist/src/command-handler.js +1 -1
- package/dist/src/commands/compact-command.d.ts +1 -1
- package/dist/src/commands/compact-command.js +6 -6
- package/dist/src/commands/context-aria-command.js +1 -1
- package/dist/src/commands/context-command.js +8 -6
- package/dist/src/commands/context-data-command.js +2 -2
- package/dist/src/commands/context-experience-command.js +2 -2
- package/dist/src/commands/context-html-command.js +2 -2
- package/dist/src/commands/context-knowledge-command.js +2 -2
- package/dist/src/commands/drill-command.js +1 -1
- package/dist/src/commands/experience-command.js +1 -1
- package/dist/src/commands/explore-command.js +4 -3
- package/dist/src/commands/freesail-command.js +2 -2
- package/dist/src/commands/knows-command.js +1 -1
- package/dist/src/commands/learn-command.js +5 -3
- package/dist/src/commands/path-command.js +1 -1
- package/dist/src/commands/research-command.js +1 -1
- package/dist/src/commands/test-command.js +1 -1
- package/dist/src/components/AddKnowledge.d.ts +2 -0
- package/dist/src/components/AddKnowledge.js +3 -7
- package/dist/src/components/App.js +3 -3
- package/dist/src/config.d.ts +19 -0
- package/dist/src/config.js +156 -10
- package/dist/src/experience-tracker.d.ts +11 -6
- package/dist/src/experience-tracker.js +82 -155
- package/dist/src/explorbot.d.ts +21 -13
- package/dist/src/explorbot.js +84 -80
- package/dist/src/explorer.d.ts +66 -102
- package/dist/src/explorer.js +332 -623
- package/dist/src/knowledge-tracker.d.ts +2 -2
- package/dist/src/knowledge-tracker.js +38 -45
- package/dist/src/state-manager.d.ts +10 -30
- package/dist/src/state-manager.js +11 -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/hooks-runner.js +2 -4
- 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/test-files.js +1 -1
- package/dist/src/utils/web-annotate.d.ts +5 -0
- package/dist/src/utils/web-annotate.js +58 -0
- package/dist/src/utils/web-eidx.d.ts +2 -0
- package/dist/src/utils/web-eidx.js +20 -0
- package/dist/src/utils/web-element.d.ts +3 -1
- package/dist/src/utils/web-element.js +32 -0
- package/dist/src/utils/web-sandbox.d.ts +4 -0
- package/dist/src/utils/web-sandbox.js +37 -0
- package/models.json +30 -0
- package/package.json +23 -3
- package/src/action-result.ts +70 -173
- package/src/action.ts +38 -132
- package/src/ai/agent.ts +20 -0
- package/src/ai/captain/idle-mode.ts +1 -1
- package/src/ai/captain/test-mode.ts +1 -1
- package/src/ai/captain/web-mode.ts +18 -30
- package/src/ai/captain.ts +17 -42
- package/src/ai/conversation.ts +9 -32
- package/src/ai/driller.ts +53 -83
- 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 +5 -3
- package/src/ai/historian/playwright.ts +7 -4
- package/src/ai/historian/screencast.ts +4 -3
- package/src/ai/historian/utils.ts +0 -1
- package/src/ai/historian.ts +6 -8
- package/src/ai/navigator.ts +39 -70
- package/src/ai/pilot.ts +59 -61
- package/src/ai/planner.ts +76 -62
- package/src/ai/provider.ts +85 -146
- package/src/ai/quartermaster.ts +39 -45
- package/src/ai/rerunner.ts +27 -50
- package/src/ai/researcher/cache.ts +10 -16
- package/src/ai/researcher/coordinates.ts +4 -3
- package/src/ai/researcher/deep-analysis.ts +10 -11
- package/src/ai/researcher/locators.ts +66 -9
- package/src/ai/researcher/parser.ts +1 -1
- package/src/ai/researcher/research-result.ts +0 -18
- package/src/ai/researcher/sections.ts +9 -3
- package/src/ai/researcher.ts +42 -119
- package/src/ai/rules.ts +24 -13
- package/src/ai/session-analyst.ts +8 -5
- package/src/ai/task-agent.ts +45 -38
- package/src/ai/tester.ts +51 -129
- package/src/ai/tools.ts +145 -153
- package/src/command-handler.ts +1 -1
- package/src/commands/compact-command.ts +7 -8
- package/src/commands/context-aria-command.ts +1 -1
- package/src/commands/context-command.ts +8 -6
- package/src/commands/context-data-command.ts +2 -2
- package/src/commands/context-experience-command.ts +2 -2
- package/src/commands/context-html-command.ts +2 -2
- package/src/commands/context-knowledge-command.ts +2 -2
- package/src/commands/drill-command.ts +1 -1
- package/src/commands/experience-command.ts +1 -1
- package/src/commands/explore-command.ts +4 -3
- package/src/commands/freesail-command.ts +2 -2
- package/src/commands/knows-command.ts +1 -1
- package/src/commands/learn-command.ts +5 -3
- package/src/commands/path-command.ts +1 -1
- package/src/commands/research-command.ts +1 -1
- package/src/commands/test-command.ts +1 -1
- package/src/components/AddKnowledge.tsx +5 -7
- package/src/components/App.tsx +3 -3
- package/src/config.ts +185 -11
- package/src/experience-tracker.ts +89 -152
- package/src/explorbot.ts +88 -83
- package/src/explorer.ts +402 -693
- package/src/knowledge-tracker.ts +42 -49
- package/src/state-manager.ts +20 -155
- 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/hooks-runner.ts +2 -4
- 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/test-files.ts +1 -1
- package/src/utils/web-annotate.ts +64 -0
- package/src/utils/web-eidx.ts +21 -0
- package/src/utils/web-element.ts +34 -0
- package/src/utils/web-sandbox.ts +43 -0
- package/dist/boat/api-tester/example/apibot.config.js +0 -30
package/src/ai/historian.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import type { ExplorbotConfig } from '../config.ts';
|
|
3
|
-
import { ExperienceTracker } from '../experience-tracker.ts';
|
|
3
|
+
import type { ExperienceTracker } from '../experience-tracker.ts';
|
|
4
|
+
import type Explorer from '../explorer.ts';
|
|
4
5
|
import type { PlaywrightRecorder } from '../playwright-recorder.ts';
|
|
5
6
|
import type { Reporter } from '../reporter.ts';
|
|
6
7
|
import type { StateManager } from '../state-manager.ts';
|
|
@@ -13,8 +14,6 @@ import { type PlaywrightMethods, WithPlaywright } from './historian/playwright.t
|
|
|
13
14
|
import { type ScreencastMethods, WithScreencast } from './historian/screencast.ts';
|
|
14
15
|
import type { Provider } from './provider.ts';
|
|
15
16
|
|
|
16
|
-
export { isNonReusableCode } from '../utils/step-analyzer.ts';
|
|
17
|
-
|
|
18
17
|
const HistorianBase = WithScreencast(WithPlaywright(WithCodeceptJS(WithExperience(Object as unknown as new (...args: any[]) => object))));
|
|
19
18
|
|
|
20
19
|
export interface Historian extends ExperienceMethods, CodeceptJSMethods, PlaywrightMethods, ScreencastMethods {}
|
|
@@ -25,13 +24,13 @@ export class Historian extends HistorianBase {
|
|
|
25
24
|
declare reporter: Reporter | undefined;
|
|
26
25
|
declare stateManager: StateManager | undefined;
|
|
27
26
|
declare config: ExplorbotConfig | undefined;
|
|
28
|
-
declare playwright: { recorder: PlaywrightRecorder;
|
|
27
|
+
declare playwright: { recorder: PlaywrightRecorder; explorer: Explorer } | undefined;
|
|
29
28
|
declare savedFiles: Set<string>;
|
|
30
29
|
|
|
31
|
-
constructor(provider: Provider, experienceTracker
|
|
30
|
+
constructor(provider: Provider, experienceTracker: ExperienceTracker, reporter?: Reporter, stateManager?: StateManager, config?: ExplorbotConfig, playwright?: { recorder: PlaywrightRecorder; explorer: Explorer }) {
|
|
32
31
|
super();
|
|
33
32
|
this.provider = provider;
|
|
34
|
-
this.experienceTracker = experienceTracker
|
|
33
|
+
this.experienceTracker = experienceTracker;
|
|
35
34
|
this.reporter = reporter;
|
|
36
35
|
this.stateManager = stateManager;
|
|
37
36
|
this.config = config;
|
|
@@ -52,11 +51,10 @@ export class Historian extends HistorianBase {
|
|
|
52
51
|
return this.isPlaywrightFramework() ? this.savePlaywrightPlanToFile(plan) : this.saveCodeceptPlanToFile(plan);
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
rewriteScenarioInFile(filePath: string, healedSteps: Array<{
|
|
54
|
+
rewriteScenarioInFile(filePath: string, healedSteps: Array<{ original: string; healed: string }>): void {
|
|
56
55
|
let content = readFileSync(filePath, 'utf-8');
|
|
57
56
|
|
|
58
57
|
for (const step of healedSteps) {
|
|
59
|
-
if (!content.includes(step.original)) continue;
|
|
60
58
|
content = content.replace(step.original, step.healed);
|
|
61
59
|
}
|
|
62
60
|
|
package/src/ai/navigator.ts
CHANGED
|
@@ -3,31 +3,30 @@ import dedent from 'dedent';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { ActionResult } from '../action-result.js';
|
|
5
5
|
import type Action from '../action.ts';
|
|
6
|
-
import {
|
|
6
|
+
import type { ExplorbotConfig } from '../config.ts';
|
|
7
|
+
import type { ExperienceTracker } from '../experience-tracker.js';
|
|
7
8
|
import Explorer from '../explorer.ts';
|
|
8
|
-
import { KnowledgeTracker } from '../knowledge-tracker.js';
|
|
9
|
-
import { type
|
|
9
|
+
import type { KnowledgeTracker } from '../knowledge-tracker.js';
|
|
10
|
+
import { type StateManager, normalizeUrl } from '../state-manager.js';
|
|
10
11
|
import { extractCodeBlocks } from '../utils/code-extractor.js';
|
|
11
12
|
import { HooksRunner } from '../utils/hooks-runner.ts';
|
|
12
13
|
import { createDebug, pluralize, tag } from '../utils/logger.js';
|
|
13
14
|
import { loop, pause } from '../utils/loop.js';
|
|
14
15
|
import { RulesLoader } from '../utils/rules-loader.ts';
|
|
15
16
|
import { extractStatePath } from '../utils/url-matcher.js';
|
|
16
|
-
import type { Agent } from './agent.js';
|
|
17
|
+
import type { Agent, AgentDeps } from './agent.js';
|
|
17
18
|
import type { Conversation } from './conversation.js';
|
|
18
|
-
import { ExperienceCompactor } from './experience-compactor.js';
|
|
19
19
|
import type { Provider } from './provider.js';
|
|
20
20
|
import { Researcher } from './researcher.ts';
|
|
21
21
|
import { actionRule, locatorRule, unexpectedPopupRule } from './rules.js';
|
|
22
22
|
import { isInteractive } from './task-agent.js';
|
|
23
|
-
import {
|
|
23
|
+
import { createLearnExperienceTool } from './tools.ts';
|
|
24
24
|
|
|
25
25
|
const debugLog = createDebug('explorbot:navigator');
|
|
26
26
|
|
|
27
27
|
class Navigator implements Agent {
|
|
28
28
|
emoji = '🧭';
|
|
29
29
|
private provider: Provider;
|
|
30
|
-
private experienceCompactor: ExperienceCompactor;
|
|
31
30
|
private knowledgeTracker: KnowledgeTracker;
|
|
32
31
|
private experienceTracker: ExperienceTracker;
|
|
33
32
|
private hooksRunner: HooksRunner;
|
|
@@ -72,26 +71,29 @@ class Navigator implements Agent {
|
|
|
72
71
|
</rules>
|
|
73
72
|
`;
|
|
74
73
|
private explorer: Explorer;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
this.
|
|
80
|
-
this.
|
|
81
|
-
this.
|
|
82
|
-
this.
|
|
74
|
+
private config: ExplorbotConfig;
|
|
75
|
+
private stateManager: StateManager;
|
|
76
|
+
|
|
77
|
+
constructor(deps: AgentDeps) {
|
|
78
|
+
this.provider = deps.ai;
|
|
79
|
+
this.explorer = deps.explorer;
|
|
80
|
+
this.config = deps.config;
|
|
81
|
+
this.stateManager = deps.stateManager;
|
|
82
|
+
this.knowledgeTracker = deps.knowledgeTracker;
|
|
83
|
+
this.experienceTracker = deps.stateManager.getExperienceTracker();
|
|
84
|
+
this.hooksRunner = new HooksRunner(deps.explorer, deps.config);
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
private get verifyAttempts(): number {
|
|
86
|
-
return this.
|
|
88
|
+
return this.config.ai?.agents?.navigator?.verifyAttempts ?? 3;
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
private get verifyTimeout(): number {
|
|
90
|
-
return this.
|
|
92
|
+
return this.config.ai?.agents?.navigator?.verifyTimeout ?? 1500;
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
private getBaseOrigin(): string | null {
|
|
94
|
-
const baseUrl = this.
|
|
96
|
+
const baseUrl = this.config.playwright.url;
|
|
95
97
|
try {
|
|
96
98
|
return new URL(baseUrl).origin;
|
|
97
99
|
} catch {
|
|
@@ -136,12 +138,12 @@ class Navigator implements Agent {
|
|
|
136
138
|
}
|
|
137
139
|
|
|
138
140
|
async visit(url: string): Promise<void> {
|
|
139
|
-
return this.
|
|
141
|
+
return this.visitOnce(url);
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
private async visitOnce(url: string): Promise<void> {
|
|
143
145
|
try {
|
|
144
|
-
const action = this.explorer.
|
|
146
|
+
const action = this.explorer.action();
|
|
145
147
|
|
|
146
148
|
await action.execute(`I.amOnPage('${url}')`);
|
|
147
149
|
|
|
@@ -174,7 +176,7 @@ class Navigator implements Agent {
|
|
|
174
176
|
throw new Error(`Navigation to ${url} failed: ${action.lastError?.message}`);
|
|
175
177
|
}
|
|
176
178
|
}
|
|
177
|
-
await this.explorer.
|
|
179
|
+
await this.explorer.capture({ screenshot: true });
|
|
178
180
|
await this.hooksRunner.runAfterHook('navigator', url);
|
|
179
181
|
} catch (error) {
|
|
180
182
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -191,22 +193,12 @@ class Navigator implements Agent {
|
|
|
191
193
|
tag('info').log('AI Navigator resolving state at', actionResult.url);
|
|
192
194
|
debugLog('Resolution message:', message);
|
|
193
195
|
|
|
194
|
-
const action = opts?.action ?? this.explorer.
|
|
196
|
+
const action = opts?.action ?? this.explorer.action();
|
|
195
197
|
const expectedUrl = opts?.expectedUrl;
|
|
196
198
|
|
|
197
|
-
|
|
199
|
+
const knowledge = this.knowledgeTracker.renderRelevantKnowledge(actionResult);
|
|
198
200
|
let experience = '';
|
|
199
201
|
|
|
200
|
-
const relevantKnowledge = this.knowledgeTracker.getRelevantKnowledge(actionResult);
|
|
201
|
-
if (relevantKnowledge.length > 0) {
|
|
202
|
-
const knowledgeContent = relevantKnowledge.map((k) => k.content).join('\n\n');
|
|
203
|
-
knowledge = `
|
|
204
|
-
<hint>
|
|
205
|
-
Here is relevant knowledge for this page:
|
|
206
|
-
${knowledgeContent}
|
|
207
|
-
</hint>`;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
202
|
if (!actionResult.isInsideIframe) {
|
|
211
203
|
const successful = this.experienceTracker.getSuccessfulExperience(actionResult);
|
|
212
204
|
if (successful.length > 0) {
|
|
@@ -354,7 +346,7 @@ class Navigator implements Agent {
|
|
|
354
346
|
codeBlockIndex++;
|
|
355
347
|
totalAttempts++;
|
|
356
348
|
|
|
357
|
-
await
|
|
349
|
+
await action.exitIframe();
|
|
358
350
|
|
|
359
351
|
const prevActionResult = action.actionResult ?? actionResult;
|
|
360
352
|
const prevHash = prevActionResult.getStateHash();
|
|
@@ -386,7 +378,7 @@ class Navigator implements Agent {
|
|
|
386
378
|
// URL did not transition to expectedUrl within timeout
|
|
387
379
|
}
|
|
388
380
|
}
|
|
389
|
-
const freshState = await this.explorer.
|
|
381
|
+
const freshState = await this.explorer.capture();
|
|
390
382
|
const currentUrl = /^https?:\/\//i.test(expectedUrl) ? freshState.fullUrl || freshState.url || '' : freshState.url || '';
|
|
391
383
|
const urlMatches = this.isSameExpectedOrigin(expectedUrl, action.stateManager) && normalizeUrl(currentUrl) === normalizeUrl(expectedUrl);
|
|
392
384
|
const stateChanged = freshState.getStateHash() !== actionResult.getStateHash();
|
|
@@ -397,7 +389,6 @@ class Navigator implements Agent {
|
|
|
397
389
|
if (freshState.getStateHash() !== prevHash) {
|
|
398
390
|
try {
|
|
399
391
|
const diff = await freshState.diff(prevActionResult);
|
|
400
|
-
await diff.calculate();
|
|
401
392
|
ariaChanges = diff.ariaChanged;
|
|
402
393
|
} catch (err) {
|
|
403
394
|
debugLog('Failed to compute pageDiff for failed URL verification:', err);
|
|
@@ -486,24 +477,17 @@ class Navigator implements Agent {
|
|
|
486
477
|
}
|
|
487
478
|
|
|
488
479
|
private buildExperienceTools(): { learnExperience: unknown } | undefined {
|
|
489
|
-
|
|
480
|
+
if (!this.experienceTracker) return undefined;
|
|
481
|
+
const stateManager = this.stateManager;
|
|
490
482
|
const getState = () => {
|
|
491
483
|
const s = stateManager.getCurrentState();
|
|
492
484
|
return s ? ActionResult.fromState(s) : null;
|
|
493
485
|
};
|
|
494
|
-
|
|
495
|
-
explorer: this.explorer,
|
|
496
|
-
researcher: null as unknown as Researcher,
|
|
497
|
-
navigator: this,
|
|
498
|
-
experienceTracker: this.experienceTracker,
|
|
499
|
-
getState,
|
|
500
|
-
});
|
|
501
|
-
if (!learnExperience) return undefined;
|
|
502
|
-
return { learnExperience };
|
|
486
|
+
return { learnExperience: createLearnExperienceTool({ getExperienceTracker: () => this.experienceTracker, getState }) };
|
|
503
487
|
}
|
|
504
488
|
|
|
505
489
|
async freeSail(opts?: { strategy?: 'deep' | 'shallow'; scope?: string; visitedUrls?: Set<string> }, actionResult?: ActionResult): Promise<{ target: string; reason: string } | null> {
|
|
506
|
-
const stateManager = this.
|
|
490
|
+
const stateManager = this.stateManager;
|
|
507
491
|
const state = stateManager.getCurrentState();
|
|
508
492
|
if (!state) {
|
|
509
493
|
return null;
|
|
@@ -641,26 +625,11 @@ class Navigator implements Agent {
|
|
|
641
625
|
return { verified: cachedVerification, successfulCodes: [], assertionSteps: [], totalAttempted: 0 };
|
|
642
626
|
}
|
|
643
627
|
|
|
644
|
-
|
|
628
|
+
const knowledge = this.knowledgeTracker.renderRelevantKnowledge(actionResult);
|
|
645
629
|
let experience = '';
|
|
646
630
|
|
|
647
|
-
const relevantKnowledge = this.knowledgeTracker.getRelevantKnowledge(actionResult);
|
|
648
|
-
if (relevantKnowledge.length > 0) {
|
|
649
|
-
const knowledgeContent = relevantKnowledge.map((k) => k.content).join('\n\n');
|
|
650
|
-
knowledge = `
|
|
651
|
-
<hint>
|
|
652
|
-
Here is relevant knowledge for this page:
|
|
653
|
-
${knowledgeContent}
|
|
654
|
-
</hint>`;
|
|
655
|
-
}
|
|
656
|
-
|
|
657
631
|
if (!actionResult.isInsideIframe) {
|
|
658
|
-
|
|
659
|
-
if (toc.length > 0) {
|
|
660
|
-
const totalSections = toc.reduce((sum, entry) => sum + entry.sections.length, 0);
|
|
661
|
-
tag('operation').log(`Found ${toc.length} experience ${pluralize(toc.length, 'file')} (${totalSections} sections) for: ${actionResult.url}`);
|
|
662
|
-
experience = renderExperienceToc(toc);
|
|
663
|
-
}
|
|
632
|
+
experience = this.experienceTracker.renderExperienceTocFor(actionResult);
|
|
664
633
|
}
|
|
665
634
|
|
|
666
635
|
const priorVerifications = Object.entries(actionResult.verifications ?? {});
|
|
@@ -726,11 +695,11 @@ class Navigator implements Agent {
|
|
|
726
695
|
const successfulCodes: string[] = [];
|
|
727
696
|
const assertionSteps: Array<{ name: string; args: any[] }> = [];
|
|
728
697
|
|
|
729
|
-
const action = this.explorer.
|
|
698
|
+
const action = this.explorer.action();
|
|
730
699
|
let failures = 0;
|
|
731
700
|
|
|
732
|
-
const page = this.explorer.
|
|
733
|
-
const originalTimeout = this.
|
|
701
|
+
const page = this.explorer.page;
|
|
702
|
+
const originalTimeout = this.config.playwright.timeout ?? 3000;
|
|
734
703
|
page?.setDefaultTimeout(this.verifyTimeout);
|
|
735
704
|
|
|
736
705
|
try {
|
|
@@ -762,9 +731,9 @@ class Navigator implements Agent {
|
|
|
762
731
|
return;
|
|
763
732
|
}
|
|
764
733
|
|
|
765
|
-
await
|
|
734
|
+
await action.exitIframe();
|
|
766
735
|
|
|
767
|
-
const verified = await action.attempt(codeBlock, message
|
|
736
|
+
const verified = await action.attempt(codeBlock, message);
|
|
768
737
|
|
|
769
738
|
if (verified) {
|
|
770
739
|
tag('success').log('Verification passed');
|
|
@@ -800,7 +769,7 @@ class Navigator implements Agent {
|
|
|
800
769
|
if (alreadyVerified) verified = true;
|
|
801
770
|
|
|
802
771
|
actionResult.addVerification(message, verified);
|
|
803
|
-
this.
|
|
772
|
+
this.stateManager.updateState(actionResult);
|
|
804
773
|
|
|
805
774
|
return { verified, successfulCodes, assertionSteps, totalAttempted };
|
|
806
775
|
}
|
package/src/ai/pilot.ts
CHANGED
|
@@ -2,9 +2,11 @@ import { tool } from 'ai';
|
|
|
2
2
|
import dedent from 'dedent';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { ActionResult } from '../action-result.ts';
|
|
5
|
+
import type { RequestStore } from '../api/request-store.ts';
|
|
5
6
|
import { ConfigParser } from '../config.ts';
|
|
6
|
-
import { type ExperienceTracker, renderExperienceToc } from '../experience-tracker.ts';
|
|
7
7
|
import type Explorer from '../explorer.ts';
|
|
8
|
+
import type { PlaywrightRecorder } from '../playwright-recorder.ts';
|
|
9
|
+
import type { StateManager } from '../state-manager.ts';
|
|
8
10
|
import { type Test, TestResult } from '../test-plan.ts';
|
|
9
11
|
import { collectInteractiveNodes, detectFocusArea, extractFocusedElement } from '../utils/aria.ts';
|
|
10
12
|
import { ErrorPageError } from '../utils/error-page.ts';
|
|
@@ -12,7 +14,7 @@ import { createDebug, tag } from '../utils/logger.ts';
|
|
|
12
14
|
|
|
13
15
|
const debugLog = createDebug('explorbot:pilot');
|
|
14
16
|
import { truncateJson } from '../utils/strings.ts';
|
|
15
|
-
import type { Agent } from './agent.ts';
|
|
17
|
+
import type { Agent, AgentDeps } from './agent.ts';
|
|
16
18
|
import type { Conversation } from './conversation.ts';
|
|
17
19
|
import type { Fisherman } from './fisherman.ts';
|
|
18
20
|
import type { Navigator } from './navigator.ts';
|
|
@@ -31,15 +33,19 @@ export class Pilot implements Agent {
|
|
|
31
33
|
private conversation: Conversation | null = null;
|
|
32
34
|
private researcher: Researcher;
|
|
33
35
|
private explorer: Explorer;
|
|
36
|
+
private stateManager: StateManager;
|
|
37
|
+
private requestStore: RequestStore;
|
|
38
|
+
private playwrightRecorder: PlaywrightRecorder;
|
|
34
39
|
private fisherman: Fisherman | null = null;
|
|
35
|
-
private experienceTracker: ExperienceTracker | null;
|
|
36
40
|
|
|
37
|
-
constructor(
|
|
38
|
-
this.provider =
|
|
41
|
+
constructor(deps: AgentDeps, agentTools: any, researcher: Researcher) {
|
|
42
|
+
this.provider = deps.ai;
|
|
39
43
|
this.agentTools = agentTools;
|
|
40
44
|
this.researcher = researcher;
|
|
41
|
-
this.explorer = explorer;
|
|
42
|
-
this.
|
|
45
|
+
this.explorer = deps.explorer;
|
|
46
|
+
this.stateManager = deps.stateManager;
|
|
47
|
+
this.requestStore = deps.requestStore;
|
|
48
|
+
this.playwrightRecorder = deps.playwrightRecorder;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
setFisherman(fisherman: Fisherman): void {
|
|
@@ -94,7 +100,7 @@ export class Pilot implements Agent {
|
|
|
94
100
|
let screenshotState: ActionResult | null = null;
|
|
95
101
|
if (type === 'finish' && this.provider.hasVision()) {
|
|
96
102
|
try {
|
|
97
|
-
screenshotState = await this.explorer.
|
|
103
|
+
screenshotState = await this.explorer.capture({ screenshot: true });
|
|
98
104
|
if (screenshotState.screenshot) {
|
|
99
105
|
visualAnalysis = (await this.researcher.answerQuestionAboutScreenshot(screenshotState, `Describe current page state relevant to: ${task.scenario}`)) || '';
|
|
100
106
|
}
|
|
@@ -111,7 +117,7 @@ export class Pilot implements Agent {
|
|
|
111
117
|
.string()
|
|
112
118
|
.nullable()
|
|
113
119
|
.describe(
|
|
114
|
-
'REQUIRED whenever decision is "pass" — a one-sentence natural-language claim about the current page that, if true, proves the scenario goal (e.g., "New
|
|
120
|
+
'REQUIRED whenever decision is "pass" — a one-sentence natural-language claim about the current page that, if true, proves the scenario goal (e.g., "New item \\"Foo\\" is visible in the items list"). NOT code: do not write I.*, expect(), .then(), grabTitle, or any JavaScript. Navigator translates the claim into CodeceptJS assertions and runs them; passing assertions are saved to the generated test file. Also use when evidence is insufficient before deciding pass/fail. Leave null for "continue", "fail", or "skipped".'
|
|
115
121
|
),
|
|
116
122
|
});
|
|
117
123
|
|
|
@@ -147,7 +153,7 @@ export class Pilot implements Agent {
|
|
|
147
153
|
- Mixed evidence + final state shows success → pass. Mixed + final state unclear → continue with guidance.
|
|
148
154
|
|
|
149
155
|
When deciding "pass", you MUST also set requestVerification to a one-sentence natural-language
|
|
150
|
-
claim about the current page (e.g., "New
|
|
156
|
+
claim about the current page (e.g., "New item Foo is visible in the items list"). NOT
|
|
151
157
|
code — do not write I.*, expect(), .then(), or any JavaScript. Choose the strongest single
|
|
152
158
|
piece of evidence (a unique element/text that exists ONLY because the scenario succeeded).
|
|
153
159
|
Navigator translates the claim into CodeceptJS assertions; without it the generated test has
|
|
@@ -157,7 +163,7 @@ export class Pilot implements Agent {
|
|
|
157
163
|
const messages = [
|
|
158
164
|
{
|
|
159
165
|
role: 'system' as const,
|
|
160
|
-
content: this.buildVerdictSystemPrompt(
|
|
166
|
+
content: this.buildVerdictSystemPrompt(task),
|
|
161
167
|
},
|
|
162
168
|
{ role: 'user' as const, content: userContent },
|
|
163
169
|
];
|
|
@@ -178,7 +184,7 @@ export class Pilot implements Agent {
|
|
|
178
184
|
tag('substep').log(`Pilot requesting verification: ${result.requestVerification}`);
|
|
179
185
|
const verifyResult = await navigator.verifyState(result.requestVerification, currentState).catch(() => null);
|
|
180
186
|
if (verifyResult?.verified && verifyResult.assertionSteps?.length) {
|
|
181
|
-
this.
|
|
187
|
+
this.playwrightRecorder.recordVerification(verifyResult.assertionSteps);
|
|
182
188
|
}
|
|
183
189
|
}
|
|
184
190
|
|
|
@@ -304,10 +310,8 @@ export class Pilot implements Agent {
|
|
|
304
310
|
}
|
|
305
311
|
}
|
|
306
312
|
|
|
307
|
-
private buildSharedEvidenceRules(
|
|
313
|
+
private buildSharedEvidenceRules(): string {
|
|
308
314
|
return dedent`
|
|
309
|
-
SCENARIO: ${task.scenario}
|
|
310
|
-
|
|
311
315
|
EVIDENCE PRIORITY (strict):
|
|
312
316
|
1) Final observable state proving the scenario goal
|
|
313
317
|
2) verify()/see() results in the LAST few actions before stop/finish
|
|
@@ -340,6 +344,12 @@ export class Pilot implements Agent {
|
|
|
340
344
|
|
|
341
345
|
Expected results are MILESTONES, not the goal. Never fail because a milestone (toast, icon, styling)
|
|
342
346
|
didn't match if the scenario goal IS accomplished.
|
|
347
|
+
`;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
private buildTaskContext(task: Test): string {
|
|
351
|
+
return dedent`
|
|
352
|
+
SCENARIO: ${task.scenario}
|
|
343
353
|
|
|
344
354
|
${this.buildDeletionScope(task)}
|
|
345
355
|
|
|
@@ -354,7 +364,7 @@ export class Pilot implements Agent {
|
|
|
354
364
|
iteration's work, but server-side side effects (records created, forms submitted) persist.
|
|
355
365
|
Unnecessary resets create duplicate data and infinite loops.
|
|
356
366
|
|
|
357
|
-
${this.buildSharedEvidenceRules(
|
|
367
|
+
${this.buildSharedEvidenceRules()}
|
|
358
368
|
|
|
359
369
|
DECISION:
|
|
360
370
|
- "allow": current page cannot host the scenario, irrecoverable error, or no path back.
|
|
@@ -371,17 +381,19 @@ export class Pilot implements Agent {
|
|
|
371
381
|
to verify, how to record. Do not suggest repeating actions that already succeeded.
|
|
372
382
|
If progress is blocked only because the page lacks target data for the scenario, prefer precondition()
|
|
373
383
|
over repeated UI attempts.
|
|
384
|
+
|
|
385
|
+
${this.buildTaskContext(task)}
|
|
374
386
|
`;
|
|
375
387
|
}
|
|
376
388
|
|
|
377
|
-
private buildVerdictSystemPrompt(
|
|
389
|
+
private buildVerdictSystemPrompt(task: Test): string {
|
|
378
390
|
return dedent`
|
|
379
|
-
You are Pilot — final decision maker for test pass/fail.
|
|
380
|
-
|
|
391
|
+
You are Pilot — final decision maker for test pass/fail. Review the evidence and commit to a
|
|
392
|
+
verdict; "continue" only when evidence is genuinely insufficient.
|
|
381
393
|
|
|
382
394
|
${capabilityGroundingRule}
|
|
383
395
|
|
|
384
|
-
${this.buildSharedEvidenceRules(
|
|
396
|
+
${this.buildSharedEvidenceRules()}
|
|
385
397
|
|
|
386
398
|
DECISION:
|
|
387
399
|
- "pass": scenario goal is fully accomplished. Set requestVerification to a one-sentence claim about
|
|
@@ -400,6 +412,8 @@ export class Pilot implements Agent {
|
|
|
400
412
|
reason field: one short sentence, maximum 120 characters. Do NOT restate the decision
|
|
401
413
|
("scenario goal achieved/not achieved"). State what happened: what was verified, what failed,
|
|
402
414
|
or what evidence was found.
|
|
415
|
+
|
|
416
|
+
${this.buildTaskContext(task)}
|
|
403
417
|
`;
|
|
404
418
|
}
|
|
405
419
|
|
|
@@ -429,10 +443,6 @@ export class Pilot implements Agent {
|
|
|
429
443
|
|
|
430
444
|
FIRST: Decide if precondition() is needed.
|
|
431
445
|
|
|
432
|
-
${capabilityGroundingRule}
|
|
433
|
-
|
|
434
|
-
${dataProtectionRules}
|
|
435
|
-
|
|
436
446
|
Call precondition() WHEN:
|
|
437
447
|
- The scenario edits/deletes/modifies an item, and you want a DISPOSABLE item to act on safely
|
|
438
448
|
- The scenario needs specific data clearly NOT on the current page (e.g., items with specific statuses for filtering)
|
|
@@ -481,9 +491,6 @@ export class Pilot implements Agent {
|
|
|
481
491
|
.slice(-this.stepsToReview);
|
|
482
492
|
const actionsContext = this.formatActions(toolCalls);
|
|
483
493
|
|
|
484
|
-
this.conversation.cleanupTag('page_summary', '...trimmed...', 1);
|
|
485
|
-
this.conversation.cleanupTag('recent_actions', '...trimmed...', 2);
|
|
486
|
-
|
|
487
494
|
return this.sendToPilot(
|
|
488
495
|
dedent`
|
|
489
496
|
Navigated to new page.
|
|
@@ -505,7 +512,8 @@ export class Pilot implements Agent {
|
|
|
505
512
|
|
|
506
513
|
First: evaluate whether this navigation makes sense for the scenario goal. If the page is unrelated, instruct Tester to back() or reset(). Then plan next steps.
|
|
507
514
|
`,
|
|
508
|
-
'pilot.reviewNewPage'
|
|
515
|
+
'pilot.reviewNewPage',
|
|
516
|
+
{ task }
|
|
509
517
|
);
|
|
510
518
|
}
|
|
511
519
|
|
|
@@ -522,8 +530,6 @@ export class Pilot implements Agent {
|
|
|
522
530
|
const actionsContext = this.formatActions(toolCalls);
|
|
523
531
|
const stateContext = this.buildStateContext(currentState);
|
|
524
532
|
|
|
525
|
-
this.conversation.cleanupTag('recent_actions', '...trimmed...', 2);
|
|
526
|
-
|
|
527
533
|
const hasFailures = toolCalls.length === 0 || toolCalls.some((t) => !t.wasSuccessful);
|
|
528
534
|
|
|
529
535
|
const text = await this.sendToPilot(
|
|
@@ -561,7 +567,7 @@ export class Pilot implements Agent {
|
|
|
561
567
|
return `CHECKED: ${checked.length > 0 ? checked.join(', ') : 'none'}\nREMAINING: ${remaining.length > 0 ? remaining.join(', ') : 'none'}`;
|
|
562
568
|
}
|
|
563
569
|
|
|
564
|
-
private async sendToPilot(userText: string, functionId: string, opts: { tools?: boolean; maxToolRoundtrips?: number; task
|
|
570
|
+
private async sendToPilot(userText: string, functionId: string, opts: { tools?: boolean; maxToolRoundtrips?: number; task: Test }): Promise<string> {
|
|
565
571
|
debugLog(`sendToPilot: ${functionId}, tools: ${!!opts.tools}, roundtrips: ${opts.maxToolRoundtrips ?? 0}`);
|
|
566
572
|
|
|
567
573
|
let finalUserText = userText;
|
|
@@ -572,19 +578,14 @@ export class Pilot implements Agent {
|
|
|
572
578
|
}
|
|
573
579
|
}
|
|
574
580
|
this.conversation!.addUserText(finalUserText);
|
|
575
|
-
let tools: any;
|
|
576
|
-
if (opts.tools) {
|
|
577
|
-
tools = this.pickPlanningTools();
|
|
578
|
-
}
|
|
579
581
|
|
|
580
|
-
|
|
581
|
-
tools = { ...tools, ...this.buildPreconditionTool(opts.task) };
|
|
582
|
-
}
|
|
582
|
+
const tools = { ...this.pickPlanningTools(), ...this.buildPreconditionTool(opts.task) };
|
|
583
583
|
|
|
584
584
|
const result = await this.provider.invokeConversation(this.conversation!, tools, {
|
|
585
585
|
maxToolRoundtrips: opts.maxToolRoundtrips ?? 0,
|
|
586
|
+
toolChoice: opts.tools ? 'auto' : 'none',
|
|
586
587
|
agentName: 'pilot',
|
|
587
|
-
stopWhen:
|
|
588
|
+
stopWhen: () => opts.task.hasFinished,
|
|
588
589
|
telemetry: { functionId },
|
|
589
590
|
});
|
|
590
591
|
const text = result?.response?.text || '';
|
|
@@ -603,12 +604,9 @@ export class Pilot implements Agent {
|
|
|
603
604
|
}
|
|
604
605
|
|
|
605
606
|
private getExperienceToc(): string {
|
|
606
|
-
|
|
607
|
-
const state = this.explorer.getStateManager().getCurrentState();
|
|
607
|
+
const state = this.stateManager.getCurrentState();
|
|
608
608
|
if (!state) return '';
|
|
609
|
-
|
|
610
|
-
const toc = this.experienceTracker.getExperienceTableOfContents(actionResult);
|
|
611
|
-
return renderExperienceToc(toc);
|
|
609
|
+
return this.stateManager.getExperienceTracker().renderExperienceTocFor(ActionResult.fromState(state));
|
|
612
610
|
}
|
|
613
611
|
|
|
614
612
|
private pickPlanningTools() {
|
|
@@ -671,7 +669,7 @@ export class Pilot implements Agent {
|
|
|
671
669
|
private async checkDataAvailability(task: Test, requestedData: string, fishermanReason: string | undefined): Promise<string | null> {
|
|
672
670
|
if (!this.provider.hasVision()) return null;
|
|
673
671
|
|
|
674
|
-
const screenshotState = await this.explorer.
|
|
672
|
+
const screenshotState = await this.explorer.capture({ screenshot: true }).catch(() => null);
|
|
675
673
|
if (!screenshotState?.screenshot) return null;
|
|
676
674
|
|
|
677
675
|
const question = dedent`
|
|
@@ -723,8 +721,8 @@ export class Pilot implements Agent {
|
|
|
723
721
|
lines.push('modal: none');
|
|
724
722
|
}
|
|
725
723
|
|
|
726
|
-
|
|
727
|
-
|
|
724
|
+
const tabs = this.stateManager.otherTabs;
|
|
725
|
+
if (tabs.length > 0) {
|
|
728
726
|
lines.push(`other tabs: ${tabs.length} (${tabs.map((t) => `${t.url} - ${t.title}`).join(', ')})`);
|
|
729
727
|
} else {
|
|
730
728
|
lines.push('other tabs: none');
|
|
@@ -747,7 +745,7 @@ export class Pilot implements Agent {
|
|
|
747
745
|
lines.push('console errors: none');
|
|
748
746
|
}
|
|
749
747
|
|
|
750
|
-
const failedRequests = this.
|
|
748
|
+
const failedRequests = this.requestStore.getFailedRequests();
|
|
751
749
|
if (failedRequests.length > 0) {
|
|
752
750
|
const sample = failedRequests
|
|
753
751
|
.slice(-5)
|
|
@@ -828,7 +826,7 @@ export class Pilot implements Agent {
|
|
|
828
826
|
|
|
829
827
|
private formatSessionLog(testerConversation: Conversation): string {
|
|
830
828
|
const executions = testerConversation.getToolExecutions().filter((t) => !META_TOOLS.includes(t.toolName));
|
|
831
|
-
const stateHistory = this.
|
|
829
|
+
const stateHistory = this.stateManager.getStateHistory();
|
|
832
830
|
|
|
833
831
|
const initialUrl = stateHistory[0]?.toState?.url || '';
|
|
834
832
|
let currentUrl = initialUrl;
|
|
@@ -985,16 +983,6 @@ export class Pilot implements Agent {
|
|
|
985
983
|
return dedent`
|
|
986
984
|
You are Pilot — a supervisor that detects problems and intervenes only when needed.
|
|
987
985
|
|
|
988
|
-
SCENARIO: ${task.scenario}
|
|
989
|
-
START URL: ${initialState.url}
|
|
990
|
-
PAGE: ${initialState.title || ''} | ${initialState.h1 || ''}
|
|
991
|
-
|
|
992
|
-
EXPECTED RESULTS:
|
|
993
|
-
${task.expected.map((e) => `- ${e}`).join('\n')}
|
|
994
|
-
|
|
995
|
-
PLANNED STEPS:
|
|
996
|
-
${stepsText}
|
|
997
|
-
|
|
998
986
|
Your job: plan, review new pages, detect stuck patterns, suggest concrete next steps. Track which
|
|
999
987
|
expectations are checked. When things go well, encourage briefly and let Tester continue. The current
|
|
1000
988
|
page is usually richer than the page summary lists — prefer exploring it before navigating away.
|
|
@@ -1056,14 +1044,24 @@ export class Pilot implements Agent {
|
|
|
1056
1044
|
|
|
1057
1045
|
${dataProtectionRules}
|
|
1058
1046
|
|
|
1059
|
-
Describe WHAT to create, not what exists. RIGHT: precondition("1
|
|
1060
|
-
precondition("1
|
|
1047
|
+
Describe WHAT to create, not what exists. RIGHT: precondition("1 post"). WRONG:
|
|
1048
|
+
precondition("1 post named Updated Post with existing comments"). Keep descriptions short.
|
|
1061
1049
|
|
|
1062
1050
|
Response format:
|
|
1063
1051
|
PROGRESS: <1 sentence assessment>
|
|
1064
1052
|
NEXT: <specific actionable instruction for Tester>
|
|
1065
1053
|
|
|
1066
1054
|
Keep user-facing reasons concise: one short sentence, maximum 120 characters, evidence only, no repeated verdict wording.
|
|
1055
|
+
|
|
1056
|
+
SCENARIO: ${task.scenario}
|
|
1057
|
+
START URL: ${initialState.url}
|
|
1058
|
+
PAGE: ${initialState.title || ''} | ${initialState.h1 || ''}
|
|
1059
|
+
|
|
1060
|
+
EXPECTED RESULTS:
|
|
1061
|
+
${task.expected.map((e) => `- ${e}`).join('\n')}
|
|
1062
|
+
|
|
1063
|
+
PLANNED STEPS:
|
|
1064
|
+
${stepsText}
|
|
1067
1065
|
`;
|
|
1068
1066
|
}
|
|
1069
1067
|
}
|