@hiveai/mcp 0.9.2 → 0.9.3
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 +86 -14
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +36 -7
- package/dist/server.js +86 -14
- package/dist/server.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -157,6 +157,30 @@ var MemSaveInputSchema = {
|
|
|
157
157
|
function bodyHash(body) {
|
|
158
158
|
return createHash("sha256").update(body.trim()).digest("hex").slice(0, 12);
|
|
159
159
|
}
|
|
160
|
+
var WORD_RE = /\b[a-z0-9]{3,}\b/gi;
|
|
161
|
+
function bodyTokenSet(body) {
|
|
162
|
+
const raw = body.toLowerCase().match(WORD_RE) ?? [];
|
|
163
|
+
return new Set(raw);
|
|
164
|
+
}
|
|
165
|
+
function maxBodySimilarity(incomingTokens, memories, scope, type, excludeIds) {
|
|
166
|
+
if (incomingTokens.size < 6) return null;
|
|
167
|
+
let best = null;
|
|
168
|
+
const skip = excludeIds ?? /* @__PURE__ */ new Set();
|
|
169
|
+
for (const { memory } of memories) {
|
|
170
|
+
const fm = memory.frontmatter;
|
|
171
|
+
if (skip.has(fm.id)) continue;
|
|
172
|
+
if (fm.scope !== scope || fm.type !== type) continue;
|
|
173
|
+
if (fm.status === "rejected" || fm.status === "deprecated") continue;
|
|
174
|
+
const other = bodyTokenSet(memory.body);
|
|
175
|
+
if (other.size === 0) continue;
|
|
176
|
+
let inter = 0;
|
|
177
|
+
for (const t of incomingTokens) if (other.has(t)) inter++;
|
|
178
|
+
const uni = incomingTokens.size + other.size - inter;
|
|
179
|
+
const j = uni === 0 ? 0 : inter / uni;
|
|
180
|
+
if (j >= 0.72 && (!best || j > best.score)) best = { score: j, id: fm.id };
|
|
181
|
+
}
|
|
182
|
+
return best;
|
|
183
|
+
}
|
|
160
184
|
async function memSave(input, ctx) {
|
|
161
185
|
if (!existsSync4(ctx.paths.haiveDir)) {
|
|
162
186
|
throw new Error(
|
|
@@ -178,12 +202,26 @@ async function memSave(input, ctx) {
|
|
|
178
202
|
`Duplicate content detected \u2014 identical body already saved as "${hashDuplicate.memory.frontmatter.id}". Use mem_update to modify it, or change the body to add new information.`
|
|
179
203
|
);
|
|
180
204
|
}
|
|
205
|
+
const incomingTokens = bodyTokenSet(input.body);
|
|
206
|
+
function bodySimilarWarnings(excludeIds) {
|
|
207
|
+
const dup = maxBodySimilarity(incomingTokens, existing, resolvedScope, input.type, excludeIds);
|
|
208
|
+
if (!dup?.id) return {};
|
|
209
|
+
const body_similar = {
|
|
210
|
+
id: dup.id,
|
|
211
|
+
score: Math.round(dup.score * 100) / 100
|
|
212
|
+
};
|
|
213
|
+
return {
|
|
214
|
+
similarityWarning: `Body is ~${Math.round(dup.score * 100)}% similar (token overlap) to existing "${dup.id}" \u2014 consolidate if redundant.`,
|
|
215
|
+
body_similar
|
|
216
|
+
};
|
|
217
|
+
}
|
|
181
218
|
if (input.topic) {
|
|
182
219
|
const topicMatch = existing.find(
|
|
183
220
|
({ memory }) => memory.frontmatter.topic === input.topic && memory.frontmatter.scope === resolvedScope && (!input.module || memory.frontmatter.module === input.module)
|
|
184
221
|
);
|
|
185
222
|
if (topicMatch) {
|
|
186
223
|
const fm = topicMatch.memory.frontmatter;
|
|
224
|
+
const { similarityWarning: simW, body_similar: bs } = bodySimilarWarnings(/* @__PURE__ */ new Set([fm.id]));
|
|
187
225
|
const newFrontmatter = {
|
|
188
226
|
...fm,
|
|
189
227
|
body: input.body,
|
|
@@ -200,13 +238,19 @@ async function memSave(input, ctx) {
|
|
|
200
238
|
serializeMemory({ frontmatter: newFrontmatter, body: input.body }),
|
|
201
239
|
"utf8"
|
|
202
240
|
);
|
|
241
|
+
const mergedTw = [
|
|
242
|
+
invalidPaths.length > 0 ? `Anchor path(s) not found in project: ${invalidPaths.join(", ")}. They will be marked stale by haive sync.` : null,
|
|
243
|
+
simW ?? null
|
|
244
|
+
].filter(Boolean).join(" \u2014 ") || void 0;
|
|
203
245
|
return {
|
|
204
246
|
id: fm.id,
|
|
205
247
|
scope: fm.scope,
|
|
206
248
|
file_path: topicMatch.filePath,
|
|
207
249
|
action: "updated",
|
|
208
250
|
revision_count: newFrontmatter.revision_count,
|
|
209
|
-
...
|
|
251
|
+
...mergedTw ? { warning: mergedTw } : {},
|
|
252
|
+
...bs ? { body_similar: bs } : {},
|
|
253
|
+
...invalidPaths.length > 0 ? { invalid_paths: invalidPaths } : {}
|
|
210
254
|
};
|
|
211
255
|
}
|
|
212
256
|
}
|
|
@@ -248,9 +292,11 @@ async function memSave(input, ctx) {
|
|
|
248
292
|
}
|
|
249
293
|
}
|
|
250
294
|
await writeFile2(file, serializeMemory({ frontmatter, body: input.body }), "utf8");
|
|
295
|
+
const { similarityWarning: simWarnNew, body_similar: bsNew } = bodySimilarWarnings();
|
|
251
296
|
const finalWarning = [
|
|
252
297
|
invalidPaths.length > 0 ? `Anchor path(s) not found in project: ${invalidPaths.join(", ")}. They will be marked stale by \`haive sync\`.` : null,
|
|
253
|
-
warning ?? null
|
|
298
|
+
warning ?? null,
|
|
299
|
+
simWarnNew ?? null
|
|
254
300
|
].filter(Boolean).join(" \u2014 ") || void 0;
|
|
255
301
|
return {
|
|
256
302
|
id: frontmatter.id,
|
|
@@ -259,6 +305,7 @@ async function memSave(input, ctx) {
|
|
|
259
305
|
action: "created",
|
|
260
306
|
...finalWarning ? { warning: finalWarning } : {},
|
|
261
307
|
...similar_found ? { similar_found } : {},
|
|
308
|
+
...bsNew ? { body_similar: bsNew } : {},
|
|
262
309
|
...invalidPaths.length > 0 ? { invalid_paths: invalidPaths } : {}
|
|
263
310
|
};
|
|
264
311
|
}
|
|
@@ -1294,6 +1341,7 @@ import {
|
|
|
1294
1341
|
DEFAULT_AUTO_PROMOTE_RULE,
|
|
1295
1342
|
deriveConfidence as deriveConfidence4,
|
|
1296
1343
|
estimateTokens,
|
|
1344
|
+
extractActionsBriefBody,
|
|
1297
1345
|
getUsage as getUsage5,
|
|
1298
1346
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
1299
1347
|
isAutoPromoteEligible,
|
|
@@ -1306,6 +1354,7 @@ import {
|
|
|
1306
1354
|
loadUsageIndex as loadUsageIndex7,
|
|
1307
1355
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
1308
1356
|
queryCodeMap,
|
|
1357
|
+
resolveBriefingBudget,
|
|
1309
1358
|
serializeMemory as serializeMemory9,
|
|
1310
1359
|
tokenizeQuery as tokenizeQuery2,
|
|
1311
1360
|
trackReads as trackReads3,
|
|
@@ -1328,17 +1377,29 @@ var GetBriefingInputSchema = {
|
|
|
1328
1377
|
),
|
|
1329
1378
|
include_stale: z17.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
1330
1379
|
track: z17.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
1331
|
-
format: z17.enum(["full", "compact"]).default("full").describe(
|
|
1332
|
-
"Output format: 'full' returns
|
|
1380
|
+
format: z17.enum(["full", "compact", "actions"]).default("full").describe(
|
|
1381
|
+
"Output format: 'full' returns memory bodies (honors token budget via truncation); 'compact' returns a 1-line summary per memory (call mem_get for detail); 'actions' squeezes bodies to actionable bullet lines \u2014 fewer tokens vs full."
|
|
1333
1382
|
),
|
|
1334
1383
|
symbols: z17.array(z17.string()).default([]).describe(
|
|
1335
1384
|
"Symbol names to look up in the code-map (e.g. ['PaymentService', 'TenantFilter']). Returns the file(s) exporting each symbol so agents don't need to grep. Requires `haive index code` to have been run."
|
|
1336
1385
|
),
|
|
1337
1386
|
min_semantic_score: z17.number().min(0).max(1).default(0).describe(
|
|
1338
1387
|
"Drop semantic-only memory hits whose cosine score is below this threshold. Useful to avoid weakly-related noise when the task is short or the corpus is broad. Has no effect on memories matched via anchor/module/literal \u2014 those are always kept. Try 0.25\u20130.4 for stricter matching."
|
|
1388
|
+
),
|
|
1389
|
+
budget_preset: z17.enum(["quick", "balanced", "deep"]).optional().describe(
|
|
1390
|
+
"Shortcut token budget: 'quick' minimizes tokens/skip module CONTEXT slices; 'balanced' mirrors historical defaults; 'deep' uses a larger briefing. When set, overrides max_tokens, max_memories, and include_module_contexts."
|
|
1339
1391
|
)
|
|
1340
1392
|
};
|
|
1393
|
+
var GetBriefingZod = z17.object(GetBriefingInputSchema);
|
|
1341
1394
|
async function getBriefing(input, ctx) {
|
|
1395
|
+
const resolvedBudget = resolveBriefingBudget(input.budget_preset, {
|
|
1396
|
+
max_tokens: input.max_tokens,
|
|
1397
|
+
max_memories: input.max_memories,
|
|
1398
|
+
include_module_contexts: input.include_module_contexts
|
|
1399
|
+
});
|
|
1400
|
+
const briefingMaxTokens = resolvedBudget.max_tokens;
|
|
1401
|
+
const briefingMaxMemories = resolvedBudget.max_memories;
|
|
1402
|
+
const briefingIncludeModules = resolvedBudget.include_module_contexts;
|
|
1342
1403
|
const inferred = inferModulesFromPaths2(input.files);
|
|
1343
1404
|
const memories = [];
|
|
1344
1405
|
let searchMode = "literal";
|
|
@@ -1450,8 +1511,8 @@ async function getBriefing(input, ctx) {
|
|
|
1450
1511
|
const sb = reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
|
|
1451
1512
|
return sb - sa;
|
|
1452
1513
|
});
|
|
1453
|
-
for (const mem of ranked.slice(0,
|
|
1454
|
-
if (seen.size >=
|
|
1514
|
+
for (const mem of ranked.slice(0, briefingMaxMemories)) {
|
|
1515
|
+
if (seen.size >= briefingMaxMemories * 2) break;
|
|
1455
1516
|
const loaded = byId.get(mem.id);
|
|
1456
1517
|
if (!loaded) continue;
|
|
1457
1518
|
for (const relId of loaded.memory.frontmatter.related_ids ?? []) {
|
|
@@ -1460,7 +1521,7 @@ async function getBriefing(input, ctx) {
|
|
|
1460
1521
|
if (related) addOrUpdate(related, "anchor", void 0, "partial");
|
|
1461
1522
|
}
|
|
1462
1523
|
}
|
|
1463
|
-
memories.push(...ranked.slice(0,
|
|
1524
|
+
memories.push(...ranked.slice(0, briefingMaxMemories));
|
|
1464
1525
|
if (input.track && memories.length > 0) {
|
|
1465
1526
|
await trackReads3(ctx.paths, memories.map((m) => m.id));
|
|
1466
1527
|
const freshUsage = await loadUsageIndex7(ctx.paths);
|
|
@@ -1540,7 +1601,7 @@ async function getBriefing(input, ctx) {
|
|
|
1540
1601
|
}
|
|
1541
1602
|
}
|
|
1542
1603
|
}
|
|
1543
|
-
const moduleContents =
|
|
1604
|
+
const moduleContents = briefingIncludeModules ? await loadModuleContexts2(ctx, inferred) : [];
|
|
1544
1605
|
const memoriesText = memories.map((m) => {
|
|
1545
1606
|
const unverified = m.status === "proposed" ? " [UNVERIFIED \u2014 not yet validated]" : "";
|
|
1546
1607
|
return `### ${m.id} (${m.scope}/${m.type}, ${m.confidence})${unverified}
|
|
@@ -1558,7 +1619,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1558
1619
|
},
|
|
1559
1620
|
{ key: "memories", text: memoriesText, weight: 4, mode: "head" }
|
|
1560
1621
|
],
|
|
1561
|
-
|
|
1622
|
+
briefingMaxTokens
|
|
1562
1623
|
);
|
|
1563
1624
|
const projectSlice = slices.find((s) => s.key === "project");
|
|
1564
1625
|
const modulesSlice = slices.find((s) => s.key === "modules");
|
|
@@ -1600,7 +1661,10 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1600
1661
|
const createdAt = loaded?.memory.frontmatter.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1601
1662
|
if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
|
|
1602
1663
|
}
|
|
1603
|
-
const outputMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : trimmedMemories
|
|
1664
|
+
const outputMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : input.format === "actions" ? trimmedMemories.map((m) => ({
|
|
1665
|
+
...m,
|
|
1666
|
+
body: extractActionsBriefBody(m.body)
|
|
1667
|
+
})) : trimmedMemories;
|
|
1604
1668
|
let symbolLocations;
|
|
1605
1669
|
const symbolsToLookup = new Set(input.symbols);
|
|
1606
1670
|
for (const m of outputMemories) {
|
|
@@ -1722,6 +1786,11 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
1722
1786
|
"After completing the task: capture new gotchas with mem_observe, failed approaches with mem_tried, validated patterns with mem_save."
|
|
1723
1787
|
);
|
|
1724
1788
|
}
|
|
1789
|
+
if (outputMemories.length > 2 && !input.budget_preset && input.task && !hints.some((h) => h.includes("budget_preset"))) {
|
|
1790
|
+
hints.push(
|
|
1791
|
+
"For tighter token budgets on small tasks pass budget_preset:'quick'; for refactor-sized work use budget_preset:'deep'."
|
|
1792
|
+
);
|
|
1793
|
+
}
|
|
1725
1794
|
}
|
|
1726
1795
|
return {
|
|
1727
1796
|
...input.task ? { task: input.task } : {},
|
|
@@ -1744,7 +1813,8 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
1744
1813
|
...hints.length > 0 ? { hints } : {},
|
|
1745
1814
|
estimated_tokens: totalTokens,
|
|
1746
1815
|
budget: {
|
|
1747
|
-
max_tokens:
|
|
1816
|
+
max_tokens: briefingMaxTokens,
|
|
1817
|
+
...input.budget_preset ? { preset_applied: input.budget_preset } : {},
|
|
1748
1818
|
spent: {
|
|
1749
1819
|
project: projectSlice.estimatedTokens,
|
|
1750
1820
|
modules: modulesSlice.estimatedTokens,
|
|
@@ -1957,7 +2027,7 @@ var MemRelevantToInputSchema = {
|
|
|
1957
2027
|
files: z21.array(z21.string()).default([]).describe("Optional: files you are about to edit \u2014 surfaces anchored memories."),
|
|
1958
2028
|
limit: z21.number().int().positive().max(30).default(8).describe("Cap on returned memories."),
|
|
1959
2029
|
min_semantic_score: z21.number().min(0).max(1).default(0.25).describe("Drop weakly-related semantic hits below this cosine threshold."),
|
|
1960
|
-
format: z21.enum(["full", "compact"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies.")
|
|
2030
|
+
format: z21.enum(["full", "compact", "actions"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies; 'actions' = bullet-first excerpts.")
|
|
1961
2031
|
};
|
|
1962
2032
|
async function memRelevantTo(input, ctx) {
|
|
1963
2033
|
const briefingInput = {
|
|
@@ -3215,7 +3285,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
3215
3285
|
|
|
3216
3286
|
// src/server.ts
|
|
3217
3287
|
var SERVER_NAME = "haive";
|
|
3218
|
-
var SERVER_VERSION = "0.9.
|
|
3288
|
+
var SERVER_VERSION = "0.9.3";
|
|
3219
3289
|
function jsonResult(data) {
|
|
3220
3290
|
return {
|
|
3221
3291
|
content: [
|
|
@@ -3381,7 +3451,8 @@ function createHaiveServer(options = {}) {
|
|
|
3381
3451
|
" task \u2014 what you are about to do (1\u20132 sentences) \u2014 ALWAYS provide this",
|
|
3382
3452
|
" files \u2014 files you are about to edit \u2014 surfaces anchored memories",
|
|
3383
3453
|
" symbols \u2014 symbol names to look up in the code-map (e.g. ['PaymentService'])",
|
|
3384
|
-
" format \u2014 'full' (default) | 'compact' (1-line
|
|
3454
|
+
" format \u2014 'full' (default) | 'compact' (1-line) | 'actions' (bullet-first excerpts)",
|
|
3455
|
+
" budget_preset \u2014 'quick' | 'balanced' | 'deep' \u2014 scales max_tokens/memories/module contexts",
|
|
3385
3456
|
"",
|
|
3386
3457
|
"EXAMPLE USAGE:",
|
|
3387
3458
|
" get_briefing({ task: 'add a Stripe payment integration', files: ['src/payments/'], symbols: ['PaymentService'] })",
|
|
@@ -3687,6 +3758,7 @@ function createHaiveServer(options = {}) {
|
|
|
3687
3758
|
" files \u2014 files you'll edit (surfaces anchored memories)",
|
|
3688
3759
|
" limit \u2014 cap on returned memories (default 8)",
|
|
3689
3760
|
" min_semantic_score \u2014 drop weak semantic hits below this cosine (default 0.25)",
|
|
3761
|
+
" format \u2014 'full' | 'compact' | 'actions' (inherits get_briefing memory framing)",
|
|
3690
3762
|
"",
|
|
3691
3763
|
"RETURNS: { task, search_mode, memories: [...], hints?: [...], empty?: true }"
|
|
3692
3764
|
].join("\n"),
|