Skip to content

feat: add OpenCode auto-plugin for zero-config MemPalace integration - #297

Open
Milofax wants to merge 4 commits into
MemPalace:developfrom
Milofax:feat/opencode-auto-plugin
Open

feat: add OpenCode auto-plugin for zero-config MemPalace integration#297
Milofax wants to merge 4 commits into
MemPalace:developfrom
Milofax:feat/opencode-auto-plugin

Conversation

@Milofax

@Milofax Milofax commented Apr 8, 2026

Copy link
Copy Markdown

Summary

  • Adds examples/opencode_auto_plugin.js — a drop-in OpenCode plugin that auto-initializes MemPalace in every git-tracked project
  • Adds examples/opencode_auto_plugin_setup.md — full setup guide matching the existing docs style (cf. gemini_cli_setup.md, mcp_setup.md)

What the Plugin Does

  1. On first event of each OpenCode session → detects git root → spawns mempalace init --yes <root> in background
  2. After init succeeds → spawns mempalace mine --limit 200 <root> in background
  3. On every chat → injects the MemPalace Memory Protocol into the system prompt via experimental.chat.system.transform

Zero per-project config needed. Drop the file into ~/.config/opencode/plugins/ and restart OpenCode.

Key Design Decisions

Decision Why
System-prompt injection, NOT AGENTS.md patching No disk writes, no conflicts with other tools, idempotent
Detached + unref spawns OpenCode never blocks on init/mine
Git-only scope Prevents accidental init in ~, /tmp, etc.
In-memory dedup Set mempalace init runs at most once per project per process
Log to /tmp/mempalace-auto.log Easy debugging, no sensitive data beyond paths

Complementary to PR #268

PR #268 adds the manual opencode_setup.md integration guide. This plugin automates what that guide describes — users install it once and MemPalace works in every project automatically.

Verification

  • node --check examples/opencode_auto_plugin.js
  • Smoke-tested: factory → hooks returned, system.transform → protocol injected, dedup → no duplicates ✅
  • No Python code modified — tests not affected

Adds an OpenCode plugin that automatically initializes MemPalace and
injects the Memory Protocol into every agent session. No per-project
config needed — drop the plugin file into ~/.config/opencode/plugins/
and every git-tracked project gets MemPalace automatically.

Plugin hooks:
  - event: runs 'mempalace init --yes' + 'mempalace mine' on first
    session event (background, detached, idempotent)
  - experimental.chat.system.transform: injects the MemPalace Memory
    Protocol into the system prompt so agents know when/how to save

Includes setup guide matching the existing integration doc style
(cf. gemini_cli_setup.md, mcp_setup.md).
@bgauryy

bgauryy commented Apr 8, 2026

Copy link
Copy Markdown

PR Review: feat: add OpenCode auto-plugin for zero-config MemPalace integration

Executive Summary

Aspect Value
PR Goal Drop-in OpenCode plugin that auto-initializes MemPalace in every git-tracked project and injects the Memory Protocol into every agent session
Files Changed 2 (both new)
Risk Level 🟡 MEDIUM - Phantom tool names will confuse agents; FD leak compounds over sessions
Review Effort 2 - Small, self-contained feature in examples/
Recommendation 🔄 REQUEST_CHANGES

Affected Areas: examples/opencode_auto_plugin.js, examples/opencode_auto_plugin_setup.md

Business Impact: Lowers barrier to MemPalace adoption for OpenCode users; wrong tool names will cause failed tool calls and erode agent trust in the protocol.

Flow Changes: No changes to existing code — both files are net-new additions under examples/.

Ratings

Aspect Score
Correctness 2/5
Security 4/5
Performance 3/5
Maintainability 3/5

PR Health

  • Has clear description
  • References ticket/issue (if applicable)
  • Appropriate size (or justified if large)
  • Has relevant tests (if applicable) — no tests for the plugin

High Priority Issues

🐛 #1: Protocol advertises 19 tools — 5 don't exist in the MCP server

Location: examples/opencode_auto_plugin.js:54-62 | Confidence: ✅ HIGH

The hardcoded MEMPALACE_PROTOCOL string claims "19 memory tools" and lists tool names that have no corresponding registration in mempalace/mcp_server.py. The real MCP server exposes 14 tools. Phantom tool names will cause agents to attempt calls that always fail, degrading trust in the entire protocol.

Ghost tools (not registered in MCP server):

  • mempalace_list_wings — use mempalace_browse instead
  • mempalace_list_rooms — use mempalace_browse instead
  • mempalace_get_taxonomy — use mempalace_browse instead
  • mempalace_check_duplicate — internal helper _check_duplicate(), not exposed as MCP tool
  • mempalace_get_aaak_spec — no such tool
  • mempalace_graph_statsgraph_stats() is imported but consumed inside tool_status(), not exposed separately

Actual 14 tools:
Palace read: mempalace_status, mempalace_browse, mempalace_search
Palace write: mempalace_add_drawer, mempalace_delete_drawer
Knowledge Graph: mempalace_kg_query, mempalace_kg_add, mempalace_kg_invalidate, mempalace_kg_timeline, mempalace_kg_stats
Navigation: mempalace_traverse, mempalace_find_tunnels
Diary: mempalace_diary_write, mempalace_diary_read

- ### Available Tools (19 total)
- - Palace read: `mempalace_status`, `mempalace_search`, `mempalace_list_wings`, `mempalace_list_rooms`, `mempalace_get_taxonomy`, `mempalace_check_duplicate`, `mempalace_get_aaak_spec`
- - Palace write: `mempalace_add_drawer`, `mempalace_delete_drawer`
- - Knowledge Graph: `mempalace_kg_query`, `mempalace_kg_add`, `mempalace_kg_invalidate`, `mempalace_kg_timeline`, `mempalace_kg_stats`
- - Navigation: `mempalace_traverse`, `mempalace_find_tunnels`, `mempalace_graph_stats`
- - Diary: `mempalace_diary_write`, `mempalace_diary_read`
+ ### Available Tools (14 total)
+ - Palace read: `mempalace_status`, `mempalace_browse`, `mempalace_search`
+ - Palace write: `mempalace_add_drawer`, `mempalace_delete_drawer`
+ - Knowledge Graph: `mempalace_kg_query`, `mempalace_kg_add`, `mempalace_kg_invalidate`, `mempalace_kg_timeline`, `mempalace_kg_stats`
+ - Navigation: `mempalace_traverse`, `mempalace_find_tunnels`
+ - Diary: `mempalace_diary_write`, `mempalace_diary_read`

Medium Priority Issues

🐛 #2: File descriptor leak in spawnDetached — 2 FDs leaked per init/mine cycle

Location: examples/opencode_auto_plugin.js:96-100 | Confidence: ✅ HIGH

openSync(LOG_FILE, 'a') is called twice for out and err. These FDs are passed to the child's stdio array, but the parent's copies are never closed. Node.js does not auto-close FDs passed to spawn({ stdio: [_, out, err] }). Each spawnDetached call leaks 2 FDs in the parent process. With init + mine per project, that's 4 leaked FDs per project.

  function spawnDetached(cmd, args, tag) {
    try {
      const out = openSync(LOG_FILE, 'a');
      const err = openSync(LOG_FILE, 'a');
      const child = spawn(cmd, args, {
        detached: true,
        stdio: ['ignore', out, err],
      });
+     closeSync(out);
+     closeSync(err);
      child.on('error', (e) => log(`[${tag}] spawn error: ${e.message}`));
      child.unref();
      return child;
    } catch (e) {
      log(`[${tag}] spawn threw: ${e?.message ?? e}`);
      return null;
    }
  }

🐛 #3: Failed init permanently prevents retry for the session

Location: examples/opencode_auto_plugin.js:113-114 | Confidence: ⚠️ MED

initializedProjects.add(projectRoot) is called immediately, before init completes. If mempalace init fails (e.g. transient disk error, Python not in PATH temporarily), the project is permanently marked as initialized. No subsequent event will retry.

  function ensureInitialized(projectRoot) {
    if (initializedProjects.has(projectRoot)) return;
-   initializedProjects.add(projectRoot);
  
    log(`init: ${projectRoot}`);
    const init = spawnDetached('mempalace', ['init', '--yes', projectRoot], 'init');
-   if (!init) return;
+   if (!init) return;  // spawn itself failed, don't mark
  
    init.on('exit', (code) => {
      if (code === 0) {
+       initializedProjects.add(projectRoot);
        log(`init ok → mine: ${projectRoot}`);
        spawnDetached('mempalace', ['mine', '--limit', '200', projectRoot], 'mine');
      } else {
        log(`init failed code=${code}: ${projectRoot}`);
      }
    });
  }

Trade-off: Moving add() after success means a failing init will retry on every event. If preferred, add a failedProjects Set with a max-retry counter instead.


Low Priority Issues

🏗️ #4: Hardcoded protocol text will drift from actual MCP server

Location: examples/opencode_auto_plugin.js:31-62 | Confidence: ⚠️ MED

The protocol is a 30-line string literal duplicating tool names and usage guidelines. As the MCP server evolves (tools added/removed/renamed), this inline copy will silently go stale — exactly the kind of drift that caused Issue #1.

Consider: reading the protocol from a shared file (e.g. examples/mempalace_protocol.md) at plugin startup, or generating it from the MCP server's tool registry at init time.


🎨 #5: Setup doc references gemini_cli_setup.md which is not in examples/

Location: examples/opencode_auto_plugin_setup.md:3 | Confidence: ⚠️ MED

The setup doc says "matching the existing docs style (cf. gemini_cli_setup.md, mcp_setup.md)" — mcp_setup.md exists in examples/, but there is no gemini_cli_setup.md in the repo.


🎨 #6: Log timestamp in setup doc is 2025, PR is from 2026

Location: examples/opencode_auto_plugin_setup.md:67-69 | Confidence: ✅ HIGH

The example log output shows 2025-04-08 timestamps. Minor cosmetic issue but signals stale copy-paste.


Created by Octocode MCP https://octocode.ai

Milofax added 2 commits April 9, 2026 11:21
Close parent file descriptors after spawning detached mempalace
processes to avoid leaking FDs across projects.

Track in-flight initialization separately from completed projects so
failed init runs can retry on later events without spawning duplicate
concurrent init processes.

Also fix the setup guide's stale timestamp and replace the invalid
 verification command with
=======================================================
  MemPalace Status — 10000 drawers
=======================================================

  WING: captains_deck
    ROOM: general               6352 drawers
    ROOM: backend               1082 drawers
    ROOM: frontend              1069 drawers
    ROOM: testing                537 drawers
    ROOM: configuration          513 drawers
    ROOM: scripts                301 drawers
    ROOM: design                 101 drawers
    ROOM: documentation           45 drawers

=======================================================.
Retryable init failures in the OpenCode auto-plugin could leave a
project stuck in the initializing set if the child errors before exit,
preventing later events from re-running mempalace init.

Finalize initialization on error, exit, or close and keep the docs
aligned with the per-project process semantics.
@Milofax

Milofax commented Apr 9, 2026

Copy link
Copy Markdown
Author

Addressed the reliability issue.

Fix

  • finalize init state on , , or
  • prevent projects from getting stuck in after a failed spawn
  • keep the setup doc aligned with the plugin's per-project process semantics

Verification

  • plugin smoke test passed ✅
  • changes pushed in

Thanks — this one was real.

@Milofax

Milofax commented Apr 9, 2026

Copy link
Copy Markdown
Author

Correcting my previous comment — the shell ate some backticked text.

Addressed the reliability issue.

Fix

  • finalize init state on error, exit, or close
  • prevent projects from getting stuck in initializingProjects after a failed spawn
  • keep the setup doc aligned with the plugin's per-project process semantics

Verification

  • node --check examples/opencode_auto_plugin.js
  • plugin smoke test passed ✅
  • changes pushed in f800db5

Thanks — this one was real.

A project was previously marked initialized immediately after
mempalace init succeeded, even if the background mine step failed.
That could leave the repo permanently unmined for the rest of the
opencode process.

Track mine-in-flight state separately and only mark the project
initialized after mempalace mine exits successfully. Failed mine runs
now clear the in-flight state so later events retry automatically.

Also correct the setup guide's security notes to mention the git root
probe and that the plugin log captures mempalace stdout/stderr.
option-K added a commit to option-K/mempalace that referenced this pull request Apr 9, 2026
First-party OpenCode plugin for MemPalace lifetime memory system.
Integrates into OpenCode's lifecycle via three hooks:
- experimental.session.compacting: Pre-compaction memory rescue
- experimental.chat.system.transform: L0/L1 wake-up injection
- chat.message: Message-count-triggered background mining

Key advantages over existing PR MemPalace#297 (opencode_auto_plugin.js):
- Modular TypeScript architecture (not a single-file script)
- Pre-Compact rescue hook (not just system.transform)
- Structured L0/L1 memory injection (not hardcoded protocol string)
- Message-count threshold trigger for auto-save (not per-chat injection)
- Debounced mining with lock to prevent concurrent saves
- Idle/crash auto-save via session.idle and process signal handlers

Closes MemPalace#342
option-K added a commit to option-K/mempalace that referenced this pull request Apr 10, 2026
First-party OpenCode plugin for MemPalace lifetime memory system.
Integrates into OpenCode's lifecycle via three hooks:
- experimental.session.compacting: Pre-compaction memory rescue
- experimental.chat.system.transform: L0/L1 wake-up injection
- chat.message: Message-count-triggered background mining

Key advantages over existing PR MemPalace#297 (opencode_auto_plugin.js):
- Modular TypeScript architecture (not a single-file script)
- Pre-Compact rescue hook (not just system.transform)
- Structured L0/L1 memory injection (not hardcoded protocol string)
- Message-count threshold trigger for auto-save (not per-chat injection)
- Debounced mining with lock to prevent concurrent saves
- Idle/crash auto-save via session.idle and process signal handlers

Closes MemPalace#342

@web3guru888 web3guru888 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-structured plugin — the auto-init + auto-mine on first event per project is a clever pattern, and the system prompt injection via experimental.chat.system.transform keeps it non-invasive.

Good design decisions:

  • Per-process dedup via in-memory Set prevents redundant init/mine
  • Skipping non-git directories is the right default
  • Detached spawns keep the plugin non-blocking
  • Log file at /tmp/mempalace-auto.log helps debugging

The protocol injection string is comprehensive (all 19 tools listed) — might be worth noting that tool availability depends on the MCP server being configured separately, so agents don't hallucinate tool calls that aren't actually wired up.

Nice addition to the examples.

🔭 Reviewed as part of the MemPalace-AGI integration project — autonomous research with perfect memory. Community interaction updates are posted regularly on the dashboard.

option-K added a commit to option-K/mempalace that referenced this pull request Apr 10, 2026
First-party OpenCode plugin for MemPalace lifetime memory system.
Integrates into OpenCode's lifecycle via three hooks:
- experimental.session.compacting: Pre-compaction memory rescue
- experimental.chat.system.transform: L0/L1 wake-up injection
- chat.message: Message-count-triggered background mining

Key advantages over existing PR MemPalace#297 (opencode_auto_plugin.js):
- Modular TypeScript architecture (not a single-file script)
- Pre-Compact rescue hook (not just system.transform)
- Structured L0/L1 memory injection (not hardcoded protocol string)
- Message-count threshold trigger for auto-save (not per-chat injection)
- Debounced mining with lock to prevent concurrent saves
- Idle/crash auto-save via session.idle and process signal handlers

Closes MemPalace#342
option-K added a commit to option-K/mempalace that referenced this pull request Apr 11, 2026
First-party OpenCode plugin for MemPalace lifetime memory system.
Integrates into OpenCode's lifecycle via three hooks:
- experimental.session.compacting: Pre-compaction memory rescue
- experimental.chat.system.transform: L0/L1 wake-up injection
- chat.message: Message-count-triggered background mining

Key advantages over existing PR MemPalace#297 (opencode_auto_plugin.js):
- Modular TypeScript architecture (not a single-file script)
- Pre-Compact rescue hook (not just system.transform)
- Structured L0/L1 memory injection (not hardcoded protocol string)
- Message-count threshold trigger for auto-save (not per-chat injection)
- Debounced mining with lock to prevent concurrent saves
- Idle/crash auto-save via session.idle and process signal handlers

Closes MemPalace#342
@bensig
bensig changed the base branch from main to develop April 11, 2026 22:22
@igorls igorls added area/install pip/uv/pipx/plugin install and packaging enhancement New feature or request labels Apr 14, 2026
rosschurchill added a commit to rosschurchill/mempalace that referenced this pull request Apr 18, 2026
…alace#297)

ChromaDB 1.0.8 running at chroma.theshellnet.com (LAN-only via
allow-home@file middleware, TLS via Traefik + Let's Encrypt).

Token stored in Vault at mempalace/chroma#token (resolved at deploy time,
never in compose file or git history).

Key lessons from deploy:
- Must join traefik-network (external) for Traefik to reach container —
  without it, stack gets 502 Bad Gateway
- container_name: explicit (required for Traefik backend URL resolution)
- ChromaDB 1.x uses /api/v2/ not /api/v1/ — healthcheck updated accordingly
- No ${VAR:-} syntax, no build:, minimal compose = no Portainer parser issues

Verified:
  GET /api/v2/heartbeat → 200 {"nanosecond heartbeat": ...}
  GET /api/v2/.../collections → 200 []

Usage:
  MEMPALACE_CHROMA_URL=http://chroma.theshellnet.com (or direct LAN IP)
  MEMPALACE_CHROMA_TOKEN=dd2ce14310fea886854ab4174eeafcc5ad15c958
  python3 -m mempalace status

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

jphein commented May 13, 2026

Copy link
Copy Markdown
Collaborator

Thanks @Milofax — just filed #1484 for the producer-side counterpart (OpenCode session → palace ingestion as an RFC 002 adapter). The two layer naturally: yours queries the palace from inside an OpenCode session, mine puts the sessions there. Happy to help with review/coordination here as it moves.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/install pip/uv/pipx/plugin install and packaging enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants