109 Commits

Author SHA1 Message Date
Rohit Ghumare e04ba88819 fix(cli): make fresh installs portable and persistent (#892)
* fix(cli): anchor engine cwd and rewrite bundled config with absolute paths

The bundled iii-config.yaml uses cwd-relative paths and the engine was
spawned without a cwd, so on global and npx installs ./data/state_store.db
and ./data/stream_store landed in whatever directory the user ran the CLI
from, and the iii-exec supervision block (src/**/*.ts watch, node
dist/index.mjs exec) never resolved, meaning the engine never supervised a
worker and nothing respawned it after the in-process worker died. That
surfaced as all data gone reports against a live REST port.

startIiiBin now prepares the launch: when the resolved config is the
bundled one it writes ~/.agentmemory/iii-config.runtime.yaml (regenerated
each boot) with absolute data paths under ~/.agentmemory/data and an
absolute node exec line for the installed worker entry, copies any legacy
./data stores from the invocation directory on first run, and spawns the
engine with cwd anchored at ~/.agentmemory. Repo checkouts keep the cwd
config and repo-root cwd, so dev behavior is unchanged. User overrides
via env or ~/.agentmemory/iii-config.yaml are passed through verbatim.

agentmemory remove gains a plan item for the generated runtime config.

Covered by test/engine-launch.test.ts including a drift guard that
rewrites the repo's real iii-config.yaml and asserts no relative paths
remain.

* fix: make fresh installs portable and persistent

* docs: refresh generated config reference
2026-08-23 15:54:08 +01:00
Rohit Ghumare be89b222b0 feat: devin support (cli adapter, plugin, cloud mcp) (#1214)
* feat: devin support replacing windsurf

* feat: devin cli adapter, plugin manifest, and hook payload compat

* fix: stale tool counts in translations and cwd validation
2026-08-16 14:44:10 +01:00
Rohit Ghumare 37ea1b99ad feat: cursor marketplace plugin with hooks, mcp, and skills (#1213)
* feat: cursor marketplace plugin with native hooks and mcp config

* fix: cursor payload compat and transcript prompt backfill in hooks

* fix: plugin-root hook paths, backfill ordering, session-id fallbacks

* docs: cursor plugin rows in readme, translations, and changelog

* fix: cursor native-plugin card, broken agent logos

* chore: sync openclaw and hermes plugin manifest versions

* docs: openclaw hook permission and hermes tool count

* chore: clawhub compat metadata for openclaw plugin

* docs: tested openclaw and hermes install rows in readme
2026-08-16 13:30:40 +01:00
Rohit Ghumare a2a2af936e feat: MCP version negotiation, translation sync, skills, mobile fix (#1211)
* fix: negotiate MCP protocolVersion instead of hardcoding 2024-11-05

* docs: sync 11 README translations with v0.9.29 English rework

* feat: memory-discipline and lesson skills for proactive memory use

* fix: bump remaining skill-count claims to 17

* fix: mobile install-box overflow and stale compare counts

* chore: regenerate website meta snapshot

* fix: honest compare benchmarks and scrollable mobile table

* docs: refine lesson skill wording and context block copy

* fix: compare table reads generated meta, focusable scroll region

* fix: render each lesson as a single context line
2026-08-15 23:05:55 +01:00
Rohit Ghumare 696cf7abb8 feat: recall quality, provenance, keyless graph, and connector parity (#1205)
* fix: prompt dedup, double summarize, docker-mode stop, hermetic tests

- observe: hash the hook payload when tool_input is absent so prompt_submit,
  notification, and lifecycle events dedup on content instead of collapsing
  onto one shared key that silently dropped every prompt after the first in
  a TTL window (#1173)
- stop hook: drop the direct /agentmemory/summarize POST; /session/end
  already fans out event::session::stopped which runs mem::summarize, so
  every Stop dispatched two full summarizes (#1203)
- cli: refuse to adopt or signal Docker/VM port holders (com.docker.backend,
  vpnkit, colima, ...) as the native engine unless --force; scope Docker-mode
  teardown to agentmemory's own compose services via rm -s -f instead of an
  unscoped down; reap the native worker before Docker teardown instead of
  deleting worker.pid with the process still running (#1151)
- tests: isolate HOME/USERPROFILE for the whole vitest run so suites stop
  reading the developer's real ~/.agentmemory/.env (#1178)

* fix(viewer): live stream port discovery, fresh tab data, honest states

- resolve the stream WebSocket target from /agentmemory/livez (new
  streamsPort field) instead of viewerPort-1 arithmetic, which pointed at
  the wrong server whenever the viewer bound a fallback port and silently
  degraded live updates to 10s polling — verified reaching 'live' on the
  fallback-port case
- refetch tab data on every tab entry; the loaded-once cache meant a
  memory saved by the agent never appeared until a hard browser reload
  (loading placeholders now render only on first load, so background
  refreshes don't flash)
- memories: rows expand on click/Enter to the full stored record —
  content, id, project, created, supersedes, files — plus a collapsible
  raw JSON view
- graph: a 503 with the structured disabled body renders 'Knowledge
  graph is off' with the enableHow text and docs link instead of a
  'query failed / Retry' error that sends users to server logs
- sessions: cards get role=button, tabindex, Enter/Space activation, and
  the detail panel scrolls into view on select; session ids truncate
  head…tail so the distinguishing suffix stays visible
- style search inputs and toolbar buttons on lessons/actions/crystals/
  replay (previously bare native controls); horizontal scroll containment
  for narrow viewports
- demo: only print the semantic-recall success notice when the search
  actually hit; on 0 hits explain the missing embedding key instead

* fix: thread agentId/project through save paths, per-session OpenCode scope

- REST /agentmemory/remember accepts and forwards agentId to mem::remember;
  it previously dropped the field so per-request multi-agent scoping was
  impossible over REST (#1159)
- memoryToObservation() carries the memory's agentId into the search-index
  shape; dropping it made every memory invisible to agent-scoped search
  (#1160)
- MCP memory_save path: the tool schema now exposes agentId, the in-worker
  MCP server forwards it, and the standalone stdio package parses and
  forwards both agentId and project — the stdio pipeline previously dropped
  project even though its schema advertised it (#1197)
- opencode plugin: project/cwd attribution is per-session (resolved from
  the session's own directory at session.created, pruned on session end)
  instead of module-level state that recorded every session in a
  multi-directory OpenCode process under whichever repo loaded the plugin
  first (#1188)

Live-verified: memory saved with agentId=agent-alpha is returned by
smart-search for agent-alpha and hidden from agent-beta.

* feat: hybrid recall everywhere, indexed lessons, provenance, recall hygiene

- mem::search ranks through the full BM25+vector+graph fusion when the
  vector index is populated (injected post-boot via setHybridRanker);
  the primary recall surface was keyword-only while only smart-search
  got hybrid ranking
- fusion weights normalize per item over the streams that actually
  ranked it, with a small explicit cross-stream agreement bonus; the
  old every-enabled-stream denominator permanently penalized
  single-stream hits (the graph stream is empty on default installs).
  Result order is now deterministic (score, best rank, id)
- lessons get a dedicated in-memory BM25 index built lazily from one KV
  list and maintained incrementally on save/delete/decay; recall
  previously listed and substring-scanned the whole corpus per query.
  Confidence x recency composite scoring is unchanged
- mem::remember finds supersession candidates through the search index
  (top-50) instead of walking every memory per save, with a full-scan
  fallback while the index is cold; near-miss similarity (0.4-0.7)
  is reported back as an advisory similarTo hint
- superseded memory versions leave the BM25 and vector indexes; the
  version chain stays in KV for history, but recall no longer returns
  an outdated fact as if current
- every observation and memory now carries an immutable origin block
  (channel: user|agent|tool|import|shared, detail, capturedAt) stamped
  at capture, save, and import, and inherited through both compression
  paths — the base for trust-aware retrieval and ingest screening
- regression tests: supersede index removal, similarTo hint,
  index-backed candidate discovery, lesson index recall/lazy
  rebuild/delete

* feat(viewer): two-pane sessions, navigable dashboard, motion and copy polish

- sessions: list + sticky detail panel side by side above 1100px (the
  detail previously rendered below the whole list, off-screen on any
  real corpus); selected/hover/active states with reserved left border
  so selection doesn't shift layout
- dashboard stat cards for sessions/memories/lessons/crystals/graph
  navigate to their tabs (click or Enter), with hover affordance
- observation subtitles that are raw serialized tool input now display
  the meaningful field (file path, command, pattern, url) instead of a
  JSON blob
- expanded memory rows show the new origin provenance (channel + detail)
- motion: 160ms view entrance, live-badge pulse, both gated behind
  prefers-reduced-motion; tabular numerals in tables
- mobile: header stops wrapping the dateline into the badge row
- lessons/crystals empty-state copy aligned with the header definitions
  (each concept was described two conflicting ways)

* refactor: cleanup pass over the branch diff

- shared test mocks: the three new test files use test/helpers/mocks
  (extended with update, store access, and an opt-in loose trigger)
  instead of three diverging inline copies
- lessons: record cache beside the index takes recall to zero KV
  round-trips (was up to 50 gets per call); the observation adapter
  moved next to memoryToObservation so both record kinds thread new
  fields in one place; dead reset export removed
- mem::search hybrid path carries the observations the ranker already
  loaded instead of refetching every result (halves KV I/O on the
  primary recall path); remember's candidate lookup skips ids that
  cannot resolve as memories and fails open to a full scan
- fusion: derived tiebreak field no longer rides along past the sort;
  comment trimmed to the non-obvious history
- cli: engine identity is a positive check (only the iii binary may be
  adopted or signaled; unknown port holders are refused, not just known
  VM names); worker reap extracted to one helper; demo notice picks its
  branch from a hoisted count
- api: livez and health share one instanceInfo source (health now
  reports streamsPort too) computed once at boot instead of rebuilding
  the merged env per request
- provenance: one importOrigin factory encodes the keep-or-mark rule at
  all three import sites
- opencode plugin: project resolution memoized per directory (was a
  blocking git subprocess per session event); session.created uses the
  entry it just built
- observe: origin channel derived from a named hook set, no nested
  ternary
- viewer: toolbar buttons merged into the .btn rules, one 720px media
  block, generic keyboard activation for role-carrying cards,
  scroll-into-view only on the stacked layout, 5s freshness gate on tab
  refetch (replay stays fetch-once, reason documented), subtitle
  humanizer covers the capture-side key variants

* feat(viewer): clarity pass and ambient refresh

- health notes/alerts translate their machine slugs into sentences
  (memory_heap_tight_93%_rss111mb reads as heap usage with context)
- lessons rows expand to full detail: rule, why-learned context, tags,
  learned/last-confirmed times, source sessions, raw record; column
  headers carry title hints for confidence and uses
- actions tab gets the same intro card as the other tabs (status flow
  and frontier explained on the populated view, not just when empty)
- timeline defaults to the session with the most observations instead
  of the newest, which was often a sparse just-started session
- consolidation status and top-concepts zero states explain what fills
  them and which flags gate it
- ambient background: the static dot grid becomes a slowly drifting
  ordered-dither field (quarter-res canvas, ~12fps, static frame under
  prefers-reduced-motion, theme-aware)
- dark theme: layered near-black surfaces, hairline borders, softened
  accent — replaces the flat gray borders

* feat(website): reskin on the near-black token system

- neutral token foundation in globals.css: canvas/canvas-soft/card
  surfaces, hairline borders, ink/body/mute text scale, one warm accent
  used sparingly, 8px card radius + pill buttons, focus-visible rings
- Inter display at weight 400 with tight tracking; mono uppercase
  eyebrows and captions; sentence-case body (case normalization only,
  no copy changes)
- hero: two-tone lowercase wordmark, install command as a soft input
  card, ambient drifting dot field capped at 0.12 alpha with a static
  frame under prefers-reduced-motion
- sections rebuilt on the card recipe: quiet background-shift hovers,
  hairline data table for the comparison, segmented tabs with polarity
  flip, per-vendor accent colors stripped from agent cards
- fixed two latent token misuses that resolved to nothing
- build green: 5/5 static pages, TypeScript clean

* fix(viewer): make the graph tab legible without edges

- nodes anchor to per-type cluster centers (captioned on the canvas)
  whenever relations are sparse; a pure force layout with no edges was
  an unlabeled scatter. Edge springs take over as real relations arrive
- labels always render on graphs of 30 or fewer visible nodes instead
  of only past a zoom threshold
- sidebar explains the entities-without-relations state and what
  produces edges; static legend removed (the type filter already
  carries color and shape)

* fix(viewer): graph readability on sparse data

- hover focus-fade only engages when the graph has edges; with none it
  faded every other node and suppressed all labels
- cluster anchor pull reduced and initial scatter widened so type
  groups spread instead of collapsing into blobs
- minimum node radius raised for degree-zero nodes; cluster captions
  offset above their groups

* fix(viewer): graph fits the view; site copy grounded in the repo

viewer graph:
- container height leaves room for the footer instead of running under it
- one-shot auto-fit zooms and pans to the node bounds once the layout
  settles, so first paint is framed instead of adrift
- cluster captions and small-graph labels hide below readable zoom

website copy (full pass, technical register):
- every unverifiable number removed: benchmark percentages, latency
  claims, press strip, testimonials, invented terminal output; the
  comparison table usage dropped rather than shipping stale competitor
  figures
- remaining stats are build-derived (54 MCP tools, 130 REST endpoints,
  12 hooks, 1619 tests) or live from the GitHub API (stars)
- feature copy corrected against src behavior: consolidation, graph
  extraction, and LLM compression activate with a provider key;
  provider list completed; install step numbering fixed
- release-branch capabilities surfaced: agent-scoped save and recall,
  write-time provenance channels, hybrid ranking on the primary recall
  path, indexed lessons, near-duplicate save hints, superseded-version
  recall hygiene, JSONL import deriving crystals and lessons
- em-dashes and slop phrasing removed throughout

* fix: restore featured strip, label collision avoidance, optional no-think

- website: FeaturedIn strip returns to the hero (its claims are the
  project's own credentials); rest of the grounded-copy pass unchanged
- viewer graph: canvas cluster captions removed and labels place
  greedily into free space (selected/hovered always win), so zoomed-out
  views degrade to fewer labels instead of overlapping pills
- graph extraction: AGENTMEMORY_LLM_NOTHINK=1 opt-in asks local
  reasoning models to skip their hidden thinking pass (several times
  faster, slight quality tradeoff); documented in .env.example, default
  behavior unchanged

* feat(website): testimonials return, OpenCode joins the featured connectors

- Testimonials section restored after LiveTerminal (launch-thread
  quotes are the project's own record)
- OpenCode promoted from the marquee to the featured grid: it ships a
  native capture plugin with per-session project attribution; fills the
  empty eighth slot
- full connector roster verified against src/cli/connect (18 dedicated
  adapters all present: featured grid + marquee)

* fix(viewer): official icon as the favicon (was a text placeholder)

* test(viewer): favicon assertion checks the served SVG, not a hex value

* test(viewer): favicon checks assert served SVG shape, not old artwork

* docs: readme grounded in source, changelog entry, env example consistency

* revert(website): restore measured benchmark claims and comparison table

The retrieval recall and token reduction figures are the project's own
measurements and its adoption story; earlier scrubbing was over-strict.
Backing them with a published run of the eval harness stays on the
roadmap.

* fix(website): drop the orphaned pause control on the hero field

The old animated constellation earned a pause button; the subtle dither
field does not, and prefers-reduced-motion already renders it static.

* fix(website): official OpenCode brand mark on the featured card

* chore: bump provider default models to current generations

OpenAI gpt-4o-mini to gpt-5.6-luna, Anthropic claude-sonnet-4-20250514 to claude-sonnet-5, Gemini gemini-2.5-flash to gemini-3.7-flash, MiniMax M2.7 to M3, OpenRouter default to anthropic/claude-sonnet-5. Premium cost warning matches the Sol tier; cheap-model hints lead with deepseek/deepseek-v4-flash-0731. README local picks move to qwen3 / gpt-oss / deepseek-r1 with a NOTHINK callout; cost table refreshed with verified OpenRouter list prices. Embedding defaults unchanged.

* feat: keyless heuristic graph extraction with LLM enrichment optional

Entities and co-occurrence do not need a language model: files and concepts on compressed observations already name the nodes, and appearing in the same observation is an edge. mem::graph-extract now always runs this deterministic pass, so the graph populates for keyless installs; the LLM pass layers typed relations on top only when GRAPH_EXTRACTION_ENABLED is set and a real provider exists. Session end fires extraction unconditionally.

* feat: DeepSeek Harness connector via home-level cordis patch layer

agentmemory connect dsh appends an @deepseek-ai/dsh-mcp-client row to DSH_HOME/cordis.patch.yml (default ~/.dsh), the machine-local patch layer every Harness profile loads, so the MCP tools register as mcp__agentmemory__* before the first turn. Idempotent, --force replaces the row, dry-run supported. Config shape verified against the mcp-client README and publish docs in deepseek-ai/deepseek-harness. Website agents grid and README connector table updated.

* feat: dsh --with-hooks auto-capture via Harness Claude Code bridge

DeepSeek Harness ships a first-party @deepseek-ai/dsh-hooks-claude-code plugin that runs Claude Code shaped command hooks on the harness's own interception points. connect dsh --with-hooks writes the bundled hook manifest (absolute script paths, reusing the codex-hooks merge engine) to DSH_HOME/agentmemory.hooks.json and appends a second patch row pointing the bridge at it. Auto-capture on SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop; PreCompact is outside the bridge subset and skipped. Adapter recategorized native. MCP-only installs never touch the hooks row; --force replaces both.

* feat(pi): automated connect install into pi's auto-discovery dir

connect pi was a stub printing manual copy steps because integrations/ never shipped in the npm package. The extension source now ships (integrations/pi/ in files), and the adapter copies index.ts + security.ts into ~/.pi/agent/extensions/agentmemory/, which pi auto-discovers with no settings.json edit; /reload picks it up live. Idempotent by content compare, stale copies refresh with a backup, dry-run supported. integrations/pi is also a private local pi package (pi-package keyword, pi.extensions manifest limited to index.ts so security.ts is not loaded as its own extension, peer deps on the pi core packages) so pi install ./integrations/pi works from a checkout; never published to npm. Type import moved to @earendil-works/pi-coding-agent (upstream rename).

* fix(codex): warn that hooks need one-time TUI trust approval

Codex executes only hooks with a recorded trusted_hash in config.toml, and the Hooks-need-review approval prompt appears only in the interactive TUI. A codex exec-only workflow therefore never runs freshly installed hooks and gets no signal why. connect codex --with-hooks now warns to launch codex once and choose Trust all, and to re-approve after upgrades since the refreshed absolute paths change the hash. Verified live on codex 0.147.0: before trust, exec dispatched nothing for agentmemory hooks; after TUI approval, SessionStart and UserPromptSubmit fired and the observation landed in the daemon.

* feat(pi): capture parity for the pi extension

Session registration on session_start (ordered after the health check so it fires on the first session of a fresh process), prompt capture with client-side dedup and user-channel provenance, per-tool observations from tool_result with AGENTMEMORY_TOOL_OBSERVE=0 opt-out, turn slices raised to 8000, memory_save scoped to the current project, session end plus one consolidate run on real quit only (no client summarize: session/end already fans out the summary), health accepts status ok, and refreshStatus binds the status setter before awaiting so a session replacement mid-check cannot throw a stale-context error. Live-verified on pi 0.84.2 with a local model: prompt and turn observations landed and the session closed as completed on quit.

* chore: regenerate skill reference docs

npm run skills:gen after the adapter, env, and tool changes: 20 adapters including dsh, refreshed env defaults, tool listing.

* refactor: trim oversized comments to constraint one-liners

Connector and extension comments compressed to the constraints the code cannot show; narrative headers, source citations, and restated behavior removed.

* docs(changelog): fold unreleased into the 0.9.29 release section

This branch ships as 0.9.29 (npm latest is 0.9.28; the previous 0.9.29 section was prepped but never published). One section, dated 2026-08-15, upgrade notes preserved, all 44 bullets intact.

* docs(readme): interactive-first install, dedupe, refresh stale counts

Install leads with npx and the first-run wizard (agent multi-select, provider pick, global-install offer) instead of six manual commands; Windows, EACCES, npx-cache, and iii-pin notes collapse into details blocks. Quick Start drops the duplicated install prose for an everyday-commands list. Nav drops the redundant iii Console link. Gist badge updated to the live 1.6k stars / 230 forks; test count pill and alt text updated to 1,648.

* fix: apply review round — ranking, indexes, lifecycle, connectors

Hybrid scoring normalizes once per query by the best attainable weighted score over streams that produced results, so configured stream weights survive single-stream hits; expansion merge gets the same deterministic tie-break. Graph functions register unconditionally (keyless installs previously fired mem::graph-extract at an unregistered function every session end) and the trigger goes through fireVoid; heuristic edges accumulate observation provenance for repeated pairs instead of dropping it. Supersession candidate search waits for the memory index walk (new isMemoryIndexReady signal) instead of trusting idx.size, and mem::search falls back to keyword search when the hybrid ranker throws. Lesson recall over-fetches under project/confidence filters, refreshes the index entry when reinforcement changes indexed text, and resetLessonIndex clears the cache after import and replay write lessons directly. Observe preserves primitive payloads in the dedup key so distinct prompts never collapse (regression test added). pi extension dedups prompts per session and passes project on both smart-search calls. dsh reads a corrupt hooks manifest as absent via readJsonSafe; pi install returns skipped instead of throwing when the bundled source is missing; both use the shared writeTextAtomic. Docker-mode stop clears each pidfile/state only after its shutdown succeeded and matches compose services at any indentation. Viewer livez fetch gets a 5s timeout. opencode session.deleted prunes through pruneSessionMaps (was leaking sessionProjects). MiniMax MAX_TOKENS doc says the real 4096 default. README MCP catalog: base-tools table completed to the registry's 14, the 8-tool core mode and 7-tool standalone fallback distinguished, two missing resources listed. Tests restore env vars without writing the string undefined, similarTo assertions are unconditional, vitest test home is unique per run, website meta regenerated, deprecated word-break replaced.

* docs: competitors refreshed — TencentDB Agent Memory column, entrants

TencentDB Agent Memory (TencentCloud OSS, May 2026, 22K stars) gets a full column: team memory hub captured through an LLM proxy, four asset types, PersonaMem 76% self-reported, Docker Core+Hub+Proxy stack. Stale star counts refreshed against the live API (mem0 58K to 63K, Letta 24K, Khoj 36K, supermemory 29K). A newer-entrants table covers Zep/Graphiti, Cognee, LangMem, Cloudflare Agent Memory, and Memobase, with matching choose-if sections in benchmark/COMPARISON.md. Section badge subtitle updated.

* fix: lesson index build races, rebuild ready flag, pi file backups

* docs: drop competitor links from README

* Update README.md
2026-08-15 20:37:12 +01:00
Rohit Ghumare 9c82d2aa7d chore(release): v0.9.29 with project-scope parity across capture surfaces (#1141)
* chore(release): v0.9.29 with project-scope parity across surfaces

Version trio + plugin manifests + supportedVersions + ExportData union
bumped to 0.9.29; CHANGELOG entry covering everything since v0.9.28 with
upgrade notes for the four visible behavior changes.

Fixes the endpoint-count drift on main (130 registered routes vs docs
saying 129 after #1132 landed in parallel with #1136).

Project-scope parity: OpenCode plugin, Hermes plugin, Pi extension, and
JSONL replay now resolve project the same way the hooks do (env
override, git toplevel basename, cwd basename) instead of sending raw
filesystem paths, closing #903 and #1135 and pre-empting the same bug
in pi. The filesystem watcher accepts AGENTMEMORY_PROJECT_NAME with the
old AGENTMEMORY_PROJECT kept as a deprecated alias, replay handles
Windows-recorded paths, and OpenCode file enrichment matches the
agent's lowercase tool names (the capitalized set never matched).

Tests: opencode fallback expectations updated to basenames per the
canonicalization, git-toplevel resolution covered with a fixture repo,
new project-scope-parity suite for replay and fs-watcher.

* fix(release): review findings, git-toplevel parity, doc counts

- skills generator dedupes routes on method plus path, so the REST
  reference lists all 130 registered routes instead of hiding the second
  method on ten dual-method paths (header said 119)
- fs-watcher trims AGENTMEMORY_PROJECT_NAME and the deprecated alias,
  treating whitespace as unset, and derives the git toplevel basename
  when watching a subdirectory
- replay resolves the git toplevel basename when the recorded cwd still
  exists locally (memoized per cwd), keeping the basename fallback for
  historical or cross-platform paths; no env override here since a bulk
  import spans many projects
- parity tests for replay git-root resolution, watcher git-root and
  trim behavior
- stat-tests badge updated from 1428+ to 1550+ passing

* fix(cli): refuse second-instance boot over a live daemon

Closes the class behind issue 1140: agentmemory consolidate (or any
unrecognized word) fell through the command table into the full server
boot, registering a duplicate worker on the running engine; on iii
0.11.2 the second instance's shutdown tears down the daemon's HTTP
trigger routing until a full engine restart. Unknown subcommands now
error with the supported list, and main() probes livez on the resolved
port and refuses to boot over a live daemon, so multi-instance setups
on other ports are unaffected. Verified behaviorally against the built
CLI: both paths refuse with exit 1.

Also from review: the watcher stamps each event with its own root's
project via a per-root map (an explicit config.project still overrides
for every root), and replay only accepts a non-empty string cwd from
parsed JSONL so malformed entries cannot reach the filesystem probe.

* test(watcher): two-repository flush events scope to their own project

* chore(release): bump packages/mcp, guard it, refresh CONTRIBUTING

packages/mcp was still 0.9.28 after the release bump because nothing
guarded it; a consistency test now pins it to package.json. CONTRIBUTING
release list corrected to the files a bump actually touches (no tracked
lockfile, the two extra plugin manifests, the export test derives from
VERSION now), and the subsystems table gains src/cli, integrations/pi,
and the generated-manifest note.

* fix(export): refuse over-frame export instead of dropping the worker

Closes the availability bug in issue 1142: GET /agentmemory/export
assembles the full store and returns it through sdk.trigger, so a store
whose serialized export passes the engine's 16 MiB WebSocket frame
(tungstenite max_frame_size, not raisable under the 0.11.2 pin) dies on
the worker->engine hop, drops the worker, and 404s every endpoint for
~1s. The session collections page on maxSessions/offset but ~18 others
do not, so a large store hits this at any parameter combination.

A shared frame-guard measures the serialized size before returning:
mem::export returns a small oversized error instead of the giant
object, and api::mesh-export returns 413 (same dead-end as #890). Either
way the over-frame payload never crosses the boundary, so the daemon
stays up and the failure is one clean request with a hint to narrow the
range. Full pagination of the non-session collections is a follow-up.

Layer 1 of the fix; verified with a synthetic oversized export returning
the error object (tiny) rather than the payload.

* ci: collapse to a single npm install to fix Node 24/26 CI

The two-step install (npm install --package-lock-only then npm ci) failed
only on the Node 24/26 matrix rows: their stricter npm rejects rolldown's
optional platform bindings (@rolldown/binding-android-arm64) that a
--package-lock-only pass does not fully enumerate. Lockfiles are gitignored,
so npm ci re-validation buys no reproducibility here. A single lenient
npm install resolves and installs in one pass.

* fix(mesh): scope exported memories by project like actions

api::mesh-export filtered actions by ?project but returned every project's
memories. On a mesh instance federating one project to a peer, the peer
pulled other projects' memories (cross-project leak), and those extras could
push the payload past the 16 MiB transport frame into a 413 even when the
requested project's own slice fit. Memories carry the same optional project
field as actions, so filter both before the frame-size guard runs.

Adds a regression test asserting a project-scoped export excludes other
projects' memories and that an oversized memory in another project no longer
413s the scoped request.

* chore(release): credit the Antigravity native hooks adapter in 0.9.29 notes

* chore(release): sweep stale 0.9.28 refs for 0.9.29

Deploy Dockerfiles/compose/render pins, AGENTS.md stats header, opencode
plugin manifest, website meta snapshot, test-count claims (1,428 -> 1,596)
in README/AGENTS/stat SVGs, and the missing 0.9.29 CHANGELOG compare link.

* chore(release): sync stat-tests badge to 1596+ and commit bridge exec bit

* refactor: trim frame-guard comments and drop issue refs from code
2026-08-09 13:22:25 +01:00
Bertho Joris d60652a705 feat(cli): native hooks adapter for Antigravity CLI (agy) (#1146)
* feat(cli): native hooks adapter for Antigravity CLI (agy)

Antigravity ships two products with unrelated configuration: the IDE,
already wired by `connect antigravity`, and the `agy` CLI, which reads
its customizations out of ~/.gemini/ and until now was not wired at all.
This adds `connect antigravity-cli` for the latter — MCP via
~/.gemini/config/mcp_config.json, plus optional native auto-capture hooks
behind --with-hooks.

Unlike Droid (#1130), the Codex merge engine could not be reused. The
Antigravity hooks contract differs in three ways:

  * hooks.json is a map of *named* hook bundles at the root, not the
    `{ hooks: { <Event>: [...] } }` envelope, so antigravity-hooks.ts
    implements a merge that owns top-level keys instead of per-event
    entries. User-authored bundles are preserved; a re-install replaces
    only the bundle whose commands point under the bundled plugin dir.
  * only five events exist (PreToolUse, PostToolUse, PreInvocation,
    PostInvocation, Stop) — no SessionStart/SessionEnd/UserPromptSubmit,
    so the session lifecycle is synthesized from the first PreInvocation
    and from Stop. PostInvocation is left unwired to avoid double-capture.
  * the stdin payload is camelCase and nested (`toolCall.args` with
    PascalCase keys, `conversationId`, `workspacePaths`), and stdout must
    be a JSON object — `pre-tool-use.mjs` writes raw prose when context
    injection is on.

plugin/scripts/antigravity-bridge.mjs bridges all three: it normalizes the
payload onto the shape the bundled hooks already accept, maps Cascade tool
names (view_file, replace_file_content, …) onto the read/edit/write/grep
vocabulary the capture heuristics use, pipes to the right script, discards
child stdout and always answers `{}` so Antigravity's own permission
decisions are never overridden.

Event names, tool names and arg keys were verified against the shipped
agy binary rather than docs alone (docs disagree on the global hooks
path); the customization dir is ~/.gemini/config/, matching where agy
already keeps mcp_config.json and plugins/.

Signed-off-by: Bertho Joris <bertho_joris@yahoo.co.id>

* fix(cli): keep $-bearing plugin paths literal when resolving hook commands

resolveBundle() expanded ${CLAUDE_PLUGIN_ROOT} via
String.prototype.replace with a string argument, so a plugin root
containing `$$`, `$&`, "$`" or `$'` was read as a replacement pattern
and rewritten:

  C:/plug$&in  ->  C:/plug${CLAUDE_PLUGIN_ROOT}in/scripts/...
  C:/plug$$in  ->  C:/plug$in/scripts/...

`$1` and `$<name>` are unaffected — the regex has no capture groups.

Switching to a replacer function keeps the path verbatim. The failure
mode this closes is silent: the hook installs with a broken command and
auto-capture simply never fires.

Regression test builds the manifest against a temp plugin root named
`plug$&$$in` and asserts the resolved command contains it literally.

Reported by CodeRabbit on #1146.

Signed-off-by: Bertho Joris <bertho_joris@yahoo.co.id>

* fix(antigravity): emit an explicit allow decision from the PreToolUse hook

Antigravity documents `decision` as a required field of PreToolUse hook
output, and agy treats a response that omits it as a denial: the bare `{}`
the bridge used to write made the agent refuse every matched tool call
(reported against agy 1.0.5 in cmux#5358) instead of passively capturing
it. `responseFor` now answers PreToolUse with `{"decision":"allow"}` and
leaves every other event on `{}`, so no event that carries no permission
decision starts overriding the user's own settings.

The response is written from the `finally` block, so a failed capture or an
unparseable payload still produces the contract rather than empty stdout,
which PreToolUse would read the same way as `{}`.

Tests cover both the pure contract and the built bundled script running
end to end with no server listening. Also extends the ARG_KEY_MAP test to
every mapped key and pins that an explicit canonical key wins over a
PascalCase alias.

* fix(antigravity): match agy's real hooks.json schema, verified against 1.0.15

Three defects found by probing a live agy 1.0.15 with an instrumented hook,
each of which stopped the adapter from capturing anything at all.

Lifecycle events take a flat handler list, not the tool-event wrapper. agy
parses `PreToolUse`/`PostToolUse` as `[{matcher, hooks: [...]}]` but
`PreInvocation`/`PostInvocation`/`Stop` as a bare `[{type, command}]`, since
there is no tool name to match on. Wrapping a lifecycle event makes agy read
the wrapper itself as a handler and reject the *whole file* with
`invalid hook "agentmemory": command hook must specify 'command'` — so the
mis-shaped Stop entry disabled every hook in the bundle, and would have
disabled hooks other tools had written to the same file.

`command` is not run through a shell and quotes are not stripped, so the
quoted path resolved to a module name that literally began with a double
quote: `Cannot find module 'C:\Users\…\.gemini\config\"C:\…\bridge.mjs"'`.
Commands are now bare. That also means a path containing spaces cannot be
expressed at all — quoted and unquoted both fail — so the installer refuses
with an explanation instead of writing hooks that can only fail at tool time.

The merge engine reads both shapes when deciding which bundles agentmemory
owns, so a re-install over the old wrapped layout still replaces it rather
than leaving a second copy behind.

Tests pin both event shapes, the absence of quotes, the space check, and
normalization of a payload captured verbatim from the live run — which also
confirms `conversationId`, PascalCase `toolCall.args`, and that agy sends no
`cwd` key at all.

* refactor(antigravity): cut comment volume to match the sibling adapters

The bundled script carried 24 comment lines where every other script in
plugin/scripts has three. The bundler strips `//` comments but preserves
JSDoc blocks, so the fix is to document the bridge's exported helpers with
line comments: the explanations stay in source and the generated artifact
comes out as clean as its siblings.

The connect adapter and merge engine restated the same facts in a file
header and again in a per-function block. Kept one statement of each,
dropped the repetition, and left the verified agy behaviour in place since
that is the part not derivable from the code.

---------

Signed-off-by: Bertho Joris <bertho_joris@yahoo.co.id>
2026-08-03 18:35:41 +01:00
Som Samantray 5023cf3ccb fix(memory): honest memory_forget reporting + lesson delete path (#1132)
* fix(memory): guard mem::forget delete/count on record existence

Calling mem::forget with a lesson id (lsn_*) deleted a nonexistent key
from the memories keyspace, counted it, and reported success. Guard the
delete, index cleanup, and counter on the kv.get result, matching the
mem::governance-delete pattern, so nonexistent ids return
{ success: true, deleted: 0 } with no audit row. Closes #1120.

* feat(lessons): add mem::lesson-delete soft-delete function

Register mem::lesson-delete to set deleted: true on a lesson, mirroring
the lesson-strengthen existence guard and audit pattern. Read paths
already filter !l.deleted, and re-saving deleted content creates a fresh
lesson. Adds lesson_delete to the audit operation union.

* feat(mcp): expose memory_lesson_delete tool and REST endpoint

Wire mem::lesson-delete through the MCP tool registry and dispatch
case (memory_lesson_delete) and a POST /agentmemory/lessons/delete REST
route with 400 for a missing lessonId and 404 for a nonexistent lesson.

* chore(consistency): bump tool/endpoint counts to 54/129

Adds memory_lesson_delete to the registry, so update every count surface:
tool-count test, README badge and prose, AGENTS.md stats,
INSTALL_FOR_AGENTS.md, plugin manifests and docs, and the two code
comments this change makes stale. REST endpoint count goes 128 to 129
for the new /agentmemory/lessons/delete route.

* refactor(lessons): simplify 404 mapping and restore decay-delta test

Cast the lesson-delete trigger result once instead of twice inline, and
restore the lastDecayedAt incremental-delta decay test that was dropped
when the lesson-delete describe block was added.

* fix(review): align 404 error shape and regenerate skill references

Review fixes: the lesson-delete REST route now returns the repo-standard
{ error: 'lesson not found' } body on 404 instead of the function-shaped
{ success: false } payload, matching api::memory-by-id. Regenerated the
autogen MCP and REST skill references so memory_lesson_delete and the
lessons/delete route appear in the tables with accurate counts.

* fix(lessons): normalize lessonId at entry points and harden no-op test

Address CodeRabbit review: trim lessonId once at both the MCP dispatch
and REST route before triggering mem::lesson-delete (whitespace-padded
ids previously 404'd or looked up raw), and extend the nonexistent-
memoryId regression test to assert the no-op path performs no kv.delete
and no search-index cleanup.

---------

Co-authored-by: Rohit Ghumare <48523873+rohitg00@users.noreply.github.com>
2026-08-02 19:07:09 +01:00
Abdullah Alaqeel 9be3d2743a feat(opencode): allow AGENTMEMORY_PROJECT_NAME to override project name (#1125)
* feat(opencode): allow AGENTMEMORY_PROJECT_NAME to override project name

The OpenCode capture plugin resolved the project as ctx.worktree ||
ctx.project?.id || process.cwd() (a full path) with no way to override
it. It now consults AGENTMEMORY_PROJECT_NAME first — the same env var the
Claude hooks use in resolveProject() — falling back to the existing
resolution when unset.

This lets users pin a stable project name and aligns the two plugin
ecosystems so OpenCode and Claude sessions can share memory when the env
var is set to the same value.

Signed-off-by: Abdullah Alaqeel <abdullah.t.aqeel@gmail.com>

* test(opencode): replace source-text assertions with runtime project-name tests

Execute the plugin end-to-end, fire a session.created event, and assert
the /session/start payload's project field. Covers whitespace trimming
of AGENTMEMORY_PROJECT_NAME and the documented fallback chain
(ctx.worktree -> ctx.project.id -> process.cwd).

Signed-off-by: Abdullah Alaqeel <abdullah.t.aqeel@gmail.com>

---------

Signed-off-by: Abdullah Alaqeel <abdullah.t.aqeel@gmail.com>
2026-08-02 19:00:05 +01:00
Bertho Joris d219763ccb feat(cli): native hooks adapter for Droid (Factory.ai) (#1130)
* feat(cli): add native hooks adapter for Droid (Factory.ai)

Droid ships a first-party hooks system (~/.factory/hooks.json) using
the same manifest shape as Codex/Claude Code. `agentmemory connect
droid --with-hooks` now merges the bundled hooks.droid.json into it,
covering SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, and
SessionEnd via the existing buildMergedHooks merge/re-install logic.

Also fixes a Windows test-isolation bug in connect-new-agents.test.ts
where os.homedir() reads USERPROFILE (not HOME), which was silently
letting adapter tests write into the real user home directory.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: changelog entry + regenerate agentmemory-agents skill reference for Droid hooks

CHANGELOG [Unreleased] entry for the new --with-hooks path, and
npm run skills:gen to sync the auto-generated agents table (pulled
from src/cli/connect/index.ts protocolNote) so it reflects Droid's
updated hooks capability. tools/rest/config/hooks reference docs
showed as stale too but diffed empty (pre-existing line-ending-only
drift on this checkout) so left untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(cli): surface skipped hook results in dry-run branch, tighten Droid hook tests

- json-mcp-adapter: capture and warn on skipped hook results in the
  dry-run branch (consistent with already-wired and post-install branches)
- droid-connect-hooks test: assert exact five-event set instead of
  presence-only plus selective absence checks
- droid-connect-hooks test: use deep equality (toEqual) for idempotency
  check instead of per-event length comparison

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* fix(cli): use Droid Create tool in PreToolUse matcher

Droid has no Write tool — file creation is Create — so the Claude-era
matcher blocked enrichment on new files. Align the manifest and test.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: drop changelog entry and trim Droid README row

Per review: remove the Unreleased CHANGELOG addition, and keep the
Droid agents-table row short with a brief --with-hooks mention.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 11:53:52 +01:00
Rohit Ghumare 6cc9b9f0fe fix: env hydration, indexing, consolidation lifecycle, connector activation, hardening (#1136)
* fix: env hydration, indexing, consolidation, connectors, hardening

- config: hydrate ~/.agentmemory/.env into process.env at boot so all modules see it
- search: shared indexRecords() so export-import and replay populate BM25 and vector (#1072)
- snapshot: wire the periodic timer (#1006), clamp non-positive intervals, add a reentrancy guard
- schema: CJK-aware jaccard dedup plus exact-match fallback for short memories
- embeddings: shared resolveDimensions() so openrouter stops hardcoding 1536 (#1002)
- viewer: buffer request bodies before decoding to fix multibyte corruption (#930)
- providers: retry 429/503 with Retry-After under a total-elapsed budget cap
- consolidation: fire on session stop (#1087), gate keyless installs, debounce the per-turn stop hook, drop the client-side double-fire
- evict: bound stale-session recovery to one consolidation pass
- api/patterns: bound session fan-out (#1100)
- connect: write a memory-usage guideline into each hook-less agent's native rules file (12 agents, doc-verified paths, --no-guidelines opt-out)
- graph: import graphify's graph.json via mem::graph::import-graphify + POST /agentmemory/graph/import-graphify; shared persistGraphDelta with endpoint remap so merged nodes never leave dangling or duplicate edges
- fs-watcher: stat roots before fs.watch so missing roots fail deterministically on Node 24+
- test: regression tests for every fix

* fix: address review findings on import, debounce, and connect paths

- guidelines: refuse to touch files with a lone or reversed marker pair
- export-import/replay: indexing after committed writes is best-effort,
  logged instead of failing the import; flatten the nested runChunked so
  replace-mode deletes stay bounded to one chunk
- graph: persist the snapshot when merge-only batches mutate cached
  topNodes/topEdges entries
- graph-import: async fs, typeof validation on path/cwd; REST handler
  whitelists the payload and 400s non-string values
- fetch: cancel discarded response bodies before retrying
- events: serialize the consolidation cooldown check so concurrent stops
  cannot both pass the read-check-write window
- evict: gate recovered-session consolidation on isConsolidationEnabled
  and mirror the stop path's force flag
- search: rebuild indexes per session chunk to bound peak memory
- test: regression coverage for each (malformed markers, concurrent
  stops, snapshot persistence, AMBIGUOUS/default mappings, env isolation)
2026-08-02 11:16:30 +01:00
Matt Van Horn d8b5267c36 fix(cli): add --data-dir flag + AGENTMEMORY_DATA_DIR so engine state lives outside repos (#314)
CI / test (20, macos-latest) (push) Has been cancelled
CI / test (20, ubuntu-latest) (push) Has been cancelled
CI / test (22, macos-latest) (push) Has been cancelled
CI / test (22, ubuntu-latest) (push) Has been cancelled
* fix(cli): add --data-dir flag + AGENTMEMORY_DATA_DIR so engine state lives outside repos (#303)

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>

* feat(cli): adopt legacy ./data stores before platform-default data dir

Before falling back to the new platform default, detect an existing
./data (prior default) store and keep using it so existing users do not
boot into an empty store. Covers both paths with tests.

* docs(skills): regenerate REFERENCE.md to include AGENTMEMORY_DATA_DIR

The autogen env block in the agentmemory-config skill reference was stale
after adding the --data-dir flag; regenerated via npm run skills:gen so
AGENTMEMORY_DATA_DIR is listed (34 -> 35 recognized variables). Fixes the
failing skills-reference drift check.

* docs: fix the local-models anchor in the provider table

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>

* fix: narrow legacy data adoption, XDG relocation, and env export

Addresses the three blocking review items.

1. resolveDataDir only adopts a cwd-local data/ directory when it is actually
   ours, keyed on data/state_store.db or data/iii-config.yaml existing. Before,
   any data/ folder was adopted, so running the CLI in an unrelated repo that
   happens to have one (common in ML projects) would start writing our stores
   into it.

2. cli.ts only exports AGENTMEMORY_DATA_DIR when the user actually supplied a
   --data-dir flag or env value. Exporting it for the default too meant
   ${AGENTMEMORY_DATA_DIR:-iii-data} in docker-compose never fell back to the
   named volume, so existing docker users booted against an empty bind-mounted
   platform dir with their memories stranded in the volume.

3. The XDG relocation now requires the XDG path to actually live under the git
   root, rather than firing whenever cwd is inside any repo with XDG_DATA_HOME
   set. Previously XDG_DATA_HOME=/mnt/data run from a normal repo was ignored
   with a warning claiming it was inside a git worktree when it was not.

The two smaller items you flagged as fine-as-follow-ups (IMAGES_DIR not moving
with --data-dir, and renderIiiConfig rewriting file_path by exact string match)
are untouched here.

---------

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
2026-07-25 22:52:31 +01:00
Rohit Ghumare 6761a99ba1 fix: guard hooks against null payload (#1074)
#1047: JSON.parse("null") returns null without throwing, so every hook's parse guard passed it through and the first data.xxx access threw a TypeError. Bare main() turned that into an unhandled rejection -> exit 1 -> host reported 'hook failed' on every affected tool call. All 13 hook entrypoints now guard non-object payloads before dereferencing and wrap main() in .catch() to fail closed (silent exit 0).

#1057: mem::context and api::context filtered candidate sessions by project only, leaking cross-agent observations/summaries under AGENTMEMORY_AGENT_SCOPE=isolated. Now applies the same agent-scope filter as mem::search (#817); api::context, api::session::start, and event::session::started forward agentId.

Also: bump 0.9.28 across manifests/deploy/export-import set; refresh stale README/AGENTS stats (files/LOC/functions/KV; AGENTS tests 950+ -> 1,428+) and regenerate the website meta snapshot to 0.9.28; CHANGELOG 0.9.28 section; remove the rate-limited star-history chart from README and all 11 translations.
2026-07-19 11:40:26 +01:00
Rohit Ghumare f3dc7f8ece fix: CLI help and docs papercuts (#897)
* fix: CLI help and docs papercuts

- derive the connect agent list in --help from the adapter registry (18 agents, was a stale 9-name list)
- warn on unknown --tools values instead of silently treating them as all
- point claude-code, hermes, and openclaw protocol notes at real doc URLs (the docs/*.md paths never existed)
- correct skill count to 15 in the connect summary and AGENTS.md
- name the real 8 core tools in INSTALL_FOR_AGENTS.md
- extend the tool-count consistency test to guard skill counts and the core tool sentence

* chore: regenerate skill reference docs
2026-06-11 00:00:10 +01:00
Rohit Ghumare c2d836d623 Add opencode connect adapter, single-source the onboarding picker (#883)
* fix(connect): add opencode adapter and derive onboarding picker from ADAPTERS

The onboarding agent picker and the connect interactive menu maintained two separate hardcoded lists that had drifted: onboarding offered opencode/goose/kilo/aider/claude-desktop/windsurf/roo (which connect had no adapter for, so picking them hit 'no adapter available, skipped'), while connect had antigravity/kiro/warp/continue/zed/droid/qwen that onboarding never offered.

Add a real opencode adapter writing OpenCode's documented mcp schema (top-level mcp key, command as array, type/enabled), and make onboarding's buildAgentOptions derive from connect's ADAPTERS so the two can never diverge again. Connect-only adapters now appear in onboarding; opencode is now wireable; phantom agents with no adapter no longer appear. Fixes #872.

* fix(connect): harden opencode adapter mcp merge and drop unexpanded env block

Guard the mcp spread against a non-object value in an existing opencode.json, and remove the environment block whose shell-style ${VAR:-default} values OpenCode does not expand (writing them literally would clobber a user's real shell AGENTMEMORY_URL). The stdio child inherits the shell env and the shim defaults unset vars. Also drop a redundant inline comment in onboarding.

* docs(skills): regenerate agents reference for opencode adapter

Adding the opencode adapter changed ADAPTERS (17 -> 18); regenerate the auto-derived agents table so npm run skills:check passes in CI.
2026-06-10 14:15:09 +01:00
Rohit Ghumare 45de643cd2 Detailed, self-updating skills covering the whole system (#854)
* feat(skills): detailed tiered skills covering the whole system

Restructure the 8 action skills into the tiered format (SKILL.md under 100 lines, EXAMPLES.md, shared troubleshooting, anti-patterns, cross-refs) and add 7 reference skills covering MCP tools, REST API, config, connect adapters, hooks, architecture, and skill authoring. Reference data tables are generated from source by scripts/skills/generate.ts and guarded against drift by npm run skills:check in CI, so the docs stay current as the repo changes.

* docs(skills): fix recall REST mapping and label code fences

Correct the recap/handoff REST fallback in _shared/TROUBLESHOOTING.md to POST /agentmemory/smart-search (/agentmemory/recall is not a registered route), and add language identifiers to all opening code fences across the skill docs to satisfy markdownlint MD040.
2026-06-07 18:36:59 +01:00
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