@muxoai/platform 0.0.0-stage → 1.2.0
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/ARCHITECTURE.md +111 -0
- package/README.md +53 -2
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +527 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.js +89 -0
- package/dist/engine.d.ts +128 -0
- package/dist/engine.js +709 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/mcp.d.ts +9 -0
- package/dist/mcp.js +401 -0
- package/dist/providers/composio-instant.d.ts +14 -0
- package/dist/providers/composio-instant.js +372 -0
- package/dist/providers/composio-sdk.d.ts +41 -0
- package/dist/providers/composio-sdk.js +514 -0
- package/dist/providers/microsoft-governance.d.ts +66 -0
- package/dist/providers/microsoft-governance.js +415 -0
- package/dist/providers/stripe-billing.d.ts +31 -0
- package/dist/providers/stripe-billing.js +168 -0
- package/dist/providers/stripe-projects.d.ts +53 -0
- package/dist/providers/stripe-projects.js +607 -0
- package/dist/providers/supermemory.d.ts +64 -0
- package/dist/providers/supermemory.js +224 -0
- package/dist/sdk.d.ts +115 -0
- package/dist/sdk.js +117 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +816 -0
- package/dist/store.d.ts +200 -0
- package/dist/store.js +1260 -0
- package/dist/types.d.ts +273 -0
- package/dist/types.js +1 -0
- package/dist/ui/console-html.d.ts +2 -0
- package/dist/ui/console-html.js +2153 -0
- package/package.json +74 -4
- package/plugins/muxo/.app.json +8 -0
- package/plugins/muxo/.claude-plugin/plugin.json +16 -0
- package/plugins/muxo/.codex-plugin/plugin.json +51 -0
- package/plugins/muxo/.mcp.json +8 -0
- package/plugins/muxo/assets/logo.png +0 -0
- package/plugins/muxo/assets/logo.svg +6 -0
- package/plugins/muxo/hooks/hooks.json +27 -0
- package/plugins/muxo/hooks/session-start.sh +85 -0
- package/plugins/muxo/hooks/user-prompt-submit.sh +45 -0
- package/plugins/muxo/skills/muxo/SKILL.md +25 -0
- package/plugins/muxo/skills/muxo/references/cli.md +57 -0
- package/plugins/muxo/skills/muxo/references/mcp.md +25 -0
- package/src/catalogues/stripe-projects-catalog.json +9832 -0
- package/vendor/microsoft-governance/README.md +363 -0
- package/vendor/microsoft-governance/dist/audit.d.ts +26 -0
- package/vendor/microsoft-governance/dist/audit.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/audit.js +107 -0
- package/vendor/microsoft-governance/dist/audit.js.map +1 -0
- package/vendor/microsoft-governance/dist/cascade-containment.d.ts +84 -0
- package/vendor/microsoft-governance/dist/cascade-containment.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/cascade-containment.js +387 -0
- package/vendor/microsoft-governance/dist/cascade-containment.js.map +1 -0
- package/vendor/microsoft-governance/dist/client.d.ts +34 -0
- package/vendor/microsoft-governance/dist/client.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/client.js +112 -0
- package/vendor/microsoft-governance/dist/client.js.map +1 -0
- package/vendor/microsoft-governance/dist/context-poisoning.d.ts +58 -0
- package/vendor/microsoft-governance/dist/context-poisoning.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/context-poisoning.js +410 -0
- package/vendor/microsoft-governance/dist/context-poisoning.js.map +1 -0
- package/vendor/microsoft-governance/dist/credential-vault.d.ts +213 -0
- package/vendor/microsoft-governance/dist/credential-vault.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/credential-vault.js +567 -0
- package/vendor/microsoft-governance/dist/credential-vault.js.map +1 -0
- package/vendor/microsoft-governance/dist/discovery.d.ts +80 -0
- package/vendor/microsoft-governance/dist/discovery.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/discovery.js +404 -0
- package/vendor/microsoft-governance/dist/discovery.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/channel.d.ts +29 -0
- package/vendor/microsoft-governance/dist/encryption/channel.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/channel.js +78 -0
- package/vendor/microsoft-governance/dist/encryption/channel.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/index.d.ts +9 -0
- package/vendor/microsoft-governance/dist/encryption/index.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/index.js +23 -0
- package/vendor/microsoft-governance/dist/encryption/index.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/mesh-client.d.ts +267 -0
- package/vendor/microsoft-governance/dist/encryption/mesh-client.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/mesh-client.js +1001 -0
- package/vendor/microsoft-governance/dist/encryption/mesh-client.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/ratchet.d.ts +38 -0
- package/vendor/microsoft-governance/dist/encryption/ratchet.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/ratchet.js +223 -0
- package/vendor/microsoft-governance/dist/encryption/ratchet.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/registry-client.d.ts +136 -0
- package/vendor/microsoft-governance/dist/encryption/registry-client.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/registry-client.js +369 -0
- package/vendor/microsoft-governance/dist/encryption/registry-client.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/x3dh.d.ts +80 -0
- package/vendor/microsoft-governance/dist/encryption/x3dh.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/x3dh.js +238 -0
- package/vendor/microsoft-governance/dist/encryption/x3dh.js.map +1 -0
- package/vendor/microsoft-governance/dist/framework-adapter.d.ts +63 -0
- package/vendor/microsoft-governance/dist/framework-adapter.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/framework-adapter.js +253 -0
- package/vendor/microsoft-governance/dist/framework-adapter.js.map +1 -0
- package/vendor/microsoft-governance/dist/identity.d.ts +124 -0
- package/vendor/microsoft-governance/dist/identity.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/identity.js +446 -0
- package/vendor/microsoft-governance/dist/identity.js.map +1 -0
- package/vendor/microsoft-governance/dist/index.d.ts +39 -0
- package/vendor/microsoft-governance/dist/index.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/index.js +95 -0
- package/vendor/microsoft-governance/dist/index.js.map +1 -0
- package/vendor/microsoft-governance/dist/kill-switch.d.ts +22 -0
- package/vendor/microsoft-governance/dist/kill-switch.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/kill-switch.js +60 -0
- package/vendor/microsoft-governance/dist/kill-switch.js.map +1 -0
- package/vendor/microsoft-governance/dist/lifecycle.d.ts +50 -0
- package/vendor/microsoft-governance/dist/lifecycle.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/lifecycle.js +113 -0
- package/vendor/microsoft-governance/dist/lifecycle.js.map +1 -0
- package/vendor/microsoft-governance/dist/mcp.d.ts +41 -0
- package/vendor/microsoft-governance/dist/mcp.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/mcp.js +233 -0
- package/vendor/microsoft-governance/dist/mcp.js.map +1 -0
- package/vendor/microsoft-governance/dist/metrics.d.ts +123 -0
- package/vendor/microsoft-governance/dist/metrics.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/metrics.js +289 -0
- package/vendor/microsoft-governance/dist/metrics.js.map +1 -0
- package/vendor/microsoft-governance/dist/oci-manifest.d.ts +49 -0
- package/vendor/microsoft-governance/dist/oci-manifest.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/oci-manifest.js +182 -0
- package/vendor/microsoft-governance/dist/oci-manifest.js.map +1 -0
- package/vendor/microsoft-governance/dist/policy-backends/cedar.d.ts +15 -0
- package/vendor/microsoft-governance/dist/policy-backends/cedar.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/policy-backends/cedar.js +88 -0
- package/vendor/microsoft-governance/dist/policy-backends/cedar.js.map +1 -0
- package/vendor/microsoft-governance/dist/policy-backends/opa.d.ts +17 -0
- package/vendor/microsoft-governance/dist/policy-backends/opa.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/policy-backends/opa.js +74 -0
- package/vendor/microsoft-governance/dist/policy-backends/opa.js.map +1 -0
- package/vendor/microsoft-governance/dist/policy.d.ts +77 -0
- package/vendor/microsoft-governance/dist/policy.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/policy.js +760 -0
- package/vendor/microsoft-governance/dist/policy.js.map +1 -0
- package/vendor/microsoft-governance/dist/prompt-defense.d.ts +37 -0
- package/vendor/microsoft-governance/dist/prompt-defense.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/prompt-defense.js +229 -0
- package/vendor/microsoft-governance/dist/prompt-defense.js.map +1 -0
- package/vendor/microsoft-governance/dist/protocol-facets.d.ts +87 -0
- package/vendor/microsoft-governance/dist/protocol-facets.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/protocol-facets.js +728 -0
- package/vendor/microsoft-governance/dist/protocol-facets.js.map +1 -0
- package/vendor/microsoft-governance/dist/rings.d.ts +17 -0
- package/vendor/microsoft-governance/dist/rings.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/rings.js +61 -0
- package/vendor/microsoft-governance/dist/rings.js.map +1 -0
- package/vendor/microsoft-governance/dist/sandbox.d.ts +61 -0
- package/vendor/microsoft-governance/dist/sandbox.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/sandbox.js +199 -0
- package/vendor/microsoft-governance/dist/sandbox.js.map +1 -0
- package/vendor/microsoft-governance/dist/surface-parity.d.ts +45 -0
- package/vendor/microsoft-governance/dist/surface-parity.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/surface-parity.js +216 -0
- package/vendor/microsoft-governance/dist/surface-parity.js.map +1 -0
- package/vendor/microsoft-governance/dist/trust.d.ts +28 -0
- package/vendor/microsoft-governance/dist/trust.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/trust.js +209 -0
- package/vendor/microsoft-governance/dist/trust.js.map +1 -0
- package/vendor/microsoft-governance/dist/types.d.ts +422 -0
- package/vendor/microsoft-governance/dist/types.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/types.js +29 -0
- package/vendor/microsoft-governance/dist/types.js.map +1 -0
- package/vendor/microsoft-governance/dist/verify.d.ts +135 -0
- package/vendor/microsoft-governance/dist/verify.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/verify.js +356 -0
- package/vendor/microsoft-governance/dist/verify.js.map +1 -0
package/dist/engine.js
ADDED
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
import { ComposioInstantAdapter } from "./providers/composio-instant.js";
|
|
2
|
+
import { ComposioSdkAdapter } from "./providers/composio-sdk.js";
|
|
3
|
+
import { MicrosoftGovernanceAdapter } from "./providers/microsoft-governance.js";
|
|
4
|
+
import { StripeBillingAdapter } from "./providers/stripe-billing.js";
|
|
5
|
+
import { StripeProjectsAdapter } from "./providers/stripe-projects.js";
|
|
6
|
+
import { SupermemoryAdapter } from "./providers/supermemory.js";
|
|
7
|
+
export class MuxoEngine {
|
|
8
|
+
config;
|
|
9
|
+
store;
|
|
10
|
+
composioSdk;
|
|
11
|
+
composioInstant;
|
|
12
|
+
stripeProjects;
|
|
13
|
+
stripeBilling;
|
|
14
|
+
governance;
|
|
15
|
+
supermemory;
|
|
16
|
+
constructor(config, store) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
this.store = store;
|
|
19
|
+
this.composioSdk = new ComposioSdkAdapter(config);
|
|
20
|
+
this.composioInstant = new ComposioInstantAdapter(config);
|
|
21
|
+
this.stripeProjects = new StripeProjectsAdapter(config);
|
|
22
|
+
this.stripeBilling = new StripeBillingAdapter(config, store);
|
|
23
|
+
this.governance = new MicrosoftGovernanceAdapter(config.vaultMasterKey);
|
|
24
|
+
this.supermemory = new SupermemoryAdapter(config);
|
|
25
|
+
}
|
|
26
|
+
// 1. get_profile
|
|
27
|
+
getProfile(ctx) {
|
|
28
|
+
return {
|
|
29
|
+
user: ctx.user,
|
|
30
|
+
actorId: ctx.actorId,
|
|
31
|
+
authType: ctx.authType,
|
|
32
|
+
workspaceId: ctx.workspace.id,
|
|
33
|
+
workspaceName: ctx.workspace.name,
|
|
34
|
+
tier: ctx.workspace.tier,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// 2. get_organization
|
|
38
|
+
getOrganization(ctx) {
|
|
39
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
40
|
+
const resources = this.store.listResources(ws.id);
|
|
41
|
+
const connections = this.store.listConnections(ws.id);
|
|
42
|
+
const supplierSettlementSummary = this.store.getSupplierSettlementSummary(ws.id);
|
|
43
|
+
return {
|
|
44
|
+
organization: {
|
|
45
|
+
id: ws.id,
|
|
46
|
+
name: ws.name,
|
|
47
|
+
slug: ws.slug,
|
|
48
|
+
tier: ws.tier,
|
|
49
|
+
usage: {
|
|
50
|
+
monthlyActionsUsed: ws.monthlyActionsUsed,
|
|
51
|
+
monthlyActionsLimit: ws.monthlyActionsLimit,
|
|
52
|
+
monthlyActionsRemaining: Math.max(0, ws.monthlyActionsLimit - ws.monthlyActionsUsed),
|
|
53
|
+
creditsBalance: ws.creditsBalance,
|
|
54
|
+
},
|
|
55
|
+
supplierSettlementSummary,
|
|
56
|
+
policy: ws.policy,
|
|
57
|
+
activeConnectionsCount: connections.filter((c) => c.status === "active").length,
|
|
58
|
+
provisionedResourcesCount: resources.filter((r) => r.status === "active").length,
|
|
59
|
+
projectsEnvironment: ws.projectsEnvName,
|
|
60
|
+
createdAt: ws.createdAt,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// 3. search_tools
|
|
65
|
+
async searchTools(params) {
|
|
66
|
+
const limit = Math.min(100, Math.max(1, Number(params.limit || 25)));
|
|
67
|
+
const lane = params.lane || "all";
|
|
68
|
+
const query = params.query?.trim();
|
|
69
|
+
const results = [];
|
|
70
|
+
if (lane === "all" || lane === "composio_instant") {
|
|
71
|
+
results.push(...this.composioInstant.searchTools(query, limit));
|
|
72
|
+
}
|
|
73
|
+
if (lane === "all" || lane === "composio_sdk") {
|
|
74
|
+
const sdkTools = await this.composioSdk.searchTools(query, limit);
|
|
75
|
+
results.push(...sdkTools);
|
|
76
|
+
}
|
|
77
|
+
if (lane === "all" || lane === "stripe_projects") {
|
|
78
|
+
results.push(...this.stripeProjects.searchTools(query, limit));
|
|
79
|
+
}
|
|
80
|
+
const filtered = params.riskLevel
|
|
81
|
+
? results.filter((t) => t.riskLevel === params.riskLevel)
|
|
82
|
+
: results;
|
|
83
|
+
return {
|
|
84
|
+
tools: filtered.slice(0, limit),
|
|
85
|
+
total: filtered.length,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// 4. inspect_tool
|
|
89
|
+
async inspectTool(slug) {
|
|
90
|
+
const normalized = slug.trim();
|
|
91
|
+
const instant = this.composioInstant.inspectTool(normalized);
|
|
92
|
+
if (instant)
|
|
93
|
+
return instant;
|
|
94
|
+
const proj = this.stripeProjects.inspectTool(normalized);
|
|
95
|
+
if (proj)
|
|
96
|
+
return proj;
|
|
97
|
+
const sdk = await this.composioSdk.inspectTool(normalized);
|
|
98
|
+
if (sdk)
|
|
99
|
+
return sdk;
|
|
100
|
+
throw new Error(`Tool '${normalized}' not found in Muxo catalog. Use search_tools to discover valid slugs.`);
|
|
101
|
+
}
|
|
102
|
+
// 5. connection_status
|
|
103
|
+
async connectionStatus(ctx, params) {
|
|
104
|
+
if (params?.syncLive !== false) {
|
|
105
|
+
// Sync live Composio connected accounts for this workspace
|
|
106
|
+
const liveComposio = await this.composioSdk.listLiveConnections(ctx.workspace.id);
|
|
107
|
+
for (const c of liveComposio) {
|
|
108
|
+
this.store.upsertConnection({
|
|
109
|
+
workspaceId: ctx.workspace.id,
|
|
110
|
+
lane: "composio_sdk",
|
|
111
|
+
providerSlug: c.providerSlug,
|
|
112
|
+
providerName: c.providerName,
|
|
113
|
+
status: c.status,
|
|
114
|
+
externalAccountId: c.externalAccountId,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
// Sync live Stripe Projects linked providers
|
|
118
|
+
try {
|
|
119
|
+
const projStatus = await this.stripeProjects.getLiveStatus();
|
|
120
|
+
for (const p of projStatus.providers) {
|
|
121
|
+
this.store.upsertConnection({
|
|
122
|
+
workspaceId: ctx.workspace.id,
|
|
123
|
+
lane: "stripe_projects",
|
|
124
|
+
providerSlug: p.slug,
|
|
125
|
+
providerName: p.name,
|
|
126
|
+
status: p.status,
|
|
127
|
+
externalAccountId: p.id,
|
|
128
|
+
redirectUrl: p.redirectUrl,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Keep cached connections if CLI is busy
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
let connections = this.store.listConnections(ctx.workspace.id);
|
|
137
|
+
if (params?.provider) {
|
|
138
|
+
const target = params.provider.toLowerCase();
|
|
139
|
+
connections = connections.filter((c) => c.providerSlug.toLowerCase() === target ||
|
|
140
|
+
c.providerName.toLowerCase().includes(target));
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
workspaceId: ctx.workspace.id,
|
|
144
|
+
instantWalletStatus: "active",
|
|
145
|
+
connections,
|
|
146
|
+
summary: {
|
|
147
|
+
total: connections.length,
|
|
148
|
+
active: connections.filter((c) => c.status === "active").length,
|
|
149
|
+
pending: connections.filter((c) => c.status === "pending").length,
|
|
150
|
+
expired: connections.filter((c) => c.status === "expired").length,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// 6. start_connection
|
|
155
|
+
async startConnection(ctx, params) {
|
|
156
|
+
const rawProvider = params.provider.trim().toLowerCase();
|
|
157
|
+
const isProjectsLane = params.lane === "stripe_projects" || rawProvider.startsWith("resource.");
|
|
158
|
+
const cleanSlug = rawProvider.replace(/^(app\.|resource\.)/, "");
|
|
159
|
+
if (isProjectsLane) {
|
|
160
|
+
const linked = await this.stripeProjects.linkProvider(cleanSlug);
|
|
161
|
+
const record = this.store.upsertConnection({
|
|
162
|
+
workspaceId: ctx.workspace.id,
|
|
163
|
+
lane: "stripe_projects",
|
|
164
|
+
providerSlug: linked.providerSlug,
|
|
165
|
+
providerName: linked.providerSlug.charAt(0).toUpperCase() +
|
|
166
|
+
linked.providerSlug.slice(1),
|
|
167
|
+
status: linked.status,
|
|
168
|
+
redirectUrl: linked.redirectUrl,
|
|
169
|
+
});
|
|
170
|
+
return { ...record, message: linked.message };
|
|
171
|
+
}
|
|
172
|
+
const started = await this.composioSdk.startConnection({
|
|
173
|
+
workspaceId: ctx.workspace.id,
|
|
174
|
+
providerSlug: cleanSlug,
|
|
175
|
+
callbackUrl: params.callbackUrl,
|
|
176
|
+
});
|
|
177
|
+
const record = this.store.upsertConnection({
|
|
178
|
+
workspaceId: ctx.workspace.id,
|
|
179
|
+
lane: "composio_sdk",
|
|
180
|
+
providerSlug: started.providerSlug,
|
|
181
|
+
providerName: started.providerName,
|
|
182
|
+
status: started.status,
|
|
183
|
+
externalAccountId: started.externalAccountId,
|
|
184
|
+
redirectUrl: started.redirectUrl,
|
|
185
|
+
metadata: { callbackUrl: started.callbackUrl },
|
|
186
|
+
});
|
|
187
|
+
return {
|
|
188
|
+
...record,
|
|
189
|
+
callbackUrl: started.callbackUrl,
|
|
190
|
+
message: started.message,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
// 6b. wait_for_connection (OAuth handoff polling & recovery, like COMPOSIO_WAIT_FOR_CONNECTIONS)
|
|
194
|
+
async waitForConnection(ctx, params) {
|
|
195
|
+
const cleanSlug = params.provider
|
|
196
|
+
.trim()
|
|
197
|
+
.toLowerCase()
|
|
198
|
+
.replace(/^(app\.|resource\.)/, "");
|
|
199
|
+
const timeoutMs = Math.min(120, Math.max(0, Number(params.timeoutSeconds ?? 15))) * 1000;
|
|
200
|
+
const pollIntervalMs = Math.min(5000, Math.max(200, Number(params.pollIntervalMs ?? 1500)));
|
|
201
|
+
const deadline = Date.now() + timeoutMs;
|
|
202
|
+
let current = this.store
|
|
203
|
+
.listConnections(ctx.workspace.id)
|
|
204
|
+
.find((c) => c.providerSlug.toLowerCase() === cleanSlug);
|
|
205
|
+
if (!current || (!current.redirectUrl && current.status !== "active")) {
|
|
206
|
+
try {
|
|
207
|
+
current = await this.startConnection(ctx, { provider: cleanSlug });
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Proceed with status check
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
while (true) {
|
|
214
|
+
const latestLocal = this.store
|
|
215
|
+
.listConnections(ctx.workspace.id)
|
|
216
|
+
.find((c) => c.providerSlug.toLowerCase() === cleanSlug);
|
|
217
|
+
if (latestLocal) {
|
|
218
|
+
current = latestLocal;
|
|
219
|
+
if (latestLocal.status === "active") {
|
|
220
|
+
return {
|
|
221
|
+
connected: true,
|
|
222
|
+
provider: cleanSlug,
|
|
223
|
+
status: "active",
|
|
224
|
+
connection: latestLocal,
|
|
225
|
+
redirectUrl: null,
|
|
226
|
+
message: `${latestLocal.providerName} OAuth handoff completed and connection is active.`,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (current?.externalAccountId && current.lane === "composio_sdk") {
|
|
231
|
+
const direct = await this.composioSdk.getConnectedAccountStatus(current.externalAccountId);
|
|
232
|
+
if (direct && direct.status === "active") {
|
|
233
|
+
current = this.store.upsertConnection({
|
|
234
|
+
workspaceId: ctx.workspace.id,
|
|
235
|
+
lane: "composio_sdk",
|
|
236
|
+
providerSlug: cleanSlug,
|
|
237
|
+
providerName: cleanSlug.charAt(0).toUpperCase() + cleanSlug.slice(1),
|
|
238
|
+
status: "active",
|
|
239
|
+
externalAccountId: direct.id,
|
|
240
|
+
redirectUrl: null,
|
|
241
|
+
});
|
|
242
|
+
return {
|
|
243
|
+
connected: true,
|
|
244
|
+
provider: cleanSlug,
|
|
245
|
+
status: "active",
|
|
246
|
+
connection: current,
|
|
247
|
+
redirectUrl: null,
|
|
248
|
+
message: `${current.providerName} OAuth handoff completed and connection is active.`,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const synced = await this.connectionStatus(ctx, {
|
|
253
|
+
provider: cleanSlug,
|
|
254
|
+
syncLive: true,
|
|
255
|
+
});
|
|
256
|
+
const list = synced.connections || [];
|
|
257
|
+
const found = list.find((c) => c.providerSlug.toLowerCase() === cleanSlug);
|
|
258
|
+
if (found) {
|
|
259
|
+
current = found;
|
|
260
|
+
if (found.status === "active") {
|
|
261
|
+
return {
|
|
262
|
+
connected: true,
|
|
263
|
+
provider: cleanSlug,
|
|
264
|
+
status: "active",
|
|
265
|
+
connection: found,
|
|
266
|
+
redirectUrl: null,
|
|
267
|
+
message: `${found.providerName} connection is active and ready for governed execution.`,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (Date.now() >= deadline) {
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
275
|
+
}
|
|
276
|
+
if (!current) {
|
|
277
|
+
current = await this.startConnection(ctx, { provider: cleanSlug });
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
connected: false,
|
|
281
|
+
provider: cleanSlug,
|
|
282
|
+
status: current.status,
|
|
283
|
+
connection: current,
|
|
284
|
+
redirectUrl: current.redirectUrl,
|
|
285
|
+
message: current.redirectUrl
|
|
286
|
+
? `Awaiting user OAuth authorization for ${current.providerName}. Open ${current.redirectUrl} in a browser, then re-run wait_for_connection.`
|
|
287
|
+
: `Connection for ${current.providerName} is currently ${current.status}.`,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
// 6c. Browser OAuth callback handoff handler (GET /oauth/callback)
|
|
291
|
+
async handleOAuthCallback(params) {
|
|
292
|
+
const cleanSlug = params.provider
|
|
293
|
+
.trim()
|
|
294
|
+
.toLowerCase()
|
|
295
|
+
.replace(/^(app\.|resource\.)/, "");
|
|
296
|
+
const providerName = cleanSlug.charAt(0).toUpperCase() + cleanSlug.slice(1);
|
|
297
|
+
const ws = this.store.getWorkspace(params.workspaceId);
|
|
298
|
+
if (!ws) {
|
|
299
|
+
throw new Error(`Workspace '${params.workspaceId}' not found`);
|
|
300
|
+
}
|
|
301
|
+
const existing = this.store
|
|
302
|
+
.listConnections(ws.id)
|
|
303
|
+
.find((c) => c.providerSlug.toLowerCase() === cleanSlug);
|
|
304
|
+
const accountId = params.connectedAccountId || existing?.externalAccountId || null;
|
|
305
|
+
let resolvedStatus = "pending";
|
|
306
|
+
if (accountId) {
|
|
307
|
+
const live = await this.composioSdk.getConnectedAccountStatus(accountId);
|
|
308
|
+
if (live) {
|
|
309
|
+
resolvedStatus = live.status;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (resolvedStatus !== "active") {
|
|
313
|
+
const liveList = await this.composioSdk.listLiveConnections(ws.id);
|
|
314
|
+
const match = liveList.find((c) => c.providerSlug === cleanSlug);
|
|
315
|
+
if (match) {
|
|
316
|
+
resolvedStatus = match.status;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const rawCallbackStatus = String(params.status || "").toLowerCase();
|
|
320
|
+
if (rawCallbackStatus === "success" ||
|
|
321
|
+
rawCallbackStatus === "active" ||
|
|
322
|
+
rawCallbackStatus === "connected") {
|
|
323
|
+
resolvedStatus = "active";
|
|
324
|
+
}
|
|
325
|
+
const updated = this.store.upsertConnection({
|
|
326
|
+
workspaceId: ws.id,
|
|
327
|
+
lane: "composio_sdk",
|
|
328
|
+
providerSlug: cleanSlug,
|
|
329
|
+
providerName,
|
|
330
|
+
status: resolvedStatus,
|
|
331
|
+
externalAccountId: accountId,
|
|
332
|
+
redirectUrl: resolvedStatus === "active" ? null : existing?.redirectUrl,
|
|
333
|
+
});
|
|
334
|
+
return {
|
|
335
|
+
ok: true,
|
|
336
|
+
workspaceId: ws.id,
|
|
337
|
+
providerSlug: cleanSlug,
|
|
338
|
+
providerName,
|
|
339
|
+
status: updated.status,
|
|
340
|
+
externalAccountId: updated.externalAccountId,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
// 7. plan_action
|
|
344
|
+
async planAction(ctx, params) {
|
|
345
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
346
|
+
const tool = await this.inspectTool(params.toolSlug);
|
|
347
|
+
if (ws.monthlyActionsUsed >= ws.monthlyActionsLimit) {
|
|
348
|
+
throw new Error(`Monthly action limit reached (${ws.monthlyActionsUsed}/${ws.monthlyActionsLimit}). Upgrade to Starter ($29/mo) to unlock 5,000 actions/mo.`);
|
|
349
|
+
}
|
|
350
|
+
if (tool.creditCost > ws.creditsBalance) {
|
|
351
|
+
throw new Error(`Insufficient workspace credits for ${tool.slug} (requires ${tool.creditCost}, available ${ws.creditsBalance}). Top up credits or upgrade plan.`);
|
|
352
|
+
}
|
|
353
|
+
if (tool.creditCost > ws.policy.maxCreditsPerRun) {
|
|
354
|
+
throw new Error(`Tool credit cost (${tool.creditCost}) exceeds workspace policy maxCreditsPerRun (${ws.policy.maxCreditsPerRun}).`);
|
|
355
|
+
}
|
|
356
|
+
// 1. Evaluate Microsoft Agent Governance Pipeline:
|
|
357
|
+
// Agent -> McpSecurityScanner -> PolicyEngine -> AgentIdentity (Ed25519 DID) -> AuditLogger -> DecisionRecord
|
|
358
|
+
const decisionRecord = this.governance.evaluatePlan({
|
|
359
|
+
workspace: ws,
|
|
360
|
+
actorId: ctx.actorId,
|
|
361
|
+
tool,
|
|
362
|
+
input: params.input || {},
|
|
363
|
+
reason: params.reason || `Planned execution of ${tool.slug}`,
|
|
364
|
+
});
|
|
365
|
+
if (decisionRecord.verdict === "deny") {
|
|
366
|
+
throw new Error(`Microsoft Agent Governance denied plan for ${tool.slug} (rule: ${decisionRecord.matchedRule}): ${decisionRecord.reason}`);
|
|
367
|
+
}
|
|
368
|
+
// 2. Recall Supermemory v5 contextual brain for this workspace + tool intent
|
|
369
|
+
const memoryQuery = `${tool.slug} ${tool.name} ${params.reason || ""}`.trim();
|
|
370
|
+
const memoryContext = await this.supermemory.getContextForWorkspace(ws.id, memoryQuery);
|
|
371
|
+
const requiresHumanApproval = decisionRecord.verdict === "escalate" ||
|
|
372
|
+
(ws.policy.mode === "enforce" &&
|
|
373
|
+
ws.policy.requireApprovalFor.includes(tool.riskLevel));
|
|
374
|
+
const approvalStatus = requiresHumanApproval
|
|
375
|
+
? "pending_approval"
|
|
376
|
+
: "auto_approved";
|
|
377
|
+
const plan = this.store.createPlan({
|
|
378
|
+
workspaceId: ws.id,
|
|
379
|
+
actorId: ctx.actorId,
|
|
380
|
+
toolSlug: tool.slug,
|
|
381
|
+
upstreamSlug: tool.upstreamSlug,
|
|
382
|
+
lane: tool.lane,
|
|
383
|
+
riskLevel: tool.riskLevel,
|
|
384
|
+
input: params.input || {},
|
|
385
|
+
reason: params.reason || `Planned execution of ${tool.slug}`,
|
|
386
|
+
creditCost: tool.creditCost,
|
|
387
|
+
approvalStatus,
|
|
388
|
+
decisionRecord,
|
|
389
|
+
memoryContext,
|
|
390
|
+
});
|
|
391
|
+
// 3. Check external user OAuth connection state for Connected Apps (composio_sdk)
|
|
392
|
+
let authHandoff;
|
|
393
|
+
if (tool.lane === "composio_sdk" && tool.requiresAuth) {
|
|
394
|
+
let existingConn = this.store
|
|
395
|
+
.listConnections(ws.id)
|
|
396
|
+
.find((c) => c.lane === "composio_sdk" &&
|
|
397
|
+
c.providerSlug.toLowerCase() === tool.providerSlug.toLowerCase());
|
|
398
|
+
if (!existingConn || existingConn.status !== "active") {
|
|
399
|
+
try {
|
|
400
|
+
if (!existingConn || !existingConn.redirectUrl) {
|
|
401
|
+
existingConn = await this.startConnection(ctx, {
|
|
402
|
+
provider: tool.providerSlug,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
// Keep existing connection state if offline
|
|
408
|
+
}
|
|
409
|
+
if (!existingConn || existingConn.status !== "active") {
|
|
410
|
+
authHandoff = {
|
|
411
|
+
required: true,
|
|
412
|
+
provider: tool.providerSlug,
|
|
413
|
+
status: existingConn?.status || "pending",
|
|
414
|
+
redirectUrl: existingConn?.redirectUrl || null,
|
|
415
|
+
externalAccountId: existingConn?.externalAccountId || null,
|
|
416
|
+
instructions: existingConn?.redirectUrl
|
|
417
|
+
? `Ask the user to authorize ${tool.providerName} at ${existingConn.redirectUrl}, then call wait_for_connection({ provider: "${tool.providerSlug}" }) before executing ${plan.id}.`
|
|
418
|
+
: `Call start_connection({ provider: "${tool.providerSlug}" }) to authorize ${tool.providerName} before executing ${plan.id}.`,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const executeToolName = `execute_${tool.riskLevel}`;
|
|
424
|
+
let nextStep = approvalStatus === "pending_approval"
|
|
425
|
+
? `Plan ${plan.id} requires human owner approval in the Muxo Console or via approve_plan before calling ${executeToolName}.`
|
|
426
|
+
: `Plan ${plan.id} is auto-approved. Call ${executeToolName}({ plan_id: "${plan.id}" }) to execute.`;
|
|
427
|
+
if (authHandoff?.required) {
|
|
428
|
+
nextStep = `${authHandoff.instructions} Once connected, ${nextStep}`;
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
plan,
|
|
432
|
+
tool,
|
|
433
|
+
decisionRecord,
|
|
434
|
+
memoryContext,
|
|
435
|
+
...(authHandoff ? { authHandoff } : {}),
|
|
436
|
+
nextStep,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
// 8. get_plan
|
|
440
|
+
getPlan(ctx, planId) {
|
|
441
|
+
const plan = this.store.getPlan(ctx.workspace.id, planId);
|
|
442
|
+
if (!plan)
|
|
443
|
+
throw new Error(`Plan '${planId}' not found in workspace.`);
|
|
444
|
+
return plan;
|
|
445
|
+
}
|
|
446
|
+
// Human owner approval / rejection
|
|
447
|
+
approvePlan(ctx, planId, decision = "approved") {
|
|
448
|
+
const plan = this.getPlan(ctx, planId);
|
|
449
|
+
if (plan.approvalStatus === "executed") {
|
|
450
|
+
throw new Error(`Plan '${planId}' has already been executed.`);
|
|
451
|
+
}
|
|
452
|
+
return this.store.updatePlanApproval({
|
|
453
|
+
workspaceId: ctx.workspace.id,
|
|
454
|
+
planId,
|
|
455
|
+
approvalStatus: decision,
|
|
456
|
+
approvedBy: ctx.user.email,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
// 9. get_run_context
|
|
460
|
+
async getRunContext(ctx, query) {
|
|
461
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
462
|
+
const resources = this.store.listResources(ws.id);
|
|
463
|
+
const activeEnv = this.store.getDecryptedWorkspaceEnv(ws.id);
|
|
464
|
+
const redactedEnv = {};
|
|
465
|
+
for (const [k, v] of Object.entries(activeEnv)) {
|
|
466
|
+
redactedEnv[k] =
|
|
467
|
+
v.length > 8 ? `${v.slice(0, 4)}••••${v.slice(-3)}` : "••••••••";
|
|
468
|
+
}
|
|
469
|
+
const memoryContext = await this.supermemory.getContextForWorkspace(ws.id, query);
|
|
470
|
+
return {
|
|
471
|
+
workspaceId: ws.id,
|
|
472
|
+
workspaceSlug: ws.slug,
|
|
473
|
+
projectsEnvironment: ws.projectsEnvName,
|
|
474
|
+
policy: ws.policy,
|
|
475
|
+
creditsBalance: ws.creditsBalance,
|
|
476
|
+
monthlyActionsRemaining: Math.max(0, ws.monthlyActionsLimit - ws.monthlyActionsUsed),
|
|
477
|
+
provisionedResources: resources.filter((r) => r.status === "active"),
|
|
478
|
+
environmentVariablesRedacted: redactedEnv,
|
|
479
|
+
supermemoryBrain: memoryContext,
|
|
480
|
+
governance: this.governance.getStatus(ws),
|
|
481
|
+
envPullHint: "Run `muxo env pull` or call GET /api/v1/resources/env to write decrypted credentials to your local .env file.",
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
// Supermemory Workspace Brain Operations
|
|
485
|
+
async getMemoryOverview(ctx, query) {
|
|
486
|
+
const context = await this.supermemory.getContextForWorkspace(ctx.workspace.id, query);
|
|
487
|
+
const recentMemories = this.store.listMemoryRecords(ctx.workspace.id, 25);
|
|
488
|
+
return {
|
|
489
|
+
enabled: context.enabled,
|
|
490
|
+
namespace: context.namespace,
|
|
491
|
+
context,
|
|
492
|
+
recentMemories,
|
|
493
|
+
memories: recentMemories,
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
async addWorkspaceMemory(ctx, params) {
|
|
497
|
+
const kind = params.kind || "fact";
|
|
498
|
+
const remote = await this.supermemory.addMemory({
|
|
499
|
+
workspaceId: ctx.workspace.id,
|
|
500
|
+
title: params.title,
|
|
501
|
+
content: params.content,
|
|
502
|
+
kind,
|
|
503
|
+
metadata: params.metadata,
|
|
504
|
+
});
|
|
505
|
+
return this.store.saveMemoryRecord({
|
|
506
|
+
workspaceId: ctx.workspace.id,
|
|
507
|
+
namespace: remote.namespace,
|
|
508
|
+
documentId: remote.documentId,
|
|
509
|
+
title: params.title,
|
|
510
|
+
content: remote.sanitizedContent,
|
|
511
|
+
kind,
|
|
512
|
+
metadata: {
|
|
513
|
+
...(params.metadata || {}),
|
|
514
|
+
supermemoryStatus: remote.status,
|
|
515
|
+
},
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
async deleteWorkspaceMemory(ctx, idOrDocId) {
|
|
519
|
+
const local = this.store.deleteMemoryRecord(ctx.workspace.id, idOrDocId);
|
|
520
|
+
const docId = local?.documentId || idOrDocId;
|
|
521
|
+
const remote = await this.supermemory.deleteMemory(ctx.workspace.id, docId);
|
|
522
|
+
return {
|
|
523
|
+
deleted: Boolean(local || remote.deleted),
|
|
524
|
+
record: local,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
// 10-15. execute_<risk_level>
|
|
528
|
+
async executePlanForRisk(ctx, planId, expectedRisk) {
|
|
529
|
+
const plan = this.getPlan(ctx, planId);
|
|
530
|
+
if (plan.riskLevel !== expectedRisk) {
|
|
531
|
+
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}'.`);
|
|
532
|
+
}
|
|
533
|
+
if (plan.runId) {
|
|
534
|
+
const existingRun = this.store.getRun(ctx.workspace.id, plan.runId);
|
|
535
|
+
const existingReceipt = this.store.getReceipt(ctx.workspace.id, plan.runId);
|
|
536
|
+
if (existingRun && existingReceipt) {
|
|
537
|
+
return { run: existingRun, receipt: existingReceipt };
|
|
538
|
+
}
|
|
539
|
+
throw new Error(`Plan '${plan.id}' was already claimed by run '${plan.runId}' (execute-once invariant).`);
|
|
540
|
+
}
|
|
541
|
+
if (plan.approvalStatus === "pending_approval") {
|
|
542
|
+
throw new Error(`Plan '${plan.id}' (${plan.riskLevel}) is awaiting human approval. Approve it in the Muxo Console or via CLI 'muxo approve ${plan.id}' before executing.`);
|
|
543
|
+
}
|
|
544
|
+
if (plan.approvalStatus === "rejected") {
|
|
545
|
+
throw new Error(`Plan '${plan.id}' was rejected and cannot be executed.`);
|
|
546
|
+
}
|
|
547
|
+
const ws = this.store.getWorkspace(ctx.workspace.id);
|
|
548
|
+
const tool = await this.inspectTool(plan.toolSlug);
|
|
549
|
+
const execDecisionRecord = this.governance.evaluateExecution({
|
|
550
|
+
workspace: ws,
|
|
551
|
+
actorId: ctx.actorId,
|
|
552
|
+
plan,
|
|
553
|
+
tool,
|
|
554
|
+
});
|
|
555
|
+
if (execDecisionRecord.verdict === "deny") {
|
|
556
|
+
throw new Error(`Microsoft Agent Governance denied execution of plan '${plan.id}': ${execDecisionRecord.reason}`);
|
|
557
|
+
}
|
|
558
|
+
const run = this.store.createRun(plan, ctx.actorId);
|
|
559
|
+
const startedMs = Date.now();
|
|
560
|
+
try {
|
|
561
|
+
let output;
|
|
562
|
+
if (plan.lane === "composio_instant") {
|
|
563
|
+
output = await this.composioInstant.executeTool({
|
|
564
|
+
workspaceId: ctx.workspace.id,
|
|
565
|
+
tool,
|
|
566
|
+
input: plan.input,
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
else if (plan.lane === "composio_sdk") {
|
|
570
|
+
let connections = this.store.listConnections(ctx.workspace.id);
|
|
571
|
+
let activeConn = connections.find((c) => c.lane === "composio_sdk" &&
|
|
572
|
+
c.providerSlug === tool.providerSlug &&
|
|
573
|
+
c.status === "active");
|
|
574
|
+
if (!activeConn) {
|
|
575
|
+
await this.waitForConnection(ctx, {
|
|
576
|
+
provider: tool.providerSlug,
|
|
577
|
+
timeoutSeconds: 0,
|
|
578
|
+
}).catch(() => { });
|
|
579
|
+
connections = this.store.listConnections(ctx.workspace.id);
|
|
580
|
+
activeConn = connections.find((c) => c.lane === "composio_sdk" &&
|
|
581
|
+
c.providerSlug === tool.providerSlug &&
|
|
582
|
+
c.status === "active");
|
|
583
|
+
}
|
|
584
|
+
output = await this.composioSdk.executeTool({
|
|
585
|
+
workspaceId: ctx.workspace.id,
|
|
586
|
+
tool,
|
|
587
|
+
input: plan.input,
|
|
588
|
+
connectedAccountId: activeConn?.externalAccountId || null,
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
else if (plan.lane === "stripe_projects") {
|
|
592
|
+
const projResult = await this.stripeProjects.executeTool({
|
|
593
|
+
workspaceId: ctx.workspace.id,
|
|
594
|
+
tool,
|
|
595
|
+
input: plan.input,
|
|
596
|
+
});
|
|
597
|
+
output = projResult.output;
|
|
598
|
+
if (projResult.provisionedResource) {
|
|
599
|
+
this.store.upsertResource({
|
|
600
|
+
workspaceId: ctx.workspace.id,
|
|
601
|
+
...projResult.provisionedResource,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
throw new Error(`Unsupported execution lane: ${plan.lane}`);
|
|
607
|
+
}
|
|
608
|
+
const durationMs = Date.now() - startedMs;
|
|
609
|
+
const finalized = this.store.finalizeRun({
|
|
610
|
+
workspaceId: ctx.workspace.id,
|
|
611
|
+
runId: run.id,
|
|
612
|
+
status: "succeeded",
|
|
613
|
+
output,
|
|
614
|
+
actionsMetered: 1,
|
|
615
|
+
creditsMetered: plan.creditCost,
|
|
616
|
+
durationMs,
|
|
617
|
+
decisionRecord: execDecisionRecord,
|
|
618
|
+
});
|
|
619
|
+
// Record secret-redacted post-run memory in Supermemory for stateful actions
|
|
620
|
+
if (plan.riskLevel !== "read") {
|
|
621
|
+
const summary = `Executed ${plan.toolSlug} (${plan.lane}, risk=${plan.riskLevel}) for reason "${plan.reason}" -> status=succeeded (receipt=${finalized.receipt.id}).`;
|
|
622
|
+
this.supermemory
|
|
623
|
+
.recordExecutionMemory({
|
|
624
|
+
workspaceId: ctx.workspace.id,
|
|
625
|
+
toolSlug: plan.toolSlug,
|
|
626
|
+
lane: plan.lane,
|
|
627
|
+
riskLevel: plan.riskLevel,
|
|
628
|
+
reason: plan.reason,
|
|
629
|
+
status: "succeeded",
|
|
630
|
+
summary,
|
|
631
|
+
})
|
|
632
|
+
.then((docId) => {
|
|
633
|
+
if (docId) {
|
|
634
|
+
this.store.saveMemoryRecord({
|
|
635
|
+
workspaceId: ctx.workspace.id,
|
|
636
|
+
namespace: this.supermemory.getWorkspaceNamespace(ctx.workspace.id),
|
|
637
|
+
documentId: docId,
|
|
638
|
+
title: `Execution: ${plan.toolSlug} (succeeded)`,
|
|
639
|
+
content: this.supermemory.sanitizeContent(summary),
|
|
640
|
+
kind: "execution_outcome",
|
|
641
|
+
metadata: {
|
|
642
|
+
runId: finalized.run.id,
|
|
643
|
+
receiptId: finalized.receipt.id,
|
|
644
|
+
toolSlug: plan.toolSlug,
|
|
645
|
+
},
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
})
|
|
649
|
+
.catch(() => { });
|
|
650
|
+
}
|
|
651
|
+
return finalized;
|
|
652
|
+
}
|
|
653
|
+
catch (err) {
|
|
654
|
+
const durationMs = Date.now() - startedMs;
|
|
655
|
+
const errorMessage = String(err?.message || err || "Execution failed");
|
|
656
|
+
return this.store.finalizeRun({
|
|
657
|
+
workspaceId: ctx.workspace.id,
|
|
658
|
+
runId: run.id,
|
|
659
|
+
status: "failed",
|
|
660
|
+
output: { error: errorMessage },
|
|
661
|
+
error: errorMessage,
|
|
662
|
+
actionsMetered: 1,
|
|
663
|
+
creditsMetered: 0,
|
|
664
|
+
durationMs,
|
|
665
|
+
decisionRecord: execDecisionRecord,
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
// 16. get_run
|
|
670
|
+
getRun(ctx, runId) {
|
|
671
|
+
const run = this.store.getRun(ctx.workspace.id, runId);
|
|
672
|
+
if (!run)
|
|
673
|
+
throw new Error(`Run '${runId}' not found in workspace.`);
|
|
674
|
+
return run;
|
|
675
|
+
}
|
|
676
|
+
// 17. cancel_run
|
|
677
|
+
cancelRun(ctx, runId) {
|
|
678
|
+
const run = this.getRun(ctx, runId);
|
|
679
|
+
if (run.status === "succeeded" || run.status === "failed" || run.status === "cancelled") {
|
|
680
|
+
return {
|
|
681
|
+
run,
|
|
682
|
+
receipt: this.store.getReceipt(ctx.workspace.id, run.id),
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
const finalized = this.store.finalizeRun({
|
|
686
|
+
workspaceId: ctx.workspace.id,
|
|
687
|
+
runId: run.id,
|
|
688
|
+
status: "cancelled",
|
|
689
|
+
output: { cancelledBy: ctx.actorId },
|
|
690
|
+
error: "Cancelled by operator",
|
|
691
|
+
actionsMetered: 0,
|
|
692
|
+
creditsMetered: 0,
|
|
693
|
+
durationMs: Date.now() - new Date(run.startedAt).getTime(),
|
|
694
|
+
});
|
|
695
|
+
return finalized;
|
|
696
|
+
}
|
|
697
|
+
// 18. get_receipt
|
|
698
|
+
getReceipt(ctx, receiptOrRunId) {
|
|
699
|
+
const receipt = this.store.getReceipt(ctx.workspace.id, receiptOrRunId);
|
|
700
|
+
if (!receipt) {
|
|
701
|
+
throw new Error(`Receipt '${receiptOrRunId}' not found in workspace.`);
|
|
702
|
+
}
|
|
703
|
+
const signatureValid = this.store.verifyReceipt(receipt);
|
|
704
|
+
const governanceSignatureValid = receipt.decisionRecord
|
|
705
|
+
? this.governance.verifyDecisionRecord(receipt.decisionRecord)
|
|
706
|
+
: true;
|
|
707
|
+
return { receipt, signatureValid, governanceSignatureValid };
|
|
708
|
+
}
|
|
709
|
+
}
|