mindgraph-mcp 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -22,36 +22,93 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
  mod
23
23
  ));
24
24
 
25
+ // src/call-ledger.ts
26
+ var import_node_crypto = __toESM(require("crypto"));
27
+ var import_node_fs = __toESM(require("fs"));
28
+ var import_node_os = __toESM(require("os"));
29
+ var import_node_path = __toESM(require("path"));
30
+ var TTL_MS = 10 * 6e4;
31
+ function callsDir(options) {
32
+ return import_node_path.default.join(
33
+ options.runtimeDir || process.env.MINDGRAPH_RUNTIME_DIR || import_node_path.default.join(import_node_os.default.homedir(), ".mindgraph", "runtime"),
34
+ "calls"
35
+ );
36
+ }
37
+ function stableJson(value) {
38
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
39
+ if (value && typeof value === "object") {
40
+ return `{${Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(",")}}`;
41
+ }
42
+ return JSON.stringify(value) ?? "null";
43
+ }
44
+ function bareToolName(name) {
45
+ const cut = name.lastIndexOf("__");
46
+ return cut === -1 ? name : name.slice(cut + 2);
47
+ }
48
+ function callKey(toolName, toolInput) {
49
+ return import_node_crypto.default.createHash("sha256").update(`${bareToolName(toolName)}\0${stableJson(toolInput)}`).digest("hex");
50
+ }
51
+ function resolvePendingCall(toolName, toolInput, options = {}) {
52
+ const now = (options.now ?? Date.now)();
53
+ const dir = callsDir(options);
54
+ const prefix = `${callKey(toolName, toolInput)}.`;
55
+ let names;
56
+ try {
57
+ names = import_node_fs.default.readdirSync(dir).filter((name) => name.startsWith(prefix) && name.endsWith(".json"));
58
+ } catch {
59
+ return void 0;
60
+ }
61
+ let matches = [];
62
+ for (const name of names) {
63
+ try {
64
+ const record = JSON.parse(
65
+ import_node_fs.default.readFileSync(import_node_path.default.join(dir, name), "utf8")
66
+ );
67
+ if (typeof record.createdAt === "number" && now - record.createdAt <= TTL_MS && record.fills && typeof record.fills === "object") {
68
+ matches.push(record);
69
+ }
70
+ } catch {
71
+ }
72
+ }
73
+ if (matches.length > 1) {
74
+ const projectDir = options.projectDir ?? process.env.CLAUDE_PROJECT_DIR;
75
+ matches = projectDir ? matches.filter((record) => record.projectDir === projectDir) : [];
76
+ }
77
+ return matches.length === 1 ? matches[0].fills : void 0;
78
+ }
79
+
25
80
  // src/index.ts
26
81
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
27
82
  var import_mindgraph3 = require("mindgraph");
28
83
 
29
84
  // src/hook-env.ts
30
- var import_node_crypto = __toESM(require("crypto"));
31
- var import_node_fs = __toESM(require("fs"));
32
- var import_node_os = __toESM(require("os"));
33
- var import_node_path = __toESM(require("path"));
85
+ var import_node_crypto2 = __toESM(require("crypto"));
86
+ var import_node_fs2 = __toESM(require("fs"));
87
+ var import_node_os2 = __toESM(require("os"));
88
+ var import_node_path2 = __toESM(require("path"));
34
89
  function stableAgentId() {
35
90
  let user = "user";
36
91
  try {
37
- user = import_node_os.default.userInfo().username || "user";
92
+ user = import_node_os2.default.userInfo().username || "user";
38
93
  } catch {
39
94
  }
40
- const digest = import_node_crypto.default.createHash("sha256").update(`${user}@${import_node_os.default.hostname()}`).digest("hex").slice(0, 12);
95
+ const digest = import_node_crypto2.default.createHash("sha256").update(`${user}@${import_node_os2.default.hostname()}`).digest("hex").slice(0, 12);
41
96
  return `u-${digest}`;
42
97
  }
43
98
  function hookEnvPath(dir) {
44
- return import_node_path.default.join(dir || import_node_path.default.join(import_node_os.default.homedir(), ".mindgraph"), "hooks.json");
99
+ return import_node_path2.default.join(dir || import_node_path2.default.join(import_node_os2.default.homedir(), ".mindgraph"), "hooks.json");
45
100
  }
46
101
  function loadHookEnv(dir) {
47
102
  try {
48
- const raw = import_node_fs.default.readFileSync(hookEnvPath(dir), "utf8");
103
+ const raw = import_node_fs2.default.readFileSync(hookEnvPath(dir), "utf8");
49
104
  const parsed = JSON.parse(raw);
50
105
  return {
51
106
  baseUrl: typeof parsed.baseUrl === "string" ? parsed.baseUrl : void 0,
52
107
  apiKey: typeof parsed.apiKey === "string" ? parsed.apiKey : void 0,
53
108
  agentId: typeof parsed.agentId === "string" ? parsed.agentId : void 0,
54
- orgId: typeof parsed.orgId === "string" ? parsed.orgId : void 0
109
+ orgId: typeof parsed.orgId === "string" ? parsed.orgId : void 0,
110
+ promptRecall: typeof parsed.promptRecall === "boolean" ? parsed.promptRecall : void 0,
111
+ capture: typeof parsed.capture === "boolean" ? parsed.capture : void 0
55
112
  };
56
113
  } catch {
57
114
  return {};
@@ -355,6 +412,14 @@ function hiddenSections(payload) {
355
412
  function renderContext(response, maxChars) {
356
413
  const payload = obj(response);
357
414
  if (!payload) return void 0;
415
+ if (typeof payload.context_text === "string" && payload.context_text.length > 0) {
416
+ const text2 = payload.context_text;
417
+ if (maxChars !== void 0 && text2.length > maxChars) {
418
+ return `${text2.slice(0, maxChars)}
419
+ [truncated to ${maxChars} characters]`;
420
+ }
421
+ return text2;
422
+ }
358
423
  const graph = obj(payload.graph);
359
424
  const rawNodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
360
425
  const shaped = rawNodes.filter(isNodeShaped);
@@ -577,15 +642,15 @@ function renderTraversal(response, maxChars) {
577
642
  const labelByUid = /* @__PURE__ */ new Map();
578
643
  for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
579
644
  if (paths.length > 0) {
580
- const items = paths.map((raw) => obj(raw)).filter((path5) => Boolean(path5)).map((path5, index) => {
581
- const uids = Array.isArray(path5.node_uids) ? path5.node_uids.map(String) : [];
582
- const labels = Array.isArray(path5.labels) ? path5.labels.map(String) : Array.isArray(path5.nodes) ? path5.nodes.map((hop) => {
645
+ const items = paths.map((raw) => obj(raw)).filter((path6) => Boolean(path6)).map((path6, index) => {
646
+ const uids = Array.isArray(path6.node_uids) ? path6.node_uids.map(String) : [];
647
+ const labels = Array.isArray(path6.labels) ? path6.labels.map(String) : Array.isArray(path6.nodes) ? path6.nodes.map((hop) => {
583
648
  const hopNode = obj(hop);
584
649
  return str(hopNode?.label) || labelByUid.get(str(hopNode?.uid) || "") || str(hop) || "?";
585
650
  }) : [];
586
651
  const route = labels.length > 0 ? labels.join(" \u2192 ") : uids.join(" \u2192 ") || "(path)";
587
- const cost = num(path5.cost) ?? num(path5.path_cost);
588
- const confidence = num(path5.path_confidence);
652
+ const cost = num(path6.cost) ?? num(path6.path_cost);
653
+ const confidence = num(path6.path_confidence);
589
654
  const meta = [
590
655
  cost !== void 0 ? `cost ${cost}` : void 0,
591
656
  confidence !== void 0 ? `confidence ${confidence}` : void 0
@@ -1587,7 +1652,7 @@ ${conversation}`
1587
1652
  }
1588
1653
 
1589
1654
  // src/memory-tool.ts
1590
- var import_node_crypto2 = require("crypto");
1655
+ var import_node_crypto3 = require("crypto");
1591
1656
  var RESULT_SCHEMA_VERSION = "mindgraph.memory.result.v1";
1592
1657
  var DEFAULT_OUTPUT_CHARS = 8e3;
1593
1658
  var MIN_OUTPUT_CHARS = 512;
@@ -1735,15 +1800,15 @@ function obj2(value) {
1735
1800
  function text(value) {
1736
1801
  return typeof value === "string" && value.length > 0 ? value : void 0;
1737
1802
  }
1738
- function stableJson(value) {
1739
- if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
1803
+ function stableJson2(value) {
1804
+ if (Array.isArray(value)) return `[${value.map(stableJson2).join(",")}]`;
1740
1805
  if (value && typeof value === "object") {
1741
- return `{${Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(",")}}`;
1806
+ return `{${Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson2(item)}`).join(",")}}`;
1742
1807
  }
1743
1808
  return JSON.stringify(value);
1744
1809
  }
1745
1810
  function sha256(value) {
1746
- return (0, import_node_crypto2.createHash)("sha256").update(value).digest("hex");
1811
+ return (0, import_node_crypto3.createHash)("sha256").update(value).digest("hex");
1747
1812
  }
1748
1813
  function outputBudget(value) {
1749
1814
  if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_OUTPUT_CHARS;
@@ -2049,7 +2114,7 @@ async function handleMemoryTool(client2, args, config = {}) {
2049
2114
  scope_uids: Array.isArray(args.scope_uids) ? args.scope_uids.map(String) : void 0
2050
2115
  });
2051
2116
  const contextId = `ctx_${sha256(
2052
- stableJson({ mode, scope, strategy, query: text(args.query), rendered_hash: renderedHash })
2117
+ stableJson2({ mode, scope, strategy, query: text(args.query), rendered_hash: renderedHash })
2053
2118
  ).slice(0, 24)}`;
2054
2119
  const bounded = candidates.length > items.length || rendered2.includes("item(s) omitted");
2055
2120
  const result = {
@@ -2210,15 +2275,15 @@ async function readGovernedResource(client2, uri, governanceConfig, memoryConfig
2210
2275
  }
2211
2276
 
2212
2277
  // src/tools.ts
2213
- var import_node_crypto5 = require("crypto");
2278
+ var import_node_crypto6 = require("crypto");
2214
2279
 
2215
2280
  // src/codegraph.ts
2216
2281
  var import_node_child_process = require("child_process");
2217
- var import_node_fs2 = require("fs");
2282
+ var import_node_fs3 = require("fs");
2218
2283
  var import_promises = require("fs/promises");
2219
- var import_node_os2 = require("os");
2220
- var import_node_path2 = require("path");
2221
- var import_node_crypto3 = require("crypto");
2284
+ var import_node_os3 = require("os");
2285
+ var import_node_path3 = require("path");
2286
+ var import_node_crypto4 = require("crypto");
2222
2287
  var UnknownRepositoryError = class extends Error {
2223
2288
  constructor(repo) {
2224
2289
  super(
@@ -2249,14 +2314,14 @@ function unavailabilityCaveats(reason, message) {
2249
2314
  var DEFAULT_TIMEOUT_MS = 5e3;
2250
2315
  var DEFAULT_STATUS_TTL_MS = 6e4;
2251
2316
  var MAX_OUTPUT_BYTES = 5 * 1024 * 1024;
2252
- function normalizePath(path5) {
2253
- return path5.replaceAll("\\", "/").replace(/^\.\//, "");
2317
+ function normalizePath(path6) {
2318
+ return path6.replaceAll("\\", "/").replace(/^\.\//, "");
2254
2319
  }
2255
2320
  function normalizeSignature(signature) {
2256
2321
  return signature.replace(/\s+/g, " ").trim();
2257
2322
  }
2258
- function languageFromPath(path5) {
2259
- const extension = (0, import_node_path2.extname)(path5).toLowerCase();
2323
+ function languageFromPath(path6) {
2324
+ const extension = (0, import_node_path3.extname)(path6).toLowerCase();
2260
2325
  return {
2261
2326
  ".c": "c",
2262
2327
  ".cc": "cpp",
@@ -2305,30 +2370,30 @@ function normalizeRemote(remote) {
2305
2370
  }
2306
2371
  }
2307
2372
  function pathContains(root, candidate) {
2308
- const child = (0, import_node_path2.relative)(root, candidate);
2309
- return child === "" || child !== ".." && !child.startsWith(`..${import_node_path2.sep}`) && !(0, import_node_path2.isAbsolute)(child);
2373
+ const child = (0, import_node_path3.relative)(root, candidate);
2374
+ return child === "" || child !== ".." && !child.startsWith(`..${import_node_path3.sep}`) && !(0, import_node_path3.isAbsolute)(child);
2310
2375
  }
2311
2376
  function nearestExistingDirectory(candidate) {
2312
- let current = (0, import_node_path2.resolve)(candidate);
2377
+ let current = (0, import_node_path3.resolve)(candidate);
2313
2378
  for (; ; ) {
2314
- if ((0, import_node_fs2.existsSync)(current)) {
2379
+ if ((0, import_node_fs3.existsSync)(current)) {
2315
2380
  try {
2316
- return (0, import_node_fs2.statSync)(current).isDirectory() ? current : (0, import_node_path2.dirname)(current);
2381
+ return (0, import_node_fs3.statSync)(current).isDirectory() ? current : (0, import_node_path3.dirname)(current);
2317
2382
  } catch {
2318
2383
  }
2319
2384
  }
2320
- const parent = (0, import_node_path2.dirname)(current);
2385
+ const parent = (0, import_node_path3.dirname)(current);
2321
2386
  if (parent === current) return current;
2322
2387
  current = parent;
2323
2388
  }
2324
2389
  }
2325
2390
  function findWorkspaceFile(start, explicit) {
2326
- if (explicit) return (0, import_node_path2.resolve)(start, explicit);
2327
- let current = (0, import_node_path2.resolve)(start);
2391
+ if (explicit) return (0, import_node_path3.resolve)(start, explicit);
2392
+ let current = (0, import_node_path3.resolve)(start);
2328
2393
  for (; ; ) {
2329
- const candidate = (0, import_node_path2.join)(current, ".mindgraph", "workspace.json");
2330
- if ((0, import_node_fs2.existsSync)(candidate)) return candidate;
2331
- const parent = (0, import_node_path2.dirname)(current);
2394
+ const candidate = (0, import_node_path3.join)(current, ".mindgraph", "workspace.json");
2395
+ if ((0, import_node_fs3.existsSync)(candidate)) return candidate;
2396
+ const parent = (0, import_node_path3.dirname)(current);
2332
2397
  if (parent === current) return void 0;
2333
2398
  current = parent;
2334
2399
  }
@@ -2415,7 +2480,7 @@ var CodegraphAdapter = class {
2415
2480
  async resolveRepository(repo, fallbackSpaceUid) {
2416
2481
  const configured = await this.resolveRepositories(fallbackSpaceUid);
2417
2482
  const selected = repo && configured.find((candidate) => candidate.repoId === repo) || repo && configured.find(
2418
- (candidate) => candidate.repoRoot === repo || (0, import_node_path2.basename)(candidate.repoRoot) === repo
2483
+ (candidate) => candidate.repoRoot === repo || (0, import_node_path3.basename)(candidate.repoRoot) === repo
2419
2484
  ) || (!repo ? [...configured].sort((a, b) => b.repoRoot.length - a.repoRoot.length).find((candidate) => pathContains(candidate.repoRoot, this.cwd)) : void 0) || (!repo && configured.length === 1 ? configured[0] : void 0);
2420
2485
  if (selected) {
2421
2486
  return !repo && this.env.MINDGRAPH_REPO_ID ? this.resolveRepositoryIdentity(
@@ -2425,10 +2490,10 @@ var CodegraphAdapter = class {
2425
2490
  ) : selected;
2426
2491
  }
2427
2492
  if (repo) {
2428
- const requestedPath = await (0, import_promises.realpath)((0, import_node_path2.resolve)(this.cwd, repo)).catch(
2429
- () => (0, import_node_path2.resolve)(this.cwd, repo)
2493
+ const requestedPath = await (0, import_promises.realpath)((0, import_node_path3.resolve)(this.cwd, repo)).catch(
2494
+ () => (0, import_node_path3.resolve)(this.cwd, repo)
2430
2495
  );
2431
- if (!(0, import_node_fs2.existsSync)(requestedPath)) {
2496
+ if (!(0, import_node_fs3.existsSync)(requestedPath)) {
2432
2497
  const current = await this.currentRepository(fallbackSpaceUid);
2433
2498
  if (current.repoId === repo) return current;
2434
2499
  throw new UnknownRepositoryError(repo);
@@ -2444,8 +2509,8 @@ var CodegraphAdapter = class {
2444
2509
  }
2445
2510
  async currentRepository(fallbackSpaceUid) {
2446
2511
  const requestedRoot = this.env.MINDGRAPH_REPO_ROOT || this.cwd;
2447
- const requestedPath = await (0, import_promises.realpath)((0, import_node_path2.resolve)(this.cwd, requestedRoot)).catch(
2448
- () => (0, import_node_path2.resolve)(this.cwd, requestedRoot)
2512
+ const requestedPath = await (0, import_promises.realpath)((0, import_node_path3.resolve)(this.cwd, requestedRoot)).catch(
2513
+ () => (0, import_node_path3.resolve)(this.cwd, requestedRoot)
2449
2514
  );
2450
2515
  const repoRoot = await this.gitRoot(requestedPath) ?? requestedPath;
2451
2516
  return this.resolveRepositoryIdentity(
@@ -2466,7 +2531,7 @@ var CodegraphAdapter = class {
2466
2531
  );
2467
2532
  }
2468
2533
  async resolveRepositoryIdentity(root, explicitRepoId, spaceUid) {
2469
- const repoRoot = await (0, import_promises.realpath)(root).catch(() => (0, import_node_path2.resolve)(root));
2534
+ const repoRoot = await (0, import_promises.realpath)(root).catch(() => (0, import_node_path3.resolve)(root));
2470
2535
  const cacheKey = `${repoRoot}\0${explicitRepoId ?? ""}\0${spaceUid ?? ""}`;
2471
2536
  const cached = this.identityCache.get(cacheKey);
2472
2537
  if (cached) return cached;
@@ -2496,20 +2561,20 @@ var CodegraphAdapter = class {
2496
2561
  this.env
2497
2562
  ).catch(() => "");
2498
2563
  if (!rawCommonDir.trim()) {
2499
- return `local:${(0, import_node_path2.basename)(repoRoot)}`;
2564
+ return `local:${(0, import_node_path3.basename)(repoRoot)}`;
2500
2565
  }
2501
- const commonDir = (0, import_node_path2.resolve)(repoRoot, rawCommonDir.trim());
2502
- const identityDir = (0, import_node_path2.join)(commonDir, "mindgraph");
2503
- const identityFile = (0, import_node_path2.join)(identityDir, "repository-id");
2566
+ const commonDir = (0, import_node_path3.resolve)(repoRoot, rawCommonDir.trim());
2567
+ const identityDir = (0, import_node_path3.join)(commonDir, "mindgraph");
2568
+ const identityFile = (0, import_node_path3.join)(identityDir, "repository-id");
2504
2569
  try {
2505
- const existing = (0, import_node_fs2.readFileSync)(identityFile, "utf8").trim();
2570
+ const existing = (0, import_node_fs3.readFileSync)(identityFile, "utf8").trim();
2506
2571
  if (existing) return existing;
2507
2572
  } catch {
2508
2573
  }
2509
- (0, import_node_fs2.mkdirSync)(identityDir, { recursive: true, mode: 448 });
2510
- const generated = (0, import_node_crypto3.randomUUID)();
2574
+ (0, import_node_fs3.mkdirSync)(identityDir, { recursive: true, mode: 448 });
2575
+ const generated = (0, import_node_crypto4.randomUUID)();
2511
2576
  try {
2512
- (0, import_node_fs2.writeFileSync)(identityFile, `${generated}
2577
+ (0, import_node_fs3.writeFileSync)(identityFile, `${generated}
2513
2578
  `, {
2514
2579
  encoding: "utf8",
2515
2580
  flag: "wx",
@@ -2518,7 +2583,7 @@ var CodegraphAdapter = class {
2518
2583
  return generated;
2519
2584
  } catch (cause) {
2520
2585
  if (cause.code !== "EEXIST") throw cause;
2521
- const winner = (0, import_node_fs2.readFileSync)(identityFile, "utf8").trim();
2586
+ const winner = (0, import_node_fs3.readFileSync)(identityFile, "utf8").trim();
2522
2587
  if (!winner) throw new Error(`empty repository identity file: ${identityFile}`);
2523
2588
  return winner;
2524
2589
  }
@@ -2531,12 +2596,12 @@ var CodegraphAdapter = class {
2531
2596
  Math.min(this.timeoutMs, 2e3),
2532
2597
  this.env
2533
2598
  ).catch(() => "");
2534
- return raw.trim() ? (0, import_node_path2.resolve)(start, raw.trim()) : void 0;
2599
+ return raw.trim() ? (0, import_node_path3.resolve)(start, raw.trim()) : void 0;
2535
2600
  }
2536
2601
  /** Why the workspace map yielded no repositories — for callers that were
2537
2602
  * explicitly asked to fan out and would otherwise return silently empty. */
2538
2603
  workspaceDiagnostic() {
2539
- if (!this.workspaceFile || !(0, import_node_fs2.existsSync)(this.workspaceFile)) {
2604
+ if (!this.workspaceFile || !(0, import_node_fs3.existsSync)(this.workspaceFile)) {
2540
2605
  return "no .mindgraph/workspace.json was found between the working directory and the filesystem root";
2541
2606
  }
2542
2607
  return `${this.workspaceFile} is malformed or declares no repositories`;
@@ -2656,7 +2721,7 @@ var CodegraphAdapter = class {
2656
2721
  return this.resolveRepository(ref.repo || defaultRepo, fallbackSpaceUid);
2657
2722
  }
2658
2723
  if (ref.path) {
2659
- const requested = (0, import_node_path2.isAbsolute)(ref.path) ? (0, import_node_path2.resolve)(ref.path) : (0, import_node_path2.resolve)(this.cwd, ref.path);
2724
+ const requested = (0, import_node_path3.isAbsolute)(ref.path) ? (0, import_node_path3.resolve)(ref.path) : (0, import_node_path3.resolve)(this.cwd, ref.path);
2660
2725
  const absolute = await (0, import_promises.realpath)(requested).catch(() => requested);
2661
2726
  const configured = await this.resolveRepositories(fallbackSpaceUid);
2662
2727
  const defining = [...configured].sort((a, b) => b.repoRoot.length - a.repoRoot.length).find((candidate) => pathContains(candidate.repoRoot, absolute));
@@ -2733,19 +2798,19 @@ var CodegraphAdapter = class {
2733
2798
  }
2734
2799
  configuredRepositories() {
2735
2800
  const configured = [];
2736
- if (this.workspaceFile && (0, import_node_fs2.existsSync)(this.workspaceFile)) {
2801
+ if (this.workspaceFile && (0, import_node_fs3.existsSync)(this.workspaceFile)) {
2737
2802
  try {
2738
2803
  const parsed = JSON.parse(
2739
- (0, import_node_fs2.readFileSync)(this.workspaceFile, "utf8")
2804
+ (0, import_node_fs3.readFileSync)(this.workspaceFile, "utf8")
2740
2805
  );
2741
2806
  if (parsed.v === 1 && Array.isArray(parsed.repositories)) {
2742
- const configDirectory = (0, import_node_path2.dirname)(this.workspaceFile);
2743
- const base = (0, import_node_path2.basename)(configDirectory) === ".mindgraph" ? (0, import_node_path2.dirname)(configDirectory) : configDirectory;
2807
+ const configDirectory = (0, import_node_path3.dirname)(this.workspaceFile);
2808
+ const base = (0, import_node_path3.basename)(configDirectory) === ".mindgraph" ? (0, import_node_path3.dirname)(configDirectory) : configDirectory;
2744
2809
  for (const repository of parsed.repositories) {
2745
2810
  if (!repository || typeof repository.root !== "string") continue;
2746
2811
  configured.push({
2747
2812
  repoId: typeof repository.repo_id === "string" ? repository.repo_id : void 0,
2748
- repoRoot: (0, import_node_path2.resolve)(base, repository.root),
2813
+ repoRoot: (0, import_node_path3.resolve)(base, repository.root),
2749
2814
  spaceUid: typeof repository.space_uid === "string" ? repository.space_uid : void 0
2750
2815
  });
2751
2816
  }
@@ -2760,7 +2825,7 @@ var CodegraphAdapter = class {
2760
2825
  for (const [repoId, config] of Object.entries(parsed)) {
2761
2826
  configured.push({
2762
2827
  repoId,
2763
- repoRoot: (0, import_node_path2.resolve)(
2828
+ repoRoot: (0, import_node_path3.resolve)(
2764
2829
  this.cwd,
2765
2830
  typeof config === "string" ? config : config.root
2766
2831
  ),
@@ -2788,9 +2853,9 @@ var CodegraphAdapter = class {
2788
2853
  const hits = await this.runJson(args);
2789
2854
  let candidates = hits.map(asNode).map(toCandidate).filter((item) => item !== null);
2790
2855
  if (ref.path) {
2791
- const absolutePath = (0, import_node_path2.isAbsolute)(ref.path) ? await (0, import_promises.realpath)(ref.path).catch(() => ref.path) : void 0;
2856
+ const absolutePath = (0, import_node_path3.isAbsolute)(ref.path) ? await (0, import_promises.realpath)(ref.path).catch(() => ref.path) : void 0;
2792
2857
  const wanted = normalizePath(
2793
- absolutePath ? (0, import_node_path2.relative)(repository.repoRoot, absolutePath) : ref.path
2858
+ absolutePath ? (0, import_node_path3.relative)(repository.repoRoot, absolutePath) : ref.path
2794
2859
  );
2795
2860
  candidates = candidates.filter((candidate) => candidate.path === wanted);
2796
2861
  }
@@ -2891,7 +2956,7 @@ var CodegraphAdapter = class {
2891
2956
  const candidates = [
2892
2957
  this.env.MINDGRAPH_CODEGRAPH_BIN,
2893
2958
  "codegraph",
2894
- (0, import_node_path2.resolve)((0, import_node_os2.homedir)(), ".local", "bin", "codegraph")
2959
+ (0, import_node_path3.resolve)((0, import_node_os3.homedir)(), ".local", "bin", "codegraph")
2895
2960
  ].filter((candidate, index, all) => {
2896
2961
  return Boolean(candidate) && all.indexOf(candidate) === index;
2897
2962
  });
@@ -3492,9 +3557,9 @@ function primaryUids(value) {
3492
3557
  }
3493
3558
 
3494
3559
  // src/sync-tool.ts
3495
- var import_node_crypto4 = __toESM(require("crypto"));
3496
- var import_node_fs3 = __toESM(require("fs"));
3497
- var import_node_path3 = __toESM(require("path"));
3560
+ var import_node_crypto5 = __toESM(require("crypto"));
3561
+ var import_node_fs4 = __toESM(require("fs"));
3562
+ var import_node_path4 = __toESM(require("path"));
3498
3563
  var ok3 = (data) => ({
3499
3564
  content: [{ type: "text", text: JSON.stringify(data) }]
3500
3565
  });
@@ -3571,12 +3636,12 @@ var SYNC_TOOL = {
3571
3636
  }
3572
3637
  };
3573
3638
  function safePath(root, candidate) {
3574
- const absolute = import_node_path3.default.resolve(root, candidate);
3575
- const relative2 = import_node_path3.default.relative(root, absolute);
3576
- if (relative2.startsWith("..") || import_node_path3.default.isAbsolute(relative2)) {
3639
+ const absolute = import_node_path4.default.resolve(root, candidate);
3640
+ const relative2 = import_node_path4.default.relative(root, absolute);
3641
+ if (relative2.startsWith("..") || import_node_path4.default.isAbsolute(relative2)) {
3577
3642
  throw new Error(`path escapes repository root: ${candidate}`);
3578
3643
  }
3579
- if (import_node_fs3.default.existsSync(absolute)) {
3644
+ if (import_node_fs4.default.existsSync(absolute)) {
3580
3645
  if (!pathContains(realPathOf(root), realPathOf(absolute))) {
3581
3646
  throw new Error(`path escapes repository root via symlink: ${candidate}`);
3582
3647
  }
@@ -3585,26 +3650,26 @@ function safePath(root, candidate) {
3585
3650
  }
3586
3651
  function realPathOf(target) {
3587
3652
  try {
3588
- return import_node_fs3.default.realpathSync(target);
3653
+ return import_node_fs4.default.realpathSync(target);
3589
3654
  } catch {
3590
- return import_node_path3.default.resolve(target);
3655
+ return import_node_path4.default.resolve(target);
3591
3656
  }
3592
3657
  }
3593
3658
  function markdownFiles(target, realRoot, visited = /* @__PURE__ */ new Set()) {
3594
- if (!import_node_fs3.default.existsSync(target)) return [];
3659
+ if (!import_node_fs4.default.existsSync(target)) return [];
3595
3660
  const real = realPathOf(target);
3596
3661
  if (!pathContains(realRoot, real)) return [];
3597
- const stat = import_node_fs3.default.statSync(target);
3662
+ const stat = import_node_fs4.default.statSync(target);
3598
3663
  if (stat.isFile()) return target.endsWith(".md") ? [target] : [];
3599
3664
  if (!stat.isDirectory() || visited.has(real)) return [];
3600
3665
  visited.add(real);
3601
- return import_node_fs3.default.readdirSync(target, { withFileTypes: true }).flatMap((entry) => markdownFiles(import_node_path3.default.join(target, entry.name), realRoot, visited));
3666
+ return import_node_fs4.default.readdirSync(target, { withFileTypes: true }).flatMap((entry) => markdownFiles(import_node_path4.default.join(target, entry.name), realRoot, visited));
3602
3667
  }
3603
3668
  function fileDescriptor(root, absolute) {
3604
- const content = import_node_fs3.default.readFileSync(absolute, "utf8");
3669
+ const content = import_node_fs4.default.readFileSync(absolute, "utf8");
3605
3670
  return {
3606
- logical_path: import_node_path3.default.relative(root, absolute).split(import_node_path3.default.sep).join("/"),
3607
- content_hash: import_node_crypto4.default.createHash("sha256").update(content).digest("hex"),
3671
+ logical_path: import_node_path4.default.relative(root, absolute).split(import_node_path4.default.sep).join("/"),
3672
+ content_hash: import_node_crypto5.default.createHash("sha256").update(content).digest("hex"),
3608
3673
  bytes: Buffer.byteLength(content),
3609
3674
  content
3610
3675
  };
@@ -3673,7 +3738,7 @@ async function handleSyncTool(client2, args) {
3673
3738
  }
3674
3739
  if (action === "status" && request.logical_path && request.file_present !== false) {
3675
3740
  const absolute = safePath(root, String(request.logical_path));
3676
- request.file_present = import_node_fs3.default.existsSync(absolute);
3741
+ request.file_present = import_node_fs4.default.existsSync(absolute);
3677
3742
  if (request.file_present && !request.content_hash) {
3678
3743
  request.content_hash = fileDescriptor(root, absolute).content_hash;
3679
3744
  }
@@ -3746,10 +3811,10 @@ var RESOLUTION_PROPERTIES = {
3746
3811
  description: "Alias for confirm_new on a near-duplicate response"
3747
3812
  }
3748
3813
  };
3749
- function stableJson2(value) {
3750
- if (Array.isArray(value)) return `[${value.map(stableJson2).join(",")}]`;
3814
+ function stableJson3(value) {
3815
+ if (Array.isArray(value)) return `[${value.map(stableJson3).join(",")}]`;
3751
3816
  if (value && typeof value === "object") {
3752
- return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson2(item)}`).join(",")}}`;
3817
+ return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson3(item)}`).join(",")}}`;
3753
3818
  }
3754
3819
  return JSON.stringify(value);
3755
3820
  }
@@ -3763,8 +3828,8 @@ function defaultWriteIdempotencyKey(tool, args) {
3763
3828
  ([key]) => key !== "invocation_context" && key !== "idempotency_key"
3764
3829
  )
3765
3830
  );
3766
- return (0, import_node_crypto5.createHash)("sha256").update(
3767
- stableJson2({
3831
+ return (0, import_node_crypto6.createHash)("sha256").update(
3832
+ stableJson3({
3768
3833
  harness_session_id: sessionId,
3769
3834
  harness_turn_id: context?.harnessTurnId ?? null,
3770
3835
  tool,
@@ -4476,6 +4541,15 @@ var TOOLS = [
4476
4541
  type: "string",
4477
4542
  description: "ISO-8601 date (e.g. '2021-06') for context: nodes with validity windows are annotated as of this date instead of today"
4478
4543
  },
4544
+ as_of: {
4545
+ type: "string",
4546
+ description: "ISO-8601 date for context: answer with what had been STATED by this date (a node is admitted when one of its sources was stated on or before it; fact dates never exclude it). Use for 'what did we know / what was the value as of <date>' questions."
4547
+ },
4548
+ retrieval_mode: {
4549
+ type: "string",
4550
+ enum: ["normal", "exhaustive"],
4551
+ description: "context: 'exhaustive' widens retrieval for list/count questions ('how many different \u2026 across', 'what two \u2026'); omitted lets the server's cue decide."
4552
+ },
4479
4553
  created_after: {
4480
4554
  type: "number",
4481
4555
  description: "Unix seconds \u2014 'recent' action: only nodes ingested after this time"
@@ -5704,6 +5778,8 @@ async function handleRetrieve(client2, args, profile) {
5704
5778
  salience_min,
5705
5779
  article_limit,
5706
5780
  valid_at,
5781
+ as_of,
5782
+ retrieval_mode,
5707
5783
  created_after,
5708
5784
  created_before,
5709
5785
  explain,
@@ -5732,9 +5808,15 @@ async function handleRetrieve(client2, args, profile) {
5732
5808
  // the advertised knob must not get a silent no-op).
5733
5809
  ...article_limit !== void 0 ? { article_limit } : include_documents === false || profile === "coding" && !include_documents ? { article_limit: 0 } : {},
5734
5810
  valid_at,
5811
+ as_of,
5812
+ retrieval_mode,
5735
5813
  include_graph,
5736
5814
  graph_expansion_limit: graph_expansion_limit ?? 3,
5737
- graph_max_depth: graph_max_depth ?? 2
5815
+ graph_max_depth: graph_max_depth ?? 2,
5816
+ // Evidence plan R2: the server's canonical rendering under a token
5817
+ // budget derived from the character budget; the local renderer is
5818
+ // the fallback for servers that predate it.
5819
+ ...format === "json" ? {} : { format: "text", max_tokens: Math.max(256, Math.floor((max_output_chars ?? 24e3) / 4)) }
5738
5820
  }),
5739
5821
  (raw) => renderContext(raw, max_output_chars)
5740
5822
  );
@@ -6208,7 +6290,12 @@ function buildMindGraphServer(wiring) {
6208
6290
  });
6209
6291
  server2.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
6210
6292
  const { name, arguments: args } = request.params;
6211
- const rawArgs = { ...args || {} };
6293
+ const rawArgs = {
6294
+ ...args || {},
6295
+ // H1: session, lease and invocation fields the local hook recorded for
6296
+ // this exact call (stdio only; hosted mode has no local hook).
6297
+ ...hosted ? {} : wiring.resolveHookFields?.(name, args || {})
6298
+ };
6212
6299
  const toolArgs = { ...rawArgs };
6213
6300
  let governanceConfig = wiring.governance;
6214
6301
  if (hosted) {
@@ -6273,13 +6360,19 @@ function buildMindGraphServer(wiring) {
6273
6360
  }
6274
6361
 
6275
6362
  // src/index.ts
6276
- var import_node_path5 = __toESM(require("path"));
6363
+ var import_node_path6 = __toESM(require("path"));
6277
6364
 
6278
6365
  // src/hook-installer.ts
6279
- var import_node_fs4 = __toESM(require("fs"));
6280
- var import_node_os3 = __toESM(require("os"));
6281
- var import_node_path4 = __toESM(require("path"));
6282
- var import_node_crypto6 = __toESM(require("crypto"));
6366
+ var import_node_fs5 = __toESM(require("fs"));
6367
+ var import_node_os4 = __toESM(require("os"));
6368
+ var import_node_path5 = __toESM(require("path"));
6369
+ var import_node_crypto7 = __toESM(require("crypto"));
6370
+ function storedFeatures(homeDir) {
6371
+ const { promptRecall, capture } = loadHookEnv(
6372
+ import_node_path5.default.join(homeDir || import_node_os4.default.homedir(), ".mindgraph")
6373
+ );
6374
+ return { promptRecall, capture };
6375
+ }
6283
6376
  var OWNER_MARKER = "--owner mindgraph";
6284
6377
  var RUNNER_RELATIVE = ".mindgraph/bin/mindgraph-hook.cjs";
6285
6378
  function command(harness) {
@@ -6290,24 +6383,24 @@ function windowsCommand(harness) {
6290
6383
  }
6291
6384
  var RUNNER_VERSION_RELATIVE = `${RUNNER_RELATIVE}.version`;
6292
6385
  function installHookRunner(sourceFile, homeDir, version) {
6293
- const target = import_node_path4.default.join(homeDir || import_node_os3.default.homedir(), RUNNER_RELATIVE);
6294
- import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(target), { recursive: true });
6295
- const temporary = `${target}.${process.pid}.${import_node_crypto6.default.randomUUID()}.tmp`;
6296
- import_node_fs4.default.copyFileSync(sourceFile, temporary);
6297
- import_node_fs4.default.renameSync(temporary, target);
6298
- const versionPath = import_node_path4.default.join(homeDir || import_node_os3.default.homedir(), RUNNER_VERSION_RELATIVE);
6386
+ const target = import_node_path5.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_RELATIVE);
6387
+ import_node_fs5.default.mkdirSync(import_node_path5.default.dirname(target), { recursive: true });
6388
+ const temporary = `${target}.${process.pid}.${import_node_crypto7.default.randomUUID()}.tmp`;
6389
+ import_node_fs5.default.copyFileSync(sourceFile, temporary);
6390
+ import_node_fs5.default.renameSync(temporary, target);
6391
+ const versionPath = import_node_path5.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_VERSION_RELATIVE);
6299
6392
  if (version) {
6300
- import_node_fs4.default.writeFileSync(versionPath, `${version}
6393
+ import_node_fs5.default.writeFileSync(versionPath, `${version}
6301
6394
  `);
6302
6395
  } else {
6303
- import_node_fs4.default.rmSync(versionPath, { force: true });
6396
+ import_node_fs5.default.rmSync(versionPath, { force: true });
6304
6397
  }
6305
6398
  return target;
6306
6399
  }
6307
6400
  function installedHookRunnerVersion(homeDir) {
6308
6401
  try {
6309
- const raw = import_node_fs4.default.readFileSync(
6310
- import_node_path4.default.join(homeDir || import_node_os3.default.homedir(), RUNNER_VERSION_RELATIVE),
6402
+ const raw = import_node_fs5.default.readFileSync(
6403
+ import_node_path5.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_VERSION_RELATIVE),
6311
6404
  "utf8"
6312
6405
  ).trim();
6313
6406
  return raw || void 0;
@@ -6332,11 +6425,11 @@ function refreshHookRunner(serverVersion, sourceBundle, homeDir) {
6332
6425
  if (process.env.MINDGRAPH_HOOK_AUTOUPDATE === "off") {
6333
6426
  return { refreshed: false, reason: "disabled" };
6334
6427
  }
6335
- const home = homeDir || import_node_os3.default.homedir();
6336
- if (!import_node_fs4.default.existsSync(import_node_path4.default.join(home, RUNNER_RELATIVE))) {
6428
+ const home = homeDir || import_node_os4.default.homedir();
6429
+ if (!import_node_fs5.default.existsSync(import_node_path5.default.join(home, RUNNER_RELATIVE))) {
6337
6430
  return { refreshed: false, reason: "no_runner" };
6338
6431
  }
6339
- if (!import_node_fs4.default.existsSync(sourceBundle)) {
6432
+ if (!import_node_fs5.default.existsSync(sourceBundle)) {
6340
6433
  return { refreshed: false, reason: "no_source" };
6341
6434
  }
6342
6435
  const server2 = parseSemver(serverVersion);
@@ -6367,9 +6460,16 @@ function refreshOwnedClaudeHooks(projectDir = process.cwd(), homeDir) {
6367
6460
  for (const scope of scopes) {
6368
6461
  try {
6369
6462
  const file = settingsPath("claude-code", scope, projectDir, void 0, homeDir);
6370
- if (!import_node_fs4.default.existsSync(file)) continue;
6371
- if (!import_node_fs4.default.readFileSync(file, "utf8").includes(OWNER_MARKER)) continue;
6372
- installHooks("claude-code", scope, projectDir, void 0, homeDir);
6463
+ if (!import_node_fs5.default.existsSync(file)) continue;
6464
+ if (!import_node_fs5.default.readFileSync(file, "utf8").includes(OWNER_MARKER)) continue;
6465
+ installHooks(
6466
+ "claude-code",
6467
+ scope,
6468
+ projectDir,
6469
+ void 0,
6470
+ homeDir,
6471
+ storedFeatures(homeDir)
6472
+ );
6373
6473
  refreshed.push(file);
6374
6474
  } catch {
6375
6475
  }
@@ -6381,15 +6481,15 @@ function versionSkewNote(serverVersion, homeDir) {
6381
6481
  if (!runnerVersion || runnerVersion === serverVersion) return void 0;
6382
6482
  return `NOTE: the installed MindGraph hook runner is v${runnerVersion} but this MCP server is v${serverVersion} \u2014 hook behavior and tool contracts may be out of sync. Tell the user to run: npx -y mindgraph-mcp@latest install-hooks (refreshes the pinned runner).`;
6383
6483
  }
6384
- function settingsPath(harness, scope, projectDir = process.cwd(), codexHomeDir = process.env.CODEX_HOME || import_node_path4.default.join(import_node_os3.default.homedir(), ".codex"), homeDir) {
6484
+ function settingsPath(harness, scope, projectDir = process.cwd(), codexHomeDir = process.env.CODEX_HOME || import_node_path5.default.join(import_node_os4.default.homedir(), ".codex"), homeDir) {
6385
6485
  if (harness === "codex") {
6386
- return scope === "user" ? import_node_path4.default.join(codexHomeDir, "hooks.json") : import_node_path4.default.join(projectDir, ".codex", "hooks.json");
6486
+ return scope === "user" ? import_node_path5.default.join(codexHomeDir, "hooks.json") : import_node_path5.default.join(projectDir, ".codex", "hooks.json");
6387
6487
  }
6388
- return scope === "user" ? import_node_path4.default.join(homeDir || import_node_os3.default.homedir(), ".claude", "settings.json") : import_node_path4.default.join(projectDir, ".claude", "settings.json");
6488
+ return scope === "user" ? import_node_path5.default.join(homeDir || import_node_os4.default.homedir(), ".claude", "settings.json") : import_node_path5.default.join(projectDir, ".claude", "settings.json");
6389
6489
  }
6390
6490
  function readSettings(file) {
6391
- if (!import_node_fs4.default.existsSync(file)) return {};
6392
- return JSON.parse(import_node_fs4.default.readFileSync(file, "utf8"));
6491
+ if (!import_node_fs5.default.existsSync(file)) return {};
6492
+ return JSON.parse(import_node_fs5.default.readFileSync(file, "utf8"));
6393
6493
  }
6394
6494
  function ownedHook(harness, timeout, additionalContextLimit) {
6395
6495
  return {
@@ -6401,7 +6501,8 @@ function ownedHook(harness, timeout, additionalContextLimit) {
6401
6501
  ...additionalContextLimit === void 0 ? {} : { additionalContextLimit }
6402
6502
  };
6403
6503
  }
6404
- function desiredEntries(harness) {
6504
+ var CLAUDE_POST_TOOL_MATCHER = "mcp__mindgraph__.*|Edit|MultiEdit|Write|NotebookEdit|Bash|Task|Agent";
6505
+ function desiredEntries(harness, features = {}) {
6405
6506
  const common = {
6406
6507
  SessionStart: [
6407
6508
  {
@@ -6433,7 +6534,17 @@ function desiredEntries(harness) {
6433
6534
  ],
6434
6535
  // PostToolUse awaits the lease renewal round-trip, which fires exactly
6435
6536
  // when the tenant is cold (post-idle reclaim) — 5s killed it mid-claim.
6436
- PostToolUse: [{ matcher: ".*", hooks: [ownedHook(harness, 10)] }],
6537
+ // Claude Code: only the tools the hook acts on (MindGraph calls, edits,
6538
+ // and the shell/agent calls that keep a lease alive). Read, Grep and Glob
6539
+ // are most of a session's tool calls and the hook did nothing useful for
6540
+ // them but start a process (roadmap H8). Codex names its tools
6541
+ // differently, so it keeps the catch-all.
6542
+ PostToolUse: [
6543
+ {
6544
+ matcher: harness === "claude-code" ? CLAUDE_POST_TOOL_MATCHER : ".*",
6545
+ hooks: [ownedHook(harness, 10)]
6546
+ }
6547
+ ],
6437
6548
  Stop: [{ hooks: [ownedHook(harness, 5)] }],
6438
6549
  // SessionEnd makes up to two sequential cloud calls (abandon_iteration +
6439
6550
  // session close) — the SAME cold-tenant latency SessionStart needs 30s
@@ -6444,11 +6555,17 @@ function desiredEntries(harness) {
6444
6555
  { hooks: [ownedHook(harness, harness === "codex" ? 3 : 30)] }
6445
6556
  ]
6446
6557
  };
6447
- return harness === "claude-code" ? {
6558
+ if (harness !== "claude-code") return common;
6559
+ return {
6448
6560
  ...common,
6449
6561
  TaskCreated: [{ hooks: [ownedHook(harness, 3)] }],
6450
- TaskCompleted: [{ hooks: [ownedHook(harness, 3)] }]
6451
- } : common;
6562
+ TaskCompleted: [{ hooks: [ownedHook(harness, 3)] }],
6563
+ // Bounded recall per prompt: the core gives up at 4s and fails open, so
6564
+ // a 5s budget never holds the prompt longer than that.
6565
+ ...features.promptRecall !== false ? { UserPromptSubmit: [{ hooks: [ownedHook(harness, 5)] }] } : {},
6566
+ // Opt-in transcript capture before compaction (cold-tenant ingest).
6567
+ ...features.capture === true ? { PreCompact: [{ hooks: [ownedHook(harness, 30)] }] } : {}
6568
+ };
6452
6569
  }
6453
6570
  function hookCommand(value) {
6454
6571
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
@@ -6461,21 +6578,30 @@ function entryHasOwnedHook(value) {
6461
6578
  return Array.isArray(hooks) && hooks.some((hook) => hookCommand(hook)?.includes(OWNER_MARKER));
6462
6579
  }
6463
6580
  function writeSettings(file, settings) {
6464
- import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(file), { recursive: true });
6465
- const temporary = `${file}.${process.pid}.${import_node_crypto6.default.randomUUID()}.tmp`;
6466
- import_node_fs4.default.writeFileSync(temporary, `${JSON.stringify(settings, null, 2)}
6581
+ import_node_fs5.default.mkdirSync(import_node_path5.default.dirname(file), { recursive: true });
6582
+ const temporary = `${file}.${process.pid}.${import_node_crypto7.default.randomUUID()}.tmp`;
6583
+ import_node_fs5.default.writeFileSync(temporary, `${JSON.stringify(settings, null, 2)}
6467
6584
  `, {
6468
6585
  mode: 384
6469
6586
  });
6470
- import_node_fs4.default.renameSync(temporary, file);
6587
+ import_node_fs5.default.renameSync(temporary, file);
6471
6588
  }
6472
- function installHooks(harness, scope, projectDir = process.cwd(), codexHomeDir, homeDir) {
6589
+ function installHooks(harness, scope, projectDir = process.cwd(), codexHomeDir, homeDir, features = {}) {
6473
6590
  const file = settingsPath(harness, scope, projectDir, codexHomeDir, homeDir);
6474
6591
  const settings = readSettings(file);
6475
6592
  const hooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
6476
6593
  let added = 0;
6477
6594
  let updated = 0;
6478
- for (const [event, wanted] of Object.entries(desiredEntries(harness))) {
6595
+ const desired = desiredEntries(harness, features);
6596
+ for (const optional of ["UserPromptSubmit", "PreCompact"]) {
6597
+ if (optional in desired || !Array.isArray(hooks[optional])) continue;
6598
+ const kept = hooks[optional].filter(
6599
+ (entry) => !entryHasOwnedHook(entry)
6600
+ );
6601
+ if (kept.length > 0) hooks[optional] = kept;
6602
+ else delete hooks[optional];
6603
+ }
6604
+ for (const [event, wanted] of Object.entries(desired)) {
6479
6605
  const current = Array.isArray(hooks[event]) ? [...hooks[event]] : [];
6480
6606
  const kept = current.filter((entry) => !entryHasOwnedHook(entry));
6481
6607
  const hadOwned = kept.length !== current.length;
@@ -6583,7 +6709,7 @@ if (PROFILE === "coding") {
6583
6709
  - Capture corrections, decisions with rationale, fixes that survived meaningful failed attempts, durable invariants/preferences, and blockers with evidence.
6584
6710
  - If a write returns \`near_duplicate\`, choose \`reuse_uid\` or \`supersedes_uid\`; use \`confirm_new\` only for genuinely distinct facts.
6585
6711
  - Do not capture routine mechanics, transient command output, facts cheaply derivable from code, unsupported speculation as high-confidence lessons, or completion without evidence.
6586
- - Adapter context is owned by ${HARNESS}; omit invocation_context because hooks replace it.`;
6712
+ - Adapter context is owned by ${HARNESS}; omit invocation_context because the adapter supplies it.`;
6587
6713
  } else {
6588
6714
  INSTRUCTIONS = INSTRUCTIONS.split("\n").filter(
6589
6715
  (line) => !line.includes("mindgraph_code") && !line.includes("`code`") && !line.includes("Typed code anchors")
@@ -6591,7 +6717,7 @@ if (PROFILE === "coding") {
6591
6717
  }
6592
6718
  var RUNNER_REFRESH = (() => {
6593
6719
  try {
6594
- return refreshHookRunner("0.22.0", import_node_path5.default.join(__dirname, "cli.js"));
6720
+ return refreshHookRunner("0.24.0", import_node_path6.default.join(__dirname, "cli.js"));
6595
6721
  } catch {
6596
6722
  return void 0;
6597
6723
  }
@@ -6605,12 +6731,12 @@ if (RUNNER_REFRESH?.refreshed) {
6605
6731
  }
6606
6732
  })();
6607
6733
  console.error(
6608
- `[mindgraph-mcp] refreshed pinned hook runner ${RUNNER_REFRESH.runnerVersion ? `v${RUNNER_REFRESH.runnerVersion}` : "(pre-sidecar)"} \u2192 v${"0.22.0"}` + (refreshedSettings.length > 0 ? `; refreshed owned hook entries in ${refreshedSettings.join(", ")}` : "")
6734
+ `[mindgraph-mcp] refreshed pinned hook runner ${RUNNER_REFRESH.runnerVersion ? `v${RUNNER_REFRESH.runnerVersion}` : "(pre-sidecar)"} \u2192 v${"0.24.0"}` + (refreshedSettings.length > 0 ? `; refreshed owned hook entries in ${refreshedSettings.join(", ")}` : "")
6609
6735
  );
6610
6736
  }
6611
6737
  var HOOK_SKEW_NOTE = (() => {
6612
6738
  try {
6613
- return versionSkewNote("0.22.0");
6739
+ return versionSkewNote("0.24.0");
6614
6740
  } catch {
6615
6741
  return void 0;
6616
6742
  }
@@ -6631,7 +6757,7 @@ var GOVERNANCE_CONFIG = {
6631
6757
  }
6632
6758
  };
6633
6759
  var MEMORY_RUNTIME_CONFIG = {
6634
- serverVersion: "0.22.0",
6760
+ serverVersion: "0.24.0",
6635
6761
  baseUrl: BASE_URL,
6636
6762
  profile: PROFILE,
6637
6763
  harness: HARNESS,
@@ -6640,18 +6766,19 @@ var MEMORY_RUNTIME_CONFIG = {
6640
6766
  };
6641
6767
  var server = buildMindGraphServer({
6642
6768
  client,
6643
- serverVersion: "0.22.0",
6769
+ serverVersion: "0.24.0",
6644
6770
  instructions: INSTRUCTIONS,
6645
6771
  profile: PROFILE,
6646
6772
  agentId: AGENT_ID,
6647
6773
  governance: GOVERNANCE_CONFIG,
6648
- memory: MEMORY_RUNTIME_CONFIG
6774
+ memory: MEMORY_RUNTIME_CONFIG,
6775
+ resolveHookFields: (name, args) => resolvePendingCall(name, args)
6649
6776
  });
6650
6777
  async function main() {
6651
6778
  const transport = new import_stdio.StdioServerTransport();
6652
6779
  await server.connect(transport);
6653
6780
  console.error(
6654
- `MindGraph MCP server v${"0.22.0"} running on stdio (${BASE_URL})`
6781
+ `MindGraph MCP server v${"0.24.0"} running on stdio (${BASE_URL})`
6655
6782
  );
6656
6783
  }
6657
6784
  main().catch((error2) => {