@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.
- package/README.md +1 -0
- package/dist/src/config/config.alwaysAllow.test.d.ts +6 -0
- package/dist/src/config/config.alwaysAllow.test.js +84 -0
- package/dist/src/config/config.alwaysAllow.test.js.map +1 -0
- package/dist/src/config/config.d.ts +26 -0
- package/dist/src/config/config.js +43 -0
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +8 -0
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/core/client.d.ts +0 -2
- package/dist/src/core/client.js +10 -85
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +7 -2
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/contentGenerator.js +1 -1
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/coreToolScheduler.d.ts +17 -7
- package/dist/src/core/coreToolScheduler.js +121 -18
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +25 -37
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.d.ts +3 -0
- package/dist/src/core/geminiChat.js +17 -13
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.js +4 -1
- package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js +8 -31
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
- package/dist/src/core/subagent.d.ts +230 -0
- package/dist/src/core/subagent.js +453 -0
- package/dist/src/core/subagent.js.map +1 -0
- package/dist/src/core/subagent.test.d.ts +6 -0
- package/dist/src/core/subagent.test.js +519 -0
- package/dist/src/core/subagent.test.js.map +1 -0
- package/dist/src/hooks/tool-render-suppression-hook.d.ts +16 -0
- package/dist/src/hooks/tool-render-suppression-hook.js +26 -0
- package/dist/src/hooks/tool-render-suppression-hook.js.map +1 -0
- package/dist/src/hooks/tool-render-suppression-hook.test.d.ts +6 -0
- package/dist/src/hooks/tool-render-suppression-hook.test.js +59 -0
- package/dist/src/hooks/tool-render-suppression-hook.test.js.map +1 -0
- package/dist/src/ide/ide-client.d.ts +22 -1
- package/dist/src/ide/ide-client.js +145 -15
- package/dist/src/ide/ide-client.js.map +1 -1
- package/dist/src/ide/ideContext.d.ts +127 -32
- package/dist/src/ide/ideContext.js +45 -0
- package/dist/src/ide/ideContext.js.map +1 -1
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/integration-tests/todo-system.test.js +38 -602
- package/dist/src/integration-tests/todo-system.test.js.map +1 -1
- package/dist/src/providers/gemini/GeminiProvider.js +5 -5
- package/dist/src/providers/gemini/GeminiProvider.js.map +1 -1
- package/dist/src/providers/openai/OpenAIProvider.js +8 -1
- package/dist/src/providers/openai/OpenAIProvider.js.map +1 -1
- package/dist/src/providers/openai/OpenAIProvider.test.js +106 -0
- package/dist/src/providers/openai/OpenAIProvider.test.js.map +1 -1
- package/dist/src/providers/types/IProviderConfig.d.ts +5 -0
- package/dist/src/services/shellExecutionService.js +29 -5
- package/dist/src/services/shellExecutionService.js.map +1 -1
- package/dist/src/services/shellExecutionService.test.js +8 -0
- package/dist/src/services/shellExecutionService.test.js.map +1 -1
- package/dist/src/services/todo-context-tracker.d.ts +31 -0
- package/dist/src/services/todo-context-tracker.js +45 -0
- package/dist/src/services/todo-context-tracker.js.map +1 -0
- package/dist/src/services/tool-call-tracker-service.d.ts +52 -0
- package/dist/src/services/tool-call-tracker-service.js +170 -0
- package/dist/src/services/tool-call-tracker-service.js.map +1 -0
- package/dist/src/services/tool-call-tracker-service.test.d.ts +6 -0
- package/dist/src/services/tool-call-tracker-service.test.js +99 -0
- package/dist/src/services/tool-call-tracker-service.test.js.map +1 -0
- package/dist/src/telemetry/loggers.test.circular.js +7 -2
- package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +5 -1
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/metrics.d.ts +1 -1
- package/dist/src/telemetry/metrics.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +2 -1
- package/dist/src/telemetry/types.js +3 -1
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.d.ts +2 -0
- package/dist/src/telemetry/uiTelemetry.js +2 -0
- package/dist/src/telemetry/uiTelemetry.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.test.js +11 -1
- package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
- package/dist/src/test-utils/tools.d.ts +23 -0
- package/dist/src/test-utils/tools.js +41 -0
- package/dist/src/test-utils/tools.js.map +1 -0
- package/dist/src/tools/edit.d.ts +5 -33
- package/dist/src/tools/edit.js +129 -124
- package/dist/src/tools/edit.js.map +1 -1
- package/dist/src/tools/edit.test.js +123 -50
- package/dist/src/tools/edit.test.js.map +1 -1
- package/dist/src/tools/glob.d.ts +3 -10
- package/dist/src/tools/glob.js +97 -99
- package/dist/src/tools/glob.js.map +1 -1
- package/dist/src/tools/glob.test.js +37 -26
- package/dist/src/tools/glob.test.js.map +1 -1
- package/dist/src/tools/grep.d.ts +3 -35
- package/dist/src/tools/grep.js +117 -88
- package/dist/src/tools/grep.js.map +1 -1
- package/dist/src/tools/grep.test.js +36 -22
- package/dist/src/tools/grep.test.js.map +1 -1
- package/dist/src/tools/mcp-client.d.ts +10 -0
- package/dist/src/tools/mcp-client.js +59 -0
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +294 -1
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/memoryTool.d.ts +2 -2
- package/dist/src/tools/memoryTool.js +1 -0
- package/dist/src/tools/memoryTool.js.map +1 -1
- package/dist/src/tools/modifiable-tool.d.ts +8 -5
- package/dist/src/tools/modifiable-tool.js +4 -1
- package/dist/src/tools/modifiable-tool.js.map +1 -1
- package/dist/src/tools/modifiable-tool.test.js +3 -3
- package/dist/src/tools/modifiable-tool.test.js.map +1 -1
- package/dist/src/tools/read-file.d.ts +4 -6
- package/dist/src/tools/read-file.js +55 -46
- package/dist/src/tools/read-file.js.map +1 -1
- package/dist/src/tools/read-file.test.js +201 -126
- package/dist/src/tools/read-file.test.js.map +1 -1
- package/dist/src/tools/read-many-files.js +8 -2
- package/dist/src/tools/read-many-files.js.map +1 -1
- package/dist/src/tools/read-many-files.test.js +16 -0
- package/dist/src/tools/read-many-files.test.js.map +1 -1
- package/dist/src/tools/todo-events.d.ts +22 -0
- package/dist/src/tools/todo-events.js +24 -0
- package/dist/src/tools/todo-events.js.map +1 -0
- package/dist/src/tools/todo-read.js.map +1 -1
- package/dist/src/tools/todo-schemas.d.ts +232 -4
- package/dist/src/tools/todo-schemas.js +13 -0
- package/dist/src/tools/todo-schemas.js.map +1 -1
- package/dist/src/tools/todo-schemas.test.js +190 -1
- package/dist/src/tools/todo-schemas.test.js.map +1 -1
- package/dist/src/tools/todo-store.d.ts +1 -4
- package/dist/src/tools/todo-store.js +41 -40
- package/dist/src/tools/todo-store.js.map +1 -1
- package/dist/src/tools/todo-store.test.js +34 -40
- package/dist/src/tools/todo-store.test.js.map +1 -1
- package/dist/src/tools/todo-write.d.ts +1 -1
- package/dist/src/tools/todo-write.js +84 -47
- package/dist/src/tools/todo-write.js.map +1 -1
- package/dist/src/tools/todo-write.test.js +23 -9
- package/dist/src/tools/todo-write.test.js.map +1 -1
- package/dist/src/tools/tool-context.d.ts +2 -0
- package/dist/src/tools/tool-registry.d.ts +12 -6
- package/dist/src/tools/tool-registry.js +16 -1
- package/dist/src/tools/tool-registry.js.map +1 -1
- package/dist/src/tools/tool-registry.test.js +1 -18
- package/dist/src/tools/tool-registry.test.js.map +1 -1
- package/dist/src/tools/tools.d.ts +127 -39
- package/dist/src/tools/tools.js +115 -10
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/write-file.d.ts +2 -2
- package/dist/src/tools/write-file.js +16 -0
- package/dist/src/tools/write-file.js.map +1 -1
- package/dist/src/tools/write-file.test.js +20 -3
- package/dist/src/tools/write-file.test.js.map +1 -1
- package/dist/src/types/modelParams.d.ts +2 -0
- package/dist/src/utils/environmentContext.d.ts +21 -0
- package/dist/src/utils/environmentContext.js +90 -0
- package/dist/src/utils/environmentContext.js.map +1 -0
- package/dist/src/utils/environmentContext.test.d.ts +6 -0
- package/dist/src/utils/environmentContext.test.js +139 -0
- package/dist/src/utils/environmentContext.test.js.map +1 -0
- package/dist/src/utils/fileUtils.js +2 -10
- package/dist/src/utils/fileUtils.js.map +1 -1
- package/dist/src/utils/fileUtils.test.js +1 -4
- package/dist/src/utils/fileUtils.test.js.map +1 -1
- package/dist/src/utils/filesearch/fileSearch.d.ts +1 -0
- package/dist/src/utils/filesearch/fileSearch.js +27 -5
- package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
- package/dist/src/utils/filesearch/fileSearch.test.js +21 -1
- package/dist/src/utils/filesearch/fileSearch.test.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.js +4 -1
- package/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/dist/src/utils/shell-utils.js +14 -2
- package/dist/src/utils/shell-utils.js.map +1 -1
- package/dist/src/utils/shell-utils.shellReplacement.test.d.ts +6 -0
- package/dist/src/utils/shell-utils.shellReplacement.test.js +149 -0
- package/dist/src/utils/shell-utils.shellReplacement.test.js.map +1 -0
- 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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|