@hiveai/mcp 0.10.5 → 0.10.8
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/index.js +236 -235
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +51 -51
- package/dist/server.js +236 -235
- package/dist/server.js.map +1 -1
- package/package.json +3 -3
package/dist/server.js
CHANGED
|
@@ -1415,9 +1415,8 @@ async function memSessionEnd(input, ctx) {
|
|
|
1415
1415
|
}
|
|
1416
1416
|
|
|
1417
1417
|
// src/tools/get-briefing.ts
|
|
1418
|
-
import { readFile as
|
|
1419
|
-
import { existsSync as
|
|
1420
|
-
import path9 from "path";
|
|
1418
|
+
import { readFile as readFile4, writeFile as writeFile11 } from "fs/promises";
|
|
1419
|
+
import { existsSync as existsSync19 } from "fs";
|
|
1421
1420
|
import {
|
|
1422
1421
|
allocateBudget,
|
|
1423
1422
|
DEFAULT_AUTO_PROMOTE_RULE,
|
|
@@ -1426,11 +1425,9 @@ import {
|
|
|
1426
1425
|
extractActionsBriefBody,
|
|
1427
1426
|
getUsage as getUsage5,
|
|
1428
1427
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
1429
|
-
isGlobPath,
|
|
1430
|
-
isRetiredMemory,
|
|
1431
1428
|
isAutoPromoteEligible,
|
|
1432
1429
|
isDecaying,
|
|
1433
|
-
|
|
1430
|
+
isRetiredMemory,
|
|
1434
1431
|
literalMatchesAllTokens as literalMatchesAllTokens2,
|
|
1435
1432
|
literalMatchesAnyToken as literalMatchesAnyToken2,
|
|
1436
1433
|
loadCodeMap,
|
|
@@ -1438,7 +1435,6 @@ import {
|
|
|
1438
1435
|
loadMemoriesFromDir as loadMemoriesFromDir13,
|
|
1439
1436
|
loadUsageIndex as loadUsageIndex7,
|
|
1440
1437
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
1441
|
-
pathsOverlap,
|
|
1442
1438
|
queryCodeMap,
|
|
1443
1439
|
resolveBriefingBudget,
|
|
1444
1440
|
serializeMemory as serializeMemory9,
|
|
@@ -1450,6 +1446,152 @@ import {
|
|
|
1450
1446
|
writeBriefingMarker
|
|
1451
1447
|
} from "@hiveai/core";
|
|
1452
1448
|
import { z as z17 } from "zod";
|
|
1449
|
+
|
|
1450
|
+
// src/tools/briefing-helpers.ts
|
|
1451
|
+
import { readdir as readdir3, readFile as readFile3 } from "fs/promises";
|
|
1452
|
+
import { existsSync as existsSync18 } from "fs";
|
|
1453
|
+
import path9 from "path";
|
|
1454
|
+
import { isGlobPath, isStackPackSeed, pathsOverlap } from "@hiveai/core";
|
|
1455
|
+
function compactSummary(body) {
|
|
1456
|
+
for (const line of body.split("\n")) {
|
|
1457
|
+
const trimmed = line.replace(/^#+\s*/, "").trim();
|
|
1458
|
+
if (trimmed.length > 0) return trimmed.slice(0, 120);
|
|
1459
|
+
}
|
|
1460
|
+
return body.slice(0, 120);
|
|
1461
|
+
}
|
|
1462
|
+
function classifyMemoryPriority(memory, loaded, inputFiles, inputSymbols) {
|
|
1463
|
+
const fm = loaded?.memory.frontmatter;
|
|
1464
|
+
const directAnchor = Boolean(
|
|
1465
|
+
fm && inputFiles.length > 0 && fm.anchor.paths.some((p) => inputFiles.some((file) => pathsOverlap(p, file)))
|
|
1466
|
+
);
|
|
1467
|
+
const directSymbol = Boolean(
|
|
1468
|
+
fm && inputSymbols.length > 0 && fm.anchor.symbols.some(
|
|
1469
|
+
(sym) => inputSymbols.some((wanted) => wanted.toLowerCase() === sym.toLowerCase())
|
|
1470
|
+
)
|
|
1471
|
+
);
|
|
1472
|
+
const strongSemantic = (memory.semantic_score ?? 0) >= 0.65;
|
|
1473
|
+
const usefulSemantic = (memory.semantic_score ?? 0) >= 0.35;
|
|
1474
|
+
if (fm?.requires_human_approval || directAnchor || directSymbol || memory.type === "attempt" && (memory.match_quality === "exact" || strongSemantic) || memory.type === "skill" && (memory.match_quality === "exact" || strongSemantic)) {
|
|
1475
|
+
return "must_read";
|
|
1476
|
+
}
|
|
1477
|
+
if (isStackPackSeed(fm)) {
|
|
1478
|
+
return "background";
|
|
1479
|
+
}
|
|
1480
|
+
if (memory.type === "skill" || memory.reasons.includes("module") || memory.reasons.includes("domain") || memory.match_quality === "exact" || usefulSemantic) {
|
|
1481
|
+
return "useful";
|
|
1482
|
+
}
|
|
1483
|
+
return "background";
|
|
1484
|
+
}
|
|
1485
|
+
function priorityRank(priority) {
|
|
1486
|
+
return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
|
|
1487
|
+
}
|
|
1488
|
+
function classifyBriefingQuality(memories, context) {
|
|
1489
|
+
const mustRead = memories.filter((m) => m.priority === "must_read").length;
|
|
1490
|
+
const useful = memories.filter((m) => m.priority === "useful").length;
|
|
1491
|
+
const background = memories.filter((m) => m.priority === "background").length;
|
|
1492
|
+
const weakSemantic = memories.filter(
|
|
1493
|
+
(m) => m.reasons.length === 1 && m.reasons.includes("semantic") && (m.semantic_score ?? 0) > 0 && (m.semantic_score ?? 0) < 0.35
|
|
1494
|
+
).length;
|
|
1495
|
+
const reasons = [];
|
|
1496
|
+
if (memories.length === 0) reasons.push("no memories matched the task or files");
|
|
1497
|
+
if (context.isTemplateContext && !context.autoContextGenerated) reasons.push("project context is still a template");
|
|
1498
|
+
if (!context.hasLastSession) reasons.push("no previous session recap");
|
|
1499
|
+
if (mustRead > 0) reasons.push(`${mustRead} must_read memor${mustRead === 1 ? "y" : "ies"} matched directly`);
|
|
1500
|
+
if (useful > 0) reasons.push(`${useful} useful memor${useful === 1 ? "y" : "ies"} matched`);
|
|
1501
|
+
if (background > useful + mustRead && background > 2) reasons.push(`${background} background memories dominate the result`);
|
|
1502
|
+
if (weakSemantic > 0) reasons.push(`${weakSemantic} weak semantic-only match${weakSemantic === 1 ? "" : "es"}`);
|
|
1503
|
+
if (context.searchMode === "literal_fallback") reasons.push("semantic index unavailable or empty; literal fallback used");
|
|
1504
|
+
if (memories.length === 0 || mustRead === 0 && useful === 0) {
|
|
1505
|
+
return { level: "thin", reasons };
|
|
1506
|
+
}
|
|
1507
|
+
if (background > useful + mustRead && background > 2) {
|
|
1508
|
+
return { level: "noisy", reasons };
|
|
1509
|
+
}
|
|
1510
|
+
return { level: "strong", reasons };
|
|
1511
|
+
}
|
|
1512
|
+
function explainWhySurfaced(memory, loaded, inputFiles, inferredModules) {
|
|
1513
|
+
const why = [];
|
|
1514
|
+
const fm = loaded?.memory.frontmatter;
|
|
1515
|
+
if (memory.reasons.includes("anchor") && fm) {
|
|
1516
|
+
const matching = fm.anchor.paths.filter(
|
|
1517
|
+
(p) => inputFiles.length === 0 || inputFiles.some((file) => pathsOverlap(p, file))
|
|
1518
|
+
);
|
|
1519
|
+
if (matching.length > 0) {
|
|
1520
|
+
const exact = matching.filter(
|
|
1521
|
+
(p) => !isGlobPath(p) && inputFiles.some((file) => p === file || pathsOverlap(p, file))
|
|
1522
|
+
);
|
|
1523
|
+
const glob = matching.filter((p) => isGlobPath(p));
|
|
1524
|
+
if (exact.length > 0) {
|
|
1525
|
+
why.push(`Exact/file anchor match: ${exact.slice(0, 4).join(", ")}`);
|
|
1526
|
+
}
|
|
1527
|
+
if (glob.length > 0) {
|
|
1528
|
+
why.push(`Glob anchor match: ${glob.slice(0, 4).join(", ")}`);
|
|
1529
|
+
}
|
|
1530
|
+
if (exact.length === 0 && glob.length === 0) {
|
|
1531
|
+
why.push(`Anchored to touched path${matching.length === 1 ? "" : "s"}: ${matching.slice(0, 4).join(", ")}`);
|
|
1532
|
+
}
|
|
1533
|
+
} else if (fm.anchor.paths.length > 0) {
|
|
1534
|
+
why.push(`Pulled by related anchor: ${fm.anchor.paths.slice(0, 4).join(", ")}`);
|
|
1535
|
+
}
|
|
1536
|
+
if (fm.anchor.symbols.length > 0) {
|
|
1537
|
+
why.push(`Anchor symbol${fm.anchor.symbols.length === 1 ? "" : "s"}: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
if (memory.reasons.includes("symbol") && fm) {
|
|
1541
|
+
why.push(`Explicit symbol match: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
|
|
1542
|
+
}
|
|
1543
|
+
if (memory.reasons.includes("module")) {
|
|
1544
|
+
const moduleHints = [
|
|
1545
|
+
...memory.module ? [memory.module] : [],
|
|
1546
|
+
...memory.tags.filter((tag) => inferredModules.includes(tag))
|
|
1547
|
+
];
|
|
1548
|
+
const shown = moduleHints.length > 0 ? [...new Set(moduleHints)].join(", ") : inferredModules.join(", ");
|
|
1549
|
+
why.push(shown ? `Matched inferred module/tag: ${shown}` : "Matched inferred module context.");
|
|
1550
|
+
}
|
|
1551
|
+
if (memory.reasons.includes("domain")) {
|
|
1552
|
+
why.push("Matched inferred domain from the target file paths.");
|
|
1553
|
+
}
|
|
1554
|
+
if (memory.reasons.includes("semantic")) {
|
|
1555
|
+
const score = memory.semantic_score !== void 0 ? ` score=${Math.round(memory.semantic_score * 100) / 100}` : "";
|
|
1556
|
+
why.push(`${memory.match_quality === "exact" ? "Literal task match" : "Semantic/task relevance"}${score}.`);
|
|
1557
|
+
}
|
|
1558
|
+
why.push(`Confidence: ${memory.confidence}; read ${memory.read_count} time${memory.read_count === 1 ? "" : "s"}.`);
|
|
1559
|
+
if (memory.type === "attempt") why.push("Failed-approach record; read before repeating the same path.");
|
|
1560
|
+
if (memory.type === "skill") why.push("Skill (reusable procedure/playbook) \u2014 follow the steps described when doing this type of task.");
|
|
1561
|
+
if (memory.status === "proposed" || memory.status === "draft") {
|
|
1562
|
+
why.push("Unvalidated record; use cautiously or ask a human before treating it as policy.");
|
|
1563
|
+
}
|
|
1564
|
+
return why;
|
|
1565
|
+
}
|
|
1566
|
+
async function trySemanticHits(ctx, task, limit) {
|
|
1567
|
+
let mod;
|
|
1568
|
+
try {
|
|
1569
|
+
mod = await import("@hiveai/embeddings");
|
|
1570
|
+
} catch {
|
|
1571
|
+
return null;
|
|
1572
|
+
}
|
|
1573
|
+
const result = await mod.semanticSearch(ctx.paths, task, { limit });
|
|
1574
|
+
if (!result) return null;
|
|
1575
|
+
return result.hits.map((h) => ({ id: h.id, score: h.score }));
|
|
1576
|
+
}
|
|
1577
|
+
async function loadModuleContexts2(ctx, modules) {
|
|
1578
|
+
if (modules.length === 0) return [];
|
|
1579
|
+
if (!existsSync18(ctx.paths.modulesContextDir)) return [];
|
|
1580
|
+
const available = new Set(
|
|
1581
|
+
(await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
1582
|
+
);
|
|
1583
|
+
const out = [];
|
|
1584
|
+
for (const m of modules) {
|
|
1585
|
+
if (!available.has(m)) continue;
|
|
1586
|
+
const file = path9.join(ctx.paths.modulesContextDir, m, "context.md");
|
|
1587
|
+
if (existsSync18(file)) {
|
|
1588
|
+
out.push({ name: m, content: await readFile3(file, "utf8") });
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
return out;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
// src/tools/get-briefing.ts
|
|
1453
1595
|
var GetBriefingInputSchema = {
|
|
1454
1596
|
task: z17.string().optional().describe(
|
|
1455
1597
|
"What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
|
|
@@ -1495,7 +1637,7 @@ async function getBriefing(input, ctx) {
|
|
|
1495
1637
|
let usage = { version: 1, updated_at: "", by_id: {} };
|
|
1496
1638
|
let byId = /* @__PURE__ */ new Map();
|
|
1497
1639
|
let lastSession;
|
|
1498
|
-
if (
|
|
1640
|
+
if (existsSync19(ctx.paths.memoriesDir)) {
|
|
1499
1641
|
const allLoaded = await loadMemoriesFromDir13(ctx.paths.memoriesDir);
|
|
1500
1642
|
const recaps = allLoaded.filter(({ memory }) => memory.frontmatter.type === "session_recap").sort(
|
|
1501
1643
|
(a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
|
|
@@ -1580,34 +1722,25 @@ async function getBriefing(input, ctx) {
|
|
|
1580
1722
|
if (input.task) {
|
|
1581
1723
|
const tokens = tokenizeQuery2(input.task);
|
|
1582
1724
|
const andHits = allMemories.filter((m) => literalMatchesAllTokens2(m.memory, tokens));
|
|
1583
|
-
for (const loaded of andHits)
|
|
1584
|
-
addOrUpdate(loaded, "semantic", void 0, "exact");
|
|
1585
|
-
}
|
|
1725
|
+
for (const loaded of andHits) addOrUpdate(loaded, "semantic", void 0, "exact");
|
|
1586
1726
|
if (andHits.length === 0 && tokens.length > 1) {
|
|
1587
1727
|
for (const loaded of allMemories) {
|
|
1588
|
-
if (literalMatchesAnyToken2(loaded.memory, tokens))
|
|
1589
|
-
addOrUpdate(loaded, "semantic", void 0, "partial");
|
|
1590
|
-
}
|
|
1728
|
+
if (literalMatchesAnyToken2(loaded.memory, tokens)) addOrUpdate(loaded, "semantic", void 0, "partial");
|
|
1591
1729
|
}
|
|
1592
1730
|
}
|
|
1593
1731
|
if (semanticHits) {
|
|
1594
1732
|
for (const hit of semanticHits) {
|
|
1595
|
-
if (hit.score < input.min_semantic_score)
|
|
1596
|
-
const existing = seen.get(hit.id);
|
|
1597
|
-
if (!existing) continue;
|
|
1598
|
-
}
|
|
1733
|
+
if (hit.score < input.min_semantic_score && !seen.has(hit.id)) continue;
|
|
1599
1734
|
const loaded = byId.get(hit.id);
|
|
1600
1735
|
if (loaded) addOrUpdate(loaded, "semantic", hit.score, "semantic");
|
|
1601
1736
|
}
|
|
1602
1737
|
}
|
|
1603
1738
|
}
|
|
1604
1739
|
const ranked = [...seen.values()].sort((a, b) => {
|
|
1605
|
-
const
|
|
1606
|
-
const reasonScore = (m) => (m.type === "attempt" ? 3 : 0) + // attempt = negative knowledge, surface first to prevent repeating mistakes
|
|
1607
|
-
(m.reasons.includes("anchor") ? 4 : 0) + (m.reasons.includes("symbol") ? 4 : 0) + (m.reasons.includes("module") ? 2 : 0) + (m.reasons.includes("semantic") ? 2 : 0) + (m.reasons.includes("domain") ? 1 : 0);
|
|
1740
|
+
const reasonScore = (m) => (m.type === "attempt" ? 3 : 0) + (m.reasons.includes("anchor") ? 4 : 0) + (m.reasons.includes("symbol") ? 4 : 0) + (m.reasons.includes("module") ? 2 : 0) + (m.reasons.includes("semantic") ? 2 : 0) + (m.reasons.includes("domain") ? 1 : 0);
|
|
1608
1741
|
const confidenceScore = (m) => m.confidence === "authoritative" ? 4 : m.confidence === "trusted" ? 3 : m.confidence === "low" ? 1 : m.confidence === "stale" ? -2 : 0;
|
|
1609
|
-
const sa =
|
|
1610
|
-
const sb =
|
|
1742
|
+
const sa = priorityRank(classifyMemoryPriority(a, byId.get(a.id), input.files, input.symbols)) * 100 + reasonScore(a) + confidenceScore(a) + (a.semantic_score ?? 0);
|
|
1743
|
+
const sb = priorityRank(classifyMemoryPriority(b, byId.get(b.id), input.files, input.symbols)) * 100 + reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
|
|
1611
1744
|
return sb - sa;
|
|
1612
1745
|
});
|
|
1613
1746
|
for (const mem of ranked.slice(0, briefingMaxMemories)) {
|
|
@@ -1636,11 +1769,7 @@ async function getBriefing(input, ctx) {
|
|
|
1636
1769
|
if (!isAutoPromoteEligible(loaded.memory.frontmatter, u, rule)) continue;
|
|
1637
1770
|
const newFm = { ...loaded.memory.frontmatter, status: "validated" };
|
|
1638
1771
|
try {
|
|
1639
|
-
await writeFile11(
|
|
1640
|
-
loaded.filePath,
|
|
1641
|
-
serializeMemory9({ frontmatter: newFm, body: loaded.memory.body }),
|
|
1642
|
-
"utf8"
|
|
1643
|
-
);
|
|
1772
|
+
await writeFile11(loaded.filePath, serializeMemory9({ frontmatter: newFm, body: loaded.memory.body }), "utf8");
|
|
1644
1773
|
m.status = "validated";
|
|
1645
1774
|
m.confidence = "trusted";
|
|
1646
1775
|
} catch {
|
|
@@ -1648,12 +1777,12 @@ async function getBriefing(input, ctx) {
|
|
|
1648
1777
|
}
|
|
1649
1778
|
}
|
|
1650
1779
|
}
|
|
1651
|
-
const projectContextRaw = input.include_project_context &&
|
|
1780
|
+
const projectContextRaw = input.include_project_context && existsSync19(ctx.paths.projectContext) ? await readFile4(ctx.paths.projectContext, "utf8") : "";
|
|
1652
1781
|
const isTemplateContext = projectContextRaw.includes("TODO \u2014 high-level overview") || projectContextRaw.includes("Generated by `haive init`");
|
|
1653
1782
|
const setupWarnings = [];
|
|
1654
1783
|
let autoContextGenerated = false;
|
|
1655
1784
|
let projectContext = isTemplateContext ? "" : projectContextRaw;
|
|
1656
|
-
if ((isTemplateContext || !
|
|
1785
|
+
if ((isTemplateContext || !existsSync19(ctx.paths.projectContext)) && input.include_project_context) {
|
|
1657
1786
|
const haiveConfig = await loadConfig3(ctx.paths);
|
|
1658
1787
|
if (haiveConfig.autoContext) {
|
|
1659
1788
|
const codeMap = await loadCodeMap(ctx.paths);
|
|
@@ -1689,15 +1818,9 @@ async function getBriefing(input, ctx) {
|
|
|
1689
1818
|
);
|
|
1690
1819
|
}
|
|
1691
1820
|
} else {
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
);
|
|
1696
|
-
} else {
|
|
1697
|
-
setupWarnings.push(
|
|
1698
|
-
"No project-context.md found. Run `haive init` then invoke the bootstrap_project MCP prompt."
|
|
1699
|
-
);
|
|
1700
|
-
}
|
|
1821
|
+
setupWarnings.push(
|
|
1822
|
+
isTemplateContext ? "project-context.md still contains the default template. Invoke the bootstrap_project MCP prompt to auto-fill it from your codebase. Until then, get_briefing returns no project context." : "No project-context.md found. Run `haive init` then invoke the bootstrap_project MCP prompt."
|
|
1823
|
+
);
|
|
1701
1824
|
}
|
|
1702
1825
|
}
|
|
1703
1826
|
const moduleContents = briefingIncludeModules ? await loadModuleContexts2(ctx, inferred) : [];
|
|
@@ -1760,10 +1883,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1760
1883
|
const createdAt = loaded?.memory.frontmatter.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1761
1884
|
if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
|
|
1762
1885
|
}
|
|
1763
|
-
const formattedMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : input.format === "actions" ? trimmedMemories.map((m) => ({
|
|
1764
|
-
...m,
|
|
1765
|
-
body: extractActionsBriefBody(m.body)
|
|
1766
|
-
})) : trimmedMemories;
|
|
1886
|
+
const formattedMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : input.format === "actions" ? trimmedMemories.map((m) => ({ ...m, body: extractActionsBriefBody(m.body) })) : trimmedMemories;
|
|
1767
1887
|
const outputMemories = formattedMemories.map((m) => ({
|
|
1768
1888
|
...m,
|
|
1769
1889
|
priority: classifyMemoryPriority(m, byId.get(m.id), input.files, input.symbols),
|
|
@@ -1778,8 +1898,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1778
1898
|
let symbolLocations;
|
|
1779
1899
|
const symbolsToLookup = new Set(input.symbols);
|
|
1780
1900
|
for (const m of outputMemories) {
|
|
1781
|
-
const
|
|
1782
|
-
for (const sym of loaded?.memory.frontmatter.anchor.symbols ?? []) {
|
|
1901
|
+
for (const sym of byId.get(m.id)?.memory.frontmatter.anchor.symbols ?? []) {
|
|
1783
1902
|
symbolsToLookup.add(sym);
|
|
1784
1903
|
}
|
|
1785
1904
|
}
|
|
@@ -1807,41 +1926,37 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1807
1926
|
}
|
|
1808
1927
|
}
|
|
1809
1928
|
const actionRequired = [];
|
|
1810
|
-
|
|
1811
|
-
const
|
|
1812
|
-
if (!loaded?.memory.frontmatter.requires_human_approval) continue;
|
|
1813
|
-
const bodyLines = loaded.memory.body.split("\n");
|
|
1929
|
+
const extractActionItem = (id, body) => {
|
|
1930
|
+
const bodyLines = body.split("\n");
|
|
1814
1931
|
const quoteBlock = bodyLines.filter((l) => l.startsWith("> ")).map((l) => l.slice(2)).join(" ").replace(/^\*«\s*/, "").replace(/\s*»\*$/, "").trim();
|
|
1815
1932
|
const headingLine = bodyLines.find((l) => l.startsWith("## "));
|
|
1816
|
-
const summary = headingLine?.replace(/^##\s*/, "").trim() ??
|
|
1817
|
-
|
|
1818
|
-
id
|
|
1933
|
+
const summary = headingLine?.replace(/^##\s*/, "").trim() ?? id;
|
|
1934
|
+
return {
|
|
1935
|
+
id,
|
|
1819
1936
|
summary,
|
|
1820
|
-
developer_message: quoteBlock || `Une modification externe potentiellement incompatible a \xE9t\xE9 d\xE9tect\xE9e (${
|
|
1821
|
-
}
|
|
1937
|
+
developer_message: quoteBlock || `Une modification externe potentiellement incompatible a \xE9t\xE9 d\xE9tect\xE9e (${id}). Veux-tu que j'analyse l'impact et que je propose des mises \xE0 jour ?`
|
|
1938
|
+
};
|
|
1939
|
+
};
|
|
1940
|
+
for (const m of outputMemories) {
|
|
1941
|
+
const loaded = byId.get(m.id);
|
|
1942
|
+
if (loaded?.memory.frontmatter.requires_human_approval) {
|
|
1943
|
+
actionRequired.push(extractActionItem(m.id, loaded.memory.body));
|
|
1944
|
+
}
|
|
1822
1945
|
}
|
|
1823
|
-
if (
|
|
1946
|
+
if (existsSync19(ctx.paths.memoriesDir)) {
|
|
1824
1947
|
const allMems = await loadMemoriesFromDir13(ctx.paths.memoriesDir);
|
|
1825
1948
|
for (const { memory } of allMems) {
|
|
1826
1949
|
const fm = memory.frontmatter;
|
|
1827
1950
|
if (!fm.requires_human_approval) continue;
|
|
1828
1951
|
if (fm.status === "rejected" || fm.status === "deprecated") continue;
|
|
1829
1952
|
if (actionRequired.some((a) => a.id === fm.id)) continue;
|
|
1830
|
-
|
|
1831
|
-
const quoteBlock = bodyLines.filter((l) => l.startsWith("> ")).map((l) => l.slice(2)).join(" ").replace(/^\*«\s*/, "").replace(/\s*»\*$/, "").trim();
|
|
1832
|
-
const headingLine = bodyLines.find((l) => l.startsWith("## "));
|
|
1833
|
-
const summary = headingLine?.replace(/^##\s*/, "").trim() ?? fm.id;
|
|
1834
|
-
actionRequired.push({
|
|
1835
|
-
id: fm.id,
|
|
1836
|
-
summary,
|
|
1837
|
-
developer_message: quoteBlock || `Une modification externe potentiellement incompatible a \xE9t\xE9 d\xE9tect\xE9e (${fm.id}). Veux-tu que j'analyse l'impact et que je propose des mises \xE0 jour ?`
|
|
1838
|
-
});
|
|
1953
|
+
actionRequired.push(extractActionItem(fm.id, memory.body));
|
|
1839
1954
|
}
|
|
1840
1955
|
}
|
|
1841
1956
|
const pendingDistillFile = pendingDistillPath(ctx);
|
|
1842
|
-
if (
|
|
1957
|
+
if (existsSync19(pendingDistillFile)) {
|
|
1843
1958
|
try {
|
|
1844
|
-
const raw = await
|
|
1959
|
+
const raw = await readFile4(pendingDistillFile, "utf8");
|
|
1845
1960
|
const pd = JSON.parse(raw);
|
|
1846
1961
|
const ageMs = Date.now() - new Date(pd.session_end).getTime();
|
|
1847
1962
|
const SEVEN_DAYS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -1868,7 +1983,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
1868
1983
|
}
|
|
1869
1984
|
}
|
|
1870
1985
|
const memoriesEmpty = outputMemories.length === 0;
|
|
1871
|
-
const hasMemoriesDir =
|
|
1986
|
+
const hasMemoriesDir = existsSync19(ctx.paths.memoriesDir);
|
|
1872
1987
|
const isColdStart = isTemplateContext && memoriesEmpty && !lastSession && !autoContextGenerated;
|
|
1873
1988
|
const hasUnguessableSignal = outputMemories.some(
|
|
1874
1989
|
(m) => (m.priority === "must_read" || m.priority === "useful") && specificityScore(m.body) >= GUESSABLE_THRESHOLD
|
|
@@ -1915,7 +2030,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
1915
2030
|
"No team-specific policy matched these files/task \u2014 nothing here a capable model can't infer. The auto-generated project context was trimmed to keep this briefing near-zero-cost; proceed with normal Read/Grep."
|
|
1916
2031
|
);
|
|
1917
2032
|
}
|
|
1918
|
-
if (
|
|
2033
|
+
if (existsSync19(ctx.paths.haiveDir)) {
|
|
1919
2034
|
await writeBriefingMarker(ctx.paths, {
|
|
1920
2035
|
sessionId: process.env.HAIVE_SESSION_ID,
|
|
1921
2036
|
...input.task ? { task: input.task } : {},
|
|
@@ -1963,144 +2078,6 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
1963
2078
|
}
|
|
1964
2079
|
};
|
|
1965
2080
|
}
|
|
1966
|
-
function compactSummary(body) {
|
|
1967
|
-
for (const line of body.split("\n")) {
|
|
1968
|
-
const trimmed = line.replace(/^#+\s*/, "").trim();
|
|
1969
|
-
if (trimmed.length > 0) return trimmed.slice(0, 120);
|
|
1970
|
-
}
|
|
1971
|
-
return body.slice(0, 120);
|
|
1972
|
-
}
|
|
1973
|
-
function classifyMemoryPriority(memory, loaded, inputFiles, inputSymbols) {
|
|
1974
|
-
const fm = loaded?.memory.frontmatter;
|
|
1975
|
-
const directAnchor = Boolean(
|
|
1976
|
-
fm && inputFiles.length > 0 && fm.anchor.paths.some((p) => inputFiles.some((file) => pathsOverlap(p, file)))
|
|
1977
|
-
);
|
|
1978
|
-
const directSymbol = Boolean(
|
|
1979
|
-
fm && inputSymbols.length > 0 && fm.anchor.symbols.some(
|
|
1980
|
-
(sym) => inputSymbols.some((wanted) => wanted.toLowerCase() === sym.toLowerCase())
|
|
1981
|
-
)
|
|
1982
|
-
);
|
|
1983
|
-
const strongSemantic = (memory.semantic_score ?? 0) >= 0.65;
|
|
1984
|
-
const usefulSemantic = (memory.semantic_score ?? 0) >= 0.35;
|
|
1985
|
-
if (fm?.requires_human_approval || directAnchor || directSymbol || memory.type === "attempt" && (memory.match_quality === "exact" || strongSemantic) || memory.type === "skill" && (memory.match_quality === "exact" || strongSemantic)) {
|
|
1986
|
-
return "must_read";
|
|
1987
|
-
}
|
|
1988
|
-
if (isStackPackSeed(fm)) {
|
|
1989
|
-
return "background";
|
|
1990
|
-
}
|
|
1991
|
-
if (memory.type === "skill" || memory.reasons.includes("module") || memory.reasons.includes("domain") || memory.match_quality === "exact" || usefulSemantic) {
|
|
1992
|
-
return "useful";
|
|
1993
|
-
}
|
|
1994
|
-
return "background";
|
|
1995
|
-
}
|
|
1996
|
-
function priorityRank(priority) {
|
|
1997
|
-
return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
|
|
1998
|
-
}
|
|
1999
|
-
function classifyBriefingQuality(memories, context) {
|
|
2000
|
-
const mustRead = memories.filter((m) => m.priority === "must_read").length;
|
|
2001
|
-
const useful = memories.filter((m) => m.priority === "useful").length;
|
|
2002
|
-
const background = memories.filter((m) => m.priority === "background").length;
|
|
2003
|
-
const weakSemantic = memories.filter(
|
|
2004
|
-
(m) => m.reasons.length === 1 && m.reasons.includes("semantic") && (m.semantic_score ?? 0) > 0 && (m.semantic_score ?? 0) < 0.35
|
|
2005
|
-
).length;
|
|
2006
|
-
const reasons = [];
|
|
2007
|
-
if (memories.length === 0) reasons.push("no memories matched the task or files");
|
|
2008
|
-
if (context.isTemplateContext && !context.autoContextGenerated) reasons.push("project context is still a template");
|
|
2009
|
-
if (!context.hasLastSession) reasons.push("no previous session recap");
|
|
2010
|
-
if (mustRead > 0) reasons.push(`${mustRead} must_read memor${mustRead === 1 ? "y" : "ies"} matched directly`);
|
|
2011
|
-
if (useful > 0) reasons.push(`${useful} useful memor${useful === 1 ? "y" : "ies"} matched`);
|
|
2012
|
-
if (background > useful + mustRead && background > 2) reasons.push(`${background} background memories dominate the result`);
|
|
2013
|
-
if (weakSemantic > 0) reasons.push(`${weakSemantic} weak semantic-only match${weakSemantic === 1 ? "" : "es"}`);
|
|
2014
|
-
if (context.searchMode === "literal_fallback") reasons.push("semantic index unavailable or empty; literal fallback used");
|
|
2015
|
-
if (memories.length === 0 || mustRead === 0 && useful === 0) {
|
|
2016
|
-
return { level: "thin", reasons };
|
|
2017
|
-
}
|
|
2018
|
-
if (background > useful + mustRead && background > 2) {
|
|
2019
|
-
return { level: "noisy", reasons };
|
|
2020
|
-
}
|
|
2021
|
-
return { level: "strong", reasons };
|
|
2022
|
-
}
|
|
2023
|
-
function explainWhySurfaced(memory, loaded, inputFiles, inferredModules) {
|
|
2024
|
-
const why = [];
|
|
2025
|
-
const fm = loaded?.memory.frontmatter;
|
|
2026
|
-
if (memory.reasons.includes("anchor") && fm) {
|
|
2027
|
-
const matching = fm.anchor.paths.filter(
|
|
2028
|
-
(p) => inputFiles.length === 0 || inputFiles.some((file) => pathsOverlap(p, file))
|
|
2029
|
-
);
|
|
2030
|
-
if (matching.length > 0) {
|
|
2031
|
-
const exact = matching.filter(
|
|
2032
|
-
(p) => !isGlobPath(p) && inputFiles.some((file) => p === file || pathsOverlap(p, file))
|
|
2033
|
-
);
|
|
2034
|
-
const glob = matching.filter((p) => isGlobPath(p));
|
|
2035
|
-
if (exact.length > 0) {
|
|
2036
|
-
why.push(`Exact/file anchor match: ${exact.slice(0, 4).join(", ")}`);
|
|
2037
|
-
}
|
|
2038
|
-
if (glob.length > 0) {
|
|
2039
|
-
why.push(`Glob anchor match: ${glob.slice(0, 4).join(", ")}`);
|
|
2040
|
-
}
|
|
2041
|
-
if (exact.length === 0 && glob.length === 0) {
|
|
2042
|
-
why.push(`Anchored to touched path${matching.length === 1 ? "" : "s"}: ${matching.slice(0, 4).join(", ")}`);
|
|
2043
|
-
}
|
|
2044
|
-
} else if (fm.anchor.paths.length > 0) {
|
|
2045
|
-
why.push(`Pulled by related anchor: ${fm.anchor.paths.slice(0, 4).join(", ")}`);
|
|
2046
|
-
}
|
|
2047
|
-
if (fm.anchor.symbols.length > 0) {
|
|
2048
|
-
why.push(`Anchor symbol${fm.anchor.symbols.length === 1 ? "" : "s"}: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
if (memory.reasons.includes("symbol") && fm) {
|
|
2052
|
-
why.push(`Explicit symbol match: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
|
|
2053
|
-
}
|
|
2054
|
-
if (memory.reasons.includes("module")) {
|
|
2055
|
-
const moduleHints = [
|
|
2056
|
-
...memory.module ? [memory.module] : [],
|
|
2057
|
-
...memory.tags.filter((tag) => inferredModules.includes(tag))
|
|
2058
|
-
];
|
|
2059
|
-
const shown = moduleHints.length > 0 ? [...new Set(moduleHints)].join(", ") : inferredModules.join(", ");
|
|
2060
|
-
why.push(shown ? `Matched inferred module/tag: ${shown}` : "Matched inferred module context.");
|
|
2061
|
-
}
|
|
2062
|
-
if (memory.reasons.includes("domain")) {
|
|
2063
|
-
why.push("Matched inferred domain from the target file paths.");
|
|
2064
|
-
}
|
|
2065
|
-
if (memory.reasons.includes("semantic")) {
|
|
2066
|
-
const score = memory.semantic_score !== void 0 ? ` score=${Math.round(memory.semantic_score * 100) / 100}` : "";
|
|
2067
|
-
why.push(`${memory.match_quality === "exact" ? "Literal task match" : "Semantic/task relevance"}${score}.`);
|
|
2068
|
-
}
|
|
2069
|
-
why.push(`Confidence: ${memory.confidence}; read ${memory.read_count} time${memory.read_count === 1 ? "" : "s"}.`);
|
|
2070
|
-
if (memory.type === "attempt") why.push("Failed-approach record; read before repeating the same path.");
|
|
2071
|
-
if (memory.type === "skill") why.push("Skill (reusable procedure/playbook) \u2014 follow the steps described when doing this type of task.");
|
|
2072
|
-
if (memory.status === "proposed" || memory.status === "draft") {
|
|
2073
|
-
why.push("Unvalidated record; use cautiously or ask a human before treating it as policy.");
|
|
2074
|
-
}
|
|
2075
|
-
return why;
|
|
2076
|
-
}
|
|
2077
|
-
async function trySemanticHits(ctx, task, limit) {
|
|
2078
|
-
let mod;
|
|
2079
|
-
try {
|
|
2080
|
-
mod = await import("@hiveai/embeddings");
|
|
2081
|
-
} catch {
|
|
2082
|
-
return null;
|
|
2083
|
-
}
|
|
2084
|
-
const result = await mod.semanticSearch(ctx.paths, task, { limit });
|
|
2085
|
-
if (!result) return null;
|
|
2086
|
-
return result.hits.map((h) => ({ id: h.id, score: h.score }));
|
|
2087
|
-
}
|
|
2088
|
-
async function loadModuleContexts2(ctx, modules) {
|
|
2089
|
-
if (modules.length === 0) return [];
|
|
2090
|
-
if (!existsSync18(ctx.paths.modulesContextDir)) return [];
|
|
2091
|
-
const available = new Set(
|
|
2092
|
-
(await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
2093
|
-
);
|
|
2094
|
-
const out = [];
|
|
2095
|
-
for (const m of modules) {
|
|
2096
|
-
if (!available.has(m)) continue;
|
|
2097
|
-
const file = path9.join(ctx.paths.modulesContextDir, m, "context.md");
|
|
2098
|
-
if (existsSync18(file)) {
|
|
2099
|
-
out.push({ name: m, content: await readFile3(file, "utf8") });
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
return out;
|
|
2103
|
-
}
|
|
2104
2081
|
|
|
2105
2082
|
// src/tools/code-map.ts
|
|
2106
2083
|
import { estimateTokens as estimateTokens2, loadCodeMap as loadCodeMap2, queryCodeMap as queryCodeMap2 } from "@hiveai/core";
|
|
@@ -2185,7 +2162,7 @@ function estimateFileEntryTokens(f) {
|
|
|
2185
2162
|
}
|
|
2186
2163
|
|
|
2187
2164
|
// src/tools/mem-diff.ts
|
|
2188
|
-
import { existsSync as
|
|
2165
|
+
import { existsSync as existsSync20 } from "fs";
|
|
2189
2166
|
import { loadMemoriesFromDir as loadMemoriesFromDir14 } from "@hiveai/core";
|
|
2190
2167
|
import { z as z19 } from "zod";
|
|
2191
2168
|
var MemDiffInputSchema = {
|
|
@@ -2193,7 +2170,7 @@ var MemDiffInputSchema = {
|
|
|
2193
2170
|
id_b: z19.string().min(1).describe("Second memory id")
|
|
2194
2171
|
};
|
|
2195
2172
|
async function memDiff(input, ctx) {
|
|
2196
|
-
if (!
|
|
2173
|
+
if (!existsSync20(ctx.paths.memoriesDir)) {
|
|
2197
2174
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
2198
2175
|
}
|
|
2199
2176
|
const all = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
|
|
@@ -2230,7 +2207,7 @@ async function memDiff(input, ctx) {
|
|
|
2230
2207
|
}
|
|
2231
2208
|
|
|
2232
2209
|
// src/tools/get-recap.ts
|
|
2233
|
-
import { existsSync as
|
|
2210
|
+
import { existsSync as existsSync21 } from "fs";
|
|
2234
2211
|
import { loadMemoriesFromDir as loadMemoriesFromDir15 } from "@hiveai/core";
|
|
2235
2212
|
import { z as z20 } from "zod";
|
|
2236
2213
|
var GetRecapInputSchema = {
|
|
@@ -2239,7 +2216,7 @@ var GetRecapInputSchema = {
|
|
|
2239
2216
|
)
|
|
2240
2217
|
};
|
|
2241
2218
|
async function getRecap(input, ctx) {
|
|
2242
|
-
if (!
|
|
2219
|
+
if (!existsSync21(ctx.paths.memoriesDir)) {
|
|
2243
2220
|
return { recap: null, notice: "No .ai/memories directory \u2014 haive not initialized here." };
|
|
2244
2221
|
}
|
|
2245
2222
|
const all = await loadMemoriesFromDir15(ctx.paths.memoriesDir);
|
|
@@ -2338,7 +2315,7 @@ async function codeSearch(input, ctx) {
|
|
|
2338
2315
|
}
|
|
2339
2316
|
|
|
2340
2317
|
// src/tools/why-this-file.ts
|
|
2341
|
-
import { existsSync as
|
|
2318
|
+
import { existsSync as existsSync22 } from "fs";
|
|
2342
2319
|
import { spawn } from "child_process";
|
|
2343
2320
|
import path10 from "path";
|
|
2344
2321
|
import {
|
|
@@ -2358,7 +2335,7 @@ var WhyThisFileInputSchema = {
|
|
|
2358
2335
|
memory_limit: z23.number().int().positive().max(20).default(5).describe("Cap on memories anchored to this path.")
|
|
2359
2336
|
};
|
|
2360
2337
|
async function whyThisFile(input, ctx) {
|
|
2361
|
-
const fileExists =
|
|
2338
|
+
const fileExists = existsSync22(path10.join(ctx.paths.root, input.path));
|
|
2362
2339
|
const [commits, memories, codeMap] = await Promise.all([
|
|
2363
2340
|
runGitLog(ctx.paths.root, input.path, input.git_log_limit).catch(() => []),
|
|
2364
2341
|
collectAnchoredMemories(ctx, input.path, input.memory_limit),
|
|
@@ -2399,7 +2376,7 @@ async function whyThisFile(input, ctx) {
|
|
|
2399
2376
|
};
|
|
2400
2377
|
}
|
|
2401
2378
|
async function collectAnchoredMemories(ctx, filePath, limit) {
|
|
2402
|
-
if (!
|
|
2379
|
+
if (!existsSync22(ctx.paths.memoriesDir)) return [];
|
|
2403
2380
|
const all = await loadMemoriesFromDir16(ctx.paths.memoriesDir);
|
|
2404
2381
|
const usage = await loadUsageIndex8(ctx.paths);
|
|
2405
2382
|
const out = [];
|
|
@@ -2454,7 +2431,7 @@ function runCommand(cmd, args, cwd) {
|
|
|
2454
2431
|
}
|
|
2455
2432
|
|
|
2456
2433
|
// src/tools/anti-patterns-check.ts
|
|
2457
|
-
import { existsSync as
|
|
2434
|
+
import { existsSync as existsSync23 } from "fs";
|
|
2458
2435
|
import {
|
|
2459
2436
|
addedLinesFromDiff,
|
|
2460
2437
|
deriveConfidence as deriveConfidence6,
|
|
@@ -2548,7 +2525,7 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
2548
2525
|
notice: "Nothing to check \u2014 provide either `diff` text or `paths`."
|
|
2549
2526
|
};
|
|
2550
2527
|
}
|
|
2551
|
-
if (!
|
|
2528
|
+
if (!existsSync23(ctx.paths.memoriesDir)) {
|
|
2552
2529
|
return { scanned: 0, warnings: [], notice: "No .ai/memories directory \u2014 nothing to check against." };
|
|
2553
2530
|
}
|
|
2554
2531
|
const all = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
|
|
@@ -2583,6 +2560,7 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
2583
2560
|
reasons: [reason],
|
|
2584
2561
|
tags: fm.tags ?? [],
|
|
2585
2562
|
anchor_paths: fm.anchor?.paths ?? [],
|
|
2563
|
+
...fm.sensor != null ? { has_sensor: true } : {},
|
|
2586
2564
|
...score !== void 0 ? { semantic_score: score } : {}
|
|
2587
2565
|
});
|
|
2588
2566
|
};
|
|
@@ -2651,7 +2629,7 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
2651
2629
|
}
|
|
2652
2630
|
|
|
2653
2631
|
// src/tools/mem-distill.ts
|
|
2654
|
-
import { existsSync as
|
|
2632
|
+
import { existsSync as existsSync24 } from "fs";
|
|
2655
2633
|
import {
|
|
2656
2634
|
loadMemoriesFromDir as loadMemoriesFromDir18,
|
|
2657
2635
|
tokenizeQuery as tokenizeQuery4
|
|
@@ -2703,7 +2681,7 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
2703
2681
|
"error"
|
|
2704
2682
|
]);
|
|
2705
2683
|
async function memDistill(input, ctx) {
|
|
2706
|
-
if (!
|
|
2684
|
+
if (!existsSync24(ctx.paths.memoriesDir)) {
|
|
2707
2685
|
return { scanned: 0, singletons: 0, clusters: [], notice: "No .ai/memories directory." };
|
|
2708
2686
|
}
|
|
2709
2687
|
const cutoff = Date.now() - input.since_days * MS_PER_DAY;
|
|
@@ -2811,7 +2789,7 @@ function firstHeading(body) {
|
|
|
2811
2789
|
}
|
|
2812
2790
|
|
|
2813
2791
|
// src/tools/why-this-decision.ts
|
|
2814
|
-
import { existsSync as
|
|
2792
|
+
import { existsSync as existsSync25 } from "fs";
|
|
2815
2793
|
import { spawn as spawn2 } from "child_process";
|
|
2816
2794
|
import {
|
|
2817
2795
|
deriveConfidence as deriveConfidence7,
|
|
@@ -2826,7 +2804,7 @@ var WhyThisDecisionInputSchema = {
|
|
|
2826
2804
|
git_log_limit: z26.number().int().positive().max(20).default(5).describe("How many recent commits per anchor path to surface.")
|
|
2827
2805
|
};
|
|
2828
2806
|
async function whyThisDecision(input, ctx) {
|
|
2829
|
-
if (!
|
|
2807
|
+
if (!existsSync25(ctx.paths.memoriesDir)) {
|
|
2830
2808
|
return {
|
|
2831
2809
|
found: false,
|
|
2832
2810
|
related: [],
|
|
@@ -2958,7 +2936,7 @@ function runCommand2(cmd, args, cwd) {
|
|
|
2958
2936
|
}
|
|
2959
2937
|
|
|
2960
2938
|
// src/tools/mem-conflicts.ts
|
|
2961
|
-
import { existsSync as
|
|
2939
|
+
import { existsSync as existsSync26 } from "fs";
|
|
2962
2940
|
import {
|
|
2963
2941
|
deriveConfidence as deriveConfidence8,
|
|
2964
2942
|
getUsage as getUsage9,
|
|
@@ -2976,7 +2954,7 @@ var MemConflictsInputSchema = {
|
|
|
2976
2954
|
var POSITIVE_PATTERNS = /\b(use|prefer|always|should use|do this|recommended|ok to)\b/i;
|
|
2977
2955
|
var NEGATIVE_PATTERNS = /\b(do not use|don'?t use|never|avoid|forbidden|deprecated|stop using|do NOT|❌)\b/i;
|
|
2978
2956
|
async function memConflicts(input, ctx) {
|
|
2979
|
-
if (!
|
|
2957
|
+
if (!existsSync26(ctx.paths.memoriesDir)) {
|
|
2980
2958
|
return { found: false, scanned: 0, conflicts: [], notice: "No .ai/memories directory." };
|
|
2981
2959
|
}
|
|
2982
2960
|
const all = await loadMemoriesFromDir20(ctx.paths.memoriesDir);
|
|
@@ -3200,6 +3178,15 @@ function classifyWarning(warning, paths, anchoredBlocks = false) {
|
|
|
3200
3178
|
};
|
|
3201
3179
|
}
|
|
3202
3180
|
if (isBlockingWarning(warning)) {
|
|
3181
|
+
if (warning.has_sensor && !warning.reasons.includes("sensor")) {
|
|
3182
|
+
return {
|
|
3183
|
+
...warning,
|
|
3184
|
+
level: "review",
|
|
3185
|
+
rationale: "memory has a sensor that did not fire \u2014 sensor is the authoritative check; strong semantic match alone is insufficient to block",
|
|
3186
|
+
affected_files: affectedFiles,
|
|
3187
|
+
repair_command: repairCommand
|
|
3188
|
+
};
|
|
3189
|
+
}
|
|
3203
3190
|
return {
|
|
3204
3191
|
...warning,
|
|
3205
3192
|
level: "blocking",
|
|
@@ -3212,6 +3199,15 @@ function classifyWarning(warning, paths, anchoredBlocks = false) {
|
|
|
3212
3199
|
const semanticScore = warning.semantic_score ?? 0;
|
|
3213
3200
|
const highConfidence = warning.confidence === "authoritative" || warning.confidence === "trusted";
|
|
3214
3201
|
if (anchoredBlocks && highConfidence && warning.reasons.includes("anchor") && (warning.reasons.includes("literal") || hasSemantic && semanticScore >= 0.45)) {
|
|
3202
|
+
if (warning.has_sensor && !warning.reasons.includes("sensor")) {
|
|
3203
|
+
return {
|
|
3204
|
+
...warning,
|
|
3205
|
+
level: "review",
|
|
3206
|
+
rationale: "memory has a sensor that did not fire \u2014 literal match alone is insufficient to block; sensor is the authoritative check",
|
|
3207
|
+
affected_files: affectedFiles,
|
|
3208
|
+
repair_command: repairCommand
|
|
3209
|
+
};
|
|
3210
|
+
}
|
|
3215
3211
|
return {
|
|
3216
3212
|
...warning,
|
|
3217
3213
|
level: "blocking",
|
|
@@ -3342,7 +3338,7 @@ function repairCommandForWarning(warning, paths) {
|
|
|
3342
3338
|
|
|
3343
3339
|
// src/tools/pattern-detect.ts
|
|
3344
3340
|
import { mkdir as mkdir7, writeFile as writeFile12 } from "fs/promises";
|
|
3345
|
-
import { existsSync as
|
|
3341
|
+
import { existsSync as existsSync27 } from "fs";
|
|
3346
3342
|
import path11 from "path";
|
|
3347
3343
|
import { execSync as execSync2 } from "child_process";
|
|
3348
3344
|
import {
|
|
@@ -3379,7 +3375,7 @@ var PatternDetectInputSchema = {
|
|
|
3379
3375
|
scope: z29.enum(["personal", "team"]).default("team").describe("Scope for proposed memories.")
|
|
3380
3376
|
};
|
|
3381
3377
|
async function patternDetect(input, ctx) {
|
|
3382
|
-
if (!
|
|
3378
|
+
if (!existsSync27(ctx.paths.haiveDir)) {
|
|
3383
3379
|
return {
|
|
3384
3380
|
scanned_events: 0,
|
|
3385
3381
|
matches: [],
|
|
@@ -3508,7 +3504,7 @@ async function patternDetect(input, ctx) {
|
|
|
3508
3504
|
fm.id,
|
|
3509
3505
|
void 0
|
|
3510
3506
|
);
|
|
3511
|
-
if (
|
|
3507
|
+
if (existsSync27(file)) continue;
|
|
3512
3508
|
await mkdir7(path11.dirname(file), { recursive: true });
|
|
3513
3509
|
await writeFile12(
|
|
3514
3510
|
file,
|
|
@@ -3554,7 +3550,7 @@ function gitFileDiff(root, file, sinceDays) {
|
|
|
3554
3550
|
}
|
|
3555
3551
|
|
|
3556
3552
|
// src/tools/mem-conflict-candidates.ts
|
|
3557
|
-
import { existsSync as
|
|
3553
|
+
import { existsSync as existsSync28 } from "fs";
|
|
3558
3554
|
import {
|
|
3559
3555
|
findLexicalConflictPairs,
|
|
3560
3556
|
findTopicStatusConflictPairs,
|
|
@@ -3572,7 +3568,7 @@ var MemConflictCandidatesInputSchema = {
|
|
|
3572
3568
|
)
|
|
3573
3569
|
};
|
|
3574
3570
|
async function memConflictCandidates(input, ctx) {
|
|
3575
|
-
if (!
|
|
3571
|
+
if (!existsSync28(ctx.paths.memoriesDir)) {
|
|
3576
3572
|
return {
|
|
3577
3573
|
pairs: [],
|
|
3578
3574
|
topic_status_pairs: [],
|
|
@@ -3624,7 +3620,7 @@ async function memSuggestTopic(input, _ctx) {
|
|
|
3624
3620
|
}
|
|
3625
3621
|
|
|
3626
3622
|
// src/tools/mem-timeline.ts
|
|
3627
|
-
import { existsSync as
|
|
3623
|
+
import { existsSync as existsSync29 } from "fs";
|
|
3628
3624
|
import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir22 } from "@hiveai/core";
|
|
3629
3625
|
import { z as z33 } from "zod";
|
|
3630
3626
|
var MemTimelineInputSchema = {
|
|
@@ -3633,7 +3629,7 @@ var MemTimelineInputSchema = {
|
|
|
3633
3629
|
limit: z33.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
|
|
3634
3630
|
};
|
|
3635
3631
|
async function memTimeline(input, ctx) {
|
|
3636
|
-
if (!
|
|
3632
|
+
if (!existsSync29(ctx.paths.memoriesDir)) {
|
|
3637
3633
|
return { entries: [], total: 0, notice: "No .ai/memories directory." };
|
|
3638
3634
|
}
|
|
3639
3635
|
const all = await loadMemoriesFromDir22(ctx.paths.memoriesDir);
|
|
@@ -3923,9 +3919,9 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
3923
3919
|
}
|
|
3924
3920
|
|
|
3925
3921
|
// src/server.ts
|
|
3926
|
-
import { loadConfigSync } from "@hiveai/core";
|
|
3922
|
+
import { hasRecentBriefingMarker, loadConfigSync } from "@hiveai/core";
|
|
3927
3923
|
var SERVER_NAME = "haive";
|
|
3928
|
-
var SERVER_VERSION = "0.10.
|
|
3924
|
+
var SERVER_VERSION = "0.10.8";
|
|
3929
3925
|
function jsonResult(data) {
|
|
3930
3926
|
return {
|
|
3931
3927
|
content: [
|
|
@@ -4029,11 +4025,16 @@ function createHaiveServer(options = {}) {
|
|
|
4029
4025
|
return await handler(input);
|
|
4030
4026
|
}
|
|
4031
4027
|
if (requireBriefingFirst && MUTATING_TOOLS.has(name) && !briefingLoaded) {
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4028
|
+
const hasDiskMarker = await hasRecentBriefingMarker(context.paths).catch(() => false);
|
|
4029
|
+
if (hasDiskMarker) {
|
|
4030
|
+
briefingLoaded = true;
|
|
4031
|
+
} else {
|
|
4032
|
+
return jsonResult({
|
|
4033
|
+
error: "haive_briefing_required",
|
|
4034
|
+
message: "This hAIve project requires get_briefing or mem_relevant_to before state-changing hAIve tools. Call get_briefing({ task: '...' }) first.",
|
|
4035
|
+
tool: name
|
|
4036
|
+
});
|
|
4037
|
+
}
|
|
4037
4038
|
}
|
|
4038
4039
|
return await handler(input);
|
|
4039
4040
|
}
|