@cpretzinger/boss-claude 1.0.0 → 1.0.2
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 +304 -1
- package/bin/boss-claude.js +1138 -0
- package/bin/commands/mode.js +250 -0
- package/bin/onyx-guard.js +259 -0
- package/bin/onyx-guard.sh +251 -0
- package/bin/prompts.js +284 -0
- package/bin/rollback.js +85 -0
- package/bin/setup-wizard.js +492 -0
- package/config/.env.example +17 -0
- package/lib/README.md +83 -0
- package/lib/agent-logger.js +61 -0
- package/lib/agents/memory-engineers/github-memory-engineer.js +251 -0
- package/lib/agents/memory-engineers/postgres-memory-engineer.js +633 -0
- package/lib/agents/memory-engineers/qdrant-memory-engineer.js +358 -0
- package/lib/agents/memory-engineers/redis-memory-engineer.js +383 -0
- package/lib/agents/memory-supervisor.js +526 -0
- package/lib/agents/registry.js +135 -0
- package/lib/auto-monitor.js +131 -0
- package/lib/checkpoint-hook.js +112 -0
- package/lib/checkpoint.js +319 -0
- package/lib/commentator.js +213 -0
- package/lib/context-scribe.js +120 -0
- package/lib/delegation-strategies.js +326 -0
- package/lib/hierarchy-validator.js +643 -0
- package/lib/index.js +15 -0
- package/lib/init-with-mode.js +261 -0
- package/lib/init.js +44 -6
- package/lib/memory-result-aggregator.js +252 -0
- package/lib/memory.js +35 -7
- package/lib/mode-enforcer.js +473 -0
- package/lib/onyx-banner.js +169 -0
- package/lib/onyx-identity.js +214 -0
- package/lib/onyx-monitor.js +381 -0
- package/lib/onyx-reminder.js +188 -0
- package/lib/onyx-tool-interceptor.js +341 -0
- package/lib/onyx-wrapper.js +315 -0
- package/lib/orchestrator-gate.js +334 -0
- package/lib/output-formatter.js +296 -0
- package/lib/postgres.js +1 -1
- package/lib/prompt-injector.js +220 -0
- package/lib/prompts.js +532 -0
- package/lib/session.js +153 -6
- package/lib/setup/README.md +187 -0
- package/lib/setup/env-manager.js +785 -0
- package/lib/setup/error-recovery.js +630 -0
- package/lib/setup/explain-scopes.js +385 -0
- package/lib/setup/github-instructions.js +333 -0
- package/lib/setup/github-repo.js +254 -0
- package/lib/setup/import-credentials.js +498 -0
- package/lib/setup/index.js +62 -0
- package/lib/setup/init-postgres.js +785 -0
- package/lib/setup/init-redis.js +456 -0
- package/lib/setup/integration-test.js +652 -0
- package/lib/setup/progress.js +357 -0
- package/lib/setup/rollback.js +670 -0
- package/lib/setup/rollback.test.js +452 -0
- package/lib/setup/setup-with-rollback.example.js +351 -0
- package/lib/setup/summary.js +400 -0
- package/lib/setup/test-github-setup.js +10 -0
- package/lib/setup/test-postgres-init.js +98 -0
- package/lib/setup/verify-setup.js +102 -0
- package/lib/task-agent-worker.js +235 -0
- package/lib/token-monitor.js +466 -0
- package/lib/tool-wrapper-integration.js +369 -0
- package/lib/tool-wrapper.js +387 -0
- package/lib/validators/README.md +497 -0
- package/lib/validators/config.js +583 -0
- package/lib/validators/config.test.js +175 -0
- package/lib/validators/github.js +310 -0
- package/lib/validators/github.test.js +61 -0
- package/lib/validators/index.js +15 -0
- package/lib/validators/postgres.js +525 -0
- package/package.json +98 -13
- package/scripts/benchmark-memory.js +433 -0
- package/scripts/check-secrets.sh +12 -0
- package/scripts/fetch-todos.mjs +148 -0
- package/scripts/graceful-shutdown.sh +156 -0
- package/scripts/install-onyx-hooks.js +373 -0
- package/scripts/install.js +119 -18
- package/scripts/redis-monitor.js +284 -0
- package/scripts/redis-setup.js +412 -0
- package/scripts/test-memory-retrieval.js +201 -0
- package/scripts/validate-exports.js +68 -0
- package/scripts/validate-package.js +120 -0
- package/scripts/verify-onyx-deployment.js +309 -0
- package/scripts/verify-redis-deployment.js +354 -0
- package/scripts/verify-redis-init.js +219 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { QdrantClient } from '@qdrant/js-client-rest';
|
|
2
|
+
import OpenAI from 'openai';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* QDRANT MEMORY ENGINEER
|
|
6
|
+
*
|
|
7
|
+
* Semantic vector search component for Boss Claude memory system.
|
|
8
|
+
* Uses Qdrant for vector similarity search with OpenAI embeddings.
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Vector similarity search
|
|
12
|
+
* - OpenAI embeddings integration (text-embedding-3-small)
|
|
13
|
+
* - Graceful degradation if Qdrant not available
|
|
14
|
+
* - Collection management
|
|
15
|
+
* - Point upsert/delete operations
|
|
16
|
+
*
|
|
17
|
+
* Environment Variables:
|
|
18
|
+
* - QDRANT_URL (optional) - Qdrant server URL
|
|
19
|
+
* - QDRANT_API_KEY (optional) - Qdrant API key
|
|
20
|
+
* - OPENAI_API_KEY (required for embeddings)
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
class QdrantMemoryEngineer {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.enabled = false;
|
|
26
|
+
this.client = null;
|
|
27
|
+
this.openai = null;
|
|
28
|
+
this.collectionName = 'boss_claude_memories';
|
|
29
|
+
this.vectorDimension = 1536; // text-embedding-3-small dimension
|
|
30
|
+
|
|
31
|
+
this.initialize();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Initialize Qdrant client and OpenAI
|
|
36
|
+
*/
|
|
37
|
+
initialize() {
|
|
38
|
+
try {
|
|
39
|
+
const qdrantUrl = process.env.QDRANT_URL;
|
|
40
|
+
const qdrantApiKey = process.env.QDRANT_API_KEY;
|
|
41
|
+
const openaiApiKey = process.env.OPENAI_API_KEY;
|
|
42
|
+
|
|
43
|
+
if (!qdrantUrl) {
|
|
44
|
+
console.log('[Qdrant] QDRANT_URL not set - semantic search disabled (graceful degradation)');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!openaiApiKey) {
|
|
49
|
+
console.warn('[Qdrant] OPENAI_API_KEY not set - cannot generate embeddings');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Initialize Qdrant client
|
|
54
|
+
this.client = new QdrantClient({
|
|
55
|
+
url: qdrantUrl,
|
|
56
|
+
apiKey: qdrantApiKey,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Initialize OpenAI for embeddings
|
|
60
|
+
// FIX: Don't pass raw API key - OpenAI SDK handles it securely from env
|
|
61
|
+
this.openai = new OpenAI({
|
|
62
|
+
apiKey: openaiApiKey,
|
|
63
|
+
dangerouslyAllowBrowser: false // Ensure server-side only
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
this.enabled = true;
|
|
67
|
+
console.log('[Qdrant] Initialized successfully');
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.warn('[Qdrant] Failed to initialize:', error.message);
|
|
70
|
+
this.enabled = false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Check if Qdrant is available
|
|
76
|
+
*/
|
|
77
|
+
isAvailable() {
|
|
78
|
+
return this.enabled;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Ensure collection exists
|
|
83
|
+
*/
|
|
84
|
+
async ensureCollection() {
|
|
85
|
+
if (!this.enabled) return false;
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
// Check if collection exists
|
|
89
|
+
const collections = await this.client.getCollections();
|
|
90
|
+
const exists = collections.collections.some(
|
|
91
|
+
col => col.name === this.collectionName
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (!exists) {
|
|
95
|
+
// Create collection
|
|
96
|
+
await this.client.createCollection(this.collectionName, {
|
|
97
|
+
vectors: {
|
|
98
|
+
size: this.vectorDimension,
|
|
99
|
+
distance: 'Cosine',
|
|
100
|
+
},
|
|
101
|
+
optimizers_config: {
|
|
102
|
+
default_segment_number: 2,
|
|
103
|
+
},
|
|
104
|
+
replication_factor: 1,
|
|
105
|
+
});
|
|
106
|
+
console.log(`[Qdrant] Created collection: ${this.collectionName}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return true;
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error('[Qdrant] Failed to ensure collection:', error.message);
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Generate embedding for text using OpenAI
|
|
118
|
+
*/
|
|
119
|
+
async generateEmbedding(text) {
|
|
120
|
+
if (!this.enabled || !this.openai) {
|
|
121
|
+
throw new Error('OpenAI not available for embeddings');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const response = await this.openai.embeddings.create({
|
|
126
|
+
model: 'text-embedding-3-small',
|
|
127
|
+
input: text,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return response.data[0].embedding;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
// FIX: Sanitize error messages to prevent API key leakage
|
|
133
|
+
const sanitizedMessage = error.message?.replace(/sk-[a-zA-Z0-9_-]{20,}/g, 'sk-***REDACTED***') || 'Unknown error';
|
|
134
|
+
console.error('[Qdrant] Failed to generate embedding:', sanitizedMessage);
|
|
135
|
+
throw new Error(sanitizedMessage);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Store memory with vector embedding
|
|
141
|
+
*
|
|
142
|
+
* @param {string} id - Unique memory ID
|
|
143
|
+
* @param {string} text - Memory text content
|
|
144
|
+
* @param {object} metadata - Additional metadata
|
|
145
|
+
*/
|
|
146
|
+
async storeMemory(id, text, metadata = {}) {
|
|
147
|
+
if (!this.enabled) {
|
|
148
|
+
console.log('[Qdrant] Not available - skipping vector storage');
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// FIX: Validate ID to prevent collisions and ensure type safety
|
|
153
|
+
if (!id || typeof id !== 'string' && typeof id !== 'number') {
|
|
154
|
+
console.error('[Qdrant] Invalid ID provided:', typeof id);
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Ensure numeric IDs or UUID format strings only
|
|
159
|
+
const validId = typeof id === 'number' ? id : String(id);
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
await this.ensureCollection();
|
|
163
|
+
|
|
164
|
+
// Generate embedding
|
|
165
|
+
const vector = await this.generateEmbedding(text);
|
|
166
|
+
|
|
167
|
+
// Upsert point
|
|
168
|
+
await this.client.upsert(this.collectionName, {
|
|
169
|
+
wait: true,
|
|
170
|
+
points: [
|
|
171
|
+
{
|
|
172
|
+
id: validId,
|
|
173
|
+
vector: vector,
|
|
174
|
+
payload: {
|
|
175
|
+
text,
|
|
176
|
+
...metadata,
|
|
177
|
+
stored_at: new Date().toISOString(),
|
|
178
|
+
original_id: id // Preserve original ID in payload
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
console.log(`[Qdrant] Stored memory: ${validId}`);
|
|
185
|
+
return true;
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error('[Qdrant] Failed to store memory:', error.message);
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Search for similar memories
|
|
194
|
+
*
|
|
195
|
+
* @param {string} query - Search query text
|
|
196
|
+
* @param {number} limit - Maximum results to return
|
|
197
|
+
* @param {number} scoreThreshold - Minimum similarity score (0-1)
|
|
198
|
+
*/
|
|
199
|
+
async searchSimilar(query, limit = 5, scoreThreshold = 0.7) {
|
|
200
|
+
if (!this.enabled) {
|
|
201
|
+
console.log('[Qdrant] Not available - returning empty results');
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
await this.ensureCollection();
|
|
207
|
+
|
|
208
|
+
// Generate query embedding
|
|
209
|
+
const queryVector = await this.generateEmbedding(query);
|
|
210
|
+
|
|
211
|
+
// Search
|
|
212
|
+
const results = await this.client.search(this.collectionName, {
|
|
213
|
+
vector: queryVector,
|
|
214
|
+
limit: limit,
|
|
215
|
+
score_threshold: scoreThreshold,
|
|
216
|
+
with_payload: true,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
return results.map(result => ({
|
|
220
|
+
id: result.id,
|
|
221
|
+
score: result.score,
|
|
222
|
+
text: result.payload.text,
|
|
223
|
+
metadata: result.payload,
|
|
224
|
+
}));
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error('[Qdrant] Search failed:', error.message);
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Delete memory by ID
|
|
233
|
+
*
|
|
234
|
+
* @param {string} id - Memory ID to delete
|
|
235
|
+
*/
|
|
236
|
+
async deleteMemory(id) {
|
|
237
|
+
if (!this.enabled) {
|
|
238
|
+
console.log('[Qdrant] Not available - skipping deletion');
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
await this.client.delete(this.collectionName, {
|
|
244
|
+
wait: true,
|
|
245
|
+
points: [id],
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
console.log(`[Qdrant] Deleted memory: ${id}`);
|
|
249
|
+
return true;
|
|
250
|
+
} catch (error) {
|
|
251
|
+
console.error('[Qdrant] Failed to delete memory:', error.message);
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Delete multiple memories by IDs
|
|
258
|
+
*
|
|
259
|
+
* @param {string[]} ids - Array of memory IDs to delete
|
|
260
|
+
*/
|
|
261
|
+
async deleteMemories(ids) {
|
|
262
|
+
if (!this.enabled) {
|
|
263
|
+
console.log('[Qdrant] Not available - skipping deletion');
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
await this.client.delete(this.collectionName, {
|
|
269
|
+
wait: true,
|
|
270
|
+
points: ids,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
console.log(`[Qdrant] Deleted ${ids.length} memories`);
|
|
274
|
+
return true;
|
|
275
|
+
} catch (error) {
|
|
276
|
+
console.error('[Qdrant] Failed to delete memories:', error.message);
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Get collection info and stats
|
|
283
|
+
*/
|
|
284
|
+
async getStats() {
|
|
285
|
+
if (!this.enabled) {
|
|
286
|
+
return {
|
|
287
|
+
available: false,
|
|
288
|
+
message: 'Qdrant not configured',
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
const info = await this.client.getCollection(this.collectionName);
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
available: true,
|
|
297
|
+
collection: this.collectionName,
|
|
298
|
+
points_count: info.points_count,
|
|
299
|
+
vectors_count: info.vectors_count,
|
|
300
|
+
indexed_vectors_count: info.indexed_vectors_count,
|
|
301
|
+
status: info.status,
|
|
302
|
+
};
|
|
303
|
+
} catch (error) {
|
|
304
|
+
return {
|
|
305
|
+
available: false,
|
|
306
|
+
error: error.message,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Clear all memories (use with caution)
|
|
313
|
+
*/
|
|
314
|
+
async clearAll() {
|
|
315
|
+
if (!this.enabled) {
|
|
316
|
+
console.log('[Qdrant] Not available - nothing to clear');
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
try {
|
|
321
|
+
await this.client.deleteCollection(this.collectionName);
|
|
322
|
+
console.log(`[Qdrant] Cleared collection: ${this.collectionName}`);
|
|
323
|
+
await this.ensureCollection();
|
|
324
|
+
return true;
|
|
325
|
+
} catch (error) {
|
|
326
|
+
console.error('[Qdrant] Failed to clear collection:', error.message);
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Health check
|
|
333
|
+
*/
|
|
334
|
+
async healthCheck() {
|
|
335
|
+
if (!this.enabled) {
|
|
336
|
+
return { healthy: false, reason: 'Not configured' };
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
await this.client.getCollections();
|
|
341
|
+
return { healthy: true };
|
|
342
|
+
} catch (error) {
|
|
343
|
+
return { healthy: false, reason: error.message };
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Singleton instance
|
|
349
|
+
let instance = null;
|
|
350
|
+
|
|
351
|
+
export function getQdrantMemoryEngineer() {
|
|
352
|
+
if (!instance) {
|
|
353
|
+
instance = new QdrantMemoryEngineer();
|
|
354
|
+
}
|
|
355
|
+
return instance;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export default QdrantMemoryEngineer;
|