@vybestack/llxprt-code-core 0.1.18 → 0.1.19-alpha

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 (182) hide show
  1. package/README.md +1 -0
  2. package/dist/src/config/config.alwaysAllow.test.d.ts +6 -0
  3. package/dist/src/config/config.alwaysAllow.test.js +84 -0
  4. package/dist/src/config/config.alwaysAllow.test.js.map +1 -0
  5. package/dist/src/config/config.d.ts +26 -0
  6. package/dist/src/config/config.js +43 -0
  7. package/dist/src/config/config.js.map +1 -1
  8. package/dist/src/config/config.test.js +8 -0
  9. package/dist/src/config/config.test.js.map +1 -1
  10. package/dist/src/core/client.d.ts +0 -2
  11. package/dist/src/core/client.js +10 -85
  12. package/dist/src/core/client.js.map +1 -1
  13. package/dist/src/core/client.test.js +7 -2
  14. package/dist/src/core/client.test.js.map +1 -1
  15. package/dist/src/core/contentGenerator.js +1 -1
  16. package/dist/src/core/contentGenerator.js.map +1 -1
  17. package/dist/src/core/coreToolScheduler.d.ts +17 -7
  18. package/dist/src/core/coreToolScheduler.js +121 -18
  19. package/dist/src/core/coreToolScheduler.js.map +1 -1
  20. package/dist/src/core/coreToolScheduler.test.js +25 -37
  21. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  22. package/dist/src/core/geminiChat.d.ts +3 -0
  23. package/dist/src/core/geminiChat.js +17 -13
  24. package/dist/src/core/geminiChat.js.map +1 -1
  25. package/dist/src/core/nonInteractiveToolExecutor.js +4 -1
  26. package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
  27. package/dist/src/core/nonInteractiveToolExecutor.test.js +8 -31
  28. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  29. package/dist/src/core/subagent.d.ts +230 -0
  30. package/dist/src/core/subagent.js +453 -0
  31. package/dist/src/core/subagent.js.map +1 -0
  32. package/dist/src/core/subagent.test.d.ts +6 -0
  33. package/dist/src/core/subagent.test.js +519 -0
  34. package/dist/src/core/subagent.test.js.map +1 -0
  35. package/dist/src/hooks/tool-render-suppression-hook.d.ts +16 -0
  36. package/dist/src/hooks/tool-render-suppression-hook.js +26 -0
  37. package/dist/src/hooks/tool-render-suppression-hook.js.map +1 -0
  38. package/dist/src/hooks/tool-render-suppression-hook.test.d.ts +6 -0
  39. package/dist/src/hooks/tool-render-suppression-hook.test.js +59 -0
  40. package/dist/src/hooks/tool-render-suppression-hook.test.js.map +1 -0
  41. package/dist/src/ide/ide-client.d.ts +22 -1
  42. package/dist/src/ide/ide-client.js +145 -15
  43. package/dist/src/ide/ide-client.js.map +1 -1
  44. package/dist/src/ide/ideContext.d.ts +127 -32
  45. package/dist/src/ide/ideContext.js +45 -0
  46. package/dist/src/ide/ideContext.js.map +1 -1
  47. package/dist/src/index.d.ts +3 -0
  48. package/dist/src/index.js +3 -0
  49. package/dist/src/index.js.map +1 -1
  50. package/dist/src/integration-tests/todo-system.test.js +38 -602
  51. package/dist/src/integration-tests/todo-system.test.js.map +1 -1
  52. package/dist/src/providers/gemini/GeminiProvider.js +5 -5
  53. package/dist/src/providers/gemini/GeminiProvider.js.map +1 -1
  54. package/dist/src/providers/openai/OpenAIProvider.js +8 -1
  55. package/dist/src/providers/openai/OpenAIProvider.js.map +1 -1
  56. package/dist/src/providers/openai/OpenAIProvider.test.js +106 -0
  57. package/dist/src/providers/openai/OpenAIProvider.test.js.map +1 -1
  58. package/dist/src/providers/types/IProviderConfig.d.ts +5 -0
  59. package/dist/src/services/shellExecutionService.js +29 -5
  60. package/dist/src/services/shellExecutionService.js.map +1 -1
  61. package/dist/src/services/shellExecutionService.test.js +8 -0
  62. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  63. package/dist/src/services/todo-context-tracker.d.ts +31 -0
  64. package/dist/src/services/todo-context-tracker.js +45 -0
  65. package/dist/src/services/todo-context-tracker.js.map +1 -0
  66. package/dist/src/services/tool-call-tracker-service.d.ts +52 -0
  67. package/dist/src/services/tool-call-tracker-service.js +170 -0
  68. package/dist/src/services/tool-call-tracker-service.js.map +1 -0
  69. package/dist/src/services/tool-call-tracker-service.test.d.ts +6 -0
  70. package/dist/src/services/tool-call-tracker-service.test.js +99 -0
  71. package/dist/src/services/tool-call-tracker-service.test.js.map +1 -0
  72. package/dist/src/telemetry/loggers.test.circular.js +7 -2
  73. package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
  74. package/dist/src/telemetry/loggers.test.js +5 -1
  75. package/dist/src/telemetry/loggers.test.js.map +1 -1
  76. package/dist/src/telemetry/metrics.d.ts +1 -1
  77. package/dist/src/telemetry/metrics.js.map +1 -1
  78. package/dist/src/telemetry/types.d.ts +2 -1
  79. package/dist/src/telemetry/types.js +3 -1
  80. package/dist/src/telemetry/types.js.map +1 -1
  81. package/dist/src/telemetry/uiTelemetry.d.ts +2 -0
  82. package/dist/src/telemetry/uiTelemetry.js +2 -0
  83. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  84. package/dist/src/telemetry/uiTelemetry.test.js +11 -1
  85. package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
  86. package/dist/src/test-utils/tools.d.ts +23 -0
  87. package/dist/src/test-utils/tools.js +41 -0
  88. package/dist/src/test-utils/tools.js.map +1 -0
  89. package/dist/src/tools/edit.d.ts +5 -33
  90. package/dist/src/tools/edit.js +129 -124
  91. package/dist/src/tools/edit.js.map +1 -1
  92. package/dist/src/tools/edit.test.js +123 -50
  93. package/dist/src/tools/edit.test.js.map +1 -1
  94. package/dist/src/tools/glob.d.ts +3 -10
  95. package/dist/src/tools/glob.js +97 -99
  96. package/dist/src/tools/glob.js.map +1 -1
  97. package/dist/src/tools/glob.test.js +37 -26
  98. package/dist/src/tools/glob.test.js.map +1 -1
  99. package/dist/src/tools/grep.d.ts +3 -35
  100. package/dist/src/tools/grep.js +117 -88
  101. package/dist/src/tools/grep.js.map +1 -1
  102. package/dist/src/tools/grep.test.js +36 -22
  103. package/dist/src/tools/grep.test.js.map +1 -1
  104. package/dist/src/tools/mcp-client.d.ts +10 -0
  105. package/dist/src/tools/mcp-client.js +59 -0
  106. package/dist/src/tools/mcp-client.js.map +1 -1
  107. package/dist/src/tools/mcp-client.test.js +294 -1
  108. package/dist/src/tools/mcp-client.test.js.map +1 -1
  109. package/dist/src/tools/memoryTool.d.ts +2 -2
  110. package/dist/src/tools/memoryTool.js +1 -0
  111. package/dist/src/tools/memoryTool.js.map +1 -1
  112. package/dist/src/tools/modifiable-tool.d.ts +8 -5
  113. package/dist/src/tools/modifiable-tool.js +4 -1
  114. package/dist/src/tools/modifiable-tool.js.map +1 -1
  115. package/dist/src/tools/modifiable-tool.test.js +3 -3
  116. package/dist/src/tools/modifiable-tool.test.js.map +1 -1
  117. package/dist/src/tools/read-file.d.ts +4 -6
  118. package/dist/src/tools/read-file.js +55 -46
  119. package/dist/src/tools/read-file.js.map +1 -1
  120. package/dist/src/tools/read-file.test.js +201 -126
  121. package/dist/src/tools/read-file.test.js.map +1 -1
  122. package/dist/src/tools/read-many-files.js +8 -2
  123. package/dist/src/tools/read-many-files.js.map +1 -1
  124. package/dist/src/tools/read-many-files.test.js +16 -0
  125. package/dist/src/tools/read-many-files.test.js.map +1 -1
  126. package/dist/src/tools/todo-events.d.ts +22 -0
  127. package/dist/src/tools/todo-events.js +24 -0
  128. package/dist/src/tools/todo-events.js.map +1 -0
  129. package/dist/src/tools/todo-read.js.map +1 -1
  130. package/dist/src/tools/todo-schemas.d.ts +232 -4
  131. package/dist/src/tools/todo-schemas.js +13 -0
  132. package/dist/src/tools/todo-schemas.js.map +1 -1
  133. package/dist/src/tools/todo-schemas.test.js +190 -1
  134. package/dist/src/tools/todo-schemas.test.js.map +1 -1
  135. package/dist/src/tools/todo-store.d.ts +1 -4
  136. package/dist/src/tools/todo-store.js +41 -40
  137. package/dist/src/tools/todo-store.js.map +1 -1
  138. package/dist/src/tools/todo-store.test.js +34 -40
  139. package/dist/src/tools/todo-store.test.js.map +1 -1
  140. package/dist/src/tools/todo-write.d.ts +1 -1
  141. package/dist/src/tools/todo-write.js +84 -47
  142. package/dist/src/tools/todo-write.js.map +1 -1
  143. package/dist/src/tools/todo-write.test.js +23 -9
  144. package/dist/src/tools/todo-write.test.js.map +1 -1
  145. package/dist/src/tools/tool-context.d.ts +2 -0
  146. package/dist/src/tools/tool-registry.d.ts +12 -6
  147. package/dist/src/tools/tool-registry.js +16 -1
  148. package/dist/src/tools/tool-registry.js.map +1 -1
  149. package/dist/src/tools/tool-registry.test.js +1 -18
  150. package/dist/src/tools/tool-registry.test.js.map +1 -1
  151. package/dist/src/tools/tools.d.ts +127 -39
  152. package/dist/src/tools/tools.js +115 -10
  153. package/dist/src/tools/tools.js.map +1 -1
  154. package/dist/src/tools/write-file.d.ts +2 -2
  155. package/dist/src/tools/write-file.js +16 -0
  156. package/dist/src/tools/write-file.js.map +1 -1
  157. package/dist/src/tools/write-file.test.js +20 -3
  158. package/dist/src/tools/write-file.test.js.map +1 -1
  159. package/dist/src/types/modelParams.d.ts +2 -0
  160. package/dist/src/utils/environmentContext.d.ts +21 -0
  161. package/dist/src/utils/environmentContext.js +90 -0
  162. package/dist/src/utils/environmentContext.js.map +1 -0
  163. package/dist/src/utils/environmentContext.test.d.ts +6 -0
  164. package/dist/src/utils/environmentContext.test.js +139 -0
  165. package/dist/src/utils/environmentContext.test.js.map +1 -0
  166. package/dist/src/utils/fileUtils.js +2 -10
  167. package/dist/src/utils/fileUtils.js.map +1 -1
  168. package/dist/src/utils/fileUtils.test.js +1 -4
  169. package/dist/src/utils/fileUtils.test.js.map +1 -1
  170. package/dist/src/utils/filesearch/fileSearch.d.ts +1 -0
  171. package/dist/src/utils/filesearch/fileSearch.js +27 -5
  172. package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
  173. package/dist/src/utils/filesearch/fileSearch.test.js +21 -1
  174. package/dist/src/utils/filesearch/fileSearch.test.js.map +1 -1
  175. package/dist/src/utils/memoryDiscovery.js +4 -1
  176. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  177. package/dist/src/utils/shell-utils.js +14 -2
  178. package/dist/src/utils/shell-utils.js.map +1 -1
  179. package/dist/src/utils/shell-utils.shellReplacement.test.d.ts +6 -0
  180. package/dist/src/utils/shell-utils.shellReplacement.test.js +149 -0
  181. package/dist/src/utils/shell-utils.shellReplacement.test.js.map +1 -0
  182. package/package.json +2 -1
@@ -3,608 +3,44 @@
3
3
  * Copyright 2025 Vybestack LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- /**
7
- * Comprehensive behavioral tests for the todo system
8
- * @requirement REQ-007: All behavioral test scenarios
9
- */
10
- import { describe, test, expect, beforeEach, afterEach } from 'vitest';
11
- import * as fs from 'node:fs/promises';
12
- import * as path from 'node:path';
13
- import * as os from 'node:os';
14
- import { TodoWrite } from '../tools/todo-write.js';
15
- import { TodoRead } from '../tools/todo-read.js';
16
- import { TodoStore } from '../tools/todo-store.js';
17
- import { TodoReminderService } from '../services/todo-reminder-service.js';
18
- import { ComplexityAnalyzer } from '../services/complexity-analyzer.js';
19
- describe('Todo System Integration Tests', () => {
20
- const testSessionId = 'test-session-123';
21
- const testAgentId = 'test-agent-456';
22
- const todoDir = path.join(os.homedir(), '.llxprt', 'todos');
23
- beforeEach(async () => {
24
- // Clean up any existing test files
25
- await cleanupTestFiles();
26
- });
27
- afterEach(async () => {
28
- // Clean up test files after each test
29
- await cleanupTestFiles();
30
- });
31
- async function cleanupTestFiles() {
32
- try {
33
- await fs.rm(todoDir, { recursive: true, force: true });
34
- }
35
- catch (_error) {
36
- // Ignore errors if directory doesn't exist
37
- }
38
- }
39
- /**
40
- * @requirement REQ-007: Todo Persistence
41
- * @scenario Create todos in session and read back in new request
42
- * @given Todos are written to storage
43
- * @when Reading todos in a new tool instance
44
- * @then The same todos are retrieved
45
- */
46
- describe('Todo Persistence', () => {
47
- test('should persist todos across tool instances', async () => {
48
- // Arrange
49
- const context = { sessionId: testSessionId };
50
- const todoWrite = new TodoWrite();
51
- todoWrite.context = context;
52
- const testTodos = [
53
- {
54
- id: '1',
55
- content: 'Implement authentication',
56
- status: 'pending',
57
- priority: 'high',
58
- },
59
- {
60
- id: '2',
61
- content: 'Write unit tests',
62
- status: 'in_progress',
63
- priority: 'medium',
64
- },
65
- {
66
- id: '3',
67
- content: 'Deploy to production',
68
- status: 'pending',
69
- priority: 'low',
70
- },
71
- ];
72
- // Act - Write todos
73
- const writeResult = await todoWrite.execute({ todos: testTodos }, new AbortController().signal);
74
- // Assert write was successful
75
- const metadata = writeResult.metadata;
76
- expect(metadata?.statistics?.total).toBe(3);
77
- // Act - Read todos with new instance
78
- const todoRead = new TodoRead();
79
- todoRead.context = context;
80
- const readResult = await todoRead.execute({}, new AbortController().signal);
81
- // Assert - Verify persistence
82
- expect(readResult.llmContent).toContain('Implement authentication');
83
- expect(readResult.llmContent).toContain('Write unit tests');
84
- expect(readResult.llmContent).toContain('Deploy to production');
85
- });
86
- test('should handle empty todo list on first read', async () => {
87
- // Arrange
88
- const context = { sessionId: 'new-session-789' };
89
- const todoRead = new TodoRead();
90
- todoRead.context = context;
91
- // Act
92
- const result = await todoRead.execute({}, new AbortController().signal);
93
- // Assert
94
- expect(result.llmContent).toContain('No todos found');
95
- });
96
- test('should persist todos with special characters', async () => {
97
- // Arrange
98
- const context = { sessionId: testSessionId };
99
- const todoWrite = new TodoWrite();
100
- todoWrite.context = context;
101
- const specialTodos = [
102
- {
103
- id: '1',
104
- content: 'Fix "quotes" & <brackets>',
105
- status: 'pending',
106
- priority: 'high',
107
- },
108
- {
109
- id: '2',
110
- content: 'Handle\nnewlines\tand\ttabs',
111
- status: 'pending',
112
- priority: 'medium',
113
- },
114
- ];
115
- // Act
116
- await todoWrite.execute({ todos: specialTodos }, new AbortController().signal);
117
- // Read back
118
- const store = new TodoStore(testSessionId);
119
- const retrievedTodos = await store.readTodos();
120
- // Assert
121
- expect(retrievedTodos).toHaveLength(2);
122
- expect(retrievedTodos[0].content).toBe('Fix "quotes" & <brackets>');
123
- expect(retrievedTodos[1].content).toBe('Handle\nnewlines\tand\ttabs');
124
- });
125
- });
126
- /**
127
- * @requirement REQ-003/004: Reminder Injection
128
- * @scenario Write todos and verify reminder generation
129
- * @given TodoWrite operation is performed
130
- * @when State changes occur
131
- * @then Appropriate reminders are generated
132
- */
133
- describe('Reminder Injection', () => {
134
- test('should generate reminder when todos are added', async () => {
135
- // Arrange
136
- const context = { sessionId: testSessionId };
137
- const todoWrite = new TodoWrite();
138
- todoWrite.context = context;
139
- const newTodos = [
140
- { id: '1', content: 'New task', status: 'pending', priority: 'high' },
141
- ];
142
- // Act
143
- const result = await todoWrite.execute({ todos: newTodos }, new AbortController().signal);
144
- // Assert
145
- expect(result.metadata?.stateChanged).toBe(true);
146
- expect(result.metadata?.todosAdded).toBe(1);
147
- expect(result.llmContent).toContain('<system-reminder>');
148
- expect(result.llmContent).toContain('Your todo list has changed');
149
- });
150
- test('should generate reminder when todo status changes', async () => {
151
- // Arrange
152
- const context = { sessionId: testSessionId };
153
- const todoWrite = new TodoWrite();
154
- todoWrite.context = context;
155
- // First write
156
- const initialTodos = [
157
- { id: '1', content: 'Task 1', status: 'pending', priority: 'high' },
158
- ];
159
- await todoWrite.execute({ todos: initialTodos }, new AbortController().signal);
160
- // Update status
161
- const updatedTodos = [
162
- { id: '1', content: 'Task 1', status: 'in_progress', priority: 'high' },
163
- ];
164
- // Act
165
- const result = await todoWrite.execute({ todos: updatedTodos }, new AbortController().signal);
166
- // Assert
167
- expect(result.metadata?.stateChanged).toBe(true);
168
- expect(result.metadata?.statusChanged).toBe(1);
169
- expect(result.llmContent).toContain('<system-reminder>');
170
- });
171
- test('should not generate reminder when no changes occur', async () => {
172
- // Arrange
173
- const context = { sessionId: testSessionId };
174
- const todoWrite = new TodoWrite();
175
- todoWrite.context = context;
176
- const todos = [
177
- { id: '1', content: 'Task 1', status: 'pending', priority: 'high' },
178
- ];
179
- // First write
180
- await todoWrite.execute({ todos }, new AbortController().signal);
181
- // Write same todos again
182
- const result = await todoWrite.execute({ todos }, new AbortController().signal);
183
- // Assert
184
- expect(result.metadata?.stateChanged).toBe(false);
185
- expect(result.llmContent).not.toContain('<system-reminder>');
186
- });
187
- test('should generate empty todo reminder for complex task', () => {
188
- // Arrange
189
- const reminderService = new TodoReminderService();
190
- // Act
191
- const reminder = reminderService.getReminderForEmptyTodos(true);
192
- // Assert
193
- expect(reminder).toContain('<system-reminder>');
194
- expect(reminder).toContain('todo list is currently empty');
195
- expect(reminder).toContain('DO NOT mention this to the user');
196
- });
197
- });
198
- /**
199
- * @requirement REQ-005: Complexity Detection
200
- * @scenario Test various message patterns for complexity
201
- * @given Different user message patterns
202
- * @when Complexity analysis is performed
203
- * @then Correct complexity scores and suggestions are generated
204
- */
205
- describe('Complexity Detection', () => {
206
- let analyzer;
207
- beforeEach(() => {
208
- analyzer = new ComplexityAnalyzer();
209
- });
210
- test('should detect numbered list as complex task', () => {
211
- // Arrange
212
- const message = `Please help me with the following:
213
- 1. Set up authentication system
214
- 2. Create user dashboard
215
- 3. Implement payment processing
216
- 4. Deploy to production`;
217
- // Act
218
- const result = analyzer.analyzeComplexity(message);
219
- // Assert
220
- expect(result.isComplex).toBe(true);
221
- expect(result.detectedTasks).toHaveLength(4);
222
- expect(result.detectedTasks[0]).toBe('Set up authentication system');
223
- expect(result.shouldSuggestTodos).toBe(true);
224
- expect(result.suggestionReminder).toContain('multiple tasks');
225
- });
226
- test('should detect bullet points as complex task', () => {
227
- // Arrange
228
- const message = `I need to:
229
- - Configure database
230
- - Set up API endpoints
231
- - Add error handling
232
- - Write documentation`;
233
- // Act
234
- const result = analyzer.analyzeComplexity(message);
235
- // Assert
236
- expect(result.isComplex).toBe(true);
237
- expect(result.detectedTasks).toHaveLength(4);
238
- expect(result.shouldSuggestTodos).toBe(true);
239
- });
240
- test('should detect sequential indicators', () => {
241
- // Arrange
242
- const message = 'First, set up the database. Then configure the API. After that, add authentication. Finally, deploy the app.';
243
- // Act
244
- const result = analyzer.analyzeComplexity(message);
245
- // Assert
246
- expect(result.isComplex).toBe(true);
247
- expect(result.sequentialIndicators).toContain('first');
248
- expect(result.sequentialIndicators).toContain('then');
249
- expect(result.sequentialIndicators).toContain('after that');
250
- expect(result.sequentialIndicators).toContain('finally');
251
- });
252
- test('should not detect simple request as complex', () => {
253
- // Arrange
254
- const message = 'What is the capital of France?';
255
- // Act
256
- const result = analyzer.analyzeComplexity(message);
257
- // Assert
258
- expect(result.isComplex).toBe(false);
259
- expect(result.detectedTasks).toHaveLength(0);
260
- expect(result.shouldSuggestTodos).toBe(false);
261
- expect(result.suggestionReminder).toBeUndefined();
262
- });
263
- test('should detect multiple questions as complex', () => {
264
- // Arrange
265
- const message = 'How do I set up authentication? What database should I use? Where should I deploy this?';
266
- // Act
267
- const result = analyzer.analyzeComplexity(message);
268
- // Assert
269
- expect(result.questionCount).toBe(3);
270
- expect(result.isComplex).toBe(true);
271
- });
272
- test('should handle comma-separated task lists', () => {
273
- // Arrange
274
- const message = 'I need to add authentication, create tests, and deploy to production.';
275
- // Act
276
- const result = analyzer.analyzeComplexity(message);
277
- // Assert
278
- expect(result.detectedTasks).toHaveLength(3);
279
- expect(result.detectedTasks).toContain('add authentication');
280
- expect(result.detectedTasks).toContain('create tests');
281
- expect(result.detectedTasks).toContain('deploy to production');
282
- });
283
- test('should track analysis statistics', () => {
284
- // Arrange
285
- analyzer.reset(); // Clear history
286
- // Act - Perform multiple analyses
287
- const result1 = analyzer.analyzeComplexity('Simple question?');
288
- const result2 = analyzer.analyzeComplexity('1. Task one\n2. Task two\n3. Task three');
289
- const result3 = analyzer.analyzeComplexity('I need to implement authentication, write tests, deploy to production, and update documentation');
290
- const stats = analyzer.getAnalysisStats();
291
- // Assert
292
- expect(stats.totalAnalyses).toBe(3);
293
- // Check actual complexity detection results
294
- expect(result1.isComplex).toBe(false);
295
- expect(result2.isComplex).toBe(true);
296
- expect(result3.isComplex).toBe(true); // Should detect comma-separated tasks
297
- expect(stats.complexRequestCount).toBe(2);
298
- expect(stats.suggestionsGenerated).toBeGreaterThanOrEqual(1);
299
- expect(stats.averageComplexityScore).toBeGreaterThan(0);
300
- });
301
- });
302
- /**
303
- * @requirement REQ-002: Multi-Agent Isolation
304
- * @scenario Create todos in main session and subagent
305
- * @given Different sessionId and agentId combinations
306
- * @when Todos are created in different contexts
307
- * @then Each context has isolated todo lists
308
- */
309
- describe('Multi-Agent Isolation', () => {
310
- test('should isolate todos between main session and subagent', async () => {
311
- // Arrange
312
- const mainContext = { sessionId: testSessionId };
313
- const subagentContext = {
314
- sessionId: testSessionId,
315
- agentId: testAgentId,
316
- };
317
- const mainWrite = new TodoWrite();
318
- mainWrite.context = mainContext;
319
- const subagentWrite = new TodoWrite();
320
- subagentWrite.context = subagentContext;
321
- const mainTodos = [
322
- { id: '1', content: 'Main task', status: 'pending', priority: 'high' },
323
- ];
324
- const subagentTodos = [
325
- {
326
- id: '1',
327
- content: 'Subagent task',
328
- status: 'pending',
329
- priority: 'medium',
330
- },
331
- ];
332
- // Act - Write to both contexts
333
- await mainWrite.execute({ todos: mainTodos }, new AbortController().signal);
334
- await subagentWrite.execute({ todos: subagentTodos }, new AbortController().signal);
335
- // Read from both contexts
336
- const mainRead = new TodoRead();
337
- mainRead.context = mainContext;
338
- const mainResult = await mainRead.execute({}, new AbortController().signal);
339
- const subagentRead = new TodoRead();
340
- subagentRead.context = subagentContext;
341
- const subagentResult = await subagentRead.execute({}, new AbortController().signal);
342
- // Assert - Verify isolation
343
- expect(mainResult.llmContent).toContain('Main task');
344
- expect(mainResult.llmContent).not.toContain('Subagent task');
345
- expect(subagentResult.llmContent).toContain('Subagent task');
346
- expect(subagentResult.llmContent).not.toContain('Main task');
347
- });
348
- test('should use different file paths for different contexts', () => {
349
- // Arrange
350
- const mainStore = new TodoStore(testSessionId);
351
- const subagentStore = new TodoStore(testSessionId, testAgentId);
352
- // Act - Get file paths using private method (via reflection for testing)
353
- const mainPath = mainStore.getFilePath();
354
- const subagentPath = subagentStore.getFilePath();
355
- // Assert
356
- expect(mainPath).not.toBe(subagentPath);
357
- expect(mainPath).toContain(testSessionId);
358
- expect(subagentPath).toContain(testSessionId);
359
- expect(subagentPath).toContain(testAgentId);
360
- });
361
- test('should handle multiple subagents independently', async () => {
362
- // Arrange
363
- const agent1Context = {
364
- sessionId: testSessionId,
365
- agentId: 'agent-1',
366
- };
367
- const agent2Context = {
368
- sessionId: testSessionId,
369
- agentId: 'agent-2',
370
- };
371
- const agent1Write = new TodoWrite();
372
- agent1Write.context = agent1Context;
373
- const agent2Write = new TodoWrite();
374
- agent2Write.context = agent2Context;
375
- // Act
376
- await agent1Write.execute({
377
- todos: [
378
- {
379
- id: '1',
380
- content: 'Agent 1 task',
381
- status: 'pending',
382
- priority: 'high',
383
- },
384
- ],
385
- }, new AbortController().signal);
386
- await agent2Write.execute({
387
- todos: [
388
- {
389
- id: '1',
390
- content: 'Agent 2 task',
391
- status: 'pending',
392
- priority: 'low',
393
- },
394
- ],
395
- }, new AbortController().signal);
396
- // Read from both agents
397
- const store1 = new TodoStore(testSessionId, 'agent-1');
398
- const store2 = new TodoStore(testSessionId, 'agent-2');
399
- const todos1 = await store1.readTodos();
400
- const todos2 = await store2.readTodos();
401
- // Assert
402
- expect(todos1).toHaveLength(1);
403
- expect(todos1[0].content).toBe('Agent 1 task');
404
- expect(todos2).toHaveLength(1);
405
- expect(todos2[0].content).toBe('Agent 2 task');
406
- });
407
- });
408
- /**
409
- * @requirement REQ-007: Error Handling
410
- * @scenario Test various error conditions
411
- * @given Malformed data or system failures
412
- * @when Operations are performed
413
- * @then Errors are handled gracefully
414
- */
415
- describe('Error Handling', () => {
416
- test('should handle malformed todo data gracefully', async () => {
417
- // Arrange
418
- const context = { sessionId: testSessionId };
419
- const todoWrite = new TodoWrite();
420
- todoWrite.context = context;
421
- const malformedTodos = [
422
- { id: '1', content: '', status: 'pending', priority: 'high' }, // Empty content
423
- ];
424
- // Act & Assert
425
- await expect(todoWrite.execute({ todos: malformedTodos }, new AbortController().signal)).rejects.toThrow('Validation error');
426
- });
427
- test('should handle missing required fields', async () => {
428
- // Arrange
429
- const context = { sessionId: testSessionId };
430
- const todoWrite = new TodoWrite();
431
- todoWrite.context = context;
432
- const incompleteTodos = [
433
- { id: '1', content: 'Task' }, // Missing status and priority
434
- ];
435
- // Act & Assert
436
- await expect(todoWrite.execute({ todos: incompleteTodos }, new AbortController().signal)).rejects.toThrow('Validation error');
437
- });
438
- test('should handle invalid status values', async () => {
439
- // Arrange
440
- const context = { sessionId: testSessionId };
441
- const todoWrite = new TodoWrite();
442
- todoWrite.context = context;
443
- const invalidTodos = [
444
- { id: '1', content: 'Task', status: 'invalid', priority: 'high' },
445
- ];
446
- // Act & Assert
447
- await expect(todoWrite.execute({ todos: invalidTodos }, new AbortController().signal)).rejects.toThrow('Validation error');
448
- });
449
- test('should recover from corrupted storage file', async () => {
450
- // Arrange
451
- const store = new TodoStore(testSessionId);
452
- const filePath = store.getFilePath();
453
- // Create corrupted file
454
- await fs.mkdir(path.dirname(filePath), { recursive: true });
455
- await fs.writeFile(filePath, 'invalid json content', 'utf-8');
456
- // Act & Assert - Should throw when reading corrupted data
457
- await expect(store.readTodos()).rejects.toThrow();
458
- });
459
- test('should handle filesystem permission errors gracefully', async () => {
460
- // This test would require mocking fs operations to simulate permission errors
461
- // Skipping actual implementation as it would require complex mocking
462
- expect(true).toBe(true);
463
- });
464
- test('should validate todo ID uniqueness', async () => {
465
- // Arrange
466
- const context = { sessionId: testSessionId };
467
- const todoWrite = new TodoWrite();
468
- todoWrite.context = context;
469
- const duplicateIdTodos = [
470
- { id: '1', content: 'Task 1', status: 'pending', priority: 'high' },
471
- { id: '1', content: 'Task 2', status: 'pending', priority: 'medium' }, // Duplicate ID
472
- ];
473
- // Act - This should succeed as the schema doesn't enforce uniqueness
474
- const result = await todoWrite.execute({ todos: duplicateIdTodos }, new AbortController().signal);
475
- // Assert - The write should succeed but we should have 2 todos
476
- const metadata = result.metadata;
477
- expect(metadata?.statistics?.total).toBe(2);
478
- });
479
- test('should handle empty message in complexity analyzer', () => {
480
- // Arrange
481
- const analyzer = new ComplexityAnalyzer();
482
- // Act
483
- const result = analyzer.analyzeComplexity('');
484
- // Assert
485
- expect(result.isComplex).toBe(false);
486
- expect(result.complexityScore).toBe(0);
487
- expect(result.detectedTasks).toHaveLength(0);
488
- });
489
- });
490
- /**
491
- * Additional behavioral tests for comprehensive coverage
492
- */
493
- describe('Advanced Scenarios', () => {
494
- test('should calculate correct statistics after multiple operations', async () => {
495
- // Arrange
496
- const context = { sessionId: testSessionId };
497
- const todoWrite = new TodoWrite();
498
- todoWrite.context = context;
499
- // Act - Multiple writes with different states
500
- const todos1 = [
501
- { id: '1', content: 'Task 1', status: 'pending', priority: 'high' },
502
- { id: '2', content: 'Task 2', status: 'pending', priority: 'medium' },
503
- ];
504
- await todoWrite.execute({ todos: todos1 }, new AbortController().signal);
505
- const todos2 = [
506
- { id: '1', content: 'Task 1', status: 'in_progress', priority: 'high' },
507
- { id: '2', content: 'Task 2', status: 'completed', priority: 'medium' },
508
- { id: '3', content: 'Task 3', status: 'pending', priority: 'low' },
509
- ];
510
- const result = await todoWrite.execute({ todos: todos2 }, new AbortController().signal);
511
- // Assert
512
- const metadata = result.metadata;
513
- expect(metadata?.statistics).toEqual({
514
- total: 3,
515
- inProgress: 1,
516
- pending: 1,
517
- completed: 1,
518
- highPriority: 1,
519
- mediumPriority: 1,
520
- lowPriority: 1,
521
- });
522
- });
523
- test('should determine correct next action based on priorities', async () => {
524
- // Arrange
525
- const context = { sessionId: testSessionId };
526
- const todoWrite = new TodoWrite();
527
- todoWrite.context = context;
528
- const todos = [
529
- {
530
- id: '1',
531
- content: 'Low priority',
532
- status: 'pending',
533
- priority: 'low',
534
- },
535
- {
536
- id: '2',
537
- content: 'High priority',
538
- status: 'pending',
539
- priority: 'high',
540
- },
541
- {
542
- id: '3',
543
- content: 'Medium priority',
544
- status: 'pending',
545
- priority: 'medium',
546
- },
547
- ];
548
- // Act
549
- const result = await todoWrite.execute({ todos }, new AbortController().signal);
550
- // Assert - Should suggest starting with high priority
551
- expect(result.llmContent).toContain('Start with: High priority');
552
- });
553
- test('should handle large todo lists efficiently', async () => {
554
- // Arrange
555
- const context = { sessionId: testSessionId };
556
- const todoWrite = new TodoWrite();
557
- todoWrite.context = context;
558
- // Create 100 todos
559
- const largeTodoList = Array.from({ length: 100 }, (_, i) => ({
560
- id: `task-${i}`,
561
- content: `Task ${i}`,
562
- status: i % 3 === 0 ? 'completed' : i % 3 === 1 ? 'in_progress' : 'pending',
563
- priority: i % 3 === 0 ? 'high' : i % 3 === 1 ? 'medium' : 'low',
564
- }));
565
- // Act
566
- const startTime = Date.now();
567
- const result = await todoWrite.execute({ todos: largeTodoList }, new AbortController().signal);
568
- const duration = Date.now() - startTime;
569
- // Assert
570
- const metadata = result.metadata;
571
- expect(metadata?.statistics?.total).toBe(100);
572
- expect(duration).toBeLessThan(1000); // Should complete within 1 second
573
- });
574
- test('should handle concurrent access to same session', async () => {
575
- // Arrange
576
- const context = { sessionId: testSessionId };
577
- const write1 = new TodoWrite();
578
- write1.context = context;
579
- const write2 = new TodoWrite();
580
- write2.context = context;
581
- const todos1 = [
582
- {
583
- id: '1',
584
- content: 'From write 1',
585
- status: 'pending',
586
- priority: 'high',
587
- },
588
- ];
589
- const todos2 = [
590
- {
591
- id: '2',
592
- content: 'From write 2',
593
- status: 'pending',
594
- priority: 'medium',
595
- },
596
- ];
597
- // Act - Sequential writes to avoid race condition in test
598
- // In real usage, file locking would handle this
599
- await write1.execute({ todos: todos1 }, new AbortController().signal);
600
- await write2.execute({ todos: todos2 }, new AbortController().signal);
601
- // Read final state
602
- const store = new TodoStore(testSessionId);
603
- const finalTodos = await store.readTodos();
604
- // Assert - Last write should win
605
- expect(finalTodos).toHaveLength(1);
606
- expect(finalTodos[0].content).toBe('From write 2');
607
- });
6
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
7
+ import { ToolCallTrackerService } from '../services/tool-call-tracker-service.js';
8
+ import { TodoContextTracker } from '../services/todo-context-tracker.js';
9
+ describe('Todo Integration Test', () => {
10
+ const sessionId = 'test-session';
11
+ const todoId = 'test-todo';
12
+ beforeEach(() => {
13
+ // Reset the context tracker
14
+ const contextTracker = TodoContextTracker.forSession(sessionId);
15
+ contextTracker.setActiveTodo(null);
16
+ });
17
+ afterEach(() => {
18
+ // Clear the context tracker
19
+ const contextTracker = TodoContextTracker.forSession(sessionId);
20
+ contextTracker.clearActiveTodo();
21
+ // Clear any executing tool calls for this session
22
+ ToolCallTrackerService.clearToolCallsForSession(sessionId);
23
+ });
24
+ it('should track tool calls associated with active todo', async () => {
25
+ // Set up the context tracker with an active todo
26
+ const contextTracker = TodoContextTracker.forSession(sessionId);
27
+ contextTracker.setActiveTodo(todoId);
28
+ // Start tracking a tool call
29
+ const toolCallId = ToolCallTrackerService.startTrackingToolCall(sessionId, 'test_tool', { param: 'value' });
30
+ // Verify we got a tool call ID
31
+ expect(toolCallId).toBeTruthy();
32
+ // Get all tool calls for the todo
33
+ const allCalls = ToolCallTrackerService.getAllToolCalls(sessionId, todoId);
34
+ // Verify we have one executing tool call
35
+ expect(allCalls).toHaveLength(1);
36
+ expect(allCalls[0].name).toBe('test_tool');
37
+ expect(allCalls[0].parameters).toEqual({ param: 'value' });
38
+ // Complete the tool call tracking
39
+ await ToolCallTrackerService.completeToolCallTracking(sessionId, toolCallId);
40
+ // Verify the tool call is still there (now completed)
41
+ const updatedCalls = ToolCallTrackerService.getAllToolCalls(sessionId, todoId);
42
+ expect(updatedCalls).toHaveLength(1);
43
+ expect(updatedCalls[0].name).toBe('test_tool');
608
44
  });
609
45
  });
610
46
  //# sourceMappingURL=todo-system.test.js.map