@kage-core/kage-graph-mcp 1.1.37 → 1.2.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/dist/cli.js CHANGED
@@ -29,6 +29,8 @@ Usage:
29
29
  kage hook uninstall --project <dir> [--json]
30
30
  kage refresh --project <dir> [--full] [--json]
31
31
  kage gc --project <dir> [--dry-run] [--force] [--json]
32
+ kage compact --project <dir> [--dry-run] [--json]
33
+ kage verify --project <dir> [--id <packet-id>] [--json]
32
34
  kage pr summarize --project <dir> [--json]
33
35
  kage pr check --project <dir> [--json]
34
36
  kage upgrade [--dry-run]
@@ -47,6 +49,7 @@ Usage:
47
49
  kage supersede --project <dir> --packet <old-id> --replacement <new-id> [--reason <text>] [--json]
48
50
  kage contributors --project <dir> [--json]
49
51
  kage profile --project <dir> [--json]
52
+ kage xray --project <dir> [--json]
50
53
  kage capabilities --project <dir> [--json]
51
54
  kage decisions --project <dir> [--json]
52
55
  kage module-health --project <dir> [--json]
@@ -73,14 +76,14 @@ Usage:
73
76
  kage graph "<query>" --project <dir> [--json]
74
77
  kage graph-registry --project <dir> [--json]
75
78
  kage embeddings build --project <dir> [--model Xenova/all-MiniLM-L6-v2] [--json]
76
- kage recall "<query>" --project <dir> [--json] [--explain] [--embeddings]
79
+ kage recall "<query>" --project <dir> [--json] [--explain] [--embeddings] [--max-context-tokens <n>] [--structural-hops <n>]
77
80
  kage observe --project <dir> --event <json>
78
81
  kage sessions --project <dir> [--json]
79
82
  kage replay --project <dir> [--session <id>] [--limit <n>] [--json]
80
83
  kage distill --project <dir> --session <id>
81
- kage learn --project <dir> --learning <text> [--title <title>] [--type <type>] [--evidence <text>] [--verified-by <text>] [--tags a,b] [--paths a,b]
84
+ kage learn --project <dir> --learning <text> [--title <title>] [--type <type>] [--evidence <text>] [--verified-by <text>] [--tags a,b] [--paths a,b] [--graph-nodes a,b] [--allow-missing-paths]
82
85
  kage feedback --project <dir> --packet <packet-id> --kind helpful|wrong|stale
83
- kage capture --project <dir> --title <title> --body <body> [--type <type>] [--summary <summary>] [--tags a,b] [--paths a,b] [--stack a,b]
86
+ kage capture --project <dir> --title <title> --body <body> [--type <type>] [--summary <summary>] [--tags a,b] [--paths a,b] [--stack a,b] [--graph-nodes a,b] [--allow-missing-paths]
84
87
  kage propose --project <dir> --from-diff
85
88
  kage review-artifact --project <dir>
86
89
  kage promote --project <dir> --public <packet-id>
@@ -403,6 +406,65 @@ async function main() {
403
406
  }
404
407
  return;
405
408
  }
409
+ if (command === "compact") {
410
+ const project = projectArg(args);
411
+ const dryRun = args.includes("--dry-run");
412
+ const result = (0, kernel_js_1.compactProject)(project, { dryRun });
413
+ if (args.includes("--json")) {
414
+ console.log(JSON.stringify(result, null, 2));
415
+ return;
416
+ }
417
+ const label = dryRun ? " [dry-run]" : "";
418
+ console.log(`Kage compact${label} — scanned ${result.total_scanned} packets`);
419
+ if (result.pruned_citations.length) {
420
+ console.log(`\nPruned dead citations (${result.pruned_citations.length}):`);
421
+ for (const p of result.pruned_citations)
422
+ console.log(` ✂ ${p.title} — removed ${p.removed_paths.join(", ")}`);
423
+ }
424
+ if (result.deprecated.length) {
425
+ console.log(`\nDeprecated stale (${result.deprecated.length}):`);
426
+ for (const p of result.deprecated)
427
+ console.log(` ✗ ${p.title} — ${p.reason}`);
428
+ }
429
+ if (result.duplicate_clusters.length) {
430
+ console.log(`\nDuplicate clusters to merge (${result.duplicate_clusters.length}) — review with kage_compact / kage supersede:`);
431
+ for (const cluster of result.duplicate_clusters) {
432
+ console.log(` ~${cluster.score} similarity:`);
433
+ for (const member of cluster.packets)
434
+ console.log(` • ${member.title} (${member.id})`);
435
+ }
436
+ }
437
+ if (!result.pruned_citations.length && !result.deprecated.length && !result.duplicate_clusters.length) {
438
+ console.log("Nothing to compact — memory is clean.");
439
+ }
440
+ return;
441
+ }
442
+ if (command === "verify") {
443
+ const project = projectArg(args);
444
+ const idFlag = args.indexOf("--id");
445
+ const id = idFlag >= 0 ? args[idFlag + 1] : undefined;
446
+ const result = (0, kernel_js_1.verifyCitations)(project, { id });
447
+ if (args.includes("--json")) {
448
+ console.log(JSON.stringify(result, null, 2));
449
+ if (!result.ok)
450
+ process.exit(2);
451
+ return;
452
+ }
453
+ if (!result.ok) {
454
+ console.log(`Verification failed:\n${result.errors.map((error) => ` - ${error}`).join("\n")}`);
455
+ process.exit(2);
456
+ }
457
+ console.log(`Kage verify — ${result.checked} packet(s): ${result.valid} valid, ${result.stale} stale, ${result.ungrounded} ungrounded`);
458
+ for (const entry of result.packets.filter((p) => p.stale || !p.grounded)) {
459
+ const flags = [entry.stale ? `stale:${entry.stale_severity}` : "", entry.grounded ? "" : "ungrounded"].filter(Boolean).join(", ");
460
+ console.log(` ⚠ ${entry.title} [${flags}]`);
461
+ if (entry.missing_paths.length)
462
+ console.log(` missing: ${entry.missing_paths.join(", ")}`);
463
+ for (const reason of entry.stale_reasons)
464
+ console.log(` - ${reason}`);
465
+ }
466
+ return;
467
+ }
406
468
  if (command === "refresh") {
407
469
  const result = (0, kernel_js_1.refreshProject)(projectArg(args), { full: args.includes("--full") });
408
470
  if (args.includes("--json")) {
@@ -692,6 +754,27 @@ async function main() {
692
754
  console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
693
755
  return;
694
756
  }
757
+ if (command === "xray" || command === "repo-xray") {
758
+ const result = (0, kernel_js_1.kageRepoXray)(projectArg(args));
759
+ if (args.includes("--json")) {
760
+ console.log(JSON.stringify(result, null, 2));
761
+ return;
762
+ }
763
+ console.log(`Kage Repo X-Ray: ${result.summary}`);
764
+ for (const line of result.first_use_script)
765
+ console.log(`- ${line}`);
766
+ for (const layer of result.layers) {
767
+ console.log(`\n${layer.title}: ${layer.summary}`);
768
+ for (const item of layer.items.slice(0, 5)) {
769
+ console.log(`- ${item.path}: ${item.evidence.slice(0, 2).join("; ")}`);
770
+ }
771
+ }
772
+ if (result.next_actions.length)
773
+ console.log(`\nNext: ${result.next_actions[0]}`);
774
+ if (result.warnings.length)
775
+ console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
776
+ return;
777
+ }
695
778
  if (command === "decisions") {
696
779
  const result = (0, kernel_js_1.kageDecisionIntelligence)(projectArg(args));
697
780
  if (args.includes("--json")) {
@@ -1355,12 +1438,17 @@ async function main() {
1355
1438
  tags: listArg(takeArg(args, "--tags")),
1356
1439
  paths: listArg(takeArg(args, "--paths")),
1357
1440
  stack: listArg(takeArg(args, "--stack")),
1441
+ graphNodes: listArg(takeArg(args, "--graph-nodes")),
1442
+ allowMissingPaths: args.includes("--allow-missing-paths"),
1443
+ strictCitations: true,
1358
1444
  });
1359
1445
  if (!result.ok) {
1360
1446
  console.error(`Learning capture blocked:\n${result.errors.map((error) => ` - ${error}`).join("\n")}`);
1361
1447
  process.exit(2);
1362
1448
  }
1363
1449
  console.log(`Captured session learning: ${result.path}`);
1450
+ if (result.warnings?.length)
1451
+ console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
1364
1452
  console.log("Repo-local memory is written immediately. Promotion to org/global still requires explicit review.");
1365
1453
  return;
1366
1454
  }
@@ -1543,9 +1631,11 @@ async function main() {
1543
1631
  const query = firstPositional(args);
1544
1632
  if (!query)
1545
1633
  usage();
1634
+ const maxContextTokens = args.includes("--max-context-tokens") ? numberArg(args, "--max-context-tokens", 0) : undefined;
1635
+ const structuralHops = args.includes("--structural-hops") ? numberArg(args, "--structural-hops", 2) : undefined;
1546
1636
  const result = args.includes("--embeddings")
1547
1637
  ? await (0, kernel_js_1.recallWithEmbeddings)(projectArg(args), query, 5, args.includes("--explain"))
1548
- : (0, kernel_js_1.recall)(projectArg(args), query, 5, args.includes("--explain"));
1638
+ : (0, kernel_js_1.recall)(projectArg(args), query, 5, args.includes("--explain"), { maxContextTokens, structuralHops });
1549
1639
  if (args.includes("--json"))
1550
1640
  console.log(JSON.stringify(result, null, 2));
1551
1641
  else
@@ -1689,6 +1779,9 @@ async function main() {
1689
1779
  tags: listArg(takeArg(args, "--tags")),
1690
1780
  paths: listArg(takeArg(args, "--paths")),
1691
1781
  stack: listArg(takeArg(args, "--stack")),
1782
+ graphNodes: listArg(takeArg(args, "--graph-nodes")),
1783
+ allowMissingPaths: args.includes("--allow-missing-paths"),
1784
+ strictCitations: true,
1692
1785
  };
1693
1786
  const result = (0, kernel_js_1.capture)(input);
1694
1787
  if (!result.ok) {
@@ -1696,6 +1789,8 @@ async function main() {
1696
1789
  process.exit(2);
1697
1790
  }
1698
1791
  console.log(`Captured repo-local packet: ${result.path}`);
1792
+ if (result.warnings?.length)
1793
+ console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
1699
1794
  console.log("Repo-local memory is written immediately. Promotion to org/global still requires explicit review.");
1700
1795
  return;
1701
1796
  }
package/dist/daemon.js CHANGED
@@ -212,9 +212,22 @@ function daemonContextReport(projectDir, body) {
212
212
  sessionId: typeof body.session_id === "string" ? body.session_id : undefined,
213
213
  limit: 5,
214
214
  });
215
+ const teammateBrief = (0, kernel_js_1.kageTeammateBrief)(projectDir, {
216
+ query,
217
+ targets: explicitTargets,
218
+ changedFiles,
219
+ recallResult,
220
+ riskResult,
221
+ reconciliation,
222
+ });
223
+ const learningLedger = typeof body.session_id === "string" && body.session_id.trim()
224
+ ? (0, kernel_js_1.kageSessionLearningLedger)(projectDir, { sessionId: body.session_id, limit: 20 })
225
+ : null;
215
226
  const validationText = validation.ok ? "Memory healthy." : `Warnings: ${validation.warnings.join("; ")}`;
216
227
  const contextBlock = [
217
228
  recallResult.context_block,
229
+ teammateBrief.context_block,
230
+ learningLedger ? learningLedger.context_block : "",
218
231
  graphResult.context_block ? `\n## Graph Facts\n${graphResult.context_block}` : "",
219
232
  riskResult ? riskContextBlock(riskResult) : "",
220
233
  dependencyResult ? `\n## Dependency Path\n${dependencyResult.summary}${dependencyResult.path.length ? `\nPath: ${dependencyResult.path.join(" -> ")}` : ""}` : "",
@@ -225,6 +238,8 @@ function daemonContextReport(projectDir, body) {
225
238
  context_block: contextBlock,
226
239
  recall: recallResult,
227
240
  graph: graphResult,
241
+ teammate_brief: teammateBrief,
242
+ learning_ledger: learningLedger,
228
243
  risk: riskResult,
229
244
  dependency_path: dependencyResult,
230
245
  validation,
@@ -267,9 +282,11 @@ function daemonDoctor(projectDir) {
267
282
  `POST http://${DEFAULT_HOST}:${restPort}/kage/feedback`,
268
283
  `POST http://${DEFAULT_HOST}:${restPort}/kage/observe`,
269
284
  `POST http://${DEFAULT_HOST}:${restPort}/kage/distill`,
285
+ `GET http://${DEFAULT_HOST}:${restPort}/kage/learning-ledger`,
270
286
  `GET http://${DEFAULT_HOST}:${restPort}/kage/replay`,
271
287
  `GET http://${DEFAULT_HOST}:${restPort}/kage/setup-doctor`,
272
288
  `GET http://${DEFAULT_HOST}:${restPort}/kage/profile`,
289
+ `GET http://${DEFAULT_HOST}:${restPort}/kage/xray`,
273
290
  `GET http://${DEFAULT_HOST}:${restPort}/kage/capabilities`,
274
291
  `GET http://${DEFAULT_HOST}:${restPort}/kage/context-slots`,
275
292
  `POST http://${DEFAULT_HOST}:${restPort}/kage/context-slots`,
@@ -369,6 +386,10 @@ async function startDaemon(projectDir, options = {}) {
369
386
  json(res, 200, (0, kernel_js_1.kageProjectProfile)(projectDir));
370
387
  return;
371
388
  }
389
+ if (req.method === "GET" && url.pathname === "/kage/xray") {
390
+ json(res, 200, (0, kernel_js_1.kageRepoXray)(projectDir));
391
+ return;
392
+ }
372
393
  if (req.method === "GET" && url.pathname === "/kage/capabilities") {
373
394
  json(res, 200, (0, kernel_js_1.kageCapabilityAudit)(projectDir));
374
395
  return;
@@ -380,6 +401,13 @@ async function startDaemon(projectDir, options = {}) {
380
401
  }));
381
402
  return;
382
403
  }
404
+ if (req.method === "GET" && url.pathname === "/kage/learning-ledger") {
405
+ json(res, 200, (0, kernel_js_1.kageSessionLearningLedger)(projectDir, {
406
+ sessionId: url.searchParams.get("session") ?? undefined,
407
+ limit: Number(url.searchParams.get("limit") ?? 50),
408
+ }));
409
+ return;
410
+ }
383
411
  if (req.method === "GET" && url.pathname === "/kage/context-slots") {
384
412
  json(res, 200, (0, kernel_js_1.kageContextSlots)(projectDir));
385
413
  return;
@@ -528,6 +556,7 @@ async function startViewer(projectDir, options = {}) {
528
556
  const benchmarkPath = (0, node_path_1.join)(reportsDir, "benchmark.json");
529
557
  const contributorsPath = (0, node_path_1.join)(reportsDir, "contributors.json");
530
558
  const profilePath = (0, node_path_1.join)(reportsDir, "profile.json");
559
+ const xrayPath = (0, node_path_1.join)(reportsDir, "xray.json");
531
560
  const capabilitiesPath = (0, node_path_1.join)(reportsDir, "capabilities.json");
532
561
  const slotsPath = (0, node_path_1.join)(reportsDir, "context-slots.json");
533
562
  const decisionsPath = (0, node_path_1.join)(reportsDir, "decisions.json");
@@ -555,6 +584,7 @@ async function startViewer(projectDir, options = {}) {
555
584
  (0, node_fs_1.writeFileSync)(benchmarkPath, JSON.stringify(viewerBenchmarkReport(projectDir), null, 2));
556
585
  (0, node_fs_1.writeFileSync)(contributorsPath, JSON.stringify((0, kernel_js_1.kageContributors)(projectDir), null, 2));
557
586
  (0, node_fs_1.writeFileSync)(profilePath, JSON.stringify((0, kernel_js_1.kageProjectProfile)(projectDir), null, 2));
587
+ (0, node_fs_1.writeFileSync)(xrayPath, JSON.stringify((0, kernel_js_1.kageRepoXray)(projectDir), null, 2));
558
588
  (0, node_fs_1.writeFileSync)(capabilitiesPath, JSON.stringify((0, kernel_js_1.kageCapabilityAudit)(projectDir), null, 2));
559
589
  (0, node_fs_1.writeFileSync)(slotsPath, JSON.stringify((0, kernel_js_1.kageContextSlots)(projectDir), null, 2));
560
590
  (0, node_fs_1.writeFileSync)(decisionsPath, JSON.stringify((0, kernel_js_1.kageDecisionIntelligence)(projectDir), null, 2));
@@ -575,7 +605,7 @@ async function startViewer(projectDir, options = {}) {
575
605
  catch {
576
606
  // non-fatal: viewer will show 404 for reports if generation fails
577
607
  }
578
- const url = `http://${host}:${port}/viewer/index.html?graph=${encodeURIComponent(graphPath)}&code=${encodeURIComponent(codePath)}&metrics=${encodeURIComponent(metricsPath)}&inbox=${encodeURIComponent(inboxPath)}&review=${encodeURIComponent(reviewPath)}&pending=${encodeURIComponent(pendingDir)}&quality=${encodeURIComponent(qualityPath)}&benchmark=${encodeURIComponent(benchmarkPath)}&contributors=${encodeURIComponent(contributorsPath)}&profile=${encodeURIComponent(profilePath)}&capabilities=${encodeURIComponent(capabilitiesPath)}&slots=${encodeURIComponent(slotsPath)}&decisions=${encodeURIComponent(decisionsPath)}&risk=${encodeURIComponent(riskPath)}&moduleHealth=${encodeURIComponent(moduleHealthPath)}&graphInsights=${encodeURIComponent(graphInsightsPath)}&workspace=${encodeURIComponent(workspacePath)}&sessions=${encodeURIComponent(sessionsPath)}&replay=${encodeURIComponent(replayPath)}&memoryAccess=${encodeURIComponent(memoryAccessPath)}&memoryAudit=${encodeURIComponent(memoryAuditPath)}&handoff=${encodeURIComponent(handoffPath)}&lifecycle=${encodeURIComponent(lifecyclePath)}&timeline=${encodeURIComponent(timelinePath)}&lineage=${encodeURIComponent(lineagePath)}&setup=${encodeURIComponent(setupPath)}&view=code`;
608
+ const url = `http://${host}:${port}/viewer/index.html?graph=${encodeURIComponent(graphPath)}&code=${encodeURIComponent(codePath)}&metrics=${encodeURIComponent(metricsPath)}&inbox=${encodeURIComponent(inboxPath)}&review=${encodeURIComponent(reviewPath)}&pending=${encodeURIComponent(pendingDir)}&quality=${encodeURIComponent(qualityPath)}&benchmark=${encodeURIComponent(benchmarkPath)}&contributors=${encodeURIComponent(contributorsPath)}&profile=${encodeURIComponent(profilePath)}&xray=${encodeURIComponent(xrayPath)}&capabilities=${encodeURIComponent(capabilitiesPath)}&slots=${encodeURIComponent(slotsPath)}&decisions=${encodeURIComponent(decisionsPath)}&risk=${encodeURIComponent(riskPath)}&moduleHealth=${encodeURIComponent(moduleHealthPath)}&graphInsights=${encodeURIComponent(graphInsightsPath)}&workspace=${encodeURIComponent(workspacePath)}&sessions=${encodeURIComponent(sessionsPath)}&replay=${encodeURIComponent(replayPath)}&memoryAccess=${encodeURIComponent(memoryAccessPath)}&memoryAudit=${encodeURIComponent(memoryAuditPath)}&handoff=${encodeURIComponent(handoffPath)}&lifecycle=${encodeURIComponent(lifecyclePath)}&timeline=${encodeURIComponent(timelinePath)}&lineage=${encodeURIComponent(lineagePath)}&setup=${encodeURIComponent(setupPath)}&view=code`;
579
609
  const server = (0, node_http_1.createServer)((req, res) => {
580
610
  const requestUrl = new URL(req.url ?? "/", `http://${host}:${port}`);
581
611
  let filePath = null;
package/dist/index.js CHANGED
@@ -152,6 +152,8 @@ function listTools() {
152
152
  explain: { type: "boolean" },
153
153
  embeddings: { type: "boolean" },
154
154
  json: { type: "boolean" },
155
+ max_context_tokens: { type: "number" },
156
+ structural_hops: { type: "number", description: "If >0, append a bounded N-hop code-graph blast radius seeded from the recalled memory's files." },
155
157
  },
156
158
  required: ["query", "project_dir"],
157
159
  },
@@ -267,6 +269,17 @@ function listTools() {
267
269
  required: ["project_dir"],
268
270
  },
269
271
  },
272
+ {
273
+ name: "kage_xray",
274
+ description: "Return a first-use Repo X-Ray: code structure layers for entry points, core files, risk, tests, memory overlay, and knowledge gaps.",
275
+ inputSchema: {
276
+ type: "object",
277
+ properties: {
278
+ project_dir: { type: "string" },
279
+ },
280
+ required: ["project_dir"],
281
+ },
282
+ },
270
283
  {
271
284
  name: "kage_capabilities",
272
285
  description: "Return an evidence-backed Kage memory-system capability audit across repo memory, collaboration/session proof, benchmarks, and dashboard/viewer readiness.",
@@ -657,7 +670,7 @@ function listTools() {
657
670
  },
658
671
  {
659
672
  name: "kage_learn",
660
- description: "Capture an actual reusable learning from the current session as repo-local memory. Prefer this over diff proposal when the agent knows what was learned.",
673
+ description: "Capture an actual reusable learning from the current session as repo-local memory. Prefer this over diff proposal when the agent knows what was learned. Capture is rejected if every referenced path is missing from the repo; set allow_missing_paths to record anyway (e.g. a file you are about to create).",
661
674
  inputSchema: {
662
675
  type: "object",
663
676
  properties: {
@@ -670,13 +683,15 @@ function listTools() {
670
683
  tags: { type: "array", items: { type: "string" } },
671
684
  paths: { type: "array", items: { type: "string" } },
672
685
  stack: { type: "array", items: { type: "string" } },
686
+ graph_nodes: { type: "array", items: { type: "string" } },
687
+ allow_missing_paths: { type: "boolean" },
673
688
  },
674
689
  required: ["project_dir", "learning"],
675
690
  },
676
691
  },
677
692
  {
678
693
  name: "kage_capture",
679
- description: "Create a repo-local Kage memory packet immediately. Org/global promotion still requires explicit human review.",
694
+ description: "Create a repo-local Kage memory packet immediately. Org/global promotion still requires explicit human review. Capture is rejected if every referenced path is missing from the repo; set allow_missing_paths to record anyway.",
680
695
  inputSchema: {
681
696
  type: "object",
682
697
  properties: {
@@ -688,10 +703,36 @@ function listTools() {
688
703
  tags: { type: "array", items: { type: "string" } },
689
704
  paths: { type: "array", items: { type: "string" } },
690
705
  stack: { type: "array", items: { type: "string" } },
706
+ graph_nodes: { type: "array", items: { type: "string" }, description: "Code-graph node references (symbol/route/file) this memory is about." },
707
+ allow_missing_paths: { type: "boolean" },
691
708
  },
692
709
  required: ["project_dir", "title", "body"],
693
710
  },
694
711
  },
712
+ {
713
+ name: "kage_verify_citations",
714
+ description: "Verify that a memory packet's cited file paths still exist and that the memory is not stale, before trusting it. Pass an id to check one packet, or omit to audit all approved repo memory. Returns grounding and staleness for each.",
715
+ inputSchema: {
716
+ type: "object",
717
+ properties: {
718
+ project_dir: { type: "string" },
719
+ id: { type: "string" },
720
+ },
721
+ required: ["project_dir"],
722
+ },
723
+ },
724
+ {
725
+ name: "kage_compact",
726
+ description: "Consolidate repo memory: prune dead citations, deprecate hard-stale packets, and surface near-duplicate clusters to merge (via kage_supersede). Defaults to a dry run; pass dry_run=false to apply pruning/deprecation. Duplicate merging stays an agent decision — no hosted LLM is used.",
727
+ inputSchema: {
728
+ type: "object",
729
+ properties: {
730
+ project_dir: { type: "string" },
731
+ dry_run: { type: "boolean" },
732
+ },
733
+ required: ["project_dir"],
734
+ },
735
+ },
695
736
  {
696
737
  name: "kage_observe",
697
738
  description: "Store an automatic local observation event from an agent session. Observations are privacy-scanned, deduplicated, and never published automatically.",
@@ -737,6 +778,19 @@ function listTools() {
737
778
  required: ["project_dir"],
738
779
  },
739
780
  },
781
+ {
782
+ name: "kage_learning_ledger",
783
+ description: "Return an agent-facing ledger that classifies observed session events into save, ignore, needs-evidence, or already-distilled memory decisions.",
784
+ inputSchema: {
785
+ type: "object",
786
+ properties: {
787
+ project_dir: { type: "string" },
788
+ session_id: { type: "string" },
789
+ limit: { type: "number" },
790
+ },
791
+ required: ["project_dir"],
792
+ },
793
+ },
740
794
  {
741
795
  name: "kage_session_replay",
742
796
  description: "Return a privacy-preserving replay digest for observed agent sessions: timeline, touched paths, commands, durable candidates, and distill actions without raw transcript text.",
@@ -1042,8 +1096,21 @@ async function callTool(name, args) {
1042
1096
  sessionId: typeof args?.session_id === "string" ? args.session_id : undefined,
1043
1097
  limit: 5,
1044
1098
  });
1099
+ const teammateBrief = (0, kernel_js_1.kageTeammateBrief)(projectDir, {
1100
+ query,
1101
+ targets: explicitTargets,
1102
+ changedFiles,
1103
+ recallResult,
1104
+ riskResult,
1105
+ reconciliation,
1106
+ });
1107
+ const learningLedger = typeof args?.session_id === "string" && args.session_id.trim()
1108
+ ? (0, kernel_js_1.kageSessionLearningLedger)(projectDir, { sessionId: args.session_id, limit: 20 })
1109
+ : null;
1045
1110
  const sections = [
1046
1111
  recallResult.context_block,
1112
+ teammateBrief.context_block,
1113
+ learningLedger ? learningLedger.context_block : "",
1047
1114
  graphResult.context_block ? `\n## Graph Facts\n${graphResult.context_block}` : "",
1048
1115
  riskResult ? riskContextBlock(riskResult) : "",
1049
1116
  dependencyResult ? `\n## Dependency Path\n${dependencyResult.summary}${dependencyResult.path.length ? `\nPath: ${dependencyResult.path.join(" -> ")}` : ""}` : "",
@@ -1055,9 +1122,11 @@ async function callTool(name, args) {
1055
1122
  };
1056
1123
  }
1057
1124
  if (name === "kage_recall") {
1125
+ const maxContextTokens = typeof args?.max_context_tokens === "number" ? args.max_context_tokens : undefined;
1126
+ const structuralHops = typeof args?.structural_hops === "number" ? args.structural_hops : undefined;
1058
1127
  const result = args?.embeddings
1059
1128
  ? await (0, kernel_js_1.recallWithEmbeddings)(String(args?.project_dir ?? ""), String(args?.query ?? ""), Number(args?.limit ?? 5), Boolean(args?.explain))
1060
- : (0, kernel_js_1.recall)(String(args?.project_dir ?? ""), String(args?.query ?? ""), Number(args?.limit ?? 5), Boolean(args?.explain));
1129
+ : (0, kernel_js_1.recall)(String(args?.project_dir ?? ""), String(args?.query ?? ""), Number(args?.limit ?? 5), Boolean(args?.explain), { maxContextTokens, structuralHops });
1061
1130
  return {
1062
1131
  content: [{ type: "text", text: args?.json || args?.explain ? JSON.stringify(result, null, 2) : result.context_block }],
1063
1132
  };
@@ -1125,6 +1194,12 @@ async function callTool(name, args) {
1125
1194
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1126
1195
  };
1127
1196
  }
1197
+ if (name === "kage_xray") {
1198
+ const result = (0, kernel_js_1.kageRepoXray)(String(args?.project_dir ?? ""));
1199
+ return {
1200
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1201
+ };
1202
+ }
1128
1203
  if (name === "kage_capabilities") {
1129
1204
  const result = (0, kernel_js_1.kageCapabilityAudit)(String(args?.project_dir ?? ""));
1130
1205
  return {
@@ -1360,13 +1435,17 @@ async function callTool(name, args) {
1360
1435
  tags: arrayArg(args?.tags),
1361
1436
  paths: arrayArg(args?.paths),
1362
1437
  stack: arrayArg(args?.stack),
1438
+ graphNodes: arrayArg(args?.graph_nodes),
1439
+ allowMissingPaths: Boolean(args?.allow_missing_paths),
1440
+ strictCitations: true,
1363
1441
  });
1442
+ const learnWarnings = result.warnings?.length ? `\nWarnings:\n${result.warnings.map((warning) => `- ${warning}`).join("\n")}` : "";
1364
1443
  return {
1365
1444
  content: [
1366
1445
  {
1367
1446
  type: "text",
1368
1447
  text: result.ok
1369
- ? `Captured session learning: ${result.path}\nRepo-local memory is written immediately. Org/global promotion still requires explicit review.`
1448
+ ? `Captured session learning: ${result.path}\nRepo-local memory is written immediately. Org/global promotion still requires explicit review.${learnWarnings}`
1370
1449
  : `Learning capture blocked:\n${result.errors.map((error) => `- ${error}`).join("\n")}`,
1371
1450
  },
1372
1451
  ],
@@ -1383,19 +1462,41 @@ async function callTool(name, args) {
1383
1462
  tags: arrayArg(args?.tags),
1384
1463
  paths: arrayArg(args?.paths),
1385
1464
  stack: arrayArg(args?.stack),
1465
+ graphNodes: arrayArg(args?.graph_nodes),
1466
+ allowMissingPaths: Boolean(args?.allow_missing_paths),
1467
+ strictCitations: true,
1386
1468
  });
1469
+ const captureWarnings = result.warnings?.length ? `\nWarnings:\n${result.warnings.map((warning) => `- ${warning}`).join("\n")}` : "";
1387
1470
  return {
1388
1471
  content: [
1389
1472
  {
1390
1473
  type: "text",
1391
1474
  text: result.ok
1392
- ? `Captured repo-local packet: ${result.path}\nOrg/global promotion still requires explicit review.`
1475
+ ? `Captured repo-local packet: ${result.path}\nOrg/global promotion still requires explicit review.${captureWarnings}`
1393
1476
  : `Capture blocked:\n${result.errors.map((error) => `- ${error}`).join("\n")}`,
1394
1477
  },
1395
1478
  ],
1396
1479
  isError: !result.ok,
1397
1480
  };
1398
1481
  }
1482
+ if (name === "kage_verify_citations") {
1483
+ const result = (0, kernel_js_1.verifyCitations)(String(args?.project_dir ?? ""), {
1484
+ id: args?.id ? String(args.id) : undefined,
1485
+ });
1486
+ return {
1487
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1488
+ isError: !result.ok,
1489
+ };
1490
+ }
1491
+ if (name === "kage_compact") {
1492
+ const result = (0, kernel_js_1.compactProject)(String(args?.project_dir ?? ""), {
1493
+ dryRun: args?.dry_run === undefined ? true : Boolean(args.dry_run),
1494
+ });
1495
+ return {
1496
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1497
+ isError: !result.ok,
1498
+ };
1499
+ }
1399
1500
  if (name === "kage_observe") {
1400
1501
  const projectDir = String(args?.project_dir ?? "");
1401
1502
  const event = { ...args };
@@ -1420,6 +1521,16 @@ async function callTool(name, args) {
1420
1521
  isError: false,
1421
1522
  };
1422
1523
  }
1524
+ if (name === "kage_learning_ledger") {
1525
+ const result = (0, kernel_js_1.kageSessionLearningLedger)(String(args?.project_dir ?? ""), {
1526
+ sessionId: args?.session_id ? String(args.session_id) : undefined,
1527
+ limit: typeof args?.limit === "number" ? args.limit : undefined,
1528
+ });
1529
+ return {
1530
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1531
+ isError: false,
1532
+ };
1533
+ }
1423
1534
  if (name === "kage_session_replay") {
1424
1535
  const result = (0, kernel_js_1.kageSessionReplay)(String(args?.project_dir ?? ""), {
1425
1536
  sessionId: args?.session_id ? String(args.session_id) : undefined,