@muxoai/platform 1.3.1 → 1.3.3

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/dist/store.js CHANGED
@@ -186,6 +186,23 @@ export class MuxoStore {
186
186
  created_at TEXT NOT NULL
187
187
  );
188
188
 
189
+ CREATE TABLE IF NOT EXISTS temporal_facts (
190
+ id TEXT PRIMARY KEY,
191
+ workspace_id TEXT NOT NULL,
192
+ subject TEXT NOT NULL,
193
+ predicate TEXT NOT NULL,
194
+ object TEXT NOT NULL,
195
+ summary TEXT NOT NULL,
196
+ confidence REAL NOT NULL DEFAULT 0.95,
197
+ valid_from TEXT NOT NULL,
198
+ valid_to TEXT,
199
+ superseded_by_id TEXT,
200
+ source_kind TEXT NOT NULL DEFAULT 'fact',
201
+ source_reference_id TEXT,
202
+ metadata_json TEXT NOT NULL DEFAULT '{}',
203
+ created_at TEXT NOT NULL
204
+ );
205
+
189
206
  CREATE TABLE IF NOT EXISTS agent_claims (
190
207
  claim_slug TEXT PRIMARY KEY,
191
208
  workspace_id TEXT NOT NULL,
@@ -516,7 +533,23 @@ export class MuxoStore {
516
533
  metadata_json = excluded.metadata_json,
517
534
  updated_at = excluded.updated_at`)
518
535
  .run(id, params.workspaceId, params.lane, params.providerSlug, params.providerName, params.status, params.externalAccountId ?? null, params.redirectUrl ?? null, JSON.stringify(params.metadata || {}), now, now);
519
- return this.getConnection(id);
536
+ const connRecord = this.getConnection(id);
537
+ this.upsertTemporalFact({
538
+ workspaceId: params.workspaceId,
539
+ subject: `app:${params.providerSlug}`,
540
+ predicate: "CONNECTION_STATUS",
541
+ object: `${params.lane}:${params.status}`,
542
+ summary: `App ${params.providerName} (${params.providerSlug}) connection on ${params.lane} is ${params.status}`,
543
+ confidence: 1.0,
544
+ sourceKind: "resource_topology",
545
+ sourceReferenceId: connRecord.id,
546
+ metadata: {
547
+ lane: params.lane,
548
+ providerSlug: params.providerSlug,
549
+ status: params.status,
550
+ },
551
+ });
552
+ return connRecord;
520
553
  }
521
554
  getConnection(id) {
522
555
  const row = this.db
@@ -566,7 +599,25 @@ export class MuxoStore {
566
599
  metadata_json = excluded.metadata_json,
567
600
  updated_at = excluded.updated_at`)
568
601
  .run(id, params.workspaceId, params.resourceName, params.providerSlug, params.providerName, params.serviceSlug, params.planSlug ?? null, params.status, JSON.stringify(envVarKeys), encryptedHex, params.externalResourceId ?? null, JSON.stringify(params.metadata || {}), now, now);
569
- return this.getResource(params.workspaceId, params.resourceName);
602
+ const resRecord = this.getResource(params.workspaceId, params.resourceName);
603
+ this.upsertTemporalFact({
604
+ workspaceId: params.workspaceId,
605
+ subject: `resource:${params.resourceName}`,
606
+ predicate: "PROVISIONED_ON",
607
+ object: `${params.serviceSlug}:${params.status}`,
608
+ summary: `Resource "${params.resourceName}" provisioned on ${params.serviceSlug} (${params.providerName}) with status ${params.status} [${resRecord.envVarKeys.join(", ") || "no env keys"}]`,
609
+ confidence: 1.0,
610
+ sourceKind: "resource_topology",
611
+ sourceReferenceId: resRecord.id,
612
+ metadata: {
613
+ providerSlug: params.providerSlug,
614
+ serviceSlug: params.serviceSlug,
615
+ planSlug: params.planSlug ?? null,
616
+ status: params.status,
617
+ envVarKeys: resRecord.envVarKeys,
618
+ },
619
+ });
620
+ return resRecord;
570
621
  }
571
622
  getResource(workspaceId, resourceNameOrId) {
572
623
  const row = this.db
@@ -793,7 +844,8 @@ export class MuxoStore {
793
844
  SET monthly_actions_used = ?, credits_balance = ?, updated_at = ?
794
845
  WHERE id = ?`)
795
846
  .run(newActionsUsed, newCreditsBalance, now, params.workspaceId);
796
- const byokUsed = this.hasByokForLane(params.workspaceId, run.lane, run.toolSlug);
847
+ const byokUsed = params.creditsMetered === 0 &&
848
+ this.hasByokForLane(params.workspaceId, run.lane, run.toolSlug);
797
849
  const settlement = this.computeSupplierSettlement(run.lane, run.toolSlug, params.creditsMetered, byokUsed ? "settled_by_customer_byok" : "settled_by_muxo_master_wallet", params.workspaceId);
798
850
  this.addLedgerEntry({
799
851
  workspaceId: params.workspaceId,
@@ -1138,6 +1190,17 @@ export class MuxoStore {
1138
1190
  totalSupplierWholesalePaidCents,
1139
1191
  totalMuxoNetMarginCents,
1140
1192
  bySupplier,
1193
+ masterWalletProtection: {
1194
+ status: process.env.MUXO_MASTER_WALLET_DEPLETED === "true"
1195
+ ? "failover_active"
1196
+ : "healthy",
1197
+ zeroChargeOnFailure: true,
1198
+ instantFailoverMode: "zero_cost_native_edge",
1199
+ paidProvisioningPolicy: "muxo_master_wallet_or_customer_stripe_byok",
1200
+ retailToWholesaleCoverageRatio: totalSupplierWholesalePaidCents > 0
1201
+ ? Math.round((totalUserRetailSpendCents / totalSupplierWholesalePaidCents) * 100) / 100
1202
+ : 1.67,
1203
+ },
1141
1204
  };
1142
1205
  }
1143
1206
  listLedgerEntries(workspaceId, limit = 50) {
@@ -1220,6 +1283,15 @@ export class MuxoStore {
1220
1283
  this.db
1221
1284
  .prepare("DELETE FROM memories WHERE id = ? AND workspace_id = ?")
1222
1285
  .run(String(row.id), workspaceId);
1286
+ // Also mark any temporal facts sourced from this memory record as invalidated (valid_to = now)
1287
+ const now = new Date().toISOString();
1288
+ this.db
1289
+ .prepare(`UPDATE temporal_facts
1290
+ SET valid_to = ?
1291
+ WHERE workspace_id = ?
1292
+ AND valid_to IS NULL
1293
+ AND (source_reference_id = ? OR source_reference_id = ? OR id = ?)`)
1294
+ .run(now, workspaceId, String(row.id), String(row.document_id), idOrDocId);
1223
1295
  return {
1224
1296
  id: String(row.id),
1225
1297
  workspaceId: String(row.workspace_id),
@@ -1232,6 +1304,166 @@ export class MuxoStore {
1232
1304
  createdAt: String(row.created_at),
1233
1305
  };
1234
1306
  }
1307
+ // --- Bi-Temporal Resource & Fact Lineage (Zero-Dependency Graphiti Alternative) ---
1308
+ upsertTemporalFact(params) {
1309
+ const now = new Date().toISOString();
1310
+ const subject = params.subject.trim();
1311
+ const predicate = params.predicate.trim().toUpperCase().replace(/[^A-Z0-9_]/g, "_");
1312
+ const object = params.object.trim();
1313
+ const confidence = params.confidence !== undefined ? Number(params.confidence) : 0.95;
1314
+ const sourceKind = params.sourceKind || "fact";
1315
+ // Check if an active fact already exists for (workspace_id, subject, predicate)
1316
+ const activeRow = this.db
1317
+ .prepare(`SELECT * FROM temporal_facts
1318
+ WHERE workspace_id = ? AND subject = ? AND predicate = ? AND valid_to IS NULL
1319
+ ORDER BY created_at DESC LIMIT 1`)
1320
+ .get(params.workspaceId, subject, predicate);
1321
+ if (activeRow && String(activeRow.object) === object) {
1322
+ // Same object is still valid — update summary/confidence/metadata in place
1323
+ const existingId = String(activeRow.id);
1324
+ this.db
1325
+ .prepare(`UPDATE temporal_facts
1326
+ SET summary = ?, confidence = ?, metadata_json = ?, source_reference_id = COALESCE(?, source_reference_id)
1327
+ WHERE id = ?`)
1328
+ .run(params.summary, confidence, JSON.stringify(params.metadata || {}), params.sourceReferenceId ?? null, existingId);
1329
+ return this.mapTemporalFact({
1330
+ ...activeRow,
1331
+ summary: params.summary,
1332
+ confidence,
1333
+ metadata_json: JSON.stringify(params.metadata || {}),
1334
+ source_reference_id: params.sourceReferenceId ?? activeRow.source_reference_id,
1335
+ });
1336
+ }
1337
+ const newId = `tf_${crypto.randomBytes(8).toString("hex")}`;
1338
+ if (activeRow && params.supersedeExisting !== false) {
1339
+ this.db
1340
+ .prepare(`UPDATE temporal_facts
1341
+ SET valid_to = ?, superseded_by_id = ?
1342
+ WHERE id = ? AND workspace_id = ?`)
1343
+ .run(now, newId, String(activeRow.id), params.workspaceId);
1344
+ }
1345
+ this.db
1346
+ .prepare(`INSERT INTO temporal_facts (
1347
+ id, workspace_id, subject, predicate, object, summary, confidence,
1348
+ valid_from, valid_to, superseded_by_id, source_kind, source_reference_id,
1349
+ metadata_json, created_at
1350
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?, ?, ?, ?)`)
1351
+ .run(newId, params.workspaceId, subject, predicate, object, params.summary, confidence, now, sourceKind, params.sourceReferenceId ?? null, JSON.stringify(params.metadata || {}), now);
1352
+ return {
1353
+ id: newId,
1354
+ workspaceId: params.workspaceId,
1355
+ subject,
1356
+ predicate,
1357
+ object,
1358
+ summary: params.summary,
1359
+ confidence,
1360
+ validFrom: now,
1361
+ validTo: null,
1362
+ supersededById: null,
1363
+ sourceKind,
1364
+ sourceReferenceId: params.sourceReferenceId ?? null,
1365
+ metadata: params.metadata || {},
1366
+ createdAt: now,
1367
+ };
1368
+ }
1369
+ listTemporalFacts(workspaceId, opts) {
1370
+ const limit = Math.min(200, Math.max(1, Number(opts?.limit || 50)));
1371
+ const includeHistorical = Boolean(opts?.includeHistorical);
1372
+ const rows = this.db
1373
+ .prepare(includeHistorical
1374
+ ? `SELECT * FROM temporal_facts WHERE workspace_id = ? ORDER BY created_at DESC LIMIT ?`
1375
+ : `SELECT * FROM temporal_facts WHERE workspace_id = ? AND valid_to IS NULL ORDER BY created_at DESC LIMIT ?`)
1376
+ .all(workspaceId, limit);
1377
+ let facts = rows.map((r) => this.mapTemporalFact(r));
1378
+ if (opts?.subject) {
1379
+ const subj = opts.subject.toLowerCase();
1380
+ facts = facts.filter((f) => f.subject.toLowerCase().includes(subj));
1381
+ }
1382
+ if (opts?.predicate) {
1383
+ const pred = opts.predicate.toUpperCase();
1384
+ facts = facts.filter((f) => f.predicate.toUpperCase().includes(pred));
1385
+ }
1386
+ if (opts?.query && opts.query.trim()) {
1387
+ const tokens = opts.query
1388
+ .toLowerCase()
1389
+ .split(/\s+/)
1390
+ .filter((t) => t.length >= 2);
1391
+ if (tokens.length > 0) {
1392
+ facts = facts.filter((f) => {
1393
+ const hay = `${f.subject} ${f.predicate} ${f.object} ${f.summary}`.toLowerCase();
1394
+ return tokens.some((tok) => hay.includes(tok));
1395
+ });
1396
+ }
1397
+ }
1398
+ return facts;
1399
+ }
1400
+ forgetMatchingMemoryRecords(workspaceId, query) {
1401
+ const trimmed = query.trim().toLowerCase();
1402
+ if (!trimmed) {
1403
+ return { deletedMemories: [], invalidatedFacts: [] };
1404
+ }
1405
+ const tokens = trimmed.split(/\s+/).filter((t) => t.length >= 2);
1406
+ const allMemories = this.listMemoryRecords(workspaceId, 200);
1407
+ const matchingMemories = allMemories.filter((m) => {
1408
+ const hay = `${m.title} ${m.content} ${m.kind}`.toLowerCase();
1409
+ return (hay.includes(trimmed) ||
1410
+ (tokens.length > 0 && tokens.every((tok) => hay.includes(tok))));
1411
+ });
1412
+ // Snapshot before deleting: deleteMemoryRecord retires sourced facts itself,
1413
+ // and those must still be reported as invalidated.
1414
+ const activeFacts = this.listTemporalFacts(workspaceId, {
1415
+ includeHistorical: false,
1416
+ limit: 200,
1417
+ });
1418
+ const deletedMemories = [];
1419
+ for (const mem of matchingMemories) {
1420
+ const deleted = this.deleteMemoryRecord(workspaceId, mem.id);
1421
+ if (deleted)
1422
+ deletedMemories.push(deleted);
1423
+ }
1424
+ const deletedSourceIds = new Set(deletedMemories.flatMap((m) => [m.id, m.documentId]));
1425
+ const matchingFacts = activeFacts.filter((f) => {
1426
+ const hay = `${f.subject} ${f.predicate} ${f.object} ${f.summary}`.toLowerCase();
1427
+ return (hay.includes(trimmed) ||
1428
+ (tokens.length > 0 && tokens.every((tok) => hay.includes(tok))) ||
1429
+ Boolean(f.sourceReferenceId && deletedSourceIds.has(f.sourceReferenceId)));
1430
+ });
1431
+ const now = new Date().toISOString();
1432
+ const invalidatedFacts = [];
1433
+ for (const fact of matchingFacts) {
1434
+ this.db
1435
+ .prepare(`UPDATE temporal_facts SET valid_to = ? WHERE id = ? AND workspace_id = ?`)
1436
+ .run(now, fact.id, workspaceId);
1437
+ invalidatedFacts.push({
1438
+ ...fact,
1439
+ validTo: now,
1440
+ });
1441
+ }
1442
+ return {
1443
+ deletedMemories,
1444
+ invalidatedFacts,
1445
+ };
1446
+ }
1447
+ mapTemporalFact(row) {
1448
+ return {
1449
+ id: String(row.id),
1450
+ workspaceId: String(row.workspace_id),
1451
+ subject: String(row.subject),
1452
+ predicate: String(row.predicate),
1453
+ object: String(row.object),
1454
+ summary: String(row.summary),
1455
+ confidence: Number(row.confidence ?? 0.95),
1456
+ validFrom: String(row.valid_from),
1457
+ validTo: row.valid_to ? String(row.valid_to) : null,
1458
+ supersededById: row.superseded_by_id ? String(row.superseded_by_id) : null,
1459
+ sourceKind: String(row.source_kind || "fact"),
1460
+ sourceReferenceId: row.source_reference_id
1461
+ ? String(row.source_reference_id)
1462
+ : null,
1463
+ metadata: JSON.parse(String(row.metadata_json || "{}")),
1464
+ createdAt: String(row.created_at),
1465
+ };
1466
+ }
1235
1467
  // --- Autonomous Agent Onboarding (agents.composio.dev pattern) ---
1236
1468
  createAutonomousAgentSignup(params) {
1237
1469
  const suffix = crypto.randomBytes(4).toString("hex");
@@ -1426,6 +1658,7 @@ export class MuxoStore {
1426
1658
  input: JSON.parse(String(row.input_json || "{}")),
1427
1659
  output: row.output_json ? JSON.parse(String(row.output_json)) : null,
1428
1660
  error: row.error ? String(row.error) : null,
1661
+ errorMessage: row.error ? String(row.error) : null,
1429
1662
  actionsMetered: Number(row.actions_metered),
1430
1663
  creditsMetered: Number(row.credits_metered),
1431
1664
  durationMs: row.duration_ms !== null ? Number(row.duration_ms) : null,
package/dist/types.d.ts CHANGED
@@ -8,6 +8,10 @@ export interface GovernancePolicy {
8
8
  allowProvisioning: boolean;
9
9
  maxCreditsPerRun: number;
10
10
  blockedToolSlugs?: string[];
11
+ executionRing?: 0 | 1 | 2 | 3;
12
+ killSwitchActive?: boolean;
13
+ killSwitchReason?: string | null;
14
+ blockDestructiveSql?: boolean;
11
15
  }
12
16
  export interface User {
13
17
  id: string;
@@ -126,15 +130,82 @@ export interface GovernanceDecisionRecord {
126
130
  previousHash: string;
127
131
  chainVerified: boolean;
128
132
  };
133
+ protocolFacets?: {
134
+ sql?: {
135
+ verb: string;
136
+ target: string;
137
+ tables: string;
138
+ functions: string;
139
+ destructive?: boolean;
140
+ };
141
+ k8s?: {
142
+ verb: string;
143
+ resource: string;
144
+ namespace: string;
145
+ name: string;
146
+ subresource: string;
147
+ };
148
+ infra?: {
149
+ provider: string;
150
+ service: string;
151
+ action: string;
152
+ destructive: boolean;
153
+ };
154
+ };
155
+ ringEnforcement?: {
156
+ agentRing: number;
157
+ requiredRing: number;
158
+ allowed: boolean;
159
+ ringLabel: string;
160
+ };
161
+ trustScore?: {
162
+ overall: number;
163
+ tier: "Untrusted" | "Provisional" | "Trusted" | "Verified";
164
+ reliability: number;
165
+ consistency: number;
166
+ };
167
+ contextPoisoning?: {
168
+ safe: boolean;
169
+ riskLevel: "none" | "low" | "medium" | "high" | "critical";
170
+ findingsCount: number;
171
+ findings: Array<{
172
+ patternId: string;
173
+ patternName: string;
174
+ severity: string;
175
+ description: string;
176
+ }>;
177
+ };
178
+ killSwitch?: {
179
+ active: boolean;
180
+ killedAt?: string | null;
181
+ reason?: string | null;
182
+ };
129
183
  ed25519Signature: string;
130
184
  signature: string;
131
185
  bindingHash: string;
132
186
  decidedAt: string;
133
187
  }
134
188
  export type MemoryRecordKind = "fact" | "preference" | "resource_note" | "operator_note" | "execution_outcome" | "resource_topology" | "policy_decision";
189
+ export interface TemporalGraphFact {
190
+ id: string;
191
+ workspaceId: string;
192
+ subject: string;
193
+ predicate: string;
194
+ object: string;
195
+ summary: string;
196
+ confidence: number;
197
+ validFrom: string;
198
+ validTo: string | null;
199
+ supersededById?: string | null;
200
+ sourceKind: MemoryRecordKind | "system";
201
+ sourceReferenceId?: string | null;
202
+ metadata: Record<string, unknown>;
203
+ createdAt: string;
204
+ }
135
205
  export interface MemoryContextSummary {
136
206
  enabled: boolean;
137
207
  namespace: string;
208
+ fundingSource?: "muxo_funded" | "customer_byok_supermemory" | "local_fallback";
138
209
  staticFacts: string[];
139
210
  dynamicContext: string[];
140
211
  recalledMemories: Array<{
@@ -145,6 +216,7 @@ export interface MemoryContextSummary {
145
216
  createdAt?: string;
146
217
  }>;
147
218
  formattedPromptContext: string;
219
+ profileMarkdown?: string;
148
220
  profile: {
149
221
  static: string[];
150
222
  dynamic: string[];
@@ -157,6 +229,12 @@ export interface MemoryContextSummary {
157
229
  similarity: number;
158
230
  createdAt?: string;
159
231
  }>;
232
+ temporalFacts?: TemporalGraphFact[];
233
+ contextPoisoningScan?: {
234
+ safe: boolean;
235
+ riskLevel: "none" | "low" | "medium" | "high" | "critical";
236
+ findingsCount: number;
237
+ };
160
238
  }
161
239
  export interface WorkspaceMemoryRecord {
162
240
  id: string;
@@ -209,6 +287,13 @@ export interface SupplierSettlementSummary {
209
287
  supplierWholesalePaidCents: number;
210
288
  muxoNetMarginCents: number;
211
289
  }>;
290
+ masterWalletProtection?: {
291
+ status: "healthy" | "failover_active";
292
+ zeroChargeOnFailure: boolean;
293
+ instantFailoverMode: "zero_cost_native_edge";
294
+ paidProvisioningPolicy: "muxo_master_wallet_or_customer_stripe_byok";
295
+ retailToWholesaleCoverageRatio: number;
296
+ };
212
297
  }
213
298
  export interface ActionPlan {
214
299
  id: string;
@@ -243,6 +328,7 @@ export interface ExecutionRun {
243
328
  input: Record<string, unknown>;
244
329
  output: Record<string, unknown> | null;
245
330
  error: string | null;
331
+ errorMessage?: string | null;
246
332
  actionsMetered: number;
247
333
  creditsMetered: number;
248
334
  durationMs: number | null;
@@ -1,2 +1,25 @@
1
+ export type ConsoleView = "overview" | "setup" | "catalog" | "connections" | "infrastructure" | "memory" | "governance" | "activity" | "billing" | "login" | "signup";
2
+ /** Every path that serves the console shell, mapped to the view it opens. */
3
+ export declare const CONSOLE_ROUTES: Readonly<Record<string, ConsoleView>>;
4
+ export declare function isConsolePath(pathname: string): boolean;
1
5
  export declare function renderConsoleHtml(): string;
2
6
  export declare function renderLandingHtml(): string;
7
+ export interface OAuthConsentOptions {
8
+ error?: string;
9
+ clientName?: string;
10
+ redirectOrigin?: string;
11
+ clerk?: {
12
+ publishableKey: string;
13
+ frontendApi: string;
14
+ } | null;
15
+ request?: Record<string, string>;
16
+ /** Renders the workspace-claim variant, which posts to `action` instead of /oauth/authorize. */
17
+ claim?: {
18
+ workspaceName: string;
19
+ targetEmail: string;
20
+ action: string;
21
+ cancelUrl: string;
22
+ };
23
+ }
24
+ /** Consent screen for MCP clients (ChatGPT, Claude, CLIs) asking to use a Muxo workspace. */
25
+ export declare function renderOAuthConsentHtml(opts: OAuthConsentOptions): string;