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
|
@@ -24,19 +24,28 @@ __export(Observations_exports, {
|
|
|
24
24
|
deleteObservation: () => deleteObservation,
|
|
25
25
|
getObservationsByProject: () => getObservationsByProject,
|
|
26
26
|
getObservationsBySession: () => getObservationsBySession,
|
|
27
|
+
isDuplicateObservation: () => isDuplicateObservation,
|
|
27
28
|
searchObservations: () => searchObservations,
|
|
28
29
|
updateLastAccessed: () => updateLastAccessed
|
|
29
30
|
});
|
|
30
31
|
function escapeLikePattern(input) {
|
|
31
32
|
return input.replace(/[%_\\]/g, "\\$&");
|
|
32
33
|
}
|
|
33
|
-
function
|
|
34
|
+
function isDuplicateObservation(db, contentHash, windowMs = 3e4) {
|
|
35
|
+
if (!contentHash) return false;
|
|
36
|
+
const threshold = Date.now() - windowMs;
|
|
37
|
+
const result = db.query(
|
|
38
|
+
"SELECT id FROM observations WHERE content_hash = ? AND created_at_epoch > ? LIMIT 1"
|
|
39
|
+
).get(contentHash, threshold);
|
|
40
|
+
return !!result;
|
|
41
|
+
}
|
|
42
|
+
function createObservation(db, memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, contentHash = null, discoveryTokens = 0) {
|
|
34
43
|
const now = /* @__PURE__ */ new Date();
|
|
35
44
|
const result = db.run(
|
|
36
|
-
`INSERT INTO observations
|
|
37
|
-
(memory_session_id, project, type, title, subtitle, text, narrative, facts, concepts, files_read, files_modified, prompt_number, created_at, created_at_epoch)
|
|
38
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
39
|
-
[memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, now.toISOString(), now.getTime()]
|
|
45
|
+
`INSERT INTO observations
|
|
46
|
+
(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)
|
|
47
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
48
|
+
[memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, now.toISOString(), now.getTime(), contentHash, discoveryTokens]
|
|
40
49
|
);
|
|
41
50
|
return Number(result.lastInsertRowid);
|
|
42
51
|
}
|
|
@@ -92,39 +101,42 @@ function consolidateObservations(db, project, options = {}) {
|
|
|
92
101
|
if (groups.length === 0) return { merged: 0, removed: 0 };
|
|
93
102
|
let totalMerged = 0;
|
|
94
103
|
let totalRemoved = 0;
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const keeper = observations[0];
|
|
108
|
-
const others = observations.slice(1);
|
|
109
|
-
const uniqueTexts = /* @__PURE__ */ new Set();
|
|
110
|
-
if (keeper.text) uniqueTexts.add(keeper.text);
|
|
111
|
-
for (const obs of others) {
|
|
112
|
-
if (obs.text && !uniqueTexts.has(obs.text)) {
|
|
113
|
-
uniqueTexts.add(obs.text);
|
|
104
|
+
const runConsolidation = db.transaction(() => {
|
|
105
|
+
for (const group of groups) {
|
|
106
|
+
const obsIds = group.ids.split(",").map(Number);
|
|
107
|
+
const placeholders = obsIds.map(() => "?").join(",");
|
|
108
|
+
const observations = db.query(
|
|
109
|
+
`SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC`
|
|
110
|
+
).all(...obsIds);
|
|
111
|
+
if (observations.length < minGroupSize) continue;
|
|
112
|
+
if (options.dryRun) {
|
|
113
|
+
totalMerged += 1;
|
|
114
|
+
totalRemoved += observations.length - 1;
|
|
115
|
+
continue;
|
|
114
116
|
}
|
|
117
|
+
const keeper = observations[0];
|
|
118
|
+
const others = observations.slice(1);
|
|
119
|
+
const uniqueTexts = /* @__PURE__ */ new Set();
|
|
120
|
+
if (keeper.text) uniqueTexts.add(keeper.text);
|
|
121
|
+
for (const obs of others) {
|
|
122
|
+
if (obs.text && !uniqueTexts.has(obs.text)) {
|
|
123
|
+
uniqueTexts.add(obs.text);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const consolidatedText = Array.from(uniqueTexts).join("\n---\n").substring(0, 1e5);
|
|
127
|
+
db.run(
|
|
128
|
+
"UPDATE observations SET text = ?, title = ? WHERE id = ?",
|
|
129
|
+
[consolidatedText, `[consolidato x${observations.length}] ${keeper.title}`, keeper.id]
|
|
130
|
+
);
|
|
131
|
+
const removeIds = others.map((o) => o.id);
|
|
132
|
+
const removePlaceholders = removeIds.map(() => "?").join(",");
|
|
133
|
+
db.run(`DELETE FROM observations WHERE id IN (${removePlaceholders})`, removeIds);
|
|
134
|
+
db.run(`DELETE FROM observation_embeddings WHERE observation_id IN (${removePlaceholders})`, removeIds);
|
|
135
|
+
totalMerged += 1;
|
|
136
|
+
totalRemoved += removeIds.length;
|
|
115
137
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
"UPDATE observations SET text = ?, title = ? WHERE id = ?",
|
|
119
|
-
[consolidatedText, `[consolidato x${observations.length}] ${keeper.title}`, keeper.id]
|
|
120
|
-
);
|
|
121
|
-
const removeIds = others.map((o) => o.id);
|
|
122
|
-
const removePlaceholders = removeIds.map(() => "?").join(",");
|
|
123
|
-
db.run(`DELETE FROM observations WHERE id IN (${removePlaceholders})`, removeIds);
|
|
124
|
-
db.run(`DELETE FROM observation_embeddings WHERE observation_id IN (${removePlaceholders})`, removeIds);
|
|
125
|
-
totalMerged += 1;
|
|
126
|
-
totalRemoved += removeIds.length;
|
|
127
|
-
}
|
|
138
|
+
});
|
|
139
|
+
runConsolidation();
|
|
128
140
|
return { merged: totalMerged, removed: totalRemoved };
|
|
129
141
|
}
|
|
130
142
|
var init_Observations = __esm({
|
|
@@ -182,7 +194,7 @@ function searchObservationsFTS(db, query, filters = {}) {
|
|
|
182
194
|
sql += " AND o.created_at_epoch <= ?";
|
|
183
195
|
params.push(filters.dateEnd);
|
|
184
196
|
}
|
|
185
|
-
sql +=
|
|
197
|
+
sql += ` ORDER BY bm25(observations_fts, ${BM25_WEIGHTS}) LIMIT ?`;
|
|
186
198
|
params.push(limit);
|
|
187
199
|
const stmt = db.query(sql);
|
|
188
200
|
return stmt.all(...params);
|
|
@@ -196,7 +208,7 @@ function searchObservationsFTSWithRank(db, query, filters = {}) {
|
|
|
196
208
|
const safeQuery = sanitizeFTS5Query(query);
|
|
197
209
|
if (!safeQuery) return [];
|
|
198
210
|
let sql = `
|
|
199
|
-
SELECT o.*,
|
|
211
|
+
SELECT o.*, bm25(observations_fts, ${BM25_WEIGHTS}) as fts5_rank FROM observations o
|
|
200
212
|
JOIN observations_fts fts ON o.id = fts.rowid
|
|
201
213
|
WHERE observations_fts MATCH ?
|
|
202
214
|
`;
|
|
@@ -217,7 +229,7 @@ function searchObservationsFTSWithRank(db, query, filters = {}) {
|
|
|
217
229
|
sql += " AND o.created_at_epoch <= ?";
|
|
218
230
|
params.push(filters.dateEnd);
|
|
219
231
|
}
|
|
220
|
-
sql +=
|
|
232
|
+
sql += ` ORDER BY bm25(observations_fts, ${BM25_WEIGHTS}) LIMIT ?`;
|
|
221
233
|
params.push(limit);
|
|
222
234
|
const stmt = db.query(sql);
|
|
223
235
|
return stmt.all(...params);
|
|
@@ -321,11 +333,23 @@ function getProjectStats(db, project) {
|
|
|
321
333
|
const sumStmt = db.query("SELECT COUNT(*) as count FROM summaries WHERE project = ?");
|
|
322
334
|
const sesStmt = db.query("SELECT COUNT(*) as count FROM sessions WHERE project = ?");
|
|
323
335
|
const prmStmt = db.query("SELECT COUNT(*) as count FROM prompts WHERE project = ?");
|
|
336
|
+
const discoveryStmt = db.query(
|
|
337
|
+
"SELECT COALESCE(SUM(discovery_tokens), 0) as total FROM observations WHERE project = ?"
|
|
338
|
+
);
|
|
339
|
+
const discoveryTokens = discoveryStmt.get(project)?.total || 0;
|
|
340
|
+
const readStmt = db.query(
|
|
341
|
+
`SELECT COALESCE(SUM(
|
|
342
|
+
CAST((LENGTH(COALESCE(title, '')) + LENGTH(COALESCE(narrative, ''))) / 4 AS INTEGER)
|
|
343
|
+
), 0) as total FROM observations WHERE project = ?`
|
|
344
|
+
);
|
|
345
|
+
const readTokens = readStmt.get(project)?.total || 0;
|
|
346
|
+
const savings = Math.max(0, discoveryTokens - readTokens);
|
|
324
347
|
return {
|
|
325
348
|
observations: obsStmt.get(project)?.count || 0,
|
|
326
349
|
summaries: sumStmt.get(project)?.count || 0,
|
|
327
350
|
sessions: sesStmt.get(project)?.count || 0,
|
|
328
|
-
prompts: prmStmt.get(project)?.count || 0
|
|
351
|
+
prompts: prmStmt.get(project)?.count || 0,
|
|
352
|
+
tokenEconomics: { discoveryTokens, readTokens, savings }
|
|
329
353
|
};
|
|
330
354
|
}
|
|
331
355
|
function getStaleObservations(db, project) {
|
|
@@ -367,9 +391,11 @@ function markObservationsStale(db, ids, stale) {
|
|
|
367
391
|
[stale ? 1 : 0, ...validIds]
|
|
368
392
|
);
|
|
369
393
|
}
|
|
394
|
+
var BM25_WEIGHTS;
|
|
370
395
|
var init_Search = __esm({
|
|
371
396
|
"src/services/sqlite/Search.ts"() {
|
|
372
397
|
"use strict";
|
|
398
|
+
BM25_WEIGHTS = "10.0, 1.0, 5.0, 3.0";
|
|
373
399
|
}
|
|
374
400
|
});
|
|
375
401
|
|
|
@@ -1111,6 +1137,29 @@ var MigrationRunner = class {
|
|
|
1111
1137
|
db.run("CREATE INDEX IF NOT EXISTS idx_checkpoints_project ON checkpoints(project)");
|
|
1112
1138
|
db.run("CREATE INDEX IF NOT EXISTS idx_checkpoints_epoch ON checkpoints(created_at_epoch)");
|
|
1113
1139
|
}
|
|
1140
|
+
},
|
|
1141
|
+
{
|
|
1142
|
+
version: 7,
|
|
1143
|
+
up: (db) => {
|
|
1144
|
+
db.run("ALTER TABLE observations ADD COLUMN content_hash TEXT");
|
|
1145
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_observations_hash ON observations(content_hash)");
|
|
1146
|
+
}
|
|
1147
|
+
},
|
|
1148
|
+
{
|
|
1149
|
+
version: 8,
|
|
1150
|
+
up: (db) => {
|
|
1151
|
+
db.run("ALTER TABLE observations ADD COLUMN discovery_tokens INTEGER DEFAULT 0");
|
|
1152
|
+
db.run("ALTER TABLE summaries ADD COLUMN discovery_tokens INTEGER DEFAULT 0");
|
|
1153
|
+
}
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
version: 9,
|
|
1157
|
+
up: (db) => {
|
|
1158
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_observations_project_epoch ON observations(project, created_at_epoch DESC)");
|
|
1159
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_observations_project_type ON observations(project, type)");
|
|
1160
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_summaries_project_epoch ON summaries(project, created_at_epoch DESC)");
|
|
1161
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_prompts_project_epoch ON prompts(project, created_at_epoch DESC)");
|
|
1162
|
+
}
|
|
1114
1163
|
}
|
|
1115
1164
|
];
|
|
1116
1165
|
}
|
|
@@ -1355,6 +1404,7 @@ init_Search();
|
|
|
1355
1404
|
|
|
1356
1405
|
// src/sdk/index.ts
|
|
1357
1406
|
init_Observations();
|
|
1407
|
+
import { createHash } from "crypto";
|
|
1358
1408
|
init_Search();
|
|
1359
1409
|
|
|
1360
1410
|
// src/services/search/EmbeddingService.ts
|
|
@@ -1867,31 +1917,71 @@ var KiroMemorySDK = class {
|
|
|
1867
1917
|
logger.debug("SDK", `Embedding generation fallita per obs ${observationId}: ${error}`);
|
|
1868
1918
|
}
|
|
1869
1919
|
}
|
|
1920
|
+
/**
|
|
1921
|
+
* Genera content hash SHA256 per deduplicazione basata su contenuto.
|
|
1922
|
+
* Usa (project + type + title + narrative) come tupla di identità semantica.
|
|
1923
|
+
* NON include sessionId perché è unico ad ogni invocazione.
|
|
1924
|
+
*/
|
|
1925
|
+
generateContentHash(type, title, narrative) {
|
|
1926
|
+
const payload = `${this.project}|${type}|${title}|${narrative || ""}`;
|
|
1927
|
+
return createHash("sha256").update(payload).digest("hex");
|
|
1928
|
+
}
|
|
1929
|
+
/**
|
|
1930
|
+
* Finestre di deduplicazione per tipo (ms).
|
|
1931
|
+
* Tipi con molte ripetizioni hanno finestre più ampie.
|
|
1932
|
+
*/
|
|
1933
|
+
getDeduplicationWindow(type) {
|
|
1934
|
+
switch (type) {
|
|
1935
|
+
case "file-read":
|
|
1936
|
+
return 6e4;
|
|
1937
|
+
// 60s — letture frequenti sugli stessi file
|
|
1938
|
+
case "file-write":
|
|
1939
|
+
return 1e4;
|
|
1940
|
+
// 10s — scritture rapide consecutive
|
|
1941
|
+
case "command":
|
|
1942
|
+
return 3e4;
|
|
1943
|
+
// 30s — standard
|
|
1944
|
+
case "research":
|
|
1945
|
+
return 12e4;
|
|
1946
|
+
// 120s — web search e fetch ripetuti
|
|
1947
|
+
case "delegation":
|
|
1948
|
+
return 6e4;
|
|
1949
|
+
// 60s — delegazioni rapide
|
|
1950
|
+
default:
|
|
1951
|
+
return 3e4;
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1870
1954
|
/**
|
|
1871
1955
|
* Store a new observation
|
|
1872
1956
|
*/
|
|
1873
1957
|
async storeObservation(data) {
|
|
1874
1958
|
this.validateObservationInput(data);
|
|
1959
|
+
const sessionId = "sdk-" + Date.now();
|
|
1960
|
+
const contentHash = this.generateContentHash(data.type, data.title, data.narrative);
|
|
1961
|
+
const dedupWindow = this.getDeduplicationWindow(data.type);
|
|
1962
|
+
if (isDuplicateObservation(this.db.db, contentHash, dedupWindow)) {
|
|
1963
|
+
logger.debug("SDK", `Osservazione duplicata scartata (${data.type}, ${dedupWindow}ms): ${data.title}`);
|
|
1964
|
+
return -1;
|
|
1965
|
+
}
|
|
1966
|
+
const filesRead = data.filesRead || (data.type === "file-read" ? data.files : void 0);
|
|
1967
|
+
const filesModified = data.filesModified || (data.type === "file-write" ? data.files : void 0);
|
|
1968
|
+
const discoveryTokens = Math.ceil(data.content.length / 4);
|
|
1875
1969
|
const observationId = createObservation(
|
|
1876
1970
|
this.db.db,
|
|
1877
|
-
|
|
1971
|
+
sessionId,
|
|
1878
1972
|
this.project,
|
|
1879
1973
|
data.type,
|
|
1880
1974
|
data.title,
|
|
1881
|
-
null,
|
|
1882
|
-
// subtitle
|
|
1975
|
+
data.subtitle || null,
|
|
1883
1976
|
data.content,
|
|
1884
|
-
null,
|
|
1885
|
-
|
|
1886
|
-
null,
|
|
1887
|
-
// facts
|
|
1977
|
+
data.narrative || null,
|
|
1978
|
+
data.facts || null,
|
|
1888
1979
|
data.concepts?.join(", ") || null,
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
// prompt_number
|
|
1980
|
+
filesRead?.join(", ") || null,
|
|
1981
|
+
filesModified?.join(", ") || null,
|
|
1982
|
+
0,
|
|
1983
|
+
contentHash,
|
|
1984
|
+
discoveryTokens
|
|
1895
1985
|
);
|
|
1896
1986
|
this.generateEmbeddingAsync(observationId, data.title, data.content, data.concepts).catch(() => {
|
|
1897
1987
|
});
|
|
@@ -1934,9 +2024,16 @@ var KiroMemorySDK = class {
|
|
|
1934
2024
|
};
|
|
1935
2025
|
}
|
|
1936
2026
|
})();
|
|
2027
|
+
const sessionId = "sdk-" + Date.now();
|
|
2028
|
+
const contentHash = this.generateContentHash(data.type, data.title);
|
|
2029
|
+
if (isDuplicateObservation(this.db.db, contentHash)) {
|
|
2030
|
+
logger.debug("SDK", `Knowledge duplicata scartata: ${data.title}`);
|
|
2031
|
+
return -1;
|
|
2032
|
+
}
|
|
2033
|
+
const discoveryTokens = Math.ceil(data.content.length / 4);
|
|
1937
2034
|
const observationId = createObservation(
|
|
1938
2035
|
this.db.db,
|
|
1939
|
-
|
|
2036
|
+
sessionId,
|
|
1940
2037
|
data.project || this.project,
|
|
1941
2038
|
data.knowledgeType,
|
|
1942
2039
|
// type = knowledgeType
|
|
@@ -1950,9 +2047,12 @@ var KiroMemorySDK = class {
|
|
|
1950
2047
|
// facts = metadati JSON
|
|
1951
2048
|
data.concepts?.join(", ") || null,
|
|
1952
2049
|
data.files?.join(", ") || null,
|
|
1953
|
-
|
|
1954
|
-
|
|
2050
|
+
null,
|
|
2051
|
+
// filesModified: knowledge non modifica file
|
|
2052
|
+
0,
|
|
1955
2053
|
// prompt_number
|
|
2054
|
+
contentHash,
|
|
2055
|
+
discoveryTokens
|
|
1956
2056
|
);
|
|
1957
2057
|
this.generateEmbeddingAsync(observationId, data.title, data.content, data.concepts).catch(() => {
|
|
1958
2058
|
});
|
|
@@ -1968,11 +2068,11 @@ var KiroMemorySDK = class {
|
|
|
1968
2068
|
"sdk-" + Date.now(),
|
|
1969
2069
|
this.project,
|
|
1970
2070
|
data.request || null,
|
|
1971
|
-
null,
|
|
2071
|
+
data.investigated || null,
|
|
1972
2072
|
data.learned || null,
|
|
1973
2073
|
data.completed || null,
|
|
1974
2074
|
data.nextSteps || null,
|
|
1975
|
-
null
|
|
2075
|
+
data.notes || null
|
|
1976
2076
|
);
|
|
1977
2077
|
}
|
|
1978
2078
|
/**
|
|
@@ -2156,7 +2256,14 @@ var KiroMemorySDK = class {
|
|
|
2156
2256
|
}));
|
|
2157
2257
|
} else {
|
|
2158
2258
|
const observations = getObservationsByProject(this.db.db, this.project, 30);
|
|
2159
|
-
|
|
2259
|
+
const knowledgeTypes = new Set(KNOWLEDGE_TYPES);
|
|
2260
|
+
const knowledgeObs = [];
|
|
2261
|
+
const normalObs = [];
|
|
2262
|
+
for (const obs of observations) {
|
|
2263
|
+
if (knowledgeTypes.has(obs.type)) knowledgeObs.push(obs);
|
|
2264
|
+
else normalObs.push(obs);
|
|
2265
|
+
}
|
|
2266
|
+
const scoreObs = (obs) => {
|
|
2160
2267
|
const signals = {
|
|
2161
2268
|
semantic: 0,
|
|
2162
2269
|
fts5: 0,
|
|
@@ -2164,7 +2271,6 @@ var KiroMemorySDK = class {
|
|
|
2164
2271
|
projectMatch: projectMatchScore(obs.project, this.project)
|
|
2165
2272
|
};
|
|
2166
2273
|
const baseScore = computeCompositeScore(signals, CONTEXT_WEIGHTS);
|
|
2167
|
-
const boostedScore = Math.min(1, baseScore * knowledgeTypeBoost(obs.type));
|
|
2168
2274
|
return {
|
|
2169
2275
|
id: obs.id,
|
|
2170
2276
|
title: obs.title,
|
|
@@ -2173,17 +2279,23 @@ var KiroMemorySDK = class {
|
|
|
2173
2279
|
project: obs.project,
|
|
2174
2280
|
created_at: obs.created_at,
|
|
2175
2281
|
created_at_epoch: obs.created_at_epoch,
|
|
2176
|
-
score:
|
|
2282
|
+
score: Math.min(1, baseScore * knowledgeTypeBoost(obs.type)),
|
|
2177
2283
|
signals
|
|
2178
2284
|
};
|
|
2179
|
-
}
|
|
2180
|
-
|
|
2285
|
+
};
|
|
2286
|
+
const scoredKnowledge = knowledgeObs.map(scoreObs).sort((a, b) => b.score - a.score);
|
|
2287
|
+
const scoredNormal = normalObs.map(scoreObs).sort((a, b) => b.score - a.score);
|
|
2288
|
+
items = [...scoredKnowledge, ...scoredNormal];
|
|
2181
2289
|
}
|
|
2182
2290
|
let tokensUsed = 0;
|
|
2291
|
+
const budgetItems = [];
|
|
2183
2292
|
for (const item of items) {
|
|
2184
|
-
|
|
2185
|
-
if (tokensUsed > tokenBudget) break;
|
|
2293
|
+
const itemTokens = Math.ceil((item.title.length + item.content.length) / 4);
|
|
2294
|
+
if (tokensUsed + itemTokens > tokenBudget) break;
|
|
2295
|
+
tokensUsed += itemTokens;
|
|
2296
|
+
budgetItems.push(item);
|
|
2186
2297
|
}
|
|
2298
|
+
items = budgetItems;
|
|
2187
2299
|
return {
|
|
2188
2300
|
project: this.project,
|
|
2189
2301
|
items,
|
|
@@ -2306,28 +2418,55 @@ runHook("stop", async (input) => {
|
|
|
2306
2418
|
const fourHoursAgo = Date.now() - 4 * 60 * 60 * 1e3;
|
|
2307
2419
|
const sessionObs = sessionId ? recentObs.filter((o) => o.memory_session_id === sessionId) : recentObs.filter((o) => o.created_at_epoch > fourHoursAgo);
|
|
2308
2420
|
if (sessionObs.length === 0) return;
|
|
2309
|
-
const
|
|
2421
|
+
const byType = /* @__PURE__ */ new Map();
|
|
2422
|
+
for (const obs of sessionObs) {
|
|
2423
|
+
const group = byType.get(obs.type) || [];
|
|
2424
|
+
group.push(obs);
|
|
2425
|
+
byType.set(obs.type, group);
|
|
2426
|
+
}
|
|
2427
|
+
const readFiles = byType.get("file-read") || [];
|
|
2428
|
+
const researched = byType.get("research") || [];
|
|
2429
|
+
const investigated = [
|
|
2430
|
+
...readFiles.slice(0, 5).map((o) => o.narrative || o.title),
|
|
2431
|
+
...researched.slice(0, 3).map((o) => o.narrative || o.title)
|
|
2432
|
+
].filter(Boolean).join("; ") || void 0;
|
|
2433
|
+
const writes = byType.get("file-write") || [];
|
|
2434
|
+
const commands = byType.get("command") || [];
|
|
2435
|
+
const completed = [
|
|
2436
|
+
...writes.slice(0, 8).map((o) => o.narrative || o.title),
|
|
2437
|
+
...commands.slice(0, 3).map((o) => o.narrative || o.title)
|
|
2438
|
+
].filter(Boolean).join("; ") || void 0;
|
|
2439
|
+
const learned = researched.map((o) => o.text?.substring(0, 150)).filter(Boolean).slice(0, 5).join("; ") || void 0;
|
|
2310
2440
|
const filesModified = [...new Set(
|
|
2311
2441
|
sessionObs.filter((o) => o.files_modified).map((o) => o.files_modified).flatMap((f) => f.split(",").map((s) => s.trim()))
|
|
2312
2442
|
)];
|
|
2313
|
-
const
|
|
2443
|
+
const sessionConcepts = [...new Set(
|
|
2444
|
+
sessionObs.filter((o) => o.concepts).flatMap((o) => o.concepts.split(",").map((c) => c.trim()))
|
|
2445
|
+
)].slice(0, 10);
|
|
2446
|
+
const nextSteps = [
|
|
2447
|
+
filesModified.length > 0 ? `Files modified: ${filesModified.slice(0, 10).join(", ")}` : "",
|
|
2448
|
+
sessionConcepts.length > 0 ? `Concepts: ${sessionConcepts.join(", ")}` : ""
|
|
2449
|
+
].filter(Boolean).join(". ") || void 0;
|
|
2450
|
+
const mainAction = writes.length > 0 ? `${writes.length} file modific${writes.length === 1 ? "ato" : "ati"}` : commands.length > 0 ? `${commands.length} comand${commands.length === 1 ? "o" : "i"}` : `${sessionObs.length} osservazion${sessionObs.length === 1 ? "e" : "i"}`;
|
|
2314
2451
|
await sdk.storeSummary({
|
|
2315
|
-
request:
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2452
|
+
request: `${project} \u2014 ${mainAction} \u2014 ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`,
|
|
2453
|
+
investigated,
|
|
2454
|
+
completed,
|
|
2455
|
+
learned,
|
|
2456
|
+
nextSteps
|
|
2319
2457
|
});
|
|
2320
2458
|
await notifyWorker("summary-created", { project });
|
|
2321
2459
|
const session = await sdk.getOrCreateSession(input.session_id || `stop-${Date.now()}`);
|
|
2322
|
-
const task = sessionObs[0]?.title ||
|
|
2323
|
-
const progress = completed || "
|
|
2324
|
-
const nextStepsCheckpoint = filesModified.length > 0 ? `
|
|
2460
|
+
const task = sessionObs[0]?.title || `${project} session`;
|
|
2461
|
+
const progress = completed || "No progress recorded";
|
|
2462
|
+
const nextStepsCheckpoint = filesModified.length > 0 ? `Continue work on: ${filesModified.slice(0, 5).join(", ")}` : void 0;
|
|
2325
2463
|
await sdk.createCheckpoint(session.id, {
|
|
2326
2464
|
task,
|
|
2327
2465
|
progress,
|
|
2328
2466
|
nextSteps: nextStepsCheckpoint,
|
|
2329
2467
|
relevantFiles: filesModified.slice(0, 20)
|
|
2330
2468
|
});
|
|
2469
|
+
await sdk.completeSession(session.id);
|
|
2331
2470
|
await notifyWorker("checkpoint-created", { project });
|
|
2332
2471
|
} finally {
|
|
2333
2472
|
sdk.close();
|