mindgraph-mcp 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/dist/cli.js +2027 -1808
- package/dist/index.js +1743 -1515
- package/dist/server.d.mts +19 -3
- package/dist/server.d.ts +19 -3
- package/dist/server.js +1734 -1611
- package/dist/server.mjs +1726 -1605
- package/package.json +7 -3
package/dist/cli.js
CHANGED
|
@@ -35,14 +35,14 @@ function errorFields(body) {
|
|
|
35
35
|
...typeof fields.retriable === "boolean" ? { retriable: fields.retriable } : {}
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
-
function canRetryRequest(method,
|
|
38
|
+
function canRetryRequest(method, path10, body) {
|
|
39
39
|
if (method === "GET" || method === "HEAD") return true;
|
|
40
40
|
if (method !== "POST") return false;
|
|
41
|
-
if (READ_POSTS.has(
|
|
41
|
+
if (READ_POSTS.has(path10)) return true;
|
|
42
42
|
const fields = body && typeof body === "object" && !Array.isArray(body) ? body : {};
|
|
43
43
|
if (typeof fields.action !== "string") return false;
|
|
44
|
-
if (Object.prototype.hasOwnProperty.call(READ_ACTIONS,
|
|
45
|
-
return
|
|
44
|
+
if (Object.prototype.hasOwnProperty.call(READ_ACTIONS, path10) && READ_ACTIONS[path10].includes(fields.action)) return true;
|
|
45
|
+
return path10 === "/agent/plan" && IDEMPOTENT_WORK.has(fields.action) && typeof fields.idempotency_key === "string" && fields.idempotency_key.trim().length > 0;
|
|
46
46
|
}
|
|
47
47
|
function permitsRetry(fields) {
|
|
48
48
|
return fields.retriable !== false && ![
|
|
@@ -179,8 +179,8 @@ var init_dist = __esm({
|
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
// ---- HTTP helpers ----
|
|
182
|
-
async request(method,
|
|
183
|
-
const url = `${this.baseUrl}${
|
|
182
|
+
async request(method, path10, body) {
|
|
183
|
+
const url = `${this.baseUrl}${path10}`;
|
|
184
184
|
const requestId = createRequestId();
|
|
185
185
|
const init = {
|
|
186
186
|
method,
|
|
@@ -195,12 +195,12 @@ var init_dist = __esm({
|
|
|
195
195
|
}
|
|
196
196
|
const retrySafe = canRetryRequest(
|
|
197
197
|
method,
|
|
198
|
-
|
|
198
|
+
path10,
|
|
199
199
|
typeof init.body === "string" ? JSON.parse(init.body) : void 0
|
|
200
200
|
);
|
|
201
201
|
let lastError;
|
|
202
202
|
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
203
|
-
const res = await this.fetchWithDeadline(url, init, method,
|
|
203
|
+
const res = await this.fetchWithDeadline(url, init, method, path10);
|
|
204
204
|
if (!res.ok) {
|
|
205
205
|
const text22 = await res.text().catch(() => "");
|
|
206
206
|
let parsed;
|
|
@@ -210,7 +210,7 @@ var init_dist = __esm({
|
|
|
210
210
|
parsed = text22;
|
|
211
211
|
}
|
|
212
212
|
const err6 = new MindGraphError(
|
|
213
|
-
`${method} ${
|
|
213
|
+
`${method} ${path10} failed: ${res.status}`,
|
|
214
214
|
res.status,
|
|
215
215
|
parsed
|
|
216
216
|
);
|
|
@@ -234,7 +234,7 @@ var init_dist = __esm({
|
|
|
234
234
|
* "timeout"`, `status: 0`). Without it the request is sent unchanged — no
|
|
235
235
|
* AbortSignal is attached, preserving the pre-0.16 behaviour.
|
|
236
236
|
*/
|
|
237
|
-
async fetchWithDeadline(url, init, method,
|
|
237
|
+
async fetchWithDeadline(url, init, method, path10) {
|
|
238
238
|
if (this.timeoutMs === void 0) {
|
|
239
239
|
return fetch(url, init);
|
|
240
240
|
}
|
|
@@ -245,7 +245,7 @@ var init_dist = __esm({
|
|
|
245
245
|
} catch (error2) {
|
|
246
246
|
if (controller.signal.aborted) {
|
|
247
247
|
throw new MindGraphError(
|
|
248
|
-
`${method} ${
|
|
248
|
+
`${method} ${path10} timed out after ${this.timeoutMs}ms`,
|
|
249
249
|
0,
|
|
250
250
|
{ error: "timeout", code: "timeout", retriable: false }
|
|
251
251
|
);
|
|
@@ -255,17 +255,17 @@ var init_dist = __esm({
|
|
|
255
255
|
clearTimeout(timer);
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
|
-
get(
|
|
259
|
-
return this.request("GET",
|
|
258
|
+
get(path10) {
|
|
259
|
+
return this.request("GET", path10);
|
|
260
260
|
}
|
|
261
|
-
post(
|
|
262
|
-
return this.request("POST",
|
|
261
|
+
post(path10, body) {
|
|
262
|
+
return this.request("POST", path10, body);
|
|
263
263
|
}
|
|
264
|
-
patch(
|
|
265
|
-
return this.request("PATCH",
|
|
264
|
+
patch(path10, body) {
|
|
265
|
+
return this.request("PATCH", path10, body);
|
|
266
266
|
}
|
|
267
|
-
del(
|
|
268
|
-
return this.request("DELETE",
|
|
267
|
+
del(path10) {
|
|
268
|
+
return this.request("DELETE", path10);
|
|
269
269
|
}
|
|
270
270
|
// ---- Health ----
|
|
271
271
|
async health() {
|
|
@@ -1240,14 +1240,14 @@ function unavailabilityCaveats(reason, message) {
|
|
|
1240
1240
|
}
|
|
1241
1241
|
return caveats;
|
|
1242
1242
|
}
|
|
1243
|
-
function normalizePath(
|
|
1244
|
-
return
|
|
1243
|
+
function normalizePath(path10) {
|
|
1244
|
+
return path10.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
1245
1245
|
}
|
|
1246
1246
|
function normalizeSignature(signature) {
|
|
1247
1247
|
return signature.replace(/\s+/g, " ").trim();
|
|
1248
1248
|
}
|
|
1249
|
-
function languageFromPath(
|
|
1250
|
-
const extension = (0, import_node_path2.extname)(
|
|
1249
|
+
function languageFromPath(path10) {
|
|
1250
|
+
const extension = (0, import_node_path2.extname)(path10).toLowerCase();
|
|
1251
1251
|
return {
|
|
1252
1252
|
".c": "c",
|
|
1253
1253
|
".cc": "cpp",
|
|
@@ -1313,6 +1313,15 @@ function nearestExistingDirectory(candidate) {
|
|
|
1313
1313
|
current = parent;
|
|
1314
1314
|
}
|
|
1315
1315
|
}
|
|
1316
|
+
function findChildWorkspaceFiles(start) {
|
|
1317
|
+
let entries;
|
|
1318
|
+
try {
|
|
1319
|
+
entries = (0, import_node_fs2.readdirSync)(start, { withFileTypes: true });
|
|
1320
|
+
} catch {
|
|
1321
|
+
return [];
|
|
1322
|
+
}
|
|
1323
|
+
return entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).slice(0, MAX_CHILD_DIRECTORIES).map((entry) => (0, import_node_path2.join)(start, entry.name, ".mindgraph", "workspace.json")).filter((candidate) => (0, import_node_fs2.existsSync)(candidate)).sort();
|
|
1324
|
+
}
|
|
1316
1325
|
function findWorkspaceFile(start, explicit) {
|
|
1317
1326
|
if (explicit) return (0, import_node_path2.resolve)(start, explicit);
|
|
1318
1327
|
let current = (0, import_node_path2.resolve)(start);
|
|
@@ -1408,7 +1417,7 @@ function codeRefIdentityKey(ref) {
|
|
|
1408
1417
|
}
|
|
1409
1418
|
};
|
|
1410
1419
|
}
|
|
1411
|
-
var import_node_child_process, import_node_fs2, import_promises, import_node_os2, import_node_path2, import_node_crypto3, UnknownRepositoryError, DEFAULT_TIMEOUT_MS, DEFAULT_STATUS_TTL_MS, MAX_OUTPUT_BYTES, CodegraphAdapter;
|
|
1420
|
+
var import_node_child_process, import_node_fs2, import_promises, import_node_os2, import_node_path2, import_node_crypto3, UnknownRepositoryError, DEFAULT_TIMEOUT_MS, DEFAULT_STATUS_TTL_MS, MAX_OUTPUT_BYTES, MAX_CHILD_DIRECTORIES, CodegraphAdapter;
|
|
1412
1421
|
var init_codegraph = __esm({
|
|
1413
1422
|
"src/codegraph.ts"() {
|
|
1414
1423
|
"use strict";
|
|
@@ -1431,12 +1440,14 @@ var init_codegraph = __esm({
|
|
|
1431
1440
|
DEFAULT_TIMEOUT_MS = 5e3;
|
|
1432
1441
|
DEFAULT_STATUS_TTL_MS = 6e4;
|
|
1433
1442
|
MAX_OUTPUT_BYTES = 5 * 1024 * 1024;
|
|
1443
|
+
MAX_CHILD_DIRECTORIES = 200;
|
|
1434
1444
|
CodegraphAdapter = class {
|
|
1435
1445
|
timeoutMs;
|
|
1436
1446
|
statusTtlMs;
|
|
1437
1447
|
env;
|
|
1438
1448
|
cwd;
|
|
1439
1449
|
workspaceFile;
|
|
1450
|
+
childWorkspaceFiles;
|
|
1440
1451
|
now;
|
|
1441
1452
|
identityCache = /* @__PURE__ */ new Map();
|
|
1442
1453
|
statusCache = /* @__PURE__ */ new Map();
|
|
@@ -1449,6 +1460,7 @@ var init_codegraph = __esm({
|
|
|
1449
1460
|
this.cwd,
|
|
1450
1461
|
options.workspaceFile ?? this.env.MINDGRAPH_WORKSPACE_FILE
|
|
1451
1462
|
);
|
|
1463
|
+
this.childWorkspaceFiles = !this.workspaceFile && options.childWorkspaces ? findChildWorkspaceFiles(this.cwd) : [];
|
|
1452
1464
|
this.now = options.now ?? Date.now;
|
|
1453
1465
|
}
|
|
1454
1466
|
async resolveRepository(repo, fallbackSpaceUid) {
|
|
@@ -1772,13 +1784,15 @@ var init_codegraph = __esm({
|
|
|
1772
1784
|
}
|
|
1773
1785
|
configuredRepositories() {
|
|
1774
1786
|
const configured = [];
|
|
1775
|
-
|
|
1787
|
+
const workspaceFiles = this.workspaceFile ? [this.workspaceFile] : this.childWorkspaceFiles;
|
|
1788
|
+
for (const workspaceFile of workspaceFiles) {
|
|
1789
|
+
if (!(0, import_node_fs2.existsSync)(workspaceFile)) continue;
|
|
1776
1790
|
try {
|
|
1777
1791
|
const parsed = JSON.parse(
|
|
1778
|
-
(0, import_node_fs2.readFileSync)(
|
|
1792
|
+
(0, import_node_fs2.readFileSync)(workspaceFile, "utf8")
|
|
1779
1793
|
);
|
|
1780
1794
|
if (parsed.v === 1 && Array.isArray(parsed.repositories)) {
|
|
1781
|
-
const configDirectory = (0, import_node_path2.dirname)(
|
|
1795
|
+
const configDirectory = (0, import_node_path2.dirname)(workspaceFile);
|
|
1782
1796
|
const base = (0, import_node_path2.basename)(configDirectory) === ".mindgraph" ? (0, import_node_path2.dirname)(configDirectory) : configDirectory;
|
|
1783
1797
|
for (const repository of parsed.repositories) {
|
|
1784
1798
|
if (!repository || typeof repository.root !== "string") continue;
|
|
@@ -1959,1735 +1973,1926 @@ var init_codegraph = __esm({
|
|
|
1959
1973
|
}
|
|
1960
1974
|
});
|
|
1961
1975
|
|
|
1962
|
-
// src/
|
|
1963
|
-
function
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1976
|
+
// src/render.ts
|
|
1977
|
+
function obj(value) {
|
|
1978
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1979
|
+
}
|
|
1980
|
+
function str(value) {
|
|
1981
|
+
return typeof value === "string" && value ? value : void 0;
|
|
1982
|
+
}
|
|
1983
|
+
function num(value) {
|
|
1984
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1985
|
+
}
|
|
1986
|
+
function edgeTypeName(value) {
|
|
1987
|
+
return str(value) ?? str(obj(value)?.Custom);
|
|
1988
|
+
}
|
|
1989
|
+
function renderBudget(requested) {
|
|
1990
|
+
if (requested === void 0 || !Number.isFinite(requested)) return RENDER_CHAR_BUDGET;
|
|
1991
|
+
return Math.min(
|
|
1992
|
+
RENDER_CHAR_BUDGET,
|
|
1993
|
+
Math.max(MIN_RENDER_CHAR_BUDGET, Math.floor(requested))
|
|
1994
|
+
);
|
|
1995
|
+
}
|
|
1996
|
+
function clip(value, max, state) {
|
|
1997
|
+
const text2 = str(value);
|
|
1998
|
+
if (!text2) return void 0;
|
|
1999
|
+
if (text2.length <= max) return text2;
|
|
2000
|
+
state.bounded = true;
|
|
2001
|
+
return `${text2.slice(0, max - 1)}\u2026`;
|
|
2002
|
+
}
|
|
2003
|
+
function isNodeShaped(value) {
|
|
2004
|
+
const node = obj(value);
|
|
2005
|
+
return Boolean(
|
|
2006
|
+
node && str(node.uid) && (str(node.label) || str(node.node_type) || obj(node.node_type))
|
|
2007
|
+
);
|
|
2008
|
+
}
|
|
2009
|
+
function nodeDisplayType(node) {
|
|
2010
|
+
const props = obj(node.props);
|
|
2011
|
+
const plain = str(node.node_type);
|
|
2012
|
+
return (plain && plain !== "Custom" ? plain : void 0) ?? str(obj(node.node_type)?.Custom) ?? str(props?.object_type) ?? str(props?.type_name) ?? str(obj(props?.data)?.domain_type) ?? plain ?? "Node";
|
|
2013
|
+
}
|
|
2014
|
+
function nodeFields(node) {
|
|
2015
|
+
const props = obj(node.props);
|
|
2016
|
+
return obj(props?.fields) ?? obj(obj(props?.data)?.fields);
|
|
2017
|
+
}
|
|
2018
|
+
function unwrapSearchResult(value) {
|
|
2019
|
+
if (isNodeShaped(value)) return value;
|
|
2020
|
+
const wrapper = obj(value);
|
|
2021
|
+
const inner = obj(wrapper?.node) ?? obj(wrapper?.object);
|
|
2022
|
+
if (inner && isNodeShaped(inner)) {
|
|
2023
|
+
const score = num(wrapper.score);
|
|
2024
|
+
return score !== void 0 ? { ...inner, retrieval_score: score } : inner;
|
|
1967
2025
|
}
|
|
1968
|
-
return
|
|
2026
|
+
return void 0;
|
|
1969
2027
|
}
|
|
1970
|
-
function
|
|
1971
|
-
|
|
1972
|
-
|
|
2028
|
+
function supersededBy(node) {
|
|
2029
|
+
const marker = node.superseded;
|
|
2030
|
+
if (!marker) return void 0;
|
|
2031
|
+
const head = obj(marker);
|
|
2032
|
+
if (!head) return str(node.superseded_by) ?? "";
|
|
2033
|
+
const label = (str(head.head_label) ?? str(head.head_uid) ?? "").replace(/\s+/g, " ").trim();
|
|
2034
|
+
const short = label.length <= 80 ? label : `${label.slice(0, 79)}\u2026`;
|
|
2035
|
+
const date = str(head.head_stated_at);
|
|
2036
|
+
return short && date ? `${short} (${date.slice(0, 10)})` : short;
|
|
2037
|
+
}
|
|
2038
|
+
function statusTag(node) {
|
|
2039
|
+
const tags = [];
|
|
2040
|
+
const replacement = supersededBy(node);
|
|
2041
|
+
if (replacement !== void 0) {
|
|
2042
|
+
tags.push(replacement ? `SUPERSEDED by ${replacement}` : "SUPERSEDED");
|
|
1973
2043
|
}
|
|
1974
|
-
const
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
2044
|
+
const truth = str(node.truth_status) ?? str(obj(node.props)?.truth_status);
|
|
2045
|
+
if (truth && truth !== "accepted") tags.push(`truth: ${truth}`);
|
|
2046
|
+
if (node.currently_valid === false) tags.push("OUTSIDE ITS VALIDITY WINDOW");
|
|
2047
|
+
if (node.valid_at_time === false) tags.push("NOT VALID AT THE REQUESTED TIME");
|
|
2048
|
+
return tags.length > 0 ? ` [${tags.join("; ")}]` : "";
|
|
2049
|
+
}
|
|
2050
|
+
function firstQuote(node, state) {
|
|
2051
|
+
const chunks = Array.isArray(node.source_chunks) ? node.source_chunks : [];
|
|
2052
|
+
for (const raw of chunks) {
|
|
2053
|
+
const chunk = obj(raw);
|
|
2054
|
+
const quote = str(chunk?.quote) ?? str(obj(chunk?.anchor)?.exact);
|
|
2055
|
+
if (quote) return clip(quote, QUOTE_MAX, state);
|
|
1978
2056
|
}
|
|
1979
|
-
return
|
|
2057
|
+
return void 0;
|
|
1980
2058
|
}
|
|
1981
|
-
function
|
|
1982
|
-
const
|
|
1983
|
-
const
|
|
1984
|
-
const
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
...code !== void 0 ? { code } : {},
|
|
1989
|
-
...typeof body.retriable === "boolean" ? { retriable: body.retriable } : {}
|
|
1990
|
-
};
|
|
1991
|
-
return {
|
|
1992
|
-
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
1993
|
-
structuredContent: payload,
|
|
1994
|
-
isError: true
|
|
1995
|
-
};
|
|
2059
|
+
function sourceTag(node) {
|
|
2060
|
+
const docs = Array.isArray(node.source_documents) ? node.source_documents : [];
|
|
2061
|
+
const first = obj(docs[0]);
|
|
2062
|
+
const title = str(first?.title);
|
|
2063
|
+
if (!title) return void 0;
|
|
2064
|
+
const extra = docs.length > 1 ? ` (+${docs.length - 1} more)` : "";
|
|
2065
|
+
return `from: ${title}${extra}`;
|
|
1996
2066
|
}
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
const
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
const
|
|
2019
|
-
const
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
};
|
|
2023
|
-
const
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
}
|
|
2036
|
-
const missingSpace = resolved.find(
|
|
2037
|
-
({ repository }) => typeof args.repo_space_uid !== "string" && !repository.spaceUid
|
|
2038
|
-
);
|
|
2039
|
-
if (missingSpace) {
|
|
2040
|
-
return err(
|
|
2041
|
-
"missing_repository_space",
|
|
2042
|
-
`repo_space_uid, a workspace space_uid, or MINDGRAPH_CODE_SPACE_UID is required for ${missingSpace.repository.repoId}`
|
|
2043
|
-
);
|
|
2044
|
-
}
|
|
2045
|
-
const repositoryAnchors = /* @__PURE__ */ new Map();
|
|
2046
|
-
for (const { repository } of resolved) {
|
|
2047
|
-
if (repositoryAnchors.has(repository.repoRoot)) continue;
|
|
2048
|
-
const spaceUid = typeof args.repo_space_uid === "string" ? args.repo_space_uid : repository.spaceUid;
|
|
2049
|
-
const anchored = await anchorRepository(
|
|
2050
|
-
codeClient,
|
|
2051
|
-
repository,
|
|
2052
|
-
spaceUid,
|
|
2053
|
-
agentId
|
|
2054
|
-
);
|
|
2055
|
-
repositoryAnchors.set(repository.repoRoot, {
|
|
2056
|
-
...anchored,
|
|
2057
|
-
repository
|
|
2058
|
-
});
|
|
2059
|
-
}
|
|
2060
|
-
const anchors = [];
|
|
2061
|
-
for (const { ref, repository } of resolved) {
|
|
2062
|
-
const repositoryAnchor = repositoryAnchors.get(repository.repoRoot);
|
|
2063
|
-
if (!repositoryAnchor.uid) continue;
|
|
2064
|
-
const spaceUid = typeof args.repo_space_uid === "string" ? args.repo_space_uid : repository.spaceUid;
|
|
2065
|
-
const anchor = await anchorCodeRef(
|
|
2066
|
-
codeClient,
|
|
2067
|
-
ref,
|
|
2068
|
-
repositoryAnchor.uid,
|
|
2069
|
-
spaceUid,
|
|
2070
|
-
agentId
|
|
2071
|
-
);
|
|
2072
|
-
anchors.push(anchor);
|
|
2073
|
-
}
|
|
2074
|
-
return ok({
|
|
2075
|
-
availability: resolution.availability,
|
|
2076
|
-
repository: repositoryAnchors.values().next().value,
|
|
2077
|
-
repositories: [...repositoryAnchors.values()],
|
|
2078
|
-
anchors,
|
|
2079
|
-
unresolved,
|
|
2080
|
-
wrote: anchors.some((anchor) => anchor.created)
|
|
2081
|
-
});
|
|
2082
|
-
}
|
|
2083
|
-
case "recall": {
|
|
2084
|
-
const materialized = new Set(anchorUids);
|
|
2085
|
-
let resolution;
|
|
2086
|
-
if (refs.length > 0) {
|
|
2087
|
-
resolution = await adapter.resolveRefs(refs, options);
|
|
2088
|
-
for (const result of resolution.results) {
|
|
2089
|
-
if (!result.resolved) continue;
|
|
2090
|
-
const uid = await resolveMaterializedUid(codeClient, result.resolved, agentId);
|
|
2091
|
-
if (uid) materialized.add(uid);
|
|
2092
|
-
}
|
|
2093
|
-
}
|
|
2094
|
-
if (materialized.size === 0) {
|
|
2095
|
-
return ok({
|
|
2096
|
-
availability: resolution?.availability,
|
|
2097
|
-
anchors: [],
|
|
2098
|
-
context: [],
|
|
2099
|
-
unresolved: resolution?.results.filter((result) => !result.resolved) ?? []
|
|
2100
|
-
});
|
|
2101
|
-
}
|
|
2102
|
-
const context = await Promise.all(
|
|
2103
|
-
[...materialized].map(async (uid) => ({
|
|
2104
|
-
anchor_uid: uid,
|
|
2105
|
-
graph: await codeClient.traverse({
|
|
2106
|
-
action: "neighborhood",
|
|
2107
|
-
start_uid: uid,
|
|
2108
|
-
depth: 2,
|
|
2109
|
-
max_nodes: Math.min(limit ?? 20, 20)
|
|
2110
|
-
})
|
|
2111
|
-
}))
|
|
2112
|
-
);
|
|
2113
|
-
return ok({
|
|
2114
|
-
availability: resolution?.availability,
|
|
2115
|
-
anchors: [...materialized],
|
|
2116
|
-
context,
|
|
2117
|
-
unresolved: resolution?.results.filter((result) => !result.resolved) ?? []
|
|
2118
|
-
});
|
|
2119
|
-
}
|
|
2120
|
-
case "expand": {
|
|
2121
|
-
const target = await resolveSingleTarget(
|
|
2122
|
-
codeClient,
|
|
2123
|
-
adapter,
|
|
2124
|
-
refs,
|
|
2125
|
-
anchorUids,
|
|
2126
|
-
options
|
|
2127
|
-
);
|
|
2128
|
-
if ("error" in target) return target.error;
|
|
2129
|
-
const expanded = await adapter.expand(target.ref, limit);
|
|
2130
|
-
const joined = await joinMaterialized(
|
|
2131
|
-
codeClient,
|
|
2132
|
-
[...expanded.callers, ...expanded.callees],
|
|
2133
|
-
agentId,
|
|
2134
|
-
Math.min(limit ?? 20, 20)
|
|
2135
|
-
);
|
|
2136
|
-
return ok({ anchor: target.ref, ...expanded, materialized: joined });
|
|
2137
|
-
}
|
|
2138
|
-
case "affected": {
|
|
2139
|
-
const targetUid = typeof args.task_uid === "string" ? args.task_uid : typeof args.execution_uid === "string" ? args.execution_uid : void 0;
|
|
2140
|
-
let graphContext;
|
|
2141
|
-
const graphRefs = [];
|
|
2142
|
-
let unavailableRepositories = [];
|
|
2143
|
-
if (targetUid) {
|
|
2144
|
-
graphContext = await codeClient.traverse({
|
|
2145
|
-
action: "neighborhood",
|
|
2146
|
-
start_uid: targetUid,
|
|
2147
|
-
depth: 2,
|
|
2148
|
-
max_nodes: 50
|
|
2149
|
-
});
|
|
2150
|
-
const fromGraph = await codeRefsFromGraphContext(
|
|
2151
|
-
codeClient,
|
|
2152
|
-
adapter,
|
|
2153
|
-
graphContext
|
|
2154
|
-
);
|
|
2155
|
-
graphRefs.push(...fromGraph.refs);
|
|
2156
|
-
unavailableRepositories = fromGraph.unavailable;
|
|
2157
|
-
}
|
|
2158
|
-
if (refs.length > 0 || anchorUids.length > 0) {
|
|
2159
|
-
const target = await resolveSingleTarget(
|
|
2160
|
-
codeClient,
|
|
2161
|
-
adapter,
|
|
2162
|
-
refs,
|
|
2163
|
-
anchorUids,
|
|
2164
|
-
options
|
|
2165
|
-
);
|
|
2166
|
-
if ("error" in target) return target.error;
|
|
2167
|
-
graphRefs.unshift(target.ref);
|
|
2168
|
-
}
|
|
2169
|
-
const unique = uniqueRefs(graphRefs);
|
|
2170
|
-
if (unique.length === 0) {
|
|
2171
|
-
return ok({
|
|
2172
|
-
target_uid: targetUid,
|
|
2173
|
-
graph_context: graphContext,
|
|
2174
|
-
affected: [],
|
|
2175
|
-
degradation: unavailableRepositories.length > 0 ? `attached code anchors reference repositories unavailable here: ${unavailableRepositories.join(", ")}` : "no materialized code anchors were attached",
|
|
2176
|
-
...unavailableRepositories.length > 0 ? { unavailable_repositories: unavailableRepositories } : {}
|
|
2177
|
-
});
|
|
2178
|
-
}
|
|
2179
|
-
const impacts = await Promise.all(
|
|
2180
|
-
unique.map(async (ref) => ({ anchor: ref, ...await adapter.affected(ref, limit) }))
|
|
2181
|
-
);
|
|
2182
|
-
const structural = uniqueRefs(impacts.flatMap((impact) => impact.affected));
|
|
2183
|
-
const joined = await joinMaterialized(
|
|
2184
|
-
codeClient,
|
|
2185
|
-
structural,
|
|
2186
|
-
agentId,
|
|
2187
|
-
Math.min(limit ?? 50, 50)
|
|
2188
|
-
);
|
|
2189
|
-
return ok({
|
|
2190
|
-
target_uid: targetUid,
|
|
2191
|
-
graph_context: graphContext,
|
|
2192
|
-
impacts,
|
|
2193
|
-
materialized: joined,
|
|
2194
|
-
...unavailableRepositories.length > 0 ? { unavailable_repositories: unavailableRepositories } : {}
|
|
2195
|
-
});
|
|
2196
|
-
}
|
|
2197
|
-
default:
|
|
2198
|
-
return err("unknown_action", `unknown mindgraph_code action: ${action}`);
|
|
2199
|
-
}
|
|
2200
|
-
} catch (cause) {
|
|
2201
|
-
if (cause instanceof UnknownRepositoryError) {
|
|
2202
|
-
return err("unknown_repository", cause.message);
|
|
2203
|
-
}
|
|
2204
|
-
return toolError(cause, "code_tool_failed");
|
|
2067
|
+
function layer7Fields(node, state) {
|
|
2068
|
+
const fields = nodeFields(node);
|
|
2069
|
+
if (!fields) return void 0;
|
|
2070
|
+
const pairs = Object.entries(fields).filter(([, value]) => value !== null && value !== void 0 && value !== "").map(([key, value]) => `${key}: ${Array.isArray(value) ? value.join(", ") : String(value)}`);
|
|
2071
|
+
if (pairs.length === 0) return void 0;
|
|
2072
|
+
return clip(pairs.join("; "), SUMMARY_MAX, state);
|
|
2073
|
+
}
|
|
2074
|
+
function nodeSummaryText(node, state) {
|
|
2075
|
+
const props = obj(node.props);
|
|
2076
|
+
return clip(node.summary, SUMMARY_MAX, state) ?? clip(props?.content, SUMMARY_MAX, state) ?? clip(props?.description, SUMMARY_MAX, state) ?? clip(props?.definition, SUMMARY_MAX, state);
|
|
2077
|
+
}
|
|
2078
|
+
function nodeLines(node, state) {
|
|
2079
|
+
const label = clip(node.label, LABEL_MAX, state) || "(unlabeled)";
|
|
2080
|
+
const type = nodeDisplayType(node);
|
|
2081
|
+
const confidence = num(node.confidence);
|
|
2082
|
+
const score = num(node.retrieval_score);
|
|
2083
|
+
const parenthetical = [
|
|
2084
|
+
type,
|
|
2085
|
+
confidence !== void 0 ? `confidence ${confidence}` : void 0,
|
|
2086
|
+
score !== void 0 ? `score ${Math.round(score * 100) / 100}` : void 0
|
|
2087
|
+
].filter(Boolean).join(", ");
|
|
2088
|
+
const head = `- **${label}** [${str(node.uid) || "?"}] (${parenthetical})${statusTag(node)}`;
|
|
2089
|
+
const summary = nodeSummaryText(node, state);
|
|
2090
|
+
const lines = [summary && summary !== label ? `${head}: ${summary}` : head];
|
|
2091
|
+
const fields = layer7Fields(node, state);
|
|
2092
|
+
if (fields) lines.push(` - fields: ${fields}`);
|
|
2093
|
+
const quote = firstQuote(node, state);
|
|
2094
|
+
if (quote) lines.push(` - source quote: "${quote}"`);
|
|
2095
|
+
const source = sourceTag(node);
|
|
2096
|
+
if (source) lines.push(` - ${source}`);
|
|
2097
|
+
const believers = Array.isArray(node.believed_by) ? node.believed_by : [];
|
|
2098
|
+
if (believers.length > 0) {
|
|
2099
|
+
const rendered2 = believers.slice(0, 3).map((raw) => {
|
|
2100
|
+
const believer = obj(raw);
|
|
2101
|
+
const conf = num(believer?.confidence);
|
|
2102
|
+
return `${str(believer?.agent_label) || "?"}${conf !== void 0 ? ` (${conf})` : ""}`;
|
|
2103
|
+
}).join(", ");
|
|
2104
|
+
lines.push(` - believed by: ${rendered2}`);
|
|
2205
2105
|
}
|
|
2106
|
+
return lines.join("\n");
|
|
2206
2107
|
}
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2108
|
+
function groupedNodeSections(nodes, state, headingPrefix = "") {
|
|
2109
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2110
|
+
for (const node of nodes) {
|
|
2111
|
+
const type = nodeDisplayType(node);
|
|
2112
|
+
const group = groups.get(type) ?? [];
|
|
2113
|
+
group.push(node);
|
|
2114
|
+
groups.set(type, group);
|
|
2210
2115
|
}
|
|
2211
|
-
const
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
wrote: false,
|
|
2218
|
-
caveat: "the write response exposed no primary UID to attach"
|
|
2219
|
-
}
|
|
2116
|
+
const sections = [];
|
|
2117
|
+
let first = true;
|
|
2118
|
+
for (const [type, group] of groups) {
|
|
2119
|
+
sections.push({
|
|
2120
|
+
heading: `${first ? headingPrefix : ""}### ${type}`,
|
|
2121
|
+
items: group.map((node) => nodeLines(node, state))
|
|
2220
2122
|
});
|
|
2123
|
+
first = false;
|
|
2221
2124
|
}
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2125
|
+
return sections;
|
|
2126
|
+
}
|
|
2127
|
+
function relationshipItems(edges, labelByUid, state, options) {
|
|
2128
|
+
const lines = [];
|
|
2129
|
+
for (const raw of edges) {
|
|
2130
|
+
const edge = obj(raw);
|
|
2131
|
+
if (!edge) continue;
|
|
2132
|
+
const type = edgeTypeName(edge.edge_type) || "?";
|
|
2133
|
+
const from = str(edge.from_uid);
|
|
2134
|
+
const to = str(edge.to_uid);
|
|
2135
|
+
if (CURATION_EDGE_TYPES.has(type)) continue;
|
|
2136
|
+
if (!from || !to || from === to) continue;
|
|
2137
|
+
const fromLabel = labelByUid.get(from) ?? (options?.fallbackToUid ? `[${from}]` : void 0);
|
|
2138
|
+
const toLabel = labelByUid.get(to) ?? (options?.fallbackToUid ? `[${to}]` : void 0);
|
|
2139
|
+
if (!fromLabel || !toLabel) continue;
|
|
2140
|
+
lines.push(`- ${fromLabel} \u2014${type}\u2192 ${toLabel}`);
|
|
2141
|
+
}
|
|
2142
|
+
if (lines.length > RELATIONSHIP_MAX) {
|
|
2143
|
+
state.bounded = true;
|
|
2144
|
+
const extra = lines.length - RELATIONSHIP_MAX;
|
|
2145
|
+
state.omitted += extra;
|
|
2146
|
+
return [...lines.slice(0, RELATIONSHIP_MAX), `- (+${extra} more relationships)`];
|
|
2147
|
+
}
|
|
2148
|
+
return lines;
|
|
2149
|
+
}
|
|
2150
|
+
function assemble(header, sections, state, maxChars) {
|
|
2151
|
+
const budget = renderBudget(maxChars);
|
|
2152
|
+
const packingBudget = Math.max(header.length, budget - BOUNDED_MARKER_RESERVE);
|
|
2153
|
+
const lines = [header];
|
|
2154
|
+
let used = header.length;
|
|
2155
|
+
for (const section of sections) {
|
|
2156
|
+
if (section.items.length === 0 && !section.heading) continue;
|
|
2157
|
+
const headingCost = section.heading.length + 2;
|
|
2158
|
+
if (used + headingCost > packingBudget) {
|
|
2159
|
+
state.bounded = true;
|
|
2160
|
+
state.omitted += Math.max(1, section.items.length);
|
|
2161
|
+
continue;
|
|
2162
|
+
}
|
|
2163
|
+
let sectionUsed = 0;
|
|
2164
|
+
const kept = [];
|
|
2165
|
+
let dropped = 0;
|
|
2166
|
+
for (const item of section.items) {
|
|
2167
|
+
if (used + headingCost + sectionUsed + item.length + 1 > packingBudget) {
|
|
2168
|
+
dropped += 1;
|
|
2169
|
+
continue;
|
|
2258
2170
|
}
|
|
2171
|
+
kept.push(item);
|
|
2172
|
+
sectionUsed += item.length + 1;
|
|
2259
2173
|
}
|
|
2260
|
-
if (
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2174
|
+
if (kept.length === 0 && section.items.length > 0) {
|
|
2175
|
+
state.bounded = true;
|
|
2176
|
+
state.omitted += dropped;
|
|
2177
|
+
continue;
|
|
2178
|
+
}
|
|
2179
|
+
if (dropped > 0) {
|
|
2180
|
+
state.bounded = true;
|
|
2181
|
+
state.omitted += dropped;
|
|
2182
|
+
const marker = `(+${dropped} more not shown)`;
|
|
2183
|
+
if (used + headingCost + sectionUsed + marker.length + 1 <= packingBudget) {
|
|
2184
|
+
kept.push(marker);
|
|
2185
|
+
sectionUsed += marker.length + 1;
|
|
2186
|
+
}
|
|
2268
2187
|
}
|
|
2188
|
+
lines.push(
|
|
2189
|
+
section.items.length === 0 ? section.heading : section.heading ? `${section.heading}
|
|
2190
|
+
${kept.join("\n")}` : kept.join("\n")
|
|
2191
|
+
);
|
|
2192
|
+
used += headingCost + sectionUsed;
|
|
2269
2193
|
}
|
|
2270
|
-
|
|
2194
|
+
if (state.bounded) {
|
|
2195
|
+
lines.push(
|
|
2196
|
+
`\u2026[rendered context bounded to ${budget} chars; ${state.omitted} item(s) omitted \u2014 fetch full node content by uid, narrow the query, raise max_output_chars, or pass format: "json" for the raw response]`
|
|
2197
|
+
);
|
|
2198
|
+
}
|
|
2199
|
+
return lines.join("\n\n").slice(0, budget);
|
|
2271
2200
|
}
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
canonical_name: repository.repoId,
|
|
2279
|
-
identifiers: { repo_id: repository.repoId }
|
|
2280
|
-
},
|
|
2281
|
-
identity: {
|
|
2282
|
-
namespace: "external.code",
|
|
2283
|
-
key_version: 1,
|
|
2284
|
-
key: repositoryIdentityKey(repository)
|
|
2285
|
-
},
|
|
2286
|
-
identity_space_uid: spaceUid,
|
|
2287
|
-
agent_id: agentId
|
|
2201
|
+
function policySections(payload, state) {
|
|
2202
|
+
const policies = Array.isArray(payload.applicable_policies) ? payload.applicable_policies : [];
|
|
2203
|
+
if (policies.length === 0) return [];
|
|
2204
|
+
const items = policies.map((raw) => obj(raw)).filter((policy) => Boolean(policy)).map((policy) => {
|
|
2205
|
+
const rules = Array.isArray(policy.prose_rules) ? policy.prose_rules.map(String).join("; ") : policy.prose_rules;
|
|
2206
|
+
return `- ${str(policy.name) || "(unnamed policy)"}: ${clip(rules, SUMMARY_MAX, state) || "(structured rules apply)"}`;
|
|
2288
2207
|
});
|
|
2208
|
+
return [{ heading: "## Applicable policies", items }];
|
|
2289
2209
|
}
|
|
2290
|
-
|
|
2291
|
-
const
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
identifiers: {
|
|
2298
|
-
repo_id: ref.repoId,
|
|
2299
|
-
path: ref.path,
|
|
2300
|
-
qualified_name: ref.qualifiedName
|
|
2301
|
-
},
|
|
2302
|
-
attributes: { code_ref: storedCodeRef(ref) }
|
|
2303
|
-
},
|
|
2304
|
-
identity: {
|
|
2305
|
-
namespace: "external.code",
|
|
2306
|
-
key_version: 1,
|
|
2307
|
-
key: codeRefIdentityKey(ref)
|
|
2308
|
-
},
|
|
2309
|
-
identity_space_uid: spaceUid,
|
|
2310
|
-
agent_id: agentId
|
|
2311
|
-
});
|
|
2312
|
-
if (response.uid && response.created !== false && response.status !== "existing") {
|
|
2313
|
-
await client2.entity({
|
|
2314
|
-
action: "relate",
|
|
2315
|
-
source_uid: response.uid,
|
|
2316
|
-
target_uid: repositoryUid,
|
|
2317
|
-
edge_type: "PART_OF",
|
|
2318
|
-
agent_id: agentId
|
|
2319
|
-
});
|
|
2320
|
-
}
|
|
2321
|
-
return { ...response, ref };
|
|
2322
|
-
}
|
|
2323
|
-
async function resolveMaterializedUid(client2, ref, agentId) {
|
|
2324
|
-
const response = await client2.entity({
|
|
2325
|
-
action: "resolve_identity",
|
|
2326
|
-
identity: {
|
|
2327
|
-
namespace: "external.code",
|
|
2328
|
-
key_version: 1,
|
|
2329
|
-
key: codeRefIdentityKey(ref)
|
|
2330
|
-
},
|
|
2331
|
-
agent_id: agentId
|
|
2332
|
-
});
|
|
2333
|
-
return response.status === "existing" ? response.uid : void 0;
|
|
2334
|
-
}
|
|
2335
|
-
async function codeRefFromAnchor(client2, adapter, uid) {
|
|
2336
|
-
const node = await client2.getNode(uid);
|
|
2337
|
-
const attributes = node.props?.attributes;
|
|
2338
|
-
if (!attributes || typeof attributes !== "object") return void 0;
|
|
2339
|
-
const codeRef = attributes.code_ref;
|
|
2340
|
-
if (!isStoredCodeRef(codeRef)) return void 0;
|
|
2341
|
-
try {
|
|
2342
|
-
const repository = await adapter.resolveRepository(codeRef.repoId);
|
|
2343
|
-
return { ref: { ...codeRef, repoRoot: repository.repoRoot } };
|
|
2344
|
-
} catch (cause) {
|
|
2345
|
-
if (cause instanceof UnknownRepositoryError) {
|
|
2346
|
-
return { missingRepo: codeRef.repoId };
|
|
2210
|
+
function hiddenSections(payload) {
|
|
2211
|
+
const hidden = payload.hidden_item_count;
|
|
2212
|
+
if (hidden === void 0 || hidden === null) return [];
|
|
2213
|
+
return [
|
|
2214
|
+
{
|
|
2215
|
+
heading: `Note: ${hidden} item(s) were hidden by access scoping.`,
|
|
2216
|
+
items: []
|
|
2347
2217
|
}
|
|
2348
|
-
|
|
2349
|
-
}
|
|
2218
|
+
];
|
|
2350
2219
|
}
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
}
|
|
2360
|
-
if ("missingRepo" in outcome) {
|
|
2361
|
-
return {
|
|
2362
|
-
error: err(
|
|
2363
|
-
"repository_unavailable",
|
|
2364
|
-
`repository ${outcome.missingRepo} is not checked out here or declared in .mindgraph/workspace.json; expand/affected need its local index`
|
|
2365
|
-
)
|
|
2366
|
-
};
|
|
2220
|
+
function renderContext(response, maxChars) {
|
|
2221
|
+
const payload = obj(response);
|
|
2222
|
+
if (!payload) return void 0;
|
|
2223
|
+
if (typeof payload.context_text === "string" && payload.context_text.length > 0) {
|
|
2224
|
+
const text2 = payload.context_text;
|
|
2225
|
+
if (maxChars !== void 0 && text2.length > maxChars) {
|
|
2226
|
+
return `${text2.slice(0, maxChars)}
|
|
2227
|
+
[truncated to ${maxChars} characters]`;
|
|
2367
2228
|
}
|
|
2368
|
-
return
|
|
2369
|
-
}
|
|
2370
|
-
if (refs.length !== 1) {
|
|
2371
|
-
return { error: err("missing_code_ref", "one code_ref or anchor_uid is required") };
|
|
2372
|
-
}
|
|
2373
|
-
const resolution = await adapter.resolveRefs(refs, options);
|
|
2374
|
-
const result = resolution.results[0];
|
|
2375
|
-
if (!result.resolved) {
|
|
2376
|
-
return {
|
|
2377
|
-
error: ok({
|
|
2378
|
-
availability: resolution.availability,
|
|
2379
|
-
result,
|
|
2380
|
-
wrote: false
|
|
2381
|
-
})
|
|
2382
|
-
};
|
|
2229
|
+
return text2;
|
|
2383
2230
|
}
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
const
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2231
|
+
const graph = obj(payload.graph);
|
|
2232
|
+
const rawNodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
|
|
2233
|
+
const shaped = rawNodes.filter(isNodeShaped);
|
|
2234
|
+
const nodes = shaped.filter((node) => !INFRA_NODE_TYPES.has(str(node.node_type) || ""));
|
|
2235
|
+
const infraOnly = shaped.length - nodes.length;
|
|
2236
|
+
const articles = Array.isArray(payload.articles) ? payload.articles : [];
|
|
2237
|
+
const chunks = Array.isArray(payload.chunks) ? payload.chunks : [];
|
|
2238
|
+
const state = { bounded: false, omitted: 0 };
|
|
2239
|
+
const sections = [];
|
|
2240
|
+
if (articles.length > 0) {
|
|
2241
|
+
sections.push({
|
|
2242
|
+
heading: "## Knowledge articles",
|
|
2243
|
+
items: articles.map((raw) => obj(raw)).filter((article) => Boolean(article)).map(
|
|
2244
|
+
(article) => `### ${str(article.label) || "(untitled)"} [${str(article.uid) || "?"}]
|
|
2245
|
+
${clip(article.content, ARTICLE_MAX, state) || ""}`
|
|
2246
|
+
)
|
|
2400
2247
|
});
|
|
2401
2248
|
}
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
const codeRef = attributes.code_ref;
|
|
2414
|
-
return isStoredCodeRef(codeRef) ? [codeRef] : [];
|
|
2415
|
-
});
|
|
2416
|
-
const refs = [];
|
|
2417
|
-
const unavailable = /* @__PURE__ */ new Set();
|
|
2418
|
-
for (const ref of stored) {
|
|
2419
|
-
try {
|
|
2420
|
-
const repository = await adapter.resolveRepository(ref.repoId);
|
|
2421
|
-
refs.push({ ...ref, repoRoot: repository.repoRoot });
|
|
2422
|
-
} catch (cause) {
|
|
2423
|
-
if (cause instanceof UnknownRepositoryError) {
|
|
2424
|
-
unavailable.add(ref.repoId);
|
|
2425
|
-
} else {
|
|
2426
|
-
throw cause;
|
|
2427
|
-
}
|
|
2249
|
+
if (nodes.length > 0) {
|
|
2250
|
+
sections.push(...groupedNodeSections(nodes, state, "## Knowledge graph\n\n"));
|
|
2251
|
+
const labelByUid = /* @__PURE__ */ new Map();
|
|
2252
|
+
for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
2253
|
+
const relationships = relationshipItems(
|
|
2254
|
+
Array.isArray(graph?.edges) ? graph.edges : [],
|
|
2255
|
+
labelByUid,
|
|
2256
|
+
state
|
|
2257
|
+
);
|
|
2258
|
+
if (relationships.length > 0) {
|
|
2259
|
+
sections.push({ heading: "## Relationships", items: relationships });
|
|
2428
2260
|
}
|
|
2261
|
+
} else if (infraOnly > 0) {
|
|
2262
|
+
sections.push({
|
|
2263
|
+
heading: `Note: ${infraOnly} document/chunk node(s) matched \u2014 use action "document_index" or include_chunks for their content.`,
|
|
2264
|
+
items: []
|
|
2265
|
+
});
|
|
2429
2266
|
}
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2267
|
+
if (chunks.length > 0) {
|
|
2268
|
+
sections.push({
|
|
2269
|
+
heading: "## Source excerpts",
|
|
2270
|
+
items: chunks.map((raw) => obj(raw)).filter((chunk) => Boolean(chunk)).map(
|
|
2271
|
+
(chunk) => `- [${str(chunk.chunk_uid) || "?"}] from "${str(chunk.document_title) || "unknown document"}": "${clip(chunk.content, CHUNK_MAX, state) || ""}"`
|
|
2272
|
+
)
|
|
2273
|
+
});
|
|
2436
2274
|
}
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
} else {
|
|
2442
|
-
collectUids(child, uids);
|
|
2443
|
-
}
|
|
2275
|
+
sections.push(...policySections(payload, state));
|
|
2276
|
+
sections.push(...hiddenSections(payload));
|
|
2277
|
+
if (sections.length === 0) {
|
|
2278
|
+
return "No results found for this search.";
|
|
2444
2279
|
}
|
|
2280
|
+
if (nodes.length === 0 && articles.length === 0 && chunks.length === 0) {
|
|
2281
|
+
return assemble(
|
|
2282
|
+
"# Retrieved context\n\nNo graph results matched this search.",
|
|
2283
|
+
sections,
|
|
2284
|
+
state,
|
|
2285
|
+
maxChars
|
|
2286
|
+
);
|
|
2287
|
+
}
|
|
2288
|
+
return assemble("# Retrieved context", sections, state, maxChars);
|
|
2445
2289
|
}
|
|
2446
|
-
function
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
const
|
|
2453
|
-
|
|
2290
|
+
function resumeEntry(raw, state, fallbackType) {
|
|
2291
|
+
const wrapper = obj(raw);
|
|
2292
|
+
if (!wrapper) return void 0;
|
|
2293
|
+
const value = obj(wrapper.node) ?? wrapper;
|
|
2294
|
+
if (isNodeShaped(value)) return nodeLines(value, state);
|
|
2295
|
+
const uid = str(value.uid);
|
|
2296
|
+
const props = obj(value.props);
|
|
2297
|
+
const label = clip(value.label, LABEL_MAX, state) ?? clip(value.name, LABEL_MAX, state) ?? clip(value.title, LABEL_MAX, state) ?? clip(value.action, LABEL_MAX, state) ?? fallbackType;
|
|
2298
|
+
const status = str(value.status) ?? str(props?.status);
|
|
2299
|
+
const summary = clip(value.summary, SUMMARY_MAX, state) ?? clip(value.description, SUMMARY_MAX, state) ?? clip(value.content, SUMMARY_MAX, state) ?? clip(value.outcome_summary, SUMMARY_MAX, state) ?? clip(props?.summary, SUMMARY_MAX, state);
|
|
2300
|
+
const details = [
|
|
2301
|
+
status ? `status ${status}` : void 0,
|
|
2302
|
+
str(value.path) ? `path ${str(value.path)}` : void 0,
|
|
2303
|
+
str(value.symbol) ? `symbol ${str(value.symbol)}` : void 0
|
|
2304
|
+
].filter(Boolean);
|
|
2305
|
+
const head = `- **${label}**${uid ? ` [${uid}]` : ""}${details.length > 0 ? ` (${details.join(", ")})` : ""}`;
|
|
2306
|
+
return summary && summary !== label ? `${head}: ${summary}` : head;
|
|
2454
2307
|
}
|
|
2455
|
-
function
|
|
2456
|
-
const
|
|
2457
|
-
return
|
|
2458
|
-
const key = `${ref.repoId}:${ref.path}:${ref.qualifiedName}:${ref.signature ?? ""}`;
|
|
2459
|
-
if (seen.has(key)) return false;
|
|
2460
|
-
seen.add(key);
|
|
2461
|
-
return true;
|
|
2462
|
-
});
|
|
2308
|
+
function resumeSection(heading, values, state, fallbackType) {
|
|
2309
|
+
const items = values.map((value) => resumeEntry(value, state, fallbackType)).filter((value) => Boolean(value));
|
|
2310
|
+
return items.length > 0 ? { heading, items } : void 0;
|
|
2463
2311
|
}
|
|
2464
|
-
function
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
const
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
"
|
|
2471
|
-
"
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
];
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
2494
|
-
});
|
|
2495
|
-
err = (code, message, details) => ({
|
|
2496
|
-
content: [
|
|
2497
|
-
{
|
|
2498
|
-
type: "text",
|
|
2499
|
-
text: JSON.stringify({ error: message, code, details })
|
|
2500
|
-
}
|
|
2501
|
-
],
|
|
2502
|
-
isError: true
|
|
2312
|
+
function renderResumeContext(response, maxChars, repositoryNames = {}) {
|
|
2313
|
+
const payload = obj(response);
|
|
2314
|
+
if (!payload) return void 0;
|
|
2315
|
+
const state = { bounded: false, omitted: 0 };
|
|
2316
|
+
const status = str(payload.status);
|
|
2317
|
+
if (!payload.task && status === "no_eligible_work") {
|
|
2318
|
+
const scopedOut = typeof payload.scoped_out_tasks === "number" ? payload.scoped_out_tasks : 0;
|
|
2319
|
+
const note = scopedOut > 0 ? ` ${scopedOut} task(s) exist outside the requested scope.` : "";
|
|
2320
|
+
return assemble(
|
|
2321
|
+
"# Resume context",
|
|
2322
|
+
[{ heading: `No eligible work was found in the requested scope.${note}`, items: [] }],
|
|
2323
|
+
state,
|
|
2324
|
+
maxChars
|
|
2325
|
+
);
|
|
2326
|
+
}
|
|
2327
|
+
const sections = [];
|
|
2328
|
+
const push = (section) => {
|
|
2329
|
+
if (section) sections.push(section);
|
|
2330
|
+
};
|
|
2331
|
+
push(resumeSection("## Current task", [payload.task], state, "Task"));
|
|
2332
|
+
const other = otherEligibleWork(payload, repositoryNames);
|
|
2333
|
+
if (other.shown > 0) {
|
|
2334
|
+
sections.push({
|
|
2335
|
+
heading: `## Other eligible work (${other.shown} shown${other.more > 0 ? `, ${other.more} more not listed` : ""})`,
|
|
2336
|
+
items: other.groups.flatMap(
|
|
2337
|
+
(group) => group.tasks.map(
|
|
2338
|
+
(task) => `- ${group.repository}: ${task.label} [${task.uid}]${task.detail ? ` \u2014 ${task.detail}` : ""}`
|
|
2339
|
+
)
|
|
2340
|
+
)
|
|
2503
2341
|
});
|
|
2504
|
-
CODE_TOOL = {
|
|
2505
|
-
name: "mindgraph_code",
|
|
2506
|
-
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. Refs with a path route to the repository defining that file (nearest declared root in .mindgraph/workspace.json); bare symbols resolve against the current repository only \u2014 pass path or repo to target a sibling.",
|
|
2507
|
-
inputSchema: {
|
|
2508
|
-
type: "object",
|
|
2509
|
-
properties: {
|
|
2510
|
-
action: {
|
|
2511
|
-
type: "string",
|
|
2512
|
-
enum: ["anchor", "recall", "expand", "affected"]
|
|
2513
|
-
},
|
|
2514
|
-
code_refs: {
|
|
2515
|
-
type: "array",
|
|
2516
|
-
maxItems: 50,
|
|
2517
|
-
items: {
|
|
2518
|
-
oneOf: [
|
|
2519
|
-
{ type: "string" },
|
|
2520
|
-
{
|
|
2521
|
-
type: "object",
|
|
2522
|
-
properties: {
|
|
2523
|
-
repo: { type: "string" },
|
|
2524
|
-
path: { type: "string" },
|
|
2525
|
-
line: { type: "integer", minimum: 1 },
|
|
2526
|
-
symbol: { type: "string" },
|
|
2527
|
-
kind: { type: "string" },
|
|
2528
|
-
language: { type: "string" },
|
|
2529
|
-
signature: { type: "string" }
|
|
2530
|
-
},
|
|
2531
|
-
additionalProperties: false
|
|
2532
|
-
}
|
|
2533
|
-
]
|
|
2534
|
-
}
|
|
2535
|
-
},
|
|
2536
|
-
anchor_uids: {
|
|
2537
|
-
type: "array",
|
|
2538
|
-
maxItems: 50,
|
|
2539
|
-
items: { type: "string" },
|
|
2540
|
-
description: "Already-materialized code anchor UIDs (codegraph is not required for recall)"
|
|
2541
|
-
},
|
|
2542
|
-
repo: {
|
|
2543
|
-
type: "string",
|
|
2544
|
-
description: "Configured repository id or repository root"
|
|
2545
|
-
},
|
|
2546
|
-
repo_space_uid: {
|
|
2547
|
-
type: "string",
|
|
2548
|
-
description: "Writable shared Project/repository Space for new anchors (anchor requires this, a workspace space_uid, or MINDGRAPH_CODE_SPACE_UID)"
|
|
2549
|
-
},
|
|
2550
|
-
task_uid: { type: "string" },
|
|
2551
|
-
execution_uid: { type: "string" },
|
|
2552
|
-
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
2553
|
-
agent_id: { type: "string" }
|
|
2554
|
-
},
|
|
2555
|
-
required: ["action"]
|
|
2556
|
-
},
|
|
2557
|
-
annotations: {
|
|
2558
|
-
readOnlyHint: false,
|
|
2559
|
-
destructiveHint: false,
|
|
2560
|
-
idempotentHint: true,
|
|
2561
|
-
openWorldHint: true
|
|
2562
|
-
}
|
|
2563
|
-
};
|
|
2564
2342
|
}
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2343
|
+
push(
|
|
2344
|
+
resumeSection(
|
|
2345
|
+
"## Goal and project",
|
|
2346
|
+
[payload.goal, payload.project].filter(Boolean),
|
|
2347
|
+
state,
|
|
2348
|
+
"Scope"
|
|
2349
|
+
)
|
|
2572
2350
|
);
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2351
|
+
push(resumeSection("## Plan", [payload.plan], state, "Plan"));
|
|
2352
|
+
push(
|
|
2353
|
+
resumeSection(
|
|
2354
|
+
"## Next steps",
|
|
2355
|
+
Array.isArray(payload.next_steps) ? payload.next_steps : [],
|
|
2356
|
+
state,
|
|
2357
|
+
"Next step"
|
|
2358
|
+
)
|
|
2359
|
+
);
|
|
2360
|
+
push(
|
|
2361
|
+
resumeSection(
|
|
2362
|
+
"## Blockers",
|
|
2363
|
+
Array.isArray(payload.blockers) ? payload.blockers : [],
|
|
2364
|
+
state,
|
|
2365
|
+
"Blocker"
|
|
2366
|
+
)
|
|
2367
|
+
);
|
|
2368
|
+
const knowledge = obj(payload.knowledge);
|
|
2369
|
+
push(
|
|
2370
|
+
resumeSection(
|
|
2371
|
+
"## Relevant lessons and risks",
|
|
2372
|
+
[
|
|
2373
|
+
...Array.isArray(knowledge?.lessons) ? knowledge.lessons : [],
|
|
2374
|
+
...Array.isArray(knowledge?.risks) ? knowledge.risks : []
|
|
2375
|
+
],
|
|
2376
|
+
state,
|
|
2377
|
+
"Knowledge"
|
|
2378
|
+
)
|
|
2379
|
+
);
|
|
2380
|
+
push(
|
|
2381
|
+
resumeSection(
|
|
2382
|
+
"## Active execution",
|
|
2383
|
+
[payload.active_execution].filter(Boolean),
|
|
2384
|
+
state,
|
|
2385
|
+
"Execution"
|
|
2386
|
+
)
|
|
2387
|
+
);
|
|
2388
|
+
push(
|
|
2389
|
+
resumeSection(
|
|
2390
|
+
"## Recent executions",
|
|
2391
|
+
Array.isArray(payload.recent_executions) ? payload.recent_executions : [],
|
|
2392
|
+
state,
|
|
2393
|
+
"Execution"
|
|
2394
|
+
)
|
|
2395
|
+
);
|
|
2396
|
+
push(
|
|
2397
|
+
resumeSection(
|
|
2398
|
+
"## Code targets",
|
|
2399
|
+
Array.isArray(payload.code_targets) ? payload.code_targets : [],
|
|
2400
|
+
state,
|
|
2401
|
+
"Code target"
|
|
2402
|
+
)
|
|
2403
|
+
);
|
|
2404
|
+
push(resumeSection("## Lease", [payload.lease].filter(Boolean), state, "Lease"));
|
|
2405
|
+
const warning = clip(payload.warning, SUMMARY_MAX, state);
|
|
2406
|
+
if (warning) sections.push({ heading: "## Warning", items: [`- ${warning}`] });
|
|
2407
|
+
const selectionReason = clip(payload.selection_reason, SUMMARY_MAX, state);
|
|
2408
|
+
if (selectionReason) {
|
|
2409
|
+
sections.push({ heading: "## Selection", items: [`- ${selectionReason}`] });
|
|
2578
2410
|
}
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
function bareToolName(name) {
|
|
2582
|
-
const cut = name.lastIndexOf("__");
|
|
2583
|
-
return cut === -1 ? name : name.slice(cut + 2);
|
|
2584
|
-
}
|
|
2585
|
-
function callKey(toolName, toolInput) {
|
|
2586
|
-
return import_node_crypto4.default.createHash("sha256").update(`${bareToolName(toolName)}\0${stableJson(toolInput)}`).digest("hex");
|
|
2587
|
-
}
|
|
2588
|
-
function sessionKey(sessionId) {
|
|
2589
|
-
return import_node_crypto4.default.createHash("sha256").update(sessionId).digest("hex").slice(0, 16);
|
|
2411
|
+
if (sections.length === 0) return void 0;
|
|
2412
|
+
return assemble("# Resume context", sections, state, maxChars);
|
|
2590
2413
|
}
|
|
2591
|
-
function
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
const
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2414
|
+
function otherEligibleWork(brief, names = {}) {
|
|
2415
|
+
const listed = Array.isArray(brief?.other_eligible) ? brief.other_eligible : [];
|
|
2416
|
+
const groups = [];
|
|
2417
|
+
let shown = 0;
|
|
2418
|
+
for (const entry of listed) {
|
|
2419
|
+
const task = obj(entry);
|
|
2420
|
+
const uid = typeof task?.uid === "string" ? task.uid : void 0;
|
|
2421
|
+
if (!task || !uid) continue;
|
|
2422
|
+
const scopes = Array.isArray(task.scope_uids) ? task.scope_uids.map(String) : [];
|
|
2423
|
+
const named = scopes.find((scope) => names[scope]);
|
|
2424
|
+
const repository = named ? names[named] : "Other scopes";
|
|
2425
|
+
let group = groups.find((candidate) => candidate.repository === repository);
|
|
2426
|
+
if (!group) {
|
|
2427
|
+
group = { repository, tasks: [] };
|
|
2428
|
+
groups.push(group);
|
|
2429
|
+
}
|
|
2430
|
+
const label = typeof task.label === "string" && task.label ? task.label : "(unlabeled)";
|
|
2431
|
+
const detail = [
|
|
2432
|
+
typeof task.status === "string" ? task.status : void 0,
|
|
2433
|
+
typeof task.priority === "string" && task.priority ? `priority ${task.priority}` : void 0
|
|
2434
|
+
].filter(Boolean).join(", ");
|
|
2435
|
+
group.tasks.push({
|
|
2436
|
+
uid,
|
|
2437
|
+
label: label.length > 160 ? `${label.slice(0, 159)}\u2026` : label,
|
|
2438
|
+
detail
|
|
2439
|
+
});
|
|
2440
|
+
shown += 1;
|
|
2604
2441
|
}
|
|
2442
|
+
const total = typeof brief?.eligible_total === "number" ? brief.eligible_total : 0;
|
|
2443
|
+
return { groups, shown, more: Math.max(0, total - 1 - shown) };
|
|
2605
2444
|
}
|
|
2606
|
-
function
|
|
2607
|
-
const
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
};
|
|
2618
|
-
const file = import_node_path3.default.join(
|
|
2619
|
-
dir,
|
|
2620
|
-
`${callKey(toolName, toolInput)}.${sessionKey(harnessSessionId)}.json`
|
|
2621
|
-
);
|
|
2622
|
-
const temporary = `${file}.${process.pid}.${import_node_crypto4.default.randomUUID()}.tmp`;
|
|
2623
|
-
import_node_fs3.default.writeFileSync(temporary, JSON.stringify(record), { mode: 384 });
|
|
2624
|
-
import_node_fs3.default.renameSync(temporary, file);
|
|
2625
|
-
} catch {
|
|
2445
|
+
function renderOtherEligibleWork(brief, names = {}) {
|
|
2446
|
+
const { groups, shown, more } = otherEligibleWork(brief, names);
|
|
2447
|
+
if (shown === 0) return void 0;
|
|
2448
|
+
const lines = [
|
|
2449
|
+
`Other eligible work in this scope (${shown} shown${more > 0 ? `, ${more} more not listed` : ""}; resume one by task_uid):`
|
|
2450
|
+
];
|
|
2451
|
+
for (const group of groups) {
|
|
2452
|
+
lines.push(`${group.repository}:`);
|
|
2453
|
+
for (const task of group.tasks) {
|
|
2454
|
+
lines.push(`- ${task.label} [${task.uid}]${task.detail ? ` \u2014 ${task.detail}` : ""}`);
|
|
2455
|
+
}
|
|
2626
2456
|
}
|
|
2457
|
+
return lines.join("\n");
|
|
2627
2458
|
}
|
|
2628
|
-
function
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
try {
|
|
2641
|
-
const record = JSON.parse(
|
|
2642
|
-
import_node_fs3.default.readFileSync(import_node_path3.default.join(dir, name), "utf8")
|
|
2643
|
-
);
|
|
2644
|
-
if (typeof record.createdAt === "number" && now - record.createdAt <= TTL_MS && record.fills && typeof record.fills === "object") {
|
|
2645
|
-
matches.push(record);
|
|
2459
|
+
function renderNodeList(response, title, limit, maxChars) {
|
|
2460
|
+
let candidates = [];
|
|
2461
|
+
if (Array.isArray(response)) {
|
|
2462
|
+
candidates = response;
|
|
2463
|
+
} else {
|
|
2464
|
+
const payload = obj(response);
|
|
2465
|
+
if (!payload) return void 0;
|
|
2466
|
+
for (const key of ["nodes", "results", "items", "goals", "questions", "claims", "preferences", "documents"]) {
|
|
2467
|
+
const candidate = payload[key];
|
|
2468
|
+
if (Array.isArray(candidate) && candidate.some((entry) => unwrapSearchResult(entry))) {
|
|
2469
|
+
candidates = candidate;
|
|
2470
|
+
break;
|
|
2646
2471
|
}
|
|
2647
|
-
} catch {
|
|
2648
2472
|
}
|
|
2649
2473
|
}
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2474
|
+
let nodes = candidates.map(unwrapSearchResult).filter((node) => Boolean(node));
|
|
2475
|
+
if (nodes.length === 0) return void 0;
|
|
2476
|
+
const total = nodes.length;
|
|
2477
|
+
const state = { bounded: false, omitted: 0 };
|
|
2478
|
+
if (limit !== void 0 && limit > 0 && nodes.length > limit) {
|
|
2479
|
+
state.omitted += nodes.length - limit;
|
|
2480
|
+
nodes = nodes.slice(0, limit);
|
|
2481
|
+
state.bounded = true;
|
|
2653
2482
|
}
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
import_node_fs3 = __toESM(require("fs"));
|
|
2662
|
-
import_node_os3 = __toESM(require("os"));
|
|
2663
|
-
import_node_path3 = __toESM(require("path"));
|
|
2664
|
-
TTL_MS = 10 * 6e4;
|
|
2665
|
-
}
|
|
2666
|
-
});
|
|
2667
|
-
|
|
2668
|
-
// src/hook-installer.ts
|
|
2669
|
-
function storedFeatures(homeDir) {
|
|
2670
|
-
const { promptRecall: promptRecall2, capture } = loadHookEnv(
|
|
2671
|
-
import_node_path5.default.join(homeDir || import_node_os5.default.homedir(), ".mindgraph")
|
|
2483
|
+
const sections = groupedNodeSections(nodes, state);
|
|
2484
|
+
const shown = nodes.length;
|
|
2485
|
+
return assemble(
|
|
2486
|
+
`# ${title} (${shown === total ? total : `${shown} of ${total}`})`,
|
|
2487
|
+
sections,
|
|
2488
|
+
state,
|
|
2489
|
+
maxChars
|
|
2672
2490
|
);
|
|
2673
|
-
return { promptRecall: promptRecall2, capture };
|
|
2674
|
-
}
|
|
2675
|
-
function command(harness) {
|
|
2676
|
-
return `node "$HOME/${RUNNER_RELATIVE}" hook --harness ${harness} --owner mindgraph`;
|
|
2677
|
-
}
|
|
2678
|
-
function windowsCommand(harness) {
|
|
2679
|
-
return `node "%USERPROFILE%\\.mindgraph\\bin\\mindgraph-hook.cjs" hook --harness ${harness} --owner mindgraph`;
|
|
2680
2491
|
}
|
|
2681
|
-
function
|
|
2682
|
-
const
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
const
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2492
|
+
function renderTraversal(response, maxChars) {
|
|
2493
|
+
const directSteps = Array.isArray(response) ? response : void 0;
|
|
2494
|
+
const payload = obj(response);
|
|
2495
|
+
if (!payload && !directSteps) return void 0;
|
|
2496
|
+
const body = payload ?? {};
|
|
2497
|
+
const container = obj(body.graph) ?? body;
|
|
2498
|
+
const rawNodes = Array.isArray(container.nodes) ? container.nodes : [];
|
|
2499
|
+
const nodes = rawNodes.filter(isNodeShaped);
|
|
2500
|
+
const paths = Array.isArray(body.paths) ? body.paths : [];
|
|
2501
|
+
const hasSteps = directSteps !== void 0 || Object.hasOwn(body, "steps");
|
|
2502
|
+
const rawSteps = directSteps ?? (Array.isArray(body.steps) ? body.steps : []);
|
|
2503
|
+
if (nodes.length === 0 && paths.length === 0 && !hasSteps) return void 0;
|
|
2504
|
+
const state = { bounded: false, omitted: 0 };
|
|
2505
|
+
const sections = [];
|
|
2506
|
+
const labelByUid = /* @__PURE__ */ new Map();
|
|
2507
|
+
for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
2508
|
+
if (paths.length > 0) {
|
|
2509
|
+
const items = paths.map((raw) => obj(raw)).filter((path10) => Boolean(path10)).map((path10, index) => {
|
|
2510
|
+
const uids = Array.isArray(path10.node_uids) ? path10.node_uids.map(String) : [];
|
|
2511
|
+
const labels = Array.isArray(path10.labels) ? path10.labels.map(String) : Array.isArray(path10.nodes) ? path10.nodes.map((hop) => {
|
|
2512
|
+
const hopNode = obj(hop);
|
|
2513
|
+
return str(hopNode?.label) || labelByUid.get(str(hopNode?.uid) || "") || str(hop) || "?";
|
|
2514
|
+
}) : [];
|
|
2515
|
+
const route = labels.length > 0 ? labels.join(" \u2192 ") : uids.join(" \u2192 ") || "(path)";
|
|
2516
|
+
const cost = num(path10.cost) ?? num(path10.path_cost);
|
|
2517
|
+
const confidence = num(path10.path_confidence);
|
|
2518
|
+
const meta = [
|
|
2519
|
+
cost !== void 0 ? `cost ${cost}` : void 0,
|
|
2520
|
+
confidence !== void 0 ? `confidence ${confidence}` : void 0
|
|
2521
|
+
].filter(Boolean);
|
|
2522
|
+
const head = `${index + 1}. ${route}${meta.length > 0 ? ` (${meta.join(", ")})` : ""}`;
|
|
2523
|
+
return labels.length > 0 && uids.length > 0 ? `${head}
|
|
2524
|
+
uids: ${uids.join(" \u2192 ")}` : head;
|
|
2525
|
+
});
|
|
2526
|
+
sections.push({ heading: "## Paths", items });
|
|
2693
2527
|
}
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2528
|
+
if (hasSteps) {
|
|
2529
|
+
const stepItems = rawSteps.map((raw) => obj(raw)).filter((step) => Boolean(step)).map((step) => {
|
|
2530
|
+
const depth = num(step.depth);
|
|
2531
|
+
const uid = str(step.node_uid) ?? str(step.uid) ?? str(step.edge_to_uid) ?? "?";
|
|
2532
|
+
const parent = str(step.parent_uid) ?? str(step.edge_from_uid);
|
|
2533
|
+
const label = str(step.label) || "(unlabeled)";
|
|
2534
|
+
const relation = edgeTypeName(step.edge_type);
|
|
2535
|
+
const role = str(step.traversal_role);
|
|
2536
|
+
const type = str(step.node_type) ?? str(obj(step.node_type)?.Custom);
|
|
2537
|
+
const cost = num(step.path_cost);
|
|
2538
|
+
const confidence = num(step.path_confidence);
|
|
2539
|
+
const prefix = [
|
|
2540
|
+
depth !== void 0 ? `depth ${depth}` : void 0,
|
|
2541
|
+
role,
|
|
2542
|
+
relation ? `\u2014${relation}\u2192` : void 0
|
|
2543
|
+
].filter(Boolean).join(" ");
|
|
2544
|
+
const meta = [
|
|
2545
|
+
type,
|
|
2546
|
+
parent ? `parent ${parent}` : void 0,
|
|
2547
|
+
cost !== void 0 ? `cost ${cost}` : void 0,
|
|
2548
|
+
confidence !== void 0 ? `confidence ${confidence}` : void 0
|
|
2549
|
+
].filter(Boolean).join(", ");
|
|
2550
|
+
return `- ${prefix ? `${prefix}: ` : ""}**${label}** [${uid}]${meta ? ` (${meta})` : ""}`;
|
|
2551
|
+
});
|
|
2552
|
+
if (stepItems.length > 0) {
|
|
2553
|
+
sections.push({ heading: "## Traversal steps", items: stepItems });
|
|
2554
|
+
} else {
|
|
2555
|
+
const start = str(body.start_uid) || "?";
|
|
2556
|
+
const end = str(body.end_uid);
|
|
2557
|
+
const mode2 = str(body.mode);
|
|
2558
|
+
sections.push({
|
|
2559
|
+
heading: mode2 === "path" && end ? `No path found from [${start}] to [${end}].` : `No traversal steps returned from [${start}].`,
|
|
2560
|
+
items: []
|
|
2561
|
+
});
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
if (nodes.length > 0) {
|
|
2565
|
+
sections.push(...groupedNodeSections(nodes, state, "## Nodes\n\n"));
|
|
2566
|
+
const relationships = relationshipItems(
|
|
2567
|
+
Array.isArray(container.edges) ? container.edges : [],
|
|
2568
|
+
labelByUid,
|
|
2569
|
+
state
|
|
2570
|
+
);
|
|
2571
|
+
if (relationships.length > 0) {
|
|
2572
|
+
sections.push({ heading: "## Relationships", items: relationships });
|
|
2573
|
+
}
|
|
2705
2574
|
}
|
|
2575
|
+
const mode = str(body.mode);
|
|
2576
|
+
return assemble(
|
|
2577
|
+
`# Graph traversal${mode ? ` (${mode})` : ""}`,
|
|
2578
|
+
sections,
|
|
2579
|
+
state,
|
|
2580
|
+
maxChars
|
|
2581
|
+
);
|
|
2706
2582
|
}
|
|
2707
|
-
function
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2583
|
+
function neighborLine(value) {
|
|
2584
|
+
const node = obj(value);
|
|
2585
|
+
if (!node) return void 0;
|
|
2586
|
+
const label = str(node.label) || "(unlabeled)";
|
|
2587
|
+
const uid = str(node.uid);
|
|
2588
|
+
const type = str(node.node_type);
|
|
2589
|
+
return `- ${label}${uid ? ` [${uid}]` : ""}${type ? ` (${type})` : ""}`;
|
|
2712
2590
|
}
|
|
2713
|
-
function
|
|
2714
|
-
|
|
2715
|
-
|
|
2591
|
+
function renderOntologyAnswer(response) {
|
|
2592
|
+
const payload = obj(response);
|
|
2593
|
+
if (!payload) return void 0;
|
|
2594
|
+
const objects = Array.isArray(payload.objects) ? payload.objects.filter(isNodeShaped) : isNodeShaped(payload.object) ? [payload.object] : [];
|
|
2595
|
+
const cognitive = obj(payload.cognitive_context);
|
|
2596
|
+
const provenance = Array.isArray(payload.provenance) ? payload.provenance : [];
|
|
2597
|
+
const hasQuerySignature = Array.isArray(payload.objects) || obj(payload.confidence) !== void 0;
|
|
2598
|
+
const cognitiveEmpty = !cognitive || Object.values(cognitive).every((v) => !Array.isArray(v) || v.length === 0);
|
|
2599
|
+
if (objects.length === 0 && cognitiveEmpty && provenance.length === 0) {
|
|
2600
|
+
return hasQuerySignature ? "No matching domain objects." : void 0;
|
|
2716
2601
|
}
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
if (!
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
const
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
return { refreshed: false, reason: "unparseable", runnerVersion };
|
|
2738
|
-
}
|
|
2739
|
-
if (!semverNewer(server2, pinned)) {
|
|
2740
|
-
return {
|
|
2741
|
-
refreshed: false,
|
|
2742
|
-
reason: runnerVersion === serverVersion ? "current" : "downgrade",
|
|
2743
|
-
runnerVersion
|
|
2744
|
-
};
|
|
2602
|
+
const state = { bounded: false, omitted: 0 };
|
|
2603
|
+
const sections = [];
|
|
2604
|
+
if (objects.length > 0) {
|
|
2605
|
+
sections.push(...groupedNodeSections(objects, state));
|
|
2606
|
+
}
|
|
2607
|
+
const labelByUid = /* @__PURE__ */ new Map();
|
|
2608
|
+
for (const node of objects) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
2609
|
+
const graph = obj(payload.graph);
|
|
2610
|
+
for (const raw of Array.isArray(graph?.nodes) ? graph.nodes : []) {
|
|
2611
|
+
const node = obj(raw);
|
|
2612
|
+
const uid = str(node?.uid);
|
|
2613
|
+
if (uid && !labelByUid.has(uid)) labelByUid.set(uid, str(node?.label) || "?");
|
|
2614
|
+
}
|
|
2615
|
+
if (cognitive) {
|
|
2616
|
+
for (const rawNodes of Object.values(cognitive)) {
|
|
2617
|
+
if (!Array.isArray(rawNodes)) continue;
|
|
2618
|
+
for (const raw of rawNodes) {
|
|
2619
|
+
const node = obj(raw);
|
|
2620
|
+
const uid = str(node?.uid);
|
|
2621
|
+
if (uid && !labelByUid.has(uid)) labelByUid.set(uid, str(node?.label) || "?");
|
|
2745
2622
|
}
|
|
2746
2623
|
}
|
|
2747
|
-
installHookRunner(sourceBundle, home, serverVersion);
|
|
2748
|
-
return { refreshed: true, reason: "updated", runnerVersion };
|
|
2749
|
-
} catch {
|
|
2750
|
-
return { refreshed: false, reason: "error" };
|
|
2751
2624
|
}
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
if (!
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
projectDir,
|
|
2765
|
-
void 0,
|
|
2766
|
-
homeDir,
|
|
2767
|
-
storedFeatures(homeDir)
|
|
2768
|
-
);
|
|
2769
|
-
refreshed.push(file);
|
|
2770
|
-
} catch {
|
|
2625
|
+
const relations = Array.isArray(payload.relations) ? payload.relations : [];
|
|
2626
|
+
const relationLines = relationshipItems(relations, labelByUid, state, { fallbackToUid: true });
|
|
2627
|
+
if (relationLines.length > 0) {
|
|
2628
|
+
sections.push({ heading: "## Relations", items: relationLines });
|
|
2629
|
+
}
|
|
2630
|
+
if (cognitive) {
|
|
2631
|
+
for (const [category, rawNodes] of Object.entries(cognitive)) {
|
|
2632
|
+
if (!Array.isArray(rawNodes) || rawNodes.length === 0) continue;
|
|
2633
|
+
const items = rawNodes.map(neighborLine).filter((line) => Boolean(line));
|
|
2634
|
+
if (items.length > 0) {
|
|
2635
|
+
sections.push({ heading: `### Cognitive context \u2014 ${category}`, items });
|
|
2636
|
+
}
|
|
2771
2637
|
}
|
|
2772
2638
|
}
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
}
|
|
2780
|
-
|
|
2781
|
-
if (harness === "codex") {
|
|
2782
|
-
return scope === "user" ? import_node_path5.default.join(codexHomeDir, "hooks.json") : import_node_path5.default.join(projectDir, ".codex", "hooks.json");
|
|
2639
|
+
if (provenance.length > 0) {
|
|
2640
|
+
const items = provenance.map((raw) => obj(raw)).filter((entry) => Boolean(entry)).map((entry) => {
|
|
2641
|
+
const span = clip(entry.text_span, QUOTE_MAX, state) || "";
|
|
2642
|
+
const by = str(entry.ingested_by_name);
|
|
2643
|
+
const suffix = `${by ? ` (ingested by ${by})` : ""} [source ${str(entry.source_uid) || "?"}]`;
|
|
2644
|
+
return str(entry.span_kind) === "anchor" ? `- "${span}"${suffix}` : `- context: ${span}${suffix}`;
|
|
2645
|
+
});
|
|
2646
|
+
sections.push({ heading: "## Sources", items });
|
|
2783
2647
|
}
|
|
2784
|
-
|
|
2648
|
+
const confidence = num(obj(payload.confidence)?.overall);
|
|
2649
|
+
const notes = [];
|
|
2650
|
+
if (confidence !== void 0) notes.push(`overall confidence ${confidence}`);
|
|
2651
|
+
if (payload.has_more === true) notes.push("more results exist (has_more)");
|
|
2652
|
+
if (payload.seed_cap_hit === true) notes.push("seed cap hit \u2014 narrow the query for a complete set");
|
|
2653
|
+
if (notes.length > 0) sections.push({ heading: `Note: ${notes.join("; ")}.`, items: [] });
|
|
2654
|
+
return assemble(
|
|
2655
|
+
`# Ontology results (${objects.length} object${objects.length === 1 ? "" : "s"})`,
|
|
2656
|
+
sections,
|
|
2657
|
+
state
|
|
2658
|
+
);
|
|
2785
2659
|
}
|
|
2786
|
-
function
|
|
2787
|
-
|
|
2788
|
-
|
|
2660
|
+
function renderObjectList(response, title) {
|
|
2661
|
+
const payload = obj(response);
|
|
2662
|
+
if (!payload) return void 0;
|
|
2663
|
+
const rawItems = Array.isArray(payload.items) ? payload.items : Array.isArray(payload.rows) && payload.rows.some((row) => unwrapSearchResult(row)) ? payload.rows : void 0;
|
|
2664
|
+
const items = (rawItems ?? []).map(unwrapSearchResult).filter((node) => Boolean(node));
|
|
2665
|
+
if (items.length === 0) {
|
|
2666
|
+
return rawItems ? `# ${title} (0 results)` : void 0;
|
|
2667
|
+
}
|
|
2668
|
+
const state = { bounded: false, omitted: 0 };
|
|
2669
|
+
const sections = groupedNodeSections(items, state);
|
|
2670
|
+
const total = num(payload.total_count);
|
|
2671
|
+
const truncations = Array.isArray(payload.truncation_reasons) ? payload.truncation_reasons.filter((reason) => typeof reason === "string") : [];
|
|
2672
|
+
const notes = [];
|
|
2673
|
+
if (payload.has_more === true) notes.push("more results exist");
|
|
2674
|
+
if (truncations.length > 0) notes.push(`truncated: ${truncations.join(", ")}`);
|
|
2675
|
+
if (notes.length > 0) sections.push({ heading: `Note: ${notes.join("; ")}.`, items: [] });
|
|
2676
|
+
const count = total !== void 0 && total !== items.length ? `${items.length} of ${total}` : `${items.length}`;
|
|
2677
|
+
return assemble(`# ${title} (${count})`, sections, state);
|
|
2789
2678
|
}
|
|
2790
|
-
function
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
30,
|
|
2816
|
-
harness === "codex" ? 1e4 : void 0
|
|
2817
|
-
)
|
|
2818
|
-
]
|
|
2819
|
-
}
|
|
2820
|
-
],
|
|
2821
|
-
PreToolUse: [
|
|
2822
|
-
{
|
|
2823
|
-
matcher: "mcp__mindgraph__.*",
|
|
2824
|
-
// Repository resolution over a large multi-repo workspace plus three
|
|
2825
|
-
// git probes can exceed 3s on the first (uncached) call; a killed
|
|
2826
|
-
// PreToolUse means the call goes through untagged.
|
|
2827
|
-
hooks: [ownedHook(harness, 5)]
|
|
2828
|
-
}
|
|
2829
|
-
],
|
|
2830
|
-
// PostToolUse awaits the lease renewal round-trip, which fires exactly
|
|
2831
|
-
// when the tenant is cold (post-idle reclaim) — 5s killed it mid-claim.
|
|
2832
|
-
// Claude Code: only the tools the hook acts on (MindGraph calls, edits,
|
|
2833
|
-
// and the shell/agent calls that keep a lease alive). Read, Grep and Glob
|
|
2834
|
-
// are most of a session's tool calls and the hook did nothing useful for
|
|
2835
|
-
// them but start a process (roadmap H8). Codex names its tools
|
|
2836
|
-
// differently, so it keeps the catch-all.
|
|
2837
|
-
PostToolUse: [
|
|
2838
|
-
{
|
|
2839
|
-
matcher: harness === "claude-code" ? CLAUDE_POST_TOOL_MATCHER : ".*",
|
|
2840
|
-
hooks: [ownedHook(harness, 10)]
|
|
2841
|
-
}
|
|
2842
|
-
],
|
|
2843
|
-
Stop: [{ hooks: [ownedHook(harness, 5)] }],
|
|
2844
|
-
// SessionEnd makes up to two sequential cloud calls (abandon_iteration +
|
|
2845
|
-
// session close) — the SAME cold-tenant latency SessionStart needs 30s
|
|
2846
|
-
// for. Claude honors the configured budget; Codex clamps SessionEnd to a
|
|
2847
|
-
// 3s platform cap regardless of this value (see codex-hooks.ts wire
|
|
2848
|
-
// map), so the codex adapter instead makes at most ONE call there.
|
|
2849
|
-
SessionEnd: [
|
|
2850
|
-
{ hooks: [ownedHook(harness, harness === "codex" ? 3 : 30)] }
|
|
2851
|
-
]
|
|
2852
|
-
};
|
|
2853
|
-
if (harness !== "claude-code") return common;
|
|
2854
|
-
return {
|
|
2855
|
-
...common,
|
|
2856
|
-
TaskCreated: [{ hooks: [ownedHook(harness, 3)] }],
|
|
2857
|
-
TaskCompleted: [{ hooks: [ownedHook(harness, 3)] }],
|
|
2858
|
-
// Bounded recall per prompt: the core gives up at 4s and fails open, so
|
|
2859
|
-
// a 5s budget never holds the prompt longer than that.
|
|
2860
|
-
...features.promptRecall !== false ? { UserPromptSubmit: [{ hooks: [ownedHook(harness, 5)] }] } : {},
|
|
2861
|
-
// Opt-in transcript capture before compaction (cold-tenant ingest).
|
|
2862
|
-
...features.capture === true ? { PreCompact: [{ hooks: [ownedHook(harness, 30)] }] } : {}
|
|
2863
|
-
};
|
|
2864
|
-
}
|
|
2865
|
-
function hookCommand(value) {
|
|
2866
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2867
|
-
const command2 = value.command;
|
|
2868
|
-
return typeof command2 === "string" ? command2 : void 0;
|
|
2869
|
-
}
|
|
2870
|
-
function entryHasOwnedHook(value) {
|
|
2871
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2872
|
-
const hooks = value.hooks;
|
|
2873
|
-
return Array.isArray(hooks) && hooks.some((hook) => hookCommand(hook)?.includes(OWNER_MARKER));
|
|
2874
|
-
}
|
|
2875
|
-
function writeSettings(file, settings) {
|
|
2876
|
-
import_node_fs6.default.mkdirSync(import_node_path5.default.dirname(file), { recursive: true });
|
|
2877
|
-
const temporary = `${file}.${process.pid}.${import_node_crypto6.default.randomUUID()}.tmp`;
|
|
2878
|
-
import_node_fs6.default.writeFileSync(temporary, `${JSON.stringify(settings, null, 2)}
|
|
2879
|
-
`, {
|
|
2880
|
-
mode: 384
|
|
2679
|
+
function renderJobs(response) {
|
|
2680
|
+
const jobs = Array.isArray(response) ? response : obj(response)?.jobs;
|
|
2681
|
+
if (!Array.isArray(jobs)) return void 0;
|
|
2682
|
+
if (jobs.length === 0) return "No ingestion jobs.";
|
|
2683
|
+
const state = { bounded: false, omitted: 0 };
|
|
2684
|
+
const sorted = jobs.map((raw) => obj(raw)).filter((job) => Boolean(job)).sort((a, b) => (num(b.created_at) ?? 0) - (num(a.created_at) ?? 0));
|
|
2685
|
+
const shown = sorted.slice(0, JOBS_RENDER_MAX);
|
|
2686
|
+
if (shown.length < sorted.length) state.bounded = true;
|
|
2687
|
+
const items = shown.map((job) => {
|
|
2688
|
+
const progress = obj(job.progress);
|
|
2689
|
+
const failure = obj(job.error_details);
|
|
2690
|
+
const message = str(failure?.message) ?? str(job.error);
|
|
2691
|
+
const done = num(progress?.processed_chunks);
|
|
2692
|
+
const totalChunks = num(progress?.total_chunks);
|
|
2693
|
+
const bits = [
|
|
2694
|
+
str(job.status) || "?",
|
|
2695
|
+
done !== void 0 && totalChunks !== void 0 ? `${done}/${totalChunks} chunks` : void 0,
|
|
2696
|
+
num(progress?.nodes_created) !== void 0 ? `${num(progress?.nodes_created)} nodes` : void 0,
|
|
2697
|
+
num(job.queue_position) !== void 0 ? `queue #${num(job.queue_position)}` : void 0,
|
|
2698
|
+
message ? `error: ${clip(message, 160, state)}` : void 0,
|
|
2699
|
+
str(failure?.code) ? `code: ${clip(failure?.code, 80, state)}` : void 0,
|
|
2700
|
+
num(failure?.status) !== void 0 ? `status: ${num(failure?.status)}` : void 0,
|
|
2701
|
+
typeof failure?.retriable === "boolean" ? `retriable: ${failure.retriable}` : void 0
|
|
2702
|
+
].filter(Boolean);
|
|
2703
|
+
return `- [${str(job.id) || "?"}] ${clip(job.title, 120, state) || "(untitled)"} \u2014 ${bits.join(", ")}`;
|
|
2881
2704
|
});
|
|
2882
|
-
|
|
2883
|
-
}
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
const hooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
|
|
2888
|
-
let added = 0;
|
|
2889
|
-
let updated = 0;
|
|
2890
|
-
const desired = desiredEntries(harness, features);
|
|
2891
|
-
for (const optional of ["UserPromptSubmit", "PreCompact"]) {
|
|
2892
|
-
if (optional in desired || !Array.isArray(hooks[optional])) continue;
|
|
2893
|
-
const kept = hooks[optional].filter(
|
|
2894
|
-
(entry) => !entryHasOwnedHook(entry)
|
|
2895
|
-
);
|
|
2896
|
-
if (kept.length > 0) hooks[optional] = kept;
|
|
2897
|
-
else delete hooks[optional];
|
|
2898
|
-
}
|
|
2899
|
-
for (const [event, wanted] of Object.entries(desired)) {
|
|
2900
|
-
const current = Array.isArray(hooks[event]) ? [...hooks[event]] : [];
|
|
2901
|
-
const kept = current.filter((entry) => !entryHasOwnedHook(entry));
|
|
2902
|
-
const hadOwned = kept.length !== current.length;
|
|
2903
|
-
for (const entry of wanted) {
|
|
2904
|
-
kept.push(entry);
|
|
2905
|
-
if (hadOwned) updated += 1;
|
|
2906
|
-
else added += 1;
|
|
2907
|
-
}
|
|
2908
|
-
hooks[event] = kept;
|
|
2909
|
-
}
|
|
2910
|
-
settings.hooks = hooks;
|
|
2911
|
-
writeSettings(file, settings);
|
|
2912
|
-
return { path: file, added, updated };
|
|
2705
|
+
return assemble(
|
|
2706
|
+
`# Ingestion jobs (${shown.length}${shown.length < sorted.length ? ` of ${sorted.length}` : ""})`,
|
|
2707
|
+
[{ heading: "", items }],
|
|
2708
|
+
state
|
|
2709
|
+
);
|
|
2913
2710
|
}
|
|
2914
|
-
function
|
|
2915
|
-
const
|
|
2916
|
-
if (!
|
|
2917
|
-
const
|
|
2918
|
-
const
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
}
|
|
2928
|
-
const
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
}
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
return !owned;
|
|
2937
|
-
});
|
|
2938
|
-
if (retainedHooks.length > 0) {
|
|
2939
|
-
entry.hooks = retainedHooks;
|
|
2940
|
-
retainedEntries.push(entry);
|
|
2941
|
-
}
|
|
2711
|
+
function renderSignals(response) {
|
|
2712
|
+
const payload = obj(response);
|
|
2713
|
+
if (!payload) return void 0;
|
|
2714
|
+
const state = { bounded: false, omitted: 0 };
|
|
2715
|
+
const sections = [];
|
|
2716
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
2717
|
+
if (!Array.isArray(value) || value.length === 0) continue;
|
|
2718
|
+
const items = value.map((raw) => obj(raw)).filter((item) => Boolean(item)).map((item) => {
|
|
2719
|
+
const fromLabel = str(item.from_label);
|
|
2720
|
+
const toLabel = str(item.to_label);
|
|
2721
|
+
const anyLabelKey = Object.keys(item).find(
|
|
2722
|
+
(k) => k.endsWith("_label") && str(item[k])
|
|
2723
|
+
);
|
|
2724
|
+
const label = str(item.label) ?? str(item.title) ?? str(item.name) ?? (fromLabel && toLabel ? `${fromLabel} \u2194 ${toLabel}` : void 0) ?? (anyLabelKey ? str(item[anyLabelKey]) : void 0) ?? str(item.uid) ?? str(item.target_uid) ?? "(item)";
|
|
2725
|
+
const uid = str(item.uid) ?? str(item.target_uid);
|
|
2726
|
+
const numbers = Object.entries(item).filter(([, v]) => typeof v === "number").slice(0, 4).map(([k, v]) => `${k} ${Math.round(v * 100) / 100}`);
|
|
2727
|
+
const lists = Object.entries(item).filter(([, v]) => Array.isArray(v) && v.length > 0).slice(0, 2).map(([k, v]) => `${k}: ${v.length}`);
|
|
2728
|
+
const meta = [...numbers, ...lists].join(", ");
|
|
2729
|
+
return `- ${clip(label, 160, state)}${uid && uid !== label ? ` [${uid}]` : ""}${meta ? ` (${meta})` : ""}`;
|
|
2730
|
+
});
|
|
2731
|
+
if (items.length > 0) {
|
|
2732
|
+
sections.push({ heading: `## ${key.replace(/_/g, " ")}`, items });
|
|
2942
2733
|
}
|
|
2943
|
-
if (retainedEntries.length > 0) hooks[event] = retainedEntries;
|
|
2944
|
-
else delete hooks[event];
|
|
2945
2734
|
}
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
return { path: file, removed };
|
|
2949
|
-
}
|
|
2950
|
-
function installClaudeHooks(scope, projectDir = process.cwd(), features = {}) {
|
|
2951
|
-
return installHooks("claude-code", scope, projectDir, void 0, void 0, features);
|
|
2952
|
-
}
|
|
2953
|
-
function uninstallClaudeHooks(scope, projectDir = process.cwd()) {
|
|
2954
|
-
return uninstallHooks("claude-code", scope, projectDir);
|
|
2955
|
-
}
|
|
2956
|
-
function installCodexHooks(scope, projectDir = process.cwd(), codexHomeDir) {
|
|
2957
|
-
return installHooks("codex", scope, projectDir, codexHomeDir);
|
|
2958
|
-
}
|
|
2959
|
-
function uninstallCodexHooks(scope, projectDir = process.cwd(), codexHomeDir) {
|
|
2960
|
-
return uninstallHooks("codex", scope, projectDir, codexHomeDir);
|
|
2735
|
+
if (sections.length === 0) return void 0;
|
|
2736
|
+
return assemble("# Synthesis signals", sections, state);
|
|
2961
2737
|
}
|
|
2962
|
-
var
|
|
2963
|
-
var
|
|
2964
|
-
"src/
|
|
2738
|
+
var LABEL_MAX, SUMMARY_MAX, QUOTE_MAX, ARTICLE_MAX, CHUNK_MAX, RELATIONSHIP_MAX, RENDER_CHAR_BUDGET, MIN_RENDER_CHAR_BUDGET, BOUNDED_MARKER_RESERVE, CURATION_EDGE_TYPES, INFRA_NODE_TYPES, JOBS_RENDER_MAX;
|
|
2739
|
+
var init_render = __esm({
|
|
2740
|
+
"src/render.ts"() {
|
|
2965
2741
|
"use strict";
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2742
|
+
LABEL_MAX = 200;
|
|
2743
|
+
SUMMARY_MAX = 400;
|
|
2744
|
+
QUOTE_MAX = 180;
|
|
2745
|
+
ARTICLE_MAX = 6e3;
|
|
2746
|
+
CHUNK_MAX = 6e3;
|
|
2747
|
+
RELATIONSHIP_MAX = 40;
|
|
2748
|
+
RENDER_CHAR_BUDGET = 24e3;
|
|
2749
|
+
MIN_RENDER_CHAR_BUDGET = 512;
|
|
2750
|
+
BOUNDED_MARKER_RESERVE = 220;
|
|
2751
|
+
CURATION_EDGE_TYPES = /* @__PURE__ */ new Set(["POSSIBLE_DUPLICATE", "PossibleDuplicate"]);
|
|
2752
|
+
INFRA_NODE_TYPES = /* @__PURE__ */ new Set(["Chunk", "Document", "Article"]);
|
|
2753
|
+
JOBS_RENDER_MAX = 20;
|
|
2975
2754
|
}
|
|
2976
2755
|
});
|
|
2977
2756
|
|
|
2978
|
-
// src/
|
|
2979
|
-
function
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
function num(value) {
|
|
2986
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2757
|
+
// src/error-detail.ts
|
|
2758
|
+
function errorDetail(e) {
|
|
2759
|
+
if (e instanceof MindGraphError && e.body !== void 0) {
|
|
2760
|
+
const detail = typeof e.body === "string" ? e.body : JSON.stringify(e.body);
|
|
2761
|
+
return `${e.message} \u2014 ${detail}`;
|
|
2762
|
+
}
|
|
2763
|
+
return e instanceof Error ? e.message : String(e);
|
|
2987
2764
|
}
|
|
2988
|
-
function
|
|
2989
|
-
|
|
2765
|
+
function conflictState(e) {
|
|
2766
|
+
if (!(e instanceof MindGraphError) || !e.body || typeof e.body !== "object") {
|
|
2767
|
+
return {};
|
|
2768
|
+
}
|
|
2769
|
+
const body = e.body;
|
|
2770
|
+
const lifted = {};
|
|
2771
|
+
for (const key of CONFLICT_STATE_KEYS) {
|
|
2772
|
+
if (body[key] !== void 0) lifted[key] = body[key];
|
|
2773
|
+
}
|
|
2774
|
+
return lifted;
|
|
2990
2775
|
}
|
|
2991
|
-
function
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
}
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
const node = obj(value);
|
|
3007
|
-
return Boolean(
|
|
3008
|
-
node && str2(node.uid) && (str2(node.label) || str2(node.node_type) || obj(node.node_type))
|
|
3009
|
-
);
|
|
3010
|
-
}
|
|
3011
|
-
function nodeDisplayType(node) {
|
|
3012
|
-
const props = obj(node.props);
|
|
3013
|
-
const plain = str2(node.node_type);
|
|
3014
|
-
return (plain && plain !== "Custom" ? plain : void 0) ?? str2(obj(node.node_type)?.Custom) ?? str2(props?.object_type) ?? str2(props?.type_name) ?? str2(obj(props?.data)?.domain_type) ?? plain ?? "Node";
|
|
3015
|
-
}
|
|
3016
|
-
function nodeFields(node) {
|
|
3017
|
-
const props = obj(node.props);
|
|
3018
|
-
return obj(props?.fields) ?? obj(obj(props?.data)?.fields);
|
|
2776
|
+
function toolError(e, fallbackCode) {
|
|
2777
|
+
const body = e instanceof MindGraphError && e.body && typeof e.body === "object" && !Array.isArray(e.body) ? e.body : {};
|
|
2778
|
+
const code = typeof body.code === "string" ? body.code : fallbackCode;
|
|
2779
|
+
const payload = {
|
|
2780
|
+
error: errorDetail(e),
|
|
2781
|
+
...conflictState(e),
|
|
2782
|
+
...e instanceof MindGraphError ? { status: e.status } : {},
|
|
2783
|
+
...code !== void 0 ? { code } : {},
|
|
2784
|
+
...typeof body.retriable === "boolean" ? { retriable: body.retriable } : {}
|
|
2785
|
+
};
|
|
2786
|
+
return {
|
|
2787
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
2788
|
+
structuredContent: payload,
|
|
2789
|
+
isError: true
|
|
2790
|
+
};
|
|
3019
2791
|
}
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
2792
|
+
var CONFLICT_STATE_KEYS;
|
|
2793
|
+
var init_error_detail = __esm({
|
|
2794
|
+
"src/error-detail.ts"() {
|
|
2795
|
+
"use strict";
|
|
2796
|
+
init_dist();
|
|
2797
|
+
CONFLICT_STATE_KEYS = [
|
|
2798
|
+
"current_version",
|
|
2799
|
+
"current_epoch",
|
|
2800
|
+
"lease_expires_at",
|
|
2801
|
+
"lease_owner_agent_id"
|
|
2802
|
+
];
|
|
3027
2803
|
}
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
2804
|
+
});
|
|
2805
|
+
|
|
2806
|
+
// src/code-tool.ts
|
|
2807
|
+
async function handleCodeTool(client2, args, adapter = new CodegraphAdapter({ cwd: invocationCwd(args) })) {
|
|
2808
|
+
const codeClient = client2;
|
|
2809
|
+
const action = args.action;
|
|
2810
|
+
const refs = Array.isArray(args.code_refs) ? args.code_refs : [];
|
|
2811
|
+
const anchorUids = Array.isArray(args.anchor_uids) ? args.anchor_uids : [];
|
|
2812
|
+
const agentId = typeof args.agent_id === "string" ? args.agent_id : "mcp";
|
|
2813
|
+
const fallbackSpaceUid = typeof args.repo_space_uid === "string" ? args.repo_space_uid : void 0;
|
|
2814
|
+
const options = {
|
|
2815
|
+
repo: typeof args.repo === "string" ? args.repo : void 0,
|
|
2816
|
+
fallbackSpaceUid
|
|
2817
|
+
};
|
|
2818
|
+
const limit = typeof args.limit === "number" ? args.limit : void 0;
|
|
2819
|
+
try {
|
|
2820
|
+
switch (action) {
|
|
2821
|
+
case "anchor": {
|
|
2822
|
+
if (refs.length === 0) return err("missing_code_refs", "code_refs is required for anchor");
|
|
2823
|
+
const resolution = await adapter.resolveRefs(refs, options);
|
|
2824
|
+
const resolved = resolution.results.flatMap(
|
|
2825
|
+
(result) => result.resolved && result.repository ? [{ ref: result.resolved, repository: result.repository }] : []
|
|
2826
|
+
);
|
|
2827
|
+
const unresolved = resolution.results.filter((result) => !result.resolved);
|
|
2828
|
+
if (resolved.length === 0) {
|
|
2829
|
+
return ok({ ...resolution, anchors: [], wrote: false });
|
|
2830
|
+
}
|
|
2831
|
+
const missingSpace = resolved.find(
|
|
2832
|
+
({ repository }) => typeof args.repo_space_uid !== "string" && !repository.spaceUid
|
|
2833
|
+
);
|
|
2834
|
+
if (missingSpace) {
|
|
2835
|
+
return err(
|
|
2836
|
+
"missing_repository_space",
|
|
2837
|
+
`repo_space_uid, a workspace space_uid, or MINDGRAPH_CODE_SPACE_UID is required for ${missingSpace.repository.repoId}`
|
|
2838
|
+
);
|
|
2839
|
+
}
|
|
2840
|
+
const repositoryAnchors = /* @__PURE__ */ new Map();
|
|
2841
|
+
for (const { repository } of resolved) {
|
|
2842
|
+
if (repositoryAnchors.has(repository.repoRoot)) continue;
|
|
2843
|
+
const spaceUid = typeof args.repo_space_uid === "string" ? args.repo_space_uid : repository.spaceUid;
|
|
2844
|
+
const anchored = await anchorRepository(
|
|
2845
|
+
codeClient,
|
|
2846
|
+
repository,
|
|
2847
|
+
spaceUid,
|
|
2848
|
+
agentId
|
|
2849
|
+
);
|
|
2850
|
+
repositoryAnchors.set(repository.repoRoot, {
|
|
2851
|
+
...anchored,
|
|
2852
|
+
repository
|
|
2853
|
+
});
|
|
2854
|
+
}
|
|
2855
|
+
const anchors = [];
|
|
2856
|
+
for (const { ref, repository } of resolved) {
|
|
2857
|
+
const repositoryAnchor = repositoryAnchors.get(repository.repoRoot);
|
|
2858
|
+
if (!repositoryAnchor.uid) continue;
|
|
2859
|
+
const spaceUid = typeof args.repo_space_uid === "string" ? args.repo_space_uid : repository.spaceUid;
|
|
2860
|
+
const anchor = await anchorCodeRef(
|
|
2861
|
+
codeClient,
|
|
2862
|
+
ref,
|
|
2863
|
+
repositoryAnchor.uid,
|
|
2864
|
+
spaceUid,
|
|
2865
|
+
agentId
|
|
2866
|
+
);
|
|
2867
|
+
anchors.push(anchor);
|
|
2868
|
+
}
|
|
2869
|
+
return ok({
|
|
2870
|
+
availability: resolution.availability,
|
|
2871
|
+
repository: repositoryAnchors.values().next().value,
|
|
2872
|
+
repositories: [...repositoryAnchors.values()],
|
|
2873
|
+
anchors,
|
|
2874
|
+
unresolved,
|
|
2875
|
+
wrote: anchors.some((anchor) => anchor.created)
|
|
2876
|
+
});
|
|
2877
|
+
}
|
|
2878
|
+
case "recall": {
|
|
2879
|
+
const materialized = new Set(anchorUids);
|
|
2880
|
+
let resolution;
|
|
2881
|
+
if (refs.length > 0) {
|
|
2882
|
+
resolution = await adapter.resolveRefs(refs, options);
|
|
2883
|
+
for (const result of resolution.results) {
|
|
2884
|
+
if (!result.resolved) continue;
|
|
2885
|
+
const uid = await resolveMaterializedUid(codeClient, result.resolved, agentId);
|
|
2886
|
+
if (uid) materialized.add(uid);
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
if (materialized.size === 0) {
|
|
2890
|
+
return ok({
|
|
2891
|
+
availability: resolution?.availability,
|
|
2892
|
+
anchors: [],
|
|
2893
|
+
context: [],
|
|
2894
|
+
unresolved: resolution?.results.filter((result) => !result.resolved) ?? []
|
|
2895
|
+
});
|
|
2896
|
+
}
|
|
2897
|
+
const context = await Promise.all(
|
|
2898
|
+
[...materialized].map(async (uid) => ({
|
|
2899
|
+
anchor_uid: uid,
|
|
2900
|
+
graph: await codeClient.traverse({
|
|
2901
|
+
action: "neighborhood",
|
|
2902
|
+
start_uid: uid,
|
|
2903
|
+
depth: 2,
|
|
2904
|
+
max_nodes: Math.min(limit ?? 20, 20)
|
|
2905
|
+
})
|
|
2906
|
+
}))
|
|
2907
|
+
);
|
|
2908
|
+
return ok({
|
|
2909
|
+
availability: resolution?.availability,
|
|
2910
|
+
anchors: [...materialized],
|
|
2911
|
+
context,
|
|
2912
|
+
unresolved: resolution?.results.filter((result) => !result.resolved) ?? []
|
|
2913
|
+
});
|
|
2914
|
+
}
|
|
2915
|
+
case "expand": {
|
|
2916
|
+
const target = await resolveSingleTarget(
|
|
2917
|
+
codeClient,
|
|
2918
|
+
adapter,
|
|
2919
|
+
refs,
|
|
2920
|
+
anchorUids,
|
|
2921
|
+
options
|
|
2922
|
+
);
|
|
2923
|
+
if ("error" in target) return target.error;
|
|
2924
|
+
const expanded = await adapter.expand(target.ref, limit);
|
|
2925
|
+
const joined = await joinMaterialized(
|
|
2926
|
+
codeClient,
|
|
2927
|
+
[...expanded.callers, ...expanded.callees],
|
|
2928
|
+
agentId,
|
|
2929
|
+
Math.min(limit ?? 20, 20)
|
|
2930
|
+
);
|
|
2931
|
+
return ok({ anchor: target.ref, ...expanded, materialized: joined });
|
|
2932
|
+
}
|
|
2933
|
+
case "affected": {
|
|
2934
|
+
const targetUid = typeof args.task_uid === "string" ? args.task_uid : typeof args.execution_uid === "string" ? args.execution_uid : void 0;
|
|
2935
|
+
let graphContext;
|
|
2936
|
+
const graphRefs = [];
|
|
2937
|
+
let unavailableRepositories = [];
|
|
2938
|
+
if (targetUid) {
|
|
2939
|
+
graphContext = await codeClient.traverse({
|
|
2940
|
+
action: "neighborhood",
|
|
2941
|
+
start_uid: targetUid,
|
|
2942
|
+
depth: 2,
|
|
2943
|
+
max_nodes: 50
|
|
2944
|
+
});
|
|
2945
|
+
const fromGraph = await codeRefsFromGraphContext(
|
|
2946
|
+
codeClient,
|
|
2947
|
+
adapter,
|
|
2948
|
+
graphContext
|
|
2949
|
+
);
|
|
2950
|
+
graphRefs.push(...fromGraph.refs);
|
|
2951
|
+
unavailableRepositories = fromGraph.unavailable;
|
|
2952
|
+
}
|
|
2953
|
+
if (refs.length > 0 || anchorUids.length > 0) {
|
|
2954
|
+
const target = await resolveSingleTarget(
|
|
2955
|
+
codeClient,
|
|
2956
|
+
adapter,
|
|
2957
|
+
refs,
|
|
2958
|
+
anchorUids,
|
|
2959
|
+
options
|
|
2960
|
+
);
|
|
2961
|
+
if ("error" in target) return target.error;
|
|
2962
|
+
graphRefs.unshift(target.ref);
|
|
2963
|
+
}
|
|
2964
|
+
const unique = uniqueRefs(graphRefs);
|
|
2965
|
+
if (unique.length === 0) {
|
|
2966
|
+
return ok({
|
|
2967
|
+
target_uid: targetUid,
|
|
2968
|
+
graph_context: graphContext,
|
|
2969
|
+
affected: [],
|
|
2970
|
+
degradation: unavailableRepositories.length > 0 ? `attached code anchors reference repositories unavailable here: ${unavailableRepositories.join(", ")}` : "no materialized code anchors were attached",
|
|
2971
|
+
...unavailableRepositories.length > 0 ? { unavailable_repositories: unavailableRepositories } : {}
|
|
2972
|
+
});
|
|
2973
|
+
}
|
|
2974
|
+
const impacts = await Promise.all(
|
|
2975
|
+
unique.map(async (ref) => ({ anchor: ref, ...await adapter.affected(ref, limit) }))
|
|
2976
|
+
);
|
|
2977
|
+
const structural = uniqueRefs(impacts.flatMap((impact) => impact.affected));
|
|
2978
|
+
const joined = await joinMaterialized(
|
|
2979
|
+
codeClient,
|
|
2980
|
+
structural,
|
|
2981
|
+
agentId,
|
|
2982
|
+
Math.min(limit ?? 50, 50)
|
|
2983
|
+
);
|
|
2984
|
+
return ok({
|
|
2985
|
+
target_uid: targetUid,
|
|
2986
|
+
graph_context: graphContext,
|
|
2987
|
+
impacts,
|
|
2988
|
+
materialized: joined,
|
|
2989
|
+
...unavailableRepositories.length > 0 ? { unavailable_repositories: unavailableRepositories } : {}
|
|
2990
|
+
});
|
|
2991
|
+
}
|
|
2992
|
+
default:
|
|
2993
|
+
return err("unknown_action", `unknown mindgraph_code action: ${action}`);
|
|
2994
|
+
}
|
|
2995
|
+
} catch (cause) {
|
|
2996
|
+
if (cause instanceof UnknownRepositoryError) {
|
|
2997
|
+
return err("unknown_repository", cause.message);
|
|
2998
|
+
}
|
|
2999
|
+
return toolError(cause, "code_tool_failed");
|
|
3036
3000
|
}
|
|
3037
|
-
const truth = str2(node.truth_status) ?? str2(obj(node.props)?.truth_status);
|
|
3038
|
-
if (truth && truth !== "accepted") tags.push(`truth: ${truth}`);
|
|
3039
|
-
if (node.currently_valid === false) tags.push("OUTSIDE ITS VALIDITY WINDOW");
|
|
3040
|
-
if (node.valid_at_time === false) tags.push("NOT VALID AT THE REQUESTED TIME");
|
|
3041
|
-
return tags.length > 0 ? ` [${tags.join("; ")}]` : "";
|
|
3042
3001
|
}
|
|
3043
|
-
function
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3002
|
+
async function attachCodeRefsToToolResult(client2, result, args, adapter) {
|
|
3003
|
+
if (result.isError || !Array.isArray(args.code_refs) || args.code_refs.length === 0) {
|
|
3004
|
+
return result;
|
|
3005
|
+
}
|
|
3006
|
+
const original = JSON.parse(result.content[0]?.text ?? "null");
|
|
3007
|
+
const targetUids = primaryUids(original);
|
|
3008
|
+
if (targetUids.length === 0) {
|
|
3009
|
+
return ok({
|
|
3010
|
+
result: original,
|
|
3011
|
+
code_anchors: {
|
|
3012
|
+
wrote: false,
|
|
3013
|
+
caveat: "the write response exposed no primary UID to attach"
|
|
3014
|
+
}
|
|
3015
|
+
});
|
|
3016
|
+
}
|
|
3017
|
+
const anchorResult = await handleCodeTool(
|
|
3018
|
+
client2,
|
|
3019
|
+
{
|
|
3020
|
+
action: "anchor",
|
|
3021
|
+
code_refs: args.code_refs,
|
|
3022
|
+
repo: args.repo,
|
|
3023
|
+
repo_space_uid: args.repo_space_uid,
|
|
3024
|
+
agent_id: args.agent_id,
|
|
3025
|
+
// Keeps the default adapter anchored to the live session directory
|
|
3026
|
+
// rather than wherever the MCP server process was launched.
|
|
3027
|
+
invocation_context: args.invocation_context
|
|
3028
|
+
},
|
|
3029
|
+
adapter
|
|
3030
|
+
);
|
|
3031
|
+
const anchors = JSON.parse(anchorResult.content[0]?.text ?? "null");
|
|
3032
|
+
if (!anchorResult.isError) {
|
|
3033
|
+
const codeClient = client2;
|
|
3034
|
+
const anchorUids = (anchors.anchors ?? []).flatMap(
|
|
3035
|
+
(anchor) => anchor.uid ? [anchor.uid] : []
|
|
3036
|
+
);
|
|
3037
|
+
const relateFailures = [];
|
|
3038
|
+
for (const targetUid of targetUids) {
|
|
3039
|
+
for (const anchorUid of anchorUids) {
|
|
3040
|
+
try {
|
|
3041
|
+
await codeClient.entity({
|
|
3042
|
+
action: "relate",
|
|
3043
|
+
source_uid: targetUid,
|
|
3044
|
+
target_uid: anchorUid,
|
|
3045
|
+
edge_type: args.action === "lesson" ? "ABOUT" : "RELEVANT_TO",
|
|
3046
|
+
agent_id: args.agent_id
|
|
3047
|
+
});
|
|
3048
|
+
} catch (cause) {
|
|
3049
|
+
relateFailures.push(
|
|
3050
|
+
`${targetUid} -> ${anchorUid}: ${errorDetail(cause)}`
|
|
3051
|
+
);
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
if (relateFailures.length > 0) {
|
|
3056
|
+
return ok({
|
|
3057
|
+
result: original,
|
|
3058
|
+
code_anchors: {
|
|
3059
|
+
...anchors,
|
|
3060
|
+
caveat: `anchors written, but ${relateFailures.length} relate edge(s) failed: ${relateFailures.join("; ")}`
|
|
3061
|
+
}
|
|
3062
|
+
});
|
|
3063
|
+
}
|
|
3049
3064
|
}
|
|
3050
|
-
return
|
|
3065
|
+
return ok({ result: original, code_anchors: anchors });
|
|
3051
3066
|
}
|
|
3052
|
-
function
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3067
|
+
async function anchorRepository(client2, repository, spaceUid, agentId) {
|
|
3068
|
+
return client2.entity({
|
|
3069
|
+
action: "create",
|
|
3070
|
+
label: repository.repoId,
|
|
3071
|
+
props: {
|
|
3072
|
+
entity_type: "repository",
|
|
3073
|
+
canonical_name: repository.repoId,
|
|
3074
|
+
identifiers: { repo_id: repository.repoId }
|
|
3075
|
+
},
|
|
3076
|
+
identity: {
|
|
3077
|
+
namespace: "external.code",
|
|
3078
|
+
key_version: 1,
|
|
3079
|
+
key: repositoryIdentityKey(repository)
|
|
3080
|
+
},
|
|
3081
|
+
identity_space_uid: spaceUid,
|
|
3082
|
+
agent_id: agentId
|
|
3083
|
+
});
|
|
3059
3084
|
}
|
|
3060
|
-
function
|
|
3061
|
-
const
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3085
|
+
async function anchorCodeRef(client2, ref, repositoryUid, spaceUid, agentId) {
|
|
3086
|
+
const response = await client2.entity({
|
|
3087
|
+
action: "create",
|
|
3088
|
+
label: ref.qualifiedName,
|
|
3089
|
+
props: {
|
|
3090
|
+
entity_type: ref.kind === "file" ? "code_file" : "code_symbol",
|
|
3091
|
+
canonical_name: ref.qualifiedName,
|
|
3092
|
+
identifiers: {
|
|
3093
|
+
repo_id: ref.repoId,
|
|
3094
|
+
path: ref.path,
|
|
3095
|
+
qualified_name: ref.qualifiedName
|
|
3096
|
+
},
|
|
3097
|
+
attributes: { code_ref: storedCodeRef(ref) }
|
|
3098
|
+
},
|
|
3099
|
+
identity: {
|
|
3100
|
+
namespace: "external.code",
|
|
3101
|
+
key_version: 1,
|
|
3102
|
+
key: codeRefIdentityKey(ref)
|
|
3103
|
+
},
|
|
3104
|
+
identity_space_uid: spaceUid,
|
|
3105
|
+
agent_id: agentId
|
|
3106
|
+
});
|
|
3107
|
+
if (response.uid && response.created !== false && response.status !== "existing") {
|
|
3108
|
+
await client2.entity({
|
|
3109
|
+
action: "relate",
|
|
3110
|
+
source_uid: response.uid,
|
|
3111
|
+
target_uid: repositoryUid,
|
|
3112
|
+
edge_type: "PART_OF",
|
|
3113
|
+
agent_id: agentId
|
|
3114
|
+
});
|
|
3115
|
+
}
|
|
3116
|
+
return { ...response, ref };
|
|
3066
3117
|
}
|
|
3067
|
-
function
|
|
3068
|
-
const
|
|
3069
|
-
|
|
3118
|
+
async function resolveMaterializedUid(client2, ref, agentId) {
|
|
3119
|
+
const response = await client2.entity({
|
|
3120
|
+
action: "resolve_identity",
|
|
3121
|
+
identity: {
|
|
3122
|
+
namespace: "external.code",
|
|
3123
|
+
key_version: 1,
|
|
3124
|
+
key: codeRefIdentityKey(ref)
|
|
3125
|
+
},
|
|
3126
|
+
agent_id: agentId
|
|
3127
|
+
});
|
|
3128
|
+
return response.status === "existing" ? response.uid : void 0;
|
|
3070
3129
|
}
|
|
3071
|
-
function
|
|
3072
|
-
const
|
|
3073
|
-
const
|
|
3074
|
-
|
|
3075
|
-
const
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
if (fields) lines.push(` - fields: ${fields}`);
|
|
3086
|
-
const quote = firstQuote(node, state);
|
|
3087
|
-
if (quote) lines.push(` - source quote: "${quote}"`);
|
|
3088
|
-
const source = sourceTag(node);
|
|
3089
|
-
if (source) lines.push(` - ${source}`);
|
|
3090
|
-
const believers = Array.isArray(node.believed_by) ? node.believed_by : [];
|
|
3091
|
-
if (believers.length > 0) {
|
|
3092
|
-
const rendered2 = believers.slice(0, 3).map((raw) => {
|
|
3093
|
-
const believer = obj(raw);
|
|
3094
|
-
const conf = num(believer?.confidence);
|
|
3095
|
-
return `${str2(believer?.agent_label) || "?"}${conf !== void 0 ? ` (${conf})` : ""}`;
|
|
3096
|
-
}).join(", ");
|
|
3097
|
-
lines.push(` - believed by: ${rendered2}`);
|
|
3130
|
+
async function codeRefFromAnchor(client2, adapter, uid) {
|
|
3131
|
+
const node = await client2.getNode(uid);
|
|
3132
|
+
const attributes = node.props?.attributes;
|
|
3133
|
+
if (!attributes || typeof attributes !== "object") return void 0;
|
|
3134
|
+
const codeRef = attributes.code_ref;
|
|
3135
|
+
if (!isStoredCodeRef(codeRef)) return void 0;
|
|
3136
|
+
try {
|
|
3137
|
+
const repository = await adapter.resolveRepository(codeRef.repoId);
|
|
3138
|
+
return { ref: { ...codeRef, repoRoot: repository.repoRoot } };
|
|
3139
|
+
} catch (cause) {
|
|
3140
|
+
if (cause instanceof UnknownRepositoryError) {
|
|
3141
|
+
return { missingRepo: codeRef.repoId };
|
|
3142
|
+
}
|
|
3143
|
+
throw cause;
|
|
3098
3144
|
}
|
|
3099
|
-
return lines.join("\n");
|
|
3100
3145
|
}
|
|
3101
|
-
function
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
const type = nodeDisplayType(node);
|
|
3105
|
-
const group = groups.get(type) ?? [];
|
|
3106
|
-
group.push(node);
|
|
3107
|
-
groups.set(type, group);
|
|
3146
|
+
async function resolveSingleTarget(client2, adapter, refs, anchorUids, options) {
|
|
3147
|
+
if (refs.length > 1 || anchorUids.length > 1) {
|
|
3148
|
+
return { error: err("ambiguous_target", "expand/affected accepts exactly one code target") };
|
|
3108
3149
|
}
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3150
|
+
if (anchorUids.length === 1) {
|
|
3151
|
+
const outcome = await codeRefFromAnchor(client2, adapter, anchorUids[0]);
|
|
3152
|
+
if (!outcome) {
|
|
3153
|
+
return { error: err("invalid_anchor", "anchor UID does not contain a typed code_ref") };
|
|
3154
|
+
}
|
|
3155
|
+
if ("missingRepo" in outcome) {
|
|
3156
|
+
return {
|
|
3157
|
+
error: err(
|
|
3158
|
+
"repository_unavailable",
|
|
3159
|
+
`repository ${outcome.missingRepo} is not checked out here or declared in .mindgraph/workspace.json; expand/affected need its local index`
|
|
3160
|
+
)
|
|
3161
|
+
};
|
|
3162
|
+
}
|
|
3163
|
+
return { ref: outcome.ref };
|
|
3117
3164
|
}
|
|
3118
|
-
|
|
3119
|
-
}
|
|
3120
|
-
function relationshipItems(edges, labelByUid, state, options) {
|
|
3121
|
-
const lines = [];
|
|
3122
|
-
for (const raw of edges) {
|
|
3123
|
-
const edge = obj(raw);
|
|
3124
|
-
if (!edge) continue;
|
|
3125
|
-
const type = edgeTypeName(edge.edge_type) || "?";
|
|
3126
|
-
const from = str2(edge.from_uid);
|
|
3127
|
-
const to = str2(edge.to_uid);
|
|
3128
|
-
if (CURATION_EDGE_TYPES.has(type)) continue;
|
|
3129
|
-
if (!from || !to || from === to) continue;
|
|
3130
|
-
const fromLabel = labelByUid.get(from) ?? (options?.fallbackToUid ? `[${from}]` : void 0);
|
|
3131
|
-
const toLabel = labelByUid.get(to) ?? (options?.fallbackToUid ? `[${to}]` : void 0);
|
|
3132
|
-
if (!fromLabel || !toLabel) continue;
|
|
3133
|
-
lines.push(`- ${fromLabel} \u2014${type}\u2192 ${toLabel}`);
|
|
3165
|
+
if (refs.length !== 1) {
|
|
3166
|
+
return { error: err("missing_code_ref", "one code_ref or anchor_uid is required") };
|
|
3134
3167
|
}
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3168
|
+
const resolution = await adapter.resolveRefs(refs, options);
|
|
3169
|
+
const result = resolution.results[0];
|
|
3170
|
+
if (!result.resolved) {
|
|
3171
|
+
return {
|
|
3172
|
+
error: ok({
|
|
3173
|
+
availability: resolution.availability,
|
|
3174
|
+
result,
|
|
3175
|
+
wrote: false
|
|
3176
|
+
})
|
|
3177
|
+
};
|
|
3140
3178
|
}
|
|
3141
|
-
return
|
|
3179
|
+
return { ref: result.resolved };
|
|
3142
3180
|
}
|
|
3143
|
-
function
|
|
3144
|
-
const
|
|
3145
|
-
const
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
if (
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3181
|
+
async function joinMaterialized(client2, refs, agentId, limit) {
|
|
3182
|
+
const joined = [];
|
|
3183
|
+
for (const ref of uniqueRefs(refs).slice(0, limit)) {
|
|
3184
|
+
const uid = await resolveMaterializedUid(client2, ref, agentId);
|
|
3185
|
+
if (!uid) continue;
|
|
3186
|
+
joined.push({
|
|
3187
|
+
ref,
|
|
3188
|
+
anchor_uid: uid,
|
|
3189
|
+
graph: await client2.traverse({
|
|
3190
|
+
action: "neighborhood",
|
|
3191
|
+
start_uid: uid,
|
|
3192
|
+
depth: 2,
|
|
3193
|
+
max_nodes: 20
|
|
3194
|
+
})
|
|
3195
|
+
});
|
|
3196
|
+
}
|
|
3197
|
+
return joined;
|
|
3198
|
+
}
|
|
3199
|
+
async function codeRefsFromGraphContext(client2, adapter, context) {
|
|
3200
|
+
const uids = /* @__PURE__ */ new Set();
|
|
3201
|
+
collectUids(context, uids);
|
|
3202
|
+
if (uids.size === 0) return { refs: [], unavailable: [] };
|
|
3203
|
+
const nodes = client2.getNodesBatch ? await client2.getNodesBatch([...uids]) : await Promise.all([...uids].map((uid) => client2.getNode(uid).catch(() => void 0)));
|
|
3204
|
+
const stored = nodes.flatMap((node) => {
|
|
3205
|
+
if (!node) return [];
|
|
3206
|
+
const attributes = node.props?.attributes;
|
|
3207
|
+
if (!attributes || typeof attributes !== "object") return [];
|
|
3208
|
+
const codeRef = attributes.code_ref;
|
|
3209
|
+
return isStoredCodeRef(codeRef) ? [codeRef] : [];
|
|
3210
|
+
});
|
|
3211
|
+
const refs = [];
|
|
3212
|
+
const unavailable = /* @__PURE__ */ new Set();
|
|
3213
|
+
for (const ref of stored) {
|
|
3214
|
+
try {
|
|
3215
|
+
const repository = await adapter.resolveRepository(ref.repoId);
|
|
3216
|
+
refs.push({ ...ref, repoRoot: repository.repoRoot });
|
|
3217
|
+
} catch (cause) {
|
|
3218
|
+
if (cause instanceof UnknownRepositoryError) {
|
|
3219
|
+
unavailable.add(ref.repoId);
|
|
3220
|
+
} else {
|
|
3221
|
+
throw cause;
|
|
3179
3222
|
}
|
|
3180
3223
|
}
|
|
3181
|
-
lines.push(
|
|
3182
|
-
section.items.length === 0 ? section.heading : section.heading ? `${section.heading}
|
|
3183
|
-
${kept.join("\n")}` : kept.join("\n")
|
|
3184
|
-
);
|
|
3185
|
-
used += headingCost + sectionUsed;
|
|
3186
3224
|
}
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3225
|
+
return { refs, unavailable: [...unavailable].sort() };
|
|
3226
|
+
}
|
|
3227
|
+
function collectUids(value, uids) {
|
|
3228
|
+
if (Array.isArray(value)) {
|
|
3229
|
+
value.forEach((item) => collectUids(item, uids));
|
|
3230
|
+
return;
|
|
3231
|
+
}
|
|
3232
|
+
if (!value || typeof value !== "object") return;
|
|
3233
|
+
for (const [key, child] of Object.entries(value)) {
|
|
3234
|
+
if (typeof child === "string" && ["uid", "node_uid", "neighbor_uid", "from_uid", "to_uid"].includes(key)) {
|
|
3235
|
+
uids.add(child);
|
|
3236
|
+
} else {
|
|
3237
|
+
collectUids(child, uids);
|
|
3238
|
+
}
|
|
3191
3239
|
}
|
|
3192
|
-
return lines.join("\n\n").slice(0, budget);
|
|
3193
3240
|
}
|
|
3194
|
-
function
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3241
|
+
function isStoredCodeRef(value) {
|
|
3242
|
+
if (!value || typeof value !== "object") return false;
|
|
3243
|
+
const ref = value;
|
|
3244
|
+
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";
|
|
3245
|
+
}
|
|
3246
|
+
function storedCodeRef(ref) {
|
|
3247
|
+
const { repoRoot: _localOnly, ...stored } = ref;
|
|
3248
|
+
return stored;
|
|
3249
|
+
}
|
|
3250
|
+
function uniqueRefs(refs) {
|
|
3251
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3252
|
+
return refs.filter((ref) => {
|
|
3253
|
+
const key = `${ref.repoId}:${ref.path}:${ref.qualifiedName}:${ref.signature ?? ""}`;
|
|
3254
|
+
if (seen.has(key)) return false;
|
|
3255
|
+
seen.add(key);
|
|
3256
|
+
return true;
|
|
3200
3257
|
});
|
|
3201
|
-
return [{ heading: "## Applicable policies", items }];
|
|
3202
3258
|
}
|
|
3203
|
-
function
|
|
3204
|
-
|
|
3205
|
-
|
|
3259
|
+
function primaryUids(value) {
|
|
3260
|
+
if (!value || typeof value !== "object") return [];
|
|
3261
|
+
const object2 = value;
|
|
3262
|
+
const keys = [
|
|
3263
|
+
"uid",
|
|
3264
|
+
"claim_uid",
|
|
3265
|
+
"goal_uid",
|
|
3266
|
+
"project_uid",
|
|
3267
|
+
"milestone_uid",
|
|
3268
|
+
"decision_uid",
|
|
3269
|
+
"task_uid",
|
|
3270
|
+
"plan_uid",
|
|
3271
|
+
"step_uid",
|
|
3272
|
+
"execution_uid",
|
|
3273
|
+
"lesson_uid"
|
|
3274
|
+
];
|
|
3206
3275
|
return [
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
}
|
|
3276
|
+
...new Set(
|
|
3277
|
+
keys.flatMap((key) => typeof object2[key] === "string" ? [object2[key]] : [])
|
|
3278
|
+
)
|
|
3211
3279
|
];
|
|
3212
3280
|
}
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
[
|
|
3221
|
-
}
|
|
3222
|
-
|
|
3281
|
+
var ok, err, CODE_TOOL;
|
|
3282
|
+
var init_code_tool = __esm({
|
|
3283
|
+
"src/code-tool.ts"() {
|
|
3284
|
+
"use strict";
|
|
3285
|
+
init_error_detail();
|
|
3286
|
+
init_codegraph();
|
|
3287
|
+
ok = (data) => ({
|
|
3288
|
+
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
3289
|
+
});
|
|
3290
|
+
err = (code, message, details) => ({
|
|
3291
|
+
content: [
|
|
3292
|
+
{
|
|
3293
|
+
type: "text",
|
|
3294
|
+
text: JSON.stringify({ error: message, code, details })
|
|
3295
|
+
}
|
|
3296
|
+
],
|
|
3297
|
+
isError: true
|
|
3298
|
+
});
|
|
3299
|
+
CODE_TOOL = {
|
|
3300
|
+
name: "mindgraph_code",
|
|
3301
|
+
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. Refs with a path route to the repository defining that file (nearest declared root in .mindgraph/workspace.json); bare symbols resolve against the current repository only \u2014 pass path or repo to target a sibling.",
|
|
3302
|
+
inputSchema: {
|
|
3303
|
+
type: "object",
|
|
3304
|
+
properties: {
|
|
3305
|
+
action: {
|
|
3306
|
+
type: "string",
|
|
3307
|
+
enum: ["anchor", "recall", "expand", "affected"]
|
|
3308
|
+
},
|
|
3309
|
+
code_refs: {
|
|
3310
|
+
type: "array",
|
|
3311
|
+
maxItems: 50,
|
|
3312
|
+
items: {
|
|
3313
|
+
oneOf: [
|
|
3314
|
+
{ type: "string" },
|
|
3315
|
+
{
|
|
3316
|
+
type: "object",
|
|
3317
|
+
properties: {
|
|
3318
|
+
repo: { type: "string" },
|
|
3319
|
+
path: { type: "string" },
|
|
3320
|
+
line: { type: "integer", minimum: 1 },
|
|
3321
|
+
symbol: { type: "string" },
|
|
3322
|
+
kind: { type: "string" },
|
|
3323
|
+
language: { type: "string" },
|
|
3324
|
+
signature: { type: "string" }
|
|
3325
|
+
},
|
|
3326
|
+
additionalProperties: false
|
|
3327
|
+
}
|
|
3328
|
+
]
|
|
3329
|
+
}
|
|
3330
|
+
},
|
|
3331
|
+
anchor_uids: {
|
|
3332
|
+
type: "array",
|
|
3333
|
+
maxItems: 50,
|
|
3334
|
+
items: { type: "string" },
|
|
3335
|
+
description: "Already-materialized code anchor UIDs (codegraph is not required for recall)"
|
|
3336
|
+
},
|
|
3337
|
+
repo: {
|
|
3338
|
+
type: "string",
|
|
3339
|
+
description: "Configured repository id or repository root"
|
|
3340
|
+
},
|
|
3341
|
+
repo_space_uid: {
|
|
3342
|
+
type: "string",
|
|
3343
|
+
description: "Writable shared Project/repository Space for new anchors (anchor requires this, a workspace space_uid, or MINDGRAPH_CODE_SPACE_UID)"
|
|
3344
|
+
},
|
|
3345
|
+
task_uid: { type: "string" },
|
|
3346
|
+
execution_uid: { type: "string" },
|
|
3347
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
3348
|
+
agent_id: { type: "string" }
|
|
3349
|
+
},
|
|
3350
|
+
required: ["action"]
|
|
3351
|
+
},
|
|
3352
|
+
annotations: {
|
|
3353
|
+
readOnlyHint: false,
|
|
3354
|
+
destructiveHint: false,
|
|
3355
|
+
idempotentHint: true,
|
|
3356
|
+
openWorldHint: true
|
|
3357
|
+
}
|
|
3358
|
+
};
|
|
3223
3359
|
}
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
(
|
|
3238
|
-
${clip(article.content, ARTICLE_MAX, state) || ""}`
|
|
3360
|
+
});
|
|
3361
|
+
|
|
3362
|
+
// src/workspace-scope.ts
|
|
3363
|
+
function readScopeCache(cache) {
|
|
3364
|
+
if (!cache) return {};
|
|
3365
|
+
try {
|
|
3366
|
+
const parsed = JSON.parse(
|
|
3367
|
+
import_node_fs3.default.readFileSync(import_node_path3.default.join(cache.dir, SCOPE_CACHE_FILE), "utf8")
|
|
3368
|
+
);
|
|
3369
|
+
if (parsed.v !== 1 || parsed.tenant !== cache.tenant || !parsed.entries) return {};
|
|
3370
|
+
const now = (cache.now ?? Date.now)();
|
|
3371
|
+
return Object.fromEntries(
|
|
3372
|
+
Object.entries(parsed.entries).filter(
|
|
3373
|
+
([, entry]) => typeof entry?.uid === "string" && typeof entry.at === "number" && now - entry.at >= 0 && now - entry.at < SCOPE_CACHE_TTL_MS
|
|
3239
3374
|
)
|
|
3240
|
-
|
|
3375
|
+
);
|
|
3376
|
+
} catch {
|
|
3377
|
+
return {};
|
|
3241
3378
|
}
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3379
|
+
}
|
|
3380
|
+
function writeScopeCache(cache, entries) {
|
|
3381
|
+
if (!cache) return;
|
|
3382
|
+
try {
|
|
3383
|
+
import_node_fs3.default.mkdirSync(cache.dir, { recursive: true, mode: 448 });
|
|
3384
|
+
const file = import_node_path3.default.join(cache.dir, SCOPE_CACHE_FILE);
|
|
3385
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
3386
|
+
import_node_fs3.default.writeFileSync(
|
|
3387
|
+
temporary,
|
|
3388
|
+
JSON.stringify({ v: 1, tenant: cache.tenant, entries }),
|
|
3389
|
+
{ mode: 384 }
|
|
3250
3390
|
);
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
}
|
|
3254
|
-
} else if (infraOnly > 0) {
|
|
3255
|
-
sections.push({
|
|
3256
|
-
heading: `Note: ${infraOnly} document/chunk node(s) matched \u2014 use action "document_index" or include_chunks for their content.`,
|
|
3257
|
-
items: []
|
|
3258
|
-
});
|
|
3391
|
+
import_node_fs3.default.renameSync(temporary, file);
|
|
3392
|
+
} catch {
|
|
3259
3393
|
}
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3394
|
+
}
|
|
3395
|
+
function record(value) {
|
|
3396
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
3397
|
+
}
|
|
3398
|
+
async function resolveRepositoryScope(cwd, client2, agentId, options = {}) {
|
|
3399
|
+
if (!client2.entity) return { uids: [], names: {}, failures: [] };
|
|
3400
|
+
const adapter = new CodegraphAdapter({
|
|
3401
|
+
cwd,
|
|
3402
|
+
workspaceFile: options.workspaceFile,
|
|
3403
|
+
childWorkspaces: true
|
|
3404
|
+
});
|
|
3405
|
+
const configured = await adapter.resolveRepositories();
|
|
3406
|
+
const repositories = configured.length > 0 ? configured : [await adapter.resolveRepository()];
|
|
3407
|
+
const failures = [];
|
|
3408
|
+
const names = {};
|
|
3409
|
+
const cached = readScopeCache(options.cache);
|
|
3410
|
+
const fresh = {};
|
|
3411
|
+
const stamp = (options.cache?.now ?? Date.now)();
|
|
3412
|
+
const resolved = await Promise.all(
|
|
3413
|
+
repositories.map(async (repository) => {
|
|
3414
|
+
const remembered = cached[repository.repoId];
|
|
3415
|
+
if (remembered) {
|
|
3416
|
+
names[remembered.uid] = repository.repoId;
|
|
3417
|
+
return remembered.uid;
|
|
3418
|
+
}
|
|
3419
|
+
try {
|
|
3420
|
+
const payload = record(
|
|
3421
|
+
await client2.entity({
|
|
3422
|
+
action: "resolve_identity",
|
|
3423
|
+
identity: {
|
|
3424
|
+
namespace: "external.code",
|
|
3425
|
+
key_version: 1,
|
|
3426
|
+
key: repositoryIdentityKey(repository)
|
|
3427
|
+
},
|
|
3428
|
+
agent_id: agentId
|
|
3429
|
+
})
|
|
3430
|
+
);
|
|
3431
|
+
if (payload?.status === "existing" && typeof payload.uid === "string" && payload.uid) {
|
|
3432
|
+
names[payload.uid] = repository.repoId;
|
|
3433
|
+
fresh[repository.repoId] = { uid: payload.uid, at: stamp };
|
|
3434
|
+
return payload.uid;
|
|
3435
|
+
}
|
|
3436
|
+
if (payload?.status === "absent") {
|
|
3437
|
+
const anchored = await anchorRepository(
|
|
3438
|
+
client2,
|
|
3439
|
+
repository,
|
|
3440
|
+
repository.spaceUid ?? `space:agent:${agentId}`,
|
|
3441
|
+
agentId
|
|
3442
|
+
);
|
|
3443
|
+
if (anchored?.uid) {
|
|
3444
|
+
names[anchored.uid] = repository.repoId;
|
|
3445
|
+
fresh[repository.repoId] = { uid: anchored.uid, at: stamp };
|
|
3446
|
+
return anchored.uid;
|
|
3447
|
+
}
|
|
3448
|
+
failures.push(
|
|
3449
|
+
`${repository.repoId}: anchor ${anchored?.status ?? "failed"}`
|
|
3450
|
+
);
|
|
3451
|
+
return void 0;
|
|
3452
|
+
}
|
|
3453
|
+
failures.push(
|
|
3454
|
+
`${repository.repoId}: ${typeof payload?.status === "string" && payload.status ? payload.status : "unresolved"}`
|
|
3455
|
+
);
|
|
3456
|
+
return void 0;
|
|
3457
|
+
} catch (error2) {
|
|
3458
|
+
failures.push(
|
|
3459
|
+
`${repository.repoId}: ${error2?.message || "resolve failed"}`
|
|
3460
|
+
);
|
|
3461
|
+
return void 0;
|
|
3462
|
+
}
|
|
3463
|
+
})
|
|
3464
|
+
);
|
|
3465
|
+
if (Object.keys(fresh).length > 0) {
|
|
3466
|
+
writeScopeCache(options.cache, { ...cached, ...fresh });
|
|
3267
3467
|
}
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3468
|
+
return {
|
|
3469
|
+
uids: [...new Set(resolved.filter((uid) => Boolean(uid)))],
|
|
3470
|
+
names,
|
|
3471
|
+
failures
|
|
3472
|
+
};
|
|
3473
|
+
}
|
|
3474
|
+
var import_node_fs3, import_node_path3, WORKSPACE_RESUME_LIMIT, SCOPE_CACHE_TTL_MS, SCOPE_CACHE_FILE;
|
|
3475
|
+
var init_workspace_scope = __esm({
|
|
3476
|
+
"src/workspace-scope.ts"() {
|
|
3477
|
+
"use strict";
|
|
3478
|
+
import_node_fs3 = __toESM(require("fs"));
|
|
3479
|
+
import_node_path3 = __toESM(require("path"));
|
|
3480
|
+
init_codegraph();
|
|
3481
|
+
init_code_tool();
|
|
3482
|
+
WORKSPACE_RESUME_LIMIT = 9;
|
|
3483
|
+
SCOPE_CACHE_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
3484
|
+
SCOPE_CACHE_FILE = "repository-scopes.json";
|
|
3272
3485
|
}
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3486
|
+
});
|
|
3487
|
+
|
|
3488
|
+
// src/call-ledger.ts
|
|
3489
|
+
function callsDir(options) {
|
|
3490
|
+
return import_node_path4.default.join(
|
|
3491
|
+
options.runtimeDir || process.env.MINDGRAPH_RUNTIME_DIR || import_node_path4.default.join(import_node_os3.default.homedir(), ".mindgraph", "runtime"),
|
|
3492
|
+
"calls"
|
|
3493
|
+
);
|
|
3494
|
+
}
|
|
3495
|
+
function stableJson(value) {
|
|
3496
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
3497
|
+
if (value && typeof value === "object") {
|
|
3498
|
+
return `{${Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(",")}}`;
|
|
3280
3499
|
}
|
|
3281
|
-
return
|
|
3500
|
+
return JSON.stringify(value) ?? "null";
|
|
3282
3501
|
}
|
|
3283
|
-
function
|
|
3284
|
-
const
|
|
3285
|
-
|
|
3286
|
-
const value = obj(wrapper.node) ?? wrapper;
|
|
3287
|
-
if (isNodeShaped(value)) return nodeLines(value, state);
|
|
3288
|
-
const uid = str2(value.uid);
|
|
3289
|
-
const props = obj(value.props);
|
|
3290
|
-
const label = clip(value.label, LABEL_MAX, state) ?? clip(value.name, LABEL_MAX, state) ?? clip(value.title, LABEL_MAX, state) ?? clip(value.action, LABEL_MAX, state) ?? fallbackType;
|
|
3291
|
-
const status = str2(value.status) ?? str2(props?.status);
|
|
3292
|
-
const summary = clip(value.summary, SUMMARY_MAX, state) ?? clip(value.description, SUMMARY_MAX, state) ?? clip(value.content, SUMMARY_MAX, state) ?? clip(value.outcome_summary, SUMMARY_MAX, state) ?? clip(props?.summary, SUMMARY_MAX, state);
|
|
3293
|
-
const details = [
|
|
3294
|
-
status ? `status ${status}` : void 0,
|
|
3295
|
-
str2(value.path) ? `path ${str2(value.path)}` : void 0,
|
|
3296
|
-
str2(value.symbol) ? `symbol ${str2(value.symbol)}` : void 0
|
|
3297
|
-
].filter(Boolean);
|
|
3298
|
-
const head = `- **${label}**${uid ? ` [${uid}]` : ""}${details.length > 0 ? ` (${details.join(", ")})` : ""}`;
|
|
3299
|
-
return summary && summary !== label ? `${head}: ${summary}` : head;
|
|
3502
|
+
function bareToolName(name) {
|
|
3503
|
+
const cut = name.lastIndexOf("__");
|
|
3504
|
+
return cut === -1 ? name : name.slice(cut + 2);
|
|
3300
3505
|
}
|
|
3301
|
-
function
|
|
3302
|
-
|
|
3303
|
-
return items.length > 0 ? { heading, items } : void 0;
|
|
3506
|
+
function callKey(toolName, toolInput) {
|
|
3507
|
+
return import_node_crypto4.default.createHash("sha256").update(`${bareToolName(toolName)}\0${stableJson(toolInput)}`).digest("hex");
|
|
3304
3508
|
}
|
|
3305
|
-
function
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
return
|
|
3314
|
-
"# Resume context",
|
|
3315
|
-
[{ heading: `No eligible work was found in the requested scope.${note}`, items: [] }],
|
|
3316
|
-
state,
|
|
3317
|
-
maxChars
|
|
3318
|
-
);
|
|
3509
|
+
function sessionKey(sessionId) {
|
|
3510
|
+
return import_node_crypto4.default.createHash("sha256").update(sessionId).digest("hex").slice(0, 16);
|
|
3511
|
+
}
|
|
3512
|
+
function prune(dir, now) {
|
|
3513
|
+
let names;
|
|
3514
|
+
try {
|
|
3515
|
+
names = import_node_fs4.default.readdirSync(dir);
|
|
3516
|
+
} catch {
|
|
3517
|
+
return;
|
|
3319
3518
|
}
|
|
3320
|
-
const
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
resumeSection(
|
|
3327
|
-
"## Goal and project",
|
|
3328
|
-
[payload.goal, payload.project].filter(Boolean),
|
|
3329
|
-
state,
|
|
3330
|
-
"Scope"
|
|
3331
|
-
)
|
|
3332
|
-
);
|
|
3333
|
-
push(resumeSection("## Plan", [payload.plan], state, "Plan"));
|
|
3334
|
-
push(
|
|
3335
|
-
resumeSection(
|
|
3336
|
-
"## Next steps",
|
|
3337
|
-
Array.isArray(payload.next_steps) ? payload.next_steps : [],
|
|
3338
|
-
state,
|
|
3339
|
-
"Next step"
|
|
3340
|
-
)
|
|
3341
|
-
);
|
|
3342
|
-
push(
|
|
3343
|
-
resumeSection(
|
|
3344
|
-
"## Blockers",
|
|
3345
|
-
Array.isArray(payload.blockers) ? payload.blockers : [],
|
|
3346
|
-
state,
|
|
3347
|
-
"Blocker"
|
|
3348
|
-
)
|
|
3349
|
-
);
|
|
3350
|
-
const knowledge = obj(payload.knowledge);
|
|
3351
|
-
push(
|
|
3352
|
-
resumeSection(
|
|
3353
|
-
"## Relevant lessons and risks",
|
|
3354
|
-
[
|
|
3355
|
-
...Array.isArray(knowledge?.lessons) ? knowledge.lessons : [],
|
|
3356
|
-
...Array.isArray(knowledge?.risks) ? knowledge.risks : []
|
|
3357
|
-
],
|
|
3358
|
-
state,
|
|
3359
|
-
"Knowledge"
|
|
3360
|
-
)
|
|
3361
|
-
);
|
|
3362
|
-
push(
|
|
3363
|
-
resumeSection(
|
|
3364
|
-
"## Active execution",
|
|
3365
|
-
[payload.active_execution].filter(Boolean),
|
|
3366
|
-
state,
|
|
3367
|
-
"Execution"
|
|
3368
|
-
)
|
|
3369
|
-
);
|
|
3370
|
-
push(
|
|
3371
|
-
resumeSection(
|
|
3372
|
-
"## Recent executions",
|
|
3373
|
-
Array.isArray(payload.recent_executions) ? payload.recent_executions : [],
|
|
3374
|
-
state,
|
|
3375
|
-
"Execution"
|
|
3376
|
-
)
|
|
3377
|
-
);
|
|
3378
|
-
push(
|
|
3379
|
-
resumeSection(
|
|
3380
|
-
"## Code targets",
|
|
3381
|
-
Array.isArray(payload.code_targets) ? payload.code_targets : [],
|
|
3382
|
-
state,
|
|
3383
|
-
"Code target"
|
|
3384
|
-
)
|
|
3385
|
-
);
|
|
3386
|
-
push(resumeSection("## Lease", [payload.lease].filter(Boolean), state, "Lease"));
|
|
3387
|
-
const warning = clip(payload.warning, SUMMARY_MAX, state);
|
|
3388
|
-
if (warning) sections.push({ heading: "## Warning", items: [`- ${warning}`] });
|
|
3389
|
-
const selectionReason = clip(payload.selection_reason, SUMMARY_MAX, state);
|
|
3390
|
-
if (selectionReason) {
|
|
3391
|
-
sections.push({ heading: "## Selection", items: [`- ${selectionReason}`] });
|
|
3519
|
+
for (const name of names) {
|
|
3520
|
+
const file = import_node_path4.default.join(dir, name);
|
|
3521
|
+
try {
|
|
3522
|
+
if (now - import_node_fs4.default.statSync(file).mtimeMs > TTL_MS) import_node_fs4.default.unlinkSync(file);
|
|
3523
|
+
} catch {
|
|
3524
|
+
}
|
|
3392
3525
|
}
|
|
3393
|
-
if (sections.length === 0) return void 0;
|
|
3394
|
-
return assemble("# Resume context", sections, state, maxChars);
|
|
3395
3526
|
}
|
|
3396
|
-
function
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3527
|
+
function recordPendingCall(toolName, toolInput, harnessSessionId, fills, options = {}) {
|
|
3528
|
+
const now = (options.now ?? Date.now)();
|
|
3529
|
+
const dir = callsDir(options);
|
|
3530
|
+
try {
|
|
3531
|
+
import_node_fs4.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
3532
|
+
prune(dir, now);
|
|
3533
|
+
const record2 = {
|
|
3534
|
+
harnessSessionId,
|
|
3535
|
+
projectDir: options.projectDir ?? process.env.CLAUDE_PROJECT_DIR,
|
|
3536
|
+
fills,
|
|
3537
|
+
createdAt: now
|
|
3538
|
+
};
|
|
3539
|
+
const file = import_node_path4.default.join(
|
|
3540
|
+
dir,
|
|
3541
|
+
`${callKey(toolName, toolInput)}.${sessionKey(harnessSessionId)}.json`
|
|
3542
|
+
);
|
|
3543
|
+
const temporary = `${file}.${process.pid}.${import_node_crypto4.default.randomUUID()}.tmp`;
|
|
3544
|
+
import_node_fs4.default.writeFileSync(temporary, JSON.stringify(record2), { mode: 384 });
|
|
3545
|
+
import_node_fs4.default.renameSync(temporary, file);
|
|
3546
|
+
} catch {
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
function resolvePendingCall(toolName, toolInput, options = {}) {
|
|
3550
|
+
const now = (options.now ?? Date.now)();
|
|
3551
|
+
const dir = callsDir(options);
|
|
3552
|
+
const prefix = `${callKey(toolName, toolInput)}.`;
|
|
3553
|
+
let names;
|
|
3554
|
+
try {
|
|
3555
|
+
names = import_node_fs4.default.readdirSync(dir).filter((name) => name.startsWith(prefix) && name.endsWith(".json"));
|
|
3556
|
+
} catch {
|
|
3557
|
+
return void 0;
|
|
3558
|
+
}
|
|
3559
|
+
let matches = [];
|
|
3560
|
+
for (const name of names) {
|
|
3561
|
+
try {
|
|
3562
|
+
const record2 = JSON.parse(
|
|
3563
|
+
import_node_fs4.default.readFileSync(import_node_path4.default.join(dir, name), "utf8")
|
|
3564
|
+
);
|
|
3565
|
+
if (typeof record2.createdAt === "number" && now - record2.createdAt <= TTL_MS && record2.fills && typeof record2.fills === "object") {
|
|
3566
|
+
matches.push(record2);
|
|
3408
3567
|
}
|
|
3568
|
+
} catch {
|
|
3409
3569
|
}
|
|
3410
3570
|
}
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
const state = { bounded: false, omitted: 0 };
|
|
3415
|
-
if (limit !== void 0 && limit > 0 && nodes.length > limit) {
|
|
3416
|
-
state.omitted += nodes.length - limit;
|
|
3417
|
-
nodes = nodes.slice(0, limit);
|
|
3418
|
-
state.bounded = true;
|
|
3571
|
+
if (matches.length > 1) {
|
|
3572
|
+
const projectDir = options.projectDir ?? process.env.CLAUDE_PROJECT_DIR;
|
|
3573
|
+
matches = projectDir ? matches.filter((record2) => record2.projectDir === projectDir) : [];
|
|
3419
3574
|
}
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3575
|
+
return matches.length === 1 ? matches[0].fills : void 0;
|
|
3576
|
+
}
|
|
3577
|
+
var import_node_crypto4, import_node_fs4, import_node_os3, import_node_path4, TTL_MS;
|
|
3578
|
+
var init_call_ledger = __esm({
|
|
3579
|
+
"src/call-ledger.ts"() {
|
|
3580
|
+
"use strict";
|
|
3581
|
+
import_node_crypto4 = __toESM(require("crypto"));
|
|
3582
|
+
import_node_fs4 = __toESM(require("fs"));
|
|
3583
|
+
import_node_os3 = __toESM(require("os"));
|
|
3584
|
+
import_node_path4 = __toESM(require("path"));
|
|
3585
|
+
TTL_MS = 10 * 6e4;
|
|
3586
|
+
}
|
|
3587
|
+
});
|
|
3588
|
+
|
|
3589
|
+
// src/hook-installer.ts
|
|
3590
|
+
function storedFeatures(homeDir) {
|
|
3591
|
+
const { promptRecall: promptRecall2, capture } = loadHookEnv(
|
|
3592
|
+
import_node_path6.default.join(homeDir || import_node_os5.default.homedir(), ".mindgraph")
|
|
3427
3593
|
);
|
|
3594
|
+
return { promptRecall: promptRecall2, capture };
|
|
3595
|
+
}
|
|
3596
|
+
function command(harness) {
|
|
3597
|
+
return `node "$HOME/${RUNNER_RELATIVE}" hook --harness ${harness} --owner mindgraph`;
|
|
3598
|
+
}
|
|
3599
|
+
function windowsCommand(harness) {
|
|
3600
|
+
return `node "%USERPROFILE%\\.mindgraph\\bin\\mindgraph-hook.cjs" hook --harness ${harness} --owner mindgraph`;
|
|
3601
|
+
}
|
|
3602
|
+
function installHookRunner(sourceFile, homeDir, version) {
|
|
3603
|
+
const target = import_node_path6.default.join(homeDir || import_node_os5.default.homedir(), RUNNER_RELATIVE);
|
|
3604
|
+
import_node_fs7.default.mkdirSync(import_node_path6.default.dirname(target), { recursive: true });
|
|
3605
|
+
const temporary = `${target}.${process.pid}.${import_node_crypto6.default.randomUUID()}.tmp`;
|
|
3606
|
+
import_node_fs7.default.copyFileSync(sourceFile, temporary);
|
|
3607
|
+
import_node_fs7.default.renameSync(temporary, target);
|
|
3608
|
+
const versionPath = import_node_path6.default.join(homeDir || import_node_os5.default.homedir(), RUNNER_VERSION_RELATIVE);
|
|
3609
|
+
if (version) {
|
|
3610
|
+
import_node_fs7.default.writeFileSync(versionPath, `${version}
|
|
3611
|
+
`);
|
|
3612
|
+
} else {
|
|
3613
|
+
import_node_fs7.default.rmSync(versionPath, { force: true });
|
|
3614
|
+
}
|
|
3615
|
+
return target;
|
|
3616
|
+
}
|
|
3617
|
+
function installedHookRunnerVersion(homeDir) {
|
|
3618
|
+
try {
|
|
3619
|
+
const raw = import_node_fs7.default.readFileSync(
|
|
3620
|
+
import_node_path6.default.join(homeDir || import_node_os5.default.homedir(), RUNNER_VERSION_RELATIVE),
|
|
3621
|
+
"utf8"
|
|
3622
|
+
).trim();
|
|
3623
|
+
return raw || void 0;
|
|
3624
|
+
} catch {
|
|
3625
|
+
return void 0;
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
function parseSemver(value) {
|
|
3629
|
+
if (!value) return void 0;
|
|
3630
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(value.trim());
|
|
3631
|
+
if (!match) return void 0;
|
|
3632
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
3428
3633
|
}
|
|
3429
|
-
function
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
if (!payload && !directSteps) return void 0;
|
|
3433
|
-
const body = payload ?? {};
|
|
3434
|
-
const container = obj(body.graph) ?? body;
|
|
3435
|
-
const rawNodes = Array.isArray(container.nodes) ? container.nodes : [];
|
|
3436
|
-
const nodes = rawNodes.filter(isNodeShaped);
|
|
3437
|
-
const paths = Array.isArray(body.paths) ? body.paths : [];
|
|
3438
|
-
const hasSteps = directSteps !== void 0 || Object.hasOwn(body, "steps");
|
|
3439
|
-
const rawSteps = directSteps ?? (Array.isArray(body.steps) ? body.steps : []);
|
|
3440
|
-
if (nodes.length === 0 && paths.length === 0 && !hasSteps) return void 0;
|
|
3441
|
-
const state = { bounded: false, omitted: 0 };
|
|
3442
|
-
const sections = [];
|
|
3443
|
-
const labelByUid = /* @__PURE__ */ new Map();
|
|
3444
|
-
for (const node of nodes) labelByUid.set(str2(node.uid), str2(node.label) || "?");
|
|
3445
|
-
if (paths.length > 0) {
|
|
3446
|
-
const items = paths.map((raw) => obj(raw)).filter((path9) => Boolean(path9)).map((path9, index) => {
|
|
3447
|
-
const uids = Array.isArray(path9.node_uids) ? path9.node_uids.map(String) : [];
|
|
3448
|
-
const labels = Array.isArray(path9.labels) ? path9.labels.map(String) : Array.isArray(path9.nodes) ? path9.nodes.map((hop) => {
|
|
3449
|
-
const hopNode = obj(hop);
|
|
3450
|
-
return str2(hopNode?.label) || labelByUid.get(str2(hopNode?.uid) || "") || str2(hop) || "?";
|
|
3451
|
-
}) : [];
|
|
3452
|
-
const route = labels.length > 0 ? labels.join(" \u2192 ") : uids.join(" \u2192 ") || "(path)";
|
|
3453
|
-
const cost = num(path9.cost) ?? num(path9.path_cost);
|
|
3454
|
-
const confidence = num(path9.path_confidence);
|
|
3455
|
-
const meta = [
|
|
3456
|
-
cost !== void 0 ? `cost ${cost}` : void 0,
|
|
3457
|
-
confidence !== void 0 ? `confidence ${confidence}` : void 0
|
|
3458
|
-
].filter(Boolean);
|
|
3459
|
-
const head = `${index + 1}. ${route}${meta.length > 0 ? ` (${meta.join(", ")})` : ""}`;
|
|
3460
|
-
return labels.length > 0 && uids.length > 0 ? `${head}
|
|
3461
|
-
uids: ${uids.join(" \u2192 ")}` : head;
|
|
3462
|
-
});
|
|
3463
|
-
sections.push({ heading: "## Paths", items });
|
|
3634
|
+
function semverNewer(a, b) {
|
|
3635
|
+
for (let index = 0; index < 3; index += 1) {
|
|
3636
|
+
if (a[index] !== b[index]) return a[index] > b[index];
|
|
3464
3637
|
}
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
const mode2 = str2(body.mode);
|
|
3495
|
-
sections.push({
|
|
3496
|
-
heading: mode2 === "path" && end ? `No path found from [${start}] to [${end}].` : `No traversal steps returned from [${start}].`,
|
|
3497
|
-
items: []
|
|
3498
|
-
});
|
|
3638
|
+
return false;
|
|
3639
|
+
}
|
|
3640
|
+
function refreshHookRunner(serverVersion, sourceBundle, homeDir) {
|
|
3641
|
+
try {
|
|
3642
|
+
if (process.env.MINDGRAPH_HOOK_AUTOUPDATE === "off") {
|
|
3643
|
+
return { refreshed: false, reason: "disabled" };
|
|
3644
|
+
}
|
|
3645
|
+
const home = homeDir || import_node_os5.default.homedir();
|
|
3646
|
+
if (!import_node_fs7.default.existsSync(import_node_path6.default.join(home, RUNNER_RELATIVE))) {
|
|
3647
|
+
return { refreshed: false, reason: "no_runner" };
|
|
3648
|
+
}
|
|
3649
|
+
if (!import_node_fs7.default.existsSync(sourceBundle)) {
|
|
3650
|
+
return { refreshed: false, reason: "no_source" };
|
|
3651
|
+
}
|
|
3652
|
+
const server = parseSemver(serverVersion);
|
|
3653
|
+
if (!server) return { refreshed: false, reason: "unparseable" };
|
|
3654
|
+
const runnerVersion = installedHookRunnerVersion(home);
|
|
3655
|
+
if (runnerVersion !== void 0) {
|
|
3656
|
+
const pinned = parseSemver(runnerVersion);
|
|
3657
|
+
if (!pinned) {
|
|
3658
|
+
return { refreshed: false, reason: "unparseable", runnerVersion };
|
|
3659
|
+
}
|
|
3660
|
+
if (!semverNewer(server, pinned)) {
|
|
3661
|
+
return {
|
|
3662
|
+
refreshed: false,
|
|
3663
|
+
reason: runnerVersion === serverVersion ? "current" : "downgrade",
|
|
3664
|
+
runnerVersion
|
|
3665
|
+
};
|
|
3666
|
+
}
|
|
3499
3667
|
}
|
|
3668
|
+
installHookRunner(sourceBundle, home, serverVersion);
|
|
3669
|
+
return { refreshed: true, reason: "updated", runnerVersion };
|
|
3670
|
+
} catch {
|
|
3671
|
+
return { refreshed: false, reason: "error" };
|
|
3500
3672
|
}
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3673
|
+
}
|
|
3674
|
+
function refreshOwnedClaudeHooks(projectDir = process.cwd(), homeDir) {
|
|
3675
|
+
const refreshed = [];
|
|
3676
|
+
const scopes = ["user", "project"];
|
|
3677
|
+
for (const scope of scopes) {
|
|
3678
|
+
try {
|
|
3679
|
+
const file = settingsPath("claude-code", scope, projectDir, void 0, homeDir);
|
|
3680
|
+
if (!import_node_fs7.default.existsSync(file)) continue;
|
|
3681
|
+
if (!import_node_fs7.default.readFileSync(file, "utf8").includes(OWNER_MARKER)) continue;
|
|
3682
|
+
installHooks(
|
|
3683
|
+
"claude-code",
|
|
3684
|
+
scope,
|
|
3685
|
+
projectDir,
|
|
3686
|
+
void 0,
|
|
3687
|
+
homeDir,
|
|
3688
|
+
storedFeatures(homeDir)
|
|
3689
|
+
);
|
|
3690
|
+
refreshed.push(file);
|
|
3691
|
+
} catch {
|
|
3510
3692
|
}
|
|
3511
3693
|
}
|
|
3512
|
-
|
|
3513
|
-
return assemble(
|
|
3514
|
-
`# Graph traversal${mode ? ` (${mode})` : ""}`,
|
|
3515
|
-
sections,
|
|
3516
|
-
state,
|
|
3517
|
-
maxChars
|
|
3518
|
-
);
|
|
3694
|
+
return refreshed;
|
|
3519
3695
|
}
|
|
3520
|
-
function
|
|
3521
|
-
const
|
|
3522
|
-
if (!
|
|
3523
|
-
|
|
3524
|
-
const uid = str2(node.uid);
|
|
3525
|
-
const type = str2(node.node_type);
|
|
3526
|
-
return `- ${label}${uid ? ` [${uid}]` : ""}${type ? ` (${type})` : ""}`;
|
|
3696
|
+
function versionSkewNote(serverVersion, homeDir) {
|
|
3697
|
+
const runnerVersion = installedHookRunnerVersion(homeDir);
|
|
3698
|
+
if (!runnerVersion || runnerVersion === serverVersion) return void 0;
|
|
3699
|
+
return `NOTE: the installed MindGraph hook runner is v${runnerVersion} but this MCP server is v${serverVersion} \u2014 hook behavior and tool contracts may be out of sync. Tell the user to run: npx -y mindgraph-mcp@latest install-hooks (refreshes the pinned runner).`;
|
|
3527
3700
|
}
|
|
3528
|
-
function
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
const objects = Array.isArray(payload.objects) ? payload.objects.filter(isNodeShaped) : isNodeShaped(payload.object) ? [payload.object] : [];
|
|
3532
|
-
const cognitive = obj(payload.cognitive_context);
|
|
3533
|
-
const provenance = Array.isArray(payload.provenance) ? payload.provenance : [];
|
|
3534
|
-
const hasQuerySignature = Array.isArray(payload.objects) || obj(payload.confidence) !== void 0;
|
|
3535
|
-
const cognitiveEmpty = !cognitive || Object.values(cognitive).every((v) => !Array.isArray(v) || v.length === 0);
|
|
3536
|
-
if (objects.length === 0 && cognitiveEmpty && provenance.length === 0) {
|
|
3537
|
-
return hasQuerySignature ? "No matching domain objects." : void 0;
|
|
3538
|
-
}
|
|
3539
|
-
const state = { bounded: false, omitted: 0 };
|
|
3540
|
-
const sections = [];
|
|
3541
|
-
if (objects.length > 0) {
|
|
3542
|
-
sections.push(...groupedNodeSections(objects, state));
|
|
3701
|
+
function settingsPath(harness, scope, projectDir = process.cwd(), codexHomeDir = process.env.CODEX_HOME || import_node_path6.default.join(import_node_os5.default.homedir(), ".codex"), homeDir) {
|
|
3702
|
+
if (harness === "codex") {
|
|
3703
|
+
return scope === "user" ? import_node_path6.default.join(codexHomeDir, "hooks.json") : import_node_path6.default.join(projectDir, ".codex", "hooks.json");
|
|
3543
3704
|
}
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3705
|
+
return scope === "user" ? import_node_path6.default.join(homeDir || import_node_os5.default.homedir(), ".claude", "settings.json") : import_node_path6.default.join(projectDir, ".claude", "settings.json");
|
|
3706
|
+
}
|
|
3707
|
+
function readSettings(file) {
|
|
3708
|
+
if (!import_node_fs7.default.existsSync(file)) return {};
|
|
3709
|
+
return JSON.parse(import_node_fs7.default.readFileSync(file, "utf8"));
|
|
3710
|
+
}
|
|
3711
|
+
function ownedHook(harness, timeout, additionalContextLimit) {
|
|
3712
|
+
return {
|
|
3713
|
+
type: "command",
|
|
3714
|
+
command: command(harness),
|
|
3715
|
+
...harness === "codex" ? { commandWindows: windowsCommand(harness) } : {},
|
|
3716
|
+
timeout,
|
|
3717
|
+
statusMessage: "Syncing MindGraph session context",
|
|
3718
|
+
...additionalContextLimit === void 0 ? {} : { additionalContextLimit }
|
|
3719
|
+
};
|
|
3720
|
+
}
|
|
3721
|
+
function desiredEntries(harness, features = {}) {
|
|
3722
|
+
const common = {
|
|
3723
|
+
SessionStart: [
|
|
3724
|
+
{
|
|
3725
|
+
matcher: harness === "claude-code" ? "startup|resume|clear|compact|fork" : "startup|resume|clear|compact",
|
|
3726
|
+
// B7 live acceptance caught cold SessionStart calls crossing the
|
|
3727
|
+
// inherited 20s edge in both harnesses: the hooks correctly failed
|
|
3728
|
+
// open, but no brief reached the model. The same installed runner
|
|
3729
|
+
// completed in 16.84s once warm. Keep a 30s cold-tenant margin.
|
|
3730
|
+
// The declared Codex context limit must not undercut the brief's own
|
|
3731
|
+
// 9,000-char budget: at 3,000 the harness applied a SECOND, tighter
|
|
3732
|
+
// truncation the renderer knew nothing about.
|
|
3733
|
+
hooks: [
|
|
3734
|
+
ownedHook(
|
|
3735
|
+
harness,
|
|
3736
|
+
30,
|
|
3737
|
+
harness === "codex" ? 1e4 : void 0
|
|
3738
|
+
)
|
|
3739
|
+
]
|
|
3740
|
+
}
|
|
3741
|
+
],
|
|
3742
|
+
PreToolUse: [
|
|
3743
|
+
{
|
|
3744
|
+
matcher: "mcp__mindgraph__.*",
|
|
3745
|
+
// Repository resolution over a large multi-repo workspace plus three
|
|
3746
|
+
// git probes can exceed 3s on the first (uncached) call; a killed
|
|
3747
|
+
// PreToolUse means the call goes through untagged.
|
|
3748
|
+
hooks: [ownedHook(harness, 5)]
|
|
3749
|
+
}
|
|
3750
|
+
],
|
|
3751
|
+
// PostToolUse awaits the lease renewal round-trip, which fires exactly
|
|
3752
|
+
// when the tenant is cold (post-idle reclaim) — 5s killed it mid-claim.
|
|
3753
|
+
// Claude Code: only the tools the hook acts on (MindGraph calls, edits,
|
|
3754
|
+
// and the shell/agent calls that keep a lease alive). Read, Grep and Glob
|
|
3755
|
+
// are most of a session's tool calls and the hook did nothing useful for
|
|
3756
|
+
// them but start a process (roadmap H8). Codex names its tools
|
|
3757
|
+
// differently, so it keeps the catch-all.
|
|
3758
|
+
PostToolUse: [
|
|
3759
|
+
{
|
|
3760
|
+
matcher: harness === "claude-code" ? CLAUDE_POST_TOOL_MATCHER : ".*",
|
|
3761
|
+
hooks: [ownedHook(harness, 10)]
|
|
3762
|
+
}
|
|
3763
|
+
],
|
|
3764
|
+
Stop: [{ hooks: [ownedHook(harness, 5)] }],
|
|
3765
|
+
// SessionEnd makes up to two sequential cloud calls (abandon_iteration +
|
|
3766
|
+
// session close) — the SAME cold-tenant latency SessionStart needs 30s
|
|
3767
|
+
// for. Claude honors the configured budget; Codex clamps SessionEnd to a
|
|
3768
|
+
// 3s platform cap regardless of this value (see codex-hooks.ts wire
|
|
3769
|
+
// map), so the codex adapter instead makes at most ONE call there.
|
|
3770
|
+
SessionEnd: [
|
|
3771
|
+
{ hooks: [ownedHook(harness, harness === "codex" ? 3 : 30)] }
|
|
3772
|
+
]
|
|
3773
|
+
};
|
|
3774
|
+
if (harness !== "claude-code") return common;
|
|
3775
|
+
return {
|
|
3776
|
+
...common,
|
|
3777
|
+
TaskCreated: [{ hooks: [ownedHook(harness, 3)] }],
|
|
3778
|
+
TaskCompleted: [{ hooks: [ownedHook(harness, 3)] }],
|
|
3779
|
+
// Bounded recall per prompt: the core gives up at 4s and fails open, so
|
|
3780
|
+
// a 5s budget never holds the prompt longer than that.
|
|
3781
|
+
...features.promptRecall !== false ? { UserPromptSubmit: [{ hooks: [ownedHook(harness, 5)] }] } : {},
|
|
3782
|
+
// Opt-in transcript capture before compaction (cold-tenant ingest).
|
|
3783
|
+
...features.capture === true ? { PreCompact: [{ hooks: [ownedHook(harness, 30)] }] } : {}
|
|
3784
|
+
};
|
|
3785
|
+
}
|
|
3786
|
+
function hookCommand(value) {
|
|
3787
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3788
|
+
const command2 = value.command;
|
|
3789
|
+
return typeof command2 === "string" ? command2 : void 0;
|
|
3790
|
+
}
|
|
3791
|
+
function entryHasOwnedHook(value) {
|
|
3792
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
3793
|
+
const hooks = value.hooks;
|
|
3794
|
+
return Array.isArray(hooks) && hooks.some((hook) => hookCommand(hook)?.includes(OWNER_MARKER));
|
|
3795
|
+
}
|
|
3796
|
+
function writeSettings(file, settings) {
|
|
3797
|
+
import_node_fs7.default.mkdirSync(import_node_path6.default.dirname(file), { recursive: true });
|
|
3798
|
+
const temporary = `${file}.${process.pid}.${import_node_crypto6.default.randomUUID()}.tmp`;
|
|
3799
|
+
import_node_fs7.default.writeFileSync(temporary, `${JSON.stringify(settings, null, 2)}
|
|
3800
|
+
`, {
|
|
3801
|
+
mode: 384
|
|
3802
|
+
});
|
|
3803
|
+
import_node_fs7.default.renameSync(temporary, file);
|
|
3804
|
+
}
|
|
3805
|
+
function installHooks(harness, scope, projectDir = process.cwd(), codexHomeDir, homeDir, features = {}) {
|
|
3806
|
+
const file = settingsPath(harness, scope, projectDir, codexHomeDir, homeDir);
|
|
3807
|
+
const settings = readSettings(file);
|
|
3808
|
+
const hooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
|
|
3809
|
+
let added = 0;
|
|
3810
|
+
let updated = 0;
|
|
3811
|
+
const desired = desiredEntries(harness, features);
|
|
3812
|
+
for (const optional of ["UserPromptSubmit", "PreCompact"]) {
|
|
3813
|
+
if (optional in desired || !Array.isArray(hooks[optional])) continue;
|
|
3814
|
+
const kept = hooks[optional].filter(
|
|
3815
|
+
(entry) => !entryHasOwnedHook(entry)
|
|
3816
|
+
);
|
|
3817
|
+
if (kept.length > 0) hooks[optional] = kept;
|
|
3818
|
+
else delete hooks[optional];
|
|
3551
3819
|
}
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3820
|
+
for (const [event, wanted] of Object.entries(desired)) {
|
|
3821
|
+
const current = Array.isArray(hooks[event]) ? [...hooks[event]] : [];
|
|
3822
|
+
const kept = current.filter((entry) => !entryHasOwnedHook(entry));
|
|
3823
|
+
const hadOwned = kept.length !== current.length;
|
|
3824
|
+
for (const entry of wanted) {
|
|
3825
|
+
kept.push(entry);
|
|
3826
|
+
if (hadOwned) updated += 1;
|
|
3827
|
+
else added += 1;
|
|
3560
3828
|
}
|
|
3829
|
+
hooks[event] = kept;
|
|
3561
3830
|
}
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3831
|
+
settings.hooks = hooks;
|
|
3832
|
+
writeSettings(file, settings);
|
|
3833
|
+
return { path: file, added, updated };
|
|
3834
|
+
}
|
|
3835
|
+
function uninstallHooks(harness, scope, projectDir = process.cwd(), codexHomeDir) {
|
|
3836
|
+
const file = settingsPath(harness, scope, projectDir, codexHomeDir);
|
|
3837
|
+
if (!import_node_fs7.default.existsSync(file)) return { path: file, removed: 0 };
|
|
3838
|
+
const settings = readSettings(file);
|
|
3839
|
+
const hooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
|
|
3840
|
+
let removed = 0;
|
|
3841
|
+
for (const [event, value] of Object.entries(hooks)) {
|
|
3842
|
+
if (!Array.isArray(value)) continue;
|
|
3843
|
+
const retainedEntries = [];
|
|
3844
|
+
for (const rawEntry of value) {
|
|
3845
|
+
if (!rawEntry || typeof rawEntry !== "object" || Array.isArray(rawEntry)) {
|
|
3846
|
+
retainedEntries.push(rawEntry);
|
|
3847
|
+
continue;
|
|
3848
|
+
}
|
|
3849
|
+
const entry = { ...rawEntry };
|
|
3850
|
+
if (!Array.isArray(entry.hooks)) {
|
|
3851
|
+
retainedEntries.push(entry);
|
|
3852
|
+
continue;
|
|
3853
|
+
}
|
|
3854
|
+
const retainedHooks = entry.hooks.filter((hook) => {
|
|
3855
|
+
const owned = hookCommand(hook)?.includes(OWNER_MARKER) || false;
|
|
3856
|
+
if (owned) removed += 1;
|
|
3857
|
+
return !owned;
|
|
3858
|
+
});
|
|
3859
|
+
if (retainedHooks.length > 0) {
|
|
3860
|
+
entry.hooks = retainedHooks;
|
|
3861
|
+
retainedEntries.push(entry);
|
|
3573
3862
|
}
|
|
3574
3863
|
}
|
|
3864
|
+
if (retainedEntries.length > 0) hooks[event] = retainedEntries;
|
|
3865
|
+
else delete hooks[event];
|
|
3575
3866
|
}
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
const by = str2(entry.ingested_by_name);
|
|
3580
|
-
const suffix = `${by ? ` (ingested by ${by})` : ""} [source ${str2(entry.source_uid) || "?"}]`;
|
|
3581
|
-
return str2(entry.span_kind) === "anchor" ? `- "${span}"${suffix}` : `- context: ${span}${suffix}`;
|
|
3582
|
-
});
|
|
3583
|
-
sections.push({ heading: "## Sources", items });
|
|
3584
|
-
}
|
|
3585
|
-
const confidence = num(obj(payload.confidence)?.overall);
|
|
3586
|
-
const notes = [];
|
|
3587
|
-
if (confidence !== void 0) notes.push(`overall confidence ${confidence}`);
|
|
3588
|
-
if (payload.has_more === true) notes.push("more results exist (has_more)");
|
|
3589
|
-
if (payload.seed_cap_hit === true) notes.push("seed cap hit \u2014 narrow the query for a complete set");
|
|
3590
|
-
if (notes.length > 0) sections.push({ heading: `Note: ${notes.join("; ")}.`, items: [] });
|
|
3591
|
-
return assemble(
|
|
3592
|
-
`# Ontology results (${objects.length} object${objects.length === 1 ? "" : "s"})`,
|
|
3593
|
-
sections,
|
|
3594
|
-
state
|
|
3595
|
-
);
|
|
3867
|
+
settings.hooks = hooks;
|
|
3868
|
+
writeSettings(file, settings);
|
|
3869
|
+
return { path: file, removed };
|
|
3596
3870
|
}
|
|
3597
|
-
function
|
|
3598
|
-
|
|
3599
|
-
if (!payload) return void 0;
|
|
3600
|
-
const rawItems = Array.isArray(payload.items) ? payload.items : Array.isArray(payload.rows) && payload.rows.some((row) => unwrapSearchResult(row)) ? payload.rows : void 0;
|
|
3601
|
-
const items = (rawItems ?? []).map(unwrapSearchResult).filter((node) => Boolean(node));
|
|
3602
|
-
if (items.length === 0) {
|
|
3603
|
-
return rawItems ? `# ${title} (0 results)` : void 0;
|
|
3604
|
-
}
|
|
3605
|
-
const state = { bounded: false, omitted: 0 };
|
|
3606
|
-
const sections = groupedNodeSections(items, state);
|
|
3607
|
-
const total = num(payload.total_count);
|
|
3608
|
-
const truncations = Array.isArray(payload.truncation_reasons) ? payload.truncation_reasons.filter((reason) => typeof reason === "string") : [];
|
|
3609
|
-
const notes = [];
|
|
3610
|
-
if (payload.has_more === true) notes.push("more results exist");
|
|
3611
|
-
if (truncations.length > 0) notes.push(`truncated: ${truncations.join(", ")}`);
|
|
3612
|
-
if (notes.length > 0) sections.push({ heading: `Note: ${notes.join("; ")}.`, items: [] });
|
|
3613
|
-
const count = total !== void 0 && total !== items.length ? `${items.length} of ${total}` : `${items.length}`;
|
|
3614
|
-
return assemble(`# ${title} (${count})`, sections, state);
|
|
3871
|
+
function installClaudeHooks(scope, projectDir = process.cwd(), features = {}) {
|
|
3872
|
+
return installHooks("claude-code", scope, projectDir, void 0, void 0, features);
|
|
3615
3873
|
}
|
|
3616
|
-
function
|
|
3617
|
-
|
|
3618
|
-
if (!Array.isArray(jobs)) return void 0;
|
|
3619
|
-
if (jobs.length === 0) return "No ingestion jobs.";
|
|
3620
|
-
const state = { bounded: false, omitted: 0 };
|
|
3621
|
-
const sorted = jobs.map((raw) => obj(raw)).filter((job) => Boolean(job)).sort((a, b) => (num(b.created_at) ?? 0) - (num(a.created_at) ?? 0));
|
|
3622
|
-
const shown = sorted.slice(0, JOBS_RENDER_MAX);
|
|
3623
|
-
if (shown.length < sorted.length) state.bounded = true;
|
|
3624
|
-
const items = shown.map((job) => {
|
|
3625
|
-
const progress = obj(job.progress);
|
|
3626
|
-
const failure = obj(job.error_details);
|
|
3627
|
-
const message = str2(failure?.message) ?? str2(job.error);
|
|
3628
|
-
const done = num(progress?.processed_chunks);
|
|
3629
|
-
const totalChunks = num(progress?.total_chunks);
|
|
3630
|
-
const bits = [
|
|
3631
|
-
str2(job.status) || "?",
|
|
3632
|
-
done !== void 0 && totalChunks !== void 0 ? `${done}/${totalChunks} chunks` : void 0,
|
|
3633
|
-
num(progress?.nodes_created) !== void 0 ? `${num(progress?.nodes_created)} nodes` : void 0,
|
|
3634
|
-
num(job.queue_position) !== void 0 ? `queue #${num(job.queue_position)}` : void 0,
|
|
3635
|
-
message ? `error: ${clip(message, 160, state)}` : void 0,
|
|
3636
|
-
str2(failure?.code) ? `code: ${clip(failure?.code, 80, state)}` : void 0,
|
|
3637
|
-
num(failure?.status) !== void 0 ? `status: ${num(failure?.status)}` : void 0,
|
|
3638
|
-
typeof failure?.retriable === "boolean" ? `retriable: ${failure.retriable}` : void 0
|
|
3639
|
-
].filter(Boolean);
|
|
3640
|
-
return `- [${str2(job.id) || "?"}] ${clip(job.title, 120, state) || "(untitled)"} \u2014 ${bits.join(", ")}`;
|
|
3641
|
-
});
|
|
3642
|
-
return assemble(
|
|
3643
|
-
`# Ingestion jobs (${shown.length}${shown.length < sorted.length ? ` of ${sorted.length}` : ""})`,
|
|
3644
|
-
[{ heading: "", items }],
|
|
3645
|
-
state
|
|
3646
|
-
);
|
|
3874
|
+
function uninstallClaudeHooks(scope, projectDir = process.cwd()) {
|
|
3875
|
+
return uninstallHooks("claude-code", scope, projectDir);
|
|
3647
3876
|
}
|
|
3648
|
-
function
|
|
3649
|
-
|
|
3650
|
-
if (!payload) return void 0;
|
|
3651
|
-
const state = { bounded: false, omitted: 0 };
|
|
3652
|
-
const sections = [];
|
|
3653
|
-
for (const [key, value] of Object.entries(payload)) {
|
|
3654
|
-
if (!Array.isArray(value) || value.length === 0) continue;
|
|
3655
|
-
const items = value.map((raw) => obj(raw)).filter((item) => Boolean(item)).map((item) => {
|
|
3656
|
-
const fromLabel = str2(item.from_label);
|
|
3657
|
-
const toLabel = str2(item.to_label);
|
|
3658
|
-
const anyLabelKey = Object.keys(item).find(
|
|
3659
|
-
(k) => k.endsWith("_label") && str2(item[k])
|
|
3660
|
-
);
|
|
3661
|
-
const label = str2(item.label) ?? str2(item.title) ?? str2(item.name) ?? (fromLabel && toLabel ? `${fromLabel} \u2194 ${toLabel}` : void 0) ?? (anyLabelKey ? str2(item[anyLabelKey]) : void 0) ?? str2(item.uid) ?? str2(item.target_uid) ?? "(item)";
|
|
3662
|
-
const uid = str2(item.uid) ?? str2(item.target_uid);
|
|
3663
|
-
const numbers = Object.entries(item).filter(([, v]) => typeof v === "number").slice(0, 4).map(([k, v]) => `${k} ${Math.round(v * 100) / 100}`);
|
|
3664
|
-
const lists = Object.entries(item).filter(([, v]) => Array.isArray(v) && v.length > 0).slice(0, 2).map(([k, v]) => `${k}: ${v.length}`);
|
|
3665
|
-
const meta = [...numbers, ...lists].join(", ");
|
|
3666
|
-
return `- ${clip(label, 160, state)}${uid && uid !== label ? ` [${uid}]` : ""}${meta ? ` (${meta})` : ""}`;
|
|
3667
|
-
});
|
|
3668
|
-
if (items.length > 0) {
|
|
3669
|
-
sections.push({ heading: `## ${key.replace(/_/g, " ")}`, items });
|
|
3670
|
-
}
|
|
3671
|
-
}
|
|
3672
|
-
if (sections.length === 0) return void 0;
|
|
3673
|
-
return assemble("# Synthesis signals", sections, state);
|
|
3877
|
+
function installCodexHooks(scope, projectDir = process.cwd(), codexHomeDir) {
|
|
3878
|
+
return installHooks("codex", scope, projectDir, codexHomeDir);
|
|
3674
3879
|
}
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3880
|
+
function uninstallCodexHooks(scope, projectDir = process.cwd(), codexHomeDir) {
|
|
3881
|
+
return uninstallHooks("codex", scope, projectDir, codexHomeDir);
|
|
3882
|
+
}
|
|
3883
|
+
var import_node_fs7, import_node_os5, import_node_path6, import_node_crypto6, OWNER_MARKER, RUNNER_RELATIVE, RUNNER_VERSION_RELATIVE, CLAUDE_POST_TOOL_MATCHER;
|
|
3884
|
+
var init_hook_installer = __esm({
|
|
3885
|
+
"src/hook-installer.ts"() {
|
|
3678
3886
|
"use strict";
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
CURATION_EDGE_TYPES = /* @__PURE__ */ new Set(["POSSIBLE_DUPLICATE", "PossibleDuplicate"]);
|
|
3689
|
-
INFRA_NODE_TYPES = /* @__PURE__ */ new Set(["Chunk", "Document", "Article"]);
|
|
3690
|
-
JOBS_RENDER_MAX = 20;
|
|
3887
|
+
import_node_fs7 = __toESM(require("fs"));
|
|
3888
|
+
import_node_os5 = __toESM(require("os"));
|
|
3889
|
+
import_node_path6 = __toESM(require("path"));
|
|
3890
|
+
import_node_crypto6 = __toESM(require("crypto"));
|
|
3891
|
+
init_hook_env();
|
|
3892
|
+
OWNER_MARKER = "--owner mindgraph";
|
|
3893
|
+
RUNNER_RELATIVE = ".mindgraph/bin/mindgraph-hook.cjs";
|
|
3894
|
+
RUNNER_VERSION_RELATIVE = `${RUNNER_RELATIVE}.version`;
|
|
3895
|
+
CLAUDE_POST_TOOL_MATCHER = "mcp__mindgraph__.*|Edit|MultiEdit|Write|NotebookEdit|Bash|Task|Agent";
|
|
3691
3896
|
}
|
|
3692
3897
|
});
|
|
3693
3898
|
|
|
@@ -4583,7 +4788,8 @@ function compactItem(raw, fallbackType) {
|
|
|
4583
4788
|
type: fallbackType ?? nodeType2(value),
|
|
4584
4789
|
label: text(value.label) ?? text(value.document_title),
|
|
4585
4790
|
truth_status: text(value.truth_status) ?? text(obj2(value.props)?.truth_status),
|
|
4586
|
-
|
|
4791
|
+
// `true` from /retrieve, an object naming the replacement from /retrieve/context.
|
|
4792
|
+
superseded: value.superseded ? true : void 0,
|
|
4587
4793
|
currently_valid: value.currently_valid === false ? false : void 0,
|
|
4588
4794
|
valid_at_time: value.valid_at_time === false ? false : void 0,
|
|
4589
4795
|
retrieval_origin: text(value.retrieval_origin),
|
|
@@ -4644,6 +4850,10 @@ function resumeCandidates(raw) {
|
|
|
4644
4850
|
const item = compactItem(payload[key], key);
|
|
4645
4851
|
if (item) items.push(item);
|
|
4646
4852
|
}
|
|
4853
|
+
for (const value of Array.isArray(payload.other_eligible) ? payload.other_eligible : []) {
|
|
4854
|
+
const item = compactItem(value, "Task");
|
|
4855
|
+
if (item) items.push(item);
|
|
4856
|
+
}
|
|
4647
4857
|
for (const key of ["next_steps", "recent_executions", "blockers", "code_targets"]) {
|
|
4648
4858
|
for (const value of Array.isArray(payload[key]) ? payload[key] : []) {
|
|
4649
4859
|
const item = compactItem(value, key.replace(/s$/, ""));
|
|
@@ -4688,9 +4898,9 @@ function latestTimestamp(value) {
|
|
|
4688
4898
|
for (const item of current) visit(item);
|
|
4689
4899
|
return;
|
|
4690
4900
|
}
|
|
4691
|
-
const
|
|
4692
|
-
if (!
|
|
4693
|
-
for (const [key, item] of Object.entries(
|
|
4901
|
+
const record2 = obj2(current);
|
|
4902
|
+
if (!record2) return;
|
|
4903
|
+
for (const [key, item] of Object.entries(record2)) {
|
|
4694
4904
|
if (["updated_at", "created_at", "completed_at", "started_at", "observed_at"].includes(key) && typeof item === "string" && !Number.isNaN(Date.parse(item)) && (!latest || Date.parse(item) > Date.parse(latest))) {
|
|
4695
4905
|
latest = item;
|
|
4696
4906
|
}
|
|
@@ -4714,7 +4924,7 @@ function contextWarnings(raw, rendered2, available, returned) {
|
|
|
4714
4924
|
message: `${Math.max(0, available - returned)} structured item(s) were omitted from the rendered context.`
|
|
4715
4925
|
});
|
|
4716
4926
|
}
|
|
4717
|
-
if (/"superseded":true/.test(serialized)) {
|
|
4927
|
+
if (/"superseded":(?:true|\{)/.test(serialized)) {
|
|
4718
4928
|
warnings.push({ code: "superseded_items_present", message: "Some retrieved items are superseded." });
|
|
4719
4929
|
}
|
|
4720
4930
|
if (/"currently_valid":false|"valid_at_time":false/.test(serialized)) {
|
|
@@ -4763,6 +4973,7 @@ async function handleMemoryTool(client2, args, config = {}) {
|
|
|
4763
4973
|
let rendered2;
|
|
4764
4974
|
let strategy;
|
|
4765
4975
|
let candidates;
|
|
4976
|
+
let workspace;
|
|
4766
4977
|
if (mode === "topic") {
|
|
4767
4978
|
const query = text(args.query);
|
|
4768
4979
|
if (!query) return error("mode='topic' requires a non-empty query of 1-3 discriminating keywords");
|
|
@@ -4785,10 +4996,27 @@ async function handleMemoryTool(client2, args, config = {}) {
|
|
|
4785
4996
|
strategy = "retrieve_context";
|
|
4786
4997
|
candidates = topicCandidates(raw);
|
|
4787
4998
|
} else {
|
|
4999
|
+
if (args.scope !== void 0 && args.scope !== "workspace") {
|
|
5000
|
+
return error("scope must be 'workspace' when given");
|
|
5001
|
+
}
|
|
5002
|
+
if (args.scope === "workspace") {
|
|
5003
|
+
if (!config.resolveWorkspaceScope) {
|
|
5004
|
+
return error(
|
|
5005
|
+
"scope='workspace' needs the local MindGraph MCP server, which reads .mindgraph/workspace.json beside the checkout; this server has no working directory. Pass scope_uids instead."
|
|
5006
|
+
);
|
|
5007
|
+
}
|
|
5008
|
+
workspace = await config.resolveWorkspaceScope(agentId ?? "mcp");
|
|
5009
|
+
if (workspace.uids.length === 0) {
|
|
5010
|
+
return error(
|
|
5011
|
+
`scope='workspace' resolved no repositories${workspace.failures.length > 0 ? ` (${workspace.failures.join("; ")})` : ""}`
|
|
5012
|
+
);
|
|
5013
|
+
}
|
|
5014
|
+
}
|
|
4788
5015
|
const scopeUids = Array.from(
|
|
4789
5016
|
/* @__PURE__ */ new Set([
|
|
4790
5017
|
...Array.isArray(args.scope_uids) ? args.scope_uids.map(String) : [],
|
|
4791
|
-
...text(args.project_uid) ? [text(args.project_uid)] : []
|
|
5018
|
+
...text(args.project_uid) ? [text(args.project_uid)] : [],
|
|
5019
|
+
...workspace?.uids ?? []
|
|
4792
5020
|
])
|
|
4793
5021
|
);
|
|
4794
5022
|
raw = await client2.plan(
|
|
@@ -4797,11 +5025,11 @@ async function handleMemoryTool(client2, args, config = {}) {
|
|
|
4797
5025
|
task_uid: text(args.task_uid),
|
|
4798
5026
|
session_uid: text(args.session_uid),
|
|
4799
5027
|
scope_uids: scopeUids.length > 0 ? scopeUids : void 0,
|
|
4800
|
-
limit: 1,
|
|
5028
|
+
limit: workspace ? WORKSPACE_RESUME_LIMIT : 1,
|
|
4801
5029
|
agent_id: agentId
|
|
4802
5030
|
})
|
|
4803
5031
|
);
|
|
4804
|
-
rendered2 = renderResumeContext(raw, budget) ?? "No eligible work was found in the requested scope.";
|
|
5032
|
+
rendered2 = renderResumeContext(raw, budget, workspace?.names) ?? "No eligible work was found in the requested scope.";
|
|
4805
5033
|
strategy = "resume_work";
|
|
4806
5034
|
candidates = resumeCandidates(raw);
|
|
4807
5035
|
}
|
|
@@ -4814,7 +5042,8 @@ async function handleMemoryTool(client2, args, config = {}) {
|
|
|
4814
5042
|
project_uid: text(args.project_uid),
|
|
4815
5043
|
task_uid: text(args.task_uid),
|
|
4816
5044
|
session_uid: text(args.session_uid),
|
|
4817
|
-
scope_uids: Array.isArray(args.scope_uids) ? args.scope_uids.map(String) : void 0
|
|
5045
|
+
scope_uids: Array.isArray(args.scope_uids) ? args.scope_uids.map(String) : void 0,
|
|
5046
|
+
workspace_repositories: workspace ? Object.values(workspace.names).sort() : void 0
|
|
4818
5047
|
});
|
|
4819
5048
|
const contextId = `ctx_${sha2562(
|
|
4820
5049
|
stableJson2({ mode, scope, strategy, query: text(args.query), rendered_hash: renderedHash })
|
|
@@ -4855,7 +5084,15 @@ async function handleMemoryTool(client2, args, config = {}) {
|
|
|
4855
5084
|
valid_at: text(args.valid_at),
|
|
4856
5085
|
latest_source_timestamp: latestTimestamp(raw)
|
|
4857
5086
|
}),
|
|
4858
|
-
warnings:
|
|
5087
|
+
warnings: [
|
|
5088
|
+
...contextWarnings(raw, rendered2, candidates.length, items.length),
|
|
5089
|
+
...workspace && workspace.failures.length > 0 ? [
|
|
5090
|
+
{
|
|
5091
|
+
code: "workspace_scope_partial",
|
|
5092
|
+
message: `${workspace.failures.length} declared repositories did not resolve and were left out: ${workspace.failures.join("; ")}`
|
|
5093
|
+
}
|
|
5094
|
+
] : []
|
|
5095
|
+
]
|
|
4859
5096
|
};
|
|
4860
5097
|
return {
|
|
4861
5098
|
content: [{ type: "text", text: rendered2 }],
|
|
@@ -4868,6 +5105,7 @@ var init_memory_tool = __esm({
|
|
|
4868
5105
|
"use strict";
|
|
4869
5106
|
import_node_crypto7 = require("crypto");
|
|
4870
5107
|
init_render();
|
|
5108
|
+
init_workspace_scope();
|
|
4871
5109
|
RESULT_SCHEMA_VERSION = "mindgraph.memory.result.v1";
|
|
4872
5110
|
DEFAULT_OUTPUT_CHARS = 8e3;
|
|
4873
5111
|
MIN_OUTPUT_CHARS = 512;
|
|
@@ -4902,6 +5140,11 @@ var init_memory_tool = __esm({
|
|
|
4902
5140
|
items: { type: "string" },
|
|
4903
5141
|
description: "Optional explicit graph scopes for continuity/resume"
|
|
4904
5142
|
},
|
|
5143
|
+
scope: {
|
|
5144
|
+
type: "string",
|
|
5145
|
+
enum: ["workspace"],
|
|
5146
|
+
description: "For continuity/resume on a local server: select work across every repository the working directory's .mindgraph/workspace.json declares, and list the other eligible tasks grouped by repository"
|
|
5147
|
+
},
|
|
4905
5148
|
node_types: {
|
|
4906
5149
|
type: "array",
|
|
4907
5150
|
items: { type: "string" },
|
|
@@ -5136,12 +5379,12 @@ var init_resources = __esm({
|
|
|
5136
5379
|
|
|
5137
5380
|
// src/sync-tool.ts
|
|
5138
5381
|
function safePath(root, candidate) {
|
|
5139
|
-
const absolute =
|
|
5140
|
-
const relative2 =
|
|
5141
|
-
if (relative2.startsWith("..") ||
|
|
5382
|
+
const absolute = import_node_path7.default.resolve(root, candidate);
|
|
5383
|
+
const relative2 = import_node_path7.default.relative(root, absolute);
|
|
5384
|
+
if (relative2.startsWith("..") || import_node_path7.default.isAbsolute(relative2)) {
|
|
5142
5385
|
throw new Error(`path escapes repository root: ${candidate}`);
|
|
5143
5386
|
}
|
|
5144
|
-
if (
|
|
5387
|
+
if (import_node_fs8.default.existsSync(absolute)) {
|
|
5145
5388
|
if (!pathContains(realPathOf(root), realPathOf(absolute))) {
|
|
5146
5389
|
throw new Error(`path escapes repository root via symlink: ${candidate}`);
|
|
5147
5390
|
}
|
|
@@ -5150,25 +5393,25 @@ function safePath(root, candidate) {
|
|
|
5150
5393
|
}
|
|
5151
5394
|
function realPathOf(target) {
|
|
5152
5395
|
try {
|
|
5153
|
-
return
|
|
5396
|
+
return import_node_fs8.default.realpathSync(target);
|
|
5154
5397
|
} catch {
|
|
5155
|
-
return
|
|
5398
|
+
return import_node_path7.default.resolve(target);
|
|
5156
5399
|
}
|
|
5157
5400
|
}
|
|
5158
5401
|
function markdownFiles(target, realRoot, visited = /* @__PURE__ */ new Set()) {
|
|
5159
|
-
if (!
|
|
5402
|
+
if (!import_node_fs8.default.existsSync(target)) return [];
|
|
5160
5403
|
const real = realPathOf(target);
|
|
5161
5404
|
if (!pathContains(realRoot, real)) return [];
|
|
5162
|
-
const stat =
|
|
5405
|
+
const stat = import_node_fs8.default.statSync(target);
|
|
5163
5406
|
if (stat.isFile()) return target.endsWith(".md") ? [target] : [];
|
|
5164
5407
|
if (!stat.isDirectory() || visited.has(real)) return [];
|
|
5165
5408
|
visited.add(real);
|
|
5166
|
-
return
|
|
5409
|
+
return import_node_fs8.default.readdirSync(target, { withFileTypes: true }).flatMap((entry) => markdownFiles(import_node_path7.default.join(target, entry.name), realRoot, visited));
|
|
5167
5410
|
}
|
|
5168
5411
|
function fileDescriptor(root, absolute) {
|
|
5169
|
-
const content =
|
|
5412
|
+
const content = import_node_fs8.default.readFileSync(absolute, "utf8");
|
|
5170
5413
|
return {
|
|
5171
|
-
logical_path:
|
|
5414
|
+
logical_path: import_node_path7.default.relative(root, absolute).split(import_node_path7.default.sep).join("/"),
|
|
5172
5415
|
content_hash: import_node_crypto8.default.createHash("sha256").update(content).digest("hex"),
|
|
5173
5416
|
bytes: Buffer.byteLength(content),
|
|
5174
5417
|
content
|
|
@@ -5238,7 +5481,7 @@ async function handleSyncTool(client2, args) {
|
|
|
5238
5481
|
}
|
|
5239
5482
|
if (action === "status" && request.logical_path && request.file_present !== false) {
|
|
5240
5483
|
const absolute = safePath(root, String(request.logical_path));
|
|
5241
|
-
request.file_present =
|
|
5484
|
+
request.file_present = import_node_fs8.default.existsSync(absolute);
|
|
5242
5485
|
if (request.file_present && !request.content_hash) {
|
|
5243
5486
|
request.content_hash = fileDescriptor(root, absolute).content_hash;
|
|
5244
5487
|
}
|
|
@@ -5282,13 +5525,13 @@ async function scanRepository(remote, repository, requested, provider, agentId)
|
|
|
5282
5525
|
files: results
|
|
5283
5526
|
};
|
|
5284
5527
|
}
|
|
5285
|
-
var import_node_crypto8,
|
|
5528
|
+
var import_node_crypto8, import_node_fs8, import_node_path7, ok3, err4, SYNC_TOOL;
|
|
5286
5529
|
var init_sync_tool = __esm({
|
|
5287
5530
|
"src/sync-tool.ts"() {
|
|
5288
5531
|
"use strict";
|
|
5289
5532
|
import_node_crypto8 = __toESM(require("crypto"));
|
|
5290
|
-
|
|
5291
|
-
|
|
5533
|
+
import_node_fs8 = __toESM(require("fs"));
|
|
5534
|
+
import_node_path7 = __toESM(require("path"));
|
|
5292
5535
|
init_codegraph();
|
|
5293
5536
|
init_error_detail();
|
|
5294
5537
|
ok3 = (data) => ({
|
|
@@ -7869,14 +8112,14 @@ var init_tools = __esm({
|
|
|
7869
8112
|
// src/server.ts
|
|
7870
8113
|
function buildMindGraphServer(wiring) {
|
|
7871
8114
|
const { client: client2, hosted } = wiring;
|
|
7872
|
-
const
|
|
8115
|
+
const server = new import_server.Server(
|
|
7873
8116
|
{ name: "mindgraph", version: wiring.serverVersion },
|
|
7874
8117
|
{
|
|
7875
8118
|
capabilities: { tools: {}, resources: {}, prompts: {} },
|
|
7876
8119
|
instructions: wiring.instructions
|
|
7877
8120
|
}
|
|
7878
8121
|
);
|
|
7879
|
-
|
|
8122
|
+
server.setRequestHandler("tools/list", async () => {
|
|
7880
8123
|
const generated = await getGeneratedTools(client2, wiring.generatedTools);
|
|
7881
8124
|
const tools = [...toolsForProfile(wiring.profile), ...generated.tools];
|
|
7882
8125
|
if (!hosted) return { tools };
|
|
@@ -7887,7 +8130,7 @@ function buildMindGraphServer(wiring) {
|
|
|
7887
8130
|
]
|
|
7888
8131
|
};
|
|
7889
8132
|
});
|
|
7890
|
-
|
|
8133
|
+
server.setRequestHandler("tools/call", async (request) => {
|
|
7891
8134
|
const { name, arguments: args } = request.params;
|
|
7892
8135
|
const rawArgs = {
|
|
7893
8136
|
...args || {},
|
|
@@ -7934,20 +8177,20 @@ function buildMindGraphServer(wiring) {
|
|
|
7934
8177
|
}
|
|
7935
8178
|
return handleTool(client2, name, toolArgs, wiring.memory);
|
|
7936
8179
|
});
|
|
7937
|
-
|
|
8180
|
+
server.setRequestHandler("prompts/list", async () => {
|
|
7938
8181
|
return { prompts: PROMPTS };
|
|
7939
8182
|
});
|
|
7940
|
-
|
|
8183
|
+
server.setRequestHandler("prompts/get", async (request) => {
|
|
7941
8184
|
const { name, arguments: args } = request.params;
|
|
7942
8185
|
return getPrompt(name, args);
|
|
7943
8186
|
});
|
|
7944
|
-
|
|
8187
|
+
server.setRequestHandler("resources/list", async () => {
|
|
7945
8188
|
return { resources: RESOURCES };
|
|
7946
8189
|
});
|
|
7947
|
-
|
|
8190
|
+
server.setRequestHandler("resources/templates/list", async () => {
|
|
7948
8191
|
return { resourceTemplates: RESOURCE_TEMPLATES };
|
|
7949
8192
|
});
|
|
7950
|
-
|
|
8193
|
+
server.setRequestHandler("resources/read", async (request) => {
|
|
7951
8194
|
return readGovernedResource(
|
|
7952
8195
|
client2,
|
|
7953
8196
|
request.params.uri,
|
|
@@ -7955,15 +8198,13 @@ function buildMindGraphServer(wiring) {
|
|
|
7955
8198
|
wiring.memory
|
|
7956
8199
|
);
|
|
7957
8200
|
});
|
|
7958
|
-
return
|
|
8201
|
+
return server;
|
|
7959
8202
|
}
|
|
7960
|
-
var import_server
|
|
8203
|
+
var import_server;
|
|
7961
8204
|
var init_server = __esm({
|
|
7962
8205
|
"src/server.ts"() {
|
|
7963
8206
|
"use strict";
|
|
7964
|
-
import_server = require("@modelcontextprotocol/
|
|
7965
|
-
import_webStandardStreamableHttp = require("@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js");
|
|
7966
|
-
import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
8207
|
+
import_server = require("@modelcontextprotocol/server");
|
|
7967
8208
|
init_generated_tools();
|
|
7968
8209
|
init_governance();
|
|
7969
8210
|
init_hosted_tools();
|
|
@@ -7976,22 +8217,24 @@ var init_server = __esm({
|
|
|
7976
8217
|
// src/index.ts
|
|
7977
8218
|
var src_exports = {};
|
|
7978
8219
|
async function main() {
|
|
7979
|
-
|
|
7980
|
-
|
|
8220
|
+
(0, import_stdio.serveStdio)(buildServer, {
|
|
8221
|
+
onerror: (error2) => console.error("MindGraph MCP transport error:", error2.message)
|
|
8222
|
+
});
|
|
7981
8223
|
console.error(
|
|
7982
|
-
`MindGraph MCP server v${"0.
|
|
8224
|
+
`MindGraph MCP server v${"0.26.0"} running on stdio (${BASE_URL})`
|
|
7983
8225
|
);
|
|
7984
8226
|
}
|
|
7985
|
-
var import_stdio,
|
|
8227
|
+
var import_stdio, import_node_path8, API_KEY, BASE_URL, AGENT_ID, ORG_ID, PROFILE, HARNESS, client, INSTRUCTIONS, RUNNER_REFRESH, HOOK_SKEW_NOTE, GOVERNANCE_CONFIG, MEMORY_RUNTIME_CONFIG, buildServer;
|
|
7986
8228
|
var init_src = __esm({
|
|
7987
8229
|
"src/index.ts"() {
|
|
7988
8230
|
"use strict";
|
|
7989
8231
|
init_call_ledger();
|
|
7990
|
-
import_stdio = require("@modelcontextprotocol/
|
|
8232
|
+
import_stdio = require("@modelcontextprotocol/server/stdio");
|
|
7991
8233
|
init_dist();
|
|
7992
8234
|
init_hook_env();
|
|
7993
8235
|
init_server();
|
|
7994
|
-
|
|
8236
|
+
init_workspace_scope();
|
|
8237
|
+
import_node_path8 = __toESM(require("path"));
|
|
7995
8238
|
init_hook_installer();
|
|
7996
8239
|
API_KEY = process.env.MINDGRAPH_API_KEY;
|
|
7997
8240
|
BASE_URL = process.env.MINDGRAPH_BASE_URL || "https://api.mindgraph.cloud";
|
|
@@ -8092,7 +8335,7 @@ Capture knowledge when the user shares something worth remembering:
|
|
|
8092
8335
|
}
|
|
8093
8336
|
RUNNER_REFRESH = (() => {
|
|
8094
8337
|
try {
|
|
8095
|
-
return refreshHookRunner("0.
|
|
8338
|
+
return refreshHookRunner("0.26.0", import_node_path8.default.join(__dirname, "cli.js"));
|
|
8096
8339
|
} catch {
|
|
8097
8340
|
return void 0;
|
|
8098
8341
|
}
|
|
@@ -8106,12 +8349,12 @@ Capture knowledge when the user shares something worth remembering:
|
|
|
8106
8349
|
}
|
|
8107
8350
|
})();
|
|
8108
8351
|
console.error(
|
|
8109
|
-
`[mindgraph-mcp] refreshed pinned hook runner ${RUNNER_REFRESH.runnerVersion ? `v${RUNNER_REFRESH.runnerVersion}` : "(pre-sidecar)"} \u2192 v${"0.
|
|
8352
|
+
`[mindgraph-mcp] refreshed pinned hook runner ${RUNNER_REFRESH.runnerVersion ? `v${RUNNER_REFRESH.runnerVersion}` : "(pre-sidecar)"} \u2192 v${"0.26.0"}` + (refreshedSettings.length > 0 ? `; refreshed owned hook entries in ${refreshedSettings.join(", ")}` : "")
|
|
8110
8353
|
);
|
|
8111
8354
|
}
|
|
8112
8355
|
HOOK_SKEW_NOTE = (() => {
|
|
8113
8356
|
try {
|
|
8114
|
-
return versionSkewNote("0.
|
|
8357
|
+
return versionSkewNote("0.26.0");
|
|
8115
8358
|
} catch {
|
|
8116
8359
|
return void 0;
|
|
8117
8360
|
}
|
|
@@ -8132,16 +8375,21 @@ ${HOOK_SKEW_NOTE}`;
|
|
|
8132
8375
|
}
|
|
8133
8376
|
};
|
|
8134
8377
|
MEMORY_RUNTIME_CONFIG = {
|
|
8135
|
-
serverVersion: "0.
|
|
8378
|
+
serverVersion: "0.26.0",
|
|
8136
8379
|
baseUrl: BASE_URL,
|
|
8137
8380
|
profile: PROFILE,
|
|
8138
8381
|
harness: HARNESS,
|
|
8139
8382
|
orgId: ORG_ID,
|
|
8140
|
-
agentId: AGENT_ID
|
|
8383
|
+
agentId: AGENT_ID,
|
|
8384
|
+
resolveWorkspaceScope: (agentId) => resolveRepositoryScope(
|
|
8385
|
+
process.env.CLAUDE_PROJECT_DIR || process.cwd(),
|
|
8386
|
+
client,
|
|
8387
|
+
agentId
|
|
8388
|
+
)
|
|
8141
8389
|
};
|
|
8142
|
-
|
|
8390
|
+
buildServer = () => buildMindGraphServer({
|
|
8143
8391
|
client,
|
|
8144
|
-
serverVersion: "0.
|
|
8392
|
+
serverVersion: "0.26.0",
|
|
8145
8393
|
instructions: INSTRUCTIONS,
|
|
8146
8394
|
profile: PROFILE,
|
|
8147
8395
|
agentId: AGENT_ID,
|
|
@@ -8157,8 +8405,8 @@ ${HOOK_SKEW_NOTE}`;
|
|
|
8157
8405
|
});
|
|
8158
8406
|
|
|
8159
8407
|
// src/cli.ts
|
|
8160
|
-
var
|
|
8161
|
-
var
|
|
8408
|
+
var fs9 = __toESM(require("fs"));
|
|
8409
|
+
var path9 = __toESM(require("path"));
|
|
8162
8410
|
var os5 = __toESM(require("os"));
|
|
8163
8411
|
var crypto6 = __toESM(require("crypto"));
|
|
8164
8412
|
var import_child_process = require("child_process");
|
|
@@ -8643,18 +8891,19 @@ async function skillsPush(api, skillPath) {
|
|
|
8643
8891
|
|
|
8644
8892
|
// src/hook-core.ts
|
|
8645
8893
|
var import_node_crypto5 = __toESM(require("crypto"));
|
|
8646
|
-
var
|
|
8894
|
+
var import_node_fs6 = __toESM(require("fs"));
|
|
8647
8895
|
var import_node_os4 = __toESM(require("os"));
|
|
8648
|
-
var
|
|
8896
|
+
var import_node_path5 = __toESM(require("path"));
|
|
8649
8897
|
var import_node_child_process2 = require("child_process");
|
|
8650
8898
|
init_codegraph();
|
|
8651
|
-
|
|
8899
|
+
init_render();
|
|
8900
|
+
init_workspace_scope();
|
|
8652
8901
|
init_call_ledger();
|
|
8653
8902
|
init_error_detail();
|
|
8654
8903
|
init_hook_env();
|
|
8655
8904
|
|
|
8656
8905
|
// src/hook-memory.ts
|
|
8657
|
-
var
|
|
8906
|
+
var import_node_fs5 = __toESM(require("fs"));
|
|
8658
8907
|
var PROMPT_RECALL_LIMIT = 5;
|
|
8659
8908
|
var PROMPT_RECALL_MAX_CHARS = 4e3;
|
|
8660
8909
|
var MIN_PROMPT_RECALL_CHARS = 12;
|
|
@@ -8724,18 +8973,18 @@ function recallTermOverlap(query, text2) {
|
|
|
8724
8973
|
function relevant(query, text2) {
|
|
8725
8974
|
if (query === void 0) return true;
|
|
8726
8975
|
const { matched, usable } = recallTermOverlap(query, text2);
|
|
8727
|
-
return usable
|
|
8976
|
+
return usable >= 2 && matched >= 2;
|
|
8728
8977
|
}
|
|
8729
8978
|
var rec = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
8730
|
-
var
|
|
8979
|
+
var str2 = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
8731
8980
|
var oneLine = (value, max) => {
|
|
8732
8981
|
const flat = value.replace(/\s+/g, " ").trim();
|
|
8733
8982
|
return flat.length <= max ? flat : `${flat.slice(0, max - 1)}\u2026`;
|
|
8734
8983
|
};
|
|
8735
8984
|
var INFRA_TYPES = /* @__PURE__ */ new Set(["Chunk", "Document", "Article", "Session"]);
|
|
8736
8985
|
function nodeType(node) {
|
|
8737
|
-
const plain =
|
|
8738
|
-
return (plain && plain !== "Custom" ? plain : void 0) ??
|
|
8986
|
+
const plain = str2(node.node_type);
|
|
8987
|
+
return (plain && plain !== "Custom" ? plain : void 0) ?? str2(rec(node.node_type)?.Custom) ?? plain ?? "Node";
|
|
8739
8988
|
}
|
|
8740
8989
|
function renderPromptRecall(response, query) {
|
|
8741
8990
|
const payload = rec(response);
|
|
@@ -8748,16 +8997,16 @@ function renderPromptRecall(response, query) {
|
|
|
8748
8997
|
const type = nodeType(node);
|
|
8749
8998
|
if (INFRA_TYPES.has(type)) continue;
|
|
8750
8999
|
const props = rec(node.props);
|
|
8751
|
-
const label =
|
|
9000
|
+
const label = str2(node.label) ?? str2(props?.label) ?? str2(props?.name);
|
|
8752
9001
|
if (!label) continue;
|
|
8753
|
-
const summary =
|
|
9002
|
+
const summary = str2(node.summary) ?? str2(props?.summary) ?? str2(props?.description) ?? str2(props?.content);
|
|
8754
9003
|
const informative = summary && summary !== label ? summary : void 0;
|
|
8755
9004
|
if (!informative && type === "Entity") continue;
|
|
8756
9005
|
if (!relevant(query, `${label} ${informative ?? ""}`)) continue;
|
|
8757
9006
|
const docs = Array.isArray(node.source_documents) ? node.source_documents : [];
|
|
8758
|
-
const source =
|
|
9007
|
+
const source = str2(rec(docs[0])?.title);
|
|
8759
9008
|
let line = `- [${type}] ${oneLine(label, 140)}`;
|
|
8760
|
-
if (node.superseded
|
|
9009
|
+
if (node.superseded) line += " (superseded)";
|
|
8761
9010
|
if (summary && summary !== label) line += ` \u2014 ${oneLine(summary, 240)}`;
|
|
8762
9011
|
if (source) line += ` (source: ${oneLine(source, 100)})`;
|
|
8763
9012
|
lines.push(line);
|
|
@@ -8766,9 +9015,9 @@ function renderPromptRecall(response, query) {
|
|
|
8766
9015
|
for (const raw of articles) {
|
|
8767
9016
|
const article = rec(raw);
|
|
8768
9017
|
if (!article || lines.length >= PROMPT_RECALL_LIMIT) continue;
|
|
8769
|
-
const label =
|
|
9018
|
+
const label = str2(article.label);
|
|
8770
9019
|
if (!label) continue;
|
|
8771
|
-
const content =
|
|
9020
|
+
const content = str2(article.content);
|
|
8772
9021
|
if (!relevant(query, `${label} ${content ?? ""}`)) continue;
|
|
8773
9022
|
lines.push(`- [Article] ${oneLine(label, 140)}${content ? ` \u2014 ${oneLine(content, 240)}` : ""}`);
|
|
8774
9023
|
}
|
|
@@ -8819,7 +9068,7 @@ function renderTranscript(turns) {
|
|
|
8819
9068
|
return start >= 0 ? tail.slice(start + 2) : tail;
|
|
8820
9069
|
}
|
|
8821
9070
|
function readTranscriptSince(file, offset = 0) {
|
|
8822
|
-
const buffer =
|
|
9071
|
+
const buffer = import_node_fs5.default.readFileSync(file);
|
|
8823
9072
|
const start = offset > buffer.length ? 0 : offset;
|
|
8824
9073
|
const lastNewline = buffer.lastIndexOf(10);
|
|
8825
9074
|
if (lastNewline < start) return { text: "", nextOffset: start };
|
|
@@ -8863,33 +9112,33 @@ function defaultLedger(sessionId, options) {
|
|
|
8863
9112
|
};
|
|
8864
9113
|
}
|
|
8865
9114
|
function runtimeDir(options) {
|
|
8866
|
-
return options.runtimeDir || process.env.MINDGRAPH_RUNTIME_DIR ||
|
|
9115
|
+
return options.runtimeDir || process.env.MINDGRAPH_RUNTIME_DIR || import_node_path5.default.join(import_node_os4.default.homedir(), ".mindgraph", "runtime");
|
|
8867
9116
|
}
|
|
8868
9117
|
function ledgerPath(sessionId, options) {
|
|
8869
9118
|
const harness = "harness" in options ? options.harness : "claude-code";
|
|
8870
9119
|
const key = import_node_crypto5.default.createHash("sha256").update(`${harness}\0${sessionId}`).digest("hex");
|
|
8871
|
-
return
|
|
9120
|
+
return import_node_path5.default.join(runtimeDir(options), `${key}.json`);
|
|
8872
9121
|
}
|
|
8873
9122
|
function waitBriefly(ms) {
|
|
8874
9123
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
8875
9124
|
}
|
|
8876
9125
|
function withLedger(sessionId, options, update) {
|
|
8877
9126
|
const file = ledgerPath(sessionId, options);
|
|
8878
|
-
|
|
9127
|
+
import_node_fs6.default.mkdirSync(import_node_path5.default.dirname(file), { recursive: true, mode: 448 });
|
|
8879
9128
|
try {
|
|
8880
|
-
|
|
9129
|
+
import_node_fs6.default.chmodSync(import_node_path5.default.dirname(file), 448);
|
|
8881
9130
|
} catch {
|
|
8882
9131
|
}
|
|
8883
9132
|
const lock = `${file}.lock`;
|
|
8884
9133
|
let lockFd;
|
|
8885
9134
|
for (let attempt = 0; attempt < 400; attempt++) {
|
|
8886
9135
|
try {
|
|
8887
|
-
lockFd =
|
|
9136
|
+
lockFd = import_node_fs6.default.openSync(lock, "wx", 384);
|
|
8888
9137
|
break;
|
|
8889
9138
|
} catch (error2) {
|
|
8890
9139
|
if (error2.code !== "EEXIST") throw error2;
|
|
8891
9140
|
try {
|
|
8892
|
-
if (Date.now() -
|
|
9141
|
+
if (Date.now() - import_node_fs6.default.statSync(lock).mtimeMs > 3e3) import_node_fs6.default.unlinkSync(lock);
|
|
8893
9142
|
} catch {
|
|
8894
9143
|
}
|
|
8895
9144
|
waitBriefly(10);
|
|
@@ -8903,25 +9152,25 @@ function withLedger(sessionId, options, update) {
|
|
|
8903
9152
|
try {
|
|
8904
9153
|
ledger = {
|
|
8905
9154
|
...ledger,
|
|
8906
|
-
...JSON.parse(
|
|
9155
|
+
...JSON.parse(import_node_fs6.default.readFileSync(file, "utf8"))
|
|
8907
9156
|
};
|
|
8908
9157
|
} catch {
|
|
8909
9158
|
}
|
|
8910
9159
|
const result = update(ledger);
|
|
8911
9160
|
ledger.updatedAt = Date.now();
|
|
8912
9161
|
const temporary = `${file}.${process.pid}.${import_node_crypto5.default.randomUUID()}.tmp`;
|
|
8913
|
-
|
|
9162
|
+
import_node_fs6.default.writeFileSync(temporary, `${JSON.stringify(ledger)}
|
|
8914
9163
|
`, { mode: 384 });
|
|
8915
|
-
|
|
9164
|
+
import_node_fs6.default.renameSync(temporary, file);
|
|
8916
9165
|
try {
|
|
8917
|
-
|
|
9166
|
+
import_node_fs6.default.chmodSync(file, 384);
|
|
8918
9167
|
} catch {
|
|
8919
9168
|
}
|
|
8920
9169
|
return result;
|
|
8921
9170
|
} finally {
|
|
8922
|
-
|
|
9171
|
+
import_node_fs6.default.closeSync(lockFd);
|
|
8923
9172
|
try {
|
|
8924
|
-
|
|
9173
|
+
import_node_fs6.default.unlinkSync(lock);
|
|
8925
9174
|
} catch {
|
|
8926
9175
|
}
|
|
8927
9176
|
}
|
|
@@ -9031,6 +9280,11 @@ function renderBrief(brief, meta) {
|
|
|
9031
9280
|
);
|
|
9032
9281
|
const description = clipTracked(props?.description, 400);
|
|
9033
9282
|
if (description) lines.push(` ${description}`);
|
|
9283
|
+
const taskScopes = Array.isArray(brief?.task_scope_uids) ? brief.task_scope_uids.map(String) : [];
|
|
9284
|
+
const taskRepository = taskScopes.map((uid) => meta.repositoryNames?.[uid]).find(Boolean);
|
|
9285
|
+
if (taskRepository && Object.keys(meta.repositoryNames ?? {}).length > 1) {
|
|
9286
|
+
lines.push(`Repository: ${taskRepository}`);
|
|
9287
|
+
}
|
|
9034
9288
|
const reason = string(brief?.selection_reason);
|
|
9035
9289
|
if (reason) lines.push(`Selection: ${reason}`);
|
|
9036
9290
|
const lease = object(brief?.claim) || object(brief?.lease);
|
|
@@ -9076,6 +9330,8 @@ ${blockerLines.join("\n")}`);
|
|
|
9076
9330
|
}
|
|
9077
9331
|
}
|
|
9078
9332
|
const optional = [];
|
|
9333
|
+
const otherWork = renderOtherEligibleWork(brief, meta.repositoryNames);
|
|
9334
|
+
if (otherWork) optional.push(otherWork);
|
|
9079
9335
|
const knowledge = Array.isArray(brief?.knowledge) ? brief.knowledge : [];
|
|
9080
9336
|
if (knowledge.length > 5) bounded = true;
|
|
9081
9337
|
const knowledgeLines = knowledge.slice(0, 5).map((item) => nodeLine(item, 350)).filter(Boolean);
|
|
@@ -9112,62 +9368,6 @@ ${knowledgeLines.join("\n")}`);
|
|
|
9112
9368
|
return rendered2.length <= BRIEF_CHAR_BUDGET ? rendered2 : `${rendered2.slice(0, BRIEF_CHAR_BUDGET - 40)}
|
|
9113
9369
|
\u2026[bounded work brief truncated]`;
|
|
9114
9370
|
}
|
|
9115
|
-
async function repositoryScopeUids(input, client2, options, agentId) {
|
|
9116
|
-
if (!client2.entity) return { uids: [], failures: [] };
|
|
9117
|
-
const adapter = new CodegraphAdapter({
|
|
9118
|
-
cwd: input.cwd,
|
|
9119
|
-
workspaceFile: options.workspaceFile
|
|
9120
|
-
});
|
|
9121
|
-
const configured = await adapter.resolveRepositories();
|
|
9122
|
-
const repositories = configured.length > 0 ? configured : [await adapter.resolveRepository()];
|
|
9123
|
-
const failures = [];
|
|
9124
|
-
const resolved = await Promise.all(
|
|
9125
|
-
repositories.map(async (repository) => {
|
|
9126
|
-
try {
|
|
9127
|
-
const payload = object(
|
|
9128
|
-
await client2.entity({
|
|
9129
|
-
action: "resolve_identity",
|
|
9130
|
-
identity: {
|
|
9131
|
-
namespace: "external.code",
|
|
9132
|
-
key_version: 1,
|
|
9133
|
-
key: repositoryIdentityKey(repository)
|
|
9134
|
-
},
|
|
9135
|
-
agent_id: agentId
|
|
9136
|
-
})
|
|
9137
|
-
);
|
|
9138
|
-
if (payload?.status === "existing" && string(payload.uid)) {
|
|
9139
|
-
return string(payload.uid);
|
|
9140
|
-
}
|
|
9141
|
-
if (payload?.status === "absent") {
|
|
9142
|
-
const anchored = await anchorRepository(
|
|
9143
|
-
client2,
|
|
9144
|
-
repository,
|
|
9145
|
-
repository.spaceUid ?? `space:agent:${agentId}`,
|
|
9146
|
-
agentId
|
|
9147
|
-
);
|
|
9148
|
-
if (anchored?.uid) return anchored.uid;
|
|
9149
|
-
failures.push(
|
|
9150
|
-
`${repository.repoId}: anchor ${anchored?.status ?? "failed"}`
|
|
9151
|
-
);
|
|
9152
|
-
return void 0;
|
|
9153
|
-
}
|
|
9154
|
-
failures.push(
|
|
9155
|
-
`${repository.repoId}: ${string(payload?.status) || "unresolved"}`
|
|
9156
|
-
);
|
|
9157
|
-
return void 0;
|
|
9158
|
-
} catch (error2) {
|
|
9159
|
-
failures.push(
|
|
9160
|
-
`${repository.repoId}: ${error2?.message || "resolve failed"}`
|
|
9161
|
-
);
|
|
9162
|
-
return void 0;
|
|
9163
|
-
}
|
|
9164
|
-
})
|
|
9165
|
-
);
|
|
9166
|
-
return {
|
|
9167
|
-
uids: [...new Set(resolved.filter((uid) => Boolean(uid)))],
|
|
9168
|
-
failures
|
|
9169
|
-
};
|
|
9170
|
-
}
|
|
9171
9371
|
function isOwnPriorWork(brief, agentId, harness) {
|
|
9172
9372
|
const reason = string(brief?.selection_reason);
|
|
9173
9373
|
if (reason === "owned_live_lease" || reason === "same_agent_session_rebind") {
|
|
@@ -9191,30 +9391,30 @@ function hasMindgraphServer(servers) {
|
|
|
9191
9391
|
return false;
|
|
9192
9392
|
}
|
|
9193
9393
|
function pathIsWithin(ancestor, descendant) {
|
|
9194
|
-
const base =
|
|
9195
|
-
const target =
|
|
9196
|
-
return target === base || target.startsWith(base +
|
|
9394
|
+
const base = import_node_path5.default.resolve(ancestor);
|
|
9395
|
+
const target = import_node_path5.default.resolve(descendant);
|
|
9396
|
+
return target === base || target.startsWith(base + import_node_path5.default.sep);
|
|
9197
9397
|
}
|
|
9198
9398
|
function claudeMcpRegistrationNote(input, options) {
|
|
9199
9399
|
if (options.harness !== "claude-code") return void 0;
|
|
9200
9400
|
if (process.env.MINDGRAPH_SKIP_MCP_REGISTRATION_CHECK === "1") return void 0;
|
|
9201
9401
|
try {
|
|
9202
|
-
const configPath = options.claudeConfigPath ||
|
|
9203
|
-
if (!
|
|
9204
|
-
const config = JSON.parse(
|
|
9402
|
+
const configPath = options.claudeConfigPath || import_node_path5.default.join(import_node_os4.default.homedir(), ".claude.json");
|
|
9403
|
+
if (!import_node_fs6.default.existsSync(configPath)) return void 0;
|
|
9404
|
+
const config = JSON.parse(import_node_fs6.default.readFileSync(configPath, "utf8"));
|
|
9205
9405
|
if (hasMindgraphServer(config.mcpServers)) return void 0;
|
|
9206
|
-
const cwd =
|
|
9406
|
+
const cwd = import_node_path5.default.resolve(input.cwd || ".");
|
|
9207
9407
|
for (const [projectDir, project] of Object.entries(config.projects || {})) {
|
|
9208
9408
|
if (!pathIsWithin(projectDir, cwd)) continue;
|
|
9209
9409
|
if (hasMindgraphServer(project?.mcpServers)) return void 0;
|
|
9210
9410
|
}
|
|
9211
|
-
for (let dir = cwd; ; dir =
|
|
9212
|
-
const candidate =
|
|
9213
|
-
if (
|
|
9214
|
-
const parsed = JSON.parse(
|
|
9411
|
+
for (let dir = cwd; ; dir = import_node_path5.default.dirname(dir)) {
|
|
9412
|
+
const candidate = import_node_path5.default.join(dir, ".mcp.json");
|
|
9413
|
+
if (import_node_fs6.default.existsSync(candidate)) {
|
|
9414
|
+
const parsed = JSON.parse(import_node_fs6.default.readFileSync(candidate, "utf8"));
|
|
9215
9415
|
if (hasMindgraphServer(parsed.mcpServers)) return void 0;
|
|
9216
9416
|
}
|
|
9217
|
-
if (dir ===
|
|
9417
|
+
if (dir === import_node_path5.default.dirname(dir)) break;
|
|
9218
9418
|
}
|
|
9219
9419
|
} catch {
|
|
9220
9420
|
return void 0;
|
|
@@ -9244,7 +9444,10 @@ async function sessionStart(input, client2, options) {
|
|
|
9244
9444
|
return void 0;
|
|
9245
9445
|
});
|
|
9246
9446
|
}
|
|
9247
|
-
const scope = await
|
|
9447
|
+
const scope = await resolveRepositoryScope(input.cwd, client2, agentId, {
|
|
9448
|
+
workspaceFile: options.workspaceFile,
|
|
9449
|
+
cache: options.scopeCacheTenant ? { dir: runtimeDir(options), tenant: options.scopeCacheTenant } : void 0
|
|
9450
|
+
});
|
|
9248
9451
|
if (scope.failures.length > 0) {
|
|
9249
9452
|
console.error(
|
|
9250
9453
|
`mindgraph-hook: ${scope.failures.length} repository scope(s) failed to resolve (${scope.failures.join("; ")}); resume runs ${scope.uids.length > 0 ? "narrowed" : "UNSCOPED"}`
|
|
@@ -9256,9 +9459,17 @@ async function sessionStart(input, client2, options) {
|
|
|
9256
9459
|
action: "resume_work",
|
|
9257
9460
|
session_uid: sessionUid,
|
|
9258
9461
|
...scopeUids.length > 0 ? { scope_uids: scopeUids } : {},
|
|
9462
|
+
// Above several repositories one task is not the picture: ask for the
|
|
9463
|
+
// work ranked behind it too. An older server ignores the field.
|
|
9464
|
+
...scopeUids.length > 1 ? { limit: WORKSPACE_RESUME_LIMIT } : {},
|
|
9259
9465
|
agent_id: agentId
|
|
9260
9466
|
})
|
|
9261
9467
|
);
|
|
9468
|
+
const workspaceList = {
|
|
9469
|
+
task_scope_uids: brief?.task_scope_uids,
|
|
9470
|
+
other_eligible: brief?.other_eligible,
|
|
9471
|
+
eligible_total: brief?.eligible_total
|
|
9472
|
+
};
|
|
9262
9473
|
const task = object(brief?.task);
|
|
9263
9474
|
const lease = object(brief?.lease);
|
|
9264
9475
|
const prior = readLedger(input.session_id, options);
|
|
@@ -9297,7 +9508,7 @@ async function sessionStart(input, client2, options) {
|
|
|
9297
9508
|
agent_id: agentId
|
|
9298
9509
|
})
|
|
9299
9510
|
);
|
|
9300
|
-
if (reResumed) brief = reResumed;
|
|
9511
|
+
if (reResumed) brief = { ...reResumed, ...workspaceList };
|
|
9301
9512
|
} catch {
|
|
9302
9513
|
}
|
|
9303
9514
|
if (brief) brief.claim = claimed;
|
|
@@ -9308,7 +9519,8 @@ async function sessionStart(input, client2, options) {
|
|
|
9308
9519
|
const briefText = renderBrief(brief || { status: "no_eligible_work" }, {
|
|
9309
9520
|
claimed: Boolean(claimed) || stillHeld,
|
|
9310
9521
|
claimFailed: claimFailed && !stillHeld,
|
|
9311
|
-
scopeNote
|
|
9522
|
+
scopeNote,
|
|
9523
|
+
repositoryNames: scope.names
|
|
9312
9524
|
});
|
|
9313
9525
|
const registrationNote = claudeMcpRegistrationNote(input, options);
|
|
9314
9526
|
if (registrationNote) {
|
|
@@ -9755,7 +9967,7 @@ function previewCapture(transcriptPath, sessionId, options = {}) {
|
|
|
9755
9967
|
let ledger = {};
|
|
9756
9968
|
try {
|
|
9757
9969
|
ledger = JSON.parse(
|
|
9758
|
-
|
|
9970
|
+
import_node_fs6.default.readFileSync(
|
|
9759
9971
|
ledgerPath(sessionId, { ...options, harness: "claude-code" }),
|
|
9760
9972
|
"utf8"
|
|
9761
9973
|
)
|
|
@@ -9776,13 +9988,13 @@ function readHookStats(sessionId, options = {}) {
|
|
|
9776
9988
|
let best;
|
|
9777
9989
|
let names = [];
|
|
9778
9990
|
try {
|
|
9779
|
-
names =
|
|
9991
|
+
names = import_node_fs6.default.readdirSync(dir).filter((name) => /^[0-9a-f]{64}\.json$/.test(name));
|
|
9780
9992
|
} catch {
|
|
9781
9993
|
return void 0;
|
|
9782
9994
|
}
|
|
9783
9995
|
for (const name of names) {
|
|
9784
9996
|
try {
|
|
9785
|
-
const ledger = JSON.parse(
|
|
9997
|
+
const ledger = JSON.parse(import_node_fs6.default.readFileSync(import_node_path5.default.join(dir, name), "utf8"));
|
|
9786
9998
|
if (!ledger.harnessSessionId || !ledger.hookStats) continue;
|
|
9787
9999
|
if (sessionId && ledger.harnessSessionId !== sessionId) continue;
|
|
9788
10000
|
const updatedAt = Number(ledger.updatedAt) || 0;
|
|
@@ -9861,15 +10073,15 @@ async function runCodexHook(input, client2, options = {}) {
|
|
|
9861
10073
|
init_hook_installer();
|
|
9862
10074
|
function recordHookHealth(harness, error2) {
|
|
9863
10075
|
try {
|
|
9864
|
-
const dir = process.env.MINDGRAPH_RUNTIME_DIR ||
|
|
9865
|
-
|
|
9866
|
-
const file =
|
|
10076
|
+
const dir = process.env.MINDGRAPH_RUNTIME_DIR || path9.join(os5.homedir(), ".mindgraph", "runtime");
|
|
10077
|
+
fs9.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
10078
|
+
const file = path9.join(dir, "hook-health.json");
|
|
9867
10079
|
let failures = 0;
|
|
9868
10080
|
try {
|
|
9869
|
-
failures = Number(JSON.parse(
|
|
10081
|
+
failures = Number(JSON.parse(fs9.readFileSync(file, "utf8")).consecutiveFailures) || 0;
|
|
9870
10082
|
} catch {
|
|
9871
10083
|
}
|
|
9872
|
-
|
|
10084
|
+
fs9.writeFileSync(
|
|
9873
10085
|
file,
|
|
9874
10086
|
`${JSON.stringify({
|
|
9875
10087
|
harness,
|
|
@@ -9885,14 +10097,14 @@ function recordHookHealth(harness, error2) {
|
|
|
9885
10097
|
}
|
|
9886
10098
|
function clearHookHealth(harness) {
|
|
9887
10099
|
try {
|
|
9888
|
-
const file =
|
|
9889
|
-
process.env.MINDGRAPH_RUNTIME_DIR ||
|
|
10100
|
+
const file = path9.join(
|
|
10101
|
+
process.env.MINDGRAPH_RUNTIME_DIR || path9.join(os5.homedir(), ".mindgraph", "runtime"),
|
|
9890
10102
|
"hook-health.json"
|
|
9891
10103
|
);
|
|
9892
|
-
if (!
|
|
9893
|
-
const current = JSON.parse(
|
|
10104
|
+
if (!fs9.existsSync(file)) return;
|
|
10105
|
+
const current = JSON.parse(fs9.readFileSync(file, "utf8"));
|
|
9894
10106
|
if (!Number(current.consecutiveFailures)) return;
|
|
9895
|
-
|
|
10107
|
+
fs9.writeFileSync(
|
|
9896
10108
|
file,
|
|
9897
10109
|
`${JSON.stringify({
|
|
9898
10110
|
harness,
|
|
@@ -9908,7 +10120,7 @@ function clearHookHealth(harness) {
|
|
|
9908
10120
|
function getClaudeDesktopConfigPath() {
|
|
9909
10121
|
switch (process.platform) {
|
|
9910
10122
|
case "darwin":
|
|
9911
|
-
return
|
|
10123
|
+
return path9.join(
|
|
9912
10124
|
os5.homedir(),
|
|
9913
10125
|
"Library",
|
|
9914
10126
|
"Application Support",
|
|
@@ -9916,13 +10128,13 @@ function getClaudeDesktopConfigPath() {
|
|
|
9916
10128
|
"claude_desktop_config.json"
|
|
9917
10129
|
);
|
|
9918
10130
|
case "win32":
|
|
9919
|
-
return
|
|
9920
|
-
process.env.APPDATA ||
|
|
10131
|
+
return path9.join(
|
|
10132
|
+
process.env.APPDATA || path9.join(os5.homedir(), "AppData", "Roaming"),
|
|
9921
10133
|
"Claude",
|
|
9922
10134
|
"claude_desktop_config.json"
|
|
9923
10135
|
);
|
|
9924
10136
|
case "linux":
|
|
9925
|
-
return
|
|
10137
|
+
return path9.join(
|
|
9926
10138
|
os5.homedir(),
|
|
9927
10139
|
".config",
|
|
9928
10140
|
"Claude",
|
|
@@ -9933,16 +10145,16 @@ function getClaudeDesktopConfigPath() {
|
|
|
9933
10145
|
}
|
|
9934
10146
|
}
|
|
9935
10147
|
function readJsonFile(filePath) {
|
|
9936
|
-
if (!
|
|
9937
|
-
const content =
|
|
10148
|
+
if (!fs9.existsSync(filePath)) return {};
|
|
10149
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
9938
10150
|
return JSON.parse(content);
|
|
9939
10151
|
}
|
|
9940
10152
|
function writeJsonFile(filePath, data) {
|
|
9941
|
-
const dir =
|
|
9942
|
-
if (!
|
|
9943
|
-
|
|
10153
|
+
const dir = path9.dirname(filePath);
|
|
10154
|
+
if (!fs9.existsSync(dir)) {
|
|
10155
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
9944
10156
|
}
|
|
9945
|
-
|
|
10157
|
+
fs9.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
9946
10158
|
}
|
|
9947
10159
|
function getMcpServerEntry(apiKey, baseUrl) {
|
|
9948
10160
|
const env = { MINDGRAPH_API_KEY: apiKey };
|
|
@@ -10029,7 +10241,7 @@ function installClaudeCode(apiKey, baseUrl, agentId) {
|
|
|
10029
10241
|
}
|
|
10030
10242
|
function uninstallClaudeDesktop() {
|
|
10031
10243
|
const configPath = getClaudeDesktopConfigPath();
|
|
10032
|
-
if (!
|
|
10244
|
+
if (!fs9.existsSync(configPath)) {
|
|
10033
10245
|
console.log("Claude Desktop config not found. Nothing to remove.");
|
|
10034
10246
|
return;
|
|
10035
10247
|
}
|
|
@@ -10056,7 +10268,7 @@ function uninstallClaudeCode() {
|
|
|
10056
10268
|
}
|
|
10057
10269
|
function printStatus() {
|
|
10058
10270
|
const desktopPath = getClaudeDesktopConfigPath();
|
|
10059
|
-
if (
|
|
10271
|
+
if (fs9.existsSync(desktopPath)) {
|
|
10060
10272
|
const config = readJsonFile(desktopPath);
|
|
10061
10273
|
const servers = config.mcpServers;
|
|
10062
10274
|
if (servers && "mindgraph" in servers) {
|
|
@@ -10074,12 +10286,12 @@ function printStatus() {
|
|
|
10074
10286
|
`MINDGRAPH_BASE_URL: ${process.env.MINDGRAPH_BASE_URL || "https://api.mindgraph.cloud (default)"}`
|
|
10075
10287
|
);
|
|
10076
10288
|
try {
|
|
10077
|
-
const healthFile =
|
|
10078
|
-
process.env.MINDGRAPH_RUNTIME_DIR ||
|
|
10289
|
+
const healthFile = path9.join(
|
|
10290
|
+
process.env.MINDGRAPH_RUNTIME_DIR || path9.join(os5.homedir(), ".mindgraph", "runtime"),
|
|
10079
10291
|
"hook-health.json"
|
|
10080
10292
|
);
|
|
10081
|
-
if (
|
|
10082
|
-
const health = JSON.parse(
|
|
10293
|
+
if (fs9.existsSync(healthFile)) {
|
|
10294
|
+
const health = JSON.parse(fs9.readFileSync(healthFile, "utf8"));
|
|
10083
10295
|
const failures = Number(health.consecutiveFailures) || 0;
|
|
10084
10296
|
console.log(
|
|
10085
10297
|
failures > 0 ? `Hooks: FAILING (${failures} consecutive; last: ${health.lastError} at ${health.lastFailureAt})` : `Hooks: healthy (last recovery ${health.lastOkAt || "unknown"})`
|
|
@@ -10108,21 +10320,21 @@ function printClaudeHookFeatures(features) {
|
|
|
10108
10320
|
);
|
|
10109
10321
|
}
|
|
10110
10322
|
function latestTranscript(cwd) {
|
|
10111
|
-
const dir =
|
|
10323
|
+
const dir = path9.join(
|
|
10112
10324
|
os5.homedir(),
|
|
10113
10325
|
".claude",
|
|
10114
10326
|
"projects",
|
|
10115
10327
|
cwd.replace(/[^A-Za-z0-9]/g, "-")
|
|
10116
10328
|
);
|
|
10117
10329
|
try {
|
|
10118
|
-
return
|
|
10330
|
+
return fs9.readdirSync(dir).filter((name) => name.endsWith(".jsonl")).map((name) => path9.join(dir, name)).sort((a, b) => fs9.statSync(b).mtimeMs - fs9.statSync(a).mtimeMs)[0];
|
|
10119
10331
|
} catch {
|
|
10120
10332
|
return void 0;
|
|
10121
10333
|
}
|
|
10122
10334
|
}
|
|
10123
10335
|
function finishCodeInstall(apiKey, baseUrl, withHooks, agentId) {
|
|
10124
10336
|
if (withHooks) {
|
|
10125
|
-
const runner = installHookRunner(__filename, void 0, "0.
|
|
10337
|
+
const runner = installHookRunner(__filename, void 0, "0.26.0");
|
|
10126
10338
|
console.log(`Installed pinned hook runner at ${runner}`);
|
|
10127
10339
|
const features = claudeHookFeatures();
|
|
10128
10340
|
const result = installClaudeHooks("user", void 0, features);
|
|
@@ -10450,7 +10662,7 @@ ${total} skill(s) synced.`);
|
|
|
10450
10662
|
if (hookHarness !== "claude-code" && hookHarness !== "codex") {
|
|
10451
10663
|
throw new Error("--harness must be claude-code or codex");
|
|
10452
10664
|
}
|
|
10453
|
-
const runner = installHookRunner(__filename, void 0, "0.
|
|
10665
|
+
const runner = installHookRunner(__filename, void 0, "0.26.0");
|
|
10454
10666
|
console.log(`Installed pinned hook runner at ${runner}`);
|
|
10455
10667
|
const features = hookHarness === "claude-code" ? claudeHookFeatures() : void 0;
|
|
10456
10668
|
const result = hookHarness === "codex" ? installCodexHooks(scope, projectDir) : installClaudeHooks(scope, projectDir, features);
|
|
@@ -10525,7 +10737,14 @@ ${total} skill(s) synced.`);
|
|
|
10525
10737
|
// No harness-name fallback here: hook-core derives a stable
|
|
10526
10738
|
// per-user default shared by both harnesses, which is what lets a
|
|
10527
10739
|
// task claimed under Codex rebind under Claude Code.
|
|
10528
|
-
agentId: process.env.MINDGRAPH_AGENT_ID || stored.agentId
|
|
10740
|
+
agentId: process.env.MINDGRAPH_AGENT_ID || stored.agentId,
|
|
10741
|
+
scopeCacheTenant: crypto6.createHash("sha256").update(
|
|
10742
|
+
[
|
|
10743
|
+
hookBaseUrl || "https://api.mindgraph.cloud",
|
|
10744
|
+
hookApiKey,
|
|
10745
|
+
process.env.MINDGRAPH_ORG_ID || stored.orgId || ""
|
|
10746
|
+
].join("\n")
|
|
10747
|
+
).digest("hex").slice(0, 16)
|
|
10529
10748
|
};
|
|
10530
10749
|
const output = hookHarness === "codex" ? await runCodexHook(
|
|
10531
10750
|
input,
|
|
@@ -10583,13 +10802,13 @@ ${total} skill(s) synced.`);
|
|
|
10583
10802
|
process.exit(1);
|
|
10584
10803
|
}
|
|
10585
10804
|
const transcript = positionals[1] || latestTranscript(process.cwd());
|
|
10586
|
-
if (!transcript || !
|
|
10805
|
+
if (!transcript || !fs9.existsSync(transcript)) {
|
|
10587
10806
|
console.error(
|
|
10588
10807
|
"No transcript found. Pass a Claude Code transcript path (~/.claude/projects/<dir>/<session>.jsonl)."
|
|
10589
10808
|
);
|
|
10590
10809
|
process.exit(1);
|
|
10591
10810
|
}
|
|
10592
|
-
const sessionId = parseArgs(process.argv).sessionId ||
|
|
10811
|
+
const sessionId = parseArgs(process.argv).sessionId || path9.basename(transcript, ".jsonl");
|
|
10593
10812
|
const preview = previewCapture(transcript, sessionId, {
|
|
10594
10813
|
agentId: process.env.MINDGRAPH_AGENT_ID || loadHookEnv().agentId
|
|
10595
10814
|
});
|