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