prism-mcp-server 7.7.0 → 7.8.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 +114 -26
- package/dist/dashboard/server.js +97 -1
- package/dist/memory/spreadingActivation.js +107 -0
- package/dist/server.js +16 -9
- package/dist/storage/sqlite.js +122 -4
- package/dist/storage/supabase.js +6 -0
- package/dist/tools/compactionHandler.js +81 -22
- package/dist/tools/graphHandlers.js +33 -5
- package/dist/tools/ledgerHandlers.js +1 -1
- package/dist/tools/sessionMemoryDefinitions.js +24 -0
- package/dist/utils/cognitiveMemory.js +3 -2
- package/dist/utils/llm/factory.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
|
|
11
11
|

|
|
12
12
|
|
|
13
|
-
**Your AI agent forgets everything between sessions. Prism fixes that.**
|
|
13
|
+
**Your AI agent forgets everything between sessions. Prism fixes that — then teaches it to think.**
|
|
14
14
|
|
|
15
|
-
|
|
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
|
|
@@ -28,6 +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
|
+
- [Cognitive Architecture (v7.8)](#-cognitive-architecture-v78)
|
|
31
32
|
- [Data Privacy & Egress](#data-privacy--egress)
|
|
32
33
|
- [Use Cases](#use-cases)
|
|
33
34
|
- [What's New](#whats-new)
|
|
@@ -45,15 +46,17 @@ Works with **Claude Desktop · Claude Code · Cursor · Windsurf · Cline · Gem
|
|
|
45
46
|
|
|
46
47
|
Every time you start a new conversation with an AI coding assistant, it starts from scratch. You re-explain your architecture, re-describe your decisions, re-list your TODOs. Hours of context — gone.
|
|
47
48
|
|
|
48
|
-
**Prism gives your agent a brain that persists.** Save what matters at the end of each session. Load it back instantly on the next one.
|
|
49
|
+
**Prism gives your agent a brain that persists — and then teaches it to reason.** Save what matters at the end of each session. Load it back instantly on the next one. But Prism goes far beyond storage: it consolidates raw experience into lasting principles, traverses causal chains to surface root causes, and knows when to say *"I don't know."*
|
|
49
50
|
|
|
50
|
-
> 📌 **Terminology:** Throughout this doc, **"Prism"** refers to the MCP server and
|
|
51
|
+
> 📌 **Terminology:** Throughout this doc, **"Prism"** refers to the MCP server and cognitive memory engine. **"Mind Palace"** refers to the visual dashboard UI at `localhost:3000` — your window into the agent's brain. They work together; the dashboard is optional.
|
|
51
52
|
|
|
52
|
-
Prism has
|
|
53
|
+
Prism has three pillars:
|
|
53
54
|
|
|
54
|
-
1. **🧠
|
|
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).)*
|
|
55
56
|
|
|
56
|
-
2.
|
|
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).)*
|
|
58
|
+
|
|
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).)*
|
|
57
60
|
|
|
58
61
|
---
|
|
59
62
|
|
|
@@ -456,6 +459,71 @@ When you trigger a Dark Factory pipeline, Prism doesn't just run your task — i
|
|
|
456
459
|
|
|
457
460
|
---
|
|
458
461
|
|
|
462
|
+
## 🧠 Cognitive Architecture (v7.8)
|
|
463
|
+
|
|
464
|
+
> *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.*
|
|
465
|
+
|
|
466
|
+
Standard RAG (Retrieval-Augmented Generation) is now a commodity. Everyone has vector search. What turns a memory *storage* system into a memory *reasoning* system is the cognitive layer between storage and retrieval. Here is what Prism v7.8 builds on top of the vector foundation:
|
|
467
|
+
|
|
468
|
+
### 1. The Agent Actually Learns (Episodic → Semantic Consolidation)
|
|
469
|
+
|
|
470
|
+
| | Standard RAG | Prism v7.8 |
|
|
471
|
+
|---|---|---|
|
|
472
|
+
| **Memory** | Giant, flat transcript of past events | Dual-memory: Episodic events + Semantic rules |
|
|
473
|
+
| **Recall** | Re-reads everything linearly | Retrieves distilled principles instantly |
|
|
474
|
+
| **Learning** | None — every session starts cold | Hebbian: confidence increases with repeated reinforcement |
|
|
475
|
+
|
|
476
|
+
**How it works:** When Prism compacts session history, it doesn't just summarize text — it extracts *principles*. Raw event logs ("We deployed v2.3 and the auth service crashed because the JWT secret was rotated") consolidate into a semantic rule ("JWT secrets must be rotated before deployment, not during"). These rules live in a dedicated `semantic_knowledge` table with `confidence` scores that increase every time the pattern is observed. **Your agent doesn't just remember what it did; it learns *how the world works* over time.** This is true Hebbian learning: neurons that fire together wire together.
|
|
477
|
+
|
|
478
|
+
### 2. "Train of Thought" Reasoning (Spreading Activation & Causality)
|
|
479
|
+
|
|
480
|
+
| | Standard RAG | Prism v7.8 |
|
|
481
|
+
|---|---|---|
|
|
482
|
+
| **Search** | Cosine similarity to the query | Multi-hop graph traversal with lateral inhibition |
|
|
483
|
+
| **Scope** | Only finds things that *look like* the prompt | Follows causal chains across memories |
|
|
484
|
+
| **Root cause** | Missed entirely | Surfaced via `caused_by` / `led_to` edges |
|
|
485
|
+
|
|
486
|
+
**How it works:** When compacting memories, Prism extracts causal links (`caused_by`, `led_to`) and persists them as edges in the knowledge graph. At retrieval time, ACT-R spreading activation propagates through these edges with a damped fan effect (`1 / ln(fan + e)`) to prevent hub-flooding, lateral inhibition to suppress noise, and configurable hop depth. If you search for "Error X", the engine traverses the graph and brings back "Workaround Y" → "Architecture Decision Z" — a literal train of thought instead of a static search result.
|
|
487
|
+
|
|
488
|
+
```
|
|
489
|
+
Query: "Why does the API timeout?"
|
|
490
|
+
│
|
|
491
|
+
┌─────────────┼─────────────┐
|
|
492
|
+
▼ ▼ ▼
|
|
493
|
+
[Memory: API [Memory: [Memory:
|
|
494
|
+
timeout error] DB pool rate limiter
|
|
495
|
+
exhaustion] misconfigured]
|
|
496
|
+
│ │
|
|
497
|
+
▼ ▼
|
|
498
|
+
[Memory: [Memory:
|
|
499
|
+
caused_by → led_to →
|
|
500
|
+
connection connection
|
|
501
|
+
leak in v2.1] pool patch
|
|
502
|
+
in v2.2]
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### 3. Self-Awareness & The End of Hallucinations (The Rejection Gate)
|
|
506
|
+
|
|
507
|
+
| | Standard RAG | Prism v7.8 |
|
|
508
|
+
|---|---|---|
|
|
509
|
+
| **Bad query** | Returns top-5 garbage results | Returns `rejected: true` with reason |
|
|
510
|
+
| **Confidence** | Always 100% confident (even when wrong) | Measures gap-distance and entropy |
|
|
511
|
+
| **Hallucination risk** | High — LLM gets garbage context | Low — LLM told "you don't know" |
|
|
512
|
+
|
|
513
|
+
**How it works:** The **Uncertainty-Aware Rejection Gate** operates on two signals: *similarity floor* (is the best match even remotely relevant?) and *gap distance* (is there meaningful separation between the top results, or are they all equally mediocre?). When both signals indicate low confidence, Prism returns a structured rejection — telling the LLM "I searched my memory, and I confidently do not know the answer" — instead of feeding it garbage context that causes hallucinations. In the current LLM landscape, **an agent that knows its own boundaries is a massive competitive advantage.**
|
|
514
|
+
|
|
515
|
+
### 4. Block Amnesia Solved (Dynamic Fast Weight Decay)
|
|
516
|
+
|
|
517
|
+
| | Standard RAG | Prism v7.8 |
|
|
518
|
+
|---|---|---|
|
|
519
|
+
| **Decay** | Uniform (everything fades equally) | Dual-rate: episodic fades fast, semantic persists |
|
|
520
|
+
| **Core knowledge** | Forgotten over time | Permanently anchored via `is_rollup` flag |
|
|
521
|
+
| **Personality drift** | Common in long-lived agents | Prevented by Long-Term Context anchors |
|
|
522
|
+
|
|
523
|
+
**How it works:** Most memory systems decay everything at the same rate, meaning agents eventually forget their core system instructions as time passes. Prism applies ACT-R base-level activation decay (`B_i = ln(Σ t_j^(-d))`) with a **50% slower decay rate for semantic rollup nodes** (`ageModifier = 0.5` for `is_rollup` entries). The agent will naturally forget what it ate for breakfast (raw episodic chatter), but it will permanently remember its core personality, project rules, and hard-won architectural decisions. The result: Long-Term Context anchors that survive indefinitely.
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
459
527
|
## 🔒 Data Privacy & Egress
|
|
460
528
|
|
|
461
529
|
**Where is my data stored?**
|
|
@@ -623,8 +691,10 @@ The Generator strips the `console.log`, resubmits, and the next `EVALUATE` retur
|
|
|
623
691
|
|
|
624
692
|
## 🆕 What's New
|
|
625
693
|
|
|
626
|
-
> **Current release: v7.
|
|
694
|
+
> **Current release: v7.8.0 — Cognitive Architecture**
|
|
627
695
|
|
|
696
|
+
- 🧠 **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)
|
|
697
|
+
- 🌐 **v7.7.0 — Cloud-Native SSE Transport:** Full unauthenticated and authenticated Server-Sent Events MCP support for seamless network deployments.
|
|
628
698
|
- 🩺 **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.
|
|
629
699
|
- ⚔️ **v7.4.0 — Adversarial Evaluation:** Split-brain anti-sycophancy pipeline. Generator and evaluator in isolated roles with evidence-bound findings.
|
|
630
700
|
- 🏭 **v7.3.x — Dark Factory + Stability:** Fail-closed 3-gate execution pipeline. Dashboard stability and verification diagnostics.
|
|
@@ -635,7 +705,7 @@ The Generator strips the `console.log`, resubmits, and the next `EVALUATE` retur
|
|
|
635
705
|
|
|
636
706
|
## ⚔️ How Prism Compares
|
|
637
707
|
|
|
638
|
-
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
|
|
708
|
+
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.
|
|
639
709
|
|
|
640
710
|
### 📊 Feature-by-Feature Comparison
|
|
641
711
|
|
|
@@ -644,7 +714,7 @@ Standard memory servers (like Mem0, Zep, or the baseline Anthropic MCP) act as p
|
|
|
644
714
|
| **Primary Interface** | **Native MCP** (Tools, Prompts, Resources) | REST API & Python/TS SDKs | REST API & Python/TS SDKs | Native MCP (Tools only) |
|
|
645
715
|
| **Storage Engine** | **BYO SQLite or Supabase** | Managed Cloud / VectorDBs | Managed Cloud / Postgres | Local SQLite only |
|
|
646
716
|
| **Context Assembly** | **Progressive (Quick/Std/Deep)** | Top-K Semantic Search | Top-K + Temporal Summaries | Basic Entity Search |
|
|
647
|
-
| **Memory Mechanics** | **ACT-R Activation
|
|
717
|
+
| **Memory Mechanics** | **ACT-R Activation, Spreading Activation, Hebbian Consolidation, Rejection Gate** | Basic Vector + Entity | Fading Temporal Graph | None (Infinite growth) |
|
|
648
718
|
| **Multi-Agent Sync** | **CRDT (Add-Wins / LWW)** | Cloud locks | Postgres locks | ❌ None (Data races) |
|
|
649
719
|
| **Data Compression** | **TurboQuant (7x smaller vectors)** | ❌ Standard F32 Vectors | ❌ Standard Vectors | ❌ No Vectors |
|
|
650
720
|
| **Observability** | **OTel Traces + Built-in PWA UI** | Cloud Dashboard | Cloud Dashboard | ❌ None |
|
|
@@ -883,7 +953,7 @@ Prism is a **stdio-based MCP server** that manages persistent agent memory. Here
|
|
|
883
953
|
```
|
|
884
954
|
┌──────────────────────────────────────────────────────────┐
|
|
885
955
|
│ MCP Client (Claude Desktop / Cursor / Antigravity) │
|
|
886
|
-
│ ↕ stdio (JSON-RPC)
|
|
956
|
+
│ ↕ stdio / SSE (JSON-RPC) │
|
|
887
957
|
├──────────────────────────────────────────────────────────┤
|
|
888
958
|
│ Prism MCP Server │
|
|
889
959
|
│ │
|
|
@@ -894,8 +964,17 @@ Prism is a **stdio-based MCP server** that manages persistent agent memory. Here
|
|
|
894
964
|
│ └──────┬───────┘ └──────────────┘ └────────────────┘ │
|
|
895
965
|
│ ↕ │
|
|
896
966
|
│ ┌────────────────────────────────────────────────────┐ │
|
|
967
|
+
│ │ Cognitive Engine (v7.8) │ │
|
|
968
|
+
│ │ • ACT-R Spreading Activation (multi-hop) │ │
|
|
969
|
+
│ │ • Episodic → Semantic Consolidation (Hebbian) │ │
|
|
970
|
+
│ │ • Uncertainty-Aware Rejection Gate │ │
|
|
971
|
+
│ │ • Dynamic Fast Weight Decay (dual-rate) │ │
|
|
972
|
+
│ │ • HDC Cognitive Routing (XOR binding) │ │
|
|
973
|
+
│ └──────┬─────────────────────────────────────────────┘ │
|
|
974
|
+
│ ↕ │
|
|
975
|
+
│ ┌────────────────────────────────────────────────────┐ │
|
|
897
976
|
│ │ Storage Engine │ │
|
|
898
|
-
│ │ Local: SQLite + FTS5 + TurboQuant
|
|
977
|
+
│ │ Local: SQLite + FTS5 + TurboQuant + semantic_knowledge │
|
|
899
978
|
│ │ Cloud: Supabase + pgvector │ │
|
|
900
979
|
│ └────────────────────────────────────────────────────┘ │
|
|
901
980
|
│ ↕ │
|
|
@@ -924,6 +1003,8 @@ Prism is a **stdio-based MCP server** that manages persistent agent memory. Here
|
|
|
924
1003
|
|-------|-----------|---------|
|
|
925
1004
|
| **Session Ledger** | SQLite (append-only) | Immutable audit trail of all agent work |
|
|
926
1005
|
| **Handoff State** | SQLite (upsert, versioned) | Live project context with OCC + CRDT merging |
|
|
1006
|
+
| **Semantic Knowledge** | SQLite (`semantic_knowledge`) | Hebbian-style distilled rules with confidence scoring |
|
|
1007
|
+
| **Memory Links** | SQLite (`memory_links`) | Causal graph edges (`caused_by`, `led_to`, `synthesized_from`) |
|
|
927
1008
|
| **Keyword Search** | FTS5 virtual tables | Zero-dependency full-text search |
|
|
928
1009
|
| **Semantic Search** | TurboQuant compressed vectors | 10× compressed 768-dim embeddings, three-tier retrieval |
|
|
929
1010
|
| **Cloud Sync** | Supabase + pgvector | Optional multi-device/team sync |
|
|
@@ -943,7 +1024,7 @@ All platforms benefit from the **server-side fallback** (v5.2.1): if `session_lo
|
|
|
943
1024
|
|
|
944
1025
|
## 🧬 Scientific Foundation
|
|
945
1026
|
|
|
946
|
-
Prism
|
|
1027
|
+
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.
|
|
947
1028
|
|
|
948
1029
|
| Phase | Feature | Inspired By | Status |
|
|
949
1030
|
|-------|---------|-------------|--------|
|
|
@@ -962,35 +1043,42 @@ Prism is evolving from smart session logging toward a **cognitive memory archite
|
|
|
962
1043
|
| **v6.1** | Prism-Port Vault Export — Obsidian/Logseq `.zip` with YAML frontmatter & `[[Wikilinks]]` | Data sovereignty, PKM interop | ✅ Shipped |
|
|
963
1044
|
| **v6.1** | Cognitive Load & Semantic Search — dynamic graph thinning, search highlights | Contextual working memory | ✅ Shipped |
|
|
964
1045
|
| **v6.2** | Synthesize & Prune — automated edge synthesis, graph pruning, SLO observability | Implicit associative memory | ✅ Shipped |
|
|
965
|
-
| **v7.0** | ACT-R Base-Level Activation — `B_i = ln(Σ t_j^(-d))` recency×frequency re-ranking over similarity candidates | Anderson's ACT-R (Adaptive Control of Thought—Rational
|
|
1046
|
+
| **v7.0** | ACT-R Base-Level Activation — `B_i = ln(Σ t_j^(-d))` recency×frequency re-ranking over similarity candidates | Anderson's ACT-R (Adaptive Control of Thought—Rational) | ✅ Shipped |
|
|
966
1047
|
| **v7.0** | Candidate-Scoped Spreading Activation — `S_i = Σ(W × strength)` bounded to search result set; prevents God-node dominance | Spreading activation networks (Collins & Loftus, 1975) | ✅ Shipped |
|
|
967
1048
|
| **v7.0** | Composite Retrieval Scoring — `0.7 × similarity + 0.3 × σ(activation)`; configurable via `PRISM_ACTR_WEIGHT_*` | Hybrid cognitive-neural retrieval models | ✅ Shipped |
|
|
968
1049
|
| **v7.0** | AccessLogBuffer — in-memory batch-write buffer with 5s flush; prevents SQLite `SQLITE_BUSY` under parallel agents | Production reliability engineering | ✅ Shipped |
|
|
969
1050
|
| **v7.3** | Dark Factory — 3-gate fail-closed EXECUTE pipeline (parse → type → scope) with structured JSON action contract | Industrial safety systems (defense-in-depth, fail-closed valves) | ✅ Shipped |
|
|
970
1051
|
| **v7.2** | Verification-first harness — spec-freeze contract, rubric hash lock, multi-layer assertions, CLI `verify` commands | Programmatic verification systems + adversarial validation loops | ✅ Shipped |
|
|
971
1052
|
| **v7.4** | Adversarial Evaluation — PLAN_CONTRACT + EVALUATE with isolated generator/evaluator roles, pre-committed rubrics, and evidence-bound findings | Anti-sycophancy research, adversarial ML evaluation frameworks | ✅ Shipped |
|
|
972
|
-
| **v7.5** | Intent Health Dashboard — 3-signal scoring
|
|
1053
|
+
| **v7.5** | Intent Health Dashboard — 3-signal scoring (staleness × TODO × decisions) with NaN guards and score ceiling | Production observability, proactive monitoring | ✅ Shipped |
|
|
1054
|
+
| **v7.7** | Cloud-Native SSE Transport — full network-accessible MCP server via Server-Sent Events | Distributed systems, cloud-native architecture | ✅ Shipped |
|
|
1055
|
+
| **v7.8** | Episodic→Semantic Consolidation — raw event logs distilled into `semantic_knowledge` rules with confidence scoring and instance tracking | Hebbian learning ("neurons that fire together wire together"), sleep consolidation (neuroscience) | ✅ Shipped |
|
|
1056
|
+
| **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 |
|
|
1057
|
+
| **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 |
|
|
1058
|
+
| **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 |
|
|
973
1059
|
| **v7.x** | Affect-Tagged Memory — sentiment shapes what gets recalled | Affect-modulated retrieval (neuroscience) | 🔭 Horizon |
|
|
974
1060
|
| **v8+** | Zero-Search Retrieval — no index, no ANN, just ask the vector | Holographic Reduced Representations | 🔭 Horizon |
|
|
975
1061
|
|
|
976
|
-
> Informed by LeCun's "Why AI Systems Don't Learn" (Dupoux, LeCun, Malik)
|
|
1062
|
+
> 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).
|
|
977
1063
|
|
|
978
1064
|
---
|
|
979
1065
|
|
|
980
1066
|
## 📦 Milestones & Roadmap
|
|
981
1067
|
|
|
982
|
-
> **Current: v7.
|
|
1068
|
+
> **Current: v7.8.0** — Cognitive Architecture ([CHANGELOG](CHANGELOG.md))
|
|
983
1069
|
|
|
984
1070
|
| Release | Headline |
|
|
985
1071
|
|---------|----------|
|
|
986
|
-
| **v7.
|
|
987
|
-
| **v7.
|
|
988
|
-
| **v7.
|
|
989
|
-
| **v7.
|
|
990
|
-
| **v7.
|
|
991
|
-
| **v7.
|
|
992
|
-
| **
|
|
993
|
-
| **
|
|
1072
|
+
| **v7.8** | 🧠 Cognitive Architecture — Hebbian consolidation, multi-hop reasoning, rejection gate, dynamic decay |
|
|
1073
|
+
| **v7.7** | 🌐 Cloud-Native SSE Transport |
|
|
1074
|
+
| **v7.5** | 🩺 Intent Health Dashboard + Security Hardening |
|
|
1075
|
+
| **v7.4** | ⚔️ Adversarial Evaluation (anti-sycophancy) |
|
|
1076
|
+
| **v7.3** | 🏭 Dark Factory fail-closed execution |
|
|
1077
|
+
| **v7.2** | ✅ Verification Harness |
|
|
1078
|
+
| **v7.1** | 🚦 Task Router |
|
|
1079
|
+
| **v7.0** | 🧬 ACT-R Activation Memory |
|
|
1080
|
+
| **v6.5** | 🔮 HDC Cognitive Routing |
|
|
1081
|
+
| **v6.2** | 🧩 Synthesize & Prune |
|
|
994
1082
|
|
|
995
1083
|
### Future Tracks
|
|
996
1084
|
- **v7.x: Affect-Tagged Memory** — Recall prioritization improves by weighting memories with affective/contextual valence.
|
|
@@ -1037,4 +1125,4 @@ MIT
|
|
|
1037
1125
|
|
|
1038
1126
|
---
|
|
1039
1127
|
|
|
1040
|
-
<sub>**Keywords:** MCP server, Model Context Protocol, Claude Desktop memory, persistent session memory, AI agent memory, local-first, SQLite MCP, Mind Palace, time travel, visual memory, VLM image captioning, OpenTelemetry, GDPR, agent telepathy, multi-agent sync, behavioral memory, cursorrules, Ollama MCP, Brave Search MCP, TurboQuant, progressive context loading, knowledge management, LangChain retriever, LangGraph agent</sub>
|
|
1128
|
+
<sub>**Keywords:** MCP server, Model Context Protocol, Claude Desktop memory, persistent session memory, AI agent memory, cognitive architecture, ACT-R spreading activation, Hebbian learning, episodic semantic consolidation, multi-hop reasoning, uncertainty rejection gate, local-first, SQLite MCP, Mind Palace, time travel, visual memory, VLM image captioning, OpenTelemetry, GDPR, agent telepathy, multi-agent sync, behavioral memory, cursorrules, Ollama MCP, Brave Search MCP, TurboQuant, progressive context loading, knowledge management, LangChain retriever, LangGraph agent</sub>
|
package/dist/dashboard/server.js
CHANGED
|
@@ -21,7 +21,7 @@ import * as path from "path";
|
|
|
21
21
|
import * as os from "os";
|
|
22
22
|
import * as fs from "fs";
|
|
23
23
|
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
24
|
-
import { createServer } from "../server.js";
|
|
24
|
+
import { createServer, getAllPossibleTools } from "../server.js";
|
|
25
25
|
import { getStorage } from "../storage/index.js";
|
|
26
26
|
import { PRISM_USER_ID, SERVER_CONFIG } from "../config.js";
|
|
27
27
|
import { renderDashboardHTML } from "./ui.js";
|
|
@@ -205,6 +205,102 @@ return false;}
|
|
|
205
205
|
});
|
|
206
206
|
return res.end(JSON.stringify({ ok: true }));
|
|
207
207
|
}
|
|
208
|
+
// ─── API: Smithery MCP Server Card (v7.4) ───
|
|
209
|
+
// Smithery explicitly requests this file to skip slow stdio scanning.
|
|
210
|
+
// MUST BE PLACED HERE (above Auth Gate) so it is a fully public manifest.
|
|
211
|
+
if (reqUrl.pathname === "/.well-known/mcp/server-card.json" && req.method === "GET") {
|
|
212
|
+
try {
|
|
213
|
+
const tools = getAllPossibleTools();
|
|
214
|
+
res.writeHead(200, {
|
|
215
|
+
"Content-Type": "application/json",
|
|
216
|
+
"Access-Control-Allow-Origin": "*",
|
|
217
|
+
"Cache-Control": "public, max-age=3600"
|
|
218
|
+
});
|
|
219
|
+
return res.end(JSON.stringify({
|
|
220
|
+
$schema: "https://smithery.ai/schema/server-card.json",
|
|
221
|
+
serverInfo: {
|
|
222
|
+
name: SERVER_CONFIG.name,
|
|
223
|
+
version: SERVER_CONFIG.version,
|
|
224
|
+
},
|
|
225
|
+
authentication: {
|
|
226
|
+
required: false
|
|
227
|
+
},
|
|
228
|
+
configSchema: {
|
|
229
|
+
type: "object",
|
|
230
|
+
required: [],
|
|
231
|
+
properties: {
|
|
232
|
+
braveApiKey: {
|
|
233
|
+
type: "string",
|
|
234
|
+
title: "Brave Search API Key",
|
|
235
|
+
description: "API key for Brave Search (powers web search and research tools). Get one at https://brave.com/search/api/"
|
|
236
|
+
},
|
|
237
|
+
googleApiKey: {
|
|
238
|
+
type: "string",
|
|
239
|
+
title: "Google AI API Key",
|
|
240
|
+
description: "API key for Google AI Studio / Gemini (powers synthesis, embeddings, paper analysis). Get one at https://aistudio.google.com/apikey"
|
|
241
|
+
},
|
|
242
|
+
storage: {
|
|
243
|
+
type: "string",
|
|
244
|
+
title: "Storage Backend",
|
|
245
|
+
description: "Storage backend: 'local' (SQLite, zero-config) or 'supabase' (PostgreSQL, multi-device)",
|
|
246
|
+
default: "local",
|
|
247
|
+
enum: ["local", "supabase"]
|
|
248
|
+
},
|
|
249
|
+
supabaseUrl: {
|
|
250
|
+
type: "string",
|
|
251
|
+
title: "Supabase URL",
|
|
252
|
+
description: "Your Supabase project URL (required if storage is 'supabase')"
|
|
253
|
+
},
|
|
254
|
+
supabaseServiceKey: {
|
|
255
|
+
type: "string",
|
|
256
|
+
title: "Supabase Service Key",
|
|
257
|
+
description: "Your Supabase service role key (required if storage is 'supabase')"
|
|
258
|
+
},
|
|
259
|
+
firecrawlApiKey: {
|
|
260
|
+
type: "string",
|
|
261
|
+
title: "Firecrawl API Key",
|
|
262
|
+
description: "Optional: API key for Firecrawl (enables Web Scholar pipeline). Get one at https://www.firecrawl.dev/"
|
|
263
|
+
},
|
|
264
|
+
braveAnswersApiKey: {
|
|
265
|
+
type: "string",
|
|
266
|
+
title: "Brave Answers API Key",
|
|
267
|
+
description: "Optional: Separate key for AI-grounded answers (brave_answers tool)"
|
|
268
|
+
},
|
|
269
|
+
prismEnableHivemind: {
|
|
270
|
+
type: "boolean",
|
|
271
|
+
title: "Enable Multi-Agent Hivemind",
|
|
272
|
+
description: "Set to true to enable multi-agent coordination tools.",
|
|
273
|
+
default: false
|
|
274
|
+
},
|
|
275
|
+
prismDarkFactoryEnabled: {
|
|
276
|
+
type: "boolean",
|
|
277
|
+
title: "Enable Dark Factory",
|
|
278
|
+
description: "Set to true to enable autonomous evaluation pipelines.",
|
|
279
|
+
default: false
|
|
280
|
+
},
|
|
281
|
+
prismTaskRouterEnabled: {
|
|
282
|
+
type: "boolean",
|
|
283
|
+
title: "Enable Task Router",
|
|
284
|
+
description: "Set to true to allow Prism to route tasks to local Ollama agents.",
|
|
285
|
+
default: false
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
tools: tools.map(t => ({
|
|
290
|
+
name: t.name,
|
|
291
|
+
description: t.description,
|
|
292
|
+
inputSchema: t.inputSchema
|
|
293
|
+
})),
|
|
294
|
+
prompts: [],
|
|
295
|
+
resources: []
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
console.error("[Dashboard] Error generating server card:", err);
|
|
300
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
301
|
+
return res.end(JSON.stringify({ error: err.message || "Failed to generate server card" }));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
208
304
|
// ─── v5.1: Auth gate — block unauthenticated requests ───
|
|
209
305
|
if (AUTH_ENABLED && !isAuthenticated(req, authConfig)) {
|
|
210
306
|
// For API calls, return 401 JSON
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apply ACT-R inspired Spreading Activation, Lateral Inhibition, and Fan Effect.
|
|
3
|
+
* It traverses the `memory_links` table over T iterations starting from the given anchors.
|
|
4
|
+
*/
|
|
5
|
+
export async function applySpreadingActivation(db, anchors, options, userId) {
|
|
6
|
+
if (!options.enabled || anchors.length === 0)
|
|
7
|
+
return anchors;
|
|
8
|
+
const T = options.iterations ?? 3;
|
|
9
|
+
const S = options.spreadFactor ?? 0.8;
|
|
10
|
+
const softM = 20; // Soft lateral inhibition during propagation
|
|
11
|
+
const finalM = options.lateralInhibition ?? 7; // Final hard lateral inhibition
|
|
12
|
+
// State: current activation score for nodes.
|
|
13
|
+
let activeNodes = new Map();
|
|
14
|
+
for (const anchor of anchors) {
|
|
15
|
+
activeNodes.set(anchor.id, anchor.similarity || 1.0);
|
|
16
|
+
}
|
|
17
|
+
for (let t = 0; t < T; t++) {
|
|
18
|
+
const nextNodes = new Map();
|
|
19
|
+
// Preserve existing activation: a_i^(t+1) = a_i^(t) + incoming
|
|
20
|
+
for (const [id, score] of activeNodes.entries()) {
|
|
21
|
+
nextNodes.set(id, score);
|
|
22
|
+
}
|
|
23
|
+
const currentIds = Array.from(activeNodes.keys());
|
|
24
|
+
if (currentIds.length === 0)
|
|
25
|
+
break;
|
|
26
|
+
const placeholders = currentIds.map(() => '?').join(',');
|
|
27
|
+
// Fetch edges connected to active nodes with LIMIT to prevent explosion on hub nodes.
|
|
28
|
+
const edgeQuery = `
|
|
29
|
+
SELECT source_id, target_id, strength
|
|
30
|
+
FROM memory_links
|
|
31
|
+
WHERE source_id IN (${placeholders}) OR target_id IN (${placeholders})
|
|
32
|
+
LIMIT 200
|
|
33
|
+
`;
|
|
34
|
+
const edgeArgs = [...currentIds, ...currentIds];
|
|
35
|
+
const edgeRes = await db.execute({ sql: edgeQuery, args: edgeArgs });
|
|
36
|
+
// Compute out-degree (Fan Effect) directly from fetched edge rows
|
|
37
|
+
// instead of a separate SQL round-trip — halves query count per iteration.
|
|
38
|
+
const fanMap = new Map();
|
|
39
|
+
for (const row of edgeRes.rows) {
|
|
40
|
+
const src = row.source_id;
|
|
41
|
+
if (activeNodes.has(src)) {
|
|
42
|
+
fanMap.set(src, (fanMap.get(src) || 0) + 1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
for (const row of edgeRes.rows) {
|
|
46
|
+
const source = row.source_id;
|
|
47
|
+
const target = row.target_id;
|
|
48
|
+
const strength = Number(row.strength);
|
|
49
|
+
// Forward flow: Source is active, flows to Target
|
|
50
|
+
if (activeNodes.has(source)) {
|
|
51
|
+
const fan = fanMap.get(source) || 1;
|
|
52
|
+
// Dampened fan effect: instead of strict 1/fan, we use 1 / ln(fan + e)
|
|
53
|
+
const dampedFan = Math.log(fan + Math.E);
|
|
54
|
+
const flow = S * (strength * activeNodes.get(source) / dampedFan);
|
|
55
|
+
nextNodes.set(target, (nextNodes.get(target) || 0) + flow);
|
|
56
|
+
}
|
|
57
|
+
// Backward flow: Target is active, flows backward to Source with a heavier penalty
|
|
58
|
+
if (activeNodes.has(target)) {
|
|
59
|
+
const flow = (S * 0.5) * (strength * activeNodes.get(target));
|
|
60
|
+
nextNodes.set(source, (nextNodes.get(source) || 0) + flow);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Soft lateral inhibition: Keep only top softM candidates to prevent explosion
|
|
64
|
+
const sorted = Array.from(nextNodes.entries()).sort((a, b) => b[1] - a[1]);
|
|
65
|
+
activeNodes = new Map(sorted.slice(0, softM));
|
|
66
|
+
}
|
|
67
|
+
// Final evaluation
|
|
68
|
+
const finalIds = Array.from(activeNodes.keys()).slice(0, finalM);
|
|
69
|
+
const anchorMap = new Map();
|
|
70
|
+
for (const a of anchors)
|
|
71
|
+
anchorMap.set(a.id, a);
|
|
72
|
+
const finalResults = [];
|
|
73
|
+
const missingIds = finalIds.filter(id => !anchorMap.has(id));
|
|
74
|
+
if (missingIds.length > 0) {
|
|
75
|
+
const placeholders = missingIds.map(() => '?').join(',');
|
|
76
|
+
const missingQuery = `
|
|
77
|
+
SELECT id, project, summary, session_date, decisions, files_changed
|
|
78
|
+
FROM session_ledger
|
|
79
|
+
WHERE id IN (${placeholders}) AND deleted_at IS NULL AND user_id = ?
|
|
80
|
+
`;
|
|
81
|
+
const missingRes = await db.execute({ sql: missingQuery, args: [...missingIds, userId] });
|
|
82
|
+
for (const row of missingRes.rows) {
|
|
83
|
+
anchorMap.set(row.id, {
|
|
84
|
+
id: row.id,
|
|
85
|
+
project: row.project,
|
|
86
|
+
summary: row.summary,
|
|
87
|
+
session_date: row.session_date,
|
|
88
|
+
decisions: row.decisions && typeof row.decisions === 'string' ? JSON.parse(row.decisions) : undefined,
|
|
89
|
+
files_changed: row.files_changed && typeof row.files_changed === 'string' ? JSON.parse(row.files_changed) : undefined,
|
|
90
|
+
similarity: 0.0 // Base similarity is 0 since it wasn't matched originally
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Compute Hybrid Score and return M nodes
|
|
95
|
+
for (const id of finalIds) {
|
|
96
|
+
if (anchorMap.has(id)) {
|
|
97
|
+
const node = anchorMap.get(id);
|
|
98
|
+
const activationScore = activeNodes.get(id) || 0;
|
|
99
|
+
node.activationScore = activationScore;
|
|
100
|
+
// Hybrid blend: 70% original match relevance, 30% activation structural energy
|
|
101
|
+
node.hybridScore = (node.similarity * 0.7) + (activationScore * 0.3);
|
|
102
|
+
finalResults.push(node);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Sort descending by Hybrid Score
|
|
106
|
+
return finalResults.sort((a, b) => (b.hybridScore || 0) - (a.hybridScore || 0));
|
|
107
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -254,7 +254,18 @@ export function notifyResourceUpdate(project, server) {
|
|
|
254
254
|
* - resources: memory://{project}/handoff — paperclip-attachable session state
|
|
255
255
|
* with subscribe support for live refresh
|
|
256
256
|
*/
|
|
257
|
-
export function
|
|
257
|
+
export function getAllPossibleTools() {
|
|
258
|
+
return [
|
|
259
|
+
...BASE_TOOLS,
|
|
260
|
+
...buildSessionMemoryTools([]),
|
|
261
|
+
...AGENT_REGISTRY_TOOLS,
|
|
262
|
+
SESSION_TASK_ROUTE_TOOL,
|
|
263
|
+
SESSION_START_PIPELINE_TOOL,
|
|
264
|
+
SESSION_CHECK_PIPELINE_STATUS_TOOL,
|
|
265
|
+
SESSION_ABORT_PIPELINE_TOOL
|
|
266
|
+
];
|
|
267
|
+
}
|
|
268
|
+
export function getAvailableTools() {
|
|
258
269
|
// ─── v4.1 FIX: Auto-Load via Dynamic Tool Descriptions ────────
|
|
259
270
|
// Read auto-load projects EXCLUSIVELY from dashboard config
|
|
260
271
|
// (available after initConfigStorage() in startServer).
|
|
@@ -273,21 +284,17 @@ export function createServer() {
|
|
|
273
284
|
if (autoloadList.length > 0) {
|
|
274
285
|
console.error(`[Prism] Auto-load projects (dashboard): ${autoloadList.join(', ')}`);
|
|
275
286
|
}
|
|
276
|
-
// Build the dynamic tool list with auto-load instruction injected
|
|
277
287
|
const SESSION_MEMORY_TOOLS = buildSessionMemoryTools(autoloadList);
|
|
278
|
-
|
|
279
|
-
// can enumerate the full capability set. Runtime guards in the CallTool handler
|
|
280
|
-
// still prevent execution without valid Supabase credentials.
|
|
281
|
-
const ALL_TOOLS = [
|
|
288
|
+
return [
|
|
282
289
|
...BASE_TOOLS,
|
|
283
290
|
...SESSION_MEMORY_TOOLS,
|
|
284
|
-
// v3.0: Agent Hivemind tools — only when PRISM_ENABLE_HIVEMIND=true
|
|
285
291
|
...(PRISM_ENABLE_HIVEMIND ? AGENT_REGISTRY_TOOLS : []),
|
|
286
|
-
// v7.1: Task Router tool — only when PRISM_TASK_ROUTER_ENABLED=true
|
|
287
292
|
...(getSettingSync("task_router_enabled", String(PRISM_TASK_ROUTER_ENABLED_ENV)) === "true" ? [SESSION_TASK_ROUTE_TOOL] : []),
|
|
288
|
-
// v7.3: Dark Factory pipeline tools — only when PRISM_DARK_FACTORY_ENABLED=true
|
|
289
293
|
...(PRISM_DARK_FACTORY_ENABLED ? [SESSION_START_PIPELINE_TOOL, SESSION_CHECK_PIPELINE_STATUS_TOOL, SESSION_ABORT_PIPELINE_TOOL] : []),
|
|
290
294
|
];
|
|
295
|
+
}
|
|
296
|
+
export function createServer() {
|
|
297
|
+
const ALL_TOOLS = getAvailableTools();
|
|
291
298
|
const server = new Server({
|
|
292
299
|
name: SERVER_CONFIG.name,
|
|
293
300
|
version: SERVER_CONFIG.version,
|
package/dist/storage/sqlite.js
CHANGED
|
@@ -516,6 +516,42 @@ export class SqliteStorage {
|
|
|
516
516
|
vector BLOB NOT NULL
|
|
517
517
|
)
|
|
518
518
|
`);
|
|
519
|
+
// ─── Phase 4 Migration: Semantic Knowledge Table ──────────────
|
|
520
|
+
//
|
|
521
|
+
// REVIEWER NOTE: Created to separate timeless semantic facts from
|
|
522
|
+
// chronological episodic ledger events, per Phase 4 consolidation.
|
|
523
|
+
await this.db.execute(`
|
|
524
|
+
CREATE TABLE IF NOT EXISTS semantic_knowledge (
|
|
525
|
+
id TEXT PRIMARY KEY,
|
|
526
|
+
project TEXT NOT NULL,
|
|
527
|
+
user_id TEXT NOT NULL DEFAULT '',
|
|
528
|
+
concept TEXT NOT NULL,
|
|
529
|
+
description TEXT NOT NULL DEFAULT '',
|
|
530
|
+
confidence REAL DEFAULT 0.5,
|
|
531
|
+
instances INTEGER DEFAULT 1,
|
|
532
|
+
related_entities TEXT DEFAULT '[]',
|
|
533
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
534
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
535
|
+
)
|
|
536
|
+
`);
|
|
537
|
+
// v7.8 Migration: Rename legacy columns if upgrading from older schema
|
|
538
|
+
try {
|
|
539
|
+
await this.db.execute(`ALTER TABLE semantic_knowledge RENAME COLUMN rule TO description`);
|
|
540
|
+
}
|
|
541
|
+
catch { /* column already renamed or doesn't exist */ }
|
|
542
|
+
try {
|
|
543
|
+
await this.db.execute(`ALTER TABLE semantic_knowledge ADD COLUMN instances INTEGER DEFAULT 1`);
|
|
544
|
+
}
|
|
545
|
+
catch { /* column already exists */ }
|
|
546
|
+
try {
|
|
547
|
+
await this.db.execute(`ALTER TABLE semantic_knowledge ADD COLUMN related_entities TEXT DEFAULT '[]'`);
|
|
548
|
+
}
|
|
549
|
+
catch { /* column already exists */ }
|
|
550
|
+
try {
|
|
551
|
+
await this.db.execute(`ALTER TABLE semantic_knowledge ADD COLUMN updated_at TEXT DEFAULT (datetime('now'))`);
|
|
552
|
+
}
|
|
553
|
+
catch { /* column already exists */ }
|
|
554
|
+
await this.db.execute(`CREATE INDEX IF NOT EXISTS idx_semantic_project ON semantic_knowledge(project)`);
|
|
519
555
|
// v6.5 Migration: Add prng_version column if missing
|
|
520
556
|
try {
|
|
521
557
|
await this.db.execute(`ALTER TABLE hdc_dictionary ADD COLUMN prng_version INTEGER DEFAULT 1`);
|
|
@@ -1341,6 +1377,21 @@ export class SqliteStorage {
|
|
|
1341
1377
|
last_accessed_at: r.last_accessed_at,
|
|
1342
1378
|
relevance: r.relevance,
|
|
1343
1379
|
}));
|
|
1380
|
+
if (params.activation?.enabled) {
|
|
1381
|
+
// Normalise FTS5 ranks (more negative = better) into positive 0-1 similarity score approximation
|
|
1382
|
+
const mappedAnchors = results.map(r => ({
|
|
1383
|
+
id: r.id,
|
|
1384
|
+
project: r.project,
|
|
1385
|
+
summary: r.summary,
|
|
1386
|
+
similarity: 1.0 / (1.0 + Math.abs(r.relevance || 0)),
|
|
1387
|
+
session_date: r.session_date,
|
|
1388
|
+
decisions: r.decisions,
|
|
1389
|
+
files_changed: r.files_changed,
|
|
1390
|
+
}));
|
|
1391
|
+
const { applySpreadingActivation } = await import("../memory/spreadingActivation.js");
|
|
1392
|
+
const activated = await applySpreadingActivation(this.db, mappedAnchors, params.activation, params.userId);
|
|
1393
|
+
return { count: activated.length, results: activated };
|
|
1394
|
+
}
|
|
1344
1395
|
return { count: results.length, results };
|
|
1345
1396
|
}
|
|
1346
1397
|
catch (err) {
|
|
@@ -1399,6 +1450,21 @@ export class SqliteStorage {
|
|
|
1399
1450
|
importance: r.importance,
|
|
1400
1451
|
last_accessed_at: r.last_accessed_at,
|
|
1401
1452
|
}));
|
|
1453
|
+
if (params.activation?.enabled) {
|
|
1454
|
+
// Base similarity mapped as 1.0 for LIKE exact/partial matches
|
|
1455
|
+
const mappedAnchors = results.map(r => ({
|
|
1456
|
+
id: r.id,
|
|
1457
|
+
project: r.project,
|
|
1458
|
+
summary: r.summary,
|
|
1459
|
+
similarity: 1.0,
|
|
1460
|
+
session_date: r.session_date,
|
|
1461
|
+
decisions: r.decisions,
|
|
1462
|
+
files_changed: r.files_changed,
|
|
1463
|
+
}));
|
|
1464
|
+
const { applySpreadingActivation } = await import("../memory/spreadingActivation.js");
|
|
1465
|
+
const activated = await applySpreadingActivation(this.db, mappedAnchors, params.activation, params.userId);
|
|
1466
|
+
return { count: activated.length, results: activated };
|
|
1467
|
+
}
|
|
1402
1468
|
return { count: results.length, results };
|
|
1403
1469
|
}
|
|
1404
1470
|
async searchMemory(params) {
|
|
@@ -1424,7 +1490,7 @@ export class SqliteStorage {
|
|
|
1424
1490
|
args.push(params.limit);
|
|
1425
1491
|
const sql = `
|
|
1426
1492
|
SELECT l.id, l.project, l.summary, l.decisions, l.files_changed,
|
|
1427
|
-
l.session_date, l.created_at,
|
|
1493
|
+
l.session_date, l.created_at, l.is_rollup, l.importance, l.last_accessed_at,
|
|
1428
1494
|
(1 - vector_distance_cos(l.embedding, vector(?))) AS similarity
|
|
1429
1495
|
FROM session_ledger l
|
|
1430
1496
|
WHERE ${conditions.join(" AND ")}
|
|
@@ -1433,7 +1499,7 @@ export class SqliteStorage {
|
|
|
1433
1499
|
`;
|
|
1434
1500
|
const result = await this.db.execute({ sql, args });
|
|
1435
1501
|
// Filter by similarity threshold and format results
|
|
1436
|
-
|
|
1502
|
+
const baseResults = result.rows
|
|
1437
1503
|
.filter(r => r.similarity >= params.similarityThreshold)
|
|
1438
1504
|
.map(r => ({
|
|
1439
1505
|
id: r.id,
|
|
@@ -1443,7 +1509,15 @@ export class SqliteStorage {
|
|
|
1443
1509
|
session_date: (r.session_date || r.created_at),
|
|
1444
1510
|
decisions: this.parseJsonColumn(r.decisions),
|
|
1445
1511
|
files_changed: this.parseJsonColumn(r.files_changed),
|
|
1512
|
+
is_rollup: Boolean(r.is_rollup),
|
|
1513
|
+
importance: r.importance ?? 0,
|
|
1514
|
+
last_accessed_at: r.last_accessed_at || null,
|
|
1446
1515
|
}));
|
|
1516
|
+
if (params.activation?.enabled) {
|
|
1517
|
+
const { applySpreadingActivation } = await import("../memory/spreadingActivation.js");
|
|
1518
|
+
return applySpreadingActivation(this.db, baseResults, params.activation, params.userId);
|
|
1519
|
+
}
|
|
1520
|
+
return baseResults;
|
|
1447
1521
|
}
|
|
1448
1522
|
catch (err) {
|
|
1449
1523
|
// ─── TIER 2 FALLBACK: Asymmetric TurboQuant search in JS ───
|
|
@@ -1495,7 +1569,8 @@ export class SqliteStorage {
|
|
|
1495
1569
|
}
|
|
1496
1570
|
const fallbackSql = `
|
|
1497
1571
|
SELECT id, project, summary, decisions, files_changed,
|
|
1498
|
-
session_date, created_at,
|
|
1572
|
+
session_date, created_at, is_rollup, importance, last_accessed_at,
|
|
1573
|
+
embedding_compressed, embedding_turbo_radius
|
|
1499
1574
|
FROM session_ledger
|
|
1500
1575
|
WHERE ${fallbackConditions.join(" AND ")}
|
|
1501
1576
|
`;
|
|
@@ -1517,6 +1592,9 @@ export class SqliteStorage {
|
|
|
1517
1592
|
session_date: (row.session_date || row.created_at),
|
|
1518
1593
|
decisions: this.parseJsonColumn(row.decisions),
|
|
1519
1594
|
files_changed: this.parseJsonColumn(row.files_changed),
|
|
1595
|
+
is_rollup: Boolean(row.is_rollup),
|
|
1596
|
+
importance: row.importance ?? 0,
|
|
1597
|
+
last_accessed_at: row.last_accessed_at || null,
|
|
1520
1598
|
});
|
|
1521
1599
|
}
|
|
1522
1600
|
}
|
|
@@ -1526,9 +1604,14 @@ export class SqliteStorage {
|
|
|
1526
1604
|
}
|
|
1527
1605
|
// Sort by similarity descending and limit
|
|
1528
1606
|
scored.sort((a, b) => b.similarity - a.similarity);
|
|
1607
|
+
const baseResults = scored.slice(0, params.limit);
|
|
1529
1608
|
debugLog(`[SqliteStorage] Tier-2 TurboQuant fallback: scored ${fallbackResult.rows.length} entries, ` +
|
|
1530
1609
|
`${scored.length} above threshold`);
|
|
1531
|
-
|
|
1610
|
+
if (params.activation?.enabled) {
|
|
1611
|
+
const { applySpreadingActivation } = await import("../memory/spreadingActivation.js");
|
|
1612
|
+
return applySpreadingActivation(this.db, baseResults, params.activation, params.userId);
|
|
1613
|
+
}
|
|
1614
|
+
return baseResults;
|
|
1532
1615
|
}
|
|
1533
1616
|
catch (fallbackErr) {
|
|
1534
1617
|
// Both tiers failed — return empty
|
|
@@ -3090,4 +3173,39 @@ export class SqliteStorage {
|
|
|
3090
3173
|
override_reason: row.override_reason || undefined
|
|
3091
3174
|
};
|
|
3092
3175
|
}
|
|
3176
|
+
// ─── v7.5: Semantic Consolidation ────────────────────────────────
|
|
3177
|
+
async upsertSemanticKnowledge(data) {
|
|
3178
|
+
const existing = await this.db.execute({
|
|
3179
|
+
sql: `SELECT id, instances, confidence FROM semantic_knowledge WHERE project = ? AND concept = ? LIMIT 1`,
|
|
3180
|
+
args: [data.project, data.concept]
|
|
3181
|
+
});
|
|
3182
|
+
if (existing.rows.length > 0) {
|
|
3183
|
+
const row = existing.rows[0];
|
|
3184
|
+
const newConfidence = Math.min(1.0, row.confidence + 0.1);
|
|
3185
|
+
await this.db.execute({
|
|
3186
|
+
sql: `UPDATE semantic_knowledge SET instances = instances + 1, confidence = ?, updated_at = ? WHERE id = ?`,
|
|
3187
|
+
args: [newConfidence, new Date().toISOString(), row.id]
|
|
3188
|
+
});
|
|
3189
|
+
return row.id;
|
|
3190
|
+
}
|
|
3191
|
+
else {
|
|
3192
|
+
const id = crypto.randomUUID();
|
|
3193
|
+
await this.db.execute({
|
|
3194
|
+
sql: `INSERT INTO semantic_knowledge (id, project, user_id, concept, description, confidence, instances, related_entities, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3195
|
+
args: [
|
|
3196
|
+
id,
|
|
3197
|
+
data.project,
|
|
3198
|
+
data.userId || '',
|
|
3199
|
+
data.concept,
|
|
3200
|
+
data.description,
|
|
3201
|
+
0.5,
|
|
3202
|
+
1,
|
|
3203
|
+
JSON.stringify(data.related_entities || []),
|
|
3204
|
+
new Date().toISOString(),
|
|
3205
|
+
new Date().toISOString()
|
|
3206
|
+
]
|
|
3207
|
+
});
|
|
3208
|
+
return id;
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3093
3211
|
}
|
package/dist/storage/supabase.js
CHANGED
|
@@ -1440,4 +1440,10 @@ export class SupabaseStorage {
|
|
|
1440
1440
|
throw e;
|
|
1441
1441
|
}
|
|
1442
1442
|
}
|
|
1443
|
+
// ─── v7.5: Semantic Consolidation ────────────────────────────────
|
|
1444
|
+
async upsertSemanticKnowledge(data) {
|
|
1445
|
+
// For now we just implement graceful degradation/no-op on Supabase until the SQL is deployed.
|
|
1446
|
+
debugLog(`[SupabaseStorage] upsertSemanticKnowledge is not fully implemented in Supabase yet. Skipping for ${data.concept}.`);
|
|
1447
|
+
return crypto.randomUUID();
|
|
1448
|
+
}
|
|
1443
1449
|
}
|
|
@@ -20,27 +20,31 @@ export function isCompactLedgerArgs(args) {
|
|
|
20
20
|
// ─── LLM Summarization ────────────────────────────────────────
|
|
21
21
|
async function summarizeEntries(entries) {
|
|
22
22
|
const llm = getLLMProvider(); // throws if no API key configured
|
|
23
|
-
const entriesText = entries.map((e, i) => `[${i + 1}] ${e.session_date || "unknown date"}: ${e.summary || "no summary"}\n` +
|
|
23
|
+
const entriesText = entries.map((e, i) => `[${i + 1}] ID: ${e.id || "N/A"} | Date: ${e.session_date || "unknown date"}: ${e.summary || "no summary"}\n` +
|
|
24
24
|
(e.decisions?.length ? ` Decisions: ${e.decisions.join("; ")}\n` : "") +
|
|
25
25
|
(e.files_changed?.length ? ` Files: ${e.files_changed.join(", ")}\n` : "")).join("\n");
|
|
26
26
|
const prompt = (`You are compressing a session history log for an AI agent's persistent memory.\n\n` +
|
|
27
|
-
`Analyze these ${entries.length} work sessions and
|
|
28
|
-
`
|
|
29
|
-
`important file changes, error resolutions, and architecture changes. ` +
|
|
30
|
-
`
|
|
31
|
-
`
|
|
32
|
-
`
|
|
33
|
-
`
|
|
34
|
-
`
|
|
35
|
-
`
|
|
27
|
+
`Analyze these ${entries.length} work sessions and output a VALID JSON OBJECT matching this structure:\n` +
|
|
28
|
+
`{\n` +
|
|
29
|
+
` "summary": "Concise paragraph preserving key decisions, important file changes, error resolutions, and architecture changes. Omit routine operations and intermediate debugging steps.",\n` +
|
|
30
|
+
` "principles": [\n` +
|
|
31
|
+
` { "concept": "Brief concept name", "description": "Reusable lesson extracted from sessions", "related_entities": ["tool", "tech"] }\n` +
|
|
32
|
+
` ],\n` +
|
|
33
|
+
` "causal_links": [\n` +
|
|
34
|
+
` { "source_id": "Session ID that caused it", "target_id": "Session ID that was affected", "relation": "led_to" | "caused_by", "reason": "Explanation" }\n` +
|
|
35
|
+
` ]\n` +
|
|
36
|
+
`}\n\n` +
|
|
36
37
|
`Sessions to analyze:\n${entriesText}\n\n` +
|
|
37
|
-
`
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
`Respond ONLY with raw JSON.`).substring(0, 30000);
|
|
39
|
+
const response = await llm.generateText(prompt);
|
|
40
|
+
try {
|
|
41
|
+
const cleanJson = response.replace(/^```json\n?/, "").replace(/\n?```$/, "");
|
|
42
|
+
return JSON.parse(cleanJson);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
debugLog(`[compact_ledger] Failed to parse JSON from LLM: ${err}`);
|
|
46
|
+
return { summary: response, principles: [], causal_links: [] };
|
|
47
|
+
}
|
|
44
48
|
}
|
|
45
49
|
// ─── Main Handler ─────────────────────────────────────────────
|
|
46
50
|
export async function compactLedgerHandler(args) {
|
|
@@ -126,19 +130,32 @@ export async function compactLedgerHandler(args) {
|
|
|
126
130
|
for (let i = 0; i < oldEntries.length; i += COMPACTION_CHUNK_SIZE) {
|
|
127
131
|
chunks.push(oldEntries.slice(i, i + COMPACTION_CHUNK_SIZE));
|
|
128
132
|
}
|
|
129
|
-
let
|
|
133
|
+
let finalSummaryText;
|
|
134
|
+
let finalPrinciples = [];
|
|
135
|
+
let finalCausalLinks = [];
|
|
130
136
|
if (chunks.length === 1) {
|
|
131
|
-
|
|
137
|
+
const res = await summarizeEntries(chunks[0]);
|
|
138
|
+
finalSummaryText = typeof res === 'string' ? res : (res.summary || JSON.stringify(res));
|
|
139
|
+
finalPrinciples = res.principles || [];
|
|
140
|
+
finalCausalLinks = res.causal_links || [];
|
|
132
141
|
}
|
|
133
142
|
else {
|
|
134
143
|
const chunkSummaries = await Promise.all(chunks.map(chunk => summarizeEntries(chunk)));
|
|
144
|
+
chunkSummaries.forEach(s => {
|
|
145
|
+
finalPrinciples.push(...(s.principles || []));
|
|
146
|
+
finalCausalLinks.push(...(s.causal_links || []));
|
|
147
|
+
});
|
|
135
148
|
const metaEntries = chunkSummaries.map((s, i) => ({
|
|
149
|
+
id: `chunk-${i}`,
|
|
136
150
|
session_date: `chunk ${i + 1}`,
|
|
137
|
-
summary: s,
|
|
151
|
+
summary: s.summary,
|
|
138
152
|
decisions: [],
|
|
139
153
|
files_changed: [],
|
|
140
154
|
}));
|
|
141
|
-
|
|
155
|
+
const metaRes = await summarizeEntries(metaEntries);
|
|
156
|
+
finalSummaryText = typeof metaRes === 'string' ? metaRes : (metaRes.summary || JSON.stringify(metaRes));
|
|
157
|
+
finalPrinciples.push(...(metaRes.principles || []));
|
|
158
|
+
finalCausalLinks.push(...(metaRes.causal_links || []));
|
|
142
159
|
}
|
|
143
160
|
// Collect all unique keywords from rolled-up entries
|
|
144
161
|
const allKeywords = [...new Set(oldEntries.flatMap((e) => e.keywords || []))];
|
|
@@ -148,7 +165,7 @@ export async function compactLedgerHandler(args) {
|
|
|
148
165
|
const savedRollup = await storage.saveLedger({
|
|
149
166
|
project: proj,
|
|
150
167
|
user_id: PRISM_USER_ID,
|
|
151
|
-
summary: `[ROLLUP of ${oldEntries.length} sessions] ${
|
|
168
|
+
summary: `[ROLLUP of ${oldEntries.length} sessions] ${finalSummaryText}`,
|
|
152
169
|
keywords: allKeywords,
|
|
153
170
|
files_changed: allFiles,
|
|
154
171
|
decisions: [`Rolled up ${oldEntries.length} sessions on ${new Date().toISOString()}`],
|
|
@@ -173,6 +190,48 @@ export async function compactLedgerHandler(args) {
|
|
|
173
190
|
debugLog(`[compact_ledger] Failed to create spawned_from link for ${rollupId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
174
191
|
}
|
|
175
192
|
}));
|
|
193
|
+
// ── v7.5: Process semantic rules and causal links ──────────
|
|
194
|
+
for (const principle of finalPrinciples) {
|
|
195
|
+
if (!principle.concept || !principle.description)
|
|
196
|
+
continue;
|
|
197
|
+
try {
|
|
198
|
+
const semanticId = await storage.upsertSemanticKnowledge({
|
|
199
|
+
project: proj,
|
|
200
|
+
concept: principle.concept,
|
|
201
|
+
description: principle.description,
|
|
202
|
+
related_entities: principle.related_entities || [],
|
|
203
|
+
userId: PRISM_USER_ID,
|
|
204
|
+
});
|
|
205
|
+
await storage.createLink({
|
|
206
|
+
source_id: rollupId,
|
|
207
|
+
target_id: semanticId,
|
|
208
|
+
link_type: "related_to",
|
|
209
|
+
strength: 0.8,
|
|
210
|
+
metadata: JSON.stringify({ reason: "derived_principle" })
|
|
211
|
+
}, PRISM_USER_ID);
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
debugLog(`[compact_ledger] Failed to upsert semantic knowledge: ${err instanceof Error ? err.message : String(err)}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
for (const link of finalCausalLinks) {
|
|
218
|
+
if (!link.source_id || !link.target_id || !link.relation)
|
|
219
|
+
continue;
|
|
220
|
+
if (link.source_id.startsWith("chunk-") || link.target_id.startsWith("chunk-"))
|
|
221
|
+
continue;
|
|
222
|
+
try {
|
|
223
|
+
await storage.createLink({
|
|
224
|
+
source_id: link.source_id,
|
|
225
|
+
target_id: link.target_id,
|
|
226
|
+
link_type: link.relation,
|
|
227
|
+
strength: 0.9,
|
|
228
|
+
metadata: JSON.stringify({ reason: link.reason || "causal inference during compaction" })
|
|
229
|
+
}, PRISM_USER_ID);
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
debugLog(`[compact_ledger] Failed to create causal link: ${err instanceof Error ? err.message : String(err)}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
176
235
|
}
|
|
177
236
|
// Step 5: Archive old entries (soft-delete)
|
|
178
237
|
for (const entry of oldEntries) {
|
|
@@ -63,7 +63,7 @@ export async function knowledgeSearchHandler(args) {
|
|
|
63
63
|
throw new Error("Invalid arguments for knowledge_search");
|
|
64
64
|
}
|
|
65
65
|
// Phase 1: destructure enable_trace (defaults to false for backward compat)
|
|
66
|
-
const { project, query, category, limit = 10, enable_trace = false } = args;
|
|
66
|
+
const { project, query, category, limit = 10, enable_trace = false, activation } = args;
|
|
67
67
|
debugLog(`[knowledge_search] Searching: project=${project || "all"}, query="${query || ""}", category=${category || "any"}, limit=${limit}`);
|
|
68
68
|
// Phase 1: Capture total start time for latency measurement
|
|
69
69
|
const totalStart = performance.now();
|
|
@@ -79,6 +79,7 @@ export async function knowledgeSearchHandler(args) {
|
|
|
79
79
|
queryText: query || null,
|
|
80
80
|
limit: Math.min(limit, 50),
|
|
81
81
|
userId: PRISM_USER_ID,
|
|
82
|
+
activation,
|
|
82
83
|
});
|
|
83
84
|
const storageMs = performance.now() - storageStart;
|
|
84
85
|
const totalMs = performance.now() - totalStart;
|
|
@@ -121,7 +122,7 @@ export async function knowledgeSearchHandler(args) {
|
|
|
121
122
|
// Mutate results to surface effective importance
|
|
122
123
|
for (const r of data.results) {
|
|
123
124
|
if (typeof r.importance === 'number' && r.importance > 0) {
|
|
124
|
-
r.effective_importance = computeEffectiveImportance(r.importance, r.last_accessed_at, r.created_at);
|
|
125
|
+
r.effective_importance = computeEffectiveImportance(r.importance, r.last_accessed_at, r.created_at, Boolean(r.is_rollup));
|
|
125
126
|
}
|
|
126
127
|
}
|
|
127
128
|
}
|
|
@@ -282,7 +283,7 @@ export async function sessionSearchMemoryHandler(args) {
|
|
|
282
283
|
// When true, a MemoryTrace JSON block is appended as content[1].
|
|
283
284
|
enable_trace = false,
|
|
284
285
|
// v5.2: Context-Weighted Retrieval — biases search toward active work context
|
|
285
|
-
context_boost = false, } = args;
|
|
286
|
+
context_boost = false, activation, } = args;
|
|
286
287
|
debugLog(`[session_search_memory] Semantic search: query="${query}", ` +
|
|
287
288
|
`project=${project || "all"}, limit=${limit}, threshold=${similarity_threshold}` +
|
|
288
289
|
`${context_boost ? ", context_boost=ON" : ""}`);
|
|
@@ -374,6 +375,7 @@ export async function sessionSearchMemoryHandler(args) {
|
|
|
374
375
|
limit: candidateLimit,
|
|
375
376
|
similarityThreshold: similarity_threshold,
|
|
376
377
|
userId: PRISM_USER_ID,
|
|
378
|
+
activation,
|
|
377
379
|
});
|
|
378
380
|
// Phase 1: Capture storage query latency and compute total
|
|
379
381
|
const storageMs = performance.now() - storageStart;
|
|
@@ -444,7 +446,10 @@ export async function sessionSearchMemoryHandler(args) {
|
|
|
444
446
|
const timestamps = accessTimestamps.length > 0
|
|
445
447
|
? accessTimestamps
|
|
446
448
|
: [new Date(r.created_at || now)];
|
|
447
|
-
|
|
449
|
+
// Rollups represent consolidated semantic knowledge over many sessions.
|
|
450
|
+
// They should decay 50% slower than raw episodic chatter to retain long-term context.
|
|
451
|
+
const decayRate = r.is_rollup ? PRISM_ACTR_DECAY * 0.5 : PRISM_ACTR_DECAY;
|
|
452
|
+
const Bi = baseLevelActivation(timestamps, now, decayRate);
|
|
448
453
|
// S_i: Candidate-scoped spreading activation
|
|
449
454
|
const outboundLinks = linksMap.get(id) || [];
|
|
450
455
|
const Si = candidateScopedSpreadingActivation(outboundLinks, candidateIdSet);
|
|
@@ -473,6 +478,29 @@ export async function sessionSearchMemoryHandler(args) {
|
|
|
473
478
|
// This MUST happen after re-ranking but BEFORE recording access events,
|
|
474
479
|
// so we only log access for results actually delivered to the LLM.
|
|
475
480
|
results.splice(limit);
|
|
481
|
+
if (results.length > 0) {
|
|
482
|
+
const topScore = PRISM_ACTR_ENABLED ? results[0]._actr_composite : results[0].similarity;
|
|
483
|
+
const secondScore = results.length > 1 ? (PRISM_ACTR_ENABLED ? results[1]._actr_composite : results[1].similarity) : 0;
|
|
484
|
+
const gapDistance = (topScore || 0) - (secondScore || 0);
|
|
485
|
+
const fallbackThreshold = PRISM_HDC_POLICY_FALLBACK_THRESHOLD || 0.85;
|
|
486
|
+
const clarifyThreshold = PRISM_HDC_POLICY_CLARIFY_THRESHOLD || 0.95;
|
|
487
|
+
const gapThreshold = clarifyThreshold - fallbackThreshold;
|
|
488
|
+
if ((topScore || 0) < fallbackThreshold || (results.length > 1 && gapDistance < gapThreshold)) {
|
|
489
|
+
return {
|
|
490
|
+
content: [{
|
|
491
|
+
type: "text",
|
|
492
|
+
text: JSON.stringify({
|
|
493
|
+
results: [],
|
|
494
|
+
meta: {
|
|
495
|
+
rejected: true,
|
|
496
|
+
reason: `Uncertainty Rejection: topScore (${(topScore || 0).toFixed(3)}) < ${fallbackThreshold} OR gapDistance (${gapDistance.toFixed(3)}) < ${gapThreshold.toFixed(3)}.`
|
|
497
|
+
}
|
|
498
|
+
}, null, 2)
|
|
499
|
+
}],
|
|
500
|
+
isError: false
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
}
|
|
476
504
|
// Fire-and-forget: record access events for the final delivered results
|
|
477
505
|
// v7.0: Writes to both access_log buffer AND legacy last_accessed_at
|
|
478
506
|
const finalIds = results.map((r) => r.id).filter(Boolean);
|
|
@@ -487,7 +515,7 @@ export async function sessionSearchMemoryHandler(args) {
|
|
|
487
515
|
: "N/A";
|
|
488
516
|
// Dynamic importance decay (uses ACT-R internally when enabled)
|
|
489
517
|
const baseImportance = r.importance ?? 0;
|
|
490
|
-
const effectiveImportance = computeEffectiveImportance(baseImportance, r.last_accessed_at, r.created_at);
|
|
518
|
+
const effectiveImportance = computeEffectiveImportance(baseImportance, r.last_accessed_at, r.created_at, Boolean(r.is_rollup));
|
|
491
519
|
const importanceStr = baseImportance > 0
|
|
492
520
|
? ` Importance: ${effectiveImportance}${effectiveImportance !== baseImportance ? ` (base: ${baseImportance}, decayed)` : ""}\n`
|
|
493
521
|
: "";
|
|
@@ -648,7 +648,7 @@ export async function sessionLoadContextHandler(args) {
|
|
|
648
648
|
formattedContext += `\n⏳ Recent Sessions:\n` + d.recent_sessions.map((s) => {
|
|
649
649
|
let impStr = "";
|
|
650
650
|
if (typeof s.importance === 'number' && s.importance > 0) {
|
|
651
|
-
const eff = computeEffectiveImportance(s.importance, s.last_accessed_at, s.created_at);
|
|
651
|
+
const eff = computeEffectiveImportance(s.importance, s.last_accessed_at, s.created_at, Boolean(s.is_rollup));
|
|
652
652
|
impStr = ` [Imp: ${eff}]`;
|
|
653
653
|
}
|
|
654
654
|
return ` [${s.session_date?.split("T")[0]}]${impStr} ${s.summary}`;
|
|
@@ -188,6 +188,16 @@ export const KNOWLEDGE_SEARCH_TOOL = {
|
|
|
188
188
|
description: "If true, returns a separate MEMORY TRACE content block with search strategy, " +
|
|
189
189
|
"latency breakdown, and scoring metadata for explainability. Default: false.",
|
|
190
190
|
},
|
|
191
|
+
activation: {
|
|
192
|
+
type: "object",
|
|
193
|
+
description: "Configuration for ACT-R inspired Spreading Activation. Use this to find structurally related memories beyond direct semantic/keyword hits.",
|
|
194
|
+
properties: {
|
|
195
|
+
enabled: { type: "boolean", description: "Enable spreading activation (default: false)." },
|
|
196
|
+
iterations: { type: "integer", description: "Number of propagation loops (default: 3)." },
|
|
197
|
+
spreadFactor: { type: "number", description: "Decay multiplier per step (default: 0.8)." },
|
|
198
|
+
lateralInhibition: { type: "integer", description: "Maximum nodes returned (default: 7)." },
|
|
199
|
+
}
|
|
200
|
+
},
|
|
191
201
|
},
|
|
192
202
|
required: ["query"],
|
|
193
203
|
},
|
|
@@ -344,6 +354,16 @@ export const SESSION_SEARCH_MEMORY_TOOL = {
|
|
|
344
354
|
"before embedding generation, naturally biasing results toward contextually relevant memories. " +
|
|
345
355
|
"Useful when searching within a specific project context. Default: false.",
|
|
346
356
|
},
|
|
357
|
+
activation: {
|
|
358
|
+
type: "object",
|
|
359
|
+
description: "Configuration for ACT-R inspired Spreading Activation. Use this to find structurally related memories beyond direct semantic/keyword hits.",
|
|
360
|
+
properties: {
|
|
361
|
+
enabled: { type: "boolean", description: "Enable spreading activation (default: false)." },
|
|
362
|
+
iterations: { type: "integer", description: "Number of propagation loops (default: 3)." },
|
|
363
|
+
spreadFactor: { type: "number", description: "Decay multiplier per step (default: 0.8)." },
|
|
364
|
+
lateralInhibition: { type: "integer", description: "Maximum nodes returned (default: 7)." },
|
|
365
|
+
}
|
|
366
|
+
},
|
|
347
367
|
},
|
|
348
368
|
required: ["query"],
|
|
349
369
|
},
|
|
@@ -440,6 +460,8 @@ export function isKnowledgeSearchArgs(args) {
|
|
|
440
460
|
return false;
|
|
441
461
|
if (a.enable_trace !== undefined && typeof a.enable_trace !== "boolean")
|
|
442
462
|
return false;
|
|
463
|
+
if (a.activation !== undefined && typeof a.activation !== "object")
|
|
464
|
+
return false;
|
|
443
465
|
return true;
|
|
444
466
|
}
|
|
445
467
|
export function isSessionSaveLedgerArgs(args) {
|
|
@@ -509,6 +531,8 @@ export function isSessionSearchMemoryArgs(args) {
|
|
|
509
531
|
return false;
|
|
510
532
|
if (a.context_boost !== undefined && typeof a.context_boost !== "boolean")
|
|
511
533
|
return false;
|
|
534
|
+
if (a.activation !== undefined && typeof a.activation !== "object")
|
|
535
|
+
return false;
|
|
512
536
|
return true;
|
|
513
537
|
}
|
|
514
538
|
// ─── v1.5.0: Type guard for backfill embeddings ──────────────
|
|
@@ -46,7 +46,7 @@ import { PRISM_ACTR_ENABLED, PRISM_ACTR_DECAY, PRISM_ACTR_SIGMOID_MIDPOINT, PRIS
|
|
|
46
46
|
* @param createdAtStr ISO string representing creation time (fallback).
|
|
47
47
|
* @returns The effective importance score, rounded to 2 decimal places.
|
|
48
48
|
*/
|
|
49
|
-
export function computeEffectiveImportance(baseImportance, lastAccessedStr, createdAtStr) {
|
|
49
|
+
export function computeEffectiveImportance(baseImportance, lastAccessedStr, createdAtStr, isRollup = false) {
|
|
50
50
|
if (baseImportance <= 0)
|
|
51
51
|
return baseImportance;
|
|
52
52
|
const now = new Date();
|
|
@@ -64,7 +64,8 @@ export function computeEffectiveImportance(baseImportance, lastAccessedStr, crea
|
|
|
64
64
|
// Use a single-timestamp proxy: treat last_accessed_at as one access event.
|
|
65
65
|
// This gives a simplified B_i = ln(t^(-d)) = -d × ln(t)
|
|
66
66
|
// The full multi-timestamp B_i runs in graphHandlers.ts search pipeline.
|
|
67
|
-
const
|
|
67
|
+
const decayRate = isRollup ? PRISM_ACTR_DECAY * 0.5 : PRISM_ACTR_DECAY;
|
|
68
|
+
const Bi = baseLevelActivation([referenceDate], now, decayRate);
|
|
68
69
|
// Normalize to (0, 1) via parameterized sigmoid
|
|
69
70
|
const activationFactor = parameterizedSigmoid(Bi, PRISM_ACTR_SIGMOID_MIDPOINT, PRISM_ACTR_SIGMOID_STEEPNESS);
|
|
70
71
|
// Scale importance by activation factor
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prism-mcp-server",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.8.0",
|
|
4
4
|
"mcpName": "io.github.dcostenco/prism-mcp",
|
|
5
5
|
"description": "The Mind Palace for AI Agents — adversarial evaluation (PLAN_CONTRACT→EVALUATE anti-sycophancy), fail-closed Dark Factory autonomous pipelines (3-gate parse→type→scope), persistent memory (SQLite/Supabase), ACT-R cognitive retrieval, behavioral learning & IDE rules sync, multi-agent Hivemind, time travel, visual dashboard. Zero-config local mode.",
|
|
6
6
|
"module": "index.ts",
|