prism-mcp-server 6.2.1 β†’ 6.5.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
@@ -3,7 +3,7 @@
3
3
  [![npm version](https://img.shields.io/npm/v/prism-mcp-server?color=cb0000&label=npm)](https://www.npmjs.com/package/prism-mcp-server)
4
4
  [![MCP Registry](https://img.shields.io/badge/MCP_Registry-listed-00ADD8?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0xMiAyTDIgN2wxMCA1IDEwLTUtMTAtNXpNMiAxN2wxMCA1IDEwLTV2LTJMMTI0djJMMiA5djh6Ii8+PC9zdmc+)](https://registry.modelcontextprotocol.io)
5
5
  [![Glama](https://img.shields.io/badge/Glama-listed-FF5601)](https://glama.ai/mcp/servers/dcostenco/prism-mcp)
6
- [![Smithery](https://img.shields.io/badge/Smithery-listed-6B4FBB)](https://smithery.ai/server/prism-mcp-server)
6
+ [![Smithery](https://img.shields.io/badge/Smithery-listed-6B4FBB)](https://smithery.ai)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
8
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
9
9
  [![Node.js](https://img.shields.io/badge/Node.js-18+-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
@@ -395,8 +395,18 @@ Soft/hard delete (Art. 17), full export in JSON, Markdown, or Obsidian vault `.z
395
395
 
396
396
  ## πŸ†• What's New
397
397
 
398
- ### v6.2 β€” The "Synthesize & Prune" Phase βœ…
399
- > **Current stable release (v6.2.1).** The Mind Palace becomes self-organizing.
398
+ ### v6.5 β€” HDC Cognitive Routing βœ…
399
+ > **Current stable release.** The Mind Palace gains a brain-inspired routing engine.
400
+
401
+ - 🧠 **Hyperdimensional Cognitive Routing** β€” New `session_cognitive_route` tool composes the agent's current state, role, and action into a single 768-dim binary hypervector via XOR binding, then resolves it to a semantic concept via Hamming distance. Three-outcome policy gateway: `direct` / `clarify` / `fallback`.
402
+ - πŸŽ›οΈ **Per-Project Threshold Overrides** β€” Fallback and clarify thresholds are configurable per-project and persisted via the existing `getSetting`/`setSetting` contract (no new migrations).
403
+ - πŸ”¬ **Explainability Mode** β€” When `explain: true`, responses include convergence steps, raw Hamming distance, and ambiguity flags for full auditability.
404
+ - πŸ“Š **Cognitive Observability** β€” `graphMetrics.ts` tracks route distribution (direct/clarify/fallback), rolling confidence/distance averages, ambiguity rates, and null-concept counts. Warning heuristics for fallback > 30% and ambiguity > 40%.
405
+ - πŸ–₯️ **Dashboard Integration** β€” Cognitive metrics card with route distribution bar, confidence gauges, and warning badges. On-demand "Cognitive Route" button in the Node Editor panel.
406
+ - πŸ”’ **Feature Gating** β€” Entire pipeline gated behind `PRISM_HDC_ENABLED` (default: `true`). Clean error + zero telemetry when disabled.
407
+
408
+ <details>
409
+ <summary><strong>v6.2 β€” The "Synthesize & Prune" Phase</strong></summary>
400
410
 
401
411
  - πŸ•ΈοΈ **Edge Synthesis ("The Dream Procedure")** β€” Automated background linker discovers semantically similar but disconnected memory nodes via cosine similarity (β‰₯ 0.7 threshold). Batch-limited to 50 sources Γ— 3 neighbors. New `session_synthesize_edges` tool for on-demand graph enrichment.
402
412
  - βœ‚οΈ **Graph Pruning (Soft-Prune)** β€” Configurable strength-based pruning soft-deletes weak links. Includes per-project cooldown, backpressure guards, and sweep budget controls. Enable with `PRISM_GRAPH_PRUNING_ENABLED=true`.
@@ -406,6 +416,8 @@ Soft/hard delete (Art. 17), full export in JSON, Markdown, or Obsidian vault `.z
406
416
  - ⚑ **Supabase Weak-Link RPC (WS4.1)** β€” New `prism_summarize_weak_links` Postgres function (migration 036) aggregates pruning server-side, eliminating N+1 network roundtrips.
407
417
  - πŸ”’ **Migration 035** β€” Tenant-safe graph writes + soft-delete hardening for MemoryLinks.
408
418
 
419
+ </details>
420
+
409
421
  <details>
410
422
  <summary><strong>v6.1 β€” Prism-Port, Cognitive Load & Semantic Search</strong></summary>
411
423
 
@@ -447,41 +459,49 @@ Soft/hard delete (Art. 17), full export in JSON, Markdown, or Obsidian vault `.z
447
459
 
448
460
  ## βš”οΈ How Prism Compares
449
461
 
450
- While standard Memory MCPs act as passive filing cabinets, Prism is an **active cognitive architecture** that manages its own health, compresses its own data, and learns autonomously in the background.
451
-
452
- | Feature | 🧠 **Prism MCP** | Official Anthropic Memory | Cloud APIs (Mem0 / Zep) | Simple SQLite/File MCPs |
453
- |:---|:---:|:---:|:---:|:---:|
454
- | **Paradigm** | **Active & Autonomous** | Passive Entity Graph | Passive Vector Store | Passive Log |
455
- | **Context Assembly** | **Progressive (Quick/Std/Deep)** | Manual JSON retrieval | Similarity Search only | Dump all / exact match |
456
- | **Graph Generation** | **Auto-Synthesized (Edges)** | Manual (LLM must write JSON) | Often none / black box | None |
457
- | **Context Window Mgmt** | **Auto-Compaction & Decay** | Endless unbounded growth | Requires paid API logic | Manual deletion required |
458
- | **Storage Engine** | **Local SQLite OR Supabase** | Local File | Cloud Only (Vendor lock-in) | Local SQLite |
459
- | **Vector Search** | **Three-Tier (Native / TQ / FTS5)** | ❌ None | βœ… Yes (Remote) | ❌ None |
460
- | **Vector Compression** | **TurboQuant (10Γ— smaller)** | ❌ N/A | ❌ Expensive/Opaque | ❌ N/A |
461
- | **Multi-Agent Sync** | **CRDTs + Hivemind Watchdog** | ❌ Single-agent only | βœ… Paid feature | ❌ Data collisions |
462
- | **Observability** | **OTel Traces + Web Dashboard** | ❌ None | βœ… Web Dashboard | ❌ None |
463
- | **Data Portability** | **Prism-Port (Obsidian Vault ZIP)** | ❌ Raw JSON | ❌ API Export | ❌ Raw DB file |
464
- | **Cost Model** | **Free + BYOM (Ollama)** | Free (limited) | Per-API-call pricing | Free (limited) |
465
-
466
- ### πŸ† Why Prism Wins: The "Big Three" Differentiators
467
-
468
- **1. Zero Cold-Starts with Progressive Loading & OCC**
469
- Other systems require the LLM to waste tokens and reasoning steps asking "What was I doing?" and calling tools to fetch memory. Prism uses MCP Resources to instantly inject the live project state into the context window *before* the LLM generates its first token. CRDT-backed Optimistic Concurrency Control ensures multiple agents (e.g., Claude + Cursor) can work on the same project simultaneously without data collisions.
470
-
471
- **2. Self-Cleaning & Self-Optimizing**
472
- If you use a standard memory tool long enough, it clogs the LLM's context window with thousands of obsolete tokens. Prism runs an autonomous Background Scheduler that:
473
- - **Ebbinghaus Decays** older, unreferenced memories β€” importance fades unless reinforced.
474
- - **Auto-Compacts** large session histories into dense, LLM-generated summaries.
475
- - **Deep Purges** high-precision vectors, replacing them with 400-byte TurboQuant compressed blobs, saving ~90% of disk space.
462
+ 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 manages the LLM's context window autonomously.
476
463
 
477
- > πŸ’° **Token Economics:** Progressive Context Loading (Quick ~50 tokens / Standard ~200 / Deep ~1000+) plus auto-compaction means you never blow your Claude/OpenAI token budget fetching 50 pages of raw chat history.
464
+ ### πŸ“Š Feature-by-Feature Comparison
478
465
 
479
- **3. The Associative Memory Graph**
480
- Prism doesn't just store logs; it connects them. When a session is saved, Prism automatically creates temporal chains (what happened next) and keyword overlap edges. In the background, Edge Synthesis actively scans for latent relationships and *synthesizes* new graph edges between conceptually similar but disconnected memories β€” turning passive storage into an active, self-organizing knowledge graph.
466
+ | Feature / Architecture | 🧠 Prism MCP | 🐘 Mem0 | ⚑ Zep | πŸ§ͺ Anthropic Base MCP |
467
+ | :--- | :--- | :--- | :--- | :--- |
468
+ | **Primary Interface** | **Native MCP** (Tools, Prompts, Resources) | REST API & Python/TS SDKs | REST API & Python/TS SDKs | Native MCP (Tools only) |
469
+ | **Storage Engine** | **BYO SQLite or Supabase** | Managed Cloud / VectorDBs | Managed Cloud / Postgres | Local SQLite only |
470
+ | **Context Assembly** | **Progressive (Quick/Std/Deep)** | Top-K Semantic Search | Top-K + Temporal Summaries | Basic Entity Search |
471
+ | **Memory Mechanics** | **Ebbinghaus Decay, SDM, HDC** | Basic Vector + Entity | Fading Temporal Graph | None (Infinite growth) |
472
+ | **Multi-Agent Sync** | **CRDT (Add-Wins / LWW)** | Cloud locks | Postgres locks | ❌ None (Data races) |
473
+ | **Data Compression** | **TurboQuant (7x smaller vectors)** | ❌ Standard F32 Vectors | ❌ Standard Vectors | ❌ No Vectors |
474
+ | **Observability** | **OTel Traces + Built-in PWA UI** | Cloud Dashboard | Cloud Dashboard | ❌ None |
475
+ | **Maintenance** | **Autonomous Background Scheduler** | Manual/API driven | Automated (Cloud) | ❌ Manual |
476
+ | **Data Portability** | **Prism-Port (Obsidian/Logseq Vault)** | JSON Export | JSON Export | Raw `.db` file |
477
+ | **Cost Model** | **Free + BYOM (Ollama)** | Per-API-call pricing | Per-API-call pricing | Free (limited) |
481
478
 
482
- > πŸ”Œ **BYOM (Bring Your Own Model):** While tools like Mem0 charge per API call, Prism's pluggable architecture lets you run `nomic-embed-text` locally via Ollama for **free vectors**, while using Claude or GPT for high-level reasoning. Zero vendor lock-in.
479
+ ### πŸ† Where Prism Crushes the Giants
480
+
481
+ #### 1. MCP-Native, Not an Adapted API
482
+ Mem0 and Zep are APIs that *can* be wrapped into an MCP server. Prism was built *for* MCP from day one. Instead of wasting tokens on "search" tool calls, Prism uses **MCP Prompts** (`/resume_session`) to inject context *before* the LLM thinks, and **MCP Resources** (`memory://project/handoff`) to attach live, subscribing context.
483
+
484
+ #### 2. Academic-Grade Cognitive Computer Science
485
+ The giants use standard RAG (Retrieval-Augmented Generation). Prism uses biological and academic models of memory: **TurboQuant** for extreme vector compression, **Ebbinghaus curves** for importance decay, and **Sparse Distributed Memory (SDM)**. This makes Prism vastly more efficient on a local machine than running a giant Postgres/pgvector instance.
486
+
487
+ #### 3. True Multi-Agent Coordination (CRDTs)
488
+ If Cursor (Agent A) and Claude Desktop (Agent B) try to update a Mem0 or standard SQLite database at the exact same time, you get a race condition and data loss. Prism uses **Optimistic Concurrency Control (OCC) with CRDT OR-Maps** β€” mathematically guaranteeing that simultaneous agent edits merge safely. Enterprise-grade distributed systems on a local machine.
489
+
490
+ #### 4. The PKM "Prism-Port" Export
491
+ AI memory is a black box. Developers hate black boxes. Prism exports memory directly into an **Obsidian/Logseq-compatible Markdown Vault** with YAML frontmatter and `[[Wikilinks]]`. Neither Mem0 nor Zep do this.
492
+
493
+ #### 5. Self-Cleaning & Self-Optimizing
494
+ If you use a standard memory tool long enough, it clogs the LLM's context window with thousands of obsolete tokens. Prism runs an autonomous [Background Scheduler](src/backgroundScheduler.ts) that Ebbinghaus-decays older memories, auto-compacts session histories into dense summaries, and deep-purges high-precision vectors β€” saving ~90% of disk space automatically.
495
+
496
+ ### 🀝 Where the Giants Currently Win (Honest Trade-offs)
497
+
498
+ 1. **Framework Integrations:** Mem0 and Zep have pre-built integrations for LangChain, LlamaIndex, Flowise, AutoGen, CrewAI, etc. Prism requires the host application to support the MCP protocol.
499
+ 2. **Managed Cloud Infrastructure:** The giants offer SaaS. Users pay $20/month and don't think about databases. Prism users must set up their own local SQLite or provision their own Supabase instance.
500
+ 3. **Implicit Memory Extraction (NER):** Zep automatically extracts names, places, and facts from raw chat logs using NLP models. Prism relies on the LLM explicitly calling the `session_save_ledger` tool to structure its own memories.
501
+
502
+ > πŸ’° **Token Economics:** Progressive Context Loading (Quick ~50 tokens / Standard ~200 / Deep ~1000+) plus auto-compaction means you never blow your Claude/OpenAI token budget fetching 50 pages of raw chat history.
483
503
  >
484
- > πŸ›οΈ **Prism-Port for PKM:** Prism turns your AI's brain into a readable [Obsidian](https://obsidian.md) / [Logseq](https://logseq.com) vault. Export with `session_export_memory(format='vault')` β€” complete with YAML frontmatter, `[[Wikilinks]]`, and keyword backlink indices. No more black-box AI memory.
504
+ > πŸ”Œ **BYOM (Bring Your Own Model):** While tools like Mem0 charge per API call, Prism's pluggable architecture lets you run `nomic-embed-text` locally via Ollama for **free vectors**, while using Claude or GPT for high-level reasoning. Zero vendor lock-in.
485
505
 
486
506
  ---
487
507
 
@@ -557,6 +577,17 @@ Prism ships 30+ tools, but **90% of your workflow uses just three:**
557
577
 
558
578
  </details>
559
579
 
580
+ <details>
581
+ <summary><strong>Cognitive Architecture (1 tool)</strong></summary>
582
+
583
+ Requires `PRISM_HDC_ENABLED=true` (default).
584
+
585
+ | Tool | Purpose |
586
+ |------|---------|
587
+ | `session_cognitive_route` | HDC compositional state resolution with policy-gated routing |
588
+
589
+ </details>
590
+
560
591
  <details>
561
592
  <summary><strong>Multi-Agent Hivemind (3 tools)</strong></summary>
562
593
 
@@ -600,6 +631,8 @@ Requires `PRISM_ENABLE_HIVEMIND=true`.
600
631
  | `PRISM_SCHOLAR_INTERVAL_MS` | No | Scholar interval in ms (default: `0` = manual only) |
601
632
  | `PRISM_SCHOLAR_TOPICS` | No | Comma-separated research topics (default: `"ai,agents"`) |
602
633
  | `PRISM_SCHOLAR_MAX_ARTICLES_PER_RUN` | No | Max articles per Scholar run (default: `3`) |
634
+ | `PRISM_HDC_ENABLED` | No | `"true"` (default) to enable HDC cognitive routing pipeline |
635
+ | `PRISM_HDC_EXPLAINABILITY_ENABLED` | No | `"true"` (default) to include convergence/distance/ambiguity in cognitive route responses |
603
636
 
604
637
  </details>
605
638
 
@@ -684,7 +717,8 @@ Prism is evolving from smart session logging toward a **cognitive memory archite
684
717
  | **v5.5** | SDM Decoder Foundation β€” pre-allocated typed-array hot loop, zero GC thrash | Kanerva's Sparse Distributed Memory (1988) | βœ… Shipped |
685
718
  | **v5.5** | Architectural Hardening β€” transactional migrations, graceful shutdown, thundering herd prevention | Production reliability engineering | βœ… Shipped |
686
719
  | **v6.1** | Intuitive Recall β€” proactive surface of relevant past decisions without explicit search; `session_intuitive_recall` tool | Predictive memory (cognitive science) | βœ… Shipped |
687
- | **v6.2+** | Full Superposed Memory (SDM) β€” O(1) key-value retrieval via Hamming correlation | Kanerva's SDM | πŸ”¬ In Progress |
720
+ | **v6.5** | HDC Cognitive Routing β€” compositional state-machine with XOR binding, Hamming resolution, and policy-gated routing | Hyperdimensional Computing (Kanerva, Gayler) | βœ… Shipped |
721
+ | **v6.5** | Cognitive Observability β€” route distribution, confidence/distance tracking, ambiguity warnings | Production reliability engineering | βœ… Shipped |
688
722
  | **v6.1** | Prism-Port Vault Export β€” Obsidian/Logseq `.zip` with YAML frontmatter & `[[Wikilinks]]` | Data sovereignty, PKM interop | βœ… Shipped |
689
723
  | **v6.1** | Cognitive Load & Semantic Search β€” dynamic graph thinning, search highlights | Contextual working memory | βœ… Shipped |
690
724
  | **v6.2** | Synthesize & Prune β€” automated edge synthesis, graph pruning, SLO observability | Implicit associative memory | βœ… Shipped |
@@ -702,8 +736,12 @@ Prism is evolving from smart session logging toward a **cognitive memory archite
702
736
  ### v6.2: The "Synthesize & Prune" Phase βœ…
703
737
  Shipped in v6.2.0. Edge synthesis, graph pruning with SLO observability, temporal decay heatmaps, active recall prompt generation, and full dashboard metrics integration.
704
738
 
705
- ### v7.x: Affect-Tagged Memory
706
- Sentiment and emotional valence shape what gets recalled β€” bringing affect-modulated retrieval from neuroscience into agentic memory.
739
+ ### v6.5: Cognitive Architecture (Primary)
740
+ Full Superposed Memory (SDM) + Hyperdimensional Computing (HDC/VSA) becomes the next major implementation phase, targeting compositional memory states and faster associative retrieval at scale.
741
+
742
+ ### After v6.5: Future Tracks
743
+ - **v7.x: Affect-Tagged Memory** β€” Recall prioritization improves by weighting memories with affective/contextual valence, making surfaced context more behaviorally useful.
744
+ - **v8+: Zero-Search Retrieval** β€” Direct vector-addressed recall (β€œjust ask the vector”) reduces retrieval indirection and moves Prism toward truly native associative memory.
707
745
 
708
746
  ---
709
747
 
package/dist/config.js CHANGED
@@ -175,6 +175,30 @@ export const PRISM_SCHOLAR_TOPICS = (process.env.PRISM_SCHOLAR_TOPICS || "ai,age
175
175
  // Controls the age threshold for link strength decay.
176
176
  // Links not traversed in the last N days lose 0.1 strength per sweep.
177
177
  export const PRISM_LINK_DECAY_DAYS = parseInt(process.env.PRISM_LINK_DECAY_DAYS || "30", 10);
178
+ // ─── v6.5: Cognitive Architecture (HDC Policy Gateway) ─────────────
179
+ // Master feature flag for HDC-driven cognitive routing APIs.
180
+ export const PRISM_HDC_ENABLED = process.env.PRISM_HDC_ENABLED === "true";
181
+ // Explainability payload toggle for cognitive routing responses.
182
+ export const PRISM_HDC_EXPLAINABILITY_ENABLED = process.env.PRISM_HDC_EXPLAINABILITY_ENABLED !== "false"; // default true
183
+ const DEFAULT_HDC_FALLBACK_THRESHOLD = 0.85;
184
+ const DEFAULT_HDC_CLARIFY_THRESHOLD = 0.95;
185
+ const rawHdcFallbackThreshold = parseFloat(process.env.PRISM_HDC_POLICY_FALLBACK_THRESHOLD || String(DEFAULT_HDC_FALLBACK_THRESHOLD));
186
+ const rawHdcClarifyThreshold = parseFloat(process.env.PRISM_HDC_POLICY_CLARIFY_THRESHOLD || String(DEFAULT_HDC_CLARIFY_THRESHOLD));
187
+ const hdcThresholdsValid = Number.isFinite(rawHdcFallbackThreshold) &&
188
+ Number.isFinite(rawHdcClarifyThreshold) &&
189
+ rawHdcFallbackThreshold >= 0 &&
190
+ rawHdcFallbackThreshold < rawHdcClarifyThreshold &&
191
+ rawHdcClarifyThreshold <= 1;
192
+ if (!hdcThresholdsValid) {
193
+ console.error("Warning: Invalid HDC policy thresholds. Falling back to defaults " +
194
+ `(fallback=${DEFAULT_HDC_FALLBACK_THRESHOLD}, clarify=${DEFAULT_HDC_CLARIFY_THRESHOLD}).`);
195
+ }
196
+ export const PRISM_HDC_POLICY_FALLBACK_THRESHOLD = hdcThresholdsValid
197
+ ? rawHdcFallbackThreshold
198
+ : DEFAULT_HDC_FALLBACK_THRESHOLD;
199
+ export const PRISM_HDC_POLICY_CLARIFY_THRESHOLD = hdcThresholdsValid
200
+ ? rawHdcClarifyThreshold
201
+ : DEFAULT_HDC_CLARIFY_THRESHOLD;
178
202
  // ─── v6.2: Graph Soft-Pruning ───────────────────────────────
179
203
  // Soft-pruning filters weak links from graph/retrieval reads while preserving
180
204
  // underlying rows for provenance. This does NOT delete links.
@@ -312,6 +312,60 @@ export async function handleGraphRoutes(url, req, res, getStorageSafe) {
312
312
  return true;
313
313
  }
314
314
  }
315
+ // ─── API: Cognitive Route Explainability (v6.5) ───
316
+ if (url.pathname === "/api/graph/cognitive-route" && req.method === "GET") {
317
+ const project = url.searchParams.get("project");
318
+ const state = url.searchParams.get("state");
319
+ const role = url.searchParams.get("role");
320
+ const action = url.searchParams.get("action");
321
+ const explain = url.searchParams.get("explain");
322
+ const fallbackThresholdRaw = url.searchParams.get("fallback_threshold");
323
+ const clarifyThresholdRaw = url.searchParams.get("clarify_threshold");
324
+ if (!project || !state || !role || !action) {
325
+ res.writeHead(400, { "Content-Type": "application/json" });
326
+ res.end(JSON.stringify({
327
+ error: "project, state, role, and action are required",
328
+ }));
329
+ return true;
330
+ }
331
+ try {
332
+ const { sessionCognitiveRouteHandler } = await import("../tools/graphHandlers.js");
333
+ const args = {
334
+ project,
335
+ state,
336
+ role,
337
+ action,
338
+ explain: explain === null ? true : explain !== "false",
339
+ };
340
+ if (fallbackThresholdRaw !== null) {
341
+ const parsed = Number(fallbackThresholdRaw);
342
+ if (!Number.isNaN(parsed))
343
+ args.fallback_threshold = parsed;
344
+ }
345
+ if (clarifyThresholdRaw !== null) {
346
+ const parsed = Number(clarifyThresholdRaw);
347
+ if (!Number.isNaN(parsed))
348
+ args.clarify_threshold = parsed;
349
+ }
350
+ const result = await sessionCognitiveRouteHandler(args);
351
+ const contentText = result.content && result.content.length > 0
352
+ ? result.content[0].text
353
+ : "";
354
+ res.writeHead(result.isError ? 400 : 200, { "Content-Type": "application/json" });
355
+ res.end(JSON.stringify({
356
+ ok: !result.isError,
357
+ isError: !!result.isError,
358
+ text: contentText,
359
+ }));
360
+ return true;
361
+ }
362
+ catch (err) {
363
+ console.error("[Dashboard] Cognitive route error:", err);
364
+ res.writeHead(500, { "Content-Type": "application/json" });
365
+ res.end(JSON.stringify({ error: err instanceof Error ? err.message : "Cognitive route failed" }));
366
+ return true;
367
+ }
368
+ }
315
369
  // ─── API: Graph Metrics (v6.0 Observability) ───
316
370
  if (url.pathname === "/api/graph/metrics" && req.method === "GET") {
317
371
  const snapshot = getGraphMetricsSnapshot();
@@ -841,7 +841,18 @@ export function renderDashboardHTML(version) {
841
841
  <span style="font-size:0.75rem; color:var(--text-muted); font-weight:600;">Active Recall</span>
842
842
  <button id="testMeBtn" onclick="triggerTestMe()" class="btn-modern" style="padding:0.3rem 0.6rem; font-size:0.75rem; background:var(--accent-teal); border-color:var(--accent-teal);" title="Generate 3 quiz questions using AI">πŸ“ Test Me</button>
843
843
  </div>
844
+ <div style="display:flex; justify-content:space-between; align-items:center; margin-top:0.8rem;">
845
+ <span style="font-size:0.75rem; color:var(--text-muted); font-weight:600;">Cognitive Route (v6.5)</span>
846
+ <button id="cognitiveRouteBtn" onclick="triggerCognitiveRoute()" class="btn-modern" style="padding:0.3rem 0.6rem; font-size:0.75rem; background:var(--accent-blue); border-color:var(--accent-blue);" title="Resolve concept route and explain why it surfaced">🧭 Route</button>
847
+ </div>
844
848
  <div id="testMeContainer" style="margin-top:0.8rem; display:flex; flex-direction:column; gap:0.5rem;"></div>
849
+
850
+ <div style="display:flex; justify-content:space-between; align-items:center; margin-top:0.8rem;">
851
+ <span style="font-size:0.75rem; color:var(--text-muted); font-weight:600;">Cognitive Route (v6.5)</span>
852
+ <button id="cognitiveRouteBtn" onclick="triggerCognitiveRoute()" class="btn-modern" style="padding:0.3rem 0.6rem; font-size:0.75rem; background:var(--accent-blue); border-color:var(--accent-blue);" title="Resolve concept route and explain why it surfaced">🧭 Route</button>
853
+ </div>
854
+ <div id="cognitiveRouteContainer" style="margin-top:0.6rem; display:flex; flex-direction:column; gap:0.4rem;"></div>
855
+
845
856
  </div>
846
857
  </div>
847
858
 
@@ -984,7 +995,7 @@ export function renderDashboardHTML(version) {
984
995
  min="0" max="100000" step="500"
985
996
  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;"
986
997
  onchange="saveSetting('max_tokens', this.value)"
987
- oninput="clearTimeout(this._t); this._t=setTimeout(()=>saveSetting('max_tokens',this.value),800)" />
998
+ oninput="clearTimeout(this._t); var _s=this; this._t=setTimeout(function(){saveSetting('max_tokens',_s.value)},800)" />
988
999
  </div>
989
1000
 
990
1001
  <div class="setting-section">Boot Settings <span class="boot-badge">Restart Required</span></div>
@@ -2372,8 +2383,66 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
2372
2383
  }
2373
2384
  }
2374
2385
 
2375
- window.triggerTestMe = triggerTestMe;
2376
- async function triggerTestMe() {
2386
+
2387
+
2388
+ async function triggerCognitiveRoute() {
2389
+ var input = document.getElementById('nodeEditorInput');
2390
+ var state = input && input.dataset && input.dataset.oldId ? input.dataset.oldId : '';
2391
+ var _gpf = document.getElementById('graphProjectFilter');
2392
+ var _ps = document.getElementById('projectSelect');
2393
+ var project = (_gpf ? _gpf.value : '') || (_ps ? _ps.value : '');
2394
+ var container = document.getElementById('cognitiveRouteContainer');
2395
+ var btn = document.getElementById('cognitiveRouteBtn');
2396
+
2397
+ if (!project || !state) {
2398
+ if (container) {
2399
+ container.innerHTML = '<div style="font-size:0.75rem;color:var(--accent-rose);">Select a project and click a graph node first.</div>';
2400
+ }
2401
+ return;
2402
+ }
2403
+
2404
+ if (btn) {
2405
+ btn.disabled = true;
2406
+ btn.textContent = '...';
2407
+ }
2408
+ if (container) {
2409
+ container.innerHTML = '<div style="font-size:0.75rem;color:var(--text-muted);text-align:center;padding:0.6rem 0;">Resolving cognitive route...</div>';
2410
+ }
2411
+
2412
+ try {
2413
+ var url = '/api/graph/cognitive-route' +
2414
+ '?project=' + encodeURIComponent(project) +
2415
+ '&state=' + encodeURIComponent('State:' + state) +
2416
+ '&role=' + encodeURIComponent('Role:dev') +
2417
+ '&action=' + encodeURIComponent('Action:inspect') +
2418
+ '&explain=true';
2419
+
2420
+ var res = await fetch(url);
2421
+ var data = await res.json();
2422
+
2423
+ if (!res.ok || data.isError) {
2424
+ if (container) {
2425
+ container.innerHTML = '<div style="font-size:0.75rem;color:var(--accent-rose);">' + escapeHtml(data.error || data.text || 'Cognitive route failed') + '</div>';
2426
+ }
2427
+ return;
2428
+ }
2429
+
2430
+ if (container) {
2431
+ var txt = data.text || '';
2432
+ container.innerHTML = '<pre style="margin:0;white-space:pre-wrap;font-size:0.72rem;line-height:1.45;background:var(--bg-secondary);border:1px solid var(--border-subtle);border-radius:6px;padding:0.6rem;color:var(--text-secondary);">' + escapeHtml(txt) + '</pre>';
2433
+ }
2434
+ } catch (err) {
2435
+ if (container) {
2436
+ container.innerHTML = '<div style="font-size:0.75rem;color:var(--accent-rose);">' + escapeHtml(err.message || 'Route error') + '</div>';
2437
+ }
2438
+ } finally {
2439
+ if (btn) {
2440
+ btn.disabled = false;
2441
+ btn.textContent = '🧭 Route';
2442
+ }
2443
+ }
2444
+ }
2445
+
2377
2446
  var input = document.getElementById('nodeEditorInput');
2378
2447
  var oldId = input.dataset.oldId;
2379
2448
  var _gpf = document.getElementById('graphProjectFilter');
@@ -3057,6 +3126,58 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
3057
3126
  parts.push('</div>');
3058
3127
  }
3059
3128
 
3129
+ // Cognitive Routing row (v6.5)
3130
+ if (m.cognitive && m.cognitive.evaluations_total > 0) {
3131
+ parts.push('<div style="border-top:1px solid var(--border-glass);padding-top:0.4rem;margin-top:0.2rem;font-size:0.75rem">');
3132
+ parts.push('<strong>🧠 Cognitive Routing</strong>');
3133
+ parts.push('<br>Evaluations: <strong>' + m.cognitive.evaluations_total + '</strong>');
3134
+
3135
+ // Route distribution bar
3136
+ var cogTotal = m.cognitive.evaluations_total;
3137
+ var autoP = Math.round((m.cognitive.route_auto_total / cogTotal) * 100);
3138
+ var clarP = Math.round((m.cognitive.route_clarify_total / cogTotal) * 100);
3139
+ var fallP = 100 - autoP - clarP;
3140
+ parts.push('<div style="display:flex;height:8px;border-radius:4px;overflow:hidden;margin:4px 0;background:var(--surface-glass)">');
3141
+ if (autoP > 0) parts.push('<div style="width:' + autoP + '%;background:var(--accent-green)" title="Auto: ' + autoP + '%"></div>');
3142
+ if (clarP > 0) parts.push('<div style="width:' + clarP + '%;background:var(--accent-amber)" title="Clarify: ' + clarP + '%"></div>');
3143
+ if (fallP > 0) parts.push('<div style="width:' + fallP + '%;background:var(--accent-rose)" title="Fallback: ' + fallP + '%"></div>');
3144
+ parts.push('</div>');
3145
+ parts.push('<span style="color:var(--accent-green)">● Auto ' + autoP + '%</span>');
3146
+ parts.push(' <span style="color:var(--accent-amber)">● Clarify ' + clarP + '%</span>');
3147
+ parts.push(' <span style="color:var(--accent-rose)">● Fallback ' + fallP + '%</span>');
3148
+
3149
+ // Rates
3150
+ if (m.cognitive.ambiguity_rate !== null) {
3151
+ var ambPct = Math.round(m.cognitive.ambiguity_rate * 100);
3152
+ var ambColor = ambPct > 40 ? 'var(--accent-rose)' : ambPct > 20 ? 'var(--accent-amber)' : 'var(--accent-green)';
3153
+ parts.push('<br>Ambiguity: <span style="color:' + ambColor + ';font-weight:600">' + ambPct + '%</span>');
3154
+ }
3155
+ if (m.cognitive.fallback_rate !== null) {
3156
+ var fbPct = Math.round(m.cognitive.fallback_rate * 100);
3157
+ var fbColor = fbPct > 30 ? 'var(--accent-rose)' : fbPct > 15 ? 'var(--accent-amber)' : 'var(--accent-green)';
3158
+ parts.push(' Β· Fallback: <span style="color:' + fbColor + ';font-weight:600">' + fbPct + '%</span>');
3159
+ }
3160
+
3161
+ // Convergence steps
3162
+ if (m.cognitive.median_convergence_steps !== null) {
3163
+ parts.push('<br>Convergence: ' + m.cognitive.median_convergence_steps + ' steps (avg)');
3164
+ }
3165
+ if (m.cognitive.duration_p50_ms !== null) {
3166
+ parts.push(' Β· p50: ' + m.cognitive.duration_p50_ms + 'ms');
3167
+ }
3168
+
3169
+ // Last evaluation
3170
+ if (m.cognitive.last_run_at) {
3171
+ var lastRoute = m.cognitive.last_route || 'β€”';
3172
+ var lastConcept = m.cognitive.last_concept || '(none)';
3173
+ var lastConf = m.cognitive.last_confidence !== null ? Math.round(m.cognitive.last_confidence * 100) + '%' : 'β€”';
3174
+ parts.push('<br>Last: ' + lastRoute + ' β†’ ' + lastConcept + ' (' + lastConf + ')');
3175
+ parts.push('<br><span style="color:var(--text-muted)">' + timeAgo(m.cognitive.last_run_at) + '</span>');
3176
+ }
3177
+
3178
+ parts.push('</div>');
3179
+ }
3180
+
3060
3181
 
3061
3182
  el.innerHTML = parts.join('');
3062
3183
 
@@ -3072,6 +3193,12 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
3072
3193
  if (m.warnings.synthesis_failure_warning) {
3073
3194
  badges.push('<span style="background:var(--accent-rose);color:#fff;padding:2px 6px;border-radius:3px;font-size:0.65rem;font-weight:600" title="Over 20% of synthesis runs are failing">⚠ Failures</span>');
3074
3195
  }
3196
+ if (m.warnings.cognitive_fallback_rate_warning) {
3197
+ badges.push('<span style="background:var(--accent-rose);color:#fff;padding:2px 6px;border-radius:3px;font-size:0.65rem;font-weight:600" title="Over 30% of cognitive routes land on FALLBACK">⚠ Cog Fallback</span>');
3198
+ }
3199
+ if (m.warnings.cognitive_ambiguity_rate_warning) {
3200
+ badges.push('<span style="background:var(--accent-amber);color:#000;padding:2px 6px;border-radius:3px;font-size:0.65rem;font-weight:600" title="Over 40% of cognitive evaluations are ambiguous">⚠ Cog Ambiguity</span>');
3201
+ }
3075
3202
  warn.innerHTML = badges.join('');
3076
3203
  }
3077
3204
  } catch(e) {
@@ -3,11 +3,12 @@
3
3
  *
4
4
  * ═══════════════════════════════════════════════════════════════════
5
5
  * PURPOSE:
6
- * Lightweight, zero-dependency metrics collection for graph synthesis
7
- * and test-me flows. All state is in-memory (resets on restart).
6
+ * Lightweight, zero-dependency metrics collection for graph synthesis,
7
+ * test-me flows, and v6.5 cognitive routing.
8
+ * All state is in-memory (resets on restart).
8
9
  *
9
10
  * DESIGN:
10
- * - Singleton counters + timestamps for synthesis and test-me
11
+ * - Singleton counters + timestamps for synthesis, test-me, and cognitive
11
12
  * - Bounded ring buffer (100 entries) for duration p50 approximation
12
13
  * - Warning flags computed on snapshot (not continuously)
13
14
  * - Structured JSON log emission via debugLog channel
@@ -16,8 +17,9 @@
16
17
  * METRICS MODEL:
17
18
  * A) Synthesis: runs, failures, links created, candidates, below-threshold, duration
18
19
  * B) Test-Me: requests, success, no_api_key, generation_failed, bad_request, duration
19
- * C) Warning flags: quality drift, provider issues, failure rate
20
+ * C) Warning flags: quality drift, provider issues, failure rate, cognitive fallback/ambiguity
20
21
  * D) SLO derivations: success rate, net new links, prune ratio, sweep duration (WS4)
22
+ * E) Cognitive (v6.5): intent evaluations, route distribution, ambiguity rate, convergence steps
21
23
  * ═══════════════════════════════════════════════════════════════════
22
24
  */
23
25
  import { debugLog } from "../utils/logger.js";
@@ -63,6 +65,7 @@ let synthesis = createFreshSynthesisMetrics();
63
65
  let testMe = createFreshTestMeMetrics();
64
66
  let schedulerSynthesis = createFreshSchedulerMetrics();
65
67
  let pruning = createFreshPruningMetrics();
68
+ let cognitive = createFreshCognitiveMetrics();
66
69
  function createFreshSynthesisMetrics() {
67
70
  return {
68
71
  runs_total: 0,
@@ -119,6 +122,21 @@ function createFreshPruningMetrics() {
119
122
  last_run_at: null,
120
123
  };
121
124
  }
125
+ function createFreshCognitiveMetrics() {
126
+ return {
127
+ evaluations_total: 0,
128
+ route_auto_total: 0,
129
+ route_clarify_total: 0,
130
+ route_fallback_total: 0,
131
+ ambiguous_total: 0,
132
+ convergence_steps_total: 0,
133
+ last_run_at: null,
134
+ last_route: null,
135
+ last_concept: null,
136
+ last_confidence: null,
137
+ duration_buffer: new DurationBuffer(),
138
+ };
139
+ }
122
140
  function emitGraphEvent(event) {
123
141
  try {
124
142
  debugLog(JSON.stringify(event));
@@ -246,6 +264,40 @@ export function recordSweepDuration(duration_ms) {
246
264
  duration_ms,
247
265
  });
248
266
  }
267
+ export function recordCognitiveRoute(data) {
268
+ const now = new Date().toISOString();
269
+ cognitive.evaluations_total++;
270
+ cognitive.last_run_at = now;
271
+ cognitive.last_route = data.route;
272
+ cognitive.last_concept = data.concept;
273
+ cognitive.last_confidence = data.confidence;
274
+ cognitive.duration_buffer.push(data.duration_ms);
275
+ cognitive.convergence_steps_total += data.steps;
276
+ if (data.ambiguous) {
277
+ cognitive.ambiguous_total++;
278
+ }
279
+ // Route distribution β€” match ActionRoute enum values
280
+ if (data.route === "ACTION_AUTO_ROUTE") {
281
+ cognitive.route_auto_total++;
282
+ }
283
+ else if (data.route === "ACTION_CLARIFY") {
284
+ cognitive.route_clarify_total++;
285
+ }
286
+ else if (data.route === "ACTION_FALLBACK") {
287
+ cognitive.route_fallback_total++;
288
+ }
289
+ emitGraphEvent({
290
+ event: "cognitive_route_evaluation",
291
+ project: data.project,
292
+ route: data.route,
293
+ concept: data.concept,
294
+ confidence: data.confidence,
295
+ distance: data.distance,
296
+ ambiguous: data.ambiguous,
297
+ steps: data.steps,
298
+ duration_ms: data.duration_ms,
299
+ });
300
+ }
249
301
  // ─── Warning Flag Computation ────────────────────────────────────
250
302
  function computeWarningFlags() {
251
303
  // Quality warning: >85% of candidates are below threshold (min 50 candidates)
@@ -256,10 +308,18 @@ function computeWarningFlags() {
256
308
  // Failure rate warning: >20% of synthesis runs failed (min 5 runs)
257
309
  const synthesis_failure_warning = synthesis.runs_total >= 5 &&
258
310
  synthesis.runs_failed / synthesis.runs_total > 0.2;
311
+ // Cognitive fallback rate warning: >30% routes go to FALLBACK (min 10 evaluations)
312
+ const cognitive_fallback_rate_warning = cognitive.evaluations_total >= 10 &&
313
+ cognitive.route_fallback_total / cognitive.evaluations_total > 0.3;
314
+ // Cognitive ambiguity rate warning: >40% evaluations are ambiguous (min 10 evaluations)
315
+ const cognitive_ambiguity_rate_warning = cognitive.evaluations_total >= 10 &&
316
+ cognitive.ambiguous_total / cognitive.evaluations_total > 0.4;
259
317
  return {
260
318
  synthesis_quality_warning,
261
319
  testme_provider_warning,
262
320
  synthesis_failure_warning,
321
+ cognitive_fallback_rate_warning,
322
+ cognitive_ambiguity_rate_warning,
263
323
  };
264
324
  }
265
325
  // ─── SLO Derivation (WS4) ────────────────────────────────────────
@@ -283,6 +343,17 @@ function computeSloMetrics() {
283
343
  };
284
344
  }
285
345
  export function getGraphMetricsSnapshot() {
346
+ // Derived cognitive rates
347
+ const cogEvalTotal = cognitive.evaluations_total;
348
+ const ambiguity_rate = cogEvalTotal > 0
349
+ ? parseFloat((cognitive.ambiguous_total / cogEvalTotal).toFixed(4))
350
+ : null;
351
+ const fallback_rate = cogEvalTotal > 0
352
+ ? parseFloat((cognitive.route_fallback_total / cogEvalTotal).toFixed(4))
353
+ : null;
354
+ const median_convergence_steps = cogEvalTotal > 0
355
+ ? parseFloat((cognitive.convergence_steps_total / cogEvalTotal).toFixed(2))
356
+ : null;
286
357
  return {
287
358
  synthesis: {
288
359
  runs_total: synthesis.runs_total,
@@ -332,6 +403,22 @@ export function getGraphMetricsSnapshot() {
332
403
  skipped_budget_last: pruning.skipped_budget_last,
333
404
  last_run_at: pruning.last_run_at,
334
405
  },
406
+ cognitive: {
407
+ evaluations_total: cognitive.evaluations_total,
408
+ route_auto_total: cognitive.route_auto_total,
409
+ route_clarify_total: cognitive.route_clarify_total,
410
+ route_fallback_total: cognitive.route_fallback_total,
411
+ ambiguous_total: cognitive.ambiguous_total,
412
+ convergence_steps_total: cognitive.convergence_steps_total,
413
+ median_convergence_steps,
414
+ ambiguity_rate,
415
+ fallback_rate,
416
+ last_run_at: cognitive.last_run_at,
417
+ last_route: cognitive.last_route,
418
+ last_concept: cognitive.last_concept,
419
+ last_confidence: cognitive.last_confidence,
420
+ duration_p50_ms: cognitive.duration_buffer.getP50(),
421
+ },
335
422
  slo: computeSloMetrics(),
336
423
  warnings: computeWarningFlags(),
337
424
  };
@@ -342,6 +429,7 @@ export function resetGraphMetricsForTests() {
342
429
  testMe = createFreshTestMeMetrics();
343
430
  schedulerSynthesis = createFreshSchedulerMetrics();
344
431
  pruning = createFreshPruningMetrics();
432
+ cognitive = createFreshCognitiveMetrics();
345
433
  sweepDurationMsLast = 0;
346
434
  sweepLastAt = null;
347
435
  }
package/dist/server.js CHANGED
@@ -58,7 +58,7 @@ ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ReadResourceRequ
58
58
  // Claude Desktop that the attached resource has changed.
59
59
  // Without this, the paperclipped context becomes stale.
60
60
  SubscribeRequestSchema, UnsubscribeRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
61
- import { SERVER_CONFIG, SESSION_MEMORY_ENABLED, PRISM_USER_ID, PRISM_ENABLE_HIVEMIND, WATCHDOG_INTERVAL_MS, WATCHDOG_STALE_MIN, WATCHDOG_FROZEN_MIN, WATCHDOG_OFFLINE_MIN, WATCHDOG_LOOP_THRESHOLD, PRISM_SCHEDULER_ENABLED, PRISM_SCHEDULER_INTERVAL_MS, PRISM_SCHOLAR_ENABLED, } from "./config.js";
61
+ import { SERVER_CONFIG, SESSION_MEMORY_ENABLED, PRISM_USER_ID, PRISM_ENABLE_HIVEMIND, WATCHDOG_INTERVAL_MS, WATCHDOG_STALE_MIN, WATCHDOG_FROZEN_MIN, WATCHDOG_OFFLINE_MIN, WATCHDOG_LOOP_THRESHOLD, PRISM_SCHEDULER_ENABLED, PRISM_SCHEDULER_INTERVAL_MS, PRISM_SCHOLAR_ENABLED, PRISM_HDC_ENABLED, } from "./config.js";
62
62
  import { startWatchdog, drainAlerts } from "./hivemindWatchdog.js";
63
63
  import { startScheduler, startScholarScheduler } from "./backgroundScheduler.js";
64
64
  import { getSyncBus } from "./sync/factory.js";
@@ -95,9 +95,9 @@ KNOWLEDGE_SET_RETENTION_TOOL,
95
95
  // v4.0: Active Behavioral Memory tools
96
96
  SESSION_SAVE_EXPERIENCE_TOOL, KNOWLEDGE_UPVOTE_TOOL, KNOWLEDGE_DOWNVOTE_TOOL,
97
97
  // v6.0: Associative Memory Graph tools
98
- SESSION_BACKFILL_LINKS_TOOL, SESSION_SYNTHESIZE_EDGES_TOOL, sessionSaveLedgerHandler, sessionSaveHandoffHandler, sessionLoadContextHandler, knowledgeSearchHandler, knowledgeForgetHandler,
98
+ SESSION_BACKFILL_LINKS_TOOL, SESSION_SYNTHESIZE_EDGES_TOOL, SESSION_COGNITIVE_ROUTE_TOOL, sessionSaveLedgerHandler, sessionSaveHandoffHandler, sessionLoadContextHandler, knowledgeSearchHandler, knowledgeForgetHandler,
99
99
  // ─── v0.4.0: New tool handlers ───
100
- compactLedgerHandler, sessionSearchMemoryHandler, backfillEmbeddingsHandler, sessionBackfillLinksHandler, sessionSynthesizeEdgesHandler,
100
+ compactLedgerHandler, sessionSearchMemoryHandler, backfillEmbeddingsHandler, sessionBackfillLinksHandler, sessionSynthesizeEdgesHandler, sessionCognitiveRouteHandler,
101
101
  // ─── v2.0: Time Travel handlers ───
102
102
  memoryHistoryHandler, memoryCheckoutHandler,
103
103
  // ─── v2.0: Visual Memory handlers ───
@@ -193,6 +193,7 @@ function buildSessionMemoryTools(autoloadList) {
193
193
  // ─── v6.0: Associative Memory Graph tools ───
194
194
  SESSION_BACKFILL_LINKS_TOOL, // session_backfill_links β€” retroactive graph edge creation
195
195
  SESSION_SYNTHESIZE_EDGES_TOOL, // session_synthesize_edges β€” inferred semantic graph enrichment
196
+ SESSION_COGNITIVE_ROUTE_TOOL, // session_cognitive_route β€” HDC policy-gated concept routing (v6.5)
196
197
  // ─── v6.1: Storage Hygiene tool ───
197
198
  MAINTENANCE_VACUUM_TOOL, // maintenance_vacuum β€” reclaim SQLite disk space post-purge
198
199
  ];
@@ -756,6 +757,13 @@ export function createServer() {
756
757
  throw new Error("Session memory not configured. Set SUPABASE_URL and SUPABASE_KEY.");
757
758
  result = await sessionSynthesizeEdgesHandler(args);
758
759
  break;
760
+ case "session_cognitive_route":
761
+ if (!SESSION_MEMORY_ENABLED)
762
+ throw new Error("Session memory not configured. Set SUPABASE_URL and SUPABASE_KEY.");
763
+ if (!PRISM_HDC_ENABLED)
764
+ throw new Error("HDC cognitive routing not enabled. Set PRISM_HDC_ENABLED=true.");
765
+ result = await sessionCognitiveRouteHandler(args);
766
+ break;
759
767
  case "session_backfill_embeddings":
760
768
  if (!SESSION_MEMORY_ENABLED)
761
769
  throw new Error("Session memory not configured. Set SUPABASE_URL and SUPABASE_KEY.");
@@ -17,6 +17,7 @@ import { formatRulesBlock, applySentinelBlock } from "./commonHelpers.js";
17
17
  * ═══════════════════════════════════════════════════════════════════
18
18
  */
19
19
  import { debugLog } from "../utils/logger.js";
20
+ import { recordCognitiveRoute } from "../observability/graphMetrics.js";
20
21
  import { getStorage } from "../storage/index.js";
21
22
  import { toKeywordArray } from "../utils/keywordExtractor.js";
22
23
  import { getLLMProvider } from "../utils/llm/factory.js";
@@ -31,7 +32,7 @@ import { createMemoryTrace, traceToContentBlock } from "../utils/tracing.js";
31
32
  import { GOOGLE_API_KEY, PRISM_USER_ID } from "../config.js";
32
33
  import { isKnowledgeSearchArgs, isKnowledgeForgetArgs, isSessionSearchMemoryArgs, isKnowledgeVoteArgs,
33
34
  // v4.2: Sync Rules type guard
34
- isKnowledgeSyncRulesArgs, isSessionIntuitiveRecallArgs, isSessionSynthesizeEdgesArgs, } from "./sessionMemoryDefinitions.js";
35
+ isKnowledgeSyncRulesArgs, isSessionIntuitiveRecallArgs, isSessionSynthesizeEdgesArgs, isSessionCognitiveRouteArgs, } from "./sessionMemoryDefinitions.js";
35
36
  // v4.2: File system access for knowledge_sync_rules
36
37
  import { readFile, writeFile, mkdir } from "node:fs/promises";
37
38
  import { existsSync } from "node:fs";
@@ -50,6 +51,11 @@ const activeCompactions = new Set();
50
51
  * After saving, generates an embedding vector for the entry via fire-and-forget.
51
52
  */
52
53
  import { computeEffectiveImportance, updateLastAccessed } from "../utils/cognitiveMemory.js";
54
+ import { HdcStateMachine } from "../sdm/stateMachine.js";
55
+ import { ConceptDictionary } from "../sdm/conceptDictionary.js";
56
+ import { PolicyGateway } from "../sdm/policyGateway.js";
57
+ import { getSdmEngine } from "../sdm/sdmEngine.js";
58
+ import { PRISM_HDC_ENABLED, PRISM_HDC_EXPLAINABILITY_ENABLED, PRISM_HDC_POLICY_FALLBACK_THRESHOLD, PRISM_HDC_POLICY_CLARIFY_THRESHOLD, } from "../config.js";
53
59
  export async function knowledgeSearchHandler(args) {
54
60
  if (!isKnowledgeSearchArgs(args)) {
55
61
  throw new Error("Invalid arguments for knowledge_search");
@@ -670,7 +676,6 @@ export async function sessionIntuitiveRecallHandler(args) {
670
676
  };
671
677
  }
672
678
  try {
673
- const { getSdmEngine } = await import("../sdm/sdmEngine.js");
674
679
  const { decodeSdmVector } = await import("../sdm/sdmDecoder.js");
675
680
  const queryVector = await getLLMProvider().generateEmbedding(args.query);
676
681
  const sdmEngine = getSdmEngine(args.project);
@@ -702,6 +707,113 @@ export async function sessionIntuitiveRecallHandler(args) {
702
707
  };
703
708
  }
704
709
  }
710
+ export async function sessionCognitiveRouteHandler(args) {
711
+ if (!isSessionCognitiveRouteArgs(args)) {
712
+ return {
713
+ content: [{ type: "text", text: "Invalid arguments for session_cognitive_route" }],
714
+ isError: true,
715
+ };
716
+ }
717
+ if (!PRISM_HDC_ENABLED) {
718
+ return {
719
+ content: [{
720
+ type: "text",
721
+ text: "⚠️ session_cognitive_route is disabled. Set PRISM_HDC_ENABLED=true to enable v6.5 cognitive routing.",
722
+ }],
723
+ isError: true,
724
+ };
725
+ }
726
+ try {
727
+ const storage = await getStorage();
728
+ const sdmEngine = getSdmEngine(args.project);
729
+ const dict = new ConceptDictionary(storage);
730
+ const stateVector = await dict.getConcept(args.state);
731
+ const roleVector = await dict.getConcept(args.role);
732
+ const actionVector = await dict.getConcept(args.action);
733
+ const machine = new HdcStateMachine(stateVector, sdmEngine);
734
+ machine.transition(roleVector, actionVector);
735
+ const fallbackKey = `hdc:fallback_threshold:${args.project}`;
736
+ const clarifyKey = `hdc:clarify_threshold:${args.project}`;
737
+ const persistedFallbackRaw = await storage.getSetting(fallbackKey);
738
+ const persistedClarifyRaw = await storage.getSetting(clarifyKey);
739
+ const persistedFallback = persistedFallbackRaw !== null ? Number(persistedFallbackRaw) : NaN;
740
+ const persistedClarify = persistedClarifyRaw !== null ? Number(persistedClarifyRaw) : NaN;
741
+ const baseFallbackThreshold = Number.isFinite(persistedFallback)
742
+ ? persistedFallback
743
+ : PRISM_HDC_POLICY_FALLBACK_THRESHOLD;
744
+ const baseClarifyThreshold = Number.isFinite(persistedClarify)
745
+ ? persistedClarify
746
+ : PRISM_HDC_POLICY_CLARIFY_THRESHOLD;
747
+ const fallbackThreshold = args.fallback_threshold ?? baseFallbackThreshold;
748
+ const clarifyThreshold = args.clarify_threshold ?? baseClarifyThreshold;
749
+ const explain = args.explain !== undefined ? args.explain : true;
750
+ if (!(fallbackThreshold >= 0 && fallbackThreshold < clarifyThreshold && clarifyThreshold <= 1)) {
751
+ return {
752
+ content: [{
753
+ type: "text",
754
+ text: `Invalid policy thresholds for project "${args.project}": ` +
755
+ `fallback=${fallbackThreshold}, clarify=${clarifyThreshold}. ` +
756
+ "Expected 0 <= fallback < clarify <= 1.",
757
+ }],
758
+ isError: true,
759
+ };
760
+ }
761
+ // Phase 2 parity hook: persist project-specific threshold overrides via storage settings.
762
+ // This works on both SQLite and Supabase backends through the shared StorageBackend API.
763
+ if (args.fallback_threshold !== undefined || args.clarify_threshold !== undefined) {
764
+ await storage.setSetting(fallbackKey, String(fallbackThreshold));
765
+ await storage.setSetting(clarifyKey, String(clarifyThreshold));
766
+ }
767
+ const gateway = new PolicyGateway(dict, {
768
+ fallbackThreshold,
769
+ clarifyThreshold,
770
+ });
771
+ const cogStart = Date.now();
772
+ const result = await gateway.evaluateIntent(machine);
773
+ const cogDuration = Date.now() - cogStart;
774
+ // Phase 4: Record cognitive route telemetry
775
+ recordCognitiveRoute({
776
+ project: args.project,
777
+ route: result.route,
778
+ concept: result.concept || null,
779
+ confidence: result.confidence,
780
+ distance: result.distance,
781
+ ambiguous: result.ambiguous,
782
+ steps: result.steps,
783
+ duration_ms: cogDuration,
784
+ });
785
+ const lines = [];
786
+ lines.push(`🧠 Cognitive Route β€” project \"${args.project}\"`);
787
+ lines.push("");
788
+ lines.push(`State: ${args.state}`);
789
+ lines.push(`Role: ${args.role}`);
790
+ lines.push(`Action: ${args.action}`);
791
+ lines.push("");
792
+ lines.push(`Route: ${result.route}`);
793
+ lines.push(`Concept: ${result.concept || "(none)"}`);
794
+ lines.push(`Confidence: ${(result.confidence * 100).toFixed(2)}%`);
795
+ lines.push(`Distance: ${result.distance}`);
796
+ lines.push(`Ambiguous: ${result.ambiguous ? "yes" : "no"}`);
797
+ lines.push(`Convergence Steps: ${result.steps}`);
798
+ if (PRISM_HDC_EXPLAINABILITY_ENABLED && explain) {
799
+ lines.push("");
800
+ lines.push("Explainability:");
801
+ lines.push(`- Policy thresholds: fallback=${fallbackThreshold}, clarify=${clarifyThreshold}`);
802
+ lines.push("- Routing logic: below fallback => FALLBACK, ambiguous/below clarify => CLARIFY, else AUTO_ROUTE");
803
+ }
804
+ return {
805
+ content: [{ type: "text", text: lines.join("\n") }],
806
+ isError: false,
807
+ };
808
+ }
809
+ catch (err) {
810
+ debugLog(`[session_cognitive_route] Failed: ${err instanceof Error ? err.message : String(err)}`);
811
+ return {
812
+ content: [{ type: "text", text: `Error triggering cognitive route: ${err instanceof Error ? err.message : String(err)}` }],
813
+ isError: true,
814
+ };
815
+ }
816
+ }
705
817
  export async function synthesizeEdgesCore({ project, similarity_threshold = 0.7, max_entries = 50, max_neighbors_per_entry = 3, randomize_selection = false, }) {
706
818
  const storage = await getStorage();
707
819
  const llm = getLLMProvider();
@@ -26,11 +26,11 @@ export { webSearchHandler, braveWebSearchCodeModeHandler, localSearchHandler, br
26
26
  // This file always exports them β€” server.ts decides whether to include them in the tool list.
27
27
  //
28
28
  // v0.4.0: Added SESSION_COMPACT_LEDGER_TOOL and SESSION_SEARCH_MEMORY_TOOL
29
- export { SESSION_SAVE_LEDGER_TOOL, SESSION_SAVE_HANDOFF_TOOL, SESSION_LOAD_CONTEXT_TOOL, KNOWLEDGE_SEARCH_TOOL, KNOWLEDGE_FORGET_TOOL, SESSION_COMPACT_LEDGER_TOOL, SESSION_SEARCH_MEMORY_TOOL, MEMORY_HISTORY_TOOL, MEMORY_CHECKOUT_TOOL, SESSION_SAVE_IMAGE_TOOL, SESSION_VIEW_IMAGE_TOOL, SESSION_HEALTH_CHECK_TOOL, SESSION_FORGET_MEMORY_TOOL, SESSION_EXPORT_MEMORY_TOOL, KNOWLEDGE_SET_RETENTION_TOOL, SESSION_SAVE_EXPERIENCE_TOOL, KNOWLEDGE_UPVOTE_TOOL, KNOWLEDGE_DOWNVOTE_TOOL, KNOWLEDGE_SYNC_RULES_TOOL, DEEP_STORAGE_PURGE_TOOL, SESSION_INTUITIVE_RECALL_TOOL, SESSION_BACKFILL_LINKS_TOOL, MAINTENANCE_VACUUM_TOOL, isDeepStoragePurgeArgs, SESSION_SYNTHESIZE_EDGES_TOOL, isSessionSynthesizeEdgesArgs } from "./sessionMemoryDefinitions.js";
29
+ export { SESSION_SAVE_LEDGER_TOOL, SESSION_SAVE_HANDOFF_TOOL, SESSION_LOAD_CONTEXT_TOOL, KNOWLEDGE_SEARCH_TOOL, KNOWLEDGE_FORGET_TOOL, SESSION_COMPACT_LEDGER_TOOL, SESSION_SEARCH_MEMORY_TOOL, MEMORY_HISTORY_TOOL, MEMORY_CHECKOUT_TOOL, SESSION_SAVE_IMAGE_TOOL, SESSION_VIEW_IMAGE_TOOL, SESSION_HEALTH_CHECK_TOOL, SESSION_FORGET_MEMORY_TOOL, SESSION_EXPORT_MEMORY_TOOL, KNOWLEDGE_SET_RETENTION_TOOL, SESSION_SAVE_EXPERIENCE_TOOL, KNOWLEDGE_UPVOTE_TOOL, KNOWLEDGE_DOWNVOTE_TOOL, KNOWLEDGE_SYNC_RULES_TOOL, DEEP_STORAGE_PURGE_TOOL, SESSION_INTUITIVE_RECALL_TOOL, SESSION_BACKFILL_LINKS_TOOL, MAINTENANCE_VACUUM_TOOL, isDeepStoragePurgeArgs, SESSION_SYNTHESIZE_EDGES_TOOL, isSessionSynthesizeEdgesArgs, SESSION_COGNITIVE_ROUTE_TOOL, isSessionCognitiveRouteArgs } from "./sessionMemoryDefinitions.js";
30
30
  // 1. Ledger (Core CRUD & State)
31
31
  export { sessionSaveLedgerHandler, sessionSaveHandoffHandler, sessionLoadContextHandler, sessionSaveExperienceHandler, sessionSaveImageHandler, sessionViewImageHandler, memoryHistoryHandler, memoryCheckoutHandler, sessionForgetMemoryHandler, sessionExportMemoryHandler } from "./ledgerHandlers.js";
32
32
  // 2. Graph (Semantic Search & Weighting)
33
- export { sessionSearchMemoryHandler, knowledgeSearchHandler, sessionIntuitiveRecallHandler, knowledgeUpvoteHandler, knowledgeDownvoteHandler, knowledgeForgetHandler, knowledgeSyncRulesHandler, sessionSynthesizeEdgesHandler } from "./graphHandlers.js";
33
+ export { sessionSearchMemoryHandler, knowledgeSearchHandler, sessionIntuitiveRecallHandler, knowledgeUpvoteHandler, knowledgeDownvoteHandler, knowledgeForgetHandler, knowledgeSyncRulesHandler, sessionSynthesizeEdgesHandler, sessionCognitiveRouteHandler } from "./graphHandlers.js";
34
34
  // 3. Hygiene (Maintenance & Integrity)
35
35
  export { deepStoragePurgeHandler, maintenanceVacuumHandler, sessionHealthCheckHandler, backfillEmbeddingsHandler, sessionBackfillLinksHandler, knowledgeSetRetentionHandler } from "./hygieneHandlers.js";
36
36
  // ── Compaction Handler (v0.4.0 β€” Enhancement #2) ──
@@ -1238,3 +1238,67 @@ export function isSessionSynthesizeEdgesArgs(args) {
1238
1238
  return false;
1239
1239
  return true;
1240
1240
  }
1241
+ export const SESSION_COGNITIVE_ROUTE_TOOL = {
1242
+ name: "session_cognitive_route",
1243
+ description: "Resolve an HDC compositional state into a nearest semantic concept with policy-gated routing. " +
1244
+ "Returns concept, confidence, distance, ambiguity, convergence steps, and route outcome. " +
1245
+ "Use this for explainable cognitive recall decisions in v6.5.",
1246
+ inputSchema: {
1247
+ type: "object",
1248
+ properties: {
1249
+ project: {
1250
+ type: "string",
1251
+ description: "Project identifier.",
1252
+ },
1253
+ state: {
1254
+ type: "string",
1255
+ description: "Current state concept key (e.g. 'State:ActiveSession').",
1256
+ },
1257
+ role: {
1258
+ type: "string",
1259
+ description: "Role concept key used for transition binding.",
1260
+ },
1261
+ action: {
1262
+ type: "string",
1263
+ description: "Action concept key used for transition binding.",
1264
+ },
1265
+ fallback_threshold: {
1266
+ type: "number",
1267
+ description: "Optional route fallback threshold override (0 <= fallback < clarify <= 1).",
1268
+ },
1269
+ clarify_threshold: {
1270
+ type: "number",
1271
+ description: "Optional route clarify threshold override (0 <= fallback < clarify <= 1).",
1272
+ },
1273
+ explain: {
1274
+ type: "boolean",
1275
+ description: "If true, include expanded explainability details in the response. Default: true.",
1276
+ },
1277
+ },
1278
+ required: ["project", "state", "role", "action"],
1279
+ },
1280
+ };
1281
+ export function isSessionCognitiveRouteArgs(args) {
1282
+ if (typeof args !== "object" || args === null)
1283
+ return false;
1284
+ const a = args;
1285
+ if (typeof a.project !== "string")
1286
+ return false;
1287
+ if (typeof a.state !== "string")
1288
+ return false;
1289
+ if (typeof a.role !== "string")
1290
+ return false;
1291
+ if (typeof a.action !== "string")
1292
+ return false;
1293
+ if (a.fallback_threshold !== undefined && typeof a.fallback_threshold !== "number")
1294
+ return false;
1295
+ if (a.clarify_threshold !== undefined && typeof a.clarify_threshold !== "number")
1296
+ return false;
1297
+ if (a.explain !== undefined && typeof a.explain !== "boolean")
1298
+ return false;
1299
+ if (typeof a.fallback_threshold === "number" &&
1300
+ typeof a.clarify_threshold === "number" &&
1301
+ !(a.fallback_threshold >= 0 && a.fallback_threshold < a.clarify_threshold && a.clarify_threshold <= 1))
1302
+ return false;
1303
+ return true;
1304
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prism-mcp-server",
3
- "version": "6.2.1",
3
+ "version": "6.5.0",
4
4
  "mcpName": "io.github.dcostenco/prism-mcp",
5
5
  "description": "The Mind Palace for AI Agents β€” persistent memory (SQLite/Supabase), behavioral learning & IDE rules sync, multimodal VLM image captioning, pluggable LLM providers (OpenAI/Anthropic/Gemini/Ollama), OpenTelemetry distributed tracing, GDPR export, multi-agent Hivemind sync, time travel, visual Mind Palace dashboard. Zero-config local mode.",
6
6
  "module": "index.ts",