@muxoai/platform 1.2.3 → 1.3.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.
package/dist/engine.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { resolveNaturalLanguageIntent, } from "./nl.js";
1
2
  import { ComposioInstantAdapter } from "./providers/composio-instant.js";
2
3
  import { ComposioSdkAdapter } from "./providers/composio-sdk.js";
3
4
  import { MicrosoftGovernanceAdapter } from "./providers/microsoft-governance.js";
@@ -79,6 +80,14 @@ export class MuxoEngine {
79
80
  }
80
81
  if (lane === "all" || lane === "stripe_projects") {
81
82
  results.push(...this.stripeProjects.searchTools(query, limit));
83
+ const billingTools = this.stripeBilling.listTools().filter((t) => {
84
+ if (!query)
85
+ return true;
86
+ const q = query.toLowerCase();
87
+ const hay = `${t.slug} ${t.name} ${t.description} ${t.categories.join(" ")}`.toLowerCase();
88
+ return q.split(/\s+/).some((tok) => hay.includes(tok));
89
+ });
90
+ results.push(...billingTools);
82
91
  }
83
92
  const filtered = params.riskLevel
84
93
  ? results.filter((t) => t.riskLevel === params.riskLevel)
@@ -88,9 +97,22 @@ export class MuxoEngine {
88
97
  total: filtered.length,
89
98
  };
90
99
  }
100
+ async listAllCatalogTools() {
101
+ const instant = this.composioInstant.searchTools(undefined, 100);
102
+ const sdk = await this.composioSdk.searchTools(undefined, 100);
103
+ const proj = this.stripeProjects.searchTools(undefined, 100);
104
+ const billing = this.stripeBilling.listTools();
105
+ return [...instant, ...billing, ...sdk, ...proj];
106
+ }
91
107
  // 4. inspect_tool
92
108
  async inspectTool(slug) {
93
109
  const normalized = slug.trim();
110
+ const billing = this.stripeBilling
111
+ .listTools()
112
+ .find((t) => t.slug.toLowerCase() === normalized.toLowerCase() ||
113
+ t.upstreamSlug.toLowerCase() === normalized.toLowerCase());
114
+ if (billing)
115
+ return billing;
94
116
  const instant = this.composioInstant.inspectTool(normalized);
95
117
  if (instant)
96
118
  return instant;
@@ -346,15 +368,40 @@ export class MuxoEngine {
346
368
  // 7. plan_action
347
369
  async planAction(ctx, params) {
348
370
  const ws = this.store.getWorkspace(ctx.workspace.id);
349
- const tool = await this.inspectTool(params.toolSlug);
371
+ let tool;
372
+ let resolvedInput = { ...(params.input || {}) };
373
+ let resolvedReason = params.reason || `Planned execution of ${params.toolSlug}`;
374
+ try {
375
+ tool = await this.inspectTool(params.toolSlug);
376
+ }
377
+ catch (inspectErr) {
378
+ // Natural-language fallback: if an agent or human passed plain English as toolSlug/instruction, resolve it!
379
+ const nlSource = `${params.toolSlug} ${params.reason || ""}`.trim();
380
+ const allTools = await this.listAllCatalogTools();
381
+ const byokEnv = this.store.getDecryptedWorkspaceEnv(ws.id);
382
+ const nl = await resolveNaturalLanguageIntent({
383
+ prompt: nlSource,
384
+ tools: allTools,
385
+ explicitInput: resolvedInput,
386
+ byokKeys: byokEnv,
387
+ });
388
+ if (!nl.resolved || !nl.tool) {
389
+ throw inspectErr;
390
+ }
391
+ tool = nl.tool;
392
+ resolvedInput = nl.input;
393
+ resolvedReason = params.reason || nl.reason || nlSource;
394
+ }
395
+ const byokActive = this.store.hasByokForLane(ws.id, tool.lane, tool.slug);
396
+ const effectiveCreditCost = byokActive ? 0 : tool.creditCost;
350
397
  if (ws.monthlyActionsUsed >= ws.monthlyActionsLimit) {
351
398
  throw new Error(`Monthly action limit reached (${ws.monthlyActionsUsed}/${ws.monthlyActionsLimit}). Upgrade to Starter ($29/mo) to unlock 5,000 actions/mo.`);
352
399
  }
353
- if (tool.creditCost > ws.creditsBalance) {
354
- throw new Error(`Insufficient workspace credits for ${tool.slug} (requires ${tool.creditCost}, available ${ws.creditsBalance}). Top up credits or upgrade plan.`);
400
+ if (effectiveCreditCost > ws.creditsBalance) {
401
+ throw new Error(`Insufficient workspace credits for ${tool.slug} (requires ${effectiveCreditCost}, available ${ws.creditsBalance}). Top up credits or configure a BYOK key.`);
355
402
  }
356
- if (tool.creditCost > ws.policy.maxCreditsPerRun) {
357
- throw new Error(`Tool credit cost (${tool.creditCost}) exceeds workspace policy maxCreditsPerRun (${ws.policy.maxCreditsPerRun}).`);
403
+ if (effectiveCreditCost > ws.policy.maxCreditsPerRun) {
404
+ throw new Error(`Tool credit cost (${effectiveCreditCost}) exceeds workspace policy maxCreditsPerRun (${ws.policy.maxCreditsPerRun}).`);
358
405
  }
359
406
  // 1. Evaluate Microsoft Agent Governance Pipeline:
360
407
  // Agent -> McpSecurityScanner -> PolicyEngine -> AgentIdentity (Ed25519 DID) -> AuditLogger -> DecisionRecord
@@ -362,14 +409,14 @@ export class MuxoEngine {
362
409
  workspace: ws,
363
410
  actorId: ctx.actorId,
364
411
  tool,
365
- input: params.input || {},
366
- reason: params.reason || `Planned execution of ${tool.slug}`,
412
+ input: resolvedInput,
413
+ reason: resolvedReason,
367
414
  });
368
415
  if (decisionRecord.verdict === "deny") {
369
416
  throw new Error(`Microsoft Agent Governance denied plan for ${tool.slug} (rule: ${decisionRecord.matchedRule}): ${decisionRecord.reason}`);
370
417
  }
371
418
  // 2. Recall Supermemory v5 contextual brain for this workspace + tool intent
372
- const memoryQuery = `${tool.slug} ${tool.name} ${params.reason || ""}`.trim();
419
+ const memoryQuery = `${tool.slug} ${tool.name} ${resolvedReason}`.trim();
373
420
  const memoryContext = await this.supermemory.getContextForWorkspace(ws.id, memoryQuery);
374
421
  const requiresHumanApproval = decisionRecord.verdict === "escalate" ||
375
422
  (ws.policy.mode === "enforce" &&
@@ -384,9 +431,9 @@ export class MuxoEngine {
384
431
  upstreamSlug: tool.upstreamSlug,
385
432
  lane: tool.lane,
386
433
  riskLevel: tool.riskLevel,
387
- input: params.input || {},
388
- reason: params.reason || `Planned execution of ${tool.slug}`,
389
- creditCost: tool.creditCost,
434
+ input: resolvedInput,
435
+ reason: resolvedReason,
436
+ creditCost: effectiveCreditCost,
390
437
  approvalStatus,
391
438
  decisionRecord,
392
439
  memoryContext,
@@ -583,7 +630,15 @@ export class MuxoEngine {
583
630
  const startedMs = Date.now();
584
631
  try {
585
632
  let output;
586
- if (plan.lane === "composio_instant") {
633
+ if (plan.toolSlug.startsWith("billing.")) {
634
+ output = await this.stripeBilling.executeTool({
635
+ workspace: ws,
636
+ customerEmail: ctx.user.email,
637
+ toolSlug: plan.toolSlug,
638
+ input: plan.input,
639
+ });
640
+ }
641
+ else if (plan.lane === "composio_instant") {
587
642
  output = await this.composioInstant.executeTool({
588
643
  workspaceId: ctx.workspace.id,
589
644
  tool,
@@ -716,4 +771,106 @@ export class MuxoEngine {
716
771
  : true;
717
772
  return { receipt, signatureValid, governanceSignatureValid };
718
773
  }
774
+ // 19. Natural Language Router & End-to-End Executor (`muxo do` / `POST /api/v1/do`)
775
+ async doNaturalLanguage(ctx, params) {
776
+ const allTools = await this.listAllCatalogTools();
777
+ const byokEnv = this.store.getDecryptedWorkspaceEnv(ctx.workspace.id);
778
+ const activeConns = new Set(this.store
779
+ .listConnections(ctx.workspace.id)
780
+ .filter((c) => c.status === "active")
781
+ .map((c) => c.providerSlug.toLowerCase()));
782
+ const resolution = await resolveNaturalLanguageIntent({
783
+ prompt: params.prompt,
784
+ tools: allTools,
785
+ explicitInput: params.input,
786
+ byokKeys: byokEnv,
787
+ activeConnectionProviders: activeConns,
788
+ });
789
+ if (!resolution.resolved || !resolution.tool) {
790
+ return {
791
+ ok: false,
792
+ executed: false,
793
+ status: "unresolved",
794
+ intent: resolution,
795
+ resolution,
796
+ byokPrompt: {
797
+ needed: !byokEnv.OPENAI_API_KEY && !byokEnv.ANTHROPIC_API_KEY && !byokEnv.OPENROUTER_API_KEY,
798
+ provider: "openai",
799
+ envVarName: "OPENAI_API_KEY",
800
+ cliCommand: "muxo byok set openai <sk-...>",
801
+ message: "Tip: Add an OpenAI, Anthropic, or OpenRouter BYOK key (`muxo byok set openai <key>`) for unrestricted natural-language LLM routing.",
802
+ },
803
+ humanMessage: resolution.humanSummary,
804
+ };
805
+ }
806
+ if (resolution.missingInputs.length > 0) {
807
+ return {
808
+ ok: false,
809
+ executed: false,
810
+ status: "missing_inputs",
811
+ intent: resolution,
812
+ resolution,
813
+ humanMessage: `Matched ${resolution.tool.name} (${resolution.tool.slug}), but missing required parameter(s): ${resolution.missingInputs.join(", ")}. Please include them in your request.`,
814
+ };
815
+ }
816
+ const planned = await this.planAction(ctx, {
817
+ toolSlug: resolution.tool.slug,
818
+ input: resolution.input,
819
+ reason: params.prompt,
820
+ });
821
+ if (planned.authHandoff?.required) {
822
+ return {
823
+ ok: false,
824
+ executed: false,
825
+ status: "awaiting_oauth_connection",
826
+ intent: resolution,
827
+ resolution,
828
+ plan: planned.plan,
829
+ authHandoff: planned.authHandoff,
830
+ humanMessage: planned.authHandoff.instructions,
831
+ };
832
+ }
833
+ if (params.dryRun || params.planOnly) {
834
+ return {
835
+ ok: true,
836
+ executed: false,
837
+ status: "planned_dry_run",
838
+ intent: resolution,
839
+ resolution,
840
+ plan: planned.plan,
841
+ humanMessage: `Planned ${planned.tool.name} (${planned.plan.id}) with risk=${planned.plan.riskLevel} and cost=${planned.plan.creditCost} credits.`,
842
+ };
843
+ }
844
+ let currentPlan = planned.plan;
845
+ if (currentPlan.approvalStatus === "pending_approval" &&
846
+ params.autoApprove !== false) {
847
+ currentPlan = this.approvePlan(ctx, currentPlan.id, "approved");
848
+ }
849
+ if (currentPlan.approvalStatus === "pending_approval") {
850
+ return {
851
+ ok: true,
852
+ executed: false,
853
+ status: "awaiting_approval",
854
+ intent: resolution,
855
+ resolution,
856
+ plan: currentPlan,
857
+ humanMessage: `Plan ${currentPlan.id} (${currentPlan.toolSlug}, risk=${currentPlan.riskLevel}) requires owner approval. Run 'muxo approve ${currentPlan.id}' or pass --yes to auto-approve.`,
858
+ };
859
+ }
860
+ const executed = await this.executePlanForRisk(ctx, currentPlan.id, currentPlan.riskLevel);
861
+ const isSuccess = executed.run.status === "succeeded";
862
+ return {
863
+ ok: isSuccess,
864
+ executed: isSuccess,
865
+ status: "executed",
866
+ intent: resolution,
867
+ resolution,
868
+ plan: currentPlan,
869
+ run: executed.run,
870
+ receipt: executed.receipt,
871
+ humanMessage: isSuccess
872
+ ? `Completed ${resolution.tool.name} (${resolution.tool.slug}) in ${executed.run.durationMs ?? 0}ms [receipt: ${executed.receipt.id}, funding: ${executed.receipt.supplierSettlement?.fundingMode || "muxo_funded"}].`
873
+ : `Execution of ${resolution.tool.slug} failed: ${executed.run.error || "Unknown error"}`,
874
+ };
875
+ }
719
876
  }
package/dist/mcp.js CHANGED
@@ -195,15 +195,18 @@ export const MUXO_MCP_TOOLS = [
195
195
  {
196
196
  name: "plan_action",
197
197
  title: "Create Governed Action Plan",
198
- description: "Create a governed execution plan (plan_*) for any tool or infrastructure provisioning operation. Evaluates workspace policy, risk level, Supermemory v5 recall, Ed25519 signature, and credit balance.",
198
+ description: "Create a governed execution plan (plan_*) for any tool, billing operation, or infrastructure provisioning operation. Accepts either an exact tool_slug (e.g. 'instant.seo.audit', 'billing.credits.topup', 'resource.turso.database') or a natural-language instruction (e.g. 'audit seo for https://muxo.ai'). Evaluates workspace policy, risk level, Supermemory v5 recall, Ed25519 signature, BYOK vs Muxo-funded mode, and credit balance.",
199
199
  inputSchema: {
200
200
  type: "object",
201
201
  required: ["tool_slug"],
202
202
  properties: {
203
- tool_slug: { type: "string", description: "Muxo tool slug to plan" },
203
+ tool_slug: {
204
+ type: "string",
205
+ description: "Muxo tool slug (e.g. 'instant.seo.audit', 'billing.credits.topup') or natural-language request",
206
+ },
204
207
  input: {
205
208
  type: "object",
206
- description: "Arguments matching the tool's inputSchema",
209
+ description: "Arguments matching the tool's inputSchema (auto-extracted when tool_slug is natural language)",
207
210
  },
208
211
  reason: {
209
212
  type: "string",
@@ -627,8 +630,13 @@ export async function dispatchMcpToolCall(engine, ctx, toolName, args = {}) {
627
630
  });
628
631
  case "plan_action":
629
632
  return engine.planAction(ctx, {
630
- toolSlug: String(args.tool_slug || args.slug || ""),
631
- input: args.input || {},
633
+ toolSlug: String(args.tool_slug ||
634
+ args.slug ||
635
+ args.tool_id ||
636
+ args.prompt ||
637
+ args.instruction ||
638
+ ""),
639
+ input: (args.input || args.arguments) || {},
632
640
  reason: args.reason ? String(args.reason) : undefined,
633
641
  });
634
642
  case "get_plan":
@@ -738,7 +746,7 @@ export async function handleJsonRpcMcpRequest(engine, ctx, rpc) {
738
746
  instructions: MUXO_MCP_SERVER_INSTRUCTIONS,
739
747
  serverInfo: {
740
748
  name: "muxo-mcp-server",
741
- version: "1.2.3",
749
+ version: "1.3.0",
742
750
  },
743
751
  capabilities: {
744
752
  tools: { listChanged: false },
package/dist/nl.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ import type { ToolDescriptor } from "./types.js";
2
+ export interface NaturalLanguageResolution {
3
+ resolved: boolean;
4
+ tool: ToolDescriptor | null;
5
+ toolSlug: string | null;
6
+ input: Record<string, unknown>;
7
+ extractedInput: Record<string, unknown>;
8
+ reason: string;
9
+ confidence: number;
10
+ resolutionMethod: "exact_slug" | "byok_llm" | "semantic_router";
11
+ byokUsed: string | null;
12
+ humanSummary: string;
13
+ missingInputs: string[];
14
+ needsConnection: boolean;
15
+ suggestedByokProvider: string | null;
16
+ alternatives: Array<{
17
+ slug: string;
18
+ name: string;
19
+ riskLevel: string;
20
+ creditCost: number;
21
+ }>;
22
+ }
23
+ export declare function extractParametersFromNaturalLanguage(prompt: string, tool: ToolDescriptor, explicitInput?: Record<string, unknown>): {
24
+ input: Record<string, unknown>;
25
+ missingInputs: string[];
26
+ };
27
+ export declare function resolveNaturalLanguageIntent(params: {
28
+ prompt: string;
29
+ tools: ToolDescriptor[];
30
+ explicitInput?: Record<string, unknown>;
31
+ byokKeys?: Record<string, string>;
32
+ activeConnectionProviders?: Set<string>;
33
+ preferByokLlm?: boolean;
34
+ }): Promise<NaturalLanguageResolution>;