prism-mcp-server 15.0.0 → 15.1.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.
@@ -269,4 +269,30 @@ export class SynaluxStorage extends SupabaseStorage {
269
269
  const results = Array.isArray(result.results) ? result.results : [];
270
270
  return { count, results };
271
271
  }
272
+ /**
273
+ * Fetch skill content from Synalux portal (paid-tier single source of truth).
274
+ * Returns a map of { skillName → SKILL.md content } for all names that exist.
275
+ * Missing skills are absent from the map — caller falls back to local SQLite.
276
+ *
277
+ * Uses a public GET (no auth required) since skill content is not sensitive
278
+ * and the route is already in synalux-private/portal at /api/v1/skills/content.
279
+ */
280
+ async fetchSkillContent(names) {
281
+ if (names.length === 0)
282
+ return {};
283
+ const url = `${this.baseUrl}/api/v1/skills/content?names=${encodeURIComponent(names.join(","))}`;
284
+ try {
285
+ const res = await fetch(url, {
286
+ headers: { Accept: "application/json" },
287
+ signal: AbortSignal.timeout(3_000),
288
+ });
289
+ if (!res.ok)
290
+ return {};
291
+ const body = await res.json();
292
+ return typeof body.skills === "object" && body.skills !== null ? body.skills : {};
293
+ }
294
+ catch {
295
+ return {};
296
+ }
297
+ }
272
298
  }
@@ -30,7 +30,7 @@ import { getCurrentGitState, getGitDrift } from "../utils/git.js";
30
30
  import { getSetting, getAllSettings } from "../storage/configStorage.js";
31
31
  import { mergeHandoff, dbToHandoffSchema, sanitizeForMerge } from "../utils/crdtMerge.js";
32
32
  import { resolveProject } from "../utils/projectResolver.js";
33
- import { PRISM_USER_ID, PRISM_AUTO_CAPTURE, PRISM_CAPTURE_PORTS } from "../config.js";
33
+ import { PRISM_USER_ID, PRISM_AUTO_CAPTURE, PRISM_CAPTURE_PORTS, SYNALUX_CONFIGURED } from "../config.js";
34
34
  import { captureLocalEnvironment } from "../utils/autoCapture.js";
35
35
  import { fireCaptionAsync } from "../utils/imageCaptioner.js";
36
36
  import { isSessionSaveLedgerArgs, isSessionSaveHandoffArgs, isSessionLoadContextArgs, isMemoryHistoryArgs, isMemoryCheckoutArgs, // v2.2.0: health check type guard
@@ -798,23 +798,30 @@ export async function sessionLoadContextHandler(args) {
798
798
  }
799
799
  }
800
800
  // ─── Project-Aware Skill Injection ──────────────────────────
801
- // Skill routing (which skills load for which project) is the SINGLE
802
- // SOURCE OF TRUTH at synalux: /api/v1/skills/routing. We pull the
803
- // canonical table on every session and resolve locally. Skill CONTENT
804
- // continues to be stored in this server's settings under skill:<name>
805
- // — synalux owns the WHICH, this server owns the WHAT, no duplication
806
- // of the routing config in three repos.
801
+ // Routing (WHICH skills): always from Synalux /api/v1/skills/routing.
802
+ // Content (WHAT): paid tier Synalux /api/v1/skills/content (batch fetch,
803
+ // single source of truth). Free tier / offline local SQLite fallback.
807
804
  const { resolveSkillsForProject } = await import("./skillRouting.js");
808
805
  const skillsToLoad = await resolveSkillsForProject(project);
806
+ // Paid tier: batch-fetch all skill content from Synalux portal in one request.
807
+ let synaluxContent = {};
808
+ if (SYNALUX_CONFIGURED && storage && typeof storage.fetchSkillContent === "function") {
809
+ const missing = skillsToLoad.filter(n => !loadedSkills.includes(n));
810
+ synaluxContent = await storage
811
+ .fetchSkillContent(missing).catch(() => ({}));
812
+ debugLog(`[session_load_context] Synalux skill content fetched: ${Object.keys(synaluxContent).join(", ") || "none"}`);
813
+ }
809
814
  for (const skillName of skillsToLoad) {
810
815
  if (loadedSkills.includes(skillName))
811
816
  continue;
812
- const content = await getSetting(`skill:${skillName}`, "");
817
+ // Prefer Synalux content (always up-to-date); fall back to local SQLite.
818
+ const content = synaluxContent[skillName] || await getSetting(`skill:${skillName}`, "");
813
819
  if (content && content.trim()) {
820
+ const source = synaluxContent[skillName] ? "synalux" : "local";
814
821
  skillBlock += `\n\n[📜 SKILL: ${skillName}]\n${content.trim()}`;
815
822
  loadedSkills.push(skillName);
816
823
  skillLoaded = true;
817
- debugLog(`[session_load_context] Skill "${skillName}" loaded for project="${project}"`);
824
+ debugLog(`[session_load_context] Skill "${skillName}" loaded (${source}) for project="${project}"`);
818
825
  }
819
826
  }
820
827
  // ─── Memory-Based Skill Discovery ──────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prism-mcp-server",
3
- "version": "15.0.0",
3
+ "version": "15.1.0",
4
4
  "mcpName": "io.github.dcostenco/prism-coder",
5
5
  "description": "Prism Coder — Cognitive memory + tool-calling intelligence for AI agents. Mind Palace persistent memory (BFCL Gold Certified, 100% Tool-Call Accuracy, 54 Agent Skills, Zero-Search HDC/HRR retrieval, HIPAA-hardened local-first storage, SLERP-optimized GRPO alignment) plus the prism-coder:7b / 14b open-weights LLM fleet.",
6
6
  "module": "index.ts",