@pcircle/memesh 4.9.4 → 4.10.1

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 (129) 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 +35 -4
  5. package/README.de.md +3 -3
  6. package/README.md +3 -3
  7. package/README.zh-TW.md +3 -3
  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/briefing-index.d.ts +30 -0
  13. package/dist/core/briefing-index.d.ts.map +1 -0
  14. package/dist/core/briefing-index.js +140 -0
  15. package/dist/core/briefing-index.js.map +1 -0
  16. package/dist/core/briefing.d.ts +4 -0
  17. package/dist/core/briefing.d.ts.map +1 -1
  18. package/dist/core/briefing.js +42 -4
  19. package/dist/core/briefing.js.map +1 -1
  20. package/dist/core/capture-liveness.d.ts +136 -0
  21. package/dist/core/capture-liveness.d.ts.map +1 -0
  22. package/dist/core/capture-liveness.js +447 -0
  23. package/dist/core/capture-liveness.js.map +1 -0
  24. package/dist/core/config.d.ts +1 -0
  25. package/dist/core/config.d.ts.map +1 -1
  26. package/dist/core/config.js +3 -1
  27. package/dist/core/config.js.map +1 -1
  28. package/dist/core/delegation.d.ts +47 -0
  29. package/dist/core/delegation.d.ts.map +1 -0
  30. package/dist/core/delegation.js +174 -0
  31. package/dist/core/delegation.js.map +1 -0
  32. package/dist/core/doctor-fixes.d.ts +1 -0
  33. package/dist/core/doctor-fixes.d.ts.map +1 -1
  34. package/dist/core/doctor-fixes.js +21 -0
  35. package/dist/core/doctor-fixes.js.map +1 -1
  36. package/dist/core/doctor.d.ts +8 -0
  37. package/dist/core/doctor.d.ts.map +1 -1
  38. package/dist/core/doctor.js +148 -3
  39. package/dist/core/doctor.js.map +1 -1
  40. package/dist/core/note-derive.d.ts +14 -0
  41. package/dist/core/note-derive.d.ts.map +1 -0
  42. package/dist/core/note-derive.js +66 -0
  43. package/dist/core/note-derive.js.map +1 -0
  44. package/dist/core/note-ingest.d.ts +34 -0
  45. package/dist/core/note-ingest.d.ts.map +1 -0
  46. package/dist/core/note-ingest.js +445 -0
  47. package/dist/core/note-ingest.js.map +1 -0
  48. package/dist/core/operations.d.ts +10 -1
  49. package/dist/core/operations.d.ts.map +1 -1
  50. package/dist/core/operations.js +129 -14
  51. package/dist/core/operations.js.map +1 -1
  52. package/dist/core/paths.d.ts.map +1 -1
  53. package/dist/core/paths.js.map +1 -1
  54. package/dist/core/schema-export.d.ts.map +1 -1
  55. package/dist/core/schema-export.js +10 -4
  56. package/dist/core/schema-export.js.map +1 -1
  57. package/dist/core/serializer.d.ts.map +1 -1
  58. package/dist/core/serializer.js +3 -0
  59. package/dist/core/serializer.js.map +1 -1
  60. package/dist/core/session-insight.d.ts +44 -0
  61. package/dist/core/session-insight.d.ts.map +1 -0
  62. package/dist/core/session-insight.js +216 -0
  63. package/dist/core/session-insight.js.map +1 -0
  64. package/dist/core/task-state-store.d.ts +4 -0
  65. package/dist/core/task-state-store.d.ts.map +1 -1
  66. package/dist/core/task-state-store.js +18 -5
  67. package/dist/core/task-state-store.js.map +1 -1
  68. package/dist/core/turn-signal.d.ts +22 -0
  69. package/dist/core/turn-signal.d.ts.map +1 -0
  70. package/dist/core/turn-signal.js +89 -0
  71. package/dist/core/turn-signal.js.map +1 -0
  72. package/dist/core/types.d.ts +12 -3
  73. package/dist/core/types.d.ts.map +1 -1
  74. package/dist/core/update-entrypoint.d.ts +19 -0
  75. package/dist/core/update-entrypoint.d.ts.map +1 -0
  76. package/dist/core/update-entrypoint.js +222 -0
  77. package/dist/core/update-entrypoint.js.map +1 -0
  78. package/dist/core/update-notice.d.ts +69 -0
  79. package/dist/core/update-notice.d.ts.map +1 -0
  80. package/dist/core/update-notice.js +188 -0
  81. package/dist/core/update-notice.js.map +1 -0
  82. package/dist/host-runtime/codex-session.d.ts +10 -0
  83. package/dist/host-runtime/codex-session.d.ts.map +1 -1
  84. package/dist/host-runtime/codex-session.js +1 -1
  85. package/dist/host-runtime/codex-session.js.map +1 -1
  86. package/dist/knowledge-graph.d.ts.map +1 -1
  87. package/dist/knowledge-graph.js +24 -3
  88. package/dist/knowledge-graph.js.map +1 -1
  89. package/dist/mcp/THIRD_PARTY_NOTICES.txt +2 -2
  90. package/dist/mcp/server.js +1079 -90
  91. package/dist/mcp/server.js.map +3 -3
  92. package/dist/skills-manifest.json +46 -26
  93. package/dist/transports/cli/cli.d.ts.map +1 -1
  94. package/dist/transports/cli/cli.js +7702 -5764
  95. package/dist/transports/cli/cli.js.map +3 -3
  96. package/dist/transports/http/server.d.ts.map +1 -1
  97. package/dist/transports/http/server.js +38 -27
  98. package/dist/transports/http/server.js.map +1 -1
  99. package/dist/transports/mcp/handlers.d.ts +25 -5
  100. package/dist/transports/mcp/handlers.d.ts.map +1 -1
  101. package/dist/transports/mcp/handlers.js +43 -5
  102. package/dist/transports/mcp/handlers.js.map +1 -1
  103. package/dist/transports/schemas.d.ts +4 -2
  104. package/dist/transports/schemas.d.ts.map +1 -1
  105. package/dist/transports/schemas.js +29 -3
  106. package/dist/transports/schemas.js.map +1 -1
  107. package/docs/platforms/README.md +1 -0
  108. package/docs/platforms/agent-messaging.md +22 -5
  109. package/package.json +14 -4
  110. package/scripts/hooks/_generated/briefing-index.js +147 -0
  111. package/scripts/hooks/_generated/capture-liveness.js +454 -0
  112. package/scripts/hooks/_generated/update-notice.js +195 -0
  113. package/scripts/hooks/_shared.js +302 -14
  114. package/scripts/hooks/_stop-notes.js +504 -0
  115. package/scripts/hooks/auto-update-runner.mjs +22 -0
  116. package/scripts/hooks/decision-nudge.js +32 -5
  117. package/scripts/hooks/guard-check.js +30 -3
  118. package/scripts/hooks/post-commit.js +329 -133
  119. package/scripts/hooks/pre-compact.js +17 -2
  120. package/scripts/hooks/pre-edit-recall.js +32 -1
  121. package/scripts/hooks/session-start.js +361 -78
  122. package/scripts/hooks/session-summary.js +217 -41
  123. package/scripts/hooks/user-prompt-intent.js +89 -19
  124. package/skills/memesh/SKILL.md +20 -8
  125. package/dist/cli/assets/d3.v7.min.js +0 -2
  126. package/dist/core/graph.d.ts +0 -26
  127. package/dist/core/graph.d.ts.map +0 -1
  128. package/dist/core/graph.js +0 -74
  129. 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(path10) {
3233
+ let input = path10;
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 [path10, query] = wsComponent.resourceName.split("?");
3486
+ wsComponent.path = path10 && path10 !== "/" ? path10 : void 0;
3487
3487
  wsComponent.query = query;
3488
3488
  wsComponent.resourceName = void 0;
3489
3489
  }
@@ -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, fs12, 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, fs12[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 fs11 from "fs";
6920
+ import path9 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, path10) {
7446
+ if (!path10)
7447
7447
  return obj;
7448
- return path7.reduce((acc, key) => acc?.[key], obj);
7448
+ return path10.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(path10, 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(path10);
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, path10 = []) => {
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 }, [...path10, ...issue2.path]));
8012
8012
  } else if (issue2.code === "invalid_key") {
8013
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
8013
+ processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
8014
8014
  } else if (issue2.code === "invalid_element") {
8015
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
8015
+ processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
8016
8016
  } else {
8017
- const fullpath = [...path7, ...issue2.path];
8017
+ const fullpath = [...path10, ...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, path10 = []) => {
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 }, [...path10, ...issue2.path]));
8049
8049
  } else if (issue2.code === "invalid_key") {
8050
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
8050
+ processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
8051
8051
  } else if (issue2.code === "invalid_element") {
8052
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
8052
+ processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
8053
8053
  } else {
8054
- const fullpath = [...path7, ...issue2.path];
8054
+ const fullpath = [...path10, ...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 path10 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
8087
+ for (const seg of path10) {
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 path10 = ref.slice(1).split("/").filter(Boolean);
21086
+ if (path10.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 (path10[0] === defsKey) {
21091
+ const key = path10[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(path10, options = {}) {
24823
+ super(path10, 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 fs10 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,6 +26994,62 @@ 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
 
@@ -26951,6 +27060,7 @@ function buildImportedMetadata(existingMetadata, args) {
26951
27060
  return {
26952
27061
  ...existingMetadata ?? {},
26953
27062
  ...bundledSafe,
27063
+ ...Array.isArray(existingMetadata?.forgotten_observation_hashes) ? { forgotten_observation_hashes: existingMetadata.forgotten_observation_hashes } : {},
26954
27064
  trust: "untrusted",
26955
27065
  provenance: {
26956
27066
  ...existingMetadata?.provenance ?? {},
@@ -27152,19 +27262,113 @@ function recallTagFilter(args) {
27152
27262
  function buildRelevanceMap(entities) {
27153
27263
  return new Map(entities.map((entity, index) => [entity.name, 1 - index / (entities.length + 1)]));
27154
27264
  }
27155
- function remember(args) {
27265
+ function remember(input) {
27156
27266
  const db2 = getDatabase();
27157
27267
  const kg = new KnowledgeGraph(db2);
27158
- return db2.transaction(() => rememberInTransaction(args, db2, kg)).immediate();
27268
+ const { args, derived, typeGiven } = resolveRememberInput(input);
27269
+ return db2.transaction(() => rememberInTransaction(args, derived, typeGiven, db2, kg)).immediate();
27270
+ }
27271
+ var REPLACED_HISTORY_MAX = 20;
27272
+ var REPLACED_HISTORY_MAX_BYTES = 64 * 1024;
27273
+ var jsonBytes = (v) => Buffer.byteLength(JSON.stringify(v), "utf8");
27274
+ function boundReplacedHistory(history) {
27275
+ let out = history.slice(-REPLACED_HISTORY_MAX);
27276
+ while (out.length > 1 && jsonBytes(out) > REPLACED_HISTORY_MAX_BYTES)
27277
+ out = out.slice(1);
27278
+ if (out.length === 1 && jsonBytes(out) > REPLACED_HISTORY_MAX_BYTES) {
27279
+ const only = out[0];
27280
+ const kept = [];
27281
+ const base = { ...only, observations: [], truncated: true };
27282
+ for (const obs of only.observations) {
27283
+ if (jsonBytes([{ ...base, observations: [...kept, obs] }]) > REPLACED_HISTORY_MAX_BYTES)
27284
+ break;
27285
+ kept.push(obs);
27286
+ }
27287
+ out = [{ ...base, observations: kept }];
27288
+ }
27289
+ return out;
27159
27290
  }
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,
27291
+ function summarizeReplacedHistory(entities) {
27292
+ for (const e of entities) {
27293
+ const history = e.metadata?.replaced_history;
27294
+ if (!Array.isArray(history))
27295
+ continue;
27296
+ const { replaced_history: _dropped, ...rest } = e.metadata;
27297
+ e.metadata = { ...rest, replaced_history_count: history.length };
27298
+ }
27299
+ return entities;
27300
+ }
27301
+ function resolveRememberInput(input) {
27302
+ if (input.note === void 0) {
27303
+ if (!input.name)
27304
+ throw new Error("remember needs `name` and `type`, or `note`");
27305
+ if (input.type === "")
27306
+ throw new Error("remember needs `name` and `type`, or `note`");
27307
+ if (input.type === void 0 && !input.replace)
27308
+ throw new Error("remember needs `name` and `type`, or `note`");
27309
+ return { args: input, typeGiven: input.type !== void 0 };
27310
+ }
27311
+ if (input.title !== void 0 || input.observations !== void 0) {
27312
+ throw new Error("`note` derives title and observations; do not also pass `title` or `observations`");
27313
+ }
27314
+ const derived = deriveNote(input.note);
27315
+ if (!derived)
27316
+ throw new Error("`note` is empty after removing control characters");
27317
+ if (input.replace && !input.name) {
27318
+ throw new Error("`replace` with `note` needs an explicit `name` (a derived name changes with the text)");
27319
+ }
27320
+ return {
27321
+ args: {
27322
+ ...input,
27323
+ name: input.name ?? derived.name,
27324
+ type: input.type ?? NOTE_DEFAULT_TYPE,
27325
+ title: derived.title,
27326
+ observations: derived.observations
27327
+ },
27328
+ derived,
27329
+ typeGiven: input.type !== void 0
27330
+ };
27331
+ }
27332
+ function rememberInTransaction(args, derived, typeGiven, db2, kg) {
27333
+ const existing = db2.prepare("SELECT id, namespace, type, title, status FROM entities WHERE name = ?").get(args.name);
27334
+ if (args.replace && existing && existing.status === "archived") {
27335
+ 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.`);
27336
+ }
27337
+ const entityType = args.type ?? existing?.type;
27338
+ if (entityType === void 0) {
27339
+ 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.`);
27340
+ }
27341
+ let replacedVersion;
27342
+ let retypedTo;
27343
+ let tags = args.tags;
27344
+ let title = args.title;
27345
+ let observations = args.observations;
27346
+ if (args.replace && existing) {
27347
+ const previousTags = db2.prepare("SELECT tag FROM tags WHERE entity_id = ? ORDER BY tag").all(existing.id).map((t) => t.tag);
27348
+ replacedVersion = {
27349
+ replaced_at: (/* @__PURE__ */ new Date()).toISOString(),
27350
+ title: existing.title,
27351
+ observations: db2.prepare("SELECT content FROM observations WHERE entity_id = ? ORDER BY id").all(existing.id).map((o) => o.content),
27352
+ tags: previousTags
27353
+ };
27354
+ kg.clearEntityData(args.name);
27355
+ if (typeGiven && entityType !== existing.type) {
27356
+ db2.prepare("UPDATE entities SET type = ? WHERE id = ?").run(entityType, existing.id);
27357
+ retypedTo = entityType;
27358
+ }
27359
+ if (tags === void 0)
27360
+ tags = previousTags;
27361
+ } else if (derived && existing) {
27362
+ title = void 0;
27363
+ const stored = new Set(db2.prepare("SELECT content FROM observations WHERE entity_id = ?").all(existing.id).map((o) => o.content));
27364
+ observations = observations?.filter((o) => !stored.has(o));
27365
+ }
27366
+ const entityId = kg.createEntity(args.name, entityType, {
27367
+ observations,
27368
+ tags,
27165
27369
  namespace: args.namespace,
27166
27370
  trustOverride: args.trustOverride,
27167
- title: args.title
27371
+ title
27168
27372
  });
27169
27373
  kg.updateEntityMetadata(args.name, (current) => buildLocalMetadata(current, {
27170
27374
  trust: args.trustOverride,
@@ -27173,6 +27377,13 @@ function rememberInTransaction(args, db2, kg) {
27173
27377
  ...args.provenanceOverride ?? {}
27174
27378
  }
27175
27379
  }));
27380
+ if (replacedVersion) {
27381
+ const version2 = replacedVersion;
27382
+ kg.updateEntityMetadata(args.name, (current) => {
27383
+ const history = Array.isArray(current.replaced_history) ? current.replaced_history : [];
27384
+ return { ...current, replaced_history: boundReplacedHistory([...history, version2]) };
27385
+ });
27386
+ }
27176
27387
  const relationsCreated = [];
27177
27388
  const relationErrors = [];
27178
27389
  if (args.relations) {
@@ -27196,29 +27407,32 @@ function rememberInTransaction(args, db2, kg) {
27196
27407
  }
27197
27408
  }
27198
27409
  }
27410
+ const storedTitle = db2.prepare("SELECT title FROM entities WHERE id = ?").get(entityId).title;
27199
27411
  return {
27200
27412
  stored: true,
27201
27413
  entityId,
27202
27414
  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,
27415
+ title: storedTitle,
27416
+ type: retypedTo ?? existing?.type ?? entityType,
27417
+ observations: observations?.length ?? 0,
27418
+ tags: tags?.length ?? 0,
27207
27419
  relations: relationsCreated.length,
27208
27420
  ...relationsCreated.length > 0 ? { relationsCreated } : {},
27209
27421
  ...existing && args.namespace !== void 0 && (existing.namespace ?? "personal") !== args.namespace ? { movedFromNamespace: existing.namespace ?? "personal" } : {},
27210
27422
  ...superseded.length > 0 ? { superseded } : {},
27211
- ...relationErrors.length > 0 ? { relationErrors } : {}
27423
+ ...relationErrors.length > 0 ? { relationErrors } : {},
27424
+ ...args.replace ? { replaced: replacedVersion !== void 0 } : {},
27425
+ ...derived ? { derived: { name: args.name, type: retypedTo ?? existing?.type ?? entityType, title: derived.title, observations: derived.observations } } : {}
27212
27426
  };
27213
27427
  }
27214
27428
  function searchAndScore(args) {
27215
27429
  const kg = new KnowledgeGraph(getDatabase());
27216
- const entities = kg.search(args.query, {
27430
+ const entities = summarizeReplacedHistory(kg.search(args.query, {
27217
27431
  tag: recallTagFilter(args),
27218
27432
  limit: args.limit,
27219
27433
  includeArchived: args.include_archived,
27220
27434
  namespace: args.namespace
27221
- });
27435
+ }));
27222
27436
  return {
27223
27437
  entities,
27224
27438
  relevanceMap: args.query ? buildRelevanceMap(entities) : /* @__PURE__ */ new Map()
@@ -27279,11 +27493,11 @@ function forget(args) {
27279
27493
  }
27280
27494
 
27281
27495
  // dist/core/dreamer.js
27282
- import { createHash as createHash5 } from "node:crypto";
27496
+ import { createHash as createHash7 } from "node:crypto";
27283
27497
 
27284
27498
  // dist/core/transcript-source.js
27285
27499
  import fs3 from "fs";
27286
- import { createHash as createHash3 } from "node:crypto";
27500
+ import { createHash as createHash5 } from "node:crypto";
27287
27501
  import path3 from "path";
27288
27502
  var MAX_TRANSCRIPT_SOURCE_BYTES = 8 * 1024 * 1024;
27289
27503
  var MAX_TRANSCRIPT_SCAN_BYTES = 16 * 1024 * 1024;
@@ -27326,7 +27540,7 @@ function readTranscriptSnapshotWithin(transcriptPath, expected, aggregateBytesRe
27326
27540
  if (after.dev !== before.dev || after.ino !== before.ino || after.size !== before.size || after.mtimeNs !== before.mtimeNs || after.ctimeNs !== before.ctimeNs) {
27327
27541
  return { snapshot: null, aggregateLimitExceeded: false };
27328
27542
  }
27329
- const contentHash = createHash3("sha256").update(bytes).digest("hex");
27543
+ const contentHash = createHash5("sha256").update(bytes).digest("hex");
27330
27544
  if (expected && contentHash !== expected.contentHash)
27331
27545
  return { snapshot: null, aggregateLimitExceeded: false };
27332
27546
  return { snapshot: { bytes, contentHash, ...identity }, aggregateLimitExceeded: false };
@@ -27516,7 +27730,7 @@ function parseVisibleConversation(transcript) {
27516
27730
  }
27517
27731
 
27518
27732
  // dist/core/product-improvements.js
27519
- import { createHash as createHash4 } from "node:crypto";
27733
+ import { createHash as createHash6 } from "node:crypto";
27520
27734
  var PRODUCT_IMPROVEMENT_KIND = "product_improvement";
27521
27735
  function clean(label, value, max) {
27522
27736
  const normalized = value.replace(/\s+/g, " ").trim();
@@ -27626,7 +27840,7 @@ function stageProductImprovement(db2, input) {
27626
27840
  success_criteria: successCriteria,
27627
27841
  priority
27628
27842
  };
27629
- const digest = createHash4("sha256").update(JSON.stringify(canonical)).digest("hex");
27843
+ const digest = createHash6("sha256").update(JSON.stringify(canonical)).digest("hex");
27630
27844
  const clusterKey = `product-improvement:${digest}`;
27631
27845
  const existing = db2.prepare(`SELECT id, project, source_ids, proposed_digest, status, reason, created_at, reviewed_at
27632
27846
  FROM dream_proposals
@@ -27832,7 +28046,7 @@ function executeWorkPackage(db2, input, context = {}) {
27832
28046
  const execute = () => {
27833
28047
  const project = input.action === "prepare" ? input.project : input.ref.project;
27834
28048
  const kind = input.action === "prepare" ? input.kind : input.ref.kind;
27835
- const hash2 = (value) => createHash5("sha256").update(JSON.stringify(value)).digest("hex");
28049
+ const hash2 = (value) => createHash7("sha256").update(JSON.stringify(value)).digest("hex");
27836
28050
  if (input.action !== "prepare") {
27837
28051
  const submitted = input.action === "submit" ? input.result : void 0;
27838
28052
  if (submitted && [submitted.name, ...submitted.observations, ...submitted.tags].some((s) => redactSecrets(s) !== s)) {
@@ -28220,21 +28434,34 @@ function taskStateLines(state, project, now = /* @__PURE__ */ new Date()) {
28220
28434
  function readState(name) {
28221
28435
  const row = getDatabase().prepare("SELECT metadata FROM entities WHERE name = ?").get(name);
28222
28436
  if (!row?.metadata)
28223
- return {};
28437
+ return { state: {}, corrupted: false };
28438
+ let parsed;
28224
28439
  try {
28225
- return parseTaskState(JSON.parse(row.metadata));
28440
+ parsed = JSON.parse(row.metadata);
28226
28441
  } catch {
28227
- return {};
28442
+ return { state: {}, corrupted: true };
28228
28443
  }
28444
+ return { state: parseTaskState(parsed), corrupted: false };
28229
28445
  }
28446
+ var TaskStateUnreadableError = class extends Error {
28447
+ project;
28448
+ constructor(project) {
28449
+ 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).`);
28450
+ this.project = project;
28451
+ this.name = "TaskStateUnreadableError";
28452
+ }
28453
+ };
28230
28454
  function getTaskState(project) {
28231
28455
  const resolved = project ?? getProjectName();
28232
- return { project: resolved, state: readState(taskStateName(resolved)) };
28456
+ const { state, corrupted } = readState(taskStateName(resolved));
28457
+ if (corrupted)
28458
+ throw new TaskStateUnreadableError(resolved);
28459
+ return { project: resolved, state };
28233
28460
  }
28234
28461
  function setTaskState(input) {
28235
28462
  const project = input.project ?? getProjectName();
28236
28463
  const name = taskStateName(project);
28237
- const previous = readState(name);
28464
+ const { state: previous } = readState(name);
28238
28465
  const { state, changed, observations } = mergeTaskState(previous, input.patch, (/* @__PURE__ */ new Date()).toISOString());
28239
28466
  if (changed.length === 0)
28240
28467
  return { project, state, changed };
@@ -28547,6 +28774,141 @@ function buildReferenceContext(memoryLines) {
28547
28774
  ].join("\n");
28548
28775
  }
28549
28776
 
28777
+ // dist/core/briefing-index.js
28778
+ var INDEX_MAX_LINES = 40;
28779
+ var INDEX_MAX_BYTES = 3072;
28780
+ var INDEX_STALE_DAYS = 180;
28781
+ var INDEX_LINE_MAX_CHARS = 120;
28782
+ var INDEX_SNIPPET_FETCH_CHARS = 4e3;
28783
+ var INDEX_CANDIDATE_CAP = 2e3;
28784
+ var INDEX_EXCLUDED_TYPES = [...EVIDENCE_LAYER_TYPES, "task-state"];
28785
+ function isIndexableType(type) {
28786
+ return !INDEX_EXCLUDED_TYPES.includes(type || "memory");
28787
+ }
28788
+ var DAY_MS = 24 * 60 * 60 * 1e3;
28789
+ function byteLength(text) {
28790
+ return new TextEncoder().encode(text).length;
28791
+ }
28792
+ function sectionBytes(lines) {
28793
+ return lines.reduce((sum, line) => sum + byteLength(line) + 1, 0);
28794
+ }
28795
+ function parseActivity(value) {
28796
+ if (!value)
28797
+ return Number.NaN;
28798
+ const iso = /[zZ]|[+-]\d\d:?\d\d$/.test(value) ? value : `${value.replace(" ", "T")}Z`;
28799
+ return Date.parse(iso);
28800
+ }
28801
+ function compareIndexCandidates(a, b) {
28802
+ const at = parseActivity(a.lastActivity);
28803
+ const bt = parseActivity(b.lastActivity);
28804
+ const av = Number.isNaN(at) ? -Infinity : at;
28805
+ const bv = Number.isNaN(bt) ? -Infinity : bt;
28806
+ if (av !== bv)
28807
+ return bv - av;
28808
+ return b.id - a.id;
28809
+ }
28810
+ function candidateIsAutoInjectable(metadata) {
28811
+ if (metadata == null)
28812
+ return true;
28813
+ if (typeof metadata === "string") {
28814
+ let parsed;
28815
+ try {
28816
+ parsed = JSON.parse(metadata);
28817
+ } catch {
28818
+ return false;
28819
+ }
28820
+ return parsed !== null && typeof parsed === "object" && isAutoInjectable(parsed);
28821
+ }
28822
+ return isAutoInjectable(metadata);
28823
+ }
28824
+ function redact(text) {
28825
+ if (!text)
28826
+ return "";
28827
+ return redactUserPaths(redactSecrets(String(text))).replace(/\s+/g, " ").trim();
28828
+ }
28829
+ function indexLine(candidate) {
28830
+ const title = redact(candidate.title);
28831
+ const snippet = redact(candidate.snippet);
28832
+ const repeats = title && snippet && snippet.toLowerCase().startsWith(title.replace(/…$/, "").toLowerCase());
28833
+ const text = title && snippet && !repeats ? `${title} \u2014 ${snippet}` : title || snippet;
28834
+ return topologyLine({ name: String(candidate.id), id: candidate.id, type: candidate.type || "memory", title: text || null }, INDEX_LINE_MAX_CHARS);
28835
+ }
28836
+ function indexHeading(projectName) {
28837
+ return `Index of durable memories for "${projectName}" (newest first):`;
28838
+ }
28839
+ function indexEmptyLine(projectName) {
28840
+ return `- No durable memories (decisions, lessons, patterns, references) for "${projectName}" yet.`;
28841
+ }
28842
+ function moreLine(n, truncated) {
28843
+ return `- ${n}${truncated ? "+" : ""} more \u2014 memesh recall --tag "project:\u2026"`;
28844
+ }
28845
+ function olderLine(n, truncated) {
28846
+ return `- ${n}${truncated ? "+" : ""} older memor${n === 1 ? "y" : "ies"} (no change in ${INDEX_STALE_DAYS} days) \u2014 recall to see`;
28847
+ }
28848
+ function footerLine(shown, bytes, tokens) {
28849
+ return `(index cost: ${shown} line${shown === 1 ? "" : "s"}, ${bytes} bytes \u2248 ${tokens} tokens; cap ${INDEX_MAX_LINES} lines / ${INDEX_MAX_BYTES} bytes)`;
28850
+ }
28851
+ function closeWithFooter(lines, shown) {
28852
+ const above = sectionBytes(lines);
28853
+ let footer = footerLine(shown, above, Math.ceil(above / 4));
28854
+ for (let step = 0; step < 8; step++) {
28855
+ const bytes = above + byteLength(footer) + 1;
28856
+ const tokens = Math.ceil(bytes / 4);
28857
+ const next = footerLine(shown, bytes, tokens);
28858
+ if (next === footer)
28859
+ return { lines: [...lines, footer], bytes, tokens };
28860
+ footer = next;
28861
+ }
28862
+ throw new Error("briefing index: the footer cost did not converge");
28863
+ }
28864
+ function buildBriefingIndex(candidates, projectName, now, options = {}) {
28865
+ const truncated = options.truncated === true;
28866
+ const cutoff = now - INDEX_STALE_DAYS * DAY_MS;
28867
+ const eligible = candidates.filter((c) => isIndexableType(c.type) && candidateIsAutoInjectable(c.metadata)).slice().sort(compareIndexCandidates);
28868
+ const current = [];
28869
+ let older = 0;
28870
+ for (const c of eligible) {
28871
+ const at = parseActivity(c.lastActivity);
28872
+ if (!Number.isNaN(at) && at < cutoff)
28873
+ older++;
28874
+ else
28875
+ current.push(c);
28876
+ }
28877
+ const heading = indexHeading(projectName);
28878
+ if (current.length === 0 && older === 0) {
28879
+ const closed2 = closeWithFooter([heading, indexEmptyLine(projectName)], 0);
28880
+ return { ...closed2, shown: 0, more: 0, older: 0, truncated, ids: [] };
28881
+ }
28882
+ const reserve = sectionBytes([
28883
+ moreLine(current.length, truncated),
28884
+ olderLine(older, truncated),
28885
+ footerLine(INDEX_MAX_LINES, INDEX_MAX_BYTES, INDEX_MAX_BYTES)
28886
+ ]);
28887
+ const budget = INDEX_MAX_BYTES - reserve - sectionBytes([heading]);
28888
+ const rendered = [];
28889
+ const ids = [];
28890
+ let used = 0;
28891
+ for (const c of current) {
28892
+ if (rendered.length >= INDEX_MAX_LINES)
28893
+ break;
28894
+ const line = indexLine(c);
28895
+ const cost = byteLength(line) + 1;
28896
+ if (used + cost > budget)
28897
+ break;
28898
+ rendered.push(line);
28899
+ ids.push(c.id);
28900
+ used += cost;
28901
+ }
28902
+ const more = current.length - rendered.length;
28903
+ const above = [heading, ...rendered];
28904
+ if (more > 0)
28905
+ above.push(moreLine(more, truncated));
28906
+ if (older > 0)
28907
+ above.push(olderLine(older, truncated));
28908
+ const closed = closeWithFooter(above, rendered.length);
28909
+ return { ...closed, shown: rendered.length, more, older, truncated, ids };
28910
+ }
28911
+
28550
28912
  // dist/core/briefing.js
28551
28913
  var PROJECT_LIMIT = 30;
28552
28914
  var RECENT_LIMIT = 5;
@@ -28587,16 +28949,48 @@ function toTopologyEntity(row, snippet) {
28587
28949
  signalScore: typeof signal === "number" ? signal : null
28588
28950
  };
28589
28951
  }
28952
+ function readBriefingIndex(db2, projectName, now = Date.now()) {
28953
+ const hasNamespace = db2.prepare("PRAGMA table_info(entities)").all().some((column) => column.name === "namespace");
28954
+ const nonGlobal = hasNamespace ? " AND (e.namespace IS NULL OR e.namespace <> 'global')" : "";
28955
+ const excluded = INDEX_EXCLUDED_TYPES.map(() => "?").join(",");
28956
+ const rows = db2.prepare(`SELECT e.id, e.type, e.title, e.metadata,
28957
+ (SELECT substr(o.content, 1, ${INDEX_SNIPPET_FETCH_CHARS}) FROM observations o
28958
+ WHERE o.entity_id = e.id ORDER BY o.id ASC LIMIT 1) AS snippet,
28959
+ max(e.created_at, COALESCE((SELECT MAX(o2.created_at) FROM observations o2
28960
+ WHERE o2.entity_id = e.id), e.created_at)) AS last_activity
28961
+ FROM entities e
28962
+ WHERE e.id IN (SELECT entity_id FROM tags WHERE tag = ?)
28963
+ AND e.status = 'active'${nonGlobal}
28964
+ AND e.type NOT IN (${excluded})
28965
+ ORDER BY last_activity DESC, e.id DESC
28966
+ LIMIT ?`).all(`project:${projectName}`, ...INDEX_EXCLUDED_TYPES, INDEX_CANDIDATE_CAP);
28967
+ const candidates = rows.map((row) => ({
28968
+ id: row.id,
28969
+ type: row.type,
28970
+ title: row.title,
28971
+ snippet: row.snippet,
28972
+ lastActivity: row.last_activity,
28973
+ metadata: row.metadata
28974
+ }));
28975
+ return buildBriefingIndex(candidates, projectName, now, { truncated: rows.length >= INDEX_CANDIDATE_CAP });
28976
+ }
28590
28977
  function assembleBriefing(project, recipient) {
28591
28978
  const projectName = project ?? getProjectName();
28592
28979
  const db2 = getDatabase();
28593
28980
  const repoLines = project === void 0 || project === getProjectName() ? repoStateLines(readRepoState()) : [];
28594
- const { state } = getTaskState(projectName);
28981
+ let taskLines;
28982
+ try {
28983
+ taskLines = taskStateLines(getTaskState(projectName).state, projectName);
28984
+ } catch (err) {
28985
+ if (!(err instanceof TaskStateUnreadableError))
28986
+ throw err;
28987
+ taskLines = [`task state for ${projectName}: ${err.message}`];
28988
+ }
28595
28989
  const inboxRecipient = recipient === void 0 ? void 0 : canonicalAgentScopeId(recipient);
28596
28990
  const unreadCount = unreadDeliveryCount(db2, canonicalAgentScopeId(projectName), inboxRecipient);
28597
28991
  const everSeen = inboxRecipient !== void 0 && unreadCount === 0 ? recipientEverSeen(db2, canonicalAgentScopeId(projectName), inboxRecipient) : void 0;
28598
28992
  const stateLines = [
28599
- ...taskStateLines(state, projectName),
28993
+ ...taskLines,
28600
28994
  ...unreadInboxLines(unreadCount, canonicalAgentScopeId(projectName), inboxRecipient, everSeen)
28601
28995
  ];
28602
28996
  const hasNamespace = db2.prepare("PRAGMA table_info(entities)").all().some((column) => column.name === "namespace");
@@ -28641,11 +29035,14 @@ function assembleBriefing(project, recipient) {
28641
29035
  { entities: toEntities(recentPool), foreign: true }
28642
29036
  ], projectName);
28643
29037
  const withRepo = lines.length > 0 && repoLines.length > 0 ? [...repoLines, "", ...lines] : lines;
29038
+ const index = readBriefingIndex(db2, projectName);
29039
+ const block = withRepo.length > 0 ? [...withRepo, "", ...index.lines] : index.lines;
28644
29040
  return {
28645
29041
  project: projectName,
28646
- text: lines.length > 0 ? buildReferenceContext(withRepo) : "",
29042
+ text: buildReferenceContext(block),
28647
29043
  entityCount: lines.filter((l) => l.startsWith("- [")).length,
28648
- hasTaskState: stateLines.length > 0
29044
+ hasTaskState: stateLines.length > 0,
29045
+ index
28649
29046
  };
28650
29047
  }
28651
29048
 
@@ -28653,10 +29050,10 @@ function assembleBriefing(project, recipient) {
28653
29050
  import { randomUUID as randomUUID4 } from "node:crypto";
28654
29051
 
28655
29052
  // dist/core/agent-messaging.js
28656
- import { createHash as createHash7, randomBytes, randomUUID as randomUUID2 } from "node:crypto";
29053
+ import { createHash as createHash9, randomBytes, randomUUID as randomUUID2 } from "node:crypto";
28657
29054
 
28658
29055
  // dist/core/agent-message-storage.js
28659
- import { createHash as createHash6, randomUUID } from "node:crypto";
29056
+ import { createHash as createHash8, randomUUID } from "node:crypto";
28660
29057
  import fs5 from "node:fs";
28661
29058
  var TERMINAL_WORKFLOW_STATES = /* @__PURE__ */ new Set(["completed", "cancelled", "rejected"]);
28662
29059
  var AgentMessageStorageError = class extends Error {
@@ -29304,7 +29701,7 @@ function parseJsonObjectOrValue(json2) {
29304
29701
  return parsed;
29305
29702
  }
29306
29703
  function hashCanonical(value) {
29307
- return createHash7("sha256").update(stableStringify(value)).digest("hex");
29704
+ return createHash9("sha256").update(stableStringify(value)).digest("hex");
29308
29705
  }
29309
29706
  function stableStringify(value) {
29310
29707
  if (value === null)
@@ -29454,14 +29851,38 @@ var WorkPackageSchema = external_exports.discriminatedUnion("action", [
29454
29851
  }).strict()
29455
29852
  ]);
29456
29853
  var RememberSchema = external_exports.object({
29457
- name: nameField,
29458
- type: external_exports.string().min(1).max(100),
29854
+ name: nameField.optional(),
29855
+ type: external_exports.string().min(1).max(100).optional(),
29459
29856
  title: titleField,
29460
29857
  observations: external_exports.array(observationField).max(100).optional(),
29858
+ note: external_exports.string().max(NOTE_MAX_CHARS).optional(),
29859
+ replace: external_exports.boolean().optional(),
29461
29860
  tags: external_exports.array(external_exports.string().max(255)).max(50).optional(),
29462
29861
  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
29862
  namespace: external_exports.enum(NAMESPACES).optional()
29464
- }).strict();
29863
+ }).strict().superRefine((data, ctx) => {
29864
+ if (data.note === void 0) {
29865
+ if (data.name === void 0)
29866
+ ctx.addIssue({ code: "custom", path: ["name"], message: "name is required (or pass `note` to have it derived)" });
29867
+ if (data.type === void 0 && !(data.replace && data.name !== void 0))
29868
+ 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)' });
29869
+ return;
29870
+ }
29871
+ for (const key of ["title", "observations"]) {
29872
+ if (data[key] !== void 0) {
29873
+ 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)` });
29874
+ }
29875
+ }
29876
+ if (data.replace && data.name === void 0) {
29877
+ 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" });
29878
+ }
29879
+ const derived = deriveNote(data.note);
29880
+ if (!derived) {
29881
+ ctx.addIssue({ code: "custom", path: ["note"], message: "note must contain some text" });
29882
+ } else if (derived.observations.length > NOTE_MAX_OBSERVATIONS) {
29883
+ ctx.addIssue({ code: "custom", path: ["note"], message: `note yields ${derived.observations.length} observations; at most ${NOTE_MAX_OBSERVATIONS} are stored per memory` });
29884
+ }
29885
+ });
29465
29886
  var RecallSchema = external_exports.object({
29466
29887
  query: external_exports.string().max(1e3).optional(),
29467
29888
  tag: external_exports.string().max(255).optional(),
@@ -30142,6 +30563,543 @@ async function executeAgentMessageAction(db2, rawInput, context, dependencies =
30142
30563
  }
30143
30564
  }
30144
30565
 
30566
+ // dist/core/update-entrypoint.js
30567
+ import fs9 from "fs";
30568
+ import path8 from "path";
30569
+ import { spawn } from "child_process";
30570
+ import { fileURLToPath } from "url";
30571
+
30572
+ // dist/core/version-check.js
30573
+ import fs7 from "fs";
30574
+ import path6 from "path";
30575
+
30576
+ // dist/core/semver.js
30577
+ 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-]+)*)?$/;
30578
+ var NUMERIC_IDENTIFIER = /^\d+$/;
30579
+ function parseSemVer(version2) {
30580
+ const match = SEMVER.exec(version2);
30581
+ if (!match)
30582
+ return null;
30583
+ const prerelease = match[4]?.split(".") ?? null;
30584
+ if (prerelease?.some((identifier) => NUMERIC_IDENTIFIER.test(identifier) && identifier.length > 1 && identifier.startsWith("0")))
30585
+ return null;
30586
+ return {
30587
+ core: [BigInt(match[1]), BigInt(match[2]), BigInt(match[3])],
30588
+ prerelease
30589
+ };
30590
+ }
30591
+ function compareIdentifiers(a, b) {
30592
+ const aNumeric = NUMERIC_IDENTIFIER.test(a);
30593
+ const bNumeric = NUMERIC_IDENTIFIER.test(b);
30594
+ if (aNumeric && bNumeric)
30595
+ return BigInt(a) < BigInt(b) ? -1 : BigInt(a) > BigInt(b) ? 1 : 0;
30596
+ if (aNumeric !== bNumeric)
30597
+ return aNumeric ? -1 : 1;
30598
+ return a < b ? -1 : a > b ? 1 : 0;
30599
+ }
30600
+ function compareSemVerPrecedence(a, b) {
30601
+ for (let index = 0; index < a.core.length; index += 1) {
30602
+ if (a.core[index] !== b.core[index])
30603
+ return a.core[index] < b.core[index] ? -1 : 1;
30604
+ }
30605
+ if (a.prerelease === null || b.prerelease === null) {
30606
+ if (a.prerelease !== b.prerelease)
30607
+ return a.prerelease === null ? 1 : -1;
30608
+ return 0;
30609
+ }
30610
+ const length = Math.max(a.prerelease.length, b.prerelease.length);
30611
+ for (let index = 0; index < length; index += 1) {
30612
+ const aIdentifier = a.prerelease[index];
30613
+ const bIdentifier = b.prerelease[index];
30614
+ if (aIdentifier === void 0 || bIdentifier === void 0) {
30615
+ if (aIdentifier !== bIdentifier)
30616
+ return aIdentifier === void 0 ? -1 : 1;
30617
+ break;
30618
+ }
30619
+ const compared = compareIdentifiers(aIdentifier, bIdentifier);
30620
+ if (compared !== 0)
30621
+ return compared;
30622
+ }
30623
+ return 0;
30624
+ }
30625
+
30626
+ // dist/core/version-check.js
30627
+ var STALE_AFTER_MS = 24 * 60 * 60 * 1e3;
30628
+ function isUpdateAvailable(currentVersion, latestVersion) {
30629
+ if (latestVersion === null)
30630
+ return false;
30631
+ const current = parseSemVer(currentVersion);
30632
+ const latest = parseSemVer(latestVersion);
30633
+ if (!current || !latest)
30634
+ return false;
30635
+ return compareSemVerPrecedence(current, latest) < 0;
30636
+ }
30637
+ function getUpdateCheckPath(updateCheckPath, currentVersion) {
30638
+ if (updateCheckPath)
30639
+ return updateCheckPath;
30640
+ if (process.env.MEMESH_UPDATE_CHECK_PATH)
30641
+ return process.env.MEMESH_UPDATE_CHECK_PATH;
30642
+ const versionTag = currentVersion && /^[0-9A-Za-z.+-]+$/.test(currentVersion) ? currentVersion : "unknown";
30643
+ return path6.join(memeshDir(), `update-check.${versionTag}.json`);
30644
+ }
30645
+ function parseIsoDate(value) {
30646
+ if (!value)
30647
+ return null;
30648
+ const timestamp = Date.parse(value);
30649
+ return Number.isNaN(timestamp) ? null : timestamp;
30650
+ }
30651
+ function determineFreshness(source, checkSucceeded, lastSuccessfulCheckAt, now) {
30652
+ if (!lastSuccessfulCheckAt)
30653
+ return "unavailable";
30654
+ if (source === "fresh" && checkSucceeded)
30655
+ return "fresh";
30656
+ const successfulAt = parseIsoDate(lastSuccessfulCheckAt);
30657
+ if (successfulAt === null)
30658
+ return "unavailable";
30659
+ return now.getTime() - successfulAt > STALE_AFTER_MS ? "stale" : "cached";
30660
+ }
30661
+ function buildResult(currentVersion, stored, source, now) {
30662
+ const versionMatches = stored.currentVersion === currentVersion;
30663
+ const deprecationMessage = versionMatches ? stored.currentVersionDeprecation : null;
30664
+ const lastError = versionMatches ? stored.lastError : null;
30665
+ return {
30666
+ currentVersion,
30667
+ latestVersion: stored.latestVersion,
30668
+ checkedAt: stored.lastAttemptAt,
30669
+ lastAttemptAt: stored.lastAttemptAt,
30670
+ lastSuccessfulCheckAt: stored.lastSuccessfulCheckAt,
30671
+ lastError,
30672
+ updateAvailable: isUpdateAvailable(currentVersion, stored.latestVersion),
30673
+ checkSucceeded: stored.checkSucceeded,
30674
+ source,
30675
+ freshness: determineFreshness(source, stored.checkSucceeded, stored.lastSuccessfulCheckAt, now),
30676
+ currentVersionDeprecated: deprecationMessage !== null,
30677
+ deprecationMessage
30678
+ };
30679
+ }
30680
+ function parseStoredUpdateCheck(raw) {
30681
+ if (!raw || typeof raw !== "object")
30682
+ return null;
30683
+ const candidate = raw;
30684
+ const latestVersion = candidate.latestVersion;
30685
+ const lastAttemptAt = "lastAttemptAt" in candidate ? candidate.lastAttemptAt : candidate.checkedAt;
30686
+ const lastSuccessfulCheckAt = "lastSuccessfulCheckAt" in candidate ? candidate.lastSuccessfulCheckAt : candidate.checkedAt;
30687
+ if (latestVersion !== null && latestVersion !== void 0 && typeof latestVersion !== "string")
30688
+ return null;
30689
+ if (lastAttemptAt !== null && lastAttemptAt !== void 0 && typeof lastAttemptAt !== "string")
30690
+ return null;
30691
+ if (lastSuccessfulCheckAt !== null && lastSuccessfulCheckAt !== void 0 && typeof lastSuccessfulCheckAt !== "string")
30692
+ return null;
30693
+ if ("lastError" in candidate && candidate.lastError !== null && typeof candidate.lastError !== "string")
30694
+ return null;
30695
+ if ("checkSucceeded" in candidate && typeof candidate.checkSucceeded !== "boolean")
30696
+ return null;
30697
+ if ("currentVersion" in candidate && candidate.currentVersion !== null && typeof candidate.currentVersion !== "string")
30698
+ return null;
30699
+ if ("currentVersionDeprecation" in candidate && candidate.currentVersionDeprecation !== null && typeof candidate.currentVersionDeprecation !== "string")
30700
+ return null;
30701
+ const normalizedLatestVersion = latestVersion ?? null;
30702
+ const normalizedLastAttemptAt = lastAttemptAt ?? null;
30703
+ const normalizedLastSuccessfulCheckAt = lastSuccessfulCheckAt ?? null;
30704
+ const checkSucceeded = typeof candidate.checkSucceeded === "boolean" ? candidate.checkSucceeded : normalizedLatestVersion !== null;
30705
+ return {
30706
+ currentVersion: typeof candidate.currentVersion === "string" ? candidate.currentVersion : null,
30707
+ latestVersion: normalizedLatestVersion,
30708
+ lastAttemptAt: normalizedLastAttemptAt,
30709
+ lastSuccessfulCheckAt: normalizedLastSuccessfulCheckAt,
30710
+ lastError: typeof candidate.lastError === "string" ? candidate.lastError : null,
30711
+ checkSucceeded,
30712
+ currentVersionDeprecation: typeof candidate.currentVersionDeprecation === "string" ? candidate.currentVersionDeprecation : null
30713
+ };
30714
+ }
30715
+ function readStoredUpdateCheck(updateCheckPath, currentVersion) {
30716
+ try {
30717
+ const targetPath = getUpdateCheckPath(updateCheckPath, currentVersion);
30718
+ if (!fs7.existsSync(targetPath))
30719
+ return null;
30720
+ return parseStoredUpdateCheck(JSON.parse(fs7.readFileSync(targetPath, "utf8")));
30721
+ } catch {
30722
+ return null;
30723
+ }
30724
+ }
30725
+ function getLastUpdateCheck(currentVersion, options = {}) {
30726
+ const stored = readStoredUpdateCheck(options.updateCheckPath, currentVersion);
30727
+ if (!stored)
30728
+ return null;
30729
+ return buildResult(currentVersion, stored, "cache", options.now ?? /* @__PURE__ */ new Date());
30730
+ }
30731
+
30732
+ // dist/core/update-notice.js
30733
+ import fs8 from "fs";
30734
+ import path7 from "path";
30735
+ var UP_TO_DATE_REFRESH_MS = 60 * 60 * 1e3;
30736
+ var UPGRADE_AVAILABLE_REFRESH_MS = 12 * 60 * 60 * 1e3;
30737
+ var ANSWER_VALID_MS = 24 * 60 * 60 * 1e3;
30738
+ var SNOOZE_LEVEL_MS = [24 * 60 * 60 * 1e3, 48 * 60 * 60 * 1e3, 7 * 24 * 60 * 60 * 1e3];
30739
+ var SNOOZE_FILE = "update-snooze.json";
30740
+ var JUST_UPGRADED_FILE = "just-upgraded.json";
30741
+ function isStrictlyOlder(a, b) {
30742
+ const parse3 = (v) => {
30743
+ const [main2, ...rest] = String(v).split(/[-+]/);
30744
+ const nums = main2.split(".").map((s) => Number.parseInt(s, 10));
30745
+ return { nums, tail: rest.join("-") };
30746
+ };
30747
+ const pa = parse3(a);
30748
+ const pb = parse3(b);
30749
+ const len = Math.max(pa.nums.length, pb.nums.length);
30750
+ for (let i = 0; i < len; i++) {
30751
+ const ai = Number.isFinite(pa.nums[i]) ? pa.nums[i] : 0;
30752
+ const bi = Number.isFinite(pb.nums[i]) ? pb.nums[i] : 0;
30753
+ if (ai !== bi)
30754
+ return ai < bi;
30755
+ }
30756
+ if (pa.tail && !pb.tail)
30757
+ return true;
30758
+ if (!pa.tail && pb.tail)
30759
+ return false;
30760
+ return pa.tail < pb.tail;
30761
+ }
30762
+ function parseIso(value) {
30763
+ if (typeof value !== "string")
30764
+ return null;
30765
+ const t = Date.parse(value);
30766
+ return Number.isNaN(t) ? null : t;
30767
+ }
30768
+ function readJson(file2) {
30769
+ try {
30770
+ if (!fs8.existsSync(file2))
30771
+ return null;
30772
+ const parsed = JSON.parse(fs8.readFileSync(file2, "utf8"));
30773
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
30774
+ } catch {
30775
+ return null;
30776
+ }
30777
+ }
30778
+ function readSnooze(dir) {
30779
+ const raw = readJson(path7.join(dir, SNOOZE_FILE));
30780
+ if (!raw)
30781
+ return null;
30782
+ const { target, level, since } = raw;
30783
+ if (typeof target !== "string" || !target)
30784
+ return null;
30785
+ if (typeof level !== "number" || !Number.isInteger(level) || level < 1)
30786
+ return null;
30787
+ if (parseIso(since) === null)
30788
+ return null;
30789
+ return { target, level, since };
30790
+ }
30791
+ function snoozeExpiresAt(state) {
30792
+ const since = parseIso(state.since) ?? 0;
30793
+ const duration3 = SNOOZE_LEVEL_MS[Math.min(state.level, SNOOZE_LEVEL_MS.length) - 1];
30794
+ return since + duration3;
30795
+ }
30796
+ function readJustUpgradedMarker(dir) {
30797
+ const raw = readJson(path7.join(dir, JUST_UPGRADED_FILE));
30798
+ if (!raw)
30799
+ return null;
30800
+ const { from, to, at } = raw;
30801
+ if (typeof from !== "string" || !from || typeof to !== "string" || !to)
30802
+ return null;
30803
+ return { from, to, at: typeof at === "string" ? at : "" };
30804
+ }
30805
+ function clearJustUpgradedMarker(dir) {
30806
+ try {
30807
+ fs8.unlinkSync(path7.join(dir, JUST_UPGRADED_FILE));
30808
+ } catch {
30809
+ }
30810
+ }
30811
+ function claimJustUpgradedMarker(dir) {
30812
+ const file2 = path7.join(dir, JUST_UPGRADED_FILE);
30813
+ const taken = `${file2}.claimed-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
30814
+ try {
30815
+ fs8.renameSync(file2, taken);
30816
+ } catch {
30817
+ return null;
30818
+ }
30819
+ const raw = readJson(taken);
30820
+ try {
30821
+ fs8.unlinkSync(taken);
30822
+ } catch {
30823
+ }
30824
+ if (!raw)
30825
+ return null;
30826
+ const { from, to, at } = raw;
30827
+ if (typeof from !== "string" || !from || typeof to !== "string" || !to)
30828
+ return null;
30829
+ return { from, to, at: typeof at === "string" ? at : "" };
30830
+ }
30831
+ function boundedReason(raw) {
30832
+ const oneLine = redactUserPaths(raw).replace(/[\u0000-\u001f\u007f]+/g, " ").replace(/\s+/g, " ").trim();
30833
+ return oneLine.length > 160 ? `${oneLine.slice(0, 157)}...` : oneLine;
30834
+ }
30835
+ function answerIsCurrent(currentVersion, cache, now) {
30836
+ if (!cache || cache.currentVersion !== currentVersion)
30837
+ return false;
30838
+ if (typeof cache.latestVersion !== "string" || !cache.latestVersion)
30839
+ return false;
30840
+ const successAt = parseIso(cache.lastSuccessfulCheckAt);
30841
+ if (successAt === null)
30842
+ return false;
30843
+ return now.getTime() - successAt <= ANSWER_VALID_MS;
30844
+ }
30845
+ function shouldRefreshUpdateCache(currentVersion, cache, now = /* @__PURE__ */ new Date()) {
30846
+ if (!answerIsCurrent(currentVersion, cache, now))
30847
+ return true;
30848
+ const successAt = parseIso(cache.lastSuccessfulCheckAt);
30849
+ const age = now.getTime() - successAt;
30850
+ const upgrade = isStrictlyOlder(currentVersion, cache.latestVersion);
30851
+ return age > (upgrade ? UPGRADE_AVAILABLE_REFRESH_MS : UP_TO_DATE_REFRESH_MS);
30852
+ }
30853
+ function resolveUpdateNotice(input) {
30854
+ const { dir, currentVersion, cache } = input;
30855
+ const now = input.now ?? /* @__PURE__ */ new Date();
30856
+ if (input.updateCheckEnabled === false)
30857
+ return { kind: "DISABLED", currentVersion };
30858
+ const marker = readJustUpgradedMarker(dir);
30859
+ if (marker) {
30860
+ if (marker.to === currentVersion) {
30861
+ return { kind: "JUST_UPGRADED", currentVersion, from: marker.from, to: marker.to };
30862
+ }
30863
+ clearJustUpgradedMarker(dir);
30864
+ }
30865
+ if (!answerIsCurrent(currentVersion, cache, now)) {
30866
+ let reason = "no update check has completed yet";
30867
+ let attempted = false;
30868
+ if (cache && cache.currentVersion === currentVersion) {
30869
+ attempted = parseIso(cache.lastSuccessfulCheckAt) !== null || typeof cache.lastError === "string" && cache.lastError.length > 0;
30870
+ if (typeof cache.lastError === "string" && cache.lastError)
30871
+ reason = boundedReason(cache.lastError);
30872
+ else if (parseIso(cache.lastSuccessfulCheckAt) !== null)
30873
+ reason = "the last successful check is more than a day old";
30874
+ }
30875
+ return { kind: "CHECK_FAILED", currentVersion, reason, attempted };
30876
+ }
30877
+ const latestVersion = cache.latestVersion;
30878
+ if (!isStrictlyOlder(currentVersion, latestVersion)) {
30879
+ return { kind: "UP_TO_DATE", currentVersion, latestVersion };
30880
+ }
30881
+ const snooze = readSnooze(dir);
30882
+ if (snooze && snooze.target === latestVersion) {
30883
+ const until = snoozeExpiresAt(snooze);
30884
+ if (now.getTime() < until) {
30885
+ return { kind: "SNOOZED", currentVersion, latestVersion, until: new Date(until).toISOString(), level: snooze.level };
30886
+ }
30887
+ }
30888
+ return { kind: "UPGRADE_AVAILABLE", currentVersion, latestVersion };
30889
+ }
30890
+
30891
+ // dist/core/update-entrypoint.js
30892
+ var RECENT_HOOK_NOTICE_MS = 10 * 60 * 1e3;
30893
+ var CLI_NOTICE_THROTTLE_MS = 24 * 60 * 60 * 1e3;
30894
+ var FRESH_CHECK_THROTTLE_MS = 5 * 60 * 1e3;
30895
+ function formatUpdateNoticeLine(notice) {
30896
+ switch (notice.kind) {
30897
+ case "UPGRADE_AVAILABLE":
30898
+ 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.`;
30899
+ case "JUST_UPGRADED":
30900
+ return `[memesh update] Upgraded ${notice.from} \u2192 ${notice.to}. Processes started before the upgrade keep running ${notice.from} until they restart.`;
30901
+ case "CHECK_FAILED":
30902
+ return `[memesh update] Could not confirm whether an update exists (${notice.reason}). Status is unknown, not current \u2014 \`memesh status\` retries.`;
30903
+ default:
30904
+ return null;
30905
+ }
30906
+ }
30907
+ function recentHookNoticeExists(dir, currentVersion, latestVersion, now = /* @__PURE__ */ new Date()) {
30908
+ const claims = path8.join(dir, "update-prompt-claims");
30909
+ let names;
30910
+ try {
30911
+ names = fs9.readdirSync(claims);
30912
+ } catch {
30913
+ return false;
30914
+ }
30915
+ for (const name of names) {
30916
+ if (!name.endsWith(".json"))
30917
+ continue;
30918
+ const file2 = path8.join(claims, name);
30919
+ let fd = null;
30920
+ try {
30921
+ fd = fs9.openSync(file2, "r");
30922
+ const stat = fs9.fstatSync(fd);
30923
+ if (now.getTime() - stat.mtimeMs > RECENT_HOOK_NOTICE_MS)
30924
+ continue;
30925
+ const value = JSON.parse(fs9.readFileSync(fd, "utf8"));
30926
+ if (value.currentVersion === currentVersion && (latestVersion === null || value.latestVersion === latestVersion))
30927
+ return true;
30928
+ } catch {
30929
+ } finally {
30930
+ if (fd !== null)
30931
+ try {
30932
+ fs9.closeSync(fd);
30933
+ } catch {
30934
+ }
30935
+ }
30936
+ }
30937
+ return false;
30938
+ }
30939
+ function updateCheckEnabledIn(dir) {
30940
+ try {
30941
+ const raw = JSON.parse(fs9.readFileSync(path8.join(dir, "config.json"), "utf8"));
30942
+ return raw.updateCheck !== false;
30943
+ } catch {
30944
+ return true;
30945
+ }
30946
+ }
30947
+ function cliThrottled(dir, currentVersion, now) {
30948
+ const tag = /^[0-9A-Za-z.+-]+$/.test(currentVersion) ? currentVersion : "unknown";
30949
+ const marker = path8.join(dir, `last-cli-update-notice.${tag}.lock`);
30950
+ let fd = null;
30951
+ try {
30952
+ try {
30953
+ fd = fs9.openSync(marker, "r+");
30954
+ } catch (err) {
30955
+ const code = err.code;
30956
+ if (code !== "ENOENT") {
30957
+ return true;
30958
+ }
30959
+ fs9.mkdirSync(dir, { recursive: true, mode: 448 });
30960
+ try {
30961
+ fd = fs9.openSync(marker, "wx", 384);
30962
+ } catch (raceErr) {
30963
+ if (raceErr.code === "EEXIST")
30964
+ return true;
30965
+ throw raceErr;
30966
+ }
30967
+ fs9.writeSync(fd, String(now.getTime()));
30968
+ return false;
30969
+ }
30970
+ const stat = fs9.fstatSync(fd);
30971
+ if (now.getTime() - stat.mtimeMs < CLI_NOTICE_THROTTLE_MS)
30972
+ return true;
30973
+ fs9.ftruncateSync(fd, 0);
30974
+ fs9.writeSync(fd, String(now.getTime()), 0);
30975
+ try {
30976
+ fs9.fchmodSync(fd, 384);
30977
+ } catch {
30978
+ }
30979
+ return false;
30980
+ } catch {
30981
+ return true;
30982
+ } finally {
30983
+ if (fd !== null)
30984
+ try {
30985
+ fs9.closeSync(fd);
30986
+ } catch {
30987
+ }
30988
+ }
30989
+ }
30990
+ function spawnCacheRefresh(dir, currentVersion, now) {
30991
+ try {
30992
+ const cliPath = fileURLToPath(new URL("../transports/cli/cli.js", import.meta.url));
30993
+ if (!fs9.existsSync(cliPath))
30994
+ return false;
30995
+ const tag = /^[0-9A-Za-z.+-]+$/.test(currentVersion) ? currentVersion : "unknown";
30996
+ const marker = path8.join(dir, `last-fresh-refresh.${tag}.lock`);
30997
+ try {
30998
+ if (now.getTime() - fs9.statSync(marker).mtimeMs < FRESH_CHECK_THROTTLE_MS)
30999
+ return false;
31000
+ fs9.unlinkSync(marker);
31001
+ } catch {
31002
+ }
31003
+ fs9.mkdirSync(dir, { recursive: true, mode: 448 });
31004
+ try {
31005
+ const fd = fs9.openSync(marker, "wx", 384);
31006
+ try {
31007
+ fs9.writeSync(fd, `${process.pid}-${now.getTime()}`);
31008
+ } finally {
31009
+ fs9.closeSync(fd);
31010
+ }
31011
+ } catch {
31012
+ return false;
31013
+ }
31014
+ const child = spawn(process.execPath, [cliPath, "status"], {
31015
+ detached: true,
31016
+ stdio: "ignore",
31017
+ env: { ...process.env },
31018
+ windowsHide: true
31019
+ });
31020
+ child.unref();
31021
+ return true;
31022
+ } catch {
31023
+ return false;
31024
+ }
31025
+ }
31026
+ var MCP_PENDING_RETRY_MS = 60 * 1e3;
31027
+ var ProcessMemo = class {
31028
+ set;
31029
+ key;
31030
+ now;
31031
+ retryPrefix;
31032
+ constructor(set2, key, now) {
31033
+ this.set = set2;
31034
+ this.key = key;
31035
+ this.now = now;
31036
+ this.retryPrefix = `${key}#retry:`;
31037
+ }
31038
+ decided() {
31039
+ if (!this.set)
31040
+ return false;
31041
+ if (this.set.has(this.key))
31042
+ return true;
31043
+ for (const entry of this.set) {
31044
+ if (entry.startsWith(this.retryPrefix) && this.now.getTime() < Number(entry.slice(this.retryPrefix.length)))
31045
+ return true;
31046
+ }
31047
+ return false;
31048
+ }
31049
+ decide() {
31050
+ this.clearRetry();
31051
+ this.set?.add(this.key);
31052
+ }
31053
+ retryLater() {
31054
+ this.clearRetry();
31055
+ this.set?.add(`${this.retryPrefix}${this.now.getTime() + MCP_PENDING_RETRY_MS}`);
31056
+ }
31057
+ clearRetry() {
31058
+ if (!this.set)
31059
+ return;
31060
+ for (const entry of [...this.set])
31061
+ if (entry.startsWith(this.retryPrefix))
31062
+ this.set.delete(entry);
31063
+ }
31064
+ };
31065
+ function updateNoticeForEntryPoint(input) {
31066
+ try {
31067
+ const now = input.now ?? /* @__PURE__ */ new Date();
31068
+ const memo = input.entryPoint === "mcp" ? new ProcessMemo(input.processOnce, input.currentVersion, now) : null;
31069
+ if (memo?.decided())
31070
+ return null;
31071
+ const dir = input.dir ?? memeshDir();
31072
+ const updateCheckEnabled = input.updateCheckEnabled ?? updateCheckEnabledIn(dir);
31073
+ const tag = /^[0-9A-Za-z.+-]+$/.test(input.currentVersion) ? input.currentVersion : "unknown";
31074
+ const cache = getLastUpdateCheck(input.currentVersion, { now, updateCheckPath: path8.join(dir, `update-check.${tag}.json`) });
31075
+ if (updateCheckEnabled && shouldRefreshUpdateCache(input.currentVersion, cache, now)) {
31076
+ (input.refresh ?? spawnCacheRefresh)(dir, input.currentVersion, now);
31077
+ }
31078
+ const notice = resolveUpdateNotice({ dir, currentVersion: input.currentVersion, cache, now, updateCheckEnabled });
31079
+ if (notice.kind === "CHECK_FAILED" && !notice.attempted) {
31080
+ memo?.retryLater();
31081
+ return null;
31082
+ }
31083
+ memo?.decide();
31084
+ if (notice.kind === "DISABLED" || notice.kind === "SNOOZED" || notice.kind === "UP_TO_DATE")
31085
+ return null;
31086
+ if (input.entryPoint === "mcp" && recentHookNoticeExists(dir, notice.currentVersion, notice.kind === "UPGRADE_AVAILABLE" ? notice.latestVersion : null, now)) {
31087
+ return null;
31088
+ }
31089
+ if (input.entryPoint === "cli" && cliThrottled(dir, input.currentVersion, now))
31090
+ return null;
31091
+ if (notice.kind === "JUST_UPGRADED") {
31092
+ const claimed = claimJustUpgradedMarker(dir);
31093
+ if (!claimed)
31094
+ return null;
31095
+ return formatUpdateNoticeLine({ ...notice, from: claimed.from, to: claimed.to });
31096
+ }
31097
+ return formatUpdateNoticeLine(notice);
31098
+ } catch {
31099
+ return null;
31100
+ }
31101
+ }
31102
+
30145
31103
  // dist/transports/mcp/handlers.js
30146
31104
  function resolveTranscriptWorkspace(project, rootUris) {
30147
31105
  if (!rootUris)
@@ -30152,8 +31110,8 @@ function resolveTranscriptWorkspace(project, rootUris) {
30152
31110
  const parsed = new URL(uri);
30153
31111
  if (parsed.protocol !== "file:")
30154
31112
  continue;
30155
- const root = fs7.realpathSync(fileURLToPath(parsed));
30156
- if (!fs7.statSync(root).isDirectory() || getProjectName(root) !== project)
31113
+ const root = fs10.realpathSync(fileURLToPath2(parsed));
31114
+ if (!fs10.statSync(root).isDirectory() || getProjectName(root) !== project)
30157
31115
  continue;
30158
31116
  matches.add(root);
30159
31117
  } catch {
@@ -30173,17 +31131,25 @@ var TOOL_DEFINITIONS = [
30173
31131
  },
30174
31132
  {
30175
31133
  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.",
31134
+ 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
31135
  inputSchema: {
30178
31136
  type: "object",
30179
31137
  properties: {
30180
31138
  name: {
30181
31139
  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.'
31140
+ 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
31141
  },
30184
31142
  type: {
30185
31143
  type: "string",
30186
- description: 'Entity type (e.g., "decision", "pattern", "lesson", "commit")'
31144
+ 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.'
31145
+ },
31146
+ note: {
31147
+ type: "string",
31148
+ 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."
31149
+ },
31150
+ replace: {
31151
+ type: "boolean",
31152
+ 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
31153
  },
30188
31154
  title: {
30189
31155
  type: "string",
@@ -30221,8 +31187,12 @@ var TOOL_DEFINITIONS = [
30221
31187
  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
31188
  }
30223
31189
  },
30224
- required: ["name", "type"],
30225
- additionalProperties: false
31190
+ additionalProperties: false,
31191
+ anyOf: [
31192
+ { required: ["note"] },
31193
+ { required: ["name", "type"] },
31194
+ { required: ["name", "replace"], properties: { replace: { const: true } } }
31195
+ ]
30226
31196
  }
30227
31197
  },
30228
31198
  {
@@ -30347,7 +31317,7 @@ var TOOL_DEFINITIONS = [
30347
31317
  },
30348
31318
  {
30349
31319
  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.",
31320
+ 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 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.",
30351
31321
  inputSchema: {
30352
31322
  type: "object",
30353
31323
  properties: {
@@ -30466,8 +31436,8 @@ function fail(message) {
30466
31436
  return { content: [{ type: "text", text: message }], isError: true };
30467
31437
  }
30468
31438
  function formatIssue(issue2) {
30469
- const path7 = issue2.path.join(".");
30470
- return path7 ? `${path7}: ${issue2.message}` : issue2.message;
31439
+ const path10 = issue2.path.join(".");
31440
+ return path10 ? `${path10}: ${issue2.message}` : issue2.message;
30471
31441
  }
30472
31442
  function stripNullProps(value) {
30473
31443
  if (Array.isArray(value))
@@ -30506,7 +31476,26 @@ function parseOrFail(schema, args) {
30506
31476
  function normalizeClientHost(name) {
30507
31477
  return (name ?? "").replace(/[\u0000-\u001F\u007F]/g, "").trim().slice(0, 64) || "mcp";
30508
31478
  }
31479
+ var packageVersion = (() => {
31480
+ try {
31481
+ return JSON.parse(fs10.readFileSync(new URL("../../../package.json", import.meta.url), "utf8")).version ?? "0.0.0";
31482
+ } catch {
31483
+ return "0.0.0";
31484
+ }
31485
+ })();
31486
+ var firstCallNoticeOnce = /* @__PURE__ */ new Set();
31487
+ function withFirstCallNotice(result) {
31488
+ if (result.isError)
31489
+ return result;
31490
+ const line = updateNoticeForEntryPoint({ currentVersion: packageVersion, entryPoint: "mcp", processOnce: firstCallNoticeOnce });
31491
+ if (!line)
31492
+ return result;
31493
+ return { ...result, content: [...result.content, { type: "text", text: line }] };
31494
+ }
30509
31495
  async function handleTool(name, args, sourceHost, signal, requestContext = {}) {
31496
+ return withFirstCallNotice(await handleToolInner(name, args, sourceHost, signal, requestContext));
31497
+ }
31498
+ async function handleToolInner(name, args, sourceHost, signal, requestContext = {}) {
30510
31499
  try {
30511
31500
  if (name === "work_package") {
30512
31501
  const parsed = parseOrFail(WorkPackageSchema, args);
@@ -30666,9 +31655,9 @@ async function handleTool(name, args, sourceHost, signal, requestContext = {}) {
30666
31655
  }
30667
31656
 
30668
31657
  // 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: {} } });
31658
+ var packageJsonPath = path9.resolve(path9.dirname(fileURLToPath3(import.meta.url)), "../../package.json");
31659
+ var packageVersion2 = JSON.parse(fs11.readFileSync(packageJsonPath, "utf8")).version ?? "0.0.0";
31660
+ var server = new Server({ name: "memesh", version: packageVersion2 }, { capabilities: { tools: {} } });
30672
31661
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
30673
31662
  tools: TOOL_DEFINITIONS.map((t) => ({
30674
31663
  name: t.name,