kiro-memory 1.8.1 → 2.1.0
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/package.json +6 -4
- package/plugin/dist/cli/contextkit.js +428 -205
- package/plugin/dist/hooks/agentSpawn.js +311 -180
- package/plugin/dist/hooks/kiro-hooks.js +299 -168
- package/plugin/dist/hooks/postToolUse.js +303 -172
- package/plugin/dist/hooks/stop.js +308 -177
- package/plugin/dist/hooks/userPromptSubmit.js +303 -172
- package/plugin/dist/index.js +303 -299
- package/plugin/dist/sdk/index.js +299 -172
- package/plugin/dist/services/search/EmbeddingService.js +88 -23
- package/plugin/dist/services/search/HybridSearch.js +190 -84
- package/plugin/dist/services/search/VectorSearch.js +128 -45
- package/plugin/dist/services/search/index.js +192 -223
- package/plugin/dist/services/sqlite/Database.js +55 -153
- package/plugin/dist/services/sqlite/Observations.js +23 -12
- package/plugin/dist/services/sqlite/Search.js +31 -19
- package/plugin/dist/services/sqlite/Sessions.js +5 -0
- package/plugin/dist/services/sqlite/index.js +113 -183
- package/plugin/dist/viewer.css +1 -0
- package/plugin/dist/viewer.html +2 -100
- package/plugin/dist/viewer.js +15 -24896
- package/plugin/dist/viewer.js.map +7 -0
- package/plugin/dist/worker-service.js +158 -5551
- package/plugin/dist/worker-service.js.map +7 -0
- package/scripts/postinstall.cjs +42 -0
|
@@ -226,8 +226,8 @@ var EmbeddingService = class {
|
|
|
226
226
|
initialized = false;
|
|
227
227
|
initializing = null;
|
|
228
228
|
/**
|
|
229
|
-
*
|
|
230
|
-
*
|
|
229
|
+
* Initialize the embedding service.
|
|
230
|
+
* Tries fastembed, then @huggingface/transformers, then fallback to null.
|
|
231
231
|
*/
|
|
232
232
|
async initialize() {
|
|
233
233
|
if (this.initialized) return this.provider !== null;
|
|
@@ -248,11 +248,11 @@ var EmbeddingService = class {
|
|
|
248
248
|
});
|
|
249
249
|
this.provider = "fastembed";
|
|
250
250
|
this.initialized = true;
|
|
251
|
-
logger.info("EMBEDDING", "
|
|
251
|
+
logger.info("EMBEDDING", "Initialized with fastembed (BGE-small-en-v1.5)");
|
|
252
252
|
return true;
|
|
253
253
|
}
|
|
254
254
|
} catch (error) {
|
|
255
|
-
logger.debug("EMBEDDING", `fastembed
|
|
255
|
+
logger.debug("EMBEDDING", `fastembed not available: ${error}`);
|
|
256
256
|
}
|
|
257
257
|
try {
|
|
258
258
|
const transformers = await import("@huggingface/transformers");
|
|
@@ -263,20 +263,20 @@ var EmbeddingService = class {
|
|
|
263
263
|
});
|
|
264
264
|
this.provider = "transformers";
|
|
265
265
|
this.initialized = true;
|
|
266
|
-
logger.info("EMBEDDING", "
|
|
266
|
+
logger.info("EMBEDDING", "Initialized with @huggingface/transformers (all-MiniLM-L6-v2)");
|
|
267
267
|
return true;
|
|
268
268
|
}
|
|
269
269
|
} catch (error) {
|
|
270
|
-
logger.debug("EMBEDDING", `@huggingface/transformers
|
|
270
|
+
logger.debug("EMBEDDING", `@huggingface/transformers not available: ${error}`);
|
|
271
271
|
}
|
|
272
272
|
this.provider = null;
|
|
273
273
|
this.initialized = true;
|
|
274
|
-
logger.warn("EMBEDDING", "
|
|
274
|
+
logger.warn("EMBEDDING", "No embedding provider available, semantic search disabled");
|
|
275
275
|
return false;
|
|
276
276
|
}
|
|
277
277
|
/**
|
|
278
|
-
*
|
|
279
|
-
*
|
|
278
|
+
* Generate embedding for a single text.
|
|
279
|
+
* Returns Float32Array with 384 dimensions, or null if not available.
|
|
280
280
|
*/
|
|
281
281
|
async embed(text) {
|
|
282
282
|
if (!this.initialized) await this.initialize();
|
|
@@ -289,46 +289,111 @@ var EmbeddingService = class {
|
|
|
289
289
|
return await this._embedTransformers(truncated);
|
|
290
290
|
}
|
|
291
291
|
} catch (error) {
|
|
292
|
-
logger.error("EMBEDDING", `
|
|
292
|
+
logger.error("EMBEDDING", `Error generating embedding: ${error}`);
|
|
293
293
|
}
|
|
294
294
|
return null;
|
|
295
295
|
}
|
|
296
296
|
/**
|
|
297
|
-
*
|
|
297
|
+
* Generate embeddings in batch.
|
|
298
|
+
* Uses native batch support when available (fastembed, transformers),
|
|
299
|
+
* falls back to serial processing on batch failure.
|
|
298
300
|
*/
|
|
299
301
|
async embedBatch(texts) {
|
|
300
302
|
if (!this.initialized) await this.initialize();
|
|
301
303
|
if (!this.provider || !this.model) return texts.map(() => null);
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
|
|
304
|
+
if (texts.length === 0) return [];
|
|
305
|
+
const truncated = texts.map((t) => t.substring(0, 2e3));
|
|
306
|
+
try {
|
|
307
|
+
if (this.provider === "fastembed") {
|
|
308
|
+
return await this._embedBatchFastembed(truncated);
|
|
309
|
+
} else if (this.provider === "transformers") {
|
|
310
|
+
return await this._embedBatchTransformers(truncated);
|
|
309
311
|
}
|
|
312
|
+
} catch (error) {
|
|
313
|
+
logger.warn("EMBEDDING", `Batch embedding failed, falling back to serial: ${error}`);
|
|
310
314
|
}
|
|
311
|
-
return
|
|
315
|
+
return this._embedBatchSerial(truncated);
|
|
312
316
|
}
|
|
313
317
|
/**
|
|
314
|
-
*
|
|
318
|
+
* Check if the service is available.
|
|
315
319
|
*/
|
|
316
320
|
isAvailable() {
|
|
317
321
|
return this.initialized && this.provider !== null;
|
|
318
322
|
}
|
|
319
323
|
/**
|
|
320
|
-
*
|
|
324
|
+
* Name of the active provider.
|
|
321
325
|
*/
|
|
322
326
|
getProvider() {
|
|
323
327
|
return this.provider;
|
|
324
328
|
}
|
|
325
329
|
/**
|
|
326
|
-
*
|
|
330
|
+
* Embedding vector dimensions.
|
|
327
331
|
*/
|
|
328
332
|
getDimensions() {
|
|
329
333
|
return 384;
|
|
330
334
|
}
|
|
331
|
-
// ---
|
|
335
|
+
// --- Batch implementations ---
|
|
336
|
+
/**
|
|
337
|
+
* Native batch embedding with fastembed.
|
|
338
|
+
* FlagEmbedding.embed() accepts string[] and returns an async iterable of batches.
|
|
339
|
+
*/
|
|
340
|
+
async _embedBatchFastembed(texts) {
|
|
341
|
+
const results = [];
|
|
342
|
+
const embeddings = this.model.embed(texts, texts.length);
|
|
343
|
+
for await (const batch of embeddings) {
|
|
344
|
+
if (batch) {
|
|
345
|
+
for (const vec of batch) {
|
|
346
|
+
results.push(vec instanceof Float32Array ? vec : new Float32Array(vec));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
while (results.length < texts.length) {
|
|
351
|
+
results.push(null);
|
|
352
|
+
}
|
|
353
|
+
return results;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Batch embedding with @huggingface/transformers pipeline.
|
|
357
|
+
* The pipeline accepts string[] and returns a Tensor with shape [N, dims].
|
|
358
|
+
*/
|
|
359
|
+
async _embedBatchTransformers(texts) {
|
|
360
|
+
const output = await this.model(texts, {
|
|
361
|
+
pooling: "mean",
|
|
362
|
+
normalize: true
|
|
363
|
+
});
|
|
364
|
+
if (!output?.data) {
|
|
365
|
+
return texts.map(() => null);
|
|
366
|
+
}
|
|
367
|
+
const dims = this.getDimensions();
|
|
368
|
+
const data = output.data instanceof Float32Array ? output.data : new Float32Array(output.data);
|
|
369
|
+
const results = [];
|
|
370
|
+
for (let i = 0; i < texts.length; i++) {
|
|
371
|
+
const offset = i * dims;
|
|
372
|
+
if (offset + dims <= data.length) {
|
|
373
|
+
results.push(data.slice(offset, offset + dims));
|
|
374
|
+
} else {
|
|
375
|
+
results.push(null);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return results;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Serial fallback: embed texts one at a time.
|
|
382
|
+
* Used when native batch fails.
|
|
383
|
+
*/
|
|
384
|
+
async _embedBatchSerial(texts) {
|
|
385
|
+
const results = [];
|
|
386
|
+
for (const text of texts) {
|
|
387
|
+
try {
|
|
388
|
+
const embedding = await this.embed(text);
|
|
389
|
+
results.push(embedding);
|
|
390
|
+
} catch {
|
|
391
|
+
results.push(null);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return results;
|
|
395
|
+
}
|
|
396
|
+
// --- Single-text provider implementations ---
|
|
332
397
|
async _embedFastembed(text) {
|
|
333
398
|
const embeddings = this.model.embed([text], 1);
|
|
334
399
|
for await (const batch of embeddings) {
|
|
@@ -359,17 +424,21 @@ function getEmbeddingService() {
|
|
|
359
424
|
}
|
|
360
425
|
|
|
361
426
|
// src/services/search/VectorSearch.ts
|
|
427
|
+
var DEFAULT_MAX_CANDIDATES = 2e3;
|
|
362
428
|
function cosineSimilarity(a, b) {
|
|
363
|
-
|
|
429
|
+
const len = a.length;
|
|
430
|
+
if (len !== b.length) return 0;
|
|
364
431
|
let dotProduct = 0;
|
|
365
432
|
let normA = 0;
|
|
366
433
|
let normB = 0;
|
|
367
|
-
for (let i = 0; i <
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
434
|
+
for (let i = 0; i < len; i++) {
|
|
435
|
+
const ai = a[i];
|
|
436
|
+
const bi = b[i];
|
|
437
|
+
dotProduct += ai * bi;
|
|
438
|
+
normA += ai * ai;
|
|
439
|
+
normB += bi * bi;
|
|
440
|
+
}
|
|
441
|
+
const denominator = Math.sqrt(normA * normB);
|
|
373
442
|
if (denominator === 0) return 0;
|
|
374
443
|
return dotProduct / denominator;
|
|
375
444
|
}
|
|
@@ -382,23 +451,36 @@ function bufferToFloat32(buf) {
|
|
|
382
451
|
}
|
|
383
452
|
var VectorSearch = class {
|
|
384
453
|
/**
|
|
385
|
-
*
|
|
454
|
+
* Semantic search with SQL pre-filtering for scalability.
|
|
455
|
+
*
|
|
456
|
+
* 2-phase strategy:
|
|
457
|
+
* 1. SQL pre-filters by project + sorts by recency (loads max N candidates)
|
|
458
|
+
* 2. JS computes cosine similarity only on filtered candidates
|
|
459
|
+
*
|
|
460
|
+
* With 50k observations and maxCandidates=2000, loads only ~4% of data.
|
|
386
461
|
*/
|
|
387
462
|
async search(db, queryEmbedding, options = {}) {
|
|
388
463
|
const limit = options.limit || 10;
|
|
389
464
|
const threshold = options.threshold || 0.3;
|
|
465
|
+
const maxCandidates = options.maxCandidates || DEFAULT_MAX_CANDIDATES;
|
|
390
466
|
try {
|
|
391
|
-
|
|
467
|
+
const conditions = [];
|
|
468
|
+
const params = [];
|
|
469
|
+
if (options.project) {
|
|
470
|
+
conditions.push("o.project = ?");
|
|
471
|
+
params.push(options.project);
|
|
472
|
+
}
|
|
473
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
474
|
+
const sql = `
|
|
392
475
|
SELECT e.observation_id, e.embedding,
|
|
393
476
|
o.title, o.text, o.type, o.project, o.created_at, o.created_at_epoch
|
|
394
477
|
FROM observation_embeddings e
|
|
395
478
|
JOIN observations o ON o.id = e.observation_id
|
|
479
|
+
${whereClause}
|
|
480
|
+
ORDER BY o.created_at_epoch DESC
|
|
481
|
+
LIMIT ?
|
|
396
482
|
`;
|
|
397
|
-
|
|
398
|
-
if (options.project) {
|
|
399
|
-
sql += " WHERE o.project = ?";
|
|
400
|
-
params.push(options.project);
|
|
401
|
-
}
|
|
483
|
+
params.push(maxCandidates);
|
|
402
484
|
const rows = db.query(sql).all(...params);
|
|
403
485
|
const scored = [];
|
|
404
486
|
for (const row of rows) {
|
|
@@ -419,14 +501,15 @@ var VectorSearch = class {
|
|
|
419
501
|
}
|
|
420
502
|
}
|
|
421
503
|
scored.sort((a, b) => b.similarity - a.similarity);
|
|
504
|
+
logger.debug("VECTOR", `Search: ${rows.length} candidates \u2192 ${scored.length} above threshold \u2192 ${Math.min(scored.length, limit)} results`);
|
|
422
505
|
return scored.slice(0, limit);
|
|
423
506
|
} catch (error) {
|
|
424
|
-
logger.error("VECTOR", `
|
|
507
|
+
logger.error("VECTOR", `Vector search error: ${error}`);
|
|
425
508
|
return [];
|
|
426
509
|
}
|
|
427
510
|
}
|
|
428
511
|
/**
|
|
429
|
-
*
|
|
512
|
+
* Store embedding for an observation.
|
|
430
513
|
*/
|
|
431
514
|
async storeEmbedding(db, observationId, embedding, model) {
|
|
432
515
|
try {
|
|
@@ -442,18 +525,18 @@ var VectorSearch = class {
|
|
|
442
525
|
embedding.length,
|
|
443
526
|
(/* @__PURE__ */ new Date()).toISOString()
|
|
444
527
|
);
|
|
445
|
-
logger.debug("VECTOR", `Embedding
|
|
528
|
+
logger.debug("VECTOR", `Embedding saved for observation ${observationId}`);
|
|
446
529
|
} catch (error) {
|
|
447
|
-
logger.error("VECTOR", `
|
|
530
|
+
logger.error("VECTOR", `Error saving embedding: ${error}`);
|
|
448
531
|
}
|
|
449
532
|
}
|
|
450
533
|
/**
|
|
451
|
-
*
|
|
534
|
+
* Generate embeddings for observations that don't have them yet.
|
|
452
535
|
*/
|
|
453
536
|
async backfillEmbeddings(db, batchSize = 50) {
|
|
454
537
|
const embeddingService2 = getEmbeddingService();
|
|
455
538
|
if (!await embeddingService2.initialize()) {
|
|
456
|
-
logger.warn("VECTOR", "Embedding service
|
|
539
|
+
logger.warn("VECTOR", "Embedding service not available, backfill skipped");
|
|
457
540
|
return 0;
|
|
458
541
|
}
|
|
459
542
|
const rows = db.query(`
|
|
@@ -479,11 +562,11 @@ var VectorSearch = class {
|
|
|
479
562
|
count++;
|
|
480
563
|
}
|
|
481
564
|
}
|
|
482
|
-
logger.info("VECTOR", `Backfill
|
|
565
|
+
logger.info("VECTOR", `Backfill completed: ${count}/${rows.length} embeddings generated`);
|
|
483
566
|
return count;
|
|
484
567
|
}
|
|
485
568
|
/**
|
|
486
|
-
*
|
|
569
|
+
* Embedding statistics.
|
|
487
570
|
*/
|
|
488
571
|
getStats(db) {
|
|
489
572
|
try {
|