@hanhnd/agent-kit 1.0.39 → 1.0.41
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.js +55 -15
- package/dist/core/handoffs/index.d.ts +2 -2
- package/dist/core/handoffs/index.js +0 -1
- package/dist/entrypoints/server.js +3 -3
- package/dist/mcp/agent.js +12 -5
- package/dist/mcp/core.js +13 -8
- package/dist/mcp/integration.js +20 -10
- package/dist/mcp/memory.d.ts +4 -9
- package/dist/mcp/memory.js +73 -29
- package/dist/mcp/registration.test.d.ts +1 -0
- package/dist/mcp/registration.test.js +77 -0
- package/dist/server.js +281 -123
- package/dist/services/memory/index.d.ts +3 -1
- package/dist/services/memory/index.js +1 -0
- package/dist/services/memory/indexer.d.ts +5 -6
- package/dist/services/memory/indexer.js +36 -15
- package/dist/services/memory/indexer.test.js +132 -0
- package/dist/services/memory/memory.test.js +61 -2
- package/dist/services/memory/subsystem.d.ts +19 -0
- package/dist/services/memory/subsystem.js +67 -0
- package/dist/services/memory/types.d.ts +20 -0
- package/dist/utils/async.d.ts +1 -0
- package/dist/utils/async.js +17 -0
- package/dist/utils/async.test.d.ts +1 -0
- package/dist/utils/async.test.js +48 -0
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -3222,8 +3222,8 @@ var require_utils = __commonJS({
|
|
|
3222
3222
|
}
|
|
3223
3223
|
return ind;
|
|
3224
3224
|
}
|
|
3225
|
-
function removeDotSegments(
|
|
3226
|
-
let input =
|
|
3225
|
+
function removeDotSegments(path16) {
|
|
3226
|
+
let input = path16;
|
|
3227
3227
|
const output = [];
|
|
3228
3228
|
let nextSlash = -1;
|
|
3229
3229
|
let len = 0;
|
|
@@ -3422,8 +3422,8 @@ var require_schemes = __commonJS({
|
|
|
3422
3422
|
wsComponent.secure = void 0;
|
|
3423
3423
|
}
|
|
3424
3424
|
if (wsComponent.resourceName) {
|
|
3425
|
-
const [
|
|
3426
|
-
wsComponent.path =
|
|
3425
|
+
const [path16, query] = wsComponent.resourceName.split("?");
|
|
3426
|
+
wsComponent.path = path16 && path16 !== "/" ? path16 : void 0;
|
|
3427
3427
|
wsComponent.query = query;
|
|
3428
3428
|
wsComponent.resourceName = void 0;
|
|
3429
3429
|
}
|
|
@@ -22164,8 +22164,8 @@ function getErrorMap() {
|
|
|
22164
22164
|
|
|
22165
22165
|
// ../node_modules/zod/v3/helpers/parseUtil.js
|
|
22166
22166
|
var makeIssue = (params) => {
|
|
22167
|
-
const { data, path:
|
|
22168
|
-
const fullPath = [...
|
|
22167
|
+
const { data, path: path16, errorMaps, issueData } = params;
|
|
22168
|
+
const fullPath = [...path16, ...issueData.path || []];
|
|
22169
22169
|
const fullIssue = {
|
|
22170
22170
|
...issueData,
|
|
22171
22171
|
path: fullPath
|
|
@@ -22281,11 +22281,11 @@ var errorUtil;
|
|
|
22281
22281
|
|
|
22282
22282
|
// ../node_modules/zod/v3/types.js
|
|
22283
22283
|
var ParseInputLazyPath = class {
|
|
22284
|
-
constructor(parent, value,
|
|
22284
|
+
constructor(parent, value, path16, key) {
|
|
22285
22285
|
this._cachedPath = [];
|
|
22286
22286
|
this.parent = parent;
|
|
22287
22287
|
this.data = value;
|
|
22288
|
-
this._path =
|
|
22288
|
+
this._path = path16;
|
|
22289
22289
|
this._key = key;
|
|
22290
22290
|
}
|
|
22291
22291
|
get path() {
|
|
@@ -25923,10 +25923,10 @@ function assignProp(target, prop, value) {
|
|
|
25923
25923
|
configurable: true
|
|
25924
25924
|
});
|
|
25925
25925
|
}
|
|
25926
|
-
function getElementAtPath(obj,
|
|
25927
|
-
if (!
|
|
25926
|
+
function getElementAtPath(obj, path16) {
|
|
25927
|
+
if (!path16)
|
|
25928
25928
|
return obj;
|
|
25929
|
-
return
|
|
25929
|
+
return path16.reduce((acc, key) => acc?.[key], obj);
|
|
25930
25930
|
}
|
|
25931
25931
|
function promiseAllObject(promisesObj) {
|
|
25932
25932
|
const keys = Object.keys(promisesObj);
|
|
@@ -26246,11 +26246,11 @@ function aborted(x, startIndex = 0) {
|
|
|
26246
26246
|
}
|
|
26247
26247
|
return false;
|
|
26248
26248
|
}
|
|
26249
|
-
function prefixIssues(
|
|
26249
|
+
function prefixIssues(path16, issues) {
|
|
26250
26250
|
return issues.map((iss) => {
|
|
26251
26251
|
var _a;
|
|
26252
26252
|
(_a = iss).path ?? (_a.path = []);
|
|
26253
|
-
iss.path.unshift(
|
|
26253
|
+
iss.path.unshift(path16);
|
|
26254
26254
|
return iss;
|
|
26255
26255
|
});
|
|
26256
26256
|
}
|
|
@@ -36078,7 +36078,6 @@ var HANDOFF_TYPES = [
|
|
|
36078
36078
|
"plan",
|
|
36079
36079
|
"ticket",
|
|
36080
36080
|
"research",
|
|
36081
|
-
"scenario",
|
|
36082
36081
|
"investigation",
|
|
36083
36082
|
"code"
|
|
36084
36083
|
];
|
|
@@ -36161,17 +36160,21 @@ function handleSaveHandoff(args, workspaceRoot2) {
|
|
|
36161
36160
|
}
|
|
36162
36161
|
}
|
|
36163
36162
|
function registerCoreTools(server2) {
|
|
36164
|
-
server2.
|
|
36163
|
+
server2.registerTool(
|
|
36165
36164
|
"kit_save_handoff",
|
|
36166
|
-
`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.`,
|
|
36167
36165
|
{
|
|
36168
|
-
|
|
36169
|
-
|
|
36170
|
-
|
|
36171
|
-
|
|
36172
|
-
|
|
36173
|
-
|
|
36174
|
-
|
|
36166
|
+
title: "Save Handoff Artifact",
|
|
36167
|
+
description: `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.`,
|
|
36168
|
+
inputSchema: {
|
|
36169
|
+
type: external_exports.enum(HANDOFF_TYPES).describe("Handoff type"),
|
|
36170
|
+
files: external_exports.record(external_exports.string(), external_exports.string()).describe(
|
|
36171
|
+
"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$/."
|
|
36172
|
+
),
|
|
36173
|
+
slug: external_exports.string().describe(
|
|
36174
|
+
'Short identifier for the folder, e.g. "user-auth" or "PROJ-123". Do NOT append version numbers (v2, v3, etc.).'
|
|
36175
|
+
)
|
|
36176
|
+
},
|
|
36177
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
36175
36178
|
},
|
|
36176
36179
|
async (args) => handleSaveHandoff(args, getWorkspaceRoot())
|
|
36177
36180
|
);
|
|
@@ -36281,12 +36284,16 @@ ${"\u2500".repeat(60)}
|
|
|
36281
36284
|
return { logPath: path6.relative(workspaceRoot2, logFile), logStream };
|
|
36282
36285
|
}
|
|
36283
36286
|
function registerAgentTools(server2) {
|
|
36284
|
-
server2.
|
|
36287
|
+
server2.registerTool(
|
|
36285
36288
|
"kit_trigger_agent",
|
|
36286
|
-
"!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).",
|
|
36287
36289
|
{
|
|
36288
|
-
|
|
36289
|
-
|
|
36290
|
+
title: "Delegate to External Agent",
|
|
36291
|
+
description: "!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).",
|
|
36292
|
+
inputSchema: {
|
|
36293
|
+
agent: external_exports.enum(["gemini", "claude", "codex"]).describe('The agent CLI to invoke: "gemini", "claude", or "codex"'),
|
|
36294
|
+
task: external_exports.string().describe('Task message or path to a handoff folder (e.g., ".agent-kit/handoffs/feature-slug/plan")')
|
|
36295
|
+
},
|
|
36296
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true }
|
|
36290
36297
|
},
|
|
36291
36298
|
async ({ agent, task }, extra) => {
|
|
36292
36299
|
const workspaceRoot2 = getWorkspaceRoot();
|
|
@@ -36641,14 +36648,18 @@ async function callRestApi(url, auth, accept = "application/json") {
|
|
|
36641
36648
|
return accept === "text/plain" ? resp.text() : resp.json();
|
|
36642
36649
|
}
|
|
36643
36650
|
function registerIntegrationTools(server2) {
|
|
36644
|
-
server2.
|
|
36651
|
+
server2.registerTool(
|
|
36645
36652
|
"kit_get_bitbucket_pr",
|
|
36646
|
-
"Get Bitbucket PR details and optionally the diff. Accepts a full PR URL or a numeric PR ID with workspace + repoSlug.",
|
|
36647
36653
|
{
|
|
36648
|
-
|
|
36649
|
-
|
|
36650
|
-
|
|
36651
|
-
|
|
36654
|
+
title: "Get Bitbucket PR",
|
|
36655
|
+
description: "Get Bitbucket PR details and optionally the diff. Accepts a full PR URL or a numeric PR ID with workspace + repoSlug.",
|
|
36656
|
+
inputSchema: {
|
|
36657
|
+
input: external_exports.string().describe("Bitbucket PR URL or numeric PR ID"),
|
|
36658
|
+
workspace: external_exports.string().optional().describe("Bitbucket workspace slug (required for numeric ID if BITBUCKET_DEFAULT_WORKSPACE not set)"),
|
|
36659
|
+
repoSlug: external_exports.string().optional().describe("Bitbucket repo slug (required for numeric ID)"),
|
|
36660
|
+
includeDiff: external_exports.boolean().optional().default(true).describe("Include unified diff in response")
|
|
36661
|
+
},
|
|
36662
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true }
|
|
36652
36663
|
},
|
|
36653
36664
|
async ({ input, workspace, repoSlug, includeDiff }) => {
|
|
36654
36665
|
try {
|
|
@@ -36725,11 +36736,15 @@ ${diff}
|
|
|
36725
36736
|
}
|
|
36726
36737
|
}
|
|
36727
36738
|
);
|
|
36728
|
-
server2.
|
|
36739
|
+
server2.registerTool(
|
|
36729
36740
|
"kit_jira_get_ticket",
|
|
36730
|
-
"Get ticket details from Jira using the Atlassian REST API",
|
|
36731
36741
|
{
|
|
36732
|
-
|
|
36742
|
+
title: "Get Jira Ticket",
|
|
36743
|
+
description: "Get ticket details from Jira using the Atlassian REST API",
|
|
36744
|
+
inputSchema: {
|
|
36745
|
+
ticketId: external_exports.string().describe("Jira ticket ID (e.g., PROJ-123)")
|
|
36746
|
+
},
|
|
36747
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true }
|
|
36733
36748
|
},
|
|
36734
36749
|
async ({ ticketId }) => {
|
|
36735
36750
|
try {
|
|
@@ -36777,7 +36792,10 @@ ${ticket.fields.labels?.join(", ") || "None"}`;
|
|
|
36777
36792
|
|
|
36778
36793
|
// src/mcp/memory.ts
|
|
36779
36794
|
import * as fs11 from "fs";
|
|
36780
|
-
import * as
|
|
36795
|
+
import * as path15 from "path";
|
|
36796
|
+
|
|
36797
|
+
// src/services/memory/subsystem.ts
|
|
36798
|
+
import * as path10 from "path";
|
|
36781
36799
|
|
|
36782
36800
|
// src/services/memory/embedder.ts
|
|
36783
36801
|
var EmbedderError = class extends Error {
|
|
@@ -36993,6 +37011,37 @@ ${sectionText}` : sectionText;
|
|
|
36993
37011
|
return chunks;
|
|
36994
37012
|
}
|
|
36995
37013
|
|
|
37014
|
+
// src/utils/async.ts
|
|
37015
|
+
async function withTimeout(promise, timeoutMs, createError) {
|
|
37016
|
+
let timer;
|
|
37017
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
37018
|
+
timer = setTimeout(() => {
|
|
37019
|
+
reject(createError());
|
|
37020
|
+
}, timeoutMs);
|
|
37021
|
+
});
|
|
37022
|
+
try {
|
|
37023
|
+
return await Promise.race([promise, timeout]);
|
|
37024
|
+
} finally {
|
|
37025
|
+
if (timer) clearTimeout(timer);
|
|
37026
|
+
}
|
|
37027
|
+
}
|
|
37028
|
+
async function mapLimit(items, limit, worker) {
|
|
37029
|
+
if (items.length === 0) return [];
|
|
37030
|
+
const concurrency = Math.max(1, Math.floor(limit));
|
|
37031
|
+
const results = new Array(items.length);
|
|
37032
|
+
let nextIndex = 0;
|
|
37033
|
+
async function runWorker() {
|
|
37034
|
+
while (nextIndex < items.length) {
|
|
37035
|
+
const currentIndex = nextIndex;
|
|
37036
|
+
nextIndex++;
|
|
37037
|
+
results[currentIndex] = await worker(items[currentIndex], currentIndex);
|
|
37038
|
+
}
|
|
37039
|
+
}
|
|
37040
|
+
const runners = Array.from({ length: Math.min(concurrency, items.length) }, () => runWorker());
|
|
37041
|
+
await Promise.all(runners);
|
|
37042
|
+
return results;
|
|
37043
|
+
}
|
|
37044
|
+
|
|
36996
37045
|
// src/services/memory/indexer.ts
|
|
36997
37046
|
function deriveSourceType(source) {
|
|
36998
37047
|
if (source.startsWith("compiled/provisional/conversation-digests/")) return "digest";
|
|
@@ -37009,9 +37058,19 @@ var MemoryIndexer = class {
|
|
|
37009
37058
|
}
|
|
37010
37059
|
_ready = false;
|
|
37011
37060
|
async indexFile(absolutePath) {
|
|
37012
|
-
|
|
37061
|
+
const mutation = await this.prepareIndexFileRelativeTo(absolutePath, this.config.wikiDir);
|
|
37062
|
+
return this.commitPreparedMutation(mutation);
|
|
37013
37063
|
}
|
|
37014
|
-
|
|
37064
|
+
makeSkippedMutation(source, skipped) {
|
|
37065
|
+
return {
|
|
37066
|
+
source,
|
|
37067
|
+
chunksToUpsert: [],
|
|
37068
|
+
embeddings: [],
|
|
37069
|
+
idsToDelete: [],
|
|
37070
|
+
stats: { indexed: 0, deleted: 0, skipped }
|
|
37071
|
+
};
|
|
37072
|
+
}
|
|
37073
|
+
async prepareIndexFileRelativeTo(absolutePath, sourceRoot) {
|
|
37015
37074
|
const source = path8.relative(sourceRoot, absolutePath);
|
|
37016
37075
|
const existingChunkIds = this.store.hashesBySource(source);
|
|
37017
37076
|
let text;
|
|
@@ -37019,7 +37078,7 @@ var MemoryIndexer = class {
|
|
|
37019
37078
|
text = await fs6.promises.readFile(absolutePath, "utf8");
|
|
37020
37079
|
} catch (err) {
|
|
37021
37080
|
console.warn(`[memory-indexer] Cannot read file ${absolutePath}:`, err);
|
|
37022
|
-
return
|
|
37081
|
+
return this.makeSkippedMutation(source, existingChunkIds.size);
|
|
37023
37082
|
}
|
|
37024
37083
|
let fileMtimeAt;
|
|
37025
37084
|
try {
|
|
@@ -37039,21 +37098,30 @@ var MemoryIndexer = class {
|
|
|
37039
37098
|
embeddings = await this.embedder.embed(toIndex.map((c) => c.content));
|
|
37040
37099
|
} catch (err) {
|
|
37041
37100
|
console.warn(`[memory-indexer] Embedding failed for ${absolutePath}:`, err);
|
|
37042
|
-
return
|
|
37101
|
+
return this.makeSkippedMutation(source, existingChunkIds.size);
|
|
37043
37102
|
}
|
|
37044
37103
|
}
|
|
37045
|
-
if (toIndex.length > 0) {
|
|
37046
|
-
this.store.upsert(toIndex, embeddings);
|
|
37047
|
-
}
|
|
37048
|
-
if (toDelete.length > 0) {
|
|
37049
|
-
this.store.deleteByIds(toDelete);
|
|
37050
|
-
}
|
|
37051
37104
|
return {
|
|
37052
|
-
|
|
37053
|
-
|
|
37054
|
-
|
|
37105
|
+
source,
|
|
37106
|
+
chunksToUpsert: toIndex,
|
|
37107
|
+
embeddings,
|
|
37108
|
+
idsToDelete: toDelete,
|
|
37109
|
+
stats: {
|
|
37110
|
+
indexed: toIndex.length,
|
|
37111
|
+
deleted: toDelete.length,
|
|
37112
|
+
skipped: allChunks.length - toIndex.length
|
|
37113
|
+
}
|
|
37055
37114
|
};
|
|
37056
37115
|
}
|
|
37116
|
+
commitPreparedMutation(mutation) {
|
|
37117
|
+
if (mutation.chunksToUpsert.length > 0) {
|
|
37118
|
+
this.store.upsert(mutation.chunksToUpsert, mutation.embeddings);
|
|
37119
|
+
}
|
|
37120
|
+
if (mutation.idsToDelete.length > 0) {
|
|
37121
|
+
this.store.deleteByIds(mutation.idsToDelete);
|
|
37122
|
+
}
|
|
37123
|
+
return mutation.stats;
|
|
37124
|
+
}
|
|
37057
37125
|
async indexDirectory(rootDir, opts = {}) {
|
|
37058
37126
|
const totals = { indexed: 0, deleted: 0, skipped: 0 };
|
|
37059
37127
|
let rootExists;
|
|
@@ -37095,8 +37163,13 @@ var MemoryIndexer = class {
|
|
|
37095
37163
|
totals.deleted += 1;
|
|
37096
37164
|
}
|
|
37097
37165
|
}
|
|
37098
|
-
|
|
37099
|
-
|
|
37166
|
+
const preparedMutations = await mapLimit(
|
|
37167
|
+
files,
|
|
37168
|
+
opts.fileConcurrency ?? 2,
|
|
37169
|
+
(file) => this.prepareIndexFileRelativeTo(file, relativeBase)
|
|
37170
|
+
);
|
|
37171
|
+
for (const mutation of preparedMutations) {
|
|
37172
|
+
const stats = this.commitPreparedMutation(mutation);
|
|
37100
37173
|
totals.indexed += stats.indexed;
|
|
37101
37174
|
totals.deleted += stats.deleted;
|
|
37102
37175
|
totals.skipped += stats.skipped;
|
|
@@ -37609,45 +37682,103 @@ var MemoryStore = class {
|
|
|
37609
37682
|
}
|
|
37610
37683
|
};
|
|
37611
37684
|
|
|
37685
|
+
// src/services/memory/subsystem.ts
|
|
37686
|
+
var DefaultMemorySubsystem = class {
|
|
37687
|
+
constructor(config2, indexer, store, initialState, initialError) {
|
|
37688
|
+
this.config = config2;
|
|
37689
|
+
this.indexer = indexer;
|
|
37690
|
+
this.store = store;
|
|
37691
|
+
this.lifecycleStatus = makeStatus(initialState, initialError);
|
|
37692
|
+
}
|
|
37693
|
+
lifecycleStatus;
|
|
37694
|
+
get status() {
|
|
37695
|
+
return this.lifecycleStatus;
|
|
37696
|
+
}
|
|
37697
|
+
async startWarmup() {
|
|
37698
|
+
if (!this.indexer) {
|
|
37699
|
+
this.lifecycleStatus = makeStatus("failed", this.lifecycleStatus.error ?? "Memory indexer is unavailable");
|
|
37700
|
+
return;
|
|
37701
|
+
}
|
|
37702
|
+
this.lifecycleStatus = makeStatus("warming");
|
|
37703
|
+
try {
|
|
37704
|
+
await this.indexer.startupIndex();
|
|
37705
|
+
this.lifecycleStatus = makeStatus("ready");
|
|
37706
|
+
} catch (err) {
|
|
37707
|
+
this.lifecycleStatus = makeStatus(this.store ? "degraded" : "failed", formatError3(err));
|
|
37708
|
+
}
|
|
37709
|
+
}
|
|
37710
|
+
close() {
|
|
37711
|
+
this.store?.close();
|
|
37712
|
+
}
|
|
37713
|
+
};
|
|
37714
|
+
function makeStatus(state, error2) {
|
|
37715
|
+
return {
|
|
37716
|
+
state,
|
|
37717
|
+
ready: state === "ready",
|
|
37718
|
+
warming: state === "initializing" || state === "warming",
|
|
37719
|
+
degraded: state === "degraded" || state === "failed",
|
|
37720
|
+
error: error2
|
|
37721
|
+
};
|
|
37722
|
+
}
|
|
37723
|
+
function formatError3(err) {
|
|
37724
|
+
return err instanceof Error ? err.message : String(err);
|
|
37725
|
+
}
|
|
37726
|
+
function createMemorySubsystem(workspaceRoot2, overrides = {}) {
|
|
37727
|
+
const config2 = overrides.config ?? resolveMemoryConfig(overrides.settings ?? {}, workspaceRoot2);
|
|
37728
|
+
if (config2.enabled !== true) return null;
|
|
37729
|
+
let store = overrides.store ?? null;
|
|
37730
|
+
let indexer = overrides.indexer ?? null;
|
|
37731
|
+
try {
|
|
37732
|
+
store = store ?? new MemoryStore(path10.join(config2.wikiDir, "index.db"), config2);
|
|
37733
|
+
if (!indexer) {
|
|
37734
|
+
const embedder = new Embedder(config2.embeddingModel);
|
|
37735
|
+
indexer = new MemoryIndexer(store, embedder, config2);
|
|
37736
|
+
}
|
|
37737
|
+
return new DefaultMemorySubsystem(config2, indexer, store, "initializing");
|
|
37738
|
+
} catch (err) {
|
|
37739
|
+
return new DefaultMemorySubsystem(config2, indexer, store, "failed", formatError3(err));
|
|
37740
|
+
}
|
|
37741
|
+
}
|
|
37742
|
+
|
|
37612
37743
|
// src/services/digest/processor.ts
|
|
37613
37744
|
import * as fs10 from "node:fs";
|
|
37614
|
-
import * as
|
|
37745
|
+
import * as path14 from "node:path";
|
|
37615
37746
|
|
|
37616
37747
|
// src/services/digest/constants.ts
|
|
37617
|
-
import * as
|
|
37748
|
+
import * as path11 from "node:path";
|
|
37618
37749
|
var DEFAULT_DIGEST_MODEL_ID = "base" /* BASE */;
|
|
37619
37750
|
var DEFAULT_DIGEST_MAX_INPUT_CHARS = 1e4;
|
|
37620
37751
|
var DEFAULT_DIGEST_TIMEOUT_MS = 12e4;
|
|
37621
37752
|
var KIT_DIR = ".agent-kit";
|
|
37622
|
-
var WIKI_DIR =
|
|
37623
|
-
var WIKI_RAW_DIR =
|
|
37624
|
-
var PROVISIONAL_DIGEST_DIR =
|
|
37625
|
-
var DIGEST_WORKER_DIR =
|
|
37626
|
-
var DIGEST_LOCKFILE_REL_PATH =
|
|
37627
|
-
var DIGEST_WORKER_STATUS_REL_PATH =
|
|
37628
|
-
var DIGEST_WORKER_LOG_REL_PATH =
|
|
37753
|
+
var WIKI_DIR = path11.join(KIT_DIR, "wiki");
|
|
37754
|
+
var WIKI_RAW_DIR = path11.join(WIKI_DIR, "raw");
|
|
37755
|
+
var PROVISIONAL_DIGEST_DIR = path11.join(WIKI_DIR, "compiled", "provisional", "conversation-digests");
|
|
37756
|
+
var DIGEST_WORKER_DIR = path11.join(WIKI_DIR, "digest");
|
|
37757
|
+
var DIGEST_LOCKFILE_REL_PATH = path11.join(DIGEST_WORKER_DIR, "digest-worker.lock");
|
|
37758
|
+
var DIGEST_WORKER_STATUS_REL_PATH = path11.join(DIGEST_WORKER_DIR, "digest-worker.status.json");
|
|
37759
|
+
var DIGEST_WORKER_LOG_REL_PATH = path11.join(DIGEST_WORKER_DIR, "digest-worker.log");
|
|
37629
37760
|
var LLAMA_CONTEXT_SIZE = 8192;
|
|
37630
37761
|
var LLAMA_MAX_GENERATED_TOKENS = 512;
|
|
37631
37762
|
var LLAMA_TEMPERATURE = 0.1;
|
|
37632
37763
|
|
|
37633
37764
|
// src/services/digest/files.ts
|
|
37634
37765
|
import * as fs8 from "node:fs";
|
|
37635
|
-
import * as
|
|
37766
|
+
import * as path12 from "node:path";
|
|
37636
37767
|
function defaultProvisionalDigestDir(workspaceRoot2) {
|
|
37637
|
-
return
|
|
37768
|
+
return path12.join(workspaceRoot2, PROVISIONAL_DIGEST_DIR);
|
|
37638
37769
|
}
|
|
37639
37770
|
function readConversationDigestInput(workspaceRoot2, inputPath) {
|
|
37640
|
-
const absoluteInput =
|
|
37771
|
+
const absoluteInput = path12.isAbsolute(inputPath) ? inputPath : path12.resolve(workspaceRoot2, inputPath);
|
|
37641
37772
|
const content = fs8.readFileSync(absoluteInput, "utf8");
|
|
37642
37773
|
return {
|
|
37643
|
-
sourcePath:
|
|
37774
|
+
sourcePath: path12.relative(workspaceRoot2, absoluteInput),
|
|
37644
37775
|
content,
|
|
37645
37776
|
contentHash: sha256Hex(content)
|
|
37646
37777
|
};
|
|
37647
37778
|
}
|
|
37648
37779
|
function provisionalDigestPath(outDir, input) {
|
|
37649
|
-
const safeName =
|
|
37650
|
-
return
|
|
37780
|
+
const safeName = path12.basename(input.sourcePath).replace(/\.[^.]+$/, "").replace(/[^a-z0-9._-]+/gi, "-");
|
|
37781
|
+
return path12.join(outDir, `${input.contentHash.slice(0, 16)}-${safeName}.md`);
|
|
37651
37782
|
}
|
|
37652
37783
|
function findExistingProvisionalDigest(outDir, input) {
|
|
37653
37784
|
const markdownPath = provisionalDigestPath(outDir, input);
|
|
@@ -37722,25 +37853,8 @@ function getDigestModelSpec(modelId) {
|
|
|
37722
37853
|
|
|
37723
37854
|
// src/services/digest/providers/llama-local.ts
|
|
37724
37855
|
import * as fs9 from "node:fs";
|
|
37725
|
-
import * as
|
|
37856
|
+
import * as path13 from "node:path";
|
|
37726
37857
|
import { getLlama, LlamaChatSession, createModelDownloader } from "node-llama-cpp";
|
|
37727
|
-
|
|
37728
|
-
// src/utils/async.ts
|
|
37729
|
-
async function withTimeout(promise, timeoutMs, createError) {
|
|
37730
|
-
let timer;
|
|
37731
|
-
const timeout = new Promise((_resolve, reject) => {
|
|
37732
|
-
timer = setTimeout(() => {
|
|
37733
|
-
reject(createError());
|
|
37734
|
-
}, timeoutMs);
|
|
37735
|
-
});
|
|
37736
|
-
try {
|
|
37737
|
-
return await Promise.race([promise, timeout]);
|
|
37738
|
-
} finally {
|
|
37739
|
-
if (timer) clearTimeout(timer);
|
|
37740
|
-
}
|
|
37741
|
-
}
|
|
37742
|
-
|
|
37743
|
-
// src/services/digest/providers/llama-local.ts
|
|
37744
37858
|
var LlamaLocalDigestProviderError = class extends Error {
|
|
37745
37859
|
constructor(message) {
|
|
37746
37860
|
super(message);
|
|
@@ -37748,7 +37862,7 @@ var LlamaLocalDigestProviderError = class extends Error {
|
|
|
37748
37862
|
}
|
|
37749
37863
|
};
|
|
37750
37864
|
function titleFromSource(sourcePath) {
|
|
37751
|
-
return
|
|
37865
|
+
return path13.basename(sourcePath).replace(/\.[^.]+$/, "").replace(/[_-]+/g, " ");
|
|
37752
37866
|
}
|
|
37753
37867
|
function trimConversationExport(content, maxInputChars) {
|
|
37754
37868
|
const trimmed = content.trim();
|
|
@@ -37791,7 +37905,7 @@ function sanitizeConversationDigestMarkdown(generatedText) {
|
|
|
37791
37905
|
}
|
|
37792
37906
|
async function createLlamaLocalDigestProvider(modelId) {
|
|
37793
37907
|
const spec = getDigestModelSpec(modelId);
|
|
37794
|
-
const modelCacheDir =
|
|
37908
|
+
const modelCacheDir = path13.join(MODEL_CACHE_DIR, "llama");
|
|
37795
37909
|
fs9.mkdirSync(modelCacheDir, { recursive: true });
|
|
37796
37910
|
let modelFilePath;
|
|
37797
37911
|
try {
|
|
@@ -37860,13 +37974,13 @@ ${sanitized}`;
|
|
|
37860
37974
|
|
|
37861
37975
|
// src/services/digest/processor.ts
|
|
37862
37976
|
function digestLockPath(workspaceRoot2) {
|
|
37863
|
-
return
|
|
37977
|
+
return path14.join(workspaceRoot2, DIGEST_LOCKFILE_REL_PATH);
|
|
37864
37978
|
}
|
|
37865
37979
|
function discoverPendingConversationFiles(workspaceRoot2) {
|
|
37866
|
-
const rawDir =
|
|
37980
|
+
const rawDir = path14.join(workspaceRoot2, WIKI_RAW_DIR);
|
|
37867
37981
|
let files;
|
|
37868
37982
|
try {
|
|
37869
|
-
files = fs10.readdirSync(rawDir).filter((f) => f.startsWith("conv_") && f.endsWith(".md")).map((f) =>
|
|
37983
|
+
files = fs10.readdirSync(rawDir).filter((f) => f.startsWith("conv_") && f.endsWith(".md")).map((f) => path14.join(rawDir, f)).sort((a, b) => fs10.statSync(a).mtimeMs - fs10.statSync(b).mtimeMs);
|
|
37870
37984
|
} catch (err) {
|
|
37871
37985
|
if (err.code === "ENOENT") {
|
|
37872
37986
|
files = [];
|
|
@@ -37894,7 +38008,7 @@ async function indexProvisionalDigestFile(workspaceRoot2, markdownPath, indexer)
|
|
|
37894
38008
|
const settings = loadGlobalSettings();
|
|
37895
38009
|
const config2 = resolveMemoryConfig(settings, workspaceRoot2);
|
|
37896
38010
|
if (config2.enabled !== true) return { indexed: false };
|
|
37897
|
-
store = new MemoryStore(
|
|
38011
|
+
store = new MemoryStore(path14.join(config2.wikiDir, "index.db"), config2);
|
|
37898
38012
|
const embedder = new Embedder(config2.embeddingModel);
|
|
37899
38013
|
await embedder.initialize();
|
|
37900
38014
|
await new MemoryIndexer(store, embedder, config2).indexFile(markdownPath);
|
|
@@ -37906,7 +38020,7 @@ async function indexProvisionalDigestFile(workspaceRoot2, markdownPath, indexer)
|
|
|
37906
38020
|
}
|
|
37907
38021
|
}
|
|
37908
38022
|
async function digestConversationFile(options) {
|
|
37909
|
-
const outDir = options.outDir ?
|
|
38023
|
+
const outDir = options.outDir ? path14.resolve(options.workspaceRoot, options.outDir) : defaultProvisionalDigestDir(options.workspaceRoot);
|
|
37910
38024
|
const input = readConversationDigestInput(options.workspaceRoot, options.inputPath);
|
|
37911
38025
|
const existingPath = findExistingProvisionalDigest(outDir, input);
|
|
37912
38026
|
if (existingPath) {
|
|
@@ -37961,7 +38075,7 @@ async function digestPendingConversations({
|
|
|
37961
38075
|
let indexer;
|
|
37962
38076
|
const memoryConfig = resolveMemoryConfig(loadGlobalSettings(), workspaceRoot2);
|
|
37963
38077
|
if (memoryConfig.enabled === true) {
|
|
37964
|
-
const store = new MemoryStore(
|
|
38078
|
+
const store = new MemoryStore(path14.join(memoryConfig.wikiDir, "index.db"), memoryConfig);
|
|
37965
38079
|
const embedder = new Embedder(memoryConfig.embeddingModel);
|
|
37966
38080
|
indexer = new MemoryIndexer(store, embedder, memoryConfig);
|
|
37967
38081
|
closeIndexer = () => store.close();
|
|
@@ -38042,20 +38156,30 @@ async function initializeConversationDigestModel(input) {
|
|
|
38042
38156
|
}
|
|
38043
38157
|
|
|
38044
38158
|
// src/mcp/memory.ts
|
|
38045
|
-
function registerMemoryToolHandlers(server2,
|
|
38046
|
-
|
|
38159
|
+
function registerMemoryToolHandlers(server2, subsystem, workspaceRoot2 = process.cwd()) {
|
|
38160
|
+
const { config: config2 } = subsystem;
|
|
38161
|
+
server2.registerTool(
|
|
38047
38162
|
"kit_memory_search",
|
|
38048
|
-
"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.",
|
|
38049
38163
|
{
|
|
38050
|
-
|
|
38051
|
-
|
|
38164
|
+
title: "Search Memory",
|
|
38165
|
+
description: "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.",
|
|
38166
|
+
inputSchema: {
|
|
38167
|
+
query: external_exports.string().min(1).describe("Keyword-only search query with stopwords removed"),
|
|
38168
|
+
top_k: external_exports.number().int().positive().optional().describe("Number of results to return")
|
|
38169
|
+
},
|
|
38170
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
|
|
38052
38171
|
},
|
|
38053
38172
|
async ({ query, top_k }) => {
|
|
38054
38173
|
try {
|
|
38174
|
+
if (!subsystem.indexer || !subsystem.store) {
|
|
38175
|
+
return mcpText(formatInitializationFailure(subsystem));
|
|
38176
|
+
}
|
|
38177
|
+
const { indexer, store } = subsystem;
|
|
38055
38178
|
const results = await indexer.search(query, top_k ?? config2.topK);
|
|
38179
|
+
const lifecycleNote = formatLifecycleNote(subsystem);
|
|
38056
38180
|
const degradedNote = !store.vecAvailable ? "\u26A0\uFE0F Vector search unavailable \u2014 showing keyword-only results.\n\n" : "";
|
|
38057
38181
|
if (results.length === 0) {
|
|
38058
|
-
return mcpText(`${degradedNote}No memories found for query: "${query}"`);
|
|
38182
|
+
return mcpText(`${lifecycleNote}${degradedNote}No memories found for query: "${query}"`);
|
|
38059
38183
|
}
|
|
38060
38184
|
const formatted = results.map((r) => {
|
|
38061
38185
|
const displaySource = r.chunk.source.replace(/^compiled\//, "");
|
|
@@ -38064,27 +38188,35 @@ ${r.chunk.content}` : r.chunk.content;
|
|
|
38064
38188
|
return `### ${displaySource} (score: ${r.score.toFixed(3)})
|
|
38065
38189
|
${content}`;
|
|
38066
38190
|
}).join("\n\n---\n\n");
|
|
38067
|
-
return mcpText(`${degradedNote}${formatted}`);
|
|
38191
|
+
return mcpText(`${lifecycleNote}${degradedNote}${formatted}`);
|
|
38068
38192
|
} catch (err) {
|
|
38069
38193
|
return mcpText(`kit_memory_search failed: ${formatError2(err)}`);
|
|
38070
38194
|
}
|
|
38071
38195
|
}
|
|
38072
38196
|
);
|
|
38073
|
-
server2.
|
|
38197
|
+
server2.registerTool(
|
|
38074
38198
|
"kit_memory_recent",
|
|
38075
|
-
'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".',
|
|
38076
38199
|
{
|
|
38077
|
-
|
|
38078
|
-
|
|
38200
|
+
title: "Recent Memory Sources",
|
|
38201
|
+
description: '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".',
|
|
38202
|
+
inputSchema: {
|
|
38203
|
+
n: external_exports.number().int().positive().max(50).optional().describe("How many recent sources to return (default 5)"),
|
|
38204
|
+
source_type: external_exports.enum(SOURCE_TYPES).optional().describe("Optional source type filter")
|
|
38205
|
+
},
|
|
38206
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
|
|
38079
38207
|
},
|
|
38080
38208
|
async ({ n, source_type }) => {
|
|
38081
38209
|
try {
|
|
38210
|
+
if (!subsystem.store) {
|
|
38211
|
+
return mcpText(formatInitializationFailure(subsystem));
|
|
38212
|
+
}
|
|
38213
|
+
const store = subsystem.store;
|
|
38082
38214
|
const rows = store.getRecentSources({ limit: n ?? 5, sourceType: source_type });
|
|
38083
38215
|
if (rows.length === 0) return mcpText("No recent sources found.");
|
|
38084
38216
|
const blocks = [];
|
|
38085
38217
|
for (const row of rows) {
|
|
38086
38218
|
try {
|
|
38087
|
-
const content = await fs11.promises.readFile(
|
|
38219
|
+
const content = await fs11.promises.readFile(path15.join(config2.wikiDir, row.source), "utf8");
|
|
38088
38220
|
const displaySource = row.source.replace(/^compiled\//, "");
|
|
38089
38221
|
blocks.push(`### ${displaySource}
|
|
38090
38222
|
${content}`);
|
|
@@ -38099,14 +38231,25 @@ ${content}`);
|
|
|
38099
38231
|
}
|
|
38100
38232
|
}
|
|
38101
38233
|
);
|
|
38102
|
-
server2.
|
|
38234
|
+
server2.registerTool(
|
|
38103
38235
|
"kit_memory_save",
|
|
38104
|
-
"Save content to wiki/raw for inclusion after the next /wiki compile.",
|
|
38105
38236
|
{
|
|
38106
|
-
|
|
38237
|
+
title: "Save to Memory",
|
|
38238
|
+
description: "Save content to wiki/raw for inclusion after the next /wiki compile.",
|
|
38239
|
+
inputSchema: {
|
|
38240
|
+
content: external_exports.string().min(1).describe("Content to save to memory")
|
|
38241
|
+
},
|
|
38242
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }
|
|
38107
38243
|
},
|
|
38108
38244
|
async ({ content }) => {
|
|
38109
38245
|
try {
|
|
38246
|
+
if (!subsystem.indexer) {
|
|
38247
|
+
return mcpJson({
|
|
38248
|
+
saved: false,
|
|
38249
|
+
error: formatInitializationFailure(subsystem)
|
|
38250
|
+
});
|
|
38251
|
+
}
|
|
38252
|
+
const indexer = subsystem.indexer;
|
|
38110
38253
|
await indexer.save(content);
|
|
38111
38254
|
return mcpJson({
|
|
38112
38255
|
saved: true,
|
|
@@ -38121,12 +38264,16 @@ ${content}`);
|
|
|
38121
38264
|
}
|
|
38122
38265
|
}
|
|
38123
38266
|
);
|
|
38124
|
-
server2.
|
|
38267
|
+
server2.registerTool(
|
|
38125
38268
|
"kit_memory_digest_init",
|
|
38126
|
-
"Initialize or toggle local conversation digesting. Downloads/cache-loads the pinned local model only when explicitly invoked.",
|
|
38127
38269
|
{
|
|
38128
|
-
|
|
38129
|
-
|
|
38270
|
+
title: "Initialize Conversation Digest",
|
|
38271
|
+
description: "Initialize or toggle local conversation digesting. Downloads/cache-loads the pinned local model only when explicitly invoked.",
|
|
38272
|
+
inputSchema: {
|
|
38273
|
+
model_id: external_exports.string().optional().describe("Digest model id"),
|
|
38274
|
+
enabled: external_exports.boolean().optional().describe("Enable or disable conversation digesting")
|
|
38275
|
+
},
|
|
38276
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
38130
38277
|
},
|
|
38131
38278
|
async ({ model_id, enabled }) => {
|
|
38132
38279
|
try {
|
|
@@ -38149,17 +38296,28 @@ ${content}`);
|
|
|
38149
38296
|
}
|
|
38150
38297
|
);
|
|
38151
38298
|
}
|
|
38299
|
+
function formatLifecycleNote(subsystem) {
|
|
38300
|
+
if (subsystem.status.state === "warming" || subsystem.status.state === "initializing") {
|
|
38301
|
+
return "\u26A0\uFE0F Memory index is warming \u2014 results may be stale until startup indexing completes.\n\n";
|
|
38302
|
+
}
|
|
38303
|
+
if (subsystem.status.state === "degraded") {
|
|
38304
|
+
return `\u26A0\uFE0F Memory index is degraded${subsystem.status.error ? `: ${subsystem.status.error}` : ""}.
|
|
38305
|
+
|
|
38306
|
+
`;
|
|
38307
|
+
}
|
|
38308
|
+
return "";
|
|
38309
|
+
}
|
|
38310
|
+
function formatInitializationFailure(subsystem) {
|
|
38311
|
+
const detail = subsystem.status.error ? `: ${subsystem.status.error}` : "";
|
|
38312
|
+
return `Memory initialization failed${detail}`;
|
|
38313
|
+
}
|
|
38152
38314
|
function registerMemoryTools(server2, workspaceRoot2, overrides) {
|
|
38153
38315
|
const settings = overrides?.settings ?? loadGlobalSettings();
|
|
38154
38316
|
if (settings.memory?.enabled !== true) return null;
|
|
38155
|
-
const
|
|
38156
|
-
|
|
38157
|
-
|
|
38158
|
-
|
|
38159
|
-
return new MemoryIndexer(store, embedder, config2);
|
|
38160
|
-
})();
|
|
38161
|
-
registerMemoryToolHandlers(server2, indexer, store, config2, workspaceRoot2);
|
|
38162
|
-
return indexer;
|
|
38317
|
+
const subsystem = createMemorySubsystem(workspaceRoot2, { ...overrides, settings });
|
|
38318
|
+
if (!subsystem) return null;
|
|
38319
|
+
registerMemoryToolHandlers(server2, subsystem, workspaceRoot2);
|
|
38320
|
+
return subsystem;
|
|
38163
38321
|
}
|
|
38164
38322
|
|
|
38165
38323
|
// src/entrypoints/server.ts
|
|
@@ -38171,9 +38329,9 @@ registerIntegrationTools(server);
|
|
|
38171
38329
|
registerCoreTools(server);
|
|
38172
38330
|
registerAgentTools(server);
|
|
38173
38331
|
var workspaceRoot = getWorkspaceRoot();
|
|
38174
|
-
var
|
|
38332
|
+
var memorySubsystem = registerMemoryTools(server, workspaceRoot);
|
|
38175
38333
|
var transport = new StdioServerTransport();
|
|
38176
38334
|
await server.connect(transport);
|
|
38177
|
-
if (
|
|
38178
|
-
void
|
|
38335
|
+
if (memorySubsystem) {
|
|
38336
|
+
void memorySubsystem.startWarmup();
|
|
38179
38337
|
}
|