@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/server.d.ts
CHANGED
|
@@ -23,7 +23,8 @@ declare class SessionTracker {
|
|
|
23
23
|
private registerShutdownHandler;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
/** Single inferred type — optional keys are optional (not `| undefined` required everywhere). */
|
|
27
|
+
declare const GetBriefingZod: z.ZodObject<{
|
|
27
28
|
task: z.ZodOptional<z.ZodString>;
|
|
28
29
|
files: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
29
30
|
max_tokens: z.ZodDefault<z.ZodNumber>;
|
|
@@ -33,13 +34,40 @@ declare const GetBriefingInputSchema: {
|
|
|
33
34
|
semantic: z.ZodDefault<z.ZodBoolean>;
|
|
34
35
|
include_stale: z.ZodDefault<z.ZodBoolean>;
|
|
35
36
|
track: z.ZodDefault<z.ZodBoolean>;
|
|
36
|
-
format: z.ZodDefault<z.ZodEnum<["full", "compact"]>>;
|
|
37
|
+
format: z.ZodDefault<z.ZodEnum<["full", "compact", "actions"]>>;
|
|
37
38
|
symbols: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
38
39
|
min_semantic_score: z.ZodDefault<z.ZodNumber>;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
budget_preset: z.ZodOptional<z.ZodEnum<["quick", "balanced", "deep"]>>;
|
|
41
|
+
}, "strip", z.ZodTypeAny, {
|
|
42
|
+
symbols: string[];
|
|
43
|
+
files: string[];
|
|
44
|
+
max_tokens: number;
|
|
45
|
+
max_memories: number;
|
|
46
|
+
include_project_context: boolean;
|
|
47
|
+
include_module_contexts: boolean;
|
|
48
|
+
semantic: boolean;
|
|
49
|
+
include_stale: boolean;
|
|
50
|
+
track: boolean;
|
|
51
|
+
format: "full" | "compact" | "actions";
|
|
52
|
+
min_semantic_score: number;
|
|
53
|
+
task?: string | undefined;
|
|
54
|
+
budget_preset?: "quick" | "balanced" | "deep" | undefined;
|
|
55
|
+
}, {
|
|
56
|
+
symbols?: string[] | undefined;
|
|
57
|
+
task?: string | undefined;
|
|
58
|
+
files?: string[] | undefined;
|
|
59
|
+
max_tokens?: number | undefined;
|
|
60
|
+
max_memories?: number | undefined;
|
|
61
|
+
include_project_context?: boolean | undefined;
|
|
62
|
+
include_module_contexts?: boolean | undefined;
|
|
63
|
+
semantic?: boolean | undefined;
|
|
64
|
+
include_stale?: boolean | undefined;
|
|
65
|
+
track?: boolean | undefined;
|
|
66
|
+
format?: "full" | "compact" | "actions" | undefined;
|
|
67
|
+
min_semantic_score?: number | undefined;
|
|
68
|
+
budget_preset?: "quick" | "balanced" | "deep" | undefined;
|
|
69
|
+
}>;
|
|
70
|
+
type GetBriefingInput = z.infer<typeof GetBriefingZod>;
|
|
43
71
|
interface BriefingMemory {
|
|
44
72
|
id: string;
|
|
45
73
|
scope: string;
|
|
@@ -133,6 +161,7 @@ interface BriefingOutput {
|
|
|
133
161
|
modules: number;
|
|
134
162
|
memories: number;
|
|
135
163
|
};
|
|
164
|
+
preset_applied?: "quick" | "balanced" | "deep";
|
|
136
165
|
};
|
|
137
166
|
}
|
|
138
167
|
declare function getBriefing(input: GetBriefingInput, ctx: HaiveContext): Promise<BriefingOutput>;
|
|
@@ -198,7 +227,7 @@ declare const MemRelevantToInputSchema: {
|
|
|
198
227
|
files: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
199
228
|
limit: z.ZodDefault<z.ZodNumber>;
|
|
200
229
|
min_semantic_score: z.ZodDefault<z.ZodNumber>;
|
|
201
|
-
format: z.ZodDefault<z.ZodEnum<["full", "compact"]>>;
|
|
230
|
+
format: z.ZodDefault<z.ZodEnum<["full", "compact", "actions"]>>;
|
|
202
231
|
};
|
|
203
232
|
type MemRelevantToInput = {
|
|
204
233
|
[K in keyof typeof MemRelevantToInputSchema]: z.infer<(typeof MemRelevantToInputSchema)[K]>;
|
package/dist/server.js
CHANGED
|
@@ -155,6 +155,30 @@ var MemSaveInputSchema = {
|
|
|
155
155
|
function bodyHash(body) {
|
|
156
156
|
return createHash("sha256").update(body.trim()).digest("hex").slice(0, 12);
|
|
157
157
|
}
|
|
158
|
+
var WORD_RE = /\b[a-z0-9]{3,}\b/gi;
|
|
159
|
+
function bodyTokenSet(body) {
|
|
160
|
+
const raw = body.toLowerCase().match(WORD_RE) ?? [];
|
|
161
|
+
return new Set(raw);
|
|
162
|
+
}
|
|
163
|
+
function maxBodySimilarity(incomingTokens, memories, scope, type, excludeIds) {
|
|
164
|
+
if (incomingTokens.size < 6) return null;
|
|
165
|
+
let best = null;
|
|
166
|
+
const skip = excludeIds ?? /* @__PURE__ */ new Set();
|
|
167
|
+
for (const { memory } of memories) {
|
|
168
|
+
const fm = memory.frontmatter;
|
|
169
|
+
if (skip.has(fm.id)) continue;
|
|
170
|
+
if (fm.scope !== scope || fm.type !== type) continue;
|
|
171
|
+
if (fm.status === "rejected" || fm.status === "deprecated") continue;
|
|
172
|
+
const other = bodyTokenSet(memory.body);
|
|
173
|
+
if (other.size === 0) continue;
|
|
174
|
+
let inter = 0;
|
|
175
|
+
for (const t of incomingTokens) if (other.has(t)) inter++;
|
|
176
|
+
const uni = incomingTokens.size + other.size - inter;
|
|
177
|
+
const j = uni === 0 ? 0 : inter / uni;
|
|
178
|
+
if (j >= 0.72 && (!best || j > best.score)) best = { score: j, id: fm.id };
|
|
179
|
+
}
|
|
180
|
+
return best;
|
|
181
|
+
}
|
|
158
182
|
async function memSave(input, ctx) {
|
|
159
183
|
if (!existsSync4(ctx.paths.haiveDir)) {
|
|
160
184
|
throw new Error(
|
|
@@ -176,12 +200,26 @@ async function memSave(input, ctx) {
|
|
|
176
200
|
`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.`
|
|
177
201
|
);
|
|
178
202
|
}
|
|
203
|
+
const incomingTokens = bodyTokenSet(input.body);
|
|
204
|
+
function bodySimilarWarnings(excludeIds) {
|
|
205
|
+
const dup = maxBodySimilarity(incomingTokens, existing, resolvedScope, input.type, excludeIds);
|
|
206
|
+
if (!dup?.id) return {};
|
|
207
|
+
const body_similar = {
|
|
208
|
+
id: dup.id,
|
|
209
|
+
score: Math.round(dup.score * 100) / 100
|
|
210
|
+
};
|
|
211
|
+
return {
|
|
212
|
+
similarityWarning: `Body is ~${Math.round(dup.score * 100)}% similar (token overlap) to existing "${dup.id}" \u2014 consolidate if redundant.`,
|
|
213
|
+
body_similar
|
|
214
|
+
};
|
|
215
|
+
}
|
|
179
216
|
if (input.topic) {
|
|
180
217
|
const topicMatch = existing.find(
|
|
181
218
|
({ memory }) => memory.frontmatter.topic === input.topic && memory.frontmatter.scope === resolvedScope && (!input.module || memory.frontmatter.module === input.module)
|
|
182
219
|
);
|
|
183
220
|
if (topicMatch) {
|
|
184
221
|
const fm = topicMatch.memory.frontmatter;
|
|
222
|
+
const { similarityWarning: simW, body_similar: bs } = bodySimilarWarnings(/* @__PURE__ */ new Set([fm.id]));
|
|
185
223
|
const newFrontmatter = {
|
|
186
224
|
...fm,
|
|
187
225
|
body: input.body,
|
|
@@ -198,13 +236,19 @@ async function memSave(input, ctx) {
|
|
|
198
236
|
serializeMemory({ frontmatter: newFrontmatter, body: input.body }),
|
|
199
237
|
"utf8"
|
|
200
238
|
);
|
|
239
|
+
const mergedTw = [
|
|
240
|
+
invalidPaths.length > 0 ? `Anchor path(s) not found in project: ${invalidPaths.join(", ")}. They will be marked stale by haive sync.` : null,
|
|
241
|
+
simW ?? null
|
|
242
|
+
].filter(Boolean).join(" \u2014 ") || void 0;
|
|
201
243
|
return {
|
|
202
244
|
id: fm.id,
|
|
203
245
|
scope: fm.scope,
|
|
204
246
|
file_path: topicMatch.filePath,
|
|
205
247
|
action: "updated",
|
|
206
248
|
revision_count: newFrontmatter.revision_count,
|
|
207
|
-
...
|
|
249
|
+
...mergedTw ? { warning: mergedTw } : {},
|
|
250
|
+
...bs ? { body_similar: bs } : {},
|
|
251
|
+
...invalidPaths.length > 0 ? { invalid_paths: invalidPaths } : {}
|
|
208
252
|
};
|
|
209
253
|
}
|
|
210
254
|
}
|
|
@@ -246,9 +290,11 @@ async function memSave(input, ctx) {
|
|
|
246
290
|
}
|
|
247
291
|
}
|
|
248
292
|
await writeFile2(file, serializeMemory({ frontmatter, body: input.body }), "utf8");
|
|
293
|
+
const { similarityWarning: simWarnNew, body_similar: bsNew } = bodySimilarWarnings();
|
|
249
294
|
const finalWarning = [
|
|
250
295
|
invalidPaths.length > 0 ? `Anchor path(s) not found in project: ${invalidPaths.join(", ")}. They will be marked stale by \`haive sync\`.` : null,
|
|
251
|
-
warning ?? null
|
|
296
|
+
warning ?? null,
|
|
297
|
+
simWarnNew ?? null
|
|
252
298
|
].filter(Boolean).join(" \u2014 ") || void 0;
|
|
253
299
|
return {
|
|
254
300
|
id: frontmatter.id,
|
|
@@ -257,6 +303,7 @@ async function memSave(input, ctx) {
|
|
|
257
303
|
action: "created",
|
|
258
304
|
...finalWarning ? { warning: finalWarning } : {},
|
|
259
305
|
...similar_found ? { similar_found } : {},
|
|
306
|
+
...bsNew ? { body_similar: bsNew } : {},
|
|
260
307
|
...invalidPaths.length > 0 ? { invalid_paths: invalidPaths } : {}
|
|
261
308
|
};
|
|
262
309
|
}
|
|
@@ -1292,6 +1339,7 @@ import {
|
|
|
1292
1339
|
DEFAULT_AUTO_PROMOTE_RULE,
|
|
1293
1340
|
deriveConfidence as deriveConfidence4,
|
|
1294
1341
|
estimateTokens,
|
|
1342
|
+
extractActionsBriefBody,
|
|
1295
1343
|
getUsage as getUsage5,
|
|
1296
1344
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
1297
1345
|
isAutoPromoteEligible,
|
|
@@ -1304,6 +1352,7 @@ import {
|
|
|
1304
1352
|
loadUsageIndex as loadUsageIndex7,
|
|
1305
1353
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
1306
1354
|
queryCodeMap,
|
|
1355
|
+
resolveBriefingBudget,
|
|
1307
1356
|
serializeMemory as serializeMemory9,
|
|
1308
1357
|
tokenizeQuery as tokenizeQuery2,
|
|
1309
1358
|
trackReads as trackReads3,
|
|
@@ -1326,17 +1375,29 @@ var GetBriefingInputSchema = {
|
|
|
1326
1375
|
),
|
|
1327
1376
|
include_stale: z17.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
1328
1377
|
track: z17.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
1329
|
-
format: z17.enum(["full", "compact"]).default("full").describe(
|
|
1330
|
-
"Output format: 'full' returns
|
|
1378
|
+
format: z17.enum(["full", "compact", "actions"]).default("full").describe(
|
|
1379
|
+
"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."
|
|
1331
1380
|
),
|
|
1332
1381
|
symbols: z17.array(z17.string()).default([]).describe(
|
|
1333
1382
|
"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."
|
|
1334
1383
|
),
|
|
1335
1384
|
min_semantic_score: z17.number().min(0).max(1).default(0).describe(
|
|
1336
1385
|
"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."
|
|
1386
|
+
),
|
|
1387
|
+
budget_preset: z17.enum(["quick", "balanced", "deep"]).optional().describe(
|
|
1388
|
+
"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."
|
|
1337
1389
|
)
|
|
1338
1390
|
};
|
|
1391
|
+
var GetBriefingZod = z17.object(GetBriefingInputSchema);
|
|
1339
1392
|
async function getBriefing(input, ctx) {
|
|
1393
|
+
const resolvedBudget = resolveBriefingBudget(input.budget_preset, {
|
|
1394
|
+
max_tokens: input.max_tokens,
|
|
1395
|
+
max_memories: input.max_memories,
|
|
1396
|
+
include_module_contexts: input.include_module_contexts
|
|
1397
|
+
});
|
|
1398
|
+
const briefingMaxTokens = resolvedBudget.max_tokens;
|
|
1399
|
+
const briefingMaxMemories = resolvedBudget.max_memories;
|
|
1400
|
+
const briefingIncludeModules = resolvedBudget.include_module_contexts;
|
|
1340
1401
|
const inferred = inferModulesFromPaths2(input.files);
|
|
1341
1402
|
const memories = [];
|
|
1342
1403
|
let searchMode = "literal";
|
|
@@ -1448,8 +1509,8 @@ async function getBriefing(input, ctx) {
|
|
|
1448
1509
|
const sb = reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
|
|
1449
1510
|
return sb - sa;
|
|
1450
1511
|
});
|
|
1451
|
-
for (const mem of ranked.slice(0,
|
|
1452
|
-
if (seen.size >=
|
|
1512
|
+
for (const mem of ranked.slice(0, briefingMaxMemories)) {
|
|
1513
|
+
if (seen.size >= briefingMaxMemories * 2) break;
|
|
1453
1514
|
const loaded = byId.get(mem.id);
|
|
1454
1515
|
if (!loaded) continue;
|
|
1455
1516
|
for (const relId of loaded.memory.frontmatter.related_ids ?? []) {
|
|
@@ -1458,7 +1519,7 @@ async function getBriefing(input, ctx) {
|
|
|
1458
1519
|
if (related) addOrUpdate(related, "anchor", void 0, "partial");
|
|
1459
1520
|
}
|
|
1460
1521
|
}
|
|
1461
|
-
memories.push(...ranked.slice(0,
|
|
1522
|
+
memories.push(...ranked.slice(0, briefingMaxMemories));
|
|
1462
1523
|
if (input.track && memories.length > 0) {
|
|
1463
1524
|
await trackReads3(ctx.paths, memories.map((m) => m.id));
|
|
1464
1525
|
const freshUsage = await loadUsageIndex7(ctx.paths);
|
|
@@ -1538,7 +1599,7 @@ async function getBriefing(input, ctx) {
|
|
|
1538
1599
|
}
|
|
1539
1600
|
}
|
|
1540
1601
|
}
|
|
1541
|
-
const moduleContents =
|
|
1602
|
+
const moduleContents = briefingIncludeModules ? await loadModuleContexts2(ctx, inferred) : [];
|
|
1542
1603
|
const memoriesText = memories.map((m) => {
|
|
1543
1604
|
const unverified = m.status === "proposed" ? " [UNVERIFIED \u2014 not yet validated]" : "";
|
|
1544
1605
|
return `### ${m.id} (${m.scope}/${m.type}, ${m.confidence})${unverified}
|
|
@@ -1556,7 +1617,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1556
1617
|
},
|
|
1557
1618
|
{ key: "memories", text: memoriesText, weight: 4, mode: "head" }
|
|
1558
1619
|
],
|
|
1559
|
-
|
|
1620
|
+
briefingMaxTokens
|
|
1560
1621
|
);
|
|
1561
1622
|
const projectSlice = slices.find((s) => s.key === "project");
|
|
1562
1623
|
const modulesSlice = slices.find((s) => s.key === "modules");
|
|
@@ -1598,7 +1659,10 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1598
1659
|
const createdAt = loaded?.memory.frontmatter.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1599
1660
|
if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
|
|
1600
1661
|
}
|
|
1601
|
-
const outputMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : trimmedMemories
|
|
1662
|
+
const outputMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : input.format === "actions" ? trimmedMemories.map((m) => ({
|
|
1663
|
+
...m,
|
|
1664
|
+
body: extractActionsBriefBody(m.body)
|
|
1665
|
+
})) : trimmedMemories;
|
|
1602
1666
|
let symbolLocations;
|
|
1603
1667
|
const symbolsToLookup = new Set(input.symbols);
|
|
1604
1668
|
for (const m of outputMemories) {
|
|
@@ -1720,6 +1784,11 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
1720
1784
|
"After completing the task: capture new gotchas with mem_observe, failed approaches with mem_tried, validated patterns with mem_save."
|
|
1721
1785
|
);
|
|
1722
1786
|
}
|
|
1787
|
+
if (outputMemories.length > 2 && !input.budget_preset && input.task && !hints.some((h) => h.includes("budget_preset"))) {
|
|
1788
|
+
hints.push(
|
|
1789
|
+
"For tighter token budgets on small tasks pass budget_preset:'quick'; for refactor-sized work use budget_preset:'deep'."
|
|
1790
|
+
);
|
|
1791
|
+
}
|
|
1723
1792
|
}
|
|
1724
1793
|
return {
|
|
1725
1794
|
...input.task ? { task: input.task } : {},
|
|
@@ -1742,7 +1811,8 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
1742
1811
|
...hints.length > 0 ? { hints } : {},
|
|
1743
1812
|
estimated_tokens: totalTokens,
|
|
1744
1813
|
budget: {
|
|
1745
|
-
max_tokens:
|
|
1814
|
+
max_tokens: briefingMaxTokens,
|
|
1815
|
+
...input.budget_preset ? { preset_applied: input.budget_preset } : {},
|
|
1746
1816
|
spent: {
|
|
1747
1817
|
project: projectSlice.estimatedTokens,
|
|
1748
1818
|
modules: modulesSlice.estimatedTokens,
|
|
@@ -1955,7 +2025,7 @@ var MemRelevantToInputSchema = {
|
|
|
1955
2025
|
files: z21.array(z21.string()).default([]).describe("Optional: files you are about to edit \u2014 surfaces anchored memories."),
|
|
1956
2026
|
limit: z21.number().int().positive().max(30).default(8).describe("Cap on returned memories."),
|
|
1957
2027
|
min_semantic_score: z21.number().min(0).max(1).default(0.25).describe("Drop weakly-related semantic hits below this cosine threshold."),
|
|
1958
|
-
format: z21.enum(["full", "compact"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies.")
|
|
2028
|
+
format: z21.enum(["full", "compact", "actions"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies; 'actions' = bullet-first excerpts.")
|
|
1959
2029
|
};
|
|
1960
2030
|
async function memRelevantTo(input, ctx) {
|
|
1961
2031
|
const briefingInput = {
|
|
@@ -3213,7 +3283,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
3213
3283
|
|
|
3214
3284
|
// src/server.ts
|
|
3215
3285
|
var SERVER_NAME = "haive";
|
|
3216
|
-
var SERVER_VERSION = "0.9.
|
|
3286
|
+
var SERVER_VERSION = "0.9.3";
|
|
3217
3287
|
function jsonResult(data) {
|
|
3218
3288
|
return {
|
|
3219
3289
|
content: [
|
|
@@ -3379,7 +3449,8 @@ function createHaiveServer(options = {}) {
|
|
|
3379
3449
|
" task \u2014 what you are about to do (1\u20132 sentences) \u2014 ALWAYS provide this",
|
|
3380
3450
|
" files \u2014 files you are about to edit \u2014 surfaces anchored memories",
|
|
3381
3451
|
" symbols \u2014 symbol names to look up in the code-map (e.g. ['PaymentService'])",
|
|
3382
|
-
" format \u2014 'full' (default) | 'compact' (1-line
|
|
3452
|
+
" format \u2014 'full' (default) | 'compact' (1-line) | 'actions' (bullet-first excerpts)",
|
|
3453
|
+
" budget_preset \u2014 'quick' | 'balanced' | 'deep' \u2014 scales max_tokens/memories/module contexts",
|
|
3383
3454
|
"",
|
|
3384
3455
|
"EXAMPLE USAGE:",
|
|
3385
3456
|
" get_briefing({ task: 'add a Stripe payment integration', files: ['src/payments/'], symbols: ['PaymentService'] })",
|
|
@@ -3685,6 +3756,7 @@ function createHaiveServer(options = {}) {
|
|
|
3685
3756
|
" files \u2014 files you'll edit (surfaces anchored memories)",
|
|
3686
3757
|
" limit \u2014 cap on returned memories (default 8)",
|
|
3687
3758
|
" min_semantic_score \u2014 drop weak semantic hits below this cosine (default 0.25)",
|
|
3759
|
+
" format \u2014 'full' | 'compact' | 'actions' (inherits get_briefing memory framing)",
|
|
3688
3760
|
"",
|
|
3689
3761
|
"RETURNS: { task, search_mode, memories: [...], hints?: [...], empty?: true }"
|
|
3690
3762
|
].join("\n"),
|