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.
- package/README.md +5 -1
- package/package.json +3 -3
- package/plugin/dist/cli/contextkit.js +2342 -183
- package/plugin/dist/hooks/agentSpawn.js +575 -52
- package/plugin/dist/hooks/kiro-hooks.js +575 -52
- package/plugin/dist/hooks/postToolUse.js +583 -59
- package/plugin/dist/hooks/stop.js +575 -52
- package/plugin/dist/hooks/userPromptSubmit.js +578 -53
- package/plugin/dist/index.js +576 -53
- package/plugin/dist/plugins/github/github-client.js +152 -0
- package/plugin/dist/plugins/github/index.js +412 -0
- package/plugin/dist/plugins/github/issue-parser.js +54 -0
- package/plugin/dist/plugins/slack/formatter.js +90 -0
- package/plugin/dist/plugins/slack/index.js +215 -0
- package/plugin/dist/sdk/index.js +575 -52
- package/plugin/dist/servers/mcp-server.js +4461 -397
- package/plugin/dist/services/search/EmbeddingService.js +64 -20
- package/plugin/dist/services/search/HybridSearch.js +380 -38
- package/plugin/dist/services/search/VectorSearch.js +65 -21
- package/plugin/dist/services/search/index.js +380 -38
- package/plugin/dist/services/sqlite/Backup.js +416 -0
- package/plugin/dist/services/sqlite/Database.js +98 -3
- package/plugin/dist/services/sqlite/ImportExport.js +452 -0
- package/plugin/dist/services/sqlite/Observations.js +291 -7
- package/plugin/dist/services/sqlite/Prompts.js +1 -1
- package/plugin/dist/services/sqlite/Search.js +10 -10
- package/plugin/dist/services/sqlite/Summaries.js +4 -4
- package/plugin/dist/services/sqlite/index.js +1350 -31
- package/plugin/dist/viewer.css +1 -1
- package/plugin/dist/viewer.js +16 -8
- package/plugin/dist/viewer.js.map +4 -4
- package/plugin/dist/worker-service.js +326 -75
- 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,
|
|
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
|
|
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
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
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",
|
|
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",
|
|
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
|
|
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
|
|
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.
|
|
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);
|