mindgraph-mcp 0.13.5 → 0.14.1
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 +2941 -216
- package/dist/index.js +2145 -160
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,22 +1,1439 @@
|
|
|
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/error-detail.ts
|
|
7
32
|
var import_mindgraph = require("mindgraph");
|
|
33
|
+
function errorDetail(e) {
|
|
34
|
+
if (e instanceof import_mindgraph.MindGraphError && e.body !== void 0) {
|
|
35
|
+
const detail = typeof e.body === "string" ? e.body : JSON.stringify(e.body);
|
|
36
|
+
return `${e.message} \u2014 ${detail}`;
|
|
37
|
+
}
|
|
38
|
+
return e instanceof Error ? e.message : String(e);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/codegraph.ts
|
|
42
|
+
var import_node_child_process = require("child_process");
|
|
43
|
+
var import_node_fs = require("fs");
|
|
44
|
+
var import_promises = require("fs/promises");
|
|
45
|
+
var import_node_os = require("os");
|
|
46
|
+
var import_node_path = require("path");
|
|
47
|
+
var import_node_crypto = require("crypto");
|
|
48
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
49
|
+
var DEFAULT_STATUS_TTL_MS = 6e4;
|
|
50
|
+
var MAX_OUTPUT_BYTES = 5 * 1024 * 1024;
|
|
51
|
+
function normalizePath(path2) {
|
|
52
|
+
return path2.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
53
|
+
}
|
|
54
|
+
function normalizeSignature(signature) {
|
|
55
|
+
return signature.replace(/\s+/g, " ").trim();
|
|
56
|
+
}
|
|
57
|
+
function languageFromPath(path2) {
|
|
58
|
+
const extension = (0, import_node_path.extname)(path2).toLowerCase();
|
|
59
|
+
return {
|
|
60
|
+
".c": "c",
|
|
61
|
+
".cc": "cpp",
|
|
62
|
+
".cpp": "cpp",
|
|
63
|
+
".go": "go",
|
|
64
|
+
".java": "java",
|
|
65
|
+
".js": "javascript",
|
|
66
|
+
".jsx": "javascript",
|
|
67
|
+
".kt": "kotlin",
|
|
68
|
+
".py": "python",
|
|
69
|
+
".rb": "ruby",
|
|
70
|
+
".rs": "rust",
|
|
71
|
+
".swift": "swift",
|
|
72
|
+
".ts": "typescript",
|
|
73
|
+
".tsx": "typescript"
|
|
74
|
+
}[extension] ?? "unknown";
|
|
75
|
+
}
|
|
76
|
+
function asNode(hit) {
|
|
77
|
+
return hit.node ?? hit;
|
|
78
|
+
}
|
|
79
|
+
function toCandidate(node) {
|
|
80
|
+
if (!node.filePath || !node.kind || !(node.qualifiedName || node.name)) return null;
|
|
81
|
+
return {
|
|
82
|
+
path: normalizePath(node.filePath),
|
|
83
|
+
language: node.language ?? languageFromPath(node.filePath),
|
|
84
|
+
kind: node.kind,
|
|
85
|
+
qualifiedName: node.qualifiedName ?? node.name ?? "",
|
|
86
|
+
signature: node.signature ?? null,
|
|
87
|
+
startLine: node.startLine ?? 1,
|
|
88
|
+
endLine: node.endLine ?? node.startLine ?? 1
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function normalizeRemote(remote) {
|
|
92
|
+
const trimmed = remote.trim();
|
|
93
|
+
if (!trimmed) return null;
|
|
94
|
+
const scp = /^(?:[^@]+@)?([^:]+):(.+)$/.exec(trimmed);
|
|
95
|
+
if (scp && !trimmed.includes("://")) {
|
|
96
|
+
return `${scp[1].toLowerCase()}/${scp[2].replace(/\.git$/, "")}`;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const url = new URL(trimmed);
|
|
100
|
+
const pathname = url.pathname.replace(/\/$/, "").replace(/\.git$/, "");
|
|
101
|
+
return `${url.host.toLowerCase()}${pathname}`;
|
|
102
|
+
} catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function pathContains(root, candidate) {
|
|
107
|
+
const child = (0, import_node_path.relative)(root, candidate);
|
|
108
|
+
return child === "" || child !== ".." && !child.startsWith(`..${import_node_path.sep}`) && !(0, import_node_path.isAbsolute)(child);
|
|
109
|
+
}
|
|
110
|
+
function nearestExistingDirectory(candidate) {
|
|
111
|
+
let current = (0, import_node_path.resolve)(candidate);
|
|
112
|
+
for (; ; ) {
|
|
113
|
+
if ((0, import_node_fs.existsSync)(current)) {
|
|
114
|
+
try {
|
|
115
|
+
return (0, import_node_fs.statSync)(current).isDirectory() ? current : (0, import_node_path.dirname)(current);
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const parent = (0, import_node_path.dirname)(current);
|
|
120
|
+
if (parent === current) return current;
|
|
121
|
+
current = parent;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function findWorkspaceFile(start, explicit) {
|
|
125
|
+
if (explicit) return (0, import_node_path.resolve)(start, explicit);
|
|
126
|
+
let current = (0, import_node_path.resolve)(start);
|
|
127
|
+
for (; ; ) {
|
|
128
|
+
const candidate = (0, import_node_path.join)(current, ".mindgraph", "workspace.json");
|
|
129
|
+
if ((0, import_node_fs.existsSync)(candidate)) return candidate;
|
|
130
|
+
const parent = (0, import_node_path.dirname)(current);
|
|
131
|
+
if (parent === current) return void 0;
|
|
132
|
+
current = parent;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function runProcess(executable, args, timeoutMs, env, cwd) {
|
|
136
|
+
return await new Promise((resolvePromise, rejectPromise) => {
|
|
137
|
+
const child = (0, import_node_child_process.spawn)(executable, args, {
|
|
138
|
+
cwd,
|
|
139
|
+
env,
|
|
140
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
141
|
+
windowsHide: true
|
|
142
|
+
});
|
|
143
|
+
const stdout = [];
|
|
144
|
+
const stderr = [];
|
|
145
|
+
let outputBytes = 0;
|
|
146
|
+
let timedOut = false;
|
|
147
|
+
const timer = setTimeout(() => {
|
|
148
|
+
timedOut = true;
|
|
149
|
+
child.kill("SIGTERM");
|
|
150
|
+
setTimeout(() => child.kill("SIGKILL"), 250).unref();
|
|
151
|
+
}, timeoutMs);
|
|
152
|
+
timer.unref();
|
|
153
|
+
const collect = (target) => (chunk) => {
|
|
154
|
+
outputBytes += chunk.length;
|
|
155
|
+
if (outputBytes > MAX_OUTPUT_BYTES) {
|
|
156
|
+
child.kill("SIGTERM");
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
target.push(chunk);
|
|
160
|
+
};
|
|
161
|
+
child.stdout.on("data", collect(stdout));
|
|
162
|
+
child.stderr.on("data", collect(stderr));
|
|
163
|
+
child.on("error", (cause) => {
|
|
164
|
+
clearTimeout(timer);
|
|
165
|
+
const error = new Error(cause.message);
|
|
166
|
+
error.kind = cause.code === "ENOENT" ? "absent" : "command_failed";
|
|
167
|
+
rejectPromise(error);
|
|
168
|
+
});
|
|
169
|
+
child.on("close", (code) => {
|
|
170
|
+
clearTimeout(timer);
|
|
171
|
+
if (timedOut) {
|
|
172
|
+
const error = new Error(`command timed out after ${timeoutMs}ms`);
|
|
173
|
+
error.kind = "timeout";
|
|
174
|
+
rejectPromise(error);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (outputBytes > MAX_OUTPUT_BYTES) {
|
|
178
|
+
const error = new Error("codegraph output exceeded 5 MiB");
|
|
179
|
+
error.kind = "command_failed";
|
|
180
|
+
rejectPromise(error);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (code !== 0) {
|
|
184
|
+
const error = new Error(Buffer.concat(stderr).toString("utf8").trim());
|
|
185
|
+
error.kind = "command_failed";
|
|
186
|
+
error.exitCode = code;
|
|
187
|
+
rejectPromise(error);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
resolvePromise(Buffer.concat(stdout).toString("utf8"));
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
var CodegraphAdapter = class {
|
|
195
|
+
timeoutMs;
|
|
196
|
+
statusTtlMs;
|
|
197
|
+
env;
|
|
198
|
+
cwd;
|
|
199
|
+
workspaceFile;
|
|
200
|
+
now;
|
|
201
|
+
identityCache = /* @__PURE__ */ new Map();
|
|
202
|
+
statusCache = /* @__PURE__ */ new Map();
|
|
203
|
+
constructor(options = {}) {
|
|
204
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
205
|
+
this.statusTtlMs = options.statusTtlMs ?? DEFAULT_STATUS_TTL_MS;
|
|
206
|
+
this.env = options.env ?? process.env;
|
|
207
|
+
this.cwd = options.cwd ?? process.cwd();
|
|
208
|
+
this.workspaceFile = findWorkspaceFile(
|
|
209
|
+
this.cwd,
|
|
210
|
+
options.workspaceFile ?? this.env.MINDGRAPH_WORKSPACE_FILE
|
|
211
|
+
);
|
|
212
|
+
this.now = options.now ?? Date.now;
|
|
213
|
+
}
|
|
214
|
+
async resolveRepository(repo, fallbackSpaceUid) {
|
|
215
|
+
const configured = await this.resolveRepositories(fallbackSpaceUid);
|
|
216
|
+
const selected = repo && configured.find((candidate) => candidate.repoId === repo) || repo && configured.find(
|
|
217
|
+
(candidate) => candidate.repoRoot === repo || (0, import_node_path.basename)(candidate.repoRoot) === repo
|
|
218
|
+
) || (!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);
|
|
219
|
+
if (selected) {
|
|
220
|
+
return !repo && this.env.MINDGRAPH_REPO_ID ? this.resolveRepositoryIdentity(
|
|
221
|
+
selected.repoRoot,
|
|
222
|
+
this.env.MINDGRAPH_REPO_ID,
|
|
223
|
+
selected.spaceUid
|
|
224
|
+
) : selected;
|
|
225
|
+
}
|
|
226
|
+
const requestedRoot = repo || this.env.MINDGRAPH_REPO_ROOT || this.cwd;
|
|
227
|
+
const requestedPath = await (0, import_promises.realpath)((0, import_node_path.resolve)(this.cwd, requestedRoot)).catch(
|
|
228
|
+
() => (0, import_node_path.resolve)(this.cwd, requestedRoot)
|
|
229
|
+
);
|
|
230
|
+
const repoRoot = await this.gitRoot(requestedPath) ?? requestedPath;
|
|
231
|
+
return this.resolveRepositoryIdentity(
|
|
232
|
+
repoRoot,
|
|
233
|
+
this.env.MINDGRAPH_REPO_ID,
|
|
234
|
+
this.env.MINDGRAPH_CODE_SPACE_UID || fallbackSpaceUid
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
async resolveRepositories(fallbackSpaceUid) {
|
|
238
|
+
return Promise.all(
|
|
239
|
+
this.configuredRepositories().map(
|
|
240
|
+
(repository) => this.resolveRepositoryIdentity(
|
|
241
|
+
repository.repoRoot,
|
|
242
|
+
repository.repoId,
|
|
243
|
+
repository.spaceUid || this.env.MINDGRAPH_CODE_SPACE_UID || fallbackSpaceUid
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
async resolveRepositoryIdentity(root, explicitRepoId, spaceUid) {
|
|
249
|
+
const repoRoot = await (0, import_promises.realpath)(root).catch(() => (0, import_node_path.resolve)(root));
|
|
250
|
+
const cacheKey = `${repoRoot}\0${explicitRepoId ?? ""}\0${spaceUid ?? ""}`;
|
|
251
|
+
const cached = this.identityCache.get(cacheKey);
|
|
252
|
+
if (cached) return cached;
|
|
253
|
+
const pending = this.loadRepositoryIdentity(repoRoot, explicitRepoId, spaceUid);
|
|
254
|
+
this.identityCache.set(cacheKey, pending);
|
|
255
|
+
return pending;
|
|
256
|
+
}
|
|
257
|
+
async loadRepositoryIdentity(repoRoot, explicitRepoId, spaceUid) {
|
|
258
|
+
const remote = await runProcess(
|
|
259
|
+
"git",
|
|
260
|
+
["-C", repoRoot, "config", "--get", "remote.origin.url"],
|
|
261
|
+
Math.min(this.timeoutMs, 2e3),
|
|
262
|
+
this.env
|
|
263
|
+
).catch(() => "");
|
|
264
|
+
const repoId = explicitRepoId || normalizeRemote(remote) || await this.persistedRepositoryId(repoRoot);
|
|
265
|
+
return {
|
|
266
|
+
repoId,
|
|
267
|
+
repoRoot,
|
|
268
|
+
spaceUid
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
async persistedRepositoryId(repoRoot) {
|
|
272
|
+
const rawCommonDir = await runProcess(
|
|
273
|
+
"git",
|
|
274
|
+
["-C", repoRoot, "rev-parse", "--git-common-dir"],
|
|
275
|
+
Math.min(this.timeoutMs, 2e3),
|
|
276
|
+
this.env
|
|
277
|
+
).catch(() => "");
|
|
278
|
+
if (!rawCommonDir.trim()) {
|
|
279
|
+
return `local:${(0, import_node_path.basename)(repoRoot)}`;
|
|
280
|
+
}
|
|
281
|
+
const commonDir = (0, import_node_path.resolve)(repoRoot, rawCommonDir.trim());
|
|
282
|
+
const identityDir = (0, import_node_path.join)(commonDir, "mindgraph");
|
|
283
|
+
const identityFile = (0, import_node_path.join)(identityDir, "repository-id");
|
|
284
|
+
try {
|
|
285
|
+
const existing = (0, import_node_fs.readFileSync)(identityFile, "utf8").trim();
|
|
286
|
+
if (existing) return existing;
|
|
287
|
+
} catch {
|
|
288
|
+
}
|
|
289
|
+
(0, import_node_fs.mkdirSync)(identityDir, { recursive: true, mode: 448 });
|
|
290
|
+
const generated = (0, import_node_crypto.randomUUID)();
|
|
291
|
+
try {
|
|
292
|
+
(0, import_node_fs.writeFileSync)(identityFile, `${generated}
|
|
293
|
+
`, {
|
|
294
|
+
encoding: "utf8",
|
|
295
|
+
flag: "wx",
|
|
296
|
+
mode: 384
|
|
297
|
+
});
|
|
298
|
+
return generated;
|
|
299
|
+
} catch (cause) {
|
|
300
|
+
if (cause.code !== "EEXIST") throw cause;
|
|
301
|
+
const winner = (0, import_node_fs.readFileSync)(identityFile, "utf8").trim();
|
|
302
|
+
if (!winner) throw new Error(`empty repository identity file: ${identityFile}`);
|
|
303
|
+
return winner;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
async gitRoot(candidate) {
|
|
307
|
+
const start = nearestExistingDirectory(candidate);
|
|
308
|
+
const raw = await runProcess(
|
|
309
|
+
"git",
|
|
310
|
+
["-C", start, "rev-parse", "--show-toplevel"],
|
|
311
|
+
Math.min(this.timeoutMs, 2e3),
|
|
312
|
+
this.env
|
|
313
|
+
).catch(() => "");
|
|
314
|
+
return raw.trim() ? (0, import_node_path.resolve)(start, raw.trim()) : void 0;
|
|
315
|
+
}
|
|
316
|
+
async availability(repository) {
|
|
317
|
+
const cached = this.statusCache.get(repository.repoRoot);
|
|
318
|
+
if (cached && cached.expiresAt > this.now()) return cached.availability;
|
|
319
|
+
try {
|
|
320
|
+
const status = await this.runJson([
|
|
321
|
+
"status",
|
|
322
|
+
repository.repoRoot,
|
|
323
|
+
"-j"
|
|
324
|
+
]);
|
|
325
|
+
const caveats = [];
|
|
326
|
+
let availability;
|
|
327
|
+
if (status.worktreeMismatch != null) {
|
|
328
|
+
availability = {
|
|
329
|
+
available: false,
|
|
330
|
+
reason: "wrong_index",
|
|
331
|
+
stale: false,
|
|
332
|
+
caveats: ["the index belongs to a different worktree"],
|
|
333
|
+
lastIndexed: status.lastIndexed
|
|
334
|
+
};
|
|
335
|
+
} else if (!status.initialized || status.index?.state !== "complete") {
|
|
336
|
+
availability = {
|
|
337
|
+
available: false,
|
|
338
|
+
reason: "index_incomplete",
|
|
339
|
+
stale: false,
|
|
340
|
+
caveats: [
|
|
341
|
+
!status.initialized ? "codegraph is not initialized for this repository" : `codegraph index state is ${status.index?.state ?? "unknown"}`
|
|
342
|
+
],
|
|
343
|
+
lastIndexed: status.lastIndexed
|
|
344
|
+
};
|
|
345
|
+
} else {
|
|
346
|
+
const pending = (status.pendingChanges?.added ?? 0) + (status.pendingChanges?.modified ?? 0) + (status.pendingChanges?.removed ?? 0);
|
|
347
|
+
if (pending > 0) caveats.push(`${pending} worktree changes are not indexed`);
|
|
348
|
+
if ((status.index?.pendingRefs ?? 0) > 0) {
|
|
349
|
+
caveats.push(`${status.index?.pendingRefs} references remain pending`);
|
|
350
|
+
}
|
|
351
|
+
if (status.index?.reindexRecommended) caveats.push("codegraph recommends reindexing");
|
|
352
|
+
availability = {
|
|
353
|
+
available: true,
|
|
354
|
+
stale: caveats.length > 0,
|
|
355
|
+
caveats,
|
|
356
|
+
lastIndexed: status.lastIndexed
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
this.statusCache.set(repository.repoRoot, {
|
|
360
|
+
status,
|
|
361
|
+
availability,
|
|
362
|
+
expiresAt: this.now() + this.statusTtlMs
|
|
363
|
+
});
|
|
364
|
+
return availability;
|
|
365
|
+
} catch (cause) {
|
|
366
|
+
const failure = cause;
|
|
367
|
+
return {
|
|
368
|
+
available: false,
|
|
369
|
+
reason: failure.kind ?? "command_failed",
|
|
370
|
+
stale: false,
|
|
371
|
+
caveats: [failure.message || "codegraph status failed"]
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
async resolveRefs(refs, options = {}) {
|
|
376
|
+
const normalized = refs.map(
|
|
377
|
+
(ref) => typeof ref === "string" ? { symbol: ref } : ref
|
|
378
|
+
);
|
|
379
|
+
const results = await Promise.all(
|
|
380
|
+
normalized.map(async (ref) => {
|
|
381
|
+
const repository2 = await this.repositoryForRef(
|
|
382
|
+
ref,
|
|
383
|
+
options.repo,
|
|
384
|
+
options.fallbackSpaceUid
|
|
385
|
+
);
|
|
386
|
+
const availability2 = await this.availability(repository2);
|
|
387
|
+
if (!availability2.available) {
|
|
388
|
+
return {
|
|
389
|
+
ref,
|
|
390
|
+
repository: repository2,
|
|
391
|
+
availability: availability2,
|
|
392
|
+
candidates: [],
|
|
393
|
+
error: availability2.reason
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
...await this.resolveOne(ref, repository2),
|
|
398
|
+
repository: repository2,
|
|
399
|
+
availability: availability2
|
|
400
|
+
};
|
|
401
|
+
})
|
|
402
|
+
);
|
|
403
|
+
const repositories = [
|
|
404
|
+
...new Map(
|
|
405
|
+
results.flatMap(
|
|
406
|
+
(result) => result.repository ? [[result.repository.repoRoot, result.repository]] : []
|
|
407
|
+
)
|
|
408
|
+
).values()
|
|
409
|
+
];
|
|
410
|
+
const repository = repositories[0] ?? await this.resolveRepository(options.repo, options.fallbackSpaceUid);
|
|
411
|
+
if (repositories.length === 0) repositories.push(repository);
|
|
412
|
+
const availabilities = results.flatMap(
|
|
413
|
+
(result) => result.availability ? [result.availability] : []
|
|
414
|
+
);
|
|
415
|
+
const availability = availabilities.length === 0 ? await this.availability(repository) : {
|
|
416
|
+
available: availabilities.every((item) => item.available),
|
|
417
|
+
reason: availabilities.find((item) => !item.available)?.reason,
|
|
418
|
+
stale: availabilities.some((item) => item.stale),
|
|
419
|
+
caveats: [
|
|
420
|
+
...new Set(availabilities.flatMap((item) => item.caveats))
|
|
421
|
+
]
|
|
422
|
+
};
|
|
423
|
+
return { repository, repositories, availability, results };
|
|
424
|
+
}
|
|
425
|
+
async repositoryForRef(ref, defaultRepo, fallbackSpaceUid) {
|
|
426
|
+
if (ref.repo || defaultRepo) {
|
|
427
|
+
return this.resolveRepository(ref.repo || defaultRepo, fallbackSpaceUid);
|
|
428
|
+
}
|
|
429
|
+
if (ref.path) {
|
|
430
|
+
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);
|
|
431
|
+
const absolute = await (0, import_promises.realpath)(requested).catch(() => requested);
|
|
432
|
+
const configured = await this.resolveRepositories(fallbackSpaceUid);
|
|
433
|
+
const defining = [...configured].sort((a, b) => b.repoRoot.length - a.repoRoot.length).find((candidate) => pathContains(candidate.repoRoot, absolute));
|
|
434
|
+
if (defining) {
|
|
435
|
+
return this.env.MINDGRAPH_REPO_ID && pathContains(defining.repoRoot, this.cwd) ? this.resolveRepositoryIdentity(
|
|
436
|
+
defining.repoRoot,
|
|
437
|
+
this.env.MINDGRAPH_REPO_ID,
|
|
438
|
+
defining.spaceUid
|
|
439
|
+
) : defining;
|
|
440
|
+
}
|
|
441
|
+
const gitRoot = await this.gitRoot(absolute);
|
|
442
|
+
if (gitRoot) {
|
|
443
|
+
return this.resolveRepositoryIdentity(
|
|
444
|
+
gitRoot,
|
|
445
|
+
this.env.MINDGRAPH_REPO_ID,
|
|
446
|
+
this.env.MINDGRAPH_CODE_SPACE_UID || fallbackSpaceUid
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return this.resolveRepository(void 0, fallbackSpaceUid);
|
|
451
|
+
}
|
|
452
|
+
async expand(resolved, limit = 20) {
|
|
453
|
+
const repository = {
|
|
454
|
+
repoId: resolved.repoId,
|
|
455
|
+
repoRoot: resolved.repoRoot
|
|
456
|
+
};
|
|
457
|
+
const availability = await this.availability(repository);
|
|
458
|
+
if (!availability.available) {
|
|
459
|
+
return { availability, callers: [], callees: [], truncated: false };
|
|
460
|
+
}
|
|
461
|
+
const cap = Math.min(Math.max(limit, 1), 20);
|
|
462
|
+
const [callers, callees] = await Promise.all([
|
|
463
|
+
this.related("callers", resolved, cap + 1),
|
|
464
|
+
this.related("callees", resolved, cap + 1)
|
|
465
|
+
]);
|
|
466
|
+
return {
|
|
467
|
+
availability,
|
|
468
|
+
callers: callers.slice(0, cap),
|
|
469
|
+
callees: callees.slice(0, cap),
|
|
470
|
+
truncated: callers.length > cap || callees.length > cap
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
async affected(resolved, limit = 50) {
|
|
474
|
+
const repository = {
|
|
475
|
+
repoId: resolved.repoId,
|
|
476
|
+
repoRoot: resolved.repoRoot
|
|
477
|
+
};
|
|
478
|
+
const availability = await this.availability(repository);
|
|
479
|
+
if (!availability.available) {
|
|
480
|
+
return { availability, affected: [], truncated: false };
|
|
481
|
+
}
|
|
482
|
+
const cap = Math.min(Math.max(limit, 1), 50);
|
|
483
|
+
const raw = await this.runJson([
|
|
484
|
+
"impact",
|
|
485
|
+
"-p",
|
|
486
|
+
resolved.repoRoot,
|
|
487
|
+
"-j",
|
|
488
|
+
"-d",
|
|
489
|
+
"2",
|
|
490
|
+
resolved.qualifiedName
|
|
491
|
+
]);
|
|
492
|
+
const refs = (raw.affected ?? []).map((node) => ({
|
|
493
|
+
path: node.filePath,
|
|
494
|
+
line: node.startLine,
|
|
495
|
+
symbol: node.qualifiedName ?? node.name,
|
|
496
|
+
kind: node.kind
|
|
497
|
+
}));
|
|
498
|
+
const resolvedRefs = await this.resolveRelatedRefs(refs.slice(0, cap + 1), repository);
|
|
499
|
+
return {
|
|
500
|
+
availability,
|
|
501
|
+
affected: resolvedRefs.slice(0, cap),
|
|
502
|
+
truncated: refs.length > cap
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
configuredRepositories() {
|
|
506
|
+
const configured = [];
|
|
507
|
+
if (this.workspaceFile && (0, import_node_fs.existsSync)(this.workspaceFile)) {
|
|
508
|
+
try {
|
|
509
|
+
const parsed = JSON.parse(
|
|
510
|
+
(0, import_node_fs.readFileSync)(this.workspaceFile, "utf8")
|
|
511
|
+
);
|
|
512
|
+
if (parsed.v === 1 && Array.isArray(parsed.repositories)) {
|
|
513
|
+
const configDirectory = (0, import_node_path.dirname)(this.workspaceFile);
|
|
514
|
+
const base = (0, import_node_path.basename)(configDirectory) === ".mindgraph" ? (0, import_node_path.dirname)(configDirectory) : configDirectory;
|
|
515
|
+
for (const repository of parsed.repositories) {
|
|
516
|
+
if (!repository || typeof repository.root !== "string") continue;
|
|
517
|
+
configured.push({
|
|
518
|
+
repoId: typeof repository.repo_id === "string" ? repository.repo_id : void 0,
|
|
519
|
+
repoRoot: (0, import_node_path.resolve)(base, repository.root),
|
|
520
|
+
spaceUid: typeof repository.space_uid === "string" ? repository.space_uid : void 0
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
} catch {
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
const raw = this.env.MINDGRAPH_CODE_REPOS;
|
|
528
|
+
if (raw) {
|
|
529
|
+
try {
|
|
530
|
+
const parsed = JSON.parse(raw);
|
|
531
|
+
for (const [repoId, config] of Object.entries(parsed)) {
|
|
532
|
+
configured.push({
|
|
533
|
+
repoId,
|
|
534
|
+
repoRoot: (0, import_node_path.resolve)(
|
|
535
|
+
this.cwd,
|
|
536
|
+
typeof config === "string" ? config : config.root
|
|
537
|
+
),
|
|
538
|
+
spaceUid: typeof config === "string" ? void 0 : config.space_uid
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
} catch {
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return [
|
|
545
|
+
...new Map(
|
|
546
|
+
configured.map((repository) => [repository.repoRoot, repository])
|
|
547
|
+
).values()
|
|
548
|
+
];
|
|
549
|
+
}
|
|
550
|
+
async resolveOne(ref, repository) {
|
|
551
|
+
const search = ref.symbol || ref.path;
|
|
552
|
+
if (!search || ref.line !== void 0 && (!Number.isInteger(ref.line) || ref.line < 1)) {
|
|
553
|
+
return { ref, candidates: [], error: "invalid_ref" };
|
|
554
|
+
}
|
|
555
|
+
try {
|
|
556
|
+
const args = ["query", "-p", repository.repoRoot, "-j", "-l", "50"];
|
|
557
|
+
if (ref.kind) args.push("-k", ref.kind);
|
|
558
|
+
args.push(search);
|
|
559
|
+
const hits = await this.runJson(args);
|
|
560
|
+
let candidates = hits.map(asNode).map(toCandidate).filter((item) => item !== null);
|
|
561
|
+
if (ref.path) {
|
|
562
|
+
const absolutePath = (0, import_node_path.isAbsolute)(ref.path) ? await (0, import_promises.realpath)(ref.path).catch(() => ref.path) : void 0;
|
|
563
|
+
const wanted = normalizePath(
|
|
564
|
+
absolutePath ? (0, import_node_path.relative)(repository.repoRoot, absolutePath) : ref.path
|
|
565
|
+
);
|
|
566
|
+
candidates = candidates.filter((candidate) => candidate.path === wanted);
|
|
567
|
+
}
|
|
568
|
+
if (ref.language) {
|
|
569
|
+
candidates = candidates.filter(
|
|
570
|
+
(candidate) => candidate.language.toLowerCase() === ref.language?.toLowerCase()
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
if (ref.kind) {
|
|
574
|
+
candidates = candidates.filter(
|
|
575
|
+
(candidate) => candidate.kind.toLowerCase() === ref.kind?.toLowerCase()
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
if (ref.symbol) {
|
|
579
|
+
const exact = candidates.filter((candidate) => {
|
|
580
|
+
const tail = candidate.qualifiedName.split(/::|\.|#/).at(-1);
|
|
581
|
+
return candidate.qualifiedName === ref.symbol || tail === ref.symbol;
|
|
582
|
+
});
|
|
583
|
+
if (exact.length > 0) candidates = exact;
|
|
584
|
+
}
|
|
585
|
+
if (ref.signature) {
|
|
586
|
+
const signature = normalizeSignature(ref.signature);
|
|
587
|
+
candidates = candidates.filter(
|
|
588
|
+
(candidate) => candidate.signature !== null && normalizeSignature(candidate.signature) === signature
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
if (ref.line !== void 0) {
|
|
592
|
+
candidates = candidates.filter(
|
|
593
|
+
(candidate) => candidate.startLine <= ref.line && candidate.endLine >= ref.line
|
|
594
|
+
);
|
|
595
|
+
if (candidates.length > 1) {
|
|
596
|
+
const narrowest = Math.min(
|
|
597
|
+
...candidates.map((candidate) => candidate.endLine - candidate.startLine)
|
|
598
|
+
);
|
|
599
|
+
const narrowed = candidates.filter(
|
|
600
|
+
(candidate) => candidate.endLine - candidate.startLine === narrowest
|
|
601
|
+
);
|
|
602
|
+
if (narrowed.length === 1) candidates = narrowed;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
if (candidates.length !== 1) {
|
|
606
|
+
return {
|
|
607
|
+
ref,
|
|
608
|
+
candidates,
|
|
609
|
+
error: candidates.length === 0 ? "not_found" : "ambiguous"
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
return {
|
|
613
|
+
ref,
|
|
614
|
+
candidates,
|
|
615
|
+
resolved: {
|
|
616
|
+
repoId: repository.repoId,
|
|
617
|
+
repoRoot: repository.repoRoot,
|
|
618
|
+
...candidates[0]
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
} catch (cause) {
|
|
622
|
+
const failure = cause;
|
|
623
|
+
return {
|
|
624
|
+
ref,
|
|
625
|
+
candidates: [],
|
|
626
|
+
error: failure.kind ?? "command_failed"
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
async related(action, resolved, limit) {
|
|
631
|
+
const raw = await this.runJson([
|
|
632
|
+
action,
|
|
633
|
+
"-p",
|
|
634
|
+
resolved.repoRoot,
|
|
635
|
+
"-j",
|
|
636
|
+
"-l",
|
|
637
|
+
String(limit),
|
|
638
|
+
resolved.qualifiedName
|
|
639
|
+
]);
|
|
640
|
+
const refs = (raw[action] ?? []).map((node) => ({
|
|
641
|
+
path: node.filePath,
|
|
642
|
+
line: node.startLine,
|
|
643
|
+
symbol: node.qualifiedName ?? node.name,
|
|
644
|
+
kind: node.kind
|
|
645
|
+
}));
|
|
646
|
+
return this.resolveRelatedRefs(refs, {
|
|
647
|
+
repoId: resolved.repoId,
|
|
648
|
+
repoRoot: resolved.repoRoot
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
async resolveRelatedRefs(refs, repository) {
|
|
652
|
+
const results = await Promise.all(refs.map((ref) => this.resolveOne(ref, repository)));
|
|
653
|
+
const seen = /* @__PURE__ */ new Set();
|
|
654
|
+
return results.flatMap((result) => result.resolved ? [result.resolved] : []).filter((result) => {
|
|
655
|
+
const key = `${result.path}:${result.qualifiedName}:${result.startLine}`;
|
|
656
|
+
if (seen.has(key)) return false;
|
|
657
|
+
seen.add(key);
|
|
658
|
+
return true;
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
async runJson(args) {
|
|
662
|
+
const candidates = [
|
|
663
|
+
this.env.MINDGRAPH_CODEGRAPH_BIN,
|
|
664
|
+
"codegraph",
|
|
665
|
+
(0, import_node_path.resolve)((0, import_node_os.homedir)(), ".local", "bin", "codegraph")
|
|
666
|
+
].filter((candidate, index, all) => {
|
|
667
|
+
return Boolean(candidate) && all.indexOf(candidate) === index;
|
|
668
|
+
});
|
|
669
|
+
let lastFailure;
|
|
670
|
+
for (const executable of candidates) {
|
|
671
|
+
try {
|
|
672
|
+
const stdout = await runProcess(
|
|
673
|
+
executable,
|
|
674
|
+
args,
|
|
675
|
+
this.timeoutMs,
|
|
676
|
+
this.env,
|
|
677
|
+
this.cwd
|
|
678
|
+
);
|
|
679
|
+
return JSON.parse(stdout);
|
|
680
|
+
} catch (cause) {
|
|
681
|
+
const failure = cause;
|
|
682
|
+
lastFailure = failure;
|
|
683
|
+
if (failure.kind !== "absent") throw failure;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
throw lastFailure ?? Object.assign(new Error("codegraph executable not found"), {
|
|
687
|
+
kind: "absent"
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
function repositoryIdentityKey(repository) {
|
|
692
|
+
return { v: 1, kind: "repository", repo_id: repository.repoId };
|
|
693
|
+
}
|
|
694
|
+
function codeRefIdentityKey(ref) {
|
|
695
|
+
if (ref.kind === "file") {
|
|
696
|
+
return {
|
|
697
|
+
v: 1,
|
|
698
|
+
kind: "file",
|
|
699
|
+
repo_id: ref.repoId,
|
|
700
|
+
path: ref.path
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
return {
|
|
704
|
+
v: 1,
|
|
705
|
+
kind: "symbol",
|
|
706
|
+
repo_id: ref.repoId,
|
|
707
|
+
language: ref.language,
|
|
708
|
+
symbol_kind: ref.kind,
|
|
709
|
+
qualified_name: ref.qualifiedName,
|
|
710
|
+
disambiguator: {
|
|
711
|
+
signature: ref.signature ?? "",
|
|
712
|
+
path: ref.path,
|
|
713
|
+
start_line: ref.startLine,
|
|
714
|
+
end_line: ref.endLine
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// src/code-tool.ts
|
|
720
|
+
var ok = (data) => ({
|
|
721
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
722
|
+
});
|
|
723
|
+
var err = (code, message, details) => ({
|
|
724
|
+
content: [
|
|
725
|
+
{
|
|
726
|
+
type: "text",
|
|
727
|
+
text: JSON.stringify({ error: message, code, details }, null, 2)
|
|
728
|
+
}
|
|
729
|
+
],
|
|
730
|
+
isError: true
|
|
731
|
+
});
|
|
732
|
+
var CODE_TOOL = {
|
|
733
|
+
name: "mindgraph_code",
|
|
734
|
+
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.",
|
|
735
|
+
inputSchema: {
|
|
736
|
+
type: "object",
|
|
737
|
+
properties: {
|
|
738
|
+
action: {
|
|
739
|
+
type: "string",
|
|
740
|
+
enum: ["anchor", "recall", "expand", "affected"]
|
|
741
|
+
},
|
|
742
|
+
code_refs: {
|
|
743
|
+
type: "array",
|
|
744
|
+
maxItems: 50,
|
|
745
|
+
items: {
|
|
746
|
+
oneOf: [
|
|
747
|
+
{ type: "string" },
|
|
748
|
+
{
|
|
749
|
+
type: "object",
|
|
750
|
+
properties: {
|
|
751
|
+
repo: { type: "string" },
|
|
752
|
+
path: { type: "string" },
|
|
753
|
+
line: { type: "integer", minimum: 1 },
|
|
754
|
+
symbol: { type: "string" },
|
|
755
|
+
kind: { type: "string" },
|
|
756
|
+
language: { type: "string" },
|
|
757
|
+
signature: { type: "string" }
|
|
758
|
+
},
|
|
759
|
+
additionalProperties: false
|
|
760
|
+
}
|
|
761
|
+
]
|
|
762
|
+
}
|
|
763
|
+
},
|
|
764
|
+
anchor_uids: {
|
|
765
|
+
type: "array",
|
|
766
|
+
maxItems: 50,
|
|
767
|
+
items: { type: "string" },
|
|
768
|
+
description: "Already-materialized code anchor UIDs (codegraph is not required for recall)"
|
|
769
|
+
},
|
|
770
|
+
repo: {
|
|
771
|
+
type: "string",
|
|
772
|
+
description: "Configured repository id or repository root"
|
|
773
|
+
},
|
|
774
|
+
repo_space_uid: {
|
|
775
|
+
type: "string",
|
|
776
|
+
description: "Writable shared Project/repository Space for new anchors"
|
|
777
|
+
},
|
|
778
|
+
task_uid: { type: "string" },
|
|
779
|
+
execution_uid: { type: "string" },
|
|
780
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
781
|
+
agent_id: { type: "string" }
|
|
782
|
+
},
|
|
783
|
+
required: ["action"]
|
|
784
|
+
},
|
|
785
|
+
annotations: {
|
|
786
|
+
readOnlyHint: false,
|
|
787
|
+
destructiveHint: false,
|
|
788
|
+
idempotentHint: true,
|
|
789
|
+
openWorldHint: true
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
async function handleCodeTool(client2, args, adapter = new CodegraphAdapter()) {
|
|
793
|
+
const codeClient = client2;
|
|
794
|
+
const action = args.action;
|
|
795
|
+
const refs = Array.isArray(args.code_refs) ? args.code_refs : [];
|
|
796
|
+
const anchorUids = Array.isArray(args.anchor_uids) ? args.anchor_uids : [];
|
|
797
|
+
const agentId = typeof args.agent_id === "string" ? args.agent_id : "mcp";
|
|
798
|
+
const fallbackSpaceUid = typeof args.repo_space_uid === "string" ? args.repo_space_uid : `space:agent:${agentId}`;
|
|
799
|
+
const options = {
|
|
800
|
+
repo: typeof args.repo === "string" ? args.repo : void 0,
|
|
801
|
+
fallbackSpaceUid
|
|
802
|
+
};
|
|
803
|
+
const limit = typeof args.limit === "number" ? args.limit : void 0;
|
|
804
|
+
try {
|
|
805
|
+
switch (action) {
|
|
806
|
+
case "anchor": {
|
|
807
|
+
if (refs.length === 0) return err("missing_code_refs", "code_refs is required for anchor");
|
|
808
|
+
const resolution = await adapter.resolveRefs(refs, options);
|
|
809
|
+
const resolved = resolution.results.flatMap(
|
|
810
|
+
(result) => result.resolved && result.repository ? [{ ref: result.resolved, repository: result.repository }] : []
|
|
811
|
+
);
|
|
812
|
+
const unresolved = resolution.results.filter((result) => !result.resolved);
|
|
813
|
+
if (resolved.length === 0) {
|
|
814
|
+
return ok({ ...resolution, anchors: [], wrote: false });
|
|
815
|
+
}
|
|
816
|
+
const missingSpace = resolved.find(
|
|
817
|
+
({ repository }) => typeof args.repo_space_uid !== "string" && !repository.spaceUid
|
|
818
|
+
);
|
|
819
|
+
if (missingSpace) {
|
|
820
|
+
return err(
|
|
821
|
+
"missing_repository_space",
|
|
822
|
+
`repo_space_uid, a workspace space_uid, or MINDGRAPH_CODE_SPACE_UID is required for ${missingSpace.repository.repoId}`
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
const repositoryAnchors = /* @__PURE__ */ new Map();
|
|
826
|
+
for (const { repository } of resolved) {
|
|
827
|
+
if (repositoryAnchors.has(repository.repoRoot)) continue;
|
|
828
|
+
const spaceUid = typeof args.repo_space_uid === "string" ? args.repo_space_uid : repository.spaceUid;
|
|
829
|
+
const anchored = await anchorRepository(
|
|
830
|
+
codeClient,
|
|
831
|
+
repository,
|
|
832
|
+
spaceUid,
|
|
833
|
+
agentId
|
|
834
|
+
);
|
|
835
|
+
repositoryAnchors.set(repository.repoRoot, {
|
|
836
|
+
...anchored,
|
|
837
|
+
repository
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
const anchors = [];
|
|
841
|
+
for (const { ref, repository } of resolved) {
|
|
842
|
+
const repositoryAnchor = repositoryAnchors.get(repository.repoRoot);
|
|
843
|
+
if (!repositoryAnchor.uid) continue;
|
|
844
|
+
const spaceUid = typeof args.repo_space_uid === "string" ? args.repo_space_uid : repository.spaceUid;
|
|
845
|
+
const anchor = await anchorCodeRef(
|
|
846
|
+
codeClient,
|
|
847
|
+
ref,
|
|
848
|
+
repositoryAnchor.uid,
|
|
849
|
+
spaceUid,
|
|
850
|
+
agentId
|
|
851
|
+
);
|
|
852
|
+
anchors.push(anchor);
|
|
853
|
+
}
|
|
854
|
+
return ok({
|
|
855
|
+
availability: resolution.availability,
|
|
856
|
+
repository: repositoryAnchors.values().next().value,
|
|
857
|
+
repositories: [...repositoryAnchors.values()],
|
|
858
|
+
anchors,
|
|
859
|
+
unresolved,
|
|
860
|
+
wrote: anchors.some((anchor) => anchor.created)
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
case "recall": {
|
|
864
|
+
const materialized = new Set(anchorUids);
|
|
865
|
+
let resolution;
|
|
866
|
+
if (refs.length > 0) {
|
|
867
|
+
resolution = await adapter.resolveRefs(refs, options);
|
|
868
|
+
for (const result of resolution.results) {
|
|
869
|
+
if (!result.resolved) continue;
|
|
870
|
+
const uid = await resolveMaterializedUid(codeClient, result.resolved, agentId);
|
|
871
|
+
if (uid) materialized.add(uid);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
if (materialized.size === 0) {
|
|
875
|
+
return ok({
|
|
876
|
+
availability: resolution?.availability,
|
|
877
|
+
anchors: [],
|
|
878
|
+
context: [],
|
|
879
|
+
unresolved: resolution?.results.filter((result) => !result.resolved) ?? []
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
const context = await Promise.all(
|
|
883
|
+
[...materialized].map(async (uid) => ({
|
|
884
|
+
anchor_uid: uid,
|
|
885
|
+
graph: await codeClient.traverse({
|
|
886
|
+
action: "neighborhood",
|
|
887
|
+
start_uid: uid,
|
|
888
|
+
depth: 2,
|
|
889
|
+
max_nodes: Math.min(limit ?? 20, 20)
|
|
890
|
+
})
|
|
891
|
+
}))
|
|
892
|
+
);
|
|
893
|
+
return ok({
|
|
894
|
+
availability: resolution?.availability,
|
|
895
|
+
anchors: [...materialized],
|
|
896
|
+
context,
|
|
897
|
+
unresolved: resolution?.results.filter((result) => !result.resolved) ?? []
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
case "expand": {
|
|
901
|
+
const target = await resolveSingleTarget(
|
|
902
|
+
codeClient,
|
|
903
|
+
adapter,
|
|
904
|
+
refs,
|
|
905
|
+
anchorUids,
|
|
906
|
+
options
|
|
907
|
+
);
|
|
908
|
+
if ("error" in target) return target.error;
|
|
909
|
+
const expanded = await adapter.expand(target.ref, limit);
|
|
910
|
+
const joined = await joinMaterialized(
|
|
911
|
+
codeClient,
|
|
912
|
+
[...expanded.callers, ...expanded.callees],
|
|
913
|
+
agentId,
|
|
914
|
+
Math.min(limit ?? 20, 20)
|
|
915
|
+
);
|
|
916
|
+
return ok({ anchor: target.ref, ...expanded, materialized: joined });
|
|
917
|
+
}
|
|
918
|
+
case "affected": {
|
|
919
|
+
const targetUid = typeof args.task_uid === "string" ? args.task_uid : typeof args.execution_uid === "string" ? args.execution_uid : void 0;
|
|
920
|
+
let graphContext;
|
|
921
|
+
const graphRefs = [];
|
|
922
|
+
if (targetUid) {
|
|
923
|
+
graphContext = await codeClient.traverse({
|
|
924
|
+
action: "neighborhood",
|
|
925
|
+
start_uid: targetUid,
|
|
926
|
+
depth: 2,
|
|
927
|
+
max_nodes: 50
|
|
928
|
+
});
|
|
929
|
+
graphRefs.push(
|
|
930
|
+
...await codeRefsFromGraphContext(codeClient, adapter, graphContext)
|
|
931
|
+
);
|
|
932
|
+
}
|
|
933
|
+
if (refs.length > 0 || anchorUids.length > 0) {
|
|
934
|
+
const target = await resolveSingleTarget(
|
|
935
|
+
codeClient,
|
|
936
|
+
adapter,
|
|
937
|
+
refs,
|
|
938
|
+
anchorUids,
|
|
939
|
+
options
|
|
940
|
+
);
|
|
941
|
+
if ("error" in target) return target.error;
|
|
942
|
+
graphRefs.unshift(target.ref);
|
|
943
|
+
}
|
|
944
|
+
const unique = uniqueRefs(graphRefs);
|
|
945
|
+
if (unique.length === 0) {
|
|
946
|
+
return ok({
|
|
947
|
+
target_uid: targetUid,
|
|
948
|
+
graph_context: graphContext,
|
|
949
|
+
affected: [],
|
|
950
|
+
degradation: "no materialized code anchors were attached"
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
const impacts = await Promise.all(
|
|
954
|
+
unique.map(async (ref) => ({ anchor: ref, ...await adapter.affected(ref, limit) }))
|
|
955
|
+
);
|
|
956
|
+
const structural = uniqueRefs(impacts.flatMap((impact) => impact.affected));
|
|
957
|
+
const joined = await joinMaterialized(
|
|
958
|
+
codeClient,
|
|
959
|
+
structural,
|
|
960
|
+
agentId,
|
|
961
|
+
Math.min(limit ?? 50, 50)
|
|
962
|
+
);
|
|
963
|
+
return ok({
|
|
964
|
+
target_uid: targetUid,
|
|
965
|
+
graph_context: graphContext,
|
|
966
|
+
impacts,
|
|
967
|
+
materialized: joined
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
default:
|
|
971
|
+
return err("unknown_action", `unknown mindgraph_code action: ${action}`);
|
|
972
|
+
}
|
|
973
|
+
} catch (cause) {
|
|
974
|
+
return err("code_tool_failed", errorDetail(cause));
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
async function attachCodeRefsToToolResult(client2, result, args, adapter) {
|
|
978
|
+
if (result.isError || !Array.isArray(args.code_refs) || args.code_refs.length === 0) {
|
|
979
|
+
return result;
|
|
980
|
+
}
|
|
981
|
+
const original = JSON.parse(result.content[0]?.text ?? "null");
|
|
982
|
+
const targetUids = primaryUids(original);
|
|
983
|
+
if (targetUids.length === 0) {
|
|
984
|
+
return ok({
|
|
985
|
+
result: original,
|
|
986
|
+
code_anchors: {
|
|
987
|
+
wrote: false,
|
|
988
|
+
caveat: "the write response exposed no primary UID to attach"
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
const anchorResult = await handleCodeTool(
|
|
993
|
+
client2,
|
|
994
|
+
{
|
|
995
|
+
action: "anchor",
|
|
996
|
+
code_refs: args.code_refs,
|
|
997
|
+
repo: args.repo,
|
|
998
|
+
repo_space_uid: args.repo_space_uid,
|
|
999
|
+
agent_id: args.agent_id
|
|
1000
|
+
},
|
|
1001
|
+
adapter
|
|
1002
|
+
);
|
|
1003
|
+
const anchors = JSON.parse(anchorResult.content[0]?.text ?? "null");
|
|
1004
|
+
if (!anchorResult.isError) {
|
|
1005
|
+
const codeClient = client2;
|
|
1006
|
+
const anchorUids = (anchors.anchors ?? []).flatMap(
|
|
1007
|
+
(anchor) => anchor.uid ? [anchor.uid] : []
|
|
1008
|
+
);
|
|
1009
|
+
for (const targetUid of targetUids) {
|
|
1010
|
+
for (const anchorUid of anchorUids) {
|
|
1011
|
+
await codeClient.entity({
|
|
1012
|
+
action: "relate",
|
|
1013
|
+
source_uid: targetUid,
|
|
1014
|
+
target_uid: anchorUid,
|
|
1015
|
+
edge_type: args.action === "lesson" ? "ABOUT" : "RELEVANT_TO",
|
|
1016
|
+
agent_id: args.agent_id
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
return ok({ result: original, code_anchors: anchors });
|
|
1022
|
+
}
|
|
1023
|
+
async function anchorRepository(client2, repository, spaceUid, agentId) {
|
|
1024
|
+
return client2.entity({
|
|
1025
|
+
action: "create",
|
|
1026
|
+
label: repository.repoId,
|
|
1027
|
+
props: {
|
|
1028
|
+
entity_type: "repository",
|
|
1029
|
+
canonical_name: repository.repoId,
|
|
1030
|
+
identifiers: { repo_id: repository.repoId }
|
|
1031
|
+
},
|
|
1032
|
+
identity: {
|
|
1033
|
+
namespace: "external.code",
|
|
1034
|
+
key_version: 1,
|
|
1035
|
+
key: repositoryIdentityKey(repository)
|
|
1036
|
+
},
|
|
1037
|
+
identity_space_uid: spaceUid,
|
|
1038
|
+
agent_id: agentId
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
async function anchorCodeRef(client2, ref, repositoryUid, spaceUid, agentId) {
|
|
1042
|
+
const response = await client2.entity({
|
|
1043
|
+
action: "create",
|
|
1044
|
+
label: ref.qualifiedName,
|
|
1045
|
+
props: {
|
|
1046
|
+
entity_type: ref.kind === "file" ? "code_file" : "code_symbol",
|
|
1047
|
+
canonical_name: ref.qualifiedName,
|
|
1048
|
+
identifiers: {
|
|
1049
|
+
repo_id: ref.repoId,
|
|
1050
|
+
path: ref.path,
|
|
1051
|
+
qualified_name: ref.qualifiedName
|
|
1052
|
+
},
|
|
1053
|
+
attributes: { code_ref: storedCodeRef(ref) }
|
|
1054
|
+
},
|
|
1055
|
+
identity: {
|
|
1056
|
+
namespace: "external.code",
|
|
1057
|
+
key_version: 1,
|
|
1058
|
+
key: codeRefIdentityKey(ref)
|
|
1059
|
+
},
|
|
1060
|
+
identity_space_uid: spaceUid,
|
|
1061
|
+
agent_id: agentId
|
|
1062
|
+
});
|
|
1063
|
+
if (response.uid) {
|
|
1064
|
+
await client2.entity({
|
|
1065
|
+
action: "relate",
|
|
1066
|
+
source_uid: response.uid,
|
|
1067
|
+
target_uid: repositoryUid,
|
|
1068
|
+
edge_type: "PART_OF",
|
|
1069
|
+
agent_id: agentId
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
return { ...response, ref };
|
|
1073
|
+
}
|
|
1074
|
+
async function resolveMaterializedUid(client2, ref, agentId) {
|
|
1075
|
+
const response = await client2.entity({
|
|
1076
|
+
action: "resolve_identity",
|
|
1077
|
+
identity: {
|
|
1078
|
+
namespace: "external.code",
|
|
1079
|
+
key_version: 1,
|
|
1080
|
+
key: codeRefIdentityKey(ref)
|
|
1081
|
+
},
|
|
1082
|
+
agent_id: agentId
|
|
1083
|
+
});
|
|
1084
|
+
return response.status === "existing" ? response.uid : void 0;
|
|
1085
|
+
}
|
|
1086
|
+
async function codeRefFromAnchor(client2, adapter, uid) {
|
|
1087
|
+
const node = await client2.getNode(uid);
|
|
1088
|
+
const attributes = node.props?.attributes;
|
|
1089
|
+
if (!attributes || typeof attributes !== "object") return void 0;
|
|
1090
|
+
const codeRef = attributes.code_ref;
|
|
1091
|
+
if (!isStoredCodeRef(codeRef)) return void 0;
|
|
1092
|
+
const repository = await adapter.resolveRepository(codeRef.repoId);
|
|
1093
|
+
return { ...codeRef, repoRoot: repository.repoRoot };
|
|
1094
|
+
}
|
|
1095
|
+
async function resolveSingleTarget(client2, adapter, refs, anchorUids, options) {
|
|
1096
|
+
if (refs.length > 1 || anchorUids.length > 1) {
|
|
1097
|
+
return { error: err("ambiguous_target", "expand/affected accepts exactly one code target") };
|
|
1098
|
+
}
|
|
1099
|
+
if (anchorUids.length === 1) {
|
|
1100
|
+
const ref = await codeRefFromAnchor(client2, adapter, anchorUids[0]);
|
|
1101
|
+
return ref ? { ref } : { error: err("invalid_anchor", "anchor UID does not contain a typed code_ref") };
|
|
1102
|
+
}
|
|
1103
|
+
if (refs.length !== 1) {
|
|
1104
|
+
return { error: err("missing_code_ref", "one code_ref or anchor_uid is required") };
|
|
1105
|
+
}
|
|
1106
|
+
const resolution = await adapter.resolveRefs(refs, options);
|
|
1107
|
+
const result = resolution.results[0];
|
|
1108
|
+
if (!result.resolved) {
|
|
1109
|
+
return {
|
|
1110
|
+
error: ok({
|
|
1111
|
+
availability: resolution.availability,
|
|
1112
|
+
result,
|
|
1113
|
+
wrote: false
|
|
1114
|
+
})
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
return { ref: result.resolved };
|
|
1118
|
+
}
|
|
1119
|
+
async function joinMaterialized(client2, refs, agentId, limit) {
|
|
1120
|
+
const joined = [];
|
|
1121
|
+
for (const ref of uniqueRefs(refs).slice(0, limit)) {
|
|
1122
|
+
const uid = await resolveMaterializedUid(client2, ref, agentId);
|
|
1123
|
+
if (!uid) continue;
|
|
1124
|
+
joined.push({
|
|
1125
|
+
ref,
|
|
1126
|
+
anchor_uid: uid,
|
|
1127
|
+
graph: await client2.traverse({
|
|
1128
|
+
action: "neighborhood",
|
|
1129
|
+
start_uid: uid,
|
|
1130
|
+
depth: 2,
|
|
1131
|
+
max_nodes: 20
|
|
1132
|
+
})
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
return joined;
|
|
1136
|
+
}
|
|
1137
|
+
async function codeRefsFromGraphContext(client2, adapter, context) {
|
|
1138
|
+
const uids = /* @__PURE__ */ new Set();
|
|
1139
|
+
collectUids(context, uids);
|
|
1140
|
+
if (uids.size === 0) return [];
|
|
1141
|
+
const nodes = client2.getNodesBatch ? await client2.getNodesBatch([...uids]) : await Promise.all([...uids].map((uid) => client2.getNode(uid).catch(() => void 0)));
|
|
1142
|
+
const stored = nodes.flatMap((node) => {
|
|
1143
|
+
if (!node) return [];
|
|
1144
|
+
const attributes = node.props?.attributes;
|
|
1145
|
+
if (!attributes || typeof attributes !== "object") return [];
|
|
1146
|
+
const codeRef = attributes.code_ref;
|
|
1147
|
+
return isStoredCodeRef(codeRef) ? [codeRef] : [];
|
|
1148
|
+
});
|
|
1149
|
+
return Promise.all(
|
|
1150
|
+
stored.map(async (ref) => {
|
|
1151
|
+
const repository = await adapter.resolveRepository(ref.repoId);
|
|
1152
|
+
return { ...ref, repoRoot: repository.repoRoot };
|
|
1153
|
+
})
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
function collectUids(value, uids) {
|
|
1157
|
+
if (Array.isArray(value)) {
|
|
1158
|
+
value.forEach((item) => collectUids(item, uids));
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
if (!value || typeof value !== "object") return;
|
|
1162
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1163
|
+
if (typeof child === "string" && ["uid", "node_uid", "neighbor_uid", "from_uid", "to_uid"].includes(key)) {
|
|
1164
|
+
uids.add(child);
|
|
1165
|
+
} else {
|
|
1166
|
+
collectUids(child, uids);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
function isStoredCodeRef(value) {
|
|
1171
|
+
if (!value || typeof value !== "object") return false;
|
|
1172
|
+
const ref = value;
|
|
1173
|
+
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";
|
|
1174
|
+
}
|
|
1175
|
+
function storedCodeRef(ref) {
|
|
1176
|
+
const { repoRoot: _localOnly, ...stored } = ref;
|
|
1177
|
+
return stored;
|
|
1178
|
+
}
|
|
1179
|
+
function uniqueRefs(refs) {
|
|
1180
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1181
|
+
return refs.filter((ref) => {
|
|
1182
|
+
const key = `${ref.repoId}:${ref.path}:${ref.qualifiedName}:${ref.signature ?? ""}`;
|
|
1183
|
+
if (seen.has(key)) return false;
|
|
1184
|
+
seen.add(key);
|
|
1185
|
+
return true;
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
function primaryUids(value) {
|
|
1189
|
+
if (!value || typeof value !== "object") return [];
|
|
1190
|
+
const object = value;
|
|
1191
|
+
const keys = [
|
|
1192
|
+
"uid",
|
|
1193
|
+
"claim_uid",
|
|
1194
|
+
"goal_uid",
|
|
1195
|
+
"project_uid",
|
|
1196
|
+
"milestone_uid",
|
|
1197
|
+
"decision_uid",
|
|
1198
|
+
"task_uid",
|
|
1199
|
+
"plan_uid",
|
|
1200
|
+
"step_uid",
|
|
1201
|
+
"execution_uid",
|
|
1202
|
+
"lesson_uid"
|
|
1203
|
+
];
|
|
1204
|
+
return [
|
|
1205
|
+
...new Set(
|
|
1206
|
+
keys.flatMap((key) => typeof object[key] === "string" ? [object[key]] : [])
|
|
1207
|
+
)
|
|
1208
|
+
];
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
// src/sync-tool.ts
|
|
1212
|
+
var import_node_crypto2 = __toESM(require("crypto"));
|
|
1213
|
+
var import_node_fs2 = __toESM(require("fs"));
|
|
1214
|
+
var import_node_path2 = __toESM(require("path"));
|
|
1215
|
+
var ok2 = (data) => ({
|
|
1216
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1217
|
+
});
|
|
1218
|
+
var err2 = (code, message) => ({
|
|
1219
|
+
content: [{ type: "text", text: JSON.stringify({ code, error: message }) }],
|
|
1220
|
+
isError: true
|
|
1221
|
+
});
|
|
1222
|
+
var SYNC_TOOL = {
|
|
1223
|
+
name: "mindgraph_sync",
|
|
1224
|
+
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.",
|
|
1225
|
+
annotations: {
|
|
1226
|
+
readOnlyHint: false,
|
|
1227
|
+
destructiveHint: false,
|
|
1228
|
+
idempotentHint: true,
|
|
1229
|
+
openWorldHint: true
|
|
1230
|
+
},
|
|
1231
|
+
inputSchema: {
|
|
1232
|
+
type: "object",
|
|
1233
|
+
properties: {
|
|
1234
|
+
action: {
|
|
1235
|
+
type: "string",
|
|
1236
|
+
enum: ["scan", "begin", "record", "finalize", "status", "abandon"]
|
|
1237
|
+
},
|
|
1238
|
+
paths: { type: "array", items: { type: "string" } },
|
|
1239
|
+
workspace: {
|
|
1240
|
+
type: "boolean",
|
|
1241
|
+
description: "For scan only: fan out across repositories explicitly listed in .mindgraph/workspace.json"
|
|
1242
|
+
},
|
|
1243
|
+
repo: {
|
|
1244
|
+
type: "string",
|
|
1245
|
+
description: "Configured repository id, name, or root"
|
|
1246
|
+
},
|
|
1247
|
+
logical_path: { type: "string" },
|
|
1248
|
+
content: { type: "string" },
|
|
1249
|
+
content_hash: { type: "string" },
|
|
1250
|
+
source_uid: { type: "string" },
|
|
1251
|
+
execution_uid: { type: "string" },
|
|
1252
|
+
assertion_fingerprint: { type: "string" },
|
|
1253
|
+
planned_fingerprints: { type: "array", items: { type: "string" } },
|
|
1254
|
+
current_fingerprints: { type: "array", items: { type: "string" } },
|
|
1255
|
+
source_selector: { type: "object" },
|
|
1256
|
+
capture_type: {
|
|
1257
|
+
type: "string",
|
|
1258
|
+
enum: ["lesson", "journal", "claim", "decision", "risk"]
|
|
1259
|
+
},
|
|
1260
|
+
label: { type: "string" },
|
|
1261
|
+
summary: { type: "string" },
|
|
1262
|
+
props: { type: "object" },
|
|
1263
|
+
expected_execution_version: { type: "integer", minimum: 1 },
|
|
1264
|
+
baseline_version: { type: "integer", minimum: 1 },
|
|
1265
|
+
repo_space_uid: { type: "string" },
|
|
1266
|
+
repo_id: { type: "string" },
|
|
1267
|
+
provider: { type: "string", enum: ["claude_project", "explicit"] },
|
|
1268
|
+
file_present: { type: "boolean" },
|
|
1269
|
+
agent_id: { type: "string" },
|
|
1270
|
+
invocation_context: {
|
|
1271
|
+
type: "object",
|
|
1272
|
+
description: "Adapter-owned; hooks replace forged or absent values.",
|
|
1273
|
+
properties: {
|
|
1274
|
+
harness: { type: "string" },
|
|
1275
|
+
harnessSessionId: { type: "string" },
|
|
1276
|
+
harnessTurnId: { type: "string" },
|
|
1277
|
+
cwd: { type: "string" },
|
|
1278
|
+
repoId: { type: "string" },
|
|
1279
|
+
branch: { type: "string" },
|
|
1280
|
+
commit: { type: "string" },
|
|
1281
|
+
worktreeState: { type: "string" },
|
|
1282
|
+
model: { type: "string" },
|
|
1283
|
+
injectedBy: { type: "string" }
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
},
|
|
1287
|
+
required: ["action"]
|
|
1288
|
+
}
|
|
1289
|
+
};
|
|
1290
|
+
function safePath(root, candidate) {
|
|
1291
|
+
const absolute = import_node_path2.default.resolve(root, candidate);
|
|
1292
|
+
const relative2 = import_node_path2.default.relative(root, absolute);
|
|
1293
|
+
if (relative2.startsWith("..") || import_node_path2.default.isAbsolute(relative2)) {
|
|
1294
|
+
throw new Error(`path escapes repository root: ${candidate}`);
|
|
1295
|
+
}
|
|
1296
|
+
return absolute;
|
|
1297
|
+
}
|
|
1298
|
+
function markdownFiles(target) {
|
|
1299
|
+
if (!import_node_fs2.default.existsSync(target)) return [];
|
|
1300
|
+
const stat = import_node_fs2.default.statSync(target);
|
|
1301
|
+
if (stat.isFile()) return target.endsWith(".md") ? [target] : [];
|
|
1302
|
+
if (!stat.isDirectory()) return [];
|
|
1303
|
+
return import_node_fs2.default.readdirSync(target, { withFileTypes: true }).flatMap((entry) => markdownFiles(import_node_path2.default.join(target, entry.name)));
|
|
1304
|
+
}
|
|
1305
|
+
function fileDescriptor(root, absolute) {
|
|
1306
|
+
const content = import_node_fs2.default.readFileSync(absolute, "utf8");
|
|
1307
|
+
return {
|
|
1308
|
+
logical_path: import_node_path2.default.relative(root, absolute).split(import_node_path2.default.sep).join("/"),
|
|
1309
|
+
content_hash: import_node_crypto2.default.createHash("sha256").update(content).digest("hex"),
|
|
1310
|
+
bytes: Buffer.byteLength(content),
|
|
1311
|
+
content
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
async function handleSyncTool(client2, args) {
|
|
1315
|
+
try {
|
|
1316
|
+
const action = String(args.action || "");
|
|
1317
|
+
const context = args.invocation_context || {};
|
|
1318
|
+
const adapter = new CodegraphAdapter({
|
|
1319
|
+
cwd: context.cwd || process.cwd()
|
|
1320
|
+
});
|
|
1321
|
+
const requestedRepo = typeof args.repo === "string" ? args.repo : typeof args.repo_id === "string" ? args.repo_id : context.repoId;
|
|
1322
|
+
const repository = await adapter.resolveRepository(requestedRepo);
|
|
1323
|
+
const root = repository.repoRoot;
|
|
1324
|
+
const repoId = repository.repoId;
|
|
1325
|
+
const provider = String(args.provider || "claude_project");
|
|
1326
|
+
const remote = client2;
|
|
1327
|
+
if (action === "scan") {
|
|
1328
|
+
const repositories = args.workspace === true ? await adapter.resolveRepositories() : [repository];
|
|
1329
|
+
const requested = Array.isArray(args.paths) && args.paths.length > 0 ? args.paths.map(String) : [".claude/agent-memory", "memory"];
|
|
1330
|
+
const scans = [];
|
|
1331
|
+
for (const selected of repositories) {
|
|
1332
|
+
scans.push(
|
|
1333
|
+
await scanRepository(
|
|
1334
|
+
remote,
|
|
1335
|
+
selected,
|
|
1336
|
+
requested,
|
|
1337
|
+
provider,
|
|
1338
|
+
args.agent_id
|
|
1339
|
+
)
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
if (args.workspace === true) {
|
|
1343
|
+
return ok2({
|
|
1344
|
+
provider,
|
|
1345
|
+
workspace: true,
|
|
1346
|
+
repositories: scans,
|
|
1347
|
+
one_way: "files_to_graph"
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
return ok2(scans[0]);
|
|
1351
|
+
}
|
|
1352
|
+
const request = {
|
|
1353
|
+
...args,
|
|
1354
|
+
provider,
|
|
1355
|
+
repo_id: repoId
|
|
1356
|
+
};
|
|
1357
|
+
delete request.invocation_context;
|
|
1358
|
+
delete request.paths;
|
|
1359
|
+
delete request.repo;
|
|
1360
|
+
delete request.workspace;
|
|
1361
|
+
if (action === "begin" && !request.content) {
|
|
1362
|
+
if (!request.logical_path) {
|
|
1363
|
+
return err2("missing_field", "logical_path is required for begin");
|
|
1364
|
+
}
|
|
1365
|
+
const file = fileDescriptor(
|
|
1366
|
+
root,
|
|
1367
|
+
safePath(root, String(request.logical_path))
|
|
1368
|
+
);
|
|
1369
|
+
request.content = file.content;
|
|
1370
|
+
request.content_hash ||= file.content_hash;
|
|
1371
|
+
}
|
|
1372
|
+
if (action === "status" && request.logical_path && request.file_present !== false) {
|
|
1373
|
+
const absolute = safePath(root, String(request.logical_path));
|
|
1374
|
+
request.file_present = import_node_fs2.default.existsSync(absolute);
|
|
1375
|
+
if (request.file_present && !request.content_hash) {
|
|
1376
|
+
request.content_hash = fileDescriptor(root, absolute).content_hash;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
return ok2(await remote.memorySync(request));
|
|
1380
|
+
} catch (error) {
|
|
1381
|
+
return err2("sync_failed", errorDetail(error));
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
async function scanRepository(remote, repository, requested, provider, agentId) {
|
|
1385
|
+
const files = [
|
|
1386
|
+
...new Set(
|
|
1387
|
+
requested.flatMap(
|
|
1388
|
+
(candidate) => markdownFiles(safePath(repository.repoRoot, candidate))
|
|
1389
|
+
)
|
|
1390
|
+
)
|
|
1391
|
+
].sort();
|
|
1392
|
+
const results = [];
|
|
1393
|
+
for (const absolute of files) {
|
|
1394
|
+
const file = fileDescriptor(repository.repoRoot, absolute);
|
|
1395
|
+
const status = await remote.memorySync({
|
|
1396
|
+
action: "scan",
|
|
1397
|
+
provider,
|
|
1398
|
+
repo_id: repository.repoId,
|
|
1399
|
+
logical_path: file.logical_path,
|
|
1400
|
+
content_hash: file.content_hash,
|
|
1401
|
+
file_present: true,
|
|
1402
|
+
agent_id: agentId
|
|
1403
|
+
});
|
|
1404
|
+
results.push({
|
|
1405
|
+
logical_path: file.logical_path,
|
|
1406
|
+
content_hash: file.content_hash,
|
|
1407
|
+
bytes: file.bytes,
|
|
1408
|
+
status
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
return {
|
|
1412
|
+
repo_id: repository.repoId,
|
|
1413
|
+
root: ".",
|
|
1414
|
+
files: results
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
8
1417
|
|
|
9
1418
|
// src/tools.ts
|
|
10
1419
|
var TOOLS = [
|
|
11
1420
|
{
|
|
12
1421
|
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
|
|
1422
|
+
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
1423
|
inputSchema: {
|
|
15
1424
|
type: "object",
|
|
16
1425
|
properties: {
|
|
17
1426
|
action: {
|
|
18
1427
|
type: "string",
|
|
19
|
-
enum: [
|
|
1428
|
+
enum: [
|
|
1429
|
+
"entity",
|
|
1430
|
+
"observation",
|
|
1431
|
+
"source",
|
|
1432
|
+
"snippet",
|
|
1433
|
+
"concept",
|
|
1434
|
+
"journal",
|
|
1435
|
+
"lesson"
|
|
1436
|
+
],
|
|
20
1437
|
description: "Type of knowledge to capture"
|
|
21
1438
|
},
|
|
22
1439
|
label: {
|
|
@@ -31,6 +1448,31 @@ var TOOLS = [
|
|
|
31
1448
|
type: "string",
|
|
32
1449
|
description: "Journal content/body text (for action=journal)"
|
|
33
1450
|
},
|
|
1451
|
+
session_uid: {
|
|
1452
|
+
type: "string",
|
|
1453
|
+
description: "Session UID that produced a lesson (for action=lesson)"
|
|
1454
|
+
},
|
|
1455
|
+
work_uid: {
|
|
1456
|
+
type: "string",
|
|
1457
|
+
description: "Durable Task/work UID this capture is relevant to"
|
|
1458
|
+
},
|
|
1459
|
+
execution_uid: {
|
|
1460
|
+
type: "string",
|
|
1461
|
+
description: "Material Execution that produced the lesson"
|
|
1462
|
+
},
|
|
1463
|
+
idempotency_key: {
|
|
1464
|
+
type: "string",
|
|
1465
|
+
description: "Stable retry key for an intentional capture"
|
|
1466
|
+
},
|
|
1467
|
+
supersedes_uid: {
|
|
1468
|
+
type: "string",
|
|
1469
|
+
description: "Earlier capture corrected or superseded by this one"
|
|
1470
|
+
},
|
|
1471
|
+
summarizes_uids: {
|
|
1472
|
+
type: "array",
|
|
1473
|
+
items: { type: "string" },
|
|
1474
|
+
description: "Source node UIDs the lesson was learned from (for action=lesson)"
|
|
1475
|
+
},
|
|
34
1476
|
entity_type: {
|
|
35
1477
|
type: "string",
|
|
36
1478
|
enum: ["person", "organization", "place", "event", "nation", "concept"],
|
|
@@ -65,6 +1507,32 @@ var TOOLS = [
|
|
|
65
1507
|
type: "object",
|
|
66
1508
|
description: "Type-specific properties (e.g. birth_date for person, coordinates for place)"
|
|
67
1509
|
},
|
|
1510
|
+
code_refs: {
|
|
1511
|
+
type: "array",
|
|
1512
|
+
items: {
|
|
1513
|
+
oneOf: [
|
|
1514
|
+
{ type: "string" },
|
|
1515
|
+
{
|
|
1516
|
+
type: "object",
|
|
1517
|
+
properties: {
|
|
1518
|
+
repo: { type: "string" },
|
|
1519
|
+
path: { type: "string" },
|
|
1520
|
+
line: { type: "integer", minimum: 1 },
|
|
1521
|
+
symbol: { type: "string" },
|
|
1522
|
+
kind: { type: "string" },
|
|
1523
|
+
language: { type: "string" },
|
|
1524
|
+
signature: { type: "string" }
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
]
|
|
1528
|
+
},
|
|
1529
|
+
description: "Typed code targets to anchor and attach to the captured node"
|
|
1530
|
+
},
|
|
1531
|
+
repo: { type: "string", description: "Configured repository id or root" },
|
|
1532
|
+
repo_space_uid: {
|
|
1533
|
+
type: "string",
|
|
1534
|
+
description: "Writable shared repository/Project Space for new code anchors"
|
|
1535
|
+
},
|
|
68
1536
|
agent_id: {
|
|
69
1537
|
type: "string",
|
|
70
1538
|
description: "Agent identity"
|
|
@@ -238,6 +1706,16 @@ var TOOLS = [
|
|
|
238
1706
|
type: "object",
|
|
239
1707
|
description: "Type-specific properties (e.g. status, priority, deadline, horizon for goals)"
|
|
240
1708
|
},
|
|
1709
|
+
code_refs: {
|
|
1710
|
+
type: "array",
|
|
1711
|
+
items: { oneOf: [{ type: "string" }, { type: "object" }] },
|
|
1712
|
+
description: "Typed code targets to anchor and attach to the created commitment"
|
|
1713
|
+
},
|
|
1714
|
+
repo: { type: "string", description: "Configured repository id or root" },
|
|
1715
|
+
repo_space_uid: {
|
|
1716
|
+
type: "string",
|
|
1717
|
+
description: "Writable shared repository/Project Space for new code anchors"
|
|
1718
|
+
},
|
|
241
1719
|
agent_id: {
|
|
242
1720
|
type: "string",
|
|
243
1721
|
description: "Agent identity"
|
|
@@ -248,7 +1726,7 @@ var TOOLS = [
|
|
|
248
1726
|
},
|
|
249
1727
|
{
|
|
250
1728
|
name: "mindgraph_plan",
|
|
251
|
-
description: "
|
|
1729
|
+
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
1730
|
inputSchema: {
|
|
253
1731
|
type: "object",
|
|
254
1732
|
properties: {
|
|
@@ -260,9 +1738,19 @@ var TOOLS = [
|
|
|
260
1738
|
"add_step",
|
|
261
1739
|
"update_status",
|
|
262
1740
|
"get_plan",
|
|
1741
|
+
"resume_work",
|
|
1742
|
+
"claim_task",
|
|
1743
|
+
"heartbeat",
|
|
1744
|
+
"start_iteration",
|
|
1745
|
+
"checkpoint_iteration",
|
|
1746
|
+
"block_task",
|
|
1747
|
+
"complete_task",
|
|
1748
|
+
"abandon_iteration",
|
|
263
1749
|
"start_execution",
|
|
264
1750
|
"complete_execution",
|
|
265
1751
|
"fail_execution",
|
|
1752
|
+
"register_agent",
|
|
1753
|
+
"get_executions",
|
|
266
1754
|
// C33: governance WRITES are deliberately not exposed here.
|
|
267
1755
|
// `create_policy` let a model author the rules it is judged by;
|
|
268
1756
|
// `request_approval` + `resolve_approval` let it raise and then
|
|
@@ -290,30 +1778,189 @@ var TOOLS = [
|
|
|
290
1778
|
},
|
|
291
1779
|
plan_uid: {
|
|
292
1780
|
type: "string",
|
|
293
|
-
description: "Plan UID (for add_step, get_plan)"
|
|
1781
|
+
description: "Plan UID (for create_plan execution link, add_step, get_plan)"
|
|
294
1782
|
},
|
|
295
1783
|
task_uid: {
|
|
296
1784
|
type: "string",
|
|
297
|
-
description: "Task
|
|
1785
|
+
description: "Task UID (for create_plan and graph-managed work actions)"
|
|
1786
|
+
},
|
|
1787
|
+
step_uid: {
|
|
1788
|
+
type: "string",
|
|
1789
|
+
description: "PlanStep UID for an iteration/checkpoint"
|
|
1790
|
+
},
|
|
1791
|
+
session_uid: {
|
|
1792
|
+
type: "string",
|
|
1793
|
+
description: "Durable Session UID that owns the task lease"
|
|
1794
|
+
},
|
|
1795
|
+
goal_uid: {
|
|
1796
|
+
type: "string",
|
|
1797
|
+
description: "Goal UID targeted by a task or plan"
|
|
298
1798
|
},
|
|
299
1799
|
target_uid: {
|
|
300
1800
|
type: "string",
|
|
301
|
-
description: "
|
|
1801
|
+
description: "Task/plan/step UID for update_status, or parent/target node for procedure and risk actions"
|
|
1802
|
+
},
|
|
1803
|
+
execution_uid: {
|
|
1804
|
+
type: "string",
|
|
1805
|
+
description: "Execution UID (required for complete_execution/fail_execution)"
|
|
1806
|
+
},
|
|
1807
|
+
executor_uid: {
|
|
1808
|
+
type: "string",
|
|
1809
|
+
description: "Agent node UID that executed the work"
|
|
1810
|
+
},
|
|
1811
|
+
affordance_uid: {
|
|
1812
|
+
type: "string",
|
|
1813
|
+
description: "Affordance invoked by start_execution"
|
|
1814
|
+
},
|
|
1815
|
+
produces_node_uid: {
|
|
1816
|
+
type: "string",
|
|
1817
|
+
description: "Node produced by a completed execution"
|
|
1818
|
+
},
|
|
1819
|
+
filter_plan_uid: {
|
|
1820
|
+
type: "string",
|
|
1821
|
+
description: "Restrict get_executions to one plan"
|
|
1822
|
+
},
|
|
1823
|
+
depends_on_uids: {
|
|
1824
|
+
type: "array",
|
|
1825
|
+
items: { type: "string" },
|
|
1826
|
+
description: "Plan-step UIDs that an added step depends on"
|
|
1827
|
+
},
|
|
1828
|
+
related_uids: {
|
|
1829
|
+
type: "array",
|
|
1830
|
+
items: { type: "string" },
|
|
1831
|
+
description: "Target node UIDs to link to created work or an execution"
|
|
302
1832
|
},
|
|
303
1833
|
status: {
|
|
304
1834
|
type: "string",
|
|
305
1835
|
description: "New status value (for update_status)"
|
|
306
1836
|
},
|
|
1837
|
+
task_status: {
|
|
1838
|
+
type: "string",
|
|
1839
|
+
description: "Task status requested by a checkpoint or abandon action"
|
|
1840
|
+
},
|
|
1841
|
+
step_status: {
|
|
1842
|
+
type: "string",
|
|
1843
|
+
description: "PlanStep status requested by a checkpoint"
|
|
1844
|
+
},
|
|
1845
|
+
execution_status: {
|
|
1846
|
+
type: "string",
|
|
1847
|
+
enum: ["completed", "failed", "abandoned"],
|
|
1848
|
+
description: "Terminal Execution status for checkpoint_iteration"
|
|
1849
|
+
},
|
|
1850
|
+
expected_version: {
|
|
1851
|
+
type: "number",
|
|
1852
|
+
description: "Current Task node version used as the optimistic fence"
|
|
1853
|
+
},
|
|
1854
|
+
lease_epoch: {
|
|
1855
|
+
type: "number",
|
|
1856
|
+
description: "Current monotonic task-lease epoch"
|
|
1857
|
+
},
|
|
1858
|
+
lease_ttl_secs: {
|
|
1859
|
+
type: "number",
|
|
1860
|
+
minimum: 1,
|
|
1861
|
+
description: "Requested server-time lease lifetime (default 300s)"
|
|
1862
|
+
},
|
|
1863
|
+
idempotency_key: {
|
|
1864
|
+
type: "string",
|
|
1865
|
+
description: "Caller-stable key required for every composite work mutation"
|
|
1866
|
+
},
|
|
1867
|
+
allow_takeover: {
|
|
1868
|
+
type: "boolean",
|
|
1869
|
+
description: "Governed claim override for a live lease owned by another agent"
|
|
1870
|
+
},
|
|
1871
|
+
release_lease: {
|
|
1872
|
+
type: "boolean",
|
|
1873
|
+
description: "Explicitly release or retain the lease after block/abandon/checkpoint"
|
|
1874
|
+
},
|
|
1875
|
+
override_reason: {
|
|
1876
|
+
type: "string",
|
|
1877
|
+
description: "Explicit completion reason when no terminal Execution evidence exists"
|
|
1878
|
+
},
|
|
1879
|
+
scope_uids: {
|
|
1880
|
+
type: "array",
|
|
1881
|
+
items: { type: "string" },
|
|
1882
|
+
description: "Ordinary target UIDs that bound deterministic resume selection"
|
|
1883
|
+
},
|
|
1884
|
+
limit: {
|
|
1885
|
+
type: "number",
|
|
1886
|
+
minimum: 1,
|
|
1887
|
+
maximum: 1,
|
|
1888
|
+
description: "Resume returns one authoritative Task"
|
|
1889
|
+
},
|
|
1890
|
+
input_snapshot: {
|
|
1891
|
+
type: "object",
|
|
1892
|
+
description: "Structured inputs captured when an execution starts"
|
|
1893
|
+
},
|
|
1894
|
+
output_snapshot: {
|
|
1895
|
+
type: "object",
|
|
1896
|
+
description: "Structured outputs captured when an execution completes or fails"
|
|
1897
|
+
},
|
|
1898
|
+
outcome: {
|
|
1899
|
+
type: "string",
|
|
1900
|
+
description: "Execution outcome"
|
|
1901
|
+
},
|
|
1902
|
+
error: {
|
|
1903
|
+
type: "string",
|
|
1904
|
+
description: "Execution error detail (for fail_execution)"
|
|
1905
|
+
},
|
|
1906
|
+
side_effects: {
|
|
1907
|
+
type: "array",
|
|
1908
|
+
items: { type: "string" },
|
|
1909
|
+
description: "Observable side effects of the execution"
|
|
1910
|
+
},
|
|
1911
|
+
next_action: {
|
|
1912
|
+
type: "string",
|
|
1913
|
+
description: "Recommended next action retained on terminal Execution evidence"
|
|
1914
|
+
},
|
|
1915
|
+
checkpoint_summary: {
|
|
1916
|
+
type: "string",
|
|
1917
|
+
description: "Bounded material-attempt checkpoint summary"
|
|
1918
|
+
},
|
|
1919
|
+
test_summary: {
|
|
1920
|
+
type: "string",
|
|
1921
|
+
description: "Test commands/results retained on the Execution"
|
|
1922
|
+
},
|
|
1923
|
+
produces_node_uids: {
|
|
1924
|
+
type: "array",
|
|
1925
|
+
items: { type: "string" },
|
|
1926
|
+
description: "Nodes produced by checkpoint_iteration"
|
|
1927
|
+
},
|
|
1928
|
+
confidence: {
|
|
1929
|
+
type: "number",
|
|
1930
|
+
minimum: 0,
|
|
1931
|
+
maximum: 1
|
|
1932
|
+
},
|
|
1933
|
+
salience: {
|
|
1934
|
+
type: "number",
|
|
1935
|
+
minimum: 0,
|
|
1936
|
+
maximum: 1
|
|
1937
|
+
},
|
|
307
1938
|
props: {
|
|
308
1939
|
type: "object",
|
|
309
1940
|
description: "Additional properties (e.g. likelihood, impact, mitigation for risks)"
|
|
310
1941
|
},
|
|
1942
|
+
code_refs: {
|
|
1943
|
+
type: "array",
|
|
1944
|
+
items: { oneOf: [{ type: "string" }, { type: "object" }] },
|
|
1945
|
+
description: "Typed code targets to anchor and attach to created work or execution"
|
|
1946
|
+
},
|
|
1947
|
+
repo: { type: "string", description: "Configured repository id or root" },
|
|
1948
|
+
repo_space_uid: {
|
|
1949
|
+
type: "string",
|
|
1950
|
+
description: "Writable shared repository/Project Space for new code anchors"
|
|
1951
|
+
},
|
|
311
1952
|
agent_id: {
|
|
312
1953
|
type: "string",
|
|
313
1954
|
description: "Agent identity"
|
|
314
1955
|
}
|
|
315
1956
|
},
|
|
316
1957
|
required: ["action"]
|
|
1958
|
+
},
|
|
1959
|
+
annotations: {
|
|
1960
|
+
readOnlyHint: false,
|
|
1961
|
+
destructiveHint: false,
|
|
1962
|
+
idempotentHint: false,
|
|
1963
|
+
openWorldHint: true
|
|
317
1964
|
}
|
|
318
1965
|
},
|
|
319
1966
|
{
|
|
@@ -688,14 +2335,45 @@ var TOOLS = [
|
|
|
688
2335
|
},
|
|
689
2336
|
required: ["action"]
|
|
690
2337
|
}
|
|
691
|
-
}
|
|
2338
|
+
},
|
|
2339
|
+
SYNC_TOOL,
|
|
2340
|
+
CODE_TOOL
|
|
692
2341
|
];
|
|
693
|
-
|
|
2342
|
+
var INVOCATION_CONTEXT_SCHEMA = {
|
|
2343
|
+
type: "object",
|
|
2344
|
+
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.",
|
|
2345
|
+
properties: {
|
|
2346
|
+
harness: { type: "string", enum: ["claude-code", "codex", "generic"] },
|
|
2347
|
+
harnessSessionId: { type: "string" },
|
|
2348
|
+
harnessTurnId: { type: "string" },
|
|
2349
|
+
cwd: { type: "string" },
|
|
2350
|
+
repoId: { type: "string" },
|
|
2351
|
+
branch: { type: "string" },
|
|
2352
|
+
commit: { type: "string" },
|
|
2353
|
+
worktreeState: { type: "string", enum: ["clean", "dirty"] },
|
|
2354
|
+
model: { type: "string" },
|
|
2355
|
+
injectedBy: { type: "string", enum: ["hook", "mcp-process", "caller"] }
|
|
2356
|
+
}
|
|
2357
|
+
};
|
|
2358
|
+
for (const tool of TOOLS) {
|
|
2359
|
+
if (tool.name === "mindgraph_retrieve") continue;
|
|
2360
|
+
const schema = tool.inputSchema;
|
|
2361
|
+
if (schema.properties && !schema.properties.invocation_context) {
|
|
2362
|
+
schema.properties.invocation_context = INVOCATION_CONTEXT_SCHEMA;
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
function toolsForProfile(profile = process.env.MINDGRAPH_PROFILE || "general") {
|
|
2366
|
+
if (profile === "coding") return TOOLS;
|
|
2367
|
+
return TOOLS.filter(
|
|
2368
|
+
(tool) => tool.name !== "mindgraph_code" && tool.name !== "mindgraph_sync"
|
|
2369
|
+
);
|
|
2370
|
+
}
|
|
2371
|
+
function ok3(data) {
|
|
694
2372
|
return {
|
|
695
2373
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
696
2374
|
};
|
|
697
2375
|
}
|
|
698
|
-
function
|
|
2376
|
+
function err3(message) {
|
|
699
2377
|
return {
|
|
700
2378
|
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
701
2379
|
isError: true
|
|
@@ -703,29 +2381,53 @@ function err(message) {
|
|
|
703
2381
|
}
|
|
704
2382
|
async function handleTool(client2, name, args) {
|
|
705
2383
|
try {
|
|
2384
|
+
let result;
|
|
706
2385
|
switch (name) {
|
|
707
2386
|
case "mindgraph_capture":
|
|
708
|
-
|
|
2387
|
+
result = await handleCapture(client2, args);
|
|
2388
|
+
break;
|
|
709
2389
|
case "mindgraph_reason":
|
|
710
|
-
|
|
2390
|
+
result = await handleReason(client2, args);
|
|
2391
|
+
break;
|
|
711
2392
|
case "mindgraph_commit":
|
|
712
|
-
|
|
2393
|
+
result = await handleCommit(client2, args);
|
|
2394
|
+
break;
|
|
713
2395
|
case "mindgraph_plan":
|
|
714
|
-
|
|
2396
|
+
result = await handlePlan(client2, args);
|
|
2397
|
+
break;
|
|
715
2398
|
case "mindgraph_retrieve":
|
|
716
|
-
|
|
2399
|
+
result = await handleRetrieve(client2, args);
|
|
2400
|
+
break;
|
|
717
2401
|
case "mindgraph_ingest":
|
|
718
|
-
|
|
2402
|
+
result = await handleIngest(client2, args);
|
|
2403
|
+
break;
|
|
719
2404
|
case "mindgraph_synthesize":
|
|
720
|
-
|
|
2405
|
+
result = await handleSynthesize(client2, args);
|
|
2406
|
+
break;
|
|
721
2407
|
case "mindgraph_ontology":
|
|
722
|
-
|
|
2408
|
+
result = await handleOntology(client2, args);
|
|
2409
|
+
break;
|
|
2410
|
+
case "mindgraph_code":
|
|
2411
|
+
result = await handleCodeTool(client2, args);
|
|
2412
|
+
break;
|
|
2413
|
+
case "mindgraph_sync":
|
|
2414
|
+
result = await handleSyncTool(client2, args);
|
|
2415
|
+
break;
|
|
723
2416
|
default:
|
|
724
|
-
|
|
2417
|
+
result = err3(`Unknown tool: ${name}`);
|
|
725
2418
|
}
|
|
2419
|
+
const action = typeof args.action === "string" ? args.action : "";
|
|
2420
|
+
const attach = name === "mindgraph_capture" || name === "mindgraph_commit" && action !== "get_open_decisions" || name === "mindgraph_plan" && ![
|
|
2421
|
+
"get_plan",
|
|
2422
|
+
"get_executions",
|
|
2423
|
+
"get_pending",
|
|
2424
|
+
"get_assessments",
|
|
2425
|
+
"resume_work",
|
|
2426
|
+
"heartbeat"
|
|
2427
|
+
].includes(action);
|
|
2428
|
+
return attach ? await attachCodeRefsToToolResult(client2, result, args) : result;
|
|
726
2429
|
} catch (e) {
|
|
727
|
-
|
|
728
|
-
return err(message);
|
|
2430
|
+
return err3(errorDetail(e));
|
|
729
2431
|
}
|
|
730
2432
|
}
|
|
731
2433
|
async function handleCapture(client2, args) {
|
|
@@ -734,6 +2436,12 @@ async function handleCapture(client2, args) {
|
|
|
734
2436
|
label,
|
|
735
2437
|
summary,
|
|
736
2438
|
content,
|
|
2439
|
+
session_uid,
|
|
2440
|
+
work_uid,
|
|
2441
|
+
execution_uid,
|
|
2442
|
+
idempotency_key,
|
|
2443
|
+
supersedes_uid,
|
|
2444
|
+
summarizes_uids,
|
|
737
2445
|
entity_type,
|
|
738
2446
|
source_uid,
|
|
739
2447
|
mood,
|
|
@@ -744,12 +2452,30 @@ async function handleCapture(client2, args) {
|
|
|
744
2452
|
agent_id
|
|
745
2453
|
} = args;
|
|
746
2454
|
switch (action) {
|
|
2455
|
+
case "lesson":
|
|
2456
|
+
return ok3(
|
|
2457
|
+
await client2.distill({
|
|
2458
|
+
label,
|
|
2459
|
+
output_type: "lesson",
|
|
2460
|
+
summary,
|
|
2461
|
+
confidence,
|
|
2462
|
+
salience,
|
|
2463
|
+
session_uid,
|
|
2464
|
+
work_uid,
|
|
2465
|
+
execution_uid,
|
|
2466
|
+
idempotency_key,
|
|
2467
|
+
supersedes_uid,
|
|
2468
|
+
summarizes_uids,
|
|
2469
|
+
props,
|
|
2470
|
+
agent_id
|
|
2471
|
+
})
|
|
2472
|
+
);
|
|
747
2473
|
case "journal": {
|
|
748
|
-
if (!content) return
|
|
2474
|
+
if (!content) return err3("content is required for action=journal");
|
|
749
2475
|
const journalProps = { content };
|
|
750
2476
|
if (mood) journalProps.mood = mood;
|
|
751
2477
|
if (tags) journalProps.tags = tags;
|
|
752
|
-
return
|
|
2478
|
+
return ok3(await client2.journal(label, journalProps, { agent_id }));
|
|
753
2479
|
}
|
|
754
2480
|
case "entity": {
|
|
755
2481
|
const type = entity_type || "concept";
|
|
@@ -757,22 +2483,22 @@ async function handleCapture(client2, args) {
|
|
|
757
2483
|
if (summary) entityProps.description = summary;
|
|
758
2484
|
switch (type) {
|
|
759
2485
|
case "person":
|
|
760
|
-
return
|
|
2486
|
+
return ok3(await client2.findOrCreatePerson(label, entityProps, agent_id));
|
|
761
2487
|
case "organization":
|
|
762
|
-
return
|
|
2488
|
+
return ok3(await client2.findOrCreateOrganization(label, entityProps, agent_id));
|
|
763
2489
|
case "nation":
|
|
764
|
-
return
|
|
2490
|
+
return ok3(await client2.findOrCreateNation(label, entityProps, agent_id));
|
|
765
2491
|
case "event":
|
|
766
|
-
return
|
|
2492
|
+
return ok3(await client2.findOrCreateEvent(label, entityProps, agent_id));
|
|
767
2493
|
case "place":
|
|
768
|
-
return
|
|
2494
|
+
return ok3(await client2.findOrCreatePlace(label, entityProps, agent_id));
|
|
769
2495
|
case "concept":
|
|
770
2496
|
default:
|
|
771
|
-
return
|
|
2497
|
+
return ok3(await client2.findOrCreateConcept(label, entityProps, agent_id));
|
|
772
2498
|
}
|
|
773
2499
|
}
|
|
774
2500
|
case "observation":
|
|
775
|
-
return
|
|
2501
|
+
return ok3(
|
|
776
2502
|
await client2.capture({
|
|
777
2503
|
action: "observation",
|
|
778
2504
|
label,
|
|
@@ -784,7 +2510,7 @@ async function handleCapture(client2, args) {
|
|
|
784
2510
|
})
|
|
785
2511
|
);
|
|
786
2512
|
case "source":
|
|
787
|
-
return
|
|
2513
|
+
return ok3(
|
|
788
2514
|
await client2.capture({
|
|
789
2515
|
action: "source",
|
|
790
2516
|
label,
|
|
@@ -796,7 +2522,7 @@ async function handleCapture(client2, args) {
|
|
|
796
2522
|
})
|
|
797
2523
|
);
|
|
798
2524
|
case "snippet":
|
|
799
|
-
return
|
|
2525
|
+
return ok3(
|
|
800
2526
|
await client2.capture({
|
|
801
2527
|
action: "snippet",
|
|
802
2528
|
label,
|
|
@@ -809,7 +2535,7 @@ async function handleCapture(client2, args) {
|
|
|
809
2535
|
})
|
|
810
2536
|
);
|
|
811
2537
|
case "concept":
|
|
812
|
-
return
|
|
2538
|
+
return ok3(
|
|
813
2539
|
await client2.structure({
|
|
814
2540
|
action: "concept",
|
|
815
2541
|
label,
|
|
@@ -821,7 +2547,7 @@ async function handleCapture(client2, args) {
|
|
|
821
2547
|
})
|
|
822
2548
|
);
|
|
823
2549
|
default:
|
|
824
|
-
return
|
|
2550
|
+
return err3(`Unknown capture action: ${action}`);
|
|
825
2551
|
}
|
|
826
2552
|
}
|
|
827
2553
|
async function handleReason(client2, args) {
|
|
@@ -839,20 +2565,30 @@ async function handleReason(client2, args) {
|
|
|
839
2565
|
agent_id
|
|
840
2566
|
} = args;
|
|
841
2567
|
if (action === "claim") {
|
|
842
|
-
const claimObj = claim
|
|
843
|
-
|
|
844
|
-
|
|
2568
|
+
const claimObj = claim ? {
|
|
2569
|
+
label: claim.label,
|
|
2570
|
+
confidence: claim.confidence,
|
|
2571
|
+
...claim.content ? { props: { content: claim.content } } : {}
|
|
2572
|
+
} : label ? { label, confidence } : null;
|
|
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
|
-
evidence
|
|
848
|
-
|
|
2577
|
+
evidence: evidence?.map((e) => ({
|
|
2578
|
+
label: e.label,
|
|
2579
|
+
...e.description ? { props: { description: e.description } } : {}
|
|
2580
|
+
})),
|
|
2581
|
+
warrant: warrant ? {
|
|
2582
|
+
label: warrant.label,
|
|
2583
|
+
...warrant.content ? { props: { content: warrant.content } } : {}
|
|
2584
|
+
} : void 0,
|
|
849
2585
|
argument,
|
|
850
2586
|
agent_id
|
|
851
2587
|
})
|
|
852
2588
|
);
|
|
853
2589
|
}
|
|
854
|
-
if (!label) return
|
|
855
|
-
return
|
|
2590
|
+
if (!label) return err3(`label is required for action=${action}`);
|
|
2591
|
+
return ok3(
|
|
856
2592
|
await client2.inquire({
|
|
857
2593
|
action,
|
|
858
2594
|
label,
|
|
@@ -885,8 +2621,8 @@ async function handleCommit(client2, args) {
|
|
|
885
2621
|
case "goal":
|
|
886
2622
|
case "project":
|
|
887
2623
|
case "milestone":
|
|
888
|
-
if (!label) return
|
|
889
|
-
return
|
|
2624
|
+
if (!label) return err3(`label is required for action=${action}`);
|
|
2625
|
+
return ok3(
|
|
890
2626
|
await client2.commit({
|
|
891
2627
|
action,
|
|
892
2628
|
label,
|
|
@@ -899,15 +2635,15 @@ async function handleCommit(client2, args) {
|
|
|
899
2635
|
})
|
|
900
2636
|
);
|
|
901
2637
|
case "open_decision":
|
|
902
|
-
if (!label) return
|
|
903
|
-
return
|
|
2638
|
+
if (!label) return err3("label is required for open_decision");
|
|
2639
|
+
return ok3(await client2.openDecision(label, { summary, props, agent_id }));
|
|
904
2640
|
case "add_option":
|
|
905
|
-
if (!decision_uid) return
|
|
906
|
-
if (!label) return
|
|
907
|
-
return
|
|
2641
|
+
if (!decision_uid) return err3("decision_uid is required for add_option");
|
|
2642
|
+
if (!label) return err3("label is required for add_option");
|
|
2643
|
+
return ok3(await client2.addOption(decision_uid, label, { summary, props, agent_id }));
|
|
908
2644
|
case "add_constraint":
|
|
909
|
-
if (!decision_uid) return
|
|
910
|
-
return
|
|
2645
|
+
if (!decision_uid) return err3("decision_uid is required for add_constraint");
|
|
2646
|
+
return ok3(
|
|
911
2647
|
await client2.deliberate({
|
|
912
2648
|
action: "add_constraint",
|
|
913
2649
|
label,
|
|
@@ -918,9 +2654,9 @@ async function handleCommit(client2, args) {
|
|
|
918
2654
|
})
|
|
919
2655
|
);
|
|
920
2656
|
case "resolve_decision":
|
|
921
|
-
if (!decision_uid) return
|
|
922
|
-
if (!chosen_option_uid) return
|
|
923
|
-
return
|
|
2657
|
+
if (!decision_uid) return err3("decision_uid is required for resolve_decision");
|
|
2658
|
+
if (!chosen_option_uid) return err3("chosen_option_uid is required for resolve_decision");
|
|
2659
|
+
return ok3(
|
|
924
2660
|
await client2.resolveDecision(decision_uid, chosen_option_uid, {
|
|
925
2661
|
summary,
|
|
926
2662
|
props,
|
|
@@ -932,9 +2668,9 @@ async function handleCommit(client2, args) {
|
|
|
932
2668
|
})
|
|
933
2669
|
);
|
|
934
2670
|
case "get_open_decisions":
|
|
935
|
-
return
|
|
2671
|
+
return ok3(await client2.getOpenDecisions());
|
|
936
2672
|
default:
|
|
937
|
-
return
|
|
2673
|
+
return err3(`Unknown commit action: ${action}`);
|
|
938
2674
|
}
|
|
939
2675
|
}
|
|
940
2676
|
async function handlePlan(client2, args) {
|
|
@@ -944,8 +2680,41 @@ async function handlePlan(client2, args) {
|
|
|
944
2680
|
summary,
|
|
945
2681
|
plan_uid,
|
|
946
2682
|
task_uid,
|
|
2683
|
+
step_uid,
|
|
2684
|
+
session_uid,
|
|
2685
|
+
goal_uid,
|
|
947
2686
|
target_uid,
|
|
2687
|
+
execution_uid,
|
|
2688
|
+
executor_uid,
|
|
2689
|
+
affordance_uid,
|
|
2690
|
+
produces_node_uid,
|
|
2691
|
+
filter_plan_uid,
|
|
2692
|
+
depends_on_uids,
|
|
2693
|
+
related_uids,
|
|
948
2694
|
status,
|
|
2695
|
+
task_status,
|
|
2696
|
+
step_status,
|
|
2697
|
+
execution_status,
|
|
2698
|
+
expected_version,
|
|
2699
|
+
lease_epoch,
|
|
2700
|
+
lease_ttl_secs,
|
|
2701
|
+
idempotency_key,
|
|
2702
|
+
allow_takeover,
|
|
2703
|
+
release_lease,
|
|
2704
|
+
override_reason,
|
|
2705
|
+
scope_uids,
|
|
2706
|
+
limit,
|
|
2707
|
+
confidence,
|
|
2708
|
+
salience,
|
|
2709
|
+
input_snapshot,
|
|
2710
|
+
output_snapshot,
|
|
2711
|
+
side_effects,
|
|
2712
|
+
outcome,
|
|
2713
|
+
error,
|
|
2714
|
+
next_action,
|
|
2715
|
+
checkpoint_summary,
|
|
2716
|
+
test_summary,
|
|
2717
|
+
produces_node_uids,
|
|
949
2718
|
props,
|
|
950
2719
|
agent_id
|
|
951
2720
|
} = args;
|
|
@@ -956,25 +2725,120 @@ async function handlePlan(client2, args) {
|
|
|
956
2725
|
case "add_step":
|
|
957
2726
|
case "update_status":
|
|
958
2727
|
case "get_plan":
|
|
959
|
-
|
|
2728
|
+
case "resume_work":
|
|
2729
|
+
case "claim_task":
|
|
2730
|
+
case "heartbeat":
|
|
2731
|
+
case "start_iteration":
|
|
2732
|
+
case "checkpoint_iteration":
|
|
2733
|
+
case "block_task":
|
|
2734
|
+
case "complete_task":
|
|
2735
|
+
case "abandon_iteration":
|
|
2736
|
+
return ok3(
|
|
960
2737
|
await client2.plan({
|
|
961
2738
|
action,
|
|
962
2739
|
label,
|
|
963
2740
|
summary,
|
|
2741
|
+
confidence,
|
|
2742
|
+
salience,
|
|
2743
|
+
goal_uid,
|
|
964
2744
|
plan_uid,
|
|
965
2745
|
task_uid,
|
|
2746
|
+
step_uid,
|
|
2747
|
+
session_uid,
|
|
2748
|
+
target_uid,
|
|
2749
|
+
depends_on_uids,
|
|
2750
|
+
related_uids,
|
|
966
2751
|
status,
|
|
2752
|
+
task_status,
|
|
2753
|
+
step_status,
|
|
2754
|
+
execution_status,
|
|
2755
|
+
execution_uid,
|
|
2756
|
+
expected_version,
|
|
2757
|
+
lease_epoch,
|
|
2758
|
+
lease_ttl_secs,
|
|
2759
|
+
idempotency_key,
|
|
2760
|
+
allow_takeover,
|
|
2761
|
+
release_lease,
|
|
2762
|
+
override_reason,
|
|
2763
|
+
scope_uids,
|
|
2764
|
+
limit,
|
|
2765
|
+
input_snapshot,
|
|
2766
|
+
output_snapshot,
|
|
2767
|
+
side_effects,
|
|
2768
|
+
outcome,
|
|
2769
|
+
error,
|
|
2770
|
+
next_action,
|
|
2771
|
+
checkpoint_summary,
|
|
2772
|
+
test_summary,
|
|
2773
|
+
produces_node_uids,
|
|
967
2774
|
props,
|
|
968
2775
|
agent_id
|
|
969
2776
|
})
|
|
970
2777
|
);
|
|
971
2778
|
// Execution tracking
|
|
972
2779
|
case "start_execution":
|
|
973
|
-
return
|
|
2780
|
+
return ok3(
|
|
2781
|
+
await client2.execution({
|
|
2782
|
+
action: "start",
|
|
2783
|
+
label,
|
|
2784
|
+
summary,
|
|
2785
|
+
confidence,
|
|
2786
|
+
salience,
|
|
2787
|
+
plan_uid,
|
|
2788
|
+
executor_uid,
|
|
2789
|
+
affordance_uid,
|
|
2790
|
+
related_uids,
|
|
2791
|
+
input_snapshot,
|
|
2792
|
+
props,
|
|
2793
|
+
agent_id
|
|
2794
|
+
})
|
|
2795
|
+
);
|
|
974
2796
|
case "complete_execution":
|
|
975
|
-
return
|
|
2797
|
+
return ok3(
|
|
2798
|
+
await client2.execution({
|
|
2799
|
+
action: "complete",
|
|
2800
|
+
execution_uid,
|
|
2801
|
+
produces_node_uid,
|
|
2802
|
+
output_snapshot,
|
|
2803
|
+
side_effects,
|
|
2804
|
+
outcome,
|
|
2805
|
+
props,
|
|
2806
|
+
agent_id
|
|
2807
|
+
})
|
|
2808
|
+
);
|
|
976
2809
|
case "fail_execution":
|
|
977
|
-
return
|
|
2810
|
+
return ok3(
|
|
2811
|
+
await client2.execution({
|
|
2812
|
+
action: "fail",
|
|
2813
|
+
execution_uid,
|
|
2814
|
+
output_snapshot,
|
|
2815
|
+
side_effects,
|
|
2816
|
+
outcome,
|
|
2817
|
+
error,
|
|
2818
|
+
props,
|
|
2819
|
+
agent_id
|
|
2820
|
+
})
|
|
2821
|
+
);
|
|
2822
|
+
case "register_agent":
|
|
2823
|
+
return ok3(
|
|
2824
|
+
await client2.execution({
|
|
2825
|
+
action: "register_agent",
|
|
2826
|
+
label,
|
|
2827
|
+
summary,
|
|
2828
|
+
confidence,
|
|
2829
|
+
salience,
|
|
2830
|
+
props,
|
|
2831
|
+
agent_id
|
|
2832
|
+
})
|
|
2833
|
+
);
|
|
2834
|
+
case "get_executions":
|
|
2835
|
+
return ok3(
|
|
2836
|
+
await client2.execution({
|
|
2837
|
+
action: "get_executions",
|
|
2838
|
+
filter_plan_uid,
|
|
2839
|
+
agent_id
|
|
2840
|
+
})
|
|
2841
|
+
);
|
|
978
2842
|
// Governance — read-only from MCP by design (C33). The write actions were
|
|
979
2843
|
// removed rather than repaired: `resolve_approval` here also sent
|
|
980
2844
|
// `task_uid` where the server requires `approval_uid`, and never sent
|
|
@@ -982,12 +2846,12 @@ async function handlePlan(client2, args) {
|
|
|
982
2846
|
// DENIED every approval an agent tried to resolve. Wiring that correctly
|
|
983
2847
|
// would have meant building a working self-approval path — the hole itself.
|
|
984
2848
|
case "get_pending":
|
|
985
|
-
return
|
|
2849
|
+
return ok3(await client2.governance({ action: "get_pending", agent_id }));
|
|
986
2850
|
// Procedures (merged from mindgraph_action)
|
|
987
2851
|
case "create_flow":
|
|
988
2852
|
case "add_affordance":
|
|
989
2853
|
case "add_control":
|
|
990
|
-
return
|
|
2854
|
+
return ok3(
|
|
991
2855
|
await client2.procedure({
|
|
992
2856
|
action,
|
|
993
2857
|
label,
|
|
@@ -998,7 +2862,7 @@ async function handlePlan(client2, args) {
|
|
|
998
2862
|
})
|
|
999
2863
|
);
|
|
1000
2864
|
case "add_procedure_step":
|
|
1001
|
-
return
|
|
2865
|
+
return ok3(
|
|
1002
2866
|
await client2.procedure({
|
|
1003
2867
|
action: "add_step",
|
|
1004
2868
|
label,
|
|
@@ -1010,7 +2874,7 @@ async function handlePlan(client2, args) {
|
|
|
1010
2874
|
);
|
|
1011
2875
|
// Risk assessment (merged from mindgraph_action)
|
|
1012
2876
|
case "assess_risk":
|
|
1013
|
-
return
|
|
2877
|
+
return ok3(
|
|
1014
2878
|
await client2.risk({
|
|
1015
2879
|
action: "assess",
|
|
1016
2880
|
label,
|
|
@@ -1021,7 +2885,7 @@ async function handlePlan(client2, args) {
|
|
|
1021
2885
|
})
|
|
1022
2886
|
);
|
|
1023
2887
|
case "get_assessments":
|
|
1024
|
-
return
|
|
2888
|
+
return ok3(
|
|
1025
2889
|
await client2.risk({
|
|
1026
2890
|
action: "get_assessments",
|
|
1027
2891
|
target_uid,
|
|
@@ -1029,7 +2893,7 @@ async function handlePlan(client2, args) {
|
|
|
1029
2893
|
})
|
|
1030
2894
|
);
|
|
1031
2895
|
default:
|
|
1032
|
-
return
|
|
2896
|
+
return err3(`Unknown plan action: ${action}`);
|
|
1033
2897
|
}
|
|
1034
2898
|
}
|
|
1035
2899
|
async function handleRetrieve(client2, args) {
|
|
@@ -1058,8 +2922,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1058
2922
|
} = args;
|
|
1059
2923
|
switch (action) {
|
|
1060
2924
|
case "context":
|
|
1061
|
-
if (!query) return
|
|
1062
|
-
return
|
|
2925
|
+
if (!query) return err3("query is required for context retrieval");
|
|
2926
|
+
return ok3(
|
|
1063
2927
|
await client2.retrieveContext({
|
|
1064
2928
|
query,
|
|
1065
2929
|
node_limit: limit,
|
|
@@ -1072,8 +2936,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1072
2936
|
})
|
|
1073
2937
|
);
|
|
1074
2938
|
case "text":
|
|
1075
|
-
if (!query) return
|
|
1076
|
-
return
|
|
2939
|
+
if (!query) return err3("query is required for text search");
|
|
2940
|
+
return ok3(
|
|
1077
2941
|
await client2.retrieve({
|
|
1078
2942
|
action: "text",
|
|
1079
2943
|
query,
|
|
@@ -1084,8 +2948,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1084
2948
|
})
|
|
1085
2949
|
);
|
|
1086
2950
|
case "semantic":
|
|
1087
|
-
if (!query) return
|
|
1088
|
-
return
|
|
2951
|
+
if (!query) return err3("query is required for semantic search");
|
|
2952
|
+
return ok3(
|
|
1089
2953
|
await client2.retrieve({
|
|
1090
2954
|
action: "semantic",
|
|
1091
2955
|
query,
|
|
@@ -1096,8 +2960,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1096
2960
|
})
|
|
1097
2961
|
);
|
|
1098
2962
|
case "hybrid":
|
|
1099
|
-
if (!query) return
|
|
1100
|
-
return
|
|
2963
|
+
if (!query) return err3("query is required for hybrid search");
|
|
2964
|
+
return ok3(
|
|
1101
2965
|
await client2.retrieve({
|
|
1102
2966
|
action: "hybrid",
|
|
1103
2967
|
query,
|
|
@@ -1109,17 +2973,17 @@ async function handleRetrieve(client2, args) {
|
|
|
1109
2973
|
);
|
|
1110
2974
|
// Structured queries
|
|
1111
2975
|
case "active_goals":
|
|
1112
|
-
return
|
|
2976
|
+
return ok3(await client2.getGoals());
|
|
1113
2977
|
case "open_questions":
|
|
1114
|
-
return
|
|
2978
|
+
return ok3(await client2.getOpenQuestions());
|
|
1115
2979
|
case "weak_claims":
|
|
1116
|
-
return
|
|
2980
|
+
return ok3(await client2.getWeakClaims());
|
|
1117
2981
|
case "pending_approvals":
|
|
1118
|
-
return
|
|
2982
|
+
return ok3(await client2.getPendingApprovals());
|
|
1119
2983
|
case "unresolved_contradictions":
|
|
1120
|
-
return
|
|
2984
|
+
return ok3(await client2.getContradictions());
|
|
1121
2985
|
case "stale_derivations":
|
|
1122
|
-
return
|
|
2986
|
+
return ok3(
|
|
1123
2987
|
await client2.retrieve({
|
|
1124
2988
|
action: "stale_derivations",
|
|
1125
2989
|
limit,
|
|
@@ -1127,7 +2991,7 @@ async function handleRetrieve(client2, args) {
|
|
|
1127
2991
|
})
|
|
1128
2992
|
);
|
|
1129
2993
|
case "preferences":
|
|
1130
|
-
return
|
|
2994
|
+
return ok3(
|
|
1131
2995
|
await client2.retrieve({
|
|
1132
2996
|
action: "preferences",
|
|
1133
2997
|
...query ? { query, k: limit } : {},
|
|
@@ -1136,8 +3000,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1136
3000
|
})
|
|
1137
3001
|
);
|
|
1138
3002
|
case "layer":
|
|
1139
|
-
if (!layer) return
|
|
1140
|
-
return
|
|
3003
|
+
if (!layer) return err3("layer is required for layer retrieval");
|
|
3004
|
+
return ok3(
|
|
1141
3005
|
await client2.retrieve({
|
|
1142
3006
|
action: "layer",
|
|
1143
3007
|
layer,
|
|
@@ -1146,7 +3010,7 @@ async function handleRetrieve(client2, args) {
|
|
|
1146
3010
|
})
|
|
1147
3011
|
);
|
|
1148
3012
|
case "recent":
|
|
1149
|
-
return
|
|
3013
|
+
return ok3(
|
|
1150
3014
|
await client2.retrieve({
|
|
1151
3015
|
action: "recent",
|
|
1152
3016
|
limit,
|
|
@@ -1158,7 +3022,7 @@ async function handleRetrieve(client2, args) {
|
|
|
1158
3022
|
);
|
|
1159
3023
|
// Document inventory
|
|
1160
3024
|
case "document_index":
|
|
1161
|
-
return
|
|
3025
|
+
return ok3(
|
|
1162
3026
|
await client2.getNodes({
|
|
1163
3027
|
node_type: "Document",
|
|
1164
3028
|
limit: limit ?? 100
|
|
@@ -1166,8 +3030,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1166
3030
|
);
|
|
1167
3031
|
// Graph traversal
|
|
1168
3032
|
case "chain":
|
|
1169
|
-
if (!start_uid) return
|
|
1170
|
-
return
|
|
3033
|
+
if (!start_uid) return err3("start_uid is required for chain traversal");
|
|
3034
|
+
return ok3(
|
|
1171
3035
|
await client2.traverse({
|
|
1172
3036
|
action: "chain",
|
|
1173
3037
|
start_uid,
|
|
@@ -1178,8 +3042,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1178
3042
|
})
|
|
1179
3043
|
);
|
|
1180
3044
|
case "neighborhood":
|
|
1181
|
-
if (!start_uid) return
|
|
1182
|
-
return
|
|
3045
|
+
if (!start_uid) return err3("start_uid is required for neighborhood traversal");
|
|
3046
|
+
return ok3(
|
|
1183
3047
|
await client2.traverse({
|
|
1184
3048
|
action: "neighborhood",
|
|
1185
3049
|
start_uid,
|
|
@@ -1192,9 +3056,9 @@ async function handleRetrieve(client2, args) {
|
|
|
1192
3056
|
})
|
|
1193
3057
|
);
|
|
1194
3058
|
case "path":
|
|
1195
|
-
if (!start_uid) return
|
|
1196
|
-
if (!end_uid) return
|
|
1197
|
-
return
|
|
3059
|
+
if (!start_uid) return err3("start_uid is required for path traversal");
|
|
3060
|
+
if (!end_uid) return err3("end_uid is required for path traversal");
|
|
3061
|
+
return ok3(
|
|
1198
3062
|
await client2.traverse({
|
|
1199
3063
|
action: "path",
|
|
1200
3064
|
start_uid,
|
|
@@ -1208,8 +3072,8 @@ async function handleRetrieve(client2, args) {
|
|
|
1208
3072
|
})
|
|
1209
3073
|
);
|
|
1210
3074
|
case "subgraph":
|
|
1211
|
-
if (!start_uid) return
|
|
1212
|
-
return
|
|
3075
|
+
if (!start_uid) return err3("start_uid is required for subgraph traversal");
|
|
3076
|
+
return ok3(
|
|
1213
3077
|
await client2.traverse({
|
|
1214
3078
|
action: "subgraph",
|
|
1215
3079
|
start_uid,
|
|
@@ -1222,7 +3086,7 @@ async function handleRetrieve(client2, args) {
|
|
|
1222
3086
|
})
|
|
1223
3087
|
);
|
|
1224
3088
|
default:
|
|
1225
|
-
return
|
|
3089
|
+
return err3(`Unknown retrieve action: ${action}`);
|
|
1226
3090
|
}
|
|
1227
3091
|
}
|
|
1228
3092
|
async function handleIngest(client2, args) {
|
|
@@ -1251,7 +3115,7 @@ async function handleIngest(client2, args) {
|
|
|
1251
3115
|
const targetsOntology = Array.isArray(layers) && layers.includes("ontology");
|
|
1252
3116
|
const cognitiveLayers = targetsOntology ? layers.filter((layer) => layer !== "ontology") : layers;
|
|
1253
3117
|
if (targetsOntology && !ontology_schema_id) {
|
|
1254
|
-
return
|
|
3118
|
+
return err3(
|
|
1255
3119
|
"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
3120
|
);
|
|
1257
3121
|
}
|
|
@@ -1259,8 +3123,8 @@ async function handleIngest(client2, args) {
|
|
|
1259
3123
|
const ontologyOnly = targetsOntology && forwardedLayers === void 0;
|
|
1260
3124
|
switch (action) {
|
|
1261
3125
|
case "chunk":
|
|
1262
|
-
if (!content) return
|
|
1263
|
-
return
|
|
3126
|
+
if (!content) return err3("content is required for chunk ingestion");
|
|
3127
|
+
return ok3(
|
|
1264
3128
|
await client2.ingestChunk({
|
|
1265
3129
|
content,
|
|
1266
3130
|
label: title,
|
|
@@ -1274,8 +3138,8 @@ async function handleIngest(client2, args) {
|
|
|
1274
3138
|
})
|
|
1275
3139
|
);
|
|
1276
3140
|
case "document":
|
|
1277
|
-
if (!content) return
|
|
1278
|
-
return
|
|
3141
|
+
if (!content) return err3("content is required for document ingestion");
|
|
3142
|
+
return ok3(
|
|
1279
3143
|
await client2.ingestDocument({
|
|
1280
3144
|
content,
|
|
1281
3145
|
title,
|
|
@@ -1294,8 +3158,8 @@ async function handleIngest(client2, args) {
|
|
|
1294
3158
|
})
|
|
1295
3159
|
);
|
|
1296
3160
|
case "session":
|
|
1297
|
-
if (!content) return
|
|
1298
|
-
return
|
|
3161
|
+
if (!content) return err3("content is required for session ingestion");
|
|
3162
|
+
return ok3(
|
|
1299
3163
|
await client2.ingestSession({
|
|
1300
3164
|
content,
|
|
1301
3165
|
title,
|
|
@@ -1312,11 +3176,11 @@ async function handleIngest(client2, args) {
|
|
|
1312
3176
|
);
|
|
1313
3177
|
case "job_status":
|
|
1314
3178
|
if (job_id) {
|
|
1315
|
-
return
|
|
3179
|
+
return ok3(await client2.getJob(job_id));
|
|
1316
3180
|
}
|
|
1317
|
-
return
|
|
3181
|
+
return ok3(await client2.listJobs());
|
|
1318
3182
|
default:
|
|
1319
|
-
return
|
|
3183
|
+
return err3(`Unknown ingest action: ${action}`);
|
|
1320
3184
|
}
|
|
1321
3185
|
}
|
|
1322
3186
|
async function handleSynthesize(client2, args) {
|
|
@@ -1324,19 +3188,19 @@ async function handleSynthesize(client2, args) {
|
|
|
1324
3188
|
switch (action) {
|
|
1325
3189
|
case "signals":
|
|
1326
3190
|
if (!project_uid)
|
|
1327
|
-
return
|
|
1328
|
-
return
|
|
3191
|
+
return err3("project_uid is required for action='signals'");
|
|
3192
|
+
return ok3(
|
|
1329
3193
|
await client2.signals(project_uid, { signals, target_types })
|
|
1330
3194
|
);
|
|
1331
3195
|
case "run":
|
|
1332
3196
|
if (!project_uid)
|
|
1333
|
-
return
|
|
1334
|
-
return
|
|
3197
|
+
return err3("project_uid is required for action='run'");
|
|
3198
|
+
return ok3(await client2.runSynthesis(project_uid));
|
|
1335
3199
|
case "job_status":
|
|
1336
|
-
if (!job_id) return
|
|
1337
|
-
return
|
|
3200
|
+
if (!job_id) return err3("job_id is required for action='job_status'");
|
|
3201
|
+
return ok3(await client2.getJob(job_id));
|
|
1338
3202
|
default:
|
|
1339
|
-
return
|
|
3203
|
+
return err3(`Unknown synthesize action: ${action}`);
|
|
1340
3204
|
}
|
|
1341
3205
|
}
|
|
1342
3206
|
async function handleOntology(client2, args) {
|
|
@@ -1364,18 +3228,18 @@ async function handleOntology(client2, args) {
|
|
|
1364
3228
|
} = args;
|
|
1365
3229
|
switch (action) {
|
|
1366
3230
|
case "schemas":
|
|
1367
|
-
return
|
|
3231
|
+
return ok3(await client2.listOntologySchemas());
|
|
1368
3232
|
case "schema":
|
|
1369
|
-
if (!schema_id) return
|
|
1370
|
-
return
|
|
3233
|
+
if (!schema_id) return err3("schema_id is required for action='schema'");
|
|
3234
|
+
return ok3(await client2.getOntologySchema(schema_id));
|
|
1371
3235
|
case "query": {
|
|
1372
|
-
if (!query) return
|
|
3236
|
+
if (!query) return err3("query is required for action='query'");
|
|
1373
3237
|
if (!schema_id) {
|
|
1374
|
-
return
|
|
3238
|
+
return err3(
|
|
1375
3239
|
"schema_id is required for action='query' (use action='schemas' to find the active schema)"
|
|
1376
3240
|
);
|
|
1377
3241
|
}
|
|
1378
|
-
return
|
|
3242
|
+
return ok3(
|
|
1379
3243
|
await client2.queryOntology({
|
|
1380
3244
|
query,
|
|
1381
3245
|
schema_id,
|
|
@@ -1388,8 +3252,8 @@ async function handleOntology(client2, args) {
|
|
|
1388
3252
|
);
|
|
1389
3253
|
}
|
|
1390
3254
|
case "search":
|
|
1391
|
-
if (!query) return
|
|
1392
|
-
return
|
|
3255
|
+
if (!query) return err3("query is required for action='search'");
|
|
3256
|
+
return ok3(
|
|
1393
3257
|
await client2.searchDomainObjects(query, {
|
|
1394
3258
|
schema_id,
|
|
1395
3259
|
object_types,
|
|
@@ -1397,18 +3261,18 @@ async function handleOntology(client2, args) {
|
|
|
1397
3261
|
})
|
|
1398
3262
|
);
|
|
1399
3263
|
case "objects":
|
|
1400
|
-
if (!schema_id) return
|
|
1401
|
-
return
|
|
3264
|
+
if (!schema_id) return err3("schema_id is required for action='objects'");
|
|
3265
|
+
return ok3(
|
|
1402
3266
|
await client2.listDomainObjects({ schema_id, object_type, limit })
|
|
1403
3267
|
);
|
|
1404
3268
|
case "object":
|
|
1405
|
-
if (!uid) return
|
|
1406
|
-
return
|
|
3269
|
+
if (!uid) return err3("uid is required for action='object'");
|
|
3270
|
+
return ok3(await client2.getDomainObject(uid));
|
|
1407
3271
|
case "object_context":
|
|
1408
|
-
if (!uid) return
|
|
1409
|
-
return
|
|
3272
|
+
if (!uid) return err3("uid is required for action='object_context'");
|
|
3273
|
+
return ok3(await client2.getDomainObjectContext(uid, depth));
|
|
1410
3274
|
case "proposals":
|
|
1411
|
-
return
|
|
3275
|
+
return ok3(
|
|
1412
3276
|
await client2.listOntologyProposals({
|
|
1413
3277
|
status: status ?? "pending",
|
|
1414
3278
|
schema_id,
|
|
@@ -1417,21 +3281,21 @@ async function handleOntology(client2, args) {
|
|
|
1417
3281
|
})
|
|
1418
3282
|
);
|
|
1419
3283
|
case "proposal":
|
|
1420
|
-
if (!proposal_id) return
|
|
1421
|
-
return
|
|
3284
|
+
if (!proposal_id) return err3("proposal_id is required for action='proposal'");
|
|
3285
|
+
return ok3(await client2.getOntologyProposal(proposal_id));
|
|
1422
3286
|
case "approve":
|
|
1423
|
-
if (!proposal_id) return
|
|
1424
|
-
return
|
|
3287
|
+
if (!proposal_id) return err3("proposal_id is required for action='approve'");
|
|
3288
|
+
return ok3(await client2.approveOntologyProposal(proposal_id, { feedback }));
|
|
1425
3289
|
case "reject":
|
|
1426
|
-
if (!proposal_id) return
|
|
1427
|
-
return
|
|
3290
|
+
if (!proposal_id) return err3("proposal_id is required for action='reject'");
|
|
3291
|
+
return ok3(await client2.rejectOntologyProposal(proposal_id, reason));
|
|
1428
3292
|
case "link": {
|
|
1429
3293
|
if (!from_uid || !to_uid || !relation_type) {
|
|
1430
|
-
return
|
|
3294
|
+
return err3(
|
|
1431
3295
|
"from_uid, to_uid, and relation_type are required for action='link'"
|
|
1432
3296
|
);
|
|
1433
3297
|
}
|
|
1434
|
-
return
|
|
3298
|
+
return ok3(
|
|
1435
3299
|
await client2.linkDomainObjects({
|
|
1436
3300
|
from_uid,
|
|
1437
3301
|
to_uid,
|
|
@@ -1442,11 +3306,11 @@ async function handleOntology(client2, args) {
|
|
|
1442
3306
|
);
|
|
1443
3307
|
}
|
|
1444
3308
|
case "extract": {
|
|
1445
|
-
if (!schema_id) return
|
|
3309
|
+
if (!schema_id) return err3("schema_id is required for action='extract'");
|
|
1446
3310
|
if (!source_uids?.length) {
|
|
1447
|
-
return
|
|
3311
|
+
return err3("source_uids is required for action='extract'");
|
|
1448
3312
|
}
|
|
1449
|
-
return
|
|
3313
|
+
return ok3(
|
|
1450
3314
|
await client2.extractOntology({
|
|
1451
3315
|
ontology_schema_id: schema_id,
|
|
1452
3316
|
source_uids
|
|
@@ -1454,15 +3318,15 @@ async function handleOntology(client2, args) {
|
|
|
1454
3318
|
);
|
|
1455
3319
|
}
|
|
1456
3320
|
default:
|
|
1457
|
-
return
|
|
3321
|
+
return err3(`Unknown ontology action: ${action}`);
|
|
1458
3322
|
}
|
|
1459
3323
|
}
|
|
1460
3324
|
|
|
1461
3325
|
// src/generated-tools.ts
|
|
1462
|
-
var
|
|
3326
|
+
var ok4 = (data) => ({
|
|
1463
3327
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1464
3328
|
});
|
|
1465
|
-
var
|
|
3329
|
+
var err4 = (message) => ({
|
|
1466
3330
|
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
1467
3331
|
isError: true
|
|
1468
3332
|
});
|
|
@@ -1529,10 +3393,10 @@ async function handleGeneratedTool(client2, desc, args) {
|
|
|
1529
3393
|
switch (desc.maps_to) {
|
|
1530
3394
|
case "search": {
|
|
1531
3395
|
const query = args.query;
|
|
1532
|
-
if (!query) return
|
|
3396
|
+
if (!query) return err4(`query is required for ${desc.name}`);
|
|
1533
3397
|
const limit = typeof args.limit === "number" ? args.limit : void 0;
|
|
1534
3398
|
const filters = Array.isArray(args.filters) ? args.filters : void 0;
|
|
1535
|
-
return
|
|
3399
|
+
return ok4(
|
|
1536
3400
|
await ontologyClient.searchDomainObjects(query, {
|
|
1537
3401
|
schema_id: desc.schema_id,
|
|
1538
3402
|
object_types: [desc.object_type],
|
|
@@ -1543,33 +3407,33 @@ async function handleGeneratedTool(client2, desc, args) {
|
|
|
1543
3407
|
}
|
|
1544
3408
|
case "object": {
|
|
1545
3409
|
const uid = args.uid;
|
|
1546
|
-
if (!uid) return
|
|
1547
|
-
return
|
|
3410
|
+
if (!uid) return err4(`uid is required for ${desc.name}`);
|
|
3411
|
+
return ok4(await ontologyClient.getDomainObject(uid, {
|
|
1548
3412
|
schema_id: desc.schema_id,
|
|
1549
3413
|
object_type: desc.object_type
|
|
1550
3414
|
}));
|
|
1551
3415
|
}
|
|
1552
3416
|
case "object_context": {
|
|
1553
3417
|
const uid = args.uid;
|
|
1554
|
-
if (!uid) return
|
|
3418
|
+
if (!uid) return err4(`uid is required for ${desc.name}`);
|
|
1555
3419
|
const depth = typeof args.depth === "number" ? args.depth : void 0;
|
|
1556
|
-
return
|
|
3420
|
+
return ok4(await ontologyClient.getDomainObjectContext(uid, depth, {
|
|
1557
3421
|
schema_id: desc.schema_id,
|
|
1558
3422
|
object_type: desc.object_type
|
|
1559
3423
|
}));
|
|
1560
3424
|
}
|
|
1561
3425
|
case "related": {
|
|
1562
3426
|
const uid = args.uid;
|
|
1563
|
-
if (!uid) return
|
|
3427
|
+
if (!uid) return err4(`uid is required for ${desc.name}`);
|
|
1564
3428
|
if (!desc.relation || !desc.entry_role || !desc.far_type) {
|
|
1565
|
-
return
|
|
3429
|
+
return err4(`invalid related descriptor: ${desc.name}`);
|
|
1566
3430
|
}
|
|
1567
3431
|
if (!ontologyClient.queryRelatedDomainObjects) {
|
|
1568
|
-
return
|
|
3432
|
+
return err4(
|
|
1569
3433
|
"related ontology tools require a graph-aware version of the mindgraph SDK"
|
|
1570
3434
|
);
|
|
1571
3435
|
}
|
|
1572
|
-
return
|
|
3436
|
+
return ok4(await ontologyClient.queryRelatedDomainObjects({
|
|
1573
3437
|
schema_id: desc.schema_id,
|
|
1574
3438
|
entry_type: desc.object_type,
|
|
1575
3439
|
uid,
|
|
@@ -1584,9 +3448,9 @@ async function handleGeneratedTool(client2, desc, args) {
|
|
|
1584
3448
|
}
|
|
1585
3449
|
case "structured_query": {
|
|
1586
3450
|
const select = args.select;
|
|
1587
|
-
if (!select) return
|
|
3451
|
+
if (!select) return err4(`select is required for ${desc.name}`);
|
|
1588
3452
|
if (!ontologyClient.queryDomainStructured) {
|
|
1589
|
-
return
|
|
3453
|
+
return err4(
|
|
1590
3454
|
"structured ontology tools require a graph-aware version of the mindgraph SDK"
|
|
1591
3455
|
);
|
|
1592
3456
|
}
|
|
@@ -1597,13 +3461,13 @@ async function handleGeneratedTool(client2, desc, args) {
|
|
|
1597
3461
|
for (const field of STRUCTURED_QUERY_PASSTHROUGH) {
|
|
1598
3462
|
if (args[field] !== void 0) request[field] = args[field];
|
|
1599
3463
|
}
|
|
1600
|
-
return
|
|
3464
|
+
return ok4(await ontologyClient.queryDomainStructured(request));
|
|
1601
3465
|
}
|
|
1602
3466
|
default:
|
|
1603
|
-
return
|
|
3467
|
+
return err4(`unknown generated tool mapping: ${desc.maps_to}`);
|
|
1604
3468
|
}
|
|
1605
3469
|
} catch (e) {
|
|
1606
|
-
return
|
|
3470
|
+
return err4(e instanceof Error ? e.message : String(e));
|
|
1607
3471
|
}
|
|
1608
3472
|
}
|
|
1609
3473
|
|
|
@@ -1617,7 +3481,7 @@ function isTransientStatus(status) {
|
|
|
1617
3481
|
return status === 408 || status === 425 || status === 429 || status >= 500;
|
|
1618
3482
|
}
|
|
1619
3483
|
function sleep(ms) {
|
|
1620
|
-
return new Promise((
|
|
3484
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1621
3485
|
}
|
|
1622
3486
|
var KNOWN_DUTIES = /* @__PURE__ */ new Set(["log", "require_approval"]);
|
|
1623
3487
|
function headers(apiKey, orgId) {
|
|
@@ -1634,6 +3498,105 @@ function unsupportedResponse(status, body) {
|
|
|
1634
3498
|
const lower = body.toLowerCase();
|
|
1635
3499
|
return lower.includes("unknown governance action") || lower.includes("unknown action") || lower.includes("invalid_governance_action");
|
|
1636
3500
|
}
|
|
3501
|
+
var PLAN_READ_ACTIONS = /* @__PURE__ */ new Set([
|
|
3502
|
+
"get_plan",
|
|
3503
|
+
"get_executions",
|
|
3504
|
+
"get_pending",
|
|
3505
|
+
"get_assessments",
|
|
3506
|
+
"resume_work"
|
|
3507
|
+
]);
|
|
3508
|
+
var ONTOLOGY_READ_ACTIONS = /* @__PURE__ */ new Set([
|
|
3509
|
+
"list_schemas",
|
|
3510
|
+
"get_schema",
|
|
3511
|
+
"query",
|
|
3512
|
+
"search_objects",
|
|
3513
|
+
"list_objects",
|
|
3514
|
+
"get_object",
|
|
3515
|
+
"get_context",
|
|
3516
|
+
"list_proposals",
|
|
3517
|
+
"get_proposal"
|
|
3518
|
+
]);
|
|
3519
|
+
function toolMutability(toolName, action) {
|
|
3520
|
+
if (toolName === "mindgraph_retrieve") return "read";
|
|
3521
|
+
if (toolName === "mindgraph_plan") {
|
|
3522
|
+
return action && PLAN_READ_ACTIONS.has(action) ? "read" : "write";
|
|
3523
|
+
}
|
|
3524
|
+
if (toolName === "mindgraph_ingest") {
|
|
3525
|
+
return action === "job_status" ? "read" : "write";
|
|
3526
|
+
}
|
|
3527
|
+
if (toolName === "mindgraph_synthesize") {
|
|
3528
|
+
return action === "signals" ? "read" : "write";
|
|
3529
|
+
}
|
|
3530
|
+
if (toolName === "mindgraph_code") {
|
|
3531
|
+
return action === "anchor" ? "write" : "read";
|
|
3532
|
+
}
|
|
3533
|
+
if (toolName === "mindgraph_sync") {
|
|
3534
|
+
return action === "scan" || action === "status" ? "read" : "write";
|
|
3535
|
+
}
|
|
3536
|
+
if (toolName === "mindgraph_ontology") {
|
|
3537
|
+
return action && ONTOLOGY_READ_ACTIONS.has(action) ? "read" : "write";
|
|
3538
|
+
}
|
|
3539
|
+
if (/_(search|get|context|related|query)$/.test(toolName) && toolName !== "mindgraph_capture") {
|
|
3540
|
+
return "read";
|
|
3541
|
+
}
|
|
3542
|
+
return "write";
|
|
3543
|
+
}
|
|
3544
|
+
var UID_FIELDS = [
|
|
3545
|
+
"uid",
|
|
3546
|
+
"source_uid",
|
|
3547
|
+
"session_uid",
|
|
3548
|
+
"work_uid",
|
|
3549
|
+
"goal_uid",
|
|
3550
|
+
"project_uid",
|
|
3551
|
+
"task_uid",
|
|
3552
|
+
"plan_uid",
|
|
3553
|
+
"step_uid",
|
|
3554
|
+
"target_uid",
|
|
3555
|
+
"execution_uid",
|
|
3556
|
+
"executor_uid",
|
|
3557
|
+
"affordance_uid",
|
|
3558
|
+
"produces_node_uid",
|
|
3559
|
+
"filter_plan_uid",
|
|
3560
|
+
"parent_uid",
|
|
3561
|
+
"decision_uid",
|
|
3562
|
+
"chosen_option_uid",
|
|
3563
|
+
"governed_uid",
|
|
3564
|
+
"approval_uid",
|
|
3565
|
+
"requires_plan_uid",
|
|
3566
|
+
"start_uid",
|
|
3567
|
+
"end_uid"
|
|
3568
|
+
];
|
|
3569
|
+
var UID_ARRAY_FIELDS = [
|
|
3570
|
+
"related_uids",
|
|
3571
|
+
"depends_on_uids",
|
|
3572
|
+
"summarizes_uids",
|
|
3573
|
+
"informs_uid",
|
|
3574
|
+
"target_uids",
|
|
3575
|
+
"anchor_uids",
|
|
3576
|
+
"scope_uids",
|
|
3577
|
+
"produces_node_uids"
|
|
3578
|
+
];
|
|
3579
|
+
function governanceTarget(toolName, args) {
|
|
3580
|
+
const action = typeof args.action === "string" ? args.action : void 0;
|
|
3581
|
+
const targetUids = /* @__PURE__ */ new Set();
|
|
3582
|
+
for (const field of UID_FIELDS) {
|
|
3583
|
+
const value = args[field];
|
|
3584
|
+
if (typeof value === "string" && value.length > 0) targetUids.add(value);
|
|
3585
|
+
}
|
|
3586
|
+
for (const field of UID_ARRAY_FIELDS) {
|
|
3587
|
+
const value = args[field];
|
|
3588
|
+
if (!Array.isArray(value)) continue;
|
|
3589
|
+
for (const uid of value) {
|
|
3590
|
+
if (typeof uid === "string" && uid.length > 0) targetUids.add(uid);
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
return {
|
|
3594
|
+
tool_name: toolName,
|
|
3595
|
+
...action ? { action } : {},
|
|
3596
|
+
mutability: toolMutability(toolName, action),
|
|
3597
|
+
target_uids: [...targetUids]
|
|
3598
|
+
};
|
|
3599
|
+
}
|
|
1637
3600
|
async function checkMcpGovernance(toolName, args, config) {
|
|
1638
3601
|
if (config.disabled) return { allowed: true };
|
|
1639
3602
|
if (capability === "unsupported") {
|
|
@@ -1656,7 +3619,7 @@ async function checkMcpGovernance(toolName, args, config) {
|
|
|
1656
3619
|
action: "check",
|
|
1657
3620
|
act: "tool_invoke",
|
|
1658
3621
|
agent_id: String(args.agent_id || config.agentId),
|
|
1659
|
-
target:
|
|
3622
|
+
target: governanceTarget(toolName, args),
|
|
1660
3623
|
context: { adapter: "mindgraph-mcp", ...config.context },
|
|
1661
3624
|
tier: "checkpoint"
|
|
1662
3625
|
})
|
|
@@ -1853,13 +3816,15 @@ var API_KEY = process.env.MINDGRAPH_API_KEY;
|
|
|
1853
3816
|
var BASE_URL = process.env.MINDGRAPH_BASE_URL || "https://api.mindgraph.cloud";
|
|
1854
3817
|
var AGENT_ID = process.env.MINDGRAPH_AGENT_ID || "mcp";
|
|
1855
3818
|
var ORG_ID = process.env.MINDGRAPH_ORG_ID;
|
|
3819
|
+
var PROFILE = process.env.MINDGRAPH_PROFILE || "general";
|
|
3820
|
+
var HARNESS = process.env.MINDGRAPH_HARNESS || "generic";
|
|
1856
3821
|
if (!API_KEY) {
|
|
1857
3822
|
console.error(
|
|
1858
3823
|
"Error: MINDGRAPH_API_KEY environment variable is required.\nGet your API key at https://mindgraph.cloud/dashboard/keys\nOr run: npx mindgraph-mcp init"
|
|
1859
3824
|
);
|
|
1860
3825
|
process.exit(1);
|
|
1861
3826
|
}
|
|
1862
|
-
var client = new
|
|
3827
|
+
var client = new import_mindgraph2.MindGraph({
|
|
1863
3828
|
baseUrl: BASE_URL,
|
|
1864
3829
|
apiKey: API_KEY,
|
|
1865
3830
|
orgId: ORG_ID,
|
|
@@ -1868,7 +3833,7 @@ var client = new import_mindgraph.MindGraph({
|
|
|
1868
3833
|
// window where npm may still resolve the preceding SDK declaration file.
|
|
1869
3834
|
telemetrySurface: "mcp"
|
|
1870
3835
|
});
|
|
1871
|
-
var INSTRUCTIONS = `You have access to a persistent knowledge graph via MindGraph
|
|
3836
|
+
var INSTRUCTIONS = `You have access to a persistent knowledge graph via MindGraph, plus any ontology tools generated by the active schema.
|
|
1872
3837
|
|
|
1873
3838
|
## #1 RULE: Go straight to the user's topic
|
|
1874
3839
|
|
|
@@ -1931,22 +3896,42 @@ Capture knowledge when the user shares something worth remembering:
|
|
|
1931
3896
|
- Questions to track \u2192 \`mindgraph_reason\` action "open_question"
|
|
1932
3897
|
- Goals or projects \u2192 \`mindgraph_commit\` action "goal"/"project"
|
|
1933
3898
|
- Decisions \u2192 \`mindgraph_commit\` action "open_decision"
|
|
3899
|
+
- Durable engineering work that must survive sessions/harnesses \u2192 \`mindgraph_plan\`
|
|
3900
|
+
- Typed code anchors, attached memory/work, callers/callees, or impact \u2192 \`mindgraph_code\`
|
|
3901
|
+
- Reusable learning from a material attempt \u2192 \`mindgraph_capture\` action "lesson"
|
|
1934
3902
|
- Long-form content \u2192 \`mindgraph_ingest\`
|
|
1935
3903
|
- 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)
|
|
1936
3904
|
|
|
1937
3905
|
## Tool tips
|
|
1938
3906
|
|
|
3907
|
+
- 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.
|
|
3908
|
+
- 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.
|
|
1939
3909
|
- \`capture\`: "entity"/"observation" for facts, "journal" for personal/informal
|
|
1940
3910
|
- \`reason\`: "claim" when evidence exists, "open_question"/"hypothesis" otherwise
|
|
1941
3911
|
- \`retrieve\`: "context" returns graph nodes only by default \u2014 set include_chunks=true for source text
|
|
1942
3912
|
- \`retrieve\`: "context" (FTS default) \u2192 "semantic" (conceptual fallback) \u2192 "hybrid" (both)
|
|
1943
3913
|
- \`retrieve\`: "neighborhood"/"chain"/"path"/"subgraph" for graph exploration
|
|
3914
|
+
- \`code\`: pass path + symbol + signature when names are overloaded; ambiguity never writes
|
|
1944
3915
|
- Confidence: 0.9+ for facts, 0.5-0.8 for uncertain, <0.5 for speculation
|
|
1945
3916
|
- Don't narrate tool usage \u2014 capture naturally as part of conversation`;
|
|
3917
|
+
if (PROFILE === "coding") {
|
|
3918
|
+
INSTRUCTIONS += `
|
|
3919
|
+
|
|
3920
|
+
## Coding profile
|
|
3921
|
+
|
|
3922
|
+
- 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.
|
|
3923
|
+
- Capture corrections, decisions with rationale, fixes that survived meaningful failed attempts, durable invariants/preferences, and blockers with evidence.
|
|
3924
|
+
- Do not capture routine mechanics, transient command output, facts cheaply derivable from code, unsupported speculation as high-confidence lessons, or completion without evidence.
|
|
3925
|
+
- Adapter context is owned by ${HARNESS}; omit invocation_context because hooks replace it.`;
|
|
3926
|
+
} else {
|
|
3927
|
+
INSTRUCTIONS = INSTRUCTIONS.split("\n").filter(
|
|
3928
|
+
(line) => !line.includes("mindgraph_code") && !line.includes("`code`") && !line.includes("Typed code anchors")
|
|
3929
|
+
).join("\n");
|
|
3930
|
+
}
|
|
1946
3931
|
var server = new import_server.Server(
|
|
1947
3932
|
{
|
|
1948
3933
|
name: "mindgraph",
|
|
1949
|
-
version: "0.
|
|
3934
|
+
version: "0.14.1"
|
|
1950
3935
|
},
|
|
1951
3936
|
{
|
|
1952
3937
|
capabilities: {
|
|
@@ -1959,7 +3944,7 @@ var server = new import_server.Server(
|
|
|
1959
3944
|
);
|
|
1960
3945
|
server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
|
|
1961
3946
|
const generated = await getGeneratedTools(client);
|
|
1962
|
-
return { tools: [...
|
|
3947
|
+
return { tools: [...toolsForProfile(PROFILE), ...generated.tools] };
|
|
1963
3948
|
});
|
|
1964
3949
|
var GOVERNANCE_CONFIG = {
|
|
1965
3950
|
baseUrl: BASE_URL,
|