@pcircle/memesh 4.9.4 → 4.10.2

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 (159) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/AGENTS.md +75 -13
  5. package/README.de.md +5 -5
  6. package/README.md +5 -5
  7. package/README.zh-TW.md +5 -5
  8. package/dashboard/dist/index.html +8 -8
  9. package/dist/cli/view-live.d.ts.map +1 -1
  10. package/dist/cli/view-live.js +0 -427
  11. package/dist/cli/view-live.js.map +1 -1
  12. package/dist/core/agent-message-inbox.d.ts +6 -0
  13. package/dist/core/agent-message-inbox.d.ts.map +1 -1
  14. package/dist/core/agent-message-inbox.js +28 -1
  15. package/dist/core/agent-message-inbox.js.map +1 -1
  16. package/dist/core/briefing-index.d.ts +30 -0
  17. package/dist/core/briefing-index.d.ts.map +1 -0
  18. package/dist/core/briefing-index.js +140 -0
  19. package/dist/core/briefing-index.js.map +1 -0
  20. package/dist/core/briefing-level.d.ts +22 -0
  21. package/dist/core/briefing-level.d.ts.map +1 -0
  22. package/dist/core/briefing-level.js +75 -0
  23. package/dist/core/briefing-level.js.map +1 -0
  24. package/dist/core/briefing.d.ts +7 -0
  25. package/dist/core/briefing.d.ts.map +1 -1
  26. package/dist/core/briefing.js +72 -12
  27. package/dist/core/briefing.js.map +1 -1
  28. package/dist/core/capture-liveness.d.ts +137 -0
  29. package/dist/core/capture-liveness.d.ts.map +1 -0
  30. package/dist/core/capture-liveness.js +448 -0
  31. package/dist/core/capture-liveness.js.map +1 -0
  32. package/dist/core/config.d.ts +2 -0
  33. package/dist/core/config.d.ts.map +1 -1
  34. package/dist/core/config.js +5 -1
  35. package/dist/core/config.js.map +1 -1
  36. package/dist/core/delegation.d.ts +47 -0
  37. package/dist/core/delegation.d.ts.map +1 -0
  38. package/dist/core/delegation.js +174 -0
  39. package/dist/core/delegation.js.map +1 -0
  40. package/dist/core/doctor-fixes.d.ts +1 -0
  41. package/dist/core/doctor-fixes.d.ts.map +1 -1
  42. package/dist/core/doctor-fixes.js +21 -0
  43. package/dist/core/doctor-fixes.js.map +1 -1
  44. package/dist/core/doctor.d.ts +8 -0
  45. package/dist/core/doctor.d.ts.map +1 -1
  46. package/dist/core/doctor.js +148 -3
  47. package/dist/core/doctor.js.map +1 -1
  48. package/dist/core/note-derive.d.ts +14 -0
  49. package/dist/core/note-derive.d.ts.map +1 -0
  50. package/dist/core/note-derive.js +66 -0
  51. package/dist/core/note-derive.js.map +1 -0
  52. package/dist/core/note-ingest.d.ts +34 -0
  53. package/dist/core/note-ingest.d.ts.map +1 -0
  54. package/dist/core/note-ingest.js +445 -0
  55. package/dist/core/note-ingest.js.map +1 -0
  56. package/dist/core/operations.d.ts +10 -1
  57. package/dist/core/operations.d.ts.map +1 -1
  58. package/dist/core/operations.js +129 -14
  59. package/dist/core/operations.js.map +1 -1
  60. package/dist/core/paths.d.ts +1 -0
  61. package/dist/core/paths.d.ts.map +1 -1
  62. package/dist/core/paths.js +4 -0
  63. package/dist/core/paths.js.map +1 -1
  64. package/dist/core/schema-export.d.ts.map +1 -1
  65. package/dist/core/schema-export.js +15 -5
  66. package/dist/core/schema-export.js.map +1 -1
  67. package/dist/core/serializer.d.ts +2 -0
  68. package/dist/core/serializer.d.ts.map +1 -1
  69. package/dist/core/serializer.js +119 -3
  70. package/dist/core/serializer.js.map +1 -1
  71. package/dist/core/session-insight.d.ts +44 -0
  72. package/dist/core/session-insight.d.ts.map +1 -0
  73. package/dist/core/session-insight.js +216 -0
  74. package/dist/core/session-insight.js.map +1 -0
  75. package/dist/core/task-state-store.d.ts +4 -0
  76. package/dist/core/task-state-store.d.ts.map +1 -1
  77. package/dist/core/task-state-store.js +18 -5
  78. package/dist/core/task-state-store.js.map +1 -1
  79. package/dist/core/task-state.d.ts +5 -0
  80. package/dist/core/task-state.d.ts.map +1 -1
  81. package/dist/core/task-state.js +70 -0
  82. package/dist/core/task-state.js.map +1 -1
  83. package/dist/core/turn-signal.d.ts +22 -0
  84. package/dist/core/turn-signal.d.ts.map +1 -0
  85. package/dist/core/turn-signal.js +89 -0
  86. package/dist/core/turn-signal.js.map +1 -0
  87. package/dist/core/types.d.ts +14 -3
  88. package/dist/core/types.d.ts.map +1 -1
  89. package/dist/core/update-entrypoint.d.ts +19 -0
  90. package/dist/core/update-entrypoint.d.ts.map +1 -0
  91. package/dist/core/update-entrypoint.js +222 -0
  92. package/dist/core/update-entrypoint.js.map +1 -0
  93. package/dist/core/update-notice.d.ts +69 -0
  94. package/dist/core/update-notice.d.ts.map +1 -0
  95. package/dist/core/update-notice.js +188 -0
  96. package/dist/core/update-notice.js.map +1 -0
  97. package/dist/core/work-topology.d.ts +1 -0
  98. package/dist/core/work-topology.d.ts.map +1 -1
  99. package/dist/core/work-topology.js +3 -0
  100. package/dist/core/work-topology.js.map +1 -1
  101. package/dist/host-runtime/codex-session.d.ts +10 -0
  102. package/dist/host-runtime/codex-session.d.ts.map +1 -1
  103. package/dist/host-runtime/codex-session.js +1 -1
  104. package/dist/host-runtime/codex-session.js.map +1 -1
  105. package/dist/knowledge-graph.d.ts.map +1 -1
  106. package/dist/knowledge-graph.js +24 -3
  107. package/dist/knowledge-graph.js.map +1 -1
  108. package/dist/mcp/THIRD_PARTY_NOTICES.txt +2 -2
  109. package/dist/mcp/server.js +1438 -122
  110. package/dist/mcp/server.js.map +3 -3
  111. package/dist/skills-manifest.json +61 -36
  112. package/dist/storage/fts-index.d.ts +1 -0
  113. package/dist/storage/fts-index.d.ts.map +1 -1
  114. package/dist/storage/fts-index.js +5 -0
  115. package/dist/storage/fts-index.js.map +1 -1
  116. package/dist/transports/cli/cli.d.ts.map +1 -1
  117. package/dist/transports/cli/cli.js +7846 -5613
  118. package/dist/transports/cli/cli.js.map +3 -3
  119. package/dist/transports/http/server.d.ts.map +1 -1
  120. package/dist/transports/http/server.js +48 -28
  121. package/dist/transports/http/server.js.map +1 -1
  122. package/dist/transports/mcp/handlers.d.ts +31 -7
  123. package/dist/transports/mcp/handlers.d.ts.map +1 -1
  124. package/dist/transports/mcp/handlers.js +49 -7
  125. package/dist/transports/mcp/handlers.js.map +1 -1
  126. package/dist/transports/schemas.d.ts +5 -2
  127. package/dist/transports/schemas.d.ts.map +1 -1
  128. package/dist/transports/schemas.js +30 -3
  129. package/dist/transports/schemas.js.map +1 -1
  130. package/docs/platforms/README.md +1 -0
  131. package/docs/platforms/agent-messaging.md +39 -8
  132. package/package.json +11 -6
  133. package/scripts/hooks/_generated/agent-message-inbox.js +28 -1
  134. package/scripts/hooks/_generated/briefing-index.js +147 -0
  135. package/scripts/hooks/_generated/briefing-level.js +82 -0
  136. package/scripts/hooks/_generated/capture-liveness.js +455 -0
  137. package/scripts/hooks/_generated/core-paths.js +4 -0
  138. package/scripts/hooks/_generated/fts-index.js +5 -0
  139. package/scripts/hooks/_generated/task-state.js +70 -0
  140. package/scripts/hooks/_generated/update-notice.js +195 -0
  141. package/scripts/hooks/_generated/work-topology.js +3 -0
  142. package/scripts/hooks/_shared.js +923 -27
  143. package/scripts/hooks/_stop-notes.js +504 -0
  144. package/scripts/hooks/auto-update-runner.mjs +22 -0
  145. package/scripts/hooks/decision-nudge.js +32 -5
  146. package/scripts/hooks/guard-check.js +30 -3
  147. package/scripts/hooks/post-commit.js +329 -133
  148. package/scripts/hooks/pre-compact.js +17 -2
  149. package/scripts/hooks/pre-edit-recall.js +416 -64
  150. package/scripts/hooks/session-start.js +617 -150
  151. package/scripts/hooks/session-summary.js +217 -41
  152. package/scripts/hooks/user-prompt-intent.js +124 -22
  153. package/scripts/upgrade-plugin.sh +3 -4
  154. package/skills/memesh/SKILL.md +45 -23
  155. package/dist/cli/assets/d3.v7.min.js +0 -2
  156. package/dist/core/graph.d.ts +0 -26
  157. package/dist/core/graph.d.ts.map +0 -1
  158. package/dist/core/graph.js +0 -74
  159. package/dist/core/graph.js.map +0 -1
@@ -3229,8 +3229,8 @@ var require_utils = __commonJS({
3229
3229
  }
3230
3230
  return ind;
3231
3231
  }
3232
- function removeDotSegments(path7) {
3233
- let input = path7;
3232
+ function removeDotSegments(path11) {
3233
+ let input = path11;
3234
3234
  const output = [];
3235
3235
  let nextSlash = -1;
3236
3236
  let len = 0;
@@ -3482,8 +3482,8 @@ var require_schemes = __commonJS({
3482
3482
  wsComponent.secure = void 0;
3483
3483
  }
3484
3484
  if (wsComponent.resourceName) {
3485
- const [path7, query] = wsComponent.resourceName.split("?");
3486
- wsComponent.path = path7 && path7 !== "/" ? path7 : void 0;
3485
+ const [path11, query] = wsComponent.resourceName.split("?");
3486
+ wsComponent.path = path11 && path11 !== "/" ? path11 : void 0;
3487
3487
  wsComponent.query = query;
3488
3488
  wsComponent.resourceName = void 0;
3489
3489
  }
@@ -6670,7 +6670,7 @@ var require_formats = __commonJS({
6670
6670
  email: /^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i
6671
6671
  };
6672
6672
  exports.formatNames = Object.keys(exports.fullFormats);
6673
- function isLeapYear(year) {
6673
+ function isLeapYear2(year) {
6674
6674
  return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
6675
6675
  }
6676
6676
  var DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
@@ -6682,7 +6682,7 @@ var require_formats = __commonJS({
6682
6682
  const year = +matches[1];
6683
6683
  const month = +matches[2];
6684
6684
  const day = +matches[3];
6685
- return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && isLeapYear(year) ? 29 : DAYS[month]);
6685
+ return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && isLeapYear2(year) ? 29 : DAYS[month]);
6686
6686
  }
6687
6687
  function compareDate(d1, d2) {
6688
6688
  if (!(d1 && d2))
@@ -6902,12 +6902,12 @@ var require_dist = __commonJS({
6902
6902
  throw new Error(`Unknown format "${name}"`);
6903
6903
  return f;
6904
6904
  };
6905
- function addFormats(ajv, list, fs9, exportName) {
6905
+ function addFormats(ajv, list, fs13, exportName) {
6906
6906
  var _a3;
6907
6907
  var _b;
6908
6908
  (_a3 = (_b = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
6909
6909
  for (const f of list)
6910
- ajv.addFormat(f, fs9[f]);
6910
+ ajv.addFormat(f, fs13[f]);
6911
6911
  }
6912
6912
  module.exports = exports = formatsPlugin;
6913
6913
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -6916,8 +6916,8 @@ var require_dist = __commonJS({
6916
6916
  });
6917
6917
 
6918
6918
  // dist/mcp/server.js
6919
- import fs8 from "fs";
6920
- import path6 from "path";
6919
+ import fs12 from "fs";
6920
+ import path10 from "path";
6921
6921
 
6922
6922
  // node_modules/zod/v4/core/index.js
6923
6923
  var core_exports2 = {};
@@ -7442,10 +7442,10 @@ function mergeDefs(...defs) {
7442
7442
  function cloneDef(schema) {
7443
7443
  return mergeDefs(schema._zod.def);
7444
7444
  }
7445
- function getElementAtPath(obj, path7) {
7446
- if (!path7)
7445
+ function getElementAtPath(obj, path11) {
7446
+ if (!path11)
7447
7447
  return obj;
7448
- return path7.reduce((acc, key) => acc?.[key], obj);
7448
+ return path11.reduce((acc, key) => acc?.[key], obj);
7449
7449
  }
7450
7450
  function promiseAllObject(promisesObj) {
7451
7451
  const keys = Object.keys(promisesObj);
@@ -7854,11 +7854,11 @@ function explicitlyAborted(x, startIndex = 0) {
7854
7854
  }
7855
7855
  return false;
7856
7856
  }
7857
- function prefixIssues(path7, issues) {
7857
+ function prefixIssues(path11, issues) {
7858
7858
  return issues.map((iss) => {
7859
7859
  var _a3;
7860
7860
  (_a3 = iss).path ?? (_a3.path = []);
7861
- iss.path.unshift(path7);
7861
+ iss.path.unshift(path11);
7862
7862
  return iss;
7863
7863
  });
7864
7864
  }
@@ -8005,16 +8005,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
8005
8005
  }
8006
8006
  function formatError(error51, mapper = (issue2) => issue2.message) {
8007
8007
  const fieldErrors = { _errors: [] };
8008
- const processError = (error52, path7 = []) => {
8008
+ const processError = (error52, path11 = []) => {
8009
8009
  for (const issue2 of error52.issues) {
8010
8010
  if (issue2.code === "invalid_union" && issue2.errors.length) {
8011
- issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
8011
+ issue2.errors.map((issues) => processError({ issues }, [...path11, ...issue2.path]));
8012
8012
  } else if (issue2.code === "invalid_key") {
8013
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
8013
+ processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
8014
8014
  } else if (issue2.code === "invalid_element") {
8015
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
8015
+ processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
8016
8016
  } else {
8017
- const fullpath = [...path7, ...issue2.path];
8017
+ const fullpath = [...path11, ...issue2.path];
8018
8018
  if (fullpath.length === 0) {
8019
8019
  fieldErrors._errors.push(mapper(issue2));
8020
8020
  } else {
@@ -8041,17 +8041,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
8041
8041
  }
8042
8042
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
8043
8043
  const result = { errors: [] };
8044
- const processError = (error52, path7 = []) => {
8044
+ const processError = (error52, path11 = []) => {
8045
8045
  var _a3, _b;
8046
8046
  for (const issue2 of error52.issues) {
8047
8047
  if (issue2.code === "invalid_union" && issue2.errors.length) {
8048
- issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
8048
+ issue2.errors.map((issues) => processError({ issues }, [...path11, ...issue2.path]));
8049
8049
  } else if (issue2.code === "invalid_key") {
8050
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
8050
+ processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
8051
8051
  } else if (issue2.code === "invalid_element") {
8052
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
8052
+ processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
8053
8053
  } else {
8054
- const fullpath = [...path7, ...issue2.path];
8054
+ const fullpath = [...path11, ...issue2.path];
8055
8055
  if (fullpath.length === 0) {
8056
8056
  result.errors.push(mapper(issue2));
8057
8057
  continue;
@@ -8083,8 +8083,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
8083
8083
  }
8084
8084
  function toDotPath(_path) {
8085
8085
  const segs = [];
8086
- const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
8087
- for (const seg of path7) {
8086
+ const path11 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
8087
+ for (const seg of path11) {
8088
8088
  if (typeof seg === "number")
8089
8089
  segs.push(`[${seg}]`);
8090
8090
  else if (typeof seg === "symbol")
@@ -21082,13 +21082,13 @@ function resolveRef(ref, ctx) {
21082
21082
  if (!ref.startsWith("#")) {
21083
21083
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
21084
21084
  }
21085
- const path7 = ref.slice(1).split("/").filter(Boolean);
21086
- if (path7.length === 0) {
21085
+ const path11 = ref.slice(1).split("/").filter(Boolean);
21086
+ if (path11.length === 0) {
21087
21087
  return ctx.rootSchema;
21088
21088
  }
21089
21089
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
21090
- if (path7[0] === defsKey) {
21091
- const key = path7[1];
21090
+ if (path11[0] === defsKey) {
21091
+ const key = path11[1];
21092
21092
  if (!key || !ctx.defs[key]) {
21093
21093
  throw new Error(`Reference not found: ${ref}`);
21094
21094
  }
@@ -24793,7 +24793,7 @@ var StdioServerTransport = class {
24793
24793
  };
24794
24794
 
24795
24795
  // dist/mcp/server.js
24796
- import { fileURLToPath as fileURLToPath2 } from "url";
24796
+ import { fileURLToPath as fileURLToPath3 } from "url";
24797
24797
 
24798
24798
  // dist/storage/sqlite.js
24799
24799
  import { createRequire } from "node:module";
@@ -24819,8 +24819,8 @@ var { DatabaseSync } = loadNodeSqlite();
24819
24819
  var BUSY_TIMEOUT_MS = 3e4;
24820
24820
  var MemeshDatabase = class extends DatabaseSync {
24821
24821
  #depth = 0;
24822
- constructor(path7, options = {}) {
24823
- super(path7, options);
24822
+ constructor(path11, options = {}) {
24823
+ super(path11, options);
24824
24824
  this.pragma(`busy_timeout = ${BUSY_TIMEOUT_MS}`);
24825
24825
  }
24826
24826
  pragma(statement) {
@@ -24858,6 +24858,9 @@ var MemeshDatabase = class extends DatabaseSync {
24858
24858
  import path2 from "path";
24859
24859
  import fs2 from "fs";
24860
24860
 
24861
+ // dist/knowledge-graph.js
24862
+ import { createHash } from "node:crypto";
24863
+
24861
24864
  // dist/storage/conflicts.js
24862
24865
  function findConflicts(db2, entityNames) {
24863
24866
  if (entityNames.length < 2)
@@ -25184,16 +25187,30 @@ var KnowledgeGraph = class {
25184
25187
  }
25185
25188
  const prevObsText = isNewEntity ? void 0 : joinIndexedObservations(prevObs.map((o) => o.content));
25186
25189
  if (opts?.observations?.length) {
25190
+ let observations = opts.observations;
25191
+ if (row.type === "session-insight" && /^session-.+-(files|fixes|summary)$/.test(name)) {
25192
+ this.updateEntityMetadata(name, (meta3) => {
25193
+ if (!Array.isArray(meta3.forgotten_observation_hashes))
25194
+ return meta3;
25195
+ const hashes = new Set(meta3.forgotten_observation_hashes);
25196
+ if ((opts.trustOverride ?? opts.metadata?.trust ?? "trusted") !== "trusted") {
25197
+ observations = observations.filter((obs) => !hashes.has(createHash("sha256").update(obs).digest("hex")));
25198
+ return meta3;
25199
+ }
25200
+ const restored = new Set(observations.map((obs) => createHash("sha256").update(obs).digest("hex")));
25201
+ return { ...meta3, forgotten_observation_hashes: meta3.forgotten_observation_hashes.filter((hash2) => !restored.has(hash2)) };
25202
+ });
25203
+ }
25187
25204
  const insertObs = this.db.prepare("INSERT INTO observations (entity_id, content) VALUES (?, ?)");
25188
25205
  const effectiveType = isNewEntity ? type : row.type;
25189
25206
  const isLessonFamily = effectiveType === "lesson_learned" || effectiveType === "lesson" || effectiveType === "mistake";
25190
25207
  if (isLessonFamily) {
25191
- for (const obs of opts.observations) {
25208
+ for (const obs of observations) {
25192
25209
  insertObs.run(entityId, obs);
25193
25210
  }
25194
25211
  } else {
25195
25212
  const existingObsContent = new Set(isNewEntity ? [] : this.db.prepare("SELECT content FROM observations WHERE entity_id = ?").all(entityId).map((o) => o.content));
25196
- for (const obs of opts.observations) {
25213
+ for (const obs of observations) {
25197
25214
  if (existingObsContent.has(obs))
25198
25215
  continue;
25199
25216
  existingObsContent.add(obs);
@@ -25512,7 +25529,7 @@ var KnowledgeGraph = class {
25512
25529
  }
25513
25530
  removeObservation(entityName, observationContent) {
25514
25531
  return this.db.transaction(() => {
25515
- const row = this.db.prepare("SELECT id, title, status FROM entities WHERE name = ?").get(entityName);
25532
+ const row = this.db.prepare("SELECT id, title, status, type, metadata FROM entities WHERE name = ?").get(entityName);
25516
25533
  if (!row)
25517
25534
  return { removed: false, remainingObservations: 0, entityFound: false };
25518
25535
  const prevObs = this.db.prepare("SELECT content FROM observations WHERE entity_id = ? ORDER BY id").all(row.id);
@@ -25527,6 +25544,12 @@ var KnowledgeGraph = class {
25527
25544
  if (deleteResult.changes === 0) {
25528
25545
  return { removed: false, remainingObservations: prevObs.length, entityFound: true };
25529
25546
  }
25547
+ if (row.type === "session-insight" && /^session-.+-(files|fixes|summary)$/.test(entityName)) {
25548
+ const meta3 = this.parseMetadata(row.metadata);
25549
+ const hashes = Array.isArray(meta3.forgotten_observation_hashes) ? meta3.forgotten_observation_hashes : [];
25550
+ const hash2 = createHash("sha256").update(observationContent).digest("hex");
25551
+ this.db.prepare("UPDATE entities SET metadata = ? WHERE id = ?").run(JSON.stringify({ ...meta3, forgotten_observation_hashes: [.../* @__PURE__ */ new Set([...hashes, hash2])] }), row.id);
25552
+ }
25530
25553
  if (row.status !== "archived") {
25531
25554
  this.rebuildFts(row.id, entityName, prevObsText, row.title);
25532
25555
  }
@@ -25598,7 +25621,7 @@ var COMPRESS_INTERVAL_MS = 24 * 60 * 60 * 1e3;
25598
25621
  import fs from "fs";
25599
25622
  import os from "os";
25600
25623
  import path from "path";
25601
- import { createHash } from "crypto";
25624
+ import { createHash as createHash2 } from "crypto";
25602
25625
  import { execFileSync } from "child_process";
25603
25626
  var AGENT_ROUTER_SOCKET_FILENAME = "agent-router-v2.sock";
25604
25627
  function homeDir() {
@@ -25658,7 +25681,7 @@ var PROJECT_ID_MAX_LENGTH = 200;
25658
25681
  var PROJECT_LABEL_MAX_LENGTH = PROJECT_ID_MAX_LENGTH - PROJECT_HASH_HEX_LENGTH - 1;
25659
25682
  function projectIdentity(label, locator) {
25660
25683
  const readable = label.normalize("NFC").slice(0, PROJECT_LABEL_MAX_LENGTH) || "project";
25661
- const suffix = createHash("sha256").update(locator).digest("hex").slice(0, PROJECT_HASH_HEX_LENGTH);
25684
+ const suffix = createHash2("sha256").update(locator).digest("hex").slice(0, PROJECT_HASH_HEX_LENGTH);
25662
25685
  return `${readable}~${suffix}`;
25663
25686
  }
25664
25687
  function tryGit(cwd, args) {
@@ -25748,6 +25771,36 @@ function redactSecrets(input) {
25748
25771
  out = out.replace(pattern, "***REDACTED***");
25749
25772
  return out;
25750
25773
  }
25774
+ function redactUserPaths(text) {
25775
+ const home = homeDir();
25776
+ const roots = /* @__PURE__ */ new Set();
25777
+ const add = (root) => {
25778
+ if (!root || !path.isAbsolute(root))
25779
+ return;
25780
+ roots.add(root);
25781
+ try {
25782
+ roots.add(fs.realpathSync(root));
25783
+ } catch {
25784
+ }
25785
+ };
25786
+ add(home);
25787
+ const isInside = (child) => {
25788
+ const rel = path.relative(home, child);
25789
+ return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel);
25790
+ };
25791
+ for (const dir of [memeshDir(), path.dirname(getDbPath())]) {
25792
+ if (dir && !isInside(dir))
25793
+ add(dir);
25794
+ }
25795
+ const flags = process.platform === "linux" ? "g" : "gi";
25796
+ let out = text;
25797
+ for (const root of [...roots].sort((a, b) => b.length - a.length)) {
25798
+ const escaped = root.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25799
+ const body = escaped.replace(/\\\\|\//g, "[\\\\/]{1,2}");
25800
+ out = out.replace(new RegExp(`(?<![\\w~](?:[\\\\/]{1,2})?)${body}(?=[\\\\/]|$)`, flags), "~");
25801
+ }
25802
+ return out;
25803
+ }
25751
25804
 
25752
25805
  // dist/core/time-utils.js
25753
25806
  function parseSqliteUtcMs(sqliteTimestamp) {
@@ -26267,12 +26320,12 @@ function ensureFtsSegmentation(db2) {
26267
26320
  }
26268
26321
 
26269
26322
  // dist/core/lesson-slug.js
26270
- import { createHash as createHash2 } from "node:crypto";
26323
+ import { createHash as createHash3 } from "node:crypto";
26271
26324
  function lessonSlug(error51) {
26272
26325
  const normalized = error51.normalize("NFKC").trim().replace(/\s+/g, " ").toLowerCase();
26273
26326
  const words = normalized.replace(/[^a-z0-9\u4e00-\u9fff]+/g, " ").trim().split(/\s+/).filter((w) => w.length > 1).slice(0, 8);
26274
26327
  const readable = words.join("-") || "unspecified";
26275
- const digest = createHash2("sha256").update(normalized).digest("hex").slice(0, 8);
26328
+ const digest = createHash3("sha256").update(normalized).digest("hex").slice(0, 8);
26276
26329
  return `${readable.slice(0, 71)}-${digest}`;
26277
26330
  }
26278
26331
 
@@ -26850,8 +26903,8 @@ function getDatabase() {
26850
26903
  }
26851
26904
 
26852
26905
  // dist/transports/mcp/handlers.js
26853
- import fs7 from "node:fs";
26854
- import { fileURLToPath } from "node:url";
26906
+ import fs11 from "node:fs";
26907
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
26855
26908
 
26856
26909
  // dist/core/scoring.js
26857
26910
  var DEFAULT_WEIGHTS = {
@@ -26941,16 +26994,158 @@ function inferErrorPattern(error51) {
26941
26994
  return "other";
26942
26995
  }
26943
26996
 
26997
+ // dist/core/note-derive.js
26998
+ import { createHash as createHash4 } from "crypto";
26999
+ var NOTE_OBSERVATION_MAX_CHARS = 1e4;
27000
+ var NOTE_MAX_OBSERVATIONS = 100;
27001
+ var NOTE_MAX_CHARS = 2e4;
27002
+ var NOTE_DEFAULT_TYPE = "note";
27003
+ function sanitizeNoteText(raw) {
27004
+ const normalized = raw.replace(/\r\n?/g, "\n");
27005
+ const stripped = normalized.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, "");
27006
+ return redactSecrets(stripped).trim();
27007
+ }
27008
+ function stripLineMarker(line) {
27009
+ return line.replace(/^\s{0,3}(?:#{1,6}\s+|[-*+]\s+|\d+[.)]\s+)/, "").trim();
27010
+ }
27011
+ function splitObservations(body) {
27012
+ const out = [];
27013
+ for (const block of body.split(/\n\s*\n/)) {
27014
+ const lines = block.split("\n").map((l) => l.trim()).filter(Boolean);
27015
+ if (lines.length === 0)
27016
+ continue;
27017
+ const isList = lines.every((l) => /^(?:[-*+]\s+|\d+[.)]\s+)/.test(l));
27018
+ const parts = isList ? lines.map(stripLineMarker) : [lines.join(" ")];
27019
+ for (const part of parts) {
27020
+ const text = part.replace(/\s+/g, " ").trim();
27021
+ if (!text)
27022
+ continue;
27023
+ out.push(text.length > NOTE_OBSERVATION_MAX_CHARS ? `${text.slice(0, NOTE_OBSERVATION_MAX_CHARS - 1).trimEnd()}\u2026` : text);
27024
+ }
27025
+ }
27026
+ return out;
27027
+ }
27028
+ function slugify2(text) {
27029
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60).replace(/-+$/g, "");
27030
+ }
27031
+ function deriveNote(raw) {
27032
+ const text = sanitizeNoteText(raw);
27033
+ if (!text)
27034
+ return null;
27035
+ const lines = text.split("\n");
27036
+ const firstIndex = lines.findIndex((l) => l.trim().length > 0);
27037
+ const firstLine = stripLineMarker(lines[firstIndex]).replace(/\s+/g, " ");
27038
+ const rest = lines.slice(firstIndex + 1).join("\n");
27039
+ let titleSource = firstLine;
27040
+ if (firstLine.length > TITLE_MAX_LENGTH) {
27041
+ const sentence = /^(.+?[.!?。!?])(?:\s|$)/.exec(firstLine);
27042
+ if (sentence)
27043
+ titleSource = sentence[1];
27044
+ }
27045
+ const title = truncateTitle(titleSource) || "note";
27046
+ const body = splitObservations(rest);
27047
+ const observations = title === firstLine && body.length > 0 ? body : [...splitObservations(firstLine), ...body];
27048
+ const digest = createHash4("sha256").update(text).digest("hex").slice(0, 8);
27049
+ const name = `${slugify2(title) || NOTE_DEFAULT_TYPE}-${digest}`;
27050
+ return { text, title, observations, name };
27051
+ }
27052
+
26944
27053
  // dist/core/types.js
26945
27054
  var NAMESPACES = ["personal", "team", "global"];
26946
27055
 
26947
27056
  // dist/core/serializer.js
27057
+ var IMPORTABLE_METADATA_KEYS = /* @__PURE__ */ new Set([
27058
+ "title_source",
27059
+ "source_kind",
27060
+ "source",
27061
+ "cluster_key",
27062
+ "dreamed_at",
27063
+ "kind",
27064
+ "project",
27065
+ "previous_namespace",
27066
+ "namespace_moved_at",
27067
+ "split_from",
27068
+ "retired_recall",
27069
+ "priority",
27070
+ "success_criteria",
27071
+ "verification_scenario",
27072
+ "implementation_state",
27073
+ "outcome_state",
27074
+ "accepted_at",
27075
+ "source_ids"
27076
+ ]);
27077
+ var FORGOTTEN_HASH_RE = /^[a-f0-9]{64}$/;
27078
+ var MAX_IMPORTED_FORGOTTEN_HASHES = 1e3;
27079
+ function validateFreshForgottenHashes(value) {
27080
+ if (!Array.isArray(value) || value.length === 0)
27081
+ return null;
27082
+ const deduped = [...new Set(value)];
27083
+ if (!deduped.every((h) => typeof h === "string" && FORGOTTEN_HASH_RE.test(h)))
27084
+ return null;
27085
+ return deduped.length <= MAX_IMPORTED_FORGOTTEN_HASHES ? deduped : null;
27086
+ }
27087
+ function validateFreshSignalScore(value) {
27088
+ return typeof value === "number" && value >= 0 && value <= 1 ? value : null;
27089
+ }
27090
+ var MAX_IMPORTED_REPLACED_HISTORY_ENTRIES = 50;
27091
+ var MAX_IMPORTED_REPLACED_HISTORY_TOTAL_BYTES = 4 * 64 * 1024;
27092
+ var REPLACED_HISTORY_ENTRY_KEYS = /* @__PURE__ */ new Set([
27093
+ "replaced_at",
27094
+ "title",
27095
+ "observations",
27096
+ "tags",
27097
+ "truncated"
27098
+ ]);
27099
+ function isPlainObject3(value) {
27100
+ return typeof value === "object" && value !== null && !Array.isArray(value);
27101
+ }
27102
+ var jsonBytesOf = (v) => Buffer.byteLength(JSON.stringify(v), "utf8");
27103
+ function isValidReplacedHistoryEntry(entry) {
27104
+ if (!isPlainObject3(entry))
27105
+ return false;
27106
+ for (const key of Object.keys(entry)) {
27107
+ if (!REPLACED_HISTORY_ENTRY_KEYS.has(key))
27108
+ return false;
27109
+ }
27110
+ if (typeof entry.replaced_at !== "string" || entry.replaced_at.length > 64)
27111
+ return false;
27112
+ if (!(entry.title === null || typeof entry.title === "string" && entry.title.length <= 500))
27113
+ return false;
27114
+ if (!Array.isArray(entry.observations) || !entry.observations.every((o) => typeof o === "string"))
27115
+ return false;
27116
+ if (!Array.isArray(entry.tags) || !entry.tags.every((t) => typeof t === "string"))
27117
+ return false;
27118
+ if ("truncated" in entry && typeof entry.truncated !== "boolean")
27119
+ return false;
27120
+ return true;
27121
+ }
27122
+ function validateFreshReplacedHistory(value) {
27123
+ if (!Array.isArray(value) || value.length === 0)
27124
+ return null;
27125
+ if (value.length > MAX_IMPORTED_REPLACED_HISTORY_ENTRIES)
27126
+ return null;
27127
+ if (!value.every(isValidReplacedHistoryEntry))
27128
+ return null;
27129
+ return jsonBytesOf(value) <= MAX_IMPORTED_REPLACED_HISTORY_TOTAL_BYTES ? value : null;
27130
+ }
26948
27131
  function buildImportedMetadata(existingMetadata, args) {
26949
- const { guard: _guard, ...bundledSafe } = args.bundled ?? {};
26950
- void _guard;
27132
+ const bundled = args.bundled ?? {};
27133
+ const bundledSafe = {};
27134
+ for (const [key, value] of Object.entries(bundled)) {
27135
+ if (IMPORTABLE_METADATA_KEYS.has(key))
27136
+ bundledSafe[key] = value;
27137
+ }
27138
+ const freshForgottenHashes = args.isNewEntity ? validateFreshForgottenHashes(bundled.forgotten_observation_hashes) : null;
27139
+ const freshPin = args.isNewEntity && bundled.pin === true;
27140
+ const freshSignalScore = args.isNewEntity ? validateFreshSignalScore(bundled.signal_score) : null;
27141
+ const freshReplacedHistory = args.isNewEntity ? validateFreshReplacedHistory(bundled.replaced_history) : null;
26951
27142
  return {
26952
27143
  ...existingMetadata ?? {},
26953
27144
  ...bundledSafe,
27145
+ ...freshForgottenHashes ? { forgotten_observation_hashes: freshForgottenHashes } : {},
27146
+ ...freshSignalScore !== null ? { signal_score: freshSignalScore } : {},
27147
+ ...freshPin ? { pin: true } : {},
27148
+ ...freshReplacedHistory ? { replaced_history: freshReplacedHistory } : {},
26954
27149
  trust: "untrusted",
26955
27150
  provenance: {
26956
27151
  ...existingMetadata?.provenance ?? {},
@@ -27020,6 +27215,12 @@ function importMemories(args) {
27020
27215
  if (!MERGE_STRATEGIES.includes(args.merge_strategy)) {
27021
27216
  throw new Error(`Unknown merge strategy "${args.merge_strategy}". Use one of: ${MERGE_STRATEGIES.join(", ")}. Nothing was imported \u2014 refusing rather than guessing, because the wrong guess overwrites existing memories.`);
27022
27217
  }
27218
+ if (args.restore_archived !== void 0 && typeof args.restore_archived !== "boolean") {
27219
+ throw new Error('restore_archived must be the boolean true or false. Nothing was imported \u2014 refusing rather than guessing, because "yes" brings back memories the user forgot.');
27220
+ }
27221
+ if (args.restore_archived === true && args.merge_strategy === "skip") {
27222
+ throw new Error('restore_archived (--restore-archived) only applies with merge strategy "append" or "overwrite"; "skip" leaves every existing entity untouched, so there is nothing to restore. Nothing was imported.');
27223
+ }
27023
27224
  if (args.namespace !== void 0 && !NAMESPACES.includes(args.namespace)) {
27024
27225
  throw new Error(`Unknown namespace "${args.namespace}". Use one of: ${NAMESPACES.join(", ")}. Nothing was imported \u2014 an unrecognised namespace would move existing memories somewhere nothing queries.`);
27025
27226
  }
@@ -27034,6 +27235,7 @@ function importMemories(args) {
27034
27235
  const pendingRelations = [];
27035
27236
  let skipped = 0;
27036
27237
  let appended = 0;
27238
+ let keptArchived = 0;
27037
27239
  const errors = [];
27038
27240
  const skippedRelations = [];
27039
27241
  const setCreatedAt = db2.prepare("UPDATE entities SET created_at = ? WHERE name = ?");
@@ -27054,11 +27256,14 @@ function importMemories(args) {
27054
27256
  bundled: entity.metadata,
27055
27257
  exportedAt: args.data.exported_at,
27056
27258
  importVersion: args.data.version,
27057
- mergeStrategy: args.merge_strategy
27259
+ mergeStrategy: args.merge_strategy,
27260
+ isNewEntity: !existing
27058
27261
  });
27059
27262
  if (existing) {
27060
27263
  if (args.merge_strategy === "skip")
27061
27264
  return { kind: "skipped" };
27265
+ if (existing.archived && args.restore_archived !== true)
27266
+ return { kind: "keptArchived" };
27062
27267
  if (args.merge_strategy === "append") {
27063
27268
  const existingText = new Set(existing.observations);
27064
27269
  const newObservations = (entity.observations ?? []).filter((o) => !existingText.has(o));
@@ -27107,6 +27312,8 @@ function importMemories(args) {
27107
27312
  }).immediate();
27108
27313
  if (outcome.kind === "skipped")
27109
27314
  skipped++;
27315
+ else if (outcome.kind === "keptArchived")
27316
+ keptArchived++;
27110
27317
  else if (outcome.kind === "appended")
27111
27318
  appended++;
27112
27319
  else {
@@ -27130,7 +27337,7 @@ function importMemories(args) {
27130
27337
  errors.push(`${rel.from} -${rel.type}-> ${rel.to}: relation not restored (${err instanceof Error ? err.message : String(err)})`);
27131
27338
  }
27132
27339
  }
27133
- return { imported, overwritten, skipped, appended, errors, skipped_relations: skippedRelations };
27340
+ return { imported, overwritten, skipped, appended, kept_archived: keptArchived, errors, skipped_relations: skippedRelations };
27134
27341
  }
27135
27342
 
27136
27343
  // dist/core/operations.js
@@ -27152,19 +27359,113 @@ function recallTagFilter(args) {
27152
27359
  function buildRelevanceMap(entities) {
27153
27360
  return new Map(entities.map((entity, index) => [entity.name, 1 - index / (entities.length + 1)]));
27154
27361
  }
27155
- function remember(args) {
27362
+ function remember(input) {
27156
27363
  const db2 = getDatabase();
27157
27364
  const kg = new KnowledgeGraph(db2);
27158
- return db2.transaction(() => rememberInTransaction(args, db2, kg)).immediate();
27365
+ const { args, derived, typeGiven } = resolveRememberInput(input);
27366
+ return db2.transaction(() => rememberInTransaction(args, derived, typeGiven, db2, kg)).immediate();
27367
+ }
27368
+ var REPLACED_HISTORY_MAX = 20;
27369
+ var REPLACED_HISTORY_MAX_BYTES = 64 * 1024;
27370
+ var jsonBytes = (v) => Buffer.byteLength(JSON.stringify(v), "utf8");
27371
+ function boundReplacedHistory(history) {
27372
+ let out = history.slice(-REPLACED_HISTORY_MAX);
27373
+ while (out.length > 1 && jsonBytes(out) > REPLACED_HISTORY_MAX_BYTES)
27374
+ out = out.slice(1);
27375
+ if (out.length === 1 && jsonBytes(out) > REPLACED_HISTORY_MAX_BYTES) {
27376
+ const only = out[0];
27377
+ const kept = [];
27378
+ const base = { ...only, observations: [], truncated: true };
27379
+ for (const obs of only.observations) {
27380
+ if (jsonBytes([{ ...base, observations: [...kept, obs] }]) > REPLACED_HISTORY_MAX_BYTES)
27381
+ break;
27382
+ kept.push(obs);
27383
+ }
27384
+ out = [{ ...base, observations: kept }];
27385
+ }
27386
+ return out;
27159
27387
  }
27160
- function rememberInTransaction(args, db2, kg) {
27161
- const existing = db2.prepare("SELECT id, namespace, type FROM entities WHERE name = ?").get(args.name);
27162
- const entityId = kg.createEntity(args.name, args.type, {
27163
- observations: args.observations,
27164
- tags: args.tags,
27388
+ function summarizeReplacedHistory(entities) {
27389
+ for (const e of entities) {
27390
+ const history = e.metadata?.replaced_history;
27391
+ if (!Array.isArray(history))
27392
+ continue;
27393
+ const { replaced_history: _dropped, ...rest } = e.metadata;
27394
+ e.metadata = { ...rest, replaced_history_count: history.length };
27395
+ }
27396
+ return entities;
27397
+ }
27398
+ function resolveRememberInput(input) {
27399
+ if (input.note === void 0) {
27400
+ if (!input.name)
27401
+ throw new Error("remember needs `name` and `type`, or `note`");
27402
+ if (input.type === "")
27403
+ throw new Error("remember needs `name` and `type`, or `note`");
27404
+ if (input.type === void 0 && !input.replace)
27405
+ throw new Error("remember needs `name` and `type`, or `note`");
27406
+ return { args: input, typeGiven: input.type !== void 0 };
27407
+ }
27408
+ if (input.title !== void 0 || input.observations !== void 0) {
27409
+ throw new Error("`note` derives title and observations; do not also pass `title` or `observations`");
27410
+ }
27411
+ const derived = deriveNote(input.note);
27412
+ if (!derived)
27413
+ throw new Error("`note` is empty after removing control characters");
27414
+ if (input.replace && !input.name) {
27415
+ throw new Error("`replace` with `note` needs an explicit `name` (a derived name changes with the text)");
27416
+ }
27417
+ return {
27418
+ args: {
27419
+ ...input,
27420
+ name: input.name ?? derived.name,
27421
+ type: input.type ?? NOTE_DEFAULT_TYPE,
27422
+ title: derived.title,
27423
+ observations: derived.observations
27424
+ },
27425
+ derived,
27426
+ typeGiven: input.type !== void 0
27427
+ };
27428
+ }
27429
+ function rememberInTransaction(args, derived, typeGiven, db2, kg) {
27430
+ const existing = db2.prepare("SELECT id, namespace, type, title, status FROM entities WHERE name = ?").get(args.name);
27431
+ if (args.replace && existing && existing.status === "archived") {
27432
+ throw new Error(`"${args.name}" was archived with forget; \`replace\` will not overwrite it. Remember it again without \`replace\` to bring it back, then replace it.`);
27433
+ }
27434
+ const entityType = args.type ?? existing?.type;
27435
+ if (entityType === void 0) {
27436
+ throw new Error(`\`replace\` on "${args.name}": there is no memory named "${args.name}" to inherit a type from, so this call would create one with no type \u2014 pass \`type\` to create it.`);
27437
+ }
27438
+ let replacedVersion;
27439
+ let retypedTo;
27440
+ let tags = args.tags;
27441
+ let title = args.title;
27442
+ let observations = args.observations;
27443
+ if (args.replace && existing) {
27444
+ const previousTags = db2.prepare("SELECT tag FROM tags WHERE entity_id = ? ORDER BY tag").all(existing.id).map((t) => t.tag);
27445
+ replacedVersion = {
27446
+ replaced_at: (/* @__PURE__ */ new Date()).toISOString(),
27447
+ title: existing.title,
27448
+ observations: db2.prepare("SELECT content FROM observations WHERE entity_id = ? ORDER BY id").all(existing.id).map((o) => o.content),
27449
+ tags: previousTags
27450
+ };
27451
+ kg.clearEntityData(args.name);
27452
+ if (typeGiven && entityType !== existing.type) {
27453
+ db2.prepare("UPDATE entities SET type = ? WHERE id = ?").run(entityType, existing.id);
27454
+ retypedTo = entityType;
27455
+ }
27456
+ if (tags === void 0)
27457
+ tags = previousTags;
27458
+ } else if (derived && existing) {
27459
+ title = void 0;
27460
+ const stored = new Set(db2.prepare("SELECT content FROM observations WHERE entity_id = ?").all(existing.id).map((o) => o.content));
27461
+ observations = observations?.filter((o) => !stored.has(o));
27462
+ }
27463
+ const entityId = kg.createEntity(args.name, entityType, {
27464
+ observations,
27465
+ tags,
27165
27466
  namespace: args.namespace,
27166
27467
  trustOverride: args.trustOverride,
27167
- title: args.title
27468
+ title
27168
27469
  });
27169
27470
  kg.updateEntityMetadata(args.name, (current) => buildLocalMetadata(current, {
27170
27471
  trust: args.trustOverride,
@@ -27173,6 +27474,13 @@ function rememberInTransaction(args, db2, kg) {
27173
27474
  ...args.provenanceOverride ?? {}
27174
27475
  }
27175
27476
  }));
27477
+ if (replacedVersion) {
27478
+ const version2 = replacedVersion;
27479
+ kg.updateEntityMetadata(args.name, (current) => {
27480
+ const history = Array.isArray(current.replaced_history) ? current.replaced_history : [];
27481
+ return { ...current, replaced_history: boundReplacedHistory([...history, version2]) };
27482
+ });
27483
+ }
27176
27484
  const relationsCreated = [];
27177
27485
  const relationErrors = [];
27178
27486
  if (args.relations) {
@@ -27196,29 +27504,32 @@ function rememberInTransaction(args, db2, kg) {
27196
27504
  }
27197
27505
  }
27198
27506
  }
27507
+ const storedTitle = db2.prepare("SELECT title FROM entities WHERE id = ?").get(entityId).title;
27199
27508
  return {
27200
27509
  stored: true,
27201
27510
  entityId,
27202
27511
  name: args.name,
27203
- ...args.title !== void 0 ? { title: args.title } : {},
27204
- type: existing?.type ?? args.type,
27205
- observations: args.observations?.length ?? 0,
27206
- tags: args.tags?.length ?? 0,
27512
+ title: storedTitle,
27513
+ type: retypedTo ?? existing?.type ?? entityType,
27514
+ observations: observations?.length ?? 0,
27515
+ tags: tags?.length ?? 0,
27207
27516
  relations: relationsCreated.length,
27208
27517
  ...relationsCreated.length > 0 ? { relationsCreated } : {},
27209
27518
  ...existing && args.namespace !== void 0 && (existing.namespace ?? "personal") !== args.namespace ? { movedFromNamespace: existing.namespace ?? "personal" } : {},
27210
27519
  ...superseded.length > 0 ? { superseded } : {},
27211
- ...relationErrors.length > 0 ? { relationErrors } : {}
27520
+ ...relationErrors.length > 0 ? { relationErrors } : {},
27521
+ ...args.replace ? { replaced: replacedVersion !== void 0 } : {},
27522
+ ...derived ? { derived: { name: args.name, type: retypedTo ?? existing?.type ?? entityType, title: derived.title, observations: derived.observations } } : {}
27212
27523
  };
27213
27524
  }
27214
27525
  function searchAndScore(args) {
27215
27526
  const kg = new KnowledgeGraph(getDatabase());
27216
- const entities = kg.search(args.query, {
27527
+ const entities = summarizeReplacedHistory(kg.search(args.query, {
27217
27528
  tag: recallTagFilter(args),
27218
27529
  limit: args.limit,
27219
27530
  includeArchived: args.include_archived,
27220
27531
  namespace: args.namespace
27221
- });
27532
+ }));
27222
27533
  return {
27223
27534
  entities,
27224
27535
  relevanceMap: args.query ? buildRelevanceMap(entities) : /* @__PURE__ */ new Map()
@@ -27279,11 +27590,11 @@ function forget(args) {
27279
27590
  }
27280
27591
 
27281
27592
  // dist/core/dreamer.js
27282
- import { createHash as createHash5 } from "node:crypto";
27593
+ import { createHash as createHash7 } from "node:crypto";
27283
27594
 
27284
27595
  // dist/core/transcript-source.js
27285
27596
  import fs3 from "fs";
27286
- import { createHash as createHash3 } from "node:crypto";
27597
+ import { createHash as createHash5 } from "node:crypto";
27287
27598
  import path3 from "path";
27288
27599
  var MAX_TRANSCRIPT_SOURCE_BYTES = 8 * 1024 * 1024;
27289
27600
  var MAX_TRANSCRIPT_SCAN_BYTES = 16 * 1024 * 1024;
@@ -27326,7 +27637,7 @@ function readTranscriptSnapshotWithin(transcriptPath, expected, aggregateBytesRe
27326
27637
  if (after.dev !== before.dev || after.ino !== before.ino || after.size !== before.size || after.mtimeNs !== before.mtimeNs || after.ctimeNs !== before.ctimeNs) {
27327
27638
  return { snapshot: null, aggregateLimitExceeded: false };
27328
27639
  }
27329
- const contentHash = createHash3("sha256").update(bytes).digest("hex");
27640
+ const contentHash = createHash5("sha256").update(bytes).digest("hex");
27330
27641
  if (expected && contentHash !== expected.contentHash)
27331
27642
  return { snapshot: null, aggregateLimitExceeded: false };
27332
27643
  return { snapshot: { bytes, contentHash, ...identity }, aggregateLimitExceeded: false };
@@ -27516,7 +27827,7 @@ function parseVisibleConversation(transcript) {
27516
27827
  }
27517
27828
 
27518
27829
  // dist/core/product-improvements.js
27519
- import { createHash as createHash4 } from "node:crypto";
27830
+ import { createHash as createHash6 } from "node:crypto";
27520
27831
  var PRODUCT_IMPROVEMENT_KIND = "product_improvement";
27521
27832
  function clean(label, value, max) {
27522
27833
  const normalized = value.replace(/\s+/g, " ").trim();
@@ -27626,7 +27937,7 @@ function stageProductImprovement(db2, input) {
27626
27937
  success_criteria: successCriteria,
27627
27938
  priority
27628
27939
  };
27629
- const digest = createHash4("sha256").update(JSON.stringify(canonical)).digest("hex");
27940
+ const digest = createHash6("sha256").update(JSON.stringify(canonical)).digest("hex");
27630
27941
  const clusterKey = `product-improvement:${digest}`;
27631
27942
  const existing = db2.prepare(`SELECT id, project, source_ids, proposed_digest, status, reason, created_at, reviewed_at
27632
27943
  FROM dream_proposals
@@ -27832,7 +28143,7 @@ function executeWorkPackage(db2, input, context = {}) {
27832
28143
  const execute = () => {
27833
28144
  const project = input.action === "prepare" ? input.project : input.ref.project;
27834
28145
  const kind = input.action === "prepare" ? input.kind : input.ref.kind;
27835
- const hash2 = (value) => createHash5("sha256").update(JSON.stringify(value)).digest("hex");
28146
+ const hash2 = (value) => createHash7("sha256").update(JSON.stringify(value)).digest("hex");
27836
28147
  if (input.action !== "prepare") {
27837
28148
  const submitted = input.action === "submit" ? input.result : void 0;
27838
28149
  if (submitted && [submitted.name, ...submitted.observations, ...submitted.tags].some((s) => redactSecrets(s) !== s)) {
@@ -28058,10 +28369,72 @@ function computePatterns(db2, categories) {
28058
28369
  };
28059
28370
  }
28060
28371
 
28061
- // dist/core/repo-state.js
28062
- import { execFileSync as execFileSync2 } from "child_process";
28372
+ // dist/core/config.js
28063
28373
  import fs4 from "fs";
28064
28374
  import path4 from "path";
28375
+ function configDir() {
28376
+ return memeshDir();
28377
+ }
28378
+ function configFilePath() {
28379
+ return path4.join(configDir(), "config.json");
28380
+ }
28381
+ var lastConfigReadWarning = null;
28382
+ function warnUnreadable(p, detail) {
28383
+ const key = `${p}::${detail}`;
28384
+ if (key === lastConfigReadWarning)
28385
+ return;
28386
+ lastConfigReadWarning = key;
28387
+ try {
28388
+ process.stderr.write(`[memesh config] ${p} exists but could not be read as a settings object (${detail}). Existing settings are ignored and will not be overwritten until the file is fixed.
28389
+ `);
28390
+ } catch {
28391
+ }
28392
+ }
28393
+ function readRawConfigResult() {
28394
+ const p = configFilePath();
28395
+ if (!fs4.existsSync(p))
28396
+ return { raw: {}, state: "absent" };
28397
+ try {
28398
+ const parsed = JSON.parse(fs4.readFileSync(p, "utf8"));
28399
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
28400
+ throw new Error("top-level JSON value is not an object");
28401
+ }
28402
+ return { raw: parsed, state: "ok" };
28403
+ } catch (error51) {
28404
+ warnUnreadable(p, error51 instanceof Error ? error51.message : String(error51));
28405
+ return { raw: {}, state: "unreadable" };
28406
+ }
28407
+ }
28408
+ function selectConfig(raw) {
28409
+ const config2 = {};
28410
+ if (typeof raw.autoCapture === "boolean")
28411
+ config2.autoCapture = raw.autoCapture;
28412
+ if (typeof raw.sessionLimit === "number" && Number.isFinite(raw.sessionLimit)) {
28413
+ config2.sessionLimit = raw.sessionLimit;
28414
+ }
28415
+ if (raw.autoUpdate === "off" || raw.autoUpdate === "patch" || raw.autoUpdate === "minor" || raw.autoUpdate === "major") {
28416
+ config2.autoUpdate = raw.autoUpdate;
28417
+ }
28418
+ if (typeof raw.updateCheck === "boolean")
28419
+ config2.updateCheck = raw.updateCheck;
28420
+ if (typeof raw.setupCompleted === "boolean")
28421
+ config2.setupCompleted = raw.setupCompleted;
28422
+ if (raw.briefing !== void 0)
28423
+ config2.briefing = raw.briefing;
28424
+ return config2;
28425
+ }
28426
+ function readConfigResult() {
28427
+ const result = readRawConfigResult();
28428
+ return { config: selectConfig(result.raw), state: result.state };
28429
+ }
28430
+ function readConfig() {
28431
+ return readConfigResult().config;
28432
+ }
28433
+
28434
+ // dist/core/repo-state.js
28435
+ import { execFileSync as execFileSync2 } from "child_process";
28436
+ import fs5 from "fs";
28437
+ import path5 from "path";
28065
28438
  var GIT_TIMEOUT_MS = 5e3;
28066
28439
  function tryGit2(cwd, args) {
28067
28440
  try {
@@ -28076,7 +28449,7 @@ function tryGit2(cwd, args) {
28076
28449
  }
28077
28450
  function declaredVersionOf(repoRoot) {
28078
28451
  try {
28079
- const raw = fs4.readFileSync(path4.join(repoRoot, "package.json"), "utf8");
28452
+ const raw = fs5.readFileSync(path5.join(repoRoot, "package.json"), "utf8");
28080
28453
  const version2 = JSON.parse(raw).version;
28081
28454
  return typeof version2 === "string" && version2.length > 0 ? version2 : null;
28082
28455
  } catch {
@@ -28215,26 +28588,109 @@ function taskStateLines(state, project, now = /* @__PURE__ */ new Date()) {
28215
28588
  }
28216
28589
  return lines;
28217
28590
  }
28591
+ var STALE_TASK_STATE_HOURS = 72;
28592
+ var CLOCK_SKEW_ALLOWANCE_MINUTES = 5;
28593
+ var ZONED_INSTANT = /^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})[Tt](?<hour>\d{2}):(?<minute>\d{2})(?::(?<second>\d{2})(?:\.\d+)?)?(?:(?<zulu>[Zz])|(?<offSign>[+-])(?<offHour>\d{2}):?(?<offMinute>\d{2}))$/;
28594
+ function isLeapYear(year) {
28595
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
28596
+ }
28597
+ var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
28598
+ function daysInMonth(year, month) {
28599
+ return month === 2 && isLeapYear(year) ? 29 : DAYS_IN_MONTH[month - 1];
28600
+ }
28601
+ function isRealInstant(groups) {
28602
+ const year = Number(groups.year);
28603
+ const month = Number(groups.month);
28604
+ const day = Number(groups.day);
28605
+ const hour = Number(groups.hour);
28606
+ const minute = Number(groups.minute);
28607
+ const second = groups.second === void 0 ? 0 : Number(groups.second);
28608
+ if (month < 1 || month > 12)
28609
+ return false;
28610
+ if (day < 1 || day > daysInMonth(year, month))
28611
+ return false;
28612
+ if (hour > 23)
28613
+ return false;
28614
+ if (minute > 59)
28615
+ return false;
28616
+ if (second > 59)
28617
+ return false;
28618
+ if (groups.zulu === void 0) {
28619
+ const offHour = Number(groups.offHour);
28620
+ const offMinute = Number(groups.offMinute);
28621
+ if (offHour > 23 || offMinute > 59)
28622
+ return false;
28623
+ if (groups.offSign === "-" && offHour === 0 && offMinute === 0)
28624
+ return false;
28625
+ }
28626
+ return true;
28627
+ }
28628
+ function resolveTaskStateAge(updatedAt, now) {
28629
+ if (!updatedAt)
28630
+ return { known: false };
28631
+ const match = ZONED_INSTANT.exec(updatedAt);
28632
+ if (!match?.groups || !isRealInstant(match.groups))
28633
+ return { known: false };
28634
+ const then = Date.parse(updatedAt);
28635
+ if (Number.isNaN(then))
28636
+ return { known: false };
28637
+ const hours = (now.getTime() - then) / 36e5;
28638
+ if (hours < -(CLOCK_SKEW_ALLOWANCE_MINUTES / 60))
28639
+ return { known: false };
28640
+ return { known: true, hours: Math.max(0, hours) };
28641
+ }
28642
+ function staleTaskStateLine(project, hours) {
28643
+ const days = Math.floor(hours / 24);
28644
+ const age = days >= 1 ? `${days} day${days === 1 ? "" : "s"} ago` : `${Math.floor(hours)} hour${Math.floor(hours) === 1 ? "" : "s"} ago`;
28645
+ return `Task state for "${project}" was last stated ${age} \u2014 older than ${STALE_TASK_STATE_HOURS}h, so it is not shown as current. Run \`memesh task\` to see or update it.`;
28646
+ }
28647
+ function taskStateAgeUnknownLine(project) {
28648
+ return `Task state for "${project}" has a missing, unreadable, or future-dated timestamp, so its age could not be established \u2014 not shown as current. Run \`memesh task\` to see or update it.`;
28649
+ }
28650
+ function briefingTaskStateLines(state, project, now = /* @__PURE__ */ new Date(), { includeFresh = true } = {}) {
28651
+ if (isEmptyTaskState(state))
28652
+ return [];
28653
+ const age = resolveTaskStateAge(state.updated_at, now);
28654
+ if (!age.known)
28655
+ return [taskStateAgeUnknownLine(project)];
28656
+ if (age.hours > STALE_TASK_STATE_HOURS) {
28657
+ return [staleTaskStateLine(project, age.hours)];
28658
+ }
28659
+ return includeFresh ? taskStateLines(state, project, now) : [];
28660
+ }
28218
28661
 
28219
28662
  // dist/core/task-state-store.js
28220
28663
  function readState(name) {
28221
28664
  const row = getDatabase().prepare("SELECT metadata FROM entities WHERE name = ?").get(name);
28222
28665
  if (!row?.metadata)
28223
- return {};
28666
+ return { state: {}, corrupted: false };
28667
+ let parsed;
28224
28668
  try {
28225
- return parseTaskState(JSON.parse(row.metadata));
28669
+ parsed = JSON.parse(row.metadata);
28226
28670
  } catch {
28227
- return {};
28671
+ return { state: {}, corrupted: true };
28228
28672
  }
28673
+ return { state: parseTaskState(parsed), corrupted: false };
28229
28674
  }
28675
+ var TaskStateUnreadableError = class extends Error {
28676
+ project;
28677
+ constructor(project) {
28678
+ super(`task state for project "${project}" is not readable: the stored record is not valid JSON. Re-state it with \`memesh task --goal \u2026\` (any write replaces the broken record).`);
28679
+ this.project = project;
28680
+ this.name = "TaskStateUnreadableError";
28681
+ }
28682
+ };
28230
28683
  function getTaskState(project) {
28231
28684
  const resolved = project ?? getProjectName();
28232
- return { project: resolved, state: readState(taskStateName(resolved)) };
28685
+ const { state, corrupted } = readState(taskStateName(resolved));
28686
+ if (corrupted)
28687
+ throw new TaskStateUnreadableError(resolved);
28688
+ return { project: resolved, state };
28233
28689
  }
28234
28690
  function setTaskState(input) {
28235
28691
  const project = input.project ?? getProjectName();
28236
28692
  const name = taskStateName(project);
28237
- const previous = readState(name);
28693
+ const { state: previous } = readState(name);
28238
28694
  const { state, changed, observations } = mergeTaskState(previous, input.patch, (/* @__PURE__ */ new Date()).toISOString());
28239
28695
  if (changed.length === 0)
28240
28696
  return { project, state, changed };
@@ -28295,7 +28751,7 @@ function unreadInboxLines(count, project, recipient, everSeen) {
28295
28751
  const displayRecipient = JSON.stringify(recipient);
28296
28752
  if (count > 0) {
28297
28753
  const noun = count === 1 ? "message" : "messages";
28298
- return [`${count} ${noun} waiting for ${displayRecipient} in project ${displayProject} \u2014 poll the message tool with project ${displayProject} and recipient ${displayRecipient}, then fetch each message_id; fetching does not acknowledge.`];
28754
+ return [`${count} ${noun} waiting for ${displayRecipient} in project ${displayProject} \u2014 poll the message tool with project ${displayProject} and recipient ${displayRecipient}, then fetch each message_id and record the intake action for it: fetching alone does not acknowledge, and only intake ends this line.`];
28299
28755
  }
28300
28756
  if (everSeen === false) {
28301
28757
  return [`No messages waiting for ${displayRecipient} in project ${displayProject} \u2014 and this recipient id has never been seen in this project (check for a typo).`];
@@ -28528,6 +28984,9 @@ function assembleTopologyBlock(stateLines, pools, projectName, budget = DEFAULT_
28528
28984
  lines.push(...globalLines);
28529
28985
  return lines;
28530
28986
  }
28987
+ function hasBriefingContent(lines) {
28988
+ return lines.length > 0;
28989
+ }
28531
28990
  function buildReferenceContext(memoryLines) {
28532
28991
  const safeLines = memoryLines.map((line) => String(line ?? "").replace(/[\s\u0085\u001c-\u001e]+/g, " ").trim());
28533
28992
  let longestRun = 0;
@@ -28547,6 +29006,214 @@ function buildReferenceContext(memoryLines) {
28547
29006
  ].join("\n");
28548
29007
  }
28549
29008
 
29009
+ // dist/core/briefing-index.js
29010
+ var INDEX_MAX_LINES = 40;
29011
+ var INDEX_MAX_BYTES = 3072;
29012
+ var INDEX_STALE_DAYS = 180;
29013
+ var INDEX_LINE_MAX_CHARS = 120;
29014
+ var INDEX_SNIPPET_FETCH_CHARS = 4e3;
29015
+ var INDEX_CANDIDATE_CAP = 2e3;
29016
+ var INDEX_EXCLUDED_TYPES = [...EVIDENCE_LAYER_TYPES, "task-state"];
29017
+ function isIndexableType(type) {
29018
+ return !INDEX_EXCLUDED_TYPES.includes(type || "memory");
29019
+ }
29020
+ var DAY_MS = 24 * 60 * 60 * 1e3;
29021
+ function byteLength(text) {
29022
+ return new TextEncoder().encode(text).length;
29023
+ }
29024
+ function sectionBytes(lines) {
29025
+ return lines.reduce((sum, line) => sum + byteLength(line) + 1, 0);
29026
+ }
29027
+ function parseActivity(value) {
29028
+ if (!value)
29029
+ return Number.NaN;
29030
+ const iso = /[zZ]|[+-]\d\d:?\d\d$/.test(value) ? value : `${value.replace(" ", "T")}Z`;
29031
+ return Date.parse(iso);
29032
+ }
29033
+ function compareIndexCandidates(a, b) {
29034
+ const at = parseActivity(a.lastActivity);
29035
+ const bt = parseActivity(b.lastActivity);
29036
+ const av = Number.isNaN(at) ? -Infinity : at;
29037
+ const bv = Number.isNaN(bt) ? -Infinity : bt;
29038
+ if (av !== bv)
29039
+ return bv - av;
29040
+ return b.id - a.id;
29041
+ }
29042
+ function candidateIsAutoInjectable(metadata) {
29043
+ if (metadata == null)
29044
+ return true;
29045
+ if (typeof metadata === "string") {
29046
+ let parsed;
29047
+ try {
29048
+ parsed = JSON.parse(metadata);
29049
+ } catch {
29050
+ return false;
29051
+ }
29052
+ return parsed !== null && typeof parsed === "object" && isAutoInjectable(parsed);
29053
+ }
29054
+ return isAutoInjectable(metadata);
29055
+ }
29056
+ function redact(text) {
29057
+ if (!text)
29058
+ return "";
29059
+ return redactUserPaths(redactSecrets(String(text))).replace(/\s+/g, " ").trim();
29060
+ }
29061
+ function indexLine(candidate) {
29062
+ const title = redact(candidate.title);
29063
+ const snippet = redact(candidate.snippet);
29064
+ const repeats = title && snippet && snippet.toLowerCase().startsWith(title.replace(/…$/, "").toLowerCase());
29065
+ const text = title && snippet && !repeats ? `${title} \u2014 ${snippet}` : title || snippet;
29066
+ return topologyLine({ name: String(candidate.id), id: candidate.id, type: candidate.type || "memory", title: text || null }, INDEX_LINE_MAX_CHARS);
29067
+ }
29068
+ function indexHeading(projectName) {
29069
+ return `Index of durable memories for "${projectName}" (newest first):`;
29070
+ }
29071
+ function indexEmptyLine(projectName) {
29072
+ return `- No durable memories (decisions, lessons, patterns, references) for "${projectName}" yet.`;
29073
+ }
29074
+ function moreLine(n, truncated) {
29075
+ return `- ${n}${truncated ? "+" : ""} more \u2014 memesh recall --tag "project:\u2026"`;
29076
+ }
29077
+ function olderLine(n, truncated) {
29078
+ return `- ${n}${truncated ? "+" : ""} older memor${n === 1 ? "y" : "ies"} (no change in ${INDEX_STALE_DAYS} days) \u2014 recall to see`;
29079
+ }
29080
+ function footerLine(shown, bytes, tokens) {
29081
+ return `(index cost: ${shown} line${shown === 1 ? "" : "s"}, ${bytes} bytes \u2248 ${tokens} tokens; cap ${INDEX_MAX_LINES} lines / ${INDEX_MAX_BYTES} bytes)`;
29082
+ }
29083
+ function closeWithFooter(lines, shown) {
29084
+ const above = sectionBytes(lines);
29085
+ let footer = footerLine(shown, above, Math.ceil(above / 4));
29086
+ for (let step = 0; step < 8; step++) {
29087
+ const bytes = above + byteLength(footer) + 1;
29088
+ const tokens = Math.ceil(bytes / 4);
29089
+ const next = footerLine(shown, bytes, tokens);
29090
+ if (next === footer)
29091
+ return { lines: [...lines, footer], bytes, tokens };
29092
+ footer = next;
29093
+ }
29094
+ throw new Error("briefing index: the footer cost did not converge");
29095
+ }
29096
+ function buildBriefingIndex(candidates, projectName, now, options = {}) {
29097
+ const truncated = options.truncated === true;
29098
+ const cutoff = now - INDEX_STALE_DAYS * DAY_MS;
29099
+ const eligible = candidates.filter((c) => isIndexableType(c.type) && candidateIsAutoInjectable(c.metadata)).slice().sort(compareIndexCandidates);
29100
+ const current = [];
29101
+ let older = 0;
29102
+ for (const c of eligible) {
29103
+ const at = parseActivity(c.lastActivity);
29104
+ if (!Number.isNaN(at) && at < cutoff)
29105
+ older++;
29106
+ else
29107
+ current.push(c);
29108
+ }
29109
+ const heading = indexHeading(projectName);
29110
+ if (current.length === 0 && older === 0) {
29111
+ const closed2 = closeWithFooter([heading, indexEmptyLine(projectName)], 0);
29112
+ return { ...closed2, shown: 0, more: 0, older: 0, truncated, ids: [] };
29113
+ }
29114
+ const reserve = sectionBytes([
29115
+ moreLine(current.length, truncated),
29116
+ olderLine(older, truncated),
29117
+ footerLine(INDEX_MAX_LINES, INDEX_MAX_BYTES, INDEX_MAX_BYTES)
29118
+ ]);
29119
+ const budget = INDEX_MAX_BYTES - reserve - sectionBytes([heading]);
29120
+ const rendered = [];
29121
+ const ids = [];
29122
+ let used = 0;
29123
+ for (const c of current) {
29124
+ if (rendered.length >= INDEX_MAX_LINES)
29125
+ break;
29126
+ const line = indexLine(c);
29127
+ const cost = byteLength(line) + 1;
29128
+ if (used + cost > budget)
29129
+ break;
29130
+ rendered.push(line);
29131
+ ids.push(c.id);
29132
+ used += cost;
29133
+ }
29134
+ const more = current.length - rendered.length;
29135
+ const above = [heading, ...rendered];
29136
+ if (more > 0)
29137
+ above.push(moreLine(more, truncated));
29138
+ if (older > 0)
29139
+ above.push(olderLine(older, truncated));
29140
+ const closed = closeWithFooter(above, rendered.length);
29141
+ return { ...closed, shown: rendered.length, more, older, truncated, ids };
29142
+ }
29143
+
29144
+ // dist/core/briefing-level.js
29145
+ var BRIEFING_LEVELS = ["minimal", "standard", "full"];
29146
+ var DEFAULT_BRIEFING_LEVEL = "standard";
29147
+ function isBriefingLevel(value) {
29148
+ return typeof value === "string" && BRIEFING_LEVELS.includes(value);
29149
+ }
29150
+ var INVALID_VALUE_SERIALIZED_MAX = 100;
29151
+ var PRE_SLICE_RAW_MAX = 256;
29152
+ function safeRawPreSlice(value, maxUnits) {
29153
+ if (value.length <= maxUnits)
29154
+ return value;
29155
+ let end = maxUnits;
29156
+ const lastKept = value.charCodeAt(end - 1);
29157
+ const nextUnit = value.charCodeAt(end);
29158
+ const lastKeptIsHighSurrogate = lastKept >= 55296 && lastKept <= 56319;
29159
+ const nextUnitIsLowSurrogate = nextUnit >= 56320 && nextUnit <= 57343;
29160
+ if (lastKeptIsHighSurrogate && nextUnitIsLowSurrogate)
29161
+ end -= 1;
29162
+ return value.slice(0, end);
29163
+ }
29164
+ function truncateToSerializedBound(codePoints) {
29165
+ let kept = 0;
29166
+ for (; kept < codePoints.length; kept++) {
29167
+ const candidate = JSON.stringify(`${codePoints.slice(0, kept + 1).join("")}\u2026`);
29168
+ if (candidate.length > INVALID_VALUE_SERIALIZED_MAX)
29169
+ break;
29170
+ }
29171
+ return JSON.stringify(`${codePoints.slice(0, kept).join("")}\u2026`);
29172
+ }
29173
+ function describeInvalidValue(value) {
29174
+ if (typeof value === "string") {
29175
+ if (value.length <= INVALID_VALUE_SERIALIZED_MAX) {
29176
+ const whole = JSON.stringify(value);
29177
+ if (whole.length <= INVALID_VALUE_SERIALIZED_MAX)
29178
+ return whole;
29179
+ return truncateToSerializedBound(Array.from(value));
29180
+ }
29181
+ return truncateToSerializedBound(Array.from(safeRawPreSlice(value, PRE_SLICE_RAW_MAX)));
29182
+ }
29183
+ if (Array.isArray(value))
29184
+ return "[array]";
29185
+ if (value !== null && typeof value === "object")
29186
+ return "[object]";
29187
+ if (typeof value === "bigint")
29188
+ return "[bigint]";
29189
+ if (typeof value === "symbol")
29190
+ return "[symbol]";
29191
+ if (typeof value === "function")
29192
+ return "[function]";
29193
+ return JSON.stringify(value) ?? String(value);
29194
+ }
29195
+ function resolveBriefingLevel(envValue, configValue) {
29196
+ if (envValue !== void 0) {
29197
+ if (isBriefingLevel(envValue))
29198
+ return { level: envValue, invalid: null };
29199
+ return { level: DEFAULT_BRIEFING_LEVEL, invalid: { source: "env", value: describeInvalidValue(envValue) } };
29200
+ }
29201
+ if (configValue !== void 0) {
29202
+ if (isBriefingLevel(configValue))
29203
+ return { level: configValue, invalid: null };
29204
+ return { level: DEFAULT_BRIEFING_LEVEL, invalid: { source: "config", value: describeInvalidValue(configValue) } };
29205
+ }
29206
+ return { level: DEFAULT_BRIEFING_LEVEL, invalid: null };
29207
+ }
29208
+ var POLICIES = {
29209
+ minimal: { global: false, foreign: false, taskState: false, index: false, workPackageNotice: false },
29210
+ standard: { global: false, foreign: false, taskState: true, index: true, workPackageNotice: false },
29211
+ full: { global: true, foreign: true, taskState: true, index: true, workPackageNotice: true }
29212
+ };
29213
+ function briefingLevelPolicy(level) {
29214
+ return POLICIES[level];
29215
+ }
29216
+
28550
29217
  // dist/core/briefing.js
28551
29218
  var PROJECT_LIMIT = 30;
28552
29219
  var RECENT_LIMIT = 5;
@@ -28587,16 +29254,61 @@ function toTopologyEntity(row, snippet) {
28587
29254
  signalScore: typeof signal === "number" ? signal : null
28588
29255
  };
28589
29256
  }
29257
+ function readBriefingIndex(db2, projectName, now = Date.now()) {
29258
+ const hasNamespace = db2.prepare("PRAGMA table_info(entities)").all().some((column) => column.name === "namespace");
29259
+ const nonGlobal = hasNamespace ? " AND (e.namespace IS NULL OR e.namespace <> 'global')" : "";
29260
+ const excluded = INDEX_EXCLUDED_TYPES.map(() => "?").join(",");
29261
+ const rows = db2.prepare(`SELECT e.id, e.type, e.title, e.metadata,
29262
+ (SELECT substr(o.content, 1, ${INDEX_SNIPPET_FETCH_CHARS}) FROM observations o
29263
+ WHERE o.entity_id = e.id ORDER BY o.id ASC LIMIT 1) AS snippet,
29264
+ max(e.created_at, COALESCE((SELECT MAX(o2.created_at) FROM observations o2
29265
+ WHERE o2.entity_id = e.id), e.created_at)) AS last_activity
29266
+ FROM entities e
29267
+ WHERE e.id IN (SELECT entity_id FROM tags WHERE tag = ?)
29268
+ AND e.status = 'active'${nonGlobal}
29269
+ AND e.type NOT IN (${excluded})
29270
+ ORDER BY last_activity DESC, e.id DESC
29271
+ LIMIT ?`).all(`project:${projectName}`, ...INDEX_EXCLUDED_TYPES, INDEX_CANDIDATE_CAP);
29272
+ const candidates = rows.map((row) => ({
29273
+ id: row.id,
29274
+ type: row.type,
29275
+ title: row.title,
29276
+ snippet: row.snippet,
29277
+ lastActivity: row.last_activity,
29278
+ metadata: row.metadata
29279
+ }));
29280
+ return buildBriefingIndex(candidates, projectName, now, { truncated: rows.length >= INDEX_CANDIDATE_CAP });
29281
+ }
28590
29282
  function assembleBriefing(project, recipient) {
28591
29283
  const projectName = project ?? getProjectName();
28592
29284
  const db2 = getDatabase();
29285
+ const resolvedLevel = resolveBriefingLevel(process.env.MEMESH_BRIEFING, readConfig().briefing);
29286
+ if (resolvedLevel.invalid) {
29287
+ const { source, value } = resolvedLevel.invalid;
29288
+ try {
29289
+ process.stderr.write(`[memesh briefing] invalid ${source} briefing level ${value} \u2014 using "${resolvedLevel.level}"
29290
+ `);
29291
+ } catch {
29292
+ }
29293
+ }
29294
+ const level = resolvedLevel.level;
29295
+ const policy = briefingLevelPolicy(level);
28593
29296
  const repoLines = project === void 0 || project === getProjectName() ? repoStateLines(readRepoState()) : [];
28594
- const { state } = getTaskState(projectName);
29297
+ let taskLines;
29298
+ try {
29299
+ taskLines = briefingTaskStateLines(getTaskState(projectName).state, projectName, /* @__PURE__ */ new Date(), {
29300
+ includeFresh: policy.taskState
29301
+ });
29302
+ } catch (err) {
29303
+ if (!(err instanceof TaskStateUnreadableError))
29304
+ throw err;
29305
+ taskLines = [`task state for ${projectName}: ${err.message}`];
29306
+ }
28595
29307
  const inboxRecipient = recipient === void 0 ? void 0 : canonicalAgentScopeId(recipient);
28596
29308
  const unreadCount = unreadDeliveryCount(db2, canonicalAgentScopeId(projectName), inboxRecipient);
28597
29309
  const everSeen = inboxRecipient !== void 0 && unreadCount === 0 ? recipientEverSeen(db2, canonicalAgentScopeId(projectName), inboxRecipient) : void 0;
28598
29310
  const stateLines = [
28599
- ...taskStateLines(state, projectName),
29311
+ ...taskLines,
28600
29312
  ...unreadInboxLines(unreadCount, canonicalAgentScopeId(projectName), inboxRecipient, everSeen)
28601
29313
  ];
28602
29314
  const hasNamespace = db2.prepare("PRAGMA table_info(entities)").all().some((column) => column.name === "namespace");
@@ -28607,17 +29319,17 @@ function assembleBriefing(project, recipient) {
28607
29319
  ORDER BY e.id DESC
28608
29320
  LIMIT ?`).all(`project:${projectName}`, TOPOLOGY_CANDIDATE_CAP);
28609
29321
  const projectPool = selectPool(projectRows, PROJECT_LIMIT);
28610
- const globalRows = hasNamespace ? db2.prepare(`SELECT ${CANDIDATE_COLUMNS}
29322
+ const globalRows = policy.global && hasNamespace ? db2.prepare(`SELECT ${CANDIDATE_COLUMNS}
28611
29323
  FROM entities e
28612
29324
  WHERE e.namespace = 'global' AND e.status = 'active'
28613
29325
  ORDER BY e.id DESC
28614
29326
  LIMIT ?`).all(TOPOLOGY_CANDIDATE_CAP) : [];
28615
29327
  const globalPool = selectPool(globalRows, GLOBAL_TOPOLOGY_LIMIT);
28616
- const recentRows = db2.prepare(`SELECT ${CANDIDATE_COLUMNS}
28617
- FROM entities e
28618
- WHERE e.status = 'active'${nonGlobal}
28619
- ORDER BY e.id DESC
28620
- LIMIT ?`).all(TOPOLOGY_CANDIDATE_CAP);
29328
+ const recentRows = policy.foreign ? db2.prepare(`SELECT ${CANDIDATE_COLUMNS}
29329
+ FROM entities e
29330
+ WHERE e.status = 'active'${nonGlobal}
29331
+ ORDER BY e.id DESC
29332
+ LIMIT ?`).all(TOPOLOGY_CANDIDATE_CAP) : [];
28621
29333
  const recentPool = selectPool(recentRows, RECENT_LIMIT);
28622
29334
  const survivorIds = [...new Set([...projectPool, ...globalPool, ...recentPool].map((row) => row.id))];
28623
29335
  const snippets = /* @__PURE__ */ new Map();
@@ -28641,11 +29353,18 @@ function assembleBriefing(project, recipient) {
28641
29353
  { entities: toEntities(recentPool), foreign: true }
28642
29354
  ], projectName);
28643
29355
  const withRepo = lines.length > 0 && repoLines.length > 0 ? [...repoLines, "", ...lines] : lines;
29356
+ const index = readBriefingIndex(db2, projectName);
29357
+ const indexLines = policy.index ? index.lines : [];
29358
+ const block = withRepo.length > 0 && indexLines.length > 0 ? [...withRepo, "", ...indexLines] : [...withRepo, ...indexLines];
29359
+ const empty = !hasBriefingContent(block);
28644
29360
  return {
28645
29361
  project: projectName,
28646
- text: lines.length > 0 ? buildReferenceContext(withRepo) : "",
29362
+ text: empty ? "" : buildReferenceContext(block),
28647
29363
  entityCount: lines.filter((l) => l.startsWith("- [")).length,
28648
- hasTaskState: stateLines.length > 0
29364
+ hasTaskState: stateLines.length > 0,
29365
+ index,
29366
+ level,
29367
+ empty
28649
29368
  };
28650
29369
  }
28651
29370
 
@@ -28653,11 +29372,11 @@ function assembleBriefing(project, recipient) {
28653
29372
  import { randomUUID as randomUUID4 } from "node:crypto";
28654
29373
 
28655
29374
  // dist/core/agent-messaging.js
28656
- import { createHash as createHash7, randomBytes, randomUUID as randomUUID2 } from "node:crypto";
29375
+ import { createHash as createHash9, randomBytes, randomUUID as randomUUID2 } from "node:crypto";
28657
29376
 
28658
29377
  // dist/core/agent-message-storage.js
28659
- import { createHash as createHash6, randomUUID } from "node:crypto";
28660
- import fs5 from "node:fs";
29378
+ import { createHash as createHash8, randomUUID } from "node:crypto";
29379
+ import fs6 from "node:fs";
28661
29380
  var TERMINAL_WORKFLOW_STATES = /* @__PURE__ */ new Set(["completed", "cancelled", "rejected"]);
28662
29381
  var AgentMessageStorageError = class extends Error {
28663
29382
  code;
@@ -29289,7 +30008,7 @@ function parseTargetKind(value) {
29289
30008
  }
29290
30009
  function parseJsonObject(json2, label) {
29291
30010
  const parsed = parseJsonObjectOrValue(json2);
29292
- if (!isPlainObject3(parsed))
30011
+ if (!isPlainObject4(parsed))
29293
30012
  throw new AgentMessagingError(`${label} must contain a JSON object.`);
29294
30013
  return parsed;
29295
30014
  }
@@ -29304,7 +30023,7 @@ function parseJsonObjectOrValue(json2) {
29304
30023
  return parsed;
29305
30024
  }
29306
30025
  function hashCanonical(value) {
29307
- return createHash7("sha256").update(stableStringify(value)).digest("hex");
30026
+ return createHash9("sha256").update(stableStringify(value)).digest("hex");
29308
30027
  }
29309
30028
  function stableStringify(value) {
29310
30029
  if (value === null)
@@ -29339,7 +30058,7 @@ function assertJsonValue(value, label) {
29339
30058
  value.forEach((entry, index) => assertJsonValue(entry, `${label}[${index}]`));
29340
30059
  return;
29341
30060
  }
29342
- if (isPlainObject3(value)) {
30061
+ if (isPlainObject4(value)) {
29343
30062
  for (const [key, entry] of Object.entries(value)) {
29344
30063
  assertJsonValue(entry, `${label}.${key}`);
29345
30064
  }
@@ -29351,7 +30070,7 @@ function normalizeObject(value) {
29351
30070
  assertJsonValue(value, "object");
29352
30071
  return value;
29353
30072
  }
29354
- function isPlainObject3(value) {
30073
+ function isPlainObject4(value) {
29355
30074
  return typeof value === "object" && value !== null && !Array.isArray(value);
29356
30075
  }
29357
30076
  function requireText(label, value, maxLength) {
@@ -29454,14 +30173,38 @@ var WorkPackageSchema = external_exports.discriminatedUnion("action", [
29454
30173
  }).strict()
29455
30174
  ]);
29456
30175
  var RememberSchema = external_exports.object({
29457
- name: nameField,
29458
- type: external_exports.string().min(1).max(100),
30176
+ name: nameField.optional(),
30177
+ type: external_exports.string().min(1).max(100).optional(),
29459
30178
  title: titleField,
29460
30179
  observations: external_exports.array(observationField).max(100).optional(),
30180
+ note: external_exports.string().max(NOTE_MAX_CHARS).optional(),
30181
+ replace: external_exports.boolean().optional(),
29461
30182
  tags: external_exports.array(external_exports.string().max(255)).max(50).optional(),
29462
30183
  relations: external_exports.array(external_exports.object({ to: external_exports.string().min(1).max(255), type: external_exports.string().min(1).max(100) }).strict()).max(50).optional(),
29463
30184
  namespace: external_exports.enum(NAMESPACES).optional()
29464
- }).strict();
30185
+ }).strict().superRefine((data, ctx) => {
30186
+ if (data.note === void 0) {
30187
+ if (data.name === void 0)
30188
+ ctx.addIssue({ code: "custom", path: ["name"], message: "name is required (or pass `note` to have it derived)" });
30189
+ if (data.type === void 0 && !(data.replace && data.name !== void 0))
30190
+ ctx.addIssue({ code: "custom", path: ["type"], message: 'type is required (or pass `note`, which defaults it to "note", or `replace: true` with a `name` to keep the type that memory already has)' });
30191
+ return;
30192
+ }
30193
+ for (const key of ["title", "observations"]) {
30194
+ if (data[key] !== void 0) {
30195
+ ctx.addIssue({ code: "custom", path: [key], message: `${key} cannot be combined with note \u2014 note derives it; to correct the derived ${key}, call again with name, replace: true and a structured ${key} (pass \`type\` only to also change the memory's type)` });
30196
+ }
30197
+ }
30198
+ if (data.replace && data.name === void 0) {
30199
+ ctx.addIssue({ code: "custom", path: ["name"], message: "replace with note needs an explicit name \u2014 a derived name changes with the text, so there is nothing to replace" });
30200
+ }
30201
+ const derived = deriveNote(data.note);
30202
+ if (!derived) {
30203
+ ctx.addIssue({ code: "custom", path: ["note"], message: "note must contain some text" });
30204
+ } else if (derived.observations.length > NOTE_MAX_OBSERVATIONS) {
30205
+ ctx.addIssue({ code: "custom", path: ["note"], message: `note yields ${derived.observations.length} observations; at most ${NOTE_MAX_OBSERVATIONS} are stored per memory` });
30206
+ }
30207
+ });
29465
30208
  var RecallSchema = external_exports.object({
29466
30209
  query: external_exports.string().max(1e3).optional(),
29467
30210
  tag: external_exports.string().max(255).optional(),
@@ -29496,7 +30239,8 @@ var ExportResultSchema = external_exports.object({
29496
30239
  var ImportSchema = external_exports.object({
29497
30240
  data: ExportResultSchema,
29498
30241
  namespace: external_exports.enum(NAMESPACES).optional(),
29499
- merge_strategy: external_exports.enum(["skip", "overwrite", "append"])
30242
+ merge_strategy: external_exports.enum(["skip", "overwrite", "append"]),
30243
+ restore_archived: external_exports.boolean().optional()
29500
30244
  }).strict();
29501
30245
  var LearnSchema = external_exports.object({
29502
30246
  error: external_exports.string().min(1).max(5e3),
@@ -29623,9 +30367,9 @@ var MessageSchema = external_exports.discriminatedUnion("action", [
29623
30367
 
29624
30368
  // dist/core/agent-router.js
29625
30369
  import { randomUUID as randomUUID3 } from "node:crypto";
29626
- import fs6 from "node:fs";
30370
+ import fs7 from "node:fs";
29627
30371
  import net from "node:net";
29628
- import path5 from "node:path";
30372
+ import path6 from "node:path";
29629
30373
  var AGENT_ROUTER_PROTOCOL_VERSION = 2;
29630
30374
  var AGENT_ROUTER_MAX_FRAME_BYTES = 64 * 1024;
29631
30375
  var MAX_LEASE_MS = 5 * 6e4;
@@ -29633,9 +30377,9 @@ var DEFAULT_CLIENT_TIMEOUT_MS = 2e3;
29633
30377
  var MAX_FIELD_LENGTH = 200;
29634
30378
  var MAX_ADAPTER_RECEIPT_BYTES = 16 * 1024;
29635
30379
  function isLegacyAgentRouterVersionMismatchResponse(value) {
29636
- if (!isPlainObject4(value) || value.version !== 1 || value.request_id !== "" || value.ok !== false)
30380
+ if (!isPlainObject5(value) || value.version !== 1 || value.request_id !== "" || value.ok !== false)
29637
30381
  return false;
29638
- if (!isPlainObject4(value.error))
30382
+ if (!isPlainObject5(value.error))
29639
30383
  return false;
29640
30384
  return value.error.code === "unsupported_type" || value.error.code === "unsupported_version";
29641
30385
  }
@@ -29703,7 +30447,7 @@ async function sendAgentRouterRequest(socketPath, request, timeoutMs = DEFAULT_C
29703
30447
  if (isLegacyAgentRouterVersionMismatchResponse(response)) {
29704
30448
  throw new AgentRouterProtocolError("router_version_mismatch", "router_version_mismatch: the configured router endpoint uses a stale protocol; restart that router with the current MeMesh version.");
29705
30449
  }
29706
- const uncorrelatedError = response.request_id === "" && response.ok === false && isPlainObject4(response.error) && typeof response.error.code === "string" && typeof response.error.message === "string";
30450
+ const uncorrelatedError = response.request_id === "" && response.ok === false && isPlainObject5(response.error) && typeof response.error.code === "string" && typeof response.error.message === "string";
29707
30451
  if (response.version !== AGENT_ROUTER_PROTOCOL_VERSION || response.request_id !== request.request_id && !uncorrelatedError) {
29708
30452
  throw new AgentRouterProtocolError("invalid_response", "Router response identity does not match.");
29709
30453
  }
@@ -29722,7 +30466,7 @@ async function sendAgentRouterRequest(socketPath, request, timeoutMs = DEFAULT_C
29722
30466
  });
29723
30467
  }
29724
30468
  function validateRouterSuccessResult(request, value) {
29725
- if (!isPlainObject4(value)) {
30469
+ if (!isPlainObject5(value)) {
29726
30470
  throw new AgentRouterProtocolError("invalid_response", "Router response result must be an object.");
29727
30471
  }
29728
30472
  const requireResultString = (field) => {
@@ -29780,17 +30524,17 @@ function validateRouterSuccessResult(request, value) {
29780
30524
  return value;
29781
30525
  }
29782
30526
  function isSelectionCard(value, project) {
29783
- if (!isPlainObject4(value))
30527
+ if (!isPlainObject5(value))
29784
30528
  return false;
29785
30529
  return typeof value.session_id === "string" && typeof value.principal_id === "string" && ["codex", "claude", "gemini", "other"].includes(String(value.host_kind)) && value.project === project && (value.model === null || typeof value.model === "string") && (value.work_summary === null || typeof value.work_summary === "string") && value.active === true && Number.isSafeInteger(value.generation) && value.generation >= 1 && Number.isSafeInteger(value.lease_expires_at_ms) && value.lease_expires_at_ms >= 0;
29786
30530
  }
29787
30531
  function validateSocketPath(socketPath) {
29788
- if (typeof socketPath !== "string" || !path5.isAbsolute(socketPath) || Buffer.byteLength(socketPath) > 103) {
30532
+ if (typeof socketPath !== "string" || !path6.isAbsolute(socketPath) || Buffer.byteLength(socketPath) > 103) {
29789
30533
  throw new AgentRouterProtocolError("invalid_socket_path", "Router socket path must be absolute and at most 103 bytes.");
29790
30534
  }
29791
30535
  return socketPath;
29792
30536
  }
29793
- function isPlainObject4(value) {
30537
+ function isPlainObject5(value) {
29794
30538
  return typeof value === "object" && value !== null && !Array.isArray(value);
29795
30539
  }
29796
30540
 
@@ -30142,6 +30886,543 @@ async function executeAgentMessageAction(db2, rawInput, context, dependencies =
30142
30886
  }
30143
30887
  }
30144
30888
 
30889
+ // dist/core/update-entrypoint.js
30890
+ import fs10 from "fs";
30891
+ import path9 from "path";
30892
+ import { spawn } from "child_process";
30893
+ import { fileURLToPath } from "url";
30894
+
30895
+ // dist/core/version-check.js
30896
+ import fs8 from "fs";
30897
+ import path7 from "path";
30898
+
30899
+ // dist/core/semver.js
30900
+ var SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
30901
+ var NUMERIC_IDENTIFIER = /^\d+$/;
30902
+ function parseSemVer(version2) {
30903
+ const match = SEMVER.exec(version2);
30904
+ if (!match)
30905
+ return null;
30906
+ const prerelease = match[4]?.split(".") ?? null;
30907
+ if (prerelease?.some((identifier) => NUMERIC_IDENTIFIER.test(identifier) && identifier.length > 1 && identifier.startsWith("0")))
30908
+ return null;
30909
+ return {
30910
+ core: [BigInt(match[1]), BigInt(match[2]), BigInt(match[3])],
30911
+ prerelease
30912
+ };
30913
+ }
30914
+ function compareIdentifiers(a, b) {
30915
+ const aNumeric = NUMERIC_IDENTIFIER.test(a);
30916
+ const bNumeric = NUMERIC_IDENTIFIER.test(b);
30917
+ if (aNumeric && bNumeric)
30918
+ return BigInt(a) < BigInt(b) ? -1 : BigInt(a) > BigInt(b) ? 1 : 0;
30919
+ if (aNumeric !== bNumeric)
30920
+ return aNumeric ? -1 : 1;
30921
+ return a < b ? -1 : a > b ? 1 : 0;
30922
+ }
30923
+ function compareSemVerPrecedence(a, b) {
30924
+ for (let index = 0; index < a.core.length; index += 1) {
30925
+ if (a.core[index] !== b.core[index])
30926
+ return a.core[index] < b.core[index] ? -1 : 1;
30927
+ }
30928
+ if (a.prerelease === null || b.prerelease === null) {
30929
+ if (a.prerelease !== b.prerelease)
30930
+ return a.prerelease === null ? 1 : -1;
30931
+ return 0;
30932
+ }
30933
+ const length = Math.max(a.prerelease.length, b.prerelease.length);
30934
+ for (let index = 0; index < length; index += 1) {
30935
+ const aIdentifier = a.prerelease[index];
30936
+ const bIdentifier = b.prerelease[index];
30937
+ if (aIdentifier === void 0 || bIdentifier === void 0) {
30938
+ if (aIdentifier !== bIdentifier)
30939
+ return aIdentifier === void 0 ? -1 : 1;
30940
+ break;
30941
+ }
30942
+ const compared = compareIdentifiers(aIdentifier, bIdentifier);
30943
+ if (compared !== 0)
30944
+ return compared;
30945
+ }
30946
+ return 0;
30947
+ }
30948
+
30949
+ // dist/core/version-check.js
30950
+ var STALE_AFTER_MS = 24 * 60 * 60 * 1e3;
30951
+ function isUpdateAvailable(currentVersion, latestVersion) {
30952
+ if (latestVersion === null)
30953
+ return false;
30954
+ const current = parseSemVer(currentVersion);
30955
+ const latest = parseSemVer(latestVersion);
30956
+ if (!current || !latest)
30957
+ return false;
30958
+ return compareSemVerPrecedence(current, latest) < 0;
30959
+ }
30960
+ function getUpdateCheckPath(updateCheckPath, currentVersion) {
30961
+ if (updateCheckPath)
30962
+ return updateCheckPath;
30963
+ if (process.env.MEMESH_UPDATE_CHECK_PATH)
30964
+ return process.env.MEMESH_UPDATE_CHECK_PATH;
30965
+ const versionTag = currentVersion && /^[0-9A-Za-z.+-]+$/.test(currentVersion) ? currentVersion : "unknown";
30966
+ return path7.join(memeshDir(), `update-check.${versionTag}.json`);
30967
+ }
30968
+ function parseIsoDate(value) {
30969
+ if (!value)
30970
+ return null;
30971
+ const timestamp = Date.parse(value);
30972
+ return Number.isNaN(timestamp) ? null : timestamp;
30973
+ }
30974
+ function determineFreshness(source, checkSucceeded, lastSuccessfulCheckAt, now) {
30975
+ if (!lastSuccessfulCheckAt)
30976
+ return "unavailable";
30977
+ if (source === "fresh" && checkSucceeded)
30978
+ return "fresh";
30979
+ const successfulAt = parseIsoDate(lastSuccessfulCheckAt);
30980
+ if (successfulAt === null)
30981
+ return "unavailable";
30982
+ return now.getTime() - successfulAt > STALE_AFTER_MS ? "stale" : "cached";
30983
+ }
30984
+ function buildResult(currentVersion, stored, source, now) {
30985
+ const versionMatches = stored.currentVersion === currentVersion;
30986
+ const deprecationMessage = versionMatches ? stored.currentVersionDeprecation : null;
30987
+ const lastError = versionMatches ? stored.lastError : null;
30988
+ return {
30989
+ currentVersion,
30990
+ latestVersion: stored.latestVersion,
30991
+ checkedAt: stored.lastAttemptAt,
30992
+ lastAttemptAt: stored.lastAttemptAt,
30993
+ lastSuccessfulCheckAt: stored.lastSuccessfulCheckAt,
30994
+ lastError,
30995
+ updateAvailable: isUpdateAvailable(currentVersion, stored.latestVersion),
30996
+ checkSucceeded: stored.checkSucceeded,
30997
+ source,
30998
+ freshness: determineFreshness(source, stored.checkSucceeded, stored.lastSuccessfulCheckAt, now),
30999
+ currentVersionDeprecated: deprecationMessage !== null,
31000
+ deprecationMessage
31001
+ };
31002
+ }
31003
+ function parseStoredUpdateCheck(raw) {
31004
+ if (!raw || typeof raw !== "object")
31005
+ return null;
31006
+ const candidate = raw;
31007
+ const latestVersion = candidate.latestVersion;
31008
+ const lastAttemptAt = "lastAttemptAt" in candidate ? candidate.lastAttemptAt : candidate.checkedAt;
31009
+ const lastSuccessfulCheckAt = "lastSuccessfulCheckAt" in candidate ? candidate.lastSuccessfulCheckAt : candidate.checkedAt;
31010
+ if (latestVersion !== null && latestVersion !== void 0 && typeof latestVersion !== "string")
31011
+ return null;
31012
+ if (lastAttemptAt !== null && lastAttemptAt !== void 0 && typeof lastAttemptAt !== "string")
31013
+ return null;
31014
+ if (lastSuccessfulCheckAt !== null && lastSuccessfulCheckAt !== void 0 && typeof lastSuccessfulCheckAt !== "string")
31015
+ return null;
31016
+ if ("lastError" in candidate && candidate.lastError !== null && typeof candidate.lastError !== "string")
31017
+ return null;
31018
+ if ("checkSucceeded" in candidate && typeof candidate.checkSucceeded !== "boolean")
31019
+ return null;
31020
+ if ("currentVersion" in candidate && candidate.currentVersion !== null && typeof candidate.currentVersion !== "string")
31021
+ return null;
31022
+ if ("currentVersionDeprecation" in candidate && candidate.currentVersionDeprecation !== null && typeof candidate.currentVersionDeprecation !== "string")
31023
+ return null;
31024
+ const normalizedLatestVersion = latestVersion ?? null;
31025
+ const normalizedLastAttemptAt = lastAttemptAt ?? null;
31026
+ const normalizedLastSuccessfulCheckAt = lastSuccessfulCheckAt ?? null;
31027
+ const checkSucceeded = typeof candidate.checkSucceeded === "boolean" ? candidate.checkSucceeded : normalizedLatestVersion !== null;
31028
+ return {
31029
+ currentVersion: typeof candidate.currentVersion === "string" ? candidate.currentVersion : null,
31030
+ latestVersion: normalizedLatestVersion,
31031
+ lastAttemptAt: normalizedLastAttemptAt,
31032
+ lastSuccessfulCheckAt: normalizedLastSuccessfulCheckAt,
31033
+ lastError: typeof candidate.lastError === "string" ? candidate.lastError : null,
31034
+ checkSucceeded,
31035
+ currentVersionDeprecation: typeof candidate.currentVersionDeprecation === "string" ? candidate.currentVersionDeprecation : null
31036
+ };
31037
+ }
31038
+ function readStoredUpdateCheck(updateCheckPath, currentVersion) {
31039
+ try {
31040
+ const targetPath = getUpdateCheckPath(updateCheckPath, currentVersion);
31041
+ if (!fs8.existsSync(targetPath))
31042
+ return null;
31043
+ return parseStoredUpdateCheck(JSON.parse(fs8.readFileSync(targetPath, "utf8")));
31044
+ } catch {
31045
+ return null;
31046
+ }
31047
+ }
31048
+ function getLastUpdateCheck(currentVersion, options = {}) {
31049
+ const stored = readStoredUpdateCheck(options.updateCheckPath, currentVersion);
31050
+ if (!stored)
31051
+ return null;
31052
+ return buildResult(currentVersion, stored, "cache", options.now ?? /* @__PURE__ */ new Date());
31053
+ }
31054
+
31055
+ // dist/core/update-notice.js
31056
+ import fs9 from "fs";
31057
+ import path8 from "path";
31058
+ var UP_TO_DATE_REFRESH_MS = 60 * 60 * 1e3;
31059
+ var UPGRADE_AVAILABLE_REFRESH_MS = 12 * 60 * 60 * 1e3;
31060
+ var ANSWER_VALID_MS = 24 * 60 * 60 * 1e3;
31061
+ var SNOOZE_LEVEL_MS = [24 * 60 * 60 * 1e3, 48 * 60 * 60 * 1e3, 7 * 24 * 60 * 60 * 1e3];
31062
+ var SNOOZE_FILE = "update-snooze.json";
31063
+ var JUST_UPGRADED_FILE = "just-upgraded.json";
31064
+ function isStrictlyOlder(a, b) {
31065
+ const parse3 = (v) => {
31066
+ const [main2, ...rest] = String(v).split(/[-+]/);
31067
+ const nums = main2.split(".").map((s) => Number.parseInt(s, 10));
31068
+ return { nums, tail: rest.join("-") };
31069
+ };
31070
+ const pa = parse3(a);
31071
+ const pb = parse3(b);
31072
+ const len = Math.max(pa.nums.length, pb.nums.length);
31073
+ for (let i = 0; i < len; i++) {
31074
+ const ai = Number.isFinite(pa.nums[i]) ? pa.nums[i] : 0;
31075
+ const bi = Number.isFinite(pb.nums[i]) ? pb.nums[i] : 0;
31076
+ if (ai !== bi)
31077
+ return ai < bi;
31078
+ }
31079
+ if (pa.tail && !pb.tail)
31080
+ return true;
31081
+ if (!pa.tail && pb.tail)
31082
+ return false;
31083
+ return pa.tail < pb.tail;
31084
+ }
31085
+ function parseIso(value) {
31086
+ if (typeof value !== "string")
31087
+ return null;
31088
+ const t = Date.parse(value);
31089
+ return Number.isNaN(t) ? null : t;
31090
+ }
31091
+ function readJson(file2) {
31092
+ try {
31093
+ if (!fs9.existsSync(file2))
31094
+ return null;
31095
+ const parsed = JSON.parse(fs9.readFileSync(file2, "utf8"));
31096
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
31097
+ } catch {
31098
+ return null;
31099
+ }
31100
+ }
31101
+ function readSnooze(dir) {
31102
+ const raw = readJson(path8.join(dir, SNOOZE_FILE));
31103
+ if (!raw)
31104
+ return null;
31105
+ const { target, level, since } = raw;
31106
+ if (typeof target !== "string" || !target)
31107
+ return null;
31108
+ if (typeof level !== "number" || !Number.isInteger(level) || level < 1)
31109
+ return null;
31110
+ if (parseIso(since) === null)
31111
+ return null;
31112
+ return { target, level, since };
31113
+ }
31114
+ function snoozeExpiresAt(state) {
31115
+ const since = parseIso(state.since) ?? 0;
31116
+ const duration3 = SNOOZE_LEVEL_MS[Math.min(state.level, SNOOZE_LEVEL_MS.length) - 1];
31117
+ return since + duration3;
31118
+ }
31119
+ function readJustUpgradedMarker(dir) {
31120
+ const raw = readJson(path8.join(dir, JUST_UPGRADED_FILE));
31121
+ if (!raw)
31122
+ return null;
31123
+ const { from, to, at } = raw;
31124
+ if (typeof from !== "string" || !from || typeof to !== "string" || !to)
31125
+ return null;
31126
+ return { from, to, at: typeof at === "string" ? at : "" };
31127
+ }
31128
+ function clearJustUpgradedMarker(dir) {
31129
+ try {
31130
+ fs9.unlinkSync(path8.join(dir, JUST_UPGRADED_FILE));
31131
+ } catch {
31132
+ }
31133
+ }
31134
+ function claimJustUpgradedMarker(dir) {
31135
+ const file2 = path8.join(dir, JUST_UPGRADED_FILE);
31136
+ const taken = `${file2}.claimed-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
31137
+ try {
31138
+ fs9.renameSync(file2, taken);
31139
+ } catch {
31140
+ return null;
31141
+ }
31142
+ const raw = readJson(taken);
31143
+ try {
31144
+ fs9.unlinkSync(taken);
31145
+ } catch {
31146
+ }
31147
+ if (!raw)
31148
+ return null;
31149
+ const { from, to, at } = raw;
31150
+ if (typeof from !== "string" || !from || typeof to !== "string" || !to)
31151
+ return null;
31152
+ return { from, to, at: typeof at === "string" ? at : "" };
31153
+ }
31154
+ function boundedReason(raw) {
31155
+ const oneLine = redactUserPaths(raw).replace(/[\u0000-\u001f\u007f]+/g, " ").replace(/\s+/g, " ").trim();
31156
+ return oneLine.length > 160 ? `${oneLine.slice(0, 157)}...` : oneLine;
31157
+ }
31158
+ function answerIsCurrent(currentVersion, cache, now) {
31159
+ if (!cache || cache.currentVersion !== currentVersion)
31160
+ return false;
31161
+ if (typeof cache.latestVersion !== "string" || !cache.latestVersion)
31162
+ return false;
31163
+ const successAt = parseIso(cache.lastSuccessfulCheckAt);
31164
+ if (successAt === null)
31165
+ return false;
31166
+ return now.getTime() - successAt <= ANSWER_VALID_MS;
31167
+ }
31168
+ function shouldRefreshUpdateCache(currentVersion, cache, now = /* @__PURE__ */ new Date()) {
31169
+ if (!answerIsCurrent(currentVersion, cache, now))
31170
+ return true;
31171
+ const successAt = parseIso(cache.lastSuccessfulCheckAt);
31172
+ const age = now.getTime() - successAt;
31173
+ const upgrade = isStrictlyOlder(currentVersion, cache.latestVersion);
31174
+ return age > (upgrade ? UPGRADE_AVAILABLE_REFRESH_MS : UP_TO_DATE_REFRESH_MS);
31175
+ }
31176
+ function resolveUpdateNotice(input) {
31177
+ const { dir, currentVersion, cache } = input;
31178
+ const now = input.now ?? /* @__PURE__ */ new Date();
31179
+ if (input.updateCheckEnabled === false)
31180
+ return { kind: "DISABLED", currentVersion };
31181
+ const marker = readJustUpgradedMarker(dir);
31182
+ if (marker) {
31183
+ if (marker.to === currentVersion) {
31184
+ return { kind: "JUST_UPGRADED", currentVersion, from: marker.from, to: marker.to };
31185
+ }
31186
+ clearJustUpgradedMarker(dir);
31187
+ }
31188
+ if (!answerIsCurrent(currentVersion, cache, now)) {
31189
+ let reason = "no update check has completed yet";
31190
+ let attempted = false;
31191
+ if (cache && cache.currentVersion === currentVersion) {
31192
+ attempted = parseIso(cache.lastSuccessfulCheckAt) !== null || typeof cache.lastError === "string" && cache.lastError.length > 0;
31193
+ if (typeof cache.lastError === "string" && cache.lastError)
31194
+ reason = boundedReason(cache.lastError);
31195
+ else if (parseIso(cache.lastSuccessfulCheckAt) !== null)
31196
+ reason = "the last successful check is more than a day old";
31197
+ }
31198
+ return { kind: "CHECK_FAILED", currentVersion, reason, attempted };
31199
+ }
31200
+ const latestVersion = cache.latestVersion;
31201
+ if (!isStrictlyOlder(currentVersion, latestVersion)) {
31202
+ return { kind: "UP_TO_DATE", currentVersion, latestVersion };
31203
+ }
31204
+ const snooze = readSnooze(dir);
31205
+ if (snooze && snooze.target === latestVersion) {
31206
+ const until = snoozeExpiresAt(snooze);
31207
+ if (now.getTime() < until) {
31208
+ return { kind: "SNOOZED", currentVersion, latestVersion, until: new Date(until).toISOString(), level: snooze.level };
31209
+ }
31210
+ }
31211
+ return { kind: "UPGRADE_AVAILABLE", currentVersion, latestVersion };
31212
+ }
31213
+
31214
+ // dist/core/update-entrypoint.js
31215
+ var RECENT_HOOK_NOTICE_MS = 10 * 60 * 1e3;
31216
+ var CLI_NOTICE_THROTTLE_MS = 24 * 60 * 60 * 1e3;
31217
+ var FRESH_CHECK_THROTTLE_MS = 5 * 60 * 1e3;
31218
+ function formatUpdateNoticeLine(notice) {
31219
+ switch (notice.kind) {
31220
+ case "UPGRADE_AVAILABLE":
31221
+ return `[memesh update] ${notice.latestVersion} is available (you are on ${notice.currentVersion}). Run \`memesh update\`, or reply \u201CNot now\u201D / \u201CNever ask again\u201D in a hooked session.`;
31222
+ case "JUST_UPGRADED":
31223
+ return `[memesh update] Upgraded ${notice.from} \u2192 ${notice.to}. Processes started before the upgrade keep running ${notice.from} until they restart.`;
31224
+ case "CHECK_FAILED":
31225
+ return `[memesh update] Could not confirm whether an update exists (${notice.reason}). Status is unknown, not current \u2014 \`memesh status\` retries.`;
31226
+ default:
31227
+ return null;
31228
+ }
31229
+ }
31230
+ function recentHookNoticeExists(dir, currentVersion, latestVersion, now = /* @__PURE__ */ new Date()) {
31231
+ const claims = path9.join(dir, "update-prompt-claims");
31232
+ let names;
31233
+ try {
31234
+ names = fs10.readdirSync(claims);
31235
+ } catch {
31236
+ return false;
31237
+ }
31238
+ for (const name of names) {
31239
+ if (!name.endsWith(".json"))
31240
+ continue;
31241
+ const file2 = path9.join(claims, name);
31242
+ let fd = null;
31243
+ try {
31244
+ fd = fs10.openSync(file2, "r");
31245
+ const stat = fs10.fstatSync(fd);
31246
+ if (now.getTime() - stat.mtimeMs > RECENT_HOOK_NOTICE_MS)
31247
+ continue;
31248
+ const value = JSON.parse(fs10.readFileSync(fd, "utf8"));
31249
+ if (value.currentVersion === currentVersion && (latestVersion === null || value.latestVersion === latestVersion))
31250
+ return true;
31251
+ } catch {
31252
+ } finally {
31253
+ if (fd !== null)
31254
+ try {
31255
+ fs10.closeSync(fd);
31256
+ } catch {
31257
+ }
31258
+ }
31259
+ }
31260
+ return false;
31261
+ }
31262
+ function updateCheckEnabledIn(dir) {
31263
+ try {
31264
+ const raw = JSON.parse(fs10.readFileSync(path9.join(dir, "config.json"), "utf8"));
31265
+ return raw.updateCheck !== false;
31266
+ } catch {
31267
+ return true;
31268
+ }
31269
+ }
31270
+ function cliThrottled(dir, currentVersion, now) {
31271
+ const tag = /^[0-9A-Za-z.+-]+$/.test(currentVersion) ? currentVersion : "unknown";
31272
+ const marker = path9.join(dir, `last-cli-update-notice.${tag}.lock`);
31273
+ let fd = null;
31274
+ try {
31275
+ try {
31276
+ fd = fs10.openSync(marker, "r+");
31277
+ } catch (err) {
31278
+ const code = err.code;
31279
+ if (code !== "ENOENT") {
31280
+ return true;
31281
+ }
31282
+ fs10.mkdirSync(dir, { recursive: true, mode: 448 });
31283
+ try {
31284
+ fd = fs10.openSync(marker, "wx", 384);
31285
+ } catch (raceErr) {
31286
+ if (raceErr.code === "EEXIST")
31287
+ return true;
31288
+ throw raceErr;
31289
+ }
31290
+ fs10.writeSync(fd, String(now.getTime()));
31291
+ return false;
31292
+ }
31293
+ const stat = fs10.fstatSync(fd);
31294
+ if (now.getTime() - stat.mtimeMs < CLI_NOTICE_THROTTLE_MS)
31295
+ return true;
31296
+ fs10.ftruncateSync(fd, 0);
31297
+ fs10.writeSync(fd, String(now.getTime()), 0);
31298
+ try {
31299
+ fs10.fchmodSync(fd, 384);
31300
+ } catch {
31301
+ }
31302
+ return false;
31303
+ } catch {
31304
+ return true;
31305
+ } finally {
31306
+ if (fd !== null)
31307
+ try {
31308
+ fs10.closeSync(fd);
31309
+ } catch {
31310
+ }
31311
+ }
31312
+ }
31313
+ function spawnCacheRefresh(dir, currentVersion, now) {
31314
+ try {
31315
+ const cliPath = fileURLToPath(new URL("../transports/cli/cli.js", import.meta.url));
31316
+ if (!fs10.existsSync(cliPath))
31317
+ return false;
31318
+ const tag = /^[0-9A-Za-z.+-]+$/.test(currentVersion) ? currentVersion : "unknown";
31319
+ const marker = path9.join(dir, `last-fresh-refresh.${tag}.lock`);
31320
+ try {
31321
+ if (now.getTime() - fs10.statSync(marker).mtimeMs < FRESH_CHECK_THROTTLE_MS)
31322
+ return false;
31323
+ fs10.unlinkSync(marker);
31324
+ } catch {
31325
+ }
31326
+ fs10.mkdirSync(dir, { recursive: true, mode: 448 });
31327
+ try {
31328
+ const fd = fs10.openSync(marker, "wx", 384);
31329
+ try {
31330
+ fs10.writeSync(fd, `${process.pid}-${now.getTime()}`);
31331
+ } finally {
31332
+ fs10.closeSync(fd);
31333
+ }
31334
+ } catch {
31335
+ return false;
31336
+ }
31337
+ const child = spawn(process.execPath, [cliPath, "status"], {
31338
+ detached: true,
31339
+ stdio: "ignore",
31340
+ env: { ...process.env },
31341
+ windowsHide: true
31342
+ });
31343
+ child.unref();
31344
+ return true;
31345
+ } catch {
31346
+ return false;
31347
+ }
31348
+ }
31349
+ var MCP_PENDING_RETRY_MS = 60 * 1e3;
31350
+ var ProcessMemo = class {
31351
+ set;
31352
+ key;
31353
+ now;
31354
+ retryPrefix;
31355
+ constructor(set2, key, now) {
31356
+ this.set = set2;
31357
+ this.key = key;
31358
+ this.now = now;
31359
+ this.retryPrefix = `${key}#retry:`;
31360
+ }
31361
+ decided() {
31362
+ if (!this.set)
31363
+ return false;
31364
+ if (this.set.has(this.key))
31365
+ return true;
31366
+ for (const entry of this.set) {
31367
+ if (entry.startsWith(this.retryPrefix) && this.now.getTime() < Number(entry.slice(this.retryPrefix.length)))
31368
+ return true;
31369
+ }
31370
+ return false;
31371
+ }
31372
+ decide() {
31373
+ this.clearRetry();
31374
+ this.set?.add(this.key);
31375
+ }
31376
+ retryLater() {
31377
+ this.clearRetry();
31378
+ this.set?.add(`${this.retryPrefix}${this.now.getTime() + MCP_PENDING_RETRY_MS}`);
31379
+ }
31380
+ clearRetry() {
31381
+ if (!this.set)
31382
+ return;
31383
+ for (const entry of [...this.set])
31384
+ if (entry.startsWith(this.retryPrefix))
31385
+ this.set.delete(entry);
31386
+ }
31387
+ };
31388
+ function updateNoticeForEntryPoint(input) {
31389
+ try {
31390
+ const now = input.now ?? /* @__PURE__ */ new Date();
31391
+ const memo = input.entryPoint === "mcp" ? new ProcessMemo(input.processOnce, input.currentVersion, now) : null;
31392
+ if (memo?.decided())
31393
+ return null;
31394
+ const dir = input.dir ?? memeshDir();
31395
+ const updateCheckEnabled = input.updateCheckEnabled ?? updateCheckEnabledIn(dir);
31396
+ const tag = /^[0-9A-Za-z.+-]+$/.test(input.currentVersion) ? input.currentVersion : "unknown";
31397
+ const cache = getLastUpdateCheck(input.currentVersion, { now, updateCheckPath: path9.join(dir, `update-check.${tag}.json`) });
31398
+ if (updateCheckEnabled && shouldRefreshUpdateCache(input.currentVersion, cache, now)) {
31399
+ (input.refresh ?? spawnCacheRefresh)(dir, input.currentVersion, now);
31400
+ }
31401
+ const notice = resolveUpdateNotice({ dir, currentVersion: input.currentVersion, cache, now, updateCheckEnabled });
31402
+ if (notice.kind === "CHECK_FAILED" && !notice.attempted) {
31403
+ memo?.retryLater();
31404
+ return null;
31405
+ }
31406
+ memo?.decide();
31407
+ if (notice.kind === "DISABLED" || notice.kind === "SNOOZED" || notice.kind === "UP_TO_DATE")
31408
+ return null;
31409
+ if (input.entryPoint === "mcp" && recentHookNoticeExists(dir, notice.currentVersion, notice.kind === "UPGRADE_AVAILABLE" ? notice.latestVersion : null, now)) {
31410
+ return null;
31411
+ }
31412
+ if (input.entryPoint === "cli" && cliThrottled(dir, input.currentVersion, now))
31413
+ return null;
31414
+ if (notice.kind === "JUST_UPGRADED") {
31415
+ const claimed = claimJustUpgradedMarker(dir);
31416
+ if (!claimed)
31417
+ return null;
31418
+ return formatUpdateNoticeLine({ ...notice, from: claimed.from, to: claimed.to });
31419
+ }
31420
+ return formatUpdateNoticeLine(notice);
31421
+ } catch {
31422
+ return null;
31423
+ }
31424
+ }
31425
+
30145
31426
  // dist/transports/mcp/handlers.js
30146
31427
  function resolveTranscriptWorkspace(project, rootUris) {
30147
31428
  if (!rootUris)
@@ -30152,8 +31433,8 @@ function resolveTranscriptWorkspace(project, rootUris) {
30152
31433
  const parsed = new URL(uri);
30153
31434
  if (parsed.protocol !== "file:")
30154
31435
  continue;
30155
- const root = fs7.realpathSync(fileURLToPath(parsed));
30156
- if (!fs7.statSync(root).isDirectory() || getProjectName(root) !== project)
31436
+ const root = fs11.realpathSync(fileURLToPath2(parsed));
31437
+ if (!fs11.statSync(root).isDirectory() || getProjectName(root) !== project)
30157
31438
  continue;
30158
31439
  matches.add(root);
30159
31440
  } catch {
@@ -30173,17 +31454,25 @@ var TOOL_DEFINITIONS = [
30173
31454
  },
30174
31455
  {
30175
31456
  name: "remember",
30176
- description: "Store knowledge as an entity with observations, tags, and relations. Use this to remember decisions, patterns, lessons learned, and important context.",
31457
+ description: "Store knowledge as an entity with observations, tags, and relations. Use this to remember decisions, patterns, lessons learned, and important context. Quickest form: pass only `note` (free text) and the server derives title, observations and name; the response echoes what it derived. To correct a memory, call again with its `name` and `replace: true` \u2014 the memory keeps the `type` it has unless you pass a different one \u2014 and the old content moves to metadata.replaced_history instead of staying next to the fix.",
30177
31458
  inputSchema: {
30178
31459
  type: "object",
30179
31460
  properties: {
30180
31461
  name: {
30181
31462
  type: "string",
30182
- description: 'Unique entity name (e.g., "auth-decision", "jwt-pattern"). Reusing a name appends observations and dedupes tags instead of replacing the entity.'
31463
+ description: 'Unique entity name (e.g., "auth-decision", "jwt-pattern"). Reusing a name appends observations and dedupes tags instead of replacing the entity (unless `replace` is true). Required unless `note` is given, in which case it is derived from the text (same text \u2192 same name).'
30183
31464
  },
30184
31465
  type: {
30185
31466
  type: "string",
30186
- description: 'Entity type (e.g., "decision", "pattern", "lesson", "commit")'
31467
+ description: 'Entity type (e.g., "decision", "pattern", "lesson", "commit"). Required unless `note` is given (it then defaults to "note"), or `replace: true` is sent with the `name` of a memory that exists \u2014 that call keeps the stored type. Passing one on a `replace` reclassifies the memory.'
31468
+ },
31469
+ note: {
31470
+ type: "string",
31471
+ description: "Free text, instead of title + observations: the first line becomes the title and each following paragraph an observation. Cannot be combined with `title` or `observations`. Control characters and credential-shaped strings are removed before storing."
31472
+ },
31473
+ replace: {
31474
+ type: "boolean",
31475
+ description: "Rewrite the memory named by `name` instead of appending to it: its observations are replaced (and its tags when `tags` is given, its title when `title` or `note` is given). The previous version is kept in metadata.replaced_history with the time it was replaced. Default false (append)."
30187
31476
  },
30188
31477
  title: {
30189
31478
  type: "string",
@@ -30221,8 +31510,12 @@ var TOOL_DEFINITIONS = [
30221
31510
  description: 'Namespace for organizing the entity. Omit it to leave an existing memory where it is \u2014 supplying it MOVES a memory that already exists, and it drops out of every other scoped view. New memories default to "personal".'
30222
31511
  }
30223
31512
  },
30224
- required: ["name", "type"],
30225
- additionalProperties: false
31513
+ additionalProperties: false,
31514
+ anyOf: [
31515
+ { required: ["note"] },
31516
+ { required: ["name", "type"] },
31517
+ { required: ["name", "replace"], properties: { replace: { const: true } } }
31518
+ ]
30226
31519
  }
30227
31520
  },
30228
31521
  {
@@ -30291,7 +31584,7 @@ var TOOL_DEFINITIONS = [
30291
31584
  },
30292
31585
  {
30293
31586
  name: "import",
30294
- description: "Import memories from a JSON export snapshot. Supports skip, append, or overwrite strategies for handling existing entities.",
31587
+ description: "Import memories from a JSON export snapshot. Supports skip, append, or overwrite strategies for handling existing entities. A local memory that was forgotten (archived) stays archived unless restore_archived is true; the result reports how many were left as they were in kept_archived.",
30295
31588
  inputSchema: {
30296
31589
  type: "object",
30297
31590
  properties: {
@@ -30301,6 +31594,10 @@ var TOOL_DEFINITIONS = [
30301
31594
  type: "string",
30302
31595
  enum: ["skip", "overwrite", "append"],
30303
31596
  description: "Required. How to handle an entity that already exists: skip = leave it untouched, append = add these observations to it, overwrite = REPLACE its observations and tags (the old ones are deleted, not archived \u2014 this cannot be undone)"
31597
+ },
31598
+ restore_archived: {
31599
+ type: "boolean",
31600
+ description: "Optional, default false. With append or overwrite, a local entity that is archived (forgotten) is left untouched and counted in kept_archived. Set true to bring it back to active and merge or overwrite it like any other; it requires merge_strategy append or overwrite (an error with skip). Only set it when the user asked for archived memories to return."
30304
31601
  }
30305
31602
  },
30306
31603
  required: ["data", "merge_strategy"],
@@ -30329,7 +31626,7 @@ var TOOL_DEFINITIONS = [
30329
31626
  },
30330
31627
  {
30331
31628
  name: "task_state",
30332
- description: 'Read or update where the work stands on this project: the goal, what is next, what is blocked, what was just finished. Call with no arguments to read it. Injected at the start of the next session, so record ONLY what the user actually stated \u2014 never infer a goal or a next step from files edited or commands run, and leave a field out if it was not said. Pass an empty string to clear a field (e.g. blocked: "" once a blocker is resolved).',
31629
+ description: `Read or update where the work stands on this project: the goal, what is next, what is blocked, what was just finished. Call with no arguments to read it. Included in the next session's briefing at the default level, so record ONLY what the user actually stated \u2014 never infer a goal or a next step from files edited or commands run, and leave a field out if it was not said. Pass an empty string to clear a field (e.g. blocked: "" once a blocker is resolved).`,
30333
31630
  inputSchema: {
30334
31631
  type: "object",
30335
31632
  properties: {
@@ -30347,7 +31644,7 @@ var TOOL_DEFINITIONS = [
30347
31644
  },
30348
31645
  {
30349
31646
  name: "briefing",
30350
- description: "The work topology for a project, assembled and ready to use: where the work was left off (goal / next / blocked / done), decisions and direction, lessons not to repeat, what is known, and recent activity \u2014 the same block Claude Code receives at session start. Call once at the START of a session to load project context; use recall for specific questions after that. Content is wrapped as untrusted background data.",
31647
+ description: "The work topology for a project, assembled and ready to use: where the work was left off (goal / next / blocked / done), decisions and direction, lessons not to repeat, what is known, recent activity, and a capped index of the project\u2019s durable memories (one line each, newest first, with [mem:id] handles; structured counts and token cost in `index`) \u2014 the same memory block Claude Code receives at session start (at `full`, the SessionStart hook additionally appends a work-package notice; that notice is a host-agent instruction, not memory, and is never part of this tool\u2019s output). How much of this is assembled follows the `briefing` setting (`minimal`, `standard` \u2014 the default, described above \u2014 or `full`, which additionally includes memories from other projects and global memory); at `minimal` it is only this project\u2019s live repository state, decisions, lessons, known facts and recent activity, with no task state and no index, and may be empty. Call once at the START of a session to load project context; use recall for specific questions after that. Content is wrapped as untrusted background data.",
30351
31648
  inputSchema: {
30352
31649
  type: "object",
30353
31650
  properties: {
@@ -30466,8 +31763,8 @@ function fail(message) {
30466
31763
  return { content: [{ type: "text", text: message }], isError: true };
30467
31764
  }
30468
31765
  function formatIssue(issue2) {
30469
- const path7 = issue2.path.join(".");
30470
- return path7 ? `${path7}: ${issue2.message}` : issue2.message;
31766
+ const path11 = issue2.path.join(".");
31767
+ return path11 ? `${path11}: ${issue2.message}` : issue2.message;
30471
31768
  }
30472
31769
  function stripNullProps(value) {
30473
31770
  if (Array.isArray(value))
@@ -30506,7 +31803,26 @@ function parseOrFail(schema, args) {
30506
31803
  function normalizeClientHost(name) {
30507
31804
  return (name ?? "").replace(/[\u0000-\u001F\u007F]/g, "").trim().slice(0, 64) || "mcp";
30508
31805
  }
31806
+ var packageVersion = (() => {
31807
+ try {
31808
+ return JSON.parse(fs11.readFileSync(new URL("../../../package.json", import.meta.url), "utf8")).version ?? "0.0.0";
31809
+ } catch {
31810
+ return "0.0.0";
31811
+ }
31812
+ })();
31813
+ var firstCallNoticeOnce = /* @__PURE__ */ new Set();
31814
+ function withFirstCallNotice(result) {
31815
+ if (result.isError)
31816
+ return result;
31817
+ const line = updateNoticeForEntryPoint({ currentVersion: packageVersion, entryPoint: "mcp", processOnce: firstCallNoticeOnce });
31818
+ if (!line)
31819
+ return result;
31820
+ return { ...result, content: [...result.content, { type: "text", text: line }] };
31821
+ }
30509
31822
  async function handleTool(name, args, sourceHost, signal, requestContext = {}) {
31823
+ return withFirstCallNotice(await handleToolInner(name, args, sourceHost, signal, requestContext));
31824
+ }
31825
+ async function handleToolInner(name, args, sourceHost, signal, requestContext = {}) {
30510
31826
  try {
30511
31827
  if (name === "work_package") {
30512
31828
  const parsed = parseOrFail(WorkPackageSchema, args);
@@ -30666,9 +31982,9 @@ async function handleTool(name, args, sourceHost, signal, requestContext = {}) {
30666
31982
  }
30667
31983
 
30668
31984
  // dist/mcp/server.js
30669
- var packageJsonPath = path6.resolve(path6.dirname(fileURLToPath2(import.meta.url)), "../../package.json");
30670
- var packageVersion = JSON.parse(fs8.readFileSync(packageJsonPath, "utf8")).version ?? "0.0.0";
30671
- var server = new Server({ name: "memesh", version: packageVersion }, { capabilities: { tools: {} } });
31985
+ var packageJsonPath = path10.resolve(path10.dirname(fileURLToPath3(import.meta.url)), "../../package.json");
31986
+ var packageVersion2 = JSON.parse(fs12.readFileSync(packageJsonPath, "utf8")).version ?? "0.0.0";
31987
+ var server = new Server({ name: "memesh", version: packageVersion2 }, { capabilities: { tools: {} } });
30672
31988
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
30673
31989
  tools: TOOL_DEFINITIONS.map((t) => ({
30674
31990
  name: t.name,