@hanhnd/agent-kit 1.0.31 → 1.0.33

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.
Files changed (105) hide show
  1. package/dist/cli/commands/cli-digest-pending.test.js +111 -0
  2. package/dist/cli/commands/cli-memory.test.js +34 -0
  3. package/dist/cli/commands/memory.d.ts +1 -0
  4. package/dist/cli/commands/memory.js +90 -0
  5. package/dist/cli/index.d.ts +1 -0
  6. package/dist/cli/index.js +9 -0
  7. package/dist/cli/utils/args.d.ts +8 -0
  8. package/dist/cli/utils/args.js +31 -0
  9. package/dist/cli.js +16489 -0
  10. package/dist/{tools/config.d.ts → core/config/index.d.ts} +4 -9
  11. package/dist/{tools/config.js → core/config/index.js} +20 -23
  12. package/dist/core/handoffs/index.d.ts +16 -0
  13. package/dist/{tools/handoffs.js → core/handoffs/index.js} +4 -5
  14. package/dist/{tools/security.d.ts → core/security/index.d.ts} +0 -19
  15. package/dist/{tools/security.js → core/security/index.js} +3 -80
  16. package/dist/entrypoints/cli.d.ts +2 -0
  17. package/dist/entrypoints/cli.js +11 -0
  18. package/dist/entrypoints/server.js +37 -0
  19. package/dist/{tools → mcp}/agent.js +3 -14
  20. package/dist/{tools → mcp}/core.d.ts +1 -1
  21. package/dist/{tools → mcp}/core.js +3 -5
  22. package/dist/{tools → mcp}/integration.js +5 -11
  23. package/dist/mcp/memory.d.ts +15 -0
  24. package/dist/{tools → mcp}/memory.js +40 -12
  25. package/dist/{kit-server.js → server.js} +720 -224
  26. package/dist/services/digest/background.d.ts +11 -0
  27. package/dist/services/digest/background.js +258 -0
  28. package/dist/services/digest/constants.d.ts +17 -0
  29. package/dist/services/digest/constants.js +19 -0
  30. package/dist/services/digest/digest-background.test.js +206 -0
  31. package/dist/services/digest/digest-init.test.js +33 -0
  32. package/dist/services/digest/digest-llama-local.test.js +21 -0
  33. package/dist/services/digest/digest-pending.test.d.ts +1 -0
  34. package/dist/services/digest/digest-pending.test.js +192 -0
  35. package/dist/services/digest/digest-processor.test.d.ts +1 -0
  36. package/dist/services/digest/digest-processor.test.js +32 -0
  37. package/dist/services/digest/files.d.ts +6 -0
  38. package/dist/services/digest/files.js +45 -0
  39. package/dist/services/digest/model-registry.d.ts +2 -0
  40. package/dist/services/digest/model-registry.js +49 -0
  41. package/dist/services/digest/processor.d.ts +13 -0
  42. package/dist/services/digest/processor.js +188 -0
  43. package/dist/services/digest/providers/llama-local.d.ts +2 -0
  44. package/dist/services/digest/providers/llama-local.js +135 -0
  45. package/dist/services/digest/types.d.ts +117 -0
  46. package/dist/services/digest/types.js +6 -0
  47. package/dist/services/digest/worker.d.ts +2 -0
  48. package/dist/services/digest/worker.js +51 -0
  49. package/dist/{utils → services/integration}/credentials.js +1 -1
  50. package/dist/{utils/parser.js → services/integration/parser/adf.js} +3 -6
  51. package/dist/{memory → services/memory}/chunker.d.ts +0 -2
  52. package/dist/{memory → services/memory}/chunker.js +5 -7
  53. package/dist/services/memory/chunker.test.d.ts +1 -0
  54. package/dist/{memory/__tests__ → services/memory}/chunker.test.js +1 -34
  55. package/dist/services/memory/constants.d.ts +5 -0
  56. package/dist/services/memory/constants.js +13 -0
  57. package/dist/{memory → services/memory}/embedder.d.ts +3 -2
  58. package/dist/{memory → services/memory}/embedder.js +15 -6
  59. package/dist/services/memory/embedder.test.d.ts +1 -0
  60. package/dist/services/memory/embedder.test.js +13 -0
  61. package/dist/{memory → services/memory}/index.d.ts +1 -1
  62. package/dist/{memory → services/memory}/index.js +1 -1
  63. package/dist/{memory → services/memory}/indexer.d.ts +6 -2
  64. package/dist/{memory → services/memory}/indexer.js +15 -37
  65. package/dist/services/memory/indexer.test.d.ts +1 -0
  66. package/dist/{memory/__tests__ → services/memory}/indexer.test.js +11 -15
  67. package/dist/services/memory/memory.test.d.ts +1 -0
  68. package/dist/{tools/__tests__ → services/memory}/memory.test.js +82 -19
  69. package/dist/{memory → services/memory}/store.js +6 -18
  70. package/dist/services/memory/store.test.d.ts +1 -0
  71. package/dist/{memory/__tests__ → services/memory}/store.test.js +18 -5
  72. package/dist/{memory → services/memory}/types.d.ts +8 -2
  73. package/dist/services/memory/types.js +6 -0
  74. package/dist/utils/async.d.ts +1 -0
  75. package/dist/utils/async.js +15 -0
  76. package/dist/utils/files.d.ts +7 -0
  77. package/dist/utils/files.js +77 -0
  78. package/dist/utils/files.test.d.ts +1 -0
  79. package/dist/utils/files.test.js +81 -0
  80. package/dist/utils/hash.d.ts +1 -0
  81. package/dist/utils/hash.js +4 -0
  82. package/dist/utils/index.d.ts +6 -0
  83. package/dist/utils/index.js +6 -0
  84. package/dist/utils/json.d.ts +1 -0
  85. package/dist/utils/json.js +3 -0
  86. package/dist/utils/paths.d.ts +0 -1
  87. package/dist/utils/paths.js +1 -1
  88. package/dist/utils/temp-dir.test.d.ts +4 -0
  89. package/dist/utils/temp-dir.test.js +20 -0
  90. package/package.json +8 -6
  91. package/dist/memory/__tests__/embedder.test.js +0 -14
  92. package/dist/memory/types.js +0 -9
  93. package/dist/tools/handoffs.d.ts +0 -22
  94. package/dist/tools/memory.d.ts +0 -16
  95. /package/dist/{memory/__tests__/chunker.test.d.ts → cli/commands/cli-digest-pending.test.d.ts} +0 -0
  96. /package/dist/{memory/__tests__/embedder.test.d.ts → cli/commands/cli-memory.test.d.ts} +0 -0
  97. /package/dist/{kit-server.d.ts → entrypoints/server.d.ts} +0 -0
  98. /package/dist/{tools → mcp}/agent.d.ts +0 -0
  99. /package/dist/{tools → mcp}/integration.d.ts +0 -0
  100. /package/dist/{memory/__tests__/indexer.test.d.ts → services/digest/digest-background.test.d.ts} +0 -0
  101. /package/dist/{memory/__tests__/store.test.d.ts → services/digest/digest-init.test.d.ts} +0 -0
  102. /package/dist/{tools/__tests__/memory.test.d.ts → services/digest/digest-llama-local.test.d.ts} +0 -0
  103. /package/dist/{utils → services/integration}/credentials.d.ts +0 -0
  104. /package/dist/{utils/parser.d.ts → services/integration/parser/adf.d.ts} +0 -0
  105. /package/dist/{memory → services/memory}/store.d.ts +0 -0
@@ -2987,7 +2987,7 @@ var require_compile = __commonJS({
2987
2987
  const schOrFunc = root.refs[ref];
2988
2988
  if (schOrFunc)
2989
2989
  return schOrFunc;
2990
- let _sch = resolve3.call(this, root, ref);
2990
+ let _sch = resolve5.call(this, root, ref);
2991
2991
  if (_sch === void 0) {
2992
2992
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2993
2993
  const { schemaId } = this.opts;
@@ -3014,7 +3014,7 @@ var require_compile = __commonJS({
3014
3014
  function sameSchemaEnv(s1, s2) {
3015
3015
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3016
3016
  }
3017
- function resolve3(root, ref) {
3017
+ function resolve5(root, ref) {
3018
3018
  let sch;
3019
3019
  while (typeof (sch = this.refs[ref]) == "string")
3020
3020
  ref = sch;
@@ -3229,8 +3229,8 @@ var require_utils = __commonJS({
3229
3229
  }
3230
3230
  return ind;
3231
3231
  }
3232
- function removeDotSegments(path10) {
3233
- let input = path10;
3232
+ function removeDotSegments(path15) {
3233
+ let input = path15;
3234
3234
  const output = [];
3235
3235
  let nextSlash = -1;
3236
3236
  let len = 0;
@@ -3429,8 +3429,8 @@ var require_schemes = __commonJS({
3429
3429
  wsComponent.secure = void 0;
3430
3430
  }
3431
3431
  if (wsComponent.resourceName) {
3432
- const [path10, query] = wsComponent.resourceName.split("?");
3433
- wsComponent.path = path10 && path10 !== "/" ? path10 : void 0;
3432
+ const [path15, query] = wsComponent.resourceName.split("?");
3433
+ wsComponent.path = path15 && path15 !== "/" ? path15 : void 0;
3434
3434
  wsComponent.query = query;
3435
3435
  wsComponent.resourceName = void 0;
3436
3436
  }
@@ -3589,55 +3589,55 @@ var require_fast_uri = __commonJS({
3589
3589
  }
3590
3590
  return uri;
3591
3591
  }
3592
- function resolve3(baseURI, relativeURI, options) {
3592
+ function resolve5(baseURI, relativeURI, options) {
3593
3593
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3594
3594
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
3595
3595
  schemelessOptions.skipEscape = true;
3596
3596
  return serialize(resolved, schemelessOptions);
3597
3597
  }
3598
- function resolveComponent(base, relative3, options, skipNormalization) {
3598
+ function resolveComponent(base, relative4, options, skipNormalization) {
3599
3599
  const target = {};
3600
3600
  if (!skipNormalization) {
3601
3601
  base = parse3(serialize(base, options), options);
3602
- relative3 = parse3(serialize(relative3, options), options);
3602
+ relative4 = parse3(serialize(relative4, options), options);
3603
3603
  }
3604
3604
  options = options || {};
3605
- if (!options.tolerant && relative3.scheme) {
3606
- target.scheme = relative3.scheme;
3607
- target.userinfo = relative3.userinfo;
3608
- target.host = relative3.host;
3609
- target.port = relative3.port;
3610
- target.path = removeDotSegments(relative3.path || "");
3611
- target.query = relative3.query;
3605
+ if (!options.tolerant && relative4.scheme) {
3606
+ target.scheme = relative4.scheme;
3607
+ target.userinfo = relative4.userinfo;
3608
+ target.host = relative4.host;
3609
+ target.port = relative4.port;
3610
+ target.path = removeDotSegments(relative4.path || "");
3611
+ target.query = relative4.query;
3612
3612
  } else {
3613
- if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
3614
- target.userinfo = relative3.userinfo;
3615
- target.host = relative3.host;
3616
- target.port = relative3.port;
3617
- target.path = removeDotSegments(relative3.path || "");
3618
- target.query = relative3.query;
3613
+ if (relative4.userinfo !== void 0 || relative4.host !== void 0 || relative4.port !== void 0) {
3614
+ target.userinfo = relative4.userinfo;
3615
+ target.host = relative4.host;
3616
+ target.port = relative4.port;
3617
+ target.path = removeDotSegments(relative4.path || "");
3618
+ target.query = relative4.query;
3619
3619
  } else {
3620
- if (!relative3.path) {
3620
+ if (!relative4.path) {
3621
3621
  target.path = base.path;
3622
- if (relative3.query !== void 0) {
3623
- target.query = relative3.query;
3622
+ if (relative4.query !== void 0) {
3623
+ target.query = relative4.query;
3624
3624
  } else {
3625
3625
  target.query = base.query;
3626
3626
  }
3627
3627
  } else {
3628
- if (relative3.path[0] === "/") {
3629
- target.path = removeDotSegments(relative3.path);
3628
+ if (relative4.path[0] === "/") {
3629
+ target.path = removeDotSegments(relative4.path);
3630
3630
  } else {
3631
3631
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
3632
- target.path = "/" + relative3.path;
3632
+ target.path = "/" + relative4.path;
3633
3633
  } else if (!base.path) {
3634
- target.path = relative3.path;
3634
+ target.path = relative4.path;
3635
3635
  } else {
3636
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
3636
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative4.path;
3637
3637
  }
3638
3638
  target.path = removeDotSegments(target.path);
3639
3639
  }
3640
- target.query = relative3.query;
3640
+ target.query = relative4.query;
3641
3641
  }
3642
3642
  target.userinfo = base.userinfo;
3643
3643
  target.host = base.host;
@@ -3645,7 +3645,7 @@ var require_fast_uri = __commonJS({
3645
3645
  }
3646
3646
  target.scheme = base.scheme;
3647
3647
  }
3648
- target.fragment = relative3.fragment;
3648
+ target.fragment = relative4.fragment;
3649
3649
  return target;
3650
3650
  }
3651
3651
  function equal(uriA, uriB, options) {
@@ -3816,7 +3816,7 @@ var require_fast_uri = __commonJS({
3816
3816
  var fastUri = {
3817
3817
  SCHEMES,
3818
3818
  normalize,
3819
- resolve: resolve3,
3819
+ resolve: resolve5,
3820
3820
  resolveComponent,
3821
3821
  equal,
3822
3822
  serialize,
@@ -6792,12 +6792,12 @@ var require_dist = __commonJS({
6792
6792
  throw new Error(`Unknown format "${name}"`);
6793
6793
  return f;
6794
6794
  };
6795
- function addFormats(ajv, list, fs7, exportName) {
6795
+ function addFormats(ajv, list, fs11, exportName) {
6796
6796
  var _a;
6797
6797
  var _b;
6798
6798
  (_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
6799
6799
  for (const f of list)
6800
- ajv.addFormat(f, fs7[f]);
6800
+ ajv.addFormat(f, fs11[f]);
6801
6801
  }
6802
6802
  module.exports = exports = formatsPlugin;
6803
6803
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -22171,8 +22171,8 @@ function getErrorMap() {
22171
22171
 
22172
22172
  // ../node_modules/zod/v3/helpers/parseUtil.js
22173
22173
  var makeIssue = (params) => {
22174
- const { data, path: path10, errorMaps, issueData } = params;
22175
- const fullPath = [...path10, ...issueData.path || []];
22174
+ const { data, path: path15, errorMaps, issueData } = params;
22175
+ const fullPath = [...path15, ...issueData.path || []];
22176
22176
  const fullIssue = {
22177
22177
  ...issueData,
22178
22178
  path: fullPath
@@ -22288,11 +22288,11 @@ var errorUtil;
22288
22288
 
22289
22289
  // ../node_modules/zod/v3/types.js
22290
22290
  var ParseInputLazyPath = class {
22291
- constructor(parent, value, path10, key) {
22291
+ constructor(parent, value, path15, key) {
22292
22292
  this._cachedPath = [];
22293
22293
  this.parent = parent;
22294
22294
  this.data = value;
22295
- this._path = path10;
22295
+ this._path = path15;
22296
22296
  this._key = key;
22297
22297
  }
22298
22298
  get path() {
@@ -25930,10 +25930,10 @@ function assignProp(target, prop, value) {
25930
25930
  configurable: true
25931
25931
  });
25932
25932
  }
25933
- function getElementAtPath(obj, path10) {
25934
- if (!path10)
25933
+ function getElementAtPath(obj, path15) {
25934
+ if (!path15)
25935
25935
  return obj;
25936
- return path10.reduce((acc, key) => acc?.[key], obj);
25936
+ return path15.reduce((acc, key) => acc?.[key], obj);
25937
25937
  }
25938
25938
  function promiseAllObject(promisesObj) {
25939
25939
  const keys = Object.keys(promisesObj);
@@ -26253,11 +26253,11 @@ function aborted(x, startIndex = 0) {
26253
26253
  }
26254
26254
  return false;
26255
26255
  }
26256
- function prefixIssues(path10, issues) {
26256
+ function prefixIssues(path15, issues) {
26257
26257
  return issues.map((iss) => {
26258
26258
  var _a;
26259
26259
  (_a = iss).path ?? (_a.path = []);
26260
- iss.path.unshift(path10);
26260
+ iss.path.unshift(path15);
26261
26261
  return iss;
26262
26262
  });
26263
26263
  }
@@ -33768,7 +33768,7 @@ var Protocol = class {
33768
33768
  return;
33769
33769
  }
33770
33770
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
33771
- await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
33771
+ await new Promise((resolve5) => setTimeout(resolve5, pollInterval));
33772
33772
  options?.signal?.throwIfAborted();
33773
33773
  }
33774
33774
  } catch (error2) {
@@ -33785,7 +33785,7 @@ var Protocol = class {
33785
33785
  */
33786
33786
  request(request, resultSchema, options) {
33787
33787
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
33788
- return new Promise((resolve3, reject) => {
33788
+ return new Promise((resolve5, reject) => {
33789
33789
  const earlyReject = (error2) => {
33790
33790
  reject(error2);
33791
33791
  };
@@ -33863,7 +33863,7 @@ var Protocol = class {
33863
33863
  if (!parseResult.success) {
33864
33864
  reject(parseResult.error);
33865
33865
  } else {
33866
- resolve3(parseResult.data);
33866
+ resolve5(parseResult.data);
33867
33867
  }
33868
33868
  } catch (error2) {
33869
33869
  reject(error2);
@@ -34124,12 +34124,12 @@ var Protocol = class {
34124
34124
  }
34125
34125
  } catch {
34126
34126
  }
34127
- return new Promise((resolve3, reject) => {
34127
+ return new Promise((resolve5, reject) => {
34128
34128
  if (signal.aborted) {
34129
34129
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
34130
34130
  return;
34131
34131
  }
34132
- const timeoutId = setTimeout(resolve3, interval);
34132
+ const timeoutId = setTimeout(resolve5, interval);
34133
34133
  signal.addEventListener("abort", () => {
34134
34134
  clearTimeout(timeoutId);
34135
34135
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -35229,7 +35229,7 @@ var McpServer = class {
35229
35229
  let task = createTaskResult.task;
35230
35230
  const pollInterval = task.pollInterval ?? 5e3;
35231
35231
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
35232
- await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
35232
+ await new Promise((resolve5) => setTimeout(resolve5, pollInterval));
35233
35233
  const updatedTask = await extra.taskStore.getTask(taskId);
35234
35234
  if (!updatedTask) {
35235
35235
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -35872,20 +35872,20 @@ var StdioServerTransport = class {
35872
35872
  this.onclose?.();
35873
35873
  }
35874
35874
  send(message) {
35875
- return new Promise((resolve3) => {
35875
+ return new Promise((resolve5) => {
35876
35876
  const json = serializeMessage(message);
35877
35877
  if (this._stdout.write(json)) {
35878
- resolve3();
35878
+ resolve5();
35879
35879
  } else {
35880
- this._stdout.once("drain", resolve3);
35880
+ this._stdout.once("drain", resolve5);
35881
35881
  }
35882
35882
  });
35883
35883
  }
35884
35884
  };
35885
35885
 
35886
- // src/tools/core.ts
35887
- import * as fs2 from "fs";
35888
- import * as path3 from "path";
35886
+ // src/mcp/core.ts
35887
+ import * as fs3 from "fs";
35888
+ import * as path4 from "path";
35889
35889
 
35890
35890
  // src/utils/utils.ts
35891
35891
  function getWorkspaceRoot() {
@@ -35898,42 +35898,128 @@ function mcpJson(obj) {
35898
35898
  return mcpText(JSON.stringify(obj, null, 2));
35899
35899
  }
35900
35900
 
35901
- // src/tools/config.ts
35902
- import * as fs from "fs";
35903
- import * as path from "path";
35901
+ // src/core/config/index.ts
35902
+ import * as fs2 from "fs";
35903
+ import * as path2 from "path";
35904
35904
 
35905
- // src/memory/types.ts
35905
+ // src/services/memory/constants.ts
35906
35906
  var DEFAULT_MEMORY_CONFIG = {
35907
35907
  enabled: false,
35908
35908
  wikiDir: "",
35909
35909
  topK: 5,
35910
35910
  chunkSize: 1500,
35911
35911
  overlapLines: 2,
35912
- embeddingModel: "Xenova/bge-small-en-v1.5",
35912
+ embeddingModel: "small" /* SMALL */,
35913
35913
  vectorDimension: 384
35914
35914
  };
35915
+ var LOCK_RETRY_MS = 50;
35916
+ var LOCK_TIMEOUT_MS = 500;
35917
+ var RRF_K = 60;
35915
35918
 
35916
- // src/tools/config.ts
35919
+ // src/utils/files.ts
35920
+ import * as fs from "node:fs";
35921
+ import * as path from "node:path";
35922
+ function atomicWriteTextFile(filePath, content) {
35923
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
35924
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
35925
+ fs.writeFileSync(tempPath, content, "utf8");
35926
+ fs.renameSync(tempPath, filePath);
35927
+ }
35928
+ function atomicWriteJsonFile(filePath, value) {
35929
+ atomicWriteTextFile(filePath, `${JSON.stringify(value, null, 2)}
35930
+ `);
35931
+ }
35932
+ function tryWriteFileExclusive(filePath, content) {
35933
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
35934
+ try {
35935
+ fs.writeFileSync(filePath, content, { flag: "wx" });
35936
+ return true;
35937
+ } catch (err) {
35938
+ if (err.code === "EEXIST") return false;
35939
+ throw err;
35940
+ }
35941
+ }
35942
+ async function acquireLock(lockPath, timeoutMs, retryMs) {
35943
+ const deadline = Date.now() + timeoutMs;
35944
+ while (Date.now() < deadline) {
35945
+ try {
35946
+ if (tryWriteFileExclusive(lockPath, String(process.pid))) return true;
35947
+ } catch {
35948
+ }
35949
+ await new Promise((r) => setTimeout(r, retryMs));
35950
+ }
35951
+ return false;
35952
+ }
35953
+ function releaseLock(lockPath, opts = {}) {
35954
+ try {
35955
+ fs.unlinkSync(lockPath);
35956
+ } catch (err) {
35957
+ if (opts.ignoreErrors || err.code === "ENOENT") return;
35958
+ throw err;
35959
+ }
35960
+ }
35961
+ function tryAcquireProcessLock(lockPath) {
35962
+ const lockContent = JSON.stringify({ pid: process.pid });
35963
+ if (tryWriteFileExclusive(lockPath, lockContent)) return true;
35964
+ let pid;
35965
+ try {
35966
+ const raw = fs.readFileSync(lockPath, "utf8");
35967
+ pid = JSON.parse(raw).pid;
35968
+ } catch {
35969
+ }
35970
+ if (pid !== void 0) {
35971
+ try {
35972
+ process.kill(pid, 0);
35973
+ return false;
35974
+ } catch (err) {
35975
+ if (err.code !== "ESRCH") return false;
35976
+ }
35977
+ }
35978
+ try {
35979
+ fs.unlinkSync(lockPath);
35980
+ } catch {
35981
+ }
35982
+ return tryWriteFileExclusive(lockPath, lockContent);
35983
+ }
35984
+
35985
+ // src/core/config/index.ts
35917
35986
  function resolveMemoryConfig(settings2, workspaceRoot2) {
35918
35987
  const override = settings2.memory ?? {};
35919
35988
  return {
35920
35989
  ...DEFAULT_MEMORY_CONFIG,
35921
35990
  ...override,
35922
35991
  enabled: override.enabled ?? DEFAULT_MEMORY_CONFIG.enabled,
35923
- wikiDir: typeof override.wikiDir === "string" && override.wikiDir.length > 0 ? override.wikiDir : path.join(workspaceRoot2, ".agent-kit", "wiki")
35992
+ wikiDir: typeof override.wikiDir === "string" && override.wikiDir.length > 0 ? override.wikiDir : path2.join(workspaceRoot2, ".agent-kit", "wiki")
35924
35993
  };
35925
35994
  }
35926
35995
  function loadProjectSettings(projectDir) {
35927
- const settingsPath = path.join(projectDir, ".agent-kit", "settings.json");
35928
- if (fs.existsSync(settingsPath)) {
35996
+ const settingsPath = path2.join(projectDir, ".agent-kit", "settings.json");
35997
+ if (fs2.existsSync(settingsPath)) {
35929
35998
  try {
35930
- return JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
35999
+ return JSON.parse(fs2.readFileSync(settingsPath, "utf-8"));
35931
36000
  } catch (error2) {
35932
36001
  console.error(`[agent-kit] Warning: Failed to parse ${settingsPath}:`, error2);
35933
36002
  }
35934
36003
  }
35935
36004
  return {};
35936
36005
  }
36006
+ function writeProjectSettings(workspaceRoot2, patch) {
36007
+ const settingsPath = path2.join(workspaceRoot2, ".agent-kit", "settings.json");
36008
+ const current = loadProjectSettings(workspaceRoot2);
36009
+ atomicWriteJsonFile(settingsPath, { ...current, ...patch });
36010
+ }
36011
+ function resolveConversationDigestConfig(settings2) {
36012
+ const digest = settings2.memory?.conversationDigest;
36013
+ if (!digest?.initialized || typeof digest.modelId !== "string" || typeof digest.initializedAt !== "string") {
36014
+ return void 0;
36015
+ }
36016
+ return {
36017
+ enabled: digest.enabled !== false,
36018
+ initialized: true,
36019
+ modelId: digest.modelId,
36020
+ initializedAt: digest.initializedAt
36021
+ };
36022
+ }
35937
36023
  var FORBIDDEN_FILES = [
35938
36024
  ".env",
35939
36025
  ".bashrc",
@@ -35965,17 +36051,10 @@ var FORBIDDEN_PATTERNS = [
35965
36051
  /secrets\.json$/i,
35966
36052
  /secret\.json$/i
35967
36053
  ];
35968
- var FORBIDDEN_DIRS = [
35969
- ".git",
35970
- ".ssh",
35971
- ".aws",
35972
- ".kube",
35973
- ".gnupg",
35974
- ".docker"
35975
- ];
36054
+ var FORBIDDEN_DIRS = [".git", ".ssh", ".aws", ".kube", ".gnupg", ".docker"];
35976
36055
 
35977
- // src/tools/handoffs.ts
35978
- import * as path2 from "path";
36056
+ // src/core/handoffs/index.ts
36057
+ import * as path3 from "path";
35979
36058
  var HANDOFF_TYPES = [
35980
36059
  "brainstorm",
35981
36060
  "clarification",
@@ -36018,16 +36097,16 @@ function resolveHandoffPath(input) {
36018
36097
  content: input.content,
36019
36098
  type: canonicalType
36020
36099
  });
36021
- const relativePath = path2.join(".agent-kit", "handoffs", featureSlug, `${canonicalType}.md`);
36100
+ const relativePath = path3.join(".agent-kit", "handoffs", featureSlug, `${canonicalType}.md`);
36022
36101
  return {
36023
36102
  featureSlug,
36024
36103
  canonicalType,
36025
- filePath: path2.join(input.workspaceRoot, relativePath),
36104
+ filePath: path3.join(input.workspaceRoot, relativePath),
36026
36105
  relativePath
36027
36106
  };
36028
36107
  }
36029
36108
 
36030
- // src/tools/core.ts
36109
+ // src/mcp/core.ts
36031
36110
  function registerCoreTools(server2) {
36032
36111
  server2.tool(
36033
36112
  "kit_save_handoff",
@@ -36047,11 +36126,11 @@ function registerCoreTools(server2) {
36047
36126
  slug,
36048
36127
  content
36049
36128
  });
36050
- const handoffDir = path3.dirname(location.filePath);
36051
- if (!fs2.existsSync(handoffDir)) {
36052
- fs2.mkdirSync(handoffDir, { recursive: true });
36129
+ const handoffDir = path4.dirname(location.filePath);
36130
+ if (!fs3.existsSync(handoffDir)) {
36131
+ fs3.mkdirSync(handoffDir, { recursive: true });
36053
36132
  }
36054
- fs2.writeFileSync(location.filePath, content, "utf8");
36133
+ fs3.writeFileSync(location.filePath, content, "utf8");
36055
36134
  return mcpText(`\u2705 Saved to: ${location.filePath}`);
36056
36135
  } catch (error2) {
36057
36136
  return mcpText(`Error saving handoff: ${error2}`);
@@ -36060,31 +36139,31 @@ function registerCoreTools(server2) {
36060
36139
  );
36061
36140
  }
36062
36141
 
36063
- // src/tools/agent.ts
36142
+ // src/mcp/agent.ts
36064
36143
  import { spawn } from "child_process";
36065
36144
  import { randomUUID } from "crypto";
36066
- import * as fs3 from "fs";
36067
- import * as path5 from "path";
36145
+ import * as fs4 from "fs";
36146
+ import * as path6 from "path";
36068
36147
 
36069
- // src/tools/security.ts
36148
+ // src/core/security/index.ts
36070
36149
  import { execFileSync } from "child_process";
36071
- import * as path4 from "path";
36150
+ import * as path5 from "path";
36072
36151
  function sanitize(input) {
36073
36152
  return String(input).replace(/[;&|`$<>\\]/g, "").trim().slice(0, 500);
36074
36153
  }
36075
36154
  function validatePath(filePath, baseDir = process.cwd()) {
36076
- const resolved = path4.resolve(baseDir, filePath);
36077
- const root = path4.resolve(baseDir);
36078
- if (resolved !== root && !resolved.startsWith(root + path4.sep)) {
36155
+ const resolved = path5.resolve(baseDir, filePath);
36156
+ const root = path5.resolve(baseDir);
36157
+ if (resolved !== root && !resolved.startsWith(root + path5.sep)) {
36079
36158
  throw new Error(`Path traversal detected: ${filePath}`);
36080
36159
  }
36081
- const segments = resolved.split(path4.sep);
36160
+ const segments = resolved.split(path5.sep);
36082
36161
  for (const segment of segments) {
36083
36162
  if (FORBIDDEN_DIRS.some((d) => segment.toLowerCase() === d.toLowerCase())) {
36084
36163
  throw new Error(`Access to sensitive directory is FORBIDDEN: ${segment}`);
36085
36164
  }
36086
36165
  }
36087
- const fileName = path4.basename(resolved);
36166
+ const fileName = path5.basename(resolved);
36088
36167
  const isExactForbidden = FORBIDDEN_FILES.some((f) => fileName.toLowerCase() === f.toLowerCase());
36089
36168
  const isPatternForbidden = FORBIDDEN_PATTERNS.some((re) => re.test(fileName));
36090
36169
  if (isExactForbidden || isPatternForbidden) {
@@ -36092,7 +36171,6 @@ function validatePath(filePath, baseDir = process.cwd()) {
36092
36171
  }
36093
36172
  return resolved;
36094
36173
  }
36095
- var GIT_TIMEOUT = parseInt(process.env.GEMINI_KIT_GIT_TIMEOUT || "30000", 10);
36096
36174
  function commandExists(cmd) {
36097
36175
  try {
36098
36176
  const checkCmd = process.platform === "win32" ? "where" : "which";
@@ -36117,7 +36195,7 @@ function sanitizeOutput(text) {
36117
36195
  // AWS access keys
36118
36196
  /xox[baprs]-[A-Za-z0-9-]{10,}/g,
36119
36197
  // Slack tokens
36120
- /[Aa]tlassian[_\-]?[Aa][Pp][Ii][_\-]?[Tt]oken[^\s]*\s*[:=]\s*\S+/g
36198
+ /[Aa]tlassian[_-]?[Aa][Pp][Ii][_-]?[Tt]oken[^\s]*\s*[:=]\s*\S+/g
36121
36199
  // Atlassian token patterns
36122
36200
  ];
36123
36201
  let sanitized = text;
@@ -36127,7 +36205,7 @@ function sanitizeOutput(text) {
36127
36205
  return sanitized;
36128
36206
  }
36129
36207
 
36130
- // src/tools/agent.ts
36208
+ // src/mcp/agent.ts
36131
36209
  var AGENT_TIMEOUT = parseInt(process.env.KIT_AGENT_TIMEOUT || "300000", 10);
36132
36210
  var jobRegistry = /* @__PURE__ */ new Map();
36133
36211
  var INSTALL_HINTS = {
@@ -36142,36 +36220,27 @@ function agentArgs(agent, prompt, workspaceRoot2) {
36142
36220
  case "claude":
36143
36221
  return ["--dangerously-skip-permissions", "-p", prompt];
36144
36222
  case "codex":
36145
- return [
36146
- "--ask-for-approval",
36147
- "never",
36148
- "--sandbox",
36149
- "workspace-write",
36150
- "--cd",
36151
- workspaceRoot2,
36152
- "exec",
36153
- prompt
36154
- ];
36223
+ return ["--ask-for-approval", "never", "--sandbox", "workspace-write", "--cd", workspaceRoot2, "exec", prompt];
36155
36224
  }
36156
36225
  }
36157
36226
  function taskSlug(task) {
36158
36227
  return task.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 6).join("-").slice(0, 48);
36159
36228
  }
36160
36229
  function initAgentLog(workspaceRoot2, slug, agent) {
36161
- const logsDir = path5.join(workspaceRoot2, ".agent-kit", "logs", "agents");
36162
- fs3.mkdirSync(logsDir, { recursive: true });
36230
+ const logsDir = path6.join(workspaceRoot2, ".agent-kit", "logs", "agents");
36231
+ fs4.mkdirSync(logsDir, { recursive: true });
36163
36232
  const now = (/* @__PURE__ */ new Date()).toISOString();
36164
- const logFile = path5.join(logsDir, `delegate-${now}-${slug}.log`);
36233
+ const logFile = path6.join(logsDir, `delegate-${now}-${slug}.log`);
36165
36234
  const header = `Agent: ${agent}
36166
36235
  Date: ${now}
36167
36236
  ${"\u2500".repeat(60)}
36168
36237
 
36169
36238
  `;
36170
- fs3.writeFileSync(logFile, header, "utf-8");
36171
- const logStream = fs3.createWriteStream(logFile, { flags: "a" });
36239
+ fs4.writeFileSync(logFile, header, "utf-8");
36240
+ const logStream = fs4.createWriteStream(logFile, { flags: "a" });
36172
36241
  logStream.on("error", () => {
36173
36242
  });
36174
- return { logPath: path5.relative(workspaceRoot2, logFile), logStream };
36243
+ return { logPath: path6.relative(workspaceRoot2, logFile), logStream };
36175
36244
  }
36176
36245
  function registerAgentTools(server2) {
36177
36246
  server2.tool(
@@ -36179,9 +36248,7 @@ function registerAgentTools(server2) {
36179
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 file (.agent-kit/handoffs/feature-slug/plan.md).",
36180
36249
  {
36181
36250
  agent: external_exports.enum(["gemini", "claude", "codex"]).describe('The agent CLI to invoke: "gemini", "claude", or "codex"'),
36182
- task: external_exports.string().describe(
36183
- 'Task message or path to a handoff file (e.g., ".agent-kit/handoffs/feature-slug/plan.md")'
36184
- )
36251
+ task: external_exports.string().describe('Task message or path to a handoff file (e.g., ".agent-kit/handoffs/feature-slug/plan.md")')
36185
36252
  },
36186
36253
  async ({ agent, task }, extra) => {
36187
36254
  const workspaceRoot2 = getWorkspaceRoot();
@@ -36190,11 +36257,11 @@ function registerAgentTools(server2) {
36190
36257
  let killChild;
36191
36258
  try {
36192
36259
  let prompt = task;
36193
- const resolvedTask = path5.resolve(workspaceRoot2, task);
36194
- if (fs3.existsSync(resolvedTask) && fs3.statSync(resolvedTask).isFile()) {
36260
+ const resolvedTask = path6.resolve(workspaceRoot2, task);
36261
+ if (fs4.existsSync(resolvedTask) && fs4.statSync(resolvedTask).isFile()) {
36195
36262
  try {
36196
36263
  validatePath(task, workspaceRoot2);
36197
- prompt = fs3.readFileSync(resolvedTask, "utf-8");
36264
+ prompt = fs4.readFileSync(resolvedTask, "utf-8");
36198
36265
  } catch (err) {
36199
36266
  return mcpText(`Error reading task file: ${err}`);
36200
36267
  }
@@ -36247,7 +36314,7 @@ function registerAgentTools(server2) {
36247
36314
  };
36248
36315
  if (extra.signal.aborted) killChild();
36249
36316
  else extra.signal.addEventListener("abort", killChild);
36250
- await new Promise((resolve3, reject) => {
36317
+ await new Promise((resolve5, reject) => {
36251
36318
  child.stdout?.on("data", (data) => {
36252
36319
  job.chunks.push(data.toString());
36253
36320
  job.logStream.write(data);
@@ -36257,11 +36324,8 @@ function registerAgentTools(server2) {
36257
36324
  job.logStream.write(data);
36258
36325
  });
36259
36326
  child.on("close", (code, signal) => {
36260
- if (code === 0) resolve3();
36261
- else
36262
- reject(
36263
- new Error(`Process exited with code ${code}${signal ? ` (signal: ${signal})` : ""}`)
36264
- );
36327
+ if (code === 0) resolve5();
36328
+ else reject(new Error(`Process exited with code ${code}${signal ? ` (signal: ${signal})` : ""}`));
36265
36329
  });
36266
36330
  child.on("error", reject);
36267
36331
  });
@@ -36291,21 +36355,21 @@ function registerAgentTools(server2) {
36291
36355
  );
36292
36356
  }
36293
36357
 
36294
- // src/tools/integration.ts
36295
- import { writeFileSync as writeFileSync3 } from "fs";
36358
+ // src/mcp/integration.ts
36359
+ import { writeFileSync as writeFileSync4 } from "fs";
36296
36360
 
36297
- // src/utils/credentials.ts
36298
- import * as fs4 from "fs";
36361
+ // src/services/integration/credentials.ts
36362
+ import * as fs5 from "fs";
36299
36363
 
36300
36364
  // src/utils/paths.ts
36301
36365
  import * as os from "os";
36302
- import * as path6 from "path";
36303
- var AGENT_KIT_HOME = path6.join(os.homedir(), ".agent-kit");
36304
- var CREDENTIALS_FILE = path6.join(AGENT_KIT_HOME, "credentials");
36305
- var MODEL_CACHE_DIR = path6.join(AGENT_KIT_HOME, "cache", "models");
36306
- var FASTEMBED_CACHE_DIR = path6.join(MODEL_CACHE_DIR, "fastembed");
36366
+ import * as path7 from "path";
36367
+ var AGENT_KIT_HOME = path7.join(os.homedir(), ".agent-kit");
36368
+ var CREDENTIALS_FILE = path7.join(AGENT_KIT_HOME, "credentials");
36369
+ var MODEL_CACHE_DIR = path7.join(AGENT_KIT_HOME, "cache", "models");
36370
+ var FASTEMBED_CACHE_DIR = path7.join(MODEL_CACHE_DIR, "fastembed");
36307
36371
 
36308
- // src/utils/credentials.ts
36372
+ // src/services/integration/credentials.ts
36309
36373
  var cachedCredentials = null;
36310
36374
  function parseIni(content) {
36311
36375
  const result = {};
@@ -36330,12 +36394,12 @@ function parseIni(content) {
36330
36394
  }
36331
36395
  function loadCredentials() {
36332
36396
  if (cachedCredentials !== null) return cachedCredentials;
36333
- if (!fs4.existsSync(CREDENTIALS_FILE)) {
36397
+ if (!fs5.existsSync(CREDENTIALS_FILE)) {
36334
36398
  cachedCredentials = {};
36335
36399
  return cachedCredentials;
36336
36400
  }
36337
36401
  try {
36338
- const stat = fs4.statSync(CREDENTIALS_FILE);
36402
+ const stat = fs5.statSync(CREDENTIALS_FILE);
36339
36403
  const mode = stat.mode & 511;
36340
36404
  if (mode !== 384) {
36341
36405
  process.stderr.write(
@@ -36345,7 +36409,7 @@ function loadCredentials() {
36345
36409
  }
36346
36410
  } catch {
36347
36411
  }
36348
- const content = fs4.readFileSync(CREDENTIALS_FILE, "utf-8");
36412
+ const content = fs5.readFileSync(CREDENTIALS_FILE, "utf-8");
36349
36413
  const ini = parseIni(content);
36350
36414
  const profile = process.env.KIT_PROFILE ?? "default";
36351
36415
  cachedCredentials = ini[profile] ?? {};
@@ -36364,7 +36428,7 @@ function getCredential(key) {
36364
36428
  return loadCredentials()[key];
36365
36429
  }
36366
36430
 
36367
- // src/utils/parser.ts
36431
+ // src/services/integration/parser/adf.ts
36368
36432
  function adfToMarkdown(adf) {
36369
36433
  if (!adf || typeof adf !== "object") return "No description";
36370
36434
  const content = adf.content;
@@ -36474,9 +36538,7 @@ ${renderNodes(node.content, listDepth, node.type).trim()}
36474
36538
  for (const row of rows) {
36475
36539
  if (row.type !== "tableRow") continue;
36476
36540
  const cells = row.content || [];
36477
- const rendered = cells.map(
36478
- (c) => renderNodes(c.content).trim().replace(/\n+/g, " ").replace(/\|/g, "\\|")
36479
- );
36541
+ const rendered = cells.map((c) => renderNodes(c.content).trim().replace(/\n+/g, " ").replace(/\|/g, "\\|"));
36480
36542
  result += "| " + rendered.join(" | ") + " |\n";
36481
36543
  if (isFirstRow) {
36482
36544
  result += "| " + cells.map(() => "---").join(" | ") + " |\n";
@@ -36488,7 +36550,7 @@ ${renderNodes(node.content, listDepth, node.type).trim()}
36488
36550
  return renderNodes(content).trim() || "No description";
36489
36551
  }
36490
36552
 
36491
- // src/tools/integration.ts
36553
+ // src/mcp/integration.ts
36492
36554
  var BitbucketPrSchema = external_exports.object({
36493
36555
  id: external_exports.number(),
36494
36556
  title: external_exports.string(),
@@ -36545,9 +36607,7 @@ function registerIntegrationTools(server2) {
36545
36607
  "Get Bitbucket PR details and optionally the diff. Accepts a full PR URL or a numeric PR ID with workspace + repoSlug.",
36546
36608
  {
36547
36609
  input: external_exports.string().describe("Bitbucket PR URL or numeric PR ID"),
36548
- workspace: external_exports.string().optional().describe(
36549
- "Bitbucket workspace slug (required for numeric ID if BITBUCKET_DEFAULT_WORKSPACE not set)"
36550
- ),
36610
+ workspace: external_exports.string().optional().describe("Bitbucket workspace slug (required for numeric ID if BITBUCKET_DEFAULT_WORKSPACE not set)"),
36551
36611
  repoSlug: external_exports.string().optional().describe("Bitbucket repo slug (required for numeric ID)"),
36552
36612
  includeDiff: external_exports.boolean().optional().default(true).describe("Include unified diff in response")
36553
36613
  },
@@ -36572,9 +36632,7 @@ function registerIntegrationTools(server2) {
36572
36632
  );
36573
36633
  }
36574
36634
  if (!repo || !prId) {
36575
- return mcpText(
36576
- `\u274C Could not parse PR URL. Expected: bitbucket.org/{ws}/{repo}/pull-requests/{id}`
36577
- );
36635
+ return mcpText(`\u274C Could not parse PR URL. Expected: bitbucket.org/{ws}/{repo}/pull-requests/{id}`);
36578
36636
  }
36579
36637
  const safeWs = sanitize(ws);
36580
36638
  const safeRepo = sanitize(repo);
@@ -36606,7 +36664,7 @@ ${diff}
36606
36664
  } else {
36607
36665
  const filePath = `/tmp/kit-pr-${prId}-${Date.now()}.diff`;
36608
36666
  try {
36609
- writeFileSync3(filePath, diff, "utf8");
36667
+ writeFileSync4(filePath, diff, "utf8");
36610
36668
  output += `
36611
36669
 
36612
36670
  ### Diff
@@ -36678,10 +36736,10 @@ ${ticket.fields.labels?.join(", ") || "None"}`;
36678
36736
  );
36679
36737
  }
36680
36738
 
36681
- // src/tools/memory.ts
36682
- import * as path9 from "path";
36739
+ // src/mcp/memory.ts
36740
+ import * as path14 from "path";
36683
36741
 
36684
- // src/memory/embedder.ts
36742
+ // src/services/memory/embedder.ts
36685
36743
  var EmbedderError = class extends Error {
36686
36744
  constructor(message, cause) {
36687
36745
  super(message);
@@ -36718,16 +36776,25 @@ var Embedder = class {
36718
36776
  isReady() {
36719
36777
  return this.pipeline !== null;
36720
36778
  }
36779
+ async initialize() {
36780
+ if (!this.pipeline) {
36781
+ await this.loadModel();
36782
+ }
36783
+ }
36721
36784
  async loadModel() {
36722
36785
  try {
36723
36786
  const { EmbeddingModel, FlagEmbedding } = await import("fastembed");
36724
36787
  const modelMap = {
36725
- "Xenova/bge-small-en-v1.5": EmbeddingModel.BGESmallENV15,
36726
- "Xenova/bge-base-en-v1.5": EmbeddingModel.BGEBaseENV15,
36727
- "Xenova/all-MiniLM-L6-v2": EmbeddingModel.AllMiniLML6V2
36788
+ ["tiny" /* TINY */]: EmbeddingModel.AllMiniLML6V2,
36789
+ ["small" /* SMALL */]: EmbeddingModel.BGESmallENV15,
36790
+ ["large" /* LARGE */]: EmbeddingModel.BGEBaseENV15
36728
36791
  };
36729
36792
  const model = modelMap[this.modelName] ?? EmbeddingModel.BGESmallENV15;
36730
- const embedder = await FlagEmbedding.init({ model, cacheDir: FASTEMBED_CACHE_DIR, showDownloadProgress: false });
36793
+ const embedder = await FlagEmbedding.init({
36794
+ model,
36795
+ cacheDir: FASTEMBED_CACHE_DIR,
36796
+ showDownloadProgress: false
36797
+ });
36731
36798
  this.pipeline = async (texts) => {
36732
36799
  const result = [];
36733
36800
  for await (const batch of embedder.embed(texts, texts.length)) {
@@ -36743,14 +36810,19 @@ var Embedder = class {
36743
36810
  }
36744
36811
  };
36745
36812
 
36746
- // src/memory/indexer.ts
36747
- import * as fs5 from "fs";
36748
- import * as path7 from "path";
36813
+ // src/services/memory/indexer.ts
36814
+ import * as fs6 from "fs";
36815
+ import * as path8 from "path";
36749
36816
 
36750
- // src/memory/chunker.ts
36751
- import { createHash } from "crypto";
36817
+ // src/utils/hash.ts
36818
+ import { createHash } from "node:crypto";
36819
+ function sha256Hex(content) {
36820
+ return createHash("sha256").update(content).digest("hex");
36821
+ }
36822
+
36823
+ // src/services/memory/chunker.ts
36752
36824
  function computeChunkId(content) {
36753
- return createHash("sha256").update(content).digest("hex").slice(0, 16);
36825
+ return sha256Hex(content).slice(0, 16);
36754
36826
  }
36755
36827
  function cleanContentForEmbedding(text) {
36756
36828
  return text.replace(/<!--[\s\S]*?-->/g, "");
@@ -36879,27 +36951,7 @@ ${sectionText}` : sectionText;
36879
36951
  return chunks;
36880
36952
  }
36881
36953
 
36882
- // src/memory/indexer.ts
36883
- var LOCK_RETRY_MS = 50;
36884
- var LOCK_TIMEOUT_MS = 500;
36885
- async function acquireLock(lockPath) {
36886
- const deadline = Date.now() + LOCK_TIMEOUT_MS;
36887
- while (Date.now() < deadline) {
36888
- try {
36889
- fs5.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
36890
- return true;
36891
- } catch {
36892
- await new Promise((r) => setTimeout(r, LOCK_RETRY_MS));
36893
- }
36894
- }
36895
- return false;
36896
- }
36897
- function releaseLock(lockPath) {
36898
- try {
36899
- fs5.unlinkSync(lockPath);
36900
- } catch {
36901
- }
36902
- }
36954
+ // src/services/memory/indexer.ts
36903
36955
  var MemoryIndexer = class {
36904
36956
  constructor(store, embedder, config2) {
36905
36957
  this.store = store;
@@ -36911,26 +36963,26 @@ var MemoryIndexer = class {
36911
36963
  return this.indexFileRelativeTo(absolutePath, this.config.wikiDir);
36912
36964
  }
36913
36965
  async indexFileRelativeTo(absolutePath, sourceRoot) {
36914
- const source = path7.relative(sourceRoot, absolutePath);
36915
- const existingHashes = this.store.hashesBySource(source);
36966
+ const source = path8.relative(sourceRoot, absolutePath);
36967
+ const existingChunkIds = this.store.hashesBySource(source);
36916
36968
  let text;
36917
36969
  try {
36918
- text = fs5.readFileSync(absolutePath, "utf8");
36970
+ text = fs6.readFileSync(absolutePath, "utf8");
36919
36971
  } catch (err) {
36920
36972
  console.warn(`[memory-indexer] Cannot read file ${absolutePath}:`, err);
36921
- return { indexed: 0, deleted: 0, skipped: existingHashes.size };
36973
+ return { indexed: 0, deleted: 0, skipped: existingChunkIds.size };
36922
36974
  }
36923
36975
  const allChunks = chunkMarkdown(text, source, this.config);
36924
- const newHashes = new Set(allChunks.map((c) => c.id));
36925
- const toIndex = allChunks.filter((c) => !existingHashes.has(c.id));
36926
- const toDelete = [...existingHashes].filter((h) => !newHashes.has(h));
36976
+ const currentChunkIds = new Set(allChunks.map((chunk) => chunk.id));
36977
+ const toIndex = allChunks.filter((chunk) => !existingChunkIds.has(chunk.id));
36978
+ const toDelete = [...existingChunkIds].filter((id) => !currentChunkIds.has(id));
36927
36979
  let embeddings = [];
36928
36980
  if (toIndex.length > 0) {
36929
36981
  try {
36930
36982
  embeddings = await this.embedder.embed(toIndex.map((c) => c.content));
36931
36983
  } catch (err) {
36932
36984
  console.warn(`[memory-indexer] Embedding failed for ${absolutePath}:`, err);
36933
- return { indexed: 0, deleted: 0, skipped: existingHashes.size };
36985
+ return { indexed: 0, deleted: 0, skipped: existingChunkIds.size };
36934
36986
  }
36935
36987
  }
36936
36988
  if (toIndex.length > 0) {
@@ -36947,20 +36999,20 @@ var MemoryIndexer = class {
36947
36999
  }
36948
37000
  async indexDirectory(rootDir, opts = {}) {
36949
37001
  const totals = { indexed: 0, deleted: 0, skipped: 0 };
36950
- if (!fs5.existsSync(rootDir)) return totals;
37002
+ if (!fs6.existsSync(rootDir)) return totals;
36951
37003
  const relativeBase = opts.relativeBase ?? this.config.wikiDir;
36952
37004
  const excludeFiles = new Set(opts.excludeFiles ?? []);
36953
37005
  const files = [];
36954
37006
  const walk = (dirPath) => {
36955
37007
  let entries;
36956
37008
  try {
36957
- entries = fs5.readdirSync(dirPath, { withFileTypes: true });
37009
+ entries = fs6.readdirSync(dirPath, { withFileTypes: true });
36958
37010
  } catch (err) {
36959
37011
  console.warn(`[memory-indexer] Cannot scan directory ${dirPath}:`, err);
36960
37012
  return;
36961
37013
  }
36962
37014
  for (const entry of entries) {
36963
- const entryPath = path7.join(dirPath, entry.name);
37015
+ const entryPath = path8.join(dirPath, entry.name);
36964
37016
  if (entry.isDirectory()) {
36965
37017
  walk(entryPath);
36966
37018
  continue;
@@ -36971,9 +37023,7 @@ var MemoryIndexer = class {
36971
37023
  }
36972
37024
  };
36973
37025
  walk(rootDir);
36974
- const currentSources = new Set(
36975
- files.map((file) => path7.relative(relativeBase, file))
36976
- );
37026
+ const currentSources = new Set(files.map((file) => path8.relative(relativeBase, file)));
36977
37027
  for (const stale of this.store.indexedSources()) {
36978
37028
  if (!currentSources.has(stale)) {
36979
37029
  this.store.deleteBySource(stale);
@@ -37004,22 +37054,21 @@ var MemoryIndexer = class {
37004
37054
  if (bm25Ids.size > 0) {
37005
37055
  denseResults = denseResults.filter((result) => bm25Ids.has(result.id));
37006
37056
  }
37007
- const k = 60;
37008
37057
  const scoreMap = /* @__PURE__ */ new Map();
37009
37058
  denseResults.forEach((r, rank) => {
37010
- scoreMap.set(r.id, { dense: 1 / (k + rank + 1), bm25: 0 });
37059
+ scoreMap.set(r.id, { dense: 1 / (RRF_K + rank + 1), bm25: 0 });
37011
37060
  });
37012
37061
  bm25Results.forEach((r, rank) => {
37013
37062
  const existing = scoreMap.get(r.id);
37014
37063
  if (existing) {
37015
- existing.bm25 = 1 / (k + rank + 1);
37064
+ existing.bm25 = 1 / (RRF_K + rank + 1);
37016
37065
  } else {
37017
- scoreMap.set(r.id, { dense: 0, bm25: 1 / (k + rank + 1) });
37066
+ scoreMap.set(r.id, { dense: 0, bm25: 1 / (RRF_K + rank + 1) });
37018
37067
  }
37019
37068
  });
37020
37069
  const hasDense = this.store.vecAvailable && denseResults.length > 0;
37021
37070
  const numRetrievers = hasDense ? 2 : 1;
37022
- const maxScore = numRetrievers / 61;
37071
+ const maxScore = numRetrievers / (RRF_K + 1);
37023
37072
  const ranked = [...scoreMap.entries()].map(([id, scores]) => ({
37024
37073
  id,
37025
37074
  totalScore: scores.dense + scores.bm25,
@@ -37040,7 +37089,7 @@ var MemoryIndexer = class {
37040
37089
  const retriever = r.hasDense && r.hasBm25 ? "both" : r.hasDense ? "dense" : "bm25";
37041
37090
  let contentSource = "file";
37042
37091
  try {
37043
- chunk.content = fs5.readFileSync(path7.join(this.config.wikiDir, chunk.source), "utf8");
37092
+ chunk.content = fs6.readFileSync(path8.join(this.config.wikiDir, chunk.source), "utf8");
37044
37093
  } catch {
37045
37094
  contentSource = "fallback";
37046
37095
  }
@@ -37050,26 +37099,27 @@ var MemoryIndexer = class {
37050
37099
  }
37051
37100
  async save(content) {
37052
37101
  const datePart = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
37053
- const rawDir = path7.join(this.config.wikiDir, "raw");
37054
- const savePath = path7.join(rawDir, `conv_save_${datePart}.md`);
37102
+ const rawDir = path8.join(this.config.wikiDir, "raw");
37103
+ const savePath = path8.join(rawDir, `conv_save_${datePart}.md`);
37055
37104
  const lockPath = `${savePath}.lock`;
37056
- fs5.mkdirSync(rawDir, { recursive: true });
37057
- const acquired = await acquireLock(lockPath);
37105
+ fs6.mkdirSync(rawDir, { recursive: true });
37106
+ const acquired = await acquireLock(lockPath, LOCK_TIMEOUT_MS, LOCK_RETRY_MS);
37058
37107
  if (!acquired) {
37059
37108
  console.warn("[memory-indexer] Could not acquire write lock, writing anyway (fail-open)");
37060
37109
  }
37061
37110
  try {
37062
- fs5.appendFileSync(savePath, `
37111
+ fs6.appendFileSync(savePath, `
37063
37112
  ${content}
37064
37113
  `, "utf8");
37065
37114
  } finally {
37066
- if (acquired) releaseLock(lockPath);
37115
+ if (acquired) releaseLock(lockPath, { ignoreErrors: true });
37067
37116
  }
37068
37117
  return { indexed: 0, deleted: 0, skipped: 0 };
37069
37118
  }
37070
37119
  async startupIndex() {
37071
37120
  try {
37072
- await this.indexDirectory(path7.join(this.config.wikiDir, "compiled"), {
37121
+ await this.embedder.initialize();
37122
+ await this.indexDirectory(path8.join(this.config.wikiDir, "compiled"), {
37073
37123
  relativeBase: this.config.wikiDir,
37074
37124
  excludeFiles: ["index.md", "log.md"]
37075
37125
  });
@@ -37084,11 +37134,11 @@ ${content}
37084
37134
  }
37085
37135
  };
37086
37136
 
37087
- // src/memory/store.ts
37137
+ // src/services/memory/store.ts
37088
37138
  var import_stopword = __toESM(require_stopword_cjs(), 1);
37089
37139
  import Database from "better-sqlite3";
37090
- import * as fs6 from "fs";
37091
- import * as path8 from "path";
37140
+ import * as fs7 from "fs";
37141
+ import * as path9 from "path";
37092
37142
  var StoreError = class extends Error {
37093
37143
  constructor(message, cause) {
37094
37144
  super(message);
@@ -37099,7 +37149,7 @@ var StoreError = class extends Error {
37099
37149
  var MemoryStore = class {
37100
37150
  constructor(dbPath, config2) {
37101
37151
  this.config = config2;
37102
- fs6.mkdirSync(path8.dirname(dbPath), { recursive: true });
37152
+ fs7.mkdirSync(path9.dirname(dbPath), { recursive: true });
37103
37153
  this.db = this.openDatabase(dbPath);
37104
37154
  }
37105
37155
  db;
@@ -37115,7 +37165,7 @@ var MemoryStore = class {
37115
37165
  } catch (err) {
37116
37166
  console.error("[memory-store] DB open failed, recreating:", err);
37117
37167
  try {
37118
- fs6.unlinkSync(dbPath);
37168
+ fs7.unlinkSync(dbPath);
37119
37169
  } catch {
37120
37170
  }
37121
37171
  const db = new Database(dbPath);
@@ -37330,8 +37380,424 @@ var MemoryStore = class {
37330
37380
  }
37331
37381
  };
37332
37382
 
37333
- // src/tools/memory.ts
37334
- function registerMemoryToolHandlers(server2, indexer, store, config2) {
37383
+ // src/services/digest/processor.ts
37384
+ import * as fs10 from "node:fs";
37385
+ import * as path13 from "node:path";
37386
+
37387
+ // src/services/digest/constants.ts
37388
+ import * as path10 from "node:path";
37389
+ var DEFAULT_DIGEST_MODEL_ID = "default" /* DEFAULT */;
37390
+ var DEFAULT_DIGEST_MAX_INPUT_CHARS = 3e3;
37391
+ var DEFAULT_DIGEST_TIMEOUT_MS = 12e4;
37392
+ var KIT_DIR = ".agent-kit";
37393
+ var WIKI_DIR = path10.join(KIT_DIR, "wiki");
37394
+ var WIKI_RAW_DIR = path10.join(WIKI_DIR, "raw");
37395
+ var PROVISIONAL_DIGEST_DIR = path10.join(WIKI_DIR, "compiled", "provisional", "conversation-digests");
37396
+ var DIGEST_WORKER_DIR = path10.join(WIKI_DIR, "digest");
37397
+ var DIGEST_LOCKFILE_REL_PATH = path10.join(DIGEST_WORKER_DIR, "digest-worker.lock");
37398
+ var DIGEST_WORKER_STATUS_REL_PATH = path10.join(DIGEST_WORKER_DIR, "digest-worker.status.json");
37399
+ var DIGEST_WORKER_LOG_REL_PATH = path10.join(DIGEST_WORKER_DIR, "digest-worker.log");
37400
+ var LLAMA_CONTEXT_SIZE = 4096;
37401
+ var LLAMA_MAX_GENERATED_TOKENS = 512;
37402
+ var LLAMA_TEMPERATURE = 0.1;
37403
+
37404
+ // src/services/digest/files.ts
37405
+ import * as fs8 from "node:fs";
37406
+ import * as path11 from "node:path";
37407
+ function defaultProvisionalDigestDir(workspaceRoot2) {
37408
+ return path11.join(workspaceRoot2, PROVISIONAL_DIGEST_DIR);
37409
+ }
37410
+ function readConversationDigestInput(workspaceRoot2, inputPath) {
37411
+ const absoluteInput = path11.isAbsolute(inputPath) ? inputPath : path11.resolve(workspaceRoot2, inputPath);
37412
+ const content = fs8.readFileSync(absoluteInput, "utf8");
37413
+ return {
37414
+ sourcePath: path11.relative(workspaceRoot2, absoluteInput),
37415
+ content,
37416
+ contentHash: sha256Hex(content)
37417
+ };
37418
+ }
37419
+ function provisionalDigestPath(outDir, input) {
37420
+ const safeName = path11.basename(input.sourcePath).replace(/\.[^.]+$/, "").replace(/[^a-z0-9._-]+/gi, "-");
37421
+ return path11.join(outDir, `${input.contentHash.slice(0, 16)}-${safeName}.md`);
37422
+ }
37423
+ function findExistingProvisionalDigest(outDir, input) {
37424
+ const markdownPath = provisionalDigestPath(outDir, input);
37425
+ return fs8.existsSync(markdownPath) ? markdownPath : void 0;
37426
+ }
37427
+ function writeProvisionalDigestFile(outDir, input, markdown) {
37428
+ const markdownPath = provisionalDigestPath(outDir, input);
37429
+ atomicWriteTextFile(markdownPath, markdown);
37430
+ return markdownPath;
37431
+ }
37432
+ function writeConversationDigestSettings(workspaceRoot2, digest) {
37433
+ const current = loadProjectSettings(workspaceRoot2);
37434
+ const memory = current.memory ?? {};
37435
+ writeProjectSettings(workspaceRoot2, {
37436
+ memory: {
37437
+ ...memory,
37438
+ enabled: memory.enabled ?? true,
37439
+ conversationDigest: digest
37440
+ }
37441
+ });
37442
+ }
37443
+
37444
+ // src/services/digest/model-registry.ts
37445
+ var DigestModelRegistryError = class extends Error {
37446
+ constructor(message) {
37447
+ super(message);
37448
+ this.name = "DigestModelRegistryError";
37449
+ }
37450
+ };
37451
+ var DIGEST_MODEL_REGISTRY = {
37452
+ ["tiny" /* TINY */]: {
37453
+ id: "tiny" /* TINY */,
37454
+ ggufUri: "hf:bartowski/Qwen2.5-0.5B-Instruct-GGUF/Qwen2.5-0.5B-Instruct-Q4_K_M.gguf",
37455
+ approxSizeBytes: 4e8,
37456
+ license: "Apache-2.0",
37457
+ sourceUrl: "https://huggingface.co/bartowski/Qwen2.5-0.5B-Instruct-GGUF",
37458
+ enabled: true
37459
+ },
37460
+ ["default" /* DEFAULT */]: {
37461
+ id: "default" /* DEFAULT */,
37462
+ ggufUri: "hf:bartowski/Qwen2.5-1.5B-Instruct-GGUF/Qwen2.5-1.5B-Instruct-Q4_K_M.gguf",
37463
+ approxSizeBytes: 1e9,
37464
+ license: "Apache-2.0",
37465
+ sourceUrl: "https://huggingface.co/bartowski/Qwen2.5-1.5B-Instruct-GGUF",
37466
+ enabled: true
37467
+ },
37468
+ ["large" /* LARGE */]: {
37469
+ id: "large" /* LARGE */,
37470
+ ggufUri: "hf:bartowski/Qwen2.5-3B-Instruct-GGUF/Qwen2.5-3B-Instruct-Q4_K_M.gguf",
37471
+ approxSizeBytes: 19e8,
37472
+ license: "Apache-2.0",
37473
+ sourceUrl: "https://huggingface.co/bartowski/Qwen2.5-3B-Instruct-GGUF",
37474
+ enabled: true
37475
+ }
37476
+ };
37477
+ function getDigestModelSpec(modelId) {
37478
+ if (!modelId.trim()) {
37479
+ throw new DigestModelRegistryError("Digest model id is required");
37480
+ }
37481
+ const spec = DIGEST_MODEL_REGISTRY[modelId];
37482
+ if (!spec) {
37483
+ throw new DigestModelRegistryError(`Unknown digest model: ${modelId}`);
37484
+ }
37485
+ if (spec.enabled !== true) {
37486
+ throw new DigestModelRegistryError(`Digest model is disabled: ${modelId}`);
37487
+ }
37488
+ if (!spec.ggufUri || !spec.sourceUrl) {
37489
+ throw new DigestModelRegistryError(`Digest model spec is incomplete: ${modelId}`);
37490
+ }
37491
+ return spec;
37492
+ }
37493
+
37494
+ // src/services/digest/providers/llama-local.ts
37495
+ import * as fs9 from "node:fs";
37496
+ import * as path12 from "node:path";
37497
+ import { getLlama, LlamaChatSession, createModelDownloader } from "node-llama-cpp";
37498
+
37499
+ // src/utils/async.ts
37500
+ async function withTimeout(promise, timeoutMs, createError) {
37501
+ let timer;
37502
+ const timeout = new Promise((_resolve, reject) => {
37503
+ timer = setTimeout(() => {
37504
+ reject(createError());
37505
+ }, timeoutMs);
37506
+ });
37507
+ try {
37508
+ return await Promise.race([promise, timeout]);
37509
+ } finally {
37510
+ if (timer) clearTimeout(timer);
37511
+ }
37512
+ }
37513
+
37514
+ // src/services/digest/providers/llama-local.ts
37515
+ var LlamaLocalDigestProviderError = class extends Error {
37516
+ constructor(message) {
37517
+ super(message);
37518
+ this.name = "LlamaLocalDigestProviderError";
37519
+ }
37520
+ };
37521
+ function titleFromSource(sourcePath) {
37522
+ return path12.basename(sourcePath).replace(/\.[^.]+$/, "").replace(/[_-]+/g, " ");
37523
+ }
37524
+ function trimConversationExport(content, maxInputChars) {
37525
+ const trimmed = content.trim();
37526
+ if (trimmed.length <= maxInputChars) return trimmed;
37527
+ return [
37528
+ "[Earlier conversation content omitted to fit the local digest context window.]",
37529
+ trimmed.slice(-maxInputChars)
37530
+ ].join("\n\n");
37531
+ }
37532
+ function buildPrompt(input, maxInputChars) {
37533
+ const conversationExport = trimConversationExport(input.content, maxInputChars);
37534
+ return [
37535
+ {
37536
+ role: "system",
37537
+ content: [
37538
+ "# ROLE",
37539
+ "You write short project-memory wiki pages from developer transcripts.",
37540
+ "Your output is temporary recall to prevent blind steps.",
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 ... ```)."
37547
+ ].join("\n")
37548
+ },
37549
+ {
37550
+ role: "user",
37551
+ content: [
37552
+ "Generate a high-level project memory page. STRICTLY adhere to this architectural layout:",
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">',
37569
+ conversationExport,
37570
+ "</conversation_export>"
37571
+ ].join("\n")
37572
+ }
37573
+ ];
37574
+ }
37575
+ function stripCodeFence(text) {
37576
+ const trimmed = text.trim();
37577
+ const match = trimmed.match(/^```(?:markdown|md)?\s*\n([\s\S]*?)\n```$/i);
37578
+ return match ? match[1].trim() : trimmed;
37579
+ }
37580
+ function sanitizeConversationDigestMarkdown(generatedText) {
37581
+ return stripCodeFence(generatedText).trim() + "\n";
37582
+ }
37583
+ async function createLlamaLocalDigestProvider(modelId) {
37584
+ const spec = getDigestModelSpec(modelId);
37585
+ const modelCacheDir = path12.join(MODEL_CACHE_DIR, "llama");
37586
+ fs9.mkdirSync(modelCacheDir, { recursive: true });
37587
+ let modelFilePath;
37588
+ try {
37589
+ const downloader = await createModelDownloader({
37590
+ modelUri: spec.ggufUri,
37591
+ dirPath: modelCacheDir
37592
+ });
37593
+ modelFilePath = downloader.entrypointFilePath;
37594
+ if (!fs9.existsSync(modelFilePath)) {
37595
+ await downloader.download();
37596
+ }
37597
+ } catch (err) {
37598
+ const message = err instanceof Error ? err.message : String(err);
37599
+ throw new LlamaLocalDigestProviderError(`Failed to download model: ${message}`);
37600
+ }
37601
+ let llama;
37602
+ let model;
37603
+ try {
37604
+ llama = await getLlama();
37605
+ model = await llama.loadModel({ modelPath: modelFilePath });
37606
+ } catch (err) {
37607
+ const message = err instanceof Error ? err.message : String(err);
37608
+ throw new LlamaLocalDigestProviderError(`Failed to load model: ${message}`);
37609
+ }
37610
+ return {
37611
+ id: "llama-local",
37612
+ async dispose() {
37613
+ await model.dispose();
37614
+ },
37615
+ async generateDigestMarkdown(input, options) {
37616
+ const messages = buildPrompt(input, options.maxInputChars);
37617
+ const systemContent = messages[0].content;
37618
+ const userContent = messages[1].content;
37619
+ const context = await model.createContext({ contextSize: LLAMA_CONTEXT_SIZE });
37620
+ try {
37621
+ const session = new LlamaChatSession({
37622
+ contextSequence: context.getSequence(),
37623
+ systemPrompt: systemContent
37624
+ });
37625
+ const response = await withTimeout(
37626
+ session.prompt(userContent, {
37627
+ maxTokens: LLAMA_MAX_GENERATED_TOKENS,
37628
+ temperature: LLAMA_TEMPERATURE
37629
+ }),
37630
+ options.timeoutMs,
37631
+ () => new LlamaLocalDigestProviderError(`Llama provider timed out after ${options.timeoutMs}ms`)
37632
+ );
37633
+ return sanitizeConversationDigestMarkdown(response);
37634
+ } finally {
37635
+ await context.dispose();
37636
+ }
37637
+ }
37638
+ };
37639
+ }
37640
+
37641
+ // src/services/digest/processor.ts
37642
+ function digestLockPath(workspaceRoot2) {
37643
+ return path13.join(workspaceRoot2, DIGEST_LOCKFILE_REL_PATH);
37644
+ }
37645
+ function discoverPendingConversationFiles(workspaceRoot2) {
37646
+ const rawDir = path13.join(workspaceRoot2, WIKI_RAW_DIR);
37647
+ let files;
37648
+ try {
37649
+ 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);
37650
+ } catch (err) {
37651
+ if (err.code === "ENOENT") {
37652
+ files = [];
37653
+ } else {
37654
+ throw err;
37655
+ }
37656
+ }
37657
+ const outDir = defaultProvisionalDigestDir(workspaceRoot2);
37658
+ return files.filter((f) => {
37659
+ try {
37660
+ const input = readConversationDigestInput(workspaceRoot2, f);
37661
+ return !findExistingProvisionalDigest(outDir, input);
37662
+ } catch {
37663
+ return false;
37664
+ }
37665
+ });
37666
+ }
37667
+ async function indexProvisionalDigestFile(workspaceRoot2, markdownPath) {
37668
+ try {
37669
+ const settings2 = loadProjectSettings(workspaceRoot2);
37670
+ const config2 = resolveMemoryConfig(settings2, workspaceRoot2);
37671
+ const store = new MemoryStore(path13.join(config2.wikiDir, "index.db"), config2);
37672
+ const embedder = { embed: async (_texts) => [], initialize: async () => {
37673
+ } };
37674
+ const indexer = new MemoryIndexer(store, embedder, config2);
37675
+ await indexer.indexFile(markdownPath);
37676
+ return { indexed: true };
37677
+ } catch (err) {
37678
+ const message = err instanceof Error ? err.message : String(err);
37679
+ return { indexed: false, error: message };
37680
+ }
37681
+ }
37682
+ async function digestConversationFile(options) {
37683
+ const outDir = options.outDir ? path13.resolve(options.workspaceRoot, options.outDir) : defaultProvisionalDigestDir(options.workspaceRoot);
37684
+ const input = readConversationDigestInput(options.workspaceRoot, options.inputPath);
37685
+ const existingPath = findExistingProvisionalDigest(outDir, input);
37686
+ if (existingPath) {
37687
+ const indexResult = await indexProvisionalDigestFile(options.workspaceRoot, existingPath);
37688
+ return {
37689
+ markdown: existingPath,
37690
+ status: "provisional",
37691
+ contentHash: input.contentHash,
37692
+ skipped: true,
37693
+ ...indexResult
37694
+ };
37695
+ }
37696
+ const provider = await createLlamaLocalDigestProvider(options.modelId);
37697
+ try {
37698
+ const markdown = await provider.generateDigestMarkdown(input, {
37699
+ modelId: options.modelId,
37700
+ maxInputChars: options.maxInputChars ?? DEFAULT_DIGEST_MAX_INPUT_CHARS,
37701
+ timeoutMs: options.timeoutMs ?? DEFAULT_DIGEST_TIMEOUT_MS
37702
+ });
37703
+ const markdownPath = writeProvisionalDigestFile(outDir, input, markdown);
37704
+ const indexResult = await indexProvisionalDigestFile(options.workspaceRoot, markdownPath);
37705
+ return {
37706
+ markdown: markdownPath,
37707
+ status: "provisional",
37708
+ contentHash: input.contentHash,
37709
+ skipped: false,
37710
+ ...indexResult
37711
+ };
37712
+ } finally {
37713
+ await provider.dispose?.();
37714
+ }
37715
+ }
37716
+ async function digestPendingConversations({
37717
+ workspaceRoot: workspaceRoot2,
37718
+ digestFn = digestConversationFile,
37719
+ onProgress
37720
+ }) {
37721
+ const digestConfig = resolveConversationDigestConfig(loadProjectSettings(workspaceRoot2));
37722
+ if (!digestConfig || digestConfig.enabled === false) {
37723
+ return { ok: true, initialized: false, action: "noop", reason: "not-initialized" };
37724
+ }
37725
+ const lockPath = digestLockPath(workspaceRoot2);
37726
+ if (!tryAcquireProcessLock(lockPath)) {
37727
+ return { ok: true, initialized: true, action: "noop", reason: "locked" };
37728
+ }
37729
+ try {
37730
+ const candidates = discoverPendingConversationFiles(workspaceRoot2);
37731
+ if (candidates.length === 0) {
37732
+ return { ok: true, initialized: true, action: "noop", reason: "no-pending" };
37733
+ }
37734
+ let count = 0;
37735
+ let skipped = 0;
37736
+ let errors = 0;
37737
+ for (const filePath of candidates) {
37738
+ try {
37739
+ const result = await digestFn({
37740
+ workspaceRoot: workspaceRoot2,
37741
+ inputPath: filePath,
37742
+ modelId: digestConfig.modelId
37743
+ });
37744
+ if (result.skipped) {
37745
+ skipped++;
37746
+ } else {
37747
+ count++;
37748
+ }
37749
+ } catch {
37750
+ errors++;
37751
+ }
37752
+ if (onProgress) {
37753
+ await onProgress({ processed: count, skipped, errors });
37754
+ }
37755
+ }
37756
+ return { ok: true, initialized: true, action: "digested", count, skipped, errors };
37757
+ } catch (err) {
37758
+ const message = err instanceof Error ? err.message : String(err);
37759
+ return { ok: false, initialized: true, action: "error", error: message };
37760
+ } finally {
37761
+ releaseLock(lockPath);
37762
+ }
37763
+ }
37764
+ async function initializeConversationDigestModel(input) {
37765
+ try {
37766
+ getDigestModelSpec(input.modelId);
37767
+ if (input.allowDownload !== true) {
37768
+ return {
37769
+ initialized: false,
37770
+ modelId: input.modelId,
37771
+ error: "Digest model initialization requires explicit download permission"
37772
+ };
37773
+ }
37774
+ const loadedProvider = await createLlamaLocalDigestProvider(input.modelId);
37775
+ await loadedProvider?.dispose?.();
37776
+ const initializedAt = (/* @__PURE__ */ new Date()).toISOString();
37777
+ writeConversationDigestSettings(input.workspaceRoot, {
37778
+ enabled: input.enabled ?? true,
37779
+ initialized: true,
37780
+ modelId: input.modelId,
37781
+ initializedAt
37782
+ });
37783
+ await digestPendingConversations({ workspaceRoot: input.workspaceRoot });
37784
+ return {
37785
+ initialized: true,
37786
+ modelId: input.modelId,
37787
+ initializedAt
37788
+ };
37789
+ } catch (err) {
37790
+ const message = err instanceof Error ? err.message : String(err);
37791
+ return {
37792
+ initialized: false,
37793
+ modelId: input.modelId,
37794
+ error: message
37795
+ };
37796
+ }
37797
+ }
37798
+
37799
+ // src/mcp/memory.ts
37800
+ function registerMemoryToolHandlers(server2, indexer, store, config2, workspaceRoot2 = process.cwd()) {
37335
37801
  server2.tool(
37336
37802
  "kit_memory_search",
37337
37803
  "Search persistent memory for context relevant to the current question. Returns semantically relevant results using hybrid dense+BM25+RRF search.",
@@ -37381,18 +37847,48 @@ ${content}`;
37381
37847
  }
37382
37848
  }
37383
37849
  );
37850
+ server2.tool(
37851
+ "kit_memory_digest_init",
37852
+ "Initialize or toggle local conversation digesting. Downloads/cache-loads the pinned local model only when explicitly invoked.",
37853
+ {
37854
+ model_id: external_exports.string().optional().describe("Digest model id"),
37855
+ enabled: external_exports.boolean().optional().describe("Enable or disable conversation digesting")
37856
+ },
37857
+ async ({ model_id, enabled }) => {
37858
+ try {
37859
+ const existingConfig = resolveConversationDigestConfig(loadProjectSettings(workspaceRoot2));
37860
+ const result = await initializeConversationDigestModel({
37861
+ workspaceRoot: workspaceRoot2,
37862
+ modelId: model_id ?? existingConfig?.modelId ?? DEFAULT_DIGEST_MODEL_ID,
37863
+ allowDownload: true,
37864
+ enabled
37865
+ });
37866
+ return mcpJson({
37867
+ initialized: result.initialized,
37868
+ model_id: result.modelId,
37869
+ initialized_at: result.initializedAt,
37870
+ error: result.error
37871
+ });
37872
+ } catch (err) {
37873
+ const message = err instanceof Error ? err.message : String(err);
37874
+ return mcpJson({ initialized: false, error: message });
37875
+ }
37876
+ }
37877
+ );
37384
37878
  }
37385
- function registerMemoryTools(server2, settings2, workspaceRoot2) {
37879
+ function registerMemoryTools(server2, settings2, workspaceRoot2, overrides) {
37386
37880
  if (settings2.memory?.enabled !== true) return null;
37387
- const config2 = resolveMemoryConfig(settings2, workspaceRoot2);
37388
- const store = new MemoryStore(path9.join(config2.wikiDir, "index.db"), config2);
37389
- const embedder = new Embedder(config2.embeddingModel);
37390
- const indexer = new MemoryIndexer(store, embedder, config2);
37391
- registerMemoryToolHandlers(server2, indexer, store, config2);
37881
+ const config2 = overrides?.config ?? resolveMemoryConfig(settings2, workspaceRoot2);
37882
+ const store = overrides?.store ?? new MemoryStore(path14.join(config2.wikiDir, "index.db"), config2);
37883
+ const indexer = overrides?.indexer ?? (() => {
37884
+ const embedder = new Embedder(config2.embeddingModel);
37885
+ return new MemoryIndexer(store, embedder, config2);
37886
+ })();
37887
+ registerMemoryToolHandlers(server2, indexer, store, config2, workspaceRoot2);
37392
37888
  return indexer;
37393
37889
  }
37394
37890
 
37395
- // src/kit-server.ts
37891
+ // src/entrypoints/server.ts
37396
37892
  var server = new McpServer({
37397
37893
  name: "kit-agents",
37398
37894
  version: "1.0.0"