@muxoai/platform 1.3.0 → 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.
Files changed (51) hide show
  1. package/.agents/plugins/marketplace.json +1 -1
  2. package/.claude-plugin/marketplace.json +19 -4
  3. package/.codex/config.toml +9 -2
  4. package/README.md +63 -20
  5. package/dist/clerk.d.ts +33 -0
  6. package/dist/clerk.js +198 -0
  7. package/dist/cli.js +1181 -99
  8. package/dist/engine.d.ts +22 -1
  9. package/dist/engine.js +284 -14
  10. package/dist/mcp.d.ts +6 -1
  11. package/dist/mcp.js +109 -22
  12. package/dist/nl.js +60 -36
  13. package/dist/policy.d.ts +9 -0
  14. package/dist/policy.js +73 -0
  15. package/dist/providers/composio-instant.d.ts +4 -0
  16. package/dist/providers/composio-instant.js +634 -534
  17. package/dist/providers/composio-sdk.d.ts +1 -0
  18. package/dist/providers/composio-sdk.js +6 -2
  19. package/dist/providers/microsoft-governance.d.ts +61 -2
  20. package/dist/providers/microsoft-governance.js +458 -24
  21. package/dist/providers/stripe-billing.js +14 -4
  22. package/dist/providers/stripe-projects.d.ts +4 -1
  23. package/dist/providers/stripe-projects.js +123 -12
  24. package/dist/providers/supermemory.d.ts +28 -5
  25. package/dist/providers/supermemory.js +183 -55
  26. package/dist/sdk.d.ts +167 -1
  27. package/dist/sdk.js +113 -2
  28. package/dist/server.js +258 -40
  29. package/dist/store.d.ts +26 -1
  30. package/dist/store.js +255 -6
  31. package/dist/types.d.ts +86 -0
  32. package/dist/ui/console-html.d.ts +16 -0
  33. package/dist/ui/console-html.js +2427 -2206
  34. package/dist/ui/design-tokens.d.ts +177 -0
  35. package/dist/ui/design-tokens.js +210 -0
  36. package/dist/version.d.ts +1 -0
  37. package/dist/version.js +1 -0
  38. package/package.json +2 -2
  39. package/plugins/muxo/.app.json +1 -1
  40. package/plugins/muxo/.claude-plugin/plugin.json +2 -2
  41. package/plugins/muxo/.codex-plugin/plugin.json +7 -5
  42. package/plugins/muxo/.mcp.json +1 -33
  43. package/plugins/muxo/assets/logo.png +0 -0
  44. package/plugins/muxo/hooks/session-start.sh +1 -1
  45. package/plugins/muxo/plugin.json +63 -6
  46. package/plugins/muxo/skills/muxo/SKILL.md +92 -14
  47. package/plugins/muxo/skills/muxo/agents/openai.yaml +12 -0
  48. package/plugins/muxo/skills/muxo/references/cli.md +4 -2
  49. package/plugins/muxo/skills/muxo/references/mcp.md +15 -9
  50. package/plugins/muxo/skills/setup/SKILL.md +8 -2
  51. package/plugins/muxo/skills/setup/agents/openai.yaml +12 -0
package/dist/mcp.js CHANGED
@@ -2,11 +2,19 @@ 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";
6
+ const DEFAULT_TOOL_SECURITY_SCHEMES = [
7
+ { type: "noauth" },
8
+ {
9
+ type: "oauth2",
10
+ scopes: ["muxo:mcp", "muxo:read", "muxo:execute", "muxo:provision"],
11
+ },
12
+ ];
5
13
  /**
6
14
  * Concise server instructions (<512 chars) per OpenAI Plugin MCP Server specification:
7
15
  * https://developers.openai.com/plugins/build/mcp-server
8
16
  */
9
- export 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.";
17
+ export const MUXO_MCP_SERVER_INSTRUCTIONS = "Muxo governs SaaS actions (Connected Apps: app.*), Instant tools (instant.*), Cloud Infrastructure (resource.*), Workspace Memory recall, and verifiable audit records. 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.";
10
18
  export const MUXO_MCP_TOOLS = [
11
19
  {
12
20
  name: "get_profile",
@@ -18,17 +26,59 @@ export const MUXO_MCP_TOOLS = [
18
26
  additionalProperties: false,
19
27
  },
20
28
  outputSchema: {
29
+ $schema: "https://json-schema.org/draft/2020-12/schema",
21
30
  type: "object",
22
31
  properties: {
23
- id: { type: "string", description: "Unique user identifier" },
24
- name: { type: "string", description: "Display name" },
25
- email: { type: "string", description: "Email address" },
26
- workspaceId: { type: "string", description: "Active workspace ID" },
27
- workspaceName: { type: "string", description: "Active workspace name" },
28
- tier: { type: "string", description: "Workspace subscription tier" },
32
+ id: {
33
+ type: "string",
34
+ minLength: 1,
35
+ pattern: "\\S",
36
+ description: "Unique identifier for the user.",
37
+ },
38
+ name: {
39
+ type: "string",
40
+ description: "Full name of the user.",
41
+ },
42
+ email: {
43
+ type: "string",
44
+ description: "Email address of the user.",
45
+ },
46
+ nickname: {
47
+ type: "string",
48
+ description: "Handle or username of the user.",
49
+ },
50
+ workspaceId: {
51
+ type: "string",
52
+ description: "Active workspace ID.",
53
+ },
54
+ workspaceName: {
55
+ type: "string",
56
+ description: "Active workspace name.",
57
+ },
58
+ tier: {
59
+ type: "string",
60
+ description: "Workspace subscription tier.",
61
+ },
62
+ authType: {
63
+ type: "string",
64
+ description: "Authentication type (session or api_key).",
65
+ },
66
+ actorId: {
67
+ type: "string",
68
+ description: "Actor identifier.",
69
+ },
70
+ user: {
71
+ type: "object",
72
+ description: "User profile record.",
73
+ },
29
74
  },
30
75
  required: ["id"],
76
+ additionalProperties: false,
31
77
  },
78
+ securitySchemes: [
79
+ { type: "noauth" },
80
+ { type: "oauth2", scopes: ["muxo:read"] },
81
+ ],
32
82
  annotations: {
33
83
  readOnlyHint: true,
34
84
  destructiveHint: false,
@@ -56,7 +106,7 @@ export const MUXO_MCP_TOOLS = [
56
106
  {
57
107
  name: "search_tools",
58
108
  title: "Search Unified Tool Catalog",
59
- description: "Search the unified Muxo catalog across Connected Apps (Composio SDK: app.*), Instant Tools (Composio Instant: instant.*), and Cloud Infrastructure (Stripe Projects: resource.*).",
109
+ description: "Search the unified Muxo catalog across Connected Apps (app.*), Instant Tools (instant.*), and Cloud Infrastructure (resource.*).",
60
110
  inputSchema: {
61
111
  type: "object",
62
112
  properties: {
@@ -114,7 +164,7 @@ export const MUXO_MCP_TOOLS = [
114
164
  {
115
165
  name: "connection_status",
116
166
  title: "Check Connection Status",
117
- description: "Check connection status across Composio OAuth apps, Instant wallet, and linked Stripe Projects cloud providers.",
167
+ description: "Check connection status across Connected Apps, Instant Tools, and linked Cloud Infrastructure providers.",
118
168
  inputSchema: {
119
169
  type: "object",
120
170
  properties: {
@@ -125,7 +175,7 @@ export const MUXO_MCP_TOOLS = [
125
175
  sync_live: {
126
176
  type: "boolean",
127
177
  default: true,
128
- description: "Whether to sync live status from upstream Composio and Stripe Projects",
178
+ description: "Whether to sync live connection status from upstream providers",
129
179
  },
130
180
  },
131
181
  additionalProperties: false,
@@ -139,7 +189,7 @@ export const MUXO_MCP_TOOLS = [
139
189
  {
140
190
  name: "start_connection",
141
191
  title: "Start OAuth Connection Handoff",
142
- description: "Initiate a managed OAuth connection handoff for a Connected App (Composio) or link a cloud provider (Stripe Projects).",
192
+ description: "Initiate a managed OAuth connection handoff for a Connected App or link a Cloud Infrastructure provider.",
143
193
  inputSchema: {
144
194
  type: "object",
145
195
  required: ["provider"],
@@ -195,7 +245,7 @@ export const MUXO_MCP_TOOLS = [
195
245
  {
196
246
  name: "plan_action",
197
247
  title: "Create Governed Action Plan",
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.",
248
+ 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, Workspace Memory recall, safety guardrails, BYOK vs Muxo-funded mode, and credit balance.",
199
249
  inputSchema: {
200
250
  type: "object",
201
251
  required: ["tool_slug"],
@@ -526,6 +576,8 @@ description: Guided onboarding skill for setting up the Muxo plugin in OpenAI Ch
526
576
 
527
577
  Call \`get_profile\` and \`get_organization\` to verify your workspace connection, or run \`npx -y @muxoai/cli@latest login --signup\` in a terminal.
528
578
  `);
579
+ const muxoOpenaiYaml = readSkillFileOrDefault("muxo/agents/openai.yaml", `interface:\n display_name: "Muxo"\n short_description: "Governed SaaS, Instant Web/SEO tools, Cloud Infrastructure, and Workspace Memory"\n default_prompt: "Audit https://muxo.ai for technical SEO and crawl its top pages."\n\ndependencies:\n tools:\n - type: "mcp"\n value: "muxo"\n description: "Muxo Governed Execution, Instant Web/SEO Tools, Connected Apps, and Cloud Infrastructure MCP Server"\n transport: "streamable_http"\n url: "https://api.muxo.ai/mcp"\n`);
580
+ const setupOpenaiYaml = readSkillFileOrDefault("setup/agents/openai.yaml", `interface:\n display_name: "Muxo Setup"\n short_description: "Guided onboarding and connection verification for Muxo in ChatGPT and Codex"\n default_prompt: "Check my Muxo profile and organization status."\n\ndependencies:\n tools:\n - type: "mcp"\n value: "muxo"\n description: "Muxo Governed Execution, Instant Web/SEO Tools, Connected Apps, and Cloud Infrastructure MCP Server"\n transport: "streamable_http"\n url: "https://api.muxo.ai/mcp"\n`);
529
581
  const muxoFm = parseFrontmatter(muxoSkillMd);
530
582
  const setupFm = parseFrontmatter(setupSkillMd);
531
583
  const muxoFiles = [
@@ -537,6 +589,14 @@ Call \`get_profile\` and \`get_organization\` to verify your workspace connectio
537
589
  mimeType: "text/markdown",
538
590
  content: muxoSkillMd,
539
591
  },
592
+ {
593
+ path: "agents/openai.yaml",
594
+ uri: "skill://muxo/muxo/agents/openai.yaml",
595
+ size: Buffer.byteLength(muxoOpenaiYaml, "utf8"),
596
+ digest: sha256Digest(muxoOpenaiYaml),
597
+ mimeType: "application/yaml",
598
+ content: muxoOpenaiYaml,
599
+ },
540
600
  {
541
601
  path: "references/cli.md",
542
602
  uri: "skill://muxo/muxo/references/cli.md",
@@ -563,6 +623,14 @@ Call \`get_profile\` and \`get_organization\` to verify your workspace connectio
563
623
  mimeType: "text/markdown",
564
624
  content: setupSkillMd,
565
625
  },
626
+ {
627
+ path: "agents/openai.yaml",
628
+ uri: "skill://muxo/setup/agents/openai.yaml",
629
+ size: Buffer.byteLength(setupOpenaiYaml, "utf8"),
630
+ digest: sha256Digest(setupOpenaiYaml),
631
+ mimeType: "application/yaml",
632
+ content: setupOpenaiYaml,
633
+ },
566
634
  ];
567
635
  const muxoName = muxoFm.name || "muxo";
568
636
  const muxoDesc = muxoFm.description ||
@@ -746,7 +814,7 @@ export async function handleJsonRpcMcpRequest(engine, ctx, rpc) {
746
814
  instructions: MUXO_MCP_SERVER_INSTRUCTIONS,
747
815
  serverInfo: {
748
816
  name: "muxo-mcp-server",
749
- version: "1.3.0",
817
+ version: MUXO_VERSION,
750
818
  },
751
819
  capabilities: {
752
820
  tools: { listChanged: false },
@@ -766,7 +834,10 @@ export async function handleJsonRpcMcpRequest(engine, ctx, rpc) {
766
834
  jsonrpc: "2.0",
767
835
  id,
768
836
  result: {
769
- tools: MUXO_MCP_TOOLS,
837
+ tools: MUXO_MCP_TOOLS.map((t) => ({
838
+ ...t,
839
+ securitySchemes: t.securitySchemes || DEFAULT_TOOL_SECURITY_SCHEMES,
840
+ })),
770
841
  },
771
842
  };
772
843
  }
@@ -795,9 +866,14 @@ export async function handleJsonRpcMcpRequest(engine, ctx, rpc) {
795
866
  // SEP-2640 Skills Import Extension (`io.modelcontextprotocol/skills`)
796
867
  if (method === "skills/list") {
797
868
  const skills = getMuxoMcpSkills().map((s) => ({
869
+ uri: s.uri,
870
+ frontmatter: s.frontmatter,
871
+ resources: s.files.map((f) => ({
872
+ uri: f.uri,
873
+ digest: f.digest,
874
+ })),
798
875
  name: s.name,
799
876
  description: s.description,
800
- uri: s.uri,
801
877
  version: s.version,
802
878
  digest: s.files[0]?.digest || "",
803
879
  files: s.files.length,
@@ -809,30 +885,39 @@ export async function handleJsonRpcMcpRequest(engine, ctx, rpc) {
809
885
  };
810
886
  }
811
887
  if (method === "skills/get") {
888
+ const requestedUri = String(params.uri || "").trim();
812
889
  const requestedName = String(params.name || "muxo").trim();
813
- const skill = getMuxoMcpSkills().find((s) => s.name === requestedName);
890
+ const skill = getMuxoMcpSkills().find((s) => s.uri === requestedUri || s.name === requestedName);
814
891
  if (!skill) {
815
892
  return {
816
893
  jsonrpc: "2.0",
817
894
  id,
818
895
  error: {
819
896
  code: -32602,
820
- message: `Skill not found: ${requestedName}`,
897
+ message: `Skill not found: ${requestedUri || requestedName}`,
821
898
  },
822
899
  };
823
900
  }
901
+ const resources = skill.files.map((f) => ({
902
+ uri: f.uri,
903
+ digest: f.digest,
904
+ }));
824
905
  return {
825
906
  jsonrpc: "2.0",
826
907
  id,
827
908
  result: {
828
909
  skill: {
910
+ uri: skill.uri,
911
+ frontmatter: skill.frontmatter,
912
+ resources,
829
913
  name: skill.name,
830
914
  description: skill.description,
831
- uri: skill.uri,
832
915
  version: skill.version,
833
916
  digest: skill.files[0]?.digest || "",
834
917
  },
918
+ uri: skill.uri,
835
919
  frontmatter: skill.frontmatter,
920
+ resources,
836
921
  files: skill.files.map((f) => ({
837
922
  path: f.path,
838
923
  uri: f.uri,
@@ -894,17 +979,19 @@ export async function handleJsonRpcMcpRequest(engine, ctx, rpc) {
894
979
  };
895
980
  }
896
981
  catch (err) {
982
+ const errPayload = {
983
+ ok: false,
984
+ error: String(err?.message || err || "MCP tool execution failed"),
985
+ };
897
986
  return {
898
987
  jsonrpc: "2.0",
899
988
  id,
900
989
  result: {
990
+ structuredContent: errPayload,
901
991
  content: [
902
992
  {
903
993
  type: "text",
904
- text: JSON.stringify({
905
- ok: false,
906
- error: String(err?.message || err || "MCP tool execution failed"),
907
- }, null, 2),
994
+ text: JSON.stringify(errPayload, null, 2),
908
995
  },
909
996
  ],
910
997
  isError: true,
package/dist/nl.js CHANGED
@@ -80,23 +80,39 @@ export function extractParametersFromNaturalLanguage(prompt, tool, explicitInput
80
80
  input.numResults = extractNumberAfterKeyword(text, ["results", "limit", "top"], 5);
81
81
  }
82
82
  }
83
- else if (slug === "instant.code.execute") {
84
- if (!input.language) {
83
+ else if (slug === "instant.code.execute" || slug === "instant.composio.workbench") {
84
+ if (!input.language && slug === "instant.code.execute") {
85
85
  input.language = /\b(javascript|js|node|typescript|ts)\b/i.test(text)
86
86
  ? "javascript"
87
87
  : "python";
88
88
  }
89
- if (!input.code) {
90
- const codeBlock = text.match(/```(?:\w+)?\n([\s\S]*?)```/);
91
- if (codeBlock) {
92
- input.code = codeBlock[1].trim();
89
+ const codeBlock = text.match(/```(?:\w+)?\n([\s\S]*?)```/);
90
+ const inlineCode = text.match(/`([^`]+)`/);
91
+ const extractedCode = codeBlock
92
+ ? codeBlock[1].trim()
93
+ : inlineCode
94
+ ? inlineCode[1].trim()
95
+ : text;
96
+ if (slug === "instant.composio.workbench") {
97
+ if (!input.code_to_execute) {
98
+ input.code_to_execute = String(input.code || extractedCode);
93
99
  }
94
- else {
95
- const inlineCode = text.match(/`([^`]+)`/);
96
- if (inlineCode)
97
- input.code = inlineCode[1].trim();
100
+ if (!input.thought) {
101
+ input.thought = `Natural language workbench execution: ${text.slice(0, 80)}`;
98
102
  }
99
103
  }
104
+ else if (!input.code) {
105
+ input.code = extractedCode;
106
+ }
107
+ }
108
+ else if (slug === "instant.github.repo_intel") {
109
+ const repoMatch = text.match(/\b([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)\b/);
110
+ if (repoMatch && !repoMatch[0].includes(".ai") && !repoMatch[0].includes(".com")) {
111
+ if (!input.owner)
112
+ input.owner = repoMatch[1];
113
+ if (!input.repo)
114
+ input.repo = repoMatch[2];
115
+ }
100
116
  }
101
117
  else if (slug.startsWith("resource.")) {
102
118
  if (!input.resource_name && !input.name) {
@@ -235,13 +251,19 @@ function scoreToolForPrompt(prompt, tool) {
235
251
  if (slug === "instant.web.scrape")
236
252
  score += 110;
237
253
  }
238
- if (/\b(search\s+the\s+web|web\s+search|search\s+for|look\s+up|find\s+news|exa|google)\b/i.test(q)) {
254
+ const isExplicitWebSearch = /\b(search\s+(?:the\s+)?web|web\s+search|search\s+online|search\s+(?:for|about)|look\s+up|find\s+(?:news|info|information|articles)|exa|google)\b/i.test(q);
255
+ const hasProvisionVerb = /\b(provision|create|deploy|spin\s+up|setup|set\s+up|add\s+resource|new\s+db|new\s+database|database|db)\b/i.test(q);
256
+ if (isExplicitWebSearch) {
239
257
  if (slug === "instant.web.search")
240
- score += 105;
258
+ score += 155;
259
+ }
260
+ if (/\b(run\s+python|execute\s+code|run\s+script|sandbox|workbench|calculate|e2b)\b/i.test(q)) {
261
+ if (slug === "instant.code.execute" || slug === "instant.composio.workbench")
262
+ score += 125;
241
263
  }
242
- if (/\b(run\s+python|execute\s+code|run\s+script|sandbox|calculate|e2b)\b/i.test(q)) {
243
- if (slug === "instant.code.execute")
244
- score += 105;
264
+ if (/\b(repo\s+intel|github\s+repo|inspect\s+repo|repository\s+intel|stars\s+and\s+forks)\b/i.test(q)) {
265
+ if (slug === "instant.github.repo_intel")
266
+ score += 135;
245
267
  }
246
268
  if (/\b(top\s*up|add\s+credits|buy\s+credits|refill\s+credits|more\s+credits|\d+\s+credits)\b/i.test(q)) {
247
269
  if (slug === "billing.credits.topup")
@@ -255,27 +277,29 @@ function scoreToolForPrompt(prompt, tool) {
255
277
  if (slug === "billing.checkout.create")
256
278
  score += 125;
257
279
  }
258
- if (/\b(turso|libsql|sqlite\s+database|sqlite\s+db)\b/i.test(q)) {
259
- if (slug.startsWith("resource.turso."))
260
- score += 120;
261
- if (slug.includes("database"))
262
- score += 15;
263
- }
264
- if (/\b(neon|postgres|postgresql|pg\s+database)\b/i.test(q)) {
265
- if (slug.startsWith("resource.neon."))
266
- score += 120;
267
- }
268
- if (/\b(upstash|redis|cache\s+store|kv\s+store)\b/i.test(q)) {
269
- if (slug.startsWith("resource.upstash."))
270
- score += 120;
271
- }
272
- if (/\b(supabase)\b/i.test(q)) {
273
- if (slug.startsWith("resource.supabase."))
274
- score += 120;
275
- }
276
- if (/\b(mongodb|mongo)\b/i.test(q)) {
277
- if (slug.startsWith("resource.mongodb."))
278
- score += 120;
280
+ if (!isExplicitWebSearch || hasProvisionVerb) {
281
+ if (/\b(turso|libsql|sqlite\s+database|sqlite\s+db)\b/i.test(q)) {
282
+ if (slug.startsWith("resource.turso."))
283
+ score += 120;
284
+ if (slug.includes("database"))
285
+ score += 15;
286
+ }
287
+ if (/\b(neon|postgres|postgresql|pg\s+database)\b/i.test(q)) {
288
+ if (slug.startsWith("resource.neon."))
289
+ score += 120;
290
+ }
291
+ if (/\b(upstash|redis|cache\s+store|kv\s+store)\b/i.test(q)) {
292
+ if (slug.startsWith("resource.upstash."))
293
+ score += 120;
294
+ }
295
+ if (/\b(supabase)\b/i.test(q)) {
296
+ if (slug.startsWith("resource.supabase."))
297
+ score += 120;
298
+ }
299
+ if (/\b(mongodb|mongo)\b/i.test(q)) {
300
+ if (slug.startsWith("resource.mongodb."))
301
+ score += 120;
302
+ }
279
303
  }
280
304
  if (/\b(hacker\s*news|hn\s+stories|top\s+stories)\b/i.test(q)) {
281
305
  if (slug === "instant.hackernews.top_stories")
@@ -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
+ }
@@ -10,5 +10,9 @@ export declare class ComposioInstantAdapter {
10
10
  workspaceId: string;
11
11
  tool: MuxoTool;
12
12
  input: Record<string, unknown>;
13
+ composioApiKey?: string | null;
14
+ exaApiKey?: string | null;
15
+ firecrawlApiKey?: string | null;
16
+ masterWalletDepleted?: boolean;
13
17
  }): Promise<Record<string, unknown>>;
14
18
  }