@browserflow-ai/exploration 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/adapters/claude-cli.d.ts +57 -0
  2. package/dist/adapters/claude-cli.d.ts.map +1 -0
  3. package/dist/adapters/claude-cli.js +195 -0
  4. package/dist/adapters/claude-cli.js.map +1 -0
  5. package/dist/adapters/claude.d.ts +54 -0
  6. package/dist/adapters/claude.d.ts.map +1 -0
  7. package/dist/adapters/claude.js +160 -0
  8. package/dist/adapters/claude.js.map +1 -0
  9. package/dist/adapters/index.d.ts +6 -0
  10. package/dist/adapters/index.d.ts.map +1 -0
  11. package/dist/adapters/index.js +4 -0
  12. package/dist/adapters/index.js.map +1 -0
  13. package/dist/adapters/types.d.ts +196 -0
  14. package/dist/adapters/types.d.ts.map +1 -0
  15. package/dist/adapters/types.js +3 -0
  16. package/dist/adapters/types.js.map +1 -0
  17. package/dist/agent-browser-session.d.ts +62 -0
  18. package/dist/agent-browser-session.d.ts.map +1 -0
  19. package/dist/agent-browser-session.js +272 -0
  20. package/dist/agent-browser-session.js.map +1 -0
  21. package/dist/evidence.d.ts +111 -0
  22. package/dist/evidence.d.ts.map +1 -0
  23. package/dist/evidence.js +144 -0
  24. package/dist/evidence.js.map +1 -0
  25. package/dist/explorer.d.ts +180 -0
  26. package/dist/explorer.d.ts.map +1 -0
  27. package/dist/explorer.js +393 -0
  28. package/dist/explorer.js.map +1 -0
  29. package/dist/index.d.ts +15 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +15 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/locator-candidates.d.ts +127 -0
  34. package/dist/locator-candidates.d.ts.map +1 -0
  35. package/dist/locator-candidates.js +358 -0
  36. package/dist/locator-candidates.js.map +1 -0
  37. package/dist/step-executor.d.ts +99 -0
  38. package/dist/step-executor.d.ts.map +1 -0
  39. package/dist/step-executor.js +646 -0
  40. package/dist/step-executor.js.map +1 -0
  41. package/package.json +34 -0
  42. package/src/adapters/claude-cli.test.ts +134 -0
  43. package/src/adapters/claude-cli.ts +240 -0
  44. package/src/adapters/claude.test.ts +195 -0
  45. package/src/adapters/claude.ts +190 -0
  46. package/src/adapters/index.ts +21 -0
  47. package/src/adapters/types.ts +207 -0
  48. package/src/agent-browser-session.test.ts +369 -0
  49. package/src/agent-browser-session.ts +349 -0
  50. package/src/evidence.test.ts +239 -0
  51. package/src/evidence.ts +203 -0
  52. package/src/explorer.test.ts +321 -0
  53. package/src/explorer.ts +565 -0
  54. package/src/index.ts +51 -0
  55. package/src/locator-candidates.test.ts +602 -0
  56. package/src/locator-candidates.ts +441 -0
  57. package/src/step-executor.test.ts +696 -0
  58. package/src/step-executor.ts +783 -0
@@ -0,0 +1,190 @@
1
+ // @browserflow-ai/exploration - Claude AI Adapter
2
+
3
+ import Anthropic from '@anthropic-ai/sdk';
4
+ import type {
5
+ AIAdapter,
6
+ ExploreParams,
7
+ ExplorationOutput,
8
+ RetryParams,
9
+ EnhancedSnapshot,
10
+ FindElementResult,
11
+ } from './types';
12
+
13
+ /**
14
+ * Configuration options for the Claude adapter
15
+ */
16
+ export interface ClaudeAdapterConfig {
17
+ apiKey?: string;
18
+ model?: string;
19
+ maxTokens?: number;
20
+ }
21
+
22
+ /**
23
+ * System prompt for element finding
24
+ */
25
+ const ELEMENT_FINDER_SYSTEM_PROMPT = `You are helping find UI elements based on natural language descriptions.
26
+ Given an accessibility snapshot of a web page, identify the element that best matches the user's description.
27
+
28
+ Rules:
29
+ 1. Return the ref (like "e1", "e2") of the matching element using the select_element tool
30
+ 2. If multiple elements could match, pick the most likely based on context
31
+ 3. If no element matches, use ref "NOT_FOUND" with confidence 0
32
+ 4. Consider the element's role, name, text content, and position in the hierarchy
33
+ 5. Be precise - prefer exact matches over partial matches`;
34
+
35
+ /**
36
+ * Tool definition for structured element selection
37
+ */
38
+ const SELECT_ELEMENT_TOOL: Anthropic.Tool = {
39
+ name: 'select_element',
40
+ description: 'Select an element by its ref from the page snapshot',
41
+ input_schema: {
42
+ type: 'object' as const,
43
+ properties: {
44
+ ref: {
45
+ type: 'string',
46
+ description: 'Element ref like e1, e2, or NOT_FOUND if no match',
47
+ },
48
+ confidence: {
49
+ type: 'number',
50
+ description: 'Confidence score 0-1 for the selection',
51
+ },
52
+ reasoning: {
53
+ type: 'string',
54
+ description: 'Explanation for why this element was selected',
55
+ },
56
+ },
57
+ required: ['ref', 'confidence', 'reasoning'],
58
+ },
59
+ };
60
+
61
+ /**
62
+ * Claude adapter for AI-powered browser exploration
63
+ *
64
+ * Uses Claude's vision and reasoning capabilities to:
65
+ * - Interpret spec steps
66
+ * - Identify elements in browser snapshots
67
+ * - Execute exploration workflows
68
+ */
69
+ export class ClaudeAdapter implements AIAdapter {
70
+ readonly name = 'claude';
71
+
72
+ private client: Anthropic;
73
+ private model: string;
74
+ private maxTokens: number;
75
+
76
+ constructor(config: ClaudeAdapterConfig = {}) {
77
+ this.client = new Anthropic({
78
+ apiKey: config.apiKey,
79
+ });
80
+ this.model = config.model ?? 'claude-haiku-4-5';
81
+ this.maxTokens = config.maxTokens ?? 8192;
82
+ }
83
+
84
+ /**
85
+ * Find element from natural language query using Claude
86
+ *
87
+ * @param query - Natural language description of the element
88
+ * @param snapshot - Browser snapshot with element tree and refs
89
+ * @returns Promise resolving to element ref with reasoning
90
+ */
91
+ async findElement(query: string, snapshot: EnhancedSnapshot): Promise<FindElementResult> {
92
+ const userMessage = `Find the element matching this description: "${query}"
93
+
94
+ Current page snapshot:
95
+ ${snapshot.tree}
96
+
97
+ Available refs: ${Object.keys(snapshot.refs).join(', ')}
98
+
99
+ Use the select_element tool to return the ref of the best matching element.`;
100
+
101
+ const response = await this.client.messages.create({
102
+ model: this.model,
103
+ max_tokens: 1024,
104
+ system: ELEMENT_FINDER_SYSTEM_PROMPT,
105
+ tools: [SELECT_ELEMENT_TOOL],
106
+ tool_choice: { type: 'tool', name: 'select_element' },
107
+ messages: [
108
+ {
109
+ role: 'user',
110
+ content: userMessage,
111
+ },
112
+ ],
113
+ });
114
+
115
+ // Handle tool_use response (preferred)
116
+ for (const block of response.content) {
117
+ if (block.type === 'tool_use' && block.name === 'select_element') {
118
+ const input = block.input as { ref: string; confidence: number; reasoning: string };
119
+ return {
120
+ ref: input.ref,
121
+ confidence: input.confidence,
122
+ reasoning: input.reasoning,
123
+ };
124
+ }
125
+ }
126
+
127
+ // Fallback: parse text response if no tool_use
128
+ for (const block of response.content) {
129
+ if (block.type === 'text') {
130
+ const refMatch = block.text.match(/\be(\d+)\b/);
131
+ if (refMatch) {
132
+ return {
133
+ ref: refMatch[0],
134
+ confidence: 0.7, // Lower confidence for text extraction
135
+ reasoning: block.text,
136
+ };
137
+ }
138
+ }
139
+ }
140
+
141
+ // No element found
142
+ return {
143
+ ref: 'NOT_FOUND',
144
+ confidence: 0,
145
+ reasoning: 'Could not extract element ref from response',
146
+ };
147
+ }
148
+
149
+ /**
150
+ * Run exploration on a spec using Claude
151
+ *
152
+ * Note: The primary AI method is findElement(). This explore() method is a stub
153
+ * that returns a minimal valid structure. Full exploration is orchestrated by
154
+ * the Explorer class which uses findElement() for AI-powered element discovery.
155
+ *
156
+ * @param params - Exploration parameters including spec and browser config
157
+ * @returns Promise resolving to exploration output
158
+ */
159
+ async explore(params: ExploreParams): Promise<ExplorationOutput> {
160
+ const explorationId = `exp-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
161
+
162
+ return {
163
+ spec: params.spec.name,
164
+ specPath: params.specPath,
165
+ explorationId,
166
+ timestamp: new Date().toISOString(),
167
+ durationMs: 0,
168
+ browser: params.browser ?? 'chromium',
169
+ viewport: params.viewport ?? { width: 1280, height: 720 },
170
+ baseUrl: params.baseUrl,
171
+ steps: [],
172
+ outcomeChecks: [],
173
+ overallStatus: 'completed',
174
+ errors: [],
175
+ };
176
+ }
177
+
178
+ /**
179
+ * Retry exploration with review feedback
180
+ *
181
+ * Note: Currently delegates to explore(). Future enhancement could use
182
+ * review feedback to improve element selection accuracy.
183
+ *
184
+ * @param params - Retry parameters including previous exploration and feedback
185
+ * @returns Promise resolving to new exploration output
186
+ */
187
+ async retryWithFeedback(params: RetryParams): Promise<ExplorationOutput> {
188
+ return this.explore(params);
189
+ }
190
+ }
@@ -0,0 +1,21 @@
1
+ // @browserflow-ai/exploration - Adapter exports
2
+
3
+ export { ClaudeAdapter } from './claude';
4
+ export type { ClaudeAdapterConfig } from './claude';
5
+
6
+ export { ClaudeCliAdapter } from './claude-cli';
7
+ export type { ClaudeCliAdapterConfig } from './claude-cli';
8
+
9
+ export type {
10
+ AIAdapter,
11
+ ExploreParams,
12
+ ExplorationOutput,
13
+ RetryParams,
14
+ ReviewFeedback,
15
+ Spec,
16
+ SpecStep,
17
+ StepResult,
18
+ StepExecution,
19
+ StepScreenshots,
20
+ OutcomeCheck,
21
+ } from './types';
@@ -0,0 +1,207 @@
1
+ // @browserflow-ai/exploration - Adapter types
2
+
3
+ /**
4
+ * Represents a step execution result from AI exploration
5
+ */
6
+ export interface StepExecution {
7
+ status: 'completed' | 'failed' | 'skipped';
8
+ method: string;
9
+ elementRef?: string;
10
+ selectorUsed?: string;
11
+ durationMs: number;
12
+ error?: string | null;
13
+ }
14
+
15
+ /**
16
+ * Screenshots captured during a step
17
+ */
18
+ export interface StepScreenshots {
19
+ before: string;
20
+ after: string;
21
+ }
22
+
23
+ /**
24
+ * Result of executing a single step
25
+ */
26
+ export interface StepResult {
27
+ stepIndex: number;
28
+ specAction: Record<string, unknown>;
29
+ execution: StepExecution;
30
+ screenshots: StepScreenshots;
31
+ snapshotBefore?: Record<string, unknown>;
32
+ snapshotAfter?: Record<string, unknown>;
33
+ }
34
+
35
+ /**
36
+ * Outcome check result
37
+ */
38
+ export interface OutcomeCheck {
39
+ check: string;
40
+ expected: unknown;
41
+ actual: unknown;
42
+ passed: boolean;
43
+ }
44
+
45
+ /**
46
+ * Complete output from an exploration run
47
+ */
48
+ export interface ExplorationOutput {
49
+ spec: string;
50
+ specPath: string;
51
+ specDescription?: string; // Spec-level description for UI display
52
+ explorationId: string;
53
+ timestamp: string;
54
+ durationMs: number;
55
+ browser: string;
56
+ viewport: { width: number; height: number };
57
+ baseUrl: string;
58
+ steps: StepResult[];
59
+ outcomeChecks: OutcomeCheck[];
60
+ overallStatus: 'completed' | 'failed' | 'timeout';
61
+ errors: string[];
62
+ }
63
+
64
+ /**
65
+ * Verify check conditions
66
+ */
67
+ export interface VerifyCheck {
68
+ element_visible?: string;
69
+ element_not_visible?: string;
70
+ text_contains?: string;
71
+ text_not_contains?: string;
72
+ url_contains?: string;
73
+ element_count?: {
74
+ selector: string;
75
+ expected: number;
76
+ };
77
+ attribute?: {
78
+ selector: string;
79
+ attribute: string;
80
+ equals: string;
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Step definition from a spec file
86
+ */
87
+ export interface SpecStep {
88
+ action: string;
89
+ query?: string;
90
+ selector?: string;
91
+ ref?: string;
92
+ url?: string; // Canonical field for navigate action
93
+ to?: string; // Legacy field for navigate action (backward compat)
94
+ value?: string;
95
+ for?: string;
96
+ text?: string;
97
+ contains?: string;
98
+ timeout?: string | number;
99
+ duration?: string | number;
100
+ checks?: VerifyCheck[];
101
+ name?: string; // 1-4 word display name for UI
102
+ description?: string;
103
+ why?: string; // Rationale for this step
104
+ option?: string;
105
+ checked?: boolean;
106
+ scrollX?: number;
107
+ scrollY?: number;
108
+ [key: string]: unknown;
109
+ }
110
+
111
+ /**
112
+ * Spec file structure
113
+ */
114
+ export interface Spec {
115
+ name: string;
116
+ description?: string;
117
+ preconditions?: Record<string, unknown>;
118
+ steps: SpecStep[];
119
+ expectedOutcomes?: Record<string, unknown>[];
120
+ tags?: string[];
121
+ timeout?: string;
122
+ priority?: string;
123
+ }
124
+
125
+ /**
126
+ * Parameters for running an exploration
127
+ */
128
+ export interface ExploreParams {
129
+ spec: Spec;
130
+ specPath: string;
131
+ baseUrl: string;
132
+ browser?: 'chromium' | 'firefox' | 'webkit';
133
+ viewport?: { width: number; height: number };
134
+ timeout?: number;
135
+ outputDir: string;
136
+ sessionId?: string;
137
+ }
138
+
139
+ /**
140
+ * Parameters for retrying with feedback
141
+ */
142
+ export interface RetryParams extends ExploreParams {
143
+ previousExploration: ExplorationOutput;
144
+ reviewFeedback: ReviewFeedback;
145
+ }
146
+
147
+ /**
148
+ * Review feedback structure
149
+ */
150
+ export interface ReviewFeedback {
151
+ explorationId: string;
152
+ reviewer: string;
153
+ steps: {
154
+ stepIndex: number;
155
+ status: 'approved' | 'rejected' | 'pending';
156
+ comment?: string;
157
+ tags?: string[];
158
+ }[];
159
+ overallNotes?: string;
160
+ verdict: 'approved' | 'rejected';
161
+ }
162
+
163
+ /**
164
+ * Enhanced snapshot from browser - includes tree and element refs
165
+ */
166
+ export interface EnhancedSnapshot {
167
+ tree: string;
168
+ refs: Record<string, unknown>;
169
+ }
170
+
171
+ /**
172
+ * Result from findElement operation
173
+ */
174
+ export interface FindElementResult {
175
+ /** Element reference (e.g., "e5") or "NOT_FOUND" */
176
+ ref: string;
177
+ /** AI reasoning for why this element was selected */
178
+ reasoning: string;
179
+ /** Confidence score 0-1 */
180
+ confidence: number;
181
+ }
182
+
183
+ /**
184
+ * AI Adapter interface - defines the contract for LLM integrations
185
+ */
186
+ export interface AIAdapter {
187
+ /**
188
+ * Name of the adapter (e.g., 'claude', 'openai')
189
+ */
190
+ readonly name: string;
191
+
192
+ /**
193
+ * Run exploration on a spec
194
+ */
195
+ explore(params: ExploreParams): Promise<ExplorationOutput>;
196
+
197
+ /**
198
+ * Find element from natural language query
199
+ * Uses AI to interpret the query and match against snapshot elements
200
+ */
201
+ findElement(query: string, snapshot: EnhancedSnapshot): Promise<FindElementResult>;
202
+
203
+ /**
204
+ * Retry exploration with review feedback
205
+ */
206
+ retryWithFeedback?(params: RetryParams): Promise<ExplorationOutput>;
207
+ }