prism-mcp-server 7.4.0 → 7.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 +83 -40
- package/dist/cli.js +0 -0
- package/dist/dashboard/intentHealth.js +62 -0
- package/dist/dashboard/server.js +35 -1
- package/dist/dashboard/ui.js +117 -11
- package/dist/storage/sqlite.js +24 -0
- package/dist/storage/supabase.js +30 -1
- package/dist/tools/ledgerHandlers.js +13 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
[](https://www.typescriptlang.org/)
|
|
9
9
|
[](CONTRIBUTING.md)
|
|
10
10
|
|
|
11
|
+

|
|
12
|
+
|
|
11
13
|
**Your AI agent forgets everything between sessions. Prism fixes that.**
|
|
12
14
|
|
|
13
15
|
One command. Persistent memory. Local-first by default. Optional cloud power-ups.
|
|
@@ -26,6 +28,7 @@ Works with **Claude Desktop · Claude Code · Cursor · Windsurf · Cline · Gem
|
|
|
26
28
|
- [Setup Guides](#-setup-guides)
|
|
27
29
|
- [Universal Import](#-universal-import-bring-your-history)
|
|
28
30
|
- [What Makes Prism Different](#-what-makes-prism-different)
|
|
31
|
+
- [Data Privacy & Egress](#-data-privacy--egress)
|
|
29
32
|
- [Use Cases](#-use-cases)
|
|
30
33
|
- [What's New](#-whats-new)
|
|
31
34
|
- [How Prism Compares](#-how-prism-compares)
|
|
@@ -33,7 +36,7 @@ Works with **Claude Desktop · Claude Code · Cursor · Windsurf · Cline · Gem
|
|
|
33
36
|
- [Environment Variables](#environment-variables)
|
|
34
37
|
- [Architecture](#architecture)
|
|
35
38
|
- [Scientific Foundation](#-scientific-foundation)
|
|
36
|
-
- [
|
|
39
|
+
- [Milestones & Roadmap](#-milestones--roadmap)
|
|
37
40
|
- [Troubleshooting FAQ](#-troubleshooting-faq)
|
|
38
41
|
|
|
39
42
|
---
|
|
@@ -46,12 +49,23 @@ Every time you start a new conversation with an AI coding assistant, it starts f
|
|
|
46
49
|
|
|
47
50
|
> 📌 **Terminology:** Throughout this doc, **"Prism"** refers to the MCP server and storage 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.
|
|
48
51
|
|
|
49
|
-
|
|
52
|
+
Prism has two pillars:
|
|
53
|
+
|
|
54
|
+
1. **🧠 Persistent Memory** — Memories are ranked like a human brain: recently and frequently accessed context surfaces first, while stale context fades naturally. The result is retrieval quality that no flat vector search can match. *(See [Scientific Foundation](#-scientific-foundation) for the ACT-R math.)*
|
|
55
|
+
|
|
56
|
+
2. **🏭 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).)*
|
|
50
57
|
|
|
51
58
|
---
|
|
52
59
|
|
|
53
60
|
## 🚀 Quick Start
|
|
54
61
|
|
|
62
|
+
### Prerequisites
|
|
63
|
+
|
|
64
|
+
- **Node.js v18+** (v20 LTS recommended; v23.x has [known `npx` quirk](#common-installation-pitfalls))
|
|
65
|
+
- Any MCP-compatible client (Claude Desktop, Cursor, Windsurf, Cline, etc.)
|
|
66
|
+
- No API keys required for core features (see [Capability Matrix](#capability-matrix))
|
|
67
|
+
|
|
68
|
+
### Install
|
|
55
69
|
|
|
56
70
|
Add to your MCP client config (`claude_desktop_config.json`, `.cursor/mcp.json`, etc.):
|
|
57
71
|
|
|
@@ -70,6 +84,8 @@ Add to your MCP client config (`claude_desktop_config.json`, `.cursor/mcp.json`,
|
|
|
70
84
|
|
|
71
85
|
**That's it.** Restart your client. All tools are available. The **Mind Palace Dashboard** (the visual UI for your agent's brain) starts automatically at `http://localhost:3000`. You don't need to keep a tab open — the dashboard runs in the background and the MCP tools work with or without it.
|
|
72
86
|
|
|
87
|
+
> 🔄 **Updating Prism:** `npx -y` caches the package locally. To force an update to the latest version, restart your MCP client — `npx -y` will fetch the newest release automatically. If you're stuck on a stale version, run `npx clear-npx-cache` (or `npm cache clean --force`) before restarting.
|
|
88
|
+
|
|
73
89
|
<details>
|
|
74
90
|
<summary>Port 3000 already in use? (Next.js / Vite / etc.)</summary>
|
|
75
91
|
|
|
@@ -109,6 +125,8 @@ Then open `http://localhost:3001` instead.
|
|
|
109
125
|
|
|
110
126
|
> 🔑 The core Mind Palace works **100% offline** with zero API keys. Cloud keys unlock intelligence features. See [Environment Variables](#environment-variables).
|
|
111
127
|
|
|
128
|
+
> 💰 **API Cost Note:** `GOOGLE_API_KEY` (Gemini) has a generous free tier that covers most individual use. `BRAVE_API_KEY` offers 2,000 free searches/month. `FIRECRAWL_API_KEY` has a free plan with 500 credits. For typical solo development, expect **$0/month** on the free tiers. Only high-volume teams or heavy autonomous pipeline usage will incur meaningful costs.
|
|
129
|
+
|
|
112
130
|
---
|
|
113
131
|
|
|
114
132
|
## ✨ The Magic Moment
|
|
@@ -321,6 +339,12 @@ Then add to your MCP config:
|
|
|
321
339
|
> **❓ Seeing warnings about missing API keys on startup?**
|
|
322
340
|
> That's expected and not an error. `BRAVE_API_KEY` / `GOOGLE_API_KEY` warnings are informational only — core session memory works with zero keys. See [Environment Variables](#environment-variables) for what each key unlocks.
|
|
323
341
|
|
|
342
|
+
> 💡 **Do agents auto-load Prism?** Agents using Cursor, Windsurf, or other MCP clients will see the `session_load_context` tool automatically, but may not call it unprompted. Add this to your project's `.cursorrules` (or equivalent system prompt) to guarantee auto-load:
|
|
343
|
+
> ```
|
|
344
|
+
> At the start of every conversation, call session_load_context with project "my-project" before doing any work.
|
|
345
|
+
> ```
|
|
346
|
+
> Claude Code users can use the `.clauderules` auto-load hook shown in the [Setup Guides](#-setup-guides). Prism also has a **server-side fallback** (v5.2.1+) that auto-pushes context after 10 seconds if no load is detected.
|
|
347
|
+
|
|
324
348
|
---
|
|
325
349
|
|
|
326
350
|
## 📥 Universal Import — Bring Your History
|
|
@@ -369,6 +393,7 @@ Every save creates a versioned snapshot. Made a mistake? `memory_checkout` rever
|
|
|
369
393
|
A gorgeous glassmorphism UI at `localhost:3000` that lets you see exactly what your agent is thinking:
|
|
370
394
|
|
|
371
395
|
- **Current State & TODOs** — the exact context injected into the LLM's prompt
|
|
396
|
+
- **Intent Health Gauges** — per-project 0–100 health score with staleness decay, TODO load, and decision signals
|
|
372
397
|
- **Interactive Knowledge Graph** — force-directed neural graph with click-to-filter, node renaming, and surgical keyword deletion
|
|
373
398
|
- **Deep Storage Manager** — preview and execute vector purge operations with dry-run safety
|
|
374
399
|
- **Session Ledger** — full audit trail of every decision your agent has made
|
|
@@ -387,7 +412,7 @@ Powered by a pure TypeScript port of Google's TurboQuant (inspired by Google's I
|
|
|
387
412
|
Multiple agents (dev, QA, PM) can work on the same project with **role-isolated memory**. Agents discover each other automatically, share context in real-time via Telepathy sync, and see a team roster during context loading. → [Multi-agent setup example](examples/multi-agent-hivemind/)
|
|
388
413
|
|
|
389
414
|
### 🚦 Task Router
|
|
390
|
-
Prism can score coding tasks and recommend whether to keep execution on the host model or delegate to local Claw. This enables faster handling of small
|
|
415
|
+
Prism can score coding tasks and recommend whether to keep execution on the host model or delegate to a **local Claw agent** (a lightweight sub-agent powered by Ollama/vLLM for fast, local-safe edits). This enables faster handling of small edits while preserving host execution for complex work. In client startup/skill flows, use defensive delegation: route only coding tasks, call `session_task_route` only when available, delegate to `claw` only when executor tooling exists and task is non-destructive, and fallback to host when router/executor is unavailable. → [Task router real-life example](examples/router_real_life_test.ts)
|
|
391
416
|
|
|
392
417
|
### 🖼️ Visual Memory
|
|
393
418
|
Save UI screenshots, architecture diagrams, and bug states to a searchable vault. Images are auto-captioned by a VLM (Claude Vision / GPT-4V / Gemini) and become semantically searchable across sessions.
|
|
@@ -398,11 +423,36 @@ OpenTelemetry spans for every MCP tool call, LLM hop, and background worker. Rou
|
|
|
398
423
|
### 🌐 Autonomous Web Scholar
|
|
399
424
|
Prism researches while you sleep. A background pipeline searches the web, scrapes articles, synthesizes findings via LLM, and injects results directly into your semantic memory — fully searchable on your next session. Brave Search → Firecrawl scrape → LLM synthesis → Prism ledger. Task-aware, Hivemind-integrated, and zero-config when API keys are missing (falls back to Yahoo + Readability).
|
|
400
425
|
|
|
401
|
-
### 🔒
|
|
402
|
-
|
|
426
|
+
### 🔒 Data Privacy & Egress
|
|
427
|
+
|
|
428
|
+
**Where is my data stored?**
|
|
429
|
+
|
|
430
|
+
All data lives under `~/.prism-mcp/` on your machine:
|
|
431
|
+
|
|
432
|
+
| File | Contents |
|
|
433
|
+
|------|----------|
|
|
434
|
+
| `~/.prism-mcp/data.db` | All sessions, handoffs, embeddings, knowledge graph (SQLite + WAL) |
|
|
435
|
+
| `~/.prism-mcp/prism-config.db` | Dashboard settings, system config, API keys |
|
|
436
|
+
| `~/.prism-mcp/media/<project>/` | Visual memory vault (screenshots, HTML captures) |
|
|
437
|
+
| `~/.prism-mcp/dashboard.port` | Ephemeral port lock file |
|
|
438
|
+
| `~/.prism-mcp/sync.lock` | Sync coordination lock |
|
|
439
|
+
|
|
440
|
+
**Hard reset:** To completely erase your agent's brain, stop Prism and delete the directory:
|
|
441
|
+
```bash
|
|
442
|
+
rm -rf ~/.prism-mcp
|
|
443
|
+
```
|
|
444
|
+
Prism will recreate the directory with empty databases on next startup.
|
|
445
|
+
|
|
446
|
+
**What leaves your machine?**
|
|
447
|
+
- **Local mode (default):** Nothing. Zero network calls. All data is on-disk SQLite.
|
|
448
|
+
- **With `GOOGLE_API_KEY`:** Text snippets are sent to Gemini for embedding generation, summaries, and Morning Briefings. No session data is stored on Google's servers beyond the API call.
|
|
449
|
+
- **With `BRAVE_API_KEY` / `FIRECRAWL_API_KEY`:** Web Scholar queries are sent to Brave/Firecrawl for search and scraping.
|
|
450
|
+
- **With Supabase:** Session data syncs to your own Supabase instance (you control the Postgres database).
|
|
451
|
+
|
|
452
|
+
**GDPR compliance:** Soft/hard delete (Art. 17), full export in JSON, Markdown, or Obsidian vault `.zip` (Art. 20), API key redaction in exports, per-project TTL retention policies, and immutable audit trail. Enterprise-ready out of the box.
|
|
403
453
|
|
|
404
454
|
### 🏭 Dark Factory — Adversarial Autonomous Pipelines
|
|
405
|
-
When you trigger a Dark Factory pipeline, Prism doesn't just run your task — it fights itself to produce high-quality output. A `PLAN_CONTRACT` step locks a machine-parseable rubric before any code is written. After execution, an **Adversarial Evaluator** (in a fully isolated context) scores the output against the rubric. It cannot pass the Generator without providing exact file and line evidence for every failing criterion. Failed evaluations inject the critique directly into the Generator's retry prompt so it's never flying blind. The result: security issues, regressions, and lazy debug logs caught autonomously — before you ever see the PR.
|
|
455
|
+
When you trigger a Dark Factory pipeline, Prism doesn't just run your task — it fights itself to produce high-quality output. A `PLAN_CONTRACT` step locks a machine-parseable rubric before any code is written. After execution, an **Adversarial Evaluator** (in a fully isolated context) scores the output against the rubric. It cannot pass the Generator without providing exact file and line evidence for every failing criterion. Failed evaluations inject the critique directly into the Generator's retry prompt so it's never flying blind. The result: security issues, regressions, and lazy debug logs caught autonomously — before you ever see the PR. → [See it in action](examples/adversarial-eval-demo/README.md)
|
|
406
456
|
|
|
407
457
|
---
|
|
408
458
|
|
|
@@ -412,6 +462,7 @@ When you trigger a Dark Factory pipeline, Prism doesn't just run your task — i
|
|
|
412
462
|
- **Multi-agent collaboration** — Dev, QA, and PM agents share real-time context without stepping on each other's memory.
|
|
413
463
|
- **Consulting / multi-project** — Switch between client projects with progressive loading: `quick` (~50 tokens), `standard` (~200), or `deep` (~1000+).
|
|
414
464
|
- **Autonomous execution (v7.4)** — Dark Factory pipeline: `plan → plan_contract → execute → evaluate → verify → finalize`. Generator and evaluator run in isolated roles — the evaluator cannot approve without evidence-bound findings scored against a pre-committed rubric.
|
|
465
|
+
- **Project health monitoring (v7.5)** — Intent Health Dashboard scores each project 0–100 based on staleness, TODO load, and decision quality — turning silent drift into an actionable signal.
|
|
415
466
|
- **Team onboarding** — New team member's agent loads the full project history instantly.
|
|
416
467
|
- **Behavior enforcement** — Agent corrections auto-graduate into permanent `.cursorrules` / `.clauderules` rules.
|
|
417
468
|
- **Offline / air-gapped** — Full SQLite local mode + Ollama LLM adapter. Zero internet dependency.
|
|
@@ -543,18 +594,13 @@ The Generator strips the `console.log`, resubmits, and the next `EVALUATE` retur
|
|
|
543
594
|
|
|
544
595
|
## 🆕 What's New
|
|
545
596
|
|
|
597
|
+
> **Current release: v7.5.0**
|
|
546
598
|
|
|
547
|
-
|
|
599
|
+
- 🩺 **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.
|
|
600
|
+
- ⚔️ **v7.4.0 — Adversarial Evaluation:** Split-brain anti-sycophancy pipeline. Generator and evaluator in isolated roles with evidence-bound findings.
|
|
601
|
+
- 🏭 **v7.3.x — Dark Factory + Stability:** Fail-closed 3-gate execution pipeline. Dashboard stability and verification diagnostics.
|
|
548
602
|
|
|
549
|
-
|
|
550
|
-
- 🔧 **v7.3.3 — Dashboard Stability Hotfix:** Fixed a multi-layer quote-escaping trap in the `abortPipeline` onclick handler that silently killed the dashboard IIFE and froze the project selector at "Loading projects..." forever. Fixed via `data-id` attribute pattern + ES5 lint guard (`npm run lint:dashboard`).
|
|
551
|
-
- 🏭 **v7.3.1 — Dark Factory (Fail-Closed Execution):** The LLM can no longer touch the filesystem directly. Every autonomous `EXECUTE` step passes 3 gates — Parse → Type → Scope — before any side effect occurs. Scope violations terminate the entire pipeline.
|
|
552
|
-
- 📊 **v7.3.2 — Verification Diagnostics v2:** `verify status --json` now emits per-layer `diff_counts` + `changed_keys`. JSON schema is contract-enforced in CI (`schema_version: 1`).
|
|
553
|
-
- 🔭 **v7.2.0 — Verification Harness:** Spec-frozen contracts (`verification_harness.json` hash-locked before execution), multi-layer assertions across Data / Agent / Pipeline, and finalization gate policies (`warn` / `gate` / `abort`).
|
|
554
|
-
- 🚦 **v7.1.0 — Task Router:** Heuristic + ML-experience routing delegates cloud vs. local model in under 2ms, cold-start safe, per-project experience-corrected.
|
|
555
|
-
- 🧠 **v7.0.0 — ACT-R Activation Memory:** `B_i = ln(Σ t_j^{-d})` recency × frequency re-ranking. Stale memories fade naturally. Active context surfaces automatically.
|
|
556
|
-
|
|
557
|
-
👉 **[Full release history → CHANGELOG.md](CHANGELOG.md)** · [ROADMAP →](ROADMAP.md)
|
|
603
|
+
👉 **[Full release history → CHANGELOG.md](CHANGELOG.md)** · **[ROADMAP →](ROADMAP.md)**
|
|
558
604
|
|
|
559
605
|
---
|
|
560
606
|
|
|
@@ -793,6 +839,10 @@ Requires `PRISM_DARK_FACTORY_ENABLED=true`.
|
|
|
793
839
|
|
|
794
840
|
</details>
|
|
795
841
|
|
|
842
|
+
### System Settings (Dashboard)
|
|
843
|
+
Some configurations are stored dynamically in SQLite (`system_settings` table) and can be edited through the Dashboard UI at `http://localhost:3000`:
|
|
844
|
+
- **`intent_health_stale_threshold_days`** (default: `30`): Number of days before a project is considered fully stale for Intent Health scoring.
|
|
845
|
+
|
|
796
846
|
---
|
|
797
847
|
|
|
798
848
|
## Architecture
|
|
@@ -888,6 +938,7 @@ Prism is evolving from smart session logging toward a **cognitive memory archite
|
|
|
888
938
|
| **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 |
|
|
889
939
|
| **v7.2** | Verification-first harness — spec-freeze contract, rubric hash lock, multi-layer assertions, CLI `verify` commands | Programmatic verification systems + adversarial validation loops | ✅ Shipped |
|
|
890
940
|
| **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 |
|
|
941
|
+
| **v7.5** | Intent Health Dashboard — 3-signal scoring algorithm (staleness, TODO load, decisions), comprehensive XSS hardening (10 vectors), NaN/`Infinity` guards | Proactive monitoring, defense-in-depth security | ✅ Shipped |
|
|
891
942
|
| **v7.x** | Affect-Tagged Memory — sentiment shapes what gets recalled | Affect-modulated retrieval (neuroscience) | 🔭 Horizon |
|
|
892
943
|
| **v8+** | Zero-Search Retrieval — no index, no ANN, just ask the vector | Holographic Reduced Representations | 🔭 Horizon |
|
|
893
944
|
|
|
@@ -895,34 +946,26 @@ Prism is evolving from smart session logging toward a **cognitive memory archite
|
|
|
895
946
|
|
|
896
947
|
---
|
|
897
948
|
|
|
898
|
-
## 📦
|
|
899
|
-
|
|
900
|
-
> **[Full ROADMAP.md →](ROADMAP.md)**
|
|
949
|
+
## 📦 Milestones & Roadmap
|
|
901
950
|
|
|
902
|
-
|
|
903
|
-
Shipped in v6.2.0. Edge synthesis, graph pruning with SLO observability, temporal decay heatmaps, active recall prompt generation, and full dashboard metrics integration.
|
|
951
|
+
> **Current: v7.5.0** — Intent Health Dashboard + XSS Hardening ([CHANGELOG](CHANGELOG.md))
|
|
904
952
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
Shipped. Deterministic task routing (`session_task_route`) with optional experience-based confidence adjustment for host vs. local Claw delegation.
|
|
916
|
-
|
|
917
|
-
### v7.0: ACT-R Activation Memory ✅
|
|
918
|
-
Shipped. Scientifically-grounded retrieval re-ranking via ACT-R base-level activation (`B_i = ln(Σ t_j^(-d))`), candidate-scoped spreading activation, parameterized sigmoid normalization, composite scoring, and zero-cold-start access log infrastructure. 49 dedicated unit tests, 705 total passing.
|
|
919
|
-
|
|
920
|
-
### v7.2: Verification Harness ✅
|
|
921
|
-
Shipped. Spec-frozen verification contract (`implementation_plan.md` + `verification_harness.json` + immutable `validation_result`), multi-layer machine checks (`data`, `agent`, `pipeline`), finalization gate policies (`warn` / `gate` / `abort`), and CLI `verify generate` / `verify status --json` with schema-versioned output.
|
|
953
|
+
| Release | Headline |
|
|
954
|
+
|---------|----------|
|
|
955
|
+
| **v7.5** | Intent Health scoring + 10 XSS patches |
|
|
956
|
+
| **v7.4** | Adversarial Evaluation (anti-sycophancy) |
|
|
957
|
+
| **v7.3** | Dark Factory fail-closed execution |
|
|
958
|
+
| **v7.2** | Verification Harness |
|
|
959
|
+
| **v7.1** | Task Router |
|
|
960
|
+
| **v7.0** | ACT-R Activation Memory |
|
|
961
|
+
| **v6.5** | HDC Cognitive Routing |
|
|
962
|
+
| **v6.2** | Synthesize & Prune |
|
|
922
963
|
|
|
923
964
|
### Future Tracks
|
|
924
|
-
- **v7.x: Affect-Tagged Memory** — Recall prioritization improves by weighting memories with affective/contextual valence
|
|
925
|
-
- **v8+: Zero-Search Retrieval** — Direct vector-addressed recall
|
|
965
|
+
- **v7.x: Affect-Tagged Memory** — Recall prioritization improves by weighting memories with affective/contextual valence.
|
|
966
|
+
- **v8+: Zero-Search Retrieval** — Direct vector-addressed recall reduces retrieval indirection.
|
|
967
|
+
|
|
968
|
+
👉 **[Full ROADMAP.md →](ROADMAP.md)**
|
|
926
969
|
|
|
927
970
|
|
|
928
971
|
## ❓ Troubleshooting FAQ
|
package/dist/cli.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export function computeIntentHealth(ctx, staleThresholdDays = 30, nowMs = Date.now()) {
|
|
2
|
+
if (!Number.isFinite(staleThresholdDays) || staleThresholdDays <= 0)
|
|
3
|
+
staleThresholdDays = 30; // Guard against NaN / zero / negative
|
|
4
|
+
// 2. Compute staleness (Days since last session)
|
|
5
|
+
let stalenessDays = 0;
|
|
6
|
+
if (ctx.recent_sessions && ctx.recent_sessions.length > 0) {
|
|
7
|
+
const lastTimestamp = ctx.recent_sessions[0].created_at ? new Date(ctx.recent_sessions[0].created_at).getTime() : NaN;
|
|
8
|
+
stalenessDays = isNaN(lastTimestamp) ? 0 : Math.max(0, (nowMs - lastTimestamp) / (1000 * 60 * 60 * 24));
|
|
9
|
+
}
|
|
10
|
+
// 3. Count TODOs and Decisions
|
|
11
|
+
const todoCount = ctx.pending_todo?.length || 0;
|
|
12
|
+
const hasDecisions = ctx.recent_sessions?.some((s) => Array.isArray(s.decisions) && s.decisions.length > 0) ?? false;
|
|
13
|
+
// 4. Execute Intent Debt Scoring Algorithm
|
|
14
|
+
// Staleness (50 points): Linear decay until threshold
|
|
15
|
+
const stalenessScore = Math.max(0, 50 - (stalenessDays / staleThresholdDays) * 50);
|
|
16
|
+
// Open TODOs (30 points)
|
|
17
|
+
let todoScore = 30;
|
|
18
|
+
if (todoCount >= 10)
|
|
19
|
+
todoScore = 0;
|
|
20
|
+
else if (todoCount >= 7)
|
|
21
|
+
todoScore = 5;
|
|
22
|
+
else if (todoCount >= 4)
|
|
23
|
+
todoScore = 15;
|
|
24
|
+
else if (todoCount >= 1)
|
|
25
|
+
todoScore = 25;
|
|
26
|
+
// Decisions (20 points): 70% of max if missing
|
|
27
|
+
const decisionScore = hasDecisions ? 20 : 14;
|
|
28
|
+
const totalScore = Math.min(100, Math.round(stalenessScore + todoScore + decisionScore));
|
|
29
|
+
// 5. Generate Actionable Signals
|
|
30
|
+
const signals = [];
|
|
31
|
+
if (stalenessDays > staleThresholdDays) {
|
|
32
|
+
signals.push({ type: "staleness", message: `Stale: Last updated ${Math.round(stalenessDays)} days ago`, severity: "critical" });
|
|
33
|
+
}
|
|
34
|
+
else if (stalenessDays > staleThresholdDays / 2) {
|
|
35
|
+
signals.push({ type: "staleness", message: `Aging: Last updated ${Math.round(stalenessDays)} days ago`, severity: "warn" });
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
signals.push({ type: "staleness", message: `Fresh: Last updated ${Math.round(stalenessDays)} days ago`, severity: "ok" });
|
|
39
|
+
}
|
|
40
|
+
if (todoCount >= 10) {
|
|
41
|
+
signals.push({ type: "todos", message: `${todoCount} TODOs pending (Overwhelming)`, severity: "critical" });
|
|
42
|
+
}
|
|
43
|
+
else if (todoCount >= 4) {
|
|
44
|
+
signals.push({ type: "todos", message: `${todoCount} TODOs pending`, severity: "warn" });
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
signals.push({ type: "todos", message: `${todoCount} TODOs pending`, severity: "ok" });
|
|
48
|
+
}
|
|
49
|
+
if (hasDecisions) {
|
|
50
|
+
signals.push({ type: "decisions", message: "Active decisions captured", severity: "ok" });
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
signals.push({ type: "decisions", message: "No active decisions documented", severity: "warn" });
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
score: totalScore,
|
|
57
|
+
staleness_days: Math.round(stalenessDays),
|
|
58
|
+
open_todo_count: todoCount,
|
|
59
|
+
has_active_decisions: hasDecisions,
|
|
60
|
+
signals: signals
|
|
61
|
+
};
|
|
62
|
+
}
|
package/dist/dashboard/server.js
CHANGED
|
@@ -23,7 +23,8 @@ import * as fs from "fs";
|
|
|
23
23
|
import { getStorage } from "../storage/index.js";
|
|
24
24
|
import { PRISM_USER_ID, SERVER_CONFIG } from "../config.js";
|
|
25
25
|
import { renderDashboardHTML } from "./ui.js";
|
|
26
|
-
import {
|
|
26
|
+
import { computeIntentHealth } from "./intentHealth.js";
|
|
27
|
+
import { getAllSettings, setSetting, getSetting, getSettingSync } from "../storage/configStorage.js";
|
|
27
28
|
import { compactLedgerHandler } from "../tools/compactionHandler.js";
|
|
28
29
|
import { getLLMProvider } from "../utils/llm/factory.js";
|
|
29
30
|
import { buildVaultDirectory } from "../utils/vaultExporter.js";
|
|
@@ -1095,6 +1096,39 @@ self.addEventListener('message', (e) => {
|
|
|
1095
1096
|
});
|
|
1096
1097
|
return res.end(Buffer.from(pngBase64, "base64"));
|
|
1097
1098
|
}
|
|
1099
|
+
// ─── API: Intent Health (Feature A) ───
|
|
1100
|
+
// GET /api/intent-health?project=xxx
|
|
1101
|
+
if (url.pathname === "/api/intent-health" && req.method === "GET") {
|
|
1102
|
+
const project = url.searchParams.get("project");
|
|
1103
|
+
if (!project) {
|
|
1104
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1105
|
+
return res.end(JSON.stringify({ error: "project is required" }));
|
|
1106
|
+
}
|
|
1107
|
+
try {
|
|
1108
|
+
const storage = await getStorageSafe();
|
|
1109
|
+
if (!storage) {
|
|
1110
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1111
|
+
return res.end(JSON.stringify({ error: "Storage initializing..." }));
|
|
1112
|
+
}
|
|
1113
|
+
// Load 'standard' context to get active_decisions and recent_sessions
|
|
1114
|
+
// Note: API MUST use "standard" level (deep skips recent_sessions, which breaks staleness).
|
|
1115
|
+
const ctx = await storage.loadContext(project, "standard", PRISM_USER_ID);
|
|
1116
|
+
if (!ctx) {
|
|
1117
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1118
|
+
return res.end(JSON.stringify({ error: "Project not found" }));
|
|
1119
|
+
}
|
|
1120
|
+
// 1. Fetch configurable threshold (default 30 days, avoiding 7-day panic)
|
|
1121
|
+
const staleThresholdDays = parseInt(getSettingSync("intent_health_stale_threshold_days", "30"), 10) || 30;
|
|
1122
|
+
const healthResult = computeIntentHealth(ctx, staleThresholdDays);
|
|
1123
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1124
|
+
return res.end(JSON.stringify(healthResult));
|
|
1125
|
+
}
|
|
1126
|
+
catch (err) {
|
|
1127
|
+
console.error("[intent-health] Error computing intent health:", err);
|
|
1128
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1129
|
+
return res.end(JSON.stringify({ error: "Failed to compute intent health" }));
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1098
1132
|
// ─── 404 ───
|
|
1099
1133
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
1100
1134
|
res.end("Not found");
|
package/dist/dashboard/ui.js
CHANGED
|
@@ -625,6 +625,15 @@ export function renderDashboardHTML(version) {
|
|
|
625
625
|
<ul class="todo-list" id="todos"></ul>
|
|
626
626
|
</div>
|
|
627
627
|
|
|
628
|
+
<!-- Intent Health (Feature A) -->
|
|
629
|
+
<div class="card" id="intentHealthCard" style="display: none;">
|
|
630
|
+
<div class="card-title" style="display: flex; justify-content: space-between; align-items: center;">
|
|
631
|
+
<div><span class="dot" style="background:var(--accent-purple)"></span> Intent Health</div>
|
|
632
|
+
<span style="font-size: 11px; color: var(--text-muted); cursor: help; border-bottom: 1px dotted var(--text-muted); letter-spacing: normal; text-transform: none;" title="Intent debt limits how AI agents can evolve the system (Storey / Fowler)">What is this?</span>
|
|
633
|
+
</div>
|
|
634
|
+
<div id="intentHealthCardContent"></div>
|
|
635
|
+
</div>
|
|
636
|
+
|
|
628
637
|
<!-- Git Metadata -->
|
|
629
638
|
<div class="card">
|
|
630
639
|
<div class="card-title"><span class="dot" style="background:var(--accent-green)"></span> Git Metadata</div>
|
|
@@ -1465,15 +1474,15 @@ function loadPipelines() {
|
|
|
1465
1474
|
html += '<span style="font-weight:600;color:var(--text-primary)">' + emoji + ' ' + p.status + '</span>';
|
|
1466
1475
|
html += '<span style="font-size:0.7rem;font-family:var(--font-mono);color:var(--text-muted)">' + p.id.slice(0, 8) + '…</span>';
|
|
1467
1476
|
html += '</div>';
|
|
1468
|
-
html += '<div style="font-size:0.82rem;color:var(--text-secondary);margin-bottom:0.35rem">' + objective + '</div>';
|
|
1477
|
+
html += '<div style="font-size:0.82rem;color:var(--text-secondary);margin-bottom:0.35rem">' + escapeHtml(objective) + '</div>';
|
|
1469
1478
|
html += '<div style="display:flex;gap:1rem;font-size:0.72rem;color:var(--text-muted);flex-wrap:wrap">';
|
|
1470
|
-
html += '<span>📁 ' + (p.project || '?') + '</span>';
|
|
1479
|
+
html += '<span>📁 ' + escapeHtml(p.project || '?') + '</span>';
|
|
1471
1480
|
html += '<span>🔄 ' + p.iteration + ' / ' + maxIter + '</span>';
|
|
1472
|
-
html += '<span>📍 ' + (p.current_step || '?') + '</span>';
|
|
1481
|
+
html += '<span>📍 ' + escapeHtml(p.current_step || '?') + '</span>';
|
|
1473
1482
|
html += '<span>🕐 ' + new Date(p.updated_at).toLocaleString() + '</span>';
|
|
1474
1483
|
html += '</div>';
|
|
1475
1484
|
if (p.error) {
|
|
1476
|
-
html += '<div style="font-size:0.72rem;color:var(--accent-rose);margin-top:0.35rem;padding:0.3rem 0.5rem;background:rgba(244,63,94,0.08);border-radius:4px">⚠ ' + p.error.slice(0, 200) + '</div>';
|
|
1485
|
+
html += '<div style="font-size:0.72rem;color:var(--accent-rose);margin-top:0.35rem;padding:0.3rem 0.5rem;background:rgba(244,63,94,0.08);border-radius:4px">⚠ ' + escapeHtml(p.error.slice(0, 200)) + '</div>';
|
|
1477
1486
|
}
|
|
1478
1487
|
if (isActive) {
|
|
1479
1488
|
html += '<div style="margin-top:0.5rem"><button onclick="abortPipeline(this.dataset.id)" data-id="' + p.id + '" class="cleanup-btn" style="font-size:0.72rem">🛑 Abort Pipeline</button></div>';
|
|
@@ -1495,7 +1504,7 @@ function loadPipelines() {
|
|
|
1495
1504
|
}
|
|
1496
1505
|
})
|
|
1497
1506
|
.catch(function (err) {
|
|
1498
|
-
document.getElementById('factoryList').innerHTML = '<div style="color:var(--accent-rose);padding:1rem">Failed to load pipelines: ' + err.message + '</div>';
|
|
1507
|
+
document.getElementById('factoryList').innerHTML = '<div style="color:var(--accent-rose);padding:1rem">Failed to load pipelines: ' + escapeHtml(err.message) + '</div>';
|
|
1499
1508
|
});
|
|
1500
1509
|
}
|
|
1501
1510
|
function abortPipeline(id) {
|
|
@@ -1672,11 +1681,36 @@ function loadIdentityChip() {
|
|
|
1672
1681
|
select = document.getElementById('projectSelect');
|
|
1673
1682
|
if (data.projects && data.projects.length > 0) {
|
|
1674
1683
|
select.innerHTML = '<option value="">— Select a project —</option>' +
|
|
1675
|
-
data.projects.map(function (p) { return '<option value="' + p + '">' + p + '</option>'; }).join('');
|
|
1684
|
+
data.projects.map(function (p) { return '<option value="' + escapeHtml(p) + '">' + escapeHtml(p) + '</option>'; }).join('');
|
|
1685
|
+
|
|
1686
|
+
// v7.5.0: Background fetch intent health to add global traffic-light dots
|
|
1687
|
+
// Fetch sequentially to prevent SQLite WAL saturation with N concurrent loadContext calls
|
|
1688
|
+
var pIndex = 0;
|
|
1689
|
+
function fetchNextHealth() {
|
|
1690
|
+
if (pIndex >= data.projects.length) return;
|
|
1691
|
+
var p = data.projects[pIndex++];
|
|
1692
|
+
fetch('/api/intent-health?project=' + encodeURIComponent(p))
|
|
1693
|
+
.then(function (res) { return res.json(); })
|
|
1694
|
+
.then(function (hData) {
|
|
1695
|
+
if (hData && typeof hData.score === 'number') {
|
|
1696
|
+
var icon = hData.score >= 80 ? "🟢" : (hData.score >= 50 ? "🟡" : "🔴");
|
|
1697
|
+
var opt = null;
|
|
1698
|
+
for (var oi = 0; oi < select.options.length; oi++) {
|
|
1699
|
+
if (select.options[oi].value === p) { opt = select.options[oi]; break; }
|
|
1700
|
+
}
|
|
1701
|
+
if (opt) opt.textContent = icon + " " + p;
|
|
1702
|
+
}
|
|
1703
|
+
fetchNextHealth();
|
|
1704
|
+
}).catch(function () { fetchNextHealth(); });
|
|
1705
|
+
}
|
|
1706
|
+
if (data.projects && data.projects.length > 0) {
|
|
1707
|
+
fetchNextHealth();
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1676
1710
|
gp = document.getElementById('graphProjectFilter');
|
|
1677
1711
|
if (gp) {
|
|
1678
1712
|
gp.innerHTML = '<option value="">All Projects</option>' +
|
|
1679
|
-
data.projects.map(function (p) { return '<option value="' + p + '">' + p + '</option>'; }).join('');
|
|
1713
|
+
data.projects.map(function (p) { return '<option value="' + escapeHtml(p) + '">' + escapeHtml(p) + '</option>'; }).join('');
|
|
1680
1714
|
}
|
|
1681
1715
|
// Restore last selected project from localStorage
|
|
1682
1716
|
var lastProject = localStorage.getItem('prism_last_project');
|
|
@@ -1708,8 +1742,15 @@ function loadProject() {
|
|
|
1708
1742
|
switch (_a.label) {
|
|
1709
1743
|
case 0:
|
|
1710
1744
|
project = document.getElementById('projectSelect').value;
|
|
1711
|
-
if (!project)
|
|
1745
|
+
if (!project) {
|
|
1746
|
+
var hc = document.getElementById('intentHealthCard');
|
|
1747
|
+
if (hc) hc.style.display = 'none';
|
|
1748
|
+
var welcomeEl = document.getElementById('welcome');
|
|
1749
|
+
var contentEl = document.getElementById('content');
|
|
1750
|
+
if (contentEl) contentEl.style.display = 'none';
|
|
1751
|
+
if (welcomeEl) welcomeEl.style.display = 'flex';
|
|
1712
1752
|
return [2 /*return*/];
|
|
1753
|
+
}
|
|
1713
1754
|
// Persist last selected project
|
|
1714
1755
|
try { localStorage.setItem('prism_last_project', project); } catch(e) {}
|
|
1715
1756
|
document.getElementById('welcome').style.display = 'none';
|
|
@@ -1767,7 +1808,7 @@ function loadProject() {
|
|
|
1767
1808
|
var snap = h.snapshot || {};
|
|
1768
1809
|
var summary = snap.last_summary || snap.summary || 'Snapshot';
|
|
1769
1810
|
return '<div class="timeline-item history">' +
|
|
1770
|
-
'<div class="meta"><span class="badge badge-purple">v' + h.version + '</span>' +
|
|
1811
|
+
'<div class="meta"><span class="badge badge-purple">v' + escapeHtml(h.version) + '</span>' +
|
|
1771
1812
|
'<span>' + formatDate(h.created_at) + '</span></div>' +
|
|
1772
1813
|
escapeHtml(summary) + '</div>';
|
|
1773
1814
|
}).join('');
|
|
@@ -1785,7 +1826,7 @@ function loadProject() {
|
|
|
1785
1826
|
try {
|
|
1786
1827
|
var parsed = typeof decisions === 'string' ? JSON.parse(decisions) : decisions;
|
|
1787
1828
|
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
1788
|
-
extra = '<div style="margin-top:0.3rem;font-size:0.75rem;color:var(--accent-cyan)">Decisions: ' + parsed.join(', ') + '</div>';
|
|
1829
|
+
extra = '<div style="margin-top:0.3rem;font-size:0.75rem;color:var(--accent-cyan)">Decisions: ' + parsed.map(function(d) { return escapeHtml(d); }).join(', ') + '</div>';
|
|
1789
1830
|
}
|
|
1790
1831
|
}
|
|
1791
1832
|
catch (e) { }
|
|
@@ -1851,6 +1892,10 @@ function loadProject() {
|
|
|
1851
1892
|
document.getElementById('content').className = 'grid grid-main fade-in';
|
|
1852
1893
|
document.getElementById('content').style.display = 'grid';
|
|
1853
1894
|
projectLoaded = true; // Fix 3: mark project as loaded for tab-switch restore
|
|
1895
|
+
|
|
1896
|
+
// Feature A: Run Intent Health Fetch
|
|
1897
|
+
fetchIntentHealth(project);
|
|
1898
|
+
|
|
1854
1899
|
// v3.1: Analytics + Lifecycle Controls + Import
|
|
1855
1900
|
document.getElementById('analyticsCard').style.display = 'block';
|
|
1856
1901
|
document.getElementById('lifecycleCard').style.display = 'block';
|
|
@@ -2393,7 +2438,7 @@ function showToast(msg, isErr) {
|
|
|
2393
2438
|
function escapeHtml(str) {
|
|
2394
2439
|
if (!str)
|
|
2395
2440
|
return '';
|
|
2396
|
-
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
2441
|
+
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
2397
2442
|
}
|
|
2398
2443
|
function formatDate(isoStr) {
|
|
2399
2444
|
if (!isoStr)
|
|
@@ -4060,6 +4105,67 @@ if ('serviceWorker' in navigator) {
|
|
|
4060
4105
|
});
|
|
4061
4106
|
}
|
|
4062
4107
|
|
|
4108
|
+
/* --- INTENT HEALTH COMPONENT (ES5 Safe) --- */
|
|
4109
|
+
|
|
4110
|
+
function fetchIntentHealth(project) {
|
|
4111
|
+
var container = document.getElementById('intentHealthCardContent');
|
|
4112
|
+
var card = document.getElementById('intentHealthCard');
|
|
4113
|
+
if (!container || !card) return;
|
|
4114
|
+
|
|
4115
|
+
card.style.display = 'block';
|
|
4116
|
+
container.innerHTML = '<div style="color: #8b949e; padding: 16px;">Computing intent debt...</div>';
|
|
4117
|
+
|
|
4118
|
+
fetch('/api/intent-health?project=' + encodeURIComponent(project))
|
|
4119
|
+
.then(function(res) { return res.json(); })
|
|
4120
|
+
.then(function(data) {
|
|
4121
|
+
if (data.error) {
|
|
4122
|
+
container.innerHTML = '<div style="color: #ff7b72; padding: 16px;">Error: ' + escapeHtml(data.error) + '</div>';
|
|
4123
|
+
return;
|
|
4124
|
+
}
|
|
4125
|
+
renderIntentHealthCard(data);
|
|
4126
|
+
})
|
|
4127
|
+
.catch(function(err) {
|
|
4128
|
+
container.innerHTML = '<div style="color: #ff7b72; padding: 16px;">Failed to load intent health.</div>';
|
|
4129
|
+
});
|
|
4130
|
+
}
|
|
4131
|
+
|
|
4132
|
+
function renderIntentHealthCard(data) {
|
|
4133
|
+
var container = document.getElementById('intentHealthCardContent');
|
|
4134
|
+
if (!container) return;
|
|
4135
|
+
|
|
4136
|
+
// Determine Gauge Color
|
|
4137
|
+
var scoreColor = '#2ea043'; // Green
|
|
4138
|
+
if (data.score < 50) {
|
|
4139
|
+
scoreColor = '#ff7b72'; // Red
|
|
4140
|
+
} else if (data.score < 80) {
|
|
4141
|
+
scoreColor = '#d29922'; // Yellow
|
|
4142
|
+
}
|
|
4143
|
+
|
|
4144
|
+
// Render Signals
|
|
4145
|
+
var signalsHtml = '';
|
|
4146
|
+
for (var i = 0; i < data.signals.length; i++) {
|
|
4147
|
+
var sig = data.signals[i];
|
|
4148
|
+
var icon = '🟢';
|
|
4149
|
+
if (sig.severity === 'warn') icon = '🟡';
|
|
4150
|
+
if (sig.severity === 'critical') icon = '🔴';
|
|
4151
|
+
signalsHtml += '<div style="margin-bottom: 8px; font-size: 13px;">' + icon + ' ' + escapeHtml(sig.message) + '</div>';
|
|
4152
|
+
}
|
|
4153
|
+
|
|
4154
|
+
// Render Layout
|
|
4155
|
+
var html = '' +
|
|
4156
|
+
'<div style="display: flex; gap: 24px; align-items: center; padding: 16px;">' +
|
|
4157
|
+
'<div style="text-align: center; flex-shrink: 0; min-width: 80px;">' +
|
|
4158
|
+
'<div style="font-size: 42px; font-weight: bold; color: ' + scoreColor + '; line-height: 1;">' + (typeof data.score === 'number' ? data.score : '—') + '</div>' +
|
|
4159
|
+
'<div style="font-size: 10px; color: #8b949e; text-transform: uppercase; letter-spacing: 1px; margin-top: 8px;">Health Score</div>' +
|
|
4160
|
+
'</div>' +
|
|
4161
|
+
'<div style="flex-grow: 1; border-left: 1px solid var(--border-glass); padding-left: 24px;">' +
|
|
4162
|
+
signalsHtml +
|
|
4163
|
+
'</div>' +
|
|
4164
|
+
'</div>';
|
|
4165
|
+
|
|
4166
|
+
container.innerHTML = html;
|
|
4167
|
+
}
|
|
4168
|
+
|
|
4063
4169
|
</script>
|
|
4064
4170
|
</body>
|
|
4065
4171
|
</html>`;
|
package/dist/storage/sqlite.js
CHANGED
|
@@ -1184,6 +1184,30 @@ export class SqliteStorage {
|
|
|
1184
1184
|
importance: r.importance,
|
|
1185
1185
|
}));
|
|
1186
1186
|
}
|
|
1187
|
+
// ─── v7.5.0: Validation Pulse (Standard & Deep) ─────────────
|
|
1188
|
+
context.recent_validations = []; // Default empty
|
|
1189
|
+
try {
|
|
1190
|
+
const validationResult = await this.db.execute({
|
|
1191
|
+
sql: `SELECT run_at, passed, pass_rate, gate_action, critical_failures
|
|
1192
|
+
FROM verification_runs
|
|
1193
|
+
WHERE project = ? AND user_id = ?
|
|
1194
|
+
ORDER BY run_at DESC
|
|
1195
|
+
LIMIT 3`,
|
|
1196
|
+
args: [project, userId],
|
|
1197
|
+
});
|
|
1198
|
+
if (validationResult.rows.length > 0) {
|
|
1199
|
+
context.recent_validations = validationResult.rows.map(r => ({
|
|
1200
|
+
run_at: r.run_at,
|
|
1201
|
+
passed: Boolean(r.passed),
|
|
1202
|
+
pass_rate: r.pass_rate,
|
|
1203
|
+
gate_action: r.gate_action,
|
|
1204
|
+
critical_failures: Number(r.critical_failures) || 0,
|
|
1205
|
+
}));
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
catch (e) {
|
|
1209
|
+
// Graceful degradation if verification_runs table hasn't been migrated yet
|
|
1210
|
+
}
|
|
1187
1211
|
if (level === "standard") {
|
|
1188
1212
|
// Add recent ledger entries (role-scoped)
|
|
1189
1213
|
const recentLedger = await this.db.execute({
|
package/dist/storage/supabase.js
CHANGED
|
@@ -170,7 +170,36 @@ export class SupabaseStorage {
|
|
|
170
170
|
p_role: role || "global", // v3.0: pass role to RPC
|
|
171
171
|
});
|
|
172
172
|
const data = Array.isArray(result) ? result[0] : result;
|
|
173
|
-
|
|
173
|
+
const context = data ?? null;
|
|
174
|
+
if (!context)
|
|
175
|
+
return null;
|
|
176
|
+
// ─── v7.5.0: Validation Pulse (Standard & Deep) ─────────────
|
|
177
|
+
if (level === "standard" || level === "deep") {
|
|
178
|
+
context.recent_validations = [];
|
|
179
|
+
try {
|
|
180
|
+
const valData = await supabaseGet("verification_runs", {
|
|
181
|
+
project: `eq.${project}`,
|
|
182
|
+
user_id: `eq.${userId}`,
|
|
183
|
+
select: "run_at,passed,pass_rate,gate_action,critical_failures",
|
|
184
|
+
order: "run_at.desc",
|
|
185
|
+
limit: "3"
|
|
186
|
+
});
|
|
187
|
+
const validations = Array.isArray(valData) ? valData : [];
|
|
188
|
+
if (validations.length > 0) {
|
|
189
|
+
context.recent_validations = validations.map((r) => ({
|
|
190
|
+
run_at: r.run_at,
|
|
191
|
+
passed: Boolean(r.passed),
|
|
192
|
+
pass_rate: r.pass_rate,
|
|
193
|
+
gate_action: r.gate_action,
|
|
194
|
+
critical_failures: Number(r.critical_failures) || 0,
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
catch (e) {
|
|
199
|
+
// Graceful degradation if table doesn't exist yet
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return context;
|
|
174
203
|
}
|
|
175
204
|
catch (e) {
|
|
176
205
|
debugLog("[SupabaseStorage] loadContext RPC failed: " + (e instanceof Error ? e.message : String(e)));
|
|
@@ -657,6 +657,19 @@ export async function sessionLoadContextHandler(args) {
|
|
|
657
657
|
if (d.session_history?.length) {
|
|
658
658
|
formattedContext += `\n📂 Session History (${d.session_history.length} entries):\n` + d.session_history.map((s) => ` [${s.session_date?.split("T")[0]}] ${s.summary}`).join("\n") + `\n`;
|
|
659
659
|
}
|
|
660
|
+
if (d.recent_validations?.length) {
|
|
661
|
+
formattedContext += `\n🔬 Recent Validations:\n` + d.recent_validations.map((v) => {
|
|
662
|
+
const passStr = v.passed ? "✅ PASS" : "❌ FAIL";
|
|
663
|
+
const icon = v.gate_action ? (v.passed ? "🚀" : "🛑") : "ℹ️";
|
|
664
|
+
const dateStr = (v.run_at || "unknown").split("T")[0];
|
|
665
|
+
const rateStr = Math.round((Number(v.pass_rate) || 0) * 100);
|
|
666
|
+
let out = ` ${icon} [${dateStr}] ${passStr} (${rateStr}%)`;
|
|
667
|
+
if (v.critical_failures > 0) {
|
|
668
|
+
out += ` -> Critical Blockers: ${v.critical_failures}`;
|
|
669
|
+
}
|
|
670
|
+
return out;
|
|
671
|
+
}).join("\n") + `\n`;
|
|
672
|
+
}
|
|
660
673
|
// ─── Role-Scoped Skill Injection ─────────────────────────────
|
|
661
674
|
// If the active role has a skill document stored, append it so the
|
|
662
675
|
// agent loads its rules/conventions automatically at session start.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prism-mcp-server",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.5.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",
|