mindgraph-mcp 0.22.0 → 0.23.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/README.md +34 -1
- package/dist/cli.js +547 -150
- package/dist/index.js +44 -14
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1184,7 +1184,9 @@ function loadHookEnv(dir) {
|
|
|
1184
1184
|
baseUrl: typeof parsed.baseUrl === "string" ? parsed.baseUrl : void 0,
|
|
1185
1185
|
apiKey: typeof parsed.apiKey === "string" ? parsed.apiKey : void 0,
|
|
1186
1186
|
agentId: typeof parsed.agentId === "string" ? parsed.agentId : void 0,
|
|
1187
|
-
orgId: typeof parsed.orgId === "string" ? parsed.orgId : void 0
|
|
1187
|
+
orgId: typeof parsed.orgId === "string" ? parsed.orgId : void 0,
|
|
1188
|
+
promptRecall: typeof parsed.promptRecall === "boolean" ? parsed.promptRecall : void 0,
|
|
1189
|
+
capture: typeof parsed.capture === "boolean" ? parsed.capture : void 0
|
|
1188
1190
|
};
|
|
1189
1191
|
} catch {
|
|
1190
1192
|
return {};
|
|
@@ -1198,7 +1200,9 @@ function saveHookEnv(env, dir) {
|
|
|
1198
1200
|
baseUrl: env.baseUrl ?? existing.baseUrl,
|
|
1199
1201
|
apiKey: env.apiKey ?? existing.apiKey,
|
|
1200
1202
|
agentId: env.agentId ?? existing.agentId,
|
|
1201
|
-
orgId: env.orgId === null ? void 0 : env.orgId ?? existing.orgId
|
|
1203
|
+
orgId: env.orgId === null ? void 0 : env.orgId ?? existing.orgId,
|
|
1204
|
+
promptRecall: env.promptRecall ?? existing.promptRecall,
|
|
1205
|
+
capture: env.capture ?? existing.capture
|
|
1202
1206
|
};
|
|
1203
1207
|
import_node_fs.default.writeFileSync(file, `${JSON.stringify(merged, null, 2)}
|
|
1204
1208
|
`, {
|
|
@@ -2561,6 +2565,12 @@ var init_code_tool = __esm({
|
|
|
2561
2565
|
});
|
|
2562
2566
|
|
|
2563
2567
|
// src/hook-installer.ts
|
|
2568
|
+
function storedFeatures(homeDir) {
|
|
2569
|
+
const { promptRecall: promptRecall2, capture } = loadHookEnv(
|
|
2570
|
+
import_node_path4.default.join(homeDir || import_node_os4.default.homedir(), ".mindgraph")
|
|
2571
|
+
);
|
|
2572
|
+
return { promptRecall: promptRecall2, capture };
|
|
2573
|
+
}
|
|
2564
2574
|
function command(harness) {
|
|
2565
2575
|
return `node "$HOME/${RUNNER_RELATIVE}" hook --harness ${harness} --owner mindgraph`;
|
|
2566
2576
|
}
|
|
@@ -2569,22 +2579,22 @@ function windowsCommand(harness) {
|
|
|
2569
2579
|
}
|
|
2570
2580
|
function installHookRunner(sourceFile, homeDir, version) {
|
|
2571
2581
|
const target = import_node_path4.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_RELATIVE);
|
|
2572
|
-
|
|
2582
|
+
import_node_fs5.default.mkdirSync(import_node_path4.default.dirname(target), { recursive: true });
|
|
2573
2583
|
const temporary = `${target}.${process.pid}.${import_node_crypto5.default.randomUUID()}.tmp`;
|
|
2574
|
-
|
|
2575
|
-
|
|
2584
|
+
import_node_fs5.default.copyFileSync(sourceFile, temporary);
|
|
2585
|
+
import_node_fs5.default.renameSync(temporary, target);
|
|
2576
2586
|
const versionPath = import_node_path4.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_VERSION_RELATIVE);
|
|
2577
2587
|
if (version) {
|
|
2578
|
-
|
|
2588
|
+
import_node_fs5.default.writeFileSync(versionPath, `${version}
|
|
2579
2589
|
`);
|
|
2580
2590
|
} else {
|
|
2581
|
-
|
|
2591
|
+
import_node_fs5.default.rmSync(versionPath, { force: true });
|
|
2582
2592
|
}
|
|
2583
2593
|
return target;
|
|
2584
2594
|
}
|
|
2585
2595
|
function installedHookRunnerVersion(homeDir) {
|
|
2586
2596
|
try {
|
|
2587
|
-
const raw =
|
|
2597
|
+
const raw = import_node_fs5.default.readFileSync(
|
|
2588
2598
|
import_node_path4.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_VERSION_RELATIVE),
|
|
2589
2599
|
"utf8"
|
|
2590
2600
|
).trim();
|
|
@@ -2611,10 +2621,10 @@ function refreshHookRunner(serverVersion, sourceBundle, homeDir) {
|
|
|
2611
2621
|
return { refreshed: false, reason: "disabled" };
|
|
2612
2622
|
}
|
|
2613
2623
|
const home = homeDir || import_node_os4.default.homedir();
|
|
2614
|
-
if (!
|
|
2624
|
+
if (!import_node_fs5.default.existsSync(import_node_path4.default.join(home, RUNNER_RELATIVE))) {
|
|
2615
2625
|
return { refreshed: false, reason: "no_runner" };
|
|
2616
2626
|
}
|
|
2617
|
-
if (!
|
|
2627
|
+
if (!import_node_fs5.default.existsSync(sourceBundle)) {
|
|
2618
2628
|
return { refreshed: false, reason: "no_source" };
|
|
2619
2629
|
}
|
|
2620
2630
|
const server2 = parseSemver(serverVersion);
|
|
@@ -2645,9 +2655,16 @@ function refreshOwnedClaudeHooks(projectDir = process.cwd(), homeDir) {
|
|
|
2645
2655
|
for (const scope of scopes) {
|
|
2646
2656
|
try {
|
|
2647
2657
|
const file = settingsPath("claude-code", scope, projectDir, void 0, homeDir);
|
|
2648
|
-
if (!
|
|
2649
|
-
if (!
|
|
2650
|
-
installHooks(
|
|
2658
|
+
if (!import_node_fs5.default.existsSync(file)) continue;
|
|
2659
|
+
if (!import_node_fs5.default.readFileSync(file, "utf8").includes(OWNER_MARKER)) continue;
|
|
2660
|
+
installHooks(
|
|
2661
|
+
"claude-code",
|
|
2662
|
+
scope,
|
|
2663
|
+
projectDir,
|
|
2664
|
+
void 0,
|
|
2665
|
+
homeDir,
|
|
2666
|
+
storedFeatures(homeDir)
|
|
2667
|
+
);
|
|
2651
2668
|
refreshed.push(file);
|
|
2652
2669
|
} catch {
|
|
2653
2670
|
}
|
|
@@ -2666,8 +2683,8 @@ function settingsPath(harness, scope, projectDir = process.cwd(), codexHomeDir =
|
|
|
2666
2683
|
return scope === "user" ? import_node_path4.default.join(homeDir || import_node_os4.default.homedir(), ".claude", "settings.json") : import_node_path4.default.join(projectDir, ".claude", "settings.json");
|
|
2667
2684
|
}
|
|
2668
2685
|
function readSettings(file) {
|
|
2669
|
-
if (!
|
|
2670
|
-
return JSON.parse(
|
|
2686
|
+
if (!import_node_fs5.default.existsSync(file)) return {};
|
|
2687
|
+
return JSON.parse(import_node_fs5.default.readFileSync(file, "utf8"));
|
|
2671
2688
|
}
|
|
2672
2689
|
function ownedHook(harness, timeout, additionalContextLimit) {
|
|
2673
2690
|
return {
|
|
@@ -2679,7 +2696,7 @@ function ownedHook(harness, timeout, additionalContextLimit) {
|
|
|
2679
2696
|
...additionalContextLimit === void 0 ? {} : { additionalContextLimit }
|
|
2680
2697
|
};
|
|
2681
2698
|
}
|
|
2682
|
-
function desiredEntries(harness) {
|
|
2699
|
+
function desiredEntries(harness, features = {}) {
|
|
2683
2700
|
const common = {
|
|
2684
2701
|
SessionStart: [
|
|
2685
2702
|
{
|
|
@@ -2722,11 +2739,17 @@ function desiredEntries(harness) {
|
|
|
2722
2739
|
{ hooks: [ownedHook(harness, harness === "codex" ? 3 : 30)] }
|
|
2723
2740
|
]
|
|
2724
2741
|
};
|
|
2725
|
-
|
|
2742
|
+
if (harness !== "claude-code") return common;
|
|
2743
|
+
return {
|
|
2726
2744
|
...common,
|
|
2727
2745
|
TaskCreated: [{ hooks: [ownedHook(harness, 3)] }],
|
|
2728
|
-
TaskCompleted: [{ hooks: [ownedHook(harness, 3)] }]
|
|
2729
|
-
|
|
2746
|
+
TaskCompleted: [{ hooks: [ownedHook(harness, 3)] }],
|
|
2747
|
+
// Bounded recall per prompt: the core gives up at 4s and fails open, so
|
|
2748
|
+
// a 5s budget never holds the prompt longer than that.
|
|
2749
|
+
...features.promptRecall !== false ? { UserPromptSubmit: [{ hooks: [ownedHook(harness, 5)] }] } : {},
|
|
2750
|
+
// Opt-in transcript capture before compaction (cold-tenant ingest).
|
|
2751
|
+
...features.capture === true ? { PreCompact: [{ hooks: [ownedHook(harness, 30)] }] } : {}
|
|
2752
|
+
};
|
|
2730
2753
|
}
|
|
2731
2754
|
function hookCommand(value) {
|
|
2732
2755
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
@@ -2739,21 +2762,30 @@ function entryHasOwnedHook(value) {
|
|
|
2739
2762
|
return Array.isArray(hooks) && hooks.some((hook) => hookCommand(hook)?.includes(OWNER_MARKER));
|
|
2740
2763
|
}
|
|
2741
2764
|
function writeSettings(file, settings) {
|
|
2742
|
-
|
|
2765
|
+
import_node_fs5.default.mkdirSync(import_node_path4.default.dirname(file), { recursive: true });
|
|
2743
2766
|
const temporary = `${file}.${process.pid}.${import_node_crypto5.default.randomUUID()}.tmp`;
|
|
2744
|
-
|
|
2767
|
+
import_node_fs5.default.writeFileSync(temporary, `${JSON.stringify(settings, null, 2)}
|
|
2745
2768
|
`, {
|
|
2746
2769
|
mode: 384
|
|
2747
2770
|
});
|
|
2748
|
-
|
|
2771
|
+
import_node_fs5.default.renameSync(temporary, file);
|
|
2749
2772
|
}
|
|
2750
|
-
function installHooks(harness, scope, projectDir = process.cwd(), codexHomeDir, homeDir) {
|
|
2773
|
+
function installHooks(harness, scope, projectDir = process.cwd(), codexHomeDir, homeDir, features = {}) {
|
|
2751
2774
|
const file = settingsPath(harness, scope, projectDir, codexHomeDir, homeDir);
|
|
2752
2775
|
const settings = readSettings(file);
|
|
2753
2776
|
const hooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
|
|
2754
2777
|
let added = 0;
|
|
2755
2778
|
let updated = 0;
|
|
2756
|
-
|
|
2779
|
+
const desired = desiredEntries(harness, features);
|
|
2780
|
+
for (const optional of ["UserPromptSubmit", "PreCompact"]) {
|
|
2781
|
+
if (optional in desired || !Array.isArray(hooks[optional])) continue;
|
|
2782
|
+
const kept = hooks[optional].filter(
|
|
2783
|
+
(entry) => !entryHasOwnedHook(entry)
|
|
2784
|
+
);
|
|
2785
|
+
if (kept.length > 0) hooks[optional] = kept;
|
|
2786
|
+
else delete hooks[optional];
|
|
2787
|
+
}
|
|
2788
|
+
for (const [event, wanted] of Object.entries(desired)) {
|
|
2757
2789
|
const current = Array.isArray(hooks[event]) ? [...hooks[event]] : [];
|
|
2758
2790
|
const kept = current.filter((entry) => !entryHasOwnedHook(entry));
|
|
2759
2791
|
const hadOwned = kept.length !== current.length;
|
|
@@ -2770,7 +2802,7 @@ function installHooks(harness, scope, projectDir = process.cwd(), codexHomeDir,
|
|
|
2770
2802
|
}
|
|
2771
2803
|
function uninstallHooks(harness, scope, projectDir = process.cwd(), codexHomeDir) {
|
|
2772
2804
|
const file = settingsPath(harness, scope, projectDir, codexHomeDir);
|
|
2773
|
-
if (!
|
|
2805
|
+
if (!import_node_fs5.default.existsSync(file)) return { path: file, removed: 0 };
|
|
2774
2806
|
const settings = readSettings(file);
|
|
2775
2807
|
const hooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
|
|
2776
2808
|
let removed = 0;
|
|
@@ -2804,8 +2836,8 @@ function uninstallHooks(harness, scope, projectDir = process.cwd(), codexHomeDir
|
|
|
2804
2836
|
writeSettings(file, settings);
|
|
2805
2837
|
return { path: file, removed };
|
|
2806
2838
|
}
|
|
2807
|
-
function installClaudeHooks(scope, projectDir = process.cwd()) {
|
|
2808
|
-
return installHooks("claude-code", scope, projectDir);
|
|
2839
|
+
function installClaudeHooks(scope, projectDir = process.cwd(), features = {}) {
|
|
2840
|
+
return installHooks("claude-code", scope, projectDir, void 0, void 0, features);
|
|
2809
2841
|
}
|
|
2810
2842
|
function uninstallClaudeHooks(scope, projectDir = process.cwd()) {
|
|
2811
2843
|
return uninstallHooks("claude-code", scope, projectDir);
|
|
@@ -2816,14 +2848,15 @@ function installCodexHooks(scope, projectDir = process.cwd(), codexHomeDir) {
|
|
|
2816
2848
|
function uninstallCodexHooks(scope, projectDir = process.cwd(), codexHomeDir) {
|
|
2817
2849
|
return uninstallHooks("codex", scope, projectDir, codexHomeDir);
|
|
2818
2850
|
}
|
|
2819
|
-
var
|
|
2851
|
+
var import_node_fs5, import_node_os4, import_node_path4, import_node_crypto5, OWNER_MARKER, RUNNER_RELATIVE, RUNNER_VERSION_RELATIVE;
|
|
2820
2852
|
var init_hook_installer = __esm({
|
|
2821
2853
|
"src/hook-installer.ts"() {
|
|
2822
2854
|
"use strict";
|
|
2823
|
-
|
|
2855
|
+
import_node_fs5 = __toESM(require("fs"));
|
|
2824
2856
|
import_node_os4 = __toESM(require("os"));
|
|
2825
2857
|
import_node_path4 = __toESM(require("path"));
|
|
2826
2858
|
import_node_crypto5 = __toESM(require("crypto"));
|
|
2859
|
+
init_hook_env();
|
|
2827
2860
|
OWNER_MARKER = "--owner mindgraph";
|
|
2828
2861
|
RUNNER_RELATIVE = ".mindgraph/bin/mindgraph-hook.cjs";
|
|
2829
2862
|
RUNNER_VERSION_RELATIVE = `${RUNNER_RELATIVE}.version`;
|
|
@@ -2834,14 +2867,14 @@ var init_hook_installer = __esm({
|
|
|
2834
2867
|
function obj(value) {
|
|
2835
2868
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2836
2869
|
}
|
|
2837
|
-
function
|
|
2870
|
+
function str2(value) {
|
|
2838
2871
|
return typeof value === "string" && value ? value : void 0;
|
|
2839
2872
|
}
|
|
2840
2873
|
function num(value) {
|
|
2841
2874
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2842
2875
|
}
|
|
2843
2876
|
function edgeTypeName(value) {
|
|
2844
|
-
return
|
|
2877
|
+
return str2(value) ?? str2(obj(value)?.Custom);
|
|
2845
2878
|
}
|
|
2846
2879
|
function renderBudget(requested) {
|
|
2847
2880
|
if (requested === void 0 || !Number.isFinite(requested)) return RENDER_CHAR_BUDGET;
|
|
@@ -2851,7 +2884,7 @@ function renderBudget(requested) {
|
|
|
2851
2884
|
);
|
|
2852
2885
|
}
|
|
2853
2886
|
function clip(value, max, state) {
|
|
2854
|
-
const text2 =
|
|
2887
|
+
const text2 = str2(value);
|
|
2855
2888
|
if (!text2) return void 0;
|
|
2856
2889
|
if (text2.length <= max) return text2;
|
|
2857
2890
|
state.bounded = true;
|
|
@@ -2860,13 +2893,13 @@ function clip(value, max, state) {
|
|
|
2860
2893
|
function isNodeShaped(value) {
|
|
2861
2894
|
const node = obj(value);
|
|
2862
2895
|
return Boolean(
|
|
2863
|
-
node &&
|
|
2896
|
+
node && str2(node.uid) && (str2(node.label) || str2(node.node_type) || obj(node.node_type))
|
|
2864
2897
|
);
|
|
2865
2898
|
}
|
|
2866
2899
|
function nodeDisplayType(node) {
|
|
2867
2900
|
const props = obj(node.props);
|
|
2868
|
-
const plain =
|
|
2869
|
-
return (plain && plain !== "Custom" ? plain : void 0) ??
|
|
2901
|
+
const plain = str2(node.node_type);
|
|
2902
|
+
return (plain && plain !== "Custom" ? plain : void 0) ?? str2(obj(node.node_type)?.Custom) ?? str2(props?.object_type) ?? str2(props?.type_name) ?? str2(obj(props?.data)?.domain_type) ?? plain ?? "Node";
|
|
2870
2903
|
}
|
|
2871
2904
|
function nodeFields(node) {
|
|
2872
2905
|
const props = obj(node.props);
|
|
@@ -2886,10 +2919,10 @@ function statusTag(node) {
|
|
|
2886
2919
|
const tags = [];
|
|
2887
2920
|
if (node.superseded === true) {
|
|
2888
2921
|
tags.push(
|
|
2889
|
-
node.superseded_by ? `SUPERSEDED by ${
|
|
2922
|
+
node.superseded_by ? `SUPERSEDED by ${str2(node.superseded_by)}` : "SUPERSEDED"
|
|
2890
2923
|
);
|
|
2891
2924
|
}
|
|
2892
|
-
const truth =
|
|
2925
|
+
const truth = str2(node.truth_status) ?? str2(obj(node.props)?.truth_status);
|
|
2893
2926
|
if (truth && truth !== "accepted") tags.push(`truth: ${truth}`);
|
|
2894
2927
|
if (node.currently_valid === false) tags.push("OUTSIDE ITS VALIDITY WINDOW");
|
|
2895
2928
|
if (node.valid_at_time === false) tags.push("NOT VALID AT THE REQUESTED TIME");
|
|
@@ -2899,7 +2932,7 @@ function firstQuote(node, state) {
|
|
|
2899
2932
|
const chunks = Array.isArray(node.source_chunks) ? node.source_chunks : [];
|
|
2900
2933
|
for (const raw of chunks) {
|
|
2901
2934
|
const chunk = obj(raw);
|
|
2902
|
-
const quote =
|
|
2935
|
+
const quote = str2(chunk?.quote) ?? str2(obj(chunk?.anchor)?.exact);
|
|
2903
2936
|
if (quote) return clip(quote, QUOTE_MAX, state);
|
|
2904
2937
|
}
|
|
2905
2938
|
return void 0;
|
|
@@ -2907,7 +2940,7 @@ function firstQuote(node, state) {
|
|
|
2907
2940
|
function sourceTag(node) {
|
|
2908
2941
|
const docs = Array.isArray(node.source_documents) ? node.source_documents : [];
|
|
2909
2942
|
const first = obj(docs[0]);
|
|
2910
|
-
const title =
|
|
2943
|
+
const title = str2(first?.title);
|
|
2911
2944
|
if (!title) return void 0;
|
|
2912
2945
|
const extra = docs.length > 1 ? ` (+${docs.length - 1} more)` : "";
|
|
2913
2946
|
return `from: ${title}${extra}`;
|
|
@@ -2933,7 +2966,7 @@ function nodeLines(node, state) {
|
|
|
2933
2966
|
confidence !== void 0 ? `confidence ${confidence}` : void 0,
|
|
2934
2967
|
score !== void 0 ? `score ${Math.round(score * 100) / 100}` : void 0
|
|
2935
2968
|
].filter(Boolean).join(", ");
|
|
2936
|
-
const head = `- **${label}** [${
|
|
2969
|
+
const head = `- **${label}** [${str2(node.uid) || "?"}] (${parenthetical})${statusTag(node)}`;
|
|
2937
2970
|
const summary = nodeSummaryText(node, state);
|
|
2938
2971
|
const lines = [summary && summary !== label ? `${head}: ${summary}` : head];
|
|
2939
2972
|
const fields = layer7Fields(node, state);
|
|
@@ -2947,7 +2980,7 @@ function nodeLines(node, state) {
|
|
|
2947
2980
|
const rendered2 = believers.slice(0, 3).map((raw) => {
|
|
2948
2981
|
const believer = obj(raw);
|
|
2949
2982
|
const conf = num(believer?.confidence);
|
|
2950
|
-
return `${
|
|
2983
|
+
return `${str2(believer?.agent_label) || "?"}${conf !== void 0 ? ` (${conf})` : ""}`;
|
|
2951
2984
|
}).join(", ");
|
|
2952
2985
|
lines.push(` - believed by: ${rendered2}`);
|
|
2953
2986
|
}
|
|
@@ -2978,8 +3011,8 @@ function relationshipItems(edges, labelByUid, state, options) {
|
|
|
2978
3011
|
const edge = obj(raw);
|
|
2979
3012
|
if (!edge) continue;
|
|
2980
3013
|
const type = edgeTypeName(edge.edge_type) || "?";
|
|
2981
|
-
const from =
|
|
2982
|
-
const to =
|
|
3014
|
+
const from = str2(edge.from_uid);
|
|
3015
|
+
const to = str2(edge.to_uid);
|
|
2983
3016
|
if (CURATION_EDGE_TYPES.has(type)) continue;
|
|
2984
3017
|
if (!from || !to || from === to) continue;
|
|
2985
3018
|
const fromLabel = labelByUid.get(from) ?? (options?.fallbackToUid ? `[${from}]` : void 0);
|
|
@@ -3051,7 +3084,7 @@ function policySections(payload, state) {
|
|
|
3051
3084
|
if (policies.length === 0) return [];
|
|
3052
3085
|
const items = policies.map((raw) => obj(raw)).filter((policy) => Boolean(policy)).map((policy) => {
|
|
3053
3086
|
const rules = Array.isArray(policy.prose_rules) ? policy.prose_rules.map(String).join("; ") : policy.prose_rules;
|
|
3054
|
-
return `- ${
|
|
3087
|
+
return `- ${str2(policy.name) || "(unnamed policy)"}: ${clip(rules, SUMMARY_MAX, state) || "(structured rules apply)"}`;
|
|
3055
3088
|
});
|
|
3056
3089
|
return [{ heading: "## Applicable policies", items }];
|
|
3057
3090
|
}
|
|
@@ -3071,7 +3104,7 @@ function renderContext(response, maxChars) {
|
|
|
3071
3104
|
const graph = obj(payload.graph);
|
|
3072
3105
|
const rawNodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
|
|
3073
3106
|
const shaped = rawNodes.filter(isNodeShaped);
|
|
3074
|
-
const nodes = shaped.filter((node) => !INFRA_NODE_TYPES.has(
|
|
3107
|
+
const nodes = shaped.filter((node) => !INFRA_NODE_TYPES.has(str2(node.node_type) || ""));
|
|
3075
3108
|
const infraOnly = shaped.length - nodes.length;
|
|
3076
3109
|
const articles = Array.isArray(payload.articles) ? payload.articles : [];
|
|
3077
3110
|
const chunks = Array.isArray(payload.chunks) ? payload.chunks : [];
|
|
@@ -3081,7 +3114,7 @@ function renderContext(response, maxChars) {
|
|
|
3081
3114
|
sections.push({
|
|
3082
3115
|
heading: "## Knowledge articles",
|
|
3083
3116
|
items: articles.map((raw) => obj(raw)).filter((article) => Boolean(article)).map(
|
|
3084
|
-
(article) => `### ${
|
|
3117
|
+
(article) => `### ${str2(article.label) || "(untitled)"} [${str2(article.uid) || "?"}]
|
|
3085
3118
|
${clip(article.content, ARTICLE_MAX, state) || ""}`
|
|
3086
3119
|
)
|
|
3087
3120
|
});
|
|
@@ -3089,7 +3122,7 @@ ${clip(article.content, ARTICLE_MAX, state) || ""}`
|
|
|
3089
3122
|
if (nodes.length > 0) {
|
|
3090
3123
|
sections.push(...groupedNodeSections(nodes, state, "## Knowledge graph\n\n"));
|
|
3091
3124
|
const labelByUid = /* @__PURE__ */ new Map();
|
|
3092
|
-
for (const node of nodes) labelByUid.set(
|
|
3125
|
+
for (const node of nodes) labelByUid.set(str2(node.uid), str2(node.label) || "?");
|
|
3093
3126
|
const relationships = relationshipItems(
|
|
3094
3127
|
Array.isArray(graph?.edges) ? graph.edges : [],
|
|
3095
3128
|
labelByUid,
|
|
@@ -3108,7 +3141,7 @@ ${clip(article.content, ARTICLE_MAX, state) || ""}`
|
|
|
3108
3141
|
sections.push({
|
|
3109
3142
|
heading: "## Source excerpts",
|
|
3110
3143
|
items: chunks.map((raw) => obj(raw)).filter((chunk) => Boolean(chunk)).map(
|
|
3111
|
-
(chunk) => `- [${
|
|
3144
|
+
(chunk) => `- [${str2(chunk.chunk_uid) || "?"}] from "${str2(chunk.document_title) || "unknown document"}": "${clip(chunk.content, CHUNK_MAX, state) || ""}"`
|
|
3112
3145
|
)
|
|
3113
3146
|
});
|
|
3114
3147
|
}
|
|
@@ -3132,15 +3165,15 @@ function resumeEntry(raw, state, fallbackType) {
|
|
|
3132
3165
|
if (!wrapper) return void 0;
|
|
3133
3166
|
const value = obj(wrapper.node) ?? wrapper;
|
|
3134
3167
|
if (isNodeShaped(value)) return nodeLines(value, state);
|
|
3135
|
-
const uid =
|
|
3168
|
+
const uid = str2(value.uid);
|
|
3136
3169
|
const props = obj(value.props);
|
|
3137
3170
|
const label = clip(value.label, LABEL_MAX, state) ?? clip(value.name, LABEL_MAX, state) ?? clip(value.title, LABEL_MAX, state) ?? clip(value.action, LABEL_MAX, state) ?? fallbackType;
|
|
3138
|
-
const status =
|
|
3171
|
+
const status = str2(value.status) ?? str2(props?.status);
|
|
3139
3172
|
const summary = clip(value.summary, SUMMARY_MAX, state) ?? clip(value.description, SUMMARY_MAX, state) ?? clip(value.content, SUMMARY_MAX, state) ?? clip(value.outcome_summary, SUMMARY_MAX, state) ?? clip(props?.summary, SUMMARY_MAX, state);
|
|
3140
3173
|
const details = [
|
|
3141
3174
|
status ? `status ${status}` : void 0,
|
|
3142
|
-
|
|
3143
|
-
|
|
3175
|
+
str2(value.path) ? `path ${str2(value.path)}` : void 0,
|
|
3176
|
+
str2(value.symbol) ? `symbol ${str2(value.symbol)}` : void 0
|
|
3144
3177
|
].filter(Boolean);
|
|
3145
3178
|
const head = `- **${label}**${uid ? ` [${uid}]` : ""}${details.length > 0 ? ` (${details.join(", ")})` : ""}`;
|
|
3146
3179
|
return summary && summary !== label ? `${head}: ${summary}` : head;
|
|
@@ -3153,7 +3186,7 @@ function renderResumeContext(response, maxChars) {
|
|
|
3153
3186
|
const payload = obj(response);
|
|
3154
3187
|
if (!payload) return void 0;
|
|
3155
3188
|
const state = { bounded: false, omitted: 0 };
|
|
3156
|
-
const status =
|
|
3189
|
+
const status = str2(payload.status);
|
|
3157
3190
|
if (!payload.task && status === "no_eligible_work") {
|
|
3158
3191
|
const scopedOut = Array.isArray(payload.scoped_out_tasks) ? payload.scoped_out_tasks.length : 0;
|
|
3159
3192
|
const note = scopedOut > 0 ? ` ${scopedOut} task(s) exist outside the requested scope.` : "";
|
|
@@ -3288,13 +3321,13 @@ function renderTraversal(response, maxChars) {
|
|
|
3288
3321
|
const state = { bounded: false, omitted: 0 };
|
|
3289
3322
|
const sections = [];
|
|
3290
3323
|
const labelByUid = /* @__PURE__ */ new Map();
|
|
3291
|
-
for (const node of nodes) labelByUid.set(
|
|
3324
|
+
for (const node of nodes) labelByUid.set(str2(node.uid), str2(node.label) || "?");
|
|
3292
3325
|
if (paths.length > 0) {
|
|
3293
3326
|
const items = paths.map((raw) => obj(raw)).filter((path8) => Boolean(path8)).map((path8, index) => {
|
|
3294
3327
|
const uids = Array.isArray(path8.node_uids) ? path8.node_uids.map(String) : [];
|
|
3295
3328
|
const labels = Array.isArray(path8.labels) ? path8.labels.map(String) : Array.isArray(path8.nodes) ? path8.nodes.map((hop) => {
|
|
3296
3329
|
const hopNode = obj(hop);
|
|
3297
|
-
return
|
|
3330
|
+
return str2(hopNode?.label) || labelByUid.get(str2(hopNode?.uid) || "") || str2(hop) || "?";
|
|
3298
3331
|
}) : [];
|
|
3299
3332
|
const route = labels.length > 0 ? labels.join(" \u2192 ") : uids.join(" \u2192 ") || "(path)";
|
|
3300
3333
|
const cost = num(path8.cost) ?? num(path8.path_cost);
|
|
@@ -3312,12 +3345,12 @@ function renderTraversal(response, maxChars) {
|
|
|
3312
3345
|
if (hasSteps) {
|
|
3313
3346
|
const stepItems = rawSteps.map((raw) => obj(raw)).filter((step) => Boolean(step)).map((step) => {
|
|
3314
3347
|
const depth = num(step.depth);
|
|
3315
|
-
const uid =
|
|
3316
|
-
const parent =
|
|
3317
|
-
const label =
|
|
3348
|
+
const uid = str2(step.node_uid) ?? str2(step.uid) ?? str2(step.edge_to_uid) ?? "?";
|
|
3349
|
+
const parent = str2(step.parent_uid) ?? str2(step.edge_from_uid);
|
|
3350
|
+
const label = str2(step.label) || "(unlabeled)";
|
|
3318
3351
|
const relation = edgeTypeName(step.edge_type);
|
|
3319
|
-
const role =
|
|
3320
|
-
const type =
|
|
3352
|
+
const role = str2(step.traversal_role);
|
|
3353
|
+
const type = str2(step.node_type) ?? str2(obj(step.node_type)?.Custom);
|
|
3321
3354
|
const cost = num(step.path_cost);
|
|
3322
3355
|
const confidence = num(step.path_confidence);
|
|
3323
3356
|
const prefix = [
|
|
@@ -3336,9 +3369,9 @@ function renderTraversal(response, maxChars) {
|
|
|
3336
3369
|
if (stepItems.length > 0) {
|
|
3337
3370
|
sections.push({ heading: "## Traversal steps", items: stepItems });
|
|
3338
3371
|
} else {
|
|
3339
|
-
const start =
|
|
3340
|
-
const end =
|
|
3341
|
-
const mode2 =
|
|
3372
|
+
const start = str2(body.start_uid) || "?";
|
|
3373
|
+
const end = str2(body.end_uid);
|
|
3374
|
+
const mode2 = str2(body.mode);
|
|
3342
3375
|
sections.push({
|
|
3343
3376
|
heading: mode2 === "path" && end ? `No path found from [${start}] to [${end}].` : `No traversal steps returned from [${start}].`,
|
|
3344
3377
|
items: []
|
|
@@ -3356,7 +3389,7 @@ function renderTraversal(response, maxChars) {
|
|
|
3356
3389
|
sections.push({ heading: "## Relationships", items: relationships });
|
|
3357
3390
|
}
|
|
3358
3391
|
}
|
|
3359
|
-
const mode =
|
|
3392
|
+
const mode = str2(body.mode);
|
|
3360
3393
|
return assemble(
|
|
3361
3394
|
`# Graph traversal${mode ? ` (${mode})` : ""}`,
|
|
3362
3395
|
sections,
|
|
@@ -3367,9 +3400,9 @@ function renderTraversal(response, maxChars) {
|
|
|
3367
3400
|
function neighborLine(value) {
|
|
3368
3401
|
const node = obj(value);
|
|
3369
3402
|
if (!node) return void 0;
|
|
3370
|
-
const label =
|
|
3371
|
-
const uid =
|
|
3372
|
-
const type =
|
|
3403
|
+
const label = str2(node.label) || "(unlabeled)";
|
|
3404
|
+
const uid = str2(node.uid);
|
|
3405
|
+
const type = str2(node.node_type);
|
|
3373
3406
|
return `- ${label}${uid ? ` [${uid}]` : ""}${type ? ` (${type})` : ""}`;
|
|
3374
3407
|
}
|
|
3375
3408
|
function renderOntologyAnswer(response) {
|
|
@@ -3389,20 +3422,20 @@ function renderOntologyAnswer(response) {
|
|
|
3389
3422
|
sections.push(...groupedNodeSections(objects, state));
|
|
3390
3423
|
}
|
|
3391
3424
|
const labelByUid = /* @__PURE__ */ new Map();
|
|
3392
|
-
for (const node of objects) labelByUid.set(
|
|
3425
|
+
for (const node of objects) labelByUid.set(str2(node.uid), str2(node.label) || "?");
|
|
3393
3426
|
const graph = obj(payload.graph);
|
|
3394
3427
|
for (const raw of Array.isArray(graph?.nodes) ? graph.nodes : []) {
|
|
3395
3428
|
const node = obj(raw);
|
|
3396
|
-
const uid =
|
|
3397
|
-
if (uid && !labelByUid.has(uid)) labelByUid.set(uid,
|
|
3429
|
+
const uid = str2(node?.uid);
|
|
3430
|
+
if (uid && !labelByUid.has(uid)) labelByUid.set(uid, str2(node?.label) || "?");
|
|
3398
3431
|
}
|
|
3399
3432
|
if (cognitive) {
|
|
3400
3433
|
for (const rawNodes of Object.values(cognitive)) {
|
|
3401
3434
|
if (!Array.isArray(rawNodes)) continue;
|
|
3402
3435
|
for (const raw of rawNodes) {
|
|
3403
3436
|
const node = obj(raw);
|
|
3404
|
-
const uid =
|
|
3405
|
-
if (uid && !labelByUid.has(uid)) labelByUid.set(uid,
|
|
3437
|
+
const uid = str2(node?.uid);
|
|
3438
|
+
if (uid && !labelByUid.has(uid)) labelByUid.set(uid, str2(node?.label) || "?");
|
|
3406
3439
|
}
|
|
3407
3440
|
}
|
|
3408
3441
|
}
|
|
@@ -3423,9 +3456,9 @@ function renderOntologyAnswer(response) {
|
|
|
3423
3456
|
if (provenance.length > 0) {
|
|
3424
3457
|
const items = provenance.map((raw) => obj(raw)).filter((entry) => Boolean(entry)).map((entry) => {
|
|
3425
3458
|
const span = clip(entry.text_span, QUOTE_MAX, state) || "";
|
|
3426
|
-
const by =
|
|
3427
|
-
const suffix = `${by ? ` (ingested by ${by})` : ""} [source ${
|
|
3428
|
-
return
|
|
3459
|
+
const by = str2(entry.ingested_by_name);
|
|
3460
|
+
const suffix = `${by ? ` (ingested by ${by})` : ""} [source ${str2(entry.source_uid) || "?"}]`;
|
|
3461
|
+
return str2(entry.span_kind) === "anchor" ? `- "${span}"${suffix}` : `- context: ${span}${suffix}`;
|
|
3429
3462
|
});
|
|
3430
3463
|
sections.push({ heading: "## Sources", items });
|
|
3431
3464
|
}
|
|
@@ -3471,20 +3504,20 @@ function renderJobs(response) {
|
|
|
3471
3504
|
const items = shown.map((job) => {
|
|
3472
3505
|
const progress = obj(job.progress);
|
|
3473
3506
|
const failure = obj(job.error_details);
|
|
3474
|
-
const message =
|
|
3507
|
+
const message = str2(failure?.message) ?? str2(job.error);
|
|
3475
3508
|
const done = num(progress?.processed_chunks);
|
|
3476
3509
|
const totalChunks = num(progress?.total_chunks);
|
|
3477
3510
|
const bits = [
|
|
3478
|
-
|
|
3511
|
+
str2(job.status) || "?",
|
|
3479
3512
|
done !== void 0 && totalChunks !== void 0 ? `${done}/${totalChunks} chunks` : void 0,
|
|
3480
3513
|
num(progress?.nodes_created) !== void 0 ? `${num(progress?.nodes_created)} nodes` : void 0,
|
|
3481
3514
|
num(job.queue_position) !== void 0 ? `queue #${num(job.queue_position)}` : void 0,
|
|
3482
3515
|
message ? `error: ${clip(message, 160, state)}` : void 0,
|
|
3483
|
-
|
|
3516
|
+
str2(failure?.code) ? `code: ${clip(failure?.code, 80, state)}` : void 0,
|
|
3484
3517
|
num(failure?.status) !== void 0 ? `status: ${num(failure?.status)}` : void 0,
|
|
3485
3518
|
typeof failure?.retriable === "boolean" ? `retriable: ${failure.retriable}` : void 0
|
|
3486
3519
|
].filter(Boolean);
|
|
3487
|
-
return `- [${
|
|
3520
|
+
return `- [${str2(job.id) || "?"}] ${clip(job.title, 120, state) || "(untitled)"} \u2014 ${bits.join(", ")}`;
|
|
3488
3521
|
});
|
|
3489
3522
|
return assemble(
|
|
3490
3523
|
`# Ingestion jobs (${shown.length}${shown.length < sorted.length ? ` of ${sorted.length}` : ""})`,
|
|
@@ -3500,13 +3533,13 @@ function renderSignals(response) {
|
|
|
3500
3533
|
for (const [key, value] of Object.entries(payload)) {
|
|
3501
3534
|
if (!Array.isArray(value) || value.length === 0) continue;
|
|
3502
3535
|
const items = value.map((raw) => obj(raw)).filter((item) => Boolean(item)).map((item) => {
|
|
3503
|
-
const fromLabel =
|
|
3504
|
-
const toLabel =
|
|
3536
|
+
const fromLabel = str2(item.from_label);
|
|
3537
|
+
const toLabel = str2(item.to_label);
|
|
3505
3538
|
const anyLabelKey = Object.keys(item).find(
|
|
3506
|
-
(k) => k.endsWith("_label") &&
|
|
3539
|
+
(k) => k.endsWith("_label") && str2(item[k])
|
|
3507
3540
|
);
|
|
3508
|
-
const label =
|
|
3509
|
-
const uid =
|
|
3541
|
+
const label = str2(item.label) ?? str2(item.title) ?? str2(item.name) ?? (fromLabel && toLabel ? `${fromLabel} \u2194 ${toLabel}` : void 0) ?? (anyLabelKey ? str2(item[anyLabelKey]) : void 0) ?? str2(item.uid) ?? str2(item.target_uid) ?? "(item)";
|
|
3542
|
+
const uid = str2(item.uid) ?? str2(item.target_uid);
|
|
3510
3543
|
const numbers = Object.entries(item).filter(([, v]) => typeof v === "number").slice(0, 4).map(([k, v]) => `${k} ${Math.round(v * 100) / 100}`);
|
|
3511
3544
|
const lists = Object.entries(item).filter(([, v]) => Array.isArray(v) && v.length > 0).slice(0, 2).map(([k, v]) => `${k}: ${v.length}`);
|
|
3512
3545
|
const meta = [...numbers, ...lists].join(", ");
|
|
@@ -4025,19 +4058,19 @@ async function handleHostedTool(name, args, client2, identity) {
|
|
|
4025
4058
|
try {
|
|
4026
4059
|
switch (name) {
|
|
4027
4060
|
case "search": {
|
|
4028
|
-
const query =
|
|
4061
|
+
const query = str3(args.query).trim();
|
|
4029
4062
|
if (!query) return err3("query is required");
|
|
4030
4063
|
const nodes = await contextNodes(client2, query, SEARCH_LIMIT);
|
|
4031
4064
|
const results = nodes.slice(0, SEARCH_LIMIT).map((node) => ({
|
|
4032
|
-
id:
|
|
4033
|
-
title:
|
|
4034
|
-
url: `${EXPLORER_URL}${encodeURIComponent(
|
|
4035
|
-
snippet:
|
|
4065
|
+
id: str3(node.uid),
|
|
4066
|
+
title: str3(node.label),
|
|
4067
|
+
url: `${EXPLORER_URL}${encodeURIComponent(str3(node.uid))}`,
|
|
4068
|
+
snippet: str3(node.summary)
|
|
4036
4069
|
}));
|
|
4037
4070
|
return twin({ results });
|
|
4038
4071
|
}
|
|
4039
4072
|
case "fetch": {
|
|
4040
|
-
const id =
|
|
4073
|
+
const id = str3(args.id).trim();
|
|
4041
4074
|
if (!id) return err3("id is required");
|
|
4042
4075
|
let node;
|
|
4043
4076
|
try {
|
|
@@ -4088,7 +4121,7 @@ async function handleHostedTool(name, args, client2, identity) {
|
|
|
4088
4121
|
return toolError(e);
|
|
4089
4122
|
}
|
|
4090
4123
|
}
|
|
4091
|
-
var EXPLORER_URL, SEARCH_LIMIT, FETCH_TEXT_LIMIT, READ_TOOLS, EVOLVE_TOOLS, SEARCH_OUTPUT_SCHEMA, FETCH_OUTPUT_SCHEMA, HOSTED_TOOLS, HOSTED_TOOL_NAMES,
|
|
4124
|
+
var EXPLORER_URL, SEARCH_LIMIT, FETCH_TEXT_LIMIT, READ_TOOLS, EVOLVE_TOOLS, SEARCH_OUTPUT_SCHEMA, FETCH_OUTPUT_SCHEMA, HOSTED_TOOLS, HOSTED_TOOL_NAMES, str3;
|
|
4092
4125
|
var init_hosted_tools = __esm({
|
|
4093
4126
|
"src/hosted-tools.ts"() {
|
|
4094
4127
|
"use strict";
|
|
@@ -4179,7 +4212,7 @@ var init_hosted_tools = __esm({
|
|
|
4179
4212
|
}
|
|
4180
4213
|
];
|
|
4181
4214
|
HOSTED_TOOL_NAMES = new Set(HOSTED_TOOLS.map((tool) => tool.name));
|
|
4182
|
-
|
|
4215
|
+
str3 = (value) => typeof value === "string" ? value : "";
|
|
4183
4216
|
}
|
|
4184
4217
|
});
|
|
4185
4218
|
|
|
@@ -4414,7 +4447,7 @@ function memoryStatus(config = {}) {
|
|
|
4414
4447
|
]
|
|
4415
4448
|
};
|
|
4416
4449
|
}
|
|
4417
|
-
function
|
|
4450
|
+
function nodeType2(node) {
|
|
4418
4451
|
const raw = node.node_type;
|
|
4419
4452
|
if (typeof raw === "string") return raw;
|
|
4420
4453
|
return text(obj2(raw)?.Custom) ?? "Node";
|
|
@@ -4427,7 +4460,7 @@ function compactItem(raw, fallbackType) {
|
|
|
4427
4460
|
if (!uid) return void 0;
|
|
4428
4461
|
return defined({
|
|
4429
4462
|
uid,
|
|
4430
|
-
type: fallbackType ??
|
|
4463
|
+
type: fallbackType ?? nodeType2(value),
|
|
4431
4464
|
label: text(value.label) ?? text(value.document_title),
|
|
4432
4465
|
truth_status: text(value.truth_status) ?? text(obj2(value.props)?.truth_status),
|
|
4433
4466
|
superseded: value.superseded === true ? true : void 0,
|
|
@@ -4988,7 +5021,7 @@ function safePath(root, candidate) {
|
|
|
4988
5021
|
if (relative2.startsWith("..") || import_node_path5.default.isAbsolute(relative2)) {
|
|
4989
5022
|
throw new Error(`path escapes repository root: ${candidate}`);
|
|
4990
5023
|
}
|
|
4991
|
-
if (
|
|
5024
|
+
if (import_node_fs6.default.existsSync(absolute)) {
|
|
4992
5025
|
if (!pathContains(realPathOf(root), realPathOf(absolute))) {
|
|
4993
5026
|
throw new Error(`path escapes repository root via symlink: ${candidate}`);
|
|
4994
5027
|
}
|
|
@@ -4997,23 +5030,23 @@ function safePath(root, candidate) {
|
|
|
4997
5030
|
}
|
|
4998
5031
|
function realPathOf(target) {
|
|
4999
5032
|
try {
|
|
5000
|
-
return
|
|
5033
|
+
return import_node_fs6.default.realpathSync(target);
|
|
5001
5034
|
} catch {
|
|
5002
5035
|
return import_node_path5.default.resolve(target);
|
|
5003
5036
|
}
|
|
5004
5037
|
}
|
|
5005
5038
|
function markdownFiles(target, realRoot, visited = /* @__PURE__ */ new Set()) {
|
|
5006
|
-
if (!
|
|
5039
|
+
if (!import_node_fs6.default.existsSync(target)) return [];
|
|
5007
5040
|
const real = realPathOf(target);
|
|
5008
5041
|
if (!pathContains(realRoot, real)) return [];
|
|
5009
|
-
const stat =
|
|
5042
|
+
const stat = import_node_fs6.default.statSync(target);
|
|
5010
5043
|
if (stat.isFile()) return target.endsWith(".md") ? [target] : [];
|
|
5011
5044
|
if (!stat.isDirectory() || visited.has(real)) return [];
|
|
5012
5045
|
visited.add(real);
|
|
5013
|
-
return
|
|
5046
|
+
return import_node_fs6.default.readdirSync(target, { withFileTypes: true }).flatMap((entry) => markdownFiles(import_node_path5.default.join(target, entry.name), realRoot, visited));
|
|
5014
5047
|
}
|
|
5015
5048
|
function fileDescriptor(root, absolute) {
|
|
5016
|
-
const content =
|
|
5049
|
+
const content = import_node_fs6.default.readFileSync(absolute, "utf8");
|
|
5017
5050
|
return {
|
|
5018
5051
|
logical_path: import_node_path5.default.relative(root, absolute).split(import_node_path5.default.sep).join("/"),
|
|
5019
5052
|
content_hash: import_node_crypto7.default.createHash("sha256").update(content).digest("hex"),
|
|
@@ -5085,7 +5118,7 @@ async function handleSyncTool(client2, args) {
|
|
|
5085
5118
|
}
|
|
5086
5119
|
if (action === "status" && request.logical_path && request.file_present !== false) {
|
|
5087
5120
|
const absolute = safePath(root, String(request.logical_path));
|
|
5088
|
-
request.file_present =
|
|
5121
|
+
request.file_present = import_node_fs6.default.existsSync(absolute);
|
|
5089
5122
|
if (request.file_present && !request.content_hash) {
|
|
5090
5123
|
request.content_hash = fileDescriptor(root, absolute).content_hash;
|
|
5091
5124
|
}
|
|
@@ -5129,12 +5162,12 @@ async function scanRepository(remote, repository, requested, provider, agentId)
|
|
|
5129
5162
|
files: results
|
|
5130
5163
|
};
|
|
5131
5164
|
}
|
|
5132
|
-
var import_node_crypto7,
|
|
5165
|
+
var import_node_crypto7, import_node_fs6, import_node_path5, ok3, err4, SYNC_TOOL;
|
|
5133
5166
|
var init_sync_tool = __esm({
|
|
5134
5167
|
"src/sync-tool.ts"() {
|
|
5135
5168
|
"use strict";
|
|
5136
5169
|
import_node_crypto7 = __toESM(require("crypto"));
|
|
5137
|
-
|
|
5170
|
+
import_node_fs6 = __toESM(require("fs"));
|
|
5138
5171
|
import_node_path5 = __toESM(require("path"));
|
|
5139
5172
|
init_codegraph();
|
|
5140
5173
|
init_error_detail();
|
|
@@ -7804,7 +7837,7 @@ async function main() {
|
|
|
7804
7837
|
const transport = new import_stdio.StdioServerTransport();
|
|
7805
7838
|
await server.connect(transport);
|
|
7806
7839
|
console.error(
|
|
7807
|
-
`MindGraph MCP server v${"0.
|
|
7840
|
+
`MindGraph MCP server v${"0.23.0"} running on stdio (${BASE_URL})`
|
|
7808
7841
|
);
|
|
7809
7842
|
}
|
|
7810
7843
|
var import_stdio, import_node_path6, API_KEY, BASE_URL, AGENT_ID, ORG_ID, PROFILE, HARNESS, client, INSTRUCTIONS, RUNNER_REFRESH, HOOK_SKEW_NOTE, GOVERNANCE_CONFIG, MEMORY_RUNTIME_CONFIG, server;
|
|
@@ -7916,7 +7949,7 @@ Capture knowledge when the user shares something worth remembering:
|
|
|
7916
7949
|
}
|
|
7917
7950
|
RUNNER_REFRESH = (() => {
|
|
7918
7951
|
try {
|
|
7919
|
-
return refreshHookRunner("0.
|
|
7952
|
+
return refreshHookRunner("0.23.0", import_node_path6.default.join(__dirname, "cli.js"));
|
|
7920
7953
|
} catch {
|
|
7921
7954
|
return void 0;
|
|
7922
7955
|
}
|
|
@@ -7930,12 +7963,12 @@ Capture knowledge when the user shares something worth remembering:
|
|
|
7930
7963
|
}
|
|
7931
7964
|
})();
|
|
7932
7965
|
console.error(
|
|
7933
|
-
`[mindgraph-mcp] refreshed pinned hook runner ${RUNNER_REFRESH.runnerVersion ? `v${RUNNER_REFRESH.runnerVersion}` : "(pre-sidecar)"} \u2192 v${"0.
|
|
7966
|
+
`[mindgraph-mcp] refreshed pinned hook runner ${RUNNER_REFRESH.runnerVersion ? `v${RUNNER_REFRESH.runnerVersion}` : "(pre-sidecar)"} \u2192 v${"0.23.0"}` + (refreshedSettings.length > 0 ? `; refreshed owned hook entries in ${refreshedSettings.join(", ")}` : "")
|
|
7934
7967
|
);
|
|
7935
7968
|
}
|
|
7936
7969
|
HOOK_SKEW_NOTE = (() => {
|
|
7937
7970
|
try {
|
|
7938
|
-
return versionSkewNote("0.
|
|
7971
|
+
return versionSkewNote("0.23.0");
|
|
7939
7972
|
} catch {
|
|
7940
7973
|
return void 0;
|
|
7941
7974
|
}
|
|
@@ -7956,7 +7989,7 @@ ${HOOK_SKEW_NOTE}`;
|
|
|
7956
7989
|
}
|
|
7957
7990
|
};
|
|
7958
7991
|
MEMORY_RUNTIME_CONFIG = {
|
|
7959
|
-
serverVersion: "0.
|
|
7992
|
+
serverVersion: "0.23.0",
|
|
7960
7993
|
baseUrl: BASE_URL,
|
|
7961
7994
|
profile: PROFILE,
|
|
7962
7995
|
harness: HARNESS,
|
|
@@ -7965,7 +7998,7 @@ ${HOOK_SKEW_NOTE}`;
|
|
|
7965
7998
|
};
|
|
7966
7999
|
server = buildMindGraphServer({
|
|
7967
8000
|
client,
|
|
7968
|
-
serverVersion: "0.
|
|
8001
|
+
serverVersion: "0.23.0",
|
|
7969
8002
|
instructions: INSTRUCTIONS,
|
|
7970
8003
|
profile: PROFILE,
|
|
7971
8004
|
agentId: AGENT_ID,
|
|
@@ -7980,7 +8013,7 @@ ${HOOK_SKEW_NOTE}`;
|
|
|
7980
8013
|
});
|
|
7981
8014
|
|
|
7982
8015
|
// src/cli.ts
|
|
7983
|
-
var
|
|
8016
|
+
var fs7 = __toESM(require("fs"));
|
|
7984
8017
|
var path7 = __toESM(require("path"));
|
|
7985
8018
|
var os4 = __toESM(require("os"));
|
|
7986
8019
|
var crypto5 = __toESM(require("crypto"));
|
|
@@ -8001,6 +8034,9 @@ function parseArgs(argv) {
|
|
|
8001
8034
|
let agentId = process.env.MINDGRAPH_AGENT_ID;
|
|
8002
8035
|
const positionals = [];
|
|
8003
8036
|
let dir;
|
|
8037
|
+
let promptRecall2;
|
|
8038
|
+
let capture;
|
|
8039
|
+
let sessionId;
|
|
8004
8040
|
for (let i = 0; i < args.length; i++) {
|
|
8005
8041
|
const arg = args[i];
|
|
8006
8042
|
switch (arg) {
|
|
@@ -8033,6 +8069,21 @@ function parseArgs(argv) {
|
|
|
8033
8069
|
case "--hooks":
|
|
8034
8070
|
hooks = true;
|
|
8035
8071
|
break;
|
|
8072
|
+
case "--prompt-recall":
|
|
8073
|
+
promptRecall2 = true;
|
|
8074
|
+
break;
|
|
8075
|
+
case "--no-prompt-recall":
|
|
8076
|
+
promptRecall2 = false;
|
|
8077
|
+
break;
|
|
8078
|
+
case "--capture":
|
|
8079
|
+
capture = true;
|
|
8080
|
+
break;
|
|
8081
|
+
case "--no-capture":
|
|
8082
|
+
capture = false;
|
|
8083
|
+
break;
|
|
8084
|
+
case "--session-id":
|
|
8085
|
+
sessionId = args[++i];
|
|
8086
|
+
break;
|
|
8036
8087
|
case "--owner":
|
|
8037
8088
|
++i;
|
|
8038
8089
|
break;
|
|
@@ -8061,7 +8112,10 @@ function parseArgs(argv) {
|
|
|
8061
8112
|
hooks,
|
|
8062
8113
|
agentId,
|
|
8063
8114
|
positionals,
|
|
8064
|
-
dir
|
|
8115
|
+
dir,
|
|
8116
|
+
promptRecall: promptRecall2,
|
|
8117
|
+
capture,
|
|
8118
|
+
sessionId
|
|
8065
8119
|
};
|
|
8066
8120
|
}
|
|
8067
8121
|
function mcpAddFailureOutput(e) {
|
|
@@ -8445,7 +8499,7 @@ async function skillsPush(api, skillPath) {
|
|
|
8445
8499
|
|
|
8446
8500
|
// src/hook-core.ts
|
|
8447
8501
|
var import_node_crypto4 = __toESM(require("crypto"));
|
|
8448
|
-
var
|
|
8502
|
+
var import_node_fs4 = __toESM(require("fs"));
|
|
8449
8503
|
var import_node_os3 = __toESM(require("os"));
|
|
8450
8504
|
var import_node_path3 = __toESM(require("path"));
|
|
8451
8505
|
var import_node_child_process2 = require("child_process");
|
|
@@ -8453,6 +8507,175 @@ init_codegraph();
|
|
|
8453
8507
|
init_code_tool();
|
|
8454
8508
|
init_error_detail();
|
|
8455
8509
|
init_hook_env();
|
|
8510
|
+
|
|
8511
|
+
// src/hook-memory.ts
|
|
8512
|
+
var import_node_fs3 = __toESM(require("fs"));
|
|
8513
|
+
var PROMPT_RECALL_LIMIT = 5;
|
|
8514
|
+
var PROMPT_RECALL_MAX_CHARS = 4e3;
|
|
8515
|
+
var MIN_PROMPT_RECALL_CHARS = 12;
|
|
8516
|
+
var CAPTURE_MAX_CHARS = 2e5;
|
|
8517
|
+
var STOPWORDS = new Set(
|
|
8518
|
+
"a an and are as at be but by can could did do does for from had has have how i if in into is it its me my of on or our please should so that the their them then there these they this to us was we were what when where which who why will with would you your about just also any some tell know want need let lets like get got make".split(" ")
|
|
8519
|
+
);
|
|
8520
|
+
var WORD = /[A-Za-z0-9][A-Za-z0-9_.\-/#+]*/g;
|
|
8521
|
+
var SECRET_PATTERNS = [
|
|
8522
|
+
[/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, "[REDACTED]"],
|
|
8523
|
+
[/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g, "[REDACTED]"],
|
|
8524
|
+
[/\b(?:mg|sk|pk|rk)[_-](?:live[_-]|test[_-]|ant[_-]|proj[_-])?[A-Za-z0-9_-]{16,}/g, "[REDACTED]"],
|
|
8525
|
+
[/\b(?:gh[pousr]_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{20,})/g, "[REDACTED]"],
|
|
8526
|
+
[/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g, "[REDACTED]"],
|
|
8527
|
+
[/\bxox[abprs]-[A-Za-z0-9-]{10,}/g, "[REDACTED]"],
|
|
8528
|
+
[/\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/gi, "Bearer [REDACTED]"],
|
|
8529
|
+
[
|
|
8530
|
+
/\b((?:api[_-]?key|secret|token|password|passwd|access[_-]?key|client[_-]?secret)\b["']?\s*[:=]\s*)["']?[^\s"',;]{8,}["']?/gi,
|
|
8531
|
+
"$1[REDACTED]"
|
|
8532
|
+
]
|
|
8533
|
+
];
|
|
8534
|
+
function redactSecrets(text2) {
|
|
8535
|
+
let out = text2;
|
|
8536
|
+
for (const [pattern, replacement] of SECRET_PATTERNS) {
|
|
8537
|
+
out = out.replace(pattern, replacement);
|
|
8538
|
+
}
|
|
8539
|
+
return out;
|
|
8540
|
+
}
|
|
8541
|
+
function promptRecallQuery(prompt, maxTerms = 8) {
|
|
8542
|
+
if (typeof prompt !== "string") return "";
|
|
8543
|
+
const trimmed = prompt.trim();
|
|
8544
|
+
if (trimmed.length < MIN_PROMPT_RECALL_CHARS || trimmed.startsWith("/")) return "";
|
|
8545
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8546
|
+
for (const match of redactSecrets(trimmed.slice(0, 600)).matchAll(WORD)) {
|
|
8547
|
+
const key = match[0].toLowerCase().replace(/^[.\-/]+|[.\-/]+$/g, "");
|
|
8548
|
+
if (key.length < 2 || STOPWORDS.has(key) || key === "redacted" || seen.has(key)) continue;
|
|
8549
|
+
seen.add(key);
|
|
8550
|
+
if (seen.size >= maxTerms) break;
|
|
8551
|
+
}
|
|
8552
|
+
return [...seen].join(" ");
|
|
8553
|
+
}
|
|
8554
|
+
function promptRecallRequest(query) {
|
|
8555
|
+
return {
|
|
8556
|
+
query,
|
|
8557
|
+
node_limit: PROMPT_RECALL_LIMIT,
|
|
8558
|
+
chunk_limit: 0,
|
|
8559
|
+
graph_expansion_limit: 3,
|
|
8560
|
+
graph_max_depth: 2
|
|
8561
|
+
};
|
|
8562
|
+
}
|
|
8563
|
+
var rec = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
8564
|
+
var str = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
8565
|
+
var oneLine = (value, max) => {
|
|
8566
|
+
const flat = value.replace(/\s+/g, " ").trim();
|
|
8567
|
+
return flat.length <= max ? flat : `${flat.slice(0, max - 1)}\u2026`;
|
|
8568
|
+
};
|
|
8569
|
+
var INFRA_TYPES = /* @__PURE__ */ new Set(["Chunk", "Document", "Article", "Session"]);
|
|
8570
|
+
function nodeType(node) {
|
|
8571
|
+
const plain = str(node.node_type);
|
|
8572
|
+
return (plain && plain !== "Custom" ? plain : void 0) ?? str(rec(node.node_type)?.Custom) ?? plain ?? "Node";
|
|
8573
|
+
}
|
|
8574
|
+
function renderPromptRecall(response) {
|
|
8575
|
+
const payload = rec(response);
|
|
8576
|
+
if (!payload) return void 0;
|
|
8577
|
+
const lines = [];
|
|
8578
|
+
const nodes = Array.isArray(rec(payload.graph)?.nodes) ? rec(payload.graph).nodes : [];
|
|
8579
|
+
for (const raw of nodes) {
|
|
8580
|
+
const node = rec(raw);
|
|
8581
|
+
if (!node || lines.length >= PROMPT_RECALL_LIMIT) continue;
|
|
8582
|
+
const type = nodeType(node);
|
|
8583
|
+
if (INFRA_TYPES.has(type)) continue;
|
|
8584
|
+
const props = rec(node.props);
|
|
8585
|
+
const label = str(node.label) ?? str(props?.label) ?? str(props?.name);
|
|
8586
|
+
if (!label) continue;
|
|
8587
|
+
const summary = str(node.summary) ?? str(props?.summary) ?? str(props?.description) ?? str(props?.content);
|
|
8588
|
+
const docs = Array.isArray(node.source_documents) ? node.source_documents : [];
|
|
8589
|
+
const source = str(rec(docs[0])?.title);
|
|
8590
|
+
let line = `- [${type}] ${oneLine(label, 140)}`;
|
|
8591
|
+
if (node.superseded === true) line += " (superseded)";
|
|
8592
|
+
if (summary && summary !== label) line += ` \u2014 ${oneLine(summary, 240)}`;
|
|
8593
|
+
if (source) line += ` (source: ${oneLine(source, 100)})`;
|
|
8594
|
+
lines.push(line);
|
|
8595
|
+
}
|
|
8596
|
+
const articles = Array.isArray(payload.articles) ? payload.articles : [];
|
|
8597
|
+
for (const raw of articles) {
|
|
8598
|
+
const article = rec(raw);
|
|
8599
|
+
if (!article || lines.length >= PROMPT_RECALL_LIMIT) continue;
|
|
8600
|
+
const label = str(article.label);
|
|
8601
|
+
if (!label) continue;
|
|
8602
|
+
const content = str(article.content);
|
|
8603
|
+
lines.push(`- [Article] ${oneLine(label, 140)}${content ? ` \u2014 ${oneLine(content, 240)}` : ""}`);
|
|
8604
|
+
}
|
|
8605
|
+
if (lines.length === 0) return void 0;
|
|
8606
|
+
let block = "MindGraph recall (bounded):";
|
|
8607
|
+
for (const line of lines) {
|
|
8608
|
+
if (block.length + 1 + line.length > PROMPT_RECALL_MAX_CHARS) break;
|
|
8609
|
+
block += `
|
|
8610
|
+
${line}`;
|
|
8611
|
+
}
|
|
8612
|
+
return block;
|
|
8613
|
+
}
|
|
8614
|
+
function messageText(content) {
|
|
8615
|
+
if (typeof content === "string") return content;
|
|
8616
|
+
if (!Array.isArray(content)) return void 0;
|
|
8617
|
+
const parts = content.map((block) => rec(block)).filter((block) => block?.type === "text" && typeof block.text === "string").map((block) => block.text);
|
|
8618
|
+
return parts.length > 0 ? parts.join("\n") : void 0;
|
|
8619
|
+
}
|
|
8620
|
+
function stripHarnessNoise(text2) {
|
|
8621
|
+
return text2.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").replace(/<local-command-(stdout|stderr)>[\s\S]*?<\/local-command-\1>/g, "").replace(/<command-(name|message|args)>[\s\S]*?<\/command-\1>/g, "").replace(/<local-command-caveat>[\s\S]*?<\/local-command-caveat>/g, "").trim();
|
|
8622
|
+
}
|
|
8623
|
+
function extractTranscriptTurns(jsonl) {
|
|
8624
|
+
const turns = [];
|
|
8625
|
+
for (const line of jsonl.split("\n")) {
|
|
8626
|
+
if (!line.trim()) continue;
|
|
8627
|
+
let entry;
|
|
8628
|
+
try {
|
|
8629
|
+
entry = rec(JSON.parse(line));
|
|
8630
|
+
} catch {
|
|
8631
|
+
continue;
|
|
8632
|
+
}
|
|
8633
|
+
if (!entry || entry.type !== "user" && entry.type !== "assistant") continue;
|
|
8634
|
+
if (entry.isMeta === true || entry.isSidechain === true || entry.isCompactSummary === true) continue;
|
|
8635
|
+
const message = rec(entry.message);
|
|
8636
|
+
const raw = messageText(message?.content);
|
|
8637
|
+
if (!raw) continue;
|
|
8638
|
+
const text2 = stripHarnessNoise(raw);
|
|
8639
|
+
if (!text2) continue;
|
|
8640
|
+
turns.push({ role: entry.type, text: text2 });
|
|
8641
|
+
}
|
|
8642
|
+
return turns;
|
|
8643
|
+
}
|
|
8644
|
+
function renderTranscript(turns) {
|
|
8645
|
+
const rendered2 = turns.map((turn) => `${turn.role === "user" ? "User" : "Assistant"}: ${redactSecrets(turn.text)}`).join("\n\n");
|
|
8646
|
+
if (rendered2.length <= CAPTURE_MAX_CHARS) return rendered2;
|
|
8647
|
+
const tail = rendered2.slice(rendered2.length - CAPTURE_MAX_CHARS);
|
|
8648
|
+
const start = tail.search(/\n\n(User|Assistant): /);
|
|
8649
|
+
return start >= 0 ? tail.slice(start + 2) : tail;
|
|
8650
|
+
}
|
|
8651
|
+
function readTranscriptSince(file, offset = 0) {
|
|
8652
|
+
const buffer = import_node_fs3.default.readFileSync(file);
|
|
8653
|
+
const start = offset > buffer.length ? 0 : offset;
|
|
8654
|
+
const lastNewline = buffer.lastIndexOf(10);
|
|
8655
|
+
if (lastNewline < start) return { text: "", nextOffset: start };
|
|
8656
|
+
return {
|
|
8657
|
+
text: buffer.subarray(start, lastNewline + 1).toString("utf8"),
|
|
8658
|
+
nextOffset: lastNewline + 1
|
|
8659
|
+
};
|
|
8660
|
+
}
|
|
8661
|
+
function buildCaptureRequest(transcriptPath, sessionId, offset, ids) {
|
|
8662
|
+
const { text: text2, nextOffset } = readTranscriptSince(transcriptPath, offset);
|
|
8663
|
+
const turns = extractTranscriptTurns(text2);
|
|
8664
|
+
const content = renderTranscript(turns);
|
|
8665
|
+
if (!content) return { nextOffset, turns: 0 };
|
|
8666
|
+
return {
|
|
8667
|
+
body: {
|
|
8668
|
+
content,
|
|
8669
|
+
title: `Claude Code session ${sessionId}`,
|
|
8670
|
+
...ids.sessionUid ? { session_uid: ids.sessionUid } : {},
|
|
8671
|
+
...ids.agentId ? { agent_id: ids.agentId } : {}
|
|
8672
|
+
},
|
|
8673
|
+
nextOffset,
|
|
8674
|
+
turns: turns.length
|
|
8675
|
+
};
|
|
8676
|
+
}
|
|
8677
|
+
|
|
8678
|
+
// src/hook-core.ts
|
|
8456
8679
|
var NOOP = { kind: "noop" };
|
|
8457
8680
|
function defaultLedger(sessionId, options) {
|
|
8458
8681
|
return {
|
|
@@ -8482,21 +8705,21 @@ function waitBriefly(ms) {
|
|
|
8482
8705
|
}
|
|
8483
8706
|
function withLedger(sessionId, options, update) {
|
|
8484
8707
|
const file = ledgerPath(sessionId, options);
|
|
8485
|
-
|
|
8708
|
+
import_node_fs4.default.mkdirSync(import_node_path3.default.dirname(file), { recursive: true, mode: 448 });
|
|
8486
8709
|
try {
|
|
8487
|
-
|
|
8710
|
+
import_node_fs4.default.chmodSync(import_node_path3.default.dirname(file), 448);
|
|
8488
8711
|
} catch {
|
|
8489
8712
|
}
|
|
8490
8713
|
const lock = `${file}.lock`;
|
|
8491
8714
|
let lockFd;
|
|
8492
8715
|
for (let attempt = 0; attempt < 400; attempt++) {
|
|
8493
8716
|
try {
|
|
8494
|
-
lockFd =
|
|
8717
|
+
lockFd = import_node_fs4.default.openSync(lock, "wx", 384);
|
|
8495
8718
|
break;
|
|
8496
8719
|
} catch (error2) {
|
|
8497
8720
|
if (error2.code !== "EEXIST") throw error2;
|
|
8498
8721
|
try {
|
|
8499
|
-
if (Date.now() -
|
|
8722
|
+
if (Date.now() - import_node_fs4.default.statSync(lock).mtimeMs > 3e3) import_node_fs4.default.unlinkSync(lock);
|
|
8500
8723
|
} catch {
|
|
8501
8724
|
}
|
|
8502
8725
|
waitBriefly(10);
|
|
@@ -8510,25 +8733,25 @@ function withLedger(sessionId, options, update) {
|
|
|
8510
8733
|
try {
|
|
8511
8734
|
ledger = {
|
|
8512
8735
|
...ledger,
|
|
8513
|
-
...JSON.parse(
|
|
8736
|
+
...JSON.parse(import_node_fs4.default.readFileSync(file, "utf8"))
|
|
8514
8737
|
};
|
|
8515
8738
|
} catch {
|
|
8516
8739
|
}
|
|
8517
8740
|
const result = update(ledger);
|
|
8518
8741
|
ledger.updatedAt = Date.now();
|
|
8519
8742
|
const temporary = `${file}.${process.pid}.${import_node_crypto4.default.randomUUID()}.tmp`;
|
|
8520
|
-
|
|
8743
|
+
import_node_fs4.default.writeFileSync(temporary, `${JSON.stringify(ledger)}
|
|
8521
8744
|
`, { mode: 384 });
|
|
8522
|
-
|
|
8745
|
+
import_node_fs4.default.renameSync(temporary, file);
|
|
8523
8746
|
try {
|
|
8524
|
-
|
|
8747
|
+
import_node_fs4.default.chmodSync(file, 384);
|
|
8525
8748
|
} catch {
|
|
8526
8749
|
}
|
|
8527
8750
|
return result;
|
|
8528
8751
|
} finally {
|
|
8529
|
-
|
|
8752
|
+
import_node_fs4.default.closeSync(lockFd);
|
|
8530
8753
|
try {
|
|
8531
|
-
|
|
8754
|
+
import_node_fs4.default.unlinkSync(lock);
|
|
8532
8755
|
} catch {
|
|
8533
8756
|
}
|
|
8534
8757
|
}
|
|
@@ -8807,8 +9030,8 @@ function claudeMcpRegistrationNote(input, options) {
|
|
|
8807
9030
|
if (process.env.MINDGRAPH_SKIP_MCP_REGISTRATION_CHECK === "1") return void 0;
|
|
8808
9031
|
try {
|
|
8809
9032
|
const configPath = options.claudeConfigPath || import_node_path3.default.join(import_node_os3.default.homedir(), ".claude.json");
|
|
8810
|
-
if (!
|
|
8811
|
-
const config = JSON.parse(
|
|
9033
|
+
if (!import_node_fs4.default.existsSync(configPath)) return void 0;
|
|
9034
|
+
const config = JSON.parse(import_node_fs4.default.readFileSync(configPath, "utf8"));
|
|
8812
9035
|
if (hasMindgraphServer(config.mcpServers)) return void 0;
|
|
8813
9036
|
const cwd = import_node_path3.default.resolve(input.cwd || ".");
|
|
8814
9037
|
for (const [projectDir, project] of Object.entries(config.projects || {})) {
|
|
@@ -8817,8 +9040,8 @@ function claudeMcpRegistrationNote(input, options) {
|
|
|
8817
9040
|
}
|
|
8818
9041
|
for (let dir = cwd; ; dir = import_node_path3.default.dirname(dir)) {
|
|
8819
9042
|
const candidate = import_node_path3.default.join(dir, ".mcp.json");
|
|
8820
|
-
if (
|
|
8821
|
-
const parsed = JSON.parse(
|
|
9043
|
+
if (import_node_fs4.default.existsSync(candidate)) {
|
|
9044
|
+
const parsed = JSON.parse(import_node_fs4.default.readFileSync(candidate, "utf8"));
|
|
8822
9045
|
if (hasMindgraphServer(parsed.mcpServers)) return void 0;
|
|
8823
9046
|
}
|
|
8824
9047
|
if (dir === import_node_path3.default.dirname(dir)) break;
|
|
@@ -9170,6 +9393,9 @@ function stop(input, options) {
|
|
|
9170
9393
|
}
|
|
9171
9394
|
async function sessionEnd(input, client2, options) {
|
|
9172
9395
|
const harnessLabel = options.harness === "claude-code" ? "Claude Code" : "Codex";
|
|
9396
|
+
if (options.harness === "claude-code") {
|
|
9397
|
+
await captureTranscript(input, client2, options);
|
|
9398
|
+
}
|
|
9173
9399
|
const ledger = readLedger(input.session_id, options);
|
|
9174
9400
|
if (ledger.executionUid && ledger.taskUid && ledger.sessionUid && ledger.taskVersion !== void 0 && ledger.leaseEpoch !== void 0) {
|
|
9175
9401
|
try {
|
|
@@ -9203,6 +9429,77 @@ async function sessionEnd(input, client2, options) {
|
|
|
9203
9429
|
}
|
|
9204
9430
|
return NOOP;
|
|
9205
9431
|
}
|
|
9432
|
+
function withDeadline(promise, ms) {
|
|
9433
|
+
let timer;
|
|
9434
|
+
const deadline = new Promise((_, reject) => {
|
|
9435
|
+
timer = setTimeout(() => reject(new Error("deadline")), ms);
|
|
9436
|
+
timer.unref?.();
|
|
9437
|
+
});
|
|
9438
|
+
return Promise.race([promise, deadline]).finally(() => clearTimeout(timer));
|
|
9439
|
+
}
|
|
9440
|
+
async function promptRecall(input, client2, options) {
|
|
9441
|
+
if (options.promptRecall === false || process.env.MINDGRAPH_HOOK_PROMPT_RECALL === "off" || !client2.retrieveContext) {
|
|
9442
|
+
return NOOP;
|
|
9443
|
+
}
|
|
9444
|
+
const query = promptRecallQuery(input.prompt);
|
|
9445
|
+
if (!query) return NOOP;
|
|
9446
|
+
try {
|
|
9447
|
+
const response = await withDeadline(
|
|
9448
|
+
client2.retrieveContext(promptRecallRequest(query)),
|
|
9449
|
+
options.promptRecallTimeoutMs ?? 4e3
|
|
9450
|
+
);
|
|
9451
|
+
const block = renderPromptRecall(response);
|
|
9452
|
+
if (!block) return NOOP;
|
|
9453
|
+
const hash = import_node_crypto4.default.createHash("sha256").update(block).digest("hex");
|
|
9454
|
+
const fresh = withLedger(input.session_id, options, (ledger) => {
|
|
9455
|
+
if (ledger.lastRecallHash === hash) return false;
|
|
9456
|
+
ledger.lastRecallHash = hash;
|
|
9457
|
+
return true;
|
|
9458
|
+
});
|
|
9459
|
+
return fresh ? { kind: "context", context: block, event: "UserPromptSubmit" } : NOOP;
|
|
9460
|
+
} catch {
|
|
9461
|
+
return NOOP;
|
|
9462
|
+
}
|
|
9463
|
+
}
|
|
9464
|
+
async function captureTranscript(input, client2, options) {
|
|
9465
|
+
const enabled = process.env.MINDGRAPH_HOOK_CAPTURE === "on" || options.capture === true && process.env.MINDGRAPH_HOOK_CAPTURE !== "off";
|
|
9466
|
+
if (!enabled || !client2.ingestSession || !input.transcript_path) return;
|
|
9467
|
+
try {
|
|
9468
|
+
const agentId = resolveAgentId(options);
|
|
9469
|
+
const ledger = readLedger(input.session_id, options);
|
|
9470
|
+
let sessionUid = ledger.sessionUid;
|
|
9471
|
+
const prepared = buildCaptureRequest(
|
|
9472
|
+
input.transcript_path,
|
|
9473
|
+
input.session_id,
|
|
9474
|
+
ledger.captureOffset ?? 0,
|
|
9475
|
+
{ sessionUid, agentId }
|
|
9476
|
+
);
|
|
9477
|
+
if (prepared.body && !sessionUid) {
|
|
9478
|
+
const opened = object(
|
|
9479
|
+
await client2.session({
|
|
9480
|
+
action: "open",
|
|
9481
|
+
label: `Claude Code ${input.session_id}`,
|
|
9482
|
+
harness: options.harness,
|
|
9483
|
+
harness_session_id: input.session_id,
|
|
9484
|
+
model: input.model,
|
|
9485
|
+
agent_id: agentId
|
|
9486
|
+
})
|
|
9487
|
+
);
|
|
9488
|
+
sessionUid = string(opened?.uid);
|
|
9489
|
+
if (sessionUid) prepared.body.session_uid = sessionUid;
|
|
9490
|
+
}
|
|
9491
|
+
if (prepared.body) await client2.ingestSession(prepared.body);
|
|
9492
|
+
withLedger(input.session_id, options, (current) => {
|
|
9493
|
+
if (sessionUid) current.sessionUid ??= sessionUid;
|
|
9494
|
+
current.captureOffset = Math.max(current.captureOffset ?? 0, prepared.nextOffset);
|
|
9495
|
+
return void 0;
|
|
9496
|
+
});
|
|
9497
|
+
} catch (error2) {
|
|
9498
|
+
console.error(
|
|
9499
|
+
`mindgraph-hook: transcript capture failed (${error2?.message || "unknown error"})`
|
|
9500
|
+
);
|
|
9501
|
+
}
|
|
9502
|
+
}
|
|
9206
9503
|
async function runHookCore(input, client2, options) {
|
|
9207
9504
|
switch (input.hook_event_name) {
|
|
9208
9505
|
case "SessionStart":
|
|
@@ -9228,6 +9525,11 @@ async function runHookCore(input, client2, options) {
|
|
|
9228
9525
|
return stop(input, options);
|
|
9229
9526
|
case "SessionEnd":
|
|
9230
9527
|
return sessionEnd(input, client2, options);
|
|
9528
|
+
case "UserPromptSubmit":
|
|
9529
|
+
return promptRecall(input, client2, options);
|
|
9530
|
+
case "PreCompact":
|
|
9531
|
+
await captureTranscript(input, client2, options);
|
|
9532
|
+
return NOOP;
|
|
9231
9533
|
default:
|
|
9232
9534
|
return NOOP;
|
|
9233
9535
|
}
|
|
@@ -9239,6 +9541,26 @@ async function readHookInput(stream = process.stdin) {
|
|
|
9239
9541
|
}
|
|
9240
9542
|
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
9241
9543
|
}
|
|
9544
|
+
function previewCapture(transcriptPath, sessionId, options = {}) {
|
|
9545
|
+
let ledger = {};
|
|
9546
|
+
try {
|
|
9547
|
+
ledger = JSON.parse(
|
|
9548
|
+
import_node_fs4.default.readFileSync(
|
|
9549
|
+
ledgerPath(sessionId, { ...options, harness: "claude-code" }),
|
|
9550
|
+
"utf8"
|
|
9551
|
+
)
|
|
9552
|
+
);
|
|
9553
|
+
} catch {
|
|
9554
|
+
}
|
|
9555
|
+
const offset = ledger.captureOffset ?? 0;
|
|
9556
|
+
return {
|
|
9557
|
+
...buildCaptureRequest(transcriptPath, sessionId, offset, {
|
|
9558
|
+
sessionUid: ledger.sessionUid,
|
|
9559
|
+
agentId: options.agentId || process.env.MINDGRAPH_AGENT_ID || stableAgentId()
|
|
9560
|
+
}),
|
|
9561
|
+
offset
|
|
9562
|
+
};
|
|
9563
|
+
}
|
|
9242
9564
|
|
|
9243
9565
|
// src/claude-hooks.ts
|
|
9244
9566
|
async function runClaudeHook(input, client2, options = {}) {
|
|
@@ -9253,7 +9575,7 @@ async function runClaudeHook(input, client2, options = {}) {
|
|
|
9253
9575
|
case "context":
|
|
9254
9576
|
return {
|
|
9255
9577
|
hookSpecificOutput: {
|
|
9256
|
-
hookEventName: "SessionStart",
|
|
9578
|
+
hookEventName: output.event ?? "SessionStart",
|
|
9257
9579
|
additionalContext: output.context
|
|
9258
9580
|
}
|
|
9259
9581
|
};
|
|
@@ -9283,7 +9605,7 @@ async function runCodexHook(input, client2, options = {}) {
|
|
|
9283
9605
|
case "context":
|
|
9284
9606
|
return {
|
|
9285
9607
|
hookSpecificOutput: {
|
|
9286
|
-
hookEventName: "SessionStart",
|
|
9608
|
+
hookEventName: output.event ?? "SessionStart",
|
|
9287
9609
|
additionalContext: output.context
|
|
9288
9610
|
}
|
|
9289
9611
|
};
|
|
@@ -9307,14 +9629,14 @@ init_hook_installer();
|
|
|
9307
9629
|
function recordHookHealth(harness, error2) {
|
|
9308
9630
|
try {
|
|
9309
9631
|
const dir = process.env.MINDGRAPH_RUNTIME_DIR || path7.join(os4.homedir(), ".mindgraph", "runtime");
|
|
9310
|
-
|
|
9632
|
+
fs7.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
9311
9633
|
const file = path7.join(dir, "hook-health.json");
|
|
9312
9634
|
let failures = 0;
|
|
9313
9635
|
try {
|
|
9314
|
-
failures = Number(JSON.parse(
|
|
9636
|
+
failures = Number(JSON.parse(fs7.readFileSync(file, "utf8")).consecutiveFailures) || 0;
|
|
9315
9637
|
} catch {
|
|
9316
9638
|
}
|
|
9317
|
-
|
|
9639
|
+
fs7.writeFileSync(
|
|
9318
9640
|
file,
|
|
9319
9641
|
`${JSON.stringify({
|
|
9320
9642
|
harness,
|
|
@@ -9334,10 +9656,10 @@ function clearHookHealth(harness) {
|
|
|
9334
9656
|
process.env.MINDGRAPH_RUNTIME_DIR || path7.join(os4.homedir(), ".mindgraph", "runtime"),
|
|
9335
9657
|
"hook-health.json"
|
|
9336
9658
|
);
|
|
9337
|
-
if (!
|
|
9338
|
-
const current = JSON.parse(
|
|
9659
|
+
if (!fs7.existsSync(file)) return;
|
|
9660
|
+
const current = JSON.parse(fs7.readFileSync(file, "utf8"));
|
|
9339
9661
|
if (!Number(current.consecutiveFailures)) return;
|
|
9340
|
-
|
|
9662
|
+
fs7.writeFileSync(
|
|
9341
9663
|
file,
|
|
9342
9664
|
`${JSON.stringify({
|
|
9343
9665
|
harness,
|
|
@@ -9378,16 +9700,16 @@ function getClaudeDesktopConfigPath() {
|
|
|
9378
9700
|
}
|
|
9379
9701
|
}
|
|
9380
9702
|
function readJsonFile(filePath) {
|
|
9381
|
-
if (!
|
|
9382
|
-
const content =
|
|
9703
|
+
if (!fs7.existsSync(filePath)) return {};
|
|
9704
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
9383
9705
|
return JSON.parse(content);
|
|
9384
9706
|
}
|
|
9385
9707
|
function writeJsonFile(filePath, data) {
|
|
9386
9708
|
const dir = path7.dirname(filePath);
|
|
9387
|
-
if (!
|
|
9388
|
-
|
|
9709
|
+
if (!fs7.existsSync(dir)) {
|
|
9710
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
9389
9711
|
}
|
|
9390
|
-
|
|
9712
|
+
fs7.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
9391
9713
|
}
|
|
9392
9714
|
function getMcpServerEntry(apiKey, baseUrl) {
|
|
9393
9715
|
const env = { MINDGRAPH_API_KEY: apiKey };
|
|
@@ -9474,7 +9796,7 @@ function installClaudeCode(apiKey, baseUrl, agentId) {
|
|
|
9474
9796
|
}
|
|
9475
9797
|
function uninstallClaudeDesktop() {
|
|
9476
9798
|
const configPath = getClaudeDesktopConfigPath();
|
|
9477
|
-
if (!
|
|
9799
|
+
if (!fs7.existsSync(configPath)) {
|
|
9478
9800
|
console.log("Claude Desktop config not found. Nothing to remove.");
|
|
9479
9801
|
return;
|
|
9480
9802
|
}
|
|
@@ -9501,7 +9823,7 @@ function uninstallClaudeCode() {
|
|
|
9501
9823
|
}
|
|
9502
9824
|
function printStatus() {
|
|
9503
9825
|
const desktopPath = getClaudeDesktopConfigPath();
|
|
9504
|
-
if (
|
|
9826
|
+
if (fs7.existsSync(desktopPath)) {
|
|
9505
9827
|
const config = readJsonFile(desktopPath);
|
|
9506
9828
|
const servers = config.mcpServers;
|
|
9507
9829
|
if (servers && "mindgraph" in servers) {
|
|
@@ -9523,8 +9845,8 @@ function printStatus() {
|
|
|
9523
9845
|
process.env.MINDGRAPH_RUNTIME_DIR || path7.join(os4.homedir(), ".mindgraph", "runtime"),
|
|
9524
9846
|
"hook-health.json"
|
|
9525
9847
|
);
|
|
9526
|
-
if (
|
|
9527
|
-
const health = JSON.parse(
|
|
9848
|
+
if (fs7.existsSync(healthFile)) {
|
|
9849
|
+
const health = JSON.parse(fs7.readFileSync(healthFile, "utf8"));
|
|
9528
9850
|
const failures = Number(health.consecutiveFailures) || 0;
|
|
9529
9851
|
console.log(
|
|
9530
9852
|
failures > 0 ? `Hooks: FAILING (${failures} consecutive; last: ${health.lastError} at ${health.lastFailureAt})` : `Hooks: healthy (last recovery ${health.lastOkAt || "unknown"})`
|
|
@@ -9539,17 +9861,46 @@ function printStatus() {
|
|
|
9539
9861
|
function withHooksFlag() {
|
|
9540
9862
|
return process.argv.includes("--hooks");
|
|
9541
9863
|
}
|
|
9864
|
+
function claudeHookFeatures() {
|
|
9865
|
+
const flags = parseArgs(process.argv);
|
|
9866
|
+
const stored = loadHookEnv();
|
|
9867
|
+
return {
|
|
9868
|
+
promptRecall: flags.promptRecall ?? stored.promptRecall ?? true,
|
|
9869
|
+
capture: flags.capture ?? stored.capture ?? false
|
|
9870
|
+
};
|
|
9871
|
+
}
|
|
9872
|
+
function printClaudeHookFeatures(features) {
|
|
9873
|
+
console.log(
|
|
9874
|
+
`Prompt recall (UserPromptSubmit): ${features.promptRecall ? "on" : "off"}; transcript capture (PreCompact/SessionEnd): ${features.capture ? "ON \u2014 user and assistant text (never tool calls or tool output), secrets redacted, is sent to MindGraph /ingest/session" : "off (enable with --capture)"}`
|
|
9875
|
+
);
|
|
9876
|
+
}
|
|
9877
|
+
function latestTranscript(cwd) {
|
|
9878
|
+
const dir = path7.join(
|
|
9879
|
+
os4.homedir(),
|
|
9880
|
+
".claude",
|
|
9881
|
+
"projects",
|
|
9882
|
+
cwd.replace(/[^A-Za-z0-9]/g, "-")
|
|
9883
|
+
);
|
|
9884
|
+
try {
|
|
9885
|
+
return fs7.readdirSync(dir).filter((name) => name.endsWith(".jsonl")).map((name) => path7.join(dir, name)).sort((a, b) => fs7.statSync(b).mtimeMs - fs7.statSync(a).mtimeMs)[0];
|
|
9886
|
+
} catch {
|
|
9887
|
+
return void 0;
|
|
9888
|
+
}
|
|
9889
|
+
}
|
|
9542
9890
|
function finishCodeInstall(apiKey, baseUrl, withHooks, agentId) {
|
|
9543
9891
|
if (withHooks) {
|
|
9544
|
-
const runner = installHookRunner(__filename, void 0, "0.
|
|
9892
|
+
const runner = installHookRunner(__filename, void 0, "0.23.0");
|
|
9545
9893
|
console.log(`Installed pinned hook runner at ${runner}`);
|
|
9546
|
-
const
|
|
9894
|
+
const features = claudeHookFeatures();
|
|
9895
|
+
const result = installClaudeHooks("user", void 0, features);
|
|
9547
9896
|
console.log(
|
|
9548
9897
|
`Installed ${result.added} and refreshed ${result.updated} MindGraph Claude Code hook entries in ${result.path}`
|
|
9549
9898
|
);
|
|
9899
|
+
printClaudeHookFeatures(features);
|
|
9550
9900
|
const envPath = saveHookEnv({
|
|
9551
9901
|
apiKey,
|
|
9552
9902
|
baseUrl,
|
|
9903
|
+
...features,
|
|
9553
9904
|
// Default the stable per-user identity only when nothing is stored —
|
|
9554
9905
|
// an explicit or previously persisted agentId must survive reinstalls.
|
|
9555
9906
|
agentId: agentId || process.env.MINDGRAPH_AGENT_ID || loadHookEnv().agentId || stableAgentId(),
|
|
@@ -9588,6 +9939,9 @@ USAGE:
|
|
|
9588
9939
|
mindgraph-mcp uninstall-code Remove from Claude Code
|
|
9589
9940
|
mindgraph-mcp install-hooks --harness claude-code|codex [--scope user|project]
|
|
9590
9941
|
mindgraph-mcp uninstall-hooks --harness claude-code|codex [--scope user|project]
|
|
9942
|
+
mindgraph-mcp hooks preview [transcript_path] [--session-id <id>]
|
|
9943
|
+
Print exactly what transcript capture would
|
|
9944
|
+
send (POST /ingest/session); sends nothing.
|
|
9591
9945
|
mindgraph-mcp status Show installation status
|
|
9592
9946
|
mindgraph-mcp skills pull [--dir .claude/skills]
|
|
9593
9947
|
Write your org's published+granted skills as
|
|
@@ -9610,6 +9964,10 @@ OPTIONS:
|
|
|
9610
9964
|
--scope <scope> Hook settings scope: project (default) or user
|
|
9611
9965
|
--project-dir <dir> Project root for project-scoped hooks
|
|
9612
9966
|
--harness <name> Hook harness (claude-code or codex)
|
|
9967
|
+
--no-prompt-recall Claude hooks: omit per-prompt recall (UserPromptSubmit)
|
|
9968
|
+
--capture Claude hooks: send user/assistant text to MindGraph
|
|
9969
|
+
before compaction and at session end (off by default;
|
|
9970
|
+
--no-capture turns it back off)
|
|
9613
9971
|
--help, -h Show this help message
|
|
9614
9972
|
|
|
9615
9973
|
EXAMPLES:
|
|
@@ -9856,16 +10214,19 @@ ${total} skill(s) synced.`);
|
|
|
9856
10214
|
if (hookHarness !== "claude-code" && hookHarness !== "codex") {
|
|
9857
10215
|
throw new Error("--harness must be claude-code or codex");
|
|
9858
10216
|
}
|
|
9859
|
-
const runner = installHookRunner(__filename, void 0, "0.
|
|
10217
|
+
const runner = installHookRunner(__filename, void 0, "0.23.0");
|
|
9860
10218
|
console.log(`Installed pinned hook runner at ${runner}`);
|
|
9861
|
-
const
|
|
10219
|
+
const features = hookHarness === "claude-code" ? claudeHookFeatures() : void 0;
|
|
10220
|
+
const result = hookHarness === "codex" ? installCodexHooks(scope, projectDir) : installClaudeHooks(scope, projectDir, features);
|
|
9862
10221
|
console.log(
|
|
9863
10222
|
`Installed ${result.added} and refreshed ${result.updated} MindGraph ${hookHarness === "codex" ? "Codex" : "Claude Code"} hook entries in ${result.path}`
|
|
9864
10223
|
);
|
|
10224
|
+
if (features) printClaudeHookFeatures(features);
|
|
9865
10225
|
printCodegraphStatus();
|
|
9866
10226
|
const envPath = saveHookEnv({
|
|
9867
10227
|
apiKey,
|
|
9868
10228
|
baseUrl,
|
|
10229
|
+
...features || {},
|
|
9869
10230
|
// Default the stable per-user identity only when nothing is stored —
|
|
9870
10231
|
// an explicit or previously persisted agentId must survive
|
|
9871
10232
|
// reinstalls.
|
|
@@ -9917,9 +10278,14 @@ ${total} skill(s) synced.`);
|
|
|
9917
10278
|
// DEFAULT org while the MCP tools write to the pinned one.
|
|
9918
10279
|
orgId: process.env.MINDGRAPH_ORG_ID || stored.orgId,
|
|
9919
10280
|
maxRetries: 0,
|
|
9920
|
-
telemetrySurface: "mcp"
|
|
10281
|
+
telemetrySurface: "mcp",
|
|
10282
|
+
// Prompt recall must never hold the prompt: abort the request at
|
|
10283
|
+
// the same 4s deadline the core races it against.
|
|
10284
|
+
...input.hook_event_name === "UserPromptSubmit" ? { timeoutMs: 4e3 } : {}
|
|
9921
10285
|
});
|
|
9922
10286
|
const hookOptions = {
|
|
10287
|
+
promptRecall: stored.promptRecall,
|
|
10288
|
+
capture: stored.capture,
|
|
9923
10289
|
// No harness-name fallback here: hook-core derives a stable
|
|
9924
10290
|
// per-user default shared by both harnesses, which is what lets a
|
|
9925
10291
|
// task claimed under Codex rebind under Claude Code.
|
|
@@ -9934,9 +10300,11 @@ ${total} skill(s) synced.`);
|
|
|
9934
10300
|
client2,
|
|
9935
10301
|
hookOptions
|
|
9936
10302
|
);
|
|
9937
|
-
process.stdout.write(`${JSON.stringify(output)}
|
|
9938
|
-
`);
|
|
9939
10303
|
clearHookHealth(hookHarness);
|
|
10304
|
+
process.stdout.write(`${JSON.stringify(output)}
|
|
10305
|
+
`, () => {
|
|
10306
|
+
if (input.hook_event_name === "UserPromptSubmit") process.exit(0);
|
|
10307
|
+
});
|
|
9940
10308
|
} catch (error2) {
|
|
9941
10309
|
recordHookHealth(
|
|
9942
10310
|
hookHarness,
|
|
@@ -9949,6 +10317,35 @@ ${total} skill(s) synced.`);
|
|
|
9949
10317
|
}
|
|
9950
10318
|
break;
|
|
9951
10319
|
}
|
|
10320
|
+
case "hooks": {
|
|
10321
|
+
if (positionals[0] !== "preview") {
|
|
10322
|
+
console.error("Usage: mindgraph-mcp hooks preview [transcript_path] [--session-id <id>]");
|
|
10323
|
+
process.exit(1);
|
|
10324
|
+
}
|
|
10325
|
+
const transcript = positionals[1] || latestTranscript(process.cwd());
|
|
10326
|
+
if (!transcript || !fs7.existsSync(transcript)) {
|
|
10327
|
+
console.error(
|
|
10328
|
+
"No transcript found. Pass a Claude Code transcript path (~/.claude/projects/<dir>/<session>.jsonl)."
|
|
10329
|
+
);
|
|
10330
|
+
process.exit(1);
|
|
10331
|
+
}
|
|
10332
|
+
const sessionId = parseArgs(process.argv).sessionId || path7.basename(transcript, ".jsonl");
|
|
10333
|
+
const preview = previewCapture(transcript, sessionId, {
|
|
10334
|
+
agentId: process.env.MINDGRAPH_AGENT_ID || loadHookEnv().agentId
|
|
10335
|
+
});
|
|
10336
|
+
const enabled = loadHookEnv().capture === true;
|
|
10337
|
+
console.error(
|
|
10338
|
+
`Transcript: ${transcript}
|
|
10339
|
+
Session: ${sessionId}
|
|
10340
|
+
Capture is ${enabled ? "ON" : "OFF (nothing is sent; enable with install-hooks --capture)"}
|
|
10341
|
+
Would send ${preview.turns} turn(s) from byte ${preview.offset} as POST /ingest/session (nothing sent by this preview):`
|
|
10342
|
+
);
|
|
10343
|
+
process.stdout.write(
|
|
10344
|
+
`${JSON.stringify(preview.body ?? null, null, 2)}
|
|
10345
|
+
`
|
|
10346
|
+
);
|
|
10347
|
+
break;
|
|
10348
|
+
}
|
|
9952
10349
|
case "status":
|
|
9953
10350
|
printStatus();
|
|
9954
10351
|
break;
|