@hanhnd/agent-kit 1.0.33 → 1.0.35
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 +307 -129
- package/dist/core/config/index.d.ts +3 -0
- package/dist/core/config/index.js +20 -0
- package/dist/core/handoffs/index.d.ts +8 -7
- package/dist/core/handoffs/index.js +12 -4
- package/dist/core/handoffs/index.test.d.ts +1 -0
- package/dist/core/handoffs/index.test.js +88 -0
- package/dist/entrypoints/server.js +1 -3
- package/dist/mcp/agent.d.ts +1 -1
- package/dist/mcp/agent.js +3 -3
- package/dist/mcp/core.d.ts +7 -0
- package/dist/mcp/core.js +39 -25
- package/dist/mcp/core.test.d.ts +1 -0
- package/dist/mcp/core.test.js +98 -0
- package/dist/mcp/memory.d.ts +2 -1
- package/dist/mcp/memory.js +45 -18
- package/dist/server.js +302 -178
- 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 +66 -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 +6 -1
- package/dist/services/memory/store.js +87 -47
- package/dist/services/memory/store.test.js +123 -3
- 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 +2 -2
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
|
}
|
|
@@ -36062,7 +36064,8 @@ var HANDOFF_TYPES = [
|
|
|
36062
36064
|
"ticket",
|
|
36063
36065
|
"research",
|
|
36064
36066
|
"scenario",
|
|
36065
|
-
"investigation"
|
|
36067
|
+
"investigation",
|
|
36068
|
+
"code"
|
|
36066
36069
|
];
|
|
36067
36070
|
var TICKET_ID_PATTERN = /\b[A-Z][A-Z0-9]+-\d+\b/;
|
|
36068
36071
|
function findTicketId(value) {
|
|
@@ -36090,52 +36093,72 @@ function deriveFeatureSlug(input) {
|
|
|
36090
36093
|
if (contentTicketSlug) return contentTicketSlug;
|
|
36091
36094
|
return sanitizeFeatureSlug(contentSlugCandidate(input.content)) || "untitled-handoff";
|
|
36092
36095
|
}
|
|
36093
|
-
function
|
|
36096
|
+
function resolveHandoffFolder(input) {
|
|
36094
36097
|
const canonicalType = normalizeHandoffType(input.type);
|
|
36095
36098
|
const featureSlug = deriveFeatureSlug({
|
|
36096
36099
|
requestedSlug: input.slug,
|
|
36097
|
-
content: input.
|
|
36100
|
+
content: input.primaryContent,
|
|
36098
36101
|
type: canonicalType
|
|
36099
36102
|
});
|
|
36100
|
-
const relativePath = path3.join(".agent-kit", "handoffs", featureSlug,
|
|
36103
|
+
const relativePath = path3.join(".agent-kit", "handoffs", featureSlug, canonicalType);
|
|
36101
36104
|
return {
|
|
36102
36105
|
featureSlug,
|
|
36103
36106
|
canonicalType,
|
|
36104
|
-
|
|
36107
|
+
folderPath: path3.join(input.workspaceRoot, relativePath),
|
|
36105
36108
|
relativePath
|
|
36106
36109
|
};
|
|
36107
36110
|
}
|
|
36111
|
+
function validateHandoffFilename(name) {
|
|
36112
|
+
const normalized = name.trim().replace(/^["']|["']$/g, "");
|
|
36113
|
+
if (!/^[A-Za-z0-9_-]+\.md$/.test(normalized)) {
|
|
36114
|
+
throw new Error(`Unsafe handoff filename: ${name}`);
|
|
36115
|
+
}
|
|
36116
|
+
return normalized;
|
|
36117
|
+
}
|
|
36108
36118
|
|
|
36109
36119
|
// src/mcp/core.ts
|
|
36120
|
+
function handleSaveHandoff(args, workspaceRoot2) {
|
|
36121
|
+
try {
|
|
36122
|
+
if (Object.keys(args.files).length === 0) {
|
|
36123
|
+
return mcpText("Error saving handoff: files cannot be empty");
|
|
36124
|
+
}
|
|
36125
|
+
for (const name of Object.keys(args.files)) {
|
|
36126
|
+
try {
|
|
36127
|
+
validateHandoffFilename(name);
|
|
36128
|
+
} catch (err) {
|
|
36129
|
+
return mcpText(`Error saving handoff: ${err instanceof Error ? err.message : err}`);
|
|
36130
|
+
}
|
|
36131
|
+
}
|
|
36132
|
+
const primaryContent = args.files["README.md"] ?? Object.values(args.files)[0];
|
|
36133
|
+
const location = resolveHandoffFolder({
|
|
36134
|
+
workspaceRoot: workspaceRoot2,
|
|
36135
|
+
type: args.type,
|
|
36136
|
+
slug: args.slug,
|
|
36137
|
+
primaryContent
|
|
36138
|
+
});
|
|
36139
|
+
fs3.mkdirSync(location.folderPath, { recursive: true });
|
|
36140
|
+
for (const [name, content] of Object.entries(args.files)) {
|
|
36141
|
+
fs3.writeFileSync(path4.join(location.folderPath, name), content, "utf8");
|
|
36142
|
+
}
|
|
36143
|
+
return mcpText(`\u2705 Saved to: ${location.folderPath}`);
|
|
36144
|
+
} catch (error2) {
|
|
36145
|
+
return mcpText(`Error saving handoff: ${error2 instanceof Error ? error2.message : error2}`);
|
|
36146
|
+
}
|
|
36147
|
+
}
|
|
36110
36148
|
function registerCoreTools(server2) {
|
|
36111
36149
|
server2.tool(
|
|
36112
36150
|
"kit_save_handoff",
|
|
36113
|
-
`Save a handoff artifact to .agent-kit/handoffs/. Returns the saved
|
|
36151
|
+
`Save a handoff artifact to .agent-kit/handoffs/. Returns the saved folder path to use in next-step instructions. Do NOT append version numbers (v2, v3, etc.) to the slug.`,
|
|
36114
36152
|
{
|
|
36115
36153
|
type: external_exports.enum(HANDOFF_TYPES).describe("Handoff type"),
|
|
36116
|
-
|
|
36154
|
+
files: external_exports.record(external_exports.string(), external_exports.string()).describe(
|
|
36155
|
+
"Markdown files to write into the handoff folder. May include one or many files per call; repeated calls with the same type and slug add or overwrite the provided files. Filenames must match /^[A-Za-z0-9_-]+\\.md$/."
|
|
36156
|
+
),
|
|
36117
36157
|
slug: external_exports.string().describe(
|
|
36118
|
-
'Short identifier for the
|
|
36158
|
+
'Short identifier for the folder, e.g. "user-auth" or "PROJ-123". Do NOT append version numbers (v2, v3, etc.).'
|
|
36119
36159
|
)
|
|
36120
36160
|
},
|
|
36121
|
-
async (
|
|
36122
|
-
try {
|
|
36123
|
-
const location = resolveHandoffPath({
|
|
36124
|
-
workspaceRoot: getWorkspaceRoot(),
|
|
36125
|
-
type,
|
|
36126
|
-
slug,
|
|
36127
|
-
content
|
|
36128
|
-
});
|
|
36129
|
-
const handoffDir = path4.dirname(location.filePath);
|
|
36130
|
-
if (!fs3.existsSync(handoffDir)) {
|
|
36131
|
-
fs3.mkdirSync(handoffDir, { recursive: true });
|
|
36132
|
-
}
|
|
36133
|
-
fs3.writeFileSync(location.filePath, content, "utf8");
|
|
36134
|
-
return mcpText(`\u2705 Saved to: ${location.filePath}`);
|
|
36135
|
-
} catch (error2) {
|
|
36136
|
-
return mcpText(`Error saving handoff: ${error2}`);
|
|
36137
|
-
}
|
|
36138
|
-
}
|
|
36161
|
+
async (args) => handleSaveHandoff(args, getWorkspaceRoot())
|
|
36139
36162
|
);
|
|
36140
36163
|
}
|
|
36141
36164
|
|
|
@@ -36245,10 +36268,10 @@ ${"\u2500".repeat(60)}
|
|
|
36245
36268
|
function registerAgentTools(server2) {
|
|
36246
36269
|
server2.tool(
|
|
36247
36270
|
"kit_trigger_agent",
|
|
36248
|
-
"!Important: Trigger only when user explicitly asks to delegate a task to an external agent CLI (gemini, claude, or codex). The task can be a direct message or a path to a handoff
|
|
36271
|
+
"!Important: Trigger only when user explicitly asks to delegate a task to an external agent CLI (gemini, claude, or codex). The task can be a direct message or a path to a handoff folder (.agent-kit/handoffs/feature-slug/plan).",
|
|
36249
36272
|
{
|
|
36250
36273
|
agent: external_exports.enum(["gemini", "claude", "codex"]).describe('The agent CLI to invoke: "gemini", "claude", or "codex"'),
|
|
36251
|
-
task: external_exports.string().describe('Task message or path to a handoff
|
|
36274
|
+
task: external_exports.string().describe('Task message or path to a handoff folder (e.g., ".agent-kit/handoffs/feature-slug/plan")')
|
|
36252
36275
|
},
|
|
36253
36276
|
async ({ agent, task }, extra) => {
|
|
36254
36277
|
const workspaceRoot2 = getWorkspaceRoot();
|
|
@@ -36362,10 +36385,11 @@ import { writeFileSync as writeFileSync4 } from "fs";
|
|
|
36362
36385
|
import * as fs5 from "fs";
|
|
36363
36386
|
|
|
36364
36387
|
// src/utils/paths.ts
|
|
36365
|
-
import * as
|
|
36388
|
+
import * as os2 from "os";
|
|
36366
36389
|
import * as path7 from "path";
|
|
36367
|
-
var AGENT_KIT_HOME = path7.join(
|
|
36390
|
+
var AGENT_KIT_HOME = path7.join(os2.homedir(), ".agent-kit");
|
|
36368
36391
|
var CREDENTIALS_FILE = path7.join(AGENT_KIT_HOME, "credentials");
|
|
36392
|
+
var GLOBAL_SETTINGS_FILE = path7.join(AGENT_KIT_HOME, "settings.json");
|
|
36369
36393
|
var MODEL_CACHE_DIR = path7.join(AGENT_KIT_HOME, "cache", "models");
|
|
36370
36394
|
var FASTEMBED_CACHE_DIR = path7.join(MODEL_CACHE_DIR, "fastembed");
|
|
36371
36395
|
|
|
@@ -36737,6 +36761,7 @@ ${ticket.fields.labels?.join(", ") || "None"}`;
|
|
|
36737
36761
|
}
|
|
36738
36762
|
|
|
36739
36763
|
// src/mcp/memory.ts
|
|
36764
|
+
import * as fs11 from "fs";
|
|
36740
36765
|
import * as path14 from "path";
|
|
36741
36766
|
|
|
36742
36767
|
// src/services/memory/embedder.ts
|
|
@@ -36786,7 +36811,7 @@ var Embedder = class {
|
|
|
36786
36811
|
const { EmbeddingModel, FlagEmbedding } = await import("fastembed");
|
|
36787
36812
|
const modelMap = {
|
|
36788
36813
|
["tiny" /* TINY */]: EmbeddingModel.AllMiniLML6V2,
|
|
36789
|
-
["
|
|
36814
|
+
["base" /* BASE */]: EmbeddingModel.BGESmallENV15,
|
|
36790
36815
|
["large" /* LARGE */]: EmbeddingModel.BGEBaseENV15
|
|
36791
36816
|
};
|
|
36792
36817
|
const model = modelMap[this.modelName] ?? EmbeddingModel.BGESmallENV15;
|
|
@@ -36898,7 +36923,7 @@ function splitSection(text, maxLen) {
|
|
|
36898
36923
|
if (byPara.every((p) => p.length <= maxLen)) return byPara;
|
|
36899
36924
|
return byPara.flatMap((p) => p.length <= maxLen ? [p] : splitAtLine(p, maxLen));
|
|
36900
36925
|
}
|
|
36901
|
-
function chunkMarkdown(text, source, config2) {
|
|
36926
|
+
function chunkMarkdown(text, source, config2, meta) {
|
|
36902
36927
|
if (!text || !text.trim()) return [];
|
|
36903
36928
|
const { chunkSize, overlapLines } = config2;
|
|
36904
36929
|
const lines = text.split("\n");
|
|
@@ -36937,11 +36962,13 @@ ${sectionText}` : sectionText;
|
|
|
36937
36962
|
chunks.push({
|
|
36938
36963
|
id: computeChunkId(content),
|
|
36939
36964
|
source,
|
|
36965
|
+
sourceType: meta.sourceType,
|
|
36940
36966
|
heading: section.heading,
|
|
36941
36967
|
headingLevel: section.headingLevel,
|
|
36942
36968
|
content,
|
|
36943
36969
|
lineStart: firstLine,
|
|
36944
|
-
lineEnd: lastLine
|
|
36970
|
+
lineEnd: lastLine,
|
|
36971
|
+
fileMtimeAt: meta.fileMtimeAt
|
|
36945
36972
|
});
|
|
36946
36973
|
if (pi === parts.length - 1) {
|
|
36947
36974
|
prevOverlapLines = partLines.slice(-overlapLines);
|
|
@@ -36952,6 +36979,13 @@ ${sectionText}` : sectionText;
|
|
|
36952
36979
|
}
|
|
36953
36980
|
|
|
36954
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
|
+
}
|
|
36955
36989
|
var MemoryIndexer = class {
|
|
36956
36990
|
constructor(store, embedder, config2) {
|
|
36957
36991
|
this.store = store;
|
|
@@ -36972,7 +37006,15 @@ var MemoryIndexer = class {
|
|
|
36972
37006
|
console.warn(`[memory-indexer] Cannot read file ${absolutePath}:`, err);
|
|
36973
37007
|
return { indexed: 0, deleted: 0, skipped: existingChunkIds.size };
|
|
36974
37008
|
}
|
|
36975
|
-
|
|
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 });
|
|
36976
37018
|
const currentChunkIds = new Set(allChunks.map((chunk) => chunk.id));
|
|
36977
37019
|
const toIndex = allChunks.filter((chunk) => !existingChunkIds.has(chunk.id));
|
|
36978
37020
|
const toDelete = [...existingChunkIds].filter((id) => !currentChunkIds.has(id));
|
|
@@ -37059,12 +37101,9 @@ var MemoryIndexer = class {
|
|
|
37059
37101
|
scoreMap.set(r.id, { dense: 1 / (RRF_K + rank + 1), bm25: 0 });
|
|
37060
37102
|
});
|
|
37061
37103
|
bm25Results.forEach((r, rank) => {
|
|
37062
|
-
const
|
|
37063
|
-
|
|
37064
|
-
|
|
37065
|
-
} else {
|
|
37066
|
-
scoreMap.set(r.id, { dense: 0, bm25: 1 / (RRF_K + rank + 1) });
|
|
37067
|
-
}
|
|
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);
|
|
37068
37107
|
});
|
|
37069
37108
|
const hasDense = this.store.vecAvailable && denseResults.length > 0;
|
|
37070
37109
|
const numRetrievers = hasDense ? 2 : 1;
|
|
@@ -37138,7 +37177,10 @@ ${content}
|
|
|
37138
37177
|
var import_stopword = __toESM(require_stopword_cjs(), 1);
|
|
37139
37178
|
import Database from "better-sqlite3";
|
|
37140
37179
|
import * as fs7 from "fs";
|
|
37180
|
+
import { createRequire } from "module";
|
|
37141
37181
|
import * as path9 from "path";
|
|
37182
|
+
var require2 = createRequire(import.meta.url);
|
|
37183
|
+
var SCHEMA_VERSION = 2;
|
|
37142
37184
|
var StoreError = class extends Error {
|
|
37143
37185
|
constructor(message, cause) {
|
|
37144
37186
|
super(message);
|
|
@@ -37178,7 +37220,7 @@ var MemoryStore = class {
|
|
|
37178
37220
|
}
|
|
37179
37221
|
loadVecExtension(db) {
|
|
37180
37222
|
try {
|
|
37181
|
-
const sqliteVec =
|
|
37223
|
+
const sqliteVec = require2("sqlite-vec");
|
|
37182
37224
|
sqliteVec.load(db);
|
|
37183
37225
|
this._vecAvailable = true;
|
|
37184
37226
|
} catch (err) {
|
|
@@ -37187,57 +37229,77 @@ var MemoryStore = class {
|
|
|
37187
37229
|
}
|
|
37188
37230
|
}
|
|
37189
37231
|
createSchema(db) {
|
|
37190
|
-
|
|
37191
|
-
|
|
37192
|
-
|
|
37193
|
-
|
|
37194
|
-
|
|
37195
|
-
|
|
37196
|
-
|
|
37197
|
-
|
|
37198
|
-
|
|
37199
|
-
|
|
37200
|
-
|
|
37201
|
-
|
|
37232
|
+
try {
|
|
37233
|
+
const current = Number(db.pragma("user_version", { simple: true }) ?? 0);
|
|
37234
|
+
if (current < SCHEMA_VERSION) {
|
|
37235
|
+
db.exec(`
|
|
37236
|
+
DROP TRIGGER IF EXISTS memory_chunks_ai;
|
|
37237
|
+
DROP TRIGGER IF EXISTS memory_chunks_ad;
|
|
37238
|
+
DROP TABLE IF EXISTS memory_fts;
|
|
37239
|
+
DROP TABLE IF EXISTS memory_vec;
|
|
37240
|
+
DROP TABLE IF EXISTS memory_chunks;
|
|
37241
|
+
`);
|
|
37242
|
+
db.pragma(`user_version = ${SCHEMA_VERSION}`);
|
|
37243
|
+
}
|
|
37244
|
+
db.exec(`
|
|
37245
|
+
CREATE TABLE IF NOT EXISTS memory_chunks (
|
|
37246
|
+
rowid INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
37247
|
+
id TEXT NOT NULL UNIQUE,
|
|
37248
|
+
source TEXT NOT NULL,
|
|
37249
|
+
source_type TEXT NOT NULL DEFAULT 'wiki',
|
|
37250
|
+
heading TEXT NOT NULL DEFAULT '',
|
|
37251
|
+
heading_level INTEGER NOT NULL DEFAULT 0,
|
|
37252
|
+
content TEXT NOT NULL,
|
|
37253
|
+
line_start INTEGER NOT NULL,
|
|
37254
|
+
line_end INTEGER NOT NULL,
|
|
37255
|
+
indexed_at INTEGER NOT NULL,
|
|
37256
|
+
file_mtime_at INTEGER NOT NULL DEFAULT 0
|
|
37257
|
+
);
|
|
37202
37258
|
|
|
37203
|
-
|
|
37204
|
-
|
|
37205
|
-
heading,
|
|
37206
|
-
content,
|
|
37207
|
-
content='memory_chunks',
|
|
37208
|
-
content_rowid='rowid'
|
|
37209
|
-
);
|
|
37259
|
+
CREATE INDEX IF NOT EXISTS idx_memory_chunks_type_mtime
|
|
37260
|
+
ON memory_chunks (source_type, file_mtime_at DESC);
|
|
37210
37261
|
|
|
37211
|
-
|
|
37212
|
-
|
|
37213
|
-
|
|
37214
|
-
|
|
37215
|
-
|
|
37262
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts USING fts5(
|
|
37263
|
+
source,
|
|
37264
|
+
heading,
|
|
37265
|
+
content,
|
|
37266
|
+
content='memory_chunks',
|
|
37267
|
+
content_rowid='rowid'
|
|
37268
|
+
);
|
|
37216
37269
|
|
|
37217
|
-
|
|
37218
|
-
|
|
37219
|
-
|
|
37220
|
-
|
|
37221
|
-
|
|
37222
|
-
|
|
37223
|
-
|
|
37224
|
-
|
|
37225
|
-
|
|
37226
|
-
|
|
37227
|
-
|
|
37228
|
-
|
|
37229
|
-
|
|
37230
|
-
|
|
37231
|
-
|
|
37232
|
-
|
|
37270
|
+
CREATE TRIGGER IF NOT EXISTS memory_chunks_ai
|
|
37271
|
+
AFTER INSERT ON memory_chunks BEGIN
|
|
37272
|
+
INSERT INTO memory_fts(rowid, source, heading, content)
|
|
37273
|
+
VALUES (new.rowid, new.source, new.heading, new.content);
|
|
37274
|
+
END;
|
|
37275
|
+
|
|
37276
|
+
CREATE TRIGGER IF NOT EXISTS memory_chunks_ad
|
|
37277
|
+
AFTER DELETE ON memory_chunks BEGIN
|
|
37278
|
+
INSERT INTO memory_fts(memory_fts, rowid, source, heading, content)
|
|
37279
|
+
VALUES ('delete', old.rowid, old.source, old.heading, old.content);
|
|
37280
|
+
END;
|
|
37281
|
+
`);
|
|
37282
|
+
if (this._vecAvailable) {
|
|
37283
|
+
try {
|
|
37284
|
+
db.exec(`
|
|
37285
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_vec USING vec0(
|
|
37286
|
+
embedding FLOAT[${this.config.vectorDimension}]
|
|
37287
|
+
);
|
|
37288
|
+
`);
|
|
37289
|
+
} catch (err) {
|
|
37290
|
+
console.warn("[memory-store] Failed to create memory_vec, disabling vector search:", err);
|
|
37291
|
+
this._vecAvailable = false;
|
|
37292
|
+
}
|
|
37233
37293
|
}
|
|
37294
|
+
} catch (err) {
|
|
37295
|
+
throw new StoreError("schema migration failed", err);
|
|
37234
37296
|
}
|
|
37235
37297
|
}
|
|
37236
37298
|
upsert(chunks, embeddings) {
|
|
37237
37299
|
const insertChunk = this.db.prepare(`
|
|
37238
37300
|
INSERT OR REPLACE INTO memory_chunks
|
|
37239
|
-
(id, source, heading, heading_level, content, line_start, line_end, indexed_at)
|
|
37240
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
37301
|
+
(id, source, source_type, heading, heading_level, content, line_start, line_end, indexed_at, file_mtime_at)
|
|
37302
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
37241
37303
|
`);
|
|
37242
37304
|
let insertVec = null;
|
|
37243
37305
|
if (this._vecAvailable) {
|
|
@@ -37251,15 +37313,17 @@ var MemoryStore = class {
|
|
|
37251
37313
|
const info = insertChunk.run(
|
|
37252
37314
|
chunk.id,
|
|
37253
37315
|
chunk.source,
|
|
37316
|
+
chunk.sourceType,
|
|
37254
37317
|
chunk.heading,
|
|
37255
37318
|
chunk.headingLevel,
|
|
37256
37319
|
chunk.content,
|
|
37257
37320
|
chunk.lineStart,
|
|
37258
37321
|
chunk.lineEnd,
|
|
37259
|
-
Date.now()
|
|
37322
|
+
Date.now(),
|
|
37323
|
+
chunk.fileMtimeAt
|
|
37260
37324
|
);
|
|
37261
37325
|
if (insertVec && embeddings[i] && info.lastInsertRowid) {
|
|
37262
|
-
const rowid =
|
|
37326
|
+
const rowid = typeof info.lastInsertRowid === "bigint" ? info.lastInsertRowid : BigInt(info.lastInsertRowid);
|
|
37263
37327
|
const emb = embeddings[i];
|
|
37264
37328
|
const buf = Buffer.from(emb.buffer, emb.byteOffset, emb.byteLength);
|
|
37265
37329
|
insertVec.run(rowid, buf);
|
|
@@ -37283,6 +37347,20 @@ var MemoryStore = class {
|
|
|
37283
37347
|
const rows = this.db.prepare(`SELECT DISTINCT source FROM memory_chunks`).all();
|
|
37284
37348
|
return rows.map((r) => r.source);
|
|
37285
37349
|
}
|
|
37350
|
+
getRecentSources(options) {
|
|
37351
|
+
const limit = Math.min(Math.max(1, options.limit || 5), 50);
|
|
37352
|
+
const whereClause = options.sourceType ? " WHERE source_type = ?" : "";
|
|
37353
|
+
const params = options.sourceType ? [options.sourceType, limit] : [limit];
|
|
37354
|
+
try {
|
|
37355
|
+
const rows = this.db.prepare(
|
|
37356
|
+
`SELECT source, MAX(file_mtime_at) AS t FROM memory_chunks${whereClause} GROUP BY source ORDER BY t DESC LIMIT ?`
|
|
37357
|
+
).all(...params);
|
|
37358
|
+
return rows.map((row) => ({ source: row.source, fileMtimeAt: row.t }));
|
|
37359
|
+
} catch (err) {
|
|
37360
|
+
console.warn("[memory-store] Recent source lookup failed:", err);
|
|
37361
|
+
return [];
|
|
37362
|
+
}
|
|
37363
|
+
}
|
|
37286
37364
|
deleteBySource(source) {
|
|
37287
37365
|
const rows = this.db.prepare(`SELECT rowid FROM memory_chunks WHERE source = ?`).all(source);
|
|
37288
37366
|
const deleteAll = this.db.transaction(() => {
|
|
@@ -37359,17 +37437,19 @@ var MemoryStore = class {
|
|
|
37359
37437
|
if (ids.length === 0) return [];
|
|
37360
37438
|
const placeholders = ids.map(() => "?").join(", ");
|
|
37361
37439
|
const rows = this.db.prepare(
|
|
37362
|
-
`SELECT id, source, heading, heading_level, content, line_start, line_end
|
|
37440
|
+
`SELECT id, source, source_type, heading, heading_level, content, line_start, line_end, file_mtime_at
|
|
37363
37441
|
FROM memory_chunks WHERE id IN (${placeholders})`
|
|
37364
37442
|
).all(...ids);
|
|
37365
37443
|
return rows.map((r) => ({
|
|
37366
37444
|
id: r.id,
|
|
37367
37445
|
source: r.source,
|
|
37446
|
+
sourceType: r.source_type,
|
|
37368
37447
|
heading: r.heading,
|
|
37369
37448
|
headingLevel: r.heading_level,
|
|
37370
37449
|
content: r.content,
|
|
37371
37450
|
lineStart: r.line_start,
|
|
37372
|
-
lineEnd: r.line_end
|
|
37451
|
+
lineEnd: r.line_end,
|
|
37452
|
+
fileMtimeAt: r.file_mtime_at
|
|
37373
37453
|
}));
|
|
37374
37454
|
}
|
|
37375
37455
|
get vecAvailable() {
|
|
@@ -37386,8 +37466,8 @@ import * as path13 from "node:path";
|
|
|
37386
37466
|
|
|
37387
37467
|
// src/services/digest/constants.ts
|
|
37388
37468
|
import * as path10 from "node:path";
|
|
37389
|
-
var DEFAULT_DIGEST_MODEL_ID = "
|
|
37390
|
-
var DEFAULT_DIGEST_MAX_INPUT_CHARS =
|
|
37469
|
+
var DEFAULT_DIGEST_MODEL_ID = "base" /* BASE */;
|
|
37470
|
+
var DEFAULT_DIGEST_MAX_INPUT_CHARS = 1e4;
|
|
37391
37471
|
var DEFAULT_DIGEST_TIMEOUT_MS = 12e4;
|
|
37392
37472
|
var KIT_DIR = ".agent-kit";
|
|
37393
37473
|
var WIKI_DIR = path10.join(KIT_DIR, "wiki");
|
|
@@ -37397,7 +37477,7 @@ var DIGEST_WORKER_DIR = path10.join(WIKI_DIR, "digest");
|
|
|
37397
37477
|
var DIGEST_LOCKFILE_REL_PATH = path10.join(DIGEST_WORKER_DIR, "digest-worker.lock");
|
|
37398
37478
|
var DIGEST_WORKER_STATUS_REL_PATH = path10.join(DIGEST_WORKER_DIR, "digest-worker.status.json");
|
|
37399
37479
|
var DIGEST_WORKER_LOG_REL_PATH = path10.join(DIGEST_WORKER_DIR, "digest-worker.log");
|
|
37400
|
-
var LLAMA_CONTEXT_SIZE =
|
|
37480
|
+
var LLAMA_CONTEXT_SIZE = 8192;
|
|
37401
37481
|
var LLAMA_MAX_GENERATED_TOKENS = 512;
|
|
37402
37482
|
var LLAMA_TEMPERATURE = 0.1;
|
|
37403
37483
|
|
|
@@ -37430,9 +37510,9 @@ function writeProvisionalDigestFile(outDir, input, markdown) {
|
|
|
37430
37510
|
return markdownPath;
|
|
37431
37511
|
}
|
|
37432
37512
|
function writeConversationDigestSettings(workspaceRoot2, digest) {
|
|
37433
|
-
const current =
|
|
37513
|
+
const current = loadGlobalSettings();
|
|
37434
37514
|
const memory = current.memory ?? {};
|
|
37435
|
-
|
|
37515
|
+
writeGlobalSettings({
|
|
37436
37516
|
memory: {
|
|
37437
37517
|
...memory,
|
|
37438
37518
|
enabled: memory.enabled ?? true,
|
|
@@ -37457,8 +37537,8 @@ var DIGEST_MODEL_REGISTRY = {
|
|
|
37457
37537
|
sourceUrl: "https://huggingface.co/bartowski/Qwen2.5-0.5B-Instruct-GGUF",
|
|
37458
37538
|
enabled: true
|
|
37459
37539
|
},
|
|
37460
|
-
["
|
|
37461
|
-
id: "
|
|
37540
|
+
["base" /* BASE */]: {
|
|
37541
|
+
id: "base" /* BASE */,
|
|
37462
37542
|
ggufUri: "hf:bartowski/Qwen2.5-1.5B-Instruct-GGUF/Qwen2.5-1.5B-Instruct-Q4_K_M.gguf",
|
|
37463
37543
|
approxSizeBytes: 1e9,
|
|
37464
37544
|
license: "Apache-2.0",
|
|
@@ -37535,39 +37615,19 @@ function buildPrompt(input, maxInputChars) {
|
|
|
37535
37615
|
{
|
|
37536
37616
|
role: "system",
|
|
37537
37617
|
content: [
|
|
37538
|
-
"
|
|
37539
|
-
"
|
|
37540
|
-
"
|
|
37541
|
-
""
|
|
37542
|
-
"# RULES",
|
|
37543
|
-
"1. Read the end of the export FIRST to discover the ultimate resolution.",
|
|
37544
|
-
"2. What the user says LAST ALWAYS overrides earlier agreements.",
|
|
37545
|
-
"3. Capture absolute engineering conclusions, never conversational pleasantries.",
|
|
37546
|
-
"4. Return Markdown only. Do not wrap output in code blocks (```markdown ... ```)."
|
|
37618
|
+
"You are a technical summarizer.",
|
|
37619
|
+
"Focus ONLY on the final decisions made at the end of the transcript.",
|
|
37620
|
+
"Never invent or guess details.",
|
|
37621
|
+
"Keep the output concise and strictly factual."
|
|
37547
37622
|
].join("\n")
|
|
37548
37623
|
},
|
|
37549
37624
|
{
|
|
37550
37625
|
role: "user",
|
|
37551
37626
|
content: [
|
|
37552
|
-
"
|
|
37553
|
-
"",
|
|
37554
|
-
"<layout>",
|
|
37555
|
-
"# Digest: " + titleFromSource(input.sourcePath),
|
|
37556
|
-
"- **Ultimate Core Resolution**: [Describe the final working state of the feature and what problem it solves in 1-2 sentences. No conversational filler.]",
|
|
37557
|
-
"- **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.]",
|
|
37558
|
-
"- **Component State**: [Briefly state how the Plugin side and the MCP/CLI side now interact based on the final decision.]",
|
|
37559
|
-
"- **Considered & Rejected**: [List concepts proposed but discarded (e.g., initial hooks location, mocking strategies). Omit if empty.]",
|
|
37560
|
-
"</layout>",
|
|
37561
|
-
"",
|
|
37562
|
-
"# CRITICAL RULES:",
|
|
37563
|
-
"- DO NOT list individual file paths, line numbers, or specific test cases (EXCLUDE lists of files).",
|
|
37564
|
-
"- Focus entirely on the SYSTEM DESIGN and ARCHITECTURE that the next engineer needs to know.",
|
|
37565
|
-
"- Keep output under 300 tokens. Stop immediately after the last valid section.",
|
|
37566
|
-
"",
|
|
37567
|
-
"Source path: " + input.sourcePath,
|
|
37568
|
-
'<conversation_export format="memory-kit">',
|
|
37627
|
+
"Transcript:",
|
|
37569
37628
|
conversationExport,
|
|
37570
|
-
"
|
|
37629
|
+
"",
|
|
37630
|
+
"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."
|
|
37571
37631
|
].join("\n")
|
|
37572
37632
|
}
|
|
37573
37633
|
];
|
|
@@ -37630,7 +37690,18 @@ async function createLlamaLocalDigestProvider(modelId) {
|
|
|
37630
37690
|
options.timeoutMs,
|
|
37631
37691
|
() => new LlamaLocalDigestProviderError(`Llama provider timed out after ${options.timeoutMs}ms`)
|
|
37632
37692
|
);
|
|
37633
|
-
|
|
37693
|
+
const metadata = `## Digest: ${titleFromSource(input.sourcePath)}
|
|
37694
|
+
|
|
37695
|
+
| Key | Value |
|
|
37696
|
+
| ------ | ----- |
|
|
37697
|
+
| **Source** | ${input.sourcePath} |
|
|
37698
|
+
| **Generated** | ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]} |
|
|
37699
|
+
| **Model** | ${modelId} |
|
|
37700
|
+
`;
|
|
37701
|
+
const sanitized = sanitizeConversationDigestMarkdown(response);
|
|
37702
|
+
return `${metadata}
|
|
37703
|
+
|
|
37704
|
+
${sanitized}`;
|
|
37634
37705
|
} finally {
|
|
37635
37706
|
await context.dispose();
|
|
37636
37707
|
}
|
|
@@ -37664,19 +37735,25 @@ function discoverPendingConversationFiles(workspaceRoot2) {
|
|
|
37664
37735
|
}
|
|
37665
37736
|
});
|
|
37666
37737
|
}
|
|
37667
|
-
async function indexProvisionalDigestFile(workspaceRoot2, markdownPath) {
|
|
37738
|
+
async function indexProvisionalDigestFile(workspaceRoot2, markdownPath, indexer) {
|
|
37739
|
+
let store;
|
|
37668
37740
|
try {
|
|
37669
|
-
|
|
37670
|
-
|
|
37671
|
-
|
|
37672
|
-
|
|
37673
|
-
|
|
37674
|
-
const
|
|
37675
|
-
|
|
37741
|
+
if (indexer) {
|
|
37742
|
+
await indexer.indexFile(markdownPath);
|
|
37743
|
+
return { indexed: true };
|
|
37744
|
+
}
|
|
37745
|
+
const settings = loadGlobalSettings();
|
|
37746
|
+
const config2 = resolveMemoryConfig(settings, workspaceRoot2);
|
|
37747
|
+
if (config2.enabled !== true) return { indexed: false };
|
|
37748
|
+
store = new MemoryStore(path13.join(config2.wikiDir, "index.db"), config2);
|
|
37749
|
+
const embedder = new Embedder(config2.embeddingModel);
|
|
37750
|
+
await embedder.initialize();
|
|
37751
|
+
await new MemoryIndexer(store, embedder, config2).indexFile(markdownPath);
|
|
37676
37752
|
return { indexed: true };
|
|
37677
37753
|
} catch (err) {
|
|
37678
|
-
|
|
37679
|
-
|
|
37754
|
+
return { indexed: false, error: err instanceof Error ? err.message : String(err) };
|
|
37755
|
+
} finally {
|
|
37756
|
+
store?.close();
|
|
37680
37757
|
}
|
|
37681
37758
|
}
|
|
37682
37759
|
async function digestConversationFile(options) {
|
|
@@ -37684,7 +37761,7 @@ async function digestConversationFile(options) {
|
|
|
37684
37761
|
const input = readConversationDigestInput(options.workspaceRoot, options.inputPath);
|
|
37685
37762
|
const existingPath = findExistingProvisionalDigest(outDir, input);
|
|
37686
37763
|
if (existingPath) {
|
|
37687
|
-
const indexResult = await indexProvisionalDigestFile(options.workspaceRoot, existingPath);
|
|
37764
|
+
const indexResult = await indexProvisionalDigestFile(options.workspaceRoot, existingPath, options.indexer);
|
|
37688
37765
|
return {
|
|
37689
37766
|
markdown: existingPath,
|
|
37690
37767
|
status: "provisional",
|
|
@@ -37701,7 +37778,7 @@ async function digestConversationFile(options) {
|
|
|
37701
37778
|
timeoutMs: options.timeoutMs ?? DEFAULT_DIGEST_TIMEOUT_MS
|
|
37702
37779
|
});
|
|
37703
37780
|
const markdownPath = writeProvisionalDigestFile(outDir, input, markdown);
|
|
37704
|
-
const indexResult = await indexProvisionalDigestFile(options.workspaceRoot, markdownPath);
|
|
37781
|
+
const indexResult = await indexProvisionalDigestFile(options.workspaceRoot, markdownPath, options.indexer);
|
|
37705
37782
|
return {
|
|
37706
37783
|
markdown: markdownPath,
|
|
37707
37784
|
status: "provisional",
|
|
@@ -37718,7 +37795,7 @@ async function digestPendingConversations({
|
|
|
37718
37795
|
digestFn = digestConversationFile,
|
|
37719
37796
|
onProgress
|
|
37720
37797
|
}) {
|
|
37721
|
-
const digestConfig = resolveConversationDigestConfig(
|
|
37798
|
+
const digestConfig = resolveConversationDigestConfig(loadGlobalSettings());
|
|
37722
37799
|
if (!digestConfig || digestConfig.enabled === false) {
|
|
37723
37800
|
return { ok: true, initialized: false, action: "noop", reason: "not-initialized" };
|
|
37724
37801
|
}
|
|
@@ -37726,11 +37803,21 @@ async function digestPendingConversations({
|
|
|
37726
37803
|
if (!tryAcquireProcessLock(lockPath)) {
|
|
37727
37804
|
return { ok: true, initialized: true, action: "noop", reason: "locked" };
|
|
37728
37805
|
}
|
|
37806
|
+
let closeIndexer;
|
|
37729
37807
|
try {
|
|
37730
37808
|
const candidates = discoverPendingConversationFiles(workspaceRoot2);
|
|
37731
37809
|
if (candidates.length === 0) {
|
|
37732
37810
|
return { ok: true, initialized: true, action: "noop", reason: "no-pending" };
|
|
37733
37811
|
}
|
|
37812
|
+
let indexer;
|
|
37813
|
+
const memoryConfig = resolveMemoryConfig(loadGlobalSettings(), workspaceRoot2);
|
|
37814
|
+
if (memoryConfig.enabled === true) {
|
|
37815
|
+
const store = new MemoryStore(path13.join(memoryConfig.wikiDir, "index.db"), memoryConfig);
|
|
37816
|
+
const embedder = new Embedder(memoryConfig.embeddingModel);
|
|
37817
|
+
indexer = new MemoryIndexer(store, embedder, memoryConfig);
|
|
37818
|
+
closeIndexer = () => store.close();
|
|
37819
|
+
await embedder.initialize();
|
|
37820
|
+
}
|
|
37734
37821
|
let count = 0;
|
|
37735
37822
|
let skipped = 0;
|
|
37736
37823
|
let errors = 0;
|
|
@@ -37739,7 +37826,8 @@ async function digestPendingConversations({
|
|
|
37739
37826
|
const result = await digestFn({
|
|
37740
37827
|
workspaceRoot: workspaceRoot2,
|
|
37741
37828
|
inputPath: filePath,
|
|
37742
|
-
modelId: digestConfig.modelId
|
|
37829
|
+
modelId: digestConfig.modelId,
|
|
37830
|
+
indexer
|
|
37743
37831
|
});
|
|
37744
37832
|
if (result.skipped) {
|
|
37745
37833
|
skipped++;
|
|
@@ -37756,9 +37844,17 @@ async function digestPendingConversations({
|
|
|
37756
37844
|
return { ok: true, initialized: true, action: "digested", count, skipped, errors };
|
|
37757
37845
|
} catch (err) {
|
|
37758
37846
|
const message = err instanceof Error ? err.message : String(err);
|
|
37847
|
+
console.warn("[digest] Pending conversation digest failed:", err);
|
|
37759
37848
|
return { ok: false, initialized: true, action: "error", error: message };
|
|
37760
37849
|
} finally {
|
|
37761
37850
|
releaseLock(lockPath);
|
|
37851
|
+
if (closeIndexer) {
|
|
37852
|
+
try {
|
|
37853
|
+
closeIndexer();
|
|
37854
|
+
} catch (err) {
|
|
37855
|
+
console.warn("[digest] Failed to close memory indexer store:", err);
|
|
37856
|
+
}
|
|
37857
|
+
}
|
|
37762
37858
|
}
|
|
37763
37859
|
}
|
|
37764
37860
|
async function initializeConversationDigestModel(input) {
|
|
@@ -37800,19 +37896,18 @@ async function initializeConversationDigestModel(input) {
|
|
|
37800
37896
|
function registerMemoryToolHandlers(server2, indexer, store, config2, workspaceRoot2 = process.cwd()) {
|
|
37801
37897
|
server2.tool(
|
|
37802
37898
|
"kit_memory_search",
|
|
37803
|
-
"Search persistent memory for
|
|
37899
|
+
"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.",
|
|
37804
37900
|
{
|
|
37805
|
-
query: external_exports.string().min(1).describe("
|
|
37901
|
+
query: external_exports.string().min(1).describe("Keyword-only search query with stopwords removed"),
|
|
37806
37902
|
top_k: external_exports.number().int().positive().optional().describe("Number of results to return")
|
|
37807
37903
|
},
|
|
37808
37904
|
async ({ query, top_k }) => {
|
|
37809
37905
|
try {
|
|
37810
37906
|
const results = await indexer.search(query, top_k ?? config2.topK);
|
|
37907
|
+
const degradedNote = !store.vecAvailable ? "\u26A0\uFE0F Vector search unavailable \u2014 showing keyword-only results.\n\n" : "";
|
|
37811
37908
|
if (results.length === 0) {
|
|
37812
|
-
|
|
37813
|
-
return mcpText(`${degradedNote2}No memories found for query: "${query}"`);
|
|
37909
|
+
return mcpText(`${degradedNote}No memories found for query: "${query}"`);
|
|
37814
37910
|
}
|
|
37815
|
-
const degradedNote = !store.vecAvailable ? "\u26A0\uFE0F Vector search unavailable \u2014 showing keyword-only results.\n\n" : "";
|
|
37816
37911
|
const formatted = results.map((r) => {
|
|
37817
37912
|
const displaySource = r.chunk.source.replace(/^compiled\//, "");
|
|
37818
37913
|
const content = r.contentSource === "fallback" ? `\u26A0\uFE0F Source file unavailable \u2014 showing matched chunk only:
|
|
@@ -37822,8 +37917,36 @@ ${content}`;
|
|
|
37822
37917
|
}).join("\n\n---\n\n");
|
|
37823
37918
|
return mcpText(`${degradedNote}${formatted}`);
|
|
37824
37919
|
} catch (err) {
|
|
37825
|
-
|
|
37826
|
-
|
|
37920
|
+
return mcpText(`kit_memory_search failed: ${formatError2(err)}`);
|
|
37921
|
+
}
|
|
37922
|
+
}
|
|
37923
|
+
);
|
|
37924
|
+
server2.tool(
|
|
37925
|
+
"kit_memory_recent",
|
|
37926
|
+
'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".',
|
|
37927
|
+
{
|
|
37928
|
+
n: external_exports.number().int().positive().max(50).optional().describe("How many recent sources to return (default 5)"),
|
|
37929
|
+
source_type: external_exports.enum(SOURCE_TYPES).optional().describe("Optional source type filter")
|
|
37930
|
+
},
|
|
37931
|
+
async ({ n, source_type }) => {
|
|
37932
|
+
try {
|
|
37933
|
+
const rows = store.getRecentSources({ limit: n ?? 5, sourceType: source_type });
|
|
37934
|
+
if (rows.length === 0) return mcpText("No recent sources found.");
|
|
37935
|
+
const blocks = [];
|
|
37936
|
+
for (const row of rows) {
|
|
37937
|
+
try {
|
|
37938
|
+
const content = fs11.readFileSync(path14.join(config2.wikiDir, row.source), "utf8");
|
|
37939
|
+
const displaySource = row.source.replace(/^compiled\//, "");
|
|
37940
|
+
blocks.push(`### ${displaySource}
|
|
37941
|
+
${content}`);
|
|
37942
|
+
} catch (err) {
|
|
37943
|
+
console.warn(`[memory] Skipping unavailable recent source ${row.source}:`, err);
|
|
37944
|
+
}
|
|
37945
|
+
}
|
|
37946
|
+
if (blocks.length === 0) return mcpText("No recent sources found.");
|
|
37947
|
+
return mcpText(blocks.join("\n\n---\n\n"));
|
|
37948
|
+
} catch (err) {
|
|
37949
|
+
return mcpText(`kit_memory_recent failed: ${formatError2(err)}`);
|
|
37827
37950
|
}
|
|
37828
37951
|
}
|
|
37829
37952
|
);
|
|
@@ -37842,8 +37965,10 @@ ${content}`;
|
|
|
37842
37965
|
message: "Saved to wiki/raw \u2014 will be indexed after next /wiki compile"
|
|
37843
37966
|
});
|
|
37844
37967
|
} catch (err) {
|
|
37845
|
-
|
|
37846
|
-
|
|
37968
|
+
return mcpJson({
|
|
37969
|
+
saved: false,
|
|
37970
|
+
error: formatError2(err)
|
|
37971
|
+
});
|
|
37847
37972
|
}
|
|
37848
37973
|
}
|
|
37849
37974
|
);
|
|
@@ -37856,7 +37981,7 @@ ${content}`;
|
|
|
37856
37981
|
},
|
|
37857
37982
|
async ({ model_id, enabled }) => {
|
|
37858
37983
|
try {
|
|
37859
|
-
const existingConfig = resolveConversationDigestConfig(
|
|
37984
|
+
const existingConfig = resolveConversationDigestConfig(loadGlobalSettings());
|
|
37860
37985
|
const result = await initializeConversationDigestModel({
|
|
37861
37986
|
workspaceRoot: workspaceRoot2,
|
|
37862
37987
|
modelId: model_id ?? existingConfig?.modelId ?? DEFAULT_DIGEST_MODEL_ID,
|
|
@@ -37870,15 +37995,15 @@ ${content}`;
|
|
|
37870
37995
|
error: result.error
|
|
37871
37996
|
});
|
|
37872
37997
|
} catch (err) {
|
|
37873
|
-
|
|
37874
|
-
return mcpJson({ initialized: false, error: message });
|
|
37998
|
+
return mcpJson({ initialized: false, error: formatError2(err) });
|
|
37875
37999
|
}
|
|
37876
38000
|
}
|
|
37877
38001
|
);
|
|
37878
38002
|
}
|
|
37879
|
-
function registerMemoryTools(server2,
|
|
37880
|
-
|
|
37881
|
-
|
|
38003
|
+
function registerMemoryTools(server2, workspaceRoot2, overrides) {
|
|
38004
|
+
const settings = overrides?.settings ?? loadGlobalSettings();
|
|
38005
|
+
if (settings.memory?.enabled !== true) return null;
|
|
38006
|
+
const config2 = overrides?.config ?? resolveMemoryConfig(settings, workspaceRoot2);
|
|
37882
38007
|
const store = overrides?.store ?? new MemoryStore(path14.join(config2.wikiDir, "index.db"), config2);
|
|
37883
38008
|
const indexer = overrides?.indexer ?? (() => {
|
|
37884
38009
|
const embedder = new Embedder(config2.embeddingModel);
|
|
@@ -37897,8 +38022,7 @@ registerIntegrationTools(server);
|
|
|
37897
38022
|
registerCoreTools(server);
|
|
37898
38023
|
registerAgentTools(server);
|
|
37899
38024
|
var workspaceRoot = getWorkspaceRoot();
|
|
37900
|
-
var
|
|
37901
|
-
var memoryIndexer = registerMemoryTools(server, settings, workspaceRoot);
|
|
38025
|
+
var memoryIndexer = registerMemoryTools(server, workspaceRoot);
|
|
37902
38026
|
var transport = new StdioServerTransport();
|
|
37903
38027
|
await server.connect(transport);
|
|
37904
38028
|
if (memoryIndexer) {
|