@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,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract base class for all implementation backends
|
|
3
|
+
* @module BaseBackend
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BackendError, ErrorTypes } from '../core/utils.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Base class that all implementation backends must extend
|
|
10
|
+
* @class
|
|
11
|
+
*/
|
|
12
|
+
class BaseBackend {
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} name - Backend name
|
|
15
|
+
* @param {string} version - Backend version
|
|
16
|
+
*/
|
|
17
|
+
constructor(name, version) {
|
|
18
|
+
if (new.target === BaseBackend) {
|
|
19
|
+
throw new Error('BaseBackend is an abstract class and cannot be instantiated directly');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this.name = name;
|
|
23
|
+
this.version = version;
|
|
24
|
+
this.initialized = false;
|
|
25
|
+
this.activeSessions = new Map();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Initialize the backend with configuration
|
|
30
|
+
* @param {import('../types/BackendTypes').BackendConfig} config - Backend-specific configuration
|
|
31
|
+
* @returns {Promise<void>}
|
|
32
|
+
* @abstract
|
|
33
|
+
*/
|
|
34
|
+
async initialize(config) {
|
|
35
|
+
throw new Error('initialize() must be implemented by subclass');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Check if backend is available and properly configured
|
|
40
|
+
* @returns {Promise<boolean>}
|
|
41
|
+
* @abstract
|
|
42
|
+
*/
|
|
43
|
+
async isAvailable() {
|
|
44
|
+
throw new Error('isAvailable() must be implemented by subclass');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get required dependencies for this backend
|
|
49
|
+
* @returns {import('../types/BackendTypes').Dependency[]}
|
|
50
|
+
* @abstract
|
|
51
|
+
*/
|
|
52
|
+
getRequiredDependencies() {
|
|
53
|
+
throw new Error('getRequiredDependencies() must be implemented by subclass');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Execute implementation task
|
|
58
|
+
* @param {import('../types/BackendTypes').ImplementRequest} request - Implementation request
|
|
59
|
+
* @returns {Promise<import('../types/BackendTypes').ImplementResult>}
|
|
60
|
+
* @abstract
|
|
61
|
+
*/
|
|
62
|
+
async execute(request) {
|
|
63
|
+
throw new Error('execute() must be implemented by subclass');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Cancel an active implementation session
|
|
68
|
+
* @param {string} sessionId - Session to cancel
|
|
69
|
+
* @returns {Promise<void>}
|
|
70
|
+
*/
|
|
71
|
+
async cancel(sessionId) {
|
|
72
|
+
const session = this.activeSessions.get(sessionId);
|
|
73
|
+
if (session && session.cancel) {
|
|
74
|
+
await session.cancel();
|
|
75
|
+
}
|
|
76
|
+
this.activeSessions.delete(sessionId);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get status of an implementation session
|
|
81
|
+
* @param {string} sessionId - Session ID
|
|
82
|
+
* @returns {Promise<import('../types/BackendTypes').BackendStatus>}
|
|
83
|
+
*/
|
|
84
|
+
async getStatus(sessionId) {
|
|
85
|
+
const session = this.activeSessions.get(sessionId);
|
|
86
|
+
if (!session) {
|
|
87
|
+
return {
|
|
88
|
+
status: 'unknown',
|
|
89
|
+
message: 'Session not found'
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
status: session.status || 'running',
|
|
95
|
+
progress: session.progress,
|
|
96
|
+
message: session.message,
|
|
97
|
+
details: session.details
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Clean up backend resources
|
|
103
|
+
* @returns {Promise<void>}
|
|
104
|
+
*/
|
|
105
|
+
async cleanup() {
|
|
106
|
+
// Cancel all active sessions
|
|
107
|
+
const sessionIds = Array.from(this.activeSessions.keys());
|
|
108
|
+
await Promise.all(sessionIds.map(id => this.cancel(id)));
|
|
109
|
+
|
|
110
|
+
this.activeSessions.clear();
|
|
111
|
+
this.initialized = false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get backend capabilities
|
|
116
|
+
* @returns {import('../types/BackendTypes').BackendCapabilities}
|
|
117
|
+
*/
|
|
118
|
+
getCapabilities() {
|
|
119
|
+
return {
|
|
120
|
+
supportsLanguages: [],
|
|
121
|
+
supportsStreaming: false,
|
|
122
|
+
supportsRollback: false,
|
|
123
|
+
supportsDirectFileEdit: false,
|
|
124
|
+
supportsPlanGeneration: false,
|
|
125
|
+
supportsTestGeneration: false,
|
|
126
|
+
maxConcurrentSessions: 1
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get backend information
|
|
132
|
+
* @returns {import('../types/BackendTypes').BackendInfo}
|
|
133
|
+
*/
|
|
134
|
+
getInfo() {
|
|
135
|
+
return {
|
|
136
|
+
name: this.name,
|
|
137
|
+
version: this.version,
|
|
138
|
+
description: this.getDescription(),
|
|
139
|
+
available: false,
|
|
140
|
+
capabilities: this.getCapabilities(),
|
|
141
|
+
dependencies: this.getRequiredDependencies()
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get backend description
|
|
147
|
+
* @returns {string}
|
|
148
|
+
*/
|
|
149
|
+
getDescription() {
|
|
150
|
+
return 'Implementation backend';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Validate implementation request
|
|
155
|
+
* @param {import('../types/BackendTypes').ImplementRequest} request - Request to validate
|
|
156
|
+
* @returns {import('../types/BackendTypes').ValidationResult}
|
|
157
|
+
*/
|
|
158
|
+
validateRequest(request) {
|
|
159
|
+
const errors = [];
|
|
160
|
+
const warnings = [];
|
|
161
|
+
|
|
162
|
+
// Required fields
|
|
163
|
+
if (!request.sessionId) {
|
|
164
|
+
errors.push('sessionId is required');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!request.task || request.task.trim().length === 0) {
|
|
168
|
+
errors.push('task description is required');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Check for active sessions limit
|
|
172
|
+
if (this.activeSessions.size >= this.getCapabilities().maxConcurrentSessions) {
|
|
173
|
+
errors.push(`Maximum concurrent sessions (${this.getCapabilities().maxConcurrentSessions}) reached`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Language support check
|
|
177
|
+
if (request.context?.language) {
|
|
178
|
+
const supportedLanguages = this.getCapabilities().supportsLanguages;
|
|
179
|
+
if (supportedLanguages.length > 0 && !supportedLanguages.includes(request.context.language)) {
|
|
180
|
+
warnings.push(`Language '${request.context.language}' may not be fully supported`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Option validation
|
|
185
|
+
if (request.options?.generateTests && !this.getCapabilities().supportsTestGeneration) {
|
|
186
|
+
warnings.push('Test generation requested but not supported by this backend');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
valid: errors.length === 0,
|
|
191
|
+
errors,
|
|
192
|
+
warnings
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Create a session info object
|
|
198
|
+
* @param {string} sessionId - Session ID
|
|
199
|
+
* @returns {Object}
|
|
200
|
+
* @protected
|
|
201
|
+
*/
|
|
202
|
+
createSessionInfo(sessionId) {
|
|
203
|
+
return {
|
|
204
|
+
sessionId,
|
|
205
|
+
startTime: Date.now(),
|
|
206
|
+
status: 'pending',
|
|
207
|
+
progress: 0,
|
|
208
|
+
message: 'Initializing',
|
|
209
|
+
cancel: null,
|
|
210
|
+
details: {}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Update session status
|
|
216
|
+
* @param {string} sessionId - Session ID
|
|
217
|
+
* @param {Partial<import('../types/BackendTypes').BackendStatus>} update - Status update
|
|
218
|
+
* @protected
|
|
219
|
+
*/
|
|
220
|
+
updateSessionStatus(sessionId, update) {
|
|
221
|
+
const session = this.activeSessions.get(sessionId);
|
|
222
|
+
if (session) {
|
|
223
|
+
Object.assign(session, update);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Check if backend is initialized
|
|
229
|
+
* @throws {Error} If backend is not initialized
|
|
230
|
+
* @protected
|
|
231
|
+
*/
|
|
232
|
+
checkInitialized() {
|
|
233
|
+
if (!this.initialized) {
|
|
234
|
+
throw new BackendError(
|
|
235
|
+
`Backend '${this.name}' is not initialized`,
|
|
236
|
+
ErrorTypes.INITIALIZATION_FAILED,
|
|
237
|
+
'BACKEND_NOT_INITIALIZED'
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Log message with backend context
|
|
244
|
+
* @param {string} level - Log level
|
|
245
|
+
* @param {string} message - Log message
|
|
246
|
+
* @param {Object} [data] - Additional data
|
|
247
|
+
* @protected
|
|
248
|
+
*/
|
|
249
|
+
log(level, message, data = {}) {
|
|
250
|
+
const logMessage = `[${this.name}] ${message}`;
|
|
251
|
+
const logData = { backend: this.name, ...data };
|
|
252
|
+
|
|
253
|
+
switch (level) {
|
|
254
|
+
case 'debug':
|
|
255
|
+
// Only output debug logs if DEBUG environment variable is set
|
|
256
|
+
if (process.env.DEBUG) {
|
|
257
|
+
console.debug(logMessage, logData);
|
|
258
|
+
}
|
|
259
|
+
break;
|
|
260
|
+
case 'info':
|
|
261
|
+
// Send info logs to stderr to avoid mixing with stdout output
|
|
262
|
+
console.error(logMessage, logData);
|
|
263
|
+
break;
|
|
264
|
+
case 'warn':
|
|
265
|
+
console.warn(logMessage, logData);
|
|
266
|
+
break;
|
|
267
|
+
case 'error':
|
|
268
|
+
console.error(logMessage, logData);
|
|
269
|
+
break;
|
|
270
|
+
default:
|
|
271
|
+
console.error(logMessage, logData);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export default BaseBackend;
|