kiro-memory 2.1.0 → 3.0.1

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.
Files changed (33) hide show
  1. package/README.md +5 -1
  2. package/package.json +3 -3
  3. package/plugin/dist/cli/contextkit.js +2342 -183
  4. package/plugin/dist/hooks/agentSpawn.js +575 -52
  5. package/plugin/dist/hooks/kiro-hooks.js +575 -52
  6. package/plugin/dist/hooks/postToolUse.js +583 -59
  7. package/plugin/dist/hooks/stop.js +575 -52
  8. package/plugin/dist/hooks/userPromptSubmit.js +578 -53
  9. package/plugin/dist/index.js +576 -53
  10. package/plugin/dist/plugins/github/github-client.js +152 -0
  11. package/plugin/dist/plugins/github/index.js +412 -0
  12. package/plugin/dist/plugins/github/issue-parser.js +54 -0
  13. package/plugin/dist/plugins/slack/formatter.js +90 -0
  14. package/plugin/dist/plugins/slack/index.js +215 -0
  15. package/plugin/dist/sdk/index.js +575 -52
  16. package/plugin/dist/servers/mcp-server.js +4461 -397
  17. package/plugin/dist/services/search/EmbeddingService.js +64 -20
  18. package/plugin/dist/services/search/HybridSearch.js +380 -38
  19. package/plugin/dist/services/search/VectorSearch.js +65 -21
  20. package/plugin/dist/services/search/index.js +380 -38
  21. package/plugin/dist/services/sqlite/Backup.js +416 -0
  22. package/plugin/dist/services/sqlite/Database.js +98 -3
  23. package/plugin/dist/services/sqlite/ImportExport.js +452 -0
  24. package/plugin/dist/services/sqlite/Observations.js +291 -7
  25. package/plugin/dist/services/sqlite/Prompts.js +1 -1
  26. package/plugin/dist/services/sqlite/Search.js +10 -10
  27. package/plugin/dist/services/sqlite/Summaries.js +4 -4
  28. package/plugin/dist/services/sqlite/index.js +1350 -31
  29. package/plugin/dist/viewer.css +1 -1
  30. package/plugin/dist/viewer.js +16 -8
  31. package/plugin/dist/viewer.js.map +4 -4
  32. package/plugin/dist/worker-service.js +326 -75
  33. package/plugin/dist/worker-service.js.map +4 -4
@@ -125,7 +125,7 @@ function searchObservationsLIKE(db, query, filters = {}) {
125
125
  sql += " AND created_at_epoch <= ?";
126
126
  params.push(filters.dateEnd);
127
127
  }
128
- sql += " ORDER BY created_at_epoch DESC LIMIT ?";
128
+ sql += " ORDER BY created_at_epoch DESC, id DESC LIMIT ?";
129
129
  params.push(limit);
130
130
  const stmt = db.query(sql);
131
131
  return stmt.all(...params);
@@ -150,7 +150,7 @@ function searchSummariesFiltered(db, query, filters = {}) {
150
150
  sql += " AND created_at_epoch <= ?";
151
151
  params.push(filters.dateEnd);
152
152
  }
153
- sql += " ORDER BY created_at_epoch DESC LIMIT ?";
153
+ sql += " ORDER BY created_at_epoch DESC, id DESC LIMIT ?";
154
154
  params.push(limit);
155
155
  const stmt = db.query(sql);
156
156
  return stmt.all(...params);
@@ -160,7 +160,7 @@ function getObservationsByIds(db, ids) {
160
160
  const validIds = ids.filter((id) => typeof id === "number" && Number.isInteger(id) && id > 0).slice(0, 500);
161
161
  if (validIds.length === 0) return [];
162
162
  const placeholders = validIds.map(() => "?").join(",");
163
- const sql = `SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC`;
163
+ const sql = `SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC, id DESC`;
164
164
  const stmt = db.query(sql);
165
165
  return stmt.all(...validIds);
166
166
  }
@@ -172,11 +172,11 @@ function getTimeline(db, anchorId, depthBefore = 5, depthAfter = 5) {
172
172
  const beforeStmt = db.query(`
173
173
  SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch
174
174
  FROM observations
175
- WHERE created_at_epoch < ?
176
- ORDER BY created_at_epoch DESC
175
+ WHERE (created_at_epoch < ? OR (created_at_epoch = ? AND id < ?))
176
+ ORDER BY created_at_epoch DESC, id DESC
177
177
  LIMIT ?
178
178
  `);
179
- const before = beforeStmt.all(anchorEpoch, depthBefore).reverse();
179
+ const before = beforeStmt.all(anchorEpoch, anchorEpoch, anchorId, depthBefore).reverse();
180
180
  const selfStmt = db.query(`
181
181
  SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch
182
182
  FROM observations WHERE id = ?
@@ -185,11 +185,11 @@ function getTimeline(db, anchorId, depthBefore = 5, depthAfter = 5) {
185
185
  const afterStmt = db.query(`
186
186
  SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch
187
187
  FROM observations
188
- WHERE created_at_epoch > ?
189
- ORDER BY created_at_epoch ASC
188
+ WHERE (created_at_epoch > ? OR (created_at_epoch = ? AND id > ?))
189
+ ORDER BY created_at_epoch ASC, id ASC
190
190
  LIMIT ?
191
191
  `);
192
- const after = afterStmt.all(anchorEpoch, depthAfter);
192
+ const after = afterStmt.all(anchorEpoch, anchorEpoch, anchorId, depthAfter);
193
193
  return [...before, ...self, ...after];
194
194
  }
195
195
  function getProjectStats(db, project) {
@@ -232,7 +232,7 @@ function getStaleObservations(db, project) {
232
232
  const rows = db.query(`
233
233
  SELECT * FROM observations
234
234
  WHERE project = ? AND files_modified IS NOT NULL AND files_modified != ''
235
- ORDER BY created_at_epoch DESC
235
+ ORDER BY created_at_epoch DESC, id DESC
236
236
  LIMIT 500
237
237
  `).all(project);
238
238
  const staleObs = [];
@@ -275,6 +275,290 @@ var init_Search = __esm({
275
275
  }
276
276
  });
277
277
 
278
+ // src/utils/secrets.ts
279
+ function redactSecrets(text) {
280
+ if (!text) return text;
281
+ let redacted = text;
282
+ for (const { pattern } of SECRET_PATTERNS) {
283
+ pattern.lastIndex = 0;
284
+ redacted = redacted.replace(pattern, (match) => {
285
+ const prefix = match.substring(0, Math.min(4, match.length));
286
+ return `${prefix}***REDACTED***`;
287
+ });
288
+ }
289
+ return redacted;
290
+ }
291
+ var SECRET_PATTERNS;
292
+ var init_secrets = __esm({
293
+ "src/utils/secrets.ts"() {
294
+ "use strict";
295
+ SECRET_PATTERNS = [
296
+ // AWS Access Keys (AKIA, ABIA, ACCA, ASIA prefixes + 16 alphanumeric chars)
297
+ { name: "aws-key", pattern: /(?:AKIA|ABIA|ACCA|ASIA)[A-Z0-9]{16}/g },
298
+ // JWT tokens (three base64url segments separated by dots)
299
+ { name: "jwt", pattern: /eyJ[a-zA-Z0-9_-]{10,}\.eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}/g },
300
+ // Generic API keys in key=value or key: value assignments
301
+ { name: "api-key", pattern: /(?:api[_-]?key|apikey|api[_-]?secret)\s*[:=]\s*['"]?([a-zA-Z0-9_\-]{20,})['"]?/gi },
302
+ // Password/secret/token in variable assignments
303
+ { name: "credential", pattern: /(?:password|passwd|pwd|secret|token|auth[_-]?token|access[_-]?token|bearer)\s*[:=]\s*['"]?([^\s'"]{8,})['"]?/gi },
304
+ // Credentials embedded in URLs (user:pass@host)
305
+ { name: "url-credential", pattern: /(?:https?:\/\/)([^:]+):([^@]+)@/g },
306
+ // PEM-encoded private keys (RSA, EC, DSA, OpenSSH)
307
+ { name: "private-key", pattern: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g },
308
+ // GitHub personal access tokens (ghp_, gho_, ghu_, ghs_, ghr_ prefixes)
309
+ { name: "github-token", pattern: /gh[pousr]_[a-zA-Z0-9]{36,}/g },
310
+ // Slack bot/user/app tokens
311
+ { name: "slack-token", pattern: /xox[bpoas]-[a-zA-Z0-9-]{10,}/g },
312
+ // HTTP Authorization Bearer header values
313
+ { name: "bearer-header", pattern: /\bBearer\s+([a-zA-Z0-9_\-\.]{20,})/g },
314
+ // Generic hex secrets (32+ hex chars after a key/secret/token/password label)
315
+ { name: "hex-secret", pattern: /(?:key|secret|token|password)\s*[:=]\s*['"]?([0-9a-f]{32,})['"]?/gi }
316
+ ];
317
+ }
318
+ });
319
+
320
+ // src/utils/categorizer.ts
321
+ function categorize(input) {
322
+ const scores = /* @__PURE__ */ new Map();
323
+ const searchText = [
324
+ input.title,
325
+ input.text || "",
326
+ input.narrative || "",
327
+ input.concepts || ""
328
+ ].join(" ").toLowerCase();
329
+ const allFiles = [input.filesModified || "", input.filesRead || ""].join(",");
330
+ for (const rule of CATEGORY_RULES) {
331
+ let score = 0;
332
+ for (const kw of rule.keywords) {
333
+ if (searchText.includes(kw.toLowerCase())) {
334
+ score += rule.weight;
335
+ }
336
+ }
337
+ if (rule.types && rule.types.includes(input.type)) {
338
+ score += rule.weight * 2;
339
+ }
340
+ if (rule.filePatterns && allFiles) {
341
+ for (const pattern of rule.filePatterns) {
342
+ if (pattern.test(allFiles)) {
343
+ score += rule.weight;
344
+ }
345
+ }
346
+ }
347
+ if (score > 0) {
348
+ scores.set(rule.category, (scores.get(rule.category) || 0) + score);
349
+ }
350
+ }
351
+ let bestCategory = "general";
352
+ let bestScore = 0;
353
+ for (const [category, score] of scores) {
354
+ if (score > bestScore) {
355
+ bestScore = score;
356
+ bestCategory = category;
357
+ }
358
+ }
359
+ return bestCategory;
360
+ }
361
+ var CATEGORY_RULES;
362
+ var init_categorizer = __esm({
363
+ "src/utils/categorizer.ts"() {
364
+ "use strict";
365
+ CATEGORY_RULES = [
366
+ {
367
+ category: "security",
368
+ keywords: [
369
+ "security",
370
+ "vulnerability",
371
+ "cve",
372
+ "xss",
373
+ "csrf",
374
+ "injection",
375
+ "sanitize",
376
+ "escape",
377
+ "auth",
378
+ "authentication",
379
+ "authorization",
380
+ "permission",
381
+ "helmet",
382
+ "cors",
383
+ "rate-limit",
384
+ "token",
385
+ "encrypt",
386
+ "decrypt",
387
+ "secret",
388
+ "redact",
389
+ "owasp"
390
+ ],
391
+ filePatterns: [/security/i, /auth/i, /secrets?\.ts/i],
392
+ weight: 10
393
+ },
394
+ {
395
+ category: "testing",
396
+ keywords: [
397
+ "test",
398
+ "spec",
399
+ "expect",
400
+ "assert",
401
+ "mock",
402
+ "stub",
403
+ "fixture",
404
+ "coverage",
405
+ "jest",
406
+ "vitest",
407
+ "bun test",
408
+ "unit test",
409
+ "integration test",
410
+ "e2e"
411
+ ],
412
+ types: ["test"],
413
+ filePatterns: [/\.test\./i, /\.spec\./i, /tests?\//i, /__tests__/i],
414
+ weight: 8
415
+ },
416
+ {
417
+ category: "debugging",
418
+ keywords: [
419
+ "debug",
420
+ "fix",
421
+ "bug",
422
+ "error",
423
+ "crash",
424
+ "stacktrace",
425
+ "stack trace",
426
+ "exception",
427
+ "breakpoint",
428
+ "investigate",
429
+ "root cause",
430
+ "troubleshoot",
431
+ "diagnose",
432
+ "bisect",
433
+ "regression"
434
+ ],
435
+ types: ["bugfix"],
436
+ weight: 8
437
+ },
438
+ {
439
+ category: "architecture",
440
+ keywords: [
441
+ "architect",
442
+ "design",
443
+ "pattern",
444
+ "modular",
445
+ "migration",
446
+ "schema",
447
+ "database",
448
+ "api design",
449
+ "abstract",
450
+ "dependency injection",
451
+ "singleton",
452
+ "factory",
453
+ "observer",
454
+ "middleware",
455
+ "pipeline",
456
+ "microservice",
457
+ "monolith"
458
+ ],
459
+ types: ["decision", "constraint"],
460
+ weight: 7
461
+ },
462
+ {
463
+ category: "refactoring",
464
+ keywords: [
465
+ "refactor",
466
+ "rename",
467
+ "extract",
468
+ "inline",
469
+ "move",
470
+ "split",
471
+ "merge",
472
+ "simplify",
473
+ "cleanup",
474
+ "clean up",
475
+ "dead code",
476
+ "consolidate",
477
+ "reorganize",
478
+ "restructure",
479
+ "decouple"
480
+ ],
481
+ weight: 6
482
+ },
483
+ {
484
+ category: "config",
485
+ keywords: [
486
+ "config",
487
+ "configuration",
488
+ "env",
489
+ "environment",
490
+ "dotenv",
491
+ ".env",
492
+ "settings",
493
+ "tsconfig",
494
+ "eslint",
495
+ "prettier",
496
+ "webpack",
497
+ "vite",
498
+ "esbuild",
499
+ "docker",
500
+ "ci/cd",
501
+ "github actions",
502
+ "deploy",
503
+ "build",
504
+ "bundle",
505
+ "package.json"
506
+ ],
507
+ filePatterns: [
508
+ /\.config\./i,
509
+ /\.env/i,
510
+ /tsconfig/i,
511
+ /\.ya?ml/i,
512
+ /Dockerfile/i,
513
+ /docker-compose/i
514
+ ],
515
+ weight: 5
516
+ },
517
+ {
518
+ category: "docs",
519
+ keywords: [
520
+ "document",
521
+ "readme",
522
+ "changelog",
523
+ "jsdoc",
524
+ "comment",
525
+ "explain",
526
+ "guide",
527
+ "tutorial",
528
+ "api doc",
529
+ "openapi",
530
+ "swagger"
531
+ ],
532
+ types: ["docs"],
533
+ filePatterns: [/\.md$/i, /docs?\//i, /readme/i, /changelog/i],
534
+ weight: 5
535
+ },
536
+ {
537
+ category: "feature-dev",
538
+ keywords: [
539
+ "feature",
540
+ "implement",
541
+ "add",
542
+ "create",
543
+ "new",
544
+ "endpoint",
545
+ "component",
546
+ "module",
547
+ "service",
548
+ "handler",
549
+ "route",
550
+ "hook",
551
+ "plugin",
552
+ "integration"
553
+ ],
554
+ types: ["feature", "file-write"],
555
+ weight: 3
556
+ // lowest — generic catch-all for development
557
+ }
558
+ ];
559
+ }
560
+ });
561
+
278
562
  // src/services/sqlite/Observations.ts
279
563
  var Observations_exports = {};
280
564
  __export(Observations_exports, {
@@ -300,11 +584,23 @@ function isDuplicateObservation(db, contentHash, windowMs = 3e4) {
300
584
  }
301
585
  function createObservation(db, memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, contentHash = null, discoveryTokens = 0) {
302
586
  const now = /* @__PURE__ */ new Date();
587
+ const safeTitle = redactSecrets(title);
588
+ const safeText = text ? redactSecrets(text) : text;
589
+ const safeNarrative = narrative ? redactSecrets(narrative) : narrative;
590
+ const autoCategory = categorize({
591
+ type,
592
+ title: safeTitle,
593
+ text: safeText,
594
+ narrative: safeNarrative,
595
+ concepts,
596
+ filesModified,
597
+ filesRead
598
+ });
303
599
  const result = db.run(
304
600
  `INSERT INTO observations
305
- (memory_session_id, project, type, title, subtitle, text, narrative, facts, concepts, files_read, files_modified, prompt_number, created_at, created_at_epoch, content_hash, discovery_tokens)
306
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
307
- [memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, now.toISOString(), now.getTime(), contentHash, discoveryTokens]
601
+ (memory_session_id, project, type, title, subtitle, text, narrative, facts, concepts, files_read, files_modified, prompt_number, created_at, created_at_epoch, content_hash, discovery_tokens, auto_category)
602
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
603
+ [memorySessionId, project, type, safeTitle, subtitle, safeText, safeNarrative, facts, concepts, filesRead, filesModified, promptNumber, now.toISOString(), now.getTime(), contentHash, discoveryTokens, autoCategory]
308
604
  );
309
605
  return Number(result.lastInsertRowid);
310
606
  }
@@ -316,16 +612,16 @@ function getObservationsBySession(db, memorySessionId) {
316
612
  }
317
613
  function getObservationsByProject(db, project, limit = 100) {
318
614
  const query = db.query(
319
- "SELECT * FROM observations WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ?"
615
+ "SELECT * FROM observations WHERE project = ? ORDER BY created_at_epoch DESC, id DESC LIMIT ?"
320
616
  );
321
617
  return query.all(project, limit);
322
618
  }
323
619
  function searchObservations(db, searchTerm, project) {
324
620
  const sql = project ? `SELECT * FROM observations
325
621
  WHERE project = ? AND (title LIKE ? ESCAPE '\\' OR text LIKE ? ESCAPE '\\' OR narrative LIKE ? ESCAPE '\\')
326
- ORDER BY created_at_epoch DESC` : `SELECT * FROM observations
622
+ ORDER BY created_at_epoch DESC, id DESC` : `SELECT * FROM observations
327
623
  WHERE title LIKE ? ESCAPE '\\' OR text LIKE ? ESCAPE '\\' OR narrative LIKE ? ESCAPE '\\'
328
- ORDER BY created_at_epoch DESC`;
624
+ ORDER BY created_at_epoch DESC, id DESC`;
329
625
  const pattern = `%${escapeLikePattern2(searchTerm)}%`;
330
626
  const query = db.query(sql);
331
627
  if (project) {
@@ -381,7 +677,7 @@ function consolidateObservations(db, project, options = {}) {
381
677
  const obsIds = group.ids.split(",").map(Number);
382
678
  const placeholders = obsIds.map(() => "?").join(",");
383
679
  const observations = db.query(
384
- `SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC`
680
+ `SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC, id DESC`
385
681
  ).all(...obsIds);
386
682
  if (observations.length < minGroupSize) continue;
387
683
  const keeper = observations[0];
@@ -412,6 +708,8 @@ function consolidateObservations(db, project, options = {}) {
412
708
  var init_Observations = __esm({
413
709
  "src/services/sqlite/Observations.ts"() {
414
710
  "use strict";
711
+ init_secrets();
712
+ init_categorizer();
415
713
  }
416
714
  });
417
715
 
@@ -635,14 +933,48 @@ ${data.stack}` : ` ${data.message}`;
635
933
  var logger = new Logger();
636
934
 
637
935
  // src/services/search/EmbeddingService.ts
936
+ var MODEL_CONFIGS = {
937
+ "all-MiniLM-L6-v2": {
938
+ modelId: "Xenova/all-MiniLM-L6-v2",
939
+ dimensions: 384
940
+ },
941
+ "jina-code-v2": {
942
+ modelId: "jinaai/jina-embeddings-v2-base-code",
943
+ dimensions: 768
944
+ },
945
+ "bge-small-en": {
946
+ modelId: "BAAI/bge-small-en-v1.5",
947
+ dimensions: 384
948
+ }
949
+ };
950
+ var FASTEMBED_COMPATIBLE_MODELS = /* @__PURE__ */ new Set(["all-MiniLM-L6-v2", "bge-small-en"]);
638
951
  var EmbeddingService = class {
639
952
  provider = null;
640
953
  model = null;
641
954
  initialized = false;
642
955
  initializing = null;
956
+ config;
957
+ configName;
958
+ constructor() {
959
+ const envModel = process.env.KIRO_MEMORY_EMBEDDING_MODEL || "all-MiniLM-L6-v2";
960
+ this.configName = envModel;
961
+ if (MODEL_CONFIGS[envModel]) {
962
+ this.config = MODEL_CONFIGS[envModel];
963
+ } else if (envModel.includes("/")) {
964
+ const dimensions = parseInt(process.env.KIRO_MEMORY_EMBEDDING_DIMENSIONS || "384", 10);
965
+ this.config = {
966
+ modelId: envModel,
967
+ dimensions: isNaN(dimensions) ? 384 : dimensions
968
+ };
969
+ } else {
970
+ logger.warn("EMBEDDING", `Unknown model name '${envModel}', falling back to 'all-MiniLM-L6-v2'`);
971
+ this.configName = "all-MiniLM-L6-v2";
972
+ this.config = MODEL_CONFIGS["all-MiniLM-L6-v2"];
973
+ }
974
+ }
643
975
  /**
644
976
  * Initialize the embedding service.
645
- * Tries fastembed, then @huggingface/transformers, then fallback to null.
977
+ * Tries fastembed (when compatible), then @huggingface/transformers, then falls back to null.
646
978
  */
647
979
  async initialize() {
648
980
  if (this.initialized) return this.provider !== null;
@@ -653,32 +985,35 @@ var EmbeddingService = class {
653
985
  return result;
654
986
  }
655
987
  async _doInitialize() {
656
- try {
657
- const fastembed = await import("fastembed");
658
- const EmbeddingModel = fastembed.EmbeddingModel || fastembed.default?.EmbeddingModel;
659
- const FlagEmbedding = fastembed.FlagEmbedding || fastembed.default?.FlagEmbedding;
660
- if (FlagEmbedding && EmbeddingModel) {
661
- this.model = await FlagEmbedding.init({
662
- model: EmbeddingModel.BGESmallENV15
663
- });
664
- this.provider = "fastembed";
665
- this.initialized = true;
666
- logger.info("EMBEDDING", "Initialized with fastembed (BGE-small-en-v1.5)");
667
- return true;
988
+ const fastembedCompatible = FASTEMBED_COMPATIBLE_MODELS.has(this.configName);
989
+ if (fastembedCompatible) {
990
+ try {
991
+ const fastembed = await import("fastembed");
992
+ const EmbeddingModel = fastembed.EmbeddingModel || fastembed.default?.EmbeddingModel;
993
+ const FlagEmbedding = fastembed.FlagEmbedding || fastembed.default?.FlagEmbedding;
994
+ if (FlagEmbedding && EmbeddingModel) {
995
+ this.model = await FlagEmbedding.init({
996
+ model: EmbeddingModel.BGESmallENV15
997
+ });
998
+ this.provider = "fastembed";
999
+ this.initialized = true;
1000
+ logger.info("EMBEDDING", `Initialized with fastembed (BGE-small-en-v1.5) for model '${this.configName}'`);
1001
+ return true;
1002
+ }
1003
+ } catch (error) {
1004
+ logger.debug("EMBEDDING", `fastembed not available: ${error}`);
668
1005
  }
669
- } catch (error) {
670
- logger.debug("EMBEDDING", `fastembed not available: ${error}`);
671
1006
  }
672
1007
  try {
673
1008
  const transformers = await import("@huggingface/transformers");
674
1009
  const pipeline = transformers.pipeline || transformers.default?.pipeline;
675
1010
  if (pipeline) {
676
- this.model = await pipeline("feature-extraction", "Xenova/all-MiniLM-L6-v2", {
1011
+ this.model = await pipeline("feature-extraction", this.config.modelId, {
677
1012
  quantized: true
678
1013
  });
679
1014
  this.provider = "transformers";
680
1015
  this.initialized = true;
681
- logger.info("EMBEDDING", "Initialized with @huggingface/transformers (all-MiniLM-L6-v2)");
1016
+ logger.info("EMBEDDING", `Initialized with @huggingface/transformers (${this.config.modelId})`);
682
1017
  return true;
683
1018
  }
684
1019
  } catch (error) {
@@ -691,7 +1026,7 @@ var EmbeddingService = class {
691
1026
  }
692
1027
  /**
693
1028
  * Generate embedding for a single text.
694
- * Returns Float32Array with 384 dimensions, or null if not available.
1029
+ * Returns Float32Array with configured dimensions, or null if not available.
695
1030
  */
696
1031
  async embed(text) {
697
1032
  if (!this.initialized) await this.initialize();
@@ -742,10 +1077,17 @@ var EmbeddingService = class {
742
1077
  return this.provider;
743
1078
  }
744
1079
  /**
745
- * Embedding vector dimensions.
1080
+ * Embedding vector dimensions for the active model configuration.
746
1081
  */
747
1082
  getDimensions() {
748
- return 384;
1083
+ return this.config.dimensions;
1084
+ }
1085
+ /**
1086
+ * Human-readable model name used as identifier in the observation_embeddings table.
1087
+ * Returns the short name (e.g., 'all-MiniLM-L6-v2') or the full HF model ID for custom models.
1088
+ */
1089
+ getModelName() {
1090
+ return this.configName;
749
1091
  }
750
1092
  // --- Batch implementations ---
751
1093
  /**
@@ -964,7 +1306,7 @@ var VectorSearch = class {
964
1306
  `).all(batchSize);
965
1307
  if (rows.length === 0) return 0;
966
1308
  let count = 0;
967
- const model = embeddingService2.getProvider() || "unknown";
1309
+ const model = embeddingService2.getModelName();
968
1310
  for (const row of rows) {
969
1311
  const parts = [row.title];
970
1312
  if (row.text) parts.push(row.text);