mindgraph-mcp 0.13.4 → 0.14.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/LICENSE +1 -1
- package/README.md +71 -3
- package/dist/cli.js +3207 -494
- package/dist/index.js +2353 -359
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,22 +1,1438 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
2
24
|
|
|
3
25
|
// src/index.ts
|
|
4
26
|
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
5
27
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
6
28
|
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
29
|
+
var import_mindgraph2 = require("mindgraph");
|
|
30
|
+
|
|
31
|
+
// src/tools.ts
|
|
7
32
|
var import_mindgraph = require("mindgraph");
|
|
8
33
|
|
|
34
|
+
// src/codegraph.ts
|
|
35
|
+
var import_node_child_process = require("child_process");
|
|
36
|
+
var import_node_fs = require("fs");
|
|
37
|
+
var import_promises = require("fs/promises");
|
|
38
|
+
var import_node_os = require("os");
|
|
39
|
+
var import_node_path = require("path");
|
|
40
|
+
var import_node_crypto = require("crypto");
|
|
41
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
42
|
+
var DEFAULT_STATUS_TTL_MS = 6e4;
|
|
43
|
+
var MAX_OUTPUT_BYTES = 5 * 1024 * 1024;
|
|
44
|
+
function normalizePath(path2) {
|
|
45
|
+
return path2.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
46
|
+
}
|
|
47
|
+
function normalizeSignature(signature) {
|
|
48
|
+
return signature.replace(/\s+/g, " ").trim();
|
|
49
|
+
}
|
|
50
|
+
function languageFromPath(path2) {
|
|
51
|
+
const extension = (0, import_node_path.extname)(path2).toLowerCase();
|
|
52
|
+
return {
|
|
53
|
+
".c": "c",
|
|
54
|
+
".cc": "cpp",
|
|
55
|
+
".cpp": "cpp",
|
|
56
|
+
".go": "go",
|
|
57
|
+
".java": "java",
|
|
58
|
+
".js": "javascript",
|
|
59
|
+
".jsx": "javascript",
|
|
60
|
+
".kt": "kotlin",
|
|
61
|
+
".py": "python",
|
|
62
|
+
".rb": "ruby",
|
|
63
|
+
".rs": "rust",
|
|
64
|
+
".swift": "swift",
|
|
65
|
+
".ts": "typescript",
|
|
66
|
+
".tsx": "typescript"
|
|
67
|
+
}[extension] ?? "unknown";
|
|
68
|
+
}
|
|
69
|
+
function asNode(hit) {
|
|
70
|
+
return hit.node ?? hit;
|
|
71
|
+
}
|
|
72
|
+
function toCandidate(node) {
|
|
73
|
+
if (!node.filePath || !node.kind || !(node.qualifiedName || node.name)) return null;
|
|
74
|
+
return {
|
|
75
|
+
path: normalizePath(node.filePath),
|
|
76
|
+
language: node.language ?? languageFromPath(node.filePath),
|
|
77
|
+
kind: node.kind,
|
|
78
|
+
qualifiedName: node.qualifiedName ?? node.name ?? "",
|
|
79
|
+
signature: node.signature ?? null,
|
|
80
|
+
startLine: node.startLine ?? 1,
|
|
81
|
+
endLine: node.endLine ?? node.startLine ?? 1
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function normalizeRemote(remote) {
|
|
85
|
+
const trimmed = remote.trim();
|
|
86
|
+
if (!trimmed) return null;
|
|
87
|
+
const scp = /^(?:[^@]+@)?([^:]+):(.+)$/.exec(trimmed);
|
|
88
|
+
if (scp && !trimmed.includes("://")) {
|
|
89
|
+
return `${scp[1].toLowerCase()}/${scp[2].replace(/\.git$/, "")}`;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const url = new URL(trimmed);
|
|
93
|
+
const pathname = url.pathname.replace(/\/$/, "").replace(/\.git$/, "");
|
|
94
|
+
return `${url.host.toLowerCase()}${pathname}`;
|
|
95
|
+
} catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function pathContains(root, candidate) {
|
|
100
|
+
const child = (0, import_node_path.relative)(root, candidate);
|
|
101
|
+
return child === "" || child !== ".." && !child.startsWith(`..${import_node_path.sep}`) && !(0, import_node_path.isAbsolute)(child);
|
|
102
|
+
}
|
|
103
|
+
function nearestExistingDirectory(candidate) {
|
|
104
|
+
let current = (0, import_node_path.resolve)(candidate);
|
|
105
|
+
for (; ; ) {
|
|
106
|
+
if ((0, import_node_fs.existsSync)(current)) {
|
|
107
|
+
try {
|
|
108
|
+
return (0, import_node_fs.statSync)(current).isDirectory() ? current : (0, import_node_path.dirname)(current);
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const parent = (0, import_node_path.dirname)(current);
|
|
113
|
+
if (parent === current) return current;
|
|
114
|
+
current = parent;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function findWorkspaceFile(start, explicit) {
|
|
118
|
+
if (explicit) return (0, import_node_path.resolve)(start, explicit);
|
|
119
|
+
let current = (0, import_node_path.resolve)(start);
|
|
120
|
+
for (; ; ) {
|
|
121
|
+
const candidate = (0, import_node_path.join)(current, ".mindgraph", "workspace.json");
|
|
122
|
+
if ((0, import_node_fs.existsSync)(candidate)) return candidate;
|
|
123
|
+
const parent = (0, import_node_path.dirname)(current);
|
|
124
|
+
if (parent === current) return void 0;
|
|
125
|
+
current = parent;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async function runProcess(executable, args, timeoutMs, env, cwd) {
|
|
129
|
+
return await new Promise((resolvePromise, rejectPromise) => {
|
|
130
|
+
const child = (0, import_node_child_process.spawn)(executable, args, {
|
|
131
|
+
cwd,
|
|
132
|
+
env,
|
|
133
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
134
|
+
windowsHide: true
|
|
135
|
+
});
|
|
136
|
+
const stdout = [];
|
|
137
|
+
const stderr = [];
|
|
138
|
+
let outputBytes = 0;
|
|
139
|
+
let timedOut = false;
|
|
140
|
+
const timer = setTimeout(() => {
|
|
141
|
+
timedOut = true;
|
|
142
|
+
child.kill("SIGTERM");
|
|
143
|
+
setTimeout(() => child.kill("SIGKILL"), 250).unref();
|
|
144
|
+
}, timeoutMs);
|
|
145
|
+
timer.unref();
|
|
146
|
+
const collect = (target) => (chunk) => {
|
|
147
|
+
outputBytes += chunk.length;
|
|
148
|
+
if (outputBytes > MAX_OUTPUT_BYTES) {
|
|
149
|
+
child.kill("SIGTERM");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
target.push(chunk);
|
|
153
|
+
};
|
|
154
|
+
child.stdout.on("data", collect(stdout));
|
|
155
|
+
child.stderr.on("data", collect(stderr));
|
|
156
|
+
child.on("error", (cause) => {
|
|
157
|
+
clearTimeout(timer);
|
|
158
|
+
const error = new Error(cause.message);
|
|
159
|
+
error.kind = cause.code === "ENOENT" ? "absent" : "command_failed";
|
|
160
|
+
rejectPromise(error);
|
|
161
|
+
});
|
|
162
|
+
child.on("close", (code) => {
|
|
163
|
+
clearTimeout(timer);
|
|
164
|
+
if (timedOut) {
|
|
165
|
+
const error = new Error(`command timed out after ${timeoutMs}ms`);
|
|
166
|
+
error.kind = "timeout";
|
|
167
|
+
rejectPromise(error);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (outputBytes > MAX_OUTPUT_BYTES) {
|
|
171
|
+
const error = new Error("codegraph output exceeded 5 MiB");
|
|
172
|
+
error.kind = "command_failed";
|
|
173
|
+
rejectPromise(error);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (code !== 0) {
|
|
177
|
+
const error = new Error(Buffer.concat(stderr).toString("utf8").trim());
|
|
178
|
+
error.kind = "command_failed";
|
|
179
|
+
error.exitCode = code;
|
|
180
|
+
rejectPromise(error);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
resolvePromise(Buffer.concat(stdout).toString("utf8"));
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
var CodegraphAdapter = class {
|
|
188
|
+
timeoutMs;
|
|
189
|
+
statusTtlMs;
|
|
190
|
+
env;
|
|
191
|
+
cwd;
|
|
192
|
+
workspaceFile;
|
|
193
|
+
now;
|
|
194
|
+
identityCache = /* @__PURE__ */ new Map();
|
|
195
|
+
statusCache = /* @__PURE__ */ new Map();
|
|
196
|
+
constructor(options = {}) {
|
|
197
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
198
|
+
this.statusTtlMs = options.statusTtlMs ?? DEFAULT_STATUS_TTL_MS;
|
|
199
|
+
this.env = options.env ?? process.env;
|
|
200
|
+
this.cwd = options.cwd ?? process.cwd();
|
|
201
|
+
this.workspaceFile = findWorkspaceFile(
|
|
202
|
+
this.cwd,
|
|
203
|
+
options.workspaceFile ?? this.env.MINDGRAPH_WORKSPACE_FILE
|
|
204
|
+
);
|
|
205
|
+
this.now = options.now ?? Date.now;
|
|
206
|
+
}
|
|
207
|
+
async resolveRepository(repo, fallbackSpaceUid) {
|
|
208
|
+
const configured = await this.resolveRepositories(fallbackSpaceUid);
|
|
209
|
+
const selected = repo && configured.find((candidate) => candidate.repoId === repo) || repo && configured.find(
|
|
210
|
+
(candidate) => candidate.repoRoot === repo || (0, import_node_path.basename)(candidate.repoRoot) === repo
|
|
211
|
+
) || (!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);
|
|
212
|
+
if (selected) {
|
|
213
|
+
return !repo && this.env.MINDGRAPH_REPO_ID ? this.resolveRepositoryIdentity(
|
|
214
|
+
selected.repoRoot,
|
|
215
|
+
this.env.MINDGRAPH_REPO_ID,
|
|
216
|
+
selected.spaceUid
|
|
217
|
+
) : selected;
|
|
218
|
+
}
|
|
219
|
+
const requestedRoot = repo || this.env.MINDGRAPH_REPO_ROOT || this.cwd;
|
|
220
|
+
const requestedPath = await (0, import_promises.realpath)((0, import_node_path.resolve)(this.cwd, requestedRoot)).catch(
|
|
221
|
+
() => (0, import_node_path.resolve)(this.cwd, requestedRoot)
|
|
222
|
+
);
|
|
223
|
+
const repoRoot = await this.gitRoot(requestedPath) ?? requestedPath;
|
|
224
|
+
return this.resolveRepositoryIdentity(
|
|
225
|
+
repoRoot,
|
|
226
|
+
this.env.MINDGRAPH_REPO_ID,
|
|
227
|
+
this.env.MINDGRAPH_CODE_SPACE_UID || fallbackSpaceUid
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
async resolveRepositories(fallbackSpaceUid) {
|
|
231
|
+
return Promise.all(
|
|
232
|
+
this.configuredRepositories().map(
|
|
233
|
+
(repository) => this.resolveRepositoryIdentity(
|
|
234
|
+
repository.repoRoot,
|
|
235
|
+
repository.repoId,
|
|
236
|
+
repository.spaceUid || this.env.MINDGRAPH_CODE_SPACE_UID || fallbackSpaceUid
|
|
237
|
+
)
|
|
238
|
+
)
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
async resolveRepositoryIdentity(root, explicitRepoId, spaceUid) {
|
|
242
|
+
const repoRoot = await (0, import_promises.realpath)(root).catch(() => (0, import_node_path.resolve)(root));
|
|
243
|
+
const cacheKey = `${repoRoot}\0${explicitRepoId ?? ""}\0${spaceUid ?? ""}`;
|
|
244
|
+
const cached = this.identityCache.get(cacheKey);
|
|
245
|
+
if (cached) return cached;
|
|
246
|
+
const pending = this.loadRepositoryIdentity(repoRoot, explicitRepoId, spaceUid);
|
|
247
|
+
this.identityCache.set(cacheKey, pending);
|
|
248
|
+
return pending;
|
|
249
|
+
}
|
|
250
|
+
async loadRepositoryIdentity(repoRoot, explicitRepoId, spaceUid) {
|
|
251
|
+
const remote = await runProcess(
|
|
252
|
+
"git",
|
|
253
|
+
["-C", repoRoot, "config", "--get", "remote.origin.url"],
|
|
254
|
+
Math.min(this.timeoutMs, 2e3),
|
|
255
|
+
this.env
|
|
256
|
+
).catch(() => "");
|
|
257
|
+
const repoId = explicitRepoId || normalizeRemote(remote) || await this.persistedRepositoryId(repoRoot);
|
|
258
|
+
return {
|
|
259
|
+
repoId,
|
|
260
|
+
repoRoot,
|
|
261
|
+
spaceUid
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
async persistedRepositoryId(repoRoot) {
|
|
265
|
+
const rawCommonDir = await runProcess(
|
|
266
|
+
"git",
|
|
267
|
+
["-C", repoRoot, "rev-parse", "--git-common-dir"],
|
|
268
|
+
Math.min(this.timeoutMs, 2e3),
|
|
269
|
+
this.env
|
|
270
|
+
).catch(() => "");
|
|
271
|
+
if (!rawCommonDir.trim()) {
|
|
272
|
+
return `local:${(0, import_node_path.basename)(repoRoot)}`;
|
|
273
|
+
}
|
|
274
|
+
const commonDir = (0, import_node_path.resolve)(repoRoot, rawCommonDir.trim());
|
|
275
|
+
const identityDir = (0, import_node_path.join)(commonDir, "mindgraph");
|
|
276
|
+
const identityFile = (0, import_node_path.join)(identityDir, "repository-id");
|
|
277
|
+
try {
|
|
278
|
+
const existing = (0, import_node_fs.readFileSync)(identityFile, "utf8").trim();
|
|
279
|
+
if (existing) return existing;
|
|
280
|
+
} catch {
|
|
281
|
+
}
|
|
282
|
+
(0, import_node_fs.mkdirSync)(identityDir, { recursive: true, mode: 448 });
|
|
283
|
+
const generated = (0, import_node_crypto.randomUUID)();
|
|
284
|
+
try {
|
|
285
|
+
(0, import_node_fs.writeFileSync)(identityFile, `${generated}
|
|
286
|
+
`, {
|
|
287
|
+
encoding: "utf8",
|
|
288
|
+
flag: "wx",
|
|
289
|
+
mode: 384
|
|
290
|
+
});
|
|
291
|
+
return generated;
|
|
292
|
+
} catch (cause) {
|
|
293
|
+
if (cause.code !== "EEXIST") throw cause;
|
|
294
|
+
const winner = (0, import_node_fs.readFileSync)(identityFile, "utf8").trim();
|
|
295
|
+
if (!winner) throw new Error(`empty repository identity file: ${identityFile}`);
|
|
296
|
+
return winner;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
async gitRoot(candidate) {
|
|
300
|
+
const start = nearestExistingDirectory(candidate);
|
|
301
|
+
const raw = await runProcess(
|
|
302
|
+
"git",
|
|
303
|
+
["-C", start, "rev-parse", "--show-toplevel"],
|
|
304
|
+
Math.min(this.timeoutMs, 2e3),
|
|
305
|
+
this.env
|
|
306
|
+
).catch(() => "");
|
|
307
|
+
return raw.trim() ? (0, import_node_path.resolve)(start, raw.trim()) : void 0;
|
|
308
|
+
}
|
|
309
|
+
async availability(repository) {
|
|
310
|
+
const cached = this.statusCache.get(repository.repoRoot);
|
|
311
|
+
if (cached && cached.expiresAt > this.now()) return cached.availability;
|
|
312
|
+
try {
|
|
313
|
+
const status = await this.runJson([
|
|
314
|
+
"status",
|
|
315
|
+
repository.repoRoot,
|
|
316
|
+
"-j"
|
|
317
|
+
]);
|
|
318
|
+
const caveats = [];
|
|
319
|
+
let availability;
|
|
320
|
+
if (status.worktreeMismatch != null) {
|
|
321
|
+
availability = {
|
|
322
|
+
available: false,
|
|
323
|
+
reason: "wrong_index",
|
|
324
|
+
stale: false,
|
|
325
|
+
caveats: ["the index belongs to a different worktree"],
|
|
326
|
+
lastIndexed: status.lastIndexed
|
|
327
|
+
};
|
|
328
|
+
} else if (!status.initialized || status.index?.state !== "complete") {
|
|
329
|
+
availability = {
|
|
330
|
+
available: false,
|
|
331
|
+
reason: "index_incomplete",
|
|
332
|
+
stale: false,
|
|
333
|
+
caveats: [
|
|
334
|
+
!status.initialized ? "codegraph is not initialized for this repository" : `codegraph index state is ${status.index?.state ?? "unknown"}`
|
|
335
|
+
],
|
|
336
|
+
lastIndexed: status.lastIndexed
|
|
337
|
+
};
|
|
338
|
+
} else {
|
|
339
|
+
const pending = (status.pendingChanges?.added ?? 0) + (status.pendingChanges?.modified ?? 0) + (status.pendingChanges?.removed ?? 0);
|
|
340
|
+
if (pending > 0) caveats.push(`${pending} worktree changes are not indexed`);
|
|
341
|
+
if ((status.index?.pendingRefs ?? 0) > 0) {
|
|
342
|
+
caveats.push(`${status.index?.pendingRefs} references remain pending`);
|
|
343
|
+
}
|
|
344
|
+
if (status.index?.reindexRecommended) caveats.push("codegraph recommends reindexing");
|
|
345
|
+
availability = {
|
|
346
|
+
available: true,
|
|
347
|
+
stale: caveats.length > 0,
|
|
348
|
+
caveats,
|
|
349
|
+
lastIndexed: status.lastIndexed
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
this.statusCache.set(repository.repoRoot, {
|
|
353
|
+
status,
|
|
354
|
+
availability,
|
|
355
|
+
expiresAt: this.now() + this.statusTtlMs
|
|
356
|
+
});
|
|
357
|
+
return availability;
|
|
358
|
+
} catch (cause) {
|
|
359
|
+
const failure = cause;
|
|
360
|
+
return {
|
|
361
|
+
available: false,
|
|
362
|
+
reason: failure.kind ?? "command_failed",
|
|
363
|
+
stale: false,
|
|
364
|
+
caveats: [failure.message || "codegraph status failed"]
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
async resolveRefs(refs, options = {}) {
|
|
369
|
+
const normalized = refs.map(
|
|
370
|
+
(ref) => typeof ref === "string" ? { symbol: ref } : ref
|
|
371
|
+
);
|
|
372
|
+
const results = await Promise.all(
|
|
373
|
+
normalized.map(async (ref) => {
|
|
374
|
+
const repository2 = await this.repositoryForRef(
|
|
375
|
+
ref,
|
|
376
|
+
options.repo,
|
|
377
|
+
options.fallbackSpaceUid
|
|
378
|
+
);
|
|
379
|
+
const availability2 = await this.availability(repository2);
|
|
380
|
+
if (!availability2.available) {
|
|
381
|
+
return {
|
|
382
|
+
ref,
|
|
383
|
+
repository: repository2,
|
|
384
|
+
availability: availability2,
|
|
385
|
+
candidates: [],
|
|
386
|
+
error: availability2.reason
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
...await this.resolveOne(ref, repository2),
|
|
391
|
+
repository: repository2,
|
|
392
|
+
availability: availability2
|
|
393
|
+
};
|
|
394
|
+
})
|
|
395
|
+
);
|
|
396
|
+
const repositories = [
|
|
397
|
+
...new Map(
|
|
398
|
+
results.flatMap(
|
|
399
|
+
(result) => result.repository ? [[result.repository.repoRoot, result.repository]] : []
|
|
400
|
+
)
|
|
401
|
+
).values()
|
|
402
|
+
];
|
|
403
|
+
const repository = repositories[0] ?? await this.resolveRepository(options.repo, options.fallbackSpaceUid);
|
|
404
|
+
if (repositories.length === 0) repositories.push(repository);
|
|
405
|
+
const availabilities = results.flatMap(
|
|
406
|
+
(result) => result.availability ? [result.availability] : []
|
|
407
|
+
);
|
|
408
|
+
const availability = availabilities.length === 0 ? await this.availability(repository) : {
|
|
409
|
+
available: availabilities.every((item) => item.available),
|
|
410
|
+
reason: availabilities.find((item) => !item.available)?.reason,
|
|
411
|
+
stale: availabilities.some((item) => item.stale),
|
|
412
|
+
caveats: [
|
|
413
|
+
...new Set(availabilities.flatMap((item) => item.caveats))
|
|
414
|
+
]
|
|
415
|
+
};
|
|
416
|
+
return { repository, repositories, availability, results };
|
|
417
|
+
}
|
|
418
|
+
async repositoryForRef(ref, defaultRepo, fallbackSpaceUid) {
|
|
419
|
+
if (ref.repo || defaultRepo) {
|
|
420
|
+
return this.resolveRepository(ref.repo || defaultRepo, fallbackSpaceUid);
|
|
421
|
+
}
|
|
422
|
+
if (ref.path) {
|
|
423
|
+
const requested = (0, import_node_path.isAbsolute)(ref.path) ? (0, import_node_path.resolve)(ref.path) : (0, import_node_path.resolve)(this.cwd, ref.path);
|
|
424
|
+
const absolute = await (0, import_promises.realpath)(requested).catch(() => requested);
|
|
425
|
+
const configured = await this.resolveRepositories(fallbackSpaceUid);
|
|
426
|
+
const defining = [...configured].sort((a, b) => b.repoRoot.length - a.repoRoot.length).find((candidate) => pathContains(candidate.repoRoot, absolute));
|
|
427
|
+
if (defining) {
|
|
428
|
+
return this.env.MINDGRAPH_REPO_ID && pathContains(defining.repoRoot, this.cwd) ? this.resolveRepositoryIdentity(
|
|
429
|
+
defining.repoRoot,
|
|
430
|
+
this.env.MINDGRAPH_REPO_ID,
|
|
431
|
+
defining.spaceUid
|
|
432
|
+
) : defining;
|
|
433
|
+
}
|
|
434
|
+
const gitRoot = await this.gitRoot(absolute);
|
|
435
|
+
if (gitRoot) {
|
|
436
|
+
return this.resolveRepositoryIdentity(
|
|
437
|
+
gitRoot,
|
|
438
|
+
this.env.MINDGRAPH_REPO_ID,
|
|
439
|
+
this.env.MINDGRAPH_CODE_SPACE_UID || fallbackSpaceUid
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return this.resolveRepository(void 0, fallbackSpaceUid);
|
|
444
|
+
}
|
|
445
|
+
async expand(resolved, limit = 20) {
|
|
446
|
+
const repository = {
|
|
447
|
+
repoId: resolved.repoId,
|
|
448
|
+
repoRoot: resolved.repoRoot
|
|
449
|
+
};
|
|
450
|
+
const availability = await this.availability(repository);
|
|
451
|
+
if (!availability.available) {
|
|
452
|
+
return { availability, callers: [], callees: [], truncated: false };
|
|
453
|
+
}
|
|
454
|
+
const cap = Math.min(Math.max(limit, 1), 20);
|
|
455
|
+
const [callers, callees] = await Promise.all([
|
|
456
|
+
this.related("callers", resolved, cap + 1),
|
|
457
|
+
this.related("callees", resolved, cap + 1)
|
|
458
|
+
]);
|
|
459
|
+
return {
|
|
460
|
+
availability,
|
|
461
|
+
callers: callers.slice(0, cap),
|
|
462
|
+
callees: callees.slice(0, cap),
|
|
463
|
+
truncated: callers.length > cap || callees.length > cap
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
async affected(resolved, limit = 50) {
|
|
467
|
+
const repository = {
|
|
468
|
+
repoId: resolved.repoId,
|
|
469
|
+
repoRoot: resolved.repoRoot
|
|
470
|
+
};
|
|
471
|
+
const availability = await this.availability(repository);
|
|
472
|
+
if (!availability.available) {
|
|
473
|
+
return { availability, affected: [], truncated: false };
|
|
474
|
+
}
|
|
475
|
+
const cap = Math.min(Math.max(limit, 1), 50);
|
|
476
|
+
const raw = await this.runJson([
|
|
477
|
+
"impact",
|
|
478
|
+
"-p",
|
|
479
|
+
resolved.repoRoot,
|
|
480
|
+
"-j",
|
|
481
|
+
"-d",
|
|
482
|
+
"2",
|
|
483
|
+
resolved.qualifiedName
|
|
484
|
+
]);
|
|
485
|
+
const refs = (raw.affected ?? []).map((node) => ({
|
|
486
|
+
path: node.filePath,
|
|
487
|
+
line: node.startLine,
|
|
488
|
+
symbol: node.qualifiedName ?? node.name,
|
|
489
|
+
kind: node.kind
|
|
490
|
+
}));
|
|
491
|
+
const resolvedRefs = await this.resolveRelatedRefs(refs.slice(0, cap + 1), repository);
|
|
492
|
+
return {
|
|
493
|
+
availability,
|
|
494
|
+
affected: resolvedRefs.slice(0, cap),
|
|
495
|
+
truncated: refs.length > cap
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
configuredRepositories() {
|
|
499
|
+
const configured = [];
|
|
500
|
+
if (this.workspaceFile && (0, import_node_fs.existsSync)(this.workspaceFile)) {
|
|
501
|
+
try {
|
|
502
|
+
const parsed = JSON.parse(
|
|
503
|
+
(0, import_node_fs.readFileSync)(this.workspaceFile, "utf8")
|
|
504
|
+
);
|
|
505
|
+
if (parsed.v === 1 && Array.isArray(parsed.repositories)) {
|
|
506
|
+
const configDirectory = (0, import_node_path.dirname)(this.workspaceFile);
|
|
507
|
+
const base = (0, import_node_path.basename)(configDirectory) === ".mindgraph" ? (0, import_node_path.dirname)(configDirectory) : configDirectory;
|
|
508
|
+
for (const repository of parsed.repositories) {
|
|
509
|
+
if (!repository || typeof repository.root !== "string") continue;
|
|
510
|
+
configured.push({
|
|
511
|
+
repoId: typeof repository.repo_id === "string" ? repository.repo_id : void 0,
|
|
512
|
+
repoRoot: (0, import_node_path.resolve)(base, repository.root),
|
|
513
|
+
spaceUid: typeof repository.space_uid === "string" ? repository.space_uid : void 0
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
} catch {
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const raw = this.env.MINDGRAPH_CODE_REPOS;
|
|
521
|
+
if (raw) {
|
|
522
|
+
try {
|
|
523
|
+
const parsed = JSON.parse(raw);
|
|
524
|
+
for (const [repoId, config] of Object.entries(parsed)) {
|
|
525
|
+
configured.push({
|
|
526
|
+
repoId,
|
|
527
|
+
repoRoot: (0, import_node_path.resolve)(
|
|
528
|
+
this.cwd,
|
|
529
|
+
typeof config === "string" ? config : config.root
|
|
530
|
+
),
|
|
531
|
+
spaceUid: typeof config === "string" ? void 0 : config.space_uid
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
} catch {
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return [
|
|
538
|
+
...new Map(
|
|
539
|
+
configured.map((repository) => [repository.repoRoot, repository])
|
|
540
|
+
).values()
|
|
541
|
+
];
|
|
542
|
+
}
|
|
543
|
+
async resolveOne(ref, repository) {
|
|
544
|
+
const search = ref.symbol || ref.path;
|
|
545
|
+
if (!search || ref.line !== void 0 && (!Number.isInteger(ref.line) || ref.line < 1)) {
|
|
546
|
+
return { ref, candidates: [], error: "invalid_ref" };
|
|
547
|
+
}
|
|
548
|
+
try {
|
|
549
|
+
const args = ["query", "-p", repository.repoRoot, "-j", "-l", "50"];
|
|
550
|
+
if (ref.kind) args.push("-k", ref.kind);
|
|
551
|
+
args.push(search);
|
|
552
|
+
const hits = await this.runJson(args);
|
|
553
|
+
let candidates = hits.map(asNode).map(toCandidate).filter((item) => item !== null);
|
|
554
|
+
if (ref.path) {
|
|
555
|
+
const absolutePath = (0, import_node_path.isAbsolute)(ref.path) ? await (0, import_promises.realpath)(ref.path).catch(() => ref.path) : void 0;
|
|
556
|
+
const wanted = normalizePath(
|
|
557
|
+
absolutePath ? (0, import_node_path.relative)(repository.repoRoot, absolutePath) : ref.path
|
|
558
|
+
);
|
|
559
|
+
candidates = candidates.filter((candidate) => candidate.path === wanted);
|
|
560
|
+
}
|
|
561
|
+
if (ref.language) {
|
|
562
|
+
candidates = candidates.filter(
|
|
563
|
+
(candidate) => candidate.language.toLowerCase() === ref.language?.toLowerCase()
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
if (ref.kind) {
|
|
567
|
+
candidates = candidates.filter(
|
|
568
|
+
(candidate) => candidate.kind.toLowerCase() === ref.kind?.toLowerCase()
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
if (ref.symbol) {
|
|
572
|
+
const exact = candidates.filter((candidate) => {
|
|
573
|
+
const tail = candidate.qualifiedName.split(/::|\.|#/).at(-1);
|
|
574
|
+
return candidate.qualifiedName === ref.symbol || tail === ref.symbol;
|
|
575
|
+
});
|
|
576
|
+
if (exact.length > 0) candidates = exact;
|
|
577
|
+
}
|
|
578
|
+
if (ref.signature) {
|
|
579
|
+
const signature = normalizeSignature(ref.signature);
|
|
580
|
+
candidates = candidates.filter(
|
|
581
|
+
(candidate) => candidate.signature !== null && normalizeSignature(candidate.signature) === signature
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
if (ref.line !== void 0) {
|
|
585
|
+
candidates = candidates.filter(
|
|
586
|
+
(candidate) => candidate.startLine <= ref.line && candidate.endLine >= ref.line
|
|
587
|
+
);
|
|
588
|
+
if (candidates.length > 1) {
|
|
589
|
+
const narrowest = Math.min(
|
|
590
|
+
...candidates.map((candidate) => candidate.endLine - candidate.startLine)
|
|
591
|
+
);
|
|
592
|
+
const narrowed = candidates.filter(
|
|
593
|
+
(candidate) => candidate.endLine - candidate.startLine === narrowest
|
|
594
|
+
);
|
|
595
|
+
if (narrowed.length === 1) candidates = narrowed;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
if (candidates.length !== 1) {
|
|
599
|
+
return {
|
|
600
|
+
ref,
|
|
601
|
+
candidates,
|
|
602
|
+
error: candidates.length === 0 ? "not_found" : "ambiguous"
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
return {
|
|
606
|
+
ref,
|
|
607
|
+
candidates,
|
|
608
|
+
resolved: {
|
|
609
|
+
repoId: repository.repoId,
|
|
610
|
+
repoRoot: repository.repoRoot,
|
|
611
|
+
...candidates[0]
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
} catch (cause) {
|
|
615
|
+
const failure = cause;
|
|
616
|
+
return {
|
|
617
|
+
ref,
|
|
618
|
+
candidates: [],
|
|
619
|
+
error: failure.kind ?? "command_failed"
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
async related(action, resolved, limit) {
|
|
624
|
+
const raw = await this.runJson([
|
|
625
|
+
action,
|
|
626
|
+
"-p",
|
|
627
|
+
resolved.repoRoot,
|
|
628
|
+
"-j",
|
|
629
|
+
"-l",
|
|
630
|
+
String(limit),
|
|
631
|
+
resolved.qualifiedName
|
|
632
|
+
]);
|
|
633
|
+
const refs = (raw[action] ?? []).map((node) => ({
|
|
634
|
+
path: node.filePath,
|
|
635
|
+
line: node.startLine,
|
|
636
|
+
symbol: node.qualifiedName ?? node.name,
|
|
637
|
+
kind: node.kind
|
|
638
|
+
}));
|
|
639
|
+
return this.resolveRelatedRefs(refs, {
|
|
640
|
+
repoId: resolved.repoId,
|
|
641
|
+
repoRoot: resolved.repoRoot
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
async resolveRelatedRefs(refs, repository) {
|
|
645
|
+
const results = await Promise.all(refs.map((ref) => this.resolveOne(ref, repository)));
|
|
646
|
+
const seen = /* @__PURE__ */ new Set();
|
|
647
|
+
return results.flatMap((result) => result.resolved ? [result.resolved] : []).filter((result) => {
|
|
648
|
+
const key = `${result.path}:${result.qualifiedName}:${result.startLine}`;
|
|
649
|
+
if (seen.has(key)) return false;
|
|
650
|
+
seen.add(key);
|
|
651
|
+
return true;
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
async runJson(args) {
|
|
655
|
+
const candidates = [
|
|
656
|
+
this.env.MINDGRAPH_CODEGRAPH_BIN,
|
|
657
|
+
"codegraph",
|
|
658
|
+
(0, import_node_path.resolve)((0, import_node_os.homedir)(), ".local", "bin", "codegraph")
|
|
659
|
+
].filter((candidate, index, all) => {
|
|
660
|
+
return Boolean(candidate) && all.indexOf(candidate) === index;
|
|
661
|
+
});
|
|
662
|
+
let lastFailure;
|
|
663
|
+
for (const executable of candidates) {
|
|
664
|
+
try {
|
|
665
|
+
const stdout = await runProcess(
|
|
666
|
+
executable,
|
|
667
|
+
args,
|
|
668
|
+
this.timeoutMs,
|
|
669
|
+
this.env,
|
|
670
|
+
this.cwd
|
|
671
|
+
);
|
|
672
|
+
return JSON.parse(stdout);
|
|
673
|
+
} catch (cause) {
|
|
674
|
+
const failure = cause;
|
|
675
|
+
lastFailure = failure;
|
|
676
|
+
if (failure.kind !== "absent") throw failure;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
throw lastFailure ?? Object.assign(new Error("codegraph executable not found"), {
|
|
680
|
+
kind: "absent"
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
function repositoryIdentityKey(repository) {
|
|
685
|
+
return { v: 1, kind: "repository", repo_id: repository.repoId };
|
|
686
|
+
}
|
|
687
|
+
function codeRefIdentityKey(ref) {
|
|
688
|
+
if (ref.kind === "file") {
|
|
689
|
+
return {
|
|
690
|
+
v: 1,
|
|
691
|
+
kind: "file",
|
|
692
|
+
repo_id: ref.repoId,
|
|
693
|
+
path: ref.path
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
return {
|
|
697
|
+
v: 1,
|
|
698
|
+
kind: "symbol",
|
|
699
|
+
repo_id: ref.repoId,
|
|
700
|
+
language: ref.language,
|
|
701
|
+
symbol_kind: ref.kind,
|
|
702
|
+
qualified_name: ref.qualifiedName,
|
|
703
|
+
disambiguator: {
|
|
704
|
+
signature: ref.signature ?? "",
|
|
705
|
+
path: ref.path,
|
|
706
|
+
start_line: ref.startLine,
|
|
707
|
+
end_line: ref.endLine
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// src/code-tool.ts
|
|
713
|
+
var ok = (data) => ({
|
|
714
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
715
|
+
});
|
|
716
|
+
var err = (code, message, details) => ({
|
|
717
|
+
content: [
|
|
718
|
+
{
|
|
719
|
+
type: "text",
|
|
720
|
+
text: JSON.stringify({ error: message, code, details }, null, 2)
|
|
721
|
+
}
|
|
722
|
+
],
|
|
723
|
+
isError: true
|
|
724
|
+
});
|
|
725
|
+
var CODE_TOOL = {
|
|
726
|
+
name: "mindgraph_code",
|
|
727
|
+
description: "Resolve typed code references through the local codegraph index and join them to persistent MindGraph memory/work. anchor creates exact repository/code anchors; recall reads attached graph context; expand reads callers/callees; affected reads impact for a code ref or Task/Execution. Ambiguous refs return candidates without writing.",
|
|
728
|
+
inputSchema: {
|
|
729
|
+
type: "object",
|
|
730
|
+
properties: {
|
|
731
|
+
action: {
|
|
732
|
+
type: "string",
|
|
733
|
+
enum: ["anchor", "recall", "expand", "affected"]
|
|
734
|
+
},
|
|
735
|
+
code_refs: {
|
|
736
|
+
type: "array",
|
|
737
|
+
maxItems: 50,
|
|
738
|
+
items: {
|
|
739
|
+
oneOf: [
|
|
740
|
+
{ type: "string" },
|
|
741
|
+
{
|
|
742
|
+
type: "object",
|
|
743
|
+
properties: {
|
|
744
|
+
repo: { type: "string" },
|
|
745
|
+
path: { type: "string" },
|
|
746
|
+
line: { type: "integer", minimum: 1 },
|
|
747
|
+
symbol: { type: "string" },
|
|
748
|
+
kind: { type: "string" },
|
|
749
|
+
language: { type: "string" },
|
|
750
|
+
signature: { type: "string" }
|
|
751
|
+
},
|
|
752
|
+
additionalProperties: false
|
|
753
|
+
}
|
|
754
|
+
]
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
anchor_uids: {
|
|
758
|
+
type: "array",
|
|
759
|
+
maxItems: 50,
|
|
760
|
+
items: { type: "string" },
|
|
761
|
+
description: "Already-materialized code anchor UIDs (codegraph is not required for recall)"
|
|
762
|
+
},
|
|
763
|
+
repo: {
|
|
764
|
+
type: "string",
|
|
765
|
+
description: "Configured repository id or repository root"
|
|
766
|
+
},
|
|
767
|
+
repo_space_uid: {
|
|
768
|
+
type: "string",
|
|
769
|
+
description: "Writable shared Project/repository Space for new anchors"
|
|
770
|
+
},
|
|
771
|
+
task_uid: { type: "string" },
|
|
772
|
+
execution_uid: { type: "string" },
|
|
773
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
774
|
+
agent_id: { type: "string" }
|
|
775
|
+
},
|
|
776
|
+
required: ["action"]
|
|
777
|
+
},
|
|
778
|
+
annotations: {
|
|
779
|
+
readOnlyHint: false,
|
|
780
|
+
destructiveHint: false,
|
|
781
|
+
idempotentHint: true,
|
|
782
|
+
openWorldHint: true
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
async function handleCodeTool(client2, args, adapter = new CodegraphAdapter()) {
|
|
786
|
+
const codeClient = client2;
|
|
787
|
+
const action = args.action;
|
|
788
|
+
const refs = Array.isArray(args.code_refs) ? args.code_refs : [];
|
|
789
|
+
const anchorUids = Array.isArray(args.anchor_uids) ? args.anchor_uids : [];
|
|
790
|
+
const agentId = typeof args.agent_id === "string" ? args.agent_id : "mcp";
|
|
791
|
+
const fallbackSpaceUid = typeof args.repo_space_uid === "string" ? args.repo_space_uid : `space:agent:${agentId}`;
|
|
792
|
+
const options = {
|
|
793
|
+
repo: typeof args.repo === "string" ? args.repo : void 0,
|
|
794
|
+
fallbackSpaceUid
|
|
795
|
+
};
|
|
796
|
+
const limit = typeof args.limit === "number" ? args.limit : void 0;
|
|
797
|
+
try {
|
|
798
|
+
switch (action) {
|
|
799
|
+
case "anchor": {
|
|
800
|
+
if (refs.length === 0) return err("missing_code_refs", "code_refs is required for anchor");
|
|
801
|
+
const resolution = await adapter.resolveRefs(refs, options);
|
|
802
|
+
const resolved = resolution.results.flatMap(
|
|
803
|
+
(result) => result.resolved && result.repository ? [{ ref: result.resolved, repository: result.repository }] : []
|
|
804
|
+
);
|
|
805
|
+
const unresolved = resolution.results.filter((result) => !result.resolved);
|
|
806
|
+
if (resolved.length === 0) {
|
|
807
|
+
return ok({ ...resolution, anchors: [], wrote: false });
|
|
808
|
+
}
|
|
809
|
+
const missingSpace = resolved.find(
|
|
810
|
+
({ repository }) => typeof args.repo_space_uid !== "string" && !repository.spaceUid
|
|
811
|
+
);
|
|
812
|
+
if (missingSpace) {
|
|
813
|
+
return err(
|
|
814
|
+
"missing_repository_space",
|
|
815
|
+
`repo_space_uid, a workspace space_uid, or MINDGRAPH_CODE_SPACE_UID is required for ${missingSpace.repository.repoId}`
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
const repositoryAnchors = /* @__PURE__ */ new Map();
|
|
819
|
+
for (const { repository } of resolved) {
|
|
820
|
+
if (repositoryAnchors.has(repository.repoRoot)) continue;
|
|
821
|
+
const spaceUid = typeof args.repo_space_uid === "string" ? args.repo_space_uid : repository.spaceUid;
|
|
822
|
+
const anchored = await anchorRepository(
|
|
823
|
+
codeClient,
|
|
824
|
+
repository,
|
|
825
|
+
spaceUid,
|
|
826
|
+
agentId
|
|
827
|
+
);
|
|
828
|
+
repositoryAnchors.set(repository.repoRoot, {
|
|
829
|
+
...anchored,
|
|
830
|
+
repository
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
const anchors = [];
|
|
834
|
+
for (const { ref, repository } of resolved) {
|
|
835
|
+
const repositoryAnchor = repositoryAnchors.get(repository.repoRoot);
|
|
836
|
+
if (!repositoryAnchor.uid) continue;
|
|
837
|
+
const spaceUid = typeof args.repo_space_uid === "string" ? args.repo_space_uid : repository.spaceUid;
|
|
838
|
+
const anchor = await anchorCodeRef(
|
|
839
|
+
codeClient,
|
|
840
|
+
ref,
|
|
841
|
+
repositoryAnchor.uid,
|
|
842
|
+
spaceUid,
|
|
843
|
+
agentId
|
|
844
|
+
);
|
|
845
|
+
anchors.push(anchor);
|
|
846
|
+
}
|
|
847
|
+
return ok({
|
|
848
|
+
availability: resolution.availability,
|
|
849
|
+
repository: repositoryAnchors.values().next().value,
|
|
850
|
+
repositories: [...repositoryAnchors.values()],
|
|
851
|
+
anchors,
|
|
852
|
+
unresolved,
|
|
853
|
+
wrote: anchors.some((anchor) => anchor.created)
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
case "recall": {
|
|
857
|
+
const materialized = new Set(anchorUids);
|
|
858
|
+
let resolution;
|
|
859
|
+
if (refs.length > 0) {
|
|
860
|
+
resolution = await adapter.resolveRefs(refs, options);
|
|
861
|
+
for (const result of resolution.results) {
|
|
862
|
+
if (!result.resolved) continue;
|
|
863
|
+
const uid = await resolveMaterializedUid(codeClient, result.resolved, agentId);
|
|
864
|
+
if (uid) materialized.add(uid);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (materialized.size === 0) {
|
|
868
|
+
return ok({
|
|
869
|
+
availability: resolution?.availability,
|
|
870
|
+
anchors: [],
|
|
871
|
+
context: [],
|
|
872
|
+
unresolved: resolution?.results.filter((result) => !result.resolved) ?? []
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
const context = await Promise.all(
|
|
876
|
+
[...materialized].map(async (uid) => ({
|
|
877
|
+
anchor_uid: uid,
|
|
878
|
+
graph: await codeClient.traverse({
|
|
879
|
+
action: "neighborhood",
|
|
880
|
+
start_uid: uid,
|
|
881
|
+
depth: 2,
|
|
882
|
+
max_nodes: Math.min(limit ?? 20, 20)
|
|
883
|
+
})
|
|
884
|
+
}))
|
|
885
|
+
);
|
|
886
|
+
return ok({
|
|
887
|
+
availability: resolution?.availability,
|
|
888
|
+
anchors: [...materialized],
|
|
889
|
+
context,
|
|
890
|
+
unresolved: resolution?.results.filter((result) => !result.resolved) ?? []
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
case "expand": {
|
|
894
|
+
const target = await resolveSingleTarget(
|
|
895
|
+
codeClient,
|
|
896
|
+
adapter,
|
|
897
|
+
refs,
|
|
898
|
+
anchorUids,
|
|
899
|
+
options
|
|
900
|
+
);
|
|
901
|
+
if ("error" in target) return target.error;
|
|
902
|
+
const expanded = await adapter.expand(target.ref, limit);
|
|
903
|
+
const joined = await joinMaterialized(
|
|
904
|
+
codeClient,
|
|
905
|
+
[...expanded.callers, ...expanded.callees],
|
|
906
|
+
agentId,
|
|
907
|
+
Math.min(limit ?? 20, 20)
|
|
908
|
+
);
|
|
909
|
+
return ok({ anchor: target.ref, ...expanded, materialized: joined });
|
|
910
|
+
}
|
|
911
|
+
case "affected": {
|
|
912
|
+
const targetUid = typeof args.task_uid === "string" ? args.task_uid : typeof args.execution_uid === "string" ? args.execution_uid : void 0;
|
|
913
|
+
let graphContext;
|
|
914
|
+
const graphRefs = [];
|
|
915
|
+
if (targetUid) {
|
|
916
|
+
graphContext = await codeClient.traverse({
|
|
917
|
+
action: "neighborhood",
|
|
918
|
+
start_uid: targetUid,
|
|
919
|
+
depth: 2,
|
|
920
|
+
max_nodes: 50
|
|
921
|
+
});
|
|
922
|
+
graphRefs.push(
|
|
923
|
+
...await codeRefsFromGraphContext(codeClient, adapter, graphContext)
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
if (refs.length > 0 || anchorUids.length > 0) {
|
|
927
|
+
const target = await resolveSingleTarget(
|
|
928
|
+
codeClient,
|
|
929
|
+
adapter,
|
|
930
|
+
refs,
|
|
931
|
+
anchorUids,
|
|
932
|
+
options
|
|
933
|
+
);
|
|
934
|
+
if ("error" in target) return target.error;
|
|
935
|
+
graphRefs.unshift(target.ref);
|
|
936
|
+
}
|
|
937
|
+
const unique = uniqueRefs(graphRefs);
|
|
938
|
+
if (unique.length === 0) {
|
|
939
|
+
return ok({
|
|
940
|
+
target_uid: targetUid,
|
|
941
|
+
graph_context: graphContext,
|
|
942
|
+
affected: [],
|
|
943
|
+
degradation: "no materialized code anchors were attached"
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
const impacts = await Promise.all(
|
|
947
|
+
unique.map(async (ref) => ({ anchor: ref, ...await adapter.affected(ref, limit) }))
|
|
948
|
+
);
|
|
949
|
+
const structural = uniqueRefs(impacts.flatMap((impact) => impact.affected));
|
|
950
|
+
const joined = await joinMaterialized(
|
|
951
|
+
codeClient,
|
|
952
|
+
structural,
|
|
953
|
+
agentId,
|
|
954
|
+
Math.min(limit ?? 50, 50)
|
|
955
|
+
);
|
|
956
|
+
return ok({
|
|
957
|
+
target_uid: targetUid,
|
|
958
|
+
graph_context: graphContext,
|
|
959
|
+
impacts,
|
|
960
|
+
materialized: joined
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
default:
|
|
964
|
+
return err("unknown_action", `unknown mindgraph_code action: ${action}`);
|
|
965
|
+
}
|
|
966
|
+
} catch (cause) {
|
|
967
|
+
return err(
|
|
968
|
+
"code_tool_failed",
|
|
969
|
+
cause instanceof Error ? cause.message : String(cause)
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
async function attachCodeRefsToToolResult(client2, result, args, adapter) {
|
|
974
|
+
if (result.isError || !Array.isArray(args.code_refs) || args.code_refs.length === 0) {
|
|
975
|
+
return result;
|
|
976
|
+
}
|
|
977
|
+
const original = JSON.parse(result.content[0]?.text ?? "null");
|
|
978
|
+
const targetUids = primaryUids(original);
|
|
979
|
+
if (targetUids.length === 0) {
|
|
980
|
+
return ok({
|
|
981
|
+
result: original,
|
|
982
|
+
code_anchors: {
|
|
983
|
+
wrote: false,
|
|
984
|
+
caveat: "the write response exposed no primary UID to attach"
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
const anchorResult = await handleCodeTool(
|
|
989
|
+
client2,
|
|
990
|
+
{
|
|
991
|
+
action: "anchor",
|
|
992
|
+
code_refs: args.code_refs,
|
|
993
|
+
repo: args.repo,
|
|
994
|
+
repo_space_uid: args.repo_space_uid,
|
|
995
|
+
agent_id: args.agent_id
|
|
996
|
+
},
|
|
997
|
+
adapter
|
|
998
|
+
);
|
|
999
|
+
const anchors = JSON.parse(anchorResult.content[0]?.text ?? "null");
|
|
1000
|
+
if (!anchorResult.isError) {
|
|
1001
|
+
const codeClient = client2;
|
|
1002
|
+
const anchorUids = (anchors.anchors ?? []).flatMap(
|
|
1003
|
+
(anchor) => anchor.uid ? [anchor.uid] : []
|
|
1004
|
+
);
|
|
1005
|
+
for (const targetUid of targetUids) {
|
|
1006
|
+
for (const anchorUid of anchorUids) {
|
|
1007
|
+
await codeClient.entity({
|
|
1008
|
+
action: "relate",
|
|
1009
|
+
source_uid: targetUid,
|
|
1010
|
+
target_uid: anchorUid,
|
|
1011
|
+
edge_type: args.action === "lesson" ? "ABOUT" : "RELEVANT_TO",
|
|
1012
|
+
agent_id: args.agent_id
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
return ok({ result: original, code_anchors: anchors });
|
|
1018
|
+
}
|
|
1019
|
+
async function anchorRepository(client2, repository, spaceUid, agentId) {
|
|
1020
|
+
return client2.entity({
|
|
1021
|
+
action: "create",
|
|
1022
|
+
label: repository.repoId,
|
|
1023
|
+
props: {
|
|
1024
|
+
entity_type: "repository",
|
|
1025
|
+
canonical_name: repository.repoId,
|
|
1026
|
+
identifiers: { repo_id: repository.repoId }
|
|
1027
|
+
},
|
|
1028
|
+
identity: {
|
|
1029
|
+
namespace: "external.code",
|
|
1030
|
+
key_version: 1,
|
|
1031
|
+
key: repositoryIdentityKey(repository)
|
|
1032
|
+
},
|
|
1033
|
+
identity_space_uid: spaceUid,
|
|
1034
|
+
agent_id: agentId
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
async function anchorCodeRef(client2, ref, repositoryUid, spaceUid, agentId) {
|
|
1038
|
+
const response = await client2.entity({
|
|
1039
|
+
action: "create",
|
|
1040
|
+
label: ref.qualifiedName,
|
|
1041
|
+
props: {
|
|
1042
|
+
entity_type: ref.kind === "file" ? "code_file" : "code_symbol",
|
|
1043
|
+
canonical_name: ref.qualifiedName,
|
|
1044
|
+
identifiers: {
|
|
1045
|
+
repo_id: ref.repoId,
|
|
1046
|
+
path: ref.path,
|
|
1047
|
+
qualified_name: ref.qualifiedName
|
|
1048
|
+
},
|
|
1049
|
+
attributes: { code_ref: storedCodeRef(ref) }
|
|
1050
|
+
},
|
|
1051
|
+
identity: {
|
|
1052
|
+
namespace: "external.code",
|
|
1053
|
+
key_version: 1,
|
|
1054
|
+
key: codeRefIdentityKey(ref)
|
|
1055
|
+
},
|
|
1056
|
+
identity_space_uid: spaceUid,
|
|
1057
|
+
agent_id: agentId
|
|
1058
|
+
});
|
|
1059
|
+
if (response.uid) {
|
|
1060
|
+
await client2.entity({
|
|
1061
|
+
action: "relate",
|
|
1062
|
+
source_uid: response.uid,
|
|
1063
|
+
target_uid: repositoryUid,
|
|
1064
|
+
edge_type: "PART_OF",
|
|
1065
|
+
agent_id: agentId
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
return { ...response, ref };
|
|
1069
|
+
}
|
|
1070
|
+
async function resolveMaterializedUid(client2, ref, agentId) {
|
|
1071
|
+
const response = await client2.entity({
|
|
1072
|
+
action: "resolve_identity",
|
|
1073
|
+
identity: {
|
|
1074
|
+
namespace: "external.code",
|
|
1075
|
+
key_version: 1,
|
|
1076
|
+
key: codeRefIdentityKey(ref)
|
|
1077
|
+
},
|
|
1078
|
+
agent_id: agentId
|
|
1079
|
+
});
|
|
1080
|
+
return response.status === "existing" ? response.uid : void 0;
|
|
1081
|
+
}
|
|
1082
|
+
async function codeRefFromAnchor(client2, adapter, uid) {
|
|
1083
|
+
const node = await client2.getNode(uid);
|
|
1084
|
+
const attributes = node.props?.attributes;
|
|
1085
|
+
if (!attributes || typeof attributes !== "object") return void 0;
|
|
1086
|
+
const codeRef = attributes.code_ref;
|
|
1087
|
+
if (!isStoredCodeRef(codeRef)) return void 0;
|
|
1088
|
+
const repository = await adapter.resolveRepository(codeRef.repoId);
|
|
1089
|
+
return { ...codeRef, repoRoot: repository.repoRoot };
|
|
1090
|
+
}
|
|
1091
|
+
async function resolveSingleTarget(client2, adapter, refs, anchorUids, options) {
|
|
1092
|
+
if (refs.length > 1 || anchorUids.length > 1) {
|
|
1093
|
+
return { error: err("ambiguous_target", "expand/affected accepts exactly one code target") };
|
|
1094
|
+
}
|
|
1095
|
+
if (anchorUids.length === 1) {
|
|
1096
|
+
const ref = await codeRefFromAnchor(client2, adapter, anchorUids[0]);
|
|
1097
|
+
return ref ? { ref } : { error: err("invalid_anchor", "anchor UID does not contain a typed code_ref") };
|
|
1098
|
+
}
|
|
1099
|
+
if (refs.length !== 1) {
|
|
1100
|
+
return { error: err("missing_code_ref", "one code_ref or anchor_uid is required") };
|
|
1101
|
+
}
|
|
1102
|
+
const resolution = await adapter.resolveRefs(refs, options);
|
|
1103
|
+
const result = resolution.results[0];
|
|
1104
|
+
if (!result.resolved) {
|
|
1105
|
+
return {
|
|
1106
|
+
error: ok({
|
|
1107
|
+
availability: resolution.availability,
|
|
1108
|
+
result,
|
|
1109
|
+
wrote: false
|
|
1110
|
+
})
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
return { ref: result.resolved };
|
|
1114
|
+
}
|
|
1115
|
+
async function joinMaterialized(client2, refs, agentId, limit) {
|
|
1116
|
+
const joined = [];
|
|
1117
|
+
for (const ref of uniqueRefs(refs).slice(0, limit)) {
|
|
1118
|
+
const uid = await resolveMaterializedUid(client2, ref, agentId);
|
|
1119
|
+
if (!uid) continue;
|
|
1120
|
+
joined.push({
|
|
1121
|
+
ref,
|
|
1122
|
+
anchor_uid: uid,
|
|
1123
|
+
graph: await client2.traverse({
|
|
1124
|
+
action: "neighborhood",
|
|
1125
|
+
start_uid: uid,
|
|
1126
|
+
depth: 2,
|
|
1127
|
+
max_nodes: 20
|
|
1128
|
+
})
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
return joined;
|
|
1132
|
+
}
|
|
1133
|
+
async function codeRefsFromGraphContext(client2, adapter, context) {
|
|
1134
|
+
const uids = /* @__PURE__ */ new Set();
|
|
1135
|
+
collectUids(context, uids);
|
|
1136
|
+
if (uids.size === 0) return [];
|
|
1137
|
+
const nodes = client2.getNodesBatch ? await client2.getNodesBatch([...uids]) : await Promise.all([...uids].map((uid) => client2.getNode(uid).catch(() => void 0)));
|
|
1138
|
+
const stored = nodes.flatMap((node) => {
|
|
1139
|
+
if (!node) return [];
|
|
1140
|
+
const attributes = node.props?.attributes;
|
|
1141
|
+
if (!attributes || typeof attributes !== "object") return [];
|
|
1142
|
+
const codeRef = attributes.code_ref;
|
|
1143
|
+
return isStoredCodeRef(codeRef) ? [codeRef] : [];
|
|
1144
|
+
});
|
|
1145
|
+
return Promise.all(
|
|
1146
|
+
stored.map(async (ref) => {
|
|
1147
|
+
const repository = await adapter.resolveRepository(ref.repoId);
|
|
1148
|
+
return { ...ref, repoRoot: repository.repoRoot };
|
|
1149
|
+
})
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
function collectUids(value, uids) {
|
|
1153
|
+
if (Array.isArray(value)) {
|
|
1154
|
+
value.forEach((item) => collectUids(item, uids));
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
if (!value || typeof value !== "object") return;
|
|
1158
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1159
|
+
if (typeof child === "string" && ["uid", "node_uid", "neighbor_uid", "from_uid", "to_uid"].includes(key)) {
|
|
1160
|
+
uids.add(child);
|
|
1161
|
+
} else {
|
|
1162
|
+
collectUids(child, uids);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
function isStoredCodeRef(value) {
|
|
1167
|
+
if (!value || typeof value !== "object") return false;
|
|
1168
|
+
const ref = value;
|
|
1169
|
+
return typeof ref.repoId === "string" && typeof ref.path === "string" && typeof ref.language === "string" && typeof ref.kind === "string" && typeof ref.qualifiedName === "string" && (typeof ref.signature === "string" || ref.signature === null) && typeof ref.startLine === "number" && typeof ref.endLine === "number";
|
|
1170
|
+
}
|
|
1171
|
+
function storedCodeRef(ref) {
|
|
1172
|
+
const { repoRoot: _localOnly, ...stored } = ref;
|
|
1173
|
+
return stored;
|
|
1174
|
+
}
|
|
1175
|
+
function uniqueRefs(refs) {
|
|
1176
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1177
|
+
return refs.filter((ref) => {
|
|
1178
|
+
const key = `${ref.repoId}:${ref.path}:${ref.qualifiedName}:${ref.signature ?? ""}`;
|
|
1179
|
+
if (seen.has(key)) return false;
|
|
1180
|
+
seen.add(key);
|
|
1181
|
+
return true;
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
function primaryUids(value) {
|
|
1185
|
+
if (!value || typeof value !== "object") return [];
|
|
1186
|
+
const object = value;
|
|
1187
|
+
const keys = [
|
|
1188
|
+
"uid",
|
|
1189
|
+
"claim_uid",
|
|
1190
|
+
"goal_uid",
|
|
1191
|
+
"project_uid",
|
|
1192
|
+
"milestone_uid",
|
|
1193
|
+
"decision_uid",
|
|
1194
|
+
"task_uid",
|
|
1195
|
+
"plan_uid",
|
|
1196
|
+
"step_uid",
|
|
1197
|
+
"execution_uid",
|
|
1198
|
+
"lesson_uid"
|
|
1199
|
+
];
|
|
1200
|
+
return [
|
|
1201
|
+
...new Set(
|
|
1202
|
+
keys.flatMap((key) => typeof object[key] === "string" ? [object[key]] : [])
|
|
1203
|
+
)
|
|
1204
|
+
];
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// src/sync-tool.ts
|
|
1208
|
+
var import_node_crypto2 = __toESM(require("crypto"));
|
|
1209
|
+
var import_node_fs2 = __toESM(require("fs"));
|
|
1210
|
+
var import_node_path2 = __toESM(require("path"));
|
|
1211
|
+
var ok2 = (data) => ({
|
|
1212
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1213
|
+
});
|
|
1214
|
+
var err2 = (code, message) => ({
|
|
1215
|
+
content: [{ type: "text", text: JSON.stringify({ code, error: message }) }],
|
|
1216
|
+
isError: true
|
|
1217
|
+
});
|
|
1218
|
+
var SYNC_TOOL = {
|
|
1219
|
+
name: "mindgraph_sync",
|
|
1220
|
+
description: "Transactional one-way project memory-file sync. scan/status are reads; begin/record/finalize/abandon are writes. Discovery and hashing are automatic; semantic assertions are deliberately supplied through record. This never writes graph state back to files.",
|
|
1221
|
+
annotations: {
|
|
1222
|
+
readOnlyHint: false,
|
|
1223
|
+
destructiveHint: false,
|
|
1224
|
+
idempotentHint: true,
|
|
1225
|
+
openWorldHint: true
|
|
1226
|
+
},
|
|
1227
|
+
inputSchema: {
|
|
1228
|
+
type: "object",
|
|
1229
|
+
properties: {
|
|
1230
|
+
action: {
|
|
1231
|
+
type: "string",
|
|
1232
|
+
enum: ["scan", "begin", "record", "finalize", "status", "abandon"]
|
|
1233
|
+
},
|
|
1234
|
+
paths: { type: "array", items: { type: "string" } },
|
|
1235
|
+
workspace: {
|
|
1236
|
+
type: "boolean",
|
|
1237
|
+
description: "For scan only: fan out across repositories explicitly listed in .mindgraph/workspace.json"
|
|
1238
|
+
},
|
|
1239
|
+
repo: {
|
|
1240
|
+
type: "string",
|
|
1241
|
+
description: "Configured repository id, name, or root"
|
|
1242
|
+
},
|
|
1243
|
+
logical_path: { type: "string" },
|
|
1244
|
+
content: { type: "string" },
|
|
1245
|
+
content_hash: { type: "string" },
|
|
1246
|
+
source_uid: { type: "string" },
|
|
1247
|
+
execution_uid: { type: "string" },
|
|
1248
|
+
assertion_fingerprint: { type: "string" },
|
|
1249
|
+
planned_fingerprints: { type: "array", items: { type: "string" } },
|
|
1250
|
+
current_fingerprints: { type: "array", items: { type: "string" } },
|
|
1251
|
+
source_selector: { type: "object" },
|
|
1252
|
+
capture_type: {
|
|
1253
|
+
type: "string",
|
|
1254
|
+
enum: ["lesson", "journal", "claim", "decision", "risk"]
|
|
1255
|
+
},
|
|
1256
|
+
label: { type: "string" },
|
|
1257
|
+
summary: { type: "string" },
|
|
1258
|
+
props: { type: "object" },
|
|
1259
|
+
expected_execution_version: { type: "integer", minimum: 1 },
|
|
1260
|
+
baseline_version: { type: "integer", minimum: 1 },
|
|
1261
|
+
repo_space_uid: { type: "string" },
|
|
1262
|
+
repo_id: { type: "string" },
|
|
1263
|
+
provider: { type: "string", enum: ["claude_project", "explicit"] },
|
|
1264
|
+
file_present: { type: "boolean" },
|
|
1265
|
+
agent_id: { type: "string" },
|
|
1266
|
+
invocation_context: {
|
|
1267
|
+
type: "object",
|
|
1268
|
+
description: "Adapter-owned; hooks replace forged or absent values.",
|
|
1269
|
+
properties: {
|
|
1270
|
+
harness: { type: "string" },
|
|
1271
|
+
harnessSessionId: { type: "string" },
|
|
1272
|
+
harnessTurnId: { type: "string" },
|
|
1273
|
+
cwd: { type: "string" },
|
|
1274
|
+
repoId: { type: "string" },
|
|
1275
|
+
branch: { type: "string" },
|
|
1276
|
+
commit: { type: "string" },
|
|
1277
|
+
worktreeState: { type: "string" },
|
|
1278
|
+
model: { type: "string" },
|
|
1279
|
+
injectedBy: { type: "string" }
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
},
|
|
1283
|
+
required: ["action"]
|
|
1284
|
+
}
|
|
1285
|
+
};
|
|
1286
|
+
function safePath(root, candidate) {
|
|
1287
|
+
const absolute = import_node_path2.default.resolve(root, candidate);
|
|
1288
|
+
const relative2 = import_node_path2.default.relative(root, absolute);
|
|
1289
|
+
if (relative2.startsWith("..") || import_node_path2.default.isAbsolute(relative2)) {
|
|
1290
|
+
throw new Error(`path escapes repository root: ${candidate}`);
|
|
1291
|
+
}
|
|
1292
|
+
return absolute;
|
|
1293
|
+
}
|
|
1294
|
+
function markdownFiles(target) {
|
|
1295
|
+
if (!import_node_fs2.default.existsSync(target)) return [];
|
|
1296
|
+
const stat = import_node_fs2.default.statSync(target);
|
|
1297
|
+
if (stat.isFile()) return target.endsWith(".md") ? [target] : [];
|
|
1298
|
+
if (!stat.isDirectory()) return [];
|
|
1299
|
+
return import_node_fs2.default.readdirSync(target, { withFileTypes: true }).flatMap((entry) => markdownFiles(import_node_path2.default.join(target, entry.name)));
|
|
1300
|
+
}
|
|
1301
|
+
function fileDescriptor(root, absolute) {
|
|
1302
|
+
const content = import_node_fs2.default.readFileSync(absolute, "utf8");
|
|
1303
|
+
return {
|
|
1304
|
+
logical_path: import_node_path2.default.relative(root, absolute).split(import_node_path2.default.sep).join("/"),
|
|
1305
|
+
content_hash: import_node_crypto2.default.createHash("sha256").update(content).digest("hex"),
|
|
1306
|
+
bytes: Buffer.byteLength(content),
|
|
1307
|
+
content
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
async function handleSyncTool(client2, args) {
|
|
1311
|
+
try {
|
|
1312
|
+
const action = String(args.action || "");
|
|
1313
|
+
const context = args.invocation_context || {};
|
|
1314
|
+
const adapter = new CodegraphAdapter({
|
|
1315
|
+
cwd: context.cwd || process.cwd()
|
|
1316
|
+
});
|
|
1317
|
+
const requestedRepo = typeof args.repo === "string" ? args.repo : typeof args.repo_id === "string" ? args.repo_id : context.repoId;
|
|
1318
|
+
const repository = await adapter.resolveRepository(requestedRepo);
|
|
1319
|
+
const root = repository.repoRoot;
|
|
1320
|
+
const repoId = repository.repoId;
|
|
1321
|
+
const provider = String(args.provider || "claude_project");
|
|
1322
|
+
const remote = client2;
|
|
1323
|
+
if (action === "scan") {
|
|
1324
|
+
const repositories = args.workspace === true ? await adapter.resolveRepositories() : [repository];
|
|
1325
|
+
const requested = Array.isArray(args.paths) && args.paths.length > 0 ? args.paths.map(String) : [".claude/agent-memory", "memory"];
|
|
1326
|
+
const scans = [];
|
|
1327
|
+
for (const selected of repositories) {
|
|
1328
|
+
scans.push(
|
|
1329
|
+
await scanRepository(
|
|
1330
|
+
remote,
|
|
1331
|
+
selected,
|
|
1332
|
+
requested,
|
|
1333
|
+
provider,
|
|
1334
|
+
args.agent_id
|
|
1335
|
+
)
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
if (args.workspace === true) {
|
|
1339
|
+
return ok2({
|
|
1340
|
+
provider,
|
|
1341
|
+
workspace: true,
|
|
1342
|
+
repositories: scans,
|
|
1343
|
+
one_way: "files_to_graph"
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
return ok2(scans[0]);
|
|
1347
|
+
}
|
|
1348
|
+
const request = {
|
|
1349
|
+
...args,
|
|
1350
|
+
provider,
|
|
1351
|
+
repo_id: repoId
|
|
1352
|
+
};
|
|
1353
|
+
delete request.invocation_context;
|
|
1354
|
+
delete request.paths;
|
|
1355
|
+
delete request.repo;
|
|
1356
|
+
delete request.workspace;
|
|
1357
|
+
if (action === "begin" && !request.content) {
|
|
1358
|
+
if (!request.logical_path) {
|
|
1359
|
+
return err2("missing_field", "logical_path is required for begin");
|
|
1360
|
+
}
|
|
1361
|
+
const file = fileDescriptor(
|
|
1362
|
+
root,
|
|
1363
|
+
safePath(root, String(request.logical_path))
|
|
1364
|
+
);
|
|
1365
|
+
request.content = file.content;
|
|
1366
|
+
request.content_hash ||= file.content_hash;
|
|
1367
|
+
}
|
|
1368
|
+
if (action === "status" && request.logical_path && request.file_present !== false) {
|
|
1369
|
+
const absolute = safePath(root, String(request.logical_path));
|
|
1370
|
+
request.file_present = import_node_fs2.default.existsSync(absolute);
|
|
1371
|
+
if (request.file_present && !request.content_hash) {
|
|
1372
|
+
request.content_hash = fileDescriptor(root, absolute).content_hash;
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
return ok2(await remote.memorySync(request));
|
|
1376
|
+
} catch (error) {
|
|
1377
|
+
return err2(
|
|
1378
|
+
"sync_failed",
|
|
1379
|
+
error instanceof Error ? error.message : String(error)
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
async function scanRepository(remote, repository, requested, provider, agentId) {
|
|
1384
|
+
const files = [
|
|
1385
|
+
...new Set(
|
|
1386
|
+
requested.flatMap(
|
|
1387
|
+
(candidate) => markdownFiles(safePath(repository.repoRoot, candidate))
|
|
1388
|
+
)
|
|
1389
|
+
)
|
|
1390
|
+
].sort();
|
|
1391
|
+
const results = [];
|
|
1392
|
+
for (const absolute of files) {
|
|
1393
|
+
const file = fileDescriptor(repository.repoRoot, absolute);
|
|
1394
|
+
const status = await remote.memorySync({
|
|
1395
|
+
action: "scan",
|
|
1396
|
+
provider,
|
|
1397
|
+
repo_id: repository.repoId,
|
|
1398
|
+
logical_path: file.logical_path,
|
|
1399
|
+
content_hash: file.content_hash,
|
|
1400
|
+
file_present: true,
|
|
1401
|
+
agent_id: agentId
|
|
1402
|
+
});
|
|
1403
|
+
results.push({
|
|
1404
|
+
logical_path: file.logical_path,
|
|
1405
|
+
content_hash: file.content_hash,
|
|
1406
|
+
bytes: file.bytes,
|
|
1407
|
+
status
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1410
|
+
return {
|
|
1411
|
+
repo_id: repository.repoId,
|
|
1412
|
+
root: ".",
|
|
1413
|
+
files: results
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
|
|
9
1417
|
// src/tools.ts
|
|
10
1418
|
var TOOLS = [
|
|
11
1419
|
{
|
|
12
1420
|
name: "mindgraph_capture",
|
|
13
|
-
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, 'concept' for abstract ideas. Use 'journal' for quick personal notes
|
|
1421
|
+
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 and 'lesson' for a durable learning attributable to a session or source nodes. Prefer 'entity'/'observation' for objective facts, 'journal' for informal notes, and 'lesson' only for reusable knowledge.",
|
|
14
1422
|
inputSchema: {
|
|
15
1423
|
type: "object",
|
|
16
1424
|
properties: {
|
|
17
1425
|
action: {
|
|
18
1426
|
type: "string",
|
|
19
|
-
enum: [
|
|
1427
|
+
enum: [
|
|
1428
|
+
"entity",
|
|
1429
|
+
"observation",
|
|
1430
|
+
"source",
|
|
1431
|
+
"snippet",
|
|
1432
|
+
"concept",
|
|
1433
|
+
"journal",
|
|
1434
|
+
"lesson"
|
|
1435
|
+
],
|
|
20
1436
|
description: "Type of knowledge to capture"
|
|
21
1437
|
},
|
|
22
1438
|
label: {
|
|
@@ -31,6 +1447,31 @@ var TOOLS = [
|
|
|
31
1447
|
type: "string",
|
|
32
1448
|
description: "Journal content/body text (for action=journal)"
|
|
33
1449
|
},
|
|
1450
|
+
session_uid: {
|
|
1451
|
+
type: "string",
|
|
1452
|
+
description: "Session UID that produced a lesson (for action=lesson)"
|
|
1453
|
+
},
|
|
1454
|
+
work_uid: {
|
|
1455
|
+
type: "string",
|
|
1456
|
+
description: "Durable Task/work UID this capture is relevant to"
|
|
1457
|
+
},
|
|
1458
|
+
execution_uid: {
|
|
1459
|
+
type: "string",
|
|
1460
|
+
description: "Material Execution that produced the lesson"
|
|
1461
|
+
},
|
|
1462
|
+
idempotency_key: {
|
|
1463
|
+
type: "string",
|
|
1464
|
+
description: "Stable retry key for an intentional capture"
|
|
1465
|
+
},
|
|
1466
|
+
supersedes_uid: {
|
|
1467
|
+
type: "string",
|
|
1468
|
+
description: "Earlier capture corrected or superseded by this one"
|
|
1469
|
+
},
|
|
1470
|
+
summarizes_uids: {
|
|
1471
|
+
type: "array",
|
|
1472
|
+
items: { type: "string" },
|
|
1473
|
+
description: "Source node UIDs the lesson was learned from (for action=lesson)"
|
|
1474
|
+
},
|
|
34
1475
|
entity_type: {
|
|
35
1476
|
type: "string",
|
|
36
1477
|
enum: ["person", "organization", "place", "event", "nation", "concept"],
|
|
@@ -65,6 +1506,32 @@ var TOOLS = [
|
|
|
65
1506
|
type: "object",
|
|
66
1507
|
description: "Type-specific properties (e.g. birth_date for person, coordinates for place)"
|
|
67
1508
|
},
|
|
1509
|
+
code_refs: {
|
|
1510
|
+
type: "array",
|
|
1511
|
+
items: {
|
|
1512
|
+
oneOf: [
|
|
1513
|
+
{ type: "string" },
|
|
1514
|
+
{
|
|
1515
|
+
type: "object",
|
|
1516
|
+
properties: {
|
|
1517
|
+
repo: { type: "string" },
|
|
1518
|
+
path: { type: "string" },
|
|
1519
|
+
line: { type: "integer", minimum: 1 },
|
|
1520
|
+
symbol: { type: "string" },
|
|
1521
|
+
kind: { type: "string" },
|
|
1522
|
+
language: { type: "string" },
|
|
1523
|
+
signature: { type: "string" }
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
]
|
|
1527
|
+
},
|
|
1528
|
+
description: "Typed code targets to anchor and attach to the captured node"
|
|
1529
|
+
},
|
|
1530
|
+
repo: { type: "string", description: "Configured repository id or root" },
|
|
1531
|
+
repo_space_uid: {
|
|
1532
|
+
type: "string",
|
|
1533
|
+
description: "Writable shared repository/Project Space for new code anchors"
|
|
1534
|
+
},
|
|
68
1535
|
agent_id: {
|
|
69
1536
|
type: "string",
|
|
70
1537
|
description: "Agent identity"
|
|
@@ -238,6 +1705,16 @@ var TOOLS = [
|
|
|
238
1705
|
type: "object",
|
|
239
1706
|
description: "Type-specific properties (e.g. status, priority, deadline, horizon for goals)"
|
|
240
1707
|
},
|
|
1708
|
+
code_refs: {
|
|
1709
|
+
type: "array",
|
|
1710
|
+
items: { oneOf: [{ type: "string" }, { type: "object" }] },
|
|
1711
|
+
description: "Typed code targets to anchor and attach to the created commitment"
|
|
1712
|
+
},
|
|
1713
|
+
repo: { type: "string", description: "Configured repository id or root" },
|
|
1714
|
+
repo_space_uid: {
|
|
1715
|
+
type: "string",
|
|
1716
|
+
description: "Writable shared repository/Project Space for new code anchors"
|
|
1717
|
+
},
|
|
241
1718
|
agent_id: {
|
|
242
1719
|
type: "string",
|
|
243
1720
|
description: "Agent identity"
|
|
@@ -248,7 +1725,7 @@ var TOOLS = [
|
|
|
248
1725
|
},
|
|
249
1726
|
{
|
|
250
1727
|
name: "mindgraph_plan",
|
|
251
|
-
description: "
|
|
1728
|
+
description: "Authoritative durable engineering work and execution evidence. Use create_task/create_plan/add_step for work that must survive sessions; resume_work for the deterministic bounded brief; claim_task + heartbeat for the fenced lease; and start_iteration/checkpoint_iteration/block_task/complete_task/abandon_iteration for idempotent material attempts. Every composite write needs task version, Session, lease epoch, and an idempotency key. Use the harness-native todo list only for local coordination; never maintain the same authoritative Task in both systems.",
|
|
252
1729
|
inputSchema: {
|
|
253
1730
|
type: "object",
|
|
254
1731
|
properties: {
|
|
@@ -260,9 +1737,19 @@ var TOOLS = [
|
|
|
260
1737
|
"add_step",
|
|
261
1738
|
"update_status",
|
|
262
1739
|
"get_plan",
|
|
1740
|
+
"resume_work",
|
|
1741
|
+
"claim_task",
|
|
1742
|
+
"heartbeat",
|
|
1743
|
+
"start_iteration",
|
|
1744
|
+
"checkpoint_iteration",
|
|
1745
|
+
"block_task",
|
|
1746
|
+
"complete_task",
|
|
1747
|
+
"abandon_iteration",
|
|
263
1748
|
"start_execution",
|
|
264
1749
|
"complete_execution",
|
|
265
1750
|
"fail_execution",
|
|
1751
|
+
"register_agent",
|
|
1752
|
+
"get_executions",
|
|
266
1753
|
// C33: governance WRITES are deliberately not exposed here.
|
|
267
1754
|
// `create_policy` let a model author the rules it is judged by;
|
|
268
1755
|
// `request_approval` + `resolve_approval` let it raise and then
|
|
@@ -288,32 +1775,191 @@ var TOOLS = [
|
|
|
288
1775
|
type: "string",
|
|
289
1776
|
description: "Description"
|
|
290
1777
|
},
|
|
291
|
-
plan_uid: {
|
|
1778
|
+
plan_uid: {
|
|
1779
|
+
type: "string",
|
|
1780
|
+
description: "Plan UID (for create_plan execution link, add_step, get_plan)"
|
|
1781
|
+
},
|
|
1782
|
+
task_uid: {
|
|
1783
|
+
type: "string",
|
|
1784
|
+
description: "Task UID (for create_plan and graph-managed work actions)"
|
|
1785
|
+
},
|
|
1786
|
+
step_uid: {
|
|
1787
|
+
type: "string",
|
|
1788
|
+
description: "PlanStep UID for an iteration/checkpoint"
|
|
1789
|
+
},
|
|
1790
|
+
session_uid: {
|
|
1791
|
+
type: "string",
|
|
1792
|
+
description: "Durable Session UID that owns the task lease"
|
|
1793
|
+
},
|
|
1794
|
+
goal_uid: {
|
|
1795
|
+
type: "string",
|
|
1796
|
+
description: "Goal UID targeted by a task or plan"
|
|
1797
|
+
},
|
|
1798
|
+
target_uid: {
|
|
1799
|
+
type: "string",
|
|
1800
|
+
description: "Task/plan/step UID for update_status, or parent/target node for procedure and risk actions"
|
|
1801
|
+
},
|
|
1802
|
+
execution_uid: {
|
|
1803
|
+
type: "string",
|
|
1804
|
+
description: "Execution UID (required for complete_execution/fail_execution)"
|
|
1805
|
+
},
|
|
1806
|
+
executor_uid: {
|
|
1807
|
+
type: "string",
|
|
1808
|
+
description: "Agent node UID that executed the work"
|
|
1809
|
+
},
|
|
1810
|
+
affordance_uid: {
|
|
1811
|
+
type: "string",
|
|
1812
|
+
description: "Affordance invoked by start_execution"
|
|
1813
|
+
},
|
|
1814
|
+
produces_node_uid: {
|
|
1815
|
+
type: "string",
|
|
1816
|
+
description: "Node produced by a completed execution"
|
|
1817
|
+
},
|
|
1818
|
+
filter_plan_uid: {
|
|
1819
|
+
type: "string",
|
|
1820
|
+
description: "Restrict get_executions to one plan"
|
|
1821
|
+
},
|
|
1822
|
+
depends_on_uids: {
|
|
1823
|
+
type: "array",
|
|
1824
|
+
items: { type: "string" },
|
|
1825
|
+
description: "Plan-step UIDs that an added step depends on"
|
|
1826
|
+
},
|
|
1827
|
+
related_uids: {
|
|
1828
|
+
type: "array",
|
|
1829
|
+
items: { type: "string" },
|
|
1830
|
+
description: "Target node UIDs to link to created work or an execution"
|
|
1831
|
+
},
|
|
1832
|
+
status: {
|
|
1833
|
+
type: "string",
|
|
1834
|
+
description: "New status value (for update_status)"
|
|
1835
|
+
},
|
|
1836
|
+
task_status: {
|
|
1837
|
+
type: "string",
|
|
1838
|
+
description: "Task status requested by a checkpoint or abandon action"
|
|
1839
|
+
},
|
|
1840
|
+
step_status: {
|
|
1841
|
+
type: "string",
|
|
1842
|
+
description: "PlanStep status requested by a checkpoint"
|
|
1843
|
+
},
|
|
1844
|
+
execution_status: {
|
|
1845
|
+
type: "string",
|
|
1846
|
+
enum: ["completed", "failed", "abandoned"],
|
|
1847
|
+
description: "Terminal Execution status for checkpoint_iteration"
|
|
1848
|
+
},
|
|
1849
|
+
expected_version: {
|
|
1850
|
+
type: "number",
|
|
1851
|
+
description: "Current Task node version used as the optimistic fence"
|
|
1852
|
+
},
|
|
1853
|
+
lease_epoch: {
|
|
1854
|
+
type: "number",
|
|
1855
|
+
description: "Current monotonic task-lease epoch"
|
|
1856
|
+
},
|
|
1857
|
+
lease_ttl_secs: {
|
|
1858
|
+
type: "number",
|
|
1859
|
+
minimum: 1,
|
|
1860
|
+
description: "Requested server-time lease lifetime (default 300s)"
|
|
1861
|
+
},
|
|
1862
|
+
idempotency_key: {
|
|
1863
|
+
type: "string",
|
|
1864
|
+
description: "Caller-stable key required for every composite work mutation"
|
|
1865
|
+
},
|
|
1866
|
+
allow_takeover: {
|
|
1867
|
+
type: "boolean",
|
|
1868
|
+
description: "Governed claim override for a live lease owned by another agent"
|
|
1869
|
+
},
|
|
1870
|
+
release_lease: {
|
|
1871
|
+
type: "boolean",
|
|
1872
|
+
description: "Explicitly release or retain the lease after block/abandon/checkpoint"
|
|
1873
|
+
},
|
|
1874
|
+
override_reason: {
|
|
1875
|
+
type: "string",
|
|
1876
|
+
description: "Explicit completion reason when no terminal Execution evidence exists"
|
|
1877
|
+
},
|
|
1878
|
+
scope_uids: {
|
|
1879
|
+
type: "array",
|
|
1880
|
+
items: { type: "string" },
|
|
1881
|
+
description: "Ordinary target UIDs that bound deterministic resume selection"
|
|
1882
|
+
},
|
|
1883
|
+
limit: {
|
|
1884
|
+
type: "number",
|
|
1885
|
+
minimum: 1,
|
|
1886
|
+
maximum: 1,
|
|
1887
|
+
description: "Resume returns one authoritative Task"
|
|
1888
|
+
},
|
|
1889
|
+
input_snapshot: {
|
|
1890
|
+
type: "object",
|
|
1891
|
+
description: "Structured inputs captured when an execution starts"
|
|
1892
|
+
},
|
|
1893
|
+
output_snapshot: {
|
|
1894
|
+
type: "object",
|
|
1895
|
+
description: "Structured outputs captured when an execution completes or fails"
|
|
1896
|
+
},
|
|
1897
|
+
outcome: {
|
|
1898
|
+
type: "string",
|
|
1899
|
+
description: "Execution outcome"
|
|
1900
|
+
},
|
|
1901
|
+
error: {
|
|
292
1902
|
type: "string",
|
|
293
|
-
description: "
|
|
1903
|
+
description: "Execution error detail (for fail_execution)"
|
|
294
1904
|
},
|
|
295
|
-
|
|
1905
|
+
side_effects: {
|
|
1906
|
+
type: "array",
|
|
1907
|
+
items: { type: "string" },
|
|
1908
|
+
description: "Observable side effects of the execution"
|
|
1909
|
+
},
|
|
1910
|
+
next_action: {
|
|
296
1911
|
type: "string",
|
|
297
|
-
description: "
|
|
1912
|
+
description: "Recommended next action retained on terminal Execution evidence"
|
|
298
1913
|
},
|
|
299
|
-
|
|
1914
|
+
checkpoint_summary: {
|
|
300
1915
|
type: "string",
|
|
301
|
-
description: "
|
|
1916
|
+
description: "Bounded material-attempt checkpoint summary"
|
|
302
1917
|
},
|
|
303
|
-
|
|
1918
|
+
test_summary: {
|
|
304
1919
|
type: "string",
|
|
305
|
-
description: "
|
|
1920
|
+
description: "Test commands/results retained on the Execution"
|
|
1921
|
+
},
|
|
1922
|
+
produces_node_uids: {
|
|
1923
|
+
type: "array",
|
|
1924
|
+
items: { type: "string" },
|
|
1925
|
+
description: "Nodes produced by checkpoint_iteration"
|
|
1926
|
+
},
|
|
1927
|
+
confidence: {
|
|
1928
|
+
type: "number",
|
|
1929
|
+
minimum: 0,
|
|
1930
|
+
maximum: 1
|
|
1931
|
+
},
|
|
1932
|
+
salience: {
|
|
1933
|
+
type: "number",
|
|
1934
|
+
minimum: 0,
|
|
1935
|
+
maximum: 1
|
|
306
1936
|
},
|
|
307
1937
|
props: {
|
|
308
1938
|
type: "object",
|
|
309
1939
|
description: "Additional properties (e.g. likelihood, impact, mitigation for risks)"
|
|
310
1940
|
},
|
|
1941
|
+
code_refs: {
|
|
1942
|
+
type: "array",
|
|
1943
|
+
items: { oneOf: [{ type: "string" }, { type: "object" }] },
|
|
1944
|
+
description: "Typed code targets to anchor and attach to created work or execution"
|
|
1945
|
+
},
|
|
1946
|
+
repo: { type: "string", description: "Configured repository id or root" },
|
|
1947
|
+
repo_space_uid: {
|
|
1948
|
+
type: "string",
|
|
1949
|
+
description: "Writable shared repository/Project Space for new code anchors"
|
|
1950
|
+
},
|
|
311
1951
|
agent_id: {
|
|
312
1952
|
type: "string",
|
|
313
1953
|
description: "Agent identity"
|
|
314
1954
|
}
|
|
315
1955
|
},
|
|
316
1956
|
required: ["action"]
|
|
1957
|
+
},
|
|
1958
|
+
annotations: {
|
|
1959
|
+
readOnlyHint: false,
|
|
1960
|
+
destructiveHint: false,
|
|
1961
|
+
idempotentHint: false,
|
|
1962
|
+
openWorldHint: true
|
|
317
1963
|
}
|
|
318
1964
|
},
|
|
319
1965
|
{
|
|
@@ -688,14 +2334,45 @@ var TOOLS = [
|
|
|
688
2334
|
},
|
|
689
2335
|
required: ["action"]
|
|
690
2336
|
}
|
|
691
|
-
}
|
|
2337
|
+
},
|
|
2338
|
+
SYNC_TOOL,
|
|
2339
|
+
CODE_TOOL
|
|
692
2340
|
];
|
|
693
|
-
|
|
2341
|
+
var INVOCATION_CONTEXT_SCHEMA = {
|
|
2342
|
+
type: "object",
|
|
2343
|
+
description: "Adapter-owned invocation context. Models should omit it; compatible hooks replace forged or absent values. cwd is consumed locally and never sent to the API.",
|
|
2344
|
+
properties: {
|
|
2345
|
+
harness: { type: "string", enum: ["claude-code", "codex", "generic"] },
|
|
2346
|
+
harnessSessionId: { type: "string" },
|
|
2347
|
+
harnessTurnId: { type: "string" },
|
|
2348
|
+
cwd: { type: "string" },
|
|
2349
|
+
repoId: { type: "string" },
|
|
2350
|
+
branch: { type: "string" },
|
|
2351
|
+
commit: { type: "string" },
|
|
2352
|
+
worktreeState: { type: "string", enum: ["clean", "dirty"] },
|
|
2353
|
+
model: { type: "string" },
|
|
2354
|
+
injectedBy: { type: "string", enum: ["hook", "mcp-process", "caller"] }
|
|
2355
|
+
}
|
|
2356
|
+
};
|
|
2357
|
+
for (const tool of TOOLS) {
|
|
2358
|
+
if (tool.name === "mindgraph_retrieve") continue;
|
|
2359
|
+
const schema = tool.inputSchema;
|
|
2360
|
+
if (schema.properties && !schema.properties.invocation_context) {
|
|
2361
|
+
schema.properties.invocation_context = INVOCATION_CONTEXT_SCHEMA;
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
function toolsForProfile(profile = process.env.MINDGRAPH_PROFILE || "general") {
|
|
2365
|
+
if (profile === "coding") return TOOLS;
|
|
2366
|
+
return TOOLS.filter(
|
|
2367
|
+
(tool) => tool.name !== "mindgraph_code" && tool.name !== "mindgraph_sync"
|
|
2368
|
+
);
|
|
2369
|
+
}
|
|
2370
|
+
function ok3(data) {
|
|
694
2371
|
return {
|
|
695
2372
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
696
2373
|
};
|
|
697
2374
|
}
|
|
698
|
-
function
|
|
2375
|
+
function err3(message) {
|
|
699
2376
|
return {
|
|
700
2377
|
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
701
2378
|
isError: true
|
|
@@ -703,29 +2380,58 @@ function err(message) {
|
|
|
703
2380
|
}
|
|
704
2381
|
async function handleTool(client2, name, args) {
|
|
705
2382
|
try {
|
|
2383
|
+
let result;
|
|
706
2384
|
switch (name) {
|
|
707
2385
|
case "mindgraph_capture":
|
|
708
|
-
|
|
2386
|
+
result = await handleCapture(client2, args);
|
|
2387
|
+
break;
|
|
709
2388
|
case "mindgraph_reason":
|
|
710
|
-
|
|
2389
|
+
result = await handleReason(client2, args);
|
|
2390
|
+
break;
|
|
711
2391
|
case "mindgraph_commit":
|
|
712
|
-
|
|
2392
|
+
result = await handleCommit(client2, args);
|
|
2393
|
+
break;
|
|
713
2394
|
case "mindgraph_plan":
|
|
714
|
-
|
|
2395
|
+
result = await handlePlan(client2, args);
|
|
2396
|
+
break;
|
|
715
2397
|
case "mindgraph_retrieve":
|
|
716
|
-
|
|
2398
|
+
result = await handleRetrieve(client2, args);
|
|
2399
|
+
break;
|
|
717
2400
|
case "mindgraph_ingest":
|
|
718
|
-
|
|
2401
|
+
result = await handleIngest(client2, args);
|
|
2402
|
+
break;
|
|
719
2403
|
case "mindgraph_synthesize":
|
|
720
|
-
|
|
2404
|
+
result = await handleSynthesize(client2, args);
|
|
2405
|
+
break;
|
|
721
2406
|
case "mindgraph_ontology":
|
|
722
|
-
|
|
2407
|
+
result = await handleOntology(client2, args);
|
|
2408
|
+
break;
|
|
2409
|
+
case "mindgraph_code":
|
|
2410
|
+
result = await handleCodeTool(client2, args);
|
|
2411
|
+
break;
|
|
2412
|
+
case "mindgraph_sync":
|
|
2413
|
+
result = await handleSyncTool(client2, args);
|
|
2414
|
+
break;
|
|
723
2415
|
default:
|
|
724
|
-
|
|
2416
|
+
result = err3(`Unknown tool: ${name}`);
|
|
725
2417
|
}
|
|
2418
|
+
const action = typeof args.action === "string" ? args.action : "";
|
|
2419
|
+
const attach = name === "mindgraph_capture" || name === "mindgraph_commit" && action !== "get_open_decisions" || name === "mindgraph_plan" && ![
|
|
2420
|
+
"get_plan",
|
|
2421
|
+
"get_executions",
|
|
2422
|
+
"get_pending",
|
|
2423
|
+
"get_assessments",
|
|
2424
|
+
"resume_work",
|
|
2425
|
+
"heartbeat"
|
|
2426
|
+
].includes(action);
|
|
2427
|
+
return attach ? await attachCodeRefsToToolResult(client2, result, args) : result;
|
|
726
2428
|
} catch (e) {
|
|
2429
|
+
if (e instanceof import_mindgraph.MindGraphError && e.body !== void 0) {
|
|
2430
|
+
const detail = typeof e.body === "string" ? e.body : JSON.stringify(e.body);
|
|
2431
|
+
return err3(`${e.message} \u2014 ${detail}`);
|
|
2432
|
+
}
|
|
727
2433
|
const message = e instanceof Error ? e.message : String(e);
|
|
728
|
-
return
|
|
2434
|
+
return err3(message);
|
|
729
2435
|
}
|
|
730
2436
|
}
|
|
731
2437
|
async function handleCapture(client2, args) {
|
|
@@ -734,6 +2440,12 @@ async function handleCapture(client2, args) {
|
|
|
734
2440
|
label,
|
|
735
2441
|
summary,
|
|
736
2442
|
content,
|
|
2443
|
+
session_uid,
|
|
2444
|
+
work_uid,
|
|
2445
|
+
execution_uid,
|
|
2446
|
+
idempotency_key,
|
|
2447
|
+
supersedes_uid,
|
|
2448
|
+
summarizes_uids,
|
|
737
2449
|
entity_type,
|
|
738
2450
|
source_uid,
|
|
739
2451
|
mood,
|
|
@@ -744,12 +2456,30 @@ async function handleCapture(client2, args) {
|
|
|
744
2456
|
agent_id
|
|
745
2457
|
} = args;
|
|
746
2458
|
switch (action) {
|
|
2459
|
+
case "lesson":
|
|
2460
|
+
return ok3(
|
|
2461
|
+
await client2.distill({
|
|
2462
|
+
label,
|
|
2463
|
+
output_type: "lesson",
|
|
2464
|
+
summary,
|
|
2465
|
+
confidence,
|
|
2466
|
+
salience,
|
|
2467
|
+
session_uid,
|
|
2468
|
+
work_uid,
|
|
2469
|
+
execution_uid,
|
|
2470
|
+
idempotency_key,
|
|
2471
|
+
supersedes_uid,
|
|
2472
|
+
summarizes_uids,
|
|
2473
|
+
props,
|
|
2474
|
+
agent_id
|
|
2475
|
+
})
|
|
2476
|
+
);
|
|
747
2477
|
case "journal": {
|
|
748
|
-
if (!content) return
|
|
2478
|
+
if (!content) return err3("content is required for action=journal");
|
|
749
2479
|
const journalProps = { content };
|
|
750
2480
|
if (mood) journalProps.mood = mood;
|
|
751
2481
|
if (tags) journalProps.tags = tags;
|
|
752
|
-
return
|
|
2482
|
+
return ok3(await client2.journal(label, journalProps, { agent_id }));
|
|
753
2483
|
}
|
|
754
2484
|
case "entity": {
|
|
755
2485
|
const type = entity_type || "concept";
|
|
@@ -757,22 +2487,22 @@ async function handleCapture(client2, args) {
|
|
|
757
2487
|
if (summary) entityProps.description = summary;
|
|
758
2488
|
switch (type) {
|
|
759
2489
|
case "person":
|
|
760
|
-
return
|
|
2490
|
+
return ok3(await client2.findOrCreatePerson(label, entityProps, agent_id));
|
|
761
2491
|
case "organization":
|
|
762
|
-
return
|
|
2492
|
+
return ok3(await client2.findOrCreateOrganization(label, entityProps, agent_id));
|
|
763
2493
|
case "nation":
|
|
764
|
-
return
|
|
2494
|
+
return ok3(await client2.findOrCreateNation(label, entityProps, agent_id));
|
|
765
2495
|
case "event":
|
|
766
|
-
return
|
|
2496
|
+
return ok3(await client2.findOrCreateEvent(label, entityProps, agent_id));
|
|
767
2497
|
case "place":
|
|
768
|
-
return
|
|
2498
|
+
return ok3(await client2.findOrCreatePlace(label, entityProps, agent_id));
|
|
769
2499
|
case "concept":
|
|
770
2500
|
default:
|
|
771
|
-
return
|
|
2501
|
+
return ok3(await client2.findOrCreateConcept(label, entityProps, agent_id));
|
|
772
2502
|
}
|
|
773
2503
|
}
|
|
774
2504
|
case "observation":
|
|
775
|
-
return
|
|
2505
|
+
return ok3(
|
|
776
2506
|
await client2.capture({
|
|
777
2507
|
action: "observation",
|
|
778
2508
|
label,
|
|
@@ -784,7 +2514,7 @@ async function handleCapture(client2, args) {
|
|
|
784
2514
|
})
|
|
785
2515
|
);
|
|
786
2516
|
case "source":
|
|
787
|
-
return
|
|
2517
|
+
return ok3(
|
|
788
2518
|
await client2.capture({
|
|
789
2519
|
action: "source",
|
|
790
2520
|
label,
|
|
@@ -796,7 +2526,7 @@ async function handleCapture(client2, args) {
|
|
|
796
2526
|
})
|
|
797
2527
|
);
|
|
798
2528
|
case "snippet":
|
|
799
|
-
return
|
|
2529
|
+
return ok3(
|
|
800
2530
|
await client2.capture({
|
|
801
2531
|
action: "snippet",
|
|
802
2532
|
label,
|
|
@@ -809,7 +2539,7 @@ async function handleCapture(client2, args) {
|
|
|
809
2539
|
})
|
|
810
2540
|
);
|
|
811
2541
|
case "concept":
|
|
812
|
-
return
|
|
2542
|
+
return ok3(
|
|
813
2543
|
await client2.structure({
|
|
814
2544
|
action: "concept",
|
|
815
2545
|
label,
|
|
@@ -821,7 +2551,7 @@ async function handleCapture(client2, args) {
|
|
|
821
2551
|
})
|
|
822
2552
|
);
|
|
823
2553
|
default:
|
|
824
|
-
return
|
|
2554
|
+
return err3(`Unknown capture action: ${action}`);
|
|
825
2555
|
}
|
|
826
2556
|
}
|
|
827
2557
|
async function handleReason(client2, args) {
|
|
@@ -840,8 +2570,8 @@ async function handleReason(client2, args) {
|
|
|
840
2570
|
} = args;
|
|
841
2571
|
if (action === "claim") {
|
|
842
2572
|
const claimObj = claim || (label ? { label, confidence } : null);
|
|
843
|
-
if (!claimObj) return
|
|
844
|
-
return
|
|
2573
|
+
if (!claimObj) return err3("claim object or label is required for action=claim");
|
|
2574
|
+
return ok3(
|
|
845
2575
|
await client2.argue({
|
|
846
2576
|
claim: claimObj,
|
|
847
2577
|
evidence,
|
|
@@ -851,8 +2581,8 @@ async function handleReason(client2, args) {
|
|
|
851
2581
|
})
|
|
852
2582
|
);
|
|
853
2583
|
}
|
|
854
|
-
if (!label) return
|
|
855
|
-
return
|
|
2584
|
+
if (!label) return err3(`label is required for action=${action}`);
|
|
2585
|
+
return ok3(
|
|
856
2586
|
await client2.inquire({
|
|
857
2587
|
action,
|
|
858
2588
|
label,
|
|
@@ -885,8 +2615,8 @@ async function handleCommit(client2, args) {
|
|
|
885
2615
|
case "goal":
|
|
886
2616
|
case "project":
|
|
887
2617
|
case "milestone":
|
|
888
|
-
if (!label) return
|
|
889
|
-
return
|
|
2618
|
+
if (!label) return err3(`label is required for action=${action}`);
|
|
2619
|
+
return ok3(
|
|
890
2620
|
await client2.commit({
|
|
891
2621
|
action,
|
|
892
2622
|
label,
|
|
@@ -899,15 +2629,15 @@ async function handleCommit(client2, args) {
|
|
|
899
2629
|
})
|
|
900
2630
|
);
|
|
901
2631
|
case "open_decision":
|
|
902
|
-
if (!label) return
|
|
903
|
-
return
|
|
2632
|
+
if (!label) return err3("label is required for open_decision");
|
|
2633
|
+
return ok3(await client2.openDecision(label, { summary, props, agent_id }));
|
|
904
2634
|
case "add_option":
|
|
905
|
-
if (!decision_uid) return
|
|
906
|
-
if (!label) return
|
|
907
|
-
return
|
|
2635
|
+
if (!decision_uid) return err3("decision_uid is required for add_option");
|
|
2636
|
+
if (!label) return err3("label is required for add_option");
|
|
2637
|
+
return ok3(await client2.addOption(decision_uid, label, { summary, props, agent_id }));
|
|
908
2638
|
case "add_constraint":
|
|
909
|
-
if (!decision_uid) return
|
|
910
|
-
return
|
|
2639
|
+
if (!decision_uid) return err3("decision_uid is required for add_constraint");
|
|
2640
|
+
return ok3(
|
|
911
2641
|
await client2.deliberate({
|
|
912
2642
|
action: "add_constraint",
|
|
913
2643
|
label,
|
|
@@ -918,9 +2648,9 @@ async function handleCommit(client2, args) {
|
|
|
918
2648
|
})
|
|
919
2649
|
);
|
|
920
2650
|
case "resolve_decision":
|
|
921
|
-
if (!decision_uid) return
|
|
922
|
-
if (!chosen_option_uid) return
|
|
923
|
-
return
|
|
2651
|
+
if (!decision_uid) return err3("decision_uid is required for resolve_decision");
|
|
2652
|
+
if (!chosen_option_uid) return err3("chosen_option_uid is required for resolve_decision");
|
|
2653
|
+
return ok3(
|
|
924
2654
|
await client2.resolveDecision(decision_uid, chosen_option_uid, {
|
|
925
2655
|
summary,
|
|
926
2656
|
props,
|
|
@@ -932,9 +2662,9 @@ async function handleCommit(client2, args) {
|
|
|
932
2662
|
})
|
|
933
2663
|
);
|
|
934
2664
|
case "get_open_decisions":
|
|
935
|
-
return
|
|
2665
|
+
return ok3(await client2.getOpenDecisions());
|
|
936
2666
|
default:
|
|
937
|
-
return
|
|
2667
|
+
return err3(`Unknown commit action: ${action}`);
|
|
938
2668
|
}
|
|
939
2669
|
}
|
|
940
2670
|
async function handlePlan(client2, args) {
|
|
@@ -944,8 +2674,41 @@ async function handlePlan(client2, args) {
|
|
|
944
2674
|
summary,
|
|
945
2675
|
plan_uid,
|
|
946
2676
|
task_uid,
|
|
2677
|
+
step_uid,
|
|
2678
|
+
session_uid,
|
|
2679
|
+
goal_uid,
|
|
947
2680
|
target_uid,
|
|
2681
|
+
execution_uid,
|
|
2682
|
+
executor_uid,
|
|
2683
|
+
affordance_uid,
|
|
2684
|
+
produces_node_uid,
|
|
2685
|
+
filter_plan_uid,
|
|
2686
|
+
depends_on_uids,
|
|
2687
|
+
related_uids,
|
|
948
2688
|
status,
|
|
2689
|
+
task_status,
|
|
2690
|
+
step_status,
|
|
2691
|
+
execution_status,
|
|
2692
|
+
expected_version,
|
|
2693
|
+
lease_epoch,
|
|
2694
|
+
lease_ttl_secs,
|
|
2695
|
+
idempotency_key,
|
|
2696
|
+
allow_takeover,
|
|
2697
|
+
release_lease,
|
|
2698
|
+
override_reason,
|
|
2699
|
+
scope_uids,
|
|
2700
|
+
limit,
|
|
2701
|
+
confidence,
|
|
2702
|
+
salience,
|
|
2703
|
+
input_snapshot,
|
|
2704
|
+
output_snapshot,
|
|
2705
|
+
side_effects,
|
|
2706
|
+
outcome,
|
|
2707
|
+
error,
|
|
2708
|
+
next_action,
|
|
2709
|
+
checkpoint_summary,
|
|
2710
|
+
test_summary,
|
|
2711
|
+
produces_node_uids,
|
|
949
2712
|
props,
|
|
950
2713
|
agent_id
|
|
951
2714
|
} = args;
|
|
@@ -956,25 +2719,120 @@ async function handlePlan(client2, args) {
|
|
|
956
2719
|
case "add_step":
|
|
957
2720
|
case "update_status":
|
|
958
2721
|
case "get_plan":
|
|
959
|
-
|
|
2722
|
+
case "resume_work":
|
|
2723
|
+
case "claim_task":
|
|
2724
|
+
case "heartbeat":
|
|
2725
|
+
case "start_iteration":
|
|
2726
|
+
case "checkpoint_iteration":
|
|
2727
|
+
case "block_task":
|
|
2728
|
+
case "complete_task":
|
|
2729
|
+
case "abandon_iteration":
|
|
2730
|
+
return ok3(
|
|
960
2731
|
await client2.plan({
|
|
961
2732
|
action,
|
|
962
2733
|
label,
|
|
963
2734
|
summary,
|
|
2735
|
+
confidence,
|
|
2736
|
+
salience,
|
|
2737
|
+
goal_uid,
|
|
964
2738
|
plan_uid,
|
|
965
2739
|
task_uid,
|
|
2740
|
+
step_uid,
|
|
2741
|
+
session_uid,
|
|
2742
|
+
target_uid,
|
|
2743
|
+
depends_on_uids,
|
|
2744
|
+
related_uids,
|
|
966
2745
|
status,
|
|
2746
|
+
task_status,
|
|
2747
|
+
step_status,
|
|
2748
|
+
execution_status,
|
|
2749
|
+
execution_uid,
|
|
2750
|
+
expected_version,
|
|
2751
|
+
lease_epoch,
|
|
2752
|
+
lease_ttl_secs,
|
|
2753
|
+
idempotency_key,
|
|
2754
|
+
allow_takeover,
|
|
2755
|
+
release_lease,
|
|
2756
|
+
override_reason,
|
|
2757
|
+
scope_uids,
|
|
2758
|
+
limit,
|
|
2759
|
+
input_snapshot,
|
|
2760
|
+
output_snapshot,
|
|
2761
|
+
side_effects,
|
|
2762
|
+
outcome,
|
|
2763
|
+
error,
|
|
2764
|
+
next_action,
|
|
2765
|
+
checkpoint_summary,
|
|
2766
|
+
test_summary,
|
|
2767
|
+
produces_node_uids,
|
|
967
2768
|
props,
|
|
968
2769
|
agent_id
|
|
969
2770
|
})
|
|
970
2771
|
);
|
|
971
2772
|
// Execution tracking
|
|
972
2773
|
case "start_execution":
|
|
973
|
-
return
|
|
2774
|
+
return ok3(
|
|
2775
|
+
await client2.execution({
|
|
2776
|
+
action: "start",
|
|
2777
|
+
label,
|
|
2778
|
+
summary,
|
|
2779
|
+
confidence,
|
|
2780
|
+
salience,
|
|
2781
|
+
plan_uid,
|
|
2782
|
+
executor_uid,
|
|
2783
|
+
affordance_uid,
|
|
2784
|
+
related_uids,
|
|
2785
|
+
input_snapshot,
|
|
2786
|
+
props,
|
|
2787
|
+
agent_id
|
|
2788
|
+
})
|
|
2789
|
+
);
|
|
974
2790
|
case "complete_execution":
|
|
975
|
-
return
|
|
2791
|
+
return ok3(
|
|
2792
|
+
await client2.execution({
|
|
2793
|
+
action: "complete",
|
|
2794
|
+
execution_uid,
|
|
2795
|
+
produces_node_uid,
|
|
2796
|
+
output_snapshot,
|
|
2797
|
+
side_effects,
|
|
2798
|
+
outcome,
|
|
2799
|
+
props,
|
|
2800
|
+
agent_id
|
|
2801
|
+
})
|
|
2802
|
+
);
|
|
976
2803
|
case "fail_execution":
|
|
977
|
-
return
|
|
2804
|
+
return ok3(
|
|
2805
|
+
await client2.execution({
|
|
2806
|
+
action: "fail",
|
|
2807
|
+
execution_uid,
|
|
2808
|
+
output_snapshot,
|
|
2809
|
+
side_effects,
|
|
2810
|
+
outcome,
|
|
2811
|
+
error,
|
|
2812
|
+
props,
|
|
2813
|
+
agent_id
|
|
2814
|
+
})
|
|
2815
|
+
);
|
|
2816
|
+
case "register_agent":
|
|
2817
|
+
return ok3(
|
|
2818
|
+
await client2.execution({
|
|
2819
|
+
action: "register_agent",
|
|
2820
|
+
label,
|
|
2821
|
+
summary,
|
|
2822
|
+
confidence,
|
|
2823
|
+
salience,
|
|
2824
|
+
props,
|
|
2825
|
+
agent_id
|
|
2826
|
+
})
|
|
2827
|
+
);
|
|
2828
|
+
case "get_executions":
|
|
2829
|
+
return ok3(
|
|
2830
|
+
await client2.execution({
|
|
2831
|
+
action: "get_executions",
|
|
2832
|
+
filter_plan_uid,
|
|
2833
|
+
agent_id
|
|
2834
|
+
})
|
|
2835
|
+
);
|
|
978
2836
|
// Governance — read-only from MCP by design (C33). The write actions were
|
|
979
2837
|
// removed rather than repaired: `resolve_approval` here also sent
|
|
980
2838
|
// `task_uid` where the server requires `approval_uid`, and never sent
|
|
@@ -982,12 +2840,12 @@ async function handlePlan(client2, args) {
|
|
|
982
2840
|
// DENIED every approval an agent tried to resolve. Wiring that correctly
|
|
983
2841
|
// would have meant building a working self-approval path — the hole itself.
|
|
984
2842
|
case "get_pending":
|
|
985
|
-
return
|
|
2843
|
+
return ok3(await client2.governance({ action: "get_pending", agent_id }));
|
|
986
2844
|
// Procedures (merged from mindgraph_action)
|
|
987
2845
|
case "create_flow":
|
|
988
2846
|
case "add_affordance":
|
|
989
2847
|
case "add_control":
|
|
990
|
-
return
|
|
2848
|
+
return ok3(
|
|
991
2849
|
await client2.procedure({
|
|
992
2850
|
action,
|
|
993
2851
|
label,
|
|
@@ -998,7 +2856,7 @@ async function handlePlan(client2, args) {
|
|
|
998
2856
|
})
|
|
999
2857
|
);
|
|
1000
2858
|
case "add_procedure_step":
|
|
1001
|
-
return
|
|
2859
|
+
return ok3(
|
|
1002
2860
|
await client2.procedure({
|
|
1003
2861
|
action: "add_step",
|
|
1004
2862
|
label,
|
|
@@ -1010,7 +2868,7 @@ async function handlePlan(client2, args) {
|
|
|
1010
2868
|
);
|
|
1011
2869
|
// Risk assessment (merged from mindgraph_action)
|
|
1012
2870
|
case "assess_risk":
|
|
1013
|
-
return
|
|
2871
|
+
return ok3(
|
|
1014
2872
|
await client2.risk({
|
|
1015
2873
|
action: "assess",
|
|
1016
2874
|
label,
|
|
@@ -1021,7 +2879,7 @@ async function handlePlan(client2, args) {
|
|
|
1021
2879
|
})
|
|
1022
2880
|
);
|
|
1023
2881
|
case "get_assessments":
|
|
1024
|
-
return
|
|
2882
|
+
return ok3(
|
|
1025
2883
|
await client2.risk({
|
|
1026
2884
|
action: "get_assessments",
|
|
1027
2885
|
target_uid,
|
|
@@ -1029,7 +2887,7 @@ async function handlePlan(client2, args) {
|
|
|
1029
2887
|
})
|
|
1030
2888
|
);
|
|
1031
2889
|
default:
|
|
1032
|
-
return
|
|
2890
|
+
return err3(`Unknown plan action: ${action}`);
|
|
1033
2891
|
}
|
|
1034
2892
|
}
|
|
1035
2893
|
async function handleRetrieve(client2, args) {
|
|
@@ -1058,8 +2916,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1058
2916
|
} = args;
|
|
1059
2917
|
switch (action) {
|
|
1060
2918
|
case "context":
|
|
1061
|
-
if (!query) return
|
|
1062
|
-
return
|
|
2919
|
+
if (!query) return err3("query is required for context retrieval");
|
|
2920
|
+
return ok3(
|
|
1063
2921
|
await client2.retrieveContext({
|
|
1064
2922
|
query,
|
|
1065
2923
|
node_limit: limit,
|
|
@@ -1072,8 +2930,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1072
2930
|
})
|
|
1073
2931
|
);
|
|
1074
2932
|
case "text":
|
|
1075
|
-
if (!query) return
|
|
1076
|
-
return
|
|
2933
|
+
if (!query) return err3("query is required for text search");
|
|
2934
|
+
return ok3(
|
|
1077
2935
|
await client2.retrieve({
|
|
1078
2936
|
action: "text",
|
|
1079
2937
|
query,
|
|
@@ -1084,8 +2942,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1084
2942
|
})
|
|
1085
2943
|
);
|
|
1086
2944
|
case "semantic":
|
|
1087
|
-
if (!query) return
|
|
1088
|
-
return
|
|
2945
|
+
if (!query) return err3("query is required for semantic search");
|
|
2946
|
+
return ok3(
|
|
1089
2947
|
await client2.retrieve({
|
|
1090
2948
|
action: "semantic",
|
|
1091
2949
|
query,
|
|
@@ -1096,8 +2954,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1096
2954
|
})
|
|
1097
2955
|
);
|
|
1098
2956
|
case "hybrid":
|
|
1099
|
-
if (!query) return
|
|
1100
|
-
return
|
|
2957
|
+
if (!query) return err3("query is required for hybrid search");
|
|
2958
|
+
return ok3(
|
|
1101
2959
|
await client2.retrieve({
|
|
1102
2960
|
action: "hybrid",
|
|
1103
2961
|
query,
|
|
@@ -1109,17 +2967,17 @@ async function handleRetrieve(client2, args) {
|
|
|
1109
2967
|
);
|
|
1110
2968
|
// Structured queries
|
|
1111
2969
|
case "active_goals":
|
|
1112
|
-
return
|
|
2970
|
+
return ok3(await client2.getGoals());
|
|
1113
2971
|
case "open_questions":
|
|
1114
|
-
return
|
|
2972
|
+
return ok3(await client2.getOpenQuestions());
|
|
1115
2973
|
case "weak_claims":
|
|
1116
|
-
return
|
|
2974
|
+
return ok3(await client2.getWeakClaims());
|
|
1117
2975
|
case "pending_approvals":
|
|
1118
|
-
return
|
|
2976
|
+
return ok3(await client2.getPendingApprovals());
|
|
1119
2977
|
case "unresolved_contradictions":
|
|
1120
|
-
return
|
|
2978
|
+
return ok3(await client2.getContradictions());
|
|
1121
2979
|
case "stale_derivations":
|
|
1122
|
-
return
|
|
2980
|
+
return ok3(
|
|
1123
2981
|
await client2.retrieve({
|
|
1124
2982
|
action: "stale_derivations",
|
|
1125
2983
|
limit,
|
|
@@ -1127,7 +2985,7 @@ async function handleRetrieve(client2, args) {
|
|
|
1127
2985
|
})
|
|
1128
2986
|
);
|
|
1129
2987
|
case "preferences":
|
|
1130
|
-
return
|
|
2988
|
+
return ok3(
|
|
1131
2989
|
await client2.retrieve({
|
|
1132
2990
|
action: "preferences",
|
|
1133
2991
|
...query ? { query, k: limit } : {},
|
|
@@ -1136,8 +2994,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1136
2994
|
})
|
|
1137
2995
|
);
|
|
1138
2996
|
case "layer":
|
|
1139
|
-
if (!layer) return
|
|
1140
|
-
return
|
|
2997
|
+
if (!layer) return err3("layer is required for layer retrieval");
|
|
2998
|
+
return ok3(
|
|
1141
2999
|
await client2.retrieve({
|
|
1142
3000
|
action: "layer",
|
|
1143
3001
|
layer,
|
|
@@ -1146,7 +3004,7 @@ async function handleRetrieve(client2, args) {
|
|
|
1146
3004
|
})
|
|
1147
3005
|
);
|
|
1148
3006
|
case "recent":
|
|
1149
|
-
return
|
|
3007
|
+
return ok3(
|
|
1150
3008
|
await client2.retrieve({
|
|
1151
3009
|
action: "recent",
|
|
1152
3010
|
limit,
|
|
@@ -1158,7 +3016,7 @@ async function handleRetrieve(client2, args) {
|
|
|
1158
3016
|
);
|
|
1159
3017
|
// Document inventory
|
|
1160
3018
|
case "document_index":
|
|
1161
|
-
return
|
|
3019
|
+
return ok3(
|
|
1162
3020
|
await client2.getNodes({
|
|
1163
3021
|
node_type: "Document",
|
|
1164
3022
|
limit: limit ?? 100
|
|
@@ -1166,8 +3024,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1166
3024
|
);
|
|
1167
3025
|
// Graph traversal
|
|
1168
3026
|
case "chain":
|
|
1169
|
-
if (!start_uid) return
|
|
1170
|
-
return
|
|
3027
|
+
if (!start_uid) return err3("start_uid is required for chain traversal");
|
|
3028
|
+
return ok3(
|
|
1171
3029
|
await client2.traverse({
|
|
1172
3030
|
action: "chain",
|
|
1173
3031
|
start_uid,
|
|
@@ -1178,8 +3036,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1178
3036
|
})
|
|
1179
3037
|
);
|
|
1180
3038
|
case "neighborhood":
|
|
1181
|
-
if (!start_uid) return
|
|
1182
|
-
return
|
|
3039
|
+
if (!start_uid) return err3("start_uid is required for neighborhood traversal");
|
|
3040
|
+
return ok3(
|
|
1183
3041
|
await client2.traverse({
|
|
1184
3042
|
action: "neighborhood",
|
|
1185
3043
|
start_uid,
|
|
@@ -1192,9 +3050,9 @@ async function handleRetrieve(client2, args) {
|
|
|
1192
3050
|
})
|
|
1193
3051
|
);
|
|
1194
3052
|
case "path":
|
|
1195
|
-
if (!start_uid) return
|
|
1196
|
-
if (!end_uid) return
|
|
1197
|
-
return
|
|
3053
|
+
if (!start_uid) return err3("start_uid is required for path traversal");
|
|
3054
|
+
if (!end_uid) return err3("end_uid is required for path traversal");
|
|
3055
|
+
return ok3(
|
|
1198
3056
|
await client2.traverse({
|
|
1199
3057
|
action: "path",
|
|
1200
3058
|
start_uid,
|
|
@@ -1208,8 +3066,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1208
3066
|
})
|
|
1209
3067
|
);
|
|
1210
3068
|
case "subgraph":
|
|
1211
|
-
if (!start_uid) return
|
|
1212
|
-
return
|
|
3069
|
+
if (!start_uid) return err3("start_uid is required for subgraph traversal");
|
|
3070
|
+
return ok3(
|
|
1213
3071
|
await client2.traverse({
|
|
1214
3072
|
action: "subgraph",
|
|
1215
3073
|
start_uid,
|
|
@@ -1222,7 +3080,7 @@ async function handleRetrieve(client2, args) {
|
|
|
1222
3080
|
})
|
|
1223
3081
|
);
|
|
1224
3082
|
default:
|
|
1225
|
-
return
|
|
3083
|
+
return err3(`Unknown retrieve action: ${action}`);
|
|
1226
3084
|
}
|
|
1227
3085
|
}
|
|
1228
3086
|
async function handleIngest(client2, args) {
|
|
@@ -1251,7 +3109,7 @@ async function handleIngest(client2, args) {
|
|
|
1251
3109
|
const targetsOntology = Array.isArray(layers) && layers.includes("ontology");
|
|
1252
3110
|
const cognitiveLayers = targetsOntology ? layers.filter((layer) => layer !== "ontology") : layers;
|
|
1253
3111
|
if (targetsOntology && !ontology_schema_id) {
|
|
1254
|
-
return
|
|
3112
|
+
return err3(
|
|
1255
3113
|
"layers included 'ontology' but no ontology_schema_id was supplied. Typed Layer-7 extraction is driven by ontology_schema_id, not by the layers list \u2014 pass the schema id, or drop 'ontology' from layers."
|
|
1256
3114
|
);
|
|
1257
3115
|
}
|
|
@@ -1259,8 +3117,8 @@ async function handleIngest(client2, args) {
|
|
|
1259
3117
|
const ontologyOnly = targetsOntology && forwardedLayers === void 0;
|
|
1260
3118
|
switch (action) {
|
|
1261
3119
|
case "chunk":
|
|
1262
|
-
if (!content) return
|
|
1263
|
-
return
|
|
3120
|
+
if (!content) return err3("content is required for chunk ingestion");
|
|
3121
|
+
return ok3(
|
|
1264
3122
|
await client2.ingestChunk({
|
|
1265
3123
|
content,
|
|
1266
3124
|
label: title,
|
|
@@ -1274,8 +3132,8 @@ async function handleIngest(client2, args) {
|
|
|
1274
3132
|
})
|
|
1275
3133
|
);
|
|
1276
3134
|
case "document":
|
|
1277
|
-
if (!content) return
|
|
1278
|
-
return
|
|
3135
|
+
if (!content) return err3("content is required for document ingestion");
|
|
3136
|
+
return ok3(
|
|
1279
3137
|
await client2.ingestDocument({
|
|
1280
3138
|
content,
|
|
1281
3139
|
title,
|
|
@@ -1294,8 +3152,8 @@ async function handleIngest(client2, args) {
|
|
|
1294
3152
|
})
|
|
1295
3153
|
);
|
|
1296
3154
|
case "session":
|
|
1297
|
-
if (!content) return
|
|
1298
|
-
return
|
|
3155
|
+
if (!content) return err3("content is required for session ingestion");
|
|
3156
|
+
return ok3(
|
|
1299
3157
|
await client2.ingestSession({
|
|
1300
3158
|
content,
|
|
1301
3159
|
title,
|
|
@@ -1312,11 +3170,11 @@ async function handleIngest(client2, args) {
|
|
|
1312
3170
|
);
|
|
1313
3171
|
case "job_status":
|
|
1314
3172
|
if (job_id) {
|
|
1315
|
-
return
|
|
3173
|
+
return ok3(await client2.getJob(job_id));
|
|
1316
3174
|
}
|
|
1317
|
-
return
|
|
3175
|
+
return ok3(await client2.listJobs());
|
|
1318
3176
|
default:
|
|
1319
|
-
return
|
|
3177
|
+
return err3(`Unknown ingest action: ${action}`);
|
|
1320
3178
|
}
|
|
1321
3179
|
}
|
|
1322
3180
|
async function handleSynthesize(client2, args) {
|
|
@@ -1324,19 +3182,19 @@ async function handleSynthesize(client2, args) {
|
|
|
1324
3182
|
switch (action) {
|
|
1325
3183
|
case "signals":
|
|
1326
3184
|
if (!project_uid)
|
|
1327
|
-
return
|
|
1328
|
-
return
|
|
3185
|
+
return err3("project_uid is required for action='signals'");
|
|
3186
|
+
return ok3(
|
|
1329
3187
|
await client2.signals(project_uid, { signals, target_types })
|
|
1330
3188
|
);
|
|
1331
3189
|
case "run":
|
|
1332
3190
|
if (!project_uid)
|
|
1333
|
-
return
|
|
1334
|
-
return
|
|
3191
|
+
return err3("project_uid is required for action='run'");
|
|
3192
|
+
return ok3(await client2.runSynthesis(project_uid));
|
|
1335
3193
|
case "job_status":
|
|
1336
|
-
if (!job_id) return
|
|
1337
|
-
return
|
|
3194
|
+
if (!job_id) return err3("job_id is required for action='job_status'");
|
|
3195
|
+
return ok3(await client2.getJob(job_id));
|
|
1338
3196
|
default:
|
|
1339
|
-
return
|
|
3197
|
+
return err3(`Unknown synthesize action: ${action}`);
|
|
1340
3198
|
}
|
|
1341
3199
|
}
|
|
1342
3200
|
async function handleOntology(client2, args) {
|
|
@@ -1364,18 +3222,18 @@ async function handleOntology(client2, args) {
|
|
|
1364
3222
|
} = args;
|
|
1365
3223
|
switch (action) {
|
|
1366
3224
|
case "schemas":
|
|
1367
|
-
return
|
|
3225
|
+
return ok3(await client2.listOntologySchemas());
|
|
1368
3226
|
case "schema":
|
|
1369
|
-
if (!schema_id) return
|
|
1370
|
-
return
|
|
3227
|
+
if (!schema_id) return err3("schema_id is required for action='schema'");
|
|
3228
|
+
return ok3(await client2.getOntologySchema(schema_id));
|
|
1371
3229
|
case "query": {
|
|
1372
|
-
if (!query) return
|
|
3230
|
+
if (!query) return err3("query is required for action='query'");
|
|
1373
3231
|
if (!schema_id) {
|
|
1374
|
-
return
|
|
3232
|
+
return err3(
|
|
1375
3233
|
"schema_id is required for action='query' (use action='schemas' to find the active schema)"
|
|
1376
3234
|
);
|
|
1377
3235
|
}
|
|
1378
|
-
return
|
|
3236
|
+
return ok3(
|
|
1379
3237
|
await client2.queryOntology({
|
|
1380
3238
|
query,
|
|
1381
3239
|
schema_id,
|
|
@@ -1388,8 +3246,8 @@ async function handleOntology(client2, args) {
|
|
|
1388
3246
|
);
|
|
1389
3247
|
}
|
|
1390
3248
|
case "search":
|
|
1391
|
-
if (!query) return
|
|
1392
|
-
return
|
|
3249
|
+
if (!query) return err3("query is required for action='search'");
|
|
3250
|
+
return ok3(
|
|
1393
3251
|
await client2.searchDomainObjects(query, {
|
|
1394
3252
|
schema_id,
|
|
1395
3253
|
object_types,
|
|
@@ -1397,18 +3255,18 @@ async function handleOntology(client2, args) {
|
|
|
1397
3255
|
})
|
|
1398
3256
|
);
|
|
1399
3257
|
case "objects":
|
|
1400
|
-
if (!schema_id) return
|
|
1401
|
-
return
|
|
3258
|
+
if (!schema_id) return err3("schema_id is required for action='objects'");
|
|
3259
|
+
return ok3(
|
|
1402
3260
|
await client2.listDomainObjects({ schema_id, object_type, limit })
|
|
1403
3261
|
);
|
|
1404
3262
|
case "object":
|
|
1405
|
-
if (!uid) return
|
|
1406
|
-
return
|
|
3263
|
+
if (!uid) return err3("uid is required for action='object'");
|
|
3264
|
+
return ok3(await client2.getDomainObject(uid));
|
|
1407
3265
|
case "object_context":
|
|
1408
|
-
if (!uid) return
|
|
1409
|
-
return
|
|
3266
|
+
if (!uid) return err3("uid is required for action='object_context'");
|
|
3267
|
+
return ok3(await client2.getDomainObjectContext(uid, depth));
|
|
1410
3268
|
case "proposals":
|
|
1411
|
-
return
|
|
3269
|
+
return ok3(
|
|
1412
3270
|
await client2.listOntologyProposals({
|
|
1413
3271
|
status: status ?? "pending",
|
|
1414
3272
|
schema_id,
|
|
@@ -1417,21 +3275,21 @@ async function handleOntology(client2, args) {
|
|
|
1417
3275
|
})
|
|
1418
3276
|
);
|
|
1419
3277
|
case "proposal":
|
|
1420
|
-
if (!proposal_id) return
|
|
1421
|
-
return
|
|
3278
|
+
if (!proposal_id) return err3("proposal_id is required for action='proposal'");
|
|
3279
|
+
return ok3(await client2.getOntologyProposal(proposal_id));
|
|
1422
3280
|
case "approve":
|
|
1423
|
-
if (!proposal_id) return
|
|
1424
|
-
return
|
|
3281
|
+
if (!proposal_id) return err3("proposal_id is required for action='approve'");
|
|
3282
|
+
return ok3(await client2.approveOntologyProposal(proposal_id, { feedback }));
|
|
1425
3283
|
case "reject":
|
|
1426
|
-
if (!proposal_id) return
|
|
1427
|
-
return
|
|
3284
|
+
if (!proposal_id) return err3("proposal_id is required for action='reject'");
|
|
3285
|
+
return ok3(await client2.rejectOntologyProposal(proposal_id, reason));
|
|
1428
3286
|
case "link": {
|
|
1429
3287
|
if (!from_uid || !to_uid || !relation_type) {
|
|
1430
|
-
return
|
|
3288
|
+
return err3(
|
|
1431
3289
|
"from_uid, to_uid, and relation_type are required for action='link'"
|
|
1432
3290
|
);
|
|
1433
3291
|
}
|
|
1434
|
-
return
|
|
3292
|
+
return ok3(
|
|
1435
3293
|
await client2.linkDomainObjects({
|
|
1436
3294
|
from_uid,
|
|
1437
3295
|
to_uid,
|
|
@@ -1442,11 +3300,11 @@ async function handleOntology(client2, args) {
|
|
|
1442
3300
|
);
|
|
1443
3301
|
}
|
|
1444
3302
|
case "extract": {
|
|
1445
|
-
if (!schema_id) return
|
|
3303
|
+
if (!schema_id) return err3("schema_id is required for action='extract'");
|
|
1446
3304
|
if (!source_uids?.length) {
|
|
1447
|
-
return
|
|
3305
|
+
return err3("source_uids is required for action='extract'");
|
|
1448
3306
|
}
|
|
1449
|
-
return
|
|
3307
|
+
return ok3(
|
|
1450
3308
|
await client2.extractOntology({
|
|
1451
3309
|
ontology_schema_id: schema_id,
|
|
1452
3310
|
source_uids
|
|
@@ -1454,26 +3312,36 @@ async function handleOntology(client2, args) {
|
|
|
1454
3312
|
);
|
|
1455
3313
|
}
|
|
1456
3314
|
default:
|
|
1457
|
-
return
|
|
3315
|
+
return err3(`Unknown ontology action: ${action}`);
|
|
1458
3316
|
}
|
|
1459
3317
|
}
|
|
1460
3318
|
|
|
1461
3319
|
// src/generated-tools.ts
|
|
1462
|
-
var
|
|
3320
|
+
var ok4 = (data) => ({
|
|
1463
3321
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1464
3322
|
});
|
|
1465
|
-
var
|
|
3323
|
+
var err4 = (message) => ({
|
|
1466
3324
|
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
1467
3325
|
isError: true
|
|
1468
3326
|
});
|
|
1469
3327
|
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
3328
|
+
var FAILURE_COOLDOWN_MS = 15 * 1e3;
|
|
1470
3329
|
var cache = null;
|
|
3330
|
+
var retryNotBefore = 0;
|
|
3331
|
+
var EMPTY = {
|
|
3332
|
+
tools: [],
|
|
3333
|
+
byName: /* @__PURE__ */ new Map()
|
|
3334
|
+
};
|
|
1471
3335
|
var DuplicateGeneratedToolError = class extends Error {
|
|
1472
3336
|
};
|
|
1473
3337
|
async function getGeneratedTools(client2) {
|
|
1474
|
-
|
|
3338
|
+
const now = Date.now();
|
|
3339
|
+
if (cache && now - cache.fetchedAt < CACHE_TTL_MS) {
|
|
1475
3340
|
return { tools: cache.tools, byName: cache.byName };
|
|
1476
3341
|
}
|
|
3342
|
+
if (now < retryNotBefore) {
|
|
3343
|
+
return cache ? { tools: cache.tools, byName: cache.byName } : EMPTY;
|
|
3344
|
+
}
|
|
1477
3345
|
try {
|
|
1478
3346
|
const c = client2;
|
|
1479
3347
|
if (typeof c.listOntologyTools !== "function") {
|
|
@@ -1497,23 +3365,32 @@ async function getGeneratedTools(client2) {
|
|
|
1497
3365
|
});
|
|
1498
3366
|
}
|
|
1499
3367
|
cache = { tools, byName, fetchedAt: Date.now() };
|
|
3368
|
+
retryNotBefore = 0;
|
|
1500
3369
|
return { tools, byName };
|
|
1501
3370
|
} catch (error) {
|
|
1502
3371
|
if (error instanceof DuplicateGeneratedToolError) throw error;
|
|
1503
|
-
|
|
1504
|
-
return { tools: cache.tools, byName: cache.byName };
|
|
3372
|
+
retryNotBefore = Date.now() + FAILURE_COOLDOWN_MS;
|
|
3373
|
+
return cache ? { tools: cache.tools, byName: cache.byName } : EMPTY;
|
|
1505
3374
|
}
|
|
1506
3375
|
}
|
|
3376
|
+
var STRUCTURED_QUERY_PASSTHROUGH = [
|
|
3377
|
+
"anchor",
|
|
3378
|
+
"path",
|
|
3379
|
+
"where",
|
|
3380
|
+
"include",
|
|
3381
|
+
"page",
|
|
3382
|
+
"aggregate"
|
|
3383
|
+
];
|
|
1507
3384
|
async function handleGeneratedTool(client2, desc, args) {
|
|
1508
3385
|
try {
|
|
1509
3386
|
const ontologyClient = client2;
|
|
1510
3387
|
switch (desc.maps_to) {
|
|
1511
3388
|
case "search": {
|
|
1512
3389
|
const query = args.query;
|
|
1513
|
-
if (!query) return
|
|
3390
|
+
if (!query) return err4(`query is required for ${desc.name}`);
|
|
1514
3391
|
const limit = typeof args.limit === "number" ? args.limit : void 0;
|
|
1515
3392
|
const filters = Array.isArray(args.filters) ? args.filters : void 0;
|
|
1516
|
-
return
|
|
3393
|
+
return ok4(
|
|
1517
3394
|
await ontologyClient.searchDomainObjects(query, {
|
|
1518
3395
|
schema_id: desc.schema_id,
|
|
1519
3396
|
object_types: [desc.object_type],
|
|
@@ -1524,33 +3401,33 @@ async function handleGeneratedTool(client2, desc, args) {
|
|
|
1524
3401
|
}
|
|
1525
3402
|
case "object": {
|
|
1526
3403
|
const uid = args.uid;
|
|
1527
|
-
if (!uid) return
|
|
1528
|
-
return
|
|
3404
|
+
if (!uid) return err4(`uid is required for ${desc.name}`);
|
|
3405
|
+
return ok4(await ontologyClient.getDomainObject(uid, {
|
|
1529
3406
|
schema_id: desc.schema_id,
|
|
1530
3407
|
object_type: desc.object_type
|
|
1531
3408
|
}));
|
|
1532
3409
|
}
|
|
1533
3410
|
case "object_context": {
|
|
1534
3411
|
const uid = args.uid;
|
|
1535
|
-
if (!uid) return
|
|
3412
|
+
if (!uid) return err4(`uid is required for ${desc.name}`);
|
|
1536
3413
|
const depth = typeof args.depth === "number" ? args.depth : void 0;
|
|
1537
|
-
return
|
|
3414
|
+
return ok4(await ontologyClient.getDomainObjectContext(uid, depth, {
|
|
1538
3415
|
schema_id: desc.schema_id,
|
|
1539
3416
|
object_type: desc.object_type
|
|
1540
3417
|
}));
|
|
1541
3418
|
}
|
|
1542
3419
|
case "related": {
|
|
1543
3420
|
const uid = args.uid;
|
|
1544
|
-
if (!uid) return
|
|
3421
|
+
if (!uid) return err4(`uid is required for ${desc.name}`);
|
|
1545
3422
|
if (!desc.relation || !desc.entry_role || !desc.far_type) {
|
|
1546
|
-
return
|
|
3423
|
+
return err4(`invalid related descriptor: ${desc.name}`);
|
|
1547
3424
|
}
|
|
1548
3425
|
if (!ontologyClient.queryRelatedDomainObjects) {
|
|
1549
|
-
return
|
|
3426
|
+
return err4(
|
|
1550
3427
|
"related ontology tools require a graph-aware version of the mindgraph SDK"
|
|
1551
3428
|
);
|
|
1552
3429
|
}
|
|
1553
|
-
return
|
|
3430
|
+
return ok4(await ontologyClient.queryRelatedDomainObjects({
|
|
1554
3431
|
schema_id: desc.schema_id,
|
|
1555
3432
|
entry_type: desc.object_type,
|
|
1556
3433
|
uid,
|
|
@@ -1565,28 +3442,41 @@ async function handleGeneratedTool(client2, desc, args) {
|
|
|
1565
3442
|
}
|
|
1566
3443
|
case "structured_query": {
|
|
1567
3444
|
const select = args.select;
|
|
1568
|
-
if (!select) return
|
|
3445
|
+
if (!select) return err4(`select is required for ${desc.name}`);
|
|
1569
3446
|
if (!ontologyClient.queryDomainStructured) {
|
|
1570
|
-
return
|
|
3447
|
+
return err4(
|
|
1571
3448
|
"structured ontology tools require a graph-aware version of the mindgraph SDK"
|
|
1572
3449
|
);
|
|
1573
3450
|
}
|
|
1574
|
-
|
|
1575
|
-
...args,
|
|
3451
|
+
const request = {
|
|
1576
3452
|
schema_id: desc.schema_id,
|
|
1577
3453
|
select
|
|
1578
|
-
}
|
|
3454
|
+
};
|
|
3455
|
+
for (const field of STRUCTURED_QUERY_PASSTHROUGH) {
|
|
3456
|
+
if (args[field] !== void 0) request[field] = args[field];
|
|
3457
|
+
}
|
|
3458
|
+
return ok4(await ontologyClient.queryDomainStructured(request));
|
|
1579
3459
|
}
|
|
1580
3460
|
default:
|
|
1581
|
-
return
|
|
3461
|
+
return err4(`unknown generated tool mapping: ${desc.maps_to}`);
|
|
1582
3462
|
}
|
|
1583
3463
|
} catch (e) {
|
|
1584
|
-
return
|
|
3464
|
+
return err4(e instanceof Error ? e.message : String(e));
|
|
1585
3465
|
}
|
|
1586
3466
|
}
|
|
1587
3467
|
|
|
1588
3468
|
// src/governance.ts
|
|
1589
3469
|
var capability = "unknown";
|
|
3470
|
+
var UNSUPPORTED_LEASE_MS = 5 * 60 * 1e3;
|
|
3471
|
+
var unsupportedUntil = 0;
|
|
3472
|
+
var unsupportedLogged = false;
|
|
3473
|
+
var DEFAULT_RETRY_DELAYS_MS = [150, 600];
|
|
3474
|
+
function isTransientStatus(status) {
|
|
3475
|
+
return status === 408 || status === 425 || status === 429 || status >= 500;
|
|
3476
|
+
}
|
|
3477
|
+
function sleep(ms) {
|
|
3478
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
3479
|
+
}
|
|
1590
3480
|
var KNOWN_DUTIES = /* @__PURE__ */ new Set(["log", "require_approval"]);
|
|
1591
3481
|
function headers(apiKey, orgId) {
|
|
1592
3482
|
const value = {
|
|
@@ -1602,42 +3492,181 @@ function unsupportedResponse(status, body) {
|
|
|
1602
3492
|
const lower = body.toLowerCase();
|
|
1603
3493
|
return lower.includes("unknown governance action") || lower.includes("unknown action") || lower.includes("invalid_governance_action");
|
|
1604
3494
|
}
|
|
3495
|
+
var PLAN_READ_ACTIONS = /* @__PURE__ */ new Set([
|
|
3496
|
+
"get_plan",
|
|
3497
|
+
"get_executions",
|
|
3498
|
+
"get_pending",
|
|
3499
|
+
"get_assessments",
|
|
3500
|
+
"resume_work"
|
|
3501
|
+
]);
|
|
3502
|
+
var ONTOLOGY_READ_ACTIONS = /* @__PURE__ */ new Set([
|
|
3503
|
+
"list_schemas",
|
|
3504
|
+
"get_schema",
|
|
3505
|
+
"query",
|
|
3506
|
+
"search_objects",
|
|
3507
|
+
"list_objects",
|
|
3508
|
+
"get_object",
|
|
3509
|
+
"get_context",
|
|
3510
|
+
"list_proposals",
|
|
3511
|
+
"get_proposal"
|
|
3512
|
+
]);
|
|
3513
|
+
function toolMutability(toolName, action) {
|
|
3514
|
+
if (toolName === "mindgraph_retrieve") return "read";
|
|
3515
|
+
if (toolName === "mindgraph_plan") {
|
|
3516
|
+
return action && PLAN_READ_ACTIONS.has(action) ? "read" : "write";
|
|
3517
|
+
}
|
|
3518
|
+
if (toolName === "mindgraph_ingest") {
|
|
3519
|
+
return action === "job_status" ? "read" : "write";
|
|
3520
|
+
}
|
|
3521
|
+
if (toolName === "mindgraph_synthesize") {
|
|
3522
|
+
return action === "signals" ? "read" : "write";
|
|
3523
|
+
}
|
|
3524
|
+
if (toolName === "mindgraph_code") {
|
|
3525
|
+
return action === "anchor" ? "write" : "read";
|
|
3526
|
+
}
|
|
3527
|
+
if (toolName === "mindgraph_sync") {
|
|
3528
|
+
return action === "scan" || action === "status" ? "read" : "write";
|
|
3529
|
+
}
|
|
3530
|
+
if (toolName === "mindgraph_ontology") {
|
|
3531
|
+
return action && ONTOLOGY_READ_ACTIONS.has(action) ? "read" : "write";
|
|
3532
|
+
}
|
|
3533
|
+
if (/_(search|get|context|related|query)$/.test(toolName) && toolName !== "mindgraph_capture") {
|
|
3534
|
+
return "read";
|
|
3535
|
+
}
|
|
3536
|
+
return "write";
|
|
3537
|
+
}
|
|
3538
|
+
var UID_FIELDS = [
|
|
3539
|
+
"uid",
|
|
3540
|
+
"source_uid",
|
|
3541
|
+
"session_uid",
|
|
3542
|
+
"work_uid",
|
|
3543
|
+
"goal_uid",
|
|
3544
|
+
"project_uid",
|
|
3545
|
+
"task_uid",
|
|
3546
|
+
"plan_uid",
|
|
3547
|
+
"step_uid",
|
|
3548
|
+
"target_uid",
|
|
3549
|
+
"execution_uid",
|
|
3550
|
+
"executor_uid",
|
|
3551
|
+
"affordance_uid",
|
|
3552
|
+
"produces_node_uid",
|
|
3553
|
+
"filter_plan_uid",
|
|
3554
|
+
"parent_uid",
|
|
3555
|
+
"decision_uid",
|
|
3556
|
+
"chosen_option_uid",
|
|
3557
|
+
"governed_uid",
|
|
3558
|
+
"approval_uid",
|
|
3559
|
+
"requires_plan_uid",
|
|
3560
|
+
"start_uid",
|
|
3561
|
+
"end_uid"
|
|
3562
|
+
];
|
|
3563
|
+
var UID_ARRAY_FIELDS = [
|
|
3564
|
+
"related_uids",
|
|
3565
|
+
"depends_on_uids",
|
|
3566
|
+
"summarizes_uids",
|
|
3567
|
+
"informs_uid",
|
|
3568
|
+
"target_uids",
|
|
3569
|
+
"anchor_uids",
|
|
3570
|
+
"scope_uids",
|
|
3571
|
+
"produces_node_uids"
|
|
3572
|
+
];
|
|
3573
|
+
function governanceTarget(toolName, args) {
|
|
3574
|
+
const action = typeof args.action === "string" ? args.action : void 0;
|
|
3575
|
+
const targetUids = /* @__PURE__ */ new Set();
|
|
3576
|
+
for (const field of UID_FIELDS) {
|
|
3577
|
+
const value = args[field];
|
|
3578
|
+
if (typeof value === "string" && value.length > 0) targetUids.add(value);
|
|
3579
|
+
}
|
|
3580
|
+
for (const field of UID_ARRAY_FIELDS) {
|
|
3581
|
+
const value = args[field];
|
|
3582
|
+
if (!Array.isArray(value)) continue;
|
|
3583
|
+
for (const uid of value) {
|
|
3584
|
+
if (typeof uid === "string" && uid.length > 0) targetUids.add(uid);
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
return {
|
|
3588
|
+
tool_name: toolName,
|
|
3589
|
+
...action ? { action } : {},
|
|
3590
|
+
mutability: toolMutability(toolName, action),
|
|
3591
|
+
target_uids: [...targetUids]
|
|
3592
|
+
};
|
|
3593
|
+
}
|
|
1605
3594
|
async function checkMcpGovernance(toolName, args, config) {
|
|
1606
|
-
if (config.disabled
|
|
3595
|
+
if (config.disabled) return { allowed: true };
|
|
3596
|
+
if (capability === "unsupported") {
|
|
3597
|
+
if (Date.now() < unsupportedUntil) return { allowed: true };
|
|
3598
|
+
capability = "unknown";
|
|
3599
|
+
}
|
|
1607
3600
|
const fetchImpl = config.fetchImpl ?? fetch;
|
|
3601
|
+
const retryDelays = config.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS;
|
|
1608
3602
|
let response;
|
|
1609
|
-
let body;
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
3603
|
+
let body = "";
|
|
3604
|
+
let lastFailure = "";
|
|
3605
|
+
for (let attempt = 0; attempt <= retryDelays.length; attempt++) {
|
|
3606
|
+
try {
|
|
3607
|
+
const candidate = await fetchImpl(
|
|
3608
|
+
`${config.baseUrl.replace(/\/$/, "")}/agent/governance`,
|
|
3609
|
+
{
|
|
3610
|
+
method: "POST",
|
|
3611
|
+
headers: headers(config.apiKey, config.orgId),
|
|
3612
|
+
body: JSON.stringify({
|
|
3613
|
+
action: "check",
|
|
3614
|
+
act: "tool_invoke",
|
|
3615
|
+
agent_id: String(args.agent_id || config.agentId),
|
|
3616
|
+
target: governanceTarget(toolName, args),
|
|
3617
|
+
context: { adapter: "mindgraph-mcp", ...config.context },
|
|
3618
|
+
tier: "checkpoint"
|
|
3619
|
+
})
|
|
3620
|
+
}
|
|
3621
|
+
);
|
|
3622
|
+
const candidateBody = await candidate.text();
|
|
3623
|
+
if (isTransientStatus(candidate.status) && attempt < retryDelays.length) {
|
|
3624
|
+
lastFailure = `HTTP ${candidate.status}`;
|
|
3625
|
+
await sleep(retryDelays[attempt]);
|
|
3626
|
+
continue;
|
|
3627
|
+
}
|
|
3628
|
+
response = candidate;
|
|
3629
|
+
body = candidateBody;
|
|
3630
|
+
break;
|
|
3631
|
+
} catch (error) {
|
|
3632
|
+
lastFailure = error instanceof Error ? error.message : String(error);
|
|
3633
|
+
if (attempt < retryDelays.length) {
|
|
3634
|
+
await sleep(retryDelays[attempt]);
|
|
3635
|
+
continue;
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3639
|
+
if (!response) {
|
|
1625
3640
|
return {
|
|
1626
3641
|
allowed: false,
|
|
1627
|
-
message: `MindGraph governance could not establish permission
|
|
3642
|
+
message: `MindGraph governance could not establish permission after ${retryDelays.length + 1} attempts (${lastFailure}). Set MINDGRAPH_GOVERNANCE=off to run this adapter ungoverned.`
|
|
1628
3643
|
};
|
|
1629
3644
|
}
|
|
1630
3645
|
if (!response.ok) {
|
|
1631
3646
|
if (capability === "unknown" && unsupportedResponse(response.status, body)) {
|
|
1632
3647
|
capability = "unsupported";
|
|
3648
|
+
unsupportedUntil = Date.now() + UNSUPPORTED_LEASE_MS;
|
|
3649
|
+
if (!unsupportedLogged) {
|
|
3650
|
+
unsupportedLogged = true;
|
|
3651
|
+
console.error(
|
|
3652
|
+
`[mindgraph-mcp] This MindGraph server does not implement the governance checkpoint (HTTP ${response.status}); tool calls proceed ungoverned. Re-probing every ${Math.round(UNSUPPORTED_LEASE_MS / 6e4)} minutes.`
|
|
3653
|
+
);
|
|
3654
|
+
}
|
|
1633
3655
|
return { allowed: true };
|
|
1634
3656
|
}
|
|
3657
|
+
if (response.status === 401 || response.status === 403) {
|
|
3658
|
+
return {
|
|
3659
|
+
allowed: false,
|
|
3660
|
+
message: `This credential is not permitted to evaluate MindGraph governance (HTTP ${response.status}). Use a user-scoped API key, or set MINDGRAPH_GOVERNANCE=off to run this adapter ungoverned.`
|
|
3661
|
+
};
|
|
3662
|
+
}
|
|
1635
3663
|
return {
|
|
1636
3664
|
allowed: false,
|
|
1637
3665
|
message: `MindGraph governance check failed (${response.status})`
|
|
1638
3666
|
};
|
|
1639
3667
|
}
|
|
1640
3668
|
capability = "supported";
|
|
3669
|
+
unsupportedLogged = false;
|
|
1641
3670
|
let decision;
|
|
1642
3671
|
try {
|
|
1643
3672
|
decision = JSON.parse(body);
|
|
@@ -1672,18 +3701,124 @@ async function checkMcpGovernance(toolName, args, config) {
|
|
|
1672
3701
|
};
|
|
1673
3702
|
}
|
|
1674
3703
|
|
|
3704
|
+
// src/resources.ts
|
|
3705
|
+
var RESOURCES = [
|
|
3706
|
+
{
|
|
3707
|
+
uri: "mindgraph://stats",
|
|
3708
|
+
name: "Graph Statistics",
|
|
3709
|
+
description: "Current knowledge graph statistics: node counts, edge counts, and layer distribution",
|
|
3710
|
+
mimeType: "application/json"
|
|
3711
|
+
},
|
|
3712
|
+
{
|
|
3713
|
+
uri: "mindgraph://goals",
|
|
3714
|
+
name: "Active Goals",
|
|
3715
|
+
description: "All active goals in the knowledge graph",
|
|
3716
|
+
mimeType: "application/json"
|
|
3717
|
+
},
|
|
3718
|
+
{
|
|
3719
|
+
uri: "mindgraph://questions",
|
|
3720
|
+
name: "Open Questions",
|
|
3721
|
+
description: "All open questions in the knowledge graph",
|
|
3722
|
+
mimeType: "application/json"
|
|
3723
|
+
},
|
|
3724
|
+
{
|
|
3725
|
+
uri: "mindgraph://contradictions",
|
|
3726
|
+
name: "Contradictions",
|
|
3727
|
+
description: "Contradictory claims in the knowledge graph",
|
|
3728
|
+
mimeType: "application/json"
|
|
3729
|
+
},
|
|
3730
|
+
{
|
|
3731
|
+
uri: "mindgraph://decisions",
|
|
3732
|
+
name: "Open Decisions",
|
|
3733
|
+
description: "Unresolved decisions awaiting resolution",
|
|
3734
|
+
mimeType: "application/json"
|
|
3735
|
+
}
|
|
3736
|
+
];
|
|
3737
|
+
var RESOURCE_TEMPLATES = [
|
|
3738
|
+
{
|
|
3739
|
+
uriTemplate: "mindgraph://node/{uid}",
|
|
3740
|
+
name: "Graph Node",
|
|
3741
|
+
description: "Retrieve a specific node by its UID, including all properties, edges, and version history",
|
|
3742
|
+
mimeType: "application/json"
|
|
3743
|
+
},
|
|
3744
|
+
{
|
|
3745
|
+
uriTemplate: "mindgraph://search/{query}",
|
|
3746
|
+
name: "Search Results",
|
|
3747
|
+
description: "BM25 keyword search across all nodes \u2014 pass 1\u20133 discriminating terms (e.g. 'Kissinger NATO'), not natural language sentences",
|
|
3748
|
+
mimeType: "application/json"
|
|
3749
|
+
},
|
|
3750
|
+
{
|
|
3751
|
+
uriTemplate: "mindgraph://layer/{layer}",
|
|
3752
|
+
name: "Layer Nodes",
|
|
3753
|
+
description: "List all nodes in a specific cognitive layer (reality, epistemic, intent, action, memory, agent)",
|
|
3754
|
+
mimeType: "application/json"
|
|
3755
|
+
}
|
|
3756
|
+
];
|
|
3757
|
+
var RESOURCE_GOVERNANCE_TOOL = "mindgraph_retrieve";
|
|
3758
|
+
function json(uri, data) {
|
|
3759
|
+
return {
|
|
3760
|
+
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(data, null, 2) }]
|
|
3761
|
+
};
|
|
3762
|
+
}
|
|
3763
|
+
async function readResource(client2, uri) {
|
|
3764
|
+
switch (uri) {
|
|
3765
|
+
case "mindgraph://stats":
|
|
3766
|
+
return json(uri, await client2.stats());
|
|
3767
|
+
case "mindgraph://goals":
|
|
3768
|
+
return json(uri, await client2.getGoals());
|
|
3769
|
+
case "mindgraph://questions":
|
|
3770
|
+
return json(uri, await client2.getOpenQuestions());
|
|
3771
|
+
case "mindgraph://contradictions":
|
|
3772
|
+
return json(uri, await client2.getContradictions());
|
|
3773
|
+
case "mindgraph://decisions":
|
|
3774
|
+
return json(uri, await client2.getOpenDecisions());
|
|
3775
|
+
}
|
|
3776
|
+
const nodeMatch = uri.match(/^mindgraph:\/\/node\/(.+)$/);
|
|
3777
|
+
if (nodeMatch) {
|
|
3778
|
+
const uid = decodeURIComponent(nodeMatch[1]);
|
|
3779
|
+
const node = await client2.getNode(uid);
|
|
3780
|
+
const edges = await client2.getEdges({ from_uid: uid });
|
|
3781
|
+
const inEdges = await client2.getEdges({ to_uid: uid });
|
|
3782
|
+
return json(uri, { node, outgoing_edges: edges, incoming_edges: inEdges });
|
|
3783
|
+
}
|
|
3784
|
+
const searchMatch = uri.match(/^mindgraph:\/\/search\/(.+)$/);
|
|
3785
|
+
if (searchMatch) {
|
|
3786
|
+
const query = decodeURIComponent(searchMatch[1]);
|
|
3787
|
+
return json(uri, await client2.search(query, { limit: 20 }));
|
|
3788
|
+
}
|
|
3789
|
+
const layerMatch = uri.match(/^mindgraph:\/\/layer\/(.+)$/);
|
|
3790
|
+
if (layerMatch) {
|
|
3791
|
+
const layer = decodeURIComponent(layerMatch[1]);
|
|
3792
|
+
return json(uri, await client2.getNodes({ layer, limit: 50 }));
|
|
3793
|
+
}
|
|
3794
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
3795
|
+
}
|
|
3796
|
+
async function readGovernedResource(client2, uri, governanceConfig) {
|
|
3797
|
+
const governance = await checkMcpGovernance(
|
|
3798
|
+
RESOURCE_GOVERNANCE_TOOL,
|
|
3799
|
+
{},
|
|
3800
|
+
{ ...governanceConfig, context: { resource_uri: uri } }
|
|
3801
|
+
);
|
|
3802
|
+
if (!governance.allowed) {
|
|
3803
|
+
throw new Error(governance.message);
|
|
3804
|
+
}
|
|
3805
|
+
return readResource(client2, uri);
|
|
3806
|
+
}
|
|
3807
|
+
|
|
1675
3808
|
// src/index.ts
|
|
1676
3809
|
var API_KEY = process.env.MINDGRAPH_API_KEY;
|
|
1677
3810
|
var BASE_URL = process.env.MINDGRAPH_BASE_URL || "https://api.mindgraph.cloud";
|
|
1678
3811
|
var AGENT_ID = process.env.MINDGRAPH_AGENT_ID || "mcp";
|
|
1679
3812
|
var ORG_ID = process.env.MINDGRAPH_ORG_ID;
|
|
3813
|
+
var PROFILE = process.env.MINDGRAPH_PROFILE || "general";
|
|
3814
|
+
var HARNESS = process.env.MINDGRAPH_HARNESS || "generic";
|
|
1680
3815
|
if (!API_KEY) {
|
|
1681
3816
|
console.error(
|
|
1682
3817
|
"Error: MINDGRAPH_API_KEY environment variable is required.\nGet your API key at https://mindgraph.cloud/dashboard/keys\nOr run: npx mindgraph-mcp init"
|
|
1683
3818
|
);
|
|
1684
3819
|
process.exit(1);
|
|
1685
3820
|
}
|
|
1686
|
-
var client = new
|
|
3821
|
+
var client = new import_mindgraph2.MindGraph({
|
|
1687
3822
|
baseUrl: BASE_URL,
|
|
1688
3823
|
apiKey: API_KEY,
|
|
1689
3824
|
orgId: ORG_ID,
|
|
@@ -1692,7 +3827,7 @@ var client = new import_mindgraph.MindGraph({
|
|
|
1692
3827
|
// window where npm may still resolve the preceding SDK declaration file.
|
|
1693
3828
|
telemetrySurface: "mcp"
|
|
1694
3829
|
});
|
|
1695
|
-
var INSTRUCTIONS = `You have access to a persistent knowledge graph via MindGraph
|
|
3830
|
+
var INSTRUCTIONS = `You have access to a persistent knowledge graph via MindGraph, plus any ontology tools generated by the active schema.
|
|
1696
3831
|
|
|
1697
3832
|
## #1 RULE: Go straight to the user's topic
|
|
1698
3833
|
|
|
@@ -1755,22 +3890,42 @@ Capture knowledge when the user shares something worth remembering:
|
|
|
1755
3890
|
- Questions to track \u2192 \`mindgraph_reason\` action "open_question"
|
|
1756
3891
|
- Goals or projects \u2192 \`mindgraph_commit\` action "goal"/"project"
|
|
1757
3892
|
- Decisions \u2192 \`mindgraph_commit\` action "open_decision"
|
|
3893
|
+
- Durable engineering work that must survive sessions/harnesses \u2192 \`mindgraph_plan\`
|
|
3894
|
+
- Typed code anchors, attached memory/work, callers/callees, or impact \u2192 \`mindgraph_code\`
|
|
3895
|
+
- Reusable learning from a material attempt \u2192 \`mindgraph_capture\` action "lesson"
|
|
1758
3896
|
- Long-form content \u2192 \`mindgraph_ingest\`
|
|
1759
3897
|
- Cross-document synthesis over a project corpus \u2192 \`mindgraph_synthesize\` (action "signals" for structural signals; action "run" to spawn a synthesis job that writes Article nodes)
|
|
1760
3898
|
|
|
1761
3899
|
## Tool tips
|
|
1762
3900
|
|
|
3901
|
+
- Use the harness todo list to organize only the current harness's local execution. Use \`mindgraph_plan\` when work is authoritative across sessions/harnesses, needs graph relationships or evidence, or must be visible to other agents. Do not maintain the same item in both.
|
|
3902
|
+
- If a local harness task remains unfinished and should become authoritative outside that local list, promote it once with a stable idempotency key when the graph-managed composite actions are available; otherwise do not copy it.
|
|
1763
3903
|
- \`capture\`: "entity"/"observation" for facts, "journal" for personal/informal
|
|
1764
3904
|
- \`reason\`: "claim" when evidence exists, "open_question"/"hypothesis" otherwise
|
|
1765
3905
|
- \`retrieve\`: "context" returns graph nodes only by default \u2014 set include_chunks=true for source text
|
|
1766
3906
|
- \`retrieve\`: "context" (FTS default) \u2192 "semantic" (conceptual fallback) \u2192 "hybrid" (both)
|
|
1767
3907
|
- \`retrieve\`: "neighborhood"/"chain"/"path"/"subgraph" for graph exploration
|
|
3908
|
+
- \`code\`: pass path + symbol + signature when names are overloaded; ambiguity never writes
|
|
1768
3909
|
- Confidence: 0.9+ for facts, 0.5-0.8 for uncertain, <0.5 for speculation
|
|
1769
3910
|
- Don't narrate tool usage \u2014 capture naturally as part of conversation`;
|
|
3911
|
+
if (PROFILE === "coding") {
|
|
3912
|
+
INSTRUCTIONS += `
|
|
3913
|
+
|
|
3914
|
+
## Coding profile
|
|
3915
|
+
|
|
3916
|
+
- 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.
|
|
3917
|
+
- Capture corrections, decisions with rationale, fixes that survived meaningful failed attempts, durable invariants/preferences, and blockers with evidence.
|
|
3918
|
+
- Do not capture routine mechanics, transient command output, facts cheaply derivable from code, unsupported speculation as high-confidence lessons, or completion without evidence.
|
|
3919
|
+
- Adapter context is owned by ${HARNESS}; omit invocation_context because hooks replace it.`;
|
|
3920
|
+
} else {
|
|
3921
|
+
INSTRUCTIONS = INSTRUCTIONS.split("\n").filter(
|
|
3922
|
+
(line) => !line.includes("mindgraph_code") && !line.includes("`code`") && !line.includes("Typed code anchors")
|
|
3923
|
+
).join("\n");
|
|
3924
|
+
}
|
|
1770
3925
|
var server = new import_server.Server(
|
|
1771
3926
|
{
|
|
1772
3927
|
name: "mindgraph",
|
|
1773
|
-
version: "0.
|
|
3928
|
+
version: "0.14.0"
|
|
1774
3929
|
},
|
|
1775
3930
|
{
|
|
1776
3931
|
capabilities: {
|
|
@@ -1783,21 +3938,25 @@ var server = new import_server.Server(
|
|
|
1783
3938
|
);
|
|
1784
3939
|
server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
|
|
1785
3940
|
const generated = await getGeneratedTools(client);
|
|
1786
|
-
return { tools: [...
|
|
3941
|
+
return { tools: [...toolsForProfile(PROFILE), ...generated.tools] };
|
|
1787
3942
|
});
|
|
3943
|
+
var GOVERNANCE_CONFIG = {
|
|
3944
|
+
baseUrl: BASE_URL,
|
|
3945
|
+
apiKey: API_KEY,
|
|
3946
|
+
orgId: ORG_ID,
|
|
3947
|
+
agentId: AGENT_ID,
|
|
3948
|
+
get disabled() {
|
|
3949
|
+
return process.env.MINDGRAPH_GOVERNANCE === "off";
|
|
3950
|
+
}
|
|
3951
|
+
};
|
|
1788
3952
|
server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
|
|
1789
3953
|
const { name, arguments: args } = request.params;
|
|
1790
|
-
const
|
|
3954
|
+
const rawArgs = { ...args || {} };
|
|
3955
|
+
const toolArgs = { ...rawArgs };
|
|
1791
3956
|
if (!toolArgs.agent_id) {
|
|
1792
3957
|
toolArgs.agent_id = AGENT_ID;
|
|
1793
3958
|
}
|
|
1794
|
-
const governance = await checkMcpGovernance(name, toolArgs,
|
|
1795
|
-
baseUrl: BASE_URL,
|
|
1796
|
-
apiKey: API_KEY,
|
|
1797
|
-
orgId: ORG_ID,
|
|
1798
|
-
agentId: AGENT_ID,
|
|
1799
|
-
disabled: process.env.MINDGRAPH_GOVERNANCE === "off"
|
|
1800
|
-
});
|
|
3959
|
+
const governance = await checkMcpGovernance(name, toolArgs, GOVERNANCE_CONFIG);
|
|
1801
3960
|
if (!governance.allowed) {
|
|
1802
3961
|
return {
|
|
1803
3962
|
content: [{ type: "text", text: JSON.stringify({ error: governance.message, ...governance }) }],
|
|
@@ -1807,7 +3966,7 @@ server.setRequestHandler(import_types.CallToolRequestSchema, async (request) =>
|
|
|
1807
3966
|
const generated = await getGeneratedTools(client);
|
|
1808
3967
|
const desc = generated.byName.get(name);
|
|
1809
3968
|
if (desc) {
|
|
1810
|
-
return handleGeneratedTool(client, desc,
|
|
3969
|
+
return handleGeneratedTool(client, desc, rawArgs);
|
|
1811
3970
|
}
|
|
1812
3971
|
return handleTool(client, name, toolArgs);
|
|
1813
3972
|
});
|
|
@@ -1968,178 +4127,13 @@ ${conversation}`
|
|
|
1968
4127
|
}
|
|
1969
4128
|
});
|
|
1970
4129
|
server.setRequestHandler(import_types.ListResourcesRequestSchema, async () => {
|
|
1971
|
-
return {
|
|
1972
|
-
resources: [
|
|
1973
|
-
{
|
|
1974
|
-
uri: "mindgraph://stats",
|
|
1975
|
-
name: "Graph Statistics",
|
|
1976
|
-
description: "Current knowledge graph statistics: node counts, edge counts, and layer distribution",
|
|
1977
|
-
mimeType: "application/json"
|
|
1978
|
-
},
|
|
1979
|
-
{
|
|
1980
|
-
uri: "mindgraph://goals",
|
|
1981
|
-
name: "Active Goals",
|
|
1982
|
-
description: "All active goals in the knowledge graph",
|
|
1983
|
-
mimeType: "application/json"
|
|
1984
|
-
},
|
|
1985
|
-
{
|
|
1986
|
-
uri: "mindgraph://questions",
|
|
1987
|
-
name: "Open Questions",
|
|
1988
|
-
description: "All open questions in the knowledge graph",
|
|
1989
|
-
mimeType: "application/json"
|
|
1990
|
-
},
|
|
1991
|
-
{
|
|
1992
|
-
uri: "mindgraph://contradictions",
|
|
1993
|
-
name: "Contradictions",
|
|
1994
|
-
description: "Contradictory claims in the knowledge graph",
|
|
1995
|
-
mimeType: "application/json"
|
|
1996
|
-
},
|
|
1997
|
-
{
|
|
1998
|
-
uri: "mindgraph://decisions",
|
|
1999
|
-
name: "Open Decisions",
|
|
2000
|
-
description: "Unresolved decisions awaiting resolution",
|
|
2001
|
-
mimeType: "application/json"
|
|
2002
|
-
}
|
|
2003
|
-
]
|
|
2004
|
-
};
|
|
4130
|
+
return { resources: RESOURCES };
|
|
2005
4131
|
});
|
|
2006
4132
|
server.setRequestHandler(import_types.ListResourceTemplatesRequestSchema, async () => {
|
|
2007
|
-
return {
|
|
2008
|
-
resourceTemplates: [
|
|
2009
|
-
{
|
|
2010
|
-
uriTemplate: "mindgraph://node/{uid}",
|
|
2011
|
-
name: "Graph Node",
|
|
2012
|
-
description: "Retrieve a specific node by its UID, including all properties, edges, and version history",
|
|
2013
|
-
mimeType: "application/json"
|
|
2014
|
-
},
|
|
2015
|
-
{
|
|
2016
|
-
uriTemplate: "mindgraph://search/{query}",
|
|
2017
|
-
name: "Search Results",
|
|
2018
|
-
description: "BM25 keyword search across all nodes \u2014 pass 1\u20133 discriminating terms (e.g. 'Kissinger NATO'), not natural language sentences",
|
|
2019
|
-
mimeType: "application/json"
|
|
2020
|
-
},
|
|
2021
|
-
{
|
|
2022
|
-
uriTemplate: "mindgraph://layer/{layer}",
|
|
2023
|
-
name: "Layer Nodes",
|
|
2024
|
-
description: "List all nodes in a specific cognitive layer (reality, epistemic, intent, action, memory, agent)",
|
|
2025
|
-
mimeType: "application/json"
|
|
2026
|
-
}
|
|
2027
|
-
]
|
|
2028
|
-
};
|
|
4133
|
+
return { resourceTemplates: RESOURCE_TEMPLATES };
|
|
2029
4134
|
});
|
|
2030
4135
|
server.setRequestHandler(import_types.ReadResourceRequestSchema, async (request) => {
|
|
2031
|
-
|
|
2032
|
-
switch (uri) {
|
|
2033
|
-
case "mindgraph://stats": {
|
|
2034
|
-
const stats = await client.stats();
|
|
2035
|
-
return {
|
|
2036
|
-
contents: [
|
|
2037
|
-
{
|
|
2038
|
-
uri,
|
|
2039
|
-
mimeType: "application/json",
|
|
2040
|
-
text: JSON.stringify(stats, null, 2)
|
|
2041
|
-
}
|
|
2042
|
-
]
|
|
2043
|
-
};
|
|
2044
|
-
}
|
|
2045
|
-
case "mindgraph://goals": {
|
|
2046
|
-
const goals = await client.getGoals();
|
|
2047
|
-
return {
|
|
2048
|
-
contents: [
|
|
2049
|
-
{
|
|
2050
|
-
uri,
|
|
2051
|
-
mimeType: "application/json",
|
|
2052
|
-
text: JSON.stringify(goals, null, 2)
|
|
2053
|
-
}
|
|
2054
|
-
]
|
|
2055
|
-
};
|
|
2056
|
-
}
|
|
2057
|
-
case "mindgraph://questions": {
|
|
2058
|
-
const questions = await client.getOpenQuestions();
|
|
2059
|
-
return {
|
|
2060
|
-
contents: [
|
|
2061
|
-
{
|
|
2062
|
-
uri,
|
|
2063
|
-
mimeType: "application/json",
|
|
2064
|
-
text: JSON.stringify(questions, null, 2)
|
|
2065
|
-
}
|
|
2066
|
-
]
|
|
2067
|
-
};
|
|
2068
|
-
}
|
|
2069
|
-
case "mindgraph://contradictions": {
|
|
2070
|
-
const contradictions = await client.getContradictions();
|
|
2071
|
-
return {
|
|
2072
|
-
contents: [
|
|
2073
|
-
{
|
|
2074
|
-
uri,
|
|
2075
|
-
mimeType: "application/json",
|
|
2076
|
-
text: JSON.stringify(contradictions, null, 2)
|
|
2077
|
-
}
|
|
2078
|
-
]
|
|
2079
|
-
};
|
|
2080
|
-
}
|
|
2081
|
-
case "mindgraph://decisions": {
|
|
2082
|
-
const decisions = await client.getOpenDecisions();
|
|
2083
|
-
return {
|
|
2084
|
-
contents: [
|
|
2085
|
-
{
|
|
2086
|
-
uri,
|
|
2087
|
-
mimeType: "application/json",
|
|
2088
|
-
text: JSON.stringify(decisions, null, 2)
|
|
2089
|
-
}
|
|
2090
|
-
]
|
|
2091
|
-
};
|
|
2092
|
-
}
|
|
2093
|
-
}
|
|
2094
|
-
const nodeMatch = uri.match(/^mindgraph:\/\/node\/(.+)$/);
|
|
2095
|
-
if (nodeMatch) {
|
|
2096
|
-
const uid = decodeURIComponent(nodeMatch[1]);
|
|
2097
|
-
const node = await client.getNode(uid);
|
|
2098
|
-
const edges = await client.getEdges({ from_uid: uid });
|
|
2099
|
-
const inEdges = await client.getEdges({ to_uid: uid });
|
|
2100
|
-
return {
|
|
2101
|
-
contents: [
|
|
2102
|
-
{
|
|
2103
|
-
uri,
|
|
2104
|
-
mimeType: "application/json",
|
|
2105
|
-
text: JSON.stringify(
|
|
2106
|
-
{ node, outgoing_edges: edges, incoming_edges: inEdges },
|
|
2107
|
-
null,
|
|
2108
|
-
2
|
|
2109
|
-
)
|
|
2110
|
-
}
|
|
2111
|
-
]
|
|
2112
|
-
};
|
|
2113
|
-
}
|
|
2114
|
-
const searchMatch = uri.match(/^mindgraph:\/\/search\/(.+)$/);
|
|
2115
|
-
if (searchMatch) {
|
|
2116
|
-
const query = decodeURIComponent(searchMatch[1]);
|
|
2117
|
-
const results = await client.search(query, { limit: 20 });
|
|
2118
|
-
return {
|
|
2119
|
-
contents: [
|
|
2120
|
-
{
|
|
2121
|
-
uri,
|
|
2122
|
-
mimeType: "application/json",
|
|
2123
|
-
text: JSON.stringify(results, null, 2)
|
|
2124
|
-
}
|
|
2125
|
-
]
|
|
2126
|
-
};
|
|
2127
|
-
}
|
|
2128
|
-
const layerMatch = uri.match(/^mindgraph:\/\/layer\/(.+)$/);
|
|
2129
|
-
if (layerMatch) {
|
|
2130
|
-
const layer = decodeURIComponent(layerMatch[1]);
|
|
2131
|
-
const nodes = await client.getNodes({ layer, limit: 50 });
|
|
2132
|
-
return {
|
|
2133
|
-
contents: [
|
|
2134
|
-
{
|
|
2135
|
-
uri,
|
|
2136
|
-
mimeType: "application/json",
|
|
2137
|
-
text: JSON.stringify(nodes, null, 2)
|
|
2138
|
-
}
|
|
2139
|
-
]
|
|
2140
|
-
};
|
|
2141
|
-
}
|
|
2142
|
-
throw new Error(`Unknown resource: ${uri}`);
|
|
4136
|
+
return readGovernedResource(client, request.params.uri, GOVERNANCE_CONFIG);
|
|
2143
4137
|
});
|
|
2144
4138
|
async function main() {
|
|
2145
4139
|
const transport = new import_stdio.StdioServerTransport();
|