@zibby/core 0.1.20 → 0.1.22

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 (167) hide show
  1. package/dist/agents/base.js +17 -0
  2. package/dist/backend-client.js +1 -0
  3. package/dist/constants/tool-names.js +1 -0
  4. package/dist/constants/zibby-scratch.js +1 -0
  5. package/dist/constants.js +1 -0
  6. package/dist/enrichment/base.js +1 -0
  7. package/dist/enrichment/enrichers/accessibility-enricher.js +1 -0
  8. package/dist/enrichment/enrichers/dom-enricher.js +1 -0
  9. package/dist/enrichment/enrichers/page-state-enricher.js +1 -0
  10. package/dist/enrichment/enrichers/position-enricher.js +1 -0
  11. package/dist/enrichment/index.js +1 -0
  12. package/dist/enrichment/mcp-integration.js +1 -0
  13. package/dist/enrichment/mcp-ref-enricher.js +1 -0
  14. package/dist/enrichment/pipeline.js +3 -0
  15. package/dist/enrichment/trace-text-enricher.js +1 -0
  16. package/dist/framework/agents/assistant-strategy.js +5 -0
  17. package/dist/framework/agents/base.js +1 -0
  18. package/dist/framework/agents/claude-strategy.js +4 -0
  19. package/dist/framework/agents/codex-strategy.js +4 -0
  20. package/dist/framework/agents/cursor-strategy.js +32 -0
  21. package/dist/framework/agents/gemini-strategy.js +11 -0
  22. package/dist/framework/agents/index.js +13 -0
  23. package/dist/framework/agents/middleware/assistant-round-pipeline.js +3 -0
  24. package/dist/framework/agents/providers/base.js +1 -0
  25. package/dist/framework/agents/providers/index.js +1 -0
  26. package/dist/framework/agents/providers/openai-transport.js +2 -0
  27. package/dist/framework/agents/providers/openai.js +1 -0
  28. package/dist/framework/agents/providers/transport-base.js +1 -0
  29. package/dist/framework/agents/utils/auth-resolver.js +1 -0
  30. package/dist/framework/agents/utils/cursor-output-formatter.js +1 -0
  31. package/dist/framework/agents/utils/openai-proxy-formatter.js +9 -0
  32. package/dist/framework/agents/utils/payload-budget.js +3 -0
  33. package/dist/framework/agents/utils/structured-output-formatter.js +21 -0
  34. package/dist/framework/code-generator.js +10 -0
  35. package/dist/framework/constants.js +1 -0
  36. package/dist/framework/context-loader.js +5 -0
  37. package/dist/framework/function-bridge.js +2 -0
  38. package/dist/framework/function-skill-registry.js +1 -0
  39. package/dist/framework/graph-compiler.js +1 -0
  40. package/dist/framework/graph.js +5 -0
  41. package/dist/framework/index.js +1 -0
  42. package/dist/framework/mcp-client.js +2 -0
  43. package/dist/framework/node-registry.js +9 -0
  44. package/dist/framework/node.js +5 -0
  45. package/dist/framework/output-parser.js +3 -0
  46. package/dist/framework/skill-registry.js +1 -0
  47. package/dist/framework/state-utils.js +1 -0
  48. package/dist/framework/state.js +1 -0
  49. package/dist/framework/tool-resolver.js +1 -0
  50. package/dist/index.js +8 -0
  51. package/dist/runtime/generation/base.js +1 -0
  52. package/dist/runtime/generation/index.js +3 -0
  53. package/dist/runtime/generation/mcp-ref-strategy.js +41 -0
  54. package/dist/runtime/generation/stable-id-strategy.js +16 -0
  55. package/dist/runtime/stable-id-runtime.js +1 -0
  56. package/dist/runtime/verification/base.js +1 -0
  57. package/dist/runtime/verification/index.js +3 -0
  58. package/dist/runtime/verification/playwright-json-strategy.js +1 -0
  59. package/dist/runtime/zibby-runtime.js +1 -0
  60. package/dist/sync/index.js +1 -0
  61. package/dist/sync/uploader.js +1 -0
  62. package/dist/tools/run-playwright-test.js +5 -0
  63. package/dist/utils/adf-converter.js +7 -0
  64. package/dist/utils/ast-utils.js +1 -0
  65. package/dist/utils/ci-setup.js +5 -0
  66. package/dist/utils/cursor-mcp-isolated-home.js +1 -0
  67. package/dist/utils/cursor-utils.js +18 -0
  68. package/dist/utils/live-frame-discovery.js +1 -0
  69. package/dist/utils/logger.js +1 -0
  70. package/dist/utils/mcp-config-writer.js +10 -0
  71. package/dist/utils/mission-control-from-run-states.js +1 -0
  72. package/dist/utils/node-schema-parser.js +1 -0
  73. package/dist/utils/parallel-config.js +1 -0
  74. package/dist/utils/post-process-events.js +1 -0
  75. package/dist/utils/result-handler.js +1 -0
  76. package/{src → dist}/utils/ripple-effect.js +3 -12
  77. package/dist/utils/run-capacity-coordinator.js +1 -0
  78. package/dist/utils/run-capacity-queue.js +2 -0
  79. package/dist/utils/run-index-merge.js +1 -0
  80. package/dist/utils/run-index-post-cli.js +1 -0
  81. package/dist/utils/run-registry.js +3 -0
  82. package/dist/utils/run-state-session.js +2 -0
  83. package/dist/utils/selector-generator.js +4 -0
  84. package/dist/utils/session-state-constants.js +1 -0
  85. package/dist/utils/session-state-live-runs.js +1 -0
  86. package/dist/utils/streaming-parser.js +4 -0
  87. package/dist/utils/test-post-processor.js +18 -0
  88. package/dist/utils/timeline.js +14 -0
  89. package/dist/utils/trace-parser.js +2 -0
  90. package/dist/utils/video-organizer.js +3 -0
  91. package/package.json +49 -35
  92. package/templates/browser-test-automation/README.md +29 -7
  93. package/templates/browser-test-automation/chat.mjs +36 -0
  94. package/templates/browser-test-automation/graph.mjs +5 -9
  95. package/templates/browser-test-automation/nodes/execute-live.mjs +30 -58
  96. package/templates/browser-test-automation/nodes/generate-script.mjs +32 -12
  97. package/templates/browser-test-automation/nodes/utils.mjs +153 -10
  98. package/templates/browser-test-automation/pipeline-ids.js +12 -0
  99. package/templates/browser-test-automation/result-handler.mjs +78 -2
  100. package/templates/browser-test-automation/run-index.mjs +418 -0
  101. package/scripts/export-default-workflows.js +0 -51
  102. package/scripts/patch-cursor-mcp.js +0 -174
  103. package/scripts/setup-ci.sh +0 -115
  104. package/scripts/setup-official-playwright-mcp.sh +0 -226
  105. package/scripts/test-with-video.sh +0 -49
  106. package/src/agents/base.js +0 -361
  107. package/src/constants.js +0 -47
  108. package/src/enrichment/base.js +0 -49
  109. package/src/enrichment/enrichers/accessibility-enricher.js +0 -197
  110. package/src/enrichment/enrichers/dom-enricher.js +0 -171
  111. package/src/enrichment/enrichers/page-state-enricher.js +0 -129
  112. package/src/enrichment/enrichers/position-enricher.js +0 -67
  113. package/src/enrichment/index.js +0 -96
  114. package/src/enrichment/mcp-integration.js +0 -149
  115. package/src/enrichment/mcp-ref-enricher.js +0 -78
  116. package/src/enrichment/pipeline.js +0 -192
  117. package/src/enrichment/trace-text-enricher.js +0 -115
  118. package/src/framework/AGENTS.md +0 -98
  119. package/src/framework/agents/base.js +0 -72
  120. package/src/framework/agents/claude-strategy.js +0 -278
  121. package/src/framework/agents/cursor-strategy.js +0 -540
  122. package/src/framework/agents/index.js +0 -105
  123. package/src/framework/agents/utils/cursor-output-formatter.js +0 -67
  124. package/src/framework/agents/utils/openai-proxy-formatter.js +0 -249
  125. package/src/framework/code-generator.js +0 -301
  126. package/src/framework/constants.js +0 -33
  127. package/src/framework/context-loader.js +0 -101
  128. package/src/framework/function-bridge.js +0 -78
  129. package/src/framework/function-skill-registry.js +0 -20
  130. package/src/framework/graph-compiler.js +0 -342
  131. package/src/framework/graph.js +0 -610
  132. package/src/framework/index.js +0 -28
  133. package/src/framework/node-registry.js +0 -163
  134. package/src/framework/node.js +0 -259
  135. package/src/framework/output-parser.js +0 -71
  136. package/src/framework/skill-registry.js +0 -55
  137. package/src/framework/state-utils.js +0 -52
  138. package/src/framework/state.js +0 -67
  139. package/src/framework/tool-resolver.js +0 -65
  140. package/src/index.js +0 -345
  141. package/src/runtime/generation/base.js +0 -46
  142. package/src/runtime/generation/index.js +0 -70
  143. package/src/runtime/generation/mcp-ref-strategy.js +0 -197
  144. package/src/runtime/generation/stable-id-strategy.js +0 -170
  145. package/src/runtime/stable-id-runtime.js +0 -248
  146. package/src/runtime/verification/base.js +0 -44
  147. package/src/runtime/verification/index.js +0 -67
  148. package/src/runtime/verification/playwright-json-strategy.js +0 -119
  149. package/src/runtime/zibby-runtime.js +0 -299
  150. package/src/sync/index.js +0 -2
  151. package/src/sync/uploader.js +0 -29
  152. package/src/tools/run-playwright-test.js +0 -158
  153. package/src/utils/adf-converter.js +0 -68
  154. package/src/utils/ast-utils.js +0 -37
  155. package/src/utils/ci-setup.js +0 -124
  156. package/src/utils/cursor-utils.js +0 -71
  157. package/src/utils/logger.js +0 -144
  158. package/src/utils/mcp-config-writer.js +0 -115
  159. package/src/utils/node-schema-parser.js +0 -522
  160. package/src/utils/post-process-events.js +0 -55
  161. package/src/utils/result-handler.js +0 -102
  162. package/src/utils/selector-generator.js +0 -239
  163. package/src/utils/streaming-parser.js +0 -387
  164. package/src/utils/test-post-processor.js +0 -211
  165. package/src/utils/timeline.js +0 -217
  166. package/src/utils/trace-parser.js +0 -325
  167. package/src/utils/video-organizer.js +0 -91
@@ -1,361 +0,0 @@
1
- import { spawn } from 'child_process';
2
- import { mkdirSync, existsSync, writeFileSync } from 'fs';
3
- import { join } from 'path';
4
- import { ContextLoader } from '../framework/context-loader.js';
5
- import { StreamingParser } from '../utils/streaming-parser.js';
6
- import { findCursorAgentPath } from '../utils/cursor-utils.js';
7
- import { DEFAULT_OUTPUT_BASE, SESSIONS_DIR, SESSION_INFO_FILE } from '../framework/constants.js';
8
- import { WorkflowGraph } from '../framework/graph.js';
9
-
10
- export class WorkflowAgent {
11
- constructor(config = {}) {
12
- this.config = config;
13
- this.adapter = null;
14
- this.paths = config.paths || { specs: 'test-specs', generated: 'tests' };
15
- this.agentCommand = config.agentCommand || 'cursor-agent';
16
- this.buildArgs = config.buildArgs || ((prompt, useStreaming = true) => {
17
- const args = [
18
- '-p',
19
- prompt,
20
- '--approve-mcps',
21
- '--force'
22
- ];
23
-
24
- // Only use streaming for non-structured output
25
- if (useStreaming) {
26
- args.push('--output-format', 'stream-json');
27
- args.push('--stream-partial-output');
28
- }
29
-
30
- return args;
31
- });
32
- }
33
-
34
- /**
35
- * Extract JSON result from NDJSON streaming output
36
- * Uses StreamingParser for clean, reliable extraction
37
- */
38
- static extractJsonFromStream(rawOutput) {
39
- return StreamingParser.extractResult(rawOutput);
40
- }
41
-
42
- async initialize(adapter) {
43
- this.adapter = adapter;
44
- if (adapter && !adapter.isConnected()) {
45
- await adapter.connect();
46
- }
47
- }
48
-
49
- buildGraph() {
50
- throw new Error('buildGraph() must be implemented by subclass');
51
- }
52
-
53
- async onComplete(_result) {
54
- // Override in subclass for post-execution processing
55
- // (e.g., save artifacts, enrich events, rename videos)
56
- }
57
-
58
- async run(input, options = {}) {
59
- const graph = this.buildGraph();
60
- const initialState = typeof input === 'object' && !Array.isArray(input)
61
- ? { input, ...input, ...options }
62
- : { input, ...options };
63
- const result = await graph.run(this, initialState);
64
- return result;
65
- }
66
-
67
- async executeNode(nodeConfig, state) {
68
- const { prompt, outputSchema, model } = nodeConfig;
69
- const promptText = typeof prompt === 'function' ? prompt(state) : prompt;
70
-
71
- console.log(`\n📝 Prompt:\n${promptText}\n`);
72
-
73
- const output = await this.executePrompt(promptText, state.cwd, 300000, model);
74
-
75
- console.log(`\n📤 Raw Output:\n${output}\n`);
76
-
77
- let parsedOutput = null;
78
- if (outputSchema) {
79
- try {
80
- // Use the static helper to extract JSON from streaming output
81
- parsedOutput = WorkflowAgent.extractJsonFromStream(output);
82
-
83
- if (!parsedOutput) {
84
- throw new Error('No valid result JSON found in output');
85
- }
86
-
87
- console.log(`\n✅ Parsed Output:\n${JSON.stringify(parsedOutput, null, 2)}\n`);
88
- } catch (err) {
89
- console.warn(`⚠️ Failed to parse output as JSON: ${err.message}`);
90
- }
91
- }
92
-
93
- return { success: true, output: parsedOutput, raw: output };
94
- }
95
-
96
- async cleanup() {
97
- if (this.adapter && this.adapter.isConnected()) {
98
- await this.adapter.disconnect();
99
- }
100
- }
101
-
102
- async executePrompt(prompt, cwd = process.cwd(), timeoutMs = 300000, model = null, useStreaming = true) {
103
- // Resolve cursor-agent path if needed (handles cases where PATH isn't set)
104
- let agentCommand = this.agentCommand;
105
- if (agentCommand === 'cursor-agent') {
106
- const resolvedPath = await findCursorAgentPath();
107
- if (resolvedPath) {
108
- agentCommand = resolvedPath;
109
- }
110
- // If not found, keep original command - error will be clearer from spawn
111
- }
112
-
113
- return new Promise((resolve, reject) => {
114
- let output = '';
115
- const args = this.buildArgs(prompt, useStreaming);
116
-
117
- if (model) {
118
- args.push('--model', model);
119
- }
120
-
121
- const env = { ...process.env };
122
-
123
- const agent = spawn(agentCommand, args, {
124
- cwd,
125
- env,
126
- stdio: ['inherit', 'pipe', 'inherit'], // Let stderr pass through for typewriter effect
127
- shell: false,
128
- detached: false,
129
- });
130
-
131
- const timeout = setTimeout(() => {
132
- console.log(`\n⏱️ Timeout reached (${timeoutMs / 1000}s) - killing agent...`);
133
- agent.kill('SIGTERM');
134
- // Force kill if still running after 2 seconds
135
- setTimeout(() => {
136
- if (!agent.killed) {
137
- console.log('⚠️ Forcing kill (SIGKILL)...');
138
- agent.kill('SIGKILL');
139
- }
140
- }, 2000);
141
- reject(new Error(`Agent timed out after ${timeoutMs / 1000}s. The agent may be stuck or waiting for user input.`));
142
- }, timeoutMs);
143
-
144
- let lastOutputLength = 0;
145
- let lastOutputTime = Date.now();
146
- const heartbeat = setInterval(() => {
147
- if (output.length > lastOutputLength) {
148
- // Output is coming in, no need to show heartbeat
149
- lastOutputLength = output.length;
150
- lastOutputTime = Date.now();
151
- } else {
152
- // Only show "thinking" message if no output for 10 seconds
153
- const timeSinceLastOutput = Date.now() - lastOutputTime;
154
- if (timeSinceLastOutput > 10000) {
155
- console.log(`⏳ AI agent is thinking... (${Math.floor(timeSinceLastOutput / 1000)}s, ${output.length} bytes so far)`);
156
- }
157
- }
158
- }, 10000);
159
-
160
- // Handle Ctrl+C gracefully
161
- const cleanup = () => {
162
- clearTimeout(timeout);
163
- clearInterval(heartbeat);
164
- if (agent && !agent.killed) {
165
- agent.kill('SIGTERM');
166
- // Force kill if not dead after 2 seconds
167
- setTimeout(() => {
168
- if (!agent.killed) {
169
- agent.kill('SIGKILL');
170
- }
171
- }, 2000);
172
- }
173
- };
174
-
175
- const sigintHandler = () => {
176
- console.log('\n\n🛑 Interrupted by user (Ctrl+C)');
177
- cleanup();
178
- // Don't reset terminal - it's too aggressive
179
- reject(new Error('Interrupted by user'));
180
- };
181
-
182
- process.on('SIGINT', sigintHandler);
183
-
184
- // Use StreamingParser for clean, reliable parsing
185
- const parser = new StreamingParser();
186
-
187
- // Handle stdout - cursor-agent outputs NDJSON protocol messages
188
- agent.stdout.on('data', (data) => {
189
- const chunk = data.toString();
190
- output += chunk; // Accumulate raw output
191
-
192
- const displayText = parser.processChunk(chunk);
193
-
194
- if (displayText) {
195
- // Write the extracted text immediately (no buffering)
196
- // This creates the typewriter effect as cursor-agent streams
197
- process.stdout.write(displayText, 'utf8', () => {
198
- // Flush immediately after write
199
- if (process.stdout.isTTY) {
200
- process.stdout._flush && process.stdout._flush();
201
- }
202
- });
203
- lastOutputTime = Date.now();
204
- lastOutputLength += displayText.length;
205
- }
206
- });
207
-
208
- agent.on('close', (code) => {
209
- process.off('SIGINT', sigintHandler);
210
- clearTimeout(timeout);
211
- clearInterval(heartbeat);
212
-
213
- // Flush any remaining buffer
214
- const remainingText = parser.flush();
215
- if (remainingText) {
216
- process.stdout.write(remainingText);
217
- }
218
-
219
- // Get the complete output (for JSON extraction, includes protocol)
220
- output = parser.getRawText();
221
-
222
- // Also store the extracted result for direct access
223
- const extractedResult = parser.getResult();
224
-
225
- if (code === 0) {
226
- // Return both raw output and extracted result
227
- resolve({ raw: output, extracted: extractedResult });
228
- } else {
229
- reject(new Error(`Agent exited with code ${code}`));
230
- }
231
- });
232
-
233
- agent.on('error', (error) => {
234
- process.off('SIGINT', sigintHandler);
235
- clearTimeout(timeout);
236
- clearInterval(heartbeat);
237
- reject(new Error(`Failed to spawn agent: ${error.message}`));
238
- });
239
- });
240
- }
241
-
242
- /**
243
- * Run a single node (for testing/debugging)
244
- * This is a framework feature, not part of workflow definition
245
- */
246
- async runSingleNode(nodeName, nodeMap, initialState) {
247
- const nodeConfig = nodeMap[nodeName];
248
- if (!nodeConfig) {
249
- throw new Error(`Unknown node: ${nodeName}. Available nodes: ${Object.keys(nodeMap).join(', ')}`);
250
- }
251
-
252
- const { cwd } = initialState;
253
- if (!cwd) {
254
- throw new Error('cwd is required for single node execution');
255
- }
256
-
257
- // Reuse existing session if provided (--session flag), otherwise create new
258
- let sessionPath = initialState.sessionPath;
259
- let sessionTimestamp = initialState.sessionTimestamp;
260
- const config = initialState.config || {};
261
-
262
- if (!sessionPath) {
263
- // Generate CI-aware session ID
264
- const ciSessionId = process.env.CI_JOB_ID ||
265
- process.env.GITHUB_RUN_ID ||
266
- process.env.CIRCLE_WORKFLOW_ID ||
267
- process.env.BUILD_ID;
268
-
269
- const baseId = ciSessionId || Date.now().toString();
270
- const prefix = config.paths?.sessionPrefix;
271
- const sessionId = prefix ? `${prefix}_${baseId}` : baseId;
272
-
273
- sessionTimestamp = sessionTimestamp || Date.now();
274
-
275
- // Use configurable output path
276
- const outputBase = config.paths?.output || DEFAULT_OUTPUT_BASE;
277
- sessionPath = join(cwd, outputBase, SESSIONS_DIR, sessionId);
278
-
279
- if (!existsSync(sessionPath)) {
280
- mkdirSync(sessionPath, { recursive: true });
281
- }
282
- }
283
-
284
- // Write session info to output directory
285
- const outputBase = config.paths?.output || DEFAULT_OUTPUT_BASE;
286
- const sessionInfoPath = join(cwd, outputBase, SESSION_INFO_FILE);
287
- mkdirSync(join(cwd, outputBase), { recursive: true });
288
- writeFileSync(sessionInfoPath, JSON.stringify({
289
- sessionPath,
290
- sessionTimestamp: sessionTimestamp || sessionPath.split('/').pop(),
291
- currentNode: nodeName,
292
- createdAt: new Date().toISOString()
293
- }), 'utf-8');
294
-
295
- console.log(`\n${'='.repeat(80)}`);
296
- console.log(`🎯 SINGLE NODE EXECUTION: ${nodeName}`);
297
- console.log(`📁 Session: ${sessionPath.split('/').pop()}${initialState.sessionPath ? ' (reusing)' : ''}`);
298
- console.log(`${'='.repeat(80)}\n`);
299
-
300
- const context = await ContextLoader.loadContext(
301
- initialState.specPath || '',
302
- cwd,
303
- initialState.contextConfig || {}
304
- );
305
-
306
- // Use Node class for consistent behavior with workflow execution
307
- const { Node } = await import('../framework/node.js');
308
- const { WorkflowState } = await import('../framework/state.js');
309
-
310
- const state = new WorkflowState({
311
- ...initialState,
312
- sessionPath,
313
- sessionTimestamp,
314
- context
315
- });
316
-
317
- const node = new Node(nodeConfig);
318
- const result = await node.execute(this, state);
319
-
320
- console.log(`\n${'='.repeat(80)}`);
321
- console.log(`✅ Node ${nodeName} completed`);
322
- console.log(`${'='.repeat(80)}\n`);
323
-
324
- return { success: true, output: result.output, outputPath: initialState.outputPath, state: result };
325
- }
326
-
327
- calculateOutputPath(specPath) {
328
- const { specs, generated } = this.paths;
329
- if (!specPath) return `${generated}/generated-test.spec.js`;
330
-
331
- const relativePath = specPath.replace(new RegExp(`^${specs}/`), '').replace(/\.[^.]+$/, '.spec.js');
332
- return `${generated}/${relativePath}`.replace(/\/+/g, '/');
333
- }
334
- }
335
-
336
- /**
337
- * Functional workflow factory — no class boilerplate needed.
338
- *
339
- * const agent = workflow((graph) => {
340
- * graph.addNode('plan', { prompt: ..., outputSchema: ... });
341
- * graph.addNode('execute', { skills: ['calculator'], prompt: ... });
342
- * graph.setEntryPoint('plan');
343
- * graph.addEdge('plan', 'execute');
344
- * graph.addEdge('execute', 'END');
345
- * });
346
- *
347
- * await agent.run("What is 15 plus 27?");
348
- */
349
- export function workflow(builder, options = {}) {
350
- const agent = new WorkflowAgent(options);
351
- agent.buildGraph = function () {
352
- const graph = new WorkflowGraph();
353
- builder(graph);
354
- return graph;
355
- };
356
- if (options.onComplete) {
357
- agent.onComplete = options.onComplete;
358
- }
359
- return agent;
360
- }
361
-
package/src/constants.js DELETED
@@ -1,47 +0,0 @@
1
- /**
2
- * Zibby Framework Constants
3
- * Single source of truth for all configuration values
4
- */
5
-
6
- export const DEFAULT_MODELS = {
7
- CLAUDE: 'claude-sonnet-4-6',
8
- CURSOR: 'auto',
9
- OPENAI_POSTPROCESSING: 'gpt-4o-mini'
10
- };
11
-
12
- export const AGENT_TYPES = {
13
- CLAUDE: 'claude',
14
- CURSOR: 'cursor'
15
- };
16
-
17
- export const LOG_LEVELS = {
18
- DEBUG: 'debug',
19
- INFO: 'info',
20
- WARN: 'warn',
21
- ERROR: 'error',
22
- SILENT: 'silent'
23
- };
24
-
25
- export const CLAUDE_MODEL_MAP = {
26
- 'auto': 'claude-sonnet-4-6',
27
- // 4.6 aliases
28
- 'sonnet-4.6': 'claude-sonnet-4-6',
29
- 'sonnet-4-6': 'claude-sonnet-4-6',
30
- 'opus-4.6': 'claude-opus-4-6',
31
- 'opus-4-6': 'claude-opus-4-6',
32
- // 4.5 aliases
33
- 'sonnet-4.5': 'claude-sonnet-4-5-20250929',
34
- 'sonnet-4-5': 'claude-sonnet-4-5-20250929',
35
- 'opus-4.5': 'claude-opus-4-20250514',
36
- 'opus-4-5': 'claude-opus-4-20250514',
37
- // Direct API IDs (pass-through)
38
- 'claude-sonnet-4-6': 'claude-sonnet-4-6',
39
- 'claude-opus-4-6': 'claude-opus-4-6',
40
- 'claude-sonnet-4-5-20250929': 'claude-sonnet-4-5-20250929',
41
- 'claude-opus-4-20250514': 'claude-opus-4-20250514',
42
- };
43
-
44
- export const TIMEOUTS = {
45
- CURSOR_AGENT_DEFAULT: 20 * 60 * 1000,
46
- OPENAI_REQUEST: 30000
47
- };
@@ -1,49 +0,0 @@
1
- /**
2
- * Base Enrichment Pipeline Component
3
- * Each enricher adds specific data to events without affecting others
4
- */
5
- export class EventEnricher {
6
- constructor(config = {}) {
7
- this.config = config;
8
- this.enabled = config.enabled !== false; // Default enabled
9
- this.priority = config.priority || 50; // Default priority
10
- }
11
-
12
- /**
13
- * Get enricher name for logging
14
- */
15
- getName() {
16
- throw new Error('EventEnricher.getName() must be implemented');
17
- }
18
-
19
- /**
20
- * Check if this enricher can run (dependencies met, etc)
21
- * @param {Object} context - { page, element, event, session }
22
- * @returns {boolean}
23
- */
24
- canEnrich(_context) {
25
- return this.enabled;
26
- }
27
-
28
- /**
29
- * Enrich the event with additional data
30
- * @param {Object} event - The MCP event to enrich
31
- * @param {Object} context - { page, element, ref }
32
- * @returns {Promise<Object>} - Enriched data to merge into event
33
- */
34
- async enrich(_event, _context) {
35
- throw new Error('EventEnricher.enrich() must be implemented');
36
- }
37
-
38
- /**
39
- * Handle errors gracefully (never break the pipeline)
40
- * @param {Error} error
41
- * @param {Object} event
42
- */
43
- handleError(error, event) {
44
- console.warn(`[${this.getName()}] Enrichment failed for event ${event.type}:`, error.message);
45
- return null; // Return null = skip this enrichment
46
- }
47
- }
48
-
49
- export default EventEnricher;
@@ -1,197 +0,0 @@
1
- /**
2
- * Accessibility Enricher - Captures accessibility tree data and structural hash
3
- */
4
- import { EventEnricher } from '../base.js';
5
- import crypto from 'crypto';
6
-
7
- export class AccessibilityEnricher extends EventEnricher {
8
- getName() {
9
- return 'AccessibilityEnricher';
10
- }
11
-
12
- getPriority() {
13
- return 100; // Highest priority - most stable identifier
14
- }
15
-
16
- canEnrich(context) {
17
- if (!this.enabled) return false;
18
- if (!context.element) return false;
19
- if (!context.event) return false;
20
-
21
- return ['click', 'fill', 'type', 'selectOption', 'hover'].includes(context.event.type);
22
- }
23
-
24
- async enrich(event, context) {
25
- try {
26
- const { page, element } = context;
27
-
28
- // Get accessibility snapshot of entire page
29
- const snapshot = await page.accessibility.snapshot();
30
-
31
- // Find the target element in AX tree
32
- const axNode = await this.findAxNode(element, snapshot);
33
- if (!axNode) return null;
34
-
35
- // Get parent and siblings
36
- const axContext = await this.getAxContext(axNode, snapshot);
37
-
38
- // Calculate structural hash (hash of AX subtree)
39
- const axTreeHash = this.hashAxSubtree(axNode);
40
- const axPathHash = this.hashAxPath(axContext.path);
41
-
42
- return {
43
- accessibility: {
44
- role: axNode.role,
45
- name: axNode.name || '',
46
- level: axContext.level,
47
- parent: axContext.parent,
48
- siblings: axContext.siblings,
49
- axTreeHash,
50
- axPathHash
51
- }
52
- };
53
- } catch (error) {
54
- return this.handleError(error, event);
55
- }
56
- }
57
-
58
- /**
59
- * Find AX node for given element
60
- */
61
- async findAxNode(element, snapshot) {
62
- // Get element's aria attributes for matching
63
- const ariaProps = await element.evaluate(el => ({
64
- role: el.getAttribute('role') || el.tagName.toLowerCase(),
65
- name: el.getAttribute('aria-label') || el.textContent?.trim() || '',
66
- tagName: el.tagName.toLowerCase()
67
- }));
68
-
69
- // Search AX tree for matching node
70
- return this.searchAxTree(snapshot, ariaProps);
71
- }
72
-
73
- /**
74
- * Recursively search AX tree
75
- */
76
- searchAxTree(node, props) {
77
- if (!node) return null;
78
-
79
- // Match by role and name
80
- if (node.role === props.role &&
81
- (node.name || '').includes(props.name.substring(0, 20))) {
82
- return node;
83
- }
84
-
85
- // Search children
86
- if (node.children) {
87
- for (const child of node.children) {
88
- const found = this.searchAxTree(child, props);
89
- if (found) return found;
90
- }
91
- }
92
-
93
- return null;
94
- }
95
-
96
- /**
97
- * Get context (parent, siblings, path)
98
- */
99
- getAxContext(node, snapshot) {
100
- const context = {
101
- level: 0,
102
- parent: null,
103
- siblings: [],
104
- path: []
105
- };
106
-
107
- // Walk up tree to find parent and siblings
108
- const parent = this.findParent(node, snapshot);
109
- if (parent) {
110
- context.parent = { role: parent.role, name: parent.name };
111
- context.siblings = (parent.children || [])
112
- .filter(c => c !== node)
113
- .map(c => ({ role: c.role, name: c.name }))
114
- .slice(0, 3); // Limit siblings
115
- }
116
-
117
- // Calculate level (depth in tree)
118
- context.level = this.calculateLevel(node, snapshot);
119
-
120
- // Build path from root to node
121
- context.path = this.buildPath(node, snapshot);
122
-
123
- return context;
124
- }
125
-
126
- findParent(node, tree, current = tree) {
127
- if (!current || !current.children) return null;
128
-
129
- if (current.children.includes(node)) {
130
- return current;
131
- }
132
-
133
- for (const child of current.children) {
134
- const found = this.findParent(node, tree, child);
135
- if (found) return found;
136
- }
137
-
138
- return null;
139
- }
140
-
141
- calculateLevel(node, tree, current = tree, level = 0) {
142
- if (current === node) return level;
143
-
144
- if (current.children) {
145
- for (const child of current.children) {
146
- const found = this.calculateLevel(node, tree, child, level + 1);
147
- if (found >= 0) return found;
148
- }
149
- }
150
-
151
- return -1;
152
- }
153
-
154
- buildPath(node, tree, current = tree, path = []) {
155
- if (current === node) {
156
- return [...path, { role: current.role, name: current.name }];
157
- }
158
-
159
- if (current.children) {
160
- for (const child of current.children) {
161
- const found = this.buildPath(node, tree, child, [
162
- ...path,
163
- { role: current.role, name: current.name }
164
- ]);
165
- if (found) return found;
166
- }
167
- }
168
-
169
- return null;
170
- }
171
-
172
- /**
173
- * Hash AX subtree for stability detection
174
- */
175
- hashAxSubtree(node) {
176
- const structure = JSON.stringify({
177
- role: node.role,
178
- name: node.name,
179
- children: (node.children || []).map(c => ({
180
- role: c.role,
181
- name: c.name
182
- }))
183
- });
184
-
185
- return crypto.createHash('md5').update(structure).digest('hex').substring(0, 12);
186
- }
187
-
188
- /**
189
- * Hash AX path from root to node
190
- */
191
- hashAxPath(path) {
192
- const pathStr = path.map(p => `${p.role}:${p.name}`).join('/');
193
- return crypto.createHash('md5').update(pathStr).digest('hex').substring(0, 12);
194
- }
195
- }
196
-
197
- export default AccessibilityEnricher;