mindgraph-mcp 0.15.0 → 0.17.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 +5 -5
- package/dist/cli.js +761 -81
- package/dist/index.js +750 -78
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2123,13 +2123,13 @@ var init_code_tool = __esm({
|
|
|
2123
2123
|
init_error_detail();
|
|
2124
2124
|
init_codegraph();
|
|
2125
2125
|
ok = (data) => ({
|
|
2126
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
2126
|
+
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
2127
2127
|
});
|
|
2128
2128
|
err = (code, message, details) => ({
|
|
2129
2129
|
content: [
|
|
2130
2130
|
{
|
|
2131
2131
|
type: "text",
|
|
2132
|
-
text: JSON.stringify({ error: message, code, details }
|
|
2132
|
+
text: JSON.stringify({ error: message, code, details })
|
|
2133
2133
|
}
|
|
2134
2134
|
],
|
|
2135
2135
|
isError: true
|
|
@@ -2335,7 +2335,7 @@ var init_sync_tool = __esm({
|
|
|
2335
2335
|
init_codegraph();
|
|
2336
2336
|
init_error_detail();
|
|
2337
2337
|
ok2 = (data) => ({
|
|
2338
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
2338
|
+
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
2339
2339
|
});
|
|
2340
2340
|
err2 = (code, message) => ({
|
|
2341
2341
|
content: [{ type: "text", text: JSON.stringify({ code, error: message }) }],
|
|
@@ -2412,6 +2412,523 @@ var init_sync_tool = __esm({
|
|
|
2412
2412
|
}
|
|
2413
2413
|
});
|
|
2414
2414
|
|
|
2415
|
+
// src/render.ts
|
|
2416
|
+
function obj(value) {
|
|
2417
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2418
|
+
}
|
|
2419
|
+
function str(value) {
|
|
2420
|
+
return typeof value === "string" && value ? value : void 0;
|
|
2421
|
+
}
|
|
2422
|
+
function num(value) {
|
|
2423
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2424
|
+
}
|
|
2425
|
+
function clip(value, max, state) {
|
|
2426
|
+
const text = str(value);
|
|
2427
|
+
if (!text) return void 0;
|
|
2428
|
+
if (text.length <= max) return text;
|
|
2429
|
+
state.bounded = true;
|
|
2430
|
+
return `${text.slice(0, max - 1)}\u2026`;
|
|
2431
|
+
}
|
|
2432
|
+
function isNodeShaped(value) {
|
|
2433
|
+
const node = obj(value);
|
|
2434
|
+
return Boolean(
|
|
2435
|
+
node && str(node.uid) && (str(node.label) || str(node.node_type) || obj(node.node_type))
|
|
2436
|
+
);
|
|
2437
|
+
}
|
|
2438
|
+
function nodeDisplayType(node) {
|
|
2439
|
+
const props = obj(node.props);
|
|
2440
|
+
const plain = str(node.node_type);
|
|
2441
|
+
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";
|
|
2442
|
+
}
|
|
2443
|
+
function nodeFields(node) {
|
|
2444
|
+
const props = obj(node.props);
|
|
2445
|
+
return obj(props?.fields) ?? obj(obj(props?.data)?.fields);
|
|
2446
|
+
}
|
|
2447
|
+
function unwrapSearchResult(value) {
|
|
2448
|
+
if (isNodeShaped(value)) return value;
|
|
2449
|
+
const wrapper = obj(value);
|
|
2450
|
+
const inner = obj(wrapper?.node) ?? obj(wrapper?.object);
|
|
2451
|
+
if (inner && isNodeShaped(inner)) {
|
|
2452
|
+
const score = num(wrapper.score);
|
|
2453
|
+
return score !== void 0 ? { ...inner, retrieval_score: score } : inner;
|
|
2454
|
+
}
|
|
2455
|
+
return void 0;
|
|
2456
|
+
}
|
|
2457
|
+
function statusTag(node) {
|
|
2458
|
+
const tags = [];
|
|
2459
|
+
if (node.superseded === true) {
|
|
2460
|
+
tags.push(
|
|
2461
|
+
node.superseded_by ? `SUPERSEDED by ${str(node.superseded_by)}` : "SUPERSEDED"
|
|
2462
|
+
);
|
|
2463
|
+
}
|
|
2464
|
+
const truth = str(node.truth_status) ?? str(obj(node.props)?.truth_status);
|
|
2465
|
+
if (truth && truth !== "accepted") tags.push(`truth: ${truth}`);
|
|
2466
|
+
if (node.currently_valid === false) tags.push("OUTSIDE ITS VALIDITY WINDOW");
|
|
2467
|
+
if (node.valid_at_time === false) tags.push("NOT VALID AT THE REQUESTED TIME");
|
|
2468
|
+
return tags.length > 0 ? ` [${tags.join("; ")}]` : "";
|
|
2469
|
+
}
|
|
2470
|
+
function firstQuote(node, state) {
|
|
2471
|
+
const chunks = Array.isArray(node.source_chunks) ? node.source_chunks : [];
|
|
2472
|
+
for (const raw of chunks) {
|
|
2473
|
+
const chunk = obj(raw);
|
|
2474
|
+
const quote = str(chunk?.quote) ?? str(obj(chunk?.anchor)?.exact);
|
|
2475
|
+
if (quote) return clip(quote, QUOTE_MAX, state);
|
|
2476
|
+
}
|
|
2477
|
+
return void 0;
|
|
2478
|
+
}
|
|
2479
|
+
function sourceTag(node) {
|
|
2480
|
+
const docs = Array.isArray(node.source_documents) ? node.source_documents : [];
|
|
2481
|
+
const first = obj(docs[0]);
|
|
2482
|
+
const title = str(first?.title);
|
|
2483
|
+
if (!title) return void 0;
|
|
2484
|
+
const extra = docs.length > 1 ? ` (+${docs.length - 1} more)` : "";
|
|
2485
|
+
return `from: ${title}${extra}`;
|
|
2486
|
+
}
|
|
2487
|
+
function layer7Fields(node, state) {
|
|
2488
|
+
const fields = nodeFields(node);
|
|
2489
|
+
if (!fields) return void 0;
|
|
2490
|
+
const pairs = Object.entries(fields).filter(([, value]) => value !== null && value !== void 0 && value !== "").map(([key, value]) => `${key}: ${Array.isArray(value) ? value.join(", ") : String(value)}`);
|
|
2491
|
+
if (pairs.length === 0) return void 0;
|
|
2492
|
+
return clip(pairs.join("; "), SUMMARY_MAX, state);
|
|
2493
|
+
}
|
|
2494
|
+
function nodeSummaryText(node, state) {
|
|
2495
|
+
const props = obj(node.props);
|
|
2496
|
+
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);
|
|
2497
|
+
}
|
|
2498
|
+
function nodeLines(node, state) {
|
|
2499
|
+
const label = clip(node.label, LABEL_MAX, state) || "(unlabeled)";
|
|
2500
|
+
const type = nodeDisplayType(node);
|
|
2501
|
+
const confidence = num(node.confidence);
|
|
2502
|
+
const score = num(node.retrieval_score);
|
|
2503
|
+
const parenthetical = [
|
|
2504
|
+
type,
|
|
2505
|
+
confidence !== void 0 ? `confidence ${confidence}` : void 0,
|
|
2506
|
+
score !== void 0 ? `score ${Math.round(score * 100) / 100}` : void 0
|
|
2507
|
+
].filter(Boolean).join(", ");
|
|
2508
|
+
const head = `- **${label}** [${str(node.uid) || "?"}] (${parenthetical})${statusTag(node)}`;
|
|
2509
|
+
const summary = nodeSummaryText(node, state);
|
|
2510
|
+
const lines = [summary && summary !== label ? `${head}: ${summary}` : head];
|
|
2511
|
+
const fields = layer7Fields(node, state);
|
|
2512
|
+
if (fields) lines.push(` - fields: ${fields}`);
|
|
2513
|
+
const quote = firstQuote(node, state);
|
|
2514
|
+
if (quote) lines.push(` - source quote: "${quote}"`);
|
|
2515
|
+
const source = sourceTag(node);
|
|
2516
|
+
if (source) lines.push(` - ${source}`);
|
|
2517
|
+
const believers = Array.isArray(node.believed_by) ? node.believed_by : [];
|
|
2518
|
+
if (believers.length > 0) {
|
|
2519
|
+
const rendered2 = believers.slice(0, 3).map((raw) => {
|
|
2520
|
+
const believer = obj(raw);
|
|
2521
|
+
const conf = num(believer?.confidence);
|
|
2522
|
+
return `${str(believer?.agent_label) || "?"}${conf !== void 0 ? ` (${conf})` : ""}`;
|
|
2523
|
+
}).join(", ");
|
|
2524
|
+
lines.push(` - believed by: ${rendered2}`);
|
|
2525
|
+
}
|
|
2526
|
+
return lines.join("\n");
|
|
2527
|
+
}
|
|
2528
|
+
function groupedNodeSections(nodes, state, headingPrefix = "") {
|
|
2529
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2530
|
+
for (const node of nodes) {
|
|
2531
|
+
const type = nodeDisplayType(node);
|
|
2532
|
+
const group = groups.get(type) ?? [];
|
|
2533
|
+
group.push(node);
|
|
2534
|
+
groups.set(type, group);
|
|
2535
|
+
}
|
|
2536
|
+
const sections = [];
|
|
2537
|
+
let first = true;
|
|
2538
|
+
for (const [type, group] of groups) {
|
|
2539
|
+
sections.push({
|
|
2540
|
+
heading: `${first ? headingPrefix : ""}### ${type}`,
|
|
2541
|
+
items: group.map((node) => nodeLines(node, state))
|
|
2542
|
+
});
|
|
2543
|
+
first = false;
|
|
2544
|
+
}
|
|
2545
|
+
return sections;
|
|
2546
|
+
}
|
|
2547
|
+
function relationshipItems(edges, labelByUid, state, options) {
|
|
2548
|
+
const lines = [];
|
|
2549
|
+
for (const raw of edges) {
|
|
2550
|
+
const edge = obj(raw);
|
|
2551
|
+
if (!edge) continue;
|
|
2552
|
+
const type = str(edge.edge_type) || "?";
|
|
2553
|
+
const from = str(edge.from_uid);
|
|
2554
|
+
const to = str(edge.to_uid);
|
|
2555
|
+
if (CURATION_EDGE_TYPES.has(type)) continue;
|
|
2556
|
+
if (!from || !to || from === to) continue;
|
|
2557
|
+
const fromLabel = labelByUid.get(from) ?? (options?.fallbackToUid ? `[${from}]` : void 0);
|
|
2558
|
+
const toLabel = labelByUid.get(to) ?? (options?.fallbackToUid ? `[${to}]` : void 0);
|
|
2559
|
+
if (!fromLabel || !toLabel) continue;
|
|
2560
|
+
lines.push(`- ${fromLabel} \u2014${type}\u2192 ${toLabel}`);
|
|
2561
|
+
}
|
|
2562
|
+
if (lines.length > RELATIONSHIP_MAX) {
|
|
2563
|
+
state.bounded = true;
|
|
2564
|
+
const extra = lines.length - RELATIONSHIP_MAX;
|
|
2565
|
+
return [...lines.slice(0, RELATIONSHIP_MAX), `- (+${extra} more relationships)`];
|
|
2566
|
+
}
|
|
2567
|
+
return lines;
|
|
2568
|
+
}
|
|
2569
|
+
function assemble(header, sections, state) {
|
|
2570
|
+
const lines = [header];
|
|
2571
|
+
let used = header.length;
|
|
2572
|
+
for (const section of sections) {
|
|
2573
|
+
if (section.items.length === 0 && !section.heading) continue;
|
|
2574
|
+
const headingCost = section.heading.length + 2;
|
|
2575
|
+
if (used + headingCost > RENDER_CHAR_BUDGET) {
|
|
2576
|
+
state.bounded = true;
|
|
2577
|
+
continue;
|
|
2578
|
+
}
|
|
2579
|
+
let sectionUsed = 0;
|
|
2580
|
+
const kept = [];
|
|
2581
|
+
let dropped = 0;
|
|
2582
|
+
for (const item of section.items) {
|
|
2583
|
+
if (used + headingCost + sectionUsed + item.length + 1 > RENDER_CHAR_BUDGET) {
|
|
2584
|
+
dropped += 1;
|
|
2585
|
+
continue;
|
|
2586
|
+
}
|
|
2587
|
+
kept.push(item);
|
|
2588
|
+
sectionUsed += item.length + 1;
|
|
2589
|
+
}
|
|
2590
|
+
if (kept.length === 0 && section.items.length > 0) {
|
|
2591
|
+
state.bounded = true;
|
|
2592
|
+
continue;
|
|
2593
|
+
}
|
|
2594
|
+
if (dropped > 0) {
|
|
2595
|
+
state.bounded = true;
|
|
2596
|
+
const marker = `(+${dropped} more not shown)`;
|
|
2597
|
+
kept.push(marker);
|
|
2598
|
+
sectionUsed += marker.length + 1;
|
|
2599
|
+
}
|
|
2600
|
+
lines.push(
|
|
2601
|
+
section.items.length === 0 ? section.heading : section.heading ? `${section.heading}
|
|
2602
|
+
${kept.join("\n")}` : kept.join("\n")
|
|
2603
|
+
);
|
|
2604
|
+
used += headingCost + sectionUsed;
|
|
2605
|
+
}
|
|
2606
|
+
if (state.bounded) {
|
|
2607
|
+
lines.push(
|
|
2608
|
+
'\u2026[rendered context bounded \u2014 fetch full node content by uid, or pass format: "json" for the raw response]'
|
|
2609
|
+
);
|
|
2610
|
+
}
|
|
2611
|
+
return lines.join("\n\n");
|
|
2612
|
+
}
|
|
2613
|
+
function policySections(payload, state) {
|
|
2614
|
+
const policies = Array.isArray(payload.applicable_policies) ? payload.applicable_policies : [];
|
|
2615
|
+
if (policies.length === 0) return [];
|
|
2616
|
+
const items = policies.map((raw) => obj(raw)).filter((policy) => Boolean(policy)).map((policy) => {
|
|
2617
|
+
const rules = Array.isArray(policy.prose_rules) ? policy.prose_rules.map(String).join("; ") : policy.prose_rules;
|
|
2618
|
+
return `- ${str(policy.name) || "(unnamed policy)"}: ${clip(rules, SUMMARY_MAX, state) || "(structured rules apply)"}`;
|
|
2619
|
+
});
|
|
2620
|
+
return [{ heading: "## Applicable policies", items }];
|
|
2621
|
+
}
|
|
2622
|
+
function hiddenSections(payload) {
|
|
2623
|
+
const hidden = payload.hidden_item_count;
|
|
2624
|
+
if (hidden === void 0 || hidden === null) return [];
|
|
2625
|
+
return [
|
|
2626
|
+
{
|
|
2627
|
+
heading: `Note: ${hidden} item(s) were hidden by access scoping.`,
|
|
2628
|
+
items: []
|
|
2629
|
+
}
|
|
2630
|
+
];
|
|
2631
|
+
}
|
|
2632
|
+
function renderContext(response) {
|
|
2633
|
+
const payload = obj(response);
|
|
2634
|
+
if (!payload) return void 0;
|
|
2635
|
+
const graph = obj(payload.graph);
|
|
2636
|
+
const rawNodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
|
|
2637
|
+
const shaped = rawNodes.filter(isNodeShaped);
|
|
2638
|
+
const nodes = shaped.filter((node) => !INFRA_NODE_TYPES.has(str(node.node_type) || ""));
|
|
2639
|
+
const infraOnly = shaped.length - nodes.length;
|
|
2640
|
+
const articles = Array.isArray(payload.articles) ? payload.articles : [];
|
|
2641
|
+
const chunks = Array.isArray(payload.chunks) ? payload.chunks : [];
|
|
2642
|
+
const state = { bounded: false };
|
|
2643
|
+
const sections = [];
|
|
2644
|
+
if (articles.length > 0) {
|
|
2645
|
+
sections.push({
|
|
2646
|
+
heading: "## Knowledge articles",
|
|
2647
|
+
items: articles.map((raw) => obj(raw)).filter((article) => Boolean(article)).map(
|
|
2648
|
+
(article) => `### ${str(article.label) || "(untitled)"} [${str(article.uid) || "?"}]
|
|
2649
|
+
${clip(article.content, ARTICLE_MAX, state) || ""}`
|
|
2650
|
+
)
|
|
2651
|
+
});
|
|
2652
|
+
}
|
|
2653
|
+
if (nodes.length > 0) {
|
|
2654
|
+
sections.push(...groupedNodeSections(nodes, state, "## Knowledge graph\n\n"));
|
|
2655
|
+
const labelByUid = /* @__PURE__ */ new Map();
|
|
2656
|
+
for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
2657
|
+
const relationships = relationshipItems(
|
|
2658
|
+
Array.isArray(graph?.edges) ? graph.edges : [],
|
|
2659
|
+
labelByUid,
|
|
2660
|
+
state
|
|
2661
|
+
);
|
|
2662
|
+
if (relationships.length > 0) {
|
|
2663
|
+
sections.push({ heading: "## Relationships", items: relationships });
|
|
2664
|
+
}
|
|
2665
|
+
} else if (infraOnly > 0) {
|
|
2666
|
+
sections.push({
|
|
2667
|
+
heading: `Note: ${infraOnly} document/chunk node(s) matched \u2014 use action "document_index" or include_chunks for their content.`,
|
|
2668
|
+
items: []
|
|
2669
|
+
});
|
|
2670
|
+
}
|
|
2671
|
+
if (chunks.length > 0) {
|
|
2672
|
+
sections.push({
|
|
2673
|
+
heading: "## Source excerpts",
|
|
2674
|
+
items: chunks.map((raw) => obj(raw)).filter((chunk) => Boolean(chunk)).map(
|
|
2675
|
+
(chunk) => `- [${str(chunk.chunk_uid) || "?"}] from "${str(chunk.document_title) || "unknown document"}": "${clip(chunk.content, CHUNK_MAX, state) || ""}"`
|
|
2676
|
+
)
|
|
2677
|
+
});
|
|
2678
|
+
}
|
|
2679
|
+
sections.push(...policySections(payload, state));
|
|
2680
|
+
sections.push(...hiddenSections(payload));
|
|
2681
|
+
if (sections.length === 0) {
|
|
2682
|
+
return "No results found for this search.";
|
|
2683
|
+
}
|
|
2684
|
+
if (nodes.length === 0 && articles.length === 0 && chunks.length === 0) {
|
|
2685
|
+
return assemble("# Retrieved context\n\nNo graph results matched this search.", sections, state);
|
|
2686
|
+
}
|
|
2687
|
+
return assemble("# Retrieved context", sections, state);
|
|
2688
|
+
}
|
|
2689
|
+
function renderNodeList(response, title, limit) {
|
|
2690
|
+
let candidates = [];
|
|
2691
|
+
if (Array.isArray(response)) {
|
|
2692
|
+
candidates = response;
|
|
2693
|
+
} else {
|
|
2694
|
+
const payload = obj(response);
|
|
2695
|
+
if (!payload) return void 0;
|
|
2696
|
+
for (const key of ["nodes", "results", "items", "goals", "questions", "claims", "preferences", "documents"]) {
|
|
2697
|
+
const candidate = payload[key];
|
|
2698
|
+
if (Array.isArray(candidate) && candidate.some((entry) => unwrapSearchResult(entry))) {
|
|
2699
|
+
candidates = candidate;
|
|
2700
|
+
break;
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
let nodes = candidates.map(unwrapSearchResult).filter((node) => Boolean(node));
|
|
2705
|
+
if (nodes.length === 0) return void 0;
|
|
2706
|
+
const total = nodes.length;
|
|
2707
|
+
const state = { bounded: false };
|
|
2708
|
+
if (limit !== void 0 && limit > 0 && nodes.length > limit) {
|
|
2709
|
+
nodes = nodes.slice(0, limit);
|
|
2710
|
+
state.bounded = true;
|
|
2711
|
+
}
|
|
2712
|
+
const sections = groupedNodeSections(nodes, state);
|
|
2713
|
+
const shown = nodes.length;
|
|
2714
|
+
return assemble(
|
|
2715
|
+
`# ${title} (${shown === total ? total : `${shown} of ${total}`})`,
|
|
2716
|
+
sections,
|
|
2717
|
+
state
|
|
2718
|
+
);
|
|
2719
|
+
}
|
|
2720
|
+
function renderTraversal(response) {
|
|
2721
|
+
const payload = obj(response);
|
|
2722
|
+
if (!payload) return void 0;
|
|
2723
|
+
const container = obj(payload.graph) ?? payload;
|
|
2724
|
+
const rawNodes = Array.isArray(container.nodes) ? container.nodes : [];
|
|
2725
|
+
const nodes = rawNodes.filter(isNodeShaped);
|
|
2726
|
+
const paths = Array.isArray(payload.paths) ? payload.paths : [];
|
|
2727
|
+
if (nodes.length === 0 && paths.length === 0) return void 0;
|
|
2728
|
+
const state = { bounded: false };
|
|
2729
|
+
const sections = [];
|
|
2730
|
+
const labelByUid = /* @__PURE__ */ new Map();
|
|
2731
|
+
for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
2732
|
+
if (paths.length > 0) {
|
|
2733
|
+
const items = paths.map((raw) => obj(raw)).filter((path6) => Boolean(path6)).map((path6, index) => {
|
|
2734
|
+
const uids = Array.isArray(path6.node_uids) ? path6.node_uids.map(String) : [];
|
|
2735
|
+
const labels = Array.isArray(path6.labels) ? path6.labels.map(String) : Array.isArray(path6.nodes) ? path6.nodes.map((hop) => {
|
|
2736
|
+
const hopNode = obj(hop);
|
|
2737
|
+
return str(hopNode?.label) || labelByUid.get(str(hopNode?.uid) || "") || str(hop) || "?";
|
|
2738
|
+
}) : [];
|
|
2739
|
+
const route = labels.length > 0 ? labels.join(" \u2192 ") : uids.join(" \u2192 ") || "(path)";
|
|
2740
|
+
const cost = num(path6.cost) ?? num(path6.path_cost);
|
|
2741
|
+
const confidence = num(path6.path_confidence);
|
|
2742
|
+
const meta = [
|
|
2743
|
+
cost !== void 0 ? `cost ${cost}` : void 0,
|
|
2744
|
+
confidence !== void 0 ? `confidence ${confidence}` : void 0
|
|
2745
|
+
].filter(Boolean);
|
|
2746
|
+
const head = `${index + 1}. ${route}${meta.length > 0 ? ` (${meta.join(", ")})` : ""}`;
|
|
2747
|
+
return labels.length > 0 && uids.length > 0 ? `${head}
|
|
2748
|
+
uids: ${uids.join(" \u2192 ")}` : head;
|
|
2749
|
+
});
|
|
2750
|
+
sections.push({ heading: "## Paths", items });
|
|
2751
|
+
}
|
|
2752
|
+
if (nodes.length > 0) {
|
|
2753
|
+
sections.push(...groupedNodeSections(nodes, state, "## Nodes\n\n"));
|
|
2754
|
+
const relationships = relationshipItems(
|
|
2755
|
+
Array.isArray(container.edges) ? container.edges : [],
|
|
2756
|
+
labelByUid,
|
|
2757
|
+
state
|
|
2758
|
+
);
|
|
2759
|
+
if (relationships.length > 0) {
|
|
2760
|
+
sections.push({ heading: "## Relationships", items: relationships });
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
return assemble("# Graph traversal", sections, state);
|
|
2764
|
+
}
|
|
2765
|
+
function neighborLine(value) {
|
|
2766
|
+
const node = obj(value);
|
|
2767
|
+
if (!node) return void 0;
|
|
2768
|
+
const label = str(node.label) || "(unlabeled)";
|
|
2769
|
+
const uid = str(node.uid);
|
|
2770
|
+
const type = str(node.node_type);
|
|
2771
|
+
return `- ${label}${uid ? ` [${uid}]` : ""}${type ? ` (${type})` : ""}`;
|
|
2772
|
+
}
|
|
2773
|
+
function renderOntologyAnswer(response) {
|
|
2774
|
+
const payload = obj(response);
|
|
2775
|
+
if (!payload) return void 0;
|
|
2776
|
+
const objects = Array.isArray(payload.objects) ? payload.objects.filter(isNodeShaped) : isNodeShaped(payload.object) ? [payload.object] : [];
|
|
2777
|
+
const cognitive = obj(payload.cognitive_context);
|
|
2778
|
+
const provenance = Array.isArray(payload.provenance) ? payload.provenance : [];
|
|
2779
|
+
const hasQuerySignature = Array.isArray(payload.objects) || obj(payload.confidence) !== void 0;
|
|
2780
|
+
const cognitiveEmpty = !cognitive || Object.values(cognitive).every((v) => !Array.isArray(v) || v.length === 0);
|
|
2781
|
+
if (objects.length === 0 && cognitiveEmpty && provenance.length === 0) {
|
|
2782
|
+
return hasQuerySignature ? "No matching domain objects." : void 0;
|
|
2783
|
+
}
|
|
2784
|
+
const state = { bounded: false };
|
|
2785
|
+
const sections = [];
|
|
2786
|
+
if (objects.length > 0) {
|
|
2787
|
+
sections.push(...groupedNodeSections(objects, state));
|
|
2788
|
+
}
|
|
2789
|
+
const labelByUid = /* @__PURE__ */ new Map();
|
|
2790
|
+
for (const node of objects) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
2791
|
+
const graph = obj(payload.graph);
|
|
2792
|
+
for (const raw of Array.isArray(graph?.nodes) ? graph.nodes : []) {
|
|
2793
|
+
const node = obj(raw);
|
|
2794
|
+
const uid = str(node?.uid);
|
|
2795
|
+
if (uid && !labelByUid.has(uid)) labelByUid.set(uid, str(node?.label) || "?");
|
|
2796
|
+
}
|
|
2797
|
+
if (cognitive) {
|
|
2798
|
+
for (const rawNodes of Object.values(cognitive)) {
|
|
2799
|
+
if (!Array.isArray(rawNodes)) continue;
|
|
2800
|
+
for (const raw of rawNodes) {
|
|
2801
|
+
const node = obj(raw);
|
|
2802
|
+
const uid = str(node?.uid);
|
|
2803
|
+
if (uid && !labelByUid.has(uid)) labelByUid.set(uid, str(node?.label) || "?");
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
const relations = Array.isArray(payload.relations) ? payload.relations : [];
|
|
2808
|
+
const relationLines = relationshipItems(relations, labelByUid, state, { fallbackToUid: true });
|
|
2809
|
+
if (relationLines.length > 0) {
|
|
2810
|
+
sections.push({ heading: "## Relations", items: relationLines });
|
|
2811
|
+
}
|
|
2812
|
+
if (cognitive) {
|
|
2813
|
+
for (const [category, rawNodes] of Object.entries(cognitive)) {
|
|
2814
|
+
if (!Array.isArray(rawNodes) || rawNodes.length === 0) continue;
|
|
2815
|
+
const items = rawNodes.map(neighborLine).filter((line) => Boolean(line));
|
|
2816
|
+
if (items.length > 0) {
|
|
2817
|
+
sections.push({ heading: `### Cognitive context \u2014 ${category}`, items });
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
if (provenance.length > 0) {
|
|
2822
|
+
const items = provenance.map((raw) => obj(raw)).filter((entry) => Boolean(entry)).map((entry) => {
|
|
2823
|
+
const span = clip(entry.text_span, QUOTE_MAX, state) || "";
|
|
2824
|
+
const by = str(entry.ingested_by_name);
|
|
2825
|
+
const suffix = `${by ? ` (ingested by ${by})` : ""} [source ${str(entry.source_uid) || "?"}]`;
|
|
2826
|
+
return str(entry.span_kind) === "anchor" ? `- "${span}"${suffix}` : `- context: ${span}${suffix}`;
|
|
2827
|
+
});
|
|
2828
|
+
sections.push({ heading: "## Sources", items });
|
|
2829
|
+
}
|
|
2830
|
+
const confidence = num(obj(payload.confidence)?.overall);
|
|
2831
|
+
const notes = [];
|
|
2832
|
+
if (confidence !== void 0) notes.push(`overall confidence ${confidence}`);
|
|
2833
|
+
if (payload.has_more === true) notes.push("more results exist (has_more)");
|
|
2834
|
+
if (payload.seed_cap_hit === true) notes.push("seed cap hit \u2014 narrow the query for a complete set");
|
|
2835
|
+
if (notes.length > 0) sections.push({ heading: `Note: ${notes.join("; ")}.`, items: [] });
|
|
2836
|
+
return assemble(
|
|
2837
|
+
`# Ontology results (${objects.length} object${objects.length === 1 ? "" : "s"})`,
|
|
2838
|
+
sections,
|
|
2839
|
+
state
|
|
2840
|
+
);
|
|
2841
|
+
}
|
|
2842
|
+
function renderObjectList(response, title) {
|
|
2843
|
+
const payload = obj(response);
|
|
2844
|
+
if (!payload) return void 0;
|
|
2845
|
+
const rawItems = Array.isArray(payload.items) ? payload.items : Array.isArray(payload.rows) && payload.rows.some((row) => unwrapSearchResult(row)) ? payload.rows : void 0;
|
|
2846
|
+
const items = (rawItems ?? []).map(unwrapSearchResult).filter((node) => Boolean(node));
|
|
2847
|
+
if (items.length === 0) {
|
|
2848
|
+
return rawItems ? `# ${title} (0 results)` : void 0;
|
|
2849
|
+
}
|
|
2850
|
+
const state = { bounded: false };
|
|
2851
|
+
const sections = groupedNodeSections(items, state);
|
|
2852
|
+
const total = num(payload.total_count);
|
|
2853
|
+
const truncations = Array.isArray(payload.truncation_reasons) ? payload.truncation_reasons.filter((reason) => typeof reason === "string") : [];
|
|
2854
|
+
const notes = [];
|
|
2855
|
+
if (payload.has_more === true) notes.push("more results exist");
|
|
2856
|
+
if (truncations.length > 0) notes.push(`truncated: ${truncations.join(", ")}`);
|
|
2857
|
+
if (notes.length > 0) sections.push({ heading: `Note: ${notes.join("; ")}.`, items: [] });
|
|
2858
|
+
const count = total !== void 0 && total !== items.length ? `${items.length} of ${total}` : `${items.length}`;
|
|
2859
|
+
return assemble(`# ${title} (${count})`, sections, state);
|
|
2860
|
+
}
|
|
2861
|
+
function renderJobs(response) {
|
|
2862
|
+
const jobs = Array.isArray(response) ? response : obj(response)?.jobs;
|
|
2863
|
+
if (!Array.isArray(jobs)) return void 0;
|
|
2864
|
+
if (jobs.length === 0) return "No ingestion jobs.";
|
|
2865
|
+
const state = { bounded: false };
|
|
2866
|
+
const sorted = jobs.map((raw) => obj(raw)).filter((job) => Boolean(job)).sort((a, b) => (num(b.created_at) ?? 0) - (num(a.created_at) ?? 0));
|
|
2867
|
+
const shown = sorted.slice(0, JOBS_RENDER_MAX);
|
|
2868
|
+
if (shown.length < sorted.length) state.bounded = true;
|
|
2869
|
+
const items = shown.map((job) => {
|
|
2870
|
+
const progress = obj(job.progress);
|
|
2871
|
+
const done = num(progress?.processed_chunks);
|
|
2872
|
+
const totalChunks = num(progress?.total_chunks);
|
|
2873
|
+
const bits = [
|
|
2874
|
+
str(job.status) || "?",
|
|
2875
|
+
done !== void 0 && totalChunks !== void 0 ? `${done}/${totalChunks} chunks` : void 0,
|
|
2876
|
+
num(progress?.nodes_created) !== void 0 ? `${num(progress?.nodes_created)} nodes` : void 0,
|
|
2877
|
+
num(job.queue_position) !== void 0 ? `queue #${num(job.queue_position)}` : void 0,
|
|
2878
|
+
str(job.error) ? `error: ${clip(job.error, 160, state)}` : void 0
|
|
2879
|
+
].filter(Boolean);
|
|
2880
|
+
return `- [${str(job.id) || "?"}] ${clip(job.title, 120, state) || "(untitled)"} \u2014 ${bits.join(", ")}`;
|
|
2881
|
+
});
|
|
2882
|
+
return assemble(
|
|
2883
|
+
`# Ingestion jobs (${shown.length}${shown.length < sorted.length ? ` of ${sorted.length}` : ""})`,
|
|
2884
|
+
[{ heading: "", items }],
|
|
2885
|
+
state
|
|
2886
|
+
);
|
|
2887
|
+
}
|
|
2888
|
+
function renderSignals(response) {
|
|
2889
|
+
const payload = obj(response);
|
|
2890
|
+
if (!payload) return void 0;
|
|
2891
|
+
const state = { bounded: false };
|
|
2892
|
+
const sections = [];
|
|
2893
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
2894
|
+
if (!Array.isArray(value) || value.length === 0) continue;
|
|
2895
|
+
const items = value.map((raw) => obj(raw)).filter((item) => Boolean(item)).map((item) => {
|
|
2896
|
+
const fromLabel = str(item.from_label);
|
|
2897
|
+
const toLabel = str(item.to_label);
|
|
2898
|
+
const anyLabelKey = Object.keys(item).find(
|
|
2899
|
+
(k) => k.endsWith("_label") && str(item[k])
|
|
2900
|
+
);
|
|
2901
|
+
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)";
|
|
2902
|
+
const uid = str(item.uid) ?? str(item.target_uid);
|
|
2903
|
+
const numbers = Object.entries(item).filter(([, v]) => typeof v === "number").slice(0, 4).map(([k, v]) => `${k} ${Math.round(v * 100) / 100}`);
|
|
2904
|
+
const lists = Object.entries(item).filter(([, v]) => Array.isArray(v) && v.length > 0).slice(0, 2).map(([k, v]) => `${k}: ${v.length}`);
|
|
2905
|
+
const meta = [...numbers, ...lists].join(", ");
|
|
2906
|
+
return `- ${clip(label, 160, state)}${uid && uid !== label ? ` [${uid}]` : ""}${meta ? ` (${meta})` : ""}`;
|
|
2907
|
+
});
|
|
2908
|
+
if (items.length > 0) {
|
|
2909
|
+
sections.push({ heading: `## ${key.replace(/_/g, " ")}`, items });
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
if (sections.length === 0) return void 0;
|
|
2913
|
+
return assemble("# Synthesis signals", sections, state);
|
|
2914
|
+
}
|
|
2915
|
+
var LABEL_MAX, SUMMARY_MAX, QUOTE_MAX, ARTICLE_MAX, CHUNK_MAX, RELATIONSHIP_MAX, RENDER_CHAR_BUDGET, CURATION_EDGE_TYPES, INFRA_NODE_TYPES, JOBS_RENDER_MAX;
|
|
2916
|
+
var init_render = __esm({
|
|
2917
|
+
"src/render.ts"() {
|
|
2918
|
+
"use strict";
|
|
2919
|
+
LABEL_MAX = 200;
|
|
2920
|
+
SUMMARY_MAX = 400;
|
|
2921
|
+
QUOTE_MAX = 180;
|
|
2922
|
+
ARTICLE_MAX = 6e3;
|
|
2923
|
+
CHUNK_MAX = 6e3;
|
|
2924
|
+
RELATIONSHIP_MAX = 40;
|
|
2925
|
+
RENDER_CHAR_BUDGET = 24e3;
|
|
2926
|
+
CURATION_EDGE_TYPES = /* @__PURE__ */ new Set(["POSSIBLE_DUPLICATE", "PossibleDuplicate"]);
|
|
2927
|
+
INFRA_NODE_TYPES = /* @__PURE__ */ new Set(["Chunk", "Document", "Article"]);
|
|
2928
|
+
JOBS_RENDER_MAX = 20;
|
|
2929
|
+
}
|
|
2930
|
+
});
|
|
2931
|
+
|
|
2415
2932
|
// src/tools.ts
|
|
2416
2933
|
function toolsForProfile(profile = process.env.MINDGRAPH_PROFILE || "general") {
|
|
2417
2934
|
if (profile === "coding") return TOOLS;
|
|
@@ -2421,9 +2938,14 @@ function toolsForProfile(profile = process.env.MINDGRAPH_PROFILE || "general") {
|
|
|
2421
2938
|
}
|
|
2422
2939
|
function ok3(data) {
|
|
2423
2940
|
return {
|
|
2424
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
2941
|
+
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
2425
2942
|
};
|
|
2426
2943
|
}
|
|
2944
|
+
function renderedResult(format, raw, render) {
|
|
2945
|
+
if (format === "json") return ok3(raw);
|
|
2946
|
+
const text = render(raw);
|
|
2947
|
+
return text !== void 0 ? { content: [{ type: "text", text }] } : ok3(raw);
|
|
2948
|
+
}
|
|
2427
2949
|
function err3(message) {
|
|
2428
2950
|
return {
|
|
2429
2951
|
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
@@ -3005,6 +3527,7 @@ async function handlePlan(client2, args) {
|
|
|
3005
3527
|
async function handleRetrieve(client2, args) {
|
|
3006
3528
|
const {
|
|
3007
3529
|
action,
|
|
3530
|
+
format,
|
|
3008
3531
|
query,
|
|
3009
3532
|
start_uid,
|
|
3010
3533
|
end_uid,
|
|
@@ -3025,12 +3548,20 @@ async function handleRetrieve(client2, args) {
|
|
|
3025
3548
|
graph_max_depth,
|
|
3026
3549
|
confidence_min,
|
|
3027
3550
|
salience_min,
|
|
3551
|
+
article_limit,
|
|
3552
|
+
valid_at,
|
|
3553
|
+
created_after,
|
|
3554
|
+
created_before,
|
|
3555
|
+
explain,
|
|
3556
|
+
k,
|
|
3557
|
+
max_cost,
|
|
3028
3558
|
agent_id
|
|
3029
3559
|
} = args;
|
|
3560
|
+
const rendered2 = (raw, render) => renderedResult(format, raw, render);
|
|
3030
3561
|
switch (action) {
|
|
3031
3562
|
case "context":
|
|
3032
3563
|
if (!query) return err3("query is required for context retrieval");
|
|
3033
|
-
return
|
|
3564
|
+
return rendered2(
|
|
3034
3565
|
await client2.retrieveContext({
|
|
3035
3566
|
query,
|
|
3036
3567
|
node_limit: limit,
|
|
@@ -3039,110 +3570,139 @@ async function handleRetrieve(client2, args) {
|
|
|
3039
3570
|
chunk_limit: include_chunks ? limit ?? 5 : 0,
|
|
3040
3571
|
// Scoped by default, never walled: the coding profile drops the
|
|
3041
3572
|
// document/article leg (the "Bob Work" class of noise) unless the
|
|
3042
|
-
// model opts in for a genuinely document-shaped question.
|
|
3043
|
-
|
|
3573
|
+
// model opts in for a genuinely document-shaped question. Any
|
|
3574
|
+
// profile can drop it with include_documents: false (previously a
|
|
3575
|
+
// silent no-op outside coding). An EXPLICIT article_limit is at
|
|
3576
|
+
// least as clear an opt-in as include_documents:true — it always
|
|
3577
|
+
// wins over profile defaults (a coding-profile model reaching for
|
|
3578
|
+
// the advertised knob must not get a silent no-op).
|
|
3579
|
+
...article_limit !== void 0 ? { article_limit } : include_documents === false || process.env.MINDGRAPH_PROFILE === "coding" && !include_documents ? { article_limit: 0 } : {},
|
|
3580
|
+
valid_at,
|
|
3044
3581
|
include_graph,
|
|
3045
3582
|
graph_expansion_limit: graph_expansion_limit ?? 3,
|
|
3046
3583
|
graph_max_depth: graph_max_depth ?? 2
|
|
3047
|
-
})
|
|
3584
|
+
}),
|
|
3585
|
+
renderContext
|
|
3048
3586
|
);
|
|
3049
3587
|
case "text":
|
|
3050
3588
|
if (!query) return err3("query is required for text search");
|
|
3051
|
-
return
|
|
3589
|
+
return rendered2(
|
|
3052
3590
|
await client2.retrieve({
|
|
3053
3591
|
action: "text",
|
|
3054
3592
|
query,
|
|
3055
3593
|
limit,
|
|
3056
3594
|
node_types,
|
|
3057
3595
|
layer,
|
|
3596
|
+
explain,
|
|
3058
3597
|
agent_id
|
|
3059
|
-
})
|
|
3598
|
+
}),
|
|
3599
|
+
(raw) => renderNodeList(raw, `Text search: ${query}`)
|
|
3060
3600
|
);
|
|
3061
3601
|
case "semantic":
|
|
3062
3602
|
if (!query) return err3("query is required for semantic search");
|
|
3063
|
-
return
|
|
3603
|
+
return rendered2(
|
|
3064
3604
|
await client2.retrieve({
|
|
3065
3605
|
action: "semantic",
|
|
3066
3606
|
query,
|
|
3067
3607
|
k: limit,
|
|
3068
3608
|
node_types,
|
|
3069
3609
|
layer,
|
|
3610
|
+
explain,
|
|
3070
3611
|
agent_id
|
|
3071
|
-
})
|
|
3612
|
+
}),
|
|
3613
|
+
(raw) => renderNodeList(raw, `Semantic search: ${query}`)
|
|
3072
3614
|
);
|
|
3073
3615
|
case "hybrid":
|
|
3074
3616
|
if (!query) return err3("query is required for hybrid search");
|
|
3075
|
-
return
|
|
3617
|
+
return rendered2(
|
|
3076
3618
|
await client2.retrieve({
|
|
3077
3619
|
action: "hybrid",
|
|
3078
3620
|
query,
|
|
3079
3621
|
k: limit,
|
|
3080
3622
|
node_types,
|
|
3081
3623
|
layer,
|
|
3624
|
+
explain,
|
|
3082
3625
|
agent_id
|
|
3083
|
-
})
|
|
3626
|
+
}),
|
|
3627
|
+
// With explain, per-leg scoring must survive — raw JSON only.
|
|
3628
|
+
explain ? () => void 0 : (raw) => renderNodeList(raw, `Hybrid search: ${query}`)
|
|
3084
3629
|
);
|
|
3085
|
-
// Structured queries
|
|
3630
|
+
// Structured queries. These go through the generic /retrieve action
|
|
3631
|
+
// dispatch rather than the SDK's no-argument convenience methods — the
|
|
3632
|
+
// conveniences silently dropped `limit`/`offset` (the tool advertised
|
|
3633
|
+
// them; five actions ignored them).
|
|
3086
3634
|
case "active_goals":
|
|
3087
|
-
return ok3(await client2.getGoals());
|
|
3088
3635
|
case "open_questions":
|
|
3089
|
-
return ok3(await client2.getOpenQuestions());
|
|
3090
3636
|
case "weak_claims":
|
|
3091
|
-
return ok3(await client2.getWeakClaims());
|
|
3092
3637
|
case "pending_approvals":
|
|
3093
|
-
return ok3(await client2.getPendingApprovals());
|
|
3094
3638
|
case "unresolved_contradictions":
|
|
3095
|
-
return
|
|
3639
|
+
return rendered2(
|
|
3640
|
+
await client2.retrieve({
|
|
3641
|
+
action,
|
|
3642
|
+
limit,
|
|
3643
|
+
offset,
|
|
3644
|
+
agent_id
|
|
3645
|
+
}),
|
|
3646
|
+
(raw) => renderNodeList(raw, action.replace(/_/g, " "), limit)
|
|
3647
|
+
);
|
|
3096
3648
|
case "stale_derivations":
|
|
3097
|
-
return
|
|
3649
|
+
return rendered2(
|
|
3098
3650
|
await client2.retrieve({
|
|
3099
3651
|
action: "stale_derivations",
|
|
3100
3652
|
limit,
|
|
3101
3653
|
offset
|
|
3102
|
-
})
|
|
3654
|
+
}),
|
|
3655
|
+
(raw) => renderNodeList(raw, "stale derivations", limit)
|
|
3103
3656
|
);
|
|
3104
3657
|
case "preferences":
|
|
3105
|
-
return
|
|
3658
|
+
return rendered2(
|
|
3106
3659
|
await client2.retrieve({
|
|
3107
3660
|
action: "preferences",
|
|
3108
3661
|
...query ? { query, k: limit } : {},
|
|
3109
3662
|
limit,
|
|
3110
3663
|
agent_id
|
|
3111
|
-
})
|
|
3664
|
+
}),
|
|
3665
|
+
(raw) => renderNodeList(raw, "Preferences", limit)
|
|
3112
3666
|
);
|
|
3113
3667
|
case "layer":
|
|
3114
3668
|
if (!layer) return err3("layer is required for layer retrieval");
|
|
3115
|
-
return
|
|
3669
|
+
return rendered2(
|
|
3116
3670
|
await client2.retrieve({
|
|
3117
3671
|
action: "layer",
|
|
3118
3672
|
layer,
|
|
3119
3673
|
limit,
|
|
3674
|
+
node_types,
|
|
3120
3675
|
agent_id
|
|
3121
|
-
})
|
|
3676
|
+
}),
|
|
3677
|
+
(raw) => renderNodeList(raw, `Layer: ${layer}`, limit)
|
|
3122
3678
|
);
|
|
3123
3679
|
case "recent":
|
|
3124
|
-
return
|
|
3680
|
+
return rendered2(
|
|
3125
3681
|
await client2.retrieve({
|
|
3126
3682
|
action: "recent",
|
|
3127
3683
|
limit,
|
|
3128
3684
|
node_types,
|
|
3129
3685
|
confidence_min,
|
|
3130
3686
|
salience_min,
|
|
3687
|
+
created_after,
|
|
3688
|
+
created_before,
|
|
3131
3689
|
agent_id
|
|
3132
|
-
})
|
|
3690
|
+
}),
|
|
3691
|
+
(raw) => renderNodeList(raw, "Recently ingested", limit)
|
|
3133
3692
|
);
|
|
3134
3693
|
// Document inventory
|
|
3135
3694
|
case "document_index":
|
|
3136
|
-
return
|
|
3695
|
+
return rendered2(
|
|
3137
3696
|
await client2.getNodes({
|
|
3138
3697
|
node_type: "Document",
|
|
3139
3698
|
limit: limit ?? 100
|
|
3140
|
-
})
|
|
3699
|
+
}),
|
|
3700
|
+
(raw) => renderNodeList(raw, "Ingested documents")
|
|
3141
3701
|
);
|
|
3142
3702
|
// Graph traversal
|
|
3143
3703
|
case "chain":
|
|
3144
3704
|
if (!start_uid) return err3("start_uid is required for chain traversal");
|
|
3145
|
-
return
|
|
3705
|
+
return rendered2(
|
|
3146
3706
|
await client2.traverse({
|
|
3147
3707
|
action: "chain",
|
|
3148
3708
|
start_uid,
|
|
@@ -3150,11 +3710,12 @@ async function handleRetrieve(client2, args) {
|
|
|
3150
3710
|
exclude_edge_types,
|
|
3151
3711
|
include_provenance,
|
|
3152
3712
|
max_nodes
|
|
3153
|
-
})
|
|
3713
|
+
}),
|
|
3714
|
+
renderTraversal
|
|
3154
3715
|
);
|
|
3155
3716
|
case "neighborhood":
|
|
3156
3717
|
if (!start_uid) return err3("start_uid is required for neighborhood traversal");
|
|
3157
|
-
return
|
|
3718
|
+
return rendered2(
|
|
3158
3719
|
await client2.traverse({
|
|
3159
3720
|
action: "neighborhood",
|
|
3160
3721
|
start_uid,
|
|
@@ -3164,12 +3725,13 @@ async function handleRetrieve(client2, args) {
|
|
|
3164
3725
|
exclude_edge_types,
|
|
3165
3726
|
include_provenance,
|
|
3166
3727
|
max_nodes
|
|
3167
|
-
})
|
|
3728
|
+
}),
|
|
3729
|
+
renderTraversal
|
|
3168
3730
|
);
|
|
3169
3731
|
case "path":
|
|
3170
3732
|
if (!start_uid) return err3("start_uid is required for path traversal");
|
|
3171
3733
|
if (!end_uid) return err3("end_uid is required for path traversal");
|
|
3172
|
-
return
|
|
3734
|
+
return rendered2(
|
|
3173
3735
|
await client2.traverse({
|
|
3174
3736
|
action: "path",
|
|
3175
3737
|
start_uid,
|
|
@@ -3180,11 +3742,12 @@ async function handleRetrieve(client2, args) {
|
|
|
3180
3742
|
exclude_edge_types,
|
|
3181
3743
|
include_provenance,
|
|
3182
3744
|
max_nodes
|
|
3183
|
-
})
|
|
3745
|
+
}),
|
|
3746
|
+
renderTraversal
|
|
3184
3747
|
);
|
|
3185
3748
|
case "subgraph":
|
|
3186
3749
|
if (!start_uid) return err3("start_uid is required for subgraph traversal");
|
|
3187
|
-
return
|
|
3750
|
+
return rendered2(
|
|
3188
3751
|
await client2.traverse({
|
|
3189
3752
|
action: "subgraph",
|
|
3190
3753
|
start_uid,
|
|
@@ -3194,7 +3757,24 @@ async function handleRetrieve(client2, args) {
|
|
|
3194
3757
|
exclude_edge_types,
|
|
3195
3758
|
include_provenance,
|
|
3196
3759
|
max_nodes
|
|
3197
|
-
})
|
|
3760
|
+
}),
|
|
3761
|
+
renderTraversal
|
|
3762
|
+
);
|
|
3763
|
+
case "top_k_paths":
|
|
3764
|
+
if (!start_uid) return err3("start_uid is required for top_k_paths");
|
|
3765
|
+
if (!end_uid) return err3("end_uid is required for top_k_paths");
|
|
3766
|
+
return rendered2(
|
|
3767
|
+
await client2.traverse({
|
|
3768
|
+
action: "top_k_paths",
|
|
3769
|
+
start_uid,
|
|
3770
|
+
end_uid,
|
|
3771
|
+
k,
|
|
3772
|
+
max_hops: max_depth,
|
|
3773
|
+
max_cost,
|
|
3774
|
+
edge_types,
|
|
3775
|
+
exclude_edge_types
|
|
3776
|
+
}),
|
|
3777
|
+
renderTraversal
|
|
3198
3778
|
);
|
|
3199
3779
|
default:
|
|
3200
3780
|
return err3(`Unknown retrieve action: ${action}`);
|
|
@@ -3220,6 +3800,7 @@ async function handleIngest(client2, args) {
|
|
|
3220
3800
|
chunk_size,
|
|
3221
3801
|
chunk_overlap,
|
|
3222
3802
|
job_id,
|
|
3803
|
+
format,
|
|
3223
3804
|
agent_id
|
|
3224
3805
|
} = args;
|
|
3225
3806
|
const src = source_uri ?? source;
|
|
@@ -3289,19 +3870,21 @@ async function handleIngest(client2, args) {
|
|
|
3289
3870
|
if (job_id) {
|
|
3290
3871
|
return ok3(await client2.getJob(job_id));
|
|
3291
3872
|
}
|
|
3292
|
-
return
|
|
3873
|
+
return renderedResult(format, await client2.listJobs(), renderJobs);
|
|
3293
3874
|
default:
|
|
3294
3875
|
return err3(`Unknown ingest action: ${action}`);
|
|
3295
3876
|
}
|
|
3296
3877
|
}
|
|
3297
3878
|
async function handleSynthesize(client2, args) {
|
|
3298
|
-
const { action, project_uid, signals, target_types, job_id } = args;
|
|
3879
|
+
const { action, project_uid, signals, target_types, job_id, format } = args;
|
|
3299
3880
|
switch (action) {
|
|
3300
3881
|
case "signals":
|
|
3301
3882
|
if (!project_uid)
|
|
3302
3883
|
return err3("project_uid is required for action='signals'");
|
|
3303
|
-
return
|
|
3304
|
-
|
|
3884
|
+
return renderedResult(
|
|
3885
|
+
format,
|
|
3886
|
+
await client2.signals(project_uid, { signals, target_types }),
|
|
3887
|
+
renderSignals
|
|
3305
3888
|
);
|
|
3306
3889
|
case "run":
|
|
3307
3890
|
if (!project_uid)
|
|
@@ -3335,6 +3918,7 @@ async function handleOntology(client2, args) {
|
|
|
3335
3918
|
relation_type,
|
|
3336
3919
|
fields,
|
|
3337
3920
|
source_uids,
|
|
3921
|
+
format,
|
|
3338
3922
|
agent_id
|
|
3339
3923
|
} = args;
|
|
3340
3924
|
switch (action) {
|
|
@@ -3350,7 +3934,8 @@ async function handleOntology(client2, args) {
|
|
|
3350
3934
|
"schema_id is required for action='query' (use action='schemas' to find the active schema)"
|
|
3351
3935
|
);
|
|
3352
3936
|
}
|
|
3353
|
-
return
|
|
3937
|
+
return renderedResult(
|
|
3938
|
+
format,
|
|
3354
3939
|
await client2.queryOntology({
|
|
3355
3940
|
query,
|
|
3356
3941
|
schema_id,
|
|
@@ -3359,29 +3944,42 @@ async function handleOntology(client2, args) {
|
|
|
3359
3944
|
include_sources: include_sources ?? true,
|
|
3360
3945
|
depth,
|
|
3361
3946
|
limit
|
|
3362
|
-
})
|
|
3947
|
+
}),
|
|
3948
|
+
renderOntologyAnswer
|
|
3363
3949
|
);
|
|
3364
3950
|
}
|
|
3365
3951
|
case "search":
|
|
3366
3952
|
if (!query) return err3("query is required for action='search'");
|
|
3367
|
-
return
|
|
3953
|
+
return renderedResult(
|
|
3954
|
+
format,
|
|
3368
3955
|
await client2.searchDomainObjects(query, {
|
|
3369
3956
|
schema_id,
|
|
3370
3957
|
object_types,
|
|
3371
3958
|
limit
|
|
3372
|
-
})
|
|
3959
|
+
}),
|
|
3960
|
+
(raw) => renderObjectList(raw, `Domain search: ${query}`)
|
|
3373
3961
|
);
|
|
3374
3962
|
case "objects":
|
|
3375
3963
|
if (!schema_id) return err3("schema_id is required for action='objects'");
|
|
3376
|
-
return
|
|
3377
|
-
|
|
3964
|
+
return renderedResult(
|
|
3965
|
+
format,
|
|
3966
|
+
await client2.listDomainObjects({ schema_id, object_type, limit }),
|
|
3967
|
+
(raw) => renderObjectList(raw, `Domain objects${object_type ? `: ${object_type}` : ""}`)
|
|
3378
3968
|
);
|
|
3379
3969
|
case "object":
|
|
3380
3970
|
if (!uid) return err3("uid is required for action='object'");
|
|
3381
|
-
return
|
|
3971
|
+
return renderedResult(
|
|
3972
|
+
format,
|
|
3973
|
+
await client2.getDomainObject(uid),
|
|
3974
|
+
(raw) => renderNodeList([raw], "Domain object")
|
|
3975
|
+
);
|
|
3382
3976
|
case "object_context":
|
|
3383
3977
|
if (!uid) return err3("uid is required for action='object_context'");
|
|
3384
|
-
return
|
|
3978
|
+
return renderedResult(
|
|
3979
|
+
format,
|
|
3980
|
+
await client2.getDomainObjectContext(uid, depth),
|
|
3981
|
+
renderOntologyAnswer
|
|
3982
|
+
);
|
|
3385
3983
|
case "proposals":
|
|
3386
3984
|
return ok3(
|
|
3387
3985
|
await client2.listOntologyProposals({
|
|
@@ -3440,6 +4038,7 @@ var init_tools = __esm({
|
|
|
3440
4038
|
init_code_tool();
|
|
3441
4039
|
init_codegraph();
|
|
3442
4040
|
init_sync_tool();
|
|
4041
|
+
init_render();
|
|
3443
4042
|
TOOLS = [
|
|
3444
4043
|
{
|
|
3445
4044
|
name: "mindgraph_capture",
|
|
@@ -4013,10 +4612,16 @@ var init_tools = __esm({
|
|
|
4013
4612
|
"neighborhood",
|
|
4014
4613
|
"path",
|
|
4015
4614
|
"subgraph",
|
|
4615
|
+
"top_k_paths",
|
|
4016
4616
|
"document_index"
|
|
4017
4617
|
],
|
|
4018
4618
|
description: "Retrieval or traversal strategy"
|
|
4019
4619
|
},
|
|
4620
|
+
format: {
|
|
4621
|
+
type: "string",
|
|
4622
|
+
enum: ["text", "json"],
|
|
4623
|
+
description: "Output format (default 'text'): a compact rendered context block \u2014 labels, uids, summaries, status tags, relationships. Pass 'json' for the raw server response (full props, source-chunk offsets, retrieval metadata)."
|
|
4624
|
+
},
|
|
4020
4625
|
query: {
|
|
4021
4626
|
type: "string",
|
|
4022
4627
|
description: "Search query \u2014 for 'text'/'context': 1-3 BM25 keywords, drop filler words. For 'semantic'/'hybrid': natural language is OK since it uses vector similarity."
|
|
@@ -4076,7 +4681,35 @@ var init_tools = __esm({
|
|
|
4076
4681
|
},
|
|
4077
4682
|
include_documents: {
|
|
4078
4683
|
type: "boolean",
|
|
4079
|
-
description: "Include ingested documents and wiki articles in context results. The coding profile defaults these OFF so engineering questions aren't answered from unrelated ingested content; pass true when the question is about a document, spec, or article."
|
|
4684
|
+
description: "Include ingested documents and wiki articles in context results. The coding profile defaults these OFF so engineering questions aren't answered from unrelated ingested content; pass true when the question is about a document, spec, or article \u2014 or false in any profile to drop the article leg."
|
|
4685
|
+
},
|
|
4686
|
+
article_limit: {
|
|
4687
|
+
type: "number",
|
|
4688
|
+
description: "Max wiki articles in context results (default 3; 0 disables the article leg). An explicit value overrides profile defaults, including the coding profile's articles-off default."
|
|
4689
|
+
},
|
|
4690
|
+
valid_at: {
|
|
4691
|
+
type: "string",
|
|
4692
|
+
description: "ISO-8601 date (e.g. '2021-06') for context: nodes with validity windows are annotated as of this date instead of today"
|
|
4693
|
+
},
|
|
4694
|
+
created_after: {
|
|
4695
|
+
type: "number",
|
|
4696
|
+
description: "Unix seconds \u2014 'recent' action: only nodes ingested after this time"
|
|
4697
|
+
},
|
|
4698
|
+
created_before: {
|
|
4699
|
+
type: "number",
|
|
4700
|
+
description: "Unix seconds \u2014 'recent' action: only nodes ingested before this time"
|
|
4701
|
+
},
|
|
4702
|
+
explain: {
|
|
4703
|
+
type: "boolean",
|
|
4704
|
+
description: "hybrid action: include per-leg retrieval scoring (legs) in the response"
|
|
4705
|
+
},
|
|
4706
|
+
k: {
|
|
4707
|
+
type: "number",
|
|
4708
|
+
description: "top_k_paths action: number of cheapest paths (default 3, cap 25)"
|
|
4709
|
+
},
|
|
4710
|
+
max_cost: {
|
|
4711
|
+
type: "number",
|
|
4712
|
+
description: "top_k_paths action: optional cost ceiling for returned paths"
|
|
4080
4713
|
},
|
|
4081
4714
|
include_chunks: {
|
|
4082
4715
|
type: "boolean",
|
|
@@ -4213,6 +4846,11 @@ var init_tools = __esm({
|
|
|
4213
4846
|
type: "string",
|
|
4214
4847
|
description: "Job ID to check status (for job_status action)"
|
|
4215
4848
|
},
|
|
4849
|
+
format: {
|
|
4850
|
+
type: "string",
|
|
4851
|
+
enum: ["text", "json"],
|
|
4852
|
+
description: "job_status without job_id: 'text' (default) renders the 20 most recent jobs; 'json' returns the full raw list."
|
|
4853
|
+
},
|
|
4216
4854
|
agent_id: {
|
|
4217
4855
|
type: "string",
|
|
4218
4856
|
description: "Agent identity"
|
|
@@ -4248,6 +4886,11 @@ var init_tools = __esm({
|
|
|
4248
4886
|
type: "string",
|
|
4249
4887
|
description: "Job ID to poll (for action='job_status')"
|
|
4250
4888
|
},
|
|
4889
|
+
format: {
|
|
4890
|
+
type: "string",
|
|
4891
|
+
enum: ["text", "json"],
|
|
4892
|
+
description: "signals action: 'text' (default) renders signal sections; 'json' returns the raw payload."
|
|
4893
|
+
},
|
|
4251
4894
|
agent_id: {
|
|
4252
4895
|
type: "string",
|
|
4253
4896
|
description: "Agent identity"
|
|
@@ -4356,6 +4999,11 @@ var init_tools = __esm({
|
|
|
4356
4999
|
items: { type: "string" },
|
|
4357
5000
|
description: "Chunk/document UIDs to extract from (for action=extract)"
|
|
4358
5001
|
},
|
|
5002
|
+
format: {
|
|
5003
|
+
type: "string",
|
|
5004
|
+
enum: ["text", "json"],
|
|
5005
|
+
description: "Output format for the read actions query/search/objects/object/object_context (default 'text'): a compact rendered block. Pass 'json' for the raw server response."
|
|
5006
|
+
},
|
|
4359
5007
|
agent_id: {
|
|
4360
5008
|
type: "string",
|
|
4361
5009
|
description: "Agent identity"
|
|
@@ -4418,10 +5066,21 @@ async function getGeneratedTools(client2) {
|
|
|
4418
5066
|
);
|
|
4419
5067
|
}
|
|
4420
5068
|
byName.set(d.name, d);
|
|
5069
|
+
const inputSchema = d.maps_to === "structured_query" ? d.input_schema : {
|
|
5070
|
+
...d.input_schema,
|
|
5071
|
+
properties: {
|
|
5072
|
+
...d.input_schema.properties,
|
|
5073
|
+
format: {
|
|
5074
|
+
type: "string",
|
|
5075
|
+
enum: ["text", "json"],
|
|
5076
|
+
description: "Output format (default 'text': compact rendered block). Pass 'json' for the raw server response."
|
|
5077
|
+
}
|
|
5078
|
+
}
|
|
5079
|
+
};
|
|
4421
5080
|
tools.push({
|
|
4422
5081
|
name: d.name,
|
|
4423
5082
|
description: d.description,
|
|
4424
|
-
inputSchema
|
|
5083
|
+
inputSchema
|
|
4425
5084
|
});
|
|
4426
5085
|
}
|
|
4427
5086
|
cache = { tools, byName, fetchedAt: Date.now() };
|
|
@@ -4442,31 +5101,41 @@ async function handleGeneratedTool(client2, desc, args) {
|
|
|
4442
5101
|
if (!query) return err4(`query is required for ${desc.name}`);
|
|
4443
5102
|
const limit = typeof args.limit === "number" ? args.limit : void 0;
|
|
4444
5103
|
const filters = Array.isArray(args.filters) ? args.filters : void 0;
|
|
4445
|
-
return
|
|
5104
|
+
return rendered(
|
|
5105
|
+
args.format,
|
|
4446
5106
|
await ontologyClient.searchDomainObjects(query, {
|
|
4447
5107
|
schema_id: desc.schema_id,
|
|
4448
5108
|
object_types: [desc.object_type],
|
|
4449
5109
|
filters,
|
|
4450
5110
|
limit
|
|
4451
|
-
})
|
|
5111
|
+
}),
|
|
5112
|
+
(raw) => renderObjectList(raw, `${desc.object_type} search: ${query}`)
|
|
4452
5113
|
);
|
|
4453
5114
|
}
|
|
4454
5115
|
case "object": {
|
|
4455
5116
|
const uid = args.uid;
|
|
4456
5117
|
if (!uid) return err4(`uid is required for ${desc.name}`);
|
|
4457
|
-
return
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
5118
|
+
return rendered(
|
|
5119
|
+
args.format,
|
|
5120
|
+
await ontologyClient.getDomainObject(uid, {
|
|
5121
|
+
schema_id: desc.schema_id,
|
|
5122
|
+
object_type: desc.object_type
|
|
5123
|
+
}),
|
|
5124
|
+
(raw) => renderNodeList([raw], desc.object_type)
|
|
5125
|
+
);
|
|
4461
5126
|
}
|
|
4462
5127
|
case "object_context": {
|
|
4463
5128
|
const uid = args.uid;
|
|
4464
5129
|
if (!uid) return err4(`uid is required for ${desc.name}`);
|
|
4465
5130
|
const depth = typeof args.depth === "number" ? args.depth : void 0;
|
|
4466
|
-
return
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
5131
|
+
return rendered(
|
|
5132
|
+
args.format,
|
|
5133
|
+
await ontologyClient.getDomainObjectContext(uid, depth, {
|
|
5134
|
+
schema_id: desc.schema_id,
|
|
5135
|
+
object_type: desc.object_type
|
|
5136
|
+
}),
|
|
5137
|
+
renderOntologyAnswer
|
|
5138
|
+
);
|
|
4470
5139
|
}
|
|
4471
5140
|
case "related": {
|
|
4472
5141
|
const uid = args.uid;
|
|
@@ -4479,18 +5148,22 @@ async function handleGeneratedTool(client2, desc, args) {
|
|
|
4479
5148
|
"related ontology tools require a graph-aware version of the mindgraph SDK"
|
|
4480
5149
|
);
|
|
4481
5150
|
}
|
|
4482
|
-
return
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
5151
|
+
return rendered(
|
|
5152
|
+
args.format,
|
|
5153
|
+
await ontologyClient.queryRelatedDomainObjects({
|
|
5154
|
+
schema_id: desc.schema_id,
|
|
5155
|
+
entry_type: desc.object_type,
|
|
5156
|
+
uid,
|
|
5157
|
+
relation: desc.relation,
|
|
5158
|
+
entry_role: desc.entry_role,
|
|
5159
|
+
far_type: desc.far_type,
|
|
5160
|
+
where: Array.isArray(args.where) ? args.where : void 0,
|
|
5161
|
+
include_provenance: args.include_provenance === true,
|
|
5162
|
+
limit: typeof args.limit === "number" ? args.limit : void 0,
|
|
5163
|
+
offset: typeof args.offset === "number" ? args.offset : void 0
|
|
5164
|
+
}),
|
|
5165
|
+
(raw) => renderObjectList(raw, `${desc.far_type} related via ${desc.relation}`)
|
|
5166
|
+
);
|
|
4494
5167
|
}
|
|
4495
5168
|
case "structured_query": {
|
|
4496
5169
|
const select = args.select;
|
|
@@ -4516,13 +5189,20 @@ async function handleGeneratedTool(client2, desc, args) {
|
|
|
4516
5189
|
return err4(e instanceof Error ? e.message : String(e));
|
|
4517
5190
|
}
|
|
4518
5191
|
}
|
|
4519
|
-
var ok4, err4, CACHE_TTL_MS, FAILURE_COOLDOWN_MS, cache, retryNotBefore, EMPTY, DuplicateGeneratedToolError, STRUCTURED_QUERY_PASSTHROUGH;
|
|
5192
|
+
var ok4, rendered, err4, CACHE_TTL_MS, FAILURE_COOLDOWN_MS, cache, retryNotBefore, EMPTY, DuplicateGeneratedToolError, STRUCTURED_QUERY_PASSTHROUGH;
|
|
4520
5193
|
var init_generated_tools = __esm({
|
|
4521
5194
|
"src/generated-tools.ts"() {
|
|
4522
5195
|
"use strict";
|
|
5196
|
+
init_render();
|
|
4523
5197
|
ok4 = (data) => ({
|
|
4524
|
-
|
|
5198
|
+
// Compact on purpose — pretty-printing doubled the token cost.
|
|
5199
|
+
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
4525
5200
|
});
|
|
5201
|
+
rendered = (format, raw, render) => {
|
|
5202
|
+
if (format === "json") return ok4(raw);
|
|
5203
|
+
const text = render(raw);
|
|
5204
|
+
return text !== void 0 ? { content: [{ type: "text", text }] } : ok4(raw);
|
|
5205
|
+
};
|
|
4526
5206
|
err4 = (message) => ({
|
|
4527
5207
|
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
4528
5208
|
isError: true
|
|
@@ -4793,7 +5473,7 @@ var init_governance = __esm({
|
|
|
4793
5473
|
// src/resources.ts
|
|
4794
5474
|
function json(uri, data) {
|
|
4795
5475
|
return {
|
|
4796
|
-
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(data
|
|
5476
|
+
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(data) }]
|
|
4797
5477
|
};
|
|
4798
5478
|
}
|
|
4799
5479
|
async function readResource(client2, uri) {
|
|
@@ -4907,7 +5587,7 @@ async function main() {
|
|
|
4907
5587
|
const transport = new import_stdio.StdioServerTransport();
|
|
4908
5588
|
await server.connect(transport);
|
|
4909
5589
|
console.error(
|
|
4910
|
-
`MindGraph MCP server v${"0.
|
|
5590
|
+
`MindGraph MCP server v${"0.17.0"} running on stdio (${BASE_URL})`
|
|
4911
5591
|
);
|
|
4912
5592
|
}
|
|
4913
5593
|
var import_server, import_stdio, import_types, API_KEY, BASE_URL, AGENT_ID, ORG_ID, PROFILE, HARNESS, client, INSTRUCTIONS, server, GOVERNANCE_CONFIG, PROMPTS;
|
|
@@ -5047,7 +5727,7 @@ Capture knowledge when the user shares something worth remembering:
|
|
|
5047
5727
|
server = new import_server.Server(
|
|
5048
5728
|
{
|
|
5049
5729
|
name: "mindgraph",
|
|
5050
|
-
version: "0.
|
|
5730
|
+
version: "0.17.0"
|
|
5051
5731
|
},
|
|
5052
5732
|
{
|
|
5053
5733
|
capabilities: {
|
|
@@ -5612,8 +6292,8 @@ ${knowledgeLines.join("\n")}`);
|
|
|
5612
6292
|
);
|
|
5613
6293
|
}
|
|
5614
6294
|
if (footer) lines.push(footer);
|
|
5615
|
-
const
|
|
5616
|
-
return
|
|
6295
|
+
const rendered2 = lines.join("\n");
|
|
6296
|
+
return rendered2.length <= BRIEF_CHAR_BUDGET ? rendered2 : `${rendered2.slice(0, BRIEF_CHAR_BUDGET - 40)}
|
|
5617
6297
|
\u2026[bounded work brief truncated]`;
|
|
5618
6298
|
}
|
|
5619
6299
|
async function repositoryScopeUids(input, client2, options, agentId) {
|