@agentmemory/agentmemory 0.8.11 → 0.8.12
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/AGENTS.md +2 -2
- package/README.md +13 -9
- package/dist/cli.mjs +4 -4
- package/dist/index.mjs +296 -9
- package/dist/index.mjs.map +1 -1
- package/dist/{src-M6V9yZW5.mjs → src-68MXysnV.mjs} +277 -10
- package/dist/src-68MXysnV.mjs.map +1 -0
- package/dist/{standalone-XB9gPYmo.mjs → standalone-c2xiEQJ9.mjs} +5 -4
- package/dist/{standalone-XB9gPYmo.mjs.map → standalone-c2xiEQJ9.mjs.map} +1 -1
- package/dist/standalone.mjs +24 -3
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-DmTkd0if.mjs → tools-registry-D96ukJg4.mjs} +22 -2
- package/dist/tools-registry-D96ukJg4.mjs.map +1 -0
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/dist/src-M6V9yZW5.mjs.map +0 -1
- package/dist/tools-registry-DmTkd0if.mjs.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -111,8 +111,8 @@ Hook scripts in `src/hooks/` are standalone Node.js scripts (no iii-sdk import).
|
|
|
111
111
|
|
|
112
112
|
## Current Stats (v0.8.9)
|
|
113
113
|
|
|
114
|
-
-
|
|
115
|
-
-
|
|
114
|
+
- 44 MCP tools (8 visible by default, `AGENTMEMORY_TOOLS=all` for all)
|
|
115
|
+
- 104 REST endpoints
|
|
116
116
|
- 6 MCP resources, 3 MCP prompts
|
|
117
117
|
- 12 hooks, 4 skills
|
|
118
118
|
- 50+ iii functions
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
</p>
|
|
9
9
|
|
|
10
10
|
<p align="center">
|
|
11
|
-
<a href="https://gist.github.com/rohitg00/2067ab416f7bbe447c1977edaaa681e2"><img src="https://img.shields.io/badge/Viral%20GitHub%20Gist-
|
|
11
|
+
<a href="https://gist.github.com/rohitg00/2067ab416f7bbe447c1977edaaa681e2"><img src="https://img.shields.io/badge/Viral%20GitHub%20Gist-719%20stars%20%2F%2097%20forks-FF6B35?style=for-the-badge&logo=github&logoColor=white&labelColor=1a1a1a" alt="Design doc: 719 stars / 97 forks on the gist" /></a>
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
14
|
<p align="center">
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<p align="center">
|
|
26
26
|
<picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/stat-recall.svg"><img src="assets/tags/stat-recall.svg" alt="95.2% retrieval R@5" height="38" /></picture>
|
|
27
27
|
<picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/stat-tokens.svg"><img src="assets/tags/stat-tokens.svg" alt="92% fewer tokens" height="38" /></picture>
|
|
28
|
-
<picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/stat-tools.svg"><img src="assets/tags/stat-tools.svg" alt="
|
|
28
|
+
<picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/stat-tools.svg"><img src="assets/tags/stat-tools.svg" alt="44 MCP tools" height="38" /></picture>
|
|
29
29
|
<picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/stat-hooks.svg"><img src="assets/tags/stat-hooks.svg" alt="12 auto hooks" height="38" /></picture>
|
|
30
30
|
<picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/stat-deps.svg"><img src="assets/tags/stat-deps.svg" alt="0 external DBs" height="38" /></picture>
|
|
31
31
|
<picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/stat-tests.svg"><img src="assets/tags/stat-tests.svg" alt="654 tests passing" height="38" /></picture>
|
|
@@ -133,7 +133,7 @@ agentmemory works with any agent that supports hooks, MCP, or REST API. All agen
|
|
|
133
133
|
<sub>AgentSDKProvider</sub>
|
|
134
134
|
</td>
|
|
135
135
|
<td align="center" width="12.5%">
|
|
136
|
-
<img src="https://img.shields.io/badge/
|
|
136
|
+
<img src="https://img.shields.io/badge/104-endpoints-1f6feb?style=flat-square" alt="REST API" width="48" /><br/>
|
|
137
137
|
<strong>Any agent</strong><br/>
|
|
138
138
|
<sub>REST API</sub>
|
|
139
139
|
</td>
|
|
@@ -317,7 +317,7 @@ Implementation details live in `src/cli.ts` (see `runUpgrade` around the `src/cl
|
|
|
317
317
|
### Claude Code (one block, paste it)
|
|
318
318
|
|
|
319
319
|
```
|
|
320
|
-
Install agentmemory: run `npx @agentmemory/agentmemory` in a separate terminal to start the memory server. Then run `/plugin marketplace add rohitg00/agentmemory` and `/plugin install agentmemory` — the plugin registers all 12 hooks, 4 skills, AND auto-wires the `@agentmemory/mcp` stdio server via its `.mcp.json`, so you get
|
|
320
|
+
Install agentmemory: run `npx @agentmemory/agentmemory` in a separate terminal to start the memory server. Then run `/plugin marketplace add rohitg00/agentmemory` and `/plugin install agentmemory` — the plugin registers all 12 hooks, 4 skills, AND auto-wires the `@agentmemory/mcp` stdio server via its `.mcp.json`, so you get 44 MCP tools (memory_smart_search, memory_save, memory_sessions, memory_governance_delete, etc.) without any extra config step. Verify with `curl http://localhost:3111/agentmemory/health`. The real-time viewer is at http://localhost:3113.
|
|
321
321
|
```
|
|
322
322
|
|
|
323
323
|
<details>
|
|
@@ -576,9 +576,9 @@ npm install @xenova/transformers
|
|
|
576
576
|
|
|
577
577
|
<h2 id="mcp-server"><picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/section-mcp.svg"><img src="assets/tags/section-mcp.svg" alt="MCP Server" height="32" /></picture></h2>
|
|
578
578
|
|
|
579
|
-
|
|
579
|
+
44 tools, 6 resources, 3 prompts, and 4 skills — the most comprehensive MCP memory toolkit for any agent.
|
|
580
580
|
|
|
581
|
-
###
|
|
581
|
+
### 44 Tools
|
|
582
582
|
|
|
583
583
|
<details>
|
|
584
584
|
<summary>Core tools (always available)</summary>
|
|
@@ -586,17 +586,21 @@ npm install @xenova/transformers
|
|
|
586
586
|
| Tool | Description |
|
|
587
587
|
|------|-------------|
|
|
588
588
|
| `memory_recall` | Search past observations |
|
|
589
|
+
| `memory_compress_file` | Compress markdown files while preserving structure |
|
|
589
590
|
| `memory_save` | Save an insight, decision, or pattern |
|
|
591
|
+
| `memory_patterns` | Detect recurring patterns |
|
|
590
592
|
| `memory_smart_search` | Hybrid semantic + keyword search |
|
|
591
593
|
| `memory_file_history` | Past observations about specific files |
|
|
592
594
|
| `memory_sessions` | List recent sessions |
|
|
595
|
+
| `memory_timeline` | Chronological observations |
|
|
593
596
|
| `memory_profile` | Project profile (concepts, files, patterns) |
|
|
594
597
|
| `memory_export` | Export all memory data |
|
|
598
|
+
| `memory_relations` | Query relationship graph |
|
|
595
599
|
|
|
596
600
|
</details>
|
|
597
601
|
|
|
598
602
|
<details>
|
|
599
|
-
<summary>Extended tools (
|
|
603
|
+
<summary>Extended tools (44 total — set AGENTMEMORY_TOOLS=all)</summary>
|
|
600
604
|
|
|
601
605
|
| Tool | Description |
|
|
602
606
|
|------|-------------|
|
|
@@ -772,7 +776,7 @@ Create `~/.agentmemory/.env`:
|
|
|
772
776
|
# USER_ID=
|
|
773
777
|
# TEAM_MODE=private
|
|
774
778
|
|
|
775
|
-
# Tool visibility: "core" (
|
|
779
|
+
# Tool visibility: "core" (8 tools) or "all" (44 tools)
|
|
776
780
|
# AGENTMEMORY_TOOLS=core
|
|
777
781
|
```
|
|
778
782
|
|
|
@@ -780,7 +784,7 @@ Create `~/.agentmemory/.env`:
|
|
|
780
784
|
|
|
781
785
|
<h2 id="api"><picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/section-api.svg"><img src="assets/tags/section-api.svg" alt="API" height="32" /></picture></h2>
|
|
782
786
|
|
|
783
|
-
|
|
787
|
+
104 endpoints on port `3111`. The REST API binds to `127.0.0.1` by default. Protected endpoints require `Authorization: Bearer <secret>` when `AGENTMEMORY_SECRET` is set, and mesh sync endpoints require `AGENTMEMORY_SECRET` on both peers.
|
|
784
788
|
|
|
785
789
|
<details>
|
|
786
790
|
<summary>Key endpoints</summary>
|
package/dist/cli.mjs
CHANGED
|
@@ -286,12 +286,12 @@ async function main() {
|
|
|
286
286
|
p.intro("agentmemory");
|
|
287
287
|
if (skipEngine) {
|
|
288
288
|
p.log.info("Skipping engine check (--no-engine)");
|
|
289
|
-
await import("./src-
|
|
289
|
+
await import("./src-68MXysnV.mjs");
|
|
290
290
|
return;
|
|
291
291
|
}
|
|
292
292
|
if (await isEngineRunning()) {
|
|
293
293
|
p.log.success("iii-engine is running");
|
|
294
|
-
await import("./src-
|
|
294
|
+
await import("./src-68MXysnV.mjs");
|
|
295
295
|
return;
|
|
296
296
|
}
|
|
297
297
|
if (!await startEngine()) {
|
|
@@ -335,7 +335,7 @@ async function main() {
|
|
|
335
335
|
process.exit(1);
|
|
336
336
|
}
|
|
337
337
|
s.stop("iii-engine is ready");
|
|
338
|
-
await import("./src-
|
|
338
|
+
await import("./src-68MXysnV.mjs");
|
|
339
339
|
}
|
|
340
340
|
async function runStatus() {
|
|
341
341
|
const port = getRestPort();
|
|
@@ -643,7 +643,7 @@ async function runUpgrade() {
|
|
|
643
643
|
].join("\n"), "agentmemory upgrade");
|
|
644
644
|
}
|
|
645
645
|
async function runMcp() {
|
|
646
|
-
await import("./standalone-
|
|
646
|
+
await import("./standalone-c2xiEQJ9.mjs");
|
|
647
647
|
}
|
|
648
648
|
({
|
|
649
649
|
status: runStatus,
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { TriggerAction, registerWorker } from "iii-sdk";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { dirname, join, resolve, sep } from "node:path";
|
|
4
|
+
import { basename, dirname, extname, join, resolve, sep } from "node:path";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
import Anthropic from "@anthropic-ai/sdk";
|
|
7
7
|
import { createHash, createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
@@ -10,7 +10,7 @@ import { execFile } from "node:child_process";
|
|
|
10
10
|
import { promisify } from "node:util";
|
|
11
11
|
import { lookup } from "node:dns/promises";
|
|
12
12
|
import { isIP } from "node:net";
|
|
13
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
13
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
import { createServer } from "node:http";
|
|
16
16
|
|
|
@@ -2267,6 +2267,17 @@ function registerSearchFunction(sdk, kv) {
|
|
|
2267
2267
|
}
|
|
2268
2268
|
const projectFilter = typeof data.project === "string" && data.project.length > 0 ? data.project : void 0;
|
|
2269
2269
|
const cwdFilter = typeof data.cwd === "string" && data.cwd.length > 0 ? data.cwd : void 0;
|
|
2270
|
+
const format = typeof data.format === "string" ? data.format : "full";
|
|
2271
|
+
if (![
|
|
2272
|
+
"full",
|
|
2273
|
+
"compact",
|
|
2274
|
+
"narrative"
|
|
2275
|
+
].includes(format)) throw new Error("mem::search: format must be one of 'full', 'compact', or 'narrative'");
|
|
2276
|
+
let tokenBudget;
|
|
2277
|
+
if (data.token_budget !== void 0) {
|
|
2278
|
+
if (!Number.isInteger(data.token_budget) || data.token_budget < 1) throw new Error("mem::search: token_budget must be a positive integer");
|
|
2279
|
+
tokenBudget = data.token_budget;
|
|
2280
|
+
}
|
|
2270
2281
|
if (idx.size === 0) {
|
|
2271
2282
|
const count = await rebuildIndex(kv);
|
|
2272
2283
|
logger.info("Search index rebuilt", { entries: count });
|
|
@@ -2303,13 +2314,81 @@ function registerSearchFunction(sdk, kv) {
|
|
|
2303
2314
|
});
|
|
2304
2315
|
}
|
|
2305
2316
|
recordAccessBatch(kv, enriched.map((r) => r.observation.id));
|
|
2317
|
+
const estimateTokens = (value) => Math.max(1, Math.ceil(JSON.stringify(value).length / 3));
|
|
2318
|
+
const applyTokenBudget = (items) => {
|
|
2319
|
+
if (!tokenBudget) return {
|
|
2320
|
+
items,
|
|
2321
|
+
used: items.reduce((sum, item) => sum + estimateTokens(item), 0),
|
|
2322
|
+
truncated: false
|
|
2323
|
+
};
|
|
2324
|
+
const selected = [];
|
|
2325
|
+
let used = 0;
|
|
2326
|
+
for (const item of items) {
|
|
2327
|
+
const itemTokens = estimateTokens(item);
|
|
2328
|
+
if (used + itemTokens > tokenBudget) return {
|
|
2329
|
+
items: selected,
|
|
2330
|
+
used,
|
|
2331
|
+
truncated: selected.length < items.length
|
|
2332
|
+
};
|
|
2333
|
+
selected.push(item);
|
|
2334
|
+
used += itemTokens;
|
|
2335
|
+
}
|
|
2336
|
+
return {
|
|
2337
|
+
items: selected,
|
|
2338
|
+
used,
|
|
2339
|
+
truncated: false
|
|
2340
|
+
};
|
|
2341
|
+
};
|
|
2342
|
+
if (format === "compact") {
|
|
2343
|
+
const packed = applyTokenBudget(enriched.map((r) => ({
|
|
2344
|
+
obsId: r.observation.id,
|
|
2345
|
+
sessionId: r.sessionId,
|
|
2346
|
+
title: r.observation.title,
|
|
2347
|
+
type: r.observation.type,
|
|
2348
|
+
score: r.score,
|
|
2349
|
+
timestamp: r.observation.timestamp
|
|
2350
|
+
})));
|
|
2351
|
+
return {
|
|
2352
|
+
format,
|
|
2353
|
+
results: packed.items,
|
|
2354
|
+
tokens_used: packed.used,
|
|
2355
|
+
tokens_budget: tokenBudget,
|
|
2356
|
+
truncated: packed.truncated
|
|
2357
|
+
};
|
|
2358
|
+
}
|
|
2359
|
+
if (format === "narrative") {
|
|
2360
|
+
const packed = applyTokenBudget(enriched.map((r) => ({
|
|
2361
|
+
obsId: r.observation.id,
|
|
2362
|
+
sessionId: r.sessionId,
|
|
2363
|
+
title: r.observation.title,
|
|
2364
|
+
narrative: r.observation.narrative,
|
|
2365
|
+
score: r.score,
|
|
2366
|
+
timestamp: r.observation.timestamp
|
|
2367
|
+
})));
|
|
2368
|
+
const text = packed.items.map((r, index) => `${index + 1}. ${r.title}\n${r.narrative}`).join("\n\n");
|
|
2369
|
+
return {
|
|
2370
|
+
format,
|
|
2371
|
+
results: packed.items,
|
|
2372
|
+
text,
|
|
2373
|
+
tokens_used: packed.used,
|
|
2374
|
+
tokens_budget: tokenBudget,
|
|
2375
|
+
truncated: packed.truncated
|
|
2376
|
+
};
|
|
2377
|
+
}
|
|
2378
|
+
const packed = applyTokenBudget(enriched);
|
|
2306
2379
|
logger.info("Search completed", {
|
|
2307
2380
|
query,
|
|
2308
|
-
results:
|
|
2381
|
+
results: packed.items.length,
|
|
2309
2382
|
hasProjectFilter: !!projectFilter,
|
|
2310
2383
|
hasCwdFilter: !!cwdFilter
|
|
2311
2384
|
});
|
|
2312
|
-
return {
|
|
2385
|
+
return {
|
|
2386
|
+
format,
|
|
2387
|
+
results: packed.items,
|
|
2388
|
+
tokens_used: packed.used,
|
|
2389
|
+
tokens_budget: tokenBudget,
|
|
2390
|
+
truncated: packed.truncated
|
|
2391
|
+
};
|
|
2313
2392
|
});
|
|
2314
2393
|
}
|
|
2315
2394
|
|
|
@@ -4398,7 +4477,7 @@ function registerAutoForgetFunction(sdk, kv) {
|
|
|
4398
4477
|
|
|
4399
4478
|
//#endregion
|
|
4400
4479
|
//#region src/version.ts
|
|
4401
|
-
const VERSION = "0.8.
|
|
4480
|
+
const VERSION = "0.8.12";
|
|
4402
4481
|
|
|
4403
4482
|
//#endregion
|
|
4404
4483
|
//#region src/functions/export-import.ts
|
|
@@ -4513,7 +4592,8 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
4513
4592
|
"0.8.8",
|
|
4514
4593
|
"0.8.9",
|
|
4515
4594
|
"0.8.10",
|
|
4516
|
-
"0.8.11"
|
|
4595
|
+
"0.8.11",
|
|
4596
|
+
"0.8.12"
|
|
4517
4597
|
]).has(importData.version)) return {
|
|
4518
4598
|
success: false,
|
|
4519
4599
|
error: `Unsupported export version: ${importData.version}`
|
|
@@ -10932,6 +11012,121 @@ function registerRetentionFunctions(sdk, kv) {
|
|
|
10932
11012
|
});
|
|
10933
11013
|
}
|
|
10934
11014
|
|
|
11015
|
+
//#endregion
|
|
11016
|
+
//#region src/functions/compress-file.ts
|
|
11017
|
+
const SENSITIVE_PATH_TERMS = [
|
|
11018
|
+
"secret",
|
|
11019
|
+
"credential",
|
|
11020
|
+
"private_key",
|
|
11021
|
+
".env",
|
|
11022
|
+
"id_rsa",
|
|
11023
|
+
"token"
|
|
11024
|
+
];
|
|
11025
|
+
const COMPRESS_FILE_SYSTEM_PROMPT = `You compress markdown while preserving structure.
|
|
11026
|
+
Rules:
|
|
11027
|
+
- Keep all headings exactly as-is.
|
|
11028
|
+
- Keep all URLs exactly as-is.
|
|
11029
|
+
- Keep all fenced code blocks exactly as-is.
|
|
11030
|
+
- Do not remove sections; shorten prose under each section.
|
|
11031
|
+
- Output only markdown, no wrappers or explanations.`;
|
|
11032
|
+
function stripMarkdownFence(text) {
|
|
11033
|
+
const trimmed = text.trim();
|
|
11034
|
+
const match = trimmed.match(/^```(?:markdown|md)?\s*([\s\S]*?)\s*```$/i);
|
|
11035
|
+
return match ? match[1].trim() : trimmed;
|
|
11036
|
+
}
|
|
11037
|
+
function extractUrls(text) {
|
|
11038
|
+
return Array.from(new Set(text.match(/https?:\/\/[^\s)]+/g) || []));
|
|
11039
|
+
}
|
|
11040
|
+
function extractHeadings(text) {
|
|
11041
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => /^#{1,6}\s+/.test(line));
|
|
11042
|
+
}
|
|
11043
|
+
function extractCodeBlocks(text) {
|
|
11044
|
+
return text.match(/```[\s\S]*?```/g) || [];
|
|
11045
|
+
}
|
|
11046
|
+
function validateCompression(original, compressed) {
|
|
11047
|
+
const errors = [];
|
|
11048
|
+
const originalHeadings = extractHeadings(original);
|
|
11049
|
+
const compressedHeadings = extractHeadings(compressed);
|
|
11050
|
+
for (const heading of originalHeadings) if (!compressedHeadings.includes(heading)) errors.push(`missing heading: ${heading}`);
|
|
11051
|
+
const originalUrls = extractUrls(original).sort();
|
|
11052
|
+
const compressedUrls = extractUrls(compressed).sort();
|
|
11053
|
+
if (originalUrls.length !== compressedUrls.length) errors.push("url count changed");
|
|
11054
|
+
else for (let i = 0; i < originalUrls.length; i++) if (originalUrls[i] !== compressedUrls[i]) {
|
|
11055
|
+
errors.push("url set changed");
|
|
11056
|
+
break;
|
|
11057
|
+
}
|
|
11058
|
+
const originalBlocks = extractCodeBlocks(original);
|
|
11059
|
+
const compressedBlocks = extractCodeBlocks(compressed);
|
|
11060
|
+
if (originalBlocks.length !== compressedBlocks.length) errors.push("code block count changed");
|
|
11061
|
+
else for (let i = 0; i < originalBlocks.length; i++) if (originalBlocks[i] !== compressedBlocks[i]) {
|
|
11062
|
+
errors.push("code block content changed");
|
|
11063
|
+
break;
|
|
11064
|
+
}
|
|
11065
|
+
return errors;
|
|
11066
|
+
}
|
|
11067
|
+
function resolveBackupPath(filePath) {
|
|
11068
|
+
const base = basename(filePath, extname(filePath));
|
|
11069
|
+
const name = base.endsWith(".original") ? base : `${base}.original`;
|
|
11070
|
+
return join(dirname(filePath), `${name}.md`);
|
|
11071
|
+
}
|
|
11072
|
+
function registerCompressFileFunction(sdk, kv, provider) {
|
|
11073
|
+
sdk.registerFunction("mem::compress-file", async (data) => {
|
|
11074
|
+
if (!data?.filePath || typeof data.filePath !== "string") return {
|
|
11075
|
+
success: false,
|
|
11076
|
+
error: "filePath is required"
|
|
11077
|
+
};
|
|
11078
|
+
const absolutePath = resolve(data.filePath);
|
|
11079
|
+
const lowerPath = absolutePath.toLowerCase();
|
|
11080
|
+
if (extname(absolutePath).toLowerCase() !== ".md") return {
|
|
11081
|
+
success: false,
|
|
11082
|
+
error: "filePath must point to a .md file"
|
|
11083
|
+
};
|
|
11084
|
+
if (SENSITIVE_PATH_TERMS.some((term) => lowerPath.includes(term))) return {
|
|
11085
|
+
success: false,
|
|
11086
|
+
error: "refusing to process sensitive-looking path"
|
|
11087
|
+
};
|
|
11088
|
+
let original;
|
|
11089
|
+
try {
|
|
11090
|
+
original = await readFile(absolutePath, "utf-8");
|
|
11091
|
+
} catch {
|
|
11092
|
+
return {
|
|
11093
|
+
success: false,
|
|
11094
|
+
error: "failed to read file"
|
|
11095
|
+
};
|
|
11096
|
+
}
|
|
11097
|
+
if (!original.trim()) return {
|
|
11098
|
+
success: true,
|
|
11099
|
+
skipped: true,
|
|
11100
|
+
reason: "file is empty"
|
|
11101
|
+
};
|
|
11102
|
+
const compressed = stripMarkdownFence(await provider.summarize(COMPRESS_FILE_SYSTEM_PROMPT, `Compress this markdown file while preserving structure and code blocks:\n\n${original}`));
|
|
11103
|
+
const validationErrors = validateCompression(original, compressed);
|
|
11104
|
+
if (validationErrors.length > 0) return {
|
|
11105
|
+
success: false,
|
|
11106
|
+
error: "compression validation failed",
|
|
11107
|
+
details: validationErrors
|
|
11108
|
+
};
|
|
11109
|
+
const backupPath = resolveBackupPath(absolutePath);
|
|
11110
|
+
await writeFile(backupPath, original, "utf-8");
|
|
11111
|
+
await writeFile(absolutePath, compressed, "utf-8");
|
|
11112
|
+
try {
|
|
11113
|
+
await recordAudit(kv, "compress", "mem::compress-file", [], {
|
|
11114
|
+
filePath: absolutePath,
|
|
11115
|
+
backupPath,
|
|
11116
|
+
originalChars: original.length,
|
|
11117
|
+
compressedChars: compressed.length
|
|
11118
|
+
});
|
|
11119
|
+
} catch {}
|
|
11120
|
+
return {
|
|
11121
|
+
success: true,
|
|
11122
|
+
filePath: absolutePath,
|
|
11123
|
+
backupPath,
|
|
11124
|
+
originalChars: original.length,
|
|
11125
|
+
compressedChars: compressed.length
|
|
11126
|
+
};
|
|
11127
|
+
});
|
|
11128
|
+
}
|
|
11129
|
+
|
|
10935
11130
|
//#endregion
|
|
10936
11131
|
//#region src/health/thresholds.ts
|
|
10937
11132
|
const DEFAULTS = {
|
|
@@ -11312,11 +11507,25 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
11312
11507
|
status_code: 400,
|
|
11313
11508
|
body: { error: "cwd must be a string" }
|
|
11314
11509
|
};
|
|
11510
|
+
if (body.format !== void 0 && (typeof body.format !== "string" || ![
|
|
11511
|
+
"full",
|
|
11512
|
+
"compact",
|
|
11513
|
+
"narrative"
|
|
11514
|
+
].includes(body.format.trim().toLowerCase()))) return {
|
|
11515
|
+
status_code: 400,
|
|
11516
|
+
body: { error: "format must be one of: full, compact, narrative" }
|
|
11517
|
+
};
|
|
11518
|
+
if (body.token_budget !== void 0 && (!Number.isInteger(body.token_budget) || body.token_budget < 1)) return {
|
|
11519
|
+
status_code: 400,
|
|
11520
|
+
body: { error: "token_budget must be a positive integer" }
|
|
11521
|
+
};
|
|
11315
11522
|
const payload = {
|
|
11316
11523
|
query: body.query.trim(),
|
|
11317
11524
|
limit: body.limit,
|
|
11318
11525
|
project: body.project,
|
|
11319
|
-
cwd: body.cwd
|
|
11526
|
+
cwd: body.cwd,
|
|
11527
|
+
format: typeof body.format === "string" ? body.format.trim().toLowerCase() : void 0,
|
|
11528
|
+
token_budget: body.token_budget
|
|
11320
11529
|
};
|
|
11321
11530
|
return {
|
|
11322
11531
|
status_code: 200,
|
|
@@ -11335,6 +11544,30 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
11335
11544
|
middleware_function_ids: ["middleware::api-auth"]
|
|
11336
11545
|
}
|
|
11337
11546
|
});
|
|
11547
|
+
sdk.registerFunction("api::compress-file", async (req) => {
|
|
11548
|
+
const authErr = checkAuth(req, secret);
|
|
11549
|
+
if (authErr) return authErr;
|
|
11550
|
+
const filePath = asNonEmptyString$1((req.body ?? {}).filePath);
|
|
11551
|
+
if (!filePath) return {
|
|
11552
|
+
status_code: 400,
|
|
11553
|
+
body: { error: "filePath is required and must be a non-empty string" }
|
|
11554
|
+
};
|
|
11555
|
+
return {
|
|
11556
|
+
status_code: 200,
|
|
11557
|
+
body: await sdk.trigger({
|
|
11558
|
+
function_id: "mem::compress-file",
|
|
11559
|
+
payload: { filePath }
|
|
11560
|
+
})
|
|
11561
|
+
};
|
|
11562
|
+
});
|
|
11563
|
+
sdk.registerTrigger({
|
|
11564
|
+
type: "http",
|
|
11565
|
+
function_id: "api::compress-file",
|
|
11566
|
+
config: {
|
|
11567
|
+
api_path: "/agentmemory/compress-file",
|
|
11568
|
+
http_method: "POST"
|
|
11569
|
+
}
|
|
11570
|
+
});
|
|
11338
11571
|
sdk.registerFunction("api::session::start", async (req) => {
|
|
11339
11572
|
const body = req.body ?? {};
|
|
11340
11573
|
const sessionId = asNonEmptyString$1(body.sessionId);
|
|
@@ -13799,11 +14032,31 @@ const CORE_TOOLS = [
|
|
|
13799
14032
|
limit: {
|
|
13800
14033
|
type: "number",
|
|
13801
14034
|
description: "Max results to return (default 10)"
|
|
14035
|
+
},
|
|
14036
|
+
format: {
|
|
14037
|
+
type: "string",
|
|
14038
|
+
description: "Result format: full, compact, or narrative (default full)"
|
|
14039
|
+
},
|
|
14040
|
+
token_budget: {
|
|
14041
|
+
type: "number",
|
|
14042
|
+
description: "Optional token budget to trim returned results"
|
|
13802
14043
|
}
|
|
13803
14044
|
},
|
|
13804
14045
|
required: ["query"]
|
|
13805
14046
|
}
|
|
13806
14047
|
},
|
|
14048
|
+
{
|
|
14049
|
+
name: "memory_compress_file",
|
|
14050
|
+
description: "Compress a markdown file to reduce token usage while preserving headings, URLs, and code blocks. Creates a .original.md backup before writing.",
|
|
14051
|
+
inputSchema: {
|
|
14052
|
+
type: "object",
|
|
14053
|
+
properties: { filePath: {
|
|
14054
|
+
type: "string",
|
|
14055
|
+
description: "Path to the markdown file to compress"
|
|
14056
|
+
} },
|
|
14057
|
+
required: ["filePath"]
|
|
14058
|
+
}
|
|
14059
|
+
},
|
|
13807
14060
|
{
|
|
13808
14061
|
name: "memory_save",
|
|
13809
14062
|
description: "Explicitly save an important insight, decision, or pattern to long-term memory.",
|
|
@@ -14758,13 +15011,46 @@ function registerMcpEndpoints(sdk, kv, secret) {
|
|
|
14758
15011
|
status_code: 400,
|
|
14759
15012
|
body: { error: "query is required for memory_recall" }
|
|
14760
15013
|
};
|
|
15014
|
+
const format = typeof args.format === "string" ? args.format.trim().toLowerCase() : "full";
|
|
15015
|
+
if (![
|
|
15016
|
+
"full",
|
|
15017
|
+
"compact",
|
|
15018
|
+
"narrative"
|
|
15019
|
+
].includes(format)) return {
|
|
15020
|
+
status_code: 400,
|
|
15021
|
+
body: { error: "format must be one of: full, compact, narrative" }
|
|
15022
|
+
};
|
|
15023
|
+
const tokenBudget = asNumber(args.token_budget);
|
|
15024
|
+
if (args.token_budget !== void 0 && (!Number.isInteger(tokenBudget) || (tokenBudget ?? 0) < 1)) return {
|
|
15025
|
+
status_code: 400,
|
|
15026
|
+
body: { error: "token_budget must be a positive integer" }
|
|
15027
|
+
};
|
|
14761
15028
|
const result = await sdk.trigger({
|
|
14762
15029
|
function_id: "mem::search",
|
|
14763
15030
|
payload: {
|
|
14764
15031
|
query: args.query,
|
|
14765
|
-
limit: typeof args.limit === "number" ? args.limit : 10
|
|
15032
|
+
limit: typeof args.limit === "number" ? args.limit : 10,
|
|
15033
|
+
format,
|
|
15034
|
+
token_budget: tokenBudget
|
|
14766
15035
|
}
|
|
14767
15036
|
});
|
|
15037
|
+
return {
|
|
15038
|
+
status_code: 200,
|
|
15039
|
+
body: { content: [{
|
|
15040
|
+
type: "text",
|
|
15041
|
+
text: format === "narrative" && result && typeof result === "object" && "text" in result && typeof result.text === "string" ? result.text : JSON.stringify(result, null, 2)
|
|
15042
|
+
}] }
|
|
15043
|
+
};
|
|
15044
|
+
}
|
|
15045
|
+
case "memory_compress_file": {
|
|
15046
|
+
if (typeof args.filePath !== "string" || !args.filePath.trim()) return {
|
|
15047
|
+
status_code: 400,
|
|
15048
|
+
body: { error: "filePath is required for memory_compress_file" }
|
|
15049
|
+
};
|
|
15050
|
+
const result = await sdk.trigger({
|
|
15051
|
+
function_id: "mem::compress-file",
|
|
15052
|
+
payload: { filePath: args.filePath.trim() }
|
|
15053
|
+
});
|
|
14768
15054
|
return {
|
|
14769
15055
|
status_code: 200,
|
|
14770
15056
|
body: { content: [{
|
|
@@ -16472,6 +16758,7 @@ async function main() {
|
|
|
16472
16758
|
registerQueryExpansionFunction(sdk, provider);
|
|
16473
16759
|
registerTemporalGraphFunctions(sdk, kv, provider);
|
|
16474
16760
|
registerRetentionFunctions(sdk, kv);
|
|
16761
|
+
registerCompressFileFunction(sdk, kv, provider);
|
|
16475
16762
|
console.log(`[agentmemory] v0.6 advanced retrieval: sliding-window, query-expansion, temporal-graph, retention-scoring`);
|
|
16476
16763
|
console.log(`[agentmemory] Orchestration layer: actions, frontier, leases, routines, signals, checkpoints, flow-compress, mesh, branch-aware, sentinels, sketches, crystallize, diagnostics, facets`);
|
|
16477
16764
|
const snapshotConfig = loadSnapshotConfig();
|
|
@@ -16511,7 +16798,7 @@ async function main() {
|
|
|
16511
16798
|
}
|
|
16512
16799
|
}
|
|
16513
16800
|
console.log(`[agentmemory] Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`);
|
|
16514
|
-
console.log(`[agentmemory] Endpoints:
|
|
16801
|
+
console.log(`[agentmemory] Endpoints: 104 REST + 44 MCP tools + 6 MCP resources + 3 MCP prompts`);
|
|
16515
16802
|
const viewerServer = startViewerServer(config.restPort + 2, kv, sdk, secret, config.restPort);
|
|
16516
16803
|
const autoForgetIntervalMs = parseInt(process.env.AUTO_FORGET_INTERVAL_MS || "3600000", 10);
|
|
16517
16804
|
const consolidationIntervalMs = parseInt(process.env.CONSOLIDATION_INTERVAL_MS || "7200000", 10);
|