prism-mcp-server 9.2.6 β 9.3.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 +54 -5
- package/dist/config.js +14 -0
- package/dist/darkfactory/safetyController.js +6 -0
- package/dist/storage/sqlite.js +17 -2
- package/dist/storage/supabase.js +17 -2
- package/dist/utils/crdtMerge.js +22 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -503,6 +503,43 @@ A gorgeous glassmorphism UI at `localhost:3000` that lets you see exactly what y
|
|
|
503
503
|
### 𧬠10à Memory Compression
|
|
504
504
|
Powered by a pure TypeScript port of Google's TurboQuant (inspired by Google's ICLR research), Prism compresses 768-dim embeddings from **3,072 bytes β ~400 bytes** β enabling decades of session history on a standard laptop. No native modules. No vector database required. To mitigate quantization degradation (where repeated compress/decompress cycles could smear subtle corrections after 10k+ memories), Prism leverages autonomous **ledger compaction** and **Deep Storage cleanup** to guarantee high-fidelity memory integrity over time.
|
|
505
505
|
|
|
506
|
+
<details>
|
|
507
|
+
<summary><strong>π 1M-Vector Benchmark (d=768, 4-bit)</strong></summary>
|
|
508
|
+
|
|
509
|
+
Validated on 1,000,000 synthetic unit vectors at production dimension (d=768), run on Apple M4 Max (36GB):
|
|
510
|
+
|
|
511
|
+
| Metric | Value |
|
|
512
|
+
|--------|-------|
|
|
513
|
+
| **Compression ratio** | 7.7Γ (3,072 β 400 bytes) |
|
|
514
|
+
| **Throughput** | 833 vectors/sec |
|
|
515
|
+
| **Peak heap** | 329 MB |
|
|
516
|
+
| **Total time** | 57.6 minutes |
|
|
517
|
+
|
|
518
|
+
**Residual norm distribution** β the quantization error after Householder rotation + Lloyd-Max scalar quantization:
|
|
519
|
+
|
|
520
|
+
| Statistic | Value |
|
|
521
|
+
|-----------|-------|
|
|
522
|
+
| Mean | 0.1855 |
|
|
523
|
+
| CV (coefficient of variation) | **0.038** |
|
|
524
|
+
| P99/P50 ratio | **1.11** |
|
|
525
|
+
| P99.9/P50 ratio | 1.16 |
|
|
526
|
+
| Max/Min ratio | 1.46 |
|
|
527
|
+
| IQR | 0.009 |
|
|
528
|
+
|
|
529
|
+
A CV of 0.038 means the residual norm barely varies across 1M vectors β **there is effectively no long tail**. The QJL correction term (which scales linearly with residualNorm) remains stable even for P99.9 outliers.
|
|
530
|
+
|
|
531
|
+
**R@k retrieval accuracy** (global corpus, 30 trials):
|
|
532
|
+
|
|
533
|
+
| Corpus Size | R@1 | R@5 |
|
|
534
|
+
|-------------|-----|-----|
|
|
535
|
+
| N=1,000 | 20.0% | 60.0% |
|
|
536
|
+
| N=10,000 | 36.7% | 76.7% |
|
|
537
|
+
| N=50,000 | 53.3% | **90.0%** |
|
|
538
|
+
|
|
539
|
+
> **Note:** R@k on random high-dimensional vectors is inherently harder than on real embeddings (all vectors are near-equidistant in d=768). Real-world retrieval with clustered embeddings produces higher accuracy. See [tests/residual-distribution.test.ts](tests/residual-distribution.test.ts) and [tests/benchmarks/residual-1m.ts](tests/benchmarks/residual-1m.ts) for full methodology.
|
|
540
|
+
|
|
541
|
+
</details>
|
|
542
|
+
|
|
506
543
|
### π Multi-Agent Hivemind & Enterprise Sync
|
|
507
544
|
While local SQLite is amazing for solo developers, enterprise teams cannot share a local SQLite file. Prism breaks the "local-only" ceiling via **Supabase Sync** and the **Multi-Agent Hivemind**βscaling effortlessly to teams of 50+ developers using agents. Multiple agents (dev, QA, PM) can work on the same project with **role-isolated memory**, discover each other automatically, and share context in real-time via Telepathy sync to a shared Postgres backend. β [Multi-agent setup example](examples/multi-agent-hivemind/)
|
|
508
545
|
|
|
@@ -789,8 +826,12 @@ The Generator strips the `console.log`, resubmits, and the next `EVALUATE` retur
|
|
|
789
826
|
|
|
790
827
|
## π What's New
|
|
791
828
|
|
|
792
|
-
> **Current release: v9.
|
|
829
|
+
> **Current release: v9.3.0 β TurboQuant ResidualNorm Tiebreaker**
|
|
793
830
|
|
|
831
|
+
- π― **v9.3.0 β TurboQuant ResidualNorm Tiebreaker:** Configurable ranking optimization for Tier-2 search. When compressed cosine scores are within Ξ΅ of each other, prefers the candidate with lower `residualNorm` (more trustworthy compressed representation). `PRISM_TURBOQUANT_TIEBREAKER_EPSILON=0.005` gives +2pp R@1, +1pp R@5. Empirically validated at N=5K with A/B test. 1066 tests, 0 regressions. Inspired by [@m13v's suggestion](https://github.com/xiaowu0162/LongMemEval/issues/31).
|
|
832
|
+
- π **v9.2.7 β Security Hardening:** Typed `PrototypePollutionError` class (replaces generic `Error` in `sanitizeForMerge()` β enables catch-site discrimination and forensic logging with `offendingKey`), explicit null-byte path injection guard in `SafetyController.validateActionsInScope()` (C-string truncation attack vector), and corrected CRDT merge semantics documentation (Remove-Wins-from-Either, not Add-Wins). 1055 tests, 0 regressions.
|
|
833
|
+
- πͺ **v9.2.6 β Windows CI Timeout Fix:** CLI integration tests timed out on Windows + Node 22.x GitHub Actions runners. Added `{ timeout: 30_000 }` to the describe block. 6 new residual distribution tests validating TurboQuant's QJL correction stability (zero R@5 delta between P50 and P95 residual vectors at d=128, 2K corpus).
|
|
834
|
+
- π§ **v9.2.5 β Reconciliation Credential Probe Fix:** `supabaseReady` guard only resolved credentials when `requestedBackend === "supabase"`, causing reconciliation to silently skip. Added second credential probe for local + reconciliation path. Fixed Supabase schema mismatch on `key_context` column.
|
|
794
835
|
- π **v9.2.4 β Cross-Backend Reconciliation:** Automatic two-layer sync from Supabase β SQLite on startup. When Claude Desktop writes handoffs and ledger entries to Supabase, Antigravity (local SQLite) now automatically detects stale data and pulls newer handoffs + the 20 most recent ledger entries. 5-second timeout prevents startup freeze. Targeted ID lookups (not full table scans) keep it safe for large databases. 13 tests including malformed JSON resilience, multi-role dedup, and timeout handling.
|
|
795
836
|
- π§ **v9.2.3 β Code Review Hardening:** 10x faster split-brain detection (lightweight direct queries replace full `StorageBackend` construction), variable shadowing fix in CLI, resource leak fix in SQLite alternate client.
|
|
796
837
|
- π¨ **v9.2.2 β Critical: Split-Brain Detection & Prevention:** When multiple MCP clients use different storage backends (e.g., Claude Desktop β Supabase, Antigravity β SQLite), session state could silently diverge, causing agents to act on stale TODOs and outdated context. **New: `--storage` flag** on `prism load` CLI lets callers explicitly select which backend to read from. **New: Split-Brain Drift Detection** in `session_load_context` β compares active and alternate backend versions at load time and warns prominently when they diverge. Session loader script updated to respect `PRISM_STORAGE` environment variable.
|
|
@@ -817,7 +858,7 @@ Standard memory servers (like Mem0, Zep, or the baseline Anthropic MCP) act as p
|
|
|
817
858
|
| **Storage Engine** | **BYO SQLite or Supabase** | Managed Cloud / VectorDBs | Managed Cloud / Postgres | Local SQLite only |
|
|
818
859
|
| **Context Assembly** | **Progressive (Quick/Std/Deep)** | Top-K Semantic Search | Top-K + Temporal Summaries | Basic Entity Search |
|
|
819
860
|
| **Memory Mechanics** | **ACT-R Activation, Spreading Activation, Hebbian Consolidation, Rejection Gate** | Basic Vector + Entity | Fading Temporal Graph | None (Infinite growth) |
|
|
820
|
-
| **Multi-Agent Sync** | **CRDT (
|
|
861
|
+
| **Multi-Agent Sync** | **CRDT (Remove-Wins / LWW)** | Cloud locks | Postgres locks | β None (Data races) |
|
|
821
862
|
| **Data Compression** | **TurboQuant (7x smaller vectors)** | β Standard F32 Vectors | β Standard Vectors | β No Vectors |
|
|
822
863
|
| **Observability** | **OTel Traces + Built-in PWA UI** | Cloud Dashboard | Cloud Dashboard | β None |
|
|
823
864
|
| **Maintenance** | **Autonomous Background Scheduler** | Manual/API driven | Automated (Cloud) | β Manual |
|
|
@@ -1188,8 +1229,16 @@ Prism has evolved from smart session logging into a **cognitive memory architect
|
|
|
1188
1229
|
| **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 |
|
|
1189
1230
|
| **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 |
|
|
1190
1231
|
| **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 |
|
|
1191
|
-
| **
|
|
1192
|
-
| **
|
|
1232
|
+
| **v9.0** | Affect-Tagged Memory β valence-scored retrieval where `\|valence\|` boosts ranking; UX warnings surface historically negative topics | Affect-modulated retrieval (neuroscience), somatic marker hypothesis | β
Shipped |
|
|
1233
|
+
| **v9.0** | Surprisal Gate β vector-based novelty pricing: high-surprisal saves cost 0.5Γ tokens, low-surprisal 2.0Γ; forces LLM data compression | Information-theoretic surprisal (Shannon), predictive coding | β
Shipped |
|
|
1234
|
+
| **v9.0** | Cognitive Budget β per-project token economy with passive UBI recovery (+100 tokens/hr); agents that over-save enter Cognitive Debt | Resource-bounded rationality (Simon, 1955) | β
Shipped |
|
|
1235
|
+
| **v9.1** | Task Router v2 β 6-signal weighted heuristic engine routing tasks between cloud host and local LLM based on file-type complexity, scope, and multi-step detection | Heuristic classification, cognitive load theory | β
Shipped |
|
|
1236
|
+
| **v9.2** | Cross-Backend Reconciliation β automatic Supabase β SQLite sync with idempotent dedup and 5s timeout | Eventual consistency, crdt-style reconciliation | β
Shipped |
|
|
1237
|
+
| **v9.2** | Split-Brain Drift Detection β dual-backend version comparison with prominent divergence warnings at load time | Byzantine fault detection, split-brain resolution | β
Shipped |
|
|
1238
|
+
| **v9.2** | TurboQuant QJL Validation β zero R@5 delta between P50 and P95 residual vectors (d=128, N=2K); CV=0.038 at d=768 proves no long tail | QJL estimator (ICLR 2026), Householder orthogonal rotation | β
Shipped |
|
|
1239
|
+
| **v9.2** | Typed Security Errors β `PrototypePollutionError` with `offendingKey` for forensic logging; null-byte path injection guard in SafetyController | Defense-in-depth (NIST), C-string truncation attack mitigation | β
Shipped |
|
|
1240
|
+
| **v9.3** | ResidualNorm Tiebreaker β within-Ξ΅ candidates ranked by compression fidelity (`PRISM_TURBOQUANT_TIEBREAKER_EPSILON`); +2pp R@1, +1pp R@5 at Ξ΅=0.005 | Quantization confidence scoring, compression-aware retrieval | β
Shipped |
|
|
1241
|
+
| **v10+** | Zero-Search Retrieval β no index, no ANN, just ask the vector | Holographic Reduced Representations | π Horizon |
|
|
1193
1242
|
|
|
1194
1243
|
> 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).
|
|
1195
1244
|
|
|
@@ -1221,7 +1270,7 @@ Prism MCP is open-source and free for individual developers. For teams and enter
|
|
|
1221
1270
|
|
|
1222
1271
|
## π¦ Milestones & Roadmap
|
|
1223
1272
|
|
|
1224
|
-
> **Current: v9.
|
|
1273
|
+
> **Current: v9.3.0** β TurboQuant ResidualNorm Tiebreaker ([CHANGELOG](CHANGELOG.md))
|
|
1225
1274
|
|
|
1226
1275
|
| Release | Headline |
|
|
1227
1276
|
|---------|----------|
|
package/dist/config.js
CHANGED
|
@@ -268,3 +268,17 @@ export const PRISM_DARK_FACTORY_ENABLED = process.env.PRISM_DARK_FACTORY_ENABLED
|
|
|
268
268
|
export const PRISM_DARK_FACTORY_POLL_MS = parseInt(process.env.PRISM_DARK_FACTORY_POLL_MS || "30000", 10);
|
|
269
269
|
/** Default max wall-clock time per pipeline (ms). Default: 15 minutes. */
|
|
270
270
|
export const PRISM_DARK_FACTORY_MAX_RUNTIME_MS = parseInt(process.env.PRISM_DARK_FACTORY_MAX_RUNTIME_MS || "900000", 10);
|
|
271
|
+
// βββ v9.3: TurboQuant ResidualNorm Tiebreaker βββββββββββββββββ
|
|
272
|
+
// When two compressed cosine scores are within Ξ΅ of each other,
|
|
273
|
+
// prefer the candidate with lower residualNorm (its compressed
|
|
274
|
+
// representation captured more signal energy, making its score
|
|
275
|
+
// more trustworthy). Empirically validated: Ξ΅=0.005 gives +2pp
|
|
276
|
+
// R@1, +1pp R@5 on random d=128 vectors. Set to 0 to disable.
|
|
277
|
+
//
|
|
278
|
+
// Only affects Tier-2 TurboQuant JS-side search (both SQLite and
|
|
279
|
+
// Supabase backends). Tier-1 native vector search is unaffected.
|
|
280
|
+
/** Tiebreaker threshold for TurboQuant Tier-2 ranking. 0 = disabled (default). */
|
|
281
|
+
const rawTiebreakerEpsilon = parseFloat(process.env.PRISM_TURBOQUANT_TIEBREAKER_EPSILON || "0");
|
|
282
|
+
export const PRISM_TURBOQUANT_TIEBREAKER_EPSILON = Number.isFinite(rawTiebreakerEpsilon) && rawTiebreakerEpsilon >= 0
|
|
283
|
+
? rawTiebreakerEpsilon
|
|
284
|
+
: 0;
|
|
@@ -84,6 +84,12 @@ export class SafetyController {
|
|
|
84
84
|
if (!action.targetPath || typeof action.targetPath !== 'string' || action.targetPath.trim() === '') {
|
|
85
85
|
return `Action[${i}]: targetPath is empty or missing`;
|
|
86
86
|
}
|
|
87
|
+
// Null-byte injection guard: C-string truncation attack vector.
|
|
88
|
+
// A path like "src/\0../../etc/passwd" would be truncated at the null byte
|
|
89
|
+
// by native fs syscalls, potentially resolving to an unintended location.
|
|
90
|
+
if (action.targetPath.includes('\0')) {
|
|
91
|
+
return `Action[${i}]: targetPath contains null byte (injection attempt)`;
|
|
92
|
+
}
|
|
87
93
|
// Resolve targetPath relative to workingDirectory for scope check
|
|
88
94
|
const resolvedTarget = spec.workingDirectory
|
|
89
95
|
? path.resolve(spec.workingDirectory, action.targetPath)
|
package/dist/storage/sqlite.js
CHANGED
|
@@ -1594,6 +1594,9 @@ export class SqliteStorage {
|
|
|
1594
1594
|
`;
|
|
1595
1595
|
const fallbackResult = await this.db.execute({ sql: fallbackSql, args: fallbackArgs });
|
|
1596
1596
|
// Score each entry using asymmetric cosine similarity
|
|
1597
|
+
// Track residualNorm for optional tiebreaker (v9.3)
|
|
1598
|
+
const { PRISM_TURBOQUANT_TIEBREAKER_EPSILON } = await import("../config.js");
|
|
1599
|
+
const eps = PRISM_TURBOQUANT_TIEBREAKER_EPSILON;
|
|
1597
1600
|
const scored = [];
|
|
1598
1601
|
for (const row of fallbackResult.rows) {
|
|
1599
1602
|
try {
|
|
@@ -1613,6 +1616,7 @@ export class SqliteStorage {
|
|
|
1613
1616
|
is_rollup: Boolean(row.is_rollup),
|
|
1614
1617
|
importance: row.importance ?? 0,
|
|
1615
1618
|
last_accessed_at: row.last_accessed_at || null,
|
|
1619
|
+
_residualNorm: eps > 0 ? compressed.residualNorm : undefined,
|
|
1616
1620
|
});
|
|
1617
1621
|
}
|
|
1618
1622
|
}
|
|
@@ -1620,9 +1624,20 @@ export class SqliteStorage {
|
|
|
1620
1624
|
// Skip entries with corrupt compressed data
|
|
1621
1625
|
}
|
|
1622
1626
|
}
|
|
1623
|
-
// Sort by similarity descending
|
|
1624
|
-
|
|
1627
|
+
// Sort by similarity descending, with optional residualNorm tiebreaker
|
|
1628
|
+
// When Ξ΅ > 0: candidates within Ξ΅ of each other are ranked by lower
|
|
1629
|
+
// residualNorm (its compressed representation is more trustworthy).
|
|
1630
|
+
scored.sort((a, b) => {
|
|
1631
|
+
const diff = b.similarity - a.similarity;
|
|
1632
|
+
if (eps > 0 && Math.abs(diff) < eps && a._residualNorm != null && b._residualNorm != null) {
|
|
1633
|
+
return a._residualNorm - b._residualNorm;
|
|
1634
|
+
}
|
|
1635
|
+
return diff;
|
|
1636
|
+
});
|
|
1625
1637
|
const baseResults = scored.slice(0, params.limit);
|
|
1638
|
+
// Strip internal tiebreaker field before returning
|
|
1639
|
+
for (const r of baseResults)
|
|
1640
|
+
delete r._residualNorm;
|
|
1626
1641
|
debugLog(`[SqliteStorage] Tier-2 TurboQuant fallback: scored ${fallbackResult.rows.length} entries, ` +
|
|
1627
1642
|
`${scored.length} above threshold`);
|
|
1628
1643
|
if (params.activation?.enabled) {
|
package/dist/storage/supabase.js
CHANGED
|
@@ -280,6 +280,9 @@ export class SupabaseStorage {
|
|
|
280
280
|
queryParams.role = `eq.${params.role}`;
|
|
281
281
|
const rows = await supabaseGet("session_ledger", queryParams);
|
|
282
282
|
const scored = [];
|
|
283
|
+
// v9.3: Import tiebreaker config for optional residualNorm ranking
|
|
284
|
+
const { PRISM_TURBOQUANT_TIEBREAKER_EPSILON } = await import("../config.js");
|
|
285
|
+
const eps = PRISM_TURBOQUANT_TIEBREAKER_EPSILON;
|
|
283
286
|
for (const row of (Array.isArray(rows) ? rows : [])) {
|
|
284
287
|
try {
|
|
285
288
|
const compressedBase64 = row.embedding_compressed;
|
|
@@ -295,6 +298,7 @@ export class SupabaseStorage {
|
|
|
295
298
|
session_date: (row.session_date || row.created_at),
|
|
296
299
|
decisions: Array.isArray(row.decisions) ? row.decisions : [],
|
|
297
300
|
files_changed: Array.isArray(row.files_changed) ? row.files_changed : [],
|
|
301
|
+
_residualNorm: eps > 0 ? compressed.residualNorm : undefined,
|
|
298
302
|
});
|
|
299
303
|
}
|
|
300
304
|
}
|
|
@@ -302,10 +306,21 @@ export class SupabaseStorage {
|
|
|
302
306
|
// Skip entries with corrupt compressed data
|
|
303
307
|
}
|
|
304
308
|
}
|
|
305
|
-
|
|
309
|
+
// Sort by similarity descending, with optional residualNorm tiebreaker
|
|
310
|
+
scored.sort((a, b) => {
|
|
311
|
+
const diff = b.similarity - a.similarity;
|
|
312
|
+
if (eps > 0 && Math.abs(diff) < eps && a._residualNorm != null && b._residualNorm != null) {
|
|
313
|
+
return a._residualNorm - b._residualNorm;
|
|
314
|
+
}
|
|
315
|
+
return diff;
|
|
316
|
+
});
|
|
306
317
|
debugLog(`[SupabaseStorage] Tier-2 TurboQuant fallback: scored ${rows.length} entries, ` +
|
|
307
318
|
`${scored.length} above threshold`);
|
|
308
|
-
|
|
319
|
+
const results = scored.slice(0, params.limit);
|
|
320
|
+
// Strip internal tiebreaker field before returning
|
|
321
|
+
for (const r of results)
|
|
322
|
+
delete r._residualNorm;
|
|
323
|
+
return results;
|
|
309
324
|
}
|
|
310
325
|
catch (tier2Err) {
|
|
311
326
|
// Both tiers failed β return empty; caller falls through to FTS5
|
package/dist/utils/crdtMerge.js
CHANGED
|
@@ -40,13 +40,28 @@
|
|
|
40
40
|
//
|
|
41
41
|
// This is a zero-dependency, fast (~10ms for a typical handoff object)
|
|
42
42
|
// solution appropriate for Prism's small merge surfaces.
|
|
43
|
+
/**
|
|
44
|
+
* Typed error thrown when sanitizeForMerge() detects prototype pollution.
|
|
45
|
+
* Provides the offending key for forensic logging and distinct catch handling.
|
|
46
|
+
*/
|
|
47
|
+
export class PrototypePollutionError extends Error {
|
|
48
|
+
offendingKey;
|
|
49
|
+
constructor(key) {
|
|
50
|
+
super(`Security violation: prototype pollution attempt detected via key "${key}"`);
|
|
51
|
+
this.name = "PrototypePollutionError";
|
|
52
|
+
this.offendingKey = key;
|
|
53
|
+
if (Error.captureStackTrace) {
|
|
54
|
+
Error.captureStackTrace(this, PrototypePollutionError);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
43
58
|
const FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
44
59
|
function walkForForbiddenKeys(current) {
|
|
45
60
|
if (!current || typeof current !== "object")
|
|
46
61
|
return;
|
|
47
62
|
for (const key of Object.keys(current)) {
|
|
48
63
|
if (FORBIDDEN_KEYS.has(key)) {
|
|
49
|
-
throw new
|
|
64
|
+
throw new PrototypePollutionError(key);
|
|
50
65
|
}
|
|
51
66
|
walkForForbiddenKeys(current[key]);
|
|
52
67
|
}
|
|
@@ -63,18 +78,19 @@ export function sanitizeForMerge(obj) {
|
|
|
63
78
|
walkForForbiddenKeys(obj);
|
|
64
79
|
return JSON.parse(JSON.stringify(obj));
|
|
65
80
|
}
|
|
66
|
-
// βββ OR-Set Logic (
|
|
81
|
+
// βββ OR-Set Logic (Remove-Wins-from-Either) ββββββββββββββββββββ
|
|
67
82
|
//
|
|
68
83
|
// 3-way set merge:
|
|
69
84
|
// added_by_incoming = incoming - base
|
|
70
85
|
// removed_by_incoming = base - incoming
|
|
71
86
|
// added_by_current = current - base
|
|
72
87
|
// removed_by_current = base - current
|
|
73
|
-
// result = (base -
|
|
88
|
+
// result = (base - all_removals) βͺ all_adds
|
|
74
89
|
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
90
|
+
// SEMANTICS: Items removed by EITHER agent are dropped from the base.
|
|
91
|
+
// Fresh additions from either agent are always preserved (union).
|
|
92
|
+
// This means a removal by one agent wins over non-action by the other,
|
|
93
|
+
// but cannot override a fresh add. Safe for TODOs and keywords.
|
|
78
94
|
function mergeArray(b = [], i = [], c = []) {
|
|
79
95
|
const bSet = new Set(b);
|
|
80
96
|
const iSet = new Set(i);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prism-mcp-server",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.3.0",
|
|
4
4
|
"mcpName": "io.github.dcostenco/prism-mcp",
|
|
5
5
|
"description": "The Mind Palace for AI Agents β a true Cognitive Architecture with Hebbian learning (episodicβsemantic consolidation), ACT-R spreading activation (multi-hop causal reasoning), uncertainty-aware rejection gates (agents that know when they don't know), adversarial evaluation (anti-sycophancy), fail-closed Dark Factory pipelines, persistent memory (SQLite/Supabase), multi-agent Hivemind, time travel & visual dashboard. Zero-config local mode.",
|
|
6
6
|
"module": "index.ts",
|