prism-mcp-server 4.0.0 โ†’ 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,7 +14,8 @@
14
14
 
15
15
  ## Table of Contents
16
16
 
17
- - [What's New (v4.0.0)](#whats-new-in-v400--behavioral-memory-)
17
+ - [What's New (v4.2.0)](#whats-new-in-v420--project-repo-registry-)
18
+ - [Multi-Instance Support](#multi-instance-support)
18
19
  - [How Prism Compares](#how-prism-compares)
19
20
  - [Quick Start](#quick-start-zero-config--local-mode)
20
21
  - [Mind Palace Dashboard](#-the-mind-palace-dashboard)
@@ -41,26 +42,48 @@
41
42
 
42
43
  ---
43
44
 
44
- ## What's New in v4.0.0 โ€” Behavioral Memory ๐Ÿง 
45
+ ## What's New in v4.2.0 โ€” Project Repo Registry ๐Ÿ—‚๏ธ
45
46
 
46
47
  | Feature | Description |
47
48
  |---|---|
48
- | ๐Ÿง  **Active Behavioral Memory** | New `session_save_experience` tool โ€” agents log actions, outcomes, corrections, and confidence scores. Prism tracks importance, applies decay over time, and injects behavioral warnings into context loading so agents learn from past mistakes. |
49
- | ๐ŸŽฏ **Dynamic Role Resolution** | Role parameter is now fully optional across all tools. The server auto-resolves from dashboard settings via `getSetting("default_role")` โ€” set your role once in the Mind Palace Dashboard, and it applies everywhere. No more hardcoding `role: "global"`. |
50
- | ๐Ÿ“ **Token Budget** | New `max_tokens` parameter on `session_load_context` โ€” set a token budget and the response is intelligently truncated to fit. Uses a 1 token โ‰ˆ 4 chars heuristic. |
51
- | ๐Ÿ“‰ **Importance Decay** | Stale behavioral experiences automatically decay over time โ€” older corrections fade in importance to keep context fresh and relevant. |
52
- | ๐Ÿ”ง **Claude Code Hooks** | Refined SessionStart/Stop hook samples that reliably trigger MCP tool calls. Simplified from multi-step workflows to single imperative instructions. |
49
+ | ๐Ÿ—‚๏ธ **Project Repo Paths** | Map each project to its repo directory in the dashboard. `session_save_ledger` validates `files_changed` paths and warns on mismatch โ€” prevents cross-project contamination. |
50
+ | ๐Ÿ”„ **Universal Auto-Load** | Auto-load projects via dynamic tool descriptions โ€” works across all MCP clients (Claude, Cursor, Gemini, Antigravity) without lifecycle hooks. Dashboard is the sole source of truth. |
51
+ | ๐Ÿ  **Dashboard-First Config** | Removed `PRISM_AUTOLOAD_PROJECTS` env var override. The Mind Palace dashboard is now the single source of truth for auto-load project configuration. |
52
+
53
+ <details>
54
+ <summary><strong>What's in v4.1.0 โ€” Auto-Migration & Multi-Instance ๐Ÿ”€</strong></summary>
55
+
56
+ | Feature | Description |
57
+ |---|---|
58
+ | ๐Ÿ”„ **Auto-Migrations (Supabase)** | Zero-config schema upgrades โ€” pending DDL migrations run automatically on server startup via `prism_apply_ddl` RPC. |
59
+ | ๐Ÿ”€ **Multi-Instance Support** | `PRISM_INSTANCE` env var enables instance-aware PID locks โ€” run multiple Prism servers side-by-side without conflicts. |
60
+ | ๐Ÿ›ก๏ธ **Server Lifecycle Management** | Singleton PID lock with graceful shutdown and stale PID recovery. |
61
+
62
+ </details>
63
+
64
+ <details>
65
+ <summary><strong>What's in v4.0.0 โ€” Behavioral Memory ๐Ÿง </strong></summary>
66
+
67
+ | Feature | Description |
68
+ |---|---|
69
+ | ๐Ÿง  **Behavioral Memory** | `session_save_experience` โ€” log actions, outcomes, corrections with confidence scores. Auto-injects warnings into context so agents learn from mistakes. |
70
+ | ๐ŸŽฏ **Dynamic Roles** | Role auto-resolves from dashboard settings. Set once in Mind Palace, applies everywhere. |
71
+ | ๐Ÿ“ **Token Budget** | `max_tokens` on `session_load_context` โ€” intelligently truncates to fit your budget. |
72
+ | ๐Ÿ“‰ **Importance Decay** | Stale corrections auto-fade over time to keep context fresh. |
73
+ | ๐Ÿ”ง **Claude Code Hooks** | Simplified SessionStart/Stop hooks that reliably trigger MCP tool calls. |
74
+
75
+ </details>
53
76
 
54
77
  <details>
55
78
  <summary><strong>What's in v3.1.0 โ€” Memory Lifecycle ๐Ÿ”„</strong></summary>
56
79
 
57
80
  | Feature | Description |
58
81
  |---|---|
59
- | ๐Ÿ“Š **Memory Analytics** | New **Memory Analytics** card in the dashboard โ€” 14-day sparkline chart, active sessions count, rollup savings, and average context richness. Powered by `getAnalytics()` on both SQLite and Supabase backends. |
60
- | โณ **Automated Data Retention (TTL)** | Set a per-project data retention policy via `knowledge_set_retention` MCP tool or the dashboard **Lifecycle Controls** card. Entries older than the TTL are soft-deleted (GDPR-compliant `archived_at` tombstone) every 12 hours automatically. Rollups are never expired. Minimum 7 days to prevent accidental mass-delete. |
61
- | ๐Ÿ—œ๏ธ **Smart Auto-Compaction** | After every `session_save_ledger`, Prism runs a background health check and triggers compaction automatically if the brain is degraded or unhealthy โ€” gated by `compaction_auto` setting and debounced per-project to prevent concurrent Gemini calls. **Compact Now** button also available in the dashboard. |
62
- | ๐Ÿ“ฆ **PKM Export (Obsidian / Logseq)** | Export any project's full memory as a ZIP archive of Markdown files โ€” one file per session with YAML-like frontmatter, TODOs, decisions, files-changed, and `#hashtag` keywords. Includes an `_index.md` with `[[wikilink]]` references. Click **Export ZIP** in the dashboard Lifecycle Controls card. |
63
- | ๐Ÿงช **Expanded Test Suite** | 37 new Vitest tests (95 total) โ€” covers analytics queries, TTL soft-delete idempotency, rollup preservation, `activeCompactions` Set memory-leak prevention, type guards, export Markdown structure, and TTL sweep scheduler contracts. |
82
+ | ๐Ÿ“Š **Memory Analytics** | Dashboard sparkline chart, session counts, rollup savings, context richness metrics. |
83
+ | โณ **Data Retention (TTL)** | Per-project TTL via `knowledge_set_retention` or dashboard. Auto-expires old entries every 12h. |
84
+ | ๐Ÿ—œ๏ธ **Auto-Compaction** | Background health check after saves โ€” auto-compacts when brain is degraded. |
85
+ | ๐Ÿ“ฆ **PKM Export** | Export project memory as ZIP of Markdown files for Obsidian/Logseq. |
86
+ | ๐Ÿงช **95 Tests** | Analytics, TTL, rollup, compaction, type guards, and export coverage. |
64
87
 
65
88
  </details>
66
89
 
@@ -69,10 +92,10 @@
69
92
 
70
93
  | Feature | Description |
71
94
  |---|---|
72
- | ๐Ÿงน **Brain Health Clean-up** | New **Fix Issues** button in the Mind Palace Dashboard's Brain Health card โ€” detects orphaned handoffs, missing embeddings, and stale rollups, then cleans them up in one click without needing the MCP tool. |
73
- | ๐Ÿ‘ค **Agent Identity Settings** | Dashboard Settings โ†’ Agent Identity panel lets you set a **Default Role** (`dev`, `qa`, `pm`โ€ฆ) and **Agent Name** (e.g. `Dmitri`). Both values auto-apply as fallbacks in all memory and Hivemind tools โ€” no need to pass them per call. |
74
- | ๐Ÿ“œ **Role-Scoped Skills** | Each agent role can have its own persistent skill/rules document stored in the dashboard (โš™๏ธ Settings โ†’ Skills). It is automatically injected into every `session_load_context` response so the agent boots with its rules pre-loaded. |
75
- | ๐Ÿ”ค **Resource Formatting Fix** | `memory://{project}/handoff` resources now render as formatted plain text (Last Summary, TODOs, Keywords) instead of a raw JSON blob โ€” readable in Claude Desktop's paperclip attach panel. |
95
+ | ๐Ÿงน **Brain Health Clean-up** | One-click **Fix Issues** button โ€” detects and cleans orphaned handoffs, missing embeddings, stale rollups. |
96
+ | ๐Ÿ‘ค **Agent Identity** | Set Default Role and Agent Name in dashboard โ€” auto-applies as fallback in all tools. |
97
+ | ๐Ÿ“œ **Role-Scoped Skills** | Per-role persistent rules documents, auto-injected at `session_load_context`. |
98
+ | ๐Ÿ”ค **Resource Formatting** | `memory://` resources render as formatted text instead of raw JSON. |
76
99
 
77
100
  </details>
78
101
 
@@ -81,13 +104,13 @@
81
104
 
82
105
  | Feature | Description |
83
106
  |---|---|
84
- | ๐Ÿ **Role-Scoped Memory** | Optional `role` parameter on ledger, handoff, and context loading โ€” each agent role (dev, qa, pm, lead, security, ux) gets its own isolated memory lane within a project. Defaults to `'global'` for full backward compatibility. |
85
- | ๐Ÿ‘ฅ **Agent Registry** | New `agent_register`, `agent_heartbeat`, `agent_list_team` tools โ€” agents announce their presence, pulse their status, and discover who else is working on the team. Stale agents are auto-pruned after 30 minutes. |
86
- | ๐ŸŽฏ **Team Roster Injection** | When loading context with a role, Prism automatically injects a "Team Roster" showing active teammates, their roles, current tasks, and last heartbeat โ€” true multi-agent awareness without extra tool calls. |
87
- | โš™๏ธ **Dashboard Settings** | New Settings modal with runtime toggles (auto-capture, theme, context depth) backed by a persistent `system_settings` key-value store. Environment variables override DB settings for safety. |
88
- | ๐Ÿ“ก **Hivemind Radar** | New dashboard widget showing active agents, their roles (with icons), current tasks, and heartbeat timestamps โ€” a real-time team coordination dashboard. |
89
- | ๐Ÿ”’ **Conditional Tool Registration** | `PRISM_ENABLE_HIVEMIND` env var gates Hivemind tools โ€” users who don't need multi-agent features keep the same lean tool count as v2.x. |
90
- | โœ… **Test Suite** | 58 tests across 4 suites (storage, tools, dashboard, load) with Vitest โ€” includes concurrent write stress tests, role isolation verification, and 0.2ms/write performance benchmarks. |
107
+ | ๐Ÿ **Role-Scoped Memory** | Optional `role` param โ€” each role gets isolated memory within a project. |
108
+ | ๐Ÿ‘ฅ **Agent Registry** | `agent_register`, `agent_heartbeat`, `agent_list_team` โ€” multi-agent discovery. |
109
+ | ๐ŸŽฏ **Team Roster** | Auto-injected teammate awareness during context loading. |
110
+ | โš™๏ธ **Dashboard Settings** | Runtime toggles backed by persistent key-value store. |
111
+ | ๐Ÿ“ก **Hivemind Radar** | Dashboard widget showing active agents, roles, and heartbeats. |
112
+ | ๐Ÿ”’ **Conditional Tools** | `PRISM_ENABLE_HIVEMIND` gates multi-agent tools. |
113
+ | โœ… **58 Tests** | Storage, tools, dashboard, concurrent writes, role isolation. |
91
114
 
92
115
  </details>
93
116
 
@@ -97,10 +120,10 @@
97
120
 
98
121
  | Feature | Description |
99
122
  |---|---|
100
- | ๐Ÿ” **Memory Tracing (Phase 1)** | Every search now returns a structured `MemoryTrace` with latency breakdown (`embedding_ms`, `storage_ms`, `total_ms`), search strategy, and scoring metadata โ€” surfaced as a separate `content[1]` block for LangSmith integration. |
101
- | ๐Ÿ›ก๏ธ **GDPR Memory Deletion (Phase 2)** | New `session_forget_memory` tool with soft-delete (tombstoning via `deleted_at`) and hard-delete. Ownership guards prevent cross-user deletion. `deleted_reason` column captures GDPR Article 17 justification. Top-K Hole solved by filtering inside SQL, not post-query (ensures we always return exactly K results, rather than returning fewer because deleted items were filtered out after the vector search). |
102
- | ๐Ÿ”— **LangChain Integration (Phase 3)** | `PrismMemoryRetriever` and `PrismKnowledgeRetriever` โ€” async-first `BaseRetriever` subclasses that wrap Prism MCP's traced search endpoints. Trace metadata flows automatically into `Document.metadata["trace"]` for LangSmith visibility. |
103
- | ๐Ÿงฉ **LangGraph Research Agent** | Full example in `examples/langgraph-agent/` โ€” a 5-node agentic research loop with MCP bridge, persistent memory, and `EnsembleRetriever` hybrid search. |
123
+ | ๐Ÿ” **Memory Tracing** | `MemoryTrace` with latency breakdown and scoring metadata for LangSmith. |
124
+ | ๐Ÿ›ก๏ธ **GDPR Deletion** | `session_forget_memory` with soft/hard delete and Article 17 justification. |
125
+ | ๐Ÿ”— **LangChain Integration** | `PrismMemoryRetriever` / `PrismKnowledgeRetriever` BaseRetriever adapters. |
126
+ | ๐Ÿงฉ **LangGraph Agent** | 5-node research agent example with MCP bridge and hybrid search. |
104
127
 
105
128
  </details>
106
129
 
@@ -109,8 +132,8 @@
109
132
 
110
133
  | Feature | Description |
111
134
  |---|---|
112
- | ๐Ÿ”„ **Dynamic Versioning** | Server version is now derived from `package.json` at startup โ€” MCP handshake, dashboard badge, and npm metadata always stay in sync. Falls back to `0.0.0` if unreadable. |
113
- | ๐Ÿ›ก๏ธ **Embedding Dimension Validation** | `generateEmbedding()` now validates the returned vector is exactly 768 dimensions at runtime, catching model regressions before storing bad vectors. Removed `as any` cast in favor of proper `EmbedContentRequest` typing. |
135
+ | ๐Ÿ”„ **Dynamic Versioning** | Version derived from `package.json` โ€” MCP handshake, dashboard, and npm stay in sync. |
136
+ | ๐Ÿ›ก๏ธ **Embedding Validation** | Validates 768-dimension vectors at runtime to catch model regressions. |
114
137
 
115
138
  </details>
116
139
 
@@ -130,11 +153,11 @@
130
153
 
131
154
  | Feature | Description |
132
155
  |---|---|
133
- | ๐Ÿค– **LangGraph Research Agent** | New `examples/langgraph-agent/` โ€” a 5-node agentic research agent (planโ†’searchโ†’analyzeโ†’decideโ†’answerโ†’save) with autonomous looping, MCP integration, and persistent memory. |
134
- | ๐Ÿง  **Agentic Memory** | `save_session` node persists research findings to a ledger โ€” the agent doesn't just answer and forget. Routes to Prism's `session_save_ledger` in MCP-connected mode. |
135
- | ๐Ÿ”Œ **MCP Client Bridge** | Raw JSON-RPC 2.0 client (`mcp_client.py`) for Python 3.9+ โ€” dynamically discovers and wraps Prism MCP tools as LangChain `StructuredTool` objects. |
136
- | ๐Ÿ”ง **Storage Abstraction Fix** | Resource/Prompt handlers now route through `getStorage()` instead of calling Supabase directly โ€” eliminates EOF crashes when reading `memory://` resources. |
137
- | ๐Ÿ›ก๏ธ **Error Boundaries** | Resource handlers catch errors gracefully and return proper MCP error responses (`isError: true`) instead of crashing the server process. |
156
+ | ๐Ÿค– **LangGraph Agent** | 5-node research agent with autonomous looping, MCP integration, persistent memory. |
157
+ | ๐Ÿง  **Agentic Memory** | `save_session` node persists findings to ledger โ€” agents don't just answer and forget. |
158
+ | ๐Ÿ”Œ **MCP Client Bridge** | JSON-RPC 2.0 client wraps Prism tools as LangChain `StructuredTool` objects. |
159
+ | ๐Ÿ”ง **Storage Fix** | Resource/Prompt handlers route through `getStorage()` โ€” eliminates EOF crashes. |
160
+ | ๐Ÿ›ก๏ธ **Error Boundaries** | Graceful error handling with proper MCP error responses. |
138
161
 
139
162
  </details>
140
163
 
@@ -172,25 +195,29 @@
172
195
 
173
196
  | Feature | **Prism MCP** | [MCP Memory](https://github.com/modelcontextprotocol/servers/tree/main/src/memory) | [Mem0](https://github.com/mem0ai/mem0) | [Mnemory](https://github.com/fpytloun/mnemory) | [Basic Memory](https://github.com/basicmachines-co/basic-memory) |
174
197
  |---|---|---|---|---|---|
175
- | **Pricing** | โœ… Free & open source (MIT) | โœ… Free & open source (MIT) | Freemium (free 10K memories; paid Pro) | โœ… Free & open source | Freemium (OSS core free; paid Pro) |
176
- | **Storage** | SQLite (local) + Supabase (cloud) | JSON file | Postgres + Qdrant (hosted or self-hosted) | Qdrant + S3/MinIO | Markdown files |
177
- | **Zero Config** | โœ… `npx -y prism-mcp-server` | โœ… | โŒ Requires Qdrant/Postgres | โœ… `uvx mnemory` | โœ… `pip install basic-memory` |
178
- | **Semantic Search** | โœ… F32_BLOB vectors + FTS5 | โŒ | โœ… pgvector | โœ… Qdrant vectors | โŒ Text search only |
179
- | **Knowledge Graph** | โœ… Neural Graph (Vis.js dashboard) | โœ… Entity/Relation model | โŒ | โœ… Relationship graph | โœ… Markdown links |
180
- | **Time Travel** | โœ… `memory_history` / `memory_checkout` | โŒ | โŒ | โŒ | โŒ |
181
- | **Fact Merging** | โœ… Async Gemini (fire-and-forget) | โŒ | โœ… Built-in | โœ… Contradiction resolution | โŒ |
182
- | **Security Scan** | โœ… Prompt injection detection | โŒ | โŒ | โœ… Anti-injection in fsck | โŒ |
183
- | **Health Check** | โœ… `session_health_check` (fsck) | โŒ | โŒ | โœ… 3-phase fsck | โŒ |
184
- | **Visual Dashboard** | โœ… Mind Palace (localhost:3000) | โŒ | โœ… Cloud dashboard | โœ… Management UI | โŒ |
185
- | **Multi-Agent Sync** | โœ… Real-time cross-client | โŒ | โŒ | โŒ Per-user isolation | โŒ |
186
- | **Visual Memory** | โœ… Screenshot vault + auto-capture | โŒ | โŒ | โœ… Artifact store | โŒ |
198
+ | **Pricing** | โœ… Free / MIT | โœ… Free / MIT | Freemium | โœ… Free / OSS | Freemium |
199
+ | **Storage** | SQLite + Supabase | JSON file | Postgres + Qdrant | Qdrant + S3 | Markdown files |
200
+ | **Zero Config** | โœ… npx one-liner | โœ… | โŒ Qdrant/Postgres | โœ… uvx | โœ… pip |
201
+ | **Behavioral Memory** | โœ… Importance tracking | โŒ | โŒ | โŒ | โŒ |
202
+ | **Dynamic Roles** | โœ… Dashboard auto-resolve | โŒ | โŒ | โŒ | โŒ |
203
+ | **Token Budget** | โœ… max_tokens param | โŒ | โŒ | โŒ | โŒ |
204
+ | **Importance Decay** | โœ… Auto-fade stale data | โŒ | โŒ | โŒ | โŒ |
205
+ | **Semantic Search** | โœ… Vectors + FTS5 | โŒ | โœ… pgvector | โœ… Qdrant | โŒ Text only |
206
+ | **Knowledge Graph** | โœ… Neural Graph | โœ… Entity model | โŒ | โœ… Graph | โœ… MD links |
207
+ | **Time Travel** | โœ… History + checkout | โŒ | โŒ | โŒ | โŒ |
208
+ | **Fact Merging** | โœ… Gemini async | โŒ | โœ… Built-in | โœ… Contradiction | โŒ |
209
+ | **Security Scan** | โœ… Injection detection | โŒ | โŒ | โœ… Anti-injection | โŒ |
210
+ | **Health Check** | โœ… fsck tool | โŒ | โŒ | โœ… 3-phase fsck | โŒ |
211
+ | **Visual Dashboard** | โœ… Mind Palace | โŒ | โœ… Cloud UI | โœ… Mgmt UI | โŒ |
212
+ | **Multi-Agent Sync** | โœ… Real-time | โŒ | โŒ | โŒ Per-user | โŒ |
213
+ | **Visual Memory** | โœ… Screenshot vault | โŒ | โŒ | โœ… Artifacts | โŒ |
187
214
  | **Auto-Compaction** | โœ… Gemini rollups | โŒ | โŒ | โŒ | โŒ |
188
215
  | **Morning Briefing** | โœ… Gemini synthesis | โŒ | โŒ | โŒ | โŒ |
189
216
  | **OCC (Concurrency)** | โœ… Version-based | โŒ | โŒ | โŒ | โŒ |
190
- | **GDPR Compliance** | โœ… Soft/hard delete + audit trail | โŒ | โŒ | โŒ | โŒ |
191
- | **Memory Tracing** | โœ… MemoryTrace with latency breakdown | โŒ | โŒ | โŒ | โŒ |
192
- | **LangChain Native** | โœ… BaseRetriever adapters | โŒ | โŒ | โŒ | โŒ |
193
- | **MCP Native** | โœ… stdio (Claude Desktop, Cursor) | โœ… stdio | โŒ Python SDK / REST | โœ… HTTP + MCP | โœ… stdio |
217
+ | **GDPR Compliance** | โœ… Soft/hard delete | โŒ | โŒ | โŒ | โŒ |
218
+ | **Memory Tracing** | โœ… Latency breakdown | โŒ | โŒ | โŒ | โŒ |
219
+ | **LangChain** | โœ… BaseRetriever | โŒ | โŒ | โŒ | โŒ |
220
+ | **MCP Native** | โœ… stdio | โœ… stdio | โŒ Python SDK | โœ… HTTP + MCP | โœ… stdio |
194
221
  | **Language** | TypeScript | TypeScript | Python | Python | Python |
195
222
 
196
223
  > **When to choose Prism MCP:** You want MCP-native memory with zero infrastructure overhead, progressive context loading, and enterprise features (OCC, compaction, time travel, security scanning) that work directly in Claude Desktop โ€” without running Qdrant, Postgres, or cloud services.
@@ -280,6 +307,7 @@ Open **`http://localhost:3000`** in your browser to see exactly what your AI age
280
307
 
281
308
  - **Current State & TODOs** โ€” See the exact context injected into the LLM's prompt
282
309
  - **Agent Identity Chip** โ€” Header shows your active role + name (e.g. `๐Ÿ› ๏ธ dev ยท Antigravity`); click to open Settings
310
+ - **Project Repo Paths** โ€” Map each project to its repo directory for save validation
283
311
  - **Brain Health ๐Ÿฉบ** โ€” Memory integrity status at a glance; **๐Ÿงน Fix Issues** button auto-cleans orphaned handoffs in one click
284
312
  - **Git Drift Detection** โ€” Alerts you if you've modified code outside the agent's view
285
313
  - **Morning Briefing** โ€” AI-synthesized action plan from your last sessions
@@ -621,6 +649,47 @@ The tool and dashboard button both call the same repair logic โ€” the dashboard
621
649
  | `session_search_memory` | Semantic search with `enable_trace` | `query`, `enable_trace` | Results + `MemoryTrace` in `content[1]` |
622
650
  | `knowledge_search` | Knowledge search with `enable_trace` | `query`, `enable_trace` | Results + `MemoryTrace` in `content[1]` |
623
651
 
652
+ ### v4.0 Behavioral Memory Tools
653
+
654
+ | Tool | Purpose | Key Args |
655
+ |------|---------|----------|
656
+ | `session_save_experience` | Record behavioral events with importance tracking | `project`, `event_type`, `context`, `action`, `outcome` |
657
+
658
+ **Dynamic Roles (v4.0):** `role` is now *optional* on all tools. Set your **Default Role** once in the dashboard (โš™๏ธ Settings โ†’ Agent Identity) and it auto-applies everywhere โ€” no need to pass it per call.
659
+
660
+ **Token Budget (v4.0):** Set a default in the dashboard (โš™๏ธ Settings โ†’ Token Budget) or pass `max_tokens` per call to override:
661
+
662
+ ```json
663
+ { "name": "session_load_context", "arguments": {
664
+ "project": "my-app", "level": "standard", "max_tokens": 2000
665
+ }}
666
+ ```
667
+
668
+ > ๐Ÿ’ก Set Token Budget to `0` in the dashboard for unlimited (default). Per-call `max_tokens` always takes priority.
669
+
670
+ **Recording experiences:**
671
+
672
+ ```json
673
+ { "name": "session_save_experience", "arguments": {
674
+ "project": "my-app",
675
+ "event_type": "correction",
676
+ "context": "User asked to add a database migration",
677
+ "action": "Ran ALTER TABLE directly in production",
678
+ "outcome": "Data was corrupted",
679
+ "correction": "Always create a migration file and test in staging first",
680
+ "confidence_score": 95
681
+ }}
682
+ ```
683
+
684
+ **Event types:** `correction` (user corrected the agent), `success` (task went well), `failure` (task failed), `learning` (new knowledge acquired).
685
+
686
+ **How behavioral memory works:**
687
+ 1. Agent records experiences via `session_save_experience`
688
+ 2. Prism assigns an **importance score** based on event type + confidence
689
+ 3. Stale entries **auto-decay** in importance over time
690
+ 4. On `session_load_context`, high-importance corrections auto-surface as `[โš ๏ธ BEHAVIORAL WARNINGS]`
691
+ 5. Agent sees warnings and avoids repeating past mistakes
692
+
624
693
  ### Code Mode Templates (v2.1)
625
694
 
626
695
  Instead of writing custom JavaScript, pass a `template` name for instant extraction:
@@ -798,6 +867,7 @@ The retrievers use `_aget_relevant_documents` as the primary path with `asyncio.
798
867
  | `BRAVE_API_KEY` | No | Brave Search Pro API key (enables web/local search tools) |
799
868
  | `PRISM_STORAGE` | No | `"local"` (default) or `"supabase"` โ€” **requires restart** |
800
869
  | `PRISM_ENABLE_HIVEMIND` | No | Set `"true"` to enable multi-agent Hivemind tools โ€” **requires restart** |
870
+ | `PRISM_INSTANCE` | No | Instance name for PID lock isolation (e.g. `"athena"`, `"prism-dev"`). Enables [multi-instance support](#multi-instance-support). Default: `"default"` |
801
871
  | `GOOGLE_API_KEY` | No | Google AI / Gemini โ€” enables paper analysis, Morning Briefings, compaction |
802
872
  | `BRAVE_ANSWERS_API_KEY` | No | Separate Brave Answers key for AI-grounded answers |
803
873
  | `SUPABASE_URL` | If cloud mode | Supabase project URL |
@@ -864,12 +934,27 @@ The agent boots up knowing exactly what to do โ€” zero prompting needed.
864
934
 
865
935
  ### Auto-Load on Session Start (Recommended)
866
936
 
867
- For the best experience, configure your AI coding assistant to **automatically call `session_load_context`** at the start of every new session. This ensures your agent always boots with full project memory โ€” no manual prompting needed.
937
+ For the best experience, ensure your agent boots with full project memory on every new session. There are three approaches โ€” use whichever fits your workflow:
938
+
939
+ #### Option A: Dashboard Setting (Easiest)
940
+
941
+ Open the **Mind Palace Dashboard** (โš™๏ธ Settings) and type your project names into the **Auto-Load Projects** field under Boot Settings:
942
+
943
+ ```
944
+ prism-mcp, my-app
945
+ ```
946
+
947
+ That's it. Restart your AI client and Prism auto-pushes context for each listed project on next boot.
948
+
949
+ #### Option B: Client-Side Hooks / Rules
950
+
951
+ For clients that support lifecycle hooks or startup rules, instruct the AI to **call `session_load_context` as a tool** at session start:
868
952
 
869
- See the full setup guides for each client:
870
953
  - **[Claude Code Integration (Hooks)](#claude-code-integration-hooks)** โ€” `SessionStart` and `Stop` hook JSON samples for `~/.claude/settings.json`
871
954
  - **[Gemini / Antigravity Integration](#gemini--antigravity-integration)** โ€” global rules for `~/.gemini/GEMINI.md` or user rules
872
955
 
956
+ > **You can use both approaches together.** The dashboard auto-load (A) injects project names into the `session_load_context` tool description โ€” works universally across all MCP clients. Client-side hooks (B) give the AI full structured access to the context response (including version numbers for OCC).
957
+
873
958
  > **Key principle:** Never hardcode a `role` in your hooks or rules. Set your role once in the **Mind Palace Dashboard โš™๏ธ Settings โ†’ Agent Identity**, and Prism automatically resolves it for every tool call across all clients. See [Role Resolution](#role-resolution--no-hardcoding-needed).
874
959
 
875
960
  > **Tip:** Replace `my-project` with your actual project identifiers. You can list as many projects as you need โ€” each one gets its own independent memory timeline.
@@ -1083,9 +1168,12 @@ Traces are returned as `content[1]` in MCP responses โ€” a separate content bloc
1083
1168
 
1084
1169
  ### 2. Apply Migrations
1085
1170
 
1086
- In the SQL Editor, run:
1171
+ In the SQL Editor, run the **bootstrap migration** first:
1087
1172
  1. [`supabase/migrations/015_session_memory.sql`](supabase/migrations/015_session_memory.sql)
1088
1173
  2. [`supabase/migrations/016_knowledge_accumulation.sql`](supabase/migrations/016_knowledge_accumulation.sql)
1174
+ 3. [`supabase/migrations/027_auto_migration_infra.sql`](supabase/migrations/027_auto_migration_infra.sql) โ€” **enables auto-migrations** (see below)
1175
+
1176
+ > **After applying migration 027**, all future schema changes are applied automatically on server startup โ€” no manual SQL required.
1089
1177
 
1090
1178
  ### 3. Get Credentials
1091
1179
 
@@ -1113,6 +1201,70 @@ export PRISM_STORAGE="supabase"
1113
1201
 
1114
1202
  </details>
1115
1203
 
1204
+ ### Auto-Migrations (Supabase Cloud)
1205
+
1206
+ Prism includes a zero-config auto-migration system for Supabase. Once the bootstrap migration (`027_auto_migration_infra.sql`) is applied, all future schema changes are applied automatically on server startup.
1207
+
1208
+ **How it works:**
1209
+
1210
+ 1. On startup, the migration runner checks each entry in `MIGRATIONS[]` (defined in `supabaseMigrations.ts`)
1211
+ 2. For each migration, it calls `prism_apply_ddl(version, name, sql)` โ€” a `SECURITY DEFINER` RPC function
1212
+ 3. The function checks `prism_schema_versions` โ€” if the version is already recorded, it's silently skipped (idempotent)
1213
+ 4. If not applied, it executes the DDL and records the version number
1214
+
1215
+ **Graceful degradation:** If `prism_apply_ddl()` doesn't exist (you haven't applied migration 027 yet), the runner logs a warning and continues โ€” the server still starts, but newer schema features may not be available.
1216
+
1217
+ **Adding new migrations** โ€” just append to the `MIGRATIONS[]` array in `src/storage/supabaseMigrations.ts`:
1218
+
1219
+ ```typescript
1220
+ {
1221
+ version: 28,
1222
+ name: "my_new_feature",
1223
+ sql: `ALTER TABLE session_ledger ADD COLUMN IF NOT EXISTS my_col TEXT;`,
1224
+ }
1225
+ ```
1226
+
1227
+ All SQL must be idempotent (`IF NOT EXISTS` / `IF EXISTS` guards).
1228
+
1229
+ ---
1230
+
1231
+ ## Multi-Instance Support
1232
+
1233
+ Run multiple Prism MCP servers side-by-side on the same machine without PID lock conflicts. This is useful when you have different MCP configurations (e.g., one for web search + memory, another for memory-only) running in different clients simultaneously.
1234
+
1235
+ ### Configuration
1236
+
1237
+ Set `PRISM_INSTANCE` to a unique name per server instance:
1238
+
1239
+ ```json
1240
+ {
1241
+ "mcpServers": {
1242
+ "prism-search": {
1243
+ "command": "node",
1244
+ "args": ["/path/to/prism/dist/server.js"],
1245
+ "env": {
1246
+ "PRISM_INSTANCE": "prism-search",
1247
+ "BRAVE_API_KEY": "your-key"
1248
+ }
1249
+ },
1250
+ "prism-memory": {
1251
+ "command": "node",
1252
+ "args": ["/path/to/prism/dist/server.js"],
1253
+ "env": {
1254
+ "PRISM_INSTANCE": "prism-memory"
1255
+ }
1256
+ }
1257
+ }
1258
+ }
1259
+ ```
1260
+
1261
+ ### How it works
1262
+
1263
+ - Each instance gets its own PID file: `/tmp/prism-{PRISM_INSTANCE}.pid`
1264
+ - Default instance name is `"default"` (backward compatible)
1265
+ - Instances share the same SQLite database and Supabase backend โ€” only the process lock is isolated
1266
+ - Graceful shutdown cleans up the instance's PID file
1267
+
1116
1268
  ---
1117
1269
 
1118
1270
  ## Hybrid Search Pipeline (Brave + Vertex AI)
@@ -1139,12 +1291,13 @@ See [`vertex-ai/`](vertex-ai/) for setup and benchmarks.
1139
1291
 
1140
1292
  ```
1141
1293
  โ”œโ”€โ”€ src/
1142
- โ”‚ โ”œโ”€โ”€ server.ts # MCP server core + tool routing
1294
+ โ”‚ โ”œโ”€โ”€ server.ts # MCP server core + tool routing + lifecycle
1143
1295
  โ”‚ โ”œโ”€โ”€ config.ts # Environment management
1144
1296
  โ”‚ โ”œโ”€โ”€ storage/
1145
1297
  โ”‚ โ”‚ โ”œโ”€โ”€ interface.ts # StorageBackend abstraction (+ GDPR delete methods)
1146
1298
  โ”‚ โ”‚ โ”œโ”€โ”€ sqlite.ts # SQLite local storage (libSQL + F32_BLOB + deleted_at migration)
1147
1299
  โ”‚ โ”‚ โ”œโ”€โ”€ supabase.ts # Supabase cloud storage (+ soft/hard delete)
1300
+ โ”‚ โ”‚ โ”œโ”€โ”€ supabaseMigrations.ts # Auto-migration runner for Supabase DDL
1148
1301
  โ”‚ โ”‚ โ”œโ”€โ”€ configStorage.ts # Boot config micro-DB (~/.prism-mcp/prism-config.db)
1149
1302
  โ”‚ โ”‚ โ””โ”€โ”€ index.ts # Backend factory (auto-selects based on PRISM_STORAGE)
1150
1303
  โ”‚ โ”œโ”€โ”€ sync/
@@ -1182,7 +1335,8 @@ See [`vertex-ai/`](vertex-ai/) for setup and benchmarks.
1182
1335
  โ”‚ โ”œโ”€โ”€ prism_retriever.py # PrismMemoryRetriever + PrismKnowledgeRetriever
1183
1336
  โ”‚ โ”œโ”€โ”€ tools.py # Agent tools + GDPR forget_memory
1184
1337
  โ”‚ โ””โ”€โ”€ demo_retriever.py # Standalone retriever demo
1185
- โ”œโ”€โ”€ supabase/migrations/ # Cloud mode SQL schemas
1338
+ โ”œโ”€โ”€ supabase/migrations/ # Cloud mode SQL schemas + auto-migration bootstrap
1339
+ โ”‚ โ””โ”€โ”€ 027_auto_migration_infra.sql # prism_apply_ddl() RPC + schema version tracking
1186
1340
  โ”œโ”€โ”€ vertex-ai/ # Vertex AI hybrid search pipeline
1187
1341
  โ”œโ”€โ”€ index.ts # Server entry point
1188
1342
  โ””โ”€โ”€ package.json
@@ -1194,40 +1348,41 @@ See [`vertex-ai/`](vertex-ai/) for setup and benchmarks.
1194
1348
 
1195
1349
  > **[View the full project board โ†’](https://github.com/users/dcostenco/projects/1/views/1)**
1196
1350
 
1197
- ### โœ… v3.0.1 โ€” Agent Identity & Brain Clean-up (Shipped!)
1351
+ ### โœ… v4.2 โ€” Project Repo Registry (Shipped!)
1198
1352
 
1199
- See [What's New in v3.0.1](#whats-new-in-v301---agent-identity--brain-clean-up-) above.
1353
+ | Feature | Description |
1354
+ |---|---|
1355
+ | ๐Ÿ—‚๏ธ **Project Repo Paths** | Dashboard UI to map projects to repo directories + `session_save_ledger` path validation. |
1356
+ | ๐Ÿ”„ **Universal Auto-Load** | Dynamic tool descriptions replace env var โ€” dashboard is sole source of truth. |
1357
+ | ๐Ÿ  **Dashboard-First Config** | Removed `PRISM_AUTOLOAD_PROJECTS` env var override. |
1200
1358
 
1201
- ### โœ… v3.0 โ€” Agent Hivemind (Shipped!)
1359
+ ### โœ… v4.1 โ€” Auto-Migration & Multi-Instance (Shipped!)
1202
1360
 
1203
- See [What's New in v3.0.0 โ€” Agent Hivemind](#whats-new-in-v300---agent-hivemind-) above.
1361
+ See [What's in v4.1.0](#whats-in-v410--auto-migration--multi-instance-) above.
1204
1362
 
1205
- ### ๐Ÿ”œ v4.0 โ€” Active Behavioral Memory (In Development)
1363
+ ### โœ… v4.0 โ€” Behavioral Memory (Shipped!)
1206
1364
 
1207
- Evolves Prism from passive session logging to an **experience learning engine** that shapes agent behavior over time.
1365
+ See [What's in v4.0.0](#whats-in-v400--behavioral-memory-) above.
1208
1366
 
1209
- | Feature | Description |
1210
- |---------|-------------|
1211
- | **Structured Event Types** | Typed experience events (`correction`, `success`, `failure`, `learning`) with `confidence_score` (1-100) โ€” agents don't just log, they learn |
1212
- | **Token-Budgeted Context Loading** | `max_tokens` param on `session_load_context` โ€” guarantees constant cost regardless of DB size (1 token โ‰ˆ 4 chars heuristic) |
1213
- | **`session_save_experience` Tool** | Dedicated tool for behavioral data: context โ†’ action โ†’ outcome โ†’ correction. Auto-extracts keywords, seeds importance |
1214
- | **Insight Graduation System** | `knowledge_upvote` / `knowledge_downvote` tools. Importance โ‰ฅ 7 โ†’ graduated rule. 30-day decay prevents bloat |
1215
- | **Behavioral Warnings** | High-importance corrections auto-surface as `[โš ๏ธ BEHAVIORAL WARNINGS]` in `session_load_context` โ€” agents proactively avoid past mistakes |
1367
+ ### โœ… v3.1 โ€” Memory Lifecycle (Shipped!)
1368
+
1369
+ See [What's in v3.1.0](#whats-in-v310--memory-lifecycle-) above.
1370
+
1371
+ ### โœ… v3.0 โ€” Agent Hivemind (Shipped!)
1372
+
1373
+ See [What's in v3.0.0](#whats-in-v300--agent-hivemind-) above.
1216
1374
 
1217
1375
  ### ๐Ÿš€ Future Ideas
1218
1376
 
1219
1377
  | Feature | Issue | Description |
1220
1378
  |---------|-------|-------------|
1221
- | **Role-Scoped Skills & Rules** | โ€” | Each agent role (`dev`, `qa`, `pm`, etc.) gets its own persistent skill/rules document. Preloaded automatically at session start via `session_load_context`. Skills editable and uploadable from the Mind Palace Dashboard (โš™๏ธ โ†’ Skills tab per role). Stored in `configStorage` per-role key โ€” backend already exists. |
1222
- | OpenTelemetry SDK Integration | [#6](https://github.com/dcostenco/prism-mcp/issues/6) | W3C-compliant tracing with Jaeger/Zipkin export |
1223
- | GDPR Right to Portability | [#7](https://github.com/dcostenco/prism-mcp/issues/7) | `session_export_memory` tool for Art. 20 compliance |
1224
- | Multi-agent CRDT Conflict Resolution | [#9](https://github.com/dcostenco/prism-mcp/issues/9) | Conflict-free replicated data types for concurrent agent edits |
1225
- | Memory Analytics Dashboard | [#10](https://github.com/dcostenco/prism-mcp/issues/10) | Usage trends, token costs, and memory health metrics |
1226
- | Pluggable LLM Providers | [#13](https://github.com/dcostenco/prism-mcp/issues/13) | Anthropic, OpenAI, Ollama provider adapters |
1227
- | VLM / OCR for Visual Memory | [#14](https://github.com/dcostenco/prism-mcp/issues/14) | Auto-extract text and insights from stored images |
1228
- | Automated Data Retention (TTL) | [#16](https://github.com/dcostenco/prism-mcp/issues/16) | Time-based memory expiration policies |
1229
- | Obsidian / Logseq Export Bridge | [#17](https://github.com/dcostenco/prism-mcp/issues/17) | Export memory to Markdown knowledge bases |
1230
- | Interactive Knowledge Graph Editor | [#19](https://github.com/dcostenco/prism-mcp/issues/19) | Visual graph editor inside the Mind Palace dashboard |
1379
+ | Insight Graduation | โ€” | `knowledge_upvote` / `knowledge_downvote` โ€” importance โ‰ฅ 7 promotes to rule |
1380
+ | OpenTelemetry | [#6](https://github.com/dcostenco/prism-mcp/issues/6) | W3C tracing with Jaeger/Zipkin export |
1381
+ | GDPR Portability | [#7](https://github.com/dcostenco/prism-mcp/issues/7) | `session_export_memory` for Art. 20 |
1382
+ | CRDT Conflict Resolution | [#9](https://github.com/dcostenco/prism-mcp/issues/9) | Conflict-free types for concurrent edits |
1383
+ | Pluggable LLM Providers | [#13](https://github.com/dcostenco/prism-mcp/issues/13) | Anthropic, OpenAI, Ollama adapters |
1384
+ | VLM / OCR for Images | [#14](https://github.com/dcostenco/prism-mcp/issues/14) | Auto-extract text from stored images |
1385
+ | Knowledge Graph Editor | [#19](https://github.com/dcostenco/prism-mcp/issues/19) | Visual graph editor in Mind Palace |
1231
1386
 
1232
1387
  ### ๐Ÿงฐ Infrastructure & Stack
1233
1388
 
package/dist/config.js CHANGED
@@ -114,6 +114,14 @@ export const PRISM_DEBUG_LOGGING = process.env.PRISM_DEBUG_LOGGING === "true";
114
114
  // doesn't increase tool count.
115
115
  // Set PRISM_ENABLE_HIVEMIND=true to unlock the Agent Registry tools.
116
116
  export const PRISM_ENABLE_HIVEMIND = process.env.PRISM_ENABLE_HIVEMIND === "true";
117
+ // โ”€โ”€โ”€ v4.1: Auto-Load Projects โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
118
+ // Auto-load is configured exclusively via the Mind Palace dashboard
119
+ // ("Auto-Load Projects" checkboxes in Settings). The setting is stored
120
+ // in prism-config.db and read at startup via getSettingSync().
121
+ //
122
+ // The PRISM_AUTOLOAD_PROJECTS env var has been removed โ€” the dashboard
123
+ // is the single source of truth. This prevents mismatches between
124
+ // env var and dashboard values causing duplicate project loads.
117
125
  if (PRISM_AUTO_CAPTURE) {
118
126
  // Use console.error instead of debugLog here to prevent circular dependency
119
127
  if (PRISM_DEBUG_LOGGING) {
@@ -696,6 +696,19 @@ export function renderDashboardHTML(version) {
696
696
  </select>
697
697
  </div>
698
698
 
699
+ <div class="setting-row">
700
+ <div>
701
+ <div class="setting-label">Token Budget</div>
702
+ <div class="setting-desc">Max tokens for session_load_context (0 = unlimited)</div>
703
+ </div>
704
+ <input type="number" id="input-max-tokens"
705
+ placeholder="0"
706
+ min="0" max="100000" step="500"
707
+ style="padding: 0.2rem 0.5rem; background: var(--bg-hover); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 4px; font-size: 0.85rem; font-family: var(--font-mono); width: 90px; text-align: right;"
708
+ onchange="saveSetting('max_tokens', this.value)"
709
+ oninput="clearTimeout(this._t); this._t=setTimeout(()=>saveSetting('max_tokens',this.value),800)" />
710
+ </div>
711
+
699
712
  <div class="setting-section">Boot Settings <span class="boot-badge">Restart Required</span></div>
700
713
 
701
714
  <div class="setting-row">
@@ -716,6 +729,26 @@ export function renderDashboardHTML(version) {
716
729
  </select>
717
730
  </div>
718
731
 
732
+ <div class="setting-row" style="align-items:flex-start">
733
+ <div>
734
+ <div class="setting-label">Auto-Load Projects</div>
735
+ <div class="setting-desc">Select projects to auto-push context on startup</div>
736
+ </div>
737
+ <div id="autoload-checkboxes" style="display:flex;flex-direction:column;gap:4px;font-size:0.85rem;font-family:var(--font-mono);max-height:120px;overflow-y:auto;">
738
+ <span style="color:var(--text-muted);font-size:0.8rem">Loadingโ€ฆ</span>
739
+ </div>
740
+ </div>
741
+
742
+ <div class="setting-row" style="align-items:flex-start">
743
+ <div>
744
+ <div class="setting-label">Project Repo Paths</div>
745
+ <div class="setting-desc">Map each project to its repo directory for save validation</div>
746
+ </div>
747
+ <div id="repopath-inputs" style="display:flex;flex-direction:column;gap:6px;font-size:0.85rem;max-height:160px;overflow-y:auto;">
748
+ <span style="color:var(--text-muted);font-size:0.8rem">Loadingโ€ฆ</span>
749
+ </div>
750
+ </div>
751
+
719
752
  <div class="setting-section">Agent Identity</div>
720
753
 
721
754
  <div class="setting-row">
@@ -1278,6 +1311,88 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
1278
1311
  }
1279
1312
 
1280
1313
 
1314
+ // โ”€โ”€โ”€ Auto-Load Checkboxes (v4.1) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1315
+ async function loadAutoloadCheckboxes() {
1316
+ var container = document.getElementById('autoload-checkboxes');
1317
+ if (!container) return;
1318
+ try {
1319
+ var projRes = await fetch('/api/projects');
1320
+ var projData = await projRes.json();
1321
+ var projects = projData.projects || [];
1322
+
1323
+ var settRes = await fetch('/api/settings');
1324
+ var settData = await settRes.json();
1325
+ var saved = (settData.settings || {}).autoload_projects || '';
1326
+ var selected = saved.split(',').map(function(s){ return s.trim(); }).filter(Boolean);
1327
+
1328
+ if (projects.length === 0) {
1329
+ container.innerHTML = '<span style="color:var(--text-muted);font-size:0.8rem">No projects found</span>';
1330
+ return;
1331
+ }
1332
+
1333
+ container.innerHTML = projects.map(function(p) {
1334
+ var checked = selected.indexOf(p) !== -1 ? ' checked' : '';
1335
+ return '<label style="display:flex;align-items:center;gap:6px;cursor:pointer;color:var(--text-primary)">' +
1336
+ '<input type="checkbox" value="' + escapeHtml(p) + '"' + checked +
1337
+ ' onchange="onAutoloadToggle()"' +
1338
+ ' style="accent-color:var(--accent-purple);cursor:pointer" />' +
1339
+ escapeHtml(p) + '</label>';
1340
+ }).join('');
1341
+ } catch(e) {
1342
+ container.innerHTML = '<span style="color:var(--accent-rose);font-size:0.8rem">Failed to load</span>';
1343
+ }
1344
+ }
1345
+
1346
+ function onAutoloadToggle() {
1347
+ var container = document.getElementById('autoload-checkboxes');
1348
+ if (!container) return;
1349
+ var boxes = container.querySelectorAll('input[type=checkbox]');
1350
+ var selected = [];
1351
+ for (var i = 0; i < boxes.length; i++) {
1352
+ if (boxes[i].checked) selected.push(boxes[i].value);
1353
+ }
1354
+ saveBootSetting('autoload_projects', selected.join(','));
1355
+ }
1356
+
1357
+ // โ”€โ”€โ”€ Project Repo Paths (v4.2) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1358
+ async function loadRepoPathInputs() {
1359
+ var container = document.getElementById('repopath-inputs');
1360
+ if (!container) return;
1361
+ try {
1362
+ var projRes = await fetch('/api/projects');
1363
+ var projData = await projRes.json();
1364
+ var projects = projData.projects || [];
1365
+
1366
+ var settRes = await fetch('/api/settings');
1367
+ var settData = await settRes.json();
1368
+ var settings = settData.settings || {};
1369
+
1370
+ if (projects.length === 0) {
1371
+ container.innerHTML = '<span style="color:var(--text-muted);font-size:0.8rem">No projects found</span>';
1372
+ return;
1373
+ }
1374
+
1375
+ container.innerHTML = projects.map(function(p) {
1376
+ var savedPath = settings['repo_path:' + p] || '';
1377
+ return '<div style="display:flex;align-items:center;gap:6px">' +
1378
+ '<span style="min-width:100px;color:var(--text-secondary);font-family:var(--font-mono);font-size:0.8rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + escapeHtml(p) + '">' + escapeHtml(p) + '</span>' +
1379
+ '<input type="text" value="' + escapeHtml(savedPath) + '"' +
1380
+ ' placeholder="/path/to/repo"' +
1381
+ ' data-project="' + escapeHtml(p) + '"' +
1382
+ ' style="flex:1;min-width:140px;padding:0.2rem 0.4rem;background:var(--bg-primary);color:var(--text-primary);border:1px solid var(--border-glass);border-radius:4px;font-size:0.8rem;font-family:var(--font-mono)"' +
1383
+ ' onchange="saveRepoPath(this.dataset.project, this.value)"' +
1384
+ ' oninput="clearTimeout(this._t); var self=this; this._t=setTimeout(function(){saveRepoPath(self.dataset.project, self.value)},1200)" />' +
1385
+ '</div>';
1386
+ }).join('');
1387
+ } catch(e) {
1388
+ container.innerHTML = '<span style="color:var(--accent-rose);font-size:0.8rem">Failed to load</span>';
1389
+ }
1390
+ }
1391
+
1392
+ async function saveRepoPath(project, path) {
1393
+ await saveSetting('repo_path:' + project, path.trim());
1394
+ }
1395
+
1281
1396
  async function loadSettings() {
1282
1397
  try {
1283
1398
  var res = await fetch('/api/settings');
@@ -1304,6 +1419,11 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
1304
1419
  // Agent Identity
1305
1420
  if (s.default_role) document.getElementById('select-default-role').value = s.default_role;
1306
1421
  if (s.agent_name) document.getElementById('input-agent-name').value = s.agent_name;
1422
+ if (s.max_tokens) document.getElementById('input-max-tokens').value = s.max_tokens;
1423
+ // Autoload checkboxes are loaded dynamically
1424
+ loadAutoloadCheckboxes();
1425
+ // Repo path inputs are loaded dynamically
1426
+ loadRepoPathInputs();
1307
1427
  } catch(e) { console.warn('Settings load failed:', e); }
1308
1428
  }
1309
1429
 
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Server Lifecycle Management
3
+ * Handles singleton PID locking, graceful shutdown, and SQLite handle cleanup.
4
+ *
5
+ * CRITICAL: All logging MUST use console.error() (stderr).
6
+ * Using console.log() (stdout) will corrupt the MCP JSON-RPC stream.
7
+ */
8
+ import * as fs from "fs";
9
+ import * as path from "path";
10
+ import * as os from "os";
11
+ import { execSync } from "child_process";
12
+ import { closeConfigStorage } from "./storage/configStorage.js";
13
+ import { getStorage } from "./storage/index.js";
14
+ const PRISM_DIR = path.join(os.homedir(), ".prism-mcp");
15
+ /**
16
+ * Instance-aware PID file.
17
+ * Set PRISM_INSTANCE env var to run multiple Prism MCP servers
18
+ * side-by-side (e.g. "athena-public" and "prism-mcp").
19
+ * Each instance gets its own PID file to prevent lock conflicts.
20
+ */
21
+ const INSTANCE_NAME = process.env.PRISM_INSTANCE || "default";
22
+ const PID_FILE = path.join(PRISM_DIR, `server-${INSTANCE_NAME}.pid`);
23
+ function log(msg) {
24
+ console.error(`[Prism Lifecycle] ${msg}`);
25
+ }
26
+ /**
27
+ * Checks if a process is an orphan (adopted by init/launchd, PPID=1).
28
+ * Returns false on Windows (PID logic is different there).
29
+ */
30
+ function isOrphanProcess(pid) {
31
+ if (process.platform === "win32") {
32
+ // Windows doesn't have reliable PPID checks via 'ps'.
33
+ // Safer to assume it's NOT an orphan to avoid killing valid instances.
34
+ return false;
35
+ }
36
+ try {
37
+ // 'ps -o ppid= -p PID' returns just the parent PID
38
+ const ppid = execSync(`ps -o ppid= -p ${pid}`, { encoding: "utf8" }).trim();
39
+ return ppid === "1";
40
+ }
41
+ catch {
42
+ // If ps fails (e.g. process gone), assume it's safe to claim
43
+ return true;
44
+ }
45
+ }
46
+ /**
47
+ * Ensures valid server execution state.
48
+ *
49
+ * LOGIC:
50
+ * 1. If --no-lock is passed, skip everything (testing mode).
51
+ * 2. If PID file exists:
52
+ * - If process is dead: Overwrite lock.
53
+ * - If process is alive AND is an orphan (PPID=1): Kill it (Zombie), then overwrite.
54
+ * - If process is alive AND has a parent: Log warning, allow coexistence (don't kill).
55
+ */
56
+ export function acquireLock() {
57
+ if (process.argv.includes("--no-lock")) {
58
+ log("Lock acquisition skipped (--no-lock flag)");
59
+ return;
60
+ }
61
+ if (!fs.existsSync(PRISM_DIR)) {
62
+ fs.mkdirSync(PRISM_DIR, { recursive: true });
63
+ }
64
+ if (fs.existsSync(PID_FILE)) {
65
+ try {
66
+ const oldPid = parseInt(fs.readFileSync(PID_FILE, "utf8").trim(), 10);
67
+ if (oldPid && oldPid !== process.pid) {
68
+ let isAlive = false;
69
+ try {
70
+ process.kill(oldPid, 0); // 0 signal checks for existence
71
+ isAlive = true;
72
+ }
73
+ catch {
74
+ isAlive = false;
75
+ }
76
+ if (isAlive) {
77
+ // Process exists. Is it a zombie?
78
+ if (isOrphanProcess(oldPid)) {
79
+ log(`Found zombie process (PID ${oldPid}, PPID=1). Terminating...`);
80
+ try {
81
+ process.kill(oldPid, "SIGTERM");
82
+ // Give it 100ms to die, then force kill if needed
83
+ setTimeout(() => {
84
+ try {
85
+ process.kill(oldPid, "SIGKILL");
86
+ }
87
+ catch { }
88
+ }, 100);
89
+ }
90
+ catch (e) {
91
+ log(`Failed to kill zombie: ${e}`);
92
+ }
93
+ }
94
+ else {
95
+ // It has a parent (e.g., another VS Code window or Claude Desktop)
96
+ log(`Existing server (PID ${oldPid}) is active and managed. Coexisting...`);
97
+ // We do NOT overwrite the PID file here.
98
+ // If we overwrite it, the *active* server will fail to clean up
99
+ // the PID file when it eventually shuts down.
100
+ return;
101
+ }
102
+ }
103
+ }
104
+ }
105
+ catch (err) {
106
+ log(`Warning: Failed to process existing PID file: ${err}`);
107
+ }
108
+ }
109
+ // Claim the lock for this process
110
+ try {
111
+ fs.writeFileSync(PID_FILE, process.pid.toString(), "utf8");
112
+ log(`Acquired singleton lock (PID ${process.pid})`);
113
+ }
114
+ catch (err) {
115
+ log(`Warning: Failed to write PID file: ${err}`);
116
+ }
117
+ }
118
+ /**
119
+ * Registers handlers to close SQLite file handles cleanly when the server stops.
120
+ */
121
+ export function registerShutdownHandlers() {
122
+ let shuttingDown = false;
123
+ const shutdown = async (reason) => {
124
+ if (shuttingDown)
125
+ return;
126
+ shuttingDown = true;
127
+ log(`Shutting down gracefully (${reason})...`);
128
+ try {
129
+ // 1. Close system settings DB
130
+ closeConfigStorage();
131
+ // 2. Close main ledger DB
132
+ const storage = await getStorage();
133
+ if (storage && typeof storage.close === "function") {
134
+ await storage.close();
135
+ }
136
+ // 3. Remove PID lockfile (only if WE own it)
137
+ if (fs.existsSync(PID_FILE)) {
138
+ try {
139
+ const storedPid = parseInt(fs.readFileSync(PID_FILE, "utf8").trim(), 10);
140
+ if (storedPid === process.pid) {
141
+ fs.unlinkSync(PID_FILE);
142
+ }
143
+ }
144
+ catch {
145
+ // Ignore read errors during shutdown
146
+ }
147
+ }
148
+ }
149
+ catch (err) {
150
+ log(`Error during shutdown cleanup: ${err}`);
151
+ }
152
+ finally {
153
+ process.exit(0);
154
+ }
155
+ };
156
+ // OS Signals
157
+ process.on("SIGINT", () => shutdown("SIGINT"));
158
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
159
+ process.on("SIGHUP", () => shutdown("SIGHUP"));
160
+ // MCP Client Disconnect (CRITICAL)
161
+ process.stdin.on("close", () => {
162
+ shutdown("CLIENT_DISCONNECTED_STDIN_CLOSED");
163
+ });
164
+ }
package/dist/server.js CHANGED
@@ -61,6 +61,7 @@ SubscribeRequestSchema, UnsubscribeRequestSchema, } from "@modelcontextprotocol/
61
61
  import { SERVER_CONFIG, SESSION_MEMORY_ENABLED, PRISM_USER_ID, PRISM_ENABLE_HIVEMIND } from "./config.js";
62
62
  import { getSyncBus } from "./sync/factory.js";
63
63
  import { startDashboardServer } from "./dashboard/server.js";
64
+ import { acquireLock, registerShutdownHandlers } from "./lifecycle.js";
64
65
  // โ”€โ”€โ”€ v2.3.6 FIX: Use Storage Abstraction for Prompts/Resources โ”€โ”€โ”€
65
66
  // CRITICAL FIX: Previously imported supabaseRpc/supabaseGet directly,
66
67
  // which bypassed the storage abstraction layer and caused the server
@@ -114,43 +115,52 @@ const BASE_TOOLS = [
114
115
  BRAVE_ANSWERS_TOOL, // brave_answers โ€” AI-grounded answers
115
116
  RESEARCH_PAPER_ANALYSIS_TOOL, // gemini_research_paper_analysis โ€” paper analysis
116
117
  ];
117
- // Session memory tools: only added when SUPABASE_URL + SUPABASE_KEY are set
118
- // REVIEWER NOTE: v0.4.0 adds 2 new tools here:
119
- // - session_compact_ledger (Enhancement #2): auto-rollup of old ledger entries
120
- // - session_search_memory (Enhancement #4): semantic search via pgvector embeddings
121
- const SESSION_MEMORY_TOOLS = [
122
- SESSION_SAVE_LEDGER_TOOL, // session_save_ledger โ€” append immutable session log
123
- SESSION_SAVE_HANDOFF_TOOL, // session_save_handoff โ€” upsert latest project state (now with OCC)
124
- SESSION_LOAD_CONTEXT_TOOL, // session_load_context โ€” progressive context loading
125
- KNOWLEDGE_SEARCH_TOOL, // knowledge_search โ€” search accumulated knowledge
126
- KNOWLEDGE_FORGET_TOOL, // knowledge_forget โ€” prune bad/old memories
127
- SESSION_COMPACT_LEDGER_TOOL, // session_compact_ledger โ€” auto-compact old ledger entries (v0.4.0)
128
- SESSION_SEARCH_MEMORY_TOOL, // session_search_memory โ€” semantic search via embeddings (v0.4.0)
129
- MEMORY_HISTORY_TOOL, // memory_history โ€” view version timeline (v2.0)
130
- MEMORY_CHECKOUT_TOOL, // memory_checkout โ€” revert to past version (v2.0)
131
- // โ”€โ”€โ”€ v2.0: Visual Memory tools โ”€โ”€โ”€
132
- SESSION_SAVE_IMAGE_TOOL, // session_save_image โ€” save image to media vault (v2.0)
133
- SESSION_VIEW_IMAGE_TOOL, // session_view_image โ€” retrieve image from vault (v2.0)
134
- // โ”€โ”€โ”€ v2.2.0: Health Check tool โ”€โ”€โ”€
135
- SESSION_HEALTH_CHECK_TOOL, // session_health_check โ€” brain integrity checker (v2.2.0)
136
- // โ”€โ”€โ”€ Phase 2: GDPR Memory Deletion tool โ”€โ”€โ”€
137
- SESSION_FORGET_MEMORY_TOOL, // session_forget_memory โ€” GDPR-compliant memory deletion (Phase 2)
138
- // โ”€โ”€โ”€ v3.1: TTL Retention tool โ”€โ”€โ”€
139
- KNOWLEDGE_SET_RETENTION_TOOL, // knowledge_set_retention โ€” set auto-expiry TTL for a project
140
- // โ”€โ”€โ”€ v4.0: Active Behavioral Memory tools โ”€โ”€โ”€
141
- SESSION_SAVE_EXPERIENCE_TOOL, // session_save_experience โ€” record typed experience events
142
- KNOWLEDGE_UPVOTE_TOOL, // knowledge_upvote โ€” increase entry importance
143
- KNOWLEDGE_DOWNVOTE_TOOL, // knowledge_downvote โ€” decrease entry importance
144
- ];
145
- // Combine: always list ALL tools so scanners (Glama, Smithery, MCP Registry)
146
- // can enumerate the full capability set. Runtime guards in the CallTool handler
147
- // still prevent execution without valid Supabase credentials.
148
- const ALL_TOOLS = [
149
- ...BASE_TOOLS,
150
- ...SESSION_MEMORY_TOOLS,
151
- // v3.0: Agent Hivemind tools โ€” only when PRISM_ENABLE_HIVEMIND=true
152
- ...(PRISM_ENABLE_HIVEMIND ? AGENT_REGISTRY_TOOLS : []),
153
- ];
118
+ // โ”€โ”€โ”€ v4.1 FIX: Build Session Memory Tools dynamically โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
119
+ // The session_load_context tool description is dynamically modified
120
+ // to include auto-load project instructions. Tool descriptions are
121
+ // the ONLY mechanism guaranteed by ALL MCP clients (Antigravity,
122
+ // Claude Code, Claude CLI). The `instructions` field and
123
+ // sendLoggingMessage are NOT reliably surfaced by any client.
124
+ //
125
+ // buildSessionMemoryTools() is called inside createServer() AFTER
126
+ // initConfigStorage() has run, so getSettingSync() returns real
127
+ // dashboard config values.
128
+ function buildSessionMemoryTools(autoloadList) {
129
+ // Dynamically inject auto-load instruction into session_load_context description
130
+ let loadContextTool = SESSION_LOAD_CONTEXT_TOOL;
131
+ if (autoloadList.length > 0) {
132
+ const projectList = autoloadList.join(", ");
133
+ loadContextTool = {
134
+ ...SESSION_LOAD_CONTEXT_TOOL,
135
+ description: SESSION_LOAD_CONTEXT_TOOL.description +
136
+ `\n\nIMPORTANT: At the start of every conversation, you MUST call this tool for each of these projects: ${projectList}. Do this before any other work. Load level should be "standard" unless the user specifies otherwise.`,
137
+ };
138
+ }
139
+ return [
140
+ SESSION_SAVE_LEDGER_TOOL, // session_save_ledger โ€” append immutable session log
141
+ SESSION_SAVE_HANDOFF_TOOL, // session_save_handoff โ€” upsert latest project state (now with OCC)
142
+ loadContextTool, // session_load_context โ€” progressive context loading (+ auto-load instruction)
143
+ KNOWLEDGE_SEARCH_TOOL, // knowledge_search โ€” search accumulated knowledge
144
+ KNOWLEDGE_FORGET_TOOL, // knowledge_forget โ€” prune bad/old memories
145
+ SESSION_COMPACT_LEDGER_TOOL, // session_compact_ledger โ€” auto-compact old ledger entries (v0.4.0)
146
+ SESSION_SEARCH_MEMORY_TOOL, // session_search_memory โ€” semantic search via embeddings (v0.4.0)
147
+ MEMORY_HISTORY_TOOL, // memory_history โ€” view version timeline (v2.0)
148
+ MEMORY_CHECKOUT_TOOL, // memory_checkout โ€” revert to past version (v2.0)
149
+ // โ”€โ”€โ”€ v2.0: Visual Memory tools โ”€โ”€โ”€
150
+ SESSION_SAVE_IMAGE_TOOL, // session_save_image โ€” save image to media vault (v2.0)
151
+ SESSION_VIEW_IMAGE_TOOL, // session_view_image โ€” retrieve image from vault (v2.0)
152
+ // โ”€โ”€โ”€ v2.2.0: Health Check tool โ”€โ”€โ”€
153
+ SESSION_HEALTH_CHECK_TOOL, // session_health_check โ€” brain integrity checker (v2.2.0)
154
+ // โ”€โ”€โ”€ Phase 2: GDPR Memory Deletion tool โ”€โ”€โ”€
155
+ SESSION_FORGET_MEMORY_TOOL, // session_forget_memory โ€” GDPR-compliant memory deletion (Phase 2)
156
+ // โ”€โ”€โ”€ v3.1: TTL Retention tool โ”€โ”€โ”€
157
+ KNOWLEDGE_SET_RETENTION_TOOL, // knowledge_set_retention โ€” set auto-expiry TTL for a project
158
+ // โ”€โ”€โ”€ v4.0: Active Behavioral Memory tools โ”€โ”€โ”€
159
+ SESSION_SAVE_EXPERIENCE_TOOL, // session_save_experience โ€” record typed experience events
160
+ KNOWLEDGE_UPVOTE_TOOL, // knowledge_upvote โ€” increase entry importance
161
+ KNOWLEDGE_DOWNVOTE_TOOL, // knowledge_downvote โ€” decrease entry importance
162
+ ];
163
+ }
154
164
  // โ”€โ”€โ”€ v0.4.0: Resource Subscription Tracking โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
155
165
  // REVIEWER NOTE: We track which project URIs clients have subscribed
156
166
  // to. When sessionSaveHandoffHandler successfully updates a project,
@@ -194,6 +204,35 @@ export function notifyResourceUpdate(project, server) {
194
204
  * with subscribe support for live refresh
195
205
  */
196
206
  export function createServer() {
207
+ // โ”€โ”€โ”€ v4.1 FIX: Auto-Load via Dynamic Tool Descriptions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
208
+ // Read auto-load projects EXCLUSIVELY from dashboard config
209
+ // (available after initConfigStorage() in startServer).
210
+ //
211
+ // ARCHITECTURE DECISION: We inject the auto-load instruction into
212
+ // the session_load_context TOOL DESCRIPTION, not into `instructions`
213
+ // or `sendLoggingMessage`. Tool descriptions are the ONLY mechanism
214
+ // guaranteed by ALL MCP clients (Antigravity, Claude Code, Claude CLI).
215
+ //
216
+ // The PRISM_AUTOLOAD_PROJECTS env var has been removed โ€” the dashboard
217
+ // is the single source of truth. This prevents mismatches between
218
+ // env var and dashboard settings causing duplicate project loads.
219
+ const dashboardAutoload = getSettingSync("autoload_projects", "");
220
+ const autoloadList = dashboardAutoload
221
+ .split(",").map(p => p.trim()).filter(Boolean);
222
+ if (autoloadList.length > 0) {
223
+ console.error(`[Prism] Auto-load projects (dashboard): ${autoloadList.join(', ')}`);
224
+ }
225
+ // Build the dynamic tool list with auto-load instruction injected
226
+ const SESSION_MEMORY_TOOLS = buildSessionMemoryTools(autoloadList);
227
+ // Combine: always list ALL tools so scanners (Glama, Smithery, MCP Registry)
228
+ // can enumerate the full capability set. Runtime guards in the CallTool handler
229
+ // still prevent execution without valid Supabase credentials.
230
+ const ALL_TOOLS = [
231
+ ...BASE_TOOLS,
232
+ ...SESSION_MEMORY_TOOLS,
233
+ // v3.0: Agent Hivemind tools โ€” only when PRISM_ENABLE_HIVEMIND=true
234
+ ...(PRISM_ENABLE_HIVEMIND ? AGENT_REGISTRY_TOOLS : []),
235
+ ];
197
236
  const server = new Server({
198
237
  name: SERVER_CONFIG.name,
199
238
  version: SERVER_CONFIG.version,
@@ -201,21 +240,13 @@ export function createServer() {
201
240
  capabilities: {
202
241
  tools: {},
203
242
  // โ”€โ”€โ”€ v0.4.0: Prompt capability (Enhancement #1) โ”€โ”€โ”€
204
- // REVIEWER NOTE: Declaring `prompts: {}` tells Claude Desktop
205
- // that we support the prompts/list and prompts/get methods.
206
- // This enables the /resume_session slash command in the UI.
207
- // Only enabled when Supabase is configured (prompts need
208
- // session data to be useful).
209
243
  ...(SESSION_MEMORY_ENABLED ? { prompts: {} } : {}),
210
244
  // โ”€โ”€โ”€ v0.4.0: Resource capability (Enhancement #3) โ”€โ”€โ”€
211
- // REVIEWER NOTE: Setting subscribe: true tells Claude Desktop
212
- // that we support resource subscriptions. When a user attaches
213
- // memory://project/handoff and the LLM later updates it,
214
- // we push an update notification so the attached context
215
- // is silently refreshed. Without subscribe:true, the
216
- // paperclipped context would become stale.
217
245
  ...(SESSION_MEMORY_ENABLED ? { resources: { subscribe: true } } : {}),
218
246
  },
247
+ // Supplementary signal โ€” not all clients support this field.
248
+ // Primary mechanism is the dynamic tool description above.
249
+ instructions: `Prism MCP โ€” The Mind Palace for AI Agents. This server provides persistent session memory, knowledge search, and context management tools. Use session_load_context to recover previous work state, session_save_ledger to log completed work, and session_save_handoff to preserve state for the next session.`,
219
250
  });
220
251
  // โ”€โ”€ Handler: Initialize โ”€โ”€
221
252
  // NOTE: The SDK's built-in _oninitialize() handles the Initialize request.
@@ -666,7 +697,7 @@ export function createSandboxServer() {
666
697
  });
667
698
  // Register all tool listings unconditionally
668
699
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
669
- tools: [...BASE_TOOLS, ...SESSION_MEMORY_TOOLS, ...AGENT_REGISTRY_TOOLS],
700
+ tools: [...BASE_TOOLS, ...buildSessionMemoryTools([]), ...AGENT_REGISTRY_TOOLS],
670
701
  }));
671
702
  // Register prompts listing so scanners see resume_session
672
703
  server.setRequestHandler(ListPromptsRequestSchema, async () => ({
@@ -715,6 +746,9 @@ export function createSandboxServer() {
715
746
  * responses to stdout. Log messages go to stderr.
716
747
  */
717
748
  export async function startServer() {
749
+ // MUST BE FIRST: Kill any zombie processes and acquire the singleton PID lock
750
+ // before touching SQLite. This prevents lock contention on prism-config.db.
751
+ acquireLock();
718
752
  // Pre-warm the config settings cache BEFORE connecting the MCP transport.
719
753
  // This ensures getSettingSync() returns real values (agent_name, default_role)
720
754
  // during the Initialize handshake โ€” zero extra latency for resource reads.
@@ -723,6 +757,10 @@ export async function startServer() {
723
757
  const server = createServer();
724
758
  const transport = new StdioServerTransport();
725
759
  await server.connect(transport);
760
+ // Register graceful shutdown handlers (SIGTERM, SIGINT, SIGHUP, stdin close).
761
+ // The stdin close handler is critical โ€” when MCP clients disconnect, they
762
+ // often just close the pipe without sending a signal, leaving zombie processes.
763
+ registerShutdownHandlers();
726
764
  // Pre-warm storage AFTER connecting โ€” fired async so we never block the
727
765
  // stdio handshake. Supabase REST initialization can take 500msโ€“5s; blocking
728
766
  // on it before server.connect() was the root cause of the 1m 56s CLI delay.
@@ -740,6 +778,18 @@ export async function startServer() {
740
778
  ]).catch(err => {
741
779
  console.error(`[Prism] Storage pre-warm failed (non-fatal): ${err}`);
742
780
  });
781
+ // โ”€โ”€โ”€ v4.1: Auto-Load is handled via dynamic tool descriptions โ”€โ”€
782
+ // The session_load_context tool description is dynamically modified
783
+ // in createServer() โ†’ buildSessionMemoryTools() to include the
784
+ // auto-load projects list. This is the ONLY universally reliable
785
+ // mechanism โ€” tool descriptions are surfaced by ALL MCP clients.
786
+ //
787
+ // Previous approaches that FAILED:
788
+ // - sendLoggingMessage: goes to debug logs, not AI conversation
789
+ // - instructions field: not supported by Claude Code or Claude CLI
790
+ //
791
+ // No runtime code needed here โ€” the instruction is baked into the
792
+ // tool schema returned by ListTools.
743
793
  }
744
794
  // โ”€โ”€โ”€ v2.0 Step 6: Initialize SyncBus (Telepathy) โ”€โ”€โ”€
745
795
  // Fire-and-forget โ€” SyncBus is non-critical for startup.
@@ -102,17 +102,36 @@ export async function getSetting(key, defaultValue = "") {
102
102
  export async function setSetting(key, value) {
103
103
  await initConfigStorage();
104
104
  const client = getClient();
105
- await client.execute({
106
- sql: `
107
- INSERT INTO system_settings (key, value, updated_at)
108
- VALUES (?, ?, CURRENT_TIMESTAMP)
109
- ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP
110
- `,
111
- args: [key, value],
112
- });
113
- // Keep the cache in sync so getSettingSync() reflects the new value immediately.
114
- if (settingsCache) {
115
- settingsCache[key] = value;
105
+ // Retry with exponential backoff for SQLITE_BUSY (concurrent writes).
106
+ // The dashboard and load tests can fire many parallel setting saves.
107
+ const MAX_RETRIES = 5;
108
+ const BASE_DELAY_MS = 20;
109
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
110
+ try {
111
+ await client.execute({
112
+ sql: `
113
+ INSERT INTO system_settings (key, value, updated_at)
114
+ VALUES (?, ?, CURRENT_TIMESTAMP)
115
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP
116
+ `,
117
+ args: [key, value],
118
+ });
119
+ // Keep the cache in sync so getSettingSync() reflects the new value immediately.
120
+ if (settingsCache) {
121
+ settingsCache[key] = value;
122
+ }
123
+ return; // Success โ€” exit
124
+ }
125
+ catch (err) {
126
+ const isBusy = err?.code === "SQLITE_BUSY" || err?.rawCode === 5;
127
+ if (isBusy && attempt < MAX_RETRIES) {
128
+ // Exponential backoff + jitter
129
+ const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 10;
130
+ await new Promise(r => setTimeout(r, delay));
131
+ continue;
132
+ }
133
+ throw err; // Not SQLITE_BUSY or retries exhausted
134
+ }
116
135
  }
117
136
  }
118
137
  export async function getAllSettings() {
@@ -129,3 +148,17 @@ export async function getAllSettings() {
129
148
  }
130
149
  return settings;
131
150
  }
151
+ /**
152
+ * Closes the config SQLite client to release the file handle on prism-config.db.
153
+ * Called by the lifecycle module during graceful shutdown.
154
+ */
155
+ export function closeConfigStorage() {
156
+ if (configClient) {
157
+ try {
158
+ configClient.close();
159
+ }
160
+ catch (e) {
161
+ console.error(`[ConfigStorage] Error closing db:`, e);
162
+ }
163
+ }
164
+ }
@@ -15,10 +15,18 @@
15
15
  import { supabasePost, supabaseGet, supabaseRpc, supabasePatch, supabaseDelete, } from "../utils/supabaseApi.js";
16
16
  import { debugLog } from "../utils/logger.js";
17
17
  import { getSetting as cfgGet, setSetting as cfgSet, getAllSettings as cfgGetAll } from "./configStorage.js";
18
+ import { runAutoMigrations } from "./supabaseMigrations.js";
18
19
  export class SupabaseStorage {
19
20
  // โ”€โ”€โ”€ Lifecycle โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
20
21
  async initialize() {
21
22
  debugLog("[SupabaseStorage] Initialized (REST API, stateless)");
23
+ // Auto-apply pending schema migrations (non-fatal)
24
+ try {
25
+ await runAutoMigrations();
26
+ }
27
+ catch (err) {
28
+ console.error("[SupabaseStorage] Auto-migration failed. Server will continue, but some tools may be unstable.", err instanceof Error ? err.message : err);
29
+ }
22
30
  }
23
31
  async close() {
24
32
  debugLog("[SupabaseStorage] Closed (no-op for REST)");
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Supabase Auto-Migration Runner (v4.1)
3
+ *
4
+ * On server startup, this module checks the `prism_schema_versions` table
5
+ * and applies any pending DDL migrations via the `prism_apply_ddl` RPC.
6
+ *
7
+ * โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
8
+ * HOW IT WORKS:
9
+ * 1. For each migration in MIGRATIONS[], call prism_apply_ddl(version, name, sql)
10
+ * 2. The Postgres function checks if the version is already applied (idempotent)
11
+ * 3. If not applied, it EXECUTE's the SQL and records the version
12
+ *
13
+ * GRACEFUL DEGRADATION:
14
+ * If prism_apply_ddl doesn't exist (PGRST202), the runner logs a
15
+ * warning and skips โ€” the server still starts, but v4+ tools may
16
+ * fail against an old schema.
17
+ *
18
+ * SECURITY NOTE:
19
+ * prism_apply_ddl is SECURITY DEFINER (runs as postgres owner).
20
+ * The prism_schema_versions table has RLS: only service_role can write.
21
+ * โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
22
+ */
23
+ import { supabaseRpc } from "../utils/supabaseApi.js";
24
+ /**
25
+ * All Supabase DDL migrations.
26
+ *
27
+ * IMPORTANT: Only add migrations for schema changes that Supabase
28
+ * users need. SQLite handles its own schema in sqlite.ts.
29
+ *
30
+ * Each `sql` string is passed to Postgres EXECUTE โ€” it runs as a
31
+ * single transaction. Use IF NOT EXISTS / IF EXISTS guards generously.
32
+ */
33
+ export const MIGRATIONS = [
34
+ {
35
+ version: 26,
36
+ name: "active_behavioral_memory",
37
+ sql: `
38
+ -- v4.0: Active Behavioral Memory columns
39
+ ALTER TABLE session_ledger ADD COLUMN IF NOT EXISTS event_type TEXT NOT NULL DEFAULT 'session';
40
+ ALTER TABLE session_ledger ADD COLUMN IF NOT EXISTS confidence_score INTEGER DEFAULT NULL;
41
+ ALTER TABLE session_ledger ADD COLUMN IF NOT EXISTS importance INTEGER NOT NULL DEFAULT 0;
42
+
43
+ -- Soft-delete / archival columns
44
+ ALTER TABLE session_ledger ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ DEFAULT NULL;
45
+ ALTER TABLE session_ledger ADD COLUMN IF NOT EXISTS archived_at TIMESTAMPTZ DEFAULT NULL;
46
+ ALTER TABLE session_ledger ADD COLUMN IF NOT EXISTS deleted_reason TEXT DEFAULT NULL;
47
+
48
+ -- Indexes
49
+ CREATE INDEX IF NOT EXISTS idx_ledger_event_type ON session_ledger(event_type);
50
+ CREATE INDEX IF NOT EXISTS idx_ledger_importance ON session_ledger(importance DESC);
51
+
52
+ -- Partial index for high-priority warnings
53
+ CREATE INDEX IF NOT EXISTS idx_ledger_behavioral_warnings
54
+ ON session_ledger(project, user_id, role, importance DESC)
55
+ WHERE event_type = 'correction' AND importance >= 3
56
+ AND deleted_at IS NULL AND archived_at IS NULL;
57
+ `,
58
+ },
59
+ // Future migrations go here (version 28+)
60
+ ];
61
+ /**
62
+ * Current schema version โ€” derived from the MIGRATIONS array.
63
+ * Automatically updates when new migrations are added.
64
+ * Used for logging and diagnostics.
65
+ */
66
+ export const CURRENT_SCHEMA_VERSION = MIGRATIONS.length > 0 ? MIGRATIONS[MIGRATIONS.length - 1].version : 27;
67
+ // โ”€โ”€โ”€ Runner โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
68
+ /**
69
+ * Run all pending auto-migrations on Supabase startup.
70
+ *
71
+ * Called from SupabaseStorage.initialize(). Non-fatal: if the
72
+ * migration infrastructure (027) hasn't been applied, the runner
73
+ * logs a warning and returns silently.
74
+ */
75
+ export async function runAutoMigrations() {
76
+ if (MIGRATIONS.length === 0) {
77
+ return; // Nothing to apply
78
+ }
79
+ console.error(`[Prism Auto-Migration] Schema v${CURRENT_SCHEMA_VERSION} โ€” checking ${MIGRATIONS.length} migration(s)โ€ฆ`);
80
+ for (const migration of MIGRATIONS) {
81
+ try {
82
+ const result = await supabaseRpc("prism_apply_ddl", {
83
+ p_version: migration.version,
84
+ p_name: migration.name,
85
+ p_sql: migration.sql,
86
+ });
87
+ // Parse the JSON result from the RPC
88
+ const data = (typeof result === "string" ? JSON.parse(result) : result);
89
+ if (data?.status === "applied") {
90
+ console.error(`[Prism Auto-Migration] โœ… Applied migration ${migration.version}: ${migration.name}`);
91
+ }
92
+ else if (data?.status === "already_applied") {
93
+ // Silent skip โ€” expected for idempotent restarts
94
+ }
95
+ }
96
+ catch (err) {
97
+ const errMsg = err instanceof Error ? err.message : String(err);
98
+ // PGRST202 = function not found โ†’ migration infra (027) not applied yet
99
+ if (errMsg.includes("PGRST202") || errMsg.includes("Could not find the function")) {
100
+ console.error("[Prism Auto-Migration] โš ๏ธ prism_apply_ddl() not found. " +
101
+ "Apply migration 027_auto_migration_infra.sql to enable auto-migrations.\n" +
102
+ " Run: supabase db push (or apply the SQL in the Supabase Dashboard SQL Editor)");
103
+ return; // Stop โ€” no point trying further migrations
104
+ }
105
+ // Any other error: log and throw to surface the problem
106
+ console.error(`[Prism Auto-Migration] โŒ Migration ${migration.version} (${migration.name}) failed: ${errMsg}`);
107
+ throw err;
108
+ }
109
+ }
110
+ }
@@ -55,6 +55,23 @@ export async function sessionSaveLedgerHandler(args) {
55
55
  }
56
56
  const { project, conversation_id, summary, todos, files_changed, decisions, role } = args;
57
57
  const storage = await getStorage();
58
+ // โ”€โ”€โ”€ Repo path mismatch validation (v4.2) โ”€โ”€โ”€
59
+ let repoPathWarning = "";
60
+ if (files_changed && files_changed.length > 0) {
61
+ try {
62
+ const configuredPath = await getSetting(`repo_path:${project}`, "");
63
+ if (configuredPath && configuredPath.trim()) {
64
+ const normalizedPath = configuredPath.trim().replace(/\\/g, "/").replace(/\/+$/, ""); // normalize + strip trailing slash
65
+ const mismatched = files_changed.filter((f) => !f.replace(/\\/g, "/").startsWith(normalizedPath));
66
+ if (mismatched.length === files_changed.length) {
67
+ repoPathWarning = `\n\nโš ๏ธ Project mismatch: none of the files_changed paths match repo_path "${normalizedPath}" ` +
68
+ `configured for project "${project}". Consider saving under the correct project.`;
69
+ debugLog(`[session_save_ledger] Repo path mismatch for "${project}": expected prefix "${normalizedPath}"`);
70
+ }
71
+ }
72
+ }
73
+ catch { /* getSetting non-fatal */ }
74
+ }
58
75
  debugLog(`[session_save_ledger] Saving ledger entry for project="${project}"`);
59
76
  // Auto-extract keywords from summary + decisions for knowledge accumulation
60
77
  const combinedText = [summary, ...(decisions || [])].join(" ");
@@ -130,6 +147,7 @@ export async function sessionSaveLedgerHandler(args) {
130
147
  (files_changed?.length ? `Files changed: ${files_changed.length}\n` : "") +
131
148
  (decisions?.length ? `Decisions: ${decisions.length}\n` : "") +
132
149
  (GOOGLE_API_KEY ? `๐Ÿ“Š Embedding generation queued for semantic search.\n` : "") +
150
+ repoPathWarning +
133
151
  `\nRaw response: ${JSON.stringify(result)}`,
134
152
  }],
135
153
  isError: false,
@@ -360,7 +378,8 @@ export async function sessionLoadContextHandler(args) {
360
378
  throw new Error("Invalid arguments for session_load_context");
361
379
  }
362
380
  const { project, level = "standard", role } = args;
363
- const maxTokens = args.max_tokens; // v4.0
381
+ const maxTokens = args.max_tokens
382
+ || parseInt(await getSetting("max_tokens", "0"), 10) || undefined; // v4.0: arg > dashboard setting > none
364
383
  const agentName = await getSetting("agent_name", "");
365
384
  const validLevels = ["quick", "standard", "deep"];
366
385
  if (!validLevels.includes(level)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prism-mcp-server",
3
- "version": "4.0.0",
3
+ "version": "4.2.0",
4
4
  "mcpName": "io.github.dcostenco/prism-mcp",
5
5
  "description": "The Mind Palace for AI Agents โ€” local-first MCP server with persistent memory (SQLite/Supabase), visual dashboard, time travel, multi-agent sync, Morning Briefings, reality drift detection, code mode templates, semantic vector search, and Brave Search + Gemini analysis. Zero-config local mode.",
6
6
  "module": "index.ts",