prism-mcp-server 5.2.0 → 5.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 +308 -218
- package/dist/backgroundScheduler.js +327 -0
- package/dist/config.js +29 -0
- package/dist/dashboard/server.js +246 -0
- package/dist/dashboard/ui.js +216 -6
- package/dist/hivemindWatchdog.js +206 -0
- package/dist/lifecycle.js +59 -4
- package/dist/scholar/freeSearch.js +78 -0
- package/dist/scholar/webScholar.js +258 -0
- package/dist/sdm/sdmDecoder.js +75 -0
- package/dist/sdm/sdmEngine.js +158 -0
- package/dist/server.js +173 -11
- package/dist/storage/sqlite.js +298 -47
- package/dist/storage/supabase.js +114 -1
- package/dist/tools/agentRegistryDefinitions.js +11 -4
- package/dist/tools/agentRegistryHandlers.js +23 -5
- package/dist/tools/index.js +2 -2
- package/dist/tools/sessionMemoryDefinitions.js +46 -1
- package/dist/tools/sessionMemoryHandlers.js +210 -38
- package/dist/utils/briefing.js +1 -1
- package/dist/utils/crdtMerge.js +152 -0
- package/dist/utils/healthCheck.js +15 -0
- package/dist/utils/llm/adapters/gemini.js +3 -3
- package/package.json +9 -2
package/dist/server.js
CHANGED
|
@@ -58,7 +58,9 @@ ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ReadResourceRequ
|
|
|
58
58
|
// Claude Desktop that the attached resource has changed.
|
|
59
59
|
// Without this, the paperclipped context becomes stale.
|
|
60
60
|
SubscribeRequestSchema, UnsubscribeRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
61
|
-
import { SERVER_CONFIG, SESSION_MEMORY_ENABLED, PRISM_USER_ID, PRISM_ENABLE_HIVEMIND } from "./config.js";
|
|
61
|
+
import { SERVER_CONFIG, SESSION_MEMORY_ENABLED, PRISM_USER_ID, PRISM_ENABLE_HIVEMIND, WATCHDOG_INTERVAL_MS, WATCHDOG_STALE_MIN, WATCHDOG_FROZEN_MIN, WATCHDOG_OFFLINE_MIN, WATCHDOG_LOOP_THRESHOLD, PRISM_SCHEDULER_ENABLED, PRISM_SCHEDULER_INTERVAL_MS, PRISM_SCHOLAR_ENABLED, } from "./config.js";
|
|
62
|
+
import { startWatchdog, drainAlerts } from "./hivemindWatchdog.js";
|
|
63
|
+
import { startScheduler, startScholarScheduler } from "./backgroundScheduler.js";
|
|
62
64
|
import { getSyncBus } from "./sync/factory.js";
|
|
63
65
|
import { startDashboardServer } from "./dashboard/server.js";
|
|
64
66
|
import { acquireLock, registerShutdownHandlers } from "./lifecycle.js";
|
|
@@ -201,6 +203,13 @@ const activeSubscriptions = new Set();
|
|
|
201
203
|
// the promise, so they never block the MCP stdio pipe during startup.
|
|
202
204
|
let storageReady = null;
|
|
203
205
|
let storageIsReady = false;
|
|
206
|
+
// ─── v5.2.1: Deferred Auto-Push Tracking ─────────────────────
|
|
207
|
+
// Tracks whether any client has already called session_load_context.
|
|
208
|
+
// Used by the deferred auto-push to skip redundant context injection.
|
|
209
|
+
// This ensures Claude CLI (which calls the tool via its hook within
|
|
210
|
+
// seconds) is never affected, while Antigravity gets a fallback push
|
|
211
|
+
// when the model fails to comply with auto-load instructions.
|
|
212
|
+
let contextLoadedByClient = false;
|
|
204
213
|
/**
|
|
205
214
|
* Notifies subscribed clients that a resource has changed.
|
|
206
215
|
*
|
|
@@ -630,6 +639,7 @@ export function createServer() {
|
|
|
630
639
|
case "session_load_context":
|
|
631
640
|
if (!SESSION_MEMORY_ENABLED)
|
|
632
641
|
throw new Error("Session memory not configured. Set SUPABASE_URL and SUPABASE_KEY.");
|
|
642
|
+
contextLoadedByClient = true; // v5.2.1: suppress deferred auto-push
|
|
633
643
|
result = await sessionLoadContextHandler(args);
|
|
634
644
|
break;
|
|
635
645
|
case "knowledge_search":
|
|
@@ -755,6 +765,34 @@ export function createServer() {
|
|
|
755
765
|
};
|
|
756
766
|
}
|
|
757
767
|
rootSpan.setStatus({ code: SpanStatusCode.OK });
|
|
768
|
+
// ═══ v5.3: Hivemind Watchdog Alert Injection (Telepathy) ═══
|
|
769
|
+
// CRITICAL: Append alerts DIRECTLY to tool response content
|
|
770
|
+
// so the LLM actually reads them. sendLoggingMessage goes to
|
|
771
|
+
// debug logs which the LLM never sees.
|
|
772
|
+
if (PRISM_ENABLE_HIVEMIND && result && !result.isError) {
|
|
773
|
+
const project = args?.project;
|
|
774
|
+
if (typeof project === "string") {
|
|
775
|
+
const alerts = drainAlerts(project);
|
|
776
|
+
if (alerts.length > 0) {
|
|
777
|
+
const alertBlock = alerts.map(a => `[🐝 SYSTEM ALERT] ⚠️ Teammate "${a.role}"` +
|
|
778
|
+
(a.agentName ? ` (${a.agentName})` : "") +
|
|
779
|
+
` is ${a.status.toUpperCase()}: ${a.message}`).join("\n");
|
|
780
|
+
// Inject into LLM context (primary mechanism)
|
|
781
|
+
result.content.push({
|
|
782
|
+
type: "text",
|
|
783
|
+
text: `\n\n${alertBlock}`,
|
|
784
|
+
});
|
|
785
|
+
// Also log to operator/debug channel (secondary)
|
|
786
|
+
try {
|
|
787
|
+
server.sendLoggingMessage({
|
|
788
|
+
level: "warning",
|
|
789
|
+
data: alertBlock,
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
catch { /* sendLoggingMessage is best-effort */ }
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
758
796
|
return result;
|
|
759
797
|
}
|
|
760
798
|
catch (error) {
|
|
@@ -866,6 +904,7 @@ export async function startServer() {
|
|
|
866
904
|
const server = createServer();
|
|
867
905
|
const transport = new StdioServerTransport();
|
|
868
906
|
await server.connect(transport);
|
|
907
|
+
console.error(`[Prism] MCP Server successfully started and listening on stdio...`);
|
|
869
908
|
// Register graceful shutdown handlers (SIGTERM, SIGINT, SIGHUP, stdin close).
|
|
870
909
|
// The stdin close handler is critical — when MCP clients disconnect, they
|
|
871
910
|
// often just close the pipe without sending a signal, leaving zombie processes.
|
|
@@ -887,18 +926,94 @@ export async function startServer() {
|
|
|
887
926
|
]).catch(err => {
|
|
888
927
|
console.error(`[Prism] Storage pre-warm failed (non-fatal): ${err}`);
|
|
889
928
|
});
|
|
890
|
-
// ─── v4.1: Auto-Load
|
|
929
|
+
// ─── v4.1: Auto-Load via dynamic tool descriptions ──────────
|
|
891
930
|
// The session_load_context tool description is dynamically modified
|
|
892
931
|
// in createServer() → buildSessionMemoryTools() to include the
|
|
893
|
-
// auto-load projects list.
|
|
894
|
-
//
|
|
932
|
+
// auto-load projects list. Tool descriptions are surfaced by ALL
|
|
933
|
+
// MCP clients — this is the primary mechanism.
|
|
895
934
|
//
|
|
896
|
-
//
|
|
897
|
-
//
|
|
898
|
-
//
|
|
935
|
+
// ─── v5.2.1: Deferred Auto-Push (fallback for non-compliant models) ──
|
|
936
|
+
// After storage warms up, wait AUTOLOAD_PUSH_DELAY_MS. If the client
|
|
937
|
+
// (model) hasn't called session_load_context by then, push context via
|
|
938
|
+
// sendLoggingMessage as a last resort. This is a FALLBACK — it's not
|
|
939
|
+
// guaranteed to be surfaced by all clients, but it's better than nothing.
|
|
899
940
|
//
|
|
900
|
-
//
|
|
901
|
-
//
|
|
941
|
+
// Why 10 seconds? Claude CLI always calls the tool within 2-3 seconds
|
|
942
|
+
// via its SessionStart hook. Antigravity models that comply also call it
|
|
943
|
+
// within 5 seconds. 10s gives ample time for well-behaved clients.
|
|
944
|
+
const AUTOLOAD_PUSH_DELAY_MS = 10_000;
|
|
945
|
+
// Read autoload projects from dashboard config (same source as createServer)
|
|
946
|
+
const pushAutoloadList = getSettingSync("autoload_projects", "")
|
|
947
|
+
.split(",").map(p => p.trim()).filter(Boolean);
|
|
948
|
+
if (pushAutoloadList.length > 0) {
|
|
949
|
+
// Wait for storage, then schedule the deferred push
|
|
950
|
+
storageReady?.then(async () => {
|
|
951
|
+
// Wait for the delay period to give the model a chance to call the tool
|
|
952
|
+
await new Promise(r => setTimeout(r, AUTOLOAD_PUSH_DELAY_MS));
|
|
953
|
+
// If the client already called session_load_context, skip the push
|
|
954
|
+
if (contextLoadedByClient) {
|
|
955
|
+
console.error(`[Prism] Auto-push skipped — client already loaded context`);
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
console.error(`[Prism] Auto-push triggered — model did not call session_load_context within ${AUTOLOAD_PUSH_DELAY_MS / 1000}s`);
|
|
959
|
+
// Load and push context for each autoload project
|
|
960
|
+
try {
|
|
961
|
+
const storage = await getStorage();
|
|
962
|
+
const defaultLevel = getSettingSync("default_context_depth", "standard");
|
|
963
|
+
for (const project of pushAutoloadList) {
|
|
964
|
+
try {
|
|
965
|
+
const data = await storage.loadContext(project, defaultLevel, PRISM_USER_ID);
|
|
966
|
+
if (!data) {
|
|
967
|
+
server.sendLoggingMessage({
|
|
968
|
+
level: "info",
|
|
969
|
+
data: `[Prism Auto-Push] No context found for project "${project}". Starting fresh.`,
|
|
970
|
+
});
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
973
|
+
// Format context identically to sessionLoadContextHandler
|
|
974
|
+
const d = data;
|
|
975
|
+
let ctx = `📋 [AUTO-PUSH] Session context for "${project}" (${defaultLevel}):\n\n`;
|
|
976
|
+
if (d.last_summary)
|
|
977
|
+
ctx += `📝 Last Summary: ${d.last_summary}\n`;
|
|
978
|
+
if (d.active_branch)
|
|
979
|
+
ctx += `🌿 Active Branch: ${d.active_branch}\n`;
|
|
980
|
+
if (d.key_context)
|
|
981
|
+
ctx += `💡 Key Context: ${d.key_context}\n`;
|
|
982
|
+
if (d.pending_todo?.length) {
|
|
983
|
+
ctx += `\n✅ Open TODOs:\n` + d.pending_todo.map((t) => ` - ${t}`).join("\n") + `\n`;
|
|
984
|
+
}
|
|
985
|
+
if (d.keywords?.length) {
|
|
986
|
+
ctx += `\n🔑 Keywords: ${d.keywords.join(", ")}\n`;
|
|
987
|
+
}
|
|
988
|
+
if (d.recent_sessions?.length) {
|
|
989
|
+
ctx += `\n⏳ Recent Sessions:\n` + d.recent_sessions.map((s) => ` [${s.session_date?.split("T")[0]}] ${s.summary}`).join("\n") + `\n`;
|
|
990
|
+
}
|
|
991
|
+
// Agent identity
|
|
992
|
+
const agentName = getSettingSync("agent_name", "");
|
|
993
|
+
const defaultRole = getSettingSync("default_role", "");
|
|
994
|
+
if (agentName || defaultRole) {
|
|
995
|
+
ctx += `\n👤 Agent: ${defaultRole || "global"} — ${agentName || "Agent"}`;
|
|
996
|
+
}
|
|
997
|
+
const version = d.version;
|
|
998
|
+
if (version) {
|
|
999
|
+
ctx += `\n🔑 Session version: ${version}. Pass expected_version: ${version} when saving handoff.`;
|
|
1000
|
+
}
|
|
1001
|
+
server.sendLoggingMessage({
|
|
1002
|
+
level: "info",
|
|
1003
|
+
data: ctx,
|
|
1004
|
+
});
|
|
1005
|
+
console.error(`[Prism] Auto-pushed context for "${project}" (${defaultLevel})`);
|
|
1006
|
+
}
|
|
1007
|
+
catch (err) {
|
|
1008
|
+
console.error(`[Prism] Auto-push failed for "${project}" (non-fatal): ${err}`);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
catch (err) {
|
|
1013
|
+
console.error(`[Prism] Auto-push storage error (non-fatal): ${err}`);
|
|
1014
|
+
}
|
|
1015
|
+
}).catch(() => { });
|
|
1016
|
+
}
|
|
902
1017
|
}
|
|
903
1018
|
// ─── v2.0 Step 6: Initialize SyncBus (Telepathy) ───
|
|
904
1019
|
// Fire-and-forget — SyncBus is non-critical for startup.
|
|
@@ -938,6 +1053,46 @@ export async function startServer() {
|
|
|
938
1053
|
console.error(`[Dashboard] Mind Palace startup failed (non-fatal): ${err}`);
|
|
939
1054
|
});
|
|
940
1055
|
}, 0);
|
|
1056
|
+
// ─── v5.3: Hivemind Watchdog ──────────────────────────────
|
|
1057
|
+
// Start the server-side health monitor after storage is warm.
|
|
1058
|
+
// Runs every WATCHDOG_INTERVAL_MS (default 60s) to detect
|
|
1059
|
+
// frozen agents, infinite loops, and task overruns.
|
|
1060
|
+
if (PRISM_ENABLE_HIVEMIND && SESSION_MEMORY_ENABLED) {
|
|
1061
|
+
storageReady?.then(() => {
|
|
1062
|
+
startWatchdog({
|
|
1063
|
+
intervalMs: WATCHDOG_INTERVAL_MS,
|
|
1064
|
+
staleThresholdMin: WATCHDOG_STALE_MIN,
|
|
1065
|
+
frozenThresholdMin: WATCHDOG_FROZEN_MIN,
|
|
1066
|
+
offlineThresholdMin: WATCHDOG_OFFLINE_MIN,
|
|
1067
|
+
loopThreshold: WATCHDOG_LOOP_THRESHOLD,
|
|
1068
|
+
});
|
|
1069
|
+
}).catch(err => {
|
|
1070
|
+
console.error(`[Watchdog] Startup failed (non-fatal): ${err}`);
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
// ─── v5.4: Background Purge Scheduler ────────────────────
|
|
1074
|
+
// Automated storage maintenance: TTL sweep, importance decay,
|
|
1075
|
+
// compaction, and deep purge. Runs every PRISM_SCHEDULER_INTERVAL_MS
|
|
1076
|
+
// (default: 12 hours). Independent from the Watchdog (60s cadence).
|
|
1077
|
+
if (PRISM_SCHEDULER_ENABLED && SESSION_MEMORY_ENABLED) {
|
|
1078
|
+
storageReady?.then(() => {
|
|
1079
|
+
startScheduler({
|
|
1080
|
+
intervalMs: PRISM_SCHEDULER_INTERVAL_MS,
|
|
1081
|
+
});
|
|
1082
|
+
}).catch(err => {
|
|
1083
|
+
console.error(`[Scheduler] Startup failed (non-fatal): ${err}`);
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
// ─── v5.4: Autonomous Web Scholar Scheduler ──────────────
|
|
1087
|
+
// Background LLM research pipeline. Independent from the
|
|
1088
|
+
// maintenance scheduler — has its own interval and enable flag.
|
|
1089
|
+
if (PRISM_SCHOLAR_ENABLED && SESSION_MEMORY_ENABLED) {
|
|
1090
|
+
storageReady?.then(() => {
|
|
1091
|
+
startScholarScheduler();
|
|
1092
|
+
}).catch(err => {
|
|
1093
|
+
console.error(`[WebScholar] Startup failed (non-fatal): ${err}`);
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
941
1096
|
// Keep the process alive — without this, Node.js would exit
|
|
942
1097
|
// because there are no active event loop handles after the
|
|
943
1098
|
// synchronous setup completes.
|
|
@@ -945,8 +1100,15 @@ export async function startServer() {
|
|
|
945
1100
|
// Heartbeat to keep the process running
|
|
946
1101
|
}, 10000);
|
|
947
1102
|
}
|
|
948
|
-
// Only auto-start when this module is executed directly (not imported by Smithery scanner)
|
|
949
|
-
|
|
1103
|
+
// Only auto-start when this module is executed directly (not imported by Smithery scanner).
|
|
1104
|
+
// IMPORTANT: npm install -g creates a symlink like /usr/local/bin/prism-mcp-server
|
|
1105
|
+
// whose path does NOT end with 'server.js'. Node.js sets process.argv[1] to the
|
|
1106
|
+
// symlink path, not the resolved target. Without the bin-name check, startServer()
|
|
1107
|
+
// never fires and the process silently exits with zero stdout (see issue #21).
|
|
1108
|
+
const entryScript = process.argv[1] ?? '';
|
|
1109
|
+
const isDirectExecution = entryScript.endsWith('server.js') ||
|
|
1110
|
+
entryScript.endsWith('server.ts') ||
|
|
1111
|
+
entryScript.endsWith('prism-mcp-server');
|
|
950
1112
|
if (isDirectExecution) {
|
|
951
1113
|
startServer().catch((error) => {
|
|
952
1114
|
console.error('Fatal error running server:', error);
|