kiro-memory 1.9.0 → 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.
@@ -98,9 +98,25 @@ function consolidateObservations(db, project, options = {}) {
98
98
  ORDER BY cnt DESC
99
99
  `).all(project, minGroupSize);
100
100
  if (groups.length === 0) return { merged: 0, removed: 0 };
101
- let totalMerged = 0;
102
- let totalRemoved = 0;
101
+ if (options.dryRun) {
102
+ let totalMerged = 0;
103
+ let totalRemoved = 0;
104
+ for (const group of groups) {
105
+ const obsIds = group.ids.split(",").map(Number);
106
+ const placeholders = obsIds.map(() => "?").join(",");
107
+ const count = db.query(
108
+ `SELECT COUNT(*) as cnt FROM observations WHERE id IN (${placeholders})`
109
+ ).get(...obsIds)?.cnt || 0;
110
+ if (count >= minGroupSize) {
111
+ totalMerged += 1;
112
+ totalRemoved += count - 1;
113
+ }
114
+ }
115
+ return { merged: totalMerged, removed: totalRemoved };
116
+ }
103
117
  const runConsolidation = db.transaction(() => {
118
+ let merged = 0;
119
+ let removed = 0;
104
120
  for (const group of groups) {
105
121
  const obsIds = group.ids.split(",").map(Number);
106
122
  const placeholders = obsIds.map(() => "?").join(",");
@@ -108,11 +124,6 @@ function consolidateObservations(db, project, options = {}) {
108
124
  `SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC`
109
125
  ).all(...obsIds);
110
126
  if (observations.length < minGroupSize) continue;
111
- if (options.dryRun) {
112
- totalMerged += 1;
113
- totalRemoved += observations.length - 1;
114
- continue;
115
- }
116
127
  const keeper = observations[0];
117
128
  const others = observations.slice(1);
118
129
  const uniqueTexts = /* @__PURE__ */ new Set();
@@ -125,18 +136,18 @@ function consolidateObservations(db, project, options = {}) {
125
136
  const consolidatedText = Array.from(uniqueTexts).join("\n---\n").substring(0, 1e5);
126
137
  db.run(
127
138
  "UPDATE observations SET text = ?, title = ? WHERE id = ?",
128
- [consolidatedText, `[consolidato x${observations.length}] ${keeper.title}`, keeper.id]
139
+ [consolidatedText, `[consolidated x${observations.length}] ${keeper.title}`, keeper.id]
129
140
  );
130
141
  const removeIds = others.map((o) => o.id);
131
142
  const removePlaceholders = removeIds.map(() => "?").join(",");
132
143
  db.run(`DELETE FROM observations WHERE id IN (${removePlaceholders})`, removeIds);
133
144
  db.run(`DELETE FROM observation_embeddings WHERE observation_id IN (${removePlaceholders})`, removeIds);
134
- totalMerged += 1;
135
- totalRemoved += removeIds.length;
145
+ merged += 1;
146
+ removed += removeIds.length;
136
147
  }
148
+ return { merged, removed };
137
149
  });
138
- runConsolidation();
139
- return { merged: totalMerged, removed: totalRemoved };
150
+ return runConsolidation();
140
151
  }
141
152
  var init_Observations = __esm({
142
153
  "src/services/sqlite/Observations.ts"() {
@@ -163,7 +174,7 @@ function escapeLikePattern3(input) {
163
174
  }
164
175
  function sanitizeFTS5Query(query) {
165
176
  const trimmed = query.length > 1e4 ? query.substring(0, 1e4) : query;
166
- const terms = trimmed.replace(/[""]/g, "").split(/\s+/).filter((t) => t.length > 0).slice(0, 100).map((t) => `"${t}"`);
177
+ const terms = trimmed.replace(/[""\u0022]/g, "").split(/\s+/).filter((t) => t.length > 0).slice(0, 100).map((t) => `"${t}"`);
167
178
  return terms.join(" ");
168
179
  }
169
180
  function searchObservationsFTS(db, query, filters = {}) {
@@ -328,26 +339,38 @@ function getTimeline(db, anchorId, depthBefore = 5, depthAfter = 5) {
328
339
  return [...before, ...self, ...after];
329
340
  }
330
341
  function getProjectStats(db, project) {
331
- const obsStmt = db.query("SELECT COUNT(*) as count FROM observations WHERE project = ?");
332
- const sumStmt = db.query("SELECT COUNT(*) as count FROM summaries WHERE project = ?");
333
- const sesStmt = db.query("SELECT COUNT(*) as count FROM sessions WHERE project = ?");
334
- const prmStmt = db.query("SELECT COUNT(*) as count FROM prompts WHERE project = ?");
335
- const discoveryStmt = db.query(
336
- "SELECT COALESCE(SUM(discovery_tokens), 0) as total FROM observations WHERE project = ?"
337
- );
338
- const discoveryTokens = discoveryStmt.get(project)?.total || 0;
339
- const readStmt = db.query(
340
- `SELECT COALESCE(SUM(
341
- CAST((LENGTH(COALESCE(title, '')) + LENGTH(COALESCE(narrative, ''))) / 4 AS INTEGER)
342
- ), 0) as total FROM observations WHERE project = ?`
343
- );
344
- const readTokens = readStmt.get(project)?.total || 0;
342
+ const sql = `
343
+ WITH
344
+ obs_stats AS (
345
+ SELECT
346
+ COUNT(*) as count,
347
+ COALESCE(SUM(discovery_tokens), 0) as discovery_tokens,
348
+ COALESCE(SUM(
349
+ CAST((LENGTH(COALESCE(title, '')) + LENGTH(COALESCE(narrative, ''))) / 4 AS INTEGER)
350
+ ), 0) as read_tokens
351
+ FROM observations WHERE project = ?
352
+ ),
353
+ sum_count AS (SELECT COUNT(*) as count FROM summaries WHERE project = ?),
354
+ ses_count AS (SELECT COUNT(*) as count FROM sessions WHERE project = ?),
355
+ prm_count AS (SELECT COUNT(*) as count FROM prompts WHERE project = ?)
356
+ SELECT
357
+ obs_stats.count as observations,
358
+ obs_stats.discovery_tokens,
359
+ obs_stats.read_tokens,
360
+ sum_count.count as summaries,
361
+ ses_count.count as sessions,
362
+ prm_count.count as prompts
363
+ FROM obs_stats, sum_count, ses_count, prm_count
364
+ `;
365
+ const row = db.query(sql).get(project, project, project, project);
366
+ const discoveryTokens = row?.discovery_tokens || 0;
367
+ const readTokens = row?.read_tokens || 0;
345
368
  const savings = Math.max(0, discoveryTokens - readTokens);
346
369
  return {
347
- observations: obsStmt.get(project)?.count || 0,
348
- summaries: sumStmt.get(project)?.count || 0,
349
- sessions: sesStmt.get(project)?.count || 0,
350
- prompts: prmStmt.get(project)?.count || 0,
370
+ observations: row?.observations || 0,
371
+ summaries: row?.summaries || 0,
372
+ sessions: row?.sessions || 0,
373
+ prompts: row?.prompts || 0,
351
374
  tokenEconomics: { discoveryTokens, readTokens, savings }
352
375
  };
353
376
  }
@@ -402,14 +425,15 @@ var init_Search = __esm({
402
425
  import BetterSqlite3 from "better-sqlite3";
403
426
  var Database = class {
404
427
  _db;
428
+ _stmtCache = /* @__PURE__ */ new Map();
405
429
  constructor(path, options) {
406
430
  this._db = new BetterSqlite3(path, {
407
- // better-sqlite3 crea il file di default (non serve 'create')
431
+ // better-sqlite3 creates the file by default ('create' not needed)
408
432
  readonly: options?.readwrite === false ? true : false
409
433
  });
410
434
  }
411
435
  /**
412
- * Esegui una query SQL senza risultati
436
+ * Execute a SQL query without results
413
437
  */
414
438
  run(sql, params) {
415
439
  const stmt = this._db.prepare(sql);
@@ -417,51 +441,53 @@ var Database = class {
417
441
  return result;
418
442
  }
419
443
  /**
420
- * Prepara una query con interfaccia compatibile bun:sqlite
444
+ * Prepare a query with bun:sqlite-compatible interface.
445
+ * Returns a cached prepared statement for repeated queries.
421
446
  */
422
447
  query(sql) {
423
- return new BunQueryCompat(this._db, sql);
448
+ let cached = this._stmtCache.get(sql);
449
+ if (!cached) {
450
+ cached = new BunQueryCompat(this._db, sql);
451
+ this._stmtCache.set(sql, cached);
452
+ }
453
+ return cached;
424
454
  }
425
455
  /**
426
- * Crea una transazione
456
+ * Create a transaction
427
457
  */
428
458
  transaction(fn) {
429
459
  return this._db.transaction(fn);
430
460
  }
431
461
  /**
432
- * Chiudi la connessione
462
+ * Close the connection
433
463
  */
434
464
  close() {
465
+ this._stmtCache.clear();
435
466
  this._db.close();
436
467
  }
437
468
  };
438
469
  var BunQueryCompat = class {
439
- _db;
440
- _sql;
470
+ _stmt;
441
471
  constructor(db, sql) {
442
- this._db = db;
443
- this._sql = sql;
472
+ this._stmt = db.prepare(sql);
444
473
  }
445
474
  /**
446
- * Restituisce tutte le righe
475
+ * Returns all rows
447
476
  */
448
477
  all(...params) {
449
- const stmt = this._db.prepare(this._sql);
450
- return params.length > 0 ? stmt.all(...params) : stmt.all();
478
+ return params.length > 0 ? this._stmt.all(...params) : this._stmt.all();
451
479
  }
452
480
  /**
453
- * Restituisce la prima riga o null
481
+ * Returns the first row or null
454
482
  */
455
483
  get(...params) {
456
- const stmt = this._db.prepare(this._sql);
457
- return params.length > 0 ? stmt.get(...params) : stmt.get();
484
+ return params.length > 0 ? this._stmt.get(...params) : this._stmt.get();
458
485
  }
459
486
  /**
460
- * Esegui senza risultati
487
+ * Execute without results
461
488
  */
462
489
  run(...params) {
463
- const stmt = this._db.prepare(this._sql);
464
- return params.length > 0 ? stmt.run(...params) : stmt.run();
490
+ return params.length > 0 ? this._stmt.run(...params) : this._stmt.run();
465
491
  }
466
492
  };
467
493
 
@@ -722,150 +748,63 @@ function ensureDir(dirPath) {
722
748
  // src/services/sqlite/Database.ts
723
749
  var SQLITE_MMAP_SIZE_BYTES = 256 * 1024 * 1024;
724
750
  var SQLITE_CACHE_SIZE_PAGES = 1e4;
725
- var dbInstance = null;
726
751
  var KiroMemoryDatabase = class {
727
- db;
752
+ _db;
728
753
  /**
729
- * @param dbPath - Percorso al file SQLite (default: DB_PATH)
730
- * @param skipMigrations - Se true, salta il migration runner (per hook ad alta frequenza)
754
+ * Readonly accessor for the underlying Database instance.
755
+ * Prefer using query() and run() proxy methods directly.
756
+ */
757
+ get db() {
758
+ return this._db;
759
+ }
760
+ /**
761
+ * @param dbPath - Path to the SQLite file (default: DB_PATH)
762
+ * @param skipMigrations - If true, skip the migration runner (for high-frequency hooks)
731
763
  */
732
764
  constructor(dbPath = DB_PATH, skipMigrations = false) {
733
765
  if (dbPath !== ":memory:") {
734
766
  ensureDir(DATA_DIR);
735
767
  }
736
- this.db = new Database(dbPath, { create: true, readwrite: true });
737
- this.db.run("PRAGMA journal_mode = WAL");
738
- this.db.run("PRAGMA synchronous = NORMAL");
739
- this.db.run("PRAGMA foreign_keys = ON");
740
- this.db.run("PRAGMA temp_store = memory");
741
- this.db.run(`PRAGMA mmap_size = ${SQLITE_MMAP_SIZE_BYTES}`);
742
- this.db.run(`PRAGMA cache_size = ${SQLITE_CACHE_SIZE_PAGES}`);
768
+ this._db = new Database(dbPath, { create: true, readwrite: true });
769
+ this._db.run("PRAGMA journal_mode = WAL");
770
+ this._db.run("PRAGMA busy_timeout = 5000");
771
+ this._db.run("PRAGMA synchronous = NORMAL");
772
+ this._db.run("PRAGMA foreign_keys = ON");
773
+ this._db.run("PRAGMA temp_store = memory");
774
+ this._db.run(`PRAGMA mmap_size = ${SQLITE_MMAP_SIZE_BYTES}`);
775
+ this._db.run(`PRAGMA cache_size = ${SQLITE_CACHE_SIZE_PAGES}`);
743
776
  if (!skipMigrations) {
744
- const migrationRunner = new MigrationRunner(this.db);
777
+ const migrationRunner = new MigrationRunner(this._db);
745
778
  migrationRunner.runAllMigrations();
746
779
  }
747
780
  }
748
781
  /**
749
- * Esegue una funzione all'interno di una transazione atomica.
750
- * Se fn() lancia un errore, la transazione viene annullata automaticamente.
751
- */
752
- withTransaction(fn) {
753
- const transaction = this.db.transaction(fn);
754
- return transaction(this.db);
755
- }
756
- /**
757
- * Close the database connection
758
- */
759
- close() {
760
- this.db.close();
761
- }
762
- };
763
- var DatabaseManager = class _DatabaseManager {
764
- static instance;
765
- db = null;
766
- migrations = [];
767
- static getInstance() {
768
- if (!_DatabaseManager.instance) {
769
- _DatabaseManager.instance = new _DatabaseManager();
770
- }
771
- return _DatabaseManager.instance;
772
- }
773
- /**
774
- * Register a migration to be run during initialization
782
+ * Prepare a query (delegates to underlying Database).
783
+ * Proxy method to avoid ctx.db.db.query() double access.
775
784
  */
776
- registerMigration(migration) {
777
- this.migrations.push(migration);
778
- this.migrations.sort((a, b) => a.version - b.version);
779
- }
780
- /**
781
- * Initialize database connection with optimized settings
782
- */
783
- async initialize() {
784
- if (this.db) {
785
- return this.db;
786
- }
787
- ensureDir(DATA_DIR);
788
- this.db = new Database(DB_PATH, { create: true, readwrite: true });
789
- this.db.run("PRAGMA journal_mode = WAL");
790
- this.db.run("PRAGMA synchronous = NORMAL");
791
- this.db.run("PRAGMA foreign_keys = ON");
792
- this.db.run("PRAGMA temp_store = memory");
793
- this.db.run(`PRAGMA mmap_size = ${SQLITE_MMAP_SIZE_BYTES}`);
794
- this.db.run(`PRAGMA cache_size = ${SQLITE_CACHE_SIZE_PAGES}`);
795
- this.initializeSchemaVersions();
796
- await this.runMigrations();
797
- dbInstance = this.db;
798
- return this.db;
785
+ query(sql) {
786
+ return this._db.query(sql);
799
787
  }
800
788
  /**
801
- * Get the current database connection
789
+ * Execute a SQL statement without results (delegates to underlying Database).
790
+ * Proxy method to avoid ctx.db.db.run() double access.
802
791
  */
803
- getConnection() {
804
- if (!this.db) {
805
- throw new Error("Database not initialized. Call initialize() first.");
806
- }
807
- return this.db;
792
+ run(sql, params) {
793
+ return this._db.run(sql, params);
808
794
  }
809
795
  /**
810
- * Execute a function within a transaction
796
+ * Executes a function within an atomic transaction.
797
+ * If fn() throws an error, the transaction is automatically rolled back.
811
798
  */
812
799
  withTransaction(fn) {
813
- const db = this.getConnection();
814
- const transaction = db.transaction(fn);
815
- return transaction(db);
800
+ const transaction = this._db.transaction(fn);
801
+ return transaction(this._db);
816
802
  }
817
803
  /**
818
804
  * Close the database connection
819
805
  */
820
806
  close() {
821
- if (this.db) {
822
- this.db.close();
823
- this.db = null;
824
- dbInstance = null;
825
- }
826
- }
827
- /**
828
- * Initialize the schema_versions table
829
- */
830
- initializeSchemaVersions() {
831
- if (!this.db) return;
832
- this.db.run(`
833
- CREATE TABLE IF NOT EXISTS schema_versions (
834
- id INTEGER PRIMARY KEY,
835
- version INTEGER UNIQUE NOT NULL,
836
- applied_at TEXT NOT NULL
837
- )
838
- `);
839
- }
840
- /**
841
- * Run all pending migrations
842
- */
843
- async runMigrations() {
844
- if (!this.db) return;
845
- const query = this.db.query("SELECT version FROM schema_versions ORDER BY version");
846
- const appliedVersions = query.all().map((row) => row.version);
847
- const maxApplied = appliedVersions.length > 0 ? Math.max(...appliedVersions) : 0;
848
- for (const migration of this.migrations) {
849
- if (migration.version > maxApplied) {
850
- logger.info("DB", `Applying migration ${migration.version}`);
851
- const transaction = this.db.transaction(() => {
852
- migration.up(this.db);
853
- const insertQuery = this.db.query("INSERT INTO schema_versions (version, applied_at) VALUES (?, ?)");
854
- insertQuery.run(migration.version, (/* @__PURE__ */ new Date()).toISOString());
855
- });
856
- transaction();
857
- logger.info("DB", `Migration ${migration.version} applied successfully`);
858
- }
859
- }
860
- }
861
- /**
862
- * Get current schema version
863
- */
864
- getCurrentVersion() {
865
- if (!this.db) return 0;
866
- const query = this.db.query("SELECT MAX(version) as version FROM schema_versions");
867
- const result = query.get();
868
- return result?.version || 0;
807
+ this._db.close();
869
808
  }
870
809
  };
871
810
  var MigrationRunner = class {
@@ -1110,16 +1049,6 @@ var MigrationRunner = class {
1110
1049
  ];
1111
1050
  }
1112
1051
  };
1113
- function getDatabase() {
1114
- if (!dbInstance) {
1115
- throw new Error("Database not initialized. Call DatabaseManager.getInstance().initialize() first.");
1116
- }
1117
- return dbInstance;
1118
- }
1119
- async function initializeDatabase() {
1120
- const manager = DatabaseManager.getInstance();
1121
- return await manager.initialize();
1122
- }
1123
1052
 
1124
1053
  // src/services/sqlite/Sessions.ts
1125
1054
  function createSession(db, contentSessionId, project, userPrompt) {
@@ -1370,8 +1299,8 @@ var EmbeddingService = class {
1370
1299
  initialized = false;
1371
1300
  initializing = null;
1372
1301
  /**
1373
- * Inizializza il servizio di embedding.
1374
- * Tenta fastembed, poi @huggingface/transformers, poi fallback a null.
1302
+ * Initialize the embedding service.
1303
+ * Tries fastembed, then @huggingface/transformers, then fallback to null.
1375
1304
  */
1376
1305
  async initialize() {
1377
1306
  if (this.initialized) return this.provider !== null;
@@ -1392,11 +1321,11 @@ var EmbeddingService = class {
1392
1321
  });
1393
1322
  this.provider = "fastembed";
1394
1323
  this.initialized = true;
1395
- logger.info("EMBEDDING", "Inizializzato con fastembed (BGE-small-en-v1.5)");
1324
+ logger.info("EMBEDDING", "Initialized with fastembed (BGE-small-en-v1.5)");
1396
1325
  return true;
1397
1326
  }
1398
1327
  } catch (error) {
1399
- logger.debug("EMBEDDING", `fastembed non disponibile: ${error}`);
1328
+ logger.debug("EMBEDDING", `fastembed not available: ${error}`);
1400
1329
  }
1401
1330
  try {
1402
1331
  const transformers = await import("@huggingface/transformers");
@@ -1407,20 +1336,20 @@ var EmbeddingService = class {
1407
1336
  });
1408
1337
  this.provider = "transformers";
1409
1338
  this.initialized = true;
1410
- logger.info("EMBEDDING", "Inizializzato con @huggingface/transformers (all-MiniLM-L6-v2)");
1339
+ logger.info("EMBEDDING", "Initialized with @huggingface/transformers (all-MiniLM-L6-v2)");
1411
1340
  return true;
1412
1341
  }
1413
1342
  } catch (error) {
1414
- logger.debug("EMBEDDING", `@huggingface/transformers non disponibile: ${error}`);
1343
+ logger.debug("EMBEDDING", `@huggingface/transformers not available: ${error}`);
1415
1344
  }
1416
1345
  this.provider = null;
1417
1346
  this.initialized = true;
1418
- logger.warn("EMBEDDING", "Nessun provider embedding disponibile, ricerca semantica disabilitata");
1347
+ logger.warn("EMBEDDING", "No embedding provider available, semantic search disabled");
1419
1348
  return false;
1420
1349
  }
1421
1350
  /**
1422
- * Genera embedding per un singolo testo.
1423
- * Ritorna Float32Array con 384 dimensioni, o null se non disponibile.
1351
+ * Generate embedding for a single text.
1352
+ * Returns Float32Array with 384 dimensions, or null if not available.
1424
1353
  */
1425
1354
  async embed(text) {
1426
1355
  if (!this.initialized) await this.initialize();
@@ -1433,46 +1362,111 @@ var EmbeddingService = class {
1433
1362
  return await this._embedTransformers(truncated);
1434
1363
  }
1435
1364
  } catch (error) {
1436
- logger.error("EMBEDDING", `Errore generazione embedding: ${error}`);
1365
+ logger.error("EMBEDDING", `Error generating embedding: ${error}`);
1437
1366
  }
1438
1367
  return null;
1439
1368
  }
1440
1369
  /**
1441
- * Genera embeddings in batch.
1370
+ * Generate embeddings in batch.
1371
+ * Uses native batch support when available (fastembed, transformers),
1372
+ * falls back to serial processing on batch failure.
1442
1373
  */
1443
1374
  async embedBatch(texts) {
1444
1375
  if (!this.initialized) await this.initialize();
1445
1376
  if (!this.provider || !this.model) return texts.map(() => null);
1446
- const results = [];
1447
- for (const text of texts) {
1448
- try {
1449
- const embedding = await this.embed(text);
1450
- results.push(embedding);
1451
- } catch {
1452
- results.push(null);
1377
+ if (texts.length === 0) return [];
1378
+ const truncated = texts.map((t) => t.substring(0, 2e3));
1379
+ try {
1380
+ if (this.provider === "fastembed") {
1381
+ return await this._embedBatchFastembed(truncated);
1382
+ } else if (this.provider === "transformers") {
1383
+ return await this._embedBatchTransformers(truncated);
1453
1384
  }
1385
+ } catch (error) {
1386
+ logger.warn("EMBEDDING", `Batch embedding failed, falling back to serial: ${error}`);
1454
1387
  }
1455
- return results;
1388
+ return this._embedBatchSerial(truncated);
1456
1389
  }
1457
1390
  /**
1458
- * Verifica se il servizio è disponibile.
1391
+ * Check if the service is available.
1459
1392
  */
1460
1393
  isAvailable() {
1461
1394
  return this.initialized && this.provider !== null;
1462
1395
  }
1463
1396
  /**
1464
- * Nome del provider attivo.
1397
+ * Name of the active provider.
1465
1398
  */
1466
1399
  getProvider() {
1467
1400
  return this.provider;
1468
1401
  }
1469
1402
  /**
1470
- * Dimensioni del vettore embedding.
1403
+ * Embedding vector dimensions.
1471
1404
  */
1472
1405
  getDimensions() {
1473
1406
  return 384;
1474
1407
  }
1475
- // --- Provider specifici ---
1408
+ // --- Batch implementations ---
1409
+ /**
1410
+ * Native batch embedding with fastembed.
1411
+ * FlagEmbedding.embed() accepts string[] and returns an async iterable of batches.
1412
+ */
1413
+ async _embedBatchFastembed(texts) {
1414
+ const results = [];
1415
+ const embeddings = this.model.embed(texts, texts.length);
1416
+ for await (const batch of embeddings) {
1417
+ if (batch) {
1418
+ for (const vec of batch) {
1419
+ results.push(vec instanceof Float32Array ? vec : new Float32Array(vec));
1420
+ }
1421
+ }
1422
+ }
1423
+ while (results.length < texts.length) {
1424
+ results.push(null);
1425
+ }
1426
+ return results;
1427
+ }
1428
+ /**
1429
+ * Batch embedding with @huggingface/transformers pipeline.
1430
+ * The pipeline accepts string[] and returns a Tensor with shape [N, dims].
1431
+ */
1432
+ async _embedBatchTransformers(texts) {
1433
+ const output = await this.model(texts, {
1434
+ pooling: "mean",
1435
+ normalize: true
1436
+ });
1437
+ if (!output?.data) {
1438
+ return texts.map(() => null);
1439
+ }
1440
+ const dims = this.getDimensions();
1441
+ const data = output.data instanceof Float32Array ? output.data : new Float32Array(output.data);
1442
+ const results = [];
1443
+ for (let i = 0; i < texts.length; i++) {
1444
+ const offset = i * dims;
1445
+ if (offset + dims <= data.length) {
1446
+ results.push(data.slice(offset, offset + dims));
1447
+ } else {
1448
+ results.push(null);
1449
+ }
1450
+ }
1451
+ return results;
1452
+ }
1453
+ /**
1454
+ * Serial fallback: embed texts one at a time.
1455
+ * Used when native batch fails.
1456
+ */
1457
+ async _embedBatchSerial(texts) {
1458
+ const results = [];
1459
+ for (const text of texts) {
1460
+ try {
1461
+ const embedding = await this.embed(text);
1462
+ results.push(embedding);
1463
+ } catch {
1464
+ results.push(null);
1465
+ }
1466
+ }
1467
+ return results;
1468
+ }
1469
+ // --- Single-text provider implementations ---
1476
1470
  async _embedFastembed(text) {
1477
1471
  const embeddings = this.model.embed([text], 1);
1478
1472
  for await (const batch of embeddings) {
@@ -1503,17 +1497,21 @@ function getEmbeddingService() {
1503
1497
  }
1504
1498
 
1505
1499
  // src/services/search/VectorSearch.ts
1500
+ var DEFAULT_MAX_CANDIDATES = 2e3;
1506
1501
  function cosineSimilarity(a, b) {
1507
- if (a.length !== b.length) return 0;
1502
+ const len = a.length;
1503
+ if (len !== b.length) return 0;
1508
1504
  let dotProduct = 0;
1509
1505
  let normA = 0;
1510
1506
  let normB = 0;
1511
- for (let i = 0; i < a.length; i++) {
1512
- dotProduct += a[i] * b[i];
1513
- normA += a[i] * a[i];
1514
- normB += b[i] * b[i];
1515
- }
1516
- const denominator = Math.sqrt(normA) * Math.sqrt(normB);
1507
+ for (let i = 0; i < len; i++) {
1508
+ const ai = a[i];
1509
+ const bi = b[i];
1510
+ dotProduct += ai * bi;
1511
+ normA += ai * ai;
1512
+ normB += bi * bi;
1513
+ }
1514
+ const denominator = Math.sqrt(normA * normB);
1517
1515
  if (denominator === 0) return 0;
1518
1516
  return dotProduct / denominator;
1519
1517
  }
@@ -1526,23 +1524,36 @@ function bufferToFloat32(buf) {
1526
1524
  }
1527
1525
  var VectorSearch = class {
1528
1526
  /**
1529
- * Ricerca semantica: calcola cosine similarity tra query e tutti gli embeddings.
1527
+ * Semantic search with SQL pre-filtering for scalability.
1528
+ *
1529
+ * 2-phase strategy:
1530
+ * 1. SQL pre-filters by project + sorts by recency (loads max N candidates)
1531
+ * 2. JS computes cosine similarity only on filtered candidates
1532
+ *
1533
+ * With 50k observations and maxCandidates=2000, loads only ~4% of data.
1530
1534
  */
1531
1535
  async search(db, queryEmbedding, options = {}) {
1532
1536
  const limit = options.limit || 10;
1533
1537
  const threshold = options.threshold || 0.3;
1538
+ const maxCandidates = options.maxCandidates || DEFAULT_MAX_CANDIDATES;
1534
1539
  try {
1535
- let sql = `
1540
+ const conditions = [];
1541
+ const params = [];
1542
+ if (options.project) {
1543
+ conditions.push("o.project = ?");
1544
+ params.push(options.project);
1545
+ }
1546
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1547
+ const sql = `
1536
1548
  SELECT e.observation_id, e.embedding,
1537
1549
  o.title, o.text, o.type, o.project, o.created_at, o.created_at_epoch
1538
1550
  FROM observation_embeddings e
1539
1551
  JOIN observations o ON o.id = e.observation_id
1552
+ ${whereClause}
1553
+ ORDER BY o.created_at_epoch DESC
1554
+ LIMIT ?
1540
1555
  `;
1541
- const params = [];
1542
- if (options.project) {
1543
- sql += " WHERE o.project = ?";
1544
- params.push(options.project);
1545
- }
1556
+ params.push(maxCandidates);
1546
1557
  const rows = db.query(sql).all(...params);
1547
1558
  const scored = [];
1548
1559
  for (const row of rows) {
@@ -1563,14 +1574,15 @@ var VectorSearch = class {
1563
1574
  }
1564
1575
  }
1565
1576
  scored.sort((a, b) => b.similarity - a.similarity);
1577
+ logger.debug("VECTOR", `Search: ${rows.length} candidates \u2192 ${scored.length} above threshold \u2192 ${Math.min(scored.length, limit)} results`);
1566
1578
  return scored.slice(0, limit);
1567
1579
  } catch (error) {
1568
- logger.error("VECTOR", `Errore ricerca vettoriale: ${error}`);
1580
+ logger.error("VECTOR", `Vector search error: ${error}`);
1569
1581
  return [];
1570
1582
  }
1571
1583
  }
1572
1584
  /**
1573
- * Salva embedding per un'osservazione.
1585
+ * Store embedding for an observation.
1574
1586
  */
1575
1587
  async storeEmbedding(db, observationId, embedding, model) {
1576
1588
  try {
@@ -1586,18 +1598,18 @@ var VectorSearch = class {
1586
1598
  embedding.length,
1587
1599
  (/* @__PURE__ */ new Date()).toISOString()
1588
1600
  );
1589
- logger.debug("VECTOR", `Embedding salvato per osservazione ${observationId}`);
1601
+ logger.debug("VECTOR", `Embedding saved for observation ${observationId}`);
1590
1602
  } catch (error) {
1591
- logger.error("VECTOR", `Errore salvataggio embedding: ${error}`);
1603
+ logger.error("VECTOR", `Error saving embedding: ${error}`);
1592
1604
  }
1593
1605
  }
1594
1606
  /**
1595
- * Genera embeddings per osservazioni che non li hanno ancora.
1607
+ * Generate embeddings for observations that don't have them yet.
1596
1608
  */
1597
1609
  async backfillEmbeddings(db, batchSize = 50) {
1598
1610
  const embeddingService2 = getEmbeddingService();
1599
1611
  if (!await embeddingService2.initialize()) {
1600
- logger.warn("VECTOR", "Embedding service non disponibile, backfill saltato");
1612
+ logger.warn("VECTOR", "Embedding service not available, backfill skipped");
1601
1613
  return 0;
1602
1614
  }
1603
1615
  const rows = db.query(`
@@ -1623,11 +1635,11 @@ var VectorSearch = class {
1623
1635
  count++;
1624
1636
  }
1625
1637
  }
1626
- logger.info("VECTOR", `Backfill completato: ${count}/${rows.length} embeddings generati`);
1638
+ logger.info("VECTOR", `Backfill completed: ${count}/${rows.length} embeddings generated`);
1627
1639
  return count;
1628
1640
  }
1629
1641
  /**
1630
- * Statistiche sugli embeddings.
1642
+ * Embedding statistics.
1631
1643
  */
1632
1644
  getStats(db) {
1633
1645
  try {
@@ -1700,21 +1712,21 @@ function knowledgeTypeBoost(type) {
1700
1712
  var HybridSearch = class {
1701
1713
  embeddingInitialized = false;
1702
1714
  /**
1703
- * Inizializza il servizio di embedding (lazy, non bloccante)
1715
+ * Initialize the embedding service (lazy, non-blocking)
1704
1716
  */
1705
1717
  async initialize() {
1706
1718
  try {
1707
1719
  const embeddingService2 = getEmbeddingService();
1708
1720
  await embeddingService2.initialize();
1709
1721
  this.embeddingInitialized = embeddingService2.isAvailable();
1710
- logger.info("SEARCH", `HybridSearch inizializzato (embedding: ${this.embeddingInitialized ? "attivo" : "disattivato"})`);
1722
+ logger.info("SEARCH", `HybridSearch initialized (embedding: ${this.embeddingInitialized ? "active" : "disabled"})`);
1711
1723
  } catch (error) {
1712
- logger.warn("SEARCH", "Inizializzazione embedding fallita, uso solo FTS5", {}, error);
1724
+ logger.warn("SEARCH", "Embedding initialization failed, using only FTS5", {}, error);
1713
1725
  this.embeddingInitialized = false;
1714
1726
  }
1715
1727
  }
1716
1728
  /**
1717
- * Ricerca ibrida con scoring a 4 segnali
1729
+ * Hybrid search with 4-signal scoring
1718
1730
  */
1719
1731
  async search(db, query, options = {}) {
1720
1732
  const limit = options.limit || 10;
@@ -1730,7 +1742,7 @@ var HybridSearch = class {
1730
1742
  const vectorResults = await vectorSearch2.search(db, queryEmbedding, {
1731
1743
  project: options.project,
1732
1744
  limit: limit * 2,
1733
- // Prendiamo piu risultati per il ranking
1745
+ // Fetch more results for ranking
1734
1746
  threshold: 0.3
1735
1747
  });
1736
1748
  for (const hit of vectorResults) {
@@ -1747,10 +1759,10 @@ var HybridSearch = class {
1747
1759
  source: "vector"
1748
1760
  });
1749
1761
  }
1750
- logger.debug("SEARCH", `Vector search: ${vectorResults.length} risultati`);
1762
+ logger.debug("SEARCH", `Vector search: ${vectorResults.length} results`);
1751
1763
  }
1752
1764
  } catch (error) {
1753
- logger.warn("SEARCH", "Ricerca vettoriale fallita, uso solo keyword", {}, error);
1765
+ logger.warn("SEARCH", "Vector search failed, using only keyword", {}, error);
1754
1766
  }
1755
1767
  }
1756
1768
  try {
@@ -1780,9 +1792,9 @@ var HybridSearch = class {
1780
1792
  });
1781
1793
  }
1782
1794
  }
1783
- logger.debug("SEARCH", `Keyword search: ${keywordResults.length} risultati`);
1795
+ logger.debug("SEARCH", `Keyword search: ${keywordResults.length} results`);
1784
1796
  } catch (error) {
1785
- logger.error("SEARCH", "Ricerca keyword fallita", {}, error);
1797
+ logger.error("SEARCH", "Keyword search failed", {}, error);
1786
1798
  }
1787
1799
  if (rawItems.size === 0) return [];
1788
1800
  const allFTS5Ranks = Array.from(rawItems.values()).filter((item) => item.fts5Rank !== null).map((item) => item.fts5Rank);
@@ -1870,33 +1882,33 @@ var KiroMemorySDK = class {
1870
1882
  };
1871
1883
  }
1872
1884
  /**
1873
- * Valida input per storeObservation
1885
+ * Validate input for storeObservation
1874
1886
  */
1875
1887
  validateObservationInput(data) {
1876
1888
  if (!data.type || typeof data.type !== "string" || data.type.length > 100) {
1877
- throw new Error("type \xE8 obbligatorio (stringa, max 100 caratteri)");
1889
+ throw new Error("type is required (string, max 100 chars)");
1878
1890
  }
1879
1891
  if (!data.title || typeof data.title !== "string" || data.title.length > 500) {
1880
- throw new Error("title \xE8 obbligatorio (stringa, max 500 caratteri)");
1892
+ throw new Error("title is required (string, max 500 chars)");
1881
1893
  }
1882
1894
  if (!data.content || typeof data.content !== "string" || data.content.length > 1e5) {
1883
- throw new Error("content \xE8 obbligatorio (stringa, max 100KB)");
1895
+ throw new Error("content is required (string, max 100KB)");
1884
1896
  }
1885
1897
  }
1886
1898
  /**
1887
- * Valida input per storeSummary
1899
+ * Validate input for storeSummary
1888
1900
  */
1889
1901
  validateSummaryInput(data) {
1890
1902
  const MAX = 5e4;
1891
1903
  for (const [key, val] of Object.entries(data)) {
1892
1904
  if (val !== void 0 && val !== null) {
1893
- if (typeof val !== "string") throw new Error(`${key} deve essere una stringa`);
1894
- if (val.length > MAX) throw new Error(`${key} troppo grande (max 50KB)`);
1905
+ if (typeof val !== "string") throw new Error(`${key} must be a string`);
1906
+ if (val.length > MAX) throw new Error(`${key} too large (max 50KB)`);
1895
1907
  }
1896
1908
  }
1897
1909
  }
1898
1910
  /**
1899
- * Genera e salva embedding per un'osservazione (fire-and-forget, non blocca)
1911
+ * Generate and store embedding for an observation (fire-and-forget, non-blocking)
1900
1912
  */
1901
1913
  async generateEmbeddingAsync(observationId, title, content, concepts) {
1902
1914
  try {
@@ -1916,39 +1928,39 @@ var KiroMemorySDK = class {
1916
1928
  );
1917
1929
  }
1918
1930
  } catch (error) {
1919
- logger.debug("SDK", `Embedding generation fallita per obs ${observationId}: ${error}`);
1931
+ logger.debug("SDK", `Embedding generation failed for obs ${observationId}: ${error}`);
1920
1932
  }
1921
1933
  }
1922
1934
  /**
1923
- * Genera content hash SHA256 per deduplicazione basata su contenuto.
1924
- * Usa (project + type + title + narrative) come tupla di identità semantica.
1925
- * NON include sessionId perché è unico ad ogni invocazione.
1935
+ * Generate SHA256 content hash for content-based deduplication.
1936
+ * Uses (project + type + title + narrative) as semantic identity tuple.
1937
+ * Does NOT include sessionId since it's unique per invocation.
1926
1938
  */
1927
1939
  generateContentHash(type, title, narrative) {
1928
1940
  const payload = `${this.project}|${type}|${title}|${narrative || ""}`;
1929
1941
  return createHash("sha256").update(payload).digest("hex");
1930
1942
  }
1931
1943
  /**
1932
- * Finestre di deduplicazione per tipo (ms).
1933
- * Tipi con molte ripetizioni hanno finestre più ampie.
1944
+ * Deduplication windows per type (ms).
1945
+ * Types with many repetitions have wider windows.
1934
1946
  */
1935
1947
  getDeduplicationWindow(type) {
1936
1948
  switch (type) {
1937
1949
  case "file-read":
1938
1950
  return 6e4;
1939
- // 60s — letture frequenti sugli stessi file
1951
+ // 60s — frequent reads on the same files
1940
1952
  case "file-write":
1941
1953
  return 1e4;
1942
- // 10s — scritture rapide consecutive
1954
+ // 10s — rapid consecutive writes
1943
1955
  case "command":
1944
1956
  return 3e4;
1945
1957
  // 30s — standard
1946
1958
  case "research":
1947
1959
  return 12e4;
1948
- // 120s — web search e fetch ripetuti
1960
+ // 120s — repeated web search and fetch
1949
1961
  case "delegation":
1950
1962
  return 6e4;
1951
- // 60s — delegazioni rapide
1963
+ // 60s — rapid delegations
1952
1964
  default:
1953
1965
  return 3e4;
1954
1966
  }
@@ -1962,7 +1974,7 @@ var KiroMemorySDK = class {
1962
1974
  const contentHash = this.generateContentHash(data.type, data.title, data.narrative);
1963
1975
  const dedupWindow = this.getDeduplicationWindow(data.type);
1964
1976
  if (isDuplicateObservation(this.db.db, contentHash, dedupWindow)) {
1965
- logger.debug("SDK", `Osservazione duplicata scartata (${data.type}, ${dedupWindow}ms): ${data.title}`);
1977
+ logger.debug("SDK", `Duplicate observation discarded (${data.type}, ${dedupWindow}ms): ${data.title}`);
1966
1978
  return -1;
1967
1979
  }
1968
1980
  const filesRead = data.filesRead || (data.type === "file-read" ? data.files : void 0);
@@ -1990,12 +2002,12 @@ var KiroMemorySDK = class {
1990
2002
  return observationId;
1991
2003
  }
1992
2004
  /**
1993
- * Salva conoscenza strutturata (constraint, decision, heuristic, rejected).
1994
- * Usa il campo `type` per il knowledgeType e `facts` per i metadati JSON.
2005
+ * Store structured knowledge (constraint, decision, heuristic, rejected).
2006
+ * Uses the `type` field for knowledgeType and `facts` for JSON metadata.
1995
2007
  */
1996
2008
  async storeKnowledge(data) {
1997
2009
  if (!KNOWLEDGE_TYPES.includes(data.knowledgeType)) {
1998
- throw new Error(`knowledgeType non valido: ${data.knowledgeType}. Valori ammessi: ${KNOWLEDGE_TYPES.join(", ")}`);
2010
+ throw new Error(`Invalid knowledgeType: ${data.knowledgeType}. Allowed values: ${KNOWLEDGE_TYPES.join(", ")}`);
1999
2011
  }
2000
2012
  this.validateObservationInput({ type: data.knowledgeType, title: data.title, content: data.content });
2001
2013
  const metadata = (() => {
@@ -2027,9 +2039,9 @@ var KiroMemorySDK = class {
2027
2039
  }
2028
2040
  })();
2029
2041
  const sessionId = "sdk-" + Date.now();
2030
- const contentHash = this.generateContentHash(data.type, data.title);
2042
+ const contentHash = this.generateContentHash(data.knowledgeType, data.title);
2031
2043
  if (isDuplicateObservation(this.db.db, contentHash)) {
2032
- logger.debug("SDK", `Knowledge duplicata scartata: ${data.title}`);
2044
+ logger.debug("SDK", `Duplicate knowledge discarded: ${data.title}`);
2033
2045
  return -1;
2034
2046
  }
2035
2047
  const discoveryTokens = Math.ceil(data.content.length / 4);
@@ -2046,11 +2058,11 @@ var KiroMemorySDK = class {
2046
2058
  null,
2047
2059
  // narrative
2048
2060
  JSON.stringify(metadata),
2049
- // facts = metadati JSON
2061
+ // facts = JSON metadata
2050
2062
  data.concepts?.join(", ") || null,
2051
2063
  data.files?.join(", ") || null,
2052
2064
  null,
2053
- // filesModified: knowledge non modifica file
2065
+ // filesModified: knowledge doesn't modify files
2054
2066
  0,
2055
2067
  // prompt_number
2056
2068
  contentHash,
@@ -2161,8 +2173,8 @@ var KiroMemorySDK = class {
2161
2173
  return this.project;
2162
2174
  }
2163
2175
  /**
2164
- * Ricerca ibrida: vector search + keyword FTS5
2165
- * Richiede inizializzazione HybridSearch (embedding service)
2176
+ * Hybrid search: vector search + keyword FTS5
2177
+ * Requires HybridSearch initialization (embedding service)
2166
2178
  */
2167
2179
  async hybridSearch(query, options = {}) {
2168
2180
  const hybridSearch2 = getHybridSearch();
@@ -2172,8 +2184,8 @@ var KiroMemorySDK = class {
2172
2184
  });
2173
2185
  }
2174
2186
  /**
2175
- * Ricerca solo semantica (vector search)
2176
- * Ritorna risultati basati su similarità coseno con gli embeddings
2187
+ * Semantic-only search (vector search)
2188
+ * Returns results based on cosine similarity with embeddings
2177
2189
  */
2178
2190
  async semanticSearch(query, options = {}) {
2179
2191
  const embeddingService2 = getEmbeddingService();
@@ -2208,21 +2220,21 @@ var KiroMemorySDK = class {
2208
2220
  }));
2209
2221
  }
2210
2222
  /**
2211
- * Genera embeddings per osservazioni che non li hanno ancora
2223
+ * Generate embeddings for observations that don't have them yet
2212
2224
  */
2213
2225
  async backfillEmbeddings(batchSize = 50) {
2214
2226
  const vectorSearch2 = getVectorSearch();
2215
2227
  return vectorSearch2.backfillEmbeddings(this.db.db, batchSize);
2216
2228
  }
2217
2229
  /**
2218
- * Statistiche sugli embeddings nel database
2230
+ * Embedding statistics in the database
2219
2231
  */
2220
2232
  getEmbeddingStats() {
2221
2233
  const vectorSearch2 = getVectorSearch();
2222
2234
  return vectorSearch2.getStats(this.db.db);
2223
2235
  }
2224
2236
  /**
2225
- * Inizializza il servizio di embedding (lazy, chiamare prima di hybridSearch)
2237
+ * Initialize the embedding service (lazy, call before hybridSearch)
2226
2238
  */
2227
2239
  async initializeEmbeddings() {
2228
2240
  const hybridSearch2 = getHybridSearch();
@@ -2230,10 +2242,10 @@ var KiroMemorySDK = class {
2230
2242
  return getEmbeddingService().isAvailable();
2231
2243
  }
2232
2244
  /**
2233
- * Contesto smart con ranking a 4 segnali e budget token.
2245
+ * Smart context with 4-signal ranking and token budget.
2234
2246
  *
2235
- * Se query presente: usa HybridSearch con SEARCH_WEIGHTS.
2236
- * Se senza query: ranking per recency + project match (CONTEXT_WEIGHTS).
2247
+ * If query present: uses HybridSearch with SEARCH_WEIGHTS.
2248
+ * If no query: ranking by recency + project match (CONTEXT_WEIGHTS).
2237
2249
  */
2238
2250
  async getSmartContext(options = {}) {
2239
2251
  const tokenBudget = options.tokenBudget || parseInt(process.env.KIRO_MEMORY_CONTEXT_TOKENS || "0", 10) || 2e3;
@@ -2307,8 +2319,8 @@ var KiroMemorySDK = class {
2307
2319
  };
2308
2320
  }
2309
2321
  /**
2310
- * Rileva osservazioni stale (file modificati dopo la creazione) e le marca nel DB.
2311
- * Ritorna il numero di osservazioni marcate come stale.
2322
+ * Detect stale observations (files modified after creation) and mark them in DB.
2323
+ * Returns the number of observations marked as stale.
2312
2324
  */
2313
2325
  async detectStaleObservations() {
2314
2326
  const staleObs = getStaleObservations(this.db.db, this.project);
@@ -2319,14 +2331,14 @@ var KiroMemorySDK = class {
2319
2331
  return staleObs.length;
2320
2332
  }
2321
2333
  /**
2322
- * Consolida osservazioni duplicate sullo stesso file e tipo.
2323
- * Raggruppa per (project, type, files_modified), mantiene la piu recente.
2334
+ * Consolidate duplicate observations on the same file and type.
2335
+ * Groups by (project, type, files_modified), keeps the most recent.
2324
2336
  */
2325
2337
  async consolidateObservations(options = {}) {
2326
2338
  return consolidateObservations(this.db.db, this.project, options);
2327
2339
  }
2328
2340
  /**
2329
- * Statistiche decay: totale, stale, mai accedute, accedute di recente.
2341
+ * Decay statistics: total, stale, never accessed, recently accessed.
2330
2342
  */
2331
2343
  async getDecayStats() {
2332
2344
  const total = this.db.db.query(
@@ -2345,8 +2357,8 @@ var KiroMemorySDK = class {
2345
2357
  return { total, stale, neverAccessed, recentlyAccessed };
2346
2358
  }
2347
2359
  /**
2348
- * Crea un checkpoint strutturato per resume sessione.
2349
- * Salva automaticamente un context_snapshot con le ultime 10 osservazioni.
2360
+ * Create a structured checkpoint for session resume.
2361
+ * Automatically saves a context_snapshot with the last 10 observations.
2350
2362
  */
2351
2363
  async createCheckpoint(sessionId, data) {
2352
2364
  const recentObs = getObservationsByProject(this.db.db, this.project, 10);
@@ -2363,21 +2375,21 @@ var KiroMemorySDK = class {
2363
2375
  });
2364
2376
  }
2365
2377
  /**
2366
- * Recupera l'ultimo checkpoint di una sessione specifica.
2378
+ * Retrieve the latest checkpoint of a specific session.
2367
2379
  */
2368
2380
  async getCheckpoint(sessionId) {
2369
2381
  return getLatestCheckpoint(this.db.db, sessionId);
2370
2382
  }
2371
2383
  /**
2372
- * Recupera l'ultimo checkpoint per il progetto corrente.
2373
- * Utile per resume automatico senza specificare session ID.
2384
+ * Retrieve the latest checkpoint for the current project.
2385
+ * Useful for automatic resume without specifying session ID.
2374
2386
  */
2375
2387
  async getLatestProjectCheckpoint() {
2376
2388
  return getLatestCheckpointByProject(this.db.db, this.project);
2377
2389
  }
2378
2390
  /**
2379
- * Genera un report di attività per il progetto corrente.
2380
- * Aggrega osservazioni, sessioni, summaries e file per un periodo temporale.
2391
+ * Generate an activity report for the current project.
2392
+ * Aggregates observations, sessions, summaries and files for a time period.
2381
2393
  */
2382
2394
  async generateReport(options) {
2383
2395
  const now = /* @__PURE__ */ new Date();
@@ -2409,8 +2421,6 @@ var KiroMemorySDK = class {
2409
2421
  function createKiroMemory(config) {
2410
2422
  return new KiroMemorySDK(config);
2411
2423
  }
2412
- var ContextKitSDK = KiroMemorySDK;
2413
- var createContextKit = createKiroMemory;
2414
2424
 
2415
2425
  // src/index.ts
2416
2426
  init_Search();
@@ -2461,7 +2471,7 @@ async function readStdin() {
2461
2471
  }
2462
2472
  resolve(JSON.parse(data));
2463
2473
  } catch (err) {
2464
- reject(new Error(`Errore parsing stdin JSON: ${err}`));
2474
+ reject(new Error(`Error parsing stdin JSON: ${err}`));
2465
2475
  }
2466
2476
  });
2467
2477
  process.stdin.on("error", (err) => {
@@ -2486,19 +2496,19 @@ function detectProject(cwd) {
2486
2496
  function formatContext(data) {
2487
2497
  let output = "";
2488
2498
  if (data.summaries && data.summaries.length > 0) {
2489
- output += "## Sessioni Precedenti\n\n";
2499
+ output += "## Previous Sessions\n\n";
2490
2500
  data.summaries.slice(0, 3).forEach((sum) => {
2491
- if (sum.learned) output += `- **Appreso**: ${sum.learned}
2501
+ if (sum.learned) output += `- **Learned**: ${sum.learned}
2492
2502
  `;
2493
- if (sum.completed) output += `- **Completato**: ${sum.completed}
2503
+ if (sum.completed) output += `- **Completed**: ${sum.completed}
2494
2504
  `;
2495
- if (sum.next_steps) output += `- **Prossimi passi**: ${sum.next_steps}
2505
+ if (sum.next_steps) output += `- **Next steps**: ${sum.next_steps}
2496
2506
  `;
2497
2507
  output += "\n";
2498
2508
  });
2499
2509
  }
2500
2510
  if (data.observations && data.observations.length > 0) {
2501
- output += "## Osservazioni Recenti\n\n";
2511
+ output += "## Recent Observations\n\n";
2502
2512
  data.observations.slice(0, 10).forEach((obs) => {
2503
2513
  const text = obs.text ? obs.text.substring(0, 150) : "";
2504
2514
  output += `- **[${obs.type || "obs"}] ${obs.title}**: ${text}
@@ -2519,35 +2529,29 @@ async function runHook(name, handler) {
2519
2529
  }
2520
2530
  debugLog(name, "stdin", input);
2521
2531
  await handler(input);
2522
- debugLog(name, "completato", { success: true });
2532
+ debugLog(name, "completed", { success: true });
2523
2533
  process.exit(0);
2524
2534
  } catch (error) {
2525
- debugLog(name, "errore", { error: String(error) });
2526
- process.stderr.write(`[kiro-memory:${name}] Errore: ${error}
2535
+ debugLog(name, "error", { error: String(error) });
2536
+ process.stderr.write(`[kiro-memory:${name}] Error: ${error}
2527
2537
  `);
2528
2538
  process.exit(0);
2529
2539
  }
2530
2540
  }
2531
2541
 
2532
2542
  // src/index.ts
2533
- var VERSION = "1.5.0";
2543
+ var VERSION = "2.1.0";
2534
2544
  export {
2535
- KiroMemoryDatabase as ContextKitDatabase,
2536
- ContextKitSDK,
2537
- DatabaseManager,
2538
2545
  KiroMemoryDatabase,
2539
2546
  KiroMemorySDK,
2540
2547
  LogLevel,
2541
2548
  VERSION,
2542
- createContextKit,
2543
2549
  createKiroMemory,
2544
2550
  detectProject,
2545
2551
  formatContext,
2546
- getDatabase,
2547
2552
  getObservationsByIds,
2548
2553
  getProjectStats,
2549
2554
  getTimeline,
2550
- initializeDatabase,
2551
2555
  logger,
2552
2556
  readStdin,
2553
2557
  runHook,