prism-mcp-server 9.2.1 → 9.2.2

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
@@ -278,6 +278,13 @@ bash ~/.gemini/antigravity/scratch/prism_session_loader.sh my-project
278
278
 
279
279
  The CLI uses the same storage layer as the MCP tool (SQLite or Supabase).
280
280
 
281
+ > ⚠️ **CRITICAL (v9.2.2): Split-Brain Prevention**
282
+ > If your MCP server is configured with `PRISM_STORAGE=local` but Supabase credentials are also set, the CLI may read from the **wrong backend** (Supabase) while the server writes to SQLite. This causes stale TODOs and divergent state. Always pass `--storage local` explicitly when using the CLI in a local-mode environment:
283
+ > ```bash
284
+ > prism load my-project --storage local --json
285
+ > ```
286
+ > The `prism_session_loader.sh` wrapper handles this automatically since v9.2.2.
287
+
281
288
  </details>
282
289
 
283
290
  <details>
@@ -295,6 +302,10 @@ SUMMARY=$(echo "$CONTEXT" | jq -r '.handoff[0].last_summary')
295
302
  VERSION=$(echo "$CONTEXT" | jq -r '.handoff[0].version')
296
303
  echo "Project at v$VERSION: $SUMMARY"
297
304
 
305
+ # Explicit storage backend (v9.2.2 — prevents split-brain)
306
+ prism load my-project --storage local --json
307
+ prism load my-project --storage supabase --json
308
+
298
309
  # Role-scoped loading
299
310
  prism load my-project --role qa --json
300
311
 
@@ -778,8 +789,9 @@ The Generator strips the `console.log`, resubmits, and the next `EVALUATE` retur
778
789
 
779
790
  ## 🆕 What's New
780
791
 
781
- > **Current release: v9.2.1CLI Full Feature Parity**
792
+ > **Current release: v9.2.2Critical Split-Brain Fix**
782
793
 
794
+ - 🚨 **v9.2.2 — Critical: Split-Brain Detection & Prevention:** When multiple MCP clients use different storage backends (e.g., Claude Desktop → Supabase, Antigravity → SQLite), session state could silently diverge, causing agents to act on stale TODOs and outdated context. **New: `--storage` flag** on `prism load` CLI lets callers explicitly select which backend to read from. **New: Split-Brain Drift Detection** in `session_load_context` — compares active and alternate backend versions at load time and warns prominently when they diverge. Session loader script updated to respect `PRISM_STORAGE` environment variable.
783
795
  - 💻 **v9.2.1 — CLI Full Feature Parity:** `prism load` text mode now delegates to the real `session_load_context` handler, giving CLI-only users the same enriched output as MCP clients: morning briefings, reality drift detection, SDM intuitive recall, visual memory index, role-scoped skill injection, behavioral warnings, importance scores, and agent identity. JSON mode now includes `agent_name` from dashboard settings. Session loader script PATH fix for Homebrew/nvm/volta environments.
784
796
  - 🚦 **v9.1.0 — Task Router v2:** File-type complexity signal for intelligent code-vs-config routing, 6-signal weighted heuristic engine, multi-step false-positive fix, expanded file extension classification. Local agent hardened with buffered streaming, system prompts, memory trimming, and stateful `/api/chat` API.
785
797
  - 🔒 **v9.0.5 — JWKS Auth Security Hardening:** JWT audience/issuer claim validation (`PRISM_JWT_AUDIENCE`, `PRISM_JWT_ISSUER`), structured error logging for JWT failures, typed `PrismAuthenticatedRequest` interface, 11 new JWKS unit tests, Smithery server card fix. Vendor-neutral — tested with Auth0, AgentLair ([llms.txt](https://agentlair.com/llms.txt)), Keycloak, and custom JWKS endpoints.
package/dist/cli.js CHANGED
@@ -19,6 +19,15 @@ program
19
19
  // Designed for environments that cannot use MCP tools directly
20
20
  // (Antigravity, Bash scripts, CI/CD pipelines).
21
21
  //
22
+ // CRITICAL: --storage flag (v9.2.2)
23
+ // When multiple MCP clients use different storage backends (e.g.
24
+ // Claude Desktop → Supabase, Antigravity → SQLite), the CLI must
25
+ // be told which backend to read from. Without this, the CLI
26
+ // inherits PRISM_STORAGE from the shell env (defaulting to
27
+ // supabase), which may differ from the MCP server's config.
28
+ // This causes a "split-brain" where the CLI returns stale state
29
+ // from the wrong backend.
30
+ //
22
31
  // TEXT MODE: Delegates to the real sessionLoadContextHandler for
23
32
  // full feature parity — morning briefing, reality drift detection,
24
33
  // SDM recall, visual memory, skill injection, behavioral warnings,
@@ -32,10 +41,21 @@ program
32
41
  .description('Load session context for a project (same output as session_load_context MCP tool)')
33
42
  .option('-l, --level <level>', 'Context depth: quick, standard, deep', 'standard')
34
43
  .option('-r, --role <role>', 'Role scope for context loading')
44
+ .option('-s, --storage <backend>', 'Storage backend: local (SQLite) or supabase. Overrides PRISM_STORAGE env var.')
35
45
  .option('--json', 'Emit machine-readable JSON instead of formatted text')
36
46
  .action(async (project, options) => {
37
47
  try {
38
- const { level, role, json: jsonOutput } = options;
48
+ const { level, role, storage, json: jsonOutput } = options;
49
+ // v9.2.2: --storage flag overrides PRISM_STORAGE env var to prevent
50
+ // split-brain when CLI environment differs from MCP server config.
51
+ if (storage) {
52
+ const validStorages = ['local', 'supabase'];
53
+ if (!validStorages.includes(storage)) {
54
+ console.error(`Error: Invalid storage "${storage}". Must be one of: ${validStorages.join(', ')}`);
55
+ process.exit(1);
56
+ }
57
+ process.env.PRISM_STORAGE = storage;
58
+ }
39
59
  const validLevels = ['quick', 'standard', 'deep'];
40
60
  if (!validLevels.includes(level)) {
41
61
  console.error(`Error: Invalid level "${level}". Must be one of: ${validLevels.join(', ')}`);
@@ -43,10 +63,10 @@ program
43
63
  }
44
64
  if (jsonOutput) {
45
65
  // ── JSON mode: structured output for programmatic consumption ──
46
- const storage = await getStorage();
66
+ const storageBackend = await getStorage();
47
67
  const effectiveRole = role || await getSetting('default_role', '') || undefined;
48
68
  const agentName = await getSetting('agent_name', '') || undefined;
49
- const data = await storage.loadContext(project, level, PRISM_USER_ID, effectiveRole);
69
+ const data = await storageBackend.loadContext(project, level, PRISM_USER_ID, effectiveRole);
50
70
  if (!data) {
51
71
  console.log(JSON.stringify({ error: `No session context found for project "${project}"` }));
52
72
  await closeStorage();
@@ -23,7 +23,7 @@ import { buildVaultDirectory } from "../utils/vaultExporter.js";
23
23
  * ═══════════════════════════════════════════════════════════════════
24
24
  */
25
25
  import { debugLog } from "../utils/logger.js";
26
- import { getStorage } from "../storage/index.js";
26
+ import { getStorage, activeStorageBackend } from "../storage/index.js";
27
27
  import { toKeywordArray } from "../utils/keywordExtractor.js";
28
28
  import { getLLMProvider } from "../utils/llm/factory.js";
29
29
  import { getCurrentGitState, getGitDrift } from "../utils/git.js";
@@ -524,6 +524,67 @@ export async function sessionLoadContextHandler(args) {
524
524
  const versionNote = version
525
525
  ? `\n\n🔑 Session version: ${version}. Pass expected_version: ${version} when saving handoff.`
526
526
  : "";
527
+ // ─── v9.2.2: Split-Brain Drift Detection ───────────────────
528
+ // When using one storage backend (e.g. SQLite), check if an
529
+ // alternate backend (e.g. Supabase) exists with a different
530
+ // version. This prevents agents from unknowingly acting on
531
+ // stale state from a diverged backend.
532
+ //
533
+ // PERF NOTE: We do NOT construct a full StorageBackend for the
534
+ // alternate check — that would trigger full migrations/schema
535
+ // validation on every load (~200-1000ms). Instead we use
536
+ // lightweight direct queries: REST GET for Supabase, raw SQL
537
+ // for SQLite. This keeps the check under ~100ms.
538
+ let splitBrainWarning = "";
539
+ try {
540
+ const { SUPABASE_CONFIGURED, SUPABASE_URL, SUPABASE_KEY } = await import("../config.js");
541
+ if (activeStorageBackend === "local" && SUPABASE_CONFIGURED && SUPABASE_URL && SUPABASE_KEY) {
542
+ // Lightweight Supabase version check via REST (no full init/migrations)
543
+ const { supabaseGet } = await import("../utils/supabaseApi.js");
544
+ const rows = await supabaseGet("session_handoffs", {
545
+ project: `eq.${project}`,
546
+ select: "version",
547
+ limit: "1",
548
+ });
549
+ const altVersion = Array.isArray(rows) && rows[0] ? rows[0].version : null;
550
+ if (altVersion && altVersion !== version) {
551
+ splitBrainWarning = `\n\n⚠️ **SPLIT-BRAIN DETECTED** (v${version} local vs v${altVersion} cloud)\n` +
552
+ `Your local SQLite state (v${version}) differs from the Supabase cloud state (v${altVersion}). ` +
553
+ `This means another client (e.g. Claude Desktop) has saved state that this environment cannot see. ` +
554
+ `TODOs, summaries, and decisions may be stale. Please reconcile by running:\n` +
555
+ ` \`prism load ${project} --storage supabase\` to see the cloud state.`;
556
+ debugLog(`[session_load_context] SPLIT-BRAIN: local v${version} vs supabase v${altVersion}`);
557
+ }
558
+ }
559
+ else if (activeStorageBackend === "supabase") {
560
+ // Lightweight SQLite version check via direct file query (no full init/migrations)
561
+ const dbPath = nodePath.join(os.homedir(), ".prism-mcp", "data.db");
562
+ if (fs.existsSync(dbPath)) {
563
+ let altClient = null;
564
+ try {
565
+ const { createClient } = await import("@libsql/client");
566
+ altClient = createClient({ url: `file:${dbPath}` });
567
+ const result = await altClient.execute(`SELECT version FROM session_handoffs WHERE project = ? LIMIT 1`, [project]);
568
+ const altVersion = result.rows?.[0]?.version;
569
+ if (altVersion && altVersion !== version) {
570
+ splitBrainWarning = `\n\n⚠️ **SPLIT-BRAIN DETECTED** (v${version} cloud vs v${altVersion} local)\n` +
571
+ `Your Supabase cloud state (v${version}) differs from the local SQLite state (v${altVersion}). ` +
572
+ `This means another client has saved state that this environment cannot see. ` +
573
+ `TODOs, summaries, and decisions may be stale. Please reconcile by running:\n` +
574
+ ` \`prism load ${project} --storage local\` to see the local state.`;
575
+ debugLog(`[session_load_context] SPLIT-BRAIN: supabase v${version} vs local v${altVersion}`);
576
+ }
577
+ }
578
+ finally {
579
+ if (altClient)
580
+ altClient.close();
581
+ }
582
+ }
583
+ }
584
+ }
585
+ catch (err) {
586
+ debugLog(`[session_load_context] Split-brain check failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
587
+ }
527
588
  // ─── Reality Drift Detection (v2.0 Step 5) ───
528
589
  // Check if the developer changed code since the last handoff save.
529
590
  let driftReport = "";
@@ -721,7 +782,7 @@ export async function sessionLoadContextHandler(args) {
721
782
  }
722
783
  }
723
784
  // Build the response object before v4.0 augmentations
724
- let responseText = `📋 Session context for "${project}" (${level}):\n\n${formattedContext.trim()}${driftReport}${briefingBlock}${sdmRecallBlock}${greetingBlock}${visualMemoryBlock}${skillBlock}${versionNote}`;
785
+ let responseText = `📋 Session context for "${project}" (${level}):\n\n${formattedContext.trim()}${splitBrainWarning}${driftReport}${briefingBlock}${sdmRecallBlock}${greetingBlock}${visualMemoryBlock}${skillBlock}${versionNote}`;
725
786
  // ─── v4.0: Behavioral Warnings Injection ───────────────────
726
787
  // If loadContext returned behavioral_warnings, add them to the
727
788
  // formatted output so the agent sees them prominently.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prism-mcp-server",
3
- "version": "9.2.1",
3
+ "version": "9.2.2",
4
4
  "mcpName": "io.github.dcostenco/prism-mcp",
5
5
  "description": "The Mind Palace for AI Agents — a true Cognitive Architecture with Hebbian learning (episodic→semantic consolidation), ACT-R spreading activation (multi-hop causal reasoning), uncertainty-aware rejection gates (agents that know when they don't know), adversarial evaluation (anti-sycophancy), fail-closed Dark Factory pipelines, persistent memory (SQLite/Supabase), multi-agent Hivemind, time travel & visual dashboard. Zero-config local mode.",
6
6
  "module": "index.ts",