Commit Graph

93 Commits

Author SHA1 Message Date
Rohit Ghumare 25158519d5 chore(release): collapse v0.9.28 into v0.9.27 (not tagged yet) (#851) 2026-06-07 09:55:51 +01:00
Rohit Ghumare a6e22fa9c7 fix(release): v0.9.28 hotfix wave (#843, #825, #817, iii pin) (#849)
* fix(release): v0.9.28 hotfix wave (data loss, graph reset, agent leak, iii pin)

* fix(graph,search): cr followups for #843/#825/#817 wave

* docs(readme): warn import-jsonl users about Claude Code 30-day cleanup
2026-06-07 01:47:56 +01:00
Rohit Ghumare a323fb0b96 chore(release): v0.9.27 (#827) 2026-06-05 14:00:21 +01:00
Rohit Ghumare 7446bded53 fix(release): v0.9.26 hotfix for first-run load crash (#797) (#804)
First boot after upgrading to v0.9.25 threw
'TypeError: Cannot read properties of undefined (reading v)' from
IndexPersistence.loadManifestData. Some iii-state adapters return
undefined (not null) for a missing key. The load path checked
manifest.value !== null and forwarded undefined to loadManifestData,
which then read undefined.v.

Self-healing: the next debounced save rebuilt a fresh manifest, so
operation continued after the warning — but the error scared every
fresh upgrader.

Fix: treat null + undefined + non-object values as 'no manifest' and
fall through to the legacy load path. Two regression tests:

  - kv.get returning undefined surfaces as a clean { bm25: null,
    vector: null } load, no throw.
  - kv.get returning a string (wrong shape) does not crash the
    manifest path.

Files bumped (9):
  package.json, plugin/.claude-plugin/plugin.json, plugin/plugin.json,
  plugin/.codex-plugin/plugin.json, src/version.ts, src/types.ts,
  src/functions/export-import.ts, test/export-import.test.ts,
  packages/mcp/package.json

125 test files / 1381 tests pass.

Closes #797.
2026-06-03 10:16:04 +01:00
Rohit Ghumare d442fee78f chore(release): v0.9.25 (#792)
Bump to 0.9.25 across 9 files + CHANGELOG.

Closes #778 #775 #783 (PR #791), #758 #726 (PR #773), #759 (PR #772),
#752 (PR #774), #729 (PR #780), #781 (PR #782), #753 (PR #789), #771
(PR #786), #762 (PR #764).

Files bumped:
  - package.json
  - packages/mcp/package.json
  - plugin/.claude-plugin/plugin.json
  - plugin/.codex-plugin/plugin.json
  - plugin/plugin.json
  - src/version.ts
  - src/types.ts (ExportData.version union)
  - src/functions/export-import.ts (supportedVersions Set)
  - test/export-import.test.ts (assertion)

125 test files / 1379 tests pass. npm audit (root + website): 0 vulns.
2026-06-03 00:32:56 +01:00
Rohit Ghumare fd9e3bd42d chore(release): v0.9.24 — --version flag + iii hard-pin enforcement (#719)
Two bugs surfaced in the first hour after v0.9.23 hit npm:

1. `agentmemory --version` silently launched the server instead of
   printing the version. `-v` was reserved by --verbose and no
   --version literal was handled — fell through to default `start`.
   Now --version (and -V capital, POSIX) prints VERSION + exits 0
   before any side effects.

2. iii-engine version pin was warn-only. When the engine on PATH
   didn't match agentmemory's pinned v0.11.2, the worker still booted
   against the mismatched engine and crashed at runtime with
   state::list not found (v0.13.0) or sandbox traps (v0.11.6).
   warnIfEngineVersionMismatch renamed to enforceEngineVersionPin
   and now p.log.errors + process.exit(1). AGENTMEMORY_III_VERSION
   override unchanged.
2026-05-29 10:23:29 +01:00
Rohit Ghumare 59ab3281dd chore(release): v0.9.23 (#702)
* chore(release): v0.9.23

Bumps version across 9 files + adds CHANGELOG entry summarizing the
18 commits since v0.9.22.

Highlights:
- GitHub Copilot CLI first-class support (#534) — plugin + hooks +
  MCP with LSP-style Content-Length framing on the standalone stdio
  transport.
- Five new MCP adapters: Warp, Cline, Continue, Zed, Droid (#677);
  ADAPTERS count 11 → 17.
- Three silent DX bugs fixed: graph extraction never fired on
  session end (#666 / #698), status reported zero memories (#666),
  consolidation defaulted off even with an LLM provider configured
  (#612 / #696).
- Nine telemetry hooks switched to fire-and-forget so they don't
  block Claude Code's next-prompt boundary (#573 / #688).
- Hook project field now sends repo basename instead of full
  filesystem path so auto-injected context isn't silently filtered
  out (#474 / #687).
- Local-LLM docs: Ollama / LM Studio / vLLM section added (#671 /
  #697).

Version-bump files: package.json, plugin/.claude-plugin/plugin.json,
plugin/plugin.json, plugin/.codex-plugin/plugin.json,
packages/mcp/package.json, src/version.ts, src/types.ts,
src/functions/export-import.ts, test/export-import.test.ts.

* chore(release): add #701 + #709 to v0.9.23 CHANGELOG
2026-05-28 14:00:13 +01:00
Ross Story a0da02b6b3 Add GitHub Copilot CLI support (#534)
* feat: add Copilot CLI plugin asset slice

- plugin/.plugin/plugin.json: Copilot manifest with name/version/skills/mcpServers/hooks refs
- plugin/.mcp.copilot.json: MCP server config with type:local, npx, env passthrough, tools:[*]
- plugin/hooks/hooks.copilot.json: Copilot hooks (version:1) with 11 supported events and PreToolUse matcher
- test/copilot-plugin.test.ts: 11 tests covering manifest, MCP config, and hooks validation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add Copilot CLI connect support

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add GitHub Copilot CLI support

Adds Copilot CLI support through a root plugin manifest, Copilot-specific MCP and hook configuration, and a connect adapter for MCP-only setup.

Includes Windows-safe Copilot MCP command generation, COPILOT_HOME handling, Copilot hook payload normalization, generated hook scripts, and targeted tests for plugin shape, hook execution, and connect behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Harden Copilot hook handling

Addresses upstream AI review suggestions by aligning the Copilot preToolUse matcher with the hook allowlist, narrowing hook payload fields at runtime, normalizing subagent fallbacks, and tightening hook config validation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add Copilot to first-run onboarding

Includes GitHub Copilot CLI in the first-run agent picker and adds a regression test so the Copilot setup path remains discoverable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Default onboarding to Copilot inside Copilot CLI

Detect Copilot CLI environment markers during first-run setup so pressing Enter wires the current agent instead of the historical Claude Code default.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Support framed stdio MCP transport

Accept Content-Length framed JSON-RPC messages in addition to the existing newline-delimited transport so Copilot CLI can initialize the standalone MCP server.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Narrow Copilot pre-tool session ids

Ensures pre-tool-use only forwards string session IDs and falls back to unknown for invalid Copilot payload values, with regression coverage for the generated plugin script.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Ross Story <rostory@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Rohit Ghumare <ghumare64@gmail.com>
2026-05-28 09:54:54 +01:00
Rohit Ghumare d626b4ea60 perf(hooks): fire-and-forget telemetry hooks (#573) (#688)
* perf(hooks): fire-and-forget telemetry hooks (closes #573)

Telemetry hooks (notification, post-tool-failure, post-tool-use,
prompt-submit, stop, session-end, subagent-start, subagent-stop,
task-completed) previously `await fetch(..., AbortSignal.timeout(N))`
inside a try/catch. The await kept the hook process alive until the
response arrived — up to N ms per request — which blocks Claude Code's
next-prompt boundary on every assistant turn.

Switch to fire-and-forget:

  fetch(url, { signal: AbortSignal.timeout(N) }).catch(() => {});
  setTimeout(() => process.exit(0), 500).unref();

The unawaited fetch dispatches the request; the unref'd setTimeout
force-exits the process after the request has been flushed to the
local daemon's socket buffer (~500ms is enough). Without the
setTimeout Node keeps the event loop alive waiting for any in-flight
fetch to settle, which means the hook still blocks Claude Code's
next-prompt boundary for up to the AbortSignal duration.

Context-injecting hooks (pre-tool-use, pre-compact, session-start)
still use `await fetch` because Claude Code reads their stdout for
context injection — left untouched.

AGENTS.md updated with the two-pattern guidance.

* chore(hooks): drop verbose comments on fire-and-forget hooks

* fix(hooks): bump stop+session-end exit delay to 1500ms

Multi-request hooks (stop fires 2, session-end up to 4) need more
than 500ms to initiate all fetches when AGENTMEMORY_URL points to a
remote daemon — DNS + TCP + TLS handshakes can eat the budget before
the second/third fetch is even dispatched. Bump to 1500ms on those
two hooks only; single-request hooks keep 500ms.

AGENTS.md updated with the multi-request exception.
2026-05-27 20:51:23 +01:00
Rohit Ghumare 0468407249 fix(hooks): send repo basename as project, not full path (#474) (#687)
* fix(hooks): send repo basename as project, not full path (closes #474)

Hooks were sending `data.cwd` (an absolute filesystem path) as the
`project` field on every observe/session/start call. Native sessions,
replay-import, and manual memory_lesson_save calls all use the repo
basename. The mismatch caused auto-injected context to filter out the
bulk of relevant lessons because the path never matches the stored
project name.

Add shared `resolveProject(cwd)` helper:
  1. AGENTMEMORY_PROJECT_NAME env (per-repo escape hatch)
  2. basename of `git rev-parse --show-toplevel` (handles subdirs)
  3. basename of cwd (final fallback when not in a git repo)

Applied to 9 hooks: notification, post-tool-use, post-tool-failure,
prompt-submit, session-start, subagent-start, subagent-stop,
task-completed, pre-compact.

Build: split hook entries into per-entry tsdown configs so each hook
bundles into a fully self-contained .mjs. Previous shared config
hoisted helpers into hashed chunks that changed on every rebuild.

* chore(hooks): drop issue-number ref from resolveProject comment

* chore: trim verbose comments on _project.ts + tsdown.config
2026-05-27 20:32:54 +01:00
Rohit Ghumare 6939d4a863 docs(readme): refresh counts + version refs for v0.9.22 (#660)
* docs(readme): refresh counts + version refs for v0.9.22

- 51 MCP tools → 53 (actual getAllTools() count)
- 4 skills → 8 (recall, remember, session-history, forget, recap,
  handoff, commit-context, commit-history)
- Embedded path example v0.9.21 → v0.9.22
- 'New in v0.9.0' callout replaced with v0.9.22 highlights
- Plugin manifest descriptions bumped to match (53 tools, 8 skills)

No code changes; 1171/1171 tests still pass.

* docs(readme): refresh gist counts, third-party logos, tool-count refs, stat SVGs

- gist badge: 1200/172 to 1.3k/182 (current live counts)
- stat-tools.svg + light: 43 to 53 MCP tools
- stat-tests.svg + light: 950+ to 1171+ tests
- Claude / Cursor / Windsurf logos: switched off brittle third-party hosts (matthiasroder.com, freelogovectors.net, exafunction.github.io) to github.com/<org>.png redirects
- all 51 memory tools to 53 in OpenClaw + Hermes setup prompts
- full 51-tool surface to 53 in MCP shim vs full-server callout
- Extended tools (51 total) to 53 in MCP table summary

* docs(readme): swap Cursor/Windsurf/OpenCode logos to svgl.app brand SVGs

github.com/<org>.png pulled personal avatars (getcursor ghost mascot, Exafunction green box, opencode-ai org icon) instead of product logos. svgl.app library serves the actual brand SVGs with light/dark variants — use <picture> with prefers-color-scheme for theme-aware rendering.
2026-05-26 12:34:54 +01:00
Rohit Ghumare fd197dfda4 chore(release): v0.9.22 (#659)
Bump version across package.json + plugin manifests + version.ts +
types.ts ExportFormat union + export-import allow-list.

Strip issue-number stamps (// #NNN:) from new-wave source + test
files. CHANGELOG.md v0.9.22 entry bundles every PR merged since
v0.9.21 across Fixed / Added / Docs / Infrastructure.

1171/1171 vitest pass.
2026-05-26 10:53:03 +01:00
Rohit Ghumare 426bbdd352 fix(mcp): full 51-tool surface by default + env defaults in plugin manifest (#650)
* fix(mcp): full 51-tool surface by default + env defaults in plugin manifest

Two user-blocking MCP issues, single root surface.

#510 Claude Code drops the agentmemory MCP server silently when
AGENTMEMORY_URL is unset. Per Claude Code MCP docs: 'If a required
environment variable is not set and has no default value, Claude Code
will fail to parse the config' — and a parse failure removes the server
from /reload-plugins output with no warning. plugin/.mcp.json now uses
${VAR:-default} form for AGENTMEMORY_URL, AGENTMEMORY_SECRET, and
AGENTMEMORY_TOOLS so fresh installs Just Work without exporting envs.

#553 OpenCode (and Claude Code, and Codex) saw 8 tools instead of the
51 every plugin manifest advertises. getVisibleTools() defaulted to
AGENTMEMORY_TOOLS=core which capped at 8 ESSENTIAL_TOOLS. README has
always said '51 MCP tools' — the default now matches the advertising.
AGENTMEMORY_TOOLS=core still available for users who want the lean set.

CLI help text and existing codex-plugin test updated to the new
contract. New test/mcp-surface-default.test.ts adds 4 regression cases
covering default, all, core, and the env-default form in the plugin
manifest.

1141/1141 vitest pass.

* fix(connect): apply same env defaults to wired-in MCP block

PR #650 fixed plugin/.mcp.json but not src/cli/connect/util.ts —
`npx @agentmemory/agentmemory connect` (Cursor, Gemini CLI, Windsurf,
all JSON-MCP agents) was still writing ${VAR} form into user configs,
keeping #510 unfixed on every non-Claude-Code-plugin path. util.ts now
ships the same ${VAR:-default} form with AGENTMEMORY_TOOLS=all.
Codex TOML adapter already used a literal URL — unaffected. Hermes
connect just prints YAML guidance — unaffected.

test/cli-connect.test.ts: updated existing assertion to the new
contract.
2026-05-25 20:27:26 +01:00
Rohit Ghumare f027c20309 fix(multi): stability pass for #627 #640 #474 #638 #431 #544 #563 (#648)
* fix(multi): stability pass for #627 #640 #474 #638 #431 #544 #563

Six issues, one PR. Each lands with a targeted regression test;
1119/1119 vitest pass.

#627 OpenAI thinking-model fallback
  src/providers/openai.ts now reads message.reasoning_content alongside
  message.reasoning. DeepSeek V4 / Qwen3 / GLM / Kimi return the
  underscored field — previously compress silently failed (0/700 calls)
  and the circuit breaker tripped.

#640 + #474 stop reaps the worker process
  src/index.ts writes ~/.agentmemory/worker.pid on registerWorker, clears
  it on graceful shutdown. src/cli.ts runStop now reads the worker pidfile
  and signals SIGTERM alongside the engine pids. Fixes both: the daemon
  wrapper surviving stop (#640) and the iii engine retaining stale
  function registrations because the worker reconnected to the new
  engine (#474).

#638 OpenCode session implicit-create on observe
  src/functions/observe.ts now creates the session record on the first
  observation when project + cwd are present and no session exists.
  OpenCode plugins (and any caller that skips POST /session/start) no
  longer leak observations into a session memory_sessions never lists,
  and summarize stops bailing with 'Session not found'.

#431 OpenCode auto-context (zero-config injection)
  plugin/opencode/agentmemory-capture.ts captures the context returned
  by POST /session/start into a per-session cache. The existing
  experimental.chat.system.transform hook now reads from the cache
  first, falls back to /context. Cleanup on session.deleted.

#544 paginated /memories + /export
  src/triggers/api.ts adds three query modes to /memories:
    ?count=true       — totals only, viewer status badge
    ?limit=N&offset=M — paged slice, default unlimited
  /export now forwards maxSessions + offset query params to mem::export
  (which already supported them). Viewer dashboard caps the memories
  fetch at 500; the memories tab at 2000. Both stop the iii invocation
  timeout from masking real corpora as 0 memories.

#563 viewer graph cool-down on >1000 nodes
  src/viewer/index.html adds tick-decayed damping (coolBoost), per-node
  velocity caps tiered by node count, and quiescence-based raf parking.
  Mousedown wakes the parked loop. Dense graphs settle instead of
  bouncing forever; CPU returns to idle once the layout is quiet.

#637 Windows em-dash ByteString — deferred to follow-up
  Cannot reproduce on macOS / Linux. The user-suggested defensive
  encoding fix is unsafe without a Windows repro confirming the actual
  exception path. Will land separately once a Windows runner or the
  reporter can validate.

* fix(multi): address review findings on PR #648

Addresses inline review on PR #648 — verified each finding against
current code and fixed the still-valid ones.

opencode plugin: snapshot activeSessionId into a local 'sessionId'
before await postJson('/session/start') — a second session.created
event during the await could rebind activeSessionId and cache the
context against the wrong key. The cache write + observe call now use
the snapshotted id.

src/cli.ts: clearWorkerPidfile() now runs in every stop branch:
  - Docker engine-not-running early return
  - Docker stopDockerEngine path
  - native engine-not-running 'Nothing to stop'
  - native happy path (was already there)
The worker pid is now read up front so the engine-down branch can also
reap an orphaned worker process (previously fell through to 'preserve
for manual cleanup'). A new dedicated branch reaps the worker and
exits cleanly when only the worker is lingering.

src/viewer/index.html: wakeGraphSim() shared helper consolidates the
quietTicks reset + raf restart pattern. Wheel handler, zoomGraph(),
recenterGraph(), and mousedown all now wake the parked simulation so
zoom/pan/click feedback is immediate after the layout has settled.
graphSim object initializes quietTicks: 0 alongside tickCount: 0.

src/functions/observe.ts: dedupe new Date().toISOString() into a
single 'ts' local for the implicit-create path so startedAt and
updatedAt stay consistent.

test/opencode-auto-context.test.ts: regex updated to assert the
snapshot-then-cache pattern instead of the previous direct
activeSessionId reference.

1119/1119 vitest pass.
2026-05-25 19:45:58 +01:00
Rohit Ghumare 3551241416 fix(hooks): stop also closes session for Codex (closes #493) (#579)
Codex does not fire a separate SessionEnd event, so its Stop hook is
the only signal we get when a Codex session terminates. The current
stop hook only POSTs /agentmemory/summarize, leaving the session row
stuck on status:"active" in the viewer for Codex users (#493).

Stop now ALSO POSTs /agentmemory/session/end, best-effort with a
short 5s timeout. For Claude Code this is a harmless idempotent second
call (session-end.mjs runs on the dedicated SessionEnd hook and sets
the same endedAt + status fields). For Codex it's the only path that
closes the lifecycle.

Tests (1081) + build pass. plugin/scripts/stop.mjs regenerated by tsdown.
2026-05-22 19:23:08 +01:00
Faraz Ahmed 3cb7f90894 fix: read tool_response instead of tool_output in PostToolUse hook (#561)
* fix: read tool_response instead of tool_output in PostToolUse hook

Claude Code's PostToolUse payload sends the field as `tool_response`,
not `tool_output`. The hook was reading `data.tool_output` which is
always undefined, so `cleanOutput` was undefined, the observe request
contained no `tool_output` value, and mem::compress consistently failed
its XML schema validation (requires narrative >= 10 chars + facts >= 1).

Fix: read `data.tool_response` with `data.tool_output` as a fallback
so older integrations that emit the legacy field name keep working.

Fixes #539

* style: remove explanatory comment per repo guidelines
2026-05-20 16:33:42 +01:00
Rohit Ghumare 1838f4d74c chore(release): v0.9.21 (#551)
Quality + integration wave. Bundles 11 PRs since v0.9.20:

Contributor feature:
- #237 OpenCode plugin with 22 auto-capture hooks (@cl0ckt0wer)

Bug fixes (9):
- #516 memory_recall endpoint + format/token_budget (@serhiizghama, closes #507/#440)
- #461 env-file AGENTMEMORY_DROP_STALE_INDEX flag honored (@honor2030, closes #456)
- #487 Windows hook path quoting (@honor2030, closes #477)
- #517 viewer IME composition guard (@jonathanzhan1975)
- #472 chunk large sessions for LLM context window (@efenex)
- #473 surface lessons in smart-search + diagnose tally (@efenex)
- #486 declare all Hermes plugin hooks (@honor2030)
- #500 rebuildIndex non-blocking on boot (@efenex)
- #504 batched embed in rebuildIndex (25h -> 3h) (@efenex)
- #491 cli skip onboarding without tty (@honor2030)

Upstream-installer revert:
- #546 drop --next workaround now that iii-hq/iii#1660 shipped

1067/1067 tests pass across 95 files.
2026-05-19 19:37:19 +01:00
이민재 48bf700f62 fix(hooks): quote plugin script paths (#487)
Co-authored-by: honor2030 <19909783+honor2030@users.noreply.github.com>
2026-05-19 12:59:22 +01:00
Trip 68fddd418e feat: OpenCode plugin with 22 auto-capture hooks (closes #236, #244) (#237)
* feat: OpenCode plugin with 22 auto-capture hooks (closes #156)

- 22 hook handlers across session lifecycle, messages, tool lifecycle, parts, files, permissions, tasks, commands, and config
- Two-layer enrichment pipeline: /context + /enrich via system.transform
- Two slash commands: /recall and /remember
- Full Claude Code hook parity documented with gap analysis

* fix(plugin): use prompt_submit hookType so sessions get firstPrompt

mem::observe checks hookType === "prompt_submit" to extract raw.userPrompt
and set session.firstPrompt. The plugin was using "user_prompt_submit" which
didn't match, so sessions were never named.

* fix: address CodeRabbit review feedback on OpenCode plugin

- Use ctx.worktree for projectPath instead of opaque project.id
- Add Array.isArray guards before output.system/.context.push()
- Only delete stashed files after successful enrich POST
- Defensive JSON.stringify for undefined tool inputs
- Per-session Map-based dedup sets to prevent unbounded growth
- Fix negative duration_ms when time.completed is unset
- Validate props.file and enforce MAX_STASHED_FILES in file.edited
- Deduplicate /summarize call on session idle
- Buffer early config events until session.created flushes them
- Move contextInjectedSessions.add after successful context fetch
- Add DEBUG-gated error logging to network helpers
- Guard config input.agent/mcp/provider against non-object types
- Fix MCP badge count 44→51 in plugin README

* fix: add language identifier to fenced code block in plugin README

* fix: store OpenCode session title as summary/firstPrompt on creation

* fix(plugin): add session instruction injection and consolidation pipeline (closes #233)

Three gaps from the Claude Code plugin port sweep:

- Inject agentmemory usage instructions (memory_save, memory_recall, etc.) into the system prompt on first turn via experimental.chat.system.transform, replacing the skills mechanism that OpenCode lacks
- Call /crystals/auto and /consolidate-pipeline on session.deleted, mirroring Claude's CONSOLIDATION_ENABLED behavior
- Document MEMORY.md vs AGENTS.md architecture comparison (two-hop file bridge vs one-hop direct injection)

Gap A (SubagentStop) is unfixable — OpenCode's SubtaskPart type has no completion/result fields.
Gap C (Claude MEMORY.md bridge) is intentionally skipped — OpenCode uses direct injection.

* fix(plugin): address adversarial code review — 7 critical/high fixes

- Guard instructions push with Array.isArray check + fix TOCTOU race
  by moving contextInjectedSessions.add() before the await
- Session-scope stashedFiles via stashFor() helper (was process-global,
  could cross-contaminate concurrent sessions)
- Fix tool prefix in instructions (agentmemory_memory_ not agentmemory_)
- Check typeof string before pushing .context into system arrays
- Change olderThanDays: 0 → 7 in consolidation fire-and-forget
- Increase fire-and-forget timeout from 5s to 30s (consolidation
  takes minutes, 5s was guaranteed to abort)

* fix(plugin): scope stashedFiles.delete(sid) not .clear() on session.deleted

* fix(plugin): address round 2 adversarial review — 3 critical fixes

- Fix session cross-contamination: file.edited and tool.execute.before
  now use props.sessionID/input.sessionID fallback instead of only
  activeSessionId, preventing subagent hijack of parent stash
- Fix contextInjectedSessions regression from round 1: move add(sid)
  to after instructions push (synchronous) but before context fetch
  (async), so failed /context calls don't permanently skip injection
- Fix Map memory leak: prune session entries (stashedFiles,
  seenSubtaskIds, seenToolCallIds) when session.status goes idle,
  preventing unbounded growth for crash-killed sessions

* fix(plugin): address round 2 remaining medium issues

- Enforce MAX_STASHED_FILES cap on chat.message (was missing, could
  grow unbounded from 50+ file-ref messages)
- Remove activeSessionId fallback from session.deleted; log warning
  when both info.id and sessionID are missing instead of guessing
- Add subtaskSetFor/toolCallSetFor lazy-init wrappers matching
  stashFor pattern; fixes dedup failures for subagents spawned
  without a preceding session.created event
- Guard chat.params against missing input.model (TypeError crash)

* fix(plugin): address round 3 adversarial review — 10 HIGH/MEDIUM fixes

- Add safeSlice() helper replacing all unsafe (v as string || "").slice()
  calls; handles objects/BigInt/circular refs via try/catch JSON.stringify
- Restore activeSessionId fallback on session.deleted (if both info.id
  and sessionID missing, fall back like every other handler)
- Fix message.updated to check info.id before info.sessionID (matches
  session.created/session.updated pattern)
- Add props.sessionID to message.part.updated fallback chain
- Guard undefined callID and subtask ID (prevent dedup silently
  dropping all subsequent undefined-ID events)
- Cap todo.updated at 100 entries (match session.diff's slice pattern)
- Fix duration_ms || → ?? (0ms genuine duration no longer falsifies)
- Fix extractErrorMessage || → ?? (0/false error values preserved)
- Replace extractErrorMessage.slice with safeSlice in retry handler

* fix(plugin): address round 4 adversarial review — 6 CRITICAL/HIGH fixes

CRITICAL:
- Revert message.updated session ID resolution: info.id is the message
  UUID, not session ID. In message.updated, info is the message object
  (has role/tokens/modelID), not session info like in session.created.
  Use props.sessionID || info.sessionID || activeSessionId instead.
- Fix post_tool_use_failure -> post_tool_failure: server types.ts
  defines "post_tool_failure" (no "use_"); compress-synthetic.ts
  classifies by that exact string. All error observations were
  misclassified as "other" instead of "error".
- Fix contextInjected ordering: move add(sid) after the context fetch
  completes, not before. If /context times out, session is no longer
  permanently marked injected, allowing retry on next transform.

HIGH:
- Add process.cwd() fallback for projectPath (was null, causing
  silent 400s on every REST call when no workspace)
- Guard session.start against null activeSessionId with early return
  (was sending sessionId: null to API)
- Fix duration_ms: use typeof number checks instead of || 0 defaults;
  missing timing data now correctly reports null instead of 0ms

* fix(plugin): address final adversarial blockers

---------

Co-authored-by: xuli500177 <62830942+xuli500177@users.noreply.github.com>
Co-authored-by: Trip <5579540+cl0ckt0wer@users.noreply.github.com>
Co-authored-by: Rohit Ghumare <ghumare64@gmail.com>
2026-05-19 00:22:42 +01:00
Rohit Ghumare 08d781d431 chore(release): v0.9.20 — hotfix Codex Stop revert (#501)
v0.9.19 shipped #495 which chained session-end.mjs after stop.mjs on
the Codex Stop hook. Field-testing surfaced the underlying issue:
Codex fires Stop multiple times within a single conversation (once
per assistant turn), so chaining session-end marked sessions as
completed while later observations were still arriving.

#501 reverts the chain. Stop returns to summarize-only behavior. The
SessionEnd-shaped solution (a dedicated terminate event the agent
sends only once on real session end) tracks at #493.

Files bumped (9):
- package.json, packages/mcp/package.json
- plugin/.claude-plugin/plugin.json, plugin/.codex-plugin/plugin.json
- src/version.ts, src/types.ts
- src/functions/export-import.ts
- test/export-import.test.ts
- CHANGELOG.md

1034/1034 tests pass.
2026-05-18 19:20:48 +01:00
Nanami a953cadb62 Revert "fix: end Codex sessions on Stop hook (#495)" (#501)
This reverts commit abeec1d0b7.

Signed-off-by: Nanami <Rex57@users.noreply.github.com>
Co-authored-by: Nanami <Rex57@users.noreply.github.com>
2026-05-18 19:17:26 +01:00
Rohit Ghumare bb259acedc chore(release): v0.9.19 — route around upstream iii-console installer bug
`install.iii.dev/console/main/install.sh` filters releases with
`startswith("v")` while `iii-hq/iii` tags as `iii/v0.12.0` —
slash-prefixed. Every stable release is filtered out and the script
bails with "no stable iii release found", breaking first-run console
install for every fresh agentmemory user.

Switch our prompt to pass `--next` through the installer. The next-
release lookup uses a regex on `-next.` without the buggy
`startswith` constraint, so it succeeds against the same tag set and
installs the most recent prerelease. Inline comment documents the
upstream tag-prefix bug + the revert condition for when the
installer is patched.

Files bumped (9):
- package.json
- packages/mcp/package.json
- plugin/.claude-plugin/plugin.json
- plugin/.codex-plugin/plugin.json
- src/version.ts
- src/types.ts
- src/functions/export-import.ts
- test/export-import.test.ts
- CHANGELOG.md

1007/1007 tests pass.
2026-05-18 13:57:23 +01:00
Rohit Ghumare caa9f5223e fix(plugin): inherit AGENTMEMORY_URL/SECRET via env passthrough (#375) (#460)
The bundled plugin .mcp.json + the `agentmemory connect <agent>` wiring
both hardcoded the MCP server to localhost:3111 with no env-var seam.
Users running agentmemory remotely (k8s cluster-internal DNS, reverse
proxy with API key auth) hit a permanent /doctor warning because they
had to add a second entry with the right AGENTMEMORY_URL + SECRET, and
both entries fought for the "agentmemory" name.

Both files now use ${AGENTMEMORY_URL} / ${AGENTMEMORY_SECRET}
expansion. Host (Claude Code, Cursor, etc.) substitutes the shell
value at MCP-server launch; when vars are unset the host passes empty
string, which the standalone shim treats as missing and falls back to
http://localhost:3111. One wired entry now covers both local and
remote without duplicates.

- plugin/.mcp.json: env block added with ${VAR} expansion
- src/cli/connect/util.ts: AGENTMEMORY_MCP_BLOCK env updated; comment
  documents the empty-string fallback contract
- test/cli-connect.test.ts: regression test asserting the env block
  shape after install() — protects against future regressions where
  the literal hardcoded URL leaks back in

1008/1008 tests pass.
2026-05-18 11:22:05 +01:00
Nanami abeec1d0b7 fix: end Codex sessions on Stop hook (#495)
Signed-off-by: Nanami <Rex57@users.noreply.github.com>
Co-authored-by: Nanami <Rex57@users.noreply.github.com>
2026-05-18 11:20:40 +01:00
Rohit Ghumare 249d8ff032 feat(commits): link agent sessions to git commits (#498)
* feat(commits): link agent sessions to git commits

Adds a commit-link layer so memories captured during an agent session
can be traced back to the git commit they produced, and vice versa.

What ships:

- KV.commits namespace storing CommitLink records keyed by full SHA.
- Session.commitShas[] back-reference for fast forward lookup.
- POST /agentmemory/session/commit upserts a link (sha, branch, repo,
  message, author, authoredAt, files, sessionId); merges sessionIds on
  re-link and preserves linkedAt.
- GET /agentmemory/session/by-commit?sha= hydrates the link plus its
  sessions.
- GET /agentmemory/commits?branch=&repo=&limit= lists recent links,
  filtered and sorted desc by linkedAt, limit clamp 1..500 (default 100).
- src/hooks/post-commit.ts shells git rev-parse/log/diff-tree and POSTs
  to /agentmemory/session/commit. Honors AGENTMEMORY_URL,
  AGENTMEMORY_SECRET, AGENTMEMORY_CWD, AGENTMEMORY_SESSION_ID,
  AGENTMEMORY_COMMIT_SHA. Best-effort, 1500 ms timeout. Wired into
  tsdown hookEntries so it bundles to dist/hooks/ and plugin/scripts/.
- Two MCP tools: memory_commit_lookup, memory_commits.
- Four user-invocable plugin skills:
  - commit-context: traces a file/function/line via git blame plus
    memory_commit_lookup plus memory_recall.
  - commit-history: lists agent-linked commits; parses branch=/repo=/
    limit= from $ARGUMENTS.
  - handoff: resumes the most recent session for cwd; surfaces an
    unanswered user-facing question before the brief.
  - recap: summarizes the last N sessions for cwd, grouped by date;
    parses last <n> / today / this week.

Wire-up to capture from a real repo:

    ln -sf "$(realpath node_modules/agentmemory/dist/hooks/post-commit.mjs)" .git/hooks/post-commit
    chmod +x .git/hooks/post-commit

No new dependencies. Additive only.

* fix(commits): address CI + review findings on commit-link feature

- Update mcp-standalone.test.ts to expect 14 CORE_TOOLS (was 12) now that
  memory_commit_lookup and memory_commits are registered. Restores CI on
  Node 20 and 22.
- Wrap CommitLink upsert and session.commitShas mutation in withKeyedLock
  to prevent lost updates when concurrent post-commit hooks race against
  each other or against api::session::commit callers. Locks keyed by
  `commit:<sha>` and `session:<sessionId>` so unrelated writers do not
  serialize.
- Parallelize hydration of linked sessions via Promise.all in both
  api::session::by-commit and the memory_commit_lookup MCP handler;
  serial kv.get loops dragged response time for commits with many
  sessions.
- Sharpen handoff SKILL.md project-path matching: resolve relative
  $ARGUMENTS to an absolute normalized path and require directory-
  boundary equality, not raw string prefix, so sibling repos sharing a
  prefix do not collide.
- Sharpen commit-history SKILL.md HTTP fallback: build the
  /agentmemory/commits URL with URL-encoded query values so branch and
  repo values containing `?`, `&`, or `#` cannot corrupt the request.

* chore(consistency): bump documented tool and endpoint counts

Bring README.md, AGENTS.md, and the boot log in src/index.ts in line
with the live tool registry (53 MCP tools, +2 from this branch) and
the live REST registration (124 endpoints, +3 from this branch).

The Consistency test in test/consistency.test.ts derives both counts
from source and asserts the markdown matches; this commit only updates
the literal numbers, no behavior change.
2026-05-18 11:19:47 +01:00
Rohit Ghumare 9061da56d5 chore(release): v0.9.18 (#459)
Hardening + DX wave. Bundles the four follow-up issues filed during
the v0.9.17 audit (#446 timeout env unify, #447 viewer CSP tighten,
#448 fs-watcher PEM/JWT redaction, #449 mcp-standalone probe DI flake)
plus the lessons-auto-inject gap surfaced in discussion #381 / fixed
in #457.

1007/1007 tests pass.

Files bumped:
- package.json
- packages/mcp/package.json
- plugin/.claude-plugin/plugin.json
- plugin/.codex-plugin/plugin.json
- src/version.ts
- src/types.ts (ExportData.version union)
- src/functions/export-import.ts (supportedVersions Set)
- test/export-import.test.ts (assertion)
- CHANGELOG.md (new section)
2026-05-17 14:05:56 +01:00
Rohit Ghumare 3a3f866ab5 chore(release): v0.9.17 — OpenAI provider + telemetry id + Compare polish (#432)
Patch bump per the established rule: additive surface only. OpenAI
provider is a new optional surface that activates only when
OPENAI_API_KEY is set, gated by OPENAI_API_KEY_FOR_LLM. Telemetry
project_name pin is pure observability metadata. Compare polish is
docs/website only.

PRs included since v0.9.16:
  #307 — OpenAI-compatible LLM provider (universal adapter for
         OpenAI, Azure OpenAI auto-detected by hostname, DeepSeek,
         SiliconFlow, vLLM, LM Studio, Ollama). Plus the maintainer-
         pushed Azure detection + fetch timeout + README scope hint
         follow-ups. Closes #185, #232, #312, supersedes #240.
  #426 — pin worker telemetry project_name
  #427 — Compare section polish (title + native plugins cell + grid)

Files bumped (9):
  package.json, packages/mcp/package.json, plugin/.claude-plugin/plugin.json,
  plugin/.codex-plugin/plugin.json, src/version.ts, src/types.ts,
  src/functions/export-import.ts, test/export-import.test.ts,
  CHANGELOG.md
2026-05-16 20:36:24 +01:00
Rohit Ghumare b7e1240d06 chore(release): v0.9.16 — DevEx polish + website refresh (#417)
Patch bump per the established rule: additive surface only, no breaks
to MemoryProvider trait, exported types, or default behaviour. New
top-level subcommands (`--reset` already shipped 0.9.15, no new
commands here) are opt-in.

PRs included since v0.9.15:
  #408 — onboarding wires selected agents inline + memory-share callout
  #409 — clarify MCP is opt-in (REST primary)
  #410 — 5-port ready panel, iii console install, global-install prompt
  #411 — splash banner rerender + README install hoist + npx caveat
  #415 — agent-memory.dev refresh (FeaturedIn bar + Agents/Compare/
         CommandCenter/Hero updates)

Files bumped (9):
  package.json, packages/mcp/package.json, plugin/.claude-plugin/plugin.json,
  plugin/.codex-plugin/plugin.json, src/version.ts, src/types.ts,
  src/functions/export-import.ts, test/export-import.test.ts,
  CHANGELOG.md
2026-05-15 19:58:24 +01:00
Rohit Ghumare c3a613aed8 chore(release): v0.9.15 — DevEx overhaul (splash, connect, doctor v2, remove, silent killers) (#407)
Patch bump per the established rule: all changes additive, no breaks
to MemoryProvider trait or exported types or default behaviour. New
top-level subcommands (connect, remove, --reset, --force) are opt-in.

PRs included since v0.9.14:
  #405 — silent killers: viewer port auto-bump, engine version-match
         warning, stop --force, adopt-on-attach, npx PATH hint
  #402 — agentmemory connect — automate native-plugin install for 8
         agents (claude-code, codex, cursor, gemini-cli, openclaw
         end-to-end; hermes/pi/openhuman stubbed)
  #406 — interactive doctor v2 + agentmemory remove (destruction plan
         + two-confirmation flow)
  #403 — splash banner + agent grid + provider picker + smart-defaults
         preferences + bootLog shim (30+ lines of log spam → 10)

Files bumped (9):
  package.json, packages/mcp/package.json, plugin/.claude-plugin/plugin.json,
  plugin/.codex-plugin/plugin.json, src/version.ts, src/types.ts,
  src/functions/export-import.ts, test/export-import.test.ts,
  CHANGELOG.md
2026-05-15 16:43:06 +01:00
Rohit Ghumare 372c6a6bbb chore(release): v0.9.14 — CLI installer first + agentmemory stop + Docker-aware teardown (#401)
Patch bump. No breaking changes to the public API or the existing CLI
subcommands. CLI bootstrap flow changes are additive; existing Docker
users keep working via the new AGENTMEMORY_USE_DOCKER=1 opt-in.

Files bumped (9):
  - package.json
  - packages/mcp/package.json
  - plugin/.claude-plugin/plugin.json
  - plugin/.codex-plugin/plugin.json
  - src/version.ts
  - src/types.ts (ExportData.version union — also adds 0.9.13
    which was missed in the previous release)
  - src/functions/export-import.ts (supportedVersions Set)
  - test/export-import.test.ts
  - CHANGELOG.md

PRs included since v0.9.13:
  #396 — feat(cli): install iii-engine binary first, Docker opt-in;
         agentmemory stop with Docker-aware teardown; LISTEN-only
         lsof filter so the CLI no longer signals its own parent.
  #397 — docs(readme): move OpenHuman after pi in agents grid.
2026-05-15 15:11:47 +01:00
Rohit Ghumare 8b98432853 chore(release): v0.9.13 — env-example + init + CJK + load bench + deploy templates + Gemini GA bumps (#383)
* chore(release): v0.9.13 — env-example discovery + CJK tokenizer + load
harness + deploy templates + Gemini GA bumps + 14 advisories closed

Six PRs landed since v0.9.12:

- #372 .env.example discovery (this commit) — repo-root template + `init`
  CLI command + CI sync-checker
- #362 CJK BM25 tokenizer (`@node-rs/jieba` + tiny-segmenter + Hangul)
- #363 `benchmark/load-100k.ts` harness with p50/p90/p99 + per-release
  results dir
- #361 one-click deploy templates for fly.io / Railway / Render / Coolify
  (multi-stage Dockerfile, `iiidev/iii` base, `gosu` privilege drop,
  first-boot HMAC, verified end-to-end on fly.io)
- #364 Python ecosystem via `iii-sdk` example (replaces closed PR #360)
- #370 Gemini GA bumps (LLM default → gemini-2.5-flash, embedding →
  gemini-embedding-001 + L2-norm + 768 dims)

Plus 14 open Dependabot advisories closed in PR #348 via Next.js → 16.2.6
and PostCSS → 8.5.10 overrides.

Bumped:

- src/version.ts: VERSION 0.9.12 → 0.9.13
- package.json: 0.9.12 → 0.9.13, files += ".env.example", build script
  copies .env.example into dist/
- packages/mcp/package.json: 0.9.12 → 0.9.13 (lockstep with main)
- plugin/.claude-plugin/plugin.json, plugin/.codex-plugin/plugin.json:
  0.9.12 → 0.9.13
- src/types.ts: ExportData.version union extended with "0.9.13"
- src/functions/export-import.ts: supportedVersions Set extended
- test/export-import.test.ts: expected version updated

New surface:

- .env.example at repo root — every env var read by src/ documented in
  one place, grouped by surface (LLM, embedding, auth, search tuning,
  behaviour flags, CLI runtime, ports, iii engine pin, Claude Code
  bridge, Obsidian export). Every line commented out by default so the
  file is a template.
- agentmemory init — copies bundled .env.example to ~/.agentmemory/.env
  if absent, refuses to overwrite, prints a diff command. Wired into
  CLI dispatch + help block.
- scripts/check-env-example.mjs — walks src/ for env-read patterns,
  fails CI on drift in either direction. Plugged into ci.yml after
  npm test. Initial bootstrap: 60 keys in sync.

Verified: npm test 903/903, npm run build clean, init smoke pass
(creates ~/.agentmemory/.env on first run, refuses overwrite on second).

* fix(init): atomic copy via COPYFILE_EXCL; address CodeRabbit review

Two valid findings from the CodeRabbit pass on PR #383.

1. `runInit` race between existsSync(target) + copyFile(template, target).
   A parallel `agentmemory init` (or any other process touching
   ~/.agentmemory/.env between the two calls) would silently overwrite
   the config the operator just wrote. Switch to a single atomic
   `copyFile(template, target, fsConstants.COPYFILE_EXCL)` and treat
   the EEXIST error as the "already configured" signal — same warning
   + diff hint as before, but the check + copy now happen in one
   syscall so they cannot race. Other failure paths still surface as
   process exit 1.

2. Comment on `scripts/check-env-example.mjs::walk` claimed it matched
   ".ts / .mts / .mjs" but the regex also matched ".js". Rewrote the
   comment to match the regex (".ts / .mts / .mjs / .js"). Same comment
   pass: noted that test/ never enters because the walk is rooted at
   src/, not because of an explicit skip.

Skipped findings:

- WHAT-style comment on `findEnvExample` — kept a one-liner explaining
  the package-vs-source priority since both paths are real; reduced
  the block from 4 lines to 2 instead of removing it entirely.
- "Add trailing newline to .env.example" — file already ends with `\n`
  (verified `tail -c 5` shows `tion\n`).

Verified locally:
- `npm run build` clean.
- `npm test` 903 / 903 pass.
- First `agentmemory init` against a clean HOME creates the file.
- Second init against the same HOME hits EEXIST and prints the
  "leaving it untouched" warning + diff hint without overwriting.
- `node scripts/check-env-example.mjs` — in sync (60 keys).
2026-05-15 10:11:37 +01:00
Rohit Ghumare 87fae50b79 chore(release): v0.9.12 — BM25 unicode + vector live-write + viewer hardening (#337)
Three landed PRs since v0.9.11:
  - #327 (#295) — BM25 tokenizer now accepts non-ASCII (Greek,
    accented Latin, Hebrew, Arabic, Cyrillic), VectorIndex.add now
    actually called at runtime via vectorIndexAddGuarded helper with
    dim guard + input clip, migrateVectorIndex for dim migrations.
  - #326 (#277) — RetentionScore type no longer declares source
    twice; JSDoc back-compat note no longer shadowed.
  - #335 (#323) — viewer drops Google Fonts <link> (CSP-blocked),
    loadDashboard now surfaces load errors inline instead of
    sticking on "Loading dashboard…".

Bumping 0.9.11 -> 0.9.12 across the 9 standard files:
- package.json
- packages/mcp/package.json
- plugin/.claude-plugin/plugin.json
- plugin/.codex-plugin/plugin.json
- src/version.ts
- src/types.ts (ExportData.version literal)
- src/functions/export-import.ts (supportedVersions)
- test/export-import.test.ts (round-trip expectation)
- CHANGELOG.md (new 0.9.12 entry)

886 / 886 tests pass. Build clean.
2026-05-13 16:17:01 +01:00
Rohit Ghumare 25dddc4379 chore(release): v0.9.11 — Codex plugin platform + OpenClaw slot fix + website star button (#317)
Three landed PRs since v0.9.10:
- #311 — Codex plugin support (manifest + marketplace + Codex-shaped
  hooks subset, sharing the same scripts as the Claude Code plugin
  via CLAUDE_PLUGIN_ROOT OOTB-compat injection on Codex's hook
  engine)
- #310 — OpenClaw plugins.slots.memory now actually claimed via
  api.registerMemoryCapability({ promptBuilder }); older builds
  without the capability API still load via the existing hook-only
  path
- #316 — Star on GitHub button in the website hero CTA row, live
  stargazer count via api.github.com, 30-minute localStorage cache,
  graceful degrade on API failure

Bumping 0.9.10 -> 0.9.11 across the 8 standard files:
- package.json
- packages/mcp/package.json
- plugin/.claude-plugin/plugin.json
- plugin/.codex-plugin/plugin.json
- src/version.ts
- src/types.ts (ExportData.version literal)
- src/functions/export-import.ts (supportedVersions)
- test/export-import.test.ts (round-trip expectation)
- CHANGELOG.md (new 0.9.11 entry)

877 / 877 tests pass. Build clean.
2026-05-13 00:21:27 +01:00
Rohit Ghumare c21410e287 feat(plugin): ship Codex plugin manifest + marketplace (#311)
* feat(plugin): ship Codex plugin manifest + marketplace (#codex-plugins)

OpenAI Codex shipped a plugin platform
(developers.openai.com/codex/plugins) with the same shape as Anthropic
Claude Code plugins: .codex-plugin/plugin.json manifest, optional
.mcp.json, hooks/hooks.json, and a skills/ directory.

Verified against openai/codex source
(codex-rs/hooks/src/engine/discovery.rs): Codex's hook engine
explicitly injects CLAUDE_PLUGIN_ROOT into hook subprocesses for
OOTB compat with existing Claude Code plugins. The wire-format
input schemas
(codex-rs/hooks/schema/generated/session-start.command.input.schema.json
and siblings) use the same field names as Claude Code
(session_id, cwd, hook_event_name, source, transcript_path, model,
permission_mode).

Net result: our existing plugin/ directory already works as a Codex
plugin with two small additions:

1. plugin/.codex-plugin/plugin.json — Codex manifest pointing at the
   shared ./.mcp.json, ./skills/, and a Codex-specific ./hooks/
   hooks.codex.json. Name (kebab-case), version, description match
   the Claude Code manifest.

2. plugin/hooks/hooks.codex.json — Codex-compatible hook subset.
   Drops SubagentStart, SubagentStop, SessionEnd, Notification,
   TaskCompleted, PostToolUseFailure (Claude-Code-only). Keeps
   SessionStart, UserPromptSubmit, PreToolUse, PostToolUse,
   PreCompact, Stop. Adds Codex-specific `statusMessage` decorations
   on the two hooks the user sees most (SessionStart + UserPromptSubmit).
   The script commands themselves still reference ${CLAUDE_PLUGIN_ROOT}
   because Codex's engine injects it.

3. .codex-plugin/marketplace.json at repo root — git-subdir source
   pointing at ./plugin so `codex plugin marketplace add rohitg00/
   agentmemory` works the same way `claude plugin marketplace add`
   does. Mirrors the existing .claude-plugin/marketplace.json shape
   but in Codex's marketplace schema (name + interface.displayName
   + plugins[].source.{source, url, path, ref} + plugins[].policy).

README updates:
- Codex CLI tile in the Works-with-every-agent grid now reads
  "6 hooks + MCP + skills" instead of "MCP server" to surface the
  upgraded surface.
- "Other agents" install table now distinguishes "Codex CLI (MCP
  only)" (existing codex mcp add path) from "Codex CLI (full
  plugin)" (new marketplace install).
- New Codex section in the "paste this prompt" block with the
  two-command install (server + marketplace add + plugin install),
  the list of registered surfaces, and the env-var-injection note
  citing the Codex source line.

6 new tests in test/codex-plugin.test.ts cover:
1. .codex-plugin/plugin.json present with kebab-case name and
   required references.
2. Manifest version matches main package.json (so future bumps
   don't drift the Codex side).
3. Every path referenced in the manifest resolves to a real file or
   directory on disk.
4. hooks.codex.json contains only events Codex supports — any
   future addition has to be added to a Codex allowlist or the
   test fails loudly.
5. Every ${CLAUDE_PLUGIN_ROOT}/scripts/* command references an
   existing script file (catches typos before users hit them).
6. .codex-plugin/marketplace.json declares git-subdir source with
   path: "./plugin" pointing at this repo.

874 / 874 tests pass.

* fix(plugin/codex): align manifest description + assert PreCompact hook

Two reviewer findings addressed:

1. plugin/.codex-plugin/plugin.json's description claimed "12 hooks"
   but Codex only registers 6 (the Codex hook input schemas don't
   define SubagentStart, SubagentStop, SessionEnd, Notification,
   TaskCompleted, PostToolUseFailure). Updated the description to
   "6 hooks, 51 MCP tools, 4 skills, real-time viewer." so the
   marketplace listing matches the registered surface.

2. test/codex-plugin.test.ts asserted five lifecycle events
   (SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop)
   but omitted PreCompact, leaving a regression hole: dropping
   PreCompact from hooks.codex.json would still pass the test
   because the allowlist loop only blocks UNKNOWN events. Added
   expect(events).toContain("PreCompact") so the suite enforces
   the full six-hook contract.

Skipped (with reason):

3. marketplace.json source.ref currently "main" — reviewer asked
   for a fixed tag/SHA. Skipping because pinning to a tag forces
   updating marketplace.json on every release; "main" matches our
   convention where releases tag from main HEAD and pre-release
   work is on feature branches. Users wanting an immutable cut
   can pass --ref vX.Y.Z to `codex plugin marketplace add` per
   Codex docs.

Validation: 6/6 codex tests pass; 874/874 full suite unchanged.
2026-05-12 23:39:10 +01:00
Rohit Ghumare 5ea01c162c fix(deploy): distroless volume perms + viewer proxy + budget loop (#299, #301) (#304)
@flamerged reported three issues on a real production deployment:

1. (#301) v0.9.7's working-directory fix moved iii-config paths from
   ./data/... to /data/... so the named volume mount is actually
   reached. But iiidev/iii is distroless and runs as UID 65532, while
   `docker volume create` initializes the named volume mountpoint as
   root:root mode 755. Engine writes fail Permission denied (os error
   13), the API silently buffers in RAM, every API call returns
   success, and state evaporates on every container restart — exactly
   what 0.9.7 set out to fix.

   Fix: ship a one-shot iii-init service in docker-compose.yml
   (busybox:1.36, ~4MB, exits in <100ms) that chowns /data to
   65532:65532. iii-engine now has user: "65532:65532" and
   depends_on.iii-init.condition: service_completed_successfully.
   Verified live: pre-fix volume stayed 4.0K after API writes; post-
   fix volume grows to 44K with state_store.db/mem%3A*.bin files
   written through the named volume.

2. (#299) src/viewer/index.html ports detection hardcoded ':3113' as
   the fallback when window.location.port is empty (page served on
   80/443 behind a reverse proxy). Every browser-side /agentmemory/*
   fetch went to <host>:3113, which is typically loopback-only on the
   self-hosted shape — the dashboard rendered cleanly but every
   panel showed the empty "first run" state.

   Fix: when neither ?port=N nor window.location.port is set, use
   window.location.origin as the REST base and window.location.host
   for the WebSocket URL — same-origin path works for both REST and
   live updates. Explicit ?port=N / non-default window.location.port
   paths unchanged.

3. (bundled) mem::context budget loop used `break` on first oversized
   block. With #288's new pinned-slot injection sorting first via
   recency: Date.now(), one fat pinned slot could starve every
   smaller block downstream that would have fit. Switched to
   `continue` so smaller blocks still slip into remaining budget.
   Total tokens still bounded by tokenBudget; only composition under
   contention changes.

Bumping 0.9.9 -> 0.9.10 across the 8 standard files (package.json,
packages/mcp/package.json, plugin/.claude-plugin/plugin.json,
src/version.ts, src/types.ts ExportData literal,
src/functions/export-import.ts supportedVersions, the export
round-trip test expectation, and CHANGELOG.md).

868 / 868 tests pass. Build clean. Volume + viewer fixes verified
end-to-end live.
2026-05-12 16:19:13 +01:00
Rohit Ghumare 03fb42db39 chore(release): v0.9.9 — pinned slot injection + MiniMax env loader (#290)
Two field-reported fixes landed since v0.9.8:

- #288 (closes #286) — wires pinned memory slots into mem::context so
  agentmemory pinned-slot content actually reaches SessionStart. The
  renderPinnedContext / listPinnedSlots helpers from #182 had zero
  callers; this lands the wiring behind isSlotsEnabled().
- #289 (closes #285) — MiniMax provider now reads MINIMAX_BASE_URL via
  the shared getEnvVar() loader (so ~/.agentmemory/.env values get
  picked up), and the default endpoint is bumped from the stale
  api.minimaxi.com to api.minimax.io/anthropic per MiniMax's current
  Anthropic-compatible docs.

Bumping 0.9.8 -> 0.9.9 across the 8 standard files (package.json,
packages/mcp/package.json, plugin/.claude-plugin/plugin.json,
src/version.ts, src/types.ts ExportData literal,
src/functions/export-import.ts supportedVersions, the export
round-trip test expectation, and CHANGELOG.md).

868 / 868 tests pass.
2026-05-11 19:38:20 +01:00
Rohit Ghumare 0f0bb5091d chore(release): v0.9.8 — local fallback tools/list returns 7 not 4 (#284)
Single-issue release: PR #283 fixed the local-mode tools/list path
in @agentmemory/mcp to expose all 7 IMPLEMENTED_TOOLS instead of the
4-tool intersection ESSENTIAL_TOOLS ∩ IMPLEMENTED_TOOLS that bug
manifested as in MCP clients (Cursor / Roo Code / others) when no
agentmemory server was reachable on localhost:3111.

Bumping 0.9.7 -> 0.9.8 across the 8 standard files (package.json,
packages/mcp/package.json, plugin/.claude-plugin/plugin.json,
src/version.ts, src/types.ts ExportData literal,
src/functions/export-import.ts supportedVersions, the export
round-trip test expectation, and CHANGELOG.md).
2026-05-11 14:56:06 +01:00
Rohit Ghumare 82d1b06df5 fix(mcp): probe diagnostics + sandbox escape hatch (#234, #278) (#280)
* fix(mcp): probe diagnostics + sandbox escape hatch (#234, #278)

Three small follow-ups to v0.9.6 reported live on #234 and #278:

1. @agentmemory/mcp shim silently degraded to 7-tool local fallback for
   sandboxed MCP clients (Flatpak VS Codeium / Roo Code) because the
   500ms livez probe failed inside the sandbox network namespace and the
   catch swallowed the error. Probe now logs the URL, status, and reason
   to stderr; default timeout raised to 2000ms; AGENTMEMORY_PROBE_TIMEOUT_MS
   overrides the timeout; AGENTMEMORY_FORCE_PROXY=1 skips the probe and
   trusts AGENTMEMORY_URL outright for sandboxed clients that can reach
   the server through a known route but not the host loopback. Closes
   the #234 follow-up reported by @jcalfee.

2. Docker compose stack persisted state to an ephemeral container path.
   iii-config.docker.yaml used file_path: ./data/state_store.db, which
   the engine resolved against its container WORKDIR=/home/nonroot --
   not the /data mount where iii-data is bound. State and stream stores
   were silently written to the container layer and lost on every
   docker compose down. Paths are now absolute (/data/state_store.db
   and /data/stream_store). Existing users need a one-time docker
   compose down -v before upgrade.

3. CLI banner leaked "which: no iii in ..." when iii wasn't on PATH.
   execFileSync default stdio inherits stderr; GNU which writes the
   miss line to stderr (exit 1). Switched to explicit
   stdio: ["ignore", "pipe", "pipe"] in whichBinary().

4. docker-compose.yml now caps engine container log size at 30MB total
   (json-file driver, max-size=10m, max-file=3) so the iiidev/iii
   crash/restart spam reported by @satabd in #278 can no longer fill
   the host disk. The upstream engine spam itself needs filing against
   iii-hq/iii -- this is the compose-side guardrail.

Version 0.9.6 -> 0.9.7 across package.json, packages/mcp/package.json,
plugin/.claude-plugin/plugin.json, src/version.ts, src/types.ts
ExportData literal, src/functions/export-import.ts supportedVersions,
and the export round-trip test expectation.

* fix(mcp): AGENTMEMORY_DEBUG flag + loud unexpected-shape warning

@jcalfee on #234 confirmed the v0.9.6 fix (and PR #280's escape hatch)
still surfaces 7 tools in Roo Code even though:
  - Flatpak share=network is on (probe succeeds via curl in-sandbox)
  - server's /agentmemory/mcp/tools returns 51 tools via curl
  - AGENTMEMORY_FORCE_PROXY=1 is set

Roo Code shows 7 tools even when docker is down -- strong signal of
client-side caching at the Roo Code layer, not a shim regression.
But the shim has zero visibility into what it actually returns to
the MCP client right now: success path is silent, only failure paths
log.

Add AGENTMEMORY_DEBUG=1 (or =true) so the shim writes to stderr:
  - which mode handle.mode resolved to (proxy vs local) and baseUrl
  - shape of the /agentmemory/mcp/tools response (keys + tools type)
  - count of tools returned to the MCP client
  - tool names in the local-fallback list if we hit it

Also: previously, when the server returned a JSON shape that wasn't
{tools: Array}, the shim silently fell back to the 7-tool local list
with no log line. Now that path warns to stderr unconditionally,
pointing at AGENTMEMORY_DEBUG=1 for inspection.

This is diagnosis enablement -- not a fix for whatever is making
Roo Code show 7 tools. Once @jcalfee can run the shim with
AGENTMEMORY_DEBUG=1 and share the stderr output, we'll know whether
the shim returns 51 (Roo Code bug) or 7 (deeper shim bug).
2026-05-11 13:59:11 +01:00
Rohit Ghumare 13924d2f1b chore(release): v0.9.6 — search recall + MCP shim + hook latency (#272)
Three reliability fixes from #269/#270/#271:

- search/recall surfaces saved memories (closes #265)
- MCP shim proxies full server tool set (closes #234)
- session/subagent hooks no longer block startup (closes #221)

Also fixes packages/mcp version drift — was stuck at 0.9.4 through v0.9.5,
now lockstepped with main.
2026-05-10 19:25:41 +01:00
Rohit Ghumare 1ff5849d9c fix: cap session-start/subagent-start hook latency (#221) (#271)
Two hook scripts blocked Claude Code's startup waiting on REST responses
they didn't actually need:

- `session-start` awaited a 5000ms POST and discarded the response when
  `AGENTMEMORY_INJECT_CONTEXT=false` (the default). Pure latency.
- `subagent-start` had a `// fire and forget` comment but the code
  awaited a 2000ms POST. Pure latency.

Under fan-out (Slack-bot orchestrators, multi-agent harnesses, fanned
`claude -p` jobs) the awaited timeouts stack and feed back into the
engine; the reporter hit a positive feedback loop that OOM-killed
iii-engine.

Fix:

- `session-start` — fire-and-forget when `INJECT_CONTEXT=false`. Cap the
  inject path at 1500ms (down from 5000ms) so a slow server can't block
  the agent indefinitely when stdout is actually consumed.
- `subagent-start` — actually fire-and-forget, matching the existing
  comment. Cap at 800ms.

Verified live against a black-hole TCP listener (accepts, never replies):
- session-start (no inject): 5.05s → 0.85s
- session-start (inject):    5.05s → 1.55s
- subagent-start:            2.05s → 0.87s

Built artifacts in `plugin/scripts/` regenerated via `npx tsdown`.

Closes #221.
2026-05-10 19:08:34 +01:00
Rohit Ghumare 0c73d868be chore(release): v0.9.5 — search recall + plugin compatibility (#261)
Bug-fix patch focused on search recall correctness and plugin
compatibility. Pins iii-engine to v0.11.2 because v0.11.6 introduces
a new sandbox-everything-via-`iii worker add` model that agentmemory
hasn't been refactored for yet — pin lifts once that refactor lands.
Adds a hard guard against silent vector-index corruption, fixes BM25
indexing for memories saved via memory_save, and lands four Hermes
plugin fixes.

Per AGENTS.md release checklist:
- package.json version 0.9.4 -> 0.9.5
- src/version.ts VERSION constant
- src/types.ts ExportData version union
- src/functions/export-import.ts supportedVersions Set
- test/export-import.test.ts assertion
- plugin/.claude-plugin/plugin.json version
- CHANGELOG.md detailed entries with contributor shoutouts

Headlines (full detail in CHANGELOG):

Fixed:
- BM25 search now indexes memories saved via memory_save (#258, #257)
  Thanks @Nizar-BenHamida for the precise repro.
- Embedding providers no longer silently corrupt the vector index when
  an API returns wrong-dimension vectors (#248, #247, #256)
  Thanks @AmmarSaleh50 for issue + fix + tests.
- Hermes handle_tool_call returns JSON strings, not raw dicts (#255, #254)
  Thanks @KyoMio for the Anthropic-protocol repro.
- Hermes status reflects real service state on systemd installs (#253, #250)
  Thanks @OptionalCoin for tracing it to env-source divergence.
- Hermes hooks accept passthrough kwargs (#252, #249)
  Thanks @OptionalCoin again for the log analysis.
- agentmemory demo now seeds observations correctly (#251, #229)
  Thanks @seishonagon for root-cause analysis.
- LLM compression / summarization timeouts increased (#213)
  Thanks @xuli500177.
- Pi / OpenClaw / Hermes integration plugin fixes (#230)
  Thanks @deepmroot.

Changed:
- iii-engine pinned to v0.11.2 across every install path (#260).
  v0.11.6 introduces a new `iii worker add` sandbox model that
  agentmemory still pre-dates; pin lifts when we refactor agentmemory
  to register as a sandboxed worker. Override with
  AGENTMEMORY_III_VERSION=<version> for users who've migrated manually.
- README documents iii worker add extension surface (#242).
- README iii Console install/launch commands corrected (#243).

Validated: 852/852 tests pass, npm run build clean.
2026-05-09 17:39:20 +01:00
Rohit Ghumare 94fc1191ef chore: bump version to 0.9.4 + add CHANGELOG (#216) 2026-04-29 17:26:04 +01:00
Rohit Ghumare 4bd0ceb72e release: v0.9.3 — DX patch (feature-flag visibility + doctor command)
Every disabled feature flag is now visible everywhere: viewer banner, CLI
status, doctor output, and REST error responses. New `agentmemory doctor`
command runs 10 diagnostic checks in one shot, each failure includes a
concrete fix hint. Viewer gains first-run hero pointing at `demo` command
and a footer with preset issue-report link.

Bumps across 8 files per release checklist:
  package.json, plugin/.claude-plugin/plugin.json, src/version.ts,
  src/types.ts, src/functions/export-import.ts, test/export-import.test.ts,
  packages/mcp/package.json, CHANGELOG.md

See CHANGELOG for full details.
2026-04-24 19:53:07 +01:00
Rohit Ghumare 196cbd6343 chore: bump version to 0.9.2 + add CHANGELOG
Rolls up #186 (OPENAI_BASE_URL / OPENAI_EMBEDDING_MODEL), #187 (Stop-hook
recursion 5-layer defense + NoopProvider + AGENTMEMORY_ALLOW_AGENT_SDK opt-in),
#188 (viewer empty-tabs + import-jsonl synthetic compression + auto-derived
lessons/crystals + richer session detail + audit/replay/frontier shape fixes),
#189 (OPENAI_EMBEDDING_DIMENSIONS + model-dimensions table), and #190
(README/website docs refresh).

Bumps: package.json, plugin/.claude-plugin/plugin.json, src/version.ts,
src/types.ts ExportData.version union, src/functions/export-import.ts
supportedVersions, test/export-import.test.ts assertion, and
packages/mcp/package.json shim (was stuck at 0.9.0).
2026-04-22 16:38:37 +01:00
Rohit Ghumare 51bcb09104 address CodeRabbit review on #187 + fix CI
Findings verified against current code on this branch; all four valid.

1. config.ts loadFallbackConfig (L281) — user could set
   FALLBACK_PROVIDERS=agent-sdk and bypass the AGENTMEMORY_ALLOW_AGENT_SDK
   gate added to detectProvider. Filter it out at the fallback layer too,
   with the same warning pointing at the opt-in flag.

2. summarize.ts (L87-92) — the empty_provider_response branch returned
   without recording failure metrics or a diagnostic log, unlike the
   parse/validation paths. Record the same metricsStore failure event and
   log provider name, prompt size, system size, and observation count so
   empty responses are visible in telemetry.

3. providers/agent-sdk.ts (L14-45) — setting
   process.env.AGENTMEMORY_SDK_CHILD = '1' without restoring it caused
   every subsequent .query() in the same parent process to hit the
   short-circuit guard and return '' (classified as a SDK child it is
   not). Capture prev, set in try, restore in finally (delete if prev
   was undefined). Child processes spawned during the for-await loop
   still inherit the marker because env is inherited at spawn time; we
   only restore after the loop completes.

4. plugin/scripts/sdk-guard-DI1NUOS9.mjs — tsdown extracted the shared
   guard helper into a hashed chunk. Hash rotates on every rebuild and
   churns the diff. Stopped using the shared module from hooks entirely
   and inlined the 6-line guard function into each hook .ts file
   instead. sdk-guard.ts stays in the tree because the unit tests cover
   it directly. Deleted the tracked hashed .mjs and confirmed no new
   chunk is emitted.

Also applied the CI two-step install (npm install --package-lock-only
then npm ci) on this branch, matching #184. Without it, npm ci fails
because lockfiles are gitignored.

Tests: 74 files / 819 tests pass.
2026-04-22 11:44:05 +01:00
Rohit Ghumare 5e63846b29 fix(hooks): break Stop-hook infinite recursion via agent-sdk fallback
Reported: a user with no provider API key and AGENTMEMORY_AUTO_COMPRESS=false
(which they believed protected them) hit unbounded recursion — Stop hook
POSTs /agentmemory/summarize, handler calls provider.summarize(), agent-sdk
provider spawns @anthropic-ai/claude-agent-sdk query(), which creates a full
CC-style child session that reads ~/.claude/settings.json, registers the
same plugin hooks, and fires its own Stop -> another child -> loop. ~579
ghost 'entrypoint: sdk-ts' sessions accumulated in a few minutes, draining
Claude Pro tokens.

#149 only added a stderr warning. AGENTMEMORY_AUTO_COMPRESS gated /compress
but never /summarize, so users who followed the warning's implied guidance
still got hit. Fix the loop at every layer:

1. config.ts detectProvider
   - Treat empty-string provider keys (ANTHROPIC_API_KEY=) as unset; they
     previously passed the truthiness check identically to a real key.
   - Stop defaulting to agent-sdk. When no key is set, return a 'noop'
     provider config and warn. Agent-sdk fallback now requires an explicit
     AGENTMEMORY_ALLOW_AGENT_SDK=true opt-in with a loud second warning.

2. providers/noop.ts (new) + providers/index.ts
   - NoopProvider implements MemoryProvider and returns empty strings for
     compress and summarize so callers can detect .name === 'noop' and
     short-circuit without spawning anything.
   - Add ProviderType 'noop' and wire it through createBaseProvider.

3. providers/agent-sdk.ts
   - Before spawning query(), check process.env.AGENTMEMORY_SDK_CHILD === '1'
     and return '' instead of recursing. Set the env var to '1' before the
     spawn so any child process (including the Agent SDK session's hooks)
     inherits it.

4. hooks/sdk-guard.ts (new) + all 12 hook scripts
   - Shared isSdkChildContext(payload) checks both AGENTMEMORY_SDK_CHILD=1
     and payload.entrypoint === 'sdk-ts' (CC writes this into the stdin
     jsonl for SDK-spawned sessions). Every hook script now bails early
     when that returns true, so even if one guard layer fails the others
     break the loop.

5. functions/summarize.ts
   - Short-circuit with {success:false, error:'no_provider'} when
     provider.name === 'noop' — never reach .summarize().
   - Treat an empty provider response as empty_provider_response instead
     of trying to parse it.

Tests: 74 files / 819 tests pass (+7 new in stop-hook-recursion-guard.test.ts).
Defense in depth means any ONE of the five layers breaks the loop.
2026-04-22 10:57:57 +01:00
Rohit Ghumare c70b60211a feat(slots): add editable pinned memory slots + idle reflection + global scope
Introduces memory slots as a new first-class memory primitive, orthogonal
to observations / memories / graph. A slot is a labelled, size-limited,
agent-editable chunk of context. 8 defaults seed on first run
(persona, user_preferences, tool_guidelines, project_context, guidance,
pending_items, session_patterns, self_notes). Agent reads/writes via
MCP tools; pinned slots serialise cleanly into SessionStart context.

Functions (`src/functions/slots.ts`):
- mem::slot-list
- mem::slot-get
- mem::slot-create
- mem::slot-append   (refuses writes over sizeLimit)
- mem::slot-replace  (refuses content over sizeLimit)
- mem::slot-delete
- mem::slot-reflect  (used by idle reflection)

REST (`src/triggers/api.ts`):
- GET    /agentmemory/slots
- GET    /agentmemory/slot?label=...
- POST   /agentmemory/slot
- POST   /agentmemory/slot/append
- POST   /agentmemory/slot/replace
- DELETE /agentmemory/slot?label=...
- POST   /agentmemory/slot/reflect

MCP tools (`src/mcp/tools-registry.ts`, `src/mcp/server.ts`):
- memory_slot_list
- memory_slot_get
- memory_slot_create
- memory_slot_append
- memory_slot_replace
- memory_slot_delete

event::session::stopped fires mem::slot-reflect via triggerVoid when
AGENTMEMORY_REFLECT=true. The reflector reads the last N observations,
extracts TODO-flavoured titles into pending_items, counts error /
command types into session_patterns, and records touched files in
project_context. Fire-and-forget: does not block summarise.

Slots carry a scope field ("project" | "global"). Global slots live at
KV.globalSlots ("mem:slots:global") — one brain across all projects for
things like persona / user_preferences / tool_guidelines. Project-
scoped slot with the same label shadows the global one on read.

- AGENTMEMORY_SLOTS=true    — enable the whole primitive
- AGENTMEMORY_REFLECT=true  — enable Stop-hook reflection (requires SLOTS)

Both default OFF. Matches the opt-in convention of auto-compress (#138),
inject-context (#143), vision embeddings (#179).

44 MCP tools → 50 across README (stats badge, install instructions,
config block) and plugin/.claude-plugin/plugin.json description. New
AGENTMEMORY_SLOTS and AGENTMEMORY_REFLECT entries in the config block.

test/slots.test.ts — 12 new cases: default seeding by scope, label
validation, create/get/append/replace/delete round-trip, duplicate-
create rejection, sizeLimit enforcement on append + replace, project
shadows global, listPinnedSlots + renderPinnedContext, reflect no-op
on empty session, reflect updates pending_items / session_patterns /
project_context from TODO + error + file signals.

Full suite: 800/800 pass (+12 new). Build clean.
2026-04-21 18:57:17 +01:00
Rohit Ghumare 241be2669c fix(vision): address CodeRabbit review on #179
- src/functions/image-refs.ts: delete imageEmbeddings BEFORE imageRefs
  and drop the silent .catch() so transient KV errors surface. Stops
  orphan searchable embeddings when the ref delete fails halfway.
- src/functions/vision-search.ts: record an audit row for every
  successful mem::vision-embed write (new "vision_embed" op). State-
  changing functions must leave an audit trail per the repo policy.
- src/functions/vision-search.ts: mem::vision-search now wraps raw
  queryImageBase64 into a data: URL before handing to embedImage, so
  the provider contract stays "data: URL or filesystem path".
- src/providers/embedding/clip.ts: drop the raw-base64 heuristic in
  loadImage. The old regex whitelisted "/" in the char class but the
  !src.includes("/") guard rejected any realistic base64 blob,
  falling through to readFile and throwing ENOENT. Now: data: URL or
  filesystem path only; callers with raw base64 pass a data: URL.
  Also drop unused RawImage.read type and collapse the redundant
  Float32Array copy in embedImage.
- src/triggers/api.ts: both handlers now whitelist fields with
  asNonEmptyString and parseOptionalPositiveInt before constructing
  the payload. Matches api::observe / api::search style, stops
  forwarding the raw req.body, clamps topK at the boundary.
- src/types.ts: add "vision_embed" to AuditEntry.operation union and
  rename EmbeddingProvider.embedImage's param to src (matches the
  implementation now that raw base64 isn't accepted).
- README.md, plugin/.claude-plugin/plugin.json: document
  AGENTMEMORY_IMAGE_EMBEDDINGS and bump tool count string.

Full suite still 794/794.
2026-04-21 12:07:47 +01:00
Rohit Ghumare 2edc02feb5 fix: resolve conflicts with main, address qodo-ai security findings
Resolves conflicts in:
- src/state/schema.ts (merged imageRefs + accessLog)
- src/functions/auto-forget.ts (kept audit, reordered ref decrement)
- src/functions/compress.ts (collapsed iii-sdk imports, s/ctx.logger/logger/)
- src/functions/evict.ts (kept audit + deleteAccessLog, deferred ref
  decrement until after delete succeeds)
- src/functions/retention.ts (kept source-aware delete routing, fetch mem
  from resolved scope, defer ref decrement)
- src/triggers/api.ts (kept main's registerFunction/trigger shape)
- src/types.ts (kept superset of AuditEntry.operation union)

qodo-ai action-required findings:
- mem::forget now decrements image refs for every deleted memory and
  observation (including the session-wipe branch), which stops sensitive
  screenshots leaking to ~/.agentmemory/images/ after user-initiated
  forget.
- Eviction / auto-forget now decrement refs only after the KV delete
  actually succeeds. The old order (decrement-then-delete) could
  desync ref counts when the delete threw.

Follow-on fixes:
- buildSyntheticCompression now carries modality and imageData through
  from raw to compressed, so the default zero-LLM path doesn't drop
  image metadata the viewer and governance paths rely on.
- Test harness updated for the current registerFunction (name, cb)
  signature and iii-sdk partial-mock pattern — 11/11 multimodal
  tests pass, full suite 788/788.

Closes #64.
2026-04-21 10:37:24 +01:00
Rohit Ghumare 21a34aa28c chore: bump version to 0.9.1 + add CHANGELOG 2026-04-21 00:30:27 +01:00