@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/engine.d.ts CHANGED
@@ -78,6 +78,7 @@ export declare class MuxoEngine {
78
78
  toolSlug: string;
79
79
  input?: Record<string, unknown>;
80
80
  reason?: string;
81
+ fundingMode?: "muxo_funded" | "byok";
81
82
  }): Promise<{
82
83
  plan: ActionPlan;
83
84
  tool: MuxoTool;
@@ -100,26 +101,45 @@ export declare class MuxoEngine {
100
101
  getMemoryOverview(ctx: ExecutionContext, query?: string): Promise<{
101
102
  enabled: boolean;
102
103
  namespace: string;
104
+ fundingSource: string;
103
105
  context: MemoryContextSummary;
104
106
  recentMemories: WorkspaceMemoryRecord[];
105
107
  memories: WorkspaceMemoryRecord[];
108
+ temporalFacts: ReturnType<MuxoStore["listTemporalFacts"]>;
109
+ historicalFacts: ReturnType<MuxoStore["listTemporalFacts"]>;
106
110
  }>;
107
111
  addWorkspaceMemory(ctx: ExecutionContext, params: {
108
112
  title: string;
109
113
  content: string;
110
114
  kind?: WorkspaceMemoryRecord["kind"];
111
115
  metadata?: Record<string, unknown>;
116
+ subject?: string;
117
+ predicate?: string;
118
+ object?: string;
112
119
  }): Promise<WorkspaceMemoryRecord>;
113
120
  deleteWorkspaceMemory(ctx: ExecutionContext, idOrDocId: string): Promise<{
114
121
  deleted: boolean;
115
122
  record: WorkspaceMemoryRecord | null;
116
123
  }>;
124
+ forgetWorkspaceMemoryMatching(ctx: ExecutionContext, query: string): Promise<{
125
+ query: string;
126
+ deletedLocalCount: number;
127
+ invalidatedFactsCount: number;
128
+ deletedRemoteIds: string[];
129
+ deletedMemories: WorkspaceMemoryRecord[];
130
+ invalidatedFacts: ReturnType<MuxoStore["listTemporalFacts"]>;
131
+ }>;
132
+ activateWorkspaceKillSwitch(ctx: ExecutionContext, params: {
133
+ reason: string;
134
+ action?: "terminate" | "quarantine" | "suspend";
135
+ }): Promise<Record<string, unknown>>;
136
+ resumeWorkspaceKillSwitch(ctx: ExecutionContext): Record<string, unknown>;
117
137
  executePlanForRisk(ctx: ExecutionContext, planId: string, expectedRisk: RiskLevel): Promise<{
118
138
  run: ExecutionRun;
119
139
  receipt: ExecutionReceipt;
120
140
  }>;
121
141
  getRun(ctx: ExecutionContext, runId: string): ExecutionRun;
122
- cancelRun(ctx: ExecutionContext, runId: string): {
142
+ cancelRun(ctx: ExecutionContext, runId: string, reason?: string): {
123
143
  run: ExecutionRun;
124
144
  receipt: ExecutionReceipt | null;
125
145
  };
package/dist/engine.js CHANGED
@@ -3,7 +3,7 @@ import { ComposioInstantAdapter } from "./providers/composio-instant.js";
3
3
  import { ComposioSdkAdapter } from "./providers/composio-sdk.js";
4
4
  import { MicrosoftGovernanceAdapter } from "./providers/microsoft-governance.js";
5
5
  import { StripeBillingAdapter } from "./providers/stripe-billing.js";
6
- import { StripeProjectsAdapter } from "./providers/stripe-projects.js";
6
+ import { StripeProjectsAdapter, isPaidCloudProvisioning, } from "./providers/stripe-projects.js";
7
7
  import { SupermemoryAdapter } from "./providers/supermemory.js";
8
8
  export class MuxoEngine {
9
9
  config;
@@ -396,8 +396,14 @@ export class MuxoEngine {
396
396
  resolvedInput = nl.input;
397
397
  resolvedReason = params.reason || nl.reason || nlSource;
398
398
  }
399
- const byokActive = this.store.hasByokForLane(ws.id, tool.lane, tool.slug);
400
- const effectiveCreditCost = byokActive ? 0 : tool.creditCost;
399
+ const byokActive = params.fundingMode === "muxo_funded"
400
+ ? false
401
+ : this.store.hasByokForLane(ws.id, tool.lane, tool.slug);
402
+ const isPaidResourcePlan = isPaidCloudProvisioning(tool, resolvedInput);
403
+ const baseCreditCost = isPaidResourcePlan
404
+ ? Math.max(tool.creditCost, 25)
405
+ : tool.creditCost;
406
+ const effectiveCreditCost = byokActive ? 0 : baseCreditCost;
401
407
  if (ws.monthlyActionsUsed >= ws.monthlyActionsLimit) {
402
408
  throw new Error(`Monthly action limit reached (${ws.monthlyActionsUsed}/${ws.monthlyActionsLimit}). Upgrade to Starter ($29/mo) to unlock 5,000 actions/mo.`);
403
409
  }
@@ -421,7 +427,8 @@ export class MuxoEngine {
421
427
  }
422
428
  // 2. Recall Supermemory v5 contextual brain for this workspace + tool intent
423
429
  const memoryQuery = `${tool.slug} ${tool.name} ${resolvedReason}`.trim();
424
- const memoryContext = await this.enrichMemoryContext(ws.id, await this.supermemory.getContextForWorkspace(ws.id, memoryQuery));
430
+ const supermemoryByok = this.store.getByokKey(ws.id, "supermemory");
431
+ const memoryContext = await this.enrichMemoryContext(ws.id, await this.supermemory.getContextForWorkspace(ws.id, memoryQuery, supermemoryByok), memoryQuery);
425
432
  const requiresHumanApproval = decisionRecord.verdict === "escalate" ||
426
433
  (ws.policy.mode === "enforce" &&
427
434
  ws.policy.requireApprovalFor.includes(tool.riskLevel));
@@ -510,13 +517,13 @@ export class MuxoEngine {
510
517
  approvedBy: ctx.user.email,
511
518
  });
512
519
  }
513
- async enrichMemoryContext(workspaceId, ctx) {
514
- if (ctx.recalledMemories.length > 0 && ctx.staticFacts.length > 0) {
515
- return ctx;
516
- }
520
+ async enrichMemoryContext(workspaceId, ctx, query) {
517
521
  const localRecords = this.store.listMemoryRecords(workspaceId, 8);
518
- if (localRecords.length === 0)
519
- return ctx;
522
+ const temporalFacts = this.store.listTemporalFacts(workspaceId, {
523
+ includeHistorical: false,
524
+ query,
525
+ limit: 15,
526
+ });
520
527
  const recalled = ctx.recalledMemories.length > 0
521
528
  ? ctx.recalledMemories
522
529
  : localRecords.slice(0, 5).map((r) => ({
@@ -524,6 +531,7 @@ export class MuxoEngine {
524
531
  title: r.title,
525
532
  snippet: r.content.slice(0, 240),
526
533
  similarity: 1,
534
+ createdAt: r.createdAt,
527
535
  }));
528
536
  const staticFacts = ctx.staticFacts.length > 0
529
537
  ? ctx.staticFacts
@@ -531,14 +539,66 @@ export class MuxoEngine {
531
539
  .filter((r) => r.kind === "fact" || r.kind === "preference")
532
540
  .slice(0, 5)
533
541
  .map((r) => `${r.title}: ${r.content.slice(0, 140)}`);
542
+ const dynamicContext = ctx.dynamicContext.length > 0
543
+ ? ctx.dynamicContext
544
+ : temporalFacts.slice(0, 6).map((f) => `${f.subject} ${f.predicate} ${f.object}`);
545
+ const combinedText = [
546
+ ...staticFacts,
547
+ ...dynamicContext,
548
+ ...recalled.map((m) => `${m.title}: ${m.snippet}`),
549
+ ].join("\n");
550
+ const poisonScan = this.governance.scanMemoryContent({
551
+ workspaceId,
552
+ sessionId: ctx.namespace,
553
+ role: "system",
554
+ content: combinedText,
555
+ recordInDetector: false,
556
+ });
557
+ const promptLines = [
558
+ `[Supermemory Brain — Namespace: ${ctx.namespace}]`,
559
+ ];
560
+ if (staticFacts.length > 0) {
561
+ promptLines.push(`Static Facts: ${staticFacts.join(" | ")}`);
562
+ }
563
+ if (dynamicContext.length > 0) {
564
+ promptLines.push(`Dynamic Context: ${dynamicContext.join(" | ")}`);
565
+ }
566
+ if (temporalFacts.length > 0) {
567
+ promptLines.push(`Bi-Temporal Lineage (${temporalFacts.length} active): ${temporalFacts
568
+ .slice(0, 5)
569
+ .map((f) => `${f.subject} -[${f.predicate}]-> ${f.object}`)
570
+ .join(" | ")}`);
571
+ }
572
+ if (recalled.length > 0) {
573
+ promptLines.push(`Recalled Memories: ${recalled.map((m) => `${m.title}: ${m.snippet}`).join(" | ")}`);
574
+ }
575
+ const mdLines = [`## Workspace Brain (${ctx.namespace})`];
576
+ if (staticFacts.length > 0) {
577
+ mdLines.push("### Static Facts", ...staticFacts.map((f) => `- ${f}`));
578
+ }
579
+ if (temporalFacts.length > 0) {
580
+ mdLines.push("### Bi-Temporal Resource & Fact Lineage (Active)", ...temporalFacts
581
+ .slice(0, 8)
582
+ .map((f) => `- **${f.subject}** \`${f.predicate}\` **${f.object}** _(validFrom: ${f.validFrom})_ — ${f.summary}`));
583
+ }
534
584
  return {
535
585
  ...ctx,
536
586
  staticFacts,
587
+ dynamicContext,
537
588
  recalledMemories: recalled,
538
589
  relevantMemories: recalled,
590
+ formattedPromptContext: promptLines.join("\n"),
591
+ profileMarkdown: ctx.profileMarkdown || mdLines.join("\n"),
539
592
  profile: {
540
593
  ...ctx.profile,
541
594
  static: staticFacts,
595
+ dynamic: dynamicContext,
596
+ },
597
+ temporalFacts,
598
+ contextPoisoningScan: {
599
+ safe: poisonScan.safe,
600
+ riskLevel: poisonScan.riskLevel,
601
+ findingsCount: poisonScan.findingsCount,
542
602
  },
543
603
  };
544
604
  }
@@ -552,7 +612,8 @@ export class MuxoEngine {
552
612
  redactedEnv[k] =
553
613
  v.length > 8 ? `${v.slice(0, 4)}••••${v.slice(-3)}` : "••••••••";
554
614
  }
555
- const memoryContext = await this.enrichMemoryContext(ws.id, await this.supermemory.getContextForWorkspace(ws.id, query));
615
+ const supermemoryByok = this.store.getByokKey(ws.id, "supermemory");
616
+ const memoryContext = await this.enrichMemoryContext(ws.id, await this.supermemory.getContextForWorkspace(ws.id, query, supermemoryByok), query);
556
617
  return {
557
618
  workspaceId: ws.id,
558
619
  workspaceSlug: ws.slug,
@@ -563,33 +624,62 @@ export class MuxoEngine {
563
624
  provisionedResources: resources.filter((r) => r.status === "active"),
564
625
  environmentVariablesRedacted: redactedEnv,
565
626
  supermemoryBrain: memoryContext,
627
+ temporalFacts: memoryContext.temporalFacts || [],
566
628
  governance: this.governance.getStatus(ws),
629
+ trustScore: this.governance.getWorkspaceTrustScore(ws.id),
567
630
  envPullHint: "Run `muxo env pull` or call GET /api/v1/resources/env to write decrypted credentials to your local .env file.",
568
631
  };
569
632
  }
570
633
  // Supermemory Workspace Brain Operations
571
634
  async getMemoryOverview(ctx, query) {
572
- const context = await this.enrichMemoryContext(ctx.workspace.id, await this.supermemory.getContextForWorkspace(ctx.workspace.id, query));
635
+ const supermemoryByok = this.store.getByokKey(ctx.workspace.id, "supermemory");
636
+ const context = await this.enrichMemoryContext(ctx.workspace.id, await this.supermemory.getContextForWorkspace(ctx.workspace.id, query, supermemoryByok), query);
573
637
  const recentMemories = this.store.listMemoryRecords(ctx.workspace.id, 25);
638
+ const allFacts = this.store.listTemporalFacts(ctx.workspace.id, {
639
+ includeHistorical: true,
640
+ query,
641
+ limit: 50,
642
+ });
574
643
  return {
575
644
  enabled: context.enabled,
576
645
  namespace: context.namespace,
646
+ fundingSource: context.fundingSource || "local_fallback",
577
647
  context,
578
648
  recentMemories,
579
649
  memories: recentMemories,
650
+ temporalFacts: allFacts.filter((f) => f.validTo === null),
651
+ historicalFacts: allFacts.filter((f) => f.validTo !== null),
580
652
  };
581
653
  }
582
654
  async addWorkspaceMemory(ctx, params) {
655
+ const ws = this.store.getWorkspace(ctx.workspace.id);
583
656
  const kind = params.kind || "fact";
657
+ // Microsoft Agent Governance OWASP ASI-06 ContextPoisoningDetector guard on memory ingestion
658
+ const poisonScan = this.governance.scanMemoryContent({
659
+ workspaceId: ws.id,
660
+ sessionId: this.supermemory.getWorkspaceNamespace(ws.id),
661
+ role: "user",
662
+ content: `${params.title}\n${params.content}`,
663
+ recordInDetector: true,
664
+ });
665
+ if (!poisonScan.safe && ws.policy.mode === "enforce") {
666
+ this.governance.recordAgentOutcome(ws.id, false, 0.1);
667
+ throw new Error(`Blocked by Microsoft ContextPoisoningDetector (OWASP ASI-06, risk=${poisonScan.riskLevel}): ${poisonScan.findings
668
+ .map((f) => `${f.patternName} (${f.description})`)
669
+ .join("; ")}`);
670
+ }
671
+ const supermemoryByok = this.store.getByokKey(ws.id, "supermemory");
584
672
  const remote = await this.supermemory.addMemory({
585
- workspaceId: ctx.workspace.id,
673
+ workspaceId: ws.id,
586
674
  title: params.title,
587
675
  content: params.content,
588
676
  kind,
589
677
  metadata: params.metadata,
678
+ apiKeyOverride: supermemoryByok,
679
+ dreaming: "instant",
590
680
  });
591
- return this.store.saveMemoryRecord({
592
- workspaceId: ctx.workspace.id,
681
+ const record = this.store.saveMemoryRecord({
682
+ workspaceId: ws.id,
593
683
  namespace: remote.namespace,
594
684
  documentId: remote.documentId,
595
685
  title: params.title,
@@ -598,18 +688,103 @@ export class MuxoEngine {
598
688
  metadata: {
599
689
  ...(params.metadata || {}),
600
690
  supermemoryStatus: remote.status,
691
+ fundingSource: remote.fundingSource,
692
+ contextPoisoningScan: {
693
+ safe: poisonScan.safe,
694
+ riskLevel: poisonScan.riskLevel,
695
+ findingsCount: poisonScan.findingsCount,
696
+ },
601
697
  },
602
698
  });
699
+ // Record bi-temporal entity/relationship lineage (auto-superseding prior facts for same subject+predicate)
700
+ const normalizedTitlePred = params.title
701
+ .trim()
702
+ .toUpperCase()
703
+ .replace(/[^A-Z0-9]+/g, "_")
704
+ .replace(/^_|_$/g, "")
705
+ .slice(0, 48);
706
+ const subject = params.subject ||
707
+ (typeof params.metadata?.subject === "string"
708
+ ? params.metadata.subject
709
+ : `workspace:${ws.slug}`);
710
+ const predicate = params.predicate ||
711
+ (typeof params.metadata?.predicate === "string"
712
+ ? params.metadata.predicate
713
+ : normalizedTitlePred || kind.toUpperCase());
714
+ const object = params.object ||
715
+ (typeof params.metadata?.object === "string"
716
+ ? params.metadata.object
717
+ : remote.sanitizedContent.slice(0, 160));
718
+ this.store.upsertTemporalFact({
719
+ workspaceId: ws.id,
720
+ subject,
721
+ predicate,
722
+ object,
723
+ summary: `${params.title}: ${remote.sanitizedContent.slice(0, 240)}`,
724
+ confidence: 0.95,
725
+ sourceKind: kind,
726
+ sourceReferenceId: record.id,
727
+ metadata: params.metadata || {},
728
+ });
729
+ return record;
603
730
  }
604
731
  async deleteWorkspaceMemory(ctx, idOrDocId) {
605
732
  const local = this.store.deleteMemoryRecord(ctx.workspace.id, idOrDocId);
606
733
  const docId = local?.documentId || idOrDocId;
607
- const remote = await this.supermemory.deleteMemory(ctx.workspace.id, docId);
734
+ const supermemoryByok = this.store.getByokKey(ctx.workspace.id, "supermemory");
735
+ const remote = await this.supermemory.deleteMemory(ctx.workspace.id, docId, supermemoryByok);
608
736
  return {
609
737
  deleted: Boolean(local || remote.deleted),
610
738
  record: local,
611
739
  };
612
740
  }
741
+ async forgetWorkspaceMemoryMatching(ctx, query) {
742
+ const localPruned = this.store.forgetMatchingMemoryRecords(ctx.workspace.id, query);
743
+ const supermemoryByok = this.store.getByokKey(ctx.workspace.id, "supermemory");
744
+ const remotePruned = await this.supermemory.forgetMatching({
745
+ workspaceId: ctx.workspace.id,
746
+ query,
747
+ apiKeyOverride: supermemoryByok,
748
+ });
749
+ return {
750
+ query,
751
+ deletedLocalCount: localPruned.deletedMemories.length,
752
+ invalidatedFactsCount: localPruned.invalidatedFacts.length,
753
+ deletedRemoteIds: remotePruned.deletedRemoteIds,
754
+ deletedMemories: localPruned.deletedMemories,
755
+ invalidatedFacts: localPruned.invalidatedFacts,
756
+ };
757
+ }
758
+ async activateWorkspaceKillSwitch(ctx, params) {
759
+ const ws = this.store.getWorkspace(ctx.workspace.id);
760
+ const result = await this.governance.activateKillSwitch(ws.id, params.reason, params.action || "quarantine");
761
+ const updatedWs = this.store.updateWorkspacePolicy(ws.id, {
762
+ ...ws.policy,
763
+ killSwitchActive: true,
764
+ killSwitchReason: params.reason,
765
+ });
766
+ return {
767
+ activated: true,
768
+ killSwitch: result,
769
+ policy: updatedWs.policy,
770
+ trustScore: this.governance.getWorkspaceTrustScore(ws.id),
771
+ };
772
+ }
773
+ resumeWorkspaceKillSwitch(ctx) {
774
+ const ws = this.store.getWorkspace(ctx.workspace.id);
775
+ const resumed = this.governance.resumeKillSwitch(ws.id);
776
+ const updatedWs = this.store.updateWorkspacePolicy(ws.id, {
777
+ ...ws.policy,
778
+ killSwitchActive: false,
779
+ killSwitchReason: null,
780
+ });
781
+ return {
782
+ ...resumed,
783
+ resumed: true,
784
+ policy: updatedWs.policy,
785
+ trustScore: this.governance.getWorkspaceTrustScore(ws.id),
786
+ };
787
+ }
613
788
  // 10-15. execute_<risk_level>
614
789
  async executePlanForRisk(ctx, planId, expectedRisk) {
615
790
  const plan = this.getPlan(ctx, planId);
@@ -632,8 +807,17 @@ export class MuxoEngine {
632
807
  }
633
808
  const ws = this.store.getWorkspace(ctx.workspace.id);
634
809
  const tool = await this.inspectTool(plan.toolSlug);
635
- const byokActive = this.store.hasByokForLane(ws.id, tool.lane, tool.slug);
636
- const effectiveCreditCost = byokActive ? 0 : plan.creditCost;
810
+ const explicitFunding = plan.supplierSettlement?.fundingMode;
811
+ const byokActive = explicitFunding === "byok"
812
+ ? true
813
+ : explicitFunding === "muxo_funded"
814
+ ? false
815
+ : this.store.hasByokForLane(ws.id, tool.lane, tool.slug);
816
+ const effectiveCreditCost = byokActive
817
+ ? 0
818
+ : plan.creditCost > 0
819
+ ? plan.creditCost
820
+ : tool.creditCost;
637
821
  if (ws.monthlyActionsUsed >= ws.monthlyActionsLimit) {
638
822
  throw new Error(`Monthly action limit reached (${ws.monthlyActionsUsed}/${ws.monthlyActionsLimit}). Upgrade to Starter ($29/mo) to unlock 5,000 actions/mo.`);
639
823
  }
@@ -683,16 +867,28 @@ export class MuxoEngine {
683
867
  });
684
868
  }
685
869
  else if (plan.lane === "composio_instant") {
686
- const composioByok = this.store.getByokKey(ctx.workspace.id, "composio");
870
+ const composioByok = byokActive
871
+ ? this.store.getByokKey(ctx.workspace.id, "composio")
872
+ : null;
873
+ const exaByok = byokActive
874
+ ? this.store.getByokKey(ctx.workspace.id, "exa")
875
+ : null;
876
+ const firecrawlByok = byokActive
877
+ ? this.store.getByokKey(ctx.workspace.id, "firecrawl")
878
+ : null;
687
879
  output = await this.composioInstant.executeTool({
688
880
  workspaceId: ctx.workspace.id,
689
881
  tool,
690
882
  input: plan.input,
691
883
  composioApiKey: composioByok,
884
+ exaApiKey: exaByok,
885
+ firecrawlApiKey: firecrawlByok,
692
886
  });
693
887
  }
694
888
  else if (plan.lane === "composio_sdk") {
695
- const composioByok = this.store.getByokKey(ctx.workspace.id, "composio");
889
+ const composioByok = byokActive
890
+ ? this.store.getByokKey(ctx.workspace.id, "composio")
891
+ : null;
696
892
  output = await this.composioSdk.executeTool({
697
893
  workspaceId: ctx.workspace.id,
698
894
  tool,
@@ -702,10 +898,14 @@ export class MuxoEngine {
702
898
  });
703
899
  }
704
900
  else if (plan.lane === "stripe_projects") {
901
+ const stripeByok = byokActive
902
+ ? this.store.getByokKey(ctx.workspace.id, "stripe")
903
+ : null;
705
904
  const projResult = await this.stripeProjects.executeTool({
706
905
  workspaceId: ctx.workspace.id,
707
906
  tool,
708
907
  input: plan.input,
908
+ stripeApiKey: stripeByok,
709
909
  });
710
910
  output = projResult.output;
711
911
  if (projResult.provisionedResource) {
@@ -719,6 +919,7 @@ export class MuxoEngine {
719
919
  throw new Error(`Unsupported execution lane: ${plan.lane}`);
720
920
  }
721
921
  const durationMs = Date.now() - startedMs;
922
+ this.governance.recordAgentOutcome(ctx.workspace.id, true, 0.05);
722
923
  const finalized = this.store.finalizeRun({
723
924
  workspaceId: ctx.workspace.id,
724
925
  runId: run.id,
@@ -729,9 +930,27 @@ export class MuxoEngine {
729
930
  durationMs,
730
931
  decisionRecord: execDecisionRecord,
731
932
  });
933
+ this.store.upsertTemporalFact({
934
+ workspaceId: ctx.workspace.id,
935
+ subject: `tool:${plan.toolSlug}`,
936
+ predicate: "LAST_EXECUTION",
937
+ object: `succeeded:${finalized.run.id}`,
938
+ summary: `Executed ${plan.toolSlug} (${plan.lane}, risk=${plan.riskLevel}) -> succeeded [receipt=${finalized.receipt.id}]`,
939
+ confidence: 1.0,
940
+ sourceKind: "execution_outcome",
941
+ sourceReferenceId: finalized.run.id,
942
+ metadata: {
943
+ runId: finalized.run.id,
944
+ receiptId: finalized.receipt.id,
945
+ toolSlug: plan.toolSlug,
946
+ lane: plan.lane,
947
+ riskLevel: plan.riskLevel,
948
+ },
949
+ });
732
950
  // Record secret-redacted post-run memory in Supermemory for stateful actions
733
951
  if (plan.riskLevel !== "read") {
734
952
  const summary = `Executed ${plan.toolSlug} (${plan.lane}, risk=${plan.riskLevel}) for reason "${plan.reason}" -> status=succeeded (receipt=${finalized.receipt.id}).`;
953
+ const supermemoryByok = this.store.getByokKey(ctx.workspace.id, "supermemory");
735
954
  this.supermemory
736
955
  .recordExecutionMemory({
737
956
  workspaceId: ctx.workspace.id,
@@ -741,6 +960,7 @@ export class MuxoEngine {
741
960
  reason: plan.reason,
742
961
  status: "succeeded",
743
962
  summary,
963
+ apiKeyOverride: supermemoryByok,
744
964
  })
745
965
  .then((docId) => {
746
966
  if (docId) {
@@ -765,6 +985,7 @@ export class MuxoEngine {
765
985
  }
766
986
  catch (err) {
767
987
  const durationMs = Date.now() - startedMs;
988
+ this.governance.recordAgentOutcome(ctx.workspace.id, false, 0.08);
768
989
  const errorMessage = String(err?.message || err || "Execution failed");
769
990
  return this.store.finalizeRun({
770
991
  workspaceId: ctx.workspace.id,
@@ -787,7 +1008,7 @@ export class MuxoEngine {
787
1008
  return run;
788
1009
  }
789
1010
  // 17. cancel_run
790
- cancelRun(ctx, runId) {
1011
+ cancelRun(ctx, runId, reason) {
791
1012
  const run = this.getRun(ctx, runId);
792
1013
  if (run.status === "succeeded" || run.status === "failed" || run.status === "cancelled") {
793
1014
  return {
@@ -799,8 +1020,8 @@ export class MuxoEngine {
799
1020
  workspaceId: ctx.workspace.id,
800
1021
  runId: run.id,
801
1022
  status: "cancelled",
802
- output: { cancelledBy: ctx.actorId },
803
- error: "Cancelled by operator",
1023
+ output: { cancelledBy: ctx.actorId, ...(reason ? { reason } : {}) },
1024
+ error: reason || "Cancelled by operator",
804
1025
  actionsMetered: 0,
805
1026
  creditsMetered: 0,
806
1027
  durationMs: Date.now() - new Date(run.startedAt).getTime(),
@@ -865,6 +1086,7 @@ export class MuxoEngine {
865
1086
  toolSlug: resolution.tool.slug,
866
1087
  input: resolution.input,
867
1088
  reason: params.prompt,
1089
+ fundingMode: params.fundingMode,
868
1090
  });
869
1091
  if (planned.authHandoff?.required) {
870
1092
  return {
package/dist/mcp.js CHANGED
@@ -2,6 +2,7 @@ import crypto from "node:crypto";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { ROOT_DIR } from "./config.js";
5
+ import { MUXO_VERSION } from "./version.js";
5
6
  const DEFAULT_TOOL_SECURITY_SCHEMES = [
6
7
  { type: "noauth" },
7
8
  {
@@ -813,7 +814,7 @@ export async function handleJsonRpcMcpRequest(engine, ctx, rpc) {
813
814
  instructions: MUXO_MCP_SERVER_INSTRUCTIONS,
814
815
  serverInfo: {
815
816
  name: "muxo-mcp-server",
816
- version: "1.3.1",
817
+ version: MUXO_VERSION,
817
818
  },
818
819
  capabilities: {
819
820
  tools: { listChanged: false },
@@ -0,0 +1,9 @@
1
+ import type { GovernancePolicy } from "./types.js";
2
+ export declare class PolicyValidationError extends Error {
3
+ }
4
+ /**
5
+ * Validates a client-supplied policy patch. Kill-switch fields are rejected so
6
+ * that pausing and resuming always goes through /api/v1/governance/kill-switch,
7
+ * which records the trust-score penalty and audit entry.
8
+ */
9
+ export declare function sanitizePolicyPatch(body: unknown): Partial<GovernancePolicy>;
package/dist/policy.js ADDED
@@ -0,0 +1,73 @@
1
+ const RISK_LEVELS = [
2
+ "read",
3
+ "write",
4
+ "admin",
5
+ "financial",
6
+ "provisioning",
7
+ "destructive",
8
+ ];
9
+ export class PolicyValidationError extends Error {
10
+ }
11
+ /**
12
+ * Validates a client-supplied policy patch. Kill-switch fields are rejected so
13
+ * that pausing and resuming always goes through /api/v1/governance/kill-switch,
14
+ * which records the trust-score penalty and audit entry.
15
+ */
16
+ export function sanitizePolicyPatch(body) {
17
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
18
+ throw new PolicyValidationError("Policy update must be a JSON object");
19
+ }
20
+ const input = body;
21
+ if ("killSwitchActive" in input || "killSwitchReason" in input) {
22
+ throw new PolicyValidationError("Use POST /api/v1/governance/kill-switch to pause or resume agents");
23
+ }
24
+ const patch = {};
25
+ if (input.mode !== undefined) {
26
+ if (input.mode !== "monitor" && input.mode !== "enforce") {
27
+ throw new PolicyValidationError('mode must be "monitor" or "enforce"');
28
+ }
29
+ patch.mode = input.mode;
30
+ }
31
+ if (input.requireApprovalFor !== undefined) {
32
+ if (!Array.isArray(input.requireApprovalFor) ||
33
+ !input.requireApprovalFor.every((r) => RISK_LEVELS.includes(r))) {
34
+ throw new PolicyValidationError(`requireApprovalFor must be a list of: ${RISK_LEVELS.join(", ")}`);
35
+ }
36
+ patch.requireApprovalFor = [...new Set(input.requireApprovalFor)];
37
+ }
38
+ if (input.executionRing !== undefined) {
39
+ const ring = Number(input.executionRing);
40
+ if (![0, 1, 2, 3].includes(ring)) {
41
+ throw new PolicyValidationError("executionRing must be 0, 1, 2, or 3");
42
+ }
43
+ patch.executionRing = ring;
44
+ }
45
+ if (input.maxCreditsPerRun !== undefined) {
46
+ const max = Number(input.maxCreditsPerRun);
47
+ if (!Number.isFinite(max) || max < 0) {
48
+ throw new PolicyValidationError("maxCreditsPerRun must be a non-negative number");
49
+ }
50
+ patch.maxCreditsPerRun = max;
51
+ }
52
+ for (const key of ["allowInstantWallet", "allowProvisioning", "blockDestructiveSql"]) {
53
+ if (input[key] !== undefined) {
54
+ if (typeof input[key] !== "boolean") {
55
+ throw new PolicyValidationError(`${key} must be true or false`);
56
+ }
57
+ patch[key] = input[key];
58
+ }
59
+ }
60
+ if (input.blockedToolSlugs !== undefined) {
61
+ if (!Array.isArray(input.blockedToolSlugs) ||
62
+ !input.blockedToolSlugs.every((s) => typeof s === "string" && s.trim())) {
63
+ throw new PolicyValidationError("blockedToolSlugs must be a list of tool slugs");
64
+ }
65
+ patch.blockedToolSlugs = [
66
+ ...new Set(input.blockedToolSlugs.map((s) => s.trim())),
67
+ ];
68
+ }
69
+ if (Object.keys(patch).length === 0) {
70
+ throw new PolicyValidationError("No supported policy fields in request");
71
+ }
72
+ return patch;
73
+ }
@@ -11,5 +11,8 @@ export declare class ComposioInstantAdapter {
11
11
  tool: MuxoTool;
12
12
  input: Record<string, unknown>;
13
13
  composioApiKey?: string | null;
14
+ exaApiKey?: string | null;
15
+ firecrawlApiKey?: string | null;
16
+ masterWalletDepleted?: boolean;
14
17
  }): Promise<Record<string, unknown>>;
15
18
  }