@hiveai/mcp 0.2.16 → 0.3.2
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 +44 -4
- package/dist/index.js +192 -24
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +13 -0
- package/dist/server.js +192 -24
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,6 +99,7 @@ One-shot onboarding: returns project context + module contexts + ranked relevant
|
|
|
99
99
|
{
|
|
100
100
|
"task": "add a Stripe payment integration",
|
|
101
101
|
"files": ["src/payments/PaymentService.ts"],
|
|
102
|
+
"symbols": ["PaymentService", "TenantFilter"],
|
|
102
103
|
"max_tokens": 8000,
|
|
103
104
|
"max_memories": 8,
|
|
104
105
|
"format": "full",
|
|
@@ -112,6 +113,7 @@ One-shot onboarding: returns project context + module contexts + ranked relevant
|
|
|
112
113
|
|---|---|---|
|
|
113
114
|
| `task` | — | What you're about to do. Used to rank memories by relevance. |
|
|
114
115
|
| `files` | `[]` | Files you're editing. Surfaces memories anchored to these files. |
|
|
116
|
+
| `symbols` | `[]` | Symbol names to look up in the code-map (e.g. `["PaymentService"]`). Returns file + line + kind without grepping. Requires `haive index code`. |
|
|
115
117
|
| `max_tokens` | `8000` | Token budget for the entire response. Sections are truncated to fit. |
|
|
116
118
|
| `max_memories` | `8` | Max memories to include. |
|
|
117
119
|
| `format` | `"full"` | `"full"` = complete bodies · `"compact"` = 1-line summaries (call `mem_get` for details) |
|
|
@@ -120,10 +122,13 @@ One-shot onboarding: returns project context + module contexts + ranked relevant
|
|
|
120
122
|
| `track` | `true` | Increment read_count for returned memories. |
|
|
121
123
|
|
|
122
124
|
**Response includes:**
|
|
123
|
-
- `
|
|
125
|
+
- `last_session` — most recent `haive session end` recap (surfaced first so agents start with fresh context)
|
|
126
|
+
- `project_context` — `.ai/project-context.md` (suppressed if still template — `is_template: true`)
|
|
124
127
|
- `module_contexts` — relevant `.ai/modules/<name>/context.md` files
|
|
125
|
-
- `memories` — ranked
|
|
126
|
-
- `
|
|
128
|
+
- `memories` — ranked memories with `confidence`, `unverified` flag (for draft/proposed), and `match reason`
|
|
129
|
+
- `symbol_locations` — file:line:kind results for each requested symbol (from code-map)
|
|
130
|
+
- `decay_warnings` — memory IDs not read in >90 days
|
|
131
|
+
- `setup_warnings` — actionable warnings (e.g. template project-context, missing init)
|
|
127
132
|
- `search_mode` — `"semantic"` | `"literal_fallback"` | `"literal"`
|
|
128
133
|
|
|
129
134
|
---
|
|
@@ -149,12 +154,15 @@ Save a new memory. For failed approaches, use `mem_tried` instead — it enforce
|
|
|
149
154
|
| `slug` | ✅ | Short kebab-case identifier |
|
|
150
155
|
| `scope` | — | `personal` (default) · `team` · `module` |
|
|
151
156
|
| `body` | ✅ | Markdown content |
|
|
152
|
-
| `paths` | — | File paths to anchor to (enables staleness detection) |
|
|
157
|
+
| `paths` | — | File paths to anchor to (enables staleness detection). **Warning returned if path doesn't exist in project.** |
|
|
153
158
|
| `symbols` | — | Function/class names to anchor to |
|
|
154
159
|
| `tags` | — | Tags for filtering |
|
|
160
|
+
| `topic` | — | Stable key for upsert: if a memory with this `topic`+`scope` already exists, it is updated in-place (`revision_count++`) |
|
|
155
161
|
| `domain` | — | Business domain (e.g. `payments`) |
|
|
156
162
|
| `author` | — | Author handle |
|
|
157
163
|
|
|
164
|
+
**Deduplication:** identical body content (same SHA-256 hash) within the same scope is rejected with an error. Use `mem_update` to modify it instead.
|
|
165
|
+
|
|
158
166
|
---
|
|
159
167
|
|
|
160
168
|
### `mem_tried` ⭐ Record failures immediately
|
|
@@ -263,6 +271,38 @@ Compare two memories side-by-side: shows frontmatter fields that differ and line
|
|
|
263
271
|
|
|
264
272
|
---
|
|
265
273
|
|
|
274
|
+
### `mem_session_end`
|
|
275
|
+
|
|
276
|
+
Save a structured end-of-session recap. Topic-upsert: one recap per scope is kept and updated with `revision_count++`. Automatically surfaced at the top of the next `get_briefing`.
|
|
277
|
+
|
|
278
|
+
```json
|
|
279
|
+
{
|
|
280
|
+
"goal": "Add Stripe payment integration",
|
|
281
|
+
"accomplished": "PaymentService done, tests passing, deployed to staging",
|
|
282
|
+
"discoveries": "Webhook signature requires raw body, not parsed JSON",
|
|
283
|
+
"files_touched": ["src/payments/PaymentService.ts", "src/payments/webhook.ts"],
|
|
284
|
+
"next_steps": "Add retry logic for failed webhooks",
|
|
285
|
+
"scope": "team"
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
### `mem_observe`
|
|
292
|
+
|
|
293
|
+
Capture a code-level discovery in structured form (found-while, not a convention or decision).
|
|
294
|
+
|
|
295
|
+
```json
|
|
296
|
+
{
|
|
297
|
+
"file": "src/payments/PaymentService.ts",
|
|
298
|
+
"symbol": "processPayment",
|
|
299
|
+
"observation": "This method calls the external API synchronously — any timeout blocks the entire request thread.",
|
|
300
|
+
"scope": "team"
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
266
306
|
### `mem_approve` / `mem_reject` / `mem_pending` / `mem_delete`
|
|
267
307
|
|
|
268
308
|
Lifecycle operations:
|
package/dist/index.js
CHANGED
|
@@ -130,6 +130,7 @@ import { existsSync as existsSync4 } from "fs";
|
|
|
130
130
|
import path3 from "path";
|
|
131
131
|
import {
|
|
132
132
|
buildFrontmatter,
|
|
133
|
+
loadConfig,
|
|
133
134
|
loadMemoriesFromDir as loadMemoriesFromDir2,
|
|
134
135
|
memoryFilePath,
|
|
135
136
|
serializeMemory
|
|
@@ -207,10 +208,12 @@ async function memSave(input, ctx) {
|
|
|
207
208
|
};
|
|
208
209
|
}
|
|
209
210
|
}
|
|
211
|
+
const haiveConfig = await loadConfig(ctx.paths);
|
|
212
|
+
const resolvedScope = input.scope !== "personal" ? input.scope : haiveConfig.defaultScope ?? "personal";
|
|
210
213
|
const frontmatter = buildFrontmatter({
|
|
211
214
|
type: input.type,
|
|
212
215
|
slug: input.slug,
|
|
213
|
-
scope:
|
|
216
|
+
scope: resolvedScope,
|
|
214
217
|
module: input.module,
|
|
215
218
|
tags: input.tags,
|
|
216
219
|
domain: input.domain,
|
|
@@ -218,7 +221,8 @@ async function memSave(input, ctx) {
|
|
|
218
221
|
paths: input.paths,
|
|
219
222
|
symbols: input.symbols,
|
|
220
223
|
commit: input.commit,
|
|
221
|
-
topic: input.topic
|
|
224
|
+
topic: input.topic,
|
|
225
|
+
status: haiveConfig.defaultStatus === "validated" ? "validated" : void 0
|
|
222
226
|
});
|
|
223
227
|
const file = memoryFilePath(
|
|
224
228
|
ctx.paths,
|
|
@@ -1164,9 +1168,12 @@ import {
|
|
|
1164
1168
|
isDecaying,
|
|
1165
1169
|
literalMatchesAllTokens as literalMatchesAllTokens2,
|
|
1166
1170
|
literalMatchesAnyToken as literalMatchesAnyToken2,
|
|
1171
|
+
loadCodeMap,
|
|
1172
|
+
loadConfig as loadConfig2,
|
|
1167
1173
|
loadMemoriesFromDir as loadMemoriesFromDir13,
|
|
1168
1174
|
loadUsageIndex as loadUsageIndex7,
|
|
1169
1175
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
1176
|
+
queryCodeMap,
|
|
1170
1177
|
tokenizeQuery as tokenizeQuery2,
|
|
1171
1178
|
trackReads as trackReads3,
|
|
1172
1179
|
truncateToTokens
|
|
@@ -1190,6 +1197,9 @@ var GetBriefingInputSchema = {
|
|
|
1190
1197
|
track: z17.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
1191
1198
|
format: z17.enum(["full", "compact"]).default("full").describe(
|
|
1192
1199
|
"Output format: 'full' returns complete memory bodies; 'compact' returns id + 1-line summary only (call mem_get for details)."
|
|
1200
|
+
),
|
|
1201
|
+
symbols: z17.array(z17.string()).default([]).describe(
|
|
1202
|
+
"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."
|
|
1193
1203
|
)
|
|
1194
1204
|
};
|
|
1195
1205
|
async function getBriefing(input, ctx) {
|
|
@@ -1251,6 +1261,7 @@ async function getBriefing(input, ctx) {
|
|
|
1251
1261
|
tags: fm.tags,
|
|
1252
1262
|
status: fm.status,
|
|
1253
1263
|
confidence: deriveConfidence4(fm, u),
|
|
1264
|
+
...fm.status === "draft" || fm.status === "proposed" ? { unverified: true } : {},
|
|
1254
1265
|
read_count: u.read_count,
|
|
1255
1266
|
reasons: [reason],
|
|
1256
1267
|
match_quality: matchQuality ?? "partial",
|
|
@@ -1316,17 +1327,55 @@ async function getBriefing(input, ctx) {
|
|
|
1316
1327
|
}
|
|
1317
1328
|
const projectContextRaw = input.include_project_context && existsSync17(ctx.paths.projectContext) ? await readFile3(ctx.paths.projectContext, "utf8") : "";
|
|
1318
1329
|
const isTemplateContext = projectContextRaw.includes("TODO \u2014 high-level overview") || projectContextRaw.includes("Generated by `haive init`");
|
|
1319
|
-
const projectContext = isTemplateContext ? "" : projectContextRaw;
|
|
1320
1330
|
const setupWarnings = [];
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
);
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1331
|
+
let autoContextGenerated = false;
|
|
1332
|
+
let projectContext = isTemplateContext ? "" : projectContextRaw;
|
|
1333
|
+
if ((isTemplateContext || !existsSync17(ctx.paths.projectContext)) && input.include_project_context) {
|
|
1334
|
+
const haiveConfig = await loadConfig2(ctx.paths);
|
|
1335
|
+
if (haiveConfig.autoContext) {
|
|
1336
|
+
const codeMap = await loadCodeMap(ctx.paths);
|
|
1337
|
+
if (codeMap) {
|
|
1338
|
+
const totalFiles = Object.keys(codeMap.files).length;
|
|
1339
|
+
const extensions = /* @__PURE__ */ new Map();
|
|
1340
|
+
for (const filePath of Object.keys(codeMap.files)) {
|
|
1341
|
+
const ext = filePath.slice(filePath.lastIndexOf(".") + 1).toLowerCase();
|
|
1342
|
+
extensions.set(ext, (extensions.get(ext) ?? 0) + 1);
|
|
1343
|
+
}
|
|
1344
|
+
const topExts = [...extensions.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([e, n]) => `${e} (${n})`).join(", ");
|
|
1345
|
+
const topSymbols = Object.entries(codeMap.files).flatMap(
|
|
1346
|
+
([fp, entry]) => entry.exports.slice(0, 3).map((e) => `${e.name} (${fp.split("/").slice(-2).join("/")})`)
|
|
1347
|
+
).slice(0, 15).join(", ");
|
|
1348
|
+
projectContext = `# Project context (auto-generated by hAIve)
|
|
1349
|
+
|
|
1350
|
+
> \u26A0 This is a minimal auto-generated context based on the code-map. Invoke the \`bootstrap_project\` MCP prompt to replace it with a full analysis.
|
|
1351
|
+
|
|
1352
|
+
## Codebase overview
|
|
1353
|
+
- **${totalFiles} files** indexed in code-map
|
|
1354
|
+
- **Main file types:** ${topExts}
|
|
1355
|
+
- **Generated at:** ${codeMap.generated_at}
|
|
1356
|
+
|
|
1357
|
+
## Key exports (sample)
|
|
1358
|
+
` + topSymbols + "\n";
|
|
1359
|
+
autoContextGenerated = true;
|
|
1360
|
+
setupWarnings.push(
|
|
1361
|
+
"project-context.md is still the default template. A minimal auto-generated context has been injected from the code-map. Invoke bootstrap_project to replace it with a full AI-analyzed context."
|
|
1362
|
+
);
|
|
1363
|
+
} else {
|
|
1364
|
+
setupWarnings.push(
|
|
1365
|
+
"project-context.md is still the default template and no code-map found. Run `haive index code` then invoke bootstrap_project for a full context."
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1368
|
+
} else {
|
|
1369
|
+
if (isTemplateContext) {
|
|
1370
|
+
setupWarnings.push(
|
|
1371
|
+
"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."
|
|
1372
|
+
);
|
|
1373
|
+
} else {
|
|
1374
|
+
setupWarnings.push(
|
|
1375
|
+
"No project-context.md found. Run `haive init` then invoke the bootstrap_project MCP prompt."
|
|
1376
|
+
);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1330
1379
|
}
|
|
1331
1380
|
const moduleContents = input.include_module_contexts ? await loadModuleContexts2(ctx, inferred) : [];
|
|
1332
1381
|
const memoriesText = memories.map((m) => {
|
|
@@ -1389,18 +1438,51 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1389
1438
|
if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
|
|
1390
1439
|
}
|
|
1391
1440
|
const outputMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : trimmedMemories;
|
|
1441
|
+
let symbolLocations;
|
|
1442
|
+
const symbolsToLookup = new Set(input.symbols);
|
|
1443
|
+
for (const m of outputMemories) {
|
|
1444
|
+
const loaded = byId.get(m.id);
|
|
1445
|
+
for (const sym of loaded?.memory.frontmatter.anchor.symbols ?? []) {
|
|
1446
|
+
symbolsToLookup.add(sym);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
if (symbolsToLookup.size > 0) {
|
|
1450
|
+
const codeMap = await loadCodeMap(ctx.paths);
|
|
1451
|
+
if (codeMap) {
|
|
1452
|
+
symbolLocations = [];
|
|
1453
|
+
for (const sym of symbolsToLookup) {
|
|
1454
|
+
const { files } = queryCodeMap(codeMap, { symbol: sym });
|
|
1455
|
+
if (files.length > 0) {
|
|
1456
|
+
symbolLocations.push({
|
|
1457
|
+
symbol: sym,
|
|
1458
|
+
locations: files.flatMap(
|
|
1459
|
+
(f) => f.entry.exports.filter((e) => e.name.toLowerCase().includes(sym.toLowerCase())).map((e) => ({
|
|
1460
|
+
file: f.path,
|
|
1461
|
+
kind: e.kind,
|
|
1462
|
+
line: e.line,
|
|
1463
|
+
...e.description ? { description: e.description } : {}
|
|
1464
|
+
}))
|
|
1465
|
+
)
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
if (symbolLocations.length === 0) symbolLocations = void 0;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1392
1472
|
return {
|
|
1393
1473
|
...input.task ? { task: input.task } : {},
|
|
1394
1474
|
search_mode: searchMode,
|
|
1395
1475
|
inferred_modules: inferred,
|
|
1396
1476
|
...lastSession ? { last_session: lastSession } : {},
|
|
1397
|
-
project_context: projectContextRaw ? {
|
|
1477
|
+
project_context: projectContextRaw || autoContextGenerated ? {
|
|
1398
1478
|
content: projectSlice.text,
|
|
1399
1479
|
truncated: projectSlice.truncated,
|
|
1400
|
-
...isTemplateContext ? { is_template: true } : {}
|
|
1480
|
+
...isTemplateContext && !autoContextGenerated ? { is_template: true } : {},
|
|
1481
|
+
...autoContextGenerated ? { auto_generated: true } : {}
|
|
1401
1482
|
} : null,
|
|
1402
1483
|
module_contexts: trimmedModules,
|
|
1403
1484
|
memories: outputMemories,
|
|
1485
|
+
...symbolLocations ? { symbol_locations: symbolLocations } : {},
|
|
1404
1486
|
decay_warnings: decayWarnings,
|
|
1405
1487
|
setup_warnings: setupWarnings,
|
|
1406
1488
|
estimated_tokens: totalTokens,
|
|
@@ -1450,7 +1532,7 @@ async function loadModuleContexts2(ctx, modules) {
|
|
|
1450
1532
|
}
|
|
1451
1533
|
|
|
1452
1534
|
// src/tools/code-map.ts
|
|
1453
|
-
import { loadCodeMap, queryCodeMap } from "@hiveai/core";
|
|
1535
|
+
import { loadCodeMap as loadCodeMap2, queryCodeMap as queryCodeMap2 } from "@hiveai/core";
|
|
1454
1536
|
import { z as z18 } from "zod";
|
|
1455
1537
|
var CodeMapInputSchema = {
|
|
1456
1538
|
file: z18.string().optional().describe("Filter to files whose path contains this substring"),
|
|
@@ -1458,7 +1540,7 @@ var CodeMapInputSchema = {
|
|
|
1458
1540
|
max_files: z18.number().int().positive().default(40).describe("Cap on returned files")
|
|
1459
1541
|
};
|
|
1460
1542
|
async function codeMapTool(input, ctx) {
|
|
1461
|
-
const map = await
|
|
1543
|
+
const map = await loadCodeMap2(ctx.paths);
|
|
1462
1544
|
if (!map) {
|
|
1463
1545
|
return {
|
|
1464
1546
|
available: false,
|
|
@@ -1466,7 +1548,7 @@ async function codeMapTool(input, ctx) {
|
|
|
1466
1548
|
notice: "No code map found. Run `haive index code` to generate `.ai/code-map.json`."
|
|
1467
1549
|
};
|
|
1468
1550
|
}
|
|
1469
|
-
const { files } =
|
|
1551
|
+
const { files } = queryCodeMap2(map, { file: input.file, symbol: input.symbol });
|
|
1470
1552
|
return {
|
|
1471
1553
|
available: true,
|
|
1472
1554
|
generated_at: map.generated_at,
|
|
@@ -1766,9 +1848,78 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
1766
1848
|
};
|
|
1767
1849
|
}
|
|
1768
1850
|
|
|
1851
|
+
// src/session-tracker.ts
|
|
1852
|
+
import { loadConfig as loadConfig3 } from "@hiveai/core";
|
|
1853
|
+
var SessionTracker = class {
|
|
1854
|
+
events = [];
|
|
1855
|
+
startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1856
|
+
config = null;
|
|
1857
|
+
ctx;
|
|
1858
|
+
shutdownRegistered = false;
|
|
1859
|
+
constructor(ctx) {
|
|
1860
|
+
this.ctx = ctx;
|
|
1861
|
+
}
|
|
1862
|
+
async init() {
|
|
1863
|
+
this.config = await loadConfig3(this.ctx.paths);
|
|
1864
|
+
if (this.config.autoSessionEnd) {
|
|
1865
|
+
this.registerShutdownHandler();
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
record(tool, summary) {
|
|
1869
|
+
this.events.push({ tool, at: (/* @__PURE__ */ new Date()).toISOString(), summary });
|
|
1870
|
+
}
|
|
1871
|
+
registerShutdownHandler() {
|
|
1872
|
+
if (this.shutdownRegistered) return;
|
|
1873
|
+
this.shutdownRegistered = true;
|
|
1874
|
+
const save = async () => {
|
|
1875
|
+
const writingTools = this.events.filter(
|
|
1876
|
+
(e) => ["mem_save", "mem_tried", "mem_observe", "mem_update", "bootstrap_project_save"].includes(e.tool)
|
|
1877
|
+
);
|
|
1878
|
+
const totalCalls = this.events.length;
|
|
1879
|
+
if (totalCalls === 0) return;
|
|
1880
|
+
const toolSummary = summarizeTools(this.events);
|
|
1881
|
+
const filesSet = /* @__PURE__ */ new Set();
|
|
1882
|
+
for (const e of this.events) {
|
|
1883
|
+
if (e.summary) {
|
|
1884
|
+
const matches = e.summary.match(/[^\s"',]+\.[a-zA-Z]{1,6}/g) ?? [];
|
|
1885
|
+
for (const m of matches) filesSet.add(m);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
try {
|
|
1889
|
+
await memSessionEnd(
|
|
1890
|
+
{
|
|
1891
|
+
goal: `Auto-captured session (${totalCalls} tool call${totalCalls === 1 ? "" : "s"})`,
|
|
1892
|
+
accomplished: toolSummary,
|
|
1893
|
+
discoveries: writingTools.length > 0 ? `${writingTools.length} memor${writingTools.length === 1 ? "y" : "ies"} saved during this session.` : "No new memories saved this session.",
|
|
1894
|
+
files_touched: [...filesSet].slice(0, 10),
|
|
1895
|
+
next_steps: "",
|
|
1896
|
+
scope: this.config?.defaultScope ?? "personal",
|
|
1897
|
+
module: void 0
|
|
1898
|
+
},
|
|
1899
|
+
this.ctx
|
|
1900
|
+
);
|
|
1901
|
+
} catch {
|
|
1902
|
+
}
|
|
1903
|
+
};
|
|
1904
|
+
process.once("SIGTERM", () => {
|
|
1905
|
+
void save().finally(() => process.exit(0));
|
|
1906
|
+
});
|
|
1907
|
+
process.once("SIGINT", () => {
|
|
1908
|
+
void save().finally(() => process.exit(0));
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1911
|
+
};
|
|
1912
|
+
function summarizeTools(events) {
|
|
1913
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1914
|
+
for (const e of events) {
|
|
1915
|
+
counts.set(e.tool, (counts.get(e.tool) ?? 0) + 1);
|
|
1916
|
+
}
|
|
1917
|
+
return [...counts.entries()].sort((a, b) => b[1] - a[1]).map(([t, n]) => `${t} \xD7${n}`).join(", ");
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1769
1920
|
// src/server.ts
|
|
1770
1921
|
var SERVER_NAME = "haive";
|
|
1771
|
-
var SERVER_VERSION = "0.2
|
|
1922
|
+
var SERVER_VERSION = "0.3.2";
|
|
1772
1923
|
function jsonResult(data) {
|
|
1773
1924
|
return {
|
|
1774
1925
|
content: [
|
|
@@ -1781,6 +1932,8 @@ function jsonResult(data) {
|
|
|
1781
1932
|
}
|
|
1782
1933
|
function createHaiveServer(options = {}) {
|
|
1783
1934
|
const context = createContext(options);
|
|
1935
|
+
const tracker = new SessionTracker(context);
|
|
1936
|
+
void tracker.init();
|
|
1784
1937
|
const server = new McpServer(
|
|
1785
1938
|
{ name: SERVER_NAME, version: SERVER_VERSION },
|
|
1786
1939
|
{ capabilities: { tools: {}, prompts: {} } }
|
|
@@ -1789,7 +1942,10 @@ function createHaiveServer(options = {}) {
|
|
|
1789
1942
|
"mem_save",
|
|
1790
1943
|
"Save a new memory (convention, decision, gotcha, architecture, glossary). For failed approaches use mem_tried instead \u2014 it enforces a structured format that is more useful to future agents. Use scope=team to share with the whole team.",
|
|
1791
1944
|
MemSaveInputSchema,
|
|
1792
|
-
async (input) =>
|
|
1945
|
+
async (input) => {
|
|
1946
|
+
tracker.record("mem_save", input.slug);
|
|
1947
|
+
return jsonResult(await memSave(input, context));
|
|
1948
|
+
}
|
|
1793
1949
|
);
|
|
1794
1950
|
server.tool(
|
|
1795
1951
|
"mem_search",
|
|
@@ -1813,7 +1969,10 @@ function createHaiveServer(options = {}) {
|
|
|
1813
1969
|
"get_briefing",
|
|
1814
1970
|
"One-shot onboarding: returns project context + module contexts + ranked relevant memories under a token budget. Replaces 4\u20135 separate calls when an agent starts a task.",
|
|
1815
1971
|
GetBriefingInputSchema,
|
|
1816
|
-
async (input) =>
|
|
1972
|
+
async (input) => {
|
|
1973
|
+
tracker.record("get_briefing", input.task ?? "");
|
|
1974
|
+
return jsonResult(await getBriefing(input, context));
|
|
1975
|
+
}
|
|
1817
1976
|
);
|
|
1818
1977
|
server.tool(
|
|
1819
1978
|
"code_map",
|
|
@@ -1879,7 +2038,10 @@ function createHaiveServer(options = {}) {
|
|
|
1879
2038
|
"mem_tried",
|
|
1880
2039
|
"Preferred way to record a failed approach. Enforces a structured what/why_failed/instead format that is immediately actionable for future agents. Auto-validated (no approval cycle). Use whenever you tried an approach and it failed \u2014 prevents the same mistake from happening in the next session.",
|
|
1881
2040
|
MemTriedInputSchema,
|
|
1882
|
-
async (input) =>
|
|
2041
|
+
async (input) => {
|
|
2042
|
+
tracker.record("mem_tried", input.what.slice(0, 80));
|
|
2043
|
+
return jsonResult(await memTried(input, context));
|
|
2044
|
+
}
|
|
1883
2045
|
);
|
|
1884
2046
|
server.tool(
|
|
1885
2047
|
"mem_diff",
|
|
@@ -1891,13 +2053,19 @@ function createHaiveServer(options = {}) {
|
|
|
1891
2053
|
"mem_observe",
|
|
1892
2054
|
"Capture a code-level discovery made during exploration: a bug, inconsistency, missing config, or security gap found by reading existing code that was NOT in the briefing. Use this whenever you read code and spot something that could silently break in production. Auto-validated, anchored to file paths for staleness detection. Prefer this over mem_save for reactive discoveries during code reading.",
|
|
1893
2055
|
MemObserveInputSchema,
|
|
1894
|
-
async (input) =>
|
|
2056
|
+
async (input) => {
|
|
2057
|
+
tracker.record("mem_observe", input.where);
|
|
2058
|
+
return jsonResult(await memObserve(input, context));
|
|
2059
|
+
}
|
|
1895
2060
|
);
|
|
1896
2061
|
server.tool(
|
|
1897
2062
|
"mem_session_end",
|
|
1898
2063
|
"Save a structured end-of-session recap (goal / accomplished / discoveries / next steps). Uses topic-upsert: one recap per scope is kept and updated in-place so the next session always has fresh context. Call this before closing every significant session. get_briefing automatically surfaces the latest recap at the top of the next session's briefing.",
|
|
1899
2064
|
MemSessionEndInputSchema,
|
|
1900
|
-
async (input) =>
|
|
2065
|
+
async (input) => {
|
|
2066
|
+
tracker.record("mem_session_end", input.goal.slice(0, 80));
|
|
2067
|
+
return jsonResult(await memSessionEnd(input, context));
|
|
2068
|
+
}
|
|
1901
2069
|
);
|
|
1902
2070
|
server.prompt(
|
|
1903
2071
|
"bootstrap_project",
|
|
@@ -1917,7 +2085,7 @@ function createHaiveServer(options = {}) {
|
|
|
1917
2085
|
ImportDocsArgsSchema,
|
|
1918
2086
|
(args) => importDocsPrompt(args, context)
|
|
1919
2087
|
);
|
|
1920
|
-
return { server, context };
|
|
2088
|
+
return { server, context, tracker };
|
|
1921
2089
|
}
|
|
1922
2090
|
|
|
1923
2091
|
// src/index.ts
|