@muxoai/sdk 1.3.0 → 1.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/.agents/plugins/marketplace.json +2 -2
  2. package/.claude-plugin/marketplace.json +19 -4
  3. package/.codex/config.toml +9 -2
  4. package/ARCHITECTURE.md +16 -6
  5. package/README.md +69 -24
  6. package/dist/clerk.d.ts +35 -0
  7. package/dist/clerk.js +201 -0
  8. package/dist/cli.js +1677 -103
  9. package/dist/composio-catalog.d.ts +234 -0
  10. package/dist/composio-catalog.js +1894 -0
  11. package/dist/config.d.ts +16 -0
  12. package/dist/config.js +27 -1
  13. package/dist/durable-workflow.d.ts +112 -0
  14. package/dist/durable-workflow.js +284 -0
  15. package/dist/engine.d.ts +377 -6
  16. package/dist/engine.js +1919 -94
  17. package/dist/index.d.ts +3 -0
  18. package/dist/index.js +3 -0
  19. package/dist/mcp-schema.d.ts +286 -0
  20. package/dist/mcp-schema.js +2580 -0
  21. package/dist/mcp.d.ts +2 -25
  22. package/dist/mcp.js +1008 -507
  23. package/dist/memory-graph.d.ts +45 -0
  24. package/dist/memory-graph.js +293 -0
  25. package/dist/nl.js +214 -35
  26. package/dist/oauth.d.ts +32 -0
  27. package/dist/oauth.js +134 -0
  28. package/dist/oss-suite.d.ts +217 -0
  29. package/dist/oss-suite.js +836 -0
  30. package/dist/policy.d.ts +190 -0
  31. package/dist/policy.js +1597 -0
  32. package/dist/providers/composio-instant.d.ts +4 -0
  33. package/dist/providers/composio-instant.js +890 -535
  34. package/dist/providers/composio-sdk.d.ts +126 -5
  35. package/dist/providers/composio-sdk.js +902 -79
  36. package/dist/providers/microsoft-governance.d.ts +89 -3
  37. package/dist/providers/microsoft-governance.js +673 -26
  38. package/dist/providers/stripe-billing.d.ts +42 -0
  39. package/dist/providers/stripe-billing.js +244 -5
  40. package/dist/providers/stripe-projects.d.ts +459 -4
  41. package/dist/providers/stripe-projects.js +2674 -122
  42. package/dist/providers/supermemory.d.ts +102 -6
  43. package/dist/providers/supermemory.js +609 -55
  44. package/dist/provisioning-managed.d.ts +50 -0
  45. package/dist/provisioning-managed.js +155 -0
  46. package/dist/sdk.d.ts +728 -7
  47. package/dist/sdk.js +539 -10
  48. package/dist/server.js +2744 -321
  49. package/dist/store.d.ts +138 -6
  50. package/dist/store.js +932 -58
  51. package/dist/types.d.ts +330 -4
  52. package/dist/ui/console-html.d.ts +62 -0
  53. package/dist/ui/console-html.js +3742 -2228
  54. package/dist/ui/design-tokens.d.ts +177 -0
  55. package/dist/ui/design-tokens.js +210 -0
  56. package/dist/version.d.ts +1 -0
  57. package/dist/version.js +1 -0
  58. package/package.json +6 -5
  59. package/plugins/muxo/.app.json +1 -1
  60. package/plugins/muxo/.claude-plugin/plugin.json +3 -3
  61. package/plugins/muxo/.codex-plugin/plugin.json +19 -10
  62. package/plugins/muxo/.cursor-plugin/plugin.json +30 -0
  63. package/plugins/muxo/.mcp.json +1 -33
  64. package/plugins/muxo/assets/logo.png +0 -0
  65. package/plugins/muxo/hooks/session-start.sh +16 -4
  66. package/plugins/muxo/hooks/user-prompt-submit.sh +14 -1
  67. package/plugins/muxo/mcp.json +0 -10
  68. package/plugins/muxo/plugin.json +65 -8
  69. package/plugins/muxo/skills/muxo/SKILL.md +142 -14
  70. package/plugins/muxo/skills/muxo/agents/openai.yaml +12 -0
  71. package/plugins/muxo/skills/muxo/references/cli.md +9 -3
  72. package/plugins/muxo/skills/muxo/references/mcp.md +22 -10
  73. package/plugins/muxo/skills/muxo-activity-summary/SKILL.md +81 -0
  74. package/plugins/muxo/skills/setup/SKILL.md +17 -3
  75. package/plugins/muxo/skills/setup/agents/openai.yaml +12 -0
  76. package/src/catalogues/stripe-projects-catalog.json +140 -0
@@ -1,5 +1,23 @@
1
1
  import crypto from "node:crypto";
2
2
  import Supermemory from "supermemory";
3
+ import { formatHumanFriendlyExplanation } from "../policy.js";
4
+ // Profile buckets can hold plain strings or memory objects depending on API version.
5
+ function toFactTexts(items) {
6
+ if (!Array.isArray(items))
7
+ return [];
8
+ return items
9
+ .map((item) => {
10
+ if (item == null)
11
+ return "";
12
+ if (typeof item !== "object")
13
+ return String(item);
14
+ const o = item;
15
+ const text = o.memory ?? o.content ?? o.text ?? o.fact ?? o.value ?? o.summary ?? o.title;
16
+ return typeof text === "string" ? text : JSON.stringify(item);
17
+ })
18
+ .map((s) => s.trim())
19
+ .filter(Boolean);
20
+ }
3
21
  /**
4
22
  * Supermemory v5 Contextual Brain & Context-Aware Tool Call Adapter.
5
23
  * Uses deterministic per-workspace namespaces (`/ns/muxo-v5-<hash>/...`)
@@ -8,21 +26,71 @@ import Supermemory from "supermemory";
8
26
  export class SupermemoryAdapter {
9
27
  config;
10
28
  client;
29
+ byokClients = new Map();
30
+ unavailableUntil = 0;
11
31
  constructor(config) {
12
32
  this.config = config;
13
33
  if (config.supermemoryApiKey) {
14
34
  this.client = new Supermemory({
15
35
  apiKey: config.supermemoryApiKey,
16
36
  baseUrl: config.supermemoryBaseUrl,
17
- maxRetries: 1,
37
+ maxRetries: 0,
38
+ timeoutInSeconds: 2,
18
39
  });
19
40
  }
20
41
  else {
21
42
  this.client = null;
22
43
  }
23
44
  }
24
- isEnabled() {
25
- return Boolean(this.client);
45
+ resolveClient(apiKeyOverride) {
46
+ const trimmedByok = String(apiKeyOverride || "").trim();
47
+ if (trimmedByok) {
48
+ let byokClient = this.byokClients.get(trimmedByok);
49
+ if (!byokClient) {
50
+ byokClient = new Supermemory({
51
+ apiKey: trimmedByok,
52
+ baseUrl: this.config.supermemoryBaseUrl,
53
+ maxRetries: 0,
54
+ timeoutInSeconds: 2,
55
+ });
56
+ this.byokClients.set(trimmedByok, byokClient);
57
+ }
58
+ return {
59
+ client: byokClient,
60
+ fundingSource: "customer_byok_supermemory",
61
+ };
62
+ }
63
+ if (this.client) {
64
+ return {
65
+ client: this.client,
66
+ fundingSource: "muxo_funded",
67
+ };
68
+ }
69
+ return {
70
+ client: null,
71
+ fundingSource: "local_fallback",
72
+ };
73
+ }
74
+ withTimeout(fn, ms = 1000) {
75
+ if (Date.now() < this.unavailableUntil) {
76
+ return Promise.resolve(null);
77
+ }
78
+ return Promise.race([
79
+ fn(),
80
+ new Promise((resolve) => {
81
+ const t = setTimeout(() => {
82
+ this.unavailableUntil = Date.now() + 30_000;
83
+ resolve(null);
84
+ }, ms);
85
+ t.unref?.();
86
+ }),
87
+ ]).catch(() => {
88
+ this.unavailableUntil = Date.now() + 30_000;
89
+ return null;
90
+ });
91
+ }
92
+ isEnabled(apiKeyOverride) {
93
+ return Boolean(this.resolveClient(apiKeyOverride).client);
26
94
  }
27
95
  /**
28
96
  * Derives a strict Supermemory v5 namespace (`^[a-zA-Z0-9_:-]+$`) scoped to a workspace.
@@ -50,21 +118,29 @@ export class SupermemoryAdapter {
50
118
  }
51
119
  /**
52
120
  * Fetches the workspace's static + dynamic profile and optionally semantic search results
53
- * in a single fail-open call.
121
+ * in a single fail-open call. Supports customer BYOK `supermemory` keys via `apiKeyOverride`.
54
122
  */
55
- async getContextForWorkspace(workspaceIdOrParams, optionalQuery) {
123
+ async getContextForWorkspace(workspaceIdOrParams, optionalQuery, optionalApiKeyOverride) {
56
124
  const params = typeof workspaceIdOrParams === "string"
57
- ? { workspaceId: workspaceIdOrParams, query: optionalQuery, limit: 5 }
125
+ ? {
126
+ workspaceId: workspaceIdOrParams,
127
+ query: optionalQuery,
128
+ limit: 5,
129
+ apiKeyOverride: optionalApiKeyOverride,
130
+ }
58
131
  : workspaceIdOrParams;
59
132
  const namespace = this.namespaceForWorkspace(params.workspaceId);
60
- if (!this.client) {
133
+ const { client, fundingSource } = this.resolveClient(params.apiKeyOverride);
134
+ if (!client) {
61
135
  return {
62
136
  enabled: false,
63
137
  namespace,
138
+ fundingSource,
64
139
  staticFacts: [],
65
140
  dynamicContext: [],
66
141
  recalledMemories: [],
67
142
  formattedPromptContext: "Supermemory v5 disabled (no API key configured).",
143
+ profileMarkdown: "",
68
144
  profile: { static: [], dynamic: [], preferences: [] },
69
145
  relevantMemories: [],
70
146
  };
@@ -74,50 +150,42 @@ export class SupermemoryAdapter {
74
150
  let dynamicFacts = [];
75
151
  let preferences = [];
76
152
  const relevantMemories = [];
77
- try {
78
- const profileRes = await this.client.profile(namespace, {
153
+ const [profileOutcome, searchOutcome] = await Promise.allSettled([
154
+ this.withTimeout(() => client.profile(namespace, {
79
155
  ...(params.query ? { q: params.query } : {}),
80
- });
81
- staticFacts = Array.isArray(profileRes?.profile?.static)
82
- ? profileRes.profile.static.map(String)
83
- : [];
84
- dynamicFacts = Array.isArray(profileRes?.profile?.dynamic)
85
- ? profileRes.profile.dynamic.map(String)
86
- : [];
87
- preferences = Array.isArray(profileRes?.profile?.buckets?.preferences)
88
- ? profileRes.profile.buckets.preferences.map(String)
89
- : [];
90
- }
91
- catch {
92
- // Fail-open: never block tool planning if profile endpoint times out
93
- }
94
- if (params.query && params.query.trim().length > 0) {
95
- try {
96
- const searchRes = await this.client.search(namespace, {
156
+ }), 1000),
157
+ params.query && params.query.trim().length > 0
158
+ ? this.withTimeout(() => client.search(namespace, {
97
159
  query: params.query.trim(),
98
160
  limit,
99
161
  searchMode: "hybrid",
100
162
  include: { documents: true, forgotten: false, related: false },
101
163
  rewriteQuery: false,
164
+ }), 1000)
165
+ : Promise.resolve(null),
166
+ ]);
167
+ if (profileOutcome.status === "fulfilled" && profileOutcome.value) {
168
+ const profileRes = profileOutcome.value;
169
+ staticFacts = toFactTexts(profileRes?.profile?.static);
170
+ dynamicFacts = toFactTexts(profileRes?.profile?.dynamic);
171
+ preferences = toFactTexts(profileRes?.profile?.buckets?.preferences);
172
+ }
173
+ if (searchOutcome.status === "fulfilled" && searchOutcome.value) {
174
+ const searchRes = searchOutcome.value;
175
+ for (const item of (searchRes?.results || []).slice(0, limit)) {
176
+ const doc = item.included?.document;
177
+ const title = String(doc?.metadata?.title ||
178
+ doc?.title ||
179
+ item.memory ||
180
+ item.chunk ||
181
+ "Workspace Memory").slice(0, 120);
182
+ const snippet = String(item.memory || item.chunk || doc?.metadata?.summary || "").slice(0, 300);
183
+ relevantMemories.push({
184
+ documentId: String(doc?.id || item.id || ""),
185
+ title,
186
+ snippet,
187
+ similarity: Number(item.similarity || 0),
102
188
  });
103
- for (const item of (searchRes?.results || []).slice(0, limit)) {
104
- const doc = item.included?.document;
105
- const title = String(doc?.metadata?.title ||
106
- doc?.title ||
107
- item.memory ||
108
- item.chunk ||
109
- "Workspace Memory").slice(0, 120);
110
- const snippet = String(item.memory || item.chunk || doc?.metadata?.summary || "").slice(0, 300);
111
- relevantMemories.push({
112
- documentId: String(doc?.id || item.id || ""),
113
- title,
114
- snippet,
115
- similarity: Number(item.similarity || 0),
116
- });
117
- }
118
- }
119
- catch {
120
- // Fail-open
121
189
  }
122
190
  }
123
191
  const promptLines = [
@@ -132,52 +200,85 @@ export class SupermemoryAdapter {
132
200
  if (relevantMemories.length > 0) {
133
201
  promptLines.push(`Recalled Memories: ${relevantMemories.map((m) => `${m.title}: ${m.snippet}`).join(" | ")}`);
134
202
  }
203
+ const mdLines = [`## Workspace Profile (${namespace})`];
204
+ if (staticFacts.length > 0) {
205
+ mdLines.push("### Static Facts", ...staticFacts.map((f) => `- ${f}`));
206
+ }
207
+ if (dynamicFacts.length > 0) {
208
+ mdLines.push("### Dynamic Context", ...dynamicFacts.map((f) => `- ${f}`));
209
+ }
210
+ if (preferences.length > 0) {
211
+ mdLines.push("### Preferences", ...preferences.map((f) => `- ${f}`));
212
+ }
213
+ const inferred = extractActiveMemoryDefaults({
214
+ preferences,
215
+ staticFacts: [
216
+ ...staticFacts,
217
+ ...dynamicFacts,
218
+ ...relevantMemories.map((m) => `${m.title}: ${m.snippet}`),
219
+ ],
220
+ });
221
+ if (Object.keys(inferred.activeDefaults).length > 0) {
222
+ promptLines.push(`Active Memory Defaults: ${Object.entries(inferred.activeDefaults)
223
+ .map(([k, v]) => `${k}=${String(v)}`)
224
+ .join(", ")}`);
225
+ mdLines.push("### Active Defaults (Auto-Fill)", ...Object.entries(inferred.activeDefaults).map(([k, v]) => `- **${k}**: \`${String(v)}\``));
226
+ }
135
227
  return {
136
228
  enabled: true,
137
229
  namespace,
230
+ fundingSource,
138
231
  staticFacts,
139
232
  dynamicContext: dynamicFacts,
140
233
  recalledMemories: relevantMemories,
141
234
  formattedPromptContext: promptLines.join("\n"),
235
+ profileMarkdown: mdLines.join("\n"),
142
236
  profile: {
143
237
  static: staticFacts,
144
238
  dynamic: dynamicFacts,
145
239
  preferences,
146
240
  },
147
241
  relevantMemories,
242
+ activeDefaults: inferred.activeDefaults,
243
+ activeGuardrails: inferred.activeGuardrails,
148
244
  };
149
245
  }
150
246
  /**
151
- * Stores a governed, secret-sanitized document/memory in the workspace's Supermemory v5 namespace.
247
+ * Stores a governed, secret-sanitized document/memory in the workspace's Supermemory v5 namespace
248
+ * with `dreaming: "instant"` so profile facts are synthesized immediately.
152
249
  */
153
250
  async addMemory(params) {
154
251
  const namespace = this.namespaceForWorkspace(params.workspaceId);
155
252
  const sanitizedContent = this.sanitizeContent(params.content);
156
253
  const docId = params.documentId || `mem_${crypto.randomBytes(8).toString("hex")}`;
157
- if (!this.client) {
254
+ const { client, fundingSource } = this.resolveClient(params.apiKeyOverride);
255
+ if (!client) {
158
256
  return {
159
257
  documentId: docId,
160
258
  namespace,
161
259
  status: "local_only",
162
260
  sanitizedContent,
261
+ fundingSource,
163
262
  };
164
263
  }
165
- const res = await this.client.add(namespace, {
264
+ const res = await this.withTimeout(() => client.add(namespace, {
166
265
  id: docId,
167
266
  content: `${params.title}\n\n${sanitizedContent}`,
168
267
  taskType: "memory",
268
+ dreaming: params.dreaming || "instant",
169
269
  metadata: {
170
270
  title: params.title,
171
271
  muxoWorkspaceId: params.workspaceId,
172
272
  muxoKind: params.kind,
173
273
  ...(params.metadata || {}),
174
274
  },
175
- });
275
+ }), 1500);
176
276
  return {
177
- documentId: String(res.id || docId),
277
+ documentId: String(res?.id || docId),
178
278
  namespace,
179
- status: String(res.status || "queued"),
279
+ status: String(res?.status || "queued"),
180
280
  sanitizedContent,
281
+ fundingSource,
181
282
  };
182
283
  }
183
284
  async recordExecutionMemory(params) {
@@ -187,6 +288,7 @@ export class SupermemoryAdapter {
187
288
  title: `Execution: ${params.toolSlug} (${params.status})`,
188
289
  content: params.summary,
189
290
  kind: "execution_outcome",
291
+ apiKeyOverride: params.apiKeyOverride,
190
292
  metadata: {
191
293
  toolSlug: params.toolSlug,
192
294
  lane: params.lane,
@@ -194,7 +296,7 @@ export class SupermemoryAdapter {
194
296
  status: params.status,
195
297
  },
196
298
  });
197
- return added.documentId;
299
+ return added ? added.documentId : null;
198
300
  }
199
301
  catch {
200
302
  return null;
@@ -203,15 +305,16 @@ export class SupermemoryAdapter {
203
305
  /**
204
306
  * Deletes/forgets a memory document from the workspace's Supermemory v5 namespace.
205
307
  */
206
- async deleteMemory(workspaceId, documentId) {
308
+ async deleteMemory(workspaceId, documentId, apiKeyOverride) {
207
309
  const namespace = this.namespaceForWorkspace(workspaceId);
208
- if (!this.client) {
310
+ const { client } = this.resolveClient(apiKeyOverride);
311
+ if (!client) {
209
312
  return { deleted: true, namespace };
210
313
  }
211
314
  try {
212
- const res = await this.client.documents.delete(namespace, {
315
+ const res = await this.withTimeout(() => client.documents.delete(namespace, {
213
316
  ids: [documentId],
214
- });
317
+ }), 1200);
215
318
  return {
216
319
  deleted: (res?.count || 0) >= 1,
217
320
  namespace,
@@ -221,4 +324,455 @@ export class SupermemoryAdapter {
221
324
  return { deleted: false, namespace };
222
325
  }
223
326
  }
327
+ /**
328
+ * Semantically searches and forgets/prunes outdated memories in Supermemory v5 matching a query.
329
+ */
330
+ async forgetMatching(params) {
331
+ const namespace = this.namespaceForWorkspace(params.workspaceId);
332
+ const { client, fundingSource } = this.resolveClient(params.apiKeyOverride);
333
+ if (!client || !params.query.trim()) {
334
+ return {
335
+ namespace,
336
+ deletedRemoteIds: [],
337
+ fundingSource,
338
+ };
339
+ }
340
+ try {
341
+ const searchRes = await this.withTimeout(() => client.search(namespace, {
342
+ query: params.query.trim(),
343
+ limit: Math.min(10, Math.max(1, Number(params.limit || 5))),
344
+ searchMode: "hybrid",
345
+ include: { documents: true, forgotten: false, related: false },
346
+ rewriteQuery: false,
347
+ }), 1200);
348
+ const docIds = Array.from(new Set((searchRes?.results || [])
349
+ .map((r) => String(r.included?.document?.id || r.id || ""))
350
+ .filter(Boolean)));
351
+ if (docIds.length > 0) {
352
+ await this.withTimeout(() => client.documents.delete(namespace, { ids: docIds }), 1200);
353
+ return {
354
+ namespace,
355
+ deletedRemoteIds: docIds,
356
+ fundingSource,
357
+ };
358
+ }
359
+ }
360
+ catch {
361
+ // Fail-open on remote pruning errors
362
+ }
363
+ return {
364
+ namespace,
365
+ deletedRemoteIds: [],
366
+ fundingSource,
367
+ };
368
+ }
369
+ }
370
+ export function extractActiveMemoryDefaults(params) {
371
+ const activeDefaults = {};
372
+ const activeGuardrails = [];
373
+ const activeFacts = (params.temporalFacts || []).filter((f) => f.validTo === null);
374
+ for (const fact of activeFacts) {
375
+ const subj = String(fact.subject || "").toLowerCase();
376
+ const pred = String(fact.predicate || "").toLowerCase();
377
+ const obj = String(fact.object || "").trim();
378
+ if (!obj)
379
+ continue;
380
+ if (subj === "workspace_guardrail" || pred === "enforces_rule") {
381
+ if (!activeGuardrails.includes(obj))
382
+ activeGuardrails.push(obj);
383
+ continue;
384
+ }
385
+ if ((subj.includes("region") || pred.includes("region")) &&
386
+ !activeDefaults.defaultRegion) {
387
+ const regToken = obj.match(/\b((?:aws-)?(?:us|eu|ap|sa|ca|me|af)-(?:east|west|central|north|south|northeast|southeast)-[1-9])\b/i);
388
+ if (regToken) {
389
+ activeDefaults.defaultRegion = regToken[1].toLowerCase();
390
+ }
391
+ else if (!/\s/.test(obj)) {
392
+ activeDefaults.defaultRegion = obj;
393
+ }
394
+ }
395
+ if ((subj.includes("github") || subj.includes("repo") || pred.includes("repo")) &&
396
+ obj.includes("/") &&
397
+ !activeDefaults.defaultGithubFullRepo) {
398
+ const [owner, repo] = obj.split("/");
399
+ if (owner && repo) {
400
+ activeDefaults.defaultGithubOwner = owner.trim();
401
+ activeDefaults.defaultGithubRepo = repo.trim();
402
+ activeDefaults.defaultGithubFullRepo = `${owner.trim()}/${repo.trim()}`;
403
+ }
404
+ }
405
+ if ((subj.includes("slack") || pred.includes("channel")) &&
406
+ !activeDefaults.defaultSlackChannel) {
407
+ activeDefaults.defaultSlackChannel = obj.replace(/^#/, "").trim();
408
+ }
409
+ if ((subj.includes("email") || pred.includes("email")) &&
410
+ obj.includes("@") &&
411
+ !activeDefaults.defaultEmail) {
412
+ activeDefaults.defaultEmail = obj;
413
+ }
414
+ if ((subj.includes("domain") || pred.includes("domain")) &&
415
+ !activeDefaults.defaultDomain) {
416
+ activeDefaults.defaultDomain = obj.replace(/^https?:\/\//i, "").replace(/\/.*$/, "");
417
+ }
418
+ }
419
+ const textCorpus = [
420
+ ...(params.memories || []).map((m) => `${m.title}: ${m.content}`),
421
+ ...(params.preferences || []),
422
+ ...(params.staticFacts || []),
423
+ ];
424
+ for (const rawText of textCorpus) {
425
+ const text = String(rawText || "");
426
+ if (!activeDefaults.defaultRegion) {
427
+ const regMatch = text.match(/\b((?:aws-)?(?:us|eu|ap|sa|ca|me|af)-(?:east|west|central|north|south|northeast|southeast)-[1-9])\b/i);
428
+ if (regMatch) {
429
+ activeDefaults.defaultRegion = regMatch[1].toLowerCase();
430
+ }
431
+ }
432
+ if (!activeDefaults.defaultGithubFullRepo) {
433
+ const repoMatch = text.match(/(?:default\s+(?:github\s+)?repo(?:sitory)?|github\s+repo(?:sitory)?|primary\s+repo(?:sitory)?|repo\s+is)\s*(?:is|:|=)?\s*["'`]?([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)["'`]?/i) ||
434
+ text.match(/\bgithub\.com\/([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)\b/i);
435
+ if (repoMatch) {
436
+ const [owner, repo] = repoMatch[1].split("/");
437
+ if (owner && repo) {
438
+ activeDefaults.defaultGithubOwner = owner;
439
+ activeDefaults.defaultGithubRepo = repo;
440
+ activeDefaults.defaultGithubFullRepo = `${owner}/${repo}`;
441
+ }
442
+ }
443
+ }
444
+ if (!activeDefaults.defaultSlackChannel) {
445
+ const slackMatch = text.match(/(?:default\s+(?:slack\s+)?channel|slack\s+channel|post\s+to\s+channel)\s*(?:is|:|=)?\s*#?([a-z0-9_-]{2,40})\b/i) || text.match(/\B#([a-z][a-z0-9_-]{2,35})\b/);
446
+ if (slackMatch) {
447
+ activeDefaults.defaultSlackChannel = slackMatch[1].toLowerCase();
448
+ }
449
+ }
450
+ if (!activeDefaults.defaultEmail) {
451
+ const emailMatch = text.match(/(?:default\s+(?:recipient\s+|alert\s+)?email|send\s+emails?\s+to|notify\s+email|recipient_email)\s*(?:is|:|=)?\s*([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})/i);
452
+ if (emailMatch) {
453
+ activeDefaults.defaultEmail = emailMatch[1];
454
+ }
455
+ }
456
+ if (!activeDefaults.defaultDomain) {
457
+ const domainMatch = text.match(/(?:default\s+domain|primary\s+domain|company\s+domain)\s*(?:is|:|=)?\s*(?:https?:\/\/)?([a-z0-9.-]+\.[a-z]{2,})\b/i);
458
+ if (domainMatch) {
459
+ activeDefaults.defaultDomain = domainMatch[1].toLowerCase();
460
+ }
461
+ }
462
+ if (/workspace\s+guardrail/i.test(text)) {
463
+ const cleaned = text.replace(/^.*?Workspace Guardrail:\s*/i, "").trim();
464
+ if (cleaned && !activeGuardrails.includes(cleaned)) {
465
+ activeGuardrails.push(cleaned);
466
+ }
467
+ }
468
+ }
469
+ return {
470
+ activeDefaults,
471
+ activeGuardrails,
472
+ };
473
+ }
474
+ export function applyActiveMemoryToToolInput(params) {
475
+ const mergedInput = { ...(params.input || {}) };
476
+ const autoFilledInput = {};
477
+ const explanations = [];
478
+ const defaults = params.activeDefaults || {};
479
+ const slug = String(params.toolSlug || "").toLowerCase();
480
+ // 1. Cloud provisioning tools: auto-fill region if not explicitly provided
481
+ if (slug.startsWith("resource.") && defaults.defaultRegion) {
482
+ if (!mergedInput.region || String(mergedInput.region).trim() === "") {
483
+ mergedInput.region = defaults.defaultRegion;
484
+ autoFilledInput.region = defaults.defaultRegion;
485
+ explanations.push(`Auto-filled region="${defaults.defaultRegion}" from Supermemory`);
486
+ }
487
+ }
488
+ // 2. GitHub tools: auto-fill owner & repo if missing or generic placeholder
489
+ if ((slug.startsWith("app.github.") || slug === "instant.github.repo_intel") &&
490
+ defaults.defaultGithubOwner &&
491
+ defaults.defaultGithubRepo) {
492
+ const isGenericPlaceholder = mergedInput.owner === "muxo-ai" && mergedInput.repo === "platform";
493
+ if (!mergedInput.owner || isGenericPlaceholder) {
494
+ mergedInput.owner = defaults.defaultGithubOwner;
495
+ autoFilledInput.owner = defaults.defaultGithubOwner;
496
+ }
497
+ if (!mergedInput.repo || isGenericPlaceholder) {
498
+ mergedInput.repo = defaults.defaultGithubRepo;
499
+ autoFilledInput.repo = defaults.defaultGithubRepo;
500
+ }
501
+ if (autoFilledInput.owner || autoFilledInput.repo) {
502
+ explanations.push(`Auto-filled GitHub repo "${defaults.defaultGithubOwner}/${defaults.defaultGithubRepo}" from Supermemory`);
503
+ }
504
+ }
505
+ // 3. Slack tools: auto-fill channel if missing or generic placeholder ("general")
506
+ if (slug.startsWith("app.slack.") && defaults.defaultSlackChannel) {
507
+ if (!mergedInput.channel || mergedInput.channel === "general") {
508
+ mergedInput.channel = defaults.defaultSlackChannel;
509
+ autoFilledInput.channel = defaults.defaultSlackChannel;
510
+ explanations.push(`Auto-filled Slack channel "#${defaults.defaultSlackChannel}" from Supermemory`);
511
+ }
512
+ }
513
+ // 4. Gmail send_email: auto-fill recipient_email if missing or placeholder
514
+ if (slug === "app.gmail.send_email" && defaults.defaultEmail) {
515
+ if (!mergedInput.recipient_email ||
516
+ mergedInput.recipient_email === "team@example.com") {
517
+ mergedInput.recipient_email = defaults.defaultEmail;
518
+ autoFilledInput.recipient_email = defaults.defaultEmail;
519
+ explanations.push(`Auto-filled recipient_email="${defaults.defaultEmail}" from Supermemory`);
520
+ }
521
+ }
522
+ // 5. DNS / Domain tools: auto-fill domain if missing or example.com
523
+ if ((slug === "instant.dns.resolve" || slug === "instant.dns.lookup") &&
524
+ defaults.defaultDomain) {
525
+ if (!mergedInput.domain || mergedInput.domain === "example.com") {
526
+ mergedInput.domain = defaults.defaultDomain;
527
+ autoFilledInput.domain = defaults.defaultDomain;
528
+ explanations.push(`Auto-filled domain="${defaults.defaultDomain}" from Supermemory`);
529
+ }
530
+ }
531
+ // 6. SEO / Web tools: auto-fill url from defaultDomain if missing or generic placeholder
532
+ if ((slug === "instant.seo.audit" ||
533
+ slug === "instant.web.scrape" ||
534
+ slug === "instant.web.crawl") &&
535
+ defaults.defaultDomain) {
536
+ if (!mergedInput.url ||
537
+ mergedInput.url === "https://example.com") {
538
+ const normalizedUrl = defaults.defaultDomain.startsWith("http")
539
+ ? defaults.defaultDomain
540
+ : `https://${defaults.defaultDomain}`;
541
+ mergedInput.url = normalizedUrl;
542
+ autoFilledInput.url = normalizedUrl;
543
+ explanations.push(`Auto-filled url="${normalizedUrl}" from Supermemory`);
544
+ }
545
+ }
546
+ return {
547
+ mergedInput,
548
+ autoFilledInput,
549
+ explanations,
550
+ };
551
+ }
552
+ export function parseNaturalLanguageMemoryInstruction(prompt) {
553
+ const cleanPrompt = String(prompt || "").trim();
554
+ const isMemoryInstruction = /^(?:hey\s+muxo[,:\s]*)?(?:please\s+)?(?:remember(?:\s+that)?|save\s+to\s+memory|store\s+in\s+memory|note\s+that|set\s+(?:our\s+|my\s+)?default|our\s+default\s+(?:github\s+repo|repo|region|slack\s+channel|email|domain)\s+is|my\s+default\s+(?:github\s+repo|repo|region|slack\s+channel|email|domain)\s+is)\b/i.test(cleanPrompt);
555
+ const normalizedBody = cleanPrompt
556
+ .replace(/^(?:hey\s+muxo[,:\s]*)?(?:please\s+)?(?:remember(?:\s+that)?|save\s+to\s+memory|store\s+in\s+memory|note\s+that)[:\s]*/i, "")
557
+ .trim();
558
+ const { activeDefaults } = extractActiveMemoryDefaults({
559
+ preferences: [cleanPrompt],
560
+ staticFacts: [normalizedBody],
561
+ });
562
+ const temporalFacts = [];
563
+ const learnedParts = [];
564
+ if (activeDefaults.defaultRegion) {
565
+ temporalFacts.push({
566
+ subject: "workspace:default_region",
567
+ predicate: "prefers_region",
568
+ object: activeDefaults.defaultRegion,
569
+ summary: `Default cloud region is ${activeDefaults.defaultRegion}`,
570
+ });
571
+ learnedParts.push(`default cloud region = ${activeDefaults.defaultRegion}`);
572
+ }
573
+ if (activeDefaults.defaultGithubFullRepo) {
574
+ temporalFacts.push({
575
+ subject: "workspace:default_github_repo",
576
+ predicate: "prefers_repo",
577
+ object: activeDefaults.defaultGithubFullRepo,
578
+ summary: `Default GitHub repository is ${activeDefaults.defaultGithubFullRepo}`,
579
+ });
580
+ learnedParts.push(`default GitHub repo = ${activeDefaults.defaultGithubFullRepo}`);
581
+ }
582
+ if (activeDefaults.defaultSlackChannel) {
583
+ temporalFacts.push({
584
+ subject: "workspace:default_slack_channel",
585
+ predicate: "prefers_channel",
586
+ object: activeDefaults.defaultSlackChannel,
587
+ summary: `Default Slack channel is #${activeDefaults.defaultSlackChannel}`,
588
+ });
589
+ learnedParts.push(`default Slack channel = #${activeDefaults.defaultSlackChannel}`);
590
+ }
591
+ if (activeDefaults.defaultEmail) {
592
+ temporalFacts.push({
593
+ subject: "workspace:default_email",
594
+ predicate: "prefers_email",
595
+ object: activeDefaults.defaultEmail,
596
+ summary: `Default notification email is ${activeDefaults.defaultEmail}`,
597
+ });
598
+ learnedParts.push(`default email = ${activeDefaults.defaultEmail}`);
599
+ }
600
+ if (activeDefaults.defaultDomain) {
601
+ temporalFacts.push({
602
+ subject: "workspace:default_domain",
603
+ predicate: "prefers_domain",
604
+ object: activeDefaults.defaultDomain,
605
+ summary: `Default workspace domain is ${activeDefaults.defaultDomain}`,
606
+ });
607
+ learnedParts.push(`default domain = ${activeDefaults.defaultDomain}`);
608
+ }
609
+ if (temporalFacts.length === 0 && isMemoryInstruction && normalizedBody) {
610
+ temporalFacts.push({
611
+ subject: "workspace:operator_preference",
612
+ predicate: "remembers",
613
+ object: normalizedBody.slice(0, 140),
614
+ summary: normalizedBody.slice(0, 180),
615
+ });
616
+ }
617
+ const title = learnedParts.length > 0
618
+ ? `Learned Default: ${learnedParts.join(", ").slice(0, 80)}`
619
+ : `Remembered Preference: ${(normalizedBody || cleanPrompt).slice(0, 72)}`;
620
+ const humanSummary = learnedParts.length > 0
621
+ ? `Remembered in Supermemory and enabled auto-fill for: ${learnedParts.join(" · ")}.`
622
+ : `Saved to Supermemory: "${normalizedBody || cleanPrompt}"`;
623
+ return {
624
+ isMemoryInstruction,
625
+ title,
626
+ content: normalizedBody || cleanPrompt,
627
+ kind: "preference",
628
+ extractedDefaults: activeDefaults,
629
+ temporalFacts,
630
+ humanSummary,
631
+ };
632
+ }
633
+ export function explainMemoryContext(params) {
634
+ const mem = params.memory;
635
+ const defaultsEntries = Object.entries(mem.activeDefaults || {});
636
+ const defaultsPlain = defaultsEntries.length > 0
637
+ ? `Learned defaults active (${defaultsEntries.map(([k, v]) => `${k}: ${String(v)}`).join(", ")}) and automatically fill into matching tool calls.`
638
+ : "No learned defaults set yet (say 'Remember our default region is us-east-1' or 'Remember our default repo is owner/repo' to set one).";
639
+ const factsCount = (mem.staticFacts?.length || 0) +
640
+ (mem.dynamicContext?.length || 0) +
641
+ (params.localCount || 0);
642
+ const scanSafe = mem.contextPoisoningScan?.safe !== false;
643
+ const headline = defaultsEntries.length > 0
644
+ ? `Muxo Memory active with ${defaultsEntries.length} auto-fill default${defaultsEntries.length === 1 ? "" : "s"} and ${factsCount} stored workspace item${factsCount === 1 ? "" : "s"}.`
645
+ : `Muxo Memory active with ${factsCount} stored workspace item${factsCount === 1 ? "" : "s"}.`;
646
+ const nonTechnical = [
647
+ headline,
648
+ defaultsPlain,
649
+ mem.recalledMemories && mem.recalledMemories.length > 0
650
+ ? `Recalled ${mem.recalledMemories.length} relevant memory note${mem.recalledMemories.length === 1 ? "" : "s"} for this context.`
651
+ : "All stored memories are secret-scrubbed so API keys and passwords are never saved.",
652
+ scanSafe
653
+ ? "Memory safety check: Clean (no prompt-injection or poisoning patterns detected)."
654
+ : `Memory safety warning: ${mem.contextPoisoningScan?.findingsCount || 1} suspicious pattern(s) flagged (${mem.contextPoisoningScan?.riskLevel || "medium"}).`,
655
+ ].join(" ");
656
+ const technical = `namespace=${mem.namespace} | enabled=${mem.enabled} | fundingSource=${mem.fundingSource || "local_fallback"} | staticFacts=${mem.staticFacts?.length || 0} | dynamicContext=${mem.dynamicContext?.length || 0} | recalledMemories=${mem.recalledMemories?.length || 0} | localRecords=${params.localCount ?? 0} | temporalGraph=(active=${params.activeTemporalCount ?? (mem.temporalFacts?.length || 0)}, superseded=${params.historicalTemporalCount ?? 0}) | activeDefaults=${JSON.stringify(mem.activeDefaults || {})} | contextPoisoning=(safe=${scanSafe}, risk=${mem.contextPoisoningScan?.riskLevel || "none"}, findings=${mem.contextPoisoningScan?.findingsCount || 0})`;
657
+ return formatHumanFriendlyExplanation({
658
+ headline,
659
+ nonTechnical,
660
+ technical,
661
+ policy: params.policy,
662
+ languageMode: params.languageMode,
663
+ });
664
+ }
665
+ /**
666
+ * Deterministic 64-dimensional semantic feature-hashing embedding vector
667
+ * compatible with Cloudflare Vectorize / Workers AI (`@cf/baai/bge-base-en-v1.5`) hybrid recall.
668
+ */
669
+ export function computeSemanticEmbedding(text, dimensions = 64) {
670
+ const vec = new Array(dimensions).fill(0);
671
+ const normalized = String(text || "")
672
+ .toLowerCase()
673
+ .replace(/[^a-z0-9_./#@-\s]/g, " ");
674
+ const tokens = normalized
675
+ .split(/\s+/)
676
+ .map((t) => t.trim())
677
+ .filter((t) => t.length >= 2);
678
+ if (tokens.length === 0)
679
+ return vec;
680
+ // Semantic concept synonym expansion so related queries ("db"/"postgres", "mail"/"email", "repo"/"github") map to shared vector dimensions
681
+ const expandedTokens = [...tokens];
682
+ for (const tok of tokens) {
683
+ if (/^(postgres|postgresql|neon|supabase|sql|db|database)$/.test(tok)) {
684
+ expandedTokens.push("concept_database", "concept_postgres");
685
+ }
686
+ if (/^(redis|upstash|cache|kv)$/.test(tok)) {
687
+ expandedTokens.push("concept_cache", "concept_redis");
688
+ }
689
+ if (/^(repo|repository|github|git|pr|issue)$/.test(tok)) {
690
+ expandedTokens.push("concept_github", "concept_repo");
691
+ }
692
+ if (/^(region|us-east-1|us-west-2|eu-central-1|zone|datacenter)$/.test(tok)) {
693
+ expandedTokens.push("concept_region", "concept_cloud");
694
+ }
695
+ if (/^(slack|channel|chat|alert|incident)$/.test(tok)) {
696
+ expandedTokens.push("concept_slack", "concept_messaging");
697
+ }
698
+ if (/^(email|gmail|inbox|recipient|mail)$/.test(tok)) {
699
+ expandedTokens.push("concept_email", "concept_communication");
700
+ }
701
+ }
702
+ for (let i = 0; i < expandedTokens.length; i++) {
703
+ const feature = expandedTokens[i];
704
+ const digest = crypto.createHash("md5").update(feature).digest();
705
+ const dimIdx = digest[0] % dimensions;
706
+ const sign = (digest[1] & 1) === 0 ? 1 : -1;
707
+ const weight = feature.startsWith("concept_") ? 1.5 : 1.0;
708
+ vec[dimIdx] += sign * weight;
709
+ // Character trigram projection for sub-word resilience
710
+ for (let c = 0; c <= feature.length - 3; c++) {
711
+ const tri = feature.slice(c, c + 3);
712
+ const triDigest = crypto.createHash("md5").update(`tri:${tri}`).digest();
713
+ const triIdx = triDigest[0] % dimensions;
714
+ const triSign = (triDigest[1] & 1) === 0 ? 0.35 : -0.35;
715
+ vec[triIdx] += triSign;
716
+ }
717
+ }
718
+ const norm = Math.sqrt(vec.reduce((acc, v) => acc + v * v, 0));
719
+ if (norm > 0) {
720
+ for (let d = 0; d < dimensions; d++) {
721
+ vec[d] = Number((vec[d] / norm).toFixed(6));
722
+ }
723
+ }
724
+ return vec;
725
+ }
726
+ export function cosineSimilarity(vecA, vecB) {
727
+ const len = Math.min(vecA.length, vecB.length);
728
+ if (len === 0)
729
+ return 0;
730
+ let dot = 0;
731
+ let normA = 0;
732
+ let normB = 0;
733
+ for (let i = 0; i < len; i++) {
734
+ dot += vecA[i] * vecB[i];
735
+ normA += vecA[i] * vecA[i];
736
+ normB += vecB[i] * vecB[i];
737
+ }
738
+ if (normA === 0 || normB === 0)
739
+ return 0;
740
+ return Math.max(0, Math.min(1, dot / (Math.sqrt(normA) * Math.sqrt(normB))));
741
+ }
742
+ export function rankMemoriesHybrid(params) {
743
+ const q = String(params.query || "").trim();
744
+ const qVec = computeSemanticEmbedding(q);
745
+ const qTokens = q
746
+ .toLowerCase()
747
+ .split(/[^a-z0-9_./#@-]+/)
748
+ .filter((t) => t.length >= 2);
749
+ const nowMs = Date.now();
750
+ const scored = (params.memories || []).map((mem) => {
751
+ const corpus = `${mem.title} ${mem.content} ${mem.kind}`;
752
+ const docVec = computeSemanticEmbedding(corpus);
753
+ const vectorSim = q ? cosineSimilarity(qVec, docVec) : 0.5;
754
+ const lowerCorpus = corpus.toLowerCase();
755
+ const matchedTokens = qTokens.length > 0
756
+ ? qTokens.filter((tok) => lowerCorpus.includes(tok)).length
757
+ : 1;
758
+ const lexicalOverlap = qTokens.length > 0 ? matchedTokens / qTokens.length : 0.5;
759
+ const ageHours = Math.max(0, (nowMs - new Date(mem.createdAt || nowMs).getTime()) / 3_600_000);
760
+ const recencyWeight = 1 / (1 + ageHours / 168); // 1-week half-life
761
+ const hybridScore = Number((0.5 * vectorSim + 0.35 * lexicalOverlap + 0.15 * recencyWeight).toFixed(4));
762
+ return {
763
+ ...mem,
764
+ hybridScore,
765
+ vectorSimilarity: Number(vectorSim.toFixed(4)),
766
+ lexicalOverlap: Number(lexicalOverlap.toFixed(4)),
767
+ };
768
+ });
769
+ scored.sort((a, b) => b.hybridScore - a.hybridScore);
770
+ const limit = Math.max(1, Math.min(50, params.limit || 10));
771
+ const top = scored.slice(0, limit);
772
+ return {
773
+ rankedMemories: top,
774
+ vectorDimensions: 64,
775
+ embeddingModel: "@cf/baai/bge-base-en-v1.5 (cloudflare-vectorize-hybrid)",
776
+ topScore: top[0]?.hybridScore || 0,
777
+ };
224
778
  }