@accomplish_ai/agent-core 0.2.0 → 0.2.2

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 (78) hide show
  1. package/README.md +141 -0
  2. package/dist/common/constants.d.ts +1 -1
  3. package/dist/common/constants.d.ts.map +1 -1
  4. package/dist/common/constants.js +1 -1
  5. package/dist/common/constants.js.map +1 -1
  6. package/dist/common/types/index.d.ts +14 -10
  7. package/dist/common/types/index.d.ts.map +1 -1
  8. package/dist/common/types/index.js +4 -10
  9. package/dist/common/types/index.js.map +1 -1
  10. package/dist/common/utils/index.d.ts +3 -3
  11. package/dist/common/utils/index.d.ts.map +1 -1
  12. package/dist/common/utils/index.js +3 -3
  13. package/dist/common/utils/index.js.map +1 -1
  14. package/dist/factories/speech.d.ts.map +1 -1
  15. package/dist/factories/speech.js.map +1 -1
  16. package/dist/index.d.ts +1 -6
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +8 -20
  19. package/dist/index.js.map +1 -1
  20. package/dist/internal/classes/SecureStorage.d.ts.map +1 -1
  21. package/dist/internal/classes/SecureStorage.js +3 -0
  22. package/dist/internal/classes/SecureStorage.js.map +1 -1
  23. package/dist/internal/classes/TaskManager.d.ts +3 -2
  24. package/dist/internal/classes/TaskManager.d.ts.map +1 -1
  25. package/dist/internal/classes/TaskManager.js +34 -1
  26. package/dist/internal/classes/TaskManager.js.map +1 -1
  27. package/dist/storage/index.d.ts +4 -1
  28. package/dist/storage/index.d.ts.map +1 -1
  29. package/dist/storage/index.js +4 -1
  30. package/dist/storage/index.js.map +1 -1
  31. package/dist/types/log-writer.d.ts +2 -15
  32. package/dist/types/log-writer.d.ts.map +1 -1
  33. package/dist/types/skills-manager.d.ts +13 -0
  34. package/dist/types/skills-manager.d.ts.map +1 -1
  35. package/dist/types/speech.d.ts +3 -2
  36. package/dist/types/speech.d.ts.map +1 -1
  37. package/dist/types/storage.d.ts +70 -0
  38. package/dist/types/storage.d.ts.map +1 -1
  39. package/dist/types/task-manager.d.ts +13 -3
  40. package/dist/types/task-manager.d.ts.map +1 -1
  41. package/dist/types.d.ts +0 -17
  42. package/dist/types.d.ts.map +1 -1
  43. package/dist/utils/index.d.ts +16 -13
  44. package/dist/utils/index.d.ts.map +1 -1
  45. package/dist/utils/index.js +13 -13
  46. package/dist/utils/index.js.map +1 -1
  47. package/mcp-tools/dev-browser/server.cjs +144 -0
  48. package/package.json +16 -3
  49. package/mcp-tools/ask-user-question/src/index.ts +0 -183
  50. package/mcp-tools/ask-user-question/tsconfig.json +0 -12
  51. package/mcp-tools/complete-task/src/index.ts +0 -92
  52. package/mcp-tools/dev-browser/src/index.ts +0 -290
  53. package/mcp-tools/dev-browser/src/relay.ts +0 -652
  54. package/mcp-tools/dev-browser/src/types.ts +0 -31
  55. package/mcp-tools/dev-browser/tsconfig.json +0 -36
  56. package/mcp-tools/dev-browser-mcp/src/index.ts +0 -3940
  57. package/mcp-tools/dev-browser-mcp/src/snapshot/compactor.test.ts +0 -86
  58. package/mcp-tools/dev-browser-mcp/src/snapshot/compactor.ts +0 -31
  59. package/mcp-tools/dev-browser-mcp/src/snapshot/differ.test.ts +0 -178
  60. package/mcp-tools/dev-browser-mcp/src/snapshot/differ.ts +0 -167
  61. package/mcp-tools/dev-browser-mcp/src/snapshot/index.ts +0 -19
  62. package/mcp-tools/dev-browser-mcp/src/snapshot/manager.test.ts +0 -247
  63. package/mcp-tools/dev-browser-mcp/src/snapshot/manager.ts +0 -131
  64. package/mcp-tools/dev-browser-mcp/src/snapshot/parser.test.ts +0 -94
  65. package/mcp-tools/dev-browser-mcp/src/snapshot/parser.ts +0 -81
  66. package/mcp-tools/dev-browser-mcp/src/snapshot/priority.test.ts +0 -104
  67. package/mcp-tools/dev-browser-mcp/src/snapshot/priority.ts +0 -84
  68. package/mcp-tools/dev-browser-mcp/src/snapshot/tokens.test.ts +0 -64
  69. package/mcp-tools/dev-browser-mcp/src/snapshot/tokens.ts +0 -36
  70. package/mcp-tools/dev-browser-mcp/src/snapshot/types.ts +0 -89
  71. package/mcp-tools/dev-browser-mcp/tsconfig.json +0 -15
  72. package/mcp-tools/file-permission/src/index.ts +0 -125
  73. package/mcp-tools/file-permission/tsconfig.json +0 -17
  74. package/mcp-tools/report-checkpoint/src/index.ts +0 -127
  75. package/mcp-tools/report-checkpoint/tsconfig.json +0 -12
  76. package/mcp-tools/report-thought/src/index.ts +0 -109
  77. package/mcp-tools/report-thought/tsconfig.json +0 -12
  78. package/mcp-tools/start-task/src/index.ts +0 -86
@@ -1,247 +0,0 @@
1
- // packages/core/mcp-tools/dev-browser-mcp/src/snapshot/manager.test.ts
2
-
3
- import { describe, it, expect, beforeEach } from 'vitest';
4
- import { SnapshotManager } from './manager.js';
5
-
6
- describe('SnapshotManager', () => {
7
- let manager: SnapshotManager;
8
-
9
- beforeEach(() => {
10
- manager = new SnapshotManager();
11
- });
12
-
13
- const simpleSnapshot = `- button "Submit" [ref=e1]`;
14
-
15
- it('returns full snapshot on first call', () => {
16
- const result = manager.processSnapshot(
17
- simpleSnapshot,
18
- 'https://example.com',
19
- 'Test Page'
20
- );
21
-
22
- expect(result.type).toBe('full');
23
- expect(result.content).toBe(simpleSnapshot);
24
- });
25
-
26
- it('returns diff on second call with same page', () => {
27
- // First call
28
- manager.processSnapshot(simpleSnapshot, 'https://example.com', 'Test');
29
-
30
- // Second call - same URL
31
- const result = manager.processSnapshot(
32
- simpleSnapshot,
33
- 'https://example.com',
34
- 'Test'
35
- );
36
-
37
- expect(result.type).toBe('diff');
38
- });
39
-
40
- it('returns full snapshot when URL changes', () => {
41
- // First call
42
- manager.processSnapshot(simpleSnapshot, 'https://example.com/page1', 'Page 1');
43
-
44
- // Second call - different URL
45
- const result = manager.processSnapshot(
46
- simpleSnapshot,
47
- 'https://example.com/page2',
48
- 'Page 2'
49
- );
50
-
51
- expect(result.type).toBe('full');
52
- });
53
-
54
- it('returns full snapshot when full_snapshot option is true', () => {
55
- // First call
56
- manager.processSnapshot(simpleSnapshot, 'https://example.com', 'Test');
57
-
58
- // Second call with full_snapshot: true
59
- const result = manager.processSnapshot(
60
- simpleSnapshot,
61
- 'https://example.com',
62
- 'Test',
63
- { fullSnapshot: true }
64
- );
65
-
66
- expect(result.type).toBe('full');
67
- });
68
-
69
- it('normalizes URLs for same-page detection', () => {
70
- // First call
71
- manager.processSnapshot(simpleSnapshot, 'https://example.com/page#section1', 'Test');
72
-
73
- // Second call - same URL, different hash
74
- const result = manager.processSnapshot(
75
- simpleSnapshot,
76
- 'https://example.com/page#section2',
77
- 'Test'
78
- );
79
-
80
- expect(result.type).toBe('diff');
81
- });
82
-
83
- it('resets state correctly', () => {
84
- // First call
85
- manager.processSnapshot(simpleSnapshot, 'https://example.com', 'Test');
86
-
87
- // Reset
88
- manager.reset();
89
-
90
- // Should act like first call again
91
- const result = manager.processSnapshot(
92
- simpleSnapshot,
93
- 'https://example.com',
94
- 'Test'
95
- );
96
-
97
- expect(result.type).toBe('full');
98
- });
99
-
100
- describe('session history', () => {
101
- it('should track navigation history', () => {
102
- // Process snapshots for different pages
103
- manager.processSnapshot(simpleSnapshot, 'https://example.com/page1', 'Page 1');
104
- manager.processSnapshot(simpleSnapshot, 'https://example.com/page2', 'Page 2');
105
- manager.processSnapshot(simpleSnapshot, 'https://example.com/page3', 'Page 3');
106
-
107
- const summary = manager.getSessionSummary();
108
- expect(summary.history).toContain('Page 1');
109
- expect(summary.history).toContain('Page 2');
110
- expect(summary.history).toContain('Page 3');
111
- expect(summary.pagesVisited).toBe(3);
112
- });
113
-
114
- it('should limit history to 10 entries', () => {
115
- for (let i = 0; i < 15; i++) {
116
- manager.processSnapshot(
117
- simpleSnapshot,
118
- `https://example.com/page${i}`,
119
- `Page ${i}`
120
- );
121
- }
122
-
123
- const summary = manager.getSessionSummary();
124
- expect(summary.pagesVisited).toBe(10);
125
- expect(summary.history).not.toContain('Page 0');
126
- expect(summary.history).toContain('Page 14');
127
- });
128
-
129
- it('should reset history on manager reset', () => {
130
- manager.processSnapshot(simpleSnapshot, 'https://example.com', 'Home');
131
- manager.reset();
132
-
133
- const summary = manager.getSessionSummary();
134
- expect(summary.pagesVisited).toBe(0);
135
- });
136
- });
137
-
138
- describe('full optimization pipeline', () => {
139
- it('should produce optimized output with all tiers', () => {
140
- // Create test YAML with various element types
141
- const yaml1 = `- button "Home" [ref=e1]
142
- - link "About" [ref=e2]
143
- - navigation "Main Nav" [ref=e3]`;
144
-
145
- const yaml2 = `- button "Search" [ref=e4]
146
- - textbox "Query" [ref=e5]
147
- - link "Results" [ref=e6]`;
148
-
149
- // Simulate navigation to first page
150
- const result1 = manager.processSnapshot(
151
- yaml1,
152
- 'https://example.com/home',
153
- 'Home',
154
- {}
155
- );
156
-
157
- // First snapshot should be full
158
- expect(result1.type).toBe('full');
159
- expect(result1.content).toBe(yaml1);
160
-
161
- // Simulate navigation to second page
162
- const result2 = manager.processSnapshot(
163
- yaml2,
164
- 'https://example.com/search',
165
- 'Search',
166
- {}
167
- );
168
-
169
- // New page should also be full snapshot
170
- expect(result2.type).toBe('full');
171
- expect(result2.content).toBe(yaml2);
172
-
173
- // Verify session tracking
174
- const summary = manager.getSessionSummary();
175
- expect(summary.pagesVisited).toBe(2);
176
- expect(summary.history).toContain('Home');
177
- expect(summary.history).toContain('Search');
178
- });
179
-
180
- it('should use diff for same-page updates', () => {
181
- const initialYaml = `- button "Submit" [ref=e1]
182
- - textbox "Name" [ref=e2]`;
183
-
184
- const updatedYaml = `- button "Submit" [ref=e1]
185
- - textbox "Name" [ref=e2]
186
- - text "Success!" [ref=e3]`;
187
-
188
- // First snapshot
189
- const result1 = manager.processSnapshot(
190
- initialYaml,
191
- 'https://example.com/form',
192
- 'Form Page',
193
- {}
194
- );
195
- expect(result1.type).toBe('full');
196
-
197
- // Same page update should use diff
198
- const result2 = manager.processSnapshot(
199
- updatedYaml,
200
- 'https://example.com/form',
201
- 'Form Page',
202
- {}
203
- );
204
- expect(result2.type).toBe('diff');
205
-
206
- // Session should show only one unique page visit path
207
- const summary = manager.getSessionSummary();
208
- expect(summary.pagesVisited).toBe(2); // Both snapshots recorded
209
- expect(summary.history).toContain('Form Page');
210
- });
211
-
212
- it('should combine navigation tracking with diff optimization', () => {
213
- // Simulate a real user flow: home -> search -> results -> back to search
214
- const homeYaml = `- link "Search" [ref=e1]
215
- - navigation "Main" [ref=e2]`;
216
-
217
- const searchYaml = `- textbox "Query" [ref=e3]
218
- - button "Search" [ref=e4]`;
219
-
220
- const searchWithResultsYaml = `- textbox "Query" [ref=e3]
221
- - button "Search" [ref=e4]
222
- - link "Result 1" [ref=e5]
223
- - link "Result 2" [ref=e6]`;
224
-
225
- // Visit home
226
- manager.processSnapshot(homeYaml, 'https://example.com/', 'Home', {});
227
-
228
- // Navigate to search
229
- manager.processSnapshot(searchYaml, 'https://example.com/search', 'Search', {});
230
-
231
- // Update search page with results (same page, should diff)
232
- const resultUpdate = manager.processSnapshot(
233
- searchWithResultsYaml,
234
- 'https://example.com/search',
235
- 'Search',
236
- {}
237
- );
238
- expect(resultUpdate.type).toBe('diff');
239
-
240
- // Verify full navigation history is tracked
241
- const summary = manager.getSessionSummary();
242
- expect(summary.pagesVisited).toBe(3);
243
- expect(summary.history).toContain('Home');
244
- expect(summary.history).toContain('Search');
245
- });
246
- });
247
- });
@@ -1,131 +0,0 @@
1
- import type { ParsedSnapshot, SnapshotResult, SessionHistoryEntry, SessionSummary } from './types.js';
2
- import { parseSnapshot } from './parser.js';
3
- import { diffSnapshots, formatDiff } from './differ.js';
4
-
5
- const SNAPSHOT_TIMEOUT_MS = 30000;
6
-
7
- export interface SnapshotManagerOptions {
8
- fullSnapshot?: boolean;
9
- interactiveOnly?: boolean;
10
- }
11
-
12
- export class SnapshotManager {
13
- private lastSnapshot: ParsedSnapshot | null = null;
14
- private lastTimestamp: number = 0;
15
- private sessionHistory: SessionHistoryEntry[] = [];
16
- private readonly MAX_HISTORY_SIZE = 10;
17
-
18
- processSnapshot(
19
- rawYaml: string,
20
- url: string,
21
- title: string,
22
- options: SnapshotManagerOptions = {}
23
- ): SnapshotResult {
24
- const currentSnapshot = parseSnapshot(rawYaml, url, title);
25
- const now = Date.now();
26
-
27
- this.recordNavigation(url, title);
28
-
29
- if (
30
- options.fullSnapshot ||
31
- !this.lastSnapshot ||
32
- now - this.lastTimestamp > SNAPSHOT_TIMEOUT_MS
33
- ) {
34
- this.updateState(currentSnapshot, now);
35
- return { type: 'full', content: rawYaml };
36
- }
37
-
38
- if (this.isSamePage(currentSnapshot.url)) {
39
- const diff = diffSnapshots(this.lastSnapshot, currentSnapshot);
40
-
41
- if (!diff) {
42
- this.updateState(currentSnapshot, now);
43
- return { type: 'full', content: rawYaml };
44
- }
45
-
46
- this.updateState(currentSnapshot, now);
47
- const formattedDiff = formatDiff(diff, url, title);
48
- return {
49
- type: 'diff',
50
- content: formattedDiff,
51
- unchangedRefs: diff.unchangedRefs,
52
- };
53
- }
54
-
55
- this.updateState(currentSnapshot, now);
56
- return { type: 'full', content: rawYaml };
57
- }
58
-
59
- reset(): void {
60
- this.lastSnapshot = null;
61
- this.lastTimestamp = 0;
62
- this.sessionHistory = [];
63
- }
64
-
65
- private isSamePage(currentUrl: string): boolean {
66
- if (!this.lastSnapshot) return false;
67
-
68
- const normalizedCurrent = this.normalizeUrl(currentUrl);
69
- const normalizedLast = this.normalizeUrl(this.lastSnapshot.url);
70
-
71
- return normalizedCurrent === normalizedLast;
72
- }
73
-
74
- private normalizeUrl(url: string): string {
75
- try {
76
- const parsed = new URL(url);
77
- parsed.hash = '';
78
- return parsed.toString();
79
- } catch {
80
- return url;
81
- }
82
- }
83
-
84
- private updateState(snapshot: ParsedSnapshot, timestamp: number): void {
85
- this.lastSnapshot = snapshot;
86
- this.lastTimestamp = timestamp;
87
- }
88
-
89
- private recordNavigation(url: string, title: string): void {
90
- this.sessionHistory.push({
91
- url,
92
- title,
93
- timestamp: Date.now(),
94
- actionsTaken: [],
95
- });
96
-
97
- if (this.sessionHistory.length > this.MAX_HISTORY_SIZE) {
98
- this.sessionHistory = this.sessionHistory.slice(-this.MAX_HISTORY_SIZE);
99
- }
100
- }
101
-
102
- public getSessionSummary(): SessionSummary {
103
- if (this.sessionHistory.length === 0) {
104
- return { history: '', pagesVisited: 0 };
105
- }
106
-
107
- const history = this.sessionHistory
108
- .map(h => h.title || new URL(h.url).pathname)
109
- .join(' → ');
110
-
111
- return {
112
- history,
113
- pagesVisited: this.sessionHistory.length,
114
- };
115
- }
116
- }
117
-
118
- let snapshotManagerInstance: SnapshotManager | null = null;
119
-
120
- export function getSnapshotManager(): SnapshotManager {
121
- if (!snapshotManagerInstance) {
122
- snapshotManagerInstance = new SnapshotManager();
123
- }
124
- return snapshotManagerInstance;
125
- }
126
-
127
- export function resetSnapshotManager(): void {
128
- if (snapshotManagerInstance) {
129
- snapshotManagerInstance.reset();
130
- }
131
- }
@@ -1,94 +0,0 @@
1
- // packages/core/mcp-tools/dev-browser-mcp/src/snapshot/parser.test.ts
2
-
3
- import { describe, it, expect } from 'vitest';
4
- import { parseSnapshot, extractTitleFromSnapshot } from './parser.js';
5
-
6
- describe('parseSnapshot', () => {
7
- it('parses a simple element with ref', () => {
8
- const yaml = `- button "Submit" [ref=e1]`;
9
- const result = parseSnapshot(yaml, 'https://example.com', 'Test Page');
10
-
11
- expect(result.elements.size).toBe(1);
12
- expect(result.elements.get('e1')).toEqual({
13
- ref: 'e1',
14
- role: 'button',
15
- name: 'Submit',
16
- });
17
- });
18
-
19
- it('parses element with value', () => {
20
- const yaml = `- textbox "Email" [ref=e1]: "user@example.com"`;
21
- const result = parseSnapshot(yaml, 'https://example.com', 'Test');
22
-
23
- expect(result.elements.get('e1')?.value).toBe('user@example.com');
24
- });
25
-
26
- it('parses disabled attribute', () => {
27
- const yaml = `- button "Submit" [ref=e1] [disabled]`;
28
- const result = parseSnapshot(yaml, 'https://example.com', 'Test');
29
-
30
- expect(result.elements.get('e1')?.disabled).toBe(true);
31
- });
32
-
33
- it('parses checked attribute', () => {
34
- const yaml = `- checkbox "Agree" [ref=e1] [checked]`;
35
- const result = parseSnapshot(yaml, 'https://example.com', 'Test');
36
-
37
- expect(result.elements.get('e1')?.checked).toBe(true);
38
- });
39
-
40
- it('parses checked=mixed attribute', () => {
41
- const yaml = `- checkbox "Partial" [ref=e1] [checked=mixed]`;
42
- const result = parseSnapshot(yaml, 'https://example.com', 'Test');
43
-
44
- expect(result.elements.get('e1')?.checked).toBe('mixed');
45
- });
46
-
47
- it('parses multiple elements', () => {
48
- const yaml = `
49
- - textbox "Email" [ref=e1]
50
- - textbox "Password" [ref=e2]
51
- - button "Login" [ref=e3]
52
- `.trim();
53
- const result = parseSnapshot(yaml, 'https://example.com', 'Login');
54
-
55
- expect(result.elements.size).toBe(3);
56
- expect(result.elements.has('e1')).toBe(true);
57
- expect(result.elements.has('e2')).toBe(true);
58
- expect(result.elements.has('e3')).toBe(true);
59
- });
60
-
61
- it('skips elements without refs', () => {
62
- const yaml = `
63
- - heading "Welcome"
64
- - button "Submit" [ref=e1]
65
- `.trim();
66
- const result = parseSnapshot(yaml, 'https://example.com', 'Test');
67
-
68
- expect(result.elements.size).toBe(1);
69
- expect(result.elements.has('e1')).toBe(true);
70
- });
71
-
72
- it('stores url and title', () => {
73
- const yaml = `- button "Test" [ref=e1]`;
74
- const result = parseSnapshot(yaml, 'https://example.com/page', 'My Page');
75
-
76
- expect(result.url).toBe('https://example.com/page');
77
- expect(result.title).toBe('My Page');
78
- });
79
- });
80
-
81
- describe('extractTitleFromSnapshot', () => {
82
- it('extracts title from Page Title header', () => {
83
- const snapshot = `# Page Info
84
- Page Title: My Login Page
85
- URL: https://example.com`;
86
-
87
- expect(extractTitleFromSnapshot(snapshot)).toBe('My Login Page');
88
- });
89
-
90
- it('returns empty string if no title found', () => {
91
- const snapshot = `# Some other content`;
92
- expect(extractTitleFromSnapshot(snapshot)).toBe('');
93
- });
94
- });
@@ -1,81 +0,0 @@
1
- import type { SnapshotElement, ParsedSnapshot } from './types.js';
2
-
3
- export function parseSnapshot(
4
- yamlSnapshot: string,
5
- url: string,
6
- title: string
7
- ): ParsedSnapshot {
8
- const elements = new Map<string, SnapshotElement>();
9
- const lines = yamlSnapshot.split('\n');
10
-
11
- const elementRegex = /^(\s*)-\s+(\w+)(?:\s+"([^"]*)"|\s+'([^']*)')?(.*)$/;
12
- const refRegex = /\[ref=(e\d+)\]/;
13
- const valueRegex = /:\s*"([^"]*)"\s*$/;
14
- const checkedRegex = /\[checked(?:=(\w+))?\]/;
15
- const disabledRegex = /\[disabled\]/;
16
- const expandedRegex = /\[expanded\]/;
17
- const selectedRegex = /\[selected\]/;
18
- const levelRegex = /\[level=(\d+)\]/;
19
- const pressedRegex = /\[pressed(?:=(\w+))?\]/;
20
-
21
- for (const line of lines) {
22
- const match = line.match(elementRegex);
23
- if (!match) continue;
24
-
25
- const [, , role, nameDouble, nameSingle, rest] = match;
26
- const name = nameDouble ?? nameSingle ?? '';
27
-
28
- const refMatch = rest.match(refRegex);
29
- if (!refMatch) continue;
30
-
31
- const ref = refMatch[1];
32
- const element: SnapshotElement = { ref, role, name };
33
-
34
- const valueMatch = line.match(valueRegex);
35
- if (valueMatch) {
36
- element.value = valueMatch[1];
37
- }
38
-
39
- const checkedMatch = rest.match(checkedRegex);
40
- if (checkedMatch) {
41
- element.checked = checkedMatch[1] === 'mixed' ? 'mixed' : true;
42
- }
43
-
44
- if (disabledRegex.test(rest)) {
45
- element.disabled = true;
46
- }
47
-
48
- if (expandedRegex.test(rest)) {
49
- element.expanded = true;
50
- }
51
-
52
- if (selectedRegex.test(rest)) {
53
- element.selected = true;
54
- }
55
-
56
- const levelMatch = rest.match(levelRegex);
57
- if (levelMatch) {
58
- element.level = parseInt(levelMatch[1], 10);
59
- }
60
-
61
- const pressedMatch = rest.match(pressedRegex);
62
- if (pressedMatch) {
63
- element.pressed = pressedMatch[1] === 'mixed' ? 'mixed' : true;
64
- }
65
-
66
- elements.set(ref, element);
67
- }
68
-
69
- return {
70
- url,
71
- title,
72
- timestamp: Date.now(),
73
- elements,
74
- rawYaml: yamlSnapshot,
75
- };
76
- }
77
-
78
- export function extractTitleFromSnapshot(snapshot: string): string {
79
- const titleMatch = snapshot.match(/(?:Page Title|Title):\s*(.+)/i);
80
- return titleMatch ? titleMatch[1].trim() : '';
81
- }
@@ -1,104 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { getElementPriority, ROLE_PRIORITIES, truncateElements, type TruncatableElement } from './priority';
3
-
4
- describe('priority scoring', () => {
5
- describe('getElementPriority', () => {
6
- it('should score buttons highest', () => {
7
- const score = getElementPriority('button', true);
8
- expect(score).toBe(150); // 100 base + 50 viewport bonus
9
- });
10
-
11
- it('should score textbox high', () => {
12
- const score = getElementPriority('textbox', true);
13
- expect(score).toBe(145); // 95 base + 50 viewport bonus
14
- });
15
-
16
- it('should give viewport bonus', () => {
17
- const inViewport = getElementPriority('link', true);
18
- const outViewport = getElementPriority('link', false);
19
- expect(inViewport - outViewport).toBe(50);
20
- });
21
-
22
- it('should default unknown roles to 50', () => {
23
- const score = getElementPriority('unknown-role', false);
24
- expect(score).toBe(50);
25
- });
26
-
27
- it('should score navigation lower than primary inputs', () => {
28
- const navigation = getElementPriority('navigation', false);
29
- const button = getElementPriority('button', false);
30
- expect(button).toBeGreaterThan(navigation);
31
- });
32
- });
33
-
34
- describe('ROLE_PRIORITIES', () => {
35
- it('should define priorities for all interactive roles', () => {
36
- const interactiveRoles = [
37
- 'button', 'link', 'textbox', 'checkbox', 'radio',
38
- 'combobox', 'listbox', 'option', 'tab', 'menuitem',
39
- ];
40
- for (const role of interactiveRoles) {
41
- expect(ROLE_PRIORITIES[role]).toBeDefined();
42
- expect(ROLE_PRIORITIES[role]).toBeGreaterThan(0);
43
- }
44
- });
45
- });
46
- });
47
-
48
- describe('truncateElements', () => {
49
- const createElements = (count: number, role = 'button', inViewport = true): TruncatableElement[] => {
50
- return Array.from({ length: count }, (_, i) => ({
51
- ref: `e${i + 1}`,
52
- role,
53
- name: `Element ${i + 1}`,
54
- inViewport,
55
- }));
56
- };
57
-
58
- it('should return all elements when under limit', () => {
59
- const elements = createElements(5);
60
- const result = truncateElements(elements, { maxElements: 10 });
61
- expect(result.elements).toHaveLength(5);
62
- expect(result.truncated).toBe(false);
63
- });
64
-
65
- it('should truncate to maxElements', () => {
66
- const elements = createElements(100);
67
- const result = truncateElements(elements, { maxElements: 50 });
68
- expect(result.elements).toHaveLength(50);
69
- expect(result.truncated).toBe(true);
70
- expect(result.totalElements).toBe(100);
71
- });
72
-
73
- it('should prioritize viewport elements', () => {
74
- const inViewport = createElements(5, 'button', true);
75
- const outViewport = createElements(5, 'button', false);
76
- const mixed = [...outViewport, ...inViewport]; // Out of viewport first
77
-
78
- const result = truncateElements(mixed, { maxElements: 5 });
79
-
80
- // Should keep all viewport elements
81
- expect(result.elements.every(e => e.inViewport)).toBe(true);
82
- });
83
-
84
- it('should prioritize by role', () => {
85
- const buttons = createElements(3, 'button', false);
86
- const links = createElements(3, 'link', false);
87
- const navs = createElements(3, 'navigation', false);
88
- const mixed = [...navs, ...links, ...buttons]; // Lowest priority first
89
-
90
- const result = truncateElements(mixed, { maxElements: 3 });
91
-
92
- // Should keep buttons (highest priority)
93
- expect(result.elements.every(e => e.role === 'button')).toBe(true);
94
- });
95
-
96
- it('should return metadata about truncation', () => {
97
- const elements = createElements(100);
98
- const result = truncateElements(elements, { maxElements: 30 });
99
-
100
- expect(result.totalElements).toBe(100);
101
- expect(result.includedElements).toBe(30);
102
- expect(result.truncated).toBe(true);
103
- });
104
- });