@agentmemory/agentmemory 0.9.10 → 0.9.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/README.md CHANGED
@@ -107,7 +107,7 @@ agentmemory works with any agent that supports hooks, MCP, or REST API. All agen
107
107
  <td align="center" width="12.5%">
108
108
  <a href="https://github.com/openai/codex"><img src="https://github.com/openai.png?size=120" alt="Codex CLI" width="48" height="48" /></a><br/>
109
109
  <strong>Codex CLI</strong><br/>
110
- <sub>MCP server</sub>
110
+ <sub>6 hooks + MCP + skills</sub>
111
111
  </td>
112
112
  <td align="center" width="12.5%">
113
113
  <a href="https://github.com/cline/cline"><img src="https://github.com/cline.png?size=120" alt="Cline" width="48" height="48" /></a><br/>
@@ -355,6 +355,25 @@ Implementation details live in `src/cli.ts` (see `runUpgrade` around the `src/cl
355
355
  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 51 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.
356
356
  ```
357
357
 
358
+ ### Codex CLI (Codex plugin platform)
359
+
360
+ ```bash
361
+ # 1. start the memory server in a separate terminal
362
+ npx @agentmemory/agentmemory
363
+
364
+ # 2. register the agentmemory marketplace and install the plugin
365
+ codex plugin marketplace add rohitg00/agentmemory
366
+ codex plugin install agentmemory
367
+ ```
368
+
369
+ The Codex plugin ships from the same `plugin/` directory as the Claude Code plugin. It registers:
370
+
371
+ - `@agentmemory/mcp` as an MCP server (all 51 tools when `AGENTMEMORY_TOOLS=all`)
372
+ - 6 lifecycle hooks: `SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, `PreCompact`, `Stop`
373
+ - 4 skills: `/recall`, `/remember`, `/session-history`, `/forget`
374
+
375
+ Codex's hook engine injects `CLAUDE_PLUGIN_ROOT` into hook subprocesses (per [`codex-rs/hooks/src/engine/discovery.rs`](https://github.com/openai/codex/blob/main/codex-rs/hooks/src/engine/discovery.rs)), so the same hook scripts work across both hosts without duplication. Subagent / SessionEnd / Notification / TaskCompleted / PostToolUseFailure events are Claude-Code-only and are not registered for Codex.
376
+
358
377
  <details>
359
378
  <summary><b>OpenClaw (paste this prompt)</b></summary>
360
379
 
@@ -427,7 +446,8 @@ The agentmemory entry is the **same MCP server block** across every host that us
427
446
  | **Windsurf** | `~/.codeium/windsurf/mcp_config.json` | Same `mcpServers` block. |
428
447
  | **Gemini CLI** | `~/.gemini/settings.json` | `gemini mcp add agentmemory npx -y @agentmemory/mcp --scope user` (auto-merges). |
429
448
  | **OpenClaw** | OpenClaw MCP config | Same `mcpServers` block, or use the deeper [memory plugin](integrations/openclaw/). |
430
- | **Codex CLI** | `.codex/config.toml` | TOML shape: `codex mcp add agentmemory -- npx -y @agentmemory/mcp`, or add `[mcp_servers.agentmemory]` manually. |
449
+ | **Codex CLI (MCP only)** | `.codex/config.toml` | TOML shape: `codex mcp add agentmemory -- npx -y @agentmemory/mcp`, or add `[mcp_servers.agentmemory]` manually. |
450
+ | **Codex CLI (full plugin)** | Codex plugin marketplace | `codex plugin marketplace add rohitg00/agentmemory` then `codex plugin install agentmemory`. Registers MCP + 6 lifecycle hooks (SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, PreCompact, Stop) + 4 skills. |
431
451
  | **OpenCode** | `opencode.json` | Different shape — top-level `mcp` key, command as array: `{"mcp": {"agentmemory": {"type": "local", "command": ["npx", "-y", "@agentmemory/mcp"], "enabled": true}}}`. |
432
452
  | **pi** | `~/.pi/agent/extensions/agentmemory` | Copy [`integrations/pi`](integrations/pi/) and restart pi. |
433
453
  | **Hermes Agent** | `~/.hermes/config.yaml` | Use the deeper [memory provider plugin](integrations/hermes/) with `memory.provider: agentmemory`. |
package/dist/cli.mjs CHANGED
@@ -361,12 +361,12 @@ async function main() {
361
361
  p.intro("agentmemory");
362
362
  if (skipEngine) {
363
363
  p.log.info("Skipping engine check (--no-engine)");
364
- await import("./src-d1vwmuPU.mjs");
364
+ await import("./src-Cqsy23f_.mjs");
365
365
  return;
366
366
  }
367
367
  if (await isEngineRunning()) {
368
368
  p.log.success("iii-engine is running");
369
- await import("./src-d1vwmuPU.mjs");
369
+ await import("./src-Cqsy23f_.mjs");
370
370
  return;
371
371
  }
372
372
  if (!await startEngine()) {
@@ -410,7 +410,7 @@ async function main() {
410
410
  process.exit(1);
411
411
  }
412
412
  s.stop("iii-engine is ready");
413
- await import("./src-d1vwmuPU.mjs");
413
+ await import("./src-Cqsy23f_.mjs");
414
414
  }
415
415
  async function apiFetch(base, path, timeoutMs = 5e3) {
416
416
  try {
@@ -872,7 +872,7 @@ async function runUpgrade() {
872
872
  ].join("\n"), "agentmemory upgrade");
873
873
  }
874
874
  async function runMcp() {
875
- await import("./standalone-DQrqCL0s.mjs");
875
+ await import("./standalone-DNt6O3zG.mjs");
876
876
  }
877
877
  async function runImportJsonl() {
878
878
  const VALUE_FLAGS = new Set(["--port", "--tools"]);
@@ -0,0 +1,3 @@
1
+ import { n as getImageRefCount, r as incrementImageRef, t as decrementImageRef } from "./src-Cqsy23f_.mjs";
2
+
3
+ export { decrementImageRef, incrementImageRef };
@@ -1,3 +1,3 @@
1
- import { a as deleteImage, c as saveImageToDisk, i as IMAGES_DIR, l as touchImage, o as getMaxBytes, s as isManagedImagePath } from "./src-d1vwmuPU.mjs";
1
+ import { a as deleteImage, c as saveImageToDisk, i as IMAGES_DIR, l as touchImage, o as getMaxBytes, s as isManagedImagePath } from "./src-Cqsy23f_.mjs";
2
2
 
3
3
  export { deleteImage, saveImageToDisk };
package/dist/index.mjs CHANGED
@@ -2231,7 +2231,7 @@ var SearchIndex = class SearchIndex {
2231
2231
  return this.tokenize(parts.join(" ").toLowerCase());
2232
2232
  }
2233
2233
  tokenize(text) {
2234
- return text.replace(/[^\w\s/.\-_]/g, " ").split(/\s+/).filter((t) => t.length > 1).map((t) => stem(t));
2234
+ return text.replace(/[^\p{L}\p{N}\s/.\\-_]/gu, " ").split(/\s+/).filter((t) => t.length > 1).map((t) => stem(t));
2235
2235
  }
2236
2236
  getSortedTerms() {
2237
2237
  if (!this.sortedTerms) this.sortedTerms = Array.from(this.invertedIndex.keys()).sort();
@@ -2530,13 +2530,55 @@ async function deleteAccessLog(kv, memoryId) {
2530
2530
  //#endregion
2531
2531
  //#region src/functions/search.ts
2532
2532
  let index = null;
2533
+ let vectorIndex = null;
2534
+ let currentEmbeddingProvider = null;
2533
2535
  function getSearchIndex() {
2534
2536
  if (!index) index = new SearchIndex();
2535
2537
  return index;
2536
2538
  }
2539
+ function setVectorIndex(idx) {
2540
+ vectorIndex = idx;
2541
+ }
2542
+ function setEmbeddingProvider(provider) {
2543
+ currentEmbeddingProvider = provider;
2544
+ }
2545
+ const EMBED_MAX_CHARS = 16e3;
2546
+ function clipEmbedInput(text) {
2547
+ if (text.length <= EMBED_MAX_CHARS) return text;
2548
+ return text.slice(0, EMBED_MAX_CHARS);
2549
+ }
2550
+ async function vectorIndexAddGuarded(id, sessionId, text, context) {
2551
+ const vi = vectorIndex;
2552
+ const ep = currentEmbeddingProvider;
2553
+ if (!vi || !ep) return false;
2554
+ try {
2555
+ const embedding = await ep.embed(clipEmbedInput(text));
2556
+ if (embedding.length !== ep.dimensions) {
2557
+ logger.warn("vector-index add: dimension mismatch — skipping", {
2558
+ kind: context.kind,
2559
+ id: context.logId,
2560
+ provider: ep.name,
2561
+ expected: ep.dimensions,
2562
+ received: embedding.length
2563
+ });
2564
+ return false;
2565
+ }
2566
+ vi.add(id, sessionId, embedding);
2567
+ return true;
2568
+ } catch (err) {
2569
+ logger.warn("vector-index add: embed failed — skipping", {
2570
+ kind: context.kind,
2571
+ id: context.logId,
2572
+ provider: ep.name,
2573
+ error: err instanceof Error ? err.message : String(err)
2574
+ });
2575
+ return false;
2576
+ }
2577
+ }
2537
2578
  async function rebuildIndex(kv) {
2538
2579
  const idx = getSearchIndex();
2539
2580
  idx.clear();
2581
+ vectorIndex?.clear();
2540
2582
  let count = 0;
2541
2583
  try {
2542
2584
  const memories = await kv.list(KV.memories);
@@ -2544,6 +2586,10 @@ async function rebuildIndex(kv) {
2544
2586
  if (memory.isLatest === false) continue;
2545
2587
  if (!memory.title || !memory.content) continue;
2546
2588
  idx.add(memoryToObservation(memory));
2589
+ await vectorIndexAddGuarded(memory.id, memory.sessionIds[0] ?? "memory", memory.title + " " + memory.content, {
2590
+ kind: "memory",
2591
+ logId: memory.id
2592
+ });
2547
2593
  count++;
2548
2594
  }
2549
2595
  } catch (err) {
@@ -2568,6 +2614,10 @@ async function rebuildIndex(kv) {
2568
2614
  if (failedSessions.length > 0) logger.warn("rebuildIndex: failed to load observations for sessions", { failedSessions });
2569
2615
  for (const observations of obsPerSession) for (const obs of observations) if (obs.title && obs.narrative) {
2570
2616
  idx.add(obs);
2617
+ await vectorIndexAddGuarded(obs.id, obs.sessionId, obs.title + " " + obs.narrative, {
2618
+ kind: "observation",
2619
+ logId: obs.id
2620
+ });
2571
2621
  count++;
2572
2622
  }
2573
2623
  return count;
@@ -2874,6 +2924,10 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
2874
2924
  const synthetic = buildSyntheticCompression(raw);
2875
2925
  await kv.set(KV.observations(payload.sessionId), obsId, synthetic);
2876
2926
  getSearchIndex().add(synthetic);
2927
+ await vectorIndexAddGuarded(synthetic.id, synthetic.sessionId, synthetic.title + " " + (synthetic.narrative || ""), {
2928
+ kind: "synthetic",
2929
+ logId: synthetic.id
2930
+ });
2877
2931
  await sdk.trigger({
2878
2932
  function_id: "stream::set",
2879
2933
  payload: {
@@ -4118,7 +4172,20 @@ function registerCompressFunction(sdk, kv, provider, metricsStore) {
4118
4172
  ...data.raw.imageData ? { imageRef: data.raw.imageData } : {}
4119
4173
  };
4120
4174
  await kv.set(KV.observations(data.sessionId), data.observationId, compressed);
4121
- getSearchIndex().add(compressed);
4175
+ try {
4176
+ getSearchIndex().add(compressed);
4177
+ } catch (err) {
4178
+ logger.warn("Failed to index compressed observation into BM25", {
4179
+ obsId: compressed.id,
4180
+ sessionId: compressed.sessionId,
4181
+ title: compressed.title,
4182
+ error: err instanceof Error ? err.message : String(err)
4183
+ });
4184
+ }
4185
+ await vectorIndexAddGuarded(compressed.id, compressed.sessionId, compressed.title + " " + (compressed.narrative || ""), {
4186
+ kind: "observation",
4187
+ logId: compressed.id
4188
+ });
4122
4189
  const streamResults = await Promise.allSettled([sdk.trigger({
4123
4190
  function_id: "stream::set",
4124
4191
  payload: {
@@ -4958,6 +5025,10 @@ function registerRememberFunction(sdk, kv) {
4958
5025
  error: err instanceof Error ? err.message : String(err)
4959
5026
  });
4960
5027
  }
5028
+ await vectorIndexAddGuarded(memory.id, memory.sessionIds[0] ?? "memory", memory.title + " " + memory.content, {
5029
+ kind: "memory",
5030
+ logId: memory.id
5031
+ });
4961
5032
  if (supersededId) await sdk.trigger({
4962
5033
  function_id: "mem::cascade-update",
4963
5034
  payload: { supersededMemoryId: supersededId },
@@ -5755,7 +5826,7 @@ function registerAutoForgetFunction(sdk, kv) {
5755
5826
 
5756
5827
  //#endregion
5757
5828
  //#region src/version.ts
5758
- const VERSION = "0.9.10";
5829
+ const VERSION = "0.9.12";
5759
5830
 
5760
5831
  //#endregion
5761
5832
  //#region src/functions/export-import.ts
@@ -5883,7 +5954,9 @@ function registerExportImportFunction(sdk, kv) {
5883
5954
  "0.9.7",
5884
5955
  "0.9.8",
5885
5956
  "0.9.9",
5886
- "0.9.10"
5957
+ "0.9.10",
5958
+ "0.9.11",
5959
+ "0.9.12"
5887
5960
  ]).has(importData.version)) return {
5888
5961
  success: false,
5889
5962
  error: `Unsupported export version: ${importData.version}`
@@ -19450,6 +19523,8 @@ async function main() {
19450
19523
  const metricsStore = new MetricsStore(kv);
19451
19524
  const dedupMap = new DedupMap();
19452
19525
  const vectorIndex = embeddingProvider ? new VectorIndex() : null;
19526
+ setVectorIndex(vectorIndex);
19527
+ setEmbeddingProvider(embeddingProvider);
19453
19528
  initMetrics(hasGetMeter(sdk) ? sdk.getMeter.bind(sdk) : void 0);
19454
19529
  registerPrivacyFunction(sdk);
19455
19530
  registerObserveFunction(sdk, kv, dedupMap, config.maxObservationsPerSession);