prism-mcp-server 3.1.1 โ†’ 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,11 +14,13 @@
14
14
 
15
15
  ## Table of Contents
16
16
 
17
- - [What's New (v3.1.0)](#whats-new-in-v310---memory-lifecycle-)
17
+ - [What's New (v4.0.0)](#whats-new-in-v400--behavioral-memory-)
18
18
  - [How Prism Compares](#how-prism-compares)
19
19
  - [Quick Start](#quick-start-zero-config--local-mode)
20
20
  - [Mind Palace Dashboard](#-the-mind-palace-dashboard)
21
21
  - [Integration Examples](#integration-examples)
22
+ - [Claude Code Integration (Hooks)](#claude-code-integration-hooks)
23
+ - [Gemini / Antigravity Integration](#gemini--antigravity-integration)
22
24
  - [Use Cases](#use-cases)
23
25
  - [Architecture](#architecture)
24
26
  - [Tool Reference](#tool-reference)
@@ -39,7 +41,18 @@
39
41
 
40
42
  ---
41
43
 
42
- ## What's New in v3.1.0 โ€” Memory Lifecycle ๐Ÿ”„
44
+ ## What's New in v4.0.0 โ€” Behavioral Memory ๐Ÿง 
45
+
46
+ | Feature | Description |
47
+ |---|---|
48
+ | ๐Ÿง  **Active Behavioral Memory** | New `session_save_experience` tool โ€” agents log actions, outcomes, corrections, and confidence scores. Prism tracks importance, applies decay over time, and injects behavioral warnings into context loading so agents learn from past mistakes. |
49
+ | ๐ŸŽฏ **Dynamic Role Resolution** | Role parameter is now fully optional across all tools. The server auto-resolves from dashboard settings via `getSetting("default_role")` โ€” set your role once in the Mind Palace Dashboard, and it applies everywhere. No more hardcoding `role: "global"`. |
50
+ | ๐Ÿ“ **Token Budget** | New `max_tokens` parameter on `session_load_context` โ€” set a token budget and the response is intelligently truncated to fit. Uses a 1 token โ‰ˆ 4 chars heuristic. |
51
+ | ๐Ÿ“‰ **Importance Decay** | Stale behavioral experiences automatically decay over time โ€” older corrections fade in importance to keep context fresh and relevant. |
52
+ | ๐Ÿ”ง **Claude Code Hooks** | Refined SessionStart/Stop hook samples that reliably trigger MCP tool calls. Simplified from multi-step workflows to single imperative instructions. |
53
+
54
+ <details>
55
+ <summary><strong>What's in v3.1.0 โ€” Memory Lifecycle ๐Ÿ”„</strong></summary>
43
56
 
44
57
  | Feature | Description |
45
58
  |---|---|
@@ -49,6 +62,8 @@
49
62
  | ๐Ÿ“ฆ **PKM Export (Obsidian / Logseq)** | Export any project's full memory as a ZIP archive of Markdown files โ€” one file per session with YAML-like frontmatter, TODOs, decisions, files-changed, and `#hashtag` keywords. Includes an `_index.md` with `[[wikilink]]` references. Click **Export ZIP** in the dashboard Lifecycle Controls card. |
50
63
  | ๐Ÿงช **Expanded Test Suite** | 37 new Vitest tests (95 total) โ€” covers analytics queries, TTL soft-delete idempotency, rollup preservation, `activeCompactions` Set memory-leak prevention, type guards, export Markdown structure, and TTL sweep scheduler contracts. |
51
64
 
65
+ </details>
66
+
52
67
  <details>
53
68
  <summary><strong>What's in v3.0.1 โ€” Agent Identity & Brain Clean-up ๐Ÿงน</strong></summary>
54
69
 
@@ -363,36 +378,124 @@ Add to your Continue `config.json` or Cline MCP settings:
363
378
 
364
379
  ---
365
380
 
366
- ## Claude Code + Gemini Startup Compatibility
381
+ ## Claude Code Integration (Hooks)
367
382
 
368
- If you want consistent behavior across clients, treat startup in two phases:
383
+ Claude Code supports **lifecycle hooks** in `~/.claude/settings.json` that fire automatically at session start and end. Use these to auto-hydrate and persist Prism memory without manual prompting.
369
384
 
370
- 1. **Server availability**: ensure `prism-mcp` is enabled in MCP config so tools are available.
371
- 2. **Context hydration**: explicitly call `session_load_context` at session start.
385
+ ### SessionStart Hook
372
386
 
373
- Recommended startup call:
387
+ Automatically loads context when a new session begins:
374
388
 
375
389
  ```json
376
390
  {
377
- "projectName": "<your-project>",
378
- "level": "standard"
391
+ "hooks": {
392
+ "SessionStart": [
393
+ {
394
+ "matcher": "*",
395
+ "hooks": [
396
+ {
397
+ "type": "command",
398
+ "command": "python3 -c \"import json; print(json.dumps({'continue': True, 'suppressOutput': False, 'systemMessage': 'You MUST call mcp__prism-mcp__session_load_context twice before responding to the user: first with project=my-project level=standard, then with project=my-other-project level=standard. Do not skip this.'}))\"",
399
+ "timeout": 10
400
+ }
401
+ ]
402
+ }
403
+ ]
404
+ }
379
405
  }
380
406
  ```
381
407
 
382
- Important distinction:
408
+ ### Stop Hook
383
409
 
384
- - Auto-loading `prism-mcp` makes the server available.
385
- - It does **not** guarantee memory context is auto-hydrated unless your client runtime/hook invokes `session_load_context`.
410
+ Automatically saves session memory when a session ends:
386
411
 
387
- Client notes:
412
+ ```json
413
+ {
414
+ "hooks": {
415
+ "Stop": [
416
+ {
417
+ "matcher": "*",
418
+ "hooks": [
419
+ {
420
+ "type": "command",
421
+ "command": "python3 -c \"import json; print(json.dumps({'continue': True, 'suppressOutput': False, 'systemMessage': 'MANDATORY END WORKFLOW: 1) Call mcp__prism-mcp__session_save_ledger with project and summary. 2) Call mcp__prism-mcp__session_save_handoff with expected_version set to the loaded version.'}))\"",
422
+ "timeout": 10
423
+ }
424
+ ]
425
+ }
426
+ ]
427
+ }
428
+ }
429
+ ```
430
+
431
+ ### How the Hooks Work
388
432
 
389
- - **Gemini runtimes** may support native startup execution depending on configuration.
390
- - **Claude Code** should use a local `SessionStart` hook in `~/.claude/settings.json` for deterministic startup context loading.
433
+ The hook `command` runs a Python one-liner that returns a JSON object to Claude Code:
434
+
435
+ | Field | Purpose |
436
+ |---|---|
437
+ | `continue: true` | Tell Claude Code to proceed (don't abort the session) |
438
+ | `suppressOutput: false` | Show the hook result to the agent |
439
+ | `systemMessage` | Instruction injected as a system message โ€” the agent follows it |
391
440
 
392
- Verification pattern (same for both clients):
441
+ The agent receives the `systemMessage` as an instruction and executes the tool calls. The server resolves the agent's **role** and **name** automatically from the dashboard โ€” no need to specify them in the hook.
393
442
 
394
- - Print a startup marker after successful `session_load_context` (for example, `PRISM_CONTEXT_LOADED`).
395
- - If the marker is missing, startup hydration did not run.
443
+ ### Role Resolution โ€” No Hardcoding Needed
444
+
445
+ Prism resolves the agent role dynamically using a priority chain:
446
+
447
+ ```
448
+ explicit tool argument โ†’ dashboard setting โ†’ "global" (default)
449
+ ```
450
+
451
+ 1. **Explicit arg wins** โ€” if `role` is passed in the tool call, it's used directly.
452
+ 2. **Dashboard fallback** โ€” if `role` is omitted, the server calls `getSetting("default_role")` and uses whatever role you configured in the **Mind Palace Dashboard โš™๏ธ Settings โ†’ Agent Identity**.
453
+ 3. **Final default** โ€” if no dashboard setting exists, falls back to `"global"`.
454
+
455
+ Change your role once in the dashboard, and it automatically applies to every session โ€” CLI, extension, and all MCP clients.
456
+
457
+ ### Verification
458
+
459
+ If hydration ran successfully, the agent's output will include:
460
+ - A `[๐Ÿ‘ค AGENT IDENTITY]` block showing your dashboard-configured role and name
461
+ - `PRISM_CONTEXT_LOADED` marker text
462
+
463
+ If the marker is missing, the hook did not fire or the MCP server is not connected.
464
+
465
+ ---
466
+
467
+ ## Gemini / Antigravity Integration
468
+
469
+ Gemini-based clients (like Antigravity) use `GEMINI.md` global rules or user rules for startup behavior. The server resolves the role from the dashboard automatically.
470
+
471
+ ### Global Rules (`~/.gemini/GEMINI.md`)
472
+
473
+ ```markdown
474
+ ## Prism MCP Memory Auto-Load (CRITICAL)
475
+ At the start of every new session, call `mcp__prism-mcp__session_load_context`
476
+ for these projects:
477
+ - `my-project` (level=standard)
478
+ - `my-other-project` (level=standard)
479
+
480
+ After both succeed, print PRISM_CONTEXT_LOADED.
481
+ ```
482
+
483
+ ### User Rules (Antigravity Settings)
484
+
485
+ If your Gemini client supports user rules, add the same instructions there. The key points:
486
+
487
+ 1. **Call `session_load_context` as a tool** โ€” not `read_resource`. Only the tool returns the `[๐Ÿ‘ค AGENT IDENTITY]` block.
488
+ 2. **Verify** โ€” confirm the response includes `version` and `last_summary`.
489
+
490
+ ### Session End
491
+
492
+ At the end of each session, save state:
493
+
494
+ ```markdown
495
+ ## Session End Protocol
496
+ 1) Call `mcp__prism-mcp__session_save_ledger` with project and summary.
497
+ 2) Call `mcp__prism-mcp__session_save_handoff` with expected_version from the loaded version.
498
+ ```
396
499
 
397
500
  ---
398
501
 
@@ -763,48 +866,11 @@ The agent boots up knowing exactly what to do โ€” zero prompting needed.
763
866
 
764
867
  For the best experience, configure your AI coding assistant to **automatically call `session_load_context`** at the start of every new session. This ensures your agent always boots with full project memory โ€” no manual prompting needed.
765
868
 
766
- <details>
767
- <summary><strong>Claude Code (.clauderules / CLAUDE.md)</strong></summary>
768
-
769
- Add this rule to your project's `.clauderules` or `CLAUDE.md`:
770
-
771
- ```markdown
772
-
773
- ## Prism MCP Memory Auto-Load (CRITICAL)
774
- At the start of every new session, you MUST call `session_load_context`
775
- at the `standard` level for these projects:
776
- - `my-project`
777
- - `my-other-project`
778
-
779
- Do NOT skip this step.
780
- ```
781
-
782
- </details>
783
-
784
- <details>
785
- <summary><strong>Gemini / Antigravity (GEMINI.md)</strong></summary>
786
-
787
- Add this rule to your `~/.gemini/GEMINI.md` global rules file:
788
-
789
- ```markdown
790
-
791
- ## Prism MCP Memory Auto-Load (CRITICAL)
792
-
793
- **At the start of every new session**, immediately after displaying
794
- the startup block, you MUST call `session_load_context` (via the
795
- `prism-mcp` MCP server) at the `standard` level for these projects:
796
- - `my-project`
797
- - `my-other-project`
869
+ See the full setup guides for each client:
870
+ - **[Claude Code Integration (Hooks)](#claude-code-integration-hooks)** โ€” `SessionStart` and `Stop` hook JSON samples for `~/.claude/settings.json`
871
+ - **[Gemini / Antigravity Integration](#gemini--antigravity-integration)** โ€” global rules for `~/.gemini/GEMINI.md` or user rules
798
872
 
799
- This ensures accumulated project memory, pending TODOs, and key context
800
- from previous sessions are always available. Do NOT skip this step.
801
-
802
- **IMPORTANT:** The `prism-mcp` MCP server is always available.
803
- Do NOT display any warnings or notes about MCP server availability
804
- โ€” just call the tools directly. Never claim the server is unavailable.
805
- ```
806
-
807
- </details>
873
+ > **Key principle:** Never hardcode a `role` in your hooks or rules. Set your role once in the **Mind Palace Dashboard โš™๏ธ Settings โ†’ Agent Identity**, and Prism automatically resolves it for every tool call across all clients. See [Role Resolution](#role-resolution--no-hardcoding-needed).
808
874
 
809
875
  > **Tip:** Replace `my-project` with your actual project identifiers. You can list as many projects as you need โ€” each one gets its own independent memory timeline.
810
876
 
@@ -1136,6 +1202,18 @@ See [What's New in v3.0.1](#whats-new-in-v301---agent-identity--brain-clean-up-)
1136
1202
 
1137
1203
  See [What's New in v3.0.0 โ€” Agent Hivemind](#whats-new-in-v300---agent-hivemind-) above.
1138
1204
 
1205
+ ### ๐Ÿ”œ v4.0 โ€” Active Behavioral Memory (In Development)
1206
+
1207
+ Evolves Prism from passive session logging to an **experience learning engine** that shapes agent behavior over time.
1208
+
1209
+ | Feature | Description |
1210
+ |---------|-------------|
1211
+ | **Structured Event Types** | Typed experience events (`correction`, `success`, `failure`, `learning`) with `confidence_score` (1-100) โ€” agents don't just log, they learn |
1212
+ | **Token-Budgeted Context Loading** | `max_tokens` param on `session_load_context` โ€” guarantees constant cost regardless of DB size (1 token โ‰ˆ 4 chars heuristic) |
1213
+ | **`session_save_experience` Tool** | Dedicated tool for behavioral data: context โ†’ action โ†’ outcome โ†’ correction. Auto-extracts keywords, seeds importance |
1214
+ | **Insight Graduation System** | `knowledge_upvote` / `knowledge_downvote` tools. Importance โ‰ฅ 7 โ†’ graduated rule. 30-day decay prevents bloat |
1215
+ | **Behavioral Warnings** | High-importance corrections auto-surface as `[โš ๏ธ BEHAVIORAL WARNINGS]` in `session_load_context` โ€” agents proactively avoid past mistakes |
1216
+
1139
1217
  ### ๐Ÿš€ Future Ideas
1140
1218
 
1141
1219
  | Feature | Issue | Description |
package/dist/server.js CHANGED
@@ -84,7 +84,9 @@ SESSION_HEALTH_CHECK_TOOL,
84
84
  // โ”€โ”€โ”€ Phase 2: GDPR Memory Deletion tool definition โ”€โ”€โ”€
85
85
  SESSION_FORGET_MEMORY_TOOL,
86
86
  // โ”€โ”€โ”€ v3.1: TTL Retention tool โ”€โ”€โ”€
87
- KNOWLEDGE_SET_RETENTION_TOOL, sessionSaveLedgerHandler, sessionSaveHandoffHandler, sessionLoadContextHandler, knowledgeSearchHandler, knowledgeForgetHandler,
87
+ KNOWLEDGE_SET_RETENTION_TOOL,
88
+ // v4.0: Active Behavioral Memory tools
89
+ SESSION_SAVE_EXPERIENCE_TOOL, KNOWLEDGE_UPVOTE_TOOL, KNOWLEDGE_DOWNVOTE_TOOL, sessionSaveLedgerHandler, sessionSaveHandoffHandler, sessionLoadContextHandler, knowledgeSearchHandler, knowledgeForgetHandler,
88
90
  // โ”€โ”€โ”€ v0.4.0: New tool handlers โ”€โ”€โ”€
89
91
  compactLedgerHandler, sessionSearchMemoryHandler,
90
92
  // โ”€โ”€โ”€ v2.0: Time Travel handlers โ”€โ”€โ”€
@@ -97,6 +99,8 @@ sessionHealthCheckHandler,
97
99
  sessionForgetMemoryHandler,
98
100
  // โ”€โ”€โ”€ v3.1: TTL Retention handler โ”€โ”€โ”€
99
101
  knowledgeSetRetentionHandler,
102
+ // v4.0: Active Behavioral Memory handlers
103
+ sessionSaveExperienceHandler, knowledgeUpvoteHandler, knowledgeDownvoteHandler,
100
104
  // โ”€โ”€โ”€ v3.0: Agent Hivemind tools โ”€โ”€โ”€
101
105
  AGENT_REGISTRY_TOOLS, agentRegisterHandler, agentHeartbeatHandler, agentListTeamHandler, } from "./tools/index.js";
102
106
  // โ”€โ”€โ”€ Dynamic Tool Registration โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@@ -133,6 +137,10 @@ const SESSION_MEMORY_TOOLS = [
133
137
  SESSION_FORGET_MEMORY_TOOL, // session_forget_memory โ€” GDPR-compliant memory deletion (Phase 2)
134
138
  // โ”€โ”€โ”€ v3.1: TTL Retention tool โ”€โ”€โ”€
135
139
  KNOWLEDGE_SET_RETENTION_TOOL, // knowledge_set_retention โ€” set auto-expiry TTL for a project
140
+ // โ”€โ”€โ”€ v4.0: Active Behavioral Memory tools โ”€โ”€โ”€
141
+ SESSION_SAVE_EXPERIENCE_TOOL, // session_save_experience โ€” record typed experience events
142
+ KNOWLEDGE_UPVOTE_TOOL, // knowledge_upvote โ€” increase entry importance
143
+ KNOWLEDGE_DOWNVOTE_TOOL, // knowledge_downvote โ€” decrease entry importance
136
144
  ];
137
145
  // Combine: always list ALL tools so scanners (Glama, Smithery, MCP Registry)
138
146
  // can enumerate the full capability set. Runtime guards in the CallTool handler
@@ -586,6 +594,19 @@ export function createServer() {
586
594
  if (!SESSION_MEMORY_ENABLED)
587
595
  throw new Error("Session memory not configured. Set SUPABASE_URL and SUPABASE_KEY.");
588
596
  return await knowledgeSetRetentionHandler(args);
597
+ // โ”€โ”€โ”€ v4.0: Active Behavioral Memory Tools โ”€โ”€โ”€
598
+ case "session_save_experience":
599
+ if (!SESSION_MEMORY_ENABLED)
600
+ throw new Error("Session memory not configured. Set SUPABASE_URL and SUPABASE_KEY.");
601
+ return await sessionSaveExperienceHandler(args);
602
+ case "knowledge_upvote":
603
+ if (!SESSION_MEMORY_ENABLED)
604
+ throw new Error("Session memory not configured. Set SUPABASE_URL and SUPABASE_KEY.");
605
+ return await knowledgeUpvoteHandler(args);
606
+ case "knowledge_downvote":
607
+ if (!SESSION_MEMORY_ENABLED)
608
+ throw new Error("Session memory not configured. Set SUPABASE_URL and SUPABASE_KEY.");
609
+ return await knowledgeDownvoteHandler(args);
589
610
  // โ”€โ”€โ”€ v3.0: Agent Hivemind Tools โ”€โ”€โ”€
590
611
  case "agent_register":
591
612
  if (!SESSION_MEMORY_ENABLED)
@@ -303,6 +303,36 @@ export class SqliteStorage {
303
303
  updated_at TEXT DEFAULT (datetime('now'))
304
304
  )
305
305
  `);
306
+ // โ”€โ”€โ”€ v4.0 Migration: Active Behavioral Memory โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
307
+ // Three new columns for typed experience events and insight graduation.
308
+ // Uses the proven idempotent try/catch pattern for safe ALTER TABLE.
309
+ try {
310
+ await this.db.execute(`ALTER TABLE session_ledger ADD COLUMN event_type TEXT DEFAULT 'session'`);
311
+ debugLog("[SqliteStorage] v4.0 migration: added event_type column");
312
+ }
313
+ catch (e) {
314
+ if (!e.message?.includes("duplicate column name"))
315
+ throw e;
316
+ }
317
+ try {
318
+ await this.db.execute(`ALTER TABLE session_ledger ADD COLUMN confidence_score INTEGER DEFAULT NULL`);
319
+ debugLog("[SqliteStorage] v4.0 migration: added confidence_score column");
320
+ }
321
+ catch (e) {
322
+ if (!e.message?.includes("duplicate column name"))
323
+ throw e;
324
+ }
325
+ try {
326
+ await this.db.execute(`ALTER TABLE session_ledger ADD COLUMN importance INTEGER DEFAULT 0`);
327
+ debugLog("[SqliteStorage] v4.0 migration: added importance column");
328
+ }
329
+ catch (e) {
330
+ if (!e.message?.includes("duplicate column name"))
331
+ throw e;
332
+ }
333
+ // Composite indexes for behavioral queries (idempotent via IF NOT EXISTS)
334
+ await this.db.execute(`CREATE INDEX IF NOT EXISTS idx_ledger_event_type ON session_ledger(event_type)`);
335
+ await this.db.execute(`CREATE INDEX IF NOT EXISTS idx_ledger_importance ON session_ledger(importance DESC)`);
306
336
  }
307
337
  // โ”€โ”€โ”€ PostgREST Filter Parser โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
308
338
  //
@@ -434,8 +464,10 @@ export class SqliteStorage {
434
464
  await this.db.execute({
435
465
  sql: `INSERT INTO session_ledger
436
466
  (id, project, conversation_id, user_id, role, summary, todos, files_changed,
437
- decisions, keywords, is_rollup, rollup_count, title, agent_name, created_at, session_date)
438
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
467
+ decisions, keywords, is_rollup, rollup_count, title, agent_name,
468
+ event_type, confidence_score, importance,
469
+ created_at, session_date)
470
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
439
471
  args: [
440
472
  id,
441
473
  entry.project,
@@ -451,6 +483,9 @@ export class SqliteStorage {
451
483
  entry.rollup_count || 0,
452
484
  entry.is_rollup ? `Session Rollup (${entry.rollup_count || 0} entries)` : null,
453
485
  entry.is_rollup ? "prism-compactor" : null,
486
+ entry.event_type || "session", // v4.0: default to 'session'
487
+ entry.confidence_score ?? null, // v4.0: nullable
488
+ entry.importance || 0, // v4.0: default to 0
454
489
  now,
455
490
  now,
456
491
  ],
@@ -674,6 +709,27 @@ export class SqliteStorage {
674
709
  context.active_decisions = this.parseJsonColumn(handoff.active_decisions);
675
710
  context.active_branch = handoff.active_branch;
676
711
  context.key_context = handoff.key_context;
712
+ // โ”€โ”€โ”€ v4.0: Behavioral Warnings (Standard & Deep) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
713
+ // Hoisted above the branch so both levels get warnings without duplication.
714
+ // Filters: role-scoped, non-archived, non-deleted, high importance.
715
+ const warningsResult = await this.db.execute({
716
+ sql: `SELECT summary, importance
717
+ FROM session_ledger
718
+ WHERE project = ? AND user_id = ? AND role = ?
719
+ AND event_type = 'correction'
720
+ AND importance >= 3
721
+ AND deleted_at IS NULL
722
+ AND archived_at IS NULL
723
+ ORDER BY importance DESC
724
+ LIMIT 5`,
725
+ args: [project, userId, effectiveRole],
726
+ });
727
+ if (warningsResult.rows.length > 0) {
728
+ context.behavioral_warnings = warningsResult.rows.map(r => ({
729
+ summary: r.summary,
730
+ importance: r.importance,
731
+ }));
732
+ }
677
733
  if (level === "standard") {
678
734
  // Add recent ledger entries (role-scoped)
679
735
  const recentLedger = await this.db.execute({
@@ -1297,6 +1353,42 @@ export class SqliteStorage {
1297
1353
  });
1298
1354
  const expired = result.rowsAffected || 0;
1299
1355
  debugLog(`[SqliteStorage] TTL sweep: expired ${expired} entries for "${project}" (cutoff: ${cutoffStr})`);
1356
+ // โ”€โ”€โ”€ v4.0: Importance Decay โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1357
+ // Decay importance of experience entries not referenced in 30 days.
1358
+ // This prevents "Insight Bloat" โ€” old corrections that are no longer
1359
+ // relevant gradually lose their weight and stop appearing as warnings.
1360
+ // Only targets typed experience events (event_type != 'session'),
1361
+ // so regular session logs are never affected.
1362
+ const decayResult = await this.db.execute({
1363
+ sql: `UPDATE session_ledger
1364
+ SET importance = MAX(0, importance - 1)
1365
+ WHERE project = ? AND user_id = ?
1366
+ AND importance > 0
1367
+ AND event_type != 'session'
1368
+ AND created_at < datetime('now', '-30 days')
1369
+ AND deleted_at IS NULL`,
1370
+ args: [project, userId],
1371
+ });
1372
+ const decayed = decayResult.rowsAffected || 0;
1373
+ if (decayed > 0) {
1374
+ debugLog(`[SqliteStorage] Importance decay: reduced ${decayed} entries for "${project}"`);
1375
+ }
1300
1376
  return { expired };
1301
1377
  }
1378
+ // โ”€โ”€โ”€ v4.0: Insight Graduation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1379
+ //
1380
+ // Adjusts the importance score of a ledger entry.
1381
+ // Used by knowledge_upvote (+1) and knowledge_downvote (-1).
1382
+ // Importance is clamped via MAX(0, ...) โ€” never goes negative.
1383
+ // Entries reaching importance >= 7 are considered "graduated"
1384
+ // and will appear prominently in behavioral warnings.
1385
+ async adjustImportance(id, delta, userId) {
1386
+ await this.db.execute({
1387
+ sql: `UPDATE session_ledger
1388
+ SET importance = MAX(0, importance + ?)
1389
+ WHERE id = ? AND user_id = ?`,
1390
+ args: [delta, id, userId],
1391
+ });
1392
+ debugLog(`[SqliteStorage] Adjusted importance for ${id} by ${delta > 0 ? "+" : ""}${delta}`);
1393
+ }
1302
1394
  }
@@ -41,6 +41,10 @@ export class SupabaseStorage {
41
41
  title: `Session Rollup (${entry.rollup_count || 0} entries)`,
42
42
  agent_name: "prism-compactor",
43
43
  }),
44
+ // v4.0: Active Behavioral Memory fields
45
+ event_type: entry.event_type || "session",
46
+ ...(entry.confidence_score !== undefined && { confidence_score: entry.confidence_score }),
47
+ importance: entry.importance || 0,
44
48
  };
45
49
  return supabasePost("session_ledger", record);
46
50
  }
@@ -348,4 +352,27 @@ export class SupabaseStorage {
348
352
  debugLog(`[SupabaseStorage] TTL sweep completed for "${project}" (cutoff: ${cutoffStr})`);
349
353
  return { expired: 0 };
350
354
  }
355
+ // โ”€โ”€โ”€ v4.0: Insight Graduation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
356
+ async adjustImportance(id, delta, userId) {
357
+ // Supabase PATCH can't do MAX(0, importance + delta) directly.
358
+ // Fetch current value first, compute new, then patch.
359
+ try {
360
+ const data = await supabaseGet("session_ledger", {
361
+ id: `eq.${id}`,
362
+ user_id: `eq.${userId}`,
363
+ select: "importance",
364
+ });
365
+ const rows = Array.isArray(data) ? data : [];
366
+ const current = rows[0]?.importance ?? 0;
367
+ const newVal = Math.max(0, current + delta);
368
+ await supabasePatch("session_ledger", { importance: newVal }, {
369
+ id: `eq.${id}`,
370
+ user_id: `eq.${userId}`,
371
+ });
372
+ debugLog(`[SupabaseStorage] Adjusted importance for ${id} by ${delta > 0 ? "+" : ""}${delta} (${current} โ†’ ${newVal})`);
373
+ }
374
+ catch (e) {
375
+ debugLog("[SupabaseStorage] adjustImportance failed: " + (e instanceof Error ? e.message : String(e)));
376
+ }
377
+ }
351
378
  }
@@ -26,8 +26,8 @@ export { webSearchHandler, braveWebSearchCodeModeHandler, localSearchHandler, br
26
26
  // This file always exports them โ€” server.ts decides whether to include them in the tool list.
27
27
  //
28
28
  // v0.4.0: Added SESSION_COMPACT_LEDGER_TOOL and SESSION_SEARCH_MEMORY_TOOL
29
- export { SESSION_SAVE_LEDGER_TOOL, SESSION_SAVE_HANDOFF_TOOL, SESSION_LOAD_CONTEXT_TOOL, KNOWLEDGE_SEARCH_TOOL, KNOWLEDGE_FORGET_TOOL, SESSION_COMPACT_LEDGER_TOOL, SESSION_SEARCH_MEMORY_TOOL, MEMORY_HISTORY_TOOL, MEMORY_CHECKOUT_TOOL, SESSION_SAVE_IMAGE_TOOL, SESSION_VIEW_IMAGE_TOOL, SESSION_HEALTH_CHECK_TOOL, SESSION_FORGET_MEMORY_TOOL, KNOWLEDGE_SET_RETENTION_TOOL } from "./sessionMemoryDefinitions.js";
30
- export { sessionSaveLedgerHandler, sessionSaveHandoffHandler, sessionLoadContextHandler, knowledgeSearchHandler, knowledgeForgetHandler, sessionSearchMemoryHandler, backfillEmbeddingsHandler, memoryHistoryHandler, memoryCheckoutHandler, sessionSaveImageHandler, sessionViewImageHandler, sessionHealthCheckHandler, sessionForgetMemoryHandler, knowledgeSetRetentionHandler } from "./sessionMemoryHandlers.js";
29
+ export { SESSION_SAVE_LEDGER_TOOL, SESSION_SAVE_HANDOFF_TOOL, SESSION_LOAD_CONTEXT_TOOL, KNOWLEDGE_SEARCH_TOOL, KNOWLEDGE_FORGET_TOOL, SESSION_COMPACT_LEDGER_TOOL, SESSION_SEARCH_MEMORY_TOOL, MEMORY_HISTORY_TOOL, MEMORY_CHECKOUT_TOOL, SESSION_SAVE_IMAGE_TOOL, SESSION_VIEW_IMAGE_TOOL, SESSION_HEALTH_CHECK_TOOL, SESSION_FORGET_MEMORY_TOOL, KNOWLEDGE_SET_RETENTION_TOOL, SESSION_SAVE_EXPERIENCE_TOOL, KNOWLEDGE_UPVOTE_TOOL, KNOWLEDGE_DOWNVOTE_TOOL } from "./sessionMemoryDefinitions.js";
30
+ export { sessionSaveLedgerHandler, sessionSaveHandoffHandler, sessionLoadContextHandler, knowledgeSearchHandler, knowledgeForgetHandler, sessionSearchMemoryHandler, backfillEmbeddingsHandler, memoryHistoryHandler, memoryCheckoutHandler, sessionSaveImageHandler, sessionViewImageHandler, sessionHealthCheckHandler, sessionForgetMemoryHandler, knowledgeSetRetentionHandler, sessionSaveExperienceHandler, knowledgeUpvoteHandler, knowledgeDownvoteHandler } from "./sessionMemoryHandlers.js";
31
31
  // โ”€โ”€ Compaction Handler (v0.4.0 โ€” Enhancement #2) โ”€โ”€
32
32
  // The compaction handler is in a separate file because it's significantly
33
33
  // more complex than the other session memory handlers (chunked Gemini
@@ -37,7 +37,7 @@ export const SESSION_SAVE_LEDGER_TOOL = {
37
37
  },
38
38
  role: {
39
39
  type: "string",
40
- description: "v3.0: Agent role for Hivemind scoping (e.g., 'dev', 'qa', 'pm'). Defaults to 'global'.",
40
+ description: "Optional. Agent role for Hivemind scoping (e.g., 'dev', 'qa', 'pm'). Omit to let the server auto-resolve from dashboard settings.",
41
41
  },
42
42
  },
43
43
  required: ["project", "conversation_id", "summary"],
@@ -90,7 +90,7 @@ export const SESSION_SAVE_HANDOFF_TOOL = {
90
90
  },
91
91
  role: {
92
92
  type: "string",
93
- description: "v3.0: Agent role for Hivemind scoping (e.g., 'dev', 'qa', 'pm'). Defaults to 'global'.",
93
+ description: "Optional. Agent role for Hivemind scoping (e.g., 'dev', 'qa', 'pm'). Omit to let the server auto-resolve from dashboard settings.",
94
94
  },
95
95
  },
96
96
  required: ["project"],
@@ -119,7 +119,12 @@ export const SESSION_LOAD_CONTEXT_TOOL = {
119
119
  },
120
120
  role: {
121
121
  type: "string",
122
- description: "v3.0: Agent role for Hivemind scoping (e.g., 'dev', 'qa', 'pm'). Defaults to 'global'. When set, also injects active_team roster.",
122
+ description: "Optional. Agent role for Hivemind scoping (e.g., 'dev', 'qa', 'pm'). Omit to let the server auto-resolve from dashboard settings. When set, also injects active_team roster.",
123
+ },
124
+ // v4.0: Token Budget
125
+ max_tokens: {
126
+ type: "integer",
127
+ description: "Maximum token budget for context response. Uses 1 token โ‰ˆ 4 chars heuristic. When set, the response is truncated to fit within the budget. Default: unlimited.",
123
128
  },
124
129
  },
125
130
  required: ["project"],
@@ -635,3 +640,106 @@ export function isKnowledgeSetRetentionArgs(args) {
635
640
  "ttl_days" in args &&
636
641
  typeof args.ttl_days === "number");
637
642
  }
643
+ // โ”€โ”€โ”€ v4.0: Active Behavioral Memory Tools โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
644
+ export const SESSION_SAVE_EXPERIENCE_TOOL = {
645
+ name: "session_save_experience",
646
+ description: "Record a typed experience event. Unlike session_save_ledger (flat logs), " +
647
+ "this captures structured behavioral data for pattern detection.\n\n" +
648
+ "Event Types:\n" +
649
+ "- **correction**: Agent was corrected by user\n" +
650
+ "- **success**: Task completed successfully\n" +
651
+ "- **failure**: Task failed\n" +
652
+ "- **learning**: New knowledge acquired",
653
+ inputSchema: {
654
+ type: "object",
655
+ properties: {
656
+ project: {
657
+ type: "string",
658
+ description: "Project identifier.",
659
+ },
660
+ event_type: {
661
+ type: "string",
662
+ enum: ["correction", "success", "failure", "learning"],
663
+ description: "Type of behavioral event.",
664
+ },
665
+ context: {
666
+ type: "string",
667
+ description: "What the agent was doing when the event occurred.",
668
+ },
669
+ action: {
670
+ type: "string",
671
+ description: "What action was tried.",
672
+ },
673
+ outcome: {
674
+ type: "string",
675
+ description: "What happened as a result.",
676
+ },
677
+ correction: {
678
+ type: "string",
679
+ description: "What should have been done instead (for correction type).",
680
+ },
681
+ confidence_score: {
682
+ type: "integer",
683
+ minimum: 1,
684
+ maximum: 100,
685
+ description: "Agent's confidence in the outcome (1-100).",
686
+ },
687
+ role: {
688
+ type: "string",
689
+ description: "Optional. Agent role for Hivemind scoping. Omit to let the server auto-resolve from dashboard settings.",
690
+ },
691
+ },
692
+ required: ["project", "event_type", "context", "action", "outcome"],
693
+ },
694
+ };
695
+ export function isSessionSaveExperienceArgs(args) {
696
+ return (typeof args === "object" &&
697
+ args !== null &&
698
+ "project" in args &&
699
+ typeof args.project === "string" &&
700
+ "event_type" in args &&
701
+ typeof args.event_type === "string" &&
702
+ "context" in args &&
703
+ typeof args.context === "string" &&
704
+ "action" in args &&
705
+ typeof args.action === "string" &&
706
+ "outcome" in args &&
707
+ typeof args.outcome === "string");
708
+ }
709
+ export const KNOWLEDGE_UPVOTE_TOOL = {
710
+ name: "knowledge_upvote",
711
+ description: "Upvote a memory entry to increase its importance (graduation). " +
712
+ "Entries with importance >= 7 become 'graduated' insights that always " +
713
+ "surface in behavioral warnings.",
714
+ inputSchema: {
715
+ type: "object",
716
+ properties: {
717
+ id: {
718
+ type: "string",
719
+ description: "The UUID of the ledger entry to upvote.",
720
+ },
721
+ },
722
+ required: ["id"],
723
+ },
724
+ };
725
+ export const KNOWLEDGE_DOWNVOTE_TOOL = {
726
+ name: "knowledge_downvote",
727
+ description: "Downvote a memory entry to decrease its importance. " +
728
+ "Importance cannot go below 0.",
729
+ inputSchema: {
730
+ type: "object",
731
+ properties: {
732
+ id: {
733
+ type: "string",
734
+ description: "The UUID of the ledger entry to downvote.",
735
+ },
736
+ },
737
+ required: ["id"],
738
+ },
739
+ };
740
+ export function isKnowledgeVoteArgs(args) {
741
+ return (typeof args === "object" &&
742
+ args !== null &&
743
+ "id" in args &&
744
+ typeof args.id === "string");
745
+ }
@@ -33,7 +33,8 @@ import { captureLocalEnvironment } from "../utils/autoCapture.js";
33
33
  import { isSessionSaveLedgerArgs, isSessionSaveHandoffArgs, isSessionLoadContextArgs, isKnowledgeSearchArgs, isKnowledgeForgetArgs, isSessionSearchMemoryArgs, isBackfillEmbeddingsArgs, isMemoryHistoryArgs, isMemoryCheckoutArgs, isSessionHealthCheckArgs, // v2.2.0: health check type guard
34
34
  isSessionForgetMemoryArgs, // Phase 2: GDPR-compliant memory deletion type guard
35
35
  isKnowledgeSetRetentionArgs, // v3.1: TTL retention policy type guard
36
- } from "./sessionMemoryDefinitions.js";
36
+ // v4.0: Active Behavioral Memory type guards
37
+ isSessionSaveExperienceArgs, isKnowledgeVoteArgs, } from "./sessionMemoryDefinitions.js";
37
38
  // v3.1: In-memory debounce lock for auto-compaction.
38
39
  // Prevents multiple concurrent Gemini compaction tasks for the same project
39
40
  // when many agents call session_save_ledger at the same time.
@@ -359,6 +360,7 @@ export async function sessionLoadContextHandler(args) {
359
360
  throw new Error("Invalid arguments for session_load_context");
360
361
  }
361
362
  const { project, level = "standard", role } = args;
363
+ const maxTokens = args.max_tokens; // v4.0
362
364
  const agentName = await getSetting("agent_name", "");
363
365
  const validLevels = ["quick", "standard", "deep"];
364
366
  if (!validLevels.includes(level)) {
@@ -533,11 +535,27 @@ export async function sessionLoadContextHandler(args) {
533
535
  const skillPart = skillLoaded ? ` ยท ๐Ÿ“œ \`${effectiveRole}\` skill loaded` : (effectiveRole ? " ยท ๐Ÿ“œ No skill configured" : "");
534
536
  greetingBlock = `\n\n[๐Ÿ‘ค AGENT IDENTITY]\n${namePart}${rolePart}${skillPart}`;
535
537
  }
538
+ // Build the response object before v4.0 augmentations
539
+ let responseText = `๐Ÿ“‹ Session context for "${project}" (${level}):\n\n${formattedContext.trim()}${driftReport}${briefingBlock}${greetingBlock}${visualMemoryBlock}${skillBlock}${versionNote}`;
540
+ // โ”€โ”€โ”€ v4.0: Behavioral Warnings Injection โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
541
+ // If loadContext returned behavioral_warnings, add them to the
542
+ // formatted output so the agent sees them prominently.
543
+ const behavWarnings = data?.behavioral_warnings;
544
+ if (behavWarnings && behavWarnings.length > 0) {
545
+ responseText += `\n\n[โš ๏ธ BEHAVIORAL WARNINGS]\n` +
546
+ behavWarnings.map(w => `- ${w.summary} (importance: ${w.importance})`).join("\n");
547
+ }
548
+ // โ”€โ”€โ”€ v4.0: Token Budget Truncation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
549
+ // 1 token โ‰ˆ 4 chars heuristic. Truncate if response exceeds budget.
550
+ if (maxTokens && maxTokens > 0) {
551
+ const maxChars = maxTokens * 4;
552
+ if (responseText.length > maxChars) {
553
+ responseText = responseText.slice(0, maxChars) + "\n\n[โ€ฆ truncated to fit token budget]";
554
+ debugLog(`[session_load_context] Truncated response to ${maxTokens} tokens (${maxChars} chars)`);
555
+ }
556
+ }
536
557
  return {
537
- content: [{
538
- type: "text",
539
- text: `๐Ÿ“‹ Session context for "${project}" (${level}):\n\n${formattedContext.trim()}${driftReport}${briefingBlock}${greetingBlock}${visualMemoryBlock}${skillBlock}${versionNote}`,
540
- }],
558
+ content: [{ type: "text", text: responseText }],
541
559
  isError: false,
542
560
  };
543
561
  }
@@ -1553,3 +1571,116 @@ export async function knowledgeSetRetentionHandler(args) {
1553
1571
  isError: false,
1554
1572
  };
1555
1573
  }
1574
+ // โ”€โ”€โ”€ v4.0: Experience Save Handler โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1575
+ /**
1576
+ * Records a typed experience event for behavioral pattern detection.
1577
+ * Unlike session_save_ledger (flat logs), this captures structured
1578
+ * context โ†’ action โ†’ outcome data with confidence scoring.
1579
+ *
1580
+ * Corrections start with importance = 1 to jumpstart visibility;
1581
+ * all other event types start at 0.
1582
+ */
1583
+ export async function sessionSaveExperienceHandler(args) {
1584
+ if (!isSessionSaveExperienceArgs(args)) {
1585
+ throw new Error("Invalid arguments for session_save_experience");
1586
+ }
1587
+ const { project, event_type, context: ctx, action, outcome, correction, confidence_score, role } = args;
1588
+ const storage = await getStorage();
1589
+ debugLog(`[session_save_experience] Recording ${event_type} event for project="${project}"`);
1590
+ // Format structured summary from event fields
1591
+ let summary = `[${event_type.toUpperCase()}] ${ctx} โ†’ ${action} โ†’ ${outcome}`;
1592
+ if (event_type === "correction" && correction) {
1593
+ summary += ` | CORRECTION: ${correction}`;
1594
+ }
1595
+ // Auto-extract keywords from the structured summary
1596
+ const keywords = toKeywordArray(summary);
1597
+ debugLog(`[session_save_experience] Extracted ${keywords.length} keywords: ${keywords.slice(0, 5).join(", ")}...`);
1598
+ const effectiveRole = role || await getSetting("default_role", "global");
1599
+ const result = await storage.saveLedger({
1600
+ project,
1601
+ conversation_id: "experience-event",
1602
+ user_id: PRISM_USER_ID,
1603
+ role: effectiveRole,
1604
+ event_type,
1605
+ summary,
1606
+ decisions: [
1607
+ `Context: ${ctx}`,
1608
+ `Action: ${action}`,
1609
+ `Outcome: ${outcome}`,
1610
+ ...(correction ? [`Correction: ${correction}`] : []),
1611
+ ],
1612
+ keywords,
1613
+ confidence_score: typeof confidence_score === "number" ? confidence_score : undefined,
1614
+ // Corrections start with importance 1 to jumpstart visibility
1615
+ importance: event_type === "correction" ? 1 : 0,
1616
+ });
1617
+ // Fire-and-forget embedding generation
1618
+ if (GOOGLE_API_KEY && result) {
1619
+ const embeddingText = summary;
1620
+ const savedEntry = Array.isArray(result) ? result[0] : result;
1621
+ const entryId = savedEntry?.id;
1622
+ if (entryId) {
1623
+ generateEmbedding(embeddingText)
1624
+ .then(async (embedding) => {
1625
+ await storage.patchLedger(entryId, {
1626
+ embedding: JSON.stringify(embedding),
1627
+ });
1628
+ debugLog(`[session_save_experience] Embedding saved for entry ${entryId}`);
1629
+ })
1630
+ .catch((err) => {
1631
+ console.error(`[session_save_experience] Embedding failed (non-fatal): ${err.message}`);
1632
+ });
1633
+ }
1634
+ }
1635
+ return {
1636
+ content: [{
1637
+ type: "text",
1638
+ text: `โœ… Experience recorded: ${event_type} for project "${project}"\n` +
1639
+ `Summary: ${summary}\n` +
1640
+ (confidence_score !== undefined ? `Confidence: ${confidence_score}%\n` : "") +
1641
+ `Importance: ${event_type === "correction" ? 1 : 0} (upvote to increase)`,
1642
+ }],
1643
+ isError: false,
1644
+ };
1645
+ }
1646
+ // โ”€โ”€โ”€ v4.0: Knowledge Upvote Handler โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1647
+ /**
1648
+ * Upvotes a ledger entry to increase its importance.
1649
+ * Entries reaching importance >= 7 are considered "graduated"
1650
+ * and will always surface as Behavioral Warnings.
1651
+ */
1652
+ export async function knowledgeUpvoteHandler(args) {
1653
+ if (!isKnowledgeVoteArgs(args)) {
1654
+ throw new Error("Invalid arguments for knowledge_upvote");
1655
+ }
1656
+ const storage = await getStorage();
1657
+ await storage.adjustImportance(args.id, 1, PRISM_USER_ID);
1658
+ debugLog(`[knowledge_upvote] Upvoted entry ${args.id}`);
1659
+ return {
1660
+ content: [{
1661
+ type: "text",
1662
+ text: `๐Ÿ‘ Entry ${args.id} upvoted (+1 importance).`,
1663
+ }],
1664
+ isError: false,
1665
+ };
1666
+ }
1667
+ // โ”€โ”€โ”€ v4.0: Knowledge Downvote Handler โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1668
+ /**
1669
+ * Downvotes a ledger entry to decrease its importance.
1670
+ * Importance is clamped at 0 (never goes negative).
1671
+ */
1672
+ export async function knowledgeDownvoteHandler(args) {
1673
+ if (!isKnowledgeVoteArgs(args)) {
1674
+ throw new Error("Invalid arguments for knowledge_downvote");
1675
+ }
1676
+ const storage = await getStorage();
1677
+ await storage.adjustImportance(args.id, -1, PRISM_USER_ID);
1678
+ debugLog(`[knowledge_downvote] Downvoted entry ${args.id}`);
1679
+ return {
1680
+ content: [{
1681
+ type: "text",
1682
+ text: `๐Ÿ‘Ž Entry ${args.id} downvoted (-1 importance).`,
1683
+ }],
1684
+ isError: false,
1685
+ };
1686
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prism-mcp-server",
3
- "version": "3.1.1",
3
+ "version": "4.0.0",
4
4
  "mcpName": "io.github.dcostenco/prism-mcp",
5
5
  "description": "The Mind Palace for AI Agents โ€” local-first MCP server with persistent memory (SQLite/Supabase), visual dashboard, time travel, multi-agent sync, Morning Briefings, reality drift detection, code mode templates, semantic vector search, and Brave Search + Gemini analysis. Zero-config local mode.",
6
6
  "module": "index.ts",