kiro-memory 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -7
- package/package.json +3 -5
- package/plugin/dist/cli/contextkit.js +177 -65
- package/plugin/dist/hooks/agentSpawn.js +177 -65
- package/plugin/dist/hooks/kiro-hooks.js +177 -65
- package/plugin/dist/hooks/postToolUse.js +415 -106
- package/plugin/dist/hooks/stop.js +213 -74
- package/plugin/dist/hooks/userPromptSubmit.js +177 -65
- package/plugin/dist/index.js +177 -65
- package/plugin/dist/sdk/index.js +177 -65
- package/plugin/dist/servers/mcp-server.js +28 -0
- package/plugin/dist/services/search/HybridSearch.js +66 -40
- package/plugin/dist/services/search/index.js +66 -40
- package/plugin/dist/services/sqlite/Database.js +23 -0
- package/plugin/dist/services/sqlite/Observations.js +48 -36
- package/plugin/dist/services/sqlite/Search.js +17 -4
- package/plugin/dist/services/sqlite/index.js +88 -40
- package/plugin/dist/viewer.html +5 -0
- package/plugin/dist/viewer.js +529 -250
- package/plugin/dist/worker-service.js +266 -47
package/README.md
CHANGED
|
@@ -11,12 +11,16 @@
|
|
|
11
11
|
<a href="https://www.npmjs.com/package/kiro-memory"><img src="https://img.shields.io/npm/v/kiro-memory" alt="npm" /></a>
|
|
12
12
|
<img src="https://img.shields.io/badge/license-AGPL--3.0-blue" alt="License" />
|
|
13
13
|
<img src="https://img.shields.io/badge/node-%3E%3D18-green" alt="Node" />
|
|
14
|
+
<a href="https://auritidesign.it/docs/kiro-memory/"><img src="https://img.shields.io/badge/docs-auritidesign.it-00b4d8" alt="Docs" /></a>
|
|
14
15
|
</p>
|
|
15
16
|
|
|
16
17
|
---
|
|
17
18
|
|
|
18
19
|
Kiro Memory gives your AI coding assistant memory that persists across sessions. It automatically captures what happened -- files changed, tools used, decisions made -- and feeds relevant context back at the start of the next session. No manual bookkeeping. Your agent picks up exactly where it left off.
|
|
19
20
|
|
|
21
|
+
|
|
22
|
+
**[Read the full documentation →](https://auritidesign.it/docs/kiro-memory/)**
|
|
23
|
+
|
|
20
24
|
Works with **Claude Code** (hooks), **Cursor** (rules + MCP), **Windsurf** (rules + MCP), **Cline** (custom instructions + MCP), and any editor that supports the **Model Context Protocol**.
|
|
21
25
|
|
|
22
26
|
## What Your Agent Sees
|
|
@@ -52,10 +56,13 @@ When a new session starts, Kiro Memory automatically injects previous session co
|
|
|
52
56
|
- **Session Checkpoint & Resume** -- Checkpoint sessions and resume from where you left off
|
|
53
57
|
- **Activity Reports** -- Weekly/monthly digests in text, Markdown, or JSON format
|
|
54
58
|
- **Analytics Dashboard** -- Activity timeline, type distribution, session stats, and file hotspots
|
|
55
|
-
- **Session
|
|
56
|
-
- **
|
|
57
|
-
- **
|
|
58
|
-
- **
|
|
59
|
+
- **Session Tracking** -- Sessions view with stats (total, active, completed, avg duration) and expandable session details
|
|
60
|
+
- **Session Summaries** -- Structured summaries with investigated/completed/learned/next_steps sections
|
|
61
|
+
- **Web Dashboard** -- Real-time viewer at `http://localhost:3001` with dark/light/system theme, hybrid search, project filters, mobile drawer, and live updates via SSE
|
|
62
|
+
- **MCP Server** -- 11 tools exposed via Model Context Protocol
|
|
63
|
+
- **Full-Text Search** -- SQLite FTS5 with weighted BM25 scoring for relevance-ranked results
|
|
64
|
+
- **Data Export** -- Export observations and summaries in JSON or Markdown format
|
|
65
|
+
- **Retention Policy** -- Automatic cleanup of old data with configurable age and dry-run mode
|
|
59
66
|
- **TypeScript SDK** -- Programmatic access to the entire memory system
|
|
60
67
|
- **CLI** -- Query and manage context directly from the terminal
|
|
61
68
|
|
|
@@ -147,6 +154,7 @@ The MCP server exposes 10 tools that your AI assistant can use directly.
|
|
|
147
154
|
| `resume_session` | Get checkpoint data to resume a previous session |
|
|
148
155
|
| `generate_report` | Generate weekly/monthly activity report in Markdown |
|
|
149
156
|
| `get_recent_context` | Get recent memory context for session injection |
|
|
157
|
+
| `save_memory` | Save a structured observation from external tools or scripts |
|
|
150
158
|
|
|
151
159
|
### Storage
|
|
152
160
|
|
|
@@ -282,9 +290,12 @@ kiro-memory decay --days=30
|
|
|
282
290
|
The worker starts automatically when a Kiro session begins (via the `agentSpawn` hook). Once running, open `http://localhost:3001` in your browser to access the web dashboard with:
|
|
283
291
|
|
|
284
292
|
- **Live feed** of observations, summaries, and prompts (via SSE)
|
|
285
|
-
- **
|
|
286
|
-
- **
|
|
287
|
-
- **
|
|
293
|
+
- **Sessions view** with stats cards and expandable session details
|
|
294
|
+
- **Analytics dashboard** with timeline charts and type distribution
|
|
295
|
+
- **Project sidebar** with type filters, stats, and token economics
|
|
296
|
+
- **Spotlight search** (Ctrl+K / Cmd+K) with hybrid search and source badges
|
|
297
|
+
- **Dark/light/system theme** cycling
|
|
298
|
+
- **Mobile-responsive** sidebar drawer
|
|
288
299
|
|
|
289
300
|
For development, you can also manage the worker manually:
|
|
290
301
|
|
|
@@ -443,3 +454,11 @@ Contributions are welcome. Please open an issue to discuss proposed changes befo
|
|
|
443
454
|
---
|
|
444
455
|
|
|
445
456
|
Built by [auriti-web-design](https://github.com/auriti-web-design)
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
<p align="center">
|
|
461
|
+
<a href="https://buymeacoffee.com/auritidesign">
|
|
462
|
+
<img src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me a Coffee" />
|
|
463
|
+
</a>
|
|
464
|
+
</p>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kiro-memory",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Persistent cross-session memory for AI coding assistants. Works with Claude Code, Cursor, Windsurf, Cline, and any MCP-compatible editor.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kiro",
|
|
@@ -94,7 +94,6 @@
|
|
|
94
94
|
"better-sqlite3": "^12.6.2",
|
|
95
95
|
"chromadb": "^3.2.2",
|
|
96
96
|
"class-variance-authority": "^0.7.1",
|
|
97
|
-
"clsx": "^2.1.1",
|
|
98
97
|
"cors": "^2.8.5",
|
|
99
98
|
"dompurify": "^3.3.1",
|
|
100
99
|
"express": "^4.18.2",
|
|
@@ -105,13 +104,12 @@
|
|
|
105
104
|
"lucide-react": "^0.574.0",
|
|
106
105
|
"react": "^18.3.1",
|
|
107
106
|
"react-dom": "^18.3.1",
|
|
108
|
-
"tailwind-merge": "^3.4.1",
|
|
109
107
|
"yaml": "^2.8.2",
|
|
110
108
|
"zod": "^3.23.8"
|
|
111
109
|
},
|
|
112
110
|
"optionalDependencies": {
|
|
113
|
-
"
|
|
114
|
-
"
|
|
111
|
+
"@huggingface/transformers": "^3.8.0",
|
|
112
|
+
"fastembed": "^2.1.0"
|
|
115
113
|
},
|
|
116
114
|
"devDependencies": {
|
|
117
115
|
"@types/better-sqlite3": "^7.6.13",
|
|
@@ -249,19 +249,28 @@ __export(Observations_exports, {
|
|
|
249
249
|
deleteObservation: () => deleteObservation,
|
|
250
250
|
getObservationsByProject: () => getObservationsByProject,
|
|
251
251
|
getObservationsBySession: () => getObservationsBySession,
|
|
252
|
+
isDuplicateObservation: () => isDuplicateObservation,
|
|
252
253
|
searchObservations: () => searchObservations,
|
|
253
254
|
updateLastAccessed: () => updateLastAccessed
|
|
254
255
|
});
|
|
255
256
|
function escapeLikePattern(input) {
|
|
256
257
|
return input.replace(/[%_\\]/g, "\\$&");
|
|
257
258
|
}
|
|
258
|
-
function
|
|
259
|
+
function isDuplicateObservation(db, contentHash, windowMs = 3e4) {
|
|
260
|
+
if (!contentHash) return false;
|
|
261
|
+
const threshold = Date.now() - windowMs;
|
|
262
|
+
const result = db.query(
|
|
263
|
+
"SELECT id FROM observations WHERE content_hash = ? AND created_at_epoch > ? LIMIT 1"
|
|
264
|
+
).get(contentHash, threshold);
|
|
265
|
+
return !!result;
|
|
266
|
+
}
|
|
267
|
+
function createObservation(db, memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, contentHash = null, discoveryTokens = 0) {
|
|
259
268
|
const now = /* @__PURE__ */ new Date();
|
|
260
269
|
const result = db.run(
|
|
261
|
-
`INSERT INTO observations
|
|
262
|
-
(memory_session_id, project, type, title, subtitle, text, narrative, facts, concepts, files_read, files_modified, prompt_number, created_at, created_at_epoch)
|
|
263
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
264
|
-
[memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, now.toISOString(), now.getTime()]
|
|
270
|
+
`INSERT INTO observations
|
|
271
|
+
(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)
|
|
272
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
273
|
+
[memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, now.toISOString(), now.getTime(), contentHash, discoveryTokens]
|
|
265
274
|
);
|
|
266
275
|
return Number(result.lastInsertRowid);
|
|
267
276
|
}
|
|
@@ -317,39 +326,42 @@ function consolidateObservations(db, project, options = {}) {
|
|
|
317
326
|
if (groups.length === 0) return { merged: 0, removed: 0 };
|
|
318
327
|
let totalMerged = 0;
|
|
319
328
|
let totalRemoved = 0;
|
|
320
|
-
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
uniqueTexts.
|
|
329
|
+
const runConsolidation = db.transaction(() => {
|
|
330
|
+
for (const group of groups) {
|
|
331
|
+
const obsIds = group.ids.split(",").map(Number);
|
|
332
|
+
const placeholders = obsIds.map(() => "?").join(",");
|
|
333
|
+
const observations = db.query(
|
|
334
|
+
`SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC`
|
|
335
|
+
).all(...obsIds);
|
|
336
|
+
if (observations.length < minGroupSize) continue;
|
|
337
|
+
if (options.dryRun) {
|
|
338
|
+
totalMerged += 1;
|
|
339
|
+
totalRemoved += observations.length - 1;
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
const keeper = observations[0];
|
|
343
|
+
const others = observations.slice(1);
|
|
344
|
+
const uniqueTexts = /* @__PURE__ */ new Set();
|
|
345
|
+
if (keeper.text) uniqueTexts.add(keeper.text);
|
|
346
|
+
for (const obs of others) {
|
|
347
|
+
if (obs.text && !uniqueTexts.has(obs.text)) {
|
|
348
|
+
uniqueTexts.add(obs.text);
|
|
349
|
+
}
|
|
339
350
|
}
|
|
351
|
+
const consolidatedText = Array.from(uniqueTexts).join("\n---\n").substring(0, 1e5);
|
|
352
|
+
db.run(
|
|
353
|
+
"UPDATE observations SET text = ?, title = ? WHERE id = ?",
|
|
354
|
+
[consolidatedText, `[consolidato x${observations.length}] ${keeper.title}`, keeper.id]
|
|
355
|
+
);
|
|
356
|
+
const removeIds = others.map((o) => o.id);
|
|
357
|
+
const removePlaceholders = removeIds.map(() => "?").join(",");
|
|
358
|
+
db.run(`DELETE FROM observations WHERE id IN (${removePlaceholders})`, removeIds);
|
|
359
|
+
db.run(`DELETE FROM observation_embeddings WHERE observation_id IN (${removePlaceholders})`, removeIds);
|
|
360
|
+
totalMerged += 1;
|
|
361
|
+
totalRemoved += removeIds.length;
|
|
340
362
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
"UPDATE observations SET text = ?, title = ? WHERE id = ?",
|
|
344
|
-
[consolidatedText, `[consolidato x${observations.length}] ${keeper.title}`, keeper.id]
|
|
345
|
-
);
|
|
346
|
-
const removeIds = others.map((o) => o.id);
|
|
347
|
-
const removePlaceholders = removeIds.map(() => "?").join(",");
|
|
348
|
-
db.run(`DELETE FROM observations WHERE id IN (${removePlaceholders})`, removeIds);
|
|
349
|
-
db.run(`DELETE FROM observation_embeddings WHERE observation_id IN (${removePlaceholders})`, removeIds);
|
|
350
|
-
totalMerged += 1;
|
|
351
|
-
totalRemoved += removeIds.length;
|
|
352
|
-
}
|
|
363
|
+
});
|
|
364
|
+
runConsolidation();
|
|
353
365
|
return { merged: totalMerged, removed: totalRemoved };
|
|
354
366
|
}
|
|
355
367
|
var init_Observations = __esm({
|
|
@@ -407,7 +419,7 @@ function searchObservationsFTS(db, query, filters = {}) {
|
|
|
407
419
|
sql += " AND o.created_at_epoch <= ?";
|
|
408
420
|
params.push(filters.dateEnd);
|
|
409
421
|
}
|
|
410
|
-
sql +=
|
|
422
|
+
sql += ` ORDER BY bm25(observations_fts, ${BM25_WEIGHTS}) LIMIT ?`;
|
|
411
423
|
params.push(limit);
|
|
412
424
|
const stmt = db.query(sql);
|
|
413
425
|
return stmt.all(...params);
|
|
@@ -421,7 +433,7 @@ function searchObservationsFTSWithRank(db, query, filters = {}) {
|
|
|
421
433
|
const safeQuery = sanitizeFTS5Query(query);
|
|
422
434
|
if (!safeQuery) return [];
|
|
423
435
|
let sql = `
|
|
424
|
-
SELECT o.*,
|
|
436
|
+
SELECT o.*, bm25(observations_fts, ${BM25_WEIGHTS}) as fts5_rank FROM observations o
|
|
425
437
|
JOIN observations_fts fts ON o.id = fts.rowid
|
|
426
438
|
WHERE observations_fts MATCH ?
|
|
427
439
|
`;
|
|
@@ -442,7 +454,7 @@ function searchObservationsFTSWithRank(db, query, filters = {}) {
|
|
|
442
454
|
sql += " AND o.created_at_epoch <= ?";
|
|
443
455
|
params.push(filters.dateEnd);
|
|
444
456
|
}
|
|
445
|
-
sql +=
|
|
457
|
+
sql += ` ORDER BY bm25(observations_fts, ${BM25_WEIGHTS}) LIMIT ?`;
|
|
446
458
|
params.push(limit);
|
|
447
459
|
const stmt = db.query(sql);
|
|
448
460
|
return stmt.all(...params);
|
|
@@ -546,11 +558,23 @@ function getProjectStats(db, project) {
|
|
|
546
558
|
const sumStmt = db.query("SELECT COUNT(*) as count FROM summaries WHERE project = ?");
|
|
547
559
|
const sesStmt = db.query("SELECT COUNT(*) as count FROM sessions WHERE project = ?");
|
|
548
560
|
const prmStmt = db.query("SELECT COUNT(*) as count FROM prompts WHERE project = ?");
|
|
561
|
+
const discoveryStmt = db.query(
|
|
562
|
+
"SELECT COALESCE(SUM(discovery_tokens), 0) as total FROM observations WHERE project = ?"
|
|
563
|
+
);
|
|
564
|
+
const discoveryTokens = discoveryStmt.get(project)?.total || 0;
|
|
565
|
+
const readStmt = db.query(
|
|
566
|
+
`SELECT COALESCE(SUM(
|
|
567
|
+
CAST((LENGTH(COALESCE(title, '')) + LENGTH(COALESCE(narrative, ''))) / 4 AS INTEGER)
|
|
568
|
+
), 0) as total FROM observations WHERE project = ?`
|
|
569
|
+
);
|
|
570
|
+
const readTokens = readStmt.get(project)?.total || 0;
|
|
571
|
+
const savings = Math.max(0, discoveryTokens - readTokens);
|
|
549
572
|
return {
|
|
550
573
|
observations: obsStmt.get(project)?.count || 0,
|
|
551
574
|
summaries: sumStmt.get(project)?.count || 0,
|
|
552
575
|
sessions: sesStmt.get(project)?.count || 0,
|
|
553
|
-
prompts: prmStmt.get(project)?.count || 0
|
|
576
|
+
prompts: prmStmt.get(project)?.count || 0,
|
|
577
|
+
tokenEconomics: { discoveryTokens, readTokens, savings }
|
|
554
578
|
};
|
|
555
579
|
}
|
|
556
580
|
function getStaleObservations(db, project) {
|
|
@@ -592,9 +616,11 @@ function markObservationsStale(db, ids, stale) {
|
|
|
592
616
|
[stale ? 1 : 0, ...validIds]
|
|
593
617
|
);
|
|
594
618
|
}
|
|
619
|
+
var BM25_WEIGHTS;
|
|
595
620
|
var init_Search = __esm({
|
|
596
621
|
"src/services/sqlite/Search.ts"() {
|
|
597
622
|
"use strict";
|
|
623
|
+
BM25_WEIGHTS = "10.0, 1.0, 5.0, 3.0";
|
|
598
624
|
}
|
|
599
625
|
});
|
|
600
626
|
|
|
@@ -1106,6 +1132,29 @@ var MigrationRunner = class {
|
|
|
1106
1132
|
db.run("CREATE INDEX IF NOT EXISTS idx_checkpoints_project ON checkpoints(project)");
|
|
1107
1133
|
db.run("CREATE INDEX IF NOT EXISTS idx_checkpoints_epoch ON checkpoints(created_at_epoch)");
|
|
1108
1134
|
}
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
version: 7,
|
|
1138
|
+
up: (db) => {
|
|
1139
|
+
db.run("ALTER TABLE observations ADD COLUMN content_hash TEXT");
|
|
1140
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_observations_hash ON observations(content_hash)");
|
|
1141
|
+
}
|
|
1142
|
+
},
|
|
1143
|
+
{
|
|
1144
|
+
version: 8,
|
|
1145
|
+
up: (db) => {
|
|
1146
|
+
db.run("ALTER TABLE observations ADD COLUMN discovery_tokens INTEGER DEFAULT 0");
|
|
1147
|
+
db.run("ALTER TABLE summaries ADD COLUMN discovery_tokens INTEGER DEFAULT 0");
|
|
1148
|
+
}
|
|
1149
|
+
},
|
|
1150
|
+
{
|
|
1151
|
+
version: 9,
|
|
1152
|
+
up: (db) => {
|
|
1153
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_observations_project_epoch ON observations(project, created_at_epoch DESC)");
|
|
1154
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_observations_project_type ON observations(project, type)");
|
|
1155
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_summaries_project_epoch ON summaries(project, created_at_epoch DESC)");
|
|
1156
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_prompts_project_epoch ON prompts(project, created_at_epoch DESC)");
|
|
1157
|
+
}
|
|
1109
1158
|
}
|
|
1110
1159
|
];
|
|
1111
1160
|
}
|
|
@@ -1350,6 +1399,7 @@ init_Search();
|
|
|
1350
1399
|
|
|
1351
1400
|
// src/sdk/index.ts
|
|
1352
1401
|
init_Observations();
|
|
1402
|
+
import { createHash } from "crypto";
|
|
1353
1403
|
init_Search();
|
|
1354
1404
|
|
|
1355
1405
|
// src/services/search/HybridSearch.ts
|
|
@@ -1779,31 +1829,71 @@ var KiroMemorySDK = class {
|
|
|
1779
1829
|
logger.debug("SDK", `Embedding generation fallita per obs ${observationId}: ${error}`);
|
|
1780
1830
|
}
|
|
1781
1831
|
}
|
|
1832
|
+
/**
|
|
1833
|
+
* Genera content hash SHA256 per deduplicazione basata su contenuto.
|
|
1834
|
+
* Usa (project + type + title + narrative) come tupla di identità semantica.
|
|
1835
|
+
* NON include sessionId perché è unico ad ogni invocazione.
|
|
1836
|
+
*/
|
|
1837
|
+
generateContentHash(type, title, narrative) {
|
|
1838
|
+
const payload = `${this.project}|${type}|${title}|${narrative || ""}`;
|
|
1839
|
+
return createHash("sha256").update(payload).digest("hex");
|
|
1840
|
+
}
|
|
1841
|
+
/**
|
|
1842
|
+
* Finestre di deduplicazione per tipo (ms).
|
|
1843
|
+
* Tipi con molte ripetizioni hanno finestre più ampie.
|
|
1844
|
+
*/
|
|
1845
|
+
getDeduplicationWindow(type) {
|
|
1846
|
+
switch (type) {
|
|
1847
|
+
case "file-read":
|
|
1848
|
+
return 6e4;
|
|
1849
|
+
// 60s — letture frequenti sugli stessi file
|
|
1850
|
+
case "file-write":
|
|
1851
|
+
return 1e4;
|
|
1852
|
+
// 10s — scritture rapide consecutive
|
|
1853
|
+
case "command":
|
|
1854
|
+
return 3e4;
|
|
1855
|
+
// 30s — standard
|
|
1856
|
+
case "research":
|
|
1857
|
+
return 12e4;
|
|
1858
|
+
// 120s — web search e fetch ripetuti
|
|
1859
|
+
case "delegation":
|
|
1860
|
+
return 6e4;
|
|
1861
|
+
// 60s — delegazioni rapide
|
|
1862
|
+
default:
|
|
1863
|
+
return 3e4;
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1782
1866
|
/**
|
|
1783
1867
|
* Store a new observation
|
|
1784
1868
|
*/
|
|
1785
1869
|
async storeObservation(data) {
|
|
1786
1870
|
this.validateObservationInput(data);
|
|
1871
|
+
const sessionId = "sdk-" + Date.now();
|
|
1872
|
+
const contentHash = this.generateContentHash(data.type, data.title, data.narrative);
|
|
1873
|
+
const dedupWindow = this.getDeduplicationWindow(data.type);
|
|
1874
|
+
if (isDuplicateObservation(this.db.db, contentHash, dedupWindow)) {
|
|
1875
|
+
logger.debug("SDK", `Osservazione duplicata scartata (${data.type}, ${dedupWindow}ms): ${data.title}`);
|
|
1876
|
+
return -1;
|
|
1877
|
+
}
|
|
1878
|
+
const filesRead = data.filesRead || (data.type === "file-read" ? data.files : void 0);
|
|
1879
|
+
const filesModified = data.filesModified || (data.type === "file-write" ? data.files : void 0);
|
|
1880
|
+
const discoveryTokens = Math.ceil(data.content.length / 4);
|
|
1787
1881
|
const observationId = createObservation(
|
|
1788
1882
|
this.db.db,
|
|
1789
|
-
|
|
1883
|
+
sessionId,
|
|
1790
1884
|
this.project,
|
|
1791
1885
|
data.type,
|
|
1792
1886
|
data.title,
|
|
1793
|
-
null,
|
|
1794
|
-
// subtitle
|
|
1887
|
+
data.subtitle || null,
|
|
1795
1888
|
data.content,
|
|
1796
|
-
null,
|
|
1797
|
-
|
|
1798
|
-
null,
|
|
1799
|
-
// facts
|
|
1889
|
+
data.narrative || null,
|
|
1890
|
+
data.facts || null,
|
|
1800
1891
|
data.concepts?.join(", ") || null,
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
// prompt_number
|
|
1892
|
+
filesRead?.join(", ") || null,
|
|
1893
|
+
filesModified?.join(", ") || null,
|
|
1894
|
+
0,
|
|
1895
|
+
contentHash,
|
|
1896
|
+
discoveryTokens
|
|
1807
1897
|
);
|
|
1808
1898
|
this.generateEmbeddingAsync(observationId, data.title, data.content, data.concepts).catch(() => {
|
|
1809
1899
|
});
|
|
@@ -1846,9 +1936,16 @@ var KiroMemorySDK = class {
|
|
|
1846
1936
|
};
|
|
1847
1937
|
}
|
|
1848
1938
|
})();
|
|
1939
|
+
const sessionId = "sdk-" + Date.now();
|
|
1940
|
+
const contentHash = this.generateContentHash(data.type, data.title);
|
|
1941
|
+
if (isDuplicateObservation(this.db.db, contentHash)) {
|
|
1942
|
+
logger.debug("SDK", `Knowledge duplicata scartata: ${data.title}`);
|
|
1943
|
+
return -1;
|
|
1944
|
+
}
|
|
1945
|
+
const discoveryTokens = Math.ceil(data.content.length / 4);
|
|
1849
1946
|
const observationId = createObservation(
|
|
1850
1947
|
this.db.db,
|
|
1851
|
-
|
|
1948
|
+
sessionId,
|
|
1852
1949
|
data.project || this.project,
|
|
1853
1950
|
data.knowledgeType,
|
|
1854
1951
|
// type = knowledgeType
|
|
@@ -1862,9 +1959,12 @@ var KiroMemorySDK = class {
|
|
|
1862
1959
|
// facts = metadati JSON
|
|
1863
1960
|
data.concepts?.join(", ") || null,
|
|
1864
1961
|
data.files?.join(", ") || null,
|
|
1865
|
-
|
|
1866
|
-
|
|
1962
|
+
null,
|
|
1963
|
+
// filesModified: knowledge non modifica file
|
|
1964
|
+
0,
|
|
1867
1965
|
// prompt_number
|
|
1966
|
+
contentHash,
|
|
1967
|
+
discoveryTokens
|
|
1868
1968
|
);
|
|
1869
1969
|
this.generateEmbeddingAsync(observationId, data.title, data.content, data.concepts).catch(() => {
|
|
1870
1970
|
});
|
|
@@ -1880,11 +1980,11 @@ var KiroMemorySDK = class {
|
|
|
1880
1980
|
"sdk-" + Date.now(),
|
|
1881
1981
|
this.project,
|
|
1882
1982
|
data.request || null,
|
|
1883
|
-
null,
|
|
1983
|
+
data.investigated || null,
|
|
1884
1984
|
data.learned || null,
|
|
1885
1985
|
data.completed || null,
|
|
1886
1986
|
data.nextSteps || null,
|
|
1887
|
-
null
|
|
1987
|
+
data.notes || null
|
|
1888
1988
|
);
|
|
1889
1989
|
}
|
|
1890
1990
|
/**
|
|
@@ -2068,7 +2168,14 @@ var KiroMemorySDK = class {
|
|
|
2068
2168
|
}));
|
|
2069
2169
|
} else {
|
|
2070
2170
|
const observations = getObservationsByProject(this.db.db, this.project, 30);
|
|
2071
|
-
|
|
2171
|
+
const knowledgeTypes = new Set(KNOWLEDGE_TYPES);
|
|
2172
|
+
const knowledgeObs = [];
|
|
2173
|
+
const normalObs = [];
|
|
2174
|
+
for (const obs of observations) {
|
|
2175
|
+
if (knowledgeTypes.has(obs.type)) knowledgeObs.push(obs);
|
|
2176
|
+
else normalObs.push(obs);
|
|
2177
|
+
}
|
|
2178
|
+
const scoreObs = (obs) => {
|
|
2072
2179
|
const signals = {
|
|
2073
2180
|
semantic: 0,
|
|
2074
2181
|
fts5: 0,
|
|
@@ -2076,7 +2183,6 @@ var KiroMemorySDK = class {
|
|
|
2076
2183
|
projectMatch: projectMatchScore(obs.project, this.project)
|
|
2077
2184
|
};
|
|
2078
2185
|
const baseScore = computeCompositeScore(signals, CONTEXT_WEIGHTS);
|
|
2079
|
-
const boostedScore = Math.min(1, baseScore * knowledgeTypeBoost(obs.type));
|
|
2080
2186
|
return {
|
|
2081
2187
|
id: obs.id,
|
|
2082
2188
|
title: obs.title,
|
|
@@ -2085,17 +2191,23 @@ var KiroMemorySDK = class {
|
|
|
2085
2191
|
project: obs.project,
|
|
2086
2192
|
created_at: obs.created_at,
|
|
2087
2193
|
created_at_epoch: obs.created_at_epoch,
|
|
2088
|
-
score:
|
|
2194
|
+
score: Math.min(1, baseScore * knowledgeTypeBoost(obs.type)),
|
|
2089
2195
|
signals
|
|
2090
2196
|
};
|
|
2091
|
-
}
|
|
2092
|
-
|
|
2197
|
+
};
|
|
2198
|
+
const scoredKnowledge = knowledgeObs.map(scoreObs).sort((a, b) => b.score - a.score);
|
|
2199
|
+
const scoredNormal = normalObs.map(scoreObs).sort((a, b) => b.score - a.score);
|
|
2200
|
+
items = [...scoredKnowledge, ...scoredNormal];
|
|
2093
2201
|
}
|
|
2094
2202
|
let tokensUsed = 0;
|
|
2203
|
+
const budgetItems = [];
|
|
2095
2204
|
for (const item of items) {
|
|
2096
|
-
|
|
2097
|
-
if (tokensUsed > tokenBudget) break;
|
|
2205
|
+
const itemTokens = Math.ceil((item.title.length + item.content.length) / 4);
|
|
2206
|
+
if (tokensUsed + itemTokens > tokenBudget) break;
|
|
2207
|
+
tokensUsed += itemTokens;
|
|
2208
|
+
budgetItems.push(item);
|
|
2098
2209
|
}
|
|
2210
|
+
items = budgetItems;
|
|
2099
2211
|
return {
|
|
2100
2212
|
project: this.project,
|
|
2101
2213
|
items,
|