@probelabs/probe-chat 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.
- package/README.md +338 -0
- package/TRACING.md +226 -0
- package/appTracer.js +947 -0
- package/auth.js +76 -0
- package/bin/probe-chat.js +13 -0
- package/cancelRequest.js +84 -0
- package/fileSpanExporter.js +183 -0
- package/implement/README.md +228 -0
- package/implement/backends/AiderBackend.js +750 -0
- package/implement/backends/BaseBackend.js +276 -0
- package/implement/backends/ClaudeCodeBackend.js +767 -0
- package/implement/backends/MockBackend.js +237 -0
- package/implement/backends/registry.js +85 -0
- package/implement/core/BackendManager.js +567 -0
- package/implement/core/ImplementTool.js +354 -0
- package/implement/core/config.js +428 -0
- package/implement/core/timeouts.js +58 -0
- package/implement/core/utils.js +496 -0
- package/implement/types/BackendTypes.js +126 -0
- package/index.html +3751 -0
- package/index.js +582 -0
- package/logo.png +0 -0
- package/package.json +101 -0
- package/probeChat.js +269 -0
- package/probeTool.js +714 -0
- package/storage/JsonChatStorage.js +476 -0
- package/telemetry.js +287 -0
- package/test/integration/chatFlows.test.js +320 -0
- package/test/integration/toolCalling.test.js +471 -0
- package/test/mocks/mockLLMProvider.js +269 -0
- package/test/test-backends.js +90 -0
- package/test/testUtils.js +530 -0
- package/test/unit/backendTimeout.test.js +161 -0
- package/test/verify-tests.js +118 -0
- package/tokenCounter.js +419 -0
- package/tokenUsageDisplay.js +134 -0
- package/tools.js +186 -0
- package/webServer.js +1103 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implementation tool wrapper that integrates with the backend system
|
|
3
|
+
* @module ImplementTool
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import BackendManager from './BackendManager.js';
|
|
7
|
+
import { createBackend, listBackendNames } from '../backends/registry.js';
|
|
8
|
+
import { BackendError, ErrorTypes } from './utils.js';
|
|
9
|
+
import { configManager } from './config.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Implementation tool that uses pluggable backends
|
|
13
|
+
* @class
|
|
14
|
+
*/
|
|
15
|
+
class ImplementTool {
|
|
16
|
+
/**
|
|
17
|
+
* @param {Object} config - Tool configuration
|
|
18
|
+
* @param {boolean} [config.enabled=false] - Whether the tool is enabled
|
|
19
|
+
* @param {Object} [config.backendConfig] - Backend manager configuration
|
|
20
|
+
*/
|
|
21
|
+
constructor(config = {}) {
|
|
22
|
+
this.enabled = config.enabled || false;
|
|
23
|
+
this.backendManager = null;
|
|
24
|
+
this.config = config;
|
|
25
|
+
this.initialized = false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Initialize the implementation tool
|
|
30
|
+
* @returns {Promise<void>}
|
|
31
|
+
*/
|
|
32
|
+
async initialize() {
|
|
33
|
+
if (this.initialized) return;
|
|
34
|
+
|
|
35
|
+
if (!this.enabled) {
|
|
36
|
+
throw new Error('Implementation tool is not enabled. Use --allow-edit flag to enable.');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Initialize configuration manager
|
|
40
|
+
await configManager.initialize(this.config.configPath);
|
|
41
|
+
|
|
42
|
+
// Get configuration from manager
|
|
43
|
+
const implementConfig = configManager.getImplementConfig();
|
|
44
|
+
const backendConfigs = configManager.get('backends') || {};
|
|
45
|
+
|
|
46
|
+
// Create backend manager with configuration
|
|
47
|
+
const backendManagerConfig = {
|
|
48
|
+
...implementConfig,
|
|
49
|
+
backends: backendConfigs,
|
|
50
|
+
...this.config.backendConfig
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
this.backendManager = new BackendManager(backendManagerConfig);
|
|
54
|
+
|
|
55
|
+
// Register available backends
|
|
56
|
+
await this.registerBackends();
|
|
57
|
+
|
|
58
|
+
// Initialize backend manager
|
|
59
|
+
await this.backendManager.initialize();
|
|
60
|
+
|
|
61
|
+
// Validate configuration
|
|
62
|
+
const configValidation = configManager.validate();
|
|
63
|
+
if (!configValidation.valid) {
|
|
64
|
+
console.error('Configuration errors:', configValidation.errors.join(', '));
|
|
65
|
+
if (configValidation.warnings.length > 0) {
|
|
66
|
+
console.warn('Configuration warnings:', configValidation.warnings.join(', '));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const backendValidation = await this.backendManager.validateConfiguration();
|
|
71
|
+
if (!backendValidation.valid) {
|
|
72
|
+
console.warn('Backend configuration warnings:', backendValidation.errors.join(', '));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Listen for configuration changes
|
|
76
|
+
configManager.onChange(async (newConfig) => {
|
|
77
|
+
console.error('Configuration changed, reinitializing backends...');
|
|
78
|
+
await this.reinitialize(newConfig);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
this.initialized = true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Register all available backends
|
|
86
|
+
* @private
|
|
87
|
+
*/
|
|
88
|
+
async registerBackends() {
|
|
89
|
+
const backendNames = listBackendNames();
|
|
90
|
+
|
|
91
|
+
for (const name of backendNames) {
|
|
92
|
+
try {
|
|
93
|
+
const backend = createBackend(name);
|
|
94
|
+
if (backend) {
|
|
95
|
+
await this.backendManager.registerBackend(backend);
|
|
96
|
+
console.error(`Registered backend: ${name}`);
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.warn(`Failed to register backend '${name}':`, error.message);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Reinitialize with new configuration
|
|
106
|
+
* @param {Object} newConfig - New configuration
|
|
107
|
+
* @private
|
|
108
|
+
*/
|
|
109
|
+
async reinitialize(newConfig) {
|
|
110
|
+
try {
|
|
111
|
+
// Clean up existing backend manager
|
|
112
|
+
if (this.backendManager) {
|
|
113
|
+
await this.backendManager.cleanup();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Create new backend manager with updated configuration
|
|
117
|
+
const implementConfig = newConfig.implement || {};
|
|
118
|
+
const backendConfigs = newConfig.backends || {};
|
|
119
|
+
|
|
120
|
+
const backendManagerConfig = {
|
|
121
|
+
...implementConfig,
|
|
122
|
+
backends: backendConfigs,
|
|
123
|
+
...this.config.backendConfig
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
this.backendManager = new BackendManager(backendManagerConfig);
|
|
127
|
+
|
|
128
|
+
// Re-register backends
|
|
129
|
+
await this.registerBackends();
|
|
130
|
+
|
|
131
|
+
// Re-initialize
|
|
132
|
+
await this.backendManager.initialize();
|
|
133
|
+
|
|
134
|
+
console.error('Backend reinitialization completed');
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error('Failed to reinitialize backends:', error);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get tool definition for AI models
|
|
142
|
+
* @returns {Object}
|
|
143
|
+
*/
|
|
144
|
+
getToolDefinition() {
|
|
145
|
+
return {
|
|
146
|
+
name: 'implement',
|
|
147
|
+
description: 'Implement a feature or fix a bug using AI-powered code generation. Only available when --allow-edit is enabled.',
|
|
148
|
+
inputSchema: {
|
|
149
|
+
type: 'object',
|
|
150
|
+
properties: {
|
|
151
|
+
task: {
|
|
152
|
+
type: 'string',
|
|
153
|
+
description: 'The task description for implementation'
|
|
154
|
+
},
|
|
155
|
+
backend: {
|
|
156
|
+
type: 'string',
|
|
157
|
+
description: 'Optional: Specific backend to use (aider, claude-code)',
|
|
158
|
+
enum: listBackendNames()
|
|
159
|
+
},
|
|
160
|
+
autoCommit: {
|
|
161
|
+
type: 'boolean',
|
|
162
|
+
description: 'Whether to auto-commit changes (default: false)'
|
|
163
|
+
},
|
|
164
|
+
generateTests: {
|
|
165
|
+
type: 'boolean',
|
|
166
|
+
description: 'Whether to generate tests for the implementation'
|
|
167
|
+
},
|
|
168
|
+
dryRun: {
|
|
169
|
+
type: 'boolean',
|
|
170
|
+
description: 'Perform a dry run without making actual changes'
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
required: ['task']
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Execute implementation task
|
|
180
|
+
* @param {Object} params - Execution parameters
|
|
181
|
+
* @param {string} params.task - Task description
|
|
182
|
+
* @param {string} [params.backend] - Specific backend to use
|
|
183
|
+
* @param {boolean} [params.autoCommit] - Auto-commit changes
|
|
184
|
+
* @param {boolean} [params.generateTests] - Generate tests
|
|
185
|
+
* @param {boolean} [params.dryRun] - Dry run mode
|
|
186
|
+
* @param {string} [params.sessionId] - Session ID
|
|
187
|
+
* @returns {Promise<Object>}
|
|
188
|
+
*/
|
|
189
|
+
async execute(params) {
|
|
190
|
+
if (!this.enabled) {
|
|
191
|
+
throw new Error('Implementation tool is not enabled. Use --allow-edit flag to enable.');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Ensure initialized
|
|
195
|
+
if (!this.initialized) {
|
|
196
|
+
await this.initialize();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const { task, backend, autoCommit, generateTests, dryRun, sessionId, ...rest } = params;
|
|
200
|
+
|
|
201
|
+
// Build implementation request
|
|
202
|
+
const request = {
|
|
203
|
+
sessionId: sessionId || `implement-${Date.now()}`,
|
|
204
|
+
task,
|
|
205
|
+
context: {
|
|
206
|
+
workingDirectory: process.cwd(),
|
|
207
|
+
...rest.context
|
|
208
|
+
},
|
|
209
|
+
options: {
|
|
210
|
+
backend,
|
|
211
|
+
autoCommit: autoCommit || false,
|
|
212
|
+
generateTests: generateTests || false,
|
|
213
|
+
dryRun: dryRun || false,
|
|
214
|
+
...rest.options
|
|
215
|
+
},
|
|
216
|
+
callbacks: {
|
|
217
|
+
onProgress: (update) => {
|
|
218
|
+
// Log progress to stderr for visibility
|
|
219
|
+
if (update.message) {
|
|
220
|
+
const prefix = update.type === 'stderr' ? '[STDERR]' : '[INFO]';
|
|
221
|
+
console.error(`${prefix} ${update.message}`);
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
onError: (error) => {
|
|
225
|
+
console.error('[ERROR]', error.message);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
console.error(`Executing implementation task: ${task.substring(0, 100)}${task.length > 100 ? '...' : ''}`);
|
|
232
|
+
console.error(`Using backend selection strategy: ${this.backendManager.config.selectionStrategy}`);
|
|
233
|
+
|
|
234
|
+
if (backend) {
|
|
235
|
+
console.error(`Requested backend: ${backend}`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Execute implementation
|
|
239
|
+
const result = await this.backendManager.executeImplementation(request);
|
|
240
|
+
|
|
241
|
+
console.error(`Implementation completed using backend: ${result.backend}`);
|
|
242
|
+
|
|
243
|
+
if (result.fallback) {
|
|
244
|
+
console.error('Note: Used fallback backend due to primary backend failure');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Format result for compatibility with existing code
|
|
248
|
+
return {
|
|
249
|
+
success: result.success,
|
|
250
|
+
output: result.output,
|
|
251
|
+
error: result.error?.message || null,
|
|
252
|
+
command: `[${result.backend}] ${task}`,
|
|
253
|
+
timestamp: new Date().toISOString(),
|
|
254
|
+
prompt: task,
|
|
255
|
+
backend: result.backend,
|
|
256
|
+
metrics: result.metrics,
|
|
257
|
+
changes: result.changes
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
} catch (error) {
|
|
261
|
+
console.error(`Implementation failed:`, error.message);
|
|
262
|
+
|
|
263
|
+
// Format error response
|
|
264
|
+
return {
|
|
265
|
+
success: false,
|
|
266
|
+
output: null,
|
|
267
|
+
error: error.message,
|
|
268
|
+
command: `[failed] ${task}`,
|
|
269
|
+
timestamp: new Date().toISOString(),
|
|
270
|
+
prompt: task,
|
|
271
|
+
backend: null,
|
|
272
|
+
errorDetails: error instanceof BackendError ? error.toJSON() : { message: error.message }
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Cancel an implementation session
|
|
279
|
+
* @param {string} sessionId - Session ID to cancel
|
|
280
|
+
* @returns {Promise<void>}
|
|
281
|
+
*/
|
|
282
|
+
async cancel(sessionId) {
|
|
283
|
+
if (!this.backendManager) {
|
|
284
|
+
throw new Error('Implementation tool not initialized');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
await this.backendManager.cancelImplementation(sessionId);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Get backend information
|
|
292
|
+
* @returns {Promise<Object>}
|
|
293
|
+
*/
|
|
294
|
+
async getBackendInfo() {
|
|
295
|
+
if (!this.initialized) {
|
|
296
|
+
await this.initialize();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const health = await this.backendManager.checkBackendHealth();
|
|
300
|
+
const availableBackends = this.backendManager.getAvailableBackends();
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
enabled: this.enabled,
|
|
304
|
+
defaultBackend: this.backendManager.config.defaultBackend,
|
|
305
|
+
fallbackBackends: this.backendManager.config.fallbackBackends,
|
|
306
|
+
availableBackends,
|
|
307
|
+
health
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Clean up resources
|
|
313
|
+
* @returns {Promise<void>}
|
|
314
|
+
*/
|
|
315
|
+
async cleanup() {
|
|
316
|
+
if (this.backendManager) {
|
|
317
|
+
await this.backendManager.cleanup();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Clean up configuration manager
|
|
321
|
+
configManager.cleanup();
|
|
322
|
+
|
|
323
|
+
this.initialized = false;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Create a singleton instance of the implementation tool
|
|
329
|
+
* This maintains compatibility with the existing code structure
|
|
330
|
+
*/
|
|
331
|
+
function createImplementTool(config = {}) {
|
|
332
|
+
const tool = new ImplementTool(config);
|
|
333
|
+
|
|
334
|
+
// Return a tool object compatible with the existing interface
|
|
335
|
+
return {
|
|
336
|
+
...tool.getToolDefinition(),
|
|
337
|
+
execute: async (params) => {
|
|
338
|
+
return await tool.execute(params);
|
|
339
|
+
},
|
|
340
|
+
cancel: async (sessionId) => {
|
|
341
|
+
return await tool.cancel(sessionId);
|
|
342
|
+
},
|
|
343
|
+
getInfo: async () => {
|
|
344
|
+
return await tool.getBackendInfo();
|
|
345
|
+
},
|
|
346
|
+
cleanup: async () => {
|
|
347
|
+
return await tool.cleanup();
|
|
348
|
+
},
|
|
349
|
+
// Expose the tool instance for advanced usage
|
|
350
|
+
instance: tool
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export { ImplementTool, createImplementTool };
|