mindgraph-mcp 0.14.9 → 0.16.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 +3 -3
- package/dist/cli.js +1112 -205
- package/dist/index.js +600 -94
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28,6 +28,38 @@ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
|
28
28
|
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
29
29
|
var import_mindgraph2 = require("mindgraph");
|
|
30
30
|
|
|
31
|
+
// src/hook-env.ts
|
|
32
|
+
var import_node_crypto = __toESM(require("crypto"));
|
|
33
|
+
var import_node_fs = __toESM(require("fs"));
|
|
34
|
+
var import_node_os = __toESM(require("os"));
|
|
35
|
+
var import_node_path = __toESM(require("path"));
|
|
36
|
+
function stableAgentId() {
|
|
37
|
+
let user = "user";
|
|
38
|
+
try {
|
|
39
|
+
user = import_node_os.default.userInfo().username || "user";
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
const digest = import_node_crypto.default.createHash("sha256").update(`${user}@${import_node_os.default.hostname()}`).digest("hex").slice(0, 12);
|
|
43
|
+
return `u-${digest}`;
|
|
44
|
+
}
|
|
45
|
+
function hookEnvPath(dir) {
|
|
46
|
+
return import_node_path.default.join(dir || import_node_path.default.join(import_node_os.default.homedir(), ".mindgraph"), "hooks.json");
|
|
47
|
+
}
|
|
48
|
+
function loadHookEnv(dir) {
|
|
49
|
+
try {
|
|
50
|
+
const raw = import_node_fs.default.readFileSync(hookEnvPath(dir), "utf8");
|
|
51
|
+
const parsed = JSON.parse(raw);
|
|
52
|
+
return {
|
|
53
|
+
baseUrl: typeof parsed.baseUrl === "string" ? parsed.baseUrl : void 0,
|
|
54
|
+
apiKey: typeof parsed.apiKey === "string" ? parsed.apiKey : void 0,
|
|
55
|
+
agentId: typeof parsed.agentId === "string" ? parsed.agentId : void 0,
|
|
56
|
+
orgId: typeof parsed.orgId === "string" ? parsed.orgId : void 0
|
|
57
|
+
};
|
|
58
|
+
} catch {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
31
63
|
// src/error-detail.ts
|
|
32
64
|
var import_mindgraph = require("mindgraph");
|
|
33
65
|
function errorDetail(e) {
|
|
@@ -37,14 +69,31 @@ function errorDetail(e) {
|
|
|
37
69
|
}
|
|
38
70
|
return e instanceof Error ? e.message : String(e);
|
|
39
71
|
}
|
|
72
|
+
var CONFLICT_STATE_KEYS = [
|
|
73
|
+
"current_version",
|
|
74
|
+
"current_epoch",
|
|
75
|
+
"lease_expires_at",
|
|
76
|
+
"lease_owner_agent_id"
|
|
77
|
+
];
|
|
78
|
+
function conflictState(e) {
|
|
79
|
+
if (!(e instanceof import_mindgraph.MindGraphError) || !e.body || typeof e.body !== "object") {
|
|
80
|
+
return {};
|
|
81
|
+
}
|
|
82
|
+
const body = e.body;
|
|
83
|
+
const lifted = {};
|
|
84
|
+
for (const key of CONFLICT_STATE_KEYS) {
|
|
85
|
+
if (body[key] !== void 0) lifted[key] = body[key];
|
|
86
|
+
}
|
|
87
|
+
return lifted;
|
|
88
|
+
}
|
|
40
89
|
|
|
41
90
|
// src/codegraph.ts
|
|
42
91
|
var import_node_child_process = require("child_process");
|
|
43
|
-
var
|
|
92
|
+
var import_node_fs2 = require("fs");
|
|
44
93
|
var import_promises = require("fs/promises");
|
|
45
|
-
var
|
|
46
|
-
var
|
|
47
|
-
var
|
|
94
|
+
var import_node_os2 = require("os");
|
|
95
|
+
var import_node_path2 = require("path");
|
|
96
|
+
var import_node_crypto2 = require("crypto");
|
|
48
97
|
function unavailabilityCaveats(reason, message) {
|
|
49
98
|
const caveats = [message || "codegraph status failed"];
|
|
50
99
|
if (reason === "absent") {
|
|
@@ -57,14 +106,14 @@ function unavailabilityCaveats(reason, message) {
|
|
|
57
106
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
58
107
|
var DEFAULT_STATUS_TTL_MS = 6e4;
|
|
59
108
|
var MAX_OUTPUT_BYTES = 5 * 1024 * 1024;
|
|
60
|
-
function normalizePath(
|
|
61
|
-
return
|
|
109
|
+
function normalizePath(path3) {
|
|
110
|
+
return path3.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
62
111
|
}
|
|
63
112
|
function normalizeSignature(signature) {
|
|
64
113
|
return signature.replace(/\s+/g, " ").trim();
|
|
65
114
|
}
|
|
66
|
-
function languageFromPath(
|
|
67
|
-
const extension = (0,
|
|
115
|
+
function languageFromPath(path3) {
|
|
116
|
+
const extension = (0, import_node_path2.extname)(path3).toLowerCase();
|
|
68
117
|
return {
|
|
69
118
|
".c": "c",
|
|
70
119
|
".cc": "cpp",
|
|
@@ -113,30 +162,30 @@ function normalizeRemote(remote) {
|
|
|
113
162
|
}
|
|
114
163
|
}
|
|
115
164
|
function pathContains(root, candidate) {
|
|
116
|
-
const child = (0,
|
|
117
|
-
return child === "" || child !== ".." && !child.startsWith(`..${
|
|
165
|
+
const child = (0, import_node_path2.relative)(root, candidate);
|
|
166
|
+
return child === "" || child !== ".." && !child.startsWith(`..${import_node_path2.sep}`) && !(0, import_node_path2.isAbsolute)(child);
|
|
118
167
|
}
|
|
119
168
|
function nearestExistingDirectory(candidate) {
|
|
120
|
-
let current = (0,
|
|
169
|
+
let current = (0, import_node_path2.resolve)(candidate);
|
|
121
170
|
for (; ; ) {
|
|
122
|
-
if ((0,
|
|
171
|
+
if ((0, import_node_fs2.existsSync)(current)) {
|
|
123
172
|
try {
|
|
124
|
-
return (0,
|
|
173
|
+
return (0, import_node_fs2.statSync)(current).isDirectory() ? current : (0, import_node_path2.dirname)(current);
|
|
125
174
|
} catch {
|
|
126
175
|
}
|
|
127
176
|
}
|
|
128
|
-
const parent = (0,
|
|
177
|
+
const parent = (0, import_node_path2.dirname)(current);
|
|
129
178
|
if (parent === current) return current;
|
|
130
179
|
current = parent;
|
|
131
180
|
}
|
|
132
181
|
}
|
|
133
182
|
function findWorkspaceFile(start, explicit) {
|
|
134
|
-
if (explicit) return (0,
|
|
135
|
-
let current = (0,
|
|
183
|
+
if (explicit) return (0, import_node_path2.resolve)(start, explicit);
|
|
184
|
+
let current = (0, import_node_path2.resolve)(start);
|
|
136
185
|
for (; ; ) {
|
|
137
|
-
const candidate = (0,
|
|
138
|
-
if ((0,
|
|
139
|
-
const parent = (0,
|
|
186
|
+
const candidate = (0, import_node_path2.join)(current, ".mindgraph", "workspace.json");
|
|
187
|
+
if ((0, import_node_fs2.existsSync)(candidate)) return candidate;
|
|
188
|
+
const parent = (0, import_node_path2.dirname)(current);
|
|
140
189
|
if (parent === current) return void 0;
|
|
141
190
|
current = parent;
|
|
142
191
|
}
|
|
@@ -223,7 +272,7 @@ var CodegraphAdapter = class {
|
|
|
223
272
|
async resolveRepository(repo, fallbackSpaceUid) {
|
|
224
273
|
const configured = await this.resolveRepositories(fallbackSpaceUid);
|
|
225
274
|
const selected = repo && configured.find((candidate) => candidate.repoId === repo) || repo && configured.find(
|
|
226
|
-
(candidate) => candidate.repoRoot === repo || (0,
|
|
275
|
+
(candidate) => candidate.repoRoot === repo || (0, import_node_path2.basename)(candidate.repoRoot) === repo
|
|
227
276
|
) || (!repo ? [...configured].sort((a, b) => b.repoRoot.length - a.repoRoot.length).find((candidate) => pathContains(candidate.repoRoot, this.cwd)) : void 0) || (!repo && configured.length === 1 ? configured[0] : void 0);
|
|
228
277
|
if (selected) {
|
|
229
278
|
return !repo && this.env.MINDGRAPH_REPO_ID ? this.resolveRepositoryIdentity(
|
|
@@ -233,8 +282,8 @@ var CodegraphAdapter = class {
|
|
|
233
282
|
) : selected;
|
|
234
283
|
}
|
|
235
284
|
const requestedRoot = repo || this.env.MINDGRAPH_REPO_ROOT || this.cwd;
|
|
236
|
-
const requestedPath = await (0, import_promises.realpath)((0,
|
|
237
|
-
() => (0,
|
|
285
|
+
const requestedPath = await (0, import_promises.realpath)((0, import_node_path2.resolve)(this.cwd, requestedRoot)).catch(
|
|
286
|
+
() => (0, import_node_path2.resolve)(this.cwd, requestedRoot)
|
|
238
287
|
);
|
|
239
288
|
const repoRoot = await this.gitRoot(requestedPath) ?? requestedPath;
|
|
240
289
|
return this.resolveRepositoryIdentity(
|
|
@@ -255,7 +304,7 @@ var CodegraphAdapter = class {
|
|
|
255
304
|
);
|
|
256
305
|
}
|
|
257
306
|
async resolveRepositoryIdentity(root, explicitRepoId, spaceUid) {
|
|
258
|
-
const repoRoot = await (0, import_promises.realpath)(root).catch(() => (0,
|
|
307
|
+
const repoRoot = await (0, import_promises.realpath)(root).catch(() => (0, import_node_path2.resolve)(root));
|
|
259
308
|
const cacheKey = `${repoRoot}\0${explicitRepoId ?? ""}\0${spaceUid ?? ""}`;
|
|
260
309
|
const cached = this.identityCache.get(cacheKey);
|
|
261
310
|
if (cached) return cached;
|
|
@@ -285,20 +334,20 @@ var CodegraphAdapter = class {
|
|
|
285
334
|
this.env
|
|
286
335
|
).catch(() => "");
|
|
287
336
|
if (!rawCommonDir.trim()) {
|
|
288
|
-
return `local:${(0,
|
|
337
|
+
return `local:${(0, import_node_path2.basename)(repoRoot)}`;
|
|
289
338
|
}
|
|
290
|
-
const commonDir = (0,
|
|
291
|
-
const identityDir = (0,
|
|
292
|
-
const identityFile = (0,
|
|
339
|
+
const commonDir = (0, import_node_path2.resolve)(repoRoot, rawCommonDir.trim());
|
|
340
|
+
const identityDir = (0, import_node_path2.join)(commonDir, "mindgraph");
|
|
341
|
+
const identityFile = (0, import_node_path2.join)(identityDir, "repository-id");
|
|
293
342
|
try {
|
|
294
|
-
const existing = (0,
|
|
343
|
+
const existing = (0, import_node_fs2.readFileSync)(identityFile, "utf8").trim();
|
|
295
344
|
if (existing) return existing;
|
|
296
345
|
} catch {
|
|
297
346
|
}
|
|
298
|
-
(0,
|
|
299
|
-
const generated = (0,
|
|
347
|
+
(0, import_node_fs2.mkdirSync)(identityDir, { recursive: true, mode: 448 });
|
|
348
|
+
const generated = (0, import_node_crypto2.randomUUID)();
|
|
300
349
|
try {
|
|
301
|
-
(0,
|
|
350
|
+
(0, import_node_fs2.writeFileSync)(identityFile, `${generated}
|
|
302
351
|
`, {
|
|
303
352
|
encoding: "utf8",
|
|
304
353
|
flag: "wx",
|
|
@@ -307,7 +356,7 @@ var CodegraphAdapter = class {
|
|
|
307
356
|
return generated;
|
|
308
357
|
} catch (cause) {
|
|
309
358
|
if (cause.code !== "EEXIST") throw cause;
|
|
310
|
-
const winner = (0,
|
|
359
|
+
const winner = (0, import_node_fs2.readFileSync)(identityFile, "utf8").trim();
|
|
311
360
|
if (!winner) throw new Error(`empty repository identity file: ${identityFile}`);
|
|
312
361
|
return winner;
|
|
313
362
|
}
|
|
@@ -320,7 +369,7 @@ var CodegraphAdapter = class {
|
|
|
320
369
|
Math.min(this.timeoutMs, 2e3),
|
|
321
370
|
this.env
|
|
322
371
|
).catch(() => "");
|
|
323
|
-
return raw.trim() ? (0,
|
|
372
|
+
return raw.trim() ? (0, import_node_path2.resolve)(start, raw.trim()) : void 0;
|
|
324
373
|
}
|
|
325
374
|
async availability(repository) {
|
|
326
375
|
const cached = this.statusCache.get(repository.repoRoot);
|
|
@@ -437,7 +486,7 @@ var CodegraphAdapter = class {
|
|
|
437
486
|
return this.resolveRepository(ref.repo || defaultRepo, fallbackSpaceUid);
|
|
438
487
|
}
|
|
439
488
|
if (ref.path) {
|
|
440
|
-
const requested = (0,
|
|
489
|
+
const requested = (0, import_node_path2.isAbsolute)(ref.path) ? (0, import_node_path2.resolve)(ref.path) : (0, import_node_path2.resolve)(this.cwd, ref.path);
|
|
441
490
|
const absolute = await (0, import_promises.realpath)(requested).catch(() => requested);
|
|
442
491
|
const configured = await this.resolveRepositories(fallbackSpaceUid);
|
|
443
492
|
const defining = [...configured].sort((a, b) => b.repoRoot.length - a.repoRoot.length).find((candidate) => pathContains(candidate.repoRoot, absolute));
|
|
@@ -514,19 +563,19 @@ var CodegraphAdapter = class {
|
|
|
514
563
|
}
|
|
515
564
|
configuredRepositories() {
|
|
516
565
|
const configured = [];
|
|
517
|
-
if (this.workspaceFile && (0,
|
|
566
|
+
if (this.workspaceFile && (0, import_node_fs2.existsSync)(this.workspaceFile)) {
|
|
518
567
|
try {
|
|
519
568
|
const parsed = JSON.parse(
|
|
520
|
-
(0,
|
|
569
|
+
(0, import_node_fs2.readFileSync)(this.workspaceFile, "utf8")
|
|
521
570
|
);
|
|
522
571
|
if (parsed.v === 1 && Array.isArray(parsed.repositories)) {
|
|
523
|
-
const configDirectory = (0,
|
|
524
|
-
const base = (0,
|
|
572
|
+
const configDirectory = (0, import_node_path2.dirname)(this.workspaceFile);
|
|
573
|
+
const base = (0, import_node_path2.basename)(configDirectory) === ".mindgraph" ? (0, import_node_path2.dirname)(configDirectory) : configDirectory;
|
|
525
574
|
for (const repository of parsed.repositories) {
|
|
526
575
|
if (!repository || typeof repository.root !== "string") continue;
|
|
527
576
|
configured.push({
|
|
528
577
|
repoId: typeof repository.repo_id === "string" ? repository.repo_id : void 0,
|
|
529
|
-
repoRoot: (0,
|
|
578
|
+
repoRoot: (0, import_node_path2.resolve)(base, repository.root),
|
|
530
579
|
spaceUid: typeof repository.space_uid === "string" ? repository.space_uid : void 0
|
|
531
580
|
});
|
|
532
581
|
}
|
|
@@ -541,7 +590,7 @@ var CodegraphAdapter = class {
|
|
|
541
590
|
for (const [repoId, config] of Object.entries(parsed)) {
|
|
542
591
|
configured.push({
|
|
543
592
|
repoId,
|
|
544
|
-
repoRoot: (0,
|
|
593
|
+
repoRoot: (0, import_node_path2.resolve)(
|
|
545
594
|
this.cwd,
|
|
546
595
|
typeof config === "string" ? config : config.root
|
|
547
596
|
),
|
|
@@ -569,9 +618,9 @@ var CodegraphAdapter = class {
|
|
|
569
618
|
const hits = await this.runJson(args);
|
|
570
619
|
let candidates = hits.map(asNode).map(toCandidate).filter((item) => item !== null);
|
|
571
620
|
if (ref.path) {
|
|
572
|
-
const absolutePath = (0,
|
|
621
|
+
const absolutePath = (0, import_node_path2.isAbsolute)(ref.path) ? await (0, import_promises.realpath)(ref.path).catch(() => ref.path) : void 0;
|
|
573
622
|
const wanted = normalizePath(
|
|
574
|
-
absolutePath ? (0,
|
|
623
|
+
absolutePath ? (0, import_node_path2.relative)(repository.repoRoot, absolutePath) : ref.path
|
|
575
624
|
);
|
|
576
625
|
candidates = candidates.filter((candidate) => candidate.path === wanted);
|
|
577
626
|
}
|
|
@@ -672,7 +721,7 @@ var CodegraphAdapter = class {
|
|
|
672
721
|
const candidates = [
|
|
673
722
|
this.env.MINDGRAPH_CODEGRAPH_BIN,
|
|
674
723
|
"codegraph",
|
|
675
|
-
(0,
|
|
724
|
+
(0, import_node_path2.resolve)((0, import_node_os2.homedir)(), ".local", "bin", "codegraph")
|
|
676
725
|
].filter((candidate, index, all) => {
|
|
677
726
|
return Boolean(candidate) && all.indexOf(candidate) === index;
|
|
678
727
|
});
|
|
@@ -1219,9 +1268,9 @@ function primaryUids(value) {
|
|
|
1219
1268
|
}
|
|
1220
1269
|
|
|
1221
1270
|
// src/sync-tool.ts
|
|
1222
|
-
var
|
|
1223
|
-
var
|
|
1224
|
-
var
|
|
1271
|
+
var import_node_crypto3 = __toESM(require("crypto"));
|
|
1272
|
+
var import_node_fs3 = __toESM(require("fs"));
|
|
1273
|
+
var import_node_path3 = __toESM(require("path"));
|
|
1225
1274
|
var ok2 = (data) => ({
|
|
1226
1275
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1227
1276
|
});
|
|
@@ -1298,25 +1347,25 @@ var SYNC_TOOL = {
|
|
|
1298
1347
|
}
|
|
1299
1348
|
};
|
|
1300
1349
|
function safePath(root, candidate) {
|
|
1301
|
-
const absolute =
|
|
1302
|
-
const relative2 =
|
|
1303
|
-
if (relative2.startsWith("..") ||
|
|
1350
|
+
const absolute = import_node_path3.default.resolve(root, candidate);
|
|
1351
|
+
const relative2 = import_node_path3.default.relative(root, absolute);
|
|
1352
|
+
if (relative2.startsWith("..") || import_node_path3.default.isAbsolute(relative2)) {
|
|
1304
1353
|
throw new Error(`path escapes repository root: ${candidate}`);
|
|
1305
1354
|
}
|
|
1306
1355
|
return absolute;
|
|
1307
1356
|
}
|
|
1308
1357
|
function markdownFiles(target) {
|
|
1309
|
-
if (!
|
|
1310
|
-
const stat =
|
|
1358
|
+
if (!import_node_fs3.default.existsSync(target)) return [];
|
|
1359
|
+
const stat = import_node_fs3.default.statSync(target);
|
|
1311
1360
|
if (stat.isFile()) return target.endsWith(".md") ? [target] : [];
|
|
1312
1361
|
if (!stat.isDirectory()) return [];
|
|
1313
|
-
return
|
|
1362
|
+
return import_node_fs3.default.readdirSync(target, { withFileTypes: true }).flatMap((entry) => markdownFiles(import_node_path3.default.join(target, entry.name)));
|
|
1314
1363
|
}
|
|
1315
1364
|
function fileDescriptor(root, absolute) {
|
|
1316
|
-
const content =
|
|
1365
|
+
const content = import_node_fs3.default.readFileSync(absolute, "utf8");
|
|
1317
1366
|
return {
|
|
1318
|
-
logical_path:
|
|
1319
|
-
content_hash:
|
|
1367
|
+
logical_path: import_node_path3.default.relative(root, absolute).split(import_node_path3.default.sep).join("/"),
|
|
1368
|
+
content_hash: import_node_crypto3.default.createHash("sha256").update(content).digest("hex"),
|
|
1320
1369
|
bytes: Buffer.byteLength(content),
|
|
1321
1370
|
content
|
|
1322
1371
|
};
|
|
@@ -1381,7 +1430,7 @@ async function handleSyncTool(client2, args) {
|
|
|
1381
1430
|
}
|
|
1382
1431
|
if (action === "status" && request.logical_path && request.file_present !== false) {
|
|
1383
1432
|
const absolute = safePath(root, String(request.logical_path));
|
|
1384
|
-
request.file_present =
|
|
1433
|
+
request.file_present = import_node_fs3.default.existsSync(absolute);
|
|
1385
1434
|
if (request.file_present && !request.content_hash) {
|
|
1386
1435
|
request.content_hash = fileDescriptor(root, absolute).content_hash;
|
|
1387
1436
|
}
|
|
@@ -1425,6 +1474,355 @@ async function scanRepository(remote, repository, requested, provider, agentId)
|
|
|
1425
1474
|
};
|
|
1426
1475
|
}
|
|
1427
1476
|
|
|
1477
|
+
// src/render.ts
|
|
1478
|
+
var LABEL_MAX = 200;
|
|
1479
|
+
var SUMMARY_MAX = 400;
|
|
1480
|
+
var QUOTE_MAX = 180;
|
|
1481
|
+
var ARTICLE_MAX = 6e3;
|
|
1482
|
+
var CHUNK_MAX = 6e3;
|
|
1483
|
+
var RELATIONSHIP_MAX = 40;
|
|
1484
|
+
var RENDER_CHAR_BUDGET = 24e3;
|
|
1485
|
+
var CURATION_EDGE_TYPES = /* @__PURE__ */ new Set(["POSSIBLE_DUPLICATE", "PossibleDuplicate"]);
|
|
1486
|
+
var INFRA_NODE_TYPES = /* @__PURE__ */ new Set(["Chunk", "Document", "Article"]);
|
|
1487
|
+
function obj(value) {
|
|
1488
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1489
|
+
}
|
|
1490
|
+
function str(value) {
|
|
1491
|
+
return typeof value === "string" && value ? value : void 0;
|
|
1492
|
+
}
|
|
1493
|
+
function num(value) {
|
|
1494
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1495
|
+
}
|
|
1496
|
+
function clip(value, max, state) {
|
|
1497
|
+
const text = str(value);
|
|
1498
|
+
if (!text) return void 0;
|
|
1499
|
+
if (text.length <= max) return text;
|
|
1500
|
+
state.bounded = true;
|
|
1501
|
+
return `${text.slice(0, max - 1)}\u2026`;
|
|
1502
|
+
}
|
|
1503
|
+
function isNodeShaped(value) {
|
|
1504
|
+
const node = obj(value);
|
|
1505
|
+
return Boolean(node && str(node.uid) && (str(node.label) || str(node.node_type)));
|
|
1506
|
+
}
|
|
1507
|
+
function unwrapSearchResult(value) {
|
|
1508
|
+
if (isNodeShaped(value)) return value;
|
|
1509
|
+
const wrapper = obj(value);
|
|
1510
|
+
const inner = obj(wrapper?.node);
|
|
1511
|
+
if (inner && isNodeShaped(inner)) {
|
|
1512
|
+
const score = num(wrapper.score);
|
|
1513
|
+
return score !== void 0 ? { ...inner, retrieval_score: score } : inner;
|
|
1514
|
+
}
|
|
1515
|
+
return void 0;
|
|
1516
|
+
}
|
|
1517
|
+
function statusTag(node) {
|
|
1518
|
+
const tags = [];
|
|
1519
|
+
if (node.superseded === true) {
|
|
1520
|
+
tags.push(
|
|
1521
|
+
node.superseded_by ? `SUPERSEDED by ${str(node.superseded_by)}` : "SUPERSEDED"
|
|
1522
|
+
);
|
|
1523
|
+
}
|
|
1524
|
+
const truth = str(node.truth_status) ?? str(obj(node.props)?.truth_status);
|
|
1525
|
+
if (truth && truth !== "accepted") tags.push(`truth: ${truth}`);
|
|
1526
|
+
if (node.currently_valid === false) tags.push("OUTSIDE ITS VALIDITY WINDOW");
|
|
1527
|
+
if (node.valid_at_time === false) tags.push("NOT VALID AT THE REQUESTED TIME");
|
|
1528
|
+
return tags.length > 0 ? ` [${tags.join("; ")}]` : "";
|
|
1529
|
+
}
|
|
1530
|
+
function firstQuote(node, state) {
|
|
1531
|
+
const chunks = Array.isArray(node.source_chunks) ? node.source_chunks : [];
|
|
1532
|
+
for (const raw of chunks) {
|
|
1533
|
+
const chunk = obj(raw);
|
|
1534
|
+
const quote = str(chunk?.quote) ?? str(obj(chunk?.anchor)?.exact);
|
|
1535
|
+
if (quote) return clip(quote, QUOTE_MAX, state);
|
|
1536
|
+
}
|
|
1537
|
+
return void 0;
|
|
1538
|
+
}
|
|
1539
|
+
function sourceTag(node) {
|
|
1540
|
+
const docs = Array.isArray(node.source_documents) ? node.source_documents : [];
|
|
1541
|
+
const first = obj(docs[0]);
|
|
1542
|
+
const title = str(first?.title);
|
|
1543
|
+
if (!title) return void 0;
|
|
1544
|
+
const extra = docs.length > 1 ? ` (+${docs.length - 1} more)` : "";
|
|
1545
|
+
return `from: ${title}${extra}`;
|
|
1546
|
+
}
|
|
1547
|
+
function layer7Fields(node, state) {
|
|
1548
|
+
const fields = obj(obj(node.props)?.fields);
|
|
1549
|
+
if (!fields) return void 0;
|
|
1550
|
+
const pairs = Object.entries(fields).filter(([, value]) => value !== null && value !== void 0 && value !== "").map(([key, value]) => `${key}: ${Array.isArray(value) ? value.join(", ") : String(value)}`);
|
|
1551
|
+
if (pairs.length === 0) return void 0;
|
|
1552
|
+
return clip(pairs.join("; "), SUMMARY_MAX, state);
|
|
1553
|
+
}
|
|
1554
|
+
function nodeSummaryText(node, state) {
|
|
1555
|
+
const props = obj(node.props);
|
|
1556
|
+
return clip(node.summary, SUMMARY_MAX, state) ?? clip(props?.content, SUMMARY_MAX, state) ?? clip(props?.description, SUMMARY_MAX, state) ?? clip(props?.definition, SUMMARY_MAX, state);
|
|
1557
|
+
}
|
|
1558
|
+
function nodeLines(node, state) {
|
|
1559
|
+
const label = clip(node.label, LABEL_MAX, state) || "(unlabeled)";
|
|
1560
|
+
const type = str(node.node_type) || "Node";
|
|
1561
|
+
const confidence = num(node.confidence);
|
|
1562
|
+
const score = num(node.retrieval_score);
|
|
1563
|
+
const parenthetical = [
|
|
1564
|
+
type,
|
|
1565
|
+
confidence !== void 0 ? `confidence ${confidence}` : void 0,
|
|
1566
|
+
score !== void 0 ? `score ${Math.round(score * 100) / 100}` : void 0
|
|
1567
|
+
].filter(Boolean).join(", ");
|
|
1568
|
+
const head = `- **${label}** [${str(node.uid) || "?"}] (${parenthetical})${statusTag(node)}`;
|
|
1569
|
+
const summary = nodeSummaryText(node, state);
|
|
1570
|
+
const lines = [summary && summary !== label ? `${head}: ${summary}` : head];
|
|
1571
|
+
const fields = layer7Fields(node, state);
|
|
1572
|
+
if (fields) lines.push(` - fields: ${fields}`);
|
|
1573
|
+
const quote = firstQuote(node, state);
|
|
1574
|
+
if (quote) lines.push(` - source quote: "${quote}"`);
|
|
1575
|
+
const source = sourceTag(node);
|
|
1576
|
+
if (source) lines.push(` - ${source}`);
|
|
1577
|
+
const believers = Array.isArray(node.believed_by) ? node.believed_by : [];
|
|
1578
|
+
if (believers.length > 0) {
|
|
1579
|
+
const rendered = believers.slice(0, 3).map((raw) => {
|
|
1580
|
+
const believer = obj(raw);
|
|
1581
|
+
const conf = num(believer?.confidence);
|
|
1582
|
+
return `${str(believer?.agent_label) || "?"}${conf !== void 0 ? ` (${conf})` : ""}`;
|
|
1583
|
+
}).join(", ");
|
|
1584
|
+
lines.push(` - believed by: ${rendered}`);
|
|
1585
|
+
}
|
|
1586
|
+
return lines.join("\n");
|
|
1587
|
+
}
|
|
1588
|
+
function groupedNodeSections(nodes, state, headingPrefix = "") {
|
|
1589
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1590
|
+
for (const node of nodes) {
|
|
1591
|
+
const type = str(node.node_type) || "Node";
|
|
1592
|
+
const group = groups.get(type) ?? [];
|
|
1593
|
+
group.push(node);
|
|
1594
|
+
groups.set(type, group);
|
|
1595
|
+
}
|
|
1596
|
+
const sections = [];
|
|
1597
|
+
let first = true;
|
|
1598
|
+
for (const [type, group] of groups) {
|
|
1599
|
+
sections.push({
|
|
1600
|
+
heading: `${first ? headingPrefix : ""}### ${type}`,
|
|
1601
|
+
items: group.map((node) => nodeLines(node, state))
|
|
1602
|
+
});
|
|
1603
|
+
first = false;
|
|
1604
|
+
}
|
|
1605
|
+
return sections;
|
|
1606
|
+
}
|
|
1607
|
+
function relationshipItems(edges, labelByUid, state) {
|
|
1608
|
+
const lines = [];
|
|
1609
|
+
for (const raw of edges) {
|
|
1610
|
+
const edge = obj(raw);
|
|
1611
|
+
if (!edge) continue;
|
|
1612
|
+
const type = str(edge.edge_type) || "?";
|
|
1613
|
+
const from = str(edge.from_uid);
|
|
1614
|
+
const to = str(edge.to_uid);
|
|
1615
|
+
if (CURATION_EDGE_TYPES.has(type)) continue;
|
|
1616
|
+
if (!from || !to || from === to) continue;
|
|
1617
|
+
const fromLabel = labelByUid.get(from);
|
|
1618
|
+
const toLabel = labelByUid.get(to);
|
|
1619
|
+
if (!fromLabel || !toLabel) continue;
|
|
1620
|
+
lines.push(`- ${fromLabel} \u2014${type}\u2192 ${toLabel}`);
|
|
1621
|
+
}
|
|
1622
|
+
if (lines.length > RELATIONSHIP_MAX) {
|
|
1623
|
+
state.bounded = true;
|
|
1624
|
+
const extra = lines.length - RELATIONSHIP_MAX;
|
|
1625
|
+
return [...lines.slice(0, RELATIONSHIP_MAX), `- (+${extra} more relationships)`];
|
|
1626
|
+
}
|
|
1627
|
+
return lines;
|
|
1628
|
+
}
|
|
1629
|
+
function assemble(header, sections, state) {
|
|
1630
|
+
const lines = [header];
|
|
1631
|
+
let used = header.length;
|
|
1632
|
+
for (const section of sections) {
|
|
1633
|
+
if (section.items.length === 0 && !section.heading) continue;
|
|
1634
|
+
const headingCost = section.heading.length + 2;
|
|
1635
|
+
if (used + headingCost > RENDER_CHAR_BUDGET) {
|
|
1636
|
+
state.bounded = true;
|
|
1637
|
+
continue;
|
|
1638
|
+
}
|
|
1639
|
+
let sectionUsed = 0;
|
|
1640
|
+
const kept = [];
|
|
1641
|
+
let dropped = 0;
|
|
1642
|
+
for (const item of section.items) {
|
|
1643
|
+
if (used + headingCost + sectionUsed + item.length + 1 > RENDER_CHAR_BUDGET) {
|
|
1644
|
+
dropped += 1;
|
|
1645
|
+
continue;
|
|
1646
|
+
}
|
|
1647
|
+
kept.push(item);
|
|
1648
|
+
sectionUsed += item.length + 1;
|
|
1649
|
+
}
|
|
1650
|
+
if (kept.length === 0 && section.items.length > 0) {
|
|
1651
|
+
state.bounded = true;
|
|
1652
|
+
continue;
|
|
1653
|
+
}
|
|
1654
|
+
if (dropped > 0) {
|
|
1655
|
+
state.bounded = true;
|
|
1656
|
+
const marker = `(+${dropped} more not shown)`;
|
|
1657
|
+
kept.push(marker);
|
|
1658
|
+
sectionUsed += marker.length + 1;
|
|
1659
|
+
}
|
|
1660
|
+
lines.push(
|
|
1661
|
+
section.items.length === 0 ? section.heading : `${section.heading}
|
|
1662
|
+
${kept.join("\n")}`
|
|
1663
|
+
);
|
|
1664
|
+
used += headingCost + sectionUsed;
|
|
1665
|
+
}
|
|
1666
|
+
if (state.bounded) {
|
|
1667
|
+
lines.push(
|
|
1668
|
+
'\u2026[rendered context bounded \u2014 fetch full node content by uid, or pass format: "json" for the raw response]'
|
|
1669
|
+
);
|
|
1670
|
+
}
|
|
1671
|
+
return lines.join("\n\n");
|
|
1672
|
+
}
|
|
1673
|
+
function policySections(payload, state) {
|
|
1674
|
+
const policies = Array.isArray(payload.applicable_policies) ? payload.applicable_policies : [];
|
|
1675
|
+
if (policies.length === 0) return [];
|
|
1676
|
+
const items = policies.map((raw) => obj(raw)).filter((policy) => Boolean(policy)).map((policy) => {
|
|
1677
|
+
const rules = Array.isArray(policy.prose_rules) ? policy.prose_rules.map(String).join("; ") : policy.prose_rules;
|
|
1678
|
+
return `- ${str(policy.name) || "(unnamed policy)"}: ${clip(rules, SUMMARY_MAX, state) || "(structured rules apply)"}`;
|
|
1679
|
+
});
|
|
1680
|
+
return [{ heading: "## Applicable policies", items }];
|
|
1681
|
+
}
|
|
1682
|
+
function hiddenSections(payload) {
|
|
1683
|
+
const hidden = payload.hidden_item_count;
|
|
1684
|
+
if (hidden === void 0 || hidden === null) return [];
|
|
1685
|
+
return [
|
|
1686
|
+
{
|
|
1687
|
+
heading: `Note: ${hidden} item(s) were hidden by access scoping.`,
|
|
1688
|
+
items: []
|
|
1689
|
+
}
|
|
1690
|
+
];
|
|
1691
|
+
}
|
|
1692
|
+
function renderContext(response) {
|
|
1693
|
+
const payload = obj(response);
|
|
1694
|
+
if (!payload) return void 0;
|
|
1695
|
+
const graph = obj(payload.graph);
|
|
1696
|
+
const rawNodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
|
|
1697
|
+
const shaped = rawNodes.filter(isNodeShaped);
|
|
1698
|
+
const nodes = shaped.filter((node) => !INFRA_NODE_TYPES.has(str(node.node_type) || ""));
|
|
1699
|
+
const infraOnly = shaped.length - nodes.length;
|
|
1700
|
+
const articles = Array.isArray(payload.articles) ? payload.articles : [];
|
|
1701
|
+
const chunks = Array.isArray(payload.chunks) ? payload.chunks : [];
|
|
1702
|
+
const state = { bounded: false };
|
|
1703
|
+
const sections = [];
|
|
1704
|
+
if (articles.length > 0) {
|
|
1705
|
+
sections.push({
|
|
1706
|
+
heading: "## Knowledge articles",
|
|
1707
|
+
items: articles.map((raw) => obj(raw)).filter((article) => Boolean(article)).map(
|
|
1708
|
+
(article) => `### ${str(article.label) || "(untitled)"} [${str(article.uid) || "?"}]
|
|
1709
|
+
${clip(article.content, ARTICLE_MAX, state) || ""}`
|
|
1710
|
+
)
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
if (nodes.length > 0) {
|
|
1714
|
+
sections.push(...groupedNodeSections(nodes, state, "## Knowledge graph\n\n"));
|
|
1715
|
+
const labelByUid = /* @__PURE__ */ new Map();
|
|
1716
|
+
for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
1717
|
+
const relationships = relationshipItems(
|
|
1718
|
+
Array.isArray(graph?.edges) ? graph.edges : [],
|
|
1719
|
+
labelByUid,
|
|
1720
|
+
state
|
|
1721
|
+
);
|
|
1722
|
+
if (relationships.length > 0) {
|
|
1723
|
+
sections.push({ heading: "## Relationships", items: relationships });
|
|
1724
|
+
}
|
|
1725
|
+
} else if (infraOnly > 0) {
|
|
1726
|
+
sections.push({
|
|
1727
|
+
heading: `Note: ${infraOnly} document/chunk node(s) matched \u2014 use action "document_index" or include_chunks for their content.`,
|
|
1728
|
+
items: []
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
if (chunks.length > 0) {
|
|
1732
|
+
sections.push({
|
|
1733
|
+
heading: "## Source excerpts",
|
|
1734
|
+
items: chunks.map((raw) => obj(raw)).filter((chunk) => Boolean(chunk)).map(
|
|
1735
|
+
(chunk) => `- [${str(chunk.chunk_uid) || "?"}] from "${str(chunk.document_title) || "unknown document"}": "${clip(chunk.content, CHUNK_MAX, state) || ""}"`
|
|
1736
|
+
)
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
sections.push(...policySections(payload, state));
|
|
1740
|
+
sections.push(...hiddenSections(payload));
|
|
1741
|
+
if (sections.length === 0) {
|
|
1742
|
+
return "No results found for this search.";
|
|
1743
|
+
}
|
|
1744
|
+
if (nodes.length === 0 && articles.length === 0 && chunks.length === 0) {
|
|
1745
|
+
return assemble("# Retrieved context\n\nNo graph results matched this search.", sections, state);
|
|
1746
|
+
}
|
|
1747
|
+
return assemble("# Retrieved context", sections, state);
|
|
1748
|
+
}
|
|
1749
|
+
function renderNodeList(response, title, limit) {
|
|
1750
|
+
let candidates = [];
|
|
1751
|
+
if (Array.isArray(response)) {
|
|
1752
|
+
candidates = response;
|
|
1753
|
+
} else {
|
|
1754
|
+
const payload = obj(response);
|
|
1755
|
+
if (!payload) return void 0;
|
|
1756
|
+
for (const key of ["nodes", "results", "items", "goals", "questions", "claims", "preferences", "documents"]) {
|
|
1757
|
+
const candidate = payload[key];
|
|
1758
|
+
if (Array.isArray(candidate) && candidate.some((entry) => unwrapSearchResult(entry))) {
|
|
1759
|
+
candidates = candidate;
|
|
1760
|
+
break;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
let nodes = candidates.map(unwrapSearchResult).filter((node) => Boolean(node));
|
|
1765
|
+
if (nodes.length === 0) return void 0;
|
|
1766
|
+
const total = nodes.length;
|
|
1767
|
+
const state = { bounded: false };
|
|
1768
|
+
if (limit !== void 0 && limit > 0 && nodes.length > limit) {
|
|
1769
|
+
nodes = nodes.slice(0, limit);
|
|
1770
|
+
state.bounded = true;
|
|
1771
|
+
}
|
|
1772
|
+
const sections = groupedNodeSections(nodes, state);
|
|
1773
|
+
const shown = nodes.length;
|
|
1774
|
+
return assemble(
|
|
1775
|
+
`# ${title} (${shown === total ? total : `${shown} of ${total}`})`,
|
|
1776
|
+
sections,
|
|
1777
|
+
state
|
|
1778
|
+
);
|
|
1779
|
+
}
|
|
1780
|
+
function renderTraversal(response) {
|
|
1781
|
+
const payload = obj(response);
|
|
1782
|
+
if (!payload) return void 0;
|
|
1783
|
+
const container = obj(payload.graph) ?? payload;
|
|
1784
|
+
const rawNodes = Array.isArray(container.nodes) ? container.nodes : [];
|
|
1785
|
+
const nodes = rawNodes.filter(isNodeShaped);
|
|
1786
|
+
const paths = Array.isArray(payload.paths) ? payload.paths : [];
|
|
1787
|
+
if (nodes.length === 0 && paths.length === 0) return void 0;
|
|
1788
|
+
const state = { bounded: false };
|
|
1789
|
+
const sections = [];
|
|
1790
|
+
const labelByUid = /* @__PURE__ */ new Map();
|
|
1791
|
+
for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
1792
|
+
if (paths.length > 0) {
|
|
1793
|
+
const items = paths.map((raw) => obj(raw)).filter((path3) => Boolean(path3)).map((path3, index) => {
|
|
1794
|
+
const uids = Array.isArray(path3.node_uids) ? path3.node_uids.map(String) : [];
|
|
1795
|
+
const labels = Array.isArray(path3.labels) ? path3.labels.map(String) : Array.isArray(path3.nodes) ? path3.nodes.map((hop) => {
|
|
1796
|
+
const hopNode = obj(hop);
|
|
1797
|
+
return str(hopNode?.label) || labelByUid.get(str(hopNode?.uid) || "") || str(hop) || "?";
|
|
1798
|
+
}) : [];
|
|
1799
|
+
const route = labels.length > 0 ? labels.join(" \u2192 ") : uids.join(" \u2192 ") || "(path)";
|
|
1800
|
+
const cost = num(path3.cost) ?? num(path3.path_cost);
|
|
1801
|
+
const confidence = num(path3.path_confidence);
|
|
1802
|
+
const meta = [
|
|
1803
|
+
cost !== void 0 ? `cost ${cost}` : void 0,
|
|
1804
|
+
confidence !== void 0 ? `confidence ${confidence}` : void 0
|
|
1805
|
+
].filter(Boolean);
|
|
1806
|
+
const head = `${index + 1}. ${route}${meta.length > 0 ? ` (${meta.join(", ")})` : ""}`;
|
|
1807
|
+
return labels.length > 0 && uids.length > 0 ? `${head}
|
|
1808
|
+
uids: ${uids.join(" \u2192 ")}` : head;
|
|
1809
|
+
});
|
|
1810
|
+
sections.push({ heading: "## Paths", items });
|
|
1811
|
+
}
|
|
1812
|
+
if (nodes.length > 0) {
|
|
1813
|
+
sections.push(...groupedNodeSections(nodes, state, "## Nodes\n\n"));
|
|
1814
|
+
const relationships = relationshipItems(
|
|
1815
|
+
Array.isArray(container.edges) ? container.edges : [],
|
|
1816
|
+
labelByUid,
|
|
1817
|
+
state
|
|
1818
|
+
);
|
|
1819
|
+
if (relationships.length > 0) {
|
|
1820
|
+
sections.push({ heading: "## Relationships", items: relationships });
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
return assemble("# Graph traversal", sections, state);
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1428
1826
|
// src/tools.ts
|
|
1429
1827
|
var TOOLS = [
|
|
1430
1828
|
{
|
|
@@ -1999,10 +2397,16 @@ var TOOLS = [
|
|
|
1999
2397
|
"neighborhood",
|
|
2000
2398
|
"path",
|
|
2001
2399
|
"subgraph",
|
|
2400
|
+
"top_k_paths",
|
|
2002
2401
|
"document_index"
|
|
2003
2402
|
],
|
|
2004
2403
|
description: "Retrieval or traversal strategy"
|
|
2005
2404
|
},
|
|
2405
|
+
format: {
|
|
2406
|
+
type: "string",
|
|
2407
|
+
enum: ["text", "json"],
|
|
2408
|
+
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)."
|
|
2409
|
+
},
|
|
2006
2410
|
query: {
|
|
2007
2411
|
type: "string",
|
|
2008
2412
|
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."
|
|
@@ -2062,7 +2466,35 @@ var TOOLS = [
|
|
|
2062
2466
|
},
|
|
2063
2467
|
include_documents: {
|
|
2064
2468
|
type: "boolean",
|
|
2065
|
-
description: "Include ingested documents and wiki articles in context results. The coding profile defaults these OFF so engineering questions aren't answered from unrelated ingested content; pass true when the question is about a document, spec, or article."
|
|
2469
|
+
description: "Include ingested documents and wiki articles in context results. The coding profile defaults these OFF so engineering questions aren't answered from unrelated ingested content; pass true when the question is about a document, spec, or article \u2014 or false in any profile to drop the article leg."
|
|
2470
|
+
},
|
|
2471
|
+
article_limit: {
|
|
2472
|
+
type: "number",
|
|
2473
|
+
description: "Max wiki articles in context results (default 3; 0 disables the article leg). An explicit value overrides profile defaults, including the coding profile's articles-off default."
|
|
2474
|
+
},
|
|
2475
|
+
valid_at: {
|
|
2476
|
+
type: "string",
|
|
2477
|
+
description: "ISO-8601 date (e.g. '2021-06') for context: nodes with validity windows are annotated as of this date instead of today"
|
|
2478
|
+
},
|
|
2479
|
+
created_after: {
|
|
2480
|
+
type: "number",
|
|
2481
|
+
description: "Unix seconds \u2014 'recent' action: only nodes ingested after this time"
|
|
2482
|
+
},
|
|
2483
|
+
created_before: {
|
|
2484
|
+
type: "number",
|
|
2485
|
+
description: "Unix seconds \u2014 'recent' action: only nodes ingested before this time"
|
|
2486
|
+
},
|
|
2487
|
+
explain: {
|
|
2488
|
+
type: "boolean",
|
|
2489
|
+
description: "hybrid action: include per-leg retrieval scoring (legs) in the response"
|
|
2490
|
+
},
|
|
2491
|
+
k: {
|
|
2492
|
+
type: "number",
|
|
2493
|
+
description: "top_k_paths action: number of cheapest paths (default 3, cap 25)"
|
|
2494
|
+
},
|
|
2495
|
+
max_cost: {
|
|
2496
|
+
type: "number",
|
|
2497
|
+
description: "top_k_paths action: optional cost ceiling for returned paths"
|
|
2066
2498
|
},
|
|
2067
2499
|
include_chunks: {
|
|
2068
2500
|
type: "boolean",
|
|
@@ -2441,6 +2873,18 @@ async function handleTool(client2, name, args) {
|
|
|
2441
2873
|
].includes(action);
|
|
2442
2874
|
return attach ? await attachCodeRefsToToolResult(client2, result, args) : result;
|
|
2443
2875
|
} catch (e) {
|
|
2876
|
+
const lifted = conflictState(e);
|
|
2877
|
+
if (Object.keys(lifted).length > 0) {
|
|
2878
|
+
return {
|
|
2879
|
+
content: [
|
|
2880
|
+
{
|
|
2881
|
+
type: "text",
|
|
2882
|
+
text: JSON.stringify({ error: errorDetail(e), ...lifted })
|
|
2883
|
+
}
|
|
2884
|
+
],
|
|
2885
|
+
isError: true
|
|
2886
|
+
};
|
|
2887
|
+
}
|
|
2444
2888
|
return err3(errorDetail(e));
|
|
2445
2889
|
}
|
|
2446
2890
|
}
|
|
@@ -2956,6 +3400,7 @@ async function handlePlan(client2, args) {
|
|
|
2956
3400
|
async function handleRetrieve(client2, args) {
|
|
2957
3401
|
const {
|
|
2958
3402
|
action,
|
|
3403
|
+
format,
|
|
2959
3404
|
query,
|
|
2960
3405
|
start_uid,
|
|
2961
3406
|
end_uid,
|
|
@@ -2976,12 +3421,24 @@ async function handleRetrieve(client2, args) {
|
|
|
2976
3421
|
graph_max_depth,
|
|
2977
3422
|
confidence_min,
|
|
2978
3423
|
salience_min,
|
|
3424
|
+
article_limit,
|
|
3425
|
+
valid_at,
|
|
3426
|
+
created_after,
|
|
3427
|
+
created_before,
|
|
3428
|
+
explain,
|
|
3429
|
+
k,
|
|
3430
|
+
max_cost,
|
|
2979
3431
|
agent_id
|
|
2980
3432
|
} = args;
|
|
3433
|
+
const rendered = (raw, render) => {
|
|
3434
|
+
if (format === "json") return ok3(raw);
|
|
3435
|
+
const text = render(raw);
|
|
3436
|
+
return text !== void 0 ? { content: [{ type: "text", text }] } : ok3(raw);
|
|
3437
|
+
};
|
|
2981
3438
|
switch (action) {
|
|
2982
3439
|
case "context":
|
|
2983
3440
|
if (!query) return err3("query is required for context retrieval");
|
|
2984
|
-
return
|
|
3441
|
+
return rendered(
|
|
2985
3442
|
await client2.retrieveContext({
|
|
2986
3443
|
query,
|
|
2987
3444
|
node_limit: limit,
|
|
@@ -2990,110 +3447,139 @@ async function handleRetrieve(client2, args) {
|
|
|
2990
3447
|
chunk_limit: include_chunks ? limit ?? 5 : 0,
|
|
2991
3448
|
// Scoped by default, never walled: the coding profile drops the
|
|
2992
3449
|
// document/article leg (the "Bob Work" class of noise) unless the
|
|
2993
|
-
// model opts in for a genuinely document-shaped question.
|
|
2994
|
-
|
|
3450
|
+
// model opts in for a genuinely document-shaped question. Any
|
|
3451
|
+
// profile can drop it with include_documents: false (previously a
|
|
3452
|
+
// silent no-op outside coding). An EXPLICIT article_limit is at
|
|
3453
|
+
// least as clear an opt-in as include_documents:true — it always
|
|
3454
|
+
// wins over profile defaults (a coding-profile model reaching for
|
|
3455
|
+
// the advertised knob must not get a silent no-op).
|
|
3456
|
+
...article_limit !== void 0 ? { article_limit } : include_documents === false || process.env.MINDGRAPH_PROFILE === "coding" && !include_documents ? { article_limit: 0 } : {},
|
|
3457
|
+
valid_at,
|
|
2995
3458
|
include_graph,
|
|
2996
3459
|
graph_expansion_limit: graph_expansion_limit ?? 3,
|
|
2997
3460
|
graph_max_depth: graph_max_depth ?? 2
|
|
2998
|
-
})
|
|
3461
|
+
}),
|
|
3462
|
+
renderContext
|
|
2999
3463
|
);
|
|
3000
3464
|
case "text":
|
|
3001
3465
|
if (!query) return err3("query is required for text search");
|
|
3002
|
-
return
|
|
3466
|
+
return rendered(
|
|
3003
3467
|
await client2.retrieve({
|
|
3004
3468
|
action: "text",
|
|
3005
3469
|
query,
|
|
3006
3470
|
limit,
|
|
3007
3471
|
node_types,
|
|
3008
3472
|
layer,
|
|
3473
|
+
explain,
|
|
3009
3474
|
agent_id
|
|
3010
|
-
})
|
|
3475
|
+
}),
|
|
3476
|
+
(raw) => renderNodeList(raw, `Text search: ${query}`)
|
|
3011
3477
|
);
|
|
3012
3478
|
case "semantic":
|
|
3013
3479
|
if (!query) return err3("query is required for semantic search");
|
|
3014
|
-
return
|
|
3480
|
+
return rendered(
|
|
3015
3481
|
await client2.retrieve({
|
|
3016
3482
|
action: "semantic",
|
|
3017
3483
|
query,
|
|
3018
3484
|
k: limit,
|
|
3019
3485
|
node_types,
|
|
3020
3486
|
layer,
|
|
3487
|
+
explain,
|
|
3021
3488
|
agent_id
|
|
3022
|
-
})
|
|
3489
|
+
}),
|
|
3490
|
+
(raw) => renderNodeList(raw, `Semantic search: ${query}`)
|
|
3023
3491
|
);
|
|
3024
3492
|
case "hybrid":
|
|
3025
3493
|
if (!query) return err3("query is required for hybrid search");
|
|
3026
|
-
return
|
|
3494
|
+
return rendered(
|
|
3027
3495
|
await client2.retrieve({
|
|
3028
3496
|
action: "hybrid",
|
|
3029
3497
|
query,
|
|
3030
3498
|
k: limit,
|
|
3031
3499
|
node_types,
|
|
3032
3500
|
layer,
|
|
3501
|
+
explain,
|
|
3033
3502
|
agent_id
|
|
3034
|
-
})
|
|
3503
|
+
}),
|
|
3504
|
+
// With explain, per-leg scoring must survive — raw JSON only.
|
|
3505
|
+
explain ? () => void 0 : (raw) => renderNodeList(raw, `Hybrid search: ${query}`)
|
|
3035
3506
|
);
|
|
3036
|
-
// Structured queries
|
|
3507
|
+
// Structured queries. These go through the generic /retrieve action
|
|
3508
|
+
// dispatch rather than the SDK's no-argument convenience methods — the
|
|
3509
|
+
// conveniences silently dropped `limit`/`offset` (the tool advertised
|
|
3510
|
+
// them; five actions ignored them).
|
|
3037
3511
|
case "active_goals":
|
|
3038
|
-
return ok3(await client2.getGoals());
|
|
3039
3512
|
case "open_questions":
|
|
3040
|
-
return ok3(await client2.getOpenQuestions());
|
|
3041
3513
|
case "weak_claims":
|
|
3042
|
-
return ok3(await client2.getWeakClaims());
|
|
3043
3514
|
case "pending_approvals":
|
|
3044
|
-
return ok3(await client2.getPendingApprovals());
|
|
3045
3515
|
case "unresolved_contradictions":
|
|
3046
|
-
return
|
|
3516
|
+
return rendered(
|
|
3517
|
+
await client2.retrieve({
|
|
3518
|
+
action,
|
|
3519
|
+
limit,
|
|
3520
|
+
offset,
|
|
3521
|
+
agent_id
|
|
3522
|
+
}),
|
|
3523
|
+
(raw) => renderNodeList(raw, action.replace(/_/g, " "), limit)
|
|
3524
|
+
);
|
|
3047
3525
|
case "stale_derivations":
|
|
3048
|
-
return
|
|
3526
|
+
return rendered(
|
|
3049
3527
|
await client2.retrieve({
|
|
3050
3528
|
action: "stale_derivations",
|
|
3051
3529
|
limit,
|
|
3052
3530
|
offset
|
|
3053
|
-
})
|
|
3531
|
+
}),
|
|
3532
|
+
(raw) => renderNodeList(raw, "stale derivations", limit)
|
|
3054
3533
|
);
|
|
3055
3534
|
case "preferences":
|
|
3056
|
-
return
|
|
3535
|
+
return rendered(
|
|
3057
3536
|
await client2.retrieve({
|
|
3058
3537
|
action: "preferences",
|
|
3059
3538
|
...query ? { query, k: limit } : {},
|
|
3060
3539
|
limit,
|
|
3061
3540
|
agent_id
|
|
3062
|
-
})
|
|
3541
|
+
}),
|
|
3542
|
+
(raw) => renderNodeList(raw, "Preferences", limit)
|
|
3063
3543
|
);
|
|
3064
3544
|
case "layer":
|
|
3065
3545
|
if (!layer) return err3("layer is required for layer retrieval");
|
|
3066
|
-
return
|
|
3546
|
+
return rendered(
|
|
3067
3547
|
await client2.retrieve({
|
|
3068
3548
|
action: "layer",
|
|
3069
3549
|
layer,
|
|
3070
3550
|
limit,
|
|
3551
|
+
node_types,
|
|
3071
3552
|
agent_id
|
|
3072
|
-
})
|
|
3553
|
+
}),
|
|
3554
|
+
(raw) => renderNodeList(raw, `Layer: ${layer}`, limit)
|
|
3073
3555
|
);
|
|
3074
3556
|
case "recent":
|
|
3075
|
-
return
|
|
3557
|
+
return rendered(
|
|
3076
3558
|
await client2.retrieve({
|
|
3077
3559
|
action: "recent",
|
|
3078
3560
|
limit,
|
|
3079
3561
|
node_types,
|
|
3080
3562
|
confidence_min,
|
|
3081
3563
|
salience_min,
|
|
3564
|
+
created_after,
|
|
3565
|
+
created_before,
|
|
3082
3566
|
agent_id
|
|
3083
|
-
})
|
|
3567
|
+
}),
|
|
3568
|
+
(raw) => renderNodeList(raw, "Recently ingested", limit)
|
|
3084
3569
|
);
|
|
3085
3570
|
// Document inventory
|
|
3086
3571
|
case "document_index":
|
|
3087
|
-
return
|
|
3572
|
+
return rendered(
|
|
3088
3573
|
await client2.getNodes({
|
|
3089
3574
|
node_type: "Document",
|
|
3090
3575
|
limit: limit ?? 100
|
|
3091
|
-
})
|
|
3576
|
+
}),
|
|
3577
|
+
(raw) => renderNodeList(raw, "Ingested documents")
|
|
3092
3578
|
);
|
|
3093
3579
|
// Graph traversal
|
|
3094
3580
|
case "chain":
|
|
3095
3581
|
if (!start_uid) return err3("start_uid is required for chain traversal");
|
|
3096
|
-
return
|
|
3582
|
+
return rendered(
|
|
3097
3583
|
await client2.traverse({
|
|
3098
3584
|
action: "chain",
|
|
3099
3585
|
start_uid,
|
|
@@ -3101,11 +3587,12 @@ async function handleRetrieve(client2, args) {
|
|
|
3101
3587
|
exclude_edge_types,
|
|
3102
3588
|
include_provenance,
|
|
3103
3589
|
max_nodes
|
|
3104
|
-
})
|
|
3590
|
+
}),
|
|
3591
|
+
renderTraversal
|
|
3105
3592
|
);
|
|
3106
3593
|
case "neighborhood":
|
|
3107
3594
|
if (!start_uid) return err3("start_uid is required for neighborhood traversal");
|
|
3108
|
-
return
|
|
3595
|
+
return rendered(
|
|
3109
3596
|
await client2.traverse({
|
|
3110
3597
|
action: "neighborhood",
|
|
3111
3598
|
start_uid,
|
|
@@ -3115,12 +3602,13 @@ async function handleRetrieve(client2, args) {
|
|
|
3115
3602
|
exclude_edge_types,
|
|
3116
3603
|
include_provenance,
|
|
3117
3604
|
max_nodes
|
|
3118
|
-
})
|
|
3605
|
+
}),
|
|
3606
|
+
renderTraversal
|
|
3119
3607
|
);
|
|
3120
3608
|
case "path":
|
|
3121
3609
|
if (!start_uid) return err3("start_uid is required for path traversal");
|
|
3122
3610
|
if (!end_uid) return err3("end_uid is required for path traversal");
|
|
3123
|
-
return
|
|
3611
|
+
return rendered(
|
|
3124
3612
|
await client2.traverse({
|
|
3125
3613
|
action: "path",
|
|
3126
3614
|
start_uid,
|
|
@@ -3131,11 +3619,12 @@ async function handleRetrieve(client2, args) {
|
|
|
3131
3619
|
exclude_edge_types,
|
|
3132
3620
|
include_provenance,
|
|
3133
3621
|
max_nodes
|
|
3134
|
-
})
|
|
3622
|
+
}),
|
|
3623
|
+
renderTraversal
|
|
3135
3624
|
);
|
|
3136
3625
|
case "subgraph":
|
|
3137
3626
|
if (!start_uid) return err3("start_uid is required for subgraph traversal");
|
|
3138
|
-
return
|
|
3627
|
+
return rendered(
|
|
3139
3628
|
await client2.traverse({
|
|
3140
3629
|
action: "subgraph",
|
|
3141
3630
|
start_uid,
|
|
@@ -3145,7 +3634,24 @@ async function handleRetrieve(client2, args) {
|
|
|
3145
3634
|
exclude_edge_types,
|
|
3146
3635
|
include_provenance,
|
|
3147
3636
|
max_nodes
|
|
3148
|
-
})
|
|
3637
|
+
}),
|
|
3638
|
+
renderTraversal
|
|
3639
|
+
);
|
|
3640
|
+
case "top_k_paths":
|
|
3641
|
+
if (!start_uid) return err3("start_uid is required for top_k_paths");
|
|
3642
|
+
if (!end_uid) return err3("end_uid is required for top_k_paths");
|
|
3643
|
+
return rendered(
|
|
3644
|
+
await client2.traverse({
|
|
3645
|
+
action: "top_k_paths",
|
|
3646
|
+
start_uid,
|
|
3647
|
+
end_uid,
|
|
3648
|
+
k,
|
|
3649
|
+
max_hops: max_depth,
|
|
3650
|
+
max_cost,
|
|
3651
|
+
edge_types,
|
|
3652
|
+
exclude_edge_types
|
|
3653
|
+
}),
|
|
3654
|
+
renderTraversal
|
|
3149
3655
|
);
|
|
3150
3656
|
default:
|
|
3151
3657
|
return err3(`Unknown retrieve action: ${action}`);
|
|
@@ -3876,7 +4382,7 @@ async function readGovernedResource(client2, uri, governanceConfig) {
|
|
|
3876
4382
|
// src/index.ts
|
|
3877
4383
|
var API_KEY = process.env.MINDGRAPH_API_KEY;
|
|
3878
4384
|
var BASE_URL = process.env.MINDGRAPH_BASE_URL || "https://api.mindgraph.cloud";
|
|
3879
|
-
var AGENT_ID = process.env.MINDGRAPH_AGENT_ID ||
|
|
4385
|
+
var AGENT_ID = process.env.MINDGRAPH_AGENT_ID || loadHookEnv().agentId || stableAgentId();
|
|
3880
4386
|
var ORG_ID = process.env.MINDGRAPH_ORG_ID;
|
|
3881
4387
|
var PROFILE = process.env.MINDGRAPH_PROFILE || "general";
|
|
3882
4388
|
var HARNESS = process.env.MINDGRAPH_HARNESS || "generic";
|
|
@@ -3998,7 +4504,7 @@ if (PROFILE === "coding") {
|
|
|
3998
4504
|
var server = new import_server.Server(
|
|
3999
4505
|
{
|
|
4000
4506
|
name: "mindgraph",
|
|
4001
|
-
version: "0.
|
|
4507
|
+
version: "0.16.0"
|
|
4002
4508
|
},
|
|
4003
4509
|
{
|
|
4004
4510
|
capabilities: {
|
|
@@ -4212,7 +4718,7 @@ async function main() {
|
|
|
4212
4718
|
const transport = new import_stdio.StdioServerTransport();
|
|
4213
4719
|
await server.connect(transport);
|
|
4214
4720
|
console.error(
|
|
4215
|
-
`MindGraph MCP server
|
|
4721
|
+
`MindGraph MCP server v${"0.16.0"} running on stdio (${BASE_URL})`
|
|
4216
4722
|
);
|
|
4217
4723
|
}
|
|
4218
4724
|
main().catch((error) => {
|