@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/.agents/plugins/marketplace.json +22 -0
- package/.claude-plugin/marketplace.json +17 -0
- package/.codex/config.toml +5 -0
- package/README.md +57 -28
- package/dist/cli.js +328 -10
- package/dist/engine.d.ts +28 -0
- package/dist/engine.js +194 -27
- package/dist/mcp.d.ts +36 -0
- package/dist/mcp.js +534 -21
- package/dist/nl.d.ts +34 -0
- package/dist/nl.js +577 -0
- package/dist/providers/composio-instant.js +442 -0
- package/dist/providers/composio-sdk.js +88 -30
- package/dist/providers/stripe-billing.d.ts +13 -3
- package/dist/providers/stripe-billing.js +240 -36
- package/dist/providers/stripe-projects.js +14 -3
- package/dist/sdk.d.ts +84 -1
- package/dist/sdk.js +53 -0
- package/dist/server.js +476 -15
- package/dist/store.d.ts +18 -2
- package/dist/store.js +197 -8
- package/dist/types.d.ts +14 -2
- package/dist/ui/console-html.js +216 -26
- package/package.json +4 -1
- package/plugins/muxo/.claude-plugin/plugin.json +2 -2
- package/plugins/muxo/.codex-plugin/plugin.json +12 -8
- package/plugins/muxo/.mcp.json +33 -1
- package/plugins/muxo/mcp.json +38 -0
- package/plugins/muxo/plugin.json +59 -0
- package/plugins/muxo/skills/setup/SKILL.md +28 -0
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
|
-
|
|
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 (
|
|
351
|
-
throw new Error(`Insufficient workspace credits for ${tool.slug} (requires ${
|
|
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 (
|
|
354
|
-
throw new Error(`Tool credit cost (${
|
|
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:
|
|
363
|
-
reason:
|
|
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} ${
|
|
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:
|
|
385
|
-
reason:
|
|
386
|
-
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.
|
|
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:
|
|
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>;
|