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