@probelabs/probe 0.6.0-rc100

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 (115) hide show
  1. package/README.md +583 -0
  2. package/bin/.gitkeep +0 -0
  3. package/bin/probe +158 -0
  4. package/bin/probe-binary +0 -0
  5. package/build/agent/ProbeAgent.d.ts +199 -0
  6. package/build/agent/ProbeAgent.js +1486 -0
  7. package/build/agent/acp/README.md +347 -0
  8. package/build/agent/acp/connection.js +237 -0
  9. package/build/agent/acp/connection.test.js +311 -0
  10. package/build/agent/acp/examples/simple-client.js +212 -0
  11. package/build/agent/acp/examples/tool-lifecycle.js +230 -0
  12. package/build/agent/acp/final-test.js +173 -0
  13. package/build/agent/acp/index.js +5 -0
  14. package/build/agent/acp/integration.test.js +385 -0
  15. package/build/agent/acp/manual-test.js +410 -0
  16. package/build/agent/acp/protocol-test.js +190 -0
  17. package/build/agent/acp/server.js +448 -0
  18. package/build/agent/acp/server.test.js +371 -0
  19. package/build/agent/acp/test-runner.js +216 -0
  20. package/build/agent/acp/test-utils/README.md +315 -0
  21. package/build/agent/acp/test-utils/acp-tester.js +484 -0
  22. package/build/agent/acp/test-utils/mock-acp-client.js +434 -0
  23. package/build/agent/acp/tools.js +368 -0
  24. package/build/agent/acp/tools.test.js +334 -0
  25. package/build/agent/acp/types.js +218 -0
  26. package/build/agent/acp/types.test.js +327 -0
  27. package/build/agent/appTracer.js +360 -0
  28. package/build/agent/fileSpanExporter.js +169 -0
  29. package/build/agent/index.js +7426 -0
  30. package/build/agent/mcp/client.js +338 -0
  31. package/build/agent/mcp/config.js +313 -0
  32. package/build/agent/mcp/index.js +64 -0
  33. package/build/agent/mcp/xmlBridge.js +371 -0
  34. package/build/agent/mockProvider.js +53 -0
  35. package/build/agent/probeTool.js +257 -0
  36. package/build/agent/schemaUtils.js +1726 -0
  37. package/build/agent/simpleTelemetry.js +267 -0
  38. package/build/agent/telemetry.js +225 -0
  39. package/build/agent/tokenCounter.js +395 -0
  40. package/build/agent/tools.js +163 -0
  41. package/build/cli.js +49 -0
  42. package/build/delegate.js +267 -0
  43. package/build/directory-resolver.js +237 -0
  44. package/build/downloader.js +750 -0
  45. package/build/extract.js +149 -0
  46. package/build/index.js +70 -0
  47. package/build/mcp/index.js +514 -0
  48. package/build/mcp/index.ts +608 -0
  49. package/build/query.js +116 -0
  50. package/build/search.js +247 -0
  51. package/build/tools/common.js +410 -0
  52. package/build/tools/index.js +40 -0
  53. package/build/tools/langchain.js +88 -0
  54. package/build/tools/system-message.js +121 -0
  55. package/build/tools/vercel.js +271 -0
  56. package/build/utils/file-lister.js +193 -0
  57. package/build/utils.js +128 -0
  58. package/cjs/agent/ProbeAgent.cjs +5829 -0
  59. package/cjs/index.cjs +6217 -0
  60. package/cjs/package.json +3 -0
  61. package/index.d.ts +401 -0
  62. package/package.json +114 -0
  63. package/scripts/postinstall.js +172 -0
  64. package/src/agent/ProbeAgent.d.ts +199 -0
  65. package/src/agent/ProbeAgent.js +1486 -0
  66. package/src/agent/acp/README.md +347 -0
  67. package/src/agent/acp/connection.js +237 -0
  68. package/src/agent/acp/connection.test.js +311 -0
  69. package/src/agent/acp/examples/simple-client.js +212 -0
  70. package/src/agent/acp/examples/tool-lifecycle.js +230 -0
  71. package/src/agent/acp/final-test.js +173 -0
  72. package/src/agent/acp/index.js +5 -0
  73. package/src/agent/acp/integration.test.js +385 -0
  74. package/src/agent/acp/manual-test.js +410 -0
  75. package/src/agent/acp/protocol-test.js +190 -0
  76. package/src/agent/acp/server.js +448 -0
  77. package/src/agent/acp/server.test.js +371 -0
  78. package/src/agent/acp/test-runner.js +216 -0
  79. package/src/agent/acp/test-utils/README.md +315 -0
  80. package/src/agent/acp/test-utils/acp-tester.js +484 -0
  81. package/src/agent/acp/test-utils/mock-acp-client.js +434 -0
  82. package/src/agent/acp/tools.js +368 -0
  83. package/src/agent/acp/tools.test.js +334 -0
  84. package/src/agent/acp/types.js +218 -0
  85. package/src/agent/acp/types.test.js +327 -0
  86. package/src/agent/appTracer.js +360 -0
  87. package/src/agent/fileSpanExporter.js +169 -0
  88. package/src/agent/index.js +813 -0
  89. package/src/agent/mcp/client.js +338 -0
  90. package/src/agent/mcp/config.js +313 -0
  91. package/src/agent/mcp/index.js +64 -0
  92. package/src/agent/mcp/xmlBridge.js +371 -0
  93. package/src/agent/mockProvider.js +53 -0
  94. package/src/agent/probeTool.js +257 -0
  95. package/src/agent/schemaUtils.js +1726 -0
  96. package/src/agent/simpleTelemetry.js +267 -0
  97. package/src/agent/telemetry.js +225 -0
  98. package/src/agent/tokenCounter.js +395 -0
  99. package/src/agent/tools.js +163 -0
  100. package/src/cli.js +49 -0
  101. package/src/delegate.js +267 -0
  102. package/src/directory-resolver.js +237 -0
  103. package/src/downloader.js +750 -0
  104. package/src/extract.js +149 -0
  105. package/src/index.js +70 -0
  106. package/src/mcp/index.ts +608 -0
  107. package/src/query.js +116 -0
  108. package/src/search.js +247 -0
  109. package/src/tools/common.js +410 -0
  110. package/src/tools/index.js +40 -0
  111. package/src/tools/langchain.js +88 -0
  112. package/src/tools/system-message.js +121 -0
  113. package/src/tools/vercel.js +271 -0
  114. package/src/utils/file-lister.js +193 -0
  115. package/src/utils.js +128 -0
@@ -0,0 +1,371 @@
1
+ // Tests for ACP Server
2
+ import { jest } from '@jest/globals';
3
+ import { ACPServer } from './server.js';
4
+ import { ACPConnection } from './connection.js';
5
+ import { ACP_PROTOCOL_VERSION, RequestMethod, SessionMode, ErrorCode } from './types.js';
6
+
7
+ // Mock manually handled below
8
+
9
+ describe('ACPServer', () => {
10
+ let server;
11
+ let mockConnection;
12
+
13
+ beforeEach(() => {
14
+ // Reset mocks
15
+ jest.clearAllMocks();
16
+
17
+ // Create mock connection
18
+ mockConnection = {
19
+ start: jest.fn(),
20
+ sendResponse: jest.fn(),
21
+ sendError: jest.fn(),
22
+ sendNotification: jest.fn(),
23
+ on: jest.fn()
24
+ };
25
+
26
+ // Mock the ACPConnection constructor
27
+ ACPConnection.mockImplementation(() => mockConnection);
28
+
29
+ server = new ACPServer({
30
+ debug: true,
31
+ provider: 'anthropic',
32
+ path: '/test/path'
33
+ });
34
+ });
35
+
36
+ describe('initialization', () => {
37
+ test('should create server with default options', () => {
38
+ const defaultServer = new ACPServer();
39
+ expect(defaultServer.options.debug).toBe(false);
40
+ expect(defaultServer.sessions.size).toBe(0);
41
+ expect(defaultServer.initialized).toBe(false);
42
+ });
43
+
44
+ test('should create server with custom options', () => {
45
+ expect(server.options.debug).toBe(true);
46
+ expect(server.options.provider).toBe('anthropic');
47
+ expect(server.options.path).toBe('/test/path');
48
+ });
49
+
50
+ test('should have correct capabilities', () => {
51
+ const capabilities = server.getCapabilities();
52
+ expect(capabilities.tools).toHaveLength(3);
53
+ expect(capabilities.tools[0].name).toBe('search');
54
+ expect(capabilities.tools[1].name).toBe('query');
55
+ expect(capabilities.tools[2].name).toBe('extract');
56
+ expect(capabilities.sessionManagement).toBe(true);
57
+ expect(capabilities.streaming).toBe(true);
58
+ });
59
+ });
60
+
61
+ describe('server startup', () => {
62
+ test('should start server and setup connection', async () => {
63
+ await server.start();
64
+
65
+ expect(ACPConnection).toHaveBeenCalledWith(process.stdin, process.stderr);
66
+ expect(mockConnection.on).toHaveBeenCalledWith('request', expect.any(Function));
67
+ expect(mockConnection.on).toHaveBeenCalledWith('notification', expect.any(Function));
68
+ expect(mockConnection.on).toHaveBeenCalledWith('error', expect.any(Function));
69
+ expect(mockConnection.on).toHaveBeenCalledWith('disconnect', expect.any(Function));
70
+ expect(mockConnection.start).toHaveBeenCalled();
71
+ });
72
+ });
73
+
74
+ describe('initialize request', () => {
75
+ test('should handle valid initialize request', async () => {
76
+ const params = { protocolVersion: ACP_PROTOCOL_VERSION };
77
+
78
+ const result = await server.handleInitialize(params);
79
+
80
+ expect(result).toEqual({
81
+ protocolVersion: ACP_PROTOCOL_VERSION,
82
+ serverInfo: {
83
+ name: 'probe-agent-acp',
84
+ version: '1.0.0',
85
+ description: 'Probe AI agent with code search capabilities'
86
+ },
87
+ capabilities: server.capabilities
88
+ });
89
+ expect(server.initialized).toBe(true);
90
+ });
91
+
92
+ test('should reject invalid protocol version', async () => {
93
+ const params = { protocolVersion: '2.0' };
94
+
95
+ await expect(server.handleInitialize(params)).rejects.toThrow('Unsupported protocol version: 2.0');
96
+ });
97
+
98
+ test('should require protocolVersion parameter', async () => {
99
+ await expect(server.handleInitialize({})).rejects.toThrow('Invalid params: protocolVersion required');
100
+ await expect(server.handleInitialize(null)).rejects.toThrow('Invalid params: protocolVersion required');
101
+ });
102
+ });
103
+
104
+ describe('session management', () => {
105
+ test('should create new session', async () => {
106
+ const result = await server.handleNewSession({});
107
+
108
+ expect(result.sessionId).toBeDefined();
109
+ expect(result.mode).toBe(SessionMode.NORMAL);
110
+ expect(result.createdAt).toBeDefined();
111
+ expect(server.sessions.has(result.sessionId)).toBe(true);
112
+ });
113
+
114
+ test('should create session with custom ID and mode', async () => {
115
+ const customId = 'test-session-123';
116
+ const params = {
117
+ sessionId: customId,
118
+ mode: SessionMode.PLANNING
119
+ };
120
+
121
+ const result = await server.handleNewSession(params);
122
+
123
+ expect(result.sessionId).toBe(customId);
124
+ expect(result.mode).toBe(SessionMode.PLANNING);
125
+ expect(server.sessions.has(customId)).toBe(true);
126
+ });
127
+
128
+ test('should load existing session', async () => {
129
+ // Create a session first
130
+ const createResult = await server.handleNewSession({});
131
+ const sessionId = createResult.sessionId;
132
+
133
+ // Load the session
134
+ const loadResult = await server.handleLoadSession({ sessionId });
135
+
136
+ expect(loadResult.id).toBe(sessionId);
137
+ expect(loadResult.mode).toBe(SessionMode.NORMAL);
138
+ expect(loadResult.historyLength).toBe(0);
139
+ expect(loadResult.toolCallsCount).toBe(0);
140
+ });
141
+
142
+ test('should fail to load non-existent session', async () => {
143
+ const params = { sessionId: 'non-existent' };
144
+
145
+ await expect(server.handleLoadSession(params)).rejects.toThrow('Session not found: non-existent');
146
+ });
147
+
148
+ test('should set session mode', async () => {
149
+ // Create a session first
150
+ const createResult = await server.handleNewSession({});
151
+ const sessionId = createResult.sessionId;
152
+
153
+ // Set mode
154
+ const result = await server.handleSetSessionMode({
155
+ sessionId,
156
+ mode: SessionMode.PLANNING
157
+ });
158
+
159
+ expect(result.success).toBe(true);
160
+ expect(mockConnection.sendNotification).toHaveBeenCalledWith(
161
+ 'sessionUpdated',
162
+ { sessionId, mode: SessionMode.PLANNING }
163
+ );
164
+
165
+ const session = server.sessions.get(sessionId);
166
+ expect(session.mode).toBe(SessionMode.PLANNING);
167
+ });
168
+ });
169
+
170
+ describe('request handling', () => {
171
+ test('should handle requests and send responses', async () => {
172
+ const message = {
173
+ method: RequestMethod.INITIALIZE,
174
+ params: { protocolVersion: ACP_PROTOCOL_VERSION },
175
+ id: 1
176
+ };
177
+
178
+ await server.handleRequest(message);
179
+
180
+ expect(mockConnection.sendResponse).toHaveBeenCalledWith(1, expect.objectContaining({
181
+ protocolVersion: ACP_PROTOCOL_VERSION
182
+ }));
183
+ });
184
+
185
+ test('should handle unknown methods', async () => {
186
+ const message = {
187
+ method: 'unknownMethod',
188
+ params: {},
189
+ id: 2
190
+ };
191
+
192
+ await server.handleRequest(message);
193
+
194
+ expect(mockConnection.sendError).toHaveBeenCalledWith(
195
+ 2,
196
+ ErrorCode.METHOD_NOT_FOUND,
197
+ 'Unknown method: unknownMethod'
198
+ );
199
+ });
200
+
201
+ test('should handle request errors', async () => {
202
+ const message = {
203
+ method: RequestMethod.LOAD_SESSION,
204
+ params: { sessionId: 'invalid' },
205
+ id: 3
206
+ };
207
+
208
+ await server.handleRequest(message);
209
+
210
+ expect(mockConnection.sendError).toHaveBeenCalledWith(
211
+ 3,
212
+ ErrorCode.INTERNAL_ERROR,
213
+ 'Session not found: invalid'
214
+ );
215
+ });
216
+ });
217
+
218
+ describe('prompt handling', () => {
219
+ let mockAgent;
220
+
221
+ beforeEach(async () => {
222
+ // Import and mock ProbeAgent
223
+ const { ProbeAgent } = await import('../ProbeAgent.js');
224
+ mockAgent = {
225
+ answer: jest.fn().mockResolvedValue('Test response'),
226
+ cancel: jest.fn()
227
+ };
228
+ ProbeAgent.mockImplementation(() => mockAgent);
229
+
230
+ // Create a session
231
+ await server.handleNewSession({ sessionId: 'test-session' });
232
+ });
233
+
234
+ test('should handle prompt request successfully', async () => {
235
+ const params = {
236
+ sessionId: 'test-session',
237
+ message: 'Test question'
238
+ };
239
+
240
+ const result = await server.handlePrompt(params);
241
+
242
+ expect(mockAgent.answer).toHaveBeenCalledWith('Test question');
243
+ expect(result).toEqual({
244
+ content: [{ type: 'text', text: 'Test response' }],
245
+ sessionId: 'test-session',
246
+ timestamp: expect.any(String)
247
+ });
248
+
249
+ // Check that history was updated
250
+ const session = server.sessions.get('test-session');
251
+ expect(session.history).toHaveLength(2);
252
+ expect(session.history[0].role).toBe('user');
253
+ expect(session.history[1].role).toBe('assistant');
254
+ });
255
+
256
+ test('should handle prompt errors gracefully', async () => {
257
+ mockAgent.answer.mockRejectedValue(new Error('AI Error'));
258
+
259
+ const params = {
260
+ sessionId: 'test-session',
261
+ message: 'Test question'
262
+ };
263
+
264
+ const result = await server.handlePrompt(params);
265
+
266
+ expect(result).toEqual({
267
+ content: [{ type: 'text', text: 'Error: AI Error' }],
268
+ sessionId: 'test-session',
269
+ timestamp: expect.any(String),
270
+ error: true
271
+ });
272
+ });
273
+
274
+ test('should require sessionId and message', async () => {
275
+ await expect(server.handlePrompt({})).rejects.toThrow('Invalid params: sessionId and message required');
276
+ await expect(server.handlePrompt({ sessionId: 'test' })).rejects.toThrow('Invalid params: sessionId and message required');
277
+ await expect(server.handlePrompt({ message: 'test' })).rejects.toThrow('Invalid params: sessionId and message required');
278
+ });
279
+
280
+ test('should require valid session', async () => {
281
+ const params = {
282
+ sessionId: 'invalid-session',
283
+ message: 'Test question'
284
+ };
285
+
286
+ await expect(server.handlePrompt(params)).rejects.toThrow('Session not found: invalid-session');
287
+ });
288
+ });
289
+
290
+ describe('cancel handling', () => {
291
+ let mockAgent;
292
+
293
+ beforeEach(async () => {
294
+ const { ProbeAgent } = await import('../ProbeAgent.js');
295
+ mockAgent = {
296
+ answer: jest.fn().mockResolvedValue('Test response'),
297
+ cancel: jest.fn()
298
+ };
299
+ ProbeAgent.mockImplementation(() => mockAgent);
300
+
301
+ // Create a session and trigger agent creation
302
+ await server.handleNewSession({ sessionId: 'test-session' });
303
+ await server.handlePrompt({
304
+ sessionId: 'test-session',
305
+ message: 'Test question'
306
+ });
307
+ });
308
+
309
+ test('should cancel session operations', async () => {
310
+ const result = await server.handleCancel({ sessionId: 'test-session' });
311
+
312
+ expect(mockAgent.cancel).toHaveBeenCalled();
313
+ expect(result.success).toBe(true);
314
+ });
315
+
316
+ test('should handle cancel for session without agent', async () => {
317
+ await server.handleNewSession({ sessionId: 'new-session' });
318
+
319
+ const result = await server.handleCancel({ sessionId: 'new-session' });
320
+ expect(result.success).toBe(true);
321
+ });
322
+
323
+ test('should require sessionId for cancel', async () => {
324
+ await expect(server.handleCancel({})).rejects.toThrow('Invalid params: sessionId required');
325
+ });
326
+ });
327
+
328
+ describe('disconnect handling', () => {
329
+ let mockAgent;
330
+
331
+ beforeEach(async () => {
332
+ const { ProbeAgent } = await import('../ProbeAgent.js');
333
+ mockAgent = {
334
+ answer: jest.fn().mockResolvedValue('Test response'),
335
+ cancel: jest.fn()
336
+ };
337
+ ProbeAgent.mockImplementation(() => mockAgent);
338
+
339
+ // Create sessions with agents
340
+ await server.handleNewSession({ sessionId: 'session1' });
341
+ await server.handleNewSession({ sessionId: 'session2' });
342
+ await server.handlePrompt({ sessionId: 'session1', message: 'test' });
343
+ await server.handlePrompt({ sessionId: 'session2', message: 'test' });
344
+ });
345
+
346
+ test('should clean up sessions and cancel agents on disconnect', () => {
347
+ expect(server.sessions.size).toBe(2);
348
+
349
+ server.handleDisconnect();
350
+
351
+ expect(mockAgent.cancel).toHaveBeenCalledTimes(2);
352
+ expect(server.sessions.size).toBe(0);
353
+ });
354
+ });
355
+
356
+ describe('stats', () => {
357
+ test('should return server statistics', async () => {
358
+ await server.handleNewSession({});
359
+ await server.handleNewSession({});
360
+ server.initialized = true;
361
+
362
+ const stats = server.getStats();
363
+
364
+ expect(stats).toEqual({
365
+ sessions: 2,
366
+ initialized: true,
367
+ capabilities: server.capabilities
368
+ });
369
+ });
370
+ });
371
+ });
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Simple test runner for ACP implementation
5
+ * Tests basic functionality without requiring a full test framework
6
+ */
7
+
8
+ import { ACPServer } from './server.js';
9
+ import { ACPConnection } from './connection.js';
10
+ import { validateMessage, createMessage, createResponse, ACP_PROTOCOL_VERSION } from './types.js';
11
+ import { EventEmitter } from 'events';
12
+
13
+ class TestRunner {
14
+ constructor() {
15
+ this.tests = [];
16
+ this.passed = 0;
17
+ this.failed = 0;
18
+ }
19
+
20
+ test(name, fn) {
21
+ this.tests.push({ name, fn });
22
+ }
23
+
24
+ assert(condition, message) {
25
+ if (!condition) {
26
+ throw new Error(message);
27
+ }
28
+ }
29
+
30
+ assertEqual(actual, expected, message = '') {
31
+ if (JSON.stringify(actual) !== JSON.stringify(expected)) {
32
+ throw new Error(`${message}\nExpected: ${JSON.stringify(expected)}\nActual: ${JSON.stringify(actual)}`);
33
+ }
34
+ }
35
+
36
+ async run() {
37
+ console.log('🧪 Running ACP Tests');
38
+ console.log('=' .repeat(40));
39
+
40
+ for (const { name, fn } of this.tests) {
41
+ try {
42
+ await fn();
43
+ console.log(`✅ ${name}`);
44
+ this.passed++;
45
+ } catch (error) {
46
+ console.log(`❌ ${name}`);
47
+ console.log(` Error: ${error.message}`);
48
+ this.failed++;
49
+ }
50
+ }
51
+
52
+ console.log('\n📊 Test Results');
53
+ console.log('-'.repeat(20));
54
+ console.log(`Passed: ${this.passed}`);
55
+ console.log(`Failed: ${this.failed}`);
56
+ console.log(`Total: ${this.tests.length}`);
57
+
58
+ if (this.failed > 0) {
59
+ process.exit(1);
60
+ } else {
61
+ console.log('\n🎉 All tests passed!');
62
+ }
63
+ }
64
+ }
65
+
66
+ const runner = new TestRunner();
67
+
68
+ // Test types and utilities
69
+ runner.test('Message validation', () => {
70
+ // Valid messages
71
+ runner.assert(validateMessage({ jsonrpc: '2.0', method: 'test' }).valid, 'Valid notification should pass');
72
+ runner.assert(validateMessage({ jsonrpc: '2.0', method: 'test', id: 1 }).valid, 'Valid request should pass');
73
+ runner.assert(validateMessage({ jsonrpc: '2.0', id: 1, result: {} }).valid, 'Valid response should pass');
74
+
75
+ // Invalid messages
76
+ runner.assert(!validateMessage(null).valid, 'Null should fail');
77
+ runner.assert(!validateMessage({ jsonrpc: '1.0', method: 'test' }).valid, 'Wrong version should fail');
78
+ runner.assert(!validateMessage({ jsonrpc: '2.0', id: 1 }).valid, 'Response without result/error should fail');
79
+ });
80
+
81
+ runner.test('Message creation', () => {
82
+ const message = createMessage('test', { param: 'value' }, 123);
83
+ runner.assertEqual(message, {
84
+ jsonrpc: '2.0',
85
+ method: 'test',
86
+ params: { param: 'value' },
87
+ id: 123
88
+ }, 'Message creation should work correctly');
89
+
90
+ const response = createResponse(456, { success: true });
91
+ runner.assertEqual(response, {
92
+ jsonrpc: '2.0',
93
+ id: 456,
94
+ result: { success: true }
95
+ }, 'Response creation should work correctly');
96
+ });
97
+
98
+ // Test server capabilities
99
+ runner.test('Server capabilities', () => {
100
+ const server = new ACPServer();
101
+ const capabilities = server.getCapabilities();
102
+
103
+ runner.assert(Array.isArray(capabilities.tools), 'Should have tools array');
104
+ runner.assert(capabilities.tools.length === 3, 'Should have 3 tools');
105
+ runner.assert(capabilities.sessionManagement === true, 'Should support sessions');
106
+ runner.assert(capabilities.streaming === true, 'Should support streaming');
107
+
108
+ const toolNames = capabilities.tools.map(t => t.name);
109
+ runner.assert(toolNames.includes('search'), 'Should have search tool');
110
+ runner.assert(toolNames.includes('query'), 'Should have query tool');
111
+ runner.assert(toolNames.includes('extract'), 'Should have extract tool');
112
+ });
113
+
114
+ // Test server initialization
115
+ runner.test('Server initialization', async () => {
116
+ const server = new ACPServer({ debug: false });
117
+
118
+ // Test valid initialization
119
+ const result = await server.handleInitialize({
120
+ protocolVersion: ACP_PROTOCOL_VERSION
121
+ });
122
+
123
+ runner.assertEqual(result.protocolVersion, ACP_PROTOCOL_VERSION, 'Should return correct version');
124
+ runner.assert(result.serverInfo.name === 'probe-agent-acp', 'Should have correct server name');
125
+ runner.assert(server.initialized === true, 'Server should be initialized');
126
+
127
+ // Test invalid protocol version
128
+ try {
129
+ await server.handleInitialize({ protocolVersion: '2.0' });
130
+ runner.assert(false, 'Should reject invalid protocol version');
131
+ } catch (error) {
132
+ runner.assert(error.message.includes('Unsupported protocol version'), 'Should have version error');
133
+ }
134
+ });
135
+
136
+ // Test session management
137
+ runner.test('Session management', async () => {
138
+ const server = new ACPServer({ debug: false });
139
+
140
+ // Create session
141
+ const createResult = await server.handleNewSession({});
142
+ runner.assert(createResult.sessionId, 'Should return session ID');
143
+ runner.assert(createResult.mode === 'normal', 'Should default to normal mode');
144
+ runner.assert(server.sessions.has(createResult.sessionId), 'Should store session');
145
+
146
+ // Load session
147
+ const loadResult = await server.handleLoadSession({
148
+ sessionId: createResult.sessionId
149
+ });
150
+ runner.assertEqual(loadResult.id, createResult.sessionId, 'Should load correct session');
151
+
152
+ // Set session mode
153
+ const modeResult = await server.handleSetSessionMode({
154
+ sessionId: createResult.sessionId,
155
+ mode: 'planning'
156
+ });
157
+ runner.assert(modeResult.success === true, 'Should set mode successfully');
158
+
159
+ const session = server.sessions.get(createResult.sessionId);
160
+ runner.assertEqual(session.mode, 'planning', 'Should update session mode');
161
+ });
162
+
163
+ // Test connection (basic functionality)
164
+ runner.test('Connection basics', () => {
165
+ class MockStream extends EventEmitter {
166
+ constructor() {
167
+ super();
168
+ this.encoding = null;
169
+ this.writtenData = [];
170
+ }
171
+ setEncoding(enc) { this.encoding = enc; }
172
+ write(data) { this.writtenData.push(data); return true; }
173
+ }
174
+
175
+ const input = new MockStream();
176
+ const output = new MockStream();
177
+ const connection = new ACPConnection(input, output);
178
+
179
+ connection.start();
180
+ runner.assert(connection.isConnected === true, 'Should be connected');
181
+
182
+ // Test message sending
183
+ connection.sendNotification('test', { data: 'value' });
184
+ runner.assert(output.writtenData.length === 1, 'Should send notification');
185
+
186
+ const sent = JSON.parse(output.writtenData[0]);
187
+ runner.assertEqual(sent.method, 'test', 'Should send correct method');
188
+ runner.assertEqual(sent.params, { data: 'value' }, 'Should send correct params');
189
+ runner.assert(!sent.id, 'Notification should not have ID');
190
+
191
+ connection.close();
192
+ });
193
+
194
+ // Test error handling
195
+ runner.test('Error handling', async () => {
196
+ const server = new ACPServer({ debug: false });
197
+
198
+ // Test missing session
199
+ try {
200
+ await server.handleLoadSession({ sessionId: 'nonexistent' });
201
+ runner.assert(false, 'Should fail for nonexistent session');
202
+ } catch (error) {
203
+ runner.assert(error.message.includes('Session not found'), 'Should have session error');
204
+ }
205
+
206
+ // Test missing parameters
207
+ try {
208
+ await server.handlePrompt({});
209
+ runner.assert(false, 'Should require sessionId and message');
210
+ } catch (error) {
211
+ runner.assert(error.message.includes('Invalid params'), 'Should have params error');
212
+ }
213
+ });
214
+
215
+ // Run all tests
216
+ runner.run().catch(console.error);