@hanhnd/agent-kit 1.0.34 → 1.0.36
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/dist/cli/commands/cli-digest-pending.test.js +19 -10
- package/dist/cli/commands/memory.js +44 -4
- package/dist/cli.js +348 -179
- package/dist/core/config/index.d.ts +3 -0
- package/dist/core/config/index.js +20 -0
- package/dist/entrypoints/server.js +1 -3
- package/dist/mcp/memory.d.ts +2 -1
- package/dist/mcp/memory.js +45 -18
- package/dist/server.js +294 -200
- package/dist/services/digest/constants.d.ts +3 -3
- package/dist/services/digest/constants.js +3 -3
- package/dist/services/digest/digest-background.test.js +19 -8
- package/dist/services/digest/digest-init.test.js +13 -2
- package/dist/services/digest/digest-pending.test.js +13 -2
- package/dist/services/digest/digest-processor.test.js +52 -0
- package/dist/services/digest/files.js +3 -3
- package/dist/services/digest/model-registry.js +2 -2
- package/dist/services/digest/processor.js +43 -13
- package/dist/services/digest/providers/llama-local.js +17 -28
- package/dist/services/digest/types.d.ts +3 -1
- package/dist/services/digest/types.js +1 -1
- package/dist/services/memory/chunker.d.ts +5 -2
- package/dist/services/memory/chunker.js +3 -1
- package/dist/services/memory/chunker.test.js +21 -11
- package/dist/services/memory/constants.js +1 -1
- package/dist/services/memory/embedder.js +1 -1
- package/dist/services/memory/index.d.ts +1 -1
- package/dist/services/memory/indexer.d.ts +2 -1
- package/dist/services/memory/indexer.js +24 -8
- package/dist/services/memory/indexer.test.js +38 -2
- package/dist/services/memory/memory.test.js +172 -15
- package/dist/services/memory/store.d.ts +9 -3
- package/dist/services/memory/store.js +132 -98
- package/dist/services/memory/store.test.js +239 -14
- package/dist/services/memory/types.d.ts +9 -1
- package/dist/services/memory/types.js +2 -1
- package/dist/utils/paths.d.ts +2 -0
- package/dist/utils/paths.js +2 -1
- package/dist/utils/utils.d.ts +1 -0
- package/dist/utils/utils.js +3 -3
- package/package.json +3 -5
package/dist/server.js
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { createRequire } from 'module'; const require = createRequire(import.meta.url);
|
|
3
2
|
var __create = Object.create;
|
|
4
3
|
var __defProp = Object.defineProperty;
|
|
5
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var
|
|
10
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
11
|
-
}) : x)(function(x) {
|
|
12
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
13
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
14
|
-
});
|
|
15
|
-
var __commonJS = (cb, mod) => function __require2() {
|
|
8
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
16
9
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
17
10
|
};
|
|
18
11
|
var __export = (target, all) => {
|
|
@@ -6792,12 +6785,12 @@ var require_dist = __commonJS({
|
|
|
6792
6785
|
throw new Error(`Unknown format "${name}"`);
|
|
6793
6786
|
return f;
|
|
6794
6787
|
};
|
|
6795
|
-
function addFormats(ajv, list,
|
|
6788
|
+
function addFormats(ajv, list, fs12, exportName) {
|
|
6796
6789
|
var _a;
|
|
6797
6790
|
var _b;
|
|
6798
6791
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
6799
6792
|
for (const f of list)
|
|
6800
|
-
ajv.addFormat(f,
|
|
6793
|
+
ajv.addFormat(f, fs12[f]);
|
|
6801
6794
|
}
|
|
6802
6795
|
module.exports = exports = formatsPlugin;
|
|
6803
6796
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -35889,7 +35882,7 @@ import * as path4 from "path";
|
|
|
35889
35882
|
|
|
35890
35883
|
// src/utils/utils.ts
|
|
35891
35884
|
function getWorkspaceRoot() {
|
|
35892
|
-
return process.env.WORKSPACE_DIR || process.env.CLAUDE_PROJECT_DIR || process.env.CODEX_PROJECT_DIR || process.env.GEMINI_PROJECT_DIR || process.env.
|
|
35885
|
+
return process.env.WORKSPACE_DIR || process.env.CLAUDE_PROJECT_DIR || process.env.CODEX_PROJECT_DIR || process.env.GEMINI_PROJECT_DIR || process.env.INIT_CWD || process.env.PWD || process.cwd();
|
|
35893
35886
|
}
|
|
35894
35887
|
function mcpText(text) {
|
|
35895
35888
|
return { content: [{ type: "text", text }] };
|
|
@@ -35897,11 +35890,18 @@ function mcpText(text) {
|
|
|
35897
35890
|
function mcpJson(obj) {
|
|
35898
35891
|
return mcpText(JSON.stringify(obj, null, 2));
|
|
35899
35892
|
}
|
|
35893
|
+
function formatError2(err) {
|
|
35894
|
+
return err instanceof Error ? err.message : String(err);
|
|
35895
|
+
}
|
|
35900
35896
|
|
|
35901
35897
|
// src/core/config/index.ts
|
|
35902
35898
|
import * as fs2 from "fs";
|
|
35899
|
+
import * as os from "os";
|
|
35903
35900
|
import * as path2 from "path";
|
|
35904
35901
|
|
|
35902
|
+
// src/services/memory/types.ts
|
|
35903
|
+
var SOURCE_TYPES = ["digest", "concept", "entity", "preference", "wiki"];
|
|
35904
|
+
|
|
35905
35905
|
// src/services/memory/constants.ts
|
|
35906
35906
|
var DEFAULT_MEMORY_CONFIG = {
|
|
35907
35907
|
enabled: false,
|
|
@@ -35909,7 +35909,7 @@ var DEFAULT_MEMORY_CONFIG = {
|
|
|
35909
35909
|
topK: 5,
|
|
35910
35910
|
chunkSize: 1500,
|
|
35911
35911
|
overlapLines: 2,
|
|
35912
|
-
embeddingModel: "
|
|
35912
|
+
embeddingModel: "base" /* BASE */,
|
|
35913
35913
|
vectorDimension: 384
|
|
35914
35914
|
};
|
|
35915
35915
|
var LOCK_RETRY_MS = 50;
|
|
@@ -35983,8 +35983,8 @@ function tryAcquireProcessLock(lockPath) {
|
|
|
35983
35983
|
}
|
|
35984
35984
|
|
|
35985
35985
|
// src/core/config/index.ts
|
|
35986
|
-
function resolveMemoryConfig(
|
|
35987
|
-
const override =
|
|
35986
|
+
function resolveMemoryConfig(settings, workspaceRoot2) {
|
|
35987
|
+
const override = settings.memory ?? {};
|
|
35988
35988
|
return {
|
|
35989
35989
|
...DEFAULT_MEMORY_CONFIG,
|
|
35990
35990
|
...override,
|
|
@@ -35992,24 +35992,26 @@ function resolveMemoryConfig(settings2, workspaceRoot2) {
|
|
|
35992
35992
|
wikiDir: typeof override.wikiDir === "string" && override.wikiDir.length > 0 ? override.wikiDir : path2.join(workspaceRoot2, ".agent-kit", "wiki")
|
|
35993
35993
|
};
|
|
35994
35994
|
}
|
|
35995
|
-
function
|
|
35996
|
-
|
|
35997
|
-
|
|
35995
|
+
function getGlobalSettingsFile() {
|
|
35996
|
+
return path2.join(os.homedir(), ".agent-kit", "settings.json");
|
|
35997
|
+
}
|
|
35998
|
+
function loadGlobalSettings() {
|
|
35999
|
+
const file = getGlobalSettingsFile();
|
|
36000
|
+
if (fs2.existsSync(file)) {
|
|
35998
36001
|
try {
|
|
35999
|
-
return JSON.parse(fs2.readFileSync(
|
|
36002
|
+
return JSON.parse(fs2.readFileSync(file, "utf-8"));
|
|
36000
36003
|
} catch (error2) {
|
|
36001
|
-
console.error(`[agent-kit] Warning: Failed to parse ${
|
|
36004
|
+
console.error(`[agent-kit] Warning: Failed to parse ${file}:`, error2);
|
|
36002
36005
|
}
|
|
36003
36006
|
}
|
|
36004
36007
|
return {};
|
|
36005
36008
|
}
|
|
36006
|
-
function
|
|
36007
|
-
const
|
|
36008
|
-
|
|
36009
|
-
atomicWriteJsonFile(settingsPath, { ...current, ...patch });
|
|
36009
|
+
function writeGlobalSettings(patch) {
|
|
36010
|
+
const current = loadGlobalSettings();
|
|
36011
|
+
atomicWriteJsonFile(getGlobalSettingsFile(), { ...current, ...patch });
|
|
36010
36012
|
}
|
|
36011
|
-
function resolveConversationDigestConfig(
|
|
36012
|
-
const digest =
|
|
36013
|
+
function resolveConversationDigestConfig(settings) {
|
|
36014
|
+
const digest = settings.memory?.conversationDigest;
|
|
36013
36015
|
if (!digest?.initialized || typeof digest.modelId !== "string" || typeof digest.initializedAt !== "string") {
|
|
36014
36016
|
return void 0;
|
|
36015
36017
|
}
|
|
@@ -36383,10 +36385,11 @@ import { writeFileSync as writeFileSync4 } from "fs";
|
|
|
36383
36385
|
import * as fs5 from "fs";
|
|
36384
36386
|
|
|
36385
36387
|
// src/utils/paths.ts
|
|
36386
|
-
import * as
|
|
36388
|
+
import * as os2 from "os";
|
|
36387
36389
|
import * as path7 from "path";
|
|
36388
|
-
var AGENT_KIT_HOME = path7.join(
|
|
36390
|
+
var AGENT_KIT_HOME = path7.join(os2.homedir(), ".agent-kit");
|
|
36389
36391
|
var CREDENTIALS_FILE = path7.join(AGENT_KIT_HOME, "credentials");
|
|
36392
|
+
var GLOBAL_SETTINGS_FILE = path7.join(AGENT_KIT_HOME, "settings.json");
|
|
36390
36393
|
var MODEL_CACHE_DIR = path7.join(AGENT_KIT_HOME, "cache", "models");
|
|
36391
36394
|
var FASTEMBED_CACHE_DIR = path7.join(MODEL_CACHE_DIR, "fastembed");
|
|
36392
36395
|
|
|
@@ -36758,6 +36761,7 @@ ${ticket.fields.labels?.join(", ") || "None"}`;
|
|
|
36758
36761
|
}
|
|
36759
36762
|
|
|
36760
36763
|
// src/mcp/memory.ts
|
|
36764
|
+
import * as fs11 from "fs";
|
|
36761
36765
|
import * as path14 from "path";
|
|
36762
36766
|
|
|
36763
36767
|
// src/services/memory/embedder.ts
|
|
@@ -36807,7 +36811,7 @@ var Embedder = class {
|
|
|
36807
36811
|
const { EmbeddingModel, FlagEmbedding } = await import("fastembed");
|
|
36808
36812
|
const modelMap = {
|
|
36809
36813
|
["tiny" /* TINY */]: EmbeddingModel.AllMiniLML6V2,
|
|
36810
|
-
["
|
|
36814
|
+
["base" /* BASE */]: EmbeddingModel.BGESmallENV15,
|
|
36811
36815
|
["large" /* LARGE */]: EmbeddingModel.BGEBaseENV15
|
|
36812
36816
|
};
|
|
36813
36817
|
const model = modelMap[this.modelName] ?? EmbeddingModel.BGESmallENV15;
|
|
@@ -36919,7 +36923,7 @@ function splitSection(text, maxLen) {
|
|
|
36919
36923
|
if (byPara.every((p) => p.length <= maxLen)) return byPara;
|
|
36920
36924
|
return byPara.flatMap((p) => p.length <= maxLen ? [p] : splitAtLine(p, maxLen));
|
|
36921
36925
|
}
|
|
36922
|
-
function chunkMarkdown(text, source, config2) {
|
|
36926
|
+
function chunkMarkdown(text, source, config2, meta) {
|
|
36923
36927
|
if (!text || !text.trim()) return [];
|
|
36924
36928
|
const { chunkSize, overlapLines } = config2;
|
|
36925
36929
|
const lines = text.split("\n");
|
|
@@ -36958,11 +36962,13 @@ ${sectionText}` : sectionText;
|
|
|
36958
36962
|
chunks.push({
|
|
36959
36963
|
id: computeChunkId(content),
|
|
36960
36964
|
source,
|
|
36965
|
+
sourceType: meta.sourceType,
|
|
36961
36966
|
heading: section.heading,
|
|
36962
36967
|
headingLevel: section.headingLevel,
|
|
36963
36968
|
content,
|
|
36964
36969
|
lineStart: firstLine,
|
|
36965
|
-
lineEnd: lastLine
|
|
36970
|
+
lineEnd: lastLine,
|
|
36971
|
+
fileMtimeAt: meta.fileMtimeAt
|
|
36966
36972
|
});
|
|
36967
36973
|
if (pi === parts.length - 1) {
|
|
36968
36974
|
prevOverlapLines = partLines.slice(-overlapLines);
|
|
@@ -36973,6 +36979,13 @@ ${sectionText}` : sectionText;
|
|
|
36973
36979
|
}
|
|
36974
36980
|
|
|
36975
36981
|
// src/services/memory/indexer.ts
|
|
36982
|
+
function deriveSourceType(source) {
|
|
36983
|
+
if (source.startsWith("compiled/provisional/conversation-digests/")) return "digest";
|
|
36984
|
+
if (source.startsWith("compiled/concepts/")) return "concept";
|
|
36985
|
+
if (source.startsWith("compiled/entities/")) return "entity";
|
|
36986
|
+
if (source === "compiled/preferences.md") return "preference";
|
|
36987
|
+
return "wiki";
|
|
36988
|
+
}
|
|
36976
36989
|
var MemoryIndexer = class {
|
|
36977
36990
|
constructor(store, embedder, config2) {
|
|
36978
36991
|
this.store = store;
|
|
@@ -36993,7 +37006,15 @@ var MemoryIndexer = class {
|
|
|
36993
37006
|
console.warn(`[memory-indexer] Cannot read file ${absolutePath}:`, err);
|
|
36994
37007
|
return { indexed: 0, deleted: 0, skipped: existingChunkIds.size };
|
|
36995
37008
|
}
|
|
36996
|
-
|
|
37009
|
+
let fileMtimeAt;
|
|
37010
|
+
try {
|
|
37011
|
+
fileMtimeAt = fs6.statSync(absolutePath).mtimeMs;
|
|
37012
|
+
} catch (err) {
|
|
37013
|
+
console.warn(`[memory-indexer] Cannot stat file ${absolutePath}:`, err);
|
|
37014
|
+
fileMtimeAt = Date.now();
|
|
37015
|
+
}
|
|
37016
|
+
const sourceType = deriveSourceType(source);
|
|
37017
|
+
const allChunks = chunkMarkdown(text, source, this.config, { sourceType, fileMtimeAt });
|
|
36997
37018
|
const currentChunkIds = new Set(allChunks.map((chunk) => chunk.id));
|
|
36998
37019
|
const toIndex = allChunks.filter((chunk) => !existingChunkIds.has(chunk.id));
|
|
36999
37020
|
const toDelete = [...existingChunkIds].filter((id) => !currentChunkIds.has(id));
|
|
@@ -37080,12 +37101,9 @@ var MemoryIndexer = class {
|
|
|
37080
37101
|
scoreMap.set(r.id, { dense: 1 / (RRF_K + rank + 1), bm25: 0 });
|
|
37081
37102
|
});
|
|
37082
37103
|
bm25Results.forEach((r, rank) => {
|
|
37083
|
-
const
|
|
37084
|
-
|
|
37085
|
-
|
|
37086
|
-
} else {
|
|
37087
|
-
scoreMap.set(r.id, { dense: 0, bm25: 1 / (RRF_K + rank + 1) });
|
|
37088
|
-
}
|
|
37104
|
+
const entry = scoreMap.get(r.id) ?? { dense: 0, bm25: 0 };
|
|
37105
|
+
entry.bm25 = 1 / (RRF_K + rank + 1);
|
|
37106
|
+
scoreMap.set(r.id, entry);
|
|
37089
37107
|
});
|
|
37090
37108
|
const hasDense = this.store.vecAvailable && denseResults.length > 0;
|
|
37091
37109
|
const numRetrievers = hasDense ? 2 : 1;
|
|
@@ -37157,9 +37175,11 @@ ${content}
|
|
|
37157
37175
|
|
|
37158
37176
|
// src/services/memory/store.ts
|
|
37159
37177
|
var import_stopword = __toESM(require_stopword_cjs(), 1);
|
|
37160
|
-
import Database from "
|
|
37178
|
+
import Database from "libsql";
|
|
37161
37179
|
import * as fs7 from "fs";
|
|
37162
37180
|
import * as path9 from "path";
|
|
37181
|
+
var SCHEMA_VERSION = 3;
|
|
37182
|
+
var LibsqlDatabase = Database;
|
|
37163
37183
|
var StoreError = class extends Error {
|
|
37164
37184
|
constructor(message, cause) {
|
|
37165
37185
|
super(message);
|
|
@@ -37174,13 +37194,11 @@ var MemoryStore = class {
|
|
|
37174
37194
|
this.db = this.openDatabase(dbPath);
|
|
37175
37195
|
}
|
|
37176
37196
|
db;
|
|
37177
|
-
_vecAvailable = false;
|
|
37178
37197
|
openDatabase(dbPath) {
|
|
37179
37198
|
try {
|
|
37180
|
-
const db = new
|
|
37199
|
+
const db = new LibsqlDatabase(dbPath);
|
|
37181
37200
|
db.pragma("journal_mode = WAL");
|
|
37182
37201
|
db.pragma("busy_timeout = 5000");
|
|
37183
|
-
this.loadVecExtension(db);
|
|
37184
37202
|
this.createSchema(db);
|
|
37185
37203
|
return db;
|
|
37186
37204
|
} catch (err) {
|
|
@@ -37189,102 +37207,115 @@ var MemoryStore = class {
|
|
|
37189
37207
|
fs7.unlinkSync(dbPath);
|
|
37190
37208
|
} catch {
|
|
37191
37209
|
}
|
|
37192
|
-
|
|
37193
|
-
|
|
37194
|
-
|
|
37195
|
-
|
|
37196
|
-
|
|
37197
|
-
|
|
37198
|
-
|
|
37199
|
-
|
|
37200
|
-
|
|
37201
|
-
try {
|
|
37202
|
-
const sqliteVec = __require("sqlite-vec");
|
|
37203
|
-
sqliteVec.load(db);
|
|
37204
|
-
this._vecAvailable = true;
|
|
37205
|
-
} catch (err) {
|
|
37206
|
-
console.warn("[memory-store] sqlite-vec unavailable, degrading to FTS5-only:", err);
|
|
37207
|
-
this._vecAvailable = false;
|
|
37210
|
+
try {
|
|
37211
|
+
const db = new LibsqlDatabase(dbPath);
|
|
37212
|
+
db.pragma("journal_mode = WAL");
|
|
37213
|
+
db.pragma("busy_timeout = 5000");
|
|
37214
|
+
this.createSchema(db);
|
|
37215
|
+
return db;
|
|
37216
|
+
} catch (retryErr) {
|
|
37217
|
+
throw new StoreError("database open failed", retryErr);
|
|
37218
|
+
}
|
|
37208
37219
|
}
|
|
37209
37220
|
}
|
|
37210
37221
|
createSchema(db) {
|
|
37211
|
-
|
|
37212
|
-
|
|
37213
|
-
|
|
37214
|
-
|
|
37215
|
-
|
|
37216
|
-
|
|
37217
|
-
|
|
37218
|
-
|
|
37219
|
-
|
|
37220
|
-
|
|
37221
|
-
|
|
37222
|
-
|
|
37222
|
+
try {
|
|
37223
|
+
const current = this.readUserVersion(db);
|
|
37224
|
+
if (current < SCHEMA_VERSION) {
|
|
37225
|
+
db.exec(`
|
|
37226
|
+
DROP TRIGGER IF EXISTS memory_chunks_ai;
|
|
37227
|
+
DROP TRIGGER IF EXISTS memory_chunks_ad;
|
|
37228
|
+
DROP TABLE IF EXISTS memory_fts;
|
|
37229
|
+
DROP TABLE IF EXISTS memory_vec;
|
|
37230
|
+
DROP INDEX IF EXISTS idx_memory_chunks_embedding;
|
|
37231
|
+
DROP TABLE IF EXISTS memory_chunks;
|
|
37232
|
+
`);
|
|
37233
|
+
db.pragma(`user_version = ${SCHEMA_VERSION}`);
|
|
37234
|
+
}
|
|
37235
|
+
db.exec(`
|
|
37236
|
+
CREATE TABLE IF NOT EXISTS memory_chunks (
|
|
37237
|
+
rowid INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
37238
|
+
id TEXT NOT NULL UNIQUE,
|
|
37239
|
+
source TEXT NOT NULL,
|
|
37240
|
+
source_type TEXT NOT NULL DEFAULT 'wiki',
|
|
37241
|
+
heading TEXT NOT NULL DEFAULT '',
|
|
37242
|
+
heading_level INTEGER NOT NULL DEFAULT 0,
|
|
37243
|
+
content TEXT NOT NULL,
|
|
37244
|
+
line_start INTEGER NOT NULL,
|
|
37245
|
+
line_end INTEGER NOT NULL,
|
|
37246
|
+
indexed_at INTEGER NOT NULL,
|
|
37247
|
+
file_mtime_at INTEGER NOT NULL DEFAULT 0,
|
|
37248
|
+
embedding F32_BLOB(${this.config.vectorDimension}) NOT NULL
|
|
37249
|
+
);
|
|
37223
37250
|
|
|
37224
|
-
|
|
37225
|
-
|
|
37226
|
-
heading,
|
|
37227
|
-
content,
|
|
37228
|
-
content='memory_chunks',
|
|
37229
|
-
content_rowid='rowid'
|
|
37230
|
-
);
|
|
37251
|
+
CREATE INDEX IF NOT EXISTS idx_memory_chunks_type_mtime
|
|
37252
|
+
ON memory_chunks (source_type, file_mtime_at DESC);
|
|
37231
37253
|
|
|
37232
|
-
|
|
37233
|
-
|
|
37234
|
-
|
|
37235
|
-
|
|
37236
|
-
|
|
37254
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts USING fts5(
|
|
37255
|
+
source,
|
|
37256
|
+
heading,
|
|
37257
|
+
content,
|
|
37258
|
+
content='memory_chunks',
|
|
37259
|
+
content_rowid='rowid'
|
|
37260
|
+
);
|
|
37237
37261
|
|
|
37238
|
-
|
|
37239
|
-
|
|
37240
|
-
|
|
37241
|
-
|
|
37242
|
-
|
|
37243
|
-
|
|
37244
|
-
|
|
37245
|
-
|
|
37246
|
-
|
|
37247
|
-
|
|
37248
|
-
|
|
37249
|
-
|
|
37250
|
-
|
|
37251
|
-
|
|
37252
|
-
|
|
37253
|
-
|
|
37254
|
-
|
|
37262
|
+
CREATE TRIGGER IF NOT EXISTS memory_chunks_ai
|
|
37263
|
+
AFTER INSERT ON memory_chunks BEGIN
|
|
37264
|
+
INSERT INTO memory_fts(rowid, source, heading, content)
|
|
37265
|
+
VALUES (new.rowid, new.source, new.heading, new.content);
|
|
37266
|
+
END;
|
|
37267
|
+
|
|
37268
|
+
CREATE TRIGGER IF NOT EXISTS memory_chunks_ad
|
|
37269
|
+
AFTER DELETE ON memory_chunks BEGIN
|
|
37270
|
+
INSERT INTO memory_fts(memory_fts, rowid, source, heading, content)
|
|
37271
|
+
VALUES ('delete', old.rowid, old.source, old.heading, old.content);
|
|
37272
|
+
END;
|
|
37273
|
+
|
|
37274
|
+
CREATE INDEX IF NOT EXISTS idx_memory_chunks_embedding
|
|
37275
|
+
ON memory_chunks (libsql_vector_idx(embedding));
|
|
37276
|
+
`);
|
|
37277
|
+
} catch (err) {
|
|
37278
|
+
throw new StoreError("schema migration failed", err);
|
|
37255
37279
|
}
|
|
37256
37280
|
}
|
|
37281
|
+
readUserVersion(db) {
|
|
37282
|
+
const value = db.pragma("user_version", { simple: true });
|
|
37283
|
+
if (typeof value === "number") return value;
|
|
37284
|
+
if (Array.isArray(value)) {
|
|
37285
|
+
const [row] = value;
|
|
37286
|
+
return typeof row?.user_version === "number" ? row.user_version : 0;
|
|
37287
|
+
}
|
|
37288
|
+
if (value && typeof value === "object" && "user_version" in value) {
|
|
37289
|
+
const row = value;
|
|
37290
|
+
return typeof row.user_version === "number" ? row.user_version : 0;
|
|
37291
|
+
}
|
|
37292
|
+
return 0;
|
|
37293
|
+
}
|
|
37257
37294
|
upsert(chunks, embeddings) {
|
|
37258
37295
|
const insertChunk = this.db.prepare(`
|
|
37259
37296
|
INSERT OR REPLACE INTO memory_chunks
|
|
37260
|
-
(id, source, heading, heading_level, content, line_start, line_end, indexed_at)
|
|
37261
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
37297
|
+
(id, source, source_type, heading, heading_level, content, line_start, line_end, indexed_at, file_mtime_at, embedding)
|
|
37298
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?))
|
|
37262
37299
|
`);
|
|
37263
|
-
let insertVec = null;
|
|
37264
|
-
if (this._vecAvailable) {
|
|
37265
|
-
insertVec = this.db.prepare(`INSERT OR REPLACE INTO memory_vec (rowid, embedding) VALUES (?, ?)`);
|
|
37266
|
-
}
|
|
37267
37300
|
const upsertAll = this.db.transaction(() => {
|
|
37268
37301
|
for (let i = 0; i < chunks.length; i++) {
|
|
37269
37302
|
const chunk = chunks[i];
|
|
37270
37303
|
try {
|
|
37304
|
+
const vectorBinding = this.toVectorBinding(embeddings[i]);
|
|
37271
37305
|
this.db.prepare(`DELETE FROM memory_chunks WHERE id = ?`).run(chunk.id);
|
|
37272
|
-
|
|
37306
|
+
insertChunk.run(
|
|
37273
37307
|
chunk.id,
|
|
37274
37308
|
chunk.source,
|
|
37309
|
+
chunk.sourceType,
|
|
37275
37310
|
chunk.heading,
|
|
37276
37311
|
chunk.headingLevel,
|
|
37277
37312
|
chunk.content,
|
|
37278
37313
|
chunk.lineStart,
|
|
37279
37314
|
chunk.lineEnd,
|
|
37280
|
-
Date.now()
|
|
37315
|
+
Date.now(),
|
|
37316
|
+
chunk.fileMtimeAt,
|
|
37317
|
+
vectorBinding
|
|
37281
37318
|
);
|
|
37282
|
-
if (insertVec && embeddings[i] && info.lastInsertRowid) {
|
|
37283
|
-
const rowid = Number(info.lastInsertRowid);
|
|
37284
|
-
const emb = embeddings[i];
|
|
37285
|
-
const buf = Buffer.from(emb.buffer, emb.byteOffset, emb.byteLength);
|
|
37286
|
-
insertVec.run(rowid, buf);
|
|
37287
|
-
}
|
|
37288
37319
|
} catch (err) {
|
|
37289
37320
|
console.warn(`[memory-store] Failed to upsert chunk ${chunk.id}:`, err);
|
|
37290
37321
|
}
|
|
@@ -37296,6 +37327,20 @@ var MemoryStore = class {
|
|
|
37296
37327
|
throw new StoreError("Full batch upsert failed", err);
|
|
37297
37328
|
}
|
|
37298
37329
|
}
|
|
37330
|
+
toVectorBinding(embedding) {
|
|
37331
|
+
if (!embedding) throw new Error("missing embedding");
|
|
37332
|
+
if (!(embedding instanceof Float32Array)) throw new Error("missing embedding");
|
|
37333
|
+
if (embedding.length !== this.config.vectorDimension) throw new Error("embedding dimension mismatch");
|
|
37334
|
+
const values = Array.from(embedding, (value) => {
|
|
37335
|
+
if (!Number.isFinite(value)) throw new Error("embedding contains non-finite value");
|
|
37336
|
+
return value;
|
|
37337
|
+
});
|
|
37338
|
+
return JSON.stringify(values);
|
|
37339
|
+
}
|
|
37340
|
+
normalizeVectorDistance(distance) {
|
|
37341
|
+
if (distance <= 0) return 1;
|
|
37342
|
+
return Math.max(0, Math.min(1, 1 - distance));
|
|
37343
|
+
}
|
|
37299
37344
|
hashesBySource(source) {
|
|
37300
37345
|
const rows = this.db.prepare(`SELECT id FROM memory_chunks WHERE source = ?`).all(source);
|
|
37301
37346
|
return new Set(rows.map((r) => r.id));
|
|
@@ -37304,13 +37349,22 @@ var MemoryStore = class {
|
|
|
37304
37349
|
const rows = this.db.prepare(`SELECT DISTINCT source FROM memory_chunks`).all();
|
|
37305
37350
|
return rows.map((r) => r.source);
|
|
37306
37351
|
}
|
|
37352
|
+
getRecentSources(options) {
|
|
37353
|
+
const limit = Math.min(Math.max(1, options.limit || 5), 50);
|
|
37354
|
+
const whereClause = options.sourceType ? " WHERE source_type = ?" : "";
|
|
37355
|
+
const params = options.sourceType ? [options.sourceType, limit] : [limit];
|
|
37356
|
+
try {
|
|
37357
|
+
const rows = this.db.prepare(
|
|
37358
|
+
`SELECT source, MAX(file_mtime_at) AS t FROM memory_chunks${whereClause} GROUP BY source ORDER BY t DESC LIMIT ?`
|
|
37359
|
+
).all(...params);
|
|
37360
|
+
return rows.map((row) => ({ source: row.source, fileMtimeAt: row.t }));
|
|
37361
|
+
} catch (err) {
|
|
37362
|
+
console.warn("[memory-store] Recent source lookup failed:", err);
|
|
37363
|
+
return [];
|
|
37364
|
+
}
|
|
37365
|
+
}
|
|
37307
37366
|
deleteBySource(source) {
|
|
37308
|
-
const rows = this.db.prepare(`SELECT rowid FROM memory_chunks WHERE source = ?`).all(source);
|
|
37309
37367
|
const deleteAll = this.db.transaction(() => {
|
|
37310
|
-
if (this._vecAvailable && rows.length > 0) {
|
|
37311
|
-
const placeholders = rows.map(() => "?").join(", ");
|
|
37312
|
-
this.db.prepare(`DELETE FROM memory_vec WHERE rowid IN (${placeholders})`).run(...rows.map((r) => r.rowid));
|
|
37313
|
-
}
|
|
37314
37368
|
this.db.prepare(`DELETE FROM memory_chunks WHERE source = ?`).run(source);
|
|
37315
37369
|
});
|
|
37316
37370
|
deleteAll();
|
|
@@ -37318,30 +37372,24 @@ var MemoryStore = class {
|
|
|
37318
37372
|
deleteByIds(ids) {
|
|
37319
37373
|
if (ids.length === 0) return;
|
|
37320
37374
|
const placeholders = ids.map(() => "?").join(", ");
|
|
37321
|
-
const rows = this.db.prepare(`SELECT rowid FROM memory_chunks WHERE id IN (${placeholders})`).all(...ids);
|
|
37322
37375
|
const deleteAll = this.db.transaction(() => {
|
|
37323
|
-
if (this._vecAvailable && rows.length > 0) {
|
|
37324
|
-
const rPlaceholders = rows.map(() => "?").join(", ");
|
|
37325
|
-
this.db.prepare(`DELETE FROM memory_vec WHERE rowid IN (${rPlaceholders})`).run(...rows.map((r) => r.rowid));
|
|
37326
|
-
}
|
|
37327
37376
|
this.db.prepare(`DELETE FROM memory_chunks WHERE id IN (${placeholders})`).run(...ids);
|
|
37328
37377
|
});
|
|
37329
37378
|
deleteAll();
|
|
37330
37379
|
}
|
|
37331
37380
|
searchDense(embedding, limit) {
|
|
37332
|
-
if (!this._vecAvailable) return [];
|
|
37333
37381
|
try {
|
|
37334
|
-
const
|
|
37382
|
+
const queryVector = this.toVectorBinding(embedding);
|
|
37335
37383
|
const rows = this.db.prepare(
|
|
37336
|
-
`SELECT mc.id,
|
|
37337
|
-
FROM
|
|
37338
|
-
JOIN memory_chunks mc ON mc.rowid =
|
|
37339
|
-
|
|
37340
|
-
|
|
37341
|
-
).all(
|
|
37384
|
+
`SELECT mc.id, vector_distance_cos(mc.embedding, vector32(?)) AS distance
|
|
37385
|
+
FROM vector_top_k('idx_memory_chunks_embedding', vector32(?), ?) AS vector_matches
|
|
37386
|
+
JOIN memory_chunks mc ON mc.rowid = vector_matches.id
|
|
37387
|
+
ORDER BY distance
|
|
37388
|
+
LIMIT ?`
|
|
37389
|
+
).all(queryVector, queryVector, limit, limit);
|
|
37342
37390
|
return rows.map((r) => ({
|
|
37343
37391
|
id: r.id,
|
|
37344
|
-
score:
|
|
37392
|
+
score: this.normalizeVectorDistance(r.distance)
|
|
37345
37393
|
}));
|
|
37346
37394
|
} catch (err) {
|
|
37347
37395
|
console.warn("[memory-store] Dense search failed:", err);
|
|
@@ -37380,21 +37428,23 @@ var MemoryStore = class {
|
|
|
37380
37428
|
if (ids.length === 0) return [];
|
|
37381
37429
|
const placeholders = ids.map(() => "?").join(", ");
|
|
37382
37430
|
const rows = this.db.prepare(
|
|
37383
|
-
`SELECT id, source, heading, heading_level, content, line_start, line_end
|
|
37431
|
+
`SELECT id, source, source_type, heading, heading_level, content, line_start, line_end, file_mtime_at
|
|
37384
37432
|
FROM memory_chunks WHERE id IN (${placeholders})`
|
|
37385
37433
|
).all(...ids);
|
|
37386
37434
|
return rows.map((r) => ({
|
|
37387
37435
|
id: r.id,
|
|
37388
37436
|
source: r.source,
|
|
37437
|
+
sourceType: r.source_type,
|
|
37389
37438
|
heading: r.heading,
|
|
37390
37439
|
headingLevel: r.heading_level,
|
|
37391
37440
|
content: r.content,
|
|
37392
37441
|
lineStart: r.line_start,
|
|
37393
|
-
lineEnd: r.line_end
|
|
37442
|
+
lineEnd: r.line_end,
|
|
37443
|
+
fileMtimeAt: r.file_mtime_at
|
|
37394
37444
|
}));
|
|
37395
37445
|
}
|
|
37396
37446
|
get vecAvailable() {
|
|
37397
|
-
return
|
|
37447
|
+
return true;
|
|
37398
37448
|
}
|
|
37399
37449
|
close() {
|
|
37400
37450
|
this.db.close();
|
|
@@ -37407,8 +37457,8 @@ import * as path13 from "node:path";
|
|
|
37407
37457
|
|
|
37408
37458
|
// src/services/digest/constants.ts
|
|
37409
37459
|
import * as path10 from "node:path";
|
|
37410
|
-
var DEFAULT_DIGEST_MODEL_ID = "
|
|
37411
|
-
var DEFAULT_DIGEST_MAX_INPUT_CHARS =
|
|
37460
|
+
var DEFAULT_DIGEST_MODEL_ID = "base" /* BASE */;
|
|
37461
|
+
var DEFAULT_DIGEST_MAX_INPUT_CHARS = 1e4;
|
|
37412
37462
|
var DEFAULT_DIGEST_TIMEOUT_MS = 12e4;
|
|
37413
37463
|
var KIT_DIR = ".agent-kit";
|
|
37414
37464
|
var WIKI_DIR = path10.join(KIT_DIR, "wiki");
|
|
@@ -37418,7 +37468,7 @@ var DIGEST_WORKER_DIR = path10.join(WIKI_DIR, "digest");
|
|
|
37418
37468
|
var DIGEST_LOCKFILE_REL_PATH = path10.join(DIGEST_WORKER_DIR, "digest-worker.lock");
|
|
37419
37469
|
var DIGEST_WORKER_STATUS_REL_PATH = path10.join(DIGEST_WORKER_DIR, "digest-worker.status.json");
|
|
37420
37470
|
var DIGEST_WORKER_LOG_REL_PATH = path10.join(DIGEST_WORKER_DIR, "digest-worker.log");
|
|
37421
|
-
var LLAMA_CONTEXT_SIZE =
|
|
37471
|
+
var LLAMA_CONTEXT_SIZE = 8192;
|
|
37422
37472
|
var LLAMA_MAX_GENERATED_TOKENS = 512;
|
|
37423
37473
|
var LLAMA_TEMPERATURE = 0.1;
|
|
37424
37474
|
|
|
@@ -37451,9 +37501,9 @@ function writeProvisionalDigestFile(outDir, input, markdown) {
|
|
|
37451
37501
|
return markdownPath;
|
|
37452
37502
|
}
|
|
37453
37503
|
function writeConversationDigestSettings(workspaceRoot2, digest) {
|
|
37454
|
-
const current =
|
|
37504
|
+
const current = loadGlobalSettings();
|
|
37455
37505
|
const memory = current.memory ?? {};
|
|
37456
|
-
|
|
37506
|
+
writeGlobalSettings({
|
|
37457
37507
|
memory: {
|
|
37458
37508
|
...memory,
|
|
37459
37509
|
enabled: memory.enabled ?? true,
|
|
@@ -37478,8 +37528,8 @@ var DIGEST_MODEL_REGISTRY = {
|
|
|
37478
37528
|
sourceUrl: "https://huggingface.co/bartowski/Qwen2.5-0.5B-Instruct-GGUF",
|
|
37479
37529
|
enabled: true
|
|
37480
37530
|
},
|
|
37481
|
-
["
|
|
37482
|
-
id: "
|
|
37531
|
+
["base" /* BASE */]: {
|
|
37532
|
+
id: "base" /* BASE */,
|
|
37483
37533
|
ggufUri: "hf:bartowski/Qwen2.5-1.5B-Instruct-GGUF/Qwen2.5-1.5B-Instruct-Q4_K_M.gguf",
|
|
37484
37534
|
approxSizeBytes: 1e9,
|
|
37485
37535
|
license: "Apache-2.0",
|
|
@@ -37556,39 +37606,19 @@ function buildPrompt(input, maxInputChars) {
|
|
|
37556
37606
|
{
|
|
37557
37607
|
role: "system",
|
|
37558
37608
|
content: [
|
|
37559
|
-
"
|
|
37560
|
-
"
|
|
37561
|
-
"
|
|
37562
|
-
""
|
|
37563
|
-
"# RULES",
|
|
37564
|
-
"1. Read the end of the export FIRST to discover the ultimate resolution.",
|
|
37565
|
-
"2. What the user says LAST ALWAYS overrides earlier agreements.",
|
|
37566
|
-
"3. Capture absolute engineering conclusions, never conversational pleasantries.",
|
|
37567
|
-
"4. Return Markdown only. Do not wrap output in code blocks (```markdown ... ```)."
|
|
37609
|
+
"You are a technical summarizer.",
|
|
37610
|
+
"Focus ONLY on the final decisions made at the end of the transcript.",
|
|
37611
|
+
"Never invent or guess details.",
|
|
37612
|
+
"Keep the output concise and strictly factual."
|
|
37568
37613
|
].join("\n")
|
|
37569
37614
|
},
|
|
37570
37615
|
{
|
|
37571
37616
|
role: "user",
|
|
37572
37617
|
content: [
|
|
37573
|
-
"
|
|
37574
|
-
"",
|
|
37575
|
-
"<layout>",
|
|
37576
|
-
"# Digest: " + titleFromSource(input.sourcePath),
|
|
37577
|
-
"- **Ultimate Core Resolution**: [Describe the final working state of the feature and what problem it solves in 1-2 sentences. No conversational filler.]",
|
|
37578
|
-
"- **Architectural Changes**: [List 3-4 high-level engineering decisions made (e.g., shifts in hook timing, data isolation, atomic locks). Focus on systemic changes, NOT individual files.]",
|
|
37579
|
-
"- **Component State**: [Briefly state how the Plugin side and the MCP/CLI side now interact based on the final decision.]",
|
|
37580
|
-
"- **Considered & Rejected**: [List concepts proposed but discarded (e.g., initial hooks location, mocking strategies). Omit if empty.]",
|
|
37581
|
-
"</layout>",
|
|
37582
|
-
"",
|
|
37583
|
-
"# CRITICAL RULES:",
|
|
37584
|
-
"- DO NOT list individual file paths, line numbers, or specific test cases (EXCLUDE lists of files).",
|
|
37585
|
-
"- Focus entirely on the SYSTEM DESIGN and ARCHITECTURE that the next engineer needs to know.",
|
|
37586
|
-
"- Keep output under 300 tokens. Stop immediately after the last valid section.",
|
|
37587
|
-
"",
|
|
37588
|
-
"Source path: " + input.sourcePath,
|
|
37589
|
-
'<conversation_export format="memory-kit">',
|
|
37618
|
+
"Transcript:",
|
|
37590
37619
|
conversationExport,
|
|
37591
|
-
"
|
|
37620
|
+
"",
|
|
37621
|
+
"Task: Write a brief summary paragraph of the final agreed-upon decisions in the transcript above. Then, provide a simple bulleted list of the specific technical changes or outcomes."
|
|
37592
37622
|
].join("\n")
|
|
37593
37623
|
}
|
|
37594
37624
|
];
|
|
@@ -37651,7 +37681,18 @@ async function createLlamaLocalDigestProvider(modelId) {
|
|
|
37651
37681
|
options.timeoutMs,
|
|
37652
37682
|
() => new LlamaLocalDigestProviderError(`Llama provider timed out after ${options.timeoutMs}ms`)
|
|
37653
37683
|
);
|
|
37654
|
-
|
|
37684
|
+
const metadata = `## Digest: ${titleFromSource(input.sourcePath)}
|
|
37685
|
+
|
|
37686
|
+
| Key | Value |
|
|
37687
|
+
| ------ | ----- |
|
|
37688
|
+
| **Source** | ${input.sourcePath} |
|
|
37689
|
+
| **Generated** | ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]} |
|
|
37690
|
+
| **Model** | ${modelId} |
|
|
37691
|
+
`;
|
|
37692
|
+
const sanitized = sanitizeConversationDigestMarkdown(response);
|
|
37693
|
+
return `${metadata}
|
|
37694
|
+
|
|
37695
|
+
${sanitized}`;
|
|
37655
37696
|
} finally {
|
|
37656
37697
|
await context.dispose();
|
|
37657
37698
|
}
|
|
@@ -37685,19 +37726,25 @@ function discoverPendingConversationFiles(workspaceRoot2) {
|
|
|
37685
37726
|
}
|
|
37686
37727
|
});
|
|
37687
37728
|
}
|
|
37688
|
-
async function indexProvisionalDigestFile(workspaceRoot2, markdownPath) {
|
|
37729
|
+
async function indexProvisionalDigestFile(workspaceRoot2, markdownPath, indexer) {
|
|
37730
|
+
let store;
|
|
37689
37731
|
try {
|
|
37690
|
-
|
|
37691
|
-
|
|
37692
|
-
|
|
37693
|
-
|
|
37694
|
-
|
|
37695
|
-
const
|
|
37696
|
-
|
|
37732
|
+
if (indexer) {
|
|
37733
|
+
await indexer.indexFile(markdownPath);
|
|
37734
|
+
return { indexed: true };
|
|
37735
|
+
}
|
|
37736
|
+
const settings = loadGlobalSettings();
|
|
37737
|
+
const config2 = resolveMemoryConfig(settings, workspaceRoot2);
|
|
37738
|
+
if (config2.enabled !== true) return { indexed: false };
|
|
37739
|
+
store = new MemoryStore(path13.join(config2.wikiDir, "index.db"), config2);
|
|
37740
|
+
const embedder = new Embedder(config2.embeddingModel);
|
|
37741
|
+
await embedder.initialize();
|
|
37742
|
+
await new MemoryIndexer(store, embedder, config2).indexFile(markdownPath);
|
|
37697
37743
|
return { indexed: true };
|
|
37698
37744
|
} catch (err) {
|
|
37699
|
-
|
|
37700
|
-
|
|
37745
|
+
return { indexed: false, error: err instanceof Error ? err.message : String(err) };
|
|
37746
|
+
} finally {
|
|
37747
|
+
store?.close();
|
|
37701
37748
|
}
|
|
37702
37749
|
}
|
|
37703
37750
|
async function digestConversationFile(options) {
|
|
@@ -37705,7 +37752,7 @@ async function digestConversationFile(options) {
|
|
|
37705
37752
|
const input = readConversationDigestInput(options.workspaceRoot, options.inputPath);
|
|
37706
37753
|
const existingPath = findExistingProvisionalDigest(outDir, input);
|
|
37707
37754
|
if (existingPath) {
|
|
37708
|
-
const indexResult = await indexProvisionalDigestFile(options.workspaceRoot, existingPath);
|
|
37755
|
+
const indexResult = await indexProvisionalDigestFile(options.workspaceRoot, existingPath, options.indexer);
|
|
37709
37756
|
return {
|
|
37710
37757
|
markdown: existingPath,
|
|
37711
37758
|
status: "provisional",
|
|
@@ -37722,7 +37769,7 @@ async function digestConversationFile(options) {
|
|
|
37722
37769
|
timeoutMs: options.timeoutMs ?? DEFAULT_DIGEST_TIMEOUT_MS
|
|
37723
37770
|
});
|
|
37724
37771
|
const markdownPath = writeProvisionalDigestFile(outDir, input, markdown);
|
|
37725
|
-
const indexResult = await indexProvisionalDigestFile(options.workspaceRoot, markdownPath);
|
|
37772
|
+
const indexResult = await indexProvisionalDigestFile(options.workspaceRoot, markdownPath, options.indexer);
|
|
37726
37773
|
return {
|
|
37727
37774
|
markdown: markdownPath,
|
|
37728
37775
|
status: "provisional",
|
|
@@ -37739,7 +37786,7 @@ async function digestPendingConversations({
|
|
|
37739
37786
|
digestFn = digestConversationFile,
|
|
37740
37787
|
onProgress
|
|
37741
37788
|
}) {
|
|
37742
|
-
const digestConfig = resolveConversationDigestConfig(
|
|
37789
|
+
const digestConfig = resolveConversationDigestConfig(loadGlobalSettings());
|
|
37743
37790
|
if (!digestConfig || digestConfig.enabled === false) {
|
|
37744
37791
|
return { ok: true, initialized: false, action: "noop", reason: "not-initialized" };
|
|
37745
37792
|
}
|
|
@@ -37747,11 +37794,21 @@ async function digestPendingConversations({
|
|
|
37747
37794
|
if (!tryAcquireProcessLock(lockPath)) {
|
|
37748
37795
|
return { ok: true, initialized: true, action: "noop", reason: "locked" };
|
|
37749
37796
|
}
|
|
37797
|
+
let closeIndexer;
|
|
37750
37798
|
try {
|
|
37751
37799
|
const candidates = discoverPendingConversationFiles(workspaceRoot2);
|
|
37752
37800
|
if (candidates.length === 0) {
|
|
37753
37801
|
return { ok: true, initialized: true, action: "noop", reason: "no-pending" };
|
|
37754
37802
|
}
|
|
37803
|
+
let indexer;
|
|
37804
|
+
const memoryConfig = resolveMemoryConfig(loadGlobalSettings(), workspaceRoot2);
|
|
37805
|
+
if (memoryConfig.enabled === true) {
|
|
37806
|
+
const store = new MemoryStore(path13.join(memoryConfig.wikiDir, "index.db"), memoryConfig);
|
|
37807
|
+
const embedder = new Embedder(memoryConfig.embeddingModel);
|
|
37808
|
+
indexer = new MemoryIndexer(store, embedder, memoryConfig);
|
|
37809
|
+
closeIndexer = () => store.close();
|
|
37810
|
+
await embedder.initialize();
|
|
37811
|
+
}
|
|
37755
37812
|
let count = 0;
|
|
37756
37813
|
let skipped = 0;
|
|
37757
37814
|
let errors = 0;
|
|
@@ -37760,7 +37817,8 @@ async function digestPendingConversations({
|
|
|
37760
37817
|
const result = await digestFn({
|
|
37761
37818
|
workspaceRoot: workspaceRoot2,
|
|
37762
37819
|
inputPath: filePath,
|
|
37763
|
-
modelId: digestConfig.modelId
|
|
37820
|
+
modelId: digestConfig.modelId,
|
|
37821
|
+
indexer
|
|
37764
37822
|
});
|
|
37765
37823
|
if (result.skipped) {
|
|
37766
37824
|
skipped++;
|
|
@@ -37777,9 +37835,17 @@ async function digestPendingConversations({
|
|
|
37777
37835
|
return { ok: true, initialized: true, action: "digested", count, skipped, errors };
|
|
37778
37836
|
} catch (err) {
|
|
37779
37837
|
const message = err instanceof Error ? err.message : String(err);
|
|
37838
|
+
console.warn("[digest] Pending conversation digest failed:", err);
|
|
37780
37839
|
return { ok: false, initialized: true, action: "error", error: message };
|
|
37781
37840
|
} finally {
|
|
37782
37841
|
releaseLock(lockPath);
|
|
37842
|
+
if (closeIndexer) {
|
|
37843
|
+
try {
|
|
37844
|
+
closeIndexer();
|
|
37845
|
+
} catch (err) {
|
|
37846
|
+
console.warn("[digest] Failed to close memory indexer store:", err);
|
|
37847
|
+
}
|
|
37848
|
+
}
|
|
37783
37849
|
}
|
|
37784
37850
|
}
|
|
37785
37851
|
async function initializeConversationDigestModel(input) {
|
|
@@ -37821,19 +37887,18 @@ async function initializeConversationDigestModel(input) {
|
|
|
37821
37887
|
function registerMemoryToolHandlers(server2, indexer, store, config2, workspaceRoot2 = process.cwd()) {
|
|
37822
37888
|
server2.tool(
|
|
37823
37889
|
"kit_memory_search",
|
|
37824
|
-
"Search persistent memory for
|
|
37890
|
+
"Search persistent memory for factual or semantic matches (decisions, concepts, entities). Extract concise keywords only, removing stopwords and filler words from the user request before querying. Use kit_memory_recent for temporal or recency questions.",
|
|
37825
37891
|
{
|
|
37826
|
-
query: external_exports.string().min(1).describe("
|
|
37892
|
+
query: external_exports.string().min(1).describe("Keyword-only search query with stopwords removed"),
|
|
37827
37893
|
top_k: external_exports.number().int().positive().optional().describe("Number of results to return")
|
|
37828
37894
|
},
|
|
37829
37895
|
async ({ query, top_k }) => {
|
|
37830
37896
|
try {
|
|
37831
37897
|
const results = await indexer.search(query, top_k ?? config2.topK);
|
|
37898
|
+
const degradedNote = !store.vecAvailable ? "\u26A0\uFE0F Vector search unavailable \u2014 showing keyword-only results.\n\n" : "";
|
|
37832
37899
|
if (results.length === 0) {
|
|
37833
|
-
|
|
37834
|
-
return mcpText(`${degradedNote2}No memories found for query: "${query}"`);
|
|
37900
|
+
return mcpText(`${degradedNote}No memories found for query: "${query}"`);
|
|
37835
37901
|
}
|
|
37836
|
-
const degradedNote = !store.vecAvailable ? "\u26A0\uFE0F Vector search unavailable \u2014 showing keyword-only results.\n\n" : "";
|
|
37837
37902
|
const formatted = results.map((r) => {
|
|
37838
37903
|
const displaySource = r.chunk.source.replace(/^compiled\//, "");
|
|
37839
37904
|
const content = r.contentSource === "fallback" ? `\u26A0\uFE0F Source file unavailable \u2014 showing matched chunk only:
|
|
@@ -37843,8 +37908,36 @@ ${content}`;
|
|
|
37843
37908
|
}).join("\n\n---\n\n");
|
|
37844
37909
|
return mcpText(`${degradedNote}${formatted}`);
|
|
37845
37910
|
} catch (err) {
|
|
37846
|
-
|
|
37847
|
-
|
|
37911
|
+
return mcpText(`kit_memory_search failed: ${formatError2(err)}`);
|
|
37912
|
+
}
|
|
37913
|
+
}
|
|
37914
|
+
);
|
|
37915
|
+
server2.tool(
|
|
37916
|
+
"kit_memory_recent",
|
|
37917
|
+
'Return the N most recently updated memory sources. Optionally filter by source type. Use for temporal questions like "what did we do last session" or "recent".',
|
|
37918
|
+
{
|
|
37919
|
+
n: external_exports.number().int().positive().max(50).optional().describe("How many recent sources to return (default 5)"),
|
|
37920
|
+
source_type: external_exports.enum(SOURCE_TYPES).optional().describe("Optional source type filter")
|
|
37921
|
+
},
|
|
37922
|
+
async ({ n, source_type }) => {
|
|
37923
|
+
try {
|
|
37924
|
+
const rows = store.getRecentSources({ limit: n ?? 5, sourceType: source_type });
|
|
37925
|
+
if (rows.length === 0) return mcpText("No recent sources found.");
|
|
37926
|
+
const blocks = [];
|
|
37927
|
+
for (const row of rows) {
|
|
37928
|
+
try {
|
|
37929
|
+
const content = fs11.readFileSync(path14.join(config2.wikiDir, row.source), "utf8");
|
|
37930
|
+
const displaySource = row.source.replace(/^compiled\//, "");
|
|
37931
|
+
blocks.push(`### ${displaySource}
|
|
37932
|
+
${content}`);
|
|
37933
|
+
} catch (err) {
|
|
37934
|
+
console.warn(`[memory] Skipping unavailable recent source ${row.source}:`, err);
|
|
37935
|
+
}
|
|
37936
|
+
}
|
|
37937
|
+
if (blocks.length === 0) return mcpText("No recent sources found.");
|
|
37938
|
+
return mcpText(blocks.join("\n\n---\n\n"));
|
|
37939
|
+
} catch (err) {
|
|
37940
|
+
return mcpText(`kit_memory_recent failed: ${formatError2(err)}`);
|
|
37848
37941
|
}
|
|
37849
37942
|
}
|
|
37850
37943
|
);
|
|
@@ -37863,8 +37956,10 @@ ${content}`;
|
|
|
37863
37956
|
message: "Saved to wiki/raw \u2014 will be indexed after next /wiki compile"
|
|
37864
37957
|
});
|
|
37865
37958
|
} catch (err) {
|
|
37866
|
-
|
|
37867
|
-
|
|
37959
|
+
return mcpJson({
|
|
37960
|
+
saved: false,
|
|
37961
|
+
error: formatError2(err)
|
|
37962
|
+
});
|
|
37868
37963
|
}
|
|
37869
37964
|
}
|
|
37870
37965
|
);
|
|
@@ -37877,7 +37972,7 @@ ${content}`;
|
|
|
37877
37972
|
},
|
|
37878
37973
|
async ({ model_id, enabled }) => {
|
|
37879
37974
|
try {
|
|
37880
|
-
const existingConfig = resolveConversationDigestConfig(
|
|
37975
|
+
const existingConfig = resolveConversationDigestConfig(loadGlobalSettings());
|
|
37881
37976
|
const result = await initializeConversationDigestModel({
|
|
37882
37977
|
workspaceRoot: workspaceRoot2,
|
|
37883
37978
|
modelId: model_id ?? existingConfig?.modelId ?? DEFAULT_DIGEST_MODEL_ID,
|
|
@@ -37891,15 +37986,15 @@ ${content}`;
|
|
|
37891
37986
|
error: result.error
|
|
37892
37987
|
});
|
|
37893
37988
|
} catch (err) {
|
|
37894
|
-
|
|
37895
|
-
return mcpJson({ initialized: false, error: message });
|
|
37989
|
+
return mcpJson({ initialized: false, error: formatError2(err) });
|
|
37896
37990
|
}
|
|
37897
37991
|
}
|
|
37898
37992
|
);
|
|
37899
37993
|
}
|
|
37900
|
-
function registerMemoryTools(server2,
|
|
37901
|
-
|
|
37902
|
-
|
|
37994
|
+
function registerMemoryTools(server2, workspaceRoot2, overrides) {
|
|
37995
|
+
const settings = overrides?.settings ?? loadGlobalSettings();
|
|
37996
|
+
if (settings.memory?.enabled !== true) return null;
|
|
37997
|
+
const config2 = overrides?.config ?? resolveMemoryConfig(settings, workspaceRoot2);
|
|
37903
37998
|
const store = overrides?.store ?? new MemoryStore(path14.join(config2.wikiDir, "index.db"), config2);
|
|
37904
37999
|
const indexer = overrides?.indexer ?? (() => {
|
|
37905
38000
|
const embedder = new Embedder(config2.embeddingModel);
|
|
@@ -37918,8 +38013,7 @@ registerIntegrationTools(server);
|
|
|
37918
38013
|
registerCoreTools(server);
|
|
37919
38014
|
registerAgentTools(server);
|
|
37920
38015
|
var workspaceRoot = getWorkspaceRoot();
|
|
37921
|
-
var
|
|
37922
|
-
var memoryIndexer = registerMemoryTools(server, settings, workspaceRoot);
|
|
38016
|
+
var memoryIndexer = registerMemoryTools(server, workspaceRoot);
|
|
37923
38017
|
var transport = new StdioServerTransport();
|
|
37924
38018
|
await server.connect(transport);
|
|
37925
38019
|
if (memoryIndexer) {
|