@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/server.js CHANGED
@@ -3222,8 +3222,8 @@ var require_utils = __commonJS({
3222
3222
  }
3223
3223
  return ind;
3224
3224
  }
3225
- function removeDotSegments(path15) {
3226
- let input = path15;
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 [path15, query] = wsComponent.resourceName.split("?");
3426
- wsComponent.path = path15 && path15 !== "/" ? path15 : void 0;
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: path15, errorMaps, issueData } = params;
22168
- const fullPath = [...path15, ...issueData.path || []];
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, path15, key) {
22284
+ constructor(parent, value, path16, key) {
22285
22285
  this._cachedPath = [];
22286
22286
  this.parent = parent;
22287
22287
  this.data = value;
22288
- this._path = path15;
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, path15) {
25927
- if (!path15)
25926
+ function getElementAtPath(obj, path16) {
25927
+ if (!path16)
25928
25928
  return obj;
25929
- return path15.reduce((acc, key) => acc?.[key], obj);
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(path15, issues) {
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(path15);
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.tool(
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
- type: external_exports.enum(HANDOFF_TYPES).describe("Handoff type"),
36169
- files: external_exports.record(external_exports.string(), external_exports.string()).describe(
36170
- "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$/."
36171
- ),
36172
- slug: external_exports.string().describe(
36173
- 'Short identifier for the folder, e.g. "user-auth" or "PROJ-123". Do NOT append version numbers (v2, v3, etc.).'
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.tool(
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
- agent: external_exports.enum(["gemini", "claude", "codex"]).describe('The agent CLI to invoke: "gemini", "claude", or "codex"'),
36289
- task: external_exports.string().describe('Task message or path to a handoff folder (e.g., ".agent-kit/handoffs/feature-slug/plan")')
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.tool(
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
- input: external_exports.string().describe("Bitbucket PR URL or numeric PR ID"),
36649
- workspace: external_exports.string().optional().describe("Bitbucket workspace slug (required for numeric ID if BITBUCKET_DEFAULT_WORKSPACE not set)"),
36650
- repoSlug: external_exports.string().optional().describe("Bitbucket repo slug (required for numeric ID)"),
36651
- includeDiff: external_exports.boolean().optional().default(true).describe("Include unified diff in response")
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.tool(
36739
+ server2.registerTool(
36729
36740
  "kit_jira_get_ticket",
36730
- "Get ticket details from Jira using the Atlassian REST API",
36731
36741
  {
36732
- ticketId: external_exports.string().describe("Jira ticket ID (e.g., PROJ-123)")
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 path14 from "path";
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
- return this.indexFileRelativeTo(absolutePath, this.config.wikiDir);
37061
+ const mutation = await this.prepareIndexFileRelativeTo(absolutePath, this.config.wikiDir);
37062
+ return this.commitPreparedMutation(mutation);
37013
37063
  }
37014
- async indexFileRelativeTo(absolutePath, sourceRoot) {
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 { indexed: 0, deleted: 0, skipped: existingChunkIds.size };
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 { indexed: 0, deleted: 0, skipped: existingChunkIds.size };
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
- indexed: toIndex.length,
37053
- deleted: toDelete.length,
37054
- skipped: allChunks.length - toIndex.length
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
- for (const file of files) {
37099
- const stats = await this.indexFileRelativeTo(file, relativeBase);
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 path13 from "node:path";
37745
+ import * as path14 from "node:path";
37615
37746
 
37616
37747
  // src/services/digest/constants.ts
37617
- import * as path10 from "node:path";
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 = path10.join(KIT_DIR, "wiki");
37623
- var WIKI_RAW_DIR = path10.join(WIKI_DIR, "raw");
37624
- var PROVISIONAL_DIGEST_DIR = path10.join(WIKI_DIR, "compiled", "provisional", "conversation-digests");
37625
- var DIGEST_WORKER_DIR = path10.join(WIKI_DIR, "digest");
37626
- var DIGEST_LOCKFILE_REL_PATH = path10.join(DIGEST_WORKER_DIR, "digest-worker.lock");
37627
- var DIGEST_WORKER_STATUS_REL_PATH = path10.join(DIGEST_WORKER_DIR, "digest-worker.status.json");
37628
- var DIGEST_WORKER_LOG_REL_PATH = path10.join(DIGEST_WORKER_DIR, "digest-worker.log");
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 path11 from "node:path";
37766
+ import * as path12 from "node:path";
37636
37767
  function defaultProvisionalDigestDir(workspaceRoot2) {
37637
- return path11.join(workspaceRoot2, PROVISIONAL_DIGEST_DIR);
37768
+ return path12.join(workspaceRoot2, PROVISIONAL_DIGEST_DIR);
37638
37769
  }
37639
37770
  function readConversationDigestInput(workspaceRoot2, inputPath) {
37640
- const absoluteInput = path11.isAbsolute(inputPath) ? inputPath : path11.resolve(workspaceRoot2, inputPath);
37771
+ const absoluteInput = path12.isAbsolute(inputPath) ? inputPath : path12.resolve(workspaceRoot2, inputPath);
37641
37772
  const content = fs8.readFileSync(absoluteInput, "utf8");
37642
37773
  return {
37643
- sourcePath: path11.relative(workspaceRoot2, absoluteInput),
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 = path11.basename(input.sourcePath).replace(/\.[^.]+$/, "").replace(/[^a-z0-9._-]+/gi, "-");
37650
- return path11.join(outDir, `${input.contentHash.slice(0, 16)}-${safeName}.md`);
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 path12 from "node:path";
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 path12.basename(sourcePath).replace(/\.[^.]+$/, "").replace(/[_-]+/g, " ");
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 = path12.join(MODEL_CACHE_DIR, "llama");
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 path13.join(workspaceRoot2, DIGEST_LOCKFILE_REL_PATH);
37977
+ return path14.join(workspaceRoot2, DIGEST_LOCKFILE_REL_PATH);
37864
37978
  }
37865
37979
  function discoverPendingConversationFiles(workspaceRoot2) {
37866
- const rawDir = path13.join(workspaceRoot2, WIKI_RAW_DIR);
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) => path13.join(rawDir, f)).sort((a, b) => fs10.statSync(a).mtimeMs - fs10.statSync(b).mtimeMs);
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(path13.join(config2.wikiDir, "index.db"), config2);
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 ? path13.resolve(options.workspaceRoot, options.outDir) : defaultProvisionalDigestDir(options.workspaceRoot);
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(path13.join(memoryConfig.wikiDir, "index.db"), memoryConfig);
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, indexer, store, config2, workspaceRoot2 = process.cwd()) {
38046
- server2.tool(
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
- query: external_exports.string().min(1).describe("Keyword-only search query with stopwords removed"),
38051
- top_k: external_exports.number().int().positive().optional().describe("Number of results to return")
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.tool(
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
- n: external_exports.number().int().positive().max(50).optional().describe("How many recent sources to return (default 5)"),
38078
- source_type: external_exports.enum(SOURCE_TYPES).optional().describe("Optional source type filter")
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(path14.join(config2.wikiDir, row.source), "utf8");
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.tool(
38234
+ server2.registerTool(
38103
38235
  "kit_memory_save",
38104
- "Save content to wiki/raw for inclusion after the next /wiki compile.",
38105
38236
  {
38106
- content: external_exports.string().min(1).describe("Content to save to memory")
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.tool(
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
- model_id: external_exports.string().optional().describe("Digest model id"),
38129
- enabled: external_exports.boolean().optional().describe("Enable or disable conversation digesting")
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 config2 = overrides?.config ?? resolveMemoryConfig(settings, workspaceRoot2);
38156
- const store = overrides?.store ?? new MemoryStore(path14.join(config2.wikiDir, "index.db"), config2);
38157
- const indexer = overrides?.indexer ?? (() => {
38158
- const embedder = new Embedder(config2.embeddingModel);
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 memoryIndexer = registerMemoryTools(server, workspaceRoot);
38332
+ var memorySubsystem = registerMemoryTools(server, workspaceRoot);
38175
38333
  var transport = new StdioServerTransport();
38176
38334
  await server.connect(transport);
38177
- if (memoryIndexer) {
38178
- void memoryIndexer.startupIndex();
38335
+ if (memorySubsystem) {
38336
+ void memorySubsystem.startWarmup();
38179
38337
  }