@muxoai/sdk 1.3.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 +19 -4
- package/.codex/config.toml +9 -2
- package/ARCHITECTURE.md +16 -6
- package/README.md +69 -24
- package/dist/clerk.d.ts +35 -0
- package/dist/clerk.js +201 -0
- package/dist/cli.js +1589 -103
- package/dist/composio-catalog.d.ts +234 -0
- package/dist/composio-catalog.js +1894 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.js +27 -1
- package/dist/durable-workflow.d.ts +45 -0
- package/dist/durable-workflow.js +90 -0
- package/dist/engine.d.ts +315 -6
- package/dist/engine.js +1778 -94
- package/dist/mcp-schema.d.ts +286 -0
- package/dist/mcp-schema.js +2559 -0
- package/dist/mcp.d.ts +2 -25
- package/dist/mcp.js +949 -507
- package/dist/memory-graph.d.ts +21 -0
- package/dist/memory-graph.js +173 -0
- package/dist/nl.js +214 -35
- package/dist/oauth.d.ts +32 -0
- package/dist/oauth.js +134 -0
- package/dist/policy.d.ts +190 -0
- package/dist/policy.js +1597 -0
- package/dist/providers/composio-instant.d.ts +4 -0
- package/dist/providers/composio-instant.js +890 -535
- package/dist/providers/composio-sdk.d.ts +126 -5
- package/dist/providers/composio-sdk.js +902 -79
- package/dist/providers/microsoft-governance.d.ts +89 -3
- package/dist/providers/microsoft-governance.js +673 -26
- package/dist/providers/stripe-billing.d.ts +42 -0
- package/dist/providers/stripe-billing.js +244 -5
- package/dist/providers/stripe-projects.d.ts +459 -4
- package/dist/providers/stripe-projects.js +2674 -122
- package/dist/providers/supermemory.d.ts +102 -6
- package/dist/providers/supermemory.js +609 -55
- package/dist/provisioning-managed.d.ts +50 -0
- package/dist/provisioning-managed.js +155 -0
- package/dist/sdk.d.ts +705 -7
- package/dist/sdk.js +488 -10
- package/dist/server.js +2646 -348
- package/dist/store.d.ts +138 -6
- package/dist/store.js +932 -58
- package/dist/types.d.ts +312 -4
- package/dist/ui/console-html.d.ts +62 -0
- package/dist/ui/console-html.js +3742 -2228
- package/dist/ui/design-tokens.d.ts +177 -0
- package/dist/ui/design-tokens.js +210 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +5 -4
- package/plugins/muxo/.app.json +1 -1
- package/plugins/muxo/.claude-plugin/plugin.json +3 -3
- package/plugins/muxo/.codex-plugin/plugin.json +19 -10
- package/plugins/muxo/.cursor-plugin/plugin.json +30 -0
- package/plugins/muxo/.mcp.json +1 -33
- package/plugins/muxo/assets/logo.png +0 -0
- package/plugins/muxo/hooks/session-start.sh +16 -4
- package/plugins/muxo/hooks/user-prompt-submit.sh +14 -1
- package/plugins/muxo/mcp.json +0 -10
- package/plugins/muxo/plugin.json +65 -8
- package/plugins/muxo/skills/muxo/SKILL.md +142 -14
- package/plugins/muxo/skills/muxo/agents/openai.yaml +12 -0
- package/plugins/muxo/skills/muxo/references/cli.md +9 -3
- package/plugins/muxo/skills/muxo/references/mcp.md +22 -10
- package/plugins/muxo/skills/muxo-activity-summary/SKILL.md +81 -0
- package/plugins/muxo/skills/setup/SKILL.md +17 -3
- package/plugins/muxo/skills/setup/agents/openai.yaml +12 -0
- package/src/catalogues/stripe-projects-catalog.json +140 -0
package/dist/engine.js
CHANGED
|
@@ -1,10 +1,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
|
-
import { StripeProjectsAdapter } from "./providers/stripe-projects.js";
|
|
7
|
-
import { SupermemoryAdapter } from "./providers/supermemory.js";
|
|
12
|
+
import { StripeProjectsAdapter, isPaidCloudProvisioning, } from "./providers/stripe-projects.js";
|
|
13
|
+
import { SupermemoryAdapter, applyActiveMemoryToToolInput, explainMemoryContext, extractActiveMemoryDefaults, parseNaturalLanguageMemoryInstruction, rankMemoriesHybrid, } from "./providers/supermemory.js";
|
|
14
|
+
function makeControlPlaneResolution(toolSlug, prompt, humanSummary, input = {}) {
|
|
15
|
+
return {
|
|
16
|
+
resolved: true,
|
|
17
|
+
confidence: 0.98,
|
|
18
|
+
resolutionMethod: "semantic_router",
|
|
19
|
+
byokUsed: null,
|
|
20
|
+
tool: null,
|
|
21
|
+
toolSlug,
|
|
22
|
+
input,
|
|
23
|
+
extractedInput: input,
|
|
24
|
+
missingInputs: [],
|
|
25
|
+
needsConnection: false,
|
|
26
|
+
suggestedByokProvider: null,
|
|
27
|
+
reason: prompt,
|
|
28
|
+
alternatives: [],
|
|
29
|
+
humanSummary,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
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;
|
|
@@ -26,10 +51,14 @@ export class MuxoEngine {
|
|
|
26
51
|
}
|
|
27
52
|
// 1. get_profile
|
|
28
53
|
getProfile(ctx) {
|
|
54
|
+
const nickname = (ctx.user.email || "").split("@")[0] ||
|
|
55
|
+
ctx.user.name.toLowerCase().replace(/\s+/g, ".") ||
|
|
56
|
+
ctx.user.id;
|
|
29
57
|
return {
|
|
30
58
|
id: ctx.user.id,
|
|
31
59
|
name: ctx.user.name,
|
|
32
60
|
email: ctx.user.email,
|
|
61
|
+
nickname,
|
|
33
62
|
user: ctx.user,
|
|
34
63
|
actorId: ctx.actorId,
|
|
35
64
|
authType: ctx.authType,
|
|
@@ -44,6 +73,25 @@ export class MuxoEngine {
|
|
|
44
73
|
const resources = this.store.listResources(ws.id);
|
|
45
74
|
const connections = this.store.listConnections(ws.id);
|
|
46
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
|
+
});
|
|
47
95
|
return {
|
|
48
96
|
organization: {
|
|
49
97
|
id: ws.id,
|
|
@@ -58,11 +106,17 @@ export class MuxoEngine {
|
|
|
58
106
|
},
|
|
59
107
|
supplierSettlementSummary,
|
|
60
108
|
policy: ws.policy,
|
|
109
|
+
plainEnglishPolicy,
|
|
110
|
+
llmPolicyBrief,
|
|
111
|
+
activeDefaults: inferred.activeDefaults,
|
|
112
|
+
explanation: plainEnglishPolicy.explanation,
|
|
61
113
|
activeConnectionsCount: connections.filter((c) => c.status === "active").length,
|
|
62
114
|
provisionedResourcesCount: resources.filter((r) => r.status === "active").length,
|
|
63
115
|
projectsEnvironment: ws.projectsEnvName,
|
|
64
116
|
createdAt: ws.createdAt,
|
|
65
117
|
},
|
|
118
|
+
llmPolicyBrief,
|
|
119
|
+
activeDefaults: inferred.activeDefaults,
|
|
66
120
|
};
|
|
67
121
|
}
|
|
68
122
|
// 3. search_tools
|
|
@@ -70,7 +124,16 @@ export class MuxoEngine {
|
|
|
70
124
|
const limit = Math.min(100, Math.max(1, Number(params.limit || 25)));
|
|
71
125
|
const lane = params.lane || "all";
|
|
72
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));
|
|
73
133
|
const results = [];
|
|
134
|
+
if (lane === "all" && prefersStripeProjects) {
|
|
135
|
+
results.push(...this.stripeProjects.searchTools(query, limit));
|
|
136
|
+
}
|
|
74
137
|
if (lane === "all" || lane === "composio_instant") {
|
|
75
138
|
results.push(...this.composioInstant.searchTools(query, limit));
|
|
76
139
|
}
|
|
@@ -78,8 +141,10 @@ export class MuxoEngine {
|
|
|
78
141
|
const sdkTools = await this.composioSdk.searchTools(query, limit);
|
|
79
142
|
results.push(...sdkTools);
|
|
80
143
|
}
|
|
81
|
-
if (lane === "
|
|
144
|
+
if (lane === "stripe_projects" || (lane === "all" && !prefersStripeProjects)) {
|
|
82
145
|
results.push(...this.stripeProjects.searchTools(query, limit));
|
|
146
|
+
}
|
|
147
|
+
if (lane === "all" || lane === "stripe_projects") {
|
|
83
148
|
const billingTools = this.stripeBilling.listTools().filter((t) => {
|
|
84
149
|
if (!query)
|
|
85
150
|
return true;
|
|
@@ -100,7 +165,7 @@ export class MuxoEngine {
|
|
|
100
165
|
async listAllCatalogTools() {
|
|
101
166
|
const instant = this.composioInstant.searchTools(undefined, 100);
|
|
102
167
|
const sdk = await this.composioSdk.searchTools(undefined, 100);
|
|
103
|
-
const proj = this.stripeProjects.
|
|
168
|
+
const proj = this.stripeProjects.getTools();
|
|
104
169
|
const billing = this.stripeBilling.listTools();
|
|
105
170
|
return [...instant, ...billing, ...sdk, ...proj];
|
|
106
171
|
}
|
|
@@ -122,14 +187,18 @@ export class MuxoEngine {
|
|
|
122
187
|
const sdk = await this.composioSdk.inspectTool(normalized);
|
|
123
188
|
if (sdk)
|
|
124
189
|
return sdk;
|
|
125
|
-
throw
|
|
190
|
+
throw unknownToolError(normalized);
|
|
126
191
|
}
|
|
127
192
|
// 5. connection_status
|
|
128
193
|
async connectionStatus(ctx, params) {
|
|
129
194
|
if (params?.syncLive !== false) {
|
|
130
195
|
// Sync live Composio connected accounts for this workspace
|
|
131
|
-
const
|
|
196
|
+
const composioByok = this.store.getByokKey(ctx.workspace.id, "composio");
|
|
197
|
+
const liveComposio = await this.composioSdk.listLiveConnections(ctx.workspace.id, composioByok);
|
|
132
198
|
for (const c of liveComposio) {
|
|
199
|
+
if (isStripeProjectsExclusiveProvider(c.providerSlug)) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
133
202
|
this.store.upsertConnection({
|
|
134
203
|
workspaceId: ctx.workspace.id,
|
|
135
204
|
lane: "composio_sdk",
|
|
@@ -141,7 +210,8 @@ export class MuxoEngine {
|
|
|
141
210
|
}
|
|
142
211
|
// Sync live Stripe Projects linked providers
|
|
143
212
|
try {
|
|
144
|
-
const
|
|
213
|
+
const stripeByok = this.store.getByokKey(ctx.workspace.id, "stripe");
|
|
214
|
+
const projStatus = await this.stripeProjects.getLiveStatus(stripeByok);
|
|
145
215
|
for (const p of projStatus.providers) {
|
|
146
216
|
this.store.upsertConnection({
|
|
147
217
|
workspaceId: ctx.workspace.id,
|
|
@@ -158,15 +228,63 @@ export class MuxoEngine {
|
|
|
158
228
|
// Keep cached connections if CLI is busy
|
|
159
229
|
}
|
|
160
230
|
}
|
|
161
|
-
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
|
+
});
|
|
162
251
|
if (params?.provider) {
|
|
163
|
-
const target = params.provider.toLowerCase();
|
|
164
|
-
|
|
165
|
-
|
|
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
|
+
}
|
|
166
273
|
}
|
|
274
|
+
const targetConn = connections[0] || null;
|
|
275
|
+
const isActive = targetConn
|
|
276
|
+
? targetConn.status === "active"
|
|
277
|
+
: connections.some((c) => c.status === "active");
|
|
167
278
|
return {
|
|
279
|
+
ok: true,
|
|
168
280
|
workspaceId: ctx.workspace.id,
|
|
169
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,
|
|
170
288
|
connections,
|
|
171
289
|
summary: {
|
|
172
290
|
total: connections.length,
|
|
@@ -176,28 +294,43 @@ export class MuxoEngine {
|
|
|
176
294
|
},
|
|
177
295
|
};
|
|
178
296
|
}
|
|
297
|
+
getHostedSiteHtml(slug, filePath) {
|
|
298
|
+
return this.stripeProjects.getHostedSiteHtml(slug, filePath);
|
|
299
|
+
}
|
|
179
300
|
// 6. start_connection
|
|
180
301
|
async startConnection(ctx, params) {
|
|
181
302
|
const rawProvider = params.provider.trim().toLowerCase();
|
|
182
|
-
const
|
|
183
|
-
|
|
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);
|
|
184
309
|
if (isProjectsLane) {
|
|
185
|
-
const
|
|
310
|
+
const stripeByok = this.store.getByokKey(ctx.workspace.id, "stripe");
|
|
311
|
+
const linked = await this.stripeProjects.linkProvider(cleanSlug, stripeByok);
|
|
186
312
|
const record = this.store.upsertConnection({
|
|
187
313
|
workspaceId: ctx.workspace.id,
|
|
188
314
|
lane: "stripe_projects",
|
|
189
315
|
providerSlug: linked.providerSlug,
|
|
190
316
|
providerName: linked.providerSlug.charAt(0).toUpperCase() +
|
|
191
317
|
linked.providerSlug.slice(1),
|
|
192
|
-
status: linked.status,
|
|
318
|
+
status: linked.status === "requires_review" ? "pending" : linked.status,
|
|
319
|
+
externalAccountId: linked.connectionRequestId || null,
|
|
193
320
|
redirectUrl: linked.redirectUrl,
|
|
194
321
|
});
|
|
195
|
-
return {
|
|
322
|
+
return {
|
|
323
|
+
...record,
|
|
324
|
+
message: linked.message,
|
|
325
|
+
...(linked.workflowStatus ? { workflowStatus: linked.workflowStatus } : {}),
|
|
326
|
+
};
|
|
196
327
|
}
|
|
328
|
+
const composioByok = this.store.getByokKey(ctx.workspace.id, "composio");
|
|
197
329
|
const started = await this.composioSdk.startConnection({
|
|
198
330
|
workspaceId: ctx.workspace.id,
|
|
199
331
|
providerSlug: cleanSlug,
|
|
200
332
|
callbackUrl: params.callbackUrl,
|
|
333
|
+
composioApiKey: composioByok,
|
|
201
334
|
});
|
|
202
335
|
const record = this.store.upsertConnection({
|
|
203
336
|
workspaceId: ctx.workspace.id,
|
|
@@ -217,13 +350,46 @@ export class MuxoEngine {
|
|
|
217
350
|
}
|
|
218
351
|
// 6b. wait_for_connection (OAuth handoff polling & recovery, like COMPOSIO_WAIT_FOR_CONNECTIONS)
|
|
219
352
|
async waitForConnection(ctx, params) {
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
.
|
|
223
|
-
|
|
224
|
-
|
|
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;
|
|
225
361
|
const pollIntervalMs = Math.min(5000, Math.max(200, Number(params.pollIntervalMs ?? 1500)));
|
|
226
|
-
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
|
+
}
|
|
227
393
|
let current = this.store
|
|
228
394
|
.listConnections(ctx.workspace.id)
|
|
229
395
|
.find((c) => c.providerSlug.toLowerCase() === cleanSlug);
|
|
@@ -236,24 +402,19 @@ export class MuxoEngine {
|
|
|
236
402
|
}
|
|
237
403
|
}
|
|
238
404
|
while (true) {
|
|
405
|
+
polledAttempts += 1;
|
|
239
406
|
const latestLocal = this.store
|
|
240
407
|
.listConnections(ctx.workspace.id)
|
|
241
408
|
.find((c) => c.providerSlug.toLowerCase() === cleanSlug);
|
|
242
409
|
if (latestLocal) {
|
|
243
410
|
current = latestLocal;
|
|
244
411
|
if (latestLocal.status === "active") {
|
|
245
|
-
return {
|
|
246
|
-
connected: true,
|
|
247
|
-
provider: cleanSlug,
|
|
248
|
-
status: "active",
|
|
249
|
-
connection: latestLocal,
|
|
250
|
-
redirectUrl: null,
|
|
251
|
-
message: `${latestLocal.providerName} OAuth handoff completed and connection is active.`,
|
|
252
|
-
};
|
|
412
|
+
return connectedResult(latestLocal, `${latestLocal.providerName} OAuth handoff completed and connection is active.`);
|
|
253
413
|
}
|
|
254
414
|
}
|
|
255
415
|
if (current?.externalAccountId && current.lane === "composio_sdk") {
|
|
256
|
-
const
|
|
416
|
+
const composioByok = this.store.getByokKey(ctx.workspace.id, "composio");
|
|
417
|
+
const direct = await this.composioSdk.getConnectedAccountStatus(current.externalAccountId, composioByok);
|
|
257
418
|
if (direct && direct.status === "active") {
|
|
258
419
|
current = this.store.upsertConnection({
|
|
259
420
|
workspaceId: ctx.workspace.id,
|
|
@@ -264,14 +425,7 @@ export class MuxoEngine {
|
|
|
264
425
|
externalAccountId: direct.id,
|
|
265
426
|
redirectUrl: null,
|
|
266
427
|
});
|
|
267
|
-
return {
|
|
268
|
-
connected: true,
|
|
269
|
-
provider: cleanSlug,
|
|
270
|
-
status: "active",
|
|
271
|
-
connection: current,
|
|
272
|
-
redirectUrl: null,
|
|
273
|
-
message: `${current.providerName} OAuth handoff completed and connection is active.`,
|
|
274
|
-
};
|
|
428
|
+
return connectedResult(current, `${current.providerName} OAuth handoff completed and connection is active.`);
|
|
275
429
|
}
|
|
276
430
|
}
|
|
277
431
|
const synced = await this.connectionStatus(ctx, {
|
|
@@ -283,14 +437,7 @@ export class MuxoEngine {
|
|
|
283
437
|
if (found) {
|
|
284
438
|
current = found;
|
|
285
439
|
if (found.status === "active") {
|
|
286
|
-
return {
|
|
287
|
-
connected: true,
|
|
288
|
-
provider: cleanSlug,
|
|
289
|
-
status: "active",
|
|
290
|
-
connection: found,
|
|
291
|
-
redirectUrl: null,
|
|
292
|
-
message: `${found.providerName} connection is active and ready for governed execution.`,
|
|
293
|
-
};
|
|
440
|
+
return connectedResult(found, `${found.providerName} connection is active and ready for governed execution.`);
|
|
294
441
|
}
|
|
295
442
|
}
|
|
296
443
|
if (Date.now() >= deadline) {
|
|
@@ -301,12 +448,27 @@ export class MuxoEngine {
|
|
|
301
448
|
if (!current) {
|
|
302
449
|
current = await this.startConnection(ctx, { provider: cleanSlug });
|
|
303
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
|
+
}
|
|
304
461
|
return {
|
|
462
|
+
ok: true,
|
|
305
463
|
connected: false,
|
|
464
|
+
ready: false,
|
|
465
|
+
timedOut: true,
|
|
306
466
|
provider: cleanSlug,
|
|
307
467
|
status: current.status,
|
|
308
468
|
connection: current,
|
|
309
469
|
redirectUrl: current.redirectUrl,
|
|
470
|
+
polledAttempts,
|
|
471
|
+
elapsedMs: Date.now() - startedAt,
|
|
310
472
|
message: current.redirectUrl
|
|
311
473
|
? `Awaiting user OAuth authorization for ${current.providerName}. Open ${current.redirectUrl} in a browser, then re-run wait_for_connection.`
|
|
312
474
|
: `Connection for ${current.providerName} is currently ${current.status}.`,
|
|
@@ -328,21 +490,29 @@ export class MuxoEngine {
|
|
|
328
490
|
.find((c) => c.providerSlug.toLowerCase() === cleanSlug);
|
|
329
491
|
const accountId = params.connectedAccountId || existing?.externalAccountId || null;
|
|
330
492
|
let resolvedStatus = "pending";
|
|
493
|
+
const composioByok = this.store.getByokKey(ws.id, "composio");
|
|
331
494
|
if (accountId) {
|
|
332
|
-
const live = await this.composioSdk.getConnectedAccountStatus(accountId);
|
|
495
|
+
const live = await this.composioSdk.getConnectedAccountStatus(accountId, composioByok);
|
|
333
496
|
if (live) {
|
|
334
497
|
resolvedStatus = live.status;
|
|
335
498
|
}
|
|
336
499
|
}
|
|
337
500
|
if (resolvedStatus !== "active") {
|
|
338
|
-
const liveList = await this.composioSdk.listLiveConnections(ws.id);
|
|
501
|
+
const liveList = await this.composioSdk.listLiveConnections(ws.id, composioByok);
|
|
339
502
|
const match = liveList.find((c) => c.providerSlug === cleanSlug);
|
|
340
503
|
if (match) {
|
|
341
504
|
resolvedStatus = match.status;
|
|
342
505
|
}
|
|
343
506
|
}
|
|
344
507
|
const rawCallbackStatus = String(params.status || "").toLowerCase();
|
|
345
|
-
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" ||
|
|
346
516
|
rawCallbackStatus === "active" ||
|
|
347
517
|
rawCallbackStatus === "connected") {
|
|
348
518
|
resolvedStatus = "active";
|
|
@@ -375,6 +545,9 @@ export class MuxoEngine {
|
|
|
375
545
|
tool = await this.inspectTool(params.toolSlug);
|
|
376
546
|
}
|
|
377
547
|
catch (inspectErr) {
|
|
548
|
+
if (looksLikeToolSlug(params.toolSlug)) {
|
|
549
|
+
throw inspectErr;
|
|
550
|
+
}
|
|
378
551
|
// Natural-language fallback: if an agent or human passed plain English as toolSlug/instruction, resolve it!
|
|
379
552
|
const nlSource = `${params.toolSlug} ${params.reason || ""}`.trim();
|
|
380
553
|
const allTools = await this.listAllCatalogTools();
|
|
@@ -392,8 +565,14 @@ export class MuxoEngine {
|
|
|
392
565
|
resolvedInput = nl.input;
|
|
393
566
|
resolvedReason = params.reason || nl.reason || nlSource;
|
|
394
567
|
}
|
|
395
|
-
const byokActive =
|
|
396
|
-
|
|
568
|
+
const byokActive = params.fundingMode === "muxo_funded"
|
|
569
|
+
? false
|
|
570
|
+
: this.store.hasByokForLane(ws.id, tool.lane, tool.slug);
|
|
571
|
+
const isPaidResourcePlan = isPaidCloudProvisioning(tool, resolvedInput);
|
|
572
|
+
const baseCreditCost = isPaidResourcePlan
|
|
573
|
+
? Math.max(tool.creditCost, 25)
|
|
574
|
+
: tool.creditCost;
|
|
575
|
+
const effectiveCreditCost = byokActive ? 0 : baseCreditCost;
|
|
397
576
|
if (ws.monthlyActionsUsed >= ws.monthlyActionsLimit) {
|
|
398
577
|
throw new Error(`Monthly action limit reached (${ws.monthlyActionsUsed}/${ws.monthlyActionsLimit}). Upgrade to Starter ($29/mo) to unlock 5,000 actions/mo.`);
|
|
399
578
|
}
|
|
@@ -403,7 +582,20 @@ export class MuxoEngine {
|
|
|
403
582
|
if (effectiveCreditCost > ws.policy.maxCreditsPerRun) {
|
|
404
583
|
throw new Error(`Tool credit cost (${effectiveCreditCost}) exceeds workspace policy maxCreditsPerRun (${ws.policy.maxCreditsPerRun}).`);
|
|
405
584
|
}
|
|
406
|
-
// 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:
|
|
407
599
|
// Agent -> McpSecurityScanner -> PolicyEngine -> AgentIdentity (Ed25519 DID) -> AuditLogger -> DecisionRecord
|
|
408
600
|
const decisionRecord = this.governance.evaluatePlan({
|
|
409
601
|
workspace: ws,
|
|
@@ -415,12 +607,7 @@ export class MuxoEngine {
|
|
|
415
607
|
if (decisionRecord.verdict === "deny") {
|
|
416
608
|
throw new Error(`Microsoft Agent Governance denied plan for ${tool.slug} (rule: ${decisionRecord.matchedRule}): ${decisionRecord.reason}`);
|
|
417
609
|
}
|
|
418
|
-
|
|
419
|
-
const memoryQuery = `${tool.slug} ${tool.name} ${resolvedReason}`.trim();
|
|
420
|
-
const memoryContext = await this.supermemory.getContextForWorkspace(ws.id, memoryQuery);
|
|
421
|
-
const requiresHumanApproval = decisionRecord.verdict === "escalate" ||
|
|
422
|
-
(ws.policy.mode === "enforce" &&
|
|
423
|
-
ws.policy.requireApprovalFor.includes(tool.riskLevel));
|
|
610
|
+
const requiresHumanApproval = decisionRecord.verdict === "escalate";
|
|
424
611
|
const approvalStatus = requiresHumanApproval
|
|
425
612
|
? "pending_approval"
|
|
426
613
|
: "auto_approved";
|
|
@@ -457,14 +644,21 @@ export class MuxoEngine {
|
|
|
457
644
|
// Keep existing connection state if offline
|
|
458
645
|
}
|
|
459
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;
|
|
460
654
|
authHandoff = {
|
|
461
655
|
required: true,
|
|
462
656
|
provider: tool.providerSlug,
|
|
463
657
|
status: existingConn?.status || "pending",
|
|
464
|
-
redirectUrl:
|
|
658
|
+
redirectUrl: wrappedRedirectUrl,
|
|
465
659
|
externalAccountId: existingConn?.externalAccountId || null,
|
|
466
|
-
instructions:
|
|
467
|
-
? `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}.`
|
|
468
662
|
: `Call start_connection({ provider: "${tool.providerSlug}" }) to authorize ${tool.providerName} before executing ${plan.id}.`,
|
|
469
663
|
};
|
|
470
664
|
}
|
|
@@ -477,12 +671,20 @@ export class MuxoEngine {
|
|
|
477
671
|
if (authHandoff?.required) {
|
|
478
672
|
nextStep = `${authHandoff.instructions} Once connected, ${nextStep}`;
|
|
479
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);
|
|
480
680
|
return {
|
|
481
681
|
plan,
|
|
482
682
|
tool,
|
|
483
683
|
decisionRecord,
|
|
484
684
|
memoryContext,
|
|
485
685
|
...(authHandoff ? { authHandoff } : {}),
|
|
686
|
+
...(humanHandoff ? { humanHandoff } : {}),
|
|
687
|
+
llmGuidance,
|
|
486
688
|
nextStep,
|
|
487
689
|
};
|
|
488
690
|
}
|
|
@@ -493,6 +695,27 @@ export class MuxoEngine {
|
|
|
493
695
|
throw new Error(`Plan '${planId}' not found in workspace.`);
|
|
494
696
|
return plan;
|
|
495
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
|
+
}
|
|
496
719
|
// Human owner approval / rejection
|
|
497
720
|
approvePlan(ctx, planId, decision = "approved") {
|
|
498
721
|
const plan = this.getPlan(ctx, planId);
|
|
@@ -506,54 +729,330 @@ export class MuxoEngine {
|
|
|
506
729
|
approvedBy: ctx.user.email,
|
|
507
730
|
});
|
|
508
731
|
}
|
|
732
|
+
buildApprovalHandoff(plan) {
|
|
733
|
+
return buildApprovalHumanHandoff(this.config.baseUrl, plan);
|
|
734
|
+
}
|
|
735
|
+
buildOAuthHandoff(handoff, providerName) {
|
|
736
|
+
return buildOAuthHumanHandoff({
|
|
737
|
+
providerSlug: handoff.provider,
|
|
738
|
+
providerName,
|
|
739
|
+
redirectUrl: handoff.redirectUrl,
|
|
740
|
+
instructions: handoff.instructions,
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
buildPlanLlmGuidance(plan, authHandoff) {
|
|
744
|
+
const executeTool = `execute_${plan.riskLevel}`;
|
|
745
|
+
const nextToolCalls = [];
|
|
746
|
+
if (authHandoff?.required) {
|
|
747
|
+
nextToolCalls.push(authHandoff.redirectUrl
|
|
748
|
+
? {
|
|
749
|
+
tool: "wait_for_connection",
|
|
750
|
+
input: { provider: authHandoff.provider },
|
|
751
|
+
when: `After the user confirms they completed OAuth at ${authHandoff.redirectUrl}`,
|
|
752
|
+
}
|
|
753
|
+
: {
|
|
754
|
+
tool: "start_connection",
|
|
755
|
+
input: { provider: authHandoff.provider },
|
|
756
|
+
when: "Immediately, to obtain an OAuth redirect URL for the user",
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
if (plan.approvalStatus === "pending_approval") {
|
|
760
|
+
nextToolCalls.push({
|
|
761
|
+
tool: "get_plan",
|
|
762
|
+
input: { plan_id: plan.id, wait_for_approval: true, timeout_seconds: 25 },
|
|
763
|
+
when: `After sharing ${this.config.baseUrl}/approve/${plan.id} with the user, to wait in-turn until they click Approve`,
|
|
764
|
+
});
|
|
765
|
+
nextToolCalls.push({
|
|
766
|
+
tool: executeTool,
|
|
767
|
+
input: { plan_id: plan.id },
|
|
768
|
+
when: `Only after the user explicitly approves plan ${plan.id} (via ${this.config.baseUrl}/approve/${plan.id} or \`muxo approve ${plan.id}\`)`,
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
nextToolCalls.push({
|
|
773
|
+
tool: executeTool,
|
|
774
|
+
input: { plan_id: plan.id },
|
|
775
|
+
when: authHandoff?.required
|
|
776
|
+
? "After the connection becomes active"
|
|
777
|
+
: "Immediately; the plan is auto-approved",
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
nextToolCalls.push({
|
|
781
|
+
tool: "get_receipt",
|
|
782
|
+
input: { run_id: "<run_id from the execute response>" },
|
|
783
|
+
when: "After execution, to verify the tamper-proof receipt",
|
|
784
|
+
});
|
|
785
|
+
const summary = authHandoff?.required
|
|
786
|
+
? `Hand the OAuth link to the user, wait for the connection, then ${plan.approvalStatus === "pending_approval" ? "collect approval and " : ""}call ${executeTool}.`
|
|
787
|
+
: plan.approvalStatus === "pending_approval"
|
|
788
|
+
? `Stop and ask the user to approve plan ${plan.id}; do not call ${executeTool} until they confirm.`
|
|
789
|
+
: `Call ${executeTool} with plan_id="${plan.id}" to execute now.`;
|
|
790
|
+
return { summary, nextToolCalls };
|
|
791
|
+
}
|
|
792
|
+
async enrichMemoryContext(workspaceId, ctx, query) {
|
|
793
|
+
const localRecords = this.store.listMemoryRecords(workspaceId, 8);
|
|
794
|
+
const temporalFacts = this.store.listTemporalFacts(workspaceId, {
|
|
795
|
+
includeHistorical: false,
|
|
796
|
+
query,
|
|
797
|
+
limit: 15,
|
|
798
|
+
});
|
|
799
|
+
const recalled = ctx.recalledMemories.length > 0
|
|
800
|
+
? ctx.recalledMemories
|
|
801
|
+
: localRecords.slice(0, 5).map((r) => ({
|
|
802
|
+
documentId: r.documentId,
|
|
803
|
+
title: r.title,
|
|
804
|
+
snippet: r.content.slice(0, 240),
|
|
805
|
+
similarity: 1,
|
|
806
|
+
createdAt: r.createdAt,
|
|
807
|
+
}));
|
|
808
|
+
const staticFacts = ctx.staticFacts.length > 0
|
|
809
|
+
? ctx.staticFacts
|
|
810
|
+
: localRecords
|
|
811
|
+
.filter((r) => r.kind === "fact" || r.kind === "preference")
|
|
812
|
+
.slice(0, 5)
|
|
813
|
+
.map((r) => `${r.title}: ${r.content.slice(0, 140)}`);
|
|
814
|
+
const dynamicContext = ctx.dynamicContext.length > 0
|
|
815
|
+
? ctx.dynamicContext
|
|
816
|
+
: temporalFacts.slice(0, 6).map((f) => `${f.subject} ${f.predicate} ${f.object}`);
|
|
817
|
+
const combinedText = [
|
|
818
|
+
...staticFacts,
|
|
819
|
+
...dynamicContext,
|
|
820
|
+
...recalled.map((m) => `${m.title}: ${m.snippet}`),
|
|
821
|
+
].join("\n");
|
|
822
|
+
const poisonScan = this.governance.scanMemoryContent({
|
|
823
|
+
workspaceId,
|
|
824
|
+
sessionId: ctx.namespace,
|
|
825
|
+
role: "system",
|
|
826
|
+
content: combinedText,
|
|
827
|
+
recordInDetector: false,
|
|
828
|
+
});
|
|
829
|
+
const promptLines = [
|
|
830
|
+
`[Supermemory Brain — Namespace: ${ctx.namespace}]`,
|
|
831
|
+
];
|
|
832
|
+
if (staticFacts.length > 0) {
|
|
833
|
+
promptLines.push(`Static Facts: ${staticFacts.join(" | ")}`);
|
|
834
|
+
}
|
|
835
|
+
if (dynamicContext.length > 0) {
|
|
836
|
+
promptLines.push(`Dynamic Context: ${dynamicContext.join(" | ")}`);
|
|
837
|
+
}
|
|
838
|
+
if (temporalFacts.length > 0) {
|
|
839
|
+
promptLines.push(`Bi-Temporal Lineage (${temporalFacts.length} active): ${temporalFacts
|
|
840
|
+
.slice(0, 5)
|
|
841
|
+
.map((f) => `${f.subject} -[${f.predicate}]-> ${f.object}`)
|
|
842
|
+
.join(" | ")}`);
|
|
843
|
+
}
|
|
844
|
+
if (recalled.length > 0) {
|
|
845
|
+
promptLines.push(`Recalled Memories: ${recalled.map((m) => `${m.title}: ${m.snippet}`).join(" | ")}`);
|
|
846
|
+
}
|
|
847
|
+
const mdLines = [`## Workspace Brain (${ctx.namespace})`];
|
|
848
|
+
if (staticFacts.length > 0) {
|
|
849
|
+
mdLines.push("### Static Facts", ...staticFacts.map((f) => `- ${f}`));
|
|
850
|
+
}
|
|
851
|
+
if (temporalFacts.length > 0) {
|
|
852
|
+
mdLines.push("### Bi-Temporal Resource & Fact Lineage (Active)", ...temporalFacts
|
|
853
|
+
.slice(0, 8)
|
|
854
|
+
.map((f) => `- **${f.subject}** \`${f.predicate}\` **${f.object}** _(validFrom: ${f.validFrom})_ — ${f.summary}`));
|
|
855
|
+
}
|
|
856
|
+
const allLocalMemories = this.store.listMemoryRecords(workspaceId, 25);
|
|
857
|
+
const allActiveFacts = this.store.listTemporalFacts(workspaceId, {
|
|
858
|
+
includeHistorical: false,
|
|
859
|
+
limit: 30,
|
|
860
|
+
});
|
|
861
|
+
const inferred = extractActiveMemoryDefaults({
|
|
862
|
+
memories: allLocalMemories,
|
|
863
|
+
temporalFacts: allActiveFacts,
|
|
864
|
+
preferences: ctx.profile?.preferences || [],
|
|
865
|
+
staticFacts,
|
|
866
|
+
});
|
|
867
|
+
const mergedActiveDefaults = {
|
|
868
|
+
...(ctx.activeDefaults || {}),
|
|
869
|
+
...inferred.activeDefaults,
|
|
870
|
+
};
|
|
871
|
+
const ws = this.store.getWorkspace(workspaceId);
|
|
872
|
+
const mergedGuardrails = Array.from(new Set([
|
|
873
|
+
...(ws?.policy?.naturalLanguageRules || []),
|
|
874
|
+
...(ctx.activeGuardrails || []),
|
|
875
|
+
...inferred.activeGuardrails,
|
|
876
|
+
]));
|
|
877
|
+
if (Object.keys(mergedActiveDefaults).length > 0) {
|
|
878
|
+
promptLines.push(`Active Memory Auto-Fill Defaults: ${Object.entries(mergedActiveDefaults)
|
|
879
|
+
.map(([k, v]) => `${k}=${String(v)}`)
|
|
880
|
+
.join(", ")}`);
|
|
881
|
+
}
|
|
882
|
+
const enriched = {
|
|
883
|
+
...ctx,
|
|
884
|
+
enabled: true,
|
|
885
|
+
staticFacts,
|
|
886
|
+
dynamicContext,
|
|
887
|
+
recalledMemories: recalled,
|
|
888
|
+
relevantMemories: recalled,
|
|
889
|
+
formattedPromptContext: promptLines.join("\n"),
|
|
890
|
+
profileMarkdown: ctx.profileMarkdown || mdLines.join("\n"),
|
|
891
|
+
profile: {
|
|
892
|
+
...ctx.profile,
|
|
893
|
+
static: staticFacts,
|
|
894
|
+
dynamic: dynamicContext,
|
|
895
|
+
},
|
|
896
|
+
temporalFacts,
|
|
897
|
+
activeDefaults: mergedActiveDefaults,
|
|
898
|
+
activeGuardrails: mergedGuardrails,
|
|
899
|
+
contextPoisoningScan: {
|
|
900
|
+
safe: poisonScan.safe,
|
|
901
|
+
riskLevel: poisonScan.riskLevel,
|
|
902
|
+
findingsCount: poisonScan.findingsCount,
|
|
903
|
+
},
|
|
904
|
+
};
|
|
905
|
+
enriched.explanation = explainMemoryContext({
|
|
906
|
+
memory: enriched,
|
|
907
|
+
localCount: allLocalMemories.length,
|
|
908
|
+
activeTemporalCount: allActiveFacts.length,
|
|
909
|
+
policy: ws?.policy,
|
|
910
|
+
});
|
|
911
|
+
return enriched;
|
|
912
|
+
}
|
|
509
913
|
// 9. get_run_context
|
|
510
914
|
async getRunContext(ctx, query) {
|
|
511
915
|
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
512
916
|
const resources = this.store.listResources(ws.id);
|
|
917
|
+
const activeResources = resources.filter((r) => r.status === "active");
|
|
513
918
|
const activeEnv = this.store.getDecryptedWorkspaceEnv(ws.id);
|
|
514
919
|
const redactedEnv = {};
|
|
515
920
|
for (const [k, v] of Object.entries(activeEnv)) {
|
|
516
921
|
redactedEnv[k] =
|
|
517
922
|
v.length > 8 ? `${v.slice(0, 4)}••••${v.slice(-3)}` : "••••••••";
|
|
518
923
|
}
|
|
519
|
-
const
|
|
924
|
+
const supermemoryByok = this.store.getByokKey(ws.id, "supermemory");
|
|
925
|
+
const memoryContext = await this.enrichMemoryContext(ws.id, await this.supermemory.getContextForWorkspace(ws.id, query, supermemoryByok), query);
|
|
926
|
+
const plainEnglishPolicy = describePolicyInPlainEnglish(ws.policy);
|
|
927
|
+
const governanceStatus = this.governance.getStatus(ws);
|
|
928
|
+
const trustScore = this.governance.getWorkspaceTrustScore(ws.id);
|
|
929
|
+
const actionsRemaining = Math.max(0, ws.monthlyActionsLimit - ws.monthlyActionsUsed);
|
|
930
|
+
const defaultsList = Object.entries(memoryContext.activeDefaults || {}).map(([k, v]) => `${k}=${String(v)}`);
|
|
931
|
+
const contextHeadline = `Workspace ${ws.name} (${ws.slug}): ${ws.creditsBalance} credits ($${(ws.creditsBalance * 0.01).toFixed(2)}) available · ${plainEnglishPolicy.headline}`;
|
|
932
|
+
const contextNonTechnical = [
|
|
933
|
+
contextHeadline,
|
|
934
|
+
defaultsList.length > 0
|
|
935
|
+
? `Learned defaults ready for auto-fill: ${defaultsList.join(", ")}.`
|
|
936
|
+
: "No learned defaults set yet.",
|
|
937
|
+
activeResources.length > 0
|
|
938
|
+
? `Active cloud resources (${activeResources.length}): ${activeResources.map((r) => `${r.resourceName} (${r.providerName})`).join(", ")}.`
|
|
939
|
+
: "No cloud infrastructure resources provisioned yet.",
|
|
940
|
+
memoryContext.explanation?.nonTechnical || "",
|
|
941
|
+
]
|
|
942
|
+
.filter(Boolean)
|
|
943
|
+
.join(" ");
|
|
944
|
+
const contextTechnical = `workspace=${ws.id} (${ws.slug}, tier=${ws.tier}) | credits=${ws.creditsBalance} | actionsRemaining=${actionsRemaining}/${ws.monthlyActionsLimit} | ${plainEnglishPolicy.technical} | supermemoryNs=${memoryContext.namespace} | temporalFacts=${(memoryContext.temporalFacts || []).length} | activeDefaults=${JSON.stringify(memoryContext.activeDefaults || {})} | activeResources=${activeResources.length} | redactedEnvKeys=[${Object.keys(redactedEnv).join(", ")}] | agentDid=${governanceStatus.agentDid} | trustScore=${trustScore.overall} (${trustScore.tier})`;
|
|
945
|
+
const explanation = formatHumanFriendlyExplanation({
|
|
946
|
+
headline: contextHeadline,
|
|
947
|
+
nonTechnical: contextNonTechnical,
|
|
948
|
+
technical: contextTechnical,
|
|
949
|
+
policy: ws.policy,
|
|
950
|
+
});
|
|
951
|
+
const llmPolicyBrief = buildLlmPolicyAndGovernanceBrief(ws.policy, {
|
|
952
|
+
workspaceName: ws.name,
|
|
953
|
+
workspaceSlug: ws.slug,
|
|
954
|
+
activeDefaults: memoryContext.activeDefaults || {},
|
|
955
|
+
trustScore: { overall: trustScore.overall, tier: trustScore.tier },
|
|
956
|
+
agentDid: governanceStatus.agentDid,
|
|
957
|
+
});
|
|
958
|
+
const llmSystemPrompt = [
|
|
959
|
+
`# Muxo Governed Workspace Context (${ws.name} / ${ws.slug})`,
|
|
960
|
+
`- **Language Preference**: ${ws.policy.languageMode || "hybrid"}${ws.policy.customInstructions?.length ? ` (Custom Instructions: ${ws.policy.customInstructions.join("; ")})` : ""}`,
|
|
961
|
+
`- **Safety Guardrails**: ${plainEnglishPolicy.preferred}`,
|
|
962
|
+
`- **Learned Memory Defaults (Auto-Fill)**: ${defaultsList.length > 0 ? defaultsList.join(", ") : "none"}`,
|
|
963
|
+
`- **Credits & Quota**: ${ws.creditsBalance} credits ($${(ws.creditsBalance * 0.01).toFixed(2)}) available, ${actionsRemaining} monthly actions remaining`,
|
|
964
|
+
`- **Active Cloud Resources**: ${activeResources.length > 0 ? activeResources.map((r) => `${r.resourceName} (${r.serviceSlug})`).join(", ") : "none"}`,
|
|
965
|
+
llmPolicyBrief.llmSystemDirective,
|
|
966
|
+
memoryContext.formattedPromptContext,
|
|
967
|
+
].join("\n");
|
|
520
968
|
return {
|
|
521
969
|
workspaceId: ws.id,
|
|
522
970
|
workspaceSlug: ws.slug,
|
|
523
971
|
projectsEnvironment: ws.projectsEnvName,
|
|
524
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 || [],
|
|
525
980
|
creditsBalance: ws.creditsBalance,
|
|
526
|
-
monthlyActionsRemaining:
|
|
527
|
-
provisionedResources:
|
|
981
|
+
monthlyActionsRemaining: actionsRemaining,
|
|
982
|
+
provisionedResources: activeResources,
|
|
528
983
|
environmentVariablesRedacted: redactedEnv,
|
|
529
984
|
supermemoryBrain: memoryContext,
|
|
530
|
-
|
|
985
|
+
temporalFacts: memoryContext.temporalFacts || [],
|
|
986
|
+
governance: governanceStatus,
|
|
987
|
+
trustScore,
|
|
988
|
+
llmSystemPrompt,
|
|
989
|
+
explanation,
|
|
531
990
|
envPullHint: "Run `muxo env pull` or call GET /api/v1/resources/env to write decrypted credentials to your local .env file.",
|
|
532
991
|
};
|
|
533
992
|
}
|
|
534
993
|
// Supermemory Workspace Brain Operations
|
|
535
994
|
async getMemoryOverview(ctx, query) {
|
|
536
|
-
const
|
|
995
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
996
|
+
const supermemoryByok = this.store.getByokKey(ctx.workspace.id, "supermemory");
|
|
997
|
+
const context = await this.enrichMemoryContext(ctx.workspace.id, await this.supermemory.getContextForWorkspace(ctx.workspace.id, query, supermemoryByok), query);
|
|
537
998
|
const recentMemories = this.store.listMemoryRecords(ctx.workspace.id, 25);
|
|
999
|
+
const allFacts = this.store.listTemporalFacts(ctx.workspace.id, {
|
|
1000
|
+
includeHistorical: true,
|
|
1001
|
+
query,
|
|
1002
|
+
limit: 50,
|
|
1003
|
+
});
|
|
1004
|
+
const activeFacts = allFacts.filter((f) => f.validTo === null);
|
|
1005
|
+
const historicalFacts = allFacts.filter((f) => f.validTo !== null);
|
|
1006
|
+
const explanation = explainMemoryContext({
|
|
1007
|
+
memory: context,
|
|
1008
|
+
localCount: recentMemories.length,
|
|
1009
|
+
activeTemporalCount: activeFacts.length,
|
|
1010
|
+
historicalTemporalCount: historicalFacts.length,
|
|
1011
|
+
policy: ws.policy,
|
|
1012
|
+
});
|
|
538
1013
|
return {
|
|
539
1014
|
enabled: context.enabled,
|
|
540
1015
|
namespace: context.namespace,
|
|
1016
|
+
fundingSource: context.fundingSource || "local_fallback",
|
|
541
1017
|
context,
|
|
1018
|
+
activeDefaults: (context.activeDefaults || {}),
|
|
1019
|
+
activeGuardrails: context.activeGuardrails || [],
|
|
1020
|
+
explanation,
|
|
542
1021
|
recentMemories,
|
|
543
1022
|
memories: recentMemories,
|
|
1023
|
+
temporalFacts: activeFacts,
|
|
1024
|
+
historicalFacts,
|
|
544
1025
|
};
|
|
545
1026
|
}
|
|
546
1027
|
async addWorkspaceMemory(ctx, params) {
|
|
1028
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
547
1029
|
const kind = params.kind || "fact";
|
|
1030
|
+
// Microsoft Agent Governance OWASP ASI-06 ContextPoisoningDetector guard on memory ingestion
|
|
1031
|
+
const poisonScan = this.governance.scanMemoryContent({
|
|
1032
|
+
workspaceId: ws.id,
|
|
1033
|
+
sessionId: this.supermemory.getWorkspaceNamespace(ws.id),
|
|
1034
|
+
role: "user",
|
|
1035
|
+
content: `${params.title}\n${params.content}`,
|
|
1036
|
+
recordInDetector: true,
|
|
1037
|
+
});
|
|
1038
|
+
if (!poisonScan.safe && ws.policy.mode === "enforce") {
|
|
1039
|
+
this.governance.recordAgentOutcome(ws.id, false, 0.1);
|
|
1040
|
+
throw new Error(`Blocked by Microsoft ContextPoisoningDetector (OWASP ASI-06, risk=${poisonScan.riskLevel}): ${poisonScan.findings
|
|
1041
|
+
.map((f) => `${f.patternName} (${f.description})`)
|
|
1042
|
+
.join("; ")}`);
|
|
1043
|
+
}
|
|
1044
|
+
const supermemoryByok = this.store.getByokKey(ws.id, "supermemory");
|
|
548
1045
|
const remote = await this.supermemory.addMemory({
|
|
549
|
-
workspaceId:
|
|
1046
|
+
workspaceId: ws.id,
|
|
550
1047
|
title: params.title,
|
|
551
1048
|
content: params.content,
|
|
552
1049
|
kind,
|
|
553
1050
|
metadata: params.metadata,
|
|
1051
|
+
apiKeyOverride: supermemoryByok,
|
|
1052
|
+
dreaming: "instant",
|
|
554
1053
|
});
|
|
555
|
-
|
|
556
|
-
workspaceId:
|
|
1054
|
+
const record = this.store.saveMemoryRecord({
|
|
1055
|
+
workspaceId: ws.id,
|
|
557
1056
|
namespace: remote.namespace,
|
|
558
1057
|
documentId: remote.documentId,
|
|
559
1058
|
title: params.title,
|
|
@@ -562,22 +1061,133 @@ export class MuxoEngine {
|
|
|
562
1061
|
metadata: {
|
|
563
1062
|
...(params.metadata || {}),
|
|
564
1063
|
supermemoryStatus: remote.status,
|
|
1064
|
+
fundingSource: remote.fundingSource,
|
|
1065
|
+
contextPoisoningScan: {
|
|
1066
|
+
safe: poisonScan.safe,
|
|
1067
|
+
riskLevel: poisonScan.riskLevel,
|
|
1068
|
+
findingsCount: poisonScan.findingsCount,
|
|
1069
|
+
},
|
|
565
1070
|
},
|
|
566
1071
|
});
|
|
1072
|
+
// Record bi-temporal entity/relationship lineage (auto-superseding prior facts for same subject+predicate)
|
|
1073
|
+
const normalizedTitlePred = params.title
|
|
1074
|
+
.trim()
|
|
1075
|
+
.toUpperCase()
|
|
1076
|
+
.replace(/[^A-Z0-9]+/g, "_")
|
|
1077
|
+
.replace(/^_|_$/g, "")
|
|
1078
|
+
.slice(0, 48);
|
|
1079
|
+
const subject = params.subject ||
|
|
1080
|
+
(typeof params.metadata?.subject === "string"
|
|
1081
|
+
? params.metadata.subject
|
|
1082
|
+
: `workspace:${ws.slug}`);
|
|
1083
|
+
const predicate = params.predicate ||
|
|
1084
|
+
(typeof params.metadata?.predicate === "string"
|
|
1085
|
+
? params.metadata.predicate
|
|
1086
|
+
: normalizedTitlePred || kind.toUpperCase());
|
|
1087
|
+
const object = params.object ||
|
|
1088
|
+
(typeof params.metadata?.object === "string"
|
|
1089
|
+
? params.metadata.object
|
|
1090
|
+
: remote.sanitizedContent.slice(0, 160));
|
|
1091
|
+
this.store.upsertTemporalFact({
|
|
1092
|
+
workspaceId: ws.id,
|
|
1093
|
+
subject,
|
|
1094
|
+
predicate,
|
|
1095
|
+
object,
|
|
1096
|
+
summary: `${params.title}: ${remote.sanitizedContent.slice(0, 240)}`,
|
|
1097
|
+
confidence: 0.95,
|
|
1098
|
+
sourceKind: kind,
|
|
1099
|
+
sourceReferenceId: record.id,
|
|
1100
|
+
metadata: params.metadata || {},
|
|
1101
|
+
});
|
|
1102
|
+
return record;
|
|
567
1103
|
}
|
|
568
1104
|
async deleteWorkspaceMemory(ctx, idOrDocId) {
|
|
569
1105
|
const local = this.store.deleteMemoryRecord(ctx.workspace.id, idOrDocId);
|
|
570
1106
|
const docId = local?.documentId || idOrDocId;
|
|
571
|
-
const
|
|
1107
|
+
const supermemoryByok = this.store.getByokKey(ctx.workspace.id, "supermemory");
|
|
1108
|
+
const remote = await this.supermemory.deleteMemory(ctx.workspace.id, docId, supermemoryByok);
|
|
572
1109
|
return {
|
|
573
1110
|
deleted: Boolean(local || remote.deleted),
|
|
574
1111
|
record: local,
|
|
575
1112
|
};
|
|
576
1113
|
}
|
|
1114
|
+
async forgetWorkspaceMemoryMatching(ctx, query) {
|
|
1115
|
+
const localPruned = this.store.forgetMatchingMemoryRecords(ctx.workspace.id, query);
|
|
1116
|
+
const supermemoryByok = this.store.getByokKey(ctx.workspace.id, "supermemory");
|
|
1117
|
+
const remotePruned = await this.supermemory.forgetMatching({
|
|
1118
|
+
workspaceId: ctx.workspace.id,
|
|
1119
|
+
query,
|
|
1120
|
+
apiKeyOverride: supermemoryByok,
|
|
1121
|
+
});
|
|
1122
|
+
return {
|
|
1123
|
+
query,
|
|
1124
|
+
deletedLocalCount: localPruned.deletedMemories.length,
|
|
1125
|
+
invalidatedFactsCount: localPruned.invalidatedFacts.length,
|
|
1126
|
+
deletedRemoteIds: remotePruned.deletedRemoteIds,
|
|
1127
|
+
deletedMemories: localPruned.deletedMemories,
|
|
1128
|
+
invalidatedFacts: localPruned.invalidatedFacts,
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Synthesizes a Bi-Temporal Knowledge Graph (entities, active relationships, superseded historical relationships,
|
|
1133
|
+
* Mermaid diagram, and human + technical explanations) directly from SQLite temporal_facts, memories, resources, and connections.
|
|
1134
|
+
*/
|
|
1135
|
+
async getMemoryGraph(ctx, query) {
|
|
1136
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1137
|
+
const overview = await this.getMemoryOverview(ctx, query);
|
|
1138
|
+
return buildMemoryKnowledgeGraph({
|
|
1139
|
+
workspace: ws,
|
|
1140
|
+
namespace: overview.namespace,
|
|
1141
|
+
query,
|
|
1142
|
+
facts: this.store.listTemporalFacts(ws.id, {
|
|
1143
|
+
includeHistorical: true,
|
|
1144
|
+
query,
|
|
1145
|
+
limit: 100,
|
|
1146
|
+
}),
|
|
1147
|
+
resources: this.store.listResources(ws.id),
|
|
1148
|
+
activeConnections: this.store
|
|
1149
|
+
.listConnections(ws.id)
|
|
1150
|
+
.filter((c) => c.status === "active"),
|
|
1151
|
+
memoriesCount: overview.memories.length,
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
async activateWorkspaceKillSwitch(ctx, params) {
|
|
1155
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1156
|
+
const result = await this.governance.activateKillSwitch(ws.id, params.reason, params.action || "quarantine");
|
|
1157
|
+
const updatedWs = this.store.updateWorkspacePolicy(ws.id, {
|
|
1158
|
+
...ws.policy,
|
|
1159
|
+
killSwitchActive: true,
|
|
1160
|
+
killSwitchReason: params.reason,
|
|
1161
|
+
});
|
|
1162
|
+
return {
|
|
1163
|
+
activated: true,
|
|
1164
|
+
killSwitch: result,
|
|
1165
|
+
policy: updatedWs.policy,
|
|
1166
|
+
trustScore: this.governance.getWorkspaceTrustScore(ws.id),
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
resumeWorkspaceKillSwitch(ctx) {
|
|
1170
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
1171
|
+
const resumed = this.governance.resumeKillSwitch(ws.id);
|
|
1172
|
+
const updatedWs = this.store.updateWorkspacePolicy(ws.id, {
|
|
1173
|
+
...ws.policy,
|
|
1174
|
+
killSwitchActive: false,
|
|
1175
|
+
killSwitchReason: null,
|
|
1176
|
+
});
|
|
1177
|
+
return {
|
|
1178
|
+
...resumed,
|
|
1179
|
+
resumed: true,
|
|
1180
|
+
policy: updatedWs.policy,
|
|
1181
|
+
trustScore: this.governance.getWorkspaceTrustScore(ws.id),
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
577
1184
|
// 10-15. execute_<risk_level>
|
|
578
1185
|
async executePlanForRisk(ctx, planId, expectedRisk) {
|
|
579
1186
|
const plan = this.getPlan(ctx, planId);
|
|
580
|
-
|
|
1187
|
+
const isProjectsWriteCompatible = plan.lane === "stripe_projects" &&
|
|
1188
|
+
plan.riskLevel === "provisioning" &&
|
|
1189
|
+
expectedRisk === "write";
|
|
1190
|
+
if (plan.riskLevel !== expectedRisk && !isProjectsWriteCompatible) {
|
|
581
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}'.`);
|
|
582
1192
|
}
|
|
583
1193
|
if (plan.runId) {
|
|
@@ -589,13 +1199,30 @@ export class MuxoEngine {
|
|
|
589
1199
|
throw new Error(`Plan '${plan.id}' was already claimed by run '${plan.runId}' (execute-once invariant).`);
|
|
590
1200
|
}
|
|
591
1201
|
if (plan.approvalStatus === "pending_approval") {
|
|
592
|
-
throw
|
|
1202
|
+
throw approvalRequiredError(this.config.baseUrl, plan);
|
|
593
1203
|
}
|
|
594
1204
|
if (plan.approvalStatus === "rejected") {
|
|
595
1205
|
throw new Error(`Plan '${plan.id}' was rejected and cannot be executed.`);
|
|
596
1206
|
}
|
|
597
1207
|
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
598
1208
|
const tool = await this.inspectTool(plan.toolSlug);
|
|
1209
|
+
const explicitFunding = plan.supplierSettlement?.fundingMode;
|
|
1210
|
+
const byokActive = explicitFunding === "byok"
|
|
1211
|
+
? true
|
|
1212
|
+
: explicitFunding === "muxo_funded"
|
|
1213
|
+
? false
|
|
1214
|
+
: this.store.hasByokForLane(ws.id, tool.lane, tool.slug);
|
|
1215
|
+
const effectiveCreditCost = byokActive
|
|
1216
|
+
? 0
|
|
1217
|
+
: plan.creditCost > 0
|
|
1218
|
+
? plan.creditCost
|
|
1219
|
+
: tool.creditCost;
|
|
1220
|
+
if (ws.monthlyActionsUsed >= ws.monthlyActionsLimit) {
|
|
1221
|
+
throw new Error(`Monthly action limit reached (${ws.monthlyActionsUsed}/${ws.monthlyActionsLimit}). Upgrade to Starter ($29/mo) to unlock 5,000 actions/mo.`);
|
|
1222
|
+
}
|
|
1223
|
+
if (effectiveCreditCost > ws.creditsBalance) {
|
|
1224
|
+
throw new Error(`Insufficient workspace credits to execute plan '${plan.id}' for ${tool.slug} (requires ${effectiveCreditCost}, available ${ws.creditsBalance}). Top up credits or configure a BYOK key.`);
|
|
1225
|
+
}
|
|
599
1226
|
const execDecisionRecord = this.governance.evaluateExecution({
|
|
600
1227
|
workspace: ws,
|
|
601
1228
|
actorId: ctx.actorId,
|
|
@@ -607,7 +1234,16 @@ export class MuxoEngine {
|
|
|
607
1234
|
}
|
|
608
1235
|
let activeConnId = null;
|
|
609
1236
|
if (plan.lane === "composio_sdk" && tool.requiresAuth) {
|
|
610
|
-
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);
|
|
611
1247
|
let activeConn = connections.find((c) => c.lane === "composio_sdk" &&
|
|
612
1248
|
c.providerSlug === tool.providerSlug &&
|
|
613
1249
|
c.status === "active");
|
|
@@ -639,25 +1275,66 @@ export class MuxoEngine {
|
|
|
639
1275
|
});
|
|
640
1276
|
}
|
|
641
1277
|
else if (plan.lane === "composio_instant") {
|
|
1278
|
+
const composioByok = byokActive
|
|
1279
|
+
? this.store.getByokKey(ctx.workspace.id, "composio")
|
|
1280
|
+
: null;
|
|
1281
|
+
const exaByok = byokActive
|
|
1282
|
+
? this.store.getByokKey(ctx.workspace.id, "exa")
|
|
1283
|
+
: null;
|
|
1284
|
+
const firecrawlByok = byokActive
|
|
1285
|
+
? this.store.getByokKey(ctx.workspace.id, "firecrawl")
|
|
1286
|
+
: null;
|
|
642
1287
|
output = await this.composioInstant.executeTool({
|
|
643
1288
|
workspaceId: ctx.workspace.id,
|
|
644
1289
|
tool,
|
|
645
1290
|
input: plan.input,
|
|
1291
|
+
composioApiKey: composioByok,
|
|
1292
|
+
exaApiKey: exaByok,
|
|
1293
|
+
firecrawlApiKey: firecrawlByok,
|
|
646
1294
|
});
|
|
647
1295
|
}
|
|
648
1296
|
else if (plan.lane === "composio_sdk") {
|
|
1297
|
+
const composioByok = byokActive
|
|
1298
|
+
? this.store.getByokKey(ctx.workspace.id, "composio")
|
|
1299
|
+
: null;
|
|
649
1300
|
output = await this.composioSdk.executeTool({
|
|
650
1301
|
workspaceId: ctx.workspace.id,
|
|
651
1302
|
tool,
|
|
652
1303
|
input: plan.input,
|
|
653
1304
|
connectedAccountId: activeConnId,
|
|
1305
|
+
composioApiKey: composioByok,
|
|
654
1306
|
});
|
|
655
1307
|
}
|
|
656
1308
|
else if (plan.lane === "stripe_projects") {
|
|
1309
|
+
const stripeByok = byokActive
|
|
1310
|
+
? this.store.getByokKey(ctx.workspace.id, "stripe")
|
|
1311
|
+
: null;
|
|
1312
|
+
const enrichedInput = { ...plan.input };
|
|
1313
|
+
if (!enrichedInput.resource_id &&
|
|
1314
|
+
(plan.toolSlug === "resource.projects.rotate_credentials" ||
|
|
1315
|
+
plan.toolSlug === "resource.projects.deprovision" ||
|
|
1316
|
+
plan.toolSlug === "resource.projects.reveal_access_configuration")) {
|
|
1317
|
+
const rName = String(enrichedInput.resource_name ||
|
|
1318
|
+
enrichedInput.database_name ||
|
|
1319
|
+
enrichedInput.name ||
|
|
1320
|
+
"")
|
|
1321
|
+
.trim()
|
|
1322
|
+
.toLowerCase();
|
|
1323
|
+
if (rName) {
|
|
1324
|
+
const matchedRes = this.store
|
|
1325
|
+
.listResources(ctx.workspace.id)
|
|
1326
|
+
.find((r) => r.resourceName.toLowerCase() === rName &&
|
|
1327
|
+
r.externalResourceId);
|
|
1328
|
+
if (matchedRes?.externalResourceId) {
|
|
1329
|
+
enrichedInput.resource_id = matchedRes.externalResourceId;
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
657
1333
|
const projResult = await this.stripeProjects.executeTool({
|
|
658
1334
|
workspaceId: ctx.workspace.id,
|
|
659
1335
|
tool,
|
|
660
|
-
input:
|
|
1336
|
+
input: enrichedInput,
|
|
1337
|
+
stripeApiKey: stripeByok,
|
|
661
1338
|
});
|
|
662
1339
|
output = projResult.output;
|
|
663
1340
|
if (projResult.provisionedResource) {
|
|
@@ -671,19 +1348,49 @@ export class MuxoEngine {
|
|
|
671
1348
|
throw new Error(`Unsupported execution lane: ${plan.lane}`);
|
|
672
1349
|
}
|
|
673
1350
|
const durationMs = Date.now() - startedMs;
|
|
1351
|
+
this.governance.recordAgentOutcome(ctx.workspace.id, true, 0.05);
|
|
674
1352
|
const finalized = this.store.finalizeRun({
|
|
675
1353
|
workspaceId: ctx.workspace.id,
|
|
676
1354
|
runId: run.id,
|
|
677
1355
|
status: "succeeded",
|
|
678
1356
|
output,
|
|
679
1357
|
actionsMetered: 1,
|
|
680
|
-
creditsMetered:
|
|
1358
|
+
creditsMetered: effectiveCreditCost,
|
|
681
1359
|
durationMs,
|
|
682
1360
|
decisionRecord: execDecisionRecord,
|
|
683
1361
|
});
|
|
1362
|
+
finalized.receipt.agenticReceipt =
|
|
1363
|
+
this.governance.buildPortableAgenticReceipt({
|
|
1364
|
+
receipt: finalized.receipt,
|
|
1365
|
+
run: finalized.run,
|
|
1366
|
+
workspace: ws,
|
|
1367
|
+
});
|
|
1368
|
+
finalized.receipt.explanation = this.explainReceipt(finalized.receipt, {
|
|
1369
|
+
signatureValid: this.store.verifyReceipt(finalized.receipt),
|
|
1370
|
+
governanceSignatureValid: true,
|
|
1371
|
+
agenticReceiptValid: true,
|
|
1372
|
+
}, ws.policy);
|
|
1373
|
+
this.store.upsertTemporalFact({
|
|
1374
|
+
workspaceId: ctx.workspace.id,
|
|
1375
|
+
subject: `tool:${plan.toolSlug}`,
|
|
1376
|
+
predicate: "LAST_EXECUTION",
|
|
1377
|
+
object: `succeeded:${finalized.run.id}`,
|
|
1378
|
+
summary: `Executed ${plan.toolSlug} (${plan.lane}, risk=${plan.riskLevel}) -> succeeded [receipt=${finalized.receipt.id}]`,
|
|
1379
|
+
confidence: 1.0,
|
|
1380
|
+
sourceKind: "execution_outcome",
|
|
1381
|
+
sourceReferenceId: finalized.run.id,
|
|
1382
|
+
metadata: {
|
|
1383
|
+
runId: finalized.run.id,
|
|
1384
|
+
receiptId: finalized.receipt.id,
|
|
1385
|
+
toolSlug: plan.toolSlug,
|
|
1386
|
+
lane: plan.lane,
|
|
1387
|
+
riskLevel: plan.riskLevel,
|
|
1388
|
+
},
|
|
1389
|
+
});
|
|
684
1390
|
// Record secret-redacted post-run memory in Supermemory for stateful actions
|
|
685
1391
|
if (plan.riskLevel !== "read") {
|
|
686
1392
|
const summary = `Executed ${plan.toolSlug} (${plan.lane}, risk=${plan.riskLevel}) for reason "${plan.reason}" -> status=succeeded (receipt=${finalized.receipt.id}).`;
|
|
1393
|
+
const supermemoryByok = this.store.getByokKey(ctx.workspace.id, "supermemory");
|
|
687
1394
|
this.supermemory
|
|
688
1395
|
.recordExecutionMemory({
|
|
689
1396
|
workspaceId: ctx.workspace.id,
|
|
@@ -693,6 +1400,7 @@ export class MuxoEngine {
|
|
|
693
1400
|
reason: plan.reason,
|
|
694
1401
|
status: "succeeded",
|
|
695
1402
|
summary,
|
|
1403
|
+
apiKeyOverride: supermemoryByok,
|
|
696
1404
|
})
|
|
697
1405
|
.then((docId) => {
|
|
698
1406
|
if (docId) {
|
|
@@ -717,8 +1425,9 @@ export class MuxoEngine {
|
|
|
717
1425
|
}
|
|
718
1426
|
catch (err) {
|
|
719
1427
|
const durationMs = Date.now() - startedMs;
|
|
1428
|
+
this.governance.recordAgentOutcome(ctx.workspace.id, false, 0.08);
|
|
720
1429
|
const errorMessage = String(err?.message || err || "Execution failed");
|
|
721
|
-
|
|
1430
|
+
const failedFinalized = this.store.finalizeRun({
|
|
722
1431
|
workspaceId: ctx.workspace.id,
|
|
723
1432
|
runId: run.id,
|
|
724
1433
|
status: "failed",
|
|
@@ -729,6 +1438,18 @@ export class MuxoEngine {
|
|
|
729
1438
|
durationMs,
|
|
730
1439
|
decisionRecord: execDecisionRecord,
|
|
731
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;
|
|
732
1453
|
}
|
|
733
1454
|
}
|
|
734
1455
|
// 16. get_run
|
|
@@ -739,40 +1460,780 @@ export class MuxoEngine {
|
|
|
739
1460
|
return run;
|
|
740
1461
|
}
|
|
741
1462
|
// 17. cancel_run
|
|
742
|
-
cancelRun(ctx, runId) {
|
|
743
|
-
const
|
|
744
|
-
if (
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
1463
|
+
cancelRun(ctx, runId, reason) {
|
|
1464
|
+
const cleanId = String(runId || "").trim();
|
|
1465
|
+
if (!cleanId)
|
|
1466
|
+
throw new Error("run_id is required to cancel a run.");
|
|
1467
|
+
const run = this.getRun(ctx, cleanId);
|
|
1468
|
+
if (run.status === "cancelled") {
|
|
1469
|
+
const existing = this.store.getReceipt(ctx.workspace.id, run.id);
|
|
1470
|
+
if (existing) {
|
|
1471
|
+
return {
|
|
1472
|
+
ok: true,
|
|
1473
|
+
cancelled: true,
|
|
1474
|
+
alreadyCancelled: true,
|
|
1475
|
+
previousStatus: "cancelled",
|
|
1476
|
+
run,
|
|
1477
|
+
receipt: existing,
|
|
1478
|
+
signatureValid: this.store.verifyReceipt(existing),
|
|
1479
|
+
message: `Run '${run.id}' was already cancelled.`,
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
749
1482
|
}
|
|
750
|
-
const
|
|
1483
|
+
const previousStatus = run.status;
|
|
1484
|
+
const cancelled = this.store.cancelRun({
|
|
751
1485
|
workspaceId: ctx.workspace.id,
|
|
752
1486
|
runId: run.id,
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
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,
|
|
759
1534
|
});
|
|
760
|
-
return finalized;
|
|
761
1535
|
}
|
|
762
1536
|
// 18. get_receipt
|
|
763
1537
|
getReceipt(ctx, receiptOrRunId) {
|
|
764
|
-
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);
|
|
765
1543
|
if (!receipt) {
|
|
766
1544
|
throw new Error(`Receipt '${receiptOrRunId}' not found in workspace.`);
|
|
767
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;
|
|
768
1553
|
const signatureValid = this.store.verifyReceipt(receipt);
|
|
769
1554
|
const governanceSignatureValid = receipt.decisionRecord
|
|
770
1555
|
? this.governance.verifyDecisionRecord(receipt.decisionRecord)
|
|
771
1556
|
: true;
|
|
772
|
-
|
|
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
|
+
};
|
|
773
1935
|
}
|
|
774
1936
|
// 19. Natural Language Router & End-to-End Executor (`muxo do` / `POST /api/v1/do`)
|
|
775
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
|
+
}
|
|
776
2237
|
const allTools = await this.listAllCatalogTools();
|
|
777
2238
|
const byokEnv = this.store.getDecryptedWorkspaceEnv(ctx.workspace.id);
|
|
778
2239
|
const activeConns = new Set(this.store
|
|
@@ -803,6 +2264,17 @@ export class MuxoEngine {
|
|
|
803
2264
|
humanMessage: resolution.humanSummary,
|
|
804
2265
|
};
|
|
805
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] === "");
|
|
806
2278
|
if (resolution.missingInputs.length > 0) {
|
|
807
2279
|
return {
|
|
808
2280
|
ok: false,
|
|
@@ -810,6 +2282,7 @@ export class MuxoEngine {
|
|
|
810
2282
|
status: "missing_inputs",
|
|
811
2283
|
intent: resolution,
|
|
812
2284
|
resolution,
|
|
2285
|
+
activeMemoryDefaults: guardrailsSummary.activeDefaults,
|
|
813
2286
|
humanMessage: `Matched ${resolution.tool.name} (${resolution.tool.slug}), but missing required parameter(s): ${resolution.missingInputs.join(", ")}. Please include them in your request.`,
|
|
814
2287
|
};
|
|
815
2288
|
}
|
|
@@ -817,6 +2290,7 @@ export class MuxoEngine {
|
|
|
817
2290
|
toolSlug: resolution.tool.slug,
|
|
818
2291
|
input: resolution.input,
|
|
819
2292
|
reason: params.prompt,
|
|
2293
|
+
fundingMode: params.fundingMode,
|
|
820
2294
|
});
|
|
821
2295
|
if (planned.authHandoff?.required) {
|
|
822
2296
|
return {
|
|
@@ -827,9 +2301,15 @@ export class MuxoEngine {
|
|
|
827
2301
|
resolution,
|
|
828
2302
|
plan: planned.plan,
|
|
829
2303
|
authHandoff: planned.authHandoff,
|
|
2304
|
+
...(planned.humanHandoff ? { humanHandoff: planned.humanHandoff } : {}),
|
|
2305
|
+
llmGuidance: planned.llmGuidance,
|
|
830
2306
|
humanMessage: planned.authHandoff.instructions,
|
|
831
2307
|
};
|
|
832
2308
|
}
|
|
2309
|
+
const combinedAutoFilled = {
|
|
2310
|
+
...activeAutoFill.autoFilledInput,
|
|
2311
|
+
...(planned.memoryContext?.autoFilledInput || {}),
|
|
2312
|
+
};
|
|
833
2313
|
if (params.dryRun || params.planOnly) {
|
|
834
2314
|
return {
|
|
835
2315
|
ok: true,
|
|
@@ -838,6 +2318,8 @@ export class MuxoEngine {
|
|
|
838
2318
|
intent: resolution,
|
|
839
2319
|
resolution,
|
|
840
2320
|
plan: planned.plan,
|
|
2321
|
+
activeMemoryDefaults: guardrailsSummary.activeDefaults,
|
|
2322
|
+
memoryAutoFilled: combinedAutoFilled,
|
|
841
2323
|
humanMessage: `Planned ${planned.tool.name} (${planned.plan.id}) with risk=${planned.plan.riskLevel} and cost=${planned.plan.creditCost} credits.`,
|
|
842
2324
|
};
|
|
843
2325
|
}
|
|
@@ -854,6 +2336,10 @@ export class MuxoEngine {
|
|
|
854
2336
|
intent: resolution,
|
|
855
2337
|
resolution,
|
|
856
2338
|
plan: currentPlan,
|
|
2339
|
+
humanHandoff: this.buildApprovalHandoff(currentPlan),
|
|
2340
|
+
llmGuidance: this.buildPlanLlmGuidance(currentPlan),
|
|
2341
|
+
activeMemoryDefaults: guardrailsSummary.activeDefaults,
|
|
2342
|
+
memoryAutoFilled: combinedAutoFilled,
|
|
857
2343
|
humanMessage: `Plan ${currentPlan.id} (${currentPlan.toolSlug}, risk=${currentPlan.riskLevel}) requires owner approval. Run 'muxo approve ${currentPlan.id}' or pass --yes to auto-approve.`,
|
|
858
2344
|
};
|
|
859
2345
|
}
|
|
@@ -868,9 +2354,207 @@ export class MuxoEngine {
|
|
|
868
2354
|
plan: currentPlan,
|
|
869
2355
|
run: executed.run,
|
|
870
2356
|
receipt: executed.receipt,
|
|
2357
|
+
explanation: executed.receipt.explanation,
|
|
2358
|
+
activeDefaults: guardrailsSummary.activeDefaults,
|
|
2359
|
+
activeMemoryDefaults: guardrailsSummary.activeDefaults,
|
|
2360
|
+
memoryAutoFilled: combinedAutoFilled,
|
|
871
2361
|
humanMessage: isSuccess
|
|
872
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"}].`
|
|
873
2363
|
: `Execution of ${resolution.tool.slug} failed: ${executed.run.error || "Unknown error"}`,
|
|
874
2364
|
};
|
|
875
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
|
+
}
|
|
876
2560
|
}
|