prism-mcp-server 7.3.3 β 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 +176 -211
- package/dist/cli.js +0 -0
- package/dist/darkfactory/clawInvocation.js +62 -7
- package/dist/darkfactory/runner.js +188 -23
- package/dist/darkfactory/safetyController.js +48 -22
- package/dist/darkfactory/schema.js +2 -0
- package/dist/dashboard/intentHealth.js +62 -0
- package/dist/dashboard/server.js +35 -1
- package/dist/dashboard/ui.js +117 -11
- package/dist/server.js +19 -0
- package/dist/storage/sqlite.js +68 -7
- package/dist/storage/supabase.js +57 -4
- package/dist/tools/ledgerHandlers.js +13 -0
- package/package.json +2 -2
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/server.js
CHANGED
|
@@ -1184,6 +1184,25 @@ export async function startServer() {
|
|
|
1184
1184
|
console.error(`[DarkFactory] Startup failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
|
|
1185
1185
|
});
|
|
1186
1186
|
}
|
|
1187
|
+
// βββ v7.4: TurboQuant Compressor Async Warmup ββββββββββββ
|
|
1188
|
+
// The first call to getDefaultCompressor() triggers construction of a
|
|
1189
|
+
// 768Γ768 rotation matrix (~15ms of synchronous CPU). Pre-warm via
|
|
1190
|
+
// setImmediate so it runs after the current event-loop tick completes,
|
|
1191
|
+
// preventing the stdio handshake from being blocked during startup.
|
|
1192
|
+
// Fire-and-forget β non-critical; subsequent calls hit the singleton cache.
|
|
1193
|
+
setImmediate(() => {
|
|
1194
|
+
try {
|
|
1195
|
+
// Dynamic import avoids loading turboquant.ts at module-parse time
|
|
1196
|
+
// (the construction side-effects run only when actually needed).
|
|
1197
|
+
import("./utils/turboquant.js").then(({ getDefaultCompressor }) => {
|
|
1198
|
+
getDefaultCompressor();
|
|
1199
|
+
console.error("[Prism] TurboQuant compressor pre-warmed (rotation matrix ready)");
|
|
1200
|
+
}).catch(err => {
|
|
1201
|
+
console.error(`[TurboQuant] Warmup failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
catch { /* warmup is a best-effort optimization */ }
|
|
1205
|
+
});
|
|
1187
1206
|
// Keep the process alive β without this, Node.js would exit
|
|
1188
1207
|
// because there are no active event loop handles after the
|
|
1189
1208
|
// synchronous setup completes.
|
package/dist/storage/sqlite.js
CHANGED
|
@@ -566,13 +566,37 @@ export class SqliteStorage {
|
|
|
566
566
|
status TEXT NOT NULL,
|
|
567
567
|
current_step TEXT NOT NULL,
|
|
568
568
|
iteration INTEGER NOT NULL,
|
|
569
|
+
eval_revisions INTEGER DEFAULT 0,
|
|
569
570
|
started_at TEXT NOT NULL,
|
|
570
571
|
updated_at TEXT NOT NULL,
|
|
571
572
|
spec TEXT NOT NULL,
|
|
572
573
|
error TEXT,
|
|
573
|
-
last_heartbeat TEXT
|
|
574
|
+
last_heartbeat TEXT,
|
|
575
|
+
contract_payload TEXT,
|
|
576
|
+
notes TEXT
|
|
574
577
|
)
|
|
575
578
|
`);
|
|
579
|
+
// βββ v7.4.0 Migration: Adversarial Eval Revisions βββββββββ
|
|
580
|
+
try {
|
|
581
|
+
await this.db.execute(`ALTER TABLE dark_factory_pipelines ADD COLUMN eval_revisions INTEGER DEFAULT 0`);
|
|
582
|
+
debugLog("[SqliteStorage] v7.4.0 migration: added eval_revisions column");
|
|
583
|
+
// Backfill existing rows β ALTER TABLE DEFAULT only applies to new inserts;
|
|
584
|
+
// rows that existed before the migration will have NULL until explicitly set.
|
|
585
|
+
await this.db.execute(`UPDATE dark_factory_pipelines SET eval_revisions = 0 WHERE eval_revisions IS NULL`);
|
|
586
|
+
debugLog("[SqliteStorage] v7.4.0 migration: backfilled eval_revisions = 0");
|
|
587
|
+
}
|
|
588
|
+
catch (e) {
|
|
589
|
+
if (!e.message?.includes("duplicate column name"))
|
|
590
|
+
throw e;
|
|
591
|
+
}
|
|
592
|
+
try {
|
|
593
|
+
await this.db.execute(`ALTER TABLE dark_factory_pipelines ADD COLUMN contract_payload TEXT`);
|
|
594
|
+
await this.db.execute(`ALTER TABLE dark_factory_pipelines ADD COLUMN notes TEXT`);
|
|
595
|
+
}
|
|
596
|
+
catch (e) {
|
|
597
|
+
if (!e.message?.includes("duplicate column name"))
|
|
598
|
+
throw e;
|
|
599
|
+
}
|
|
576
600
|
await this.db.execute(`CREATE INDEX IF NOT EXISTS idx_pipelines_status ON dark_factory_pipelines(user_id, project, status)`);
|
|
577
601
|
// βββ v7.2.0 Migration: Verification Harness ββββββββββββββββ
|
|
578
602
|
await this.db.execute(`
|
|
@@ -1160,6 +1184,30 @@ export class SqliteStorage {
|
|
|
1160
1184
|
importance: r.importance,
|
|
1161
1185
|
}));
|
|
1162
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
|
+
}
|
|
1163
1211
|
if (level === "standard") {
|
|
1164
1212
|
// Add recent ledger entries (role-scoped)
|
|
1165
1213
|
const recentLedger = await this.db.execute({
|
|
@@ -2888,16 +2936,19 @@ export class SqliteStorage {
|
|
|
2888
2936
|
}
|
|
2889
2937
|
await this.db.execute({
|
|
2890
2938
|
sql: `
|
|
2891
|
-
INSERT INTO dark_factory_pipelines (id, project, user_id, status, current_step, iteration, started_at, updated_at, spec, error, last_heartbeat)
|
|
2892
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2939
|
+
INSERT INTO dark_factory_pipelines (id, project, user_id, status, current_step, iteration, eval_revisions, started_at, updated_at, spec, error, last_heartbeat, contract_payload, notes)
|
|
2940
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2893
2941
|
ON CONFLICT(id) DO UPDATE SET
|
|
2894
2942
|
status = excluded.status,
|
|
2895
2943
|
current_step = excluded.current_step,
|
|
2896
2944
|
iteration = excluded.iteration,
|
|
2945
|
+
eval_revisions = excluded.eval_revisions,
|
|
2897
2946
|
updated_at = excluded.updated_at,
|
|
2898
2947
|
spec = excluded.spec,
|
|
2899
2948
|
error = excluded.error,
|
|
2900
|
-
last_heartbeat = excluded.last_heartbeat
|
|
2949
|
+
last_heartbeat = excluded.last_heartbeat,
|
|
2950
|
+
contract_payload = excluded.contract_payload,
|
|
2951
|
+
notes = excluded.notes
|
|
2901
2952
|
`,
|
|
2902
2953
|
args: [
|
|
2903
2954
|
updatedState.id,
|
|
@@ -2906,11 +2957,14 @@ export class SqliteStorage {
|
|
|
2906
2957
|
updatedState.status,
|
|
2907
2958
|
updatedState.current_step,
|
|
2908
2959
|
updatedState.iteration,
|
|
2960
|
+
updatedState.eval_revisions ?? 0,
|
|
2909
2961
|
updatedState.started_at,
|
|
2910
2962
|
updatedState.updated_at,
|
|
2911
2963
|
updatedState.spec,
|
|
2912
2964
|
updatedState.error || null,
|
|
2913
|
-
updatedState.last_heartbeat || null
|
|
2965
|
+
updatedState.last_heartbeat || null,
|
|
2966
|
+
updatedState.contract_payload ? JSON.stringify(updatedState.contract_payload) : null,
|
|
2967
|
+
updatedState.notes || null
|
|
2914
2968
|
]
|
|
2915
2969
|
});
|
|
2916
2970
|
}
|
|
@@ -2921,7 +2975,11 @@ export class SqliteStorage {
|
|
|
2921
2975
|
});
|
|
2922
2976
|
if (result.rows.length === 0)
|
|
2923
2977
|
return null;
|
|
2924
|
-
|
|
2978
|
+
const row = result.rows[0];
|
|
2979
|
+
return {
|
|
2980
|
+
...row,
|
|
2981
|
+
contract_payload: row.contract_payload ? JSON.parse(row.contract_payload) : undefined
|
|
2982
|
+
};
|
|
2925
2983
|
}
|
|
2926
2984
|
async listPipelines(project, status, userId) {
|
|
2927
2985
|
const conditions = ['user_id = ?'];
|
|
@@ -2937,7 +2995,10 @@ export class SqliteStorage {
|
|
|
2937
2995
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
2938
2996
|
const sql = `SELECT * FROM dark_factory_pipelines ${where} ORDER BY updated_at DESC`;
|
|
2939
2997
|
const result = await this.db.execute({ sql, args });
|
|
2940
|
-
return result.rows
|
|
2998
|
+
return result.rows.map((row) => ({
|
|
2999
|
+
...row,
|
|
3000
|
+
contract_payload: row.contract_payload ? JSON.parse(row.contract_payload) : undefined
|
|
3001
|
+
}));
|
|
2941
3002
|
}
|
|
2942
3003
|
// βββ Verification Harness (v7.2.0) βββββββββββββββββββββββββββ
|
|
2943
3004
|
async saveVerificationHarness(harness, userId) {
|
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)));
|
|
@@ -1222,7 +1251,13 @@ export class SupabaseStorage {
|
|
|
1222
1251
|
updated_at: updatedState.updated_at,
|
|
1223
1252
|
spec: updatedState.spec,
|
|
1224
1253
|
error: updatedState.error || null,
|
|
1225
|
-
last_heartbeat: updatedState.last_heartbeat || null
|
|
1254
|
+
last_heartbeat: updatedState.last_heartbeat || null,
|
|
1255
|
+
// βββ v7.4: Adversarial Evaluation fields βββ
|
|
1256
|
+
eval_revisions: updatedState.eval_revisions ?? 0,
|
|
1257
|
+
contract_payload: updatedState.contract_payload
|
|
1258
|
+
? JSON.stringify(updatedState.contract_payload)
|
|
1259
|
+
: null,
|
|
1260
|
+
notes: updatedState.notes || null,
|
|
1226
1261
|
}, { on_conflict: "id" }, { Prefer: "return=minimal,resolution=merge-duplicates" });
|
|
1227
1262
|
}
|
|
1228
1263
|
catch (e) {
|
|
@@ -1244,7 +1279,15 @@ export class SupabaseStorage {
|
|
|
1244
1279
|
const rows = Array.isArray(result) ? result : [];
|
|
1245
1280
|
if (rows.length === 0)
|
|
1246
1281
|
return null;
|
|
1247
|
-
|
|
1282
|
+
const row = rows[0];
|
|
1283
|
+
// βββ v7.4: Deserialize contract_payload from JSON TEXT βββ
|
|
1284
|
+
if (row.contract_payload && typeof row.contract_payload === "string") {
|
|
1285
|
+
try {
|
|
1286
|
+
row.contract_payload = JSON.parse(row.contract_payload);
|
|
1287
|
+
}
|
|
1288
|
+
catch { /* leave as-is */ }
|
|
1289
|
+
}
|
|
1290
|
+
return row;
|
|
1248
1291
|
}
|
|
1249
1292
|
catch (e) {
|
|
1250
1293
|
if (e.message?.includes("PGRST202") || e.message?.includes("Could not find the relation"))
|
|
@@ -1263,7 +1306,17 @@ export class SupabaseStorage {
|
|
|
1263
1306
|
if (status)
|
|
1264
1307
|
query.status = `eq.${status}`;
|
|
1265
1308
|
const result = await supabaseGet("dark_factory_pipelines", query);
|
|
1266
|
-
|
|
1309
|
+
const rows = (Array.isArray(result) ? result : []);
|
|
1310
|
+
// βββ v7.4: Deserialize contract_payload from JSON TEXT βββ
|
|
1311
|
+
return rows.map(row => {
|
|
1312
|
+
if (row.contract_payload && typeof row.contract_payload === "string") {
|
|
1313
|
+
try {
|
|
1314
|
+
row.contract_payload = JSON.parse(row.contract_payload);
|
|
1315
|
+
}
|
|
1316
|
+
catch { /* leave as-is */ }
|
|
1317
|
+
}
|
|
1318
|
+
return row;
|
|
1319
|
+
});
|
|
1267
1320
|
}
|
|
1268
1321
|
catch (e) {
|
|
1269
1322
|
if (e.message?.includes("PGRST202") || e.message?.includes("Could not find the relation"))
|
|
@@ -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,8 +1,8 @@
|
|
|
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
|
-
"description": "The Mind Palace for AI Agents β fail-closed Dark Factory autonomous pipelines (3-gate parseβtypeβscope
|
|
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",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "dist/server.js",
|