@agentmemory/agentmemory 0.7.9 → 0.8.0
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 +61 -3
- package/dist/cli.mjs +57 -13
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +144 -15
- package/dist/index.mjs.map +1 -1
- package/dist/{src-DNbB7fd7.mjs → src-qOdKVNQz.mjs} +145 -16
- package/dist/src-qOdKVNQz.mjs.map +1 -0
- package/dist/standalone.mjs +1 -1
- package/dist/standalone.mjs.map +1 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/src-DNbB7fd7.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -241,6 +241,12 @@ Add to `.opencode/config.json`:
|
|
|
241
241
|
}
|
|
242
242
|
```
|
|
243
243
|
|
|
244
|
+
**Any agent via SkillKit (32+ agents supported):**
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
npx skillkit install agentmemory
|
|
248
|
+
```
|
|
249
|
+
|
|
244
250
|
**REST API (any agent, any language):**
|
|
245
251
|
|
|
246
252
|
```bash
|
|
@@ -298,6 +304,54 @@ If you prefer not to use the plugin, add hooks directly to `~/.claude/settings.j
|
|
|
298
304
|
}
|
|
299
305
|
```
|
|
300
306
|
|
|
307
|
+
## First Steps After Install
|
|
308
|
+
|
|
309
|
+
Once hooks are installed, memory builds silently. No action needed — just use your agent normally.
|
|
310
|
+
|
|
311
|
+
### Session 1: Your agent works as usual
|
|
312
|
+
|
|
313
|
+
```text
|
|
314
|
+
You: "Add JWT auth to the Express API"
|
|
315
|
+
Agent: reads files, writes code, runs tests, fixes errors
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
agentmemory captures every tool use via PostToolUse hooks. At session end, 47 raw observations compress into structured memory:
|
|
319
|
+
|
|
320
|
+
```json
|
|
321
|
+
{
|
|
322
|
+
"type": "file_edit",
|
|
323
|
+
"title": "Implement JWT middleware with jose",
|
|
324
|
+
"facts": ["Using jose library for Edge compatibility", "JWT tokens expire after 30 days", "Middleware in src/middleware/auth.ts"],
|
|
325
|
+
"concepts": ["jwt", "authentication", "jose", "middleware"],
|
|
326
|
+
"files": ["src/middleware/auth.ts", "src/app/api/auth/route.ts"],
|
|
327
|
+
"importance": 9
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Session 2: The payoff
|
|
332
|
+
|
|
333
|
+
You start a new session. Before the agent responds, the SessionStart hook fires and injects context (~1,900 tokens):
|
|
334
|
+
|
|
335
|
+
```text
|
|
336
|
+
Agent already knows:
|
|
337
|
+
- Auth uses jose JWT middleware in src/middleware/auth.ts
|
|
338
|
+
- Tests in test/auth.test.ts cover token validation
|
|
339
|
+
- You chose jose over jsonwebtoken for Edge compatibility
|
|
340
|
+
- Rate limiting discussion from last week's debugging session
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
No re-explaining. The agent starts working immediately.
|
|
344
|
+
|
|
345
|
+
### How to verify it's working
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
npx @agentmemory/agentmemory status # quick terminal check
|
|
349
|
+
curl http://localhost:3111/agentmemory/health
|
|
350
|
+
open http://localhost:3113 # real-time viewer
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
After 1 session: check the Timeline tab in the viewer. After 2+ sessions: check Dashboard for memory count > 0 and the Token Savings card.
|
|
354
|
+
|
|
301
355
|
## How It Works
|
|
302
356
|
|
|
303
357
|
### Observation Pipeline
|
|
@@ -613,7 +667,8 @@ agentmemory needs an LLM for compressing observations and generating summaries.
|
|
|
613
667
|
| Provider | Config | Notes |
|
|
614
668
|
|----------|--------|-------|
|
|
615
669
|
| **Claude subscription** (default) | No config needed | Uses `@anthropic-ai/claude-agent-sdk`. Zero cost beyond your Max/Pro plan |
|
|
616
|
-
| **Anthropic API** | `ANTHROPIC_API_KEY` | Direct API access, per-token billing |
|
|
670
|
+
| **Anthropic API** | `ANTHROPIC_API_KEY` | Direct API access, per-token billing. Supports `ANTHROPIC_BASE_URL` for custom endpoints |
|
|
671
|
+
| **MiniMax** | `MINIMAX_API_KEY` | Anthropic-compatible API. Default model: `MiniMax-M2.7` |
|
|
617
672
|
| **Gemini** | `GEMINI_API_KEY` | Also enables Gemini embeddings (free tier) |
|
|
618
673
|
| **OpenRouter** | `OPENROUTER_API_KEY` | Access any model through one API |
|
|
619
674
|
|
|
@@ -626,6 +681,9 @@ Create `~/.agentmemory/.env`:
|
|
|
626
681
|
```env
|
|
627
682
|
# LLM provider (pick one, or leave empty for Claude subscription)
|
|
628
683
|
ANTHROPIC_API_KEY=sk-ant-...
|
|
684
|
+
# ANTHROPIC_BASE_URL=https://custom-endpoint.example.com
|
|
685
|
+
# MINIMAX_API_KEY=...
|
|
686
|
+
# MINIMAX_MODEL=MiniMax-M2.7
|
|
629
687
|
# GEMINI_API_KEY=...
|
|
630
688
|
# OPENROUTER_API_KEY=...
|
|
631
689
|
|
|
@@ -640,7 +698,7 @@ ANTHROPIC_API_KEY=sk-ant-...
|
|
|
640
698
|
# VECTOR_WEIGHT=0.6
|
|
641
699
|
|
|
642
700
|
# Provider fallback chain (comma-separated, tried in order)
|
|
643
|
-
# FALLBACK_PROVIDERS=anthropic,gemini,openrouter
|
|
701
|
+
# FALLBACK_PROVIDERS=anthropic,minimax,gemini,openrouter
|
|
644
702
|
|
|
645
703
|
# Bearer token for API auth
|
|
646
704
|
# AGENTMEMORY_SECRET=your-secret-here
|
|
@@ -703,7 +761,7 @@ ANTHROPIC_API_KEY=sk-ant-...
|
|
|
703
761
|
| `POST` | `/agentmemory/session/end` | Mark session complete |
|
|
704
762
|
| `POST` | `/agentmemory/observe` | Capture observation |
|
|
705
763
|
| `POST` | `/agentmemory/context` | Generate context |
|
|
706
|
-
| `POST` | `/agentmemory/search` | Search observations (BM25) |
|
|
764
|
+
| `POST` | `/agentmemory/search` | Search observations (BM25). Optional `project`/`cwd` filters |
|
|
707
765
|
| `POST` | `/agentmemory/smart-search` | Hybrid search with progressive disclosure |
|
|
708
766
|
| `POST` | `/agentmemory/summarize` | Generate session summary |
|
|
709
767
|
| `POST` | `/agentmemory/remember` | Save to long-term memory |
|
package/dist/cli.mjs
CHANGED
|
@@ -12,7 +12,11 @@ if (args.includes("--help") || args.includes("-h")) {
|
|
|
12
12
|
console.log(`
|
|
13
13
|
agentmemory — persistent memory for AI coding agents
|
|
14
14
|
|
|
15
|
-
Usage: agentmemory [options]
|
|
15
|
+
Usage: agentmemory [command] [options]
|
|
16
|
+
|
|
17
|
+
Commands:
|
|
18
|
+
(default) Start agentmemory worker
|
|
19
|
+
status Show connection status, memory count, and health
|
|
16
20
|
|
|
17
21
|
Options:
|
|
18
22
|
--help, -h Show this help
|
|
@@ -20,15 +24,10 @@ Options:
|
|
|
20
24
|
--no-engine Skip auto-starting iii-engine
|
|
21
25
|
--port <N> Override REST port (default: 3111)
|
|
22
26
|
|
|
23
|
-
Environment:
|
|
24
|
-
AGENTMEMORY_TOOLS=all Expose all 41 MCP tools
|
|
25
|
-
AGENTMEMORY_SECRET=xxx Auth secret for REST/MCP
|
|
26
|
-
CONSOLIDATION_ENABLED=true Enable auto-consolidation (off by default)
|
|
27
|
-
OBSIDIAN_AUTO_EXPORT=true Auto-export on consolidation
|
|
28
|
-
|
|
29
27
|
Quick start:
|
|
30
|
-
npx @agentmemory/agentmemory
|
|
31
|
-
npx agentmemory
|
|
28
|
+
npx @agentmemory/agentmemory # start everything
|
|
29
|
+
npx @agentmemory/agentmemory status # check health
|
|
30
|
+
npx agentmemory-mcp # standalone MCP (no engine)
|
|
32
31
|
`);
|
|
33
32
|
process.exit(0);
|
|
34
33
|
}
|
|
@@ -160,12 +159,12 @@ async function main() {
|
|
|
160
159
|
p.intro("agentmemory");
|
|
161
160
|
if (skipEngine) {
|
|
162
161
|
p.log.info("Skipping engine check (--no-engine)");
|
|
163
|
-
await import("./src-
|
|
162
|
+
await import("./src-qOdKVNQz.mjs");
|
|
164
163
|
return;
|
|
165
164
|
}
|
|
166
165
|
if (await isEngineRunning()) {
|
|
167
166
|
p.log.success("iii-engine is running");
|
|
168
|
-
await import("./src-
|
|
167
|
+
await import("./src-qOdKVNQz.mjs");
|
|
169
168
|
return;
|
|
170
169
|
}
|
|
171
170
|
if (!await startEngine()) {
|
|
@@ -193,9 +192,54 @@ async function main() {
|
|
|
193
192
|
process.exit(1);
|
|
194
193
|
}
|
|
195
194
|
s.stop("iii-engine is ready");
|
|
196
|
-
await import("./src-
|
|
195
|
+
await import("./src-qOdKVNQz.mjs");
|
|
197
196
|
}
|
|
198
|
-
|
|
197
|
+
async function runStatus() {
|
|
198
|
+
const port = getRestPort();
|
|
199
|
+
const base = `http://localhost:${port}`;
|
|
200
|
+
p.intro("agentmemory status");
|
|
201
|
+
if (!await isEngineRunning()) {
|
|
202
|
+
p.log.error(`Not running — no response on port ${port}`);
|
|
203
|
+
p.log.info("Start with: npx @agentmemory/agentmemory");
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const [healthRes, sessionsRes, graphRes] = await Promise.all([
|
|
208
|
+
fetch(`${base}/agentmemory/health`, { signal: AbortSignal.timeout(5e3) }).then((r) => r.json()).catch(() => null),
|
|
209
|
+
fetch(`${base}/agentmemory/sessions`, { signal: AbortSignal.timeout(5e3) }).then((r) => r.json()).catch(() => null),
|
|
210
|
+
fetch(`${base}/agentmemory/graph/stats`, { signal: AbortSignal.timeout(5e3) }).then((r) => r.json()).catch(() => null)
|
|
211
|
+
]);
|
|
212
|
+
const h = healthRes?.health;
|
|
213
|
+
const status = healthRes?.status || "unknown";
|
|
214
|
+
const version = healthRes?.version || "?";
|
|
215
|
+
const sessions = Array.isArray(sessionsRes?.sessions) ? sessionsRes.sessions.length : 0;
|
|
216
|
+
h?.workers?.[0]?.function_count;
|
|
217
|
+
const nodes = graphRes?.nodes || 0;
|
|
218
|
+
const edges = graphRes?.edges || 0;
|
|
219
|
+
const cb = healthRes?.circuitBreaker?.state || "closed";
|
|
220
|
+
const heapMB = h?.memory ? Math.round(h.memory.heapUsed / 1048576) : 0;
|
|
221
|
+
const uptime = h?.uptimeSeconds ? Math.round(h.uptimeSeconds) : 0;
|
|
222
|
+
p.log.success(`Connected — v${version} on port ${port}`);
|
|
223
|
+
const lines = [
|
|
224
|
+
`Health: ${status === "healthy" ? "healthy" : status}`,
|
|
225
|
+
`Sessions: ${sessions}`,
|
|
226
|
+
`Graph: ${nodes} nodes, ${edges} edges`,
|
|
227
|
+
`Circuit: ${cb}`,
|
|
228
|
+
`Heap: ${heapMB} MB`,
|
|
229
|
+
`Uptime: ${uptime}s`,
|
|
230
|
+
`Viewer: http://localhost:${port + 2}`
|
|
231
|
+
];
|
|
232
|
+
p.note(lines.join("\n"), "agentmemory");
|
|
233
|
+
} catch (err) {
|
|
234
|
+
p.log.error(err instanceof Error ? err.message : String(err));
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (args[0] === "status") runStatus().catch((err) => {
|
|
239
|
+
p.log.error(err instanceof Error ? err.message : String(err));
|
|
240
|
+
process.exit(1);
|
|
241
|
+
});
|
|
242
|
+
else main().catch((err) => {
|
|
199
243
|
p.log.error(err instanceof Error ? err.message : String(err));
|
|
200
244
|
process.exit(1);
|
|
201
245
|
});
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { spawn, execFileSync, execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as p from \"@clack/prompts\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst args = process.argv.slice(2);\n\nif (args.includes(\"--help\") || args.includes(\"-h\")) {\n console.log(`\nagentmemory — persistent memory for AI coding agents\n\nUsage: agentmemory [options]\n\nOptions:\n --help, -h Show this help\n --tools all|core Tool visibility (default: core = 7 tools)\n --no-engine Skip auto-starting iii-engine\n --port <N> Override REST port (default: 3111)\n\nEnvironment:\n AGENTMEMORY_TOOLS=all Expose all 41 MCP tools\n AGENTMEMORY_SECRET=xxx Auth secret for REST/MCP\n CONSOLIDATION_ENABLED=true Enable auto-consolidation (off by default)\n OBSIDIAN_AUTO_EXPORT=true Auto-export on consolidation\n\nQuick start:\n npx @agentmemory/agentmemory # installs iii if missing, starts everything\n npx agentmemory-mcp # standalone MCP server (no engine needed)\n`);\n process.exit(0);\n}\n\nconst toolsIdx = args.indexOf(\"--tools\");\nif (toolsIdx !== -1 && args[toolsIdx + 1]) {\n process.env[\"AGENTMEMORY_TOOLS\"] = args[toolsIdx + 1];\n}\n\nconst portIdx = args.indexOf(\"--port\");\nif (portIdx !== -1 && args[portIdx + 1]) {\n process.env[\"III_REST_PORT\"] = args[portIdx + 1];\n}\n\nconst skipEngine = args.includes(\"--no-engine\");\n\nfunction getRestPort(): number {\n return parseInt(process.env[\"III_REST_PORT\"] || \"3111\", 10) || 3111;\n}\n\nasync function isEngineRunning(): Promise<boolean> {\n try {\n await fetch(`http://localhost:${getRestPort()}/`, {\n signal: AbortSignal.timeout(2000),\n });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction findIiiConfig(): string {\n const candidates = [\n join(__dirname, \"iii-config.yaml\"),\n join(__dirname, \"..\", \"iii-config.yaml\"),\n join(process.cwd(), \"iii-config.yaml\"),\n ];\n for (const c of candidates) {\n if (existsSync(c)) return c;\n }\n return \"\";\n}\n\nfunction whichBinary(name: string): string | null {\n const cmd = process.platform === \"win32\" ? \"where\" : \"which\";\n try {\n return execFileSync(cmd, [name], { encoding: \"utf-8\" }).trim().split(\"\\n\")[0];\n } catch {\n return null;\n }\n}\n\nasync function installIii(): Promise<boolean> {\n if (process.platform === \"win32\") {\n p.log.warn(\"Automatic iii-engine install is not supported on Windows.\");\n p.log.info(\"Install manually: https://iii.dev/docs\");\n return false;\n }\n\n const curlBin = whichBinary(\"curl\");\n if (!curlBin) {\n p.log.warn(\"curl not found — cannot auto-install iii-engine.\");\n return false;\n }\n\n const shouldInstall = await p.confirm({\n message: \"iii-engine is not installed. Install it now?\",\n initialValue: true,\n });\n\n if (p.isCancel(shouldInstall) || !shouldInstall) {\n return false;\n }\n\n const s = p.spinner();\n s.start(\"Installing iii-engine...\");\n\n try {\n execSync(\"curl -fsSL https://install.iii.dev/iii/main/install.sh | sh\", {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n timeout: 120000,\n });\n\n const installed = whichBinary(\"iii\");\n if (installed) {\n s.stop(\"iii-engine installed successfully\");\n return true;\n }\n\n s.stop(\"Installation completed but iii not found in PATH\");\n p.log.warn(\"You may need to restart your shell or add iii to your PATH.\");\n\n const iiiPaths = [\n join(process.env[\"HOME\"] || \"\", \".local\", \"bin\", \"iii\"),\n \"/usr/local/bin/iii\",\n ];\n for (const iiiPath of iiiPaths) {\n if (existsSync(iiiPath)) {\n p.log.info(`Found iii at: ${iiiPath}`);\n process.env[\"PATH\"] = `${dirname(iiiPath)}:${process.env[\"PATH\"]}`;\n return true;\n }\n }\n\n return false;\n } catch (err) {\n s.stop(\"Failed to install iii-engine\");\n p.log.error(err instanceof Error ? err.message : String(err));\n return false;\n }\n}\n\nasync function startEngine(): Promise<boolean> {\n const configPath = findIiiConfig();\n let iiiBin = whichBinary(\"iii\");\n\n if (!iiiBin) {\n const installed = await installIii();\n if (installed) {\n iiiBin = whichBinary(\"iii\");\n }\n }\n\n if (iiiBin && configPath) {\n const s = p.spinner();\n s.start(`Starting iii-engine: ${iiiBin}`);\n const child = spawn(iiiBin, [\"--config\", configPath], {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n s.stop(\"iii-engine process started\");\n return true;\n }\n\n const dockerBin = whichBinary(\"docker\");\n const dockerCompose = join(__dirname, \"..\", \"docker-compose.yml\");\n const dcExists = existsSync(dockerCompose) || existsSync(join(process.cwd(), \"docker-compose.yml\"));\n\n if (dockerBin && dcExists) {\n const composeFile = existsSync(dockerCompose) ? dockerCompose : join(process.cwd(), \"docker-compose.yml\");\n const s = p.spinner();\n s.start(\"Starting iii-engine via Docker...\");\n const child = spawn(dockerBin, [\"compose\", \"-f\", composeFile, \"up\", \"-d\"], {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n s.stop(\"Docker compose started\");\n return true;\n }\n\n return false;\n}\n\nasync function waitForEngine(timeoutMs: number): Promise<boolean> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n if (await isEngineRunning()) return true;\n await new Promise((r) => setTimeout(r, 500));\n }\n return false;\n}\n\nasync function main() {\n p.intro(\"agentmemory\");\n\n if (skipEngine) {\n p.log.info(\"Skipping engine check (--no-engine)\");\n await import(\"./index.js\");\n return;\n }\n\n if (await isEngineRunning()) {\n p.log.success(\"iii-engine is running\");\n await import(\"./index.js\");\n return;\n }\n\n const started = await startEngine();\n if (!started) {\n p.log.error(\"Could not start iii-engine.\");\n p.note(\n [\n \"Install iii-engine (pick one):\",\n \" curl -fsSL https://install.iii.dev/iii/main/install.sh | sh\",\n \" cargo install iii-engine\",\n \"\",\n \"Or use Docker:\",\n \" docker pull iiidev/iii:latest\",\n \"\",\n \"Docs: https://iii.dev/docs\",\n \"\",\n \"Or skip with: agentmemory --no-engine\",\n ].join(\"\\n\"),\n \"Setup required\",\n );\n process.exit(1);\n }\n\n const s = p.spinner();\n s.start(\"Waiting for iii-engine to be ready...\");\n\n const ready = await waitForEngine(15000);\n if (!ready) {\n const port = getRestPort();\n s.stop(\"iii-engine did not become ready within 15s\");\n p.log.error(`Check that ports ${port}, ${port + 1}, 49134 are available.`);\n process.exit(1);\n }\n\n s.stop(\"iii-engine is ready\");\n await import(\"./index.js\");\n}\n\nmain().catch((err) => {\n p.log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { spawn, execFileSync, execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as p from \"@clack/prompts\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst args = process.argv.slice(2);\n\nif (args.includes(\"--help\") || args.includes(\"-h\")) {\n console.log(`\nagentmemory — persistent memory for AI coding agents\n\nUsage: agentmemory [command] [options]\n\nCommands:\n (default) Start agentmemory worker\n status Show connection status, memory count, and health\n\nOptions:\n --help, -h Show this help\n --tools all|core Tool visibility (default: core = 7 tools)\n --no-engine Skip auto-starting iii-engine\n --port <N> Override REST port (default: 3111)\n\nQuick start:\n npx @agentmemory/agentmemory # start everything\n npx @agentmemory/agentmemory status # check health\n npx agentmemory-mcp # standalone MCP (no engine)\n`);\n process.exit(0);\n}\n\nconst toolsIdx = args.indexOf(\"--tools\");\nif (toolsIdx !== -1 && args[toolsIdx + 1]) {\n process.env[\"AGENTMEMORY_TOOLS\"] = args[toolsIdx + 1];\n}\n\nconst portIdx = args.indexOf(\"--port\");\nif (portIdx !== -1 && args[portIdx + 1]) {\n process.env[\"III_REST_PORT\"] = args[portIdx + 1];\n}\n\nconst skipEngine = args.includes(\"--no-engine\");\n\nfunction getRestPort(): number {\n return parseInt(process.env[\"III_REST_PORT\"] || \"3111\", 10) || 3111;\n}\n\nasync function isEngineRunning(): Promise<boolean> {\n try {\n await fetch(`http://localhost:${getRestPort()}/`, {\n signal: AbortSignal.timeout(2000),\n });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction findIiiConfig(): string {\n const candidates = [\n join(__dirname, \"iii-config.yaml\"),\n join(__dirname, \"..\", \"iii-config.yaml\"),\n join(process.cwd(), \"iii-config.yaml\"),\n ];\n for (const c of candidates) {\n if (existsSync(c)) return c;\n }\n return \"\";\n}\n\nfunction whichBinary(name: string): string | null {\n const cmd = process.platform === \"win32\" ? \"where\" : \"which\";\n try {\n return execFileSync(cmd, [name], { encoding: \"utf-8\" }).trim().split(\"\\n\")[0];\n } catch {\n return null;\n }\n}\n\nasync function installIii(): Promise<boolean> {\n if (process.platform === \"win32\") {\n p.log.warn(\"Automatic iii-engine install is not supported on Windows.\");\n p.log.info(\"Install manually: https://iii.dev/docs\");\n return false;\n }\n\n const curlBin = whichBinary(\"curl\");\n if (!curlBin) {\n p.log.warn(\"curl not found — cannot auto-install iii-engine.\");\n return false;\n }\n\n const shouldInstall = await p.confirm({\n message: \"iii-engine is not installed. Install it now?\",\n initialValue: true,\n });\n\n if (p.isCancel(shouldInstall) || !shouldInstall) {\n return false;\n }\n\n const s = p.spinner();\n s.start(\"Installing iii-engine...\");\n\n try {\n execSync(\"curl -fsSL https://install.iii.dev/iii/main/install.sh | sh\", {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n timeout: 120000,\n });\n\n const installed = whichBinary(\"iii\");\n if (installed) {\n s.stop(\"iii-engine installed successfully\");\n return true;\n }\n\n s.stop(\"Installation completed but iii not found in PATH\");\n p.log.warn(\"You may need to restart your shell or add iii to your PATH.\");\n\n const iiiPaths = [\n join(process.env[\"HOME\"] || \"\", \".local\", \"bin\", \"iii\"),\n \"/usr/local/bin/iii\",\n ];\n for (const iiiPath of iiiPaths) {\n if (existsSync(iiiPath)) {\n p.log.info(`Found iii at: ${iiiPath}`);\n process.env[\"PATH\"] = `${dirname(iiiPath)}:${process.env[\"PATH\"]}`;\n return true;\n }\n }\n\n return false;\n } catch (err) {\n s.stop(\"Failed to install iii-engine\");\n p.log.error(err instanceof Error ? err.message : String(err));\n return false;\n }\n}\n\nasync function startEngine(): Promise<boolean> {\n const configPath = findIiiConfig();\n let iiiBin = whichBinary(\"iii\");\n\n if (!iiiBin) {\n const installed = await installIii();\n if (installed) {\n iiiBin = whichBinary(\"iii\");\n }\n }\n\n if (iiiBin && configPath) {\n const s = p.spinner();\n s.start(`Starting iii-engine: ${iiiBin}`);\n const child = spawn(iiiBin, [\"--config\", configPath], {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n s.stop(\"iii-engine process started\");\n return true;\n }\n\n const dockerBin = whichBinary(\"docker\");\n const dockerCompose = join(__dirname, \"..\", \"docker-compose.yml\");\n const dcExists = existsSync(dockerCompose) || existsSync(join(process.cwd(), \"docker-compose.yml\"));\n\n if (dockerBin && dcExists) {\n const composeFile = existsSync(dockerCompose) ? dockerCompose : join(process.cwd(), \"docker-compose.yml\");\n const s = p.spinner();\n s.start(\"Starting iii-engine via Docker...\");\n const child = spawn(dockerBin, [\"compose\", \"-f\", composeFile, \"up\", \"-d\"], {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n s.stop(\"Docker compose started\");\n return true;\n }\n\n return false;\n}\n\nasync function waitForEngine(timeoutMs: number): Promise<boolean> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n if (await isEngineRunning()) return true;\n await new Promise((r) => setTimeout(r, 500));\n }\n return false;\n}\n\nasync function main() {\n p.intro(\"agentmemory\");\n\n if (skipEngine) {\n p.log.info(\"Skipping engine check (--no-engine)\");\n await import(\"./index.js\");\n return;\n }\n\n if (await isEngineRunning()) {\n p.log.success(\"iii-engine is running\");\n await import(\"./index.js\");\n return;\n }\n\n const started = await startEngine();\n if (!started) {\n p.log.error(\"Could not start iii-engine.\");\n p.note(\n [\n \"Install iii-engine (pick one):\",\n \" curl -fsSL https://install.iii.dev/iii/main/install.sh | sh\",\n \" cargo install iii-engine\",\n \"\",\n \"Or use Docker:\",\n \" docker pull iiidev/iii:latest\",\n \"\",\n \"Docs: https://iii.dev/docs\",\n \"\",\n \"Or skip with: agentmemory --no-engine\",\n ].join(\"\\n\"),\n \"Setup required\",\n );\n process.exit(1);\n }\n\n const s = p.spinner();\n s.start(\"Waiting for iii-engine to be ready...\");\n\n const ready = await waitForEngine(15000);\n if (!ready) {\n const port = getRestPort();\n s.stop(\"iii-engine did not become ready within 15s\");\n p.log.error(`Check that ports ${port}, ${port + 1}, 49134 are available.`);\n process.exit(1);\n }\n\n s.stop(\"iii-engine is ready\");\n await import(\"./index.js\");\n}\n\nasync function runStatus() {\n const port = getRestPort();\n const base = `http://localhost:${port}`;\n p.intro(\"agentmemory status\");\n\n const up = await isEngineRunning();\n if (!up) {\n p.log.error(`Not running — no response on port ${port}`);\n p.log.info(\"Start with: npx @agentmemory/agentmemory\");\n process.exit(1);\n }\n\n try {\n const [healthRes, sessionsRes, graphRes] = await Promise.all([\n fetch(`${base}/agentmemory/health`, { signal: AbortSignal.timeout(5000) }).then((r) => r.json()).catch(() => null),\n fetch(`${base}/agentmemory/sessions`, { signal: AbortSignal.timeout(5000) }).then((r) => r.json()).catch(() => null),\n fetch(`${base}/agentmemory/graph/stats`, { signal: AbortSignal.timeout(5000) }).then((r) => r.json()).catch(() => null),\n ]);\n\n const h = healthRes?.health;\n const status = healthRes?.status || \"unknown\";\n const version = healthRes?.version || \"?\";\n const sessions = Array.isArray(sessionsRes?.sessions) ? sessionsRes.sessions.length : 0;\n const memories = h?.workers?.[0]?.function_count || 0;\n const nodes = graphRes?.nodes || 0;\n const edges = graphRes?.edges || 0;\n const cb = healthRes?.circuitBreaker?.state || \"closed\";\n const heapMB = h?.memory ? Math.round(h.memory.heapUsed / 1048576) : 0;\n const uptime = h?.uptimeSeconds ? Math.round(h.uptimeSeconds) : 0;\n\n p.log.success(`Connected — v${version} on port ${port}`);\n\n const lines = [\n `Health: ${status === \"healthy\" ? \"healthy\" : status}`,\n `Sessions: ${sessions}`,\n `Graph: ${nodes} nodes, ${edges} edges`,\n `Circuit: ${cb}`,\n `Heap: ${heapMB} MB`,\n `Uptime: ${uptime}s`,\n `Viewer: http://localhost:${port + 2}`,\n ];\n p.note(lines.join(\"\\n\"), \"agentmemory\");\n } catch (err) {\n p.log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n}\n\nif (args[0] === \"status\") {\n runStatus().catch((err) => {\n p.log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n });\n} else {\n main().catch((err) => {\n p.log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n });\n}\n"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -40,10 +40,16 @@ function loadEnvFile() {
|
|
|
40
40
|
}
|
|
41
41
|
function detectProvider(env) {
|
|
42
42
|
const maxTokens = parseInt(env["MAX_TOKENS"] || "4096", 10);
|
|
43
|
+
if (env["MINIMAX_API_KEY"]) return {
|
|
44
|
+
provider: "minimax",
|
|
45
|
+
model: env["MINIMAX_MODEL"] || "MiniMax-M2.7",
|
|
46
|
+
maxTokens
|
|
47
|
+
};
|
|
43
48
|
if (env["ANTHROPIC_API_KEY"]) return {
|
|
44
49
|
provider: "anthropic",
|
|
45
50
|
model: env["ANTHROPIC_MODEL"] || "claude-sonnet-4-20250514",
|
|
46
|
-
maxTokens
|
|
51
|
+
maxTokens,
|
|
52
|
+
baseURL: env["ANTHROPIC_BASE_URL"]
|
|
47
53
|
};
|
|
48
54
|
if (env["GEMINI_API_KEY"]) return {
|
|
49
55
|
provider: "gemini",
|
|
@@ -157,7 +163,8 @@ const VALID_PROVIDERS = new Set([
|
|
|
157
163
|
"anthropic",
|
|
158
164
|
"gemini",
|
|
159
165
|
"openrouter",
|
|
160
|
-
"agent-sdk"
|
|
166
|
+
"agent-sdk",
|
|
167
|
+
"minimax"
|
|
161
168
|
]);
|
|
162
169
|
function loadFallbackConfig() {
|
|
163
170
|
return { providers: (getMergedEnv()["FALLBACK_PROVIDERS"] || "").split(",").map((p) => p.trim()).filter((p) => Boolean(p) && VALID_PROVIDERS.has(p)) };
|
|
@@ -196,8 +203,11 @@ var AnthropicProvider = class {
|
|
|
196
203
|
client;
|
|
197
204
|
model;
|
|
198
205
|
maxTokens;
|
|
199
|
-
constructor(apiKey, model, maxTokens) {
|
|
200
|
-
this.client = new Anthropic({
|
|
206
|
+
constructor(apiKey, model, maxTokens, baseURL) {
|
|
207
|
+
this.client = new Anthropic({
|
|
208
|
+
apiKey,
|
|
209
|
+
...baseURL ? { baseURL } : {}
|
|
210
|
+
});
|
|
201
211
|
this.model = model;
|
|
202
212
|
this.maxTokens = maxTokens;
|
|
203
213
|
}
|
|
@@ -220,6 +230,67 @@ var AnthropicProvider = class {
|
|
|
220
230
|
}
|
|
221
231
|
};
|
|
222
232
|
|
|
233
|
+
//#endregion
|
|
234
|
+
//#region src/providers/minimax.ts
|
|
235
|
+
/**
|
|
236
|
+
* MiniMax provider using raw fetch to call MiniMax's Anthropic-compatible API.
|
|
237
|
+
*
|
|
238
|
+
* The Anthropic SDK automatically injects `x-stainless-*` headers that MiniMax
|
|
239
|
+
* rejects with 403. This provider bypasses the SDK and calls the API directly.
|
|
240
|
+
*
|
|
241
|
+
* Required env vars:
|
|
242
|
+
* MINIMAX_API_KEY — your MiniMax API key
|
|
243
|
+
* MINIMAX_MODEL — model name (default: MiniMax-M2.7)
|
|
244
|
+
* MAX_TOKENS — max output tokens (default: 800; MiniMax-M2.7 needs ≤800)
|
|
245
|
+
*
|
|
246
|
+
* Optional:
|
|
247
|
+
* MINIMAX_BASE_URL — base URL without path (default: https://api.minimaxi.com/anthropic)
|
|
248
|
+
*/
|
|
249
|
+
var MinimaxProvider = class {
|
|
250
|
+
name = "minimax";
|
|
251
|
+
apiKey;
|
|
252
|
+
model;
|
|
253
|
+
maxTokens;
|
|
254
|
+
baseUrl;
|
|
255
|
+
constructor(apiKey, model, maxTokens) {
|
|
256
|
+
this.apiKey = apiKey;
|
|
257
|
+
this.model = model;
|
|
258
|
+
this.maxTokens = maxTokens;
|
|
259
|
+
this.baseUrl = process.env["MINIMAX_BASE_URL"] || "https://api.minimaxi.com/anthropic";
|
|
260
|
+
}
|
|
261
|
+
async compress(systemPrompt, userPrompt) {
|
|
262
|
+
return this.call(systemPrompt, userPrompt);
|
|
263
|
+
}
|
|
264
|
+
async summarize(systemPrompt, userPrompt) {
|
|
265
|
+
return this.call(systemPrompt, userPrompt);
|
|
266
|
+
}
|
|
267
|
+
async call(systemPrompt, userPrompt) {
|
|
268
|
+
const url = `${this.baseUrl}/v1/messages`;
|
|
269
|
+
const response = await fetch(url, {
|
|
270
|
+
method: "POST",
|
|
271
|
+
headers: {
|
|
272
|
+
"Content-Type": "application/json",
|
|
273
|
+
"x-api-key": this.apiKey,
|
|
274
|
+
"anthropic-version": "2023-06-01"
|
|
275
|
+
},
|
|
276
|
+
body: JSON.stringify({
|
|
277
|
+
model: this.model,
|
|
278
|
+
max_tokens: this.maxTokens,
|
|
279
|
+
system: systemPrompt,
|
|
280
|
+
messages: [{
|
|
281
|
+
role: "user",
|
|
282
|
+
content: userPrompt
|
|
283
|
+
}]
|
|
284
|
+
})
|
|
285
|
+
});
|
|
286
|
+
if (!response.ok) {
|
|
287
|
+
const text = await response.text();
|
|
288
|
+
throw new Error(`MiniMax API error ${response.status}: ${text}`);
|
|
289
|
+
}
|
|
290
|
+
return ((await response.json()).content?.find((b) => b.type === "text"))?.text ?? "";
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
223
294
|
//#endregion
|
|
224
295
|
//#region src/providers/openrouter.ts
|
|
225
296
|
var OpenRouterProvider = class {
|
|
@@ -647,7 +718,8 @@ function createFallbackProvider(config, fallbackConfig) {
|
|
|
647
718
|
}
|
|
648
719
|
function createBaseProvider(config) {
|
|
649
720
|
switch (config.provider) {
|
|
650
|
-
case "
|
|
721
|
+
case "minimax": return new MinimaxProvider(requireEnvVar("MINIMAX_API_KEY"), config.model, config.maxTokens);
|
|
722
|
+
case "anthropic": return new AnthropicProvider(requireEnvVar("ANTHROPIC_API_KEY"), config.model, config.maxTokens, config.baseURL);
|
|
651
723
|
case "gemini": return new OpenRouterProvider(requireEnvVar("GEMINI_API_KEY"), config.model, config.maxTokens, "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions");
|
|
652
724
|
case "openrouter": return new OpenRouterProvider(requireEnvVar("OPENROUTER_API_KEY"), config.model, config.maxTokens, "https://openrouter.ai/api/v1/chat/completions");
|
|
653
725
|
default: return new AgentSDKProvider();
|
|
@@ -2123,23 +2195,56 @@ function registerSearchFunction(sdk, kv) {
|
|
|
2123
2195
|
}, async (data) => {
|
|
2124
2196
|
const ctx = getContext();
|
|
2125
2197
|
const idx = getSearchIndex();
|
|
2198
|
+
if (typeof data?.query !== "string" || !data.query.trim()) throw new Error("mem::search: query must be a non-empty string");
|
|
2199
|
+
const query = data.query.trim();
|
|
2200
|
+
const MAX_LIMIT = 100;
|
|
2201
|
+
let effectiveLimit = 20;
|
|
2202
|
+
if (data.limit !== void 0) {
|
|
2203
|
+
if (!Number.isInteger(data.limit) || data.limit < 1) throw new Error("mem::search: limit must be a positive integer");
|
|
2204
|
+
effectiveLimit = Math.min(data.limit, MAX_LIMIT);
|
|
2205
|
+
}
|
|
2206
|
+
const projectFilter = typeof data.project === "string" && data.project.length > 0 ? data.project : void 0;
|
|
2207
|
+
const cwdFilter = typeof data.cwd === "string" && data.cwd.length > 0 ? data.cwd : void 0;
|
|
2126
2208
|
if (idx.size === 0) {
|
|
2127
2209
|
const count = await rebuildIndex(kv);
|
|
2128
2210
|
ctx.logger.info("Search index rebuilt", { entries: count });
|
|
2129
2211
|
}
|
|
2130
|
-
const
|
|
2131
|
-
const
|
|
2212
|
+
const filtering = !!(projectFilter || cwdFilter);
|
|
2213
|
+
const fetchLimit = filtering ? Math.max(effectiveLimit * 10, 100) : effectiveLimit;
|
|
2214
|
+
const results = idx.search(query, fetchLimit);
|
|
2215
|
+
const sessionCache = /* @__PURE__ */ new Map();
|
|
2216
|
+
const loadSession = async (sessionId) => {
|
|
2217
|
+
if (sessionCache.has(sessionId)) return sessionCache.get(sessionId);
|
|
2218
|
+
const s = await kv.get(KV.sessions, sessionId);
|
|
2219
|
+
sessionCache.set(sessionId, s ?? null);
|
|
2220
|
+
return s ?? null;
|
|
2221
|
+
};
|
|
2222
|
+
const candidates = [];
|
|
2132
2223
|
for (const r of results) {
|
|
2133
|
-
|
|
2224
|
+
if (candidates.length >= effectiveLimit) break;
|
|
2225
|
+
if (filtering) {
|
|
2226
|
+
const s = await loadSession(r.sessionId);
|
|
2227
|
+
if (!s) continue;
|
|
2228
|
+
if (projectFilter && s.project !== projectFilter) continue;
|
|
2229
|
+
if (cwdFilter && s.cwd !== cwdFilter) continue;
|
|
2230
|
+
}
|
|
2231
|
+
candidates.push(r);
|
|
2232
|
+
}
|
|
2233
|
+
const obsResults = await Promise.all(candidates.map((r) => kv.get(KV.observations(r.sessionId), r.obsId)));
|
|
2234
|
+
const enriched = [];
|
|
2235
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
2236
|
+
const obs = obsResults[i];
|
|
2134
2237
|
if (obs) enriched.push({
|
|
2135
2238
|
observation: obs,
|
|
2136
|
-
score:
|
|
2137
|
-
sessionId:
|
|
2239
|
+
score: candidates[i].score,
|
|
2240
|
+
sessionId: candidates[i].sessionId
|
|
2138
2241
|
});
|
|
2139
2242
|
}
|
|
2140
2243
|
ctx.logger.info("Search completed", {
|
|
2141
|
-
query
|
|
2142
|
-
results: enriched.length
|
|
2244
|
+
query,
|
|
2245
|
+
results: enriched.length,
|
|
2246
|
+
hasProjectFilter: !!projectFilter,
|
|
2247
|
+
hasCwdFilter: !!cwdFilter
|
|
2143
2248
|
});
|
|
2144
2249
|
return { results: enriched };
|
|
2145
2250
|
});
|
|
@@ -3805,7 +3910,7 @@ function registerAutoForgetFunction(sdk, kv) {
|
|
|
3805
3910
|
|
|
3806
3911
|
//#endregion
|
|
3807
3912
|
//#region src/version.ts
|
|
3808
|
-
const VERSION = "0.
|
|
3913
|
+
const VERSION = "0.8.0";
|
|
3809
3914
|
|
|
3810
3915
|
//#endregion
|
|
3811
3916
|
//#region src/functions/export-import.ts
|
|
@@ -3910,7 +4015,8 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
3910
4015
|
"0.7.5",
|
|
3911
4016
|
"0.7.6",
|
|
3912
4017
|
"0.7.7",
|
|
3913
|
-
"0.7.9"
|
|
4018
|
+
"0.7.9",
|
|
4019
|
+
"0.8.0"
|
|
3914
4020
|
]).has(importData.version)) return {
|
|
3915
4021
|
success: false,
|
|
3916
4022
|
error: `Unsupported export version: ${importData.version}`
|
|
@@ -10033,9 +10139,32 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
10033
10139
|
sdk.registerFunction({ id: "api::search" }, async (req) => {
|
|
10034
10140
|
const authErr = checkAuth$1(req, secret);
|
|
10035
10141
|
if (authErr) return authErr;
|
|
10142
|
+
const body = req.body ?? {};
|
|
10143
|
+
if (typeof body.query !== "string" || !body.query.trim()) return {
|
|
10144
|
+
status_code: 400,
|
|
10145
|
+
body: { error: "query is required and must be a non-empty string" }
|
|
10146
|
+
};
|
|
10147
|
+
if (body.limit !== void 0 && (!Number.isInteger(body.limit) || body.limit < 1)) return {
|
|
10148
|
+
status_code: 400,
|
|
10149
|
+
body: { error: "limit must be a positive integer" }
|
|
10150
|
+
};
|
|
10151
|
+
if (body.project !== void 0 && typeof body.project !== "string") return {
|
|
10152
|
+
status_code: 400,
|
|
10153
|
+
body: { error: "project must be a string" }
|
|
10154
|
+
};
|
|
10155
|
+
if (body.cwd !== void 0 && typeof body.cwd !== "string") return {
|
|
10156
|
+
status_code: 400,
|
|
10157
|
+
body: { error: "cwd must be a string" }
|
|
10158
|
+
};
|
|
10159
|
+
const payload = {
|
|
10160
|
+
query: body.query.trim(),
|
|
10161
|
+
limit: body.limit,
|
|
10162
|
+
project: body.project,
|
|
10163
|
+
cwd: body.cwd
|
|
10164
|
+
};
|
|
10036
10165
|
return {
|
|
10037
10166
|
status_code: 200,
|
|
10038
|
-
body: await sdk.trigger("mem::search",
|
|
10167
|
+
body: await sdk.trigger("mem::search", payload)
|
|
10039
10168
|
};
|
|
10040
10169
|
});
|
|
10041
10170
|
sdk.registerTrigger({
|