mindgraph-mcp 0.18.0 → 0.20.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 +27 -4
- package/dist/cli.js +1792 -290
- package/dist/index.js +1153 -189
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -65,8 +65,8 @@ var init_dist = __esm({
|
|
|
65
65
|
this.retryBackoffMs = config.retryBackoffMs ?? 1e3;
|
|
66
66
|
}
|
|
67
67
|
// ---- HTTP helpers ----
|
|
68
|
-
async request(method,
|
|
69
|
-
const url = `${this.baseUrl}${
|
|
68
|
+
async request(method, path8, body) {
|
|
69
|
+
const url = `${this.baseUrl}${path8}`;
|
|
70
70
|
const requestId = createRequestId();
|
|
71
71
|
const init = {
|
|
72
72
|
method,
|
|
@@ -83,43 +83,44 @@ var init_dist = __esm({
|
|
|
83
83
|
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
84
84
|
const res = await fetch(url, init);
|
|
85
85
|
if (!res.ok) {
|
|
86
|
-
const
|
|
86
|
+
const text22 = await res.text().catch(() => "");
|
|
87
87
|
let parsed;
|
|
88
88
|
try {
|
|
89
|
-
parsed = JSON.parse(
|
|
89
|
+
parsed = JSON.parse(text22);
|
|
90
90
|
} catch {
|
|
91
|
-
parsed =
|
|
91
|
+
parsed = text22;
|
|
92
92
|
}
|
|
93
93
|
const err5 = new MindGraphError(
|
|
94
|
-
`${method} ${
|
|
94
|
+
`${method} ${path8} failed: ${res.status}`,
|
|
95
95
|
res.status,
|
|
96
96
|
parsed
|
|
97
97
|
);
|
|
98
98
|
if (res.status === 503 && attempt < this.maxRetries) {
|
|
99
99
|
lastError = err5;
|
|
100
|
-
const
|
|
100
|
+
const retryAfterSecs = Number(res.headers.get("retry-after"));
|
|
101
|
+
const delay = Number.isFinite(retryAfterSecs) && retryAfterSecs > 0 ? Math.min(retryAfterSecs * 1e3, 1e4) : this.retryBackoffMs * 2 ** attempt;
|
|
101
102
|
await new Promise((r) => setTimeout(r, delay));
|
|
102
103
|
continue;
|
|
103
104
|
}
|
|
104
105
|
throw err5;
|
|
105
106
|
}
|
|
106
|
-
const
|
|
107
|
-
if (!
|
|
108
|
-
return JSON.parse(
|
|
107
|
+
const text2 = await res.text();
|
|
108
|
+
if (!text2) return void 0;
|
|
109
|
+
return JSON.parse(text2);
|
|
109
110
|
}
|
|
110
111
|
throw lastError;
|
|
111
112
|
}
|
|
112
|
-
get(
|
|
113
|
-
return this.request("GET",
|
|
113
|
+
get(path8) {
|
|
114
|
+
return this.request("GET", path8);
|
|
114
115
|
}
|
|
115
|
-
post(
|
|
116
|
-
return this.request("POST",
|
|
116
|
+
post(path8, body) {
|
|
117
|
+
return this.request("POST", path8, body);
|
|
117
118
|
}
|
|
118
|
-
patch(
|
|
119
|
-
return this.request("PATCH",
|
|
119
|
+
patch(path8, body) {
|
|
120
|
+
return this.request("PATCH", path8, body);
|
|
120
121
|
}
|
|
121
|
-
del(
|
|
122
|
-
return this.request("DELETE",
|
|
122
|
+
del(path8) {
|
|
123
|
+
return this.request("DELETE", path8);
|
|
123
124
|
}
|
|
124
125
|
// ---- Health ----
|
|
125
126
|
async health() {
|
|
@@ -148,6 +149,52 @@ var init_dist = __esm({
|
|
|
148
149
|
async entity(req) {
|
|
149
150
|
return this.post("/reality/entity", req);
|
|
150
151
|
}
|
|
152
|
+
async series(req) {
|
|
153
|
+
return this.post("/reality/series", req);
|
|
154
|
+
}
|
|
155
|
+
async createSeries(req) {
|
|
156
|
+
return this.post("/reality/series", { action: "create", ...req });
|
|
157
|
+
}
|
|
158
|
+
async appendSeries(req) {
|
|
159
|
+
return this.post("/reality/series", { action: "append", ...req });
|
|
160
|
+
}
|
|
161
|
+
async seriesWindow(req) {
|
|
162
|
+
return this.post("/reality/series", { action: "window", ...req });
|
|
163
|
+
}
|
|
164
|
+
async aggregateSeries(req) {
|
|
165
|
+
return this.post("/reality/series", { action: "aggregate", ...req });
|
|
166
|
+
}
|
|
167
|
+
async latestSeries(seriesUid, options) {
|
|
168
|
+
return this.post("/reality/series", {
|
|
169
|
+
action: "latest",
|
|
170
|
+
series_uid: seriesUid,
|
|
171
|
+
...options
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
async listSeriesForEntity(entityUid, options) {
|
|
175
|
+
return this.post("/reality/series", {
|
|
176
|
+
action: "list_for_entity",
|
|
177
|
+
entity_uid: entityUid,
|
|
178
|
+
...options
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
async batchLatestSeries(entityUids, options) {
|
|
182
|
+
return this.post("/reality/series", {
|
|
183
|
+
action: "batch_latest",
|
|
184
|
+
entity_uids: entityUids,
|
|
185
|
+
...options
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
async batchAggregateSeries(req) {
|
|
189
|
+
return this.post("/reality/series", { action: "batch_aggregate", ...req });
|
|
190
|
+
}
|
|
191
|
+
async deleteSeries(seriesUid, options) {
|
|
192
|
+
return this.post("/reality/series", {
|
|
193
|
+
action: "delete_series",
|
|
194
|
+
series_uid: seriesUid,
|
|
195
|
+
...options
|
|
196
|
+
});
|
|
197
|
+
}
|
|
151
198
|
/** Convenience: create or find an entity by label. */
|
|
152
199
|
async findOrCreateEntity(label, props, agentId) {
|
|
153
200
|
return this.post("/reality/entity", {
|
|
@@ -185,10 +232,10 @@ var init_dist = __esm({
|
|
|
185
232
|
* Resolve text to an existing entity via alias matching.
|
|
186
233
|
* Returns `{ uid: string | null }`.
|
|
187
234
|
*/
|
|
188
|
-
async resolveEntity(
|
|
235
|
+
async resolveEntity(text2, agentId) {
|
|
189
236
|
return this.post("/reality/entity", {
|
|
190
237
|
action: "resolve",
|
|
191
|
-
text,
|
|
238
|
+
text: text2,
|
|
192
239
|
agent_id: agentId
|
|
193
240
|
});
|
|
194
241
|
}
|
|
@@ -196,10 +243,10 @@ var init_dist = __esm({
|
|
|
196
243
|
* Fuzzy-match text against existing entities.
|
|
197
244
|
* Returns `{ matches: [{ uid, label, score }] }`.
|
|
198
245
|
*/
|
|
199
|
-
async fuzzyResolveEntity(
|
|
246
|
+
async fuzzyResolveEntity(text2, limit = 5, agentId) {
|
|
200
247
|
return this.post("/reality/entity", {
|
|
201
248
|
action: "fuzzy_resolve",
|
|
202
|
-
text,
|
|
249
|
+
text: text2,
|
|
203
250
|
limit,
|
|
204
251
|
agent_id: agentId
|
|
205
252
|
});
|
|
@@ -498,8 +545,8 @@ var init_dist = __esm({
|
|
|
498
545
|
async getAliases(uid) {
|
|
499
546
|
return this.get(`/aliases/${seg(uid)}`);
|
|
500
547
|
}
|
|
501
|
-
async resolveAlias(
|
|
502
|
-
return this.get(`/resolve?text=${encodeURIComponent(
|
|
548
|
+
async resolveAlias(text2) {
|
|
549
|
+
return this.get(`/resolve?text=${encodeURIComponent(text2)}`);
|
|
503
550
|
}
|
|
504
551
|
// ---- Export / Import ----
|
|
505
552
|
async exportGraph() {
|
|
@@ -717,6 +764,20 @@ var init_dist = __esm({
|
|
|
717
764
|
async getOntologySchema(id) {
|
|
718
765
|
return this.get(`/v1/ontology/schemas/${seg(id)}`);
|
|
719
766
|
}
|
|
767
|
+
async createOntologySeriesBinding(schemaId, request) {
|
|
768
|
+
return this.post(`/v1/ontology/schemas/${seg(schemaId)}/series-bindings`, request);
|
|
769
|
+
}
|
|
770
|
+
async syncOntologySeriesBinding(bindingId, mode) {
|
|
771
|
+
return this.post(
|
|
772
|
+
`/v1/ontology/series-bindings/${seg(bindingId)}/sync`,
|
|
773
|
+
mode ? { mode } : {}
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
async archiveOntologySeriesBinding(schemaId, bindingId) {
|
|
777
|
+
return this.del(
|
|
778
|
+
`/v1/ontology/schemas/${seg(schemaId)}/series-bindings/${seg(bindingId)}`
|
|
779
|
+
);
|
|
780
|
+
}
|
|
720
781
|
async createOntologySchema(req) {
|
|
721
782
|
return this.post("/v1/ontology/schemas", req);
|
|
722
783
|
}
|
|
@@ -988,14 +1049,14 @@ function unavailabilityCaveats(reason, message) {
|
|
|
988
1049
|
}
|
|
989
1050
|
return caveats;
|
|
990
1051
|
}
|
|
991
|
-
function normalizePath(
|
|
992
|
-
return
|
|
1052
|
+
function normalizePath(path8) {
|
|
1053
|
+
return path8.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
993
1054
|
}
|
|
994
1055
|
function normalizeSignature(signature) {
|
|
995
1056
|
return signature.replace(/\s+/g, " ").trim();
|
|
996
1057
|
}
|
|
997
|
-
function languageFromPath(
|
|
998
|
-
const extension = (0, import_node_path2.extname)(
|
|
1058
|
+
function languageFromPath(path8) {
|
|
1059
|
+
const extension = (0, import_node_path2.extname)(path8).toLowerCase();
|
|
999
1060
|
return {
|
|
1000
1061
|
".c": "c",
|
|
1001
1062
|
".cc": "cpp",
|
|
@@ -1102,29 +1163,29 @@ async function runProcess(executable, args, timeoutMs, env, cwd) {
|
|
|
1102
1163
|
child.stderr.on("data", collect(stderr));
|
|
1103
1164
|
child.on("error", (cause) => {
|
|
1104
1165
|
clearTimeout(timer);
|
|
1105
|
-
const
|
|
1106
|
-
|
|
1107
|
-
rejectPromise(
|
|
1166
|
+
const error2 = new Error(cause.message);
|
|
1167
|
+
error2.kind = cause.code === "ENOENT" ? "absent" : "command_failed";
|
|
1168
|
+
rejectPromise(error2);
|
|
1108
1169
|
});
|
|
1109
1170
|
child.on("close", (code) => {
|
|
1110
1171
|
clearTimeout(timer);
|
|
1111
1172
|
if (timedOut) {
|
|
1112
|
-
const
|
|
1113
|
-
|
|
1114
|
-
rejectPromise(
|
|
1173
|
+
const error2 = new Error(`command timed out after ${timeoutMs}ms`);
|
|
1174
|
+
error2.kind = "timeout";
|
|
1175
|
+
rejectPromise(error2);
|
|
1115
1176
|
return;
|
|
1116
1177
|
}
|
|
1117
1178
|
if (outputBytes > MAX_OUTPUT_BYTES) {
|
|
1118
|
-
const
|
|
1119
|
-
|
|
1120
|
-
rejectPromise(
|
|
1179
|
+
const error2 = new Error("codegraph output exceeded 5 MiB");
|
|
1180
|
+
error2.kind = "command_failed";
|
|
1181
|
+
rejectPromise(error2);
|
|
1121
1182
|
return;
|
|
1122
1183
|
}
|
|
1123
1184
|
if (code !== 0) {
|
|
1124
|
-
const
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
rejectPromise(
|
|
1185
|
+
const error2 = new Error(Buffer.concat(stderr).toString("utf8").trim());
|
|
1186
|
+
error2.kind = "command_failed";
|
|
1187
|
+
error2.exitCode = code;
|
|
1188
|
+
rejectPromise(error2);
|
|
1128
1189
|
return;
|
|
1129
1190
|
}
|
|
1130
1191
|
resolvePromise(Buffer.concat(stdout).toString("utf8"));
|
|
@@ -1152,13 +1213,11 @@ function codeRefIdentityKey(ref) {
|
|
|
1152
1213
|
qualified_name: ref.qualifiedName,
|
|
1153
1214
|
disambiguator: {
|
|
1154
1215
|
signature: ref.signature ?? "",
|
|
1155
|
-
path: ref.path
|
|
1156
|
-
start_line: ref.startLine,
|
|
1157
|
-
end_line: ref.endLine
|
|
1216
|
+
path: ref.path
|
|
1158
1217
|
}
|
|
1159
1218
|
};
|
|
1160
1219
|
}
|
|
1161
|
-
var import_node_child_process, import_node_fs2, import_promises, import_node_os2, import_node_path2,
|
|
1220
|
+
var import_node_child_process, import_node_fs2, import_promises, import_node_os2, import_node_path2, import_node_crypto3, UnknownRepositoryError, DEFAULT_TIMEOUT_MS, DEFAULT_STATUS_TTL_MS, MAX_OUTPUT_BYTES, CodegraphAdapter;
|
|
1162
1221
|
var init_codegraph = __esm({
|
|
1163
1222
|
"src/codegraph.ts"() {
|
|
1164
1223
|
"use strict";
|
|
@@ -1167,7 +1226,7 @@ var init_codegraph = __esm({
|
|
|
1167
1226
|
import_promises = require("fs/promises");
|
|
1168
1227
|
import_node_os2 = require("os");
|
|
1169
1228
|
import_node_path2 = require("path");
|
|
1170
|
-
|
|
1229
|
+
import_node_crypto3 = require("crypto");
|
|
1171
1230
|
UnknownRepositoryError = class extends Error {
|
|
1172
1231
|
constructor(repo) {
|
|
1173
1232
|
super(
|
|
@@ -1296,7 +1355,7 @@ var init_codegraph = __esm({
|
|
|
1296
1355
|
} catch {
|
|
1297
1356
|
}
|
|
1298
1357
|
(0, import_node_fs2.mkdirSync)(identityDir, { recursive: true, mode: 448 });
|
|
1299
|
-
const generated = (0,
|
|
1358
|
+
const generated = (0, import_node_crypto3.randomUUID)();
|
|
1300
1359
|
try {
|
|
1301
1360
|
(0, import_node_fs2.writeFileSync)(identityFile, `${generated}
|
|
1302
1361
|
`, {
|
|
@@ -2043,7 +2102,7 @@ async function anchorCodeRef(client2, ref, repositoryUid, spaceUid, agentId) {
|
|
|
2043
2102
|
identity_space_uid: spaceUid,
|
|
2044
2103
|
agent_id: agentId
|
|
2045
2104
|
});
|
|
2046
|
-
if (response.uid) {
|
|
2105
|
+
if (response.uid && response.created !== false && response.status !== "existing") {
|
|
2047
2106
|
await client2.entity({
|
|
2048
2107
|
action: "relate",
|
|
2049
2108
|
source_uid: response.uid,
|
|
@@ -2308,7 +2367,7 @@ function windowsCommand(harness) {
|
|
|
2308
2367
|
function installHookRunner(sourceFile, homeDir, version) {
|
|
2309
2368
|
const target = import_node_path4.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_RELATIVE);
|
|
2310
2369
|
import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(target), { recursive: true });
|
|
2311
|
-
const temporary = `${target}.${process.pid}.${
|
|
2370
|
+
const temporary = `${target}.${process.pid}.${import_node_crypto5.default.randomUUID()}.tmp`;
|
|
2312
2371
|
import_node_fs4.default.copyFileSync(sourceFile, temporary);
|
|
2313
2372
|
import_node_fs4.default.renameSync(temporary, target);
|
|
2314
2373
|
const versionPath = import_node_path4.default.join(homeDir || import_node_os4.default.homedir(), RUNNER_VERSION_RELATIVE);
|
|
@@ -2478,7 +2537,7 @@ function entryHasOwnedHook(value) {
|
|
|
2478
2537
|
}
|
|
2479
2538
|
function writeSettings(file, settings) {
|
|
2480
2539
|
import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(file), { recursive: true });
|
|
2481
|
-
const temporary = `${file}.${process.pid}.${
|
|
2540
|
+
const temporary = `${file}.${process.pid}.${import_node_crypto5.default.randomUUID()}.tmp`;
|
|
2482
2541
|
import_node_fs4.default.writeFileSync(temporary, `${JSON.stringify(settings, null, 2)}
|
|
2483
2542
|
`, {
|
|
2484
2543
|
mode: 384
|
|
@@ -2554,14 +2613,14 @@ function installCodexHooks(scope, projectDir = process.cwd(), codexHomeDir) {
|
|
|
2554
2613
|
function uninstallCodexHooks(scope, projectDir = process.cwd(), codexHomeDir) {
|
|
2555
2614
|
return uninstallHooks("codex", scope, projectDir, codexHomeDir);
|
|
2556
2615
|
}
|
|
2557
|
-
var import_node_fs4, import_node_os4, import_node_path4,
|
|
2616
|
+
var import_node_fs4, import_node_os4, import_node_path4, import_node_crypto5, OWNER_MARKER, RUNNER_RELATIVE, RUNNER_VERSION_RELATIVE;
|
|
2558
2617
|
var init_hook_installer = __esm({
|
|
2559
2618
|
"src/hook-installer.ts"() {
|
|
2560
2619
|
"use strict";
|
|
2561
2620
|
import_node_fs4 = __toESM(require("fs"));
|
|
2562
2621
|
import_node_os4 = __toESM(require("os"));
|
|
2563
2622
|
import_node_path4 = __toESM(require("path"));
|
|
2564
|
-
|
|
2623
|
+
import_node_crypto5 = __toESM(require("crypto"));
|
|
2565
2624
|
OWNER_MARKER = "--owner mindgraph";
|
|
2566
2625
|
RUNNER_RELATIVE = ".mindgraph/bin/mindgraph-hook.cjs";
|
|
2567
2626
|
RUNNER_VERSION_RELATIVE = `${RUNNER_RELATIVE}.version`;
|
|
@@ -2603,7 +2662,7 @@ function fileDescriptor(root, absolute) {
|
|
|
2603
2662
|
const content = import_node_fs5.default.readFileSync(absolute, "utf8");
|
|
2604
2663
|
return {
|
|
2605
2664
|
logical_path: import_node_path5.default.relative(root, absolute).split(import_node_path5.default.sep).join("/"),
|
|
2606
|
-
content_hash:
|
|
2665
|
+
content_hash: import_node_crypto6.default.createHash("sha256").update(content).digest("hex"),
|
|
2607
2666
|
bytes: Buffer.byteLength(content),
|
|
2608
2667
|
content
|
|
2609
2668
|
};
|
|
@@ -2678,8 +2737,8 @@ async function handleSyncTool(client2, args) {
|
|
|
2678
2737
|
}
|
|
2679
2738
|
}
|
|
2680
2739
|
return ok2(await remote.memorySync(request));
|
|
2681
|
-
} catch (
|
|
2682
|
-
return err2("sync_failed", errorDetail(
|
|
2740
|
+
} catch (error2) {
|
|
2741
|
+
return err2("sync_failed", errorDetail(error2));
|
|
2683
2742
|
}
|
|
2684
2743
|
}
|
|
2685
2744
|
async function scanRepository(remote, repository, requested, provider, agentId) {
|
|
@@ -2716,11 +2775,11 @@ async function scanRepository(remote, repository, requested, provider, agentId)
|
|
|
2716
2775
|
files: results
|
|
2717
2776
|
};
|
|
2718
2777
|
}
|
|
2719
|
-
var
|
|
2778
|
+
var import_node_crypto6, import_node_fs5, import_node_path5, ok2, err2, SYNC_TOOL;
|
|
2720
2779
|
var init_sync_tool = __esm({
|
|
2721
2780
|
"src/sync-tool.ts"() {
|
|
2722
2781
|
"use strict";
|
|
2723
|
-
|
|
2782
|
+
import_node_crypto6 = __toESM(require("crypto"));
|
|
2724
2783
|
import_node_fs5 = __toESM(require("fs"));
|
|
2725
2784
|
import_node_path5 = __toESM(require("path"));
|
|
2726
2785
|
init_codegraph();
|
|
@@ -2813,12 +2872,22 @@ function str(value) {
|
|
|
2813
2872
|
function num(value) {
|
|
2814
2873
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2815
2874
|
}
|
|
2875
|
+
function edgeTypeName(value) {
|
|
2876
|
+
return str(value) ?? str(obj(value)?.Custom);
|
|
2877
|
+
}
|
|
2878
|
+
function renderBudget(requested) {
|
|
2879
|
+
if (requested === void 0 || !Number.isFinite(requested)) return RENDER_CHAR_BUDGET;
|
|
2880
|
+
return Math.min(
|
|
2881
|
+
RENDER_CHAR_BUDGET,
|
|
2882
|
+
Math.max(MIN_RENDER_CHAR_BUDGET, Math.floor(requested))
|
|
2883
|
+
);
|
|
2884
|
+
}
|
|
2816
2885
|
function clip(value, max, state) {
|
|
2817
|
-
const
|
|
2818
|
-
if (!
|
|
2819
|
-
if (
|
|
2886
|
+
const text2 = str(value);
|
|
2887
|
+
if (!text2) return void 0;
|
|
2888
|
+
if (text2.length <= max) return text2;
|
|
2820
2889
|
state.bounded = true;
|
|
2821
|
-
return `${
|
|
2890
|
+
return `${text2.slice(0, max - 1)}\u2026`;
|
|
2822
2891
|
}
|
|
2823
2892
|
function isNodeShaped(value) {
|
|
2824
2893
|
const node = obj(value);
|
|
@@ -2940,7 +3009,7 @@ function relationshipItems(edges, labelByUid, state, options) {
|
|
|
2940
3009
|
for (const raw of edges) {
|
|
2941
3010
|
const edge = obj(raw);
|
|
2942
3011
|
if (!edge) continue;
|
|
2943
|
-
const type =
|
|
3012
|
+
const type = edgeTypeName(edge.edge_type) || "?";
|
|
2944
3013
|
const from = str(edge.from_uid);
|
|
2945
3014
|
const to = str(edge.to_uid);
|
|
2946
3015
|
if (CURATION_EDGE_TYPES.has(type)) continue;
|
|
@@ -2953,25 +3022,29 @@ function relationshipItems(edges, labelByUid, state, options) {
|
|
|
2953
3022
|
if (lines.length > RELATIONSHIP_MAX) {
|
|
2954
3023
|
state.bounded = true;
|
|
2955
3024
|
const extra = lines.length - RELATIONSHIP_MAX;
|
|
3025
|
+
state.omitted += extra;
|
|
2956
3026
|
return [...lines.slice(0, RELATIONSHIP_MAX), `- (+${extra} more relationships)`];
|
|
2957
3027
|
}
|
|
2958
3028
|
return lines;
|
|
2959
3029
|
}
|
|
2960
|
-
function assemble(header, sections, state) {
|
|
3030
|
+
function assemble(header, sections, state, maxChars) {
|
|
3031
|
+
const budget = renderBudget(maxChars);
|
|
3032
|
+
const packingBudget = Math.max(header.length, budget - BOUNDED_MARKER_RESERVE);
|
|
2961
3033
|
const lines = [header];
|
|
2962
3034
|
let used = header.length;
|
|
2963
3035
|
for (const section of sections) {
|
|
2964
3036
|
if (section.items.length === 0 && !section.heading) continue;
|
|
2965
3037
|
const headingCost = section.heading.length + 2;
|
|
2966
|
-
if (used + headingCost >
|
|
3038
|
+
if (used + headingCost > packingBudget) {
|
|
2967
3039
|
state.bounded = true;
|
|
3040
|
+
state.omitted += Math.max(1, section.items.length);
|
|
2968
3041
|
continue;
|
|
2969
3042
|
}
|
|
2970
3043
|
let sectionUsed = 0;
|
|
2971
3044
|
const kept = [];
|
|
2972
3045
|
let dropped = 0;
|
|
2973
3046
|
for (const item of section.items) {
|
|
2974
|
-
if (used + headingCost + sectionUsed + item.length + 1 >
|
|
3047
|
+
if (used + headingCost + sectionUsed + item.length + 1 > packingBudget) {
|
|
2975
3048
|
dropped += 1;
|
|
2976
3049
|
continue;
|
|
2977
3050
|
}
|
|
@@ -2980,13 +3053,17 @@ function assemble(header, sections, state) {
|
|
|
2980
3053
|
}
|
|
2981
3054
|
if (kept.length === 0 && section.items.length > 0) {
|
|
2982
3055
|
state.bounded = true;
|
|
3056
|
+
state.omitted += dropped;
|
|
2983
3057
|
continue;
|
|
2984
3058
|
}
|
|
2985
3059
|
if (dropped > 0) {
|
|
2986
3060
|
state.bounded = true;
|
|
3061
|
+
state.omitted += dropped;
|
|
2987
3062
|
const marker = `(+${dropped} more not shown)`;
|
|
2988
|
-
|
|
2989
|
-
|
|
3063
|
+
if (used + headingCost + sectionUsed + marker.length + 1 <= packingBudget) {
|
|
3064
|
+
kept.push(marker);
|
|
3065
|
+
sectionUsed += marker.length + 1;
|
|
3066
|
+
}
|
|
2990
3067
|
}
|
|
2991
3068
|
lines.push(
|
|
2992
3069
|
section.items.length === 0 ? section.heading : section.heading ? `${section.heading}
|
|
@@ -2996,10 +3073,10 @@ ${kept.join("\n")}` : kept.join("\n")
|
|
|
2996
3073
|
}
|
|
2997
3074
|
if (state.bounded) {
|
|
2998
3075
|
lines.push(
|
|
2999
|
-
|
|
3076
|
+
`\u2026[rendered context bounded to ${budget} chars; ${state.omitted} item(s) omitted \u2014 fetch full node content by uid, narrow the query, raise max_output_chars, or pass format: "json" for the raw response]`
|
|
3000
3077
|
);
|
|
3001
3078
|
}
|
|
3002
|
-
return lines.join("\n\n");
|
|
3079
|
+
return lines.join("\n\n").slice(0, budget);
|
|
3003
3080
|
}
|
|
3004
3081
|
function policySections(payload, state) {
|
|
3005
3082
|
const policies = Array.isArray(payload.applicable_policies) ? payload.applicable_policies : [];
|
|
@@ -3020,7 +3097,7 @@ function hiddenSections(payload) {
|
|
|
3020
3097
|
}
|
|
3021
3098
|
];
|
|
3022
3099
|
}
|
|
3023
|
-
function renderContext(response) {
|
|
3100
|
+
function renderContext(response, maxChars) {
|
|
3024
3101
|
const payload = obj(response);
|
|
3025
3102
|
if (!payload) return void 0;
|
|
3026
3103
|
const graph = obj(payload.graph);
|
|
@@ -3030,7 +3107,7 @@ function renderContext(response) {
|
|
|
3030
3107
|
const infraOnly = shaped.length - nodes.length;
|
|
3031
3108
|
const articles = Array.isArray(payload.articles) ? payload.articles : [];
|
|
3032
3109
|
const chunks = Array.isArray(payload.chunks) ? payload.chunks : [];
|
|
3033
|
-
const state = { bounded: false };
|
|
3110
|
+
const state = { bounded: false, omitted: 0 };
|
|
3034
3111
|
const sections = [];
|
|
3035
3112
|
if (articles.length > 0) {
|
|
3036
3113
|
sections.push({
|
|
@@ -3073,11 +3150,129 @@ ${clip(article.content, ARTICLE_MAX, state) || ""}`
|
|
|
3073
3150
|
return "No results found for this search.";
|
|
3074
3151
|
}
|
|
3075
3152
|
if (nodes.length === 0 && articles.length === 0 && chunks.length === 0) {
|
|
3076
|
-
return assemble(
|
|
3153
|
+
return assemble(
|
|
3154
|
+
"# Retrieved context\n\nNo graph results matched this search.",
|
|
3155
|
+
sections,
|
|
3156
|
+
state,
|
|
3157
|
+
maxChars
|
|
3158
|
+
);
|
|
3159
|
+
}
|
|
3160
|
+
return assemble("# Retrieved context", sections, state, maxChars);
|
|
3161
|
+
}
|
|
3162
|
+
function resumeEntry(raw, state, fallbackType) {
|
|
3163
|
+
const wrapper = obj(raw);
|
|
3164
|
+
if (!wrapper) return void 0;
|
|
3165
|
+
const value = obj(wrapper.node) ?? wrapper;
|
|
3166
|
+
if (isNodeShaped(value)) return nodeLines(value, state);
|
|
3167
|
+
const uid = str(value.uid);
|
|
3168
|
+
const props = obj(value.props);
|
|
3169
|
+
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;
|
|
3170
|
+
const status = str(value.status) ?? str(props?.status);
|
|
3171
|
+
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);
|
|
3172
|
+
const details = [
|
|
3173
|
+
status ? `status ${status}` : void 0,
|
|
3174
|
+
str(value.path) ? `path ${str(value.path)}` : void 0,
|
|
3175
|
+
str(value.symbol) ? `symbol ${str(value.symbol)}` : void 0
|
|
3176
|
+
].filter(Boolean);
|
|
3177
|
+
const head = `- **${label}**${uid ? ` [${uid}]` : ""}${details.length > 0 ? ` (${details.join(", ")})` : ""}`;
|
|
3178
|
+
return summary && summary !== label ? `${head}: ${summary}` : head;
|
|
3179
|
+
}
|
|
3180
|
+
function resumeSection(heading, values, state, fallbackType) {
|
|
3181
|
+
const items = values.map((value) => resumeEntry(value, state, fallbackType)).filter((value) => Boolean(value));
|
|
3182
|
+
return items.length > 0 ? { heading, items } : void 0;
|
|
3183
|
+
}
|
|
3184
|
+
function renderResumeContext(response, maxChars) {
|
|
3185
|
+
const payload = obj(response);
|
|
3186
|
+
if (!payload) return void 0;
|
|
3187
|
+
const state = { bounded: false, omitted: 0 };
|
|
3188
|
+
const status = str(payload.status);
|
|
3189
|
+
if (!payload.task && status === "no_eligible_work") {
|
|
3190
|
+
const scopedOut = Array.isArray(payload.scoped_out_tasks) ? payload.scoped_out_tasks.length : 0;
|
|
3191
|
+
const note = scopedOut > 0 ? ` ${scopedOut} task(s) exist outside the requested scope.` : "";
|
|
3192
|
+
return assemble(
|
|
3193
|
+
"# Resume context",
|
|
3194
|
+
[{ heading: `No eligible work was found in the requested scope.${note}`, items: [] }],
|
|
3195
|
+
state,
|
|
3196
|
+
maxChars
|
|
3197
|
+
);
|
|
3077
3198
|
}
|
|
3078
|
-
|
|
3199
|
+
const sections = [];
|
|
3200
|
+
const push = (section) => {
|
|
3201
|
+
if (section) sections.push(section);
|
|
3202
|
+
};
|
|
3203
|
+
push(resumeSection("## Current task", [payload.task], state, "Task"));
|
|
3204
|
+
push(
|
|
3205
|
+
resumeSection(
|
|
3206
|
+
"## Goal and project",
|
|
3207
|
+
[payload.goal, payload.project].filter(Boolean),
|
|
3208
|
+
state,
|
|
3209
|
+
"Scope"
|
|
3210
|
+
)
|
|
3211
|
+
);
|
|
3212
|
+
push(resumeSection("## Plan", [payload.plan], state, "Plan"));
|
|
3213
|
+
push(
|
|
3214
|
+
resumeSection(
|
|
3215
|
+
"## Next steps",
|
|
3216
|
+
Array.isArray(payload.next_steps) ? payload.next_steps : [],
|
|
3217
|
+
state,
|
|
3218
|
+
"Next step"
|
|
3219
|
+
)
|
|
3220
|
+
);
|
|
3221
|
+
push(
|
|
3222
|
+
resumeSection(
|
|
3223
|
+
"## Blockers",
|
|
3224
|
+
Array.isArray(payload.blockers) ? payload.blockers : [],
|
|
3225
|
+
state,
|
|
3226
|
+
"Blocker"
|
|
3227
|
+
)
|
|
3228
|
+
);
|
|
3229
|
+
const knowledge = obj(payload.knowledge);
|
|
3230
|
+
push(
|
|
3231
|
+
resumeSection(
|
|
3232
|
+
"## Relevant lessons and risks",
|
|
3233
|
+
[
|
|
3234
|
+
...Array.isArray(knowledge?.lessons) ? knowledge.lessons : [],
|
|
3235
|
+
...Array.isArray(knowledge?.risks) ? knowledge.risks : []
|
|
3236
|
+
],
|
|
3237
|
+
state,
|
|
3238
|
+
"Knowledge"
|
|
3239
|
+
)
|
|
3240
|
+
);
|
|
3241
|
+
push(
|
|
3242
|
+
resumeSection(
|
|
3243
|
+
"## Active execution",
|
|
3244
|
+
[payload.active_execution].filter(Boolean),
|
|
3245
|
+
state,
|
|
3246
|
+
"Execution"
|
|
3247
|
+
)
|
|
3248
|
+
);
|
|
3249
|
+
push(
|
|
3250
|
+
resumeSection(
|
|
3251
|
+
"## Recent executions",
|
|
3252
|
+
Array.isArray(payload.recent_executions) ? payload.recent_executions : [],
|
|
3253
|
+
state,
|
|
3254
|
+
"Execution"
|
|
3255
|
+
)
|
|
3256
|
+
);
|
|
3257
|
+
push(
|
|
3258
|
+
resumeSection(
|
|
3259
|
+
"## Code targets",
|
|
3260
|
+
Array.isArray(payload.code_targets) ? payload.code_targets : [],
|
|
3261
|
+
state,
|
|
3262
|
+
"Code target"
|
|
3263
|
+
)
|
|
3264
|
+
);
|
|
3265
|
+
push(resumeSection("## Lease", [payload.lease].filter(Boolean), state, "Lease"));
|
|
3266
|
+
const warning = clip(payload.warning, SUMMARY_MAX, state);
|
|
3267
|
+
if (warning) sections.push({ heading: "## Warning", items: [`- ${warning}`] });
|
|
3268
|
+
const selectionReason = clip(payload.selection_reason, SUMMARY_MAX, state);
|
|
3269
|
+
if (selectionReason) {
|
|
3270
|
+
sections.push({ heading: "## Selection", items: [`- ${selectionReason}`] });
|
|
3271
|
+
}
|
|
3272
|
+
if (sections.length === 0) return void 0;
|
|
3273
|
+
return assemble("# Resume context", sections, state, maxChars);
|
|
3079
3274
|
}
|
|
3080
|
-
function renderNodeList(response, title, limit) {
|
|
3275
|
+
function renderNodeList(response, title, limit, maxChars) {
|
|
3081
3276
|
let candidates = [];
|
|
3082
3277
|
if (Array.isArray(response)) {
|
|
3083
3278
|
candidates = response;
|
|
@@ -3095,8 +3290,9 @@ function renderNodeList(response, title, limit) {
|
|
|
3095
3290
|
let nodes = candidates.map(unwrapSearchResult).filter((node) => Boolean(node));
|
|
3096
3291
|
if (nodes.length === 0) return void 0;
|
|
3097
3292
|
const total = nodes.length;
|
|
3098
|
-
const state = { bounded: false };
|
|
3293
|
+
const state = { bounded: false, omitted: 0 };
|
|
3099
3294
|
if (limit !== void 0 && limit > 0 && nodes.length > limit) {
|
|
3295
|
+
state.omitted += nodes.length - limit;
|
|
3100
3296
|
nodes = nodes.slice(0, limit);
|
|
3101
3297
|
state.bounded = true;
|
|
3102
3298
|
}
|
|
@@ -3105,31 +3301,36 @@ function renderNodeList(response, title, limit) {
|
|
|
3105
3301
|
return assemble(
|
|
3106
3302
|
`# ${title} (${shown === total ? total : `${shown} of ${total}`})`,
|
|
3107
3303
|
sections,
|
|
3108
|
-
state
|
|
3304
|
+
state,
|
|
3305
|
+
maxChars
|
|
3109
3306
|
);
|
|
3110
3307
|
}
|
|
3111
|
-
function renderTraversal(response) {
|
|
3308
|
+
function renderTraversal(response, maxChars) {
|
|
3309
|
+
const directSteps = Array.isArray(response) ? response : void 0;
|
|
3112
3310
|
const payload = obj(response);
|
|
3113
|
-
if (!payload) return void 0;
|
|
3114
|
-
const
|
|
3311
|
+
if (!payload && !directSteps) return void 0;
|
|
3312
|
+
const body = payload ?? {};
|
|
3313
|
+
const container = obj(body.graph) ?? body;
|
|
3115
3314
|
const rawNodes = Array.isArray(container.nodes) ? container.nodes : [];
|
|
3116
3315
|
const nodes = rawNodes.filter(isNodeShaped);
|
|
3117
|
-
const paths = Array.isArray(
|
|
3118
|
-
|
|
3119
|
-
const
|
|
3316
|
+
const paths = Array.isArray(body.paths) ? body.paths : [];
|
|
3317
|
+
const hasSteps = directSteps !== void 0 || Object.hasOwn(body, "steps");
|
|
3318
|
+
const rawSteps = directSteps ?? (Array.isArray(body.steps) ? body.steps : []);
|
|
3319
|
+
if (nodes.length === 0 && paths.length === 0 && !hasSteps) return void 0;
|
|
3320
|
+
const state = { bounded: false, omitted: 0 };
|
|
3120
3321
|
const sections = [];
|
|
3121
3322
|
const labelByUid = /* @__PURE__ */ new Map();
|
|
3122
3323
|
for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
3123
3324
|
if (paths.length > 0) {
|
|
3124
|
-
const items = paths.map((raw) => obj(raw)).filter((
|
|
3125
|
-
const uids = Array.isArray(
|
|
3126
|
-
const labels = Array.isArray(
|
|
3325
|
+
const items = paths.map((raw) => obj(raw)).filter((path8) => Boolean(path8)).map((path8, index) => {
|
|
3326
|
+
const uids = Array.isArray(path8.node_uids) ? path8.node_uids.map(String) : [];
|
|
3327
|
+
const labels = Array.isArray(path8.labels) ? path8.labels.map(String) : Array.isArray(path8.nodes) ? path8.nodes.map((hop) => {
|
|
3127
3328
|
const hopNode = obj(hop);
|
|
3128
3329
|
return str(hopNode?.label) || labelByUid.get(str(hopNode?.uid) || "") || str(hop) || "?";
|
|
3129
3330
|
}) : [];
|
|
3130
3331
|
const route = labels.length > 0 ? labels.join(" \u2192 ") : uids.join(" \u2192 ") || "(path)";
|
|
3131
|
-
const cost = num(
|
|
3132
|
-
const confidence = num(
|
|
3332
|
+
const cost = num(path8.cost) ?? num(path8.path_cost);
|
|
3333
|
+
const confidence = num(path8.path_confidence);
|
|
3133
3334
|
const meta = [
|
|
3134
3335
|
cost !== void 0 ? `cost ${cost}` : void 0,
|
|
3135
3336
|
confidence !== void 0 ? `confidence ${confidence}` : void 0
|
|
@@ -3140,6 +3341,42 @@ function renderTraversal(response) {
|
|
|
3140
3341
|
});
|
|
3141
3342
|
sections.push({ heading: "## Paths", items });
|
|
3142
3343
|
}
|
|
3344
|
+
if (hasSteps) {
|
|
3345
|
+
const stepItems = rawSteps.map((raw) => obj(raw)).filter((step) => Boolean(step)).map((step) => {
|
|
3346
|
+
const depth = num(step.depth);
|
|
3347
|
+
const uid = str(step.node_uid) ?? str(step.uid) ?? str(step.edge_to_uid) ?? "?";
|
|
3348
|
+
const parent = str(step.parent_uid) ?? str(step.edge_from_uid);
|
|
3349
|
+
const label = str(step.label) || "(unlabeled)";
|
|
3350
|
+
const relation = edgeTypeName(step.edge_type);
|
|
3351
|
+
const role = str(step.traversal_role);
|
|
3352
|
+
const type = str(step.node_type) ?? str(obj(step.node_type)?.Custom);
|
|
3353
|
+
const cost = num(step.path_cost);
|
|
3354
|
+
const confidence = num(step.path_confidence);
|
|
3355
|
+
const prefix = [
|
|
3356
|
+
depth !== void 0 ? `depth ${depth}` : void 0,
|
|
3357
|
+
role,
|
|
3358
|
+
relation ? `\u2014${relation}\u2192` : void 0
|
|
3359
|
+
].filter(Boolean).join(" ");
|
|
3360
|
+
const meta = [
|
|
3361
|
+
type,
|
|
3362
|
+
parent ? `parent ${parent}` : void 0,
|
|
3363
|
+
cost !== void 0 ? `cost ${cost}` : void 0,
|
|
3364
|
+
confidence !== void 0 ? `confidence ${confidence}` : void 0
|
|
3365
|
+
].filter(Boolean).join(", ");
|
|
3366
|
+
return `- ${prefix ? `${prefix}: ` : ""}**${label}** [${uid}]${meta ? ` (${meta})` : ""}`;
|
|
3367
|
+
});
|
|
3368
|
+
if (stepItems.length > 0) {
|
|
3369
|
+
sections.push({ heading: "## Traversal steps", items: stepItems });
|
|
3370
|
+
} else {
|
|
3371
|
+
const start = str(body.start_uid) || "?";
|
|
3372
|
+
const end = str(body.end_uid);
|
|
3373
|
+
const mode2 = str(body.mode);
|
|
3374
|
+
sections.push({
|
|
3375
|
+
heading: mode2 === "path" && end ? `No path found from [${start}] to [${end}].` : `No traversal steps returned from [${start}].`,
|
|
3376
|
+
items: []
|
|
3377
|
+
});
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3143
3380
|
if (nodes.length > 0) {
|
|
3144
3381
|
sections.push(...groupedNodeSections(nodes, state, "## Nodes\n\n"));
|
|
3145
3382
|
const relationships = relationshipItems(
|
|
@@ -3151,7 +3388,13 @@ function renderTraversal(response) {
|
|
|
3151
3388
|
sections.push({ heading: "## Relationships", items: relationships });
|
|
3152
3389
|
}
|
|
3153
3390
|
}
|
|
3154
|
-
|
|
3391
|
+
const mode = str(body.mode);
|
|
3392
|
+
return assemble(
|
|
3393
|
+
`# Graph traversal${mode ? ` (${mode})` : ""}`,
|
|
3394
|
+
sections,
|
|
3395
|
+
state,
|
|
3396
|
+
maxChars
|
|
3397
|
+
);
|
|
3155
3398
|
}
|
|
3156
3399
|
function neighborLine(value) {
|
|
3157
3400
|
const node = obj(value);
|
|
@@ -3172,7 +3415,7 @@ function renderOntologyAnswer(response) {
|
|
|
3172
3415
|
if (objects.length === 0 && cognitiveEmpty && provenance.length === 0) {
|
|
3173
3416
|
return hasQuerySignature ? "No matching domain objects." : void 0;
|
|
3174
3417
|
}
|
|
3175
|
-
const state = { bounded: false };
|
|
3418
|
+
const state = { bounded: false, omitted: 0 };
|
|
3176
3419
|
const sections = [];
|
|
3177
3420
|
if (objects.length > 0) {
|
|
3178
3421
|
sections.push(...groupedNodeSections(objects, state));
|
|
@@ -3238,7 +3481,7 @@ function renderObjectList(response, title) {
|
|
|
3238
3481
|
if (items.length === 0) {
|
|
3239
3482
|
return rawItems ? `# ${title} (0 results)` : void 0;
|
|
3240
3483
|
}
|
|
3241
|
-
const state = { bounded: false };
|
|
3484
|
+
const state = { bounded: false, omitted: 0 };
|
|
3242
3485
|
const sections = groupedNodeSections(items, state);
|
|
3243
3486
|
const total = num(payload.total_count);
|
|
3244
3487
|
const truncations = Array.isArray(payload.truncation_reasons) ? payload.truncation_reasons.filter((reason) => typeof reason === "string") : [];
|
|
@@ -3253,7 +3496,7 @@ function renderJobs(response) {
|
|
|
3253
3496
|
const jobs = Array.isArray(response) ? response : obj(response)?.jobs;
|
|
3254
3497
|
if (!Array.isArray(jobs)) return void 0;
|
|
3255
3498
|
if (jobs.length === 0) return "No ingestion jobs.";
|
|
3256
|
-
const state = { bounded: false };
|
|
3499
|
+
const state = { bounded: false, omitted: 0 };
|
|
3257
3500
|
const sorted = jobs.map((raw) => obj(raw)).filter((job) => Boolean(job)).sort((a, b) => (num(b.created_at) ?? 0) - (num(a.created_at) ?? 0));
|
|
3258
3501
|
const shown = sorted.slice(0, JOBS_RENDER_MAX);
|
|
3259
3502
|
if (shown.length < sorted.length) state.bounded = true;
|
|
@@ -3279,7 +3522,7 @@ function renderJobs(response) {
|
|
|
3279
3522
|
function renderSignals(response) {
|
|
3280
3523
|
const payload = obj(response);
|
|
3281
3524
|
if (!payload) return void 0;
|
|
3282
|
-
const state = { bounded: false };
|
|
3525
|
+
const state = { bounded: false, omitted: 0 };
|
|
3283
3526
|
const sections = [];
|
|
3284
3527
|
for (const [key, value] of Object.entries(payload)) {
|
|
3285
3528
|
if (!Array.isArray(value) || value.length === 0) continue;
|
|
@@ -3303,7 +3546,7 @@ function renderSignals(response) {
|
|
|
3303
3546
|
if (sections.length === 0) return void 0;
|
|
3304
3547
|
return assemble("# Synthesis signals", sections, state);
|
|
3305
3548
|
}
|
|
3306
|
-
var LABEL_MAX, SUMMARY_MAX, QUOTE_MAX, ARTICLE_MAX, CHUNK_MAX, RELATIONSHIP_MAX, RENDER_CHAR_BUDGET, CURATION_EDGE_TYPES, INFRA_NODE_TYPES, JOBS_RENDER_MAX;
|
|
3549
|
+
var LABEL_MAX, SUMMARY_MAX, QUOTE_MAX, ARTICLE_MAX, CHUNK_MAX, RELATIONSHIP_MAX, RENDER_CHAR_BUDGET, MIN_RENDER_CHAR_BUDGET, BOUNDED_MARKER_RESERVE, CURATION_EDGE_TYPES, INFRA_NODE_TYPES, JOBS_RENDER_MAX;
|
|
3307
3550
|
var init_render = __esm({
|
|
3308
3551
|
"src/render.ts"() {
|
|
3309
3552
|
"use strict";
|
|
@@ -3314,13 +3557,570 @@ var init_render = __esm({
|
|
|
3314
3557
|
CHUNK_MAX = 6e3;
|
|
3315
3558
|
RELATIONSHIP_MAX = 40;
|
|
3316
3559
|
RENDER_CHAR_BUDGET = 24e3;
|
|
3560
|
+
MIN_RENDER_CHAR_BUDGET = 512;
|
|
3561
|
+
BOUNDED_MARKER_RESERVE = 220;
|
|
3317
3562
|
CURATION_EDGE_TYPES = /* @__PURE__ */ new Set(["POSSIBLE_DUPLICATE", "PossibleDuplicate"]);
|
|
3318
3563
|
INFRA_NODE_TYPES = /* @__PURE__ */ new Set(["Chunk", "Document", "Article"]);
|
|
3319
3564
|
JOBS_RENDER_MAX = 20;
|
|
3320
3565
|
}
|
|
3321
3566
|
});
|
|
3322
3567
|
|
|
3568
|
+
// src/memory-tool.ts
|
|
3569
|
+
function obj2(value) {
|
|
3570
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
3571
|
+
}
|
|
3572
|
+
function text(value) {
|
|
3573
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
3574
|
+
}
|
|
3575
|
+
function stableJson(value) {
|
|
3576
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
3577
|
+
if (value && typeof value === "object") {
|
|
3578
|
+
return `{${Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(",")}}`;
|
|
3579
|
+
}
|
|
3580
|
+
return JSON.stringify(value);
|
|
3581
|
+
}
|
|
3582
|
+
function sha2562(value) {
|
|
3583
|
+
return (0, import_node_crypto7.createHash)("sha256").update(value).digest("hex");
|
|
3584
|
+
}
|
|
3585
|
+
function outputBudget(value) {
|
|
3586
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_OUTPUT_CHARS;
|
|
3587
|
+
return Math.min(MAX_OUTPUT_CHARS, Math.max(MIN_OUTPUT_CHARS, Math.floor(value)));
|
|
3588
|
+
}
|
|
3589
|
+
function defined(values) {
|
|
3590
|
+
return Object.fromEntries(Object.entries(values).filter(([, value]) => value !== void 0));
|
|
3591
|
+
}
|
|
3592
|
+
function memoryStatus(config = {}) {
|
|
3593
|
+
return {
|
|
3594
|
+
schema_version: RESULT_SCHEMA_VERSION,
|
|
3595
|
+
action: "status",
|
|
3596
|
+
profile: "M0",
|
|
3597
|
+
server: defined({
|
|
3598
|
+
name: "mindgraph-mcp",
|
|
3599
|
+
version: config.serverVersion ?? "unknown",
|
|
3600
|
+
base_url: config.baseUrl
|
|
3601
|
+
}),
|
|
3602
|
+
integration: {
|
|
3603
|
+
adapter_state: "model_controlled",
|
|
3604
|
+
harness: config.harness ?? "generic",
|
|
3605
|
+
configured_tool_profile: config.profile ?? "general",
|
|
3606
|
+
automatic_capture: false,
|
|
3607
|
+
automatic_delivery: false,
|
|
3608
|
+
delivery_verification: "unsupported",
|
|
3609
|
+
curator_mode: "disabled"
|
|
3610
|
+
},
|
|
3611
|
+
scope: defined({ org_id: config.orgId, agent_id: config.agentId }),
|
|
3612
|
+
capabilities: {
|
|
3613
|
+
status: true,
|
|
3614
|
+
topic_context: true,
|
|
3615
|
+
authoritative_resume_context: true,
|
|
3616
|
+
bounded_rendering: true,
|
|
3617
|
+
structured_content: true,
|
|
3618
|
+
typed_explicit_writes: true,
|
|
3619
|
+
lifecycle_event_ingest: false,
|
|
3620
|
+
automatic_context_injection: false,
|
|
3621
|
+
curator_mutation: false
|
|
3622
|
+
},
|
|
3623
|
+
degradation: [
|
|
3624
|
+
{
|
|
3625
|
+
code: "automatic_capture_unavailable",
|
|
3626
|
+
message: "M0 does not observe host lifecycle events; explicit typed write tools remain available."
|
|
3627
|
+
},
|
|
3628
|
+
{
|
|
3629
|
+
code: "delivery_unverified",
|
|
3630
|
+
message: "A successful MCP read does not prove that the host injected or used the returned context."
|
|
3631
|
+
},
|
|
3632
|
+
{
|
|
3633
|
+
code: "curator_disabled",
|
|
3634
|
+
message: "Curator proposals and autonomous mutation are not enabled in M0."
|
|
3635
|
+
}
|
|
3636
|
+
]
|
|
3637
|
+
};
|
|
3638
|
+
}
|
|
3639
|
+
function nodeType(node) {
|
|
3640
|
+
const raw = node.node_type;
|
|
3641
|
+
if (typeof raw === "string") return raw;
|
|
3642
|
+
return text(obj2(raw)?.Custom) ?? "Node";
|
|
3643
|
+
}
|
|
3644
|
+
function compactItem(raw, fallbackType) {
|
|
3645
|
+
const wrapper = obj2(raw);
|
|
3646
|
+
if (!wrapper) return void 0;
|
|
3647
|
+
const value = obj2(wrapper.node) ?? obj2(wrapper.object) ?? wrapper;
|
|
3648
|
+
const uid = text(value.uid) ?? text(value.chunk_uid);
|
|
3649
|
+
if (!uid) return void 0;
|
|
3650
|
+
return defined({
|
|
3651
|
+
uid,
|
|
3652
|
+
type: fallbackType ?? nodeType(value),
|
|
3653
|
+
label: text(value.label) ?? text(value.document_title),
|
|
3654
|
+
truth_status: text(value.truth_status) ?? text(obj2(value.props)?.truth_status),
|
|
3655
|
+
superseded: value.superseded === true ? true : void 0,
|
|
3656
|
+
currently_valid: value.currently_valid === false ? false : void 0,
|
|
3657
|
+
valid_at_time: value.valid_at_time === false ? false : void 0,
|
|
3658
|
+
retrieval_origin: text(value.retrieval_origin),
|
|
3659
|
+
retrieval_depth: typeof value.retrieval_depth === "number" ? value.retrieval_depth : void 0
|
|
3660
|
+
});
|
|
3661
|
+
}
|
|
3662
|
+
function topicCandidates(raw) {
|
|
3663
|
+
const payload = obj2(raw);
|
|
3664
|
+
if (!payload) return [];
|
|
3665
|
+
const graph = obj2(payload.graph);
|
|
3666
|
+
const items = [];
|
|
3667
|
+
for (const article of Array.isArray(payload.articles) ? payload.articles : []) {
|
|
3668
|
+
const item = compactItem(article, "Article");
|
|
3669
|
+
if (item) items.push(item);
|
|
3670
|
+
}
|
|
3671
|
+
const rawNodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
|
|
3672
|
+
const labels = /* @__PURE__ */ new Map();
|
|
3673
|
+
for (const node of rawNodes) {
|
|
3674
|
+
const item = compactItem(node);
|
|
3675
|
+
if (item) {
|
|
3676
|
+
if (HIDDEN_CONTEXT_NODE_TYPES.has(text(item.type) ?? "")) continue;
|
|
3677
|
+
items.push(item);
|
|
3678
|
+
const uid = text(item.uid);
|
|
3679
|
+
const label = text(item.label);
|
|
3680
|
+
if (uid && label) labels.set(uid, label);
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
for (const rawEdge of Array.isArray(graph?.edges) ? graph.edges : []) {
|
|
3684
|
+
const edge = obj2(rawEdge);
|
|
3685
|
+
const fromUid = text(edge?.from_uid);
|
|
3686
|
+
const toUid = text(edge?.to_uid);
|
|
3687
|
+
const edgeType = text(edge?.edge_type) ?? text(obj2(edge?.edge_type)?.Custom);
|
|
3688
|
+
if (!fromUid || !toUid || !edgeType || HIDDEN_CONTEXT_EDGE_TYPES.has(edgeType) || !labels.has(fromUid) || !labels.has(toUid)) {
|
|
3689
|
+
continue;
|
|
3690
|
+
}
|
|
3691
|
+
items.push(
|
|
3692
|
+
defined({
|
|
3693
|
+
type: "Relationship",
|
|
3694
|
+
from_uid: fromUid,
|
|
3695
|
+
to_uid: toUid,
|
|
3696
|
+
edge_type: edgeType,
|
|
3697
|
+
from_label: labels.get(fromUid),
|
|
3698
|
+
to_label: labels.get(toUid)
|
|
3699
|
+
})
|
|
3700
|
+
);
|
|
3701
|
+
}
|
|
3702
|
+
for (const chunk of Array.isArray(payload.chunks) ? payload.chunks : []) {
|
|
3703
|
+
const item = compactItem(chunk, "SourceExcerpt");
|
|
3704
|
+
if (item) items.push(item);
|
|
3705
|
+
}
|
|
3706
|
+
return items;
|
|
3707
|
+
}
|
|
3708
|
+
function resumeCandidates(raw) {
|
|
3709
|
+
const payload = obj2(raw);
|
|
3710
|
+
if (!payload) return [];
|
|
3711
|
+
const items = [];
|
|
3712
|
+
for (const key of ["task", "goal", "project", "plan", "active_execution", "lease"]) {
|
|
3713
|
+
const item = compactItem(payload[key], key);
|
|
3714
|
+
if (item) items.push(item);
|
|
3715
|
+
}
|
|
3716
|
+
for (const key of ["next_steps", "recent_executions", "blockers", "code_targets"]) {
|
|
3717
|
+
for (const value of Array.isArray(payload[key]) ? payload[key] : []) {
|
|
3718
|
+
const item = compactItem(value, key.replace(/s$/, ""));
|
|
3719
|
+
if (item) items.push(item);
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
const knowledge = obj2(payload.knowledge);
|
|
3723
|
+
for (const key of ["lessons", "risks"]) {
|
|
3724
|
+
for (const value of Array.isArray(knowledge?.[key]) ? knowledge[key] : []) {
|
|
3725
|
+
const item = compactItem(value, key.replace(/s$/, ""));
|
|
3726
|
+
if (item) items.push(item);
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
return items;
|
|
3730
|
+
}
|
|
3731
|
+
function visibleItems(candidates, rendered2) {
|
|
3732
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3733
|
+
return candidates.filter((item) => {
|
|
3734
|
+
const uid = text(item.uid);
|
|
3735
|
+
if (uid) {
|
|
3736
|
+
if (seen.has(uid) || !rendered2.includes(`[${uid}]`)) return false;
|
|
3737
|
+
seen.add(uid);
|
|
3738
|
+
return true;
|
|
3739
|
+
}
|
|
3740
|
+
if (item.type === "Relationship") {
|
|
3741
|
+
const from = text(item.from_label);
|
|
3742
|
+
const to = text(item.to_label);
|
|
3743
|
+
const edgeType = text(item.edge_type);
|
|
3744
|
+
if (!from || !to || !edgeType) return false;
|
|
3745
|
+
const key = `${text(item.from_uid)}:${edgeType}:${text(item.to_uid)}`;
|
|
3746
|
+
if (seen.has(key) || !rendered2.includes(`${from} \u2014${edgeType}\u2192 ${to}`)) return false;
|
|
3747
|
+
seen.add(key);
|
|
3748
|
+
return true;
|
|
3749
|
+
}
|
|
3750
|
+
return false;
|
|
3751
|
+
});
|
|
3752
|
+
}
|
|
3753
|
+
function latestTimestamp(value) {
|
|
3754
|
+
let latest;
|
|
3755
|
+
const visit = (current) => {
|
|
3756
|
+
if (Array.isArray(current)) {
|
|
3757
|
+
for (const item of current) visit(item);
|
|
3758
|
+
return;
|
|
3759
|
+
}
|
|
3760
|
+
const record = obj2(current);
|
|
3761
|
+
if (!record) return;
|
|
3762
|
+
for (const [key, item] of Object.entries(record)) {
|
|
3763
|
+
if (["updated_at", "created_at", "completed_at", "started_at", "observed_at"].includes(key) && typeof item === "string" && !Number.isNaN(Date.parse(item)) && (!latest || Date.parse(item) > Date.parse(latest))) {
|
|
3764
|
+
latest = item;
|
|
3765
|
+
}
|
|
3766
|
+
visit(item);
|
|
3767
|
+
}
|
|
3768
|
+
};
|
|
3769
|
+
visit(value);
|
|
3770
|
+
return latest;
|
|
3771
|
+
}
|
|
3772
|
+
function contextWarnings(raw, rendered2, available, returned) {
|
|
3773
|
+
const warnings = [
|
|
3774
|
+
{
|
|
3775
|
+
code: "delivery_unverified",
|
|
3776
|
+
message: "The MCP server returned this context but cannot verify that the host injected or used it."
|
|
3777
|
+
}
|
|
3778
|
+
];
|
|
3779
|
+
const serialized = JSON.stringify(raw);
|
|
3780
|
+
if (available > returned || rendered2.includes("item(s) omitted")) {
|
|
3781
|
+
warnings.push({
|
|
3782
|
+
code: "context_bounded",
|
|
3783
|
+
message: `${Math.max(0, available - returned)} structured item(s) were omitted from the rendered context.`
|
|
3784
|
+
});
|
|
3785
|
+
}
|
|
3786
|
+
if (/"superseded":true/.test(serialized)) {
|
|
3787
|
+
warnings.push({ code: "superseded_items_present", message: "Some retrieved items are superseded." });
|
|
3788
|
+
}
|
|
3789
|
+
if (/"currently_valid":false|"valid_at_time":false/.test(serialized)) {
|
|
3790
|
+
warnings.push({
|
|
3791
|
+
code: "validity_warning",
|
|
3792
|
+
message: "Some retrieved items are outside their current or requested validity window."
|
|
3793
|
+
});
|
|
3794
|
+
}
|
|
3795
|
+
const payload = obj2(raw);
|
|
3796
|
+
if (payload?.truncated === true || obj2(payload?.truncation)) {
|
|
3797
|
+
const truncation = obj2(payload?.truncation);
|
|
3798
|
+
if (payload?.truncated === true || Object.values(truncation ?? {}).some(Boolean)) {
|
|
3799
|
+
warnings.push({ code: "source_truncated", message: "The authoritative source reported truncated fields." });
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
if (text(payload?.warning)) {
|
|
3803
|
+
warnings.push({ code: "source_warning", message: text(payload?.warning) });
|
|
3804
|
+
}
|
|
3805
|
+
return warnings;
|
|
3806
|
+
}
|
|
3807
|
+
function error(message) {
|
|
3808
|
+
return {
|
|
3809
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
3810
|
+
isError: true
|
|
3811
|
+
};
|
|
3812
|
+
}
|
|
3813
|
+
async function handleMemoryTool(client2, args, config = {}) {
|
|
3814
|
+
const action = text(args.action);
|
|
3815
|
+
if (action === "status") {
|
|
3816
|
+
const status = memoryStatus({ ...config, agentId: text(args.agent_id) ?? config.agentId });
|
|
3817
|
+
return {
|
|
3818
|
+
content: [{ type: "text", text: JSON.stringify(status) }],
|
|
3819
|
+
structuredContent: status
|
|
3820
|
+
};
|
|
3821
|
+
}
|
|
3822
|
+
if (action !== "context") {
|
|
3823
|
+
return error("action must be 'status' or 'context'");
|
|
3824
|
+
}
|
|
3825
|
+
const mode = text(args.mode) ?? (text(args.query) ? "topic" : void 0);
|
|
3826
|
+
if (!mode || !["topic", "continuity", "resume"].includes(mode)) {
|
|
3827
|
+
return error("action='context' requires mode='topic', 'continuity', or 'resume'");
|
|
3828
|
+
}
|
|
3829
|
+
const budget = outputBudget(args.max_output_chars);
|
|
3830
|
+
const agentId = text(args.agent_id) ?? config.agentId;
|
|
3831
|
+
let raw;
|
|
3832
|
+
let rendered2;
|
|
3833
|
+
let strategy;
|
|
3834
|
+
let candidates;
|
|
3835
|
+
if (mode === "topic") {
|
|
3836
|
+
const query = text(args.query);
|
|
3837
|
+
if (!query) return error("mode='topic' requires a non-empty query of 1-3 discriminating keywords");
|
|
3838
|
+
const limit = typeof args.limit === "number" && Number.isFinite(args.limit) ? Math.min(50, Math.max(1, Math.floor(args.limit))) : 8;
|
|
3839
|
+
const request = defined({
|
|
3840
|
+
query,
|
|
3841
|
+
project_uid: text(args.project_uid),
|
|
3842
|
+
node_limit: limit,
|
|
3843
|
+
node_types: Array.isArray(args.node_types) ? args.node_types.map(String) : void 0,
|
|
3844
|
+
layer: text(args.layer),
|
|
3845
|
+
chunk_limit: args.include_chunks === true ? limit : 0,
|
|
3846
|
+
article_limit: args.include_documents === true ? limit : 0,
|
|
3847
|
+
valid_at: text(args.valid_at),
|
|
3848
|
+
include_graph: true,
|
|
3849
|
+
graph_expansion_limit: typeof args.graph_expansion_limit === "number" ? args.graph_expansion_limit : 3,
|
|
3850
|
+
graph_max_depth: typeof args.graph_max_depth === "number" ? args.graph_max_depth : 2
|
|
3851
|
+
});
|
|
3852
|
+
raw = await client2.retrieveContext(request);
|
|
3853
|
+
rendered2 = renderContext(raw, budget) ?? "No results found for this search.";
|
|
3854
|
+
strategy = "retrieve_context";
|
|
3855
|
+
candidates = topicCandidates(raw);
|
|
3856
|
+
} else {
|
|
3857
|
+
const scopeUids = Array.from(
|
|
3858
|
+
/* @__PURE__ */ new Set([
|
|
3859
|
+
...Array.isArray(args.scope_uids) ? args.scope_uids.map(String) : [],
|
|
3860
|
+
...text(args.project_uid) ? [text(args.project_uid)] : []
|
|
3861
|
+
])
|
|
3862
|
+
);
|
|
3863
|
+
raw = await client2.plan(
|
|
3864
|
+
defined({
|
|
3865
|
+
action: "resume_work",
|
|
3866
|
+
task_uid: text(args.task_uid),
|
|
3867
|
+
session_uid: text(args.session_uid),
|
|
3868
|
+
scope_uids: scopeUids.length > 0 ? scopeUids : void 0,
|
|
3869
|
+
limit: 1,
|
|
3870
|
+
agent_id: agentId
|
|
3871
|
+
})
|
|
3872
|
+
);
|
|
3873
|
+
rendered2 = renderResumeContext(raw, budget) ?? "No eligible work was found in the requested scope.";
|
|
3874
|
+
strategy = "resume_work";
|
|
3875
|
+
candidates = resumeCandidates(raw);
|
|
3876
|
+
}
|
|
3877
|
+
const items = visibleItems(candidates, rendered2);
|
|
3878
|
+
const renderedHash = sha2562(rendered2);
|
|
3879
|
+
const scope = defined({
|
|
3880
|
+
org_id: config.orgId,
|
|
3881
|
+
agent_id: agentId,
|
|
3882
|
+
harness: config.harness ?? "generic",
|
|
3883
|
+
project_uid: text(args.project_uid),
|
|
3884
|
+
task_uid: text(args.task_uid),
|
|
3885
|
+
session_uid: text(args.session_uid),
|
|
3886
|
+
scope_uids: Array.isArray(args.scope_uids) ? args.scope_uids.map(String) : void 0
|
|
3887
|
+
});
|
|
3888
|
+
const contextId = `ctx_${sha2562(
|
|
3889
|
+
stableJson({ mode, scope, strategy, query: text(args.query), rendered_hash: renderedHash })
|
|
3890
|
+
).slice(0, 24)}`;
|
|
3891
|
+
const bounded = candidates.length > items.length || rendered2.includes("item(s) omitted");
|
|
3892
|
+
const result = {
|
|
3893
|
+
schema_version: RESULT_SCHEMA_VERSION,
|
|
3894
|
+
action: "context",
|
|
3895
|
+
context_id: contextId,
|
|
3896
|
+
mode,
|
|
3897
|
+
scope,
|
|
3898
|
+
rendered_context: rendered2,
|
|
3899
|
+
rendered_hash: renderedHash,
|
|
3900
|
+
items,
|
|
3901
|
+
retrieval_trace: defined({
|
|
3902
|
+
strategy,
|
|
3903
|
+
query: text(args.query),
|
|
3904
|
+
project_uid: text(args.project_uid),
|
|
3905
|
+
task_uid: text(args.task_uid),
|
|
3906
|
+
session_uid: text(args.session_uid),
|
|
3907
|
+
node_types: Array.isArray(args.node_types) ? args.node_types.map(String) : void 0,
|
|
3908
|
+
graph_expansion_limit: mode === "topic" ? args.graph_expansion_limit ?? 3 : void 0,
|
|
3909
|
+
graph_max_depth: mode === "topic" ? args.graph_max_depth ?? 2 : void 0,
|
|
3910
|
+
available_items: candidates.length,
|
|
3911
|
+
returned_items: items.length,
|
|
3912
|
+
omitted_items: Math.max(0, candidates.length - items.length)
|
|
3913
|
+
}),
|
|
3914
|
+
budget: {
|
|
3915
|
+
unit: "characters",
|
|
3916
|
+
requested_chars: budget,
|
|
3917
|
+
used_chars: rendered2.length,
|
|
3918
|
+
available_items: candidates.length,
|
|
3919
|
+
returned_items: items.length,
|
|
3920
|
+
omitted_items: Math.max(0, candidates.length - items.length),
|
|
3921
|
+
bounded
|
|
3922
|
+
},
|
|
3923
|
+
freshness: defined({
|
|
3924
|
+
valid_at: text(args.valid_at),
|
|
3925
|
+
latest_source_timestamp: latestTimestamp(raw)
|
|
3926
|
+
}),
|
|
3927
|
+
warnings: contextWarnings(raw, rendered2, candidates.length, items.length)
|
|
3928
|
+
};
|
|
3929
|
+
return {
|
|
3930
|
+
content: [{ type: "text", text: rendered2 }],
|
|
3931
|
+
structuredContent: result
|
|
3932
|
+
};
|
|
3933
|
+
}
|
|
3934
|
+
var import_node_crypto7, RESULT_SCHEMA_VERSION, DEFAULT_OUTPUT_CHARS, MIN_OUTPUT_CHARS, MAX_OUTPUT_CHARS, HIDDEN_CONTEXT_NODE_TYPES, HIDDEN_CONTEXT_EDGE_TYPES, MEMORY_TOOL;
|
|
3935
|
+
var init_memory_tool = __esm({
|
|
3936
|
+
"src/memory-tool.ts"() {
|
|
3937
|
+
"use strict";
|
|
3938
|
+
import_node_crypto7 = require("crypto");
|
|
3939
|
+
init_render();
|
|
3940
|
+
RESULT_SCHEMA_VERSION = "mindgraph.memory.result.v1";
|
|
3941
|
+
DEFAULT_OUTPUT_CHARS = 8e3;
|
|
3942
|
+
MIN_OUTPUT_CHARS = 512;
|
|
3943
|
+
MAX_OUTPUT_CHARS = 24e3;
|
|
3944
|
+
HIDDEN_CONTEXT_NODE_TYPES = /* @__PURE__ */ new Set(["Chunk", "Document", "Article"]);
|
|
3945
|
+
HIDDEN_CONTEXT_EDGE_TYPES = /* @__PURE__ */ new Set(["POSSIBLE_DUPLICATE", "PossibleDuplicate"]);
|
|
3946
|
+
MEMORY_TOOL = {
|
|
3947
|
+
name: "mindgraph_memory",
|
|
3948
|
+
description: "Read the portable MindGraph memory surface. Use action='context' with mode='resume' or 'continuity' for authoritative task-first work state, and mode='topic' with a short keyword query for prior knowledge relevant to the user's request. Use action='status' to inspect integration capabilities and limitations. This tool never writes; durable capture and correction use the existing typed MindGraph write tools.",
|
|
3949
|
+
inputSchema: {
|
|
3950
|
+
type: "object",
|
|
3951
|
+
properties: {
|
|
3952
|
+
action: {
|
|
3953
|
+
type: "string",
|
|
3954
|
+
enum: ["status", "context"],
|
|
3955
|
+
description: "Read integration status or bounded memory context"
|
|
3956
|
+
},
|
|
3957
|
+
mode: {
|
|
3958
|
+
type: "string",
|
|
3959
|
+
enum: ["topic", "continuity", "resume"],
|
|
3960
|
+
description: "Context strategy. topic uses query-shaped retrieval; continuity/resume use authoritative task-first resume_work."
|
|
3961
|
+
},
|
|
3962
|
+
query: {
|
|
3963
|
+
type: "string",
|
|
3964
|
+
description: "Required for mode='topic': 1-3 discriminating BM25 keywords, not a full sentence"
|
|
3965
|
+
},
|
|
3966
|
+
project_uid: { type: "string", description: "Optional project scope" },
|
|
3967
|
+
task_uid: { type: "string", description: "Optional task scope for resume context" },
|
|
3968
|
+
session_uid: { type: "string", description: "Optional durable session scope" },
|
|
3969
|
+
scope_uids: {
|
|
3970
|
+
type: "array",
|
|
3971
|
+
items: { type: "string" },
|
|
3972
|
+
description: "Optional explicit graph scopes for continuity/resume"
|
|
3973
|
+
},
|
|
3974
|
+
node_types: {
|
|
3975
|
+
type: "array",
|
|
3976
|
+
items: { type: "string" },
|
|
3977
|
+
description: "Optional node-type filter for topic context"
|
|
3978
|
+
},
|
|
3979
|
+
layer: {
|
|
3980
|
+
type: "string",
|
|
3981
|
+
enum: ["reality", "epistemic", "intent", "action", "memory", "agent"],
|
|
3982
|
+
description: "Optional cognitive-layer filter for topic context"
|
|
3983
|
+
},
|
|
3984
|
+
limit: {
|
|
3985
|
+
type: "integer",
|
|
3986
|
+
minimum: 1,
|
|
3987
|
+
maximum: 50,
|
|
3988
|
+
default: 8,
|
|
3989
|
+
description: "Maximum direct graph items requested before bounded expansion"
|
|
3990
|
+
},
|
|
3991
|
+
include_chunks: {
|
|
3992
|
+
type: "boolean",
|
|
3993
|
+
default: false,
|
|
3994
|
+
description: "Include bounded source excerpts when topic summaries are insufficient"
|
|
3995
|
+
},
|
|
3996
|
+
include_documents: {
|
|
3997
|
+
type: "boolean",
|
|
3998
|
+
default: false,
|
|
3999
|
+
description: "Include bounded knowledge articles for topic context"
|
|
4000
|
+
},
|
|
4001
|
+
valid_at: {
|
|
4002
|
+
type: "string",
|
|
4003
|
+
description: "Optional ISO timestamp for time-aware topic retrieval"
|
|
4004
|
+
},
|
|
4005
|
+
graph_expansion_limit: {
|
|
4006
|
+
type: "integer",
|
|
4007
|
+
minimum: 0,
|
|
4008
|
+
maximum: 20,
|
|
4009
|
+
default: 3,
|
|
4010
|
+
description: "Bounded graph neighbors added after direct topic matches"
|
|
4011
|
+
},
|
|
4012
|
+
graph_max_depth: {
|
|
4013
|
+
type: "integer",
|
|
4014
|
+
minimum: 0,
|
|
4015
|
+
maximum: 5,
|
|
4016
|
+
default: 2,
|
|
4017
|
+
description: "Maximum graph-expansion depth for topic context"
|
|
4018
|
+
},
|
|
4019
|
+
max_output_chars: {
|
|
4020
|
+
type: "integer",
|
|
4021
|
+
minimum: MIN_OUTPUT_CHARS,
|
|
4022
|
+
maximum: MAX_OUTPUT_CHARS,
|
|
4023
|
+
default: DEFAULT_OUTPUT_CHARS,
|
|
4024
|
+
description: "Hard character budget for model-ready context; whole items are retained or omitted"
|
|
4025
|
+
},
|
|
4026
|
+
agent_id: { type: "string", description: "Agent identity" }
|
|
4027
|
+
},
|
|
4028
|
+
required: ["action"]
|
|
4029
|
+
},
|
|
4030
|
+
outputSchema: {
|
|
4031
|
+
type: "object",
|
|
4032
|
+
properties: {
|
|
4033
|
+
schema_version: { type: "string", const: RESULT_SCHEMA_VERSION },
|
|
4034
|
+
action: { type: "string", enum: ["status", "context"] },
|
|
4035
|
+
profile: { type: "string" },
|
|
4036
|
+
server: { type: "object" },
|
|
4037
|
+
integration: { type: "object" },
|
|
4038
|
+
capabilities: { type: "object" },
|
|
4039
|
+
degradation: { type: "array", items: { type: "object" } },
|
|
4040
|
+
context_id: { type: "string" },
|
|
4041
|
+
mode: { type: "string", enum: ["topic", "continuity", "resume"] },
|
|
4042
|
+
scope: { type: "object" },
|
|
4043
|
+
rendered_context: { type: "string" },
|
|
4044
|
+
rendered_hash: { type: "string" },
|
|
4045
|
+
items: { type: "array", items: { type: "object" } },
|
|
4046
|
+
retrieval_trace: { type: "object" },
|
|
4047
|
+
budget: { type: "object" },
|
|
4048
|
+
freshness: { type: "object" },
|
|
4049
|
+
warnings: { type: "array", items: { type: "object" } }
|
|
4050
|
+
},
|
|
4051
|
+
required: ["schema_version", "action"],
|
|
4052
|
+
oneOf: [
|
|
4053
|
+
{
|
|
4054
|
+
properties: { action: { const: "status" } },
|
|
4055
|
+
required: ["profile", "server", "integration", "scope", "capabilities", "degradation"]
|
|
4056
|
+
},
|
|
4057
|
+
{
|
|
4058
|
+
properties: { action: { const: "context" } },
|
|
4059
|
+
required: [
|
|
4060
|
+
"context_id",
|
|
4061
|
+
"mode",
|
|
4062
|
+
"scope",
|
|
4063
|
+
"rendered_context",
|
|
4064
|
+
"rendered_hash",
|
|
4065
|
+
"items",
|
|
4066
|
+
"retrieval_trace",
|
|
4067
|
+
"budget",
|
|
4068
|
+
"freshness",
|
|
4069
|
+
"warnings"
|
|
4070
|
+
]
|
|
4071
|
+
}
|
|
4072
|
+
]
|
|
4073
|
+
},
|
|
4074
|
+
annotations: {
|
|
4075
|
+
readOnlyHint: true,
|
|
4076
|
+
destructiveHint: false,
|
|
4077
|
+
idempotentHint: true,
|
|
4078
|
+
openWorldHint: false
|
|
4079
|
+
}
|
|
4080
|
+
};
|
|
4081
|
+
}
|
|
4082
|
+
});
|
|
4083
|
+
|
|
3323
4084
|
// src/tools.ts
|
|
4085
|
+
function stableJson2(value) {
|
|
4086
|
+
if (Array.isArray(value)) return `[${value.map(stableJson2).join(",")}]`;
|
|
4087
|
+
if (value && typeof value === "object") {
|
|
4088
|
+
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson2(item)}`).join(",")}}`;
|
|
4089
|
+
}
|
|
4090
|
+
return JSON.stringify(value);
|
|
4091
|
+
}
|
|
4092
|
+
function defaultWriteIdempotencyKey(tool, args) {
|
|
4093
|
+
if (typeof args.idempotency_key === "string") return args.idempotency_key;
|
|
4094
|
+
const context = args.invocation_context;
|
|
4095
|
+
const sessionId = context?.harnessSessionId;
|
|
4096
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) return void 0;
|
|
4097
|
+
const canonicalArgs = Object.fromEntries(
|
|
4098
|
+
Object.entries(args).filter(
|
|
4099
|
+
([key]) => key !== "invocation_context" && key !== "idempotency_key"
|
|
4100
|
+
)
|
|
4101
|
+
);
|
|
4102
|
+
return (0, import_node_crypto8.createHash)("sha256").update(
|
|
4103
|
+
stableJson2({
|
|
4104
|
+
harness_session_id: sessionId,
|
|
4105
|
+
harness_turn_id: context?.harnessTurnId ?? null,
|
|
4106
|
+
tool,
|
|
4107
|
+
action: args.action ?? null,
|
|
4108
|
+
args: canonicalArgs
|
|
4109
|
+
})
|
|
4110
|
+
).digest("hex");
|
|
4111
|
+
}
|
|
4112
|
+
function resolutionFields(args) {
|
|
4113
|
+
return Object.fromEntries(
|
|
4114
|
+
[
|
|
4115
|
+
"idempotency_key",
|
|
4116
|
+
"reuse_uid",
|
|
4117
|
+
"supersedes_uid",
|
|
4118
|
+
"contradicts_uid",
|
|
4119
|
+
"confirm_new",
|
|
4120
|
+
"on_near_duplicate"
|
|
4121
|
+
].filter((key) => args[key] !== void 0).map((key) => [key, args[key]])
|
|
4122
|
+
);
|
|
4123
|
+
}
|
|
3324
4124
|
function toolsForProfile(profile = process.env.MINDGRAPH_PROFILE || "general") {
|
|
3325
4125
|
if (profile === "coding") return TOOLS;
|
|
3326
4126
|
return TOOLS.filter(
|
|
@@ -3334,8 +4134,8 @@ function ok3(data) {
|
|
|
3334
4134
|
}
|
|
3335
4135
|
function renderedResult(format, raw, render) {
|
|
3336
4136
|
if (format === "json") return ok3(raw);
|
|
3337
|
-
const
|
|
3338
|
-
return
|
|
4137
|
+
const text2 = render(raw);
|
|
4138
|
+
return text2 !== void 0 ? { content: [{ type: "text", text: text2 }] } : ok3(raw);
|
|
3339
4139
|
}
|
|
3340
4140
|
function err3(message) {
|
|
3341
4141
|
return {
|
|
@@ -3343,8 +4143,22 @@ function err3(message) {
|
|
|
3343
4143
|
isError: true
|
|
3344
4144
|
};
|
|
3345
4145
|
}
|
|
3346
|
-
async function handleTool(client2, name, args) {
|
|
4146
|
+
async function handleTool(client2, name, args, memoryConfig = {}) {
|
|
3347
4147
|
try {
|
|
4148
|
+
if (["mindgraph_capture", "mindgraph_reason", "mindgraph_commit"].includes(name) && args.action !== "get_open_decisions") {
|
|
4149
|
+
const key = defaultWriteIdempotencyKey(name, args);
|
|
4150
|
+
if (key) args = { ...args, idempotency_key: key };
|
|
4151
|
+
const dispositions = [
|
|
4152
|
+
args.reuse_uid,
|
|
4153
|
+
args.supersedes_uid,
|
|
4154
|
+
args.contradicts_uid,
|
|
4155
|
+
args.confirm_new === true ? true : void 0,
|
|
4156
|
+
args.on_near_duplicate
|
|
4157
|
+
].filter((value) => value !== void 0);
|
|
4158
|
+
if (dispositions.length > 1) {
|
|
4159
|
+
return err3("resolution dispositions are mutually exclusive");
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
3348
4162
|
let result;
|
|
3349
4163
|
switch (name) {
|
|
3350
4164
|
case "mindgraph_capture":
|
|
@@ -3362,6 +4176,12 @@ async function handleTool(client2, name, args) {
|
|
|
3362
4176
|
case "mindgraph_retrieve":
|
|
3363
4177
|
result = await handleRetrieve(client2, args);
|
|
3364
4178
|
break;
|
|
4179
|
+
case "mindgraph_memory":
|
|
4180
|
+
result = await handleMemoryTool(client2, args, memoryConfig);
|
|
4181
|
+
break;
|
|
4182
|
+
case "mindgraph_series_query":
|
|
4183
|
+
result = await handleSeriesQuery(client2, args);
|
|
4184
|
+
break;
|
|
3365
4185
|
case "mindgraph_ingest":
|
|
3366
4186
|
result = await handleIngest(client2, args);
|
|
3367
4187
|
break;
|
|
@@ -3406,17 +4226,45 @@ async function handleTool(client2, name, args) {
|
|
|
3406
4226
|
return err3(errorDetail(e));
|
|
3407
4227
|
}
|
|
3408
4228
|
}
|
|
4229
|
+
async function handleSeriesQuery(client2, args) {
|
|
4230
|
+
const action = typeof args.action === "string" ? args.action : "";
|
|
4231
|
+
if (!["list_for_entity", "latest", "window", "aggregate"].includes(action)) {
|
|
4232
|
+
return err3(`Unknown series query action: ${action}`);
|
|
4233
|
+
}
|
|
4234
|
+
if (action === "list_for_entity" && typeof args.entity_uid !== "string") {
|
|
4235
|
+
return err3("entity_uid is required for action='list_for_entity'");
|
|
4236
|
+
}
|
|
4237
|
+
if (action !== "list_for_entity" && typeof args.series_uid !== "string") {
|
|
4238
|
+
return err3(`series_uid is required for action='${action}'`);
|
|
4239
|
+
}
|
|
4240
|
+
if ((action === "window" || action === "aggregate") && (typeof args.from !== "number" || typeof args.to !== "number")) {
|
|
4241
|
+
return err3(`from and to are required for action='${action}'`);
|
|
4242
|
+
}
|
|
4243
|
+
if (action === "aggregate" && (typeof args.bucket !== "string" || typeof args.agg !== "string")) {
|
|
4244
|
+
return err3("bucket and agg are required for action='aggregate'");
|
|
4245
|
+
}
|
|
4246
|
+
return ok3(
|
|
4247
|
+
await client2.series(args)
|
|
4248
|
+
);
|
|
4249
|
+
}
|
|
3409
4250
|
async function handleCapture(client2, args) {
|
|
3410
4251
|
const {
|
|
3411
4252
|
action,
|
|
3412
4253
|
label,
|
|
3413
4254
|
summary,
|
|
3414
4255
|
content,
|
|
4256
|
+
name,
|
|
4257
|
+
description,
|
|
4258
|
+
license,
|
|
3415
4259
|
session_uid,
|
|
3416
4260
|
work_uid,
|
|
3417
4261
|
execution_uid,
|
|
3418
4262
|
idempotency_key,
|
|
4263
|
+
reuse_uid,
|
|
3419
4264
|
supersedes_uid,
|
|
4265
|
+
contradicts_uid,
|
|
4266
|
+
confirm_new,
|
|
4267
|
+
on_near_duplicate,
|
|
3420
4268
|
summarizes_uids,
|
|
3421
4269
|
entity_type,
|
|
3422
4270
|
source_uid,
|
|
@@ -3440,38 +4288,82 @@ async function handleCapture(client2, args) {
|
|
|
3440
4288
|
work_uid,
|
|
3441
4289
|
execution_uid,
|
|
3442
4290
|
idempotency_key,
|
|
4291
|
+
reuse_uid,
|
|
3443
4292
|
supersedes_uid,
|
|
4293
|
+
contradicts_uid,
|
|
4294
|
+
confirm_new,
|
|
4295
|
+
on_near_duplicate,
|
|
3444
4296
|
summarizes_uids,
|
|
3445
4297
|
props,
|
|
3446
4298
|
agent_id
|
|
3447
4299
|
})
|
|
3448
4300
|
);
|
|
4301
|
+
case "skill": {
|
|
4302
|
+
if (!name) return err3("name is required for action=skill");
|
|
4303
|
+
if (!description) return err3("description is required for action=skill");
|
|
4304
|
+
if (content === void 0) return err3("content is required for action=skill");
|
|
4305
|
+
const hasProvenance = [session_uid, work_uid, execution_uid].some(
|
|
4306
|
+
(uid) => typeof uid === "string" && uid.length > 0
|
|
4307
|
+
) || Array.isArray(summarizes_uids) && summarizes_uids.some((uid) => typeof uid === "string" && uid.length > 0);
|
|
4308
|
+
if (!hasProvenance) {
|
|
4309
|
+
return err3(
|
|
4310
|
+
"at least one of session_uid, work_uid, execution_uid, or summarizes_uids is required for action=skill"
|
|
4311
|
+
);
|
|
4312
|
+
}
|
|
4313
|
+
return ok3(
|
|
4314
|
+
await client2.distill({
|
|
4315
|
+
label,
|
|
4316
|
+
output_type: "skill",
|
|
4317
|
+
summary,
|
|
4318
|
+
confidence,
|
|
4319
|
+
salience,
|
|
4320
|
+
session_uid,
|
|
4321
|
+
work_uid,
|
|
4322
|
+
execution_uid,
|
|
4323
|
+
idempotency_key,
|
|
4324
|
+
supersedes_uid,
|
|
4325
|
+
summarizes_uids,
|
|
4326
|
+
props: {
|
|
4327
|
+
name,
|
|
4328
|
+
description,
|
|
4329
|
+
content,
|
|
4330
|
+
...license ? { license } : {}
|
|
4331
|
+
},
|
|
4332
|
+
agent_id
|
|
4333
|
+
})
|
|
4334
|
+
);
|
|
4335
|
+
}
|
|
3449
4336
|
case "journal": {
|
|
3450
4337
|
if (!content) return err3("content is required for action=journal");
|
|
3451
4338
|
const journalProps = { content };
|
|
3452
4339
|
if (mood) journalProps.mood = mood;
|
|
3453
4340
|
if (tags) journalProps.tags = tags;
|
|
3454
|
-
return ok3(await client2.journal(label, journalProps, {
|
|
4341
|
+
return ok3(await client2.journal(label, journalProps, {
|
|
4342
|
+
agent_id,
|
|
4343
|
+
idempotency_key,
|
|
4344
|
+
reuse_uid,
|
|
4345
|
+
supersedes_uid,
|
|
4346
|
+
contradicts_uid,
|
|
4347
|
+
confirm_new,
|
|
4348
|
+
on_near_duplicate
|
|
4349
|
+
}));
|
|
3455
4350
|
}
|
|
3456
4351
|
case "entity": {
|
|
3457
4352
|
const type = entity_type || "concept";
|
|
3458
4353
|
const entityProps = { ...props };
|
|
3459
4354
|
if (summary) entityProps.description = summary;
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
default:
|
|
3473
|
-
return ok3(await client2.findOrCreateConcept(label, entityProps, agent_id));
|
|
3474
|
-
}
|
|
4355
|
+
return ok3(await client2.entity({
|
|
4356
|
+
action: "create",
|
|
4357
|
+
label,
|
|
4358
|
+
props: { ...entityProps, entity_type: type },
|
|
4359
|
+
agent_id,
|
|
4360
|
+
idempotency_key,
|
|
4361
|
+
reuse_uid,
|
|
4362
|
+
supersedes_uid,
|
|
4363
|
+
contradicts_uid,
|
|
4364
|
+
confirm_new,
|
|
4365
|
+
on_near_duplicate
|
|
4366
|
+
}));
|
|
3475
4367
|
}
|
|
3476
4368
|
case "observation":
|
|
3477
4369
|
return ok3(
|
|
@@ -3482,7 +4374,13 @@ async function handleCapture(client2, args) {
|
|
|
3482
4374
|
confidence,
|
|
3483
4375
|
salience,
|
|
3484
4376
|
props,
|
|
3485
|
-
agent_id
|
|
4377
|
+
agent_id,
|
|
4378
|
+
idempotency_key,
|
|
4379
|
+
reuse_uid,
|
|
4380
|
+
supersedes_uid,
|
|
4381
|
+
contradicts_uid,
|
|
4382
|
+
confirm_new,
|
|
4383
|
+
on_near_duplicate
|
|
3486
4384
|
})
|
|
3487
4385
|
);
|
|
3488
4386
|
case "source":
|
|
@@ -3494,7 +4392,13 @@ async function handleCapture(client2, args) {
|
|
|
3494
4392
|
confidence,
|
|
3495
4393
|
salience,
|
|
3496
4394
|
props,
|
|
3497
|
-
agent_id
|
|
4395
|
+
agent_id,
|
|
4396
|
+
idempotency_key,
|
|
4397
|
+
reuse_uid,
|
|
4398
|
+
supersedes_uid,
|
|
4399
|
+
contradicts_uid,
|
|
4400
|
+
confirm_new,
|
|
4401
|
+
on_near_duplicate
|
|
3498
4402
|
})
|
|
3499
4403
|
);
|
|
3500
4404
|
case "snippet":
|
|
@@ -3507,26 +4411,38 @@ async function handleCapture(client2, args) {
|
|
|
3507
4411
|
confidence,
|
|
3508
4412
|
salience,
|
|
3509
4413
|
props,
|
|
3510
|
-
agent_id
|
|
4414
|
+
agent_id,
|
|
4415
|
+
idempotency_key,
|
|
4416
|
+
reuse_uid,
|
|
4417
|
+
supersedes_uid,
|
|
4418
|
+
contradicts_uid,
|
|
4419
|
+
confirm_new,
|
|
4420
|
+
on_near_duplicate
|
|
3511
4421
|
})
|
|
3512
4422
|
);
|
|
3513
4423
|
case "concept":
|
|
3514
|
-
return ok3(
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
4424
|
+
return ok3(await client2.entity({
|
|
4425
|
+
action: "create",
|
|
4426
|
+
label,
|
|
4427
|
+
props: {
|
|
4428
|
+
...props,
|
|
4429
|
+
entity_type: "concept",
|
|
4430
|
+
...summary ? { description: summary } : {}
|
|
4431
|
+
},
|
|
4432
|
+
agent_id,
|
|
4433
|
+
idempotency_key,
|
|
4434
|
+
reuse_uid,
|
|
4435
|
+
supersedes_uid,
|
|
4436
|
+
contradicts_uid,
|
|
4437
|
+
confirm_new,
|
|
4438
|
+
on_near_duplicate
|
|
4439
|
+
}));
|
|
3525
4440
|
default:
|
|
3526
4441
|
return err3(`Unknown capture action: ${action}`);
|
|
3527
4442
|
}
|
|
3528
4443
|
}
|
|
3529
4444
|
async function handleReason(client2, args) {
|
|
4445
|
+
const resolution = resolutionFields(args);
|
|
3530
4446
|
const {
|
|
3531
4447
|
action,
|
|
3532
4448
|
label,
|
|
@@ -3559,7 +4475,8 @@ async function handleReason(client2, args) {
|
|
|
3559
4475
|
...warrant.content ? { props: { content: warrant.content } } : {}
|
|
3560
4476
|
} : void 0,
|
|
3561
4477
|
argument,
|
|
3562
|
-
agent_id
|
|
4478
|
+
agent_id,
|
|
4479
|
+
...resolution
|
|
3563
4480
|
})
|
|
3564
4481
|
);
|
|
3565
4482
|
}
|
|
@@ -3572,11 +4489,13 @@ async function handleReason(client2, args) {
|
|
|
3572
4489
|
confidence,
|
|
3573
4490
|
salience,
|
|
3574
4491
|
props,
|
|
3575
|
-
agent_id
|
|
4492
|
+
agent_id,
|
|
4493
|
+
...resolution
|
|
3576
4494
|
})
|
|
3577
4495
|
);
|
|
3578
4496
|
}
|
|
3579
4497
|
async function handleCommit(client2, args) {
|
|
4498
|
+
const resolution = resolutionFields(args);
|
|
3580
4499
|
const {
|
|
3581
4500
|
action,
|
|
3582
4501
|
label,
|
|
@@ -3607,16 +4526,17 @@ async function handleCommit(client2, args) {
|
|
|
3607
4526
|
confidence,
|
|
3608
4527
|
salience,
|
|
3609
4528
|
props,
|
|
3610
|
-
agent_id
|
|
4529
|
+
agent_id,
|
|
4530
|
+
...resolution
|
|
3611
4531
|
})
|
|
3612
4532
|
);
|
|
3613
4533
|
case "open_decision":
|
|
3614
4534
|
if (!label) return err3("label is required for open_decision");
|
|
3615
|
-
return ok3(await client2.openDecision(label, { summary, props, agent_id }));
|
|
4535
|
+
return ok3(await client2.openDecision(label, { summary, props, agent_id, ...resolution }));
|
|
3616
4536
|
case "add_option":
|
|
3617
4537
|
if (!decision_uid) return err3("decision_uid is required for add_option");
|
|
3618
4538
|
if (!label) return err3("label is required for add_option");
|
|
3619
|
-
return ok3(await client2.addOption(decision_uid, label, { summary, props, agent_id }));
|
|
4539
|
+
return ok3(await client2.addOption(decision_uid, label, { summary, props, agent_id, ...resolution }));
|
|
3620
4540
|
case "add_constraint":
|
|
3621
4541
|
if (!decision_uid) return err3("decision_uid is required for add_constraint");
|
|
3622
4542
|
return ok3(
|
|
@@ -3626,7 +4546,8 @@ async function handleCommit(client2, args) {
|
|
|
3626
4546
|
decision_uid,
|
|
3627
4547
|
summary,
|
|
3628
4548
|
props,
|
|
3629
|
-
agent_id
|
|
4549
|
+
agent_id,
|
|
4550
|
+
...resolution
|
|
3630
4551
|
})
|
|
3631
4552
|
);
|
|
3632
4553
|
case "resolve_decision":
|
|
@@ -3640,7 +4561,8 @@ async function handleCommit(client2, args) {
|
|
|
3640
4561
|
as_of_date,
|
|
3641
4562
|
session_id,
|
|
3642
4563
|
retrieval_trace_id,
|
|
3643
|
-
agent_id
|
|
4564
|
+
agent_id,
|
|
4565
|
+
...resolution
|
|
3644
4566
|
})
|
|
3645
4567
|
);
|
|
3646
4568
|
case "get_open_decisions":
|
|
@@ -3721,7 +4643,7 @@ async function handlePlan(client2, args) {
|
|
|
3721
4643
|
output_snapshot,
|
|
3722
4644
|
side_effects,
|
|
3723
4645
|
outcome,
|
|
3724
|
-
error,
|
|
4646
|
+
error: error2,
|
|
3725
4647
|
next_action,
|
|
3726
4648
|
checkpoint_summary,
|
|
3727
4649
|
test_summary,
|
|
@@ -3784,7 +4706,7 @@ async function handlePlan(client2, args) {
|
|
|
3784
4706
|
output_snapshot,
|
|
3785
4707
|
side_effects,
|
|
3786
4708
|
outcome,
|
|
3787
|
-
error,
|
|
4709
|
+
error: error2,
|
|
3788
4710
|
next_action,
|
|
3789
4711
|
checkpoint_summary,
|
|
3790
4712
|
test_summary,
|
|
@@ -3833,7 +4755,7 @@ async function handlePlan(client2, args) {
|
|
|
3833
4755
|
output_snapshot,
|
|
3834
4756
|
side_effects,
|
|
3835
4757
|
outcome,
|
|
3836
|
-
error,
|
|
4758
|
+
error: error2,
|
|
3837
4759
|
props,
|
|
3838
4760
|
agent_id
|
|
3839
4761
|
})
|
|
@@ -3919,6 +4841,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3919
4841
|
const {
|
|
3920
4842
|
action,
|
|
3921
4843
|
format,
|
|
4844
|
+
max_output_chars,
|
|
3922
4845
|
query,
|
|
3923
4846
|
start_uid,
|
|
3924
4847
|
end_uid,
|
|
@@ -3973,7 +4896,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3973
4896
|
graph_expansion_limit: graph_expansion_limit ?? 3,
|
|
3974
4897
|
graph_max_depth: graph_max_depth ?? 2
|
|
3975
4898
|
}),
|
|
3976
|
-
renderContext
|
|
4899
|
+
(raw) => renderContext(raw, max_output_chars)
|
|
3977
4900
|
);
|
|
3978
4901
|
case "text":
|
|
3979
4902
|
if (!query) return err3("query is required for text search");
|
|
@@ -3987,7 +4910,7 @@ async function handleRetrieve(client2, args) {
|
|
|
3987
4910
|
explain,
|
|
3988
4911
|
agent_id
|
|
3989
4912
|
}),
|
|
3990
|
-
(raw) => renderNodeList(raw, `Text search: ${query}
|
|
4913
|
+
(raw) => renderNodeList(raw, `Text search: ${query}`, void 0, max_output_chars)
|
|
3991
4914
|
);
|
|
3992
4915
|
case "semantic":
|
|
3993
4916
|
if (!query) return err3("query is required for semantic search");
|
|
@@ -4001,7 +4924,7 @@ async function handleRetrieve(client2, args) {
|
|
|
4001
4924
|
explain,
|
|
4002
4925
|
agent_id
|
|
4003
4926
|
}),
|
|
4004
|
-
(raw) => renderNodeList(raw, `Semantic search: ${query}
|
|
4927
|
+
(raw) => renderNodeList(raw, `Semantic search: ${query}`, void 0, max_output_chars)
|
|
4005
4928
|
);
|
|
4006
4929
|
case "hybrid":
|
|
4007
4930
|
if (!query) return err3("query is required for hybrid search");
|
|
@@ -4016,7 +4939,7 @@ async function handleRetrieve(client2, args) {
|
|
|
4016
4939
|
agent_id
|
|
4017
4940
|
}),
|
|
4018
4941
|
// With explain, per-leg scoring must survive — raw JSON only.
|
|
4019
|
-
explain ? () => void 0 : (raw) => renderNodeList(raw, `Hybrid search: ${query}
|
|
4942
|
+
explain ? () => void 0 : (raw) => renderNodeList(raw, `Hybrid search: ${query}`, void 0, max_output_chars)
|
|
4020
4943
|
);
|
|
4021
4944
|
// Structured queries. These go through the generic /retrieve action
|
|
4022
4945
|
// dispatch rather than the SDK's no-argument convenience methods — the
|
|
@@ -4034,7 +4957,7 @@ async function handleRetrieve(client2, args) {
|
|
|
4034
4957
|
offset,
|
|
4035
4958
|
agent_id
|
|
4036
4959
|
}),
|
|
4037
|
-
(raw) => renderNodeList(raw, action.replace(/_/g, " "), limit)
|
|
4960
|
+
(raw) => renderNodeList(raw, action.replace(/_/g, " "), limit, max_output_chars)
|
|
4038
4961
|
);
|
|
4039
4962
|
case "stale_derivations":
|
|
4040
4963
|
return rendered2(
|
|
@@ -4043,7 +4966,7 @@ async function handleRetrieve(client2, args) {
|
|
|
4043
4966
|
limit,
|
|
4044
4967
|
offset
|
|
4045
4968
|
}),
|
|
4046
|
-
(raw) => renderNodeList(raw, "stale derivations", limit)
|
|
4969
|
+
(raw) => renderNodeList(raw, "stale derivations", limit, max_output_chars)
|
|
4047
4970
|
);
|
|
4048
4971
|
case "preferences":
|
|
4049
4972
|
return rendered2(
|
|
@@ -4053,7 +4976,7 @@ async function handleRetrieve(client2, args) {
|
|
|
4053
4976
|
limit,
|
|
4054
4977
|
agent_id
|
|
4055
4978
|
}),
|
|
4056
|
-
(raw) => renderNodeList(raw, "Preferences", limit)
|
|
4979
|
+
(raw) => renderNodeList(raw, "Preferences", limit, max_output_chars)
|
|
4057
4980
|
);
|
|
4058
4981
|
case "layer":
|
|
4059
4982
|
if (!layer) return err3("layer is required for layer retrieval");
|
|
@@ -4065,7 +4988,7 @@ async function handleRetrieve(client2, args) {
|
|
|
4065
4988
|
node_types,
|
|
4066
4989
|
agent_id
|
|
4067
4990
|
}),
|
|
4068
|
-
(raw) => renderNodeList(raw, `Layer: ${layer}`, limit)
|
|
4991
|
+
(raw) => renderNodeList(raw, `Layer: ${layer}`, limit, max_output_chars)
|
|
4069
4992
|
);
|
|
4070
4993
|
case "recent":
|
|
4071
4994
|
return rendered2(
|
|
@@ -4079,7 +5002,7 @@ async function handleRetrieve(client2, args) {
|
|
|
4079
5002
|
created_before,
|
|
4080
5003
|
agent_id
|
|
4081
5004
|
}),
|
|
4082
|
-
(raw) => renderNodeList(raw, "Recently ingested", limit)
|
|
5005
|
+
(raw) => renderNodeList(raw, "Recently ingested", limit, max_output_chars)
|
|
4083
5006
|
);
|
|
4084
5007
|
// Document inventory
|
|
4085
5008
|
case "document_index":
|
|
@@ -4088,7 +5011,7 @@ async function handleRetrieve(client2, args) {
|
|
|
4088
5011
|
node_type: "Document",
|
|
4089
5012
|
limit: limit ?? 100
|
|
4090
5013
|
}),
|
|
4091
|
-
(raw) => renderNodeList(raw, "Ingested documents")
|
|
5014
|
+
(raw) => renderNodeList(raw, "Ingested documents", void 0, max_output_chars)
|
|
4092
5015
|
);
|
|
4093
5016
|
// Graph traversal
|
|
4094
5017
|
case "chain":
|
|
@@ -4102,7 +5025,7 @@ async function handleRetrieve(client2, args) {
|
|
|
4102
5025
|
include_provenance,
|
|
4103
5026
|
max_nodes
|
|
4104
5027
|
}),
|
|
4105
|
-
renderTraversal
|
|
5028
|
+
(raw) => renderTraversal(raw, max_output_chars)
|
|
4106
5029
|
);
|
|
4107
5030
|
case "neighborhood":
|
|
4108
5031
|
if (!start_uid) return err3("start_uid is required for neighborhood traversal");
|
|
@@ -4117,7 +5040,7 @@ async function handleRetrieve(client2, args) {
|
|
|
4117
5040
|
include_provenance,
|
|
4118
5041
|
max_nodes
|
|
4119
5042
|
}),
|
|
4120
|
-
renderTraversal
|
|
5043
|
+
(raw) => renderTraversal(raw, max_output_chars)
|
|
4121
5044
|
);
|
|
4122
5045
|
case "path":
|
|
4123
5046
|
if (!start_uid) return err3("start_uid is required for path traversal");
|
|
@@ -4134,7 +5057,7 @@ async function handleRetrieve(client2, args) {
|
|
|
4134
5057
|
include_provenance,
|
|
4135
5058
|
max_nodes
|
|
4136
5059
|
}),
|
|
4137
|
-
renderTraversal
|
|
5060
|
+
(raw) => renderTraversal(raw, max_output_chars)
|
|
4138
5061
|
);
|
|
4139
5062
|
case "subgraph":
|
|
4140
5063
|
if (!start_uid) return err3("start_uid is required for subgraph traversal");
|
|
@@ -4149,7 +5072,7 @@ async function handleRetrieve(client2, args) {
|
|
|
4149
5072
|
include_provenance,
|
|
4150
5073
|
max_nodes
|
|
4151
5074
|
}),
|
|
4152
|
-
renderTraversal
|
|
5075
|
+
(raw) => renderTraversal(raw, max_output_chars)
|
|
4153
5076
|
);
|
|
4154
5077
|
case "top_k_paths":
|
|
4155
5078
|
if (!start_uid) return err3("start_uid is required for top_k_paths");
|
|
@@ -4165,7 +5088,7 @@ async function handleRetrieve(client2, args) {
|
|
|
4165
5088
|
edge_types,
|
|
4166
5089
|
exclude_edge_types
|
|
4167
5090
|
}),
|
|
4168
|
-
renderTraversal
|
|
5091
|
+
(raw) => renderTraversal(raw, max_output_chars)
|
|
4169
5092
|
);
|
|
4170
5093
|
default:
|
|
4171
5094
|
return err3(`Unknown retrieve action: ${action}`);
|
|
@@ -4421,19 +5344,48 @@ async function handleOntology(client2, args) {
|
|
|
4421
5344
|
return err3(`Unknown ontology action: ${action}`);
|
|
4422
5345
|
}
|
|
4423
5346
|
}
|
|
4424
|
-
var TOOLS, INVOCATION_CONTEXT_SCHEMA;
|
|
5347
|
+
var import_node_crypto8, RESOLUTION_PROPERTIES, TOOLS, INVOCATION_CONTEXT_SCHEMA;
|
|
4425
5348
|
var init_tools = __esm({
|
|
4426
5349
|
"src/tools.ts"() {
|
|
4427
5350
|
"use strict";
|
|
5351
|
+
import_node_crypto8 = require("crypto");
|
|
4428
5352
|
init_error_detail();
|
|
4429
5353
|
init_code_tool();
|
|
4430
5354
|
init_codegraph();
|
|
4431
5355
|
init_sync_tool();
|
|
5356
|
+
init_memory_tool();
|
|
4432
5357
|
init_render();
|
|
5358
|
+
RESOLUTION_PROPERTIES = {
|
|
5359
|
+
idempotency_key: {
|
|
5360
|
+
type: "string",
|
|
5361
|
+
description: "Stable retry key; a mismatched replay is rejected"
|
|
5362
|
+
},
|
|
5363
|
+
reuse_uid: {
|
|
5364
|
+
type: "string",
|
|
5365
|
+
description: "Reuse this live same-scope node after a duplicate response"
|
|
5366
|
+
},
|
|
5367
|
+
supersedes_uid: {
|
|
5368
|
+
type: "string",
|
|
5369
|
+
description: "Create and mark this same-scope node as superseded"
|
|
5370
|
+
},
|
|
5371
|
+
contradicts_uid: {
|
|
5372
|
+
type: "string",
|
|
5373
|
+
description: "Create and mark this same-scope node as contradicted"
|
|
5374
|
+
},
|
|
5375
|
+
confirm_new: {
|
|
5376
|
+
type: "boolean",
|
|
5377
|
+
description: "Confirm that the new fact is genuinely distinct"
|
|
5378
|
+
},
|
|
5379
|
+
on_near_duplicate: {
|
|
5380
|
+
type: "string",
|
|
5381
|
+
enum: ["create"],
|
|
5382
|
+
description: "Alias for confirm_new on a near-duplicate response"
|
|
5383
|
+
}
|
|
5384
|
+
};
|
|
4433
5385
|
TOOLS = [
|
|
4434
5386
|
{
|
|
4435
5387
|
name: "mindgraph_capture",
|
|
4436
|
-
description: "Capture knowledge into the graph. Use 'entity' for people/orgs/places/events/nations/concepts (auto-deduplicates \u2014 safe to call even if already exists), 'observation' for factual statements, 'source' for documents/URLs, 'snippet' for quotes from a source, and 'concept' for abstract ideas. Use 'journal' for quick personal notes
|
|
5388
|
+
description: "Capture knowledge into the graph. Use 'entity' for people/orgs/places/events/nations/concepts (auto-deduplicates \u2014 safe to call even if already exists), 'observation' for factual statements, 'source' for documents/URLs, 'snippet' for quotes from a source, and 'concept' for abstract ideas. Use 'journal' for quick personal notes, 'lesson' for a durable learning, and 'skill' for a reusable procedure authored from completed work. Skill capture always creates a candidate requiring admin review; it never publishes. Skill names must be 1-64 lowercase a-z, digits, or hyphens, with no leading/trailing/consecutive hyphens and no 'anthropic' or 'claude'. Provide at least one provenance field and write instructions, not a transcript dump.",
|
|
4437
5389
|
inputSchema: {
|
|
4438
5390
|
type: "object",
|
|
4439
5391
|
properties: {
|
|
@@ -4446,7 +5398,8 @@ var init_tools = __esm({
|
|
|
4446
5398
|
"snippet",
|
|
4447
5399
|
"concept",
|
|
4448
5400
|
"journal",
|
|
4449
|
-
"lesson"
|
|
5401
|
+
"lesson",
|
|
5402
|
+
"skill"
|
|
4450
5403
|
],
|
|
4451
5404
|
description: "Type of knowledge to capture"
|
|
4452
5405
|
},
|
|
@@ -4460,11 +5413,30 @@ var init_tools = __esm({
|
|
|
4460
5413
|
},
|
|
4461
5414
|
content: {
|
|
4462
5415
|
type: "string",
|
|
4463
|
-
|
|
5416
|
+
maxLength: 65536,
|
|
5417
|
+
description: "Journal body (action=journal) or complete SKILL.md instructions, at most 64 KiB UTF-8 (action=skill)"
|
|
5418
|
+
},
|
|
5419
|
+
name: {
|
|
5420
|
+
type: "string",
|
|
5421
|
+
minLength: 1,
|
|
5422
|
+
maxLength: 64,
|
|
5423
|
+
pattern: "^(?!.*(?:anthropic|claude))(?!-)(?!.*--)[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$",
|
|
5424
|
+
description: "Stable skill identifier using lowercase a-z, digits, and hyphens (for action=skill)"
|
|
5425
|
+
},
|
|
5426
|
+
description: {
|
|
5427
|
+
type: "string",
|
|
5428
|
+
minLength: 1,
|
|
5429
|
+
maxLength: 1024,
|
|
5430
|
+
description: "When the skill should be used; XML tags are rejected (for action=skill)"
|
|
5431
|
+
},
|
|
5432
|
+
license: {
|
|
5433
|
+
type: "string",
|
|
5434
|
+
description: "Optional skill license (for action=skill)"
|
|
4464
5435
|
},
|
|
4465
5436
|
session_uid: {
|
|
4466
5437
|
type: "string",
|
|
4467
|
-
|
|
5438
|
+
minLength: 1,
|
|
5439
|
+
description: "Session UID that produced a lesson or skill"
|
|
4468
5440
|
},
|
|
4469
5441
|
work_uid: {
|
|
4470
5442
|
type: "string",
|
|
@@ -4474,18 +5446,12 @@ var init_tools = __esm({
|
|
|
4474
5446
|
type: "string",
|
|
4475
5447
|
description: "Material Execution that produced the lesson"
|
|
4476
5448
|
},
|
|
4477
|
-
|
|
4478
|
-
type: "string",
|
|
4479
|
-
description: "Stable retry key for an intentional capture"
|
|
4480
|
-
},
|
|
4481
|
-
supersedes_uid: {
|
|
4482
|
-
type: "string",
|
|
4483
|
-
description: "Earlier capture corrected or superseded by this one"
|
|
4484
|
-
},
|
|
5449
|
+
...RESOLUTION_PROPERTIES,
|
|
4485
5450
|
summarizes_uids: {
|
|
4486
5451
|
type: "array",
|
|
4487
5452
|
items: { type: "string" },
|
|
4488
|
-
|
|
5453
|
+
minItems: 1,
|
|
5454
|
+
description: "Source node UIDs the lesson or skill was learned from"
|
|
4489
5455
|
},
|
|
4490
5456
|
entity_type: {
|
|
4491
5457
|
type: "string",
|
|
@@ -4552,7 +5518,24 @@ var init_tools = __esm({
|
|
|
4552
5518
|
description: "Agent identity"
|
|
4553
5519
|
}
|
|
4554
5520
|
},
|
|
4555
|
-
required: ["action", "label"]
|
|
5521
|
+
required: ["action", "label"],
|
|
5522
|
+
allOf: [
|
|
5523
|
+
{
|
|
5524
|
+
if: {
|
|
5525
|
+
properties: { action: { const: "skill" } },
|
|
5526
|
+
required: ["action"]
|
|
5527
|
+
},
|
|
5528
|
+
then: {
|
|
5529
|
+
required: ["name", "description", "content"],
|
|
5530
|
+
anyOf: [
|
|
5531
|
+
{ required: ["session_uid"] },
|
|
5532
|
+
{ required: ["work_uid"] },
|
|
5533
|
+
{ required: ["execution_uid"] },
|
|
5534
|
+
{ required: ["summarizes_uids"] }
|
|
5535
|
+
]
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
]
|
|
4556
5539
|
}
|
|
4557
5540
|
},
|
|
4558
5541
|
{
|
|
@@ -4561,6 +5544,7 @@ var init_tools = __esm({
|
|
|
4561
5544
|
inputSchema: {
|
|
4562
5545
|
type: "object",
|
|
4563
5546
|
properties: {
|
|
5547
|
+
...RESOLUTION_PROPERTIES,
|
|
4564
5548
|
action: {
|
|
4565
5549
|
type: "string",
|
|
4566
5550
|
enum: [
|
|
@@ -4655,6 +5639,7 @@ var init_tools = __esm({
|
|
|
4655
5639
|
inputSchema: {
|
|
4656
5640
|
type: "object",
|
|
4657
5641
|
properties: {
|
|
5642
|
+
...RESOLUTION_PROPERTIES,
|
|
4658
5643
|
action: {
|
|
4659
5644
|
type: "string",
|
|
4660
5645
|
enum: [
|
|
@@ -5013,6 +5998,12 @@ var init_tools = __esm({
|
|
|
5013
5998
|
enum: ["text", "json"],
|
|
5014
5999
|
description: "Output format (default 'text'): a compact rendered context block \u2014 labels, uids, summaries, status tags, relationships. Pass 'json' for the raw server response (full props, source-chunk offsets, retrieval metadata)."
|
|
5015
6000
|
},
|
|
6001
|
+
max_output_chars: {
|
|
6002
|
+
type: "integer",
|
|
6003
|
+
minimum: 512,
|
|
6004
|
+
maximum: 24e3,
|
|
6005
|
+
description: "Hard character budget for rendered text output (512-24000; default 24000). Normal per-field clipping still applies. Whole evidence items are then kept or omitted rather than cut to satisfy this budget; the final marker reports omissions. Ignored when format='json'."
|
|
6006
|
+
},
|
|
5016
6007
|
query: {
|
|
5017
6008
|
type: "string",
|
|
5018
6009
|
description: "Search query \u2014 for 'text'/'context': 1-3 BM25 keywords, drop filler words. For 'semantic'/'hybrid': natural language is OK since it uses vector similarity."
|
|
@@ -5138,6 +6129,42 @@ var init_tools = __esm({
|
|
|
5138
6129
|
required: ["action"]
|
|
5139
6130
|
}
|
|
5140
6131
|
},
|
|
6132
|
+
{
|
|
6133
|
+
name: "mindgraph_series_query",
|
|
6134
|
+
description: "Read dense numeric time series without placing raw points in general retrieval context. Use list_for_entity to discover series, latest for cached statistics, window for [from,to) keyset pages, and aggregate for bounded calendar buckets.",
|
|
6135
|
+
inputSchema: {
|
|
6136
|
+
type: "object",
|
|
6137
|
+
additionalProperties: false,
|
|
6138
|
+
properties: {
|
|
6139
|
+
action: {
|
|
6140
|
+
type: "string",
|
|
6141
|
+
enum: ["list_for_entity", "latest", "window", "aggregate"]
|
|
6142
|
+
},
|
|
6143
|
+
entity_uid: { type: "string" },
|
|
6144
|
+
series_uid: { type: "string" },
|
|
6145
|
+
from: { type: "number" },
|
|
6146
|
+
to: { type: "number" },
|
|
6147
|
+
cursor: { type: "number" },
|
|
6148
|
+
limit: { type: "number", minimum: 1, maximum: 1e4 },
|
|
6149
|
+
bucket: {
|
|
6150
|
+
type: "string",
|
|
6151
|
+
enum: ["year", "quarter", "month", "week", "day", "hour"]
|
|
6152
|
+
},
|
|
6153
|
+
agg: {
|
|
6154
|
+
type: "string",
|
|
6155
|
+
enum: ["sum", "mean", "min", "max", "count", "std_dev", "last"]
|
|
6156
|
+
},
|
|
6157
|
+
fill: { type: "string", enum: ["none", "null", "prev"] }
|
|
6158
|
+
},
|
|
6159
|
+
required: ["action"]
|
|
6160
|
+
},
|
|
6161
|
+
annotations: {
|
|
6162
|
+
readOnlyHint: true,
|
|
6163
|
+
destructiveHint: false,
|
|
6164
|
+
idempotentHint: true,
|
|
6165
|
+
openWorldHint: false
|
|
6166
|
+
}
|
|
6167
|
+
},
|
|
5141
6168
|
{
|
|
5142
6169
|
name: "mindgraph_ingest",
|
|
5143
6170
|
description: "Ingest long-form content (articles, transcripts, documents, meeting notes) into the knowledge graph. The pipeline automatically chunks the text, extracts entities/claims/relationships via LLM, and deduplicates against existing nodes. Use 'chunk' for a single passage (synchronous), 'document' for full documents (async \u2014 returns a job ID), 'session' for conversation transcripts. Use 'job_status' to check progress. For short individual facts, use the specific cognitive tools instead.",
|
|
@@ -5403,6 +6430,7 @@ var init_tools = __esm({
|
|
|
5403
6430
|
required: ["action"]
|
|
5404
6431
|
}
|
|
5405
6432
|
},
|
|
6433
|
+
MEMORY_TOOL,
|
|
5406
6434
|
SYNC_TOOL,
|
|
5407
6435
|
CODE_TOOL
|
|
5408
6436
|
];
|
|
@@ -5423,7 +6451,7 @@ var init_tools = __esm({
|
|
|
5423
6451
|
}
|
|
5424
6452
|
};
|
|
5425
6453
|
for (const tool of TOOLS) {
|
|
5426
|
-
if (tool.name === "mindgraph_retrieve") continue;
|
|
6454
|
+
if (tool.name === "mindgraph_retrieve" || tool.name === "mindgraph_memory") continue;
|
|
5427
6455
|
const schema = tool.inputSchema;
|
|
5428
6456
|
if (schema.properties && !schema.properties.invocation_context) {
|
|
5429
6457
|
schema.properties.invocation_context = INVOCATION_CONTEXT_SCHEMA;
|
|
@@ -5477,8 +6505,8 @@ async function getGeneratedTools(client2) {
|
|
|
5477
6505
|
cache = { tools, byName, fetchedAt: Date.now() };
|
|
5478
6506
|
retryNotBefore = 0;
|
|
5479
6507
|
return { tools, byName };
|
|
5480
|
-
} catch (
|
|
5481
|
-
if (
|
|
6508
|
+
} catch (error2) {
|
|
6509
|
+
if (error2 instanceof DuplicateGeneratedToolError) throw error2;
|
|
5482
6510
|
retryNotBefore = Date.now() + FAILURE_COOLDOWN_MS;
|
|
5483
6511
|
return cache ? { tools: cache.tools, byName: cache.byName } : EMPTY;
|
|
5484
6512
|
}
|
|
@@ -5591,8 +6619,8 @@ var init_generated_tools = __esm({
|
|
|
5591
6619
|
});
|
|
5592
6620
|
rendered = (format, raw, render) => {
|
|
5593
6621
|
if (format === "json") return ok4(raw);
|
|
5594
|
-
const
|
|
5595
|
-
return
|
|
6622
|
+
const text2 = render(raw);
|
|
6623
|
+
return text2 !== void 0 ? { content: [{ type: "text", text: text2 }] } : ok4(raw);
|
|
5596
6624
|
};
|
|
5597
6625
|
err4 = (message) => ({
|
|
5598
6626
|
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
@@ -5624,7 +6652,7 @@ function isTransientStatus(status) {
|
|
|
5624
6652
|
return status === 408 || status === 425 || status === 429 || status >= 500;
|
|
5625
6653
|
}
|
|
5626
6654
|
function sleep(ms) {
|
|
5627
|
-
return new Promise((
|
|
6655
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
5628
6656
|
}
|
|
5629
6657
|
function headers(apiKey, orgId) {
|
|
5630
6658
|
const value = {
|
|
@@ -5641,7 +6669,9 @@ function unsupportedResponse(status, body) {
|
|
|
5641
6669
|
return lower.includes("unknown governance action") || lower.includes("unknown action") || lower.includes("invalid_governance_action");
|
|
5642
6670
|
}
|
|
5643
6671
|
function toolMutability(toolName, action) {
|
|
6672
|
+
if (toolName === "mindgraph_memory") return "read";
|
|
5644
6673
|
if (toolName === "mindgraph_retrieve") return "read";
|
|
6674
|
+
if (toolName === "mindgraph_series_query") return "read";
|
|
5645
6675
|
if (toolName === "mindgraph_plan") {
|
|
5646
6676
|
return action && PLAN_READ_ACTIONS.has(action) ? "read" : "write";
|
|
5647
6677
|
}
|
|
@@ -5723,8 +6753,8 @@ async function checkMcpGovernance(toolName, args, config) {
|
|
|
5723
6753
|
response = candidate;
|
|
5724
6754
|
body = candidateBody;
|
|
5725
6755
|
break;
|
|
5726
|
-
} catch (
|
|
5727
|
-
lastFailure =
|
|
6756
|
+
} catch (error2) {
|
|
6757
|
+
lastFailure = error2 instanceof Error ? error2.message : String(error2);
|
|
5728
6758
|
if (attempt < retryDelays.length) {
|
|
5729
6759
|
await sleep(retryDelays[attempt]);
|
|
5730
6760
|
continue;
|
|
@@ -5826,6 +6856,8 @@ var init_governance = __esm({
|
|
|
5826
6856
|
UID_FIELDS = [
|
|
5827
6857
|
"uid",
|
|
5828
6858
|
"source_uid",
|
|
6859
|
+
"entity_uid",
|
|
6860
|
+
"series_uid",
|
|
5829
6861
|
"session_uid",
|
|
5830
6862
|
"work_uid",
|
|
5831
6863
|
"goal_uid",
|
|
@@ -5867,8 +6899,10 @@ function json(uri, data) {
|
|
|
5867
6899
|
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(data) }]
|
|
5868
6900
|
};
|
|
5869
6901
|
}
|
|
5870
|
-
async function readResource(client2, uri) {
|
|
6902
|
+
async function readResource(client2, uri, memoryConfig = {}) {
|
|
5871
6903
|
switch (uri) {
|
|
6904
|
+
case "mindgraph://memory/status":
|
|
6905
|
+
return json(uri, memoryStatus(memoryConfig));
|
|
5872
6906
|
case "mindgraph://stats":
|
|
5873
6907
|
return json(uri, await client2.stats());
|
|
5874
6908
|
case "mindgraph://goals":
|
|
@@ -5900,23 +6934,32 @@ async function readResource(client2, uri) {
|
|
|
5900
6934
|
}
|
|
5901
6935
|
throw new Error(`Unknown resource: ${uri}`);
|
|
5902
6936
|
}
|
|
5903
|
-
async function readGovernedResource(client2, uri, governanceConfig) {
|
|
6937
|
+
async function readGovernedResource(client2, uri, governanceConfig, memoryConfig = {}) {
|
|
6938
|
+
const toolName = uri === "mindgraph://memory/status" ? "mindgraph_memory" : RESOURCE_GOVERNANCE_TOOL;
|
|
6939
|
+
const toolArgs = uri === "mindgraph://memory/status" ? { action: "status" } : {};
|
|
5904
6940
|
const governance = await checkMcpGovernance(
|
|
5905
|
-
|
|
5906
|
-
|
|
6941
|
+
toolName,
|
|
6942
|
+
toolArgs,
|
|
5907
6943
|
{ ...governanceConfig, context: { resource_uri: uri } }
|
|
5908
6944
|
);
|
|
5909
6945
|
if (!governance.allowed) {
|
|
5910
6946
|
throw new Error(governance.message);
|
|
5911
6947
|
}
|
|
5912
|
-
return readResource(client2, uri);
|
|
6948
|
+
return readResource(client2, uri, memoryConfig);
|
|
5913
6949
|
}
|
|
5914
6950
|
var RESOURCES, RESOURCE_TEMPLATES, RESOURCE_GOVERNANCE_TOOL;
|
|
5915
6951
|
var init_resources = __esm({
|
|
5916
6952
|
"src/resources.ts"() {
|
|
5917
6953
|
"use strict";
|
|
5918
6954
|
init_governance();
|
|
6955
|
+
init_memory_tool();
|
|
5919
6956
|
RESOURCES = [
|
|
6957
|
+
{
|
|
6958
|
+
uri: "mindgraph://memory/status",
|
|
6959
|
+
name: "Memory Integration Status",
|
|
6960
|
+
description: "Portable memory profile, adapter state, supported capabilities, and explicit limitations",
|
|
6961
|
+
mimeType: "application/json"
|
|
6962
|
+
},
|
|
5920
6963
|
{
|
|
5921
6964
|
uri: "mindgraph://stats",
|
|
5922
6965
|
name: "Graph Statistics",
|
|
@@ -5978,10 +7021,10 @@ async function main() {
|
|
|
5978
7021
|
const transport = new import_stdio.StdioServerTransport();
|
|
5979
7022
|
await server.connect(transport);
|
|
5980
7023
|
console.error(
|
|
5981
|
-
`MindGraph MCP server v${"0.
|
|
7024
|
+
`MindGraph MCP server v${"0.20.0"} running on stdio (${BASE_URL})`
|
|
5982
7025
|
);
|
|
5983
7026
|
}
|
|
5984
|
-
var import_server, import_stdio, import_types, import_node_path6, API_KEY, BASE_URL, AGENT_ID, ORG_ID, PROFILE, HARNESS, client, INSTRUCTIONS, RUNNER_REFRESH, HOOK_SKEW_NOTE, server, GOVERNANCE_CONFIG, PROMPTS;
|
|
7027
|
+
var import_server, import_stdio, import_types, import_node_path6, API_KEY, BASE_URL, AGENT_ID, ORG_ID, PROFILE, HARNESS, client, INSTRUCTIONS, RUNNER_REFRESH, HOOK_SKEW_NOTE, server, GOVERNANCE_CONFIG, MEMORY_RUNTIME_CONFIG, PROMPTS;
|
|
5985
7028
|
var init_src = __esm({
|
|
5986
7029
|
"src/index.ts"() {
|
|
5987
7030
|
"use strict";
|
|
@@ -6019,56 +7062,29 @@ var init_src = __esm({
|
|
|
6019
7062
|
});
|
|
6020
7063
|
INSTRUCTIONS = `You have access to a persistent knowledge graph via MindGraph, plus any ontology tools generated by the active schema.
|
|
6021
7064
|
|
|
6022
|
-
##
|
|
6023
|
-
|
|
6024
|
-
When the user asks about something, your FIRST and ONLY action should be a SINGLE \`mindgraph_retrieve\` call with action "context" and keyword terms from their message. Nothing else first.
|
|
7065
|
+
## Default memory workflow: orient \u2192 answer \u2192 preserve
|
|
6025
7066
|
|
|
6026
|
-
**
|
|
6027
|
-
- Do NOT retrieve active_goals, open_questions, contradictions, or weak_claims
|
|
6028
|
-
- Do NOT follow a "session lifecycle" or "warm-up" sequence
|
|
6029
|
-
- Do NOT make multiple serial retrieve calls when one "context" call covers it
|
|
7067
|
+
**Orient only when prior durable state may matter.** Self-contained questions do not require a memory call or a warm-up sequence.
|
|
6030
7068
|
|
|
6031
|
-
|
|
7069
|
+
- Resuming work, asking what was in progress, or continuing a task: call \`mindgraph_memory\` once with action \`context\` and mode \`resume\` or \`continuity\`. This uses authoritative, task-first, lease-aware work state.
|
|
7070
|
+
- Asking about a topic that may depend on prior knowledge: call \`mindgraph_memory\` once with action \`context\`, mode \`topic\`, and 1\u20133 discriminating keywords.
|
|
7071
|
+
- Asking about integration capability or automatic behavior: call \`mindgraph_memory\` with action \`status\`.
|
|
7072
|
+
- Use the comprehensive read tools below only when the high-level result is insufficient or the user asks for a specific graph operation.
|
|
6032
7073
|
|
|
6033
|
-
**
|
|
6034
|
-
\u2713 CORRECT: \`mindgraph_retrieve({action: "context", query: "Indian philosophy Taoism"})\`
|
|
6035
|
-
\u2717 WRONG: First retrieve active_goals, then open_questions, then search
|
|
6036
|
-
\u2717 WRONG: \`mindgraph_retrieve({action: "context", query: "Tell me about Indian philosophy and Taoism"})\` \u2014 natural language won't match BM25
|
|
7074
|
+
**Answer from the bounded result.** Treat superseded, refuted, or out-of-validity items as warnings, not current facts. Retrieval proves only that the server returned context; it does not prove that a host automatically injected or used it.
|
|
6037
7075
|
|
|
6038
|
-
|
|
7076
|
+
**Preserve selectively after material durable change.** Use the existing typed write tool for a fact, correction, decision, work-state transition, or reusable lesson. Do not write ephemeral conversation, routine mechanics, unsupported guesses, or information already represented accurately. An explicit no-write is the correct outcome when nothing durable changed.
|
|
6039
7077
|
|
|
6040
|
-
|
|
6041
|
-
Use \`action: "context"\` with keywords. By default this returns only graph nodes \u2014 structured knowledge with labels, summaries, types, confidence scores, and edges. This is usually sufficient and keeps results compact.
|
|
7078
|
+
## Topic retrieval details
|
|
6042
7079
|
|
|
6043
|
-
|
|
6044
|
-
If you need the original source text behind a node (for verbatim quotes, citations, or when graph summaries lack detail), use \`action: "context"\` with \`include_chunks: true\`. This adds full document chunks (400-800 words each) to the response.
|
|
7080
|
+
Topic context is graph-only by default. Set \`include_chunks: true\` only for source text needed for quotes, citations, or missing detail. Query with a few proper nouns or technical terms rather than a full sentence. If keyword retrieval fails, use \`mindgraph_retrieve\` with \`semantic\` or \`hybrid\`; use \`document_index\` to inspect ingested sources.
|
|
6045
7081
|
|
|
6046
|
-
|
|
6047
|
-
- \`action: "document_index"\` \u2014 list all ingested documents (titles, dates, UIDs) to see what's in the graph
|
|
7082
|
+
## Comprehensive read tools
|
|
6048
7083
|
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
**Keyword examples:**
|
|
6054
|
-
- "What is Kissinger's view on NATO?" \u2192 query: \`"Kissinger NATO"\`
|
|
6055
|
-
- "Tell me about async in Rust" \u2192 query: \`"Rust async runtime"\`
|
|
6056
|
-
- "Implications of carbon tax?" \u2192 query: \`"carbon tax"\`
|
|
6057
|
-
|
|
6058
|
-
If keyword search returns no results, escalate:
|
|
6059
|
-
1. Try synonyms (e.g. \`"global warming"\` instead of \`"climate change"\`)
|
|
6060
|
-
2. Use \`action: "semantic"\` \u2014 vector similarity, natural language OK (e.g. \`"ways to manage stress"\` finds "mental health" nodes)
|
|
6061
|
-
3. Use \`action: "hybrid"\` \u2014 combines BM25 + semantic for best coverage
|
|
6062
|
-
|
|
6063
|
-
## When to READ
|
|
6064
|
-
|
|
6065
|
-
- User asks about topic X \u2192 \`mindgraph_retrieve\` action "context", query "X keywords"
|
|
6066
|
-
- User asks "what documents are in my graph?" \u2192 action "document_index"
|
|
6067
|
-
- User explicitly asks "what are my goals?" \u2192 action "active_goals"
|
|
6068
|
-
- User explicitly asks about open questions \u2192 action "open_questions"
|
|
6069
|
-
- User asks for advice or a recommendation ("suggest a hotel", "what should I read?") \u2192 action "preferences", query "topic keywords" \u2014 tailor the answer to what they like
|
|
6070
|
-
- Explore around a known node \u2192 action "neighborhood" with start_uid
|
|
6071
|
-
- Find connections between nodes \u2192 action "path" with start_uid + end_uid
|
|
7084
|
+
- Explicit graph inventory or state questions \u2192 the matching \`mindgraph_retrieve\` action such as \`document_index\`, \`active_goals\`, or \`open_questions\`
|
|
7085
|
+
- Advice or recommendations \u2192 \`mindgraph_retrieve\` action \`preferences\`, with topic keywords
|
|
7086
|
+
- Explore around a known node \u2192 \`mindgraph_retrieve\` action \`neighborhood\` with start_uid
|
|
7087
|
+
- Find connections between nodes \u2192 \`mindgraph_retrieve\` action \`path\` with start_uid + end_uid
|
|
6072
7088
|
|
|
6073
7089
|
## When to WRITE
|
|
6074
7090
|
|
|
@@ -6103,13 +7119,14 @@ Capture knowledge when the user shares something worth remembering:
|
|
|
6103
7119
|
|
|
6104
7120
|
## Coding profile
|
|
6105
7121
|
|
|
6106
|
-
**
|
|
6107
|
-
- "What was I working on?" / current work state / resuming anything: call \`
|
|
7122
|
+
**Coding recall order:**
|
|
7123
|
+
- "What was I working on?" / current work state / resuming anything: call \`mindgraph_memory\` action \`context\`, mode \`resume\` FIRST. It delegates to structural \`resume_work\`; never answer work state from keyword search ("recent work" famously retrieves people named Work).
|
|
6108
7124
|
- Knowledge about specific code (a function, file, or route): \`mindgraph_code\` action \`recall\` \u2014 anchor-based, not keyword-based.
|
|
6109
7125
|
- Broad knowledge searches: \`mindgraph_retrieve\` as usual, but when searching for work artifacts pass node_types filters (Task, Lesson, RiskAssessment, Claim) to keep personal-graph content out of engineering answers.
|
|
6110
7126
|
|
|
6111
7127
|
- Use mindgraph_sync for explicit, one-way project memory-file imports. It discovers and hashes files, but you deliberately map durable assertions with record; it never writes graph state back to files.
|
|
6112
7128
|
- Capture corrections, decisions with rationale, fixes that survived meaningful failed attempts, durable invariants/preferences, and blockers with evidence.
|
|
7129
|
+
- If a write returns \`near_duplicate\`, choose \`reuse_uid\` or \`supersedes_uid\`; use \`confirm_new\` only for genuinely distinct facts.
|
|
6113
7130
|
- Do not capture routine mechanics, transient command output, facts cheaply derivable from code, unsupported speculation as high-confidence lessons, or completion without evidence.
|
|
6114
7131
|
- Adapter context is owned by ${HARNESS}; omit invocation_context because hooks replace it.`;
|
|
6115
7132
|
} else {
|
|
@@ -6119,7 +7136,7 @@ Capture knowledge when the user shares something worth remembering:
|
|
|
6119
7136
|
}
|
|
6120
7137
|
RUNNER_REFRESH = (() => {
|
|
6121
7138
|
try {
|
|
6122
|
-
return refreshHookRunner("0.
|
|
7139
|
+
return refreshHookRunner("0.20.0", import_node_path6.default.join(__dirname, "cli.js"));
|
|
6123
7140
|
} catch {
|
|
6124
7141
|
return void 0;
|
|
6125
7142
|
}
|
|
@@ -6133,12 +7150,12 @@ Capture knowledge when the user shares something worth remembering:
|
|
|
6133
7150
|
}
|
|
6134
7151
|
})();
|
|
6135
7152
|
console.error(
|
|
6136
|
-
`[mindgraph-mcp] refreshed pinned hook runner ${RUNNER_REFRESH.runnerVersion ? `v${RUNNER_REFRESH.runnerVersion}` : "(pre-sidecar)"} \u2192 v${"0.
|
|
7153
|
+
`[mindgraph-mcp] refreshed pinned hook runner ${RUNNER_REFRESH.runnerVersion ? `v${RUNNER_REFRESH.runnerVersion}` : "(pre-sidecar)"} \u2192 v${"0.20.0"}` + (refreshedSettings.length > 0 ? `; refreshed owned hook entries in ${refreshedSettings.join(", ")}` : "")
|
|
6137
7154
|
);
|
|
6138
7155
|
}
|
|
6139
7156
|
HOOK_SKEW_NOTE = (() => {
|
|
6140
7157
|
try {
|
|
6141
|
-
return versionSkewNote("0.
|
|
7158
|
+
return versionSkewNote("0.20.0");
|
|
6142
7159
|
} catch {
|
|
6143
7160
|
return void 0;
|
|
6144
7161
|
}
|
|
@@ -6152,7 +7169,7 @@ ${HOOK_SKEW_NOTE}`;
|
|
|
6152
7169
|
server = new import_server.Server(
|
|
6153
7170
|
{
|
|
6154
7171
|
name: "mindgraph",
|
|
6155
|
-
version: "0.
|
|
7172
|
+
version: "0.20.0"
|
|
6156
7173
|
},
|
|
6157
7174
|
{
|
|
6158
7175
|
capabilities: {
|
|
@@ -6176,6 +7193,14 @@ ${HOOK_SKEW_NOTE}`;
|
|
|
6176
7193
|
return process.env.MINDGRAPH_GOVERNANCE === "off";
|
|
6177
7194
|
}
|
|
6178
7195
|
};
|
|
7196
|
+
MEMORY_RUNTIME_CONFIG = {
|
|
7197
|
+
serverVersion: "0.20.0",
|
|
7198
|
+
baseUrl: BASE_URL,
|
|
7199
|
+
profile: PROFILE,
|
|
7200
|
+
harness: HARNESS,
|
|
7201
|
+
orgId: ORG_ID,
|
|
7202
|
+
agentId: AGENT_ID
|
|
7203
|
+
};
|
|
6179
7204
|
server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
|
|
6180
7205
|
const { name, arguments: args } = request.params;
|
|
6181
7206
|
const rawArgs = { ...args || {} };
|
|
@@ -6195,7 +7220,7 @@ ${HOOK_SKEW_NOTE}`;
|
|
|
6195
7220
|
if (desc) {
|
|
6196
7221
|
return handleGeneratedTool(client, desc, rawArgs);
|
|
6197
7222
|
}
|
|
6198
|
-
return handleTool(client, name, toolArgs);
|
|
7223
|
+
return handleTool(client, name, toolArgs, MEMORY_RUNTIME_CONFIG);
|
|
6199
7224
|
});
|
|
6200
7225
|
PROMPTS = [
|
|
6201
7226
|
{
|
|
@@ -6360,18 +7385,23 @@ ${conversation}`
|
|
|
6360
7385
|
return { resourceTemplates: RESOURCE_TEMPLATES };
|
|
6361
7386
|
});
|
|
6362
7387
|
server.setRequestHandler(import_types.ReadResourceRequestSchema, async (request) => {
|
|
6363
|
-
return readGovernedResource(
|
|
7388
|
+
return readGovernedResource(
|
|
7389
|
+
client,
|
|
7390
|
+
request.params.uri,
|
|
7391
|
+
GOVERNANCE_CONFIG,
|
|
7392
|
+
MEMORY_RUNTIME_CONFIG
|
|
7393
|
+
);
|
|
6364
7394
|
});
|
|
6365
|
-
main().catch((
|
|
6366
|
-
console.error("Fatal error:",
|
|
7395
|
+
main().catch((error2) => {
|
|
7396
|
+
console.error("Fatal error:", error2);
|
|
6367
7397
|
process.exit(1);
|
|
6368
7398
|
});
|
|
6369
7399
|
}
|
|
6370
7400
|
});
|
|
6371
7401
|
|
|
6372
7402
|
// src/cli.ts
|
|
6373
|
-
var
|
|
6374
|
-
var
|
|
7403
|
+
var fs6 = __toESM(require("fs"));
|
|
7404
|
+
var path7 = __toESM(require("path"));
|
|
6375
7405
|
var os4 = __toESM(require("os"));
|
|
6376
7406
|
var crypto5 = __toESM(require("crypto"));
|
|
6377
7407
|
var import_child_process = require("child_process");
|
|
@@ -6389,6 +7419,8 @@ function parseArgs(argv) {
|
|
|
6389
7419
|
let harness;
|
|
6390
7420
|
let hooks = false;
|
|
6391
7421
|
let agentId = process.env.MINDGRAPH_AGENT_ID;
|
|
7422
|
+
const positionals = [];
|
|
7423
|
+
let dir;
|
|
6392
7424
|
for (let i = 0; i < args.length; i++) {
|
|
6393
7425
|
const arg = args[i];
|
|
6394
7426
|
switch (arg) {
|
|
@@ -6409,6 +7441,9 @@ function parseArgs(argv) {
|
|
|
6409
7441
|
case "--project-dir":
|
|
6410
7442
|
projectDir = args[++i];
|
|
6411
7443
|
break;
|
|
7444
|
+
case "--dir":
|
|
7445
|
+
dir = args[++i];
|
|
7446
|
+
break;
|
|
6412
7447
|
case "--harness":
|
|
6413
7448
|
harness = args[++i];
|
|
6414
7449
|
break;
|
|
@@ -6427,12 +7462,27 @@ function parseArgs(argv) {
|
|
|
6427
7462
|
break;
|
|
6428
7463
|
default:
|
|
6429
7464
|
if (!arg.startsWith("-")) {
|
|
6430
|
-
command2
|
|
7465
|
+
if (command2 === "serve") {
|
|
7466
|
+
command2 = arg;
|
|
7467
|
+
} else {
|
|
7468
|
+
positionals.push(arg);
|
|
7469
|
+
}
|
|
6431
7470
|
}
|
|
6432
7471
|
break;
|
|
6433
7472
|
}
|
|
6434
7473
|
}
|
|
6435
|
-
return {
|
|
7474
|
+
return {
|
|
7475
|
+
command: command2,
|
|
7476
|
+
apiKey,
|
|
7477
|
+
baseUrl,
|
|
7478
|
+
scope,
|
|
7479
|
+
projectDir,
|
|
7480
|
+
harness,
|
|
7481
|
+
hooks,
|
|
7482
|
+
agentId,
|
|
7483
|
+
positionals,
|
|
7484
|
+
dir
|
|
7485
|
+
};
|
|
6436
7486
|
}
|
|
6437
7487
|
function mcpAddFailureOutput(e) {
|
|
6438
7488
|
const err5 = e;
|
|
@@ -6446,8 +7496,375 @@ function classifyMcpAddFailure(e) {
|
|
|
6446
7496
|
return "other";
|
|
6447
7497
|
}
|
|
6448
7498
|
|
|
7499
|
+
// src/skills-cli.ts
|
|
7500
|
+
var import_node_crypto2 = require("crypto");
|
|
7501
|
+
var fs2 = __toESM(require("fs"));
|
|
7502
|
+
var path2 = __toESM(require("path"));
|
|
7503
|
+
function httpSkillsApi(baseUrl, apiKey, agentId) {
|
|
7504
|
+
const endpoint = `${baseUrl.replace(/\/$/, "")}/action/skill`;
|
|
7505
|
+
const call = async (body) => {
|
|
7506
|
+
const response = await fetch(endpoint, {
|
|
7507
|
+
method: "POST",
|
|
7508
|
+
headers: {
|
|
7509
|
+
"content-type": "application/json",
|
|
7510
|
+
authorization: `Bearer ${apiKey}`
|
|
7511
|
+
},
|
|
7512
|
+
body: JSON.stringify(
|
|
7513
|
+
agentId ? { agent_id: agentId, ...body } : body
|
|
7514
|
+
)
|
|
7515
|
+
});
|
|
7516
|
+
let parsed = {};
|
|
7517
|
+
try {
|
|
7518
|
+
parsed = await response.json();
|
|
7519
|
+
} catch {
|
|
7520
|
+
parsed = { error: `non-JSON response (HTTP ${response.status})` };
|
|
7521
|
+
}
|
|
7522
|
+
return { status: response.status, body: parsed };
|
|
7523
|
+
};
|
|
7524
|
+
return {
|
|
7525
|
+
async list(grantedTo) {
|
|
7526
|
+
const { status, body } = await call({
|
|
7527
|
+
action: "list",
|
|
7528
|
+
...grantedTo ? { granted_to: grantedTo } : {}
|
|
7529
|
+
});
|
|
7530
|
+
if (status !== 200) {
|
|
7531
|
+
throw new Error(
|
|
7532
|
+
`skill list failed (HTTP ${status}): ${String(body.error ?? "")}`
|
|
7533
|
+
);
|
|
7534
|
+
}
|
|
7535
|
+
return body.skills ?? [];
|
|
7536
|
+
},
|
|
7537
|
+
async get(uid) {
|
|
7538
|
+
const { status, body } = await call({ action: "get", uid });
|
|
7539
|
+
if (status === 404) return null;
|
|
7540
|
+
if (status !== 200) {
|
|
7541
|
+
throw new Error(
|
|
7542
|
+
`skill get failed (HTTP ${status}): ${String(body.error ?? "")}`
|
|
7543
|
+
);
|
|
7544
|
+
}
|
|
7545
|
+
const props = body.props ?? {};
|
|
7546
|
+
return {
|
|
7547
|
+
uid: String(body.uid ?? uid),
|
|
7548
|
+
name: String(props.name ?? ""),
|
|
7549
|
+
description: String(props.description ?? ""),
|
|
7550
|
+
content: String(props.content ?? ""),
|
|
7551
|
+
license: props.license ?? null,
|
|
7552
|
+
status: String(props.status ?? "candidate"),
|
|
7553
|
+
status_reason: props.status_reason ?? null,
|
|
7554
|
+
content_hash: String(props.content_hash ?? ""),
|
|
7555
|
+
version: Number(body.version ?? 0)
|
|
7556
|
+
};
|
|
7557
|
+
},
|
|
7558
|
+
send: call
|
|
7559
|
+
};
|
|
7560
|
+
}
|
|
7561
|
+
function isValidSkillName(name) {
|
|
7562
|
+
return name.length >= 1 && name.length <= 64 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name);
|
|
7563
|
+
}
|
|
7564
|
+
function renderSkillMd(skill) {
|
|
7565
|
+
const lines = [
|
|
7566
|
+
"---",
|
|
7567
|
+
`name: ${JSON.stringify(skill.name)}`,
|
|
7568
|
+
`description: ${JSON.stringify(skill.description)}`
|
|
7569
|
+
];
|
|
7570
|
+
if (skill.license) {
|
|
7571
|
+
lines.push(`license: ${JSON.stringify(skill.license)}`);
|
|
7572
|
+
}
|
|
7573
|
+
lines.push(
|
|
7574
|
+
"metadata:",
|
|
7575
|
+
` mindgraph-uid: ${JSON.stringify(skill.uid)}`,
|
|
7576
|
+
` mindgraph-version: ${JSON.stringify(String(skill.version))}`,
|
|
7577
|
+
` mindgraph-content-hash: ${JSON.stringify(skill.content_hash)}`,
|
|
7578
|
+
"---",
|
|
7579
|
+
"",
|
|
7580
|
+
skill.content
|
|
7581
|
+
);
|
|
7582
|
+
return `${lines.join("\n")}
|
|
7583
|
+
`;
|
|
7584
|
+
}
|
|
7585
|
+
function scalar(raw) {
|
|
7586
|
+
const trimmed = raw.trim();
|
|
7587
|
+
try {
|
|
7588
|
+
const parsed = JSON.parse(trimmed);
|
|
7589
|
+
if (typeof parsed === "string") return parsed;
|
|
7590
|
+
} catch {
|
|
7591
|
+
}
|
|
7592
|
+
return trimmed;
|
|
7593
|
+
}
|
|
7594
|
+
function parseSkillMd(text2) {
|
|
7595
|
+
if (!text2.startsWith("---\n")) return null;
|
|
7596
|
+
const end = text2.indexOf("\n---\n", 4);
|
|
7597
|
+
if (end < 0) return null;
|
|
7598
|
+
const frontmatter = text2.slice(4, end + 1);
|
|
7599
|
+
const body = text2.slice(end + 5).replace(/^\n/, "").replace(/\n$/, "");
|
|
7600
|
+
const entries = [];
|
|
7601
|
+
for (const line of frontmatter.split("\n")) {
|
|
7602
|
+
const top = /^([A-Za-z0-9_-]+):(.*)$/.exec(line);
|
|
7603
|
+
if (top) {
|
|
7604
|
+
entries.push({ key: top[1], raw: top[2] });
|
|
7605
|
+
} else if (entries.length > 0) {
|
|
7606
|
+
entries[entries.length - 1].raw += `
|
|
7607
|
+
${line}`;
|
|
7608
|
+
}
|
|
7609
|
+
}
|
|
7610
|
+
const result = { unknown: {}, body };
|
|
7611
|
+
for (const { key, raw } of entries) {
|
|
7612
|
+
switch (key) {
|
|
7613
|
+
case "name":
|
|
7614
|
+
result.name = scalar(raw);
|
|
7615
|
+
break;
|
|
7616
|
+
case "description":
|
|
7617
|
+
result.description = scalar(raw);
|
|
7618
|
+
break;
|
|
7619
|
+
case "license":
|
|
7620
|
+
result.license = scalar(raw);
|
|
7621
|
+
break;
|
|
7622
|
+
case "metadata": {
|
|
7623
|
+
const uid = /mindgraph-uid:\s*(.+)/.exec(raw);
|
|
7624
|
+
const version = /mindgraph-version:\s*(.+)/.exec(raw);
|
|
7625
|
+
const hash = /mindgraph-content-hash:\s*(.+)/.exec(raw);
|
|
7626
|
+
if (uid) {
|
|
7627
|
+
result.pin = {
|
|
7628
|
+
uid: scalar(uid[1]),
|
|
7629
|
+
version: version ? scalar(version[1]) : void 0,
|
|
7630
|
+
contentHash: hash ? scalar(hash[1]) : void 0
|
|
7631
|
+
};
|
|
7632
|
+
} else if (raw.trim().length > 0) {
|
|
7633
|
+
result.unknown[key] = raw;
|
|
7634
|
+
}
|
|
7635
|
+
break;
|
|
7636
|
+
}
|
|
7637
|
+
default:
|
|
7638
|
+
result.unknown[key] = raw;
|
|
7639
|
+
break;
|
|
7640
|
+
}
|
|
7641
|
+
}
|
|
7642
|
+
return result;
|
|
7643
|
+
}
|
|
7644
|
+
var STATE_FILE = ".mindgraph-skills-state.json";
|
|
7645
|
+
function sha256(text2) {
|
|
7646
|
+
return (0, import_node_crypto2.createHash)("sha256").update(text2).digest("hex");
|
|
7647
|
+
}
|
|
7648
|
+
function loadState(dir) {
|
|
7649
|
+
try {
|
|
7650
|
+
return JSON.parse(
|
|
7651
|
+
fs2.readFileSync(path2.join(dir, STATE_FILE), "utf8")
|
|
7652
|
+
);
|
|
7653
|
+
} catch {
|
|
7654
|
+
return {};
|
|
7655
|
+
}
|
|
7656
|
+
}
|
|
7657
|
+
function saveState(dir, state) {
|
|
7658
|
+
fs2.writeFileSync(
|
|
7659
|
+
path2.join(dir, STATE_FILE),
|
|
7660
|
+
`${JSON.stringify(state, null, 2)}
|
|
7661
|
+
`
|
|
7662
|
+
);
|
|
7663
|
+
}
|
|
7664
|
+
function isSymlink(target) {
|
|
7665
|
+
try {
|
|
7666
|
+
return fs2.lstatSync(target).isSymbolicLink();
|
|
7667
|
+
} catch {
|
|
7668
|
+
return false;
|
|
7669
|
+
}
|
|
7670
|
+
}
|
|
7671
|
+
function safeSkillPaths(dir, name) {
|
|
7672
|
+
if (!isValidSkillName(name)) {
|
|
7673
|
+
return `invalid skill name ${JSON.stringify(name)}`;
|
|
7674
|
+
}
|
|
7675
|
+
const root = path2.resolve(dir);
|
|
7676
|
+
const skillDir = path2.resolve(root, name);
|
|
7677
|
+
if (skillDir !== path2.join(root, name) || !skillDir.startsWith(root + path2.sep)) {
|
|
7678
|
+
return "resolved path escapes the target directory";
|
|
7679
|
+
}
|
|
7680
|
+
if (isSymlink(skillDir)) {
|
|
7681
|
+
return "target directory is a symlink";
|
|
7682
|
+
}
|
|
7683
|
+
const skillFile = path2.join(skillDir, "SKILL.md");
|
|
7684
|
+
if (isSymlink(skillFile)) {
|
|
7685
|
+
return "target SKILL.md is a symlink";
|
|
7686
|
+
}
|
|
7687
|
+
return { skillDir, skillFile };
|
|
7688
|
+
}
|
|
7689
|
+
async function skillsPull(api, dir, grantedTo) {
|
|
7690
|
+
const report = {
|
|
7691
|
+
written: [],
|
|
7692
|
+
unchanged: [],
|
|
7693
|
+
locallyEdited: [],
|
|
7694
|
+
conflicts: [],
|
|
7695
|
+
removed: [],
|
|
7696
|
+
refused: []
|
|
7697
|
+
};
|
|
7698
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
7699
|
+
const state = loadState(dir);
|
|
7700
|
+
const summaries = await api.list(grantedTo);
|
|
7701
|
+
const fetched = /* @__PURE__ */ new Set();
|
|
7702
|
+
for (const summary of summaries) {
|
|
7703
|
+
const detail = await api.get(summary.uid);
|
|
7704
|
+
if (!detail || detail.status !== "published") continue;
|
|
7705
|
+
fetched.add(detail.name);
|
|
7706
|
+
const paths = safeSkillPaths(dir, detail.name);
|
|
7707
|
+
if (typeof paths === "string") {
|
|
7708
|
+
report.refused.push({ name: detail.name, reason: paths });
|
|
7709
|
+
continue;
|
|
7710
|
+
}
|
|
7711
|
+
const { skillDir, skillFile } = paths;
|
|
7712
|
+
const rendered2 = renderSkillMd(detail);
|
|
7713
|
+
let existing = null;
|
|
7714
|
+
try {
|
|
7715
|
+
existing = fs2.readFileSync(skillFile, "utf8");
|
|
7716
|
+
} catch {
|
|
7717
|
+
existing = null;
|
|
7718
|
+
}
|
|
7719
|
+
if (existing === rendered2) {
|
|
7720
|
+
state[detail.name] = {
|
|
7721
|
+
uid: detail.uid,
|
|
7722
|
+
content_hash: detail.content_hash,
|
|
7723
|
+
file_sha256: sha256(rendered2)
|
|
7724
|
+
};
|
|
7725
|
+
report.unchanged.push(detail.name);
|
|
7726
|
+
continue;
|
|
7727
|
+
}
|
|
7728
|
+
const recorded = state[detail.name];
|
|
7729
|
+
const locallyModified = existing !== null && (!recorded || sha256(existing) !== recorded.file_sha256);
|
|
7730
|
+
if (locallyModified) {
|
|
7731
|
+
if (recorded && recorded.content_hash === detail.content_hash) {
|
|
7732
|
+
report.locallyEdited.push(detail.name);
|
|
7733
|
+
continue;
|
|
7734
|
+
}
|
|
7735
|
+
fs2.writeFileSync(`${skillFile}.conflict`, rendered2);
|
|
7736
|
+
report.conflicts.push(detail.name);
|
|
7737
|
+
continue;
|
|
7738
|
+
}
|
|
7739
|
+
fs2.mkdirSync(skillDir, { recursive: true });
|
|
7740
|
+
fs2.writeFileSync(skillFile, rendered2);
|
|
7741
|
+
state[detail.name] = {
|
|
7742
|
+
uid: detail.uid,
|
|
7743
|
+
content_hash: detail.content_hash,
|
|
7744
|
+
file_sha256: sha256(rendered2)
|
|
7745
|
+
};
|
|
7746
|
+
report.written.push(detail.name);
|
|
7747
|
+
}
|
|
7748
|
+
for (const [name, recorded] of Object.entries(state)) {
|
|
7749
|
+
if (fetched.has(name)) continue;
|
|
7750
|
+
const paths = safeSkillPaths(dir, name);
|
|
7751
|
+
if (typeof paths === "string") {
|
|
7752
|
+
delete state[name];
|
|
7753
|
+
continue;
|
|
7754
|
+
}
|
|
7755
|
+
const { skillDir, skillFile } = paths;
|
|
7756
|
+
let statusReason = null;
|
|
7757
|
+
try {
|
|
7758
|
+
const gone = await api.get(recorded.uid);
|
|
7759
|
+
statusReason = gone?.status_reason ?? (gone ? `status: ${gone.status}` : null);
|
|
7760
|
+
} catch {
|
|
7761
|
+
statusReason = null;
|
|
7762
|
+
}
|
|
7763
|
+
let existing = null;
|
|
7764
|
+
try {
|
|
7765
|
+
existing = fs2.readFileSync(skillFile, "utf8");
|
|
7766
|
+
} catch {
|
|
7767
|
+
existing = null;
|
|
7768
|
+
}
|
|
7769
|
+
if (existing === null) {
|
|
7770
|
+
delete state[name];
|
|
7771
|
+
continue;
|
|
7772
|
+
}
|
|
7773
|
+
const locallyModified = sha256(existing) !== recorded.file_sha256;
|
|
7774
|
+
if (locallyModified) {
|
|
7775
|
+
fs2.renameSync(skillFile, `${skillFile}.conflict`);
|
|
7776
|
+
report.removed.push({ name, statusReason, kept: true });
|
|
7777
|
+
} else {
|
|
7778
|
+
fs2.rmSync(skillDir, { recursive: true, force: true });
|
|
7779
|
+
report.removed.push({ name, statusReason, kept: false });
|
|
7780
|
+
}
|
|
7781
|
+
delete state[name];
|
|
7782
|
+
}
|
|
7783
|
+
saveState(dir, state);
|
|
7784
|
+
return report;
|
|
7785
|
+
}
|
|
7786
|
+
async function skillsPush(api, skillPath) {
|
|
7787
|
+
const resolved = path2.resolve(skillPath);
|
|
7788
|
+
const file = resolved.endsWith("SKILL.md") ? resolved : path2.join(resolved, "SKILL.md");
|
|
7789
|
+
if (isSymlink(file)) {
|
|
7790
|
+
return { outcome: "error", message: `${file} is a symlink; refusing` };
|
|
7791
|
+
}
|
|
7792
|
+
let text2;
|
|
7793
|
+
try {
|
|
7794
|
+
text2 = fs2.readFileSync(file, "utf8");
|
|
7795
|
+
} catch {
|
|
7796
|
+
return { outcome: "error", message: `could not read ${file}` };
|
|
7797
|
+
}
|
|
7798
|
+
const parsed = parseSkillMd(text2);
|
|
7799
|
+
if (!parsed) {
|
|
7800
|
+
return {
|
|
7801
|
+
outcome: "error",
|
|
7802
|
+
message: "SKILL.md must start with a --- frontmatter block"
|
|
7803
|
+
};
|
|
7804
|
+
}
|
|
7805
|
+
if (!parsed.name || !parsed.description) {
|
|
7806
|
+
return {
|
|
7807
|
+
outcome: "error",
|
|
7808
|
+
message: "SKILL.md frontmatter requires name and description"
|
|
7809
|
+
};
|
|
7810
|
+
}
|
|
7811
|
+
if (parsed.pin?.uid) {
|
|
7812
|
+
const { status: status2, body: body2 } = await api.send({
|
|
7813
|
+
action: "update",
|
|
7814
|
+
uid: parsed.pin.uid,
|
|
7815
|
+
name: parsed.name,
|
|
7816
|
+
description: parsed.description,
|
|
7817
|
+
content: parsed.body,
|
|
7818
|
+
...parsed.license ? { license: parsed.license } : {}
|
|
7819
|
+
});
|
|
7820
|
+
if (status2 === 200) {
|
|
7821
|
+
return {
|
|
7822
|
+
outcome: "updated",
|
|
7823
|
+
message: `updated candidate ${parsed.name} (${parsed.pin.uid})`,
|
|
7824
|
+
uid: parsed.pin.uid
|
|
7825
|
+
};
|
|
7826
|
+
}
|
|
7827
|
+
return {
|
|
7828
|
+
outcome: status2 === 409 ? "conflict" : "error",
|
|
7829
|
+
message: `update failed (HTTP ${status2}): ${String(body2.error ?? "")}`,
|
|
7830
|
+
existingUid: body2.existing_uid ?? void 0,
|
|
7831
|
+
existingStatus: body2.existing_status ?? void 0
|
|
7832
|
+
};
|
|
7833
|
+
}
|
|
7834
|
+
const { status, body } = await api.send({
|
|
7835
|
+
action: "create",
|
|
7836
|
+
skill_source: "imported",
|
|
7837
|
+
name: parsed.name,
|
|
7838
|
+
description: parsed.description,
|
|
7839
|
+
content: parsed.body,
|
|
7840
|
+
...parsed.license ? { license: parsed.license } : {},
|
|
7841
|
+
...Object.keys(parsed.unknown).length > 0 ? { imported_frontmatter: parsed.unknown } : {},
|
|
7842
|
+
import_origin: `skills-push:${path2.basename(resolved)}`
|
|
7843
|
+
});
|
|
7844
|
+
if (status === 200 && body.replayed === true) {
|
|
7845
|
+
return {
|
|
7846
|
+
outcome: "replayed",
|
|
7847
|
+
message: `unchanged: ${parsed.name} already imported (${String(body.uid)})`,
|
|
7848
|
+
uid: String(body.uid)
|
|
7849
|
+
};
|
|
7850
|
+
}
|
|
7851
|
+
if (status === 200) {
|
|
7852
|
+
return {
|
|
7853
|
+
outcome: "created",
|
|
7854
|
+
message: `imported ${parsed.name} as a candidate (${String(body.uid)}) \u2014 review and publish in the dashboard`,
|
|
7855
|
+
uid: String(body.uid)
|
|
7856
|
+
};
|
|
7857
|
+
}
|
|
7858
|
+
return {
|
|
7859
|
+
outcome: status === 409 ? "conflict" : "error",
|
|
7860
|
+
message: `import failed (HTTP ${status}): ${String(body.error ?? "")}`,
|
|
7861
|
+
existingUid: body.existing_uid ?? void 0,
|
|
7862
|
+
existingStatus: body.existing_status ?? void 0
|
|
7863
|
+
};
|
|
7864
|
+
}
|
|
7865
|
+
|
|
6449
7866
|
// src/hook-core.ts
|
|
6450
|
-
var
|
|
7867
|
+
var import_node_crypto4 = __toESM(require("crypto"));
|
|
6451
7868
|
var import_node_fs3 = __toESM(require("fs"));
|
|
6452
7869
|
var import_node_os3 = __toESM(require("os"));
|
|
6453
7870
|
var import_node_path3 = __toESM(require("path"));
|
|
@@ -6477,7 +7894,7 @@ function runtimeDir(options) {
|
|
|
6477
7894
|
}
|
|
6478
7895
|
function ledgerPath(sessionId, options) {
|
|
6479
7896
|
const harness = "harness" in options ? options.harness : "claude-code";
|
|
6480
|
-
const key =
|
|
7897
|
+
const key = import_node_crypto4.default.createHash("sha256").update(`${harness}\0${sessionId}`).digest("hex");
|
|
6481
7898
|
return import_node_path3.default.join(runtimeDir(options), `${key}.json`);
|
|
6482
7899
|
}
|
|
6483
7900
|
function waitBriefly(ms) {
|
|
@@ -6496,8 +7913,8 @@ function withLedger(sessionId, options, update) {
|
|
|
6496
7913
|
try {
|
|
6497
7914
|
lockFd = import_node_fs3.default.openSync(lock, "wx", 384);
|
|
6498
7915
|
break;
|
|
6499
|
-
} catch (
|
|
6500
|
-
if (
|
|
7916
|
+
} catch (error2) {
|
|
7917
|
+
if (error2.code !== "EEXIST") throw error2;
|
|
6501
7918
|
try {
|
|
6502
7919
|
if (Date.now() - import_node_fs3.default.statSync(lock).mtimeMs > 3e3) import_node_fs3.default.unlinkSync(lock);
|
|
6503
7920
|
} catch {
|
|
@@ -6519,7 +7936,7 @@ function withLedger(sessionId, options, update) {
|
|
|
6519
7936
|
}
|
|
6520
7937
|
const result = update(ledger);
|
|
6521
7938
|
ledger.updatedAt = Date.now();
|
|
6522
|
-
const temporary = `${file}.${process.pid}.${
|
|
7939
|
+
const temporary = `${file}.${process.pid}.${import_node_crypto4.default.randomUUID()}.tmp`;
|
|
6523
7940
|
import_node_fs3.default.writeFileSync(temporary, `${JSON.stringify(ledger)}
|
|
6524
7941
|
`, { mode: 384 });
|
|
6525
7942
|
import_node_fs3.default.renameSync(temporary, file);
|
|
@@ -6602,11 +8019,11 @@ var BRIEF_CHAR_BUDGET = 9e3;
|
|
|
6602
8019
|
function renderBrief(brief, meta) {
|
|
6603
8020
|
let bounded = false;
|
|
6604
8021
|
const clipTracked = (value, max) => {
|
|
6605
|
-
const
|
|
6606
|
-
if (!
|
|
6607
|
-
if (
|
|
8022
|
+
const text2 = string(value);
|
|
8023
|
+
if (!text2) return void 0;
|
|
8024
|
+
if (text2.length <= max) return text2;
|
|
6608
8025
|
bounded = true;
|
|
6609
|
-
return `${
|
|
8026
|
+
return `${text2.slice(0, max - 1)}\u2026`;
|
|
6610
8027
|
};
|
|
6611
8028
|
const nodeLine = (value, summaryMax) => {
|
|
6612
8029
|
const node = object(value);
|
|
@@ -6765,9 +8182,9 @@ async function repositoryScopeUids(input, client2, options, agentId) {
|
|
|
6765
8182
|
`${repository.repoId}: ${string(payload?.status) || "unresolved"}`
|
|
6766
8183
|
);
|
|
6767
8184
|
return void 0;
|
|
6768
|
-
} catch (
|
|
8185
|
+
} catch (error2) {
|
|
6769
8186
|
failures.push(
|
|
6770
|
-
`${repository.repoId}: ${
|
|
8187
|
+
`${repository.repoId}: ${error2?.message || "resolve failed"}`
|
|
6771
8188
|
);
|
|
6772
8189
|
return void 0;
|
|
6773
8190
|
}
|
|
@@ -6791,12 +8208,12 @@ function hasMindgraphServer(servers) {
|
|
|
6791
8208
|
for (const [name, entry] of Object.entries(servers)) {
|
|
6792
8209
|
if (name === "mindgraph") return true;
|
|
6793
8210
|
const cfg = entry && typeof entry === "object" ? entry : void 0;
|
|
6794
|
-
const
|
|
8211
|
+
const text2 = [
|
|
6795
8212
|
String(cfg?.command ?? ""),
|
|
6796
8213
|
Array.isArray(cfg?.args) ? cfg.args.join(" ") : "",
|
|
6797
8214
|
String(cfg?.url ?? "")
|
|
6798
8215
|
].join(" ");
|
|
6799
|
-
if (
|
|
8216
|
+
if (text2.includes("mindgraph-mcp")) return true;
|
|
6800
8217
|
}
|
|
6801
8218
|
return false;
|
|
6802
8219
|
}
|
|
@@ -6927,7 +8344,7 @@ async function sessionStart(input, client2, options) {
|
|
|
6927
8344
|
const contextText = registrationNote ? `${registrationNote}
|
|
6928
8345
|
|
|
6929
8346
|
${briefText}` : briefText;
|
|
6930
|
-
const briefHash =
|
|
8347
|
+
const briefHash = import_node_crypto4.default.createHash("sha256").update(contextText).digest("hex");
|
|
6931
8348
|
const shouldInject = withLedger(input.session_id, options, (ledger) => {
|
|
6932
8349
|
const changed = ledger.lastBriefHash !== briefHash;
|
|
6933
8350
|
const reinjectCompact = options.reinjectUnchangedCompact && input.source === "compact";
|
|
@@ -7026,7 +8443,7 @@ async function postTool(input, client2, options) {
|
|
|
7026
8443
|
ledger.mutatingOperations += 1;
|
|
7027
8444
|
}
|
|
7028
8445
|
if (!payload?.error) {
|
|
7029
|
-
if (name.includes("mindgraph_capture") && ["lesson", "journal"].includes(action || "") || name.includes("mindgraph_commit") && action === "resolve_decision" || name.includes("mindgraph_plan") && action === "assess_risk") {
|
|
8446
|
+
if (name.includes("mindgraph_capture") && (["lesson", "journal", "skill"].includes(action || "") || ["observation", "entity"].includes(action || "") && (Array.isArray(args.code_refs) || typeof args.supersedes_uid === "string")) || name.includes("mindgraph_commit") && action === "resolve_decision" || name.includes("mindgraph_plan") && action === "assess_risk") {
|
|
7030
8447
|
ledger.memoryWritten = true;
|
|
7031
8448
|
}
|
|
7032
8449
|
}
|
|
@@ -7307,21 +8724,21 @@ async function runCodexHook(input, client2, options = {}) {
|
|
|
7307
8724
|
|
|
7308
8725
|
// src/cli.ts
|
|
7309
8726
|
init_hook_installer();
|
|
7310
|
-
function recordHookHealth(harness,
|
|
8727
|
+
function recordHookHealth(harness, error2) {
|
|
7311
8728
|
try {
|
|
7312
|
-
const dir = process.env.MINDGRAPH_RUNTIME_DIR ||
|
|
7313
|
-
|
|
7314
|
-
const file =
|
|
8729
|
+
const dir = process.env.MINDGRAPH_RUNTIME_DIR || path7.join(os4.homedir(), ".mindgraph", "runtime");
|
|
8730
|
+
fs6.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
8731
|
+
const file = path7.join(dir, "hook-health.json");
|
|
7315
8732
|
let failures = 0;
|
|
7316
8733
|
try {
|
|
7317
|
-
failures = Number(JSON.parse(
|
|
8734
|
+
failures = Number(JSON.parse(fs6.readFileSync(file, "utf8")).consecutiveFailures) || 0;
|
|
7318
8735
|
} catch {
|
|
7319
8736
|
}
|
|
7320
|
-
|
|
8737
|
+
fs6.writeFileSync(
|
|
7321
8738
|
file,
|
|
7322
8739
|
`${JSON.stringify({
|
|
7323
8740
|
harness,
|
|
7324
|
-
lastError:
|
|
8741
|
+
lastError: error2.slice(0, 500),
|
|
7325
8742
|
lastFailureAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7326
8743
|
consecutiveFailures: failures + 1
|
|
7327
8744
|
})}
|
|
@@ -7333,14 +8750,14 @@ function recordHookHealth(harness, error) {
|
|
|
7333
8750
|
}
|
|
7334
8751
|
function clearHookHealth(harness) {
|
|
7335
8752
|
try {
|
|
7336
|
-
const file =
|
|
7337
|
-
process.env.MINDGRAPH_RUNTIME_DIR ||
|
|
8753
|
+
const file = path7.join(
|
|
8754
|
+
process.env.MINDGRAPH_RUNTIME_DIR || path7.join(os4.homedir(), ".mindgraph", "runtime"),
|
|
7338
8755
|
"hook-health.json"
|
|
7339
8756
|
);
|
|
7340
|
-
if (!
|
|
7341
|
-
const current = JSON.parse(
|
|
8757
|
+
if (!fs6.existsSync(file)) return;
|
|
8758
|
+
const current = JSON.parse(fs6.readFileSync(file, "utf8"));
|
|
7342
8759
|
if (!Number(current.consecutiveFailures)) return;
|
|
7343
|
-
|
|
8760
|
+
fs6.writeFileSync(
|
|
7344
8761
|
file,
|
|
7345
8762
|
`${JSON.stringify({
|
|
7346
8763
|
harness,
|
|
@@ -7356,7 +8773,7 @@ function clearHookHealth(harness) {
|
|
|
7356
8773
|
function getClaudeDesktopConfigPath() {
|
|
7357
8774
|
switch (process.platform) {
|
|
7358
8775
|
case "darwin":
|
|
7359
|
-
return
|
|
8776
|
+
return path7.join(
|
|
7360
8777
|
os4.homedir(),
|
|
7361
8778
|
"Library",
|
|
7362
8779
|
"Application Support",
|
|
@@ -7364,13 +8781,13 @@ function getClaudeDesktopConfigPath() {
|
|
|
7364
8781
|
"claude_desktop_config.json"
|
|
7365
8782
|
);
|
|
7366
8783
|
case "win32":
|
|
7367
|
-
return
|
|
7368
|
-
process.env.APPDATA ||
|
|
8784
|
+
return path7.join(
|
|
8785
|
+
process.env.APPDATA || path7.join(os4.homedir(), "AppData", "Roaming"),
|
|
7369
8786
|
"Claude",
|
|
7370
8787
|
"claude_desktop_config.json"
|
|
7371
8788
|
);
|
|
7372
8789
|
case "linux":
|
|
7373
|
-
return
|
|
8790
|
+
return path7.join(
|
|
7374
8791
|
os4.homedir(),
|
|
7375
8792
|
".config",
|
|
7376
8793
|
"Claude",
|
|
@@ -7381,16 +8798,16 @@ function getClaudeDesktopConfigPath() {
|
|
|
7381
8798
|
}
|
|
7382
8799
|
}
|
|
7383
8800
|
function readJsonFile(filePath) {
|
|
7384
|
-
if (!
|
|
7385
|
-
const content =
|
|
8801
|
+
if (!fs6.existsSync(filePath)) return {};
|
|
8802
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
7386
8803
|
return JSON.parse(content);
|
|
7387
8804
|
}
|
|
7388
8805
|
function writeJsonFile(filePath, data) {
|
|
7389
|
-
const dir =
|
|
7390
|
-
if (!
|
|
7391
|
-
|
|
8806
|
+
const dir = path7.dirname(filePath);
|
|
8807
|
+
if (!fs6.existsSync(dir)) {
|
|
8808
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
7392
8809
|
}
|
|
7393
|
-
|
|
8810
|
+
fs6.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
7394
8811
|
}
|
|
7395
8812
|
function getMcpServerEntry(apiKey, baseUrl) {
|
|
7396
8813
|
const env = { MINDGRAPH_API_KEY: apiKey };
|
|
@@ -7477,7 +8894,7 @@ function installClaudeCode(apiKey, baseUrl, agentId) {
|
|
|
7477
8894
|
}
|
|
7478
8895
|
function uninstallClaudeDesktop() {
|
|
7479
8896
|
const configPath = getClaudeDesktopConfigPath();
|
|
7480
|
-
if (!
|
|
8897
|
+
if (!fs6.existsSync(configPath)) {
|
|
7481
8898
|
console.log("Claude Desktop config not found. Nothing to remove.");
|
|
7482
8899
|
return;
|
|
7483
8900
|
}
|
|
@@ -7504,7 +8921,7 @@ function uninstallClaudeCode() {
|
|
|
7504
8921
|
}
|
|
7505
8922
|
function printStatus() {
|
|
7506
8923
|
const desktopPath = getClaudeDesktopConfigPath();
|
|
7507
|
-
if (
|
|
8924
|
+
if (fs6.existsSync(desktopPath)) {
|
|
7508
8925
|
const config = readJsonFile(desktopPath);
|
|
7509
8926
|
const servers = config.mcpServers;
|
|
7510
8927
|
if (servers && "mindgraph" in servers) {
|
|
@@ -7522,12 +8939,12 @@ function printStatus() {
|
|
|
7522
8939
|
`MINDGRAPH_BASE_URL: ${process.env.MINDGRAPH_BASE_URL || "https://api.mindgraph.cloud (default)"}`
|
|
7523
8940
|
);
|
|
7524
8941
|
try {
|
|
7525
|
-
const healthFile =
|
|
7526
|
-
process.env.MINDGRAPH_RUNTIME_DIR ||
|
|
8942
|
+
const healthFile = path7.join(
|
|
8943
|
+
process.env.MINDGRAPH_RUNTIME_DIR || path7.join(os4.homedir(), ".mindgraph", "runtime"),
|
|
7527
8944
|
"hook-health.json"
|
|
7528
8945
|
);
|
|
7529
|
-
if (
|
|
7530
|
-
const health = JSON.parse(
|
|
8946
|
+
if (fs6.existsSync(healthFile)) {
|
|
8947
|
+
const health = JSON.parse(fs6.readFileSync(healthFile, "utf8"));
|
|
7531
8948
|
const failures = Number(health.consecutiveFailures) || 0;
|
|
7532
8949
|
console.log(
|
|
7533
8950
|
failures > 0 ? `Hooks: FAILING (${failures} consecutive; last: ${health.lastError} at ${health.lastFailureAt})` : `Hooks: healthy (last recovery ${health.lastOkAt || "unknown"})`
|
|
@@ -7544,7 +8961,7 @@ function withHooksFlag() {
|
|
|
7544
8961
|
}
|
|
7545
8962
|
function finishCodeInstall(apiKey, baseUrl, withHooks, agentId) {
|
|
7546
8963
|
if (withHooks) {
|
|
7547
|
-
const runner = installHookRunner(__filename, void 0, "0.
|
|
8964
|
+
const runner = installHookRunner(__filename, void 0, "0.20.0");
|
|
7548
8965
|
console.log(`Installed pinned hook runner at ${runner}`);
|
|
7549
8966
|
const result = installClaudeHooks("user");
|
|
7550
8967
|
console.log(
|
|
@@ -7592,6 +9009,20 @@ USAGE:
|
|
|
7592
9009
|
mindgraph-mcp install-hooks --harness claude-code|codex [--scope user|project]
|
|
7593
9010
|
mindgraph-mcp uninstall-hooks --harness claude-code|codex [--scope user|project]
|
|
7594
9011
|
mindgraph-mcp status Show installation status
|
|
9012
|
+
mindgraph-mcp skills pull [--dir .claude/skills]
|
|
9013
|
+
Write your org's published+granted skills as
|
|
9014
|
+
SKILL.md directories (allowlisted frontmatter
|
|
9015
|
+
only). Revoked/archived skills are withdrawn;
|
|
9016
|
+
local edits are never overwritten (.conflict
|
|
9017
|
+
sidecars). NOTE: coding harnesses hot-reload
|
|
9018
|
+
skill files \u2014 running sessions adopt pulled
|
|
9019
|
+
changes immediately; session pinning exists
|
|
9020
|
+
on the managed-agent leg only.
|
|
9021
|
+
mindgraph-mcp skills push <path>
|
|
9022
|
+
Import a SKILL.md directory as a review
|
|
9023
|
+
candidate (nothing auto-publishes). A
|
|
9024
|
+
previously pulled directory routes to the
|
|
9025
|
+
candidate-only update path.
|
|
7595
9026
|
|
|
7596
9027
|
OPTIONS:
|
|
7597
9028
|
--api-key <key> MindGraph API key (or set MINDGRAPH_API_KEY env var)
|
|
@@ -7655,7 +9086,7 @@ async function pollForKey(code) {
|
|
|
7655
9086
|
} catch (e) {
|
|
7656
9087
|
if (e instanceof Error && e.message.includes("Session")) throw e;
|
|
7657
9088
|
}
|
|
7658
|
-
await new Promise((
|
|
9089
|
+
await new Promise((resolve3) => setTimeout(resolve3, POLL_INTERVAL));
|
|
7659
9090
|
}
|
|
7660
9091
|
throw new Error("Timed out waiting for authorization.");
|
|
7661
9092
|
}
|
|
@@ -7665,7 +9096,7 @@ async function interactiveInit(baseUrl) {
|
|
|
7665
9096
|
input: process.stdin,
|
|
7666
9097
|
output: process.stdout
|
|
7667
9098
|
});
|
|
7668
|
-
const ask = (q) => new Promise((
|
|
9099
|
+
const ask = (q) => new Promise((resolve3) => rl.question(q, resolve3));
|
|
7669
9100
|
console.log("\nMindGraph MCP Server Setup\n");
|
|
7670
9101
|
console.log(" 1. Sign in via browser (recommended)");
|
|
7671
9102
|
console.log(" 2. Enter API key manually");
|
|
@@ -7736,8 +9167,79 @@ async function interactiveInit(baseUrl) {
|
|
|
7736
9167
|
console.log("\nDone! Your knowledge graph is now connected.");
|
|
7737
9168
|
}
|
|
7738
9169
|
async function main2() {
|
|
7739
|
-
const {
|
|
9170
|
+
const {
|
|
9171
|
+
command: command2,
|
|
9172
|
+
apiKey,
|
|
9173
|
+
baseUrl,
|
|
9174
|
+
scope,
|
|
9175
|
+
projectDir,
|
|
9176
|
+
harness,
|
|
9177
|
+
hooks,
|
|
9178
|
+
agentId,
|
|
9179
|
+
positionals,
|
|
9180
|
+
dir
|
|
9181
|
+
} = parseArgs(process.argv);
|
|
7740
9182
|
switch (command2) {
|
|
9183
|
+
case "skills": {
|
|
9184
|
+
if (!apiKey) {
|
|
9185
|
+
console.error(
|
|
9186
|
+
"API key required. Use --api-key or set MINDGRAPH_API_KEY."
|
|
9187
|
+
);
|
|
9188
|
+
process.exit(1);
|
|
9189
|
+
}
|
|
9190
|
+
const sub = positionals[0];
|
|
9191
|
+
const api = httpSkillsApi(
|
|
9192
|
+
baseUrl ?? "https://api.mindgraph.cloud",
|
|
9193
|
+
apiKey,
|
|
9194
|
+
agentId
|
|
9195
|
+
);
|
|
9196
|
+
if (sub === "pull") {
|
|
9197
|
+
if (!agentId) {
|
|
9198
|
+
console.error(
|
|
9199
|
+
"note: no --agent-id/MINDGRAPH_AGENT_ID \u2014 pulling ALL published skills; bind an agent identity to scope the set by its grants."
|
|
9200
|
+
);
|
|
9201
|
+
}
|
|
9202
|
+
const report = await skillsPull(api, dir ?? ".claude/skills", agentId);
|
|
9203
|
+
for (const name of report.written) console.log(` written ${name}`);
|
|
9204
|
+
for (const name of report.unchanged)
|
|
9205
|
+
console.log(` unchanged ${name}`);
|
|
9206
|
+
for (const name of report.locallyEdited)
|
|
9207
|
+
console.log(` local-edit ${name} \u2014 upstream unchanged; left alone`);
|
|
9208
|
+
for (const name of report.conflicts)
|
|
9209
|
+
console.log(
|
|
9210
|
+
` CONFLICT ${name} \u2014 local edits kept; new upstream saved as SKILL.md.conflict`
|
|
9211
|
+
);
|
|
9212
|
+
for (const removal of report.removed)
|
|
9213
|
+
console.log(
|
|
9214
|
+
` withdrawn ${removal.name}${removal.kept ? " (local edits kept as SKILL.md.conflict)" : ""}${removal.statusReason ? ` \u2014 ${removal.statusReason}` : ""}`
|
|
9215
|
+
);
|
|
9216
|
+
for (const refusal of report.refused)
|
|
9217
|
+
console.log(` REFUSED ${refusal.name} \u2014 ${refusal.reason}`);
|
|
9218
|
+
const total = report.written.length + report.unchanged.length + report.locallyEdited.length + report.conflicts.length;
|
|
9219
|
+
console.log(`
|
|
9220
|
+
${total} skill(s) synced.`);
|
|
9221
|
+
} else if (sub === "push") {
|
|
9222
|
+
const target = positionals[1];
|
|
9223
|
+
if (!target) {
|
|
9224
|
+
console.error("Usage: mindgraph-mcp skills push <path>");
|
|
9225
|
+
process.exit(1);
|
|
9226
|
+
}
|
|
9227
|
+
const report = await skillsPush(api, target);
|
|
9228
|
+
console.log(report.message);
|
|
9229
|
+
if (report.existingUid) {
|
|
9230
|
+
console.log(
|
|
9231
|
+
` existing skill: ${report.existingUid}${report.existingStatus ? ` (${report.existingStatus})` : ""}`
|
|
9232
|
+
);
|
|
9233
|
+
}
|
|
9234
|
+
if (report.outcome === "error" || report.outcome === "conflict") {
|
|
9235
|
+
process.exit(1);
|
|
9236
|
+
}
|
|
9237
|
+
} else {
|
|
9238
|
+
console.error("Usage: mindgraph-mcp skills pull|push [...]");
|
|
9239
|
+
process.exit(1);
|
|
9240
|
+
}
|
|
9241
|
+
break;
|
|
9242
|
+
}
|
|
7741
9243
|
case "help":
|
|
7742
9244
|
printUsage();
|
|
7743
9245
|
break;
|
|
@@ -7774,7 +9276,7 @@ async function main2() {
|
|
|
7774
9276
|
if (hookHarness !== "claude-code" && hookHarness !== "codex") {
|
|
7775
9277
|
throw new Error("--harness must be claude-code or codex");
|
|
7776
9278
|
}
|
|
7777
|
-
const runner = installHookRunner(__filename, void 0, "0.
|
|
9279
|
+
const runner = installHookRunner(__filename, void 0, "0.20.0");
|
|
7778
9280
|
console.log(`Installed pinned hook runner at ${runner}`);
|
|
7779
9281
|
const result = hookHarness === "codex" ? installCodexHooks(scope, projectDir) : installClaudeHooks(scope, projectDir);
|
|
7780
9282
|
console.log(
|
|
@@ -7855,13 +9357,13 @@ async function main2() {
|
|
|
7855
9357
|
process.stdout.write(`${JSON.stringify(output)}
|
|
7856
9358
|
`);
|
|
7857
9359
|
clearHookHealth(hookHarness);
|
|
7858
|
-
} catch (
|
|
9360
|
+
} catch (error2) {
|
|
7859
9361
|
recordHookHealth(
|
|
7860
9362
|
hookHarness,
|
|
7861
|
-
|
|
9363
|
+
error2?.message || "hook failed"
|
|
7862
9364
|
);
|
|
7863
9365
|
console.error(
|
|
7864
|
-
`mindgraph-hook: failing open (${
|
|
9366
|
+
`mindgraph-hook: failing open (${error2?.message || "unknown error"})`
|
|
7865
9367
|
);
|
|
7866
9368
|
process.stdout.write("{}\n");
|
|
7867
9369
|
}
|
|
@@ -7880,7 +9382,7 @@ async function main2() {
|
|
|
7880
9382
|
process.exit(1);
|
|
7881
9383
|
}
|
|
7882
9384
|
}
|
|
7883
|
-
main2().catch((
|
|
7884
|
-
console.error("Error:",
|
|
9385
|
+
main2().catch((error2) => {
|
|
9386
|
+
console.error("Error:", error2.message || error2);
|
|
7885
9387
|
process.exit(1);
|
|
7886
9388
|
});
|