kiro-memory 1.7.1 → 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 +263 -44
package/plugin/dist/index.js
CHANGED
|
@@ -23,19 +23,28 @@ __export(Observations_exports, {
|
|
|
23
23
|
deleteObservation: () => deleteObservation,
|
|
24
24
|
getObservationsByProject: () => getObservationsByProject,
|
|
25
25
|
getObservationsBySession: () => getObservationsBySession,
|
|
26
|
+
isDuplicateObservation: () => isDuplicateObservation,
|
|
26
27
|
searchObservations: () => searchObservations,
|
|
27
28
|
updateLastAccessed: () => updateLastAccessed
|
|
28
29
|
});
|
|
29
30
|
function escapeLikePattern(input) {
|
|
30
31
|
return input.replace(/[%_\\]/g, "\\$&");
|
|
31
32
|
}
|
|
32
|
-
function
|
|
33
|
+
function isDuplicateObservation(db, contentHash, windowMs = 3e4) {
|
|
34
|
+
if (!contentHash) return false;
|
|
35
|
+
const threshold = Date.now() - windowMs;
|
|
36
|
+
const result = db.query(
|
|
37
|
+
"SELECT id FROM observations WHERE content_hash = ? AND created_at_epoch > ? LIMIT 1"
|
|
38
|
+
).get(contentHash, threshold);
|
|
39
|
+
return !!result;
|
|
40
|
+
}
|
|
41
|
+
function createObservation(db, memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, contentHash = null, discoveryTokens = 0) {
|
|
33
42
|
const now = /* @__PURE__ */ new Date();
|
|
34
43
|
const result = db.run(
|
|
35
|
-
`INSERT INTO observations
|
|
36
|
-
(memory_session_id, project, type, title, subtitle, text, narrative, facts, concepts, files_read, files_modified, prompt_number, created_at, created_at_epoch)
|
|
37
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
38
|
-
[memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, now.toISOString(), now.getTime()]
|
|
44
|
+
`INSERT INTO observations
|
|
45
|
+
(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)
|
|
46
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
47
|
+
[memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, now.toISOString(), now.getTime(), contentHash, discoveryTokens]
|
|
39
48
|
);
|
|
40
49
|
return Number(result.lastInsertRowid);
|
|
41
50
|
}
|
|
@@ -91,39 +100,42 @@ function consolidateObservations(db, project, options = {}) {
|
|
|
91
100
|
if (groups.length === 0) return { merged: 0, removed: 0 };
|
|
92
101
|
let totalMerged = 0;
|
|
93
102
|
let totalRemoved = 0;
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
uniqueTexts.
|
|
103
|
+
const runConsolidation = db.transaction(() => {
|
|
104
|
+
for (const group of groups) {
|
|
105
|
+
const obsIds = group.ids.split(",").map(Number);
|
|
106
|
+
const placeholders = obsIds.map(() => "?").join(",");
|
|
107
|
+
const observations = db.query(
|
|
108
|
+
`SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC`
|
|
109
|
+
).all(...obsIds);
|
|
110
|
+
if (observations.length < minGroupSize) continue;
|
|
111
|
+
if (options.dryRun) {
|
|
112
|
+
totalMerged += 1;
|
|
113
|
+
totalRemoved += observations.length - 1;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const keeper = observations[0];
|
|
117
|
+
const others = observations.slice(1);
|
|
118
|
+
const uniqueTexts = /* @__PURE__ */ new Set();
|
|
119
|
+
if (keeper.text) uniqueTexts.add(keeper.text);
|
|
120
|
+
for (const obs of others) {
|
|
121
|
+
if (obs.text && !uniqueTexts.has(obs.text)) {
|
|
122
|
+
uniqueTexts.add(obs.text);
|
|
123
|
+
}
|
|
113
124
|
}
|
|
125
|
+
const consolidatedText = Array.from(uniqueTexts).join("\n---\n").substring(0, 1e5);
|
|
126
|
+
db.run(
|
|
127
|
+
"UPDATE observations SET text = ?, title = ? WHERE id = ?",
|
|
128
|
+
[consolidatedText, `[consolidato x${observations.length}] ${keeper.title}`, keeper.id]
|
|
129
|
+
);
|
|
130
|
+
const removeIds = others.map((o) => o.id);
|
|
131
|
+
const removePlaceholders = removeIds.map(() => "?").join(",");
|
|
132
|
+
db.run(`DELETE FROM observations WHERE id IN (${removePlaceholders})`, removeIds);
|
|
133
|
+
db.run(`DELETE FROM observation_embeddings WHERE observation_id IN (${removePlaceholders})`, removeIds);
|
|
134
|
+
totalMerged += 1;
|
|
135
|
+
totalRemoved += removeIds.length;
|
|
114
136
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"UPDATE observations SET text = ?, title = ? WHERE id = ?",
|
|
118
|
-
[consolidatedText, `[consolidato x${observations.length}] ${keeper.title}`, keeper.id]
|
|
119
|
-
);
|
|
120
|
-
const removeIds = others.map((o) => o.id);
|
|
121
|
-
const removePlaceholders = removeIds.map(() => "?").join(",");
|
|
122
|
-
db.run(`DELETE FROM observations WHERE id IN (${removePlaceholders})`, removeIds);
|
|
123
|
-
db.run(`DELETE FROM observation_embeddings WHERE observation_id IN (${removePlaceholders})`, removeIds);
|
|
124
|
-
totalMerged += 1;
|
|
125
|
-
totalRemoved += removeIds.length;
|
|
126
|
-
}
|
|
137
|
+
});
|
|
138
|
+
runConsolidation();
|
|
127
139
|
return { merged: totalMerged, removed: totalRemoved };
|
|
128
140
|
}
|
|
129
141
|
var init_Observations = __esm({
|
|
@@ -181,7 +193,7 @@ function searchObservationsFTS(db, query, filters = {}) {
|
|
|
181
193
|
sql += " AND o.created_at_epoch <= ?";
|
|
182
194
|
params.push(filters.dateEnd);
|
|
183
195
|
}
|
|
184
|
-
sql +=
|
|
196
|
+
sql += ` ORDER BY bm25(observations_fts, ${BM25_WEIGHTS}) LIMIT ?`;
|
|
185
197
|
params.push(limit);
|
|
186
198
|
const stmt = db.query(sql);
|
|
187
199
|
return stmt.all(...params);
|
|
@@ -195,7 +207,7 @@ function searchObservationsFTSWithRank(db, query, filters = {}) {
|
|
|
195
207
|
const safeQuery = sanitizeFTS5Query(query);
|
|
196
208
|
if (!safeQuery) return [];
|
|
197
209
|
let sql = `
|
|
198
|
-
SELECT o.*,
|
|
210
|
+
SELECT o.*, bm25(observations_fts, ${BM25_WEIGHTS}) as fts5_rank FROM observations o
|
|
199
211
|
JOIN observations_fts fts ON o.id = fts.rowid
|
|
200
212
|
WHERE observations_fts MATCH ?
|
|
201
213
|
`;
|
|
@@ -216,7 +228,7 @@ function searchObservationsFTSWithRank(db, query, filters = {}) {
|
|
|
216
228
|
sql += " AND o.created_at_epoch <= ?";
|
|
217
229
|
params.push(filters.dateEnd);
|
|
218
230
|
}
|
|
219
|
-
sql +=
|
|
231
|
+
sql += ` ORDER BY bm25(observations_fts, ${BM25_WEIGHTS}) LIMIT ?`;
|
|
220
232
|
params.push(limit);
|
|
221
233
|
const stmt = db.query(sql);
|
|
222
234
|
return stmt.all(...params);
|
|
@@ -320,11 +332,23 @@ function getProjectStats(db, project) {
|
|
|
320
332
|
const sumStmt = db.query("SELECT COUNT(*) as count FROM summaries WHERE project = ?");
|
|
321
333
|
const sesStmt = db.query("SELECT COUNT(*) as count FROM sessions WHERE project = ?");
|
|
322
334
|
const prmStmt = db.query("SELECT COUNT(*) as count FROM prompts WHERE project = ?");
|
|
335
|
+
const discoveryStmt = db.query(
|
|
336
|
+
"SELECT COALESCE(SUM(discovery_tokens), 0) as total FROM observations WHERE project = ?"
|
|
337
|
+
);
|
|
338
|
+
const discoveryTokens = discoveryStmt.get(project)?.total || 0;
|
|
339
|
+
const readStmt = db.query(
|
|
340
|
+
`SELECT COALESCE(SUM(
|
|
341
|
+
CAST((LENGTH(COALESCE(title, '')) + LENGTH(COALESCE(narrative, ''))) / 4 AS INTEGER)
|
|
342
|
+
), 0) as total FROM observations WHERE project = ?`
|
|
343
|
+
);
|
|
344
|
+
const readTokens = readStmt.get(project)?.total || 0;
|
|
345
|
+
const savings = Math.max(0, discoveryTokens - readTokens);
|
|
323
346
|
return {
|
|
324
347
|
observations: obsStmt.get(project)?.count || 0,
|
|
325
348
|
summaries: sumStmt.get(project)?.count || 0,
|
|
326
349
|
sessions: sesStmt.get(project)?.count || 0,
|
|
327
|
-
prompts: prmStmt.get(project)?.count || 0
|
|
350
|
+
prompts: prmStmt.get(project)?.count || 0,
|
|
351
|
+
tokenEconomics: { discoveryTokens, readTokens, savings }
|
|
328
352
|
};
|
|
329
353
|
}
|
|
330
354
|
function getStaleObservations(db, project) {
|
|
@@ -366,9 +390,11 @@ function markObservationsStale(db, ids, stale) {
|
|
|
366
390
|
[stale ? 1 : 0, ...validIds]
|
|
367
391
|
);
|
|
368
392
|
}
|
|
393
|
+
var BM25_WEIGHTS;
|
|
369
394
|
var init_Search = __esm({
|
|
370
395
|
"src/services/sqlite/Search.ts"() {
|
|
371
396
|
"use strict";
|
|
397
|
+
BM25_WEIGHTS = "10.0, 1.0, 5.0, 3.0";
|
|
372
398
|
}
|
|
373
399
|
});
|
|
374
400
|
|
|
@@ -1057,6 +1083,29 @@ var MigrationRunner = class {
|
|
|
1057
1083
|
db.run("CREATE INDEX IF NOT EXISTS idx_checkpoints_project ON checkpoints(project)");
|
|
1058
1084
|
db.run("CREATE INDEX IF NOT EXISTS idx_checkpoints_epoch ON checkpoints(created_at_epoch)");
|
|
1059
1085
|
}
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
version: 7,
|
|
1089
|
+
up: (db) => {
|
|
1090
|
+
db.run("ALTER TABLE observations ADD COLUMN content_hash TEXT");
|
|
1091
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_observations_hash ON observations(content_hash)");
|
|
1092
|
+
}
|
|
1093
|
+
},
|
|
1094
|
+
{
|
|
1095
|
+
version: 8,
|
|
1096
|
+
up: (db) => {
|
|
1097
|
+
db.run("ALTER TABLE observations ADD COLUMN discovery_tokens INTEGER DEFAULT 0");
|
|
1098
|
+
db.run("ALTER TABLE summaries ADD COLUMN discovery_tokens INTEGER DEFAULT 0");
|
|
1099
|
+
}
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
version: 9,
|
|
1103
|
+
up: (db) => {
|
|
1104
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_observations_project_epoch ON observations(project, created_at_epoch DESC)");
|
|
1105
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_observations_project_type ON observations(project, type)");
|
|
1106
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_summaries_project_epoch ON summaries(project, created_at_epoch DESC)");
|
|
1107
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_prompts_project_epoch ON prompts(project, created_at_epoch DESC)");
|
|
1108
|
+
}
|
|
1060
1109
|
}
|
|
1061
1110
|
];
|
|
1062
1111
|
}
|
|
@@ -1311,6 +1360,7 @@ init_Search();
|
|
|
1311
1360
|
|
|
1312
1361
|
// src/sdk/index.ts
|
|
1313
1362
|
init_Observations();
|
|
1363
|
+
import { createHash } from "crypto";
|
|
1314
1364
|
init_Search();
|
|
1315
1365
|
|
|
1316
1366
|
// src/services/search/EmbeddingService.ts
|
|
@@ -1869,31 +1919,71 @@ var KiroMemorySDK = class {
|
|
|
1869
1919
|
logger.debug("SDK", `Embedding generation fallita per obs ${observationId}: ${error}`);
|
|
1870
1920
|
}
|
|
1871
1921
|
}
|
|
1922
|
+
/**
|
|
1923
|
+
* Genera content hash SHA256 per deduplicazione basata su contenuto.
|
|
1924
|
+
* Usa (project + type + title + narrative) come tupla di identità semantica.
|
|
1925
|
+
* NON include sessionId perché è unico ad ogni invocazione.
|
|
1926
|
+
*/
|
|
1927
|
+
generateContentHash(type, title, narrative) {
|
|
1928
|
+
const payload = `${this.project}|${type}|${title}|${narrative || ""}`;
|
|
1929
|
+
return createHash("sha256").update(payload).digest("hex");
|
|
1930
|
+
}
|
|
1931
|
+
/**
|
|
1932
|
+
* Finestre di deduplicazione per tipo (ms).
|
|
1933
|
+
* Tipi con molte ripetizioni hanno finestre più ampie.
|
|
1934
|
+
*/
|
|
1935
|
+
getDeduplicationWindow(type) {
|
|
1936
|
+
switch (type) {
|
|
1937
|
+
case "file-read":
|
|
1938
|
+
return 6e4;
|
|
1939
|
+
// 60s — letture frequenti sugli stessi file
|
|
1940
|
+
case "file-write":
|
|
1941
|
+
return 1e4;
|
|
1942
|
+
// 10s — scritture rapide consecutive
|
|
1943
|
+
case "command":
|
|
1944
|
+
return 3e4;
|
|
1945
|
+
// 30s — standard
|
|
1946
|
+
case "research":
|
|
1947
|
+
return 12e4;
|
|
1948
|
+
// 120s — web search e fetch ripetuti
|
|
1949
|
+
case "delegation":
|
|
1950
|
+
return 6e4;
|
|
1951
|
+
// 60s — delegazioni rapide
|
|
1952
|
+
default:
|
|
1953
|
+
return 3e4;
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1872
1956
|
/**
|
|
1873
1957
|
* Store a new observation
|
|
1874
1958
|
*/
|
|
1875
1959
|
async storeObservation(data) {
|
|
1876
1960
|
this.validateObservationInput(data);
|
|
1961
|
+
const sessionId = "sdk-" + Date.now();
|
|
1962
|
+
const contentHash = this.generateContentHash(data.type, data.title, data.narrative);
|
|
1963
|
+
const dedupWindow = this.getDeduplicationWindow(data.type);
|
|
1964
|
+
if (isDuplicateObservation(this.db.db, contentHash, dedupWindow)) {
|
|
1965
|
+
logger.debug("SDK", `Osservazione duplicata scartata (${data.type}, ${dedupWindow}ms): ${data.title}`);
|
|
1966
|
+
return -1;
|
|
1967
|
+
}
|
|
1968
|
+
const filesRead = data.filesRead || (data.type === "file-read" ? data.files : void 0);
|
|
1969
|
+
const filesModified = data.filesModified || (data.type === "file-write" ? data.files : void 0);
|
|
1970
|
+
const discoveryTokens = Math.ceil(data.content.length / 4);
|
|
1877
1971
|
const observationId = createObservation(
|
|
1878
1972
|
this.db.db,
|
|
1879
|
-
|
|
1973
|
+
sessionId,
|
|
1880
1974
|
this.project,
|
|
1881
1975
|
data.type,
|
|
1882
1976
|
data.title,
|
|
1883
|
-
null,
|
|
1884
|
-
// subtitle
|
|
1977
|
+
data.subtitle || null,
|
|
1885
1978
|
data.content,
|
|
1886
|
-
null,
|
|
1887
|
-
|
|
1888
|
-
null,
|
|
1889
|
-
// facts
|
|
1979
|
+
data.narrative || null,
|
|
1980
|
+
data.facts || null,
|
|
1890
1981
|
data.concepts?.join(", ") || null,
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
// prompt_number
|
|
1982
|
+
filesRead?.join(", ") || null,
|
|
1983
|
+
filesModified?.join(", ") || null,
|
|
1984
|
+
0,
|
|
1985
|
+
contentHash,
|
|
1986
|
+
discoveryTokens
|
|
1897
1987
|
);
|
|
1898
1988
|
this.generateEmbeddingAsync(observationId, data.title, data.content, data.concepts).catch(() => {
|
|
1899
1989
|
});
|
|
@@ -1936,9 +2026,16 @@ var KiroMemorySDK = class {
|
|
|
1936
2026
|
};
|
|
1937
2027
|
}
|
|
1938
2028
|
})();
|
|
2029
|
+
const sessionId = "sdk-" + Date.now();
|
|
2030
|
+
const contentHash = this.generateContentHash(data.type, data.title);
|
|
2031
|
+
if (isDuplicateObservation(this.db.db, contentHash)) {
|
|
2032
|
+
logger.debug("SDK", `Knowledge duplicata scartata: ${data.title}`);
|
|
2033
|
+
return -1;
|
|
2034
|
+
}
|
|
2035
|
+
const discoveryTokens = Math.ceil(data.content.length / 4);
|
|
1939
2036
|
const observationId = createObservation(
|
|
1940
2037
|
this.db.db,
|
|
1941
|
-
|
|
2038
|
+
sessionId,
|
|
1942
2039
|
data.project || this.project,
|
|
1943
2040
|
data.knowledgeType,
|
|
1944
2041
|
// type = knowledgeType
|
|
@@ -1952,9 +2049,12 @@ var KiroMemorySDK = class {
|
|
|
1952
2049
|
// facts = metadati JSON
|
|
1953
2050
|
data.concepts?.join(", ") || null,
|
|
1954
2051
|
data.files?.join(", ") || null,
|
|
1955
|
-
|
|
1956
|
-
|
|
2052
|
+
null,
|
|
2053
|
+
// filesModified: knowledge non modifica file
|
|
2054
|
+
0,
|
|
1957
2055
|
// prompt_number
|
|
2056
|
+
contentHash,
|
|
2057
|
+
discoveryTokens
|
|
1958
2058
|
);
|
|
1959
2059
|
this.generateEmbeddingAsync(observationId, data.title, data.content, data.concepts).catch(() => {
|
|
1960
2060
|
});
|
|
@@ -1970,11 +2070,11 @@ var KiroMemorySDK = class {
|
|
|
1970
2070
|
"sdk-" + Date.now(),
|
|
1971
2071
|
this.project,
|
|
1972
2072
|
data.request || null,
|
|
1973
|
-
null,
|
|
2073
|
+
data.investigated || null,
|
|
1974
2074
|
data.learned || null,
|
|
1975
2075
|
data.completed || null,
|
|
1976
2076
|
data.nextSteps || null,
|
|
1977
|
-
null
|
|
2077
|
+
data.notes || null
|
|
1978
2078
|
);
|
|
1979
2079
|
}
|
|
1980
2080
|
/**
|
|
@@ -2158,7 +2258,14 @@ var KiroMemorySDK = class {
|
|
|
2158
2258
|
}));
|
|
2159
2259
|
} else {
|
|
2160
2260
|
const observations = getObservationsByProject(this.db.db, this.project, 30);
|
|
2161
|
-
|
|
2261
|
+
const knowledgeTypes = new Set(KNOWLEDGE_TYPES);
|
|
2262
|
+
const knowledgeObs = [];
|
|
2263
|
+
const normalObs = [];
|
|
2264
|
+
for (const obs of observations) {
|
|
2265
|
+
if (knowledgeTypes.has(obs.type)) knowledgeObs.push(obs);
|
|
2266
|
+
else normalObs.push(obs);
|
|
2267
|
+
}
|
|
2268
|
+
const scoreObs = (obs) => {
|
|
2162
2269
|
const signals = {
|
|
2163
2270
|
semantic: 0,
|
|
2164
2271
|
fts5: 0,
|
|
@@ -2166,7 +2273,6 @@ var KiroMemorySDK = class {
|
|
|
2166
2273
|
projectMatch: projectMatchScore(obs.project, this.project)
|
|
2167
2274
|
};
|
|
2168
2275
|
const baseScore = computeCompositeScore(signals, CONTEXT_WEIGHTS);
|
|
2169
|
-
const boostedScore = Math.min(1, baseScore * knowledgeTypeBoost(obs.type));
|
|
2170
2276
|
return {
|
|
2171
2277
|
id: obs.id,
|
|
2172
2278
|
title: obs.title,
|
|
@@ -2175,17 +2281,23 @@ var KiroMemorySDK = class {
|
|
|
2175
2281
|
project: obs.project,
|
|
2176
2282
|
created_at: obs.created_at,
|
|
2177
2283
|
created_at_epoch: obs.created_at_epoch,
|
|
2178
|
-
score:
|
|
2284
|
+
score: Math.min(1, baseScore * knowledgeTypeBoost(obs.type)),
|
|
2179
2285
|
signals
|
|
2180
2286
|
};
|
|
2181
|
-
}
|
|
2182
|
-
|
|
2287
|
+
};
|
|
2288
|
+
const scoredKnowledge = knowledgeObs.map(scoreObs).sort((a, b) => b.score - a.score);
|
|
2289
|
+
const scoredNormal = normalObs.map(scoreObs).sort((a, b) => b.score - a.score);
|
|
2290
|
+
items = [...scoredKnowledge, ...scoredNormal];
|
|
2183
2291
|
}
|
|
2184
2292
|
let tokensUsed = 0;
|
|
2293
|
+
const budgetItems = [];
|
|
2185
2294
|
for (const item of items) {
|
|
2186
|
-
|
|
2187
|
-
if (tokensUsed > tokenBudget) break;
|
|
2295
|
+
const itemTokens = Math.ceil((item.title.length + item.content.length) / 4);
|
|
2296
|
+
if (tokensUsed + itemTokens > tokenBudget) break;
|
|
2297
|
+
tokensUsed += itemTokens;
|
|
2298
|
+
budgetItems.push(item);
|
|
2188
2299
|
}
|
|
2300
|
+
items = budgetItems;
|
|
2189
2301
|
return {
|
|
2190
2302
|
project: this.project,
|
|
2191
2303
|
items,
|