@muxoai/platform 1.3.1 → 1.3.2

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/server.js CHANGED
@@ -6,7 +6,9 @@ import { getConfig, ROOT_DIR } from "./config.js";
6
6
  import { MuxoEngine } from "./engine.js";
7
7
  import { getMuxoMcpSkills, handleJsonRpcMcpRequest, MUXO_MCP_SERVER_INSTRUCTIONS, MUXO_MCP_TOOLS, } from "./mcp.js";
8
8
  import { MuxoStore } from "./store.js";
9
- import { renderConsoleHtml, renderLandingHtml } from "./ui/console-html.js";
9
+ import { sanitizePolicyPatch } from "./policy.js";
10
+ import { MUXO_VERSION } from "./version.js";
11
+ import { isConsolePath, renderConsoleHtml, renderLandingHtml } from "./ui/console-html.js";
10
12
  function renderSkillMarkdown(baseUrl) {
11
13
  return `---
12
14
  name: muxo
@@ -170,7 +172,7 @@ export function createMuxoServer(customConfig) {
170
172
  ok: true,
171
173
  status: "ready",
172
174
  service: "muxo-platform",
173
- version: "1.3.1",
175
+ version: MUXO_VERSION,
174
176
  environment: "production",
175
177
  live_execution: true,
176
178
  customer_billing: true,
@@ -464,7 +466,7 @@ export function createMuxoServer(customConfig) {
464
466
  openapi: "3.1.0",
465
467
  info: {
466
468
  title: "Muxo Unified Agent Execution, Infrastructure, Memory & Governance API",
467
- version: "1.3.1",
469
+ version: MUXO_VERSION,
468
470
  description: "One governed boundary over Connected Apps (Composio SDK), Instant Tools (Composio Instant), Cloud Infrastructure (Stripe Projects), Supermemory v5 Brain, and Microsoft Agent Governance Toolkit.",
469
471
  },
470
472
  servers: [{ url: config.baseUrl }],
@@ -634,12 +636,20 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
634
636
  });
635
637
  return;
636
638
  }
637
- // Web Console UI (app.muxo.ai)
638
- if ((pathname === "/" || pathname === "/console") && req.method === "GET") {
639
+ // Web Console UI (app.muxo.ai) & SPA deep-links
640
+ if (isConsolePath(pathname) && req.method === "GET") {
639
641
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
640
642
  res.end(renderConsoleHtml());
641
643
  return;
642
644
  }
645
+ if (pathname === "/downloads/muxo-cli.zip" && req.method === "GET") {
646
+ res.writeHead(302, {
647
+ Location: "https://muxo.ai/plugins/muxo-chatgpt-plugin.zip",
648
+ "Cache-Control": "no-store",
649
+ });
650
+ res.end();
651
+ return;
652
+ }
643
653
  // Built-in Interactive Landing & Provider Canvas (muxo.ai)
644
654
  if (pathname === "/landing" && req.method === "GET") {
645
655
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
@@ -753,8 +763,11 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
753
763
  </html>`);
754
764
  return;
755
765
  }
756
- // Autonomous Agent 2-Call Signup (agents.composio.dev pattern) — Public
757
- if (pathname === "/api/v1/agents/signup" && req.method === "POST") {
766
+ // Autonomous Agent 2-Call Signup — Public
767
+ if ((pathname === "/api/v1/agents/signup" ||
768
+ pathname === "/api/v1/auth/signup" ||
769
+ pathname === "/api/signup") &&
770
+ req.method === "POST") {
758
771
  const body = JSON.parse((await readBody(req)) || "{}");
759
772
  const created = store.createAutonomousAgentSignup({
760
773
  agentName: body.agentName ? String(body.agentName) : undefined,
@@ -805,7 +818,7 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
805
818
  protocolVersion: "2025-06-18",
806
819
  serverInfo: {
807
820
  name: "muxo-mcp-server",
808
- version: "1.3.1",
821
+ version: MUXO_VERSION,
809
822
  },
810
823
  instructions: MUXO_MCP_SERVER_INSTRUCTIONS,
811
824
  toolsCount: MUXO_MCP_TOOLS.length,
@@ -845,6 +858,10 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
845
858
  return;
846
859
  }
847
860
  // Auth Endpoints
861
+ if (pathname === "/api/v1/auth/config" && req.method === "GET") {
862
+ sendJson(res, 200, { ok: true, provider: "password", clerk: null, signInUrl: "/login" });
863
+ return;
864
+ }
848
865
  if (pathname === "/api/v1/auth/register" && req.method === "POST") {
849
866
  const body = JSON.parse((await readBody(req)) || "{}");
850
867
  const created = store.registerUser({
@@ -853,7 +870,16 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
853
870
  password: String(body.password || ""),
854
871
  workspaceName: body.workspaceName ? String(body.workspaceName) : undefined,
855
872
  });
856
- sendJson(res, 201, { ok: true, ...created });
873
+ const initialKey = store.createApiKey({
874
+ workspaceId: created.workspace.id,
875
+ name: "Default Live Key",
876
+ environment: "live",
877
+ });
878
+ sendJson(res, 201, {
879
+ ok: true,
880
+ ...created,
881
+ apiKey: initialKey.secretKey,
882
+ });
857
883
  return;
858
884
  }
859
885
  if (pathname === "/api/v1/auth/login" && req.method === "POST") {
@@ -917,11 +943,49 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
917
943
  }
918
944
  // Microsoft Agent Governance Status & Audit Verification
919
945
  if (pathname === "/api/v1/governance" && req.method === "GET") {
946
+ const ws = store.getWorkspace(ctx.workspace.id) || ctx.workspace;
947
+ const fullReport = engine.governance.getGovernanceStatus(ws);
920
948
  sendJson(res, 200, {
921
949
  ok: true,
922
- governance: engine.governance.getStatus(),
923
- workspacePolicy: ctx.workspace.policy,
950
+ governance: {
951
+ ...engine.governance.getStatus(ws),
952
+ modules: fullReport.modules,
953
+ trustScore: fullReport.trustScore,
954
+ killSwitch: fullReport.killSwitch,
955
+ contextPoisoning: fullReport.contextPoisoning,
956
+ auditChain: fullReport.auditChain,
957
+ },
958
+ workspacePolicy: ws.policy,
959
+ });
960
+ return;
961
+ }
962
+ if (pathname === "/api/v1/governance/kill-switch" && req.method === "POST") {
963
+ const body = JSON.parse((await readBody(req)) || "{}");
964
+ const action = String(body.action || "quarantine").toLowerCase();
965
+ if (action === "resume" || body.active === false) {
966
+ const out = engine.resumeWorkspaceKillSwitch(ctx);
967
+ sendJson(res, 200, { ok: true, ...out });
968
+ return;
969
+ }
970
+ const out = await engine.activateWorkspaceKillSwitch(ctx, {
971
+ reason: String(body.reason || "Emergency kill-switch activated by workspace operator"),
972
+ action: action === "terminate" || action === "suspend"
973
+ ? action
974
+ : "quarantine",
975
+ });
976
+ sendJson(res, 200, { ok: true, ...out });
977
+ return;
978
+ }
979
+ if (pathname === "/api/v1/governance/scan-context" && req.method === "POST") {
980
+ const body = JSON.parse((await readBody(req)) || "{}");
981
+ const scan = engine.governance.scanMemoryContent({
982
+ workspaceId: ctx.workspace.id,
983
+ sessionId: String(body.sessionId || "default"),
984
+ role: body.role || "user",
985
+ content: String(body.content || ""),
986
+ recordInDetector: Boolean(body.recordInDetector),
924
987
  });
988
+ sendJson(res, 200, { ok: true, scan });
925
989
  return;
926
990
  }
927
991
  // Supermemory v5 Workspace Brain Endpoints
@@ -933,6 +997,37 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
933
997
  sendJson(res, 200, { ok: true, ...overview });
934
998
  return;
935
999
  }
1000
+ if (pathname === "/api/v1/memory/facts" && req.method === "GET") {
1001
+ const query = url.searchParams.get("query") ||
1002
+ url.searchParams.get("q") ||
1003
+ undefined;
1004
+ const includeHistorical = url.searchParams.get("historical") !== "false";
1005
+ const facts = store.listTemporalFacts(ctx.workspace.id, {
1006
+ includeHistorical,
1007
+ subject: url.searchParams.get("subject") || undefined,
1008
+ predicate: url.searchParams.get("predicate") || undefined,
1009
+ query,
1010
+ limit: Number(url.searchParams.get("limit") || 50),
1011
+ });
1012
+ sendJson(res, 200, {
1013
+ ok: true,
1014
+ activeFacts: facts.filter((f) => f.validTo === null),
1015
+ historicalFacts: facts.filter((f) => f.validTo !== null),
1016
+ facts,
1017
+ });
1018
+ return;
1019
+ }
1020
+ if (pathname === "/api/v1/memory/forget" && req.method === "POST") {
1021
+ const body = JSON.parse((await readBody(req)) || "{}");
1022
+ const query = String(body.query || body.q || "").trim();
1023
+ if (!query) {
1024
+ sendJson(res, 400, { ok: false, error: "query is required to forget matching memories" });
1025
+ return;
1026
+ }
1027
+ const pruned = await engine.forgetWorkspaceMemoryMatching(ctx, query);
1028
+ sendJson(res, 200, { ok: true, ...pruned });
1029
+ return;
1030
+ }
936
1031
  if (pathname === "/api/v1/memory" && req.method === "POST") {
937
1032
  const body = JSON.parse((await readBody(req)) || "{}");
938
1033
  const record = await engine.addWorkspaceMemory(ctx, {
@@ -940,6 +1035,9 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
940
1035
  content: String(body.content || ""),
941
1036
  kind: body.kind || "fact",
942
1037
  metadata: body.metadata || {},
1038
+ subject: body.subject ? String(body.subject) : undefined,
1039
+ predicate: body.predicate ? String(body.predicate) : undefined,
1040
+ object: body.object ? String(body.object) : undefined,
943
1041
  });
944
1042
  sendJson(res, 201, { ok: true, memory: record });
945
1043
  return;
@@ -961,6 +1059,15 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
961
1059
  sendJson(res, 201, { ok: true, ...created });
962
1060
  return;
963
1061
  }
1062
+ if (pathname.startsWith("/api/v1/api-keys/") && req.method === "DELETE") {
1063
+ const keyId = decodeURIComponent(pathname.slice("/api/v1/api-keys/".length));
1064
+ if (!store.revokeApiKey(ctx.workspace.id, keyId)) {
1065
+ sendJson(res, 404, { ok: false, error: "API key not found" });
1066
+ return;
1067
+ }
1068
+ sendJson(res, 200, { ok: true, revoked: keyId });
1069
+ return;
1070
+ }
964
1071
  // Profile & Organization (v1 and api/v1)
965
1072
  if ((pathname === "/api/v1/profile" || pathname === "/v1/profile") && req.method === "GET") {
966
1073
  sendJson(res, 200, { ok: true, ...engine.getProfile(ctx) });
@@ -1060,7 +1167,7 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
1060
1167
  // Governance Policy
1061
1168
  if (pathname === "/api/v1/policy" && req.method === "POST") {
1062
1169
  const body = JSON.parse((await readBody(req)) || "{}");
1063
- const updated = store.updateWorkspacePolicy(ctx.workspace.id, body);
1170
+ const updated = store.updateWorkspacePolicy(ctx.workspace.id, sanitizePolicyPatch(body));
1064
1171
  sendJson(res, 200, { ok: true, workspace: updated });
1065
1172
  return;
1066
1173
  }
@@ -1074,10 +1181,17 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
1074
1181
  }
1075
1182
  if (pathname === "/api/v1/plans" && req.method === "POST") {
1076
1183
  const body = JSON.parse((await readBody(req)) || "{}");
1184
+ const rawFunding = body.fundingMode || body.funding_mode;
1185
+ const fundingMode = rawFunding === "byok" || rawFunding === "customer_funded"
1186
+ ? "byok"
1187
+ : rawFunding === "muxo_funded"
1188
+ ? "muxo_funded"
1189
+ : undefined;
1077
1190
  const planned = await engine.planAction(ctx, {
1078
1191
  toolSlug: String(body.toolSlug || body.tool_slug || ""),
1079
1192
  input: body.input || {},
1080
1193
  reason: body.reason ? String(body.reason) : undefined,
1194
+ fundingMode,
1081
1195
  });
1082
1196
  sendJson(res, 201, { ok: true, ...planned });
1083
1197
  return;
@@ -1101,6 +1215,13 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
1101
1215
  });
1102
1216
  return;
1103
1217
  }
1218
+ const planGetMatch = pathname.match(/^\/api\/v1\/plans\/([^/]+)$/);
1219
+ if (planGetMatch && req.method === "GET") {
1220
+ const planId = decodeURIComponent(planGetMatch[1]);
1221
+ const plan = engine.getPlan(ctx, planId);
1222
+ sendJson(res, 200, { ok: true, plan });
1223
+ return;
1224
+ }
1104
1225
  // Runs & Receipts
1105
1226
  if (pathname === "/api/v1/runs" && req.method === "GET") {
1106
1227
  sendJson(res, 200, {
@@ -1109,6 +1230,21 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
1109
1230
  });
1110
1231
  return;
1111
1232
  }
1233
+ const runCancelMatch = pathname.match(/^\/api\/v1\/runs\/([^/]+)\/cancel$/);
1234
+ if (runCancelMatch && req.method === "POST") {
1235
+ const runId = decodeURIComponent(runCancelMatch[1]);
1236
+ const body = JSON.parse((await readBody(req)) || "{}");
1237
+ const cancelled = engine.cancelRun(ctx, runId, body.reason ? String(body.reason) : undefined);
1238
+ sendJson(res, 200, { ok: true, ...cancelled });
1239
+ return;
1240
+ }
1241
+ const runGetMatch = pathname.match(/^\/api\/v1\/runs\/([^/]+)$/);
1242
+ if (runGetMatch && req.method === "GET") {
1243
+ const runId = decodeURIComponent(runGetMatch[1]);
1244
+ const run = engine.getRun(ctx, runId);
1245
+ sendJson(res, 200, { ok: true, run });
1246
+ return;
1247
+ }
1112
1248
  if (pathname.startsWith("/api/v1/receipts/") && req.method === "GET") {
1113
1249
  const receiptId = decodeURIComponent(pathname.slice("/api/v1/receipts/".length));
1114
1250
  const verified = engine.getReceipt(ctx, receiptId);
@@ -1158,7 +1294,13 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
1158
1294
  if ((pathname === "/api/v1/do" || pathname === "/v1/do") &&
1159
1295
  req.method === "POST") {
1160
1296
  const body = JSON.parse((await readBody(req)) || "{}");
1161
- const prompt = String(body.prompt || body.instruction || body.query || body.command || "").trim();
1297
+ const prompt = String(body.prompt ||
1298
+ body.instruction ||
1299
+ body.query ||
1300
+ body.command ||
1301
+ body.toolSlug ||
1302
+ body.tool ||
1303
+ "").trim();
1162
1304
  if (!prompt) {
1163
1305
  sendJson(res, 400, {
1164
1306
  ok: false,
@@ -1166,19 +1308,37 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
1166
1308
  });
1167
1309
  return;
1168
1310
  }
1311
+ const dryRun = body.dryRun === true ||
1312
+ body.dry_run === true ||
1313
+ body.planOnly === true ||
1314
+ body.autoExecute === false ||
1315
+ body.auto_execute === false;
1316
+ const autoApprove = body.autoApprove !== false &&
1317
+ body.auto_approve !== false &&
1318
+ body.approveRisky !== false &&
1319
+ body.approve_risky !== false;
1320
+ const rawFunding = body.fundingMode || body.funding_mode;
1321
+ const fundingMode = rawFunding === "byok" || rawFunding === "customer_funded"
1322
+ ? "byok"
1323
+ : rawFunding === "muxo_funded"
1324
+ ? "muxo_funded"
1325
+ : undefined;
1169
1326
  const result = await engine.doNaturalLanguage(ctx, {
1170
1327
  prompt,
1171
1328
  input: body.input && typeof body.input === "object"
1172
1329
  ? body.input
1173
1330
  : undefined,
1174
- autoApprove: body.autoApprove !== false,
1175
- dryRun: body.dryRun === true,
1331
+ autoApprove,
1332
+ dryRun,
1333
+ planOnly: dryRun,
1334
+ fundingMode,
1176
1335
  });
1177
1336
  sendJson(res, 200, result);
1178
1337
  return;
1179
1338
  }
1180
1339
  // Live Stripe Billing, Credit Top-Up & Supplier Settlement Ledger
1181
- if (pathname === "/api/v1/billing/status" && req.method === "GET") {
1340
+ if ((pathname === "/api/v1/billing/status" || pathname === "/api/v1/billing") &&
1341
+ req.method === "GET") {
1182
1342
  const ws = store.getWorkspace(ctx.workspace.id);
1183
1343
  const settlementSummary = store.getSupplierSettlementSummary(ctx.workspace.id);
1184
1344
  const byokKeys = store.listByokKeys(ctx.workspace.id);
package/dist/store.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DatabaseSync } from "node:sqlite";
2
- import type { ActionPlan, ApiKeyRecord, ByokKeyRecord, ConnectionRecord, ExecutionReceipt, ExecutionRun, GovernanceDecisionRecord, GovernancePolicy, LedgerEntry, MemoryContextSummary, ProviderLane, ProvisionedResource, RiskLevel, SupplierSettlement, SupplierSettlementSummary, User, Workspace, WorkspaceMemoryRecord } from "./types.js";
2
+ import type { ActionPlan, ApiKeyRecord, ByokKeyRecord, ConnectionRecord, ExecutionReceipt, ExecutionRun, GovernanceDecisionRecord, GovernancePolicy, LedgerEntry, MemoryContextSummary, ProviderLane, ProvisionedResource, RiskLevel, SupplierSettlement, SupplierSettlementSummary, TemporalGraphFact, User, Workspace, WorkspaceMemoryRecord } from "./types.js";
3
3
  export declare const BYOK_PROVIDER_CATALOG: Record<string, {
4
4
  category: "llm" | "supplier";
5
5
  envVarName: string;
@@ -173,6 +173,30 @@ export declare class MuxoStore {
173
173
  }): WorkspaceMemoryRecord;
174
174
  listMemoryRecords(workspaceId: string, limit?: number): WorkspaceMemoryRecord[];
175
175
  deleteMemoryRecord(workspaceId: string, idOrDocId: string): WorkspaceMemoryRecord | null;
176
+ upsertTemporalFact(params: {
177
+ workspaceId: string;
178
+ subject: string;
179
+ predicate: string;
180
+ object: string;
181
+ summary: string;
182
+ confidence?: number;
183
+ sourceKind?: TemporalGraphFact["sourceKind"];
184
+ sourceReferenceId?: string | null;
185
+ metadata?: Record<string, unknown>;
186
+ supersedeExisting?: boolean;
187
+ }): TemporalGraphFact;
188
+ listTemporalFacts(workspaceId: string, opts?: {
189
+ includeHistorical?: boolean;
190
+ subject?: string;
191
+ predicate?: string;
192
+ query?: string;
193
+ limit?: number;
194
+ }): TemporalGraphFact[];
195
+ forgetMatchingMemoryRecords(workspaceId: string, query: string): {
196
+ deletedMemories: WorkspaceMemoryRecord[];
197
+ invalidatedFacts: TemporalGraphFact[];
198
+ };
199
+ private mapTemporalFact;
176
200
  createAutonomousAgentSignup(params?: {
177
201
  agentName?: string;
178
202
  harness?: string;
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,