testchimp-runner-core 0.0.21 → 0.0.23
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/VISION_DIAGNOSTICS_IMPROVEMENTS.md +336 -0
- package/dist/credit-usage-service.d.ts +9 -0
- package/dist/credit-usage-service.d.ts.map +1 -1
- package/dist/credit-usage-service.js +20 -5
- package/dist/credit-usage-service.js.map +1 -1
- package/dist/execution-service.d.ts +7 -2
- package/dist/execution-service.d.ts.map +1 -1
- package/dist/execution-service.js +91 -36
- package/dist/execution-service.js.map +1 -1
- package/dist/index.d.ts +30 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +91 -26
- package/dist/index.js.map +1 -1
- package/dist/llm-facade.d.ts +64 -8
- package/dist/llm-facade.d.ts.map +1 -1
- package/dist/llm-facade.js +361 -109
- package/dist/llm-facade.js.map +1 -1
- package/dist/llm-provider.d.ts +39 -0
- package/dist/llm-provider.d.ts.map +1 -0
- package/dist/llm-provider.js +7 -0
- package/dist/llm-provider.js.map +1 -0
- package/dist/model-constants.d.ts +21 -0
- package/dist/model-constants.d.ts.map +1 -0
- package/dist/model-constants.js +24 -0
- package/dist/model-constants.js.map +1 -0
- package/dist/orchestrator/index.d.ts +8 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/index.js +23 -0
- package/dist/orchestrator/index.js.map +1 -0
- package/dist/orchestrator/orchestrator-agent.d.ts +66 -0
- package/dist/orchestrator/orchestrator-agent.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator-agent.js +855 -0
- package/dist/orchestrator/orchestrator-agent.js.map +1 -0
- package/dist/orchestrator/tool-registry.d.ts +74 -0
- package/dist/orchestrator/tool-registry.d.ts.map +1 -0
- package/dist/orchestrator/tool-registry.js +131 -0
- package/dist/orchestrator/tool-registry.js.map +1 -0
- package/dist/orchestrator/tools/check-page-ready.d.ts +13 -0
- package/dist/orchestrator/tools/check-page-ready.d.ts.map +1 -0
- package/dist/orchestrator/tools/check-page-ready.js +72 -0
- package/dist/orchestrator/tools/check-page-ready.js.map +1 -0
- package/dist/orchestrator/tools/extract-data.d.ts +13 -0
- package/dist/orchestrator/tools/extract-data.d.ts.map +1 -0
- package/dist/orchestrator/tools/extract-data.js +84 -0
- package/dist/orchestrator/tools/extract-data.js.map +1 -0
- package/dist/orchestrator/tools/index.d.ts +10 -0
- package/dist/orchestrator/tools/index.d.ts.map +1 -0
- package/dist/orchestrator/tools/index.js +18 -0
- package/dist/orchestrator/tools/index.js.map +1 -0
- package/dist/orchestrator/tools/inspect-page.d.ts +13 -0
- package/dist/orchestrator/tools/inspect-page.d.ts.map +1 -0
- package/dist/orchestrator/tools/inspect-page.js +39 -0
- package/dist/orchestrator/tools/inspect-page.js.map +1 -0
- package/dist/orchestrator/tools/recall-history.d.ts +13 -0
- package/dist/orchestrator/tools/recall-history.d.ts.map +1 -0
- package/dist/orchestrator/tools/recall-history.js +64 -0
- package/dist/orchestrator/tools/recall-history.js.map +1 -0
- package/dist/orchestrator/tools/take-screenshot.d.ts +15 -0
- package/dist/orchestrator/tools/take-screenshot.d.ts.map +1 -0
- package/dist/orchestrator/tools/take-screenshot.js +112 -0
- package/dist/orchestrator/tools/take-screenshot.js.map +1 -0
- package/dist/orchestrator/types.d.ts +133 -0
- package/dist/orchestrator/types.d.ts.map +1 -0
- package/dist/orchestrator/types.js +28 -0
- package/dist/orchestrator/types.js.map +1 -0
- package/dist/playwright-mcp-service.d.ts +9 -0
- package/dist/playwright-mcp-service.d.ts.map +1 -1
- package/dist/playwright-mcp-service.js +20 -5
- package/dist/playwright-mcp-service.js.map +1 -1
- package/dist/progress-reporter.d.ts +97 -0
- package/dist/progress-reporter.d.ts.map +1 -0
- package/dist/progress-reporter.js +18 -0
- package/dist/progress-reporter.js.map +1 -0
- package/dist/prompts.d.ts +24 -0
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +593 -68
- package/dist/prompts.js.map +1 -1
- package/dist/providers/backend-proxy-llm-provider.d.ts +25 -0
- package/dist/providers/backend-proxy-llm-provider.d.ts.map +1 -0
- package/dist/providers/backend-proxy-llm-provider.js +76 -0
- package/dist/providers/backend-proxy-llm-provider.js.map +1 -0
- package/dist/providers/local-llm-provider.d.ts +21 -0
- package/dist/providers/local-llm-provider.d.ts.map +1 -0
- package/dist/providers/local-llm-provider.js +35 -0
- package/dist/providers/local-llm-provider.js.map +1 -0
- package/dist/scenario-service.d.ts +27 -1
- package/dist/scenario-service.d.ts.map +1 -1
- package/dist/scenario-service.js +48 -12
- package/dist/scenario-service.js.map +1 -1
- package/dist/scenario-worker-class.d.ts +39 -2
- package/dist/scenario-worker-class.d.ts.map +1 -1
- package/dist/scenario-worker-class.js +614 -86
- package/dist/scenario-worker-class.js.map +1 -1
- package/dist/script-utils.d.ts +2 -0
- package/dist/script-utils.d.ts.map +1 -1
- package/dist/script-utils.js +44 -4
- package/dist/script-utils.js.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/browser-utils.d.ts +20 -1
- package/dist/utils/browser-utils.d.ts.map +1 -1
- package/dist/utils/browser-utils.js +102 -51
- package/dist/utils/browser-utils.js.map +1 -1
- package/dist/utils/page-info-utils.d.ts +23 -4
- package/dist/utils/page-info-utils.d.ts.map +1 -1
- package/dist/utils/page-info-utils.js +174 -43
- package/dist/utils/page-info-utils.js.map +1 -1
- package/package.json +1 -2
- package/plandocs/HUMAN_LIKE_IMPROVEMENTS.md +642 -0
- package/plandocs/MULTI_AGENT_ARCHITECTURE_REVIEW.md +844 -0
- package/plandocs/ORCHESTRATOR_MVP_SUMMARY.md +539 -0
- package/plandocs/PHASE1_ABSTRACTION_COMPLETE.md +241 -0
- package/plandocs/PHASE1_FINAL_STATUS.md +210 -0
- package/plandocs/PLANNING_SESSION_SUMMARY.md +372 -0
- package/plandocs/SCRIPT_CLEANUP_FEATURE.md +201 -0
- package/plandocs/SCRIPT_GENERATION_ARCHITECTURE.md +364 -0
- package/plandocs/SELECTOR_IMPROVEMENTS.md +139 -0
- package/src/credit-usage-service.ts +23 -5
- package/src/execution-service.ts +152 -42
- package/src/index.ts +169 -26
- package/src/llm-facade.ts +500 -126
- package/src/llm-provider.ts +43 -0
- package/src/model-constants.ts +23 -0
- package/src/orchestrator/index.ts +33 -0
- package/src/orchestrator/orchestrator-agent.ts +1037 -0
- package/src/orchestrator/tool-registry.ts +182 -0
- package/src/orchestrator/tools/check-page-ready.ts +75 -0
- package/src/orchestrator/tools/extract-data.ts +92 -0
- package/src/orchestrator/tools/index.ts +11 -0
- package/src/orchestrator/tools/inspect-page.ts +42 -0
- package/src/orchestrator/tools/recall-history.ts +72 -0
- package/src/orchestrator/tools/take-screenshot.ts +128 -0
- package/src/orchestrator/types.ts +200 -0
- package/src/playwright-mcp-service.ts +23 -5
- package/src/progress-reporter.ts +109 -0
- package/src/prompts.ts +606 -69
- package/src/providers/backend-proxy-llm-provider.ts +91 -0
- package/src/providers/local-llm-provider.ts +38 -0
- package/src/scenario-service.ts +83 -13
- package/src/scenario-worker-class.ts +740 -72
- package/src/script-utils.ts +50 -5
- package/src/types.ts +13 -1
- package/src/utils/browser-utils.ts +123 -51
- package/src/utils/page-info-utils.ts +210 -53
- package/testchimp-runner-core-0.0.22.tgz +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Registry - Dynamic tool registration and prompt generation
|
|
3
|
+
* Tools can be added at runtime and their descriptions are automatically included in agent prompts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ToolCall, ToolResult } from './types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Tool parameter definition
|
|
10
|
+
*/
|
|
11
|
+
export interface ToolParameter {
|
|
12
|
+
name: string;
|
|
13
|
+
type: 'string' | 'number' | 'boolean' | 'object';
|
|
14
|
+
description: string;
|
|
15
|
+
required: boolean;
|
|
16
|
+
default?: any;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Tool definition
|
|
21
|
+
*/
|
|
22
|
+
export interface Tool {
|
|
23
|
+
name: string;
|
|
24
|
+
description: string;
|
|
25
|
+
parameters: ToolParameter[];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Execute the tool
|
|
29
|
+
* @param params Tool parameters
|
|
30
|
+
* @param context Execution context (page, memory, etc.)
|
|
31
|
+
*/
|
|
32
|
+
execute(params: Record<string, any>, context: ToolExecutionContext): Promise<ToolResult>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Context provided to tool execution
|
|
37
|
+
*/
|
|
38
|
+
export interface ToolExecutionContext {
|
|
39
|
+
page: any; // Playwright Page
|
|
40
|
+
memory: any; // JourneyMemory
|
|
41
|
+
stepNumber: number;
|
|
42
|
+
logger?: (message: string, level?: 'log' | 'error' | 'warn') => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Tool Registry - manages available tools and generates prompts
|
|
47
|
+
*/
|
|
48
|
+
export class ToolRegistry {
|
|
49
|
+
private tools: Map<string, Tool> = new Map();
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Register a tool
|
|
53
|
+
*/
|
|
54
|
+
register(tool: Tool): void {
|
|
55
|
+
this.tools.set(tool.name, tool);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Unregister a tool
|
|
60
|
+
*/
|
|
61
|
+
unregister(toolName: string): void {
|
|
62
|
+
this.tools.delete(toolName);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get a tool by name
|
|
67
|
+
*/
|
|
68
|
+
get(toolName: string): Tool | undefined {
|
|
69
|
+
return this.tools.get(toolName);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get all registered tools
|
|
74
|
+
*/
|
|
75
|
+
getAll(): Tool[] {
|
|
76
|
+
return Array.from(this.tools.values());
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Execute a tool
|
|
81
|
+
*/
|
|
82
|
+
async execute(toolCall: ToolCall, context: ToolExecutionContext): Promise<ToolResult> {
|
|
83
|
+
const tool = this.tools.get(toolCall.name);
|
|
84
|
+
|
|
85
|
+
if (!tool) {
|
|
86
|
+
return {
|
|
87
|
+
success: false,
|
|
88
|
+
error: `Tool '${toolCall.name}' not found`
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Validate required parameters
|
|
93
|
+
const missingParams = tool.parameters
|
|
94
|
+
.filter(p => p.required && !(p.name in toolCall.params))
|
|
95
|
+
.map(p => p.name);
|
|
96
|
+
|
|
97
|
+
if (missingParams.length > 0) {
|
|
98
|
+
return {
|
|
99
|
+
success: false,
|
|
100
|
+
error: `Missing required parameters: ${missingParams.join(', ')}`
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Apply defaults for missing optional parameters
|
|
105
|
+
const params = { ...toolCall.params };
|
|
106
|
+
for (const param of tool.parameters) {
|
|
107
|
+
if (!param.required && !(param.name in params) && param.default !== undefined) {
|
|
108
|
+
params[param.name] = param.default;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
return await tool.execute(params, context);
|
|
114
|
+
} catch (error: any) {
|
|
115
|
+
return {
|
|
116
|
+
success: false,
|
|
117
|
+
error: `Tool execution failed: ${error.message}`
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Generate tool descriptions for agent prompt
|
|
124
|
+
* Returns formatted text describing all available tools
|
|
125
|
+
*/
|
|
126
|
+
generateToolDescriptions(): string {
|
|
127
|
+
if (this.tools.size === 0) {
|
|
128
|
+
return 'No tools available.';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const descriptions: string[] = [];
|
|
132
|
+
|
|
133
|
+
descriptions.push('AVAILABLE TOOLS:');
|
|
134
|
+
descriptions.push('');
|
|
135
|
+
|
|
136
|
+
for (const tool of this.tools.values()) {
|
|
137
|
+
descriptions.push(`${tool.name}:`);
|
|
138
|
+
descriptions.push(` Description: ${tool.description}`);
|
|
139
|
+
|
|
140
|
+
if (tool.parameters.length > 0) {
|
|
141
|
+
descriptions.push(` Parameters:`);
|
|
142
|
+
for (const param of tool.parameters) {
|
|
143
|
+
const required = param.required ? '(required)' : '(optional)';
|
|
144
|
+
const defaultVal = param.default !== undefined ? ` [default: ${JSON.stringify(param.default)}]` : '';
|
|
145
|
+
descriptions.push(` - ${param.name} (${param.type}) ${required}: ${param.description}${defaultVal}`);
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
descriptions.push(` Parameters: none`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
descriptions.push('');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
descriptions.push('To use a tool, include it in your "toolCalls" array with the tool name and parameters.');
|
|
155
|
+
descriptions.push('');
|
|
156
|
+
|
|
157
|
+
return descriptions.join('\n');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Generate JSON schema for tool calls (for structured output)
|
|
162
|
+
*/
|
|
163
|
+
generateToolCallSchema(): any {
|
|
164
|
+
return {
|
|
165
|
+
type: 'array',
|
|
166
|
+
items: {
|
|
167
|
+
type: 'object',
|
|
168
|
+
properties: {
|
|
169
|
+
name: {
|
|
170
|
+
type: 'string',
|
|
171
|
+
enum: Array.from(this.tools.keys())
|
|
172
|
+
},
|
|
173
|
+
params: {
|
|
174
|
+
type: 'object'
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
required: ['name', 'params']
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check Page Ready Tool
|
|
3
|
+
* Verify that page has finished loading and is interactive
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Tool, ToolParameter, ToolExecutionContext } from '../tool-registry';
|
|
7
|
+
import { ToolResult } from '../types';
|
|
8
|
+
|
|
9
|
+
export class CheckPageReadyTool implements Tool {
|
|
10
|
+
name = 'check_page_ready';
|
|
11
|
+
description = 'Verify that the page has finished loading and is ready for interaction. Checks load state, no pending network requests, and DOM stability.';
|
|
12
|
+
|
|
13
|
+
parameters: ToolParameter[] = [
|
|
14
|
+
{
|
|
15
|
+
name: 'timeout',
|
|
16
|
+
type: 'number',
|
|
17
|
+
description: 'Maximum time to wait for page to be ready (milliseconds)',
|
|
18
|
+
required: false,
|
|
19
|
+
default: 5000
|
|
20
|
+
}
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
async execute(params: Record<string, any>, context: ToolExecutionContext): Promise<ToolResult> {
|
|
24
|
+
const { page, logger } = context;
|
|
25
|
+
const timeout = params.timeout || 5000;
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
logger?.(`[CheckPageReady] Verifying page is ready (timeout: ${timeout}ms)`, 'log');
|
|
29
|
+
|
|
30
|
+
// Wait for load state
|
|
31
|
+
await page.waitForLoadState('domcontentloaded', { timeout });
|
|
32
|
+
|
|
33
|
+
// Check if page is interactive (use string to avoid TypeScript DOM type issues)
|
|
34
|
+
const isInteractive = await page.evaluate(() => {
|
|
35
|
+
const doc = (globalThis as any).document;
|
|
36
|
+
return doc.readyState === 'complete' || doc.readyState === 'interactive';
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Get page state info
|
|
40
|
+
const url = page.url();
|
|
41
|
+
const title = await page.title();
|
|
42
|
+
|
|
43
|
+
if (isInteractive) {
|
|
44
|
+
logger?.(`[CheckPageReady] ✓ Page is ready: ${title} (${url})`, 'log');
|
|
45
|
+
return {
|
|
46
|
+
success: true,
|
|
47
|
+
data: {
|
|
48
|
+
ready: true,
|
|
49
|
+
url,
|
|
50
|
+
title,
|
|
51
|
+
readyState: 'complete'
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
} else {
|
|
55
|
+
logger?.(`[CheckPageReady] ⚠ Page not fully ready yet`, 'warn');
|
|
56
|
+
return {
|
|
57
|
+
success: true,
|
|
58
|
+
data: {
|
|
59
|
+
ready: false,
|
|
60
|
+
url,
|
|
61
|
+
title,
|
|
62
|
+
readyState: 'loading'
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
} catch (error: any) {
|
|
67
|
+
logger?.(`[CheckPageReady] ✗ Failed: ${error.message}`, 'error');
|
|
68
|
+
return {
|
|
69
|
+
success: false,
|
|
70
|
+
error: `Page ready check failed: ${error.message}`
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract Data Tool
|
|
3
|
+
* Save data from page for use in later steps
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Tool, ToolParameter, ToolExecutionContext } from '../tool-registry';
|
|
7
|
+
import { ToolResult } from '../types';
|
|
8
|
+
import { JourneyMemory } from '../types';
|
|
9
|
+
|
|
10
|
+
export class ExtractDataTool implements Tool {
|
|
11
|
+
name = 'extract_data';
|
|
12
|
+
description = 'Extract and save data from the page for use in later steps. Useful for capturing IDs, usernames, confirmation codes, etc.';
|
|
13
|
+
|
|
14
|
+
parameters: ToolParameter[] = [
|
|
15
|
+
{
|
|
16
|
+
name: 'selector',
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'CSS selector or text content to extract',
|
|
19
|
+
required: true
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'dataName',
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Name to save the data under (e.g., "userId", "confirmationCode")',
|
|
25
|
+
required: true
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'attribute',
|
|
29
|
+
type: 'string',
|
|
30
|
+
description: 'Optional: Extract specific attribute instead of text content (e.g., "href", "value")',
|
|
31
|
+
required: false
|
|
32
|
+
}
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
async execute(params: Record<string, any>, context: ToolExecutionContext): Promise<ToolResult> {
|
|
36
|
+
const { page, memory, logger } = context;
|
|
37
|
+
const journeyMemory = memory as JourneyMemory;
|
|
38
|
+
const { selector, dataName, attribute } = params;
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
logger?.(`[ExtractData] Extracting "${dataName}" from selector: ${selector}`, 'log');
|
|
42
|
+
|
|
43
|
+
// Try to find element
|
|
44
|
+
const element = await page.locator(selector).first();
|
|
45
|
+
const count = await page.locator(selector).count();
|
|
46
|
+
|
|
47
|
+
if (count === 0) {
|
|
48
|
+
logger?.(`[ExtractData] ✗ Element not found: ${selector}`, 'error');
|
|
49
|
+
return {
|
|
50
|
+
success: false,
|
|
51
|
+
error: `Element not found: ${selector}`
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Extract data
|
|
56
|
+
let extractedValue: string;
|
|
57
|
+
if (attribute) {
|
|
58
|
+
extractedValue = await element.getAttribute(attribute) || '';
|
|
59
|
+
} else {
|
|
60
|
+
extractedValue = await element.textContent() || '';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
extractedValue = extractedValue.trim();
|
|
64
|
+
|
|
65
|
+
if (!extractedValue) {
|
|
66
|
+
logger?.(`[ExtractData] ⚠ Extracted empty value from ${selector}`, 'warn');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Save to memory
|
|
70
|
+
journeyMemory.extractedData[dataName] = extractedValue;
|
|
71
|
+
|
|
72
|
+
logger?.(`[ExtractData] ✓ Saved "${dataName}" = "${extractedValue.substring(0, 50)}${extractedValue.length > 50 ? '...' : ''}"`, 'log');
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
success: true,
|
|
76
|
+
data: {
|
|
77
|
+
dataName,
|
|
78
|
+
value: extractedValue,
|
|
79
|
+
selector,
|
|
80
|
+
attribute
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
} catch (error: any) {
|
|
84
|
+
logger?.(`[ExtractData] ✗ Failed: ${error.message}`, 'error');
|
|
85
|
+
return {
|
|
86
|
+
success: false,
|
|
87
|
+
error: `Data extraction failed: ${error.message}`
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool exports - 5 information-gathering tools
|
|
3
|
+
* Note: State changes (navigation, clicks, fills) are done via Playwright commands, not tools
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { TakeScreenshotTool } from './take-screenshot';
|
|
7
|
+
export { RecallHistoryTool } from './recall-history';
|
|
8
|
+
export { InspectPageTool } from './inspect-page';
|
|
9
|
+
export { CheckPageReadyTool } from './check-page-ready';
|
|
10
|
+
export { ExtractDataTool } from './extract-data';
|
|
11
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inspect Page Tool
|
|
3
|
+
* Get current DOM snapshot (might be redundant since DOM is always-provided, but keeps extensibility)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Tool, ToolParameter, ToolExecutionContext } from '../tool-registry';
|
|
7
|
+
import { ToolResult } from '../types';
|
|
8
|
+
import { getEnhancedPageInfo } from '../../utils/page-info-utils';
|
|
9
|
+
|
|
10
|
+
export class InspectPageTool implements Tool {
|
|
11
|
+
name = 'inspect_page';
|
|
12
|
+
description = 'Get current page DOM snapshot. Note: Current page info is already provided in context, so this tool is mainly for forcing a fresh snapshot if needed.';
|
|
13
|
+
|
|
14
|
+
parameters: ToolParameter[] = [];
|
|
15
|
+
|
|
16
|
+
async execute(params: Record<string, any>, context: ToolExecutionContext): Promise<ToolResult> {
|
|
17
|
+
const { page, logger } = context;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
logger?.(`[InspectPage] Getting fresh DOM snapshot`, 'log');
|
|
21
|
+
|
|
22
|
+
const pageInfo = await getEnhancedPageInfo(page);
|
|
23
|
+
|
|
24
|
+
logger?.(`[InspectPage] ✓ DOM snapshot retrieved`, 'log');
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
success: true,
|
|
28
|
+
data: {
|
|
29
|
+
pageInfo,
|
|
30
|
+
url: page.url()
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
} catch (error: any) {
|
|
34
|
+
logger?.(`[InspectPage] ✗ Failed: ${error.message}`, 'error');
|
|
35
|
+
return {
|
|
36
|
+
success: false,
|
|
37
|
+
error: `Page inspection failed: ${error.message}`
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recall History Tool
|
|
3
|
+
* Access deeper history beyond the recent 6-7 steps always provided
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Tool, ToolParameter, ToolExecutionContext } from '../tool-registry';
|
|
7
|
+
import { ToolResult } from '../types';
|
|
8
|
+
import { JourneyMemory } from '../types';
|
|
9
|
+
|
|
10
|
+
export class RecallHistoryTool implements Tool {
|
|
11
|
+
name = 'recall_history';
|
|
12
|
+
description = 'Access journey history beyond the recent 6-7 steps always provided in context. Use when you need to remember what happened earlier in the journey.';
|
|
13
|
+
|
|
14
|
+
parameters: ToolParameter[] = [
|
|
15
|
+
{
|
|
16
|
+
name: 'maxSteps',
|
|
17
|
+
type: 'number',
|
|
18
|
+
description: 'Maximum number of historical steps to retrieve (from most recent backwards)',
|
|
19
|
+
required: false,
|
|
20
|
+
default: 10
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'query',
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Optional: Filter for specific actions (e.g., "login", "form fill")',
|
|
26
|
+
required: false
|
|
27
|
+
}
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
async execute(params: Record<string, any>, context: ToolExecutionContext): Promise<ToolResult> {
|
|
31
|
+
const { memory, logger } = context;
|
|
32
|
+
const journeyMemory = memory as JourneyMemory;
|
|
33
|
+
const maxSteps = params.maxSteps || 10;
|
|
34
|
+
const query = params.query?.toLowerCase();
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
logger?.(`[RecallHistory] Retrieving up to ${maxSteps} historical steps${query ? ` matching "${query}"` : ''}`, 'log');
|
|
38
|
+
|
|
39
|
+
let steps = [...journeyMemory.history];
|
|
40
|
+
|
|
41
|
+
// Filter by query if provided
|
|
42
|
+
if (query) {
|
|
43
|
+
steps = steps.filter(step =>
|
|
44
|
+
step.action.toLowerCase().includes(query) ||
|
|
45
|
+
step.code.toLowerCase().includes(query) ||
|
|
46
|
+
step.observation.toLowerCase().includes(query)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Take most recent N steps
|
|
51
|
+
const recentSteps = steps.slice(-maxSteps);
|
|
52
|
+
|
|
53
|
+
logger?.(`[RecallHistory] ✓ Found ${recentSteps.length} historical steps`, 'log');
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
success: true,
|
|
57
|
+
data: {
|
|
58
|
+
steps: recentSteps,
|
|
59
|
+
totalHistorySize: journeyMemory.history.length,
|
|
60
|
+
filtered: !!query
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
} catch (error: any) {
|
|
64
|
+
logger?.(`[RecallHistory] ✗ Failed: ${error.message}`, 'error');
|
|
65
|
+
return {
|
|
66
|
+
success: false,
|
|
67
|
+
error: `History recall failed: ${error.message}`
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Take Screenshot Tool
|
|
3
|
+
* Captures current page state visually and analyzes it with DOM snapshot
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Tool, ToolParameter, ToolExecutionContext } from '../tool-registry';
|
|
7
|
+
import { ToolResult } from '../types';
|
|
8
|
+
import { getEnhancedPageInfo } from '../../utils/page-info-utils';
|
|
9
|
+
import { VISION_MODEL } from '../../model-constants';
|
|
10
|
+
|
|
11
|
+
export class TakeScreenshotTool implements Tool {
|
|
12
|
+
name = 'take_screenshot';
|
|
13
|
+
description = 'Capture a screenshot and analyze it with DOM snapshot to get actionable selector recommendations. Use when you need to see the actual page visually to find the right elements. Returns text-based analysis with selector suggestions.';
|
|
14
|
+
|
|
15
|
+
// LLM facade will be injected
|
|
16
|
+
private llmFacade?: any;
|
|
17
|
+
|
|
18
|
+
setLLMFacade(llmFacade: any): void {
|
|
19
|
+
this.llmFacade = llmFacade;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
parameters: ToolParameter[] = [
|
|
23
|
+
{
|
|
24
|
+
name: 'isFullPage',
|
|
25
|
+
type: 'boolean',
|
|
26
|
+
description: 'If true, captures entire page (scrolling). If false, captures only viewport. DEFAULT: true (recommended for finding elements that may be below fold)',
|
|
27
|
+
required: false,
|
|
28
|
+
default: true
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'purpose',
|
|
32
|
+
type: 'string',
|
|
33
|
+
description: 'What you are trying to find or understand from the screenshot',
|
|
34
|
+
required: false
|
|
35
|
+
}
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
async execute(params: Record<string, any>, context: ToolExecutionContext): Promise<ToolResult> {
|
|
39
|
+
const { page, logger } = context;
|
|
40
|
+
const isFullPage = params.isFullPage !== undefined ? params.isFullPage : true; // Default: true (capture full page)
|
|
41
|
+
const purpose = params.purpose || 'Analyze page structure and elements';
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
logger?.(`[TakeScreenshot] Capturing ${isFullPage ? 'full page' : 'viewport'} screenshot for: ${purpose}`, 'log');
|
|
45
|
+
|
|
46
|
+
// Capture screenshot (JPEG 60 quality for smaller size)
|
|
47
|
+
const screenshotBuffer = await page.screenshot({
|
|
48
|
+
fullPage: isFullPage,
|
|
49
|
+
type: 'jpeg',
|
|
50
|
+
quality: 60
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Convert to data URL
|
|
54
|
+
const base64 = screenshotBuffer.toString('base64');
|
|
55
|
+
const dataUrl = `data:image/jpeg;base64,${base64}`;
|
|
56
|
+
|
|
57
|
+
logger?.(`[TakeScreenshot] ✓ Screenshot captured (${Math.round(base64.length / 1024)}KB), analyzing with DOM...`, 'log');
|
|
58
|
+
|
|
59
|
+
// Get DOM snapshot for correlation (already has ARIA tree + interactive elements with bboxes)
|
|
60
|
+
const pageInfo = await getEnhancedPageInfo(page);
|
|
61
|
+
|
|
62
|
+
// Analyze screenshot with structured DOM via LLM
|
|
63
|
+
let analysis = 'Screenshot captured. Use DOM snapshot in context to find selectors.';
|
|
64
|
+
|
|
65
|
+
if (this.llmFacade) {
|
|
66
|
+
try {
|
|
67
|
+
logger?.(`[TakeScreenshot] Calling LLM for vision analysis...`, 'log');
|
|
68
|
+
|
|
69
|
+
const analysisPrompt = `Analyze screenshot WITH DOM structure to identify correct Playwright selectors.
|
|
70
|
+
|
|
71
|
+
PURPOSE: ${purpose}
|
|
72
|
+
|
|
73
|
+
INTERACTIVE ELEMENTS (with positions and suggested selectors):
|
|
74
|
+
${pageInfo.formattedElements}
|
|
75
|
+
|
|
76
|
+
ARIA TREE (hierarchical structure):
|
|
77
|
+
${JSON.stringify(pageInfo.ariaSnapshot, null, 2).substring(0, 2000)}
|
|
78
|
+
|
|
79
|
+
TASK:
|
|
80
|
+
1. Look at screenshot - identify the visual elements you need for: ${purpose}
|
|
81
|
+
2. Match visual position with bounding boxes above
|
|
82
|
+
3. Recommend SEMANTIC SELECTORS FIRST: getByRole, getByLabel, getByPlaceholder, getByText
|
|
83
|
+
4. AVOID auto-generated IDs with unicode (e.g., #«r3»-form-item)
|
|
84
|
+
|
|
85
|
+
Output format:
|
|
86
|
+
"For [visual element description]:
|
|
87
|
+
Try: [semantic selector from list - prefer getByRole/getByLabel]
|
|
88
|
+
Or: [alternative selector]"
|
|
89
|
+
|
|
90
|
+
Be concise. Only 2-3 recommendations. Prioritize user-friendly semantic selectors.`;
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
const llmResponse = await this.llmFacade.llmProvider.callLLM({
|
|
94
|
+
model: VISION_MODEL,
|
|
95
|
+
systemPrompt: 'You are a vision analysis expert for web automation. Analyze screenshots with DOM snapshots to recommend working Playwright selectors. ALWAYS prioritize semantic selectors (getByRole, getByLabel, getByText) over CSS selectors with auto-generated IDs.',
|
|
96
|
+
userPrompt: analysisPrompt,
|
|
97
|
+
imageUrl: dataUrl
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
analysis = llmResponse.answer || analysis;
|
|
101
|
+
logger?.(`[TakeScreenshot] ✓ Vision analysis complete`, 'log');
|
|
102
|
+
|
|
103
|
+
} catch (error: any) {
|
|
104
|
+
logger?.(`[TakeScreenshot] ⚠ Vision analysis failed, returning raw screenshot: ${error.message}`, 'warn');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
success: true,
|
|
110
|
+
data: {
|
|
111
|
+
screenshot: dataUrl,
|
|
112
|
+
isFullPage,
|
|
113
|
+
size: base64.length,
|
|
114
|
+
interactiveElementCount: pageInfo.interactiveElements.length
|
|
115
|
+
},
|
|
116
|
+
learning: analysis // Text-based analysis with selector recommendations
|
|
117
|
+
};
|
|
118
|
+
} catch (error: any) {
|
|
119
|
+
logger?.(`[TakeScreenshot] ✗ Failed: ${error.message}`, 'error');
|
|
120
|
+
return {
|
|
121
|
+
success: false,
|
|
122
|
+
error: `Screenshot capture failed: ${error.message}`
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|