@hanhnd/agent-kit 1.0.32 → 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 (107) 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/{memory.js → commands/memory.js} +24 -11
  4. package/dist/cli/index.js +2 -2
  5. package/dist/{utils → cli/utils}/args.d.ts +2 -1
  6. package/dist/{kit-cli.js → cli.js} +342 -58
  7. package/dist/{tools/config.d.ts → core/config/index.d.ts} +2 -7
  8. package/dist/{tools/config.js → core/config/index.js} +2 -13
  9. package/dist/core/handoffs/index.d.ts +16 -0
  10. package/dist/{tools/handoffs.js → core/handoffs/index.js} +3 -3
  11. package/dist/{tools/security.d.ts → core/security/index.d.ts} +0 -19
  12. package/dist/{tools/security.js → core/security/index.js} +3 -76
  13. package/dist/entrypoints/cli.js +11 -0
  14. package/dist/entrypoints/server.js +37 -0
  15. package/dist/{tools → mcp}/agent.js +1 -1
  16. package/dist/{tools → mcp}/core.d.ts +1 -1
  17. package/dist/{tools → mcp}/core.js +2 -2
  18. package/dist/{tools → mcp}/integration.js +3 -3
  19. package/dist/mcp/memory.d.ts +15 -0
  20. package/dist/{tools → mcp}/memory.js +15 -13
  21. package/dist/{kit-server.js → server.js} +80 -70
  22. package/dist/services/digest/background.d.ts +11 -0
  23. package/dist/services/digest/background.js +258 -0
  24. package/dist/{memory → services}/digest/constants.d.ts +4 -1
  25. package/dist/{memory → services}/digest/constants.js +6 -2
  26. package/dist/services/digest/digest-background.test.js +206 -0
  27. package/dist/{tools/__tests__ → services/digest}/digest-init.test.js +4 -3
  28. package/dist/{memory/__tests__ → services/digest}/digest-llama-local.test.js +1 -13
  29. package/dist/{memory/__tests__ → services/digest}/digest-pending.test.js +39 -11
  30. package/dist/{memory/__tests__ → services/digest}/digest-processor.test.js +5 -7
  31. package/dist/{memory → services}/digest/files.d.ts +0 -1
  32. package/dist/{memory → services}/digest/files.js +2 -2
  33. package/dist/services/digest/model-registry.d.ts +2 -0
  34. package/dist/{memory → services}/digest/model-registry.js +9 -8
  35. package/dist/services/digest/processor.d.ts +13 -0
  36. package/dist/{memory → services}/digest/processor.js +48 -31
  37. package/dist/services/digest/providers/llama-local.d.ts +2 -0
  38. package/dist/{memory → services}/digest/providers/llama-local.js +3 -4
  39. package/dist/{memory → services}/digest/types.d.ts +34 -2
  40. package/dist/services/digest/types.js +6 -0
  41. package/dist/{memory → services}/digest/worker.d.ts +0 -1
  42. package/dist/{memory → services}/digest/worker.js +2 -2
  43. package/dist/{utils → services/integration}/credentials.js +1 -1
  44. package/dist/{utils/parser.js → services/integration/parser/adf.js} +1 -0
  45. package/dist/{memory → services/memory}/chunker.d.ts +0 -2
  46. package/dist/{memory → services/memory}/chunker.js +3 -3
  47. package/dist/{memory/__tests__ → services/memory}/chunker.test.js +1 -34
  48. package/dist/{memory → services/memory}/constants.d.ts +1 -1
  49. package/dist/{memory → services/memory}/constants.js +2 -1
  50. package/dist/{memory → services/memory}/embedder.d.ts +2 -2
  51. package/dist/{memory → services/memory}/embedder.js +5 -5
  52. package/dist/services/memory/embedder.test.js +13 -0
  53. package/dist/{memory → services/memory}/indexer.d.ts +2 -1
  54. package/dist/{memory → services/memory}/indexer.js +1 -1
  55. package/dist/{memory/__tests__ → services/memory}/indexer.test.js +4 -3
  56. package/dist/{tools/__tests__ → services/memory}/memory.test.js +54 -13
  57. package/dist/{memory/__tests__ → services/memory}/store.test.js +3 -2
  58. package/dist/{memory → services/memory}/types.d.ts +7 -2
  59. package/dist/services/memory/types.js +6 -0
  60. package/dist/utils/files.d.ts +0 -1
  61. package/dist/utils/files.js +1 -1
  62. package/dist/{memory/__tests__/lockfile.test.js → utils/files.test.js} +14 -14
  63. package/dist/utils/index.d.ts +6 -0
  64. package/dist/utils/index.js +6 -0
  65. package/dist/utils/json.d.ts +0 -1
  66. package/dist/utils/json.js +0 -10
  67. package/dist/utils/paths.d.ts +0 -1
  68. package/dist/utils/paths.js +1 -1
  69. package/package.json +6 -6
  70. package/dist/memory/__tests__/digest-worker.test.js +0 -46
  71. package/dist/memory/__tests__/embedder.test.js +0 -14
  72. package/dist/memory/digest/model-registry.d.ts +0 -6
  73. package/dist/memory/digest/processor.d.ts +0 -7
  74. package/dist/memory/digest/providers/llama-local.d.ts +0 -7
  75. package/dist/memory/digest/types.js +0 -1
  76. package/dist/memory/types.js +0 -1
  77. package/dist/tools/__tests__/cli-digest-pending.test.js +0 -51
  78. package/dist/tools/__tests__/cli-memory.test.js +0 -22
  79. package/dist/tools/handoffs.d.ts +0 -22
  80. package/dist/tools/memory.d.ts +0 -16
  81. /package/dist/{tools/__tests__ → cli/commands}/cli-digest-pending.test.d.ts +0 -0
  82. /package/dist/{tools/__tests__ → cli/commands}/cli-memory.test.d.ts +0 -0
  83. /package/dist/cli/{memory.d.ts → commands/memory.d.ts} +0 -0
  84. /package/dist/{utils → cli/utils}/args.js +0 -0
  85. /package/dist/{kit-cli.d.ts → entrypoints/cli.d.ts} +0 -0
  86. /package/dist/{kit-server.d.ts → entrypoints/server.d.ts} +0 -0
  87. /package/dist/{tools → mcp}/agent.d.ts +0 -0
  88. /package/dist/{tools → mcp}/integration.d.ts +0 -0
  89. /package/dist/{memory/__tests__/chunker.test.d.ts → services/digest/digest-background.test.d.ts} +0 -0
  90. /package/dist/{tools/__tests__ → services/digest}/digest-init.test.d.ts +0 -0
  91. /package/dist/{memory/__tests__ → services/digest}/digest-llama-local.test.d.ts +0 -0
  92. /package/dist/{memory/__tests__ → services/digest}/digest-pending.test.d.ts +0 -0
  93. /package/dist/{memory/__tests__ → services/digest}/digest-processor.test.d.ts +0 -0
  94. /package/dist/{utils → services/integration}/credentials.d.ts +0 -0
  95. /package/dist/{utils/parser.d.ts → services/integration/parser/adf.d.ts} +0 -0
  96. /package/dist/{memory/__tests__/digest-worker.test.d.ts → services/memory/chunker.test.d.ts} +0 -0
  97. /package/dist/{memory/__tests__ → services/memory}/embedder.test.d.ts +0 -0
  98. /package/dist/{memory → services/memory}/index.d.ts +0 -0
  99. /package/dist/{memory → services/memory}/index.js +0 -0
  100. /package/dist/{memory/__tests__ → services/memory}/indexer.test.d.ts +0 -0
  101. /package/dist/{tools/__tests__ → services/memory}/memory.test.d.ts +0 -0
  102. /package/dist/{memory → services/memory}/store.d.ts +0 -0
  103. /package/dist/{memory → services/memory}/store.js +0 -0
  104. /package/dist/{memory/__tests__ → services/memory}/store.test.d.ts +0 -0
  105. /package/dist/{memory/__tests__/lockfile.test.d.ts → utils/files.test.d.ts} +0 -0
  106. /package/dist/{__tests__/temp-dir.d.ts → utils/temp-dir.test.d.ts} +0 -0
  107. /package/dist/{__tests__/temp-dir.js → utils/temp-dir.test.js} +0 -0
@@ -35883,7 +35883,7 @@ var StdioServerTransport = class {
35883
35883
  }
35884
35884
  };
35885
35885
 
35886
- // src/tools/core.ts
35886
+ // src/mcp/core.ts
35887
35887
  import * as fs3 from "fs";
35888
35888
  import * as path4 from "path";
35889
35889
 
@@ -35898,18 +35898,18 @@ function mcpJson(obj) {
35898
35898
  return mcpText(JSON.stringify(obj, null, 2));
35899
35899
  }
35900
35900
 
35901
- // src/tools/config.ts
35901
+ // src/core/config/index.ts
35902
35902
  import * as fs2 from "fs";
35903
35903
  import * as path2 from "path";
35904
35904
 
35905
- // src/memory/constants.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
35915
  var LOCK_RETRY_MS = 50;
@@ -35982,7 +35982,7 @@ function tryAcquireProcessLock(lockPath) {
35982
35982
  return tryWriteFileExclusive(lockPath, lockContent);
35983
35983
  }
35984
35984
 
35985
- // src/tools/config.ts
35985
+ // src/core/config/index.ts
35986
35986
  function resolveMemoryConfig(settings2, workspaceRoot2) {
35987
35987
  const override = settings2.memory ?? {};
35988
35988
  return {
@@ -36053,7 +36053,7 @@ var FORBIDDEN_PATTERNS = [
36053
36053
  ];
36054
36054
  var FORBIDDEN_DIRS = [".git", ".ssh", ".aws", ".kube", ".gnupg", ".docker"];
36055
36055
 
36056
- // src/tools/handoffs.ts
36056
+ // src/core/handoffs/index.ts
36057
36057
  import * as path3 from "path";
36058
36058
  var HANDOFF_TYPES = [
36059
36059
  "brainstorm",
@@ -36106,7 +36106,7 @@ function resolveHandoffPath(input) {
36106
36106
  };
36107
36107
  }
36108
36108
 
36109
- // src/tools/core.ts
36109
+ // src/mcp/core.ts
36110
36110
  function registerCoreTools(server2) {
36111
36111
  server2.tool(
36112
36112
  "kit_save_handoff",
@@ -36139,13 +36139,13 @@ function registerCoreTools(server2) {
36139
36139
  );
36140
36140
  }
36141
36141
 
36142
- // src/tools/agent.ts
36142
+ // src/mcp/agent.ts
36143
36143
  import { spawn } from "child_process";
36144
36144
  import { randomUUID } from "crypto";
36145
36145
  import * as fs4 from "fs";
36146
36146
  import * as path6 from "path";
36147
36147
 
36148
- // src/tools/security.ts
36148
+ // src/core/security/index.ts
36149
36149
  import { execFileSync } from "child_process";
36150
36150
  import * as path5 from "path";
36151
36151
  function sanitize(input) {
@@ -36171,7 +36171,6 @@ function validatePath(filePath, baseDir = process.cwd()) {
36171
36171
  }
36172
36172
  return resolved;
36173
36173
  }
36174
- var GIT_TIMEOUT = parseInt(process.env.GEMINI_KIT_GIT_TIMEOUT || "30000", 10);
36175
36174
  function commandExists(cmd) {
36176
36175
  try {
36177
36176
  const checkCmd = process.platform === "win32" ? "where" : "which";
@@ -36196,7 +36195,7 @@ function sanitizeOutput(text) {
36196
36195
  // AWS access keys
36197
36196
  /xox[baprs]-[A-Za-z0-9-]{10,}/g,
36198
36197
  // Slack tokens
36199
- /[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
36200
36199
  // Atlassian token patterns
36201
36200
  ];
36202
36201
  let sanitized = text;
@@ -36206,7 +36205,7 @@ function sanitizeOutput(text) {
36206
36205
  return sanitized;
36207
36206
  }
36208
36207
 
36209
- // src/tools/agent.ts
36208
+ // src/mcp/agent.ts
36210
36209
  var AGENT_TIMEOUT = parseInt(process.env.KIT_AGENT_TIMEOUT || "300000", 10);
36211
36210
  var jobRegistry = /* @__PURE__ */ new Map();
36212
36211
  var INSTALL_HINTS = {
@@ -36356,10 +36355,10 @@ function registerAgentTools(server2) {
36356
36355
  );
36357
36356
  }
36358
36357
 
36359
- // src/tools/integration.ts
36358
+ // src/mcp/integration.ts
36360
36359
  import { writeFileSync as writeFileSync4 } from "fs";
36361
36360
 
36362
- // src/utils/credentials.ts
36361
+ // src/services/integration/credentials.ts
36363
36362
  import * as fs5 from "fs";
36364
36363
 
36365
36364
  // src/utils/paths.ts
@@ -36370,7 +36369,7 @@ var CREDENTIALS_FILE = path7.join(AGENT_KIT_HOME, "credentials");
36370
36369
  var MODEL_CACHE_DIR = path7.join(AGENT_KIT_HOME, "cache", "models");
36371
36370
  var FASTEMBED_CACHE_DIR = path7.join(MODEL_CACHE_DIR, "fastembed");
36372
36371
 
36373
- // src/utils/credentials.ts
36372
+ // src/services/integration/credentials.ts
36374
36373
  var cachedCredentials = null;
36375
36374
  function parseIni(content) {
36376
36375
  const result = {};
@@ -36429,7 +36428,7 @@ function getCredential(key) {
36429
36428
  return loadCredentials()[key];
36430
36429
  }
36431
36430
 
36432
- // src/utils/parser.ts
36431
+ // src/services/integration/parser/adf.ts
36433
36432
  function adfToMarkdown(adf) {
36434
36433
  if (!adf || typeof adf !== "object") return "No description";
36435
36434
  const content = adf.content;
@@ -36551,7 +36550,7 @@ ${renderNodes(node.content, listDepth, node.type).trim()}
36551
36550
  return renderNodes(content).trim() || "No description";
36552
36551
  }
36553
36552
 
36554
- // src/tools/integration.ts
36553
+ // src/mcp/integration.ts
36555
36554
  var BitbucketPrSchema = external_exports.object({
36556
36555
  id: external_exports.number(),
36557
36556
  title: external_exports.string(),
@@ -36737,10 +36736,10 @@ ${ticket.fields.labels?.join(", ") || "None"}`;
36737
36736
  );
36738
36737
  }
36739
36738
 
36740
- // src/tools/memory.ts
36739
+ // src/mcp/memory.ts
36741
36740
  import * as path14 from "path";
36742
36741
 
36743
- // src/memory/embedder.ts
36742
+ // src/services/memory/embedder.ts
36744
36743
  var EmbedderError = class extends Error {
36745
36744
  constructor(message, cause) {
36746
36745
  super(message);
@@ -36786,9 +36785,9 @@ var Embedder = class {
36786
36785
  try {
36787
36786
  const { EmbeddingModel, FlagEmbedding } = await import("fastembed");
36788
36787
  const modelMap = {
36789
- "Xenova/bge-small-en-v1.5": EmbeddingModel.BGESmallENV15,
36790
- "Xenova/bge-base-en-v1.5": EmbeddingModel.BGEBaseENV15,
36791
- "Xenova/all-MiniLM-L6-v2": EmbeddingModel.AllMiniLML6V2
36788
+ ["tiny" /* TINY */]: EmbeddingModel.AllMiniLML6V2,
36789
+ ["small" /* SMALL */]: EmbeddingModel.BGESmallENV15,
36790
+ ["large" /* LARGE */]: EmbeddingModel.BGEBaseENV15
36792
36791
  };
36793
36792
  const model = modelMap[this.modelName] ?? EmbeddingModel.BGESmallENV15;
36794
36793
  const embedder = await FlagEmbedding.init({
@@ -36811,7 +36810,7 @@ var Embedder = class {
36811
36810
  }
36812
36811
  };
36813
36812
 
36814
- // src/memory/indexer.ts
36813
+ // src/services/memory/indexer.ts
36815
36814
  import * as fs6 from "fs";
36816
36815
  import * as path8 from "path";
36817
36816
 
@@ -36821,7 +36820,7 @@ function sha256Hex(content) {
36821
36820
  return createHash("sha256").update(content).digest("hex");
36822
36821
  }
36823
36822
 
36824
- // src/memory/chunker.ts
36823
+ // src/services/memory/chunker.ts
36825
36824
  function computeChunkId(content) {
36826
36825
  return sha256Hex(content).slice(0, 16);
36827
36826
  }
@@ -36952,7 +36951,7 @@ ${sectionText}` : sectionText;
36952
36951
  return chunks;
36953
36952
  }
36954
36953
 
36955
- // src/memory/indexer.ts
36954
+ // src/services/memory/indexer.ts
36956
36955
  var MemoryIndexer = class {
36957
36956
  constructor(store, embedder, config2) {
36958
36957
  this.store = store;
@@ -37135,7 +37134,7 @@ ${content}
37135
37134
  }
37136
37135
  };
37137
37136
 
37138
- // src/memory/store.ts
37137
+ // src/services/memory/store.ts
37139
37138
  var import_stopword = __toESM(require_stopword_cjs(), 1);
37140
37139
  import Database from "better-sqlite3";
37141
37140
  import * as fs7 from "fs";
@@ -37381,25 +37380,28 @@ var MemoryStore = class {
37381
37380
  }
37382
37381
  };
37383
37382
 
37384
- // src/memory/digest/processor.ts
37383
+ // src/services/digest/processor.ts
37385
37384
  import * as fs10 from "node:fs";
37386
37385
  import * as path13 from "node:path";
37387
37386
 
37388
- // src/memory/digest/constants.ts
37387
+ // src/services/digest/constants.ts
37389
37388
  import * as path10 from "node:path";
37390
- var DEFAULT_DIGEST_MODEL_ID = "qwen2.5-1.5b-instruct-q4";
37389
+ var DEFAULT_DIGEST_MODEL_ID = "default" /* DEFAULT */;
37391
37390
  var DEFAULT_DIGEST_MAX_INPUT_CHARS = 3e3;
37392
37391
  var DEFAULT_DIGEST_TIMEOUT_MS = 12e4;
37393
37392
  var KIT_DIR = ".agent-kit";
37394
37393
  var WIKI_DIR = path10.join(KIT_DIR, "wiki");
37395
37394
  var WIKI_RAW_DIR = path10.join(WIKI_DIR, "raw");
37396
37395
  var PROVISIONAL_DIGEST_DIR = path10.join(WIKI_DIR, "compiled", "provisional", "conversation-digests");
37397
- var DIGEST_LOCKFILE_REL_PATH = path10.join(KIT_DIR, "digest-worker.lock");
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");
37398
37400
  var LLAMA_CONTEXT_SIZE = 4096;
37399
37401
  var LLAMA_MAX_GENERATED_TOKENS = 512;
37400
37402
  var LLAMA_TEMPERATURE = 0.1;
37401
37403
 
37402
- // src/memory/digest/files.ts
37404
+ // src/services/digest/files.ts
37403
37405
  import * as fs8 from "node:fs";
37404
37406
  import * as path11 from "node:path";
37405
37407
  function defaultProvisionalDigestDir(workspaceRoot2) {
@@ -37439,7 +37441,7 @@ function writeConversationDigestSettings(workspaceRoot2, digest) {
37439
37441
  });
37440
37442
  }
37441
37443
 
37442
- // src/memory/digest/model-registry.ts
37444
+ // src/services/digest/model-registry.ts
37443
37445
  var DigestModelRegistryError = class extends Error {
37444
37446
  constructor(message) {
37445
37447
  super(message);
@@ -37447,24 +37449,24 @@ var DigestModelRegistryError = class extends Error {
37447
37449
  }
37448
37450
  };
37449
37451
  var DIGEST_MODEL_REGISTRY = {
37450
- "qwen2.5-0.5b-instruct-q4": {
37451
- id: "qwen2.5-0.5b-instruct-q4",
37452
+ ["tiny" /* TINY */]: {
37453
+ id: "tiny" /* TINY */,
37452
37454
  ggufUri: "hf:bartowski/Qwen2.5-0.5B-Instruct-GGUF/Qwen2.5-0.5B-Instruct-Q4_K_M.gguf",
37453
37455
  approxSizeBytes: 4e8,
37454
37456
  license: "Apache-2.0",
37455
37457
  sourceUrl: "https://huggingface.co/bartowski/Qwen2.5-0.5B-Instruct-GGUF",
37456
37458
  enabled: true
37457
37459
  },
37458
- "qwen2.5-1.5b-instruct-q4": {
37459
- id: "qwen2.5-1.5b-instruct-q4",
37460
+ ["default" /* DEFAULT */]: {
37461
+ id: "default" /* DEFAULT */,
37460
37462
  ggufUri: "hf:bartowski/Qwen2.5-1.5B-Instruct-GGUF/Qwen2.5-1.5B-Instruct-Q4_K_M.gguf",
37461
37463
  approxSizeBytes: 1e9,
37462
37464
  license: "Apache-2.0",
37463
37465
  sourceUrl: "https://huggingface.co/bartowski/Qwen2.5-1.5B-Instruct-GGUF",
37464
37466
  enabled: true
37465
37467
  },
37466
- "qwen2.5-3b-instruct-q4": {
37467
- id: "qwen2.5-3b-instruct-q4",
37468
+ ["large" /* LARGE */]: {
37469
+ id: "large" /* LARGE */,
37468
37470
  ggufUri: "hf:bartowski/Qwen2.5-3B-Instruct-GGUF/Qwen2.5-3B-Instruct-Q4_K_M.gguf",
37469
37471
  approxSizeBytes: 19e8,
37470
37472
  license: "Apache-2.0",
@@ -37489,7 +37491,7 @@ function getDigestModelSpec(modelId) {
37489
37491
  return spec;
37490
37492
  }
37491
37493
 
37492
- // src/memory/digest/providers/llama-local.ts
37494
+ // src/services/digest/providers/llama-local.ts
37493
37495
  import * as fs9 from "node:fs";
37494
37496
  import * as path12 from "node:path";
37495
37497
  import { getLlama, LlamaChatSession, createModelDownloader } from "node-llama-cpp";
@@ -37509,7 +37511,7 @@ async function withTimeout(promise, timeoutMs, createError) {
37509
37511
  }
37510
37512
  }
37511
37513
 
37512
- // src/memory/digest/providers/llama-local.ts
37514
+ // src/services/digest/providers/llama-local.ts
37513
37515
  var LlamaLocalDigestProviderError = class extends Error {
37514
37516
  constructor(message) {
37515
37517
  super(message);
@@ -37529,7 +37531,6 @@ function trimConversationExport(content, maxInputChars) {
37529
37531
  }
37530
37532
  function buildPrompt(input, maxInputChars) {
37531
37533
  const conversationExport = trimConversationExport(input.content, maxInputChars);
37532
- const sourceName = path12.basename(input.sourcePath);
37533
37534
  return [
37534
37535
  {
37535
37536
  role: "system",
@@ -37637,10 +37638,32 @@ async function createLlamaLocalDigestProvider(modelId) {
37637
37638
  };
37638
37639
  }
37639
37640
 
37640
- // src/memory/digest/processor.ts
37641
+ // src/services/digest/processor.ts
37641
37642
  function digestLockPath(workspaceRoot2) {
37642
37643
  return path13.join(workspaceRoot2, DIGEST_LOCKFILE_REL_PATH);
37643
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
+ }
37644
37667
  async function indexProvisionalDigestFile(workspaceRoot2, markdownPath) {
37645
37668
  try {
37646
37669
  const settings2 = loadProjectSettings(workspaceRoot2);
@@ -37692,7 +37715,8 @@ async function digestConversationFile(options) {
37692
37715
  }
37693
37716
  async function digestPendingConversations({
37694
37717
  workspaceRoot: workspaceRoot2,
37695
- digestFn = digestConversationFile
37718
+ digestFn = digestConversationFile,
37719
+ onProgress
37696
37720
  }) {
37697
37721
  const digestConfig = resolveConversationDigestConfig(loadProjectSettings(workspaceRoot2));
37698
37722
  if (!digestConfig || digestConfig.enabled === false) {
@@ -37703,26 +37727,7 @@ async function digestPendingConversations({
37703
37727
  return { ok: true, initialized: true, action: "noop", reason: "locked" };
37704
37728
  }
37705
37729
  try {
37706
- const rawDir = path13.join(workspaceRoot2, WIKI_RAW_DIR);
37707
- let files;
37708
- try {
37709
- 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);
37710
- } catch (err) {
37711
- if (err.code === "ENOENT") {
37712
- files = [];
37713
- } else {
37714
- throw err;
37715
- }
37716
- }
37717
- const outDir = defaultProvisionalDigestDir(workspaceRoot2);
37718
- const candidates = files.filter((f) => {
37719
- try {
37720
- const input = readConversationDigestInput(workspaceRoot2, f);
37721
- return !findExistingProvisionalDigest(outDir, input);
37722
- } catch {
37723
- return false;
37724
- }
37725
- });
37730
+ const candidates = discoverPendingConversationFiles(workspaceRoot2);
37726
37731
  if (candidates.length === 0) {
37727
37732
  return { ok: true, initialized: true, action: "noop", reason: "no-pending" };
37728
37733
  }
@@ -37744,6 +37749,9 @@ async function digestPendingConversations({
37744
37749
  } catch {
37745
37750
  errors++;
37746
37751
  }
37752
+ if (onProgress) {
37753
+ await onProgress({ processed: count, skipped, errors });
37754
+ }
37747
37755
  }
37748
37756
  return { ok: true, initialized: true, action: "digested", count, skipped, errors };
37749
37757
  } catch (err) {
@@ -37788,7 +37796,7 @@ async function initializeConversationDigestModel(input) {
37788
37796
  }
37789
37797
  }
37790
37798
 
37791
- // src/tools/memory.ts
37799
+ // src/mcp/memory.ts
37792
37800
  function registerMemoryToolHandlers(server2, indexer, store, config2, workspaceRoot2 = process.cwd()) {
37793
37801
  server2.tool(
37794
37802
  "kit_memory_search",
@@ -37868,17 +37876,19 @@ ${content}`;
37868
37876
  }
37869
37877
  );
37870
37878
  }
37871
- function registerMemoryTools(server2, settings2, workspaceRoot2) {
37879
+ function registerMemoryTools(server2, settings2, workspaceRoot2, overrides) {
37872
37880
  if (settings2.memory?.enabled !== true) return null;
37873
- const config2 = resolveMemoryConfig(settings2, workspaceRoot2);
37874
- const store = new MemoryStore(path14.join(config2.wikiDir, "index.db"), config2);
37875
- const embedder = new Embedder(config2.embeddingModel);
37876
- const indexer = new MemoryIndexer(store, embedder, 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
+ })();
37877
37887
  registerMemoryToolHandlers(server2, indexer, store, config2, workspaceRoot2);
37878
37888
  return indexer;
37879
37889
  }
37880
37890
 
37881
- // src/kit-server.ts
37891
+ // src/entrypoints/server.ts
37882
37892
  var server = new McpServer({
37883
37893
  name: "kit-agents",
37884
37894
  version: "1.0.0"
@@ -0,0 +1,11 @@
1
+ import type { DigestPendingLauncherResult, DigestWorkerStatus, DigestFileOptions, ProvisionalDigestResult } from './types.js';
2
+ export declare function runDigestPendingWorker(input: {
3
+ workspaceRoot: string;
4
+ pendingAtStart?: number;
5
+ digestFn?: (opts: DigestFileOptions) => Promise<ProvisionalDigestResult>;
6
+ }): Promise<DigestWorkerStatus>;
7
+ export declare function launchDigestPendingWorker(input: {
8
+ workspaceRoot: string;
9
+ args: string[];
10
+ entrypoint: string | undefined;
11
+ }): DigestPendingLauncherResult;
@@ -0,0 +1,258 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { spawn } from 'node:child_process';
4
+ import { DIGEST_LOCKFILE_REL_PATH, DIGEST_WORKER_FLAG, DIGEST_WORKER_LOG_REL_PATH, DIGEST_WORKER_STATUS_REL_PATH, } from './constants.js';
5
+ import { digestPendingConversations, summarizePendingConversations } from './processor.js';
6
+ import { atomicWriteJsonFile } from '../../utils/files.js';
7
+ import { isRecord } from '../../utils/json.js';
8
+ function digestWorkerStatusPath(workspaceRoot) {
9
+ return path.join(workspaceRoot, DIGEST_WORKER_STATUS_REL_PATH);
10
+ }
11
+ function digestWorkerLogPath(workspaceRoot) {
12
+ return path.join(workspaceRoot, DIGEST_WORKER_LOG_REL_PATH);
13
+ }
14
+ function digestWorkerLockPath(workspaceRoot) {
15
+ return path.join(workspaceRoot, DIGEST_LOCKFILE_REL_PATH);
16
+ }
17
+ function isoNow() {
18
+ return new Date().toISOString();
19
+ }
20
+ function errorMessage(err) {
21
+ return err instanceof Error ? err.message : String(err);
22
+ }
23
+ function emptyStatus(state, pendingAtStart, startedAt = isoNow()) {
24
+ return {
25
+ state,
26
+ pid: null,
27
+ startedAt,
28
+ updatedAt: startedAt,
29
+ pendingAtStart,
30
+ processed: 0,
31
+ skipped: 0,
32
+ errors: 0,
33
+ };
34
+ }
35
+ const WORKER_STATES = new Set([
36
+ 'running',
37
+ 'complete',
38
+ 'failed',
39
+ 'not-initialized',
40
+ 'no-pending',
41
+ 'locked',
42
+ 'stale',
43
+ ]);
44
+ function isDigestWorkerStatus(value) {
45
+ if (!isRecord(value))
46
+ return false;
47
+ return (typeof value.state === 'string' &&
48
+ WORKER_STATES.has(value.state) &&
49
+ (typeof value.pid === 'number' || value.pid === null) &&
50
+ typeof value.startedAt === 'string' &&
51
+ typeof value.updatedAt === 'string' &&
52
+ typeof value.pendingAtStart === 'number' &&
53
+ typeof value.processed === 'number' &&
54
+ typeof value.skipped === 'number' &&
55
+ typeof value.errors === 'number' &&
56
+ (value.lastError === undefined || typeof value.lastError === 'string'));
57
+ }
58
+ function readDigestWorkerStatus(workspaceRoot) {
59
+ try {
60
+ const parsed = JSON.parse(fs.readFileSync(digestWorkerStatusPath(workspaceRoot), 'utf8'));
61
+ return isDigestWorkerStatus(parsed) ? parsed : undefined;
62
+ }
63
+ catch {
64
+ return undefined;
65
+ }
66
+ }
67
+ function writeDigestWorkerStatus(workspaceRoot, status) {
68
+ atomicWriteJsonFile(digestWorkerStatusPath(workspaceRoot), status);
69
+ }
70
+ function appendDigestWorkerLog(workspaceRoot, message) {
71
+ try {
72
+ const logPath = digestWorkerLogPath(workspaceRoot);
73
+ fs.mkdirSync(path.dirname(logPath), { recursive: true });
74
+ fs.appendFileSync(logPath, `[${isoNow()}] ${message}\n`, 'utf8');
75
+ }
76
+ catch {
77
+ // Diagnostics must never block hook startup or worker completion.
78
+ }
79
+ }
80
+ function isLivePid(pid) {
81
+ if (pid === null || !Number.isInteger(pid) || pid <= 0)
82
+ return false;
83
+ try {
84
+ process.kill(pid, 0);
85
+ return true;
86
+ }
87
+ catch (err) {
88
+ return err.code !== 'ESRCH';
89
+ }
90
+ }
91
+ function readLockPid(workspaceRoot) {
92
+ const lockPath = digestWorkerLockPath(workspaceRoot);
93
+ if (!fs.existsSync(lockPath))
94
+ return undefined;
95
+ try {
96
+ const parsed = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
97
+ return isRecord(parsed) && typeof parsed.pid === 'number' ? parsed.pid : null;
98
+ }
99
+ catch {
100
+ return null;
101
+ }
102
+ }
103
+ function statusFromPendingResult(result, startedAt, pendingAtStart) {
104
+ const updatedAt = isoNow();
105
+ if (!result.ok) {
106
+ return {
107
+ ...emptyStatus('failed', pendingAtStart, startedAt),
108
+ updatedAt,
109
+ errors: 1,
110
+ lastError: result.error,
111
+ };
112
+ }
113
+ if (result.action === 'digested') {
114
+ return {
115
+ state: 'complete',
116
+ pid: null,
117
+ startedAt,
118
+ updatedAt,
119
+ pendingAtStart,
120
+ processed: result.count,
121
+ skipped: result.skipped,
122
+ errors: result.errors,
123
+ };
124
+ }
125
+ return {
126
+ ...emptyStatus(result.reason, pendingAtStart, startedAt),
127
+ updatedAt,
128
+ };
129
+ }
130
+ export async function runDigestPendingWorker(input) {
131
+ const startedAt = isoNow();
132
+ let pendingAtStart = input.pendingAtStart ?? 0;
133
+ try {
134
+ const summary = summarizePendingConversations(input.workspaceRoot);
135
+ if (input.pendingAtStart === undefined) {
136
+ pendingAtStart = summary.initialized ? summary.pending : 0;
137
+ }
138
+ const runningStatus = {
139
+ ...emptyStatus('running', pendingAtStart, startedAt),
140
+ pid: process.pid,
141
+ };
142
+ writeDigestWorkerStatus(input.workspaceRoot, runningStatus);
143
+ appendDigestWorkerLog(input.workspaceRoot, `worker started pid=${process.pid} pending=${pendingAtStart}`);
144
+ const result = await digestPendingConversations({
145
+ workspaceRoot: input.workspaceRoot,
146
+ digestFn: input.digestFn,
147
+ onProgress: (progress) => {
148
+ const currentStatus = {
149
+ ...runningStatus,
150
+ updatedAt: isoNow(),
151
+ processed: progress.processed,
152
+ skipped: progress.skipped,
153
+ errors: progress.errors,
154
+ };
155
+ writeDigestWorkerStatus(input.workspaceRoot, currentStatus);
156
+ },
157
+ });
158
+ const status = statusFromPendingResult(result, startedAt, pendingAtStart);
159
+ writeDigestWorkerStatus(input.workspaceRoot, status);
160
+ appendDigestWorkerLog(input.workspaceRoot, `worker finished state=${status.state} processed=${status.processed} skipped=${status.skipped} errors=${status.errors}`);
161
+ return status;
162
+ }
163
+ catch (err) {
164
+ const status = {
165
+ ...emptyStatus('failed', pendingAtStart, startedAt),
166
+ updatedAt: isoNow(),
167
+ errors: 1,
168
+ lastError: errorMessage(err),
169
+ };
170
+ writeDigestWorkerStatus(input.workspaceRoot, status);
171
+ appendDigestWorkerLog(input.workspaceRoot, `worker failed error=${status.lastError}`);
172
+ return status;
173
+ }
174
+ }
175
+ export function launchDigestPendingWorker(input) {
176
+ const startedAt = isoNow();
177
+ let pendingAtStart = 0;
178
+ try {
179
+ const previousStatus = readDigestWorkerStatus(input.workspaceRoot);
180
+ if (previousStatus?.state === 'running' && isLivePid(previousStatus.pid)) {
181
+ const status = {
182
+ ...previousStatus,
183
+ state: 'locked',
184
+ updatedAt: isoNow(),
185
+ lastError: undefined,
186
+ };
187
+ writeDigestWorkerStatus(input.workspaceRoot, status);
188
+ return { ok: true, mode: 'background-launcher', status, spawned: false, reason: 'locked' };
189
+ }
190
+ const lockPid = readLockPid(input.workspaceRoot);
191
+ if (lockPid !== undefined && isLivePid(lockPid)) {
192
+ const status = emptyStatus('locked', 0, startedAt);
193
+ writeDigestWorkerStatus(input.workspaceRoot, status);
194
+ return { ok: true, mode: 'background-launcher', status, spawned: false, reason: 'locked' };
195
+ }
196
+ const summary = summarizePendingConversations(input.workspaceRoot);
197
+ if (!summary.initialized) {
198
+ const status = emptyStatus('not-initialized', 0, startedAt);
199
+ writeDigestWorkerStatus(input.workspaceRoot, status);
200
+ return { ok: true, mode: 'background-launcher', status, spawned: false, reason: 'not-initialized' };
201
+ }
202
+ if (summary.pending === 0) {
203
+ const status = emptyStatus('no-pending', 0, startedAt);
204
+ writeDigestWorkerStatus(input.workspaceRoot, status);
205
+ return { ok: true, mode: 'background-launcher', status, spawned: false, reason: 'no-pending' };
206
+ }
207
+ pendingAtStart = summary.pending;
208
+ if (!input.entrypoint) {
209
+ const status = {
210
+ ...emptyStatus('failed', pendingAtStart, startedAt),
211
+ errors: 1,
212
+ lastError: 'CLI entrypoint is unavailable',
213
+ };
214
+ writeDigestWorkerStatus(input.workspaceRoot, status);
215
+ return {
216
+ ok: false,
217
+ mode: 'background-launcher',
218
+ status,
219
+ spawned: false,
220
+ reason: 'spawn-failed',
221
+ error: status.lastError,
222
+ };
223
+ }
224
+ const child = spawn(process.execPath, [input.entrypoint, 'memory', 'digest-pending', ...input.args, `--${DIGEST_WORKER_FLAG}`], {
225
+ cwd: input.workspaceRoot,
226
+ detached: true,
227
+ stdio: 'ignore',
228
+ });
229
+ child.unref();
230
+ if (!child.pid) {
231
+ throw new Error('worker process did not expose a pid');
232
+ }
233
+ const status = {
234
+ ...emptyStatus('running', pendingAtStart, startedAt),
235
+ pid: child.pid,
236
+ };
237
+ writeDigestWorkerStatus(input.workspaceRoot, status);
238
+ appendDigestWorkerLog(input.workspaceRoot, `launcher spawned pid=${child.pid} pending=${pendingAtStart}`);
239
+ return { ok: true, mode: 'background-launcher', status, spawned: true, pid: child.pid };
240
+ }
241
+ catch (err) {
242
+ const status = {
243
+ ...emptyStatus('failed', pendingAtStart, startedAt),
244
+ errors: 1,
245
+ lastError: errorMessage(err),
246
+ };
247
+ writeDigestWorkerStatus(input.workspaceRoot, status);
248
+ appendDigestWorkerLog(input.workspaceRoot, `launcher failed error=${status.lastError}`);
249
+ return {
250
+ ok: false,
251
+ mode: 'background-launcher',
252
+ status,
253
+ spawned: false,
254
+ reason: 'spawn-failed',
255
+ error: status.lastError,
256
+ };
257
+ }
258
+ }