@muxoai/platform 1.2.2 → 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";
@@ -26,6 +27,9 @@ export class MuxoEngine {
26
27
  // 1. get_profile
27
28
  getProfile(ctx) {
28
29
  return {
30
+ id: ctx.user.id,
31
+ name: ctx.user.name,
32
+ email: ctx.user.email,
29
33
  user: ctx.user,
30
34
  actorId: ctx.actorId,
31
35
  authType: ctx.authType,
@@ -76,6 +80,14 @@ export class MuxoEngine {
76
80
  }
77
81
  if (lane === "all" || lane === "stripe_projects") {
78
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);
79
91
  }
80
92
  const filtered = params.riskLevel
81
93
  ? results.filter((t) => t.riskLevel === params.riskLevel)
@@ -85,9 +97,22 @@ export class MuxoEngine {
85
97
  total: filtered.length,
86
98
  };
87
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
+ }
88
107
  // 4. inspect_tool
89
108
  async inspectTool(slug) {
90
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;
91
116
  const instant = this.composioInstant.inspectTool(normalized);
92
117
  if (instant)
93
118
  return instant;
@@ -343,15 +368,40 @@ export class MuxoEngine {
343
368
  // 7. plan_action
344
369
  async planAction(ctx, params) {
345
370
  const ws = this.store.getWorkspace(ctx.workspace.id);
346
- 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;
347
397
  if (ws.monthlyActionsUsed >= ws.monthlyActionsLimit) {
348
398
  throw new Error(`Monthly action limit reached (${ws.monthlyActionsUsed}/${ws.monthlyActionsLimit}). Upgrade to Starter ($29/mo) to unlock 5,000 actions/mo.`);
349
399
  }
350
- if (tool.creditCost > ws.creditsBalance) {
351
- 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.`);
352
402
  }
353
- if (tool.creditCost > ws.policy.maxCreditsPerRun) {
354
- 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}).`);
355
405
  }
356
406
  // 1. Evaluate Microsoft Agent Governance Pipeline:
357
407
  // Agent -> McpSecurityScanner -> PolicyEngine -> AgentIdentity (Ed25519 DID) -> AuditLogger -> DecisionRecord
@@ -359,14 +409,14 @@ export class MuxoEngine {
359
409
  workspace: ws,
360
410
  actorId: ctx.actorId,
361
411
  tool,
362
- input: params.input || {},
363
- reason: params.reason || `Planned execution of ${tool.slug}`,
412
+ input: resolvedInput,
413
+ reason: resolvedReason,
364
414
  });
365
415
  if (decisionRecord.verdict === "deny") {
366
416
  throw new Error(`Microsoft Agent Governance denied plan for ${tool.slug} (rule: ${decisionRecord.matchedRule}): ${decisionRecord.reason}`);
367
417
  }
368
418
  // 2. Recall Supermemory v5 contextual brain for this workspace + tool intent
369
- const memoryQuery = `${tool.slug} ${tool.name} ${params.reason || ""}`.trim();
419
+ const memoryQuery = `${tool.slug} ${tool.name} ${resolvedReason}`.trim();
370
420
  const memoryContext = await this.supermemory.getContextForWorkspace(ws.id, memoryQuery);
371
421
  const requiresHumanApproval = decisionRecord.verdict === "escalate" ||
372
422
  (ws.policy.mode === "enforce" &&
@@ -381,9 +431,9 @@ export class MuxoEngine {
381
431
  upstreamSlug: tool.upstreamSlug,
382
432
  lane: tool.lane,
383
433
  riskLevel: tool.riskLevel,
384
- input: params.input || {},
385
- reason: params.reason || `Planned execution of ${tool.slug}`,
386
- creditCost: tool.creditCost,
434
+ input: resolvedInput,
435
+ reason: resolvedReason,
436
+ creditCost: effectiveCreditCost,
387
437
  approvalStatus,
388
438
  decisionRecord,
389
439
  memoryContext,
@@ -555,11 +605,40 @@ export class MuxoEngine {
555
605
  if (execDecisionRecord.verdict === "deny") {
556
606
  throw new Error(`Microsoft Agent Governance denied execution of plan '${plan.id}': ${execDecisionRecord.reason}`);
557
607
  }
608
+ let activeConnId = null;
609
+ if (plan.lane === "composio_sdk" && tool.requiresAuth) {
610
+ let connections = this.store.listConnections(ctx.workspace.id);
611
+ let activeConn = connections.find((c) => c.lane === "composio_sdk" &&
612
+ c.providerSlug === tool.providerSlug &&
613
+ c.status === "active");
614
+ if (!activeConn) {
615
+ await this.waitForConnection(ctx, {
616
+ provider: tool.providerSlug,
617
+ timeoutSeconds: 0,
618
+ }).catch(() => { });
619
+ connections = this.store.listConnections(ctx.workspace.id);
620
+ activeConn = connections.find((c) => c.lane === "composio_sdk" &&
621
+ c.providerSlug === tool.providerSlug &&
622
+ c.status === "active");
623
+ }
624
+ if (!activeConn) {
625
+ throw new Error(`No active OAuth connection for ${tool.providerSlug}. Complete OAuth handoff and call wait_for_connection({ provider: "${tool.providerSlug}" }) before executing plan '${plan.id}'.`);
626
+ }
627
+ activeConnId = activeConn.externalAccountId;
628
+ }
558
629
  const run = this.store.createRun(plan, ctx.actorId);
559
630
  const startedMs = Date.now();
560
631
  try {
561
632
  let output;
562
- 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") {
563
642
  output = await this.composioInstant.executeTool({
564
643
  workspaceId: ctx.workspace.id,
565
644
  tool,
@@ -567,25 +646,11 @@ export class MuxoEngine {
567
646
  });
568
647
  }
569
648
  else if (plan.lane === "composio_sdk") {
570
- let connections = this.store.listConnections(ctx.workspace.id);
571
- let activeConn = connections.find((c) => c.lane === "composio_sdk" &&
572
- c.providerSlug === tool.providerSlug &&
573
- c.status === "active");
574
- if (!activeConn) {
575
- await this.waitForConnection(ctx, {
576
- provider: tool.providerSlug,
577
- timeoutSeconds: 0,
578
- }).catch(() => { });
579
- connections = this.store.listConnections(ctx.workspace.id);
580
- activeConn = connections.find((c) => c.lane === "composio_sdk" &&
581
- c.providerSlug === tool.providerSlug &&
582
- c.status === "active");
583
- }
584
649
  output = await this.composioSdk.executeTool({
585
650
  workspaceId: ctx.workspace.id,
586
651
  tool,
587
652
  input: plan.input,
588
- connectedAccountId: activeConn?.externalAccountId || null,
653
+ connectedAccountId: activeConnId,
589
654
  });
590
655
  }
591
656
  else if (plan.lane === "stripe_projects") {
@@ -706,4 +771,106 @@ export class MuxoEngine {
706
771
  : true;
707
772
  return { receipt, signatureValid, governanceSignatureValid };
708
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
+ }
709
876
  }
package/dist/mcp.d.ts CHANGED
@@ -1,9 +1,45 @@
1
1
  import type { ExecutionContext, MuxoEngine } from "./engine.js";
2
+ export interface McpToolAnnotations {
3
+ readOnlyHint: boolean;
4
+ destructiveHint: boolean;
5
+ openWorldHint: boolean;
6
+ }
2
7
  export interface McpToolDefinition {
3
8
  name: string;
9
+ title: string;
4
10
  description: string;
5
11
  inputSchema: Record<string, unknown>;
12
+ outputSchema?: Record<string, unknown>;
13
+ annotations: McpToolAnnotations;
14
+ _meta?: Record<string, unknown>;
6
15
  }
16
+ /**
17
+ * Concise server instructions (<512 chars) per OpenAI Plugin MCP Server specification:
18
+ * https://developers.openai.com/plugins/build/mcp-server
19
+ */
20
+ export declare const MUXO_MCP_SERVER_INSTRUCTIONS = "Muxo governs SaaS actions (Composio app.*), Instant tools (instant.*), Cloud Infrastructure (Stripe Projects resource.*), Supermemory v5 recall, and Microsoft Agent Governance Ed25519 receipts. Workflow: 1) search_tools & inspect_tool. 2) plan_action to get a plan_*. 3) If authHandoff.required is true, show redirectUrl and call wait_for_connection. 4) Execute via the matching risk tool (execute_read/write/admin/financial/provisioning/destructive). 5) Verify via get_receipt.";
7
21
  export declare const MUXO_MCP_TOOLS: McpToolDefinition[];
22
+ /**
23
+ * OpenAI Company Knowledge standard `search` and `fetch` tool definitions
24
+ * per https://developers.openai.com/plugins/build/mcp-server#company-knowledge-compatibility
25
+ */
26
+ export declare const OPENAI_COMPANY_KNOWLEDGE_TOOLS: McpToolDefinition[];
27
+ export interface McpSkillFileEntry {
28
+ path: string;
29
+ uri: string;
30
+ size: number;
31
+ digest: string;
32
+ mimeType: string;
33
+ content: string;
34
+ }
35
+ export interface McpSkillBundle {
36
+ name: string;
37
+ description: string;
38
+ uri: string;
39
+ version: string;
40
+ frontmatter: Record<string, string>;
41
+ files: McpSkillFileEntry[];
42
+ }
43
+ export declare function getMuxoMcpSkills(): McpSkillBundle[];
8
44
  export declare function dispatchMcpToolCall(engine: MuxoEngine, ctx: ExecutionContext, toolName: string, args?: Record<string, unknown>): Promise<unknown>;
9
45
  export declare function handleJsonRpcMcpRequest(engine: MuxoEngine, ctx: ExecutionContext, rpc: Record<string, unknown>): Promise<Record<string, unknown> | null>;