prism-mcp-server 20.21.20 → 20.21.22

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
@@ -158,6 +158,22 @@ or by re-enabling after each run.
158
158
  <details>
159
159
  <summary>Release history (optional)</summary>
160
160
 
161
+ ## What's New in v20.21.22
162
+
163
+ - Factory pipeline lists for cloud accounts use the authenticated Portal API,
164
+ including exact project and status filters, without direct database credentials.
165
+ - Invalid, incomplete, or oversized reads show an unavailable state. A valid empty
166
+ list remains distinct from a failed read.
167
+ - This release covers pipeline lists. Cloud pipeline creation, controls, and
168
+ execution still need separate validation.
169
+
170
+ ## What's New in v20.21.21
171
+
172
+ - Prism Dashboard now reports unavailable analytics and factory reads clearly,
173
+ prevents stale project results, and keeps graph filters and counts in sync.
174
+ - Project analytics can be read through the authenticated Portal API with
175
+ validated 14-day activity data.
176
+
161
177
  ## What's New in v20.21.20
162
178
 
163
179
  - Fixed: a `session_save_ledger` call refused with `context_not_loaded`, or the
@@ -1,7 +1,9 @@
1
+ import { PRISM_USER_ID } from '../config.js';
1
2
  /** Paid clients have Portal credentials, not direct Supabase credentials. */
2
- export async function readDashboardLedger(storage, backend, project, order, limit) {
3
+ export async function readDashboardLedger(storage, backend, project, order, limit, userId = PRISM_USER_ID) {
3
4
  if (backend !== "synalux") {
4
- return storage.getLedgerEntries({ project: `eq.${project}`, order, limit: String(limit) });
5
+ return storage.getLedgerEntries({ project: `eq.${project}`, user_id: `eq.${userId}`,
6
+ deleted_at: 'is.null', order, limit: String(limit) });
5
7
  }
6
8
  if (!storage.getDashboardLedger)
7
9
  throw new Error("Cloud dashboard ledger reader unavailable");
@@ -0,0 +1,48 @@
1
+ export const DASHBOARD_READ_MESSAGES = {
2
+ analyticsLoading: 'Loading memory analytics…',
3
+ analyticsUnavailable: 'Memory analytics unavailable. Retry to refresh this project.',
4
+ pipelinesLoading: 'Loading pipelines…',
5
+ pipelinesUnavailable: 'Pipelines unavailable. Retry to refresh the list.',
6
+ retry: 'Retry',
7
+ activeEntries: 'Active entries',
8
+ consolidatedEntries: 'Entries consolidated',
9
+ sessionEntry: 'session', rollupEntry: 'rollup',
10
+ projectUnavailable: 'Project data unavailable. Retry Inspect to refresh.',
11
+ graphLoading: 'Loading graph…',
12
+ graphUnavailable: 'Graph unavailable. Retry to refresh the selected filters.',
13
+ graphEmpty: 'No knowledge associations match these filters.',
14
+ graphSessions: 'sessions', graphStoredLinks: 'stored links',
15
+ graphProjects: 'projects', graphKeywords: 'keywords', graphEdges: 'edges',
16
+ vmUnavailable: 'VM inventory unavailable. This dashboard has not loaded a live template or running-instance source.',
17
+ vmActionUnavailable: 'VM creation is unavailable until a runtime provider is connected to this dashboard.',
18
+ marketplaceUnavailable: 'Component catalog unavailable. Product availability, prices and payouts have not been loaded from a live source.',
19
+ marketplaceActionUnavailable: 'This action is unavailable until a live component catalog is connected.',
20
+ complianceUnavailable: 'Live enforcement status unavailable in this dashboard. No control can be certified active from this view.',
21
+ auditUnavailable: 'Audit events unavailable. This dashboard has not loaded a live audit source.',
22
+ policyUnavailable: 'This dashboard has not loaded a verified policy reference.',
23
+ directGeminiKey: 'Used for direct Gemini operations. Synalux-managed Gemini embeddings use your account instead of a separate Google key.',
24
+ providerChoice: 'Text generation and semantic retrieval use separate provider settings. Choose the providers that match your configured services.',
25
+ customRoleInstructions: 'Custom role instructions are injected when context loads for this role. This editor does not list the provisioned skill library.',
26
+ vmTitle: 'VM Lab 🖥️', vmAvailability: 'Check VM availability',
27
+ marketplaceTitle: 'Component Marketplace 🛒', marketplaceAvailability: 'Check catalog availability',
28
+ publishComponent: 'Publish Component', complianceTitle: 'Compliance status 🛡️',
29
+ policyAvailability: 'Check policy reference',
30
+ memoryScanTitle: 'Memory scan 🩺', refreshScan: 'Refresh scan',
31
+ scanLoading: 'Scanning…', coverageUnavailable: 'Scan coverage unavailable. Refresh to try again.',
32
+ scanUnavailable: 'Scan unavailable. No integrity result can be confirmed.',
33
+ partialScan: 'Partial inventory', scanPassed: 'Scan passed',
34
+ partialCoverage: 'Account-wide embedding inventory only. Duplicate, orphan and rollup checks are not measured by this cloud view.',
35
+ backendCoverage: 'Account-wide memory scan. This result does not describe overall service health.',
36
+ noScanIssues: 'No issues reported by this scan.',
37
+ noEmbeddingIssues: 'No missing embeddings reported. Other integrity checks are not measured here.',
38
+ scanIssues: 'Issues reported',
39
+ repairCoverage: 'Fix Issues is available for missing embeddings or orphaned handoffs. Cleanup repairs embeddings and removes orphaned handoffs. Other failures require their own recovery path.',
40
+ graphActivityTitle: 'Graph activity · this process', recordedSuccess: 'Recorded synthesis success',
41
+ schedulerOutcome: 'Latest scheduler graph synthesis', recordedResults: 'Recorded results',
42
+ schedulerSucceeded: 'succeeded', schedulerFailed: 'failed', schedulerAttempted: 'attempted',
43
+ lastDirectLinks: 'Last direct synthesis links', lastPrune: 'Last pruning ratio',
44
+ };
45
+ export const ANALYTICS_METRIC_IDS = [
46
+ 'astat-entries', 'astat-rollups', 'astat-savings', 'astat-avglen',
47
+ ];
48
+ export const ANALYTICS_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
@@ -37,6 +37,7 @@ import { redactSettings } from "../tools/commonHelpers.js";
37
37
  import { handleGraphRoutes } from "./graphRouter.js";
38
38
  import { handleAccountRoutes } from "./accountRouter.js";
39
39
  import { createDashboardStorageAccessor } from "./storageAccessor.js";
40
+ import { DASHBOARD_READ_MESSAGES } from "./readMessages.js";
40
41
  import { isDashboardSettingKeyAllowed, isDashboardSettingValueAllowed } from "./settingsPolicy.js";
41
42
  import { isTrustedRequest, isRebindGuardedPath } from "./hostGuard.js";
42
43
  import { resolveDashboardToken, requestHasToken, buildTokenCookie, dashboardTokenCookieName, isDashboardTokenProtectedPath, } from "./dashboardToken.js";
@@ -352,7 +353,7 @@ return false;}
352
353
  googleApiKey: {
353
354
  type: "string",
354
355
  title: "Google AI API Key",
355
- description: "API key for Google AI Studio / Gemini (powers synthesis, embeddings, paper analysis). Get one at https://aistudio.google.com/apikey"
356
+ description: DASHBOARD_READ_MESSAGES.directGeminiKey,
356
357
  },
357
358
  storage: {
358
359
  type: "string",
@@ -553,18 +554,19 @@ return false;}
553
554
  }
554
555
  const stats = await s.getHealthStats(PRISM_USER_ID);
555
556
  const report = runHealthCheck(stats);
556
- res.writeHead(200, { "Content-Type": "application/json" });
557
- return res.end(JSON.stringify(report));
557
+ res.writeHead(stats.missingEmbeddings < 0 ? 503 : 200, { "Content-Type": "application/json", "Cache-Control": "no-store" });
558
+ return res.end(JSON.stringify({ ...report, scope: 'account', backend: activeStorageBackend,
559
+ coverage: stats.missingEmbeddings < 0 ? 'unavailable' : activeStorageBackend === 'synalux' ? 'embedding_inventory' : 'backend_scan' }));
558
560
  }
559
561
  catch (err) {
560
562
  console.error("[Dashboard] Health check error:", err);
561
- res.writeHead(200, { "Content-Type": "application/json" });
563
+ res.writeHead(503, { "Content-Type": "application/json", "Cache-Control": "no-store" });
562
564
  return res.end(JSON.stringify({
563
565
  status: "unknown",
566
+ scope: 'account', backend: activeStorageBackend, coverage: 'unavailable',
564
567
  summary: "Health check unavailable",
565
568
  issues: [],
566
569
  counts: { errors: 0, warnings: 0, infos: 0 },
567
- totals: { activeEntries: 0, handoffs: 0, rollups: 0 },
568
570
  timestamp: new Date().toISOString(),
569
571
  }));
570
572
  }
@@ -581,6 +583,10 @@ return false;}
581
583
  }
582
584
  const stats = await s.getHealthStats(PRISM_USER_ID);
583
585
  const report = runHealthCheck(stats);
586
+ if (stats.missingEmbeddings < 0) {
587
+ res.writeHead(503, { "Content-Type": "application/json", "Cache-Control": "no-store" });
588
+ return res.end(JSON.stringify({ ok: false, error: DASHBOARD_READ_MESSAGES.scanUnavailable }));
589
+ }
584
590
  let repairedCount = 0;
585
591
  let failedCount = 0;
586
592
  let cleanupMessages = [];
@@ -782,11 +788,8 @@ return false;}
782
788
  }
783
789
  catch (err) {
784
790
  console.error("[Dashboard] Analytics error:", err);
785
- res.writeHead(200, { "Content-Type": "application/json" });
786
- return res.end(JSON.stringify({
787
- totalEntries: 0, totalRollups: 0, rollupSavings: 0,
788
- avgSummaryLength: 0, sessionsByDay: [],
789
- }));
791
+ res.writeHead(503, { "Content-Type": "application/json", "Cache-Control": "no-store" });
792
+ return res.end(JSON.stringify({ error: DASHBOARD_READ_MESSAGES.analyticsUnavailable }));
790
793
  }
791
794
  }
792
795
  // ─── API: Retention (TTL) Settings (v3.1) ──────────────