@zibby/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +147 -0
  3. package/package.json +94 -0
  4. package/src/agents/base.js +361 -0
  5. package/src/constants.js +47 -0
  6. package/src/enrichment/base.js +49 -0
  7. package/src/enrichment/enrichers/accessibility-enricher.js +197 -0
  8. package/src/enrichment/enrichers/dom-enricher.js +171 -0
  9. package/src/enrichment/enrichers/page-state-enricher.js +129 -0
  10. package/src/enrichment/enrichers/position-enricher.js +67 -0
  11. package/src/enrichment/index.js +96 -0
  12. package/src/enrichment/mcp-integration.js +149 -0
  13. package/src/enrichment/mcp-ref-enricher.js +78 -0
  14. package/src/enrichment/pipeline.js +192 -0
  15. package/src/enrichment/trace-text-enricher.js +115 -0
  16. package/src/framework/AGENTS.md +98 -0
  17. package/src/framework/agents/base.js +72 -0
  18. package/src/framework/agents/claude-strategy.js +278 -0
  19. package/src/framework/agents/cursor-strategy.js +459 -0
  20. package/src/framework/agents/index.js +105 -0
  21. package/src/framework/agents/utils/cursor-output-formatter.js +67 -0
  22. package/src/framework/agents/utils/openai-proxy-formatter.js +249 -0
  23. package/src/framework/code-generator.js +301 -0
  24. package/src/framework/constants.js +33 -0
  25. package/src/framework/context-loader.js +101 -0
  26. package/src/framework/function-bridge.js +78 -0
  27. package/src/framework/function-skill-registry.js +20 -0
  28. package/src/framework/graph-compiler.js +342 -0
  29. package/src/framework/graph.js +610 -0
  30. package/src/framework/index.js +28 -0
  31. package/src/framework/node-registry.js +163 -0
  32. package/src/framework/node.js +259 -0
  33. package/src/framework/output-parser.js +71 -0
  34. package/src/framework/skill-registry.js +55 -0
  35. package/src/framework/state-utils.js +52 -0
  36. package/src/framework/state.js +67 -0
  37. package/src/framework/tool-resolver.js +65 -0
  38. package/src/index.js +342 -0
  39. package/src/runtime/generation/base.js +46 -0
  40. package/src/runtime/generation/index.js +70 -0
  41. package/src/runtime/generation/mcp-ref-strategy.js +197 -0
  42. package/src/runtime/generation/stable-id-strategy.js +170 -0
  43. package/src/runtime/stable-id-runtime.js +248 -0
  44. package/src/runtime/verification/base.js +44 -0
  45. package/src/runtime/verification/index.js +67 -0
  46. package/src/runtime/verification/playwright-json-strategy.js +119 -0
  47. package/src/runtime/zibby-runtime.js +299 -0
  48. package/src/sync/index.js +2 -0
  49. package/src/sync/uploader.js +29 -0
  50. package/src/tools/run-playwright-test.js +158 -0
  51. package/src/utils/adf-converter.js +68 -0
  52. package/src/utils/ast-utils.js +37 -0
  53. package/src/utils/ci-setup.js +124 -0
  54. package/src/utils/cursor-utils.js +71 -0
  55. package/src/utils/logger.js +144 -0
  56. package/src/utils/mcp-config-writer.js +115 -0
  57. package/src/utils/node-schema-parser.js +522 -0
  58. package/src/utils/post-process-events.js +55 -0
  59. package/src/utils/result-handler.js +102 -0
  60. package/src/utils/ripple-effect.js +84 -0
  61. package/src/utils/selector-generator.js +239 -0
  62. package/src/utils/streaming-parser.js +387 -0
  63. package/src/utils/test-post-processor.js +211 -0
  64. package/src/utils/timeline.js +217 -0
  65. package/src/utils/trace-parser.js +325 -0
  66. package/src/utils/video-organizer.js +91 -0
  67. package/templates/browser-test-automation/README.md +114 -0
  68. package/templates/browser-test-automation/graph.js +54 -0
  69. package/templates/browser-test-automation/nodes/execute-live.js +250 -0
  70. package/templates/browser-test-automation/nodes/generate-script.js +77 -0
  71. package/templates/browser-test-automation/nodes/index.js +3 -0
  72. package/templates/browser-test-automation/nodes/preflight.js +59 -0
  73. package/templates/browser-test-automation/nodes/utils.js +154 -0
  74. package/templates/browser-test-automation/result-handler.js +286 -0
  75. package/templates/code-analysis/graph.js +72 -0
  76. package/templates/code-analysis/index.js +18 -0
  77. package/templates/code-analysis/nodes/analyze-ticket-node.js +204 -0
  78. package/templates/code-analysis/nodes/create-pr-node.js +175 -0
  79. package/templates/code-analysis/nodes/finalize-node.js +118 -0
  80. package/templates/code-analysis/nodes/generate-code-node.js +425 -0
  81. package/templates/code-analysis/nodes/generate-test-cases-node.js +376 -0
  82. package/templates/code-analysis/nodes/services/prMetaService.js +86 -0
  83. package/templates/code-analysis/nodes/setup-node.js +142 -0
  84. package/templates/code-analysis/prompts/analyze-ticket.md +181 -0
  85. package/templates/code-analysis/prompts/generate-code.md +33 -0
  86. package/templates/code-analysis/prompts/generate-test-cases.md +110 -0
  87. package/templates/code-analysis/state.js +40 -0
  88. package/templates/code-implementation/graph.js +35 -0
  89. package/templates/code-implementation/index.js +7 -0
  90. package/templates/code-implementation/state.js +14 -0
  91. package/templates/global-setup.js +56 -0
  92. package/templates/index.js +94 -0
  93. package/templates/register-nodes.js +24 -0
@@ -0,0 +1,49 @@
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;
@@ -0,0 +1,197 @@
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;
@@ -0,0 +1,171 @@
1
+ /**
2
+ * DOM Enricher - Captures DOM path, XPath, and element attributes
3
+ * This file contains browser code executed via element.evaluate() where browser globals are available
4
+ */
5
+ /* global document, window */
6
+ import { EventEnricher } from '../base.js';
7
+
8
+ export class DOMEnricher extends EventEnricher {
9
+ getName() {
10
+ return 'DOMEnricher';
11
+ }
12
+
13
+ getPriority() {
14
+ return 85; // High priority - structural data
15
+ }
16
+
17
+ canEnrich(context) {
18
+ if (!this.enabled) return false;
19
+ if (!context.element) return false;
20
+ if (!context.event) return false;
21
+
22
+ return ['click', 'fill', 'type', 'selectOption', 'hover'].includes(context.event.type);
23
+ }
24
+
25
+ async enrich(event, context) {
26
+ try {
27
+ const { element } = context;
28
+
29
+ // Get DOM path, XPath, and attributes
30
+ const domData = await element.evaluate(el => {
31
+ // Build CSS path
32
+ const getCssPath = (innerEl) => {
33
+ const path = [];
34
+ let current = innerEl;
35
+
36
+ while (current && current !== document.body) {
37
+ let selector = current.tagName.toLowerCase();
38
+
39
+ // Add nth-child if there are siblings of same type
40
+ const parent = current.parentElement;
41
+ if (parent) {
42
+ const siblings = Array.from(parent.children).filter(
43
+ child => child.tagName === current.tagName
44
+ );
45
+ if (siblings.length > 1) {
46
+ const index = siblings.indexOf(current) + 1;
47
+ selector += `:nth-child(${index})`;
48
+ }
49
+ }
50
+
51
+ path.unshift(selector);
52
+ current = current.parentElement;
53
+ }
54
+
55
+ return `body > ${path.join(' > ')}`;
56
+ };
57
+
58
+ // Build XPath
59
+ const getXPath = (innerEl) => {
60
+ const path = [];
61
+ let current = innerEl;
62
+
63
+ while (current && current !== document.body) {
64
+ let index = 1;
65
+ let sibling = current.previousSibling;
66
+
67
+ while (sibling) {
68
+ if (sibling.nodeType === 1 && sibling.tagName === current.tagName) {
69
+ index++;
70
+ }
71
+ sibling = sibling.previousSibling;
72
+ }
73
+
74
+ const tag = current.tagName.toLowerCase();
75
+ path.unshift(`${tag}[${index}]`);
76
+ current = current.parentElement;
77
+ }
78
+
79
+ return `/html/body/${path.join('/')}`;
80
+ };
81
+
82
+ // Get all attributes
83
+ const attributes = {};
84
+ for (const attr of el.attributes) {
85
+ attributes[attr.name] = attr.value;
86
+ }
87
+
88
+ // Get computed style (only essential properties)
89
+ const computed = window.getComputedStyle(el);
90
+ const state = {
91
+ display: computed.display,
92
+ visibility: computed.visibility,
93
+ opacity: computed.opacity,
94
+ pointerEvents: computed.pointerEvents
95
+ };
96
+
97
+ // Calculate depth
98
+ let depth = 0;
99
+ let parent = el.parentElement;
100
+ while (parent) {
101
+ depth++;
102
+ parent = parent.parentElement;
103
+ }
104
+
105
+ return {
106
+ path: getCssPath(el),
107
+ xpath: getXPath(el),
108
+ depth,
109
+ parent: el.parentElement ? el.parentElement.tagName.toLowerCase() : null,
110
+ tagName: el.tagName.toLowerCase(),
111
+ attributes,
112
+ state: {
113
+ visible: computed.display !== 'none' && computed.visibility !== 'hidden',
114
+ enabled: !el.disabled,
115
+ focused: document.activeElement === el,
116
+ ...state
117
+ }
118
+ };
119
+ });
120
+
121
+ return {
122
+ dom: {
123
+ path: domData.path,
124
+ xpath: domData.xpath,
125
+ depth: domData.depth,
126
+ parent: domData.parent,
127
+ selector: this.buildSmartSelector(domData)
128
+ },
129
+ attributes: domData.attributes,
130
+ state: domData.state
131
+ };
132
+ } catch (error) {
133
+ return this.handleError(error, event);
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Build a smart CSS selector from DOM data
139
+ */
140
+ buildSmartSelector(domData) {
141
+ let selector = domData.tagName;
142
+
143
+ // Add ID if available
144
+ if (domData.attributes.id) {
145
+ return `#${domData.attributes.id}`;
146
+ }
147
+
148
+ // Add data-test-id if available
149
+ if (domData.attributes['data-test-id']) {
150
+ return `[data-test-id="${domData.attributes['data-test-id']}"]`;
151
+ }
152
+
153
+ // Add class if available
154
+ if (domData.attributes.class) {
155
+ const classes = domData.attributes.class.split(' ')
156
+ .filter(c => c && !c.match(/^(active|focus|hover|disabled)/)); // Skip state classes
157
+ if (classes.length > 0) {
158
+ selector += `.${classes.slice(0, 2).join('.')}`;
159
+ }
160
+ }
161
+
162
+ // Add parent context
163
+ if (domData.parent) {
164
+ selector = `${domData.parent} > ${selector}`;
165
+ }
166
+
167
+ return selector;
168
+ }
169
+ }
170
+
171
+ export default DOMEnricher;
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Page State Enricher - Captures network and page stability state
3
+ * This file contains browser code executed via page.evaluate() where browser globals are available
4
+ */
5
+ /* global document, MutationObserver */
6
+ import { EventEnricher } from '../base.js';
7
+
8
+ export class PageStateEnricher extends EventEnricher {
9
+ constructor(config = {}) {
10
+ super(config);
11
+ this.pendingRequests = new Set();
12
+ this.setupNetworkTracking = false;
13
+ }
14
+
15
+ getName() {
16
+ return 'PageStateEnricher';
17
+ }
18
+
19
+ getPriority() {
20
+ return 95; // Very high priority - critical for timing
21
+ }
22
+
23
+ canEnrich(context) {
24
+ return this.enabled && context.page;
25
+ }
26
+
27
+ /**
28
+ * Setup network request tracking (call once per page)
29
+ */
30
+ async setupTracking(page) {
31
+ if (this.setupNetworkTracking) return;
32
+
33
+ page.on('request', (request) => {
34
+ if (['document', 'xhr', 'fetch'].includes(request.resourceType())) {
35
+ this.pendingRequests.add(request.url());
36
+ }
37
+ });
38
+
39
+ page.on('requestfinished', (request) => {
40
+ this.pendingRequests.delete(request.url());
41
+ });
42
+
43
+ page.on('requestfailed', (request) => {
44
+ this.pendingRequests.delete(request.url());
45
+ });
46
+
47
+ this.setupNetworkTracking = true;
48
+ }
49
+
50
+ async enrich(event, context) {
51
+ try {
52
+ const { page } = context;
53
+
54
+ // Setup tracking if not done
55
+ await this.setupTracking(page);
56
+
57
+ // Get page load state
58
+ const pageState = await page.evaluate(() => ({
59
+ readyState: document.readyState,
60
+ domContentLoaded: document.readyState !== 'loading',
61
+ loadComplete: document.readyState === 'complete',
62
+ url: document.location.href
63
+ }));
64
+
65
+ // Check if DOM is stable (no mutations for 500ms)
66
+ const domStable = await this.checkDOMStability(page);
67
+
68
+ return {
69
+ page: {
70
+ networkIdle: this.pendingRequests.size === 0,
71
+ pendingRequests: this.pendingRequests.size,
72
+ domStable,
73
+ ...pageState
74
+ }
75
+ };
76
+ } catch (error) {
77
+ return this.handleError(error, event);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Check if DOM hasn't mutated for 500ms
83
+ */
84
+ async checkDOMStability(page, timeoutMs = 500) {
85
+ try {
86
+ const stable = await page.evaluate((timeout) => {
87
+ return new Promise((resolve) => {
88
+ let timer;
89
+ let mutations = 0;
90
+
91
+ const observer = new MutationObserver(() => {
92
+ mutations++;
93
+ clearTimeout(timer);
94
+ timer = setTimeout(() => {
95
+ observer.disconnect();
96
+ resolve(mutations === 0);
97
+ }, timeout);
98
+ });
99
+
100
+ observer.observe(document.body, {
101
+ childList: true,
102
+ subtree: true,
103
+ attributes: true
104
+ });
105
+
106
+ // Initial timer
107
+ timer = setTimeout(() => {
108
+ observer.disconnect();
109
+ resolve(true);
110
+ }, timeout);
111
+ });
112
+ }, timeoutMs);
113
+
114
+ return stable;
115
+ } catch (_error) {
116
+ return false; // Assume not stable on error
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Reset tracking for new page navigation
122
+ */
123
+ reset() {
124
+ this.pendingRequests.clear();
125
+ this.setupNetworkTracking = false;
126
+ }
127
+ }
128
+
129
+ export default PageStateEnricher;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Position Enricher - Captures element bounding box and viewport state
3
+ * This file contains browser code executed via element.evaluate() where browser globals are available
4
+ */
5
+ /* global window */
6
+ import { EventEnricher } from '../base.js';
7
+
8
+ export class PositionEnricher extends EventEnricher {
9
+ getName() {
10
+ return 'PositionEnricher';
11
+ }
12
+
13
+ getPriority() {
14
+ return 90; // High priority - cheap and useful
15
+ }
16
+
17
+ canEnrich(context) {
18
+ // Only enrich interactive actions that have an element
19
+ if (!this.enabled) return false;
20
+ if (!context.element) return false;
21
+ if (!context.event) return false;
22
+
23
+ return ['click', 'fill', 'type', 'selectOption', 'hover'].includes(context.event.type);
24
+ }
25
+
26
+ async enrich(event, context) {
27
+ try {
28
+ const { page, element } = context;
29
+
30
+ // Get bounding box
31
+ const box = await element.boundingBox();
32
+ if (!box) return null; // Element not visible
33
+
34
+ // Get viewport state
35
+ const viewport = await page.evaluate(() => ({
36
+ scrollX: window.scrollX,
37
+ scrollY: window.scrollY,
38
+ width: window.innerWidth,
39
+ height: window.innerHeight
40
+ }));
41
+
42
+ // Check if in viewport
43
+ const inViewport = (
44
+ box.y >= viewport.scrollY &&
45
+ box.y + box.height <= viewport.scrollY + viewport.height &&
46
+ box.x >= 0 &&
47
+ box.x + box.width <= viewport.width
48
+ );
49
+
50
+ return {
51
+ position: {
52
+ boundingBox: box,
53
+ viewport,
54
+ inViewport,
55
+ centerPoint: {
56
+ x: Math.round(box.x + box.width / 2),
57
+ y: Math.round(box.y + box.height / 2)
58
+ }
59
+ }
60
+ };
61
+ } catch (error) {
62
+ return this.handleError(error, event);
63
+ }
64
+ }
65
+ }
66
+
67
+ export default PositionEnricher;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Event Enrichment System
3
+ * Modular pipeline for adding data to MCP events
4
+ */
5
+
6
+ export { EventEnricher } from './base.js';
7
+ export { EnrichmentPipeline } from './pipeline.js';
8
+
9
+ // Enrichers
10
+ export { PositionEnricher } from './enrichers/position-enricher.js';
11
+ export { AccessibilityEnricher } from './enrichers/accessibility-enricher.js';
12
+ export { PageStateEnricher } from './enrichers/page-state-enricher.js';
13
+ export { DOMEnricher } from './enrichers/dom-enricher.js';
14
+ export { MCPRefEnricher } from './mcp-ref-enricher.js';
15
+ export { TraceTextEnricher } from './trace-text-enricher.js';
16
+
17
+ import { EnrichmentPipeline } from './pipeline.js';
18
+ import { PositionEnricher } from './enrichers/position-enricher.js';
19
+ import { AccessibilityEnricher } from './enrichers/accessibility-enricher.js';
20
+ import { PageStateEnricher } from './enrichers/page-state-enricher.js';
21
+ import { DOMEnricher } from './enrichers/dom-enricher.js';
22
+ import { MCPRefEnricher } from './mcp-ref-enricher.js';
23
+ import { TraceTextEnricher } from './trace-text-enricher.js';
24
+
25
+ /**
26
+ * Create a default enrichment pipeline with all enrichers
27
+ * @param {Object} config - Configuration options
28
+ * @param {boolean} config.enablePosition - Enable position enricher (default: true)
29
+ * @param {boolean} config.enableAccessibility - Enable accessibility enricher (default: true)
30
+ * @param {boolean} config.enablePageState - Enable page state enricher (default: true)
31
+ * @param {boolean} config.enableDOM - Enable DOM enricher (default: true)
32
+ * @returns {EnrichmentPipeline}
33
+ */
34
+ export function createDefaultPipeline(config = {}) {
35
+ const pipeline = new EnrichmentPipeline(config);
36
+
37
+ // Register enrichers (they'll auto-sort by priority)
38
+ // MCPRefEnricher has highest priority (200) - captures exact MCP refs
39
+ if (config.enableMCPRef !== false) {
40
+ pipeline.register(new MCPRefEnricher(config));
41
+ }
42
+
43
+ // TraceTextEnricher (190) - extracts ACTUAL text from trace (for Chinese/multi-language)
44
+ if (config.enableTraceText !== false) {
45
+ pipeline.register(new TraceTextEnricher(config));
46
+ }
47
+
48
+ if (config.enableAccessibility !== false) {
49
+ pipeline.register(new AccessibilityEnricher(config));
50
+ }
51
+
52
+ if (config.enablePageState !== false) {
53
+ pipeline.register(new PageStateEnricher(config));
54
+ }
55
+
56
+ if (config.enablePosition !== false) {
57
+ pipeline.register(new PositionEnricher(config));
58
+ }
59
+
60
+ if (config.enableDOM !== false) {
61
+ pipeline.register(new DOMEnricher(config));
62
+ }
63
+
64
+ return pipeline;
65
+ }
66
+
67
+ /**
68
+ * Create a minimal pipeline (only critical enrichers)
69
+ * @param {Object} config
70
+ * @returns {EnrichmentPipeline}
71
+ */
72
+ export function createMinimalPipeline(config = {}) {
73
+ const pipeline = new EnrichmentPipeline(config);
74
+
75
+ // Only most critical enrichers
76
+ pipeline.register(new AccessibilityEnricher(config));
77
+ pipeline.register(new PageStateEnricher(config));
78
+
79
+ return pipeline;
80
+ }
81
+
82
+ /**
83
+ * Create a custom pipeline
84
+ * @param {Array} enrichers - Array of enricher instances
85
+ * @param {Object} config
86
+ * @returns {EnrichmentPipeline}
87
+ */
88
+ export function createCustomPipeline(enrichers, config = {}) {
89
+ const pipeline = new EnrichmentPipeline(config);
90
+
91
+ for (const enricher of enrichers) {
92
+ pipeline.register(enricher);
93
+ }
94
+
95
+ return pipeline;
96
+ }