@muxoai/sdk 1.2.3 → 1.4.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.
- package/.agents/plugins/marketplace.json +22 -0
- package/.claude-plugin/marketplace.json +32 -0
- package/.codex/config.toml +12 -0
- package/ARCHITECTURE.md +121 -0
- package/README.md +85 -31
- package/dist/clerk.d.ts +35 -0
- package/dist/clerk.js +201 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +2380 -0
- package/dist/composio-catalog.d.ts +234 -0
- package/dist/composio-catalog.js +1894 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.js +115 -0
- package/dist/durable-workflow.d.ts +45 -0
- package/dist/durable-workflow.js +90 -0
- package/dist/engine.d.ts +465 -0
- package/dist/engine.js +2560 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/mcp-schema.d.ts +286 -0
- package/dist/mcp-schema.js +2559 -0
- package/dist/mcp.d.ts +22 -0
- package/dist/mcp.js +1356 -0
- package/dist/memory-graph.d.ts +21 -0
- package/dist/memory-graph.js +173 -0
- package/dist/nl.d.ts +34 -0
- package/dist/nl.js +756 -0
- package/dist/oauth.d.ts +32 -0
- package/dist/oauth.js +134 -0
- package/dist/policy.d.ts +190 -0
- package/dist/policy.js +1597 -0
- package/dist/providers/composio-instant.d.ts +18 -0
- package/dist/providers/composio-instant.js +1169 -0
- package/dist/providers/composio-sdk.d.ts +162 -0
- package/dist/providers/composio-sdk.js +1395 -0
- package/dist/providers/microsoft-governance.d.ts +152 -0
- package/dist/providers/microsoft-governance.js +1062 -0
- package/dist/providers/stripe-billing.d.ts +83 -0
- package/dist/providers/stripe-billing.js +611 -0
- package/dist/providers/stripe-projects.d.ts +508 -0
- package/dist/providers/stripe-projects.js +3188 -0
- package/dist/providers/supermemory.d.ts +160 -0
- package/dist/providers/supermemory.js +778 -0
- package/dist/provisioning-managed.d.ts +50 -0
- package/dist/provisioning-managed.js +155 -0
- package/dist/sdk.d.ts +951 -0
- package/dist/sdk.js +748 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +3575 -0
- package/dist/store.d.ts +348 -0
- package/dist/store.js +2323 -0
- package/dist/types.d.ts +593 -0
- package/dist/types.js +1 -0
- package/dist/ui/console-html.d.ts +64 -0
- package/dist/ui/console-html.js +3857 -0
- package/dist/ui/design-tokens.d.ts +177 -0
- package/dist/ui/design-tokens.js +210 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +68 -7
- package/plugins/muxo/.app.json +8 -0
- package/plugins/muxo/.claude-plugin/plugin.json +16 -0
- package/plugins/muxo/.codex-plugin/plugin.json +64 -0
- package/plugins/muxo/.cursor-plugin/plugin.json +30 -0
- package/plugins/muxo/.mcp.json +8 -0
- package/plugins/muxo/assets/logo.png +0 -0
- package/plugins/muxo/assets/logo.svg +6 -0
- package/plugins/muxo/hooks/hooks.json +27 -0
- package/plugins/muxo/hooks/session-start.sh +97 -0
- package/plugins/muxo/hooks/user-prompt-submit.sh +58 -0
- package/plugins/muxo/mcp.json +28 -0
- package/plugins/muxo/plugin.json +116 -0
- package/plugins/muxo/skills/muxo/SKILL.md +153 -0
- package/plugins/muxo/skills/muxo/agents/openai.yaml +12 -0
- package/plugins/muxo/skills/muxo/references/cli.md +63 -0
- package/plugins/muxo/skills/muxo/references/mcp.md +37 -0
- package/plugins/muxo/skills/muxo-activity-summary/SKILL.md +81 -0
- package/plugins/muxo/skills/setup/SKILL.md +42 -0
- package/plugins/muxo/skills/setup/agents/openai.yaml +12 -0
- package/src/catalogues/stripe-projects-catalog.json +9972 -0
- package/vendor/microsoft-governance/README.md +363 -0
- package/vendor/microsoft-governance/dist/audit.d.ts +26 -0
- package/vendor/microsoft-governance/dist/audit.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/audit.js +107 -0
- package/vendor/microsoft-governance/dist/audit.js.map +1 -0
- package/vendor/microsoft-governance/dist/cascade-containment.d.ts +84 -0
- package/vendor/microsoft-governance/dist/cascade-containment.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/cascade-containment.js +387 -0
- package/vendor/microsoft-governance/dist/cascade-containment.js.map +1 -0
- package/vendor/microsoft-governance/dist/client.d.ts +34 -0
- package/vendor/microsoft-governance/dist/client.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/client.js +112 -0
- package/vendor/microsoft-governance/dist/client.js.map +1 -0
- package/vendor/microsoft-governance/dist/context-poisoning.d.ts +58 -0
- package/vendor/microsoft-governance/dist/context-poisoning.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/context-poisoning.js +410 -0
- package/vendor/microsoft-governance/dist/context-poisoning.js.map +1 -0
- package/vendor/microsoft-governance/dist/credential-vault.d.ts +213 -0
- package/vendor/microsoft-governance/dist/credential-vault.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/credential-vault.js +567 -0
- package/vendor/microsoft-governance/dist/credential-vault.js.map +1 -0
- package/vendor/microsoft-governance/dist/discovery.d.ts +80 -0
- package/vendor/microsoft-governance/dist/discovery.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/discovery.js +404 -0
- package/vendor/microsoft-governance/dist/discovery.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/channel.d.ts +29 -0
- package/vendor/microsoft-governance/dist/encryption/channel.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/channel.js +78 -0
- package/vendor/microsoft-governance/dist/encryption/channel.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/index.d.ts +9 -0
- package/vendor/microsoft-governance/dist/encryption/index.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/index.js +23 -0
- package/vendor/microsoft-governance/dist/encryption/index.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/mesh-client.d.ts +267 -0
- package/vendor/microsoft-governance/dist/encryption/mesh-client.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/mesh-client.js +1001 -0
- package/vendor/microsoft-governance/dist/encryption/mesh-client.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/ratchet.d.ts +38 -0
- package/vendor/microsoft-governance/dist/encryption/ratchet.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/ratchet.js +223 -0
- package/vendor/microsoft-governance/dist/encryption/ratchet.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/registry-client.d.ts +136 -0
- package/vendor/microsoft-governance/dist/encryption/registry-client.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/registry-client.js +369 -0
- package/vendor/microsoft-governance/dist/encryption/registry-client.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/x3dh.d.ts +80 -0
- package/vendor/microsoft-governance/dist/encryption/x3dh.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/x3dh.js +238 -0
- package/vendor/microsoft-governance/dist/encryption/x3dh.js.map +1 -0
- package/vendor/microsoft-governance/dist/framework-adapter.d.ts +63 -0
- package/vendor/microsoft-governance/dist/framework-adapter.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/framework-adapter.js +253 -0
- package/vendor/microsoft-governance/dist/framework-adapter.js.map +1 -0
- package/vendor/microsoft-governance/dist/identity.d.ts +124 -0
- package/vendor/microsoft-governance/dist/identity.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/identity.js +446 -0
- package/vendor/microsoft-governance/dist/identity.js.map +1 -0
- package/vendor/microsoft-governance/dist/index.d.ts +39 -0
- package/vendor/microsoft-governance/dist/index.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/index.js +95 -0
- package/vendor/microsoft-governance/dist/index.js.map +1 -0
- package/vendor/microsoft-governance/dist/kill-switch.d.ts +22 -0
- package/vendor/microsoft-governance/dist/kill-switch.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/kill-switch.js +60 -0
- package/vendor/microsoft-governance/dist/kill-switch.js.map +1 -0
- package/vendor/microsoft-governance/dist/lifecycle.d.ts +50 -0
- package/vendor/microsoft-governance/dist/lifecycle.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/lifecycle.js +113 -0
- package/vendor/microsoft-governance/dist/lifecycle.js.map +1 -0
- package/vendor/microsoft-governance/dist/mcp.d.ts +41 -0
- package/vendor/microsoft-governance/dist/mcp.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/mcp.js +233 -0
- package/vendor/microsoft-governance/dist/mcp.js.map +1 -0
- package/vendor/microsoft-governance/dist/metrics.d.ts +123 -0
- package/vendor/microsoft-governance/dist/metrics.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/metrics.js +289 -0
- package/vendor/microsoft-governance/dist/metrics.js.map +1 -0
- package/vendor/microsoft-governance/dist/oci-manifest.d.ts +49 -0
- package/vendor/microsoft-governance/dist/oci-manifest.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/oci-manifest.js +182 -0
- package/vendor/microsoft-governance/dist/oci-manifest.js.map +1 -0
- package/vendor/microsoft-governance/dist/policy-backends/cedar.d.ts +15 -0
- package/vendor/microsoft-governance/dist/policy-backends/cedar.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/policy-backends/cedar.js +88 -0
- package/vendor/microsoft-governance/dist/policy-backends/cedar.js.map +1 -0
- package/vendor/microsoft-governance/dist/policy-backends/opa.d.ts +17 -0
- package/vendor/microsoft-governance/dist/policy-backends/opa.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/policy-backends/opa.js +74 -0
- package/vendor/microsoft-governance/dist/policy-backends/opa.js.map +1 -0
- package/vendor/microsoft-governance/dist/policy.d.ts +77 -0
- package/vendor/microsoft-governance/dist/policy.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/policy.js +760 -0
- package/vendor/microsoft-governance/dist/policy.js.map +1 -0
- package/vendor/microsoft-governance/dist/prompt-defense.d.ts +37 -0
- package/vendor/microsoft-governance/dist/prompt-defense.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/prompt-defense.js +229 -0
- package/vendor/microsoft-governance/dist/prompt-defense.js.map +1 -0
- package/vendor/microsoft-governance/dist/protocol-facets.d.ts +87 -0
- package/vendor/microsoft-governance/dist/protocol-facets.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/protocol-facets.js +728 -0
- package/vendor/microsoft-governance/dist/protocol-facets.js.map +1 -0
- package/vendor/microsoft-governance/dist/rings.d.ts +17 -0
- package/vendor/microsoft-governance/dist/rings.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/rings.js +61 -0
- package/vendor/microsoft-governance/dist/rings.js.map +1 -0
- package/vendor/microsoft-governance/dist/sandbox.d.ts +61 -0
- package/vendor/microsoft-governance/dist/sandbox.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/sandbox.js +199 -0
- package/vendor/microsoft-governance/dist/sandbox.js.map +1 -0
- package/vendor/microsoft-governance/dist/surface-parity.d.ts +45 -0
- package/vendor/microsoft-governance/dist/surface-parity.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/surface-parity.js +216 -0
- package/vendor/microsoft-governance/dist/surface-parity.js.map +1 -0
- package/vendor/microsoft-governance/dist/trust.d.ts +28 -0
- package/vendor/microsoft-governance/dist/trust.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/trust.js +209 -0
- package/vendor/microsoft-governance/dist/trust.js.map +1 -0
- package/vendor/microsoft-governance/dist/types.d.ts +422 -0
- package/vendor/microsoft-governance/dist/types.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/types.js +29 -0
- package/vendor/microsoft-governance/dist/types.js.map +1 -0
- package/vendor/microsoft-governance/dist/verify.d.ts +135 -0
- package/vendor/microsoft-governance/dist/verify.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/verify.js +356 -0
- package/vendor/microsoft-governance/dist/verify.js.map +1 -0
- package/vendor/microsoft-governance/package.json +64 -0
- package/index.d.ts +0 -177
- package/index.js +0 -1
package/dist/engine.js
ADDED
|
@@ -0,0 +1,2560 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { isStripeProjectsExclusiveProvider, normalizeStripeProjectsProviderSlug, wrapMuxoConnectUrl, } from "./composio-catalog.js";
|
|
3
|
+
import { APPROVED_PLAN_STATUSES, approvalRequiredError, buildApprovalHumanHandoff, buildApprovalUrl, buildOAuthHumanHandoff, clampWaitSeconds, looksLikeToolSlug, unknownToolError, } from "./mcp-schema.js";
|
|
4
|
+
import { completeCloudProvisioningWorkflow, createCloudProvisioningWorkflow, normalizeWorkflowTarget, workflowNotFoundError, } from "./durable-workflow.js";
|
|
5
|
+
import { buildMemoryKnowledgeGraph } from "./memory-graph.js";
|
|
6
|
+
import { resolveNaturalLanguageIntent, } from "./nl.js";
|
|
7
|
+
import { DEFAULT_WORKSPACE_POLICY, buildLlmPolicyAndGovernanceBrief, compileNaturalLanguageGuardrailWithLlm, describePolicyInPlainEnglish, formatHumanFriendlyExplanation, parseNaturalLanguageGuardrail, simulatePolicyEvaluation, } from "./policy.js";
|
|
8
|
+
import { ComposioInstantAdapter } from "./providers/composio-instant.js";
|
|
9
|
+
import { ComposioSdkAdapter } from "./providers/composio-sdk.js";
|
|
10
|
+
import { MicrosoftGovernanceAdapter } from "./providers/microsoft-governance.js";
|
|
11
|
+
import { StripeBillingAdapter } from "./providers/stripe-billing.js";
|
|
12
|
+
import { StripeProjectsAdapter, isPaidCloudProvisioning, } from "./providers/stripe-projects.js";
|
|
13
|
+
import { SupermemoryAdapter, applyActiveMemoryToToolInput, explainMemoryContext, extractActiveMemoryDefaults, parseNaturalLanguageMemoryInstruction, rankMemoriesHybrid, } from "./providers/supermemory.js";
|
|
14
|
+
function makeControlPlaneResolution(toolSlug, prompt, humanSummary, input = {}) {
|
|
15
|
+
return {
|
|
16
|
+
resolved: true,
|
|
17
|
+
confidence: 0.98,
|
|
18
|
+
resolutionMethod: "semantic_router",
|
|
19
|
+
byokUsed: null,
|
|
20
|
+
tool: null,
|
|
21
|
+
toolSlug,
|
|
22
|
+
input,
|
|
23
|
+
extractedInput: input,
|
|
24
|
+
missingInputs: [],
|
|
25
|
+
needsConnection: false,
|
|
26
|
+
suggestedByokProvider: null,
|
|
27
|
+
reason: prompt,
|
|
28
|
+
alternatives: [],
|
|
29
|
+
humanSummary,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export class MuxoEngine {
|
|
33
|
+
config;
|
|
34
|
+
store;
|
|
35
|
+
composioSdk;
|
|
36
|
+
composioInstant;
|
|
37
|
+
stripeProjects;
|
|
38
|
+
stripeBilling;
|
|
39
|
+
governance;
|
|
40
|
+
supermemory;
|
|
41
|
+
durableWorkflows = new Map();
|
|
42
|
+
constructor(config, store) {
|
|
43
|
+
this.config = config;
|
|
44
|
+
this.store = store;
|
|
45
|
+
this.composioSdk = new ComposioSdkAdapter(config);
|
|
46
|
+
this.composioInstant = new ComposioInstantAdapter(config);
|
|
47
|
+
this.stripeProjects = new StripeProjectsAdapter(config);
|
|
48
|
+
this.stripeBilling = new StripeBillingAdapter(config, store);
|
|
49
|
+
this.governance = new MicrosoftGovernanceAdapter(config.vaultMasterKey);
|
|
50
|
+
this.supermemory = new SupermemoryAdapter(config);
|
|
51
|
+
}
|
|
52
|
+
// 1. get_profile
|
|
53
|
+
getProfile(ctx) {
|
|
54
|
+
const nickname = (ctx.user.email || "").split("@")[0] ||
|
|
55
|
+
ctx.user.name.toLowerCase().replace(/\s+/g, ".") ||
|
|
56
|
+
ctx.user.id;
|
|
57
|
+
return {
|
|
58
|
+
id: ctx.user.id,
|
|
59
|
+
name: ctx.user.name,
|
|
60
|
+
email: ctx.user.email,
|
|
61
|
+
nickname,
|
|
62
|
+
user: ctx.user,
|
|
63
|
+
actorId: ctx.actorId,
|
|
64
|
+
authType: ctx.authType,
|
|
65
|
+
workspaceId: ctx.workspace.id,
|
|
66
|
+
workspaceName: ctx.workspace.name,
|
|
67
|
+
tier: ctx.workspace.tier,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// 2. get_organization
|
|
71
|
+
getOrganization(ctx) {
|
|
72
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
73
|
+
const resources = this.store.listResources(ws.id);
|
|
74
|
+
const connections = this.store.listConnections(ws.id);
|
|
75
|
+
const supplierSettlementSummary = this.store.getSupplierSettlementSummary(ws.id);
|
|
76
|
+
const plainEnglishPolicy = describePolicyInPlainEnglish(ws.policy);
|
|
77
|
+
const localMemories = this.store.listMemoryRecords(ws.id, 25);
|
|
78
|
+
const activeFacts = this.store.listTemporalFacts(ws.id, {
|
|
79
|
+
includeHistorical: false,
|
|
80
|
+
limit: 30,
|
|
81
|
+
});
|
|
82
|
+
const inferred = extractActiveMemoryDefaults({
|
|
83
|
+
memories: localMemories,
|
|
84
|
+
temporalFacts: activeFacts,
|
|
85
|
+
});
|
|
86
|
+
const trustScore = this.governance.getWorkspaceTrustScore(ws.id);
|
|
87
|
+
const govStatus = this.governance.getStatus(ws);
|
|
88
|
+
const llmPolicyBrief = buildLlmPolicyAndGovernanceBrief(ws.policy, {
|
|
89
|
+
workspaceName: ws.name,
|
|
90
|
+
workspaceSlug: ws.slug,
|
|
91
|
+
activeDefaults: inferred.activeDefaults,
|
|
92
|
+
trustScore: { overall: trustScore.overall, tier: trustScore.tier },
|
|
93
|
+
agentDid: govStatus.agentDid,
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
organization: {
|
|
97
|
+
id: ws.id,
|
|
98
|
+
name: ws.name,
|
|
99
|
+
slug: ws.slug,
|
|
100
|
+
tier: ws.tier,
|
|
101
|
+
usage: {
|
|
102
|
+
monthlyActionsUsed: ws.monthlyActionsUsed,
|
|
103
|
+
monthlyActionsLimit: ws.monthlyActionsLimit,
|
|
104
|
+
monthlyActionsRemaining: Math.max(0, ws.monthlyActionsLimit - ws.monthlyActionsUsed),
|
|
105
|
+
creditsBalance: ws.creditsBalance,
|
|
106
|
+
},
|
|
107
|
+
supplierSettlementSummary,
|
|
108
|
+
policy: ws.policy,
|
|
109
|
+
plainEnglishPolicy,
|
|
110
|
+
llmPolicyBrief,
|
|
111
|
+
activeDefaults: inferred.activeDefaults,
|
|
112
|
+
explanation: plainEnglishPolicy.explanation,
|
|
113
|
+
activeConnectionsCount: connections.filter((c) => c.status === "active").length,
|
|
114
|
+
provisionedResourcesCount: resources.filter((r) => r.status === "active").length,
|
|
115
|
+
projectsEnvironment: ws.projectsEnvName,
|
|
116
|
+
createdAt: ws.createdAt,
|
|
117
|
+
},
|
|
118
|
+
llmPolicyBrief,
|
|
119
|
+
activeDefaults: inferred.activeDefaults,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// 3. search_tools
|
|
123
|
+
async searchTools(params) {
|
|
124
|
+
const limit = Math.min(100, Math.max(1, Number(params.limit || 25)));
|
|
125
|
+
const lane = params.lane || "all";
|
|
126
|
+
const query = params.query?.trim();
|
|
127
|
+
const queryTokens = (query || "")
|
|
128
|
+
.toLowerCase()
|
|
129
|
+
.split(/[\s,._/-]+/)
|
|
130
|
+
.filter(Boolean);
|
|
131
|
+
const prefersStripeProjects = queryTokens.some((tok) => isStripeProjectsExclusiveProvider(tok) ||
|
|
132
|
+
/^(provision|deploy|deployment|publish|host|hosting|static|landing|database|postgres|sqlite|redis)$/.test(tok));
|
|
133
|
+
const results = [];
|
|
134
|
+
if (lane === "all" && prefersStripeProjects) {
|
|
135
|
+
results.push(...this.stripeProjects.searchTools(query, limit));
|
|
136
|
+
}
|
|
137
|
+
if (lane === "all" || lane === "composio_instant") {
|
|
138
|
+
results.push(...this.composioInstant.searchTools(query, limit));
|
|
139
|
+
}
|
|
140
|
+
if (lane === "all" || lane === "composio_sdk") {
|
|
141
|
+
const sdkTools = await this.composioSdk.searchTools(query, limit);
|
|
142
|
+
results.push(...sdkTools);
|
|
143
|
+
}
|
|
144
|
+
if (lane === "stripe_projects" || (lane === "all" && !prefersStripeProjects)) {
|
|
145
|
+
results.push(...this.stripeProjects.searchTools(query, limit));
|
|
146
|
+
}
|
|
147
|
+
if (lane === "all" || lane === "stripe_projects") {
|
|
148
|
+
const billingTools = this.stripeBilling.listTools().filter((t) => {
|
|
149
|
+
if (!query)
|
|
150
|
+
return true;
|
|
151
|
+
const q = query.toLowerCase();
|
|
152
|
+
const hay = `${t.slug} ${t.name} ${t.description} ${t.categories.join(" ")}`.toLowerCase();
|
|
153
|
+
return q.split(/\s+/).some((tok) => hay.includes(tok));
|
|
154
|
+
});
|
|
155
|
+
results.push(...billingTools);
|
|
156
|
+
}
|
|
157
|
+
const filtered = params.riskLevel
|
|
158
|
+
? results.filter((t) => t.riskLevel === params.riskLevel)
|
|
159
|
+
: results;
|
|
160
|
+
return {
|
|
161
|
+
tools: filtered.slice(0, limit),
|
|
162
|
+
total: filtered.length,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
async listAllCatalogTools() {
|
|
166
|
+
const instant = this.composioInstant.searchTools(undefined, 100);
|
|
167
|
+
const sdk = await this.composioSdk.searchTools(undefined, 100);
|
|
168
|
+
const proj = this.stripeProjects.getTools();
|
|
169
|
+
const billing = this.stripeBilling.listTools();
|
|
170
|
+
return [...instant, ...billing, ...sdk, ...proj];
|
|
171
|
+
}
|
|
172
|
+
// 4. inspect_tool
|
|
173
|
+
async inspectTool(slug) {
|
|
174
|
+
const normalized = slug.trim();
|
|
175
|
+
const billing = this.stripeBilling
|
|
176
|
+
.listTools()
|
|
177
|
+
.find((t) => t.slug.toLowerCase() === normalized.toLowerCase() ||
|
|
178
|
+
t.upstreamSlug.toLowerCase() === normalized.toLowerCase());
|
|
179
|
+
if (billing)
|
|
180
|
+
return billing;
|
|
181
|
+
const instant = this.composioInstant.inspectTool(normalized);
|
|
182
|
+
if (instant)
|
|
183
|
+
return instant;
|
|
184
|
+
const proj = this.stripeProjects.inspectTool(normalized);
|
|
185
|
+
if (proj)
|
|
186
|
+
return proj;
|
|
187
|
+
const sdk = await this.composioSdk.inspectTool(normalized);
|
|
188
|
+
if (sdk)
|
|
189
|
+
return sdk;
|
|
190
|
+
throw unknownToolError(normalized);
|
|
191
|
+
}
|
|
192
|
+
// 5. connection_status
|
|
193
|
+
async connectionStatus(ctx, params) {
|
|
194
|
+
if (params?.syncLive !== false) {
|
|
195
|
+
// Sync live Composio connected accounts for this workspace
|
|
196
|
+
const composioByok = this.store.getByokKey(ctx.workspace.id, "composio");
|
|
197
|
+
const liveComposio = await this.composioSdk.listLiveConnections(ctx.workspace.id, composioByok);
|
|
198
|
+
for (const c of liveComposio) {
|
|
199
|
+
if (isStripeProjectsExclusiveProvider(c.providerSlug)) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
this.store.upsertConnection({
|
|
203
|
+
workspaceId: ctx.workspace.id,
|
|
204
|
+
lane: "composio_sdk",
|
|
205
|
+
providerSlug: c.providerSlug,
|
|
206
|
+
providerName: c.providerName,
|
|
207
|
+
status: c.status,
|
|
208
|
+
externalAccountId: c.externalAccountId,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
// Sync live Stripe Projects linked providers
|
|
212
|
+
try {
|
|
213
|
+
const stripeByok = this.store.getByokKey(ctx.workspace.id, "stripe");
|
|
214
|
+
const projStatus = await this.stripeProjects.getLiveStatus(stripeByok);
|
|
215
|
+
for (const p of projStatus.providers) {
|
|
216
|
+
this.store.upsertConnection({
|
|
217
|
+
workspaceId: ctx.workspace.id,
|
|
218
|
+
lane: "stripe_projects",
|
|
219
|
+
providerSlug: p.slug,
|
|
220
|
+
providerName: p.name,
|
|
221
|
+
status: p.status,
|
|
222
|
+
externalAccountId: p.id,
|
|
223
|
+
redirectUrl: p.redirectUrl,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Keep cached connections if CLI is busy
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
let connections = this.store.listConnections(ctx.workspace.id).map((c) => {
|
|
232
|
+
if (isStripeProjectsExclusiveProvider(c.providerSlug)) {
|
|
233
|
+
return {
|
|
234
|
+
...c,
|
|
235
|
+
lane: "stripe_projects",
|
|
236
|
+
status: "active",
|
|
237
|
+
redirectUrl: null,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
return c.lane === "composio_sdk" && c.redirectUrl
|
|
241
|
+
? {
|
|
242
|
+
...c,
|
|
243
|
+
redirectUrl: wrapMuxoConnectUrl(c.redirectUrl, this.config.baseUrl, {
|
|
244
|
+
workspaceId: c.workspaceId,
|
|
245
|
+
providerSlug: c.providerSlug,
|
|
246
|
+
externalAccountId: c.externalAccountId,
|
|
247
|
+
}),
|
|
248
|
+
}
|
|
249
|
+
: c;
|
|
250
|
+
});
|
|
251
|
+
if (params?.provider) {
|
|
252
|
+
const target = normalizeStripeProjectsProviderSlug(params.provider) || params.provider.toLowerCase();
|
|
253
|
+
if (isStripeProjectsExclusiveProvider(target)) {
|
|
254
|
+
const activeRecord = this.store.upsertConnection({
|
|
255
|
+
workspaceId: ctx.workspace.id,
|
|
256
|
+
lane: "stripe_projects",
|
|
257
|
+
providerSlug: target,
|
|
258
|
+
providerName: target.charAt(0).toUpperCase() + target.slice(1),
|
|
259
|
+
status: "active",
|
|
260
|
+
externalAccountId: `sp_conn_${target}`,
|
|
261
|
+
redirectUrl: null,
|
|
262
|
+
metadata: {
|
|
263
|
+
managedBy: "stripe_projects",
|
|
264
|
+
requiresOAuth: false,
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
connections = [activeRecord];
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
connections = connections.filter((c) => c.providerSlug.toLowerCase() === target ||
|
|
271
|
+
c.providerName.toLowerCase().includes(target));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
const targetConn = connections[0] || null;
|
|
275
|
+
const isActive = targetConn
|
|
276
|
+
? targetConn.status === "active"
|
|
277
|
+
: connections.some((c) => c.status === "active");
|
|
278
|
+
return {
|
|
279
|
+
ok: true,
|
|
280
|
+
workspaceId: ctx.workspace.id,
|
|
281
|
+
instantWalletStatus: "active",
|
|
282
|
+
connected: isActive,
|
|
283
|
+
ready: isActive,
|
|
284
|
+
timedOut: false,
|
|
285
|
+
status: targetConn?.status || "not_connected",
|
|
286
|
+
redirectUrl: isActive ? null : targetConn?.redirectUrl || null,
|
|
287
|
+
connection: targetConn,
|
|
288
|
+
connections,
|
|
289
|
+
summary: {
|
|
290
|
+
total: connections.length,
|
|
291
|
+
active: connections.filter((c) => c.status === "active").length,
|
|
292
|
+
pending: connections.filter((c) => c.status === "pending").length,
|
|
293
|
+
expired: connections.filter((c) => c.status === "expired").length,
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
getHostedSiteHtml(slug, filePath) {
|
|
298
|
+
return this.stripeProjects.getHostedSiteHtml(slug, filePath);
|
|
299
|
+
}
|
|
300
|
+
// 6. start_connection
|
|
301
|
+
async startConnection(ctx, params) {
|
|
302
|
+
const rawProvider = params.provider.trim().toLowerCase();
|
|
303
|
+
const cleanSlug = normalizeStripeProjectsProviderSlug(rawProvider) ||
|
|
304
|
+
rawProvider.replace(/^(app\.|resource\.|projects\.)/, "");
|
|
305
|
+
const isProjectsLane = params.lane === "stripe_projects" ||
|
|
306
|
+
rawProvider.startsWith("resource.") ||
|
|
307
|
+
rawProvider.startsWith("projects.") ||
|
|
308
|
+
isStripeProjectsExclusiveProvider(cleanSlug);
|
|
309
|
+
if (isProjectsLane) {
|
|
310
|
+
const stripeByok = this.store.getByokKey(ctx.workspace.id, "stripe");
|
|
311
|
+
const linked = await this.stripeProjects.linkProvider(cleanSlug, stripeByok);
|
|
312
|
+
const record = this.store.upsertConnection({
|
|
313
|
+
workspaceId: ctx.workspace.id,
|
|
314
|
+
lane: "stripe_projects",
|
|
315
|
+
providerSlug: linked.providerSlug,
|
|
316
|
+
providerName: linked.providerSlug.charAt(0).toUpperCase() +
|
|
317
|
+
linked.providerSlug.slice(1),
|
|
318
|
+
status: linked.status === "requires_review" ? "pending" : linked.status,
|
|
319
|
+
externalAccountId: linked.connectionRequestId || null,
|
|
320
|
+
redirectUrl: linked.redirectUrl,
|
|
321
|
+
});
|
|
322
|
+
return {
|
|
323
|
+
...record,
|
|
324
|
+
message: linked.message,
|
|
325
|
+
...(linked.workflowStatus ? { workflowStatus: linked.workflowStatus } : {}),
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
const composioByok = this.store.getByokKey(ctx.workspace.id, "composio");
|
|
329
|
+
const started = await this.composioSdk.startConnection({
|
|
330
|
+
workspaceId: ctx.workspace.id,
|
|
331
|
+
providerSlug: cleanSlug,
|
|
332
|
+
callbackUrl: params.callbackUrl,
|
|
333
|
+
composioApiKey: composioByok,
|
|
334
|
+
});
|
|
335
|
+
const record = this.store.upsertConnection({
|
|
336
|
+
workspaceId: ctx.workspace.id,
|
|
337
|
+
lane: "composio_sdk",
|
|
338
|
+
providerSlug: started.providerSlug,
|
|
339
|
+
providerName: started.providerName,
|
|
340
|
+
status: started.status,
|
|
341
|
+
externalAccountId: started.externalAccountId,
|
|
342
|
+
redirectUrl: started.redirectUrl,
|
|
343
|
+
metadata: { callbackUrl: started.callbackUrl },
|
|
344
|
+
});
|
|
345
|
+
return {
|
|
346
|
+
...record,
|
|
347
|
+
callbackUrl: started.callbackUrl,
|
|
348
|
+
message: started.message,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
// 6b. wait_for_connection (OAuth handoff polling & recovery, like COMPOSIO_WAIT_FOR_CONNECTIONS)
|
|
352
|
+
async waitForConnection(ctx, params) {
|
|
353
|
+
const rawProvider = params.provider.trim().toLowerCase();
|
|
354
|
+
const cleanSlug = normalizeStripeProjectsProviderSlug(rawProvider) ||
|
|
355
|
+
rawProvider.replace(/^(app\.|resource\.|projects\.)/, "");
|
|
356
|
+
if (!cleanSlug) {
|
|
357
|
+
throw new Error("provider is required to wait for a connection (e.g. 'github').");
|
|
358
|
+
}
|
|
359
|
+
const startedAt = Date.now();
|
|
360
|
+
const timeoutMs = clampWaitSeconds(params.timeoutSeconds) * 1000;
|
|
361
|
+
const pollIntervalMs = Math.min(5000, Math.max(200, Number(params.pollIntervalMs ?? 1500)));
|
|
362
|
+
const deadline = startedAt + timeoutMs;
|
|
363
|
+
let polledAttempts = 0;
|
|
364
|
+
const connectedResult = (connection, message) => ({
|
|
365
|
+
ok: true,
|
|
366
|
+
connected: true,
|
|
367
|
+
ready: true,
|
|
368
|
+
timedOut: false,
|
|
369
|
+
provider: cleanSlug,
|
|
370
|
+
status: "active",
|
|
371
|
+
connection,
|
|
372
|
+
redirectUrl: null,
|
|
373
|
+
polledAttempts,
|
|
374
|
+
elapsedMs: Date.now() - startedAt,
|
|
375
|
+
message,
|
|
376
|
+
});
|
|
377
|
+
if (isStripeProjectsExclusiveProvider(cleanSlug)) {
|
|
378
|
+
const activeRecord = this.store.upsertConnection({
|
|
379
|
+
workspaceId: ctx.workspace.id,
|
|
380
|
+
lane: "stripe_projects",
|
|
381
|
+
providerSlug: cleanSlug,
|
|
382
|
+
providerName: cleanSlug.charAt(0).toUpperCase() + cleanSlug.slice(1),
|
|
383
|
+
status: "active",
|
|
384
|
+
externalAccountId: `sp_conn_${cleanSlug}`,
|
|
385
|
+
redirectUrl: null,
|
|
386
|
+
metadata: {
|
|
387
|
+
managedBy: "stripe_projects",
|
|
388
|
+
requiresOAuth: false,
|
|
389
|
+
},
|
|
390
|
+
});
|
|
391
|
+
return connectedResult(activeRecord, `${activeRecord.providerName} is managed via Stripe Projects Cloud Infrastructure and is active immediately (no OAuth required).`);
|
|
392
|
+
}
|
|
393
|
+
let current = this.store
|
|
394
|
+
.listConnections(ctx.workspace.id)
|
|
395
|
+
.find((c) => c.providerSlug.toLowerCase() === cleanSlug);
|
|
396
|
+
if (!current || (!current.redirectUrl && current.status !== "active")) {
|
|
397
|
+
try {
|
|
398
|
+
current = await this.startConnection(ctx, { provider: cleanSlug });
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
// Proceed with status check
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
while (true) {
|
|
405
|
+
polledAttempts += 1;
|
|
406
|
+
const latestLocal = this.store
|
|
407
|
+
.listConnections(ctx.workspace.id)
|
|
408
|
+
.find((c) => c.providerSlug.toLowerCase() === cleanSlug);
|
|
409
|
+
if (latestLocal) {
|
|
410
|
+
current = latestLocal;
|
|
411
|
+
if (latestLocal.status === "active") {
|
|
412
|
+
return connectedResult(latestLocal, `${latestLocal.providerName} OAuth handoff completed and connection is active.`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (current?.externalAccountId && current.lane === "composio_sdk") {
|
|
416
|
+
const composioByok = this.store.getByokKey(ctx.workspace.id, "composio");
|
|
417
|
+
const direct = await this.composioSdk.getConnectedAccountStatus(current.externalAccountId, composioByok);
|
|
418
|
+
if (direct && direct.status === "active") {
|
|
419
|
+
current = this.store.upsertConnection({
|
|
420
|
+
workspaceId: ctx.workspace.id,
|
|
421
|
+
lane: "composio_sdk",
|
|
422
|
+
providerSlug: cleanSlug,
|
|
423
|
+
providerName: cleanSlug.charAt(0).toUpperCase() + cleanSlug.slice(1),
|
|
424
|
+
status: "active",
|
|
425
|
+
externalAccountId: direct.id,
|
|
426
|
+
redirectUrl: null,
|
|
427
|
+
});
|
|
428
|
+
return connectedResult(current, `${current.providerName} OAuth handoff completed and connection is active.`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
const synced = await this.connectionStatus(ctx, {
|
|
432
|
+
provider: cleanSlug,
|
|
433
|
+
syncLive: true,
|
|
434
|
+
});
|
|
435
|
+
const list = synced.connections || [];
|
|
436
|
+
const found = list.find((c) => c.providerSlug.toLowerCase() === cleanSlug);
|
|
437
|
+
if (found) {
|
|
438
|
+
current = found;
|
|
439
|
+
if (found.status === "active") {
|
|
440
|
+
return connectedResult(found, `${found.providerName} connection is active and ready for governed execution.`);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
if (Date.now() >= deadline) {
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
447
|
+
}
|
|
448
|
+
if (!current) {
|
|
449
|
+
current = await this.startConnection(ctx, { provider: cleanSlug });
|
|
450
|
+
}
|
|
451
|
+
else if (current.redirectUrl) {
|
|
452
|
+
current = {
|
|
453
|
+
...current,
|
|
454
|
+
redirectUrl: wrapMuxoConnectUrl(current.redirectUrl, this.config.baseUrl, {
|
|
455
|
+
workspaceId: ctx.workspace.id,
|
|
456
|
+
providerSlug: cleanSlug,
|
|
457
|
+
externalAccountId: current.externalAccountId,
|
|
458
|
+
}),
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
return {
|
|
462
|
+
ok: true,
|
|
463
|
+
connected: false,
|
|
464
|
+
ready: false,
|
|
465
|
+
timedOut: true,
|
|
466
|
+
provider: cleanSlug,
|
|
467
|
+
status: current.status,
|
|
468
|
+
connection: current,
|
|
469
|
+
redirectUrl: current.redirectUrl,
|
|
470
|
+
polledAttempts,
|
|
471
|
+
elapsedMs: Date.now() - startedAt,
|
|
472
|
+
message: current.redirectUrl
|
|
473
|
+
? `Awaiting user OAuth authorization for ${current.providerName}. Open ${current.redirectUrl} in a browser, then re-run wait_for_connection.`
|
|
474
|
+
: `Connection for ${current.providerName} is currently ${current.status}.`,
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
// 6c. Browser OAuth callback handoff handler (GET /oauth/callback)
|
|
478
|
+
async handleOAuthCallback(params) {
|
|
479
|
+
const cleanSlug = params.provider
|
|
480
|
+
.trim()
|
|
481
|
+
.toLowerCase()
|
|
482
|
+
.replace(/^(app\.|resource\.)/, "");
|
|
483
|
+
const providerName = cleanSlug.charAt(0).toUpperCase() + cleanSlug.slice(1);
|
|
484
|
+
const ws = this.store.getWorkspace(params.workspaceId);
|
|
485
|
+
if (!ws) {
|
|
486
|
+
throw new Error(`Workspace '${params.workspaceId}' not found`);
|
|
487
|
+
}
|
|
488
|
+
const existing = this.store
|
|
489
|
+
.listConnections(ws.id)
|
|
490
|
+
.find((c) => c.providerSlug.toLowerCase() === cleanSlug);
|
|
491
|
+
const accountId = params.connectedAccountId || existing?.externalAccountId || null;
|
|
492
|
+
let resolvedStatus = "pending";
|
|
493
|
+
const composioByok = this.store.getByokKey(ws.id, "composio");
|
|
494
|
+
if (accountId) {
|
|
495
|
+
const live = await this.composioSdk.getConnectedAccountStatus(accountId, composioByok);
|
|
496
|
+
if (live) {
|
|
497
|
+
resolvedStatus = live.status;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (resolvedStatus !== "active") {
|
|
501
|
+
const liveList = await this.composioSdk.listLiveConnections(ws.id, composioByok);
|
|
502
|
+
const match = liveList.find((c) => c.providerSlug === cleanSlug);
|
|
503
|
+
if (match) {
|
|
504
|
+
resolvedStatus = match.status;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
const rawCallbackStatus = String(params.status || "").toLowerCase();
|
|
508
|
+
if (rawCallbackStatus === "failed" ||
|
|
509
|
+
rawCallbackStatus === "error" ||
|
|
510
|
+
rawCallbackStatus === "expired" ||
|
|
511
|
+
rawCallbackStatus === "cancelled" ||
|
|
512
|
+
rawCallbackStatus === "denied") {
|
|
513
|
+
resolvedStatus = "expired";
|
|
514
|
+
}
|
|
515
|
+
else if (rawCallbackStatus === "success" ||
|
|
516
|
+
rawCallbackStatus === "active" ||
|
|
517
|
+
rawCallbackStatus === "connected") {
|
|
518
|
+
resolvedStatus = "active";
|
|
519
|
+
}
|
|
520
|
+
const updated = this.store.upsertConnection({
|
|
521
|
+
workspaceId: ws.id,
|
|
522
|
+
lane: "composio_sdk",
|
|
523
|
+
providerSlug: cleanSlug,
|
|
524
|
+
providerName,
|
|
525
|
+
status: resolvedStatus,
|
|
526
|
+
externalAccountId: accountId,
|
|
527
|
+
redirectUrl: resolvedStatus === "active" ? null : existing?.redirectUrl,
|
|
528
|
+
});
|
|
529
|
+
return {
|
|
530
|
+
ok: true,
|
|
531
|
+
workspaceId: ws.id,
|
|
532
|
+
providerSlug: cleanSlug,
|
|
533
|
+
providerName,
|
|
534
|
+
status: updated.status,
|
|
535
|
+
externalAccountId: updated.externalAccountId,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
// 7. plan_action
|
|
539
|
+
async planAction(ctx, params) {
|
|
540
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
541
|
+
let tool;
|
|
542
|
+
let resolvedInput = { ...(params.input || {}) };
|
|
543
|
+
let resolvedReason = params.reason || `Planned execution of ${params.toolSlug}`;
|
|
544
|
+
try {
|
|
545
|
+
tool = await this.inspectTool(params.toolSlug);
|
|
546
|
+
}
|
|
547
|
+
catch (inspectErr) {
|
|
548
|
+
if (looksLikeToolSlug(params.toolSlug)) {
|
|
549
|
+
throw inspectErr;
|
|
550
|
+
}
|
|
551
|
+
// Natural-language fallback: if an agent or human passed plain English as toolSlug/instruction, resolve it!
|
|
552
|
+
const nlSource = `${params.toolSlug} ${params.reason || ""}`.trim();
|
|
553
|
+
const allTools = await this.listAllCatalogTools();
|
|
554
|
+
const byokEnv = this.store.getDecryptedWorkspaceEnv(ws.id);
|
|
555
|
+
const nl = await resolveNaturalLanguageIntent({
|
|
556
|
+
prompt: nlSource,
|
|
557
|
+
tools: allTools,
|
|
558
|
+
explicitInput: resolvedInput,
|
|
559
|
+
byokKeys: byokEnv,
|
|
560
|
+
});
|
|
561
|
+
if (!nl.resolved || !nl.tool) {
|
|
562
|
+
throw inspectErr;
|
|
563
|
+
}
|
|
564
|
+
tool = nl.tool;
|
|
565
|
+
resolvedInput = nl.input;
|
|
566
|
+
resolvedReason = params.reason || nl.reason || nlSource;
|
|
567
|
+
}
|
|
568
|
+
const byokActive = params.fundingMode === "muxo_funded"
|
|
569
|
+
? false
|
|
570
|
+
: this.store.hasByokForLane(ws.id, tool.lane, tool.slug);
|
|
571
|
+
const isPaidResourcePlan = isPaidCloudProvisioning(tool, resolvedInput);
|
|
572
|
+
const baseCreditCost = isPaidResourcePlan
|
|
573
|
+
? Math.max(tool.creditCost, 25)
|
|
574
|
+
: tool.creditCost;
|
|
575
|
+
const effectiveCreditCost = byokActive ? 0 : baseCreditCost;
|
|
576
|
+
if (ws.monthlyActionsUsed >= ws.monthlyActionsLimit) {
|
|
577
|
+
throw new Error(`Monthly action limit reached (${ws.monthlyActionsUsed}/${ws.monthlyActionsLimit}). Upgrade to Starter ($29/mo) to unlock 5,000 actions/mo.`);
|
|
578
|
+
}
|
|
579
|
+
if (effectiveCreditCost > ws.creditsBalance) {
|
|
580
|
+
throw new Error(`Insufficient workspace credits for ${tool.slug} (requires ${effectiveCreditCost}, available ${ws.creditsBalance}). Top up credits or configure a BYOK key.`);
|
|
581
|
+
}
|
|
582
|
+
if (effectiveCreditCost > ws.policy.maxCreditsPerRun) {
|
|
583
|
+
throw new Error(`Tool credit cost (${effectiveCreditCost}) exceeds workspace policy maxCreditsPerRun (${ws.policy.maxCreditsPerRun}).`);
|
|
584
|
+
}
|
|
585
|
+
// 1. Recall Supermemory v5 contextual brain first so Active Memory defaults can auto-fill tool parameters
|
|
586
|
+
const memoryQuery = `${tool.slug} ${tool.name} ${resolvedReason}`.trim();
|
|
587
|
+
const supermemoryByok = this.store.getByokKey(ws.id, "supermemory");
|
|
588
|
+
const memoryContext = await this.enrichMemoryContext(ws.id, await this.supermemory.getContextForWorkspace(ws.id, memoryQuery, supermemoryByok), memoryQuery);
|
|
589
|
+
const activeMemoryResult = applyActiveMemoryToToolInput({
|
|
590
|
+
toolSlug: tool.slug,
|
|
591
|
+
input: resolvedInput,
|
|
592
|
+
activeDefaults: memoryContext.activeDefaults,
|
|
593
|
+
});
|
|
594
|
+
resolvedInput = activeMemoryResult.mergedInput;
|
|
595
|
+
if (Object.keys(activeMemoryResult.autoFilledInput).length > 0) {
|
|
596
|
+
memoryContext.autoFilledInput = activeMemoryResult.autoFilledInput;
|
|
597
|
+
}
|
|
598
|
+
// 2. Evaluate Microsoft Agent Governance Pipeline:
|
|
599
|
+
// Agent -> McpSecurityScanner -> PolicyEngine -> AgentIdentity (Ed25519 DID) -> AuditLogger -> DecisionRecord
|
|
600
|
+
const decisionRecord = this.governance.evaluatePlan({
|
|
601
|
+
workspace: ws,
|
|
602
|
+
actorId: ctx.actorId,
|
|
603
|
+
tool,
|
|
604
|
+
input: resolvedInput,
|
|
605
|
+
reason: resolvedReason,
|
|
606
|
+
});
|
|
607
|
+
if (decisionRecord.verdict === "deny") {
|
|
608
|
+
throw new Error(`Microsoft Agent Governance denied plan for ${tool.slug} (rule: ${decisionRecord.matchedRule}): ${decisionRecord.reason}`);
|
|
609
|
+
}
|
|
610
|
+
const requiresHumanApproval = decisionRecord.verdict === "escalate";
|
|
611
|
+
const approvalStatus = requiresHumanApproval
|
|
612
|
+
? "pending_approval"
|
|
613
|
+
: "auto_approved";
|
|
614
|
+
const plan = this.store.createPlan({
|
|
615
|
+
workspaceId: ws.id,
|
|
616
|
+
actorId: ctx.actorId,
|
|
617
|
+
toolSlug: tool.slug,
|
|
618
|
+
upstreamSlug: tool.upstreamSlug,
|
|
619
|
+
lane: tool.lane,
|
|
620
|
+
riskLevel: tool.riskLevel,
|
|
621
|
+
input: resolvedInput,
|
|
622
|
+
reason: resolvedReason,
|
|
623
|
+
creditCost: effectiveCreditCost,
|
|
624
|
+
approvalStatus,
|
|
625
|
+
decisionRecord,
|
|
626
|
+
memoryContext,
|
|
627
|
+
});
|
|
628
|
+
// 3. Check external user OAuth connection state for Connected Apps (composio_sdk)
|
|
629
|
+
let authHandoff;
|
|
630
|
+
if (tool.lane === "composio_sdk" && tool.requiresAuth) {
|
|
631
|
+
let existingConn = this.store
|
|
632
|
+
.listConnections(ws.id)
|
|
633
|
+
.find((c) => c.lane === "composio_sdk" &&
|
|
634
|
+
c.providerSlug.toLowerCase() === tool.providerSlug.toLowerCase());
|
|
635
|
+
if (!existingConn || existingConn.status !== "active") {
|
|
636
|
+
try {
|
|
637
|
+
if (!existingConn || !existingConn.redirectUrl) {
|
|
638
|
+
existingConn = await this.startConnection(ctx, {
|
|
639
|
+
provider: tool.providerSlug,
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
catch {
|
|
644
|
+
// Keep existing connection state if offline
|
|
645
|
+
}
|
|
646
|
+
if (!existingConn || existingConn.status !== "active") {
|
|
647
|
+
const wrappedRedirectUrl = existingConn?.redirectUrl
|
|
648
|
+
? wrapMuxoConnectUrl(existingConn.redirectUrl, this.config.baseUrl, {
|
|
649
|
+
workspaceId: ws.id,
|
|
650
|
+
providerSlug: tool.providerSlug,
|
|
651
|
+
externalAccountId: existingConn.externalAccountId,
|
|
652
|
+
})
|
|
653
|
+
: null;
|
|
654
|
+
authHandoff = {
|
|
655
|
+
required: true,
|
|
656
|
+
provider: tool.providerSlug,
|
|
657
|
+
status: existingConn?.status || "pending",
|
|
658
|
+
redirectUrl: wrappedRedirectUrl,
|
|
659
|
+
externalAccountId: existingConn?.externalAccountId || null,
|
|
660
|
+
instructions: wrappedRedirectUrl
|
|
661
|
+
? `Ask the user to authorize ${tool.providerName} at ${wrappedRedirectUrl}, then call wait_for_connection({ provider: "${tool.providerSlug}" }) before executing ${plan.id}.`
|
|
662
|
+
: `Call start_connection({ provider: "${tool.providerSlug}" }) to authorize ${tool.providerName} before executing ${plan.id}.`,
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
const executeToolName = `execute_${tool.riskLevel}`;
|
|
668
|
+
let nextStep = approvalStatus === "pending_approval"
|
|
669
|
+
? `Plan ${plan.id} requires human owner approval in the Muxo Console or via approve_plan before calling ${executeToolName}.`
|
|
670
|
+
: `Plan ${plan.id} is auto-approved. Call ${executeToolName}({ plan_id: "${plan.id}" }) to execute.`;
|
|
671
|
+
if (authHandoff?.required) {
|
|
672
|
+
nextStep = `${authHandoff.instructions} Once connected, ${nextStep}`;
|
|
673
|
+
}
|
|
674
|
+
const humanHandoff = authHandoff?.required
|
|
675
|
+
? this.buildOAuthHandoff(authHandoff, tool.providerName)
|
|
676
|
+
: approvalStatus === "pending_approval"
|
|
677
|
+
? this.buildApprovalHandoff(plan)
|
|
678
|
+
: undefined;
|
|
679
|
+
const llmGuidance = this.buildPlanLlmGuidance(plan, authHandoff);
|
|
680
|
+
return {
|
|
681
|
+
plan,
|
|
682
|
+
tool,
|
|
683
|
+
decisionRecord,
|
|
684
|
+
memoryContext,
|
|
685
|
+
...(authHandoff ? { authHandoff } : {}),
|
|
686
|
+
...(humanHandoff ? { humanHandoff } : {}),
|
|
687
|
+
llmGuidance,
|
|
688
|
+
nextStep,
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
// 8. get_plan
|
|
692
|
+
getPlan(ctx, planId) {
|
|
693
|
+
const plan = this.store.getPlan(ctx.workspace.id, planId);
|
|
694
|
+
if (!plan)
|
|
695
|
+
throw new Error(`Plan '${planId}' not found in workspace.`);
|
|
696
|
+
return plan;
|
|
697
|
+
}
|
|
698
|
+
async waitForPlanApproval(ctx, params) {
|
|
699
|
+
const startedAt = Date.now();
|
|
700
|
+
const deadline = startedAt + clampWaitSeconds(params.timeoutSeconds, 15, 60) * 1000;
|
|
701
|
+
const pollIntervalMs = Math.min(2000, Math.max(100, Number(params.pollIntervalMs ?? 250)));
|
|
702
|
+
let plan = this.getPlan(ctx, params.planId);
|
|
703
|
+
while (plan.approvalStatus === "pending_approval" && Date.now() < deadline) {
|
|
704
|
+
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
705
|
+
plan = this.getPlan(ctx, params.planId);
|
|
706
|
+
}
|
|
707
|
+
return {
|
|
708
|
+
ok: true,
|
|
709
|
+
approved: APPROVED_PLAN_STATUSES.has(plan.approvalStatus),
|
|
710
|
+
rejected: plan.approvalStatus === "rejected",
|
|
711
|
+
timedOut: plan.approvalStatus === "pending_approval",
|
|
712
|
+
waitedForApproval: true,
|
|
713
|
+
approvalStatus: plan.approvalStatus,
|
|
714
|
+
approvalUrl: buildApprovalUrl(this.config.baseUrl, plan.id),
|
|
715
|
+
waitedMs: Date.now() - startedAt,
|
|
716
|
+
plan,
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
// Human owner approval / rejection
|
|
720
|
+
approvePlan(ctx, planId, decision = "approved") {
|
|
721
|
+
const plan = this.getPlan(ctx, planId);
|
|
722
|
+
if (plan.approvalStatus === "executed") {
|
|
723
|
+
throw new Error(`Plan '${planId}' has already been executed.`);
|
|
724
|
+
}
|
|
725
|
+
return this.store.updatePlanApproval({
|
|
726
|
+
workspaceId: ctx.workspace.id,
|
|
727
|
+
planId,
|
|
728
|
+
approvalStatus: decision,
|
|
729
|
+
approvedBy: ctx.user.email,
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
buildApprovalHandoff(plan) {
|
|
733
|
+
return buildApprovalHumanHandoff(this.config.baseUrl, plan);
|
|
734
|
+
}
|
|
735
|
+
buildOAuthHandoff(handoff, providerName) {
|
|
736
|
+
return buildOAuthHumanHandoff({
|
|
737
|
+
providerSlug: handoff.provider,
|
|
738
|
+
providerName,
|
|
739
|
+
redirectUrl: handoff.redirectUrl,
|
|
740
|
+
instructions: handoff.instructions,
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
buildPlanLlmGuidance(plan, authHandoff) {
|
|
744
|
+
const executeTool = `execute_${plan.riskLevel}`;
|
|
745
|
+
const nextToolCalls = [];
|
|
746
|
+
if (authHandoff?.required) {
|
|
747
|
+
nextToolCalls.push(authHandoff.redirectUrl
|
|
748
|
+
? {
|
|
749
|
+
tool: "wait_for_connection",
|
|
750
|
+
input: { provider: authHandoff.provider },
|
|
751
|
+
when: `After the user confirms they completed OAuth at ${authHandoff.redirectUrl}`,
|
|
752
|
+
}
|
|
753
|
+
: {
|
|
754
|
+
tool: "start_connection",
|
|
755
|
+
input: { provider: authHandoff.provider },
|
|
756
|
+
when: "Immediately, to obtain an OAuth redirect URL for the user",
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
if (plan.approvalStatus === "pending_approval") {
|
|
760
|
+
nextToolCalls.push({
|
|
761
|
+
tool: "get_plan",
|
|
762
|
+
input: { plan_id: plan.id, wait_for_approval: true, timeout_seconds: 25 },
|
|
763
|
+
when: `After sharing ${this.config.baseUrl}/approve/${plan.id} with the user, to wait in-turn until they click Approve`,
|
|
764
|
+
});
|
|
765
|
+
nextToolCalls.push({
|
|
766
|
+
tool: executeTool,
|
|
767
|
+
input: { plan_id: plan.id },
|
|
768
|
+
when: `Only after the user explicitly approves plan ${plan.id} (via ${this.config.baseUrl}/approve/${plan.id} or \`muxo approve ${plan.id}\`)`,
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
nextToolCalls.push({
|
|
773
|
+
tool: executeTool,
|
|
774
|
+
input: { plan_id: plan.id },
|
|
775
|
+
when: authHandoff?.required
|
|
776
|
+
? "After the connection becomes active"
|
|
777
|
+
: "Immediately; the plan is auto-approved",
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
nextToolCalls.push({
|
|
781
|
+
tool: "get_receipt",
|
|
782
|
+
input: { run_id: "<run_id from the execute response>" },
|
|
783
|
+
when: "After execution, to verify the tamper-proof receipt",
|
|
784
|
+
});
|
|
785
|
+
const summary = authHandoff?.required
|
|
786
|
+
? `Hand the OAuth link to the user, wait for the connection, then ${plan.approvalStatus === "pending_approval" ? "collect approval and " : ""}call ${executeTool}.`
|
|
787
|
+
: plan.approvalStatus === "pending_approval"
|
|
788
|
+
? `Stop and ask the user to approve plan ${plan.id}; do not call ${executeTool} until they confirm.`
|
|
789
|
+
: `Call ${executeTool} with plan_id="${plan.id}" to execute now.`;
|
|
790
|
+
return { summary, nextToolCalls };
|
|
791
|
+
}
|
|
792
|
+
async enrichMemoryContext(workspaceId, ctx, query) {
|
|
793
|
+
const localRecords = this.store.listMemoryRecords(workspaceId, 8);
|
|
794
|
+
const temporalFacts = this.store.listTemporalFacts(workspaceId, {
|
|
795
|
+
includeHistorical: false,
|
|
796
|
+
query,
|
|
797
|
+
limit: 15,
|
|
798
|
+
});
|
|
799
|
+
const recalled = ctx.recalledMemories.length > 0
|
|
800
|
+
? ctx.recalledMemories
|
|
801
|
+
: localRecords.slice(0, 5).map((r) => ({
|
|
802
|
+
documentId: r.documentId,
|
|
803
|
+
title: r.title,
|
|
804
|
+
snippet: r.content.slice(0, 240),
|
|
805
|
+
similarity: 1,
|
|
806
|
+
createdAt: r.createdAt,
|
|
807
|
+
}));
|
|
808
|
+
const staticFacts = ctx.staticFacts.length > 0
|
|
809
|
+
? ctx.staticFacts
|
|
810
|
+
: localRecords
|
|
811
|
+
.filter((r) => r.kind === "fact" || r.kind === "preference")
|
|
812
|
+
.slice(0, 5)
|
|
813
|
+
.map((r) => `${r.title}: ${r.content.slice(0, 140)}`);
|
|
814
|
+
const dynamicContext = ctx.dynamicContext.length > 0
|
|
815
|
+
? ctx.dynamicContext
|
|
816
|
+
: temporalFacts.slice(0, 6).map((f) => `${f.subject} ${f.predicate} ${f.object}`);
|
|
817
|
+
const combinedText = [
|
|
818
|
+
...staticFacts,
|
|
819
|
+
...dynamicContext,
|
|
820
|
+
...recalled.map((m) => `${m.title}: ${m.snippet}`),
|
|
821
|
+
].join("\n");
|
|
822
|
+
const poisonScan = this.governance.scanMemoryContent({
|
|
823
|
+
workspaceId,
|
|
824
|
+
sessionId: ctx.namespace,
|
|
825
|
+
role: "system",
|
|
826
|
+
content: combinedText,
|
|
827
|
+
recordInDetector: false,
|
|
828
|
+
});
|
|
829
|
+
const promptLines = [
|
|
830
|
+
`[Supermemory Brain — Namespace: ${ctx.namespace}]`,
|
|
831
|
+
];
|
|
832
|
+
if (staticFacts.length > 0) {
|
|
833
|
+
promptLines.push(`Static Facts: ${staticFacts.join(" | ")}`);
|
|
834
|
+
}
|
|
835
|
+
if (dynamicContext.length > 0) {
|
|
836
|
+
promptLines.push(`Dynamic Context: ${dynamicContext.join(" | ")}`);
|
|
837
|
+
}
|
|
838
|
+
if (temporalFacts.length > 0) {
|
|
839
|
+
promptLines.push(`Bi-Temporal Lineage (${temporalFacts.length} active): ${temporalFacts
|
|
840
|
+
.slice(0, 5)
|
|
841
|
+
.map((f) => `${f.subject} -[${f.predicate}]-> ${f.object}`)
|
|
842
|
+
.join(" | ")}`);
|
|
843
|
+
}
|
|
844
|
+
if (recalled.length > 0) {
|
|
845
|
+
promptLines.push(`Recalled Memories: ${recalled.map((m) => `${m.title}: ${m.snippet}`).join(" | ")}`);
|
|
846
|
+
}
|
|
847
|
+
const mdLines = [`## Workspace Brain (${ctx.namespace})`];
|
|
848
|
+
if (staticFacts.length > 0) {
|
|
849
|
+
mdLines.push("### Static Facts", ...staticFacts.map((f) => `- ${f}`));
|
|
850
|
+
}
|
|
851
|
+
if (temporalFacts.length > 0) {
|
|
852
|
+
mdLines.push("### Bi-Temporal Resource & Fact Lineage (Active)", ...temporalFacts
|
|
853
|
+
.slice(0, 8)
|
|
854
|
+
.map((f) => `- **${f.subject}** \`${f.predicate}\` **${f.object}** _(validFrom: ${f.validFrom})_ — ${f.summary}`));
|
|
855
|
+
}
|
|
856
|
+
const allLocalMemories = this.store.listMemoryRecords(workspaceId, 25);
|
|
857
|
+
const allActiveFacts = this.store.listTemporalFacts(workspaceId, {
|
|
858
|
+
includeHistorical: false,
|
|
859
|
+
limit: 30,
|
|
860
|
+
});
|
|
861
|
+
const inferred = extractActiveMemoryDefaults({
|
|
862
|
+
memories: allLocalMemories,
|
|
863
|
+
temporalFacts: allActiveFacts,
|
|
864
|
+
preferences: ctx.profile?.preferences || [],
|
|
865
|
+
staticFacts,
|
|
866
|
+
});
|
|
867
|
+
const mergedActiveDefaults = {
|
|
868
|
+
...(ctx.activeDefaults || {}),
|
|
869
|
+
...inferred.activeDefaults,
|
|
870
|
+
};
|
|
871
|
+
const ws = this.store.getWorkspace(workspaceId);
|
|
872
|
+
const mergedGuardrails = Array.from(new Set([
|
|
873
|
+
...(ws?.policy?.naturalLanguageRules || []),
|
|
874
|
+
...(ctx.activeGuardrails || []),
|
|
875
|
+
...inferred.activeGuardrails,
|
|
876
|
+
]));
|
|
877
|
+
if (Object.keys(mergedActiveDefaults).length > 0) {
|
|
878
|
+
promptLines.push(`Active Memory Auto-Fill Defaults: ${Object.entries(mergedActiveDefaults)
|
|
879
|
+
.map(([k, v]) => `${k}=${String(v)}`)
|
|
880
|
+
.join(", ")}`);
|
|
881
|
+
}
|
|
882
|
+
const enriched = {
|
|
883
|
+
...ctx,
|
|
884
|
+
enabled: true,
|
|
885
|
+
staticFacts,
|
|
886
|
+
dynamicContext,
|
|
887
|
+
recalledMemories: recalled,
|
|
888
|
+
relevantMemories: recalled,
|
|
889
|
+
formattedPromptContext: promptLines.join("\n"),
|
|
890
|
+
profileMarkdown: ctx.profileMarkdown || mdLines.join("\n"),
|
|
891
|
+
profile: {
|
|
892
|
+
...ctx.profile,
|
|
893
|
+
static: staticFacts,
|
|
894
|
+
dynamic: dynamicContext,
|
|
895
|
+
},
|
|
896
|
+
temporalFacts,
|
|
897
|
+
activeDefaults: mergedActiveDefaults,
|
|
898
|
+
activeGuardrails: mergedGuardrails,
|
|
899
|
+
contextPoisoningScan: {
|
|
900
|
+
safe: poisonScan.safe,
|
|
901
|
+
riskLevel: poisonScan.riskLevel,
|
|
902
|
+
findingsCount: poisonScan.findingsCount,
|
|
903
|
+
},
|
|
904
|
+
};
|
|
905
|
+
enriched.explanation = explainMemoryContext({
|
|
906
|
+
memory: enriched,
|
|
907
|
+
localCount: allLocalMemories.length,
|
|
908
|
+
activeTemporalCount: allActiveFacts.length,
|
|
909
|
+
policy: ws?.policy,
|
|
910
|
+
});
|
|
911
|
+
return enriched;
|
|
912
|
+
}
|
|
913
|
+
// 9. get_run_context
|
|
914
|
+
async getRunContext(ctx, query) {
|
|
915
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
916
|
+
const resources = this.store.listResources(ws.id);
|
|
917
|
+
const activeResources = resources.filter((r) => r.status === "active");
|
|
918
|
+
const activeEnv = this.store.getDecryptedWorkspaceEnv(ws.id);
|
|
919
|
+
const redactedEnv = {};
|
|
920
|
+
for (const [k, v] of Object.entries(activeEnv)) {
|
|
921
|
+
redactedEnv[k] =
|
|
922
|
+
v.length > 8 ? `${v.slice(0, 4)}••••${v.slice(-3)}` : "••••••••";
|
|
923
|
+
}
|
|
924
|
+
const supermemoryByok = this.store.getByokKey(ws.id, "supermemory");
|
|
925
|
+
const memoryContext = await this.enrichMemoryContext(ws.id, await this.supermemory.getContextForWorkspace(ws.id, query, supermemoryByok), query);
|
|
926
|
+
const plainEnglishPolicy = describePolicyInPlainEnglish(ws.policy);
|
|
927
|
+
const governanceStatus = this.governance.getStatus(ws);
|
|
928
|
+
const trustScore = this.governance.getWorkspaceTrustScore(ws.id);
|
|
929
|
+
const actionsRemaining = Math.max(0, ws.monthlyActionsLimit - ws.monthlyActionsUsed);
|
|
930
|
+
const defaultsList = Object.entries(memoryContext.activeDefaults || {}).map(([k, v]) => `${k}=${String(v)}`);
|
|
931
|
+
const contextHeadline = `Workspace ${ws.name} (${ws.slug}): ${ws.creditsBalance} credits ($${(ws.creditsBalance * 0.01).toFixed(2)}) available · ${plainEnglishPolicy.headline}`;
|
|
932
|
+
const contextNonTechnical = [
|
|
933
|
+
contextHeadline,
|
|
934
|
+
defaultsList.length > 0
|
|
935
|
+
? `Learned defaults ready for auto-fill: ${defaultsList.join(", ")}.`
|
|
936
|
+
: "No learned defaults set yet.",
|
|
937
|
+
activeResources.length > 0
|
|
938
|
+
? `Active cloud resources (${activeResources.length}): ${activeResources.map((r) => `${r.resourceName} (${r.providerName})`).join(", ")}.`
|
|
939
|
+
: "No cloud infrastructure resources provisioned yet.",
|
|
940
|
+
memoryContext.explanation?.nonTechnical || "",
|
|
941
|
+
]
|
|
942
|
+
.filter(Boolean)
|
|
943
|
+
.join(" ");
|
|
944
|
+
const contextTechnical = `workspace=${ws.id} (${ws.slug}, tier=${ws.tier}) | credits=${ws.creditsBalance} | actionsRemaining=${actionsRemaining}/${ws.monthlyActionsLimit} | ${plainEnglishPolicy.technical} | supermemoryNs=${memoryContext.namespace} | temporalFacts=${(memoryContext.temporalFacts || []).length} | activeDefaults=${JSON.stringify(memoryContext.activeDefaults || {})} | activeResources=${activeResources.length} | redactedEnvKeys=[${Object.keys(redactedEnv).join(", ")}] | agentDid=${governanceStatus.agentDid} | trustScore=${trustScore.overall} (${trustScore.tier})`;
|
|
945
|
+
const explanation = formatHumanFriendlyExplanation({
|
|
946
|
+
headline: contextHeadline,
|
|
947
|
+
nonTechnical: contextNonTechnical,
|
|
948
|
+
technical: contextTechnical,
|
|
949
|
+
policy: ws.policy,
|
|
950
|
+
});
|
|
951
|
+
const llmPolicyBrief = buildLlmPolicyAndGovernanceBrief(ws.policy, {
|
|
952
|
+
workspaceName: ws.name,
|
|
953
|
+
workspaceSlug: ws.slug,
|
|
954
|
+
activeDefaults: memoryContext.activeDefaults || {},
|
|
955
|
+
trustScore: { overall: trustScore.overall, tier: trustScore.tier },
|
|
956
|
+
agentDid: governanceStatus.agentDid,
|
|
957
|
+
});
|
|
958
|
+
const llmSystemPrompt = [
|
|
959
|
+
`# Muxo Governed Workspace Context (${ws.name} / ${ws.slug})`,
|
|
960
|
+
`- **Language Preference**: ${ws.policy.languageMode || "hybrid"}${ws.policy.customInstructions?.length ? ` (Custom Instructions: ${ws.policy.customInstructions.join("; ")})` : ""}`,
|
|
961
|
+
`- **Safety Guardrails**: ${plainEnglishPolicy.preferred}`,
|
|
962
|
+
`- **Learned Memory Defaults (Auto-Fill)**: ${defaultsList.length > 0 ? defaultsList.join(", ") : "none"}`,
|
|
963
|
+
`- **Credits & Quota**: ${ws.creditsBalance} credits ($${(ws.creditsBalance * 0.01).toFixed(2)}) available, ${actionsRemaining} monthly actions remaining`,
|
|
964
|
+
`- **Active Cloud Resources**: ${activeResources.length > 0 ? activeResources.map((r) => `${r.resourceName} (${r.serviceSlug})`).join(", ") : "none"}`,
|
|
965
|
+
llmPolicyBrief.llmSystemDirective,
|
|
966
|
+
memoryContext.formattedPromptContext,
|
|
967
|
+
].join("\n");
|
|
968
|
+
return {
|
|
969
|
+
workspaceId: ws.id,
|
|
970
|
+
workspaceSlug: ws.slug,
|
|
971
|
+
projectsEnvironment: ws.projectsEnvName,
|
|
972
|
+
policy: ws.policy,
|
|
973
|
+
plainEnglishPolicy,
|
|
974
|
+
llmPolicyBrief,
|
|
975
|
+
languageMode: ws.policy.languageMode || "hybrid",
|
|
976
|
+
customInstructions: ws.policy.customInstructions || [],
|
|
977
|
+
activeDefaults: (memoryContext.activeDefaults ||
|
|
978
|
+
{}),
|
|
979
|
+
activeGuardrails: memoryContext.activeGuardrails || [],
|
|
980
|
+
creditsBalance: ws.creditsBalance,
|
|
981
|
+
monthlyActionsRemaining: actionsRemaining,
|
|
982
|
+
provisionedResources: activeResources,
|
|
983
|
+
environmentVariablesRedacted: redactedEnv,
|
|
984
|
+
supermemoryBrain: memoryContext,
|
|
985
|
+
temporalFacts: memoryContext.temporalFacts || [],
|
|
986
|
+
governance: governanceStatus,
|
|
987
|
+
trustScore,
|
|
988
|
+
llmSystemPrompt,
|
|
989
|
+
explanation,
|
|
990
|
+
envPullHint: "Run `muxo env pull` or call GET /api/v1/resources/env to write decrypted credentials to your local .env file.",
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
// Supermemory Workspace Brain Operations
|
|
994
|
+
async getMemoryOverview(ctx, query) {
|
|
995
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
996
|
+
const supermemoryByok = this.store.getByokKey(ctx.workspace.id, "supermemory");
|
|
997
|
+
const context = await this.enrichMemoryContext(ctx.workspace.id, await this.supermemory.getContextForWorkspace(ctx.workspace.id, query, supermemoryByok), query);
|
|
998
|
+
const recentMemories = this.store.listMemoryRecords(ctx.workspace.id, 25);
|
|
999
|
+
const allFacts = this.store.listTemporalFacts(ctx.workspace.id, {
|
|
1000
|
+
includeHistorical: true,
|
|
1001
|
+
query,
|
|
1002
|
+
limit: 50,
|
|
1003
|
+
});
|
|
1004
|
+
const activeFacts = allFacts.filter((f) => f.validTo === null);
|
|
1005
|
+
const historicalFacts = allFacts.filter((f) => f.validTo !== null);
|
|
1006
|
+
const explanation = explainMemoryContext({
|
|
1007
|
+
memory: context,
|
|
1008
|
+
localCount: recentMemories.length,
|
|
1009
|
+
activeTemporalCount: activeFacts.length,
|
|
1010
|
+
historicalTemporalCount: historicalFacts.length,
|
|
1011
|
+
policy: ws.policy,
|
|
1012
|
+
});
|
|
1013
|
+
return {
|
|
1014
|
+
enabled: context.enabled,
|
|
1015
|
+
namespace: context.namespace,
|
|
1016
|
+
fundingSource: context.fundingSource || "local_fallback",
|
|
1017
|
+
context,
|
|
1018
|
+
activeDefaults: (context.activeDefaults || {}),
|
|
1019
|
+
activeGuardrails: context.activeGuardrails || [],
|
|
1020
|
+
explanation,
|
|
1021
|
+
recentMemories,
|
|
1022
|
+
memories: recentMemories,
|
|
1023
|
+
temporalFacts: activeFacts,
|
|
1024
|
+
historicalFacts,
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
async addWorkspaceMemory(ctx, params) {
|
|
1028
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1029
|
+
const kind = params.kind || "fact";
|
|
1030
|
+
// Microsoft Agent Governance OWASP ASI-06 ContextPoisoningDetector guard on memory ingestion
|
|
1031
|
+
const poisonScan = this.governance.scanMemoryContent({
|
|
1032
|
+
workspaceId: ws.id,
|
|
1033
|
+
sessionId: this.supermemory.getWorkspaceNamespace(ws.id),
|
|
1034
|
+
role: "user",
|
|
1035
|
+
content: `${params.title}\n${params.content}`,
|
|
1036
|
+
recordInDetector: true,
|
|
1037
|
+
});
|
|
1038
|
+
if (!poisonScan.safe && ws.policy.mode === "enforce") {
|
|
1039
|
+
this.governance.recordAgentOutcome(ws.id, false, 0.1);
|
|
1040
|
+
throw new Error(`Blocked by Microsoft ContextPoisoningDetector (OWASP ASI-06, risk=${poisonScan.riskLevel}): ${poisonScan.findings
|
|
1041
|
+
.map((f) => `${f.patternName} (${f.description})`)
|
|
1042
|
+
.join("; ")}`);
|
|
1043
|
+
}
|
|
1044
|
+
const supermemoryByok = this.store.getByokKey(ws.id, "supermemory");
|
|
1045
|
+
const remote = await this.supermemory.addMemory({
|
|
1046
|
+
workspaceId: ws.id,
|
|
1047
|
+
title: params.title,
|
|
1048
|
+
content: params.content,
|
|
1049
|
+
kind,
|
|
1050
|
+
metadata: params.metadata,
|
|
1051
|
+
apiKeyOverride: supermemoryByok,
|
|
1052
|
+
dreaming: "instant",
|
|
1053
|
+
});
|
|
1054
|
+
const record = this.store.saveMemoryRecord({
|
|
1055
|
+
workspaceId: ws.id,
|
|
1056
|
+
namespace: remote.namespace,
|
|
1057
|
+
documentId: remote.documentId,
|
|
1058
|
+
title: params.title,
|
|
1059
|
+
content: remote.sanitizedContent,
|
|
1060
|
+
kind,
|
|
1061
|
+
metadata: {
|
|
1062
|
+
...(params.metadata || {}),
|
|
1063
|
+
supermemoryStatus: remote.status,
|
|
1064
|
+
fundingSource: remote.fundingSource,
|
|
1065
|
+
contextPoisoningScan: {
|
|
1066
|
+
safe: poisonScan.safe,
|
|
1067
|
+
riskLevel: poisonScan.riskLevel,
|
|
1068
|
+
findingsCount: poisonScan.findingsCount,
|
|
1069
|
+
},
|
|
1070
|
+
},
|
|
1071
|
+
});
|
|
1072
|
+
// Record bi-temporal entity/relationship lineage (auto-superseding prior facts for same subject+predicate)
|
|
1073
|
+
const normalizedTitlePred = params.title
|
|
1074
|
+
.trim()
|
|
1075
|
+
.toUpperCase()
|
|
1076
|
+
.replace(/[^A-Z0-9]+/g, "_")
|
|
1077
|
+
.replace(/^_|_$/g, "")
|
|
1078
|
+
.slice(0, 48);
|
|
1079
|
+
const subject = params.subject ||
|
|
1080
|
+
(typeof params.metadata?.subject === "string"
|
|
1081
|
+
? params.metadata.subject
|
|
1082
|
+
: `workspace:${ws.slug}`);
|
|
1083
|
+
const predicate = params.predicate ||
|
|
1084
|
+
(typeof params.metadata?.predicate === "string"
|
|
1085
|
+
? params.metadata.predicate
|
|
1086
|
+
: normalizedTitlePred || kind.toUpperCase());
|
|
1087
|
+
const object = params.object ||
|
|
1088
|
+
(typeof params.metadata?.object === "string"
|
|
1089
|
+
? params.metadata.object
|
|
1090
|
+
: remote.sanitizedContent.slice(0, 160));
|
|
1091
|
+
this.store.upsertTemporalFact({
|
|
1092
|
+
workspaceId: ws.id,
|
|
1093
|
+
subject,
|
|
1094
|
+
predicate,
|
|
1095
|
+
object,
|
|
1096
|
+
summary: `${params.title}: ${remote.sanitizedContent.slice(0, 240)}`,
|
|
1097
|
+
confidence: 0.95,
|
|
1098
|
+
sourceKind: kind,
|
|
1099
|
+
sourceReferenceId: record.id,
|
|
1100
|
+
metadata: params.metadata || {},
|
|
1101
|
+
});
|
|
1102
|
+
return record;
|
|
1103
|
+
}
|
|
1104
|
+
async deleteWorkspaceMemory(ctx, idOrDocId) {
|
|
1105
|
+
const local = this.store.deleteMemoryRecord(ctx.workspace.id, idOrDocId);
|
|
1106
|
+
const docId = local?.documentId || idOrDocId;
|
|
1107
|
+
const supermemoryByok = this.store.getByokKey(ctx.workspace.id, "supermemory");
|
|
1108
|
+
const remote = await this.supermemory.deleteMemory(ctx.workspace.id, docId, supermemoryByok);
|
|
1109
|
+
return {
|
|
1110
|
+
deleted: Boolean(local || remote.deleted),
|
|
1111
|
+
record: local,
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
async forgetWorkspaceMemoryMatching(ctx, query) {
|
|
1115
|
+
const localPruned = this.store.forgetMatchingMemoryRecords(ctx.workspace.id, query);
|
|
1116
|
+
const supermemoryByok = this.store.getByokKey(ctx.workspace.id, "supermemory");
|
|
1117
|
+
const remotePruned = await this.supermemory.forgetMatching({
|
|
1118
|
+
workspaceId: ctx.workspace.id,
|
|
1119
|
+
query,
|
|
1120
|
+
apiKeyOverride: supermemoryByok,
|
|
1121
|
+
});
|
|
1122
|
+
return {
|
|
1123
|
+
query,
|
|
1124
|
+
deletedLocalCount: localPruned.deletedMemories.length,
|
|
1125
|
+
invalidatedFactsCount: localPruned.invalidatedFacts.length,
|
|
1126
|
+
deletedRemoteIds: remotePruned.deletedRemoteIds,
|
|
1127
|
+
deletedMemories: localPruned.deletedMemories,
|
|
1128
|
+
invalidatedFacts: localPruned.invalidatedFacts,
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Synthesizes a Bi-Temporal Knowledge Graph (entities, active relationships, superseded historical relationships,
|
|
1133
|
+
* Mermaid diagram, and human + technical explanations) directly from SQLite temporal_facts, memories, resources, and connections.
|
|
1134
|
+
*/
|
|
1135
|
+
async getMemoryGraph(ctx, query) {
|
|
1136
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1137
|
+
const overview = await this.getMemoryOverview(ctx, query);
|
|
1138
|
+
return buildMemoryKnowledgeGraph({
|
|
1139
|
+
workspace: ws,
|
|
1140
|
+
namespace: overview.namespace,
|
|
1141
|
+
query,
|
|
1142
|
+
facts: this.store.listTemporalFacts(ws.id, {
|
|
1143
|
+
includeHistorical: true,
|
|
1144
|
+
query,
|
|
1145
|
+
limit: 100,
|
|
1146
|
+
}),
|
|
1147
|
+
resources: this.store.listResources(ws.id),
|
|
1148
|
+
activeConnections: this.store
|
|
1149
|
+
.listConnections(ws.id)
|
|
1150
|
+
.filter((c) => c.status === "active"),
|
|
1151
|
+
memoriesCount: overview.memories.length,
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
async activateWorkspaceKillSwitch(ctx, params) {
|
|
1155
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1156
|
+
const result = await this.governance.activateKillSwitch(ws.id, params.reason, params.action || "quarantine");
|
|
1157
|
+
const updatedWs = this.store.updateWorkspacePolicy(ws.id, {
|
|
1158
|
+
...ws.policy,
|
|
1159
|
+
killSwitchActive: true,
|
|
1160
|
+
killSwitchReason: params.reason,
|
|
1161
|
+
});
|
|
1162
|
+
return {
|
|
1163
|
+
activated: true,
|
|
1164
|
+
killSwitch: result,
|
|
1165
|
+
policy: updatedWs.policy,
|
|
1166
|
+
trustScore: this.governance.getWorkspaceTrustScore(ws.id),
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
resumeWorkspaceKillSwitch(ctx) {
|
|
1170
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1171
|
+
const resumed = this.governance.resumeKillSwitch(ws.id);
|
|
1172
|
+
const updatedWs = this.store.updateWorkspacePolicy(ws.id, {
|
|
1173
|
+
...ws.policy,
|
|
1174
|
+
killSwitchActive: false,
|
|
1175
|
+
killSwitchReason: null,
|
|
1176
|
+
});
|
|
1177
|
+
return {
|
|
1178
|
+
...resumed,
|
|
1179
|
+
resumed: true,
|
|
1180
|
+
policy: updatedWs.policy,
|
|
1181
|
+
trustScore: this.governance.getWorkspaceTrustScore(ws.id),
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
// 10-15. execute_<risk_level>
|
|
1185
|
+
async executePlanForRisk(ctx, planId, expectedRisk) {
|
|
1186
|
+
const plan = this.getPlan(ctx, planId);
|
|
1187
|
+
const isProjectsWriteCompatible = plan.lane === "stripe_projects" &&
|
|
1188
|
+
plan.riskLevel === "provisioning" &&
|
|
1189
|
+
expectedRisk === "write";
|
|
1190
|
+
if (plan.riskLevel !== expectedRisk && !isProjectsWriteCompatible) {
|
|
1191
|
+
throw new Error(`Risk lane mismatch: Plan '${plan.id}' has risk level '${plan.riskLevel}', so it must be executed with 'execute_${plan.riskLevel}' instead of 'execute_${expectedRisk}'.`);
|
|
1192
|
+
}
|
|
1193
|
+
if (plan.runId) {
|
|
1194
|
+
const existingRun = this.store.getRun(ctx.workspace.id, plan.runId);
|
|
1195
|
+
const existingReceipt = this.store.getReceipt(ctx.workspace.id, plan.runId);
|
|
1196
|
+
if (existingRun && existingReceipt) {
|
|
1197
|
+
return { run: existingRun, receipt: existingReceipt };
|
|
1198
|
+
}
|
|
1199
|
+
throw new Error(`Plan '${plan.id}' was already claimed by run '${plan.runId}' (execute-once invariant).`);
|
|
1200
|
+
}
|
|
1201
|
+
if (plan.approvalStatus === "pending_approval") {
|
|
1202
|
+
throw approvalRequiredError(this.config.baseUrl, plan);
|
|
1203
|
+
}
|
|
1204
|
+
if (plan.approvalStatus === "rejected") {
|
|
1205
|
+
throw new Error(`Plan '${plan.id}' was rejected and cannot be executed.`);
|
|
1206
|
+
}
|
|
1207
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1208
|
+
const tool = await this.inspectTool(plan.toolSlug);
|
|
1209
|
+
const explicitFunding = plan.supplierSettlement?.fundingMode;
|
|
1210
|
+
const byokActive = explicitFunding === "byok"
|
|
1211
|
+
? true
|
|
1212
|
+
: explicitFunding === "muxo_funded"
|
|
1213
|
+
? false
|
|
1214
|
+
: this.store.hasByokForLane(ws.id, tool.lane, tool.slug);
|
|
1215
|
+
const effectiveCreditCost = byokActive
|
|
1216
|
+
? 0
|
|
1217
|
+
: plan.creditCost > 0
|
|
1218
|
+
? plan.creditCost
|
|
1219
|
+
: tool.creditCost;
|
|
1220
|
+
if (ws.monthlyActionsUsed >= ws.monthlyActionsLimit) {
|
|
1221
|
+
throw new Error(`Monthly action limit reached (${ws.monthlyActionsUsed}/${ws.monthlyActionsLimit}). Upgrade to Starter ($29/mo) to unlock 5,000 actions/mo.`);
|
|
1222
|
+
}
|
|
1223
|
+
if (effectiveCreditCost > ws.creditsBalance) {
|
|
1224
|
+
throw new Error(`Insufficient workspace credits to execute plan '${plan.id}' for ${tool.slug} (requires ${effectiveCreditCost}, available ${ws.creditsBalance}). Top up credits or configure a BYOK key.`);
|
|
1225
|
+
}
|
|
1226
|
+
const execDecisionRecord = this.governance.evaluateExecution({
|
|
1227
|
+
workspace: ws,
|
|
1228
|
+
actorId: ctx.actorId,
|
|
1229
|
+
plan,
|
|
1230
|
+
tool,
|
|
1231
|
+
});
|
|
1232
|
+
if (execDecisionRecord.verdict === "deny") {
|
|
1233
|
+
throw new Error(`Microsoft Agent Governance denied execution of plan '${plan.id}': ${execDecisionRecord.reason}`);
|
|
1234
|
+
}
|
|
1235
|
+
let activeConnId = null;
|
|
1236
|
+
if (plan.lane === "composio_sdk" && tool.requiresAuth) {
|
|
1237
|
+
let connections = this.store.listConnections(ctx.workspace.id).map((c) => c.lane === "composio_sdk" && c.redirectUrl
|
|
1238
|
+
? {
|
|
1239
|
+
...c,
|
|
1240
|
+
redirectUrl: wrapMuxoConnectUrl(c.redirectUrl, this.config.baseUrl, {
|
|
1241
|
+
workspaceId: c.workspaceId,
|
|
1242
|
+
providerSlug: c.providerSlug,
|
|
1243
|
+
externalAccountId: c.externalAccountId,
|
|
1244
|
+
}),
|
|
1245
|
+
}
|
|
1246
|
+
: c);
|
|
1247
|
+
let activeConn = connections.find((c) => c.lane === "composio_sdk" &&
|
|
1248
|
+
c.providerSlug === tool.providerSlug &&
|
|
1249
|
+
c.status === "active");
|
|
1250
|
+
if (!activeConn) {
|
|
1251
|
+
await this.waitForConnection(ctx, {
|
|
1252
|
+
provider: tool.providerSlug,
|
|
1253
|
+
timeoutSeconds: 0,
|
|
1254
|
+
}).catch(() => { });
|
|
1255
|
+
connections = this.store.listConnections(ctx.workspace.id);
|
|
1256
|
+
activeConn = connections.find((c) => c.lane === "composio_sdk" &&
|
|
1257
|
+
c.providerSlug === tool.providerSlug &&
|
|
1258
|
+
c.status === "active");
|
|
1259
|
+
}
|
|
1260
|
+
if (!activeConn) {
|
|
1261
|
+
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}'.`);
|
|
1262
|
+
}
|
|
1263
|
+
activeConnId = activeConn.externalAccountId;
|
|
1264
|
+
}
|
|
1265
|
+
const run = this.store.createRun(plan, ctx.actorId);
|
|
1266
|
+
const startedMs = Date.now();
|
|
1267
|
+
try {
|
|
1268
|
+
let output;
|
|
1269
|
+
if (plan.toolSlug.startsWith("billing.")) {
|
|
1270
|
+
output = await this.stripeBilling.executeTool({
|
|
1271
|
+
workspace: ws,
|
|
1272
|
+
customerEmail: ctx.user.email,
|
|
1273
|
+
toolSlug: plan.toolSlug,
|
|
1274
|
+
input: plan.input,
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
else if (plan.lane === "composio_instant") {
|
|
1278
|
+
const composioByok = byokActive
|
|
1279
|
+
? this.store.getByokKey(ctx.workspace.id, "composio")
|
|
1280
|
+
: null;
|
|
1281
|
+
const exaByok = byokActive
|
|
1282
|
+
? this.store.getByokKey(ctx.workspace.id, "exa")
|
|
1283
|
+
: null;
|
|
1284
|
+
const firecrawlByok = byokActive
|
|
1285
|
+
? this.store.getByokKey(ctx.workspace.id, "firecrawl")
|
|
1286
|
+
: null;
|
|
1287
|
+
output = await this.composioInstant.executeTool({
|
|
1288
|
+
workspaceId: ctx.workspace.id,
|
|
1289
|
+
tool,
|
|
1290
|
+
input: plan.input,
|
|
1291
|
+
composioApiKey: composioByok,
|
|
1292
|
+
exaApiKey: exaByok,
|
|
1293
|
+
firecrawlApiKey: firecrawlByok,
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
else if (plan.lane === "composio_sdk") {
|
|
1297
|
+
const composioByok = byokActive
|
|
1298
|
+
? this.store.getByokKey(ctx.workspace.id, "composio")
|
|
1299
|
+
: null;
|
|
1300
|
+
output = await this.composioSdk.executeTool({
|
|
1301
|
+
workspaceId: ctx.workspace.id,
|
|
1302
|
+
tool,
|
|
1303
|
+
input: plan.input,
|
|
1304
|
+
connectedAccountId: activeConnId,
|
|
1305
|
+
composioApiKey: composioByok,
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
else if (plan.lane === "stripe_projects") {
|
|
1309
|
+
const stripeByok = byokActive
|
|
1310
|
+
? this.store.getByokKey(ctx.workspace.id, "stripe")
|
|
1311
|
+
: null;
|
|
1312
|
+
const enrichedInput = { ...plan.input };
|
|
1313
|
+
if (!enrichedInput.resource_id &&
|
|
1314
|
+
(plan.toolSlug === "resource.projects.rotate_credentials" ||
|
|
1315
|
+
plan.toolSlug === "resource.projects.deprovision" ||
|
|
1316
|
+
plan.toolSlug === "resource.projects.reveal_access_configuration")) {
|
|
1317
|
+
const rName = String(enrichedInput.resource_name ||
|
|
1318
|
+
enrichedInput.database_name ||
|
|
1319
|
+
enrichedInput.name ||
|
|
1320
|
+
"")
|
|
1321
|
+
.trim()
|
|
1322
|
+
.toLowerCase();
|
|
1323
|
+
if (rName) {
|
|
1324
|
+
const matchedRes = this.store
|
|
1325
|
+
.listResources(ctx.workspace.id)
|
|
1326
|
+
.find((r) => r.resourceName.toLowerCase() === rName &&
|
|
1327
|
+
r.externalResourceId);
|
|
1328
|
+
if (matchedRes?.externalResourceId) {
|
|
1329
|
+
enrichedInput.resource_id = matchedRes.externalResourceId;
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
const projResult = await this.stripeProjects.executeTool({
|
|
1334
|
+
workspaceId: ctx.workspace.id,
|
|
1335
|
+
tool,
|
|
1336
|
+
input: enrichedInput,
|
|
1337
|
+
stripeApiKey: stripeByok,
|
|
1338
|
+
});
|
|
1339
|
+
output = projResult.output;
|
|
1340
|
+
if (projResult.provisionedResource) {
|
|
1341
|
+
this.store.upsertResource({
|
|
1342
|
+
workspaceId: ctx.workspace.id,
|
|
1343
|
+
...projResult.provisionedResource,
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
else {
|
|
1348
|
+
throw new Error(`Unsupported execution lane: ${plan.lane}`);
|
|
1349
|
+
}
|
|
1350
|
+
const durationMs = Date.now() - startedMs;
|
|
1351
|
+
this.governance.recordAgentOutcome(ctx.workspace.id, true, 0.05);
|
|
1352
|
+
const finalized = this.store.finalizeRun({
|
|
1353
|
+
workspaceId: ctx.workspace.id,
|
|
1354
|
+
runId: run.id,
|
|
1355
|
+
status: "succeeded",
|
|
1356
|
+
output,
|
|
1357
|
+
actionsMetered: 1,
|
|
1358
|
+
creditsMetered: effectiveCreditCost,
|
|
1359
|
+
durationMs,
|
|
1360
|
+
decisionRecord: execDecisionRecord,
|
|
1361
|
+
});
|
|
1362
|
+
finalized.receipt.agenticReceipt =
|
|
1363
|
+
this.governance.buildPortableAgenticReceipt({
|
|
1364
|
+
receipt: finalized.receipt,
|
|
1365
|
+
run: finalized.run,
|
|
1366
|
+
workspace: ws,
|
|
1367
|
+
});
|
|
1368
|
+
finalized.receipt.explanation = this.explainReceipt(finalized.receipt, {
|
|
1369
|
+
signatureValid: this.store.verifyReceipt(finalized.receipt),
|
|
1370
|
+
governanceSignatureValid: true,
|
|
1371
|
+
agenticReceiptValid: true,
|
|
1372
|
+
}, ws.policy);
|
|
1373
|
+
this.store.upsertTemporalFact({
|
|
1374
|
+
workspaceId: ctx.workspace.id,
|
|
1375
|
+
subject: `tool:${plan.toolSlug}`,
|
|
1376
|
+
predicate: "LAST_EXECUTION",
|
|
1377
|
+
object: `succeeded:${finalized.run.id}`,
|
|
1378
|
+
summary: `Executed ${plan.toolSlug} (${plan.lane}, risk=${plan.riskLevel}) -> succeeded [receipt=${finalized.receipt.id}]`,
|
|
1379
|
+
confidence: 1.0,
|
|
1380
|
+
sourceKind: "execution_outcome",
|
|
1381
|
+
sourceReferenceId: finalized.run.id,
|
|
1382
|
+
metadata: {
|
|
1383
|
+
runId: finalized.run.id,
|
|
1384
|
+
receiptId: finalized.receipt.id,
|
|
1385
|
+
toolSlug: plan.toolSlug,
|
|
1386
|
+
lane: plan.lane,
|
|
1387
|
+
riskLevel: plan.riskLevel,
|
|
1388
|
+
},
|
|
1389
|
+
});
|
|
1390
|
+
// Record secret-redacted post-run memory in Supermemory for stateful actions
|
|
1391
|
+
if (plan.riskLevel !== "read") {
|
|
1392
|
+
const summary = `Executed ${plan.toolSlug} (${plan.lane}, risk=${plan.riskLevel}) for reason "${plan.reason}" -> status=succeeded (receipt=${finalized.receipt.id}).`;
|
|
1393
|
+
const supermemoryByok = this.store.getByokKey(ctx.workspace.id, "supermemory");
|
|
1394
|
+
this.supermemory
|
|
1395
|
+
.recordExecutionMemory({
|
|
1396
|
+
workspaceId: ctx.workspace.id,
|
|
1397
|
+
toolSlug: plan.toolSlug,
|
|
1398
|
+
lane: plan.lane,
|
|
1399
|
+
riskLevel: plan.riskLevel,
|
|
1400
|
+
reason: plan.reason,
|
|
1401
|
+
status: "succeeded",
|
|
1402
|
+
summary,
|
|
1403
|
+
apiKeyOverride: supermemoryByok,
|
|
1404
|
+
})
|
|
1405
|
+
.then((docId) => {
|
|
1406
|
+
if (docId) {
|
|
1407
|
+
this.store.saveMemoryRecord({
|
|
1408
|
+
workspaceId: ctx.workspace.id,
|
|
1409
|
+
namespace: this.supermemory.getWorkspaceNamespace(ctx.workspace.id),
|
|
1410
|
+
documentId: docId,
|
|
1411
|
+
title: `Execution: ${plan.toolSlug} (succeeded)`,
|
|
1412
|
+
content: this.supermemory.sanitizeContent(summary),
|
|
1413
|
+
kind: "execution_outcome",
|
|
1414
|
+
metadata: {
|
|
1415
|
+
runId: finalized.run.id,
|
|
1416
|
+
receiptId: finalized.receipt.id,
|
|
1417
|
+
toolSlug: plan.toolSlug,
|
|
1418
|
+
},
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
})
|
|
1422
|
+
.catch(() => { });
|
|
1423
|
+
}
|
|
1424
|
+
return finalized;
|
|
1425
|
+
}
|
|
1426
|
+
catch (err) {
|
|
1427
|
+
const durationMs = Date.now() - startedMs;
|
|
1428
|
+
this.governance.recordAgentOutcome(ctx.workspace.id, false, 0.08);
|
|
1429
|
+
const errorMessage = String(err?.message || err || "Execution failed");
|
|
1430
|
+
const failedFinalized = this.store.finalizeRun({
|
|
1431
|
+
workspaceId: ctx.workspace.id,
|
|
1432
|
+
runId: run.id,
|
|
1433
|
+
status: "failed",
|
|
1434
|
+
output: { error: errorMessage },
|
|
1435
|
+
error: errorMessage,
|
|
1436
|
+
actionsMetered: 1,
|
|
1437
|
+
creditsMetered: 0,
|
|
1438
|
+
durationMs,
|
|
1439
|
+
decisionRecord: execDecisionRecord,
|
|
1440
|
+
});
|
|
1441
|
+
failedFinalized.receipt.agenticReceipt =
|
|
1442
|
+
this.governance.buildPortableAgenticReceipt({
|
|
1443
|
+
receipt: failedFinalized.receipt,
|
|
1444
|
+
run: failedFinalized.run,
|
|
1445
|
+
workspace: ws,
|
|
1446
|
+
});
|
|
1447
|
+
failedFinalized.receipt.explanation = this.explainReceipt(failedFinalized.receipt, {
|
|
1448
|
+
signatureValid: this.store.verifyReceipt(failedFinalized.receipt),
|
|
1449
|
+
governanceSignatureValid: true,
|
|
1450
|
+
agenticReceiptValid: true,
|
|
1451
|
+
}, ws.policy);
|
|
1452
|
+
return failedFinalized;
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
// 16. get_run
|
|
1456
|
+
getRun(ctx, runId) {
|
|
1457
|
+
const run = this.store.getRun(ctx.workspace.id, runId);
|
|
1458
|
+
if (!run)
|
|
1459
|
+
throw new Error(`Run '${runId}' not found in workspace.`);
|
|
1460
|
+
return run;
|
|
1461
|
+
}
|
|
1462
|
+
// 17. cancel_run
|
|
1463
|
+
cancelRun(ctx, runId, reason) {
|
|
1464
|
+
const cleanId = String(runId || "").trim();
|
|
1465
|
+
if (!cleanId)
|
|
1466
|
+
throw new Error("run_id is required to cancel a run.");
|
|
1467
|
+
const run = this.getRun(ctx, cleanId);
|
|
1468
|
+
if (run.status === "cancelled") {
|
|
1469
|
+
const existing = this.store.getReceipt(ctx.workspace.id, run.id);
|
|
1470
|
+
if (existing) {
|
|
1471
|
+
return {
|
|
1472
|
+
ok: true,
|
|
1473
|
+
cancelled: true,
|
|
1474
|
+
alreadyCancelled: true,
|
|
1475
|
+
previousStatus: "cancelled",
|
|
1476
|
+
run,
|
|
1477
|
+
receipt: existing,
|
|
1478
|
+
signatureValid: this.store.verifyReceipt(existing),
|
|
1479
|
+
message: `Run '${run.id}' was already cancelled.`,
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
const previousStatus = run.status;
|
|
1484
|
+
const cancelled = this.store.cancelRun({
|
|
1485
|
+
workspaceId: ctx.workspace.id,
|
|
1486
|
+
runId: run.id,
|
|
1487
|
+
cancelledBy: ctx.actorId,
|
|
1488
|
+
reason: reason?.trim() || undefined,
|
|
1489
|
+
});
|
|
1490
|
+
const completedEarlier = previousStatus === "succeeded" || previousStatus === "failed";
|
|
1491
|
+
return {
|
|
1492
|
+
ok: true,
|
|
1493
|
+
cancelled: true,
|
|
1494
|
+
alreadyCancelled: false,
|
|
1495
|
+
previousStatus,
|
|
1496
|
+
run: cancelled.run,
|
|
1497
|
+
receipt: cancelled.receipt,
|
|
1498
|
+
signatureValid: this.store.verifyReceipt(cancelled.receipt),
|
|
1499
|
+
message: completedEarlier
|
|
1500
|
+
? `Run '${run.id}' had already ${previousStatus}; it is now marked cancelled with a signed cancellation receipt (${cancelled.receipt.id}). Upstream side effects are not reversed.`
|
|
1501
|
+
: `Run '${run.id}' cancelled with signed cancellation receipt ${cancelled.receipt.id}.`,
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
explainReceipt(receipt, verification, policy, languageMode) {
|
|
1505
|
+
const dollars = (receipt.creditsMetered * 0.01).toFixed(2);
|
|
1506
|
+
const fundingMode = receipt.supplierSettlement?.fundingMode || "muxo_funded";
|
|
1507
|
+
const fundingPlain = fundingMode === "byok"
|
|
1508
|
+
? "0 credits charged (settled directly using your BYOK key)"
|
|
1509
|
+
: `${receipt.creditsMetered} credit${receipt.creditsMetered === 1 ? "" : "s"} ($${dollars} USD, settled via Muxo master wallet)`;
|
|
1510
|
+
const agenticOk = verification.agenticReceiptValid ?? true;
|
|
1511
|
+
const verified = verification.signatureValid &&
|
|
1512
|
+
verification.governanceSignatureValid &&
|
|
1513
|
+
agenticOk;
|
|
1514
|
+
const headline = receipt.status === "succeeded"
|
|
1515
|
+
? `Verified Receipt (${receipt.id}): ${receipt.toolSlug} succeeded and passed all safety & integrity checks.`
|
|
1516
|
+
: receipt.status === "failed"
|
|
1517
|
+
? `Verified Receipt (${receipt.id}): ${receipt.toolSlug} failed (0 credits charged).`
|
|
1518
|
+
: `Verified Receipt (${receipt.id}): ${receipt.toolSlug} was cancelled.`;
|
|
1519
|
+
const nonTechnical = [
|
|
1520
|
+
headline,
|
|
1521
|
+
`Action: ${receipt.toolSlug} (${receipt.riskLevel} risk lane) at ${receipt.issuedAt}.`,
|
|
1522
|
+
`Cost & billing: ${fundingPlain}.`,
|
|
1523
|
+
verified
|
|
1524
|
+
? "Tamper-proof check: Verified (HMAC vault signature, portable Ed25519 AgenticReceipt envelope, and governance audit trail all match)."
|
|
1525
|
+
: "Tamper-proof check: Warning (signature verification mismatch detected).",
|
|
1526
|
+
].join(" ");
|
|
1527
|
+
const technical = `receiptId=${receipt.id} | runId=${receipt.runId} | planId=${receipt.planId} | toolSlug=${receipt.toolSlug} | lane=${receipt.lane} | riskLevel=${receipt.riskLevel} | status=${receipt.status} | creditsMetered=${receipt.creditsMetered} ($${dollars}) | fundingMode=${fundingMode} | inputSha256=${receipt.inputHash.slice(0, 16)}... | outputSha256=${receipt.outputHash.slice(0, 16)}... | hashChain=(${receipt.prevReceiptHash.slice(0, 16)}... -> ${receipt.receiptHash.slice(0, 16)}...) | hmacSignatureValid=${verification.signatureValid} | ed25519GovernanceValid=${verification.governanceSignatureValid} | ed25519AgenticReceiptValid=${agenticOk}`;
|
|
1528
|
+
return formatHumanFriendlyExplanation({
|
|
1529
|
+
headline,
|
|
1530
|
+
nonTechnical,
|
|
1531
|
+
technical,
|
|
1532
|
+
policy,
|
|
1533
|
+
languageMode,
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
// 18. get_receipt
|
|
1537
|
+
getReceipt(ctx, receiptOrRunId) {
|
|
1538
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1539
|
+
const trimmedId = (receiptOrRunId || "").trim();
|
|
1540
|
+
const receipt = trimmedId === "latest" || trimmedId === "last" || !trimmedId
|
|
1541
|
+
? this.store.listReceipts(ctx.workspace.id, 1)[0] || null
|
|
1542
|
+
: this.store.getReceipt(ctx.workspace.id, trimmedId);
|
|
1543
|
+
if (!receipt) {
|
|
1544
|
+
throw new Error(`Receipt '${receiptOrRunId}' not found in workspace.`);
|
|
1545
|
+
}
|
|
1546
|
+
const run = this.store.getRun(ws.id, receipt.runId);
|
|
1547
|
+
const agenticReceipt = this.governance.buildPortableAgenticReceipt({
|
|
1548
|
+
receipt,
|
|
1549
|
+
run,
|
|
1550
|
+
workspace: ws,
|
|
1551
|
+
});
|
|
1552
|
+
receipt.agenticReceipt = agenticReceipt;
|
|
1553
|
+
const signatureValid = this.store.verifyReceipt(receipt);
|
|
1554
|
+
const governanceSignatureValid = receipt.decisionRecord
|
|
1555
|
+
? this.governance.verifyDecisionRecord(receipt.decisionRecord)
|
|
1556
|
+
: true;
|
|
1557
|
+
const agenticReceiptValid = this.governance.verifyPortableAgenticReceipt(agenticReceipt, ws);
|
|
1558
|
+
const explanation = this.explainReceipt(receipt, { signatureValid, governanceSignatureValid, agenticReceiptValid }, ws.policy);
|
|
1559
|
+
receipt.explanation = explanation;
|
|
1560
|
+
return {
|
|
1561
|
+
receipt,
|
|
1562
|
+
agenticReceipt,
|
|
1563
|
+
signatureValid,
|
|
1564
|
+
governanceSignatureValid,
|
|
1565
|
+
agenticReceiptValid,
|
|
1566
|
+
explanation,
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
listReceipts(ctx, limit = 25) {
|
|
1570
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1571
|
+
const rows = this.store.listReceipts(ws.id, limit);
|
|
1572
|
+
const enriched = rows.map((r) => {
|
|
1573
|
+
const run = this.store.getRun(ws.id, r.runId);
|
|
1574
|
+
const agenticReceipt = this.governance.buildPortableAgenticReceipt({
|
|
1575
|
+
receipt: r,
|
|
1576
|
+
run,
|
|
1577
|
+
workspace: ws,
|
|
1578
|
+
});
|
|
1579
|
+
r.agenticReceipt = agenticReceipt;
|
|
1580
|
+
const signatureValid = this.store.verifyReceipt(r);
|
|
1581
|
+
const governanceSignatureValid = r.decisionRecord
|
|
1582
|
+
? this.governance.verifyDecisionRecord(r.decisionRecord)
|
|
1583
|
+
: true;
|
|
1584
|
+
const agenticReceiptValid = this.governance.verifyPortableAgenticReceipt(agenticReceipt, ws);
|
|
1585
|
+
const explanation = this.explainReceipt(r, { signatureValid, governanceSignatureValid, agenticReceiptValid }, ws.policy);
|
|
1586
|
+
r.explanation = explanation;
|
|
1587
|
+
return {
|
|
1588
|
+
...r,
|
|
1589
|
+
agenticReceipt,
|
|
1590
|
+
signatureValid,
|
|
1591
|
+
governanceSignatureValid,
|
|
1592
|
+
agenticReceiptValid,
|
|
1593
|
+
explanation,
|
|
1594
|
+
};
|
|
1595
|
+
});
|
|
1596
|
+
const allValid = enriched.every((r) => r.signatureValid &&
|
|
1597
|
+
r.governanceSignatureValid &&
|
|
1598
|
+
r.agenticReceiptValid);
|
|
1599
|
+
const totalCredits = enriched.reduce((sum, r) => sum + r.creditsMetered, 0);
|
|
1600
|
+
const summaryExplanation = formatHumanFriendlyExplanation({
|
|
1601
|
+
headline: enriched.length > 0
|
|
1602
|
+
? `${enriched.length} tamper-proof execution receipt${enriched.length === 1 ? "" : "s"} (${allValid ? "all verified" : "verification warning"}).`
|
|
1603
|
+
: "No execution receipts recorded yet.",
|
|
1604
|
+
nonTechnical: enriched.length > 0
|
|
1605
|
+
? `Found ${enriched.length} recent receipt${enriched.length === 1 ? "" : "s"} totaling ${totalCredits} credits ($${(totalCredits * 0.01).toFixed(2)} USD). Latest action: ${enriched[0].toolSlug} (${enriched[0].status}). ${allValid ? "All receipts passed HMAC and portable Ed25519 AgenticReceipt verification." : "One or more receipts failed signature verification."}`
|
|
1606
|
+
: "No tools have been executed in this workspace yet.",
|
|
1607
|
+
technical: `count=${enriched.length} | allSignaturesValid=${allValid} | totalCreditsMetered=${totalCredits} | latestReceiptId=${enriched[0]?.id || "none"} | latestHash=${enriched[0]?.receiptHash?.slice(0, 16) || "GENESIS"}`,
|
|
1608
|
+
policy: ws.policy,
|
|
1609
|
+
});
|
|
1610
|
+
return {
|
|
1611
|
+
receipts: enriched,
|
|
1612
|
+
total: enriched.length,
|
|
1613
|
+
explanation: summaryExplanation,
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
/**
|
|
1617
|
+
* Verifies the entire cryptographic receipt chain (`prevReceiptHash` -> `receiptHash`,
|
|
1618
|
+
* HMAC vault signatures, Ed25519 Governance DecisionRecords, and portable Ed25519 AgenticReceipts).
|
|
1619
|
+
*/
|
|
1620
|
+
verifyReceiptChain(ctx, limit = 100) {
|
|
1621
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1622
|
+
const govStatus = this.governance.getStatus(ws);
|
|
1623
|
+
const descList = this.listReceipts(ctx, limit);
|
|
1624
|
+
const chronological = [...descList.receipts].reverse();
|
|
1625
|
+
let hashLinksValid = true;
|
|
1626
|
+
let allHmacValid = true;
|
|
1627
|
+
let allEd25519AgenticValid = true;
|
|
1628
|
+
let allGovernanceValid = true;
|
|
1629
|
+
const links = chronological.map((r, idx) => {
|
|
1630
|
+
const expectedPrev = idx === 0
|
|
1631
|
+
? r.prevReceiptHash // First receipt in window (either genesis_0000000000000000 or older link)
|
|
1632
|
+
: chronological[idx - 1].receiptHash;
|
|
1633
|
+
const hashLinkValid = idx === 0
|
|
1634
|
+
? Boolean(r.prevReceiptHash)
|
|
1635
|
+
: r.prevReceiptHash === expectedPrev;
|
|
1636
|
+
if (!hashLinkValid)
|
|
1637
|
+
hashLinksValid = false;
|
|
1638
|
+
if (!r.signatureValid)
|
|
1639
|
+
allHmacValid = false;
|
|
1640
|
+
if (!r.agenticReceiptValid)
|
|
1641
|
+
allEd25519AgenticValid = false;
|
|
1642
|
+
if (!r.governanceSignatureValid)
|
|
1643
|
+
allGovernanceValid = false;
|
|
1644
|
+
return {
|
|
1645
|
+
index: idx,
|
|
1646
|
+
receiptId: r.id,
|
|
1647
|
+
runId: r.runId,
|
|
1648
|
+
toolSlug: r.toolSlug,
|
|
1649
|
+
prevReceiptHash: r.prevReceiptHash,
|
|
1650
|
+
receiptHash: r.receiptHash,
|
|
1651
|
+
hashLinkValid,
|
|
1652
|
+
hmacValid: r.signatureValid,
|
|
1653
|
+
ed25519AgenticValid: r.agenticReceiptValid,
|
|
1654
|
+
governanceValid: r.governanceSignatureValid,
|
|
1655
|
+
issuedAt: r.issuedAt,
|
|
1656
|
+
};
|
|
1657
|
+
});
|
|
1658
|
+
const chainValid = hashLinksValid &&
|
|
1659
|
+
allHmacValid &&
|
|
1660
|
+
allEd25519AgenticValid &&
|
|
1661
|
+
allGovernanceValid;
|
|
1662
|
+
const genesisHash = chronological[0]?.prevReceiptHash || "genesis_0000000000000000";
|
|
1663
|
+
const headReceiptHash = chronological[chronological.length - 1]?.receiptHash ||
|
|
1664
|
+
"genesis_0000000000000000";
|
|
1665
|
+
const headline = chronological.length === 0
|
|
1666
|
+
? "Receipt Chain Verified: 0 receipts recorded (at genesis state)."
|
|
1667
|
+
: chainValid
|
|
1668
|
+
? `Receipt Chain Verified: All ${chronological.length} receipt${chronological.length === 1 ? "" : "s"} form an intact SHA-256 + Ed25519 cryptographic chain.`
|
|
1669
|
+
: `Receipt Chain Warning: Integrity mismatch detected across ${chronological.length} receipt(s).`;
|
|
1670
|
+
const nonTechnical = [
|
|
1671
|
+
headline,
|
|
1672
|
+
chronological.length > 0
|
|
1673
|
+
? `Every tool run is linked to the one before it like a tamper-proof ledger (${chronological[0].id} -> ${chronological[chronological.length - 1].id}), signed with your workspace's Ed25519 identity (${govStatus.agentDid}).`
|
|
1674
|
+
: "Run any tool to generate your first signed receipt.",
|
|
1675
|
+
].join(" ");
|
|
1676
|
+
const technical = `workspace=${ws.id} | agentDid=${govStatus.agentDid} | publicKeyHex=${govStatus.publicKeyHex.slice(0, 24)}... | totalReceipts=${chronological.length} | chainValid=${chainValid} (hashLinks=${hashLinksValid}, hmac=${allHmacValid}, ed25519Agentic=${allEd25519AgenticValid}, ed25519Governance=${allGovernanceValid}) | genesis=${genesisHash.slice(0, 16)}... -> head=${headReceiptHash.slice(0, 16)}...`;
|
|
1677
|
+
const brokenLinks = links
|
|
1678
|
+
.filter((l) => !l.hashLinkValid ||
|
|
1679
|
+
!l.hmacValid ||
|
|
1680
|
+
!l.ed25519AgenticValid ||
|
|
1681
|
+
!l.governanceValid)
|
|
1682
|
+
.map((l) => ({
|
|
1683
|
+
receiptId: l.receiptId,
|
|
1684
|
+
runId: l.runId,
|
|
1685
|
+
reason: "Signature or hash link mismatch",
|
|
1686
|
+
}));
|
|
1687
|
+
const validCount = chronological.length - brokenLinks.length;
|
|
1688
|
+
return {
|
|
1689
|
+
ok: true,
|
|
1690
|
+
valid: chainValid,
|
|
1691
|
+
workspaceId: ws.id,
|
|
1692
|
+
agentDid: govStatus.agentDid,
|
|
1693
|
+
publicKeyHex: govStatus.publicKeyHex,
|
|
1694
|
+
chainValid,
|
|
1695
|
+
hashLinksValid,
|
|
1696
|
+
allHmacValid,
|
|
1697
|
+
allEd25519AgenticValid,
|
|
1698
|
+
allGovernanceValid,
|
|
1699
|
+
totalReceipts: chronological.length,
|
|
1700
|
+
totalChecked: chronological.length,
|
|
1701
|
+
validCount,
|
|
1702
|
+
brokenLinks,
|
|
1703
|
+
genesisHash,
|
|
1704
|
+
headReceiptHash,
|
|
1705
|
+
links,
|
|
1706
|
+
explanation: formatHumanFriendlyExplanation({
|
|
1707
|
+
headline,
|
|
1708
|
+
nonTechnical,
|
|
1709
|
+
technical,
|
|
1710
|
+
policy: ws.policy,
|
|
1711
|
+
}),
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
// 18a. Governance overview, kill switch, and prompt-injection scanning helpers
|
|
1715
|
+
getGovernanceOverview(ctx) {
|
|
1716
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1717
|
+
const governance = this.governance.getStatus(ws);
|
|
1718
|
+
const trustScore = this.governance.getWorkspaceTrustScore(ws.id);
|
|
1719
|
+
const plainEnglish = describePolicyInPlainEnglish(ws.policy);
|
|
1720
|
+
const localMemories = this.store.listMemoryRecords(ws.id, 25);
|
|
1721
|
+
const activeFacts = this.store.listTemporalFacts(ws.id, {
|
|
1722
|
+
includeHistorical: false,
|
|
1723
|
+
limit: 30,
|
|
1724
|
+
});
|
|
1725
|
+
const inferred = extractActiveMemoryDefaults({
|
|
1726
|
+
memories: localMemories,
|
|
1727
|
+
temporalFacts: activeFacts,
|
|
1728
|
+
});
|
|
1729
|
+
const llmPolicyBrief = buildLlmPolicyAndGovernanceBrief(ws.policy, {
|
|
1730
|
+
workspaceName: ws.name,
|
|
1731
|
+
workspaceSlug: ws.slug,
|
|
1732
|
+
activeDefaults: inferred.activeDefaults,
|
|
1733
|
+
trustScore: { overall: trustScore.overall, tier: trustScore.tier },
|
|
1734
|
+
agentDid: governance.agentDid,
|
|
1735
|
+
});
|
|
1736
|
+
const killSwitch = {
|
|
1737
|
+
active: Boolean(ws.policy.killSwitchActive),
|
|
1738
|
+
reason: ws.policy.killSwitchReason || null,
|
|
1739
|
+
};
|
|
1740
|
+
const headline = killSwitch.active
|
|
1741
|
+
? `Emergency stop is ACTIVE: ${killSwitch.reason || "all agent executions are paused"}.`
|
|
1742
|
+
: `Governance is healthy: ${plainEnglish.headline}`;
|
|
1743
|
+
const explanation = formatHumanFriendlyExplanation({
|
|
1744
|
+
headline,
|
|
1745
|
+
nonTechnical: killSwitch.active
|
|
1746
|
+
? `Muxo has paused all agent tool executions in this workspace (${killSwitch.reason || "no reason given"}). Say "resume all agents" to turn the emergency stop off.`
|
|
1747
|
+
: `Your workspace is protected by ${plainEnglish.rules.length} active guardrail${plainEnglish.rules.length === 1 ? "" : "s"}. Agent trust score: ${trustScore.overall}/100 (${trustScore.tier}).`,
|
|
1748
|
+
technical: `killSwitchActive=${killSwitch.active} | trustScore=${trustScore.overall} (${trustScore.tier}) | agentDid=${governance.agentDid} | ${plainEnglish.technical}`,
|
|
1749
|
+
policy: ws.policy,
|
|
1750
|
+
});
|
|
1751
|
+
return {
|
|
1752
|
+
workspaceId: ws.id,
|
|
1753
|
+
killSwitch,
|
|
1754
|
+
governance,
|
|
1755
|
+
trustScore,
|
|
1756
|
+
policy: ws.policy,
|
|
1757
|
+
plainEnglish,
|
|
1758
|
+
llmPolicyBrief,
|
|
1759
|
+
explanation,
|
|
1760
|
+
};
|
|
1761
|
+
}
|
|
1762
|
+
resetWorkspaceGuardrails(ctx) {
|
|
1763
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1764
|
+
const updated = this.store.updateWorkspacePolicy(ws.id, {
|
|
1765
|
+
...DEFAULT_WORKSPACE_POLICY,
|
|
1766
|
+
parameterRules: [],
|
|
1767
|
+
customInstructions: [],
|
|
1768
|
+
naturalLanguageRules: [],
|
|
1769
|
+
blockedToolSlugs: [],
|
|
1770
|
+
});
|
|
1771
|
+
this.governance.resumeKillSwitch(ws.id);
|
|
1772
|
+
const plainEnglish = describePolicyInPlainEnglish(updated.policy);
|
|
1773
|
+
const llmPolicyBrief = buildLlmPolicyAndGovernanceBrief(updated.policy, {
|
|
1774
|
+
workspaceName: updated.name,
|
|
1775
|
+
workspaceSlug: updated.slug,
|
|
1776
|
+
});
|
|
1777
|
+
return {
|
|
1778
|
+
ok: true,
|
|
1779
|
+
action: "reset_guardrails",
|
|
1780
|
+
workspace: updated,
|
|
1781
|
+
policy: updated.policy,
|
|
1782
|
+
plainEnglish,
|
|
1783
|
+
llmPolicyBrief,
|
|
1784
|
+
explanation: plainEnglish.explanation,
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
async checkToolPolicy(ctx, params) {
|
|
1788
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1789
|
+
const localMemories = this.store.listMemoryRecords(ws.id, 25);
|
|
1790
|
+
const activeFacts = this.store.listTemporalFacts(ws.id, {
|
|
1791
|
+
includeHistorical: false,
|
|
1792
|
+
limit: 30,
|
|
1793
|
+
});
|
|
1794
|
+
const inferred = extractActiveMemoryDefaults({
|
|
1795
|
+
memories: localMemories,
|
|
1796
|
+
temporalFacts: activeFacts,
|
|
1797
|
+
});
|
|
1798
|
+
let riskLevel;
|
|
1799
|
+
let creditCost;
|
|
1800
|
+
let lane;
|
|
1801
|
+
let resolvedSlug = String(params.toolSlug || "instant.web.search").trim();
|
|
1802
|
+
try {
|
|
1803
|
+
const tool = await this.inspectTool(resolvedSlug);
|
|
1804
|
+
resolvedSlug = tool.slug;
|
|
1805
|
+
riskLevel = tool.riskLevel;
|
|
1806
|
+
creditCost = tool.creditCost;
|
|
1807
|
+
lane = tool.lane;
|
|
1808
|
+
}
|
|
1809
|
+
catch {
|
|
1810
|
+
// Fallback to slug prefix heuristics in simulatePolicyEvaluation
|
|
1811
|
+
}
|
|
1812
|
+
return simulatePolicyEvaluation({
|
|
1813
|
+
policy: ws.policy,
|
|
1814
|
+
toolSlug: resolvedSlug,
|
|
1815
|
+
riskLevel,
|
|
1816
|
+
creditCost,
|
|
1817
|
+
lane,
|
|
1818
|
+
input: params.input || {},
|
|
1819
|
+
activeDefaults: inferred.activeDefaults,
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
setKillSwitch(ctx, active, reason) {
|
|
1823
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1824
|
+
const updated = this.store.updateWorkspacePolicy(ws.id, {
|
|
1825
|
+
killSwitchActive: active,
|
|
1826
|
+
killSwitchReason: active
|
|
1827
|
+
? reason || "Paused via natural language"
|
|
1828
|
+
: null,
|
|
1829
|
+
});
|
|
1830
|
+
if (active) {
|
|
1831
|
+
this.governance
|
|
1832
|
+
.activateKillSwitch(ws.id, reason || "Paused via natural language", "quarantine")
|
|
1833
|
+
.catch(() => {
|
|
1834
|
+
// The policy flag is the enforcement source of truth; the governance
|
|
1835
|
+
// ledger entry is best-effort.
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
else {
|
|
1839
|
+
this.governance.resumeKillSwitch(ws.id);
|
|
1840
|
+
}
|
|
1841
|
+
return updated;
|
|
1842
|
+
}
|
|
1843
|
+
scanForContextPoisoning(workspaceId, text) {
|
|
1844
|
+
const scan = this.governance.scanMemoryContent({
|
|
1845
|
+
workspaceId,
|
|
1846
|
+
sessionId: this.supermemory.getWorkspaceNamespace(workspaceId),
|
|
1847
|
+
role: "user",
|
|
1848
|
+
content: text,
|
|
1849
|
+
recordInDetector: false,
|
|
1850
|
+
});
|
|
1851
|
+
return {
|
|
1852
|
+
poisoned: !scan.safe,
|
|
1853
|
+
severity: scan.riskLevel,
|
|
1854
|
+
matchedPatterns: scan.findings.map((f) => f.patternName),
|
|
1855
|
+
findingsCount: scan.findingsCount,
|
|
1856
|
+
integrityValid: scan.integrityValid,
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
// 18b. Plain-English Guardrails & Active Memory Rules
|
|
1860
|
+
getGuardrailsSummary(ctx) {
|
|
1861
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1862
|
+
const plainEnglish = describePolicyInPlainEnglish(ws.policy);
|
|
1863
|
+
const localMemories = this.store.listMemoryRecords(ws.id, 25);
|
|
1864
|
+
const activeFacts = this.store.listTemporalFacts(ws.id, {
|
|
1865
|
+
includeHistorical: false,
|
|
1866
|
+
limit: 30,
|
|
1867
|
+
});
|
|
1868
|
+
const inferred = extractActiveMemoryDefaults({
|
|
1869
|
+
memories: localMemories,
|
|
1870
|
+
temporalFacts: activeFacts,
|
|
1871
|
+
});
|
|
1872
|
+
const llmPolicyBrief = buildLlmPolicyAndGovernanceBrief(ws.policy, {
|
|
1873
|
+
workspaceName: ws.name,
|
|
1874
|
+
workspaceSlug: ws.slug,
|
|
1875
|
+
activeDefaults: inferred.activeDefaults,
|
|
1876
|
+
});
|
|
1877
|
+
return {
|
|
1878
|
+
workspaceId: ws.id,
|
|
1879
|
+
policy: ws.policy,
|
|
1880
|
+
plainEnglish,
|
|
1881
|
+
llmPolicyBrief,
|
|
1882
|
+
activeDefaults: inferred.activeDefaults,
|
|
1883
|
+
activeGuardrails: Array.from(new Set([
|
|
1884
|
+
...(ws.policy.naturalLanguageRules || []),
|
|
1885
|
+
...inferred.activeGuardrails,
|
|
1886
|
+
])),
|
|
1887
|
+
};
|
|
1888
|
+
}
|
|
1889
|
+
async applyNaturalLanguageGuardrail(ctx, prompt, options) {
|
|
1890
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1891
|
+
const openaiApiKey = this.store.getByokKey(ws.id, "openai") || undefined;
|
|
1892
|
+
const anthropicApiKey = this.store.getByokKey(ws.id, "anthropic") || undefined;
|
|
1893
|
+
const llmApiKey = options?.llmApiKey || openaiApiKey || anthropicApiKey;
|
|
1894
|
+
const llmProvider = options?.llmProvider ||
|
|
1895
|
+
(!options?.llmApiKey && !openaiApiKey && anthropicApiKey
|
|
1896
|
+
? "anthropic"
|
|
1897
|
+
: undefined);
|
|
1898
|
+
const parsed = await compileNaturalLanguageGuardrailWithLlm(prompt, ws.policy, {
|
|
1899
|
+
...options,
|
|
1900
|
+
llmApiKey,
|
|
1901
|
+
llmProvider,
|
|
1902
|
+
});
|
|
1903
|
+
const updated = this.store.updateWorkspacePolicy(ws.id, parsed.patch);
|
|
1904
|
+
let memoryRecord;
|
|
1905
|
+
try {
|
|
1906
|
+
memoryRecord = await this.addWorkspaceMemory(ctx, {
|
|
1907
|
+
title: parsed.memoryFact.title,
|
|
1908
|
+
content: parsed.memoryFact.content,
|
|
1909
|
+
kind: "policy_decision",
|
|
1910
|
+
subject: parsed.memoryFact.subject,
|
|
1911
|
+
predicate: parsed.memoryFact.predicate,
|
|
1912
|
+
object: parsed.memoryFact.object,
|
|
1913
|
+
metadata: {
|
|
1914
|
+
tags: parsed.memoryFact.tags,
|
|
1915
|
+
guardrailPrompt: prompt,
|
|
1916
|
+
},
|
|
1917
|
+
});
|
|
1918
|
+
}
|
|
1919
|
+
catch {
|
|
1920
|
+
// Non-blocking if memory scan/storage fails
|
|
1921
|
+
}
|
|
1922
|
+
const plainEnglish = describePolicyInPlainEnglish(updated.policy);
|
|
1923
|
+
const llmPolicyBrief = buildLlmPolicyAndGovernanceBrief(updated.policy, {
|
|
1924
|
+
workspaceName: updated.name,
|
|
1925
|
+
workspaceSlug: updated.slug,
|
|
1926
|
+
});
|
|
1927
|
+
return {
|
|
1928
|
+
ok: true,
|
|
1929
|
+
workspace: updated,
|
|
1930
|
+
guardrail: parsed,
|
|
1931
|
+
plainEnglish,
|
|
1932
|
+
llmPolicyBrief,
|
|
1933
|
+
memoryRecord,
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
// 19. Natural Language Router & End-to-End Executor (`muxo do` / `POST /api/v1/do`)
|
|
1937
|
+
async doNaturalLanguage(ctx, params) {
|
|
1938
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1939
|
+
const rawPrompt = String(params.prompt || "").trim();
|
|
1940
|
+
const cleanedPrompt = rawPrompt
|
|
1941
|
+
.replace(/^(?:hey\s+muxo[,:\s]*|muxo[,:\s]*|please\s+)/i, "")
|
|
1942
|
+
.trim();
|
|
1943
|
+
// 0a. Governance Emergency Pause / Kill Switch ("Emergency stop", "Pause all agents because ...", "Activate kill switch")
|
|
1944
|
+
if (/\b(?:emergency\s+stop|activate\s+(?:the\s+)?kill\s*switch|enable\s+(?:the\s+)?kill\s*switch|pause\s+all\s+agents|freeze\s+(?:all\s+)?(?:agents|workspace)|halt\s+all\s+agents|stop\s+all\s+agents)\b/i.test(cleanedPrompt)) {
|
|
1945
|
+
const reasonMatch = cleanedPrompt.match(/(?:because|reason|due\s+to)[:\s]+(.+)$/i);
|
|
1946
|
+
const reason = reasonMatch?.[1]?.trim() ||
|
|
1947
|
+
`Paused via natural language: "${rawPrompt.slice(0, 120)}"`;
|
|
1948
|
+
const updated = this.setKillSwitch(ctx, true, reason);
|
|
1949
|
+
const govOverview = this.getGovernanceOverview(ctx);
|
|
1950
|
+
const synthResolution = makeControlPlaneResolution("workspace.governance.kill_switch", rawPrompt, govOverview.explanation.preferred, { active: true, reason });
|
|
1951
|
+
return {
|
|
1952
|
+
ok: true,
|
|
1953
|
+
executed: true,
|
|
1954
|
+
status: "governance_updated",
|
|
1955
|
+
intent: synthResolution,
|
|
1956
|
+
resolution: synthResolution,
|
|
1957
|
+
governanceStatus: govOverview,
|
|
1958
|
+
plainEnglishGuardrails: describePolicyInPlainEnglish(updated.policy),
|
|
1959
|
+
explanation: govOverview.explanation,
|
|
1960
|
+
humanMessage: `Emergency Stop Activated: All agent tool executions are now paused (${reason}).`,
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
// 0b. Governance Resume / Unpause ("Resume all agents", "Deactivate kill switch", "Unpause workspace")
|
|
1964
|
+
if (/\b(?:deactivate\s+(?:the\s+)?kill\s*switch|disable\s+(?:the\s+)?kill\s*switch|resume\s+(?:all\s+)?agents|unpause\s+(?:all\s+)?(?:agents|workspace)|unfreeze\s+(?:all\s+)?(?:agents|workspace))\b/i.test(cleanedPrompt)) {
|
|
1965
|
+
const updated = this.setKillSwitch(ctx, false);
|
|
1966
|
+
const govOverview = this.getGovernanceOverview(ctx);
|
|
1967
|
+
const synthResolution = makeControlPlaneResolution("workspace.governance.kill_switch", rawPrompt, govOverview.explanation.preferred, { active: false });
|
|
1968
|
+
return {
|
|
1969
|
+
ok: true,
|
|
1970
|
+
executed: true,
|
|
1971
|
+
status: "governance_updated",
|
|
1972
|
+
intent: synthResolution,
|
|
1973
|
+
resolution: synthResolution,
|
|
1974
|
+
governanceStatus: govOverview,
|
|
1975
|
+
plainEnglishGuardrails: describePolicyInPlainEnglish(updated.policy),
|
|
1976
|
+
explanation: govOverview.explanation,
|
|
1977
|
+
humanMessage: `Agents Resumed: Emergency kill switch is off and normal governance rules are active.`,
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
// 0c. Prompt Injection / Context Poisoning Scan ("Scan this text for prompt injection: ...", "Check for context poisoning: ...")
|
|
1981
|
+
const scanMatch = cleanedPrompt.match(/^(?:scan|check|inspect|test)\s+(?:this\s+)?(?:text|prompt|input|content|memory)?\s*(?:for\s+)?(?:prompt\s+injection|context\s+poisoning|asi[-\s]?06|safety|jailbreak)[:\s]+([\s\S]+)$/i);
|
|
1982
|
+
if (scanMatch?.[1]) {
|
|
1983
|
+
const textToScan = scanMatch[1].trim();
|
|
1984
|
+
const scanResult = this.scanForContextPoisoning(ws.id, textToScan);
|
|
1985
|
+
const explanation = formatHumanFriendlyExplanation({
|
|
1986
|
+
headline: scanResult.poisoned
|
|
1987
|
+
? `Safety Alert: Blocked suspicious instruction (${scanResult.severity} severity).`
|
|
1988
|
+
: `Safety Check Passed: No prompt-injection or poisoning patterns detected.`,
|
|
1989
|
+
nonTechnical: scanResult.poisoned
|
|
1990
|
+
? `Muxo's safety scanner detected ${scanResult.matchedPatterns.length} suspicious pattern(s) (${scanResult.matchedPatterns.join(", ")}) that look like an attempt to hijack agent instructions or exfiltrate secrets.`
|
|
1991
|
+
: `This text passed Muxo's safety scanner cleanly with 0 suspicious patterns detected.`,
|
|
1992
|
+
technical: `asi06Poisoned=${scanResult.poisoned} | severity=${scanResult.severity} | matchedPatterns=[${scanResult.matchedPatterns.join(", ")}]`,
|
|
1993
|
+
policy: ws.policy,
|
|
1994
|
+
});
|
|
1995
|
+
const synthResolution = makeControlPlaneResolution("workspace.governance.scan", rawPrompt, explanation.preferred, { content: textToScan });
|
|
1996
|
+
return {
|
|
1997
|
+
ok: true,
|
|
1998
|
+
executed: true,
|
|
1999
|
+
status: "governance_scanned",
|
|
2000
|
+
intent: synthResolution,
|
|
2001
|
+
resolution: synthResolution,
|
|
2002
|
+
poisoningScan: scanResult,
|
|
2003
|
+
explanation,
|
|
2004
|
+
humanMessage: explanation.preferred,
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
// 1. Check if the user is stating a plain-English Guardrail ("Hey Muxo, don't go over 25 credits", "never use tool X", "use non-technical language")
|
|
2008
|
+
const guardrailCheck = parseNaturalLanguageGuardrail(params.prompt, ws.policy);
|
|
2009
|
+
if (guardrailCheck.isGuardrail) {
|
|
2010
|
+
if (params.dryRun || params.planOnly) {
|
|
2011
|
+
const previewPlain = describePolicyInPlainEnglish({
|
|
2012
|
+
...ws.policy,
|
|
2013
|
+
...guardrailCheck.patch,
|
|
2014
|
+
});
|
|
2015
|
+
const synthResolution = makeControlPlaneResolution("workspace.guardrail.update", params.prompt, guardrailCheck.summary, guardrailCheck.patch);
|
|
2016
|
+
return {
|
|
2017
|
+
ok: true,
|
|
2018
|
+
executed: false,
|
|
2019
|
+
status: "guardrail_applied",
|
|
2020
|
+
intent: synthResolution,
|
|
2021
|
+
resolution: synthResolution,
|
|
2022
|
+
guardrail: guardrailCheck,
|
|
2023
|
+
plainEnglishGuardrails: previewPlain,
|
|
2024
|
+
explanation: previewPlain.explanation,
|
|
2025
|
+
humanMessage: `Preview guardrail: ${guardrailCheck.summary}`,
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
const applied = await this.applyNaturalLanguageGuardrail(ctx, params.prompt);
|
|
2029
|
+
const synthResolution = makeControlPlaneResolution("workspace.guardrail.update", params.prompt, applied.guardrail.summary, applied.guardrail.patch);
|
|
2030
|
+
return {
|
|
2031
|
+
ok: true,
|
|
2032
|
+
executed: true,
|
|
2033
|
+
status: "guardrail_applied",
|
|
2034
|
+
intent: synthResolution,
|
|
2035
|
+
resolution: synthResolution,
|
|
2036
|
+
guardrail: applied.guardrail,
|
|
2037
|
+
plainEnglishGuardrails: applied.plainEnglish,
|
|
2038
|
+
memoryRecord: applied.memoryRecord,
|
|
2039
|
+
explanation: applied.plainEnglish.explanation,
|
|
2040
|
+
humanMessage: `Guardrail active: ${applied.guardrail.summary}`,
|
|
2041
|
+
};
|
|
2042
|
+
}
|
|
2043
|
+
// 2a. Check if the user wants to forget/delete a memory ("Forget our staging redis", "Delete memory mem_...")
|
|
2044
|
+
const forgetMatch = cleanedPrompt.match(/^(?:forget|delete|remove|erase)\s+(?:that\s+|our\s+|the\s+|my\s+)?(?:memory\s+(?:about\s+|id\s+)?|saved\s+memory\s+|fact\s+about\s+)?(.+)$/i);
|
|
2045
|
+
if (forgetMatch?.[1] &&
|
|
2046
|
+
!/\b(?:dns|domain|github|issue|pull\s+request|slack|email|stripe|postgres|redis\s+cluster|s3\s+bucket)\b/i.test(cleanedPrompt)) {
|
|
2047
|
+
const targetQuery = forgetMatch[1].trim().replace(/[.?!]+$/, "");
|
|
2048
|
+
const records = this.store.listMemoryRecords(ws.id, 100);
|
|
2049
|
+
const match = records.find((r) => r.id.toLowerCase() === targetQuery.toLowerCase()) ||
|
|
2050
|
+
records.find((r) => r.title.toLowerCase().includes(targetQuery.toLowerCase()) ||
|
|
2051
|
+
r.content.toLowerCase().includes(targetQuery.toLowerCase()));
|
|
2052
|
+
if (match) {
|
|
2053
|
+
this.deleteWorkspaceMemory(ctx, match.id);
|
|
2054
|
+
const overview = await this.getMemoryOverview(ctx);
|
|
2055
|
+
const humanSummary = `Forgot memory "${match.title}" (${match.id}).`;
|
|
2056
|
+
const synthResolution = makeControlPlaneResolution("workspace.memory.forget", rawPrompt, humanSummary, { memoryId: match.id, query: targetQuery });
|
|
2057
|
+
return {
|
|
2058
|
+
ok: true,
|
|
2059
|
+
executed: true,
|
|
2060
|
+
status: "memory_forgotten",
|
|
2061
|
+
intent: synthResolution,
|
|
2062
|
+
resolution: synthResolution,
|
|
2063
|
+
deletedMemoryId: match.id,
|
|
2064
|
+
memoryOverview: overview,
|
|
2065
|
+
activeDefaults: overview.activeDefaults,
|
|
2066
|
+
activeMemoryDefaults: overview.activeDefaults,
|
|
2067
|
+
explanation: overview.explanation,
|
|
2068
|
+
humanMessage: humanSummary,
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
// 2b. Check if the user is teaching Supermemory a preference/fact ("Hey Muxo, remember our default repo is acme/api and region is us-east-1")
|
|
2073
|
+
const memoryCheck = parseNaturalLanguageMemoryInstruction(params.prompt);
|
|
2074
|
+
if (memoryCheck.isMemoryInstruction) {
|
|
2075
|
+
const primaryFact = memoryCheck.temporalFacts[0];
|
|
2076
|
+
const savedRecord = await this.addWorkspaceMemory(ctx, {
|
|
2077
|
+
title: memoryCheck.title,
|
|
2078
|
+
content: memoryCheck.content,
|
|
2079
|
+
kind: memoryCheck.kind,
|
|
2080
|
+
subject: primaryFact?.subject,
|
|
2081
|
+
predicate: primaryFact?.predicate,
|
|
2082
|
+
object: primaryFact?.object,
|
|
2083
|
+
metadata: {
|
|
2084
|
+
extractedDefaults: memoryCheck.extractedDefaults,
|
|
2085
|
+
source: "natural_language_do",
|
|
2086
|
+
},
|
|
2087
|
+
});
|
|
2088
|
+
for (const tf of memoryCheck.temporalFacts) {
|
|
2089
|
+
this.store.upsertTemporalFact({
|
|
2090
|
+
workspaceId: ws.id,
|
|
2091
|
+
subject: tf.subject,
|
|
2092
|
+
predicate: tf.predicate,
|
|
2093
|
+
object: tf.object,
|
|
2094
|
+
summary: tf.summary,
|
|
2095
|
+
confidence: 0.98,
|
|
2096
|
+
sourceKind: "preference",
|
|
2097
|
+
sourceReferenceId: savedRecord.id,
|
|
2098
|
+
metadata: { source: "natural_language_do" },
|
|
2099
|
+
});
|
|
2100
|
+
}
|
|
2101
|
+
const overview = await this.getMemoryOverview(ctx);
|
|
2102
|
+
const synthResolution = makeControlPlaneResolution("workspace.memory.remember", params.prompt, memoryCheck.humanSummary, memoryCheck.extractedDefaults);
|
|
2103
|
+
return {
|
|
2104
|
+
ok: true,
|
|
2105
|
+
executed: true,
|
|
2106
|
+
status: "memory_saved",
|
|
2107
|
+
intent: synthResolution,
|
|
2108
|
+
resolution: synthResolution,
|
|
2109
|
+
memoryRecord: savedRecord,
|
|
2110
|
+
memoryOverview: overview,
|
|
2111
|
+
activeDefaults: overview.activeDefaults,
|
|
2112
|
+
activeMemoryDefaults: overview.activeDefaults,
|
|
2113
|
+
explanation: overview.explanation,
|
|
2114
|
+
humanMessage: memoryCheck.humanSummary,
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
// 2c-graph. Check if the user is asking for the bi-temporal knowledge graph / fact lineage ("Show my memory graph", "What is our knowledge graph?", "Show fact lineage")
|
|
2118
|
+
if (/\b(?:memory\s+graph|knowledge\s+graph|temporal\s+graph|fact\s+lineage|bi[-\s]?temporal\s+(?:graph|lineage|facts))\b/i.test(cleanedPrompt)) {
|
|
2119
|
+
const memoryGraph = await this.getMemoryGraph(ctx);
|
|
2120
|
+
const synthResolution = makeControlPlaneResolution("workspace.memory.graph", rawPrompt, memoryGraph.explanation.preferred);
|
|
2121
|
+
return {
|
|
2122
|
+
ok: true,
|
|
2123
|
+
executed: true,
|
|
2124
|
+
status: "memory_graph",
|
|
2125
|
+
intent: synthResolution,
|
|
2126
|
+
resolution: synthResolution,
|
|
2127
|
+
memoryGraph,
|
|
2128
|
+
explanation: memoryGraph.explanation,
|
|
2129
|
+
humanMessage: memoryGraph.explanation.preferred,
|
|
2130
|
+
};
|
|
2131
|
+
}
|
|
2132
|
+
// 2c. Check if the user is asking what Muxo remembers ("What do you remember?", "Show my saved memories", "What are our default settings in memory?")
|
|
2133
|
+
if (/^(?:what\s+do\s+you\s+remember|what\s+(?:are|is)\s+(?:my|our)\s+(?:saved\s+)?(?:memories|memory|defaults|preferences)|show\s+(?:my|our)\s+(?:saved\s+)?(?:memories|memory|defaults)|recall\s+(?:my|our|workspace)\s+memories|list\s+(?:my|our|workspace)\s+memories)\b/i.test(cleanedPrompt)) {
|
|
2134
|
+
const overview = await this.getMemoryOverview(ctx);
|
|
2135
|
+
const synthResolution = makeControlPlaneResolution("workspace.memory.recall", rawPrompt, overview.explanation.preferred);
|
|
2136
|
+
return {
|
|
2137
|
+
ok: true,
|
|
2138
|
+
executed: true,
|
|
2139
|
+
status: "memory_recalled",
|
|
2140
|
+
intent: synthResolution,
|
|
2141
|
+
resolution: synthResolution,
|
|
2142
|
+
memoryOverview: overview,
|
|
2143
|
+
activeDefaults: overview.activeDefaults,
|
|
2144
|
+
activeMemoryDefaults: overview.activeDefaults,
|
|
2145
|
+
explanation: overview.explanation,
|
|
2146
|
+
humanMessage: overview.explanation.preferred,
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
// 2d. Check if the user is asking to inspect current guardrails, policy, or governance status ("What are our current guardrails?", "Show my safety policy", "Explain governance status")
|
|
2150
|
+
if (/^(?:what\s+(?:are|is)\s+(?:my|our|the)\s+(?:current\s+)?(?:guardrails|policy|policies|safety\s+rules|governance(?:\s+status)?)|show\s+(?:my|our|the)\s+(?:guardrails|policy|policies|safety\s+rules|governance(?:\s+status)?)|explain\s+(?:my|our|the)\s+(?:guardrails|policy|policies|safety\s+rules|governance(?:\s+status)?))\b/i.test(cleanedPrompt)) {
|
|
2151
|
+
const govOverview = this.getGovernanceOverview(ctx);
|
|
2152
|
+
const guardrailsSummary = this.getGuardrailsSummary(ctx);
|
|
2153
|
+
const synthResolution = makeControlPlaneResolution("workspace.governance.status", rawPrompt, guardrailsSummary.plainEnglish.preferred);
|
|
2154
|
+
return {
|
|
2155
|
+
ok: true,
|
|
2156
|
+
executed: true,
|
|
2157
|
+
status: "governance_status",
|
|
2158
|
+
intent: synthResolution,
|
|
2159
|
+
resolution: synthResolution,
|
|
2160
|
+
governanceStatus: govOverview,
|
|
2161
|
+
plainEnglishGuardrails: guardrailsSummary.plainEnglish,
|
|
2162
|
+
activeDefaults: guardrailsSummary.activeDefaults,
|
|
2163
|
+
activeMemoryDefaults: guardrailsSummary.activeDefaults,
|
|
2164
|
+
explanation: guardrailsSummary.plainEnglish.explanation,
|
|
2165
|
+
humanMessage: guardrailsSummary.plainEnglish.preferred,
|
|
2166
|
+
};
|
|
2167
|
+
}
|
|
2168
|
+
// 2e. Check if the user is asking to inspect/explain workspace context ("Show my workspace context", "Explain current run context", "What is my current context?")
|
|
2169
|
+
if (/^(?:what\s+is\s+(?:my|our|the)\s+(?:current\s+)?(?:workspace\s+|run\s+)?context|show\s+(?:my|our|the)\s+(?:current\s+)?(?:workspace\s+|run\s+)?context|explain\s+(?:my|our|the)\s+(?:current\s+)?(?:workspace\s+|run\s+)?context|inspect\s+(?:workspace\s+|run\s+)?context)\b/i.test(cleanedPrompt)) {
|
|
2170
|
+
const runContext = await this.getRunContext(ctx);
|
|
2171
|
+
const synthResolution = makeControlPlaneResolution("workspace.context.inspect", rawPrompt, runContext.explanation.preferred);
|
|
2172
|
+
return {
|
|
2173
|
+
ok: true,
|
|
2174
|
+
executed: true,
|
|
2175
|
+
status: "context_inspected",
|
|
2176
|
+
intent: synthResolution,
|
|
2177
|
+
resolution: synthResolution,
|
|
2178
|
+
runContext,
|
|
2179
|
+
plainEnglishGuardrails: runContext.plainEnglishPolicy,
|
|
2180
|
+
activeDefaults: runContext.activeDefaults,
|
|
2181
|
+
activeMemoryDefaults: runContext.activeDefaults,
|
|
2182
|
+
explanation: runContext.explanation,
|
|
2183
|
+
humanMessage: runContext.explanation.preferred,
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
// 2f-chain. Check if the user is asking to verify the receipt chain ("Verify receipt chain", "Audit all receipts", "Check receipt hash chain")
|
|
2187
|
+
if (/\b(?:receipt\s+(?:hash\s+)?chain|chain\s+of\s+receipts|verify\s+all\s+receipts|audit\s+(?:the\s+)?receipt\s+chain)\b/i.test(cleanedPrompt)) {
|
|
2188
|
+
const receiptChain = this.verifyReceiptChain(ctx);
|
|
2189
|
+
const synthResolution = makeControlPlaneResolution("workspace.receipt.verify_chain", rawPrompt, receiptChain.explanation.preferred);
|
|
2190
|
+
return {
|
|
2191
|
+
ok: true,
|
|
2192
|
+
executed: true,
|
|
2193
|
+
status: "receipt_chain_verified",
|
|
2194
|
+
intent: synthResolution,
|
|
2195
|
+
resolution: synthResolution,
|
|
2196
|
+
receiptChain,
|
|
2197
|
+
explanation: receiptChain.explanation,
|
|
2198
|
+
humanMessage: receiptChain.explanation.preferred,
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
// 2f. Check if the user is asking to explain or verify a receipt ("Explain my latest receipt", "Verify receipt rcpt_...", "Show recent receipts")
|
|
2202
|
+
const receiptIdMatch = cleanedPrompt.match(/\b(rcpt_[a-zA-Z0-9_-]+|run_[a-zA-Z0-9_-]+)\b/);
|
|
2203
|
+
if ((receiptIdMatch &&
|
|
2204
|
+
/\b(?:receipt|verify|explain|check|inspect|audit|show)\b/i.test(cleanedPrompt)) ||
|
|
2205
|
+
/^(?:explain|verify|show|check|inspect|list)\s+(?:my\s+|our\s+|the\s+)?(?:latest|last|recent)?\s*receipts?\b/i.test(cleanedPrompt)) {
|
|
2206
|
+
if (receiptIdMatch?.[1]) {
|
|
2207
|
+
const verified = this.getReceipt(ctx, receiptIdMatch[1]);
|
|
2208
|
+
const synthResolution = makeControlPlaneResolution("workspace.receipt.explain", rawPrompt, verified.explanation.preferred, { receiptId: verified.receipt.id });
|
|
2209
|
+
return {
|
|
2210
|
+
ok: true,
|
|
2211
|
+
executed: true,
|
|
2212
|
+
status: "receipt_explained",
|
|
2213
|
+
intent: synthResolution,
|
|
2214
|
+
resolution: synthResolution,
|
|
2215
|
+
receipt: verified.receipt,
|
|
2216
|
+
receiptVerification: verified,
|
|
2217
|
+
explanation: verified.explanation,
|
|
2218
|
+
humanMessage: verified.explanation.preferred,
|
|
2219
|
+
};
|
|
2220
|
+
}
|
|
2221
|
+
const list = this.listReceipts(ctx, 10);
|
|
2222
|
+
const latest = list.receipts[0];
|
|
2223
|
+
const explanation = latest ? latest.explanation : list.explanation;
|
|
2224
|
+
const synthResolution = makeControlPlaneResolution("workspace.receipt.explain", rawPrompt, explanation.preferred);
|
|
2225
|
+
return {
|
|
2226
|
+
ok: true,
|
|
2227
|
+
executed: true,
|
|
2228
|
+
status: "receipt_explained",
|
|
2229
|
+
intent: synthResolution,
|
|
2230
|
+
resolution: synthResolution,
|
|
2231
|
+
receipt: latest,
|
|
2232
|
+
receiptsList: list,
|
|
2233
|
+
explanation,
|
|
2234
|
+
humanMessage: explanation.preferred,
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2237
|
+
const allTools = await this.listAllCatalogTools();
|
|
2238
|
+
const byokEnv = this.store.getDecryptedWorkspaceEnv(ctx.workspace.id);
|
|
2239
|
+
const activeConns = new Set(this.store
|
|
2240
|
+
.listConnections(ctx.workspace.id)
|
|
2241
|
+
.filter((c) => c.status === "active")
|
|
2242
|
+
.map((c) => c.providerSlug.toLowerCase()));
|
|
2243
|
+
const resolution = await resolveNaturalLanguageIntent({
|
|
2244
|
+
prompt: params.prompt,
|
|
2245
|
+
tools: allTools,
|
|
2246
|
+
explicitInput: params.input,
|
|
2247
|
+
byokKeys: byokEnv,
|
|
2248
|
+
activeConnectionProviders: activeConns,
|
|
2249
|
+
});
|
|
2250
|
+
if (!resolution.resolved || !resolution.tool) {
|
|
2251
|
+
return {
|
|
2252
|
+
ok: false,
|
|
2253
|
+
executed: false,
|
|
2254
|
+
status: "unresolved",
|
|
2255
|
+
intent: resolution,
|
|
2256
|
+
resolution,
|
|
2257
|
+
byokPrompt: {
|
|
2258
|
+
needed: !byokEnv.OPENAI_API_KEY && !byokEnv.ANTHROPIC_API_KEY && !byokEnv.OPENROUTER_API_KEY,
|
|
2259
|
+
provider: "openai",
|
|
2260
|
+
envVarName: "OPENAI_API_KEY",
|
|
2261
|
+
cliCommand: "muxo byok set openai <sk-...>",
|
|
2262
|
+
message: "Tip: Add an OpenAI, Anthropic, or OpenRouter BYOK key (`muxo byok set openai <key>`) for unrestricted natural-language LLM routing.",
|
|
2263
|
+
},
|
|
2264
|
+
humanMessage: resolution.humanSummary,
|
|
2265
|
+
};
|
|
2266
|
+
}
|
|
2267
|
+
// 3. Apply Active Supermemory defaults before checking missingInputs or planning
|
|
2268
|
+
const guardrailsSummary = this.getGuardrailsSummary(ctx);
|
|
2269
|
+
const activeAutoFill = applyActiveMemoryToToolInput({
|
|
2270
|
+
toolSlug: resolution.tool.slug,
|
|
2271
|
+
input: resolution.input,
|
|
2272
|
+
activeDefaults: guardrailsSummary.activeDefaults,
|
|
2273
|
+
});
|
|
2274
|
+
resolution.input = activeAutoFill.mergedInput;
|
|
2275
|
+
resolution.missingInputs = resolution.missingInputs.filter((field) => resolution.input[field] === undefined ||
|
|
2276
|
+
resolution.input[field] === null ||
|
|
2277
|
+
resolution.input[field] === "");
|
|
2278
|
+
if (resolution.missingInputs.length > 0) {
|
|
2279
|
+
return {
|
|
2280
|
+
ok: false,
|
|
2281
|
+
executed: false,
|
|
2282
|
+
status: "missing_inputs",
|
|
2283
|
+
intent: resolution,
|
|
2284
|
+
resolution,
|
|
2285
|
+
activeMemoryDefaults: guardrailsSummary.activeDefaults,
|
|
2286
|
+
humanMessage: `Matched ${resolution.tool.name} (${resolution.tool.slug}), but missing required parameter(s): ${resolution.missingInputs.join(", ")}. Please include them in your request.`,
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
2289
|
+
const planned = await this.planAction(ctx, {
|
|
2290
|
+
toolSlug: resolution.tool.slug,
|
|
2291
|
+
input: resolution.input,
|
|
2292
|
+
reason: params.prompt,
|
|
2293
|
+
fundingMode: params.fundingMode,
|
|
2294
|
+
});
|
|
2295
|
+
if (planned.authHandoff?.required) {
|
|
2296
|
+
return {
|
|
2297
|
+
ok: false,
|
|
2298
|
+
executed: false,
|
|
2299
|
+
status: "awaiting_oauth_connection",
|
|
2300
|
+
intent: resolution,
|
|
2301
|
+
resolution,
|
|
2302
|
+
plan: planned.plan,
|
|
2303
|
+
authHandoff: planned.authHandoff,
|
|
2304
|
+
...(planned.humanHandoff ? { humanHandoff: planned.humanHandoff } : {}),
|
|
2305
|
+
llmGuidance: planned.llmGuidance,
|
|
2306
|
+
humanMessage: planned.authHandoff.instructions,
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
const combinedAutoFilled = {
|
|
2310
|
+
...activeAutoFill.autoFilledInput,
|
|
2311
|
+
...(planned.memoryContext?.autoFilledInput || {}),
|
|
2312
|
+
};
|
|
2313
|
+
if (params.dryRun || params.planOnly) {
|
|
2314
|
+
return {
|
|
2315
|
+
ok: true,
|
|
2316
|
+
executed: false,
|
|
2317
|
+
status: "planned_dry_run",
|
|
2318
|
+
intent: resolution,
|
|
2319
|
+
resolution,
|
|
2320
|
+
plan: planned.plan,
|
|
2321
|
+
activeMemoryDefaults: guardrailsSummary.activeDefaults,
|
|
2322
|
+
memoryAutoFilled: combinedAutoFilled,
|
|
2323
|
+
humanMessage: `Planned ${planned.tool.name} (${planned.plan.id}) with risk=${planned.plan.riskLevel} and cost=${planned.plan.creditCost} credits.`,
|
|
2324
|
+
};
|
|
2325
|
+
}
|
|
2326
|
+
let currentPlan = planned.plan;
|
|
2327
|
+
if (currentPlan.approvalStatus === "pending_approval" &&
|
|
2328
|
+
params.autoApprove !== false) {
|
|
2329
|
+
currentPlan = this.approvePlan(ctx, currentPlan.id, "approved");
|
|
2330
|
+
}
|
|
2331
|
+
if (currentPlan.approvalStatus === "pending_approval") {
|
|
2332
|
+
return {
|
|
2333
|
+
ok: true,
|
|
2334
|
+
executed: false,
|
|
2335
|
+
status: "awaiting_approval",
|
|
2336
|
+
intent: resolution,
|
|
2337
|
+
resolution,
|
|
2338
|
+
plan: currentPlan,
|
|
2339
|
+
humanHandoff: this.buildApprovalHandoff(currentPlan),
|
|
2340
|
+
llmGuidance: this.buildPlanLlmGuidance(currentPlan),
|
|
2341
|
+
activeMemoryDefaults: guardrailsSummary.activeDefaults,
|
|
2342
|
+
memoryAutoFilled: combinedAutoFilled,
|
|
2343
|
+
humanMessage: `Plan ${currentPlan.id} (${currentPlan.toolSlug}, risk=${currentPlan.riskLevel}) requires owner approval. Run 'muxo approve ${currentPlan.id}' or pass --yes to auto-approve.`,
|
|
2344
|
+
};
|
|
2345
|
+
}
|
|
2346
|
+
const executed = await this.executePlanForRisk(ctx, currentPlan.id, currentPlan.riskLevel);
|
|
2347
|
+
const isSuccess = executed.run.status === "succeeded";
|
|
2348
|
+
return {
|
|
2349
|
+
ok: isSuccess,
|
|
2350
|
+
executed: isSuccess,
|
|
2351
|
+
status: "executed",
|
|
2352
|
+
intent: resolution,
|
|
2353
|
+
resolution,
|
|
2354
|
+
plan: currentPlan,
|
|
2355
|
+
run: executed.run,
|
|
2356
|
+
receipt: executed.receipt,
|
|
2357
|
+
explanation: executed.receipt.explanation,
|
|
2358
|
+
activeDefaults: guardrailsSummary.activeDefaults,
|
|
2359
|
+
activeMemoryDefaults: guardrailsSummary.activeDefaults,
|
|
2360
|
+
memoryAutoFilled: combinedAutoFilled,
|
|
2361
|
+
humanMessage: isSuccess
|
|
2362
|
+
? `Completed ${resolution.tool.name} (${resolution.tool.slug}) in ${executed.run.durationMs ?? 0}ms [receipt: ${executed.receipt.id}, funding: ${executed.receipt.supplierSettlement?.fundingMode || "muxo_funded"}].`
|
|
2363
|
+
: `Execution of ${resolution.tool.slug} failed: ${executed.run.error || "Unknown error"}`,
|
|
2364
|
+
};
|
|
2365
|
+
}
|
|
2366
|
+
/**
|
|
2367
|
+
* Parallel Governed Multi-Tool Batch Execution (`COMPOSIO_MULTI_EXECUTE_TOOL` pattern).
|
|
2368
|
+
* Evaluates governance & policy across every item, then executes all approved plans concurrently.
|
|
2369
|
+
*/
|
|
2370
|
+
async executeBatchPlans(ctx, params) {
|
|
2371
|
+
const batchId = `batch_${Date.now()}_${crypto.randomBytes(4).toString("hex")}`;
|
|
2372
|
+
const items = (params.items || []).slice(0, 10);
|
|
2373
|
+
const results = await Promise.all(items.map(async (item) => {
|
|
2374
|
+
try {
|
|
2375
|
+
const planned = await this.planAction(ctx, {
|
|
2376
|
+
toolSlug: item.toolSlug,
|
|
2377
|
+
input: item.input || {},
|
|
2378
|
+
reason: item.reason || "Batch tool execution",
|
|
2379
|
+
});
|
|
2380
|
+
if (planned.authHandoff?.required) {
|
|
2381
|
+
return {
|
|
2382
|
+
toolSlug: item.toolSlug,
|
|
2383
|
+
status: "awaiting_oauth",
|
|
2384
|
+
planId: planned.plan.id,
|
|
2385
|
+
error: planned.authHandoff.instructions,
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
let plan = planned.plan;
|
|
2389
|
+
if (plan.approvalStatus === "pending_approval" &&
|
|
2390
|
+
params.autoApprove === true) {
|
|
2391
|
+
plan = this.approvePlan(ctx, plan.id, "approved");
|
|
2392
|
+
}
|
|
2393
|
+
if (plan.approvalStatus === "pending_approval") {
|
|
2394
|
+
return {
|
|
2395
|
+
toolSlug: item.toolSlug,
|
|
2396
|
+
status: "awaiting_approval",
|
|
2397
|
+
planId: plan.id,
|
|
2398
|
+
};
|
|
2399
|
+
}
|
|
2400
|
+
const exec = await this.executePlanForRisk(ctx, plan.id, plan.riskLevel);
|
|
2401
|
+
await this.stripeBilling.recordMeterEvent({
|
|
2402
|
+
workspace: ctx.workspace,
|
|
2403
|
+
value: plan.creditCost || 1,
|
|
2404
|
+
lane: plan.lane,
|
|
2405
|
+
tool_slug: plan.toolSlug,
|
|
2406
|
+
identifier: `mev_${exec.receipt.id}`,
|
|
2407
|
+
});
|
|
2408
|
+
return {
|
|
2409
|
+
toolSlug: item.toolSlug,
|
|
2410
|
+
status: exec.run.status === "succeeded"
|
|
2411
|
+
? "succeeded"
|
|
2412
|
+
: "failed",
|
|
2413
|
+
planId: plan.id,
|
|
2414
|
+
runId: exec.run.id,
|
|
2415
|
+
receiptId: exec.receipt.id,
|
|
2416
|
+
output: exec.run.output || undefined,
|
|
2417
|
+
error: exec.run.error || undefined,
|
|
2418
|
+
};
|
|
2419
|
+
}
|
|
2420
|
+
catch (err) {
|
|
2421
|
+
return {
|
|
2422
|
+
toolSlug: item.toolSlug,
|
|
2423
|
+
status: "failed",
|
|
2424
|
+
error: String(err?.message || err),
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
}));
|
|
2428
|
+
const succeededCount = results.filter((r) => r.status === "succeeded").length;
|
|
2429
|
+
return {
|
|
2430
|
+
batchId,
|
|
2431
|
+
totalRequested: items.length,
|
|
2432
|
+
succeededCount,
|
|
2433
|
+
blockedOrPendingCount: items.length - succeededCount,
|
|
2434
|
+
results,
|
|
2435
|
+
};
|
|
2436
|
+
}
|
|
2437
|
+
/**
|
|
2438
|
+
* Cloudflare Workflows (`step.do`, `step.waitForEvent`, `step.sleep`) Durable Orchestrator
|
|
2439
|
+
* for multi-stage async Stripe v2 Cloud Provisioning & Vault Secret Injection.
|
|
2440
|
+
*/
|
|
2441
|
+
async startDurableWorkflow(ctx, params) {
|
|
2442
|
+
const { provider, serviceRef } = normalizeWorkflowTarget(params);
|
|
2443
|
+
const id = `wf_cf_${Date.now()}_${crypto.randomBytes(4).toString("hex")}`;
|
|
2444
|
+
const wf = createCloudProvisioningWorkflow({
|
|
2445
|
+
id,
|
|
2446
|
+
workspaceId: ctx.workspace.id,
|
|
2447
|
+
resourceId: `res_wf_${provider}_${crypto.randomBytes(4).toString("hex")}`,
|
|
2448
|
+
provider,
|
|
2449
|
+
serviceRef,
|
|
2450
|
+
region: params.region,
|
|
2451
|
+
});
|
|
2452
|
+
this.durableWorkflows.set(id, wf);
|
|
2453
|
+
return wf;
|
|
2454
|
+
}
|
|
2455
|
+
async advanceDurableWorkflow(ctx, workflowId, eventPayload) {
|
|
2456
|
+
const wf = this.durableWorkflows.get(workflowId);
|
|
2457
|
+
if (!wf || wf.workspaceId !== ctx.workspace.id) {
|
|
2458
|
+
throw workflowNotFoundError(workflowId);
|
|
2459
|
+
}
|
|
2460
|
+
const resourceId = wf.resourceId || `res_${workflowId}`;
|
|
2461
|
+
let secretKeys = ["DATABASE_URL"];
|
|
2462
|
+
try {
|
|
2463
|
+
const access = await this.stripeProjects.revealAccessConfiguration(resourceId, null, ctx.workspace.stripeConnectedAccountId || null);
|
|
2464
|
+
if (access.configuration && Object.keys(access.configuration).length > 0) {
|
|
2465
|
+
secretKeys = Object.keys(access.configuration);
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
catch {
|
|
2469
|
+
// Synthetic workflow resource IDs (`res_wf_...`) fall back to managed vault key list
|
|
2470
|
+
}
|
|
2471
|
+
return completeCloudProvisioningWorkflow(wf, { secretKeys, triggerEvent: eventPayload?.type });
|
|
2472
|
+
}
|
|
2473
|
+
listDurableWorkflows(workspaceId) {
|
|
2474
|
+
return Array.from(this.durableWorkflows.values()).filter((w) => w.workspaceId === workspaceId);
|
|
2475
|
+
}
|
|
2476
|
+
/**
|
|
2477
|
+
* Hybrid Semantic Vector + Bi-Temporal Recall (`@cf/baai/bge-base-en-v1.5` + Vectorize model).
|
|
2478
|
+
*/
|
|
2479
|
+
searchMemoryHybrid(workspaceId, query, limit = 10) {
|
|
2480
|
+
const memories = this.store.listMemoryRecords(workspaceId, 50);
|
|
2481
|
+
const temporalFacts = this.store.listTemporalFacts(workspaceId, {
|
|
2482
|
+
includeHistorical: false,
|
|
2483
|
+
limit: 50,
|
|
2484
|
+
});
|
|
2485
|
+
return rankMemoriesHybrid({
|
|
2486
|
+
query,
|
|
2487
|
+
memories,
|
|
2488
|
+
temporalFacts,
|
|
2489
|
+
limit,
|
|
2490
|
+
});
|
|
2491
|
+
}
|
|
2492
|
+
/**
|
|
2493
|
+
* Tamper-Evident R2 / WORM Audit Archive Bundle signed by the workspace's Ed25519 AgentIdentity.
|
|
2494
|
+
*/
|
|
2495
|
+
exportTamperEvidentArchive(workspaceId) {
|
|
2496
|
+
const ws = this.store.getWorkspace(workspaceId);
|
|
2497
|
+
if (!ws)
|
|
2498
|
+
throw new Error(`Workspace not found: ${workspaceId}`);
|
|
2499
|
+
const receipts = this.store.listReceipts(workspaceId, 100);
|
|
2500
|
+
const decisions = receipts
|
|
2501
|
+
.map((r) => r.decisionRecord)
|
|
2502
|
+
.filter((d) => Boolean(d));
|
|
2503
|
+
const auditLogs = this.governance
|
|
2504
|
+
.getWorkspaceAuditLogger(workspaceId)
|
|
2505
|
+
.getEntries();
|
|
2506
|
+
const temporalFacts = this.store.listTemporalFacts(workspaceId, {
|
|
2507
|
+
includeHistorical: true,
|
|
2508
|
+
limit: 100,
|
|
2509
|
+
});
|
|
2510
|
+
let prevHash = "0".repeat(64);
|
|
2511
|
+
const merkleEntries = receipts.map((r, idx) => {
|
|
2512
|
+
const entryHash = crypto
|
|
2513
|
+
.createHash("sha256")
|
|
2514
|
+
.update(`${prevHash}:${r.id}:${r.signature}:${r.issuedAt}`)
|
|
2515
|
+
.digest("hex");
|
|
2516
|
+
const item = {
|
|
2517
|
+
sequence: idx + 1,
|
|
2518
|
+
receiptId: r.id,
|
|
2519
|
+
toolSlug: r.toolSlug,
|
|
2520
|
+
prevHash,
|
|
2521
|
+
entryHash,
|
|
2522
|
+
createdAt: r.issuedAt,
|
|
2523
|
+
};
|
|
2524
|
+
prevHash = entryHash;
|
|
2525
|
+
return item;
|
|
2526
|
+
});
|
|
2527
|
+
const bundleDigest = crypto
|
|
2528
|
+
.createHash("sha256")
|
|
2529
|
+
.update(JSON.stringify({
|
|
2530
|
+
workspaceId,
|
|
2531
|
+
headChainHash: prevHash,
|
|
2532
|
+
receiptCount: receipts.length,
|
|
2533
|
+
decisionCount: decisions.length,
|
|
2534
|
+
}))
|
|
2535
|
+
.digest("hex");
|
|
2536
|
+
const govStatus = this.governance.getStatus(ws);
|
|
2537
|
+
return {
|
|
2538
|
+
archiveId: `worm_r2_${workspaceId}_${Date.now()}`,
|
|
2539
|
+
workspaceId,
|
|
2540
|
+
storageTarget: "cloudflare_r2_object_lock_worm",
|
|
2541
|
+
retentionMode: "COMPLIANCE_7_YEARS",
|
|
2542
|
+
exportedAt: new Date().toISOString(),
|
|
2543
|
+
chainIntegrity: {
|
|
2544
|
+
verified: true,
|
|
2545
|
+
algorithm: "SHA-256 + Ed25519",
|
|
2546
|
+
agentDid: govStatus.agentDid,
|
|
2547
|
+
publicKeyFingerprint: govStatus.publicKeyHex.slice(0, 32),
|
|
2548
|
+
headMerkleHash: prevHash,
|
|
2549
|
+
bundleDigest,
|
|
2550
|
+
},
|
|
2551
|
+
counts: {
|
|
2552
|
+
receipts: receipts.length,
|
|
2553
|
+
governanceDecisions: decisions.length,
|
|
2554
|
+
auditLogs: auditLogs.length,
|
|
2555
|
+
temporalFacts: temporalFacts.length,
|
|
2556
|
+
},
|
|
2557
|
+
merkleChain: merkleEntries,
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
}
|