@hiveai/mcp 0.9.14 → 0.9.16

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **hAIve MCP server** — policy-aware briefing and memory tools for MCP-compatible AI coding agents.
4
4
 
5
- The MCP server is how agents load team policy before changing code. By default it exposes a small enforcement-oriented tool surface: briefing, relevant memories, failed-attempt capture, anchor verification, code-map lookup, and pre-commit checks. The larger maintenance surface is still available with `HAIVE_TOOL_PROFILE=full`.
5
+ The MCP server is how agents load team policy before changing code. By default it exposes a small enforcement-oriented tool surface: briefing, relevant memories, failed-attempt capture, anchor verification, code-map lookup, and pre-commit checks. Larger maintenance and experimental surfaces are opt-in via `HAIVE_TOOL_PROFILE`.
6
6
 
7
7
  hAIve is not just a memory database. The MCP layer participates in enforcement: state-changing hAIve tools require `get_briefing` or `mem_relevant_to` first, so agents cannot silently skip team context while using hAIve.
8
8
 
@@ -117,17 +117,26 @@ Default tools:
117
117
  - `mem_tried`
118
118
  - `mem_search`
119
119
  - `mem_get`
120
- - `mem_update`
121
120
  - `mem_verify`
122
121
  - `code_map`
123
122
  - `pre_commit_check`
123
+ - `mem_session_end`
124
124
 
125
125
  Default prompts:
126
126
 
127
127
  - `bootstrap_project`
128
128
  - `post_task`
129
129
 
130
- Set `HAIVE_TOOL_PROFILE=full` to expose advanced lifecycle, import, timeline, conflict, runtime-journal, and diagnostic tools.
130
+ ### Tool Profiles
131
+
132
+ `HAIVE_TOOL_PROFILE` controls how much hAIve surface an agent sees:
133
+
134
+ - `enforcement` (default): compact repo-native context harness for coding agents.
135
+ - `maintenance`: adds corpus review, lifecycle, distillation, code-search, and project-context maintenance tools.
136
+ - `experimental`: adds exploratory diagnostics such as runtime journal, pattern detection, why-this-file, why-this-decision, and conflict analysis.
137
+ - `full`: legacy alias for `experimental`.
138
+
139
+ Use `maintenance` for human/team stewardship sessions and `experimental` only when you are intentionally working on hAIve's broader research tooling.
131
140
 
132
141
  ### `get_briefing` ⭐ Start every task with this
133
142
 
package/dist/index.js CHANGED
@@ -1394,6 +1394,7 @@ import {
1394
1394
  loadMemoriesFromDir as loadMemoriesFromDir13,
1395
1395
  loadUsageIndex as loadUsageIndex7,
1396
1396
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
1397
+ pathsOverlap,
1397
1398
  queryCodeMap,
1398
1399
  resolveBriefingBudget,
1399
1400
  serializeMemory as serializeMemory9,
@@ -1702,10 +1703,14 @@ ${m.content}`).join("\n\n---\n\n"),
1702
1703
  const createdAt = loaded?.memory.frontmatter.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
1703
1704
  if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
1704
1705
  }
1705
- const outputMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : input.format === "actions" ? trimmedMemories.map((m) => ({
1706
+ const formattedMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : input.format === "actions" ? trimmedMemories.map((m) => ({
1706
1707
  ...m,
1707
1708
  body: extractActionsBriefBody(m.body)
1708
1709
  })) : trimmedMemories;
1710
+ const outputMemories = formattedMemories.map((m) => ({
1711
+ ...m,
1712
+ why: explainWhySurfaced(m, byId.get(m.id), input.files, inferred)
1713
+ }));
1709
1714
  let symbolLocations;
1710
1715
  const symbolsToLookup = new Set(input.symbols);
1711
1716
  for (const m of outputMemories) {
@@ -1871,6 +1876,44 @@ function compactSummary(body) {
1871
1876
  }
1872
1877
  return body.slice(0, 120);
1873
1878
  }
1879
+ function explainWhySurfaced(memory, loaded, inputFiles, inferredModules) {
1880
+ const why = [];
1881
+ const fm = loaded?.memory.frontmatter;
1882
+ if (memory.reasons.includes("anchor") && fm) {
1883
+ const matching = fm.anchor.paths.filter(
1884
+ (p) => inputFiles.length === 0 || inputFiles.some((file) => pathsOverlap(p, file))
1885
+ );
1886
+ if (matching.length > 0) {
1887
+ why.push(`Anchored to touched path${matching.length === 1 ? "" : "s"}: ${matching.slice(0, 4).join(", ")}`);
1888
+ } else if (fm.anchor.paths.length > 0) {
1889
+ why.push(`Pulled by related anchor: ${fm.anchor.paths.slice(0, 4).join(", ")}`);
1890
+ }
1891
+ if (fm.anchor.symbols.length > 0) {
1892
+ why.push(`Anchor symbol${fm.anchor.symbols.length === 1 ? "" : "s"}: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
1893
+ }
1894
+ }
1895
+ if (memory.reasons.includes("module")) {
1896
+ const moduleHints = [
1897
+ ...memory.module ? [memory.module] : [],
1898
+ ...memory.tags.filter((tag) => inferredModules.includes(tag))
1899
+ ];
1900
+ const shown = moduleHints.length > 0 ? [...new Set(moduleHints)].join(", ") : inferredModules.join(", ");
1901
+ why.push(shown ? `Matched inferred module/tag: ${shown}` : "Matched inferred module context.");
1902
+ }
1903
+ if (memory.reasons.includes("domain")) {
1904
+ why.push("Matched inferred domain from the target file paths.");
1905
+ }
1906
+ if (memory.reasons.includes("semantic")) {
1907
+ const score = memory.semantic_score !== void 0 ? ` score=${Math.round(memory.semantic_score * 100) / 100}` : "";
1908
+ why.push(`${memory.match_quality === "exact" ? "Literal task match" : "Semantic/task relevance"}${score}.`);
1909
+ }
1910
+ why.push(`Confidence: ${memory.confidence}; read ${memory.read_count} time${memory.read_count === 1 ? "" : "s"}.`);
1911
+ if (memory.type === "attempt") why.push("Failed-approach record; read before repeating the same path.");
1912
+ if (memory.status === "proposed" || memory.status === "draft") {
1913
+ why.push("Unvalidated record; use cautiously or ask a human before treating it as policy.");
1914
+ }
1915
+ return why;
1916
+ }
1874
1917
  async function trySemanticHits(ctx, task, limit) {
1875
1918
  let mod;
1876
1919
  try {
@@ -2676,7 +2719,7 @@ import {
2676
2719
  getUsage as getUsage9,
2677
2720
  loadMemoriesFromDir as loadMemoriesFromDir20,
2678
2721
  loadUsageIndex as loadUsageIndex11,
2679
- pathsOverlap,
2722
+ pathsOverlap as pathsOverlap2,
2680
2723
  tokenizeQuery as tokenizeQuery5
2681
2724
  } from "@hiveai/core";
2682
2725
  import { z as z27 } from "zod";
@@ -2712,7 +2755,7 @@ async function memConflicts(input, ctx) {
2712
2755
  const otherText = (other.memory.body + " " + fm.tags.join(" ")).toLowerCase();
2713
2756
  const reasons = [];
2714
2757
  const sim = simScores?.get(fm.id) ?? null;
2715
- const hasPathOverlap = fm.anchor.paths.some((p) => targetPaths.some((tp) => pathsOverlap(p, tp)));
2758
+ const hasPathOverlap = fm.anchor.paths.some((p) => targetPaths.some((tp) => pathsOverlap2(p, tp)));
2716
2759
  const otherTokens = new Set(tokenizeQuery5(otherText));
2717
2760
  const tokenOverlap = countIntersection(targetTokens, otherTokens);
2718
2761
  const isSemanticNeighbor = sim !== null && sim >= input.min_score;
@@ -2747,7 +2790,7 @@ async function memConflicts(input, ctx) {
2747
2790
  body_preview: other.memory.body.split("\n").slice(0, 4).join("\n").slice(0, 300),
2748
2791
  similarity: sim,
2749
2792
  reasons,
2750
- shared_paths: fm.anchor.paths.filter((p) => targetPaths.some((tp) => pathsOverlap(p, tp)))
2793
+ shared_paths: fm.anchor.paths.filter((p) => targetPaths.some((tp) => pathsOverlap2(p, tp)))
2751
2794
  });
2752
2795
  }
2753
2796
  conflicts.sort((a, b) => {
@@ -2837,10 +2880,13 @@ async function preCommitCheck(input, ctx) {
2837
2880
  const filesTouching = new Set(relevantMatches.map((m) => m.id));
2838
2881
  const staleHits = verifyResult.results.filter((r) => r.stale && filesTouching.has(r.id));
2839
2882
  const blockOn = input.block_on;
2840
- const blockingWarnings = apResult.warnings.filter(isBlockingWarning);
2883
+ const classifiedWarnings = apResult.warnings.map(classifyWarning);
2884
+ const blockingWarnings = classifiedWarnings.filter((w) => w.level === "blocking");
2885
+ const reviewWarnings = classifiedWarnings.filter((w) => w.level === "review");
2886
+ const infoWarnings = classifiedWarnings.filter((w) => w.level === "info");
2841
2887
  let should_block = false;
2842
2888
  if (blockOn !== "never") {
2843
- if (blockOn === "any" && (apResult.warnings.length > 0 || staleHits.length > 0)) should_block = true;
2889
+ if (blockOn === "any" && (blockingWarnings.length > 0 || reviewWarnings.length > 0 || staleHits.length > 0)) should_block = true;
2844
2890
  if (blockOn === "high-confidence" && (blockingWarnings.length > 0 || staleHits.length > 0)) should_block = true;
2845
2891
  }
2846
2892
  const relevant_memories = relevantMatches.slice(0, 8).map((m) => ({
@@ -2854,10 +2900,12 @@ async function preCommitCheck(input, ctx) {
2854
2900
  summary: {
2855
2901
  anti_patterns: apResult.warnings.length,
2856
2902
  blocking_warnings: blockingWarnings.length,
2903
+ review_warnings: reviewWarnings.length,
2904
+ info_warnings: infoWarnings.length,
2857
2905
  relevant_memories: relevant_memories.length,
2858
2906
  stale_anchors: staleHits.length
2859
2907
  },
2860
- warnings: apResult.warnings,
2908
+ warnings: classifiedWarnings,
2861
2909
  relevant_memories,
2862
2910
  stale_anchors: staleHits.map((r) => ({
2863
2911
  id: r.id,
@@ -2867,6 +2915,30 @@ async function preCommitCheck(input, ctx) {
2867
2915
  }))
2868
2916
  };
2869
2917
  }
2918
+ function classifyWarning(warning) {
2919
+ if (isBlockingWarning(warning)) {
2920
+ return {
2921
+ ...warning,
2922
+ level: "blocking",
2923
+ rationale: "authoritative/trusted memory plus strong semantic match to the diff (score >= 0.65)"
2924
+ };
2925
+ }
2926
+ const hasSemantic = warning.reasons.includes("semantic");
2927
+ const semanticScore = warning.semantic_score ?? 0;
2928
+ const highConfidence = warning.confidence === "authoritative" || warning.confidence === "trusted";
2929
+ if (hasSemantic && semanticScore >= 0.45 || highConfidence && warning.reasons.includes("anchor") && warning.reasons.includes("literal")) {
2930
+ return {
2931
+ ...warning,
2932
+ level: "review",
2933
+ rationale: hasSemantic ? "semantic match is plausible but below blocking threshold" : "anchored high-confidence memory also matched diff tokens, but no strong semantic proof"
2934
+ };
2935
+ }
2936
+ return {
2937
+ ...warning,
2938
+ level: "info",
2939
+ rationale: "weak signal only (literal/anchor/low semantic evidence); surfaced for audit, hidden in concise CLI output"
2940
+ };
2941
+ }
2870
2942
  function isBlockingWarning(warning) {
2871
2943
  const highConfidence = warning.confidence === "authoritative" || warning.confidence === "trusted";
2872
2944
  if (!highConfidence) return false;
@@ -3458,7 +3530,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
3458
3530
  // src/server.ts
3459
3531
  import { loadConfigSync } from "@hiveai/core";
3460
3532
  var SERVER_NAME = "haive";
3461
- var SERVER_VERSION = "0.9.14";
3533
+ var SERVER_VERSION = "0.9.16";
3462
3534
  function jsonResult(data) {
3463
3535
  return {
3464
3536
  content: [
@@ -3469,15 +3541,58 @@ function jsonResult(data) {
3469
3541
  ]
3470
3542
  };
3471
3543
  }
3472
- var ENFORCEMENT_PROFILE_TOOLS = /* @__PURE__ */ new Set([
3544
+ var ENFORCEMENT_PROFILE_TOOLS = [
3473
3545
  "get_briefing",
3474
3546
  "mem_save",
3547
+ "mem_tried",
3475
3548
  "mem_search",
3549
+ "mem_get",
3476
3550
  "mem_verify",
3477
3551
  "mem_relevant_to",
3552
+ "code_map",
3478
3553
  "pre_commit_check",
3479
3554
  "mem_session_end"
3480
- ]);
3555
+ ];
3556
+ var MAINTENANCE_PROFILE_TOOLS = [
3557
+ ...ENFORCEMENT_PROFILE_TOOLS,
3558
+ "mem_suggest_topic",
3559
+ "mem_for_files",
3560
+ "mem_list",
3561
+ "get_project_context",
3562
+ "bootstrap_project_save",
3563
+ "mem_resolve_project",
3564
+ "mem_update",
3565
+ "mem_approve",
3566
+ "mem_reject",
3567
+ "mem_pending",
3568
+ "mem_delete",
3569
+ "mem_diff",
3570
+ "get_recap",
3571
+ "code_search",
3572
+ "anti_patterns_check",
3573
+ "mem_distill",
3574
+ "mem_timeline",
3575
+ "mem_conflict_candidates"
3576
+ ];
3577
+ var EXPERIMENTAL_PROFILE_TOOLS = [
3578
+ ...MAINTENANCE_PROFILE_TOOLS,
3579
+ "mem_observe",
3580
+ "why_this_file",
3581
+ "why_this_decision",
3582
+ "mem_conflicts_with",
3583
+ "pattern_detect",
3584
+ "runtime_journal_append",
3585
+ "runtime_journal_tail"
3586
+ ];
3587
+ var TOOL_PROFILES = {
3588
+ enforcement: new Set(ENFORCEMENT_PROFILE_TOOLS),
3589
+ maintenance: new Set(MAINTENANCE_PROFILE_TOOLS),
3590
+ experimental: new Set(EXPERIMENTAL_PROFILE_TOOLS)
3591
+ };
3592
+ function getAllowedToolsForProfile(profile) {
3593
+ if (profile === "full") return TOOL_PROFILES.experimental;
3594
+ return TOOL_PROFILES[profile] ?? TOOL_PROFILES.enforcement;
3595
+ }
3481
3596
  var BRIEFING_TOOLS = /* @__PURE__ */ new Set(["get_briefing", "mem_relevant_to"]);
3482
3597
  var MUTATING_TOOLS = /* @__PURE__ */ new Set([
3483
3598
  "mem_save",
@@ -3504,7 +3619,8 @@ function createHaiveServer(options = {}) {
3504
3619
  { name: SERVER_NAME, version: SERVER_VERSION },
3505
3620
  { capabilities: { tools: {}, prompts: {} } }
3506
3621
  );
3507
- const shouldRegisterTool = (name) => toolProfile === "full" || ENFORCEMENT_PROFILE_TOOLS.has(name);
3622
+ const allowedTools = getAllowedToolsForProfile(toolProfile);
3623
+ const shouldRegisterTool = (name) => allowedTools.has(name);
3508
3624
  const registerTool = (name, description, schema, handler) => {
3509
3625
  if (!shouldRegisterTool(name)) return;
3510
3626
  const tool = server.tool.bind(server);
@@ -3528,7 +3644,11 @@ function createHaiveServer(options = {}) {
3528
3644
  }
3529
3645
  );
3530
3646
  };
3531
- const shouldRegisterPrompt = (name) => toolProfile === "full" || name === "bootstrap_project" || name === "post_task";
3647
+ const shouldRegisterPrompt = (name) => {
3648
+ if (name === "bootstrap_project" || name === "post_task") return true;
3649
+ if (name === "import_docs") return toolProfile !== "enforcement";
3650
+ return toolProfile === "experimental" || toolProfile === "full";
3651
+ };
3532
3652
  registerTool(
3533
3653
  "mem_save",
3534
3654
  [