prism-mcp-server 8.0.3 → 9.0.5

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
@@ -12,7 +12,7 @@
12
12
 
13
13
  **Your AI agent forgets everything between sessions. Prism fixes that — then teaches it to think.**
14
14
 
15
- Prism v8.0 is a true **Cognitive Architecture** inspired by human brain mechanics. The new **Synapse Engine** replaces flat vector search with pure multi-hop graph propagation your agent now follows causal trains of thought across memory, forms principles from experience, and knows when it lacks information. **Your agents don't just remember; they think.**
15
+ Prism v7.8 is a true **Cognitive Architecture** inspired by human brain mechanics. Beyond flat vector search, your agent now forms principles from experience, follows causal trains of thought, and possesses the self-awareness to know when it lacks information. **Your agents don't just remember; they learn.**
16
16
 
17
17
  ```bash
18
18
  npx -y prism-mcp-server
@@ -20,7 +20,7 @@ npx -y prism-mcp-server
20
20
 
21
21
  Works with **Claude Desktop · Claude Code · Cursor · Windsurf · Cline · Gemini · Antigravity** — **any MCP client.**
22
22
 
23
- ## Table of Contents
23
+ ## 📖 Table of Contents
24
24
 
25
25
  - [Why Prism?](#why-prism)
26
26
  - [Quick Start](#quick-start)
@@ -28,8 +28,7 @@ Works with **Claude Desktop · Claude Code · Cursor · Windsurf · Cline · Gem
28
28
  - [Setup Guides](#setup-guides)
29
29
  - [Universal Import: Bring Your History](#universal-import-bring-your-history)
30
30
  - [What Makes Prism Different](#what-makes-prism-different)
31
- - [Synapse Engine (v8.0)](#synapse-engine-v80)
32
- - [Cognitive Architecture (v7.8)](#cognitive-architecture-v78)
31
+ - [Cognitive Architecture (v7.8)](#-cognitive-architecture-v78)
33
32
  - [Data Privacy & Egress](#data-privacy--egress)
34
33
  - [Use Cases](#use-cases)
35
34
  - [What's New](#whats-new)
@@ -53,15 +52,15 @@ Every time you start a new conversation with an AI coding assistant, it starts f
53
52
 
54
53
  Prism has three pillars:
55
54
 
56
- 1. **🧠 Cognitive Memory** — Memories are ranked like a human brain: recently and frequently accessed context surfaces first, while stale context fades naturally via ACT-R activation decay. Raw experience consolidates into semantic principles through Hebbian learning. The result is retrieval quality that no flat vector search can match. *(See [Cognitive Architecture](#cognitive-architecture-v78) and [Scientific Foundation](#scientific-foundation).)*
55
+ 1. **🧠 Cognitive Memory** — Memories are ranked like a human brain: recently and frequently accessed context surfaces first, while stale context fades naturally via ACT-R activation decay. Raw experience consolidates into semantic principles through Hebbian learning. The result is retrieval quality that no flat vector search can match. *(See [Cognitive Architecture](#-cognitive-architecture-v78) and [Scientific Foundation](#-scientific-foundation).)*
57
56
 
58
- 2. **⚡ Synapse Engine (GraphRAG)** — When your agent searches for "Error X", the Synapse Engine doesn't just find logs mentioning "Error X". Multi-hop energy propagation traverses the causal graph dampened by fan effect, bounded by lateral inhibition — and surfaces "Workaround Y" connected to "Architecture Decision Z". Nodes discovered exclusively via graph traversal are tagged `[🌐 Synapse]` so you can *see* the engine working. *(See [Synapse Engine](#synapse-engine-v80).)*
57
+ 2. **🔗 Multi-Hop Reasoning** — When your agent searches for "Error X", Prism doesn't just find logs mentioning "Error X". Spreading activation traverses the causal graph and brings back "Workaround Y", which is connected to "Architecture Decision Z" a literal train of thought. *(See [Cognitive Architecture](#-cognitive-architecture-v78).)*
59
58
 
60
- 3. **🏭 Autonomous Execution (Dark Factory)** — When you're ready, Prism can run coding tasks end-to-end with a fail-closed pipeline where an adversarial evaluator catches bugs the generator missed — before you ever see the PR. *(See [Dark Factory](#dark-factory--adversarial-autonomous-pipelines).)*
59
+ 3. **🏭 Autonomous Execution (Dark Factory)** — When you're ready, Prism can run coding tasks end-to-end with a fail-closed pipeline where an adversarial evaluator catches bugs the generator missed — before you ever see the PR. *(See [Dark Factory](#-dark-factory--adversarial-autonomous-pipelines).)*
61
60
 
62
61
  ---
63
62
 
64
- ## Quick Start
63
+ ## 🚀 Quick Start
65
64
 
66
65
  ### Prerequisites
67
66
 
@@ -135,7 +134,7 @@ Then open `http://localhost:3001` instead.
135
134
 
136
135
  ---
137
136
 
138
- ## The Magic Moment
137
+ ## The Magic Moment
139
138
 
140
139
  > **Session 1** (Monday evening):
141
140
  > ```
@@ -156,7 +155,7 @@ Then open `http://localhost:3001` instead.
156
155
 
157
156
  ---
158
157
 
159
- ## Setup Guides
158
+ ## 📖 Setup Guides
160
159
 
161
160
  <details>
162
161
  <summary><strong>Claude Desktop</strong></summary>
@@ -379,7 +378,7 @@ Prism can be deployed natively to cloud platforms like [Render](https://render.c
379
378
 
380
379
  ---
381
380
 
382
- ## Universal Import: Bring Your History
381
+ ## 📥 Universal Import: Bring Your History
383
382
 
384
383
  Switching to Prism? Don't leave months of AI session history behind. Prism can **ingest historical sessions from Claude Code, Gemini, and OpenAI** and give your Mind Palace an instant head start — no manual re-entry required.
385
384
 
@@ -412,7 +411,7 @@ npx -y prism-mcp-server universal-import --format gemini --path ./gemini_history
412
411
 
413
412
  ---
414
413
 
415
- ## What Makes Prism Different
414
+ ## What Makes Prism Different
416
415
 
417
416
 
418
417
  ### 🧠 Your Agent Learns From Mistakes
@@ -455,59 +454,46 @@ OpenTelemetry spans for every MCP tool call, LLM hop, and background worker. Rou
455
454
  ### 🌐 Autonomous Web Scholar
456
455
  Prism researches while you sleep. A background pipeline searches the web, scrapes articles, synthesizes findings via LLM, and injects results directly into your semantic memory — fully searchable on your next session. Brave Search → Firecrawl scrape → LLM synthesis → Prism ledger. Task-aware, Hivemind-integrated, and zero-config when API keys are missing (falls back to Yahoo + Readability).
457
456
 
458
- ### Dark Factory — Adversarial Autonomous Pipelines
457
+ ### 🏭 Dark Factory — Adversarial Autonomous Pipelines
459
458
  When you trigger a Dark Factory pipeline, Prism doesn't just run your task — it fights itself to produce high-quality output. A `PLAN_CONTRACT` step locks a machine-parseable rubric before any code is written. After execution, an **Adversarial Evaluator** (in a fully isolated context) scores the output against the rubric. It cannot pass the Generator without providing exact file and line evidence for every failing criterion. Failed evaluations inject the critique directly into the Generator's retry prompt so it's never flying blind. The result: security issues, regressions, and lazy debug logs caught autonomously — before you ever see the PR. → [See it in action](examples/adversarial-eval-demo/README.md)
460
459
 
461
460
  ---
462
461
 
463
- ## Synapse Engine (v8.0)
462
+ ## 🤖 Autonomous Cognitive OS (v9.0)
464
463
 
465
- > *Standard RAG retrieves documents. GraphRAG traverses relationships. The Synapse Engine does both a pure, storage-agnostic multi-hop propagation engine that turns your agent's memory into an associative reasoning network.*
464
+ > *Memory isn't just about storing data; it's about economics and emotion. Prism v9.0 transforms passive memory into a living Cognitive Operating System that forces agents to learn compression and develop intuition.*
466
465
 
467
- The Synapse Engine (v8.0) replaces the legacy SQL-coupled spreading activation with a **pure functional graph propagation core** inspired by ACT-R cognitive architecture. It is Prism's native, low-latency GraphRAG solution no external graph database required.
466
+ Most AI agents have an infinite memory budget. They dump massive, repetitive logs into vector databases until they bankrupt your API budget and choke their own context windows. Prism v9.0 fixes this by introducing **Token-Economic Reinforcement Learning** and **Affect-Tagged Memory**.
468
467
 
469
- ### Before vs After
468
+ ### 💰 Memory-as-an-Economy (The Surprisal Gate)
469
+ Prism assigns every project a strict **Cognitive Budget** (e.g., 2,000 tokens) that persists across sessions. Every time the agent saves a memory, it costs tokens.
470
470
 
471
- | | **v7.x (Standard RAG)** | **v8.0 (Synapse Engine)** |
472
- |---|---|---|
473
- | **Query** | "Tell me about Project Apollo" | "Tell me about Project Apollo" |
474
- | **Retrieval** | Returns the design doc (1 hop, cosine match) | Returns the design doc follows `caused_by` edge to a developer's debugging session → discovers an old Slack thread about a critical auth bug |
475
- | **Agent output** | Summarizes the design doc | Summarizes the design doc **and warns about the unresolved auth issue** |
476
- | **Discovery tag** | — | `[🌐 Synapse]` marks the auth bug node, proving the engine found context the user didn't ask for |
471
+ But not all memories are priced equally. Prism intercepts the save and runs a **Vector-Based Surprisal** calculation against recent memories:
472
+ * **High Surprisal (Novel thought):** Costs 0.5× tokens. The agent is rewarded for new insights.
473
+ * **Low Surprisal (Boilerplate):** Costs 2.0× tokens. The agent is penalized for repeating itself.
474
+ * **Universal Basic Income (UBI):** The budget recovers passively over time (+100 tokens/hour).
477
475
 
478
- ### How It Works
476
+ If an agent is too verbose, it goes into **Cognitive Debt**. You don't need to prompt the agent to "be concise." The physics of the system force the LLM to learn data compression to avoid bankruptcy.
479
477
 
480
- ```
481
- Query: "Project Apollo status"
482
-
483
- ┌───────────┼───────────────┐
484
- ▼ ▼ ▼
485
- [Design [Sprint [Deployment
486
- Doc] Retro] Log]
487
- │ 1.0 │ 0.8 │ 0.6 ← semantic anchors
488
- │ │ │
489
- ▼ ▼ ▼
490
- [Dev [Auth Bug [Perf ← Synapse discovered
491
- Profile Thread 🌐] Regression 🌐] (multi-hop)
492
- 0.42] 0.38] 0.31]
493
- ```
478
+ ### 🎭 Affect-Tagged Memory (Giving AI a "Gut Feeling")
479
+ Vector math measures *semantic similarity*, not *sentiment*. If an agent searches for "Authentication Architecture," standard RAG will return two past approaches—it doesn't know that Approach A caused a 3-day production outage, while Approach B worked perfectly.
494
480
 
495
- **Key design decisions:**
481
+ * **Affective Salience:** Prism automatically tags experience events with a `valence` score (-1.0 for failure, +1.0 for success).
482
+ * **Emotional Retrieval:** At retrieval time, the absolute magnitude (`|valence|`) significantly boosts the memory's ranking score. Extreme failures and extreme successes surface to the top.
483
+ * **UX Warnings:** If an agent retrieves memories that are historically negative, Prism intercepts the prompt injection: `⚠️ Caution: This topic is strongly correlated with historical failures. Review past decisions before proceeding.` Your AI now has a "gut feeling" about bad code.
496
484
 
497
- | Mechanism | Purpose |
498
- |---|---|
499
- | **Dampened Fan Effect** (`1/ln(degree+e)`) | Prevents hub nodes from flooding results |
500
- | **Asymmetric Propagation** (fwd 100%, back 50%) | Preserves causal directionality |
501
- | **Cyclic Loop Prevention** (`visitedEdges` set) | Prevents infinite energy amplification |
502
- | **Sigmoid Normalization** | Structural scores can't overwhelm semantic base |
503
- | **Lateral Inhibition** | Caps output to top-K most energized nodes |
504
- | **Hybrid Scoring** (70% semantic / 30% structural) | Base relevance always matters |
485
+ ### The Paradigm Shift
505
486
 
506
- > 💡 Synapse is **non-fatal** if the graph traversal fails for any reason, search gracefully returns the original semantic matches. Zero risk of degraded search.
487
+ | Feature | Standard RAG / Agents | Prism v9.0 |
488
+ | :--- | :--- | :--- |
489
+ | **Storage Limit** | Infinite (bloats context) | Bounded Token Economy |
490
+ | **Data Quality** | Saves repetitive boilerplate | Surprisal Gate penalizes redundancy |
491
+ | **Sentiment** | Treats all data as neutral facts | Affect-Tagged (Warns agent of past trauma) |
492
+ | **Recovery** | Manual deletion | Universal Basic Income (UBI) over time |
507
493
 
508
494
  ---
509
495
 
510
- ## Cognitive Architecture (v7.8)
496
+ ## 🧠 Cognitive Architecture (v7.8)
511
497
 
512
498
  > *Prism v7.8 is our biggest leap forward yet. We have moved beyond flat vector search and implemented a true Cognitive Architecture inspired by human brain mechanics. With the new ACT-R Spreading Activation Engine, Episodic-to-Semantic memory consolidation, and Uncertainty-Aware Rejection Gates, Prism doesn't just store logs anymore — it forms principles, follows causal trains of thought, and possesses the self-awareness to know when it lacks information.*
513
499
 
@@ -572,7 +558,7 @@ Standard RAG (Retrieval-Augmented Generation) is now a commodity. Everyone has v
572
558
 
573
559
  ---
574
560
 
575
- ## Data Privacy & Egress
561
+ ## 🔒 Data Privacy & Egress
576
562
 
577
563
  **Where is my data stored?**
578
564
 
@@ -603,7 +589,7 @@ Prism will recreate the directory with empty databases on next startup.
603
589
 
604
590
  ---
605
591
 
606
- ## Use Cases
592
+ ## 🎯 Use Cases
607
593
 
608
594
  - **Long-running feature work** — Save state at end of day, restore full context next morning. No re-explaining.
609
595
  - **Multi-agent collaboration** — Dev, QA, and PM agents share real-time context without stepping on each other's memory.
@@ -632,7 +618,7 @@ Then continue a specific thread with a follow-up message to the selected agent,
632
618
 
633
619
  ---
634
620
 
635
- ## Adversarial Evaluation in Action
621
+ ## ⚔️ Adversarial Evaluation in Action
636
622
 
637
623
  > **Split-Brain Anti-Sycophancy** — the signature feature of v7.4.0.
638
624
 
@@ -737,12 +723,11 @@ The Generator strips the `console.log`, resubmits, and the next `EVALUATE` retur
737
723
 
738
724
  ---
739
725
 
740
- ## What's New
726
+ ## 🆕 What's New
741
727
 
742
- > **Current release: v8.0.0Synapse Engine**
728
+ > **Current release: v7.8.2Cognitive Architecture**
743
729
 
744
- - **v8.0.0 — Synapse Engine:** Pure, storage-agnostic multi-hop graph propagation engine replaces the legacy SQL-coupled spreading activation. O(T × M) bounded ACT-R energy propagation with dampened fan effect, asymmetric bidirectional flow, cyclic loop prevention, and sigmoid normalization. Full integration into both SQLite and Supabase backends. 5 new config knobs. Battle-hardened with NaN guards, config clamping, non-fatal enrichment, and 16 passing tests. **Memory search now follows the causal graph, not just keywords.** → [Synapse Engine](#synapse-engine-v80)
745
- - 🧠 **v7.8.x — Cognitive Architecture:** Episodic-to-Semantic consolidation (Hebbian learning), ACT-R Spreading Activation with multi-hop causal reasoning, Uncertainty-Aware Rejection Gate, and Dynamic Fast Weight Decay. Validated by **LoCoMo-Plus benchmark**. → [Cognitive Architecture](#cognitive-architecture-v78)
730
+ - 🧠 **v7.8.0 — Cognitive Architecture:** The biggest leap forward yet. Moved beyond flat vector search into a true cognitive architecture inspired by human brain mechanics. Episodic-to-Semantic memory consolidation (Hebbian learning), ACT-R Spreading Activation with multi-hop causal reasoning, Uncertainty-Aware Rejection Gate (your agent can say "I don't know"), and Dynamic Fast Weight Decay (semantic memories outlive episodic chatter by 2×). **Your agents don't just remember; they learn.** → [Cognitive Architecture](#-cognitive-architecture-v78)
746
731
  - 🌐 **v7.7.0 — Cloud-Native SSE Transport:** Full unauthenticated and authenticated Server-Sent Events MCP support for seamless network deployments.
747
732
  - 🩺 **v7.5.0 — Intent Health Dashboard + Security Hardening:** Real-time 0–100 project health scoring (staleness × TODO load × decisions). 10 XSS injection vectors patched. Algorithm hardened with NaN guards and score ceiling.
748
733
  - ⚔️ **v7.4.0 — Adversarial Evaluation:** Split-brain anti-sycophancy pipeline. Generator and evaluator in isolated roles with evidence-bound findings.
@@ -752,7 +737,7 @@ The Generator strips the `console.log`, resubmits, and the next `EVALUATE` retur
752
737
 
753
738
  ---
754
739
 
755
- ## How Prism Compares
740
+ ## ⚔️ How Prism Compares
756
741
 
757
742
  Standard memory servers (like Mem0, Zep, or the baseline Anthropic MCP) act as passive filing cabinets — they wait for the LLM to search them. **Prism is an active cognitive architecture.** Designed specifically for the **Model Context Protocol (MCP)**, Prism doesn't just store vectors — it consolidates experience into principles, traverses causal graphs for multi-hop reasoning, and rejects queries it can't confidently answer.
758
743
 
@@ -804,7 +789,7 @@ Every other AI coding pipeline has a fatal flaw: it asks the same model that wro
804
789
 
805
790
  ---
806
791
 
807
- ## Tool Reference
792
+ ## 🔧 Tool Reference
808
793
 
809
794
  Prism ships 30+ tools, but **90% of your workflow uses just three:**
810
795
 
@@ -986,11 +971,6 @@ Requires `PRISM_DARK_FACTORY_ENABLED=true`.
986
971
  | `PRISM_ACTR_WEIGHT_ACTIVATION` | No | Composite score ACT-R activation weight (default: `0.3`) |
987
972
  | `PRISM_ACTR_ACCESS_LOG_RETENTION_DAYS` | No | Days before access logs are pruned by background scheduler (default: `90`) |
988
973
  | `PRISM_DARK_FACTORY_ENABLED` | No | `"true"` to enable Dark Factory autonomous pipeline tools (`session_start_pipeline`, `session_check_pipeline_status`, `session_abort_pipeline`) |
989
- | `PRISM_SYNAPSE_ENABLED` | No | `"true"` (default) to enable Synapse Engine graph propagation in search results |
990
- | `PRISM_SYNAPSE_ITERATIONS` | No | Propagation iterations (default: `3`). Higher = deeper graph traversal |
991
- | `PRISM_SYNAPSE_SPREAD_FACTOR` | No | Energy decay multiplier per hop (default: `0.8`). Range: 0.0–1.0 |
992
- | `PRISM_SYNAPSE_LATERAL_INHIBITION` | No | Max nodes returned by Synapse (default: `7`, min: `1`) |
993
- | `PRISM_SYNAPSE_SOFT_CAP` | No | Max candidate pool size during propagation (default: `20`, min: `1`) |
994
974
 
995
975
  </details>
996
976
 
@@ -1018,11 +998,10 @@ Prism is a **stdio-based MCP server** that manages persistent agent memory. Here
1018
998
  │ └──────┬───────┘ └──────────────┘ └────────────────┘ │
1019
999
  │ ↕ │
1020
1000
  │ ┌────────────────────────────────────────────────────┐ │
1021
- │ │ Cognitive Engine (v8.0) │ │
1022
- │ │ • Synapse Engine (pure multi-hop propagation) │ │
1001
+ │ │ Cognitive Engine (v7.8) │ │
1002
+ │ │ • ACT-R Spreading Activation (multi-hop) │ │
1023
1003
  │ │ • Episodic → Semantic Consolidation (Hebbian) │ │
1024
1004
  │ │ • Uncertainty-Aware Rejection Gate │ │
1025
- │ │ • LoCoMo-Plus Benchmark Validation │ │
1026
1005
  │ │ • Dynamic Fast Weight Decay (dual-rate) │ │
1027
1006
  │ │ • HDC Cognitive Routing (XOR binding) │ │
1028
1007
  │ └──────┬─────────────────────────────────────────────┘ │
@@ -1066,7 +1045,7 @@ Prism is a **stdio-based MCP server** that manages persistent agent memory. Here
1066
1045
 
1067
1046
  ### Auto-Load Architecture
1068
1047
 
1069
- Each MCP client has its own mechanism for ensuring Prism context loads on session start. See the platform-specific [Setup Guides](#setup-guides) above for detailed instructions:
1048
+ Each MCP client has its own mechanism for ensuring Prism context loads on session start. See the platform-specific [Setup Guides](#-setup-guides) above for detailed instructions:
1070
1049
 
1071
1050
  - **Claude Code** — Lifecycle hooks (`SessionStart` / `Stop`)
1072
1051
  - **Gemini / Antigravity** — Three-layer architecture (User Rules + AGENTS.md + Startup Skill)
@@ -1077,7 +1056,7 @@ All platforms benefit from the **server-side fallback** (v5.2.1): if `session_lo
1077
1056
 
1078
1057
  ---
1079
1058
 
1080
- ## Scientific Foundation
1059
+ ## 🧬 Scientific Foundation
1081
1060
 
1082
1061
  Prism has evolved from smart session logging into a **cognitive memory architecture** — grounded in real research, not marketing. Every retrieval decision is backed by peer-reviewed models from cognitive psychology, neuroscience, and distributed computing.
1083
1062
 
@@ -1111,22 +1090,19 @@ Prism has evolved from smart session logging into a **cognitive memory architect
1111
1090
  | **v7.8** | Multi-Hop Causal Reasoning — spreading activation traverses `caused_by`/`led_to` edges with damped fan effect (`1/ln(fan+e)`) and lateral inhibition | ACT-R spreading activation (Anderson), Collins & Loftus (1975) | ✅ Shipped |
1112
1091
  | **v7.8** | Uncertainty-Aware Rejection Gate — dual-signal (similarity floor + gap distance) safety layer prevents hallucination from low-confidence retrievals | Metacognition research, uncertainty quantification | ✅ Shipped |
1113
1092
  | **v7.8** | Dynamic Fast Weight Decay — `is_rollup` semantic nodes decay 50% slower (`ageModifier = 0.5`) than episodic entries, creating Long-Term Context anchors | ACT-R base-level activation with differential decay rates | ✅ Shipped |
1114
- | **v7.8** | LoCoMo Benchmark Harness — deterministic integration suite (`tests/benchmarks/locomo.ts`, 20 assertions) benchmarking multi-hop compaction structures via `MockLLM` | Long-Context Memory evaluation (cognitive benchmarking) | ✅ Shipped |
1115
- | **v7.8** | LoCoMo-Plus Benchmark — 16-assertion suite (`tests/benchmarks/locomo-plus.ts`) adapted from arXiv 2602.10715 validating cue–trigger semantic disconnect bridging via graph traversal and Hebbian consolidation; reports Precision@1/3/5/10 and MRR | LoCoMo-Plus (Li et al., ARR 2026), cue–trigger disconnect research | ✅ Shipped |
1116
1093
  | **v7.x** | Affect-Tagged Memory — sentiment shapes what gets recalled | Affect-modulated retrieval (neuroscience) | 🔭 Horizon |
1117
1094
  | **v8+** | Zero-Search Retrieval — no index, no ANN, just ask the vector | Holographic Reduced Representations | 🔭 Horizon |
1118
1095
 
1119
- > Informed by Anderson's ACT-R (Adaptive Control of Thought—Rational), Collins & Loftus spreading activation networks (1975), Kanerva's SDM (1988), Hebb's learning rule, Li et al. LoCoMo-Plus (ARR 2026), and LeCun's "Why AI Systems Don't Learn" (Dupoux, LeCun, Malik).
1096
+ > Informed by Anderson's ACT-R (Adaptive Control of Thought—Rational), Collins & Loftus spreading activation networks (1975), Kanerva's SDM (1988), Hebb's learning rule, and LeCun's "Why AI Systems Don't Learn" (Dupoux, LeCun, Malik).
1120
1097
 
1121
1098
  ---
1122
1099
 
1123
- ## Milestones & Roadmap
1100
+ ## 📦 Milestones & Roadmap
1124
1101
 
1125
- > **Current: v8.0.0** — Synapse Engine ([CHANGELOG](CHANGELOG.md))
1102
+ > **Current: v7.8.2** — Cognitive Architecture ([CHANGELOG](CHANGELOG.md))
1126
1103
 
1127
1104
  | Release | Headline |
1128
1105
  |---------|----------|
1129
- | **v8.0** | ⚡ Synapse Engine — Pure multi-hop GraphRAG propagation, storage-agnostic, NaN-hardened, `[🌐 Synapse]` discovery tags |
1130
1106
  | **v7.8** | 🧠 Cognitive Architecture — Hebbian consolidation, multi-hop reasoning, rejection gate, dynamic decay |
1131
1107
  | **v7.7** | 🌐 Cloud-Native SSE Transport |
1132
1108
  | **v7.5** | 🩺 Intent Health Dashboard + Security Hardening |
@@ -1145,7 +1121,7 @@ Prism has evolved from smart session logging into a **cognitive memory architect
1145
1121
  👉 **[Full ROADMAP.md →](ROADMAP.md)**
1146
1122
 
1147
1123
 
1148
- ## Troubleshooting FAQ
1124
+ ## Troubleshooting FAQ
1149
1125
 
1150
1126
  **Q: Why is the dashboard project selector stuck on "Loading projects..."?**
1151
1127
  A: Fixed in v7.3.3. The root cause was a multi-layer quote-escaping trap in the `abortPipeline` onclick handler that generated a `SyntaxError` in the browser, silently killing the entire dashboard IIFE. Update to v7.3.3+ (`npx -y prism-mcp-server`). If still stuck, check that Supabase env values are properly set (unresolved placeholders like `${SUPABASE_URL}` cause `/api/projects` to return empty). Prism auto-falls back to local SQLite when Supabase is misconfigured.
package/dist/cli.js CHANGED
File without changes
package/dist/config.js CHANGED
@@ -268,17 +268,3 @@ export const PRISM_DARK_FACTORY_ENABLED = process.env.PRISM_DARK_FACTORY_ENABLED
268
268
  export const PRISM_DARK_FACTORY_POLL_MS = parseInt(process.env.PRISM_DARK_FACTORY_POLL_MS || "30000", 10);
269
269
  /** Default max wall-clock time per pipeline (ms). Default: 15 minutes. */
270
270
  export const PRISM_DARK_FACTORY_MAX_RUNTIME_MS = parseInt(process.env.PRISM_DARK_FACTORY_MAX_RUNTIME_MS || "900000", 10);
271
- // ─── v8.0: Synapse — Spreading Activation Engine ──────────────
272
- // Multi-hop energy propagation through memory_links graph.
273
- // Enabled by default. Set PRISM_SYNAPSE_ENABLED=false to fall back
274
- // to 1-hop candidateScopedSpreadingActivation (v7.0 behavior).
275
- /** Master switch for the Synapse engine. Enabled by default (opt-out). */
276
- export const PRISM_SYNAPSE_ENABLED = (process.env.PRISM_SYNAPSE_ENABLED ?? "true") !== "false";
277
- /** Number of propagation iterations (depth). Higher = deeper traversal, more latency. (Default: 3) */
278
- export const PRISM_SYNAPSE_ITERATIONS = parseInt(process.env.PRISM_SYNAPSE_ITERATIONS || "3", 10);
279
- /** Energy attenuation per hop. Must be < 1.0 for convergence. (Default: 0.8) */
280
- export const PRISM_SYNAPSE_SPREAD_FACTOR = parseFloat(process.env.PRISM_SYNAPSE_SPREAD_FACTOR || "0.8");
281
- /** Hard cap on final output nodes (lateral inhibition). (Default: 7) */
282
- export const PRISM_SYNAPSE_LATERAL_INHIBITION = parseInt(process.env.PRISM_SYNAPSE_LATERAL_INHIBITION || "7", 10);
283
- /** Soft cap on active nodes per iteration (prevents explosion). (Default: 20) */
284
- export const PRISM_SYNAPSE_SOFT_CAP = parseInt(process.env.PRISM_SYNAPSE_SOFT_CAP || "20", 10);
@@ -599,13 +599,6 @@ async function runnerTick() {
599
599
  const harnessPath = path.join(path.resolve(spec.workingDirectory), 'verification_harness.json');
600
600
  if (fs.existsSync(harnessPath)) {
601
601
  try {
602
- // MED-5 FIX: Guard against LLM-generated files that are malformed or
603
- // excessively large. Cap at 1MB to prevent heap exhaustion.
604
- const MAX_HARNESS_SIZE = 1_000_000;
605
- const stat = fs.statSync(harnessPath);
606
- if (stat.size > MAX_HARNESS_SIZE) {
607
- throw new Error(`Verification harness too large (${stat.size} bytes, max ${MAX_HARNESS_SIZE}). Possible corrupt LLM output.`);
608
- }
609
602
  const rawHarness = fs.readFileSync(harnessPath, 'utf8');
610
603
  const harnessData = JSON.parse(rawHarness);
611
604
  // GAP-5 fix: Persist the harness so CLI drift detection works for DarkFactory runs
@@ -45,16 +45,8 @@ export class SafetyController {
45
45
  if (!spec.workingDirectory)
46
46
  return true;
47
47
  // Resolve symlinks and protect against ../ escapes.
48
- let resolvedTarget = path.resolve(targetPath);
49
- let resolvedWorkspace = path.resolve(spec.workingDirectory);
50
- // EDGE-2 FIX: macOS (HFS+/APFS) and Windows (NTFS) use case-insensitive
51
- // filesystems by default. Without case normalization, "/App/Workspace"
52
- // and "/app/workspace" are treated as different paths — allowing a scope
53
- // escape via case mismatch.
54
- if (process.platform === 'darwin' || process.platform === 'win32') {
55
- resolvedTarget = resolvedTarget.toLowerCase();
56
- resolvedWorkspace = resolvedWorkspace.toLowerCase();
57
- }
48
+ const resolvedTarget = path.resolve(targetPath);
49
+ const resolvedWorkspace = path.resolve(spec.workingDirectory);
58
50
  // Path Traversal Guard: A naive startsWith() check is vulnerable to
59
51
  // prefix collisions — e.g. /app/workspace-hacked passes startsWith('/app/workspace').
60
52
  // We require EITHER exact match OR the target starts with workspace + path separator.
@@ -2,6 +2,7 @@
2
2
  * Dashboard authentication helpers extracted from server.ts for direct unit testing.
3
3
  */
4
4
  import { randomBytes } from "crypto";
5
+ import { createRemoteJWKSet, jwtVerify } from "jose";
5
6
  // ─────────────────────────────────────────────────────────────────
6
7
  // TIMING-SAFE COMPARISON
7
8
  // ─────────────────────────────────────────────────────────────────
@@ -34,6 +35,18 @@ export function generateToken() {
34
35
  return randomBytes(32).toString("hex");
35
36
  }
36
37
  // ─────────────────────────────────────────────────────────────────
38
+ // JWKS SETUP
39
+ // ─────────────────────────────────────────────────────────────────
40
+ let jwksCache = null;
41
+ export function initJWKS(uri) {
42
+ try {
43
+ jwksCache = createRemoteJWKSet(new URL(uri));
44
+ }
45
+ catch (err) {
46
+ console.error(`Failed to initialize JWKS from ${uri}:`, err);
47
+ }
48
+ }
49
+ // ─────────────────────────────────────────────────────────────────
37
50
  // AUTHENTICATION CHECK
38
51
  // ─────────────────────────────────────────────────────────────────
39
52
  /**
@@ -47,9 +60,25 @@ export function generateToken() {
47
60
  * Side effect: expired session tokens are lazily cleaned up when
48
61
  * encountered, preventing unbounded memory growth.
49
62
  */
50
- export function isAuthenticated(req, config) {
63
+ export async function isAuthenticated(req, config) {
51
64
  if (!config.authEnabled)
52
65
  return true;
66
+ // Check Bearer Token (JWKS)
67
+ const authHeader = req.headers.authorization || "";
68
+ if (authHeader.startsWith("Bearer ") && jwksCache) {
69
+ try {
70
+ const token = authHeader.slice(7);
71
+ const { payload } = await jwtVerify(token, jwksCache);
72
+ // Attach agent_id to the request for traceability if needed downstream
73
+ req.agent_id = payload.agent_id || payload.sub;
74
+ return true;
75
+ }
76
+ catch (err) {
77
+ // Verification failed — let it fall through or return false
78
+ // console.error("JWT verification failed:", err);
79
+ return false;
80
+ }
81
+ }
53
82
  // Check session cookie first
54
83
  const cookies = req.headers.cookie || "";
55
84
  const match = cookies.match(/prism_session=([a-f0-9]{64})/);
@@ -63,7 +92,6 @@ export function isAuthenticated(req, config) {
63
92
  config.activeSessions.delete(token);
64
93
  }
65
94
  // Check Basic Auth header
66
- const authHeader = req.headers.authorization || "";
67
95
  if (authHeader.startsWith("Basic ")) {
68
96
  try {
69
97
  const decoded = Buffer.from(authHeader.slice(6), "base64").toString("utf-8");
@@ -32,7 +32,7 @@ import { getLLMProvider } from "../utils/llm/factory.js";
32
32
  import { buildVaultDirectory } from "../utils/vaultExporter.js";
33
33
  import { redactSettings } from "../tools/commonHelpers.js";
34
34
  import { handleGraphRoutes } from "./graphRouter.js";
35
- import { safeCompare, generateToken, isAuthenticated, createRateLimiter, } from "./authUtils.js";
35
+ import { safeCompare, generateToken, isAuthenticated, createRateLimiter, initJWKS, } from "./authUtils.js";
36
36
  const PORT = parseInt(process.env.PRISM_DASHBOARD_PORT || "3000", 10);
37
37
  /** Read HTTP request body as string (Buffer-based to avoid GC thrash on large imports) */
38
38
  function readBody(req) {
@@ -76,7 +76,12 @@ export async function startDashboardServer() {
76
76
  */
77
77
  const AUTH_USER = process.env.PRISM_DASHBOARD_USER || "";
78
78
  const AUTH_PASS = process.env.PRISM_DASHBOARD_PASS || "";
79
- const AUTH_ENABLED = AUTH_USER.length > 0 && AUTH_PASS.length > 0;
79
+ const AUTH_JWKS_URI = process.env.PRISM_JWKS_URI || process.env.AUTH_JWKS_URI || "";
80
+ // Auth is enabled if basic auth is configured OR if JWKS is configured
81
+ const AUTH_ENABLED = (AUTH_USER.length > 0 && AUTH_PASS.length > 0) || AUTH_JWKS_URI.length > 0;
82
+ if (AUTH_JWKS_URI) {
83
+ initJWKS(AUTH_JWKS_URI);
84
+ }
80
85
  const SESSION_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
81
86
  const activeSessions = new Map(); // token → expiry timestamp
82
87
  // Auth config object — injectable for testing via authUtils.ts
@@ -301,10 +306,11 @@ return false;}
301
306
  return res.end(JSON.stringify({ error: err.message || "Failed to generate server card" }));
302
307
  }
303
308
  }
304
- // ─── v5.1: Auth gate — block unauthenticated requests ───
305
- if (AUTH_ENABLED && !isAuthenticated(req, authConfig)) {
306
- // For API calls, return 401 JSON
307
- if (reqUrl.pathname.startsWith("/api/") || reqUrl.pathname === "/sse" || reqUrl.pathname === "/messages") {
309
+ // ─── AUTHENTICATION GATE ───
310
+ // Basic Auth & Session & JWKS
311
+ if (AUTH_ENABLED && !(await isAuthenticated(req, authConfig))) {
312
+ // If it's an API request, return 401 JSON
313
+ if (reqUrl.pathname.startsWith('/api/') || reqUrl.pathname === "/sse" || reqUrl.pathname === "/messages") {
308
314
  res.writeHead(401, { "Content-Type": "application/json" });
309
315
  return res.end(JSON.stringify({ error: "Authentication required" }));
310
316
  }
@@ -445,7 +451,6 @@ return false;}
445
451
  let cursorId = undefined;
446
452
  let iterations = 0;
447
453
  const MAX_ITERATIONS = 100; // safety cap: 100 × 50 = 5000 entries max
448
- let lastBackfillError = undefined;
449
454
  while (hasMore && iterations < MAX_ITERATIONS) {
450
455
  iterations++;
451
456
  const result = await backfillEmbeddingsHandler({ dry_run: false, limit: 50, _cursor_id: cursorId });
@@ -453,8 +458,6 @@ return false;}
453
458
  if (bStats) {
454
459
  repairedCount += bStats.repaired;
455
460
  failedCount += bStats.failed;
456
- if (bStats.error)
457
- lastBackfillError = bStats.error;
458
461
  if (bStats.last_id)
459
462
  cursorId = bStats.last_id;
460
463
  else
@@ -467,10 +470,8 @@ return false;}
467
470
  }
468
471
  }
469
472
  cleanupMessages.push(`Repaired ${repairedCount} embeddings`);
470
- if (failedCount > 0) {
471
- const errMsg = lastBackfillError ? ` (${lastBackfillError})` : '';
472
- cleanupMessages.push(`Failed to repair ${failedCount} embeddings${errMsg}`);
473
- }
473
+ if (failedCount > 0)
474
+ cleanupMessages.push(`Failed to repair ${failedCount} embeddings`);
474
475
  }
475
476
  catch (err) {
476
477
  console.error("[Dashboard] Failed to backfill embeddings:", err);
@@ -1253,9 +1253,7 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
1253
1253
  onchange="onEmbeddingProviderChange(this.value)">
1254
1254
  <option value="auto">🔄 Auto (same as Text Provider)</option>
1255
1255
  <option value="gemini">🔵 Gemini</option>
1256
- <option value="openai">🟢 OpenAI</option>
1257
- <option value="voyage">🔮 Voyage AI</option>
1258
- <option value="ollama">🟠 Ollama (Local)</option>
1256
+ <option value="openai">🟢 OpenAI / Ollama</option>
1259
1257
  </select>
1260
1258
  </div>
1261
1259
 
@@ -1263,7 +1261,7 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
1263
1261
  <div id="anthropic-embed-warning" style="display:none;margin-top:0.5rem;padding:0.5rem 0.75rem;background:rgba(251,146,60,0.1);border:1px solid rgba(251,146,60,0.3);border-radius:6px;font-size:0.78rem;color:#fb923c;line-height:1.5">
1264
1262
  ⚠️ <strong>Anthropic has no native embedding API.</strong>
1265
1263
  Auto mode will route embeddings to <strong>Gemini</strong>.
1266
- Set Embedding Provider to <strong>Ollama (Local)</strong> for free local embeddings, or <strong>Voyage AI</strong> for the Anthropic-recommended cloud pairing.
1264
+ Set Embedding Provider to <strong>OpenAI / Ollama</strong> to use a local model (e.g. <code>nomic-embed-text</code>).
1267
1265
  </div>
1268
1266
 
1269
1267
  <!-- OpenAI embedding model field (shown when embedding_provider = openai) -->
@@ -1271,7 +1269,7 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
1271
1269
  <div class="setting-row">
1272
1270
  <div>
1273
1271
  <div class="setting-label">Embedding Model</div>
1274
- <div class="setting-desc">Must output 768 dims. Default: text-embedding-3-small</div>
1272
+ <div class="setting-desc">Must output 768 dims. Ollama: nomic-embed-text · OpenAI: text-embedding-3-small</div>
1275
1273
  </div>
1276
1274
  <input type="text" id="input-openai-embedding-model"
1277
1275
  placeholder="text-embedding-3-small"
@@ -1281,61 +1279,9 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
1281
1279
  </div>
1282
1280
  </div>
1283
1281
 
1284
- <!-- Voyage AI embedding fields (shown when embedding_provider = voyage) -->
1285
- <div id="embed-fields-voyage" style="display:none">
1286
- <div class="setting-row">
1287
- <div>
1288
- <div class="setting-label">Voyage API Key</div>
1289
- <div class="setting-desc">Get one free at <a href="https://dash.voyageai.com" target="_blank" style="color:var(--accent)">dash.voyageai.com</a></div>
1290
- </div>
1291
- <input type="password" id="input-voyage-api-key"
1292
- placeholder="pa-…"
1293
- 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: 180px;"
1294
- onchange="saveBootSetting('VOYAGE_API_KEY', this.value)"
1295
- oninput="clearTimeout(this._pv); var self=this; this._pv=setTimeout(function(){saveBootSetting('VOYAGE_API_KEY',self.value)},800)" />
1296
- </div>
1297
- <div class="setting-row">
1298
- <div>
1299
- <div class="setting-label">Voyage Model</div>
1300
- <div class="setting-desc">voyage-code-3 (code) · voyage-3 (general). Both MRL → 768 dims.</div>
1301
- </div>
1302
- <input type="text" id="input-voyage-model"
1303
- placeholder="voyage-code-3"
1304
- 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: 160px;"
1305
- onchange="saveBootSetting('voyage_model', this.value)"
1306
- oninput="clearTimeout(this._pvm); var self=this; this._pvm=setTimeout(function(){saveBootSetting('voyage_model',self.value)},800)" />
1307
- </div>
1308
- </div>
1309
-
1310
- <!-- Ollama embedding fields (shown when embedding_provider = ollama) -->
1311
- <div id="embed-fields-ollama" style="display:none">
1312
- <div class="setting-row">
1313
- <div>
1314
- <div class="setting-label">Ollama Base URL</div>
1315
- <div class="setting-desc">Where Ollama is running locally</div>
1316
- </div>
1317
- <input type="text" id="input-ollama-base-url"
1318
- placeholder="http://localhost:11434"
1319
- 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: 220px;"
1320
- onchange="saveBootSetting('ollama_base_url', this.value)"
1321
- oninput="clearTimeout(this._pou); var self=this; this._pou=setTimeout(function(){saveBootSetting('ollama_base_url',self.value)},800)" />
1322
- </div>
1323
- <div class="setting-row">
1324
- <div>
1325
- <div class="setting-label">Embedding Model</div>
1326
- <div class="setting-desc">Must output 768 dims. <code>nomic-embed-text</code> recommended.</div>
1327
- </div>
1328
- <input type="text" id="input-ollama-model"
1329
- placeholder="nomic-embed-text"
1330
- 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: 180px;"
1331
- onchange="saveBootSetting('ollama_model', this.value)"
1332
- oninput="clearTimeout(this._pom); var self=this; this._pom=setTimeout(function(){saveBootSetting('ollama_model',self.value)},800)" />
1333
- </div>
1334
- </div>
1335
-
1336
1282
  <div style="margin-top:1rem;padding:0.6rem 0.8rem;background:rgba(139,92,246,0.08);border:1px solid rgba(139,92,246,0.2);border-radius:6px;font-size:0.78rem;color:var(--text-secondary);line-height:1.5">
1337
- 💡 <strong>Zero-cost setup:</strong> Text Provider → <code>Anthropic</code>, Embedding Provider → <code>Ollama (Local)</code>.<br>
1338
- Use Claude for reasoning &amp; <code>nomic-embed-text</code> (free, local, 768-dim native) for embeddings.
1283
+ 💡 <strong>Cost-optimized setup:</strong> Text Provider → <code>Anthropic</code>, Embedding Provider → <code>OpenAI / Ollama</code>.<br>
1284
+ Use Claude 3.5 Sonnet for reasoning &amp; <code>nomic-embed-text</code> (free, local) for embeddings.
1339
1285
  </div>
1340
1286
 
1341
1287
  <span class="setting-saved" id="savedToastProviders">Saved ✓</span>
@@ -3189,10 +3135,8 @@ function onTextProviderChange(value) {
3189
3135
  // Called when the EMBEDDING provider dropdown changes.
3190
3136
  function onEmbeddingProviderChange(value) {
3191
3137
  var textVal = document.getElementById('select-text-provider').value;
3192
- // Show provider-specific fields based on the selected embedding provider
3138
+ // Show the OpenAI embedding model field only when embedding=openai
3193
3139
  document.getElementById('embed-fields-openai').style.display = value === 'openai' ? '' : 'none';
3194
- document.getElementById('embed-fields-voyage').style.display = value === 'voyage' ? '' : 'none';
3195
- document.getElementById('embed-fields-ollama').style.display = value === 'ollama' ? '' : 'none';
3196
3140
  refreshAnthropicWarning(textVal, value);
3197
3141
  saveBootSetting('embedding_provider', value);
3198
3142
  }
@@ -3230,17 +3174,7 @@ function loadAiProviderSettings() {
3230
3174
  if (embedSel)
3231
3175
  embedSel.value = embedProvider;
3232
3176
  document.getElementById('embed-fields-openai').style.display = embedProvider === 'openai' ? '' : 'none';
3233
- document.getElementById('embed-fields-voyage').style.display = embedProvider === 'voyage' ? '' : 'none';
3234
- document.getElementById('embed-fields-ollama').style.display = embedProvider === 'ollama' ? '' : 'none';
3235
3177
  refreshAnthropicWarning(textProvider, embedProvider);
3236
- var vKey = document.getElementById('input-voyage-api-key');
3237
- if (vKey) vKey.placeholder = s.VOYAGE_API_KEY ? '(key saved — paste to update)' : 'pa-…';
3238
- var vMod = document.getElementById('input-voyage-model');
3239
- if (vMod && s.voyage_model) vMod.value = s.voyage_model;
3240
- var olUrl = document.getElementById('input-ollama-base-url');
3241
- if (olUrl && s.ollama_base_url) olUrl.value = s.ollama_base_url;
3242
- var olMod = document.getElementById('input-ollama-model');
3243
- if (olMod && s.ollama_model) olMod.value = s.ollama_model;
3244
3178
  gKey = document.getElementById('input-google-api-key');
3245
3179
  if (gKey)
3246
3180
  gKey.placeholder = s.GOOGLE_API_KEY ? '(key saved — paste to update)' : 'AIza…';